From 572c48cf98601c66eebec27bb40cfc7e05ea341c Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 6 Jul 2020 09:08:53 +0200 Subject: BLI: improve exception safety of memory utils Even if we do not use exception in many places in Blender, our core C++ library should become exception safe. Otherwise, we don't even have the option to work with exceptions if we decide to do so. --- tests/gtests/blenlib/BLI_memory_utils_test.cc | 120 ++++++++++++++++++++++++++ tests/gtests/blenlib/BLI_vector_test.cc | 4 +- tests/gtests/blenlib/CMakeLists.txt | 1 + tests/gtests/functions/FN_cpp_type_test.cc | 4 +- 4 files changed, 125 insertions(+), 4 deletions(-) create mode 100644 tests/gtests/blenlib/BLI_memory_utils_test.cc (limited to 'tests') diff --git a/tests/gtests/blenlib/BLI_memory_utils_test.cc b/tests/gtests/blenlib/BLI_memory_utils_test.cc new file mode 100644 index 00000000000..aec57d02819 --- /dev/null +++ b/tests/gtests/blenlib/BLI_memory_utils_test.cc @@ -0,0 +1,120 @@ +#include "BLI_memory_utils.hh" +#include "BLI_strict_flags.h" +#include "testing/testing.h" + +namespace blender { + +struct MyValue { + static inline int alive = 0; + + MyValue() + { + if (alive == 15) { + throw std::exception(); + } + + alive++; + } + + MyValue(const MyValue &other) + { + if (alive == 15) { + throw std::exception(); + } + + alive++; + } + + ~MyValue() + { + alive--; + } +}; + +TEST(memory_utils, DefaultConstructN_ActuallyCallsConstructor) +{ + constexpr int amount = 10; + TypedBuffer buffer; + + EXPECT_EQ(MyValue::alive, 0); + default_construct_n(buffer.ptr(), amount); + EXPECT_EQ(MyValue::alive, amount); + destruct_n(buffer.ptr(), amount); + EXPECT_EQ(MyValue::alive, 0); +} + +TEST(memory_utils, DefaultConstructN_StrongExceptionSafety) +{ + constexpr int amount = 20; + TypedBuffer buffer; + + EXPECT_EQ(MyValue::alive, 0); + EXPECT_THROW(default_construct_n(buffer.ptr(), amount), std::exception); + EXPECT_EQ(MyValue::alive, 0); +} + +TEST(memory_utils, UninitializedCopyN_ActuallyCopies) +{ + constexpr int amount = 5; + TypedBuffer buffer1; + TypedBuffer buffer2; + + EXPECT_EQ(MyValue::alive, 0); + default_construct_n(buffer1.ptr(), amount); + EXPECT_EQ(MyValue::alive, amount); + uninitialized_copy_n(buffer1.ptr(), amount, buffer2.ptr()); + EXPECT_EQ(MyValue::alive, 2 * amount); + destruct_n(buffer1.ptr(), amount); + EXPECT_EQ(MyValue::alive, amount); + destruct_n(buffer2.ptr(), amount); + EXPECT_EQ(MyValue::alive, 0); +} + +TEST(memory_utils, UninitializedCopyN_StrongExceptionSafety) +{ + constexpr int amount = 10; + TypedBuffer buffer1; + TypedBuffer buffer2; + + EXPECT_EQ(MyValue::alive, 0); + default_construct_n(buffer1.ptr(), amount); + EXPECT_EQ(MyValue::alive, amount); + EXPECT_THROW(uninitialized_copy_n(buffer1.ptr(), amount, buffer2.ptr()), std::exception); + EXPECT_EQ(MyValue::alive, amount); + destruct_n(buffer1.ptr(), amount); + EXPECT_EQ(MyValue::alive, 0); +} + +TEST(memory_utils, UninitializedFillN_ActuallyCopies) +{ + constexpr int amount = 10; + TypedBuffer buffer; + + EXPECT_EQ(MyValue::alive, 0); + { + MyValue value; + EXPECT_EQ(MyValue::alive, 1); + uninitialized_fill_n(buffer.ptr(), amount, value); + EXPECT_EQ(MyValue::alive, 1 + amount); + destruct_n(buffer.ptr(), amount); + EXPECT_EQ(MyValue::alive, 1); + } + EXPECT_EQ(MyValue::alive, 0); +} + +TEST(memory_utils, UninitializedFillN_StrongExceptionSafety) +{ + constexpr int amount = 20; + TypedBuffer buffer; + + EXPECT_EQ(MyValue::alive, 0); + { + MyValue value; + EXPECT_EQ(MyValue::alive, 1); + EXPECT_THROW(uninitialized_fill_n(buffer.ptr(), amount, value), std::exception); + EXPECT_EQ(MyValue::alive, 1); + } + EXPECT_EQ(MyValue::alive, 0); +} + +} // namespace blender diff --git a/tests/gtests/blenlib/BLI_vector_test.cc b/tests/gtests/blenlib/BLI_vector_test.cc index ea4711ca015..bd72a67746a 100644 --- a/tests/gtests/blenlib/BLI_vector_test.cc +++ b/tests/gtests/blenlib/BLI_vector_test.cc @@ -499,7 +499,7 @@ class TypeConstructMock { { } - TypeConstructMock(TypeConstructMock &&other) : move_constructed(true) + TypeConstructMock(TypeConstructMock &&other) noexcept : move_constructed(true) { } @@ -513,7 +513,7 @@ class TypeConstructMock { return *this; } - TypeConstructMock &operator=(TypeConstructMock &&other) + TypeConstructMock &operator=(TypeConstructMock &&other) noexcept { if (this == &other) { return *this; diff --git a/tests/gtests/blenlib/CMakeLists.txt b/tests/gtests/blenlib/CMakeLists.txt index ba493b22b42..0831024556b 100644 --- a/tests/gtests/blenlib/CMakeLists.txt +++ b/tests/gtests/blenlib/CMakeLists.txt @@ -63,6 +63,7 @@ BLENDER_TEST(BLI_math_geom "bf_blenlib") BLENDER_TEST(BLI_math_matrix "bf_blenlib") BLENDER_TEST(BLI_math_vector "bf_blenlib") BLENDER_TEST(BLI_memiter "bf_blenlib") +BLENDER_TEST(BLI_memory_utils "bf_blenlib") BLENDER_TEST(BLI_path_util "${BLI_path_util_extra_libs}") BLENDER_TEST(BLI_polyfill_2d "bf_blenlib") BLENDER_TEST(BLI_set "bf_blenlib") diff --git a/tests/gtests/functions/FN_cpp_type_test.cc b/tests/gtests/functions/FN_cpp_type_test.cc index f6ae0877ed1..1297ca471b7 100644 --- a/tests/gtests/functions/FN_cpp_type_test.cc +++ b/tests/gtests/functions/FN_cpp_type_test.cc @@ -50,7 +50,7 @@ struct TestType { other.value = copy_constructed_from_value; } - TestType(TestType &&other) + TestType(TestType &&other) noexcept { value = move_constructed_value; other.value = move_constructed_from_value; @@ -63,7 +63,7 @@ struct TestType { return *this; } - TestType &operator=(TestType &&other) + TestType &operator=(TestType &&other) noexcept { value = move_assigned_value; other.value = move_assigned_from_value; -- cgit v1.2.3