main.c 20.1 KB
Newer Older
1 2 3
/*
 * Wine Conformance Test EXE
 *
4
 * Copyright 2003, 2004 Jakob Eriksson   (for Solid Form Sweden AB)
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
 * Copyright 2003 Dimitrie O. Paun
 * Copyright 2003 Ferenc Wagner
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 22 23 24 25 26 27 28 29 30 31 32
 *
 * This program is dedicated to Anna Lindh,
 * Swedish Minister of Foreign Affairs.
 * Anna was murdered September 11, 2003.
 *
 */

#include "config.h"
#include "wine/port.h"

#include <stdio.h>
#include <stdlib.h>
33
#include <assert.h>
34 35 36 37 38 39 40
#include <errno.h>
#ifdef HAVE_UNISTD_H
#  include <unistd.h>
#endif
#include <windows.h>

#include "winetest.h"
41
#include "resource.h"
42

43 44 45 46 47 48 49 50 51
struct wine_test
{
    char *name;
    int resource;
    int subtest_count;
    char **subtests;
    char *exename;
};

52 53 54 55 56 57
struct rev_info
{
    const char* file;
    const char* rev;
};

58
char *tag = NULL;
59
static struct wine_test *wine_tests;
60
static int nr_of_files, nr_of_tests;
61
static struct rev_info *rev_infos = NULL;
62
static const char whitespace[] = " \t\r\n";
63

64
static int running_under_wine (void)
Jakob Eriksson's avatar
Jakob Eriksson committed
65 66 67 68
{
    HMODULE module = GetModuleHandleA("ntdll.dll");

    if (!module) return 0;
69
    return (GetProcAddress(module, "wine_server_call") != NULL);
Jakob Eriksson's avatar
Jakob Eriksson committed
70 71
}

72
static int running_on_visible_desktop (void)
73
{
74 75 76 77
    HWND desktop;
    HMODULE huser32 = GetModuleHandle("user32.dll");
    FARPROC pGetProcessWindowStation = GetProcAddress(huser32, "GetProcessWindowStation");
    FARPROC pGetUserObjectInformationA = GetProcAddress(huser32, "GetUserObjectInformationA");
78

79 80 81 82 83
    desktop = GetDesktopWindow();
    if (!GetWindowLongPtrW(desktop, GWLP_WNDPROC)) /* Win9x */
        return IsWindowVisible(desktop);

    if (pGetProcessWindowStation && pGetUserObjectInformationA)
84 85 86 87
    {
        DWORD len;
        HWINSTA wstation;
        USEROBJECTFLAGS uoflags;
88

89 90 91 92
        wstation = (HWINSTA)pGetProcessWindowStation();
        assert(pGetUserObjectInformationA(wstation, UOI_FLAGS, &uoflags, sizeof(uoflags), &len));
        return (uoflags.dwFlags & WSF_VISIBLE) != 0;
    }
93
    return IsWindowVisible(desktop);
94 95
}

96
static void print_version (void)
97 98 99
{
    OSVERSIONINFOEX ver;
    BOOL ext;
100
    int is_win2k3_r2;
101 102 103 104 105 106

    ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    if (!(ext = GetVersionEx ((OSVERSIONINFO *) &ver)))
    {
	ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
	if (!GetVersionEx ((OSVERSIONINFO *) &ver))
107
	    report (R_FATAL, "Can't get OS version.");
108 109
    }

110
    xprintf ("    bRunningUnderWine=%d\n", running_under_wine ());
111
    xprintf ("    bRunningOnVisibleDesktop=%d\n", running_on_visible_desktop ());
112 113 114 115 116
    xprintf ("    dwMajorVersion=%ld\n    dwMinorVersion=%ld\n"
             "    dwBuildNumber=%ld\n    PlatformId=%ld\n    szCSDVersion=%s\n",
             ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber,
             ver.dwPlatformId, ver.szCSDVersion);

117 118 119 120
    is_win2k3_r2 = GetSystemMetrics(SM_SERVERR2);
    if(is_win2k3_r2)
        xprintf("    R2 build number=%d\n", is_win2k3_r2);

121 122 123 124 125 126 127 128 129 130 131 132 133
    if (!ext) return;

    xprintf ("    wServicePackMajor=%d\n    wServicePackMinor=%d\n"
             "    wSuiteMask=%d\n    wProductType=%d\n    wReserved=%d\n",
             ver.wServicePackMajor, ver.wServicePackMinor, ver.wSuiteMask,
             ver.wProductType, ver.wReserved);
}

