Reland "[utils] Test HeapArray::operator=()" This is a reland of commit b403e86696b9c74fa6739f512e0dab1f43e81907 Should be relandable now that warnings are suppressed better in https://dawn-review.googlesource.com/c/dawn/+/325155 Original change's description: > [utils] Test HeapArray::operator=() > > When run with LeakSanitizer, this will also catch if it leaks. > Unfortunately I can't find a CQ or CI bot that does that, so keep the > old (slow) leak tests too. > > Bug: 533297991, 512465980 > Change-Id: I4d91fc46cc466325fa3c439fe620e3cf3246a4e7 > Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/323555 > Commit-Queue: Kai Ninomiya <kainino@chromium.org> > Reviewed-by: Loko Kung <lokokung@google.com> Bug: 533297991, 512465980 Change-Id: I72acfff6b3c9ab9ca095657510b6c0c123b18cc2 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/325195 Commit-Queue: Loko Kung <lokokung@google.com> Auto-Submit: Kai Ninomiya <kainino@chromium.org> Reviewed-by: Loko Kung <lokokung@google.com>
diff --git a/src/utils/heap_array_test.cc b/src/utils/heap_array_test.cc index e9f4008..556220d 100644 --- a/src/utils/heap_array_test.cc +++ b/src/utils/heap_array_test.cc
@@ -45,6 +45,16 @@ // Type for use with Uninit() which requires a POD value type. using HeapArrayPOD = ityp::HeapArray<Index, int>; + + // Read the pointer of a HeapArray's allocation to prevent the allocation attempt from + // being optimized away, forcing any OOM or leak to actually happen at runtime. + template <typename Arr> + void PreventElidingAllocation(const Arr& arr) { + // Write the pointer value to volatile memory so that a pointer address must be created. + using ConstVoidPtr = const void*; + [[maybe_unused]] static volatile ConstVoidPtr data; + data = arr.data(); + } }; // Name "*DeathTest" per https://google.github.io/googletest/advanced.html#death-test-naming @@ -238,6 +248,24 @@ } } +// Test for HeapArray's assignment operator. +TEST_F(HeapArrayTest, Assignment) { + HeapArray<int> arr(100); + PreventElidingAllocation(arr); + arr[0] = 10; + EXPECT_EQ(arr[0], 10); + + // operator=() should replace the old allocation. If the test runs with LeakSanitizer + // (is_asan + ASAN_OPTIONS=detect_leaks=1), this will also check it's not leaked, but + // the FreedBy* tests are the primary tests of this. + arr = HeapArray<int>(200); + PreventElidingAllocation(arr); + EXPECT_EQ(arr.size(), 200); + EXPECT_EQ(arr[0], 0); + arr[199] = 20; + EXPECT_EQ(arr[199], 20); +} + // HeapArray is freed when it goes out of scope. Requires 512MiB of memory if working. // Should OOM (at least on systems without ridiculous amounts of memory) if broken. TEST_F(HeapArrayTest, FreedByScope) { @@ -346,9 +374,7 @@ // and one element smaller so that HeapArray will actually try to allocate. for (size_t leaveSpace : {0u, 4094u, 4095u}) { HeapArray<uint8_t> arr{std::numeric_limits<size_t>::max() - leaveSpace, std::nothrow}; - // Access the pointer first to prevent the allocation from being optimized away by - // the compiler assuming it will succeed. - EXPECT_EQ(arr.data(), nullptr); + PreventElidingAllocation(arr); EXPECT_FALSE(bool{arr}); } } @@ -360,8 +386,7 @@ // and one element smaller so that HeapArray will actually try to allocate. for (size_t leaveSpace : {0u, 1022u, 1023u}) { HeapArray<uint32_t> arr{kMaxSize - leaveSpace, std::nothrow}; - // Access the pointer first to prevent the allocation from being optimized away. - EXPECT_EQ(arr.data(), nullptr); + PreventElidingAllocation(arr); EXPECT_FALSE(bool{arr}); } @@ -384,45 +409,42 @@ { ityp::HeapArray<Key64, Val> arr{kHugeSize, std::nothrow}; - // Access the pointer first to prevent the allocation from being optimized away. - EXPECT_EQ(arr.data(), nullptr); + PreventElidingAllocation(arr); EXPECT_FALSE(bool{arr}); } { auto arr = // SAFETY: Testing unsafe API. DAWN_UNSAFE_BUFFERS(ityp::HeapArray<Key64, int>::Uninit(kHugeSize, std::nothrow)); - // Access the pointer first to prevent the allocation from being optimized away. - EXPECT_EQ(arr.data(), nullptr); + PreventElidingAllocation(arr); EXPECT_FALSE(bool{arr}); } } // Test allocations close to the size of the address space. TEST_F(HeapArrayDeathTest, OutOfMemoryAtLimit) { - // We store the allocation's pointer here so the allocation attempt can't be optimized away. - void* volatile ptr; - // Allocation of uint8_t values { // Exact maximum amount that will fit in the address space EXPECT_DEATH_IF_SUPPORTED( - (ptr = HeapArray<uint8_t>{std::numeric_limits<size_t>::max()}.data()), ""); + PreventElidingAllocation(HeapArray<uint8_t>{std::numeric_limits<size_t>::max()}), ""); // And a bit less. EXPECT_DEATH_IF_SUPPORTED( - (ptr = HeapArray<uint8_t>{std::numeric_limits<size_t>::max() - 4095}.data()), ""); + PreventElidingAllocation(HeapArray<uint8_t>{std::numeric_limits<size_t>::max() - 4095}), + ""); } // Allocation of uint32_t values { static constexpr size_t kMaxSize = std::numeric_limits<size_t>::max() / sizeof(uint32_t); // Maximum amount that will fit in the address space - EXPECT_DEATH_IF_SUPPORTED((ptr = HeapArray<uint32_t>{kMaxSize}.data()), ""); + EXPECT_DEATH_IF_SUPPORTED(PreventElidingAllocation(HeapArray<uint32_t>{kMaxSize}), ""); // And a bit less. - EXPECT_DEATH_IF_SUPPORTED((ptr = HeapArray<uint32_t>{kMaxSize - 1023}.data()), ""); + EXPECT_DEATH_IF_SUPPORTED(PreventElidingAllocation(HeapArray<uint32_t>{kMaxSize - 1023}), + ""); // Cases where size * sizeof(val) is larger than the address space and could overflow. - EXPECT_DEATH_IF_SUPPORTED((ptr = HeapArray<uint32_t>{kMaxSize + 1}.data()), ""); - EXPECT_DEATH_IF_SUPPORTED((ptr = HeapArray<uint32_t>{kMaxSize + 2}.data()), ""); + EXPECT_DEATH_IF_SUPPORTED(PreventElidingAllocation(HeapArray<uint32_t>{kMaxSize + 1}), ""); + EXPECT_DEATH_IF_SUPPORTED(PreventElidingAllocation(HeapArray<uint32_t>{kMaxSize + 2}), ""); } } @@ -431,13 +453,12 @@ using Key64 = TypedInteger<struct Key64T, uint64_t>; static constexpr Key64 kHugeSize{0x1000'0000'0000'0000u}; - // We store the allocation's pointer here so the allocation attempt can't be optimized away. - void* volatile ptr; - - EXPECT_DEATH_IF_SUPPORTED((ptr = ityp::HeapArray<Key64, Val>{kHugeSize}.data()), ""); + EXPECT_DEATH_IF_SUPPORTED(PreventElidingAllocation(ityp::HeapArray<Key64, Val>{kHugeSize}), ""); EXPECT_DEATH_IF_SUPPORTED( - // SAFETY: Testing unsafe API. - (ptr = DAWN_UNSAFE_BUFFERS(ityp::HeapArray<Key64, int>::Uninit(kHugeSize)).data()), ""); + PreventElidingAllocation( + // SAFETY: Testing unsafe API. + DAWN_UNSAFE_BUFFERS(ityp::HeapArray<Key64, int>::Uninit(kHugeSize))), + ""); } // Out of bounds accesses should crash even in release (the underlying container