Add unit tests for optional and result helper types

This commit is contained in:
Jeremy Rifkin 2025-01-26 16:02:34 -06:00
parent b705afba69
commit 906784284f
No known key found for this signature in database
GPG Key ID: 19AA8270105E8EB4
10 changed files with 230 additions and 10 deletions

View File

@ -60,19 +60,19 @@ namespace detail {
}
NODISCARD optional<T> value() const & {
return has_value() ? value_ : nullopt;
return has_value() ? optional<T>(value_) : nullopt;
}
NODISCARD optional<E> error() const & {
return is_error() ? error_ : nullopt;
return is_error() ? optional<E>(error_) : nullopt;
}
NODISCARD optional<T> value() && {
return has_value() ? std::move(value_) : nullopt;
return has_value() ? optional<T>(std::move(value_)) : nullopt;
}
NODISCARD optional<E> error() && {
return is_error() ? std::move(error_) : nullopt;
return is_error() ? optional<E>(std::move(error_)) : nullopt;
}
NODISCARD T& unwrap_value() & {

View File

@ -81,12 +81,14 @@ if(NOT CPPTRACE_SKIP_UNIT)
add_executable(
unittest
unit/main.cpp
unit/raw_trace.cpp
unit/object_trace.cpp
unit/stacktrace.cpp
unit/from_current.cpp
unit/from_current_z.cpp
unit/traced_exception.cpp
unit/tracing/raw_trace.cpp
unit/tracing/object_trace.cpp
unit/tracing/stacktrace.cpp
unit/tracing/from_current.cpp
unit/tracing/from_current_z.cpp
unit/tracing/traced_exception.cpp
unit/internals/optional.cpp
unit/internals/result.cpp
)
target_compile_features(unittest PRIVATE cxx_std_20)
target_link_libraries(unittest PRIVATE ${target_name} GTest::gtest_main GTest::gmock_main)
@ -101,5 +103,6 @@ if(NOT CPPTRACE_SKIP_UNIT)
if(CPPTRACE_SANITIZER_BUILD)
target_compile_definitions(unittest PRIVATE CPPTRACE_SANITIZER_BUILD)
endif()
target_include_directories(unittest PRIVATE ../src)
add_test(NAME unittest COMMAND unittest)
endif()

View File

@ -0,0 +1,119 @@
#include <gtest/gtest.h>
#include "utils/optional.hpp"
using namespace cpptrace::detail;
TEST(OptionalTest, DefaultConstructor) {
optional<int> o;
EXPECT_FALSE(o.has_value());
EXPECT_FALSE(static_cast<bool>(o));
}
TEST(OptionalTest, ConstructWithNullopt) {
optional<int> o(nullopt);
EXPECT_FALSE(o.has_value());
}
TEST(OptionalTest, ValueConstructor) {
optional<int> o(42);
EXPECT_TRUE(o.has_value());
EXPECT_EQ(o.unwrap(), 42);
int x = 100;
optional<int> o2(x);
EXPECT_TRUE(o2.has_value());
EXPECT_EQ(o2.unwrap(), 100);
}
TEST(OptionalTest, CopyConstructor) {
optional<int> o1(42);
optional<int> o2(o1);
EXPECT_TRUE(o2.has_value());
EXPECT_EQ(o2.unwrap(), 42);
optional<int> o3(nullopt);
optional<int> o4(o3);
EXPECT_FALSE(o4.has_value());
}
TEST(OptionalTest, MoveConstructor) {
optional<int> o1(42);
optional<int> o2(std::move(o1));
EXPECT_TRUE(o2.has_value());
EXPECT_EQ(o2.unwrap(), 42);
optional<int> o3(nullopt);
optional<int> o4(std::move(o3));
EXPECT_FALSE(o4.has_value());
}
TEST(OptionalTest, CopyAssignmentOperator) {
optional<int> o1(42);
optional<int> o2;
o2 = o1;
EXPECT_TRUE(o2.has_value());
EXPECT_EQ(o2.unwrap(), 42);
optional<int> o3(nullopt);
optional<int> o4(100);
o4 = o3;
EXPECT_FALSE(o4.has_value());
}
TEST(OptionalTest, MoveAssignmentOperator) {
optional<int> o1(42);
optional<int> o2;
o2 = std::move(o1);
EXPECT_TRUE(o2.has_value());
EXPECT_EQ(o2.unwrap(), 42);
optional<int> o3(nullopt);
optional<int> o4(99);
o4 = std::move(o3);
EXPECT_FALSE(o4.has_value());
}
TEST(OptionalTest, AssignmentFromValue) {
optional<int> o;
o = 123;
EXPECT_TRUE(o.has_value());
EXPECT_EQ(o.unwrap(), 123);
o = nullopt;
EXPECT_FALSE(o.has_value());
}
TEST(OptionalTest, Reset) {
optional<int> o(42);
EXPECT_TRUE(o.has_value());
o.reset();
EXPECT_FALSE(o.has_value());
}
TEST(OptionalTest, Swap) {
optional<int> o1(42);
optional<int> o2(100);
o1.swap(o2);
EXPECT_TRUE(o1.has_value());
EXPECT_TRUE(o2.has_value());
EXPECT_EQ(o1.unwrap(), 100);
EXPECT_EQ(o2.unwrap(), 42);
// Swap a value-holding optional with an empty optional
optional<int> o3(7);
optional<int> o4(nullopt);
o3.swap(o4);
EXPECT_FALSE(o3.has_value());
EXPECT_TRUE(o4.has_value());
EXPECT_EQ(o4.unwrap(), 7);
}
TEST(OptionalTest, ValueOr) {
optional<int> o1(42);
EXPECT_EQ(o1.value_or(100), 42);
optional<int> o2(nullopt);
EXPECT_EQ(o2.value_or(100), 100);
}

View File

@ -0,0 +1,98 @@
#include <gtest/gtest.h>
#include "utils/result.hpp"
using namespace cpptrace::detail;
// A simple custom error type that behaves like a standard exception.
struct error {
int x;
const char* what() const {
return "error...";
}
};
class ResultFixture : public testing::Test {
public:
ResultFixture() {
cpptrace::absorb_trace_exceptions(true);
}
~ResultFixture() override {
cpptrace::absorb_trace_exceptions(false);
}
};
TEST_F(ResultFixture, ConstructWithValueRValue) {
cpptrace::detail::Result<std::string, error> result("test");
EXPECT_TRUE(result.has_value());
EXPECT_FALSE(result.is_error());
EXPECT_TRUE(static_cast<bool>(result));
EXPECT_EQ(result.unwrap_value(), "test");
EXPECT_FALSE(result.error().has_value());
}
TEST_F(ResultFixture, ConstructWithValueLValue) {
std::string s = "test";
cpptrace::detail::Result<std::string, error> result(s);
EXPECT_TRUE(result.has_value());
EXPECT_FALSE(result.is_error());
EXPECT_EQ(result.unwrap_value(), "test");
s = "x";
EXPECT_EQ(result.unwrap_value(), "test");
}
TEST_F(ResultFixture, ConstructWithErrorRValue) {
cpptrace::detail::Result<std::string, error> result(error{1});
EXPECT_FALSE(result.has_value());
EXPECT_TRUE(result.is_error());
EXPECT_FALSE(static_cast<bool>(result));
EXPECT_EQ(result.unwrap_error().x, 1);
// Check that value() returns nullopt in this scenario
EXPECT_FALSE(result.value().has_value());
}
TEST_F(ResultFixture, ConstructWithErrorLValue) {
error e{1};
cpptrace::detail::Result<std::string, error> result(e);
EXPECT_FALSE(result.has_value());
EXPECT_TRUE(result.is_error());
EXPECT_EQ(result.unwrap_error().x, 1);
}
TEST_F(ResultFixture, MoveConstructorValue) {
cpptrace::detail::Result<std::string, error> original(std::string("move"));
cpptrace::detail::Result<std::string, error> moved(std::move(original));
EXPECT_TRUE(moved.has_value());
EXPECT_EQ(moved.unwrap_value(), "move");
EXPECT_TRUE(original.has_value());
}
TEST_F(ResultFixture, MoveConstructorError) {
cpptrace::detail::Result<std::string, error> original(error{1});
cpptrace::detail::Result<std::string, error> moved(std::move(original));
EXPECT_TRUE(moved.is_error());
EXPECT_EQ(moved.unwrap_error().x, 1);
EXPECT_TRUE(original.is_error());
}
TEST_F(ResultFixture, ValueOr) {
{
cpptrace::detail::Result<int, error> res_with_value(42);
EXPECT_EQ(res_with_value.value_or(-1), 42);
EXPECT_EQ(std::move(res_with_value).value_or(-1), 42);
}
{
cpptrace::detail::Result<int, error> res_with_error(error{});
EXPECT_EQ(res_with_error.value_or(-1), -1);
EXPECT_EQ(std::move(res_with_error).value_or(-1), -1);
}
}