static inline int is_dot_dir(const char* x)
{
    return ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))));
}

134
static void remove_dir (const char *dir)
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
{
    HANDLE  hFind;
    WIN32_FIND_DATA wfd;
    char path[MAX_PATH];
    size_t dirlen = strlen (dir);

    /* Make sure the directory exists before going further */
    memcpy (path, dir, dirlen);
    strcpy (path + dirlen++, "\\*");
    hFind = FindFirstFile (path, &wfd);
    if (hFind == INVALID_HANDLE_VALUE) return;

    do {
        char *lp = wfd.cFileName;

        if (!lp[0]) lp = wfd.cAlternateFileName; /* ? FIXME not (!lp) ? */
        if (is_dot_dir (lp)) continue;
        strcpy (path + dirlen, lp);
        if (FILE_ATTRIBUTE_DIRECTORY & wfd.dwFileAttributes)
            remove_dir(path);
        else if (!DeleteFile (path))
156 157
            report (R_WARNING, "Can't delete file %s: error %d",
                    path, GetLastError ());
158 159 160
    } while (FindNextFile (hFind, &wfd));
    FindClose (hFind);
    if (!RemoveDirectory (dir))
161 162
        report (R_WARNING, "Can't remove directory %s: error %d",
                dir, GetLastError ());
163 164
}

165
static const char* get_test_source_file(const char* test, const char* subtest)
166 167
{
    static const char* special_dirs[][2] = {
168
	{ 0, 0 }
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
    };
    static char buffer[MAX_PATH];
    int i;

    for (i = 0; special_dirs[i][0]; i++) {
	if (strcmp(test, special_dirs[i][0]) == 0) {
	    test = special_dirs[i][1];
	    break;
	}
    }

    snprintf(buffer, sizeof(buffer), "dlls/%s/tests/%s.c", test, subtest);
    return buffer;
}

184
static const char* get_file_rev(const char* file)
185 186 187 188 189 190 191
{
    const struct rev_info* rev;
 
    for(rev = rev_infos; rev->file; rev++) {
	if (strcmp(rev->file, file) == 0) return rev->rev;
    }

192
    return "-";
193 194
}

195
static void extract_rev_infos (void)
196 197
{
    char revinfo[256], *p;
198 199
    int size = 0, i;
    unsigned int len;
200 201 202 203 204
    HMODULE module = GetModuleHandle (NULL);

    for (i = 0; TRUE; i++) {
	if (i >= size) {
	    size += 100;
205
	    rev_infos = xrealloc (rev_infos, size * sizeof (*rev_infos));
206 207 208
	}
	memset(rev_infos + i, 0, sizeof(rev_infos[i]));

209
        len = LoadStringA (module, REV_INFO+i, revinfo, sizeof(revinfo));
210
        if (len == 0) break; /* end of revision info */
211
	if (len >= sizeof(revinfo) - 1) 
212 213 214 215 216 217
	    report (R_FATAL, "Revision info too long.");
	if(!(p = strrchr(revinfo, ':')))
	    report (R_FATAL, "Revision info malformed (i=%d)", i);
	*p = 0;
	rev_infos[i].file = strdup(revinfo);
	rev_infos[i].rev = strdup(p + 1);
218
    }
219 220
}

221
static void* extract_rcdata (LPTSTR name, int type, DWORD* size)
222 223 224
{
    HRSRC rsrc;
    HGLOBAL hdl;
225
    LPVOID addr;
226
    
227
    if (!(rsrc = FindResource (NULL, name, MAKEINTRESOURCE(type))) ||
228 229 230
        !(*size = SizeofResource (0, rsrc)) ||
        !(hdl = LoadResource (0, rsrc)) ||
        !(addr = LockResource (hdl)))
231
        return NULL;
232
    return addr;
233 234
}

