ok() does not support '%S'. Store the Ansi version, convert to Unicode
[wine] / dlls / kernel / tests / process.c
1 /*
2  * Unit test suite for CreateProcess function.
3  *
4  * Copyright 2002 Eric Pouech
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <assert.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24
25 #include "wine/test.h"
26 #include "winbase.h"
27 #include "winuser.h"
28
29 static char     base[MAX_PATH];
30 static char     selfname[MAX_PATH];
31 static char     resfile[MAX_PATH];
32
33 static int      myARGC;
34 static char**   myARGV;
35
36 /* as some environment variables get very long on Unix, we only test for
37  * the first 127 bytes
38  */
39 #define MAX_LISTED_ENV_VAR      128
40
41 /* ---------------- portable memory allocation thingie */
42
43 static char     memory[1024*32];
44 static char*    memory_index = memory;
45
46 static char*    grab_memory(size_t len)
47 {
48     char*       ret = memory_index;
49     /* align on dword */
50     len = (len + 3) & ~3;
51     memory_index += len;
52     assert(memory_index <= memory + sizeof(memory));
53     return ret;
54 }
55
56 static void     release_memory(void)
57 {
58     memory_index = memory;
59 }
60
61 /* ---------------- simplistic tool to encode/decode strings (to hide \ " ' and such) */
62
63 static char*    encodeA(const char* str)
64 {
65     char*       ptr;
66     size_t      len,i;
67
68     if (!str) return "";
69     len = strlen(str) + 1;
70     ptr = grab_memory(len * 2 + 1);
71     for (i = 0; i < len; i++)
72         sprintf(&ptr[i * 2], "%02x", (unsigned char)str[i]);
73     ptr[2 * len] = '\0';
74     return ptr;
75 }
76
77 static char*    encodeW(const WCHAR* str)
78 {
79     char*       ptr;
80     size_t      len,i;
81
82     if (!str) return "";
83     len = lstrlenW(str) + 1;
84     ptr = grab_memory(len * 4 + 1);
85     assert(ptr);
86     for (i = 0; i < len; i++)
87         sprintf(&ptr[i * 4], "%04x", (unsigned int)(unsigned short)str[i]);
88     ptr[4 * len] = '\0';
89     return ptr;
90 }
91
92 static unsigned decode_char(char c)
93 {
94     if (c >= '0' && c <= '9') return c - '0';
95     if (c >= 'a' && c <= 'f') return c - 'a' + 10;
96     assert(c >= 'A' && c <= 'F');
97     return c - 'A' + 10;
98 }
99
100 static char*    decodeA(const char* str)
101 {
102     char*       ptr;
103     size_t      len,i;
104
105     len = strlen(str) / 2;
106     if (!len--) return NULL;
107     ptr = grab_memory(len + 1);
108     for (i = 0; i < len; i++)
109         ptr[i] = (decode_char(str[2 * i]) << 4) | decode_char(str[2 * i + 1]);
110     ptr[len] = '\0';
111     return ptr;
112 }
113
114 #if 0
115 /* This will be needed to decode Unicode strings saved by the child process
116  * when we test Unicode functions.
117  */
118 static WCHAR*   decodeW(const char* str)
119 {
120     size_t      len;
121     WCHAR*      ptr;
122     int         i;
123
124     len = strlen(str) / 4;
125     if (!len--) return NULL;
126     ptr = (WCHAR*)grab_memory(len * 2 + 1);
127     for (i = 0; i < len; i++)
128         ptr[i] = (decode_char(str[4 * i]) << 12) |
129             (decode_char(str[4 * i + 1]) << 8) |
130             (decode_char(str[4 * i + 2]) << 4) |
131             (decode_char(str[4 * i + 3]) << 0);
132     ptr[len] = '\0';
133     return ptr;
134 }
135 #endif
136
137 /******************************************************************
138  *              init
139  *
140  * generates basic information like:
141  *      base:           absolute path to curr dir
142  *      selfname:       the way to reinvoke ourselves
143  */
144 static int     init(void)
145 {
146     myARGC = winetest_get_mainargs( &myARGV );
147     if (!GetCurrentDirectoryA(sizeof(base), base)) return 0;
148     strcpy(selfname, myARGV[0]);
149     return 1;
150 }
151
152 /******************************************************************
153  *              get_file_name
154  *
155  * generates an absolute file_name for temporary file
156  *
157  */
158 static void     get_file_name(char* buf)
159 {
160     char        path[MAX_PATH];
161
162     buf[0] = '\0';
163     GetTempPathA(sizeof(path), path);
164     GetTempFileNameA(path, "wt", 0, buf);
165 }
166
167 /******************************************************************
168  *              static void     childPrintf
169  *
170  */
171 static void     childPrintf(HANDLE h, const char* fmt, ...)
172 {
173     va_list     valist;
174     char        buffer[2048];
175     DWORD       w;
176
177     va_start(valist, fmt);
178     vsprintf(buffer, fmt, valist);
179     va_end(valist);
180     WriteFile(h, buffer, strlen(buffer), &w, NULL);
181 }
182
183
184 /******************************************************************
185  *              doChild
186  *
187  * output most of the information in the child process
188  */
189 static void     doChild(const char* file)
190 {
191     STARTUPINFOA        siA;
192     STARTUPINFOW        siW;
193     int                 i;
194     char*               ptrA;
195     WCHAR*              ptrW;
196     char                bufA[MAX_PATH];
197     WCHAR               bufW[MAX_PATH];
198     HANDLE              hFile = CreateFileA(file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
199
200     if (hFile == INVALID_HANDLE_VALUE) return;
201
202     /* output of startup info (Ansi) */
203     GetStartupInfoA(&siA);
204     childPrintf(hFile,
205                 "[StartupInfoA]\ncb=%08ld\nlpDesktop=%s\nlpTitle=%s\n"
206                 "dwX=%lu\ndwY=%lu\ndwXSize=%lu\ndwYSize=%lu\n"
207                 "dwXCountChars=%lu\ndwYCountChars=%lu\ndwFillAttribute=%lu\n"
208                 "dwFlags=%lu\nwShowWindow=%u\n"
209                 "hStdInput=%lu\nhStdOutput=%lu\nhStdError=%lu\n\n",
210                 siA.cb, encodeA(siA.lpDesktop), encodeA(siA.lpTitle),
211                 siA.dwX, siA.dwY, siA.dwXSize, siA.dwYSize,
212                 siA.dwXCountChars, siA.dwYCountChars, siA.dwFillAttribute,
213                 siA.dwFlags, siA.wShowWindow,
214                 (DWORD)siA.hStdInput, (DWORD)siA.hStdOutput, (DWORD)siA.hStdError);
215
216     /* since GetStartupInfoW is only implemented in win2k,
217      * zero out before calling so we can notice the difference
218      */
219     memset(&siW, 0, sizeof(siW));
220     GetStartupInfoW(&siW);
221     childPrintf(hFile,
222                 "[StartupInfoW]\ncb=%08ld\nlpDesktop=%s\nlpTitle=%s\n"
223                 "dwX=%lu\ndwY=%lu\ndwXSize=%lu\ndwYSize=%lu\n"
224                 "dwXCountChars=%lu\ndwYCountChars=%lu\ndwFillAttribute=%lu\n"
225                 "dwFlags=%lu\nwShowWindow=%u\n"
226                 "hStdInput=%lu\nhStdOutput=%lu\nhStdError=%lu\n\n",
227                 siW.cb, encodeW(siW.lpDesktop), encodeW(siW.lpTitle),
228                 siW.dwX, siW.dwY, siW.dwXSize, siW.dwYSize,
229                 siW.dwXCountChars, siW.dwYCountChars, siW.dwFillAttribute,
230                 siW.dwFlags, siW.wShowWindow,
231                 (DWORD)siW.hStdInput, (DWORD)siW.hStdOutput, (DWORD)siW.hStdError);
232
233     /* Arguments */
234     childPrintf(hFile, "[Arguments]\nargcA=%d\n", myARGC);
235     for (i = 0; i < myARGC; i++)
236     {
237         childPrintf(hFile, "argvA%d=%s\n", i, encodeA(myARGV[i]));
238     }
239     childPrintf(hFile, "CommandLineA=%s\n", encodeA(GetCommandLineA()));
240
241 #if 0
242     int                 argcW;
243     WCHAR**             argvW;
244
245     /* this is part of shell32... and should be tested there */
246     argvW = CommandLineToArgvW(GetCommandLineW(), &argcW);
247     for (i = 0; i < argcW; i++)
248     {
249         childPrintf(hFile, "argvW%d=%s\n", i, encodeW(argvW[i]));
250     }
251 #endif
252     childPrintf(hFile, "CommandLineW=%s\n\n", encodeW(GetCommandLineW()));
253
254     /* output of environment (Ansi) */
255     ptrA = GetEnvironmentStringsA();
256     if (ptrA)
257     {
258         char    env_var[MAX_LISTED_ENV_VAR];
259
260         childPrintf(hFile, "[EnvironmentA]\n");
261         i = 0;
262         while (*ptrA)
263         {
264             strncpy(env_var, ptrA, MAX_LISTED_ENV_VAR - 1);
265             env_var[MAX_LISTED_ENV_VAR - 1] = '\0';
266             childPrintf(hFile, "env%d=%s\n", i, encodeA(env_var));
267             i++;
268             ptrA += strlen(ptrA) + 1;
269         }
270         childPrintf(hFile, "len=%d\n\n", i);
271     }
272
273     /* output of environment (Unicode) */
274     ptrW = GetEnvironmentStringsW();
275     if (ptrW)
276     {
277         WCHAR   env_var[MAX_LISTED_ENV_VAR];
278
279         childPrintf(hFile, "[EnvironmentW]\n");
280         i = 0;
281         while (*ptrW)
282         {
283             lstrcpynW(env_var, ptrW, MAX_LISTED_ENV_VAR - 1);
284             env_var[MAX_LISTED_ENV_VAR - 1] = '\0';
285             childPrintf(hFile, "env%d=%s\n", i, encodeW(ptrW));
286             i++;
287             ptrW += lstrlenW(ptrW) + 1;
288         }
289         childPrintf(hFile, "len=%d\n\n", i);
290     }
291
292     childPrintf(hFile, "[Misc]\n");
293     if (GetCurrentDirectoryA(sizeof(bufA), bufA))
294         childPrintf(hFile, "CurrDirA=%s\n", encodeA(bufA));
295     if (GetCurrentDirectoryW(sizeof(bufW) / sizeof(bufW[0]), bufW))
296         childPrintf(hFile, "CurrDirW=%s\n", encodeW(bufW));
297     childPrintf(hFile, "\n");
298
299     CloseHandle(hFile);
300 }
301
302 static char* getChildString(const char* sect, const char* key)
303 {
304     char        buf[1024];
305     char*       ret;
306
307     GetPrivateProfileStringA(sect, key, "-", buf, sizeof(buf), resfile);
308     if (buf[0] == '\0' || (buf[0] == '-' && buf[1] == '\0')) return NULL;
309     assert(!(strlen(buf) & 1));
310     ret = decodeA(buf);
311     return ret;
312 }
313
314 /* FIXME: this may be moved to the wtmain.c file, because it may be needed by
315  * others... (windows uses stricmp while Un*x uses strcasecmp...)
316  */
317 static int wtstrcasecmp(const char* p1, const char* p2)
318 {
319     char c1, c2;
320
321     c1 = c2 = '@';
322     while (c1 == c2 && c1)
323     {
324         c1 = *p1++; c2 = *p2++;
325         if (c1 != c2)
326         {
327             c1 = toupper(c1); c2 = toupper(c2);
328         }
329     }
330     return c1 - c2;
331 }
332
333 static int strCmp(const char* s1, const char* s2, BOOL sensitive)
334 {
335     if (!s1 && !s2) return 0;
336     if (!s2) return -1;
337     if (!s1) return 1;
338     return (sensitive) ? strcmp(s1, s2) : wtstrcasecmp(s1, s2);
339 }
340
341 #define okChildString(sect, key, expect) \
342     do { \
343         char* result = getChildString((sect), (key)); \
344         ok(strCmp(result, expect, 1) == 0, "%s:%s expected %s, got %s", (sect), (key), (expect)?(expect):"(null)", result); \
345     } while (0)
346
347 #define okChildIString(sect, key, expect) \
348     do { \
349         char* result = getChildString(sect, key); \
350         ok(strCmp(result, expect, 0) == 0, "%s:%s expected %s, got %s", sect, key, expect, result); \
351     } while (0)
352
353 /* using !expect insures that the test will fail if the sect/key isn't present
354  * in result file
355  */
356 #define okChildInt(sect, key, expect) \
357     do { \
358         UINT result = GetPrivateProfileIntA((sect), (key), !(expect), resfile); \
359         ok(result == expect, "%s:%s expected %d, but got %d\n", (sect), (key), (int)(expect), result); \
360    } while (0)
361
362 static void test_Startup(void)
363 {
364     char                buffer[MAX_PATH];
365     PROCESS_INFORMATION info;
366     STARTUPINFOA        startup,si;
367
368     /* let's start simplistic */
369     memset(&startup, 0, sizeof(startup));
370     startup.cb = sizeof(startup);
371     startup.dwFlags = STARTF_USESHOWWINDOW;
372     startup.wShowWindow = SW_SHOWNORMAL;
373
374     get_file_name(resfile);
375     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
376     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess");
377     /* wait for child to terminate */
378     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
379     /* child process has changed result file, so let profile functions know about it */
380     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
381
382     GetStartupInfoA(&si);
383     okChildInt("StartupInfoA", "cb", startup.cb);
384     okChildString("StartupInfoA", "lpDesktop", si.lpDesktop);
385     okChildString("StartupInfoA", "lpTitle", si.lpTitle);
386     okChildInt("StartupInfoA", "dwX", startup.dwX);
387     okChildInt("StartupInfoA", "dwY", startup.dwY);
388     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
389     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
390     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
391     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
392     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
393     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
394     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
395     release_memory();
396     assert(DeleteFileA(resfile) != 0);
397
398     /* not so simplistic now */
399     memset(&startup, 0, sizeof(startup));
400     startup.cb = sizeof(startup);
401     startup.dwFlags = STARTF_USESHOWWINDOW;
402     startup.wShowWindow = SW_SHOWNORMAL;
403     startup.lpTitle = "I'm the title string";
404     startup.lpDesktop = "I'm the desktop string";
405     startup.dwXCountChars = 0x12121212;
406     startup.dwYCountChars = 0x23232323;
407     startup.dwX = 0x34343434;
408     startup.dwY = 0x45454545;
409     startup.dwXSize = 0x56565656;
410     startup.dwYSize = 0x67676767;
411     startup.dwFillAttribute = 0xA55A;
412
413     get_file_name(resfile);
414     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
415     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess");
416     /* wait for child to terminate */
417     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
418     /* child process has changed result file, so let profile functions know about it */
419     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
420
421     okChildInt("StartupInfoA", "cb", startup.cb);
422     okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
423     okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
424     okChildInt("StartupInfoA", "dwX", startup.dwX);
425     okChildInt("StartupInfoA", "dwY", startup.dwY);
426     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
427     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
428     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
429     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
430     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
431     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
432     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
433     release_memory();
434     assert(DeleteFileA(resfile) != 0);
435
436     /* not so simplistic now */
437     memset(&startup, 0, sizeof(startup));
438     startup.cb = sizeof(startup);
439     startup.dwFlags = STARTF_USESHOWWINDOW;
440     startup.wShowWindow = SW_SHOWNORMAL;
441     startup.lpTitle = "I'm the title string";
442     startup.lpDesktop = NULL;
443     startup.dwXCountChars = 0x12121212;
444     startup.dwYCountChars = 0x23232323;
445     startup.dwX = 0x34343434;
446     startup.dwY = 0x45454545;
447     startup.dwXSize = 0x56565656;
448     startup.dwYSize = 0x67676767;
449     startup.dwFillAttribute = 0xA55A;
450
451     get_file_name(resfile);
452     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
453     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess");
454     /* wait for child to terminate */
455     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
456     /* child process has changed result file, so let profile functions know about it */
457     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
458
459     okChildInt("StartupInfoA", "cb", startup.cb);
460     okChildString("StartupInfoA", "lpDesktop", si.lpDesktop);
461     okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
462     okChildInt("StartupInfoA", "dwX", startup.dwX);
463     okChildInt("StartupInfoA", "dwY", startup.dwY);
464     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
465     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
466     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
467     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
468     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
469     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
470     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
471     release_memory();
472     assert(DeleteFileA(resfile) != 0);
473
474     /* not so simplistic now */
475     memset(&startup, 0, sizeof(startup));
476     startup.cb = sizeof(startup);
477     startup.dwFlags = STARTF_USESHOWWINDOW;
478     startup.wShowWindow = SW_SHOWNORMAL;
479     startup.lpTitle = "I'm the title string";
480     startup.lpDesktop = "";
481     startup.dwXCountChars = 0x12121212;
482     startup.dwYCountChars = 0x23232323;
483     startup.dwX = 0x34343434;
484     startup.dwY = 0x45454545;
485     startup.dwXSize = 0x56565656;
486     startup.dwYSize = 0x67676767;
487     startup.dwFillAttribute = 0xA55A;
488
489     get_file_name(resfile);
490     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
491     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess");
492     /* wait for child to terminate */
493     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
494     /* child process has changed result file, so let profile functions know about it */
495     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
496
497     okChildInt("StartupInfoA", "cb", startup.cb);
498     todo_wine okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
499     okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
500     okChildInt("StartupInfoA", "dwX", startup.dwX);
501     okChildInt("StartupInfoA", "dwY", startup.dwY);
502     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
503     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
504     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
505     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
506     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
507     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
508     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
509     release_memory();
510     assert(DeleteFileA(resfile) != 0);
511
512     /* not so simplistic now */
513     memset(&startup, 0, sizeof(startup));
514     startup.cb = sizeof(startup);
515     startup.dwFlags = STARTF_USESHOWWINDOW;
516     startup.wShowWindow = SW_SHOWNORMAL;
517     startup.lpTitle = NULL;
518     startup.lpDesktop = "I'm the desktop string";
519     startup.dwXCountChars = 0x12121212;
520     startup.dwYCountChars = 0x23232323;
521     startup.dwX = 0x34343434;
522     startup.dwY = 0x45454545;
523     startup.dwXSize = 0x56565656;
524     startup.dwYSize = 0x67676767;
525     startup.dwFillAttribute = 0xA55A;
526
527     get_file_name(resfile);
528     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
529     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess");
530     /* wait for child to terminate */
531     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
532     /* child process has changed result file, so let profile functions know about it */
533     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
534
535     okChildInt("StartupInfoA", "cb", startup.cb);
536     okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
537     okChildString("StartupInfoA", "lpTitle", si.lpTitle);
538     okChildInt("StartupInfoA", "dwX", startup.dwX);
539     okChildInt("StartupInfoA", "dwY", startup.dwY);
540     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
541     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
542     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
543     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
544     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
545     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
546     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
547     release_memory();
548     assert(DeleteFileA(resfile) != 0);
549
550     /* not so simplistic now */
551     memset(&startup, 0, sizeof(startup));
552     startup.cb = sizeof(startup);
553     startup.dwFlags = STARTF_USESHOWWINDOW;
554     startup.wShowWindow = SW_SHOWNORMAL;
555     startup.lpTitle = "";
556     startup.lpDesktop = "I'm the desktop string";
557     startup.dwXCountChars = 0x12121212;
558     startup.dwYCountChars = 0x23232323;
559     startup.dwX = 0x34343434;
560     startup.dwY = 0x45454545;
561     startup.dwXSize = 0x56565656;
562     startup.dwYSize = 0x67676767;
563     startup.dwFillAttribute = 0xA55A;
564
565     get_file_name(resfile);
566     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
567     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess");
568     /* wait for child to terminate */
569     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
570     /* child process has changed result file, so let profile functions know about it */
571     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
572
573     okChildInt("StartupInfoA", "cb", startup.cb);
574     okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
575     todo_wine okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
576     okChildInt("StartupInfoA", "dwX", startup.dwX);
577     okChildInt("StartupInfoA", "dwY", startup.dwY);
578     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
579     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
580     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
581     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
582     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
583     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
584     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
585     release_memory();
586     assert(DeleteFileA(resfile) != 0);
587
588     /* not so simplistic now */
589     memset(&startup, 0, sizeof(startup));
590     startup.cb = sizeof(startup);
591     startup.dwFlags = STARTF_USESHOWWINDOW;
592     startup.wShowWindow = SW_SHOWNORMAL;
593     startup.lpTitle = "";
594     startup.lpDesktop = "";
595     startup.dwXCountChars = 0x12121212;
596     startup.dwYCountChars = 0x23232323;
597     startup.dwX = 0x34343434;
598     startup.dwY = 0x45454545;
599     startup.dwXSize = 0x56565656;
600     startup.dwYSize = 0x67676767;
601     startup.dwFillAttribute = 0xA55A;
602
603     get_file_name(resfile);
604     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
605     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess");
606     /* wait for child to terminate */
607     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
608     /* child process has changed result file, so let profile functions know about it */
609     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
610
611     okChildInt("StartupInfoA", "cb", startup.cb);
612     todo_wine okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
613     todo_wine okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
614     okChildInt("StartupInfoA", "dwX", startup.dwX);
615     okChildInt("StartupInfoA", "dwY", startup.dwY);
616     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
617     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
618     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
619     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
620     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
621     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
622     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
623     release_memory();
624     assert(DeleteFileA(resfile) != 0);
625
626     /* TODO: test for A/W and W/A and W/W */
627 }
628
629 static void test_CommandLine(void)
630 {
631     char                buffer[MAX_PATH];
632     PROCESS_INFORMATION info;
633     STARTUPINFOA        startup;
634
635     memset(&startup, 0, sizeof(startup));
636     startup.cb = sizeof(startup);
637     startup.dwFlags = STARTF_USESHOWWINDOW;
638     startup.wShowWindow = SW_SHOWNORMAL;
639
640     /* the basics */
641     get_file_name(resfile);
642     sprintf(buffer, "%s tests/process.c %s \"C:\\Program Files\\my nice app.exe\"", selfname, resfile);
643     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess");
644     /* wait for child to terminate */
645     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
646     /* child process has changed result file, so let profile functions know about it */
647     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
648
649     okChildInt("Arguments", "argcA", 4);
650     okChildString("Arguments", "argvA3", "C:\\Program Files\\my nice app.exe");
651     okChildString("Arguments", "argvA4", NULL);
652     okChildString("Arguments", "CommandLineA", buffer);
653     release_memory();
654     assert(DeleteFileA(resfile) != 0);
655
656     memset(&startup, 0, sizeof(startup));
657     startup.cb = sizeof(startup);
658     startup.dwFlags = STARTF_USESHOWWINDOW;
659     startup.wShowWindow = SW_SHOWNORMAL;
660
661     /* from Frangois */
662     get_file_name(resfile);
663     sprintf(buffer, "%s tests/process.c %s \"a\\\"b\\\\\" c\\\" d", selfname, resfile);
664     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess");
665     /* wait for child to terminate */
666     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
667     /* child process has changed result file, so let profile functions know about it */
668     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
669
670     okChildInt("Arguments", "argcA", 6);
671     okChildString("Arguments", "argvA3", "a\"b\\");
672     okChildString("Arguments", "argvA4", "c\"");
673     okChildString("Arguments", "argvA5", "d");
674     okChildString("Arguments", "argvA6", NULL);
675     okChildString("Arguments", "CommandLineA", buffer);
676     release_memory();
677     assert(DeleteFileA(resfile) != 0);
678 }
679
680 static void test_Directory(void)
681 {
682     char                buffer[MAX_PATH];
683     PROCESS_INFORMATION info;
684     STARTUPINFOA        startup;
685     char windir[MAX_PATH];
686
687     memset(&startup, 0, sizeof(startup));
688     startup.cb = sizeof(startup);
689     startup.dwFlags = STARTF_USESHOWWINDOW;
690     startup.wShowWindow = SW_SHOWNORMAL;
691
692     /* the basics */
693     get_file_name(resfile);
694     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
695     GetWindowsDirectoryA( windir, sizeof(windir) );
696     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, windir, &startup, &info), "CreateProcess");
697     /* wait for child to terminate */
698     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
699     /* child process has changed result file, so let profile functions know about it */
700     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
701
702     okChildIString("Misc", "CurrDirA", windir);
703     release_memory();
704     assert(DeleteFileA(resfile) != 0);
705 }
706
707 static BOOL is_str_env_drive_dir(const char* str)
708 {
709     return str[0] == '=' && str[1] >= 'A' && str[1] <= 'Z' && str[2] == ':' &&
710         str[3] == '=' && str[4] == str[1];
711 }
712
713 /* compared expected child's environment (in gesA) from actual
714  * environment our child got
715  */
716 static void cmpEnvironment(const char* gesA)
717 {
718     int                 i, clen;
719     const char*         ptrA;
720     char*               res;
721     char                key[32];
722     BOOL                found;
723
724     clen = GetPrivateProfileIntA("EnvironmentA", "len", 0, resfile);
725     
726     /* now look each parent env in child */
727     if ((ptrA = gesA) != NULL)
728     {
729         while (*ptrA)
730         {
731             for (i = 0; i < clen; i++)
732             {
733                 sprintf(key, "env%d", i);
734                 res = getChildString("EnvironmentA", key);
735                 if (strncmp(ptrA, res, MAX_LISTED_ENV_VAR - 1) == 0)
736                     break;
737             }
738             found = i < clen;
739             ok(found, "Parent-env string %s isn't in child process", ptrA);
740             
741             ptrA += strlen(ptrA) + 1;
742             release_memory();
743         }
744     }
745     /* and each child env in parent */
746     for (i = 0; i < clen; i++)
747     {
748         sprintf(key, "env%d", i);
749         res = getChildString("EnvironmentA", key);
750         if ((ptrA = gesA) != NULL)
751         {
752             while (*ptrA)
753             {
754                 if (strncmp(res, ptrA, MAX_LISTED_ENV_VAR - 1) == 0)
755                     break;
756                 ptrA += strlen(ptrA) + 1;
757             }
758             if (!*ptrA) ptrA = NULL;
759         }
760
761         if (!is_str_env_drive_dir(res))
762         {
763             found = ptrA != NULL;
764             ok(found, "Child-env string %s isn't in parent process", res);
765         }
766         /* else => should also test we get the right per drive default directory here... */
767     }
768 }
769
770 static void test_Environment(void)
771 {
772     char                buffer[MAX_PATH];
773     PROCESS_INFORMATION info;
774     STARTUPINFOA        startup;
775     char                child_env[4096];
776     char*               ptr;
777     char*               env;
778
779     memset(&startup, 0, sizeof(startup));
780     startup.cb = sizeof(startup);
781     startup.dwFlags = STARTF_USESHOWWINDOW;
782     startup.wShowWindow = SW_SHOWNORMAL;
783
784     /* the basics */
785     get_file_name(resfile);
786     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
787     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess");
788     /* wait for child to terminate */
789     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
790     /* child process has changed result file, so let profile functions know about it */
791     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
792     
793     cmpEnvironment(GetEnvironmentStringsA());
794     release_memory();
795     assert(DeleteFileA(resfile) != 0);
796
797     memset(&startup, 0, sizeof(startup));
798     startup.cb = sizeof(startup);
799     startup.dwFlags = STARTF_USESHOWWINDOW;
800     startup.wShowWindow = SW_SHOWNORMAL;
801
802     /* the basics */
803     get_file_name(resfile);
804     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
805     ptr = child_env;
806     sprintf(ptr, "=%c:=%s", 'C', "C:\\FOO\\BAR");
807     ptr += strlen(ptr) + 1;
808     strcpy(ptr, "PATH=C:\\WINDOWS;C:\\WINDOWS\\SYSTEM;C:\\MY\\OWN\\DIR");
809     ptr += strlen(ptr) + 1;
810     strcpy(ptr, "FOO=BAR");
811     ptr += strlen(ptr) + 1;
812     strcpy(ptr, "BAR=FOOBAR");
813     ptr += strlen(ptr) + 1;
814     /* copy all existing variables except:
815      * - WINELOADER
816      * - PATH (already set above)
817      * - the directory definitions (=[A-Z]:=)
818      */
819     for (env = GetEnvironmentStringsA(); *env; env += strlen(env) + 1)
820     {
821         if (strncmp(env, "PATH=", 5) != 0 &&
822             strncmp(env, "WINELOADER=", 11) != 0 &&
823             !is_str_env_drive_dir(env))
824         {
825             strcpy(ptr, env);
826             ptr += strlen(ptr) + 1;
827         }
828     }
829     *ptr = '\0';
830     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, child_env, NULL, &startup, &info), "CreateProcess");
831     /* wait for child to terminate */
832     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
833     /* child process has changed result file, so let profile functions know about it */
834     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
835     
836     cmpEnvironment(child_env);
837
838     release_memory();
839     assert(DeleteFileA(resfile) != 0);
840 }
841
842 static  void    test_SuspendFlag(void)
843 {
844     char                buffer[MAX_PATH];
845     PROCESS_INFORMATION info;
846     STARTUPINFOA        startup;
847     DWORD               exit_status;
848
849     /* let's start simplistic */
850     memset(&startup, 0, sizeof(startup));
851     startup.cb = sizeof(startup);
852     startup.dwFlags = STARTF_USESHOWWINDOW;
853     startup.wShowWindow = SW_SHOWNORMAL;
854
855     get_file_name(resfile);
856     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
857     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &startup, &info), "CreateProcess");
858
859     ok(GetExitCodeThread(info.hThread, &exit_status) && exit_status == STILL_ACTIVE, "thread still running");
860     Sleep(8000);
861     ok(GetExitCodeThread(info.hThread, &exit_status) && exit_status == STILL_ACTIVE, "thread still running");
862     ok(ResumeThread(info.hThread) == 1, "Resuming thread\n");
863
864     /* wait for child to terminate */
865     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
866     /* child process has changed result file, so let profile functions know about it */
867     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
868
869     okChildInt("StartupInfoA", "cb", startup.cb);
870     okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
871     okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
872     okChildInt("StartupInfoA", "dwX", startup.dwX);
873     okChildInt("StartupInfoA", "dwY", startup.dwY);
874     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
875     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
876     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
877     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
878     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
879     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
880     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
881     release_memory();
882     assert(DeleteFileA(resfile) != 0);
883 }
884
885 static  void    test_DebuggingFlag(void)
886 {
887     char                buffer[MAX_PATH];
888     PROCESS_INFORMATION info;
889     STARTUPINFOA        startup;
890     DEBUG_EVENT         de;
891     unsigned            dbg = 0;
892
893     /* let's start simplistic */
894     memset(&startup, 0, sizeof(startup));
895     startup.cb = sizeof(startup);
896     startup.dwFlags = STARTF_USESHOWWINDOW;
897     startup.wShowWindow = SW_SHOWNORMAL;
898
899     get_file_name(resfile);
900     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
901     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &startup, &info), "CreateProcess");
902
903     /* get all startup events up to the entry point break exception */
904     do 
905     {
906         ok(WaitForDebugEvent(&de, INFINITE), "reading debug event");
907         ContinueDebugEvent(de.dwProcessId, de.dwThreadId, DBG_CONTINUE);
908         if (de.dwDebugEventCode != EXCEPTION_DEBUG_EVENT) dbg++;
909     } while (de.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT);
910
911     ok(dbg, "I have seen a debug event");
912     /* wait for child to terminate */
913     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
914     /* child process has changed result file, so let profile functions know about it */
915     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
916
917     okChildInt("StartupInfoA", "cb", startup.cb);
918     okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
919     okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
920     okChildInt("StartupInfoA", "dwX", startup.dwX);
921     okChildInt("StartupInfoA", "dwY", startup.dwY);
922     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
923     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
924     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
925     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
926     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
927     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
928     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
929     release_memory();
930     assert(DeleteFileA(resfile) != 0);
931 }
932
933 START_TEST(process)
934 {
935     int b = init();
936     ok(b, "Basic init of CreateProcess test");
937     if (!b) return;
938
939     if (myARGC >= 3)
940     {
941         doChild(myARGV[2]);
942         return;
943     }
944     test_Startup();
945     test_CommandLine();
946     test_Directory();
947     test_Environment();
948     test_SuspendFlag();
949     test_DebuggingFlag();
950     /* things that can be tested:
951      *  lookup:         check the way program to be executed is searched
952      *  handles:        check the handle inheritance stuff (+sec options)
953      *  console:        check if console creation parameters work
954      */
955 }