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 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>);
static_assert(
    std::is_move_assignable_v>);
static_assert(
    !std::is_copy_constructible_v>);
static_assert(
    !std::is_copy_assignable_v>);

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

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