1// Copyright 2015 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 "engine/atf_list.hpp" 30 31#include <sstream> 32#include <string> 33 34#include <atf-c++.hpp> 35 36#include "engine/exceptions.hpp" 37#include "model/metadata.hpp" 38#include "model/test_case.hpp" 39#include "model/types.hpp" 40#include "utils/datetime.hpp" 41#include "utils/format/containers.ipp" 42#include "utils/fs/path.hpp" 43#include "utils/units.hpp" 44 45namespace datetime = utils::datetime; 46namespace fs = utils::fs; 47namespace units = utils::units; 48 49 50ATF_TEST_CASE_WITHOUT_HEAD(parse_atf_metadata__defaults) 51ATF_TEST_CASE_BODY(parse_atf_metadata__defaults) 52{ 53 const model::properties_map properties; 54 const model::metadata md = engine::parse_atf_metadata(properties); 55 56 const model::metadata exp_md = model::metadata_builder().build(); 57 ATF_REQUIRE_EQ(exp_md, md); 58} 59 60 61ATF_TEST_CASE_WITHOUT_HEAD(parse_atf_metadata__override_all) 62ATF_TEST_CASE_BODY(parse_atf_metadata__override_all) 63{ 64 model::properties_map properties; 65 properties["descr"] = "Some text"; 66 properties["has.cleanup"] = "true"; 67 properties["require.arch"] = "i386 x86_64"; 68 properties["require.config"] = "var1 var2 var3"; 69 properties["require.files"] = "/file1 /dir/file2"; 70 properties["require.machine"] = "amd64"; 71 properties["require.memory"] = "1m"; 72 properties["require.progs"] = "/bin/ls svn"; 73 properties["require.user"] = "root"; 74 properties["timeout"] = "123"; 75 properties["X-foo"] = "value1"; 76 properties["X-bar"] = "value2"; 77 properties["X-baz-www"] = "value3"; 78 const model::metadata md = engine::parse_atf_metadata(properties); 79 80 const model::metadata exp_md = model::metadata_builder() 81 .add_allowed_architecture("i386") 82 .add_allowed_architecture("x86_64") 83 .add_allowed_platform("amd64") 84 .add_custom("foo", "value1") 85 .add_custom("bar", "value2") 86 .add_custom("baz-www", "value3") 87 .add_required_config("var1") 88 .add_required_config("var2") 89 .add_required_config("var3") 90 .add_required_file(fs::path("/file1")) 91 .add_required_file(fs::path("/dir/file2")) 92 .add_required_program(fs::path("/bin/ls")) 93 .add_required_program(fs::path("svn")) 94 .set_description("Some text") 95 .set_has_cleanup(true) 96 .set_required_memory(units::bytes::parse("1m")) 97 .set_required_user("root") 98 .set_timeout(datetime::delta(123, 0)) 99 .build(); 100 ATF_REQUIRE_EQ(exp_md, md); 101} 102 103 104ATF_TEST_CASE_WITHOUT_HEAD(parse_atf_metadata__unknown) 105ATF_TEST_CASE_BODY(parse_atf_metadata__unknown) 106{ 107 model::properties_map properties; 108 properties["foobar"] = "Some text"; 109 110 ATF_REQUIRE_THROW_RE(engine::format_error, "Unknown.*property.*'foobar'", 111 engine::parse_atf_metadata(properties)); 112} 113 114 115ATF_TEST_CASE_WITHOUT_HEAD(parse_atf_list__empty); 116ATF_TEST_CASE_BODY(parse_atf_list__empty) 117{ 118 const std::string text = ""; 119 std::istringstream input(text); 120 ATF_REQUIRE_THROW_RE(engine::format_error, "expecting Content-Type", 121 engine::parse_atf_list(input)); 122} 123 124 125ATF_TEST_CASE_WITHOUT_HEAD(parse_atf_list__invalid_header); 126ATF_TEST_CASE_BODY(parse_atf_list__invalid_header) 127{ 128 { 129 const std::string text = 130 "Content-Type: application/X-atf-tp; version=\"1\"\n"; 131 std::istringstream input(text); 132 ATF_REQUIRE_THROW_RE(engine::format_error, "expecting.*blank line", 133 engine::parse_atf_list(input)); 134 } 135 136 { 137 const std::string text = 138 "Content-Type: application/X-atf-tp; version=\"1\"\nfoo\n"; 139 std::istringstream input(text); 140 ATF_REQUIRE_THROW_RE(engine::format_error, "expecting.*blank line", 141 engine::parse_atf_list(input)); 142 } 143 144 { 145 const std::string text = 146 "Content-Type: application/X-atf-tp; version=\"2\"\n\n"; 147 std::istringstream input(text); 148 ATF_REQUIRE_THROW_RE(engine::format_error, "expecting Content-Type", 149 engine::parse_atf_list(input)); 150 } 151} 152 153 154ATF_TEST_CASE_WITHOUT_HEAD(parse_atf_list__no_test_cases); 155ATF_TEST_CASE_BODY(parse_atf_list__no_test_cases) 156{ 157 const std::string text = 158 "Content-Type: application/X-atf-tp; version=\"1\"\n\n"; 159 std::istringstream input(text); 160 ATF_REQUIRE_THROW_RE(engine::format_error, "No test cases", 161 engine::parse_atf_list(input)); 162} 163 164 165ATF_TEST_CASE_WITHOUT_HEAD(parse_atf_list__one_test_case_simple); 166ATF_TEST_CASE_BODY(parse_atf_list__one_test_case_simple) 167{ 168 const std::string text = 169 "Content-Type: application/X-atf-tp; version=\"1\"\n" 170 "\n" 171 "ident: test-case\n"; 172 std::istringstream input(text); 173 const model::test_cases_map tests = engine::parse_atf_list(input); 174 175 const model::test_cases_map exp_tests = model::test_cases_map_builder() 176 .add("test-case").build(); 177 ATF_REQUIRE_EQ(exp_tests, tests); 178} 179 180 181ATF_TEST_CASE_WITHOUT_HEAD(parse_atf_list__one_test_case_complex); 182ATF_TEST_CASE_BODY(parse_atf_list__one_test_case_complex) 183{ 184 const std::string text = 185 "Content-Type: application/X-atf-tp; version=\"1\"\n" 186 "\n" 187 "ident: first\n" 188 "descr: This is the description\n" 189 "timeout: 500\n"; 190 std::istringstream input(text); 191 const model::test_cases_map tests = engine::parse_atf_list(input); 192 193 const model::test_cases_map exp_tests = model::test_cases_map_builder() 194 .add("first", model::metadata_builder() 195 .set_description("This is the description") 196 .set_timeout(datetime::delta(500, 0)) 197 .build()) 198 .build(); 199 ATF_REQUIRE_EQ(exp_tests, tests); 200} 201 202 203ATF_TEST_CASE_WITHOUT_HEAD(parse_atf_list__one_test_case_invalid_syntax); 204ATF_TEST_CASE_BODY(parse_atf_list__one_test_case_invalid_syntax) 205{ 206 const std::string text = 207 "Content-Type: application/X-atf-tp; version=\"1\"\n\n" 208 "descr: This is the description\n" 209 "ident: first\n"; 210 std::istringstream input(text); 211 ATF_REQUIRE_THROW_RE(engine::format_error, "preceeded.*identifier", 212 engine::parse_atf_list(input)); 213} 214 215 216ATF_TEST_CASE_WITHOUT_HEAD(parse_atf_list__one_test_case_invalid_properties); 217ATF_TEST_CASE_BODY(parse_atf_list__one_test_case_invalid_properties) 218{ 219 // Inject a single invalid property that makes test_case::from_properties() 220 // raise a particular error message so that we can validate that such 221 // function was called. We do intensive testing separately, so it is not 222 // necessary to redo it here. 223 const std::string text = 224 "Content-Type: application/X-atf-tp; version=\"1\"\n\n" 225 "ident: first\n" 226 "require.progs: bin/ls\n"; 227 std::istringstream input(text); 228 ATF_REQUIRE_THROW_RE(engine::format_error, "Relative path 'bin/ls'", 229 engine::parse_atf_list(input)); 230} 231 232 233ATF_TEST_CASE_WITHOUT_HEAD(parse_atf_list__many_test_cases); 234ATF_TEST_CASE_BODY(parse_atf_list__many_test_cases) 235{ 236 const std::string text = 237 "Content-Type: application/X-atf-tp; version=\"1\"\n" 238 "\n" 239 "ident: first\n" 240 "descr: This is the description\n" 241 "\n" 242 "ident: second\n" 243 "timeout: 500\n" 244 "descr: Some text\n" 245 "\n" 246 "ident: third\n"; 247 std::istringstream input(text); 248 const model::test_cases_map tests = engine::parse_atf_list(input); 249 250 const model::test_cases_map exp_tests = model::test_cases_map_builder() 251 .add("first", model::metadata_builder() 252 .set_description("This is the description") 253 .build()) 254 .add("second", model::metadata_builder() 255 .set_description("Some text") 256 .set_timeout(datetime::delta(500, 0)) 257 .build()) 258 .add("third") 259 .build(); 260 ATF_REQUIRE_EQ(exp_tests, tests); 261} 262 263 264ATF_INIT_TEST_CASES(tcs) 265{ 266 ATF_ADD_TEST_CASE(tcs, parse_atf_metadata__defaults); 267 ATF_ADD_TEST_CASE(tcs, parse_atf_metadata__override_all); 268 ATF_ADD_TEST_CASE(tcs, parse_atf_metadata__unknown); 269 270 ATF_ADD_TEST_CASE(tcs, parse_atf_list__empty); 271 ATF_ADD_TEST_CASE(tcs, parse_atf_list__invalid_header); 272 ATF_ADD_TEST_CASE(tcs, parse_atf_list__no_test_cases); 273 ATF_ADD_TEST_CASE(tcs, parse_atf_list__one_test_case_simple); 274 ATF_ADD_TEST_CASE(tcs, parse_atf_list__one_test_case_complex); 275 ATF_ADD_TEST_CASE(tcs, parse_atf_list__one_test_case_invalid_syntax); 276 ATF_ADD_TEST_CASE(tcs, parse_atf_list__one_test_case_invalid_properties); 277 ATF_ADD_TEST_CASE(tcs, parse_atf_list__many_test_cases); 278} 279