main.c 20.2 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 168
{
    static const char* special_dirs[][2] = {
	{ "gdi32", "gdi"}, { "kernel32", "kernel" },
169
        { "msacm32", "msacm" },
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
	{ "user32", "user" }, { "winspool.drv", "winspool" },
	{ "ws2_32", "winsock" }, { 0, 0 }
    };
    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;
}

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

195
    return "-";
196 197
}

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

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

212
        len = LoadStringA (module, REV_INFO+i, revinfo, sizeof(revinfo));
213
        if (len == 0) break; /* end of revision info */
214
	if (len >= sizeof(revinfo) - 1) 
215 216 217 218 219 220
	    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);
221
    }
222 223
}

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

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

247 248 249 250 251
    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 );
252
    test->exename = strmake (NULL, "%s/%s", dir, test->name);
Ferenc Wagner's avatar
Ferenc Wagner committed
253
    exepos = strstr (test->name, "_test.exe");
254 255 256
    if (!exepos) report (R_FATAL, "Not an .exe file: %s", test->name);
    *exepos = 0;
    test->name = xrealloc (test->name, exepos - test->name + 1);
257
    report (R_STEP, "Extracting: %s", test->name);
258

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

265 266 267 268 269 270
/* 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.
 */
271
static int
272 273 274 275 276 277 278 279
run_ex (char *cmd, const char *out, DWORD ms)
{
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    int fd, oldstdout = -1;
    DWORD wait, status;

    GetStartupInfo (&si);
280
    si.dwFlags = 0;
281 282 283 284 285 286 287 288 289 290 291 292 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 344 345 346

    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,
                         NULL, NULL, &si, &pi)) {
        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;
}

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

357 358
    test->subtest_count = 0;

359
    subname = tempnam (0, "sub");
360
    if (!subname) report (R_FATAL, "Can't name subtests file.");
361

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

    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;
    }
380
    buffer[total] = 0;
381

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

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

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

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

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

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

434 435 436 437 438 439 440 441 442 443 444
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;
}

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

454
    SetErrorMode (SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
455

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

462 463 464 465
    logfile = open (logname, O_WRONLY | O_CREAT | O_EXCL | O_APPEND,
                    0666);
    if (-1 == logfile) {
        if (EEXIST == errno)
466
            report (R_FATAL, "File %s already exists.", logname);
467 468 469 470 471 472 473 474 475
        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%%).");
476 477 478 479 480 481 482 483 484
    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))
485
        report (R_FATAL, "Could not create directory: %s", tempdir);
486
    report (R_DIR, tempdir);
487

Ferenc Wagner's avatar
Ferenc Wagner committed
488
    xprintf ("Version 3\n");
489
    strres = extract_rcdata (MAKEINTRESOURCE(WINE_BUILD), STRINGRES, &strsize);
490 491 492
    xprintf ("Tests from build ");
    if (strres) xprintf ("%.*s", strsize, strres);
    else xprintf ("-\n");
493
    strres = extract_rcdata (MAKEINTRESOURCE(TESTS_URL), STRINGRES, &strsize);
494 495 496
    xprintf ("Archive: ");
    if (strres) xprintf ("%.*s", strsize, strres);
    else xprintf ("-\n");
497
    xprintf ("Tag: %s\n", tag);
498
    xprintf ("Build info:\n");
499
    strres = extract_rcdata (MAKEINTRESOURCE(BUILD_INFO), STRINGRES, &strsize);
500 501 502 503 504 505 506 507 508 509 510 511 512
    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;
    }
513 514 515 516
    xprintf ("Operating system version:\n");
    print_version ();
    xprintf ("Test output:\n" );

517
    report (R_STATUS, "Counting tests");
518
    if (!EnumResourceNames (NULL, MAKEINTRESOURCE(TESTRES),
519 520 521 522 523 524
                            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");
525
    report (R_PROGRESS, 0, nr_of_files);
526 527 528 529 530 531 532
    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 ());

533 534 535
    report (R_DELTA, 0, "Extracting: Done");

    report (R_STATUS, "Running tests");
536
    report (R_PROGRESS, 1, nr_of_tests);
537 538 539 540 541
    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
542
            report (R_STEP, "Running: %s:%s", test->name,
543 544 545 546 547
                    test->subtests[j]);
	    run_test (test, test->subtests[j]);
        }
    }
    report (R_DELTA, 0, "Running: Done");
548

549
    report (R_STATUS, "Cleaning up");
550 551
    close (1);
    remove_dir (tempdir);
552 553
    free (tempdir);
    free (wine_tests);
554

555 556
    return logname;
}
557

558 559
static void
usage (void)
560 561 562 563
{
    fprintf (stderr, "\
Usage: winetest [OPTION]...\n\n\
  -c       console mode, no GUI\n\
564
  -e       preserve the environment\n\
565 566 567 568
  -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\
569
  -t TAG   include TAG of characters [-.0-9a-zA-Z] in the report\n");
570
}
571

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

580 581 582
    /* initialize the revision information first */
    extract_rev_infos();

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

639 640 641 642
        report (R_STATUS, "Starting up");

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

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

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

658
        if (!logname) {
659
            logname = run_tests (NULL);
660 661 662 663 664
            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);
665
        } else run_tests (logname);
666 667
        report (R_STATUS, "Finished");
    }
668
    exit (0);
669
}