diff options
author | Steven Le Rouzic <steven.lerouzic@gmail.com> | 2024-10-28 23:52:48 +0100 |
---|---|---|
committer | Steven Le Rouzic <steven.lerouzic@gmail.com> | 2024-12-20 15:35:58 +0100 |
commit | 2a10eaae094e48a157d55ec886aaa07b0d0be6c9 (patch) | |
tree | e334ce5d2de1604eb168a3269be887bbc078df70 /asl | |
parent | 46cc6bfc5f62bb45427ef7778ba5fc04d7a546da (diff) |
Some work on test framework & option
Diffstat (limited to 'asl')
-rw-r--r-- | asl/maybe_uninit.hpp | 6 | ||||
-rw-r--r-- | asl/option.hpp | 31 | ||||
-rw-r--r-- | asl/testing/testing.cpp | 20 | ||||
-rw-r--r-- | asl/testing/testing.hpp | 8 | ||||
-rw-r--r-- | asl/tests/format_tests.cpp | 40 | ||||
-rw-r--r-- | asl/tests/option_tests.cpp | 87 | ||||
-rw-r--r-- | asl/tests/test_types.hpp | 30 |
7 files changed, 173 insertions, 49 deletions
diff --git a/asl/maybe_uninit.hpp b/asl/maybe_uninit.hpp index 19351d5..0ab09ee 100644 --- a/asl/maybe_uninit.hpp +++ b/asl/maybe_uninit.hpp @@ -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>
diff --git a/asl/option.hpp b/asl/option.hpp index 7600252..ddd531d 100644 --- a/asl/option.hpp +++ b/asl/option.hpp @@ -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
diff --git a/asl/testing/testing.cpp b/asl/testing/testing.cpp index f0c4fb5..405df34 100644 --- a/asl/testing/testing.cpp +++ b/asl/testing/testing.cpp @@ -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;
}
diff --git a/asl/testing/testing.hpp b/asl/testing/testing.hpp index 4fe44df..bf8f54e 100644 --- a/asl/testing/testing.hpp +++ b/asl/testing/testing.hpp @@ -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__); }
diff --git a/asl/tests/format_tests.cpp b/asl/tests/format_tests.cpp index 6e2430d..f4ca2cf 100644 --- a/asl/tests/format_tests.cpp +++ b/asl/tests/format_tests.cpp @@ -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);
}
diff --git a/asl/tests/option_tests.cpp b/asl/tests/option_tests.cpp index e423cc9..e7fff8b 100644 --- a/asl/tests/option_tests.cpp +++ b/asl/tests/option_tests.cpp @@ -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());
+
+ b.reset();
+ ASL_TEST_EXPECT(!b.has_value());
+}
+
+ASL_TEST(call_destructor)
+{
+ bool destroyed = false;
+
+ {
+ 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);
+ }
- a = ASL_MOVE(b);
- asl::testing::report_failure("OH NO", __FILE__, __LINE__);
+ ASL_TEST_EXPECT(destroyed);
}
-ASL_TEST(Option_cheese4)
+ASL_TEST(call_destructor_on_reset)
{
- asl::option<int> a;
- asl::option<int> b;
+ 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);
- a = ASL_MOVE(b);
+ 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);
}
diff --git a/asl/tests/test_types.hpp b/asl/tests/test_types.hpp index da1faa6..cabc084 100644 --- a/asl/tests/test_types.hpp +++ b/asl/tests/test_types.hpp @@ -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;
+ }
+ } +};
+
|