Some work on test framework & option
This commit is contained in:
2
.bazelrc
2
.bazelrc
@ -17,4 +17,4 @@ build --cxxopt=-Wno-extra-semi-stmt
|
||||
build --cxxopt=-Wno-extra-semi
|
||||
build --cxxopt=-Wno-global-constructors
|
||||
|
||||
test --test_output=all
|
||||
test --test_output=errors
|
||||
|
@ -28,3 +28,4 @@ Checks:
|
||||
- "-*-no-array-decay"
|
||||
- "-*-signed-bitwise"
|
||||
- "-readability-use-anyofallof"
|
||||
- "-readability-function-cognitive-complexity"
|
||||
|
@ -39,9 +39,9 @@ public:
|
||||
constexpr T* init_ptr_unsafe() & { return &m_value; }
|
||||
|
||||
// @Safety Reference must only be accessed when in initialized state.
|
||||
constexpr T& as_init_unsafe() && = delete;
|
||||
constexpr const T& as_init_unsafe() const& { return m_value; }
|
||||
constexpr T& as_init_unsafe() & { return m_value; }
|
||||
constexpr T&& as_init_unsafe() && { return ASL_MOVE(m_value); }
|
||||
constexpr const T& as_init_unsafe() const& { return m_value; }
|
||||
constexpr T& as_init_unsafe() & { return m_value; }
|
||||
|
||||
// @Safety Must be called only when in uninitialized state.
|
||||
template<typename... Args>
|
||||
|
@ -40,6 +40,13 @@ public:
|
||||
constexpr option() = default;
|
||||
constexpr option(nullopt_t) {} // NOLINT(*-explicit-conversions)
|
||||
|
||||
template<typename U>
|
||||
// NOLINTNEXTLINE(*-explicit-conversions)
|
||||
constexpr option(U&& value) requires constructible<T, U>
|
||||
{
|
||||
construct(ASL_FWD(value));
|
||||
}
|
||||
|
||||
constexpr option(const option& other) requires copy_constructible<T>
|
||||
{
|
||||
if (other.m_has_value)
|
||||
@ -116,6 +123,30 @@ public:
|
||||
m_has_value = false;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool has_value() const { return m_has_value; }
|
||||
|
||||
// @Todo Do we want this on rvalues? Or maybe some kind of unwrap?
|
||||
constexpr T&& value() &&
|
||||
{
|
||||
ASL_ASSERT(m_has_value); // @Todo Release assert
|
||||
return ASL_MOVE(m_payload).as_init_unsafe();
|
||||
}
|
||||
|
||||
constexpr T& value() &
|
||||
{
|
||||
ASL_ASSERT(m_has_value); // @Todo Release assert
|
||||
return m_payload.as_init_unsafe();
|
||||
}
|
||||
|
||||
constexpr const T& value() const&
|
||||
{
|
||||
ASL_ASSERT(m_has_value); // @Todo Release assert
|
||||
return m_payload.as_init_unsafe();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
option(T) -> option<T>;
|
||||
|
||||
} // namespace asl
|
||||
|
@ -25,14 +25,14 @@ void asl::testing::report_failure(const char* msg, const char* file, int line)
|
||||
{
|
||||
asl::eprint("--------------------------------------------------------------\n");
|
||||
asl::eprint("Test assertion failed at {}, line {}:\n", file, line);
|
||||
asl::eprint(" {}:\n", msg);
|
||||
asl::eprint(" {}\n", msg);
|
||||
asl::eprint("--------------------------------------------------------------\n");
|
||||
g_current_test_fail = true;
|
||||
}
|
||||
|
||||
#define RESET "\x1b[0m"
|
||||
#define RED "\x1b[0;31m"
|
||||
#define GREEN "\x1b[0;32m"
|
||||
#define RED(S) "\x1b[0;31m" S RESET
|
||||
#define GREEN(S) "\x1b[0;32m" S RESET
|
||||
|
||||
int main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[])
|
||||
{
|
||||
@ -41,33 +41,33 @@ int main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[])
|
||||
|
||||
for (auto* it = g_head; it != nullptr; it = it->m_next)
|
||||
{
|
||||
asl::eprint(GREEN "[ RUN ]" RESET " {}\n", it->m_case_name);
|
||||
asl::eprint(GREEN("[ RUN ]") " {}\n", it->m_case_name);
|
||||
|
||||
g_current_test_fail = false;
|
||||
it->m_fn();
|
||||
|
||||
if (!g_current_test_fail)
|
||||
{
|
||||
asl::eprint(GREEN "[ OK ]" RESET " {}\n", it->m_case_name);
|
||||
asl::eprint(GREEN("[ OK ]") " {}\n", it->m_case_name);
|
||||
pass += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
asl::eprint(RED "[ FAILED ]" RESET " {}\n", it->m_case_name);
|
||||
asl::eprint(RED("[ FAILED ]") " {}\n", it->m_case_name);
|
||||
fail += 1;
|
||||
}
|
||||
}
|
||||
|
||||
asl::eprint(GREEN "[----------]" RESET " {} test(s) run\n", fail + pass);
|
||||
asl::eprint(GREEN("[----------]") " {} test(s) run\n", fail + pass);
|
||||
|
||||
if (fail == 0)
|
||||
{
|
||||
asl::eprint(GREEN "[ PASSED ]" RESET " Good job!\n");
|
||||
asl::eprint(GREEN("[ PASSED ]") " Good job!\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
asl::eprint(RED "[ FAILED ]" RESET " {} test(s) failed\n", fail);
|
||||
asl::eprint(RED("[ FAILED ]") " {} test(s) failed\n", fail);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return fail;
|
||||
}
|
||||
|
@ -36,3 +36,11 @@ struct Test
|
||||
#CASE, \
|
||||
asl_test_fn_##CASE); \
|
||||
void asl_test_fn_##CASE()
|
||||
|
||||
#define ASL_TEST_ASSERT(EXPR) \
|
||||
if (EXPR) {} \
|
||||
else { ::asl::testing::report_failure(#EXPR, __FILE__, __LINE__); return; }
|
||||
|
||||
#define ASL_TEST_EXPECT(EXPR) \
|
||||
if (EXPR) {} \
|
||||
else { ::asl::testing::report_failure(#EXPR, __FILE__, __LINE__); }
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "asl/format.hpp"
|
||||
#include "asl/testing/testing.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
@ -33,74 +34,73 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
int main2()
|
||||
ASL_TEST(Format)
|
||||
{
|
||||
StringSink sink;
|
||||
|
||||
// @Todo Use the testing framework
|
||||
// @Todo Introduce ASL_TEST_ASSERT_EQ, or ASL_TEST_ASSERT_STREQ
|
||||
// @Todo Don't use strcmp for string comparison
|
||||
|
||||
asl::format(&sink, "Hello, world!");
|
||||
assert(strcmp(sink.cstr(), "Hello, world!") == 0);
|
||||
ASL_TEST_ASSERT(strcmp(sink.cstr(), "Hello, world!") == 0);
|
||||
|
||||
sink.reset();
|
||||
asl::format(&sink, "");
|
||||
assert(strcmp(sink.cstr(), "") == 0);
|
||||
ASL_TEST_ASSERT(strcmp(sink.cstr(), "") == 0);
|
||||
|
||||
sink.reset();
|
||||
asl::format(&sink, "Hello, {}!", "world");
|
||||
assert(strcmp(sink.cstr(), "Hello, world!") == 0);
|
||||
ASL_TEST_ASSERT(strcmp(sink.cstr(), "Hello, world!") == 0);
|
||||
|
||||
sink.reset();
|
||||
asl::format(&sink, "Hello, {}! {}", "world");
|
||||
assert(strcmp(sink.cstr(), "Hello, world! <ERROR>") == 0);
|
||||
ASL_TEST_ASSERT(strcmp(sink.cstr(), "Hello, world! <ERROR>") == 0);
|
||||
|
||||
sink.reset();
|
||||
asl::format(&sink, "Hello, pup!", "world");
|
||||
assert(strcmp(sink.cstr(), "Hello, pup!") == 0);
|
||||
ASL_TEST_ASSERT(strcmp(sink.cstr(), "Hello, pup!") == 0);
|
||||
|
||||
sink.reset();
|
||||
asl::format(&sink, "{}", "CHEESE");
|
||||
assert(strcmp(sink.cstr(), "CHEESE") == 0);
|
||||
ASL_TEST_ASSERT(strcmp(sink.cstr(), "CHEESE") == 0);
|
||||
|
||||
sink.reset();
|
||||
asl::format(&sink, "{ ", "CHEESE");
|
||||
assert(strcmp(sink.cstr(), "<ERROR> ") == 0);
|
||||
ASL_TEST_ASSERT(strcmp(sink.cstr(), "<ERROR> ") == 0);
|
||||
|
||||
sink.reset();
|
||||
asl::format(&sink, "{", "CHEESE");
|
||||
assert(strcmp(sink.cstr(), "<ERROR>") == 0);
|
||||
ASL_TEST_ASSERT(strcmp(sink.cstr(), "<ERROR>") == 0);
|
||||
|
||||
sink.reset();
|
||||
asl::format(&sink, "a{{b");
|
||||
assert(strcmp(sink.cstr(), "a{b") == 0);
|
||||
ASL_TEST_ASSERT(strcmp(sink.cstr(), "a{b") == 0);
|
||||
|
||||
sink.reset();
|
||||
asl::format(&sink, "{{{}}} }", "CHEESE");
|
||||
assert(strcmp(sink.cstr(), "{CHEESE} }") == 0);
|
||||
ASL_TEST_ASSERT(strcmp(sink.cstr(), "{CHEESE} }") == 0);
|
||||
|
||||
sink.reset();
|
||||
asl::format(&sink, "{} {} {}", 0, 1, 2);
|
||||
assert(strcmp(sink.cstr(), "0 1 2") == 0);
|
||||
ASL_TEST_ASSERT(strcmp(sink.cstr(), "0 1 2") == 0);
|
||||
|
||||
sink.reset();
|
||||
asl::format(&sink, "{} {} {}", 10, 11, 12);
|
||||
assert(strcmp(sink.cstr(), "10 11 12") == 0);
|
||||
ASL_TEST_ASSERT(strcmp(sink.cstr(), "10 11 12") == 0);
|
||||
|
||||
sink.reset();
|
||||
asl::format(&sink, "{} {} {}", 100, 101, 102);
|
||||
assert(strcmp(sink.cstr(), "100 101 102") == 0);
|
||||
ASL_TEST_ASSERT(strcmp(sink.cstr(), "100 101 102") == 0);
|
||||
|
||||
sink.reset();
|
||||
asl::format(&sink, "{} {} {}", 1000, 1001, 1002);
|
||||
assert(strcmp(sink.cstr(), "1000 1001 1002") == 0);
|
||||
ASL_TEST_ASSERT(strcmp(sink.cstr(), "1000 1001 1002") == 0);
|
||||
|
||||
sink.reset();
|
||||
asl::format(&sink, "{} {} {} {}", -1, -23, -456, -7890);
|
||||
assert(strcmp(sink.cstr(), "-1 -23 -456 -7890") == 0);
|
||||
ASL_TEST_ASSERT(strcmp(sink.cstr(), "-1 -23 -456 -7890") == 0);
|
||||
|
||||
sink.reset();
|
||||
asl::format(&sink, "{} {}", true, false);
|
||||
assert(strcmp(sink.cstr(), "true false") == 0);
|
||||
|
||||
return 0;
|
||||
ASL_TEST_ASSERT(strcmp(sink.cstr(), "true false") == 0);
|
||||
}
|
||||
|
@ -24,35 +24,90 @@ static_assert(asl::move_assignable<asl::option<int>>);
|
||||
static_assert(asl::move_assignable<asl::option<CopyAssignable>>);
|
||||
static_assert(!asl::move_assignable<asl::option<NonMoveAssignable>>);
|
||||
|
||||
ASL_TEST(Option_cheese)
|
||||
ASL_TEST(make_null)
|
||||
{
|
||||
asl::option<int> a;
|
||||
asl::option<int> b;
|
||||
asl::option<int> b = asl::nullopt;
|
||||
|
||||
a = ASL_MOVE(b);
|
||||
ASL_TEST_EXPECT(!a.has_value());
|
||||
ASL_TEST_EXPECT(!b.has_value());
|
||||
}
|
||||
|
||||
ASL_TEST(Option_cheese2)
|
||||
ASL_TEST(make_value)
|
||||
{
|
||||
asl::option<int> a;
|
||||
asl::option<int> b;
|
||||
asl::option<int> a = 48;
|
||||
|
||||
a = ASL_MOVE(b);
|
||||
ASL_TEST_EXPECT(a.has_value());
|
||||
}
|
||||
|
||||
ASL_TEST(Option_cheese3)
|
||||
ASL_TEST(reset)
|
||||
{
|
||||
asl::option<int> a;
|
||||
asl::option<int> b;
|
||||
asl::option<int> b = 48;
|
||||
ASL_TEST_EXPECT(b.has_value());
|
||||
|
||||
a = ASL_MOVE(b);
|
||||
asl::testing::report_failure("OH NO", __FILE__, __LINE__);
|
||||
b.reset();
|
||||
ASL_TEST_EXPECT(!b.has_value());
|
||||
}
|
||||
|
||||
ASL_TEST(Option_cheese4)
|
||||
ASL_TEST(call_destructor)
|
||||
{
|
||||
asl::option<int> a;
|
||||
asl::option<int> b;
|
||||
bool destroyed = false;
|
||||
|
||||
a = ASL_MOVE(b);
|
||||
{
|
||||
DestructorObserver obs(&destroyed);
|
||||
|
||||
asl::option<DestructorObserver> opt(ASL_MOVE(obs));
|
||||
ASL_TEST_EXPECT(!destroyed);
|
||||
|
||||
asl::option<DestructorObserver> opt2 = ASL_MOVE(opt);
|
||||
ASL_TEST_EXPECT(!destroyed);
|
||||
}
|
||||
|
||||
ASL_TEST_EXPECT(destroyed);
|
||||
}
|
||||
|
||||
ASL_TEST(call_destructor_on_reset)
|
||||
{
|
||||
bool destroyed = false;
|
||||
|
||||
asl::option<DestructorObserver> opt(&destroyed);
|
||||
ASL_TEST_EXPECT(!destroyed);
|
||||
|
||||
opt.reset();
|
||||
ASL_TEST_EXPECT(destroyed);
|
||||
}
|
||||
|
||||
ASL_TEST(value)
|
||||
{
|
||||
asl::option<int> a = 1;
|
||||
asl::option<int> b = 2;
|
||||
asl::option<int> c = a;
|
||||
|
||||
ASL_TEST_EXPECT(a.value() == 1);
|
||||
ASL_TEST_EXPECT(b.value() == 2);
|
||||
ASL_TEST_EXPECT(c.value() == 1);
|
||||
|
||||
c = b;
|
||||
ASL_TEST_EXPECT(c.value() == 2);
|
||||
}
|
||||
|
||||
ASL_TEST(value_move)
|
||||
{
|
||||
bool destroyed = false;
|
||||
|
||||
asl::option<DestructorObserver> opt(&destroyed);
|
||||
ASL_TEST_EXPECT(!destroyed);
|
||||
|
||||
{
|
||||
auto x = ASL_MOVE(opt).value();
|
||||
ASL_TEST_EXPECT(!destroyed);
|
||||
}
|
||||
|
||||
ASL_TEST_EXPECT(destroyed);
|
||||
}
|
||||
|
||||
ASL_TEST(deduction_guide)
|
||||
{
|
||||
asl::option opt(45);
|
||||
ASL_TEST_EXPECT(opt.value() == 45);
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "asl/utility.hpp"
|
||||
|
||||
struct DefaultConstructible { DefaultConstructible() {} };
|
||||
struct TriviallyDefaultConstructible { TriviallyDefaultConstructible() = default; };
|
||||
struct NonDefaultConstructible { NonDefaultConstructible() = delete; };
|
||||
@ -23,3 +25,31 @@ struct NonMoveAssignable { NonMoveAssignable& operator=(NonMoveAssignable&&) = d
|
||||
struct TriviallyDestructible { ~TriviallyDestructible() = default; };
|
||||
struct HasDestructor { ~HasDestructor() {} };
|
||||
|
||||
struct DestructorObserver
|
||||
{
|
||||
bool* destroyed;
|
||||
|
||||
explicit DestructorObserver(bool* destroyed_) : destroyed{destroyed_} {}
|
||||
|
||||
DestructorObserver(DestructorObserver&& other)
|
||||
: destroyed{asl::exchange(other.destroyed, nullptr)}
|
||||
{}
|
||||
|
||||
DestructorObserver& operator=(DestructorObserver&& other)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
destroyed = asl::exchange(other.destroyed, nullptr);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~DestructorObserver()
|
||||
{
|
||||
if (destroyed != nullptr)
|
||||
{
|
||||
*destroyed = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user