1 // Copyright 2011 Google Inc.
2 // All rights reserved.
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 // * Neither the name of Google Inc. nor the names of its contributors
14 // may be used to endorse or promote products derived from this software
15 // without specific prior written permission.
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include "cli/config.hpp"
31 #include <atf-c++.hpp>
33 #include "engine/config.hpp"
34 #include "engine/exceptions.hpp"
35 #include "utils/env.hpp"
36 #include "utils/format/macros.hpp"
37 #include "utils/fs/operations.hpp"
38 #include "utils/fs/path.hpp"
40 namespace cmdline
= utils::cmdline
;
41 namespace config
= utils::config
;
42 namespace fs
= utils::fs
;
48 /// Creates a configuration file for testing purposes.
50 /// To ensure that the loaded file is the one created by this function, use
51 /// validate_mock_config().
53 /// \param name The name of the configuration file to create.
54 /// \param cookie The magic value to set in the configuration file, or NULL if a
55 /// broken configuration file is desired.
57 create_mock_config(const char* name
, const char* cookie
)
60 atf::utils::create_file(
63 "test_suites.suite.magic_value = '%s'\n") % cookie
);
65 atf::utils::create_file(name
, "syntax(200)\n");
70 /// Creates an invalid system configuration.
72 /// \param cookie The magic value to set in the configuration file, or NULL if a
73 /// broken configuration file is desired.
75 mock_system_config(const char* cookie
)
77 fs::mkdir(fs::path("system-dir"), 0755);
78 utils::setenv("KYUA_CONFDIR", (fs::current_path() / "system-dir").str());
79 create_mock_config("system-dir/kyua.conf", cookie
);
83 /// Creates an invalid user configuration.
85 /// \param cookie The magic value to set in the configuration file, or NULL if a
86 /// broken configuration file is desired.
88 mock_user_config(const char* cookie
)
90 fs::mkdir(fs::path("user-dir"), 0755);
91 fs::mkdir(fs::path("user-dir/.kyua"), 0755);
92 utils::setenv("HOME", (fs::current_path() / "user-dir").str());
93 create_mock_config("user-dir/.kyua/kyua.conf", cookie
);
97 /// Ensures that a loaded configuration was created with create_mock_config().
99 /// \param user_config The configuration to validate.
100 /// \param cookie The magic value to expect in the configuration file.
102 validate_mock_config(const config::tree
& user_config
, const char* cookie
)
104 const config::properties_map
& properties
= user_config
.all_properties(
105 "test_suites.suite", true);
106 const config::properties_map::const_iterator iter
=
107 properties
.find("magic_value");
108 ATF_REQUIRE(iter
!= properties
.end());
109 ATF_REQUIRE_EQ(cookie
, (*iter
).second
);
113 /// Ensures that two configuration trees are equal.
115 /// \param exp_tree The expected configuration tree.
116 /// \param actual_tree The configuration tree being validated against exp_tree.
118 require_eq(const config::tree
& exp_tree
, const config::tree
& actual_tree
)
120 ATF_REQUIRE(exp_tree
.all_properties() == actual_tree
.all_properties());
124 } // anonymous namespace
127 ATF_TEST_CASE_WITHOUT_HEAD(load_config__none
);
128 ATF_TEST_CASE_BODY(load_config__none
)
130 utils::setenv("KYUA_CONFDIR", "/the/system/does/not/exist");
131 utils::setenv("HOME", "/the/user/does/not/exist");
133 std::map
< std::string
, std::vector
< std::string
> > options
;
134 options
["config"].push_back(cli::config_option
.default_value());
135 const cmdline::parsed_cmdline
mock_cmdline(options
, cmdline::args_vector());
137 require_eq(engine::default_config(),
138 cli::load_config(mock_cmdline
, true));
142 ATF_TEST_CASE_WITHOUT_HEAD(load_config__explicit__ok
);
143 ATF_TEST_CASE_BODY(load_config__explicit__ok
)
145 mock_system_config(NULL
);
146 mock_user_config(NULL
);
148 create_mock_config("test-file", "hello");
150 std::map
< std::string
, std::vector
< std::string
> > options
;
151 options
["config"].push_back("test-file");
152 const cmdline::parsed_cmdline
mock_cmdline(options
, cmdline::args_vector());
154 const config::tree user_config
= cli::load_config(mock_cmdline
, true);
155 validate_mock_config(user_config
, "hello");
159 ATF_TEST_CASE_WITHOUT_HEAD(load_config__explicit__disable
);
160 ATF_TEST_CASE_BODY(load_config__explicit__disable
)
162 mock_system_config(NULL
);
163 mock_user_config(NULL
);
165 std::map
< std::string
, std::vector
< std::string
> > options
;
166 options
["config"].push_back("none");
167 const cmdline::parsed_cmdline
mock_cmdline(options
, cmdline::args_vector());
169 require_eq(engine::default_config(),
170 cli::load_config(mock_cmdline
, true));
174 ATF_TEST_CASE_WITHOUT_HEAD(load_config__explicit__fail
);
175 ATF_TEST_CASE_BODY(load_config__explicit__fail
)
177 mock_system_config("ok1");
178 mock_user_config("ok2");
180 create_mock_config("test-file", NULL
);
182 std::map
< std::string
, std::vector
< std::string
> > options
;
183 options
["config"].push_back("test-file");
184 const cmdline::parsed_cmdline
mock_cmdline(options
, cmdline::args_vector());
186 ATF_REQUIRE_THROW_RE(engine::error
, "200",
187 cli::load_config(mock_cmdline
, true));
189 const config::tree config
= cli::load_config(mock_cmdline
, false);
190 require_eq(engine::default_config(), config
);
194 ATF_TEST_CASE_WITHOUT_HEAD(load_config__user__ok
);
195 ATF_TEST_CASE_BODY(load_config__user__ok
)
197 mock_system_config(NULL
);
198 mock_user_config("I am the user config");
200 std::map
< std::string
, std::vector
< std::string
> > options
;
201 options
["config"].push_back(cli::config_option
.default_value());
202 const cmdline::parsed_cmdline
mock_cmdline(options
, cmdline::args_vector());
204 const config::tree user_config
= cli::load_config(mock_cmdline
, true);
205 validate_mock_config(user_config
, "I am the user config");
209 ATF_TEST_CASE_WITHOUT_HEAD(load_config__user__fail
);
210 ATF_TEST_CASE_BODY(load_config__user__fail
)
212 mock_system_config("valid");
213 mock_user_config(NULL
);
215 std::map
< std::string
, std::vector
< std::string
> > options
;
216 options
["config"].push_back(cli::config_option
.default_value());
217 const cmdline::parsed_cmdline
mock_cmdline(options
, cmdline::args_vector());
219 ATF_REQUIRE_THROW_RE(engine::error
, "200",
220 cli::load_config(mock_cmdline
, true));
222 const config::tree config
= cli::load_config(mock_cmdline
, false);
223 require_eq(engine::default_config(), config
);
227 ATF_TEST_CASE_WITHOUT_HEAD(load_config__user__bad_home
);
228 ATF_TEST_CASE_BODY(load_config__user__bad_home
)
230 mock_system_config("Fallback system config");
231 utils::setenv("HOME", "");
233 std::map
< std::string
, std::vector
< std::string
> > options
;
234 options
["config"].push_back(cli::config_option
.default_value());
235 const cmdline::parsed_cmdline
mock_cmdline(options
, cmdline::args_vector());
237 const config::tree user_config
= cli::load_config(mock_cmdline
, true);
238 validate_mock_config(user_config
, "Fallback system config");
242 ATF_TEST_CASE_WITHOUT_HEAD(load_config__system__ok
);
243 ATF_TEST_CASE_BODY(load_config__system__ok
)
245 mock_system_config("I am the system config");
246 utils::setenv("HOME", "/the/user/does/not/exist");
248 std::map
< std::string
, std::vector
< std::string
> > options
;
249 options
["config"].push_back(cli::config_option
.default_value());
250 const cmdline::parsed_cmdline
mock_cmdline(options
, cmdline::args_vector());
252 const config::tree user_config
= cli::load_config(mock_cmdline
, true);
253 validate_mock_config(user_config
, "I am the system config");
257 ATF_TEST_CASE_WITHOUT_HEAD(load_config__system__fail
);
258 ATF_TEST_CASE_BODY(load_config__system__fail
)
260 mock_system_config(NULL
);
261 utils::setenv("HOME", "/the/user/does/not/exist");
263 std::map
< std::string
, std::vector
< std::string
> > options
;
264 options
["config"].push_back(cli::config_option
.default_value());
265 const cmdline::parsed_cmdline
mock_cmdline(options
, cmdline::args_vector());
267 ATF_REQUIRE_THROW_RE(engine::error
, "200",
268 cli::load_config(mock_cmdline
, true));
270 const config::tree config
= cli::load_config(mock_cmdline
, false);
271 require_eq(engine::default_config(), config
);
275 ATF_TEST_CASE_WITHOUT_HEAD(load_config__overrides__no
);
276 ATF_TEST_CASE_BODY(load_config__overrides__no
)
278 utils::setenv("KYUA_CONFDIR", fs::current_path().str());
280 std::map
< std::string
, std::vector
< std::string
> > options
;
281 options
["config"].push_back(cli::config_option
.default_value());
282 options
["variable"].push_back("architecture=1");
283 options
["variable"].push_back("platform=2");
284 const cmdline::parsed_cmdline
mock_cmdline(options
, cmdline::args_vector());
286 const config::tree user_config
= cli::load_config(mock_cmdline
, true);
288 user_config
.lookup
< config::string_node
>("architecture"));
290 user_config
.lookup
< config::string_node
>("platform"));
294 ATF_TEST_CASE_WITHOUT_HEAD(load_config__overrides__yes
);
295 ATF_TEST_CASE_BODY(load_config__overrides__yes
)
297 atf::utils::create_file(
300 "architecture = 'do not see me'\n"
301 "platform = 'see me'\n");
303 std::map
< std::string
, std::vector
< std::string
> > options
;
304 options
["config"].push_back("config");
305 options
["variable"].push_back("architecture=overriden");
306 const cmdline::parsed_cmdline
mock_cmdline(options
, cmdline::args_vector());
308 const config::tree user_config
= cli::load_config(mock_cmdline
, true);
309 ATF_REQUIRE_EQ("overriden",
310 user_config
.lookup
< config::string_node
>("architecture"));
311 ATF_REQUIRE_EQ("see me",
312 user_config
.lookup
< config::string_node
>("platform"));
316 ATF_TEST_CASE_WITHOUT_HEAD(load_config__overrides__fail
);
317 ATF_TEST_CASE_BODY(load_config__overrides__fail
)
319 utils::setenv("KYUA_CONFDIR", fs::current_path().str());
321 std::map
< std::string
, std::vector
< std::string
> > options
;
322 options
["config"].push_back(cli::config_option
.default_value());
323 options
["variable"].push_back(".a=d");
324 const cmdline::parsed_cmdline
mock_cmdline(options
, cmdline::args_vector());
326 ATF_REQUIRE_THROW_RE(engine::error
, "Empty component in key.*'\\.a'",
327 cli::load_config(mock_cmdline
, true));
329 const config::tree config
= cli::load_config(mock_cmdline
, false);
330 require_eq(engine::default_config(), config
);
334 ATF_INIT_TEST_CASES(tcs
)
336 ATF_ADD_TEST_CASE(tcs
, load_config__none
);
337 ATF_ADD_TEST_CASE(tcs
, load_config__explicit__ok
);
338 ATF_ADD_TEST_CASE(tcs
, load_config__explicit__disable
);
339 ATF_ADD_TEST_CASE(tcs
, load_config__explicit__fail
);
340 ATF_ADD_TEST_CASE(tcs
, load_config__user__ok
);
341 ATF_ADD_TEST_CASE(tcs
, load_config__user__fail
);
342 ATF_ADD_TEST_CASE(tcs
, load_config__user__bad_home
);
343 ATF_ADD_TEST_CASE(tcs
, load_config__system__ok
);
344 ATF_ADD_TEST_CASE(tcs
, load_config__system__fail
);
345 ATF_ADD_TEST_CASE(tcs
, load_config__overrides__no
);
346 ATF_ADD_TEST_CASE(tcs
, load_config__overrides__yes
);
347 ATF_ADD_TEST_CASE(tcs
, load_config__overrides__fail
);