# 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;
    };