From: Rémi Bernon rbernon@codeweavers.com
--- programs/winetest/main.c | 161 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 155 insertions(+), 6 deletions(-)
diff --git a/programs/winetest/main.c b/programs/winetest/main.c index 4764075d166..90e9bbad2eb 100644 --- a/programs/winetest/main.c +++ b/programs/winetest/main.c @@ -68,6 +68,7 @@ static char build_id[64]; static BOOL is_wow64; static int failures; static int quiet_mode; +static int junit_mode;
/* filters for running only specific tests */ static char **filters; @@ -869,24 +870,152 @@ get_subtests (const char *tempdir, struct wine_test *test, LPSTR res_name) return 0; }
+static char *strescape( const char *src, const char *end, int comment ) +{ + char *dst = calloc( 1, (end - src) * 6 + 1 ), *tmp; + + while (end > src && (end[-1] == '\r' || end[-1] == '\n')) end--; + for (tmp = dst; tmp && src < end; src++) + { + if (!junit_mode) *tmp++ = *src; + else if (comment && *src == '-') tmp += sprintf( tmp, "-" ); + else if (comment) *tmp++ = *src; + else if (*src == '&') tmp += sprintf( tmp, "&" ); + else if (*src == '"') tmp += sprintf( tmp, """ ); + else if (*src == '<') tmp += sprintf( tmp, "<" ); + else if (*src == '>') tmp += sprintf( tmp, ">" ); + else if (*src < ' ' && *src != '\t' && *src != '\r' && *src != '\n') + { + char *esc = strmake( NULL, "\x%02x", *src ); + tmp += sprintf( tmp, "%s", esc ); + free( esc ); + } + else *tmp++ = *src; + } + + return dst; +} + static void report_test_start( struct wine_test *test, const char *subtest, const char *file ) { report( R_STEP, "Running: %s:%s", test->name, subtest ); - xprintf( "%s:%s start %s\n", test->name, subtest, file ); + if (!junit_mode) xprintf( "%s:%s start %s\n", test->name, subtest, file ); }
static void report_test_done( struct wine_test *test, const char *subtest, const char *file, UINT pid, UINT ticks, HANDLE out_file, UINT status, const char *output, DWORD size ) { - if (quiet_mode <= 1 || status || size > MAX_OUTPUT_SIZE) WriteFile( out_file, output, size, &size, NULL ); - xprintf ("%s:%s:%04x done (%d) in %ds %uB\n", test->name, subtest, pid, status, ticks / 1000, size); - if (size > MAX_OUTPUT_SIZE) xprintf ("%s:%s:%04x The test prints too much data (%u bytes)\n", test->name, subtest, pid, size); + if (junit_mode) + { + int total = 0, fail_total = 0, skip_total = 0, failures = 0; + const char *next, *line, *ptr, *dir = strrchr( file, '/' ); + float time, last_time = 0; + char *tmp; + + for (line = next = output; *line; line = next) + { + int count, todo_count, flaky_count, fail_count, skip_count; + + if ((next = strchr( next, '\n' ))) next += 1; + else next = line + strlen( line ); + if (!(ptr = strchr( line, ' ' ))) continue; + + if (sscanf( ptr, " %d tests executed (%d marked as todo, %d as flaky, %d failur%*[^)]), %d skipped.", + &count, &todo_count, &flaky_count, &fail_count, &skip_count ) == 5) + { + total += count; + fail_total += fail_count; + skip_total += skip_count; + } + } + + xprintf( " <testsuite name="%s:%s" file="%s" time="%f" tests="%d" failures="%d" skipped="%d">\n", test->name, subtest, + file, ticks / 1000.0, total, fail_total, skip_total ); + xprintf( " <system-out>" ); + tmp = strescape( output, output + size, 0 ); + WriteFile( out_file, tmp, strlen( tmp ), &size, NULL ); + free( tmp ); + xprintf( "</system-out>\n" ); + + for (line = next = output; *line; line = next) + { + struct { const char *pattern; int length; int error; } patterns[] = + { + #define X(x, y) {.pattern = x, .length = strlen(x), .error = y} + X("Tests skipped: ", -1), + X("Test failed: ", 1), + X("Test succeeded inside todo block: ", 1), + X("Test succeeded inside flaky todo block: ", 1), + #undef X + }; + char src[256], *name; + int i, n, len; + + if ((next = strchr( next, '\n' ))) next += 1; + else next = line + strlen( line ); + + if ((len = sscanf( line, "%255[^:\n]:%d:%f", src, &n, &time )) != 2 && len != 3) continue; + if (len == 2) time = last_time; + + if (!(ptr = strchr( line, ' ' ))) continue; + ptr++; + + name = strmake( NULL, "%s:%d", src, n ); + for (i = 0; i < ARRAY_SIZE(patterns); i++) + { + if (!strncmp( ptr, patterns[i].pattern, patterns[i].length )) + { + char *message = strescape( ptr, next, 0 ); + xprintf( " <testcase classname="%s:%s" name="%s:%s %s" file="%.*s%s#L%d" assertions="1" time="%f">", + test->name, subtest, test->name, subtest, name, dir - file + 1, file, src, n, time - last_time ); + if (patterns[i].error == -1) xprintf( "<system-out>%s</system-out><skipped/>", message ); + else if (patterns[i].error == 1) xprintf( "<system-out>%s</system-out><failure/>", message ); + xprintf( "</testcase>\n" ); + free( message ); + + if (patterns[i].error == 1) failures++; + last_time = time; + break; + } + } + free( name ); + } + + if (total > fail_total + skip_total) + { + xprintf( " <testcase classname="%s:%s" name="%d succeeding tests" file="%s" assertions="%d" time="%f"/>\n", + test->name, subtest, total - fail_total - skip_total, file, total - fail_total - skip_total, ticks / 1000.0 - last_time ); + } + + if (status == WAIT_TIMEOUT) + { + xprintf( " <testcase classname="%s:%s" name="%s:%s timeout" file="%s" assertions="%d" time="%f">", + test->name, subtest, test->name, subtest, file, total, ticks / 1000.0 ); + xprintf( "<system-out>Test timeout after %f seconds</system-out><failure/>", ticks / 1000.0 ); + xprintf( "</testcase>\n" ); + } + else if (status != failures) + { + xprintf( " <testcase classname="%s:%s" name="%s:%s status %d" file="%s" assertions="%d" time="%f">", + test->name, subtest, test->name, subtest, status, file, total, ticks / 1000.0 ); + xprintf( "<system-out>Test exited with status %d</system-out><failure/>", status ); + xprintf( "</testcase>\n" ); + } + + xprintf( " </testsuite>\n" ); + } + else + { + if (quiet_mode <= 1 || status || size > MAX_OUTPUT_SIZE) WriteFile( out_file, output, size, &size, NULL ); + xprintf ("%s:%s:%04x done (%d) in %ds %uB\n", test->name, subtest, pid, status, ticks / 1000, size); + if (size > MAX_OUTPUT_SIZE) xprintf ("%s:%s:%04x The test prints too much data (%u bytes)\n", test->name, subtest, pid, size); + } }
static void report_test_skip( struct wine_test *test, const char *subtest, const char *file ) { report( R_STEP, "Skipping: %s:%s", test->name, subtest ); - xprintf( "%s:%s skipped %s\n", test->name, subtest, file ); + if (!junit_mode) xprintf( "%s:%s skipped %s\n", test->name, subtest, file ); }
static void @@ -1182,6 +1311,11 @@ run_tests (char *logname, char *outdir)
report (R_DIR, "%s", tempdir);
+ if (junit_mode) + { + xprintf( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" ); + xprintf( "<testsuites>\n<!--\n" ); + } xprintf ("Version 4\n"); xprintf ("Tests from build %s\n", build_id[0] ? build_id : "-" ); xprintf ("Archive: -\n"); /* no longer used */ @@ -1189,6 +1323,7 @@ run_tests (char *logname, char *outdir) xprintf ("Build info:\n"); strres = extract_rcdata ("BUILD_INFO", "STRINGRES", &strsize); while (strres) { + char *tmp; eol = memchr (strres, '\n', strsize); if (!eol) { nextline = NULL; @@ -1198,7 +1333,10 @@ run_tests (char *logname, char *outdir) nextline = strsize?eol+1:NULL; if (eol > strres && *(eol-1) == '\r') eol--; } - xprintf (" %.*s\n", eol-strres, strres); + + tmp = strescape( strres, eol, 1 ); + xprintf (" %s\n", tmp ); + free( tmp ); strres = nextline; } xprintf ("Operating system version:\n"); @@ -1237,6 +1375,7 @@ run_tests (char *logname, char *outdir) if (aborting) return logname;
xprintf ("Test output:\n" ); + if (junit_mode) xprintf ("-->\n" );
report (R_DELTA, 0, "Extracting: Done");
@@ -1267,6 +1406,7 @@ run_tests (char *logname, char *outdir) } } report (R_DELTA, 0, "Running: Done"); + if (junit_mode) xprintf( "</testsuites>\n" );
report (R_STATUS, "Cleaning up - %u failures", failures); if (strcmp(logname, "-") != 0) CloseHandle( logfile ); @@ -1347,6 +1487,7 @@ usage (void) " -n exclude the specified tests\n" " -p shutdown when the tests are done\n" " -q quiet mode, no output at all\n" +" -J junit mode, output XML report\n" " -o FILE put report into FILE, do not submit\n" " -s FILE submit FILE, do not run tests\n" " -S URL URL to submit the results to\n" @@ -1406,6 +1547,9 @@ int __cdecl main( int argc, char *argv[] ) exit( 2 ); } break; + case 'J': + junit_mode = 1; + break; case 'm': if (!(email = argv[++i])) { @@ -1523,6 +1667,11 @@ int __cdecl main( int argc, char *argv[] ) SetEnvironmentVariableA( "WINETEST_INTERACTIVE", "0" ); SetEnvironmentVariableA( "WINETEST_REPORT_SUCCESS", "0" ); } + if (junit_mode) + { + SetEnvironmentVariableA( "WINETEST_COLOR", "0" ); + SetEnvironmentVariableA( "WINETEST_TIME", "1" ); + }
if (nb_filters && !exclude_tests) {