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

thetic / mu.tiny / 24941618688

25 Apr 2026 09:57PM UTC coverage: 98.931% (+0.005%) from 98.926%
24941618688

Pull #91

github

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

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

2 existing lines in 2 files now uncovered.

5366 of 5424 relevant lines covered (98.93%)

3407.32 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" || argument == "--verbose=1") {
203✔
94
    verbose_ = true;
48✔
95
    return true;
48✔
96
  }
97
  if (argument == "-vv" || argument == "--verbose=2") {
155✔
98
    very_verbose_ = true;
14✔
99
    return true;
14✔
100
  }
101
  if (argument == "-c" || argument == "--color") {
141✔
102
    color_ = true;
13✔
103
    return true;
13✔
104
  }
105
  if (argument == "-b" || argument == "--reverse") {
128✔
106
    reversing_ = true;
3✔
107
    return true;
3✔
108
  }
109
  if (argument == "--list-groups") {
125✔
110
    list_test_group_names_ = true;
2✔
111
    return true;
2✔
112
  }
113
  if (argument == "--list-tests") {
123✔
114
    list_test_group_and_case_names_ = true;
2✔
115
    return true;
2✔
116
  }
117
  if (argument == "--list-ordered-locations") {
121✔
118
    list_ordered_test_locations_ = true;
1✔
119
    return true;
1✔
120
  }
121
  if (argument == "--list-group-locations") {
120✔
122
    list_test_group_locations_ = true;
2✔
123
    return true;
2✔
124
  }
125
  if (argument == "--list-locations") {
118✔
126
    list_test_locations_ = true;
2✔
127
    return true;
2✔
128
  }
129
  if (argument == "--run-skipped") {
116✔
130
    run_skipped_ = true;
2✔
131
    return true;
2✔
132
  }
133
  if (argument == "-f" || argument == "--crash-on-fail") {
114✔
134
    crash_on_fail_ = true;
2✔
135
    return true;
2✔
136
  }
137
  if (argument == "-e" || argument == "--no-rethrow") {
112✔
138
    rethrow_exceptions_ = false;
2✔
139
    return true;
2✔
140
  }
141
  return false;
110✔
142
}
143

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

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

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

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

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

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

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

327
  return help_str;
10✔
328
}
5✔
329

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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