1 1.1 jmmv // Copyright 2012 Google Inc. 2 1.1 jmmv // All rights reserved. 3 1.1 jmmv // 4 1.1 jmmv // Redistribution and use in source and binary forms, with or without 5 1.1 jmmv // modification, are permitted provided that the following conditions are 6 1.1 jmmv // met: 7 1.1 jmmv // 8 1.1 jmmv // * Redistributions of source code must retain the above copyright 9 1.1 jmmv // notice, this list of conditions and the following disclaimer. 10 1.1 jmmv // * Redistributions in binary form must reproduce the above copyright 11 1.1 jmmv // notice, this list of conditions and the following disclaimer in the 12 1.1 jmmv // documentation and/or other materials provided with the distribution. 13 1.1 jmmv // * Neither the name of Google Inc. nor the names of its contributors 14 1.1 jmmv // may be used to endorse or promote products derived from this software 15 1.1 jmmv // without specific prior written permission. 16 1.1 jmmv // 17 1.1 jmmv // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 1.1 jmmv // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 1.1 jmmv // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 1.1 jmmv // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 1.1 jmmv // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 1.1 jmmv // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 1.1 jmmv // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 1.1 jmmv // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 1.1 jmmv // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 1.1 jmmv // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 1.1 jmmv // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 1.1 jmmv 29 1.1 jmmv #include "atf_result.h" 30 1.1 jmmv 31 1.1 jmmv #include <sys/types.h> 32 1.1 jmmv #include <sys/wait.h> 33 1.1 jmmv 34 1.1 jmmv #include <assert.h> 35 1.1 jmmv #include <errno.h> 36 1.1 jmmv #include <limits.h> 37 1.1 jmmv #include <stdbool.h> 38 1.1 jmmv #include <stdio.h> 39 1.1 jmmv #include <stdlib.h> 40 1.1 jmmv #include <string.h> 41 1.1 jmmv 42 1.1 jmmv #include "error.h" 43 1.1 jmmv #include "result.h" 44 1.1 jmmv 45 1.1 jmmv 46 1.1 jmmv // Enumeration of the different result types returned by an ATF test case. 47 1.1 jmmv enum atf_status { 48 1.1 jmmv ATF_STATUS_EXPECTED_DEATH, 49 1.1 jmmv ATF_STATUS_EXPECTED_EXIT, 50 1.1 jmmv ATF_STATUS_EXPECTED_FAILURE, 51 1.1 jmmv ATF_STATUS_EXPECTED_SIGNAL, 52 1.1 jmmv ATF_STATUS_EXPECTED_TIMEOUT, 53 1.1 jmmv ATF_STATUS_FAILED, 54 1.1 jmmv ATF_STATUS_PASSED, 55 1.1 jmmv ATF_STATUS_SKIPPED, 56 1.1 jmmv 57 1.1 jmmv // The broken status below is never returned by the test cases themselves. 58 1.1 jmmv // We use it internally to pass around problems detected while dealing with 59 1.1 jmmv // the test case itself (like an invalid result file). 60 1.1 jmmv ATF_STATUS_BROKEN, 61 1.1 jmmv }; 62 1.1 jmmv 63 1.1 jmmv 64 1.1 jmmv /// Magic number representing a missing argument to the test result status. 65 1.1 jmmv /// 66 1.1 jmmv /// Use this to specify that an expected_exit or expected_signal result accepts 67 1.1 jmmv /// any exit code or signal, respectively. 68 1.1 jmmv #define NO_STATUS_ARG -1 69 1.1 jmmv 70 1.1 jmmv 71 1.1 jmmv /// Removes a trailing newline from a string (supposedly read by fgets(3)). 72 1.1 jmmv /// 73 1.1 jmmv /// \param [in,out] str The string to remove the trailing newline from. 74 1.1 jmmv /// 75 1.1 jmmv /// \return True if there was a newline character; false otherwise. 76 1.1 jmmv static bool 77 1.1 jmmv trim_newline(char* str) 78 1.1 jmmv { 79 1.1 jmmv const size_t length = strlen(str); 80 1.1 jmmv if (length == 0) { 81 1.1 jmmv return false; 82 1.1 jmmv } else { 83 1.1 jmmv if (str[length - 1] == '\n') { 84 1.1 jmmv str[length - 1] = '\0'; 85 1.1 jmmv return true; 86 1.1 jmmv } else { 87 1.1 jmmv return false; 88 1.1 jmmv } 89 1.1 jmmv } 90 1.1 jmmv } 91 1.1 jmmv 92 1.1 jmmv 93 1.1 jmmv /// Force read on stream to see if we are really at EOF. 94 1.1 jmmv /// 95 1.1 jmmv /// A call to fgets(3) will not return EOF when it returns valid data. But 96 1.1 jmmv /// because of our semantics below, we need to be able to tell if more lines are 97 1.1 jmmv /// available before actually reading them. 98 1.1 jmmv /// 99 1.1 jmmv /// \param input The stream to check for EOF. 100 1.1 jmmv /// 101 1.1 jmmv /// \return True if the stream is not at EOF yet; false otherwise. 102 1.1 jmmv static bool 103 1.1 jmmv is_really_eof(FILE* input) 104 1.1 jmmv { 105 1.1 jmmv const int ch = getc(input); 106 1.1 jmmv const bool real_eof = feof(input); 107 1.1 jmmv (void)ungetc(ch, input); 108 1.1 jmmv return real_eof; 109 1.1 jmmv } 110 1.1 jmmv 111 1.1 jmmv 112 1.1 jmmv /// Parses the optional argument to a result status. 113 1.1 jmmv /// 114 1.1 jmmv /// \param str Pointer to the argument. May be \0 in those cases where the 115 1.1 jmmv /// status does not have any argument. 116 1.1 jmmv /// \param [out] status_arg Value of the parsed argument. 117 1.1 jmmv /// 118 1.1 jmmv /// \return OK if the argument exists and is valid, or if it does not exist; an 119 1.1 jmmv /// error otherwise. 120 1.1 jmmv static kyua_error_t 121 1.1 jmmv parse_status_arg(const char* str, int* status_arg) 122 1.1 jmmv { 123 1.1 jmmv if (*str == '\0') { 124 1.1 jmmv *status_arg = NO_STATUS_ARG; 125 1.1 jmmv return kyua_error_ok(); 126 1.1 jmmv } 127 1.1 jmmv 128 1.1 jmmv const size_t length = strlen(str); 129 1.1 jmmv if (*str != '(' || *(str + length - 1) != ')') 130 1.1 jmmv return kyua_generic_error_new("Invalid status argument %s", str); 131 1.1 jmmv const char* const arg = str + 1; 132 1.1 jmmv 133 1.1 jmmv char* endptr; 134 1.1 jmmv const long value = strtol(arg, &endptr, 10); 135 1.1 jmmv if (arg[0] == '\0' || endptr != str + length - 1) 136 1.1 jmmv return kyua_generic_error_new("Invalid status argument %s: not a " 137 1.1 jmmv "number", str); 138 1.1 jmmv if (errno == ERANGE && (value == LONG_MAX || value == LONG_MIN)) 139 1.1 jmmv return kyua_generic_error_new("Invalid status argument %s: out of " 140 1.1 jmmv "range", str); 141 1.1 jmmv if (value < INT_MIN || value > INT_MAX) 142 1.1 jmmv return kyua_generic_error_new("Invalid status argument %s: out of " 143 1.1 jmmv "range", str); 144 1.1 jmmv 145 1.1 jmmv *status_arg = (int)value; 146 1.1 jmmv return kyua_error_ok(); 147 1.1 jmmv } 148 1.1 jmmv 149 1.1 jmmv 150 1.1 jmmv /// Parses a textual result status. 151 1.1 jmmv /// 152 1.1 jmmv /// \param str The text to parse. 153 1.1 jmmv /// \param [out] status Status type if the input is valid. 154 1.1 jmmv /// \param [out] status_arg Optional integral argument to the status. 155 1.1 jmmv /// \param [out] need_reason Whether the detected status requires a reason. 156 1.1 jmmv /// 157 1.1 jmmv /// \return An error if the status is not valid. 158 1.1 jmmv static kyua_error_t 159 1.1 jmmv parse_status(const char* str, enum atf_status* status, int* status_arg, 160 1.1 jmmv bool* need_reason) 161 1.1 jmmv { 162 1.1 jmmv if (strcmp(str, "passed") == 0) { 163 1.1 jmmv *status = ATF_STATUS_PASSED; 164 1.1 jmmv *need_reason = false; 165 1.1 jmmv return kyua_error_ok(); 166 1.1 jmmv } else if (strcmp(str, "failed") == 0) { 167 1.1 jmmv *status = ATF_STATUS_FAILED; 168 1.1 jmmv *need_reason = true; 169 1.1 jmmv return kyua_error_ok(); 170 1.1 jmmv } else if (strcmp(str, "skipped") == 0) { 171 1.1 jmmv *status = ATF_STATUS_SKIPPED; 172 1.1 jmmv *need_reason = true; 173 1.1 jmmv return kyua_error_ok(); 174 1.1 jmmv } else if (strcmp(str, "expected_death") == 0) { 175 1.1 jmmv *status = ATF_STATUS_EXPECTED_DEATH; 176 1.1 jmmv *need_reason = true; 177 1.1 jmmv return kyua_error_ok(); 178 1.1 jmmv } else if (strncmp(str, "expected_exit", 13) == 0) { 179 1.1 jmmv *status = ATF_STATUS_EXPECTED_EXIT; 180 1.1 jmmv *need_reason = true; 181 1.1 jmmv return parse_status_arg(str + 13, status_arg); 182 1.1 jmmv } else if (strcmp(str, "expected_failure") == 0) { 183 1.1 jmmv *status = ATF_STATUS_EXPECTED_FAILURE; 184 1.1 jmmv *need_reason = true; 185 1.1 jmmv return kyua_error_ok(); 186 1.1 jmmv } else if (strncmp(str, "expected_signal", 15) == 0){ 187 1.1 jmmv *status = ATF_STATUS_EXPECTED_SIGNAL; 188 1.1 jmmv *need_reason = true; 189 1.1 jmmv return parse_status_arg(str + 15, status_arg); 190 1.1 jmmv } else if (strcmp(str, "expected_timeout") == 0) { 191 1.1 jmmv *status = ATF_STATUS_EXPECTED_TIMEOUT; 192 1.1 jmmv *need_reason = true; 193 1.1 jmmv return kyua_error_ok(); 194 1.1 jmmv } else { 195 1.1 jmmv return kyua_generic_error_new("Unknown test case result status %s", 196 1.1 jmmv str); 197 1.1 jmmv } 198 1.1 jmmv } 199 1.1 jmmv 200 1.1 jmmv 201 1.1 jmmv /// Advances a pointer to a buffer to its end. 202 1.1 jmmv /// 203 1.1 jmmv /// \param [in,out] buffer Current buffer contents; updated on exit to point to 204 1.1 jmmv /// the termination character. 205 1.1 jmmv /// \param [in,out] buffer_size Current buffer size; updated on exit to account 206 1.1 jmmv /// for the decreased capacity due to the pointer increase. 207 1.1 jmmv static void 208 1.1 jmmv advance(char** buffer, size_t* buffer_size) 209 1.1 jmmv { 210 1.1 jmmv const size_t increment = strlen(*buffer); 211 1.1 jmmv *buffer += increment; 212 1.1 jmmv *buffer_size -= increment; 213 1.1 jmmv } 214 1.1 jmmv 215 1.1 jmmv 216 1.1 jmmv /// Extracts the result reason from the input file. 217 1.1 jmmv /// 218 1.1 jmmv /// \pre This can only be called for those result types that require a reason. 219 1.1 jmmv /// 220 1.1 jmmv /// \param [in,out] input The file from which to read. 221 1.1 jmmv /// \param first_line The first line of the reason. Because this is part of the 222 1.1 jmmv /// same line in which the result status is printed, this line has already 223 1.1 jmmv /// been read by the caller and thus must be provided here. 224 1.1 jmmv /// \param [out] output Buffer to which to write the full reason. 225 1.1 jmmv /// \param output_size Size of the output buffer. 226 1.1 jmmv /// 227 1.1 jmmv /// \return An error if there was no reason in the input or if there is a 228 1.1 jmmv /// problem reading it. 229 1.1 jmmv static kyua_error_t 230 1.1 jmmv read_reason(FILE* input, const char* first_line, char* output, 231 1.1 jmmv size_t output_size) 232 1.1 jmmv { 233 1.1 jmmv if (first_line == NULL || *first_line == '\0') 234 1.1 jmmv return kyua_generic_error_new("Test case should have reported a " 235 1.1 jmmv "failure reason but didn't"); 236 1.1 jmmv 237 1.1 jmmv snprintf(output, output_size, "%s", first_line); 238 1.1 jmmv advance(&output, &output_size); 239 1.1 jmmv 240 1.1 jmmv bool had_newline = true; 241 1.1 jmmv while (!is_really_eof(input)) { 242 1.1 jmmv if (had_newline) { 243 1.1 jmmv snprintf(output, output_size, "<<NEWLINE>>"); 244 1.1 jmmv advance(&output, &output_size); 245 1.1 jmmv } 246 1.1 jmmv 247 1.1 jmmv if (fgets(output, output_size, input) == NULL) { 248 1.1 jmmv assert(ferror(input)); 249 1.1 jmmv return kyua_libc_error_new(errno, "Failed to read reason from " 250 1.1 jmmv "result file"); 251 1.1 jmmv } 252 1.1 jmmv had_newline = trim_newline(output); 253 1.1 jmmv advance(&output, &output_size); 254 1.1 jmmv } 255 1.1 jmmv 256 1.1 jmmv return kyua_error_ok(); 257 1.1 jmmv } 258 1.1 jmmv 259 1.1 jmmv 260 1.1 jmmv /// Parses a results file written by an ATF test case. 261 1.1 jmmv /// 262 1.1 jmmv /// \param input_name Path to the result file to parse. 263 1.1 jmmv /// \param [out] status Type of result. 264 1.1 jmmv /// \param [out] status_arg Optional integral argument to the status. 265 1.1 jmmv /// \param [out] reason Textual explanation of the result, if any. 266 1.1 jmmv /// \param reason_size Length of the reason output buffer. 267 1.1 jmmv /// 268 1.1 jmmv /// \return An error if the input_name file has an invalid syntax; OK otherwise. 269 1.1 jmmv static kyua_error_t 270 1.1 jmmv read_atf_result(const char* input_name, enum atf_status* status, 271 1.1 jmmv int* status_arg, char* const reason, const size_t reason_size) 272 1.1 jmmv { 273 1.1 jmmv kyua_error_t error = kyua_error_ok(); 274 1.1 jmmv 275 1.1 jmmv FILE* input = fopen(input_name, "r"); 276 1.1 jmmv if (input == NULL) { 277 1.1 jmmv error = kyua_generic_error_new("Premature exit"); 278 1.1 jmmv goto out; 279 1.1 jmmv } 280 1.1 jmmv 281 1.1 jmmv char line[1024]; 282 1.1 jmmv if (fgets(line, sizeof(line), input) == NULL) { 283 1.1 jmmv if (ferror(input)) { 284 1.1 jmmv error = kyua_libc_error_new(errno, "Failed to read result from " 285 1.1 jmmv "file %s", input_name); 286 1.1 jmmv goto out_input; 287 1.1 jmmv } else { 288 1.1 jmmv assert(feof(input)); 289 1.1 jmmv error = kyua_generic_error_new("Empty result file %s", input_name); 290 1.1 jmmv goto out_input; 291 1.1 jmmv } 292 1.1 jmmv } 293 1.1 jmmv 294 1.1 jmmv if (!trim_newline(line)) { 295 1.1 jmmv error = kyua_generic_error_new("Missing newline in result file"); 296 1.1 jmmv goto out_input; 297 1.1 jmmv } 298 1.1 jmmv 299 1.1 jmmv char* reason_start = strstr(line, ": "); 300 1.1 jmmv if (reason_start != NULL) { 301 1.1 jmmv *reason_start = '\0'; 302 1.1 jmmv *(reason_start + 1) = '\0'; 303 1.1 jmmv reason_start += 2; 304 1.1 jmmv } 305 1.1 jmmv 306 1.1 jmmv bool need_reason = false; // Initialize to shut up gcc warning. 307 1.1 jmmv error = parse_status(line, status, status_arg, &need_reason); 308 1.1 jmmv if (kyua_error_is_set(error)) 309 1.1 jmmv goto out_input; 310 1.1 jmmv 311 1.1 jmmv if (need_reason) { 312 1.1 jmmv error = read_reason(input, reason_start, reason, reason_size); 313 1.1 jmmv } else { 314 1.1 jmmv if (reason_start != NULL || !is_really_eof(input)) { 315 1.1 jmmv error = kyua_generic_error_new("Found unexpected reason in passed " 316 1.1 jmmv "test result"); 317 1.1 jmmv goto out_input; 318 1.1 jmmv } 319 1.1 jmmv reason[0] = '\0'; 320 1.1 jmmv } 321 1.1 jmmv 322 1.1 jmmv out_input: 323 1.1 jmmv fclose(input); 324 1.1 jmmv out: 325 1.1 jmmv return error; 326 1.1 jmmv } 327 1.1 jmmv 328 1.1 jmmv 329 1.1 jmmv /// Writes a generic result file for an ATF broken result. 330 1.1 jmmv /// 331 1.1 jmmv /// \param reason Textual explanation of the result. 332 1.1 jmmv /// \param status Exit code of the test program as returned by wait(). 333 1.1 jmmv /// \param output Path to the generic result file to create. 334 1.1 jmmv /// \param [out] success Whether the result should be considered a success or 335 1.1 jmmv /// not; e.g. passed and skipped are successful, but failed is not. 336 1.1 jmmv /// 337 1.1 jmmv /// \return An error if the conversion fails; OK otherwise. 338 1.1 jmmv static kyua_error_t 339 1.1 jmmv convert_broken(const char* reason, int status, const char* output, 340 1.1 jmmv bool* success) 341 1.1 jmmv { 342 1.1 jmmv if (WIFEXITED(status)) { 343 1.1 jmmv *success = false; 344 1.1 jmmv return kyua_result_write( 345 1.1 jmmv output, KYUA_RESULT_BROKEN, "%s; test case exited with code %d", 346 1.1 jmmv reason, WEXITSTATUS(status)); 347 1.1 jmmv } else { 348 1.1 jmmv assert(WIFSIGNALED(status)); 349 1.1 jmmv *success = false; 350 1.1 jmmv return kyua_result_write( 351 1.1 jmmv output, KYUA_RESULT_BROKEN, "%s; test case received signal %d%s", 352 1.1 jmmv reason, WTERMSIG(status), 353 1.1 jmmv WCOREDUMP(status) ? " (core dumped)" : ""); 354 1.1 jmmv } 355 1.1 jmmv } 356 1.1 jmmv 357 1.1 jmmv 358 1.1 jmmv /// Writes a generic result file for an ATF expected_death result. 359 1.1 jmmv /// 360 1.1 jmmv /// \param reason Textual explanation of the result. 361 1.1 jmmv /// \param status Exit code of the test program as returned by wait(). 362 1.1 jmmv /// \param output Path to the generic result file to create. 363 1.1 jmmv /// \param [out] success Whether the result should be considered a success or 364 1.1 jmmv /// not; e.g. passed and skipped are successful, but failed is not. 365 1.1 jmmv /// 366 1.1 jmmv /// \return An error if the conversion fails; OK otherwise. 367 1.1 jmmv static kyua_error_t 368 1.1 jmmv convert_expected_death(const char* reason, int status, const char* output, 369 1.1 jmmv bool* success) 370 1.1 jmmv { 371 1.1 jmmv if (WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS) { 372 1.1 jmmv *success = false; 373 1.1 jmmv return kyua_result_write( 374 1.1 jmmv output, KYUA_RESULT_FAILED, "Test case expected to die but exited " 375 1.1 jmmv "successfully"); 376 1.1 jmmv } else { 377 1.1 jmmv *success = true; 378 1.1 jmmv return kyua_result_write( 379 1.1 jmmv output, KYUA_RESULT_EXPECTED_FAILURE, "%s", reason); 380 1.1 jmmv } 381 1.1 jmmv } 382 1.1 jmmv 383 1.1 jmmv 384 1.1 jmmv /// Writes a generic result file for an ATF expected_exit result 385 1.1 jmmv /// 386 1.1 jmmv /// \param status_arg Optional integral argument to the status. 387 1.1 jmmv /// \param reason Textual explanation of the result. 388 1.1 jmmv /// \param status Exit code of the test program as returned by wait(). 389 1.1 jmmv /// \param output Path to the generic result file to create. 390 1.1 jmmv /// \param [out] success Whether the result should be considered a success or 391 1.1 jmmv /// not; e.g. passed and skipped are successful, but failed is not. 392 1.1 jmmv /// 393 1.1 jmmv /// \return An error if the conversion fails; OK otherwise. 394 1.1 jmmv static kyua_error_t 395 1.1 jmmv convert_expected_exit(const int status_arg, const char* reason, int status, 396 1.1 jmmv const char* output, bool* success) 397 1.1 jmmv { 398 1.1 jmmv if (WIFEXITED(status)) { 399 1.1 jmmv if (status_arg == NO_STATUS_ARG || status_arg == WEXITSTATUS(status)) { 400 1.1 jmmv *success = true; 401 1.1 jmmv return kyua_result_write( 402 1.1 jmmv output, KYUA_RESULT_EXPECTED_FAILURE, "%s", reason); 403 1.1 jmmv } else { 404 1.1 jmmv *success = false; 405 1.1 jmmv return kyua_result_write( 406 1.1 jmmv output, KYUA_RESULT_FAILED, "Test case expected to exit with " 407 1.1 jmmv "code %d but got code %d", status_arg, WEXITSTATUS(status)); 408 1.1 jmmv } 409 1.1 jmmv } else { 410 1.1 jmmv assert(WIFSIGNALED(status)); 411 1.1 jmmv *success = false; 412 1.1 jmmv return kyua_result_write( 413 1.1 jmmv output, KYUA_RESULT_FAILED, "Test case expected to exit normally " 414 1.1 jmmv "but received signal %d%s", WTERMSIG(status), 415 1.1 jmmv WCOREDUMP(status) ? " (core dumped)" : ""); 416 1.1 jmmv } 417 1.1 jmmv } 418 1.1 jmmv 419 1.1 jmmv 420 1.1 jmmv /// Writes a generic result file for an ATF expected_failure result. 421 1.1 jmmv /// 422 1.1 jmmv /// \param reason Textual explanation of the result. 423 1.1 jmmv /// \param status Exit code of the test program as returned by wait(). 424 1.1 jmmv /// \param output Path to the generic result file to create. 425 1.1 jmmv /// \param [out] success Whether the result should be considered a success or 426 1.1 jmmv /// not; e.g. passed and skipped are successful, but failed is not. 427 1.1 jmmv /// 428 1.1 jmmv /// \return An error if the conversion fails; OK otherwise. 429 1.1 jmmv static kyua_error_t 430 1.1 jmmv convert_expected_failure(const char* reason, int status, const char* output, 431 1.1 jmmv bool* success) 432 1.1 jmmv { 433 1.1 jmmv if (WIFEXITED(status)) { 434 1.1 jmmv if (WEXITSTATUS(status) == EXIT_SUCCESS) { 435 1.1 jmmv *success = true; 436 1.1 jmmv return kyua_result_write( 437 1.1 jmmv output, KYUA_RESULT_EXPECTED_FAILURE, "%s", reason); 438 1.1 jmmv } else { 439 1.1 jmmv *success = false; 440 1.1 jmmv return kyua_result_write( 441 1.1 jmmv output, KYUA_RESULT_FAILED, "Test case expected a failure but " 442 1.1 jmmv "exited with error code %d", WEXITSTATUS(status)); 443 1.1 jmmv } 444 1.1 jmmv } else { 445 1.1 jmmv assert(WIFSIGNALED(status)); 446 1.1 jmmv *success = false; 447 1.1 jmmv return kyua_result_write( 448 1.1 jmmv output, KYUA_RESULT_FAILED, "Test case expected a failure but " 449 1.1 jmmv "received signal %d%s", WTERMSIG(status), 450 1.1 jmmv WCOREDUMP(status) ? " (core dumped)" : ""); 451 1.1 jmmv } 452 1.1 jmmv } 453 1.1 jmmv 454 1.1 jmmv 455 1.1 jmmv /// Writes a generic result file for an ATF expected_signal result. 456 1.1 jmmv /// 457 1.1 jmmv /// \param status_arg Optional integral argument to the status. 458 1.1 jmmv /// \param reason Textual explanation of the result. 459 1.1 jmmv /// \param status Exit code of the test program as returned by wait(). 460 1.1 jmmv /// \param output Path to the generic result file to create. 461 1.1 jmmv /// \param [out] success Whether the result should be considered a success or 462 1.1 jmmv /// not; e.g. passed and skipped are successful, but failed is not. 463 1.1 jmmv /// 464 1.1 jmmv /// \return An error if the conversion fails; OK otherwise. 465 1.1 jmmv static kyua_error_t 466 1.1 jmmv convert_expected_signal(const int status_arg, const char* reason, int status, 467 1.1 jmmv const char* output, bool* success) 468 1.1 jmmv { 469 1.1 jmmv if (WIFSIGNALED(status)) { 470 1.1 jmmv if (status_arg == NO_STATUS_ARG || status_arg == WTERMSIG(status)) { 471 1.1 jmmv *success = true; 472 1.1 jmmv return kyua_result_write( 473 1.1 jmmv output, KYUA_RESULT_EXPECTED_FAILURE, "%s", reason); 474 1.1 jmmv } else { 475 1.1 jmmv *success = false; 476 1.1 jmmv return kyua_result_write( 477 1.1 jmmv output, KYUA_RESULT_FAILED, "Test case expected to receive " 478 1.1 jmmv "signal %d but got %d", status_arg, WTERMSIG(status)); 479 1.1 jmmv } 480 1.1 jmmv } else { 481 1.1 jmmv assert(WIFEXITED(status)); 482 1.1 jmmv *success = false; 483 1.1 jmmv return kyua_result_write( 484 1.1 jmmv output, KYUA_RESULT_FAILED, "Test case expected to receive a " 485 1.1 jmmv "signal but exited with code %d", WEXITSTATUS(status)); 486 1.1 jmmv } 487 1.1 jmmv } 488 1.1 jmmv 489 1.1 jmmv 490 1.1 jmmv /// Writes a generic result file for an ATF expected_timeout result. 491 1.1 jmmv /// 492 1.1 jmmv /// \param status Exit code of the test program as returned by wait(). 493 1.1 jmmv /// \param output Path to the generic result file to create. 494 1.1 jmmv /// \param [out] success Whether the result should be considered a success or 495 1.1 jmmv /// not; e.g. passed and skipped are successful, but failed is not. 496 1.1 jmmv /// 497 1.1 jmmv /// \return An error if the conversion fails; OK otherwise. 498 1.1 jmmv static kyua_error_t 499 1.1 jmmv convert_expected_timeout(int status, const char* output, bool* success) 500 1.1 jmmv { 501 1.1 jmmv if (WIFEXITED(status)) { 502 1.1 jmmv *success = false; 503 1.1 jmmv return kyua_result_write( 504 1.1 jmmv output, KYUA_RESULT_FAILED, "Test case expected to time out but " 505 1.1 jmmv "exited with code %d", WEXITSTATUS(status)); 506 1.1 jmmv } else { 507 1.1 jmmv assert(WIFSIGNALED(status)); 508 1.1 jmmv *success = false; 509 1.1 jmmv return kyua_result_write( 510 1.1 jmmv output, KYUA_RESULT_FAILED, "Test case expected to time out but " 511 1.1 jmmv "received signal %d%s", WTERMSIG(status), 512 1.1 jmmv WCOREDUMP(status) ? " (core dumped)" : ""); 513 1.1 jmmv } 514 1.1 jmmv } 515 1.1 jmmv 516 1.1 jmmv 517 1.1 jmmv /// Writes a generic result file for an ATF failed result. 518 1.1 jmmv /// 519 1.1 jmmv /// \param reason Textual explanation of the result. 520 1.1 jmmv /// \param status Exit code of the test program as returned by wait(). 521 1.1 jmmv /// \param output Path to the generic result file to create. 522 1.1 jmmv /// \param [out] success Whether the result should be considered a success or 523 1.1 jmmv /// not; e.g. passed and skipped are successful, but failed is not. 524 1.1 jmmv /// 525 1.1 jmmv /// \return An error if the conversion fails; OK otherwise. 526 1.1 jmmv static kyua_error_t 527 1.1 jmmv convert_failed(const char* reason, int status, const char* output, 528 1.1 jmmv bool* success) 529 1.1 jmmv { 530 1.1 jmmv if (WIFEXITED(status)) { 531 1.1 jmmv if (WEXITSTATUS(status) == EXIT_SUCCESS) { 532 1.1 jmmv *success = false; 533 1.1 jmmv return kyua_result_write( 534 1.1 jmmv output, KYUA_RESULT_BROKEN, "Test case reported a failed " 535 1.1 jmmv "result but exited with a successful exit code"); 536 1.1 jmmv } else { 537 1.1 jmmv *success = false; 538 1.1 jmmv return kyua_result_write( 539 1.1 jmmv output, KYUA_RESULT_FAILED, "%s", reason); 540 1.1 jmmv } 541 1.1 jmmv } else { 542 1.1 jmmv assert(WIFSIGNALED(status)); 543 1.1 jmmv *success = false; 544 1.1 jmmv return kyua_result_write( 545 1.1 jmmv output, KYUA_RESULT_BROKEN, "Test case reported a failed result " 546 1.1 jmmv "but received signal %d%s", WTERMSIG(status), 547 1.1 jmmv WCOREDUMP(status) ? " (core dumped)" : ""); 548 1.1 jmmv } 549 1.1 jmmv } 550 1.1 jmmv 551 1.1 jmmv 552 1.1 jmmv /// Writes a generic result file for an ATF passed result. 553 1.1 jmmv /// 554 1.1 jmmv /// \param status Exit code of the test program as returned by wait(). 555 1.1 jmmv /// \param output Path to the generic result file to create. 556 1.1 jmmv /// \param [out] success Whether the result should be considered a success or 557 1.1 jmmv /// not; e.g. passed and skipped are successful, but failed is not. 558 1.1 jmmv /// 559 1.1 jmmv /// \return An error if the conversion fails; OK otherwise. 560 1.1 jmmv static kyua_error_t 561 1.1 jmmv convert_passed(int status, const char* output, bool* success) 562 1.1 jmmv { 563 1.1 jmmv if (WIFEXITED(status)) { 564 1.1 jmmv if (WEXITSTATUS(status) == EXIT_SUCCESS) { 565 1.1 jmmv *success = true; 566 1.1 jmmv return kyua_result_write(output, KYUA_RESULT_PASSED, NULL); 567 1.1 jmmv } else { 568 1.1 jmmv *success = false; 569 1.1 jmmv return kyua_result_write( 570 1.1 jmmv output, KYUA_RESULT_BROKEN, "Test case reported a passed " 571 1.1 jmmv "result but returned a non-zero exit code %d", 572 1.1 jmmv WEXITSTATUS(status)); 573 1.1 jmmv } 574 1.1 jmmv } else { 575 1.1 jmmv assert(WIFSIGNALED(status)); 576 1.1 jmmv *success = false; 577 1.1 jmmv return kyua_result_write( 578 1.1 jmmv output, KYUA_RESULT_BROKEN, "Test case reported a passed result " 579 1.1 jmmv "but received signal %d%s", WTERMSIG(status), 580 1.1 jmmv WCOREDUMP(status) ? " (core dumped)" : ""); 581 1.1 jmmv } 582 1.1 jmmv } 583 1.1 jmmv 584 1.1 jmmv 585 1.1 jmmv /// Writes a generic result file for an ATF skipped result. 586 1.1 jmmv /// 587 1.1 jmmv /// \param reason Textual explanation of the result. 588 1.1 jmmv /// \param status Exit code of the test program as returned by wait(). 589 1.1 jmmv /// \param output Path to the generic result file to create. 590 1.1 jmmv /// \param [out] success Whether the result should be considered a success or 591 1.1 jmmv /// not; e.g. passed and skipped are successful, but failed is not. 592 1.1 jmmv /// 593 1.1 jmmv /// \return An error if the conversion fails; OK otherwise. 594 1.1 jmmv static kyua_error_t 595 1.1 jmmv convert_skipped(const char* reason, int status, const char* output, 596 1.1 jmmv bool* success) 597 1.1 jmmv { 598 1.1 jmmv if (WIFEXITED(status)) { 599 1.1 jmmv if (WEXITSTATUS(status) == EXIT_SUCCESS) { 600 1.1 jmmv *success = true; 601 1.1 jmmv return kyua_result_write(output, KYUA_RESULT_SKIPPED, "%s", reason); 602 1.1 jmmv } else { 603 1.1 jmmv *success = false; 604 1.1 jmmv return kyua_result_write( 605 1.1 jmmv output, KYUA_RESULT_BROKEN, "Test case reported a skipped " 606 1.1 jmmv "result but returned a non-zero exit code %d", 607 1.1 jmmv WEXITSTATUS(status)); 608 1.1 jmmv } 609 1.1 jmmv } else { 610 1.1 jmmv *success = false; 611 1.1 jmmv assert(WIFSIGNALED(status)); 612 1.1 jmmv return kyua_result_write( 613 1.1 jmmv output, KYUA_RESULT_BROKEN, "Test case reported a skipped result " 614 1.1 jmmv "but received signal %d%s", WTERMSIG(status), 615 1.1 jmmv WCOREDUMP(status) ? " (core dumped)" : ""); 616 1.1 jmmv } 617 1.1 jmmv } 618 1.1 jmmv 619 1.1 jmmv 620 1.1 jmmv /// Writes a generic result file based on an ATF result and an exit code. 621 1.1 jmmv /// 622 1.1 jmmv /// \param status Type of the ATF result. 623 1.1 jmmv /// \param status_arg Optional integral argument to the status. 624 1.1 jmmv /// \param reason Textual explanation of the result. 625 1.1 jmmv /// \param wait_status Exit code of the test program as returned by wait(). 626 1.1 jmmv /// \param timed_out Whether the test program timed out or not. 627 1.1 jmmv /// \param output Path to the generic result file to create. 628 1.1 jmmv /// \param [out] success Whether the result should be considered a success or 629 1.1 jmmv /// not; e.g. passed and skipped are successful, but failed is not. 630 1.1 jmmv /// 631 1.1 jmmv /// \return An error if the conversion fails; OK otherwise. 632 1.1 jmmv static kyua_error_t 633 1.1 jmmv convert_result(const enum atf_status status, const int status_arg, 634 1.1 jmmv const char* reason, const int wait_status, const bool timed_out, 635 1.1 jmmv const char* output, bool* success) 636 1.1 jmmv { 637 1.1 jmmv if (timed_out) { 638 1.1 jmmv if (status == ATF_STATUS_EXPECTED_TIMEOUT) { 639 1.1 jmmv *success = true; 640 1.1 jmmv return kyua_result_write( 641 1.1 jmmv output, KYUA_RESULT_EXPECTED_FAILURE, "%s", reason); 642 1.1 jmmv } else { 643 1.1 jmmv assert(status == ATF_STATUS_BROKEN); 644 1.1 jmmv *success = false; 645 1.1 jmmv return kyua_result_write( 646 1.1 jmmv output, KYUA_RESULT_BROKEN, "Test case body timed out"); 647 1.1 jmmv } 648 1.1 jmmv } 649 1.1 jmmv 650 1.1 jmmv switch (status) { 651 1.1 jmmv case ATF_STATUS_BROKEN: 652 1.1 jmmv return convert_broken(reason, wait_status, output, success); 653 1.1 jmmv 654 1.1 jmmv case ATF_STATUS_EXPECTED_DEATH: 655 1.1 jmmv return convert_expected_death(reason, wait_status, output, success); 656 1.1 jmmv 657 1.1 jmmv case ATF_STATUS_EXPECTED_EXIT: 658 1.1 jmmv return convert_expected_exit(status_arg, reason, wait_status, output, 659 1.1 jmmv success); 660 1.1 jmmv 661 1.1 jmmv case ATF_STATUS_EXPECTED_FAILURE: 662 1.1 jmmv return convert_expected_failure(reason, wait_status, output, success); 663 1.1 jmmv 664 1.1 jmmv case ATF_STATUS_EXPECTED_SIGNAL: 665 1.1 jmmv return convert_expected_signal(status_arg, reason, wait_status, output, 666 1.1 jmmv success); 667 1.1 jmmv 668 1.1 jmmv case ATF_STATUS_EXPECTED_TIMEOUT: 669 1.1 jmmv return convert_expected_timeout(wait_status, output, success); 670 1.1 jmmv 671 1.1 jmmv case ATF_STATUS_FAILED: 672 1.1 jmmv return convert_failed(reason, wait_status, output, success); 673 1.1 jmmv 674 1.1 jmmv case ATF_STATUS_PASSED: 675 1.1 jmmv return convert_passed(wait_status, output, success); 676 1.1 jmmv 677 1.1 jmmv case ATF_STATUS_SKIPPED: 678 1.1 jmmv return convert_skipped(reason, wait_status, output, success); 679 1.1 jmmv } 680 1.1 jmmv 681 1.1 jmmv assert(false); 682 1.1 jmmv } 683 1.1 jmmv 684 1.1 jmmv 685 1.1 jmmv /// Writes a generic result file based on an ATF result file and an exit code. 686 1.1 jmmv /// 687 1.1 jmmv /// \param input_name Path to the ATF result file to parse. 688 1.1 jmmv /// \param output_name Path to the generic result file to create. 689 1.1 jmmv /// \param wait_status Exit code of the test program as returned by wait(). 690 1.1 jmmv /// \param timed_out Whether the test program timed out or not. 691 1.1 jmmv /// \param [out] success Whether the result should be considered a success or 692 1.1 jmmv /// not; e.g. passed and skipped are successful, but failed is not. 693 1.1 jmmv /// 694 1.1 jmmv /// \return An error if the conversion fails; OK otherwise. 695 1.1 jmmv kyua_error_t 696 1.1 jmmv kyua_atf_result_rewrite(const char* input_name, const char* output_name, 697 1.1 jmmv const int wait_status, const bool timed_out, 698 1.1 jmmv bool* success) 699 1.1 jmmv { 700 1.1 jmmv enum atf_status status; int status_arg; char reason[1024]; 701 1.1 jmmv status = ATF_STATUS_BROKEN; // Initialize to shut up gcc warning. 702 1.1 jmmv const kyua_error_t error = read_atf_result(input_name, &status, &status_arg, 703 1.1 jmmv reason, sizeof(reason)); 704 1.1 jmmv if (kyua_error_is_set(error)) { 705 1.1 jmmv // Errors while parsing the ATF result file can often be attributed to 706 1.1 jmmv // the result file being bogus. Therefore, just mark the test case as 707 1.1 jmmv // broken, because it possibly is. 708 1.1 jmmv status = ATF_STATUS_BROKEN; 709 1.1 jmmv kyua_error_format(error, reason, sizeof(reason)); 710 1.1 jmmv kyua_error_free(error); 711 1.1 jmmv } 712 1.1 jmmv 713 1.1 jmmv // Errors converting the loaded result to the final result file are not due 714 1.1 jmmv // to a bad test program: they are because our own code fails (e.g. cannot 715 1.1 jmmv // create the output file). These need to be returned to the caller. 716 1.1 jmmv return convert_result(status, status_arg, reason, wait_status, timed_out, 717 1.1 jmmv output_name, success); 718 1.1 jmmv } 719 1.1 jmmv 720 1.1 jmmv 721 1.1 jmmv /// Creates a result file for a failed cleanup routine. 722 1.1 jmmv /// 723 1.1 jmmv /// This function is supposed to be invoked after the body has had a chance to 724 1.1 jmmv /// create its own result file, and only if the body has terminated with a 725 1.1 jmmv /// non-failure result. 726 1.1 jmmv /// 727 1.1 jmmv /// \param output_name Path to the generic result file to create. 728 1.1 jmmv /// \param wait_status Exit code of the test program as returned by wait(). 729 1.1 jmmv /// \param timed_out Whether the test program timed out or not. 730 1.1 jmmv /// \param [out] success Whether the result should be considered a success or 731 1.1 jmmv /// not; i.e. a clean exit is successful, but anything else is a failure. 732 1.1 jmmv /// 733 1.1 jmmv /// \return An error if there is a problem writing the result; OK otherwise. 734 1.1 jmmv kyua_error_t 735 1.1 jmmv kyua_atf_result_cleanup_rewrite(const char* output_name, int wait_status, 736 1.1 jmmv const bool timed_out, bool* success) 737 1.1 jmmv { 738 1.1 jmmv if (timed_out) { 739 1.1 jmmv *success = false; 740 1.1 jmmv return kyua_result_write( 741 1.1 jmmv output_name, KYUA_RESULT_BROKEN, "Test case cleanup timed out"); 742 1.1 jmmv } else { 743 1.1 jmmv if (WIFEXITED(wait_status)) { 744 1.1 jmmv if (WEXITSTATUS(wait_status) == EXIT_SUCCESS) { 745 1.1 jmmv *success = true; 746 1.1 jmmv // Reuse the result file created by the body. I.e. avoid 747 1.1 jmmv // creating a new file here. 748 1.1 jmmv return kyua_error_ok(); 749 1.1 jmmv } else { 750 1.1 jmmv *success = false; 751 1.1 jmmv return kyua_result_write( 752 1.1 jmmv output_name, KYUA_RESULT_BROKEN, "Test case cleanup exited " 753 1.1 jmmv "with code %d", WEXITSTATUS(wait_status)); 754 1.1 jmmv } 755 1.1 jmmv } else { 756 1.1 jmmv *success = false; 757 1.1 jmmv return kyua_result_write( 758 1.1 jmmv output_name, KYUA_RESULT_BROKEN, "Test case cleanup received " 759 1.1 jmmv "signal %d%s", WTERMSIG(wait_status), 760 1.1 jmmv WCOREDUMP(wait_status) ? " (core dumped)" : ""); 761 1.1 jmmv } 762 1.1 jmmv } 763 1.1 jmmv } 764