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

thetic / mu.tiny / 24941405756

25 Apr 2026 09:45PM UTC coverage: 98.927% (+0.001%) from 98.926%
24941405756

Pull #91

github

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

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

2 existing lines in 2 files now uncovered.

5348 of 5406 relevant lines covered (98.93%)

3333.65 hits per line

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

99.32
/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(
80✔
23
    int argc,
24
    const char* const* argv,
25
    const String& parameter_name
26
)
27
{
28
  size_t parameter_length = parameter_name.size();
80✔
29
  String parameter(argv[0]);
80✔
30
  if (parameter.size() > parameter_length) {
80✔
31
    size_t offset = parameter_length;
20✔
32
    if (argv[0][offset] == '=') {
20✔
33
      offset++;
15✔
34
    }
35
    ParsedField result = { String(argv[0] + offset), 0 };
20✔
36
    return result;
20✔
37
  }
20✔
38
  if (argc > 1) {
60✔
39
    ParsedField result = { String(argv[1]), 1 };
59✔
40
    return result;
59✔
41
  }
59✔
42
  ParsedField result = { String(""), 0 };
1✔
43
  return result;
1✔
44
}
80✔
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)
127✔
67
  : ac_(argc)
127✔
68
  , av_(argv)
127✔
69

70
{
71
}
127✔
72

73
CommandLineArguments::~CommandLineArguments()
254✔
74
{
75
  while (group_filters_ != nullptr) {
317✔
76
    Filter* current = group_filters_;
63✔
77
    group_filters_ = group_filters_->get_next();
63✔
78
    delete current;
63✔
79
  }
80
  while (name_filters_ != nullptr) {
274✔
81
    Filter* current = name_filters_;
20✔
82
    name_filters_ = name_filters_->get_next();
20✔
83
    delete current;
20✔
84
  }
85
}
254✔
86

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

144
int CommandLineArguments::parse_prefix_arg(
99✔
145
    int argc,
146
    const char* const* argv,
147
    Plugin* plugin
148
)
149
{
150
  String argument(argv[0]);
99✔
151
  if (string_starts_with(argument, "-r")) {
99✔
152
    return set_repeat_count(argc, argv);
3✔
153
  }
154
  if (string_starts_with(argument, "--exact-group")) {
96✔
155
    return add_strict_group_filter(argc, argv);
48✔
156
  }
157
  if (string_starts_with(argument, "--exclude-exact-group")) {
48✔
158
    return add_exclude_strict_group_filter(argc, argv);
2✔
159
  }
160
  if (string_starts_with(argument, "--exclude-group")) {
46✔
161
    return add_exclude_group_filter(argc, argv);
2✔
162
  }
163
  if (string_starts_with(argument, "--exact-name")) {
44✔
164
    return add_strict_name_filter(argc, argv);
6✔
165
  }
166
  if (string_starts_with(argument, "--exclude-exact-name")) {
38✔
167
    return add_exclude_strict_name_filter(argc, argv);
2✔
168
  }
169
  if (string_starts_with(argument, "--exclude-name")) {
36✔
170
    return add_exclude_name_filter(argc, argv);
3✔
171
  }
172
  if (string_starts_with(argument, "--exact-test")) {
33✔
173
    return add_group_dot_name_filter(argc, argv, "--exact-test", true, false);
2✔
174
  }
175
  if (string_starts_with(argument, "--exclude-exact-test")) {
31✔
176
    return add_group_dot_name_filter(
4✔
177
        argc, argv, "--exclude-exact-test", true, true
178
    );
2✔
179
  }
180
  if (string_starts_with(argument, "--exclude-test")) {
29✔
181
    return add_group_dot_name_filter(argc, argv, "--exclude-test", false, true);
2✔
182
  }
183
  if (string_starts_with(argument, "-g")) {
27✔
184
    return add_group_filter(argc, argv);
4✔
185
  }
186
  if (string_starts_with(argument, "-t")) {
23✔
187
    return add_group_dot_name_filter(argc, argv, "-t", false, false);
2✔
188
  }
189
  if (string_starts_with(argument, "-n")) {
21✔
190
    return add_name_filter(argc, argv);
2✔
191
  }
192
  if (string_starts_with(argument, "-s")) {
19✔
193
    return set_shuffle(argc, argv);
9✔
194
  }
195
  if (string_starts_with(argument, "TEST(")) {
10✔
196
    return add_test_to_run_based_on_verbose_output(argc, argv, "TEST(");
2✔
197
  }
198
  if (string_starts_with(argument, "SKIPPED_TEST(")) {
8✔
199
    return add_test_to_run_based_on_verbose_output(argc, argv, "SKIPPED_TEST(");
1✔
200
  }
201
  if (string_starts_with(argument, "-p")) {
7✔
202
    return plugin->parse_all_arguments(argc, argv) ? 0 : -1;
4✔
203
  }
204
  return -1;
3✔
205
}
99✔
206

