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