From 673d7788693ab3b3a3da1f612825c9b03727b633 Mon Sep 17 00:00:00 2001 From: Denes Tarjan Date: Fri, 19 Jan 2024 11:36:08 +0100 Subject: [PATCH] [test] Implement testing of min_max_loc --- intrinsiccv/include/intrinsiccv.h | 15 ++ test/api/test_min_max.cpp | 371 ++++++++++++++++++------------ 2 files changed, 233 insertions(+), 153 deletions(-) diff --git a/intrinsiccv/include/intrinsiccv.h b/intrinsiccv/include/intrinsiccv.h index aa02dfb5f..fd7dba814 100644 --- a/intrinsiccv/include/intrinsiccv.h +++ b/intrinsiccv/include/intrinsiccv.h @@ -418,6 +418,21 @@ void INTRINSICCV_C_API(min_max_s32)(const int32_t *src, size_t src_stride, size_t width, size_t height, int32_t *min_value, int32_t *max_value); +/// Finds minimum and maximum element value across the source data, +/// and returns their location in the source data as offset in bytes +/// from the source beginning. +/// +/// @param src Pointer to the source data. Must be non-null. +/// @param src_stride Distance in bytes from the start of one row to the +/// start of the next row in the source data. Must not be +/// less than width * (element size in bytes). +/// @param width How many elements are in a row +/// @param height How many rows are in the data +/// @param min_offset Pointer to save result offset of minimum value to, or +/// nullptr if minimum is not to be calculated. +/// @param max_offset Pointer to save result offset of maximum value to, or +/// nullptr if maximum is not to be calculated. +/// void INTRINSICCV_C_API(min_max_loc_u8)(const uint8_t *src, size_t src_stride, size_t width, size_t height, size_t *min_offset, size_t *max_offset); diff --git a/test/api/test_min_max.cpp b/test/api/test_min_max.cpp index 9e8238cc2..c10cf475d 100644 --- a/test/api/test_min_max.cpp +++ b/test/api/test_min_max.cpp @@ -17,13 +17,16 @@ INTRINSICCV_MIN_MAX(int16_t, s16); INTRINSICCV_MIN_MAX(uint16_t, u16); INTRINSICCV_MIN_MAX(int32_t, s32); -template +#define INTRINSICCV_MIN_MAX_LOC(type, suffix) \ + INTRINSICCV_API(min_max_loc, intrinsiccv_min_max_loc_##suffix, type) + +INTRINSICCV_MIN_MAX_LOC(uint8_t, u8); + +template class MinMaxTest { using ArrayType = test::Array2D; - /// Returns the number of padding bytes at the end of rows. - size_t padding() const { return padding_; } - + protected: /// Returns the minimum value for ElementType. static constexpr ElementType min() { return std::numeric_limits::min(); @@ -35,10 +38,22 @@ class MinMaxTest { } /// Returns the number of vector lanes. - size_t lanes() const { return test::Options::vector_lanes(); } + static size_t lanes() { return test::Options::vector_lanes(); } + + // We have 2 rows, source_row0 and source_row1 + static size_t height() { return 2; } + + /// Tested number of elements in a row. + static size_t width() { + // Sufficient number of elements to exercise both vector and scalar paths. + return 3 * lanes() - 1; + } - /// Number of padding bytes at the end of rows. - size_t padding_{0}; + // Number of bytes in a row where the scalar path begins + static size_t scalar_offset() { return 2 * test::Options::vector_length(); } + + // Total number of bytes per row, padding included + static size_t stride() { return width() * sizeof(ElementType) + Padding; } struct Elements { std::initializer_list source_row0_vector; @@ -48,171 +63,221 @@ class MinMaxTest { ElementType filler_value; ElementType expected_min; ElementType expected_max; + size_t expected_min_offset; + size_t expected_max_offset; }; - // clang-format off - static constexpr Elements test_elements[] = { - { - {}, {}, - {}, {}, - 10, - 10, 10 - }, - { - {}, {}, - {}, {}, - min(), - min(), min() - }, - { - {}, {}, - {}, {}, - 0, - 0, 0 - }, - { - {}, {}, - {}, {}, - max(), - max(), max() - }, - { - {min()+10}, {}, - {max()}, {}, - min()+20, - min()+10, max() - }, - { - {}, {min()}, - {}, {max() - 10}, - min()+20, - min(), max()-10 - }, - { - {9}, {}, - {}, {11}, - 10, - 9, 11 - }, - { - {}, {9}, - {11}, {}, - 10, - 9, 11 - }, - { - {11}, {}, - {}, {9}, - 10, - 9, 11 - }, - { - {10, 9, 11}, {}, - {}, {}, - 10, - 9, 11 - }, - { - {9, 10, 11}, {}, - {}, {}, - 10, - 9, 11 - }, - { - { 3, 4, 5, 6}, {15, 16, 17}, - {20, 21, 22, 23}, {35, 2, 33}, - 10, - 2, 35 - }, - { - { 3, 4, 5, 6}, {15, 16, 17}, - {20, 2, 22, 23}, {35, 36, 33}, - 10, - 2, 36 - }, - { - { 1, 2, 3, 4}, {15, 16, 42}, - { 1, 2, 3, 4}, {15, 16, 42}, - 10, - 1, 42 - }, - - }; - // clang-format on + const std::array& test_elements() const { + static const std::array elements = {{ + // clang-format off + { + {}, {}, + {}, {}, + 10, + 10, 10, + 0, 0 + }, + { + {}, {}, + {}, {}, + min(), + min(), min(), + 0, 0 + }, + { + {}, {}, + {}, {}, + 0, + 0, 0, + 0, 0 + }, + { + {}, {}, + {}, {}, + max(), + max(), max(), + 0, 0 + }, + { + {min()+10}, {}, + {max()}, {}, + min()+20, + min()+10, max(), + 0, stride() + }, + { + {}, {min()}, + {}, {max() - 10}, + min()+20, + min(), max()-10, + scalar_offset(), stride() + scalar_offset() + }, + { + {9}, {}, + {}, {11}, + 10, + 9, 11, + 0, stride() + scalar_offset() + }, + { + {}, {9}, + {11}, {}, + 10, + 9, 11, + scalar_offset(), stride() + }, + { + {11}, {}, + {}, {9}, + 10, + 9, 11, + stride() + scalar_offset(), 0 + }, + { + {10, 9, 11}, {}, + {}, {}, + 10, + 9, 11, + 1, 2 + }, + { + {9, 10, 11}, {}, + {}, {}, + 10, + 9, 11, + 0, 2 + }, + { + { 3, 4, 5, 6}, {15, 16, 17}, + {20, 21, 22, 23}, {35, 2, 33}, + 10, + 2, 35, + stride() + scalar_offset() + sizeof(ElementType), stride() + scalar_offset() + }, + { + { 3, 4, 5, 6}, {15, 16, 17}, + {20, 2, 22, 23}, {35, 36, 33}, + 10, + 2, 36, + stride() + sizeof(ElementType), stride() + scalar_offset() + sizeof(ElementType) + }, + { + { 1, 2, 3, 4}, {15, 16, 42}, + { 1, 2, 3, 4}, {15, 16, 42}, + 10, + 1, 42, + 0, (scalar_offset() + 2 * sizeof(ElementType)) + }, + { + { 5, 6, 7, 8}, {1, 16, 42}, + { 1, 2, 3, 42}, {1, 16, 42}, + 10, + 1, 42, + scalar_offset(), (scalar_offset() + 2 * sizeof(ElementType)) + } + // clang-format on + }}; + return elements; + } - // We have 2 rows, source_row0 and source_row1 - size_t height() const { return 2; } + static void setup(ArrayType& source, const Elements& testData) { + source.fill(testData.filler_value); + source.set(0, 0, testData.source_row0_vector); + source.set(1, 0, testData.source_row1_vector); + source.set(0, (width() / lanes()) * lanes(), testData.source_row0_scalar); + source.set(1, (width() / lanes()) * lanes(), testData.source_row1_scalar); + } - /// Tested number of elements in a row. - size_t width() const { - // Sufficient number of elements to exercise both vector and scalar paths. - return 3 * lanes() - 1; + void one_test_call(const ArrayType& source, ElementType* p_min, + ElementType* p_max, ElementType expected_min, + ElementType expected_max) { + if (p_min) *p_min = std::numeric_limits::max(); + if (p_max) *p_max = std::numeric_limits::min(); + min_max()(source.data(), source.stride(), width(), height(), + p_min, p_max); + if (p_min) EXPECT_EQ(*p_min, expected_min); + if (p_max) EXPECT_EQ(*p_max, expected_max); } public: - MinMaxTest& with_padding(size_t padding) { - padding_ = padding; - return *this; + void test() { + for (auto& testData : test_elements()) { + ArrayType source{width(), height(), Padding}; + ASSERT_TRUE(source.valid()); + + setup(source, testData); + + ElementType actual_min, actual_max; + one_test_call(source, nullptr, nullptr, 0, 0); + one_test_call(source, &actual_min, nullptr, testData.expected_min, 0); + one_test_call(source, nullptr, &actual_max, 0, testData.expected_max); + one_test_call(source, &actual_min, &actual_max, testData.expected_min, + testData.expected_max); + } + } +}; // end of class MinMaxTest + +template +class MinMaxLocTest : public MinMaxTest { + using ArrayType = test::Array2D; + using Super = MinMaxTest; + using Super::height; + using Super::setup; + using Super::test_elements; + using Super::width; + + void one_test_call(const ArrayType& source, size_t* p_min_offset, + size_t* p_max_offset, size_t expected_min_offset, + size_t expected_max_offset) { + if (p_min_offset) *p_min_offset = std::numeric_limits::max(); + if (p_max_offset) *p_max_offset = std::numeric_limits::max(); + min_max_loc()(source.data(), source.stride(), width(), + height(), p_min_offset, p_max_offset); + if (p_min_offset) EXPECT_EQ(*p_min_offset, expected_min_offset); + if (p_max_offset) EXPECT_EQ(*p_max_offset, expected_max_offset); } + public: void test() { - for (auto testData : test_elements) { - ArrayType source{width(), height(), padding()}; + for (auto testData : test_elements()) { + ArrayType source{width(), height(), Padding}; ASSERT_TRUE(source.valid()); - source.fill(testData.filler_value); - // Fill elements one vector length apart. - for (size_t column_index = 0; column_index + lanes() < width(); - column_index += lanes()) { - source.set(0, column_index, testData.source_row0_vector); - source.set(1, column_index, testData.source_row1_vector); - } - source.set(0, (width() / lanes()) * lanes(), testData.source_row0_scalar); - source.set(1, (width() / lanes()) * lanes(), testData.source_row1_scalar); - - ElementType expected_min = testData.expected_min; - ElementType expected_max = testData.expected_max; - - ElementType actual_min = max(); - ElementType actual_max = min(); - - min_max()(source.data(), source.stride(), width(), height(), - nullptr, nullptr); - EXPECT_EQ(actual_min, max()); - EXPECT_EQ(actual_max, min()); - - actual_min = max(); - actual_max = min(); - min_max()(source.data(), source.stride(), width(), height(), - &actual_min, nullptr); - EXPECT_EQ(actual_min, expected_min); - EXPECT_EQ(actual_max, min()); - - actual_min = max(); - actual_max = min(); - min_max()(source.data(), source.stride(), width(), height(), - nullptr, &actual_max); - EXPECT_EQ(actual_min, max()); - EXPECT_EQ(actual_max, expected_max); - - actual_min = max(); - actual_max = min(); - min_max()(source.data(), source.stride(), width(), height(), - &actual_min, &actual_max); - EXPECT_EQ(actual_min, expected_min); - EXPECT_EQ(actual_max, expected_max); + setup(source, testData); + + size_t min_offset, max_offset; + one_test_call(source, nullptr, nullptr, 0, 0); + one_test_call(source, &min_offset, nullptr, testData.expected_min_offset, + 0); + one_test_call(source, nullptr, &max_offset, 0, + testData.expected_max_offset); + one_test_call(source, &min_offset, &max_offset, + testData.expected_min_offset, testData.expected_max_offset); } } -}; // end of class MinMaxTest +}; // end of class MinMaxLocTest template class MinMax : public testing::Test {}; -using ElementTypes = +using MinMaxElementTypes = ::testing::Types; -TYPED_TEST_SUITE(MinMax, ElementTypes); +TYPED_TEST_SUITE(MinMax, MinMaxElementTypes); TYPED_TEST(MinMax, API) { - MinMaxTest{}.test(); - MinMaxTest{}.with_padding(test::Options::vector_length()).test(); + MinMaxTest{}.test(); + MinMaxTest{}.test(); +} + +template +class MinMaxLoc : public testing::Test {}; + +using MinMaxLocElementTypes = ::testing::Types; +TYPED_TEST_SUITE(MinMaxLoc, MinMaxLocElementTypes); + +using namespace std; + +TYPED_TEST(MinMaxLoc, API) { + MinMaxLocTest{}.test(); + MinMaxLocTest{}.test(); } -- GitLab