34#if defined(__cpp_exceptions) || defined(__EXCEPTIONS)
42#ifndef FLTEST_MAX_SUBCASE_DEPTH
43#define FLTEST_MAX_SUBCASE_DEPTH 8
46#ifndef FLTEST_MAX_TEST_CASES
47#define FLTEST_MAX_TEST_CASES 512
139 return mLine == other.mLine &&
145 return !(*
this == other);
152 for (
const char* p = sig.mName; *p; ++p) {
153 hash = hash * 31 +
static_cast<fl::u32
>(*p);
155 for (
const char* p = sig.mFile; *p; ++p) {
156 hash = hash * 31 +
static_cast<fl::u32
>(*p);
158 hash = hash * 31 +
static_cast<fl::u32
>(sig.mLine);
196 int run(
int argc = 0,
const char*
const* argv =
nullptr)
FL_NOEXCEPT;
216 void checkFailed(
const char* expr,
const char* file,
int line)
FL_NOEXCEPT;
217 void requireFailed(
const char* expr,
const char* file,
int line)
FL_NOEXCEPT;
325template<
typename L,
typename R>
330template<
typename L,
typename R>
335template<
typename L,
typename R>
340template<
typename L,
typename R>
345template<
typename L,
typename R>
350template<
typename L,
typename R>
356template<
typename L,
typename R,
typename Cmp>
358 const char* lhsExpr,
const char* op,
const char* rhsExpr,
360 bool result = cmp(lhs, rhs);
366 exprSS << lhsExpr <<
" " << op <<
" " << rhsExpr;
371 expandedSS << lhs <<
" " << op <<
" " << rhs;
415 mMargin = newMargin < 0 ? 0 : newMargin;
432 double diff = lhs - rhs.mValue;
433 if (diff < 0) diff = -diff;
436 if (rhs.mMargin > 0.0 && diff <= rhs.mMargin) {
441 double lhsAbs = lhs < 0 ? -lhs : lhs;
442 double rhsAbs = rhs.mValue < 0 ? -rhs.mValue : rhs.mValue;
443 double maxAbs = lhsAbs > rhsAbs ? lhsAbs : rhsAbs;
444 double relativeMargin = rhs.mEpsilon * (rhs.mScale + maxAbs);
445 return diff <= relativeMargin;
453 return !(lhs == rhs);
457 return !(lhs == rhs);
461 return lhs < rhs.mValue || lhs == rhs;
465 return lhs > rhs.mValue || lhs == rhs;
469 return lhs < rhs.mValue && !(lhs == rhs);
473 return lhs > rhs.mValue && !(lhs == rhs);
485 os <<
"Approx(" << approx.value() <<
")";
494void outputMessage(
const char* msg,
const char* file,
int line)
FL_NOEXCEPT;
497void outputCapture(
const char* name,
const char*
value,
const char* file,
int line)
FL_NOEXCEPT;
500void fail(
const char* msg,
const char* file,
int line,
bool isFatal)
FL_NOEXCEPT;
527 void testCaseStart(const
char* name)
FL_NOEXCEPT override;
528 void testCaseEnd(
bool passed,
fl::u32 durationMs = 0)
FL_NOEXCEPT override;
529 void subcaseStart(const
char* name)
FL_NOEXCEPT override;
564 void testCaseStart(const
char* name)
FL_NOEXCEPT override;
565 void testCaseEnd(
bool passed,
fl::u32 durationMs = 0)
FL_NOEXCEPT override;
566 void subcaseStart(const
char* name)
FL_NOEXCEPT override;
609 void testCaseStart(const
char* name)
FL_NOEXCEPT override;
610 void testCaseEnd(
bool passed,
fl::u32 durationMs = 0)
FL_NOEXCEPT override;
611 void subcaseStart(const
char* name)
FL_NOEXCEPT override;
659 void testCaseStart(const
char* name)
FL_NOEXCEPT override;
660 void testCaseEnd(
bool passed,
fl::u32 durationMs = 0)
FL_NOEXCEPT override;
661 void subcaseStart(const
char* name)
FL_NOEXCEPT override;
704#define FLTEST_CAT_IMPL(a, b) a##b
705#define FLTEST_CAT(a, b) FLTEST_CAT_IMPL(a, b)
709#define FLTEST_UNIQUE(x) FLTEST_CAT(x, __LINE__)
713#define FL_TEST_CASE(name) \
714 static void FLTEST_UNIQUE(FLTEST_FUNC_)(); \
715 static fl::test::TestRegistrar FLTEST_UNIQUE(FLTEST_REG_)( \
716 FLTEST_UNIQUE(FLTEST_FUNC_), name, __FILE__, __LINE__); \
717 static void FLTEST_UNIQUE(FLTEST_FUNC_)()
720#define FL_SUBCASE(name) \
721 if (const fl::test::Subcase& FLTEST_UNIQUE(FLTEST_SUB_) = \
722 fl::test::Subcase(name, __FILE__, __LINE__))
725#define FL_CHECK(expr) \
728 fl::test::TestContext::instance().checkFailed(#expr, __FILE__, __LINE__); \
730 fl::test::AssertResult ar(true); \
731 ar.mExpression = #expr; \
732 ar.mLocation = fl::test::SourceLocation(__FILE__, __LINE__); \
733 fl::test::TestContext::instance().reportAssert(ar); \
737#define FL_CHECK_FALSE(expr) \
740 fl::test::TestContext::instance().checkFailed("!(" #expr ")", __FILE__, __LINE__); \
742 fl::test::AssertResult ar(true); \
743 ar.mExpression = "!(" #expr ")"; \
744 ar.mLocation = fl::test::SourceLocation(__FILE__, __LINE__); \
745 fl::test::TestContext::instance().reportAssert(ar); \
749#define FL_REQUIRE(expr) \
752 fl::test::TestContext::instance().requireFailed(#expr, __FILE__, __LINE__); \
755 fl::test::AssertResult ar(true); \
756 ar.mExpression = #expr; \
757 ar.mLocation = fl::test::SourceLocation(__FILE__, __LINE__); \
758 fl::test::TestContext::instance().reportAssert(ar); \
762#define FL_REQUIRE_FALSE(expr) \
765 fl::test::TestContext::instance().requireFailed("!(" #expr ")", __FILE__, __LINE__); \
768 fl::test::AssertResult ar(true); \
769 ar.mExpression = "!(" #expr ")"; \
770 ar.mLocation = fl::test::SourceLocation(__FILE__, __LINE__); \
771 fl::test::TestContext::instance().reportAssert(ar); \
776#define FL_CHECK_EQ(lhs, rhs) \
777 fl::test::binaryAssert(lhs, rhs, fl::test::CompareEq<fl::decay_t<decltype(lhs)>, fl::decay_t<decltype(rhs)>>{}, \
778 #lhs, "==", #rhs, __FILE__, __LINE__)
780#define FL_CHECK_NE(lhs, rhs) \
781 fl::test::binaryAssert(lhs, rhs, fl::test::CompareNe<fl::decay_t<decltype(lhs)>, fl::decay_t<decltype(rhs)>>{}, \
782 #lhs, "!=", #rhs, __FILE__, __LINE__)
784#define FL_CHECK_LT(lhs, rhs) \
785 fl::test::binaryAssert(lhs, rhs, fl::test::CompareLt<fl::decay_t<decltype(lhs)>, fl::decay_t<decltype(rhs)>>{}, \
786 #lhs, "<", #rhs, __FILE__, __LINE__)
788#define FL_CHECK_GT(lhs, rhs) \
789 fl::test::binaryAssert(lhs, rhs, fl::test::CompareGt<fl::decay_t<decltype(lhs)>, fl::decay_t<decltype(rhs)>>{}, \
790 #lhs, ">", #rhs, __FILE__, __LINE__)
792#define FL_CHECK_LE(lhs, rhs) \
793 fl::test::binaryAssert(lhs, rhs, fl::test::CompareLe<fl::decay_t<decltype(lhs)>, fl::decay_t<decltype(rhs)>>{}, \
794 #lhs, "<=", #rhs, __FILE__, __LINE__)
796#define FL_CHECK_GE(lhs, rhs) \
797 fl::test::binaryAssert(lhs, rhs, fl::test::CompareGe<fl::decay_t<decltype(lhs)>, fl::decay_t<decltype(rhs)>>{}, \
798 #lhs, ">=", #rhs, __FILE__, __LINE__)
800#define FL_REQUIRE_EQ(lhs, rhs) \
802 if (!FL_CHECK_EQ(lhs, rhs)) return; \
805#define FL_REQUIRE_NE(lhs, rhs) \
807 if (!FL_CHECK_NE(lhs, rhs)) return; \
810#define FL_REQUIRE_LT(lhs, rhs) \
812 if (!FL_CHECK_LT(lhs, rhs)) return; \
815#define FL_REQUIRE_GT(lhs, rhs) \
817 if (!FL_CHECK_GT(lhs, rhs)) return; \
820#define FL_REQUIRE_LE(lhs, rhs) \
822 if (!FL_CHECK_LE(lhs, rhs)) return; \
825#define FL_REQUIRE_GE(lhs, rhs) \
827 if (!FL_CHECK_GE(lhs, rhs)) return; \
835#define FL_MESSAGE(msg) \
837 fl::sstream FLTEST_UNIQUE(ss_); \
838 FLTEST_UNIQUE(ss_) << msg; \
839 fl::test::outputMessage(FLTEST_UNIQUE(ss_).str().c_str(), __FILE__, __LINE__); \
845#define FL_INFO(msg) FL_MESSAGE(msg)
849#define FL_CAPTURE(x) \
851 fl::sstream FLTEST_UNIQUE(ss_); \
852 FLTEST_UNIQUE(ss_) << (x); \
853 fl::test::outputCapture(#x, FLTEST_UNIQUE(ss_).str().c_str(), __FILE__, __LINE__); \
857#define FL_FAIL(msg) \
859 fl::sstream FLTEST_UNIQUE(ss_); \
860 FLTEST_UNIQUE(ss_) << msg; \
861 fl::test::fail(FLTEST_UNIQUE(ss_).str().c_str(), __FILE__, __LINE__, true); \
866#define FL_FAIL_CHECK(msg) \
868 fl::sstream FLTEST_UNIQUE(ss_); \
869 FLTEST_UNIQUE(ss_) << msg; \
870 fl::test::fail(FLTEST_UNIQUE(ss_).str().c_str(), __FILE__, __LINE__, false); \
876#define FL_WARN(expr) \
879 fl::test::outputMessage("Warning: " #expr " is false", __FILE__, __LINE__); \
885#define FL_WARN_FALSE(expr) \
888 fl::test::outputMessage("Warning: !(" #expr ") is false", __FILE__, __LINE__); \
897#define FL_WARN_EQ(lhs, rhs) \
899 if (!((lhs) == (rhs))) { \
900 fl::sstream _fl_warn_ss; \
901 _fl_warn_ss << "Warning: " << #lhs << " == " << #rhs \
902 << " failed: " << (lhs) << " != " << (rhs); \
903 fl::test::outputMessage(_fl_warn_ss.str().c_str(), __FILE__, __LINE__); \
907#define FL_WARN_NE(lhs, rhs) \
909 if (!((lhs) != (rhs))) { \
910 fl::sstream _fl_warn_ss; \
911 _fl_warn_ss << "Warning: " << #lhs << " != " << #rhs \
912 << " failed: both equal " << (lhs); \
913 fl::test::outputMessage(_fl_warn_ss.str().c_str(), __FILE__, __LINE__); \
917#define FL_WARN_LT(lhs, rhs) \
919 if (!((lhs) < (rhs))) { \
920 fl::sstream _fl_warn_ss; \
921 _fl_warn_ss << "Warning: " << #lhs << " < " << #rhs \
922 << " failed: " << (lhs) << " >= " << (rhs); \
923 fl::test::outputMessage(_fl_warn_ss.str().c_str(), __FILE__, __LINE__); \
927#define FL_WARN_GT(lhs, rhs) \
929 if (!((lhs) > (rhs))) { \
930 fl::sstream _fl_warn_ss; \
931 _fl_warn_ss << "Warning: " << #lhs << " > " << #rhs \
932 << " failed: " << (lhs) << " <= " << (rhs); \
933 fl::test::outputMessage(_fl_warn_ss.str().c_str(), __FILE__, __LINE__); \
937#define FL_WARN_LE(lhs, rhs) \
939 if (!((lhs) <= (rhs))) { \
940 fl::sstream _fl_warn_ss; \
941 _fl_warn_ss << "Warning: " << #lhs << " <= " << #rhs \
942 << " failed: " << (lhs) << " > " << (rhs); \
943 fl::test::outputMessage(_fl_warn_ss.str().c_str(), __FILE__, __LINE__); \
947#define FL_WARN_GE(lhs, rhs) \
949 if (!((lhs) >= (rhs))) { \
950 fl::sstream _fl_warn_ss; \
951 _fl_warn_ss << "Warning: " << #lhs << " >= " << #rhs \
952 << " failed: " << (lhs) << " < " << (rhs); \
953 fl::test::outputMessage(_fl_warn_ss.str().c_str(), __FILE__, __LINE__); \
959#define FL_SKIP(reason) \
961 fl::test::skipTest(reason, __FILE__, __LINE__); \
971#define FL_CHECK_MESSAGE(expr, msg) \
974 fl::sstream FLTEST_UNIQUE(ss_); \
975 FLTEST_UNIQUE(ss_) << msg; \
976 fl::test::AssertResult ar(false); \
977 ar.mExpression = #expr; \
978 ar.mExpanded = FLTEST_UNIQUE(ss_).str(); \
979 ar.mLocation = fl::test::SourceLocation(__FILE__, __LINE__); \
980 fl::test::TestContext::instance().reportAssert(ar); \
982 fl::test::AssertResult ar(true); \
983 ar.mExpression = #expr; \
984 ar.mLocation = fl::test::SourceLocation(__FILE__, __LINE__); \
985 fl::test::TestContext::instance().reportAssert(ar); \
989#define FL_REQUIRE_MESSAGE(expr, msg) \
992 fl::sstream FLTEST_UNIQUE(ss_); \
993 FLTEST_UNIQUE(ss_) << msg; \
994 fl::test::AssertResult ar(false); \
995 ar.mExpression = #expr; \
996 ar.mExpanded = FLTEST_UNIQUE(ss_).str(); \
997 ar.mLocation = fl::test::SourceLocation(__FILE__, __LINE__); \
998 fl::test::TestContext::instance().reportAssert(ar); \
1001 fl::test::AssertResult ar(true); \
1002 ar.mExpression = #expr; \
1003 ar.mLocation = fl::test::SourceLocation(__FILE__, __LINE__); \
1004 fl::test::TestContext::instance().reportAssert(ar); \
1019 static const char* name =
nullptr;
1043#define FL_TEST_SUITE(name) \
1044 namespace FLTEST_UNIQUE(FLTEST_SUITE_NS_) { \
1045 static fl::test::detail::SuiteScope FLTEST_UNIQUE(FLTEST_SUITE_SCOPE_)(name); \
1047 namespace FLTEST_UNIQUE(FLTEST_SUITE_NS_)
1058#define FL_TEST_SUITE_BEGIN(name) \
1060 static fl::test::detail::SuiteScope FLTEST_UNIQUE(FLTEST_SUITE_SCOPE_)(name);
1062#define FL_TEST_SUITE_END() \
1071#define FL_SCENARIO(name) FL_TEST_CASE("Scenario: " name)
1072#define FL_GIVEN(name) FL_SUBCASE("Given: " name)
1073#define FL_WHEN(name) FL_SUBCASE("When: " name)
1074#define FL_AND_WHEN(name) FL_SUBCASE("And when: " name)
1075#define FL_THEN(name) FL_SUBCASE("Then: " name)
1076#define FL_AND_THEN(name) FL_SUBCASE("And: " name)
1083#define FL_CHECK_CLOSE(a, b, epsilon) \
1087 auto _fl_diff = _fl_a - _fl_b; \
1088 if (_fl_diff < 0) _fl_diff = -_fl_diff; \
1089 bool _fl_result = _fl_diff <= (epsilon); \
1090 fl::test::AssertResult ar(_fl_result); \
1091 ar.mLocation = fl::test::SourceLocation(__FILE__, __LINE__); \
1093 ss << #a << " ~= " << #b << " (eps=" << (epsilon) << ")"; \
1094 ar.mExpression = ss.str(); \
1095 if (!_fl_result) { \
1097 ess << _fl_a << " ~= " << _fl_b << " (diff=" << _fl_diff << ")"; \
1098 ar.mExpanded = ess.str(); \
1100 fl::test::TestContext::instance().reportAssert(ar); \
1103#define FL_REQUIRE_CLOSE(a, b, epsilon) \
1107 auto _fl_diff = _fl_a - _fl_b; \
1108 if (_fl_diff < 0) _fl_diff = -_fl_diff; \
1109 bool _fl_result = _fl_diff <= (epsilon); \
1110 fl::test::AssertResult ar(_fl_result); \
1111 ar.mLocation = fl::test::SourceLocation(__FILE__, __LINE__); \
1113 ss << #a << " ~= " << #b << " (eps=" << (epsilon) << ")"; \
1114 ar.mExpression = ss.str(); \
1115 if (!_fl_result) { \
1117 ess << _fl_a << " ~= " << _fl_b << " (diff=" << _fl_diff << ")"; \
1118 ar.mExpanded = ess.str(); \
1119 fl::test::TestContext::instance().reportAssert(ar); \
1122 fl::test::TestContext::instance().reportAssert(ar); \
1143#define FL_TEST_CASE_FIXTURE(fixture, name) \
1144 struct FLTEST_UNIQUE(FLTEST_FIXTURE_) : public fixture { \
1147 static void FLTEST_UNIQUE(FLTEST_FIXTURE_FUNC_)() { \
1148 FLTEST_UNIQUE(FLTEST_FIXTURE_) instance; \
1151 static fl::test::TestRegistrar FLTEST_UNIQUE(FLTEST_FIXTURE_REG_)( \
1152 FLTEST_UNIQUE(FLTEST_FIXTURE_FUNC_), name, __FILE__, __LINE__); \
1153 void FLTEST_UNIQUE(FLTEST_FIXTURE_)::run()
1160#define FL_CHECK_STR_EQ(a, b) \
1162 fl::string _fl_a_str(a); \
1163 fl::string _fl_b_str(b); \
1164 bool _fl_result = (_fl_a_str == _fl_b_str); \
1165 fl::test::AssertResult ar(_fl_result); \
1166 ar.mLocation = fl::test::SourceLocation(__FILE__, __LINE__); \
1168 ss << #a << " == " << #b; \
1169 ar.mExpression = ss.str(); \
1170 if (!_fl_result) { \
1172 ess << "\"" << _fl_a_str << "\" != \"" << _fl_b_str << "\""; \
1173 ar.mExpanded = ess.str(); \
1175 fl::test::TestContext::instance().reportAssert(ar); \
1178#define FL_CHECK_STR_NE(a, b) \
1180 fl::string _fl_a_str(a); \
1181 fl::string _fl_b_str(b); \
1182 bool _fl_result = (_fl_a_str != _fl_b_str); \
1183 fl::test::AssertResult ar(_fl_result); \
1184 ar.mLocation = fl::test::SourceLocation(__FILE__, __LINE__); \
1186 ss << #a << " != " << #b; \
1187 ar.mExpression = ss.str(); \
1188 if (!_fl_result) { \
1190 ess << "Both equal: \"" << _fl_a_str << "\""; \
1191 ar.mExpanded = ess.str(); \
1193 fl::test::TestContext::instance().reportAssert(ar); \
1197#define FL_CHECK_STR_CONTAINS(haystack, needle) \
1199 fl::string _fl_haystack(haystack); \
1200 fl::string _fl_needle(needle); \
1201 bool _fl_result = (_fl_haystack.find(_fl_needle) != fl::string::npos); \
1202 fl::test::AssertResult ar(_fl_result); \
1203 ar.mLocation = fl::test::SourceLocation(__FILE__, __LINE__); \
1204 fl::sstream _fl_ss; \
1205 _fl_ss << #haystack << " contains " << #needle; \
1206 ar.mExpression = _fl_ss.str(); \
1207 if (!_fl_result) { \
1208 fl::sstream _fl_ess; \
1209 _fl_ess << "\"" << _fl_haystack << "\" does not contain \"" << _fl_needle << "\""; \
1210 ar.mExpanded = _fl_ess.str(); \
1212 fl::test::TestContext::instance().reportAssert(ar); \
1215#define FL_REQUIRE_STR_EQ(a, b) \
1217 fl::string _fl_a_str(a); \
1218 fl::string _fl_b_str(b); \
1219 bool _fl_result = (_fl_a_str == _fl_b_str); \
1220 fl::test::AssertResult ar(_fl_result); \
1221 ar.mLocation = fl::test::SourceLocation(__FILE__, __LINE__); \
1223 ss << #a << " == " << #b; \
1224 ar.mExpression = ss.str(); \
1225 if (!_fl_result) { \
1227 ess << "\"" << _fl_a_str << "\" != \"" << _fl_b_str << "\""; \
1228 ar.mExpanded = ess.str(); \
1229 fl::test::TestContext::instance().reportAssert(ar); \
1232 fl::test::TestContext::instance().reportAssert(ar); \
1235#define FL_REQUIRE_STR_CONTAINS(haystack, needle) \
1237 fl::string _fl_haystack(haystack); \
1238 fl::string _fl_needle(needle); \
1239 bool _fl_result = (_fl_haystack.find(_fl_needle) != fl::string::npos); \
1240 fl::test::AssertResult ar(_fl_result); \
1241 ar.mLocation = fl::test::SourceLocation(__FILE__, __LINE__); \
1242 fl::sstream _fl_ss; \
1243 _fl_ss << #haystack << " contains " << #needle; \
1244 ar.mExpression = _fl_ss.str(); \
1245 if (!_fl_result) { \
1246 fl::sstream _fl_ess; \
1247 _fl_ess << "\"" << _fl_haystack << "\" does not contain \"" << _fl_needle << "\""; \
1248 ar.mExpanded = _fl_ess.str(); \
1249 fl::test::TestContext::instance().reportAssert(ar); \
1252 fl::test::TestContext::instance().reportAssert(ar); \
1263#define FL_CHECK_ARRAY_EQ(actual, expected, arrsize) \
1265 bool _fl_arr_match = true; \
1266 fl::size _fl_arr_mismatch_idx = 0; \
1268 fl::size _fl_arr_i = 0; \
1269 fl::size _fl_arr_sz = static_cast<fl::size>(arrsize); \
1270 while (_fl_arr_i < _fl_arr_sz) { \
1271 if (!((actual)[_fl_arr_i] == (expected)[_fl_arr_i])) { \
1272 _fl_arr_match = false; \
1273 _fl_arr_mismatch_idx = _fl_arr_i; \
1279 fl::test::AssertResult _fl_arr_ar(_fl_arr_match); \
1280 _fl_arr_ar.mLocation = fl::test::SourceLocation(__FILE__, __LINE__); \
1281 fl::sstream _fl_arr_ss; \
1282 _fl_arr_ss << #actual << " == " << #expected << " (size=" << (arrsize) << ")"; \
1283 _fl_arr_ar.mExpression = _fl_arr_ss.str(); \
1284 if (!_fl_arr_match) { \
1285 fl::sstream _fl_arr_ess; \
1286 _fl_arr_ess << "Mismatch at index " << _fl_arr_mismatch_idx \
1287 << ": " << (actual)[_fl_arr_mismatch_idx] \
1288 << " != " << (expected)[_fl_arr_mismatch_idx]; \
1289 _fl_arr_ar.mExpanded = _fl_arr_ess.str(); \
1291 fl::test::TestContext::instance().reportAssert(_fl_arr_ar); \
1294#define FL_REQUIRE_ARRAY_EQ(actual, expected, arrsize) \
1296 bool _fl_arr_match = true; \
1297 fl::size _fl_arr_mismatch_idx = 0; \
1299 fl::size _fl_arr_i = 0; \
1300 fl::size _fl_arr_sz = static_cast<fl::size>(arrsize); \
1301 while (_fl_arr_i < _fl_arr_sz) { \
1302 if (!((actual)[_fl_arr_i] == (expected)[_fl_arr_i])) { \
1303 _fl_arr_match = false; \
1304 _fl_arr_mismatch_idx = _fl_arr_i; \
1310 fl::test::AssertResult _fl_arr_ar(_fl_arr_match); \
1311 _fl_arr_ar.mLocation = fl::test::SourceLocation(__FILE__, __LINE__); \
1312 fl::sstream _fl_arr_ss; \
1313 _fl_arr_ss << #actual << " == " << #expected << " (size=" << (arrsize) << ")"; \
1314 _fl_arr_ar.mExpression = _fl_arr_ss.str(); \
1315 if (!_fl_arr_match) { \
1316 fl::sstream _fl_arr_ess; \
1317 _fl_arr_ess << "Mismatch at index " << _fl_arr_mismatch_idx \
1318 << ": " << (actual)[_fl_arr_mismatch_idx] \
1319 << " != " << (expected)[_fl_arr_mismatch_idx]; \
1320 _fl_arr_ar.mExpanded = _fl_arr_ess.str(); \
1321 fl::test::TestContext::instance().reportAssert(_fl_arr_ar); \
1324 fl::test::TestContext::instance().reportAssert(_fl_arr_ar); \
1338#if defined(__cpp_exceptions) || defined(__EXCEPTIONS)
1339#define FLTEST_EXCEPTIONS_ENABLED 1
1341#define FLTEST_EXCEPTIONS_ENABLED 0
1344#if FLTEST_EXCEPTIONS_ENABLED
1346#define FL_CHECK_THROWS(expr) \
1348 bool _fl_threw = false; \
1349 try { (void)(expr); } \
1350 catch (...) { _fl_threw = true; } \
1351 fl::test::AssertResult ar(_fl_threw); \
1352 ar.mLocation = fl::test::SourceLocation(__FILE__, __LINE__); \
1353 ar.mExpression = #expr " throws"; \
1355 ar.mExpanded = "No exception was thrown"; \
1357 fl::test::TestContext::instance().reportAssert(ar); \
1360#define FL_CHECK_NOTHROW(expr) \
1362 bool _fl_threw = false; \
1363 try { (void)(expr); } \
1364 catch (...) { _fl_threw = true; } \
1365 fl::test::AssertResult ar(!_fl_threw); \
1366 ar.mLocation = fl::test::SourceLocation(__FILE__, __LINE__); \
1367 ar.mExpression = #expr " nothrow"; \
1369 ar.mExpanded = "An exception was thrown"; \
1371 fl::test::TestContext::instance().reportAssert(ar); \
1374#define FL_REQUIRE_THROWS(expr) \
1376 bool _fl_threw = false; \
1377 try { (void)(expr); } \
1378 catch (...) { _fl_threw = true; } \
1379 fl::test::AssertResult ar(_fl_threw); \
1380 ar.mLocation = fl::test::SourceLocation(__FILE__, __LINE__); \
1381 ar.mExpression = #expr " throws"; \
1383 ar.mExpanded = "No exception was thrown"; \
1384 fl::test::TestContext::instance().reportAssert(ar); \
1387 fl::test::TestContext::instance().reportAssert(ar); \
1390#define FL_REQUIRE_NOTHROW(expr) \
1392 bool _fl_threw = false; \
1393 try { (void)(expr); } \
1394 catch (...) { _fl_threw = true; } \
1395 fl::test::AssertResult ar(!_fl_threw); \
1396 ar.mLocation = fl::test::SourceLocation(__FILE__, __LINE__); \
1397 ar.mExpression = #expr " nothrow"; \
1399 ar.mExpanded = "An exception was thrown"; \
1400 fl::test::TestContext::instance().reportAssert(ar); \
1403 fl::test::TestContext::instance().reportAssert(ar); \
1408#define FL_CHECK_THROWS_AS(expr, exType) \
1410 bool _fl_threw_correct = false; \
1411 bool _fl_threw_any = false; \
1412 try { (void)(expr); } \
1413 catch (const exType&) { _fl_threw_correct = true; _fl_threw_any = true; } \
1414 catch (...) { _fl_threw_any = true; } \
1415 fl::test::AssertResult ar(_fl_threw_correct); \
1416 ar.mLocation = fl::test::SourceLocation(__FILE__, __LINE__); \
1417 fl::sstream _fl_expr_ss; \
1418 _fl_expr_ss << #expr << " throws " << #exType; \
1419 ar.mExpression = _fl_expr_ss.str(); \
1420 if (!_fl_threw_correct) { \
1421 if (_fl_threw_any) { \
1422 ar.mExpanded = "Threw a different exception type"; \
1424 ar.mExpanded = "No exception was thrown"; \
1427 fl::test::TestContext::instance().reportAssert(ar); \
1430#define FL_REQUIRE_THROWS_AS(expr, exType) \
1432 bool _fl_threw_correct = false; \
1433 bool _fl_threw_any = false; \
1434 try { (void)(expr); } \
1435 catch (const exType&) { _fl_threw_correct = true; _fl_threw_any = true; } \
1436 catch (...) { _fl_threw_any = true; } \
1437 fl::test::AssertResult ar(_fl_threw_correct); \
1438 ar.mLocation = fl::test::SourceLocation(__FILE__, __LINE__); \
1439 fl::sstream _fl_expr_ss; \
1440 _fl_expr_ss << #expr << " throws " << #exType; \
1441 ar.mExpression = _fl_expr_ss.str(); \
1442 if (!_fl_threw_correct) { \
1443 if (_fl_threw_any) { \
1444 ar.mExpanded = "Threw a different exception type"; \
1446 ar.mExpanded = "No exception was thrown"; \
1448 fl::test::TestContext::instance().reportAssert(ar); \
1451 fl::test::TestContext::instance().reportAssert(ar); \
1456#define FL_CHECK_THROWS_WITH(expr, msg) \
1458 bool _fl_threw = false; \
1459 bool _fl_msg_match = false; \
1460 fl::string _fl_actual_msg; \
1461 try { (void)(expr); } \
1462 catch (const std::exception& e) { \
1464 _fl_actual_msg = e.what(); \
1465 _fl_msg_match = (_fl_actual_msg.find(msg) != fl::string::npos); \
1467 catch (...) { _fl_threw = true; } \
1468 fl::test::AssertResult ar(_fl_threw && _fl_msg_match); \
1469 ar.mLocation = fl::test::SourceLocation(__FILE__, __LINE__); \
1470 fl::sstream _fl_expr_ss; \
1471 _fl_expr_ss << #expr << " throws with \"" << msg << "\""; \
1472 ar.mExpression = _fl_expr_ss.str(); \
1474 ar.mExpanded = "No exception was thrown"; \
1475 } else if (!_fl_msg_match) { \
1476 fl::sstream _fl_exp_ss; \
1477 _fl_exp_ss << "Exception message: \"" << _fl_actual_msg << "\""; \
1478 ar.mExpanded = _fl_exp_ss.str(); \
1480 fl::test::TestContext::instance().reportAssert(ar); \
1483#define FL_REQUIRE_THROWS_WITH(expr, msg) \
1485 bool _fl_threw = false; \
1486 bool _fl_msg_match = false; \
1487 fl::string _fl_actual_msg; \
1488 try { (void)(expr); } \
1489 catch (const std::exception& e) { \
1491 _fl_actual_msg = e.what(); \
1492 _fl_msg_match = (_fl_actual_msg.find(msg) != fl::string::npos); \
1494 catch (...) { _fl_threw = true; } \
1495 fl::test::AssertResult ar(_fl_threw && _fl_msg_match); \
1496 ar.mLocation = fl::test::SourceLocation(__FILE__, __LINE__); \
1497 fl::sstream _fl_expr_ss; \
1498 _fl_expr_ss << #expr << " throws with \"" << msg << "\""; \
1499 ar.mExpression = _fl_expr_ss.str(); \
1501 ar.mExpanded = "No exception was thrown"; \
1502 } else if (!_fl_msg_match) { \
1503 fl::sstream _fl_exp_ss; \
1504 _fl_exp_ss << "Exception message: \"" << _fl_actual_msg << "\""; \
1505 ar.mExpanded = _fl_exp_ss.str(); \
1507 fl::test::TestContext::instance().reportAssert(ar); \
1508 if (!(_fl_threw && _fl_msg_match)) return; \
1516#define FL_WARN_THROWS(expr) \
1518 bool _fl_threw = false; \
1519 try { (void)(expr); } \
1520 catch (...) { _fl_threw = true; } \
1522 fl::test::outputMessage("Warning: " #expr " did not throw", __FILE__, __LINE__); \
1526#define FL_WARN_NOTHROW(expr) \
1528 bool _fl_threw = false; \
1529 try { (void)(expr); } \
1530 catch (...) { _fl_threw = true; } \
1532 fl::test::outputMessage("Warning: " #expr " threw an exception", __FILE__, __LINE__); \
1536#define FL_WARN_THROWS_AS(expr, exType) \
1538 bool _fl_threw_correct = false; \
1539 bool _fl_threw_any = false; \
1540 try { (void)(expr); } \
1541 catch (const exType&) { _fl_threw_correct = true; _fl_threw_any = true; } \
1542 catch (...) { _fl_threw_any = true; } \
1543 if (!_fl_threw_correct) { \
1544 if (_fl_threw_any) { \
1545 fl::test::outputMessage("Warning: " #expr " threw different type than " #exType, __FILE__, __LINE__); \
1547 fl::test::outputMessage("Warning: " #expr " did not throw " #exType, __FILE__, __LINE__); \
1552#define FL_WARN_THROWS_WITH(expr, msg) \
1554 bool _fl_threw = false; \
1555 bool _fl_msg_match = false; \
1556 fl::string _fl_actual_msg; \
1557 try { (void)(expr); } \
1558 catch (const std::exception& e) { \
1560 _fl_actual_msg = e.what(); \
1561 _fl_msg_match = (_fl_actual_msg.find(msg) != fl::string::npos); \
1563 catch (...) { _fl_threw = true; } \
1565 fl::test::outputMessage("Warning: " #expr " did not throw", __FILE__, __LINE__); \
1566 } else if (!_fl_msg_match) { \
1567 fl::sstream _fl_warn_ss; \
1568 _fl_warn_ss << "Warning: " #expr " threw but message \"" << _fl_actual_msg \
1569 << "\" does not contain \"" << msg << "\""; \
1570 fl::test::outputMessage(_fl_warn_ss.str().c_str(), __FILE__, __LINE__); \
1577#define FL_CHECK_THROWS(expr) \
1579 fl::test::outputMessage("CHECK_THROWS skipped (exceptions disabled)", __FILE__, __LINE__); \
1582#define FL_CHECK_NOTHROW(expr) \
1583 do { (void)(expr); } while (0)
1585#define FL_REQUIRE_THROWS(expr) \
1587 fl::test::outputMessage("REQUIRE_THROWS skipped (exceptions disabled)", __FILE__, __LINE__); \
1590#define FL_REQUIRE_NOTHROW(expr) \
1591 do { (void)(expr); } while (0)
1593#define FL_CHECK_THROWS_AS(expr, exType) \
1595 fl::test::outputMessage("CHECK_THROWS_AS skipped (exceptions disabled)", __FILE__, __LINE__); \
1598#define FL_REQUIRE_THROWS_AS(expr, exType) \
1600 fl::test::outputMessage("REQUIRE_THROWS_AS skipped (exceptions disabled)", __FILE__, __LINE__); \
1603#define FL_CHECK_THROWS_WITH(expr, msg) \
1605 fl::test::outputMessage("CHECK_THROWS_WITH skipped (exceptions disabled)", __FILE__, __LINE__); \
1608#define FL_REQUIRE_THROWS_WITH(expr, msg) \
1610 fl::test::outputMessage("REQUIRE_THROWS_WITH skipped (exceptions disabled)", __FILE__, __LINE__); \
1613#define FL_WARN_THROWS(expr) \
1615 fl::test::outputMessage("WARN_THROWS skipped (exceptions disabled)", __FILE__, __LINE__); \
1618#define FL_WARN_NOTHROW(expr) \
1619 do { (void)(expr); } while (0)
1621#define FL_WARN_THROWS_AS(expr, exType) \
1623 fl::test::outputMessage("WARN_THROWS_AS skipped (exceptions disabled)", __FILE__, __LINE__); \
1626#define FL_WARN_THROWS_WITH(expr, msg) \
1628 fl::test::outputMessage("WARN_THROWS_WITH skipped (exceptions disabled)", __FILE__, __LINE__); \
1636#ifdef FLTEST_ENABLE_DOCTEST_COMPAT
1637#define TEST_CASE(name) FL_TEST_CASE(name)
1638#define SUBCASE(name) FL_SUBCASE(name)
1639#define CHECK(expr) FL_CHECK(expr)
1640#define CHECK_FALSE(expr) FL_CHECK_FALSE(expr)
1641#define CHECK_EQ(a, b) FL_CHECK_EQ(a, b)
1642#define CHECK_NE(a, b) FL_CHECK_NE(a, b)
1643#define CHECK_LT(a, b) FL_CHECK_LT(a, b)
1644#define CHECK_GT(a, b) FL_CHECK_GT(a, b)
1645#define CHECK_LE(a, b) FL_CHECK_LE(a, b)
1646#define CHECK_GE(a, b) FL_CHECK_GE(a, b)
1647#define REQUIRE(expr) FL_REQUIRE(expr)
1648#define REQUIRE_FALSE(expr) FL_REQUIRE_FALSE(expr)
1649#define REQUIRE_EQ(a, b) FL_REQUIRE_EQ(a, b)
1650#define REQUIRE_NE(a, b) FL_REQUIRE_NE(a, b)
1651#define REQUIRE_LT(a, b) FL_REQUIRE_LT(a, b)
1652#define REQUIRE_GT(a, b) FL_REQUIRE_GT(a, b)
1653#define REQUIRE_LE(a, b) FL_REQUIRE_LE(a, b)
1654#define REQUIRE_GE(a, b) FL_REQUIRE_GE(a, b)
1655#define MESSAGE(msg) FL_MESSAGE(msg)
1656#define INFO(msg) FL_INFO(msg)
1657#define CAPTURE(x) FL_CAPTURE(x)
1658#define FAIL(msg) FL_FAIL(msg)
1659#define FAIL_CHECK(msg) FL_FAIL_CHECK(msg)
1660#define WARN(expr) FL_WARN(expr)
1662#define WARN_FALSE(expr) FL_WARN_FALSE(expr)
1663#define WARN_EQ(a, b) FL_WARN_EQ(a, b)
1664#define WARN_NE(a, b) FL_WARN_NE(a, b)
1665#define WARN_LT(a, b) FL_WARN_LT(a, b)
1666#define WARN_GT(a, b) FL_WARN_GT(a, b)
1667#define WARN_LE(a, b) FL_WARN_LE(a, b)
1668#define WARN_GE(a, b) FL_WARN_GE(a, b)
1670#define CHECK_MESSAGE(expr, msg) FL_CHECK_MESSAGE(expr, msg)
1671#define REQUIRE_MESSAGE(expr, msg) FL_REQUIRE_MESSAGE(expr, msg)
1673#define SCENARIO(name) FL_SCENARIO(name)
1674#define GIVEN(name) FL_GIVEN(name)
1675#define WHEN(name) FL_WHEN(name)
1676#define AND_WHEN(name) FL_AND_WHEN(name)
1677#define THEN(name) FL_THEN(name)
1678#define AND_THEN(name) FL_AND_THEN(name)
1680#define TEST_CASE_FIXTURE(fixture, name) FL_TEST_CASE_FIXTURE(fixture, name)
1681#define TEST_SUITE(name) FL_TEST_SUITE(name)
1683#define CHECK_CLOSE(a, b, eps) FL_CHECK_CLOSE(a, b, eps)
1684#define REQUIRE_CLOSE(a, b, eps) FL_REQUIRE_CLOSE(a, b, eps)
1686#define CHECK_STR_EQ(a, b) FL_CHECK_STR_EQ(a, b)
1687#define CHECK_STR_NE(a, b) FL_CHECK_STR_NE(a, b)
1688#define CHECK_STR_CONTAINS(str, substr) FL_CHECK_STR_CONTAINS(str, substr)
1689#define REQUIRE_STR_EQ(a, b) FL_REQUIRE_STR_EQ(a, b)
1690#define REQUIRE_STR_CONTAINS(str, substr) FL_REQUIRE_STR_CONTAINS(str, substr)
1692#define CHECK_THROWS(expr) FL_CHECK_THROWS(expr)
1693#define CHECK_NOTHROW(expr) FL_CHECK_NOTHROW(expr)
1694#define REQUIRE_THROWS(expr) FL_REQUIRE_THROWS(expr)
1695#define REQUIRE_NOTHROW(expr) FL_REQUIRE_NOTHROW(expr)
1696#define CHECK_THROWS_AS(expr, exType) FL_CHECK_THROWS_AS(expr, exType)
1697#define REQUIRE_THROWS_AS(expr, exType) FL_REQUIRE_THROWS_AS(expr, exType)
1698#define CHECK_THROWS_WITH(expr, msg) FL_CHECK_THROWS_WITH(expr, msg)
1699#define REQUIRE_THROWS_WITH(expr, msg) FL_REQUIRE_THROWS_WITH(expr, msg)
1701#define WARN_THROWS(expr) FL_WARN_THROWS(expr)
1702#define WARN_NOTHROW(expr) FL_WARN_NOTHROW(expr)
1703#define WARN_THROWS_AS(expr, exType) FL_WARN_THROWS_AS(expr, exType)
1704#define WARN_THROWS_WITH(expr, msg) FL_WARN_THROWS_WITH(expr, msg)
1706#define SKIP(reason) FL_SKIP(reason)
1708#define CHECK_ARRAY_EQ(actual, expected, size) FL_CHECK_ARRAY_EQ(actual, expected, size)
1709#define REQUIRE_ARRAY_EQ(actual, expected, size) FL_REQUIRE_ARRAY_EQ(actual, expected, size)
1711#define TYPE_TO_STRING(type, str) FL_TYPE_TO_STRING(type, str)
1712#define TYPE_TO_STRING_AS(str, type) FL_TYPE_TO_STRING_AS(str, type)
1713#define TEST_CASE_TEMPLATE(name, T, ...) FL_TEST_CASE_TEMPLATE(name, T, __VA_ARGS__)
1714#define TEST_CASE_TEMPLATE_DEFINE(name, T, id) FL_TEST_CASE_TEMPLATE_DEFINE(name, T, id)
1715#define TEST_CASE_TEMPLATE_INVOKE(id, ...) FL_TEST_CASE_TEMPLATE_INVOKE(id, __VA_ARGS__)
1716#define TEST_CASE_TEMPLATE_APPLY(id, typelist) FL_TEST_CASE_TEMPLATE_APPLY(id, typelist)
1750template <
typename T>
1752#if defined(__clang__) || defined(__GNUC__)
1755 const char*
begin = func;
1764 if (*
end ==
'<') ++depth;
1765 else if (*
end ==
'>') {
1766 if (depth == 0)
break;
1769 else if (*
end ==
']' && depth == 0)
break;
1774#elif defined(_MSC_VER)
1777 const char*
begin = func;
1787 while (*
end && depth > 0) {
1788 if (*
end ==
'<') ++depth;
1789 else if (*
end ==
'>') --depth;
1800template <
typename T>
1804 return cachedName.
c_str();
1809template <
typename... Ts>
1813template <
typename TL,
typename TestFunc>
1817template <
typename TestFunc>
1823template <
typename T,
typename... Rest,
typename TestFunc>
1845#define FL_TYPE_TO_STRING(type, str) \
1846 namespace fl { namespace test { namespace detail { \
1848 struct TypeNameHolder<type> { \
1849 static const char* name() { return str; } \
1854#define FL_TYPE_TO_STRING_AS(str, type) FL_TYPE_TO_STRING(type, str)
1861#define FL_TEST_CASE_TEMPLATE(name, T, ...) \
1862 template <typename T> \
1863 static void FLTEST_UNIQUE(FLTEST_TMPL_FUNC_)(); \
1864 struct FLTEST_UNIQUE(FLTEST_TMPL_REG_) { \
1865 template <typename T> \
1866 static void run() { \
1867 FLTEST_UNIQUE(FLTEST_TMPL_FUNC_)<T>(); \
1870 static struct FLTEST_UNIQUE(FLTEST_TMPL_INIT_) { \
1871 FLTEST_UNIQUE(FLTEST_TMPL_INIT_)() { \
1872 using TL = fl::test::detail::TypeList<__VA_ARGS__>; \
1873 fl::test::detail::TypeIterator<TL, FLTEST_UNIQUE(FLTEST_TMPL_REG_)>::iterate( \
1874 name, __FILE__, __LINE__, 0); \
1876 } FLTEST_UNIQUE(FLTEST_TMPL_INST_); \
1877 template <typename T> \
1878 static void FLTEST_UNIQUE(FLTEST_TMPL_FUNC_)()
1884#define FL_TEST_CASE_TEMPLATE_DEFINE(name, T, id) \
1885 template <typename T> \
1886 static void FLTEST_CAT(id, _FUNC_)(); \
1887 struct FLTEST_CAT(id, _REG_) { \
1888 template <typename T> \
1889 static void run() { \
1890 FLTEST_CAT(id, _FUNC_)<T>(); \
1892 static const char* baseName() { return name; } \
1894 template <typename T> \
1895 static void FLTEST_CAT(id, _FUNC_)()
1899#define FL_TEST_CASE_TEMPLATE_INVOKE(id, ...) \
1900 static struct FLTEST_UNIQUE(FLTEST_TMPL_INVOKE_) { \
1901 FLTEST_UNIQUE(FLTEST_TMPL_INVOKE_)() { \
1902 using TL = fl::test::detail::TypeList<__VA_ARGS__>; \
1903 fl::test::detail::TypeIterator<TL, FLTEST_CAT(id, _REG_)>::iterate( \
1904 FLTEST_CAT(id, _REG_)::baseName(), __FILE__, __LINE__, 0); \
1906 } FLTEST_UNIQUE(FLTEST_TMPL_INVOKE_INST_); \
1907 FL_STATIC_ASSERT(true, "")
1912#define FL_TEST_CASE_TEMPLATE_APPLY(id, typelist) \
1913 static struct FLTEST_UNIQUE(FLTEST_TMPL_APPLY_) { \
1914 FLTEST_UNIQUE(FLTEST_TMPL_APPLY_)() { \
1915 fl::test::detail::TypeIterator<typelist, FLTEST_CAT(id, _REG_)>::iterate( \
1916 FLTEST_CAT(id, _REG_)::baseName(), __FILE__, __LINE__, 0); \
1918 } FLTEST_UNIQUE(FLTEST_TMPL_APPLY_INST_); \
1919 FL_STATIC_ASSERT(true, "")
1937#ifdef FLTEST_IMPLEMENT_MAIN
1938int main(
int argc,
char** argv) {
1960#define FL_RUN_ALL_TESTS_ARDUINO(serial_obj) \
1962 auto _fl_serial_print = [](const char* msg) { serial_obj.print(msg); }; \
1963 fl::test::SerialReporter _fl_reporter(_fl_serial_print); \
1964 fl::test::TestContext::instance().setReporter(&_fl_reporter); \
1965 int _fl_result = fl::test::TestContext::instance().run(); \
1966 if (_fl_result == 0) { \
1967 serial_obj.println("\n=== ALL TESTS PASSED ==="); \
1969 serial_obj.println("\n=== TESTS FAILED ==="); \
1971 while (true) { delay(1000); } \
const char * c_str() const FL_NOEXCEPT
string str() const FL_NOEXCEPT
Approx & margin(double newMargin) FL_NOEXCEPT
Set absolute margin for comparison When margin > 0, passes if |a - b| <= margin.
friend bool operator>(double lhs, const Approx &rhs) FL_NOEXCEPT
friend bool operator<=(double lhs, const Approx &rhs) FL_NOEXCEPT
friend bool operator==(double lhs, const Approx &rhs) FL_NOEXCEPT
double getScale() const FL_NOEXCEPT
Approx & epsilon(double newEpsilon) FL_NOEXCEPT
Set custom relative epsilon for comparison Comparison uses: epsilon * (scale + max(|a|,...
friend bool operator!=(double lhs, const Approx &rhs) FL_NOEXCEPT
Approx & scale(double newScale) FL_NOEXCEPT
Set custom scale for comparison.
friend bool operator==(const Approx &lhs, double rhs) FL_NOEXCEPT
double getMargin() const FL_NOEXCEPT
Approx(double value) FL_NOEXCEPT
friend bool operator>=(double lhs, const Approx &rhs) FL_NOEXCEPT
friend bool operator!=(const Approx &lhs, double rhs) FL_NOEXCEPT
friend bool operator<(double lhs, const Approx &rhs) FL_NOEXCEPT
double getEpsilon() const FL_NOEXCEPT
double value() const FL_NOEXCEPT
Helper class for approximate floating-point comparisons.
virtual void testCaseStart(const char *name) FL_NOEXCEPT=0
virtual void testRunStart() FL_NOEXCEPT=0
virtual void assertResult(const AssertResult &result) FL_NOEXCEPT=0
virtual void testRunEnd(const TestStats &stats) FL_NOEXCEPT=0
virtual void subcaseStart(const char *name) FL_NOEXCEPT=0
virtual ~IReporter() FL_NOEXCEPT=default
virtual void testCaseEnd(bool passed, fl::u32 durationMs=0) FL_NOEXCEPT=0
Called when a test case ends.
virtual void subcaseEnd() FL_NOEXCEPT=0
fl::vector< fl::string > mCurrentTestFailures
static fl::string escapeJson(const char *text) FL_NOEXCEPT
fl::string mCurrentTestName
fl::vector< fl::string > mTestResults
JSONReporter(fl::string *outputBuffer) FL_NOEXCEPT
Create a JSON reporter that writes to the given string buffer.
void setPrintFunc(SerialPrintFunc func) FL_NOEXCEPT
Set the print function.
SerialReporter(SerialPrintFunc printFunc=nullptr) FL_NOEXCEPT
Create a serial reporter with custom print function.
SerialPrintFunc mPrintFunc
Subcase(const char *name, const char *file, int line) FL_NOEXCEPT
SubcaseSignature mSignature
void output(const char *line) FL_NOEXCEPT
fl::string mCurrentTestName
TAPReporter(SerialPrintFunc printFunc) FL_NOEXCEPT
Create a TAP reporter that uses a print function for streaming output.
SerialPrintFunc mPrintFunc
void setTotalTests(fl::u32 total) FL_NOEXCEPT
Set the total number of tests (for TAP plan line) If not set, plan is output at the end instead.
fl::string mStreamingOutput
fl::vector< fl::string > mDiagnostics
TAPReporter(fl::string *outputBuffer) FL_NOEXCEPT
Create a TAP reporter that writes to the given string buffer.
fl::u32 mDefaultTimeoutMs
fl::vector< SubcaseSignature > mNextSubcaseStack
bool matchesFilter(const char *name, const char *filter) const FL_NOEXCEPT
fl::size mSubcaseDiscoveryDepth
TestContext() FL_NOEXCEPT
DefaultReporter mDefaultReporter
fl::u32 hashCurrentPath(const SubcaseSignature &sig) const FL_NOEXCEPT
bool hasFailure() const FL_NOEXCEPT
const TestStats & stats() const FL_NOEXCEPT
TimeoutHandlerFunc mTimeoutHandler
bool needsReentry() const FL_NOEXCEPT
void runTestCase(const TestCaseInfo &info) FL_NOEXCEPT
fl::size mCurrentSubcaseDepth
fl::vector< TestCaseInfo > mTestCases
bool mCurrentTestTimedOut
bool enterSubcase(const SubcaseSignature &sig) FL_NOEXCEPT
void setCurrentTestFailed(bool failed) FL_NOEXCEPT
fl::vector< fl::u32 > mFullyTraversedHashes
fl::vector< SubcaseSignature > mSubcaseStack
IReporter * reporter() FL_NOEXCEPT
static TestContext & instance() FL_NOEXCEPT
void markFullyTraversed(fl::u32 hash) FL_NOEXCEPT
void setTimeoutHandler(TimeoutHandlerFunc func) FL_NOEXCEPT
Set the timeout handler callback.
int registerTest(TestFunc func, const char *name, const char *file, int line) FL_NOEXCEPT
bool isFullyTraversed(fl::u32 hash) const FL_NOEXCEPT
fl::u32 mCurrentTestStartMs
void setDefaultTimeoutMs(fl::u32 timeoutMs) FL_NOEXCEPT
Set default timeout for all tests (0 = no timeout)
fl::size listTests(const char *filter=nullptr) const FL_NOEXCEPT
int run(int argc=0, const char *const *argv=nullptr) FL_NOEXCEPT
void exitSubcase(const SubcaseSignature &sig) FL_NOEXCEPT
const char * mCurrentTestName
void setGetMillis(GetMillisFunc func) FL_NOEXCEPT
Set the function to get current time in milliseconds.
void setReporter(IReporter *reporter) FL_NOEXCEPT
TestStats & stats() FL_NOEXCEPT
static fl::string escapeXml(const char *text) FL_NOEXCEPT
void setSuiteName(const char *name) FL_NOEXCEPT
Set the test suite name (used in XML output)
fl::vector< fl::string > mTestCaseResults
fl::string mCurrentTestName
fl::string mCurrentTestFailures
XMLReporter(fl::string *outputBuffer, const char *suiteName="fltest") FL_NOEXCEPT
Create an XML reporter that writes to the given string buffer.
fl::string getTypeName() FL_NOEXCEPT
const char *& currentSuiteName() FL_NOEXCEPT
bool binaryAssert(const L &lhs, const R &rhs, Cmp cmp, const char *lhsExpr, const char *op, const char *rhsExpr, const char *file, int line) FL_NOEXCEPT
fl::u32(* GetMillisFunc)()
Callback type for getting current time in milliseconds Example: uint32_t getMillis() { return millis(...
bool(* TimeoutHandlerFunc)(const char *testName, fl::u32 elapsedMs)
Callback type for timeout handler Called when a test times out.
void skipTest(const char *reason, const char *file, int line) FL_NOEXCEPT
Record that the current test should be skipped.
fl::sstream & operator<<(fl::sstream &os, const Approx &approx) FL_NOEXCEPT
fl::u32 hashSubcaseSignature(const SubcaseSignature &sig) FL_NOEXCEPT
bool isTestSkipped() FL_NOEXCEPT
Check if current test has been marked as skipped.
void(* SerialPrintFunc)(const char *msg)
Type for the serial print function callback.
constexpr int type_rank< T >::value
constexpr T * begin(T(&array)[N]) FL_NOEXCEPT
constexpr T * end(T(&array)[N]) FL_NOEXCEPT
expected< T, E > result
Alias for expected (Rust-style naming)
int strcmp(const char *s1, const char *s2) FL_NOEXCEPT
Base definition for an LED controller.
#define FL_PRETTY_FUNCTION
Portable compile-time assertion wrapper.
AssertResult(bool passed=true) FL_NOEXCEPT
bool operator()(const L &lhs, const R &rhs) const FL_NOEXCEPT
bool operator()(const L &lhs, const R &rhs) const FL_NOEXCEPT
bool operator()(const L &lhs, const R &rhs) const FL_NOEXCEPT
bool operator()(const L &lhs, const R &rhs) const FL_NOEXCEPT
bool operator()(const L &lhs, const R &rhs) const FL_NOEXCEPT
bool operator()(const L &lhs, const R &rhs) const FL_NOEXCEPT
ExpressionValue(const T &value) FL_NOEXCEPT
const char * str() const FL_NOEXCEPT
SourceLocation(const char *file="", int line=0) FL_NOEXCEPT
bool operator!=(const SubcaseSignature &other) const FL_NOEXCEPT
bool operator==(const SubcaseSignature &other) const FL_NOEXCEPT
TestRegistrar(TestContext::TestFunc func, const char *name, const char *file, int line) FL_NOEXCEPT
bool allPassed() const FL_NOEXCEPT
fl::u32 mTestCasesSkipped
const char * mPreviousName
SuiteScope(const char *name) FL_NOEXCEPT
~SuiteScope() FL_NOEXCEPT
static void iterate(const char *baseName, const char *file, int line, int index) FL_NOEXCEPT
static void iterate(const char *, const char *, int, int) FL_NOEXCEPT
static const char * name() FL_NOEXCEPT