207
int CommandLineArguments::parse_argument(
188✔
208
    int argc,
209
    const char* const* argv,
210
    Plugin* plugin
211
)
212
{
213
  if (parse_simple_flag(String(argv[0]))) {
188✔
214
    return 0;
89✔
215
  }
216
  return parse_prefix_arg(argc, argv, plugin);
99✔
217
}
218

219
bool CommandLineArguments::parse(Plugin* plugin)
127✔
220
{
221
  for (int i = 1; i < ac_; i++) {
306✔
222
    int extra = parse_argument(ac_ - i, av_ + i, plugin);
188✔
223
    if (extra < 0) {
188✔
224
      return false;
9✔
225
    }
226
    i += extra;
179✔
227
  }
228
  return true;
118✔
229
}
230

231
String CommandLineArguments::help()
5✔
232
{
233
  String help_str =
234
      "mutiny v" MUTINY_VERSION_STRING "\n\n"
235
      "Options that do not run tests but query:\n"
236
      "  -h                         - this wonderful help screen. Joy!\n"
237
      "  --list-groups              - print a list of group names, separated "
238
      "by spaces\n"
239
      "  --list-tests               - print a list of test names in the form "
240
      "of group.name, separated by spaces\n"
241
      "  --list-locations           - print a list of test names in the form "
242
      "of group.name.test_file_path.line\n"
243
      "  --list-ordered-locations   - print a list of ordered test names in "
244
      "the form of group.name.test_file_path.line\n"
245
      "  --list-group-locations     - print a list of group locations in the "
246
      "form of group.file_path.line\n"
247
      "\n"
248
      "Options that change the output format:\n"
249
      "  -c                         - colorize output, print green if OK, or "
250
      "red if failed\n"
251
      "  -v                         - verbose, print each test name as it "
252
      "runs\n"
253
      "  -vv                        - very verbose, print internal information "
254
      "during test run\n";
5✔
255

256
  Plugin* plugin = Registry::get_current_registry()->get_first_plugin();
5✔
257
  String plugin_help = plugin->get_all_help();
5✔
258

259
  if (!plugin_help.empty()) {
5✔
260
    help_str += "\nOptions that are provided by plugins:\n";
5✔
261
    help_str += plugin_help;
5✔
262
  }
263

264
  help_str +=
265
      "\n"
266
      "Options that control which tests are run:\n"
267
      "  -g <group>                    - only run tests whose group contains "
268
      "<group>\n"
269
      "  -n <name>                     - only run tests whose name contains "
270
      "<name>\n"
271
      "  -t <group>.<name>             - only run tests whose group and name "
272
      "contain <group> and <name>\n"
273
      "  --exact-group <group>         - only run tests whose group exactly "
274
      "matches <group>\n"
275
      "  --exact-name <name>           - only run tests whose name exactly "
276
      "matches <name>\n"
277
      "  --exact-test <grp>.<name>     - only run tests whose group and name "
278
      "exactly match <grp> and <name>\n"
279
      "  --exclude-group <group>       - exclude tests whose group contains "
280
      "<group>\n"
281
      "  --exclude-name <name>         - exclude tests whose name contains "
282
      "<name>\n"
283
      "  --exclude-test <grp>.<name>   - exclude tests whose group and name "
284
      "contain <grp> and <name>\n"
285
      "  --exclude-exact-group <group> - exclude tests whose group exactly "
286
      "matches <group>\n"
287
      "  --exclude-exact-name <name>   - exclude tests whose name exactly "
288
      "matches <name>\n"
289
      "  --exclude-exact-test <grp>.<name>\n"
290
      "                                - exclude tests whose group and name "
291
      "exactly match <grp> and <name>\n"
292
      "  \"[SKIPPED_]TEST(<group>, <name>)\"\n"
293
      "                    - only run tests whose group and name exactly "
294
      "match <group> and <name>\n"
295
      "                      (this can be used to copy-paste output from "
296
      "the -v option on the command line)\n"
297
      "\n"
298
      "Options that control how the tests are run:\n"
299
      "  -b                - run the tests backwards, reversing the normal "
300
      "way\n"
301
      "  -s [<seed>]       - shuffle tests randomly (randomization seed is "
302
      "optional, must be greater than 0)\n"
303
      "  -r[<#>]           - repeat the tests <#> times (or twice if <#> is "
304
      "not specified)\n"
305
      "  --run-skipped     - run skipped tests as if they are not skipped\n"
306
      "  -f                - Cause the tests to crash on failure (to allow "
307
      "the test to be debugged if necessary)\n"
308
      "  -e                - do not rethrow unexpected exceptions on "
309
      "failure\n";
5✔
310

311
  return help_str;
10✔
312
}
5✔
313

