5#include "../../catch2/catch.hpp"
11using namespace mx::app;
22void captureConfigLog(
const std::string &name,
const int &code,
const std::string &value,
const std::string &source )
39int allocationFailureParser(
iniFile &parser ,
40 const std::string &filename )
42 static_cast<void>( parser );
43 static_cast<void>( filename );
70TEST_CASE(
"config file parsing",
"[appConfigurator]" )
72 GIVEN(
"a basic config file" )
77 {
"",
"",
"",
"",
"",
"" },
78 {
"key0",
"key1",
"key2",
"key3",
"key4",
"key5" },
79 {
"val0",
"val1",
"val2",
"val3",
"val4",
"val5" } );
82 config.
add(
"key0",
"",
"", 0,
"",
"key0",
false,
"",
"" );
83 config.add(
"key1",
"",
"", 0,
"",
"key1",
false,
"",
"" );
84 config.add(
"key2",
"",
"", 0,
"",
"key2",
false,
"",
"" );
85 config.add(
"key3",
"",
"", 0,
"",
"key3",
false,
"",
"" );
86 config.add(
"key4",
"",
"", 0,
"",
"key4",
false,
"",
"" );
87 config.add(
"key5",
"",
"", 0,
"",
"key5",
false,
"",
"" );
89 config.readConfig(
"/tmp/test.conf" );
93 config( val,
"key0" );
94 REQUIRE( val ==
"val0" );
96 config( val,
"key1" );
97 REQUIRE( val ==
"val1" );
99 config( val,
"key2" );
100 REQUIRE( val ==
"val2" );
102 config( val,
"key3" );
103 REQUIRE( val ==
"val3" );
105 config( val,
"key4" );
106 REQUIRE( val ==
"val4" );
108 config( val,
"key5" );
109 REQUIRE( val ==
"val5" );
112 WHEN(
"sections, unique keys" )
115 {
"",
"",
"sect1",
"sect1",
"sect2",
"sect2" },
116 {
"key0",
"key1",
"key2",
"key3",
"key4",
"key5" },
117 {
"val0",
"val1",
"val2",
"val3",
"val4",
"val5" } );
120 config.
add(
"key0",
"",
"", 0,
"",
"key0",
false,
"",
"" );
121 config.add(
"key1",
"",
"", 0,
"",
"key1",
false,
"",
"" );
122 config.add(
"sect1.key2",
"",
"", 0,
"sect1",
"key2",
false,
"",
"" );
123 config.add(
"sect1.key3",
"",
"", 0,
"sect1",
"key3",
false,
"",
"" );
124 config.add(
"sect2.key4",
"",
"", 0,
"sect2",
"key4",
false,
"",
"" );
125 config.add(
"sect2.key5",
"",
"", 0,
"sect2",
"key5",
false,
"",
"" );
127 config.readConfig(
"/tmp/test.conf" );
131 config( val,
"key0" );
132 REQUIRE( val ==
"val0" );
134 config( val,
"key1" );
135 REQUIRE( val ==
"val1" );
137 config( val,
"sect1.key2" );
138 REQUIRE( val ==
"val2" );
140 config( val,
"sect1.key3" );
141 REQUIRE( val ==
"val3" );
143 config( val,
"sect2.key4" );
144 REQUIRE( val ==
"val4" );
146 config( val,
"sect2.key5" );
147 REQUIRE( val ==
"val5" );
150 WHEN(
"sections, repeated keys still unique within sections" )
153 {
"",
"",
"sect1",
"sect1",
"sect2",
"sect2" },
154 {
"key0",
"key1",
"key2",
"key3",
"key2",
"key3" },
155 {
"val0",
"val1",
"val2",
"val3",
"val4",
"val5" } );
158 config.
add(
"key0",
"",
"", 0,
"",
"key0",
false,
"",
"" );
159 config.add(
"key1",
"",
"", 0,
"",
"key1",
false,
"",
"" );
160 config.add(
"sect1.key2",
"",
"", 0,
"sect1",
"key2",
false,
"",
"" );
161 config.add(
"sect1.key3",
"",
"", 0,
"sect1",
"key3",
false,
"",
"" );
162 config.add(
"sect2.key2",
"",
"", 0,
"sect2",
"key2",
false,
"",
"" );
163 config.add(
"sect2.key3",
"",
"", 0,
"sect2",
"key3",
false,
"",
"" );
165 config.readConfig(
"/tmp/test.conf" );
169 config( val,
"key0" );
170 REQUIRE( val ==
"val0" );
172 config( val,
"key1" );
173 REQUIRE( val ==
"val1" );
175 config( val,
"sect1.key2" );
176 REQUIRE( val ==
"val2" );
178 config( val,
"sect1.key3" );
179 REQUIRE( val ==
"val3" );
181 config( val,
"sect2.key2" );
182 REQUIRE( val ==
"val4" );
184 config( val,
"sect2.key3" );
185 REQUIRE( val ==
"val5" );
189 GIVEN(
"a config file with unused entries" )
192 WHEN(
"no sections, unused entry in middle" )
195 {
"",
"",
"",
"",
"",
"" },
196 {
"key0",
"key1",
"key2",
"key3",
"key4",
"key5" },
197 {
"val0",
"val1",
"val2",
"val3",
"val4",
"val5" } );
200 config.
add(
"key0",
"",
"", 0,
"",
"key0",
false,
"",
"" );
201 config.add(
"key1",
"",
"", 0,
"",
"key1",
false,
"",
"" );
202 config.add(
"key2",
"",
"", 0,
"",
"key2",
false,
"",
"" );
203 config.add(
"key4",
"",
"", 0,
"",
"key4",
false,
"",
"" );
204 config.add(
"key5",
"",
"", 0,
"",
"key5",
false,
"",
"" );
206 config.readConfig(
"/tmp/test.conf" );
211 config( val,
"key0" );
212 REQUIRE( val ==
"val0" );
214 config( val,
"key1" );
215 REQUIRE( val ==
"val1" );
217 config( val,
"key2" );
218 REQUIRE( val ==
"val2" );
220 config( val,
"key4" );
221 REQUIRE( val ==
"val4" );
223 config( val,
"key5" );
224 REQUIRE( val ==
"val5" );
227 REQUIRE( config.m_unusedConfigs[
iniFile::makeKey(
"",
"key3" )].used ==
false );
230 config.configUnused( val,
"",
"key3" );
231 REQUIRE( val ==
"val3" );
234 REQUIRE( config.m_unusedConfigs[
iniFile::makeKey(
"",
"key3" )].used ==
true );
237 WHEN(
"sections, repeated keys, unused sections" )
240 {
"",
"",
"sect1",
"sect1",
"sect2",
"sect2",
"sect3" },
241 {
"key0",
"key1",
"key2",
"key3",
"key2",
"key3",
"key4" },
242 {
"val0",
"val1",
"val2",
"val3",
"val4",
"val5",
"val6" } );
245 config.
add(
"key0",
"",
"", 0,
"",
"key0",
false,
"",
"" );
246 config.add(
"key1",
"",
"", 0,
"",
"key1",
false,
"",
"" );
247 config.add(
"sect2.key2",
"",
"", 0,
"sect2",
"key2",
false,
"",
"" );
248 config.add(
"sect2.key3",
"",
"", 0,
"sect2",
"key3",
false,
"",
"" );
250 config.readConfig(
"/tmp/test.conf" );
254 config( val,
"key0" );
255 REQUIRE( val ==
"val0" );
257 config( val,
"key1" );
258 REQUIRE( val ==
"val1" );
260 config( val,
"sect2.key2" );
261 REQUIRE( val ==
"val4" );
263 config( val,
"sect2.key3" );
264 REQUIRE( val ==
"val5" );
266 config.configUnused( val,
"sect1",
"key2" );
267 REQUIRE( val ==
"val2" );
269 config.configUnused( val,
"sect1",
"key3" );
270 REQUIRE( val ==
"val3" );
272 config.configUnused( val,
"sect3",
"key4" );
273 REQUIRE( val ==
"val6" );
275 std::vector<std::string> sections;
276 config.unusedSections( sections );
277 REQUIRE( sections.size() == 2 );
278 REQUIRE( ( sections[0] ==
"sect1" || sections[1] ==
"sect1" ) );
279 REQUIRE( ( sections[0] ==
"sect3" || sections[1] ==
"sect3" ) );
284 GIVEN(
"a config file with repeated keys within the same section" )
286 WHEN(
"no sections" )
289 {
"",
"",
"",
"",
"",
"" },
290 {
"key0",
"key0",
"key2",
"key2",
"key4",
"key4" },
291 {
"val0",
"val1",
"val2",
"val3",
"val4",
"val5" } );
294 config.
add(
"key0",
"",
"", 0,
"",
"key0",
false,
"",
"" );
295 config.add(
"key2",
"",
"", 0,
"",
"key2",
false,
"",
"" );
296 config.add(
"key4",
"",
"", 0,
"",
"key4",
false,
"",
"" );
298 config.readConfig(
"/tmp/test.conf" );
302 config( val,
"key0" );
303 REQUIRE( val ==
"val0val1" );
305 config( val,
"key2" );
306 REQUIRE( val ==
"val2val3" );
308 config( val,
"key4" );
309 REQUIRE( val ==
"val4val5" );
312 WHEN(
"repeated sections and keys" )
315 {
"",
"sect1",
"sect2",
"sect1",
"sect2",
"sect2",
"sect3" },
316 {
"key0",
"key1",
"key2",
"key1",
"key2",
"key2",
"key3" },
317 {
"val0",
"val1",
"val2",
"val3",
"val4",
"val4.1",
"val5" } );
320 config.
add(
"key0",
"",
"", 0,
"",
"key0",
false,
"",
"" );
321 config.add(
"key1",
"",
"", 0,
"sect1",
"key1",
false,
"",
"" );
322 config.add(
"key2",
"",
"", 0,
"sect2",
"key2",
false,
"",
"" );
323 config.add(
"key3",
"",
"", 0,
"sect3",
"key3",
false,
"",
"" );
325 config.readConfig(
"/tmp/test7.conf" );
329 config( val,
"key0" );
330 REQUIRE( val ==
"val0" );
332 config( val,
"key1" );
333 REQUIRE( val ==
"val1val3" );
335 config( val,
"key2" );
336 REQUIRE( val ==
"val2val4val4.1" );
338 config( val,
"key3" );
339 REQUIRE( val ==
"val5" );
342 WHEN(
"multi-line keys" )
345 {
"",
"sect1",
"sect2",
"sect1",
"sect2",
"sect3" },
346 {
"key0",
"key1",
"key2",
"key1",
"key2",
"key3" },
347 {
"val0\n val0.1",
"val1",
"val2",
"val3\n val3.1",
"val4",
"val5" } );
350 config.
add(
"key0",
"",
"", 0,
"",
"key0",
false,
"",
"" );
351 config.add(
"key1",
"",
"", 0,
"sect1",
"key1",
false,
"",
"" );
352 config.add(
"key2",
"",
"", 0,
"sect2",
"key2",
false,
"",
"" );
353 config.add(
"key3",
"",
"", 0,
"sect3",
"key3",
false,
"",
"" );
355 config.readConfig(
"/tmp/test.conf" );
359 config( val,
"key0" );
360 REQUIRE( val ==
"val0val0.1" );
362 config( val,
"key1" );
363 REQUIRE( val ==
"val1val3val3.1" );
365 config( val,
"key2" );
366 REQUIRE( val ==
"val2val4" );
368 config( val,
"key3" );
369 REQUIRE( val ==
"val5" );
373 GIVEN(
"a config file with vectors" )
375 WHEN(
"no sections" )
379 {
"key0",
"key1",
"key2" },
380 {
"val0,val1,val2",
" val3, val4, ",
"val5" } );
383 config.
add(
"key0",
"",
"", 0,
"",
"key0",
false,
"",
"" );
384 config.add(
"key1",
"",
"", 0,
"",
"key1",
false,
"",
"" );
385 config.add(
"key2",
"",
"", 0,
"",
"key2",
false,
"",
"" );
387 config.readConfig(
"/tmp/test.conf" );
389 std::vector<std::string> vals;
391 config( vals,
"key0" );
392 REQUIRE( vals[0] ==
"val0" );
393 REQUIRE( vals[1] ==
"val1" );
394 REQUIRE( vals[2] ==
"val2" );
396 config( vals,
"key1" );
397 REQUIRE( vals[0] ==
"val3" );
398 REQUIRE( vals[1] ==
"val4" );
399 REQUIRE( vals[2] ==
"" );
401 config( vals,
"key2" );
402 REQUIRE( vals[0] ==
"val5" );
412TEST_CASE(
"readConfig handles failures and tracks recognized sources",
"[appConfigurator]" )
416 config.
add(
"known",
"",
"", 0,
"section",
"known",
false,
"",
"" );
419 REQUIRE( config.
readConfig(
"/tmp/mxlib-missing-config.conf",
false ) == -1 );
420 REQUIRE( config.
readConfig(
"/tmp/mxlib-missing-config.conf" ) == -1 );
423 std::ofstream badConfig(
"/tmp/mxlib-invalid-config.conf" );
424 badConfig <<
"[unterminated\n";
426 REQUIRE( config.
readConfig(
"/tmp/mxlib-invalid-config.conf" ) == -1 );
429 std::ofstream goodConfig(
"/tmp/mxlib-read-config.conf" );
430 goodConfig <<
"[section]\nknown = value\n[extra]\nunused = retained\n";
432 REQUIRE( config.
readConfig(
"/tmp/mxlib-read-config.conf" ) == 0 );
433 REQUIRE( config.
m_targets.at(
"known" ).values.at( 0 ) ==
"value" );
434 REQUIRE( config.
m_targets.at(
"known" ).sources.at( 0 ) ==
"/tmp/mxlib-read-config.conf" );
435 REQUIRE( config.
m_unusedConfigs.at(
"extra=unused" ).values.at( 0 ) ==
"retained" );
436 REQUIRE( config.
m_unusedConfigs.at(
"extra=unused" ).sources.at( 0 ) ==
"/tmp/mxlib-read-config.conf" );
440 REQUIRE( config.
readConfig(
"/tmp/mxlib-allocation-failure.conf" ) == -1 );
447TEST_CASE(
"command-line configuration targets preserve metadata and typed values",
"[appConfigurator]" )
450 config.
add(
"images",
"i",
"images", mx::app::argType::Required,
"files",
"images",
true,
"string",
"" );
451 config.
add(
"threshold",
"t",
"threshold", mx::app::argType::Required,
"quality",
"threshold",
false,
"float",
"" );
452 config.
add(
"enabled",
"",
"enabled", mx::app::argType::True,
"preProcess",
"enabled",
false,
"bool",
"" );
454 char invokedName[] =
"configTest";
455 char imagesOption[] =
"--images=target.fits";
456 char thresholdOption[] =
"--threshold=0.75";
457 char enabledOption[] =
"--enabled";
458 char *argv[] = { invokedName, imagesOption, thresholdOption, enabledOption };
461 REQUIRE( config.
nAdded == 3 );
462 REQUIRE( config.
m_targets.at(
"images" ).section ==
"files" );
463 REQUIRE( config.
m_targets.at(
"images" ).keyword ==
"images" );
464 REQUIRE( config.
m_targets.at(
"images" ).isRequired );
465 REQUIRE( config.
isSet(
"images" ) );
466 REQUIRE( config.
isSet(
"threshold" ) );
467 REQUIRE( config.
isSet(
"enabled" ) );
470 float threshold{ 0 };
471 bool enabled{
false };
472 REQUIRE( config( images,
"images" ) == 0 );
473 REQUIRE( config( threshold,
"threshold" ) == 0 );
474 REQUIRE( config( enabled,
"enabled" ) == 0 );
475 REQUIRE( images ==
"target.fits" );
476 REQUIRE( threshold == 0.75f );
480 REQUIRE( config( unset,
"not-added" ) == 0 );
481 REQUIRE( unset == 19 );
488TEST_CASE(
"command-line configuration tracks selected and unknown options",
"[appConfigurator]" )
492 config.
add(
"first",
"",
"first", mx::app::argType::Required,
"",
"first",
false,
"int",
"" );
493 config.
add(
"second",
"",
"second", mx::app::argType::Required,
"",
"second",
false,
"int",
"" );
494 char invokedName[] =
"configTest";
495 char firstOption[] =
"--first=5";
496 char secondOption[] =
"--second=7";
497 char unknownOption[] =
"--unknown=value";
498 char *argv[] = { invokedName, firstOption, secondOption, unknownOption };
501 REQUIRE( config.
isSet(
"first" ) );
502 REQUIRE_FALSE( config.
isSet(
"second" ) );
503 REQUIRE( config.
m_targets.at(
"first" ).sources.at( 0 ) ==
"command line" );
506 REQUIRE( config.
m_unusedConfigs.begin()->second.sources.at( 0 ) ==
"command line" );
509 REQUIRE( config.
get( first,
"first", 0 ) == 0 );
510 REQUIRE( first == 5 );
511 REQUIRE( config.
get( first,
"first", 1 ) == -1 );
515 REQUIRE( config( second,
"second" ) == 0 );
516 REQUIRE( second == 7 );
517 REQUIRE( config.
verbosity(
"first" ) == 1 );
518 REQUIRE( config.
count(
"first" ) == 2 );
531TEST_CASE(
"command-line-only targets retain duplicate names",
"[appConfigurator]" )
534 config.
add(
"verbosity",
"v",
"verbose", mx::app::argType::True,
"",
"",
false,
"",
"" );
535 config.
add(
"verbosity",
"V",
"very-verbose", mx::app::argType::True,
"",
"",
false,
"",
"" );
539 REQUIRE( config.
m_targets.at(
"verbosity" ).orderAdded == 0 );
541 REQUIRE( config.
clOnlyTargets.begin()->longOpt ==
"very-verbose" );
548TEST_CASE(
"scalar configuration access covers HCI value types",
"[appConfigurator]" )
554 int defaultValue{ 42 };
555 REQUIRE( config( defaultValue,
"unset" ) == 0 );
556 REQUIRE( defaultValue == 42 );
557 REQUIRE( logName ==
"unset" );
559 REQUIRE( logValue ==
"42" );
560 REQUIRE( logSource ==
"default" );
563 REQUIRE( config.
get( defaultValue,
"unset-indexed", 0 ) == 0 );
564 REQUIRE( defaultValue == 42 );
565 REQUIRE( logName ==
"unset-indexed" );
566 REQUIRE( logValue ==
"42" );
567 REQUIRE( logSource ==
"default" );
569 config.
add(
"integer",
"",
"", 0,
"",
"integer" );
570 config.
m_targets.at(
"integer" ).set =
true;
571 config.
m_targets.at(
"integer" ).values = {
"11",
"23" };
572 config.
m_targets.at(
"integer" ).sources = {
"first.conf",
"second.conf" };
574 int integerValue{ 0 };
576 REQUIRE( config.
get( integerValue,
"integer", 0 ) == 0 );
577 REQUIRE( integerValue == 11 );
578 REQUIRE( logValue ==
"11" );
579 REQUIRE( logSource.empty() );
580 REQUIRE( config.
get( integerValue,
"integer", 2 ) == -1 );
584 REQUIRE( config.
get( integerValue,
"integer" ) == 0 );
585 REQUIRE( integerValue == 23 );
586 REQUIRE( logValue ==
"23" );
587 REQUIRE( logSource ==
"second.conf" );
591 config.
add(
"empty",
"",
"", 0,
"",
"empty" );
592 config.
m_targets.at(
"empty" ).set =
true;
593 REQUIRE( config.
get( integerValue,
"empty" ) == -1 );
595 config.
add(
"string",
"",
"", 0,
"",
"string" );
596 config.
add(
"boolean",
"",
"", 0,
"",
"boolean" );
597 config.
add(
"float",
"",
"", 0,
"",
"float" );
598 config.
add(
"double",
"",
"", 0,
"",
"double" );
599 config.
m_targets.at(
"string" ).set =
true;
600 config.
m_targets.at(
"boolean" ).set =
true;
601 config.
m_targets.at(
"float" ).set =
true;
602 config.
m_targets.at(
"double" ).set =
true;
603 config.
m_targets.at(
"string" ).values = {
"target.fits" };
604 config.
m_targets.at(
"boolean" ).values = {
"true" };
605 config.
m_targets.at(
"float" ).values = {
"1.25" };
606 config.
m_targets.at(
"double" ).values = {
"2.5" };
608 std::string stringValue;
609 bool boolValue{
false };
610 float floatValue{ 0 };
611 double doubleValue{ 0 };
612 REQUIRE( config( stringValue,
"string" ) == 0 );
613 REQUIRE( config( boolValue,
"boolean" ) == 0 );
614 REQUIRE( config( floatValue,
"float" ) == 0 );
615 REQUIRE( config( doubleValue,
"double" ) == 0 );
616 REQUIRE( stringValue ==
"target.fits" );
617 REQUIRE( boolValue );
618 REQUIRE( floatValue == 1.25f );
619 REQUIRE( doubleValue == 2.5 );
626TEST_CASE(
"vector configuration access covers HCI value types",
"[appConfigurator]" )
631 std::vector<int> defaultValues{ 7, 8 };
633 REQUIRE( config( defaultValues,
"unset-vector" ) == 0 );
634 REQUIRE( defaultValues == ( std::vector<int>{ 7, 8 } ) );
635 REQUIRE( logName ==
"unset-vector" );
636 REQUIRE( logValue ==
"[need a vector to string]" );
637 REQUIRE( logSource ==
"default" );
640 REQUIRE( config.
get( defaultValues,
"unset-vector-indexed", 0 ) == 0 );
641 REQUIRE( defaultValues == ( std::vector<int>{ 7, 8 } ) );
642 REQUIRE( logName ==
"unset-vector-indexed" );
643 REQUIRE( logValue ==
"[need a vector to string]" );
644 REQUIRE( logSource ==
"default" );
646 config.
add(
"empty-vector",
"",
"", 0,
"",
"empty-vector" );
647 config.
m_targets.at(
"empty-vector" ).set =
true;
648 config.
m_targets.at(
"empty-vector" ).values = {
"" };
649 config.
m_targets.at(
"empty-vector" ).sources = {
"empty.conf" };
652 REQUIRE( config.
get( defaultValues,
"empty-vector", 0 ) == 0 );
653 REQUIRE( defaultValues.empty() );
654 REQUIRE( logValue.empty() );
655 REQUIRE( logSource ==
"empty.conf" );
658 defaultValues = { 9 };
660 REQUIRE( config.
get( defaultValues,
"empty-vector", 0 ) == 0 );
661 REQUIRE( defaultValues.empty() );
662 REQUIRE( logSource.empty() );
663 REQUIRE( config.
get( defaultValues,
"empty-vector", 1 ) == -1 );
665 config.
add(
"integer-vector",
"",
"", 0,
"",
"integer-vector" );
666 config.
m_targets.at(
"integer-vector" ).set =
true;
667 config.
m_targets.at(
"integer-vector" ).values = {
" 1, 2,3",
"4,5,6" };
668 config.
m_targets.at(
"integer-vector" ).sources = {
"first.conf",
"second.conf" };
670 std::vector<int> integerValues;
672 REQUIRE( config.
get( integerValues,
"integer-vector", 0 ) == 0 );
673 REQUIRE( integerValues == ( std::vector<int>{ 1, 2, 3 } ) );
674 REQUIRE( logValue ==
" 1, 2,3" );
675 REQUIRE( logSource.empty() );
679 REQUIRE( config.
get( integerValues,
"integer-vector" ) == 0 );
680 REQUIRE( integerValues == ( std::vector<int>{ 4, 5, 6 } ) );
681 REQUIRE( logValue ==
"4,5,6" );
682 REQUIRE( logSource ==
"second.conf" );
686 config.
add(
"float-vector",
"",
"", 0,
"",
"float-vector" );
687 config.
m_targets.at(
"float-vector" ).set =
true;
688 config.
m_targets.at(
"float-vector" ).values = {
"1.5,2.5" };
689 std::vector<float> floatValues;
690 REQUIRE( config( floatValues,
"float-vector" ) == 0 );
691 REQUIRE( floatValues == ( std::vector<float>{ 1.5f, 2.5f } ) );
693 config.
add(
"string-vector",
"",
"", 0,
"",
"string-vector" );
694 config.
m_targets.at(
"string-vector" ).set =
true;
695 config.
m_targets.at(
"string-vector" ).values = {
"alpha, beta," };
696 std::vector<std::string> stringValues;
697 REQUIRE( config( stringValues,
"string-vector" ) == 0 );
698 REQUIRE( stringValues == ( std::vector<std::string>{
"alpha",
"beta",
"" } ) );
700 config.
add(
"empty-values",
"",
"", 0,
"",
"empty-values" );
701 config.
m_targets.at(
"empty-values" ).set =
true;
702 REQUIRE( config.
get( integerValues,
"empty-values" ) == -1 );
An application configuration manager.
void writeConfigFile(const std::string &fname, const std::vector< std::string > §ions, const std::vector< std::string > &keywords, const std::vector< std::string > &values)
A simple config file writing function, useful for testing.
int(* appConfiguratorParser)(iniFile &parser, const std::string &filename)
Optional parser override used to deterministically test appConfigurator parser failures.
TEST_CASE("config file parsing", "[appConfigurator]")
Verify that appConfigurator parses global, sectioned, and unused configuration entries.
Class to manage a set of configurable values, and read their values from config/ini files and the com...
std::unordered_map< std::string, configTarget > m_targets
The targets are stored in an unordered_map for fast access by key.
int readConfig(const std::string &fname, bool reportFileNotFound=true)
Read and parse a config/ini file, updating the targets.
void add(const configTarget &tgt)
Add a configTarget.
bool m_sources
Flag controlling whether or not to record config sources.
int get(typeT &v, const std::string &name, size_t i, std::unordered_map< std::string, configTarget > &targets)
Get the i-th value of the target, converted to the specified type.
std::vector< std::string > nonOptions
Non-option arguments from the command line.
void(* configLog)(const std::string &name, const int &code, const std::string &valueStr, const std::string &source)
Call an external logging function whenever a config value is accessed by get or operator().
int verbosity(const std::string &name, std::unordered_map< std::string, configTarget > &targets)
Get the command line verbosity count for this option.
bool isSet(const std::string &name, std::unordered_map< std::string, configTarget > &targets)
Check if a target has been set by the configuration.
void clear()
Clear the containers and free up the associated memory.
std::list< configTarget > clOnlyTargets
Targets which are only for the command line are stored separately in a list.
int count(const std::string &name, std::unordered_map< std::string, configTarget > &targets)
Get the number of different values set for the specified config target.
void parseCommandLine(int argc, char **argv, const std::string &oneTarget="")
Parse the command line, updating the targets.
int nAdded
Running count of options added, used to track order.
std::unordered_map< std::string, configTarget > m_unusedConfigs
A wrapper for the ini functions.
static std::string makeKey(const std::string §ion, const std::string &name)
Return a key generated from the section and name.