235
/* Fills in the name and exename fields */
236
static void
237
extract_test (struct wine_test *test, const char *dir, LPTSTR res_name)
238 239 240 241
{
    BYTE* code;
    DWORD size;
    FILE* fout;
242
    char *exepos;
243

244 245 246 247 248
    code = extract_rcdata (res_name, TESTRES, &size);
    if (!code) report (R_FATAL, "Can't find test resource %s: %d",
                       res_name, GetLastError ());
    test->name = xstrdup( res_name );
    CharLowerA( test->name );
249
    test->exename = strmake (NULL, "%s/%s", dir, test->name);
Ferenc Wagner's avatar
Ferenc Wagner committed
250
    exepos = strstr (test->name, "_test.exe");
251 252 253
    if (!exepos) report (R_FATAL, "Not an .exe file: %s", test->name);
    *exepos = 0;
    test->name = xrealloc (test->name, exepos - test->name + 1);
254
    report (R_STEP, "Extracting: %s", test->name);
255

256
    if (!(fout = fopen (test->exename, "wb")) ||
257
        (fwrite (code, size, 1, fout) != 1) ||
258
        fclose (fout)) report (R_FATAL, "Failed to write file %s.",
259
                               test->exename);
260 261
}

262 263 264 265 266 267
/* Run a command for MS milliseconds.  If OUT != NULL, also redirect
   stdout to there.

   Return the exit status, -2 if can't create process or the return
   value of WaitForSingleObject.
 */
268
static int
269
run_ex (char *cmd, const char *out, const char *tempdir, DWORD ms)
270 271 272 273 274 275 276
{
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    int fd, oldstdout = -1;
    DWORD wait, status;

    GetStartupInfo (&si);
277
    si.dwFlags = 0;
278 279 280 281 282 283 284 285 286 287 288 289 290 291

    if (out) {
        fd = open (out, O_WRONLY | O_CREAT, 0666);
        if (-1 == fd)
            report (R_FATAL, "Can't open '%s': %d", out, errno);
        oldstdout = dup (1);
        if (-1 == oldstdout)
            report (R_FATAL, "Can't save stdout: %d", errno);
        if (-1 == dup2 (fd, 1))
            report (R_FATAL, "Can't redirect stdout: %d", errno);
        close (fd);
    }

    if (!CreateProcessA (NULL, cmd, NULL, NULL, TRUE, 0,
292
                         NULL, tempdir, &si, &pi)) {
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
        status = -2;
    } else {
        CloseHandle (pi.hThread);
        wait = WaitForSingleObject (pi.hProcess, ms);
        if (wait == WAIT_OBJECT_0) {
            GetExitCodeProcess (pi.hProcess, &status);
        } else {
            switch (wait) {
            case WAIT_FAILED:
                report (R_ERROR, "Wait for '%s' failed: %d", cmd,
                        GetLastError ());
                break;
            case WAIT_TIMEOUT:
                report (R_ERROR, "Process '%s' timed out.", cmd);
                break;
            default:
                report (R_ERROR, "Wait returned %d", wait);
            }
            status = wait;
            if (!TerminateProcess (pi.hProcess, 257))
                report (R_ERROR, "TerminateProcess failed: %d",
                        GetLastError ());
            wait = WaitForSingleObject (pi.hProcess, 5000);
            switch (wait) {
            case WAIT_FAILED:
                report (R_ERROR,
                        "Wait for termination of '%s' failed: %d",
                        cmd, GetLastError ());
                break;
            case WAIT_OBJECT_0:
                break;
            case WAIT_TIMEOUT:
                report (R_ERROR, "Can't kill process '%s'", cmd);
                break;
            default:
                report (R_ERROR, "Waiting for termination: %d",
                        wait);
            }
        }
        CloseHandle (pi.hProcess);
    }

    if (out) {
        close (1);
        if (-1 == dup2 (oldstdout, 1))
            report (R_FATAL, "Can't recover stdout: %d", errno);
        close (oldstdout);
    }
    return status;
}

