Various new tests for CreateProcess showing a couple of issues in current implementation.
From: Eric Pouech epouech@codeweavers.com
Move a couple of existing tests to this infrastructure.
Signed-off-by: Eric Pouech epouech@codeweavers.com --- dlls/kernel32/tests/process.c | 418 ++++++++++++++++++++++++---------- 1 file changed, 302 insertions(+), 116 deletions(-)
diff --git a/dlls/kernel32/tests/process.c b/dlls/kernel32/tests/process.c index 8bb2748f8e1..760bb497efa 100644 --- a/dlls/kernel32/tests/process.c +++ b/dlls/kernel32/tests/process.c @@ -317,6 +317,38 @@ static void WINAPIV __WINE_PRINTF_ATTR(2,3) childPrintf(HANDLE h, const char* fm WriteFile(h, buffer, strlen(buffer), &w, NULL); }
+/* bits 0..1 contains FILE_TYPE_{UNKNOWN, CHAR, PIPE, DISK} */ +#define HATTR_NULL 0x08 /* NULL handle value */ +#define HATTR_INVALID 0x04 /* INVALID_HANDLE_VALUE */ +#define HATTR_TYPE 0x0c /* valid handle, with type set */ +#define HATTR_INHERIT 0x10 /* inheritance flag set */ +#define HATTR_DANGLING 0x40 /* a pseudo value to show that the handle value has been copied but not inherited */ + +static unsigned encode_handle_attributes(HANDLE h) +{ + DWORD dw; + unsigned result; + + if (h == NULL) + result = HATTR_NULL; + else if (h == INVALID_HANDLE_VALUE) + result = HATTR_INVALID; + else + { + result = HATTR_TYPE; + dw = GetFileType(h); + if (dw == FILE_TYPE_CHAR || dw == FILE_TYPE_DISK || dw == FILE_TYPE_PIPE) + { + DWORD info; + if (GetHandleInformation(h, &info) && (info & HANDLE_FLAG_INHERIT)) + result |= HATTR_INHERIT; + } + else + dw = FILE_TYPE_UNKNOWN; + result |= dw; + } + return result; +}
/****************************************************************** * doChild @@ -347,17 +379,24 @@ static void doChild(const char* file, const char* option) "dwX=%lu\ndwY=%lu\ndwXSize=%lu\ndwYSize=%lu\n" "dwXCountChars=%lu\ndwYCountChars=%lu\ndwFillAttribute=%lu\n" "dwFlags=%lu\nwShowWindow=%u\n" - "hStdInput=%Iu\nhStdOutput=%Iu\nhStdError=%Iu\n\n", + "hStdInput=%Iu\nhStdOutput=%Iu\nhStdError=%Iu\n" + "hStdInputEncode=%u\nhStdOutputEncode=%u\nhStdErrorEncode=%u\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_PTR)siA.hStdInput, (DWORD_PTR)siA.hStdOutput, (DWORD_PTR)siA.hStdError); + (DWORD_PTR)siA.hStdInput, (DWORD_PTR)siA.hStdOutput, (DWORD_PTR)siA.hStdError, + encode_handle_attributes(siA.hStdInput), encode_handle_attributes(siA.hStdOutput), + encode_handle_attributes(siA.hStdError));
/* check the console handles in the TEB */ - childPrintf(hFile, "[TEB]\nhStdInput=%Iu\nhStdOutput=%Iu\nhStdError=%Iu\n\n", + childPrintf(hFile, + "[TEB]\nhStdInput=%Iu\nhStdOutput=%Iu\nhStdError=%Iu\n" + "hStdInputEncode=%u\nhStdOutputEncode=%u\nhStdErrorEncode=%u\n\n", (DWORD_PTR)params->hStdInput, (DWORD_PTR)params->hStdOutput, - (DWORD_PTR)params->hStdError); + (DWORD_PTR)params->hStdError, + encode_handle_attributes(params->hStdInput), encode_handle_attributes(params->hStdOutput), + encode_handle_attributes(params->hStdError));
/* since GetStartupInfoW is only implemented in win2k, * zero out before calling so we can notice the difference @@ -369,12 +408,15 @@ static void doChild(const char* file, const char* option) "dwX=%lu\ndwY=%lu\ndwXSize=%lu\ndwYSize=%lu\n" "dwXCountChars=%lu\ndwYCountChars=%lu\ndwFillAttribute=%lu\n" "dwFlags=%lu\nwShowWindow=%u\n" - "hStdInput=%Iu\nhStdOutput=%Iu\nhStdError=%Iu\n\n", + "hStdInput=%Iu\nhStdOutput=%Iu\nhStdError=%Iu\n" + "hStdInputEncode=%u\nhStdOutputEncode=%u\nhStdErrorEncode=%u\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_PTR)siW.hStdInput, (DWORD_PTR)siW.hStdOutput, (DWORD_PTR)siW.hStdError); + (DWORD_PTR)siW.hStdInput, (DWORD_PTR)siW.hStdOutput, (DWORD_PTR)siW.hStdError, + encode_handle_attributes(siW.hStdInput), encode_handle_attributes(siW.hStdOutput), + encode_handle_attributes(siW.hStdError));
/* Arguments */ childPrintf(hFile, "[Arguments]\nargcA=%d\n", myARGC); @@ -595,10 +637,17 @@ static void ok_child_int( int line, const char *sect, const char *key, UINT expe ok_(__FILE__, line)( result == expect, "%s:%s expected %u, but got %u\n", sect, key, expect, result ); }
+static inline void ok_child_hexint( int line, const char *sect, const char *key, UINT expect, UINT is_broken ) +{ + UINT result = GetPrivateProfileIntA( sect, key, !expect, resfile ); + ok_(__FILE__, line)( result == expect || broken( is_broken && result == is_broken ), "%s:%s expected %#x, but got %#x\n", sect, key, expect, result ); +} + #define okChildString(sect, key, expect) ok_child_string(__LINE__, (sect), (key), (expect), 1 ) #define okChildIString(sect, key, expect) ok_child_string(__LINE__, (sect), (key), (expect), 0 ) #define okChildStringWA(sect, key, expect) ok_child_stringWA(__LINE__, (sect), (key), (expect), 1 ) #define okChildInt(sect, key, expect) ok_child_int(__LINE__, (sect), (key), (expect)) +#define okChildHexInt(sect, key, expect, is_broken) ok_child_hexint(__LINE__, (sect), (key), (expect), (is_broken))
static void test_Startup(void) { @@ -3057,72 +3106,264 @@ static void test_BreakawayOk(HANDLE parent_job) ok(ret, "SetInformationJobObject error %lu\n", GetLastError()); }
-static void test_StartupNoConsole(void) +/* copy an executable, but changing its subsystem */ +static inline void copy_change_subsystem(const char* in, const char* out, DWORD subsyst) +{ + BOOL ret; + HANDLE hFile, hMap; + void* mapping; + IMAGE_NT_HEADERS *nthdr; + + ret = CopyFileA(in, out, FALSE); + ok(ret, "Failed to copy executable %s in %s (%lu)\n", in, out, GetLastError()); + + hFile = CreateFileA(out, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + ok(hFile != INVALID_HANDLE_VALUE, "Couldn't open file %s (%lu)\n", out, GetLastError()); + hMap = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, 0, NULL); + ok(hMap != NULL, "Couldn't create map (%lu)\n", GetLastError()); + mapping = MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0); + ok(mapping != NULL, "Couldn't map (%lu)\n", GetLastError()); + nthdr = RtlImageNtHeader(mapping); + ok(nthdr != NULL, "Cannot get NT headers out of %s\n", out); + if (nthdr) nthdr->OptionalHeader.Subsystem = subsyst; + ret = UnmapViewOfFile(mapping); + ok(ret, "Couldn't unmap (%lu)\n", GetLastError()); + CloseHandle(hMap); + CloseHandle(hFile); +} + +#define H_CONSOLE 0 +#define H_DISK 1 + +#define ARG_STD 0x80000000 +#define ARG_STARTUPINFO 0x00000000 +#define ARG_CP_INHERIT 0x40000000 +#define ARG_HANDLE_INHERIT 0x20000000 +#define ARG_HANDLE_MASK (~0xff000000) + +static inline BOOL check_run_child(const char *exec, DWORD flags, BOOL cp_inherit, + STARTUPINFOA *si) { -#ifndef _WIN64 - char buffer[2 * MAX_PATH + 25]; - STARTUPINFOA startup; PROCESS_INFORMATION info; + char buffer[2 * MAX_PATH + 64]; + DWORD exit_code; + BOOL res; + DWORD ret;
- memset(&startup, 0, sizeof(startup)); - startup.cb = sizeof(startup); - startup.dwFlags = STARTF_USESHOWWINDOW; - startup.wShowWindow = SW_SHOWNORMAL; get_file_name(resfile); - sprintf(buffer, ""%s" process dump "%s"", selfname, resfile); - ok(CreateProcessA(NULL, buffer, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &startup, - &info), "CreateProcess\n"); - wait_and_close_child_process(&info); + sprintf(buffer, ""%s" process dump "%s"", exec, resfile);
- reload_child_info(resfile); - okChildInt("StartupInfoA", "hStdInput", (UINT)INVALID_HANDLE_VALUE); - okChildInt("StartupInfoA", "hStdOutput", (UINT)INVALID_HANDLE_VALUE); - okChildInt("StartupInfoA", "hStdError", (UINT)INVALID_HANDLE_VALUE); - okChildInt("TEB", "hStdInput", 0); - okChildInt("TEB", "hStdOutput", 0); - okChildInt("TEB", "hStdError", 0); - release_memory(); - DeleteFileA(resfile); -#endif + res = CreateProcessA(NULL, buffer, NULL, NULL, cp_inherit, flags, NULL, NULL, si, &info); + ok(res, "CreateProcess failed: %lu %s\n", GetLastError(), buffer); + CloseHandle(info.hThread); + ret = WaitForSingleObject(info.hProcess, 30000); + ok(ret == WAIT_OBJECT_0, "Could not wait for the child process: %ld le=%lu\n", + ret, GetLastError()); + res = GetExitCodeProcess(info.hProcess, &exit_code); + ok(res && exit_code == 0, "Couldn't get exit_code\n"); + CloseHandle(info.hProcess); + return res; }
-static void test_DetachConsoleHandles(void) +static char std_handle_file[MAX_PATH]; + +static inline BOOL build_startupinfo( STARTUPINFOA *startup, unsigned args, HANDLE hstd[2] ) +{ + SECURITY_ATTRIBUTES sa = {0, NULL, TRUE}; + SECURITY_ATTRIBUTES* psa; + + BOOL needs_close = FALSE; + + memset(startup, 0, sizeof(*startup)); + startup->cb = sizeof(*startup); + + psa = (args & ARG_HANDLE_INHERIT) ? &sa : NULL; + + switch (args & ARG_HANDLE_MASK) + { + case H_CONSOLE: + hstd[0] = CreateFileA("CONIN$", GENERIC_READ, 0, psa, OPEN_EXISTING, 0, 0); + ok(hstd[0] != INVALID_HANDLE_VALUE, "Couldn't create input to console\n"); + hstd[1] = CreateFileA("CONOUT$", GENERIC_WRITE|GENERIC_WRITE, 0, psa, OPEN_EXISTING, 0, 0); + ok(hstd[1] != INVALID_HANDLE_VALUE, "Couldn't create input to console\n"); + needs_close = TRUE; + break; + case H_DISK: + hstd[0] = CreateFileA(std_handle_file, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, psa, OPEN_EXISTING, 0, 0); + ok(hstd[0] != INVALID_HANDLE_VALUE, "Couldn't create input to file %s\n", std_handle_file); + hstd[1] = CreateFileA(std_handle_file, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, psa, OPEN_EXISTING, 0, 0); + ok(hstd[1] != INVALID_HANDLE_VALUE, "Couldn't create input to file %s\n", std_handle_file); + needs_close = TRUE; + break; + default: + ok(0, "Unsupported handle type %x\n", args & ARG_HANDLE_MASK); + return FALSE; + } + if (args & ARG_STD) + { + SetStdHandle(STD_INPUT_HANDLE, hstd[0]); + SetStdHandle(STD_OUTPUT_HANDLE, hstd[1]); + } + else /* through startup info */ + { + startup->dwFlags |= STARTF_USESTDHANDLES; + startup->hStdInput = hstd[0]; + startup->hStdOutput = hstd[1]; + } + return needs_close; +} + +struct std_handle_test +{ + /* input */ + unsigned args; + /* output */ + DWORD expected; + unsigned is_todo; /* bitmask: 1 on TEB values, 2 on StartupInfoA values, 4 on StartupInfoW values */ + DWORD is_broken; /* Win7 broken file types */ +}; + +static void test_StdHandleInheritance(void) { #ifndef _WIN64 - char buffer[2 * MAX_PATH + 25]; - STARTUPINFOA startup; - PROCESS_INFORMATION info; - UINT result; + HANDLE hsavestd[3]; + static char guiexec[MAX_PATH]; + static char cuiexec[MAX_PATH]; + char **argv; + BOOL ret; + int i, j;
- memset(&startup, 0, sizeof(startup)); - startup.cb = sizeof(startup); - startup.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES; - startup.wShowWindow = SW_SHOWNORMAL; - startup.hStdInput = GetStdHandle(STD_INPUT_HANDLE); - startup.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); - startup.hStdError = GetStdHandle(STD_ERROR_HANDLE); - get_file_name(resfile); - sprintf(buffer, ""%s" process dump "%s"", selfname, resfile); - ok(CreateProcessA(NULL, buffer, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &startup, - &info), "CreateProcess\n"); - wait_and_close_child_process(&info); + static const struct std_handle_test + detached_cui[] = + { + {ARG_STD | ARG_CP_INHERIT | ARG_HANDLE_INHERIT | H_CONSOLE, HATTR_NULL, .is_todo = 4}, + {ARG_STARTUPINFO | ARG_CP_INHERIT | ARG_HANDLE_INHERIT | H_CONSOLE, HATTR_TYPE | HATTR_INHERIT | FILE_TYPE_CHAR, .is_broken = HATTR_TYPE | FILE_TYPE_UNKNOWN}, + /* all others handles type behave as H_DISK */ + {ARG_STD | ARG_CP_INHERIT | ARG_HANDLE_INHERIT | H_DISK, HATTR_NULL, .is_todo = 4}, + {ARG_STARTUPINFO | ARG_CP_INHERIT | ARG_HANDLE_INHERIT | H_DISK, HATTR_TYPE | HATTR_INHERIT | FILE_TYPE_DISK}, + }, + detached_gui[] = + { + {ARG_STD | ARG_CP_INHERIT | ARG_HANDLE_INHERIT | H_CONSOLE, HATTR_NULL, .is_todo = 4}, + {ARG_STARTUPINFO | ARG_CP_INHERIT | ARG_HANDLE_INHERIT | H_CONSOLE, HATTR_NULL, .is_todo = 7, .is_broken = HATTR_TYPE | FILE_TYPE_UNKNOWN}, + /* all others handles type behave as H_DISK */ + {ARG_STD | ARG_CP_INHERIT | ARG_HANDLE_INHERIT | H_DISK, HATTR_NULL, .is_todo = 4}, + {ARG_STARTUPINFO | ARG_CP_INHERIT | ARG_HANDLE_INHERIT | H_DISK, HATTR_TYPE | HATTR_INHERIT | FILE_TYPE_DISK}, + }; + static const struct + { + DWORD cp_flags; + BOOL use_cui; + const struct std_handle_test* tests; + size_t count; + const char* descr; + } + tests[] = + { +#define X(d, cg, s) {(d), (cg), s, ARRAY_SIZE(s), #s} + X(DETACHED_PROCESS, TRUE, detached_cui), + X(DETACHED_PROCESS, FALSE, detached_gui), +#undef X + };
- reload_child_info(resfile); - result = GetPrivateProfileIntA("StartupInfoA", "hStdInput", 0, resfile); - ok(result != 0 && result != (UINT)INVALID_HANDLE_VALUE, "bad handle %x\n", result); - result = GetPrivateProfileIntA("StartupInfoA", "hStdOutput", 0, resfile); - ok(result != 0 && result != (UINT)INVALID_HANDLE_VALUE, "bad handle %x\n", result); - result = GetPrivateProfileIntA("StartupInfoA", "hStdError", 0, resfile); - ok(result != 0 && result != (UINT)INVALID_HANDLE_VALUE, "bad handle %x\n", result); - result = GetPrivateProfileIntA("TEB", "hStdInput", 0, resfile); - ok(result != 0 && result != (UINT)INVALID_HANDLE_VALUE, "bad handle %x\n", result); - result = GetPrivateProfileIntA("TEB", "hStdOutput", 0, resfile); - ok(result != 0 && result != (UINT)INVALID_HANDLE_VALUE, "bad handle %x\n", result); - result = GetPrivateProfileIntA("TEB", "hStdError", 0, resfile); - ok(result != 0 && result != (UINT)INVALID_HANDLE_VALUE, "bad handle %x\n", result); + hsavestd[0] = GetStdHandle(STD_INPUT_HANDLE); + hsavestd[1] = GetStdHandle(STD_OUTPUT_HANDLE); + hsavestd[2] = GetStdHandle(STD_ERROR_HANDLE);
- release_memory(); - DeleteFileA(resfile); + winetest_get_mainargs(&argv); + + GetTempPathA(ARRAY_SIZE(guiexec), guiexec); + strcat(guiexec, "process_gui.exe"); + copy_change_subsystem(argv[0], guiexec, IMAGE_SUBSYSTEM_WINDOWS_GUI); + GetTempPathA(ARRAY_SIZE(cuiexec), cuiexec); + strcat(cuiexec, "process_cui.exe"); + copy_change_subsystem(argv[0], cuiexec, IMAGE_SUBSYSTEM_WINDOWS_CUI); + get_file_name(std_handle_file); + + for (j = 0; j < ARRAY_SIZE(tests); j++) + { + const struct std_handle_test* std_tests = tests[j].tests; + + for (i = 0; i < tests[j].count; i++) + { + STARTUPINFOA startup; + HANDLE hstd[2] = {}; + BOOL needs_close; + + winetest_push_context("%s[%u] ", tests[j].descr, i); + needs_close = build_startupinfo( &startup, std_tests[i].args, hstd ); + + ret = check_run_child(tests[j].use_cui ? cuiexec : guiexec, + tests[j].cp_flags, !!(std_tests[i].args & ARG_CP_INHERIT), + &startup); + ok(ret, "Couldn't run child\n"); + reload_child_info(resfile); + + if (std_tests[i].expected & HATTR_DANGLING) + { + /* The value of the handle has been copied in fields, but the object hasn't been inherited. + * There's no reliable way to test that the object hasn't been inherited, as the + * entry in the handle table is free and can have been reused before this test occurs. + * So simply test that the value is passed untouched. + */ + todo_wine_if((std_tests[i].args & ARG_STD) != 0) + { + okChildHexInt("StartupInfoA", "hStdInput", (DWORD_PTR)((std_tests[i].args & ARG_STD) ? INVALID_HANDLE_VALUE : hstd[0]), std_tests[i].is_broken); + okChildHexInt("StartupInfoA", "hStdOutput", (DWORD_PTR)((std_tests[i].args & ARG_STD) ? INVALID_HANDLE_VALUE : hstd[1]), std_tests[i].is_broken); + okChildHexInt("StartupInfoW", "hStdInput", (DWORD_PTR)((std_tests[i].args & ARG_STD) ? NULL : hstd[0]), std_tests[i].is_broken); + okChildHexInt("StartupInfoW", "hStdOutput", (DWORD_PTR)((std_tests[i].args & ARG_STD) ? NULL : hstd[1]), std_tests[i].is_broken); + } + + todo_wine + { + okChildHexInt("TEB", "hStdInput", (DWORD_PTR)hstd[0], std_tests[i].is_broken); + okChildHexInt("TEB", "hStdOutput", (DWORD_PTR)hstd[1], std_tests[i].is_broken); + } + } + else + { + unsigned startup_expected = (std_tests[i].args & ARG_STD) ? HATTR_INVALID : std_tests[i].expected; + + todo_wine_if(std_tests[i].is_todo & 2) + { + okChildHexInt("StartupInfoA", "hStdInputEncode", startup_expected, std_tests[i].is_broken); + okChildHexInt("StartupInfoA", "hStdOutputEncode", startup_expected, std_tests[i].is_broken); + } + + startup_expected = (std_tests[i].args & ARG_STD) ? HATTR_NULL : std_tests[i].expected; + + todo_wine_if(std_tests[i].is_todo & 4) + { + okChildHexInt("StartupInfoW", "hStdInputEncode", startup_expected, std_tests[i].is_broken); + okChildHexInt("StartupInfoW", "hStdOutputEncode", startup_expected, std_tests[i].is_broken); + } + + todo_wine_if(std_tests[i].is_todo & 1) + { + okChildHexInt("TEB", "hStdInputEncode", std_tests[i].expected, std_tests[i].is_broken); + okChildHexInt("TEB", "hStdOutputEncode", std_tests[i].expected, std_tests[i].is_broken); + } + } + + release_memory(); + DeleteFileA(resfile); + if (needs_close) + { + CloseHandle(hstd[0]); + CloseHandle(hstd[1]); + } + winetest_pop_context(); + } + } + + DeleteFileA(guiexec); + DeleteFileA(cuiexec); + DeleteFileA(std_handle_file); + + SetStdHandle(STD_INPUT_HANDLE, hsavestd[0]); + SetStdHandle(STD_OUTPUT_HANDLE, hsavestd[1]); + SetStdHandle(STD_ERROR_HANDLE, hsavestd[2]); #endif }
@@ -3568,59 +3809,6 @@ static void test_SuspendProcessState(void) } #endif
-static void test_DetachStdHandles(void) -{ -#ifndef _WIN64 - char buffer[2 * MAX_PATH + 25], tempfile[MAX_PATH]; - STARTUPINFOA startup; - PROCESS_INFORMATION info; - HANDLE hstdin, hstdout, hstderr, htemp; - BOOL res; - - hstdin = GetStdHandle(STD_INPUT_HANDLE); - hstdout = GetStdHandle(STD_OUTPUT_HANDLE); - hstderr = GetStdHandle(STD_ERROR_HANDLE); - - get_file_name(tempfile); - htemp = CreateFileA(tempfile, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0); - ok(htemp != INVALID_HANDLE_VALUE, "failed opening temporary file\n"); - - memset(&startup, 0, sizeof(startup)); - startup.cb = sizeof(startup); - startup.dwFlags = STARTF_USESHOWWINDOW; - startup.wShowWindow = SW_SHOWNORMAL; - get_file_name(resfile); - sprintf(buffer, ""%s" process dump "%s"", selfname, resfile); - - SetStdHandle(STD_INPUT_HANDLE, htemp); - SetStdHandle(STD_OUTPUT_HANDLE, htemp); - SetStdHandle(STD_ERROR_HANDLE, htemp); - - res = CreateProcessA(NULL, buffer, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &startup, - &info); - - SetStdHandle(STD_INPUT_HANDLE, hstdin); - SetStdHandle(STD_OUTPUT_HANDLE, hstdout); - SetStdHandle(STD_ERROR_HANDLE, hstderr); - - ok(res, "CreateProcess failed\n"); - wait_and_close_child_process(&info); - - reload_child_info(resfile); - okChildInt("StartupInfoA", "hStdInput", (UINT)INVALID_HANDLE_VALUE); - okChildInt("StartupInfoA", "hStdOutput", (UINT)INVALID_HANDLE_VALUE); - okChildInt("StartupInfoA", "hStdError", (UINT)INVALID_HANDLE_VALUE); - okChildInt("TEB", "hStdInput", 0); - okChildInt("TEB", "hStdOutput", 0); - okChildInt("TEB", "hStdError", 0); - release_memory(); - DeleteFileA(resfile); - - CloseHandle(htemp); - DeleteFileA(tempfile); -#endif -} - static void test_GetNumaProcessorNode(void) { SYSTEM_INFO si; @@ -5105,9 +5293,7 @@ START_TEST(process) test_ProcessorCount(); test_RegistryQuota(); test_DuplicateHandle(); - test_StartupNoConsole(); - test_DetachConsoleHandles(); - test_DetachStdHandles(); + test_StdHandleInheritance(); test_GetNumaProcessorNode(); test_session_info(); test_GetLogicalProcessorInformationEx();
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- dlls/kernel32/tests/process.c | 90 ++++++++++++++++++++++++++++++++++- 1 file changed, 88 insertions(+), 2 deletions(-)
diff --git a/dlls/kernel32/tests/process.c b/dlls/kernel32/tests/process.c index 760bb497efa..aa190148546 100644 --- a/dlls/kernel32/tests/process.c +++ b/dlls/kernel32/tests/process.c @@ -3133,8 +3133,13 @@ static inline void copy_change_subsystem(const char* in, const char* out, DWORD CloseHandle(hFile); }
-#define H_CONSOLE 0 -#define H_DISK 1 +#define H_NULL 0 +#define H_INVALID 1 +#define H_DEVIL 2 +#define H_CONSOLE 3 +#define H_CHAR 4 +#define H_DISK 5 +#define H_PIPE 6
#define ARG_STD 0x80000000 #define ARG_STARTUPINFO 0x00000000 @@ -3174,6 +3179,7 @@ static inline BOOL build_startupinfo( STARTUPINFOA *startup, unsigned args, HAND SECURITY_ATTRIBUTES* psa;
BOOL needs_close = FALSE; + BOOL ret;
memset(startup, 0, sizeof(*startup)); startup->cb = sizeof(*startup); @@ -3182,6 +3188,16 @@ static inline BOOL build_startupinfo( STARTUPINFOA *startup, unsigned args, HAND
switch (args & ARG_HANDLE_MASK) { + case H_NULL: + hstd[0] = hstd[1] = NULL; + break; + case H_INVALID: + hstd[0] = hstd[1] = INVALID_HANDLE_VALUE; + break; + case H_DEVIL: + hstd[0] = (HANDLE)(ULONG_PTR)0x066600; + hstd[1] = (HANDLE)(ULONG_PTR)0x066610; + break; case H_CONSOLE: hstd[0] = CreateFileA("CONIN$", GENERIC_READ, 0, psa, OPEN_EXISTING, 0, 0); ok(hstd[0] != INVALID_HANDLE_VALUE, "Couldn't create input to console\n"); @@ -3189,6 +3205,13 @@ static inline BOOL build_startupinfo( STARTUPINFOA *startup, unsigned args, HAND ok(hstd[1] != INVALID_HANDLE_VALUE, "Couldn't create input to console\n"); needs_close = TRUE; break; + case H_CHAR: + hstd[0] = CreateFileA("NUL", GENERIC_READ, 0, psa, OPEN_EXISTING, 0, 0); + ok(hstd[0] != INVALID_HANDLE_VALUE, "Couldn't create input to NUL\n"); + hstd[1] = CreateFileA("NUL", GENERIC_READ|GENERIC_WRITE, 0, psa, OPEN_EXISTING, 0, 0); + ok(hstd[1] != INVALID_HANDLE_VALUE, "Couldn't create input to NUL\n"); + needs_close = TRUE; + break; case H_DISK: hstd[0] = CreateFileA(std_handle_file, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, psa, OPEN_EXISTING, 0, 0); ok(hstd[0] != INVALID_HANDLE_VALUE, "Couldn't create input to file %s\n", std_handle_file); @@ -3196,6 +3219,11 @@ static inline BOOL build_startupinfo( STARTUPINFOA *startup, unsigned args, HAND ok(hstd[1] != INVALID_HANDLE_VALUE, "Couldn't create input to file %s\n", std_handle_file); needs_close = TRUE; break; + case H_PIPE: + ret = CreatePipe(&hstd[0], &hstd[1], psa, 0); + ok(ret, "Couldn't create anon pipe\n"); + needs_close = TRUE; + break; default: ok(0, "Unsupported handle type %x\n", args & ARG_HANDLE_MASK); return FALSE; @@ -3235,6 +3263,62 @@ static void test_StdHandleInheritance(void) int i, j;
static const struct std_handle_test + nothing_cui[] = + { + /* all others handles type behave as H_DISK */ +/* 0*/ {ARG_STARTUPINFO | ARG_CP_INHERIT | ARG_HANDLE_INHERIT | H_DISK, HATTR_TYPE | HATTR_INHERIT | FILE_TYPE_DISK}, + {ARG_STD | ARG_CP_INHERIT | ARG_HANDLE_INHERIT | H_DISK, HATTR_TYPE | HATTR_INHERIT | FILE_TYPE_DISK, .is_todo = 6}, + + /* all others handles type behave as H_DISK */ + {ARG_STARTUPINFO | ARG_HANDLE_INHERIT | H_DISK, HATTR_NULL, .is_todo = 7, .is_broken = HATTR_TYPE | FILE_TYPE_UNKNOWN}, + {ARG_STD | ARG_HANDLE_INHERIT | H_DISK, HATTR_TYPE | HATTR_INHERIT | FILE_TYPE_DISK, .is_todo = 6}, + + /* all others handles type behave as H_DISK */ + {ARG_STARTUPINFO | ARG_CP_INHERIT | H_DISK, HATTR_DANGLING, .is_broken = HATTR_TYPE | FILE_TYPE_UNKNOWN}, +/* 5*/ {ARG_STD | ARG_CP_INHERIT | H_DISK, HATTR_DANGLING}, + + /* all others handles type behave as H_DISK */ + {ARG_STARTUPINFO | H_DISK, HATTR_NULL, .is_todo = 7, .is_broken = HATTR_TYPE | FILE_TYPE_UNKNOWN}, + {ARG_STD | H_DISK, HATTR_TYPE | FILE_TYPE_DISK, .is_todo = 7}, + + {ARG_STARTUPINFO | H_DEVIL, HATTR_NULL, .is_todo = 6, .is_broken = HATTR_TYPE | FILE_TYPE_UNKNOWN}, + {ARG_STD | H_DEVIL, HATTR_NULL, .is_todo = 4}, +/*10*/ {ARG_STARTUPINFO | H_INVALID, HATTR_NULL, .is_todo = 6, .is_broken = HATTR_INVALID}, + {ARG_STD | H_INVALID, HATTR_NULL, .is_todo = 6, .is_broken = HATTR_TYPE | FILE_TYPE_UNKNOWN}, + {ARG_STARTUPINFO | H_NULL, HATTR_NULL, .is_todo = 6, .is_broken = HATTR_INVALID}, + {ARG_STD | H_NULL, HATTR_NULL, .is_todo = 4, .is_broken = HATTR_INVALID}, + }, + nothing_gui[] = + { + /* testing all types because of discrepancies */ +/* 0*/ {ARG_STARTUPINFO | ARG_CP_INHERIT | ARG_HANDLE_INHERIT | H_DISK, HATTR_TYPE | HATTR_INHERIT | FILE_TYPE_DISK}, + {ARG_STD | ARG_CP_INHERIT | ARG_HANDLE_INHERIT | H_DISK, HATTR_TYPE | HATTR_INHERIT | FILE_TYPE_DISK, .is_todo = 6}, + {ARG_STARTUPINFO | ARG_CP_INHERIT | ARG_HANDLE_INHERIT | H_PIPE, HATTR_TYPE | HATTR_INHERIT | FILE_TYPE_PIPE}, + {ARG_STD | ARG_CP_INHERIT | ARG_HANDLE_INHERIT | H_PIPE, HATTR_TYPE | HATTR_INHERIT | FILE_TYPE_PIPE, .is_todo = 6}, + {ARG_STARTUPINFO | ARG_CP_INHERIT | ARG_HANDLE_INHERIT | H_CHAR, HATTR_TYPE | HATTR_INHERIT | FILE_TYPE_CHAR}, +/* 5*/ {ARG_STD | ARG_CP_INHERIT | ARG_HANDLE_INHERIT | H_CHAR, HATTR_TYPE | HATTR_INHERIT | FILE_TYPE_CHAR, .is_todo = 6}, + {ARG_STARTUPINFO | ARG_CP_INHERIT | ARG_HANDLE_INHERIT | H_CONSOLE, HATTR_NULL, .is_todo = 7, .is_broken = HATTR_TYPE | FILE_TYPE_UNKNOWN}, + {ARG_STD | ARG_CP_INHERIT | ARG_HANDLE_INHERIT | H_CONSOLE, HATTR_NULL, .is_todo = 7, .is_broken = HATTR_TYPE | FILE_TYPE_UNKNOWN}, + + /* all others handles type behave as H_DISK */ + {ARG_STARTUPINFO | ARG_HANDLE_INHERIT | H_DISK, HATTR_NULL, .is_todo = 7, .is_broken = HATTR_TYPE | FILE_TYPE_UNKNOWN}, + {ARG_STD | ARG_HANDLE_INHERIT | H_DISK, HATTR_NULL, .is_todo = 7}, + + /* all others handles type behave as H_DISK */ +/*10*/ {ARG_STARTUPINFO | ARG_CP_INHERIT | H_DISK, HATTR_DANGLING}, + {ARG_STD | ARG_CP_INHERIT | H_DISK, HATTR_DANGLING}, + + /* all others handles type behave as H_DISK */ + {ARG_STARTUPINFO | H_DISK, HATTR_NULL, .is_todo = 7, .is_broken = HATTR_TYPE | FILE_TYPE_UNKNOWN}, + {ARG_STD | H_DISK, HATTR_NULL, .is_todo = 7}, + + {ARG_STARTUPINFO | H_DEVIL, HATTR_NULL, .is_todo = 6, .is_broken = HATTR_TYPE | FILE_TYPE_UNKNOWN}, +/*15*/ {ARG_STD | H_DEVIL, HATTR_NULL, .is_todo = 4}, + {ARG_STARTUPINFO | H_INVALID, HATTR_NULL, .is_todo = 6, .is_broken = HATTR_INVALID}, + {ARG_STD | H_INVALID, HATTR_NULL, .is_todo = 6}, + {ARG_STARTUPINFO | H_NULL, HATTR_NULL, .is_todo = 6}, + {ARG_STD | H_NULL, HATTR_NULL, .is_todo = 4}, + }, detached_cui[] = { {ARG_STD | ARG_CP_INHERIT | ARG_HANDLE_INHERIT | H_CONSOLE, HATTR_NULL, .is_todo = 4}, @@ -3262,6 +3346,8 @@ static void test_StdHandleInheritance(void) tests[] = { #define X(d, cg, s) {(d), (cg), s, ARRAY_SIZE(s), #s} + X(0, TRUE, nothing_cui), + X(0, FALSE, nothing_gui), X(DETACHED_PROCESS, TRUE, detached_cui), X(DETACHED_PROCESS, FALSE, detached_gui), #undef X
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- dlls/msvcrt/tests/file.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/dlls/msvcrt/tests/file.c b/dlls/msvcrt/tests/file.c index 75d30648230..9357b215cd8 100644 --- a/dlls/msvcrt/tests/file.c +++ b/dlls/msvcrt/tests/file.c @@ -1896,6 +1896,9 @@ static void test_invalid_stdin_child( void ) handle = (HANDLE)_get_osfhandle(STDIN_FILENO); ok(handle == (HANDLE)-2, "handle = %p\n", handle); ok(errno == 0xdeadbeef, "errno = %d\n", errno); + handle = GetStdHandle(STD_INPUT_HANDLE); + todo_wine + ok((LONG_PTR)handle > 0, "Expecting passed handle to be untouched\n");
info = &__pioinfo[STDIN_FILENO/MSVCRT_FD_BLOCK_SIZE][STDIN_FILENO%MSVCRT_FD_BLOCK_SIZE]; ok(info->handle == (HANDLE)-2, "info->handle = %p\n", info->handle);
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=140058
Your paranoid android.
=== debian11 (32 bit report) ===
Report validation errors: kernel32:process prints too much data (36469 bytes)
=== debian11 (32 bit ar:MA report) ===
Report validation errors: kernel32:process prints too much data (36469 bytes)
=== debian11 (32 bit de report) ===
Report validation errors: kernel32:process prints too much data (36469 bytes)
=== debian11 (32 bit fr report) ===
Report validation errors: kernel32:process prints too much data (36469 bytes)
=== debian11 (32 bit he:IL report) ===
Report validation errors: kernel32:process prints too much data (36469 bytes)
=== debian11 (32 bit hi:IN report) ===
Report validation errors: kernel32:process prints too much data (36469 bytes)
=== debian11 (32 bit ja:JP report) ===
Report validation errors: kernel32:process prints too much data (36469 bytes)
=== debian11 (32 bit zh:CN report) ===
Report validation errors: kernel32:process prints too much data (36469 bytes)
=== debian11b (32 bit WoW report) ===
Report validation errors: kernel32:process prints too much data (36556 bytes)
I would really like to see the kernel part fixed first. It's hard to say at this point what is caused by problems in C-runtime implementation (and it looks like test failures are mostly caused by kernel part). Also lots of these test looks like they belong to kernel - e.g. if some handles are not passed to the created process there's no way msvcrt will use them.
I don't think we want to test every possible configuration in msvcrt.
I don't get it. Most of the tests of this MR are in kernel32 not in msvcrt.
On Tue Nov 21 17:15:06 2023 +0000, eric pouech wrote:
I don't get it. Most of the tests of this MR are in kernel32 not in msvcrt.
Sorry, I've misread the patches (I thought that all of this tests are added to msvcrt). The msvcrt part looks good for me.
On Tue Nov 21 17:21:40 2023 +0000, Piotr Caban wrote:
Sorry, I've misread the patches (I thought that all of this tests are added to msvcrt). The msvcrt part looks good for me.
no worries. I think you're supposed to approve (at least for the msvcrt part).
This merge request was approved by Piotr Caban.
@julliard what's the status of this one? TIA
The test is failing (too much output).
grr... I was testing locally with 'winetest -o -' which doesn't warn when size is too big
will rework the series