314
bool CommandLineArguments::need_help() const
72✔
315
{
316
  return need_help_;
72✔
317
}
318

319
bool CommandLineArguments::is_verbose() const
85✔
320
{
321
  return verbose_;
85✔
322
}
323

324
bool CommandLineArguments::is_very_verbose() const
83✔
325
{
326
  return very_verbose_;
83✔
327
}
328

329
bool CommandLineArguments::is_color() const
71✔
330
{
331
  return color_;
71✔
332
}
333

334
bool CommandLineArguments::is_listing_test_group_names() const
71✔
335
{
336
  return list_test_group_names_;
71✔
337
}
338

339
bool CommandLineArguments::is_listing_test_group_and_case_names() const
70✔
340
{
341
  return list_test_group_and_case_names_;
70✔
342
}
343

344
bool CommandLineArguments::is_listing_test_locations() const
68✔
345
{
346
  return list_test_locations_;
68✔
347
}
348

349
bool CommandLineArguments::is_listing_ordered_test_locations() const
66✔
350
{
351
  return list_ordered_test_locations_;
66✔
352
}
353

354
bool CommandLineArguments::is_listing_test_group_locations() const
66✔
355
{
356
  return list_test_group_locations_;
66✔
357
}
358

359
bool CommandLineArguments::is_run_skipped() const
71✔
360
{
361
  return run_skipped_;
71✔
362
}
363

364
unsigned int CommandLineArguments::get_repeat_count() const
74✔
365
{
366
  return repeat_;
74✔
367
}
368

369
bool CommandLineArguments::is_reversing() const
65✔
370
{
371
  return reversing_;
65✔
372
}
373

374
bool CommandLineArguments::is_crashing_on_fail() const
72✔
375
{
376
  return crash_on_fail_;
72✔
377
}
378

379
bool CommandLineArguments::is_rethrowing_exceptions() const
72✔
380
{
381
  return rethrow_exceptions_;
72✔
382
}
383

384
bool CommandLineArguments::is_shuffling() const
131✔
385
{
386
  return shuffling_;
131✔
387
}
388

389
unsigned int CommandLineArguments::get_shuffle_seed() const
8✔
390
{
391
  return shuffle_seed_;
8✔
392
}
393

394
const Filter* CommandLineArguments::get_group_filters() const
88✔
395
{
396
  return group_filters_;
88✔
397
}
398

