Files
OCCT/src/FoundationClasses/TKernel/GTests/NCollection_Array1_Test.cxx
Pasukhin Dmitry 218862282b Foundation Classes - Add Emplace methods to NCollection containers (#1035)
Add in-place construction support to sequential and array containers, following the pattern already established in map containers.

New methods added:
- NCollection_List: EmplaceAppend, EmplacePrepend, EmplaceBefore, EmplaceAfter
- NCollection_Sequence: EmplaceAppend, EmplacePrepend, EmplaceAfter, EmplaceBefore
- NCollection_DynamicArray: EmplaceAppend, EmplaceValue
- NCollection_Array1: EmplaceValue
- NCollection_Array2: EmplaceValue

NCollection_Sequence::Node class extended with in-place constructor to support the new Emplace methods.

All methods use perfect forwarding to construct elements in-place, avoiding unnecessary copies or moves. This is particularly useful for:
- Types with expensive copy/move operations
- Types with multiple constructor arguments
- Move-only types (non-copyable)

Added corresponding unit tests for all new methods.
2026-01-28 16:42:43 +00:00

478 lines
11 KiB
C++

// Copyright (c) 2025 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.
#include <NCollection_Array1.hxx>
#include <Standard_Integer.hxx>
#include <gtest/gtest.h>
#include <algorithm>
#include <random>
#include <vector>
TEST(NCollection_Array1Test, DefaultConstructor)
{
// Default constructor should not compile as it's explicitly deleted
// NCollection_Array1<int> anArray;
// Instead we need to use the parameterized constructor
NCollection_Array1<int> anArray(1, 10);
EXPECT_EQ(10, anArray.Length());
EXPECT_EQ(1, anArray.Lower());
EXPECT_EQ(10, anArray.Upper());
}
TEST(NCollection_Array1Test, ConstructorWithBounds)
{
// Test constructor with explicit bounds
NCollection_Array1<int> anArray(1, 5);
EXPECT_EQ(5, anArray.Size());
EXPECT_EQ(1, anArray.Lower());
EXPECT_EQ(5, anArray.Upper());
}
TEST(NCollection_Array1Test, ConstructorWithNegativeBounds)
{
// Test constructor with negative bounds
NCollection_Array1<int> anArray(-3, 2);
EXPECT_EQ(6, anArray.Size());
EXPECT_EQ(-3, anArray.Lower());
EXPECT_EQ(2, anArray.Upper());
}
TEST(NCollection_Array1Test, AssignmentValue)
{
NCollection_Array1<int> anArray(1, 5);
// Initialize array
for (int i = anArray.Lower(); i <= anArray.Upper(); i++)
{
anArray(i) = i * 10;
}
// Verify values
for (int i = anArray.Lower(); i <= anArray.Upper(); i++)
{
EXPECT_EQ(i * 10, anArray(i));
}
// Test Value vs operator()
for (int i = anArray.Lower(); i <= anArray.Upper(); i++)
{
EXPECT_EQ(anArray.Value(i), anArray(i));
}
}
TEST(NCollection_Array1Test, CopyConstructor)
{
NCollection_Array1<int> anArray1(1, 5);
// Initialize array
for (int i = anArray1.Lower(); i <= anArray1.Upper(); i++)
{
anArray1(i) = i * 10;
}
// Copy construct
NCollection_Array1<int> anArray2(anArray1);
// Verify copy
EXPECT_EQ(anArray1.Length(), anArray2.Length());
EXPECT_EQ(anArray1.Lower(), anArray2.Lower());
EXPECT_EQ(anArray1.Upper(), anArray2.Upper());
for (int i = anArray1.Lower(); i <= anArray1.Upper(); i++)
{
EXPECT_EQ(anArray1(i), anArray2(i));
}
// Modify original to ensure deep copy
anArray1(3) = 999;
EXPECT_NE(anArray1(3), anArray2(3));
}
TEST(NCollection_Array1Test, ValueAccess)
{
NCollection_Array1<int> anArray(1, 5);
// Set values
for (int i = 1; i <= 5; i++)
{
anArray.SetValue(i, i * 10);
}
// Check values
for (int i = 1; i <= 5; i++)
{
EXPECT_EQ(i * 10, anArray.Value(i));
EXPECT_EQ(i * 10, anArray(i)); // Using operator()
}
}
TEST(NCollection_Array1Test, ChangeValueAccess)
{
NCollection_Array1<int> anArray(1, 5);
// Set values using ChangeValue
for (int i = 1; i <= 5; i++)
{
anArray.ChangeValue(i) = i * 10;
}
// Check values
for (int i = 1; i <= 5; i++)
{
EXPECT_EQ(i * 10, anArray(i));
}
// Modify values through references
for (int i = 1; i <= 5; i++)
{
anArray.ChangeValue(i) += 5;
}
// Check modified values
for (int i = 1; i <= 5; i++)
{
EXPECT_EQ(i * 10 + 5, anArray(i));
}
}
TEST(NCollection_Array1Test, AssignmentOperator)
{
NCollection_Array1<int> anArray1(1, 5);
// Initialize array
for (int i = anArray1.Lower(); i <= anArray1.Upper(); i++)
{
anArray1(i) = i * 10;
}
// Test assignment
NCollection_Array1<int> anArray2(11, 15);
anArray2 = anArray1;
anArray2.Resize(1, 5, true); // Resize to match anArray1
// Verify assignment result
EXPECT_EQ(anArray1.Length(), anArray2.Length());
EXPECT_EQ(anArray1.Lower(), anArray2.Lower());
EXPECT_EQ(anArray1.Upper(), anArray2.Upper());
for (int i = anArray1.Lower(); i <= anArray1.Upper(); i++)
{
EXPECT_EQ(anArray1(i), anArray2(i));
}
}
TEST(NCollection_Array1Test, Move)
{
NCollection_Array1<int> anArray1(1, 5);
// Initialize array
for (int i = anArray1.Lower(); i <= anArray1.Upper(); i++)
{
anArray1(i) = i * 10;
}
// Test Move method
NCollection_Array1<int> anArray2(11, 15);
anArray2.Move(anArray1);
anArray2.Resize(1, 5, true); // Resize to match anArray1
// Verify move result
EXPECT_EQ(5, anArray2.Length());
EXPECT_EQ(1, anArray2.Lower());
EXPECT_EQ(5, anArray2.Upper());
// Original array is not keep referecing the same data
EXPECT_EQ(anArray1.Length(), 0);
EXPECT_EQ(anArray1.Lower(), 1);
}
TEST(NCollection_Array1Test, Init)
{
NCollection_Array1<int> anArray(1, 5);
// Test Init method
anArray.Init(42);
// Verify all values initialized
for (int i = anArray.Lower(); i <= anArray.Upper(); i++)
{
EXPECT_EQ(42, anArray(i));
}
}
TEST(NCollection_Array1Test, SetValue)
{
NCollection_Array1<int> anArray(1, 5);
// Test SetValue method
anArray.SetValue(3, 123);
// Verify value set
EXPECT_EQ(123, anArray(3));
}
TEST(NCollection_Array1Test, FirstLast)
{
NCollection_Array1<int> anArray(5, 10);
anArray(5) = 55;
anArray(10) = 1010;
// Test First and Last methods
EXPECT_EQ(55, anArray.First());
EXPECT_EQ(1010, anArray.Last());
// Change First and Last
anArray.ChangeFirst() = 555;
anArray.ChangeLast() = 10101;
EXPECT_EQ(555, anArray.First());
EXPECT_EQ(10101, anArray.Last());
}
TEST(NCollection_Array1Test, STLIteration)
{
NCollection_Array1<int> anArray(1, 5);
for (int i = anArray.Lower(); i <= anArray.Upper(); i++)
{
anArray(i) = i * 10;
}
// Test STL-style iteration
int index = 1;
for (const auto& val : anArray)
{
EXPECT_EQ(index * 10, val);
index++;
}
}
TEST(NCollection_Array1Test, Resize)
{
NCollection_Array1<int> anArray(1, 5);
for (int i = anArray.Lower(); i <= anArray.Upper(); i++)
{
anArray(i) = i * 10;
}
// Test Resize method - increasing size
anArray.Resize(1, 10, true);
// Check new size
EXPECT_EQ(10, anArray.Length());
EXPECT_EQ(1, anArray.Lower());
EXPECT_EQ(10, anArray.Upper());
// Verify original data preserved
for (int i = 1; i <= 5; i++)
{
EXPECT_EQ(i * 10, anArray(i));
}
// Test Resize method - decreasing size
NCollection_Array1<int> anArray2(1, 10);
for (int i = anArray2.Lower(); i <= anArray2.Upper(); i++)
{
anArray2(i) = i * 10;
}
anArray2.Resize(1, 5, true);
// Check new size
EXPECT_EQ(5, anArray2.Length());
EXPECT_EQ(1, anArray2.Lower());
EXPECT_EQ(5, anArray2.Upper());
// Verify original data preserved
for (int i = 1; i <= 5; i++)
{
EXPECT_EQ(i * 10, anArray2(i));
}
}
TEST(NCollection_Array1Test, ChangeValue)
{
NCollection_Array1<int> anArray(1, 5);
anArray.Init(42);
// Test ChangeValue method
anArray.ChangeValue(3) = 123;
// Verify value changed
EXPECT_EQ(123, anArray(3));
// Verify other values unchanged
EXPECT_EQ(42, anArray(1));
EXPECT_EQ(42, anArray(2));
EXPECT_EQ(42, anArray(4));
EXPECT_EQ(42, anArray(5));
}
TEST(NCollection_Array1Test, IteratorAccess)
{
NCollection_Array1<int> anArray(1, 5);
for (int i = 1; i <= 5; i++)
{
anArray(i) = i * 10;
}
// Test iteration using STL-compatible iterators
int index = 1;
for (auto it = anArray.begin(); it != anArray.end(); ++it, ++index)
{
EXPECT_EQ(index * 10, *it);
}
// Test range-based for loop
index = 1;
for (const auto& value : anArray)
{
EXPECT_EQ(index * 10, value);
index++;
}
}
TEST(NCollection_Array1Test, STLAlgorithmCompatibility_MinMax)
{
const int size = 100;
NCollection_Array1<int> anArray(1, size);
std::vector<int> aVector;
std::mt19937 aGenerator(1); // Fixed seed for reproducible tests
std::uniform_int_distribution<int> aDistribution(0, RAND_MAX);
for (int anIdx = 1; anIdx <= size; ++anIdx)
{
int aVal = aDistribution(aGenerator);
anArray(anIdx) = aVal;
aVector.push_back(aVal);
}
auto aMinOCCT = std::min_element(anArray.begin(), anArray.end());
auto aMinStd = std::min_element(aVector.begin(), aVector.end());
auto aMaxOCCT = std::max_element(anArray.begin(), anArray.end());
auto aMaxStd = std::max_element(aVector.begin(), aVector.end());
EXPECT_EQ(*aMinOCCT, *aMinStd);
EXPECT_EQ(*aMaxOCCT, *aMaxStd);
}
TEST(NCollection_Array1Test, STLAlgorithmCompatibility_Replace)
{
const int size = 100;
NCollection_Array1<int> anArray(1, size);
std::vector<int> aVector;
std::mt19937 aGenerator(1); // Fixed seed for reproducible tests
std::uniform_int_distribution<int> aDistribution(0, RAND_MAX);
for (int anIdx = 1; anIdx <= size; ++anIdx)
{
int aVal = aDistribution(aGenerator);
anArray(anIdx) = aVal;
aVector.push_back(aVal);
}
int aTargetValue = aVector.back();
int aNewValue = -1;
std::replace(anArray.begin(), anArray.end(), aTargetValue, aNewValue);
std::replace(aVector.begin(), aVector.end(), aTargetValue, aNewValue);
EXPECT_TRUE(std::equal(anArray.begin(), anArray.end(), aVector.begin()));
}
TEST(NCollection_Array1Test, STLAlgorithmCompatibility_Sort)
{
const int size = 100;
NCollection_Array1<int> anArray(1, size);
std::vector<int> aVector;
std::mt19937 aGenerator(1); // Fixed seed for reproducible tests
std::uniform_int_distribution<int> aDistribution(0, RAND_MAX);
for (int anIdx = 1; anIdx <= size; ++anIdx)
{
int aVal = aDistribution(aGenerator);
anArray(anIdx) = aVal;
aVector.push_back(aVal);
}
std::sort(anArray.begin(), anArray.end());
std::sort(aVector.begin(), aVector.end());
EXPECT_TRUE(std::equal(anArray.begin(), anArray.end(), aVector.begin()));
}
// Helper struct for testing in-place construction with multiple arguments
struct Array1MultiArgType
{
int myA;
double myB;
Array1MultiArgType()
: myA(0),
myB(0.0)
{
}
Array1MultiArgType(int theA, double theB)
: myA(theA),
myB(theB)
{
}
};
TEST(NCollection_Array1Test, EmplaceValue)
{
NCollection_Array1<Array1MultiArgType> anArray(1, 5);
// Test EmplaceValue with multiple constructor arguments
Array1MultiArgType& aRef1 = anArray.EmplaceValue(1, 42, 3.14);
EXPECT_EQ(42, aRef1.myA);
EXPECT_NEAR(3.14, aRef1.myB, 1e-10);
Array1MultiArgType& aRef2 = anArray.EmplaceValue(3, 100, 2.71);
EXPECT_EQ(100, aRef2.myA);
EXPECT_NEAR(2.71, aRef2.myB, 1e-10);
// Verify the values are in the array
EXPECT_EQ(42, anArray(1).myA);
EXPECT_EQ(100, anArray(3).myA);
// Verify other elements are default-constructed
EXPECT_EQ(0, anArray(2).myA);
EXPECT_EQ(0, anArray(4).myA);
EXPECT_EQ(0, anArray(5).myA);
}
TEST(NCollection_Array1Test, EmplaceValue_ReplacesExisting)
{
NCollection_Array1<Array1MultiArgType> anArray(1, 3);
// Set initial values
anArray.EmplaceValue(1, 10, 1.0);
anArray.EmplaceValue(2, 20, 2.0);
anArray.EmplaceValue(3, 30, 3.0);
// Replace value at index 2
Array1MultiArgType& aRef = anArray.EmplaceValue(2, 200, 20.0);
EXPECT_EQ(200, aRef.myA);
EXPECT_NEAR(20.0, aRef.myB, 1e-10);
// Verify other values unchanged
EXPECT_EQ(10, anArray(1).myA);
EXPECT_EQ(200, anArray(2).myA);
EXPECT_EQ(30, anArray(3).myA);
}