1 #include <memory>
2 #include <utility>
3 #include <iterator>
4 #include <exception>
5 #include <type_traits>
6 #include <unordered_set>
7 #include <gtest/gtest.h>
8 #include <entt/entity/component.hpp>
9 #include <entt/entity/fwd.hpp>
10 #include <entt/entity/storage.hpp>
11 #include "throwing_allocator.hpp"
12 #include "throwing_component.hpp"
13
14 struct empty_type {};
15 struct boxed_int { int value; };
16 struct stable_type { int value; };
17
18 struct update_from_destructor {
update_from_destructorupdate_from_destructor19 update_from_destructor(entt::storage<update_from_destructor> &ref, entt::entity other)
20 : storage{&ref},
21 target{other}
22 {}
23
update_from_destructorupdate_from_destructor24 update_from_destructor(update_from_destructor &&other) ENTT_NOEXCEPT
25 : storage{std::exchange(other.storage, nullptr)},
26 target{std::exchange(other.target, entt::null)}
27 {}
28
operator =update_from_destructor29 update_from_destructor & operator=(update_from_destructor &&other) ENTT_NOEXCEPT {
30 storage = std::exchange(other.storage, nullptr);
31 target = std::exchange(other.target, entt::null);
32 return *this;
33 }
34
~update_from_destructorupdate_from_destructor35 ~update_from_destructor() {
36 if(target != entt::null && storage->contains(target)) {
37 storage->erase(target);
38 }
39 }
40
41 private:
42 entt::storage<update_from_destructor> *storage{};
43 entt::entity target{entt::null};
44 };
45
46 template<>
47 struct entt::component_traits<stable_type>: basic_component_traits {
48 using in_place_delete = std::true_type;
49 };
50
operator ==(const boxed_int & lhs,const boxed_int & rhs)51 bool operator==(const boxed_int &lhs, const boxed_int &rhs) {
52 return lhs.value == rhs.value;
53 }
54
TEST(Storage,Functionalities)55 TEST(Storage, Functionalities) {
56 entt::storage<int> pool;
57
58 pool.reserve(42);
59
60 ASSERT_EQ(pool.capacity(), ENTT_PACKED_PAGE);
61 ASSERT_TRUE(pool.empty());
62 ASSERT_EQ(pool.size(), 0u);
63 ASSERT_EQ(std::as_const(pool).begin(), std::as_const(pool).end());
64 ASSERT_EQ(pool.begin(), pool.end());
65 ASSERT_FALSE(pool.contains(entt::entity{0}));
66 ASSERT_FALSE(pool.contains(entt::entity{41}));
67
68 pool.reserve(0);
69
70 ASSERT_EQ(pool.capacity(), ENTT_PACKED_PAGE);
71 ASSERT_TRUE(pool.empty());
72
73 pool.emplace(entt::entity{41}, 3);
74
75 ASSERT_FALSE(pool.empty());
76 ASSERT_EQ(pool.size(), 1u);
77 ASSERT_NE(std::as_const(pool).begin(), std::as_const(pool).end());
78 ASSERT_NE(pool.begin(), pool.end());
79 ASSERT_FALSE(pool.contains(entt::entity{0}));
80 ASSERT_TRUE(pool.contains(entt::entity{41}));
81 ASSERT_EQ(pool.get(entt::entity{41}), 3);
82
83 pool.erase(entt::entity{41});
84
85 ASSERT_TRUE(pool.empty());
86 ASSERT_EQ(pool.size(), 0u);
87 ASSERT_EQ(std::as_const(pool).begin(), std::as_const(pool).end());
88 ASSERT_EQ(pool.begin(), pool.end());
89 ASSERT_FALSE(pool.contains(entt::entity{0}));
90 ASSERT_FALSE(pool.contains(entt::entity{41}));
91
92 pool.emplace(entt::entity{41}, 12);
93
94 ASSERT_EQ(pool.get(entt::entity{41}), 12);
95
96 pool.clear();
97
98 ASSERT_TRUE(pool.empty());
99 ASSERT_EQ(pool.size(), 0u);
100 ASSERT_EQ(std::as_const(pool).begin(), std::as_const(pool).end());
101 ASSERT_EQ(pool.begin(), pool.end());
102 ASSERT_FALSE(pool.contains(entt::entity{0}));
103 ASSERT_FALSE(pool.contains(entt::entity{41}));
104
105 ASSERT_EQ(pool.capacity(), ENTT_PACKED_PAGE);
106
107 pool.shrink_to_fit();
108
109 ASSERT_EQ(pool.capacity(), 0u);
110 }
111
TEST(Storage,Move)112 TEST(Storage, Move) {
113 entt::storage<int> pool;
114 pool.emplace(entt::entity{3}, 3);
115
116 ASSERT_TRUE(std::is_move_constructible_v<decltype(pool)>);
117 ASSERT_TRUE(std::is_move_assignable_v<decltype(pool)>);
118
119 entt::storage<int> other{std::move(pool)};
120
121 ASSERT_TRUE(pool.empty());
122 ASSERT_FALSE(other.empty());
123 ASSERT_EQ(pool.at(0u), static_cast<entt::entity>(entt::null));
124 ASSERT_EQ(other.at(0u), entt::entity{3});
125 ASSERT_EQ(other.get(entt::entity{3}), 3);
126
127 pool = std::move(other);
128
129 ASSERT_FALSE(pool.empty());
130 ASSERT_TRUE(other.empty());
131 ASSERT_EQ(pool.at(0u), entt::entity{3});
132 ASSERT_EQ(pool.get(entt::entity{3}), 3);
133 ASSERT_EQ(other.at(0u), static_cast<entt::entity>(entt::null));
134
135 other = entt::storage<int>{};
136 other.emplace(entt::entity{42}, 42);
137 other = std::move(pool);
138
139 ASSERT_TRUE(pool.empty());
140 ASSERT_FALSE(other.empty());
141 ASSERT_EQ(pool.at(0u), static_cast<entt::entity>(entt::null));
142 ASSERT_EQ(other.at(0u), entt::entity{3});
143 ASSERT_EQ(other.get(entt::entity{3}), 3);
144 }
145
TEST(Storage,EmptyType)146 TEST(Storage, EmptyType) {
147 entt::storage<empty_type> pool;
148 pool.emplace(entt::entity{99});
149
150 ASSERT_TRUE(pool.contains(entt::entity{99}));
151 }
152
TEST(Storage,Insert)153 TEST(Storage, Insert) {
154 entt::storage<stable_type> pool;
155 entt::entity entities[2u];
156
157 entities[0u] = entt::entity{3};
158 entities[1u] = entt::entity{42};
159 pool.insert(std::begin(entities), std::end(entities), stable_type{99});
160
161 ASSERT_TRUE(pool.contains(entities[0u]));
162 ASSERT_TRUE(pool.contains(entities[1u]));
163
164 ASSERT_FALSE(pool.empty());
165 ASSERT_EQ(pool.size(), 2u);
166 ASSERT_EQ(pool.get(entities[0u]).value, 99);
167 ASSERT_EQ(pool.get(entities[1u]).value, 99);
168
169 pool.erase(std::begin(entities), std::end(entities));
170 const stable_type values[2u] = { stable_type{42}, stable_type{3} };
171 pool.insert(std::rbegin(entities), std::rend(entities), std::begin(values));
172
173 ASSERT_EQ(pool.size(), 4u);
174 ASSERT_TRUE(pool.at(0u) == entt::tombstone);
175 ASSERT_TRUE(pool.at(1u) == entt::tombstone);
176 ASSERT_EQ(pool.at(2u), entities[1u]);
177 ASSERT_EQ(pool.at(3u), entities[0u]);
178 ASSERT_EQ(pool.index(entities[0u]), 3u);
179 ASSERT_EQ(pool.index(entities[1u]), 2u);
180 ASSERT_EQ(pool.get(entities[0u]).value, 3);
181 ASSERT_EQ(pool.get(entities[1u]).value, 42);
182 }
183
TEST(Storage,InsertEmptyType)184 TEST(Storage, InsertEmptyType) {
185 entt::storage<empty_type> pool;
186 entt::entity entities[2u];
187
188 entities[0u] = entt::entity{3};
189 entities[1u] = entt::entity{42};
190
191 pool.insert(std::begin(entities), std::end(entities));
192
193 ASSERT_TRUE(pool.contains(entities[0u]));
194 ASSERT_TRUE(pool.contains(entities[1u]));
195
196 ASSERT_FALSE(pool.empty());
197 ASSERT_EQ(pool.size(), 2u);
198 }
199
TEST(Storage,Erase)200 TEST(Storage, Erase) {
201 entt::storage<int> pool;
202 entt::entity entities[3u];
203
204 entities[0u] = entt::entity{3};
205 entities[1u] = entt::entity{42};
206 entities[2u] = entt::entity{9};
207
208 pool.emplace(entities[0u]);
209 pool.emplace(entities[1u]);
210 pool.emplace(entities[2u]);
211 pool.erase(std::begin(entities), std::end(entities));
212
213 ASSERT_DEATH(pool.erase(std::begin(entities), std::end(entities)), "");
214 ASSERT_TRUE(pool.empty());
215
216 pool.emplace(entities[0u], 0);
217 pool.emplace(entities[1u], 1);
218 pool.emplace(entities[2u], 2);
219 pool.erase(entities, entities + 2u);
220
221 ASSERT_FALSE(pool.empty());
222 ASSERT_EQ(*pool.begin(), 2);
223
224 pool.erase(entities[2u]);
225
226 ASSERT_DEATH(pool.erase(entities[2u]), "");
227 ASSERT_TRUE(pool.empty());
228
229 pool.emplace(entities[0u], 0);
230 pool.emplace(entities[1u], 1);
231 pool.emplace(entities[2u], 2);
232 std::swap(entities[1u], entities[2u]);
233 pool.erase(entities, entities + 2u);
234
235 ASSERT_FALSE(pool.empty());
236 ASSERT_EQ(*pool.begin(), 1);
237 }
238
TEST(Storage,StableErase)239 TEST(Storage, StableErase) {
240 entt::storage<stable_type> pool;
241 entt::entity entities[3u];
242
243 ASSERT_DEATH([[maybe_unused]] auto &&value = pool.get(entt::tombstone), "");
244 ASSERT_DEATH([[maybe_unused]] auto &&value = pool.get(entt::null), "");
245
246 entities[0u] = entt::entity{3};
247 entities[1u] = entt::entity{42};
248 entities[2u] = entt::entity{9};
249
250 pool.emplace(entities[0u], stable_type{0});
251 pool.emplace(entities[1u], stable_type{1});
252 pool.emplace(entities[2u], stable_type{2});
253
254 pool.erase(std::begin(entities), std::end(entities));
255
256 ASSERT_DEATH(pool.erase(std::begin(entities), std::end(entities)), "");
257 ASSERT_FALSE(pool.empty());
258 ASSERT_EQ(pool.size(), 3u);
259 ASSERT_TRUE(pool.at(2u) == entt::tombstone);
260 ASSERT_DEATH([[maybe_unused]] auto &&value = pool.get(entt::tombstone), "");
261 ASSERT_DEATH([[maybe_unused]] auto &&value = pool.get(entt::null), "");
262 ASSERT_DEATH([[maybe_unused]] auto &&value = pool.get(entities[1u]), "");
263
264 pool.emplace(entities[2u], stable_type{2});
265 pool.emplace(entities[0u], stable_type{0});
266 pool.emplace(entities[1u], stable_type{1});
267
268 ASSERT_EQ(pool.get(entities[0u]).value, 0);
269 ASSERT_EQ(pool.get(entities[1u]).value, 1);
270 ASSERT_EQ(pool.get(entities[2u]).value, 2);
271
272 ASSERT_EQ(pool.begin()->value, 2);
273 ASSERT_EQ(pool.index(entities[0u]), 1u);
274 ASSERT_EQ(pool.index(entities[1u]), 0u);
275 ASSERT_EQ(pool.index(entities[2u]), 2u);
276
277 pool.erase(entities, entities + 2u);
278
279 ASSERT_FALSE(pool.empty());
280 ASSERT_EQ(pool.size(), 3u);
281 ASSERT_EQ(pool.begin()->value, 2);
282 ASSERT_EQ(pool.index(entities[2u]), 2u);
283
284 pool.erase(entities[2u]);
285
286 ASSERT_DEATH(pool.erase(entities[2u]), "");
287 ASSERT_FALSE(pool.empty());
288 ASSERT_EQ(pool.size(), 3u);
289 ASSERT_FALSE(pool.contains(entities[0u]));
290 ASSERT_FALSE(pool.contains(entities[1u]));
291 ASSERT_FALSE(pool.contains(entities[2u]));
292
293 pool.emplace(entities[0u], stable_type{0});
294 pool.emplace(entities[1u], stable_type{1});
295 pool.emplace(entities[2u], stable_type{2});
296 std::swap(entities[1u], entities[2u]);
297 pool.erase(entities, entities + 2u);
298
299 ASSERT_FALSE(pool.empty());
300 ASSERT_EQ(pool.size(), 3u);
301 ASSERT_TRUE(pool.contains(entities[2u]));
302 ASSERT_EQ(pool.index(entities[2u]), 0u);
303 ASSERT_EQ(pool.get(entities[2u]).value, 1);
304
305 pool.compact();
306
307 ASSERT_FALSE(pool.empty());
308 ASSERT_EQ(pool.size(), 1u);
309 ASSERT_EQ(pool.begin()->value, 1);
310
311 pool.clear();
312
313 ASSERT_EQ(pool.size(), 0u);
314
315 pool.emplace(entities[0u], stable_type{0});
316 pool.emplace(entities[1u], stable_type{2});
317 pool.emplace(entities[2u], stable_type{1});
318 pool.erase(entities[2u]);
319
320 ASSERT_DEATH(pool.erase(entities[2u]), "");
321
322 pool.erase(entities[0u]);
323 pool.erase(entities[1u]);
324
325 ASSERT_DEATH(pool.erase(entities, entities + 2u), "");
326 ASSERT_EQ(pool.size(), 3u);
327 ASSERT_TRUE(pool.at(2u) == entt::tombstone);
328
329 pool.emplace(entities[0u], stable_type{99});
330
331 ASSERT_EQ((++pool.begin())->value, 99);
332
333 pool.emplace(entities[1u], stable_type{2});
334 pool.emplace(entities[2u], stable_type{1});
335 pool.emplace(entt::entity{0}, stable_type{7});
336
337 ASSERT_EQ(pool.size(), 4u);
338 ASSERT_EQ(pool.begin()->value, 7);
339 ASSERT_EQ(pool.at(0u), entities[1u]);
340 ASSERT_EQ(pool.at(1u), entities[0u]);
341 ASSERT_EQ(pool.at(2u), entities[2u]);
342
343 ASSERT_EQ(pool.get(entities[0u]).value, 99);
344 ASSERT_EQ(pool.get(entities[1u]).value, 2);
345 ASSERT_EQ(pool.get(entities[2u]).value, 1);
346 }
347
TEST(Storage,Remove)348 TEST(Storage, Remove) {
349 entt::storage<int> pool;
350 entt::entity entities[3u];
351
352 entities[0u] = entt::entity{3};
353 entities[1u] = entt::entity{42};
354 entities[2u] = entt::entity{9};
355
356 pool.emplace(entities[0u]);
357 pool.emplace(entities[1u]);
358 pool.emplace(entities[2u]);
359
360 ASSERT_EQ(pool.remove(std::begin(entities), std::end(entities)), 3u);
361 ASSERT_EQ(pool.remove(std::begin(entities), std::end(entities)), 0u);
362 ASSERT_TRUE(pool.empty());
363
364 pool.emplace(entities[0u], 0);
365 pool.emplace(entities[1u], 1);
366 pool.emplace(entities[2u], 2);
367
368 ASSERT_EQ(pool.remove(entities, entities + 2u), 2u);
369 ASSERT_FALSE(pool.empty());
370 ASSERT_EQ(*pool.begin(), 2);
371
372 ASSERT_EQ(pool.remove(entities[2u]), 1u);
373 ASSERT_EQ(pool.remove(entities[2u]), 0u);
374 ASSERT_TRUE(pool.empty());
375
376 pool.emplace(entities[0u], 0);
377 pool.emplace(entities[1u], 1);
378 pool.emplace(entities[2u], 2);
379 std::swap(entities[1u], entities[2u]);
380
381 ASSERT_EQ(pool.remove(entities, entities + 2u), 2u);
382 ASSERT_FALSE(pool.empty());
383 ASSERT_EQ(*pool.begin(), 1);
384 }
385
TEST(Storage,StableRemove)386 TEST(Storage, StableRemove) {
387 entt::storage<stable_type> pool;
388 entt::entity entities[3u];
389
390 entities[0u] = entt::entity{3};
391 entities[1u] = entt::entity{42};
392 entities[2u] = entt::entity{9};
393
394 pool.emplace(entities[0u], stable_type{0});
395 pool.emplace(entities[1u], stable_type{1});
396 pool.emplace(entities[2u], stable_type{2});
397
398 ASSERT_EQ(pool.remove(std::begin(entities), std::end(entities)), 3u);
399 ASSERT_EQ(pool.remove(std::begin(entities), std::end(entities)), 0u);
400 ASSERT_FALSE(pool.empty());
401 ASSERT_EQ(pool.size(), 3u);
402 ASSERT_TRUE(pool.at(2u) == entt::tombstone);
403 ASSERT_DEATH([[maybe_unused]] auto &&value = pool.get(entt::tombstone), "");
404 ASSERT_DEATH([[maybe_unused]] auto &&value = pool.get(entt::null), "");
405 ASSERT_DEATH([[maybe_unused]] auto &&value = pool.get(entities[1u]), "");
406
407 pool.emplace(entities[2u], stable_type{2});
408 pool.emplace(entities[0u], stable_type{0});
409 pool.emplace(entities[1u], stable_type{1});
410
411 ASSERT_EQ(pool.get(entities[0u]).value, 0);
412 ASSERT_EQ(pool.get(entities[1u]).value, 1);
413 ASSERT_EQ(pool.get(entities[2u]).value, 2);
414
415 ASSERT_EQ(pool.begin()->value, 2);
416 ASSERT_EQ(pool.index(entities[0u]), 1u);
417 ASSERT_EQ(pool.index(entities[1u]), 0u);
418 ASSERT_EQ(pool.index(entities[2u]), 2u);
419
420 ASSERT_EQ(pool.remove(entities, entities + 2u), 2u);
421 ASSERT_FALSE(pool.empty());
422 ASSERT_EQ(pool.size(), 3u);
423 ASSERT_EQ(pool.begin()->value, 2);
424 ASSERT_EQ(pool.index(entities[2u]), 2u);
425
426 ASSERT_EQ(pool.remove(entities[2u]), 1u);
427 ASSERT_EQ(pool.remove(entities[2u]), 0u);
428 ASSERT_EQ(pool.remove(entities[2u]), 0u);
429 ASSERT_FALSE(pool.empty());
430 ASSERT_EQ(pool.size(), 3u);
431 ASSERT_FALSE(pool.contains(entities[0u]));
432 ASSERT_FALSE(pool.contains(entities[1u]));
433 ASSERT_FALSE(pool.contains(entities[2u]));
434
435 pool.emplace(entities[0u], stable_type{0});
436 pool.emplace(entities[1u], stable_type{1});
437 pool.emplace(entities[2u], stable_type{2});
438 std::swap(entities[1u], entities[2u]);
439
440 ASSERT_EQ(pool.remove(entities, entities + 2u), 2u);
441 ASSERT_FALSE(pool.empty());
442 ASSERT_EQ(pool.size(), 3u);
443 ASSERT_TRUE(pool.contains(entities[2u]));
444 ASSERT_EQ(pool.index(entities[2u]), 0u);
445 ASSERT_EQ(pool.get(entities[2u]).value, 1);
446
447 pool.compact();
448
449 ASSERT_FALSE(pool.empty());
450 ASSERT_EQ(pool.size(), 1u);
451 ASSERT_EQ(pool.begin()->value, 1);
452
453 pool.clear();
454
455 ASSERT_EQ(pool.size(), 0u);
456
457 pool.emplace(entities[0u], stable_type{0});
458 pool.emplace(entities[1u], stable_type{2});
459 pool.emplace(entities[2u], stable_type{1});
460
461 ASSERT_EQ(pool.remove(entities[2u]), 1u);
462 ASSERT_EQ(pool.remove(entities[2u]), 0u);
463
464 ASSERT_EQ(pool.remove(entities[0u]), 1u);
465 ASSERT_EQ(pool.remove(entities[1u]), 1u);
466 ASSERT_EQ(pool.remove(entities, entities + 2u), 0u);
467
468 ASSERT_EQ(pool.size(), 3u);
469 ASSERT_TRUE(pool.at(2u) == entt::tombstone);
470
471 pool.emplace(entities[0u], stable_type{99});
472
473 ASSERT_EQ((++pool.begin())->value, 99);
474
475 pool.emplace(entities[1u], stable_type{2});
476 pool.emplace(entities[2u], stable_type{1});
477 pool.emplace(entt::entity{0}, stable_type{7});
478
479 ASSERT_EQ(pool.size(), 4u);
480 ASSERT_EQ(pool.begin()->value, 7);
481 ASSERT_EQ(pool.at(0u), entities[1u]);
482 ASSERT_EQ(pool.at(1u), entities[0u]);
483 ASSERT_EQ(pool.at(2u), entities[2u]);
484
485 ASSERT_EQ(pool.get(entities[0u]).value, 99);
486 ASSERT_EQ(pool.get(entities[1u]).value, 2);
487 ASSERT_EQ(pool.get(entities[2u]).value, 1);
488 }
489
TEST(Storage,Compact)490 TEST(Storage, Compact) {
491 entt::storage<stable_type> pool;
492
493 ASSERT_TRUE(pool.empty());
494 ASSERT_EQ(pool.size(), 0u);
495
496 pool.compact();
497
498 ASSERT_TRUE(pool.empty());
499 ASSERT_EQ(pool.size(), 0u);
500
501 pool.emplace(entt::entity{0}, stable_type{0});
502 pool.compact();
503
504 ASSERT_FALSE(pool.empty());
505 ASSERT_EQ(pool.size(), 1u);
506
507 pool.emplace(entt::entity{42}, stable_type{42});
508 pool.erase(entt::entity{0});
509
510 ASSERT_EQ(pool.size(), 2u);
511 ASSERT_EQ(pool.index(entt::entity{42}), 1u);
512 ASSERT_EQ(pool.get(entt::entity{42}).value, 42);
513
514 pool.compact();
515
516 ASSERT_EQ(pool.size(), 1u);
517 ASSERT_EQ(pool.index(entt::entity{42}), 0u);
518 ASSERT_EQ(pool.get(entt::entity{42}).value, 42);
519
520 pool.emplace(entt::entity{0}, stable_type{0});
521 pool.compact();
522
523 ASSERT_EQ(pool.size(), 2u);
524 ASSERT_EQ(pool.index(entt::entity{42}), 0u);
525 ASSERT_EQ(pool.index(entt::entity{0}), 1u);
526 ASSERT_EQ(pool.get(entt::entity{42}).value, 42);
527 ASSERT_EQ(pool.get(entt::entity{0}).value, 0);
528
529 pool.erase(entt::entity{0});
530 pool.erase(entt::entity{42});
531 pool.compact();
532
533 ASSERT_TRUE(pool.empty());
534 }
535
TEST(Storage,ShrinkToFit)536 TEST(Storage, ShrinkToFit) {
537 entt::storage<int> pool;
538
539 for(std::size_t next{}; next < ENTT_PACKED_PAGE; ++next) {
540 pool.emplace(entt::entity(next));
541 }
542
543 pool.emplace(entt::entity{ENTT_PACKED_PAGE});
544 pool.erase(entt::entity{ENTT_PACKED_PAGE});
545
546 ASSERT_EQ(pool.capacity(), 2 * ENTT_PACKED_PAGE);
547 ASSERT_EQ(pool.size(), ENTT_PACKED_PAGE);
548
549 pool.shrink_to_fit();
550
551 ASSERT_EQ(pool.capacity(), ENTT_PACKED_PAGE);
552 ASSERT_EQ(pool.size(), ENTT_PACKED_PAGE);
553
554 pool.clear();
555
556 ASSERT_EQ(pool.capacity(), ENTT_PACKED_PAGE);
557 ASSERT_EQ(pool.size(), 0u);
558
559 pool.shrink_to_fit();
560
561 ASSERT_EQ(pool.capacity(), 0u);
562 ASSERT_EQ(pool.size(), 0u);
563 }
564
TEST(Storage,AggregatesMustWork)565 TEST(Storage, AggregatesMustWork) {
566 struct aggregate_type { int value; };
567 // the goal of this test is to enforce the requirements for aggregate types
568 entt::storage<aggregate_type>{}.emplace(entt::entity{0}, 42);
569 }
570
TEST(Storage,TypesFromStandardTemplateLibraryMustWork)571 TEST(Storage, TypesFromStandardTemplateLibraryMustWork) {
572 // see #37 - this test shouldn't crash, that's all
573 entt::storage<std::unordered_set<int>> pool;
574 pool.emplace(entt::entity{0}).insert(42);
575 pool.erase(entt::entity{0});
576 }
577
TEST(Storage,Iterator)578 TEST(Storage, Iterator) {
579 using iterator = typename entt::storage<boxed_int>::iterator;
580
581 entt::storage<boxed_int> pool;
582 pool.emplace(entt::entity{3}, 42);
583
584 iterator end{pool.begin()};
585 iterator begin{};
586 begin = pool.end();
587 std::swap(begin, end);
588
589 ASSERT_EQ(begin, pool.begin());
590 ASSERT_EQ(end, pool.end());
591 ASSERT_NE(begin, end);
592
593 ASSERT_EQ(begin++, pool.begin());
594 ASSERT_EQ(begin--, pool.end());
595
596 ASSERT_EQ(begin+1, pool.end());
597 ASSERT_EQ(end-1, pool.begin());
598
599 ASSERT_EQ(++begin, pool.end());
600 ASSERT_EQ(--begin, pool.begin());
601
602 ASSERT_EQ(begin += 1, pool.end());
603 ASSERT_EQ(begin -= 1, pool.begin());
604
605 ASSERT_EQ(begin + (end - begin), pool.end());
606 ASSERT_EQ(begin - (begin - end), pool.end());
607
608 ASSERT_EQ(end - (end - begin), pool.begin());
609 ASSERT_EQ(end + (begin - end), pool.begin());
610
611 ASSERT_EQ(begin[0u].value, pool.begin()->value);
612
613 ASSERT_LT(begin, end);
614 ASSERT_LE(begin, pool.begin());
615
616 ASSERT_GT(end, begin);
617 ASSERT_GE(end, pool.end());
618 }
619
TEST(Storage,ConstIterator)620 TEST(Storage, ConstIterator) {
621 using iterator = typename entt::storage<boxed_int>::const_iterator;
622
623 entt::storage<boxed_int> pool;
624 pool.emplace(entt::entity{3}, 42);
625
626 iterator cend{pool.cbegin()};
627 iterator cbegin{};
628 cbegin = pool.cend();
629 std::swap(cbegin, cend);
630
631 ASSERT_EQ(cbegin, pool.cbegin());
632 ASSERT_EQ(cend, pool.cend());
633 ASSERT_NE(cbegin, cend);
634
635 ASSERT_EQ(cbegin++, pool.cbegin());
636 ASSERT_EQ(cbegin--, pool.cend());
637
638 ASSERT_EQ(cbegin+1, pool.cend());
639 ASSERT_EQ(cend-1, pool.cbegin());
640
641 ASSERT_EQ(++cbegin, pool.cend());
642 ASSERT_EQ(--cbegin, pool.cbegin());
643
644 ASSERT_EQ(cbegin += 1, pool.cend());
645 ASSERT_EQ(cbegin -= 1, pool.cbegin());
646
647 ASSERT_EQ(cbegin + (cend - cbegin), pool.cend());
648 ASSERT_EQ(cbegin - (cbegin - cend), pool.cend());
649
650 ASSERT_EQ(cend - (cend - cbegin), pool.cbegin());
651 ASSERT_EQ(cend + (cbegin - cend), pool.cbegin());
652
653 ASSERT_EQ(cbegin[0u].value, pool.cbegin()->value);
654
655 ASSERT_LT(cbegin, cend);
656 ASSERT_LE(cbegin, pool.cbegin());
657
658 ASSERT_GT(cend, cbegin);
659 ASSERT_GE(cend, pool.cend());
660 }
661
TEST(Storage,ReverseIterator)662 TEST(Storage, ReverseIterator) {
663 using reverse_iterator = typename entt::storage<boxed_int>::reverse_iterator;
664
665 entt::storage<boxed_int> pool;
666 pool.emplace(entt::entity{3}, 42);
667
668 reverse_iterator end{pool.rbegin()};
669 reverse_iterator begin{};
670 begin = pool.rend();
671 std::swap(begin, end);
672
673 ASSERT_EQ(begin, pool.rbegin());
674 ASSERT_EQ(end, pool.rend());
675 ASSERT_NE(begin, end);
676
677 ASSERT_EQ(begin++, pool.rbegin());
678 ASSERT_EQ(begin--, pool.rend());
679
680 ASSERT_EQ(begin+1, pool.rend());
681 ASSERT_EQ(end-1, pool.rbegin());
682
683 ASSERT_EQ(++begin, pool.rend());
684 ASSERT_EQ(--begin, pool.rbegin());
685
686 ASSERT_EQ(begin += 1, pool.rend());
687 ASSERT_EQ(begin -= 1, pool.rbegin());
688
689 ASSERT_EQ(begin + (end - begin), pool.rend());
690 ASSERT_EQ(begin - (begin - end), pool.rend());
691
692 ASSERT_EQ(end - (end - begin), pool.rbegin());
693 ASSERT_EQ(end + (begin - end), pool.rbegin());
694
695 ASSERT_EQ(begin[0u].value, pool.rbegin()->value);
696
697 ASSERT_LT(begin, end);
698 ASSERT_LE(begin, pool.rbegin());
699
700 ASSERT_GT(end, begin);
701 ASSERT_GE(end, pool.rend());
702 }
703
TEST(Storage,ConstReverseIterator)704 TEST(Storage, ConstReverseIterator) {
705 using const_reverse_iterator = typename entt::storage<boxed_int>::const_reverse_iterator;
706
707 entt::storage<boxed_int> pool;
708 pool.emplace(entt::entity{3}, 42);
709
710 const_reverse_iterator cend{pool.crbegin()};
711 const_reverse_iterator cbegin{};
712 cbegin = pool.crend();
713 std::swap(cbegin, cend);
714
715 ASSERT_EQ(cbegin, pool.crbegin());
716 ASSERT_EQ(cend, pool.crend());
717 ASSERT_NE(cbegin, cend);
718
719 ASSERT_EQ(cbegin++, pool.crbegin());
720 ASSERT_EQ(cbegin--, pool.crend());
721
722 ASSERT_EQ(cbegin+1, pool.crend());
723 ASSERT_EQ(cend-1, pool.crbegin());
724
725 ASSERT_EQ(++cbegin, pool.crend());
726 ASSERT_EQ(--cbegin, pool.crbegin());
727
728 ASSERT_EQ(cbegin += 1, pool.crend());
729 ASSERT_EQ(cbegin -= 1, pool.crbegin());
730
731 ASSERT_EQ(cbegin + (cend - cbegin), pool.crend());
732 ASSERT_EQ(cbegin - (cbegin - cend), pool.crend());
733
734 ASSERT_EQ(cend - (cend - cbegin), pool.crbegin());
735 ASSERT_EQ(cend + (cbegin - cend), pool.crbegin());
736
737 ASSERT_EQ(cbegin[0u].value, pool.crbegin()->value);
738
739 ASSERT_LT(cbegin, cend);
740 ASSERT_LE(cbegin, pool.crbegin());
741
742 ASSERT_GT(cend, cbegin);
743 ASSERT_GE(cend, pool.crend());
744 }
745
TEST(Storage,Raw)746 TEST(Storage, Raw) {
747 entt::storage<int> pool;
748
749 pool.emplace(entt::entity{3}, 3);
750 pool.emplace(entt::entity{12}, 6);
751 pool.emplace(entt::entity{42}, 9);
752
753 ASSERT_EQ(pool.get(entt::entity{3}), 3);
754 ASSERT_EQ(std::as_const(pool).get(entt::entity{12}), 6);
755 ASSERT_EQ(pool.get(entt::entity{42}), 9);
756
757 ASSERT_EQ(pool.raw()[0u][0u], 3);
758 ASSERT_EQ(std::as_const(pool).raw()[0u][1u], 6);
759 ASSERT_EQ(pool.raw()[0u][2u], 9);
760 }
761
TEST(Storage,SortOrdered)762 TEST(Storage, SortOrdered) {
763 entt::storage<boxed_int> pool;
764 entt::entity entities[5u]{entt::entity{12}, entt::entity{42}, entt::entity{7}, entt::entity{3}, entt::entity{9}};
765 boxed_int values[5u]{{12}, {9}, {6}, {3}, {1}};
766
767 pool.insert(std::begin(entities), std::end(entities), values);
768 pool.sort([](auto lhs, auto rhs) { return lhs.value < rhs.value; });
769
770 ASSERT_TRUE(std::equal(std::rbegin(entities), std::rend(entities), pool.entt::sparse_set::begin(), pool.entt::sparse_set::end()));
771 ASSERT_TRUE(std::equal(std::rbegin(values), std::rend(values), pool.begin(), pool.end()));
772 }
773
TEST(Storage,SortReverse)774 TEST(Storage, SortReverse) {
775 entt::storage<boxed_int> pool;
776 entt::entity entities[5u]{entt::entity{12}, entt::entity{42}, entt::entity{7}, entt::entity{3}, entt::entity{9}};
777 boxed_int values[5u]{{1}, {3}, {6}, {9}, {12}};
778
779 pool.insert(std::begin(entities), std::end(entities), values);
780 pool.sort([](auto lhs, auto rhs) { return lhs.value < rhs.value; });
781
782 ASSERT_TRUE(std::equal(std::begin(entities), std::end(entities), pool.entt::sparse_set::begin(), pool.entt::sparse_set::end()));
783 ASSERT_TRUE(std::equal(std::begin(values), std::end(values), pool.begin(), pool.end()));
784 }
785
TEST(Storage,SortUnordered)786 TEST(Storage, SortUnordered) {
787 entt::storage<boxed_int> pool;
788 entt::entity entities[5u]{entt::entity{12}, entt::entity{42}, entt::entity{7}, entt::entity{3}, entt::entity{9}};
789 boxed_int values[5u]{{6}, {3}, {1}, {9}, {12}};
790
791 pool.insert(std::begin(entities), std::end(entities), values);
792 pool.sort([](auto lhs, auto rhs) { return lhs.value < rhs.value; });
793
794 auto begin = pool.begin();
795 auto end = pool.end();
796
797 ASSERT_EQ(*(begin++), boxed_int{1});
798 ASSERT_EQ(*(begin++), boxed_int{3});
799 ASSERT_EQ(*(begin++), boxed_int{6});
800 ASSERT_EQ(*(begin++), boxed_int{9});
801 ASSERT_EQ(*(begin++), boxed_int{12});
802 ASSERT_EQ(begin, end);
803
804 ASSERT_EQ(pool.data()[0u], entt::entity{9});
805 ASSERT_EQ(pool.data()[1u], entt::entity{3});
806 ASSERT_EQ(pool.data()[2u], entt::entity{12});
807 ASSERT_EQ(pool.data()[3u], entt::entity{42});
808 ASSERT_EQ(pool.data()[4u], entt::entity{7});
809 }
810
TEST(Storage,SortRange)811 TEST(Storage, SortRange) {
812 entt::storage<boxed_int> pool;
813 entt::entity entities[5u]{entt::entity{12}, entt::entity{42}, entt::entity{7}, entt::entity{3}, entt::entity{9}};
814 boxed_int values[5u]{{3}, {6}, {1}, {9}, {12}};
815
816 pool.insert(std::begin(entities), std::end(entities), values);
817 pool.sort_n(0u, [](auto lhs, auto rhs) { return lhs.value < rhs.value; });
818
819 ASSERT_TRUE(std::equal(std::rbegin(entities), std::rend(entities), pool.entt::sparse_set::begin(), pool.entt::sparse_set::end()));
820 ASSERT_TRUE(std::equal(std::rbegin(values), std::rend(values), pool.begin(), pool.end()));
821
822 pool.sort_n(2u, [](auto lhs, auto rhs) { return lhs.value < rhs.value; });
823
824 ASSERT_EQ(pool.raw()[0u][0u], boxed_int{6});
825 ASSERT_EQ(pool.raw()[0u][1u], boxed_int{3});
826 ASSERT_EQ(pool.raw()[0u][2u], boxed_int{1});
827
828 ASSERT_EQ(pool.data()[0u], entt::entity{42});
829 ASSERT_EQ(pool.data()[1u], entt::entity{12});
830 ASSERT_EQ(pool.data()[2u], entt::entity{7});
831
832 pool.sort_n(5u, [](auto lhs, auto rhs) { return lhs.value < rhs.value; });
833
834 auto begin = pool.begin();
835 auto end = pool.end();
836
837 ASSERT_EQ(*(begin++), boxed_int{1});
838 ASSERT_EQ(*(begin++), boxed_int{3});
839 ASSERT_EQ(*(begin++), boxed_int{6});
840 ASSERT_EQ(*(begin++), boxed_int{9});
841 ASSERT_EQ(*(begin++), boxed_int{12});
842 ASSERT_EQ(begin, end);
843
844 ASSERT_EQ(pool.data()[0u], entt::entity{9});
845 ASSERT_EQ(pool.data()[1u], entt::entity{3});
846 ASSERT_EQ(pool.data()[2u], entt::entity{42});
847 ASSERT_EQ(pool.data()[3u], entt::entity{12});
848 ASSERT_EQ(pool.data()[4u], entt::entity{7});
849 }
850
TEST(Storage,RespectDisjoint)851 TEST(Storage, RespectDisjoint) {
852 entt::storage<int> lhs;
853 entt::storage<int> rhs;
854
855 entt::entity lhs_entities[3u]{entt::entity{3}, entt::entity{12}, entt::entity{42}};
856 int lhs_values[3u]{3, 6, 9};
857 lhs.insert(std::begin(lhs_entities), std::end(lhs_entities), lhs_values);
858
859 ASSERT_TRUE(std::equal(std::rbegin(lhs_entities), std::rend(lhs_entities), lhs.entt::sparse_set::begin(), lhs.entt::sparse_set::end()));
860 ASSERT_TRUE(std::equal(std::rbegin(lhs_values), std::rend(lhs_values), lhs.begin(), lhs.end()));
861
862 lhs.respect(rhs);
863
864 ASSERT_TRUE(std::equal(std::rbegin(lhs_entities), std::rend(lhs_entities), lhs.entt::sparse_set::begin(), lhs.entt::sparse_set::end()));
865 ASSERT_TRUE(std::equal(std::rbegin(lhs_values), std::rend(lhs_values), lhs.begin(), lhs.end()));
866 }
867
TEST(Storage,RespectOverlap)868 TEST(Storage, RespectOverlap) {
869 entt::storage<int> lhs;
870 entt::storage<int> rhs;
871
872 entt::entity lhs_entities[3u]{entt::entity{3}, entt::entity{12}, entt::entity{42}};
873 int lhs_values[3u]{3, 6, 9};
874 lhs.insert(std::begin(lhs_entities), std::end(lhs_entities), lhs_values);
875
876 entt::entity rhs_entities[1u]{entt::entity{12}};
877 int rhs_values[1u]{6};
878 rhs.insert(std::begin(rhs_entities), std::end(rhs_entities), rhs_values);
879
880 ASSERT_TRUE(std::equal(std::rbegin(lhs_entities), std::rend(lhs_entities), lhs.entt::sparse_set::begin(), lhs.entt::sparse_set::end()));
881 ASSERT_TRUE(std::equal(std::rbegin(lhs_values), std::rend(lhs_values), lhs.begin(), lhs.end()));
882
883 ASSERT_TRUE(std::equal(std::rbegin(rhs_entities), std::rend(rhs_entities), rhs.entt::sparse_set::begin(), rhs.entt::sparse_set::end()));
884 ASSERT_TRUE(std::equal(std::rbegin(rhs_values), std::rend(rhs_values), rhs.begin(), rhs.end()));
885
886 lhs.respect(rhs);
887
888 auto begin = lhs.begin();
889 auto end = lhs.end();
890
891 ASSERT_EQ(*(begin++), 6);
892 ASSERT_EQ(*(begin++), 9);
893 ASSERT_EQ(*(begin++), 3);
894 ASSERT_EQ(begin, end);
895
896 ASSERT_EQ(lhs.data()[0u], entt::entity{3});
897 ASSERT_EQ(lhs.data()[1u], entt::entity{42});
898 ASSERT_EQ(lhs.data()[2u], entt::entity{12});
899 }
900
TEST(Storage,RespectOrdered)901 TEST(Storage, RespectOrdered) {
902 entt::storage<int> lhs;
903 entt::storage<int> rhs;
904
905 entt::entity lhs_entities[5u]{entt::entity{1}, entt::entity{2}, entt::entity{3}, entt::entity{4}, entt::entity{5}};
906 int lhs_values[5u]{1, 2, 3, 4, 5};
907 lhs.insert(std::begin(lhs_entities), std::end(lhs_entities), lhs_values);
908
909 entt::entity rhs_entities[6u]{entt::entity{6}, entt::entity{1}, entt::entity{2}, entt::entity{3}, entt::entity{4}, entt::entity{5}};
910 int rhs_values[6u]{6, 1, 2, 3, 4, 5};
911 rhs.insert(std::begin(rhs_entities), std::end(rhs_entities), rhs_values);
912
913 ASSERT_TRUE(std::equal(std::rbegin(lhs_entities), std::rend(lhs_entities), lhs.entt::sparse_set::begin(), lhs.entt::sparse_set::end()));
914 ASSERT_TRUE(std::equal(std::rbegin(lhs_values), std::rend(lhs_values), lhs.begin(), lhs.end()));
915
916 ASSERT_TRUE(std::equal(std::rbegin(rhs_entities), std::rend(rhs_entities), rhs.entt::sparse_set::begin(), rhs.entt::sparse_set::end()));
917 ASSERT_TRUE(std::equal(std::rbegin(rhs_values), std::rend(rhs_values), rhs.begin(), rhs.end()));
918
919 rhs.respect(lhs);
920
921 ASSERT_TRUE(std::equal(std::rbegin(rhs_entities), std::rend(rhs_entities), rhs.entt::sparse_set::begin(), rhs.entt::sparse_set::end()));
922 ASSERT_TRUE(std::equal(std::rbegin(rhs_values), std::rend(rhs_values), rhs.begin(), rhs.end()));
923 }
924
TEST(Storage,RespectReverse)925 TEST(Storage, RespectReverse) {
926 entt::storage<int> lhs;
927 entt::storage<int> rhs;
928
929 entt::entity lhs_entities[5u]{entt::entity{1}, entt::entity{2}, entt::entity{3}, entt::entity{4}, entt::entity{5}};
930 int lhs_values[5u]{1, 2, 3, 4, 5};
931 lhs.insert(std::begin(lhs_entities), std::end(lhs_entities), lhs_values);
932
933 entt::entity rhs_entities[6u]{entt::entity{5}, entt::entity{4}, entt::entity{3}, entt::entity{2}, entt::entity{1}, entt::entity{6}};
934 int rhs_values[6u]{5, 4, 3, 2, 1, 6};
935 rhs.insert(std::begin(rhs_entities), std::end(rhs_entities), rhs_values);
936
937 ASSERT_TRUE(std::equal(std::rbegin(lhs_entities), std::rend(lhs_entities), lhs.entt::sparse_set::begin(), lhs.entt::sparse_set::end()));
938 ASSERT_TRUE(std::equal(std::rbegin(lhs_values), std::rend(lhs_values), lhs.begin(), lhs.end()));
939
940 ASSERT_TRUE(std::equal(std::rbegin(rhs_entities), std::rend(rhs_entities), rhs.entt::sparse_set::begin(), rhs.entt::sparse_set::end()));
941 ASSERT_TRUE(std::equal(std::rbegin(rhs_values), std::rend(rhs_values), rhs.begin(), rhs.end()));
942
943 rhs.respect(lhs);
944
945 auto begin = rhs.begin();
946 auto end = rhs.end();
947
948 ASSERT_EQ(*(begin++), 5);
949 ASSERT_EQ(*(begin++), 4);
950 ASSERT_EQ(*(begin++), 3);
951 ASSERT_EQ(*(begin++), 2);
952 ASSERT_EQ(*(begin++), 1);
953 ASSERT_EQ(*(begin++), 6);
954 ASSERT_EQ(begin, end);
955
956 ASSERT_EQ(rhs.data()[0u], entt::entity{6});
957 ASSERT_EQ(rhs.data()[1u], entt::entity{1});
958 ASSERT_EQ(rhs.data()[2u], entt::entity{2});
959 ASSERT_EQ(rhs.data()[3u], entt::entity{3});
960 ASSERT_EQ(rhs.data()[4u], entt::entity{4});
961 ASSERT_EQ(rhs.data()[5u], entt::entity{5});
962 }
963
TEST(Storage,RespectUnordered)964 TEST(Storage, RespectUnordered) {
965 entt::storage<int> lhs;
966 entt::storage<int> rhs;
967
968 entt::entity lhs_entities[5u]{entt::entity{1}, entt::entity{2}, entt::entity{3}, entt::entity{4}, entt::entity{5}};
969 int lhs_values[5u]{1, 2, 3, 4, 5};
970 lhs.insert(std::begin(lhs_entities), std::end(lhs_entities), lhs_values);
971
972 entt::entity rhs_entities[6u]{entt::entity{3}, entt::entity{2}, entt::entity{6}, entt::entity{1}, entt::entity{4}, entt::entity{5}};
973 int rhs_values[6u]{3, 2, 6, 1, 4, 5};
974 rhs.insert(std::begin(rhs_entities), std::end(rhs_entities), rhs_values);
975
976 ASSERT_TRUE(std::equal(std::rbegin(lhs_entities), std::rend(lhs_entities), lhs.entt::sparse_set::begin(), lhs.entt::sparse_set::end()));
977 ASSERT_TRUE(std::equal(std::rbegin(lhs_values), std::rend(lhs_values), lhs.begin(), lhs.end()));
978
979 ASSERT_TRUE(std::equal(std::rbegin(rhs_entities), std::rend(rhs_entities), rhs.entt::sparse_set::begin(), rhs.entt::sparse_set::end()));
980 ASSERT_TRUE(std::equal(std::rbegin(rhs_values), std::rend(rhs_values), rhs.begin(), rhs.end()));
981
982 rhs.respect(lhs);
983
984 auto begin = rhs.begin();
985 auto end = rhs.end();
986
987 ASSERT_EQ(*(begin++), 5);
988 ASSERT_EQ(*(begin++), 4);
989 ASSERT_EQ(*(begin++), 3);
990 ASSERT_EQ(*(begin++), 2);
991 ASSERT_EQ(*(begin++), 1);
992 ASSERT_EQ(*(begin++), 6);
993 ASSERT_EQ(begin, end);
994
995 ASSERT_EQ(rhs.data()[0u], entt::entity{6});
996 ASSERT_EQ(rhs.data()[1u], entt::entity{1});
997 ASSERT_EQ(rhs.data()[2u], entt::entity{2});
998 ASSERT_EQ(rhs.data()[3u], entt::entity{3});
999 ASSERT_EQ(rhs.data()[4u], entt::entity{4});
1000 ASSERT_EQ(rhs.data()[5u], entt::entity{5});
1001 }
1002
TEST(Storage,CanModifyDuringIteration)1003 TEST(Storage, CanModifyDuringIteration) {
1004 entt::storage<int> pool;
1005 pool.emplace(entt::entity{0}, 42);
1006
1007 ASSERT_EQ(pool.capacity(), ENTT_PACKED_PAGE);
1008
1009 const auto it = pool.cbegin();
1010 pool.reserve(ENTT_PACKED_PAGE + 1u);
1011
1012 ASSERT_EQ(pool.capacity(), 2 * ENTT_PACKED_PAGE);
1013
1014 // this should crash with asan enabled if we break the constraint
1015 const auto entity = *it;
1016 (void)entity;
1017 }
1018
TEST(Storage,ReferencesGuaranteed)1019 TEST(Storage, ReferencesGuaranteed) {
1020 entt::storage<boxed_int> pool;
1021
1022 pool.emplace(entt::entity{0}, 0);
1023 pool.emplace(entt::entity{1}, 1);
1024
1025 ASSERT_EQ(pool.get(entt::entity{0}).value, 0);
1026 ASSERT_EQ(pool.get(entt::entity{1}).value, 1);
1027
1028 for(auto &&type: pool) {
1029 if(type.value) {
1030 type.value = 42;
1031 }
1032 }
1033
1034 ASSERT_EQ(pool.get(entt::entity{0}).value, 0);
1035 ASSERT_EQ(pool.get(entt::entity{1}).value, 42);
1036
1037 auto begin = pool.begin();
1038
1039 while(begin != pool.end()) {
1040 (begin++)->value = 3;
1041 }
1042
1043 ASSERT_EQ(pool.get(entt::entity{0}).value, 3);
1044 ASSERT_EQ(pool.get(entt::entity{1}).value, 3);
1045 }
1046
TEST(Storage,MoveOnlyComponent)1047 TEST(Storage, MoveOnlyComponent) {
1048 // the purpose is to ensure that move only components are always accepted
1049 entt::storage<std::unique_ptr<int>> pool;
1050 (void)pool;
1051 }
1052
TEST(Storage,UpdateFromDestructor)1053 TEST(Storage, UpdateFromDestructor) {
1054 static constexpr auto size = 10u;
1055
1056 auto test = [](const auto target) {
1057 entt::storage<update_from_destructor> pool;
1058
1059 for(std::size_t next{}; next < size; ++next) {
1060 const auto entity = entt::entity(next);
1061 pool.emplace(entity, pool, entity == entt::entity(size/2) ? target : entity);
1062 }
1063
1064 pool.erase(entt::entity(size/2));
1065
1066 ASSERT_EQ(pool.size(), size - 1u - (target != entt::null));
1067 ASSERT_FALSE(pool.contains(entt::entity(size/2)));
1068 ASSERT_FALSE(pool.contains(target));
1069
1070 pool.clear();
1071
1072 ASSERT_TRUE(pool.empty());
1073
1074 for(std::size_t next{}; next < size; ++next) {
1075 ASSERT_FALSE(pool.contains(entt::entity(next)));
1076 }
1077 };
1078
1079 test(entt::entity(size - 1u));
1080 test(entt::entity(size - 2u));
1081 test(entt::entity{0u});
1082 }
1083
TEST(Storage,ThrowingComponent)1084 TEST(Storage, ThrowingComponent) {
1085 entt::storage<test::throwing_component> pool;
1086 test::throwing_component::trigger_on_value = 42;
1087
1088 // strong exception safety
1089 ASSERT_THROW(pool.emplace(entt::entity{0}, test::throwing_component{42}), typename test::throwing_component::exception_type);
1090 ASSERT_TRUE(pool.empty());
1091
1092 const entt::entity entities[2u]{entt::entity{42}, entt::entity{1}};
1093 const test::throwing_component components[2u]{42, 1};
1094
1095 // basic exception safety
1096 ASSERT_THROW(pool.insert(std::begin(entities), std::end(entities), test::throwing_component{42}), typename test::throwing_component::exception_type);
1097 ASSERT_EQ(pool.size(), 0u);
1098 ASSERT_FALSE(pool.contains(entt::entity{1}));
1099
1100 // basic exception safety
1101 ASSERT_THROW(pool.insert(std::begin(entities), std::end(entities), std::begin(components)), typename test::throwing_component::exception_type);
1102 ASSERT_EQ(pool.size(), 0u);
1103 ASSERT_FALSE(pool.contains(entt::entity{1}));
1104
1105 // basic exception safety
1106 ASSERT_THROW(pool.insert(std::rbegin(entities), std::rend(entities), std::rbegin(components)), typename test::throwing_component::exception_type);
1107 ASSERT_EQ(pool.size(), 1u);
1108 ASSERT_TRUE(pool.contains(entt::entity{1}));
1109 ASSERT_EQ(pool.get(entt::entity{1}), 1);
1110
1111 pool.clear();
1112 pool.emplace(entt::entity{1}, 1);
1113 pool.emplace(entt::entity{42}, 42);
1114
1115 // basic exception safety
1116 ASSERT_THROW(pool.erase(entt::entity{1}), typename test::throwing_component::exception_type);
1117 ASSERT_EQ(pool.size(), 2u);
1118 ASSERT_TRUE(pool.contains(entt::entity{42}));
1119 ASSERT_TRUE(pool.contains(entt::entity{1}));
1120 ASSERT_EQ(pool.at(0u), entt::entity{1});
1121 ASSERT_EQ(pool.at(1u), entt::entity{42});
1122 ASSERT_EQ(pool.get(entt::entity{42}), 42);
1123 // the element may have been moved but it's still there
1124 ASSERT_EQ(pool.get(entt::entity{1}), test::throwing_component::moved_from_value);
1125
1126 test::throwing_component::trigger_on_value = 99;
1127 pool.erase(entt::entity{1});
1128
1129 ASSERT_EQ(pool.size(), 1u);
1130 ASSERT_TRUE(pool.contains(entt::entity{42}));
1131 ASSERT_FALSE(pool.contains(entt::entity{1}));
1132 ASSERT_EQ(pool.at(0u), entt::entity{42});
1133 ASSERT_EQ(pool.get(entt::entity{42}), 42);
1134 }
1135
TEST(Storage,ThrowingAllocator)1136 TEST(Storage, ThrowingAllocator) {
1137 entt::basic_storage<entt::entity, int, test::throwing_allocator<int>> pool;
1138
1139 test::throwing_allocator<int>::trigger_on_allocate = true;
1140
1141 // strong exception safety
1142 ASSERT_THROW(pool.reserve(1u), test::throwing_allocator<int>::exception_type);
1143 ASSERT_EQ(pool.capacity(), 0u);
1144
1145 test::throwing_allocator<int>::trigger_after_allocate = true;
1146
1147 // strong exception safety
1148 ASSERT_THROW(pool.reserve(2 * ENTT_PACKED_PAGE), test::throwing_allocator<int>::exception_type);
1149 ASSERT_EQ(pool.capacity(), 0u);
1150
1151 pool.shrink_to_fit();
1152 test::throwing_allocator<int>::trigger_on_allocate = true;
1153
1154 // strong exception safety
1155 ASSERT_THROW(pool.emplace(entt::entity{0}, 0), test::throwing_allocator<int>::exception_type);
1156 ASSERT_FALSE(pool.contains(entt::entity{0}));
1157 ASSERT_TRUE(pool.empty());
1158
1159 test::throwing_allocator<entt::entity>::trigger_on_allocate = true;
1160
1161 // strong exception safety
1162 ASSERT_THROW(pool.emplace(entt::entity{0}, 0), test::throwing_allocator<entt::entity>::exception_type);
1163 ASSERT_FALSE(pool.contains(entt::entity{0}));
1164 ASSERT_TRUE(pool.empty());
1165
1166 pool.emplace(entt::entity{0}, 0);
1167 const entt::entity entities[2u]{entt::entity{1}, entt::entity{ENTT_SPARSE_PAGE}};
1168 test::throwing_allocator<entt::entity>::trigger_after_allocate = true;
1169
1170 // basic exception safety
1171 ASSERT_THROW(pool.insert(std::begin(entities), std::end(entities), 0), test::throwing_allocator<entt::entity>::exception_type);
1172 ASSERT_TRUE(pool.contains(entt::entity{1}));
1173 ASSERT_FALSE(pool.contains(entt::entity{ENTT_SPARSE_PAGE}));
1174
1175 pool.erase(entt::entity{1});
1176 const int components[2u]{1, ENTT_SPARSE_PAGE};
1177 test::throwing_allocator<entt::entity>::trigger_on_allocate = true;
1178
1179 // basic exception safety
1180 ASSERT_THROW(pool.insert(std::begin(entities), std::end(entities), std::begin(components)), test::throwing_allocator<entt::entity>::exception_type);
1181 ASSERT_TRUE(pool.contains(entt::entity{1}));
1182 ASSERT_FALSE(pool.contains(entt::entity{ENTT_SPARSE_PAGE}));
1183 }
1184