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