22 inline bool validateDoctestArguments(
int argc,
char **argv) {
25 static const std::vector<std::string> valid_prefixes = {
27 "-?",
"--help",
"-h",
"-v",
"--version",
"-c",
"--count",
28 "-ltc",
"--list-test-cases",
"-lts",
"--list-test-suites",
"-lr",
"--list-reporters",
30 "-tc",
"--test-case",
"-tce",
"--test-case-exclude",
31 "-sf",
"--source-file",
"-sfe",
"--source-file-exclude",
32 "-ts",
"--test-suite",
"-tse",
"--test-suite-exclude",
33 "-sc",
"--subcase",
"-sce",
"--subcase-exclude",
36 "-o",
"--out",
"-ob",
"--order-by",
"-rs",
"--rand-seed",
37 "-f",
"--first",
"-l",
"--last",
"-aa",
"--abort-after",
38 "-scfl",
"--subcase-filter-levels",
40 "-s",
"--success",
"-cs",
"--case-sensitive",
"-e",
"--exit",
41 "-d",
"--duration",
"-m",
"--minimal",
"-q",
"--quiet",
42 "-nt",
"--no-throw",
"-ne",
"--no-exitcode",
"-nr",
"--no-run",
43 "-ni",
"--no-intro",
"-nv",
"--no-version",
"-nc",
"--no-colors",
44 "-fc",
"--force-colors",
"-nb",
"--no-breaks",
"-ns",
"--no-skip",
45 "-gfl",
"--gnu-file-line",
"-npf",
"--no-path-filenames",
46 "-sfp",
"--strip-file-prefixes",
"-nln",
"--no-line-numbers",
47 "-ndo",
"--no-debug-output",
"-nss",
"--no-skipped-summary",
48 "-ntio",
"--no-time-in-output",
50 "-dt-?",
"--dt-help",
"-dt-h",
"-dt-v",
"--dt-version",
"-dt-c",
"--dt-count",
51 "-dt-ltc",
"--dt-list-test-cases",
"-dt-lts",
"--dt-list-test-suites",
"-dt-lr",
"--dt-list-reporters",
53 "-dt-tc",
"--dt-test-case",
"-dt-tce",
"--dt-test-case-exclude",
54 "-dt-sf",
"--dt-source-file",
"-dt-sfe",
"--dt-source-file-exclude",
55 "-dt-ts",
"--dt-test-suite",
"-dt-tse",
"--dt-test-suite-exclude",
56 "-dt-sc",
"--dt-subcase",
"-dt-sce",
"--dt-subcase-exclude",
57 "-dt-r",
"--dt-reporters",
59 "-dt-o",
"--dt-out",
"-dt-ob",
"--dt-order-by",
"-dt-rs",
"--dt-rand-seed",
60 "-dt-f",
"--dt-first",
"-dt-l",
"--dt-last",
"-dt-aa",
"--dt-abort-after",
61 "-dt-scfl",
"--dt-subcase-filter-levels",
63 "-dt-s",
"--dt-success",
"-dt-cs",
"--dt-case-sensitive",
"-dt-e",
"--dt-exit",
64 "-dt-d",
"--dt-duration",
"-dt-m",
"--dt-minimal",
"-dt-q",
"--dt-quiet",
65 "-dt-nt",
"--dt-no-throw",
"-dt-ne",
"--dt-no-exitcode",
"-dt-nr",
"--dt-no-run",
66 "-dt-ni",
"--dt-no-intro",
"-dt-nv",
"--dt-no-version",
"-dt-nc",
"--dt-no-colors",
67 "-dt-fc",
"--dt-force-colors",
"-dt-nb",
"--dt-no-breaks",
"-dt-ns",
"--dt-no-skip",
68 "-dt-gfl",
"--dt-gnu-file-line",
"-dt-npf",
"--dt-no-path-filenames",
69 "-dt-sfp",
"--dt-strip-file-prefixes",
"-dt-nln",
"--dt-no-line-numbers",
70 "-dt-ndo",
"--dt-no-debug-output",
"-dt-nss",
"--dt-no-skipped-summary",
71 "-dt-ntio",
"--dt-no-time-in-output"};
73 auto isValidDoctestArgument = [&](
const std::string &arg) ->
bool {
75 for (
const auto &prefix: valid_prefixes) {
76 if (arg == prefix || (arg.length() > prefix.length() && arg.substr(0, prefix.length()) == prefix && arg[prefix.length()] ==
'=')) {
84 std::vector<std::string> invalid_args;
85 for (
int i = 1; i < argc; ++i) {
86 std::string arg(argv[i]);
87 if (!isValidDoctestArgument(arg)) {
88 invalid_args.push_back(arg);
93 if (!invalid_args.empty()) {
94 std::cerr <<
"WARNING: Invalid or unrecognized test arguments detected:" << std::endl;
95 for (
const auto &invalid_arg: invalid_args) {
96 std::cerr <<
" " << invalid_arg << std::endl;
98 std::cerr << std::endl;
99 std::cerr <<
"Common valid patterns:" << std::endl;
100 std::cerr <<
" -tc=\"pattern\" : Run tests matching pattern" << std::endl;
101 std::cerr <<
" --test-case=\"pattern\" : Run tests matching pattern" << std::endl;
102 std::cerr <<
" -s : Include successful assertions in output" << std::endl;
103 std::cerr <<
" -c : Count matching tests" << std::endl;
104 std::cerr <<
" --help : Show full help" << std::endl;
105 std::cerr << std::endl;
108 for (
const auto &invalid_arg: invalid_args) {
109 if (!invalid_arg.empty() && invalid_arg[0] !=
'-') {
110 std::cerr <<
"Suggestion: Did you mean -tc=\"" << invalid_arg <<
"\" ?" << std::endl;
113 std::cerr << std::endl;
114 std::cerr <<
"Proceeding with test execution..." << std::endl;
115 std::cerr << std::endl;
133 inline bool checkTestFiltersMatchTests(
int argc,
char **argv) {
135 doctest::Context count_context;
136 count_context.applyCommandLine(argc, argv);
139 count_context.setOption(
"count",
true);
140 count_context.setOption(
"no-run",
true);
143 std::ostringstream count_output;
144 std::streambuf *orig_cout = std::cout.rdbuf();
145 std::cout.rdbuf(count_output.rdbuf());
148 int count_result = count_context.run();
151 std::cout.rdbuf(orig_cout);
154 std::string output = count_output.str();
157 if (output.find(
"unskipped test cases passing the current filters: 0") != std::string::npos || output.find(
"number of tests: 0") != std::string::npos) {
159 bool has_test_filters =
false;
160 for (
int i = 1; i < argc; ++i) {
161 std::string arg(argv[i]);
162 if (arg.find(
"-tc=") == 0 || arg.find(
"--test-case=") == 0 || arg.find(
"-ts=") == 0 || arg.find(
"--test-suite=") == 0 || arg.find(
"-sc=") == 0 || arg.find(
"--subcase=") == 0 || arg.find(
"-dt-tc=") == 0 ||
163 arg.find(
"--dt-test-case=") == 0 || arg.find(
"-dt-ts=") == 0 || arg.find(
"--dt-test-suite=") == 0 || arg.find(
"-dt-sc=") == 0 || arg.find(
"--dt-subcase=") == 0) {
164 has_test_filters =
true;
169 if (has_test_filters) {
170 std::cerr <<
"WARNING: No tests match the specified filters!" << std::endl;
171 std::cerr <<
"Your filter criteria resulted in 0 matching tests." << std::endl;
172 std::cerr <<
"Use -ltc or --list-test-cases to see available tests." << std::endl;
173 std::cerr << std::endl;
192 template<
typename ContextType = doctest::Context>
193 inline int runDoctestWithValidation(
int argc,
char **argv) {
195 validateDoctestArguments(argc, argv);
198 checkTestFiltersMatchTests(argc, argv);
202 context.applyCommandLine(argc, argv);
203 int res = context.run();
205 if (context.shouldExit()) {