344
static void
345
get_subtests (const char *tempdir, struct wine_test *test, LPTSTR res_name)
346
{
347
    char *subname, *cmd;
348
    FILE *subfile;
349 350
    size_t total;
    char buffer[8192], *index;
351
    static const char header[] = "Valid test names:";
352
    int allocated;
353

354 355
    test->subtest_count = 0;

356
    subname = tempnam (0, "sub");
357
    if (!subname) report (R_FATAL, "Can't name subtests file.");
358

359
    extract_test (test, tempdir, res_name);
360
    cmd = strmake (NULL, "%s --list", test->exename);
361
    run_ex (cmd, subname, tempdir, 5000);
362
    free (cmd);
363 364 365 366 367 368 369 370 371 372 373 374 375 376

    subfile = fopen (subname, "r");
    if (!subfile) {
        report (R_ERROR, "Can't open subtests output of %s: %d",
                test->name, errno);
        goto quit;
    }
    total = fread (buffer, 1, sizeof buffer, subfile);
    fclose (subfile);
    if (sizeof buffer == total) {
        report (R_ERROR, "Subtest list of %s too big.",
                test->name, sizeof buffer);
        goto quit;
    }
377
    buffer[total] = 0;
378

379
    index = strstr (buffer, header);
380 381
    if (!index) {
        report (R_ERROR, "Can't parse subtests output of %s",
382
                test->name);
383 384
        goto quit;
    }
385 386 387 388
    index += sizeof header;

    allocated = 10;
    test->subtests = xmalloc (allocated * sizeof(char*));
389
    index = strtok (index, whitespace);
390 391 392 393 394
    while (index) {
        if (test->subtest_count == allocated) {
            allocated *= 2;
            test->subtests = xrealloc (test->subtests,
                                       allocated * sizeof(char*));
395
        }
396
        test->subtests[test->subtest_count++] = strdup (index);
397
        index = strtok (NULL, whitespace);
398
    }
399 400
    test->subtests = xrealloc (test->subtests,
                               test->subtest_count * sizeof(char*));
401 402

 quit:
403
    if (remove (subname))
404 405
        report (R_WARNING, "Can't delete file '%s': %d",
                subname, errno);
406 407 408
    free (subname);
}

409
static void
410
run_test (struct wine_test* test, const char* subtest, const char *tempdir)
411 412
{
    int status;
413 414
    const char* file = get_test_source_file(test->name, subtest);
    const char* rev = get_file_rev(file);
415
    char *cmd = strmake (NULL, "%s %s", test->exename, subtest);
416

417
    xprintf ("%s:%s start %s %s\n", test->name, subtest, file, rev);
418
    status = run_ex (cmd, NULL, tempdir, 120000);
419
    free (cmd);
420
    xprintf ("%s:%s done (%d)\n", test->name, subtest, status);
421 422
}

423
static BOOL CALLBACK
424 425
EnumTestFileProc (HMODULE hModule, LPCTSTR lpszType,
                  LPTSTR lpszName, LONG_PTR lParam)
426
{
427 428 429 430
    (*(int*)lParam)++;
    return TRUE;
}

431 432 433 434 435 436 437 438 439 440 441
static BOOL CALLBACK
extract_test_proc (HMODULE hModule, LPCTSTR lpszType,
                   LPTSTR lpszName, LONG_PTR lParam)
{
    const char *tempdir = (const char *)lParam;
    get_subtests( tempdir, &wine_tests[nr_of_files], lpszName );
    nr_of_tests += wine_tests[nr_of_files].subtest_count;
    nr_of_files++;
    return TRUE;
}

