Global Metrics

path: .metrics.halstead.estimated_program_length
old: 1882.2032716457309
new: 1545.3602229221508

path: .metrics.halstead.n1
old: 35.0
new: 25.0

path: .metrics.halstead.vocabulary
old: 254.0
new: 214.0

path: .metrics.halstead.difficulty
old: 70.71917808219177
new: 123.47883597883596

path: .metrics.halstead.time
old: 64749.91569766869
new: 234303.50832690872

path: .metrics.halstead.N1
old: 1178.0
new: 2545.0

path: .metrics.halstead.n2
old: 219.0
new: 189.0

path: .metrics.halstead.volume
old: 16480.65650881098
new: 34155.35234400186

path: .metrics.halstead.length
old: 2063.0
new: 4412.0

path: .metrics.halstead.purity_ratio
old: 0.9123622257129088
new: 0.3502629698372962

path: .metrics.halstead.effort
old: 1165498.4825580365
new: 4217463.149884357

path: .metrics.halstead.level
old: 0.01414043583535109
new: 0.008098553829673272

path: .metrics.halstead.bugs
old: 3.6916446228570425
new: 8.70120918663034

path: .metrics.halstead.N2
old: 885.0
new: 1867.0

path: .metrics.nom.functions
old: 31.0
new: 65.0

path: .metrics.nom.total
old: 31.0
new: 66.0

path: .metrics.nom.closures
old: 0.0
new: 1.0

path: .metrics.cyclomatic.sum
old: 93.0
new: 96.0

path: .metrics.cyclomatic.average
old: 2.5135135135135136
new: 1.263157894736842

path: .metrics.cognitive.sum
old: 63.0
new: 15.0

path: .metrics.cognitive.average
old: 2.032258064516129
new: 0.2272727272727273

path: .metrics.loc.cloc
old: 78.0
new: 61.0

path: .metrics.loc.sloc
old: 542.0
new: 975.0

path: .metrics.loc.blank
old: 81.0
new: 182.0

path: .metrics.loc.ploc
old: 383.0
new: 732.0

path: .metrics.loc.lloc
old: 161.0
new: 296.0

path: .metrics.mi.mi_sei
old: -42.64265848621707
new: -71.35146705275383

path: .metrics.mi.mi_original
old: -2.8650109480446986
new: -16.856594862506952

path: .metrics.nexits.sum
old: 43.0
new: 8.0

path: .metrics.nexits.average
old: 1.3870967741935485
new: 0.12121212121212122

path: .metrics.nargs.sum
old: 47.0
new: 98.0

path: .metrics.nargs.average
old: 1.5161290322580645
new: 1.4848484848484849

Spaces Data

Minimal test - lines (59, 975)

path: .spaces[3].metrics.nargs.sum
old: 1.0
new: 96.0

path: .spaces[3].metrics.nargs.average
old: 1.0
new: 1.6271186440677967

path: .spaces[3].metrics.cognitive.average
old: 0.0
new: 0.22033898305084745

path: .spaces[3].metrics.cognitive.sum
old: 0.0
new: 13.0

path: .spaces[3].metrics.nom.functions
old: 1.0
new: 58.0

path: .spaces[3].metrics.nom.total
old: 1.0
new: 59.0

path: .spaces[3].metrics.nom.closures
old: 0.0
new: 1.0

path: .spaces[3].metrics.loc.lloc
old: 4.0
new: 291.0

path: .spaces[3].metrics.loc.blank
old: 2.0
new: 169.0

path: .spaces[3].metrics.loc.cloc
old: 0.0
new: 54.0

path: .spaces[3].metrics.loc.ploc
old: 8.0
new: 694.0

path: .spaces[3].metrics.loc.sloc
old: 10.0
new: 917.0

path: .spaces[3].metrics.nexits.sum
old: 0.0
new: 8.0

path: .spaces[3].metrics.nexits.average
old: 0.0
new: 0.13559322033898305

path: .spaces[3].metrics.mi.mi_visual_studio
old: 61.90764290798603
new: 0.0

path: .spaces[3].metrics.mi.mi_original
old: 105.86206937265612
new: -11.982158173849584

path: .spaces[3].metrics.mi.mi_sei
old: 77.1276503695661
new: -66.48453954963408

path: .spaces[3].metrics.cyclomatic.average
old: 1.0
new: 1.2698412698412698

path: .spaces[3].metrics.cyclomatic.sum
old: 1.0
new: 80.0

path: .spaces[3].metrics.halstead.purity_ratio
old: 2.2794463561450367
new: 0.34054930127402505

path: .spaces[3].metrics.halstead.time
old: 62.41907449237361
new: 221750.8810432135

path: .spaces[3].metrics.halstead.difficulty
old: 5.5588235294117645
new: 121.46666666666668

path: .spaces[3].metrics.halstead.vocabulary
old: 26.0
new: 204.0

path: .spaces[3].metrics.halstead.effort
old: 1123.543340862725
new: 3991515.8587778434

path: .spaces[3].metrics.halstead.N1
old: 22.0
new: 2461.0

path: .spaces[3].metrics.halstead.level
old: 0.1798941798941799
new: 0.00823271130625686

path: .spaces[3].metrics.halstead.bugs
old: 0.03602510859626821
new: 8.387592409932365

path: .spaces[3].metrics.halstead.n2
old: 17.0
new: 180.0

path: .spaces[3].metrics.halstead.N2
old: 21.0
new: 1822.0

path: .spaces[3].metrics.halstead.length
old: 43.0
new: 4283.0

