@micro-os-plus/micro-test-plus 3.2.3 → 3.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -62,6 +62,10 @@ namespace micro_os_plus::micro_test_plus
62
62
  {
63
63
  // --------------------------------------------------------------------------
64
64
 
65
+ test_reporter::~test_reporter () = default;
66
+
67
+ // --------------------------------------------------------------------------
68
+
65
69
  /**
66
70
  * @details
67
71
  * The `endl` function inserts a newline character into the specified
@@ -73,127 +77,10 @@ namespace micro_os_plus::micro_test_plus
73
77
  test_reporter&
74
78
  endl (test_reporter& stream)
75
79
  {
76
- reporter.endline ();
80
+ reporter->endline ();
77
81
  return stream;
78
82
  }
79
83
 
80
- // --------------------------------------------------------------------------
81
-
82
- /**
83
- * @details
84
- * This method outputs the prefix for a passing test result, applying the
85
- * appropriate colour formatting and symbols to clearly indicate success. If
86
- * the output occurs within a test case, additional indentation is applied
87
- * for readability. The prefix includes a tick symbol (`✓`) and, if provided,
88
- * an associated message. Colour formatting is reset after the prefix to
89
- * maintain consistent output style across all test cases and folders.
90
- *
91
- * The prefix/suffix methods help shorten the code
92
- * generated by the template methods.
93
- */
94
- void
95
- test_reporter::output_pass_prefix_ (std::string& message)
96
- {
97
- *this << colors_.pass;
98
- if (is_in_test_case_)
99
- {
100
- *this << " ";
101
- }
102
- *this << " ✓ ";
103
- *this << colors_.none;
104
- if (!message.empty ())
105
- {
106
- *this << message.c_str ();
107
- }
108
- }
109
-
110
- /**
111
- * @details
112
- * The `endl` function acts as a stream manipulator for the `test_reporter`,
113
- * inserting a line ending into the output buffer and flushing the current
114
- * content if necessary. This ensures that test report output is clearly
115
- * separated and formatted, improving readability and professionalism in the
116
- * presentation of test results.
117
- *
118
- * Using `endl` in conjunction with the `test_reporter` output operators
119
- * provides a familiar and convenient mechanism for managing line breaks,
120
- * similar to standard C++ stream manipulators.
121
- *
122
- * The prefix/suffix methods help shorten the code
123
- * generated by the template methods.
124
- */
125
- void
126
- test_reporter::output_pass_suffix_ (void)
127
- {
128
- *this << endl;
129
-
130
- flush ();
131
- }
132
-
133
- /**
134
- * @details
135
- * This method outputs the prefix for a failing test result, applying the
136
- * appropriate colour formatting and symbols to clearly indicate failure. If
137
- * the output occurs within a test case, additional indentation is applied
138
- * for readability. The prefix includes a cross symbol (`✗`), an optional
139
- * message, and the label "FAILED". The source location is appended in
140
- * parentheses, showing the file or folder name and line number where the
141
- * failure occurred. Colour formatting is reset after the prefix to maintain
142
- * consistent output style across all test cases and folders.
143
- */
144
- void
145
- test_reporter::output_fail_prefix_ (
146
- std::string& message, const reflection::source_location& location)
147
- {
148
- *this << colors_.fail;
149
- if (is_in_test_case_)
150
- {
151
- *this << " ";
152
- }
153
- *this << " ✗ ";
154
- *this << colors_.none;
155
- if (!message.empty ())
156
- {
157
- *this << message.c_str ();
158
- *this << " ";
159
- }
160
- *this << colors_.fail << "FAILED" << colors_.none;
161
- #pragma GCC diagnostic push
162
- #if defined(__clang__)
163
- #pragma clang diagnostic ignored "-Wsign-conversion"
164
- #elif defined(__GNUC__)
165
- #pragma GCC diagnostic ignored "-Wnarrowing"
166
- #pragma GCC diagnostic ignored "-Wsign-conversion"
167
- #endif
168
- *this << " (" << reflection::short_name (location.file_name ()) << ":"
169
- << type_traits::genuine_integral_value<unsigned int>{
170
- location.line ()
171
- };
172
- #pragma GCC diagnostic pop
173
- }
174
-
175
- /**
176
- * @details
177
- * This method outputs the suffix for a failing test result by closing the
178
- * location information, appending an "aborted..." message if the test was
179
- * aborted, and then adding a newline to the test output. The output stream
180
- * is flushed to ensure immediate visibility. This approach guarantees that
181
- * failure results are clearly separated, promptly reported, and easily
182
- * distinguishable across all test cases and folders.
183
- */
184
- void
185
- test_reporter::output_fail_suffix_ (bool abort)
186
- {
187
- *this << ")";
188
- if (abort)
189
- {
190
- *this << " aborted...";
191
- }
192
- *this << endl;
193
-
194
- flush ();
195
- }
196
-
197
84
  /**
198
85
  * @details
199
86
  * This operator overload enables manipulators, such as `endl`, to be used
@@ -211,34 +98,6 @@ namespace micro_os_plus::micro_test_plus
211
98
  return *this;
212
99
  }
213
100
 
214
- /**
215
- * @details
216
- * This method appends a newline character to the internal output buffer of
217
- * the `test_reporter` and immediately flushes the stream. This ensures that
218
- * each line of test output is clearly separated and promptly displayed,
219
- * enhancing the readability and organisation of test results across all test
220
- * cases and folders.
221
- */
222
- void
223
- test_reporter::endline (void)
224
- {
225
- out_.append ("\n");
226
- flush ();
227
- }
228
-
229
- /**
230
- * @details
231
- * This method flushes the output buffer of the `test_reporter` by
232
- * synchronising it with the standard output stream. This guarantees that all
233
- * pending test output is immediately written and visible, ensuring prompt
234
- * and reliable reporting of test results across all test cases and folders.
235
- */
236
- void
237
- test_reporter::flush (void)
238
- {
239
- fflush (stdout); // Sync STDOUT.
240
- }
241
-
242
101
  /**
243
102
  * @details
244
103
  * This operator overload appends the contents of the provided
@@ -534,240 +393,6 @@ namespace micro_os_plus::micro_test_plus
534
393
  return *this;
535
394
  }
536
395
 
537
- /**
538
- * @details
539
- * This method marks the beginning of a test case, setting the internal state
540
- * to indicate that test output is now within a test case context. If there
541
- * is pending output and the verbosity level is set to verbose, it ensures
542
- * that output is properly separated and displayed, adding an empty line if
543
- * necessary. The output buffer is cleared and the stream is flushed to
544
- * guarantee that all previous output is visible before the new test case
545
- * begins. This approach enhances the clarity and organisation of test
546
- * results across all test cases and folders.
547
- */
548
- void
549
- test_reporter::begin_test_case ([[maybe_unused]] const char* name)
550
- {
551
- is_in_test_case_ = true;
552
-
553
- if (!out_.empty () && (verbosity == verbosity::verbose))
554
- {
555
- if (add_empty_line)
556
- {
557
- printf ("\n");
558
- }
559
- output ();
560
- add_empty_line = true;
561
- }
562
-
563
- out_.clear ();
564
-
565
- flush ();
566
- }
567
-
568
- /**
569
- * @details
570
- * This method marks the end of a test case, summarising its outcome and
571
- * outputting the results with appropriate formatting and colour coding. If
572
- * any checks have failed, a failure message is displayed, including the
573
- * number of successful and failed checks. For passing test cases, a success
574
- * message is shown with the total number of checks. The output is adjusted
575
- * according to the verbosity level, and additional spacing is managed for
576
- * clarity. The output buffer is cleared and the stream is flushed to ensure
577
- * all results are immediately visible, supporting clear and organised
578
- * reporting across all test cases and folders.
579
- */
580
- void
581
- test_reporter::end_test_case ([[maybe_unused]] const char* name)
582
- {
583
- if (verbosity == verbosity::normal || verbosity == verbosity::verbose)
584
- {
585
- if (current_test_suite->current_test_case.failed_checks > 0)
586
- {
587
- if (true /* add_empty_line */)
588
- {
589
- printf ("\n");
590
- }
591
- #pragma GCC diagnostic push
592
- #if defined(__clang__)
593
- #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
594
- #endif
595
- printf (" • %s - test case started\n", name);
596
- output ();
597
- printf (
598
- " %s✗%s %s - test case %sFAILED%s (%d %s passed, %d "
599
- "failed)\n",
600
- colors_.fail, colors_.none, name, colors_.fail, colors_.none,
601
- current_test_suite->current_test_case.successful_checks,
602
- current_test_suite->current_test_case.successful_checks == 1
603
- ? "check"
604
- : "checks",
605
- current_test_suite->current_test_case.failed_checks);
606
- #pragma GCC diagnostic pop
607
- add_empty_line = true;
608
- }
609
- else
610
- {
611
- if (add_empty_line)
612
- {
613
- printf ("\n");
614
- }
615
- if (verbosity == verbosity::verbose)
616
- {
617
- #pragma GCC diagnostic push
618
- #if defined(__clang__)
619
- #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
620
- #endif
621
- printf (" • %s - test case started\n", name);
622
- output ();
623
- printf (
624
- " %s✓%s %s - test case passed (%d %s)\n", colors_.pass,
625
- colors_.none, name,
626
- current_test_suite->current_test_case.successful_checks,
627
- current_test_suite->current_test_case.successful_checks
628
- == 1
629
- ? "check"
630
- : "checks");
631
- #pragma GCC diagnostic pop
632
- add_empty_line = true;
633
- }
634
- else
635
- {
636
- #pragma GCC diagnostic push
637
- #if defined(__clang__)
638
- #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
639
- #endif
640
- printf (
641
- " %s✓%s %s - test case passed (%d %s)\n", colors_.pass,
642
- colors_.none, name,
643
- current_test_suite->current_test_case.successful_checks,
644
- current_test_suite->current_test_case.successful_checks
645
- == 1
646
- ? "check"
647
- : "checks");
648
- #pragma GCC diagnostic pop
649
-
650
- add_empty_line = false;
651
- }
652
- }
653
- }
654
-
655
- out_.clear ();
656
- flush ();
657
-
658
- is_in_test_case_ = false;
659
- }
660
-
661
- /**
662
- * @details
663
- * This method marks the beginning of a test suite, ensuring that output is
664
- * properly separated and clearly presented. If there is pending output, the
665
- * stream is flushed and an empty line is added for clarity. For silent or
666
- * quiet verbosity levels, output is suppressed. Otherwise, a message
667
- * indicating the start of the test suite is displayed. This approach
668
- * enhances the organisation and readability of test results across all test
669
- * cases and folders.
670
- */
671
- void
672
- test_reporter::begin_test_suite (const char* name)
673
- {
674
- if (add_empty_line)
675
- {
676
- flush ();
677
- printf ("\n");
678
- }
679
-
680
- if (verbosity == verbosity::silent || verbosity == verbosity::quiet)
681
- {
682
- add_empty_line = false;
683
- return;
684
- }
685
-
686
- #pragma GCC diagnostic push
687
- #if defined(__clang__)
688
- #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
689
- #endif
690
- printf ("• %s - test suite started\n", name);
691
- #pragma GCC diagnostic pop
692
-
693
- add_empty_line = true;
694
- }
695
-
696
- /**
697
- * @details
698
- * This method marks the end of a test suite, summarising the overall results
699
- * and presenting them with appropriate formatting and colour coding. If the
700
- * suite contains test cases and the verbosity is not set to quiet, an empty
701
- * line is added for clarity. For suites with no failed checks and at least
702
- * one successful check, a success message is displayed, including the number
703
- * of checks and test cases. Otherwise, a failure message is shown, detailing
704
- * the number of successful and failed checks, as well as the total number of
705
- * test cases. The output is immediately flushed to ensure prompt and
706
- * organised reporting across all test cases and folders.
707
- */
708
- void
709
- test_reporter::end_test_suite (test_suite_base& suite)
710
- {
711
- if (verbosity == verbosity::silent)
712
- {
713
- return;
714
- }
715
-
716
- if (suite.test_cases () > 0 && verbosity != verbosity::quiet)
717
- {
718
- printf ("\n");
719
- add_empty_line = true;
720
- }
721
-
722
- // Also fail if none passed.
723
- if (suite.failed_checks () == 0 && suite.successful_checks () != 0)
724
- {
725
- #pragma GCC diagnostic push
726
- #if defined(__clang__)
727
- #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
728
- #endif
729
- printf ("%s✓%s %s - test suite passed (%d %s in %d test %s)\n",
730
- colors_.pass, colors_.none, suite.name (),
731
- suite.successful_checks (),
732
- suite.successful_checks () == 1 ? "check" : "checks",
733
- suite.test_cases (),
734
- suite.test_cases () == 1 ? "case" : "cases");
735
- #pragma GCC diagnostic pop
736
- }
737
- else
738
- {
739
- #pragma GCC diagnostic push
740
- #if defined(__clang__)
741
- #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
742
- #endif
743
- printf ("%s✗%s %s - test suite %sFAILED%s (%d %s passed, %d failed, "
744
- "in %d test %s)\n",
745
- colors_.fail, colors_.none, suite.name (), colors_.fail,
746
- colors_.none, suite.successful_checks (),
747
- suite.successful_checks () == 1 ? "check" : "checks",
748
- suite.failed_checks (), suite.test_cases (),
749
- suite.test_cases () == 1 ? "case" : "cases");
750
- #pragma GCC diagnostic pop
751
- }
752
- flush ();
753
- }
754
-
755
- /**
756
- * @details
757
- * This method writes the contents of the internal output buffer to the
758
- * standard output stream without appending a newline character. After
759
- * outputting the buffer, it is cleared to prepare for subsequent output.
760
- * This approach ensures that test results are presented promptly and
761
- * efficiently, supporting clear and organised reporting across all test
762
- * cases and folders.
763
- */
764
- void
765
- test_reporter::output (void)
766
- {
767
- printf ("%s", out_.c_str ()); // No `\n` here.
768
- out_.clear ();
769
- }
770
-
771
396
  // --------------------------------------------------------------------------
772
397
  } // namespace micro_os_plus::micro_test_plus
