JayBeams  0.1
Another project to have fun coding.
ut_config_files_location.cpp
Go to the documentation of this file.
1 #include <jb/gmock/init.hpp>
3 
4 #include <boost/filesystem/operations.hpp>
5 #include <boost/test/unit_test.hpp>
6 
7 namespace fs = boost::filesystem;
8 
9 /**
10  * Mocks and helper types used in the jb::config_files_location tests.
11  */
12 namespace {
13 struct trivial_getenv {
14  char const* operator()(char const*) {
15  return nullptr;
16  }
17 };
18 
19 struct trivial_validator {
20  bool operator()(fs::path const&) {
21  return true;
22  }
23 };
24 
26 } // anonymous namespace
27 
28 namespace boost {
29 namespace filesystem {
30 // Introduce help for GoogleMock library.
31 void PrintTo(path const& p, ::std::ostream* os) {
32  *os << p.string();
33 }
34 } // namespace filesystem
35 } // namespace boost
36 
37 /**
38  * @test Verify that the common constructors compile.
39  */
40 BOOST_AUTO_TEST_CASE(config_files_location_constructors) {
41  trivial_getenv getenv;
42 
43  trivial t0(fs::path("/foo/var/baz/program"), "TEST_ROOT", getenv);
44  BOOST_CHECK(not t0.search_path().empty());
45 
46  trivial t1(fs::path("/foo/var/baz/program"), "TEST_ROOT");
47  BOOST_CHECK(not t1.search_path().empty());
48 
49  trivial t3(fs::path("/foo/var/baz/program"), getenv);
50  BOOST_CHECK(not t3.search_path().empty());
51 
52  trivial t4(fs::path("/foo/var/baz/program"));
53  BOOST_CHECK(not t4.search_path().empty());
54 
55  trivial t5("/foo/var/baz/program", "TEST_ROOT", getenv);
56  BOOST_CHECK(not t5.search_path().empty());
57 
58  trivial t6("/foo/var/baz/program", "TEST_ROOT");
59  BOOST_CHECK(not t6.search_path().empty());
60 
61  trivial t7("/foo/var/baz/program", getenv);
62  BOOST_CHECK(not t7.search_path().empty());
63 
64  trivial t8("/foo/var/baz/program");
65  BOOST_CHECK(not t8.search_path().empty());
66 }
67 
68 namespace {
69 template <typename Functor>
70 struct shared_functor {
71  shared_functor()
72  : mock(new Functor) {
73  }
74 
75  template <typename... T>
76  auto operator()(T&&... a) -> decltype(Functor().exec(std::forward<T>(a)...)) {
77  return mock->exec(std::forward<T>(a)...);
78  }
79 
80  void reset() {
81  mock = std::shared_ptr<Functor>(new Functor);
82  }
83 
84  std::shared_ptr<Functor> mock;
85 };
86 
87 /// Mock implementation for getenv()
88 struct mock_getenv_f {
89  MOCK_CONST_METHOD1(exec, char const*(char const*));
90 };
91 using mock_getenv = shared_functor<mock_getenv_f>;
92 
93 /// Mock implementation for the validator()
94 struct mock_validator_f {
95  MOCK_CONST_METHOD1(exec, bool(fs::path const&));
96 };
97 using mock_validator = shared_functor<mock_validator_f>;
98 
99 /// The object under test
101 
102 /// Configure mocks for most tests
103 void set_mocks(
104  mock_getenv& getenv, mock_validator& validator, char const* test_root,
105  char const* jaybeams_root, bool valid) {
106  getenv.reset();
107  using namespace ::testing;
108  EXPECT_CALL(*getenv.mock, exec(Truly([](auto arg) {
109  return std::string("TEST_ROOT") == arg;
110  }))).WillRepeatedly(Return(test_root));
111  EXPECT_CALL(*getenv.mock, exec(Truly([](auto arg) {
112  return std::string("JAYBEAMS_ROOT") == arg;
113  }))).WillRepeatedly(Return(jaybeams_root));
114 
115  validator.reset();
116  EXPECT_CALL(*validator.mock, exec(_)).WillRepeatedly(Return(valid));
117 }
118 } // anonymous namespace
119 
120 /**
121  * @test Verify that a simple config_files_locations<> works as expected.
122  */
123 BOOST_AUTO_TEST_CASE(config_files_location_program_root) {
124  mock_getenv getenv;
125  mock_validator validator;
126  set_mocks(getenv, validator, "/test/path", "/install/path", true);
127 
128  fs::path programdir = fs::path("/foo/var/baz");
129  mocked t(programdir / "program", "TEST_ROOT", getenv);
130 
131  fs::path etc = fs::path(jb::sysconfdir()).filename();
132  std::vector<fs::path> expected({"/test/path" / etc, "/install/path" / etc,
133  jb::sysconfdir(), programdir});
134  using namespace ::testing;
135  BOOST_CHECK_EQUAL_COLLECTIONS(
136  t.search_path().begin(), t.search_path().end(), expected.begin(),
137  expected.end());
138 }
139 
140 /**
141  * @test Verify that a simple config_files_locations<> works without a
142  * program root.
143  */
144 BOOST_AUTO_TEST_CASE(config_files_location_no_program_root) {
145  mock_getenv getenv;
146  mock_validator validator;
147  set_mocks(getenv, validator, "/test/path", "/install/path", true);
148 
149  fs::path programdir = fs::path("/foo/var/baz");
150  mocked t(programdir / "program", getenv);
151 
152  fs::path etc = fs::path(jb::sysconfdir()).filename();
153  std::vector<fs::path> expected(
154  {"/install/path" / etc, jb::sysconfdir(), programdir});
155  using namespace ::testing;
156  BOOST_CHECK_EQUAL_COLLECTIONS(
157  t.search_path().begin(), t.search_path().end(), expected.begin(),
158  expected.end());
159 }
160 
161 /**
162  * @test Verify that a simple config_files_locations<> works with some
163  * variables undefined.
164  */
165 BOOST_AUTO_TEST_CASE(config_files_location_undefined_undef_test_root) {
166  mock_getenv getenv;
167  mock_validator validator;
168  set_mocks(getenv, validator, nullptr, "/install/path", true);
169 
170  fs::path programdir = fs::path("/foo/var/baz");
171  mocked t(programdir / "program", "TEST_ROOT", getenv);
172 
173  fs::path etc = fs::path(jb::sysconfdir()).filename();
174  std::vector<fs::path> expected(
175  {"/install/path" / etc, jb::sysconfdir(), programdir});
176  using namespace ::testing;
177  BOOST_CHECK_EQUAL_COLLECTIONS(
178  t.search_path().begin(), t.search_path().end(), expected.begin(),
179  expected.end());
180 }
181 
182 /**
183  * @test Verify that a simple config_files_locations<> works with some
184  * variables undefined.
185  */
186 BOOST_AUTO_TEST_CASE(config_files_location_undefined_undef_system_root) {
187  mock_getenv getenv;
188  mock_validator validator;
189  set_mocks(getenv, validator, "/test/path", nullptr, true);
190 
191  fs::path programdir = fs::path("/foo/var/baz");
192  mocked t(programdir / "program", "TEST_ROOT", getenv);
193 
194  fs::path etc = fs::path(jb::sysconfdir()).filename();
195  std::vector<fs::path> expected(
196  {"/test/path" / etc, jb::sysconfdir(), programdir});
197  using namespace ::testing;
198  BOOST_CHECK_EQUAL_COLLECTIONS(
199  t.search_path().begin(), t.search_path().end(), expected.begin(),
200  expected.end());
201 }
202 
203 /**
204  * @test Verify that a simple config with a valid path for the binary works.
205  */
206 BOOST_AUTO_TEST_CASE(config_files_location_installed_binary) {
207  mock_getenv getenv;
208  mock_validator validator;
209  set_mocks(getenv, validator, "/test/path", "/install/path", true);
210 
211  fs::path etc = fs::path(jb::sysconfdir()).filename();
212 
213  fs::path install_path = fs::path("/install") / jb::bindir();
214  fs::path program = install_path / "program";
215 
216  mocked t(program, "TEST_ROOT", getenv);
217 
218  std::vector<fs::path> expected({"/test/path" / etc, "/install/path" / etc,
219  jb::sysconfdir(),
220  program.parent_path().parent_path() / etc});
221  using namespace ::testing;
222  BOOST_CHECK_EQUAL_COLLECTIONS(
223  t.search_path().begin(), t.search_path().end(), expected.begin(),
224  expected.end());
225 }
226 
227 /**
228  * @test Verify that a simple config_files_locations<> works when the
229  * program is not path.
230  */
231 BOOST_AUTO_TEST_CASE(config_files_location_no_program_path) {
232  mock_getenv getenv;
233  mock_validator validator;
234  set_mocks(getenv, validator, "/test/path", "/install/path", true);
235 
236  mocked t("program", getenv);
237 
238  fs::path etc = fs::path(jb::sysconfdir()).filename();
239  std::vector<fs::path> expected({"/install/path" / etc, jb::sysconfdir()});
240  using namespace ::testing;
241  BOOST_CHECK_EQUAL_COLLECTIONS(
242  t.search_path().begin(), t.search_path().end(), expected.begin(),
243  expected.end());
244 }
245 
246 /**
247  * @test Verify that the search algorithm works as expected.
248  */
249 BOOST_AUTO_TEST_CASE(config_files_location_find) {
250  mock_getenv getenv;
251  mock_validator validator;
252  set_mocks(getenv, validator, "/test/path", "/install/path", true);
253 
254  fs::path etc = fs::path(jb::sysconfdir()).filename();
255 
256  fs::path install_path = fs::path("/install") / jb::bindir();
257  fs::path program = install_path / "program";
258 
259  mocked t(program, "TEST_ROOT", getenv);
260 
261  // Fist check that the right exception is raised if no file can be found ...
262  std::string filename = "test.yaml";
263  using namespace ::testing;
264  validator.reset();
265  EXPECT_CALL(*validator.mock, exec(_)).WillRepeatedly(Return(false));
266 
267  BOOST_CHECK_THROW(
268  t.find_configuration_file(filename, validator), std::runtime_error);
269 
270  // ... then check that each path is checked in order ...
271  validator.reset();
272  for (auto path : t.search_path()) {
273  EXPECT_CALL(*validator.mock, exec(_))
274  .WillRepeatedly(Invoke(
275  [path, filename](auto arg) { return path / filename == arg; }));
276  auto full = path / filename;
277  BOOST_CHECK_EQUAL(full, t.find_configuration_file(filename, validator));
278  validator.reset();
279  }
280 }
BOOST_AUTO_TEST_CASE(config_files_location_constructors)
char const * bindir()
Return the binary installation directory directory.
void PrintTo(path const &p, ::std::ostream *os)
Initialize GMock to work with Boost.Test.
char const * sysconfdir()
Return the system configuration directory.
Compute the directories where a configuration file can be found.