path: .spaces[3].metrics.halstead.n1
old: 9.0
new: 24.0

path: .spaces[3].metrics.halstead.volume
old: 202.11890788006696
new: 32860.997739663915

path: .spaces[3].metrics.halstead.estimated_program_length
old: 98.01619331423656
new: 1458.5726573566492

Code

namespace TestTArray {

constexpr int dummyArrayData[] = {4, 1, 2, 8};

static const nsTArray& DummyArray() {
  static nsTArray sArray;
  if (sArray.IsEmpty()) {
    sArray.AppendElements(dummyArrayData, ArrayLength(dummyArrayData));
  }
  return sArray;
}

// This returns an invalid nsTArray with a huge length in order to test that
// fallible operations actually fail.
#ifdef DEBUG
static const nsTArray& FakeHugeArray() {
  static nsTArray sArray;
  if (sArray.IsEmpty()) {
    sArray.AppendElement();
    ((nsTArrayHeader*)sArray.DebugGetHeader())->mLength = UINT32_MAX;
  }
  return sArray;
}
#endif

TEST(TArray, int_AppendElements_PlainArray)
{
  nsTArray array;

  int* ptr = array.AppendElements(dummyArrayData, ArrayLength(dummyArrayData));
  ASSERT_EQ(&array[0], ptr);
  ASSERT_EQ(DummyArray(), array);

  ptr = array.AppendElements(dummyArrayData, ArrayLength(dummyArrayData));
  ASSERT_EQ(&array[DummyArray().Length()], ptr);
  nsTArray expected;
  expected.AppendElements(DummyArray());
  expected.AppendElements(DummyArray());
  ASSERT_EQ(expected, array);
}

TEST(TArray, int_AppendElements_PlainArray_Fallible)
{
  nsTArray array;

  int* ptr = array.AppendElements(dummyArrayData, ArrayLength(dummyArrayData),
                                  fallible);
  ASSERT_EQ(&array[0], ptr);
  ASSERT_EQ(DummyArray(), array);

  ptr = array.AppendElements(dummyArrayData, ArrayLength(dummyArrayData),
                             fallible);
  ASSERT_EQ(&array[DummyArray().Length()], ptr);
  nsTArray expected;
  expected.AppendElements(DummyArray());
  expected.AppendElements(DummyArray());
  ASSERT_EQ(expected, array);
}

TEST(TArray, int_AppendElements_TArray_Copy)
{
  nsTArray array;

  const nsTArray temp(DummyArray().Clone());
  int* ptr = array.AppendElements(temp);
  ASSERT_EQ(&array[0], ptr);
  ASSERT_EQ(DummyArray(), array);
  ASSERT_FALSE(temp.IsEmpty());

  ptr = array.AppendElements(temp);
  ASSERT_EQ(&array[DummyArray().Length()], ptr);
  nsTArray expected;
  expected.AppendElements(DummyArray());
  expected.AppendElements(DummyArray());
  ASSERT_EQ(expected, array);
  ASSERT_FALSE(temp.IsEmpty());
}

TEST(TArray, int_AppendElements_TArray_Copy_Fallible)
{
  nsTArray array;

  const nsTArray temp(DummyArray().Clone());
  int* ptr = array.AppendElements(temp, fallible);
  ASSERT_EQ(&array[0], ptr);
  ASSERT_EQ(DummyArray(), array);
  ASSERT_FALSE(temp.IsEmpty());

  ptr = array.AppendElements(temp, fallible);
  ASSERT_EQ(&array[DummyArray().Length()], ptr);
  nsTArray expected;
  expected.AppendElements(DummyArray());
  expected.AppendElements(DummyArray());
  ASSERT_EQ(expected, array);
  ASSERT_FALSE(temp.IsEmpty());
}

TEST(TArray, int_AppendElements_TArray_Rvalue)
{
  nsTArray array;

  nsTArray temp(DummyArray().Clone());
  int* ptr = array.AppendElements(std::move(temp));
  ASSERT_EQ(&array[0], ptr);
  ASSERT_EQ(DummyArray(), array);
  ASSERT_TRUE(temp.IsEmpty());

  temp = DummyArray().Clone();
  ptr = array.AppendElements(std::move(temp));
  ASSERT_EQ(&array[DummyArray().Length()], ptr);
  nsTArray expected;
  expected.AppendElements(DummyArray());
  expected.AppendElements(DummyArray());
  ASSERT_EQ(expected, array);
  ASSERT_TRUE(temp.IsEmpty());
}

TEST(TArray, int_AppendElements_TArray_Rvalue_Fallible)
{
  nsTArray array;

  nsTArray temp(DummyArray().Clone());
  int* ptr = array.AppendElements(std::move(temp), fallible);
  ASSERT_EQ(&array[0], ptr);
  ASSERT_EQ(DummyArray(), array);
  ASSERT_TRUE(temp.IsEmpty());

  temp = DummyArray().Clone();
  ptr = array.AppendElements(std::move(temp), fallible);
  ASSERT_EQ(&array[DummyArray().Length()], ptr);
  nsTArray expected;
  expected.AppendElements(DummyArray());
  expected.AppendElements(DummyArray());
  ASSERT_EQ(expected, array);
  ASSERT_TRUE(temp.IsEmpty());
}

TEST(TArray, int_AppendElements_FallibleArray_Rvalue)
{
  nsTArray array;

  FallibleTArray temp;
  ASSERT_TRUE(temp.AppendElements(DummyArray(), fallible));
  int* ptr = array.AppendElements(std::move(temp));
  ASSERT_EQ(&array[0], ptr);
  ASSERT_EQ(DummyArray(), array);
  ASSERT_TRUE(temp.IsEmpty());

  ASSERT_TRUE(temp.AppendElements(DummyArray(), fallible));
  ptr = array.AppendElements(std::move(temp));
  ASSERT_EQ(&array[DummyArray().Length()], ptr);
  nsTArray expected;
  expected.AppendElements(DummyArray());
  expected.AppendElements(DummyArray());
  ASSERT_EQ(expected, array);
  ASSERT_TRUE(temp.IsEmpty());
}

TEST(TArray, int_AppendElements_FallibleArray_Rvalue_Fallible)
{
  nsTArray array;

  FallibleTArray temp;
  ASSERT_TRUE(temp.AppendElements(DummyArray(), fallible));
  int* ptr = array.AppendElements(std::move(temp), fallible);
  ASSERT_EQ(&array[0], ptr);
  ASSERT_EQ(DummyArray(), array);
  ASSERT_TRUE(temp.IsEmpty());

  ASSERT_TRUE(temp.AppendElements(DummyArray(), fallible));
  ptr = array.AppendElements(std::move(temp), fallible);
  ASSERT_EQ(&array[DummyArray().Length()], ptr);
  nsTArray expected;
  expected.AppendElements(DummyArray());
  expected.AppendElements(DummyArray());
  ASSERT_EQ(expected, array);
  ASSERT_TRUE(temp.IsEmpty());
}

TEST(TArray, AppendElementsSpan)
{
  nsTArray array;

  nsTArray temp(DummyArray().Clone());
  Span span = temp;
  array.AppendElements(span);
  ASSERT_EQ(DummyArray(), array);

  Span constSpan = temp;
  array.AppendElements(constSpan);
  nsTArray expected;
  expected.AppendElements(DummyArray());
  expected.AppendElements(DummyArray());
  ASSERT_EQ(expected, array);
}

TEST(TArray, int_AppendElement_NoElementArg)
{
  nsTArray array;
  array.AppendElement();

  ASSERT_EQ(1u, array.Length());
}

TEST(TArray, int_AppendElement_NoElementArg_Fallible)
{
  nsTArray array;
  ASSERT_NE(nullptr, array.AppendElement(fallible));

  ASSERT_EQ(1u, array.Length());
}

TEST(TArray, int_AppendElement_NoElementArg_Address)
{
  nsTArray array;
  *array.AppendElement() = 42;

  ASSERT_EQ(1u, array.Length());
  ASSERT_EQ(42, array[0]);
}

TEST(TArray, int_AppendElement_NoElementArg_Fallible_Address)
{
  nsTArray array;
  *array.AppendElement(fallible) = 42;

  ASSERT_EQ(1u, array.Length());
  ASSERT_EQ(42, array[0]);
}

TEST(TArray, int_AppendElement_ElementArg)
{
  nsTArray array;
  array.AppendElement(42);

  ASSERT_EQ(1u, array.Length());
  ASSERT_EQ(42, array[0]);
}

TEST(TArray, int_AppendElement_ElementArg_Fallible)
{
  nsTArray array;
  ASSERT_NE(nullptr, array.AppendElement(42, fallible));

  ASSERT_EQ(1u, array.Length());
  ASSERT_EQ(42, array[0]);
}

constexpr size_t dummyMovableArrayLength = 4;
uint32_t dummyMovableArrayDestructorCounter;

static nsTArray DummyMovableArray() {
  nsTArray res;
  res.SetLength(dummyMovableArrayLength);
  for (size_t i = 0; i < dummyMovableArrayLength; ++i) {
    res[i].mDestructionCounter = &dummyMovableArrayDestructorCounter;
  }
  return res;
}

TEST(TArray, Movable_AppendElements_TArray_Rvalue)
{
  dummyMovableArrayDestructorCounter = 0;
  {
    nsTArray array;

    nsTArray temp(DummyMovableArray());
    Movable* ptr = array.AppendElements(std::move(temp));
    ASSERT_EQ(&array[0], ptr);
    ASSERT_TRUE(temp.IsEmpty());

    temp = DummyMovableArray();
    ptr = array.AppendElements(std::move(temp));
    ASSERT_EQ(&array[dummyMovableArrayLength], ptr);
    ASSERT_TRUE(temp.IsEmpty());
  }
  ASSERT_EQ(2 * dummyMovableArrayLength, dummyMovableArrayDestructorCounter);
}

TEST(TArray, Movable_AppendElements_TArray_Rvalue_Fallible)
{
  dummyMovableArrayDestructorCounter = 0;
  {
    nsTArray array;

    nsTArray temp(DummyMovableArray());
    Movable* ptr = array.AppendElements(std::move(temp), fallible);
    ASSERT_EQ(&array[0], ptr);
    ASSERT_TRUE(temp.IsEmpty());

    temp = DummyMovableArray();
    ptr = array.AppendElements(std::move(temp), fallible);
    ASSERT_EQ(&array[dummyMovableArrayLength], ptr);
    ASSERT_TRUE(temp.IsEmpty());
  }
  ASSERT_EQ(2 * dummyMovableArrayLength, dummyMovableArrayDestructorCounter);
}

TEST(TArray, Movable_AppendElements_FallibleArray_Rvalue)
{
  dummyMovableArrayDestructorCounter = 0;
  {
    nsTArray array;

    FallibleTArray temp(DummyMovableArray());
    Movable* ptr = array.AppendElements(std::move(temp));
    ASSERT_EQ(&array[0], ptr);
    ASSERT_TRUE(temp.IsEmpty());

    temp = DummyMovableArray();
    ptr = array.AppendElements(std::move(temp));
    ASSERT_EQ(&array[dummyMovableArrayLength], ptr);
    ASSERT_TRUE(temp.IsEmpty());
  }
  ASSERT_EQ(2 * dummyMovableArrayLength, dummyMovableArrayDestructorCounter);
}

TEST(TArray, Movable_AppendElements_FallibleArray_Rvalue_Fallible)
{
  dummyMovableArrayDestructorCounter = 0;
  {
    nsTArray array;

    FallibleTArray temp(DummyMovableArray());
    Movable* ptr = array.AppendElements(std::move(temp), fallible);
    ASSERT_EQ(&array[0], ptr);
    ASSERT_TRUE(temp.IsEmpty());

    temp = DummyMovableArray();
    ptr = array.AppendElements(std::move(temp), fallible);
    ASSERT_EQ(&array[dummyMovableArrayLength], ptr);
    ASSERT_TRUE(temp.IsEmpty());
  }
  ASSERT_EQ(2 * dummyMovableArrayLength, dummyMovableArrayDestructorCounter);
}

TEST(TArray, Movable_AppendElement_NoElementArg)
{
  nsTArray array;
  array.AppendElement();

  ASSERT_EQ(1u, array.Length());
}

TEST(TArray, Movable_AppendElement_NoElementArg_Fallible)
{
  nsTArray array;
  ASSERT_NE(nullptr, array.AppendElement(fallible));

  ASSERT_EQ(1u, array.Length());
}

TEST(TArray, Movable_AppendElement_NoElementArg_Address)
{
  dummyMovableArrayDestructorCounter = 0;
  {
    nsTArray array;
    array.AppendElement()->mDestructionCounter =
        &dummyMovableArrayDestructorCounter;

    ASSERT_EQ(1u, array.Length());
  }
  ASSERT_EQ(1u, dummyMovableArrayDestructorCounter);
}

TEST(TArray, Movable_AppendElement_NoElementArg_Fallible_Address)
{
  dummyMovableArrayDestructorCounter = 0;
  {
    nsTArray array;
    array.AppendElement(fallible)->mDestructionCounter =
        &dummyMovableArrayDestructorCounter;

    ASSERT_EQ(1u, array.Length());
    ASSERT_EQ(&dummyMovableArrayDestructorCounter,
              array[0].mDestructionCounter);
  }
  ASSERT_EQ(1u, dummyMovableArrayDestructorCounter);
}

TEST(TArray, Movable_AppendElement_ElementArg)
{
  dummyMovableArrayDestructorCounter = 0;
  Movable movable;
  movable.mDestructionCounter = &dummyMovableArrayDestructorCounter;
  {
    nsTArray array;
    array.AppendElement(std::move(movable));

    ASSERT_EQ(1u, array.Length());
    ASSERT_EQ(&dummyMovableArrayDestructorCounter,
              array[0].mDestructionCounter);
  }
  ASSERT_EQ(1u, dummyMovableArrayDestructorCounter);
}

TEST(TArray, Movable_AppendElement_ElementArg_Fallible)
{
  dummyMovableArrayDestructorCounter = 0;
  Movable movable;
  movable.mDestructionCounter = &dummyMovableArrayDestructorCounter;
  {
    nsTArray array;
    ASSERT_NE(nullptr, array.AppendElement(std::move(movable), fallible));

    ASSERT_EQ(1u, array.Length());
    ASSERT_EQ(&dummyMovableArrayDestructorCounter,
              array[0].mDestructionCounter);
  }
  ASSERT_EQ(1u, dummyMovableArrayDestructorCounter);
}

TEST(TArray, int_Assign)
{
  nsTArray array;
  array.Assign(DummyArray());
  ASSERT_EQ(DummyArray(), array);

  ASSERT_TRUE(array.Assign(DummyArray(), fallible));
  ASSERT_EQ(DummyArray(), array);

#ifdef DEBUG
  ASSERT_FALSE(array.Assign(FakeHugeArray(), fallible));
#endif

  nsTArray array2;
  array2.Assign(std::move(array));
  ASSERT_TRUE(array.IsEmpty());
  ASSERT_EQ(DummyArray(), array2);
}

TEST(TArray, int_Assign_FromEmpty_ToNonEmpty)
{
  nsTArray array;
  array.AppendElement(42);

  const nsTArray empty;
  array.Assign(empty);

  ASSERT_TRUE(array.IsEmpty());
}

TEST(TArray, int_Assign_FromEmpty_ToNonEmpty_Fallible)
{
  nsTArray array;
  array.AppendElement(42);

  const nsTArray empty;
  ASSERT_TRUE(array.Assign(empty, fallible));

  ASSERT_TRUE(array.IsEmpty());
}

TEST(TArray, int_AssignmentOperatorSelfAssignment)
{
  CopyableTArray array;
  array = DummyArray();

  array = *&array;
  ASSERT_EQ(DummyArray(), array);

#if defined(__clang__)
#  pragma clang diagnostic push
#  pragma clang diagnostic ignored "-Wself-move"
#endif
  array = std::move(array);  // self-move
  ASSERT_EQ(DummyArray(), array);
#if defined(__clang__)
#  pragma clang diagnostic pop
#endif
}

TEST(TArray, Movable_CopyOverlappingForwards)
{
  const size_t rangeLength = 8;
  const size_t initialLength = 2 * rangeLength;
  uint32_t destructionCounters[initialLength];
  nsTArray array;
  array.AppendElements(initialLength);

  for (uint32_t i = 0; i < initialLength; ++i) {
    destructionCounters[i] = 0;
  }
  for (uint32_t i = 0; i < initialLength; ++i) {
    array[i].mDestructionCounter = &destructionCounters[i];
  }

  const size_t removedLength = rangeLength / 2;
  array.RemoveElementsAt(0, removedLength);

  for (uint32_t i = 0; i < removedLength; ++i) {
    ASSERT_EQ(destructionCounters[i], 1u);
  }
  for (uint32_t i = removedLength; i < initialLength; ++i) {
    ASSERT_EQ(destructionCounters[i], 0u);
  }
}

// The code to copy overlapping regions had a bug in that it wouldn't correctly
// destroy all over the source elements being copied.
TEST(TArray, Copyable_CopyOverlappingBackwards)
{
  const size_t rangeLength = 8;
  const size_t initialLength = 2 * rangeLength;
  uint32_t destructionCounters[initialLength];
  nsTArray array;
  array.SetCapacity(3 * rangeLength);
  array.AppendElements(initialLength);
  // To tickle the bug, we need to copy a source region:
  //
  //   ..XXXXX..
  //
  // such that it overlaps the destination region:
  //
  //   ....XXXXX
  //
  // so we are forced to copy back-to-front to ensure correct behavior.
  // The easiest way to do that is to call InsertElementsAt, which will force
  // the desired kind of shift.
  for (uint32_t i = 0; i < initialLength; ++i) {
    destructionCounters[i] = 0;
  }
  for (uint32_t i = 0; i < initialLength; ++i) {
    array[i].mDestructionCounter = &destructionCounters[i];
  }

  array.InsertElementsAt(0, rangeLength);

  for (uint32_t i = 0; i < initialLength; ++i) {
    ASSERT_EQ(destructionCounters[i], 1u);
  }
}

namespace {

class E {
 public:
  E() : mA(-1), mB(-2) { constructCount++; }
  E(int a, int b) : mA(a), mB(b) { constructCount++; }
  E(E&& aRhs) : mA(aRhs.mA), mB(aRhs.mB) {
    aRhs.mA = 0;
    aRhs.mB = 0;
    moveCount++;
  }

