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 + */ +}
Eric Pouech eric.pouech@wanadoo.fr writes:
+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;
+}
You should never do that kind of things. The platform must be used exclusively for todo tests, not for changing the test behavior. And backslashes should work fine under Wine too.
Alexandre Julliard a écrit :
Eric Pouech eric.pouech@wanadoo.fr writes:
+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;
+}
You should never do that kind of things. The platform must be used exclusively for todo tests, not for changing the test behavior. And backslashes should work fine under Wine too.
the point here is not really part of the test. I needed a way to send back information from the child to the parent. To minimize the scope of the test, I didn't want to use too many Windows functions here, and decided to use libc file IO for that. The encoding here is just to use the same absolute filename (absolute because tests may change the current directory, so relative paths are no good, name with libc semantics)
of course, I could use regular windows file name (or even another way of sharing information (registry, anon map, pipe...)), but I wanted the test to be as independent as possible on other APIs
so, my final questions: what is your direction here: to test a specific API, should we limit the scaffholding (provide by other APIs), or should we just use them as freely as needed
My point here is that the more APIs we use, the more regressions we'll have in the tests after one evilish modification, hence complicating its search.
A+
Eric Pouech eric.pouech@wanadoo.fr writes:
so, my final questions: what is your direction here: to test a specific API, should we limit the scaffholding (provide by other APIs), or should we just use them as freely as needed
I don't see any problem in using other APIs, that's just another way to get them some more testing. And it is very important that the tests behave exactly the same under Windows and Wine so that any difference can be found. If we start having different code paths on different platforms we can never be sure that we are actually testing the same thing.
On Mon, 1 Apr 2002, Eric Pouech wrote:
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
Just a minor point, but maybe you could replace:
ok(cmpChildInt("StartupInfoA", "cb", startup.cb), NULL);
with
ok(cmpChildInt("StartupInfoA", "cb", startup.cb), "StartupInfoA.cb");
or even:
#define okChildInt(section,key,expected) \ do { \ int res=GetPrivateProfileIntA(section,key,!expected,resfile); \ ok(res==expected, section key ": got %d instead of %d", \ res, expected); \ } while (0)
...
okChildInt("StartupInfoA", "cb", startup.cb);
The nice thing is that if something goes wrong, all the relevant information is printed. I found that this makes it easier when developping a test.
Same thing for cmpChildString, etc.
-- Francois Gouget fgouget@free.fr http://fgouget.free.fr/ Demander si un ordinateur peut penser revient à demander si un sous-marin peut nager.