• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

thetic / mutiny / 24060724143

07 Apr 2026 02:06AM UTC coverage: 98.944% (-0.03%) from 98.971%
24060724143

Pull #40

github

web-flow
Merge 655290c5a into b181672a1
Pull Request #40: Replace type-specific *_EQUAL macros with typed CHECK_EQUAL

41 of 41 new or added lines in 7 files covered. (100.0%)

1 existing line in 1 file now uncovered.

5530 of 5589 relevant lines covered (98.94%)

3433.02 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.32
/src/test/Shell.cpp
1
#include "mutiny/test/Shell.hpp"
2

3
#include "mutiny/test/ConsoleOutput.hpp"
4
#include "mutiny/test/Failure.hpp"
5
#include "mutiny/test/Plugin.hpp"
6
#include "mutiny/test/Registry.hpp"
7
#include "mutiny/test/Result.hpp"
8
#include "mutiny/test/Test.hpp"
9
#include "mutiny/test/jump_buffer.hpp"
10

11
#include "mutiny/String.hpp"
12
#include "mutiny/math.hpp"
13

14
#include <math.h>
15
#include <stdlib.h>
16

17
namespace mu {
18
namespace tiny {
19
namespace test {
20

21
namespace {
22

23
/* Sometimes stubs use the mutiny assertions.
24
 * Its not correct to do so, but this small helper class will prevent a
25
 * segmentation fault and instead will give an error message and also the
26
 * file/line of the check that was executed outside the tests.
27
 */
28
class OutsideTestRunnerUTest : public Shell
29
{
30
public:
31
  static OutsideTestRunnerUTest& instance();
32
  Result& get_result() { return default_test_result_; }
69✔
33
  ~OutsideTestRunnerUTest() override = default;
46✔
34

35
private:
36
  OutsideTestRunnerUTest()
46✔
37
    : Shell(
46✔
38
          "\n\t NOTE: Assertion happened without being in a test run "
39
          "(perhaps in main?)",
40
          "\n\t       Something is very wrong. Check this assertion and fix",
41
          "unknown file",
42
          0
43
      )
44
    , default_test_result_(default_output_)
46✔
45
  {
46
  }
46✔
47
  ConsoleOutput default_output_;
48
  Result default_test_result_;
49
};
50

51
OutsideTestRunnerUTest& OutsideTestRunnerUTest::instance()
138✔
52
{
53
  static OutsideTestRunnerUTest instance;
138✔
54
  return instance;
138✔
55
}
56

57
/*
58
 * Below helpers are used for the PlatformSpecificSetJmp and LongJmp. They pass
59
 * a method for what needs to happen after the jump, so that the stack stays
60
 * right.
61
 *
62
 */
63

64
class HelperTestRunInfo
65
{
66
public:
67
  HelperTestRunInfo(Shell* s, Plugin* p, Result* r)
1,179✔
68
    : shell(s)
1,179✔
69
    , plugin(p)
1,179✔
70
    , result(r)
1,179✔
71
  {
72
  }
1,179✔
73

74
  Shell* shell;
75
  Plugin* plugin;
76
  Result* result;
77
};
78

79
void helper_do_run_one_test_in_current_process(void* data)
1,179✔
80
{
81
  auto* run_info = static_cast<HelperTestRunInfo*>(data);
1,179✔
82

83
  Shell* shell = run_info->shell;
1,179✔
84
  Plugin* plugin = run_info->plugin;
1,179✔
85
  Result* result = run_info->result;
1,179✔
86

87
  shell->run_one_test_in_current_process(plugin, *result);
1,179✔
88
}
1,175✔
89

90
const NormalTerminator normal_test_terminator = NormalTerminator();
91
const CrashingTerminator crashing_test_terminator = CrashingTerminator();
92
const TerminatorWithoutExceptions normal_test_terminator_without_exceptions =
93
    TerminatorWithoutExceptions();
94
const CrashingTerminatorWithoutExceptions
95
    crashing_test_terminator_without_exceptions =
96
        CrashingTerminatorWithoutExceptions();
97

98
void (*please_crash_me_right_now)() = abort;
99
} // namespace
100

101
bool approx_equal(double d1, double d2, double threshold)
65✔
102
{
103
  if (is_nan(d1) || is_nan(d2) || is_nan(threshold))
65✔
104
    return false;
3✔
105

106
  if (is_inf(d1) && is_inf(d2)) {
62✔
107
    return true;
2✔
108
  }
109

110
  return fabs(d1 - d2) <= threshold;
60✔
111
}
112

113
const Terminator* Shell::current_test_terminator_ = &normal_test_terminator;
114
const Terminator* Shell::current_test_terminator_without_exceptions_ =
115
    &normal_test_terminator_without_exceptions;
116

117
bool Shell::rethrow_exceptions_ = false;
118

119
Shell::Shell() noexcept
31,662✔
120
  : group_("UndefinedTestGroup")
31,662✔
121
  , name_("UndefinedTest")
31,662✔
122
  , file_("UndefinedFile")
31,662✔
123
  , line_number_(0)
31,662✔
124
  , next_(nullptr)
31,662✔
125
  , has_failed_(false)
31,662✔
126
{
127
}
31,662✔
128

129
Shell::Shell(
533✔
130
    const char* group_name,
131
    const char* test_name,
132
    const char* file_name,
133
    size_t line_number
134
) noexcept
533✔
135
  : group_(group_name)
533✔
136
  , name_(test_name)
533✔
137
  , file_(file_name)
533✔
138
  , line_number_(line_number)
533✔
139
  , next_(nullptr)
533✔
140
  , has_failed_(false)
533✔
141
{
142
}
533✔
143

144
void Shell::set_crash_method(void (*crashme)())
11✔
145
{
146
  please_crash_me_right_now = crashme;
11✔
147
}
11✔
148

149
void Shell::reset_crash_method()
11✔
150
{
151
  please_crash_me_right_now = abort;
11✔
152
}
11✔
153

154
void Shell::crash()
6✔
155
{
156
  please_crash_me_right_now();
6✔
157
}
6✔
158

159
void Shell::run_one_test(Plugin* plugin, Result& result)
1,179✔
160
{
161
  has_failed_ = false;
1,179✔
162
  result.count_run();
1,179✔
163
  HelperTestRunInfo run_info(this, plugin, &result);
1,179✔
164
  set_jump(helper_do_run_one_test_in_current_process, &run_info);
1,179✔
165
}
1,175✔
166

167
Test* Shell::create_test()
16✔
168
{
169
  return new Test();
16✔
170
}
171

172
void Shell::destroy_test(Test* test)
1,180✔
173
{
174
  delete test;
1,180✔
175
}
1,180✔
176

177
void Shell::run_one_test_in_current_process(Plugin* plugin, Result& result)
1,180✔
178
{
179
  result.print_very_verbose("\n-- before runAllPreTestAction: ");
1,180✔
180
  plugin->run_all_pre_test_action(*this, result);
1,180✔
181
  result.print_very_verbose("\n-- after runAllPreTestAction: ");
1,180✔
182

183
  // save test context, so that test class can be tested
184
  Shell* saved_test = Shell::get_current();
1,180✔
185
  Result* saved_result = Shell::get_test_result();
1,180✔
186

187
  Shell::set_test_result(&result);
1,180✔
188
  Shell::set_current_test(this);
1,180✔
189

190
  Test* test_to_run = nullptr;
1,180✔
191

192
#if MUTINY_HAVE_EXCEPTIONS
193
  try {
194
#endif
195
    result.print_very_verbose("\n---- before createTest: ");
1,180✔
196
    test_to_run = create_test();
1,180✔
197
    result.print_very_verbose("\n---- after createTest: ");
1,180✔
198

199
    result.print_very_verbose("\n------ before runTest: ");
1,180✔
200
    test_to_run->run();
1,180✔
201
    result.print_very_verbose("\n------ after runTest: ");
1,176✔
202

203
    Shell::set_current_test(saved_test);
1,176✔
204
    Shell::set_test_result(saved_result);
1,176✔
205
#if MUTINY_HAVE_EXCEPTIONS
206
  } catch (...) {
4✔
207
    destroy_test(test_to_run);
4✔
208
    throw;
4✔
209
  }
4✔
210
#endif
211

212
  result.print_very_verbose("\n---- before destroyTest: ");
1,176✔
213
  destroy_test(test_to_run);
1,176✔
214
  result.print_very_verbose("\n---- after destroyTest: ");
1,176✔
215

216
  result.print_very_verbose("\n-- before runAllPostTestAction: ");
1,176✔
217
  plugin->run_all_post_test_action(*this, result);
1,176✔
218
  result.print_very_verbose("\n-- after runAllPostTestAction: ");
1,176✔
219
}
1,176✔
220

221
Shell* Shell::get_next() const
125,439✔
222
{
223
  return next_;
125,439✔
224
}
225

226
Shell* Shell::add_test(Shell* test)
32,331✔
227
{
228
  next_ = test;
32,331✔
229
  return this;
32,331✔
230
}
231

232
size_t Shell::count_tests()
33✔
233
{
234
  return next_ ? next_->count_tests() + 1 : 1;
33✔
235
}
236

237
String Shell::get_macro_name() const
1,374✔
238
{
239
  return "TEST";
1,374✔
240
}
241

242
const char* Shell::get_name() const
1,513✔
243
{
244
  return name_;
1,513✔
245
}
246

247
const char* Shell::get_group() const
62,147✔
248
{
249
  return group_;
62,147✔
250
}
251

252
String Shell::get_formatted_name() const
1,458✔
253
{
254
  String formatted_name(get_macro_name());
1,458✔
255
  formatted_name += "(";
1,458✔
256
  formatted_name += group_;
1,458✔
257
  formatted_name += ", ";
1,458✔
258
  formatted_name += name_;
1,458✔
259
  formatted_name += ")";
1,458✔
260

261
  return formatted_name;
1,458✔
262
}
×
263

264
bool Shell::has_failed() const
21✔
265
{
266
  return has_failed_;
21✔
267
}
268

269
void Shell::count_check()
1,055✔
270
{
271
  get_test_result()->count_check();
1,055✔
272
}
1,055✔
273

274
bool Shell::will_run() const
1,241✔
275
{
276
  return true;
1,241✔
277
}
278

279
bool Shell::is_ordered() const
2✔
280
{
281
  return false;
2✔
282
}
283

284
void Shell::set_run_ignored() {}
1✔
285

286
void Shell::set_file_name(const char* file_name)
31,599✔
287
{
288
  file_ = file_name;
31,599✔
289
}
31,599✔
290

291
void Shell::set_line_number(size_t line_number)
31,599✔
292
{
293
  line_number_ = line_number;
31,599✔
294
}
31,599✔
295

296
void Shell::set_group_name(const char* group_name)
31,611✔
297
{
298
  group_ = group_name;
31,611✔
299
}
31,611✔
300

301
void Shell::set_test_name(const char* test_name)
31,607✔
302
{
303
  name_ = test_name;
31,607✔
304
}
31,607✔
305

306
const char* Shell::get_file() const
1,666✔
307
{
308
  return file_;
1,666✔
309
}
310

311
size_t Shell::get_line_number() const
1,666✔
312
{
313
  return line_number_;
1,666✔
314
}
315

316
bool Shell::match(const char* target, const Filter* filters) const
31,903✔
317
{
318
  if (filters == nullptr)
31,903✔
319
    return true;
1,370✔
320

321
  for (; filters != nullptr; filters = filters->get_next())
60,016✔
322
    if (filters->match(target))
30,533✔
323
      return true;
1,050✔
324

325
  return false;
29,483✔
326
}
327

328
bool Shell::should_run(
30,692✔
329
    const Filter* group_filters,
330
    const Filter* name_filters
331
) const
332
{
333
  return match(group_, group_filters) && match(name_, name_filters);
30,692✔
334
}
335

336
void Shell::add_failure(const Failure& failure)
124✔
337
{
338
  has_failed_ = true;
124✔
339
  get_test_result()->add_failure(failure);
124✔
340
}
124✔
341

342
void Shell::add_test_property(const char* name, const char* value)
1✔
343
{
344
  get_test_result()->add_test_property(name, value);
1✔
345
}
1✔
346

347
void Shell::exit_test(const Terminator& terminator)
5✔
348
{
349
  terminator.exit_current_test();
5✔
350
}
×
351

352
void Shell::assert_true(
557✔
353
    bool condition,
354
    const char* check_string,
355
    const char* condition_string,
356
    const char* text,
357
    const char* file_name,
358
    size_t line_number,
359
    const Terminator& test_terminator
360
)
361
{
362
  get_test_result()->count_check();
557✔
363
  if (!condition) {
557✔
364
    add_failure(CheckFailure(
4✔
365
        this, file_name, line_number, check_string, condition_string, text
366
    ));
367
    test_terminator.exit_current_test();
4✔
368
  }
369
}
553✔
370

371
void Shell::fail(
26✔
372
    const char* text,
373
    const char* file_name,
374
    size_t line_number,
375
    const Terminator& test_terminator
376
)
377
{
378
  get_test_result()->count_check();
26✔
379
  add_failure(FailFailure(this, file_name, line_number, text));
26✔
380
  test_terminator.exit_current_test();
26✔
381
}
×
382

383
void Shell::assert_cstr_equal(
728✔
384
    const char* expected,
385
    const char* actual,
386
    const char* text,
387
    const char* file_name,
388
    size_t line_number,
389
    const Terminator& test_terminator
390
)
391
{
392
  get_test_result()->count_check();
728✔
393
  if (actual == nullptr && expected == nullptr)
728✔
394
    return;
1✔
395
  if (actual == nullptr || expected == nullptr) {
727✔
396
    add_failure(
2✔
397
        StringEqualFailure(this, file_name, line_number, expected, actual, text)
4✔
398
    );
399
    test_terminator.exit_current_test();
2✔
400
  }
401
  if (strcmp(expected, actual) != 0) {
725✔
402
    add_failure(
4✔
403
        StringEqualFailure(this, file_name, line_number, expected, actual, text)
8✔
404
    );
405
    test_terminator.exit_current_test();
4✔
406
  }
407
}
408

409
void Shell::assert_cstr_n_equal(
15✔
410
    const char* expected,
411
    const char* actual,
412
    size_t length,
413
    const char* text,
414
    const char* file_name,
415
    size_t line_number,
416
    const Terminator& test_terminator
417
)
418
{
419
  get_test_result()->count_check();
15✔
420
  if (actual == nullptr && expected == nullptr)
15✔
421
    return;
1✔
422
  if (actual == nullptr || expected == nullptr) {
14✔
423
    add_failure(
2✔
424
        StringEqualFailure(this, file_name, line_number, expected, actual, text)
4✔
425
    );
426
    test_terminator.exit_current_test();
2✔
427
  }
428
  if (strncmp(expected, actual, length) != 0) {
12✔
429
    add_failure(
6✔
430
        StringEqualFailure(this, file_name, line_number, expected, actual, text)
12✔
431
    );
432
    test_terminator.exit_current_test();
6✔
433
  }
434
}
435

436
void Shell::assert_cstr_contains(
246✔
437
    const char* expected,
438
    const char* actual,
439
    const char* text,
440
    const char* file_name,
441
    size_t line_number
442
)
443
{
444
  get_test_result()->count_check();
246✔
445
  if (actual == nullptr && expected == nullptr)
246✔
446
    return;
1✔
447
  if (actual == nullptr || expected == nullptr) {
245✔
448
    add_failure(ContainsFailure(
4✔
449
        this,
450
        file_name,
451
        line_number,
452
        string_from_or_null(expected),
4✔
453
        string_from_or_null(actual),
4✔
454
        text ? text : ""
455
    ));
456
    exit_test(get_current_test_terminator());
2✔
457
  } else if (!string_contains(actual, expected)) {
243✔
458
    add_failure(ContainsFailure(
2✔
459
        this, file_name, line_number, expected, actual, text ? text : ""
460
    ));
461
    exit_test(get_current_test_terminator());
2✔
462
  }
463
}
464

465
void Shell::assert_intmax_equal(
12✔
466
    intmax_t expected,
467
    intmax_t actual,
468
    const char* text,
469
    const char* file_name,
470
    size_t line_number,
471
    const Terminator& test_terminator
472
)
473
{
474
  get_test_result()->count_check();
12✔
475
  if (expected != actual) {
12✔
476
    add_failure(
6✔
477
        IntMaxEqualFailure(this, file_name, line_number, expected, actual, text)
12✔
478
    );
479
    test_terminator.exit_current_test();
6✔
480
  }
481
}
6✔
482

483
void Shell::assert_uintmax_equal(
12✔
484
    uintmax_t expected,
485
    uintmax_t actual,
486
    const char* text,
487
    const char* file_name,
488
    size_t line_number,
489
    const Terminator& test_terminator
490
)
491
{
492
  get_test_result()->count_check();
12✔
493
  if (expected != actual) {
12✔
494
    add_failure(UintMaxEqualFailure(
6✔
495
        this, file_name, line_number, expected, actual, text
496
    ));
497
    test_terminator.exit_current_test();
6✔
498
  }
499
}
6✔
500

501
void Shell::assert_pointers_equal(
4✔
502
    const void* expected,
503
    const void* actual,
504
    const char* text,
505
    const char* file_name,
506
    size_t line_number,
507
    const Terminator& test_terminator
508
)
509
{
510
  get_test_result()->count_check();
4✔
511
  if (expected != actual) {
4✔
512
    add_failure(EqualsFailure(
4✔
513
        this,
514
        file_name,
515
        line_number,
516
        string_from(expected),
4✔
517
        string_from(actual),
4✔
518
        text
519
    ));
520
    test_terminator.exit_current_test();
2✔
521
  }
522
}
2✔
523

524
void Shell::assert_approx_equal(
6✔
525
    double expected,
526
    double actual,
527
    double threshold,
528
    const char* text,
529
    const char* file_name,
530
    size_t line_number,
531
    const Terminator& test_terminator
532
)
533
{
534
  add_failure(ApproxEqualFailure(
6✔
535
      this, file_name, line_number, expected, actual, threshold, text
536
  ));
537
  test_terminator.exit_current_test();
6✔
UNCOV
538
}
×
539

540
void Shell::assert_binary_equal(
14✔
541
    const void* expected,
542
    const void* actual,
543
    size_t length,
544
    const char* text,
545
    const char* file_name,
546
    size_t line_number,
547
    const Terminator& test_terminator
548
)
549
{
550
  get_test_result()->count_check();
14✔
551
  if (length == 0)
14✔
552
    return;
3✔
553
  if (actual == nullptr && expected == nullptr)
11✔
554
    return;
1✔
555
  if (actual == nullptr || expected == nullptr) {
10✔
556
    add_failure(BinaryEqualFailure(
2✔
557
        this,
558
        file_name,
559
        line_number,
560
        static_cast<const unsigned char*>(expected),
561
        static_cast<const unsigned char*>(actual),
562
        length,
563
        text
564
    ));
565
    test_terminator.exit_current_test();
2✔
566
  }
567
  if (memcmp(expected, actual, length) != 0) {
8✔
568
    add_failure(BinaryEqualFailure(
4✔
569
        this,
570
        file_name,
571
        line_number,
572
        static_cast<const unsigned char*>(expected),
573
        static_cast<const unsigned char*>(actual),
574
        length,
575
        text
576
    ));
577
    test_terminator.exit_current_test();
4✔
578
  }
579
}
580

581
void Shell::assert_equals(
26✔
582
    bool failed,
583
    const char* expected,
584
    const char* actual,
585
    const char* text,
586
    const char* file,
587
    size_t line,
588
    const Terminator& test_terminator
589
)
590
{
591
  get_test_result()->count_check();
26✔
592
  if (failed) {
26✔
593
    add_failure(CheckEqualFailure(this, file, line, expected, actual, text));
22✔
594
    test_terminator.exit_current_test();
22✔
595
  }
596
}
4✔
597

598
void Shell::assert_equals(
12✔
599
    bool failed,
600
    String expected,
601
    String actual,
602
    const char* text,
603
    const char* file,
604
    size_t line,
605
    const Terminator& test_terminator
606
)
607
{
608
  get_test_result()->count_check();
12✔
609
  if (failed) {
12✔
610
    add_failure(CheckEqualFailure(
6✔
611
        this, file, line, expected.c_str(), actual.c_str(), text
612
    ));
613
    expected.clear();
6✔
614
    actual.clear();
6✔
615
    test_terminator.exit_current_test();
6✔
616
  }
617
}
6✔
618

619
void Shell::assert_compare(
2✔
620
    bool comparison,
621
    const char* check_string,
622
    const char* comparison_string,
623
    const char* text,
624
    const char* file_name,
625
    size_t line_number,
626
    const Terminator& test_terminator
627
)
628
{
629
  get_test_result()->count_check();
2✔
630
  if (!comparison) {
2✔
631
    add_failure(ComparisonFailure(
2✔
632
        this, file_name, line_number, check_string, comparison_string, text
633
    ));
634
    test_terminator.exit_current_test();
2✔
635
  }
636
}
×
637

638
void Shell::print_very_verbose(const char* text)
6,987✔
639
{
640
  get_test_result()->print_very_verbose(text);
6,987✔
641
}
6,987✔
642

643
Result* Shell::test_result_ = nullptr;
644
Shell* Shell::current_test_ = nullptr;
645

646
void Shell::set_test_result(Result* result)
2,356✔
647
{
648
  test_result_ = result;
2,356✔
649
}
2,356✔
650

651
void Shell::set_current_test(Shell* test)
2,356✔
652
{
653
  current_test_ = test;
2,356✔
654
}
2,356✔
655

656
Result* Shell::get_test_result()
11,001✔
657
{
658
  if (test_result_ == nullptr)
11,001✔
659
    return &OutsideTestRunnerUTest::instance().get_result();
69✔
660
  return test_result_;
10,932✔
661
}
662

663
Shell* Shell::get_current()
5,245✔
664
{
665
  if (current_test_ == nullptr)
5,245✔
666
    return &OutsideTestRunnerUTest::instance();
69✔
667
  return current_test_;
5,176✔
668
}
669

670
const Terminator& Shell::get_current_test_terminator()
1,293✔
671
{
672
  return *current_test_terminator_;
1,293✔
673
}
674

675
const Terminator& Shell::get_current_test_terminator_without_exceptions()
138✔
676
{
677
  return *current_test_terminator_without_exceptions_;
138✔
678
}
679

680
void Shell::set_crash_on_fail()
5✔
681
{
682
  current_test_terminator_ = &crashing_test_terminator;
5✔
683
  current_test_terminator_without_exceptions_ =
5✔
684
      &crashing_test_terminator_without_exceptions;
685
}
5✔
686

687
void Shell::restore_default_test_terminator()
5✔
688
{
689
  current_test_terminator_ = &normal_test_terminator;
5✔
690
  current_test_terminator_without_exceptions_ =
5✔
691
      &normal_test_terminator_without_exceptions;
692
}
5✔
693

694
void Shell::set_rethrow_exceptions(bool rethrow_exceptions)
80✔
695
{
696
  rethrow_exceptions_ = rethrow_exceptions;
80✔
697
}
80✔
698

699
bool Shell::is_rethrowing_exceptions()
17✔
700
{
701
  return rethrow_exceptions_;
17✔
702
}
703

704
} // namespace test
705
} // namespace tiny
706
} // namespace mu
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc