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