diff --git a/reactos/lib/kernel32/tests/tests/codepage.c b/reactos/lib/kernel32/tests/tests/codepage.c new file mode 100644 index 00000000000..038b428f9bd --- /dev/null +++ b/reactos/lib/kernel32/tests/tests/codepage.c @@ -0,0 +1,62 @@ +/* + * Unit tests for code page to/from unicode translations + * + * Copyright (c) 2002 Dmitry Timoshkov + * + * 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 + +#include "wine/test.h" +#include "windef.h" +#include "winbase.h" +#include "winnls.h" + +/* lstrcmpW is not supported on Win9x! */ +static int mylstrcmpW(const WCHAR* str1, const WCHAR* str2) +{ + while (*str1 && *str1==*str2) { + str1++; + str2++; + } + return *str1-*str2; +} + +static void test_negative_source_length(void) +{ + int len; + char buf[10]; + WCHAR bufW[10]; + static const WCHAR foobarW[] = {'f','o','o','b','a','r',0}; + + /* Test, whether any negative source length works as strlen() + 1 */ + SetLastError( 0xdeadbeef ); + memset(buf,'x',sizeof(buf)); + len = WideCharToMultiByte(CP_ACP, 0, foobarW, -2002, buf, 10, NULL, NULL); + ok(len == 7 && !lstrcmpA(buf, "foobar") && GetLastError() == 0xdeadbeef, + "WideCharToMultiByte(-2002): len=%d error=%ld\n",len,GetLastError()); + + SetLastError( 0xdeadbeef ); + memset(bufW,'x',sizeof(bufW)); + len = MultiByteToWideChar(CP_ACP, 0, "foobar", -2002, bufW, 10); + ok(len == 7 && !mylstrcmpW(bufW, foobarW) && GetLastError() == 0xdeadbeef, + "MultiByteToWideChar(-2002): len=%d error=%ld\n",len,GetLastError()); +} + +START_TEST(codepage) +{ + test_negative_source_length(); +} diff --git a/reactos/lib/kernel32/tests/tests/format_msg.c b/reactos/lib/kernel32/tests/tests/format_msg.c new file mode 100644 index 00000000000..e6f317c7db2 --- /dev/null +++ b/reactos/lib/kernel32/tests/tests/format_msg.c @@ -0,0 +1,226 @@ +/* Unit test suite for FormatMessageA + * + * Copyright 2002 Mike McCormack for CodeWeavers + * + * 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 + +#include "wine/test.h" +#include "windef.h" +#include "winbase.h" + +/* #define ok(cond,failstr) if(!(cond)) {printf("line %d : %s\n",__LINE__,failstr);exit(1);} */ + +DWORD doit(DWORD flags, LPCVOID src, DWORD msg_id, DWORD lang_id, + LPSTR out, DWORD outsize, ... ) +{ + va_list list; + DWORD r; + + va_start(list, outsize); + r = FormatMessageA(flags, src, msg_id, + lang_id, out, outsize, &list); + va_end(list); + return r; +} + +void test_message_from_string(void) +{ + CHAR out[0x100] = {0}; + DWORD r; + static const WCHAR szwTest[] = { 't','e','s','t',0}; + + /* the basics */ + r = FormatMessageA(FORMAT_MESSAGE_FROM_STRING, "test", 0, + 0, out, sizeof(out)/sizeof(CHAR),NULL); + ok(!strcmp("test", out),"failed out=[%s]\n",out); + ok(r==4,"failed: r=%ld\n",r); + + /* using the format feature */ + r = doit(FORMAT_MESSAGE_FROM_STRING, "%1!s!", 0, + 0, out, sizeof(out)/sizeof(CHAR), "test"); + ok(!strcmp("test", out),"failed out=[%s]\n",out); + ok(r==4,"failed: r=%ld\n",r); + + /* no format */ + r = doit(FORMAT_MESSAGE_FROM_STRING, "%1", 0, + 0, out, sizeof(out)/sizeof(CHAR), "test"); + ok(!strcmp("test", out),"failed out=[%s]\n",out); + ok(r==4,"failed: r=%ld\n",r); + + /* two pieces */ + r = doit(FORMAT_MESSAGE_FROM_STRING, "%1%2", 0, + 0, out, sizeof(out)/sizeof(CHAR), "te","st"); + ok(!strcmp("test", out),"failed out=[%s]\n",out); + ok(r==4,"failed: r=%ld\n",r); + + /* three pieces */ + r = doit(FORMAT_MESSAGE_FROM_STRING, "%1%3%2%1", 0, + 0, out, sizeof(out)/sizeof(CHAR), "t","s","e"); + ok(!strcmp("test", out),"failed out=[%s]\n",out); + ok(r==4,"failed: r=%ld\n",r); + + /* s doesn't seem to work in format strings */ + r = doit(FORMAT_MESSAGE_FROM_STRING, "%!s!", 0, + 0, out, sizeof(out)/sizeof(CHAR), "test"); + ok(!strcmp("!s!", out),"failed out=[%s]\n",out); + ok(r==3,"failed: r=%ld\n",r); + + /* S is unicode */ + r = doit(FORMAT_MESSAGE_FROM_STRING, "%1!S!", 0, + 0, out, sizeof(out)/sizeof(CHAR), szwTest); + ok(!strcmp("test", out),"failed out=[%s]\n",out); + ok(r==4,"failed: r=%ld\n",r); + + /* as characters */ + r = doit(FORMAT_MESSAGE_FROM_STRING, "%1!c!%2!c!%3!c!%1!c!", 0, + 0, out, sizeof(out)/sizeof(CHAR), 't','e','s'); + ok(!strcmp("test", out),"failed out=[%s]\n",out); + ok(r==4,"failed: r=%ld\n",r); + + /* some numbers */ + r = doit(FORMAT_MESSAGE_FROM_STRING, "%1!d!%2!d!%3!d!", 0, + 0, out, sizeof(out)/sizeof(CHAR), 1,2,3); + ok(!strcmp("123", out),"failed out=[%s]\n",out); + ok(r==3,"failed: r=%ld\n",r); + + /* a single digit with some spacing */ + r = doit(FORMAT_MESSAGE_FROM_STRING, "%1!4d!", 0, + 0, out, sizeof(out)/sizeof(CHAR), 1); + ok(!strcmp(" 1", out),"failed out=[%s]\n",out); + ok(r==4,"failed: r=%ld\n",r); + + /* a single digit, left justified */ + r = doit(FORMAT_MESSAGE_FROM_STRING, "%1!-4d!", 0, + 0, out, sizeof(out)/sizeof(CHAR), 1); + ok(!strcmp("1 ", out),"failed out=[%s]\n",out); + ok(r==4,"failed: r=%ld\n",r); + + /* two digit decimal number */ + r = doit(FORMAT_MESSAGE_FROM_STRING, "%1!4d!", 0, + 0, out, sizeof(out)/sizeof(CHAR), 11); + ok(!strcmp(" 11", out),"failed out=[%s]\n",out); + ok(r==4,"failed: r=%ld\n",r); + + /* a hex number */ + r = doit(FORMAT_MESSAGE_FROM_STRING, "%1!4x!", 0, + 0, out, sizeof(out)/sizeof(CHAR), 11); + ok(!strcmp(" b", out),"failed out=[%s]\n",out); + ok(r==4,"failed: r=%ld\n",r); + + /* a hex number, upper case */ + r = doit(FORMAT_MESSAGE_FROM_STRING, "%1!4X!", 0, + 0, out, sizeof(out)/sizeof(CHAR), 11); + ok(!strcmp(" B", out),"failed out=[%s]\n",out); + ok(r==4,"failed: r=%ld\n",r); + + /* a hex number, upper case, left justified */ + r = doit(FORMAT_MESSAGE_FROM_STRING, "%1!-4X!", 0, + 0, out, sizeof(out)/sizeof(CHAR), 11); + ok(!strcmp("B ", out),"failed out=[%s]\n",out); + ok(r==4,"failed: r=%ld\n",r); + + /* a long hex number, upper case */ + r = doit(FORMAT_MESSAGE_FROM_STRING, "%1!4X!", 0, + 0, out, sizeof(out)/sizeof(CHAR), 0x1ab); + ok(!strcmp(" 1AB", out),"failed out=[%s]\n",out); + ok(r==4,"failed: r=%ld\n",r); + + /* two percent... */ + r = doit(FORMAT_MESSAGE_FROM_STRING, " %%%% ", 0, + 0, out, sizeof(out)/sizeof(CHAR)); + ok(!strcmp(" %% ", out),"failed out=[%s]\n",out); + ok(r==4,"failed: r=%ld\n",r); + + /* periods are special cases */ + r = doit(FORMAT_MESSAGE_FROM_STRING, " %.%. %1!d!", 0, + 0, out, sizeof(out)/sizeof(CHAR), 0x1ab); + ok(!strcmp(" .. 427", out),"failed out=[%s]\n",out); + ok(r==7,"failed: r=%ld\n",r); + + /* %0 ends the line */ + r = doit(FORMAT_MESSAGE_FROM_STRING, "test%0test", 0, + 0, out, sizeof(out)/sizeof(CHAR)); + ok(!strcmp("test", out),"failed out=[%s]\n",out); + ok(r==4,"failed: r=%ld\n",r); + + /* %! prints an exclaimation */ + r = doit(FORMAT_MESSAGE_FROM_STRING, "yah%!%0 ", 0, + 0, out, sizeof(out)/sizeof(CHAR)); + ok(!strcmp("yah!", out),"failed out=[%s]\n",out); + ok(r==4,"failed: r=%ld\n",r); + + /* %space */ + r = doit(FORMAT_MESSAGE_FROM_STRING, "% % ", 0, + 0, out, sizeof(out)/sizeof(CHAR)); + ok(!strcmp(" ", out),"failed out=[%s]\n",out); + ok(r==4,"failed: r=%ld\n",r); + + /* line feed */ + r = doit(FORMAT_MESSAGE_FROM_STRING, "hi\n", 0, + 0, out, sizeof(out)/sizeof(CHAR)); + ok(!strcmp("hi\r\n", out),"failed out=[%s]\n",out); + ok(r==4,"failed: r=%ld\n",r); + + /* carriage return line feed */ + r = doit(FORMAT_MESSAGE_FROM_STRING, "hi\r\n", 0, + 0, out, sizeof(out)/sizeof(CHAR)); + ok(!strcmp("hi\r\n", out),"failed out=[%s]\n",out); + ok(r==4,"failed: r=%ld\n",r); + + /* carriage return line feed */ + r = doit(FORMAT_MESSAGE_FROM_STRING, "\r", 0, + 0, out, sizeof(out)/sizeof(CHAR)); + ok(!strcmp("\r\n", out),"failed out=[%s]\n",out); + ok(r==2,"failed: r=%ld\n",r); + + /* carriage return line feed */ + r = doit(FORMAT_MESSAGE_FROM_STRING, "\r\r\n", 0, + 0, out, sizeof(out)/sizeof(CHAR)); + ok(!strcmp("\r\n\r\n", out),"failed out=[%s]\n",out); + ok(r==4,"failed: r=%ld\n",r); + + /* change of pace... test the low byte of dwflags */ + /* line feed */ + r = doit(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_MAX_WIDTH_MASK, "hi\n", 0, + 0, out, sizeof(out)/sizeof(CHAR)); + ok(!strcmp("hi ", out) || !strcmp("hi\r\n", out),"failed out=[%s]\n",out); + ok(r==3 || r==4,"failed: r=%ld\n",r); + + /* carriage return line feed */ + r = doit(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_MAX_WIDTH_MASK, "hi\r\n", 0, + 0, out, sizeof(out)/sizeof(CHAR)); + ok(!strcmp("hi ", out),"failed out=[%s]\n",out); + ok(r==3,"failed: r=%ld\n",r); + + /* carriage return line feed */ + r = doit(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_MAX_WIDTH_MASK, "\r", 0, + 0, out, sizeof(out)/sizeof(CHAR)); + ok(!strcmp(" ", out),"failed out=[%s]\n",out); + ok(r==1,"failed: r=%ld\n",r); + + /* carriage return line feed */ + r = doit(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_MAX_WIDTH_MASK, "\r\r\n", 0, + 0, out, sizeof(out)/sizeof(CHAR)); + ok(!strcmp(" ", out),"failed out=[%s]\n",out); + ok(r==2,"failed: r=%ld\n",r); +} + +START_TEST(format_msg) +{ + test_message_from_string(); +} diff --git a/reactos/lib/kernel32/tests/tests/process.c b/reactos/lib/kernel32/tests/tests/process.c new file mode 100644 index 00000000000..7d962a9f9b0 --- /dev/null +++ b/reactos/lib/kernel32/tests/tests/process.c @@ -0,0 +1,1208 @@ +/* + * 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 +#include +#include +#include + +#include "wine/test.h" +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "wincon.h" +#include "winnls.h" + +static char base[MAX_PATH]; +static char selfname[MAX_PATH]; +static char resfile[MAX_PATH]; + +static int myARGC; +static char** myARGV; + +/* As some environment variables get very long on Unix, we only test for + * the first 127 bytes. + * Note that increasing this value past 256 may exceed the buffer size + * limitations of the *Profile functions (at least on Wine). + */ +#define MAX_LISTED_ENV_VAR 128 + +/* ---------------- portable memory allocation thingie */ + +static char memory[1024*32]; +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 const char* encodeA(const char* str) +{ + char* ptr; + size_t len,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 const char* encodeW(const WCHAR* str) +{ + char* ptr; + size_t len,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) +{ + char* ptr; + size_t len,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; +} + +#if 0 +/* This will be needed to decode Unicode strings saved by the child process + * when we test Unicode functions. + */ +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; +} +#endif + +/****************************************************************** + * init + * + * generates basic information like: + * base: absolute path to curr dir + * selfname: the way to reinvoke ourselves + */ +static int init(void) +{ + myARGC = winetest_get_mainargs( &myARGV ); + 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 void get_file_name(char* buf) +{ + char path[MAX_PATH]; + + buf[0] = '\0'; + GetTempPathA(sizeof(path), path); + GetTempFileNameA(path, "wt", 0, buf); +} + +/****************************************************************** + * static void childPrintf + * + */ +static void childPrintf(HANDLE h, const char* fmt, ...) +{ + va_list valist; + char buffer[1024+4*MAX_LISTED_ENV_VAR]; + DWORD w; + + va_start(valist, fmt); + vsprintf(buffer, fmt, valist); + va_end(valist); + WriteFile(h, buffer, strlen(buffer), &w, NULL); +} + + +/****************************************************************** + * doChild + * + * output most of the information in the child process + */ +static void doChild(const char* file, const char* option) +{ + STARTUPINFOA siA; + STARTUPINFOW siW; + int i; + char* ptrA; + WCHAR* ptrW; + char bufA[MAX_PATH]; + WCHAR bufW[MAX_PATH]; + HANDLE hFile = CreateFileA(file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0); + + if (hFile == INVALID_HANDLE_VALUE) return; + + /* output of startup info (Ansi) */ + GetStartupInfoA(&siA); + childPrintf(hFile, + "[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); + childPrintf(hFile, + "[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 */ + childPrintf(hFile, "[Arguments]\nargcA=%d\n", myARGC); + for (i = 0; i < myARGC; i++) + { + childPrintf(hFile, "argvA%d=%s\n", i, encodeA(myARGV[i])); + } + childPrintf(hFile, "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++) + { + childPrintf(hFile, "argvW%d=%s\n", i, encodeW(argvW[i])); + } +#endif + childPrintf(hFile, "CommandLineW=%s\n\n", encodeW(GetCommandLineW())); + + /* output of environment (Ansi) */ + ptrA = GetEnvironmentStringsA(); + if (ptrA) + { + char env_var[MAX_LISTED_ENV_VAR]; + + childPrintf(hFile, "[EnvironmentA]\n"); + i = 0; + while (*ptrA) + { + strncpy(env_var, ptrA, MAX_LISTED_ENV_VAR - 1); + env_var[MAX_LISTED_ENV_VAR - 1] = '\0'; + childPrintf(hFile, "env%d=%s\n", i, encodeA(env_var)); + i++; + ptrA += strlen(ptrA) + 1; + } + childPrintf(hFile, "len=%d\n\n", i); + } + + /* output of environment (Unicode) */ + ptrW = GetEnvironmentStringsW(); + if (ptrW) + { + WCHAR env_var[MAX_LISTED_ENV_VAR]; + + childPrintf(hFile, "[EnvironmentW]\n"); + i = 0; + while (*ptrW) + { + lstrcpynW(env_var, ptrW, MAX_LISTED_ENV_VAR - 1); + env_var[MAX_LISTED_ENV_VAR - 1] = '\0'; + childPrintf(hFile, "env%d=%s\n", i, encodeW(env_var)); + i++; + ptrW += lstrlenW(ptrW) + 1; + } + childPrintf(hFile, "len=%d\n\n", i); + } + + childPrintf(hFile, "[Misc]\n"); + if (GetCurrentDirectoryA(sizeof(bufA), bufA)) + childPrintf(hFile, "CurrDirA=%s\n", encodeA(bufA)); + if (GetCurrentDirectoryW(sizeof(bufW) / sizeof(bufW[0]), bufW)) + childPrintf(hFile, "CurrDirW=%s\n", encodeW(bufW)); + childPrintf(hFile, "\n"); + + if (option && strcmp(option, "console") == 0) + { + CONSOLE_SCREEN_BUFFER_INFO sbi; + HANDLE hConIn = GetStdHandle(STD_INPUT_HANDLE); + HANDLE hConOut = GetStdHandle(STD_OUTPUT_HANDLE); + DWORD modeIn, modeOut; + + childPrintf(hFile, "[Console]\n"); + if (GetConsoleScreenBufferInfo(hConOut, &sbi)) + { + childPrintf(hFile, "SizeX=%d\nSizeY=%d\nCursorX=%d\nCursorY=%d\nAttributes=%d\n", + sbi.dwSize.X, sbi.dwSize.Y, sbi.dwCursorPosition.X, sbi.dwCursorPosition.Y, sbi.wAttributes); + childPrintf(hFile, "winLeft=%d\nwinTop=%d\nwinRight=%d\nwinBottom=%d\n", + sbi.srWindow.Left, sbi.srWindow.Top, sbi.srWindow.Right, sbi.srWindow.Bottom); + childPrintf(hFile, "maxWinWidth=%d\nmaxWinHeight=%d\n", + sbi.dwMaximumWindowSize.X, sbi.dwMaximumWindowSize.Y); + } + childPrintf(hFile, "InputCP=%d\nOutputCP=%d\n", + GetConsoleCP(), GetConsoleOutputCP()); + if (GetConsoleMode(hConIn, &modeIn)) + childPrintf(hFile, "InputMode=%ld\n", modeIn); + if (GetConsoleMode(hConOut, &modeOut)) + childPrintf(hFile, "OutputMode=%ld\n", modeOut); + + /* now that we have written all relevant information, let's change it */ + ok(SetConsoleCP(1252), "Setting CP\n"); + ok(SetConsoleOutputCP(1252), "Setting SB CP\n"); + ok(SetConsoleMode(hConIn, modeIn ^ 1), "Setting mode (%ld)\n", GetLastError()); + ok(SetConsoleMode(hConOut, modeOut ^ 1), "Setting mode (%ld)\n", GetLastError()); + sbi.dwCursorPosition.X ^= 1; + sbi.dwCursorPosition.Y ^= 1; + ok(SetConsoleCursorPosition(hConOut, sbi.dwCursorPosition), "Setting cursor position (%ld)\n", GetLastError()); + } + if (option && strcmp(option, "stdhandle") == 0) + { + HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE); + HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); + + if (hStdIn != INVALID_HANDLE_VALUE || hStdOut != INVALID_HANDLE_VALUE) + { + char buf[1024]; + DWORD r, w; + + ok(ReadFile(hStdIn, buf, sizeof(buf), &r, NULL) && r > 0, "Reading message from input pipe\n"); + childPrintf(hFile, "[StdHandle]\nmsg=%s\n\n", encodeA(buf)); + ok(WriteFile(hStdOut, buf, r, &w, NULL) && w == r, "Writing message to output pipe\n"); + } + } + + if (option && strcmp(option, "exit_code") == 0) + { + childPrintf(hFile, "[ExitCode]\nvalue=%d\n\n", 123); + CloseHandle(hFile); + ExitProcess(123); + } + + CloseHandle(hFile); +} + +static char* getChildString(const char* sect, const char* key) +{ + char buf[1024+4*MAX_LISTED_ENV_VAR]; + 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; +} + +/* 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 wtstrcasecmp(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 strCmp(const char* s1, const char* s2, BOOL sensitive) +{ + if (!s1 && !s2) return 0; + if (!s2) return -1; + if (!s1) return 1; + return (sensitive) ? strcmp(s1, s2) : wtstrcasecmp(s1, s2); +} + +#define okChildString(sect, key, expect) \ + do { \ + char* result = getChildString((sect), (key)); \ + ok(strCmp(result, expect, 1) == 0, "%s:%s expected '%s', got '%s'\n", (sect), (key), (expect)?(expect):"(null)", result); \ + } while (0) + +#define okChildIString(sect, key, expect) \ + do { \ + char* result = getChildString(sect, key); \ + ok(strCmp(result, expect, 0) == 0, "%s:%s expected '%s', got '%s'\n", sect, key, expect, result); \ + } while (0) + +/* using !expect insures that the test will fail if the sect/key isn't present + * in result file + */ +#define okChildInt(sect, key, expect) \ + do { \ + UINT result = GetPrivateProfileIntA((sect), (key), !(expect), resfile); \ + ok(result == expect, "%s:%s expected %d, but got %d\n", (sect), (key), (int)(expect), result); \ + } while (0) + +static void test_Startup(void) +{ + char buffer[MAX_PATH]; + PROCESS_INFORMATION info; + STARTUPINFOA startup,si; + + /* 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); + sprintf(buffer, "%s tests/process.c %s", selfname, resfile); + ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n"); + /* wait for child to terminate */ + ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n"); + /* child process has changed result file, so let profile functions know about it */ + WritePrivateProfileStringA(NULL, NULL, NULL, resfile); + + GetStartupInfoA(&si); + okChildInt("StartupInfoA", "cb", startup.cb); + okChildString("StartupInfoA", "lpDesktop", si.lpDesktop); + okChildString("StartupInfoA", "lpTitle", si.lpTitle); + okChildInt("StartupInfoA", "dwX", startup.dwX); + okChildInt("StartupInfoA", "dwY", startup.dwY); + okChildInt("StartupInfoA", "dwXSize", startup.dwXSize); + okChildInt("StartupInfoA", "dwYSize", startup.dwYSize); + okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars); + okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars); + okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute); + okChildInt("StartupInfoA", "dwFlags", startup.dwFlags); + okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow); + release_memory(); + assert(DeleteFileA(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); + sprintf(buffer, "%s tests/process.c %s", selfname, resfile); + ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n"); + /* wait for child to terminate */ + ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n"); + /* child process has changed result file, so let profile functions know about it */ + WritePrivateProfileStringA(NULL, NULL, NULL, resfile); + + okChildInt("StartupInfoA", "cb", startup.cb); + okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop); + okChildString("StartupInfoA", "lpTitle", startup.lpTitle); + okChildInt("StartupInfoA", "dwX", startup.dwX); + okChildInt("StartupInfoA", "dwY", startup.dwY); + okChildInt("StartupInfoA", "dwXSize", startup.dwXSize); + okChildInt("StartupInfoA", "dwYSize", startup.dwYSize); + okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars); + okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars); + okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute); + okChildInt("StartupInfoA", "dwFlags", startup.dwFlags); + okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow); + release_memory(); + assert(DeleteFileA(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); + sprintf(buffer, "%s tests/process.c %s", selfname, resfile); + ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n"); + /* wait for child to terminate */ + ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n"); + /* child process has changed result file, so let profile functions know about it */ + WritePrivateProfileStringA(NULL, NULL, NULL, resfile); + + okChildInt("StartupInfoA", "cb", startup.cb); + okChildString("StartupInfoA", "lpDesktop", si.lpDesktop); + okChildString("StartupInfoA", "lpTitle", startup.lpTitle); + okChildInt("StartupInfoA", "dwX", startup.dwX); + okChildInt("StartupInfoA", "dwY", startup.dwY); + okChildInt("StartupInfoA", "dwXSize", startup.dwXSize); + okChildInt("StartupInfoA", "dwYSize", startup.dwYSize); + okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars); + okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars); + okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute); + okChildInt("StartupInfoA", "dwFlags", startup.dwFlags); + okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow); + release_memory(); + assert(DeleteFileA(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); + sprintf(buffer, "%s tests/process.c %s", selfname, resfile); + ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n"); + /* wait for child to terminate */ + ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n"); + /* child process has changed result file, so let profile functions know about it */ + WritePrivateProfileStringA(NULL, NULL, NULL, resfile); + + okChildInt("StartupInfoA", "cb", startup.cb); + todo_wine okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop); + okChildString("StartupInfoA", "lpTitle", startup.lpTitle); + okChildInt("StartupInfoA", "dwX", startup.dwX); + okChildInt("StartupInfoA", "dwY", startup.dwY); + okChildInt("StartupInfoA", "dwXSize", startup.dwXSize); + okChildInt("StartupInfoA", "dwYSize", startup.dwYSize); + okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars); + okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars); + okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute); + okChildInt("StartupInfoA", "dwFlags", startup.dwFlags); + okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow); + release_memory(); + assert(DeleteFileA(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); + sprintf(buffer, "%s tests/process.c %s", selfname, resfile); + ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n"); + /* wait for child to terminate */ + ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n"); + /* child process has changed result file, so let profile functions know about it */ + WritePrivateProfileStringA(NULL, NULL, NULL, resfile); + + okChildInt("StartupInfoA", "cb", startup.cb); + okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop); + okChildString("StartupInfoA", "lpTitle", si.lpTitle); + okChildInt("StartupInfoA", "dwX", startup.dwX); + okChildInt("StartupInfoA", "dwY", startup.dwY); + okChildInt("StartupInfoA", "dwXSize", startup.dwXSize); + okChildInt("StartupInfoA", "dwYSize", startup.dwYSize); + okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars); + okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars); + okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute); + okChildInt("StartupInfoA", "dwFlags", startup.dwFlags); + okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow); + release_memory(); + assert(DeleteFileA(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); + sprintf(buffer, "%s tests/process.c %s", selfname, resfile); + ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n"); + /* wait for child to terminate */ + ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n"); + /* child process has changed result file, so let profile functions know about it */ + WritePrivateProfileStringA(NULL, NULL, NULL, resfile); + + okChildInt("StartupInfoA", "cb", startup.cb); + okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop); + todo_wine okChildString("StartupInfoA", "lpTitle", startup.lpTitle); + okChildInt("StartupInfoA", "dwX", startup.dwX); + okChildInt("StartupInfoA", "dwY", startup.dwY); + okChildInt("StartupInfoA", "dwXSize", startup.dwXSize); + okChildInt("StartupInfoA", "dwYSize", startup.dwYSize); + okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars); + okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars); + okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute); + okChildInt("StartupInfoA", "dwFlags", startup.dwFlags); + okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow); + release_memory(); + assert(DeleteFileA(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); + sprintf(buffer, "%s tests/process.c %s", selfname, resfile); + ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n"); + /* wait for child to terminate */ + ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n"); + /* child process has changed result file, so let profile functions know about it */ + WritePrivateProfileStringA(NULL, NULL, NULL, resfile); + + okChildInt("StartupInfoA", "cb", startup.cb); + todo_wine okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop); + todo_wine okChildString("StartupInfoA", "lpTitle", startup.lpTitle); + okChildInt("StartupInfoA", "dwX", startup.dwX); + okChildInt("StartupInfoA", "dwY", startup.dwY); + okChildInt("StartupInfoA", "dwXSize", startup.dwXSize); + okChildInt("StartupInfoA", "dwYSize", startup.dwYSize); + okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars); + okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars); + okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute); + okChildInt("StartupInfoA", "dwFlags", startup.dwFlags); + okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow); + release_memory(); + assert(DeleteFileA(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); + 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\n"); + /* wait for child to terminate */ + ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n"); + /* child process has changed result file, so let profile functions know about it */ + WritePrivateProfileStringA(NULL, NULL, NULL, resfile); + + okChildInt("Arguments", "argcA", 4); + okChildString("Arguments", "argvA3", "C:\\Program Files\\my nice app.exe"); + okChildString("Arguments", "argvA4", NULL); + okChildString("Arguments", "CommandLineA", buffer); + release_memory(); + assert(DeleteFileA(resfile) != 0); + + memset(&startup, 0, sizeof(startup)); + startup.cb = sizeof(startup); + startup.dwFlags = STARTF_USESHOWWINDOW; + startup.wShowWindow = SW_SHOWNORMAL; + + /* from Frangois */ + get_file_name(resfile); + 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\n"); + /* wait for child to terminate */ + ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n"); + /* child process has changed result file, so let profile functions know about it */ + WritePrivateProfileStringA(NULL, NULL, NULL, resfile); + + okChildInt("Arguments", "argcA", 6); + okChildString("Arguments", "argvA3", "a\"b\\"); + okChildString("Arguments", "argvA4", "c\""); + okChildString("Arguments", "argvA5", "d"); + okChildString("Arguments", "argvA6", NULL); + okChildString("Arguments", "CommandLineA", buffer); + release_memory(); + assert(DeleteFileA(resfile) != 0); +} + +static void test_Directory(void) +{ + char buffer[MAX_PATH]; + PROCESS_INFORMATION info; + STARTUPINFOA startup; + char windir[MAX_PATH]; + + memset(&startup, 0, sizeof(startup)); + startup.cb = sizeof(startup); + startup.dwFlags = STARTF_USESHOWWINDOW; + startup.wShowWindow = SW_SHOWNORMAL; + + /* the basics */ + get_file_name(resfile); + sprintf(buffer, "%s tests/process.c %s", selfname, resfile); + GetWindowsDirectoryA( windir, sizeof(windir) ); + ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, windir, &startup, &info), "CreateProcess\n"); + /* wait for child to terminate */ + ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n"); + /* child process has changed result file, so let profile functions know about it */ + WritePrivateProfileStringA(NULL, NULL, NULL, resfile); + + okChildIString("Misc", "CurrDirA", windir); + release_memory(); + assert(DeleteFileA(resfile) != 0); +} + +static BOOL is_str_env_drive_dir(const char* str) +{ + return str[0] == '=' && str[1] >= 'A' && str[1] <= 'Z' && str[2] == ':' && + str[3] == '=' && str[4] == str[1]; +} + +/* compared expected child's environment (in gesA) from actual + * environment our child got + */ +static void cmpEnvironment(const char* gesA) +{ + int i, clen; + const char* ptrA; + char* res; + char key[32]; + BOOL found; + + clen = GetPrivateProfileIntA("EnvironmentA", "len", 0, resfile); + + /* now look each parent env in child */ + if ((ptrA = gesA) != NULL) + { + while (*ptrA) + { + for (i = 0; i < clen; i++) + { + sprintf(key, "env%d", i); + res = getChildString("EnvironmentA", key); + if (strncmp(ptrA, res, MAX_LISTED_ENV_VAR - 1) == 0) + break; + } + found = i < clen; + ok(found, "Parent-env string %s isn't in child process\n", ptrA); + + ptrA += strlen(ptrA) + 1; + release_memory(); + } + } + /* and each child env in parent */ + for (i = 0; i < clen; i++) + { + sprintf(key, "env%d", i); + res = getChildString("EnvironmentA", key); + if ((ptrA = gesA) != NULL) + { + while (*ptrA) + { + if (strncmp(res, ptrA, MAX_LISTED_ENV_VAR - 1) == 0) + break; + ptrA += strlen(ptrA) + 1; + } + if (!*ptrA) ptrA = NULL; + } + + if (!is_str_env_drive_dir(res)) + { + found = ptrA != NULL; + ok(found, "Child-env string %s isn't in parent process\n", res); + } + /* else => should also test we get the right per drive default directory here... */ + } +} + +static void test_Environment(void) +{ + char buffer[MAX_PATH]; + PROCESS_INFORMATION info; + STARTUPINFOA startup; + char child_env[4096]; + char* ptr; + char* env; + + memset(&startup, 0, sizeof(startup)); + startup.cb = sizeof(startup); + startup.dwFlags = STARTF_USESHOWWINDOW; + startup.wShowWindow = SW_SHOWNORMAL; + + /* the basics */ + get_file_name(resfile); + sprintf(buffer, "%s tests/process.c %s", selfname, resfile); + ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n"); + /* wait for child to terminate */ + ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n"); + /* child process has changed result file, so let profile functions know about it */ + WritePrivateProfileStringA(NULL, NULL, NULL, resfile); + + cmpEnvironment(GetEnvironmentStringsA()); + release_memory(); + assert(DeleteFileA(resfile) != 0); + + memset(&startup, 0, sizeof(startup)); + startup.cb = sizeof(startup); + startup.dwFlags = STARTF_USESHOWWINDOW; + startup.wShowWindow = SW_SHOWNORMAL; + + /* the basics */ + get_file_name(resfile); + sprintf(buffer, "%s tests/process.c %s", selfname, resfile); + ptr = child_env; + sprintf(ptr, "=%c:=%s", 'C', "C:\\FOO\\BAR"); + ptr += strlen(ptr) + 1; + strcpy(ptr, "PATH=C:\\WINDOWS;C:\\WINDOWS\\SYSTEM;C:\\MY\\OWN\\DIR"); + ptr += strlen(ptr) + 1; + strcpy(ptr, "FOO=BAR"); + ptr += strlen(ptr) + 1; + strcpy(ptr, "BAR=FOOBAR"); + ptr += strlen(ptr) + 1; + /* copy all existing variables except: + * - WINELOADER + * - PATH (already set above) + * - the directory definitions (=[A-Z]:=) + */ + for (env = GetEnvironmentStringsA(); *env; env += strlen(env) + 1) + { + if (strncmp(env, "PATH=", 5) != 0 && + strncmp(env, "WINELOADER=", 11) != 0 && + !is_str_env_drive_dir(env)) + { + strcpy(ptr, env); + ptr += strlen(ptr) + 1; + } + } + *ptr = '\0'; + ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, child_env, NULL, &startup, &info), "CreateProcess\n"); + /* wait for child to terminate */ + ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n"); + /* child process has changed result file, so let profile functions know about it */ + WritePrivateProfileStringA(NULL, NULL, NULL, resfile); + + cmpEnvironment(child_env); + + release_memory(); + assert(DeleteFileA(resfile) != 0); +} + +static void test_SuspendFlag(void) +{ + char buffer[MAX_PATH]; + PROCESS_INFORMATION info; + STARTUPINFOA startup, us; + DWORD exit_status; + + /* 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); + sprintf(buffer, "%s tests/process.c %s", selfname, resfile); + ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &startup, &info), "CreateProcess\n"); + + ok(GetExitCodeThread(info.hThread, &exit_status) && exit_status == STILL_ACTIVE, "thread still running\n"); + Sleep(8000); + ok(GetExitCodeThread(info.hThread, &exit_status) && exit_status == STILL_ACTIVE, "thread still running\n"); + ok(ResumeThread(info.hThread) == 1, "Resuming thread\n"); + + /* wait for child to terminate */ + ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n"); + /* child process has changed result file, so let profile functions know about it */ + WritePrivateProfileStringA(NULL, NULL, NULL, resfile); + + GetStartupInfoA(&us); + + okChildInt("StartupInfoA", "cb", startup.cb); + okChildString("StartupInfoA", "lpDesktop", us.lpDesktop); + okChildString("StartupInfoA", "lpTitle", startup.lpTitle); + okChildInt("StartupInfoA", "dwX", startup.dwX); + okChildInt("StartupInfoA", "dwY", startup.dwY); + okChildInt("StartupInfoA", "dwXSize", startup.dwXSize); + okChildInt("StartupInfoA", "dwYSize", startup.dwYSize); + okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars); + okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars); + okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute); + okChildInt("StartupInfoA", "dwFlags", startup.dwFlags); + okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow); + release_memory(); + assert(DeleteFileA(resfile) != 0); +} + +static void test_DebuggingFlag(void) +{ + char buffer[MAX_PATH]; + PROCESS_INFORMATION info; + STARTUPINFOA startup, us; + DEBUG_EVENT de; + unsigned dbg = 0; + + /* 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); + sprintf(buffer, "%s tests/process.c %s", selfname, resfile); + ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &startup, &info), "CreateProcess\n"); + + /* get all startup events up to the entry point break exception */ + do + { + ok(WaitForDebugEvent(&de, INFINITE), "reading debug event\n"); + ContinueDebugEvent(de.dwProcessId, de.dwThreadId, DBG_CONTINUE); + if (de.dwDebugEventCode != EXCEPTION_DEBUG_EVENT) dbg++; + } while (de.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT); + + ok(dbg, "I have seen a debug event\n"); + /* wait for child to terminate */ + ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n"); + /* child process has changed result file, so let profile functions know about it */ + WritePrivateProfileStringA(NULL, NULL, NULL, resfile); + + GetStartupInfoA(&us); + + okChildInt("StartupInfoA", "cb", startup.cb); + okChildString("StartupInfoA", "lpDesktop", us.lpDesktop); + okChildString("StartupInfoA", "lpTitle", startup.lpTitle); + okChildInt("StartupInfoA", "dwX", startup.dwX); + okChildInt("StartupInfoA", "dwY", startup.dwY); + okChildInt("StartupInfoA", "dwXSize", startup.dwXSize); + okChildInt("StartupInfoA", "dwYSize", startup.dwYSize); + okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars); + okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars); + okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute); + okChildInt("StartupInfoA", "dwFlags", startup.dwFlags); + okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow); + release_memory(); + assert(DeleteFileA(resfile) != 0); +} + +static void test_Console(void) +{ + char buffer[MAX_PATH]; + PROCESS_INFORMATION info; + STARTUPINFOA startup, us; + SECURITY_ATTRIBUTES sa; + CONSOLE_SCREEN_BUFFER_INFO sbi, sbiC; + DWORD modeIn, modeOut, modeInC, modeOutC; + DWORD cpIn, cpOut, cpInC, cpOutC; + DWORD w; + HANDLE hChildIn, hChildInInh, hChildOut, hChildOutInh, hParentIn, hParentOut; + const char* msg = "This is a std-handle inheritance test."; + unsigned msg_len; + + memset(&startup, 0, sizeof(startup)); + startup.cb = sizeof(startup); + startup.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES; + startup.wShowWindow = SW_SHOWNORMAL; + + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = TRUE; + + startup.hStdInput = CreateFileA("CONIN$", GENERIC_READ|GENERIC_WRITE, 0, &sa, OPEN_EXISTING, 0, 0); + startup.hStdOutput = CreateFileA("CONOUT$", GENERIC_READ|GENERIC_WRITE, 0, &sa, OPEN_EXISTING, 0, 0); + + /* first, we need to be sure we're attached to a console */ + if (startup.hStdInput == INVALID_HANDLE_VALUE || startup.hStdOutput == INVALID_HANDLE_VALUE) + { + /* we're not attached to a console, let's do it */ + AllocConsole(); + startup.hStdInput = CreateFileA("CONIN$", GENERIC_READ|GENERIC_WRITE, 0, &sa, OPEN_EXISTING, 0, 0); + startup.hStdOutput = CreateFileA("CONOUT$", GENERIC_READ|GENERIC_WRITE, 0, &sa, OPEN_EXISTING, 0, 0); + } + /* now verify everything's ok */ + ok(startup.hStdInput != INVALID_HANDLE_VALUE, "Opening ConIn\n"); + ok(startup.hStdOutput != INVALID_HANDLE_VALUE, "Opening ConOut\n"); + startup.hStdError = startup.hStdOutput; + + ok(GetConsoleScreenBufferInfo(startup.hStdOutput, &sbi), "Getting sb info\n"); + ok(GetConsoleMode(startup.hStdInput, &modeIn) && + GetConsoleMode(startup.hStdOutput, &modeOut), "Getting console modes\n"); + cpIn = GetConsoleCP(); + cpOut = GetConsoleOutputCP(); + + get_file_name(resfile); + sprintf(buffer, "%s tests/process.c %s console", selfname, resfile); + ok(CreateProcessA(NULL, buffer, NULL, NULL, TRUE, 0, NULL, NULL, &startup, &info), "CreateProcess\n"); + + /* wait for child to terminate */ + ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n"); + /* child process has changed result file, so let profile functions know about it */ + WritePrivateProfileStringA(NULL, NULL, NULL, resfile); + + /* now get the modification the child has made, and resets parents expected values */ + ok(GetConsoleScreenBufferInfo(startup.hStdOutput, &sbiC), "Getting sb info\n"); + ok(GetConsoleMode(startup.hStdInput, &modeInC) && + GetConsoleMode(startup.hStdOutput, &modeOutC), "Getting console modes\n"); + + SetConsoleMode(startup.hStdInput, modeIn); + SetConsoleMode(startup.hStdOutput, modeOut); + + cpInC = GetConsoleCP(); + cpOutC = GetConsoleOutputCP(); + SetConsoleCP(cpIn); + SetConsoleOutputCP(cpOut); + + GetStartupInfoA(&us); + + okChildInt("StartupInfoA", "cb", startup.cb); + okChildString("StartupInfoA", "lpDesktop", us.lpDesktop); + okChildString("StartupInfoA", "lpTitle", startup.lpTitle); + okChildInt("StartupInfoA", "dwX", startup.dwX); + okChildInt("StartupInfoA", "dwY", startup.dwY); + okChildInt("StartupInfoA", "dwXSize", startup.dwXSize); + okChildInt("StartupInfoA", "dwYSize", startup.dwYSize); + okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars); + okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars); + okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute); + okChildInt("StartupInfoA", "dwFlags", startup.dwFlags); + okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow); + + /* check child correctly inherited the console */ + okChildInt("StartupInfoA", "hStdInput", (DWORD)startup.hStdInput); + okChildInt("StartupInfoA", "hStdOutput", (DWORD)startup.hStdOutput); + okChildInt("StartupInfoA", "hStdError", (DWORD)startup.hStdError); + okChildInt("Console", "SizeX", (DWORD)sbi.dwSize.X); + okChildInt("Console", "SizeY", (DWORD)sbi.dwSize.Y); + okChildInt("Console", "CursorX", (DWORD)sbi.dwCursorPosition.X); + okChildInt("Console", "CursorY", (DWORD)sbi.dwCursorPosition.Y); + okChildInt("Console", "Attributes", sbi.wAttributes); + okChildInt("Console", "winLeft", (DWORD)sbi.srWindow.Left); + okChildInt("Console", "winTop", (DWORD)sbi.srWindow.Top); + okChildInt("Console", "winRight", (DWORD)sbi.srWindow.Right); + okChildInt("Console", "winBottom", (DWORD)sbi.srWindow.Bottom); + okChildInt("Console", "maxWinWidth", (DWORD)sbi.dwMaximumWindowSize.X); + okChildInt("Console", "maxWinHeight", (DWORD)sbi.dwMaximumWindowSize.Y); + okChildInt("Console", "InputCP", cpIn); + okChildInt("Console", "OutputCP", cpOut); + okChildInt("Console", "InputMode", modeIn); + okChildInt("Console", "OutputMode", modeOut); + + todo_wine ok(cpInC == 1252, "Wrong console CP (expected 1252 got %ld/%ld)\n", cpInC, cpIn); + todo_wine ok(cpOutC == 1252, "Wrong console-SB CP (expected 1252 got %ld/%ld)\n", cpOutC, cpOut); + ok(modeInC == (modeIn ^ 1), "Wrong console mode\n"); + ok(modeOutC == (modeOut ^ 1), "Wrong console-SB mode\n"); + ok(sbiC.dwCursorPosition.X == (sbi.dwCursorPosition.X ^ 1), "Wrong cursor position\n"); + ok(sbiC.dwCursorPosition.Y == (sbi.dwCursorPosition.Y ^ 1), "Wrong cursor position\n"); + + release_memory(); + assert(DeleteFileA(resfile) != 0); + + ok(CreatePipe(&hParentIn, &hChildOut, NULL, 0), "Creating parent-input pipe\n"); + ok(DuplicateHandle(GetCurrentProcess(), hChildOut, GetCurrentProcess(), + &hChildOutInh, 0, TRUE, DUPLICATE_SAME_ACCESS), + "Duplicating as inheritable child-output pipe\n"); + CloseHandle(hChildOut); + + ok(CreatePipe(&hChildIn, &hParentOut, NULL, 0), "Creating parent-output pipe\n"); + ok(DuplicateHandle(GetCurrentProcess(), hChildIn, GetCurrentProcess(), + &hChildInInh, 0, TRUE, DUPLICATE_SAME_ACCESS), + "Duplicating as inheritable child-input pipe\n"); + CloseHandle(hChildIn); + + memset(&startup, 0, sizeof(startup)); + startup.cb = sizeof(startup); + startup.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES; + startup.wShowWindow = SW_SHOWNORMAL; + startup.hStdInput = hChildInInh; + startup.hStdOutput = hChildOutInh; + startup.hStdError = hChildOutInh; + + get_file_name(resfile); + sprintf(buffer, "%s tests/process.c %s stdhandle", selfname, resfile); + ok(CreateProcessA(NULL, buffer, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &startup, &info), "CreateProcess\n"); + ok(CloseHandle(hChildInInh), "Closing handle\n"); + ok(CloseHandle(hChildOutInh), "Closing handle\n"); + + msg_len = strlen(msg) + 1; + ok(WriteFile(hParentOut, msg, msg_len, &w, NULL), "Writing to child\n"); + ok(w == msg_len, "Should have written %u bytes, actually wrote %lu\n", msg_len, w); + memset(buffer, 0, sizeof(buffer)); + ok(ReadFile(hParentIn, buffer, sizeof(buffer), &w, NULL), "Reading from child\n"); + ok(strcmp(buffer, msg) == 0, "Should have received '%s'\n", msg); + + /* wait for child to terminate */ + ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n"); + /* child process has changed result file, so let profile functions know about it */ + WritePrivateProfileStringA(NULL, NULL, NULL, resfile); + + okChildString("StdHandle", "msg", msg); + + release_memory(); + assert(DeleteFileA(resfile) != 0); +} + +static void test_ExitCode(void) +{ + char buffer[MAX_PATH]; + PROCESS_INFORMATION info; + STARTUPINFOA startup; + DWORD code; + + /* 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); + sprintf(buffer, "%s tests/process.c %s exit_code", selfname, resfile); + ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info), "CreateProcess\n"); + + /* wait for child to terminate */ + ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n"); + /* child process has changed result file, so let profile functions know about it */ + WritePrivateProfileStringA(NULL, NULL, NULL, resfile); + + ok(GetExitCodeProcess(info.hProcess, &code), "Getting exit code\n"); + okChildInt("ExitCode", "value", code); + + release_memory(); + assert(DeleteFileA(resfile) != 0); +} + +START_TEST(process) +{ + int b = init(); + ok(b, "Basic init of CreateProcess test\n"); + if (!b) return; + + if (myARGC >= 3) + { + doChild(myARGV[2], (myARGC == 3) ? NULL : myARGV[3]); + return; + } + test_Startup(); + test_CommandLine(); + test_Directory(); + test_Environment(); + test_SuspendFlag(); + test_DebuggingFlag(); + test_Console(); + test_ExitCode(); + /* things that can be tested: + * lookup: check the way program to be executed is searched + * handles: check the handle inheritance stuff (+sec options) + * console: check if console creation parameters work + */ +} diff --git a/reactos/lib/kernel32/tests/tests/thread.c b/reactos/lib/kernel32/tests/tests/thread.c new file mode 100644 index 00000000000..e9b8b2d42a6 --- /dev/null +++ b/reactos/lib/kernel32/tests/tests/thread.c @@ -0,0 +1,572 @@ +/* + * Unit test suite for directory functions. + * + * Copyright 2002 Geoffrey Hausheer + * + * 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 + */ + +/* Define _WIN32_WINNT to get SetThreadIdealProcessor on Windows */ +#define _WIN32_WINNT 0x0500 + +#include + +#include "wine/test.h" +#include +#include +#include +#include + +/* Specify the number of simultaneous threads to test */ +#define NUM_THREADS 4 +/* Specify whether to test the extended priorities for Win2k/XP */ +#define USE_EXTENDED_PRIORITIES 0 +/* Specify whether to test the stack allocation in CreateThread */ +#define CHECK_STACK 0 + +/* Set CHECK_STACK to 1 if you want to try to test the stack-limit from + CreateThread. So far I have been unable to make this work, and + I am in doubt as to how portable it is. Also, according to MSDN, + you shouldn't mix C-run-time-libraries (i.e. alloca) with CreateThread. + Anyhow, the check is currently commented out +*/ +#if CHECK_STACK + #ifdef __try + #define __TRY __try + #define __EXCEPT __except + #define __ENDTRY + #else + #include "wine/exception.h" + #endif +#endif + +typedef BOOL (WINAPI *GetThreadPriorityBoost_t)(HANDLE,PBOOL); +static GetThreadPriorityBoost_t pGetThreadPriorityBoost=NULL; + +typedef HANDLE (WINAPI *OpenThread_t)(DWORD,BOOL,DWORD); +static OpenThread_t pOpenThread=NULL; + +typedef DWORD (WINAPI *SetThreadIdealProcessor_t)(HANDLE,DWORD); +static SetThreadIdealProcessor_t pSetThreadIdealProcessor=NULL; + +typedef BOOL (WINAPI *SetThreadPriorityBoost_t)(HANDLE,BOOL); +static SetThreadPriorityBoost_t pSetThreadPriorityBoost=NULL; + +/* Functions not tested yet: + AttachThreadInput + CreateRemoteThread + SetThreadContext + SwitchToThread + +In addition there are no checks that the inheritance works properly in +CreateThread +*/ + +DWORD tlsIndex; + +typedef struct { + int threadnum; + HANDLE *event; + DWORD *threadmem; +} t1Struct; + +/* Basic test that simulatneous threads can access shared memory, + that the thread local storage routines work correctly, and that + threads actually run concurrently +*/ +VOID WINAPI threadFunc1(t1Struct *tstruct) +{ + int i; +/* write our thread # into shared memory */ + tstruct->threadmem[tstruct->threadnum]=GetCurrentThreadId(); + ok(TlsSetValue(tlsIndex,(LPVOID)(tstruct->threadnum+1))!=0, + "TlsSetValue failed\n"); +/* The threads synchronize before terminating. This is done by + Signaling an event, and waiting for all events to occur +*/ + SetEvent(tstruct->event[tstruct->threadnum]); + WaitForMultipleObjects(NUM_THREADS,tstruct->event,TRUE,INFINITE); +/* Double check that all threads really did run by validating that + they have all written to the shared memory. There should be no race + here, since all threads were synchronized after the write.*/ + for(i=0;ithreadmem[i]==0) ; + } +/* Check that noone cahnged our tls memory */ + ok((int)TlsGetValue(tlsIndex)-1==tstruct->threadnum, + "TlsGetValue failed\n"); + ExitThread(NUM_THREADS+tstruct->threadnum); +} + +VOID WINAPI threadFunc2() +{ + ExitThread(99); +} + +VOID WINAPI threadFunc3() +{ + HANDLE thread; + thread=GetCurrentThread(); + SuspendThread(thread); + ExitThread(99); +} + +VOID WINAPI threadFunc4(HANDLE event) +{ + if(event != NULL) { + SetEvent(event); + } + Sleep(99000); + ExitThread(0); +} + +#if CHECK_STACK +VOID WINAPI threadFunc5(DWORD *exitCode) +{ + SYSTEM_INFO sysInfo; + sysInfo.dwPageSize=0; + GetSystemInfo(&sysInfo); + *exitCode=0; + __TRY + { + alloca(2*sysInfo.dwPageSize); + } + __EXCEPT(1) { + *exitCode=1; + } + __ENDTRY + ExitThread(0); +} +#endif + +/* Check basic funcationality of CreateThread and Tls* functions */ +VOID test_CreateThread_basic() +{ + HANDLE thread[NUM_THREADS],event[NUM_THREADS]; + DWORD threadid[NUM_THREADS],curthreadId; + DWORD threadmem[NUM_THREADS]; + DWORD exitCode; + t1Struct tstruct[NUM_THREADS]; + int error; + DWORD i,j; +/* Retrieve current Thread ID for later comparisons */ + curthreadId=GetCurrentThreadId(); +/* Allocate some local storage */ + ok((tlsIndex=TlsAlloc())!=TLS_OUT_OF_INDEXES,"TlsAlloc failed\n"); +/* Create events for thread synchronization */ + for(i=0;i0,"GetSystemInfo should return a valid page size\n"); + thread = CreateThread(NULL,sysInfo.dwPageSize, + (LPTHREAD_START_ROUTINE)threadFunc5,&exitCode, + 0,&threadId); + ok(WaitForSingleObject(thread,5000)==WAIT_OBJECT_0, + "TerminateThread didn't work\n"); + ok(exitCode==1,"CreateThread did not obey stack-size-limit\n"); + ok(CloseHandle(thread)!=0,"CloseHandle failed\n"); +#endif +} + +/* Check whether setting/retrieving thread priorities works */ +VOID test_thread_priority() +{ + HANDLE curthread,access_thread; + DWORD curthreadId,exitCode; + int min_priority=-2,max_priority=2; + BOOL disabled; + int i; + + curthread=GetCurrentThread(); + curthreadId=GetCurrentThreadId(); +/* Check thread priority */ +/* NOTE: on Win2k/XP priority can be from -7 to 6. All other platforms it + is -2 to 2. However, even on a real Win2k system, using thread + priorities beyond the -2 to 2 range does not work. If you want to try + anyway, enable USE_EXTENDED_PRIORITIES +*/ + ok(GetThreadPriority(curthread)==THREAD_PRIORITY_NORMAL, + "GetThreadPriority Failed\n"); + + if (pOpenThread) { +/* check that access control is obeyed */ + access_thread=pOpenThread(THREAD_ALL_ACCESS & + (~THREAD_QUERY_INFORMATION) & (~THREAD_SET_INFORMATION), + 0,curthreadId); + ok(access_thread!=NULL,"OpenThread returned an invalid handle\n"); + if (access_thread!=NULL) { + ok(SetThreadPriority(access_thread,1)==0, + "SetThreadPriority did not obey access restrictions\n"); + ok(GetThreadPriority(access_thread)==THREAD_PRIORITY_ERROR_RETURN, + "GetThreadPriority did not obey access restrictions\n"); + if (pSetThreadPriorityBoost) + ok(pSetThreadPriorityBoost(access_thread,1)==0, + "SetThreadPriorityBoost did not obey access restrictions\n"); + if (pGetThreadPriorityBoost) + ok(pGetThreadPriorityBoost(access_thread,&disabled)==0, + "GetThreadPriorityBoost did not obey access restrictions\n"); + ok(GetExitCodeThread(access_thread,&exitCode)==0, + "GetExitCodeThread did not obey access restrictions\n"); + ok(CloseHandle(access_thread),"Error Closing thread handle\n"); + } +#if USE_EXTENDED_PRIORITIES + min_priority=-7; max_priority=6; +#endif + } + for(i=min_priority;i<=max_priority;i++) { + ok(SetThreadPriority(curthread,i)!=0, + "SetThreadPriority Failed for priority: %d\n",i); + ok(GetThreadPriority(curthread)==i, + "GetThreadPriority Failed for priority: %d\n",i); + } + ok(SetThreadPriority(curthread,THREAD_PRIORITY_TIME_CRITICAL)!=0, + "SetThreadPriority Failed\n"); + ok(GetThreadPriority(curthread)==THREAD_PRIORITY_TIME_CRITICAL, + "GetThreadPriority Failed\n"); + ok(SetThreadPriority(curthread,THREAD_PRIORITY_IDLE)!=0, + "SetThreadPriority Failed\n"); + ok(GetThreadPriority(curthread)==THREAD_PRIORITY_IDLE, + "GetThreadPriority Failed\n"); + ok(SetThreadPriority(curthread,0)!=0,"SetThreadPriority Failed\n"); + +/* Check thread priority boost */ + if (pGetThreadPriorityBoost && pSetThreadPriorityBoost) { + BOOL rc; + todo_wine { + SetLastError(0); + rc=pGetThreadPriorityBoost(curthread,&disabled); + if (rc!=0 || GetLastError()!=ERROR_CALL_NOT_IMPLEMENTED) { + ok(rc!=0,"error=%ld\n",GetLastError()); + + ok(pSetThreadPriorityBoost(curthread,1)!=0, + "error=%ld\n",GetLastError()); + rc=pGetThreadPriorityBoost(curthread,&disabled); + ok(rc!=0 && disabled==1, + "rc=%d error=%ld disabled=%d\n",rc,GetLastError(),disabled); + + ok(pSetThreadPriorityBoost(curthread,0)!=0, + "error=%ld\n",GetLastError()); + rc=pGetThreadPriorityBoost(curthread,&disabled); + ok(rc!=0 && disabled==0, + "rc=%d error=%ld disabled=%d\n",rc,GetLastError(),disabled); + } + } + } +} + +/* check the GetThreadTimes function */ +VOID test_GetThreadTimes() +{ + HANDLE thread,access_thread=NULL; + FILETIME creationTime,exitTime,kernelTime,userTime; + DWORD threadId; + int error; + + thread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)threadFunc2,NULL, + CREATE_SUSPENDED,&threadId); + + ok(thread!=NULL,"Create Thread failed\n"); +/* check that access control is obeyed */ + if (pOpenThread) { + access_thread=pOpenThread(THREAD_ALL_ACCESS & + (~THREAD_QUERY_INFORMATION), 0,threadId); + ok(access_thread!=NULL, + "OpenThread returned an invalid handle\n"); + } + ok(ResumeThread(thread)==1,"Resume thread returned an invalid value\n"); + ok(WaitForSingleObject(thread,5000)==WAIT_OBJECT_0, + "ResumeThread didn't work\n"); + if(access_thread!=NULL) { + error=GetThreadTimes(access_thread,&creationTime,&exitTime, + &kernelTime,&userTime); + ok(error==0, "GetThreadTimes did not obey access restrictions\n"); + ok(CloseHandle(access_thread)!=0,"CloseHandle Failed\n"); + } + creationTime.dwLowDateTime=99; creationTime.dwHighDateTime=99; + exitTime.dwLowDateTime=99; exitTime.dwHighDateTime=99; + kernelTime.dwLowDateTime=99; kernelTime.dwHighDateTime=99; + userTime.dwLowDateTime=99; userTime.dwHighDateTime=99; +/* GetThreadTimes should set all of the parameters passed to it */ + error=GetThreadTimes(thread,&creationTime,&exitTime, + &kernelTime,&userTime); + if (error!=0 || GetLastError()!=ERROR_CALL_NOT_IMPLEMENTED) { + ok(error!=0,"GetThreadTimes failed\n"); + ok(creationTime.dwLowDateTime!=99 || creationTime.dwHighDateTime!=99, + "creationTime was invalid\n"); + ok(exitTime.dwLowDateTime!=99 || exitTime.dwHighDateTime!=99, + "exitTime was invalid\n"); + ok(kernelTime.dwLowDateTime!=99 || kernelTime.dwHighDateTime!=99, + "kernelTimewas invalid\n"); + ok(userTime.dwLowDateTime!=99 || userTime.dwHighDateTime!=99, + "userTime was invalid\n"); + ok(CloseHandle(thread)!=0,"CloseHandle failed\n"); + } +} + +/* Check the processor affinity functions */ +/* NOTE: These functions should also be checked that they obey access control +*/ +VOID test_thread_processor() +{ + HANDLE curthread,curproc; + DWORD processMask,systemMask; + SYSTEM_INFO sysInfo; + int error=0; + + sysInfo.dwNumberOfProcessors=0; + GetSystemInfo(&sysInfo); + ok(sysInfo.dwNumberOfProcessors>0, + "GetSystemInfo failed to return a valid # of processors\n"); +/* Use the current Thread/process for all tests */ + curthread=GetCurrentThread(); + ok(curthread!=NULL,"GetCurrentThread failed\n"); + curproc=GetCurrentProcess(); + ok(curproc!=NULL,"GetCurrentProcess failed\n"); +/* Check the Affinity Mask functions */ + ok(GetProcessAffinityMask(curproc,&processMask,&systemMask)!=0, + "GetProcessAffinityMask failed\n"); + ok(SetThreadAffinityMask(curthread,processMask)==1, + "SetThreadAffinityMask failed\n"); + ok(SetThreadAffinityMask(curthread,processMask+1)==0, + "SetThreadAffinityMask passed for an illegal processor\n"); +/* NOTE: This only works on WinNT/2000/XP) */ + if (pSetThreadIdealProcessor) { + todo_wine { + SetLastError(0); + error=pSetThreadIdealProcessor(curthread,0); + if (GetLastError()!=ERROR_CALL_NOT_IMPLEMENTED) { + ok(error!=-1, "SetThreadIdealProcessor failed\n"); + } + } + if (GetLastError()!=ERROR_CALL_NOT_IMPLEMENTED) { + error=pSetThreadIdealProcessor(curthread,MAXIMUM_PROCESSORS+1); + ok(error==-1, + "SetThreadIdealProcessor succeeded with an illegal processor #\n"); + todo_wine { + error=pSetThreadIdealProcessor(curthread,MAXIMUM_PROCESSORS); + ok(error==0, "SetThreadIdealProcessor returned an incorrect value\n"); + } + } + } +} + +START_TEST(thread) +{ + HINSTANCE lib; +/* Neither Cygwin nor mingW export OpenThread, so do a dynamic check + so that the compile passes +*/ + lib=LoadLibraryA("kernel32"); + ok(lib!=NULL,"Couldn't load kernel32.dll\n"); + pGetThreadPriorityBoost=(GetThreadPriorityBoost_t)GetProcAddress(lib,"GetThreadPriorityBoost"); + pOpenThread=(OpenThread_t)GetProcAddress(lib,"OpenThread"); + pSetThreadIdealProcessor=(SetThreadIdealProcessor_t)GetProcAddress(lib,"SetThreadIdealProcessor"); + pSetThreadPriorityBoost=(SetThreadPriorityBoost_t)GetProcAddress(lib,"SetThreadPriorityBoost"); + test_CreateThread_basic(); + test_CreateThread_suspended(); + test_SuspendThread(); + test_TerminateThread(); + test_CreateThread_stack(); + test_thread_priority(); + test_GetThreadTimes(); + test_thread_processor(); +}