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

thetic / mutiny / 24021211194

06 Apr 2026 06:16AM UTC coverage: 98.912% (-0.06%) from 98.971%
24021211194

Pull #40

github

web-flow
Merge a69e83fde into b70bdccaa
Pull Request #40: WIP: generalize CHECK_EQUAL

21 of 22 new or added lines in 6 files covered. (95.45%)

2 existing lines in 1 file now uncovered.

5544 of 5605 relevant lines covered (98.91%)

3529.8 hits per line

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

97.53
/src/test/Failure.cpp
1
#include "mutiny/test/Failure.hpp"
2

3
#include "mutiny/test/Output.hpp"
4
#include "mutiny/test/Shell.hpp"
5

6
#include "mutiny/String.hpp"
7
#include "mutiny/math.hpp"
8

9
#if MUTINY_USE_STD_CPP_LIB
10
#include <typeinfo>
11
#if defined(__GNUC__)
12
#include <memory>
13

14
#include <cxxabi.h>
15
#endif
16
#endif
17

18
namespace mu {
19
namespace tiny {
20
namespace test {
21
namespace {
22
bool is_control_with_short_escape_sequence(char ch)
846✔
23
{
24
  return '\a' <= ch && '\r' >= ch;
846✔
25
}
26

27
void pad_strings_to_same_length(String& str1, String& str2, char pad_character)
19✔
28
{
29
  if (str1.size() > str2.size()) {
19✔
UNCOV
30
    pad_strings_to_same_length(str2, str1, pad_character);
×
UNCOV
31
    return;
×
32
  }
33

34
  str1 = String(str2.size() - str1.size(), pad_character) + str1;
19✔
35
}
36

37
size_t get_printable_size(String const& str)
100✔
38
{
39
  size_t str_size = str.size();
100✔
40
  size_t printable_str_size = str_size;
100✔
41

42
  for (size_t i = 0; i < str_size; i++) {
523✔
43
    char c = str.c_str()[i];
423✔
44
    if (is_control_with_short_escape_sequence(c)) {
423✔
45
      printable_str_size += 1;
6✔
46
    } else if (iscntrl(c)) {
417✔
47
      printable_str_size += 3;
2✔
48
    }
49
  }
50

51
  return printable_str_size;
100✔
52
}
53

54
String printable(String const& str)
100✔
55
{
56
  static const char* short_escape_codes[] = { "\\a", "\\b", "\\t", "\\n",
57
                                              "\\v", "\\f", "\\r" };
58

59
  String result;
100✔
60
  result.reserve(get_printable_size(str));
100✔
61

62
  size_t str_size = str.size();
100✔
63
  for (size_t i = 0; i < str_size; i++) {
523✔
64
    char c = str.c_str()[i];
423✔
65
    if (is_control_with_short_escape_sequence(c)) {
423✔
66
      result += short_escape_codes[static_cast<unsigned char>(c - '\a')];
6✔
67
    } else if (iscntrl(c)) {
417✔
68
      result += string_from_format("\\x%02X ", c);
2✔
69
    } else {
70
      result += c;
415✔
71
    }
72
  }
73

74
  return result;
100✔
75
}
×
76

77
String printable_string_from_or_null(const char* expected)
108✔
78
{
79
  return (expected) ? printable(string_from(expected)) : string_from("(null)");
216✔
80
}
81
} // namespace
82

83
Failure::Failure(
82✔
84
    Shell* test,
85
    const char* file_name,
86
    size_t line_number,
87
    const String& the_message
88
)
82✔
89
  : test_name_(test->get_formatted_name())
82✔
90
  , test_name_only_(test->get_name())
82✔
91
  , file_name_(file_name)
82✔
92
  , line_number_(line_number)
82✔
93
  , test_file_name_(test->get_file())
82✔
94
  , test_line_number_(test->get_line_number())
82✔
95
  , message_(the_message)
164✔
96
{
97
}
82✔
98

99
Failure::Failure(Shell* test, const String& the_message)
158✔
100
  : test_name_(test->get_formatted_name())
158✔
101
  , test_name_only_(test->get_name())
158✔
102
  , file_name_(test->get_file())
158✔
103
  , line_number_(test->get_line_number())
158✔
104
  , test_file_name_(test->get_file())
158✔
105
  , test_line_number_(test->get_line_number())
158✔
106
  , message_(the_message)
316✔
107
{
108
}
158✔
109

110
Failure::Failure(Shell* test, const char* file_name, size_t line_num)
155✔
111
  : test_name_(test->get_formatted_name())
155✔
112
  , test_name_only_(test->get_name())
155✔
113
  , file_name_(file_name)
155✔
114
  , line_number_(line_num)
155✔
115
  , test_file_name_(test->get_file())
155✔
116
  , test_line_number_(test->get_line_number())
155✔
117
  , message_("no message")
310✔
118
{
119
}
155✔
120

121
Failure::Failure(Failure&& f) noexcept
168✔
122
  : test_name_(static_cast<String&&>(f.test_name_))
168✔
123
  , test_name_only_(static_cast<String&&>(f.test_name_only_))
168✔
124
  , file_name_(static_cast<String&&>(f.file_name_))
168✔
125
  , line_number_(f.line_number_)
168✔
126
  , test_file_name_(static_cast<String&&>(f.test_file_name_))
168✔
127
  , test_line_number_(f.test_line_number_)
168✔
128
  , message_(static_cast<String&&>(f.message_))
336✔
129
{
130
}
168✔
131

132
const String& Failure::get_file_name() const
163✔
133
{
134
  return file_name_;
163✔
135
}
136

137
const String& Failure::get_test_file_name() const
111✔
138
{
139
  return test_file_name_;
111✔
140
}
141

142
const String& Failure::get_test_name() const
137✔
143
{
144
  return test_name_;
137✔
145
}
146

147
const String& Failure::get_test_name_only() const
×
148
{
149
  return test_name_only_;
×
150
}
151

152
size_t Failure::get_failure_line_number() const
163✔
153
{
154
  return line_number_;
163✔
155
}
156

157
size_t Failure::get_test_line_number() const
111✔
158
{
159
  return test_line_number_;
111✔
160
}
161

162
const String& Failure::get_message() const
335✔
163
{
164
  return message_;
335✔
165
}
166

167
bool Failure::is_outside_test_file() const
137✔
168
{
169
  return test_file_name_ != file_name_;
137✔
170
}
171

172
bool Failure::is_in_helper_function() const
28✔
173
{
174
  return line_number_ < test_line_number_;
28✔
175
}
176

177
String Failure::create_but_was_string(
109✔
178
    const String& expected,
179
    const String& actual
180
)
181
{
182
  return string_from_format(
183
      "expected <%s>\n\tbut was  <%s>", expected.c_str(), actual.c_str()
184
  );
109✔
185
}
186

187
String Failure::create_difference_at_pos_string(
56✔
188
    const String& actual,
189
    size_t offset,
190
    size_t reported_position
191
)
192
{
193
  String result;
56✔
194
  const size_t extra_characters_window = 20;
56✔
195
  const size_t half_of_extra_characters_window = extra_characters_window / 2;
56✔
196

197
  String padding_for_preventing_out_of_bounds(
198
      half_of_extra_characters_window, ' '
199
  );
56✔
200
  String actual_string = padding_for_preventing_out_of_bounds + actual +
112✔
201
                         padding_for_preventing_out_of_bounds;
56✔
202
  String different_string = string_from_format(
203
      "difference starts at position %lu at: <",
204
      static_cast<unsigned long>(reported_position)
205
  );
56✔
206

207
  result += "\n";
56✔
208
  result += string_from_format(
112✔
209
      "\t%s%s>\n",
210
      different_string.c_str(),
211
      actual_string.substr(offset, extra_characters_window).c_str()
112✔
212
  );
56✔
213

214
  result += string_from_format(
112✔
215
      "\t%s^",
216
      String(different_string.size() + half_of_extra_characters_window, ' ')
112✔
217
          .c_str()
218
  );
56✔
219
  return result;
112✔
220
}
56✔
221

222
String Failure::create_user_text(const String& text)
126✔
223
{
224
  String user_message = "";
126✔
225
  if (!text.empty()) {
126✔
226
    user_message += "Message: ";
41✔
227
    user_message += text;
41✔
228
    user_message += "\n\t";
41✔
229
  }
230
  return user_message;
126✔
231
}
×
232

233
EqualsFailure::EqualsFailure(
4✔
234
    Shell* test,
235
    const char* file_name,
236
    size_t line_number,
237
    const char* expected,
238
    const char* actual,
239
    const String& text
240
)
4✔
241
  : Failure(test, file_name, line_number)
4✔
242
{
243
  message_ = create_user_text(text);
4✔
244

245
  message_ += create_but_was_string(
8✔
246
      string_from_or_null(expected), string_from_or_null(actual)
8✔
247
  );
4✔
248
}
4✔
249

250
EqualsFailure::EqualsFailure(
6✔
251
    Shell* test,
252
    const char* file_name,
253
    size_t line_number,
254
    const String& expected,
255
    const String& actual,
256
    const String& text
257
)
6✔
258
  : Failure(test, file_name, line_number)
6✔
259
{
260
  message_ = create_user_text(text);
6✔
261

262
  message_ += create_but_was_string(expected, actual);
6✔
263
}
6✔
264

265
DoublesEqualFailure::DoublesEqualFailure(
12✔
266
    Shell* test,
267
    const char* file_name,
268
    size_t line_number,
269
    double expected,
270
    double actual,
271
    double threshold,
272
    const String& text
273
)
12✔
274
  : Failure(test, file_name, line_number)
12✔
275
{
276
  message_ = create_user_text(text);
12✔
277

278
  message_ +=
279
      create_but_was_string(string_from(expected, 7), string_from(actual, 7));
12✔
280
  message_ += " threshold used was <";
12✔
281
  message_ += string_from(threshold, 7);
12✔
282
  message_ += ">";
12✔
283

284
  if (is_nan(expected) || is_nan(actual) || is_nan(threshold))
12✔
285
    message_ += "\n\tCannot make comparisons with Nan";
4✔
286
}
12✔
287

288
CheckEqualFailure::CheckEqualFailure(
26✔
289
    Shell* test,
290
    const char* file_name,
291
    size_t line_number,
292
    const String& expected,
293
    const String& actual,
294
    const String& text
295
)
26✔
296
  : Failure(test, file_name, line_number)
26✔
297
{
298
  message_ = create_user_text(text);
26✔
299

300
  String printable_expected = printable_string_from_or_null(expected.c_str());
26✔
301
  String printable_actual = printable_string_from_or_null(actual.c_str());
26✔
302

303
  message_ += create_but_was_string(printable_expected, printable_actual);
26✔
304

305
  size_t fail_start;
306
  for (fail_start = 0; actual[fail_start] == expected[fail_start]; fail_start++)
35✔
307
    ;
308
  size_t fail_start_printable;
309
  for (fail_start_printable = 0; printable_actual[fail_start_printable] ==
36✔
310
                                 printable_expected[fail_start_printable];
36✔
311
       fail_start_printable++)
312
    ;
313
  message_ += create_difference_at_pos_string(
52✔
314
      printable_actual, fail_start_printable, fail_start
315
  );
26✔
316
}
26✔
317

318
ComparisonFailure::ComparisonFailure(
2✔
319
    Shell* test,
320
    const char* file_name,
321
    size_t line_number,
322
    const String& check_string,
323
    const String& comparison_string,
324
    const String& text
325
)
2✔
326
  : Failure(test, file_name, line_number)
2✔
327
{
328
  message_ = create_user_text(text);
2✔
329
  message_ += check_string;
2✔
330
  message_ += "(";
2✔
331
  message_ += comparison_string;
2✔
332
  message_ += ") failed";
2✔
333
}
2✔
334

335
ContainsFailure::ContainsFailure(
4✔
336
    Shell* test,
337
    const char* file_name,
338
    size_t line_number,
339
    const String& expected,
340
    const String& actual,
341
    const String& text
342
)
4✔
343
  : Failure(test, file_name, line_number)
4✔
344
{
345
  message_ = create_user_text(text);
4✔
346

347
  message_ += string_from_format(
8✔
348
      "actual <%s>\n\tdid not contain  <%s>", actual.c_str(), expected.c_str()
349
  );
4✔
350
}
4✔
351

352
CheckFailure::CheckFailure(
10✔
353
    Shell* test,
354
    const char* file_name,
355
    size_t line_number,
356
    const String& check_string,
357
    const String& condition_string,
358
    const String& text
359
)
10✔
360
  : Failure(test, file_name, line_number)
10✔
361
{
362
  message_ = create_user_text(text);
10✔
363

364
  message_ += check_string;
10✔
365
  message_ += "(";
10✔
366
  message_ += condition_string;
10✔
367
  message_ += ") failed";
10✔
368
}
10✔
369

370
FailFailure::FailFailure(
28✔
371
    Shell* test,
372
    const char* file_name,
373
    size_t line_number,
374
    const String& message
375
)
28✔
376
  : Failure(test, file_name, line_number)
28✔
377
{
378
  message_ = message;
28✔
379
}
28✔
380

381
IntMaxEqualFailure::IntMaxEqualFailure(
9✔
382
    Shell* test,
383
    const char* file_name,
384
    size_t line_number,
385
    long long expected,
386
    long long actual,
387
    const String& text
388
)
9✔
389
  : Failure(test, file_name, line_number)
9✔
390
{
391
  message_ = create_user_text(text);
9✔
392

393
  String a_decimal = string_from(actual);
9✔
394
  String e_decimal = string_from(expected);
9✔
395

396
  pad_strings_to_same_length(a_decimal, e_decimal, ' ');
9✔
397

398
  String actual_reported =
399
      a_decimal + " " + brackets_formatted_hex_string_from(actual);
9✔
400
  String expected_reported =
401
      e_decimal + " " + brackets_formatted_hex_string_from(expected);
9✔
402
  message_ += create_but_was_string(expected_reported, actual_reported);
9✔
403
}
9✔
404

405
UintMaxEqualFailure::UintMaxEqualFailure(
7✔
406
    Shell* test,
407
    const char* file_name,
408
    size_t line_number,
409
    unsigned long long expected,
410
    unsigned long long actual,
411
    const String& text
412
)
7✔
413
  : Failure(test, file_name, line_number)
7✔
414
{
415
  message_ = create_user_text(text);
7✔
416

417
  String a_decimal = string_from(actual);
7✔
418
  String e_decimal = string_from(expected);
7✔
419

420
  pad_strings_to_same_length(a_decimal, e_decimal, ' ');
7✔
421

422
  String actual_reported =
423
      a_decimal + " " + brackets_formatted_hex_string_from(actual);
7✔
424
  String expected_reported =
425
      e_decimal + " " + brackets_formatted_hex_string_from(expected);
7✔
426
  message_ += create_but_was_string(expected_reported, actual_reported);
7✔
427
}
7✔
428

429
SignedBytesEqualFailure::SignedBytesEqualFailure(
3✔
430
    Shell* test,
431
    const char* file_name,
432
    size_t line_number,
433
    signed char expected,
434
    signed char actual,
435
    const String& text
436
)
3✔
437
  : Failure(test, file_name, line_number)
3✔
438
{
439
  message_ = create_user_text(text);
3✔
440

441
  String a_decimal = string_from(static_cast<int>(actual));
3✔
442
  String e_decimal = string_from(static_cast<int>(expected));
3✔
443

444
  pad_strings_to_same_length(a_decimal, e_decimal, ' ');
3✔
445

446
  String actual_reported =
447
      a_decimal + " " + brackets_formatted_hex_string_from(actual);
3✔
448
  String expected_reported =
449
      e_decimal + " " + brackets_formatted_hex_string_from(expected);
3✔
450
  message_ += create_but_was_string(expected_reported, actual_reported);
3✔
451
}
3✔
452

453
StringEqualFailure::StringEqualFailure(
23✔
454
    Shell* test,
455
    const char* file_name,
456
    size_t line_number,
457
    const char* expected,
458
    const char* actual,
459
    const String& text
460
)
23✔
461
  : Failure(test, file_name, line_number)
23✔
462
{
463
  message_ = create_user_text(text);
23✔
464

465
  String printable_expected = printable_string_from_or_null(expected);
23✔
466
  String printable_actual = printable_string_from_or_null(actual);
23✔
467

468
  message_ += create_but_was_string(printable_expected, printable_actual);
23✔
469
  if ((expected) && (actual)) {
23✔
470
    size_t fail_start;
471
    for (fail_start = 0; actual[fail_start] == expected[fail_start];
85✔
472
         fail_start++)
473
      ;
474
    size_t fail_start_printable;
475
    for (fail_start_printable = 0; printable_actual[fail_start_printable] ==
87✔
476
                                   printable_expected[fail_start_printable];
87✔
477
         fail_start_printable++)
478
      ;
479
    message_ += create_difference_at_pos_string(
34✔
480
        printable_actual, fail_start_printable, fail_start
481
    );
17✔
482
  }
483
}
23✔
484

485
StringEqualNoCaseFailure::StringEqualNoCaseFailure(
5✔
486
    Shell* test,
487
    const char* file_name,
488
    size_t line_number,
489
    const char* expected,
490
    const char* actual,
491
    const String& text
492
)
5✔
493
  : Failure(test, file_name, line_number)
5✔
494
{
495
  message_ = create_user_text(text);
5✔
496

497
  String printable_expected = printable_string_from_or_null(expected);
5✔
498
  String printable_actual = printable_string_from_or_null(actual);
5✔
499

500
  message_ += create_but_was_string(printable_expected, printable_actual);
5✔
501
  if ((expected) && (actual)) {
5✔
502
    size_t fail_start;
503
    for (fail_start = 0;
8✔
504
         tolower(actual[fail_start]) == tolower(expected[fail_start]);
8✔
505
         fail_start++)
506
      ;
507
    size_t fail_start_printable;
508
    for (fail_start_printable = 0;
8✔
509
         tolower(printable_actual[fail_start_printable]) ==
8✔
510
         tolower(printable_expected[fail_start_printable]);
8✔
511
         fail_start_printable++)
512
      ;
513
    message_ += create_difference_at_pos_string(
6✔
514
        printable_actual, fail_start_printable, fail_start
515
    );
3✔
516
  }
517
}
5✔
518

519
BinaryEqualFailure::BinaryEqualFailure(
14✔
520
    Shell* test,
521
    const char* file_name,
522
    size_t line_number,
523
    const unsigned char* expected,
524
    const unsigned char* actual,
525
    size_t size,
526
    const String& text
527
)
14✔
528
  : Failure(test, file_name, line_number)
14✔
529
{
530
  message_ = create_user_text(text);
14✔
531

532
  String actual_hex = string_from_binary_or_null(actual, size);
14✔
533

534
  message_ += create_but_was_string(
28✔
535
      string_from_binary_or_null(expected, size), actual_hex
28✔
536
  );
14✔
537
  if ((expected) && (actual)) {
14✔
538
    size_t fail_start;
539
    for (fail_start = 0; actual[fail_start] == expected[fail_start];
25✔
540
         fail_start++)
541
      ;
542
    message_ += create_difference_at_pos_string(
10✔
543
        actual_hex, (fail_start * 3 + 1), fail_start
10✔
544
    );
10✔
545
  }
546
}
14✔
547

548
FeatureUnsupportedFailure::FeatureUnsupportedFailure(
1✔
549
    Shell* test,
550
    const char* file_name,
551
    size_t line_number,
552
    const String& feature_name,
553
    const String& text
554
)
1✔
555
  : Failure(test, file_name, line_number)
1✔
556
{
557
  message_ = create_user_text(text);
1✔
558

559
  message_ += string_from_format(
2✔
560
      "The feature \"%s\" is not supported in this environment or with the "
561
      "feature set selected when building the library.",
562
      feature_name.c_str()
563
  );
1✔
564
}
1✔
565

566
#if MUTINY_HAVE_EXCEPTIONS
567
UnexpectedExceptionFailure::UnexpectedExceptionFailure(Shell* test)
9✔
568
  : Failure(test, "Unexpected exception of unknown type was thrown.")
9✔
569
{
570
}
9✔
571

572
#if MUTINY_USE_STD_CPP_LIB
573
#if MUTINY_HAVE_RTTI
574
namespace {
575
String get_exception_type_name(const std::exception& e)
5✔
576
{
577
  const char* name = typeid(e).name();
5✔
578
#if defined(__GNUC__)
579
  int status = -1;
5✔
580

581
  std::unique_ptr<char, void (*)(void*)> demangled_name(
582
      abi::__cxa_demangle(name, nullptr, nullptr, &status), free
×
583
  );
5✔
584

585
  return (status == 0) ? demangled_name.get() : name;
10✔
586
#else
587
  return name;
588
#endif
589
}
5✔
590
} // namespace
591
#endif // MUTINY_HAVE_RTTI
592

593
UnexpectedExceptionFailure::UnexpectedExceptionFailure(
5✔
594
    Shell* test,
595
    const std::exception& e
596
)
5✔
597
  : Failure(
598
        test,
599
#if MUTINY_HAVE_RTTI
600
        string_from_format(
10✔
601
            "Unexpected exception of type '%s' was thrown: %s",
602
            get_exception_type_name(e).c_str(),
10✔
603
            e.what()
5✔
604
        )
605
#else
606
        "Unexpected exception of unknown type was thrown."
607
#endif
608
    )
10✔
609
{
610
  (void)e;
611
}
5✔
612
#endif // MUTINY_USE_STD_CPP_LIB
613
#endif // MUTINY_HAVE_EXCEPTIONS
614

615
} // namespace test
616
} // namespace tiny
617
} // 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