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