di 0.1.0
Loading...
Searching...
No Matches
test_manager.h
Go to the documentation of this file.
1#pragma once
2
4#include "di/cli/prelude.h"
8#include "di/test/test_case.h"
9
10namespace di::test {
11class TestManager {
12public:
13 static auto the() -> TestManager& {
14 static TestManager s_the;
15 return s_the;
16 }
17
18 void register_test_case(TestCase test_case) { m_test_cases.push_back(di::move(test_case)); }
19
20 void set_writer(di::Any<di::Writer> writer) { m_writer = di::move(writer); }
21
22 void set_did_finish(di::Function<void(i32)> did_finish) { m_did_finish = di::move(did_finish); }
23
24 struct Args {
25 bool list_simple { false };
28 bool help { false };
29
30 constexpr static auto get_cli_parser() {
31 return di::cli_parser<Args>("dius_test"_sv, "Dius Test Runner"_sv)
32 .help()
33 .option<&Args::list_simple>('L', "list-simple"_tsv,
34 "Output a simple machine readable list of test cases"_sv)
35 .option<&Args::suite_name>('s', "suite"_tsv, "Specifc test suite to run"_sv)
36 .option<&Args::case_name>('t', "test-case"_tsv, "Specific case to run in the format ([suite:]case)"_sv);
37 }
38 };
39
41 auto [list_simple, suite_name, case_name, _] = args;
42
43 auto [first_to_remove, last_to_remove] = di::container::remove_if(m_test_cases, [&](auto&& test_case) {
44 if (suite_name && *suite_name != test_case.suite_name()) {
45 return true;
46 }
47 if (case_name) {
48 auto [colon_it, colon_it_end] = case_name->find(':');
49 if (colon_it != colon_it_end) {
50 return test_case.suite_name() != case_name->substr(case_name->begin(), colon_it) ||
51 test_case.case_name() != case_name->substr(colon_it_end);
52 }
53 return test_case.case_name() != *case_name;
54 }
55 return false;
56 });
57 m_test_cases.erase(first_to_remove, last_to_remove);
58
59 if (m_test_cases.empty() && (suite_name || case_name)) {
61 m_writer, "No test cases match filter: [suite={}] [case={}]"_sv, suite_name, case_name);
62 return di::Unexpected(di::BasicError::InvalidArgument);
63 }
64
65 if (list_simple) {
66 for (auto& test_case : m_test_cases) {
67 di::writer_println<di::StringView::Encoding>(m_writer, "{}:{}"_sv, test_case.suite_name(),
68 test_case.case_name());
69 }
70 return {};
71 }
72
73 execute_remaining_tests();
74
75 return {};
76 }
77
78 auto is_test_application() const -> bool { return !m_test_cases.empty(); }
80 print_failure_message();
81 ++m_current_test_index;
82 final_report();
83 }
84
85private:
86 TestManager() = default;
87
88 void print_failure_message() {
89 auto& test_case = m_test_cases[m_current_test_index];
91 m_writer, "{}: {}: {}"_sv, di::Styled("FAIL"_sv, di::FormatEffect::Bold | di::FormatColor::Red),
92 di::Styled(test_case.suite_name(), di::FormatEffect::Bold), test_case.case_name());
93 ++m_fail_count;
94 }
95
96 void print_success_message() {
97 auto& test_case = m_test_cases[m_current_test_index];
98
100 m_writer, "{}: {}: {}"_sv, di::Styled("PASS"_sv, di::FormatEffect::Bold | di::FormatColor::Green),
101 di::Styled(test_case.suite_name(), di::FormatEffect::Bold), test_case.case_name());
102 ++m_success_count;
103 }
104
105 void run_current_test() {
106 auto& test_case = m_test_cases[m_current_test_index];
107 test_case.execute();
108 print_success_message();
109 }
110
111 void execute_remaining_tests() {
112 while (m_current_test_index < m_test_cases.size()) {
113 run_current_test();
114 m_current_test_index++;
115 }
116 final_report();
117 }
118
119 void final_report() {
120 auto tests_skipped = m_test_cases.size() - m_current_test_index;
121
122 di::writer_print<di::StringView::Encoding>(m_writer, "\n{} / {} Test Passed"_sv,
123 di::Styled(m_success_count, di::FormatEffect::Bold),
124 di::Styled(m_test_cases.size(), di::FormatEffect::Bold));
125 if (tests_skipped) {
126 di::writer_print<di::StringView::Encoding>(m_writer, " ({} Failed {} Skipped)"_sv,
127 di::Styled(m_fail_count, di::FormatEffect::Bold),
128 di::Styled(tests_skipped, di::FormatEffect::Bold));
129 }
131 m_writer, ": {}"_sv,
132 m_fail_count ? di::Styled("Tests Failed"_sv, di::FormatEffect::Bold | di::FormatColor::Red)
133 : di::Styled("Tests Passed"_sv, di::FormatEffect::Bold | di::FormatColor::Green));
134
135 auto result = int(m_fail_count > 0);
136 DI_ASSERT(m_did_finish);
137 m_did_finish(result);
138 }
139
140 di::Any<di::Writer> m_writer;
141 di::Function<void(int)> m_did_finish;
142 di::Vector<TestCase> m_test_cases;
143 usize m_current_test_index { 0 };
144 usize m_fail_count { 0 };
145 usize m_success_count { 0 };
146};
147}
#define DI_ASSERT(...)
Definition assert_bool.h:7
Definition style.h:131
Definition function.h:365
Definition test_case.h:9
Definition test_manager.h:11
void register_test_case(TestCase test_case)
Definition test_manager.h:18
void set_did_finish(di::Function< void(i32)> did_finish)
Definition test_manager.h:22
auto is_test_application() const -> bool
Definition test_manager.h:78
void handle_assertion_failure()
Definition test_manager.h:79
void set_writer(di::Any< di::Writer > writer)
Definition test_manager.h:20
auto run_tests(Args &args) -> di::Result< void >
Definition test_manager.h:40
static auto the() -> TestManager &
Definition test_manager.h:13
Definition optional_forward_declaration.h:5
Definition unexpected.h:14
meta::Type< AnyT< UserInterface, Storage, VTablePolicy > > Any
Definition any.h:302
constexpr auto remove_if
Definition remove_if.h:38
Definition test_case.h:6
size_t usize
Definition integers.h:33
__INT32_TYPE__ i32
Definition integers.h:16
Expected< T, Error > Result
Definition result.h:8
constexpr auto writer_print
Definition writer_print.h:20
constexpr auto writer_println
Definition writer_println.h:21
constexpr auto cli_parser(StringView app_name, StringView description)
Definition parser.h:331
Definition test_manager.h:24
bool help
Definition test_manager.h:28
di::Optional< di::TransparentStringView > case_name
Definition test_manager.h:27
di::Optional< di::TransparentStringView > suite_name
Definition test_manager.h:26
static constexpr auto get_cli_parser()
Definition test_manager.h:30
bool list_simple
Definition test_manager.h:25