Take advantage of the new winegcc -B support.
[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, ".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.");
298     if (i >= sizeof build_tag)
299         report (R_FATAL, "Build descriptor too long.");
300     xprintf ("Tests from build %s\n", build_tag);
301     xprintf ("Tag: %s", tag?tag:"");
302     xprintf ("Operating system version:\n");
303     print_version ();
304     xprintf ("Test output:\n" );
305
306     report (R_STATUS, "Counting tests");
307     if (!EnumResourceNames (NULL, TESTRESOURCE,
308                             EnumTestFileProc, (LPARAM)&nr_of_files))
309         report (R_FATAL, "Can't enumerate test files: %d",
310                 GetLastError ());
311     wine_tests = xmalloc (nr_of_files * sizeof wine_tests[0]);
312
313     report (R_STATUS, "Extracting tests");
314     report (R_PROGRESS, 0, nr_of_files);
315     for (i = 0; i < nr_of_files; i++) {
316         get_subtests (tempdir, wine_tests+i, i+1);
317         nr_of_tests += wine_tests[i].subtest_count;
318     }
319     report (R_DELTA, 0, "Extracting: Done");
320
321     report (R_STATUS, "Running tests");
322     report (R_PROGRESS, 1, nr_of_tests);
323     for (i = 0; i < nr_of_files; i++) {
324         struct wine_test *test = wine_tests + i;
325         int j;
326
327         for (j = 0; j < test->subtest_count; j++) {
328             report (R_STEP, "Running: %s: %s", test->name,
329                     test->subtests[j]);
330             run_test (test, test->subtests[j]);
331         }
332     }
333     report (R_DELTA, 0, "Running: Done");
334
335     report (R_STATUS, "Cleaning up");
336     close (1);
337     remove_dir (tempdir);
338     free (tempdir);
339     free (wine_tests);
340
341     return logname;
342 }
343
344 void
345 usage ()
346 {
347     fprintf (stderr, "\
348 Usage: winetest [OPTION]...\n\n\
349   -c       console mode, no GUI\n\
350   -h       print this message and exit\n\
351   -q       quiet mode, no output at all\n\
352   -o FILE  put report into FILE, do not submit\n\
353   -s FILE  submit FILE, do not run tests\n\
354   -t TAG   include TAG of characters [-.0-9a-zA-Z] in the report\n");
355 }
356
357 int WINAPI WinMain (HINSTANCE hInst, HINSTANCE hPrevInst,
358                     LPSTR cmdLine, int cmdShow)
359 {
360     char *logname = NULL;
361     char *tag = NULL, *cp;
362     const char *submit = NULL;
363
364     cmdLine = strtok (cmdLine, " ");
365     while (cmdLine) {
366         if (*cmdLine == '-')
367             if (cmdLine[2]) {
368                 report (R_ERROR, "Not a single letter option: %s",
369                         cmdLine);
370                 usage ();
371                 exit (2);
372             }
373             cmdLine++;
374             switch (*cmdLine) {
375             case 'c':
376                 report (R_TEXTMODE);
377                 break;
378             case 'h':
379                 usage ();
380                 exit (0);
381             case 'q':
382                 report (R_QUIET);
383                 break;
384             case 's':
385                 submit = strtok (NULL, " ");
386                 if (tag)
387                     report (R_WARNING, "ignoring tag for submit");
388                 send_file (submit);
389                 break;
390             case 'o':
391                 logname = strtok (NULL, " ");
392                 run_tests (logname, tag);
393                 break;
394             case 't':
395                 tag = strtok (NULL, " ");
396                 cp = badtagchar (tag);
397                 if (cp) {
398                     report (R_ERROR, "invalid char in tag: %c", *cp);
399                     usage ();
400                     exit (2);
401                 }
402                 break;
403             default:
404                 report (R_ERROR, "invalid option: -%c", *cmdLine);
405                 usage ();
406                 exit (2);
407             }
408         cmdLine = strtok (NULL, " ");
409     }
410     if (!logname && !submit) {
411         report (R_STATUS, "Starting up");
412         logname = run_tests (NULL, tag);
413         if (report (R_ASK, MB_YESNO, "Do you want to submit the "
414                     "test results?") == IDYES)
415             if (!send_file (logname) && remove (logname))
416                 report (R_WARNING, "Can't remove logfile: %d.", errno);
417         free (logname);
418         report (R_STATUS, "Finished");
419     }
420     exit (0);
421 }