1 // Copyright 2011 Google Inc. 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 <unistd.h> 33 } 34 35 #include <cstdlib> 36 #include <fstream> 37 #include <iostream> 38 #include <sstream> 39 40 #include "utils/defs.hpp" 41 #include "utils/env.hpp" 42 #include "utils/fs/operations.hpp" 43 #include "utils/fs/path.hpp" 44 #include "utils/optional.ipp" 45 46 namespace fs = utils::fs; 47 48 using utils::optional; 49 50 51 namespace { 52 53 54 /// Logs an error message and exits the test with an error code. 55 /// 56 /// \param str The error message to log. 57 static void 58 fail(const char* str) 59 { 60 std::cerr << str << '\n'; 61 std::exit(EXIT_FAILURE); 62 } 63 64 65 /// A test case that crashes. 66 static void 67 test_crash(void) 68 { 69 std::abort(); 70 } 71 72 73 /// A test case that exits with a non-zero exit code, and not 1. 74 static void 75 test_fail(void) 76 { 77 std::exit(8); 78 } 79 80 81 /// A test case that passes. 82 static void 83 test_pass(void) 84 { 85 } 86 87 88 /// A test case that spawns a subchild that gets stuck. 89 /// 90 /// This test case is used by the caller to validate that the whole process tree 91 /// is terminated when the test case is killed. 92 static void 93 test_spawn_blocking_child(void) 94 { 95 pid_t pid = ::fork(); 96 if (pid == -1) 97 fail("Cannot fork subprocess"); 98 else if (pid == 0) { 99 for (;;) 100 ::pause(); 101 } else { 102 const fs::path name = fs::path(utils::getenv("CONTROL_DIR").get()) / 103 "pid"; 104 std::ofstream pidfile(name.c_str()); 105 if (!pidfile) 106 fail("Failed to create the pidfile"); 107 pidfile << pid; 108 pidfile.close(); 109 } 110 } 111 112 113 /// A test case that times out. 114 /// 115 /// Note that the timeout is defined in the Kyuafile, as the plain interface has 116 /// no means for test programs to specify this by themselves. 117 static void 118 test_timeout(void) 119 { 120 ::sleep(10); 121 const fs::path control_dir = fs::path(utils::getenv("CONTROL_DIR").get()); 122 std::ofstream file((control_dir / "cookie").c_str()); 123 if (!file) 124 fail("Failed to create the control cookie"); 125 file.close(); 126 } 127 128 129 /// A test case that performs basic checks on the runtime environment. 130 /// 131 /// If the runtime environment does not look clean (according to the rules in 132 /// the Kyua runtime properties), the test fails. 133 static void 134 test_validate_isolation(void) 135 { 136 if (utils::getenv("HOME").get() == "fake-value") 137 fail("HOME not reset"); 138 if (utils::getenv("LANG")) 139 fail("LANG not unset"); 140 } 141 142 143 } // anonymous namespace 144 145 146 /// Entry point to the test program. 147 /// 148 /// The caller can select which test case to run by defining the TEST_CASE 149 /// environment variable. This is not "standard", in the sense this is not a 150 /// generic property of the plain test case interface. 151 /// 152 /// \todo It may be worth to split this binary into separate, smaller binaries, 153 /// one for every "test case". We use this program as a dispatcher for 154 /// different "main"s, the only reason being to keep the amount of helper test 155 /// programs to a minimum. However, putting this each function in its own 156 /// binary could simplify many other things. 157 /// 158 /// \param argc The number of CLI arguments. 159 /// \param unused_argv The CLI arguments themselves. These are not used because 160 /// Kyua will not pass any arguments to the plain test program. 161 int 162 main(int argc, char** UTILS_UNUSED_PARAM(argv)) 163 { 164 if (argc != 1) { 165 std::cerr << "No arguments allowed; select the test case with the " 166 "TEST_CASE variable"; 167 return EXIT_FAILURE; 168 } 169 170 const optional< std::string > test_case_env = utils::getenv("TEST_CASE"); 171 if (!test_case_env) { 172 std::cerr << "TEST_CASE not defined"; 173 return EXIT_FAILURE; 174 } 175 const std::string& test_case = test_case_env.get(); 176 177 if (test_case == "crash") 178 test_crash(); 179 else if (test_case == "fail") 180 test_fail(); 181 else if (test_case == "pass") 182 test_pass(); 183 else if (test_case == "spawn_blocking_child") 184 test_spawn_blocking_child(); 185 else if (test_case == "timeout") 186 test_timeout(); 187 else if (test_case == "validate_isolation") 188 test_validate_isolation(); 189 else { 190 std::cerr << "Unknown test case"; 191 return EXIT_FAILURE; 192 } 193 194 return EXIT_SUCCESS; 195 } 196