773
398
 
@@ -45,6 +45,7 @@
45
45
  #include <micro-os-plus/micro-test-plus.h>
46
46
 
47
47
  #include <stdio.h>
48
+ #include <stdlib.h>
48
49
  #include <vector>
49
50
 
50
51
  // ----------------------------------------------------------------------------
@@ -99,6 +100,14 @@ namespace micro_os_plus::micro_test_plus
99
100
  printf ("%s\n", __PRETTY_FUNCTION__);
100
101
  #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS
101
102
 
103
+ #if defined(_WIN32) || defined(CLOCK_MONOTONIC)
104
+ #if defined(_WIN32)
105
+ timespec_get (&begin_time, TIME_UTC);
106
+ #else
107
+ clock_gettime (CLOCK_MONOTONIC, &begin_time);
108
+ #endif
109
+ #endif
110
+
102
111
  argc_ = argc;
103
112
  argv_ = argv;
104
113
 
@@ -120,6 +129,7 @@ namespace micro_os_plus::micro_test_plus
120
129
  #endif // !defined(MICRO_OS_PLUS_INCLUDE_STARTUP)
121
130
 
122
131
  verbosity_t verbosity = verbosity::normal;
132
+ const char* reporter_name = "tap";
123
133
  for (int i = 0; i < argc; ++i)
