1 | // [Broken] |
2 | // Lightweight Unit Testing for C++. |
3 | // |
4 | // [License] |
5 | // Public Domain (Unlicense) |
6 | |
7 | #ifndef BROKEN_INTERNAL_H |
8 | #define BROKEN_INTERNAL_H |
9 | |
10 | #include <stdio.h> |
11 | #include <stdlib.h> |
12 | #include <string.h> |
13 | #include <utility> |
14 | |
15 | // Hide everything when using Doxygen. Ideally this can be protected by a macro, |
16 | // but there is not globally and widely used one across multiple projects. |
17 | |
18 | //! \internal |
19 | //! \{ |
20 | |
21 | // ============================================================================ |
22 | // [Broken - API] |
23 | // ============================================================================ |
24 | |
25 | struct BrokenAPI { |
26 | //! Entry point of a unit test defined by `UNIT` macro. |
27 | typedef void (*Entry)(void); |
28 | |
29 | //! Test defined by `UNIT` macro. |
30 | struct Unit { |
31 | const char* name; |
32 | Entry entry; |
33 | size_t finished; |
34 | Unit* next; |
35 | }; |
36 | |
37 | //! Automatic unit registration by using static initialization. |
38 | struct AutoUnit : Unit { |
39 | inline AutoUnit(const char* _name, Entry _entry) noexcept { |
40 | name = _name; |
41 | entry = _entry; |
42 | finished = false; |
43 | next = NULL; |
44 | |
45 | BrokenAPI::add(this); |
46 | } |
47 | }; |
48 | |
49 | static bool hasArg(const char* name) noexcept; |
50 | |
51 | //! Register a new unit test (called automatically by `AutoUnit` and `UNIT`). |
52 | static void add(Unit* unit) noexcept; |
53 | |
54 | //! Set output file to a `file`. |
55 | static void setOutputFile(FILE* file) noexcept; |
56 | |
57 | //! Initialize `Broken` framework. |
58 | //! |
59 | //! Returns `true` if `run()` should be called. |
60 | static int run(int argc, const char* argv[], Entry onBeforeRun = nullptr, Entry onAfterRun = nullptr) noexcept; |
61 | |
62 | //! Log message, adds automatically new line if not present. |
63 | static void info(const char* fmt, ...) noexcept; |
64 | |
65 | //! Called on `EXPECT()` failure. |
66 | static void fail(const char* file, int line, const char* fmt, ...) noexcept; |
67 | |
68 | //! Used internally by `EXPECT` macro. |
69 | template<typename T> |
70 | static inline void expect(const char* file, int line, const T& exp) noexcept { |
71 | if (!exp) |
72 | fail(file, line, nullptr); |
73 | } |
74 | |
75 | //! Used internally by `EXPECT` macro. |
76 | template<typename T, typename... Args> |
77 | static inline void expect(const char* file, int line, const T& exp, const char* fmt, Args&&... args) noexcept { |
78 | if (!exp) |
79 | fail(file, line, fmt, std::forward<Args>(args)...); |
80 | } |
81 | }; |
82 | |
83 | // ============================================================================ |
84 | // [Broken - Macros] |
85 | // ============================================================================ |
86 | |
87 | //! Define a unit test. |
88 | //! |
89 | //! `NAME` can only contain ASCII characters, numbers and underscore. It has |
90 | //! the same rules as identifiers in C and C++. |
91 | #define UNIT(NAME) \ |
92 | static void unit_##NAME##_entry(void) noexcept; \ |
93 | \ |
94 | static ::BrokenAPI::AutoUnit unit_##NAME##_autoinit( \ |
95 | #NAME, unit_##NAME##_entry); \ |
96 | \ |
97 | static void unit_##NAME##_entry(void) noexcept |
98 | |
99 | //! #define INFO(FORMAT [, ...]) |
100 | //! |
101 | //! Informative message printed to `stdout`. |
102 | #define INFO(...) ::BrokenAPI::info(__VA_ARGS__) |
103 | |
104 | //! #define INFO(EXP [, FORMAT [, ...]]) |
105 | //! |
106 | //! Expect `EXP` to be true or evaluates to true, fail otherwise. |
107 | #define EXPECT(...) ::BrokenAPI::expect(__FILE__, __LINE__, __VA_ARGS__) |
108 | |
109 | //! \} |
110 | |
111 | #endif // BROKEN_INTERNAL_H |
112 | |