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