as already discussed a bit, here's a first shot at testing CreateProcess patch is made of two parts: - extension to existing C test framework to pass argc/argv to any test function - the test itself
any comments are welcome A+
Name: test_arg ChangeLog: now C tests are able to access argc/argv as passed to the program License: X11 GenDate: 2002/04/01 15:49:15 UTC ModifiedFiles: programs/winetest/wtmain.c include/wine/test.h AddedFiles: =================================================================== RCS file: /home/cvs/cvsroot/wine/wine/programs/winetest/wtmain.c,v retrieving revision 1.4 diff -u -u -r1.4 wtmain.c --- programs/winetest/wtmain.c 29 Mar 2002 18:05:17 -0000 1.4 +++ programs/winetest/wtmain.c 30 Mar 2002 06:55:02 -0000 @@ -31,6 +31,10 @@ /* current platform */ const char *winetest_platform = "windows";
+/* passing arguments around */ +static int winetest_argc; +static char** winetest_argv; + struct test { const char *name; @@ -153,6 +157,12 @@ todo_level--; }
+int winetest_get_mainargs( char*** pargv ) +{ + *pargv = winetest_argv; + return winetest_argc; +} + /* Find a test by name */ static const struct test *find_test( const char *name ) { @@ -205,6 +215,9 @@ int main( int argc, char **argv ) { char *p; + + winetest_argc = argc; + winetest_argv = argv;
if ((p = getenv( "WINETEST_PLATFORM" ))) winetest_platform = p; if ((p = getenv( "WINETEST_DEBUG" ))) winetest_debug = atoi(p); Index: include/wine/test.h =================================================================== RCS file: /home/cvs/cvsroot/wine/wine/include/wine/test.h,v retrieving revision 1.3 diff -u -u -r1.3 test.h --- include/wine/test.h 22 Mar 2002 00:58:00 -0000 1.3 +++ include/wine/test.h 30 Mar 2002 06:50:53 -0000 @@ -38,6 +38,7 @@ extern void winetest_start_todo( const char* platform ); extern int winetest_loop_todo(void); extern void winetest_end_todo( const char* platform ); +extern int winetest_get_mainargs( char*** pargv );
#define START_TEST(name) void func_##name(void)
Name: test_cp ChangeLog: added a framework for testing CreateProcess and a few tests License: X11 GenDate: 2002/04/01 16:32:58 UTC ModifiedFiles: dlls/kernel/Makefile.in dlls/kernel/tests/.cvsignore AddedFiles: dlls/kernel/tests/process.c =================================================================== RCS file: /home/cvs/cvsroot/wine/wine/dlls/kernel/Makefile.in,v retrieving revision 1.29 diff -u -u -r1.29 Makefile.in --- dlls/kernel/Makefile.in 27 Mar 2002 21:03:50 -0000 1.29 +++ dlls/kernel/Makefile.in 28 Mar 2002 06:27:06 -0000 @@ -41,7 +41,8 @@
CTESTS = \ tests/alloc.c \ - tests/directory.c + tests/directory.c \ + tests/process.c
PLTESTS = \ tests/atom.pl Index: dlls/kernel/tests/.cvsignore =================================================================== RCS file: /home/cvs/cvsroot/wine/wine/dlls/kernel/tests/.cvsignore,v retrieving revision 1.3 diff -u -u -r1.3 .cvsignore --- dlls/kernel/tests/.cvsignore 27 Mar 2002 21:18:02 -0000 1.3 +++ dlls/kernel/tests/.cvsignore 28 Mar 2002 06:27:19 -0000 @@ -2,4 +2,5 @@ atom.ok directory.ok kernel32_test.spec.c +process.ok testlist.c --- /dev/null Thu Jan 1 01:00:00 1970 +++ dlls/kernel/tests/process.c Mon Apr 1 18:32:36 2002 @@ -0,0 +1,732 @@ +/* + * Unit test suite for CreateProcess function. + * + * Copyright 2002 Eric Pouech + * + * 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 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include "winbase.h" +#include "winuser.h" +#include "wine/test.h" + +static char base[MAX_PATH]; +static char selfname[MAX_PATH]; +static char resfile[MAX_PATH]; + +static int myARGC; +static char** myARGV; + +/* ---------------- portable memory allocation thingie */ + +static char memory[16384]; +static char* memory_index = memory; + +static char* grab_memory(size_t len) +{ + char* ret = memory_index; + /* align on dword */ + len = (len + 3) & ~3; + memory_index += len; + assert(memory_index <= memory + sizeof(memory)); + return ret; +} + +static void release_memory(void) +{ + memory_index = memory; +} + +/* ---------------- simplistic tool to encode/decode strings (to hide \ " ' and such) */ + +static char* encodeA(const char* str) +{ + size_t len; + char* ptr; + int i; + + if (!str) return ""; + len = strlen(str) + 1; + ptr = grab_memory(len * 2 + 1); + for (i = 0; i < len; i++) + sprintf(&ptr[i * 2], "%02x", (unsigned char)str[i]); + ptr[2 * len] = '\0'; + return ptr; +} + +static char* encodeW(const WCHAR* str) +{ + size_t len; + char* ptr; + int i; + + if (!str) return ""; + len = lstrlenW(str) + 1; + ptr = grab_memory(len * 4 + 1); + assert(ptr); + for (i = 0; i < len; i++) + sprintf(&ptr[i * 4], "%04x", (unsigned int)(unsigned short)str[i]); + ptr[4 * len] = '\0'; + return ptr; +} + +static unsigned decode_char(char c) +{ + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'a' && c <= 'f') return c - 'a' + 10; + assert(c >= 'A' && c <= 'F'); + return c - 'A' + 10; +} + +static char* decodeA(const char* str) +{ + size_t len; + char* ptr; + int i; + + len = strlen(str) / 2; + if (!len--) return NULL; + ptr = grab_memory(len + 1); + for (i = 0; i < len; i++) + ptr[i] = (decode_char(str[2 * i]) << 4) | decode_char(str[2 * i + 1]); + ptr[len] = '\0'; + return ptr; +} + +static WCHAR* decodeW(const char* str) +{ + size_t len; + WCHAR* ptr; + int i; + + len = strlen(str) / 4; + if (!len--) return NULL; + ptr = (WCHAR*)grab_memory(len * 2 + 1); + for (i = 0; i < len; i++) + ptr[i] = (decode_char(str[4 * i]) << 12) | + (decode_char(str[4 * i + 1]) << 8) | + (decode_char(str[4 * i + 2]) << 4) | + (decode_char(str[4 * i + 3]) << 0); + ptr[len] = '\0'; + return ptr; +} + +/****************************************************************** + * init + * + * generates basic information like: + * base: top of wine tree on linux, curr dir on windows + * selfname: the way to reinvoke ourselves + */ +static int init(void) +{ + myARGC = winetest_get_mainargs( &myARGV ); + if (strcmp(winetest_platform, "windows")) + { + char* ptr = getenv("WINELOADER"); + + if (!ptr) return 0; + strcpy(base, ptr); + ptr = strrchr(base, '/'); + if (!ptr) return 0; + *ptr = '\0'; + /* be sure to use absolute pathnames so we can change dirs whenever we want */ + sprintf(selfname, + "%s/programs/winetest/runtest -q -P wine -M kernel32.dll -T %s -p %s/dlls/kernel/tests/kernel32_test", + base, base, base); + } + else + { + if (!GetCurrentDirectoryA(sizeof(base), base)) return 0; + strcpy(selfname, myARGV[0]); + } + + return 1; +} + +/****************************************************************** + * get_file_name + * + * generates an absolute file_name for temporary file + * + */ +static int get_file_name(char* buf, size_t len, const char* hint) +{ + char* ptr; + if (strcmp(winetest_platform, "windows")) + { + sprintf(buf, "%s/%s", base, hint); + for (ptr = buf; *ptr; ptr++) + if (*ptr == '\') *ptr = '/'; + } + else + { + sprintf(buf, "%s\%s", base, hint); + for (ptr = buf; *ptr; ptr++) + if (*ptr == '/') *ptr = '\'; + } + //fprintf(stderr, "resfile=%s\n", buf); + return 0; +} + +/****************************************************************** + * doChild + * + * output most of the information in the child process + */ +static void doChild(const char* file) +{ + FILE* f = fopen(file, "w+"); + STARTUPINFOA siA; + STARTUPINFOW siW; + int i; + char* ptrA; + WCHAR* ptrW; + char bufA[MAX_PATH]; + WCHAR bufW[MAX_PATH]; + + if (!f) return; + + /* output of startup info (Ansi) */ + GetStartupInfoA(&siA); + fprintf(f, + "[StartupInfoA]\ncb=%08ld\nlpDesktop=%s\nlpTitle=%s\n" + "dwX=%lu\ndwY=%lu\ndwXSize=%lu\ndwYSize=%lu\n" + "dwXCountChars=%lu\ndwYCountChars=%lu\ndwFillAttribute=%lu\n" + "dwFlags=%lu\nwShowWindow=%u\n" + "hStdInput=%lu\nhStdOutput=%lu\nhStdError=%lu\n\n", + siA.cb, encodeA(siA.lpDesktop), encodeA(siA.lpTitle), + siA.dwX, siA.dwY, siA.dwXSize, siA.dwYSize, + siA.dwXCountChars, siA.dwYCountChars, siA.dwFillAttribute, + siA.dwFlags, siA.wShowWindow, + (DWORD)siA.hStdInput, (DWORD)siA.hStdOutput, (DWORD)siA.hStdError); + + /* since GetStartupInfoW is only implemented in win2k, + * zero out before calling so we can notice the difference + */ + memset(&siW, 0, sizeof(siW)); + GetStartupInfoW(&siW); + fprintf(f, + "[StartupInfoW]\ncb=%08ld\nlpDesktop=%s\nlpTitle=%s\n" + "dwX=%lu\ndwY=%lu\ndwXSize=%lu\ndwYSize=%lu\n" + "dwXCountChars=%lu\ndwYCountChars=%lu\ndwFillAttribute=%lu\n" + "dwFlags=%lu\nwShowWindow=%u\n" + "hStdInput=%lu\nhStdOutput=%lu\nhStdError=%lu\n\n", + siW.cb, encodeW(siW.lpDesktop), encodeW(siW.lpTitle), + siW.dwX, siW.dwY, siW.dwXSize, siW.dwYSize, + siW.dwXCountChars, siW.dwYCountChars, siW.dwFillAttribute, + siW.dwFlags, siW.wShowWindow, + (DWORD)siW.hStdInput, (DWORD)siW.hStdOutput, (DWORD)siW.hStdError); + + /* Arguments */ + fprintf(f, "[Arguments]\nargcA=%d\n", myARGC); + for (i = 0; i < myARGC; i++) + { + fprintf(f, "argvA%d=%s\n", i, encodeA(myARGV[i])); + } + fprintf(f, "CommandLineA=%s\n", encodeA(GetCommandLineA())); + +#if 0 + int argcW; + WCHAR** argvW; + + /* this is part of shell32... and should be tested there */ + argvW = CommandLineToArgvW(GetCommandLineW(), &argcW); + for (i = 0; i < argcW; i++) + { + fprintf(f, "argvW%d=%s\n", i, encodeW(argvW[i])); + } +#endif + fprintf(f, "CommandLineW=%s\n", encodeW(GetCommandLineW())); + fprintf(f, "\n"); + + /* output of environment (Ansi) */ + ptrA = GetEnvironmentStringsA(); + if (ptrA) + { + fprintf(f, "[EnvironmentA]\n"); + i = 0; + while (*ptrA) + { + if (strlen(ptrA) < 128) + { + fprintf(f, "env%d=%s\n", i, encodeA(ptrA)); + i++; + } + ptrA += strlen(ptrA) + 1; + } + fprintf(f, "\n"); + } + + /* output of environment (Unicode) */ + ptrW = GetEnvironmentStringsW(); + if (ptrW) + { + fprintf(f, "[EnvironmentW]\n"); + i = 0; + while (*ptrW) + { + if (lstrlenW(ptrW) < 128) + { + fprintf(f, "env%d=%s\n", i, encodeW(ptrW)); + i++; + } + ptrW += lstrlenW(ptrW) + 1; + } + fprintf(f, "\n"); + } + + fprintf(f, "[Misc]\n"); + if (GetCurrentDirectoryA(sizeof(bufA), bufA)) + fprintf(f, "cdirA=%s\n", encodeA(bufA)); + if (GetCurrentDirectoryW(sizeof(bufW) / sizeof(bufW[0]), bufW)) + fprintf(f, "cdirW=%s\n", encodeW(bufW)); + fprintf(f, "\n"); + + fclose(f); +} + +static char* getChildString(const char* sect, const char* key) +{ + char buf[1024]; + char* ret; + + GetPrivateProfileStringA(sect, key, "-", buf, sizeof(buf), resfile); + if (buf[0] == '\0' || (buf[0] == '-' && buf[1] == '\0')) return NULL; + assert(!(strlen(buf) & 1)); + ret = decodeA(buf); + return ret; +} + +static int cmpChildString(const char* sect, const char* key, const char* result) +{ + char* ptr = getChildString(sect, key); + int ret; + + if (!ptr && !result) return 0; + if (!ptr) return -1; + if (!result) return 1; + if ((ret = strcmp(ptr, result)) == 0) return 0; + fprintf(stderr, "%s <> %s\n", ptr, result); + return ret; +} + +/* FIXME: this may be moved to the wtmain.c file, because it may be needed by + * others... (windows uses stricmp while Un*x uses strcasecmp...) + */ +static int mystrcasecmp(const char* p1, const char* p2) +{ + char c1, c2; + + c1 = c2 = '@'; + while (c1 == c2 && c1) + { + c1 = *p1++; c2 = *p2++; + if (c1 != c2) + { + c1 = toupper(c1); c2 = toupper(c2); + } + } + return c1 - c2; +} + +static int cmpiChildString(const char* sect, const char* key, const char* result) +{ + char* ptr = getChildString(sect, key); + int ret; + + if (!ptr && !result) return 0; + if (!ptr) return -1; + if (!result) return 1; + if ((ret = mystrcasecmp(ptr, result)) == 0) return 0; + fprintf(stderr, "%s <I> %s\n", ptr, result); + return ret; +} + +static int cmpChildInt(const char* sect, const char* key, int val) +{ + /* using !val insures that the test will fail if the sect/key isn't present + * in result file + */ + return GetPrivateProfileIntA(sect, key, !val, resfile) == val; +} + +static void test_Startup(void) +{ + char buffer[MAX_PATH]; + PROCESS_INFORMATION info; + STARTUPINFOA startup; + + /* let's start simplistic */ + memset(&startup, 0, sizeof(startup)); + startup.cb = sizeof(startup); + startup.dwFlags = STARTF_USESHOWWINDOW; + startup.wShowWindow = SW_SHOWNORMAL; + + get_file_name(resfile, sizeof(resfile), "foo.ini"); + sprintf(buffer, "%s tests/process.c %s", selfname, resfile); + ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess"); + /* wait for child to terminate */ + ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination"); + /* child process has changed result file, so let profile functions know about it */ + WritePrivateProfileStringA(NULL, NULL, NULL, resfile); + + ok(cmpChildInt("StartupInfoA", "cb", startup.cb), NULL); + ok(cmpChildString("StartupInfoA", "lpDesktop", startup.lpDesktop) == 0, NULL); + ok(cmpChildString("StartupInfoA", "lpTitle", startup.lpTitle) == 0, NULL); + ok(cmpChildInt("StartupInfoA", "dwX", startup.dwX), NULL); + ok(cmpChildInt("StartupInfoA", "dwY", startup.dwY), NULL); + ok(cmpChildInt("StartupInfoA", "dwXSize", startup.dwXSize), NULL); + ok(cmpChildInt("StartupInfoA", "dwYSize", startup.dwYSize), NULL); + ok(cmpChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars), NULL); + ok(cmpChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars), NULL); + ok(cmpChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute), NULL); + ok(cmpChildInt("StartupInfoA", "dwFlags", startup.dwFlags), NULL); + ok(cmpChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow), NULL); + release_memory(); + assert(unlink(resfile) == 0); + + /* not so simplistic now */ + memset(&startup, 0, sizeof(startup)); + startup.cb = sizeof(startup); + startup.dwFlags = STARTF_USESHOWWINDOW; + startup.wShowWindow = SW_SHOWNORMAL; + startup.lpTitle = "I'm the title string"; + startup.lpDesktop = "I'm the desktop string"; + startup.dwXCountChars = 0x12121212; + startup.dwYCountChars = 0x23232323; + startup.dwX = 0x34343434; + startup.dwY = 0x45454545; + startup.dwXSize = 0x56565656; + startup.dwYSize = 0x67676767; + startup.dwFillAttribute = 0xA55A; + + get_file_name(resfile, sizeof(resfile), "foo.ini"); + sprintf(buffer, "%s tests/process.c %s", selfname, resfile); + ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess"); + /* wait for child to terminate */ + ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination"); + /* child process has changed result file, so let profile functions know about it */ + WritePrivateProfileStringA(NULL, NULL, NULL, resfile); + + ok(cmpChildInt("StartupInfoA", "cb", startup.cb), NULL); + ok(cmpChildString("StartupInfoA", "lpDesktop", startup.lpDesktop) == 0, NULL); + ok(cmpChildString("StartupInfoA", "lpTitle", startup.lpTitle) == 0, NULL); + ok(cmpChildInt("StartupInfoA", "dwX", startup.dwX), NULL); + ok(cmpChildInt("StartupInfoA", "dwY", startup.dwY), NULL); + ok(cmpChildInt("StartupInfoA", "dwXSize", startup.dwXSize), NULL); + ok(cmpChildInt("StartupInfoA", "dwYSize", startup.dwYSize), NULL); + ok(cmpChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars), NULL); + ok(cmpChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars), NULL); + ok(cmpChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute), NULL); + ok(cmpChildInt("StartupInfoA", "dwFlags", startup.dwFlags), NULL); + ok(cmpChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow), NULL); + release_memory(); + assert(unlink(resfile) == 0); + + /* not so simplistic now */ + memset(&startup, 0, sizeof(startup)); + startup.cb = sizeof(startup); + startup.dwFlags = STARTF_USESHOWWINDOW; + startup.wShowWindow = SW_SHOWNORMAL; + startup.lpTitle = "I'm the title string"; + startup.lpDesktop = NULL; + startup.dwXCountChars = 0x12121212; + startup.dwYCountChars = 0x23232323; + startup.dwX = 0x34343434; + startup.dwY = 0x45454545; + startup.dwXSize = 0x56565656; + startup.dwYSize = 0x67676767; + startup.dwFillAttribute = 0xA55A; + + get_file_name(resfile, sizeof(resfile), "foo.ini"); + sprintf(buffer, "%s tests/process.c %s", selfname, resfile); + ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess"); + /* wait for child to terminate */ + ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination"); + /* child process has changed result file, so let profile functions know about it */ + WritePrivateProfileStringA(NULL, NULL, NULL, resfile); + + ok(cmpChildInt("StartupInfoA", "cb", startup.cb), NULL); + ok(cmpChildString("StartupInfoA", "lpDesktop", startup.lpDesktop) == 0, NULL); + ok(cmpChildString("StartupInfoA", "lpTitle", startup.lpTitle) == 0, NULL); + ok(cmpChildInt("StartupInfoA", "dwX", startup.dwX), NULL); + ok(cmpChildInt("StartupInfoA", "dwY", startup.dwY), NULL); + ok(cmpChildInt("StartupInfoA", "dwXSize", startup.dwXSize), NULL); + ok(cmpChildInt("StartupInfoA", "dwYSize", startup.dwYSize), NULL); + ok(cmpChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars), NULL); + ok(cmpChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars), NULL); + ok(cmpChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute), NULL); + ok(cmpChildInt("StartupInfoA", "dwFlags", startup.dwFlags), NULL); + ok(cmpChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow), NULL); + release_memory(); + assert(unlink(resfile) == 0); + + /* not so simplistic now */ + memset(&startup, 0, sizeof(startup)); + startup.cb = sizeof(startup); + startup.dwFlags = STARTF_USESHOWWINDOW; + startup.wShowWindow = SW_SHOWNORMAL; + startup.lpTitle = "I'm the title string"; + startup.lpDesktop = ""; + startup.dwXCountChars = 0x12121212; + startup.dwYCountChars = 0x23232323; + startup.dwX = 0x34343434; + startup.dwY = 0x45454545; + startup.dwXSize = 0x56565656; + startup.dwYSize = 0x67676767; + startup.dwFillAttribute = 0xA55A; + + get_file_name(resfile, sizeof(resfile), "foo.ini"); + sprintf(buffer, "%s tests/process.c %s", selfname, resfile); + ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess"); + /* wait for child to terminate */ + ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination"); + /* child process has changed result file, so let profile functions know about it */ + WritePrivateProfileStringA(NULL, NULL, NULL, resfile); + + ok(cmpChildInt("StartupInfoA", "cb", startup.cb), NULL); + todo_wine ok(cmpChildString("StartupInfoA", "lpDesktop", startup.lpDesktop) == 0, NULL); + ok(cmpChildString("StartupInfoA", "lpTitle", startup.lpTitle) == 0, NULL); + ok(cmpChildInt("StartupInfoA", "dwX", startup.dwX), NULL); + ok(cmpChildInt("StartupInfoA", "dwY", startup.dwY), NULL); + ok(cmpChildInt("StartupInfoA", "dwXSize", startup.dwXSize), NULL); + ok(cmpChildInt("StartupInfoA", "dwYSize", startup.dwYSize), NULL); + ok(cmpChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars), NULL); + ok(cmpChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars), NULL); + ok(cmpChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute), NULL); + ok(cmpChildInt("StartupInfoA", "dwFlags", startup.dwFlags), NULL); + ok(cmpChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow), NULL); + release_memory(); + assert(unlink(resfile) == 0); + + /* not so simplistic now */ + memset(&startup, 0, sizeof(startup)); + startup.cb = sizeof(startup); + startup.dwFlags = STARTF_USESHOWWINDOW; + startup.wShowWindow = SW_SHOWNORMAL; + startup.lpTitle = NULL; + startup.lpDesktop = "I'm the desktop string"; + startup.dwXCountChars = 0x12121212; + startup.dwYCountChars = 0x23232323; + startup.dwX = 0x34343434; + startup.dwY = 0x45454545; + startup.dwXSize = 0x56565656; + startup.dwYSize = 0x67676767; + startup.dwFillAttribute = 0xA55A; + + get_file_name(resfile, sizeof(resfile), "foo.ini"); + sprintf(buffer, "%s tests/process.c %s", selfname, resfile); + ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess"); + /* wait for child to terminate */ + ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination"); + /* child process has changed result file, so let profile functions know about it */ + WritePrivateProfileStringA(NULL, NULL, NULL, resfile); + + ok(cmpChildInt("StartupInfoA", "cb", startup.cb), NULL); + ok(cmpChildString("StartupInfoA", "lpDesktop", startup.lpDesktop) == 0, NULL); + ok(cmpChildString("StartupInfoA", "lpTitle", startup.lpTitle) == 0, NULL); + ok(cmpChildInt("StartupInfoA", "dwX", startup.dwX), NULL); + ok(cmpChildInt("StartupInfoA", "dwY", startup.dwY), NULL); + ok(cmpChildInt("StartupInfoA", "dwXSize", startup.dwXSize), NULL); + ok(cmpChildInt("StartupInfoA", "dwYSize", startup.dwYSize), NULL); + ok(cmpChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars), NULL); + ok(cmpChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars), NULL); + ok(cmpChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute), NULL); + ok(cmpChildInt("StartupInfoA", "dwFlags", startup.dwFlags), NULL); + ok(cmpChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow), NULL); + release_memory(); + assert(unlink(resfile) == 0); + + /* not so simplistic now */ + memset(&startup, 0, sizeof(startup)); + startup.cb = sizeof(startup); + startup.dwFlags = STARTF_USESHOWWINDOW; + startup.wShowWindow = SW_SHOWNORMAL; + startup.lpTitle = ""; + startup.lpDesktop = "I'm the desktop string"; + startup.dwXCountChars = 0x12121212; + startup.dwYCountChars = 0x23232323; + startup.dwX = 0x34343434; + startup.dwY = 0x45454545; + startup.dwXSize = 0x56565656; + startup.dwYSize = 0x67676767; + startup.dwFillAttribute = 0xA55A; + + get_file_name(resfile, sizeof(resfile), "foo.ini"); + sprintf(buffer, "%s tests/process.c %s", selfname, resfile); + ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess"); + /* wait for child to terminate */ + ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination"); + /* child process has changed result file, so let profile functions know about it */ + WritePrivateProfileStringA(NULL, NULL, NULL, resfile); + + ok(cmpChildInt("StartupInfoA", "cb", startup.cb), NULL); + ok(cmpChildString("StartupInfoA", "lpDesktop", startup.lpDesktop) == 0, NULL); + todo_wine ok(cmpChildString("StartupInfoA", "lpTitle", startup.lpTitle) == 0, NULL); + ok(cmpChildInt("StartupInfoA", "dwX", startup.dwX), NULL); + ok(cmpChildInt("StartupInfoA", "dwY", startup.dwY), NULL); + ok(cmpChildInt("StartupInfoA", "dwXSize", startup.dwXSize), NULL); + ok(cmpChildInt("StartupInfoA", "dwYSize", startup.dwYSize), NULL); + ok(cmpChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars), NULL); + ok(cmpChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars), NULL); + ok(cmpChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute), NULL); + ok(cmpChildInt("StartupInfoA", "dwFlags", startup.dwFlags), NULL); + ok(cmpChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow), NULL); + release_memory(); + assert(unlink(resfile) == 0); + + /* not so simplistic now */ + memset(&startup, 0, sizeof(startup)); + startup.cb = sizeof(startup); + startup.dwFlags = STARTF_USESHOWWINDOW; + startup.wShowWindow = SW_SHOWNORMAL; + startup.lpTitle = ""; + startup.lpDesktop = ""; + startup.dwXCountChars = 0x12121212; + startup.dwYCountChars = 0x23232323; + startup.dwX = 0x34343434; + startup.dwY = 0x45454545; + startup.dwXSize = 0x56565656; + startup.dwYSize = 0x67676767; + startup.dwFillAttribute = 0xA55A; + + get_file_name(resfile, sizeof(resfile), "foo.ini"); + sprintf(buffer, "%s tests/process.c %s", selfname, resfile); + ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess"); + /* wait for child to terminate */ + ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination"); + /* child process has changed result file, so let profile functions know about it */ + WritePrivateProfileStringA(NULL, NULL, NULL, resfile); + + ok(cmpChildInt("StartupInfoA", "cb", startup.cb), NULL); + todo_wine ok(cmpChildString("StartupInfoA", "lpDesktop", startup.lpDesktop) == 0, NULL); + todo_wine ok(cmpChildString("StartupInfoA", "lpTitle", startup.lpTitle) == 0, NULL); + ok(cmpChildInt("StartupInfoA", "dwX", startup.dwX), NULL); + ok(cmpChildInt("StartupInfoA", "dwY", startup.dwY), NULL); + ok(cmpChildInt("StartupInfoA", "dwXSize", startup.dwXSize), NULL); + ok(cmpChildInt("StartupInfoA", "dwYSize", startup.dwYSize), NULL); + ok(cmpChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars), NULL); + ok(cmpChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars), NULL); + ok(cmpChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute), NULL); + ok(cmpChildInt("StartupInfoA", "dwFlags", startup.dwFlags), NULL); + ok(cmpChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow), NULL); + release_memory(); + assert(unlink(resfile) == 0); + + /* TODO: test for A/W and W/A and W/W */ +} + +static void test_CommandLine(void) +{ + char buffer[MAX_PATH]; + PROCESS_INFORMATION info; + STARTUPINFOA startup; + + memset(&startup, 0, sizeof(startup)); + startup.cb = sizeof(startup); + startup.dwFlags = STARTF_USESHOWWINDOW; + startup.wShowWindow = SW_SHOWNORMAL; + + /* the basics */ + get_file_name(resfile, sizeof(resfile), "foo.ini"); + sprintf(buffer, "%s tests/process.c %s "C:\Program Files\my nice app.exe"", selfname, resfile); + ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess"); + /* wait for child to terminate */ + ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination"); + /* child process has changed result file, so let profile functions know about it */ + WritePrivateProfileStringA(NULL, NULL, NULL, resfile); + + ok(cmpChildInt("Arguments", "argcA", 4), NULL); + ok(cmpChildString("Arguments", "argvA3", "C:\Program Files\my nice app.exe") == 0, NULL); + ok(cmpChildString("Arguments", "argvA4", NULL) == 0, NULL); + ok(cmpChildString("Arguments", "CommandLineA", buffer) == 0, NULL); + release_memory(); + assert(unlink(resfile) == 0); + + memset(&startup, 0, sizeof(startup)); + startup.cb = sizeof(startup); + startup.dwFlags = STARTF_USESHOWWINDOW; + startup.wShowWindow = SW_SHOWNORMAL; + + /* from François */ + get_file_name(resfile, sizeof(resfile), "foo.ini"); + sprintf(buffer, "%s tests/process.c %s "a\"b\\" c\" d", selfname, resfile); + ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess"); + /* wait for child to terminate */ + ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination"); + /* child process has changed result file, so let profile functions know about it */ + WritePrivateProfileStringA(NULL, NULL, NULL, resfile); + + ok(cmpChildInt("Arguments", "argcA", 6), NULL); + ok(cmpChildString("Arguments", "argvA3", "a"b\") == 0, NULL); + ok(cmpChildString("Arguments", "argvA4", "c"") == 0, NULL); + ok(cmpChildString("Arguments", "argvA5", "d") == 0, NULL); + ok(cmpChildString("Arguments", "argvA6", NULL) == 0, NULL); + ok(cmpChildString("Arguments", "CommandLineA", buffer) == 0, NULL); + release_memory(); + assert(unlink(resfile) == 0); +} + +static void test_Directory(void) +{ + char buffer[MAX_PATH]; + PROCESS_INFORMATION info; + STARTUPINFOA startup; + + memset(&startup, 0, sizeof(startup)); + startup.cb = sizeof(startup); + startup.dwFlags = STARTF_USESHOWWINDOW; + startup.wShowWindow = SW_SHOWNORMAL; + + /* the basics */ + get_file_name(resfile, sizeof(resfile), "foo.ini"); + sprintf(buffer, "%s tests/process.c %s", selfname, resfile); + ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, "C:\Windows", &startup, &info), "CreateProcess"); + /* wait for child to terminate */ + ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination"); + /* child process has changed result file, so let profile functions know about it */ + WritePrivateProfileStringA(NULL, NULL, NULL, resfile); + + ok(cmpiChildString("Misc", "cdirA", "C:\Windows") == 0, NULL); + release_memory(); + assert(unlink(resfile) == 0); +} + +START_TEST(process) +{ + int b = init(); + ok(b, "Basic init of CreateProcess test"); + if (!b) return; + + if (myARGC >= 3) + { + doChild(myARGV[2]); + return; + } + test_Startup(); + test_CommandLine(); + test_Directory(); + + /* things that can be tested: + * lookup: check the way program to be executed is searched + * environment: check environment string passing + * handles: check the handle inheritance stuff (+sec options) + * console: check if console creation parameters work + */ +}