442
static char *
443
run_tests (char *logname)
444
{
445
    int i;
446
    char *tempdir, *shorttempdir;
447 448 449
    int logfile;
    char *strres, *eol, *nextline;
    DWORD strsize;
450

451
    SetErrorMode (SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
452

453 454 455 456
    if (!logname) {
        logname = tempnam (0, "res");
        if (!logname) report (R_FATAL, "Can't name logfile.");
    }
457
    report (R_OUT, logname);
458

459 460 461 462
    logfile = open (logname, O_WRONLY | O_CREAT | O_EXCL | O_APPEND,
                    0666);
    if (-1 == logfile) {
        if (EEXIST == errno)
463
            report (R_FATAL, "File %s already exists.", logname);
464 465 466 467 468 469 470 471 472
        else report (R_FATAL, "Could not open logfile: %d", errno);
    }
    if (-1 == dup2 (logfile, 1))
        report (R_FATAL, "Can't redirect stdout: %d", errno);
    close (logfile);

    tempdir = tempnam (0, "wct");
    if (!tempdir)
        report (R_FATAL, "Can't name temporary dir (check %%TEMP%%).");
473 474 475 476 477 478 479 480 481
    shorttempdir = strdup (tempdir);
    if (shorttempdir) {         /* try stable path for ZoneAlarm */
        strstr (shorttempdir, "wct")[3] = 0;
        if (CreateDirectoryA (shorttempdir, NULL)) {
            free (tempdir);
            tempdir = shorttempdir;
        } else free (shorttempdir);
    }
    if (tempdir != shorttempdir && !CreateDirectoryA (tempdir, NULL))
482
        report (R_FATAL, "Could not create directory: %s", tempdir);
483
    report (R_DIR, tempdir);
484

Ferenc Wagner's avatar
Ferenc Wagner committed
485
    xprintf ("Version 3\n");
486
    strres = extract_rcdata (MAKEINTRESOURCE(WINE_BUILD), STRINGRES, &strsize);
487 488 489
    xprintf ("Tests from build ");
    if (strres) xprintf ("%.*s", strsize, strres);
    else xprintf ("-\n");
490
    strres = extract_rcdata (MAKEINTRESOURCE(TESTS_URL), STRINGRES, &strsize);
491 492 493
    xprintf ("Archive: ");
    if (strres) xprintf ("%.*s", strsize, strres);
    else xprintf ("-\n");
494
    xprintf ("Tag: %s\n", tag);
495
    xprintf ("Build info:\n");
496
    strres = extract_rcdata (MAKEINTRESOURCE(BUILD_INFO), STRINGRES, &strsize);
497 498 499 500 501 502 503 504 505 506 507 508 509
    while (strres) {
        eol = memchr (strres, '\n', strsize);
        if (!eol) {
            nextline = NULL;
            eol = strres + strsize;
        } else {
            strsize -= eol - strres + 1;
            nextline = strsize?eol+1:NULL;
            if (eol > strres && *(eol-1) == '\r') eol--;
        }
        xprintf ("    %.*s\n", eol-strres, strres);
        strres = nextline;
    }
510 511 512 513
    xprintf ("Operating system version:\n");
    print_version ();
    xprintf ("Test output:\n" );

514
    report (R_STATUS, "Counting tests");
515
    if (!EnumResourceNames (NULL, MAKEINTRESOURCE(TESTRES),
516 517 518 519 520 521
                            EnumTestFileProc, (LPARAM)&nr_of_files))
        report (R_FATAL, "Can't enumerate test files: %d",
                GetLastError ());
    wine_tests = xmalloc (nr_of_files * sizeof wine_tests[0]);

    report (R_STATUS, "Extracting tests");
522
    report (R_PROGRESS, 0, nr_of_files);
523 524 525 526 527 528 529
    nr_of_files = 0;
    nr_of_tests = 0;
    if (!EnumResourceNames (NULL, MAKEINTRESOURCE(TESTRES),
                            extract_test_proc, (LPARAM)tempdir))
        report (R_FATAL, "Can't enumerate test files: %d",
                GetLastError ());

530 531 532
    report (R_DELTA, 0, "Extracting: Done");

    report (R_STATUS, "Running tests");
533
    report (R_PROGRESS, 1, nr_of_tests);
534 535 536 537 538
    for (i = 0; i < nr_of_files; i++) {
        struct wine_test *test = wine_tests + i;
        int j;

	for (j = 0; j < test->subtest_count; j++) {
Ferenc Wagner's avatar
Ferenc Wagner committed
539
            report (R_STEP, "Running: %s:%s", test->name,
540
                    test->subtests[j]);
541
	    run_test (test, test->subtests[j], tempdir);
542 543 544
        }
    }
    report (R_DELTA, 0, "Running: Done");
545

546
    report (R_STATUS, "Cleaning up");
547 548
    close (1);
    remove_dir (tempdir);
549 550
    free (tempdir);
    free (wine_tests);
551

552 553
    return logname;
}
554

555 556
static void
usage (void)
557
{
558 559 560 561 562 563 564 565 566
    fprintf (stderr,
"Usage: winetest [OPTION]...\n\n"
"  -c       console mode, no GUI\n"
"  -e       preserve the environment\n"
"  -h       print this message and exit\n"
"  -q       quiet mode, no output at all\n"
"  -o FILE  put report into FILE, do not submit\n"
"  -s FILE  submit FILE, do not run tests\n"
"  -t TAG   include TAG of characters [-.0-9a-zA-Z] in the report\n");
567
}
568

569 570 571
int WINAPI WinMain (HINSTANCE hInst, HINSTANCE hPrevInst,
                    LPSTR cmdLine, int cmdShow)
{
572
    char *logname = NULL;
573
    const char *cp, *submit = NULL;
574
    int reset_env = 1;
575
    int interactive = 1;
576

577 578 579
    /* initialize the revision information first */
    extract_rev_infos();

580
    cmdLine = strtok (cmdLine, whitespace);
581
    while (cmdLine) {
582 583 584 585 586 587 588 589
        if (cmdLine[0] != '-' || cmdLine[2]) {
            report (R_ERROR, "Not a single letter option: %s", cmdLine);
            usage ();
            exit (2);
        }
        switch (cmdLine[1]) {
        case 'c':
            report (R_TEXTMODE);
590
            interactive = 0;
591
            break;
592 593 594
        case 'e':
            reset_env = 0;
            break;
595 596 597 598 599
        case 'h':
            usage ();
            exit (0);
        case 'q':
            report (R_QUIET);
600
            interactive = 0;
601 602
            break;
        case 's':
603
            submit = strtok (NULL, whitespace);
604 605
            if (tag)
                report (R_WARNING, "ignoring tag for submission");
606 607 608
            send_file (submit);
            break;
        case 'o':
609
            logname = strtok (NULL, whitespace);
610 611
            break;
        case 't':
612
            tag = strtok (NULL, whitespace);
613 614 615
            if (strlen (tag) > MAXTAGLEN)
                report (R_FATAL, "tag is too long (maximum %d characters)",
                        MAXTAGLEN);
616
            cp = findbadtagchar (tag);
617 618
            if (cp) {
                report (R_ERROR, "invalid char in tag: %c", *cp);
619 620 621
                usage ();
                exit (2);
            }
622 623 624 625 626 627
            break;
        default:
            report (R_ERROR, "invalid option: -%c", cmdLine[1]);
            usage ();
            exit (2);
        }
628
        cmdLine = strtok (NULL, whitespace);
629
    }
630
    if (!submit) {
631 632 633 634 635
        static CHAR platform_windows[]  = "WINETEST_PLATFORM=windows",
                    debug_yes[]         = "WINETEST_DEBUG=1",
                    interactive_no[]    = "WINETEST_INTERACTIVE=0",
                    report_success_no[] = "WINETEST_REPORT_SUCCESS=0";

636 637 638 639
        report (R_STATUS, "Starting up");

        if (!running_on_visible_desktop ())
            report (R_FATAL, "Tests must be run on a visible desktop");
640

641 642 643 644
        if (reset_env && (putenv (platform_windows) ||
                          putenv (debug_yes)        ||
                          putenv (interactive_no)   ||
                          putenv (report_success_no)))
645 646
            report (R_FATAL, "Could not reset environment: %d", errno);

647 648 649 650 651 652 653 654
        if (!tag) {
            if (!interactive)
                report (R_FATAL, "Please specify a tag (-t option) if "
                        "running noninteractive!");
            if (guiAskTag () == IDABORT) exit (1);
        }
        report (R_TAG);

655
        if (!logname) {
656
            logname = run_tests (NULL);
657 658 659 660 661
            if (report (R_ASK, MB_YESNO, "Do you want to submit the "
                        "test results?") == IDYES)
                if (!send_file (logname) && remove (logname))
                    report (R_WARNING, "Can't remove logfile: %d.", errno);
            free (logname);
662
        } else run_tests (logname);
663 664
        report (R_STATUS, "Finished");
    }
665
    exit (0);
666
}