diff --git a/.vscode/settings.json b/.vscode/settings.json index 879390ca..053790b9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -40,44 +40,49 @@ "sonar.cfamily.reportingCppStandardOverride": "c++14" }, "cSpell.words": [ + "arity", + "AUTOSAR", + "builddir", "cetl", - "DCETL_", - "cetlvast", - "DCETLVAST_", - "DCYPHAL", - "cyphal", - "opencyphal", "cetlpf", - "AUTOSAR", - "tparam", - "unsynchronized", + "cetlvast", + "COMPILETEST_PRECHECK", "copydoc", - "trimleft", - "Pavel", - "Kirienko", - "gtest", - "gmock", - "DSDL", - "rend", - "rbegin", - "builddir", - "sonarqube", - "sonarcloud", - "doxygen", - "googletest", - "googlemock", - "DCMAKE_", "ctest", + "cyphal", + "DCETL_", + "DCETLVAST_", + "DCMAKE_", "DCTEST_", - "pushd", - "popd", - "gcovr", + "DCYPHAL", "devcontainer", "DFETCHCONTENT_FULLY_DISCONNECTED", - "COMPILETEST_PRECHECK", - "arity", + "doxygen", + "DSDL", + "endforeach", + "endfunction", + "gcovr", + "gmock", + "googlemock", + "googletest", + "gtest", + "insertable", + "Kirienko", "mainpage", - "subpage" + "NOTFOUND", + "opencyphal", + "Pavel", + "popd", + "pushd", + "rbegin", + "rend", + "sonarcloud", + "sonarqube", + "STREQUAL", + "subpage", + "tparam", + "trimleft", + "unsynchronized" ], "files.associations": { "memory_resource": "cpp", @@ -180,7 +185,9 @@ "typeindex": "cpp", "charconv": "cpp", "csignal": "cpp", - "format": "cpp" + "format": "cpp", + "execution": "cpp", + "filesystem": "cpp" }, "git-blame.gitWebUrl": "" } diff --git a/cetlvast/cmake/modules/Findgcovr.cmake b/cetlvast/cmake/modules/Findgcovr.cmake index 7657f3e6..9d1fc8d1 100644 --- a/cetlvast/cmake/modules/Findgcovr.cmake +++ b/cetlvast/cmake/modules/Findgcovr.cmake @@ -3,6 +3,7 @@ # Copyright Amazon.com Inc. or its affiliates. # SPDX-License-Identifier: MIT # +# cSpell: words fprofile fcoverage gcov tracefile objdir gcno gcda objlib tracefiles find_program(GCOVR gcovr) @@ -22,7 +23,7 @@ define_property(DIRECTORY ) # function: enable_instrumentation -# Sets well-known compiler flags for gcc and/or clang to insert intrumentations +# Sets well-known compiler flags for gcc and/or clang to insert instrumentations # into binaries that generate coverage data. # # param: TARGET target - The target to set compile and link options on. @@ -199,12 +200,12 @@ endfunction(define_gcovr_tracefile_target) # # param: COVERAGE_REPORT_FORMATS - Supports html or sonarqube # param: ROOT_DIRECTORY string - The root directory of the source to be covered. -# param: OUT_REPORT_INDICIES list[string] - The name of a variable to set to a list of index files of the reports. +# param: OUT_REPORT_INDICES list[string] - The name of a variable to set to a list of index files of the reports. # function (enable_coverage_report) #+-[input]----------------------------------------------------------------+ set(options "") - set(singleValueArgs OUT_REPORT_INDICIES ROOT_DIRECTORY) + set(singleValueArgs OUT_REPORT_INDICES ROOT_DIRECTORY) set(multiValueArgs COVERAGE_REPORT_FORMATS) cmake_parse_arguments(PARSE_ARGV 0 ARG "${options}" "${singleValueArgs}" "${multiValueArgs}") @@ -241,7 +242,7 @@ function (enable_coverage_report) else() message(FATAL_ERROR "${LOCAL_REPORT_FORMAT} is not a supported coverage report format.") endif() - list(APPEND LOCAL_REPORT_INDICIES ${LOCAL_REPORT_INDEX}) + list(APPEND LOCAL_REPORT_INDICES ${LOCAL_REPORT_INDEX}) add_custom_command( OUTPUT ${LOCAL_REPORT_INDEX} @@ -263,8 +264,8 @@ function (enable_coverage_report) #+-[output]---------------------------------------------------------------+ - if (NOT ARG_OUT_REPORT_INDICIES STREQUAL "") - set(${ARG_OUT_REPORT_INDICIES} "${LOCAL_REPORT_INDICIES}" PARENT_SCOPE) + if (NOT ARG_OUT_REPORT_INDICES STREQUAL "") + set(${ARG_OUT_REPORT_INDICES} "${LOCAL_REPORT_INDICES}" PARENT_SCOPE) endif() endfunction(enable_coverage_report) diff --git a/cetlvast/suites/unittest/CMakeLists.txt b/cetlvast/suites/unittest/CMakeLists.txt index c32ccc60..3ec901c5 100644 --- a/cetlvast/suites/unittest/CMakeLists.txt +++ b/cetlvast/suites/unittest/CMakeLists.txt @@ -89,7 +89,7 @@ set_directory_properties(PROPERTIES if (CMAKE_BUILD_TYPE STREQUAL "Coverage") enable_coverage_report(COVERAGE_REPORT_FORMATS html sonarqube ROOT_DIRECTORY ${CETL_ROOT} - OUT_REPORT_INDICIES LOCAL_COVERAGE_REPORT_INDICIES + OUT_REPORT_INDICES LOCAL_COVERAGE_REPORT_INDICIES ) endif() diff --git a/cetlvast/suites/unittest/test_variable_length_array_compat.cpp b/cetlvast/suites/unittest/test_variable_length_array_compat.cpp index c09919be..246821df 100644 --- a/cetlvast/suites/unittest/test_variable_length_array_compat.cpp +++ b/cetlvast/suites/unittest/test_variable_length_array_compat.cpp @@ -137,10 +137,10 @@ TYPED_TEST(VLATestsCompatPrimitiveTypes, SelfAssignment) TYPED_TEST(VLATestsCompatPrimitiveTypes, TestAssignCountItems) { - std::allocator allocator{}; + std::allocator allocator{}; cetl::VariableLengthArray> subject{allocator}; - const TypeParam value0 = std::numeric_limits::max(); - const TypeParam value1 = std::numeric_limits::min(); + const TypeParam value0 = std::numeric_limits::max(); + const TypeParam value1 = std::numeric_limits::min(); subject.assign(16, value0); ASSERT_EQ(16, subject.size()); for (auto i = subject.begin(), e = subject.end(); i != e; ++i) @@ -157,30 +157,45 @@ TYPED_TEST(VLATestsCompatPrimitiveTypes, TestAssignCountItems) // +-------------------------------------------------------------------------------------------------------------------+ // | ANY TYPE -// | These are just the rest of the tests. All ad-hoc and simple. +// | Various type handling across implementations. // +-------------------------------------------------------------------------------------------------------------------+ +template +class VLATestsCompatAnyType : public ::testing::Test +{ +protected: + void SetUp() override + { + cetlvast::InstrumentedAllocatorStatistics::reset(); + } -TEST(VLATestsCompatAnyType, TestDeallocSizeNonBool) + template + using TestSubjectType = std::conditional_t::value, + cetl::VariableLengthArray, + std::vector>; +}; +using VLATestsCompatAnyTypeTypes = ::testing::Types; +TYPED_TEST_SUITE(VLATestsCompatAnyType, VLATestsCompatAnyTypeTypes, ); + +TYPED_TEST(VLATestsCompatAnyType, TestDeallocSizeNonBool) { - cetlvast::InstrumentedAllocatorStatistics& stats = cetlvast::InstrumentedAllocatorStatistics::get(); - cetlvast::InstrumentedNewDeleteAllocator allocator; - cetl::VariableLengthArray subject{allocator}; + cetlvast::InstrumentedAllocatorStatistics& stats = cetlvast::InstrumentedAllocatorStatistics::get(); + cetlvast::InstrumentedNewDeleteAllocator allocator; + typename TestFixture::template TestSubjectType subject{allocator}; subject.reserve(10U); ASSERT_EQ(10U, subject.capacity()); ASSERT_EQ(1U, stats.allocations); ASSERT_EQ(10U * sizeof(int), stats.last_allocation_size_bytes); ASSERT_EQ(0U, stats.last_deallocation_size_bytes); - subject.pop_back(); subject.shrink_to_fit(); ASSERT_EQ(10U * sizeof(int), stats.last_deallocation_size_bytes); } -TEST(VLATestsCompatAnyType, TestPush) +TYPED_TEST(VLATestsCompatAnyType, TestPush) { cetlvast::InstrumentedAllocatorStatistics& stats = cetlvast::InstrumentedAllocatorStatistics::get(); cetlvast::InstrumentedNewDeleteAllocator allocator; - cetl::VariableLengthArray subject{allocator}; + typename TestFixture::template TestSubjectType subject{allocator}; ASSERT_EQ(nullptr, subject.data()); ASSERT_EQ(0U, subject.size()); std::size_t x = 0; @@ -237,12 +252,11 @@ class Doomed bool moved_; }; -TEST(VLATestsCompatAnyType, TestDestroy) +TYPED_TEST(VLATestsCompatAnyType, TestDestroy) { - int dtor_called = 0; - - auto subject = - std::make_shared>>(std::allocator{}); + int dtor_called = 0; + using TestDestroySubjectType = typename TestFixture::template TestSubjectType>; + auto subject = std::make_shared(std::allocator{}); subject->reserve(10); ASSERT_EQ(10U, subject->capacity()); @@ -255,11 +269,11 @@ TEST(VLATestsCompatAnyType, TestDestroy) ASSERT_EQ(2, dtor_called); } -TEST(VLATestsCompatAnyType, TestNonFundamental) +TYPED_TEST(VLATestsCompatAnyType, TestNonFundamental) { int dtor_called = 0; - cetl::VariableLengthArray> subject(std::allocator{}); + typename TestFixture::template TestSubjectType> subject(std::allocator{}); subject.reserve(10U); ASSERT_EQ(10U, subject.capacity()); @@ -269,7 +283,11 @@ TEST(VLATestsCompatAnyType, TestNonFundamental) ASSERT_EQ(1, dtor_called); } -TEST(VLATestsCompatAnyType, TestNotMovable) +#if !defined(__clang__) +/// Clang seems to have a different opinion about this. It statically asserts that a type must be move insertable +/// to use reserve or the internal memory reallocation logic shared by many of the vector routines. GCC and CETL +/// allow degraded behaviour instead where the type is copied if it cannot be moved. +TYPED_TEST(VLATestsCompatAnyType, TestNotMovable) { class NotMovable { @@ -280,8 +298,14 @@ TEST(VLATestsCompatAnyType, TestNotMovable) { (void) rhs; } + NotMovable& operator=(const NotMovable& rhs) noexcept + { + (void) rhs; + return *this; + } }; - cetl::VariableLengthArray> subject(std::allocator{}); + typename TestFixture::template TestSubjectType> subject( + std::allocator{}); subject.reserve(10U); ASSERT_EQ(10U, subject.capacity()); @@ -289,8 +313,9 @@ TEST(VLATestsCompatAnyType, TestNotMovable) subject.push_back(source); ASSERT_EQ(1U, subject.size()); } +#endif -TEST(VLATestsCompatAnyType, TestMovable) +TYPED_TEST(VLATestsCompatAnyType, TestMovable) { class Movable { @@ -313,7 +338,7 @@ TEST(VLATestsCompatAnyType, TestMovable) private: int data_; }; - cetl::VariableLengthArray> subject(std::allocator{}); + typename TestFixture::template TestSubjectType> subject(std::allocator{}); subject.reserve(10U); ASSERT_EQ(10U, subject.capacity()); subject.push_back(Movable(1)); @@ -323,10 +348,10 @@ TEST(VLATestsCompatAnyType, TestMovable) ASSERT_EQ(1, pushed->get_data()); } -TEST(VLATestsCompatAnyType, TestInitializerArray) +TYPED_TEST(VLATestsCompatAnyType, TestInitializerArray) { - cetl::VariableLengthArray> subject{{10, 9, 8, 7, 6, 5, 4, 3, 2, 1}, - std::allocator{}}; + typename TestFixture::template TestSubjectType> + subject{{10, 9, 8, 7, 6, 5, 4, 3, 2, 1}, std::allocator{}}; ASSERT_EQ(10U, subject.size()); for (std::size_t i = 0; i < subject.size(); ++i) { @@ -334,12 +359,12 @@ TEST(VLATestsCompatAnyType, TestInitializerArray) } } -TEST(VLATestsCompatAnyType, TestCopyConstructor) +TYPED_TEST(VLATestsCompatAnyType, TestCopyConstructor) { - cetl::VariableLengthArray> fixture{{10, 9, 8, 7, 6, 5, 4, 3, 2, 1}, - std::allocator{}}; + typename TestFixture::template TestSubjectType> + fixture{{10, 9, 8, 7, 6, 5, 4, 3, 2, 1}, std::allocator{}}; - cetl::VariableLengthArray> subject(fixture); + typename TestFixture::template TestSubjectType> subject(fixture); ASSERT_EQ(10U, subject.size()); for (std::size_t i = 0; i < subject.size(); ++i) { @@ -347,12 +372,13 @@ TEST(VLATestsCompatAnyType, TestCopyConstructor) } } -TEST(VLATestsCompatAnyType, TestMoveConstructor) +TYPED_TEST(VLATestsCompatAnyType, TestMoveConstructor) { - cetl::VariableLengthArray> fixture{{10, 9, 8, 7, 6, 5, 4, 3, 2, 1}, - std::allocator{}}; + typename TestFixture::template TestSubjectType> + fixture{{10, 9, 8, 7, 6, 5, 4, 3, 2, 1}, std::allocator{}}; - cetl::VariableLengthArray> subject(std::move(fixture)); + typename TestFixture::template TestSubjectType> subject( + std::move(fixture)); ASSERT_EQ(10U, subject.size()); for (std::size_t i = 0; i < subject.size(); ++i) { @@ -362,46 +388,49 @@ TEST(VLATestsCompatAnyType, TestMoveConstructor) ASSERT_EQ(0U, fixture.capacity()); } -TEST(VLATestsCompatAnyType, TestCompare) +TYPED_TEST(VLATestsCompatAnyType, TestCompare) { - std::allocator allocator{}; - cetl::VariableLengthArray> one{{10, 9, 8, 7, 6, 5, 4, 3, 2, 1}, allocator}; - cetl::VariableLengthArray> two{{10, 9, 8, 7, 6, 5, 4, 3, 2, 1}, allocator}; - cetl::VariableLengthArray> three{{9, 8, 7, 6, 5, 4, 3, 2, 1}, allocator}; + std::allocator allocator{}; + typename TestFixture::template TestSubjectType> + one{{10, 9, 8, 7, 6, 5, 4, 3, 2, 1}, allocator}; + typename TestFixture::template TestSubjectType> + two{{10, 9, 8, 7, 6, 5, 4, 3, 2, 1}, allocator}; + typename TestFixture::template TestSubjectType> + three{{9, 8, 7, 6, 5, 4, 3, 2, 1}, allocator}; ASSERT_EQ(one, one); ASSERT_EQ(one, two); ASSERT_NE(one, three); } -TEST(VLATestsCompatAnyType, TestFPCompare) +TYPED_TEST(VLATestsCompatAnyType, TestFPCompare) { - std::allocator allocator{}; - cetl::VariableLengthArray> one{{1.00, 2.00}, allocator}; - cetl::VariableLengthArray> two{{1.00, 2.00}, allocator}; + std::allocator allocator{}; + typename TestFixture::template TestSubjectType> one{{1.00, 2.00}, allocator}; + typename TestFixture::template TestSubjectType> two{{1.00, 2.00}, allocator}; const double epsilon_for_two_comparison = std::nextafter(4.00, INFINITY) - 4.00; - cetl::VariableLengthArray> + typename TestFixture::template TestSubjectType> three{{1.00, std::nextafter(2.00 + epsilon_for_two_comparison, INFINITY)}, allocator}; ASSERT_EQ(one, one); ASSERT_EQ(one, two); ASSERT_NE(one, three); } -TEST(VLATestsCompatAnyType, TestCompareBool) +TYPED_TEST(VLATestsCompatAnyType, TestCompareBool) { - std::allocator allocator{}; - cetl::VariableLengthArray> one{{true, false, true}, allocator}; - cetl::VariableLengthArray> two{{true, false, true}, allocator}; - cetl::VariableLengthArray> three{{true, true, false}, allocator}; + std::allocator allocator{}; + typename TestFixture::template TestSubjectType> one{{true, false, true}, allocator}; + typename TestFixture::template TestSubjectType> two{{true, false, true}, allocator}; + typename TestFixture::template TestSubjectType> three{{true, true, false}, allocator}; ASSERT_EQ(one, one); ASSERT_EQ(one, two); ASSERT_NE(one, three); } -TEST(VLATestsCompatAnyType, TestCopyAssignment) +TYPED_TEST(VLATestsCompatAnyType, TestCopyAssignment) { - std::allocator allocator{}; - cetl::VariableLengthArray> lhs{{1.00}, allocator}; - cetl::VariableLengthArray> rhs{{2.00, 3.00}, allocator}; + std::allocator allocator{}; + typename TestFixture::template TestSubjectType> lhs{{1.00}, allocator}; + typename TestFixture::template TestSubjectType> rhs{{2.00, 3.00}, allocator}; ASSERT_EQ(1U, lhs.size()); ASSERT_EQ(2U, rhs.size()); ASSERT_NE(lhs, rhs); @@ -411,15 +440,16 @@ TEST(VLATestsCompatAnyType, TestCopyAssignment) ASSERT_EQ(lhs, rhs); } -TEST(VLATestsCompatAnyType, TestMoveAssignment) +TYPED_TEST(VLATestsCompatAnyType, TestMoveAssignment) { - std::allocator allocator{}; - cetl::VariableLengthArray> lhs{{std::string("one"), std::string("two")}, - allocator}; - cetl::VariableLengthArray> rhs{{std::string("three"), - std::string("four"), - std::string("five")}, - allocator}; + std::allocator allocator{}; + typename TestFixture::template TestSubjectType> lhs{{std::string("one"), + std::string("two")}, + allocator}; + typename TestFixture::template TestSubjectType> rhs{{std::string("three"), + std::string("four"), + std::string("five")}, + allocator}; ASSERT_EQ(2U, lhs.size()); ASSERT_EQ(3U, rhs.size()); ASSERT_NE(lhs, rhs); @@ -451,10 +481,11 @@ struct NoDefault int data_; }; -TEST(VLATestsCompatAnyType, TestResizeWithNoDefaultCtorData) +TYPED_TEST(VLATestsCompatAnyType, TestResizeWithNoDefaultCtorData) { - std::allocator allocator{}; - cetl::VariableLengthArray> subject{{NoDefault{1}}, allocator}; + std::allocator allocator{}; + typename TestFixture::template TestSubjectType> subject{{NoDefault{1}}, + allocator}; ASSERT_EQ(1, subject.size()); subject.resize(10, NoDefault{2}); ASSERT_EQ(10, subject.size()); @@ -490,24 +521,66 @@ struct Grenade throw GrenadeError("Kaboom!"); } } + + Grenade& operator=(const Grenade& rhs) + { + value_ = rhs.value_; + return *this; + } + + int value() const + { + return value_; + } + private: int value_; }; -TEST(VLATestsCompatAnyType, TestResizeExceptionFromCtorOnResize) +TYPED_TEST(VLATestsCompatAnyType, TestResizeExceptionFromCtorOnResize) { - std::allocator allocator{}; - cetl::VariableLengthArray> subject{{Grenade{1}}, allocator}; + std::allocator allocator{}; + typename TestFixture::template TestSubjectType> subject{{Grenade{1}}, allocator}; ASSERT_EQ(1, subject.size()); ASSERT_THROW(subject.resize(2, Grenade{2}), GrenadeError); } +TYPED_TEST(VLATestsCompatAnyType, TestAt) +{ + // Grenade shouldn't explode if we are just accessing a reference value using at() + std::allocator allocator{}; + typename TestFixture::template TestSubjectType> subject{{Grenade{1}}, allocator}; + ASSERT_EQ(1, subject.size()); + ASSERT_EQ(1, subject.at(0).value()); + ASSERT_EQ(1, reinterpret_cast(&subject)->at(0).value()); +} + +TYPED_TEST(VLATestsCompatAnyType, TestAtThrows) +{ + // Grenade shouldn't explode if we are just accessing a reference value using at() + std::allocator allocator{}; + typename TestFixture::template TestSubjectType> subject{{5}, allocator}; + ASSERT_EQ(1, subject.size()); + ASSERT_THROW(subject.at(1), std::out_of_range); + ASSERT_THROW(subject.at(2), std::out_of_range); +} + +TYPED_TEST(VLATestsCompatAnyType, TestConstAtThrows) +{ + // Grenade shouldn't explode if we are just accessing a reference value using at() + std::allocator allocator{}; + typename TestFixture::template TestSubjectType> subject{{2}, allocator}; + ASSERT_EQ(1, subject.size()); + ASSERT_THROW(reinterpret_cast(&subject)->at(1), std::out_of_range); + ASSERT_THROW(reinterpret_cast(&subject)->at(2), std::out_of_range); +} + #endif // __cpp_exceptions -TEST(VLATestsCompatAnyType, TestAssignCountItems) +TYPED_TEST(VLATestsCompatAnyType, TestAssignCountItems) { - std::allocator allocator{}; - cetl::VariableLengthArray> subject{allocator}; + std::allocator allocator{}; + typename TestFixture::template TestSubjectType> subject{allocator}; subject.assign(25, "Hi müm"); ASSERT_EQ(25, subject.size()); for (auto i = subject.begin(), e = subject.end(); i != e; ++i) diff --git a/cetlvast/suites/unittest/test_variable_length_array_debug_asserts.cpp b/cetlvast/suites/unittest/test_variable_length_array_debug_asserts.cpp index 1f48368f..0d0c191a 100644 --- a/cetlvast/suites/unittest/test_variable_length_array_debug_asserts.cpp +++ b/cetlvast/suites/unittest/test_variable_length_array_debug_asserts.cpp @@ -112,7 +112,7 @@ static void TestConstFrontOnEmpty() { flush_coverage_on_death(); const cetl::VariableLengthArray > vla{std::allocator()}; - (void)vla.front(); + (void)reinterpret_cast(&vla)->front(); } TEST(DeathTestVLAAssertions, TestConstFrontOnEmpty) @@ -144,7 +144,7 @@ static void TestConstFrontOnEmptyBool() { flush_coverage_on_death(); const cetl::VariableLengthArray > vla{std::allocator()}; - (void)vla.front(); + (void)reinterpret_cast(&vla)->front(); } TEST(DeathTestVLAAssertions, TestConstFrontOnEmptyBool) @@ -176,7 +176,7 @@ static void TestConstBackOnEmpty() { flush_coverage_on_death(); const cetl::VariableLengthArray > vla{std::allocator()}; - (void)vla.back(); + (void)reinterpret_cast(&vla)->back(); } TEST(DeathTestVLAAssertions, TestConstBackOnEmpty) @@ -208,7 +208,7 @@ static void TestConstBackOnEmptyBool() { flush_coverage_on_death(); const cetl::VariableLengthArray > vla{std::allocator()}; - (void)vla.back(); + (void)reinterpret_cast(&vla)->back(); } TEST(DeathTestVLAAssertions, TestConstBackOnEmptyBool) diff --git a/include/cetl/variable_length_array.hpp b/include/cetl/variable_length_array.hpp index f0bf21a3..52825c2a 100644 --- a/include/cetl/variable_length_array.hpp +++ b/include/cetl/variable_length_array.hpp @@ -932,7 +932,7 @@ class VariableLengthArray : protected VariableLengthArrayBase /// /// STL-like declaration of constant-reference type. /// - using const_reference = typename std::add_const_t>; + using const_reference = typename std::add_lvalue_reference_t>; // +----------------------------------------------------------------------+ // | CONSTRUCTORS @@ -1215,7 +1215,7 @@ class VariableLengthArray : protected VariableLengthArrayBase { throw std::out_of_range("at position argument is outside of container size."); } - return this->operator[][pos]; + return this->operator[](pos); } /// Returns a const reference to the element at specified location pos, with bounds checking. @@ -1232,7 +1232,7 @@ class VariableLengthArray : protected VariableLengthArrayBase { throw std::out_of_range("at position argument is outside of container size."); } - return this->operator[][pos]; + return this->operator[](pos); } #endif