1 1.1 jmmv // Copyright 2011 Google Inc. 2 1.1 jmmv // All rights reserved. 3 1.1 jmmv // 4 1.1 jmmv // Redistribution and use in source and binary forms, with or without 5 1.1 jmmv // modification, are permitted provided that the following conditions are 6 1.1 jmmv // met: 7 1.1 jmmv // 8 1.1 jmmv // * Redistributions of source code must retain the above copyright 9 1.1 jmmv // notice, this list of conditions and the following disclaimer. 10 1.1 jmmv // * Redistributions in binary form must reproduce the above copyright 11 1.1 jmmv // notice, this list of conditions and the following disclaimer in the 12 1.1 jmmv // documentation and/or other materials provided with the distribution. 13 1.1 jmmv // * Neither the name of Google Inc. nor the names of its contributors 14 1.1 jmmv // may be used to endorse or promote products derived from this software 15 1.1 jmmv // without specific prior written permission. 16 1.1 jmmv // 17 1.1 jmmv // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 1.1 jmmv // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 1.1 jmmv // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 1.1 jmmv // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 1.1 jmmv // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 1.1 jmmv // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 1.1 jmmv // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 1.1 jmmv // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 1.1 jmmv // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 1.1 jmmv // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 1.1 jmmv // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 1.1 jmmv 29 1.1 jmmv #include "cli/config.hpp" 30 1.1 jmmv 31 1.1 jmmv #include <atf-c++.hpp> 32 1.1 jmmv 33 1.1 jmmv #include "engine/config.hpp" 34 1.1 jmmv #include "engine/exceptions.hpp" 35 1.1 jmmv #include "utils/env.hpp" 36 1.1 jmmv #include "utils/format/macros.hpp" 37 1.1 jmmv #include "utils/fs/operations.hpp" 38 1.1 jmmv #include "utils/fs/path.hpp" 39 1.1 jmmv 40 1.1 jmmv namespace cmdline = utils::cmdline; 41 1.1 jmmv namespace config = utils::config; 42 1.1 jmmv namespace fs = utils::fs; 43 1.1 jmmv 44 1.1 jmmv 45 1.1 jmmv namespace { 46 1.1 jmmv 47 1.1 jmmv 48 1.1 jmmv /// Creates a configuration file for testing purposes. 49 1.1 jmmv /// 50 1.1 jmmv /// To ensure that the loaded file is the one created by this function, use 51 1.1 jmmv /// validate_mock_config(). 52 1.1 jmmv /// 53 1.1 jmmv /// \param name The name of the configuration file to create. 54 1.1 jmmv /// \param cookie The magic value to set in the configuration file, or NULL if a 55 1.1 jmmv /// broken configuration file is desired. 56 1.1 jmmv static void 57 1.1 jmmv create_mock_config(const char* name, const char* cookie) 58 1.1 jmmv { 59 1.1 jmmv if (cookie != NULL) { 60 1.1 jmmv atf::utils::create_file( 61 1.1 jmmv name, 62 1.1 jmmv F("syntax(2)\n" 63 1.1 jmmv "test_suites.suite.magic_value = '%s'\n") % cookie); 64 1.1 jmmv } else { 65 1.1 jmmv atf::utils::create_file(name, "syntax(200)\n"); 66 1.1 jmmv } 67 1.1 jmmv } 68 1.1 jmmv 69 1.1 jmmv 70 1.1 jmmv /// Creates an invalid system configuration. 71 1.1 jmmv /// 72 1.1 jmmv /// \param cookie The magic value to set in the configuration file, or NULL if a 73 1.1 jmmv /// broken configuration file is desired. 74 1.1 jmmv static void 75 1.1 jmmv mock_system_config(const char* cookie) 76 1.1 jmmv { 77 1.1 jmmv fs::mkdir(fs::path("system-dir"), 0755); 78 1.1 jmmv utils::setenv("KYUA_CONFDIR", (fs::current_path() / "system-dir").str()); 79 1.1 jmmv create_mock_config("system-dir/kyua.conf", cookie); 80 1.1 jmmv } 81 1.1 jmmv 82 1.1 jmmv 83 1.1 jmmv /// Creates an invalid user configuration. 84 1.1 jmmv /// 85 1.1 jmmv /// \param cookie The magic value to set in the configuration file, or NULL if a 86 1.1 jmmv /// broken configuration file is desired. 87 1.1 jmmv static void 88 1.1 jmmv mock_user_config(const char* cookie) 89 1.1 jmmv { 90 1.1 jmmv fs::mkdir(fs::path("user-dir"), 0755); 91 1.1 jmmv fs::mkdir(fs::path("user-dir/.kyua"), 0755); 92 1.1 jmmv utils::setenv("HOME", (fs::current_path() / "user-dir").str()); 93 1.1 jmmv create_mock_config("user-dir/.kyua/kyua.conf", cookie); 94 1.1 jmmv } 95 1.1 jmmv 96 1.1 jmmv 97 1.1 jmmv /// Ensures that a loaded configuration was created with create_mock_config(). 98 1.1 jmmv /// 99 1.1 jmmv /// \param user_config The configuration to validate. 100 1.1 jmmv /// \param cookie The magic value to expect in the configuration file. 101 1.1 jmmv static void 102 1.1 jmmv validate_mock_config(const config::tree& user_config, const char* cookie) 103 1.1 jmmv { 104 1.1 jmmv const config::properties_map& properties = user_config.all_properties( 105 1.1 jmmv "test_suites.suite", true); 106 1.1 jmmv const config::properties_map::const_iterator iter = 107 1.1 jmmv properties.find("magic_value"); 108 1.1 jmmv ATF_REQUIRE(iter != properties.end()); 109 1.1 jmmv ATF_REQUIRE_EQ(cookie, (*iter).second); 110 1.1 jmmv } 111 1.1 jmmv 112 1.1 jmmv 113 1.1 jmmv /// Ensures that two configuration trees are equal. 114 1.1 jmmv /// 115 1.1 jmmv /// \param exp_tree The expected configuration tree. 116 1.1 jmmv /// \param actual_tree The configuration tree being validated against exp_tree. 117 1.1 jmmv static void 118 1.1 jmmv require_eq(const config::tree& exp_tree, const config::tree& actual_tree) 119 1.1 jmmv { 120 1.1 jmmv ATF_REQUIRE(exp_tree.all_properties() == actual_tree.all_properties()); 121 1.1 jmmv } 122 1.1 jmmv 123 1.1 jmmv 124 1.1 jmmv } // anonymous namespace 125 1.1 jmmv 126 1.1 jmmv 127 1.1 jmmv ATF_TEST_CASE_WITHOUT_HEAD(load_config__none); 128 1.1 jmmv ATF_TEST_CASE_BODY(load_config__none) 129 1.1 jmmv { 130 1.1 jmmv utils::setenv("KYUA_CONFDIR", "/the/system/does/not/exist"); 131 1.1 jmmv utils::setenv("HOME", "/the/user/does/not/exist"); 132 1.1 jmmv 133 1.1 jmmv std::map< std::string, std::vector< std::string > > options; 134 1.1 jmmv options["config"].push_back(cli::config_option.default_value()); 135 1.1 jmmv const cmdline::parsed_cmdline mock_cmdline(options, cmdline::args_vector()); 136 1.1 jmmv 137 1.1 jmmv require_eq(engine::default_config(), 138 1.1 jmmv cli::load_config(mock_cmdline, true)); 139 1.1 jmmv } 140 1.1 jmmv 141 1.1 jmmv 142 1.1 jmmv ATF_TEST_CASE_WITHOUT_HEAD(load_config__explicit__ok); 143 1.1 jmmv ATF_TEST_CASE_BODY(load_config__explicit__ok) 144 1.1 jmmv { 145 1.1 jmmv mock_system_config(NULL); 146 1.1 jmmv mock_user_config(NULL); 147 1.1 jmmv 148 1.1 jmmv create_mock_config("test-file", "hello"); 149 1.1 jmmv 150 1.1 jmmv std::map< std::string, std::vector< std::string > > options; 151 1.1 jmmv options["config"].push_back("test-file"); 152 1.1 jmmv const cmdline::parsed_cmdline mock_cmdline(options, cmdline::args_vector()); 153 1.1 jmmv 154 1.1 jmmv const config::tree user_config = cli::load_config(mock_cmdline, true); 155 1.1 jmmv validate_mock_config(user_config, "hello"); 156 1.1 jmmv } 157 1.1 jmmv 158 1.1 jmmv 159 1.1 jmmv ATF_TEST_CASE_WITHOUT_HEAD(load_config__explicit__disable); 160 1.1 jmmv ATF_TEST_CASE_BODY(load_config__explicit__disable) 161 1.1 jmmv { 162 1.1 jmmv mock_system_config(NULL); 163 1.1 jmmv mock_user_config(NULL); 164 1.1 jmmv 165 1.1 jmmv std::map< std::string, std::vector< std::string > > options; 166 1.1 jmmv options["config"].push_back("none"); 167 1.1 jmmv const cmdline::parsed_cmdline mock_cmdline(options, cmdline::args_vector()); 168 1.1 jmmv 169 1.1 jmmv require_eq(engine::default_config(), 170 1.1 jmmv cli::load_config(mock_cmdline, true)); 171 1.1 jmmv } 172 1.1 jmmv 173 1.1 jmmv 174 1.1 jmmv ATF_TEST_CASE_WITHOUT_HEAD(load_config__explicit__fail); 175 1.1 jmmv ATF_TEST_CASE_BODY(load_config__explicit__fail) 176 1.1 jmmv { 177 1.1 jmmv mock_system_config("ok1"); 178 1.1 jmmv mock_user_config("ok2"); 179 1.1 jmmv 180 1.1 jmmv create_mock_config("test-file", NULL); 181 1.1 jmmv 182 1.1 jmmv std::map< std::string, std::vector< std::string > > options; 183 1.1 jmmv options["config"].push_back("test-file"); 184 1.1 jmmv const cmdline::parsed_cmdline mock_cmdline(options, cmdline::args_vector()); 185 1.1 jmmv 186 1.1 jmmv ATF_REQUIRE_THROW_RE(engine::error, "200", 187 1.1 jmmv cli::load_config(mock_cmdline, true)); 188 1.1 jmmv 189 1.1 jmmv const config::tree config = cli::load_config(mock_cmdline, false); 190 1.1 jmmv require_eq(engine::default_config(), config); 191 1.1 jmmv } 192 1.1 jmmv 193 1.1 jmmv 194 1.1 jmmv ATF_TEST_CASE_WITHOUT_HEAD(load_config__user__ok); 195 1.1 jmmv ATF_TEST_CASE_BODY(load_config__user__ok) 196 1.1 jmmv { 197 1.1 jmmv mock_system_config(NULL); 198 1.1 jmmv mock_user_config("I am the user config"); 199 1.1 jmmv 200 1.1 jmmv std::map< std::string, std::vector< std::string > > options; 201 1.1 jmmv options["config"].push_back(cli::config_option.default_value()); 202 1.1 jmmv const cmdline::parsed_cmdline mock_cmdline(options, cmdline::args_vector()); 203 1.1 jmmv 204 1.1 jmmv const config::tree user_config = cli::load_config(mock_cmdline, true); 205 1.1 jmmv validate_mock_config(user_config, "I am the user config"); 206 1.1 jmmv } 207 1.1 jmmv 208 1.1 jmmv 209 1.1 jmmv ATF_TEST_CASE_WITHOUT_HEAD(load_config__user__fail); 210 1.1 jmmv ATF_TEST_CASE_BODY(load_config__user__fail) 211 1.1 jmmv { 212 1.1 jmmv mock_system_config("valid"); 213 1.1 jmmv mock_user_config(NULL); 214 1.1 jmmv 215 1.1 jmmv std::map< std::string, std::vector< std::string > > options; 216 1.1 jmmv options["config"].push_back(cli::config_option.default_value()); 217 1.1 jmmv const cmdline::parsed_cmdline mock_cmdline(options, cmdline::args_vector()); 218 1.1 jmmv 219 1.1 jmmv ATF_REQUIRE_THROW_RE(engine::error, "200", 220 1.1 jmmv cli::load_config(mock_cmdline, true)); 221 1.1 jmmv 222 1.1 jmmv const config::tree config = cli::load_config(mock_cmdline, false); 223 1.1 jmmv require_eq(engine::default_config(), config); 224 1.1 jmmv } 225 1.1 jmmv 226 1.1 jmmv 227 1.1 jmmv ATF_TEST_CASE_WITHOUT_HEAD(load_config__user__bad_home); 228 1.1 jmmv ATF_TEST_CASE_BODY(load_config__user__bad_home) 229 1.1 jmmv { 230 1.1 jmmv mock_system_config("Fallback system config"); 231 1.1 jmmv utils::setenv("HOME", ""); 232 1.1 jmmv 233 1.1 jmmv std::map< std::string, std::vector< std::string > > options; 234 1.1 jmmv options["config"].push_back(cli::config_option.default_value()); 235 1.1 jmmv const cmdline::parsed_cmdline mock_cmdline(options, cmdline::args_vector()); 236 1.1 jmmv 237 1.1 jmmv const config::tree user_config = cli::load_config(mock_cmdline, true); 238 1.1 jmmv validate_mock_config(user_config, "Fallback system config"); 239 1.1 jmmv } 240 1.1 jmmv 241 1.1 jmmv 242 1.1 jmmv ATF_TEST_CASE_WITHOUT_HEAD(load_config__system__ok); 243 1.1 jmmv ATF_TEST_CASE_BODY(load_config__system__ok) 244 1.1 jmmv { 245 1.1 jmmv mock_system_config("I am the system config"); 246 1.1 jmmv utils::setenv("HOME", "/the/user/does/not/exist"); 247 1.1 jmmv 248 1.1 jmmv std::map< std::string, std::vector< std::string > > options; 249 1.1 jmmv options["config"].push_back(cli::config_option.default_value()); 250 1.1 jmmv const cmdline::parsed_cmdline mock_cmdline(options, cmdline::args_vector()); 251 1.1 jmmv 252 1.1 jmmv const config::tree user_config = cli::load_config(mock_cmdline, true); 253 1.1 jmmv validate_mock_config(user_config, "I am the system config"); 254 1.1 jmmv } 255 1.1 jmmv 256 1.1 jmmv 257 1.1 jmmv ATF_TEST_CASE_WITHOUT_HEAD(load_config__system__fail); 258 1.1 jmmv ATF_TEST_CASE_BODY(load_config__system__fail) 259 1.1 jmmv { 260 1.1 jmmv mock_system_config(NULL); 261 1.1 jmmv utils::setenv("HOME", "/the/user/does/not/exist"); 262 1.1 jmmv 263 1.1 jmmv std::map< std::string, std::vector< std::string > > options; 264 1.1 jmmv options["config"].push_back(cli::config_option.default_value()); 265 1.1 jmmv const cmdline::parsed_cmdline mock_cmdline(options, cmdline::args_vector()); 266 1.1 jmmv 267 1.1 jmmv ATF_REQUIRE_THROW_RE(engine::error, "200", 268 1.1 jmmv cli::load_config(mock_cmdline, true)); 269 1.1 jmmv 270 1.1 jmmv const config::tree config = cli::load_config(mock_cmdline, false); 271 1.1 jmmv require_eq(engine::default_config(), config); 272 1.1 jmmv } 273 1.1 jmmv 274 1.1 jmmv 275 1.1 jmmv ATF_TEST_CASE_WITHOUT_HEAD(load_config__overrides__no); 276 1.1 jmmv ATF_TEST_CASE_BODY(load_config__overrides__no) 277 1.1 jmmv { 278 1.1 jmmv utils::setenv("KYUA_CONFDIR", fs::current_path().str()); 279 1.1 jmmv 280 1.1 jmmv std::map< std::string, std::vector< std::string > > options; 281 1.1 jmmv options["config"].push_back(cli::config_option.default_value()); 282 1.1 jmmv options["variable"].push_back("architecture=1"); 283 1.1 jmmv options["variable"].push_back("platform=2"); 284 1.1 jmmv const cmdline::parsed_cmdline mock_cmdline(options, cmdline::args_vector()); 285 1.1 jmmv 286 1.1 jmmv const config::tree user_config = cli::load_config(mock_cmdline, true); 287 1.1 jmmv ATF_REQUIRE_EQ("1", 288 1.1 jmmv user_config.lookup< config::string_node >("architecture")); 289 1.1 jmmv ATF_REQUIRE_EQ("2", 290 1.1 jmmv user_config.lookup< config::string_node >("platform")); 291 1.1 jmmv } 292 1.1 jmmv 293 1.1 jmmv 294 1.1 jmmv ATF_TEST_CASE_WITHOUT_HEAD(load_config__overrides__yes); 295 1.1 jmmv ATF_TEST_CASE_BODY(load_config__overrides__yes) 296 1.1 jmmv { 297 1.1 jmmv atf::utils::create_file( 298 1.1 jmmv "config", 299 1.1 jmmv "syntax(2)\n" 300 1.1 jmmv "architecture = 'do not see me'\n" 301 1.1 jmmv "platform = 'see me'\n"); 302 1.1 jmmv 303 1.1 jmmv std::map< std::string, std::vector< std::string > > options; 304 1.1 jmmv options["config"].push_back("config"); 305 1.1 jmmv options["variable"].push_back("architecture=overriden"); 306 1.1 jmmv const cmdline::parsed_cmdline mock_cmdline(options, cmdline::args_vector()); 307 1.1 jmmv 308 1.1 jmmv const config::tree user_config = cli::load_config(mock_cmdline, true); 309 1.1 jmmv ATF_REQUIRE_EQ("overriden", 310 1.1 jmmv user_config.lookup< config::string_node >("architecture")); 311 1.1 jmmv ATF_REQUIRE_EQ("see me", 312 1.1 jmmv user_config.lookup< config::string_node >("platform")); 313 1.1 jmmv } 314 1.1 jmmv 315 1.1 jmmv 316 1.1 jmmv ATF_TEST_CASE_WITHOUT_HEAD(load_config__overrides__fail); 317 1.1 jmmv ATF_TEST_CASE_BODY(load_config__overrides__fail) 318 1.1 jmmv { 319 1.1 jmmv utils::setenv("KYUA_CONFDIR", fs::current_path().str()); 320 1.1 jmmv 321 1.1 jmmv std::map< std::string, std::vector< std::string > > options; 322 1.1 jmmv options["config"].push_back(cli::config_option.default_value()); 323 1.1 jmmv options["variable"].push_back(".a=d"); 324 1.1 jmmv const cmdline::parsed_cmdline mock_cmdline(options, cmdline::args_vector()); 325 1.1 jmmv 326 1.1 jmmv ATF_REQUIRE_THROW_RE(engine::error, "Empty component in key.*'\\.a'", 327 1.1 jmmv cli::load_config(mock_cmdline, true)); 328 1.1 jmmv 329 1.1 jmmv const config::tree config = cli::load_config(mock_cmdline, false); 330 1.1 jmmv require_eq(engine::default_config(), config); 331 1.1 jmmv } 332 1.1 jmmv 333 1.1 jmmv 334 1.1 jmmv ATF_INIT_TEST_CASES(tcs) 335 1.1 jmmv { 336 1.1 jmmv ATF_ADD_TEST_CASE(tcs, load_config__none); 337 1.1 jmmv ATF_ADD_TEST_CASE(tcs, load_config__explicit__ok); 338 1.1 jmmv ATF_ADD_TEST_CASE(tcs, load_config__explicit__disable); 339 1.1 jmmv ATF_ADD_TEST_CASE(tcs, load_config__explicit__fail); 340 1.1 jmmv ATF_ADD_TEST_CASE(tcs, load_config__user__ok); 341 1.1 jmmv ATF_ADD_TEST_CASE(tcs, load_config__user__fail); 342 1.1 jmmv ATF_ADD_TEST_CASE(tcs, load_config__user__bad_home); 343 1.1 jmmv ATF_ADD_TEST_CASE(tcs, load_config__system__ok); 344 1.1 jmmv ATF_ADD_TEST_CASE(tcs, load_config__system__fail); 345 1.1 jmmv ATF_ADD_TEST_CASE(tcs, load_config__overrides__no); 346 1.1 jmmv ATF_ADD_TEST_CASE(tcs, load_config__overrides__yes); 347 1.1 jmmv ATF_ADD_TEST_CASE(tcs, load_config__overrides__fail); 348 1.1 jmmv } 349