  E& operator=(E&& aRhs) {
    mA = aRhs.mA;
    aRhs.mA = 0;
    mB = aRhs.mB;
    aRhs.mB = 0;
    moveCount++;
    return *this;
  }

  int a() const { return mA; }
  int b() const { return mB; }

  E(const E&) = delete;
  E& operator=(const E&) = delete;

  static size_t constructCount;
  static size_t moveCount;

 private:
  int mA;
  int mB;
};

size_t E::constructCount = 0;
size_t E::moveCount = 0;

}  // namespace

TEST(TArray, Emplace)
{
  nsTArray array;
  array.SetCapacity(20);

  ASSERT_EQ(array.Length(), 0u);

  for (int i = 0; i < 10; i++) {
    E s(i, i * i);
    array.AppendElement(std::move(s));
  }

  ASSERT_EQ(array.Length(), 10u);
  ASSERT_EQ(E::constructCount, 10u);
  ASSERT_EQ(E::moveCount, 10u);

  for (int i = 10; i < 20; i++) {
    array.EmplaceBack(i, i * i);
  }

  ASSERT_EQ(array.Length(), 20u);
  ASSERT_EQ(E::constructCount, 20u);
  ASSERT_EQ(E::moveCount, 10u);

  for (int i = 0; i < 20; i++) {
    ASSERT_EQ(array[i].a(), i);
    ASSERT_EQ(array[i].b(), i * i);
  }

  array.EmplaceBack();

  ASSERT_EQ(array.Length(), 21u);
  ASSERT_EQ(E::constructCount, 21u);
  ASSERT_EQ(E::moveCount, 10u);

  ASSERT_EQ(array[20].a(), -1);
  ASSERT_EQ(array[20].b(), -2);
}

TEST(TArray, UnorderedRemoveElements)
{
  // When removing an element from the end of the array, it can be removed in
  // place, by destroying it and decrementing the length.
  //
  // [ 1, 2, 3 ] => [ 1, 2 ]
  //         ^
  {
    nsTArray array{1, 2, 3};
    array.UnorderedRemoveElementAt(2);

    nsTArray goal{1, 2};
    ASSERT_EQ(array, goal);
  }

  // When removing any other single element, it is removed by swapping it with
  // the last element, and then decrementing the length as before.
  //
  // [ 1, 2, 3, 4, 5, 6 ]  => [ 1, 6, 3, 4, 5 ]
  //      ^
  {
    nsTArray array{1, 2, 3, 4, 5, 6};
    array.UnorderedRemoveElementAt(1);

    nsTArray goal{1, 6, 3, 4, 5};
    ASSERT_EQ(array, goal);
  }

  // This method also supports efficiently removing a range of elements. If they
  // are at the end, then they can all be removed like in the one element case.
  //
  // [ 1, 2, 3, 4, 5, 6 ] => [ 1, 2 ]
  //         ^--------^
  {
    nsTArray array{1, 2, 3, 4, 5, 6};
    array.UnorderedRemoveElementsAt(2, 4);

    nsTArray goal{1, 2};
    ASSERT_EQ(array, goal);
  }

  // If more elements are removed than exist after the removed section, the
  // remaining elements will be shifted down like in a normal removal.
  //
  // [ 1, 2, 3, 4, 5, 6, 7, 8 ] => [ 1, 2, 7, 8 ]
  //         ^--------^
  {
    nsTArray array{1, 2, 3, 4, 5, 6, 7, 8};
    array.UnorderedRemoveElementsAt(2, 4);

    nsTArray goal{1, 2, 7, 8};
    ASSERT_EQ(array, goal);
  }

  // And if fewer elements are removed than exist after the removed section,
  // elements will be moved from the end of the array to fill the vacated space.
  //
  // [ 1, 2, 3, 4, 5, 6, 7, 8 ] => [ 1, 7, 8, 4, 5, 6 ]
  //      ^--^
  {
    nsTArray array{1, 2, 3, 4, 5, 6, 7, 8};
    array.UnorderedRemoveElementsAt(1, 2);

    nsTArray goal{1, 7, 8, 4, 5, 6};
    ASSERT_EQ(array, goal);
  }

  // We should do the right thing if we drain the entire array.
  {
    nsTArray array{1, 2, 3, 4, 5};
    array.UnorderedRemoveElementsAt(0, 5);

    nsTArray goal{};
    ASSERT_EQ(array, goal);
  }

  {
    nsTArray array{1};
    array.UnorderedRemoveElementAt(0);

    nsTArray goal{};
    ASSERT_EQ(array, goal);
  }

  // We should do the right thing if we remove the same number of elements that
  // we have remaining.
  {
    nsTArray array{1, 2, 3, 4, 5, 6};
    array.UnorderedRemoveElementsAt(2, 2);

    nsTArray goal{1, 2, 5, 6};
    ASSERT_EQ(array, goal);
  }

  {
    nsTArray array{1, 2, 3};
    array.UnorderedRemoveElementAt(1);

    nsTArray goal{1, 3};
    ASSERT_EQ(array, goal);
  }

  // We should be able to remove elements from the front without issue.
  {
    nsTArray array{1, 2, 3, 4, 5, 6};
    array.UnorderedRemoveElementsAt(0, 2);

    nsTArray goal{5, 6, 3, 4};
    ASSERT_EQ(array, goal);
  }

  {
    nsTArray array{1, 2, 3, 4};
    array.UnorderedRemoveElementAt(0);

    nsTArray goal{4, 2, 3};
    ASSERT_EQ(array, goal);
  }
}

TEST(TArray, RemoveFromEnd)
{
  {
    nsTArray array{1, 2, 3, 4};
    ASSERT_EQ(array.PopLastElement(), 4);
    array.RemoveLastElement();
    ASSERT_EQ(array.PopLastElement(), 2);
    array.RemoveLastElement();
    ASSERT_TRUE(array.IsEmpty());
  }
}

TEST(TArray, ConvertIteratorToConstIterator)
{
  nsTArray array{1, 2, 3, 4};

  nsTArray::const_iterator it = array.begin();
  ASSERT_EQ(array.cbegin(), it);
}

TEST(TArray, RemoveElementAt_ByIterator)
{
  nsTArray array{1, 2, 3, 4};
  const auto it = std::find(array.begin(), array.end(), 3);
  const auto itAfter = array.RemoveElementAt(it);

  // Based on the implementation of the iterator, we could compare it and
  // itAfter, but we should not rely on such implementation details.

  ASSERT_EQ(2, std::distance(array.cbegin(), itAfter));
  const nsTArray expected{1, 2, 4};
  ASSERT_EQ(expected, array);
}

TEST(TArray, RemoveElementsRange_ByIterator)
{
  nsTArray array{1, 2, 3, 4};
  const auto it = std::find(array.begin(), array.end(), 3);
  const auto itAfter = array.RemoveElementsRange(it, array.end());

  // Based on the implementation of the iterator, we could compare it and
  // itAfter, but we should not rely on such implementation details.

  ASSERT_EQ(2, std::distance(array.cbegin(), itAfter));
  const nsTArray expected{1, 2};
  ASSERT_EQ(expected, array);
}

TEST(TArray, RemoveLastElements_None)
{
  const nsTArray original{1, 2, 3, 4};
  nsTArray array = original.Clone();
  array.RemoveLastElements(0);

  ASSERT_EQ(original, array);
}

TEST(TArray, RemoveLastElements_Empty_None)
{
  nsTArray array;
  array.RemoveLastElements(0);

  ASSERT_EQ(0u, array.Length());
}

TEST(TArray, RemoveLastElements_All)
{
  nsTArray array{1, 2, 3, 4};
  array.RemoveLastElements(4);

  ASSERT_EQ(0u, array.Length());
}

TEST(TArray, RemoveLastElements_One)
{
  nsTArray array{1, 2, 3, 4};
  array.RemoveLastElements(1);

  ASSERT_EQ((nsTArray{1, 2, 3}), array);
}

static_assert(std::is_copy_assignable&>()))>::value,
              "output iteraror must be copy-assignable");
