diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 5aa0a99..d11f711 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -15,6 +15,7 @@ env: DEFAULT_CXX_STANDARD: 20 DEFAULT_LLVM_VERSION: 18 DEFAULT_GCC_VERSION: 13 + MULL_LLVM_VERSION: 15 concurrency: group: ${{ github.head_ref || github.run_id }} @@ -348,6 +349,52 @@ jobs: echo "" >> $GITHUB_STEP_SUMMARY test $FAILSIZE = "0" + mutate: + runs-on: ${{ github.repository_owner == 'intel' && 'intel-' || '' }}ubuntu-22.04 + steps: + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + + - name: Install build tools + run: | + wget https://apt.llvm.org/llvm.sh && chmod +x llvm.sh && sudo ./llvm.sh ${{env.MULL_LLVM_VERSION}} + sudo apt install -y ninja-build + + - name: Install mull + env: + MULL_VERSION: 0.23.0 + run: | + wget https://github.com/mull-project/mull/releases/download/${{env.MULL_VERSION}}/Mull-${{env.MULL_LLVM_VERSION}}-${{env.MULL_VERSION}}-LLVM-${{env.MULL_LLVM_VERSION}}.0-ubuntu-22.04.deb + sudo dpkg -i Mull-${{env.MULL_LLVM_VERSION}}-${{env.MULL_VERSION}}-LLVM-${{env.MULL_LLVM_VERSION}}.0-ubuntu-22.04.deb + + - name: Restore CPM cache + env: + cache-name: cpm-cache-0 + id: cpm-cache-restore + uses: actions/cache/restore@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 + with: + path: ~/cpm-cache + key: ${{runner.os}}-${{env.cache-name}}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }} + restore-keys: | + ${{runner.os}}-${{env.cache-name}}- + + - name: Configure CMake + env: + CC: "/usr/lib/llvm-${{env.MULL_LLVM_VERSION}}/bin/clang" + CXX: "/usr/lib/llvm-${{env.MULL_LLVM_VERSION}}/bin/clang++" + run: cmake -B ${{github.workspace}}/build -DCMAKE_CXX_STANDARD=${{env.DEFAULT_CXX_STANDARD}} -DCPM_SOURCE_CACHE=~/cpm-cache + + - name: Save CPM cache + env: + cache-name: cpm-cache-0 + if: steps.cpm-cache-restore.outputs.cache-hit != 'true' + uses: actions/cache/save@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 + with: + path: ~/cpm-cache + key: ${{runner.os}}-${{env.cache-name}}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }} + + - name: Build and run mull tests + run: cmake --build build -t mull_tests + merge_ok: runs-on: ${{ github.repository_owner == 'intel' && 'intel-' || '' }}ubuntu-22.04 needs: [build_and_test, quality_checks_pass, sanitize, valgrind] diff --git a/mull.yml b/mull.yml new file mode 100644 index 0000000..8471aa0 --- /dev/null +++ b/mull.yml @@ -0,0 +1,7 @@ +mutators: + - cxx_all +ignoreMutators: + - cxx_remove_void_call +excludePaths: + - .*/include/stdx/.* +timeout: 10000 diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4fdddea..e57a339 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,5 +1,8 @@ function(add_tests) - foreach(name ${ARGN}) + set(multiValueArgs FILES MULL_EXCLUSIONS) + cmake_parse_arguments(TEST "" "" "${multiValueArgs}" ${ARGN}) + + foreach(name ${TEST_FILES}) add_unit_test( "${name}_test" CATCH2 @@ -8,10 +11,14 @@ function(add_tests) LIBRARIES warnings stdx) + if(NOT name IN_LIST TEST_MULL_EXCLUSIONS) + add_mull_test("${name}_test" EXCLUDE_CTEST) + endif() endforeach() endfunction() add_tests( + FILES algorithm always_false bind @@ -53,7 +60,7 @@ add_tests( udls) if(${CMAKE_CXX_STANDARD} GREATER_EQUAL 20) - add_tests(ct_format ct_string indexed_tuple tuple tuple_algorithms) + add_tests(FILES ct_format ct_string indexed_tuple tuple tuple_algorithms) endif() add_subdirectory(fail) diff --git a/test/ct_format.cpp b/test/ct_format.cpp index af7a183..c1257aa 100644 --- a/test/ct_format.cpp +++ b/test/ct_format.cpp @@ -67,20 +67,20 @@ TEST_CASE("format a compile-time enum argument", "[ct_format]") { } TEST_CASE("format a runtime argument", "[ct_format]") { - auto x = 42; + auto x = 17; CHECK(stdx::ct_format<"Hello {}">(x) == - stdx::format_result{"Hello {}"_cts, stdx::make_tuple(42)}); - static_assert(stdx::ct_format<"Hello {}">(42) == - stdx::format_result{"Hello {}"_cts, stdx::make_tuple(42)}); + stdx::format_result{"Hello {}"_cts, stdx::make_tuple(17)}); + static_assert(stdx::ct_format<"Hello {}">(17) == + stdx::format_result{"Hello {}"_cts, stdx::make_tuple(17)}); } TEST_CASE("format a compile-time and a runtime argument (1)", "[ct_format]") { - auto x = 42; + auto x = 17; CHECK(stdx::ct_format<"Hello {} {}">(CX_VALUE(int), x) == - stdx::format_result{"Hello int {}"_cts, stdx::make_tuple(42)}); + stdx::format_result{"Hello int {}"_cts, stdx::make_tuple(17)}); static_assert( - stdx::ct_format<"Hello {} {}">(CX_VALUE(int), 42) == - stdx::format_result{"Hello int {}"_cts, stdx::make_tuple(42)}); + stdx::ct_format<"Hello {} {}">(CX_VALUE(int), 17) == + stdx::format_result{"Hello int {}"_cts, stdx::make_tuple(17)}); } TEST_CASE("format a compile-time and a runtime argument (2)", "[ct_format]") { @@ -141,13 +141,13 @@ TEST_CASE("format_to a different type", "[ct_format]") { static_assert(stdx::ct_format<"{}", string_constant>(CX_VALUE("A"sv)) == string_constant{}); - auto x = 42; + auto x = 17; CHECK(stdx::ct_format<"{}", string_constant>(x) == stdx::format_result{string_constant{}, - stdx::make_tuple(42)}); - static_assert(stdx::ct_format<"{}", string_constant>(42) == + stdx::make_tuple(17)}); + static_assert(stdx::ct_format<"{}", string_constant>(17) == stdx::format_result{string_constant{}, - stdx::make_tuple(42)}); + stdx::make_tuple(17)}); } TEST_CASE("format a string-type argument", "[ct_format]") { diff --git a/test/cx_map.cpp b/test/cx_map.cpp index ed422ad..c6822ea 100644 --- a/test/cx_map.cpp +++ b/test/cx_map.cpp @@ -44,8 +44,10 @@ TEST_CASE("const get", "[cx_map]") { TEST_CASE("update existing key", "[cx_map]") { auto t = stdx::cx_map{}; t.put(13, 500); - t.put(13, 700); + REQUIRE(t.contains(13)); + CHECK(t.get(13) == 500); + t.put(13, 700); REQUIRE(t.contains(13)); CHECK(t.get(13) == 700); } diff --git a/test/cx_multimap.cpp b/test/cx_multimap.cpp index 3265e0c..7f21265 100644 --- a/test/cx_multimap.cpp +++ b/test/cx_multimap.cpp @@ -50,13 +50,25 @@ TEST_CASE("erase values", "[cx_multimap]") { t.put(60, 1); t.put(60, 2); - t.put(60, 3); + t.put(61, 3); + t.put(62, 4); + CHECK(t.size() == 3); - t.erase(60, 2); - CHECK(t.size() == 1); + CHECK(t.erase(60, 1)); + CHECK(t.size() == 3); + CHECK(not t.contains(60, 1)); + CHECK(t.contains(60, 2)); + + CHECK(t.erase(60, 2)); + CHECK(t.size() == 2); CHECK(not t.contains(60, 2)); + CHECK(t.contains(61, 3)); + CHECK(t.contains(62, 4)); - t.erase(60); + t.erase(61); + CHECK(t.size() == 1); + CHECK(t.contains(62, 4)); + t.erase(62); CHECK(t.empty()); } diff --git a/test/cx_set.cpp b/test/cx_set.cpp index 713de49..864758e 100644 --- a/test/cx_set.cpp +++ b/test/cx_set.cpp @@ -45,8 +45,8 @@ TEST_CASE("insert multiple", "[cx_set]") { stdx::cx_set t; t.insert(10); - t.insert(10); - + CHECK(t.size() == 1); + CHECK(not t.insert(10)); CHECK(t.size() == 1); CHECK(not t.empty()); CHECK(t.contains(10)); @@ -56,7 +56,7 @@ TEST_CASE("erase all", "[cx_set]") { stdx::cx_set t; t.insert(10); - t.erase(10); + CHECK(t.erase(10) == 1); CHECK(t.size() == 0); CHECK(t.empty()); @@ -67,6 +67,7 @@ TEST_CASE("erase some", "[cx_set]") { stdx::cx_set t; t.insert(10); + CHECK(t.contains(10)); t.insert(11); t.erase(10); diff --git a/test/cx_vector.cpp b/test/cx_vector.cpp index b513bf2..2e57235 100644 --- a/test/cx_vector.cpp +++ b/test/cx_vector.cpp @@ -172,9 +172,9 @@ TEST_CASE("resize_and_overwrite", "[cx_vector]") { stdx::cx_vector v{1, 2, 3, 4, 5}; resize_and_overwrite(v, [](int *dest, std::size_t max_size) { CHECK(max_size == 5); - *dest = 42; + *dest = 17; return 1u; }); REQUIRE(v.size() == 1u); - CHECK(v[0] == 42); + CHECK(v[0] == 17); } diff --git a/test/function_traits.cpp b/test/function_traits.cpp index 1e42b42..b0eba59 100644 --- a/test/function_traits.cpp +++ b/test/function_traits.cpp @@ -170,28 +170,29 @@ TEST_CASE("generic lambda arity", "[function_traits]") { } namespace { -bool called_1{}; -bool called_2{}; +int called_1{}; +int called_2{}; template constexpr auto call_f(F f, stdx::priority_t<1>) -> stdx::return_t { - called_1 = true; + ++called_1; return f(); } template constexpr auto call_f(F f, stdx::priority_t<0>) -> void { - called_2 = true; + ++called_2; f(0); } } // namespace TEST_CASE("SFINAE friendly", "[function_traits]") { - called_1 = false; - called_2 = false; + called_1 = 0; + called_2 = 0; auto f1 = []() -> int { return 1; }; auto f2 = [](auto) -> void {}; call_f(f1, stdx::priority<1>); - CHECK(called_1); + CHECK(called_1 == 1); + CHECK(called_2 == 0); call_f(f2, stdx::priority<1>); - CHECK(called_2); + CHECK(called_2 == 1); } diff --git a/test/intrusive_forward_list.cpp b/test/intrusive_forward_list.cpp index 8288f8e..20a6807 100644 --- a/test/intrusive_forward_list.cpp +++ b/test/intrusive_forward_list.cpp @@ -240,9 +240,9 @@ TEST_CASE("checked operation clears pointers on clear", "[intrusive_list]") { namespace { #if __cplusplus >= 202002L -bool compile_time_call{}; +int compile_time_calls{}; #else -bool runtime_call{}; +int runtime_calls{}; #endif struct injected_handler { @@ -250,13 +250,13 @@ struct injected_handler { template static auto panic(Ts &&...) noexcept -> void { static_assert(std::string_view{Why} == "bad list node!"); - compile_time_call = true; + ++compile_time_calls; } #else template static auto panic(Why why, Ts &&...) noexcept -> void { CHECK(std::string_view{why} == "bad list node!"); - runtime_call = true; + ++runtime_calls; } #endif }; @@ -270,15 +270,15 @@ TEST_CASE("checked panic when pushing populated node", "[intrusive_list]") { int_node n{5}; n.next = &n; - compile_time_call = false; + compile_time_calls = 0; list.push_back(&n); - CHECK(compile_time_call); + CHECK(compile_time_calls == 1); list.pop_front(); n.next = &n; - compile_time_call = false; + compile_time_calls = 0; list.push_front(&n); - CHECK(compile_time_call); + CHECK(compile_time_calls == 1); } #else TEST_CASE("checked panic when pushing populated node", "[intrusive_list]") { @@ -286,15 +286,15 @@ TEST_CASE("checked panic when pushing populated node", "[intrusive_list]") { int_node n{5}; n.next = &n; - runtime_call = false; + runtime_calls = 0; list.push_back(&n); - CHECK(runtime_call); + CHECK(runtime_calls == 1); list.pop_front(); n.next = &n; - runtime_call = false; + runtime_calls = 0; list.push_front(&n); - CHECK(runtime_call); + CHECK(runtime_calls == 1); } #endif diff --git a/test/intrusive_list.cpp b/test/intrusive_list.cpp index 1db46ff..c19a8ba 100644 --- a/test/intrusive_list.cpp +++ b/test/intrusive_list.cpp @@ -134,9 +134,9 @@ TEST_CASE("remove only node", "[intrusive_list]") { int_node n1{1}; list.push_back(&n1); + CHECK(not list.empty()); list.remove(&n1); - CHECK(list.empty()); } @@ -241,7 +241,7 @@ TEST_CASE("iterator equality", "[intrusive_list]") { int_node n1{1}; list.push_back(&n1); - + CHECK(not list.empty()); CHECK(std::begin(list) == std::begin(list)); } @@ -278,6 +278,7 @@ TEST_CASE("clear", "[intrusive_list]") { int_node n2{2}; list.push_back(&n1); + CHECK(not list.empty()); list.push_back(&n2); list.clear(); @@ -352,9 +353,9 @@ TEST_CASE("checked operation clears pointers on clear", "[intrusive_list]") { namespace { #if __cplusplus >= 202002L -bool compile_time_call{}; +int compile_time_calls{}; #else -bool runtime_call{}; +int runtime_calls{}; #endif struct injected_handler { @@ -362,13 +363,13 @@ struct injected_handler { template static auto panic(Ts &&...) noexcept -> void { static_assert(std::string_view{Why} == "bad list node!"); - compile_time_call = true; + ++compile_time_calls; } #else template static auto panic(Why why, Ts &&...) noexcept -> void { CHECK(std::string_view{why} == "bad list node!"); - runtime_call = true; + ++runtime_calls; } #endif }; @@ -382,15 +383,15 @@ TEST_CASE("checked panic when pushing populated node", "[intrusive_list]") { int_node n{5}; n.prev = &n; - compile_time_call = false; + compile_time_calls = 0; list.push_back(&n); - CHECK(compile_time_call); + CHECK(compile_time_calls == 1); list.pop_back(); n.prev = &n; - compile_time_call = false; + compile_time_calls = 0; list.push_front(&n); - CHECK(compile_time_call); + CHECK(compile_time_calls == 1); } #else TEST_CASE("checked panic when pushing populated node", "[intrusive_list]") { @@ -398,15 +399,15 @@ TEST_CASE("checked panic when pushing populated node", "[intrusive_list]") { int_node n{5}; n.prev = &n; - runtime_call = false; + runtime_calls = 0; list.push_back(&n); - CHECK(runtime_call); + CHECK(runtime_calls == 1); list.pop_back(); n.prev = &n; - runtime_call = false; + runtime_calls = 0; list.push_front(&n); - CHECK(runtime_call); + CHECK(runtime_calls == 1); } #endif @@ -492,17 +493,19 @@ TEST_CASE("insert in middle", "[intrusive_list]") { TEST_CASE("insert use case", "[intrusive_list]") { stdx::intrusive_list list{}; int_node n1{1}; - int_node n3{3}; + int_node n2{2}; list.push_back(&n1); - list.push_back(&n3); + list.push_back(&n2); - int_node n2{2}; + int_node n{2}; { auto it = std::find_if(std::begin(list), std::end(list), - [&](auto &node) { return node.value >= n2.value; }); - list.insert(it, &n2); + [&](auto &node) { return n.value <= node.value; }); + list.insert(it, &n); + CHECK(n2.prev == &n); + CHECK(n.prev == &n1); } auto it = std::cbegin(list); @@ -510,7 +513,7 @@ TEST_CASE("insert use case", "[intrusive_list]") { ++it; CHECK(it->value == 2); ++it; - CHECK(it->value == 3); + CHECK(it->value == 2); ++it; CHECK(it == std::cend(list)); } diff --git a/test/intrusive_list_properties.cpp b/test/intrusive_list_properties.cpp index 2520206..6604b7d 100644 --- a/test/intrusive_list_properties.cpp +++ b/test/intrusive_list_properties.cpp @@ -42,7 +42,7 @@ template struct IntrusiveListCommands { static void rc_assert_equal(ListModel const &m, ListSut const &sut) { RC_ASSERT(m.empty() == sut.list.empty()); - if (!m.empty() && !sut.list.empty()) { + if (!m.empty()) { RC_ASSERT(m.front() == sut.list.front().value); RC_ASSERT(m.back() == sut.list.back().value); } diff --git a/test/numeric.cpp b/test/numeric.cpp index e26c7e3..c20e259 100644 --- a/test/numeric.cpp +++ b/test/numeric.cpp @@ -40,13 +40,14 @@ TEST_CASE("n-ary transform_reduce_n", "[numeric]") { CHECK(v == 40); } -TEST_CASE("transform_reduce with output fixed by template arumgent", +TEST_CASE("transform_reduce with output fixed by template argument", "[numeric]") { auto const input = std::array{1.5, 2.5, 3.5, 4.5}; - auto const v = stdx::transform_reduce( - std::cbegin(input), std::cend(input), 0, std::plus{}, - [](auto n) { return n * 2; }); - CHECK(v == 24); + auto v = + stdx::transform_reduce(std::cbegin(input), std::cend(input), 0, + std::plus{}, [](auto n) { return n; }); + static_assert(std::is_same_v); + CHECK(v == 12); } TEST_CASE("saturate_cast cppreference example", "[numeric]") { diff --git a/test/optional.cpp b/test/optional.cpp index ae1ca99..0c406f9 100644 --- a/test/optional.cpp +++ b/test/optional.cpp @@ -95,6 +95,16 @@ TEST_CASE("in-place construction (no args)", "[optional]") { static_assert(o); } +TEST_CASE("retrieve value", "[optional]") { + auto o = stdx::optional{E::VALUE}; + CHECK(o.value() == E::VALUE); +} + +TEST_CASE("operator*", "[optional]") { + auto o = stdx::optional{E::VALUE}; + CHECK(*o == E::VALUE); +} + TEST_CASE("assignment from nullopt", "[optional]") { auto o = stdx::optional{E::VALUE}; o = std::nullopt; @@ -104,7 +114,8 @@ TEST_CASE("assignment from nullopt", "[optional]") { TEST_CASE("assignment from value", "[optional]") { auto o = stdx::optional{}; o = E::VALUE; - CHECK(o); + REQUIRE(o); + CHECK(*o == E::VALUE); } TEST_CASE("trivially copy assignable", "[optional]") { @@ -123,16 +134,6 @@ TEST_CASE("has_value and boolean conversion", "[optional]") { static_assert(o); } -TEST_CASE("retrieve value", "[optional]") { - auto o = stdx::optional{E::VALUE}; - CHECK(o.value() == E::VALUE); -} - -TEST_CASE("operator*", "[optional]") { - auto o = stdx::optional{E::VALUE}; - CHECK(*o == E::VALUE); -} - TEST_CASE("operator->", "[optional]") { auto o = stdx::optional{42}; CHECK(o->value == 42); @@ -181,6 +182,7 @@ TEST_CASE("less than - both engaged", "[optional]") { auto o1 = stdx::optional{17}; auto o2 = stdx::optional{42}; CHECK(o1 < o2); + CHECK(not(o1 < o1)); } TEST_CASE("less than - neither engaged", "[optional]") { @@ -397,6 +399,7 @@ TEST_CASE("optional pointer value has default sentinel", "[optional]") { CHECK(*o1 == nullptr); float f{1.0f}; auto const o2 = stdx::optional{&f}; + CHECK(**o2 == 1.0f); CHECK(o1 < o2); } diff --git a/test/panic.cpp b/test/panic.cpp index 00e23a4..cde3178 100644 --- a/test/panic.cpp +++ b/test/panic.cpp @@ -7,23 +7,23 @@ #include namespace { -bool runtime_call{}; +int runtime_calls{}; #if __cplusplus >= 202002L -bool compile_time_call{}; +int compile_time_calls{}; #endif struct injected_handler { template static auto panic(Why why, Ts &&...) noexcept -> void { CHECK(std::string_view{why} == "uh-oh"); - runtime_call = true; + ++runtime_calls; } #if __cplusplus >= 202002L template static auto panic(Ts &&...) noexcept -> void { static_assert(std::string_view{Why} == "uh-oh"); - compile_time_call = true; + ++compile_time_calls; } #endif }; @@ -32,28 +32,28 @@ struct injected_handler { template <> inline auto stdx::panic_handler<> = injected_handler{}; TEST_CASE("panic called with runtime arguments", "[panic]") { - runtime_call = false; + runtime_calls = 0; stdx::panic("uh-oh"); - CHECK(runtime_call); + CHECK(runtime_calls == 1); } #if __cplusplus >= 202002L TEST_CASE("panic called with compile-time strings", "[panic]") { - compile_time_call = false; + compile_time_calls = 0; using namespace stdx::ct_string_literals; stdx::panic<"uh-oh"_cts>(); - CHECK(compile_time_call); + CHECK(compile_time_calls == 1); } TEST_CASE("compile-time panic called through macro", "[panic]") { - compile_time_call = false; + compile_time_calls = 0; STDX_PANIC("uh-oh"); - CHECK(compile_time_call); + CHECK(compile_time_calls == 1); } #else TEST_CASE("runtime panic called through macro", "[panic]") { - runtime_call = false; + runtime_calls = 0; STDX_PANIC("uh-oh"); - CHECK(runtime_call); + CHECK(runtime_calls == 1); } #endif diff --git a/test/tuple.cpp b/test/tuple.cpp index ab5af0f..a026395 100644 --- a/test/tuple.cpp +++ b/test/tuple.cpp @@ -28,8 +28,7 @@ TEST_CASE("single element tuple", "[tuple]") { static_assert(T::size() == 1); static_assert(sizeof(T) == sizeof(int)); - auto x = 1; - auto u = stdx::tuple{x}; + auto u = stdx::tuple{1}; using U = decltype(u); static_assert(std::is_same_v>); static_assert(stdx::tuple_size_v == 1); @@ -141,9 +140,9 @@ TEST_CASE("tuple of lvalue references", "[tuple]") { TEST_CASE("tuple of lambdas", "[tuple]") { auto x = 1; - auto t = stdx::make_tuple([&] { x = 2; }, [&] { x = 3; }); + auto t = stdx::make_tuple([&] { x += 2; }, [&] { x += 3; }); get<0>(t)(); - CHECK(x == 2); + CHECK(x == 3); } TEST_CASE("tuple size/elements", "[tuple]") { @@ -335,12 +334,12 @@ TEST_CASE("order comparable", "[tuple]") { } TEST_CASE("order comparable (references and non-references)", "[tuple]") { - int x{6}; - int y{5}; + int x{5}; + int y{6}; auto const t = stdx::tuple{x, y}; - auto const u = stdx::tuple{6, 4}; - CHECK(t > u); - CHECK(u < t); + auto const u = stdx::tuple{5, 7}; + CHECK(t < u); + CHECK(u > t); } TEST_CASE("spaceship comparable", "[tuple]") { @@ -378,18 +377,17 @@ TEST_CASE("free get is SFINAE-friendly", "[tuple]") { } TEST_CASE("copy/move behavior for tuple", "[tuple]") { - counter::reset(); auto t1 = stdx::tuple{counter{}}; - auto const orig_moves = counter::moves; - auto const orig_copies = counter::copies; + counter::reset(); [[maybe_unused]] auto t2 = t1; - CHECK(counter::moves == orig_moves); - CHECK(counter::copies == orig_copies + 1); + CHECK(counter::moves == 0); + CHECK(counter::copies == 1); + counter::reset(); [[maybe_unused]] auto t3 = std::move(t1); - CHECK(counter::moves == orig_moves + 1); - CHECK(counter::copies == orig_copies + 1); + CHECK(counter::moves == 1); + CHECK(counter::copies == 0); } TEST_CASE("make_tuple", "[tuple]") { @@ -426,11 +424,13 @@ TEST_CASE("tuple type-based concat", "[tuple]") { } TEST_CASE("forward_as_tuple", "[tuple]") { - auto const x = 42; - auto y = 42; - auto t = stdx::forward_as_tuple(x, y, 42); + auto const x = 17; + auto y = 17; + auto z = 17; + auto t = stdx::forward_as_tuple(x, y, std::move(z)); static_assert( std::is_same_v>); + CHECK(t == stdx::tuple{17, 17, 17}); } TEST_CASE("one_of", "[tuple]") { diff --git a/test/tuple_algorithms.cpp b/test/tuple_algorithms.cpp index 614445f..b229e70 100644 --- a/test/tuple_algorithms.cpp +++ b/test/tuple_algorithms.cpp @@ -51,6 +51,8 @@ TEST_CASE("transform preserves references", "[tuple_algorithms]") { }, stdx::tuple{1}); CHECK(std::addressof(value) == std::addressof(u[stdx::index<0>])); + CHECK(u == stdx::tuple{2}); + CHECK(value == 2); } namespace { @@ -187,6 +189,9 @@ TEST_CASE("tuple_cat (references)", "[tuple_algorithms]") { auto x = 1; auto t = stdx::tuple_cat(stdx::tuple{x}, stdx::tuple{x}); static_assert(std::is_same_v>); + CHECK(std::addressof(x) == std::addressof(t[stdx::index<0>])); + CHECK(std::addressof(x) == std::addressof(t[stdx::index<1>])); + CHECK(x == 1); stdx::get<0>(t) = 2; CHECK(x == 2); stdx::get<1>(t) = 1; @@ -199,6 +204,9 @@ TEST_CASE("tuple_cat (const references)", "[tuple_algorithms]") { stdx::tuple{x}); static_assert( std::is_same_v>); + CHECK(std::addressof(x) == std::addressof(t[stdx::index<0>])); + CHECK(std::addressof(x) == std::addressof(t[stdx::index<1>])); + CHECK(x == 1); x = 2; CHECK(stdx::get<0>(t) == 2); CHECK(stdx::get<1>(t) == 2); @@ -206,10 +214,15 @@ TEST_CASE("tuple_cat (const references)", "[tuple_algorithms]") { TEST_CASE("tuple_cat (rvalue references)", "[tuple_algorithms]") { auto x = 1; - auto y = 2; + auto y = 1; auto t = stdx::tuple_cat(stdx::tuple{std::move(x)}, stdx::tuple{std::move(y)}); static_assert(std::is_same_v>); + CHECK(std::addressof(x) == std::addressof(t[stdx::index<0>])); + CHECK(std::addressof(y) == std::addressof(t[stdx::index<1>])); + CHECK(x == 1); + CHECK(y == 1); + x = 2; CHECK(stdx::get<0>(t) == 2); y = 2; @@ -558,9 +571,11 @@ TEST_CASE("unique preserves references", "[tuple_algorithms]") { int y{2}; auto t = stdx::forward_as_tuple(x, y); static_assert(std::is_same_v>); + CHECK(t == stdx::tuple{1, 2}); auto u = stdx::unique(t); static_assert(std::is_same_v>); CHECK(u == stdx::tuple{1}); + CHECK(std::addressof(x) == std::addressof(u[stdx::index<0>])); } TEST_CASE("unique with move only types", "[tuple_algorithms]") { @@ -597,9 +612,12 @@ TEST_CASE("to_sorted_set preserves references", "[tuple_algorithms]") { static_assert( std::is_same_v>); + CHECK(t == stdx::tuple{1, 2, 3.0, 4.0}); auto u = stdx::to_sorted_set(t); static_assert(std::is_same_v>); CHECK(u == stdx::tuple{3.0, 1}); + CHECK(std::addressof(a) == std::addressof(u[stdx::index<0>])); + CHECK(std::addressof(x) == std::addressof(u[stdx::index<1>])); } TEST_CASE("to_unsorted_set", "[tuple_algorithms]") { @@ -619,9 +637,12 @@ TEST_CASE("to_unsorted_set preserves references", "[tuple_algorithms]") { static_assert( std::is_same_v>); + CHECK(t == stdx::tuple{1, 2, 3.0, 4.0}); auto u = stdx::to_unsorted_set(t); static_assert(std::is_same_v>); CHECK(u == stdx::tuple{1, 3.0}); + CHECK(std::addressof(x) == std::addressof(u[stdx::index<0>])); + CHECK(std::addressof(a) == std::addressof(u[stdx::index<1>])); } TEST_CASE("to_unsorted_set with move only types", "[tuple_algorithms]") { @@ -740,6 +761,8 @@ TEST_CASE("tuple_cons (references)", "[tuple_algorithms]") { auto x = 1; auto t = stdx::tuple_cons(1, stdx::tuple{x}); static_assert(std::is_same_v>); + CHECK(t == stdx::tuple{1, 1}); + CHECK(std::addressof(x) == std::addressof(t[stdx::index<1>])); } TEST_CASE("tuple_snoc", "[tuple_algorithms]") { @@ -759,6 +782,8 @@ TEST_CASE("tuple_snoc (references)", "[tuple_algorithms]") { auto x = 1; auto t = stdx::tuple_snoc(stdx::tuple{x}, 1); static_assert(std::is_same_v>); + CHECK(t == stdx::tuple{1, 1}); + CHECK(std::addressof(x) == std::addressof(t[stdx::index<0>])); } TEST_CASE("tuple_push_front", "[tuple_algorithms]") { diff --git a/test/type_traits.cpp b/test/type_traits.cpp index e2b9e11..d94742e 100644 --- a/test/type_traits.cpp +++ b/test/type_traits.cpp @@ -96,6 +96,9 @@ TEST_CASE("type_or_t", "[type_traits]") { namespace { int value{}; struct add_value { + add_value() = default; + add_value(int init) { value = init; } + template constexpr auto operator()() const -> void { value += T::value; } @@ -121,30 +124,26 @@ TEST_CASE("template_for_each with type list", "[type_traits]") { } TEST_CASE("template_for_each with empty type list", "[type_traits]") { - value = 42; using L = stdx::type_list<>; - stdx::template_for_each(add_value{}); - CHECK(value == 42); + stdx::template_for_each(add_value{17}); + CHECK(value == 17); } TEST_CASE("template_for_each with value list", "[type_traits]") { - value = 0; using L = stdx::value_list<1, 2>; - stdx::template_for_each(add_value{}); + stdx::template_for_each(add_value{0}); CHECK(value == 3); } TEST_CASE("template_for_each with empty value list", "[type_traits]") { - value = 17; using L = stdx::value_list<>; - stdx::template_for_each(add_value{}); + stdx::template_for_each(add_value{17}); CHECK(value == 17); } TEST_CASE("template_for_each with index sequence", "[type_traits]") { - value = 0; using L = std::make_index_sequence<3>; - stdx::template_for_each(add_value{}); + stdx::template_for_each(add_value{0}); CHECK(value == 3); } diff --git a/test/with_result_of.cpp b/test/with_result_of.cpp index 66f9620..34c3778 100644 --- a/test/with_result_of.cpp +++ b/test/with_result_of.cpp @@ -26,9 +26,9 @@ TEST_CASE("implicit conversion (rvalue)", "[with_result_of]") { } TEST_CASE("capturing lambda", "[with_result_of]") { - auto value = 42; + auto value = 17; auto const result = [](int n) { - return n; + return n + 42; }(stdx::with_result_of{[&]() { return value; }}); - CHECK(result == value); + CHECK(result == 59); }