2 * Wine Conformance Test EXE
4 * Copyright 2003 Jakob Eriksson (for Solid Form Sweden AB)
5 * Copyright 2003 Dimitrie O. Paun
6 * Copyright 2003 Ferenc Wagner
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.
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.
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
21 * This program is dedicated to Anna Lindh,
22 * Swedish Minister of Foreign Affairs.
23 * Anna was murdered September 11, 2003.
28 #include "wine/port.h"
40 #define TESTRESOURCE "USERDATA"
52 static struct wine_test *wine_tests;
54 static const char *wineloader;
61 ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
62 if (!(ext = GetVersionEx ((OSVERSIONINFO *) &ver)))
64 ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
65 if (!GetVersionEx ((OSVERSIONINFO *) &ver))
66 report (R_FATAL, "Can't get OS version.");
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);
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);
82 static inline int is_dot_dir(const char* x)
84 return ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))));
87 void remove_dir (const char *dir)
92 size_t dirlen = strlen (dir);
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;
101 char *lp = wfd.cFileName;
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)
108 else if (!DeleteFile (path))
109 report (R_WARNING, "Can't delete file %s: error %d",
110 path, GetLastError ());
111 } while (FindNextFile (hFind, &wfd));
113 if (!RemoveDirectory (dir))
114 report (R_WARNING, "Can't remove directory %s: error %d",
115 dir, GetLastError ());
118 void* extract_rcdata (int id, DWORD* size)
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 ());
133 /* Fills out the name, is_elf and exename fields */
135 extract_test (struct wine_test *test, const char *dir, int id)
140 int strlen, bufflen = 128;
143 code = extract_rcdata (id, &size);
144 test->name = xmalloc (bufflen);
145 while ((strlen = LoadStringA (NULL, id, test->name, bufflen))
148 test->name = xrealloc (test->name, bufflen);
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);
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);
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.",
166 get_subtests (const char *tempdir, struct wine_test *test, int id)
170 size_t subsize, bytes_read, total;
171 char *buffer, *index;
172 const char header[] = "Valid test names:", seps[] = " \r\n";
174 const char *argv[] = {"wine", NULL, NULL};
177 subname = tempnam (0, "sub");
178 if (!subname) report (R_FATAL, "Can't name subtests file.");
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.");
187 extract_test (test, tempdir, id);
188 argv[1] = test->exename;
190 spawnvp (_P_WAIT, wineloader, argv);
192 spawnvp (_P_WAIT, test->exename, argv+1);
193 subsize = lseek (1, 0, SEEK_CUR);
194 buffer = xmalloc (subsize+1);
196 lseek (1, 0, SEEK_SET);
198 while ((bytes_read = read (1, buffer + total, subsize - total))
199 && (signed)bytes_read != -1)
202 report (R_FATAL, "Can't get subtests of %s", test->name);
204 index = strstr (buffer, header);
206 report (R_FATAL, "Can't parse subtests output of %s",
208 index += sizeof header;
211 test->subtests = xmalloc (allocated * sizeof(char*));
212 test->subtest_count = 0;
213 index = strtok (index, seps);
215 if (test->subtest_count == allocated) {
217 test->subtests = xrealloc (test->subtests,
218 allocated * sizeof(char*));
220 test->subtests[test->subtest_count++] = strdup (index);
221 index = strtok (NULL, seps);
223 test->subtests = xrealloc (test->subtests,
224 test->subtest_count * sizeof(char*));
227 if (-1 == dup2 (oldstdout, 1))
228 report (R_FATAL, "Can't recover old stdout.");
230 if (remove (subname))
231 report (R_FATAL, "Can't remove subtests file.");
235 /* Return number of failures, -1 if couldn't spawn process. */
236 int run_test (struct wine_test* test, const char* subtest)
239 const char *argv[] = {"wine", test->exename, subtest, NULL};
241 xprintf ("%s:%s start\n", test->name, subtest);
243 status = spawnvp (_P_WAIT, wineloader, argv);
245 status = spawnvp (_P_WAIT, test->exename, argv+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);
254 EnumTestFileProc (HMODULE hModule, LPCTSTR lpszType,
255 LPTSTR lpszName, LONG_PTR lParam)
262 run_tests (char *logname, const char *tag)
264 int nr_of_files = 0, nr_of_tests = 0, i;
269 SetErrorMode (SEM_FAILCRITICALERRORS);
271 if (!(wineloader = getenv("WINELOADER"))) wineloader = "wine";
272 if (setvbuf (stdout, NULL, _IONBF, 0))
273 report (R_FATAL, "Can't unbuffer output.");
275 tempdir = tempnam (0, "wct");
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);
283 logname = tempnam (0, "res");
284 if (!logname) report (R_FATAL, "Can't name logfile.");
286 report (R_OUT, logname);
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.");
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",
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");
305 xprintf ("Test output:\n" );
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",
312 wine_tests = xmalloc (nr_of_files * sizeof wine_tests[0]);
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;
320 report (R_DELTA, 0, "Extracting: Done");
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;
328 for (j = 0; j < test->subtest_count; j++) {
329 report (R_STEP, "Running: %s:%s", test->name,
331 run_test (test, test->subtests[j]);
334 report (R_DELTA, 0, "Running: Done");
336 report (R_STATUS, "Cleaning up");
338 remove_dir (tempdir);
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");
358 /* One can't nest strtok()-s, so here is a replacement. */
360 mystrtok (char *newstr)
362 static char *start, *end;
363 static int finish = 1;
369 if (finish) return NULL;
370 while (*start == ' ') start++;
371 if (*start == 0) return NULL;
382 int WINAPI WinMain (HINSTANCE hInst, HINSTANCE hPrevInst,
383 LPSTR cmdLine, int cmdShow)
385 char *logname = NULL;
386 char *tag = NULL, *cp;
387 const char *submit = NULL;
389 cmdLine = mystrtok (cmdLine);
393 report (R_ERROR, "Not a single letter option: %s",
410 submit = mystrtok (NULL);
412 report (R_WARNING, "ignoring tag for submit");
416 logname = mystrtok (NULL);
417 run_tests (logname, tag);
420 tag = mystrtok (NULL);
421 cp = badtagchar (tag);
423 report (R_ERROR, "invalid char in tag: %c", *cp);
429 report (R_ERROR, "invalid option: -%c", *cmdLine);
433 cmdLine = mystrtok (NULL);
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);
443 report (R_STATUS, "Finished");