1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "nsTArray.h"
8 #include "gtest/gtest.h"
9 #include "mozilla/ArrayUtils.h"
10 #include "mozilla/RefPtr.h"
11 
12 using namespace mozilla;
13 
14 namespace TestTArray {
15 
16 struct Copyable {
CopyableTestTArray::Copyable17   Copyable() : mDestructionCounter(nullptr) {}
18 
~CopyableTestTArray::Copyable19   ~Copyable() {
20     if (mDestructionCounter) {
21       (*mDestructionCounter)++;
22     }
23   }
24 
25   Copyable(const Copyable&) = default;
26   Copyable& operator=(const Copyable&) = default;
27 
28   uint32_t* mDestructionCounter;
29 };
30 
31 struct Movable {
MovableTestTArray::Movable32   Movable() : mDestructionCounter(nullptr) {}
33 
~MovableTestTArray::Movable34   ~Movable() {
35     if (mDestructionCounter) {
36       (*mDestructionCounter)++;
37     }
38   }
39 
MovableTestTArray::Movable40   Movable(Movable&& aOther) : mDestructionCounter(aOther.mDestructionCounter) {
41     aOther.mDestructionCounter = nullptr;
42   }
43 
44   uint32_t* mDestructionCounter;
45 };
46 
47 }  // namespace TestTArray
48 
49 template <>
50 struct nsTArray_RelocationStrategy<TestTArray::Copyable> {
51   using Type = nsTArray_RelocateUsingMoveConstructor<TestTArray::Copyable>;
52 };
53 
54 template <>
55 struct nsTArray_RelocationStrategy<TestTArray::Movable> {
56   using Type = nsTArray_RelocateUsingMoveConstructor<TestTArray::Movable>;
57 };
58 
59 namespace TestTArray {
60 
61 constexpr int dummyArrayData[] = {4, 1, 2, 8};
62 
DummyArray()63 static const nsTArray<int>& DummyArray() {
64   static nsTArray<int> sArray;
65   if (sArray.IsEmpty()) {
66     sArray.AppendElements(dummyArrayData, ArrayLength(dummyArrayData));
67   }
68   return sArray;
69 }
70 
71 // This returns an invalid nsTArray with a huge length in order to test that
72 // fallible operations actually fail.
73 #ifdef DEBUG
FakeHugeArray()74 static const nsTArray<int>& FakeHugeArray() {
75   static nsTArray<int> sArray;
76   if (sArray.IsEmpty()) {
77     sArray.AppendElement();
78     ((nsTArrayHeader*)sArray.DebugGetHeader())->mLength = UINT32_MAX;
79   }
80   return sArray;
81 }
82 #endif
83 
TEST(TArray,int_AppendElements_PlainArray)84 TEST(TArray, int_AppendElements_PlainArray)
85 {
86   nsTArray<int> array;
87 
88   int* ptr = array.AppendElements(dummyArrayData, ArrayLength(dummyArrayData));
89   ASSERT_EQ(&array[0], ptr);
90   ASSERT_EQ(DummyArray(), array);
91 
92   ptr = array.AppendElements(dummyArrayData, ArrayLength(dummyArrayData));
93   ASSERT_EQ(&array[DummyArray().Length()], ptr);
94   nsTArray<int> expected;
95   expected.AppendElements(DummyArray());
96   expected.AppendElements(DummyArray());
97   ASSERT_EQ(expected, array);
98 }
99 
TEST(TArray,int_AppendElements_PlainArray_Fallible)100 TEST(TArray, int_AppendElements_PlainArray_Fallible)
101 {
102   nsTArray<int> array;
103 
104   int* ptr = array.AppendElements(dummyArrayData, ArrayLength(dummyArrayData),
105                                   fallible);
106   ASSERT_EQ(&array[0], ptr);
107   ASSERT_EQ(DummyArray(), array);
108 
109   ptr = array.AppendElements(dummyArrayData, ArrayLength(dummyArrayData),
110                              fallible);
111   ASSERT_EQ(&array[DummyArray().Length()], ptr);
112   nsTArray<int> expected;
113   expected.AppendElements(DummyArray());
114   expected.AppendElements(DummyArray());
115   ASSERT_EQ(expected, array);
116 }
117 
TEST(TArray,int_AppendElements_TArray_Copy)118 TEST(TArray, int_AppendElements_TArray_Copy)
119 {
120   nsTArray<int> array;
121 
122   const nsTArray<int> temp(DummyArray().Clone());
123   int* ptr = array.AppendElements(temp);
124   ASSERT_EQ(&array[0], ptr);
125   ASSERT_EQ(DummyArray(), array);
126   ASSERT_FALSE(temp.IsEmpty());
127 
128   ptr = array.AppendElements(temp);
129   ASSERT_EQ(&array[DummyArray().Length()], ptr);
130   nsTArray<int> expected;
131   expected.AppendElements(DummyArray());
132   expected.AppendElements(DummyArray());
133   ASSERT_EQ(expected, array);
134   ASSERT_FALSE(temp.IsEmpty());
135 }
136 
TEST(TArray,int_AppendElements_TArray_Copy_Fallible)137 TEST(TArray, int_AppendElements_TArray_Copy_Fallible)
138 {
139   nsTArray<int> array;
140 
141   const nsTArray<int> temp(DummyArray().Clone());
142   int* ptr = array.AppendElements(temp, fallible);
143   ASSERT_EQ(&array[0], ptr);
144   ASSERT_EQ(DummyArray(), array);
145   ASSERT_FALSE(temp.IsEmpty());
146 
147   ptr = array.AppendElements(temp, fallible);
148   ASSERT_EQ(&array[DummyArray().Length()], ptr);
149   nsTArray<int> expected;
150   expected.AppendElements(DummyArray());
151   expected.AppendElements(DummyArray());
152   ASSERT_EQ(expected, array);
153   ASSERT_FALSE(temp.IsEmpty());
154 }
155 
TEST(TArray,int_AppendElements_TArray_Rvalue)156 TEST(TArray, int_AppendElements_TArray_Rvalue)
157 {
158   nsTArray<int> array;
159 
160   nsTArray<int> temp(DummyArray().Clone());
161   int* ptr = array.AppendElements(std::move(temp));
162   ASSERT_EQ(&array[0], ptr);
163   ASSERT_EQ(DummyArray(), array);
164   ASSERT_TRUE(temp.IsEmpty());
165 
166   temp = DummyArray().Clone();
167   ptr = array.AppendElements(std::move(temp));
168   ASSERT_EQ(&array[DummyArray().Length()], ptr);
169   nsTArray<int> expected;
170   expected.AppendElements(DummyArray());
171   expected.AppendElements(DummyArray());
172   ASSERT_EQ(expected, array);
173   ASSERT_TRUE(temp.IsEmpty());
174 }
175 
TEST(TArray,int_AppendElements_TArray_Rvalue_Fallible)176 TEST(TArray, int_AppendElements_TArray_Rvalue_Fallible)
177 {
178   nsTArray<int> array;
179 
180   nsTArray<int> temp(DummyArray().Clone());
181   int* ptr = array.AppendElements(std::move(temp), fallible);
182   ASSERT_EQ(&array[0], ptr);
183   ASSERT_EQ(DummyArray(), array);
184   ASSERT_TRUE(temp.IsEmpty());
185 
186   temp = DummyArray().Clone();
187   ptr = array.AppendElements(std::move(temp), fallible);
188   ASSERT_EQ(&array[DummyArray().Length()], ptr);
189   nsTArray<int> expected;
190   expected.AppendElements(DummyArray());
191   expected.AppendElements(DummyArray());
192   ASSERT_EQ(expected, array);
193   ASSERT_TRUE(temp.IsEmpty());
194 }
195 
TEST(TArray,int_AppendElements_FallibleArray_Rvalue)196 TEST(TArray, int_AppendElements_FallibleArray_Rvalue)
197 {
198   nsTArray<int> array;
199 
200   FallibleTArray<int> temp;
201   ASSERT_TRUE(temp.AppendElements(DummyArray(), fallible));
202   int* ptr = array.AppendElements(std::move(temp));
203   ASSERT_EQ(&array[0], ptr);
204   ASSERT_EQ(DummyArray(), array);
205   ASSERT_TRUE(temp.IsEmpty());
206 
207   ASSERT_TRUE(temp.AppendElements(DummyArray(), fallible));
208   ptr = array.AppendElements(std::move(temp));
209   ASSERT_EQ(&array[DummyArray().Length()], ptr);
210   nsTArray<int> expected;
211   expected.AppendElements(DummyArray());
212   expected.AppendElements(DummyArray());
213   ASSERT_EQ(expected, array);
214   ASSERT_TRUE(temp.IsEmpty());
215 }
216 
TEST(TArray,int_AppendElements_FallibleArray_Rvalue_Fallible)217 TEST(TArray, int_AppendElements_FallibleArray_Rvalue_Fallible)
218 {
219   nsTArray<int> array;
220 
221   FallibleTArray<int> temp;
222   ASSERT_TRUE(temp.AppendElements(DummyArray(), fallible));
223   int* ptr = array.AppendElements(std::move(temp), fallible);
224   ASSERT_EQ(&array[0], ptr);
225   ASSERT_EQ(DummyArray(), array);
226   ASSERT_TRUE(temp.IsEmpty());
227 
228   ASSERT_TRUE(temp.AppendElements(DummyArray(), fallible));
229   ptr = array.AppendElements(std::move(temp), fallible);
230   ASSERT_EQ(&array[DummyArray().Length()], ptr);
231   nsTArray<int> expected;
232   expected.AppendElements(DummyArray());
233   expected.AppendElements(DummyArray());
234   ASSERT_EQ(expected, array);
235   ASSERT_TRUE(temp.IsEmpty());
236 }
237 
TEST(TArray,AppendElementsSpan)238 TEST(TArray, AppendElementsSpan)
239 {
240   nsTArray<int> array;
241 
242   nsTArray<int> temp(DummyArray().Clone());
243   Span<int> span = temp;
244   array.AppendElements(span);
245   ASSERT_EQ(DummyArray(), array);
246 
247   Span<const int> constSpan = temp;
248   array.AppendElements(constSpan);
249   nsTArray<int> expected;
250   expected.AppendElements(DummyArray());
251   expected.AppendElements(DummyArray());
252   ASSERT_EQ(expected, array);
253 }
254 
TEST(TArray,int_AppendElement_NoElementArg)255 TEST(TArray, int_AppendElement_NoElementArg)
256 {
257   nsTArray<int> array;
258   array.AppendElement();
259 
260   ASSERT_EQ(1u, array.Length());
261 }
262 
TEST(TArray,int_AppendElement_NoElementArg_Fallible)263 TEST(TArray, int_AppendElement_NoElementArg_Fallible)
264 {
265   nsTArray<int> array;
266   ASSERT_NE(nullptr, array.AppendElement(fallible));
267 
268   ASSERT_EQ(1u, array.Length());
269 }
270 
TEST(TArray,int_AppendElement_NoElementArg_Address)271 TEST(TArray, int_AppendElement_NoElementArg_Address)
272 {
273   nsTArray<int> array;
274   *array.AppendElement() = 42;
275 
276   ASSERT_EQ(1u, array.Length());
277   ASSERT_EQ(42, array[0]);
278 }
279 
TEST(TArray,int_AppendElement_NoElementArg_Fallible_Address)280 TEST(TArray, int_AppendElement_NoElementArg_Fallible_Address)
281 {
282   nsTArray<int> array;
283   *array.AppendElement(fallible) = 42;
284 
285   ASSERT_EQ(1u, array.Length());
286   ASSERT_EQ(42, array[0]);
287 }
288 
TEST(TArray,int_AppendElement_ElementArg)289 TEST(TArray, int_AppendElement_ElementArg)
290 {
291   nsTArray<int> array;
292   array.AppendElement(42);
293 
294   ASSERT_EQ(1u, array.Length());
295   ASSERT_EQ(42, array[0]);
296 }
297 
TEST(TArray,int_AppendElement_ElementArg_Fallible)298 TEST(TArray, int_AppendElement_ElementArg_Fallible)
299 {
300   nsTArray<int> array;
301   ASSERT_NE(nullptr, array.AppendElement(42, fallible));
302 
303   ASSERT_EQ(1u, array.Length());
304   ASSERT_EQ(42, array[0]);
305 }
306 
307 constexpr size_t dummyMovableArrayLength = 4;
308 uint32_t dummyMovableArrayDestructorCounter;
309 
DummyMovableArray()310 static nsTArray<Movable> DummyMovableArray() {
311   nsTArray<Movable> res;
312   res.SetLength(dummyMovableArrayLength);
313   for (size_t i = 0; i < dummyMovableArrayLength; ++i) {
314     res[i].mDestructionCounter = &dummyMovableArrayDestructorCounter;
315   }
316   return res;
317 }
318 
TEST(TArray,Movable_AppendElements_TArray_Rvalue)319 TEST(TArray, Movable_AppendElements_TArray_Rvalue)
320 {
321   dummyMovableArrayDestructorCounter = 0;
322   {
323     nsTArray<Movable> array;
324 
325     nsTArray<Movable> temp(DummyMovableArray());
326     Movable* ptr = array.AppendElements(std::move(temp));
327     ASSERT_EQ(&array[0], ptr);
328     ASSERT_TRUE(temp.IsEmpty());
329 
330     temp = DummyMovableArray();
331     ptr = array.AppendElements(std::move(temp));
332     ASSERT_EQ(&array[dummyMovableArrayLength], ptr);
333     ASSERT_TRUE(temp.IsEmpty());
334   }
335   ASSERT_EQ(2 * dummyMovableArrayLength, dummyMovableArrayDestructorCounter);
336 }
337 
TEST(TArray,Movable_AppendElements_TArray_Rvalue_Fallible)338 TEST(TArray, Movable_AppendElements_TArray_Rvalue_Fallible)
339 {
340   dummyMovableArrayDestructorCounter = 0;
341   {
342     nsTArray<Movable> array;
343 
344     nsTArray<Movable> temp(DummyMovableArray());
345     Movable* ptr = array.AppendElements(std::move(temp), fallible);
346     ASSERT_EQ(&array[0], ptr);
347     ASSERT_TRUE(temp.IsEmpty());
348 
349     temp = DummyMovableArray();
350     ptr = array.AppendElements(std::move(temp), fallible);
351     ASSERT_EQ(&array[dummyMovableArrayLength], ptr);
352     ASSERT_TRUE(temp.IsEmpty());
353   }
354   ASSERT_EQ(2 * dummyMovableArrayLength, dummyMovableArrayDestructorCounter);
355 }
356 
TEST(TArray,Movable_AppendElements_FallibleArray_Rvalue)357 TEST(TArray, Movable_AppendElements_FallibleArray_Rvalue)
358 {
359   dummyMovableArrayDestructorCounter = 0;
360   {
361     nsTArray<Movable> array;
362 
363     FallibleTArray<Movable> temp(DummyMovableArray());
364     Movable* ptr = array.AppendElements(std::move(temp));
365     ASSERT_EQ(&array[0], ptr);
366     ASSERT_TRUE(temp.IsEmpty());
367 
368     temp = DummyMovableArray();
369     ptr = array.AppendElements(std::move(temp));
370     ASSERT_EQ(&array[dummyMovableArrayLength], ptr);
371     ASSERT_TRUE(temp.IsEmpty());
372   }
373   ASSERT_EQ(2 * dummyMovableArrayLength, dummyMovableArrayDestructorCounter);
374 }
375 
TEST(TArray,Movable_AppendElements_FallibleArray_Rvalue_Fallible)376 TEST(TArray, Movable_AppendElements_FallibleArray_Rvalue_Fallible)
377 {
378   dummyMovableArrayDestructorCounter = 0;
379   {
380     nsTArray<Movable> array;
381 
382     FallibleTArray<Movable> temp(DummyMovableArray());
383     Movable* ptr = array.AppendElements(std::move(temp), fallible);
384     ASSERT_EQ(&array[0], ptr);
385     ASSERT_TRUE(temp.IsEmpty());
386 
387     temp = DummyMovableArray();
388     ptr = array.AppendElements(std::move(temp), fallible);
389     ASSERT_EQ(&array[dummyMovableArrayLength], ptr);
390     ASSERT_TRUE(temp.IsEmpty());
391   }
392   ASSERT_EQ(2 * dummyMovableArrayLength, dummyMovableArrayDestructorCounter);
393 }
394 
TEST(TArray,Movable_AppendElement_NoElementArg)395 TEST(TArray, Movable_AppendElement_NoElementArg)
396 {
397   nsTArray<Movable> array;
398   array.AppendElement();
399 
400   ASSERT_EQ(1u, array.Length());
401 }
402 
TEST(TArray,Movable_AppendElement_NoElementArg_Fallible)403 TEST(TArray, Movable_AppendElement_NoElementArg_Fallible)
404 {
405   nsTArray<Movable> array;
406   ASSERT_NE(nullptr, array.AppendElement(fallible));
407 
408   ASSERT_EQ(1u, array.Length());
409 }
410 
TEST(TArray,Movable_AppendElement_NoElementArg_Address)411 TEST(TArray, Movable_AppendElement_NoElementArg_Address)
412 {
413   dummyMovableArrayDestructorCounter = 0;
414   {
415     nsTArray<Movable> array;
416     array.AppendElement()->mDestructionCounter =
417         &dummyMovableArrayDestructorCounter;
418 
419     ASSERT_EQ(1u, array.Length());
420   }
421   ASSERT_EQ(1u, dummyMovableArrayDestructorCounter);
422 }
423 
TEST(TArray,Movable_AppendElement_NoElementArg_Fallible_Address)424 TEST(TArray, Movable_AppendElement_NoElementArg_Fallible_Address)
425 {
426   dummyMovableArrayDestructorCounter = 0;
427   {
428     nsTArray<Movable> array;
429     array.AppendElement(fallible)->mDestructionCounter =
430         &dummyMovableArrayDestructorCounter;
431 
432     ASSERT_EQ(1u, array.Length());
433     ASSERT_EQ(&dummyMovableArrayDestructorCounter,
434               array[0].mDestructionCounter);
435   }
436   ASSERT_EQ(1u, dummyMovableArrayDestructorCounter);
437 }
438 
TEST(TArray,Movable_AppendElement_ElementArg)439 TEST(TArray, Movable_AppendElement_ElementArg)
440 {
441   dummyMovableArrayDestructorCounter = 0;
442   Movable movable;
443   movable.mDestructionCounter = &dummyMovableArrayDestructorCounter;
444   {
445     nsTArray<Movable> array;
446     array.AppendElement(std::move(movable));
447 
448     ASSERT_EQ(1u, array.Length());
449     ASSERT_EQ(&dummyMovableArrayDestructorCounter,
450               array[0].mDestructionCounter);
451   }
452   ASSERT_EQ(1u, dummyMovableArrayDestructorCounter);
453 }
454 
TEST(TArray,Movable_AppendElement_ElementArg_Fallible)455 TEST(TArray, Movable_AppendElement_ElementArg_Fallible)
456 {
457   dummyMovableArrayDestructorCounter = 0;
458   Movable movable;
459   movable.mDestructionCounter = &dummyMovableArrayDestructorCounter;
460   {
461     nsTArray<Movable> array;
462     ASSERT_NE(nullptr, array.AppendElement(std::move(movable), fallible));
463 
464     ASSERT_EQ(1u, array.Length());
465     ASSERT_EQ(&dummyMovableArrayDestructorCounter,
466               array[0].mDestructionCounter);
467   }
468   ASSERT_EQ(1u, dummyMovableArrayDestructorCounter);
469 }
470 
TEST(TArray,int_Assign)471 TEST(TArray, int_Assign)
472 {
473   nsTArray<int> array;
474   array.Assign(DummyArray());
475   ASSERT_EQ(DummyArray(), array);
476 
477   ASSERT_TRUE(array.Assign(DummyArray(), fallible));
478   ASSERT_EQ(DummyArray(), array);
479 
480 #ifdef DEBUG
481   ASSERT_FALSE(array.Assign(FakeHugeArray(), fallible));
482 #endif
483 
484   nsTArray<int> array2;
485   array2.Assign(std::move(array));
486   ASSERT_TRUE(array.IsEmpty());
487   ASSERT_EQ(DummyArray(), array2);
488 }
489 
TEST(TArray,int_AssignmentOperatorSelfAssignment)490 TEST(TArray, int_AssignmentOperatorSelfAssignment)
491 {
492   CopyableTArray<int> array;
493   array = DummyArray();
494 
495   array = *&array;
496   ASSERT_EQ(DummyArray(), array);
497 
498 #if defined(__clang__)
499 #  pragma clang diagnostic push
500 #  pragma clang diagnostic ignored "-Wself-move"
501 #endif
502   array = std::move(array);  // self-move
503   ASSERT_EQ(DummyArray(), array);
504 #if defined(__clang__)
505 #  pragma clang diagnostic pop
506 #endif
507 }
508 
TEST(TArray,Movable_CopyOverlappingForwards)509 TEST(TArray, Movable_CopyOverlappingForwards)
510 {
511   const size_t rangeLength = 8;
512   const size_t initialLength = 2 * rangeLength;
513   uint32_t destructionCounters[initialLength];
514   nsTArray<Movable> array;
515   array.AppendElements(initialLength);
516 
517   for (uint32_t i = 0; i < initialLength; ++i) {
518     destructionCounters[i] = 0;
519   }
520   for (uint32_t i = 0; i < initialLength; ++i) {
521     array[i].mDestructionCounter = &destructionCounters[i];
522   }
523 
524   const size_t removedLength = rangeLength / 2;
525   array.RemoveElementsAt(0, removedLength);
526 
527   for (uint32_t i = 0; i < removedLength; ++i) {
528     ASSERT_EQ(destructionCounters[i], 1u);
529   }
530   for (uint32_t i = removedLength; i < initialLength; ++i) {
531     ASSERT_EQ(destructionCounters[i], 0u);
532   }
533 }
534 
535 // The code to copy overlapping regions had a bug in that it wouldn't correctly
536 // destroy all over the source elements being copied.
TEST(TArray,Copyable_CopyOverlappingBackwards)537 TEST(TArray, Copyable_CopyOverlappingBackwards)
538 {
539   const size_t rangeLength = 8;
540   const size_t initialLength = 2 * rangeLength;
541   uint32_t destructionCounters[initialLength];
542   nsTArray<Copyable> array;
543   array.SetCapacity(3 * rangeLength);
544   array.AppendElements(initialLength);
545   // To tickle the bug, we need to copy a source region:
546   //
547   //   ..XXXXX..
548   //
549   // such that it overlaps the destination region:
550   //
551   //   ....XXXXX
552   //
553   // so we are forced to copy back-to-front to ensure correct behavior.
554   // The easiest way to do that is to call InsertElementsAt, which will force
555   // the desired kind of shift.
556   for (uint32_t i = 0; i < initialLength; ++i) {
557     destructionCounters[i] = 0;
558   }
559   for (uint32_t i = 0; i < initialLength; ++i) {
560     array[i].mDestructionCounter = &destructionCounters[i];
561   }
562 
563   array.InsertElementsAt(0, rangeLength);
564 
565   for (uint32_t i = 0; i < initialLength; ++i) {
566     ASSERT_EQ(destructionCounters[i], 1u);
567   }
568 }
569 
570 namespace {
571 
572 class E {
573  public:
E()574   E() : mA(-1), mB(-2) { constructCount++; }
E(int a,int b)575   E(int a, int b) : mA(a), mB(b) { constructCount++; }
E(E && aRhs)576   E(E&& aRhs) : mA(aRhs.mA), mB(aRhs.mB) {
577     aRhs.mA = 0;
578     aRhs.mB = 0;
579     moveCount++;
580   }
581 
operator =(E && aRhs)582   E& operator=(E&& aRhs) {
583     mA = aRhs.mA;
584     aRhs.mA = 0;
585     mB = aRhs.mB;
586     aRhs.mB = 0;
587     moveCount++;
588     return *this;
589   }
590 
a() const591   int a() const { return mA; }
b() const592   int b() const { return mB; }
593 
594   E(const E&) = delete;
595   E& operator=(const E&) = delete;
596 
597   static size_t constructCount;
598   static size_t moveCount;
599 
600  private:
601   int mA;
602   int mB;
603 };
604 
605 size_t E::constructCount = 0;
606 size_t E::moveCount = 0;
607 
608 }  // namespace
609 
TEST(TArray,Emplace)610 TEST(TArray, Emplace)
611 {
612   nsTArray<E> array;
613   array.SetCapacity(20);
614 
615   ASSERT_EQ(array.Length(), 0u);
616 
617   for (int i = 0; i < 10; i++) {
618     E s(i, i * i);
619     array.AppendElement(std::move(s));
620   }
621 
622   ASSERT_EQ(array.Length(), 10u);
623   ASSERT_EQ(E::constructCount, 10u);
624   ASSERT_EQ(E::moveCount, 10u);
625 
626   for (int i = 10; i < 20; i++) {
627     array.EmplaceBack(i, i * i);
628   }
629 
630   ASSERT_EQ(array.Length(), 20u);
631   ASSERT_EQ(E::constructCount, 20u);
632   ASSERT_EQ(E::moveCount, 10u);
633 
634   for (int i = 0; i < 20; i++) {
635     ASSERT_EQ(array[i].a(), i);
636     ASSERT_EQ(array[i].b(), i * i);
637   }
638 
639   array.EmplaceBack();
640 
641   ASSERT_EQ(array.Length(), 21u);
642   ASSERT_EQ(E::constructCount, 21u);
643   ASSERT_EQ(E::moveCount, 10u);
644 
645   ASSERT_EQ(array[20].a(), -1);
646   ASSERT_EQ(array[20].b(), -2);
647 }
648 
TEST(TArray,UnorderedRemoveElements)649 TEST(TArray, UnorderedRemoveElements)
650 {
651   // When removing an element from the end of the array, it can be removed in
652   // place, by destroying it and decrementing the length.
653   //
654   // [ 1, 2, 3 ] => [ 1, 2 ]
655   //         ^
656   {
657     nsTArray<int> array{1, 2, 3};
658     array.UnorderedRemoveElementAt(2);
659 
660     nsTArray<int> goal{1, 2};
661     ASSERT_EQ(array, goal);
662   }
663 
664   // When removing any other single element, it is removed by swapping it with
665   // the last element, and then decrementing the length as before.
666   //
667   // [ 1, 2, 3, 4, 5, 6 ]  => [ 1, 6, 3, 4, 5 ]
668   //      ^
669   {
670     nsTArray<int> array{1, 2, 3, 4, 5, 6};
671     array.UnorderedRemoveElementAt(1);
672 
673     nsTArray<int> goal{1, 6, 3, 4, 5};
674     ASSERT_EQ(array, goal);
675   }
676 
677   // This method also supports efficiently removing a range of elements. If they
678   // are at the end, then they can all be removed like in the one element case.
679   //
680   // [ 1, 2, 3, 4, 5, 6 ] => [ 1, 2 ]
681   //         ^--------^
682   {
683     nsTArray<int> array{1, 2, 3, 4, 5, 6};
684     array.UnorderedRemoveElementsAt(2, 4);
685 
686     nsTArray<int> goal{1, 2};
687     ASSERT_EQ(array, goal);
688   }
689 
690   // If more elements are removed than exist after the removed section, the
691   // remaining elements will be shifted down like in a normal removal.
692   //
693   // [ 1, 2, 3, 4, 5, 6, 7, 8 ] => [ 1, 2, 7, 8 ]
694   //         ^--------^
695   {
696     nsTArray<int> array{1, 2, 3, 4, 5, 6, 7, 8};
697     array.UnorderedRemoveElementsAt(2, 4);
698 
699     nsTArray<int> goal{1, 2, 7, 8};
700     ASSERT_EQ(array, goal);
701   }
702 
703   // And if fewer elements are removed than exist after the removed section,
704   // elements will be moved from the end of the array to fill the vacated space.
705   //
706   // [ 1, 2, 3, 4, 5, 6, 7, 8 ] => [ 1, 7, 8, 4, 5, 6 ]
707   //      ^--^
708   {
709     nsTArray<int> array{1, 2, 3, 4, 5, 6, 7, 8};
710     array.UnorderedRemoveElementsAt(1, 2);
711 
712     nsTArray<int> goal{1, 7, 8, 4, 5, 6};
713     ASSERT_EQ(array, goal);
714   }
715 
716   // We should do the right thing if we drain the entire array.
717   {
718     nsTArray<int> array{1, 2, 3, 4, 5};
719     array.UnorderedRemoveElementsAt(0, 5);
720 
721     nsTArray<int> goal{};
722     ASSERT_EQ(array, goal);
723   }
724 
725   {
726     nsTArray<int> array{1};
727     array.UnorderedRemoveElementAt(0);
728 
729     nsTArray<int> goal{};
730     ASSERT_EQ(array, goal);
731   }
732 
733   // We should do the right thing if we remove the same number of elements that
734   // we have remaining.
735   {
736     nsTArray<int> array{1, 2, 3, 4, 5, 6};
737     array.UnorderedRemoveElementsAt(2, 2);
738 
739     nsTArray<int> goal{1, 2, 5, 6};
740     ASSERT_EQ(array, goal);
741   }
742 
743   {
744     nsTArray<int> array{1, 2, 3};
745     array.UnorderedRemoveElementAt(1);
746 
747     nsTArray<int> goal{1, 3};
748     ASSERT_EQ(array, goal);
749   }
750 
751   // We should be able to remove elements from the front without issue.
752   {
753     nsTArray<int> array{1, 2, 3, 4, 5, 6};
754     array.UnorderedRemoveElementsAt(0, 2);
755 
756     nsTArray<int> goal{5, 6, 3, 4};
757     ASSERT_EQ(array, goal);
758   }
759 
760   {
761     nsTArray<int> array{1, 2, 3, 4};
762     array.UnorderedRemoveElementAt(0);
763 
764     nsTArray<int> goal{4, 2, 3};
765     ASSERT_EQ(array, goal);
766   }
767 }
768 
TEST(TArray,RemoveFromEnd)769 TEST(TArray, RemoveFromEnd)
770 {
771   {
772     nsTArray<int> array{1, 2, 3, 4};
773     ASSERT_EQ(array.PopLastElement(), 4);
774     array.RemoveLastElement();
775     ASSERT_EQ(array.PopLastElement(), 2);
776     array.RemoveLastElement();
777     ASSERT_TRUE(array.IsEmpty());
778   }
779 }
780 
TEST(TArray,ConvertIteratorToConstIterator)781 TEST(TArray, ConvertIteratorToConstIterator)
782 {
783   nsTArray<int> array{1, 2, 3, 4};
784 
785   nsTArray<int>::const_iterator it = array.begin();
786   ASSERT_EQ(array.cbegin(), it);
787 }
788 
TEST(TArray,RemoveElementAt_ByIterator)789 TEST(TArray, RemoveElementAt_ByIterator)
790 {
791   nsTArray<int> array{1, 2, 3, 4};
792   const auto it = std::find(array.begin(), array.end(), 3);
793   const auto itAfter = array.RemoveElementAt(it);
794 
795   // Based on the implementation of the iterator, we could compare it and
796   // itAfter, but we should not rely on such implementation details.
797 
798   ASSERT_EQ(2, std::distance(array.cbegin(), itAfter));
799   const nsTArray<int> expected{1, 2, 4};
800   ASSERT_EQ(expected, array);
801 }
802 
TEST(TArray,RemoveElementsAt_ByIterator)803 TEST(TArray, RemoveElementsAt_ByIterator)
804 {
805   nsTArray<int> array{1, 2, 3, 4};
806   const auto it = std::find(array.begin(), array.end(), 3);
807   const auto itAfter = array.RemoveElementsAt(it, array.end());
808 
809   // Based on the implementation of the iterator, we could compare it and
810   // itAfter, but we should not rely on such implementation details.
811 
812   ASSERT_EQ(2, std::distance(array.cbegin(), itAfter));
813   const nsTArray<int> expected{1, 2};
814   ASSERT_EQ(expected, array);
815 }
816 
817 static_assert(std::is_copy_assignable<decltype(
818                   MakeBackInserter(std::declval<nsTArray<int>&>()))>::value,
819               "output iteraror must be copy-assignable");
820 static_assert(std::is_copy_constructible<decltype(
821                   MakeBackInserter(std::declval<nsTArray<int>&>()))>::value,
822               "output iterator must be copy-constructible");
823 
TEST(TArray,MakeBackInserter)824 TEST(TArray, MakeBackInserter)
825 {
826   const std::vector<int> src{1, 2, 3, 4};
827   nsTArray<int> dst;
828 
829   std::copy(src.begin(), src.end(), MakeBackInserter(dst));
830 
831   const nsTArray<int> expected{1, 2, 3, 4};
832   ASSERT_EQ(expected, dst);
833 }
834 
TEST(TArray,MakeBackInserter_Move)835 TEST(TArray, MakeBackInserter_Move)
836 {
837   uint32_t destructionCounter = 0;
838 
839   {
840     std::vector<Movable> src(1);
841     src[0].mDestructionCounter = &destructionCounter;
842 
843     nsTArray<Movable> dst;
844 
845     std::copy(std::make_move_iterator(src.begin()),
846               std::make_move_iterator(src.end()), MakeBackInserter(dst));
847 
848     ASSERT_EQ(1u, dst.Length());
849     ASSERT_EQ(0u, destructionCounter);
850   }
851 
852   ASSERT_EQ(1u, destructionCounter);
853 }
854 
TEST(TArray,ConvertToSpan)855 TEST(TArray, ConvertToSpan)
856 {
857   nsTArray<int> arr = {1, 2, 3, 4, 5};
858 
859   // from const
860   {
861     const auto& constArrRef = arr;
862 
863     auto span = Span{constArrRef};
864     static_assert(std::is_same_v<decltype(span), Span<const int>>);
865   }
866 
867   // from non-const
868   {
869     auto span = Span{arr};
870     static_assert(std::is_same_v<decltype(span), Span<int>>);
871   }
872 }
873 
874 // This should compile:
875 struct RefCounted;
876 
877 class Foo {
878   ~Foo();  // Intentionally out of line
879 
880   nsTArray<RefPtr<RefCounted>> mArray;
881 
GetFirst() const882   const RefCounted* GetFirst() const { return mArray.SafeElementAt(0); }
883 };
884 
885 }  // namespace TestTArray
886