static_assert(std::is_copy_constructible&>()))>::value,
              "output iterator must be copy-constructible");

TEST(TArray, MakeBackInserter)
{
  const std::vector src{1, 2, 3, 4};
  nsTArray dst;

  std::copy(src.begin(), src.end(), MakeBackInserter(dst));

  const nsTArray expected{1, 2, 3, 4};
  ASSERT_EQ(expected, dst);
}

TEST(TArray, MakeBackInserter_Move)
{
  uint32_t destructionCounter = 0;

  {
    std::vector src(1);
    src[0].mDestructionCounter = &destructionCounter;

    nsTArray dst;

    std::copy(std::make_move_iterator(src.begin()),
              std::make_move_iterator(src.end()), MakeBackInserter(dst));

    ASSERT_EQ(1u, dst.Length());
    ASSERT_EQ(0u, destructionCounter);
  }

  ASSERT_EQ(1u, destructionCounter);
}

TEST(TArray, ConvertToSpan)
{
  nsTArray arr = {1, 2, 3, 4, 5};

  // from const
  {
    const auto& constArrRef = arr;

    auto span = Span{constArrRef};
    static_assert(std::is_same_v>);
  }

  // from non-const
  {
    auto span = Span{arr};
    static_assert(std::is_same_v>);
  }
}

