JayBeams  0.1
Another project to have fun coding.
ut_config_object.cpp
Go to the documentation of this file.
2 #include <jb/config_object.hpp>
3 
4 #include <boost/filesystem.hpp>
5 #include <boost/test/unit_test.hpp>
6 #include <cstdlib>
7 #include <fstream>
8 #include <string>
9 #include <vector>
10 
11 /**
12  * Helper classes to test jb::config_object and jb::config_attribute
13  */
14 namespace {
15 class simple : public jb::config_object {
16 public:
17  simple()
18  : foo(this)
19  , bar(this, "default value")
20  , baz(this, 123) {
21  }
23 
27 };
28 
29 class complex : public jb::config_object {
30 public:
31  complex()
32  : base(this)
33  , names(this, {"one", "two", "3"}) {
34  }
36 
39 };
40 
41 class test_variadic : public jb::config_object {
42 public:
43  test_variadic()
44  : foo(this, 1, 2) {
45  }
46  config_object_constructors(test_variadic);
47 
49 };
50 }
51 
52 /**
53  * @test Verify we can create simple jb::config_attribute objects
54  */
55 BOOST_AUTO_TEST_CASE(config_attribute_simple) {
56  simple tested;
57 
58  BOOST_CHECK_EQUAL(tested.foo(), std::string(""));
59  BOOST_CHECK_EQUAL(tested.bar(), std::string("default value"));
60  BOOST_CHECK_EQUAL(tested.baz(), 123);
61 
62  tested.foo("new value").baz(456);
63  BOOST_CHECK_EQUAL(tested.foo(), std::string("new value"));
64  BOOST_CHECK_EQUAL(tested.baz(), 456);
65 }
66 
67 /**
68  * @test Verify we can create more complex jb::config_attribute objects
69  */
70 BOOST_AUTO_TEST_CASE(config_attribute_complex) {
71  complex tested;
72 
73  BOOST_CHECK_EQUAL(tested.base().foo(), std::string(""));
74  BOOST_CHECK_EQUAL(tested.base().bar(), std::string("default value"));
75  BOOST_CHECK_EQUAL(tested.base().baz(), 123);
76  BOOST_REQUIRE_EQUAL(tested.names().size(), 3);
77  BOOST_CHECK_EQUAL(tested.names()[0], "one");
78  BOOST_CHECK_EQUAL(tested.names()[1], "two");
79  BOOST_CHECK_EQUAL(tested.names()[2], "3");
80 
81  auto tmp = tested.base();
82  tmp.foo("new value").baz(456);
83  tested.base(std::move(tmp));
84 
85  complex other(tested);
86  BOOST_CHECK_EQUAL(other.base().foo(), std::string("new value"));
87  BOOST_CHECK_EQUAL(other.base().baz(), 456);
88 }
89 
90 /**
91  * @test Verify we can copy and assign complex config objects.
92  */
93 BOOST_AUTO_TEST_CASE(config_attribute_complex_copy) {
94  complex src;
95 
96  auto tmp = src.base();
97  tmp.foo("new value").baz(456);
98  src.base(std::move(tmp));
99  src.names(std::vector<std::string>({"1", "2", "three"}));
100 
101  complex tested;
102  tested = std::move(src);
103  BOOST_REQUIRE_EQUAL(tested.names().size(), 3);
104  BOOST_CHECK_EQUAL(tested.names()[0], "1");
105  BOOST_CHECK_EQUAL(tested.names()[1], "2");
106  BOOST_CHECK_EQUAL(tested.names()[2], "three");
107  BOOST_CHECK_EQUAL(src.names().size(), 0);
108 
109  complex other(std::move(tested));
110  BOOST_REQUIRE_EQUAL(other.names().size(), 3);
111  BOOST_CHECK_EQUAL(other.names()[0], "1");
112  BOOST_CHECK_EQUAL(other.names()[1], "2");
113  BOOST_CHECK_EQUAL(other.names()[2], "three");
114  BOOST_CHECK_EQUAL(tested.names().size(), 0);
115 }
116 
117 /**
118  * @test Verify we can create jb::config_attribute objects with
119  * complex constructors
120  */
121 BOOST_AUTO_TEST_CASE(config_attribute_variadic_constructor) {
122  test_variadic tested;
123 
124  BOOST_CHECK_EQUAL(tested.foo().first, 1);
125  BOOST_CHECK_EQUAL(tested.foo().second, 2);
126 }
127 
128 namespace {
129 
130 class config0 : public jb::config_object {
131 public:
132  config0()
133  : x(desc("x"), this)
134  , y(desc("y"), this)
135  , z(desc("z"), this) {
136  }
137 
139 
143 };
144 
145 // Convenience functions for the unit tests.
146 std::ostream& operator<<(std::ostream& os, config0 const& x) {
147  return os << "{x=" << x.x() << ", y=" << x.y() << ", z=" << x.z() << "}";
148 }
149 
150 bool operator==(config0 const& lhs, config0 const& rhs) {
151  return lhs.x() == rhs.x() and lhs.y() == rhs.y() and lhs.z() == rhs.z();
152 }
153 
154 config0 make_config0(int x, int y, int z) {
155  return std::move(config0().x(x).y(y).z(z));
156 }
157 
158 class config1 : public jb::config_object {
159 public:
160  config1()
161  : list(desc("list"), this, {"ini", "mini", "myni", "mo"})
162  , pos(desc("pos", "config0"), this) {
163  }
164 
166 
169 };
170 
171 } // namespace anonymous
172 
173 /**
174  * @test Verify the framework support deeply nested configs with
175  * by_class overrides.
176  */
177 BOOST_AUTO_TEST_CASE(config_object_apply) {
178  char const contents[] = R"""(# YAML overrides
179 # This applies to all objects of type 'config0'
180 :config0:
181  x: -1
182  y: -2
183  z: -3
184 list: ['1', '3', '5', '7']
185 pos:
186  x: 2
187  y: 4
188 )""";
189 
190  BOOST_TEST_MESSAGE("Applying overrides from\n" << contents << "\n");
191  config1 tested;
192  std::vector<std::string> expected({"ini", "mini", "myni", "mo"});
193  BOOST_CHECK_EQUAL_COLLECTIONS(
194  tested.list().begin(), tested.list().end(), expected.begin(),
195  expected.end());
196  BOOST_CHECK_EQUAL(tested.pos(), make_config0(0, 0, 0));
197 
198  std::istringstream is(contents);
199  int argc = 0;
200  tested.load_overrides(argc, nullptr, is);
201 
202  expected.assign({"1", "3", "5", "7"});
203  BOOST_CHECK_EQUAL_COLLECTIONS(
204  tested.list().begin(), tested.list().end(), expected.begin(),
205  expected.end());
206  BOOST_CHECK_EQUAL(tested.pos(), make_config0(2, 4, -3));
207 }
208 
209 namespace {
210 class config2 : public jb::config_object {
211 public:
212  config2()
213  : vars(desc("vars"), this) {
214  }
215 
217 
219 };
220 } // namespace anonymous
221 
222 /**
223  * @test Verify the framework supports vectors of config objects.
224  */
225 BOOST_AUTO_TEST_CASE(config_object_vector) {
226  char const contents[] = R"""(# YAML overrides
227 vars:
228  - list: ['1', '3', '5', '7']
229  pos:
230  x: 2
231  y: 4
232  - list: ['2', '4', '6', '8']
233  pos:
234  y: 1
235  z: 3
236  - list: ['11']
237  pos:
238  x: 1
239  y: 2
240  z: 3
241 )""";
242 
243  BOOST_TEST_MESSAGE("Applying overrides from\n" << contents << "\n");
244 
245  config2 tested;
246  std::istringstream is(contents);
247  int argc = 0;
248  tested.load_overrides(argc, nullptr, is);
249 
250  BOOST_REQUIRE_EQUAL(tested.vars().size(), 3);
251 
252  std::vector<std::string> expected;
253 
254  expected.assign({"1", "3", "5", "7"});
255  BOOST_CHECK_EQUAL_COLLECTIONS(
256  tested.vars()[0].list().begin(), tested.vars()[0].list().end(),
257  expected.begin(), expected.end());
258  BOOST_CHECK_EQUAL(tested.vars()[0].pos().x(), 2);
259  BOOST_CHECK_EQUAL(tested.vars()[0].pos().y(), 4);
260 
261  expected.assign({"2", "4", "6", "8"});
262  BOOST_CHECK_EQUAL_COLLECTIONS(
263  tested.vars()[1].list().begin(), tested.vars()[1].list().end(),
264  expected.begin(), expected.end());
265  BOOST_CHECK_EQUAL(tested.vars()[1].pos().y(), 1);
266  BOOST_CHECK_EQUAL(tested.vars()[1].pos().z(), 3);
267 
268  expected.assign({"11"});
269  BOOST_CHECK_EQUAL_COLLECTIONS(
270  tested.vars()[2].list().begin(), tested.vars()[2].list().end(),
271  expected.begin(), expected.end());
272  BOOST_CHECK_EQUAL(tested.vars()[2].pos(), make_config0(1, 2, 3));
273 }
274 
275 /**
276  * @test Verify the framework supports vectors of config objects that
277  * are empty.
278  */
279 BOOST_AUTO_TEST_CASE(config_object_vector_empty) {
280  config2 tested;
281  std::istringstream is("");
282  char argv0[] = "not_a_path";
283  char argv1[] = "--vars.0.pos.x=2";
284  char* argv[] = {argv0, argv1, nullptr};
285  int argc = sizeof(argv) / sizeof(argv[0]) - 1;
286  tested.load_overrides(argc, argv, is);
287 
288  BOOST_REQUIRE_EQUAL(tested.vars().size(), 1);
289  BOOST_CHECK_EQUAL(tested.vars()[0].pos().x(), 2);
290 
291  std::ostringstream os;
292  BOOST_CHECK_NO_THROW(os << tested);
293  config2 empty;
294  BOOST_CHECK_NO_THROW(os << empty);
295 }
296 
297 namespace {
298 class config3 : public jb::config_object {
299 public:
300  config3()
301  : foo(desc("foo", "config0"), this)
302  , bar(desc("bar", "config0"), this)
303  , baz(desc("baz", "config0"), this) {
304  }
305 
307 
311 };
312 
313 class config4 : public jb::config_object {
314 public:
315  config4()
316  : ini(desc("ini"), this)
317  , mini(desc("mini"), this)
318  , myni(desc("myni"), this) {
319  }
320 
322 
326 };
327 
328 } // namespace anonymous
329 
330 /**
331  * @test Verify the framework supports configuring by class in nested structs.
332  */
333 BOOST_AUTO_TEST_CASE(config_object_nested_by_class) {
334  char const contents[] = R"""(# YAML overrides
335 # This override applies to all objects of type config0, wherever
336 # they are found...
337 :config0:
338  x: -1
339  y: -1
340  z: -1
341 ini:
342  # While this configuration only applies to the objects in this scope...
343  :config0:
344  x: -2
345  y: -2
346  z: -2
347  bar:
348  x: 1
349  y: 2
350 mini:
351  foo:
352  x: 3
353  bar:
354  y: 4
355 myni:
356  # Notice that we can override just a few fields too
357  :config0:
358  z: -3
359  baz:
360  z: 5
361 )""";
362 
363  BOOST_TEST_MESSAGE("Applying overrides from doc\n" << contents << "\n");
364 
365  config4 tested;
366  std::istringstream is(contents);
367  int argc = 0;
368  tested.load_overrides(argc, nullptr, is);
369 
370  BOOST_CHECK_EQUAL(tested.ini().foo(), make_config0(-2, -2, -2));
371  BOOST_CHECK_EQUAL(tested.ini().bar(), make_config0(1, 2, -2));
372  BOOST_CHECK_EQUAL(tested.ini().baz(), make_config0(-2, -2, -2));
373 
374  BOOST_CHECK_EQUAL(tested.mini().foo(), make_config0(3, -1, -1));
375  BOOST_CHECK_EQUAL(tested.mini().bar(), make_config0(-1, 4, -1));
376  BOOST_CHECK_EQUAL(tested.mini().baz(), make_config0(-1, -1, -1));
377 
378  BOOST_CHECK_EQUAL(tested.myni().foo(), make_config0(-1, -1, -3));
379  BOOST_CHECK_EQUAL(tested.myni().bar(), make_config0(-1, -1, -3));
380  BOOST_CHECK_EQUAL(tested.myni().baz(), make_config0(-1, -1, 5));
381 
382  std::ostringstream os;
383  os << tested;
384 }
385 
386 /**
387  * @test Verify that we can load configurations from an iostream.
388  */
389 BOOST_AUTO_TEST_CASE(config_object_load) {
390  char const contents[] = R"""(# YAML overrides
391 :config0:
392  x: -1
393  y: -2
394  z: -3
395 list: ['1', '3', '5', '7']
396 pos:
397  x: 2
398  y: 4
399 )""";
400 
401  std::istringstream is(contents);
402 
403  config1 tested;
404  char argv0[] = "not_a_path";
405  char* argv[] = {argv0, nullptr};
406  int argc = sizeof(argv) / sizeof(argv[0]) - 1;
407 
408  tested.load_overrides(argc, argv, is);
409 
410  std::vector<std::string> expected({"1", "3", "5", "7"});
411  BOOST_CHECK_EQUAL_COLLECTIONS(
412  tested.list().begin(), tested.list().end(), expected.begin(),
413  expected.end());
414  BOOST_CHECK_EQUAL(tested.pos(), make_config0(2, 4, -3));
415 }
416 
417 /**
418  * @test Verify that we apply command-line arguments after the overrides.
419  */
420 BOOST_AUTO_TEST_CASE(config_object_cmdline_args) {
421  char const contents[] = R"""(# YAML overrides
422 :config0:
423  x: -1
424  y: -2
425  z: -3
426 pos:
427  x: 2
428  y: 4
429 )""";
430 
431  std::istringstream is(contents);
432 
433  config1 tested;
434  char argv0[] = "not_a_path";
435  char argv1[] = "--pos.x=3";
436  char argv2[] = "--list=1";
437  char argv3[] = "--list=3";
438  char argv4[] = "--list=5";
439  char argv5[] = "--list=7";
440  char* argv[] = {argv0, argv1, argv2, argv3, argv4, argv5, nullptr};
441  int argc = sizeof(argv) / sizeof(argv[0]) - 1;
442 
443  tested.load_overrides(argc, argv, is);
444 
445  std::vector<std::string> expected({"1", "3", "5", "7"});
446  BOOST_CHECK_EQUAL_COLLECTIONS(
447  tested.list().begin(), tested.list().end(), expected.begin(),
448  expected.end());
449  BOOST_CHECK_EQUAL(tested.pos(), make_config0(3, 4, -3));
450 }
451 
452 /**
453  * @test Verify that config objects raise usage exceptions.
454  */
455 BOOST_AUTO_TEST_CASE(config_object_usage) {
456  config1 tested;
457  char argv0[] = "binary";
458  char argv1[] = "--help";
459  char* argv[] = {argv0, argv1, nullptr};
460  int argc = sizeof(argv) / sizeof(argv[0]) - 1;
461  std::istringstream is("");
462 
463  BOOST_CHECK_THROW(tested.load_overrides(argc, argv, is), jb::usage);
464 }
465 
466 /**
467  * @test Verify that config objects raise exceptions when presented
468  * with invalid options.
469  */
470 BOOST_AUTO_TEST_CASE(config_object_invalid_option) {
471  config1 tested;
472  char argv0[] = "binary";
473  char argv1[] = "--invalid-option";
474  char* argv[] = {argv0, argv1, nullptr};
475  int argc = sizeof(argv) / sizeof(argv[0]) - 1;
476  std::istringstream is("");
477 
478  BOOST_CHECK_THROW(tested.load_overrides(argc, argv, is), std::exception);
479 }
480 
481 namespace {
482 class config5 : public jb::config_object {
483 public:
484  config5()
485  : foo(desc("foo"), this) {
486  }
487 
489 
491 };
492 } // anonymous namespace
493 
494 /**
495  * @test Verify config_objects can handle std::pair<>
496  */
497 BOOST_AUTO_TEST_CASE(config_object_pair_yaml) {
498  char const contents[] = R"""(# YAML overrides
499 foo:
500  - 2
501  - 7
502 )""";
503 
504  std::istringstream is(contents);
505  config5 tested;
506  int argc = 0;
507  tested.load_overrides(argc, nullptr, is);
508 
509  BOOST_CHECK_EQUAL(tested.foo().first, 2);
510  BOOST_CHECK_EQUAL(tested.foo().second, 7);
511 }
512 
513 /**
514  * @test Verify config_objects can handle std::pair<>
515  */
516 BOOST_AUTO_TEST_CASE(config_object_pair_options) {
517  char const contents[] = R"""(# YAML overrides
518 foo:
519  - 2
520  - 7
521 )""";
522 
523  std::istringstream is(contents);
524  config5 tested;
525  char argv0[] = "binary";
526  char argv1[] = "--foo.first=42";
527  char argv2[] = "--foo.second=43";
528  char* argv[] = {argv0, argv1, argv2, nullptr};
529  int argc = sizeof(argv) / sizeof(argv[0]) - 1;
530  tested.load_overrides(argc, argv, is);
531 
532  BOOST_CHECK_EQUAL(tested.foo().first, 42);
533  BOOST_CHECK_EQUAL(tested.foo().second, 43);
534 
535  std::ostringstream os;
536  BOOST_CHECK_NO_THROW(os << tested);
537 }
538 
539 namespace {
540 class config6 : public jb::config_object {
541 public:
542  config6()
543  : foo(desc("foo"), this)
544  , bar(desc("bar", "config0").help("not much help"), this)
545  , baz(desc("baz", "config0").help("not much help"), this) {
546  }
547 
549 
553 };
554 } // anonymous namespace
555 
556 /**
557  * @test Verify that config object works correctly with real files and
558  * an environment variable.
559  */
560 BOOST_AUTO_TEST_CASE(config_object_config_file_env) {
561  char const contents[] = R"""(# YAML overrides
562 :config0:
563  x: -1
564  y: -2
565  z: -3
566 foo: this is a long string
567 baz:
568  z: 4
569 )""";
570  namespace fs = boost::filesystem;
571  fs::path tmpdir = fs::temp_directory_path() / fs::unique_path();
572  BOOST_TEST_MESSAGE("creating unique tempdir at " << tmpdir);
573  BOOST_REQUIRE(fs::create_directories(tmpdir));
574  std::shared_ptr<int> delete_dir(new int(5), [tmpdir](int* x) {
575  delete x;
576  fs::remove_all(tmpdir);
577  });
578  std::string filename = "test.yml";
579  // ... create a file in the temporary directory with these
580  {
581  fs::path base = fs::path(jb::sysconfdir()).filename();
582  BOOST_REQUIRE(fs::create_directories(tmpdir / base));
583  fs::path fullpath = tmpdir / base / filename;
584  BOOST_TEST_MESSAGE("writing contents to " << fullpath.string());
585  std::ofstream os(fullpath.string());
586  os << contents;
587  }
588 
589  // ... setup the environment variable to the test directory ...
590  (void)setenv("TEST_ROOT", tmpdir.string().c_str(), true);
591  char argv0[] = "binary";
592  char argv1[] = "--bar.x=42";
593  char argv2[] = "--baz.y=24";
594  char* argv[] = {argv0, argv1, argv2, nullptr};
595  int argc = sizeof(argv) / sizeof(argv[0]) - 1;
596  config6 tested;
597  tested.load_overrides(argc, argv, filename, "TEST_ROOT");
598  BOOST_CHECK_EQUAL(tested.foo(), "this is a long string");
599  BOOST_CHECK_EQUAL(tested.bar(), make_config0(42, -2, -3));
600  BOOST_CHECK_EQUAL(tested.baz(), make_config0(-1, 24, 4));
601 }
602 
603 /**
604  * @test Verify that config object works correctly with a missing file
605  * and the environment variable.
606  */
607 BOOST_AUTO_TEST_CASE(config_object_config_file_missing_with_env) {
608  namespace fs = boost::filesystem;
609  fs::path tmpdir = fs::temp_directory_path() / fs::unique_path();
610  BOOST_TEST_MESSAGE("creating unique tempdir at " << tmpdir);
611  BOOST_REQUIRE(fs::create_directories(tmpdir));
612  std::shared_ptr<int> delete_dir(new int(5), [tmpdir](int* x) {
613  delete x;
614  fs::remove_all(tmpdir);
615  });
616  std::string filename = "test.yml";
617 
618  // ... setup the environment variable to the test directory ...
619  (void)setenv("TEST_ROOT", tmpdir.string().c_str(), true);
620  char argv0[] = "binary";
621  char argv1[] = "--bar.x=42";
622  char argv2[] = "--baz.y=24";
623  char* argv[] = {argv0, argv1, argv2, nullptr};
624  int argc = sizeof(argv) / sizeof(argv[0]) - 1;
625  config6 tested;
626  tested.load_overrides(argc, argv, filename, "TEST_ROOT");
627  BOOST_CHECK_EQUAL(tested.foo(), "");
628  BOOST_CHECK_EQUAL(tested.bar(), make_config0(42, 0, 0));
629  BOOST_CHECK_EQUAL(tested.baz(), make_config0(0, 24, 0));
630 }
631 
632 /**
633  * @test Verify that config object works correctly with real files and
634  * the default environment variable.
635  */
636 BOOST_AUTO_TEST_CASE(config_object_config_file) {
637  char const contents[] = R"""(# YAML overrides
638 :config0:
639  x: -1
640  y: -2
641  z: -3
642 foo: this is a long string
643 baz:
644  z: 4
645 )""";
646  namespace fs = boost::filesystem;
647  fs::path tmpdir = fs::temp_directory_path() / fs::unique_path();
648  BOOST_TEST_MESSAGE("creating unique tempdir at " << tmpdir);
649  BOOST_REQUIRE(fs::create_directories(tmpdir));
650  std::shared_ptr<int> delete_dir(new int(5), [tmpdir](int* x) {
651  delete x;
652  fs::remove_all(tmpdir);
653  });
654  std::string filename = "test.yml";
655  // ... create a file in the temporary directory with these
656  {
657  fs::path base = fs::path(jb::sysconfdir()).filename();
658  BOOST_REQUIRE(fs::create_directories(tmpdir / base));
659  fs::path fullpath = tmpdir / base / filename;
660  BOOST_TEST_MESSAGE("writing contents to " << fullpath.string());
661  std::ofstream os(fullpath.string());
662  os << contents;
663  }
664 
665  // ... setup the environment variable to the test directory ...
666  (void)setenv("JAYBEAMS_ROOT", tmpdir.string().c_str(), true);
667  char argv0[] = "binary";
668  char argv1[] = "--bar.x=42";
669  char argv2[] = "--baz.y=24";
670  char* argv[] = {argv0, argv1, argv2, nullptr};
671  int argc = sizeof(argv) / sizeof(argv[0]) - 1;
672  config6 tested;
673  tested.load_overrides(argc, argv, filename);
674  BOOST_CHECK_EQUAL(tested.foo(), "this is a long string");
675  BOOST_CHECK_EQUAL(tested.bar(), make_config0(42, -2, -3));
676  BOOST_CHECK_EQUAL(tested.baz(), make_config0(-1, 24, 4));
677 }
678 
679 /**
680  * @test Verify that config object works correctly when the real file
681  * is not found using the default environment variable.
682  */
683 BOOST_AUTO_TEST_CASE(config_object_config_file_missing) {
684  std::string filename("missing-config-file.bad.bad.bad.yml");
685 
686  char argv0[] = "binary";
687  char argv1[] = "--foo=this is a long string";
688  char argv2[] = "--baz.y=24";
689  char* argv[] = {argv0, argv1, argv2, nullptr};
690  int argc = sizeof(argv) / sizeof(argv[0]) - 1;
691  config6 tested;
692  tested.load_overrides(argc, argv, filename);
693  BOOST_CHECK_EQUAL(tested.foo(), "this is a long string");
694  BOOST_CHECK_EQUAL(tested.bar(), make_config0(0, 0, 0));
695  BOOST_CHECK_EQUAL(tested.baz(), make_config0(0, 24, 0));
696 }
697 
698 namespace {
699 class config7 : public jb::config_object {
700 public:
701  config7()
702  : foo(desc("foo"), this)
703  , bar(desc("bar").help("not much help").positional(), this)
704  , baz(desc("baz").help("not much help").positional(), this) {
705  }
706 
708 
712 };
713 } // anonymous namespace
714 
715 /**
716  * @test Verify that config object works correctly with real files and
717  * an environment variable.
718  */
719 BOOST_AUTO_TEST_CASE(config_object_positional) {
720  std::istringstream is("");
721  char argv0[] = "binary";
722  char argv1[] = "should-be-bar";
723  char argv2[] = "should-be-baz";
724  char argv3[] = "--foo=another";
725  char* argv[] = {argv0, argv1, argv2, argv3, nullptr};
726  int argc = sizeof(argv) / sizeof(argv[0]) - 1;
727  config7 tested;
728  tested.load_overrides(argc, argv, is);
729  BOOST_CHECK_EQUAL(tested.foo(), "another");
730  BOOST_CHECK_EQUAL(tested.bar(), "should-be-bar");
731  BOOST_CHECK_EQUAL(tested.baz(), "should-be-baz");
732 }
733 
734 /**
735  * @test Complete coverage for jb::usage
736  */
737 BOOST_AUTO_TEST_CASE(usage_coverage) {
738  jb::usage a("foo", 0);
739  jb::usage b(std::string("foo"), 0);
740 
741  BOOST_CHECK_EQUAL(a.exit_status(), b.exit_status());
742  BOOST_CHECK_EQUAL(a.what(), b.what());
743 }
bool operator==(book_update const &a, book_update const &b)
Base class for all configuration objects.
std::ostream & operator<<(std::ostream &os, as_hhmmssu const &x)
Format as_hhmmssu into an iostream.
Definition: as_hhmmss.cpp:8
int exit_status() const
Definition: usage.hpp:21
BOOST_AUTO_TEST_CASE(config_attribute_simple)
#define config_object_constructors(NAME)
Helper class to easily define configuration attributes.
A simple class to communicate the result of parsing the options.
Definition: usage.hpp:11
char const * sysconfdir()
Return the system configuration directory.