- Fix report() dispatch.
[wine] / programs / winetest / main.c
1 /*
2  * Wine Conformance Test EXE
3  *
4  * Copyright 2003 Jakob Eriksson   (for Solid Form Sweden AB)
5  * Copyright 2003 Dimitrie O. Paun
6  * Copyright 2003 Ferenc Wagner
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  *
21  * This program is dedicated to Anna Lindh,
22  * Swedish Minister of Foreign Affairs.
23  * Anna was murdered September 11, 2003.
24  *
25  */
26
27 #include "config.h"
28 #include "wine/port.h"
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <errno.h>
33 #ifdef HAVE_UNISTD_H
34 #  include <unistd.h>
35 #endif
36 #include <windows.h>
37
38 #include "winetest.h"
39
40 #define TESTRESOURCE "USERDATA"
41
42 struct wine_test
43 {
44     char *name;
45     int resource;
46     int subtest_count;
47     char **subtests;
48     int is_elf;
49     char *exename;
50 };
51
52 static struct wine_test *wine_tests;
53
54 static const char *wineloader;
55
56 void print_version ()
57 {
58     OSVERSIONINFOEX ver;
59     BOOL ext;
60
61     ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
62     if (!(ext = GetVersionEx ((OSVERSIONINFO *) &ver)))
63     {
64         ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
65         if (!GetVersionEx ((OSVERSIONINFO *) &ver))
66             report (R_FATAL, "Can't get OS version.");
67     }
68
69     xprintf ("    dwMajorVersion=%ld\n    dwMinorVersion=%ld\n"
70              "    dwBuildNumber=%ld\n    PlatformId=%ld\n    szCSDVersion=%s\n",
71              ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber,
72              ver.dwPlatformId, ver.szCSDVersion);
73
74     if (!ext) return;
75
76     xprintf ("    wServicePackMajor=%d\n    wServicePackMinor=%d\n"
77              "    wSuiteMask=%d\n    wProductType=%d\n    wReserved=%d\n",
78              ver.wServicePackMajor, ver.wServicePackMinor, ver.wSuiteMask,
79              ver.wProductType, ver.wReserved);
80 }
81
82 static inline int is_dot_dir(const char* x)
83 {
84     return ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))));
85 }
86
87 void remove_dir (const char *dir)
88 {
89     HANDLE  hFind;
90     WIN32_FIND_DATA wfd;
91     char path[MAX_PATH];
92     size_t dirlen = strlen (dir);
93
94     /* Make sure the directory exists before going further */
95     memcpy (path, dir, dirlen);
96     strcpy (path + dirlen++, "\\*");
97     hFind = FindFirstFile (path, &wfd);
98     if (hFind == INVALID_HANDLE_VALUE) return;
99
100     do {
101         char *lp = wfd.cFileName;
102
103         if (!lp[0]) lp = wfd.cAlternateFileName; /* ? FIXME not (!lp) ? */
104         if (is_dot_dir (lp)) continue;
105         strcpy (path + dirlen, lp);
106         if (FILE_ATTRIBUTE_DIRECTORY & wfd.dwFileAttributes)
107             remove_dir(path);
108         else if (!DeleteFile (path))
109             report (R_WARNING, "Can't delete file %s: error %d",
110                     path, GetLastError ());
111     } while (FindNextFile (hFind, &wfd));
112     FindClose (hFind);
113     if (!RemoveDirectory (dir))
114         report (R_WARNING, "Can't remove directory %s: error %d",
115                 dir, GetLastError ());
116 }
117
118 void* extract_rcdata (int id, DWORD* size)
119 {
120     HRSRC rsrc;
121     HGLOBAL hdl;
122     LPVOID addr = NULL;
123     
124     if (!(rsrc = FindResource (0, (LPTSTR)id, TESTRESOURCE)) ||
125         !(*size = SizeofResource (0, rsrc)) ||
126         !(hdl = LoadResource (0, rsrc)) ||
127         !(addr = LockResource (hdl)))
128         report (R_FATAL, "Can't extract test file of id %d: %d",
129                 id, GetLastError ());
130     return addr;
131 }
132
133 /* Fills out the name, is_elf and exename fields */
134 void
135 extract_test (struct wine_test *test, const char *dir, int id)
136 {
137     BYTE* code;
138     DWORD size;
139     FILE* fout;
140     int strlen, bufflen = 128;
141     char *exepos;
142
143     code = extract_rcdata (id, &size);
144     test->name = xmalloc (bufflen);
145     while ((strlen = LoadStringA (NULL, id, test->name, bufflen))
146            == bufflen - 1) {
147         bufflen *= 2;
148         test->name = xrealloc (test->name, bufflen);
149     }
150     if (!strlen) report (R_FATAL, "Can't read name of test %d.", id);
151     test->exename = strmake (NULL, "%s/%s", dir, test->name);
152     exepos = strstr (test->name, "_test.exe");
153     if (!exepos) report (R_FATAL, "Not an .exe file: %s", test->name);
154     *exepos = 0;
155     test->name = xrealloc (test->name, exepos - test->name + 1);
156     report (R_STEP, "Extracting: %s", test->name);
157     test->is_elf = !memcmp (code+1, "ELF", 3);
158
159     if (!(fout = fopen (test->exename, "wb")) ||
160         (fwrite (code, size, 1, fout) != 1) ||
161         fclose (fout)) report (R_FATAL, "Failed to write file %s.",
162                                test->exename);
163 }
164
165 void
166 get_subtests (const char *tempdir, struct wine_test *test, int id)
167 {
168     char *subname;
169     FILE *subfile;
170     size_t subsize, bytes_read, total;
171     char *buffer, *index;
172     const char header[] = "Valid test names:", seps[] = " \r\n";
173     int oldstdout;
174     const char *argv[] = {"wine", NULL, NULL};
175     int allocated;
176
177     subname = tempnam (0, "sub");
178     if (!subname) report (R_FATAL, "Can't name subtests file.");
179     oldstdout = dup (1);
180     if (-1 == oldstdout) report (R_FATAL, "Can't preserve stdout.");
181     subfile = fopen (subname, "w+b");
182     if (!subfile) report (R_FATAL, "Can't open subtests file.");
183     if (-1 == dup2 (fileno (subfile), 1))
184         report (R_FATAL, "Can't redirect output to subtests.");
185     fclose (subfile);
186
187     extract_test (test, tempdir, id);
188     argv[1] = test->exename;
189     if (test->is_elf)
190         spawnvp (_P_WAIT, wineloader, argv);
191     else
192         spawnvp (_P_WAIT, test->exename, argv+1);
193     subsize = lseek (1, 0, SEEK_CUR);
194     buffer = xmalloc (subsize+1);
195
196     lseek (1, 0, SEEK_SET);
197     total = 0;
198     while ((bytes_read = read (1, buffer + total, subsize - total))
199                && (signed)bytes_read != -1)
200             total += bytes_read;
201     if (bytes_read)
202         report (R_FATAL, "Can't get subtests of %s", test->name);
203     buffer[total] = 0;
204     index = strstr (buffer, header);
205     if (!index)
206         report (R_FATAL, "Can't parse subtests output of %s",
207                 test->name);
208     index += sizeof header;
209
210     allocated = 10;
211     test->subtests = xmalloc (allocated * sizeof(char*));
212     test->subtest_count = 0;
213     index = strtok (index, seps);
214     while (index) {
215         if (test->subtest_count == allocated) {
216             allocated *= 2;
217             test->subtests = xrealloc (test->subtests,
218                                        allocated * sizeof(char*));
219         }
220         test->subtests[test->subtest_count++] = strdup (index);
221         index = strtok (NULL, seps);
222     }
223     test->subtests = xrealloc (test->subtests,
224                                test->subtest_count * sizeof(char*));
225     free (buffer);
226     close (1);
227     if (-1 == dup2 (oldstdout, 1))
228         report (R_FATAL, "Can't recover old stdout.");
229     close (oldstdout);
230     if (remove (subname))
231         report (R_FATAL, "Can't remove subtests file.");
232     free (subname);
233 }
234
235 /* Return number of failures, -1 if couldn't spawn process. */
236 int run_test (struct wine_test* test, const char* subtest)
237 {
238     int status;
239     const char *argv[] = {"wine", test->exename, subtest, NULL};
240
241     xprintf ("%s:%s start\n", test->name, subtest);
242     if (test->is_elf)
243         status = spawnvp (_P_WAIT, wineloader, argv);
244     else
245         status = spawnvp (_P_WAIT, test->exename, argv+1);
246     if (status == -1)
247         xprintf ("Can't run: %d, errno=%d: %s\n",
248                  status, errno, strerror (errno));
249     xprintf ("%s:%s done (%d)\n", test->name, subtest, status);
250     return status;
251 }
252
253 BOOL CALLBACK
254 EnumTestFileProc (HMODULE hModule, LPCTSTR lpszType,
255                   LPTSTR lpszName, LONG_PTR lParam)
256 {
257     (*(int*)lParam)++;
258     return TRUE;
259 }
260
261 char *
262 run_tests (char *logname, const char *tag)
263 {
264     int nr_of_files = 0, nr_of_tests = 0, i;
265     char *tempdir;
266     FILE *logfile;
267     char build_tag[128];
268
269     SetErrorMode (SEM_FAILCRITICALERRORS);
270
271     if (!(wineloader = getenv("WINELOADER"))) wineloader = "wine";
272     if (setvbuf (stdout, NULL, _IONBF, 0))
273         report (R_FATAL, "Can't unbuffer output.");
274
275     tempdir = tempnam (0, "wct");
276     if (!tempdir)
277         report (R_FATAL, "Can't name temporary dir (check %%TEMP%%).");
278     report (R_DIR, tempdir);
279     if (!CreateDirectory (tempdir, NULL))
280         report (R_FATAL, "Could not create directory: %s", tempdir);
281
282     if (!logname) {
283         logname = tempnam (0, "res");
284         if (!logname) report (R_FATAL, "Can't name logfile.");
285     }
286     report (R_OUT, logname);
287
288     logfile = fopen (logname, "a");
289     if (!logfile) report (R_FATAL, "Could not open logfile.");
290     if (-1 == dup2 (fileno (logfile), 1))
291         report (R_FATAL, "Can't redirect stdout.");
292     fclose (logfile);
293
294     xprintf ("Version 2\n");
295     i = LoadStringA (GetModuleHandle (NULL), 0,
296                      build_tag, sizeof build_tag);
297     if (i == 0) report (R_FATAL, "Build descriptor not found: %d",
298                         GetLastError ());
299     if (i >= sizeof build_tag)
300         report (R_FATAL, "Build descriptor too long.");
301     xprintf ("Tests from build %s\n", build_tag);
302     xprintf ("Tag: %s\n", tag?tag:"");
303     xprintf ("Operating system version:\n");
304     print_version ();
305     xprintf ("Test output:\n" );
306
307     report (R_STATUS, "Counting tests");
308     if (!EnumResourceNames (NULL, TESTRESOURCE,
309                             EnumTestFileProc, (LPARAM)&nr_of_files))
310         report (R_FATAL, "Can't enumerate test files: %d",
311                 GetLastError ());
312     wine_tests = xmalloc (nr_of_files * sizeof wine_tests[0]);
313
314     report (R_STATUS, "Extracting tests");
315     report (R_PROGRESS, 0, nr_of_files);
316     for (i = 0; i < nr_of_files; i++) {
317         get_subtests (tempdir, wine_tests+i, i+1);
318         nr_of_tests += wine_tests[i].subtest_count;
319     }
320     report (R_DELTA, 0, "Extracting: Done");
321
322     report (R_STATUS, "Running tests");
323     report (R_PROGRESS, 1, nr_of_tests);
324     for (i = 0; i < nr_of_files; i++) {
325         struct wine_test *test = wine_tests + i;
326         int j;
327
328         for (j = 0; j < test->subtest_count; j++) {
329             report (R_STEP, "Running: %s:%s", test->name,
330                     test->subtests[j]);
331             run_test (test, test->subtests[j]);
332         }
333     }
334     report (R_DELTA, 0, "Running: Done");
335
336     report (R_STATUS, "Cleaning up");
337     close (1);
338     remove_dir (tempdir);
339     free (tempdir);
340     free (wine_tests);
341
342     return logname;
343 }
344
345 void
346 usage ()
347 {
348     fprintf (stderr, "\
349 Usage: winetest [OPTION]...\n\n\
350   -c       console mode, no GUI\n\
351   -h       print this message and exit\n\
352   -q       quiet mode, no output at all\n\
353   -o FILE  put report into FILE, do not submit\n\
354   -s FILE  submit FILE, do not run tests\n\
355   -t TAG   include TAG of characters [-.0-9a-zA-Z] in the report\n");
356 }
357
358 /* One can't nest strtok()-s, so here is a replacement. */
359 char *
360 mystrtok (char *newstr)
361 {
362     static char *start, *end;
363     static int finish = 1;
364
365     if (newstr) {
366         start = newstr;
367         finish = 0;
368     } else start = end;
369     if (finish) return NULL;
370     while (*start == ' ') start++;
371     if (*start == 0) return NULL;
372     end = start;
373     while (*end != ' ')
374         if (*end == 0) {
375             finish = 1;
376             return start;
377         } else end++;
378     *end++ = 0;
379     return start;
380 }
381
382 int WINAPI WinMain (HINSTANCE hInst, HINSTANCE hPrevInst,
383                     LPSTR cmdLine, int cmdShow)
384 {
385     char *logname = NULL;
386     char *tag = NULL, *cp;
387     const char *submit = NULL;
388
389     cmdLine = mystrtok (cmdLine);
390     while (cmdLine) {
391         if (*cmdLine == '-')
392             if (cmdLine[2]) {
393                 report (R_ERROR, "Not a single letter option: %s",
394                         cmdLine);
395                 usage ();
396                 exit (2);
397             }
398             cmdLine++;
399             switch (*cmdLine) {
400             case 'c':
401                 report (R_TEXTMODE);
402                 break;
403             case 'h':
404                 usage ();
405                 exit (0);
406             case 'q':
407                 report (R_QUIET);
408                 break;
409             case 's':
410                 submit = mystrtok (NULL);
411                 if (tag)
412                     report (R_WARNING, "ignoring tag for submit");
413                 send_file (submit);
414                 break;
415             case 'o':
416                 logname = mystrtok (NULL);
417                 run_tests (logname, tag);
418                 break;
419             case 't':
420                 tag = mystrtok (NULL);
421                 cp = badtagchar (tag);
422                 if (cp) {
423                     report (R_ERROR, "invalid char in tag: %c", *cp);
424                     usage ();
425                     exit (2);
426                 }
427                 break;
428             default:
429                 report (R_ERROR, "invalid option: -%c", *cmdLine);
430                 usage ();
431                 exit (2);
432             }
433         cmdLine = mystrtok (NULL);
434     }
435     if (!logname && !submit) {
436         report (R_STATUS, "Starting up");
437         logname = run_tests (NULL, tag);
438         if (report (R_ASK, MB_YESNO, "Do you want to submit the "
439                     "test results?") == IDYES)
440             if (!send_file (logname) && remove (logname))
441                 report (R_WARNING, "Can't remove logfile: %d.", errno);
442         free (logname);
443         report (R_STATUS, "Finished");
444     }
445     exit (0);
446 }