// This should compile:
struct RefCounted;

class Foo {
  ~Foo();  // Intentionally out of line

  nsTArray> mArray;

  const RefCounted* GetFirst() const { return mArray.SafeElementAt(0); }
};

TEST(TArray, ArrayView)
{
  const nsTArray expected = {1, 2, 3, 4, 5};
  const nsTArrayView view(expected.Clone());

  nsTArray fromSpan;
  fromSpan.AppendElements(view.AsSpan());
  EXPECT_EQ(expected, fromSpan);

  for (auto& element : view) {
    element++;
  }

  int i = 2;
  for (const auto& element : view) {
    EXPECT_EQ(i++, element);
  }
}

TEST(TArray, StableSort)
{
  const nsTArray> expected = {
      std::pair(1, 9), std::pair(1, 8), std::pair(1, 7), std::pair(2, 0),
      std::pair(3, 0)};
  nsTArray> array = {std::pair(1, 9), std::pair(2, 0),
                                         std::pair(1, 8), std::pair(3, 0),
                                         std::pair(1, 7)};

  array.StableSort([](std::pair left, std::pair right) {
    return left.first - right.first;
  });

  EXPECT_EQ(expected, array);
}

}  // namespace TestTArray

Minimal test - lines (50, 52)

path: .spaces[1].metrics.loc.blank
old: 1.0
new: 0.0

