diff options
Diffstat (limited to 'src/utils/array_2d_test.cc')
-rw-r--r-- | src/utils/array_2d_test.cc | 248 |
1 files changed, 248 insertions, 0 deletions
diff --git a/src/utils/array_2d_test.cc b/src/utils/array_2d_test.cc new file mode 100644 index 0000000..0535274 --- /dev/null +++ b/src/utils/array_2d_test.cc @@ -0,0 +1,248 @@ +// Copyright 2021 The libgav1 Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "src/utils/array_2d.h" + +#include <cstdint> +#include <memory> +#include <new> +#include <type_traits> + +#include "gtest/gtest.h" +#include "src/utils/compiler_attributes.h" + +#if LIBGAV1_MSAN +#include <sanitizer/msan_interface.h> +#endif + +namespace libgav1 { +namespace { + +constexpr int kRows = 50; +constexpr int kColumns = 200; + +TEST(Array2dViewTest, TestUint8) { + uint8_t data[kRows * kColumns] = {}; + Array2DView<uint8_t> data2d(kRows, kColumns, data); + + // Verify data. + data[kColumns] = 100; + data[kColumns + 1] = 101; + data[kColumns * 2 + 10] = 210; + data[kColumns * 2 + 40] = 240; + EXPECT_EQ(data2d[1][0], 100); + EXPECT_EQ(data2d[1][1], 101); + EXPECT_EQ(data2d[2][10], 210); + EXPECT_EQ(data2d[2][40], 240); + + // Verify pointers. + EXPECT_EQ(data2d[10], data + 10 * kColumns); +} + +TEST(Array2dViewTest, TestUint16) { + uint16_t data[kRows * kColumns] = {}; + Array2DView<uint16_t> data2d(kRows, kColumns, data); + + // Verify data. + data[kColumns] = 100; + data[kColumns + 1] = 101; + data[kColumns * 2 + 10] = 210; + data[kColumns * 2 + 40] = 240; + EXPECT_EQ(data2d[1][0], 100); + EXPECT_EQ(data2d[1][1], 101); + EXPECT_EQ(data2d[2][10], 210); + EXPECT_EQ(data2d[2][40], 240); + + // Verify pointers. + EXPECT_EQ(data2d[10], data + 10 * kColumns); +} + +TEST(Array2dViewTest, TestUint8Const) { + uint8_t data[kRows * kColumns] = {}; + // Declared as const to provide a read-only view of |data|. + const Array2DView<uint8_t> data2d(kRows, kColumns, data); + + // Verify data. + data[kColumns] = 100; + data[kColumns + 1] = 101; + data[kColumns * 2 + 10] = 210; + data[kColumns * 2 + 40] = 240; + EXPECT_EQ(data2d[1][0], 100); + EXPECT_EQ(data2d[1][1], 101); + EXPECT_EQ(data2d[2][10], 210); + EXPECT_EQ(data2d[2][40], 240); + + // Verify pointers. + EXPECT_EQ(data2d[10], data + 10 * kColumns); +} + +TEST(Array2dTest, TestUint8) { + Array2D<uint8_t> data2d; + ASSERT_TRUE(data2d.Reset(kRows, kColumns, true)); + + EXPECT_EQ(data2d.rows(), kRows); + EXPECT_EQ(data2d.columns(), kColumns); + + // Verify pointers. + for (int i = 0; i < kRows; ++i) { + EXPECT_NE(data2d[i], nullptr); + } + + // Verify data (must be zero initialized). + for (int i = 0; i < kRows; ++i) { + for (int j = 0; j < kColumns; ++j) { + EXPECT_EQ(data2d[i][j], 0) << "Mismatch in [" << i << "][" << j << "]"; + } + } + + // Reset to a 2d array of smaller size with zero_initialize == false. + data2d[0][0] = 10; + ASSERT_TRUE(data2d.Reset(kRows - 1, kColumns - 1, false)); + + EXPECT_EQ(data2d.rows(), kRows - 1); + EXPECT_EQ(data2d.columns(), kColumns - 1); + + // Verify pointers. + for (int i = 0; i < kRows - 1; ++i) { + EXPECT_NE(data2d[i], nullptr); + } + + // Verify data (must be zero except for 0,0 because it was zero initialized in + // the previous call to Reset). + for (int i = 0; i < kRows - 1; ++i) { + for (int j = 0; j < kColumns - 1; ++j) { + if (i == 0 && j == 0) { + EXPECT_EQ(data2d[i][j], 10) << "Mismatch in [" << i << "][" << j << "]"; + } else { + EXPECT_EQ(data2d[i][j], 0) << "Mismatch in [" << i << "][" << j << "]"; + } + } + } + + // Reset to a 2d array of smaller size with zero_initialize == true. + ASSERT_TRUE(data2d.Reset(kRows - 2, kColumns - 2, true)); + + EXPECT_EQ(data2d.rows(), kRows - 2); + EXPECT_EQ(data2d.columns(), kColumns - 2); + + // Verify pointers. + for (int i = 0; i < kRows - 2; ++i) { + EXPECT_NE(data2d[i], nullptr); + } + + // Verify data (must be zero initialized). + for (int i = 0; i < kRows - 2; ++i) { + for (int j = 0; j < kColumns - 2; ++j) { + EXPECT_EQ(data2d[i][j], 0) << "Mismatch in [" << i << "][" << j << "]"; + } + } +} + +TEST(Array2dTest, TestUniquePtr1) { + // A simple class that sets an int value to 0 in the destructor. + class Cleaner { + public: + explicit Cleaner(int* value) : value_(value) {} + ~Cleaner() { *value_ = 0; } + + private: + int* value_; + }; + int value = 100; + Array2D<std::unique_ptr<Cleaner>> data2d; + ASSERT_TRUE(data2d.Reset(4, 4, true)); + data2d[0][0].reset(new (std::nothrow) Cleaner(&value)); + EXPECT_EQ(value, 100); + // Reset to a smaller size. Depending on the implementation, the data_ buffer + // may or may not be reused. + ASSERT_TRUE(data2d.Reset(2, 2, true)); + // Reset to a much larger size. The data_ buffer will be reallocated. + ASSERT_TRUE(data2d.Reset(32, 32, true)); + // The destructors of all elements in the former data_ buffer should have + // been invoked. + EXPECT_EQ(value, 0); +} + +TEST(Array2dTest, TestUniquePtr2) { + // A simple class that sets an int value to 0 in the destructor. + class Cleaner { + public: + explicit Cleaner(int* value) : value_(value) {} + ~Cleaner() { *value_ = 0; } + + private: + int* value_; + }; + int value1 = 100; + int value2 = 200; + Array2D<std::unique_ptr<Cleaner>> data2d; + ASSERT_TRUE(data2d.Reset(4, 4, false)); + data2d[0][0].reset(new (std::nothrow) Cleaner(&value1)); + data2d[3][3].reset(new (std::nothrow) Cleaner(&value2)); + EXPECT_EQ(value1, 100); + EXPECT_EQ(value2, 200); + // Reset to a smaller size. Whether or not the data_ buffer is reused, the + // destructors of all existing elements should be invoked. + ASSERT_TRUE(data2d.Reset(2, 2, false)); + EXPECT_EQ(value1, 0); + EXPECT_EQ(value2, 0); +} + +// Shows that std::is_standard_layout is not relevant to the default +// initialization vs. value initialization issue, but std::is_trivial is. +TEST(Array2dTest, TestStructInit) { + // Make one data member private so that this struct does not have a standard + // layout. This also makes the struct not a POD type. + struct Point { + int x; + int Y() const { return y; } + + private: + int y; + }; + + EXPECT_TRUE(std::is_trivial<Point>::value); + EXPECT_FALSE(std::is_standard_layout<Point>::value); + + // The Point structs in this array are default initialized. + Array2D<Point> data2d_default_init; + ASSERT_TRUE(data2d_default_init.Reset(kRows, kColumns, false)); + // The Point structs in this array are value initialized (i.e., zero + // initialized). + Array2D<Point> data2d; + ASSERT_TRUE(data2d.Reset(kRows, kColumns, true)); + +#if LIBGAV1_MSAN + // Use MemorySanitizer to check Reset(rows, columns, false) does not + // initialize the memory while Reset(rows, columns, true) does. + // + // __msan_test_shadow(const void *x, uptr size) returns the offset of the + // first (at least partially) poisoned byte in the range, or -1 if the whole + // range is good. + for (int i = 0; i < kRows; ++i) { + EXPECT_EQ(__msan_test_shadow(data2d_default_init[i], + sizeof(data2d_default_init[0][0]) * kColumns), + 0); + EXPECT_EQ(__msan_test_shadow(data2d[i], sizeof(data2d[0][0]) * kColumns), + -1); + for (int j = 0; j < kColumns; ++j) { + EXPECT_EQ(data2d[i][j].x, 0); + EXPECT_EQ(data2d[i][j].Y(), 0); + } + } +#endif +} + +} // namespace +} // namespace libgav1 |