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