xref: /minix3/external/bsd/kyua-cli/dist/cli/config_test.cpp (revision 11be35a165022172ed3cea20f2b5df0307540b0e)
1 // Copyright 2011 Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
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.
16 //
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.
28 
29 #include "cli/config.hpp"
30 
31 #include <atf-c++.hpp>
32 
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"
39 
40 namespace cmdline = utils::cmdline;
41 namespace config = utils::config;
42 namespace fs = utils::fs;
43 
44 
45 namespace {
46 
47 
48 /// Creates a configuration file for testing purposes.
49 ///
50 /// To ensure that the loaded file is the one created by this function, use
51 /// validate_mock_config().
52 ///
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.
56 static void
create_mock_config(const char * name,const char * cookie)57 create_mock_config(const char* name, const char* cookie)
58 {
59     if (cookie != NULL) {
60         atf::utils::create_file(
61             name,
62             F("syntax(2)\n"
63               "test_suites.suite.magic_value = '%s'\n") % cookie);
64     } else {
65         atf::utils::create_file(name, "syntax(200)\n");
66     }
67 }
68 
69 
70 /// Creates an invalid system configuration.
71 ///
72 /// \param cookie The magic value to set in the configuration file, or NULL if a
73 ///     broken configuration file is desired.
74 static void
mock_system_config(const char * cookie)75 mock_system_config(const char* cookie)
76 {
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);
80 }
81 
82 
83 /// Creates an invalid user configuration.
84 ///
85 /// \param cookie The magic value to set in the configuration file, or NULL if a
86 ///     broken configuration file is desired.
87 static void
mock_user_config(const char * cookie)88 mock_user_config(const char* cookie)
89 {
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);
94 }
95 
96 
97 /// Ensures that a loaded configuration was created with create_mock_config().
98 ///
99 /// \param user_config The configuration to validate.
100 /// \param cookie The magic value to expect in the configuration file.
101 static void
validate_mock_config(const config::tree & user_config,const char * cookie)102 validate_mock_config(const config::tree& user_config, const char* cookie)
103 {
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);
110 }
111 
112 
113 /// Ensures that two configuration trees are equal.
114 ///
115 /// \param exp_tree The expected configuration tree.
116 /// \param actual_tree The configuration tree being validated against exp_tree.
117 static void
require_eq(const config::tree & exp_tree,const config::tree & actual_tree)118 require_eq(const config::tree& exp_tree, const config::tree& actual_tree)
119 {
120     ATF_REQUIRE(exp_tree.all_properties() == actual_tree.all_properties());
121 }
122 
123 
124 }  // anonymous namespace
125 
126 
127 ATF_TEST_CASE_WITHOUT_HEAD(load_config__none);
ATF_TEST_CASE_BODY(load_config__none)128 ATF_TEST_CASE_BODY(load_config__none)
129 {
130     utils::setenv("KYUA_CONFDIR", "/the/system/does/not/exist");
131     utils::setenv("HOME", "/the/user/does/not/exist");
132 
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());
136 
137     require_eq(engine::default_config(),
138                cli::load_config(mock_cmdline, true));
139 }
140 
141 
142 ATF_TEST_CASE_WITHOUT_HEAD(load_config__explicit__ok);
ATF_TEST_CASE_BODY(load_config__explicit__ok)143 ATF_TEST_CASE_BODY(load_config__explicit__ok)
144 {
145     mock_system_config(NULL);
146     mock_user_config(NULL);
147 
148     create_mock_config("test-file", "hello");
149 
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());
153 
154     const config::tree user_config = cli::load_config(mock_cmdline, true);
155     validate_mock_config(user_config, "hello");
156 }
157 
158 
159 ATF_TEST_CASE_WITHOUT_HEAD(load_config__explicit__disable);
ATF_TEST_CASE_BODY(load_config__explicit__disable)160 ATF_TEST_CASE_BODY(load_config__explicit__disable)
161 {
162     mock_system_config(NULL);
163     mock_user_config(NULL);
164 
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());
168 
169     require_eq(engine::default_config(),
170                cli::load_config(mock_cmdline, true));
171 }
172 
173 
174 ATF_TEST_CASE_WITHOUT_HEAD(load_config__explicit__fail);
ATF_TEST_CASE_BODY(load_config__explicit__fail)175 ATF_TEST_CASE_BODY(load_config__explicit__fail)
176 {
177     mock_system_config("ok1");
178     mock_user_config("ok2");
179 
180     create_mock_config("test-file", NULL);
181 
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());
185 
186     ATF_REQUIRE_THROW_RE(engine::error, "200",
187                          cli::load_config(mock_cmdline, true));
188 
189     const config::tree config = cli::load_config(mock_cmdline, false);
190     require_eq(engine::default_config(), config);
191 }
192 
193 
194 ATF_TEST_CASE_WITHOUT_HEAD(load_config__user__ok);
ATF_TEST_CASE_BODY(load_config__user__ok)195 ATF_TEST_CASE_BODY(load_config__user__ok)
196 {
197     mock_system_config(NULL);
198     mock_user_config("I am the user config");
199 
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());
203 
204     const config::tree user_config = cli::load_config(mock_cmdline, true);
205     validate_mock_config(user_config, "I am the user config");
206 }
207 
208 
209 ATF_TEST_CASE_WITHOUT_HEAD(load_config__user__fail);
ATF_TEST_CASE_BODY(load_config__user__fail)210 ATF_TEST_CASE_BODY(load_config__user__fail)
211 {
212     mock_system_config("valid");
213     mock_user_config(NULL);
214 
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());
218 
219     ATF_REQUIRE_THROW_RE(engine::error, "200",
220                          cli::load_config(mock_cmdline, true));
221 
222     const config::tree config = cli::load_config(mock_cmdline, false);
223     require_eq(engine::default_config(), config);
224 }
225 
226 
227 ATF_TEST_CASE_WITHOUT_HEAD(load_config__user__bad_home);
ATF_TEST_CASE_BODY(load_config__user__bad_home)228 ATF_TEST_CASE_BODY(load_config__user__bad_home)
229 {
230     mock_system_config("Fallback system config");
231     utils::setenv("HOME", "");
232 
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());
236 
237     const config::tree user_config = cli::load_config(mock_cmdline, true);
238     validate_mock_config(user_config, "Fallback system config");
239 }
240 
241 
242 ATF_TEST_CASE_WITHOUT_HEAD(load_config__system__ok);
ATF_TEST_CASE_BODY(load_config__system__ok)243 ATF_TEST_CASE_BODY(load_config__system__ok)
244 {
245     mock_system_config("I am the system config");
246     utils::setenv("HOME", "/the/user/does/not/exist");
247 
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());
251 
252     const config::tree user_config = cli::load_config(mock_cmdline, true);
253     validate_mock_config(user_config, "I am the system config");
254 }
255 
256 
257 ATF_TEST_CASE_WITHOUT_HEAD(load_config__system__fail);
ATF_TEST_CASE_BODY(load_config__system__fail)258 ATF_TEST_CASE_BODY(load_config__system__fail)
259 {
260     mock_system_config(NULL);
261     utils::setenv("HOME", "/the/user/does/not/exist");
262 
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());
266 
267     ATF_REQUIRE_THROW_RE(engine::error, "200",
268                          cli::load_config(mock_cmdline, true));
269 
270     const config::tree config = cli::load_config(mock_cmdline, false);
271     require_eq(engine::default_config(), config);
272 }
273 
274 
275 ATF_TEST_CASE_WITHOUT_HEAD(load_config__overrides__no);
ATF_TEST_CASE_BODY(load_config__overrides__no)276 ATF_TEST_CASE_BODY(load_config__overrides__no)
277 {
278     utils::setenv("KYUA_CONFDIR", fs::current_path().str());
279 
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());
285 
286     const config::tree user_config = cli::load_config(mock_cmdline, true);
287     ATF_REQUIRE_EQ("1",
288                    user_config.lookup< config::string_node >("architecture"));
289     ATF_REQUIRE_EQ("2",
290                    user_config.lookup< config::string_node >("platform"));
291 }
292 
293 
294 ATF_TEST_CASE_WITHOUT_HEAD(load_config__overrides__yes);
ATF_TEST_CASE_BODY(load_config__overrides__yes)295 ATF_TEST_CASE_BODY(load_config__overrides__yes)
296 {
297     atf::utils::create_file(
298         "config",
299         "syntax(2)\n"
300         "architecture = 'do not see me'\n"
301         "platform = 'see me'\n");
302 
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());
307 
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"));
313 }
314 
315 
316 ATF_TEST_CASE_WITHOUT_HEAD(load_config__overrides__fail);
ATF_TEST_CASE_BODY(load_config__overrides__fail)317 ATF_TEST_CASE_BODY(load_config__overrides__fail)
318 {
319     utils::setenv("KYUA_CONFDIR", fs::current_path().str());
320 
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());
325 
326     ATF_REQUIRE_THROW_RE(engine::error, "Empty component in key.*'\\.a'",
327                          cli::load_config(mock_cmdline, true));
328 
329     const config::tree config = cli::load_config(mock_cmdline, false);
330     require_eq(engine::default_config(), config);
331 }
332 
333 
ATF_INIT_TEST_CASES(tcs)334 ATF_INIT_TEST_CASES(tcs)
335 {
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);
348 }
349