path: .spaces[1].metrics.loc.lloc
old: 2.0
new: 0.0

path: .spaces[1].metrics.loc.sloc
old: 13.0
new: 3.0

path: .spaces[1].metrics.loc.ploc
old: 12.0
new: 3.0

path: .spaces[1].metrics.mi.mi_visual_studio
old: 57.67475002336739
new: 77.78160637851651

path: .spaces[1].metrics.mi.mi_original
old: 98.62382253995824
new: 133.00654690726324

path: .spaces[1].metrics.mi.mi_sei
old: 66.68506755930264
new: 116.28885349626572

path: .spaces[1].metrics.cognitive.average
old: 0.0
new: null

path: .spaces[1].metrics.halstead.difficulty
old: 6.8
new: 3.75

path: .spaces[1].metrics.halstead.estimated_program_length
old: 149.31568569324173
new: 23.509775004326936

path: .spaces[1].metrics.halstead.N1
old: 36.0
new: 9.0

path: .spaces[1].metrics.halstead.n2
old: 25.0
new: 4.0

path: .spaces[1].metrics.halstead.volume
old: 359.0498111861476
new: 46.50699332842307

path: .spaces[1].metrics.halstead.N2
old: 34.0
new: 5.0

path: .spaces[1].metrics.halstead.bugs
old: 0.06043954072860638
new: 0.010405072229378904

