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 <errno.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <unistd.h> 37 38 #include "atf_list.h" 39 #include "atf_result.h" 40 #include "cli.h" 41 #include "defs.h" 42 #include "error.h" 43 #include "fs.h" 44 #include "run.h" 45 #include "stacktrace.h" 46 #include "text.h" 47 48 49 /// Template for the creation of the temporary work directories. 50 #define WORKDIR_TEMPLATE "kyua.atf-tester.XXXXXX" 51 52 53 static void run_list(const char*, const int[2]) KYUA_DEFS_NORETURN; 54 55 56 /// Executes the test program in list mode. 57 /// 58 /// \param test_program Path to the test program to execute; should be absolute. 59 /// \param stdout_fds Pipe to write the output of the test program to. 60 static void 61 run_list(const char* test_program, const int stdout_fds[2]) 62 { 63 (void)close(stdout_fds[0]); 64 65 if (stdout_fds[1] != STDOUT_FILENO) { 66 if (dup2(stdout_fds[1], STDOUT_FILENO) == -1) 67 err(EXIT_FAILURE, "dup2 failed"); 68 (void)close(stdout_fds[1]); 69 } 70 71 if (dup2(STDOUT_FILENO, STDERR_FILENO) == -1) 72 err(EXIT_FAILURE, "dup2 failed"); 73 74 const char* const program_args[] = { test_program, "-l", NULL }; 75 kyua_run_exec(test_program, program_args); 76 } 77 78 79 /// Dumps the contents of the input file into the output. 80 /// 81 /// \param input File from which to read. 82 /// \param output File to which to write. 83 /// 84 /// \return An error if there is a problem. 85 static kyua_error_t 86 dump_file(FILE* input, FILE* output) 87 { 88 char buffer[1024]; 89 size_t length; 90 91 while ((length = fread(buffer, 1, sizeof(buffer), input)) > 0) { 92 if (fwrite(buffer, 1, length, output) != length) { 93 return kyua_generic_error_new("Failed to write to output file"); 94 } 95 } 96 if (ferror(input)) 97 return kyua_libc_error_new(errno, "Failed to read test cases list"); 98 99 return kyua_error_ok(); 100 } 101 102 103 /// Creates a file within the work directory. 104 /// 105 /// \param work_directory Path to the work directory. 106 /// \param name Name of the file to create. 107 /// \param mode Mode of the file, as specified by fopen(3). 108 /// \param [out] file Pointer to the created stream. 109 /// 110 /// \return An error if there is a problem. 111 static kyua_error_t 112 create_file_in_work_directory(const char* work_directory, const char* name, 113 const char* mode, FILE** file) 114 { 115 char* path; 116 kyua_error_t error = kyua_fs_concat(&path, work_directory, name, NULL); 117 if (kyua_error_is_set(error)) 118 goto out; 119 120 FILE* tmp_file = fopen(path, mode); 121 if (tmp_file == NULL) { 122 error = kyua_libc_error_new(errno, "Failed to create %s", path); 123 goto out_path; 124 } 125 126 *file = tmp_file; 127 128 assert(!kyua_error_is_set(error)); 129 out_path: 130 free(path); 131 out: 132 return error; 133 } 134 135 136 /// Lists the test cases in a test program. 137 /// 138 /// \param test_program Path to the test program for which to list the test 139 /// cases. Should be absolute. 140 /// \param run_params Execution parameters to configure the test process. 141 /// 142 /// \return An error if the listing fails; OK otherwise. 143 static kyua_error_t 144 list_test_cases(const char* test_program, const kyua_run_params_t* run_params) 145 { 146 kyua_error_t error; 147 148 char* work_directory; 149 error = kyua_run_work_directory_enter(WORKDIR_TEMPLATE, 150 run_params->unprivileged_user, 151 run_params->unprivileged_group, 152 &work_directory); 153 if (kyua_error_is_set(error)) 154 goto out; 155 kyua_run_params_t real_run_params = *run_params; 156 real_run_params.work_directory = work_directory; 157 158 int stdout_fds[2]; 159 if (pipe(stdout_fds) == -1) { 160 error = kyua_libc_error_new(errno, "pipe failed"); 161 goto out_work_directory; 162 } 163 164 pid_t pid; 165 error = kyua_run_fork(&real_run_params, &pid); 166 if (!kyua_error_is_set(error) && pid == 0) { 167 run_list(test_program, stdout_fds); 168 } 169 assert(pid != -1 && pid != 0); 170 if (kyua_error_is_set(error)) 171 goto out_stdout_fds; 172 173 FILE* tmp_output = NULL; // Initialize to shut up gcc warning. 174 error = create_file_in_work_directory(real_run_params.work_directory, 175 "list.txt", "w+", &tmp_output); 176 if (kyua_error_is_set(error)) 177 goto out_stdout_fds; 178 179 close(stdout_fds[1]); stdout_fds[1] = -1; 180 kyua_error_t parse_error = atf_list_parse(stdout_fds[0], tmp_output); 181 stdout_fds[0] = -1; // Guaranteed closed by atf_list_parse. 182 // Delay reporting of parse errors to later. If we detect a problem while 183 // waiting for the test program, we know that the parsing has most likely 184 // failed and therefore the error with the program is more important for 185 // reporting purposes. 186 187 int status; bool timed_out; 188 error = kyua_run_wait(pid, &status, &timed_out); 189 if (kyua_error_is_set(error)) 190 goto out_tmp_output; 191 if (!WIFEXITED(status) || WEXITSTATUS(status) != EXIT_SUCCESS) { 192 error = kyua_generic_error_new("Test program list did not return " 193 "success"); 194 goto out_tmp_output; 195 } 196 197 error = kyua_error_subsume(error, parse_error); 198 if (!kyua_error_is_set(error)) { 199 rewind(tmp_output); 200 error = dump_file(tmp_output, stdout); 201 } 202 203 out_tmp_output: 204 fclose(tmp_output); 205 out_stdout_fds: 206 if (stdout_fds[0] != -1) 207 close(stdout_fds[0]); 208 if (stdout_fds[1] != -1) 209 close(stdout_fds[1]); 210 out_work_directory: 211 error = kyua_error_subsume(error, 212 kyua_run_work_directory_leave(&work_directory)); 213 out: 214 return error; 215 } 216 217 218 /// Counts the length of a user variables array. 219 /// 220 /// \param user_variables The array of elements to be counted. 221 /// 222 /// \return The length of the array. 223 static size_t 224 count_variables(const char* const user_variables[]) 225 { 226 size_t count = 0; 227 const char* const* iter; 228 for (iter = user_variables; *iter != NULL; ++iter) 229 count++; 230 return count; 231 } 232 233 234 static void exec_body(const char* test_program, const char* test_case, 235 const char* result_file, 236 const char* const user_variables[]) KYUA_DEFS_NORETURN; 237 238 239 /// Executes the body of a test case. 240 /// 241 /// \param test_program Path to the test program to execute. 242 /// \param test_case Name of the test case to run. 243 /// \param result_file Path to the ATF result file to be created. 244 /// \param user_variables Set of configuration variables to pass to the test. 245 static void 246 exec_body(const char* test_program, const char* test_case, 247 const char* result_file, const char* const user_variables[]) 248 { 249 const size_t nargs = 250 1 /* test_program */ + 251 2 /* -r result_file */ 252 + 2 * count_variables(user_variables) /* -v name=value */ 253 + 1 /* test_case */ + 254 1 /* NULL */; 255 256 const char** args = malloc(sizeof(const char*) * nargs); 257 if (args == NULL) 258 kyua_error_err(EXIT_FAILURE, kyua_oom_error_new(), 259 "Failed to construct arguments list"); 260 261 size_t i = 0; 262 args[i++] = test_program; 263 args[i++] = "-r"; 264 args[i++] = result_file; 265 const char* const* iter; 266 for (iter = user_variables; *iter != NULL; ++iter) { 267 args[i++] = "-v"; 268 args[i++] = *iter; 269 } 270 args[i++] = test_case; 271 args[i++] = NULL; 272 assert(i == nargs); 273 274 kyua_run_exec(test_program, args); 275 } 276 277 278 /// Forks and executes the body of a test case in a controlled manner. 279 /// 280 /// \param test_program Path to the test program to execute. 281 /// \param test_case Name of the test case to run. 282 /// \param result_file Path to the ATF result file to be created. 283 /// \param user_variables Set of configuration variables to pass to the test. 284 /// \param run_params Settings to control the subprocess. 285 /// \param [out] success Set to true if the test case runs properly and returns 286 /// a result that is to be considered as successful. 287 /// 288 /// \return OK if all goes well, an error otherwise. Note that a failed test 289 /// case is denoted by setting success to false on exit, not by returning an 290 /// error. 291 static kyua_error_t 292 run_body(const char* test_program, const char* test_case, 293 const char* result_file, const char* const user_variables[], 294 const kyua_run_params_t* run_params, bool* success) 295 { 296 kyua_error_t error; 297 298 char* tmp_result_file; 299 error = kyua_fs_concat(&tmp_result_file, run_params->work_directory, 300 "result.txt", NULL); 301 if (kyua_error_is_set(error)) 302 goto out; 303 304 pid_t pid; 305 error = kyua_run_fork(run_params, &pid); 306 if (!kyua_error_is_set(error) && pid == 0) { 307 exec_body(test_program, test_case, tmp_result_file, user_variables); 308 } 309 assert(pid != -1 && pid != 0); 310 if (kyua_error_is_set(error)) 311 goto out_tmp_result_file; 312 313 int status; bool timed_out; 314 error = kyua_run_wait(pid, &status, &timed_out); 315 if (kyua_error_is_set(error)) 316 goto out_tmp_result_file; 317 318 if (WIFSIGNALED(status) && WCOREDUMP(status)) { 319 kyua_stacktrace_dump(test_program, pid, run_params, stderr); 320 } 321 322 error = kyua_atf_result_rewrite(tmp_result_file, result_file, status, 323 timed_out, success); 324 325 out_tmp_result_file: 326 free(tmp_result_file); 327 out: 328 return error; 329 } 330 331 332 static void exec_cleanup(const char* test_program, const char* test_case, 333 const char* const user_variables[]) KYUA_DEFS_NORETURN; 334 335 336 /// Executes the cleanup of a test case. 337 /// 338 /// \param test_program Path to the test program to execute. 339 /// \param test_case Name of the test case to run. 340 /// \param user_variables Set of configuration variables to pass to the test. 341 static void 342 exec_cleanup(const char* test_program, const char* test_case, 343 const char* const user_variables[]) 344 { 345 char* name; 346 kyua_error_t error = kyua_text_printf(&name, "%s:cleanup", test_case); 347 if (kyua_error_is_set(error)) 348 kyua_error_err(EXIT_FAILURE, error, 349 "Failed to construct argument list"); 350 351 const size_t nargs = 352 1 /* test_program */ + 353 + 2 * count_variables(user_variables) /* -v name=value */ 354 + 1 /* test_case */ + 355 1 /* NULL */; 356 357 const char** args = malloc(sizeof(const char*) * nargs); 358 if (args == NULL) 359 kyua_error_err(EXIT_FAILURE, kyua_oom_error_new(), 360 "Failed to construct arguments list"); 361 362 size_t i = 0; 363 args[i++] = test_program; 364 const char* const* iter; 365 for (iter = user_variables; *iter != NULL; ++iter) { 366 args[i++] = "-v"; 367 args[i++] = *iter; 368 } 369 args[i++] = name; 370 args[i++] = NULL; 371 assert(i == nargs); 372 373 kyua_run_exec(test_program, args); 374 } 375 376 377 /// Forks and executes the cleanup of a test case in a controlled manner. 378 /// 379 /// \param test_program Path to the test program to execute. 380 /// \param test_case Name of the test case to run. 381 /// \param result_file Path to the ATF result file created by the body of the 382 /// test case. The cleanup may update such file if it fails. 383 /// \param user_variables Set of configuration variables to pass to the test. 384 /// \param run_params Settings to control the subprocess. 385 /// \param body_success The success value returned by run_body(). 386 /// \param [out] success Set to true if the test case runs properly and returns 387 /// a result that is to be considered as successful. 388 /// 389 /// \return OK if all goes well, an error otherwise. Note that a failed test 390 /// case cleanup is denoted by setting success to false on exit, not by 391 /// returning an error. 392 static kyua_error_t 393 run_cleanup(const char* test_program, const char* test_case, 394 const char* result_file, const char* const user_variables[], 395 const kyua_run_params_t* run_params, const bool body_success, 396 bool* success) 397 { 398 kyua_error_t error; 399 400 pid_t pid; 401 error = kyua_run_fork(run_params, &pid); 402 if (!kyua_error_is_set(error) && pid == 0) { 403 exec_cleanup(test_program, test_case, user_variables); 404 } 405 assert(pid != -1 && pid != 0); 406 if (kyua_error_is_set(error)) 407 goto out; 408 409 int status; bool timed_out; 410 error = kyua_run_wait(pid, &status, &timed_out); 411 if (kyua_error_is_set(error)) 412 goto out; 413 414 if (WIFSIGNALED(status) && WCOREDUMP(status)) { 415 kyua_stacktrace_dump(test_program, pid, run_params, stderr); 416 } 417 418 if (body_success) { 419 // If the body has reported a successful result, we inspect the status 420 // of the cleanup routine. If the cleanup has failed, then we need to 421 // mark the test as broken. However, if the body itself had failed, we 422 // don't do this to give preference to the original result, which is 423 // probably more informative. 424 error = kyua_atf_result_cleanup_rewrite(result_file, status, 425 timed_out, success); 426 } 427 428 out: 429 return error; 430 } 431 432 433 /// Checks if the user variables indicate that a test has a cleanup routine. 434 /// 435 /// This is an ugly hack to allow us to run the cleanup routine only when a test 436 /// case has it. When Kyua invokes the tester to generate the test case list, 437 /// the tester tells Kyua which tests have a cleanup routine. However, when the 438 /// tests are later run from here (as a different invocation) we cannot know if 439 /// the test had a cleanup routine or not. We rely on Kyua telling us this fact 440 /// by specifying has.cleanup=true in the variables. 441 /// 442 /// \param user_variables Array of name=value pairs that describe the user 443 /// configuration variables for the test case. 444 static bool 445 has_cleanup(const char* const* user_variables) 446 { 447 const char* const* iter; 448 for (iter = user_variables; *iter != NULL; ++iter) { 449 if (strcmp(*iter, "has.cleanup=false") == 0) 450 return false; 451 } 452 453 // The default is true because not running a cleanup routine when it exists 454 // is worse than running an empty routine when not told to do so. 455 return true; 456 } 457 458 459 /// Runs a single test cases of a test program. 460 /// 461 /// \param test_program Path to the test program for which to list the test 462 /// cases. Should be absolute. 463 /// \param test_case Name of the test case to run. 464 /// \param result_file Path to the file to which to write the result of the 465 /// test. Should be absolute. 466 /// \param user_variables Array of name=value pairs that describe the user 467 /// configuration variables for the test case. 468 /// \param run_params Execution parameters to configure the test process. 469 /// \param [out] success Set to true if the test case reported a valid exit 470 /// condition (like "passed" or "skipped"); false otherwise. This is 471 /// only updated if the method returns OK. 472 /// 473 /// \return An error if the listing fails; OK otherwise. 474 static kyua_error_t 475 run_test_case(const char* test_program, const char* test_case, 476 const char* result_file, const char* const user_variables[], 477 const kyua_run_params_t* run_params, bool* success) 478 { 479 kyua_error_t error; 480 481 char* work_directory; 482 error = kyua_run_work_directory_enter(WORKDIR_TEMPLATE, 483 run_params->unprivileged_user, 484 run_params->unprivileged_group, 485 &work_directory); 486 if (kyua_error_is_set(error)) 487 goto out; 488 kyua_run_params_t real_run_params = *run_params; 489 real_run_params.work_directory = work_directory; 490 491 error = run_body(test_program, test_case, result_file, user_variables, 492 &real_run_params, success); 493 if (has_cleanup(user_variables)) { 494 error = run_cleanup(test_program, test_case, result_file, 495 user_variables, &real_run_params, *success, 496 success); 497 } 498 499 error = kyua_error_subsume(error, 500 kyua_run_work_directory_leave(&work_directory)); 501 out: 502 return error; 503 } 504 505 506 /// Definition of the tester. 507 static kyua_cli_tester_t atf_tester = { 508 .list_test_cases = list_test_cases, 509 .run_test_case = run_test_case, 510 }; 511 512 513 /// Tester entry point. 514 /// 515 /// \param argc Number of command line arguments. 516 /// \param argv NULL-terminated array of command line arguments. 517 /// 518 /// \return An exit code. 519 int 520 main(const int argc, char* const* const argv) 521 { 522 return kyua_cli_main(argc, argv, &atf_tester); 523 } 524