1 // Copyright 2010 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 extern "C" { 30 #include <sys/stat.h> 31 32 #include <signal.h> 33 #include <unistd.h> 34 } 35 36 #include <algorithm> 37 #include <cstdlib> 38 #include <fstream> 39 #include <iostream> 40 #include <set> 41 #include <sstream> 42 #include <string> 43 #include <vector> 44 45 #include <atf-c++.hpp> 46 47 #include "utils/env.hpp" 48 #include "utils/fs/operations.hpp" 49 #include "utils/fs/path.hpp" 50 #include "utils/logging/operations.hpp" 51 #include "utils/optional.ipp" 52 #include "utils/test_utils.ipp" 53 #include "utils/text/operations.ipp" 54 55 namespace fs = utils::fs; 56 namespace logging = utils::logging; 57 namespace text = utils::text; 58 59 using utils::optional; 60 61 62 namespace { 63 64 65 /// Creates an empty file in the given directory. 66 /// 67 /// \param test_case The test case currently running. 68 /// \param directory The name of the configuration variable that holds the path 69 /// to the directory in which to create the cookie file. 70 /// \param name The name of the cookie file to create. 71 static void 72 create_cookie(const atf::tests::tc* test_case, const char* directory, 73 const char* name) 74 { 75 if (!test_case->has_config_var(directory)) 76 test_case->fail(std::string(name) + " not provided"); 77 78 const fs::path control_dir(test_case->get_config_var(directory)); 79 std::ofstream file((control_dir / name).c_str()); 80 if (!file) 81 test_case->fail("Failed to create the control cookie"); 82 file.close(); 83 } 84 85 86 } // anonymous namespace 87 88 89 ATF_TEST_CASE_WITH_CLEANUP(check_cleanup_workdir); 90 ATF_TEST_CASE_HEAD(check_cleanup_workdir) 91 { 92 set_md_var("require.config", "control_dir"); 93 } 94 ATF_TEST_CASE_BODY(check_cleanup_workdir) 95 { 96 std::ofstream cookie("workdir_cookie"); 97 cookie << "1234\n"; 98 cookie.close(); 99 skip("cookie created"); 100 } 101 ATF_TEST_CASE_CLEANUP(check_cleanup_workdir) 102 { 103 const fs::path control_dir(get_config_var("control_dir")); 104 105 std::ifstream cookie("workdir_cookie"); 106 if (!cookie) { 107 std::ofstream((control_dir / "missing_cookie").c_str()).close(); 108 std::exit(EXIT_FAILURE); 109 } 110 111 std::string value; 112 cookie >> value; 113 if (value != "1234") { 114 std::ofstream((control_dir / "invalid_cookie").c_str()).close(); 115 std::exit(EXIT_FAILURE); 116 } 117 118 std::ofstream((control_dir / "cookie_ok").c_str()).close(); 119 std::exit(EXIT_SUCCESS); 120 } 121 122 123 ATF_TEST_CASE_WITHOUT_HEAD(check_configuration_variables); 124 ATF_TEST_CASE_BODY(check_configuration_variables) 125 { 126 ATF_REQUIRE(has_config_var("first")); 127 ATF_REQUIRE_EQ("some value", get_config_var("first")); 128 129 ATF_REQUIRE(has_config_var("second")); 130 ATF_REQUIRE_EQ("some other value", get_config_var("second")); 131 } 132 133 134 ATF_TEST_CASE(check_list_config); 135 ATF_TEST_CASE_HEAD(check_list_config) 136 { 137 std::string description = "Found:"; 138 139 if (has_config_var("var1")) 140 description += " var1=" + get_config_var("var1"); 141 if (has_config_var("var2")) 142 description += " var2=" + get_config_var("var2"); 143 144 set_md_var("descr", description); 145 } 146 ATF_TEST_CASE_BODY(check_list_config) 147 { 148 } 149 150 151 ATF_TEST_CASE_WITHOUT_HEAD(check_unprivileged); 152 ATF_TEST_CASE_BODY(check_unprivileged) 153 { 154 if (::getuid() == 0) 155 fail("Running as root, but I shouldn't be"); 156 157 std::ofstream file("cookie"); 158 if (!file) 159 fail("Failed to create the cookie; work directory probably owned by " 160 "root"); 161 file.close(); 162 } 163 164 165 ATF_TEST_CASE_WITHOUT_HEAD(crash); 166 ATF_TEST_CASE_BODY(crash) 167 { 168 std::abort(); 169 } 170 171 172 ATF_TEST_CASE(crash_head); 173 ATF_TEST_CASE_HEAD(crash_head) 174 { 175 utils::abort_without_coredump(); 176 } 177 ATF_TEST_CASE_BODY(crash_head) 178 { 179 } 180 181 182 ATF_TEST_CASE_WITH_CLEANUP(crash_cleanup); 183 ATF_TEST_CASE_HEAD(crash_cleanup) 184 { 185 } 186 ATF_TEST_CASE_BODY(crash_cleanup) 187 { 188 } 189 ATF_TEST_CASE_CLEANUP(crash_cleanup) 190 { 191 utils::abort_without_coredump(); 192 } 193 194 195 ATF_TEST_CASE_WITHOUT_HEAD(create_cookie_in_control_dir); 196 ATF_TEST_CASE_BODY(create_cookie_in_control_dir) 197 { 198 create_cookie(this, "control_dir", "cookie"); 199 } 200 201 202 ATF_TEST_CASE_WITHOUT_HEAD(create_cookie_in_workdir); 203 ATF_TEST_CASE_BODY(create_cookie_in_workdir) 204 { 205 std::ofstream file("cookie"); 206 if (!file) 207 fail("Failed to create the cookie"); 208 file.close(); 209 } 210 211 212 ATF_TEST_CASE_WITH_CLEANUP(create_cookie_from_cleanup); 213 ATF_TEST_CASE_HEAD(create_cookie_from_cleanup) 214 { 215 } 216 ATF_TEST_CASE_BODY(create_cookie_from_cleanup) 217 { 218 } 219 ATF_TEST_CASE_CLEANUP(create_cookie_from_cleanup) 220 { 221 create_cookie(this, "control_dir", "cookie"); 222 } 223 224 225 ATF_TEST_CASE_WITH_CLEANUP(expect_timeout); 226 ATF_TEST_CASE_HEAD(expect_timeout) 227 { 228 if (has_config_var("timeout")) 229 set_md_var("timeout", get_config_var("timeout")); 230 } 231 ATF_TEST_CASE_BODY(expect_timeout) 232 { 233 expect_timeout("Times out on purpose"); 234 ::sleep(10); 235 create_cookie(this, "control_dir", "cookie"); 236 } 237 ATF_TEST_CASE_CLEANUP(expect_timeout) 238 { 239 create_cookie(this, "control_dir", "cookie.cleanup"); 240 } 241 242 243 ATF_TEST_CASE_WITH_CLEANUP(output); 244 ATF_TEST_CASE_HEAD(output) 245 { 246 } 247 ATF_TEST_CASE_BODY(output) 248 { 249 std::cout << "Body message to stdout\n"; 250 std::cerr << "Body message to stderr\n"; 251 } 252 ATF_TEST_CASE_CLEANUP(output) 253 { 254 std::cout << "Cleanup message to stdout\n"; 255 std::cerr << "Cleanup message to stderr\n"; 256 } 257 258 259 ATF_TEST_CASE(output_in_list); 260 ATF_TEST_CASE_HEAD(output_in_list) 261 { 262 std::cerr << "Should not write anything!\n"; 263 } 264 ATF_TEST_CASE_BODY(output_in_list) 265 { 266 } 267 268 269 ATF_TEST_CASE(pass); 270 ATF_TEST_CASE_HEAD(pass) 271 { 272 set_md_var("descr", "Always-passing test case"); 273 } 274 ATF_TEST_CASE_BODY(pass) 275 { 276 } 277 278 279 ATF_TEST_CASE_WITH_CLEANUP(shared_workdir); 280 ATF_TEST_CASE_HEAD(shared_workdir) 281 { 282 } 283 ATF_TEST_CASE_BODY(shared_workdir) 284 { 285 atf::utils::create_file("shared_cookie", ""); 286 } 287 ATF_TEST_CASE_CLEANUP(shared_workdir) 288 { 289 if (!atf::utils::file_exists("shared_cookie")) 290 utils::abort_without_coredump(); 291 } 292 293 294 ATF_TEST_CASE(spawn_blocking_child); 295 ATF_TEST_CASE_HEAD(spawn_blocking_child) 296 { 297 set_md_var("require.config", "control_dir"); 298 } 299 ATF_TEST_CASE_BODY(spawn_blocking_child) 300 { 301 pid_t pid = ::fork(); 302 if (pid == -1) 303 fail("Cannot fork subprocess"); 304 else if (pid == 0) { 305 for (;;) 306 ::pause(); 307 } else { 308 const fs::path name = fs::path(get_config_var("control_dir")) / "pid"; 309 std::ofstream pidfile(name.c_str()); 310 ATF_REQUIRE(pidfile); 311 pidfile << pid; 312 pidfile.close(); 313 } 314 } 315 316 317 ATF_TEST_CASE_WITH_CLEANUP(timeout_body); 318 ATF_TEST_CASE_HEAD(timeout_body) 319 { 320 if (has_config_var("timeout")) 321 set_md_var("timeout", get_config_var("timeout")); 322 } 323 ATF_TEST_CASE_BODY(timeout_body) 324 { 325 ::sleep(10); 326 create_cookie(this, "control_dir", "cookie"); 327 } 328 ATF_TEST_CASE_CLEANUP(timeout_body) 329 { 330 create_cookie(this, "control_dir", "cookie.cleanup"); 331 } 332 333 334 ATF_TEST_CASE_WITH_CLEANUP(timeout_cleanup); 335 ATF_TEST_CASE_HEAD(timeout_cleanup) 336 { 337 } 338 ATF_TEST_CASE_BODY(timeout_cleanup) 339 { 340 } 341 ATF_TEST_CASE_CLEANUP(timeout_cleanup) 342 { 343 ::sleep(10); 344 create_cookie(this, "control_dir", "cookie"); 345 } 346 347 348 ATF_TEST_CASE_WITHOUT_HEAD(validate_isolation); 349 ATF_TEST_CASE_BODY(validate_isolation) 350 { 351 ATF_REQUIRE(utils::getenv("HOME").get() != "fake-value"); 352 ATF_REQUIRE(!utils::getenv("LANG")); 353 } 354 355 356 /// Wrapper around ATF_ADD_TEST_CASE to only add a test when requested. 357 /// 358 /// The caller can set the TEST_CASES environment variable to a 359 /// whitespace-separated list of test case names to enable. If not empty, the 360 /// list acts as a filter for the tests to add. 361 /// 362 /// \param tcs List of test cases into which to register the test. 363 /// \param filters List of filters to determine whether the test applies or not. 364 /// \param name Name of the test case being added. 365 #define ADD_TEST_CASE(tcs, filters, name) \ 366 do { \ 367 if (filters.empty() || filters.find(#name) != filters.end()) \ 368 ATF_ADD_TEST_CASE(tcs, name); \ 369 } while (false) 370 371 372 ATF_INIT_TEST_CASES(tcs) 373 { 374 logging::set_inmemory(); 375 376 // TODO(jmmv): Instead of using "filters", we should make TEST_CASES 377 // explicitly list all the test cases to enable. This would let us get rid 378 // of some of the hacks below... 379 std::set< std::string > filters; 380 381 const optional< std::string > names_raw = utils::getenv("TEST_CASES"); 382 if (names_raw) { 383 if (names_raw.get().empty()) 384 return; // See TODO above. 385 386 const std::vector< std::string > names = text::split( 387 names_raw.get(), ' '); 388 std::copy(names.begin(), names.end(), 389 std::inserter(filters, filters.begin())); 390 } 391 392 if (filters.find("crash_head") != filters.end()) // See TODO above. 393 ATF_ADD_TEST_CASE(tcs, crash_head); 394 if (filters.find("output_in_list") != filters.end()) // See TODO above. 395 ATF_ADD_TEST_CASE(tcs, output_in_list); 396 397 ADD_TEST_CASE(tcs, filters, check_cleanup_workdir); 398 ADD_TEST_CASE(tcs, filters, check_configuration_variables); 399 ADD_TEST_CASE(tcs, filters, check_list_config); 400 ADD_TEST_CASE(tcs, filters, check_unprivileged); 401 ADD_TEST_CASE(tcs, filters, crash); 402 ADD_TEST_CASE(tcs, filters, crash_cleanup); 403 ADD_TEST_CASE(tcs, filters, create_cookie_in_control_dir); 404 ADD_TEST_CASE(tcs, filters, create_cookie_in_workdir); 405 ADD_TEST_CASE(tcs, filters, create_cookie_from_cleanup); 406 ADD_TEST_CASE(tcs, filters, expect_timeout); 407 ADD_TEST_CASE(tcs, filters, output); 408 ADD_TEST_CASE(tcs, filters, pass); 409 ADD_TEST_CASE(tcs, filters, shared_workdir); 410 ADD_TEST_CASE(tcs, filters, spawn_blocking_child); 411 ADD_TEST_CASE(tcs, filters, timeout_body); 412 ADD_TEST_CASE(tcs, filters, timeout_cleanup); 413 ADD_TEST_CASE(tcs, filters, validate_isolation); 414 } 415