399
const Filter* CommandLineArguments::get_name_filters() const
86✔
400
{
401
  return name_filters_;
86✔
402
}
403

404
int CommandLineArguments::set_repeat_count(int argc, const char* const* argv)
3✔
405
{
406
  repeat_ = 0;
3✔
407
  int extra = 0;
3✔
408

409
  String repeat_parameter(argv[0]);
3✔
410
  if (repeat_parameter.size() > 2) {
3✔
411
    repeat_ = static_cast<unsigned int>(strtoul(argv[0] + 2));
1✔
412
  } else if (argc > 1) {
2✔
413
    repeat_ = static_cast<unsigned int>(strtoul(argv[1]));
1✔
414
    if (repeat_ != 0) {
1✔
415
      extra = 1;
1✔
416
    }
417
  }
418

419
  if (0 == repeat_) {
3✔
420
    repeat_ = 2;
1✔
421
  }
422
  return extra;
3✔
423
}
3✔
424

425
int CommandLineArguments::set_shuffle(int argc, const char* const* argv)
9✔
426
{
427
  shuffling_ = true;
9✔
428
  shuffle_seed_ = static_cast<unsigned int>(get_time_in_millis());
9✔
429
  if (shuffle_seed_ == 0) {
9✔
UNCOV
430
    shuffle_seed_++;
×
431
  }
432

433
  int extra = 0;
9✔
434
  String shuffle_parameter = argv[0];
9✔
435
  if (shuffle_parameter.size() > 2) {
9✔
436
    shuffling_pre_seeded_ = true;
5✔
437
    shuffle_seed_ = static_cast<unsigned>(strtoul(argv[0] + 2));
5✔
438
  } else if (argc > 1) {
4✔
439
    auto parsed_parameter = static_cast<unsigned>(strtoul(argv[1]));
2✔
440
    if (parsed_parameter != 0) {
2✔
441
      shuffling_pre_seeded_ = true;
1✔
442
      shuffle_seed_ = parsed_parameter;
1✔
443
      extra = 1;
1✔
444
    }
445
  }
446
  return (shuffle_seed_ != 0) ? extra : -1;
18✔
447
}
9✔
448

449
int CommandLineArguments::add_group_filter(int argc, const char* const* argv)
4✔
450
{
451
  ParsedField field = get_parameter_field(argc, argv, "-g");
4✔
452
  auto* group_filter = new Filter(field.value);
4✔
453
  group_filters_ = group_filter->add(group_filters_);
4✔
454
  return field.extra;
4✔
455
}
4✔
456

457
int CommandLineArguments::add_group_dot_name_filter(
8✔
458
    int argc,
459
    const char* const* argv,
460
    const String& parameter_name,
461
    bool strict,
462
    bool exclude
463
)
464
{
465
  ParsedField field = get_parameter_field(argc, argv, parameter_name);
8✔
466
  StringCollection collection(field.value, '.');
8✔
467

468
  if (collection.size() != 2) {
8✔
469
    return -1;
4✔
470
  }
471

472
  auto* group_filter =
473
      new Filter(collection[0].substr(0, collection[0].size() - 1));
4✔
474
  auto* name_filter = new Filter(collection[1]);
4✔
475
  if (strict) {
4✔
476
    group_filter->strict_matching();
2✔
477
    name_filter->strict_matching();
2✔
478
  }
479
  if (exclude) {
4✔
480
    group_filter->invert_matching();
2✔
481
    name_filter->invert_matching();
2✔
482
  }
483
  group_filters_ = group_filter->add(group_filters_);
4✔
484
  name_filters_ = name_filter->add(name_filters_);
4✔
485
  return field.extra;
4✔
486
}
8✔
487

488
int CommandLineArguments::add_strict_group_filter(
48✔
489
    int argc,
490
    const char* const* argv
491
)
492
{
493
  ParsedField field = get_parameter_field(argc, argv, "--exact-group");
48✔
494
  auto* group_filter = new Filter(field.value);
48✔
495
  group_filter->strict_matching();
48✔
496
  group_filters_ = group_filter->add(group_filters_);
48✔
497
  return field.extra;
48✔
498
}
48✔
499

