18 fl::printf(
"===============================================================================\n");
20 fl::printf(
"===============================================================================\n");
25 fl::printf(
"===============================================================================\n");
27 fl::printf(
"-------------------------------------------------------------------------------\n");
28 if (stats.mTestCasesSkipped > 0) {
29 fl::printf(
"Test cases: %u passed, %u failed, %u skipped, %u total\n",
30 stats.mTestCasesPassed, stats.mTestCasesFailed,
31 stats.mTestCasesSkipped, stats.mTestCasesRun);
33 fl::printf(
"Test cases: %u passed, %u failed, %u total\n",
34 stats.mTestCasesPassed, stats.mTestCasesFailed, stats.mTestCasesRun);
36 fl::printf(
"Assertions: %u passed, %u failed\n",
37 stats.mAssertsPassed, stats.mAssertsFailed);
38 if (stats.mTotalDurationMs > 0) {
39 fl::printf(
"Duration: %u ms\n", stats.mTotalDurationMs);
41 fl::printf(
"===============================================================================\n");
43 if (stats.allPassed()) {
83 if (!
result.mExpanded.empty()) {
114 const char* filter =
nullptr;
115 if (argc > 1 && argv && argv[1]) {
125 for (fl::size i = 0; i <
mTestCases.size(); ++i) {
127 if (filter && filter[0] !=
'\0') {
136 return mStats.allPassed() ? 0 : 1;
144 for (fl::size i = 0; i <
mTestCases.size(); ++i) {
146 if (filter && filter[0] !=
'\0') {
157 fl::printf(
"Total: %u tests\n\n",
static_cast<fl::u32
>(count));
162 if (!filter || filter[0] ==
'\0') {
167 const char* n = name;
168 const char* f = filter;
171 bool hasWildcards =
false;
172 for (
const char* p = filter; *p; ++p) {
173 if (*p ==
'*' || *p ==
'?') {
183 const char* ff = filter;
184 while (*nn && *ff && *nn == *ff) {
211 }
else if (*f ==
'?') {
236 mReporter->testCaseStart(info.mName.c_str());
284 fl::u32 testDurationMs = 0;
287 mStats.mTotalDurationMs += testDurationMs;
291 mStats.mTestCasesSkipped++;
292 mReporter->testCaseEnd(
true, testDurationMs);
294 mStats.mTestCasesFailed++;
295 mReporter->testCaseEnd(
false, testDurationMs);
297 mStats.mTestCasesPassed++;
298 mReporter->testCaseEnd(
true, testDurationMs);
318 fl::printf(
" [TIMEOUT] Test exceeded %u ms (elapsed: %u ms)\n",
408 result.mExpression = expr;
463 fl::printf(
" [MESSAGE] %s:%d: %s\n", file, line, msg);
476 fl::printf(
" FAIL (fatal): %s:%d: %s\n", file, line, msg);
478 fl::printf(
" FAIL_CHECK: %s:%d: %s\n", file, line, msg);
491 fl::printf(
" [SKIPPED] %s:%d: %s\n", file, line, reason);
513 print(
"====== FL TEST: Running tests... ======\n");
517 print(
"\n====== FL TEST: Results ======\n");
521 ss <<
"Passed: " << stats.mTestCasesPassed
522 <<
"/" << stats.mTestCasesRun <<
" tests\n";
525 if (stats.mTestCasesSkipped > 0) {
527 ss_skip <<
"Skipped: " << stats.mTestCasesSkipped <<
"\n";
531 if (stats.mAssertsFailed > 0) {
533 ss2 <<
"Failed assertions: " << stats.mAssertsFailed <<
"\n";
537 if (stats.allPassed()) {
538 print(
"Status: PASS\n");
540 print(
"Status: FAIL\n");
546 ss <<
"\n[TEST] " << name <<
"\n";
552 if (durationMs > 0) {
554 ss <<
"[PASS] (" << durationMs <<
" ms)\n";
560 if (durationMs > 0) {
562 ss <<
"[FAIL] (" << durationMs <<
" ms)\n";
572 ss <<
" [SUBCASE] " << name <<
"\n";
583 ss <<
" FAIL: " <<
result.mLocation.mFile
584 <<
":" <<
result.mLocation.mLine <<
"\n";
585 ss <<
" Expr: " <<
result.mExpression <<
"\n";
586 if (!
result.mExpanded.empty()) {
587 ss <<
" Got: " <<
result.mExpanded <<
"\n";
601 for (
const char* p = text; *p; ++p) {
603 case '&':
result +=
"&";
break;
604 case '<':
result +=
"<";
break;
605 case '>':
result +=
">";
break;
606 case '"':
result +=
""";
break;
607 case '\'':
result +=
"'";
break;
608 default:
result += *p;
break;
624 ss <<
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
628 ss <<
"tests=\"" << stats.mTestCasesRun <<
"\" ";
629 ss <<
"failures=\"" << stats.mTestCasesFailed <<
"\" ";
630 ss <<
"errors=\"0\" ";
631 ss <<
"skipped=\"" << stats.mTestCasesSkipped <<
"\">\n";
638 ss <<
"</testsuite>\n";
654 if (durationMs > 0) {
656 fl::u32 secs = durationMs / 1000;
657 fl::u32
millis = durationMs % 1000;
658 ss <<
" time=\"" << secs <<
"." << (
millis < 100 ? (
millis < 10 ?
"00" :
"0") :
"") <<
millis <<
"\"";
665 ss <<
" <failure message=\"Test failed\">\n";
667 ss <<
" </failure>\n";
668 ss <<
" </testcase>\n";
687 ss <<
result.mLocation.mFile <<
":" <<
result.mLocation.mLine <<
"\n";
688 ss <<
" Expression: " <<
result.mExpression <<
"\n";
689 if (!
result.mExpanded.empty()) {
690 ss <<
" Expanded: " <<
result.mExpanded <<
"\n";
705 for (
const char* p = text; *p; ++p) {
707 case '\\':
result +=
"\\\\";
break;
708 case '"':
result +=
"\\\"";
break;
709 case '\n':
result +=
"\\n";
break;
710 case '\r':
result +=
"\\r";
break;
711 case '\t':
result +=
"\\t";
break;
714 if (
static_cast<unsigned char>(*p) < 32) {
718 hex[0] =
"0123456789abcdef"[(*p >> 4) & 0xF];
719 hex[1] =
"0123456789abcdef"[*p & 0xF];
741 ss <<
" \"summary\": {\n";
742 ss <<
" \"total\": " << stats.mTestCasesRun <<
",\n";
743 ss <<
" \"passed\": " << stats.mTestCasesPassed <<
",\n";
744 ss <<
" \"failed\": " << stats.mTestCasesFailed <<
",\n";
745 ss <<
" \"skipped\": " << stats.mTestCasesSkipped <<
",\n";
746 ss <<
" \"assertionsPassed\": " << stats.mAssertsPassed <<
",\n";
747 ss <<
" \"assertionsFailed\": " << stats.mAssertsFailed <<
"\n";
749 ss <<
" \"tests\": [\n";
775 ss <<
" \"passed\": " << (passed ?
"true" :
"false");
778 if (durationMs > 0) {
780 ss <<
" \"durationMs\": " << durationMs;
785 ss <<
" \"failures\": [\n";
818 ss <<
" \"line\": " <<
result.mLocation.mLine <<
",\n";
819 ss <<
" \"expression\": \"" <<
escapeJson(
result.mExpression.c_str()) <<
"\"";
821 if (!
result.mExpanded.empty()) {
864 ss <<
"1.." << stats.mTestCasesRun;
870 ss <<
"# Tests: " << stats.mTestCasesRun
871 <<
", Passed: " << stats.mTestCasesPassed
872 <<
", Failed: " << stats.mTestCasesFailed;
873 if (stats.mTestCasesSkipped > 0) {
874 ss <<
", Skipped: " << stats.mTestCasesSkipped;
901 if (durationMs > 0) {
902 ss <<
" # (" << durationMs <<
" ms)";
917 ss <<
" Subcase: " << (name ? name :
"unknown");
930 ss <<
" Failed at " <<
result.mLocation.mFile <<
":" <<
result.mLocation.mLine;
934 ss2 <<
" Expression: " <<
result.mExpression;
937 if (!
result.mExpanded.empty()) {
939 ss3 <<
" Expanded: " <<
result.mExpanded;
const char * c_str() const FL_NOEXCEPT
string str() const FL_NOEXCEPT
void subcaseStart(const char *name) FL_NOEXCEPT override
void testCaseEnd(bool passed, fl::u32 durationMs=0) FL_NOEXCEPT override
Called when a test case ends.
void testRunEnd(const TestStats &stats) FL_NOEXCEPT override
void testRunStart() FL_NOEXCEPT override
void subcaseEnd() FL_NOEXCEPT override
void assertResult(const AssertResult &result) FL_NOEXCEPT override
void testCaseStart(const char *name) FL_NOEXCEPT override
fl::vector< fl::string > mCurrentTestFailures
void testCaseEnd(bool passed, fl::u32 durationMs=0) FL_NOEXCEPT override
Called when a test case ends.
void subcaseEnd() FL_NOEXCEPT override
static fl::string escapeJson(const char *text) FL_NOEXCEPT
void testRunStart() FL_NOEXCEPT override
void subcaseStart(const char *name) FL_NOEXCEPT override
fl::string mCurrentTestName
fl::vector< fl::string > mTestResults
void testRunEnd(const TestStats &stats) FL_NOEXCEPT override
void testCaseStart(const char *name) FL_NOEXCEPT override
void assertResult(const AssertResult &result) FL_NOEXCEPT override
void subcaseEnd() FL_NOEXCEPT override
void assertResult(const AssertResult &result) FL_NOEXCEPT override
void testCaseStart(const char *name) FL_NOEXCEPT override
void testRunStart() FL_NOEXCEPT override
SerialPrintFunc mPrintFunc
void testRunEnd(const TestStats &stats) FL_NOEXCEPT override
void testCaseEnd(bool passed, fl::u32 durationMs=0) FL_NOEXCEPT override
Called when a test case ends.
void print(const char *msg) FL_NOEXCEPT
void subcaseStart(const char *name) FL_NOEXCEPT override
Subcase(const char *name, const char *file, int line) FL_NOEXCEPT
SubcaseSignature mSignature
void output(const char *line) FL_NOEXCEPT
fl::string mCurrentTestName
void subcaseEnd() FL_NOEXCEPT override
void testRunEnd(const TestStats &stats) FL_NOEXCEPT override
SerialPrintFunc mPrintFunc
void testCaseEnd(bool passed, fl::u32 durationMs=0) FL_NOEXCEPT override
Called when a test case ends.
void subcaseStart(const char *name) FL_NOEXCEPT override
fl::string mStreamingOutput
fl::vector< fl::string > mDiagnostics
void testRunStart() FL_NOEXCEPT override
void testCaseStart(const char *name) FL_NOEXCEPT override
void assertResult(const AssertResult &result) FL_NOEXCEPT override
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
TimeoutHandlerFunc mTimeoutHandler
void runTestCase(const TestCaseInfo &info) FL_NOEXCEPT
fl::size mCurrentSubcaseDepth
fl::vector< TestCaseInfo > mTestCases
bool mCurrentTestTimedOut
bool enterSubcase(const SubcaseSignature &sig) FL_NOEXCEPT
fl::vector< fl::u32 > mFullyTraversedHashes
fl::vector< SubcaseSignature > mSubcaseStack
static TestContext & instance() FL_NOEXCEPT
void markFullyTraversed(fl::u32 hash) FL_NOEXCEPT
int registerTest(TestFunc func, const char *name, const char *file, int line) FL_NOEXCEPT
void requireFailed(const char *expr, const char *file, int line) FL_NOEXCEPT
bool isFullyTraversed(fl::u32 hash) const FL_NOEXCEPT
fl::u32 mCurrentTestStartMs
void reportAssert(const AssertResult &result) FL_NOEXCEPT
fl::size listTests(const char *filter=nullptr) const FL_NOEXCEPT
bool checkTimeout() FL_NOEXCEPT
Check if current test has timed out (call periodically in long tests) Returns true if timed out.
int run(int argc=0, const char *const *argv=nullptr) FL_NOEXCEPT
void exitSubcase(const SubcaseSignature &sig) FL_NOEXCEPT
void checkFailed(const char *expr, const char *file, int line) FL_NOEXCEPT
const char * mCurrentTestName
fl::u32 getElapsedMs() const FL_NOEXCEPT
Get elapsed time for current test (in ms)
void assertResult(const AssertResult &result) FL_NOEXCEPT override
void testRunStart() FL_NOEXCEPT override
void testCaseEnd(bool passed, fl::u32 durationMs=0) FL_NOEXCEPT override
Called when a test case ends.
void testCaseStart(const char *name) FL_NOEXCEPT override
void testRunEnd(const TestStats &stats) FL_NOEXCEPT override
void subcaseStart(const char *name) FL_NOEXCEPT override
static fl::string escapeXml(const char *text) FL_NOEXCEPT
fl::vector< fl::string > mTestCaseResults
void subcaseEnd() FL_NOEXCEPT override
fl::string mCurrentTestName
fl::string mCurrentTestFailures
Portable test framework for FastLED.
Centralized logging categories for FastLED hardware interfaces and subsystems.
static bool sCurrentTestSkipped
void skipTest(const char *reason, const char *file, int line) FL_NOEXCEPT
Record that the current test should be skipped.
void fail(const char *msg, const char *file, int line, bool isFatal) FL_NOEXCEPT
Helper for FAIL macros.
fl::u32 hashSubcaseSignature(const SubcaseSignature &sig) FL_NOEXCEPT
bool isTestSkipped() FL_NOEXCEPT
Check if current test has been marked as skipped.
void outputCapture(const char *name, const char *value, const char *file, int line) FL_NOEXCEPT
Helper to output CAPTURE variable.
void outputMessage(const char *msg, const char *file, int line) FL_NOEXCEPT
Helper to output INFO/MESSAGE during test execution.
static const char * sSkipReason
constexpr int type_rank< T >::value
void print(const char *str)
fl::u32 millis()
Universal millisecond timer - returns milliseconds since system startup.
void printf(const char *format, const Args &... args) FL_NOEXCEPT
Printf-like formatting function that prints directly to the platform output.
expected< T, E > result
Alias for expected (Rust-style naming)
Base definition for an LED controller.