path: .spaces[1].metrics.halstead.vocabulary
old: 35.0
new: 10.0

path: .spaces[1].metrics.halstead.length
old: 70.0
new: 14.0

path: .spaces[1].metrics.halstead.effort
old: 2441.538716065804
new: 174.40122498158652

path: .spaces[1].metrics.halstead.level
old: 0.14705882352941177
new: 0.26666666666666666

path: .spaces[1].metrics.halstead.time
old: 135.64103978143353
new: 9.688956943421474

path: .spaces[1].metrics.halstead.purity_ratio
old: 2.1330812241891675
new: 1.6792696431662095

path: .spaces[1].metrics.halstead.n1
old: 10.0
new: 6.0

path: .spaces[1].metrics.nargs.average
old: 4.0
new: null

path: .spaces[1].metrics.nargs.sum
old: 4.0
new: 0.0

path: .spaces[1].metrics.nom.total
old: 1.0
new: 0.0

path: .spaces[1].metrics.nom.functions
old: 1.0
new: 0.0

path: .spaces[1].metrics.nexits.average
old: 0.0
new: null

Code

struct nsTArray_RelocationStrategy {
  using Type = nsTArray_RelocateUsingMoveConstructor;
};

Minimal test - lines (14, 47)

path: .spaces[0].metrics.cognitive.sum
old: 0.0
new: 2.0

path: .spaces[0].metrics.cognitive.average
old: 0.0
new: 0.2857142857142857

path: .spaces[0].metrics.nargs.average
old: 3.0
new: 0.2857142857142857

path: .spaces[0].metrics.nargs.sum
old: 3.0
new: 2.0

path: .spaces[0].metrics.nom.total
old: 1.0
new: 7.0

path: .spaces[0].metrics.nom.functions
old: 1.0
new: 7.0

path: .spaces[0].metrics.halstead.n1
old: 9.0
new: 14.0

path: .spaces[0].metrics.halstead.level
old: 0.16296296296296295
new: 0.02857142857142857

