diff options
Diffstat (limited to 'source/blender/functions/tests')
7 files changed, 1515 insertions, 0 deletions
diff --git a/source/blender/functions/tests/FN_array_spans_test.cc b/source/blender/functions/tests/FN_array_spans_test.cc new file mode 100644 index 00000000000..9a632b58be8 --- /dev/null +++ b/source/blender/functions/tests/FN_array_spans_test.cc @@ -0,0 +1,132 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include "FN_array_spans.hh" +#include "FN_generic_vector_array.hh" + +#include "BLI_array.hh" + +namespace blender::fn::tests { + +TEST(virtual_array_span, EmptyConstructor) +{ + VArraySpan<int> span; + EXPECT_EQ(span.size(), 0); + EXPECT_TRUE(span.is_empty()); + + GVArraySpan converted(span); + EXPECT_EQ(converted.type(), CPPType::get<int>()); + EXPECT_EQ(converted.size(), 0); +} + +TEST(virtual_array_span, SingleArrayConstructor) +{ + std::array<int, 4> values = {3, 4, 5, 6}; + VArraySpan<int> span{Span<int>(values), 3}; + EXPECT_EQ(span.size(), 3); + EXPECT_FALSE(span.is_empty()); + EXPECT_EQ(span[0].size(), 4); + EXPECT_EQ(span[1].size(), 4); + EXPECT_EQ(span[2].size(), 4); + EXPECT_EQ(span[0][0], 3); + EXPECT_EQ(span[0][1], 4); + EXPECT_EQ(span[0][2], 5); + EXPECT_EQ(span[0][3], 6); + EXPECT_EQ(span[1][3], 6); + EXPECT_EQ(span[2][1], 4); + + GVArraySpan converted(span); + EXPECT_EQ(converted.type(), CPPType::get<int>()); + EXPECT_EQ(converted.size(), 3); + EXPECT_EQ(converted[0].size(), 4); + EXPECT_EQ(converted[1].size(), 4); + EXPECT_EQ(converted[1][2], &values[2]); +} + +TEST(virtual_array_span, MultipleArrayConstructor) +{ + std::array<int, 4> values0 = {1, 2, 3, 4}; + std::array<int, 2> values1 = {6, 7}; + std::array<int, 1> values2 = {8}; + std::array<const int *, 3> starts = {values0.data(), values1.data(), values2.data()}; + std::array<int64_t, 3> sizes{values0.size(), values1.size(), values2.size()}; + + VArraySpan<int> span{starts, sizes}; + EXPECT_EQ(span.size(), 3); + EXPECT_FALSE(span.is_empty()); + EXPECT_EQ(span[0].size(), 4); + EXPECT_EQ(span[1].size(), 2); + EXPECT_EQ(span[2].size(), 1); + EXPECT_EQ(&span[0][0], values0.data()); + EXPECT_EQ(&span[1][0], values1.data()); + EXPECT_EQ(&span[2][0], values2.data()); + EXPECT_EQ(span[2][0], 8); + EXPECT_EQ(span[1][1], 7); + + GVArraySpan converted(span); + EXPECT_EQ(converted.type(), CPPType::get<int>()); + EXPECT_EQ(converted.size(), 3); + EXPECT_EQ(converted[0].size(), 4); + EXPECT_EQ(converted[1].size(), 2); + EXPECT_EQ(converted[2].size(), 1); + EXPECT_EQ(converted[0][0], values0.data()); + EXPECT_EQ(converted[1][1], values1.data() + 1); +} + +TEST(generic_virtual_array_span, TypeConstructor) +{ + GVArraySpan span{CPPType::get<int32_t>()}; + EXPECT_EQ(span.size(), 0); + EXPECT_TRUE(span.is_empty()); + + VArraySpan converted = span.typed<int>(); + EXPECT_EQ(converted.size(), 0); +} + +TEST(generic_virtual_array_span, GSpanConstructor) +{ + std::array<std::string, 3> values = {"hello", "world", "test"}; + GVArraySpan span{GSpan(CPPType::get<std::string>(), values.data(), 3), 5}; + EXPECT_EQ(span.size(), 5); + EXPECT_FALSE(span.is_empty()); + EXPECT_EQ(span[0][0], values.data()); + EXPECT_EQ(span[1][0], values.data()); + EXPECT_EQ(span[4][0], values.data()); + EXPECT_EQ(span[0].size(), 3); + EXPECT_EQ(span[2].size(), 3); + EXPECT_EQ(*(std::string *)span[3][1], "world"); + + VArraySpan converted = span.typed<std::string>(); + EXPECT_EQ(converted.size(), 5); + EXPECT_EQ(converted[0][0], "hello"); + EXPECT_EQ(converted[1][0], "hello"); + EXPECT_EQ(converted[4][0], "hello"); + EXPECT_EQ(converted[0].size(), 3); + EXPECT_EQ(converted[2].size(), 3); +} + +TEST(generic_virtual_array_span, IsSingleArray1) +{ + Array<int> values = {5, 6, 7}; + GVArraySpan span{GSpan(values.as_span()), 4}; + EXPECT_TRUE(span.is_single_array()); + + VArraySpan converted = span.typed<int>(); + EXPECT_TRUE(converted.is_single_array()); +} + +TEST(generic_virtual_array_span, IsSingleArray2) +{ + GVectorArray vectors{CPPType::get<int32_t>(), 3}; + GVectorArrayRef<int> vectors_ref = vectors; + vectors_ref.append(1, 4); + + GVArraySpan span = vectors; + EXPECT_FALSE(span.is_single_array()); + + VArraySpan converted = span.typed<int>(); + EXPECT_FALSE(converted.is_single_array()); +} + +} // namespace blender::fn::tests diff --git a/source/blender/functions/tests/FN_attributes_ref_test.cc b/source/blender/functions/tests/FN_attributes_ref_test.cc new file mode 100644 index 00000000000..3a5e4743c1e --- /dev/null +++ b/source/blender/functions/tests/FN_attributes_ref_test.cc @@ -0,0 +1,97 @@ +/* Apache License, Version 2.0 */ + +#include "BLI_float3.hh" +#include "FN_attributes_ref.hh" + +#include "testing/testing.h" + +namespace blender::fn::tests { + +TEST(attributes_info, BuildEmpty) +{ + AttributesInfoBuilder info_builder; + AttributesInfo info{info_builder}; + + EXPECT_EQ(info.size(), 0); +} + +TEST(attributes_info, AddSameNameTwice) +{ + AttributesInfoBuilder info_builder; + info_builder.add<int>("A", 4); + info_builder.add<int>("A", 5); + AttributesInfo info{info_builder}; + EXPECT_EQ(info.size(), 1); + EXPECT_TRUE(info.has_attribute("A", CPPType::get<int>())); + EXPECT_FALSE(info.has_attribute("B", CPPType::get<int>())); + EXPECT_FALSE(info.has_attribute("A", CPPType::get<float>())); + EXPECT_EQ(info.default_of<int>("A"), 4); + EXPECT_EQ(info.name_of(0), "A"); + EXPECT_EQ(info.index_range().start(), 0); + EXPECT_EQ(info.index_range().one_after_last(), 1); +} + +TEST(attributes_info, BuildWithDefaultString) +{ + AttributesInfoBuilder info_builder; + info_builder.add("A", CPPType::get<std::string>()); + AttributesInfo info{info_builder}; + EXPECT_EQ(info.default_of<std::string>("A"), ""); +} + +TEST(attributes_info, BuildWithGivenDefault) +{ + AttributesInfoBuilder info_builder; + info_builder.add<std::string>("A", "hello world"); + AttributesInfo info{info_builder}; + const void *default_value = info.default_of("A"); + EXPECT_EQ(*(const std::string *)default_value, "hello world"); + EXPECT_EQ(info.type_of("A"), CPPType::get<std::string>()); +} + +TEST(mutable_attributes_ref, ComplexTest) +{ + AttributesInfoBuilder info_builder; + info_builder.add<float3>("Position", {0, 0, 10}); + info_builder.add<uint>("ID", 0); + info_builder.add<float>("Size", 0.5f); + info_builder.add<std::string>("Name", "<no name>"); + AttributesInfo info{info_builder}; + + int amount = 5; + Array<float3> positions(amount); + Array<uint> ids(amount, 0); + Array<float> sizes(amount); + Array<std::string> names(amount); + + Array<void *> buffers = {positions.data(), ids.data(), sizes.data(), names.data()}; + MutableAttributesRef attributes{info, buffers, IndexRange(1, 3)}; + EXPECT_EQ(attributes.size(), 3); + EXPECT_EQ(attributes.info().size(), 4); + EXPECT_EQ(attributes.get("Position").data(), positions.data() + 1); + EXPECT_EQ(attributes.get("ID").data(), ids.data() + 1); + EXPECT_EQ(attributes.get("Size").data(), sizes.data() + 1); + EXPECT_EQ(attributes.get("Name").data(), names.data() + 1); + + EXPECT_EQ(attributes.get("ID").size(), 3); + EXPECT_EQ(attributes.get<uint>("ID").size(), 3); + + EXPECT_EQ(ids[2], 0); + MutableSpan<uint> ids_span = attributes.get<uint>("ID"); + ids_span[1] = 42; + EXPECT_EQ(ids[2], 42); + + EXPECT_FALSE(attributes.try_get<int>("not existant").has_value()); + EXPECT_FALSE(attributes.try_get<int>("Position").has_value()); + EXPECT_TRUE(attributes.try_get<float3>("Position").has_value()); + EXPECT_FALSE(attributes.try_get("not existant", CPPType::get<int>()).has_value()); + EXPECT_FALSE(attributes.try_get("Position", CPPType::get<int>()).has_value()); + EXPECT_TRUE(attributes.try_get("Position", CPPType::get<float3>()).has_value()); + + MutableAttributesRef sliced = attributes.slice(IndexRange(1, 2)); + EXPECT_EQ(sliced.size(), 2); + sliced.get<uint>("ID")[0] = 100; + EXPECT_EQ(ids[2], 100); +} + +} // namespace blender::fn::tests diff --git a/source/blender/functions/tests/FN_cpp_type_test.cc b/source/blender/functions/tests/FN_cpp_type_test.cc new file mode 100644 index 00000000000..29368b251cc --- /dev/null +++ b/source/blender/functions/tests/FN_cpp_type_test.cc @@ -0,0 +1,325 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include "FN_cpp_type.hh" + +namespace blender::fn::tests { + +static const int default_constructed_value = 1; +static const int copy_constructed_value = 2; +static const int move_constructed_value = 3; +static const int copy_constructed_from_value = 4; +static const int move_constructed_from_value = 5; +static const int copy_assigned_value = 6; +static const int copy_assigned_from_value = 7; +static const int move_assigned_value = 8; +static const int move_assigned_from_value = 9; +static const int destructed_value = 10; + +struct TestType { + mutable volatile int value; + + TestType() + { + value = default_constructed_value; + } + + ~TestType() + { + value = destructed_value; + } + + TestType(const TestType &other) + { + value = copy_constructed_value; + other.value = copy_constructed_from_value; + } + + TestType(TestType &&other) noexcept + { + value = move_constructed_value; + other.value = move_constructed_from_value; + } + + TestType &operator=(const TestType &other) + { + value = copy_assigned_value; + other.value = copy_assigned_from_value; + return *this; + } + + TestType &operator=(TestType &&other) noexcept + { + value = move_assigned_value; + other.value = move_assigned_from_value; + return *this; + } + + friend std::ostream &operator<<(std::ostream &stream, const TestType &value) + { + stream << value.value; + return stream; + } + + friend bool operator==(const TestType &UNUSED(a), const TestType &UNUSED(b)) + { + return false; + } + + uint64_t hash() const + { + return 0; + } +}; + +} // namespace blender::fn::tests + +MAKE_CPP_TYPE(TestType, blender::fn::tests::TestType) + +namespace blender::fn::tests { + +const CPPType &CPPType_TestType = CPPType::get<TestType>(); + +TEST(cpp_type, Size) +{ + EXPECT_EQ(CPPType_TestType.size(), sizeof(TestType)); +} + +TEST(cpp_type, Alignment) +{ + EXPECT_EQ(CPPType_TestType.alignment(), alignof(TestType)); +} + +TEST(cpp_type, Is) +{ + EXPECT_TRUE(CPPType_TestType.is<TestType>()); + EXPECT_FALSE(CPPType_TestType.is<int>()); +} + +TEST(cpp_type, DefaultConstruction) +{ + int buffer[10] = {0}; + CPPType_TestType.construct_default((void *)buffer); + EXPECT_EQ(buffer[0], default_constructed_value); + EXPECT_EQ(buffer[1], 0); + CPPType_TestType.construct_default_n((void *)buffer, 3); + EXPECT_EQ(buffer[0], default_constructed_value); + EXPECT_EQ(buffer[1], default_constructed_value); + EXPECT_EQ(buffer[2], default_constructed_value); + EXPECT_EQ(buffer[3], 0); + CPPType_TestType.construct_default_indices((void *)buffer, {2, 5, 7}); + EXPECT_EQ(buffer[2], default_constructed_value); + EXPECT_EQ(buffer[4], 0); + EXPECT_EQ(buffer[5], default_constructed_value); + EXPECT_EQ(buffer[6], 0); + EXPECT_EQ(buffer[7], default_constructed_value); + EXPECT_EQ(buffer[8], 0); +} + +TEST(cpp_type, Destruct) +{ + int buffer[10] = {0}; + CPPType_TestType.destruct((void *)buffer); + EXPECT_EQ(buffer[0], destructed_value); + EXPECT_EQ(buffer[1], 0); + CPPType_TestType.destruct_n((void *)buffer, 3); + EXPECT_EQ(buffer[0], destructed_value); + EXPECT_EQ(buffer[1], destructed_value); + EXPECT_EQ(buffer[2], destructed_value); + EXPECT_EQ(buffer[3], 0); + CPPType_TestType.destruct_indices((void *)buffer, {2, 5, 7}); + EXPECT_EQ(buffer[2], destructed_value); + EXPECT_EQ(buffer[4], 0); + EXPECT_EQ(buffer[5], destructed_value); + EXPECT_EQ(buffer[6], 0); + EXPECT_EQ(buffer[7], destructed_value); + EXPECT_EQ(buffer[8], 0); +} + +TEST(cpp_type, CopyToUninitialized) +{ + int buffer1[10] = {0}; + int buffer2[10] = {0}; + CPPType_TestType.copy_to_uninitialized((void *)buffer1, (void *)buffer2); + EXPECT_EQ(buffer1[0], copy_constructed_from_value); + EXPECT_EQ(buffer2[0], copy_constructed_value); + CPPType_TestType.copy_to_uninitialized_n((void *)buffer1, (void *)buffer2, 3); + EXPECT_EQ(buffer1[0], copy_constructed_from_value); + EXPECT_EQ(buffer2[0], copy_constructed_value); + EXPECT_EQ(buffer1[1], copy_constructed_from_value); + EXPECT_EQ(buffer2[1], copy_constructed_value); + EXPECT_EQ(buffer1[2], copy_constructed_from_value); + EXPECT_EQ(buffer2[2], copy_constructed_value); + EXPECT_EQ(buffer1[3], 0); + EXPECT_EQ(buffer2[3], 0); + CPPType_TestType.copy_to_uninitialized_indices((void *)buffer1, (void *)buffer2, {2, 5, 7}); + EXPECT_EQ(buffer1[2], copy_constructed_from_value); + EXPECT_EQ(buffer2[2], copy_constructed_value); + EXPECT_EQ(buffer1[4], 0); + EXPECT_EQ(buffer2[4], 0); + EXPECT_EQ(buffer1[5], copy_constructed_from_value); + EXPECT_EQ(buffer2[5], copy_constructed_value); + EXPECT_EQ(buffer1[6], 0); + EXPECT_EQ(buffer2[6], 0); + EXPECT_EQ(buffer1[7], copy_constructed_from_value); + EXPECT_EQ(buffer2[7], copy_constructed_value); + EXPECT_EQ(buffer1[8], 0); + EXPECT_EQ(buffer2[8], 0); +} + +TEST(cpp_type, CopyToInitialized) +{ + int buffer1[10] = {0}; + int buffer2[10] = {0}; + CPPType_TestType.copy_to_initialized((void *)buffer1, (void *)buffer2); + EXPECT_EQ(buffer1[0], copy_assigned_from_value); + EXPECT_EQ(buffer2[0], copy_assigned_value); + CPPType_TestType.copy_to_initialized_n((void *)buffer1, (void *)buffer2, 3); + EXPECT_EQ(buffer1[0], copy_assigned_from_value); + EXPECT_EQ(buffer2[0], copy_assigned_value); + EXPECT_EQ(buffer1[1], copy_assigned_from_value); + EXPECT_EQ(buffer2[1], copy_assigned_value); + EXPECT_EQ(buffer1[2], copy_assigned_from_value); + EXPECT_EQ(buffer2[2], copy_assigned_value); + EXPECT_EQ(buffer1[3], 0); + EXPECT_EQ(buffer2[3], 0); + CPPType_TestType.copy_to_initialized_indices((void *)buffer1, (void *)buffer2, {2, 5, 7}); + EXPECT_EQ(buffer1[2], copy_assigned_from_value); + EXPECT_EQ(buffer2[2], copy_assigned_value); + EXPECT_EQ(buffer1[4], 0); + EXPECT_EQ(buffer2[4], 0); + EXPECT_EQ(buffer1[5], copy_assigned_from_value); + EXPECT_EQ(buffer2[5], copy_assigned_value); + EXPECT_EQ(buffer1[6], 0); + EXPECT_EQ(buffer2[6], 0); + EXPECT_EQ(buffer1[7], copy_assigned_from_value); + EXPECT_EQ(buffer2[7], copy_assigned_value); + EXPECT_EQ(buffer1[8], 0); + EXPECT_EQ(buffer2[8], 0); +} + +TEST(cpp_type, RelocateToUninitialized) +{ + int buffer1[10] = {0}; + int buffer2[10] = {0}; + CPPType_TestType.relocate_to_uninitialized((void *)buffer1, (void *)buffer2); + EXPECT_EQ(buffer1[0], destructed_value); + EXPECT_EQ(buffer2[0], move_constructed_value); + CPPType_TestType.relocate_to_uninitialized_n((void *)buffer1, (void *)buffer2, 3); + EXPECT_EQ(buffer1[0], destructed_value); + EXPECT_EQ(buffer2[0], move_constructed_value); + EXPECT_EQ(buffer1[1], destructed_value); + EXPECT_EQ(buffer2[1], move_constructed_value); + EXPECT_EQ(buffer1[2], destructed_value); + EXPECT_EQ(buffer2[2], move_constructed_value); + EXPECT_EQ(buffer1[3], 0); + EXPECT_EQ(buffer2[3], 0); + CPPType_TestType.relocate_to_uninitialized_indices((void *)buffer1, (void *)buffer2, {2, 5, 7}); + EXPECT_EQ(buffer1[2], destructed_value); + EXPECT_EQ(buffer2[2], move_constructed_value); + EXPECT_EQ(buffer1[4], 0); + EXPECT_EQ(buffer2[4], 0); + EXPECT_EQ(buffer1[5], destructed_value); + EXPECT_EQ(buffer2[5], move_constructed_value); + EXPECT_EQ(buffer1[6], 0); + EXPECT_EQ(buffer2[6], 0); + EXPECT_EQ(buffer1[7], destructed_value); + EXPECT_EQ(buffer2[7], move_constructed_value); + EXPECT_EQ(buffer1[8], 0); + EXPECT_EQ(buffer2[8], 0); +} + +TEST(cpp_type, RelocateToInitialized) +{ + int buffer1[10] = {0}; + int buffer2[10] = {0}; + CPPType_TestType.relocate_to_initialized((void *)buffer1, (void *)buffer2); + EXPECT_EQ(buffer1[0], destructed_value); + EXPECT_EQ(buffer2[0], move_assigned_value); + CPPType_TestType.relocate_to_initialized_n((void *)buffer1, (void *)buffer2, 3); + EXPECT_EQ(buffer1[0], destructed_value); + EXPECT_EQ(buffer2[0], move_assigned_value); + EXPECT_EQ(buffer1[1], destructed_value); + EXPECT_EQ(buffer2[1], move_assigned_value); + EXPECT_EQ(buffer1[2], destructed_value); + EXPECT_EQ(buffer2[2], move_assigned_value); + EXPECT_EQ(buffer1[3], 0); + EXPECT_EQ(buffer2[3], 0); + CPPType_TestType.relocate_to_initialized_indices((void *)buffer1, (void *)buffer2, {2, 5, 7}); + EXPECT_EQ(buffer1[2], destructed_value); + EXPECT_EQ(buffer2[2], move_assigned_value); + EXPECT_EQ(buffer1[4], 0); + EXPECT_EQ(buffer2[4], 0); + EXPECT_EQ(buffer1[5], destructed_value); + EXPECT_EQ(buffer2[5], move_assigned_value); + EXPECT_EQ(buffer1[6], 0); + EXPECT_EQ(buffer2[6], 0); + EXPECT_EQ(buffer1[7], destructed_value); + EXPECT_EQ(buffer2[7], move_assigned_value); + EXPECT_EQ(buffer1[8], 0); + EXPECT_EQ(buffer2[8], 0); +} + +TEST(cpp_type, FillInitialized) +{ + int buffer1 = 0; + int buffer2[10] = {0}; + CPPType_TestType.fill_initialized((void *)&buffer1, (void *)buffer2, 3); + EXPECT_EQ(buffer1, copy_assigned_from_value); + EXPECT_EQ(buffer2[0], copy_assigned_value); + EXPECT_EQ(buffer2[1], copy_assigned_value); + EXPECT_EQ(buffer2[2], copy_assigned_value); + EXPECT_EQ(buffer2[3], 0); + + buffer1 = 0; + CPPType_TestType.fill_initialized_indices((void *)&buffer1, (void *)buffer2, {1, 6, 8}); + EXPECT_EQ(buffer1, copy_assigned_from_value); + EXPECT_EQ(buffer2[0], copy_assigned_value); + EXPECT_EQ(buffer2[1], copy_assigned_value); + EXPECT_EQ(buffer2[2], copy_assigned_value); + EXPECT_EQ(buffer2[3], 0); + EXPECT_EQ(buffer2[4], 0); + EXPECT_EQ(buffer2[5], 0); + EXPECT_EQ(buffer2[6], copy_assigned_value); + EXPECT_EQ(buffer2[7], 0); + EXPECT_EQ(buffer2[8], copy_assigned_value); + EXPECT_EQ(buffer2[9], 0); +} + +TEST(cpp_type, FillUninitialized) +{ + int buffer1 = 0; + int buffer2[10] = {0}; + CPPType_TestType.fill_uninitialized((void *)&buffer1, (void *)buffer2, 3); + EXPECT_EQ(buffer1, copy_constructed_from_value); + EXPECT_EQ(buffer2[0], copy_constructed_value); + EXPECT_EQ(buffer2[1], copy_constructed_value); + EXPECT_EQ(buffer2[2], copy_constructed_value); + EXPECT_EQ(buffer2[3], 0); + + buffer1 = 0; + CPPType_TestType.fill_uninitialized_indices((void *)&buffer1, (void *)buffer2, {1, 6, 8}); + EXPECT_EQ(buffer1, copy_constructed_from_value); + EXPECT_EQ(buffer2[0], copy_constructed_value); + EXPECT_EQ(buffer2[1], copy_constructed_value); + EXPECT_EQ(buffer2[2], copy_constructed_value); + EXPECT_EQ(buffer2[3], 0); + EXPECT_EQ(buffer2[4], 0); + EXPECT_EQ(buffer2[5], 0); + EXPECT_EQ(buffer2[6], copy_constructed_value); + EXPECT_EQ(buffer2[7], 0); + EXPECT_EQ(buffer2[8], copy_constructed_value); + EXPECT_EQ(buffer2[9], 0); +} + +TEST(cpp_type, DebugPrint) +{ + int value = 42; + std::stringstream ss; + CPPType::get<int32_t>().debug_print((void *)&value, ss); + std::string text = ss.str(); + EXPECT_EQ(text, "42"); +} + +} // namespace blender::fn::tests diff --git a/source/blender/functions/tests/FN_generic_vector_array_test.cc b/source/blender/functions/tests/FN_generic_vector_array_test.cc new file mode 100644 index 00000000000..77ec05f12dc --- /dev/null +++ b/source/blender/functions/tests/FN_generic_vector_array_test.cc @@ -0,0 +1,101 @@ +/* Apache License, Version 2.0 */ + +#include "FN_generic_vector_array.hh" + +#include "testing/testing.h" + +namespace blender::fn::tests { + +TEST(generic_vector_array, Constructor) +{ + GVectorArray vectors{CPPType::get<int32_t>(), 3}; + EXPECT_EQ(vectors.size(), 3); + EXPECT_EQ(vectors.lengths().size(), 3); + EXPECT_EQ(vectors.starts().size(), 3); + EXPECT_EQ(vectors.lengths()[0], 0); + EXPECT_EQ(vectors.lengths()[1], 0); + EXPECT_EQ(vectors.lengths()[2], 0); + EXPECT_EQ(vectors.type(), CPPType::get<int32_t>()); +} + +TEST(generic_vector_array, Append) +{ + GVectorArray vectors{CPPType::get<std::string>(), 3}; + std::string value = "hello"; + vectors.append(0, &value); + value = "world"; + vectors.append(0, &value); + vectors.append(2, &value); + + EXPECT_EQ(vectors.lengths()[0], 2); + EXPECT_EQ(vectors.lengths()[1], 0); + EXPECT_EQ(vectors.lengths()[2], 1); + EXPECT_EQ(vectors[0].size(), 2); + EXPECT_EQ(vectors[0].typed<std::string>()[0], "hello"); + EXPECT_EQ(vectors[0].typed<std::string>()[1], "world"); + EXPECT_EQ(vectors[2].typed<std::string>()[0], "world"); +} + +TEST(generic_vector_array, AsArraySpan) +{ + GVectorArray vectors{CPPType::get<int32_t>(), 3}; + int value = 3; + vectors.append(0, &value); + vectors.append(0, &value); + value = 5; + vectors.append(2, &value); + vectors.append(2, &value); + vectors.append(2, &value); + + GVArraySpan span = vectors; + EXPECT_EQ(span.type(), CPPType::get<int32_t>()); + EXPECT_EQ(span.size(), 3); + EXPECT_EQ(span[0].size(), 2); + EXPECT_EQ(span[1].size(), 0); + EXPECT_EQ(span[2].size(), 3); + EXPECT_EQ(span[0].typed<int>()[1], 3); + EXPECT_EQ(span[2].typed<int>()[0], 5); +} + +TEST(generic_vector_array, TypedRef) +{ + GVectorArray vectors{CPPType::get<int32_t>(), 4}; + GVectorArrayRef<int> ref = vectors.typed<int>(); + ref.append(0, 2); + ref.append(0, 6); + ref.append(0, 7); + ref.append(2, 1); + ref.append(2, 1); + ref.append(3, 5); + ref.append(3, 6); + + EXPECT_EQ(ref[0].size(), 3); + EXPECT_EQ(vectors[0].size(), 3); + EXPECT_EQ(ref[0][0], 2); + EXPECT_EQ(ref[0][1], 6); + EXPECT_EQ(ref[0][2], 7); + EXPECT_EQ(ref[1].size(), 0); + EXPECT_EQ(ref[2][0], 1); + EXPECT_EQ(ref[2][1], 1); + EXPECT_EQ(ref[3][0], 5); + EXPECT_EQ(ref[3][1], 6); +} + +TEST(generic_vector_array, Extend) +{ + GVectorArray vectors{CPPType::get<int32_t>(), 3}; + GVectorArrayRef<int> ref = vectors; + + ref.extend(1, {5, 6, 7}); + ref.extend(0, {3}); + + EXPECT_EQ(vectors[0].size(), 1); + EXPECT_EQ(vectors[1].size(), 3); + EXPECT_EQ(vectors[2].size(), 0); + EXPECT_EQ(ref[1][0], 5); + EXPECT_EQ(ref[1][1], 6); + EXPECT_EQ(ref[1][2], 7); + EXPECT_EQ(ref[0][0], 3); +} + +} // namespace blender::fn::tests diff --git a/source/blender/functions/tests/FN_multi_function_network_test.cc b/source/blender/functions/tests/FN_multi_function_network_test.cc new file mode 100644 index 00000000000..53290e9170c --- /dev/null +++ b/source/blender/functions/tests/FN_multi_function_network_test.cc @@ -0,0 +1,253 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include "FN_multi_function_builder.hh" +#include "FN_multi_function_network.hh" +#include "FN_multi_function_network_evaluation.hh" + +namespace blender::fn::tests { + +TEST(multi_function_network, Test1) +{ + CustomMF_SI_SO<int, int> add_10_fn("add 10", [](int value) { return value + 10; }); + CustomMF_SI_SI_SO<int, int, int> multiply_fn("multiply", [](int a, int b) { return a * b; }); + + MFNetwork network; + + MFNode &node1 = network.add_function(add_10_fn); + MFNode &node2 = network.add_function(multiply_fn); + MFOutputSocket &input_socket = network.add_input("Input", MFDataType::ForSingle<int>()); + MFInputSocket &output_socket = network.add_output("Output", MFDataType::ForSingle<int>()); + network.add_link(node1.output(0), node2.input(0)); + network.add_link(node1.output(0), node2.input(1)); + network.add_link(node2.output(0), output_socket); + network.add_link(input_socket, node1.input(0)); + + MFNetworkEvaluator network_fn{{&input_socket}, {&output_socket}}; + + { + Array<int> values = {4, 6, 1, 2, 0}; + Array<int> results(values.size(), 0); + + MFParamsBuilder params(network_fn, values.size()); + params.add_readonly_single_input(values.as_span()); + params.add_uninitialized_single_output(results.as_mutable_span()); + + MFContextBuilder context; + + network_fn.call({0, 2, 3, 4}, params, context); + + EXPECT_EQ(results[0], 14 * 14); + EXPECT_EQ(results[1], 0); + EXPECT_EQ(results[2], 11 * 11); + EXPECT_EQ(results[3], 12 * 12); + EXPECT_EQ(results[4], 10 * 10); + } + { + int value = 3; + Array<int> results(5, 0); + + MFParamsBuilder params(network_fn, results.size()); + params.add_readonly_single_input(&value); + params.add_uninitialized_single_output(results.as_mutable_span()); + + MFContextBuilder context; + + network_fn.call({1, 2, 4}, params, context); + + EXPECT_EQ(results[0], 0); + EXPECT_EQ(results[1], 13 * 13); + EXPECT_EQ(results[2], 13 * 13); + EXPECT_EQ(results[3], 0); + EXPECT_EQ(results[4], 13 * 13); + } +} + +class ConcatVectorsFunction : public MultiFunction { + public: + ConcatVectorsFunction() + { + MFSignatureBuilder signature = this->get_builder("Concat Vectors"); + signature.vector_mutable<int>("A"); + signature.vector_input<int>("B"); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + GVectorArrayRef<int> a = params.vector_mutable<int>(0); + VArraySpan<int> b = params.readonly_vector_input<int>(1); + + for (int64_t i : mask) { + a.extend(i, b[i]); + } + } +}; + +class AppendFunction : public MultiFunction { + public: + AppendFunction() + { + MFSignatureBuilder signature = this->get_builder("Append"); + signature.vector_mutable<int>("Vector"); + signature.single_input<int>("Value"); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + GVectorArrayRef<int> vectors = params.vector_mutable<int>(0); + VSpan<int> values = params.readonly_single_input<int>(1); + + for (int64_t i : mask) { + vectors.append(i, values[i]); + } + } +}; + +class SumVectorFunction : public MultiFunction { + public: + SumVectorFunction() + { + MFSignatureBuilder signature = this->get_builder("Sum Vector"); + signature.vector_input<int>("Vector"); + signature.single_output<int>("Sum"); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + VArraySpan<int> vectors = params.readonly_vector_input<int>(0); + MutableSpan<int> sums = params.uninitialized_single_output<int>(1); + + for (int64_t i : mask) { + int sum = 0; + VSpan<int> vector = vectors[i]; + for (int j = 0; j < vector.size(); j++) { + sum += vector[j]; + } + sums[i] = sum; + } + } +}; + +class CreateRangeFunction : public MultiFunction { + public: + CreateRangeFunction() + { + MFSignatureBuilder builder = this->get_builder("Create Range"); + builder.single_input<int>("Size"); + builder.vector_output<int>("Range"); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + VSpan<int> sizes = params.readonly_single_input<int>(0, "Size"); + GVectorArrayRef<int> ranges = params.vector_output<int>(1, "Range"); + + for (int64_t i : mask) { + int size = sizes[i]; + for (int j : IndexRange(size)) { + ranges.append(i, j); + } + } + } +}; + +TEST(multi_function_network, Test2) +{ + CustomMF_SI_SO<int, int> add_3_fn("add 3", [](int value) { return value + 3; }); + + ConcatVectorsFunction concat_vectors_fn; + AppendFunction append_fn; + SumVectorFunction sum_fn; + CreateRangeFunction create_range_fn; + + MFNetwork network; + + MFOutputSocket &input1 = network.add_input("Input 1", MFDataType::ForVector<int>()); + MFOutputSocket &input2 = network.add_input("Input 2", MFDataType::ForSingle<int>()); + MFInputSocket &output1 = network.add_output("Output 1", MFDataType::ForVector<int>()); + MFInputSocket &output2 = network.add_output("Output 2", MFDataType::ForSingle<int>()); + + MFNode &node1 = network.add_function(add_3_fn); + MFNode &node2 = network.add_function(create_range_fn); + MFNode &node3 = network.add_function(concat_vectors_fn); + MFNode &node4 = network.add_function(sum_fn); + MFNode &node5 = network.add_function(append_fn); + MFNode &node6 = network.add_function(sum_fn); + + network.add_link(input2, node1.input(0)); + network.add_link(node1.output(0), node2.input(0)); + network.add_link(node2.output(0), node3.input(1)); + network.add_link(input1, node3.input(0)); + network.add_link(input1, node4.input(0)); + network.add_link(node4.output(0), node5.input(1)); + network.add_link(node3.output(0), node5.input(0)); + network.add_link(node5.output(0), node6.input(0)); + network.add_link(node3.output(0), output1); + network.add_link(node6.output(0), output2); + + // std::cout << network.to_dot() << "\n\n"; + + MFNetworkEvaluator network_fn{{&input1, &input2}, {&output1, &output2}}; + + { + Array<int> input_value_1 = {3, 6}; + int input_value_2 = 4; + + GVectorArray output_value_1(CPPType::get<int32_t>(), 5); + Array<int> output_value_2(5, -1); + + MFParamsBuilder params(network_fn, 5); + params.add_readonly_vector_input(GVArraySpan(input_value_1.as_span(), 5)); + params.add_readonly_single_input(&input_value_2); + params.add_vector_output(output_value_1); + params.add_uninitialized_single_output(output_value_2.as_mutable_span()); + + MFContextBuilder context; + + network_fn.call({1, 2, 4}, params, context); + + EXPECT_EQ(output_value_1[0].size(), 0); + EXPECT_EQ(output_value_1[1].size(), 9); + EXPECT_EQ(output_value_1[2].size(), 9); + EXPECT_EQ(output_value_1[3].size(), 0); + EXPECT_EQ(output_value_1[4].size(), 9); + + EXPECT_EQ(output_value_2[0], -1); + EXPECT_EQ(output_value_2[1], 39); + EXPECT_EQ(output_value_2[2], 39); + EXPECT_EQ(output_value_2[3], -1); + EXPECT_EQ(output_value_2[4], 39); + } + { + GVectorArray input_value_1(CPPType::get<int32_t>(), 3); + GVectorArrayRef<int> input_value_ref_1 = input_value_1; + input_value_ref_1.extend(0, {3, 4, 5}); + input_value_ref_1.extend(1, {1, 2}); + + Array<int> input_value_2 = {4, 2, 3}; + + GVectorArray output_value_1(CPPType::get<int32_t>(), 3); + Array<int> output_value_2(3, -1); + + MFParamsBuilder params(network_fn, 3); + params.add_readonly_vector_input(input_value_1); + params.add_readonly_single_input(input_value_2.as_span()); + params.add_vector_output(output_value_1); + params.add_uninitialized_single_output(output_value_2.as_mutable_span()); + + MFContextBuilder context; + + network_fn.call({0, 1, 2}, params, context); + + EXPECT_EQ(output_value_1[0].size(), 10); + EXPECT_EQ(output_value_1[1].size(), 7); + EXPECT_EQ(output_value_1[2].size(), 6); + + EXPECT_EQ(output_value_2[0], 45); + EXPECT_EQ(output_value_2[1], 16); + EXPECT_EQ(output_value_2[2], 15); + } +} + +} // namespace blender::fn::tests diff --git a/source/blender/functions/tests/FN_multi_function_test.cc b/source/blender/functions/tests/FN_multi_function_test.cc new file mode 100644 index 00000000000..6acb6e22b01 --- /dev/null +++ b/source/blender/functions/tests/FN_multi_function_test.cc @@ -0,0 +1,385 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include "FN_multi_function.hh" +#include "FN_multi_function_builder.hh" + +namespace blender::fn::tests { + +class AddFunction : public MultiFunction { + public: + AddFunction() + { + MFSignatureBuilder builder = this->get_builder("Add"); + builder.single_input<int>("A"); + builder.single_input<int>("B"); + builder.single_output<int>("Result"); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + VSpan<int> a = params.readonly_single_input<int>(0, "A"); + VSpan<int> b = params.readonly_single_input<int>(1, "B"); + MutableSpan<int> result = params.uninitialized_single_output<int>(2, "Result"); + + for (int64_t i : mask) { + result[i] = a[i] + b[i]; + } + } +}; + +TEST(multi_function, AddFunction) +{ + AddFunction fn; + + Array<int> input1 = {4, 5, 6}; + Array<int> input2 = {10, 20, 30}; + Array<int> output(3, -1); + + MFParamsBuilder params(fn, 3); + params.add_readonly_single_input(input1.as_span()); + params.add_readonly_single_input(input2.as_span()); + params.add_uninitialized_single_output(output.as_mutable_span()); + + MFContextBuilder context; + + fn.call({0, 2}, params, context); + + EXPECT_EQ(output[0], 14); + EXPECT_EQ(output[1], -1); + EXPECT_EQ(output[2], 36); +} + +class AddPrefixFunction : public MultiFunction { + public: + AddPrefixFunction() + { + MFSignatureBuilder builder = this->get_builder("Add Prefix"); + builder.single_input<std::string>("Prefix"); + builder.single_mutable<std::string>("Strings"); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + VSpan<std::string> prefixes = params.readonly_single_input<std::string>(0, "Prefix"); + MutableSpan<std::string> strings = params.single_mutable<std::string>(1, "Strings"); + + for (int64_t i : mask) { + strings[i] = prefixes[i] + strings[i]; + } + } +}; + +TEST(multi_function, AddPrefixFunction) +{ + AddPrefixFunction fn; + + Array<std::string> strings = { + "Hello", + "World", + "This is a test", + "Another much longer string to trigger an allocation", + }; + + std::string prefix = "AB"; + + MFParamsBuilder params(fn, strings.size()); + params.add_readonly_single_input(&prefix); + params.add_single_mutable(strings.as_mutable_span()); + + MFContextBuilder context; + + fn.call({0, 2, 3}, params, context); + + EXPECT_EQ(strings[0], "ABHello"); + EXPECT_EQ(strings[1], "World"); + EXPECT_EQ(strings[2], "ABThis is a test"); + EXPECT_EQ(strings[3], "ABAnother much longer string to trigger an allocation"); +} + +class CreateRangeFunction : public MultiFunction { + public: + CreateRangeFunction() + { + MFSignatureBuilder builder = this->get_builder("Create Range"); + builder.single_input<uint>("Size"); + builder.vector_output<uint>("Range"); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + VSpan<uint> sizes = params.readonly_single_input<uint>(0, "Size"); + GVectorArrayRef<uint> ranges = params.vector_output<uint>(1, "Range"); + + for (int64_t i : mask) { + uint size = sizes[i]; + for (uint j : IndexRange(size)) { + ranges.append(i, j); + } + } + } +}; + +TEST(multi_function, CreateRangeFunction) +{ + CreateRangeFunction fn; + + GVectorArray ranges(CPPType::get<int32_t>(), 5); + GVectorArrayRef<uint> ranges_ref(ranges); + Array<uint> sizes = {3, 0, 6, 1, 4}; + + MFParamsBuilder params(fn, ranges.size()); + params.add_readonly_single_input(sizes.as_span()); + params.add_vector_output(ranges); + + MFContextBuilder context; + + fn.call({0, 1, 2, 3}, params, context); + + EXPECT_EQ(ranges_ref[0].size(), 3); + EXPECT_EQ(ranges_ref[1].size(), 0); + EXPECT_EQ(ranges_ref[2].size(), 6); + EXPECT_EQ(ranges_ref[3].size(), 1); + EXPECT_EQ(ranges_ref[4].size(), 0); + + EXPECT_EQ(ranges_ref[0][0], 0); + EXPECT_EQ(ranges_ref[0][1], 1); + EXPECT_EQ(ranges_ref[0][2], 2); + EXPECT_EQ(ranges_ref[2][0], 0); + EXPECT_EQ(ranges_ref[2][1], 1); +} + +class GenericAppendFunction : public MultiFunction { + public: + GenericAppendFunction(const CPPType &type) + { + MFSignatureBuilder builder = this->get_builder("Append"); + builder.vector_mutable("Vector", type); + builder.single_input("Value", type); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + GVectorArray &vectors = params.vector_mutable(0, "Vector"); + GVSpan values = params.readonly_single_input(1, "Value"); + + for (int64_t i : mask) { + vectors.append(i, values[i]); + } + } +}; + +TEST(multi_function, GenericAppendFunction) +{ + GenericAppendFunction fn(CPPType::get<int32_t>()); + + GVectorArray vectors(CPPType::get<int32_t>(), 4); + GVectorArrayRef<int> vectors_ref(vectors); + vectors_ref.append(0, 1); + vectors_ref.append(0, 2); + vectors_ref.append(2, 6); + Array<int> values = {5, 7, 3, 1}; + + MFParamsBuilder params(fn, vectors.size()); + params.add_vector_mutable(vectors); + params.add_readonly_single_input(values.as_span()); + + MFContextBuilder context; + + fn.call(IndexRange(vectors.size()), params, context); + + EXPECT_EQ(vectors_ref[0].size(), 3); + EXPECT_EQ(vectors_ref[1].size(), 1); + EXPECT_EQ(vectors_ref[2].size(), 2); + EXPECT_EQ(vectors_ref[3].size(), 1); + + EXPECT_EQ(vectors_ref[0][0], 1); + EXPECT_EQ(vectors_ref[0][1], 2); + EXPECT_EQ(vectors_ref[0][2], 5); + EXPECT_EQ(vectors_ref[1][0], 7); + EXPECT_EQ(vectors_ref[2][0], 6); + EXPECT_EQ(vectors_ref[2][1], 3); + EXPECT_EQ(vectors_ref[3][0], 1); +} + +TEST(multi_function, CustomMF_SI_SO) +{ + CustomMF_SI_SO<std::string, uint> fn("strlen", + [](const std::string &str) { return str.size(); }); + + Array<std::string> strings = {"hello", "world", "test", "another test"}; + Array<uint> sizes(strings.size(), 0); + + MFParamsBuilder params(fn, strings.size()); + params.add_readonly_single_input(strings.as_span()); + params.add_uninitialized_single_output(sizes.as_mutable_span()); + + MFContextBuilder context; + + fn.call(IndexRange(strings.size()), params, context); + + EXPECT_EQ(sizes[0], 5); + EXPECT_EQ(sizes[1], 5); + EXPECT_EQ(sizes[2], 4); + EXPECT_EQ(sizes[3], 12); +} + +TEST(multi_function, CustomMF_SI_SI_SO) +{ + CustomMF_SI_SI_SO<int, int, int> fn("mul", [](int a, int b) { return a * b; }); + + Array<int> values_a = {4, 6, 8, 9}; + int value_b = 10; + Array<int> outputs(values_a.size(), -1); + + MFParamsBuilder params(fn, values_a.size()); + params.add_readonly_single_input(values_a.as_span()); + params.add_readonly_single_input(&value_b); + params.add_uninitialized_single_output(outputs.as_mutable_span()); + + MFContextBuilder context; + + fn.call({0, 1, 3}, params, context); + + EXPECT_EQ(outputs[0], 40); + EXPECT_EQ(outputs[1], 60); + EXPECT_EQ(outputs[2], -1); + EXPECT_EQ(outputs[3], 90); +} + +TEST(multi_function, CustomMF_SI_SI_SI_SO) +{ + CustomMF_SI_SI_SI_SO<int, std::string, bool, uint> fn{ + "custom", + [](int a, const std::string &b, bool c) { return (uint)((uint)a + b.size() + (uint)c); }}; + + Array<int> values_a = {5, 7, 3, 8}; + Array<std::string> values_b = {"hello", "world", "another", "test"}; + Array<bool> values_c = {true, false, false, true}; + Array<uint> outputs(values_a.size(), 0); + + MFParamsBuilder params(fn, values_a.size()); + params.add_readonly_single_input(values_a.as_span()); + params.add_readonly_single_input(values_b.as_span()); + params.add_readonly_single_input(values_c.as_span()); + params.add_uninitialized_single_output(outputs.as_mutable_span()); + + MFContextBuilder context; + + fn.call({1, 2, 3}, params, context); + + EXPECT_EQ(outputs[0], 0); + EXPECT_EQ(outputs[1], 12); + EXPECT_EQ(outputs[2], 10); + EXPECT_EQ(outputs[3], 13); +} + +TEST(multi_function, CustomMF_SM) +{ + CustomMF_SM<std::string> fn("AddSuffix", [](std::string &value) { value += " test"; }); + + Array<std::string> values = {"a", "b", "c", "d", "e"}; + + MFParamsBuilder params(fn, values.size()); + params.add_single_mutable(values.as_mutable_span()); + + MFContextBuilder context; + + fn.call({1, 2, 3}, params, context); + + EXPECT_EQ(values[0], "a"); + EXPECT_EQ(values[1], "b test"); + EXPECT_EQ(values[2], "c test"); + EXPECT_EQ(values[3], "d test"); + EXPECT_EQ(values[4], "e"); +} + +TEST(multi_function, CustomMF_Constant) +{ + CustomMF_Constant<int> fn{42}; + + Array<int> outputs(4, 0); + + MFParamsBuilder params(fn, outputs.size()); + params.add_uninitialized_single_output(outputs.as_mutable_span()); + + MFContextBuilder context; + + fn.call({0, 2, 3}, params, context); + + EXPECT_EQ(outputs[0], 42); + EXPECT_EQ(outputs[1], 0); + EXPECT_EQ(outputs[2], 42); + EXPECT_EQ(outputs[3], 42); +} + +TEST(multi_function, CustomMF_GenericConstant) +{ + int value = 42; + CustomMF_GenericConstant fn{CPPType::get<int32_t>(), (const void *)&value}; + EXPECT_EQ(fn.param_name(0), "42"); + + Array<int> outputs(4, 0); + + MFParamsBuilder params(fn, outputs.size()); + params.add_uninitialized_single_output(outputs.as_mutable_span()); + + MFContextBuilder context; + + fn.call({0, 1, 2}, params, context); + + EXPECT_EQ(outputs[0], 42); + EXPECT_EQ(outputs[1], 42); + EXPECT_EQ(outputs[2], 42); + EXPECT_EQ(outputs[3], 0); +} + +TEST(multi_function, CustomMF_GenericConstantArray) +{ + std::array<int, 4> values = {3, 4, 5, 6}; + CustomMF_GenericConstantArray fn{GSpan(Span(values))}; + EXPECT_EQ(fn.param_name(0), "[3, 4, 5, 6, ]"); + + GVectorArray g_vector_array{CPPType::get<int32_t>(), 4}; + GVectorArrayRef<int> vector_array = g_vector_array; + + MFParamsBuilder params(fn, g_vector_array.size()); + params.add_vector_output(g_vector_array); + + MFContextBuilder context; + + fn.call({1, 2, 3}, params, context); + + EXPECT_EQ(vector_array[0].size(), 0); + EXPECT_EQ(vector_array[1].size(), 4); + EXPECT_EQ(vector_array[2].size(), 4); + EXPECT_EQ(vector_array[3].size(), 4); + for (int i = 1; i < 4; i++) { + EXPECT_EQ(vector_array[i][0], 3); + EXPECT_EQ(vector_array[i][1], 4); + EXPECT_EQ(vector_array[i][2], 5); + EXPECT_EQ(vector_array[i][3], 6); + } +} + +TEST(multi_function, CustomMF_Convert) +{ + CustomMF_Convert<float, int> fn; + + Array<float> inputs = {5.4f, 7.1f, 9.0f}; + Array<int> outputs(inputs.size(), 0); + + MFParamsBuilder params(fn, inputs.size()); + params.add_readonly_single_input(inputs.as_span()); + params.add_uninitialized_single_output(outputs.as_mutable_span()); + + MFContextBuilder context; + fn.call({0, 2}, params, context); + + EXPECT_EQ(outputs[0], 5); + EXPECT_EQ(outputs[1], 0); + EXPECT_EQ(outputs[2], 9); +} + +} // namespace blender::fn::tests diff --git a/source/blender/functions/tests/FN_spans_test.cc b/source/blender/functions/tests/FN_spans_test.cc new file mode 100644 index 00000000000..fbcf1fda71e --- /dev/null +++ b/source/blender/functions/tests/FN_spans_test.cc @@ -0,0 +1,222 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include "FN_spans.hh" + +namespace blender::fn::tests { + +TEST(generic_span, TypeConstructor) +{ + GSpan span(CPPType::get<float>()); + EXPECT_EQ(span.size(), 0); + EXPECT_EQ(span.typed<float>().size(), 0); + EXPECT_TRUE(span.is_empty()); +} + +TEST(generic_span, BufferAndSizeConstructor) +{ + int values[4] = {6, 7, 3, 2}; + void *buffer = (void *)values; + GSpan span(CPPType::get<int32_t>(), buffer, 4); + EXPECT_EQ(span.size(), 4); + EXPECT_FALSE(span.is_empty()); + EXPECT_EQ(span.typed<int>().size(), 4); + EXPECT_EQ(span[0], &values[0]); + EXPECT_EQ(span[1], &values[1]); + EXPECT_EQ(span[2], &values[2]); + EXPECT_EQ(span[3], &values[3]); +} + +TEST(generic_mutable_span, TypeConstructor) +{ + GMutableSpan span(CPPType::get<int32_t>()); + EXPECT_EQ(span.size(), 0); + EXPECT_TRUE(span.is_empty()); +} + +TEST(generic_mutable_span, BufferAndSizeConstructor) +{ + int values[4] = {4, 7, 3, 5}; + void *buffer = (void *)values; + GMutableSpan span(CPPType::get<int32_t>(), buffer, 4); + EXPECT_EQ(span.size(), 4); + EXPECT_FALSE(span.is_empty()); + EXPECT_EQ(span.typed<int>().size(), 4); + EXPECT_EQ(values[2], 3); + *(int *)span[2] = 10; + EXPECT_EQ(values[2], 10); + span.typed<int>()[2] = 20; + EXPECT_EQ(values[2], 20); +} + +TEST(virtual_span, EmptyConstructor) +{ + VSpan<int> span; + EXPECT_EQ(span.size(), 0); + EXPECT_TRUE(span.is_empty()); + EXPECT_FALSE(span.is_single_element()); + EXPECT_TRUE(span.is_full_array()); + + GVSpan converted(span); + EXPECT_EQ(converted.type(), CPPType::get<int>()); + EXPECT_EQ(converted.size(), 0); +} + +TEST(virtual_span, SpanConstructor) +{ + std::array<int, 5> values = {7, 3, 8, 6, 4}; + Span<int> span = values; + VSpan<int> virtual_span = span; + EXPECT_EQ(virtual_span.size(), 5); + EXPECT_FALSE(virtual_span.is_empty()); + EXPECT_EQ(virtual_span[0], 7); + EXPECT_EQ(virtual_span[2], 8); + EXPECT_EQ(virtual_span[3], 6); + EXPECT_FALSE(virtual_span.is_single_element()); + EXPECT_TRUE(virtual_span.is_full_array()); + + GVSpan converted(span); + EXPECT_EQ(converted.type(), CPPType::get<int>()); + EXPECT_EQ(converted.size(), 5); +} + +TEST(virtual_span, PointerSpanConstructor) +{ + int x0 = 3; + int x1 = 6; + int x2 = 7; + std::array<const int *, 3> pointers = {&x0, &x2, &x1}; + VSpan<int> span = Span<const int *>(pointers); + EXPECT_EQ(span.size(), 3); + EXPECT_FALSE(span.is_empty()); + EXPECT_EQ(span[0], 3); + EXPECT_EQ(span[1], 7); + EXPECT_EQ(span[2], 6); + EXPECT_EQ(&span[1], &x2); + EXPECT_FALSE(span.is_single_element()); + EXPECT_FALSE(span.is_full_array()); + + GVSpan converted(span); + EXPECT_EQ(converted.type(), CPPType::get<int>()); + EXPECT_EQ(converted.size(), 3); + EXPECT_EQ(converted[0], &x0); + EXPECT_EQ(converted[1], &x2); + EXPECT_EQ(converted[2], &x1); +} + +TEST(virtual_span, SingleConstructor) +{ + int value = 5; + VSpan<int> span = VSpan<int>::FromSingle(&value, 3); + EXPECT_EQ(span.size(), 3); + EXPECT_FALSE(span.is_empty()); + EXPECT_EQ(span[0], 5); + EXPECT_EQ(span[1], 5); + EXPECT_EQ(span[2], 5); + EXPECT_EQ(&span[0], &value); + EXPECT_EQ(&span[1], &value); + EXPECT_EQ(&span[2], &value); + EXPECT_TRUE(span.is_single_element()); + EXPECT_FALSE(span.is_full_array()); + + GVSpan converted(span); + EXPECT_EQ(converted.type(), CPPType::get<int>()); + EXPECT_EQ(converted.size(), 3); + EXPECT_EQ(converted[0], &value); + EXPECT_EQ(converted[1], &value); + EXPECT_EQ(converted[2], &value); +} + +TEST(generic_virtual_span, TypeConstructor) +{ + GVSpan span(CPPType::get<int32_t>()); + EXPECT_EQ(span.size(), 0); + EXPECT_TRUE(span.is_empty()); + EXPECT_FALSE(span.is_single_element()); + EXPECT_TRUE(span.is_full_array()); + + VSpan<int> converted = span.typed<int>(); + EXPECT_EQ(converted.size(), 0); +} + +TEST(generic_virtual_span, GenericSpanConstructor) +{ + int values[4] = {3, 4, 5, 6}; + GVSpan span{GSpan(CPPType::get<int32_t>(), values, 4)}; + EXPECT_EQ(span.size(), 4); + EXPECT_FALSE(span.is_empty()); + EXPECT_EQ(span[0], &values[0]); + EXPECT_EQ(span[1], &values[1]); + EXPECT_EQ(span[2], &values[2]); + EXPECT_EQ(span[3], &values[3]); + EXPECT_FALSE(span.is_single_element()); + EXPECT_TRUE(span.is_full_array()); + + int materialized[4] = {0}; + span.materialize_to_uninitialized(materialized); + EXPECT_EQ(materialized[0], 3); + EXPECT_EQ(materialized[1], 4); + EXPECT_EQ(materialized[2], 5); + EXPECT_EQ(materialized[3], 6); + + VSpan<int> converted = span.typed<int>(); + EXPECT_EQ(converted.size(), 4); + EXPECT_EQ(converted[0], 3); + EXPECT_EQ(converted[1], 4); + EXPECT_EQ(converted[2], 5); + EXPECT_EQ(converted[3], 6); +} + +TEST(generic_virtual_span, SpanConstructor) +{ + std::array<int, 3> values = {6, 7, 8}; + GVSpan span{Span<int>(values)}; + EXPECT_EQ(span.type(), CPPType::get<int32_t>()); + EXPECT_EQ(span.size(), 3); + EXPECT_EQ(span[0], &values[0]); + EXPECT_EQ(span[1], &values[1]); + EXPECT_EQ(span[2], &values[2]); + EXPECT_FALSE(span.is_single_element()); + EXPECT_TRUE(span.is_full_array()); + + int materialized[3] = {0}; + span.materialize_to_uninitialized(materialized); + EXPECT_EQ(materialized[0], 6); + EXPECT_EQ(materialized[1], 7); + EXPECT_EQ(materialized[2], 8); + + VSpan<int> converted = span.typed<int>(); + EXPECT_EQ(converted.size(), 3); + EXPECT_EQ(converted[0], 6); + EXPECT_EQ(converted[1], 7); + EXPECT_EQ(converted[2], 8); +} + +TEST(generic_virtual_span, SingleConstructor) +{ + int value = 5; + GVSpan span = GVSpan::FromSingle(CPPType::get<int32_t>(), &value, 3); + EXPECT_EQ(span.size(), 3); + EXPECT_FALSE(span.is_empty()); + EXPECT_EQ(span[0], &value); + EXPECT_EQ(span[1], &value); + EXPECT_EQ(span[2], &value); + EXPECT_TRUE(span.is_single_element()); + EXPECT_EQ(span.as_single_element(), &value); + EXPECT_FALSE(span.is_full_array()); + + int materialized[3] = {0}; + span.materialize_to_uninitialized({1, 2}, materialized); + EXPECT_EQ(materialized[0], 0); + EXPECT_EQ(materialized[1], 5); + EXPECT_EQ(materialized[2], 5); + + VSpan<int> converted = span.typed<int>(); + EXPECT_EQ(converted.size(), 3); + EXPECT_EQ(converted[0], 5); + EXPECT_EQ(converted[1], 5); + EXPECT_EQ(converted[2], 5); +} + +} // namespace blender::fn::tests |