summaryrefslogtreecommitdiffstats
path: root/xpcom/tests/gtest/TestTArray2.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xpcom/tests/gtest/TestTArray2.cpp')
-rw-r--r--xpcom/tests/gtest/TestTArray2.cpp1524
1 files changed, 1524 insertions, 0 deletions
diff --git a/xpcom/tests/gtest/TestTArray2.cpp b/xpcom/tests/gtest/TestTArray2.cpp
new file mode 100644
index 0000000000..2ccf7df4fa
--- /dev/null
+++ b/xpcom/tests/gtest/TestTArray2.cpp
@@ -0,0 +1,1524 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Unused.h"
+#include "mozilla/TimeStamp.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <iostream>
+#include "nsTArray.h"
+#include "nsString.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsXPCOM.h"
+#include "nsIFile.h"
+
+#include "gtest/gtest.h"
+#include "mozilla/gtest/MozAssertions.h"
+
+using namespace mozilla;
+
+namespace TestTArray {
+
+// Define this so we can use test_basic_array in test_comptr_array
+template <class T>
+inline bool operator<(const nsCOMPtr<T>& lhs, const nsCOMPtr<T>& rhs) {
+ return lhs.get() < rhs.get();
+}
+
+//----
+
+template <class ElementType>
+static bool test_basic_array(ElementType* data, size_t dataLen,
+ const ElementType& extra) {
+ CopyableTArray<ElementType> ary;
+ const nsTArray<ElementType>& cary = ary;
+
+ ary.AppendElements(data, dataLen);
+ if (ary.Length() != dataLen) {
+ return false;
+ }
+ if (!(ary == ary)) {
+ return false;
+ }
+ size_t i;
+ for (i = 0; i < ary.Length(); ++i) {
+ if (ary[i] != data[i]) return false;
+ }
+ for (i = 0; i < ary.Length(); ++i) {
+ if (ary.SafeElementAt(i, extra) != data[i]) return false;
+ }
+ if (ary.SafeElementAt(ary.Length(), extra) != extra ||
+ ary.SafeElementAt(ary.Length() * 10, extra) != extra)
+ return false;
+ // ensure sort results in ascending order
+ ary.Sort();
+ size_t j = 0, k = ary.IndexOfFirstElementGt(extra);
+ if (k != 0 && ary[k - 1] == extra) return false;
+ for (i = 0; i < ary.Length(); ++i) {
+ k = ary.IndexOfFirstElementGt(ary[i]);
+ if (k == 0 || ary[k - 1] != ary[i]) return false;
+ if (k < j) return false;
+ j = k;
+ }
+ for (i = ary.Length(); --i;) {
+ if (ary[i] < ary[i - 1]) return false;
+ if (ary[i] == ary[i - 1]) ary.RemoveElementAt(i);
+ }
+ if (!(ary == ary)) {
+ return false;
+ }
+ for (i = 0; i < ary.Length(); ++i) {
+ if (ary.BinaryIndexOf(ary[i]) != i) return false;
+ }
+ if (ary.BinaryIndexOf(extra) != ary.NoIndex) return false;
+ size_t oldLen = ary.Length();
+ ary.RemoveElement(data[dataLen / 2]);
+ if (ary.Length() != (oldLen - 1)) return false;
+ if (!(ary == ary)) return false;
+
+ if (ary.ApplyIf(
+ extra, []() { return true; }, []() { return false; }))
+ return false;
+ if (ary.ApplyIf(
+ extra, [](size_t) { return true; }, []() { return false; }))
+ return false;
+ // On a non-const array, ApplyIf's first lambda may use either const or non-
+ // const element types.
+ if (ary.ApplyIf(
+ extra, [](ElementType&) { return true; }, []() { return false; }))
+ return false;
+ if (ary.ApplyIf(
+ extra, [](const ElementType&) { return true; },
+ []() { return false; }))
+ return false;
+ if (ary.ApplyIf(
+ extra, [](size_t, ElementType&) { return true; },
+ []() { return false; }))
+ return false;
+ if (ary.ApplyIf(
+ extra, [](size_t, const ElementType&) { return true; },
+ []() { return false; }))
+ return false;
+
+ if (cary.ApplyIf(
+ extra, []() { return true; }, []() { return false; }))
+ if (cary.ApplyIf(
+ extra, [](size_t) { return true; }, []() { return false; }))
+ // On a const array, ApplyIf's first lambda must only use const element
+ // types.
+ if (cary.ApplyIf(
+ extra, [](const ElementType&) { return true; },
+ []() { return false; }))
+ if (cary.ApplyIf(
+ extra, [](size_t, const ElementType&) { return true; },
+ []() { return false; }))
+ return false;
+
+ size_t index = ary.Length() / 2;
+ ary.InsertElementAt(index, extra);
+ if (!(ary == ary)) return false;
+ if (ary[index] != extra) return false;
+ if (ary.IndexOf(extra) == ary.NoIndex) return false;
+ if (ary.LastIndexOf(extra) == ary.NoIndex) return false;
+ // ensure proper searching
+ if (ary.IndexOf(extra) > ary.LastIndexOf(extra)) return false;
+ if (ary.IndexOf(extra, index) != ary.LastIndexOf(extra, index)) return false;
+ if (!ary.ApplyIf(
+ extra,
+ [&](size_t i, const ElementType& e) {
+ return i == index && e == extra;
+ },
+ []() { return false; }))
+ return false;
+ if (!cary.ApplyIf(
+ extra,
+ [&](size_t i, const ElementType& e) {
+ return i == index && e == extra;
+ },
+ []() { return false; }))
+ return false;
+
+ nsTArray<ElementType> copy(ary.Clone());
+ if (!(ary == copy)) return false;
+ for (i = 0; i < copy.Length(); ++i) {
+ if (ary[i] != copy[i]) return false;
+ }
+ ary.AppendElements(copy);
+ size_t cap = ary.Capacity();
+ ary.RemoveElementsAt(copy.Length(), copy.Length());
+ ary.Compact();
+ if (ary.Capacity() == cap) return false;
+
+ ary.Clear();
+ if (ary.IndexOf(extra) != ary.NoIndex) return false;
+ if (ary.LastIndexOf(extra) != ary.NoIndex) return false;
+ if (ary.ApplyIf(
+ extra, []() { return true; }, []() { return false; }))
+ return false;
+ if (cary.ApplyIf(
+ extra, []() { return true; }, []() { return false; }))
+ return false;
+
+ ary.Clear();
+ if (!ary.IsEmpty()) return false;
+ if (!(ary == nsTArray<ElementType>())) return false;
+ if (ary == copy) return false;
+ if (ary.SafeElementAt(0, extra) != extra ||
+ ary.SafeElementAt(10, extra) != extra)
+ return false;
+
+ ary = copy;
+ if (!(ary == copy)) return false;
+ for (i = 0; i < copy.Length(); ++i) {
+ if (ary[i] != copy[i]) return false;
+ }
+
+ ary.InsertElementsAt(0, copy);
+ if (ary == copy) return false;
+ ary.RemoveElementsAt(0, copy.Length());
+ for (i = 0; i < copy.Length(); ++i) {
+ if (ary[i] != copy[i]) return false;
+ }
+
+ // These shouldn't crash!
+ nsTArray<ElementType> empty;
+ ary.AppendElements(reinterpret_cast<ElementType*>(0), 0);
+ ary.AppendElements(empty);
+
+ // See bug 324981
+ ary.RemoveElement(extra);
+ ary.RemoveElement(extra);
+
+ return true;
+}
+
+TEST(TArray, test_int_array)
+{
+ int data[] = {4, 6, 8, 2, 4, 1, 5, 7, 3};
+ ASSERT_TRUE(test_basic_array(data, ArrayLength(data), int(14)));
+}
+
+TEST(TArray, test_int64_array)
+{
+ int64_t data[] = {4, 6, 8, 2, 4, 1, 5, 7, 3};
+ ASSERT_TRUE(test_basic_array(data, ArrayLength(data), int64_t(14)));
+}
+
+TEST(TArray, test_char_array)
+{
+ char data[] = {4, 6, 8, 2, 4, 1, 5, 7, 3};
+ ASSERT_TRUE(test_basic_array(data, ArrayLength(data), char(14)));
+}
+
+TEST(TArray, test_uint32_array)
+{
+ uint32_t data[] = {4, 6, 8, 2, 4, 1, 5, 7, 3};
+ ASSERT_TRUE(test_basic_array(data, ArrayLength(data), uint32_t(14)));
+}
+
+//----
+
+class Object {
+ public:
+ Object() : mNum(0) {}
+ Object(const char* str, uint32_t num) : mStr(str), mNum(num) {}
+ Object(const Object& other) = default;
+ ~Object() = default;
+
+ Object& operator=(const Object& other) = default;
+
+ bool operator==(const Object& other) const {
+ return mStr == other.mStr && mNum == other.mNum;
+ }
+
+ bool operator<(const Object& other) const {
+ // sort based on mStr only
+ return Compare(mStr, other.mStr) < 0;
+ }
+
+ const char* Str() const { return mStr.get(); }
+ uint32_t Num() const { return mNum; }
+
+ private:
+ nsCString mStr;
+ uint32_t mNum;
+};
+
+TEST(TArray, test_object_array)
+{
+ nsTArray<Object> objArray;
+ const char kdata[] = "hello world";
+ size_t i;
+ for (i = 0; i < ArrayLength(kdata); ++i) {
+ char x[] = {kdata[i], '\0'};
+ objArray.AppendElement(Object(x, i));
+ }
+ for (i = 0; i < ArrayLength(kdata); ++i) {
+ ASSERT_EQ(objArray[i].Str()[0], kdata[i]);
+ ASSERT_EQ(objArray[i].Num(), i);
+ }
+ objArray.Sort();
+ const char ksorted[] = "\0 dehllloorw";
+ for (i = 0; i < ArrayLength(kdata) - 1; ++i) {
+ ASSERT_EQ(objArray[i].Str()[0], ksorted[i]);
+ }
+}
+
+class Countable {
+ static int sCount;
+
+ public:
+ Countable() { sCount++; }
+
+ Countable(const Countable& aOther) { sCount++; }
+
+ static int Count() { return sCount; }
+};
+
+class Moveable {
+ static int sCount;
+
+ public:
+ Moveable() { sCount++; }
+
+ Moveable(const Moveable& aOther) { sCount++; }
+
+ Moveable(Moveable&& aOther) {
+ // Do not increment sCount
+ }
+
+ static int Count() { return sCount; }
+};
+
+class MoveOnly_RelocateUsingMemutils {
+ public:
+ MoveOnly_RelocateUsingMemutils() = default;
+
+ MoveOnly_RelocateUsingMemutils(const MoveOnly_RelocateUsingMemutils&) =
+ delete;
+ MoveOnly_RelocateUsingMemutils(MoveOnly_RelocateUsingMemutils&&) = default;
+
+ MoveOnly_RelocateUsingMemutils& operator=(
+ const MoveOnly_RelocateUsingMemutils&) = delete;
+ MoveOnly_RelocateUsingMemutils& operator=(MoveOnly_RelocateUsingMemutils&&) =
+ default;
+};
+
+static_assert(
+ std::is_move_constructible_v<nsTArray<MoveOnly_RelocateUsingMemutils>>);
+static_assert(
+ std::is_move_assignable_v<nsTArray<MoveOnly_RelocateUsingMemutils>>);
+static_assert(
+ !std::is_copy_constructible_v<nsTArray<MoveOnly_RelocateUsingMemutils>>);
+static_assert(
+ !std::is_copy_assignable_v<nsTArray<MoveOnly_RelocateUsingMemutils>>);
+
+class MoveOnly_RelocateUsingMoveConstructor {
+ public:
+ MoveOnly_RelocateUsingMoveConstructor() = default;
+
+ MoveOnly_RelocateUsingMoveConstructor(
+ const MoveOnly_RelocateUsingMoveConstructor&) = delete;
+ MoveOnly_RelocateUsingMoveConstructor(
+ MoveOnly_RelocateUsingMoveConstructor&&) = default;
+
+ MoveOnly_RelocateUsingMoveConstructor& operator=(
+ const MoveOnly_RelocateUsingMoveConstructor&) = delete;
+ MoveOnly_RelocateUsingMoveConstructor& operator=(
+ MoveOnly_RelocateUsingMoveConstructor&&) = default;
+};
+} // namespace TestTArray
+
+MOZ_DECLARE_RELOCATE_USING_MOVE_CONSTRUCTOR(
+ TestTArray::MoveOnly_RelocateUsingMoveConstructor)
+
+namespace TestTArray {
+static_assert(std::is_move_constructible_v<
+ nsTArray<MoveOnly_RelocateUsingMoveConstructor>>);
+static_assert(
+ std::is_move_assignable_v<nsTArray<MoveOnly_RelocateUsingMoveConstructor>>);
+static_assert(!std::is_copy_constructible_v<
+ nsTArray<MoveOnly_RelocateUsingMoveConstructor>>);
+static_assert(!std::is_copy_assignable_v<
+ nsTArray<MoveOnly_RelocateUsingMoveConstructor>>);
+} // namespace TestTArray
+
+namespace TestTArray {
+
+/* static */
+int Countable::sCount = 0;
+/* static */
+int Moveable::sCount = 0;
+
+static nsTArray<int> returns_by_value() {
+ nsTArray<int> result;
+ return result;
+}
+
+TEST(TArray, test_return_by_value)
+{
+ nsTArray<int> result = returns_by_value();
+ ASSERT_TRUE(true); // This is just a compilation test.
+}
+
+TEST(TArray, test_move_array)
+{
+ nsTArray<Countable> countableArray;
+ uint32_t i;
+ for (i = 0; i < 4; ++i) {
+ countableArray.AppendElement(Countable());
+ }
+
+ ASSERT_EQ(Countable::Count(), 8);
+
+ const nsTArray<Countable>& constRefCountableArray = countableArray;
+
+ ASSERT_EQ(Countable::Count(), 8);
+
+ nsTArray<Countable> copyCountableArray(constRefCountableArray.Clone());
+
+ ASSERT_EQ(Countable::Count(), 12);
+
+ nsTArray<Countable>&& moveRefCountableArray = std::move(countableArray);
+ moveRefCountableArray.Length(); // Make compilers happy.
+
+ ASSERT_EQ(Countable::Count(), 12);
+
+ nsTArray<Countable> movedCountableArray(std::move(countableArray));
+
+ ASSERT_EQ(Countable::Count(), 12);
+
+ // Test ctor
+ FallibleTArray<Countable> differentAllocatorCountableArray(
+ std::move(copyCountableArray));
+ // operator=
+ copyCountableArray = std::move(differentAllocatorCountableArray);
+ differentAllocatorCountableArray = std::move(copyCountableArray);
+ // And the other ctor
+ nsTArray<Countable> copyCountableArray2(
+ std::move(differentAllocatorCountableArray));
+ // with auto
+ AutoTArray<Countable, 3> autoCountableArray(std::move(copyCountableArray2));
+ // operator=
+ copyCountableArray2 = std::move(autoCountableArray);
+ // Mix with FallibleTArray
+ FallibleTArray<Countable> differentAllocatorCountableArray2(
+ std::move(copyCountableArray2));
+ AutoTArray<Countable, 4> autoCountableArray2(
+ std::move(differentAllocatorCountableArray2));
+ differentAllocatorCountableArray2 = std::move(autoCountableArray2);
+
+ ASSERT_EQ(Countable::Count(), 12);
+
+ nsTArray<Moveable> moveableArray;
+ for (i = 0; i < 4; ++i) {
+ moveableArray.AppendElement(Moveable());
+ }
+
+ ASSERT_EQ(Moveable::Count(), 4);
+
+ const nsTArray<Moveable>& constRefMoveableArray = moveableArray;
+
+ ASSERT_EQ(Moveable::Count(), 4);
+
+ nsTArray<Moveable> copyMoveableArray(constRefMoveableArray.Clone());
+
+ ASSERT_EQ(Moveable::Count(), 8);
+
+ nsTArray<Moveable>&& moveRefMoveableArray = std::move(moveableArray);
+ moveRefMoveableArray.Length(); // Make compilers happy.
+
+ ASSERT_EQ(Moveable::Count(), 8);
+
+ nsTArray<Moveable> movedMoveableArray(std::move(moveableArray));
+
+ ASSERT_EQ(Moveable::Count(), 8);
+
+ // Test ctor
+ FallibleTArray<Moveable> differentAllocatorMoveableArray(
+ std::move(copyMoveableArray));
+ // operator=
+ copyMoveableArray = std::move(differentAllocatorMoveableArray);
+ differentAllocatorMoveableArray = std::move(copyMoveableArray);
+ // And the other ctor
+ nsTArray<Moveable> copyMoveableArray2(
+ std::move(differentAllocatorMoveableArray));
+ // with auto
+ AutoTArray<Moveable, 3> autoMoveableArray(std::move(copyMoveableArray2));
+ // operator=
+ copyMoveableArray2 = std::move(autoMoveableArray);
+ // Mix with FallibleTArray
+ FallibleTArray<Moveable> differentAllocatorMoveableArray2(
+ std::move(copyMoveableArray2));
+ AutoTArray<Moveable, 4> autoMoveableArray2(
+ std::move(differentAllocatorMoveableArray2));
+ differentAllocatorMoveableArray2 = std::move(autoMoveableArray2);
+
+ ASSERT_EQ(Moveable::Count(), 8);
+
+ AutoTArray<Moveable, 8> moveableAutoArray;
+ for (uint32_t i = 0; i < 4; ++i) {
+ moveableAutoArray.AppendElement(Moveable());
+ }
+
+ ASSERT_EQ(Moveable::Count(), 12);
+
+ const AutoTArray<Moveable, 8>& constRefMoveableAutoArray = moveableAutoArray;
+
+ ASSERT_EQ(Moveable::Count(), 12);
+
+ CopyableAutoTArray<Moveable, 8> copyMoveableAutoArray(
+ constRefMoveableAutoArray);
+
+ ASSERT_EQ(Moveable::Count(), 16);
+
+ AutoTArray<Moveable, 8> movedMoveableAutoArray(std::move(moveableAutoArray));
+
+ ASSERT_EQ(Moveable::Count(), 16);
+}
+
+template <typename TypeParam>
+class TArray_MoveOnlyTest : public ::testing::Test {};
+
+TYPED_TEST_SUITE_P(TArray_MoveOnlyTest);
+
+static constexpr size_t kMoveOnlyTestArrayLength = 4;
+
+template <typename ArrayType>
+static auto MakeMoveOnlyArray() {
+ ArrayType moveOnlyArray;
+ for (size_t i = 0; i < kMoveOnlyTestArrayLength; ++i) {
+ EXPECT_TRUE(moveOnlyArray.AppendElement(typename ArrayType::value_type(),
+ fallible));
+ }
+ return moveOnlyArray;
+}
+
+TYPED_TEST_P(TArray_MoveOnlyTest, nsTArray_MoveConstruct) {
+ auto moveOnlyArray = MakeMoveOnlyArray<nsTArray<TypeParam>>();
+ nsTArray<TypeParam> movedMoveOnlyArray(std::move(moveOnlyArray));
+
+ ASSERT_EQ(0u, moveOnlyArray.Length());
+ ASSERT_EQ(kMoveOnlyTestArrayLength, movedMoveOnlyArray.Length());
+}
+
+TYPED_TEST_P(TArray_MoveOnlyTest, nsTArray_MoveAssign) {
+ auto moveOnlyArray = MakeMoveOnlyArray<nsTArray<TypeParam>>();
+ nsTArray<TypeParam> movedMoveOnlyArray;
+ movedMoveOnlyArray = std::move(moveOnlyArray);
+
+ ASSERT_EQ(0u, moveOnlyArray.Length());
+ ASSERT_EQ(kMoveOnlyTestArrayLength, movedMoveOnlyArray.Length());
+}
+
+TYPED_TEST_P(TArray_MoveOnlyTest, nsTArray_MoveReAssign) {
+ nsTArray<TypeParam> movedMoveOnlyArray;
+ movedMoveOnlyArray = MakeMoveOnlyArray<nsTArray<TypeParam>>();
+ // Re-assign, to check that move-assign does not only work on an empty array.
+ movedMoveOnlyArray = MakeMoveOnlyArray<nsTArray<TypeParam>>();
+
+ ASSERT_EQ(kMoveOnlyTestArrayLength, movedMoveOnlyArray.Length());
+}
+
+TYPED_TEST_P(TArray_MoveOnlyTest, nsTArray_to_FallibleTArray_MoveConstruct) {
+ auto moveOnlyArray = MakeMoveOnlyArray<nsTArray<TypeParam>>();
+ FallibleTArray<TypeParam> differentAllocatorMoveOnlyArray(
+ std::move(moveOnlyArray));
+
+ ASSERT_EQ(0u, moveOnlyArray.Length());
+ ASSERT_EQ(kMoveOnlyTestArrayLength, differentAllocatorMoveOnlyArray.Length());
+}
+
+TYPED_TEST_P(TArray_MoveOnlyTest, nsTArray_to_FallibleTArray_MoveAssign) {
+ auto moveOnlyArray = MakeMoveOnlyArray<nsTArray<TypeParam>>();
+ FallibleTArray<TypeParam> differentAllocatorMoveOnlyArray;
+ differentAllocatorMoveOnlyArray = std::move(moveOnlyArray);
+
+ ASSERT_EQ(0u, moveOnlyArray.Length());
+ ASSERT_EQ(kMoveOnlyTestArrayLength, differentAllocatorMoveOnlyArray.Length());
+}
+
+TYPED_TEST_P(TArray_MoveOnlyTest, FallibleTArray_to_nsTArray_MoveConstruct) {
+ auto moveOnlyArray = MakeMoveOnlyArray<FallibleTArray<TypeParam>>();
+ nsTArray<TypeParam> differentAllocatorMoveOnlyArray(std::move(moveOnlyArray));
+
+ ASSERT_EQ(0u, moveOnlyArray.Length());
+ ASSERT_EQ(kMoveOnlyTestArrayLength, differentAllocatorMoveOnlyArray.Length());
+}
+
+TYPED_TEST_P(TArray_MoveOnlyTest, FallibleTArray_to_nsTArray_MoveAssign) {
+ auto moveOnlyArray = MakeMoveOnlyArray<FallibleTArray<TypeParam>>();
+ nsTArray<TypeParam> differentAllocatorMoveOnlyArray;
+ differentAllocatorMoveOnlyArray = std::move(moveOnlyArray);
+
+ ASSERT_EQ(0u, moveOnlyArray.Length());
+ ASSERT_EQ(kMoveOnlyTestArrayLength, differentAllocatorMoveOnlyArray.Length());
+}
+
+TYPED_TEST_P(TArray_MoveOnlyTest, AutoTArray_AutoStorage_MoveConstruct) {
+ auto moveOnlyArray =
+ MakeMoveOnlyArray<AutoTArray<TypeParam, kMoveOnlyTestArrayLength>>();
+ AutoTArray<TypeParam, kMoveOnlyTestArrayLength> autoMoveOnlyArray(
+ std::move(moveOnlyArray));
+
+ ASSERT_EQ(0u, moveOnlyArray.Length());
+ ASSERT_EQ(kMoveOnlyTestArrayLength, autoMoveOnlyArray.Length());
+}
+
+TYPED_TEST_P(TArray_MoveOnlyTest, AutoTArray_AutoStorage_MoveAssign) {
+ auto moveOnlyArray =
+ MakeMoveOnlyArray<AutoTArray<TypeParam, kMoveOnlyTestArrayLength>>();
+ AutoTArray<TypeParam, kMoveOnlyTestArrayLength> autoMoveOnlyArray;
+ autoMoveOnlyArray = std::move(moveOnlyArray);
+
+ ASSERT_EQ(0u, moveOnlyArray.Length());
+ ASSERT_EQ(kMoveOnlyTestArrayLength, autoMoveOnlyArray.Length());
+}
+
+TYPED_TEST_P(TArray_MoveOnlyTest,
+ nsTArray_to_AutoTArray_AutoStorage_MoveConstruct) {
+ auto moveOnlyArray = MakeMoveOnlyArray<nsTArray<TypeParam>>();
+ AutoTArray<TypeParam, kMoveOnlyTestArrayLength> autoMoveOnlyArray(
+ std::move(moveOnlyArray));
+
+ ASSERT_EQ(0u, moveOnlyArray.Length());
+ ASSERT_EQ(kMoveOnlyTestArrayLength, autoMoveOnlyArray.Length());
+}
+
+TYPED_TEST_P(TArray_MoveOnlyTest,
+ nsTArray_to_AutoTArray_AutoStorage_MoveAssign) {
+ auto moveOnlyArray = MakeMoveOnlyArray<nsTArray<TypeParam>>();
+ AutoTArray<TypeParam, kMoveOnlyTestArrayLength> autoMoveOnlyArray;
+ autoMoveOnlyArray = std::move(moveOnlyArray);
+
+ ASSERT_EQ(0u, moveOnlyArray.Length());
+ ASSERT_EQ(kMoveOnlyTestArrayLength, autoMoveOnlyArray.Length());
+}
+
+TYPED_TEST_P(TArray_MoveOnlyTest,
+ nsTArray_to_AutoTArray_HeapStorage_MoveConstruct) {
+ auto moveOnlyArray = MakeMoveOnlyArray<nsTArray<TypeParam>>();
+ AutoTArray<TypeParam, kMoveOnlyTestArrayLength - 1> autoMoveOnlyArray(
+ std::move(moveOnlyArray));
+
+ ASSERT_EQ(0u, moveOnlyArray.Length());
+ ASSERT_EQ(kMoveOnlyTestArrayLength, autoMoveOnlyArray.Length());
+}
+
+TYPED_TEST_P(TArray_MoveOnlyTest,
+ nsTArray_to_AutoTArray_HeapStorage_MoveAssign) {
+ auto moveOnlyArray = MakeMoveOnlyArray<nsTArray<TypeParam>>();
+ AutoTArray<TypeParam, kMoveOnlyTestArrayLength - 1> autoMoveOnlyArray;
+ autoMoveOnlyArray = std::move(moveOnlyArray);
+
+ ASSERT_EQ(0u, moveOnlyArray.Length());
+ ASSERT_EQ(kMoveOnlyTestArrayLength, autoMoveOnlyArray.Length());
+}
+
+TYPED_TEST_P(TArray_MoveOnlyTest,
+ FallibleTArray_to_AutoTArray_HeapStorage_MoveConstruct) {
+ auto moveOnlyArray = MakeMoveOnlyArray<FallibleTArray<TypeParam>>();
+ AutoTArray<TypeParam, 4> autoMoveOnlyArray(std::move(moveOnlyArray));
+
+ ASSERT_EQ(0u, moveOnlyArray.Length());
+ ASSERT_EQ(kMoveOnlyTestArrayLength, autoMoveOnlyArray.Length());
+}
+
+TYPED_TEST_P(TArray_MoveOnlyTest,
+ FallibleTArray_to_AutoTArray_HeapStorage_MoveAssign) {
+ auto moveOnlyArray = MakeMoveOnlyArray<FallibleTArray<TypeParam>>();
+ AutoTArray<TypeParam, 4> autoMoveOnlyArray;
+ autoMoveOnlyArray = std::move(moveOnlyArray);
+
+ ASSERT_EQ(0u, moveOnlyArray.Length());
+ ASSERT_EQ(kMoveOnlyTestArrayLength, autoMoveOnlyArray.Length());
+}
+
+REGISTER_TYPED_TEST_SUITE_P(
+ TArray_MoveOnlyTest, nsTArray_MoveConstruct, nsTArray_MoveAssign,
+ nsTArray_MoveReAssign, nsTArray_to_FallibleTArray_MoveConstruct,
+ nsTArray_to_FallibleTArray_MoveAssign,
+ FallibleTArray_to_nsTArray_MoveConstruct,
+ FallibleTArray_to_nsTArray_MoveAssign, AutoTArray_AutoStorage_MoveConstruct,
+ AutoTArray_AutoStorage_MoveAssign,
+ nsTArray_to_AutoTArray_AutoStorage_MoveConstruct,
+ nsTArray_to_AutoTArray_AutoStorage_MoveAssign,
+ nsTArray_to_AutoTArray_HeapStorage_MoveConstruct,
+ nsTArray_to_AutoTArray_HeapStorage_MoveAssign,
+ FallibleTArray_to_AutoTArray_HeapStorage_MoveConstruct,
+ FallibleTArray_to_AutoTArray_HeapStorage_MoveAssign);
+
+using BothMoveOnlyTypes =
+ ::testing::Types<MoveOnly_RelocateUsingMemutils,
+ MoveOnly_RelocateUsingMoveConstructor>;
+INSTANTIATE_TYPED_TEST_SUITE_P(InstantiationOf, TArray_MoveOnlyTest,
+ BothMoveOnlyTypes);
+
+//----
+
+TEST(TArray, test_string_array)
+{
+ nsTArray<nsCString> strArray;
+ const char kdata[] = "hello world";
+ size_t i;
+ for (i = 0; i < ArrayLength(kdata); ++i) {
+ nsCString str;
+ str.Assign(kdata[i]);
+ strArray.AppendElement(str);
+ }
+ for (i = 0; i < ArrayLength(kdata); ++i) {
+ ASSERT_EQ(strArray[i].CharAt(0), kdata[i]);
+ }
+
+ const char kextra[] = "foo bar";
+ size_t oldLen = strArray.Length();
+ strArray.AppendElement(kextra);
+ strArray.RemoveElement(kextra);
+ ASSERT_EQ(oldLen, strArray.Length());
+
+ ASSERT_EQ(strArray.IndexOf("e"), size_t(1));
+ ASSERT_TRUE(strArray.ApplyIf(
+ "e", [](size_t i, nsCString& s) { return i == 1 && s == "e"; },
+ []() { return false; }));
+
+ strArray.Sort();
+ const char ksorted[] = "\0 dehllloorw";
+ for (i = ArrayLength(kdata); i--;) {
+ ASSERT_EQ(strArray[i].CharAt(0), ksorted[i]);
+ if (i > 0 && strArray[i] == strArray[i - 1]) strArray.RemoveElementAt(i);
+ }
+ for (i = 0; i < strArray.Length(); ++i) {
+ ASSERT_EQ(strArray.BinaryIndexOf(strArray[i]), i);
+ }
+ auto no_index = strArray.NoIndex; // Fixes gtest compilation error
+ ASSERT_EQ(strArray.BinaryIndexOf(""_ns), no_index);
+
+ nsCString rawArray[MOZ_ARRAY_LENGTH(kdata) - 1];
+ for (i = 0; i < ArrayLength(rawArray); ++i)
+ rawArray[i].Assign(kdata + i); // substrings of kdata
+
+ ASSERT_TRUE(
+ test_basic_array(rawArray, ArrayLength(rawArray), nsCString("foopy")));
+}
+
+//----
+
+typedef nsCOMPtr<nsIFile> FilePointer;
+
+class nsFileNameComparator {
+ public:
+ bool Equals(const FilePointer& a, const char* b) const {
+ nsAutoCString name;
+ a->GetNativeLeafName(name);
+ return name.Equals(b);
+ }
+};
+
+TEST(TArray, test_comptr_array)
+{
+ FilePointer tmpDir;
+ NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmpDir));
+ ASSERT_TRUE(tmpDir);
+ const char* kNames[] = {"foo.txt", "bar.html", "baz.gif"};
+ nsTArray<FilePointer> fileArray;
+ size_t i;
+ for (i = 0; i < ArrayLength(kNames); ++i) {
+ FilePointer f;
+ tmpDir->Clone(getter_AddRefs(f));
+ ASSERT_TRUE(f);
+ ASSERT_NS_SUCCEEDED(f->AppendNative(nsDependentCString(kNames[i])));
+ fileArray.AppendElement(f);
+ }
+
+ ASSERT_EQ(fileArray.IndexOf(kNames[1], 0, nsFileNameComparator()), size_t(1));
+ ASSERT_TRUE(fileArray.ApplyIf(
+ kNames[1], 0, nsFileNameComparator(), [](size_t i) { return i == 1; },
+ []() { return false; }));
+
+ // It's unclear what 'operator<' means for nsCOMPtr, but whatever...
+ ASSERT_TRUE(
+ test_basic_array(fileArray.Elements(), fileArray.Length(), tmpDir));
+}
+
+//----
+
+class RefcountedObject {
+ public:
+ RefcountedObject() : rc(0) { val = std::rand(); }
+ void AddRef() {
+ MOZ_DIAGNOSTIC_ASSERT(rcchangeallowed);
+ ++rc;
+ }
+ void Release() {
+ MOZ_DIAGNOSTIC_ASSERT(rcchangeallowed);
+ if (--rc == 0) delete this;
+ }
+ ~RefcountedObject() = default;
+
+ int32_t GetVal() const { return val; }
+
+ static void AllowRCChange() { rcchangeallowed = true; }
+ static void ForbidRCChange() { rcchangeallowed = false; }
+
+ bool operator<(const RefcountedObject& b) const {
+ return this->GetVal() < b.GetVal();
+ };
+
+ bool operator==(const RefcountedObject& b) const {
+ return this->GetVal() == b.GetVal();
+ };
+
+ private:
+ int rc;
+ int32_t val;
+ static bool rcchangeallowed;
+};
+bool RefcountedObject::rcchangeallowed = true;
+
+class ObjectComparatorRaw {
+ public:
+ bool Equals(RefcountedObject* const& a, RefcountedObject* const& b) const {
+ return a->GetVal() == b->GetVal();
+ }
+
+ bool LessThan(RefcountedObject* const& a, RefcountedObject* const& b) const {
+ return a->GetVal() < b->GetVal();
+ }
+};
+
+class ObjectComparatorRefPtr {
+ public:
+ bool Equals(RefPtr<RefcountedObject> const& a,
+ RefPtr<RefcountedObject> const& b) const {
+ return a->GetVal() == b->GetVal();
+ }
+
+ bool LessThan(RefPtr<RefcountedObject> const& a,
+ RefPtr<RefcountedObject> const& b) const {
+ return a->GetVal() < b->GetVal();
+ }
+};
+
+TEST(TArray, test_refptr_array)
+{
+ nsTArray<RefPtr<RefcountedObject>> objArray;
+
+ RefcountedObject* a = new RefcountedObject();
+ a->AddRef();
+ RefcountedObject* b = new RefcountedObject();
+ b->AddRef();
+ RefcountedObject* c = new RefcountedObject();
+ c->AddRef();
+
+ objArray.AppendElement(a);
+ objArray.AppendElement(b);
+ objArray.AppendElement(c);
+
+ ASSERT_EQ(objArray.IndexOf(b), size_t(1));
+ ASSERT_TRUE(objArray.ApplyIf(
+ b,
+ [&](size_t i, RefPtr<RefcountedObject>& r) { return i == 1 && r == b; },
+ []() { return false; }));
+
+ a->Release();
+ b->Release();
+ c->Release();
+}
+
+TEST(TArray, test_sort_refptr)
+{
+ int numobjects = 1111111;
+ std::vector<RefPtr<RefcountedObject>> myobjects;
+ for (int i = 0; i < numobjects; i++) {
+ auto* obj = new RefcountedObject();
+ myobjects.push_back(obj);
+ }
+
+ {
+ nsTArray<RefPtr<RefcountedObject>> objArray(numobjects);
+ std::vector<RefPtr<RefcountedObject>> plainRefPtrArray(numobjects, nullptr);
+
+ for (int i = 0; i < numobjects; i++) {
+ objArray.AppendElement(myobjects[i]);
+ plainRefPtrArray[i] = myobjects[i];
+ }
+
+ ASSERT_EQ(objArray.IndexOf(myobjects[1]), size_t(1));
+ ASSERT_TRUE(objArray.ApplyIf(
+ myobjects[1],
+ [&](size_t i, RefPtr<RefcountedObject>& r) {
+ return i == 1 && r == myobjects[1];
+ },
+ []() { return false; }));
+
+ // Do not expect that sorting affects the reference counters of elements.
+ RefcountedObject::ForbidRCChange();
+
+ // Sort objArray with explicit, pointee value based comparator
+ objArray.Sort(ObjectComparatorRefPtr());
+ for (int i = 0; i < numobjects - 1; i++) {
+ ASSERT_TRUE(objArray[i]->GetVal() <= objArray[i + 1]->GetVal());
+ }
+
+ // std::sort plainRefPtrArray
+ auto comp = ObjectComparatorRefPtr();
+ std::sort(plainRefPtrArray.begin(), plainRefPtrArray.end(),
+ [&comp](auto const& left, auto const& right) {
+ return comp.LessThan(left, right);
+ });
+
+ // We expect the order to be the same.
+ for (int i = 0; i < numobjects; i++) {
+ ASSERT_TRUE(objArray[i]->GetVal() == plainRefPtrArray[i]->GetVal());
+ }
+
+ RefcountedObject::AllowRCChange();
+ // Destroy the arrays
+ }
+
+ for (int i = 0; i < numobjects; i++) {
+ myobjects.pop_back();
+ }
+}
+
+//----
+
+TEST(TArray, test_ptrarray)
+{
+ nsTArray<uint32_t*> ary;
+ ASSERT_EQ(ary.SafeElementAt(0), nullptr);
+ ASSERT_EQ(ary.SafeElementAt(1000), nullptr);
+
+ uint32_t a = 10;
+ ary.AppendElement(&a);
+ ASSERT_EQ(*ary[0], a);
+ ASSERT_EQ(*ary.SafeElementAt(0), a);
+
+ nsTArray<const uint32_t*> cary;
+ ASSERT_EQ(cary.SafeElementAt(0), nullptr);
+ ASSERT_EQ(cary.SafeElementAt(1000), nullptr);
+
+ const uint32_t b = 14;
+ cary.AppendElement(&a);
+ cary.AppendElement(&b);
+ ASSERT_EQ(*cary[0], a);
+ ASSERT_EQ(*cary[1], b);
+ ASSERT_EQ(*cary.SafeElementAt(0), a);
+ ASSERT_EQ(*cary.SafeElementAt(1), b);
+}
+
+//----
+
+// This test relies too heavily on the existence of DebugGetHeader to be
+// useful in non-debug builds.
+#ifdef DEBUG
+TEST(TArray, test_autoarray)
+{
+ uint32_t data[] = {4, 6, 8, 2, 4, 1, 5, 7, 3};
+ AutoTArray<uint32_t, MOZ_ARRAY_LENGTH(data)> array;
+
+ void* hdr = array.DebugGetHeader();
+ ASSERT_NE(hdr, nsTArray<uint32_t>().DebugGetHeader());
+ ASSERT_NE(hdr,
+ (AutoTArray<uint32_t, MOZ_ARRAY_LENGTH(data)>().DebugGetHeader()));
+
+ array.AppendElement(1u);
+ ASSERT_EQ(hdr, array.DebugGetHeader());
+
+ array.RemoveElement(1u);
+ array.AppendElements(data, ArrayLength(data));
+ ASSERT_EQ(hdr, array.DebugGetHeader());
+
+ array.AppendElement(2u);
+ ASSERT_NE(hdr, array.DebugGetHeader());
+
+ array.Clear();
+ array.Compact();
+ ASSERT_EQ(hdr, array.DebugGetHeader());
+ array.AppendElements(data, ArrayLength(data));
+ ASSERT_EQ(hdr, array.DebugGetHeader());
+
+ nsTArray<uint32_t> array2;
+ void* emptyHdr = array2.DebugGetHeader();
+ array.SwapElements(array2);
+ ASSERT_NE(emptyHdr, array.DebugGetHeader());
+ ASSERT_NE(hdr, array2.DebugGetHeader());
+ size_t i;
+ for (i = 0; i < ArrayLength(data); ++i) {
+ ASSERT_EQ(array2[i], data[i]);
+ }
+ ASSERT_TRUE(array.IsEmpty());
+
+ array.Compact();
+ array.AppendElements(data, ArrayLength(data));
+ uint32_t data3[] = {5, 7, 11};
+ AutoTArray<uint32_t, MOZ_ARRAY_LENGTH(data3)> array3;
+ array3.AppendElements(data3, ArrayLength(data3));
+ array.SwapElements(array3);
+ for (i = 0; i < ArrayLength(data); ++i) {
+ ASSERT_EQ(array3[i], data[i]);
+ }
+ for (i = 0; i < ArrayLength(data3); ++i) {
+ ASSERT_EQ(array[i], data3[i]);
+ }
+}
+#endif
+
+//----
+
+// IndexOf used to potentially scan beyond the end of the array. Test for
+// this incorrect behavior by adding a value (5), removing it, then seeing
+// if IndexOf finds it.
+TEST(TArray, test_indexof)
+{
+ nsTArray<int> array;
+ array.AppendElement(0);
+ // add and remove the 5
+ array.AppendElement(5);
+ array.RemoveElementAt(1);
+ // we should not find the 5!
+ auto no_index = array.NoIndex; // Fixes gtest compilation error.
+ ASSERT_EQ(array.IndexOf(5, 1), no_index);
+ ASSERT_FALSE(array.ApplyIf(
+ 5, 1, []() { return true; }, []() { return false; }));
+}
+
+//----
+
+template <class Array>
+static bool is_heap(const Array& ary, size_t len) {
+ size_t index = 1;
+ while (index < len) {
+ if (ary[index] > ary[(index - 1) >> 1]) return false;
+ index++;
+ }
+ return true;
+}
+
+//----
+
+// An array |arr| is using its auto buffer if |&arr < arr.Elements()| and
+// |arr.Elements() - &arr| is small.
+
+#define IS_USING_AUTO(arr) \
+ ((uintptr_t) & (arr) < (uintptr_t)(arr).Elements() && \
+ ((ptrdiff_t)(arr).Elements() - (ptrdiff_t) & (arr)) <= 16)
+
+#define CHECK_IS_USING_AUTO(arr) \
+ do { \
+ ASSERT_TRUE(IS_USING_AUTO(arr)); \
+ } while (0)
+
+#define CHECK_NOT_USING_AUTO(arr) \
+ do { \
+ ASSERT_FALSE(IS_USING_AUTO(arr)); \
+ } while (0)
+
+#define CHECK_USES_SHARED_EMPTY_HDR(arr) \
+ do { \
+ nsTArray<int> _empty; \
+ ASSERT_EQ(_empty.Elements(), (arr).Elements()); \
+ } while (0)
+
+#define CHECK_EQ_INT(actual, expected) \
+ do { \
+ ASSERT_EQ((actual), (expected)); \
+ } while (0)
+
+#define CHECK_ARRAY(arr, data) \
+ do { \
+ CHECK_EQ_INT((arr).Length(), (size_t)ArrayLength(data)); \
+ for (size_t _i = 0; _i < ArrayLength(data); _i++) { \
+ CHECK_EQ_INT((arr)[_i], (data)[_i]); \
+ } \
+ } while (0)
+
+TEST(TArray, test_swap)
+{
+ // Test nsTArray::SwapElements. Unfortunately there are many cases.
+ int data1[] = {8, 6, 7, 5};
+ int data2[] = {3, 0, 9};
+
+ // Swap two auto arrays.
+ {
+ AutoTArray<int, 8> a;
+ AutoTArray<int, 6> b;
+
+ a.AppendElements(data1, ArrayLength(data1));
+ b.AppendElements(data2, ArrayLength(data2));
+ CHECK_IS_USING_AUTO(a);
+ CHECK_IS_USING_AUTO(b);
+
+ a.SwapElements(b);
+
+ CHECK_IS_USING_AUTO(a);
+ CHECK_IS_USING_AUTO(b);
+ CHECK_ARRAY(a, data2);
+ CHECK_ARRAY(b, data1);
+ }
+
+ // Swap two auto arrays -- one whose data lives on the heap, the other whose
+ // data lives on the stack -- which each fits into the other's auto storage.
+ {
+ AutoTArray<int, 3> a;
+ AutoTArray<int, 3> b;
+
+ a.AppendElements(data1, ArrayLength(data1));
+ a.RemoveElementAt(3);
+ b.AppendElements(data2, ArrayLength(data2));
+
+ // Here and elsewhere, we assert that if we start with an auto array
+ // capable of storing N elements, we store N+1 elements into the array, and
+ // then we remove one element, that array is still not using its auto
+ // buffer.
+ //
+ // This isn't at all required by the TArray API. It would be fine if, when
+ // we shrink back to N elements, the TArray frees its heap storage and goes
+ // back to using its stack storage. But we assert here as a check that the
+ // test does what we expect. If the TArray implementation changes, just
+ // change the failing assertions.
+ CHECK_NOT_USING_AUTO(a);
+
+ // This check had better not change, though.
+ CHECK_IS_USING_AUTO(b);
+
+ a.SwapElements(b);
+
+ CHECK_IS_USING_AUTO(b);
+ CHECK_ARRAY(a, data2);
+ int expectedB[] = {8, 6, 7};
+ CHECK_ARRAY(b, expectedB);
+ }
+
+ // Swap two auto arrays which are using heap storage such that one fits into
+ // the other's auto storage, but the other needs to stay on the heap.
+ {
+ AutoTArray<int, 3> a;
+ AutoTArray<int, 2> b;
+ a.AppendElements(data1, ArrayLength(data1));
+ a.RemoveElementAt(3);
+
+ b.AppendElements(data2, ArrayLength(data2));
+ b.RemoveElementAt(2);
+
+ CHECK_NOT_USING_AUTO(a);
+ CHECK_NOT_USING_AUTO(b);
+
+ a.SwapElements(b);
+
+ CHECK_NOT_USING_AUTO(b);
+
+ int expected1[] = {3, 0};
+ int expected2[] = {8, 6, 7};
+
+ CHECK_ARRAY(a, expected1);
+ CHECK_ARRAY(b, expected2);
+ }
+
+ // Swap two arrays, neither of which fits into the other's auto-storage.
+ {
+ AutoTArray<int, 1> a;
+ AutoTArray<int, 3> b;
+
+ a.AppendElements(data1, ArrayLength(data1));
+ b.AppendElements(data2, ArrayLength(data2));
+
+ a.SwapElements(b);
+
+ CHECK_ARRAY(a, data2);
+ CHECK_ARRAY(b, data1);
+ }
+
+ // Swap an empty nsTArray with a non-empty AutoTArray.
+ {
+ nsTArray<int> a;
+ AutoTArray<int, 3> b;
+
+ b.AppendElements(data2, ArrayLength(data2));
+ CHECK_IS_USING_AUTO(b);
+
+ a.SwapElements(b);
+
+ CHECK_ARRAY(a, data2);
+ CHECK_EQ_INT(b.Length(), size_t(0));
+ CHECK_IS_USING_AUTO(b);
+ }
+
+ // Swap two big auto arrays.
+ {
+ const unsigned size = 8192;
+ AutoTArray<unsigned, size> a;
+ AutoTArray<unsigned, size> b;
+
+ for (unsigned i = 0; i < size; i++) {
+ a.AppendElement(i);
+ b.AppendElement(i + 1);
+ }
+
+ CHECK_IS_USING_AUTO(a);
+ CHECK_IS_USING_AUTO(b);
+
+ a.SwapElements(b);
+
+ CHECK_IS_USING_AUTO(a);
+ CHECK_IS_USING_AUTO(b);
+
+ CHECK_EQ_INT(a.Length(), size_t(size));
+ CHECK_EQ_INT(b.Length(), size_t(size));
+
+ for (unsigned i = 0; i < size; i++) {
+ CHECK_EQ_INT(a[i], i + 1);
+ CHECK_EQ_INT(b[i], i);
+ }
+ }
+
+ // Swap two arrays and make sure that their capacities don't increase
+ // unnecessarily.
+ {
+ nsTArray<int> a;
+ nsTArray<int> b;
+ b.AppendElements(data2, ArrayLength(data2));
+
+ CHECK_EQ_INT(a.Capacity(), size_t(0));
+ size_t bCapacity = b.Capacity();
+
+ a.SwapElements(b);
+
+ // Make sure that we didn't increase the capacity of either array.
+ CHECK_ARRAY(a, data2);
+ CHECK_EQ_INT(b.Length(), size_t(0));
+ CHECK_EQ_INT(b.Capacity(), size_t(0));
+ CHECK_EQ_INT(a.Capacity(), bCapacity);
+ }
+
+ // Swap an auto array with a TArray, then clear the auto array and make sure
+ // it doesn't forget the fact that it has an auto buffer.
+ {
+ nsTArray<int> a;
+ AutoTArray<int, 3> b;
+
+ a.AppendElements(data1, ArrayLength(data1));
+
+ a.SwapElements(b);
+
+ CHECK_EQ_INT(a.Length(), size_t(0));
+ CHECK_ARRAY(b, data1);
+
+ b.Clear();
+
+ CHECK_USES_SHARED_EMPTY_HDR(a);
+ CHECK_IS_USING_AUTO(b);
+ }
+
+ // Same thing as the previous test, but with more auto arrays.
+ {
+ AutoTArray<int, 16> a;
+ AutoTArray<int, 3> b;
+
+ a.AppendElements(data1, ArrayLength(data1));
+
+ a.SwapElements(b);
+
+ CHECK_EQ_INT(a.Length(), size_t(0));
+ CHECK_ARRAY(b, data1);
+
+ b.Clear();
+
+ CHECK_IS_USING_AUTO(a);
+ CHECK_IS_USING_AUTO(b);
+ }
+
+ // Swap an empty nsTArray and an empty AutoTArray.
+ {
+ AutoTArray<int, 8> a;
+ nsTArray<int> b;
+
+ a.SwapElements(b);
+
+ CHECK_IS_USING_AUTO(a);
+ CHECK_NOT_USING_AUTO(b);
+ CHECK_EQ_INT(a.Length(), size_t(0));
+ CHECK_EQ_INT(b.Length(), size_t(0));
+ }
+
+ // Swap empty auto array with non-empty AutoTArray using malloc'ed storage.
+ // I promise, all these tests have a point.
+ {
+ AutoTArray<int, 2> a;
+ AutoTArray<int, 1> b;
+
+ a.AppendElements(data1, ArrayLength(data1));
+
+ a.SwapElements(b);
+
+ CHECK_IS_USING_AUTO(a);
+ CHECK_NOT_USING_AUTO(b);
+ CHECK_ARRAY(b, data1);
+ CHECK_EQ_INT(a.Length(), size_t(0));
+ }
+
+ // Test fallible SwapElements of nsTArray.
+ {
+ nsTArray<int> a;
+ nsTArray<int> b;
+
+ a.AppendElements(data1, ArrayLength(data1));
+
+ ASSERT_TRUE(a.SwapElements(b, fallible));
+
+ CHECK_ARRAY(b, data1);
+ CHECK_EQ_INT(a.Length(), size_t(0));
+ }
+
+ // Test fallible SwapElements of FallibleTArray.
+ {
+ FallibleTArray<int> a;
+ FallibleTArray<int> b;
+
+ ASSERT_TRUE(a.AppendElements(data1, ArrayLength(data1), fallible));
+
+ ASSERT_TRUE(a.SwapElements(b, fallible));
+
+ CHECK_ARRAY(b, data1);
+ CHECK_EQ_INT(a.Length(), size_t(0));
+ }
+
+ // Test fallible SwapElements of FallibleTArray with large AutoTArray.
+ {
+ FallibleTArray<int> a;
+ AutoTArray<int, 8192> b;
+
+ ASSERT_TRUE(a.AppendElements(data1, ArrayLength(data1), fallible));
+
+ ASSERT_TRUE(a.SwapElements(b, fallible));
+
+ CHECK_IS_USING_AUTO(b);
+ CHECK_ARRAY(b, data1);
+ CHECK_EQ_INT(a.Length(), size_t(0));
+ }
+}
+
+// Bug 1171296: Disabled on andoid due to crashes.
+#if !defined(ANDROID)
+TEST(TArray, test_fallible)
+{
+ // Test that FallibleTArray works properly; that is, it never OOMs, but
+ // instead eventually returns false.
+ //
+ // This test is only meaningful on 32-bit systems. On a 64-bit system, we
+ // might never OOM.
+ if (sizeof(void*) > 4) {
+ ASSERT_TRUE(true);
+ return;
+ }
+
+ // Allocate a bunch of 128MB arrays. Larger allocations will fail on some
+ // platforms without actually hitting OOM.
+ //
+ // 36 * 128MB > 4GB, so we should definitely OOM by the 36th array.
+ const unsigned numArrays = 36;
+ FallibleTArray<char> arrays[numArrays];
+ bool oomed = false;
+ for (size_t i = 0; i < numArrays; i++) {
+ // SetCapacity allocates the requested capacity + a header, and we want to
+ // avoid allocating more than 128MB overall because of the size padding it
+ // will cause, which depends on allocator behavior, so use 128MB - an
+ // arbitrary size larger than the array header, so that chances are good
+ // that allocations will always be 128MB.
+ bool success = arrays[i].SetCapacity(128 * 1024 * 1024 - 1024, fallible);
+ if (!success) {
+ // We got our OOM. Check that it didn't come too early.
+ oomed = true;
+# ifdef XP_WIN
+ // 32-bit Windows sometimes OOMs on the 6th, 7th, or 8th. To keep the
+ // test green, choose the lower of those: the important thing here is
+ // that some allocations fail and some succeed. We're not too
+ // concerned about how many iterations it takes.
+ const size_t kOOMIterations = 6;
+# else
+ const size_t kOOMIterations = 8;
+# endif
+ ASSERT_GE(i, kOOMIterations)
+ << "Got OOM on iteration " << i << ". Too early!";
+ }
+ }
+
+ ASSERT_TRUE(oomed)
+ << "Didn't OOM or crash? nsTArray::SetCapacity"
+ "must be lying.";
+}
+#endif
+
+TEST(TArray, test_conversion_operator)
+{
+ FallibleTArray<int> f;
+ const FallibleTArray<int> fconst;
+
+ nsTArray<int> t;
+ const nsTArray<int> tconst;
+ AutoTArray<int, 8> tauto;
+ const AutoTArray<int, 8> tautoconst;
+
+#define CHECK_ARRAY_CAST(type) \
+ do { \
+ const type<int>& z1 = f; \
+ ASSERT_EQ((void*)&z1, (void*)&f); \
+ const type<int>& z2 = fconst; \
+ ASSERT_EQ((void*)&z2, (void*)&fconst); \
+ const type<int>& z9 = t; \
+ ASSERT_EQ((void*)&z9, (void*)&t); \
+ const type<int>& z10 = tconst; \
+ ASSERT_EQ((void*)&z10, (void*)&tconst); \
+ const type<int>& z11 = tauto; \
+ ASSERT_EQ((void*)&z11, (void*)&tauto); \
+ const type<int>& z12 = tautoconst; \
+ ASSERT_EQ((void*)&z12, (void*)&tautoconst); \
+ } while (0)
+
+ CHECK_ARRAY_CAST(FallibleTArray);
+ CHECK_ARRAY_CAST(nsTArray);
+
+#undef CHECK_ARRAY_CAST
+}
+
+template <class T>
+struct BufAccessor : public T {
+ void* GetHdr() { return T::mHdr; }
+};
+
+TEST(TArray, test_SetLengthAndRetainStorage_no_ctor)
+{
+ // 1050 because sizeof(int)*1050 is more than a page typically.
+ const int N = 1050;
+ FallibleTArray<int> f;
+
+ nsTArray<int> t;
+ AutoTArray<int, N> tauto;
+
+#define LPAREN (
+#define RPAREN )
+#define FOR_EACH(pre, post) \
+ do { \
+ pre f post; \
+ pre t post; \
+ pre tauto post; \
+ } while (0)
+
+ // Setup test arrays.
+ FOR_EACH(; Unused <<, .SetLength(N, fallible));
+ for (int n = 0; n < N; ++n) {
+ FOR_EACH(;, [n] = n);
+ }
+
+ void* initial_Hdrs[] = {
+ static_cast<BufAccessor<FallibleTArray<int>>&>(f).GetHdr(),
+ static_cast<BufAccessor<nsTArray<int>>&>(t).GetHdr(),
+ static_cast<BufAccessor<AutoTArray<int, N>>&>(tauto).GetHdr(), nullptr};
+
+ // SetLengthAndRetainStorage(n), should NOT overwrite memory when T hasn't
+ // a default constructor.
+ FOR_EACH(;, .SetLengthAndRetainStorage(8));
+ FOR_EACH(;, .SetLengthAndRetainStorage(12));
+ for (int n = 0; n < 12; ++n) {
+ ASSERT_EQ(f[n], n);
+ ASSERT_EQ(t[n], n);
+ ASSERT_EQ(tauto[n], n);
+ }
+ FOR_EACH(;, .SetLengthAndRetainStorage(0));
+ FOR_EACH(;, .SetLengthAndRetainStorage(N));
+ for (int n = 0; n < N; ++n) {
+ ASSERT_EQ(f[n], n);
+ ASSERT_EQ(t[n], n);
+ ASSERT_EQ(tauto[n], n);
+ }
+
+ void* current_Hdrs[] = {
+ static_cast<BufAccessor<FallibleTArray<int>>&>(f).GetHdr(),
+ static_cast<BufAccessor<nsTArray<int>>&>(t).GetHdr(),
+ static_cast<BufAccessor<AutoTArray<int, N>>&>(tauto).GetHdr(), nullptr};
+
+ // SetLengthAndRetainStorage(n) should NOT have reallocated the internal
+ // memory.
+ ASSERT_EQ(sizeof(initial_Hdrs), sizeof(current_Hdrs));
+ for (size_t n = 0; n < sizeof(current_Hdrs) / sizeof(current_Hdrs[0]); ++n) {
+ ASSERT_EQ(current_Hdrs[n], initial_Hdrs[n]);
+ }
+
+#undef FOR_EACH
+#undef LPAREN
+#undef RPAREN
+}
+
+template <typename Comparator>
+bool TestCompareMethods(const Comparator& aComp) {
+ nsTArray<int> ary({57, 4, 16, 17, 3, 5, 96, 12});
+
+ ary.Sort(aComp);
+
+ const int sorted[] = {3, 4, 5, 12, 16, 17, 57, 96};
+ for (size_t i = 0; i < MOZ_ARRAY_LENGTH(sorted); i++) {
+ if (sorted[i] != ary[i]) {
+ return false;
+ }
+ }
+
+ if (!ary.ContainsSorted(5, aComp)) {
+ return false;
+ }
+ if (ary.ContainsSorted(42, aComp)) {
+ return false;
+ }
+
+ if (ary.BinaryIndexOf(16, aComp) != 4) {
+ return false;
+ }
+
+ return true;
+}
+
+struct IntComparator {
+ bool Equals(int aLeft, int aRight) const { return aLeft == aRight; }
+
+ bool LessThan(int aLeft, int aRight) const { return aLeft < aRight; }
+};
+
+TEST(TArray, test_comparator_objects)
+{
+ ASSERT_TRUE(TestCompareMethods(IntComparator()));
+ ASSERT_TRUE(
+ TestCompareMethods([](int aLeft, int aRight) { return aLeft - aRight; }));
+}
+
+struct Big {
+ uint64_t size[40] = {};
+};
+
+TEST(TArray, test_AutoTArray_SwapElements)
+{
+ AutoTArray<Big, 40> oneArray;
+ AutoTArray<Big, 40> another;
+
+ for (size_t i = 0; i < 8; ++i) {
+ oneArray.AppendElement(Big());
+ }
+ oneArray[0].size[10] = 1;
+ for (size_t i = 0; i < 9; ++i) {
+ another.AppendElement(Big());
+ }
+ oneArray.SwapElements(another);
+
+ ASSERT_EQ(oneArray.Length(), 9u);
+ ASSERT_EQ(another.Length(), 8u);
+
+ ASSERT_EQ(oneArray[0].size[10], 0u);
+ ASSERT_EQ(another[0].size[10], 1u);
+}
+
+} // namespace TestTArray