diff --git a/Makefile b/Makefile index 73edb673..f27b162a 100755 --- a/Makefile +++ b/Makefile @@ -167,11 +167,15 @@ lint-manual: ## Run all manual tools in pre-commit .PHONY: coverage coverage: ## Build and run the tests with the GCOV profile and process the results -coverage: venv +coverage: venv $(_build_path)/CMakeCache.txt $(ACTIVATE) cmake --build $(_build_path) --config Gcov $(ACTIVATE) ctest --build-config Gcov --output-on-failure --test-dir $(_build_path) $(ACTIVATE) cmake --build $(_build_path) --config Gcov --target process_coverage +.PHONY: view-coverage +view-coverage: ## View the coverage report + sensible-browser $(_build_path)/coverage/coverage.html + # Help target .PHONY: help help: ## Show this help. diff --git a/etc/clang-20-toolchain.cmake b/etc/clang-20-toolchain.cmake index b41013da..d2be17fa 100644 --- a/etc/clang-20-toolchain.cmake +++ b/etc/clang-20-toolchain.cmake @@ -7,5 +7,6 @@ include_guard(GLOBAL) set(CMAKE_C_COMPILER clang-20) set(CMAKE_CXX_COMPILER clang++-20) +set(GCOV_EXECUTABLE "llvm-cov-20 gcov" CACHE STRING "GCOV executable" FORCE) include("${CMAKE_CURRENT_LIST_DIR}/clang-flags.cmake") diff --git a/etc/clang-21-toolchain.cmake b/etc/clang-21-toolchain.cmake index a2c0b6d7..1f533a40 100644 --- a/etc/clang-21-toolchain.cmake +++ b/etc/clang-21-toolchain.cmake @@ -7,5 +7,6 @@ include_guard(GLOBAL) set(CMAKE_C_COMPILER clang-21) set(CMAKE_CXX_COMPILER clang++-21) +set(GCOV_EXECUTABLE "llvm-cov-21 gcov" CACHE STRING "GCOV executable" FORCE) include("${CMAKE_CURRENT_LIST_DIR}/clang-flags.cmake") diff --git a/etc/gcc-flags.cmake b/etc/gcc-flags.cmake index 1d451f59..641808d1 100644 --- a/etc/gcc-flags.cmake +++ b/etc/gcc-flags.cmake @@ -36,7 +36,7 @@ set(CMAKE_CXX_FLAGS_ASAN ) set(CMAKE_CXX_FLAGS_GCOV - "-O0 -fno-inline -g --coverage -fprofile-abs-path" + "-O0 -fno-default-inline -fno-inline -g --coverage -fprofile-abs-path" CACHE STRING "C++ GCOV Flags" FORCE diff --git a/tests/beman/optional/optional.t.cpp b/tests/beman/optional/optional.t.cpp index ae2296a8..c76bb6f6 100644 --- a/tests/beman/optional/optional.t.cpp +++ b/tests/beman/optional/optional.t.cpp @@ -153,6 +153,53 @@ TEST(OptionalTest, OptionalOfOptional) { EXPECT_TRUE(oo2.value() == 43); } +TEST(OptionalTest, EmplaceVariadic) { + struct for_emplace { + int a; + int b; + int c; + } f{4, 5, 6}; + + beman::optional::optional o3; + o3.emplace(1, 2, 3); + EXPECT_TRUE(o3.has_value()); + + beman::optional::optional engaged{f}; + engaged.emplace(1, 2, 3); + EXPECT_TRUE(engaged.has_value()); + EXPECT_EQ(engaged->a, 1); +} + +TEST(OptionalTest, EmplaceInitializerList) { + struct for_emplace { + std::vector v; // something to construct with list + std::string name = ""; + for_emplace(std::initializer_list l) : v(l) {} + for_emplace(std::initializer_list l, std::string n) : v(l), name(n) {} + } f{{1, 2, 3}}; + + beman::optional::optional o3; + o3.emplace({1, 2, 3}); + EXPECT_TRUE(o3.has_value()); + + beman::optional::optional engaged{f}; + + auto e1 = engaged.emplace({1, 2, 3}); + + EXPECT_TRUE(engaged.has_value()); + EXPECT_EQ(engaged->v[0], 1); + EXPECT_EQ(engaged->name, std::string("")); + EXPECT_EQ(engaged->v[0], e1.v[0]); + EXPECT_EQ(engaged->name, e1.name); + + auto e2 = engaged.emplace({2, 3, 4}, "Name"); + EXPECT_TRUE(engaged.has_value()); + EXPECT_EQ(engaged->v[0], 2); + EXPECT_EQ(engaged->name, std::string("Name")); + EXPECT_EQ(engaged->v[0], e2.v[0]); + EXPECT_EQ(engaged->name, e2.name); +} + TEST(OptionalTest, AssignmentValue) { beman::optional::optional o1 = 42; beman::optional::optional o2 = 12; @@ -192,6 +239,15 @@ TEST(OptionalTest, AssignmentValue) { o1 = s; EXPECT_TRUE(*o1 == 54); + beman::optional::optional emptyShort; + o1 = emptyShort; + EXPECT_FALSE(o1); + + o1 = o4; + EXPECT_TRUE(*o1 == 42); + o1 = std::move(emptyShort); + EXPECT_FALSE(o1); + struct not_trivial_copy_assignable { int i_; constexpr not_trivial_copy_assignable(int i) : i_(i) {} @@ -243,6 +299,58 @@ TEST(OptionalTest, AssignmentValue) { EXPECT_FALSE(o8); } +TEST(OptionalTest, ConvertingAssignmentValue) { + beman::optional::optional o1 = 42; + beman::optional::optional o2; + + short s = 9; + o1 = s; + o2 = s; + EXPECT_TRUE(o1); + EXPECT_TRUE(o2); +} + +TEST(OptionalTest, ConvertingValueAssignment) { + struct base {}; + + struct convertible { + operator base() { return base{}; } + }; + + beman::optional::optional empty; + beman::optional::optional engaged(base{}); + + empty = convertible{}; + engaged = convertible{}; + EXPECT_TRUE(empty); + EXPECT_TRUE(engaged); +} + +TEST(OptionalTest, ValueObserver) { + beman::optional::optional empty; + beman::optional::optional bound{5}; + EXPECT_TRUE(bound); + EXPECT_FALSE(empty); + EXPECT_EQ(*bound, 5); + EXPECT_EQ(bound.value(), 5); + EXPECT_EQ(std::as_const(bound).value(), 5); + EXPECT_EQ(std::move(bound).value(), 5); + + EXPECT_THROW( + { + try { + empty.value(); + } catch (const beman::optional::bad_optional_access& e) { + EXPECT_STREQ("Optional has no value", e.what()); + throw; + } + }, + beman::optional::bad_optional_access); + + EXPECT_THROW({ std::as_const(empty).value(); }, beman::optional::bad_optional_access); + EXPECT_THROW({ std::move(empty).value(); }, beman::optional::bad_optional_access); +} + TEST(OptionalTest, Triviality) { EXPECT_TRUE(std::is_trivially_copy_constructible>::value); EXPECT_TRUE(std::is_trivially_copy_assignable>::value); @@ -401,6 +509,16 @@ TEST(OptionalTest, MakeOptional) { EXPECT_TRUE(o5->v[1] == 1); EXPECT_TRUE(std::get<0>(o5->t) == 2); EXPECT_TRUE(std::get<1>(o5->t) == 3); + + struct for_variadic { + int a; + int b; + int c; + }; + + auto o6 = beman::optional::make_optional(0, 1, 3); + EXPECT_TRUE(o6); + EXPECT_EQ(o6->a, 0); } TEST(OptionalTest, Nullopt) { diff --git a/tests/beman/optional/optional_constexpr.t.cpp b/tests/beman/optional/optional_constexpr.t.cpp index 00b64aab..626ce9e2 100644 --- a/tests/beman/optional/optional_constexpr.t.cpp +++ b/tests/beman/optional/optional_constexpr.t.cpp @@ -161,6 +161,14 @@ consteval bool testConstexprAssignmentValue() { o1 = s; retval &= (*o1 == 54); + beman::optional::optional emptyShort; + o1 = emptyShort; + retval &= !o1.has_value(); + + o1 = o4; + o1 = std::move(emptyShort); + retval &= !o1.has_value(); + struct not_trivial_copy_assignable { int i_; constexpr not_trivial_copy_assignable(int i) : i_(i) {} diff --git a/tests/beman/optional/optional_ref.t.cpp b/tests/beman/optional/optional_ref.t.cpp index 0bc9deae..321c0356 100644 --- a/tests/beman/optional/optional_ref.t.cpp +++ b/tests/beman/optional/optional_ref.t.cpp @@ -92,6 +92,8 @@ beman::optional::optional process() { return t; } +beman::optional::optional processEmpty() { return beman::optional::nullopt; } + TEST(OptionalRefTest, BaseDerivedCastConstruction) { base b; derived& dref(b); // ok @@ -145,6 +147,9 @@ TEST(OptionalRefTest, Assignment) { o = process(); // well-formed EXPECT_TRUE(o); + o = processEmpty(); // well-formed + EXPECT_FALSE(o); + beman::optional::optional o2; EXPECT_FALSE(o2); o2 = [&]() { return i1; }(); @@ -152,6 +157,66 @@ TEST(OptionalRefTest, Assignment) { EXPECT_EQ(*o2, 7); } +TEST(OptionalRefTest, NullOptAssignment) { + beman::optional::optional i1; + EXPECT_FALSE(i1); + int i = 5; + i1 = i; + + EXPECT_TRUE(i1); + i1 = beman::optional::nullopt; + EXPECT_FALSE(i1); + i1 = i; + EXPECT_TRUE(i1); +} + +TEST(OptionalRefTest, ConstRefAssignment) { + int i = 7; + beman::optional::optional i1{i}; + const beman::optional::optional i2 = i1; + + beman::optional::optional c1; + c1 = i2; + EXPECT_TRUE(c1); + EXPECT_EQ(*c1, 7); + + i = 5; + EXPECT_EQ(*c1, 5); + const beman::optional::optional empty(beman::optional::nullopt); + c1 = empty; + EXPECT_FALSE(c1); +} + +TEST(OptionalRefTest, ConvertingConstRvalRef) { + int i = 7; + beman::optional::optional i1{i}; + const beman::optional::optional i2 = i1; + + beman::optional::optional c1; + c1 = std::move(i2); + EXPECT_TRUE(c1); + EXPECT_EQ(*c1, 7); + + i = 5; + EXPECT_EQ(*c1, 5); + const beman::optional::optional empty(beman::optional::nullopt); + c1 = std::move(empty); + EXPECT_FALSE(c1); +} + +TEST(OptionalRefTest, NullOptConstruction) { + beman::optional::optional i1(beman::optional::nullopt); + EXPECT_FALSE(i1); + int i = 5; + i1 = i; + + EXPECT_TRUE(i1); + i1 = beman::optional::nullopt; + EXPECT_FALSE(i1); + i1 = i; + EXPECT_TRUE(i1); +} + TEST(OptionalRefTest, RelationalOps) { int i1 = 4; int i2 = 42; @@ -527,17 +592,21 @@ TEST(OptionalRefTest, Observers) { beman::optional::optional ob1 = i1; beman::optional::optional ob2; const beman::optional::optional ob3 = i1; - success = std::is_samei_), int>::value; - static_assert(std::is_samei_), int>::value); + + EXPECT_EQ(ob1->i_, 3); + EXPECT_EQ(ob3->i_, 3); + + success = std::is_same_vi_), int>; + static_assert(std::is_same_vi_), int>); EXPECT_TRUE(success); - success = std::is_samei_), int>::value; - static_assert(std::is_samei_), int>::value); + success = std::is_same_vi_), int>; + static_assert(std::is_same_vi_), int>); EXPECT_TRUE(success); - success = std::is_samei_), int>::value; - static_assert(std::is_samei_), int>::value); + success = std::is_same_vi_), int>; + static_assert(std::is_same_vi_), int>); EXPECT_TRUE(success); - success = std::is_samei_), int>::value; - static_assert(std::is_samei_), int>::value); + success = std::is_same_vi_), int>; + static_assert(std::is_same_vi_), int>); EXPECT_TRUE(success); }