1 // Copyright 2012 The Kyua Authors. 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 "model/metadata.hpp" 30 31 #include <sstream> 32 33 #include <atf-c++.hpp> 34 35 #include "model/types.hpp" 36 #include "utils/datetime.hpp" 37 #include "utils/format/containers.ipp" 38 #include "utils/fs/path.hpp" 39 #include "utils/units.hpp" 40 41 namespace datetime = utils::datetime; 42 namespace fs = utils::fs; 43 namespace units = utils::units; 44 45 46 ATF_TEST_CASE_WITHOUT_HEAD(defaults); 47 ATF_TEST_CASE_BODY(defaults) 48 { 49 const model::metadata md = model::metadata_builder().build(); 50 ATF_REQUIRE(md.allowed_architectures().empty()); 51 ATF_REQUIRE(md.allowed_platforms().empty()); 52 ATF_REQUIRE(md.allowed_platforms().empty()); 53 ATF_REQUIRE(md.custom().empty()); 54 ATF_REQUIRE(md.description().empty()); 55 ATF_REQUIRE(!md.has_cleanup()); 56 ATF_REQUIRE(!md.is_exclusive()); 57 ATF_REQUIRE(md.required_configs().empty()); 58 ATF_REQUIRE_EQ(units::bytes(0), md.required_disk_space()); 59 ATF_REQUIRE(md.required_files().empty()); 60 ATF_REQUIRE_EQ(units::bytes(0), md.required_memory()); 61 ATF_REQUIRE(md.required_programs().empty()); 62 ATF_REQUIRE(md.required_user().empty()); 63 ATF_REQUIRE(datetime::delta(300, 0) == md.timeout()); 64 } 65 66 67 ATF_TEST_CASE_WITHOUT_HEAD(add); 68 ATF_TEST_CASE_BODY(add) 69 { 70 model::strings_set architectures; 71 architectures.insert("1-architecture"); 72 architectures.insert("2-architecture"); 73 74 model::strings_set platforms; 75 platforms.insert("1-platform"); 76 platforms.insert("2-platform"); 77 78 model::properties_map custom; 79 custom["1-custom"] = "first"; 80 custom["2-custom"] = "second"; 81 82 model::strings_set configs; 83 configs.insert("1-config"); 84 configs.insert("2-config"); 85 86 model::paths_set files; 87 files.insert(fs::path("1-file")); 88 files.insert(fs::path("2-file")); 89 90 model::paths_set programs; 91 programs.insert(fs::path("1-program")); 92 programs.insert(fs::path("2-program")); 93 94 const model::metadata md = model::metadata_builder() 95 .add_allowed_architecture("1-architecture") 96 .add_allowed_platform("1-platform") 97 .add_custom("1-custom", "first") 98 .add_custom("2-custom", "second") 99 .add_required_config("1-config") 100 .add_required_file(fs::path("1-file")) 101 .add_required_program(fs::path("1-program")) 102 .add_allowed_architecture("2-architecture") 103 .add_allowed_platform("2-platform") 104 .add_required_config("2-config") 105 .add_required_file(fs::path("2-file")) 106 .add_required_program(fs::path("2-program")) 107 .build(); 108 109 ATF_REQUIRE(architectures == md.allowed_architectures()); 110 ATF_REQUIRE(platforms == md.allowed_platforms()); 111 ATF_REQUIRE(custom == md.custom()); 112 ATF_REQUIRE(configs == md.required_configs()); 113 ATF_REQUIRE(files == md.required_files()); 114 ATF_REQUIRE(programs == md.required_programs()); 115 } 116 117 118 ATF_TEST_CASE_WITHOUT_HEAD(copy); 119 ATF_TEST_CASE_BODY(copy) 120 { 121 const model::metadata md1 = model::metadata_builder() 122 .add_allowed_architecture("1-architecture") 123 .add_allowed_platform("1-platform") 124 .build(); 125 126 const model::metadata md2 = model::metadata_builder(md1) 127 .add_allowed_architecture("2-architecture") 128 .build(); 129 130 ATF_REQUIRE_EQ(1, md1.allowed_architectures().size()); 131 ATF_REQUIRE_EQ(2, md2.allowed_architectures().size()); 132 ATF_REQUIRE_EQ(1, md1.allowed_platforms().size()); 133 ATF_REQUIRE_EQ(1, md2.allowed_platforms().size()); 134 } 135 136 137 ATF_TEST_CASE_WITHOUT_HEAD(apply_overrides); 138 ATF_TEST_CASE_BODY(apply_overrides) 139 { 140 const model::metadata md1 = model::metadata_builder() 141 .add_allowed_architecture("1-architecture") 142 .add_allowed_platform("1-platform") 143 .set_description("Explicit description") 144 .build(); 145 146 const model::metadata md2 = model::metadata_builder() 147 .add_allowed_architecture("2-architecture") 148 .set_description("") 149 .set_timeout(datetime::delta(500, 0)) 150 .build(); 151 152 const model::metadata merge_1_2 = model::metadata_builder() 153 .add_allowed_architecture("2-architecture") 154 .add_allowed_platform("1-platform") 155 .set_description("") 156 .set_timeout(datetime::delta(500, 0)) 157 .build(); 158 ATF_REQUIRE_EQ(merge_1_2, md1.apply_overrides(md2)); 159 160 const model::metadata merge_2_1 = model::metadata_builder() 161 .add_allowed_architecture("1-architecture") 162 .add_allowed_platform("1-platform") 163 .set_description("Explicit description") 164 .set_timeout(datetime::delta(500, 0)) 165 .build(); 166 ATF_REQUIRE_EQ(merge_2_1, md2.apply_overrides(md1)); 167 } 168 169 170 ATF_TEST_CASE_WITHOUT_HEAD(override_all_with_setters); 171 ATF_TEST_CASE_BODY(override_all_with_setters) 172 { 173 model::strings_set architectures; 174 architectures.insert("the-architecture"); 175 176 model::strings_set platforms; 177 platforms.insert("the-platforms"); 178 179 model::properties_map custom; 180 custom["first"] = "hello"; 181 custom["second"] = "bye"; 182 183 const std::string description = "Some long text"; 184 185 model::strings_set configs; 186 configs.insert("the-configs"); 187 188 model::paths_set files; 189 files.insert(fs::path("the-files")); 190 191 const units::bytes disk_space(6789); 192 193 const units::bytes memory(12345); 194 195 model::paths_set programs; 196 programs.insert(fs::path("the-programs")); 197 198 const std::string user = "root"; 199 200 const datetime::delta timeout(123, 0); 201 202 const model::metadata md = model::metadata_builder() 203 .set_allowed_architectures(architectures) 204 .set_allowed_platforms(platforms) 205 .set_custom(custom) 206 .set_description(description) 207 .set_has_cleanup(true) 208 .set_is_exclusive(true) 209 .set_required_configs(configs) 210 .set_required_disk_space(disk_space) 211 .set_required_files(files) 212 .set_required_memory(memory) 213 .set_required_programs(programs) 214 .set_required_user(user) 215 .set_timeout(timeout) 216 .build(); 217 218 ATF_REQUIRE(architectures == md.allowed_architectures()); 219 ATF_REQUIRE(platforms == md.allowed_platforms()); 220 ATF_REQUIRE(custom == md.custom()); 221 ATF_REQUIRE_EQ(description, md.description()); 222 ATF_REQUIRE(md.has_cleanup()); 223 ATF_REQUIRE(md.is_exclusive()); 224 ATF_REQUIRE(configs == md.required_configs()); 225 ATF_REQUIRE_EQ(disk_space, md.required_disk_space()); 226 ATF_REQUIRE(files == md.required_files()); 227 ATF_REQUIRE_EQ(memory, md.required_memory()); 228 ATF_REQUIRE(programs == md.required_programs()); 229 ATF_REQUIRE_EQ(user, md.required_user()); 230 ATF_REQUIRE(timeout == md.timeout()); 231 } 232 233 234 ATF_TEST_CASE_WITHOUT_HEAD(override_all_with_set_string); 235 ATF_TEST_CASE_BODY(override_all_with_set_string) 236 { 237 model::strings_set architectures; 238 architectures.insert("a1"); 239 architectures.insert("a2"); 240 241 model::strings_set platforms; 242 platforms.insert("p1"); 243 platforms.insert("p2"); 244 245 model::properties_map custom; 246 custom["user-defined"] = "the-value"; 247 248 const std::string description = "Another long text"; 249 250 model::strings_set configs; 251 configs.insert("config-var"); 252 253 model::paths_set files; 254 files.insert(fs::path("plain")); 255 files.insert(fs::path("/absolute/path")); 256 257 const units::bytes disk_space( 258 static_cast< uint64_t >(16) * 1024 * 1024 * 1024); 259 260 const units::bytes memory(1024 * 1024); 261 262 model::paths_set programs; 263 programs.insert(fs::path("program")); 264 programs.insert(fs::path("/absolute/prog")); 265 266 const std::string user = "unprivileged"; 267 268 const datetime::delta timeout(45, 0); 269 270 const model::metadata md = model::metadata_builder() 271 .set_string("allowed_architectures", "a1 a2") 272 .set_string("allowed_platforms", "p1 p2") 273 .set_string("custom.user-defined", "the-value") 274 .set_string("description", "Another long text") 275 .set_string("has_cleanup", "true") 276 .set_string("is_exclusive", "true") 277 .set_string("required_configs", "config-var") 278 .set_string("required_disk_space", "16G") 279 .set_string("required_files", "plain /absolute/path") 280 .set_string("required_memory", "1M") 281 .set_string("required_programs", "program /absolute/prog") 282 .set_string("required_user", "unprivileged") 283 .set_string("timeout", "45") 284 .build(); 285 286 ATF_REQUIRE(architectures == md.allowed_architectures()); 287 ATF_REQUIRE(platforms == md.allowed_platforms()); 288 ATF_REQUIRE(custom == md.custom()); 289 ATF_REQUIRE_EQ(description, md.description()); 290 ATF_REQUIRE(md.has_cleanup()); 291 ATF_REQUIRE(md.is_exclusive()); 292 ATF_REQUIRE(configs == md.required_configs()); 293 ATF_REQUIRE_EQ(disk_space, md.required_disk_space()); 294 ATF_REQUIRE(files == md.required_files()); 295 ATF_REQUIRE_EQ(memory, md.required_memory()); 296 ATF_REQUIRE(programs == md.required_programs()); 297 ATF_REQUIRE_EQ(user, md.required_user()); 298 ATF_REQUIRE(timeout == md.timeout()); 299 } 300 301 302 ATF_TEST_CASE_WITHOUT_HEAD(to_properties); 303 ATF_TEST_CASE_BODY(to_properties) 304 { 305 const model::metadata md = model::metadata_builder() 306 .add_allowed_architecture("abc") 307 .add_required_file(fs::path("foo")) 308 .add_required_file(fs::path("bar")) 309 .set_required_memory(units::bytes(1024)) 310 .add_custom("foo", "bar") 311 .build(); 312 313 model::properties_map props; 314 props["allowed_architectures"] = "abc"; 315 props["allowed_platforms"] = ""; 316 props["custom.foo"] = "bar"; 317 props["description"] = ""; 318 props["execenv"] = ""; 319 props["execenv_jail_params"] = ""; 320 props["has_cleanup"] = "false"; 321 props["is_exclusive"] = "false"; 322 props["required_configs"] = ""; 323 props["required_disk_space"] = "0"; 324 props["required_files"] = "bar foo"; 325 props["required_memory"] = "1.00K"; 326 props["required_programs"] = ""; 327 props["required_user"] = ""; 328 props["timeout"] = "300"; 329 ATF_REQUIRE_EQ(props, md.to_properties()); 330 } 331 332 333 ATF_TEST_CASE_WITHOUT_HEAD(operators_eq_and_ne__empty); 334 ATF_TEST_CASE_BODY(operators_eq_and_ne__empty) 335 { 336 const model::metadata md1 = model::metadata_builder().build(); 337 const model::metadata md2 = model::metadata_builder().build(); 338 ATF_REQUIRE( md1 == md2); 339 ATF_REQUIRE(!(md1 != md2)); 340 } 341 342 343 ATF_TEST_CASE_WITHOUT_HEAD(operators_eq_and_ne__copy); 344 ATF_TEST_CASE_BODY(operators_eq_and_ne__copy) 345 { 346 const model::metadata md1 = model::metadata_builder() 347 .add_custom("foo", "bar") 348 .build(); 349 const model::metadata md2 = md1; 350 ATF_REQUIRE( md1 == md2); 351 ATF_REQUIRE(!(md1 != md2)); 352 } 353 354 355 ATF_TEST_CASE_WITHOUT_HEAD(operators_eq_and_ne__equal); 356 ATF_TEST_CASE_BODY(operators_eq_and_ne__equal) 357 { 358 const model::metadata md1 = model::metadata_builder() 359 .add_allowed_architecture("a") 360 .add_allowed_architecture("b") 361 .add_custom("foo", "bar") 362 .build(); 363 const model::metadata md2 = model::metadata_builder() 364 .add_allowed_architecture("b") 365 .add_allowed_architecture("a") 366 .add_custom("foo", "bar") 367 .build(); 368 ATF_REQUIRE( md1 == md2); 369 ATF_REQUIRE(!(md1 != md2)); 370 } 371 372 373 ATF_TEST_CASE_WITHOUT_HEAD(operators_eq_and_ne__equal_overriden_defaults); 374 ATF_TEST_CASE_BODY(operators_eq_and_ne__equal_overriden_defaults) 375 { 376 const model::metadata defaults = model::metadata_builder().build(); 377 378 const model::metadata md1 = model::metadata_builder() 379 .add_allowed_architecture("a") 380 .build(); 381 const model::metadata md2 = model::metadata_builder() 382 .add_allowed_architecture("a") 383 .set_timeout(defaults.timeout()) 384 .build(); 385 ATF_REQUIRE( md1 == md2); 386 ATF_REQUIRE(!(md1 != md2)); 387 } 388 389 390 ATF_TEST_CASE_WITHOUT_HEAD(operators_eq_and_ne__different); 391 ATF_TEST_CASE_BODY(operators_eq_and_ne__different) 392 { 393 const model::metadata md1 = model::metadata_builder() 394 .add_custom("foo", "bar") 395 .build(); 396 const model::metadata md2 = model::metadata_builder() 397 .add_custom("foo", "bar") 398 .add_custom("baz", "foo bar") 399 .build(); 400 ATF_REQUIRE(!(md1 == md2)); 401 ATF_REQUIRE( md1 != md2); 402 } 403 404 405 ATF_TEST_CASE_WITHOUT_HEAD(output__defaults); 406 ATF_TEST_CASE_BODY(output__defaults) 407 { 408 std::ostringstream str; 409 str << model::metadata_builder().build(); 410 ATF_REQUIRE_EQ("metadata{allowed_architectures='', allowed_platforms='', " 411 "description='', execenv='', execenv_jail_params='', " 412 "has_cleanup='false', is_exclusive='false', " 413 "required_configs='', " 414 "required_disk_space='0', required_files='', " 415 "required_memory='0', " 416 "required_programs='', required_user='', timeout='300'}", 417 str.str()); 418 } 419 420 421 ATF_TEST_CASE_WITHOUT_HEAD(output__some_values); 422 ATF_TEST_CASE_BODY(output__some_values) 423 { 424 std::ostringstream str; 425 str << model::metadata_builder() 426 .add_allowed_architecture("abc") 427 .add_required_file(fs::path("foo")) 428 .add_required_file(fs::path("bar")) 429 .set_is_exclusive(true) 430 .set_required_memory(units::bytes(1024)) 431 .build(); 432 ATF_REQUIRE_EQ( 433 "metadata{allowed_architectures='abc', allowed_platforms='', " 434 "description='', execenv='', execenv_jail_params='', " 435 "has_cleanup='false', is_exclusive='true', " 436 "required_configs='', " 437 "required_disk_space='0', required_files='bar foo', " 438 "required_memory='1.00K', " 439 "required_programs='', required_user='', timeout='300'}", 440 str.str()); 441 } 442 443 444 ATF_INIT_TEST_CASES(tcs) 445 { 446 ATF_ADD_TEST_CASE(tcs, defaults); 447 ATF_ADD_TEST_CASE(tcs, add); 448 ATF_ADD_TEST_CASE(tcs, copy); 449 ATF_ADD_TEST_CASE(tcs, apply_overrides); 450 ATF_ADD_TEST_CASE(tcs, override_all_with_setters); 451 ATF_ADD_TEST_CASE(tcs, override_all_with_set_string); 452 ATF_ADD_TEST_CASE(tcs, to_properties); 453 454 ATF_ADD_TEST_CASE(tcs, operators_eq_and_ne__empty); 455 ATF_ADD_TEST_CASE(tcs, operators_eq_and_ne__copy); 456 ATF_ADD_TEST_CASE(tcs, operators_eq_and_ne__equal); 457 ATF_ADD_TEST_CASE(tcs, operators_eq_and_ne__equal_overriden_defaults); 458 ATF_ADD_TEST_CASE(tcs, operators_eq_and_ne__different); 459 460 ATF_ADD_TEST_CASE(tcs, output__defaults); 461 ATF_ADD_TEST_CASE(tcs, output__some_values); 462 463 // TODO(jmmv): Add tests for error conditions (invalid keys and invalid 464 // values). 465 } 466