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

thetic / mu.tiny / 24941705800

25 Apr 2026 10:02PM UTC coverage: 98.931% (+0.005%) from 98.926%
24941705800

Pull #91

github

web-flow
Merge 4f771f02e into 150ab4b90
Pull Request #91: replace multi-char single-dash flags with -- long options

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

2 existing lines in 2 files now uncovered.

5367 of 5425 relevant lines covered (98.93%)

3406.83 hits per line

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

99.36
/src/test/CommandLineArguments.cpp
1
#include "mu/tiny/test/CommandLineArguments.hpp"
2

3
#include "mu/tiny/test/Plugin.hpp"
4
#include "mu/tiny/test/Registry.hpp"
5

6
#include "mu/tiny/StringCollection.hpp"
7
#include "mu/tiny/time.hpp"
8
#include "mu/tiny/version.h"
9

10
namespace mu {
11
namespace tiny {
12
namespace test {
13

14
namespace {
15
class ParsedField
16
{
17
public:
18
  String value;
19
  int extra;
20
};
21

22
ParsedField get_parameter_field(
85✔
23
    int argc,
24
    const char* const* argv,
25
    const String& parameter_name
26
)
27
{
28
  size_t parameter_length = parameter_name.size();
85✔
29
  String parameter(argv[0]);
85✔
30
  if (parameter.size() > parameter_length) {
85✔
31
    size_t offset = parameter_length;
22✔
32
    if (argv[0][offset] == '=') {
22✔
33
      offset++;
17✔
34
    }
35
    ParsedField result = { String(argv[0] + offset), 0 };
22✔
36
    return result;
22✔
37
  }
22✔
38
  if (argc > 1) {
63✔
39
    ParsedField result = { String(argv[1]), 1 };
62✔
40
    return result;
62✔
41
  }
62✔
42
  ParsedField result = { String(""), 0 };
1✔
43
  return result;
1✔
44
}
85✔
45

46
String sub_string_from_till(
6✔
47
    const String& str,
48
    char start_char,
49
    char last_excluded_char
50
)
51
{
52
  size_t begin_pos = str.find(start_char);
6✔
53
  if (begin_pos == String::npos) {
6✔
54
    return "";
×
55
  }
56

57
  size_t end_pos = str.find(last_excluded_char, begin_pos);
6✔
58
  if (end_pos == String::npos) {
6✔
59
    return str.substr(begin_pos);
1✔
60
  }
61

62
  return str.substr(begin_pos, end_pos - begin_pos);
5✔
63
}
64
} // namespace
65

66
CommandLineArguments::CommandLineArguments(int argc, const char* const* argv)
146✔
67
  : ac_(argc)
146✔
68
  , av_(argv)
146✔
69

70
{
71
}
146✔
72

73
CommandLineArguments::~CommandLineArguments()
292✔
74
{
75
  while (group_filters_ != nullptr) {
358✔
76
    Filter* current = group_filters_;
66✔
77
    group_filters_ = group_filters_->get_next();
66✔
78
    delete current;
66✔
79
  }
80
  while (name_filters_ != nullptr) {
315✔
81
    Filter* current = name_filters_;
23✔
82
    name_filters_ = name_filters_->get_next();
23✔
83
    delete current;
23✔
84
  }
85
}
292✔
86

87
bool CommandLineArguments::parse_simple_flag(const String& argument)
207✔
88
{
89
  if ((argument == "-h") || (argument == "--help")) {
207✔
90
    need_help_ = true;
4✔
91
    return true;
4✔
92
  }
93
  if ((argument == "-v") || (argument == "--verbose") ||
562✔
94
      (argument == "--verbose=1")) {
359✔
95
    verbose_ = true;
48✔
96
    return true;
48✔
97
  }
98
  if ((argument == "-vv") || (argument == "--verbose=2")) {
155✔
99
    very_verbose_ = true;
14✔
100
    return true;
14✔
101
  }
102
  if ((argument == "-c") || (argument == "--color")) {
141✔
103
    color_ = true;
13✔
104
    return true;
13✔
105
  }
106
  if ((argument == "-b") || (argument == "--reverse")) {
128✔
107
    reversing_ = true;
3✔
108
    return true;
3✔
109
  }
110
  if (argument == "--list-groups") {
125✔
111
    list_test_group_names_ = true;
2✔
112
    return true;
2✔
113
  }
114
  if (argument == "--list-tests") {
123✔
115
    list_test_group_and_case_names_ = true;
2✔
116
    return true;
2✔
117
  }
118
  if (argument == "--list-ordered-locations") {
121✔
119
    list_ordered_test_locations_ = true;
1✔
120
    return true;
1✔
121
  }
122
  if (argument == "--list-group-locations") {
120✔
123
    list_test_group_locations_ = true;
2✔
124
    return true;
2✔
125
  }
126
  if (argument == "--list-locations") {
118✔
127
    list_test_locations_ = true;
2✔
128
    return true;
2✔
129
  }
130
  if (argument == "--run-skipped") {
116✔
131
    run_skipped_ = true;
2✔
132
    return true;
2✔
133
  }
134
  if ((argument == "-f") || (argument == "--crash-on-fail")) {
114✔
135
    crash_on_fail_ = true;
2✔
136
    return true;
2✔
137
  }
138
  if ((argument == "-e") || (argument == "--no-rethrow")) {
112✔
139
    rethrow_exceptions_ = false;
2✔
140
    return true;
2✔
141
  }
142
  return false;
110✔
143
}
144

145
int CommandLineArguments::parse_prefix_arg(
110✔
146
    int argc,
147
    const char* const* argv,
148
    Plugin* plugin
149
)
150
{
151
  String argument(argv[0]);
110✔
152
  if (string_starts_with(argument, "--repeat")) {
110✔
153
    return set_repeat_count(argc, argv, "--repeat");
3✔
154
  }
155
  if (string_starts_with(argument, "--shuffle")) {
107✔
156
    return set_shuffle(argc, argv, "--shuffle");
2✔
157
  }
158
  if (string_starts_with(argument, "-r")) {
105✔
159
    return set_repeat_count(argc, argv);
3✔
160
  }
161
  if (string_starts_with(argument, "--exact-group")) {
102✔
162
    return add_strict_group_filter(argc, argv);
48✔
163
  }
164
  if (string_starts_with(argument, "--exclude-exact-group")) {
54✔
165
    return add_exclude_strict_group_filter(argc, argv);
2✔
166
  }
167
  if (string_starts_with(argument, "--exclude-group")) {
52✔
168
    return add_exclude_group_filter(argc, argv);
2✔
169
  }
170
  if (string_starts_with(argument, "--exact-name")) {
50✔
171
    return add_strict_name_filter(argc, argv);
6✔
172
  }
173
  if (string_starts_with(argument, "--exclude-exact-name")) {
44✔
174
    return add_exclude_strict_name_filter(argc, argv);
2✔
175
  }
176
  if (string_starts_with(argument, "--exclude-name")) {
42✔
177
    return add_exclude_name_filter(argc, argv);
3✔
178
  }
179
  if (string_starts_with(argument, "--exact-test")) {
39✔
180
    return add_group_dot_name_filter(argc, argv, "--exact-test", true, false);
2✔
181
  }
182
  if (string_starts_with(argument, "--exclude-exact-test")) {
37✔
183
    return add_group_dot_name_filter(
4✔
184
        argc, argv, "--exclude-exact-test", true, true
185
    );
2✔
186
  }
187
  if (string_starts_with(argument, "--exclude-test")) {
35✔
188
    return add_group_dot_name_filter(argc, argv, "--exclude-test", false, true);
2✔
189
  }
190
  if (string_starts_with(argument, "--group")) {
33✔
191
    return add_group_filter(argc, argv, "--group");
2✔
192
  }
193
  if (string_starts_with(argument, "--name")) {
31✔
194
    return add_name_filter(argc, argv, "--name");
2✔
195
  }
196
  if (string_starts_with(argument, "--test")) {
29✔
197
    return add_group_dot_name_filter(argc, argv, "--test", false, false);
1✔
198
  }
199
  if (string_starts_with(argument, "-g")) {
28✔
200
    return add_group_filter(argc, argv);
4✔
201
  }
202
  if (string_starts_with(argument, "-t")) {
24✔
203
    return add_group_dot_name_filter(argc, argv, "-t", false, false);
2✔
204
  }
205
  if (string_starts_with(argument, "-n")) {
22✔
206
    return add_name_filter(argc, argv);
2✔
207
  }
208
  if (string_starts_with(argument, "-s")) {
20✔
209
    return set_shuffle(argc, argv);
9✔
210
  }
211
  if (string_starts_with(argument, "TEST(")) {
11✔
212
    return add_test_to_run_based_on_verbose_output(argc, argv, "TEST(");
2✔
213
  }
214
  if (string_starts_with(argument, "SKIPPED_TEST(")) {
9✔
215
    return add_test_to_run_based_on_verbose_output(argc, argv, "SKIPPED_TEST(");
1✔
216
  }
217
  if (string_starts_with(argument, "-p")) {
8✔
218
    return plugin->parse_all_arguments(argc, argv) ? 0 : -1;
4✔
219
  }
220
  return -1;
4✔
221
}
110✔
222

223
int CommandLineArguments::parse_argument(
207✔
224
    int argc,
225
    const char* const* argv,
226
    Plugin* plugin
227
)
228
{
229
  if (parse_simple_flag(String(argv[0]))) {
207✔
230
    return 0;
97✔
231
  }
232
  return parse_prefix_arg(argc, argv, plugin);
110✔
233
}
234

235
bool CommandLineArguments::parse(Plugin* plugin)
146✔
236
{
237
  for (int i = 1; i < ac_; i++) {
343✔
238
    int extra = parse_argument(ac_ - i, av_ + i, plugin);
207✔
239
    if (extra < 0) {
207✔
240
      return false;
10✔
241
    }
242
    i += extra;
197✔
243
  }
244
  return true;
136✔
245
}
246

247
String CommandLineArguments::help()
5✔
248
{
249
  String help_str =
250
      "mutiny v" MUTINY_VERSION_STRING "\n\n"
251
      "Options that do not run tests but query:\n"
252
      "  -h, --help                 - this wonderful help screen. Joy!\n"
253
      "  --list-groups              - print a list of group names, separated "
254
      "by spaces\n"
255
      "  --list-tests               - print a list of test names in the form "
256
      "of group.name, separated by spaces\n"
257
      "  --list-locations           - print a list of test names in the form "
258
      "of group.name.test_file_path.line\n"
259
      "  --list-ordered-locations   - print a list of ordered test names in "
260
      "the form of group.name.test_file_path.line\n"
261
      "  --list-group-locations     - print a list of group locations in the "
262
      "form of group.file_path.line\n"
263
      "\n"
264
      "Options that change the output format:\n"
265
      "  -c, --color                - colorize output, print green if OK, or "
266
      "red if failed\n"
267
      "  -v, --verbose[=1]          - verbose, print each test name as it "
268
      "runs\n"
269
      "  -vv, --verbose=2           - very verbose, print internal information "
270
      "during test run\n";
5✔
271

272
  Plugin* plugin = Registry::get_current_registry()->get_first_plugin();
5✔
273
  String plugin_help = plugin->get_all_help();
5✔
274

275
  if (!plugin_help.empty()) {
5✔
276
    help_str += "\nOptions that are provided by plugins:\n";
5✔
277
    help_str += plugin_help;
5✔
278
  }
279

280
  help_str +=
281
      "\n"
282
      "Options that control which tests are run:\n"
283
      "  -g, --group <group>           - only run tests whose group contains "
284
      "<group>\n"
285
      "  -n, --name <name>             - only run tests whose name contains "
286
      "<name>\n"
287
      "  -t, --test <group>.<name>     - only run tests whose group and name "
288
      "contain <group> and <name>\n"
289
      "  --exact-group <group>         - only run tests whose group exactly "
290
      "matches <group>\n"
291
      "  --exact-name <name>           - only run tests whose name exactly "
292
      "matches <name>\n"
293
      "  --exact-test <grp>.<name>     - only run tests whose group and name "
294
      "exactly match <grp> and <name>\n"
295
      "  --exclude-group <group>       - exclude tests whose group contains "
296
      "<group>\n"
297
      "  --exclude-name <name>         - exclude tests whose name contains "
298
      "<name>\n"
299
      "  --exclude-test <grp>.<name>   - exclude tests whose group and name "
300
      "contain <grp> and <name>\n"
301
      "  --exclude-exact-group <group> - exclude tests whose group exactly "
302
      "matches <group>\n"
303
      "  --exclude-exact-name <name>   - exclude tests whose name exactly "
304
      "matches <name>\n"
305
      "  --exclude-exact-test <grp>.<name>\n"
306
      "                                - exclude tests whose group and name "
307
      "exactly match <grp> and <name>\n"
308
      "  \"[SKIPPED_]TEST(<group>, <name>)\"\n"
309
      "                    - only run tests whose group and name exactly "
310
      "match <group> and <name>\n"
311
      "                      (this can be used to copy-paste output from "
312
      "the -v option on the command line)\n"
313
      "\n"
314
      "Options that control how the tests are run:\n"
315
      "  -b, --reverse               - run the tests backwards, reversing the "
316
      "normal way\n"
317
      "  -s, --shuffle [<seed>]      - shuffle tests randomly (randomization "
318
      "seed is optional, must be greater than 0)\n"
319
      "  -r, --repeat [<#>]          - repeat the tests <#> times (or twice "
320
      "if <#> is not specified)\n"
321
      "  --run-skipped               - run skipped tests as if they are not "
322
      "skipped\n"
323
      "  -f, --crash-on-fail         - Cause the tests to crash on failure "
324
      "(to allow the test to be debugged if necessary)\n"
325
      "  -e, --no-rethrow            - do not rethrow unexpected exceptions on "
326
      "failure\n";
5✔
327

328
  return help_str;
10✔
329
}
5✔
330

331
bool CommandLineArguments::need_help() const
73✔
332
{
333
  return need_help_;
73✔
334
}
335

336
bool CommandLineArguments::is_verbose() const
87✔
337
{
338
  return verbose_;
87✔
339
}
340

341
bool CommandLineArguments::is_very_verbose() const
86✔
342
{
343
  return very_verbose_;
86✔
344
}
345

346
bool CommandLineArguments::is_color() const
72✔
347
{
348
  return color_;
72✔
349
}
350

351
bool CommandLineArguments::is_listing_test_group_names() const
71✔
352
{
353
  return list_test_group_names_;
71✔
354
}
355

356
bool CommandLineArguments::is_listing_test_group_and_case_names() const
70✔
357
{
358
  return list_test_group_and_case_names_;
70✔
359
}
360

361
bool CommandLineArguments::is_listing_test_locations() const
68✔
362
{
363
  return list_test_locations_;
68✔
364
}
365

366
bool CommandLineArguments::is_listing_ordered_test_locations() const
66✔
367
{
368
  return list_ordered_test_locations_;
66✔
369
}
370

371
bool CommandLineArguments::is_listing_test_group_locations() const
66✔
372
{
373
  return list_test_group_locations_;
66✔
374
}
375

376
bool CommandLineArguments::is_run_skipped() const
71✔
377
{
378
  return run_skipped_;
71✔
379
}
380

381
unsigned int CommandLineArguments::get_repeat_count() const
77✔
382
{
383
  return repeat_;
77✔
384
}
385

386
bool CommandLineArguments::is_reversing() const
66✔
387
{
388
  return reversing_;
66✔
389
}
390

391
bool CommandLineArguments::is_crashing_on_fail() const
73✔
392
{
393
  return crash_on_fail_;
73✔
394
}
395

396
bool CommandLineArguments::is_rethrowing_exceptions() const
73✔
397
{
398
  return rethrow_exceptions_;
73✔
399
}
400

401
bool CommandLineArguments::is_shuffling() const
133✔
402
{
403
  return shuffling_;
133✔
404
}
405

406
unsigned int CommandLineArguments::get_shuffle_seed() const
9✔
407
{
408
  return shuffle_seed_;
9✔
409
}
410

411
const Filter* CommandLineArguments::get_group_filters() const
91✔
412
{
413
  return group_filters_;
91✔
414
}
415

416
const Filter* CommandLineArguments::get_name_filters() const
89✔
417
{
418
  return name_filters_;
89✔
419
}
420

421
int CommandLineArguments::set_repeat_count(
6✔
422
    int argc,
423
    const char* const* argv,
424
    const String& flag
425
)
426
{
427
  repeat_ = 0;
6✔
428
  int extra = 0;
6✔
429

430
  size_t flag_length = flag.size();
6✔
431
  String repeat_parameter(argv[0]);
6✔
432
  if (repeat_parameter.size() > flag_length) {
6✔
433
    size_t offset = flag_length;
2✔
434
    if (argv[0][offset] == '=') {
2✔
435
      offset++;
1✔
436
    }
437
    repeat_ = static_cast<unsigned int>(strtoul(argv[0] + offset));
2✔
438
  } else if (argc > 1) {
4✔
439
    repeat_ = static_cast<unsigned int>(strtoul(argv[1]));
2✔
440
    if (repeat_ != 0) {
2✔
441
      extra = 1;
2✔
442
    }
443
  }
444

445
  if (0 == repeat_) {
6✔
446
    repeat_ = 2;
2✔
447
  }
448
  return extra;
6✔
449
}
6✔
450

451
int CommandLineArguments::set_shuffle(
11✔
452
    int argc,
453
    const char* const* argv,
454
    const String& flag
455
)
456
{
457
  shuffling_ = true;
11✔
458
  shuffle_seed_ = static_cast<unsigned int>(get_time_in_millis());
11✔
459
  if (shuffle_seed_ == 0) {
11✔
UNCOV
460
    shuffle_seed_++;
×
461
  }
462

463
  int extra = 0;
11✔
464
  size_t flag_length = flag.size();
11✔
465
  String shuffle_parameter = argv[0];
11✔
466
  if (shuffle_parameter.size() > flag_length) {
11✔
467
    shuffling_pre_seeded_ = true;
6✔
468
    size_t offset = flag_length;
6✔
469
    if (argv[0][offset] == '=') {
6✔
470
      offset++;
1✔
471
    }
472
    shuffle_seed_ = static_cast<unsigned>(strtoul(argv[0] + offset));
6✔
473
  } else if (argc > 1) {
5✔
474
    auto parsed_parameter = static_cast<unsigned>(strtoul(argv[1]));
2✔
475
    if (parsed_parameter != 0) {
2✔
476
      shuffling_pre_seeded_ = true;
1✔
477
      shuffle_seed_ = parsed_parameter;
1✔
478
      extra = 1;
1✔
479
    }
480
  }
481
  return (shuffle_seed_ != 0) ? extra : -1;
22✔
482
}
11✔
483

484
int CommandLineArguments::add_group_filter(
6✔
485
    int argc,
486
    const char* const* argv,
487
    const String& flag
488
)
489
{
490
  ParsedField field = get_parameter_field(argc, argv, flag);
6✔
491
  auto* group_filter = new Filter(field.value);
6✔
492
  group_filters_ = group_filter->add(group_filters_);
6✔
493
  return field.extra;
6✔
494
}
6✔
495

496
int CommandLineArguments::add_group_dot_name_filter(
9✔
497
    int argc,
498
    const char* const* argv,
499
    const String& parameter_name,
500
    bool strict,
501
    bool exclude
502
)
503
{
504
  ParsedField field = get_parameter_field(argc, argv, parameter_name);
9✔
505
  StringCollection collection(field.value, '.');
9✔
506

507
  if (collection.size() != 2) {
9✔
508
    return -1;
4✔
509
  }
510

511
  auto* group_filter =
512
      new Filter(collection[0].substr(0, collection[0].size() - 1));
5✔
513
  auto* name_filter = new Filter(collection[1]);
5✔
514
  if (strict) {
5✔
515
    group_filter->strict_matching();
2✔
516
    name_filter->strict_matching();
2✔
517
  }
518
  if (exclude) {
5✔
519
    group_filter->invert_matching();
2✔
520
    name_filter->invert_matching();
2✔
521
  }
522
  group_filters_ = group_filter->add(group_filters_);
5✔
523
  name_filters_ = name_filter->add(name_filters_);
5✔
524
  return field.extra;
5✔
525
}
9✔
526

527
int CommandLineArguments::add_strict_group_filter(
48✔
528
    int argc,
529
    const char* const* argv
530
)
531
{
532
  ParsedField field = get_parameter_field(argc, argv, "--exact-group");
48✔
533
  auto* group_filter = new Filter(field.value);
48✔
534
  group_filter->strict_matching();
48✔
535
  group_filters_ = group_filter->add(group_filters_);
48✔
536
  return field.extra;
48✔
537
}
48✔
538

539
int CommandLineArguments::add_exclude_group_filter(
2✔
540
    int argc,
541
    const char* const* argv
542
)
543
{
544
  ParsedField field = get_parameter_field(argc, argv, "--exclude-group");
2✔
545
  auto* group_filter = new Filter(field.value);
2✔
546
  group_filter->invert_matching();
2✔
547
  group_filters_ = group_filter->add(group_filters_);
2✔
548
  return field.extra;
2✔
549
}
2✔
550

551
int CommandLineArguments::add_exclude_strict_group_filter(
2✔
552
    int argc,
553
    const char* const* argv
554
)
555
{
556
  ParsedField field = get_parameter_field(argc, argv, "--exclude-exact-group");
2✔
557
  auto* group_filter = new Filter(field.value);
2✔
558
  group_filter->strict_matching();
2✔
559
  group_filter->invert_matching();
2✔
560
  group_filters_ = group_filter->add(group_filters_);
2✔
561
  return field.extra;
2✔
562
}
2✔
563

564
int CommandLineArguments::add_name_filter(
4✔
565
    int argc,
566
    const char* const* argv,
567
    const String& flag
568
)
569
{
570
  ParsedField field = get_parameter_field(argc, argv, flag);
4✔
571
  auto* name_filter = new Filter(field.value);
4✔
572
  name_filters_ = name_filter->add(name_filters_);
4✔
573
  return field.extra;
4✔
574
}
4✔
575

576
int CommandLineArguments::add_strict_name_filter(
6✔
577
    int argc,
578
    const char* const* argv
579
)
580
{
581
  ParsedField field = get_parameter_field(argc, argv, "--exact-name");
6✔
582
  auto* name_filter = new Filter(field.value);
6✔
583
  name_filter->strict_matching();
6✔
584
  name_filters_ = name_filter->add(name_filters_);
6✔
585
  return field.extra;
6✔
586
}
6✔
587

588
int CommandLineArguments::add_exclude_name_filter(
3✔
589
    int argc,
590
    const char* const* argv
591
)
592
{
593
  ParsedField field = get_parameter_field(argc, argv, "--exclude-name");
3✔
594
  auto* name_filter = new Filter(field.value);
3✔
595
  name_filter->invert_matching();
3✔
596
  name_filters_ = name_filter->add(name_filters_);
3✔
597
  return field.extra;
3✔
598
}
3✔
599

600
int CommandLineArguments::add_exclude_strict_name_filter(
2✔
601
    int argc,
602
    const char* const* argv
603
)
604
{
605
  ParsedField field = get_parameter_field(argc, argv, "--exclude-exact-name");
2✔
606
  auto* name_filter = new Filter(field.value);
2✔
607
  name_filter->invert_matching();
2✔
608
  name_filter->strict_matching();
2✔
609
  name_filters_ = name_filter->add(name_filters_);
2✔
610
  return field.extra;
2✔
611
}
2✔
612

613
int CommandLineArguments::add_test_to_run_based_on_verbose_output(
3✔
614
    int argc,
615
    const char* const* argv,
616
    const char* parameter_name
617
)
618
{
619
  ParsedField field = get_parameter_field(argc, argv, parameter_name);
3✔
620
  String testname = sub_string_from_till(field.value, ',', ')');
3✔
621
  testname = testname.substr(2);
3✔
622
  auto* namefilter = new Filter(testname);
3✔
623
  auto* groupfilter =
624
      new Filter(sub_string_from_till(field.value, field.value[0], ','));
3✔
625
  namefilter->strict_matching();
3✔
626
  groupfilter->strict_matching();
3✔
627
  group_filters_ = groupfilter->add(group_filters_);
3✔
628
  name_filters_ = namefilter->add(name_filters_);
3✔
629
  return field.extra;
3✔
630
}
3✔
631

632
} // namespace test
633
} // namespace tiny
634
} // 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