124
134
  {
125
135
  if (strcmp (argv[i], "--verbose") == 0)
@@ -134,10 +144,27 @@ namespace micro_os_plus::micro_test_plus
134
144
  {
135
145
  verbosity = verbosity::silent;
136
146
  }
147
+ else if (strncmp (argv[i], "--reporter=", 11) == 0)
148
+ {
149
+ reporter_name = argv[i] + 11;
150
+ }
137
151
  }
138
152
 
139
- // Pass the verbosity to the reporter.
140
- reporter.verbosity = verbosity;
153
+ // Initialize and configure the reporter.
154
+ if (strcmp (reporter_name, "basic") == 0)
155
+ {
156
+ reporter = new test_reporter_basic ();
157
+ }
158
+ else if (strcmp (reporter_name, "tap") == 0)
159
+ {
160
+ reporter = new test_reporter_tap ();
161
+ }
162
+ else
163
+ {
164
+ fprintf (stderr, "error: unknown reporter '%s'\n", reporter_name);
165
+ exit (1);
166
+ }
167
+ reporter->verbosity = verbosity;
141
168
 
142
169
  // ------------------------------------------------------------------------
143
170
 
@@ -177,11 +204,12 @@ namespace micro_os_plus::micro_test_plus
177
204
 
178
205
  // ------------------------------------------------------------------------
