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

thetic / mutiny / 24575604752

17 Apr 2026 04:26PM UTC coverage: 98.713% (+0.08%) from 98.629%
24575604752

Pull #59

github

web-flow
Merge 3d55a5f86 into e0583988c
Pull Request #59: Dissolve internal unit tests; restore coverage via public interface

3 of 3 new or added lines in 2 files covered. (100.0%)

4 existing lines in 4 files now uncovered.

5062 of 5128 relevant lines covered (98.71%)

3554.9 hits per line

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

96.97
/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_; }
39✔
33
  ~OutsideTestRunnerUTest() override = default;
39✔
34

35
private:
36
  OutsideTestRunnerUTest()
39✔
37
    : Shell(
39✔
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_)
39✔
45
  {
46
  }
39✔
47
  ConsoleOutput default_output_;
48
  Result default_test_result_;
49
};
50

51
OutsideTestRunnerUTest& OutsideTestRunnerUTest::instance()
78✔
52
{
53
  static OutsideTestRunnerUTest instance;
78✔
54
  return instance;
78✔
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,154✔
68
    : shell(s)
1,154✔
69
    , plugin(p)
1,154✔
70
    , result(r)
1,154✔
71
  {
72
  }
1,154✔
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,154✔
80
{
81
  auto* run_info = static_cast<HelperTestRunInfo*>(data);
1,154✔
82

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

87
  shell->run_one_test_in_current_process(plugin, *result);
1,154✔
88
}
1,150✔
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)
56✔
102
{
103
  if (is_nan(d1) || is_nan(d2) || is_nan(threshold))
56✔
104
    return false;
3✔
105

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

110
  return fabs(d1 - d2) <= threshold;
51✔
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
42,066✔
120
  : group_("UndefinedTestGroup")
42,066✔
121
  , name_("UndefinedTest")
42,066✔
122
  , file_("UndefinedFile")
42,066✔
123
  , line_number_(0)
42,066✔
124
  , next_(nullptr)
42,066✔
125
  , has_failed_(false)
42,066✔
126
{
127
}
42,066✔
128

129
Shell::Shell(
515✔
130
    const char* group_name,
131
    const char* test_name,
132
    const char* file_name,
133
    size_t line_number
134
) noexcept
515✔
135
  : group_(group_name)
515✔
136
  , name_(test_name)
515✔
137
  , file_(file_name)
515✔
138
  , line_number_(line_number)
515✔
139
  , next_(nullptr)
515✔
140
  , has_failed_(false)
515✔
141
{
142
}
515✔
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,154✔
160
{
161
  has_failed_ = false;
1,154✔
162
  result.count_run();
1,154✔
163
  HelperTestRunInfo run_info(this, plugin, &result);
1,154✔
164
  set_jump(helper_do_run_one_test_in_current_process, &run_info);
1,154✔
165
}
1,150✔
166

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

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

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

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

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

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

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

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

203
    Shell::set_current_test(saved_test);
1,151✔
204
    Shell::set_test_result(saved_result);
1,151✔
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,151✔
213
  destroy_test(test_to_run);
1,151✔
214
  result.print_very_verbose("\n---- after destroyTest: ");
1,151✔
215

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

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

226
Shell* Shell::add_test(Shell* test)
42,803✔
227
{
228
  next_ = test;
42,803✔
229
  return this;
42,803✔
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,309✔
238
{
239
  return "TEST";
1,309✔
240
}
241

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

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

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

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

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

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

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

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

UNCOV
284
void Shell::set_run_ignored() {}
×
285

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

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

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

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

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

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

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

321
  for (; filters != nullptr; filters = filters->get_next())
78,860✔
322
    if (filters->match(target))
39,943✔
323
      return true;
1,026✔
324

325
  return false;
38,917✔
326
}
327

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

336
void Shell::add_failure(const Failure& failure)
128✔
337
{
338
  has_failed_ = true;
128✔
339
  get_test_result()->add_failure(failure);
128✔
340
}
128✔
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::skip_test(
×
353
    const char* text,
354
    const char* /*file_name*/,
355
    size_t /*line_number*/,
356
    const Terminator& terminator
357
)
358
{
359
  get_test_result()->skip_test(text);
×
360
  terminator.exit_current_test();
×
361
}
×
362

363
void Shell::assert_true(
470✔
364
    bool condition,
365
    const char* check_string,
366
    const char* condition_string,
367
    const char* text,
368
    const char* file_name,
369
    size_t line_number,
370
    const Terminator& test_terminator
371
)
372
{
373
  get_test_result()->count_check();
470✔
374
  if (!condition) {
470✔
375
    add_failure(CheckFailure(
4✔
376
        this, file_name, line_number, check_string, condition_string, text
377
    ));
378
    test_terminator.exit_current_test();
4✔
379
  }
380
}
466✔
381

382
void Shell::fail(
29✔
383
    const char* text,
384
    const char* file_name,
385
    size_t line_number,
386
    const Terminator& test_terminator
387
)
388
{
389
  get_test_result()->count_check();
29✔
390
  add_failure(FailFailure(this, file_name, line_number, text));
29✔
391
  test_terminator.exit_current_test();
29✔
392
}
×
393

394
void Shell::assert_cstr_equal(
695✔
395
    const char* expected,
396
    const char* actual,
397
    const char* text,
398
    const char* file_name,
399
    size_t line_number,
400
    const Terminator& test_terminator
401
)
402
{
403
  get_test_result()->count_check();
695✔
404
  if (actual == nullptr && expected == nullptr)
695✔
405
    return;
1✔
406
  if (actual == nullptr || expected == nullptr) {
694✔
407
    add_failure(
2✔
408
        StringEqualFailure(this, file_name, line_number, expected, actual, text)
4✔
409
    );
410
    test_terminator.exit_current_test();
2✔
411
  }
412
  if (strcmp(expected, actual) != 0) {
692✔
413
    add_failure(
4✔
414
        StringEqualFailure(this, file_name, line_number, expected, actual, text)
8✔
415
    );
416
    test_terminator.exit_current_test();
4✔
417
  }
418
}
419

420
void Shell::assert_cstr_n_equal(
13✔
421
    const char* expected,
422
    const char* actual,
423
    size_t length,
424
    const char* text,
425
    const char* file_name,
426
    size_t line_number,
427
    const Terminator& test_terminator
428
)
429
{
430
  get_test_result()->count_check();
13✔
431
  if (actual == nullptr && expected == nullptr)
13✔
432
    return;
1✔
433
  if (actual == nullptr || expected == nullptr) {
12✔
434
    add_failure(
2✔
435
        StringEqualFailure(this, file_name, line_number, expected, actual, text)
4✔
436
    );
437
    test_terminator.exit_current_test();
2✔
438
  }
439
  if (strncmp(expected, actual, length) != 0) {
10✔
440
    add_failure(
6✔
441
        StringEqualFailure(this, file_name, line_number, expected, actual, text)
12✔
442
    );
443
    test_terminator.exit_current_test();
6✔
444
  }
445
}
446

447
void Shell::assert_cstr_contains(
249✔
448
    const char* expected,
449
    const char* actual,
450
    const char* text,
451
    const char* file_name,
452
    size_t line_number
453
)
454
{
455
  get_test_result()->count_check();
249✔
456
  if (actual == nullptr && expected == nullptr)
249✔
457
    return;
1✔
458
  if (actual == nullptr || expected == nullptr) {
248✔
459
    add_failure(ContainsFailure(
4✔
460
        this,
461
        file_name,
462
        line_number,
463
        string_from_or_null(expected),
4✔
464
        string_from_or_null(actual),
4✔
465
        text ? text : ""
466
    ));
467
    exit_test(get_current_test_terminator());
2✔
468
  } else if (!string_contains(actual, expected)) {
246✔
469
    add_failure(ContainsFailure(
2✔
470
        this, file_name, line_number, expected, actual, text ? text : ""
471
    ));
472
    exit_test(get_current_test_terminator());
2✔
473
  }
474
}
475

476
void Shell::assert_intmax_equal(
12✔
477
    intmax_t expected,
478
    intmax_t actual,
479
    const char* text,
480
    const char* file_name,
481
    size_t line_number,
482
    const Terminator& test_terminator
483
)
484
{
485
  get_test_result()->count_check();
12✔
486
  if (expected != actual) {
12✔
487
    add_failure(
6✔
488
        IntMaxEqualFailure(this, file_name, line_number, expected, actual, text)
12✔
489
    );
490
    test_terminator.exit_current_test();
6✔
491
  }
492
}
6✔
493

494
void Shell::assert_uintmax_equal(
12✔
495
    uintmax_t expected,
496
    uintmax_t actual,
497
    const char* text,
498
    const char* file_name,
499
    size_t line_number,
500
    const Terminator& test_terminator
501
)
502
{
503
  get_test_result()->count_check();
12✔
504
  if (expected != actual) {
12✔
505
    add_failure(UintMaxEqualFailure(
6✔
506
        this, file_name, line_number, expected, actual, text
507
    ));
508
    test_terminator.exit_current_test();
6✔
509
  }
510
}
6✔
511

512
void Shell::assert_pointers_equal(
4✔
513
    const void* expected,
514
    const void* actual,
515
    const char* text,
516
    const char* file_name,
517
    size_t line_number,
518
    const Terminator& test_terminator
519
)
520
{
521
  get_test_result()->count_check();
4✔
522
  if (expected != actual) {
4✔
523
    add_failure(EqualsFailure(
4✔
524
        this,
525
        file_name,
526
        line_number,
527
        string_from(expected),
4✔
528
        string_from(actual),
4✔
529
        text
530
    ));
531
    test_terminator.exit_current_test();
2✔
532
  }
533
}
2✔
534

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

576
void Shell::assert_equals(
24✔
577
    bool failed,
578
    const char* expected,
579
    const char* actual,
580
    const char* text,
581
    const char* file,
582
    size_t line,
583
    const Terminator& test_terminator
584
)
585
{
586
  get_test_result()->count_check();
24✔
587
  if (failed) {
24✔
588
    add_failure(CheckEqualFailure(this, file, line, expected, actual, text));
20✔
589
    test_terminator.exit_current_test();
20✔
590
  }
591
}
4✔
592

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

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

633
void Shell::print_very_verbose(const char* text)
6,833✔
634
{
635
  get_test_result()->print_very_verbose(text);
6,833✔
636
}
6,833✔
637

638
Result* Shell::test_result_ = nullptr;
639
Shell* Shell::current_test_ = nullptr;
640

641
void Shell::set_test_result(Result* result)
2,306✔
642
{
643
  test_result_ = result;
2,306✔
644
}
2,306✔
645

646
void Shell::set_current_test(Shell* test)
2,306✔
647
{
648
  current_test_ = test;
2,306✔
649
}
2,306✔
650

651
Result* Shell::get_test_result()
10,716✔
652
{
653
  if (test_result_ == nullptr)
10,716✔
654
    return &OutsideTestRunnerUTest::instance().get_result();
39✔
655
  return test_result_;
10,677✔
656
}
657

658
Shell* Shell::get_current()
5,020✔
659
{
660
  if (current_test_ == nullptr)
5,020✔
661
    return &OutsideTestRunnerUTest::instance();
39✔
662
  return current_test_;
4,981✔
663
}
664

665
const Terminator& Shell::get_current_test_terminator()
1,175✔
666
{
667
  return *current_test_terminator_;
1,175✔
668
}
669

670
const Terminator& Shell::get_current_test_terminator_without_exceptions()
138✔
671
{
672
  return *current_test_terminator_without_exceptions_;
138✔
673
}
674

675
void Shell::set_crash_on_fail()
5✔
676
{
677
  current_test_terminator_ = &crashing_test_terminator;
5✔
678
  current_test_terminator_without_exceptions_ =
5✔
679
      &crashing_test_terminator_without_exceptions;
680
}
5✔
681

682
void Shell::restore_default_test_terminator()
5✔
683
{
684
  current_test_terminator_ = &normal_test_terminator;
5✔
685
  current_test_terminator_without_exceptions_ =
5✔
686
      &normal_test_terminator_without_exceptions;
687
}
5✔
688

689
void Shell::set_rethrow_exceptions(bool rethrow_exceptions)
84✔
690
{
691
  rethrow_exceptions_ = rethrow_exceptions;
84✔
692
}
84✔
693

694
bool Shell::is_rethrowing_exceptions()
17✔
695
{
696
  return rethrow_exceptions_;
17✔
697
}
698

699
} // namespace test
700
} // namespace tiny
701
} // 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