1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
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 "mozilla/ArrayUtils.h"
8 #include "mozilla/Unused.h"
9 
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <iostream>
13 #include "nsTArray.h"
14 #include "nsString.h"
15 #include "nsDirectoryServiceDefs.h"
16 #include "nsDirectoryServiceUtils.h"
17 #include "nsComponentManagerUtils.h"
18 #include "nsXPCOM.h"
19 #include "nsIFile.h"
20 
21 #include "gtest/gtest.h"
22 
23 using namespace mozilla;
24 
25 namespace TestTArray {
26 
27 // Define this so we can use test_basic_array in test_comptr_array
28 template <class T>
operator <(const nsCOMPtr<T> & lhs,const nsCOMPtr<T> & rhs)29 inline bool operator<(const nsCOMPtr<T>& lhs, const nsCOMPtr<T>& rhs) {
30   return lhs.get() < rhs.get();
31 }
32 
33 //----
34 
35 template <class ElementType>
test_basic_array(ElementType * data,size_t dataLen,const ElementType & extra)36 static bool test_basic_array(ElementType* data, size_t dataLen,
37                              const ElementType& extra) {
38   CopyableTArray<ElementType> ary;
39   const nsTArray<ElementType>& cary = ary;
40 
41   ary.AppendElements(data, dataLen);
42   if (ary.Length() != dataLen) {
43     return false;
44   }
45   if (!(ary == ary)) {
46     return false;
47   }
48   size_t i;
49   for (i = 0; i < ary.Length(); ++i) {
50     if (ary[i] != data[i]) return false;
51   }
52   for (i = 0; i < ary.Length(); ++i) {
53     if (ary.SafeElementAt(i, extra) != data[i]) return false;
54   }
55   if (ary.SafeElementAt(ary.Length(), extra) != extra ||
56       ary.SafeElementAt(ary.Length() * 10, extra) != extra)
57     return false;
58   // ensure sort results in ascending order
59   ary.Sort();
60   size_t j = 0, k = ary.IndexOfFirstElementGt(extra);
61   if (k != 0 && ary[k - 1] == extra) return false;
62   for (i = 0; i < ary.Length(); ++i) {
63     k = ary.IndexOfFirstElementGt(ary[i]);
64     if (k == 0 || ary[k - 1] != ary[i]) return false;
65     if (k < j) return false;
66     j = k;
67   }
68   for (i = ary.Length(); --i;) {
69     if (ary[i] < ary[i - 1]) return false;
70     if (ary[i] == ary[i - 1]) ary.RemoveElementAt(i);
71   }
72   if (!(ary == ary)) {
73     return false;
74   }
75   for (i = 0; i < ary.Length(); ++i) {
76     if (ary.BinaryIndexOf(ary[i]) != i) return false;
77   }
78   if (ary.BinaryIndexOf(extra) != ary.NoIndex) return false;
79   size_t oldLen = ary.Length();
80   ary.RemoveElement(data[dataLen / 2]);
81   if (ary.Length() != (oldLen - 1)) return false;
82   if (!(ary == ary)) return false;
83 
84   if (ary.ApplyIf(
85           extra, []() { return true; }, []() { return false; }))
86     return false;
87   if (ary.ApplyIf(
88           extra, [](size_t) { return true; }, []() { return false; }))
89     return false;
90   // On a non-const array, ApplyIf's first lambda may use either const or non-
91   // const element types.
92   if (ary.ApplyIf(
93           extra, [](ElementType&) { return true; }, []() { return false; }))
94     return false;
95   if (ary.ApplyIf(
96           extra, [](const ElementType&) { return true; },
97           []() { return false; }))
98     return false;
99   if (ary.ApplyIf(
100           extra, [](size_t, ElementType&) { return true; },
101           []() { return false; }))
102     return false;
103   if (ary.ApplyIf(
104           extra, [](size_t, const ElementType&) { return true; },
105           []() { return false; }))
106     return false;
107 
108   if (cary.ApplyIf(
109           extra, []() { return true; }, []() { return false; }))
110     if (cary.ApplyIf(
111             extra, [](size_t) { return true; }, []() { return false; }))
112       // On a const array, ApplyIf's first lambda must only use const element
113       // types.
114       if (cary.ApplyIf(
115               extra, [](const ElementType&) { return true; },
116               []() { return false; }))
117         if (cary.ApplyIf(
118                 extra, [](size_t, const ElementType&) { return true; },
119                 []() { return false; }))
120           return false;
121 
122   size_t index = ary.Length() / 2;
123   ary.InsertElementAt(index, extra);
124   if (!(ary == ary)) return false;
125   if (ary[index] != extra) return false;
126   if (ary.IndexOf(extra) == ary.NoIndex) return false;
127   if (ary.LastIndexOf(extra) == ary.NoIndex) return false;
128   // ensure proper searching
129   if (ary.IndexOf(extra) > ary.LastIndexOf(extra)) return false;
130   if (ary.IndexOf(extra, index) != ary.LastIndexOf(extra, index)) return false;
131   if (!ary.ApplyIf(
132           extra,
133           [&](size_t i, const ElementType& e) {
134             return i == index && e == extra;
135           },
136           []() { return false; }))
137     return false;
138   if (!cary.ApplyIf(
139           extra,
140           [&](size_t i, const ElementType& e) {
141             return i == index && e == extra;
142           },
143           []() { return false; }))
144     return false;
145 
146   nsTArray<ElementType> copy(ary.Clone());
147   if (!(ary == copy)) return false;
148   for (i = 0; i < copy.Length(); ++i) {
149     if (ary[i] != copy[i]) return false;
150   }
151   ary.AppendElements(copy);
152   size_t cap = ary.Capacity();
153   ary.RemoveElementsAt(copy.Length(), copy.Length());
154   ary.Compact();
155   if (ary.Capacity() == cap) return false;
156 
157   ary.Clear();
158   if (ary.IndexOf(extra) != ary.NoIndex) return false;
159   if (ary.LastIndexOf(extra) != ary.NoIndex) return false;
160   if (ary.ApplyIf(
161           extra, []() { return true; }, []() { return false; }))
162     return false;
163   if (cary.ApplyIf(
164           extra, []() { return true; }, []() { return false; }))
165     return false;
166 
167   ary.Clear();
168   if (!ary.IsEmpty()) return false;
169   if (!(ary == nsTArray<ElementType>())) return false;
170   if (ary == copy) return false;
171   if (ary.SafeElementAt(0, extra) != extra ||
172       ary.SafeElementAt(10, extra) != extra)
173     return false;
174 
175   ary = copy;
176   if (!(ary == copy)) return false;
177   for (i = 0; i < copy.Length(); ++i) {
178     if (ary[i] != copy[i]) return false;
179   }
180 
181   ary.InsertElementsAt(0, copy);
182   if (ary == copy) return false;
183   ary.RemoveElementsAt(0, copy.Length());
184   for (i = 0; i < copy.Length(); ++i) {
185     if (ary[i] != copy[i]) return false;
186   }
187 
188   // These shouldn't crash!
189   nsTArray<ElementType> empty;
190   ary.AppendElements(reinterpret_cast<ElementType*>(0), 0);
191   ary.AppendElements(empty);
192 
193   // See bug 324981
194   ary.RemoveElement(extra);
195   ary.RemoveElement(extra);
196 
197   return true;
198 }
199 
TEST(TArray,test_int_array)200 TEST(TArray, test_int_array)
201 {
202   int data[] = {4, 6, 8, 2, 4, 1, 5, 7, 3};
203   ASSERT_TRUE(test_basic_array(data, ArrayLength(data), int(14)));
204 }
205 
TEST(TArray,test_int64_array)206 TEST(TArray, test_int64_array)
207 {
208   int64_t data[] = {4, 6, 8, 2, 4, 1, 5, 7, 3};
209   ASSERT_TRUE(test_basic_array(data, ArrayLength(data), int64_t(14)));
210 }
211 
TEST(TArray,test_char_array)212 TEST(TArray, test_char_array)
213 {
214   char data[] = {4, 6, 8, 2, 4, 1, 5, 7, 3};
215   ASSERT_TRUE(test_basic_array(data, ArrayLength(data), char(14)));
216 }
217 
TEST(TArray,test_uint32_array)218 TEST(TArray, test_uint32_array)
219 {
220   uint32_t data[] = {4, 6, 8, 2, 4, 1, 5, 7, 3};
221   ASSERT_TRUE(test_basic_array(data, ArrayLength(data), uint32_t(14)));
222 }
223 
224 //----
225 
226 class Object {
227  public:
Object()228   Object() : mNum(0) {}
Object(const char * str,uint32_t num)229   Object(const char* str, uint32_t num) : mStr(str), mNum(num) {}
230   Object(const Object& other) = default;
231   ~Object() = default;
232 
233   Object& operator=(const Object& other) = default;
234 
operator ==(const Object & other) const235   bool operator==(const Object& other) const {
236     return mStr == other.mStr && mNum == other.mNum;
237   }
238 
operator <(const Object & other) const239   bool operator<(const Object& other) const {
240     // sort based on mStr only
241     return mStr.Compare(other.mStr.get()) < 0;
242   }
243 
Str() const244   const char* Str() const { return mStr.get(); }
Num() const245   uint32_t Num() const { return mNum; }
246 
247  private:
248   nsCString mStr;
249   uint32_t mNum;
250 };
251 
TEST(TArray,test_object_array)252 TEST(TArray, test_object_array)
253 {
254   nsTArray<Object> objArray;
255   const char kdata[] = "hello world";
256   size_t i;
257   for (i = 0; i < ArrayLength(kdata); ++i) {
258     char x[] = {kdata[i], '\0'};
259     objArray.AppendElement(Object(x, i));
260   }
261   for (i = 0; i < ArrayLength(kdata); ++i) {
262     ASSERT_EQ(objArray[i].Str()[0], kdata[i]);
263     ASSERT_EQ(objArray[i].Num(), i);
264   }
265   objArray.Sort();
266   const char ksorted[] = "\0 dehllloorw";
267   for (i = 0; i < ArrayLength(kdata) - 1; ++i) {
268     ASSERT_EQ(objArray[i].Str()[0], ksorted[i]);
269   }
270 }
271 
272 class Countable {
273   static int sCount;
274 
275  public:
Countable()276   Countable() { sCount++; }
277 
Countable(const Countable & aOther)278   Countable(const Countable& aOther) { sCount++; }
279 
Count()280   static int Count() { return sCount; }
281 };
282 
283 class Moveable {
284   static int sCount;
285 
286  public:
Moveable()287   Moveable() { sCount++; }
288 
Moveable(const Moveable & aOther)289   Moveable(const Moveable& aOther) { sCount++; }
290 
Moveable(Moveable && aOther)291   Moveable(Moveable&& aOther) {
292     // Do not increment sCount
293   }
294 
Count()295   static int Count() { return sCount; }
296 };
297 
298 class MoveOnly_RelocateUsingMemutils {
299  public:
300   MoveOnly_RelocateUsingMemutils() = default;
301 
302   MoveOnly_RelocateUsingMemutils(const MoveOnly_RelocateUsingMemutils&) =
303       delete;
304   MoveOnly_RelocateUsingMemutils(MoveOnly_RelocateUsingMemutils&&) = default;
305 
306   MoveOnly_RelocateUsingMemutils& operator=(
307       const MoveOnly_RelocateUsingMemutils&) = delete;
308   MoveOnly_RelocateUsingMemutils& operator=(MoveOnly_RelocateUsingMemutils&&) =
309       default;
310 };
311 
312 static_assert(
313     std::is_move_constructible_v<nsTArray<MoveOnly_RelocateUsingMemutils>>);
314 static_assert(
315     std::is_move_assignable_v<nsTArray<MoveOnly_RelocateUsingMemutils>>);
316 static_assert(
317     !std::is_copy_constructible_v<nsTArray<MoveOnly_RelocateUsingMemutils>>);
318 static_assert(
319     !std::is_copy_assignable_v<nsTArray<MoveOnly_RelocateUsingMemutils>>);
320 
321 class MoveOnly_RelocateUsingMoveConstructor {
322  public:
323   MoveOnly_RelocateUsingMoveConstructor() = default;
324 
325   MoveOnly_RelocateUsingMoveConstructor(
326       const MoveOnly_RelocateUsingMoveConstructor&) = delete;
327   MoveOnly_RelocateUsingMoveConstructor(
328       MoveOnly_RelocateUsingMoveConstructor&&) = default;
329 
330   MoveOnly_RelocateUsingMoveConstructor& operator=(
331       const MoveOnly_RelocateUsingMoveConstructor&) = delete;
332   MoveOnly_RelocateUsingMoveConstructor& operator=(
333       MoveOnly_RelocateUsingMoveConstructor&&) = default;
334 };
335 }  // namespace TestTArray
336 
337 MOZ_DECLARE_RELOCATE_USING_MOVE_CONSTRUCTOR(
338     TestTArray::MoveOnly_RelocateUsingMoveConstructor)
339 
340 namespace TestTArray {
341 static_assert(std::is_move_constructible_v<
342               nsTArray<MoveOnly_RelocateUsingMoveConstructor>>);
343 static_assert(
344     std::is_move_assignable_v<nsTArray<MoveOnly_RelocateUsingMoveConstructor>>);
345 static_assert(!std::is_copy_constructible_v<
346               nsTArray<MoveOnly_RelocateUsingMoveConstructor>>);
347 static_assert(!std::is_copy_assignable_v<
348               nsTArray<MoveOnly_RelocateUsingMoveConstructor>>);
349 }  // namespace TestTArray
350 
351 namespace TestTArray {
352 
353 /* static */
354 int Countable::sCount = 0;
355 /* static */
356 int Moveable::sCount = 0;
357 
returns_by_value()358 static nsTArray<int> returns_by_value() {
359   nsTArray<int> result;
360   return result;
361 }
362 
TEST(TArray,test_return_by_value)363 TEST(TArray, test_return_by_value)
364 {
365   nsTArray<int> result = returns_by_value();
366   ASSERT_TRUE(true);  // This is just a compilation test.
367 }
368 
TEST(TArray,test_move_array)369 TEST(TArray, test_move_array)
370 {
371   nsTArray<Countable> countableArray;
372   uint32_t i;
373   for (i = 0; i < 4; ++i) {
374     countableArray.AppendElement(Countable());
375   }
376 
377   ASSERT_EQ(Countable::Count(), 8);
378 
379   const nsTArray<Countable>& constRefCountableArray = countableArray;
380 
381   ASSERT_EQ(Countable::Count(), 8);
382 
383   nsTArray<Countable> copyCountableArray(constRefCountableArray.Clone());
384 
385   ASSERT_EQ(Countable::Count(), 12);
386 
387   nsTArray<Countable>&& moveRefCountableArray = std::move(countableArray);
388   moveRefCountableArray.Length();  // Make compilers happy.
389 
390   ASSERT_EQ(Countable::Count(), 12);
391 
392   nsTArray<Countable> movedCountableArray(std::move(countableArray));
393 
394   ASSERT_EQ(Countable::Count(), 12);
395 
396   // Test ctor
397   FallibleTArray<Countable> differentAllocatorCountableArray(
398       std::move(copyCountableArray));
399   // operator=
400   copyCountableArray = std::move(differentAllocatorCountableArray);
401   differentAllocatorCountableArray = std::move(copyCountableArray);
402   // And the other ctor
403   nsTArray<Countable> copyCountableArray2(
404       std::move(differentAllocatorCountableArray));
405   // with auto
406   AutoTArray<Countable, 3> autoCountableArray(std::move(copyCountableArray2));
407   // operator=
408   copyCountableArray2 = std::move(autoCountableArray);
409   // Mix with FallibleTArray
410   FallibleTArray<Countable> differentAllocatorCountableArray2(
411       std::move(copyCountableArray2));
412   AutoTArray<Countable, 4> autoCountableArray2(
413       std::move(differentAllocatorCountableArray2));
414   differentAllocatorCountableArray2 = std::move(autoCountableArray2);
415 
416   ASSERT_EQ(Countable::Count(), 12);
417 
418   nsTArray<Moveable> moveableArray;
419   for (i = 0; i < 4; ++i) {
420     moveableArray.AppendElement(Moveable());
421   }
422 
423   ASSERT_EQ(Moveable::Count(), 4);
424 
425   const nsTArray<Moveable>& constRefMoveableArray = moveableArray;
426 
427   ASSERT_EQ(Moveable::Count(), 4);
428 
429   nsTArray<Moveable> copyMoveableArray(constRefMoveableArray.Clone());
430 
431   ASSERT_EQ(Moveable::Count(), 8);
432 
433   nsTArray<Moveable>&& moveRefMoveableArray = std::move(moveableArray);
434   moveRefMoveableArray.Length();  // Make compilers happy.
435 
436   ASSERT_EQ(Moveable::Count(), 8);
437 
438   nsTArray<Moveable> movedMoveableArray(std::move(moveableArray));
439 
440   ASSERT_EQ(Moveable::Count(), 8);
441 
442   // Test ctor
443   FallibleTArray<Moveable> differentAllocatorMoveableArray(
444       std::move(copyMoveableArray));
445   // operator=
446   copyMoveableArray = std::move(differentAllocatorMoveableArray);
447   differentAllocatorMoveableArray = std::move(copyMoveableArray);
448   // And the other ctor
449   nsTArray<Moveable> copyMoveableArray2(
450       std::move(differentAllocatorMoveableArray));
451   // with auto
452   AutoTArray<Moveable, 3> autoMoveableArray(std::move(copyMoveableArray2));
453   // operator=
454   copyMoveableArray2 = std::move(autoMoveableArray);
455   // Mix with FallibleTArray
456   FallibleTArray<Moveable> differentAllocatorMoveableArray2(
457       std::move(copyMoveableArray2));
458   AutoTArray<Moveable, 4> autoMoveableArray2(
459       std::move(differentAllocatorMoveableArray2));
460   differentAllocatorMoveableArray2 = std::move(autoMoveableArray2);
461 
462   ASSERT_EQ(Moveable::Count(), 8);
463 
464   AutoTArray<Moveable, 8> moveableAutoArray;
465   for (uint32_t i = 0; i < 4; ++i) {
466     moveableAutoArray.AppendElement(Moveable());
467   }
468 
469   ASSERT_EQ(Moveable::Count(), 12);
470 
471   const AutoTArray<Moveable, 8>& constRefMoveableAutoArray = moveableAutoArray;
472 
473   ASSERT_EQ(Moveable::Count(), 12);
474 
475   CopyableAutoTArray<Moveable, 8> copyMoveableAutoArray(
476       constRefMoveableAutoArray);
477 
478   ASSERT_EQ(Moveable::Count(), 16);
479 
480   AutoTArray<Moveable, 8> movedMoveableAutoArray(std::move(moveableAutoArray));
481 
482   ASSERT_EQ(Moveable::Count(), 16);
483 }
484 
485 template <typename TypeParam>
486 class TArray_MoveOnlyTest : public ::testing::Test {};
487 
488 TYPED_TEST_CASE_P(TArray_MoveOnlyTest);
489 
490 static constexpr size_t kMoveOnlyTestArrayLength = 4;
491 
492 template <typename ArrayType>
MakeMoveOnlyArray()493 static auto MakeMoveOnlyArray() {
494   ArrayType moveOnlyArray;
495   for (size_t i = 0; i < kMoveOnlyTestArrayLength; ++i) {
496     EXPECT_TRUE(
497         moveOnlyArray.AppendElement(typename ArrayType::elem_type(), fallible));
498   }
499   return moveOnlyArray;
500 }
501 
TYPED_TEST_P(TArray_MoveOnlyTest,nsTArray_MoveConstruct)502 TYPED_TEST_P(TArray_MoveOnlyTest, nsTArray_MoveConstruct) {
503   auto moveOnlyArray = MakeMoveOnlyArray<nsTArray<TypeParam>>();
504   nsTArray<TypeParam> movedMoveOnlyArray(std::move(moveOnlyArray));
505 
506   ASSERT_EQ(0u, moveOnlyArray.Length());
507   ASSERT_EQ(kMoveOnlyTestArrayLength, movedMoveOnlyArray.Length());
508 }
509 
TYPED_TEST_P(TArray_MoveOnlyTest,nsTArray_MoveAssign)510 TYPED_TEST_P(TArray_MoveOnlyTest, nsTArray_MoveAssign) {
511   auto moveOnlyArray = MakeMoveOnlyArray<nsTArray<TypeParam>>();
512   nsTArray<TypeParam> movedMoveOnlyArray;
513   movedMoveOnlyArray = std::move(moveOnlyArray);
514 
515   ASSERT_EQ(0u, moveOnlyArray.Length());
516   ASSERT_EQ(kMoveOnlyTestArrayLength, movedMoveOnlyArray.Length());
517 }
518 
TYPED_TEST_P(TArray_MoveOnlyTest,nsTArray_MoveReAssign)519 TYPED_TEST_P(TArray_MoveOnlyTest, nsTArray_MoveReAssign) {
520   nsTArray<TypeParam> movedMoveOnlyArray;
521   movedMoveOnlyArray = MakeMoveOnlyArray<nsTArray<TypeParam>>();
522   // Re-assign, to check that move-assign does not only work on an empty array.
523   movedMoveOnlyArray = MakeMoveOnlyArray<nsTArray<TypeParam>>();
524 
525   ASSERT_EQ(kMoveOnlyTestArrayLength, movedMoveOnlyArray.Length());
526 }
527 
TYPED_TEST_P(TArray_MoveOnlyTest,nsTArray_to_FallibleTArray_MoveConstruct)528 TYPED_TEST_P(TArray_MoveOnlyTest, nsTArray_to_FallibleTArray_MoveConstruct) {
529   auto moveOnlyArray = MakeMoveOnlyArray<nsTArray<TypeParam>>();
530   FallibleTArray<TypeParam> differentAllocatorMoveOnlyArray(
531       std::move(moveOnlyArray));
532 
533   ASSERT_EQ(0u, moveOnlyArray.Length());
534   ASSERT_EQ(kMoveOnlyTestArrayLength, differentAllocatorMoveOnlyArray.Length());
535 }
536 
TYPED_TEST_P(TArray_MoveOnlyTest,nsTArray_to_FallibleTArray_MoveAssign)537 TYPED_TEST_P(TArray_MoveOnlyTest, nsTArray_to_FallibleTArray_MoveAssign) {
538   auto moveOnlyArray = MakeMoveOnlyArray<nsTArray<TypeParam>>();
539   FallibleTArray<TypeParam> differentAllocatorMoveOnlyArray;
540   differentAllocatorMoveOnlyArray = std::move(moveOnlyArray);
541 
542   ASSERT_EQ(0u, moveOnlyArray.Length());
543   ASSERT_EQ(kMoveOnlyTestArrayLength, differentAllocatorMoveOnlyArray.Length());
544 }
545 
TYPED_TEST_P(TArray_MoveOnlyTest,FallibleTArray_to_nsTArray_MoveConstruct)546 TYPED_TEST_P(TArray_MoveOnlyTest, FallibleTArray_to_nsTArray_MoveConstruct) {
547   auto moveOnlyArray = MakeMoveOnlyArray<FallibleTArray<TypeParam>>();
548   nsTArray<TypeParam> differentAllocatorMoveOnlyArray(std::move(moveOnlyArray));
549 
550   ASSERT_EQ(0u, moveOnlyArray.Length());
551   ASSERT_EQ(kMoveOnlyTestArrayLength, differentAllocatorMoveOnlyArray.Length());
552 }
553 
TYPED_TEST_P(TArray_MoveOnlyTest,FallibleTArray_to_nsTArray_MoveAssign)554 TYPED_TEST_P(TArray_MoveOnlyTest, FallibleTArray_to_nsTArray_MoveAssign) {
555   auto moveOnlyArray = MakeMoveOnlyArray<FallibleTArray<TypeParam>>();
556   nsTArray<TypeParam> differentAllocatorMoveOnlyArray;
557   differentAllocatorMoveOnlyArray = std::move(moveOnlyArray);
558 
559   ASSERT_EQ(0u, moveOnlyArray.Length());
560   ASSERT_EQ(kMoveOnlyTestArrayLength, differentAllocatorMoveOnlyArray.Length());
561 }
562 
TYPED_TEST_P(TArray_MoveOnlyTest,AutoTArray_AutoStorage_MoveConstruct)563 TYPED_TEST_P(TArray_MoveOnlyTest, AutoTArray_AutoStorage_MoveConstruct) {
564   auto moveOnlyArray =
565       MakeMoveOnlyArray<AutoTArray<TypeParam, kMoveOnlyTestArrayLength>>();
566   AutoTArray<TypeParam, kMoveOnlyTestArrayLength> autoMoveOnlyArray(
567       std::move(moveOnlyArray));
568 
569   ASSERT_EQ(0u, moveOnlyArray.Length());
570   ASSERT_EQ(kMoveOnlyTestArrayLength, autoMoveOnlyArray.Length());
571 }
572 
TYPED_TEST_P(TArray_MoveOnlyTest,AutoTArray_AutoStorage_MoveAssign)573 TYPED_TEST_P(TArray_MoveOnlyTest, AutoTArray_AutoStorage_MoveAssign) {
574   auto moveOnlyArray =
575       MakeMoveOnlyArray<AutoTArray<TypeParam, kMoveOnlyTestArrayLength>>();
576   AutoTArray<TypeParam, kMoveOnlyTestArrayLength> autoMoveOnlyArray;
577   autoMoveOnlyArray = std::move(moveOnlyArray);
578 
579   ASSERT_EQ(0u, moveOnlyArray.Length());
580   ASSERT_EQ(kMoveOnlyTestArrayLength, autoMoveOnlyArray.Length());
581 }
582 
TYPED_TEST_P(TArray_MoveOnlyTest,nsTArray_to_AutoTArray_AutoStorage_MoveConstruct)583 TYPED_TEST_P(TArray_MoveOnlyTest,
584              nsTArray_to_AutoTArray_AutoStorage_MoveConstruct) {
585   auto moveOnlyArray = MakeMoveOnlyArray<nsTArray<TypeParam>>();
586   AutoTArray<TypeParam, kMoveOnlyTestArrayLength> autoMoveOnlyArray(
587       std::move(moveOnlyArray));
588 
589   ASSERT_EQ(0u, moveOnlyArray.Length());
590   ASSERT_EQ(kMoveOnlyTestArrayLength, autoMoveOnlyArray.Length());
591 }
592 
TYPED_TEST_P(TArray_MoveOnlyTest,nsTArray_to_AutoTArray_AutoStorage_MoveAssign)593 TYPED_TEST_P(TArray_MoveOnlyTest,
594              nsTArray_to_AutoTArray_AutoStorage_MoveAssign) {
595   auto moveOnlyArray = MakeMoveOnlyArray<nsTArray<TypeParam>>();
596   AutoTArray<TypeParam, kMoveOnlyTestArrayLength> autoMoveOnlyArray;
597   autoMoveOnlyArray = std::move(moveOnlyArray);
598 
599   ASSERT_EQ(0u, moveOnlyArray.Length());
600   ASSERT_EQ(kMoveOnlyTestArrayLength, autoMoveOnlyArray.Length());
601 }
602 
TYPED_TEST_P(TArray_MoveOnlyTest,nsTArray_to_AutoTArray_HeapStorage_MoveConstruct)603 TYPED_TEST_P(TArray_MoveOnlyTest,
604              nsTArray_to_AutoTArray_HeapStorage_MoveConstruct) {
605   auto moveOnlyArray = MakeMoveOnlyArray<nsTArray<TypeParam>>();
606   AutoTArray<TypeParam, kMoveOnlyTestArrayLength - 1> autoMoveOnlyArray(
607       std::move(moveOnlyArray));
608 
609   ASSERT_EQ(0u, moveOnlyArray.Length());
610   ASSERT_EQ(kMoveOnlyTestArrayLength, autoMoveOnlyArray.Length());
611 }
612 
TYPED_TEST_P(TArray_MoveOnlyTest,nsTArray_to_AutoTArray_HeapStorage_MoveAssign)613 TYPED_TEST_P(TArray_MoveOnlyTest,
614              nsTArray_to_AutoTArray_HeapStorage_MoveAssign) {
615   auto moveOnlyArray = MakeMoveOnlyArray<nsTArray<TypeParam>>();
616   AutoTArray<TypeParam, kMoveOnlyTestArrayLength - 1> autoMoveOnlyArray;
617   autoMoveOnlyArray = std::move(moveOnlyArray);
618 
619   ASSERT_EQ(0u, moveOnlyArray.Length());
620   ASSERT_EQ(kMoveOnlyTestArrayLength, autoMoveOnlyArray.Length());
621 }
622 
TYPED_TEST_P(TArray_MoveOnlyTest,FallibleTArray_to_AutoTArray_HeapStorage_MoveConstruct)623 TYPED_TEST_P(TArray_MoveOnlyTest,
624              FallibleTArray_to_AutoTArray_HeapStorage_MoveConstruct) {
625   auto moveOnlyArray = MakeMoveOnlyArray<FallibleTArray<TypeParam>>();
626   AutoTArray<TypeParam, 4> autoMoveOnlyArray(std::move(moveOnlyArray));
627 
628   ASSERT_EQ(0u, moveOnlyArray.Length());
629   ASSERT_EQ(kMoveOnlyTestArrayLength, autoMoveOnlyArray.Length());
630 }
631 
TYPED_TEST_P(TArray_MoveOnlyTest,FallibleTArray_to_AutoTArray_HeapStorage_MoveAssign)632 TYPED_TEST_P(TArray_MoveOnlyTest,
633              FallibleTArray_to_AutoTArray_HeapStorage_MoveAssign) {
634   auto moveOnlyArray = MakeMoveOnlyArray<FallibleTArray<TypeParam>>();
635   AutoTArray<TypeParam, 4> autoMoveOnlyArray;
636   autoMoveOnlyArray = std::move(moveOnlyArray);
637 
638   ASSERT_EQ(0u, moveOnlyArray.Length());
639   ASSERT_EQ(kMoveOnlyTestArrayLength, autoMoveOnlyArray.Length());
640 }
641 
642 REGISTER_TYPED_TEST_CASE_P(
643     TArray_MoveOnlyTest, nsTArray_MoveConstruct, nsTArray_MoveAssign,
644     nsTArray_MoveReAssign, nsTArray_to_FallibleTArray_MoveConstruct,
645     nsTArray_to_FallibleTArray_MoveAssign,
646     FallibleTArray_to_nsTArray_MoveConstruct,
647     FallibleTArray_to_nsTArray_MoveAssign, AutoTArray_AutoStorage_MoveConstruct,
648     AutoTArray_AutoStorage_MoveAssign,
649     nsTArray_to_AutoTArray_AutoStorage_MoveConstruct,
650     nsTArray_to_AutoTArray_AutoStorage_MoveAssign,
651     nsTArray_to_AutoTArray_HeapStorage_MoveConstruct,
652     nsTArray_to_AutoTArray_HeapStorage_MoveAssign,
653     FallibleTArray_to_AutoTArray_HeapStorage_MoveConstruct,
654     FallibleTArray_to_AutoTArray_HeapStorage_MoveAssign);
655 
656 using BothMoveOnlyTypes =
657     ::testing::Types<MoveOnly_RelocateUsingMemutils,
658                      MoveOnly_RelocateUsingMoveConstructor>;
659 INSTANTIATE_TYPED_TEST_CASE_P(InstantiationOf, TArray_MoveOnlyTest,
660                               BothMoveOnlyTypes);
661 
662 //----
663 
TEST(TArray,test_string_array)664 TEST(TArray, test_string_array)
665 {
666   nsTArray<nsCString> strArray;
667   const char kdata[] = "hello world";
668   size_t i;
669   for (i = 0; i < ArrayLength(kdata); ++i) {
670     nsCString str;
671     str.Assign(kdata[i]);
672     strArray.AppendElement(str);
673   }
674   for (i = 0; i < ArrayLength(kdata); ++i) {
675     ASSERT_EQ(strArray[i].CharAt(0), kdata[i]);
676   }
677 
678   const char kextra[] = "foo bar";
679   size_t oldLen = strArray.Length();
680   strArray.AppendElement(kextra);
681   strArray.RemoveElement(kextra);
682   ASSERT_EQ(oldLen, strArray.Length());
683 
684   ASSERT_EQ(strArray.IndexOf("e"), size_t(1));
685   ASSERT_TRUE(strArray.ApplyIf(
686       "e", [](size_t i, nsCString& s) { return i == 1 && s == "e"; },
687       []() { return false; }));
688 
689   strArray.Sort();
690   const char ksorted[] = "\0 dehllloorw";
691   for (i = ArrayLength(kdata); i--;) {
692     ASSERT_EQ(strArray[i].CharAt(0), ksorted[i]);
693     if (i > 0 && strArray[i] == strArray[i - 1]) strArray.RemoveElementAt(i);
694   }
695   for (i = 0; i < strArray.Length(); ++i) {
696     ASSERT_EQ(strArray.BinaryIndexOf(strArray[i]), i);
697   }
698   auto no_index = strArray.NoIndex;  // Fixes gtest compilation error
699   ASSERT_EQ(strArray.BinaryIndexOf(""_ns), no_index);
700 
701   nsCString rawArray[MOZ_ARRAY_LENGTH(kdata) - 1];
702   for (i = 0; i < ArrayLength(rawArray); ++i)
703     rawArray[i].Assign(kdata + i);  // substrings of kdata
704 
705   ASSERT_TRUE(
706       test_basic_array(rawArray, ArrayLength(rawArray), nsCString("foopy")));
707 }
708 
709 //----
710 
711 typedef nsCOMPtr<nsIFile> FilePointer;
712 
713 class nsFileNameComparator {
714  public:
Equals(const FilePointer & a,const char * b) const715   bool Equals(const FilePointer& a, const char* b) const {
716     nsAutoCString name;
717     a->GetNativeLeafName(name);
718     return name.Equals(b);
719   }
720 };
721 
TEST(TArray,test_comptr_array)722 TEST(TArray, test_comptr_array)
723 {
724   FilePointer tmpDir;
725   NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmpDir));
726   ASSERT_TRUE(tmpDir);
727   const char* kNames[] = {"foo.txt", "bar.html", "baz.gif"};
728   nsTArray<FilePointer> fileArray;
729   size_t i;
730   for (i = 0; i < ArrayLength(kNames); ++i) {
731     FilePointer f;
732     tmpDir->Clone(getter_AddRefs(f));
733     ASSERT_TRUE(f);
734     ASSERT_FALSE(NS_FAILED(f->AppendNative(nsDependentCString(kNames[i]))));
735     fileArray.AppendElement(f);
736   }
737 
738   ASSERT_EQ(fileArray.IndexOf(kNames[1], 0, nsFileNameComparator()), size_t(1));
739   ASSERT_TRUE(fileArray.ApplyIf(
740       kNames[1], 0, nsFileNameComparator(), [](size_t i) { return i == 1; },
741       []() { return false; }));
742 
743   // It's unclear what 'operator<' means for nsCOMPtr, but whatever...
744   ASSERT_TRUE(
745       test_basic_array(fileArray.Elements(), fileArray.Length(), tmpDir));
746 }
747 
748 //----
749 
750 class RefcountedObject {
751  public:
RefcountedObject()752   RefcountedObject() : rc(0) {}
AddRef()753   void AddRef() { ++rc; }
Release()754   void Release() {
755     if (--rc == 0) delete this;
756   }
757   ~RefcountedObject() = default;
758 
759  private:
760   int32_t rc;
761 };
762 
TEST(TArray,test_refptr_array)763 TEST(TArray, test_refptr_array)
764 {
765   nsTArray<RefPtr<RefcountedObject>> objArray;
766 
767   RefcountedObject* a = new RefcountedObject();
768   a->AddRef();
769   RefcountedObject* b = new RefcountedObject();
770   b->AddRef();
771   RefcountedObject* c = new RefcountedObject();
772   c->AddRef();
773 
774   objArray.AppendElement(a);
775   objArray.AppendElement(b);
776   objArray.AppendElement(c);
777 
778   ASSERT_EQ(objArray.IndexOf(b), size_t(1));
779   ASSERT_TRUE(objArray.ApplyIf(
780       b,
781       [&](size_t i, RefPtr<RefcountedObject>& r) { return i == 1 && r == b; },
782       []() { return false; }));
783 
784   a->Release();
785   b->Release();
786   c->Release();
787 }
788 
789 //----
790 
TEST(TArray,test_ptrarray)791 TEST(TArray, test_ptrarray)
792 {
793   nsTArray<uint32_t*> ary;
794   ASSERT_EQ(ary.SafeElementAt(0), nullptr);
795   ASSERT_EQ(ary.SafeElementAt(1000), nullptr);
796 
797   uint32_t a = 10;
798   ary.AppendElement(&a);
799   ASSERT_EQ(*ary[0], a);
800   ASSERT_EQ(*ary.SafeElementAt(0), a);
801 
802   nsTArray<const uint32_t*> cary;
803   ASSERT_EQ(cary.SafeElementAt(0), nullptr);
804   ASSERT_EQ(cary.SafeElementAt(1000), nullptr);
805 
806   const uint32_t b = 14;
807   cary.AppendElement(&a);
808   cary.AppendElement(&b);
809   ASSERT_EQ(*cary[0], a);
810   ASSERT_EQ(*cary[1], b);
811   ASSERT_EQ(*cary.SafeElementAt(0), a);
812   ASSERT_EQ(*cary.SafeElementAt(1), b);
813 }
814 
815 //----
816 
817 // This test relies too heavily on the existence of DebugGetHeader to be
818 // useful in non-debug builds.
819 #ifdef DEBUG
TEST(TArray,test_autoarray)820 TEST(TArray, test_autoarray)
821 {
822   uint32_t data[] = {4, 6, 8, 2, 4, 1, 5, 7, 3};
823   AutoTArray<uint32_t, MOZ_ARRAY_LENGTH(data)> array;
824 
825   void* hdr = array.DebugGetHeader();
826   ASSERT_NE(hdr, nsTArray<uint32_t>().DebugGetHeader());
827   ASSERT_NE(hdr,
828             (AutoTArray<uint32_t, MOZ_ARRAY_LENGTH(data)>().DebugGetHeader()));
829 
830   array.AppendElement(1u);
831   ASSERT_EQ(hdr, array.DebugGetHeader());
832 
833   array.RemoveElement(1u);
834   array.AppendElements(data, ArrayLength(data));
835   ASSERT_EQ(hdr, array.DebugGetHeader());
836 
837   array.AppendElement(2u);
838   ASSERT_NE(hdr, array.DebugGetHeader());
839 
840   array.Clear();
841   array.Compact();
842   ASSERT_EQ(hdr, array.DebugGetHeader());
843   array.AppendElements(data, ArrayLength(data));
844   ASSERT_EQ(hdr, array.DebugGetHeader());
845 
846   nsTArray<uint32_t> array2;
847   void* emptyHdr = array2.DebugGetHeader();
848   array.SwapElements(array2);
849   ASSERT_NE(emptyHdr, array.DebugGetHeader());
850   ASSERT_NE(hdr, array2.DebugGetHeader());
851   size_t i;
852   for (i = 0; i < ArrayLength(data); ++i) {
853     ASSERT_EQ(array2[i], data[i]);
854   }
855   ASSERT_TRUE(array.IsEmpty());
856 
857   array.Compact();
858   array.AppendElements(data, ArrayLength(data));
859   uint32_t data3[] = {5, 7, 11};
860   AutoTArray<uint32_t, MOZ_ARRAY_LENGTH(data3)> array3;
861   array3.AppendElements(data3, ArrayLength(data3));
862   array.SwapElements(array3);
863   for (i = 0; i < ArrayLength(data); ++i) {
864     ASSERT_EQ(array3[i], data[i]);
865   }
866   for (i = 0; i < ArrayLength(data3); ++i) {
867     ASSERT_EQ(array[i], data3[i]);
868   }
869 }
870 #endif
871 
872 //----
873 
874 // IndexOf used to potentially scan beyond the end of the array.  Test for
875 // this incorrect behavior by adding a value (5), removing it, then seeing
876 // if IndexOf finds it.
TEST(TArray,test_indexof)877 TEST(TArray, test_indexof)
878 {
879   nsTArray<int> array;
880   array.AppendElement(0);
881   // add and remove the 5
882   array.AppendElement(5);
883   array.RemoveElementAt(1);
884   // we should not find the 5!
885   auto no_index = array.NoIndex;  // Fixes gtest compilation error.
886   ASSERT_EQ(array.IndexOf(5, 1), no_index);
887   ASSERT_FALSE(array.ApplyIf(
888       5, 1, []() { return true; }, []() { return false; }));
889 }
890 
891 //----
892 
893 template <class Array>
is_heap(const Array & ary,size_t len)894 static bool is_heap(const Array& ary, size_t len) {
895   size_t index = 1;
896   while (index < len) {
897     if (ary[index] > ary[(index - 1) >> 1]) return false;
898     index++;
899   }
900   return true;
901 }
902 
903 //----
904 
905 // An array |arr| is using its auto buffer if |&arr < arr.Elements()| and
906 // |arr.Elements() - &arr| is small.
907 
908 #define IS_USING_AUTO(arr)                            \
909   ((uintptr_t) & (arr) < (uintptr_t)arr.Elements() && \
910    ((ptrdiff_t)arr.Elements() - (ptrdiff_t)&arr) <= 16)
911 
912 #define CHECK_IS_USING_AUTO(arr)     \
913   do {                               \
914     ASSERT_TRUE(IS_USING_AUTO(arr)); \
915   } while (0)
916 
917 #define CHECK_NOT_USING_AUTO(arr)     \
918   do {                                \
919     ASSERT_FALSE(IS_USING_AUTO(arr)); \
920   } while (0)
921 
922 #define CHECK_USES_SHARED_EMPTY_HDR(arr)          \
923   do {                                            \
924     nsTArray<int> _empty;                         \
925     ASSERT_EQ(_empty.Elements(), arr.Elements()); \
926   } while (0)
927 
928 #define CHECK_EQ_INT(actual, expected) \
929   do {                                 \
930     ASSERT_EQ((actual), (expected));   \
931   } while (0)
932 
933 #define CHECK_ARRAY(arr, data)                               \
934   do {                                                       \
935     CHECK_EQ_INT((arr).Length(), (size_t)ArrayLength(data)); \
936     for (size_t _i = 0; _i < ArrayLength(data); _i++) {      \
937       CHECK_EQ_INT((arr)[_i], (data)[_i]);                   \
938     }                                                        \
939   } while (0)
940 
TEST(TArray,test_swap)941 TEST(TArray, test_swap)
942 {
943   // Test nsTArray::SwapElements.  Unfortunately there are many cases.
944   int data1[] = {8, 6, 7, 5};
945   int data2[] = {3, 0, 9};
946 
947   // Swap two auto arrays.
948   {
949     AutoTArray<int, 8> a;
950     AutoTArray<int, 6> b;
951 
952     a.AppendElements(data1, ArrayLength(data1));
953     b.AppendElements(data2, ArrayLength(data2));
954     CHECK_IS_USING_AUTO(a);
955     CHECK_IS_USING_AUTO(b);
956 
957     a.SwapElements(b);
958 
959     CHECK_IS_USING_AUTO(a);
960     CHECK_IS_USING_AUTO(b);
961     CHECK_ARRAY(a, data2);
962     CHECK_ARRAY(b, data1);
963   }
964 
965   // Swap two auto arrays -- one whose data lives on the heap, the other whose
966   // data lives on the stack -- which each fits into the other's auto storage.
967   {
968     AutoTArray<int, 3> a;
969     AutoTArray<int, 3> b;
970 
971     a.AppendElements(data1, ArrayLength(data1));
972     a.RemoveElementAt(3);
973     b.AppendElements(data2, ArrayLength(data2));
974 
975     // Here and elsewhere, we assert that if we start with an auto array
976     // capable of storing N elements, we store N+1 elements into the array, and
977     // then we remove one element, that array is still not using its auto
978     // buffer.
979     //
980     // This isn't at all required by the TArray API. It would be fine if, when
981     // we shrink back to N elements, the TArray frees its heap storage and goes
982     // back to using its stack storage.  But we assert here as a check that the
983     // test does what we expect.  If the TArray implementation changes, just
984     // change the failing assertions.
985     CHECK_NOT_USING_AUTO(a);
986 
987     // This check had better not change, though.
988     CHECK_IS_USING_AUTO(b);
989 
990     a.SwapElements(b);
991 
992     CHECK_IS_USING_AUTO(b);
993     CHECK_ARRAY(a, data2);
994     int expectedB[] = {8, 6, 7};
995     CHECK_ARRAY(b, expectedB);
996   }
997 
998   // Swap two auto arrays which are using heap storage such that one fits into
999   // the other's auto storage, but the other needs to stay on the heap.
1000   {
1001     AutoTArray<int, 3> a;
1002     AutoTArray<int, 2> b;
1003     a.AppendElements(data1, ArrayLength(data1));
1004     a.RemoveElementAt(3);
1005 
1006     b.AppendElements(data2, ArrayLength(data2));
1007     b.RemoveElementAt(2);
1008 
1009     CHECK_NOT_USING_AUTO(a);
1010     CHECK_NOT_USING_AUTO(b);
1011 
1012     a.SwapElements(b);
1013 
1014     CHECK_NOT_USING_AUTO(b);
1015 
1016     int expected1[] = {3, 0};
1017     int expected2[] = {8, 6, 7};
1018 
1019     CHECK_ARRAY(a, expected1);
1020     CHECK_ARRAY(b, expected2);
1021   }
1022 
1023   // Swap two arrays, neither of which fits into the other's auto-storage.
1024   {
1025     AutoTArray<int, 1> a;
1026     AutoTArray<int, 3> b;
1027 
1028     a.AppendElements(data1, ArrayLength(data1));
1029     b.AppendElements(data2, ArrayLength(data2));
1030 
1031     a.SwapElements(b);
1032 
1033     CHECK_ARRAY(a, data2);
1034     CHECK_ARRAY(b, data1);
1035   }
1036 
1037   // Swap an empty nsTArray with a non-empty AutoTArray.
1038   {
1039     nsTArray<int> a;
1040     AutoTArray<int, 3> b;
1041 
1042     b.AppendElements(data2, ArrayLength(data2));
1043     CHECK_IS_USING_AUTO(b);
1044 
1045     a.SwapElements(b);
1046 
1047     CHECK_ARRAY(a, data2);
1048     CHECK_EQ_INT(b.Length(), size_t(0));
1049     CHECK_IS_USING_AUTO(b);
1050   }
1051 
1052   // Swap two big auto arrays.
1053   {
1054     const unsigned size = 8192;
1055     AutoTArray<unsigned, size> a;
1056     AutoTArray<unsigned, size> b;
1057 
1058     for (unsigned i = 0; i < size; i++) {
1059       a.AppendElement(i);
1060       b.AppendElement(i + 1);
1061     }
1062 
1063     CHECK_IS_USING_AUTO(a);
1064     CHECK_IS_USING_AUTO(b);
1065 
1066     a.SwapElements(b);
1067 
1068     CHECK_IS_USING_AUTO(a);
1069     CHECK_IS_USING_AUTO(b);
1070 
1071     CHECK_EQ_INT(a.Length(), size_t(size));
1072     CHECK_EQ_INT(b.Length(), size_t(size));
1073 
1074     for (unsigned i = 0; i < size; i++) {
1075       CHECK_EQ_INT(a[i], i + 1);
1076       CHECK_EQ_INT(b[i], i);
1077     }
1078   }
1079 
1080   // Swap two arrays and make sure that their capacities don't increase
1081   // unnecessarily.
1082   {
1083     nsTArray<int> a;
1084     nsTArray<int> b;
1085     b.AppendElements(data2, ArrayLength(data2));
1086 
1087     CHECK_EQ_INT(a.Capacity(), size_t(0));
1088     size_t bCapacity = b.Capacity();
1089 
1090     a.SwapElements(b);
1091 
1092     // Make sure that we didn't increase the capacity of either array.
1093     CHECK_ARRAY(a, data2);
1094     CHECK_EQ_INT(b.Length(), size_t(0));
1095     CHECK_EQ_INT(b.Capacity(), size_t(0));
1096     CHECK_EQ_INT(a.Capacity(), bCapacity);
1097   }
1098 
1099   // Swap an auto array with a TArray, then clear the auto array and make sure
1100   // it doesn't forget the fact that it has an auto buffer.
1101   {
1102     nsTArray<int> a;
1103     AutoTArray<int, 3> b;
1104 
1105     a.AppendElements(data1, ArrayLength(data1));
1106 
1107     a.SwapElements(b);
1108 
1109     CHECK_EQ_INT(a.Length(), size_t(0));
1110     CHECK_ARRAY(b, data1);
1111 
1112     b.Clear();
1113 
1114     CHECK_USES_SHARED_EMPTY_HDR(a);
1115     CHECK_IS_USING_AUTO(b);
1116   }
1117 
1118   // Same thing as the previous test, but with more auto arrays.
1119   {
1120     AutoTArray<int, 16> a;
1121     AutoTArray<int, 3> b;
1122 
1123     a.AppendElements(data1, ArrayLength(data1));
1124 
1125     a.SwapElements(b);
1126 
1127     CHECK_EQ_INT(a.Length(), size_t(0));
1128     CHECK_ARRAY(b, data1);
1129 
1130     b.Clear();
1131 
1132     CHECK_IS_USING_AUTO(a);
1133     CHECK_IS_USING_AUTO(b);
1134   }
1135 
1136   // Swap an empty nsTArray and an empty AutoTArray.
1137   {
1138     AutoTArray<int, 8> a;
1139     nsTArray<int> b;
1140 
1141     a.SwapElements(b);
1142 
1143     CHECK_IS_USING_AUTO(a);
1144     CHECK_NOT_USING_AUTO(b);
1145     CHECK_EQ_INT(a.Length(), size_t(0));
1146     CHECK_EQ_INT(b.Length(), size_t(0));
1147   }
1148 
1149   // Swap empty auto array with non-empty AutoTArray using malloc'ed storage.
1150   // I promise, all these tests have a point.
1151   {
1152     AutoTArray<int, 2> a;
1153     AutoTArray<int, 1> b;
1154 
1155     a.AppendElements(data1, ArrayLength(data1));
1156 
1157     a.SwapElements(b);
1158 
1159     CHECK_IS_USING_AUTO(a);
1160     CHECK_NOT_USING_AUTO(b);
1161     CHECK_ARRAY(b, data1);
1162     CHECK_EQ_INT(a.Length(), size_t(0));
1163   }
1164 
1165   // Test fallible SwapElements of nsTArray.
1166   {
1167     nsTArray<int> a;
1168     nsTArray<int> b;
1169 
1170     a.AppendElements(data1, ArrayLength(data1));
1171 
1172     ASSERT_TRUE(a.SwapElements(b, fallible));
1173 
1174     CHECK_ARRAY(b, data1);
1175     CHECK_EQ_INT(a.Length(), size_t(0));
1176   }
1177 
1178   // Test fallible SwapElements of FallibleTArray.
1179   {
1180     FallibleTArray<int> a;
1181     FallibleTArray<int> b;
1182 
1183     ASSERT_TRUE(a.AppendElements(data1, ArrayLength(data1), fallible));
1184 
1185     ASSERT_TRUE(a.SwapElements(b, fallible));
1186 
1187     CHECK_ARRAY(b, data1);
1188     CHECK_EQ_INT(a.Length(), size_t(0));
1189   }
1190 
1191   // Test fallible SwapElements of FallibleTArray with large AutoTArray.
1192   {
1193     FallibleTArray<int> a;
1194     AutoTArray<int, 8192> b;
1195 
1196     ASSERT_TRUE(a.AppendElements(data1, ArrayLength(data1), fallible));
1197 
1198     ASSERT_TRUE(a.SwapElements(b, fallible));
1199 
1200     CHECK_IS_USING_AUTO(b);
1201     CHECK_ARRAY(b, data1);
1202     CHECK_EQ_INT(a.Length(), size_t(0));
1203   }
1204 }
1205 
1206 // Bug 1171296: Disabled on andoid due to crashes.
1207 #if !defined(ANDROID)
TEST(TArray,test_fallible)1208 TEST(TArray, test_fallible)
1209 {
1210   // Test that FallibleTArray works properly; that is, it never OOMs, but
1211   // instead eventually returns false.
1212   //
1213   // This test is only meaningful on 32-bit systems.  On a 64-bit system, we
1214   // might never OOM.
1215   if (sizeof(void*) > 4) {
1216     ASSERT_TRUE(true);
1217     return;
1218   }
1219 
1220   // Allocate a bunch of 128MB arrays.  Larger allocations will fail on some
1221   // platforms without actually hitting OOM.
1222   //
1223   // 36 * 128MB > 4GB, so we should definitely OOM by the 36th array.
1224   const unsigned numArrays = 36;
1225   FallibleTArray<char> arrays[numArrays];
1226   bool oomed = false;
1227   for (size_t i = 0; i < numArrays; i++) {
1228     // SetCapacity allocates the requested capacity + a header, and we want to
1229     // avoid allocating more than 128MB overall because of the size padding it
1230     // will cause, which depends on allocator behavior, so use 128MB - an
1231     // arbitrary size larger than the array header, so that chances are good
1232     // that allocations will always be 128MB.
1233     bool success = arrays[i].SetCapacity(128 * 1024 * 1024 - 1024, fallible);
1234     if (!success) {
1235       // We got our OOM.  Check that it didn't come too early.
1236       oomed = true;
1237 #  ifdef XP_WIN
1238       // 32-bit Windows sometimes OOMs on the 6th, 7th, or 8th.  To keep the
1239       // test green, choose the lower of those: the important thing here is
1240       // that some allocations fail and some succeed.  We're not too
1241       // concerned about how many iterations it takes.
1242       const size_t kOOMIterations = 6;
1243 #  else
1244       const size_t kOOMIterations = 8;
1245 #  endif
1246       ASSERT_GE(i, kOOMIterations)
1247           << "Got OOM on iteration " << i << ". Too early!";
1248     }
1249   }
1250 
1251   ASSERT_TRUE(oomed)
1252   << "Didn't OOM or crash?  nsTArray::SetCapacity"
1253      "must be lying.";
1254 }
1255 #endif
1256 
TEST(TArray,test_conversion_operator)1257 TEST(TArray, test_conversion_operator)
1258 {
1259   FallibleTArray<int> f;
1260   const FallibleTArray<int> fconst;
1261 
1262   nsTArray<int> t;
1263   const nsTArray<int> tconst;
1264   AutoTArray<int, 8> tauto;
1265   const AutoTArray<int, 8> tautoconst;
1266 
1267 #define CHECK_ARRAY_CAST(type)                  \
1268   do {                                          \
1269     const type<int>& z1 = f;                    \
1270     ASSERT_EQ((void*)&z1, (void*)&f);           \
1271     const type<int>& z2 = fconst;               \
1272     ASSERT_EQ((void*)&z2, (void*)&fconst);      \
1273     const type<int>& z9 = t;                    \
1274     ASSERT_EQ((void*)&z9, (void*)&t);           \
1275     const type<int>& z10 = tconst;              \
1276     ASSERT_EQ((void*)&z10, (void*)&tconst);     \
1277     const type<int>& z11 = tauto;               \
1278     ASSERT_EQ((void*)&z11, (void*)&tauto);      \
1279     const type<int>& z12 = tautoconst;          \
1280     ASSERT_EQ((void*)&z12, (void*)&tautoconst); \
1281   } while (0)
1282 
1283   CHECK_ARRAY_CAST(FallibleTArray);
1284   CHECK_ARRAY_CAST(nsTArray);
1285 
1286 #undef CHECK_ARRAY_CAST
1287 }
1288 
1289 template <class T>
1290 struct BufAccessor : public T {
GetHdrTestTArray::BufAccessor1291   void* GetHdr() { return T::mHdr; }
1292 };
1293 
TEST(TArray,test_SetLengthAndRetainStorage_no_ctor)1294 TEST(TArray, test_SetLengthAndRetainStorage_no_ctor)
1295 {
1296   // 1050 because sizeof(int)*1050 is more than a page typically.
1297   const int N = 1050;
1298   FallibleTArray<int> f;
1299 
1300   nsTArray<int> t;
1301   AutoTArray<int, N> tauto;
1302 
1303 #define LPAREN (
1304 #define RPAREN )
1305 #define FOR_EACH(pre, post) \
1306   do {                      \
1307     pre f post;             \
1308     pre t post;             \
1309     pre tauto post;         \
1310   } while (0)
1311 
1312   // Setup test arrays.
1313   FOR_EACH(; Unused <<, .SetLength(N, fallible));
1314   for (int n = 0; n < N; ++n) {
1315     FOR_EACH(;, [n] = n);
1316   }
1317 
1318   void* initial_Hdrs[] = {
1319       static_cast<BufAccessor<FallibleTArray<int>>&>(f).GetHdr(),
1320       static_cast<BufAccessor<nsTArray<int>>&>(t).GetHdr(),
1321       static_cast<BufAccessor<AutoTArray<int, N>>&>(tauto).GetHdr(), nullptr};
1322 
1323   // SetLengthAndRetainStorage(n), should NOT overwrite memory when T hasn't
1324   // a default constructor.
1325   FOR_EACH(;, .SetLengthAndRetainStorage(8));
1326   FOR_EACH(;, .SetLengthAndRetainStorage(12));
1327   for (int n = 0; n < 12; ++n) {
1328     ASSERT_EQ(f[n], n);
1329     ASSERT_EQ(t[n], n);
1330     ASSERT_EQ(tauto[n], n);
1331   }
1332   FOR_EACH(;, .SetLengthAndRetainStorage(0));
1333   FOR_EACH(;, .SetLengthAndRetainStorage(N));
1334   for (int n = 0; n < N; ++n) {
1335     ASSERT_EQ(f[n], n);
1336     ASSERT_EQ(t[n], n);
1337     ASSERT_EQ(tauto[n], n);
1338   }
1339 
1340   void* current_Hdrs[] = {
1341       static_cast<BufAccessor<FallibleTArray<int>>&>(f).GetHdr(),
1342       static_cast<BufAccessor<nsTArray<int>>&>(t).GetHdr(),
1343       static_cast<BufAccessor<AutoTArray<int, N>>&>(tauto).GetHdr(), nullptr};
1344 
1345   // SetLengthAndRetainStorage(n) should NOT have reallocated the internal
1346   // memory.
1347   ASSERT_EQ(sizeof(initial_Hdrs), sizeof(current_Hdrs));
1348   for (size_t n = 0; n < sizeof(current_Hdrs) / sizeof(current_Hdrs[0]); ++n) {
1349     ASSERT_EQ(current_Hdrs[n], initial_Hdrs[n]);
1350   }
1351 
1352 #undef FOR_EACH
1353 #undef LPAREN
1354 #undef RPAREN
1355 }
1356 
1357 template <typename Comparator>
TestCompareMethods(const Comparator & aComp)1358 bool TestCompareMethods(const Comparator& aComp) {
1359   nsTArray<int> ary({57, 4, 16, 17, 3, 5, 96, 12});
1360 
1361   ary.Sort(aComp);
1362 
1363   const int sorted[] = {3, 4, 5, 12, 16, 17, 57, 96};
1364   for (size_t i = 0; i < MOZ_ARRAY_LENGTH(sorted); i++) {
1365     if (sorted[i] != ary[i]) {
1366       return false;
1367     }
1368   }
1369 
1370   if (!ary.ContainsSorted(5, aComp)) {
1371     return false;
1372   }
1373   if (ary.ContainsSorted(42, aComp)) {
1374     return false;
1375   }
1376 
1377   if (ary.BinaryIndexOf(16, aComp) != 4) {
1378     return false;
1379   }
1380 
1381   return true;
1382 }
1383 
1384 struct IntComparator {
EqualsTestTArray::IntComparator1385   bool Equals(int aLeft, int aRight) const { return aLeft == aRight; }
1386 
LessThanTestTArray::IntComparator1387   bool LessThan(int aLeft, int aRight) const { return aLeft < aRight; }
1388 };
1389 
TEST(TArray,test_comparator_objects)1390 TEST(TArray, test_comparator_objects)
1391 {
1392   ASSERT_TRUE(TestCompareMethods(IntComparator()));
1393   ASSERT_TRUE(
1394       TestCompareMethods([](int aLeft, int aRight) { return aLeft - aRight; }));
1395 }
1396 
1397 struct Big {
1398   uint64_t size[40] = {};
1399 };
1400 
TEST(TArray,test_AutoTArray_SwapElements)1401 TEST(TArray, test_AutoTArray_SwapElements)
1402 {
1403   AutoTArray<Big, 40> oneArray;
1404   AutoTArray<Big, 40> another;
1405 
1406   for (size_t i = 0; i < 8; ++i) {
1407     oneArray.AppendElement(Big());
1408   }
1409   oneArray[0].size[10] = 1;
1410   for (size_t i = 0; i < 9; ++i) {
1411     another.AppendElement(Big());
1412   }
1413   oneArray.SwapElements(another);
1414 
1415   ASSERT_EQ(oneArray.Length(), 9u);
1416   ASSERT_EQ(another.Length(), 8u);
1417 
1418   ASSERT_EQ(oneArray[0].size[10], 0u);
1419   ASSERT_EQ(another[0].size[10], 1u);
1420 }
1421 
1422 }  // namespace TestTArray
1423