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