500
int CommandLineArguments::add_exclude_group_filter(
2✔
501
    int argc,
502
    const char* const* argv
503
)
504
{
505
  ParsedField field = get_parameter_field(argc, argv, "--exclude-group");
2✔
506
  auto* group_filter = new Filter(field.value);
2✔
507
  group_filter->invert_matching();
2✔
508
  group_filters_ = group_filter->add(group_filters_);
2✔
509
  return field.extra;
2✔
510
}
2✔
511

512
int CommandLineArguments::add_exclude_strict_group_filter(
2✔
513
    int argc,
514
    const char* const* argv
515
)
516
{
517
  ParsedField field = get_parameter_field(argc, argv, "--exclude-exact-group");
2✔
518
  auto* group_filter = new Filter(field.value);
2✔
519
  group_filter->strict_matching();
2✔
520
  group_filter->invert_matching();
2✔
521
  group_filters_ = group_filter->add(group_filters_);
2✔
522
  return field.extra;
2✔
523
}
2✔
524

525
int CommandLineArguments::add_name_filter(int argc, const char* const* argv)
2✔
526
{
527
  ParsedField field = get_parameter_field(argc, argv, "-n");
2✔
528
  auto* name_filter = new Filter(field.value);
2✔
529
  name_filters_ = name_filter->add(name_filters_);
2✔
530
  return field.extra;
2✔
531
}
2✔
532

533
int CommandLineArguments::add_strict_name_filter(
6✔
534
    int argc,
535
    const char* const* argv
536
)
537
{
538
  ParsedField field = get_parameter_field(argc, argv, "--exact-name");
6✔
539
  auto* name_filter = new Filter(field.value);
6✔
540
  name_filter->strict_matching();
6✔
541
  name_filters_ = name_filter->add(name_filters_);
6✔
542
  return field.extra;
6✔
543
}
6✔
544

545
int CommandLineArguments::add_exclude_name_filter(
3✔
546
    int argc,
547
    const char* const* argv
548
)
549
{
550
  ParsedField field = get_parameter_field(argc, argv, "--exclude-name");
3✔
551
  auto* name_filter = new Filter(field.value);
3✔
552
  name_filter->invert_matching();
3✔
553
  name_filters_ = name_filter->add(name_filters_);
3✔
554
  return field.extra;
3✔
555
}
3✔
556

557
int CommandLineArguments::add_exclude_strict_name_filter(
2✔
558
    int argc,
559
    const char* const* argv
560
)
561
{
562
  ParsedField field = get_parameter_field(argc, argv, "--exclude-exact-name");
2✔
563
  auto* name_filter = new Filter(field.value);
2✔
564
  name_filter->invert_matching();
2✔
565
  name_filter->strict_matching();
2✔
566
  name_filters_ = name_filter->add(name_filters_);
2✔
567
  return field.extra;
2✔
568
}
2✔
569

570
int CommandLineArguments::add_test_to_run_based_on_verbose_output(
3✔
571
    int argc,
572
    const char* const* argv,
573
    const char* parameter_name
574
)
575
{
576
  ParsedField field = get_parameter_field(argc, argv, parameter_name);
3✔
577
  String testname = sub_string_from_till(field.value, ',', ')');
3✔
578
  testname = testname.substr(2);
3✔
579
  auto* namefilter = new Filter(testname);
3✔
580
  auto* groupfilter =
581
      new Filter(sub_string_from_till(field.value, field.value[0], ','));
3✔
582
  namefilter->strict_matching();
3✔
583
  groupfilter->strict_matching();
3✔
584
  group_filters_ = groupfilter->add(group_filters_);
3✔
585
  name_filters_ = namefilter->add(name_filters_);
3✔
586
  return field.extra;
3✔
587
}
3✔
588

589
} // namespace test
590
} // namespace tiny
591
} // 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