HART  0.2.0
High level Audio Regression and Testing
Loading...
Searching...
No Matches
hart_test_registry.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm> // shuffle()
4#include <iostream>
5#include <random>
6#include <string>
7#include <unordered_set>
8#include <vector>
9
14
15namespace hart
16{
17
18/// @brief Determines whether the task is a test or a generator
19/// @private
20enum class TaskCategory
21{
22 test,
23 generate
24};
25
26/// @brief Runs the test cases
27/// @details For internal use by HART. You're not supposed to interact with it directly, only through the macros
28/// such as @ref HART_RUN_ALL_TESTS(), @ref HART_TEST(), @ref HART_TEST_WITH_TAGS(), @ref HART_GENERATE(), @ref HART_GENERATE_WITH_TAGS()
29/// @ingroup TestRunner
31public:
32
33 /// @brief Gets the singleton instance
35 {
36 static TestRegistry reg;
37 return reg;
38 }
39
40 /// @brief Adds a task (test or generator)
41 /// @details Gets called when a test case is declared with a macro like @ref HART_TEST()
42 /// @private
43 void add (const std::string& name, const std::string& tags, TaskCategory testCategory, void (*func)())
44 {
45 std::unordered_set<std::string>& registeredNamesContainer =
46 testCategory == TaskCategory::test
47 ? registeredTestNames
48 : registeredGeneratorNames;
49
50 const auto insertResult = registeredNamesContainer.insert (name);
51 const bool isDuplicate = ! insertResult.second;
52
53 if (isDuplicate)
54 HART_THROW_OR_RETURN_VOID (hart::ValueError, std::string ("Duplicate test case name found: ") + name);
55
56 std::vector<TaskInfo>& tasks =
57 testCategory == TaskCategory::test
58 ? tests
59 : generators;
60
61 tasks.emplace_back (TaskInfo {name, tags, func});
62 }
63
64 /// @brief Runs all tests or generators
65 int runAll()
66 {
67 // TODO: Make data root dir if set, but doesn't exist
68 // TODO: Add support for runnings tasks in a thread pool
69
70 std::cout << hartAsciiArt << std::endl;
71
72 std::vector<TaskInfo>& tasks =
74 ? generators
75 : tests;
76
77 if (tasks.size() == 0)
78 {
79 std::cout << "Nothing to run!" << std::endl;
80 return 0;
81 }
82
84 shuffleTasks (tasks);
85
86 for (const TaskInfo& task : tasks)
87 runTask (task);
88
89 std::cout << std::endl;
90 std::cout << "[ PASSED ] " << tasksPassed << '/' << tasks.size() << std::endl;
91
92 if (tasksFailed > 0)
93 std::cout << "[ FAILED ] " << tasksFailed << '/' << tasks.size() << std::endl;
94
95 const char* resultAsciiArt = tasksFailed > 0 ? failAsciiArt : passAsciiArt;
96 std::cout << std::endl << resultAsciiArt << std::endl;
97 return (int) (tasksFailed != 0);
98 }
99
100private:
101 struct TaskInfo
102 {
103 std::string name;
104 std::string tags;
105 void (*func)();
106 };
107
108 TestRegistry() = default; // Private ctor for singleton
109 std::vector<TaskInfo> tests;
110 std::vector<TaskInfo> generators;
111 std::unordered_set<std::string> registeredTestNames;
112 std::unordered_set<std::string> registeredGeneratorNames;
113
114 size_t tasksPassed = 0;
115 size_t tasksFailed = 0;
116
117 void runTask (const TaskInfo& task)
118 {
119 std::cout << "[ ... ] Running " << task.name;
120 bool assertionFailed = false;
121 std::string assertionFailMessage;
123
124 try
125 {
126 task.func();
127 }
128 catch (const hart::TestAssertException& e)
129 {
130 assertionFailMessage = e.what();
131 assertionFailed = true;
132 }
133 catch (const hart::ConfigurationError& e)
134 {
135 assertionFailMessage = e.what();
136 assertionFailed = true;
137 }
138
139 // TODO: Output test durations
140
141 std::cout << '\r';
142 const bool expectationsFailed = ExpectationFailureMessages::get().size() > 0;
143
144 if (assertionFailed || expectationsFailed)
145 {
146 constexpr char separator[] = "-------------------------------------------";
147 std::cout << "[ </3 ] " << task.name << " - failed" << std::endl;
148
149 if (assertionFailed)
150 {
151 std::cout << separator << std::endl << assertionFailMessage << std::endl;
152 }
153
154 for (const std::string& expectationFailureMessage : ExpectationFailureMessages::get())
155 {
156 std::cout << separator << std::endl << expectationFailureMessage << std::endl;
157 }
158
159 std::cout << separator << std::endl;
160 ++tasksFailed;
161 }
162 else
163 {
164 std::cout << "[ <3 ] " << task.name << " - passed" << std::endl;
165 ++tasksPassed;
166 }
167 }
168
169 static void shuffleTasks (std::vector<TaskInfo>& tasks)
170 {
172 std::shuffle (tasks.begin(), tasks.end(), rng);
173 }
174};
175
176} // namespace hart
static std::vector< std::string > & get()
Runs the test cases.
static TestRegistry & getInstance()
Gets the singleton instance.
int runAll()
Runs all tests or generators.
#define HART_THROW_OR_RETURN_VOID(ExceptionType, message)
static const char * hartAsciiArt
static const char * passAsciiArt
static const char * failAsciiArt
uint_fast32_t getRandomSeed()
static CLIConfig & getInstance()