# Global Metrics

path: .metrics.halstead.effort
old: 275810.3077690965
new: 7968143.125638689
path: .metrics.halstead.length
old: 1067.0
new: 6493.0
path: .metrics.halstead.volume
old: 7733.5386568443055
new: 55292.43904997829
path: .metrics.halstead.time
old: 15322.794876060918
new: 442674.61809103825
path: .metrics.halstead.N2
old: 531.0
new: 2642.0
path: .metrics.halstead.level
old: 0.02803933877380205
new: 0.00693918748422912
path: .metrics.halstead.difficulty
old: 35.66417910447761
new: 144.1090909090909
path: .metrics.halstead.vocabulary
old: 152.0
new: 366.0
path: .metrics.halstead.n2
old: 134.0
new: 330.0
path: .metrics.halstead.N1
old: 536.0
new: 3851.0
path: .metrics.halstead.bugs
old: 1.412382758120731
new: 13.29791327245145
path: .metrics.halstead.estimated_program_length
old: 1021.914601547303
new: 2947.0036307530427
path: .metrics.halstead.n1
old: 18.0
new: 36.0
path: .metrics.halstead.purity_ratio
old: 0.9577456434370224
new: 0.45387396130495034
path: .metrics.cyclomatic.average
old: 1.75
new: 2.2247191011235956
path: .metrics.cyclomatic.sum
old: 7.0
new: 198.0
path: .metrics.loc.lloc
old: 31.0
new: 508.0
path: .metrics.loc.ploc
old: 146.0
new: 1011.0
path: .metrics.loc.sloc
old: 215.0
new: 1422.0
path: .metrics.loc.blank
old: 46.0
new: 293.0
path: .metrics.loc.cloc
old: 23.0
new: 118.0
path: .metrics.mi.mi_sei
old: 0.9660247870779912
new: -104.55881276717582
path: .metrics.mi.mi_original
old: 35.82839048507509
new: -48.93511323892007
path: .metrics.mi.mi_visual_studio
old: 20.95227513747081
new: 0.0
path: .metrics.nargs.sum
old: 12.0
new: 94.0
path: .metrics.nargs.average
old: 4.0
new: 0.8468468468468469
path: .metrics.nexits.sum
old: 2.0
new: 103.0
path: .metrics.nexits.average
old: 0.6666666666666666
new: 0.927927927927928
path: .metrics.cognitive.sum
old: 3.0
new: 113.0
path: .metrics.cognitive.average
old: 1.0
new: 1.018018018018018
path: .metrics.nom.total
old: 3.0
new: 111.0
path: .metrics.nom.closures
old: 0.0
new: 37.0
path: .metrics.nom.functions
old: 3.0
new: 74.0

# Spaces Data

## Minimal test - lines (25, 335)

path: .spaces[0].metrics.nom.closures
old: 0.0
new: 28.0
path: .spaces[0].metrics.nom.functions
old: 1.0
new: 33.0
path: .spaces[0].metrics.nom.total
old: 1.0
new: 61.0
path: .spaces[0].metrics.mi.mi_original
old: 66.4612079100047
new: 3.5479775642512834
path: .spaces[0].metrics.mi.mi_visual_studio
old: 38.866203456143104
new: 2.0748406808487037
path: .spaces[0].metrics.mi.mi_sei
old: 20.386042790050325
new: -43.80869978924521
path: .spaces[0].metrics.cyclomatic.average
old: 2.0
new: 2.8205128205128207
path: .spaces[0].metrics.cyclomatic.sum
old: 2.0
new: 110.0
path: .spaces[0].metrics.nexits.average
old: 1.0
new: 1.3114754098360657
path: .spaces[0].metrics.nexits.sum
old: 1.0
new: 80.0
path: .spaces[0].metrics.cognitive.average
old: 1.0
new: 1.1967213114754098
path: .spaces[0].metrics.cognitive.sum
old: 1.0
new: 73.0
path: .spaces[0].metrics.loc.sloc
old: 56.0
new: 311.0
path: .spaces[0].metrics.loc.cloc
old: 0.0
new: 13.0
path: .spaces[0].metrics.loc.lloc
old: 11.0
new: 171.0
path: .spaces[0].metrics.loc.blank
old: 16.0
new: 43.0
path: .spaces[0].metrics.loc.ploc
old: 40.0
new: 255.0
path: .spaces[0].metrics.nargs.average
old: 4.0
new: 0.4426229508196721
path: .spaces[0].metrics.nargs.sum
old: 4.0
new: 27.0
path: .spaces[0].metrics.halstead.volume
old: 1762.755689410334
new: 12775.248378070268
path: .spaces[0].metrics.halstead.vocabulary
old: 75.0
new: 125.0
path: .spaces[0].metrics.halstead.effort
old: 32984.445442525575
new: 1304406.0896025498
path: .spaces[0].metrics.halstead.N2
old: 138.0
new: 676.0
path: .spaces[0].metrics.halstead.estimated_program_length
old: 411.07593991234864
new: 773.0378489279306
path: .spaces[0].metrics.halstead.N1
old: 145.0
new: 1158.0
path: .spaces[0].metrics.halstead.bugs
old: 0.34283477677033547
new: 3.9794277462644314
path: .spaces[0].metrics.halstead.time
old: 1832.4691912514209
new: 72467.00497791944
path: .spaces[0].metrics.halstead.difficulty
old: 18.71186440677966
new: 102.10416666666669
path: .spaces[0].metrics.halstead.n1
old: 16.0
new: 29.0
path: .spaces[0].metrics.halstead.level
old: 0.05344202898550725
new: 0.009793919608243214
path: .spaces[0].metrics.halstead.length
old: 283.0
new: 1834.0
path: .spaces[0].metrics.halstead.n2
old: 59.0
new: 96.0
path: .spaces[0].metrics.halstead.purity_ratio
old: 1.4525651587008783
new: 0.4215037344209

