broken.h (7577B)
1 // Broken - Lightweight unit testing for C++ 2 // 3 // This is free and unencumbered software released into the public domain. 4 // 5 // Anyone is free to copy, modify, publish, use, compile, sell, or 6 // distribute this software, either in source code form or as a compiled 7 // binary, for any purpose, commercial or non-commercial, and by any 8 // means. 9 // 10 // In jurisdictions that recognize copyright laws, the author or authors 11 // of this software dedicate any and all copyright interest in the 12 // software to the public domain. We make this dedication for the benefit 13 // of the public at large and to the detriment of our heirs and 14 // successors. We intend this dedication to be an overt act of 15 // relinquishment in perpetuity of all present and future rights to this 16 // software under copyright law. 17 // 18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 // IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 22 // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 23 // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 // OTHER DEALINGS IN THE SOFTWARE. 25 // 26 // For more information, please refer to <http://unlicense.org> 27 28 #ifndef BROKEN_H_INCLUDED 29 #define BROKEN_H_INCLUDED 30 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <utility> 35 36 // Hide everything when using Doxygen. Ideally this can be protected by a macro, 37 // but there is not globally and widely used one across multiple projects. 38 39 //! \cond 40 41 // Broken - API 42 // ============ 43 44 namespace BrokenAPI { 45 46 //! Entry point of a unit test defined by `UNIT` macro. 47 typedef void (*Entry)(void); 48 49 enum Flags : unsigned { 50 kFlagFinished = 0x1 51 }; 52 53 struct Unit; 54 55 bool has_arg(const char* name) noexcept; 56 57 //! Register a new unit test (called automatically by `AutoUnit` and `UNIT`). 58 void add_unit(Unit* unit) noexcept; 59 60 //! Set output file to a `file`. 61 void set_output_file(FILE* file) noexcept; 62 63 //! Initialize `Broken` framework. 64 //! 65 //! Returns `true` if `run()` should be called. 66 int run(int argc, const char* argv[], Entry on_before_run = nullptr, Entry on_after_run = nullptr); 67 68 //! Log message, adds automatically new line if not present. 69 void info(const char* fmt, ...) noexcept; 70 71 //! Called on `EXPECT()` failure. 72 void fail(const char* file, int line, const char* expression, const char* fmt, ...) noexcept; 73 74 //! Test defined by `UNIT` macro. 75 struct Unit { 76 Entry entry; 77 const char* name; 78 int priority; 79 unsigned flags; 80 Unit* next; 81 }; 82 83 //! Automatic unit registration by using static initialization. 84 class AutoUnit : public Unit { 85 public: 86 inline AutoUnit(Entry entry_, const char* name_, int priority_ = 0, int dummy_ = 0) noexcept { 87 // Not used, only to trick `UNIT()` macro. 88 (void)dummy_; 89 90 this->entry = entry_; 91 this->name = name_; 92 this->priority = priority_; 93 this->flags = 0; 94 this->next = nullptr; 95 add_unit(this); 96 } 97 }; 98 99 class Failure { 100 public: 101 const char* _file = nullptr; 102 const char* _expression = nullptr; 103 int _line = 0; 104 bool _handled = false; 105 106 inline Failure(const char* file, int line, const char* expression) noexcept 107 : _file(file), 108 _expression(expression), 109 _line(line) {} 110 111 inline ~Failure() noexcept { 112 if (!_handled) 113 fail(_file, _line, _expression, nullptr); 114 } 115 116 template<typename... Args> 117 inline void message(const char* fmt, Args&&... args) noexcept { 118 fail(_file, _line, _expression, fmt, std::forward<Args>(args)...); 119 _handled = true; 120 } 121 }; 122 123 template<typename Result> 124 static inline bool check(Result&& result) noexcept { return !!result; } 125 126 template<typename LHS, typename RHS> 127 static inline bool check_eq(LHS&& lhs, RHS&& rhs) noexcept { return lhs == rhs; } 128 129 template<typename LHS, typename RHS> 130 static inline bool check_ne(LHS&& lhs, RHS&& rhs) noexcept { return lhs != rhs; } 131 132 template<typename LHS, typename RHS> 133 static inline bool check_gt(LHS&& lhs, RHS&& rhs) noexcept { return lhs > rhs; } 134 135 template<typename LHS, typename RHS> 136 static inline bool check_ge(LHS&& lhs, RHS&& rhs) noexcept { return lhs >= rhs; } 137 138 template<typename LHS, typename RHS> 139 static inline bool check_lt(LHS&& lhs, RHS&& rhs) noexcept { return lhs < rhs; } 140 141 template<typename LHS, typename RHS> 142 static inline bool check_le(LHS&& lhs, RHS&& rhs) noexcept { return lhs <= rhs; } 143 144 template<typename Result> 145 static inline bool check_true(Result&& result) noexcept { return !!result; } 146 147 template<typename Result> 148 static inline bool check_false(Result&& result) noexcept { return !result; } 149 150 template<typename Result> 151 static inline bool check_null(Result&& result) noexcept { return result == nullptr; } 152 153 template<typename Result> 154 static inline bool check_not_null(Result&& result) noexcept { return result != nullptr; } 155 156 } // {BrokenAPI} 157 158 // Broken - Macros 159 // =============== 160 161 //! Internal macro used by `UNIT()`. 162 #define BROKEN_UNIT_INTERNAL(NAME, PRIORITY) \ 163 static void unit_##NAME##_entry(void); \ 164 static ::BrokenAPI::AutoUnit unit_##NAME##_autoinit(unit_##NAME##_entry, #NAME, PRIORITY); \ 165 static void unit_##NAME##_entry(void) 166 167 //! \def UNIT(NAME [, PRIORITY]) 168 //! 169 //! Define a unit test with an optional priority. 170 //! 171 //! `NAME` can only contain ASCII characters, numbers and underscore. It has the same rules as identifiers in C and C++. 172 //! 173 //! `PRIORITY` specifies the order in which unit tests are run. Lesses value increases the priority. At the moment all 174 //!units are first sorted by priority and then by name - this makes the run always deterministic. 175 #define UNIT(NAME, ...) BROKEN_UNIT_INTERNAL(NAME, __VA_ARGS__ + 0) 176 177 //! #define INFO(FORMAT [, ...]) 178 //! 179 //! Informative message printed to `stdout`. 180 #define INFO(...) ::BrokenAPI::info(__VA_ARGS__) 181 182 #define BROKEN_EXPECT_INTERNAL(file, line, expression, result) \ 183 for (bool _test_internal_result = (result); !_test_internal_result; _test_internal_result = true) \ 184 ::BrokenAPI::Failure(file, line, expression) 185 186 #define EXPECT(...) BROKEN_EXPECT_INTERNAL(__FILE__, __LINE__, "EXPECT(" #__VA_ARGS__ ")", !!(__VA_ARGS__)) 187 #define EXPECT_EQ(...) BROKEN_EXPECT_INTERNAL(__FILE__, __LINE__, "EXPECT_EQ(" #__VA_ARGS__ ")", ::BrokenAPI::check_eq(__VA_ARGS__)) 188 #define EXPECT_NE(...) BROKEN_EXPECT_INTERNAL(__FILE__, __LINE__, "EXPECT_NE(" #__VA_ARGS__ ")", ::BrokenAPI::check_ne(__VA_ARGS__)) 189 #define EXPECT_GT(...) BROKEN_EXPECT_INTERNAL(__FILE__, __LINE__, "EXPECT_GT(" #__VA_ARGS__ ")", ::BrokenAPI::check_gt(__VA_ARGS__)) 190 #define EXPECT_GE(...) BROKEN_EXPECT_INTERNAL(__FILE__, __LINE__, "EXPECT_GE(" #__VA_ARGS__ ")", ::BrokenAPI::check_ge(__VA_ARGS__)) 191 #define EXPECT_LT(...) BROKEN_EXPECT_INTERNAL(__FILE__, __LINE__, "EXPECT_LT(" #__VA_ARGS__ ")", ::BrokenAPI::check_lt(__VA_ARGS__)) 192 #define EXPECT_LE(...) BROKEN_EXPECT_INTERNAL(__FILE__, __LINE__, "EXPECT_LE(" #__VA_ARGS__ ")", ::BrokenAPI::check_le(__VA_ARGS__)) 193 #define EXPECT_TRUE(...) BROKEN_EXPECT_INTERNAL(__FILE__, __LINE__, "EXPECT_TRUE(" #__VA_ARGS__ ")", ::BrokenAPI::check_true(__VA_ARGS__)) 194 #define EXPECT_FALSE(...) BROKEN_EXPECT_INTERNAL(__FILE__, __LINE__, "EXPECT_FALSE(" #__VA_ARGS__ ")", ::BrokenAPI::check_false(__VA_ARGS__)) 195 #define EXPECT_NULL(...) BROKEN_EXPECT_INTERNAL(__FILE__, __LINE__, "EXPECT_NULL(" #__VA_ARGS__ ")", ::BrokenAPI::check_null(__VA_ARGS__)) 196 #define EXPECT_NOT_NULL(...) BROKEN_EXPECT_INTERNAL(__FILE__, __LINE__, "EXPECT_NOT_NULL(" #__VA_ARGS__ ")", ::BrokenAPI::check_not_null(__VA_ARGS__)) 197 198 //! \endcond 199 200 #endif // BROKEN_H_INCLUDED