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-extra-semi
|
||||||
build --cxxopt=-Wno-global-constructors
|
build --cxxopt=-Wno-global-constructors
|
||||||
|
|
||||||
test --test_output=all
|
test --test_output=errors
|
||||||
|
@ -28,3 +28,4 @@ Checks:
|
|||||||
- "-*-no-array-decay"
|
- "-*-no-array-decay"
|
||||||
- "-*-signed-bitwise"
|
- "-*-signed-bitwise"
|
||||||
- "-readability-use-anyofallof"
|
- "-readability-use-anyofallof"
|
||||||
|
- "-readability-function-cognitive-complexity"
|
||||||
|
@ -39,7 +39,7 @@ public:
|
|||||||
constexpr T* init_ptr_unsafe() & { return &m_value; }
|
constexpr T* init_ptr_unsafe() & { return &m_value; }
|
||||||
|
|
||||||
// @Safety Reference must only be accessed when in initialized state.
|
// @Safety Reference must only be accessed when in initialized state.
|
||||||
constexpr T& as_init_unsafe() && = delete;
|
constexpr T&& as_init_unsafe() && { return ASL_MOVE(m_value); }
|
||||||
constexpr const T& as_init_unsafe() const& { return m_value; }
|
constexpr const T& as_init_unsafe() const& { return m_value; }
|
||||||
constexpr T& as_init_unsafe() & { return m_value; }
|
constexpr T& as_init_unsafe() & { return m_value; }
|
||||||
|
|
||||||
|
@ -40,6 +40,13 @@ public:
|
|||||||
constexpr option() = default;
|
constexpr option() = default;
|
||||||
constexpr option(nullopt_t) {} // NOLINT(*-explicit-conversions)
|
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>
|
constexpr option(const option& other) requires copy_constructible<T>
|
||||||
{
|
{
|
||||||
if (other.m_has_value)
|
if (other.m_has_value)
|
||||||
@ -116,6 +123,30 @@ public:
|
|||||||
m_has_value = false;
|
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
|
} // namespace asl
|
||||||
|
@ -25,14 +25,14 @@ void asl::testing::report_failure(const char* msg, const char* file, int line)
|
|||||||
{
|
{
|
||||||
asl::eprint("--------------------------------------------------------------\n");
|
asl::eprint("--------------------------------------------------------------\n");
|
||||||
asl::eprint("Test assertion failed at {}, line {}:\n", file, line);
|
asl::eprint("Test assertion failed at {}, line {}:\n", file, line);
|
||||||
asl::eprint(" {}:\n", msg);
|
asl::eprint(" {}\n", msg);
|
||||||
asl::eprint("--------------------------------------------------------------\n");
|
asl::eprint("--------------------------------------------------------------\n");
|
||||||
g_current_test_fail = true;
|
g_current_test_fail = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define RESET "\x1b[0m"
|
#define RESET "\x1b[0m"
|
||||||
#define RED "\x1b[0;31m"
|
#define RED(S) "\x1b[0;31m" S RESET
|
||||||
#define GREEN "\x1b[0;32m"
|
#define GREEN(S) "\x1b[0;32m" S RESET
|
||||||
|
|
||||||
int main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[])
|
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)
|
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;
|
g_current_test_fail = false;
|
||||||
it->m_fn();
|
it->m_fn();
|
||||||
|
|
||||||
if (!g_current_test_fail)
|
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;
|
pass += 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
asl::eprint(RED "[ FAILED ]" RESET " {}\n", it->m_case_name);
|
asl::eprint(RED("[ FAILED ]") " {}\n", it->m_case_name);
|
||||||
fail += 1;
|
fail += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
asl::eprint(GREEN "[----------]" RESET " {} test(s) run\n", fail + pass);
|
asl::eprint(GREEN("[----------]") " {} test(s) run\n", fail + pass);
|
||||||
|
|
||||||
if (fail == 0)
|
if (fail == 0)
|
||||||
{
|
{
|
||||||
asl::eprint(GREEN "[ PASSED ]" RESET " Good job!\n");
|
asl::eprint(GREEN("[ PASSED ]") " Good job!\n");
|
||||||
}
|
}
|
||||||
else
|
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, \
|
#CASE, \
|
||||||
asl_test_fn_##CASE); \
|
asl_test_fn_##CASE); \
|
||||||
void 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/format.hpp"
|
||||||
|
#include "asl/testing/testing.hpp"
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
@ -33,74 +34,73 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
int main2()
|
ASL_TEST(Format)
|
||||||
{
|
{
|
||||||
StringSink sink;
|
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!");
|
asl::format(&sink, "Hello, world!");
|
||||||
assert(strcmp(sink.cstr(), "Hello, world!") == 0);
|
ASL_TEST_ASSERT(strcmp(sink.cstr(), "Hello, world!") == 0);
|
||||||
|
|
||||||
sink.reset();
|
sink.reset();
|
||||||
asl::format(&sink, "");
|
asl::format(&sink, "");
|
||||||
assert(strcmp(sink.cstr(), "") == 0);
|
ASL_TEST_ASSERT(strcmp(sink.cstr(), "") == 0);
|
||||||
|
|
||||||
sink.reset();
|
sink.reset();
|
||||||
asl::format(&sink, "Hello, {}!", "world");
|
asl::format(&sink, "Hello, {}!", "world");
|
||||||
assert(strcmp(sink.cstr(), "Hello, world!") == 0);
|
ASL_TEST_ASSERT(strcmp(sink.cstr(), "Hello, world!") == 0);
|
||||||
|
|
||||||
sink.reset();
|
sink.reset();
|
||||||
asl::format(&sink, "Hello, {}! {}", "world");
|
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();
|
sink.reset();
|
||||||
asl::format(&sink, "Hello, pup!", "world");
|
asl::format(&sink, "Hello, pup!", "world");
|
||||||
assert(strcmp(sink.cstr(), "Hello, pup!") == 0);
|
ASL_TEST_ASSERT(strcmp(sink.cstr(), "Hello, pup!") == 0);
|
||||||
|
|
||||||
sink.reset();
|
sink.reset();
|
||||||
asl::format(&sink, "{}", "CHEESE");
|
asl::format(&sink, "{}", "CHEESE");
|
||||||
assert(strcmp(sink.cstr(), "CHEESE") == 0);
|
ASL_TEST_ASSERT(strcmp(sink.cstr(), "CHEESE") == 0);
|
||||||
|
|
||||||
sink.reset();
|
sink.reset();
|
||||||
asl::format(&sink, "{ ", "CHEESE");
|
asl::format(&sink, "{ ", "CHEESE");
|
||||||
assert(strcmp(sink.cstr(), "<ERROR> ") == 0);
|
ASL_TEST_ASSERT(strcmp(sink.cstr(), "<ERROR> ") == 0);
|
||||||
|
|
||||||
sink.reset();
|
sink.reset();
|
||||||
asl::format(&sink, "{", "CHEESE");
|
asl::format(&sink, "{", "CHEESE");
|
||||||
assert(strcmp(sink.cstr(), "<ERROR>") == 0);
|
ASL_TEST_ASSERT(strcmp(sink.cstr(), "<ERROR>") == 0);
|
||||||
|
|
||||||
sink.reset();
|
sink.reset();
|
||||||
asl::format(&sink, "a{{b");
|
asl::format(&sink, "a{{b");
|
||||||
assert(strcmp(sink.cstr(), "a{b") == 0);
|
ASL_TEST_ASSERT(strcmp(sink.cstr(), "a{b") == 0);
|
||||||
|
|
||||||
sink.reset();
|
sink.reset();
|
||||||
asl::format(&sink, "{{{}}} }", "CHEESE");
|
asl::format(&sink, "{{{}}} }", "CHEESE");
|
||||||
assert(strcmp(sink.cstr(), "{CHEESE} }") == 0);
|
ASL_TEST_ASSERT(strcmp(sink.cstr(), "{CHEESE} }") == 0);
|
||||||
|
|
||||||
sink.reset();
|
sink.reset();
|
||||||
asl::format(&sink, "{} {} {}", 0, 1, 2);
|
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();
|
sink.reset();
|
||||||
asl::format(&sink, "{} {} {}", 10, 11, 12);
|
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();
|
sink.reset();
|
||||||
asl::format(&sink, "{} {} {}", 100, 101, 102);
|
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();
|
sink.reset();
|
||||||
asl::format(&sink, "{} {} {}", 1000, 1001, 1002);
|
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();
|
sink.reset();
|
||||||
asl::format(&sink, "{} {} {} {}", -1, -23, -456, -7890);
|
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();
|
sink.reset();
|
||||||
asl::format(&sink, "{} {}", true, false);
|
asl::format(&sink, "{} {}", true, false);
|
||||||
assert(strcmp(sink.cstr(), "true false") == 0);
|
ASL_TEST_ASSERT(strcmp(sink.cstr(), "true false") == 0);
|
||||||
|
|
||||||
return 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<CopyAssignable>>);
|
||||||
static_assert(!asl::move_assignable<asl::option<NonMoveAssignable>>);
|
static_assert(!asl::move_assignable<asl::option<NonMoveAssignable>>);
|
||||||
|
|
||||||
ASL_TEST(Option_cheese)
|
ASL_TEST(make_null)
|
||||||
{
|
{
|
||||||
asl::option<int> a;
|
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> a = 48;
|
||||||
asl::option<int> b;
|
|
||||||
|
|
||||||
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 = 48;
|
||||||
asl::option<int> b;
|
ASL_TEST_EXPECT(b.has_value());
|
||||||
|
|
||||||
a = ASL_MOVE(b);
|
b.reset();
|
||||||
asl::testing::report_failure("OH NO", __FILE__, __LINE__);
|
ASL_TEST_EXPECT(!b.has_value());
|
||||||
}
|
}
|
||||||
|
|
||||||
ASL_TEST(Option_cheese4)
|
ASL_TEST(call_destructor)
|
||||||
{
|
{
|
||||||
asl::option<int> a;
|
bool destroyed = false;
|
||||||
asl::option<int> b;
|
|
||||||
|
|
||||||
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
|
#pragma once
|
||||||
|
|
||||||
|
#include "asl/utility.hpp"
|
||||||
|
|
||||||
struct DefaultConstructible { DefaultConstructible() {} };
|
struct DefaultConstructible { DefaultConstructible() {} };
|
||||||
struct TriviallyDefaultConstructible { TriviallyDefaultConstructible() = default; };
|
struct TriviallyDefaultConstructible { TriviallyDefaultConstructible() = default; };
|
||||||
struct NonDefaultConstructible { NonDefaultConstructible() = delete; };
|
struct NonDefaultConstructible { NonDefaultConstructible() = delete; };
|
||||||
@ -23,3 +25,31 @@ struct NonMoveAssignable { NonMoveAssignable& operator=(NonMoveAssignable&&) = d
|
|||||||
struct TriviallyDestructible { ~TriviallyDestructible() = default; };
|
struct TriviallyDestructible { ~TriviallyDestructible() = default; };
|
||||||
struct HasDestructor { ~HasDestructor() {} };
|
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