### Code

    namespace TestTArray {

    // Define this so we can use test_basic_array in test_comptr_array
    template
    inline bool operator<(const nsCOMPtr& lhs, const nsCOMPtr& rhs) {
      return lhs.get() < rhs.get();
    }

    //----

    template
    static bool test_basic_array(ElementType* data, size_t dataLen,
                                 const ElementType& extra) {
      CopyableTArray ary;
      const nsTArray& 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 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())) 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 empty;
      ary.AppendElements(reinterpret_cast(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 mStr.Compare(other.mStr.get()) < 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

## Minimal test - lines (340, 349)

path: .spaces[1].metrics.mi.mi_sei
old: 32.20128999153216
new: 78.53601731121599
path: .spaces[1].metrics.mi.mi_original
old: 63.32801827216964
new: 106.83827494745456
path: .spaces[1].metrics.mi.mi_visual_studio
old: 37.0339288141343
new: 62.478523361084534
path: .spaces[1].metrics.nexits.sum
old: 1.0
new: 0.0
path: .spaces[1].metrics.nexits.average
old: 1.0
new: null
path: .spaces[1].metrics.loc.blank
old: 15.0
new: 0.0
path: .spaces[1].metrics.loc.cloc
old: 3.0
new: 0.0
path: .spaces[1].metrics.loc.lloc
old: 11.0
new: 0.0
path: .spaces[1].metrics.loc.ploc
old: 47.0
new: 10.0
path: .spaces[1].metrics.loc.sloc
old: 65.0
new: 10.0
path: .spaces[1].metrics.nargs.sum
old: 4.0
new: 0.0
path: .spaces[1].metrics.nargs.average
old: 4.0
new: null
path: .spaces[1].metrics.cognitive.sum
old: 1.0
new: 0.0
path: .spaces[1].metrics.cognitive.average
old: 1.0
new: null
path: .spaces[1].metrics.nom.functions
old: 1.0
new: 0.0
path: .spaces[1].metrics.nom.total
old: 1.0
new: 0.0
path: .spaces[1].metrics.halstead.N2
old: 161.0
new: 13.0
path: .spaces[1].metrics.halstead.n2
old: 56.0
new: 7.0
path: .spaces[1].metrics.halstead.volume
old: 2024.0726307537657
new: 167.5236165705346
path: .spaces[1].metrics.halstead.level
old: 0.04092071611253197
new: 0.15384615384615383
path: .spaces[1].metrics.halstead.N1
old: 166.0
new: 31.0
path: .spaces[1].metrics.halstead.time
old: 2747.9597174469527
new: 60.49463931713749
path: .spaces[1].metrics.halstead.bugs
old: 0.44915956931870626
new: 0.035280793408348214
path: .spaces[1].metrics.halstead.effort
old: 49463.27491404515
new: 1088.9035077084748
path: .spaces[1].metrics.halstead.purity_ratio
old: 1.2070297979708915
new: 0.8932492933819649
path: .spaces[1].metrics.halstead.difficulty
old: 24.4375
new: 6.5
path: .spaces[1].metrics.halstead.vocabulary
old: 73.0
new: 14.0
path: .spaces[1].metrics.halstead.estimated_program_length
old: 394.6987439364815
new: 39.302968908806456
path: .spaces[1].metrics.halstead.n1
old: 17.0
new: 7.0
path: .spaces[1].metrics.halstead.length
old: 327.0
new: 44.0
path: .spaces[1].metrics.cyclomatic.sum
old: 2.0
new: 1.0
path: .spaces[1].metrics.cyclomatic.average
old: 2.0
new: 1.0

### Code

    namespace TestTArray {
    static_assert(std::is_move_constructible_v<
                  nsTArray>);
    static_assert(
        std::is_move_assignable_v>);
    static_assert(!std::is_copy_constructible_v<
                  nsTArray>);
    static_assert(!std::is_copy_assignable_v<
                  nsTArray>);
    }  // namespace TestTArray

## Minimal test - lines (351, 1422)

path: .spaces[2].metrics.cyclomatic.sum
old: 2.0
new: 86.0
path: .spaces[2].metrics.cyclomatic.average
old: 2.0
new: 1.7916666666666667
path: .spaces[2].metrics.loc.ploc
old: 51.0
new: 730.0
path: .spaces[2].metrics.loc.cloc
old: 1.0
new: 97.0
path: .spaces[2].metrics.loc.blank
old: 8.0
new: 245.0
path: .spaces[2].metrics.loc.lloc
old: 9.0
new: 336.0
path: .spaces[2].metrics.loc.sloc
old: 60.0
new: 1072.0
path: .spaces[2].metrics.mi.mi_sei
old: 25.290167431998434
new: -68.52720793253948
path: .spaces[2].metrics.mi.mi_visual_studio
old: 36.82756359852001
new: 0.0
path: .spaces[2].metrics.mi.mi_original
old: 62.97513375346921
new: -16.669549074146104
path: .spaces[2].metrics.nargs.sum
old: 4.0
new: 67.0
path: .spaces[2].metrics.nargs.average
old: 4.0
new: 1.34
path: .spaces[2].metrics.nom.functions
old: 1.0
new: 41.0
path: .spaces[2].metrics.nom.closures
old: 0.0
new: 9.0
path: .spaces[2].metrics.nom.total
old: 1.0
new: 50.0
path: .spaces[2].metrics.nexits.sum
old: 0.0
new: 23.0
path: .spaces[2].metrics.nexits.average
old: 0.0
new: 0.46
path: .spaces[2].metrics.cognitive.sum
old: 1.0
new: 40.0
path: .spaces[2].metrics.cognitive.average
old: 1.0
new: 0.8
path: .spaces[2].metrics.halstead.level
old: 0.03930870891223314
new: 0.008247422680412371
path: .spaces[2].metrics.halstead.time
old: 3928.5692025122416
new: 257048.3730172803
path: .spaces[2].metrics.halstead.N2
old: 227.0
new: 1940.0
path: .spaces[2].metrics.halstead.effort
old: 70714.24564522035
new: 4626870.714311046
path: .spaces[2].metrics.halstead.volume
old: 2779.6856980161165
new: 38159.75846854471
path: .spaces[2].metrics.halstead.bugs
old: 0.5700111537236041
new: 9.25558034004688
path: .spaces[2].metrics.halstead.length
old: 452.0
new: 4598.0
path: .spaces[2].metrics.halstead.estimated_program_length
old: 387.8686140532334
new: 2455.724150337665
path: .spaces[2].metrics.halstead.N1
old: 225.0
new: 2658.0
path: .spaces[2].metrics.halstead.difficulty
old: 25.439655172413794
new: 121.25
path: .spaces[2].metrics.halstead.n2
old: 58.0
new: 280.0
path: .spaces[2].metrics.halstead.purity_ratio
old: 0.8581164027726401
new: 0.5340852871547771
path: .spaces[2].metrics.halstead.n1
old: 13.0
new: 35.0
path: .spaces[2].metrics.halstead.vocabulary
old: 71.0
new: 315.0

### Code

    namespace TestTArray {

    /* static */
    int Countable::sCount = 0;
    /* static */
    int Moveable::sCount = 0;

    static nsTArray returns_by_value() {
      nsTArray result;
      return result;
    }

    TEST(TArray, test_return_by_value)
    {
      nsTArray result = returns_by_value();
      ASSERT_TRUE(true);  // This is just a compilation test.
    }

    TEST(TArray, test_move_array)
    {
      nsTArray countableArray;
      uint32_t i;
      for (i = 0; i < 4; ++i) {
        countableArray.AppendElement(Countable());
      }

      ASSERT_EQ(Countable::Count(), 8);

      const nsTArray& constRefCountableArray = countableArray;

      ASSERT_EQ(Countable::Count(), 8);

      nsTArray copyCountableArray(constRefCountableArray.Clone());

      ASSERT_EQ(Countable::Count(), 12);

      nsTArray&& moveRefCountableArray = std::move(countableArray);
      moveRefCountableArray.Length();  // Make compilers happy.

      ASSERT_EQ(Countable::Count(), 12);

      nsTArray movedCountableArray(std::move(countableArray));

      ASSERT_EQ(Countable::Count(), 12);

      // Test ctor
      FallibleTArray differentAllocatorCountableArray(
          std::move(copyCountableArray));
      // operator=
      copyCountableArray = std::move(differentAllocatorCountableArray);
      differentAllocatorCountableArray = std::move(copyCountableArray);
      // And the other ctor
      nsTArray copyCountableArray2(
          std::move(differentAllocatorCountableArray));
      // with auto
      AutoTArray autoCountableArray(std::move(copyCountableArray2));
      // operator=
      copyCountableArray2 = std::move(autoCountableArray);
      // Mix with FallibleTArray
      FallibleTArray differentAllocatorCountableArray2(
          std::move(copyCountableArray2));
      AutoTArray autoCountableArray2(
          std::move(differentAllocatorCountableArray2));
      differentAllocatorCountableArray2 = std::move(autoCountableArray2);

      ASSERT_EQ(Countable::Count(), 12);

      nsTArray moveableArray;
      for (i = 0; i < 4; ++i) {
        moveableArray.AppendElement(Moveable());
      }

      ASSERT_EQ(Moveable::Count(), 4);

      const nsTArray& constRefMoveableArray = moveableArray;

      ASSERT_EQ(Moveable::Count(), 4);

      nsTArray copyMoveableArray(constRefMoveableArray.Clone());

      ASSERT_EQ(Moveable::Count(), 8);

      nsTArray&& moveRefMoveableArray = std::move(moveableArray);
      moveRefMoveableArray.Length();  // Make compilers happy.

      ASSERT_EQ(Moveable::Count(), 8);

      nsTArray movedMoveableArray(std::move(moveableArray));

      ASSERT_EQ(Moveable::Count(), 8);

      // Test ctor
      FallibleTArray differentAllocatorMoveableArray(
          std::move(copyMoveableArray));
      // operator=
      copyMoveableArray = std::move(differentAllocatorMoveableArray);
      differentAllocatorMoveableArray = std::move(copyMoveableArray);
      // And the other ctor
      nsTArray copyMoveableArray2(
          std::move(differentAllocatorMoveableArray));
      // with auto
      AutoTArray autoMoveableArray(std::move(copyMoveableArray2));
      // operator=
      copyMoveableArray2 = std::move(autoMoveableArray);
      // Mix with FallibleTArray
      FallibleTArray differentAllocatorMoveableArray2(
          std::move(copyMoveableArray2));
      AutoTArray autoMoveableArray2(
          std::move(differentAllocatorMoveableArray2));
      differentAllocatorMoveableArray2 = std::move(autoMoveableArray2);

      ASSERT_EQ(Moveable::Count(), 8);

      AutoTArray moveableAutoArray;
      for (uint32_t i = 0; i < 4; ++i) {
        moveableAutoArray.AppendElement(Moveable());
      }

      ASSERT_EQ(Moveable::Count(), 12);

      const AutoTArray& constRefMoveableAutoArray = moveableAutoArray;

      ASSERT_EQ(Moveable::Count(), 12);

      CopyableAutoTArray copyMoveableAutoArray(
          constRefMoveableAutoArray);

      ASSERT_EQ(Moveable::Count(), 16);

      AutoTArray movedMoveableAutoArray(std::move(moveableAutoArray));

      ASSERT_EQ(Moveable::Count(), 16);
    }

    template
    class TArray_MoveOnlyTest : public ::testing::Test {};

    TYPED_TEST_CASE_P(TArray_MoveOnlyTest);

    static constexpr size_t kMoveOnlyTestArrayLength = 4;

    template
    static auto MakeMoveOnlyArray() {
      ArrayType moveOnlyArray;
      for (size_t i = 0; i < kMoveOnlyTestArrayLength; ++i) {
        EXPECT_TRUE(
            moveOnlyArray.AppendElement(typename ArrayType::elem_type(), fallible));
      }
      return moveOnlyArray;
    }

    TYPED_TEST_P(TArray_MoveOnlyTest, nsTArray_MoveConstruct) {
      auto moveOnlyArray = MakeMoveOnlyArray>();
      nsTArray 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 movedMoveOnlyArray;
      movedMoveOnlyArray = std::move(moveOnlyArray);

      ASSERT_EQ(0u, moveOnlyArray.Length());
      ASSERT_EQ(kMoveOnlyTestArrayLength, movedMoveOnlyArray.Length());
    }

    TYPED_TEST_P(TArray_MoveOnlyTest, nsTArray_MoveReAssign) {
      nsTArray movedMoveOnlyArray;
      movedMoveOnlyArray = MakeMoveOnlyArray>();
      // Re-assign, to check that move-assign does not only work on an empty array.
      movedMoveOnlyArray = MakeMoveOnlyArray>();

      ASSERT_EQ(kMoveOnlyTestArrayLength, movedMoveOnlyArray.Length());
    }

    TYPED_TEST_P(TArray_MoveOnlyTest, nsTArray_to_FallibleTArray_MoveConstruct) {
      auto moveOnlyArray = MakeMoveOnlyArray>();
      FallibleTArray 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>();
      FallibleTArray 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>();
      nsTArray 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>();
      nsTArray 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 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 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>();
      AutoTArray 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>();
      AutoTArray 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>();
      AutoTArray 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>();
      AutoTArray 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>();
      AutoTArray 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>();
      AutoTArray autoMoveOnlyArray;
      autoMoveOnlyArray = std::move(moveOnlyArray);

      ASSERT_EQ(0u, moveOnlyArray.Length());
      ASSERT_EQ(kMoveOnlyTestArrayLength, autoMoveOnlyArray.Length());
    }

    REGISTER_TYPED_TEST_CASE_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;
    INSTANTIATE_TYPED_TEST_CASE_P(InstantiationOf, TArray_MoveOnlyTest,
                                  BothMoveOnlyTypes);

    //----

    TEST(TArray, test_string_array)
    {
      nsTArray 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 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 fileArray;
      size_t i;
      for (i = 0; i < ArrayLength(kNames); ++i) {
        FilePointer f;
        tmpDir->Clone(getter_AddRefs(f));
        ASSERT_TRUE(f);
        ASSERT_FALSE(NS_FAILED(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) {}
      void AddRef() { ++rc; }
      void Release() {
        if (--rc == 0) delete this;
      }
      ~RefcountedObject() = default;

     private:
      int32_t rc;
    };

    TEST(TArray, test_refptr_array)
    {
      nsTArray> 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& r) { return i == 1 && r == b; },
          []() { return false; }));

      a->Release();
      b->Release();
      c->Release();
    }

    //----

    TEST(TArray, test_ptrarray)
    {
      nsTArray 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 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 array;

      void* hdr = array.DebugGetHeader();
      ASSERT_NE(hdr, nsTArray().DebugGetHeader());
      ASSERT_NE(hdr,
                (AutoTArray().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 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 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 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
    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 _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 a;
        AutoTArray 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 a;
        AutoTArray 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 a;
        AutoTArray 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 a;
        AutoTArray 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 a;
        AutoTArray 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 a;
        AutoTArray 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 a;
        nsTArray 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 a;
        AutoTArray 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 a;
        AutoTArray 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 a;
        nsTArray 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 a;
        AutoTArray 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 a;
        nsTArray 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 a;
        FallibleTArray 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 a;
        AutoTArray 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 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 f;
      const FallibleTArray fconst;

      nsTArray t;
      const nsTArray tconst;
      AutoTArray tauto;
      const AutoTArray tautoconst;

    #define CHECK_ARRAY_CAST(type)                  \
      do {                                          \
        const type& z1 = f;                    \
        ASSERT_EQ((void*)&z1, (void*)&f);           \
        const type& z2 = fconst;               \
        ASSERT_EQ((void*)&z2, (void*)&fconst);      \
        const type& z9 = t;                    \
        ASSERT_EQ((void*)&z9, (void*)&t);           \
        const type& z10 = tconst;              \
        ASSERT_EQ((void*)&z10, (void*)&tconst);     \
        const type& z11 = tauto;               \
        ASSERT_EQ((void*)&z11, (void*)&tauto);      \
        const type& z12 = tautoconst;          \
        ASSERT_EQ((void*)&z12, (void*)&tautoconst); \
      } while (0)

      CHECK_ARRAY_CAST(FallibleTArray);
      CHECK_ARRAY_CAST(nsTArray);

    #undef CHECK_ARRAY_CAST
    }

    template
    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 f;

      nsTArray t;
      AutoTArray 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>&>(f).GetHdr(),
          static_cast>&>(t).GetHdr(),
          static_cast>&>(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>&>(f).GetHdr(),
          static_cast>&>(t).GetHdr(),
          static_cast>&>(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
    bool TestCompareMethods(const Comparator& aComp) {
      nsTArray 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 oneArray;
      AutoTArray 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