1 // Copyright 2012 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 #include <sys/wait.h> 30 31 #include <assert.h> 32 #include <err.h> 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <string.h> 36 37 #include "cli.h" 38 #include "defs.h" 39 #include "error.h" 40 #include "result.h" 41 #include "run.h" 42 #include "stacktrace.h" 43 44 #if !defined(WCOREDUMP) && defined(__minix) 45 #define WCOREDUMP(x) ((x) & 0x80) 46 #endif /*!defined(WCOREDUMP) && defined(__minix) */ 47 48 49 /// Template for the creation of the temporary work directories. 50 #define WORKDIR_TEMPLATE "kyua.plain-tester.XXXXXX" 51 52 53 /// Name of the fake test case exposed by the program. 54 const char* const fake_test_case_name = "main"; 55 56 57 /// Converts the exit status of a program to a result. 58 /// 59 /// \param status Exit status of the test program, as returned by waitpid(). 60 /// \param timed_out Whether the test program timed out or not. 61 /// \param result_file Path to the result file to create. 62 /// \param [out] success Set to true if the test program returned with a 63 /// successful condition. 64 /// 65 /// \return An error if something went wrong. 66 static kyua_error_t 67 status_to_result(int status, const bool timed_out, const char* result_file, 68 bool* success) 69 { 70 if (timed_out) { 71 *success = false; 72 return kyua_result_write(result_file, KYUA_RESULT_BROKEN, 73 "Test case timed out"); 74 } 75 76 if (WIFEXITED(status)) { 77 if (WEXITSTATUS(status) == EXIT_SUCCESS) { 78 *success = true; 79 return kyua_result_write(result_file, KYUA_RESULT_PASSED, NULL); 80 } else { 81 *success = false; 82 return kyua_result_write(result_file, KYUA_RESULT_FAILED, 83 "Returned non-success exit status %d", 84 WEXITSTATUS(status)); 85 } 86 } else { 87 assert(WIFSIGNALED(status)); 88 *success = false; 89 return kyua_result_write(result_file, KYUA_RESULT_BROKEN, 90 "Received signal %d", WTERMSIG(status)); 91 } 92 } 93 94 95 /// Lists the test cases in a test program. 96 /// 97 /// \param unused_test_program Path to the test program for which to list the 98 /// test cases. Should be absolute. 99 /// \param unused_run_params Execution parameters to configure the test process. 100 /// 101 /// \return An error if the listing fails; OK otherwise. 102 static kyua_error_t 103 list_test_cases(const char* KYUA_DEFS_UNUSED_PARAM(test_program), 104 const kyua_run_params_t* KYUA_DEFS_UNUSED_PARAM(run_params)) 105 { 106 printf("test_case{name='%s'}\n", fake_test_case_name); 107 return kyua_error_ok(); 108 } 109 110 111 /// Runs a single test cases of a test program. 112 /// 113 /// \param test_program Path to the test program for which to list the test 114 /// cases. Should be absolute. 115 /// \param test_case Name of the test case to run. 116 /// \param result_file Path to the file to which to write the result of the 117 /// test. Should be absolute. 118 /// \param user_variables Array of name=value pairs that describe the user 119 /// configuration variables for the test case. 120 /// \param run_params Execution parameters to configure the test process. 121 /// \param [out] success Set to true if the test case reported a valid exit 122 /// condition (like "passed" or "skipped"); false otherwise. This is 123 /// only updated if the method returns OK. 124 /// 125 /// \return An error if the listing fails; OK otherwise. 126 static kyua_error_t 127 run_test_case(const char* test_program, const char* test_case, 128 const char* result_file, const char* const user_variables[], 129 const kyua_run_params_t* run_params, 130 bool* success) 131 { 132 kyua_error_t error; 133 134 if (strcmp(test_case, fake_test_case_name) != 0) { 135 error = kyua_generic_error_new("Unknown test case '%s'", test_case); 136 goto out; 137 } 138 139 const char* const* iter; 140 for (iter = user_variables; *iter != NULL; ++iter) { 141 warnx("Configuration variables not supported; ignoring '%s'", *iter); 142 } 143 144 char *work_directory; 145 error = kyua_run_work_directory_enter(WORKDIR_TEMPLATE, 146 run_params->unprivileged_user, 147 run_params->unprivileged_group, 148 &work_directory); 149 if (kyua_error_is_set(error)) 150 goto out; 151 kyua_run_params_t real_run_params = *run_params; 152 real_run_params.work_directory = work_directory; 153 154 pid_t pid; 155 error = kyua_run_fork(&real_run_params, &pid); 156 if (!kyua_error_is_set(error) && pid == 0) { 157 const char* const program_args[] = { test_program, NULL }; 158 kyua_run_exec(test_program, program_args); 159 } 160 assert(pid != -1 && pid != 0); 161 if (kyua_error_is_set(error)) 162 goto out_work_directory; 163 164 int status; bool timed_out; 165 error = kyua_run_wait(pid, &status, &timed_out); 166 if (kyua_error_is_set(error)) 167 goto out_work_directory; 168 169 if (WIFSIGNALED(status) && WCOREDUMP(status)) { 170 kyua_stacktrace_dump(test_program, pid, run_params, stderr); 171 } 172 173 error = status_to_result(status, timed_out, result_file, success); 174 175 out_work_directory: 176 error = kyua_error_subsume(error, 177 kyua_run_work_directory_leave(&work_directory)); 178 out: 179 return error; 180 } 181 182 183 /// Definition of the tester. 184 static kyua_cli_tester_t plain_tester = { 185 .list_test_cases = list_test_cases, 186 .run_test_case = run_test_case, 187 }; 188 189 190 /// Tester entry point. 191 /// 192 /// \param argc Number of command line arguments. 193 /// \param argv NULL-terminated array of command line arguments. 194 /// 195 /// \return An exit code. 196 int 197 main(const int argc, char* const* const argv) 198 { 199 return kyua_cli_main(argc, argv, &plain_tester); 200 } 201