Line data Source code
1 : // Copyright (c) 2006-2025 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/advgetopt
4 : // contact@m2osw.com
5 : //
6 : // This program is free software; you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation; either version 2 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License along
17 : // with this program; if not, write to the Free Software Foundation, Inc.,
18 : // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 :
20 : // advgetopt
21 : //
22 : #include <advgetopt/exception.h>
23 :
24 :
25 : // self
26 : //
27 : #include "catch_main.h"
28 :
29 :
30 : // snapdev
31 : //
32 : #include <snapdev/safe_setenv.h>
33 : #include <snapdev/string_replace_many.h>
34 :
35 :
36 : // C++
37 : //
38 : #include <fstream>
39 :
40 :
41 : // last include
42 : //
43 : #include <snapdev/poison.h>
44 :
45 :
46 :
47 :
48 :
49 1 : CATCH_TEST_CASE("options_sources", "[options][sources][valid]")
50 : {
51 1 : CATCH_START_SECTION("options_sources: system options only")
52 : {
53 1 : advgetopt::option const options[] =
54 : {
55 : advgetopt::define_option(
56 : advgetopt::Name("verbose")
57 : , advgetopt::ShortName('v')
58 : , advgetopt::Flags(advgetopt::command_flags<
59 : advgetopt::GETOPT_FLAG_REQUIRED
60 : | advgetopt::GETOPT_FLAG_DYNAMIC_CONFIGURATION>())
61 : , advgetopt::Help("make it all verbose.")
62 : ),
63 : advgetopt::end_options()
64 : };
65 :
66 1 : advgetopt::options_environment environment_options;
67 1 : environment_options.f_project_name = "unittest";
68 1 : environment_options.f_group_name = "sources";
69 1 : environment_options.f_options = options;
70 1 : environment_options.f_environment_flags = advgetopt::GETOPT_ENVIRONMENT_FLAG_SYSTEM_PARAMETERS;
71 1 : environment_options.f_help_header = "Usage: test valid options from system options only";
72 :
73 1 : char const * cargv[] =
74 : {
75 : "tests/options-parser",
76 : "--license",
77 : "--show-option-sources",
78 : "--version",
79 : nullptr
80 : };
81 1 : int const argc(sizeof(cargv) / sizeof(cargv[0]) - 1);
82 1 : char ** argv = const_cast<char **>(cargv);
83 :
84 1 : advgetopt::getopt opt(environment_options, argc, argv);
85 :
86 3 : advgetopt::option_info::pointer_t verbose(opt.get_option("verbose"));
87 1 : CATCH_REQUIRE(verbose != nullptr);
88 3 : verbose->set_value(0, "true");
89 :
90 3 : advgetopt::option_info::pointer_t copyright(opt.get_option("copyright"));
91 1 : CATCH_REQUIRE(copyright != nullptr);
92 3 : copyright->set_value(0, "1", advgetopt::string_list_t(), advgetopt::option_source_t::SOURCE_DYNAMIC);
93 1 : copyright->reset();
94 :
95 3 : advgetopt::option_info::pointer_t version(opt.get_option("version"));
96 1 : CATCH_REQUIRE(version != nullptr);
97 :
98 3 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: option \"--version\" can't be directly updated.");
99 3 : version->set_value(0, "true");
100 1 : SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
101 :
102 : // check that we got source info as expected
103 :
104 3 : advgetopt::option_info::pointer_t license(opt.get_option("license"));
105 1 : CATCH_REQUIRE(license != nullptr);
106 1 : advgetopt::string_list_t license_sources(license->trace_sources());
107 1 : CATCH_REQUIRE(license_sources.size() == 1);
108 1 : CATCH_REQUIRE(license_sources[0] == "license= [command-line]");
109 :
110 3 : advgetopt::option_info::pointer_t show(opt.get_option("show-option-sources"));
111 1 : CATCH_REQUIRE(show != nullptr);
112 1 : advgetopt::string_list_t show_option_sources(show->trace_sources());
113 1 : CATCH_REQUIRE(show_option_sources.size() == 1);
114 1 : CATCH_REQUIRE(show_option_sources[0] == "show-option-sources= [command-line]");
115 :
116 1 : advgetopt::string_list_t verbose_sources(verbose->trace_sources());
117 1 : CATCH_REQUIRE(verbose_sources.size() == 1);
118 1 : CATCH_REQUIRE(verbose_sources[0] == "verbose=true [direct]");
119 :
120 3 : advgetopt::option_info::pointer_t help(opt.get_option("help"));
121 1 : CATCH_REQUIRE(help != nullptr);
122 1 : advgetopt::string_list_t help_sources(help->trace_sources());
123 1 : CATCH_REQUIRE(help_sources.size() == 0);
124 :
125 1 : advgetopt::string_list_t copyright_sources(copyright->trace_sources());
126 1 : CATCH_REQUIRE(copyright_sources.size() == 2);
127 1 : CATCH_REQUIRE(copyright_sources[0] == "copyright=1 [dynamic]");
128 1 : CATCH_REQUIRE(copyright_sources[1] == "copyright [*undefined-source*]");
129 :
130 : // process system options now
131 1 : std::stringstream ss;
132 1 : advgetopt::flag_t const result(opt.process_system_options(ss));
133 1 : CATCH_REQUIRE(result == (advgetopt::SYSTEM_OPTION_SHOW_OPTION_SOURCES
134 : | advgetopt::SYSTEM_OPTION_VERSION
135 : | advgetopt::SYSTEM_OPTION_LICENSE));
136 3 : CATCH_REQUIRE_LONG_STRING(ss.str(),
137 : "warning: no version found.\n"
138 : "warning: no license found.\n"
139 : "Option Sources:\n"
140 : " 1. option \"build-date\" (undefined)\n"
141 : "\n"
142 : " 2. option \"compiler-version\" (undefined)\n"
143 : "\n"
144 : " 3. option \"configuration-filenames\" (undefined)\n"
145 : "\n"
146 : " 4. option \"copyright\"\n"
147 : " copyright=1 [dynamic]\n"
148 : " copyright [*undefined-source*]\n"
149 : "\n"
150 : " 5. option \"environment-variable-name\" (undefined)\n"
151 : "\n"
152 : " 6. option \"filenames-of-option-definitions\" (undefined)\n"
153 : "\n"
154 : " 7. option \"has-sanitizer\" (undefined)\n"
155 : "\n"
156 : " 8. option \"help\" (undefined)\n"
157 : "\n"
158 : " 9. option \"license\"\n"
159 : " license= [command-line]\n"
160 : "\n"
161 : " 10. option \"long-help\" (undefined)\n"
162 : "\n"
163 : " 11. option \"path-to-option-definitions\" (undefined)\n"
164 : "\n"
165 : " 12. option \"print-option\" (undefined)\n"
166 : "\n"
167 : " 13. option \"show-option-sources\"\n"
168 : " show-option-sources= [command-line]\n"
169 : "\n"
170 : " 14. option \"system-help\" (undefined)\n"
171 : "\n"
172 : " 15. option \"verbose\"\n"
173 : " verbose=true [direct]\n"
174 : "\n"
175 : " 16. option \"version\"\n"
176 : " version= [command-line]\n"
177 : "\n"
178 : );
179 1 : }
180 1 : CATCH_END_SECTION()
181 1 : }
182 :
183 :
184 2 : CATCH_TEST_CASE("options_sources_environment_variable_and_config_file", "[options][sources][valid][config][environment_variable]")
185 : {
186 2 : CATCH_START_SECTION("options_sources_environment_variable_and_config_file: load options from a config file, environment variable, and command line with --config-dir on the command line")
187 : {
188 5 : SNAP_CATCH2_NAMESPACE::init_tmp_dir("sources", "src");
189 :
190 : {
191 1 : std::ofstream config_file;
192 1 : config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
193 1 : CATCH_REQUIRE(config_file.good());
194 : config_file <<
195 : "# Auto-generated\n"
196 : "unique = perfect \n"
197 : "definition = long value here\n"
198 : "sources = just fine \t\n"
199 : "multiple = value\r\n"
200 : "good = variable \n"
201 : "organized = set\n"
202 1 : "more = data\t \n"
203 : ;
204 1 : }
205 :
206 1 : advgetopt::option const options[] =
207 : {
208 : advgetopt::define_option(
209 : advgetopt::Name("unique")
210 : , advgetopt::ShortName('u')
211 : , advgetopt::Flags(advgetopt::all_flags<advgetopt::GETOPT_FLAG_REQUIRED>())
212 : , advgetopt::Help("unique option.")
213 : , advgetopt::DefaultValue("long")
214 : ),
215 : advgetopt::define_option(
216 : advgetopt::Name("color")
217 : , advgetopt::ShortName('c')
218 : , advgetopt::Flags(advgetopt::all_flags<advgetopt::GETOPT_FLAG_REQUIRED
219 : , advgetopt::GETOPT_FLAG_MULTIPLE
220 : , advgetopt::GETOPT_FLAG_SHOW_GROUP2>())
221 : , advgetopt::Help("specify a color.")
222 : ),
223 : advgetopt::define_option(
224 : advgetopt::Name("definition")
225 : , advgetopt::ShortName('d')
226 : , advgetopt::Flags(advgetopt::all_flags<advgetopt::GETOPT_FLAG_REQUIRED
227 : , advgetopt::GETOPT_FLAG_SHOW_GROUP1>())
228 : , advgetopt::Help("complete definition.")
229 : ),
230 : advgetopt::define_option(
231 : advgetopt::Name("sources")
232 : , advgetopt::ShortName('s')
233 : , advgetopt::Flags(advgetopt::all_flags<advgetopt::GETOPT_FLAG_REQUIRED
234 : , advgetopt::GETOPT_FLAG_SHOW_GROUP2>())
235 : , advgetopt::Help("where it all came from.")
236 : ),
237 : advgetopt::define_option(
238 : advgetopt::Name("multiple")
239 : , advgetopt::ShortName('m')
240 : , advgetopt::Flags(advgetopt::all_flags<advgetopt::GETOPT_FLAG_REQUIRED
241 : , advgetopt::GETOPT_FLAG_MULTIPLE
242 : , advgetopt::GETOPT_FLAG_SHOW_GROUP1>())
243 : , advgetopt::Help("whether we have one or more.")
244 : ),
245 : advgetopt::define_option(
246 : advgetopt::Name("zap")
247 : , advgetopt::ShortName('z')
248 : , advgetopt::Flags(advgetopt::all_flags<advgetopt::GETOPT_FLAG_SHOW_GROUP2>())
249 : , advgetopt::Help("zap all of it.")
250 : ),
251 : advgetopt::define_option(
252 : advgetopt::Name("good")
253 : , advgetopt::ShortName('g')
254 : , advgetopt::Flags(advgetopt::all_flags<advgetopt::GETOPT_FLAG_REQUIRED
255 : , advgetopt::GETOPT_FLAG_SHOW_GROUP1>())
256 : , advgetopt::Help("current status.")
257 : ),
258 : advgetopt::define_option(
259 : advgetopt::Name("organized")
260 : , advgetopt::ShortName('o')
261 : , advgetopt::Flags(advgetopt::all_flags<advgetopt::GETOPT_FLAG_REQUIRED
262 : , advgetopt::GETOPT_FLAG_SHOW_GROUP2>())
263 : , advgetopt::Help("how the whole thing was organized.")
264 : ),
265 : advgetopt::define_option(
266 : advgetopt::Name("more")
267 : , advgetopt::ShortName('+')
268 : , advgetopt::Flags(advgetopt::all_flags<advgetopt::GETOPT_FLAG_REQUIRED
269 : , advgetopt::GETOPT_FLAG_SHOW_GROUP1>())
270 : , advgetopt::Help("anything more you can think of.")
271 : ),
272 : advgetopt::define_option(
273 : advgetopt::Name("verbose")
274 : , advgetopt::ShortName('v')
275 : , advgetopt::Flags(advgetopt::all_flags<advgetopt::GETOPT_FLAG_SHOW_GROUP1>())
276 : , advgetopt::Help("anything more you can think of.")
277 : ),
278 : advgetopt::end_options()
279 : };
280 :
281 1 : snapdev::safe_setenv env(
282 : "ADVGETOPT_TEST_OPTIONS"
283 5 : , "--verbose --more=instructions --color black orange purple --sources=all");
284 :
285 1 : advgetopt::options_environment environment_options;
286 1 : environment_options.f_project_name = "unittest";
287 1 : environment_options.f_group_name = "sources";
288 1 : environment_options.f_options = options;
289 1 : environment_options.f_environment_variable_name = "ADVGETOPT_TEST_OPTIONS";
290 1 : environment_options.f_configuration_filename = "src.config";
291 1 : environment_options.f_environment_flags = advgetopt::GETOPT_ENVIRONMENT_FLAG_SYSTEM_PARAMETERS;
292 1 : environment_options.f_help_header = "Usage: test source from command line, environment variable, and configuration file";
293 1 : environment_options.f_version = "1.2.3";
294 :
295 1 : std::string const config_dir("--config-dir=" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config");
296 1 : char const * cargv[] =
297 : {
298 : "tests/options-parser",
299 : "--version",
300 : "--show-option-sources",
301 : "--more=magical",
302 : "--organized",
303 : "logically",
304 : "--zap",
305 1 : config_dir.c_str(),
306 : nullptr
307 1 : };
308 1 : int const argc(sizeof(cargv) / sizeof(cargv[0]) - 1);
309 1 : char ** argv = const_cast<char **>(cargv);
310 :
311 1 : advgetopt::getopt opt(environment_options, argc, argv);
312 :
313 : // verify command line options
314 :
315 3 : advgetopt::option_info::pointer_t version(opt.get_option("version"));
316 1 : CATCH_REQUIRE(version != nullptr);
317 3 : CATCH_REQUIRE(opt.is_defined("version"));
318 1 : advgetopt::string_list_t version_sources(version->trace_sources());
319 1 : CATCH_REQUIRE(version_sources.size() == 1);
320 1 : CATCH_REQUIRE(version_sources[0] == "version= [command-line]");
321 :
322 3 : advgetopt::option_info::pointer_t show(opt.get_option("show-option-sources"));
323 1 : CATCH_REQUIRE(show != nullptr);
324 3 : CATCH_REQUIRE(opt.is_defined("show-option-sources"));
325 1 : advgetopt::string_list_t show_option_sources(show->trace_sources());
326 1 : CATCH_REQUIRE(show_option_sources.size() == 1);
327 1 : CATCH_REQUIRE(show_option_sources[0] == "show-option-sources= [command-line]");
328 :
329 3 : advgetopt::option_info::pointer_t more(opt.get_option("more"));
330 1 : CATCH_REQUIRE(more != nullptr);
331 3 : CATCH_REQUIRE(opt.is_defined("more"));
332 1 : advgetopt::string_list_t more_sources(more->trace_sources());
333 1 : CATCH_REQUIRE(more_sources.size() == 3);
334 1 : std::string expected_config("more=data [configuration=\"" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config/src.config\"]");
335 1 : CATCH_REQUIRE(more_sources[0] == expected_config);
336 1 : CATCH_REQUIRE(more_sources[1] == "more=instructions [environment-variable]");
337 1 : CATCH_REQUIRE(more_sources[2] == "more=magical [command-line]");
338 :
339 3 : advgetopt::option_info::pointer_t organized(opt.get_option("organized"));
340 1 : CATCH_REQUIRE(organized != nullptr);
341 3 : CATCH_REQUIRE(opt.is_defined("organized"));
342 1 : advgetopt::string_list_t organized_sources(organized->trace_sources());
343 1 : CATCH_REQUIRE(organized_sources.size() == 2);
344 1 : expected_config = "organized=set [configuration=\"" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config/src.config\"]";
345 1 : CATCH_REQUIRE(organized_sources[0] == expected_config);
346 1 : CATCH_REQUIRE(organized_sources[1] == "organized=logically [command-line]");
347 :
348 3 : advgetopt::option_info::pointer_t zap(opt.get_option("zap"));
349 1 : CATCH_REQUIRE(zap != nullptr);
350 3 : CATCH_REQUIRE(opt.is_defined("zap"));
351 1 : advgetopt::string_list_t zap_sources(zap->trace_sources());
352 1 : CATCH_REQUIRE(zap_sources.size() == 1);
353 1 : CATCH_REQUIRE(zap_sources[0] == "zap= [command-line]");
354 :
355 3 : advgetopt::option_info::pointer_t config_dir_opt(opt.get_option("config-dir"));
356 1 : CATCH_REQUIRE(config_dir_opt != nullptr);
357 3 : CATCH_REQUIRE(opt.is_defined("config_dir"));
358 1 : advgetopt::string_list_t config_dir_sources(config_dir_opt->trace_sources());
359 1 : CATCH_REQUIRE(config_dir_sources.size() == 1);
360 1 : expected_config = "config-dir[0]=" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config [command-line]";
361 1 : CATCH_REQUIRE(config_dir_sources[0] == expected_config);
362 :
363 : // verify environment variable options which are not also on the command line
364 :
365 3 : advgetopt::option_info::pointer_t verbose(opt.get_option("verbose"));
366 1 : CATCH_REQUIRE(verbose != nullptr);
367 3 : CATCH_REQUIRE(opt.is_defined("verbose"));
368 1 : advgetopt::string_list_t verbose_sources(verbose->trace_sources());
369 1 : CATCH_REQUIRE(verbose_sources.size() == 1);
370 1 : CATCH_REQUIRE(verbose_sources[0] == "verbose= [environment-variable]");
371 :
372 3 : advgetopt::option_info::pointer_t color(opt.get_option("color"));
373 1 : CATCH_REQUIRE(color != nullptr);
374 3 : CATCH_REQUIRE(opt.is_defined("color"));
375 1 : advgetopt::string_list_t color_sources(color->trace_sources());
376 1 : CATCH_REQUIRE(color_sources.size() == 3);
377 1 : CATCH_REQUIRE(color_sources[0] == "color[0]=black [environment-variable]");
378 1 : CATCH_REQUIRE(color_sources[1] == "color[1]=orange [environment-variable]");
379 1 : CATCH_REQUIRE(color_sources[2] == "color[2]=purple [environment-variable]");
380 :
381 3 : advgetopt::option_info::pointer_t sources(opt.get_option("sources"));
382 1 : CATCH_REQUIRE(sources != nullptr);
383 3 : CATCH_REQUIRE(opt.is_defined("sources"));
384 1 : advgetopt::string_list_t sources_sources(sources->trace_sources());
385 1 : CATCH_REQUIRE(sources_sources.size() == 2);
386 1 : expected_config = "sources=just fine [configuration=\"" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config/src.config\"]";
387 1 : CATCH_REQUIRE(sources_sources[0] == expected_config);
388 1 : CATCH_REQUIRE(sources_sources[1] == "sources=all [environment-variable]");
389 :
390 : // verify configuration file parameters that appear no where else
391 :
392 3 : advgetopt::option_info::pointer_t unique(opt.get_option("unique"));
393 1 : CATCH_REQUIRE(unique != nullptr);
394 3 : CATCH_REQUIRE(opt.is_defined("unique"));
395 1 : advgetopt::string_list_t unique_sources(unique->trace_sources());
396 1 : CATCH_REQUIRE(unique_sources.size() == 1);
397 1 : expected_config = "unique=perfect [configuration=\"" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config/src.config\"]";
398 1 : CATCH_REQUIRE(unique_sources[0] == expected_config);
399 :
400 3 : advgetopt::option_info::pointer_t definition(opt.get_option("definition"));
401 1 : CATCH_REQUIRE(definition != nullptr);
402 3 : CATCH_REQUIRE(opt.is_defined("definition"));
403 1 : advgetopt::string_list_t definition_sources(definition->trace_sources());
404 1 : CATCH_REQUIRE(definition_sources.size() == 1);
405 1 : expected_config = "definition=long value here [configuration=\"" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config/src.config\"]";
406 1 : CATCH_REQUIRE(definition_sources[0] == expected_config);
407 :
408 3 : advgetopt::option_info::pointer_t multiple(opt.get_option("multiple"));
409 1 : CATCH_REQUIRE(multiple != nullptr);
410 3 : CATCH_REQUIRE(opt.is_defined("multiple"));
411 1 : advgetopt::string_list_t multiple_sources(multiple->trace_sources());
412 1 : CATCH_REQUIRE(multiple_sources.size() == 1);
413 1 : expected_config = "multiple[0]=value [configuration=\"" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config/src.config\"]";
414 1 : CATCH_REQUIRE(multiple_sources[0] == expected_config);
415 :
416 3 : advgetopt::option_info::pointer_t good(opt.get_option("good"));
417 1 : CATCH_REQUIRE(good != nullptr);
418 3 : CATCH_REQUIRE(opt.is_defined("good"));
419 1 : advgetopt::string_list_t good_sources(good->trace_sources());
420 1 : CATCH_REQUIRE(good_sources.size() == 1);
421 1 : expected_config = "good=variable [configuration=\"" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config/src.config\"]";
422 1 : CATCH_REQUIRE(good_sources[0] == expected_config);
423 :
424 : // process system options now
425 1 : std::stringstream ss;
426 1 : advgetopt::flag_t const result(opt.process_system_options(ss));
427 1 : CATCH_REQUIRE(result == (advgetopt::SYSTEM_OPTION_SHOW_OPTION_SOURCES
428 : | advgetopt::SYSTEM_OPTION_VERSION
429 : | advgetopt::SYSTEM_OPTION_CONFIG_DIR));
430 1 : CATCH_REQUIRE_LONG_STRING(ss.str(),
431 : "1.2.3\n"
432 : "Option Sources:\n"
433 : " 1. option \"build-date\" (undefined)\n"
434 : "\n"
435 : " 2. option \"color\"\n"
436 : " color[0]=black [environment-variable]\n"
437 : " color[1]=orange [environment-variable]\n"
438 : " color[2]=purple [environment-variable]\n"
439 : "\n"
440 : " 3. option \"compiler-version\" (undefined)\n"
441 : "\n"
442 : " 4. option \"config-dir\"\n"
443 : " config-dir[0]=" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config [command-line]\n"
444 : "\n"
445 : " 5. option \"configuration-filenames\" (undefined)\n"
446 : "\n"
447 : " 6. option \"copyright\" (undefined)\n"
448 : "\n"
449 : " 7. option \"definition\"\n"
450 : " definition=long value here [configuration=\"" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config/src.config\"]\n"
451 : "\n"
452 : " 8. option \"environment-variable-name\" (undefined)\n"
453 : "\n"
454 : " 9. option \"filenames-of-option-definitions\" (undefined)\n"
455 : "\n"
456 : " 10. option \"good\"\n"
457 : " good=variable [configuration=\"" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config/src.config\"]\n"
458 : "\n"
459 : " 11. option \"has-sanitizer\" (undefined)\n"
460 : "\n"
461 : " 12. option \"help\" (undefined)\n"
462 : "\n"
463 : " 13. option \"license\" (undefined)\n"
464 : "\n"
465 : " 14. option \"long-help\" (undefined)\n"
466 : "\n"
467 : " 15. option \"more\"\n"
468 : " more=data [configuration=\"" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config/src.config\"]\n"
469 : " more=instructions [environment-variable]\n"
470 : " more=magical [command-line]\n"
471 : "\n"
472 : " 16. option \"multiple\"\n"
473 : " multiple[0]=value [configuration=\"" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config/src.config\"]\n"
474 : "\n"
475 : " 17. option \"organized\"\n"
476 : " organized=set [configuration=\"" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config/src.config\"]\n"
477 : " organized=logically [command-line]\n"
478 : "\n"
479 : " 18. option \"path-to-option-definitions\" (undefined)\n"
480 : "\n"
481 : " 19. option \"print-option\" (undefined)\n"
482 : "\n"
483 : " 20. option \"show-option-sources\"\n"
484 : " show-option-sources= [command-line]\n"
485 : "\n"
486 : " 21. option \"sources\"\n"
487 : " sources=just fine [configuration=\"" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config/src.config\"]\n"
488 : " sources=all [environment-variable]\n"
489 : "\n"
490 : " 22. option \"system-help\" (undefined)\n"
491 : "\n"
492 : " 23. option \"unique\"\n"
493 : " unique=perfect [configuration=\"" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config/src.config\"]\n"
494 : "\n"
495 : " 24. option \"verbose\"\n"
496 : " verbose= [environment-variable]\n"
497 : "\n"
498 : " 25. option \"version\"\n"
499 : " version= [command-line]\n"
500 : "\n"
501 : " 26. option \"zap\"\n"
502 : " zap= [command-line]\n"
503 : "\n"
504 : );
505 1 : }
506 2 : CATCH_END_SECTION()
507 :
508 2 : CATCH_START_SECTION("options_sources_environment_variable_and_config_file: load options from a config file, environment variable, and command line with --config-dir in the environment variable")
509 : {
510 5 : SNAP_CATCH2_NAMESPACE::init_tmp_dir("sources", "hidden");
511 :
512 : {
513 1 : std::ofstream config_file;
514 1 : config_file.open(SNAP_CATCH2_NAMESPACE::g_config_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
515 1 : CATCH_REQUIRE(config_file.good());
516 : config_file <<
517 : "# Auto-generated\n"
518 : "unique = perfect \n"
519 : "definition = long value here\n"
520 : "sources = just fine \t\n"
521 : "multiple = the origin of life\r\n"
522 : "good = variable \n"
523 : "organized = set\n"
524 1 : "more = data\t \n"
525 : ;
526 1 : }
527 :
528 3 : std::string const sub_config_file(snapdev::string_replace_many(
529 : SNAP_CATCH2_NAMESPACE::g_config_project_filename
530 5 : , {{"50", "65"}}));
531 : {
532 1 : std::ofstream config_file;
533 1 : config_file.open(sub_config_file, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
534 1 : CATCH_REQUIRE(config_file.good());
535 : config_file <<
536 : "definition=\"this is a better definition\"\n"
537 : "#sources=overwrite\n"
538 : "multiple=should we not see this one instead?\n"
539 : "good=enhanced\n"
540 1 : "# Auto-generated\n"
541 : ;
542 1 : }
543 :
544 1 : advgetopt::option const options[] =
545 : {
546 : advgetopt::define_option(
547 : advgetopt::Name("unique")
548 : , advgetopt::ShortName('u')
549 : , advgetopt::Flags(advgetopt::all_flags<advgetopt::GETOPT_FLAG_REQUIRED>())
550 : , advgetopt::Help("unique option.")
551 : , advgetopt::DefaultValue("long")
552 : ),
553 : advgetopt::define_option(
554 : advgetopt::Name("color")
555 : , advgetopt::ShortName('c')
556 : , advgetopt::Flags(advgetopt::all_flags<advgetopt::GETOPT_FLAG_REQUIRED
557 : , advgetopt::GETOPT_FLAG_MULTIPLE
558 : , advgetopt::GETOPT_FLAG_SHOW_GROUP2>())
559 : , advgetopt::Help("specify a color.")
560 : ),
561 : advgetopt::define_option(
562 : advgetopt::Name("definition")
563 : , advgetopt::ShortName('d')
564 : , advgetopt::Flags(advgetopt::all_flags<advgetopt::GETOPT_FLAG_REQUIRED
565 : , advgetopt::GETOPT_FLAG_SHOW_GROUP1>())
566 : , advgetopt::Help("complete definition.")
567 : ),
568 : advgetopt::define_option(
569 : advgetopt::Name("sources")
570 : , advgetopt::ShortName('s')
571 : , advgetopt::Flags(advgetopt::all_flags<advgetopt::GETOPT_FLAG_REQUIRED
572 : , advgetopt::GETOPT_FLAG_SHOW_GROUP2>())
573 : , advgetopt::Help("where it all came from.")
574 : ),
575 : advgetopt::define_option(
576 : advgetopt::Name("multiple")
577 : , advgetopt::ShortName('m')
578 : , advgetopt::Flags(advgetopt::all_flags<advgetopt::GETOPT_FLAG_REQUIRED
579 : , advgetopt::GETOPT_FLAG_MULTIPLE
580 : , advgetopt::GETOPT_FLAG_SHOW_GROUP1>())
581 : , advgetopt::Help("whether we have one or more.")
582 : ),
583 : advgetopt::define_option(
584 : advgetopt::Name("zap")
585 : , advgetopt::ShortName('z')
586 : , advgetopt::Flags(advgetopt::all_flags<advgetopt::GETOPT_FLAG_SHOW_GROUP2>())
587 : , advgetopt::Help("zap all of it.")
588 : ),
589 : advgetopt::define_option(
590 : advgetopt::Name("good")
591 : , advgetopt::ShortName('g')
592 : , advgetopt::Flags(advgetopt::all_flags<advgetopt::GETOPT_FLAG_REQUIRED
593 : , advgetopt::GETOPT_FLAG_SHOW_GROUP1>())
594 : , advgetopt::Help("current status.")
595 : ),
596 : advgetopt::define_option(
597 : advgetopt::Name("organized")
598 : , advgetopt::ShortName('o')
599 : , advgetopt::Flags(advgetopt::all_flags<advgetopt::GETOPT_FLAG_REQUIRED
600 : , advgetopt::GETOPT_FLAG_SHOW_GROUP2>())
601 : , advgetopt::Help("how the whole thing was organized.")
602 : ),
603 : advgetopt::define_option(
604 : advgetopt::Name("more")
605 : , advgetopt::ShortName('+')
606 : , advgetopt::Flags(advgetopt::all_flags<advgetopt::GETOPT_FLAG_REQUIRED
607 : , advgetopt::GETOPT_FLAG_SHOW_GROUP1>())
608 : , advgetopt::Help("anything more you can think of.")
609 : ),
610 : advgetopt::define_option(
611 : advgetopt::Name("verbose")
612 : , advgetopt::ShortName('v')
613 : , advgetopt::Flags(advgetopt::all_flags<advgetopt::GETOPT_FLAG_SHOW_GROUP1>())
614 : , advgetopt::Help("anything more you can think of.")
615 : ),
616 : advgetopt::end_options()
617 : };
618 :
619 1 : std::string const config_dir("--config-dir=\"" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config\"");
620 1 : snapdev::safe_setenv env(
621 : "ADVGETOPT_TEST_OPTIONS"
622 : , "--verbose --more=instructions "
623 2 : + config_dir
624 5 : + " --color black orange purple --sources=all --multiple here too");
625 :
626 1 : advgetopt::options_environment environment_options;
627 1 : environment_options.f_project_name = "unittest";
628 1 : environment_options.f_group_name = "sources";
629 1 : environment_options.f_options = options;
630 1 : environment_options.f_environment_variable_name = "ADVGETOPT_TEST_OPTIONS";
631 1 : environment_options.f_configuration_filename = "hidden.config";
632 1 : environment_options.f_environment_flags = advgetopt::GETOPT_ENVIRONMENT_FLAG_SYSTEM_PARAMETERS;
633 1 : environment_options.f_help_header = "Usage: test source from command line, environment variable, and configuration file";
634 1 : environment_options.f_license = "GPL 2 or GPL 3";
635 :
636 1 : char const * cargv[] =
637 : {
638 : "tests/options-parser",
639 : "--license",
640 : "--show-option-sources",
641 : "--more=magical",
642 : "--multiple=first",
643 : "--organized",
644 : "logically",
645 : "--zap",
646 : "--multiple=last",
647 : "--copyright",
648 : nullptr
649 : };
650 1 : int const argc(sizeof(cargv) / sizeof(cargv[0]) - 1);
651 1 : char ** argv = const_cast<char **>(cargv);
652 :
653 1 : advgetopt::getopt opt(environment_options, argc, argv);
654 :
655 : // verify command line options
656 :
657 3 : advgetopt::option_info::pointer_t license(opt.get_option("license"));
658 1 : CATCH_REQUIRE(license != nullptr);
659 3 : CATCH_REQUIRE(opt.is_defined("license"));
660 1 : advgetopt::string_list_t license_sources(license->trace_sources());
661 1 : CATCH_REQUIRE(license_sources.size() == 1);
662 1 : CATCH_REQUIRE(license_sources[0] == "license= [command-line]");
663 :
664 3 : advgetopt::option_info::pointer_t show(opt.get_option("show-option-sources"));
665 1 : CATCH_REQUIRE(show != nullptr);
666 3 : CATCH_REQUIRE(opt.is_defined("show-option-sources"));
667 1 : advgetopt::string_list_t show_option_sources(show->trace_sources());
668 1 : CATCH_REQUIRE(show_option_sources.size() == 1);
669 1 : CATCH_REQUIRE(show_option_sources[0] == "show-option-sources= [command-line]");
670 :
671 3 : advgetopt::option_info::pointer_t more(opt.get_option("more"));
672 1 : CATCH_REQUIRE(more != nullptr);
673 3 : CATCH_REQUIRE(opt.is_defined("more"));
674 1 : advgetopt::string_list_t more_sources(more->trace_sources());
675 1 : CATCH_REQUIRE(more_sources.size() == 3);
676 1 : std::string expected_config("more=data [configuration=\"" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config/hidden.config\"]");
677 1 : CATCH_REQUIRE(more_sources[0] == expected_config);
678 1 : CATCH_REQUIRE(more_sources[1] == "more=instructions [environment-variable]");
679 1 : CATCH_REQUIRE(more_sources[2] == "more=magical [command-line]");
680 :
681 3 : advgetopt::option_info::pointer_t organized(opt.get_option("organized"));
682 1 : CATCH_REQUIRE(organized != nullptr);
683 3 : CATCH_REQUIRE(opt.is_defined("organized"));
684 1 : advgetopt::string_list_t organized_sources(organized->trace_sources());
685 1 : CATCH_REQUIRE(organized_sources.size() == 2);
686 1 : expected_config = "organized=set [configuration=\"" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config/hidden.config\"]";
687 1 : CATCH_REQUIRE(organized_sources[0] == expected_config);
688 1 : CATCH_REQUIRE(organized_sources[1] == "organized=logically [command-line]");
689 :
690 3 : advgetopt::option_info::pointer_t multiple(opt.get_option("multiple"));
691 1 : CATCH_REQUIRE(multiple != nullptr);
692 3 : CATCH_REQUIRE(opt.is_defined("multiple"));
693 1 : advgetopt::string_list_t multiple_sources(multiple->trace_sources());
694 1 : CATCH_REQUIRE(multiple_sources.size() == 6);
695 1 : expected_config = "multiple[0]=the origin of life [configuration=\"" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config/hidden.config\"]";
696 1 : CATCH_REQUIRE(multiple_sources[0] == expected_config);
697 1 : expected_config = "multiple[0]=should we not see this one instead? [configuration=\"" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config/sources.d/65-hidden.config\"]"; // TODO: this should not be [0]?
698 1 : CATCH_REQUIRE(multiple_sources[1] == expected_config);
699 1 : expected_config = "multiple[1]=here [environment-variable]"; // TODO: this should not be [1]?
700 1 : CATCH_REQUIRE(multiple_sources[2] == expected_config);
701 1 : expected_config = "multiple[2]=too [environment-variable]"; // TODO: this should not be [2]?
702 1 : CATCH_REQUIRE(multiple_sources[3] == expected_config);
703 1 : expected_config = "multiple[0]=first [command-line]"; // TODO: this should not be [0]?
704 1 : CATCH_REQUIRE(multiple_sources[4] == expected_config);
705 1 : expected_config = "multiple[0]=last [command-line]"; // TODO: this should not be [0]?
706 1 : CATCH_REQUIRE(multiple_sources[5] == expected_config);
707 :
708 3 : advgetopt::option_info::pointer_t zap(opt.get_option("zap"));
709 1 : CATCH_REQUIRE(zap != nullptr);
710 3 : CATCH_REQUIRE(opt.is_defined("zap"));
711 1 : advgetopt::string_list_t zap_sources(zap->trace_sources());
712 1 : CATCH_REQUIRE(zap_sources.size() == 1);
713 1 : CATCH_REQUIRE(zap_sources[0] == "zap= [command-line]");
714 :
715 : // verify environment variable options which are not also on the command line
716 :
717 3 : advgetopt::option_info::pointer_t verbose(opt.get_option("verbose"));
718 1 : CATCH_REQUIRE(verbose != nullptr);
719 3 : CATCH_REQUIRE(opt.is_defined("verbose"));
720 1 : advgetopt::string_list_t verbose_sources(verbose->trace_sources());
721 1 : CATCH_REQUIRE(verbose_sources.size() == 1);
722 1 : CATCH_REQUIRE(verbose_sources[0] == "verbose= [environment-variable]");
723 :
724 3 : advgetopt::option_info::pointer_t color(opt.get_option("color"));
725 1 : CATCH_REQUIRE(color != nullptr);
726 3 : CATCH_REQUIRE(opt.is_defined("color"));
727 1 : advgetopt::string_list_t color_sources(color->trace_sources());
728 1 : CATCH_REQUIRE(color_sources.size() == 3);
729 1 : CATCH_REQUIRE(color_sources[0] == "color[0]=black [environment-variable]");
730 1 : CATCH_REQUIRE(color_sources[1] == "color[1]=orange [environment-variable]");
731 1 : CATCH_REQUIRE(color_sources[2] == "color[2]=purple [environment-variable]");
732 :
733 3 : advgetopt::option_info::pointer_t sources(opt.get_option("sources"));
734 1 : CATCH_REQUIRE(sources != nullptr);
735 3 : CATCH_REQUIRE(opt.is_defined("sources"));
736 1 : advgetopt::string_list_t sources_sources(sources->trace_sources());
737 1 : CATCH_REQUIRE(sources_sources.size() == 2);
738 1 : expected_config = "sources=just fine [configuration=\"" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config/hidden.config\"]";
739 1 : CATCH_REQUIRE(sources_sources[0] == expected_config);
740 1 : CATCH_REQUIRE(sources_sources[1] == "sources=all [environment-variable]");
741 :
742 3 : advgetopt::option_info::pointer_t config_dir_opt(opt.get_option("config-dir"));
743 1 : CATCH_REQUIRE(config_dir_opt != nullptr);
744 3 : CATCH_REQUIRE(opt.is_defined("config_dir"));
745 1 : advgetopt::string_list_t config_dir_sources(config_dir_opt->trace_sources());
746 1 : CATCH_REQUIRE(config_dir_sources.size() == 1);
747 1 : expected_config = "config-dir[0]=" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config [environment-variable]";
748 1 : CATCH_REQUIRE(config_dir_sources[0] == expected_config);
749 :
750 : // verify configuration file parameters that appear no where else
751 :
752 3 : advgetopt::option_info::pointer_t unique(opt.get_option("unique"));
753 1 : CATCH_REQUIRE(unique != nullptr);
754 3 : CATCH_REQUIRE(opt.is_defined("unique"));
755 1 : advgetopt::string_list_t unique_sources(unique->trace_sources());
756 1 : CATCH_REQUIRE(unique_sources.size() == 1);
757 1 : expected_config = "unique=perfect [configuration=\"" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config/hidden.config\"]";
758 1 : CATCH_REQUIRE(unique_sources[0] == expected_config);
759 :
760 3 : advgetopt::option_info::pointer_t definition(opt.get_option("definition"));
761 1 : CATCH_REQUIRE(definition != nullptr);
762 3 : CATCH_REQUIRE(opt.is_defined("definition"));
763 1 : advgetopt::string_list_t definition_sources(definition->trace_sources());
764 1 : CATCH_REQUIRE(definition_sources.size() == 2);
765 1 : expected_config = "definition=long value here [configuration=\"" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config/hidden.config\"]";
766 1 : CATCH_REQUIRE(definition_sources[0] == expected_config);
767 1 : expected_config = "definition=this is a better definition [configuration=\"" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config/sources.d/65-hidden.config\"]";
768 1 : CATCH_REQUIRE(definition_sources[1] == expected_config);
769 :
770 3 : advgetopt::option_info::pointer_t good(opt.get_option("good"));
771 1 : CATCH_REQUIRE(good != nullptr);
772 3 : CATCH_REQUIRE(opt.is_defined("good"));
773 1 : advgetopt::string_list_t good_sources(good->trace_sources());
774 1 : CATCH_REQUIRE(good_sources.size() == 2);
775 1 : expected_config = "good=variable [configuration=\"" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config/hidden.config\"]";
776 1 : CATCH_REQUIRE(good_sources[0] == expected_config);
777 1 : expected_config = "good=enhanced [configuration=\"" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config/sources.d/65-hidden.config\"]";
778 1 : CATCH_REQUIRE(good_sources[1] == expected_config);
779 :
780 : // process system options now
781 1 : std::stringstream ss;
782 1 : advgetopt::flag_t const result(opt.process_system_options(ss));
783 1 : CATCH_REQUIRE(result == (advgetopt::SYSTEM_OPTION_SHOW_OPTION_SOURCES
784 : | advgetopt::SYSTEM_OPTION_LICENSE
785 : | advgetopt::SYSTEM_OPTION_COPYRIGHT
786 : | advgetopt::SYSTEM_OPTION_CONFIG_DIR));
787 : //std::cerr << "++++++++++++++++ [" << ss.str() << "]\n";;
788 1 : CATCH_REQUIRE_LONG_STRING(ss.str(),
789 : "warning: no copyright notice found.\n"
790 : "GPL 2 or GPL 3\n"
791 : "Option Sources:\n"
792 : " 1. option \"build-date\" (undefined)\n"
793 : "\n"
794 : " 2. option \"color\"\n"
795 : " color[0]=black [environment-variable]\n"
796 : " color[1]=orange [environment-variable]\n"
797 : " color[2]=purple [environment-variable]\n"
798 : "\n"
799 : " 3. option \"compiler-version\" (undefined)\n"
800 : "\n"
801 : " 4. option \"config-dir\"\n"
802 : " config-dir[0]=" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config [environment-variable]\n"
803 : "\n"
804 : " 5. option \"configuration-filenames\" (undefined)\n"
805 : "\n"
806 : " 6. option \"copyright\"\n"
807 : " copyright= [command-line]\n"
808 : "\n"
809 : " 7. option \"definition\"\n"
810 : " definition=long value here [configuration=\"" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config/hidden.config\"]\n"
811 : " definition=this is a better definition [configuration=\"" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config/sources.d/65-hidden.config\"]\n"
812 : "\n"
813 : " 8. option \"environment-variable-name\" (undefined)\n"
814 : "\n"
815 : " 9. option \"filenames-of-option-definitions\" (undefined)\n"
816 : "\n"
817 : " 10. option \"good\"\n"
818 : " good=variable [configuration=\"" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config/hidden.config\"]\n"
819 : " good=enhanced [configuration=\"" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config/sources.d/65-hidden.config\"]\n"
820 : "\n"
821 : " 11. option \"has-sanitizer\" (undefined)\n"
822 : "\n"
823 : " 12. option \"help\" (undefined)\n"
824 : "\n"
825 : " 13. option \"license\"\n"
826 : " license= [command-line]\n"
827 : "\n"
828 : " 14. option \"long-help\" (undefined)\n"
829 : "\n"
830 : " 15. option \"more\"\n"
831 : " more=data [configuration=\"" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config/hidden.config\"]\n"
832 : " more=instructions [environment-variable]\n"
833 : " more=magical [command-line]\n"
834 : "\n"
835 : " 16. option \"multiple\"\n"
836 : " multiple[0]=the origin of life [configuration=\"" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config/hidden.config\"]\n"
837 : " multiple[0]=should we not see this one instead? [configuration=\"" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config/sources.d/65-hidden.config\"]\n"
838 : " multiple[1]=here [environment-variable]\n"
839 : " multiple[2]=too [environment-variable]\n"
840 : " multiple[0]=first [command-line]\n"
841 : " multiple[0]=last [command-line]\n"
842 : "\n"
843 : " 17. option \"organized\"\n"
844 : " organized=set [configuration=\"" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config/hidden.config\"]\n"
845 : " organized=logically [command-line]\n"
846 : "\n"
847 : " 18. option \"path-to-option-definitions\" (undefined)\n"
848 : "\n"
849 : " 19. option \"print-option\" (undefined)\n"
850 : "\n"
851 : " 20. option \"show-option-sources\"\n"
852 : " show-option-sources= [command-line]\n"
853 : "\n"
854 : " 21. option \"sources\"\n"
855 : " sources=just fine [configuration=\"" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config/hidden.config\"]\n"
856 : " sources=all [environment-variable]\n"
857 : "\n"
858 : " 22. option \"system-help\" (undefined)\n"
859 : "\n"
860 : " 23. option \"unique\"\n"
861 : " unique=perfect [configuration=\"" + SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/.config/hidden.config\"]\n"
862 : "\n"
863 : " 24. option \"verbose\"\n"
864 : " verbose= [environment-variable]\n"
865 : "\n"
866 : " 25. option \"version\" (undefined)\n"
867 : "\n"
868 : " 26. option \"zap\"\n"
869 : " zap= [command-line]\n"
870 : "\n"
871 : );
872 1 : }
873 2 : CATCH_END_SECTION()
874 3 : }
875 :
876 :
877 :
878 : // vim: ts=4 sw=4 et
|