179
206
 
180
- default_test_suite_ = new test_suite_base (default_suite_name_);
181
- current_test_suite = default_test_suite_;
207
+ default_test_suite = new test_suite_base (default_suite_name_);
208
+ current_test_suite = default_test_suite;
182
209
 
183
210
  // Deferred to first test case or test suite end, to allow various
184
211
  // initialisations to display their messages.
212
+ // reporter->begin_test (test_suites_count ());
185
213
  // default_test_suite_->begin_test_suite ();
186
214
  }
187
215
  #pragma GCC diagnostic pop
@@ -191,29 +219,48 @@ namespace micro_os_plus::micro_test_plus
191
219
  {
192
220
  bool was_successful = true;
193
221
 
194
- if (!default_test_suite_->unused ())
222
+ if (!default_test_suite->unused ())
195
223
  {
196
- default_test_suite_->end_test_suite ();
197
- was_successful = default_test_suite_->was_successful ();
224
+ default_test_suite->end_test_suite ();
225
+ was_successful = default_test_suite->was_successful ();
226
+
227
+ totals.successful_checks += default_test_suite->successful_checks ();
228
+ totals.failed_checks += default_test_suite->failed_checks ();
229
+ totals.test_cases_count += default_test_suite->test_cases_count ();
198
230
  }
199
231
 
200
- if (suites_ != nullptr)
232
+ if (test_suites != nullptr)
201
233
  {
202
- for (auto suite : *suites_)
234
+ for (auto test_suite : *test_suites)
203
235
  {
204
- current_test_suite = suite;
236
+ current_test_suite = test_suite;
205
237
 
206
- suite->begin_test_suite ();
207
- suite->run ();
208
- suite->end_test_suite ();
238
+ test_suite->begin_test_suite ();
239
+ test_suite->run ();
240
+ test_suite->end_test_suite ();
209
241
 
210
- was_successful &= suite->was_successful ();
242
+ was_successful &= test_suite->was_successful ();
243
+
244
+ totals.successful_checks += test_suite->successful_checks ();
245
+ totals.failed_checks += test_suite->failed_checks ();
246
+ totals.test_cases_count += test_suite->test_cases_count ();
211
247
  }
212
- if (reporter.verbosity != verbosity::silent)
248
+ if (reporter->verbosity != verbosity::silent)
213
249
  {
214
250
  // printf ("\n");
215
251
  }
216
252
  }
253
+
254
+ #if defined(_WIN32) || defined(CLOCK_MONOTONIC)
255
+ #if defined(_WIN32)
256
+ timespec_get (&end_time, TIME_UTC);
257
+ #else
258
+ clock_gettime (CLOCK_MONOTONIC, &end_time);
259
+ #endif
260
+ #endif
261
+
262
+ reporter->end_test (*this);
263
+
217
264
  return was_successful ? 0 : 1;
218
265
  }