path: .spaces[0].metrics.halstead.N1
old: 31.0
new: 59.0

path: .spaces[0].metrics.halstead.purity_ratio
old: 2.0760134528360874
new: 0.7731768979003751

path: .spaces[0].metrics.halstead.estimated_program_length
old: 126.63682062300134
new: 68.81274391313339

path: .spaces[0].metrics.halstead.bugs
old: 0.05031392152958735
new: 0.1886408757924609

path: .spaces[0].metrics.halstead.n2
old: 22.0
new: 6.0

path: .spaces[0].metrics.halstead.vocabulary
old: 31.0
new: 20.0

path: .spaces[0].metrics.halstead.difficulty
old: 6.136363636363637
new: 35.0

path: .spaces[0].metrics.halstead.length
old: 61.0
new: 89.0

path: .spaces[0].metrics.halstead.volume
old: 302.20597493359935
new: 384.65160044497526

path: .spaces[0].metrics.halstead.time
old: 103.02476418190888
new: 747.9336675318963

path: .spaces[0].metrics.halstead.effort
old: 1854.4457552743595
new: 13462.806015574131

path: .spaces[0].metrics.cyclomatic.average
old: 1.0
new: 1.3

path: .spaces[0].metrics.cyclomatic.sum
old: 1.0
new: 13.0

path: .spaces[0].metrics.loc.blank
old: 0.0
new: 9.0

path: .spaces[0].metrics.loc.lloc
old: 1.0
new: 5.0

path: .spaces[0].metrics.loc.ploc
old: 10.0
new: 25.0

path: .spaces[0].metrics.loc.sloc
old: 10.0
new: 34.0

path: .spaces[0].metrics.mi.mi_sei
old: 74.1099453171143
new: 40.938577767339

path: .spaces[0].metrics.mi.mi_original
old: 103.77035562378764
new: 79.93080194968866

path: .spaces[0].metrics.mi.mi_visual_studio
old: 60.68441849344306
new: 46.743159034905645

Code

namespace TestTArray {

struct Copyable {
  Copyable() : mDestructionCounter(nullptr) {}

  ~Copyable() {
    if (mDestructionCounter) {
      (*mDestructionCounter)++;
    }
  }

  Copyable(const Copyable&) = default;
  Copyable& operator=(const Copyable&) = default;

  uint32_t* mDestructionCounter;
};

struct Movable {
  Movable() : mDestructionCounter(nullptr) {}

  ~Movable() {
    if (mDestructionCounter) {
      (*mDestructionCounter)++;
    }
  }

  Movable(Movable&& aOther) : mDestructionCounter(aOther.mDestructionCounter) {
    aOther.mDestructionCounter = nullptr;
  }

  uint32_t* mDestructionCounter;
};

}  // namespace TestTArray

Minimal test - lines (55, 57)

path: .spaces[2].metrics.nexits.average
old: 0.0
new: null

path: .spaces[2].metrics.nom.total
old: 1.0
new: 0.0

path: .spaces[2].metrics.nom.functions
old: 1.0
new: 0.0

path: .spaces[2].metrics.cognitive.average
old: 0.0
new: null

path: .spaces[2].metrics.nargs.sum
old: 4.0
new: 0.0

path: .spaces[2].metrics.nargs.average
old: 4.0
new: null

path: .spaces[2].metrics.halstead.estimated_program_length
old: 155.430713620542
new: 23.509775004326936

path: .spaces[2].metrics.halstead.level
old: 0.14444444444444443
new: 0.26666666666666666

path: .spaces[2].metrics.halstead.time
old: 147.14401927181967
new: 9.688956943421474

path: .spaces[2].metrics.halstead.volume
old: 382.5744501067311
new: 46.50699332842307

path: .spaces[2].metrics.halstead.length
old: 74.0
new: 14.0

path: .spaces[2].metrics.halstead.n1
old: 10.0
new: 6.0

path: .spaces[2].metrics.halstead.N1
old: 38.0
new: 9.0

path: .spaces[2].metrics.halstead.purity_ratio
old: 2.100415048926243
new: 1.6792696431662095

path: .spaces[2].metrics.halstead.effort
old: 2648.592346892754
new: 174.40122498158652

path: .spaces[2].metrics.halstead.vocabulary
old: 36.0
new: 10.0

path: .spaces[2].metrics.halstead.bugs
old: 0.06381001183354938
new: 0.010405072229378904

path: .spaces[2].metrics.halstead.n2
old: 26.0
new: 4.0

path: .spaces[2].metrics.halstead.N2
old: 36.0
new: 5.0

path: .spaces[2].metrics.halstead.difficulty
old: 6.923076923076923
new: 3.75

path: .spaces[2].metrics.loc.ploc
old: 13.0
new: 3.0

path: .spaces[2].metrics.loc.lloc
old: 2.0
new: 0.0

path: .spaces[2].metrics.loc.sloc
old: 14.0
new: 3.0

path: .spaces[2].metrics.loc.blank
old: 1.0
new: 0.0

path: .spaces[2].metrics.mi.mi_sei
old: 64.47694733127707
new: 116.28885349626572

path: .spaces[2].metrics.mi.mi_original
old: 97.09327022956496
new: 133.00654690726324

path: .spaces[2].metrics.mi.mi_visual_studio
old: 56.77969019272804
new: 77.78160637851651

Code

struct nsTArray_RelocationStrategy {
  using Type = nsTArray_RelocateUsingMoveConstructor;
};