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 29extern "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 55namespace fs = utils::fs; 56namespace logging = utils::logging; 57namespace text = utils::text; 58 59using utils::optional; 60 61 62namespace { 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. 71static void 72create_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 89ATF_TEST_CASE_WITH_CLEANUP(check_cleanup_workdir); 90ATF_TEST_CASE_HEAD(check_cleanup_workdir) 91{ 92 set_md_var("require.config", "control_dir"); 93} 94ATF_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} 101ATF_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 123ATF_TEST_CASE_WITHOUT_HEAD(check_configuration_variables); 124ATF_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 134ATF_TEST_CASE(check_list_config); 135ATF_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} 146ATF_TEST_CASE_BODY(check_list_config) 147{ 148} 149 150 151ATF_TEST_CASE_WITHOUT_HEAD(check_unprivileged); 152ATF_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 165ATF_TEST_CASE_WITHOUT_HEAD(crash); 166ATF_TEST_CASE_BODY(crash) 167{ 168 std::abort(); 169} 170 171 172ATF_TEST_CASE(crash_head); 173ATF_TEST_CASE_HEAD(crash_head) 174{ 175 utils::abort_without_coredump(); 176} 177ATF_TEST_CASE_BODY(crash_head) 178{ 179} 180 181 182ATF_TEST_CASE_WITH_CLEANUP(crash_cleanup); 183ATF_TEST_CASE_HEAD(crash_cleanup) 184{ 185} 186ATF_TEST_CASE_BODY(crash_cleanup) 187{ 188} 189ATF_TEST_CASE_CLEANUP(crash_cleanup) 190{ 191 utils::abort_without_coredump(); 192} 193 194 195ATF_TEST_CASE_WITHOUT_HEAD(create_cookie_in_control_dir); 196ATF_TEST_CASE_BODY(create_cookie_in_control_dir) 197{ 198 create_cookie(this, "control_dir", "cookie"); 199} 200 201 202ATF_TEST_CASE_WITHOUT_HEAD(create_cookie_in_workdir); 203ATF_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 212ATF_TEST_CASE_WITH_CLEANUP(create_cookie_from_cleanup); 213ATF_TEST_CASE_HEAD(create_cookie_from_cleanup) 214{ 215} 216ATF_TEST_CASE_BODY(create_cookie_from_cleanup) 217{ 218} 219ATF_TEST_CASE_CLEANUP(create_cookie_from_cleanup) 220{ 221 create_cookie(this, "control_dir", "cookie"); 222} 223 224 225ATF_TEST_CASE_WITH_CLEANUP(expect_timeout); 226ATF_TEST_CASE_HEAD(expect_timeout) 227{ 228 if (has_config_var("timeout")) 229 set_md_var("timeout", get_config_var("timeout")); 230} 231ATF_TEST_CASE_BODY(expect_timeout) 232{ 233 expect_timeout("Times out on purpose"); 234 ::sleep(10); 235 create_cookie(this, "control_dir", "cookie"); 236} 237ATF_TEST_CASE_CLEANUP(expect_timeout) 238{ 239 create_cookie(this, "control_dir", "cookie.cleanup"); 240} 241 242 243ATF_TEST_CASE_WITH_CLEANUP(output); 244ATF_TEST_CASE_HEAD(output) 245{ 246} 247ATF_TEST_CASE_BODY(output) 248{ 249 std::cout << "Body message to stdout\n"; 250 std::cerr << "Body message to stderr\n"; 251} 252ATF_TEST_CASE_CLEANUP(output) 253{ 254 std::cout << "Cleanup message to stdout\n"; 255 std::cerr << "Cleanup message to stderr\n"; 256} 257 258 259ATF_TEST_CASE(output_in_list); 260ATF_TEST_CASE_HEAD(output_in_list) 261{ 262 std::cerr << "Should not write anything!\n"; 263} 264ATF_TEST_CASE_BODY(output_in_list) 265{ 266} 267 268 269ATF_TEST_CASE(pass); 270ATF_TEST_CASE_HEAD(pass) 271{ 272 set_md_var("descr", "Always-passing test case"); 273} 274ATF_TEST_CASE_BODY(pass) 275{ 276} 277 278 279ATF_TEST_CASE_WITH_CLEANUP(shared_workdir); 280ATF_TEST_CASE_HEAD(shared_workdir) 281{ 282} 283ATF_TEST_CASE_BODY(shared_workdir) 284{ 285 atf::utils::create_file("shared_cookie", ""); 286} 287ATF_TEST_CASE_CLEANUP(shared_workdir) 288{ 289 if (!atf::utils::file_exists("shared_cookie")) 290 utils::abort_without_coredump(); 291} 292 293 294ATF_TEST_CASE(spawn_blocking_child); 295ATF_TEST_CASE_HEAD(spawn_blocking_child) 296{ 297 set_md_var("require.config", "control_dir"); 298} 299ATF_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 317ATF_TEST_CASE_WITH_CLEANUP(timeout_body); 318ATF_TEST_CASE_HEAD(timeout_body) 319{ 320 if (has_config_var("timeout")) 321 set_md_var("timeout", get_config_var("timeout")); 322} 323ATF_TEST_CASE_BODY(timeout_body) 324{ 325 ::sleep(10); 326 create_cookie(this, "control_dir", "cookie"); 327} 328ATF_TEST_CASE_CLEANUP(timeout_body) 329{ 330 create_cookie(this, "control_dir", "cookie.cleanup"); 331} 332 333 334ATF_TEST_CASE_WITH_CLEANUP(timeout_cleanup); 335ATF_TEST_CASE_HEAD(timeout_cleanup) 336{ 337} 338ATF_TEST_CASE_BODY(timeout_cleanup) 339{ 340} 341ATF_TEST_CASE_CLEANUP(timeout_cleanup) 342{ 343 ::sleep(10); 344 create_cookie(this, "control_dir", "cookie"); 345} 346 347 348ATF_TEST_CASE_WITHOUT_HEAD(validate_isolation); 349ATF_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 372ATF_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