219
266
 
@@ -235,11 +282,12 @@ namespace micro_os_plus::micro_test_plus
235
282
  printf ("%s\n", __PRETTY_FUNCTION__);
236
283
  #endif // MICRO_OS_PLUS_TRACE_MICRO_TEST_PLUS
237
284
 
238
- if (suites_ == nullptr)
285
+ if (test_suites == nullptr)
239
286
  {
240
- suites_ = new std::vector<test_suite_base*> ();
287
+ test_suites = new std::vector<test_suite_base*> ();
241
288
  }
242
- suites_->push_back (suite);
289
+ test_suites->push_back (suite);
290
+ suite->index = test_suites->size () + 1;
243
291
  }
244
292
 
245
293
  /**
@@ -121,9 +121,17 @@ namespace micro_os_plus::micro_test_plus
121
121
  void
122
122
  test_suite_base::begin_test_suite (void)
123
123
  {
124
+ #if defined(_WIN32) || defined(CLOCK_MONOTONIC)
125
+ #if defined(_WIN32)
126
+ timespec_get (&begin_time, TIME_UTC);
127
+ #else
128
+ clock_gettime (CLOCK_MONOTONIC, &begin_time);
129
+ #endif
130
+ #endif
131
+
124
132
  process_deferred_begin = false;
125
133
 
126
- reporter.begin_test_suite (name_);
134
+ reporter->begin_test_suite (name_);
127
135
  }
128
136
 
129
137
  /**
@@ -140,9 +148,19 @@ namespace micro_os_plus::micro_test_plus
140
148
  {
141
149
  if (process_deferred_begin)
142
150
  {
151
+ reporter->begin_test (runner.test_suites_count ());
152
+
143
153
  begin_test_suite ();
144
154
  }
145
- reporter.end_test_suite (*this);
155
+
156
+ #if defined(_WIN32) || defined(CLOCK_MONOTONIC)
157
+ #if defined(_WIN32)
158
+ timespec_get (&end_time, TIME_UTC);
159
+ #else
160
+ clock_gettime (CLOCK_MONOTONIC, &end_time);
161
+ #endif
162
+ #endif
163
+ reporter->end_test_suite (*this);
146
164
  }
147
165
 
148
166
  /**
@@ -160,15 +178,17 @@ namespace micro_os_plus::micro_test_plus
160
178
  {
161
179
  if (process_deferred_begin)
162
180
  {
181
+ reporter->begin_test (runner.test_suites_count ());
182
+
163
183
  begin_test_suite ();
164
184
  }
165
185
 
166
186
  test_case_name_ = name;
167
- ++test_cases_;
187
+ ++test_cases_count_;
168
188
 
169
189
  current_test_case = {};
170
190
 
171
- reporter.begin_test_case (test_case_name_);
191
+ reporter->begin_test_case (test_case_name_);
172
192
  }
173
193
 
174
194
  /**
@@ -182,7 +202,7 @@ namespace micro_os_plus::micro_test_plus
182
202
  void
183
203
  test_suite_base::end_test_case (void)
184
204
  {
185
- reporter.end_test_case (test_case_name_);
205
+ reporter->end_test_case (test_case_name_);
186
206
  }
187
207
 
188
208
  /**
@@ -213,6 +233,39 @@ namespace micro_os_plus::micro_test_plus
213
233
  ++current_test_case.failed_checks;
214
234
  }
215
235
 
236
+ #if defined(_WIN32) || defined(CLOCK_MONOTONIC)
237
+ /**
238
+ * @details
239
+ * Subtracts `begin_time` from `end_time` using monotonic clock arithmetic,
240
+ * handling the nanosecond borrow correctly, then splits the result into
241
+ * whole milliseconds and the sub-millisecond remainder expressed in
242
+ * microseconds (0–999).
243
+ */
244
+ #pragma GCC diagnostic push
245
+ #pragma GCC diagnostic ignored "-Wshadow"
246
+ void
247
+ test_suite_base::compute_elapsed_time (timespec& begin_time,
248
+ timespec& end_time,
249
+ long& milliseconds,
250
+ long& microseconds)
251
+ {
252
+ long long delta_ns = end_time.tv_nsec - begin_time.tv_nsec;
253
+ long long delta_s = end_time.tv_sec - begin_time.tv_sec;
254
+ if (delta_ns < 0)
255
+ {
256
+ delta_ns += 1000000000LL;
257
+ --delta_s;
258
+ }
259
+
260
+ // Split into milliseconds and microseconds.
261
+ const long long total_us = delta_s * 1000000LL + delta_ns / 1000LL;
262
+ milliseconds = static_cast<long> (total_us / 1000LL);
263
+ microseconds = static_cast<long> (total_us % 1000LL);
264
+ }
265
+ #pragma GCC diagnostic pop
266
+
267
+ #endif
268
+
216
269
  // ==========================================================================
217
270
 
218
271
  /**