So that we can have full featured wine test with thread local data and context.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com ---
I don't know what was going on with [1] and following patches but Marvin reported a succeeded todo_wine on a test that isn't even marked todo.
I suspect there's some sort of thread conflict happening and this should fix such issues, see [2] without vs [3] with this patch.
We also miss winetest_push_context / winetest_pop_context and all the possible improvements done to winetest, so I think it's good to have this factored together.
I've left winetest_elapsed aside, as it requires KeQueryTickCount, which isn't exported on Windows. I don't think it's very important to have it anyway, as we don't even pass winetest_time yet to the driver.
Sorry in advance for the amount of time this will take if Marvin decides to run everything...
[1] https://testbot.winehq.org/JobDetails.pl?Key=93428 [2] https://testbot.winehq.org/JobDetails.pl?Key=93447 [3] https://testbot.winehq.org/JobDetails.pl?Key=93452
dlls/ntoskrnl.exe/tests/Makefile.in | 16 +- dlls/ntoskrnl.exe/tests/driver.c | 36 ++-- dlls/ntoskrnl.exe/tests/driver.h | 8 - dlls/ntoskrnl.exe/tests/driver_hid.c | 2 +- dlls/ntoskrnl.exe/tests/driver_netio.c | 6 +- dlls/ntoskrnl.exe/tests/driver_pnp.c | 2 +- dlls/ntoskrnl.exe/tests/ntoskrnl.c | 49 +---- dlls/ntoskrnl.exe/tests/utils.h | 257 ------------------------ include/wine/test.h | 260 +++++++++++++++++++++++-- 9 files changed, 287 insertions(+), 349 deletions(-) delete mode 100644 dlls/ntoskrnl.exe/tests/utils.h
diff --git a/dlls/ntoskrnl.exe/tests/Makefile.in b/dlls/ntoskrnl.exe/tests/Makefile.in index 863fad30f63..037f0fda637 100644 --- a/dlls/ntoskrnl.exe/tests/Makefile.in +++ b/dlls/ntoskrnl.exe/tests/Makefile.in @@ -1,17 +1,23 @@ TESTDLL = ntoskrnl.exe IMPORTS = advapi32 crypt32 newdev setupapi user32 wintrust ws2_32 hid
-driver_IMPORTS = winecrt0 ntoskrnl +driver_IMPORTS = winecrt0 ntoskrnl hal +driver_EXTRADEFS = -DSTANDALONE -DWINETEST_KERNEL driver_EXTRADLLFLAGS = -nodefaultlibs -nostartfiles -Wl,--subsystem,native -driver2_IMPORTS = winecrt0 ntoskrnl +driver2_IMPORTS = winecrt0 ntoskrnl hal +driver2_EXTRADEFS = -DSTANDALONE -DWINETEST_KERNEL driver2_EXTRADLLFLAGS = -nodefaultlibs -nostartfiles -Wl,--subsystem,native -driver3_IMPORTS = winecrt0 ntoskrnl +driver3_IMPORTS = winecrt0 ntoskrnl hal +driver3_EXTRADEFS = -DSTANDALONE -DWINETEST_KERNEL driver3_EXTRADLLFLAGS = -nodefaultlibs -nostartfiles -Wl,--subsystem,native -driver_hid_IMPORTS = winecrt0 ntoskrnl hidclass +driver_hid_IMPORTS = winecrt0 ntoskrnl hal hidclass +driver_hid_EXTRADEFS = -DSTANDALONE -DWINETEST_KERNEL driver_hid_EXTRADLLFLAGS = -nodefaultlibs -nostartfiles -Wl,--subsystem,native -driver_netio_IMPORTS = winecrt0 ntoskrnl netio +driver_netio_IMPORTS = winecrt0 ntoskrnl hal netio +driver_netio_EXTRADEFS = -DSTANDALONE -DWINETEST_KERNEL driver_netio_EXTRADLLFLAGS = -nodefaultlibs -nostartfiles -Wl,--subsystem,native driver_pnp_IMPORTS = winecrt0 ntoskrnl hal +driver_pnp_EXTRADEFS = -DSTANDALONE -DWINETEST_KERNEL driver_pnp_EXTRADLLFLAGS = -nodefaultlibs -nostartfiles -Wl,--subsystem,native
SOURCES = \ diff --git a/dlls/ntoskrnl.exe/tests/driver.c b/dlls/ntoskrnl.exe/tests/driver.c index 94139b4b654..8c5ac6d89d4 100644 --- a/dlls/ntoskrnl.exe/tests/driver.c +++ b/dlls/ntoskrnl.exe/tests/driver.c @@ -33,9 +33,9 @@ #include "ddk/ntifs.h" #include "ddk/wdm.h"
-#include "driver.h" +#include "wine/test.h"
-#include "utils.h" +#include "driver.h"
/* memcmp() isn't exported from ntoskrnl on i386 */ static int kmemcmp( const void *ptr1, const void *ptr2, size_t n ) @@ -999,41 +999,41 @@ static void test_call_driver(DEVICE_OBJECT *device) irp = IoBuildAsynchronousFsdRequest(IRP_MJ_FLUSH_BUFFERS, device, NULL, 0, NULL, &iosb); ok(irp->UserIosb == &iosb, "unexpected UserIosb\n"); ok(!irp->Cancel, "Cancel = %x\n", irp->Cancel); - ok(!irp->CancelRoutine, "CancelRoutine = %x\n", irp->CancelRoutine); + ok(!irp->CancelRoutine, "CancelRoutine = %p\n", irp->CancelRoutine); ok(!irp->UserEvent, "UserEvent = %p\n", irp->UserEvent); ok(irp->CurrentLocation == 2, "CurrentLocation = %u\n", irp->CurrentLocation); ok(irp->Tail.Overlay.Thread == (PETHREAD)KeGetCurrentThread(), "IRP thread is not the current thread\n"); ok(!irp->IoStatus.Status, "got status %#x\n", irp->IoStatus.Status); - ok(!irp->IoStatus.Information, "got information %#x\n", irp->IoStatus.Information); + ok(!irp->IoStatus.Information, "got information %#I64x\n", (UINT64)irp->IoStatus.Information); ok(iosb.Status == 0xdeadbeef, "got status %#x\n", iosb.Status); - ok(iosb.Information == 0xdeadbeef, "got information %#x\n", iosb.Information); + ok(iosb.Information == 0xdeadbeef, "got information %#I64x\n", (UINT64)iosb.Information);
irpsp = IoGetNextIrpStackLocation(irp); ok(irpsp->MajorFunction == IRP_MJ_FLUSH_BUFFERS, "MajorFunction = %u\n", irpsp->MajorFunction); - ok(!irpsp->DeviceObject, "DeviceObject = %u\n", irpsp->DeviceObject); - ok(!irpsp->FileObject, "FileObject = %u\n", irpsp->FileObject); + ok(!irpsp->DeviceObject, "DeviceObject = %p\n", irpsp->DeviceObject); + ok(!irpsp->FileObject, "FileObject = %p\n", irpsp->FileObject); ok(!irpsp->CompletionRoutine, "CompletionRoutine = %p\n", irpsp->CompletionRoutine);
status = IoCallDriver(device, irp); ok(status == STATUS_PENDING, "IoCallDriver returned %#x\n", status); ok(!irp->IoStatus.Status, "got status %#x\n", irp->IoStatus.Status); - ok(!irp->IoStatus.Information, "got information %#x\n", irp->IoStatus.Information); + ok(!irp->IoStatus.Information, "got information %#I64x\n", (UINT64)irp->IoStatus.Information); ok(iosb.Status == 0xdeadbeef, "got status %#x\n", iosb.Status); - ok(iosb.Information == 0xdeadbeef, "got information %#x\n", iosb.Information); + ok(iosb.Information == 0xdeadbeef, "got information %#I64x\n", (UINT64)iosb.Information);
irp->IoStatus.Status = STATUS_SUCCESS; irp->IoStatus.Information = 123; IoCompleteRequest(irp, IO_NO_INCREMENT); ok(iosb.Status == STATUS_SUCCESS, "got status %#x\n", iosb.Status); - ok(iosb.Information == 123, "got information %#x\n", iosb.Information); + ok(iosb.Information == 123, "got information %#I64x\n", (UINT64)iosb.Information);
KeInitializeEvent(&event, NotificationEvent, FALSE);
irp = IoBuildSynchronousFsdRequest(IRP_MJ_FLUSH_BUFFERS, device, NULL, 0, NULL, &event, &iosb); ok(irp->UserIosb == &iosb, "unexpected UserIosb\n"); ok(!irp->Cancel, "Cancel = %x\n", irp->Cancel); - ok(!irp->CancelRoutine, "CancelRoutine = %x\n", irp->CancelRoutine); + ok(!irp->CancelRoutine, "CancelRoutine = %p\n", irp->CancelRoutine); ok(irp->UserEvent == &event, "UserEvent = %p\n", irp->UserEvent); ok(irp->CurrentLocation == 2, "CurrentLocation = %u\n", irp->CurrentLocation); ok(irp->Tail.Overlay.Thread == (PETHREAD)KeGetCurrentThread(), @@ -1041,8 +1041,8 @@ static void test_call_driver(DEVICE_OBJECT *device)
irpsp = IoGetNextIrpStackLocation(irp); ok(irpsp->MajorFunction == IRP_MJ_FLUSH_BUFFERS, "MajorFunction = %u\n", irpsp->MajorFunction); - ok(!irpsp->DeviceObject, "DeviceObject = %u\n", irpsp->DeviceObject); - ok(!irpsp->FileObject, "FileObject = %u\n", irpsp->FileObject); + ok(!irpsp->DeviceObject, "DeviceObject = %p\n", irpsp->DeviceObject); + ok(!irpsp->FileObject, "FileObject = %p\n", irpsp->FileObject); ok(!irpsp->CompletionRoutine, "CompletionRoutine = %p\n", irpsp->CompletionRoutine);
status = wait_single(&event, 0); @@ -1118,7 +1118,7 @@ static void test_cancel_irp(DEVICE_OBJECT *device)
ok(irp->CurrentLocation == 1, "CurrentLocation = %u\n", irp->CurrentLocation); irpsp = IoGetCurrentIrpStackLocation(irp); - ok(irpsp->DeviceObject == device, "DeviceObject = %u\n", irpsp->DeviceObject); + ok(irpsp->DeviceObject == device, "DeviceObject = %p\n", irpsp->DeviceObject);
IoSetCancelRoutine(irp, cancel_irp); cancel_cnt = 0; @@ -1432,16 +1432,16 @@ static void check_resource_(int line, ERESOURCE *resource, ULONG exclusive_waite ULONG count;
count = ExGetExclusiveWaiterCount(resource); - ok_(__FILE__, line, count == exclusive_waiters, + ok_(__FILE__, line)(count == exclusive_waiters, "expected %u exclusive waiters, got %u\n", exclusive_waiters, count); count = ExGetSharedWaiterCount(resource); - ok_(__FILE__, line, count == shared_waiters, + ok_(__FILE__, line)(count == shared_waiters, "expected %u shared waiters, got %u\n", shared_waiters, count); ret = ExIsResourceAcquiredExclusiveLite(resource); - ok_(__FILE__, line, ret == exclusive, + ok_(__FILE__, line)(ret == exclusive, "expected exclusive %u, got %u\n", exclusive, ret); count = ExIsResourceAcquiredSharedLite(resource); - ok_(__FILE__, line, count == shared_count, + ok_(__FILE__, line)(count == shared_count, "expected shared %u, got %u\n", shared_count, count); } #define check_resource(a,b,c,d,e) check_resource_(__LINE__,a,b,c,d,e) diff --git a/dlls/ntoskrnl.exe/tests/driver.h b/dlls/ntoskrnl.exe/tests/driver.h index 455695ad36b..01d0991a25c 100644 --- a/dlls/ntoskrnl.exe/tests/driver.h +++ b/dlls/ntoskrnl.exe/tests/driver.h @@ -49,14 +49,6 @@
static const char teststr[] = "Wine is not an emulator";
-struct test_data -{ - int running_under_wine; - int winetest_report_success; - int winetest_debug; - int successes, failures, skipped, todo_successes, todo_failures; -}; - struct main_test_input { DWORD process_id; diff --git a/dlls/ntoskrnl.exe/tests/driver_hid.c b/dlls/ntoskrnl.exe/tests/driver_hid.c index 793b25f3189..ed3e5c17d6f 100644 --- a/dlls/ntoskrnl.exe/tests/driver_hid.c +++ b/dlls/ntoskrnl.exe/tests/driver_hid.c @@ -32,10 +32,10 @@ #include "ddk/hidpi.h" #include "ddk/hidport.h"
+#include "wine/test.h" #include "wine/list.h"
#include "driver.h" -#include "utils.h"
static UNICODE_STRING control_symlink;
diff --git a/dlls/ntoskrnl.exe/tests/driver_netio.c b/dlls/ntoskrnl.exe/tests/driver_netio.c index ea9cfd1a4c5..68dbfd1c5e8 100644 --- a/dlls/ntoskrnl.exe/tests/driver_netio.c +++ b/dlls/ntoskrnl.exe/tests/driver_netio.c @@ -32,9 +32,9 @@ #include "ddk/wdm.h" #include "ddk/wsk.h"
-#include "driver.h" +#include "wine/test.h"
-#include "utils.h" +#include "driver.h"
static DRIVER_OBJECT *driver_obj; static DEVICE_OBJECT *device_obj; @@ -141,7 +141,7 @@ static void test_wsk_get_address_info(void) { struct sockaddr_in *addr = (struct sockaddr_in *)addr_info->ai_addr;
- ok(addr_info->ai_addrlen == sizeof(*addr), "Got unexpected ai_addrlen %u.\n", addr_info->ai_addrlen); + ok(addr_info->ai_addrlen == sizeof(*addr), "Got unexpected ai_addrlen %I64u.\n", (UINT64)addr_info->ai_addrlen); ok(addr->sin_family == AF_INET, "Got unexpected sin_family %u.\n", addr->sin_family); ok(ntohs(addr->sin_port) == 12345, "Got unexpected sin_port %u.\n", ntohs(addr->sin_port)); ok(ntohl(addr->sin_addr.s_addr) == 0x7f000001, "Got unexpected sin_addr %#x.\n", diff --git a/dlls/ntoskrnl.exe/tests/driver_pnp.c b/dlls/ntoskrnl.exe/tests/driver_pnp.c index 98ca2ff7961..cf672f6f397 100644 --- a/dlls/ntoskrnl.exe/tests/driver_pnp.c +++ b/dlls/ntoskrnl.exe/tests/driver_pnp.c @@ -29,10 +29,10 @@ #include "winioctl.h" #include "ddk/wdm.h"
+#include "wine/test.h" #include "wine/list.h"
#include "driver.h" -#include "utils.h"
static const GUID bus_class = {0xdeadbeef, 0x29ef, 0x4538, {0xa5, 0xfd, 0xb6, 0x95, 0x73, 0xa3, 0x62, 0xc1}}; static const GUID child_class = {0xdeadbeef, 0x29ef, 0x4538, {0xa5, 0xfd, 0xb6, 0x95, 0x73, 0xa3, 0x62, 0xc2}}; diff --git a/dlls/ntoskrnl.exe/tests/ntoskrnl.c b/dlls/ntoskrnl.exe/tests/ntoskrnl.c index 0ef621011d3..c3456e56274 100644 --- a/dlls/ntoskrnl.exe/tests/ntoskrnl.c +++ b/dlls/ntoskrnl.exe/tests/ntoskrnl.c @@ -52,8 +52,6 @@ static const GUID GUID_NULL;
static HANDLE device;
-static struct test_data *test_data; - static BOOL (WINAPI *pRtlDosPathNameToNtPathName_U)(const WCHAR *, UNICODE_STRING *, WCHAR **, CURDIR *); static BOOL (WINAPI *pRtlFreeUnicodeString)(UNICODE_STRING *); static BOOL (WINAPI *pCancelIoEx)(HANDLE, OVERLAPPED *); @@ -367,27 +365,6 @@ static BOOL start_driver(HANDLE service, BOOL vista_plus) return TRUE; }
-static HANDLE okfile; - -static void cat_okfile(void) -{ - char buffer[512]; - DWORD size; - - SetFilePointer(okfile, 0, NULL, FILE_BEGIN); - - do - { - ReadFile(okfile, buffer, sizeof(buffer), &size, NULL); - printf("%.*s", size, buffer); - } while (size == sizeof(buffer)); - - SetFilePointer(okfile, 0, NULL, FILE_BEGIN); - SetEndOfFile(okfile); - - winetest_add_failures(InterlockedExchange(&test_data->failures, 0)); -} - static ULONG64 modified_value;
static void main_test(void) @@ -931,7 +908,7 @@ static void test_driver_netio(struct testsign_context *ctx) ret = DeleteFileW(filename); ok(ret, "DeleteFile failed: %u\n", GetLastError());
- cat_okfile(); + winetest_kernel_check(); }
#ifdef __i386__ @@ -1499,7 +1476,7 @@ static void test_pnp_driver(struct testsign_context *ctx) unload_driver(service); CloseServiceHandle(manager);
- cat_okfile(); + winetest_kernel_check();
GetFullPathNameA("winetest.inf", sizeof(path), path, NULL); ret = SetupCopyOEMInfA(path, NULL, 0, 0, dest, sizeof(dest), NULL, &filepart); @@ -2638,7 +2615,7 @@ static void test_hid_driver(struct testsign_context *ctx, DWORD report_id) unload_driver(service); CloseServiceHandle(manager);
- cat_okfile(); + winetest_kernel_check();
GetFullPathNameA("winetest.inf", sizeof(path), path, NULL); ret = SetupCopyOEMInfA(path, NULL, 0, 0, dest, sizeof(dest), NULL, &filepart); @@ -2665,7 +2642,6 @@ START_TEST(ntoskrnl) struct testsign_context ctx; SC_HANDLE service, service2; BOOL ret, is_wow64; - HANDLE mapping; DWORD written;
pRtlDosPathNameToNtPathName_U = (void *)GetProcAddress(GetModuleHandleA("ntdll"), "RtlDosPathNameToNtPathName_U"); @@ -2685,17 +2661,7 @@ START_TEST(ntoskrnl) if (!testsign_create_cert(&ctx)) return;
- mapping = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, - 0, sizeof(*test_data), "Global\winetest_ntoskrnl_section"); - ok(!!mapping, "got error %u\n", GetLastError()); - test_data = MapViewOfFile(mapping, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 1024); - test_data->running_under_wine = !strcmp(winetest_platform, "wine"); - test_data->winetest_report_success = winetest_report_success; - test_data->winetest_debug = winetest_debug; - - okfile = CreateFileA("C:\windows\winetest_ntoskrnl_okfile", GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL); - ok(okfile != INVALID_HANDLE_VALUE, "failed to create file, error %u\n", GetLastError()); + winetest_kernel_init();
subtest("driver"); if (!(service = load_driver(&ctx, filename, L"driver.dll", L"WineTestDriver"))) @@ -2737,7 +2703,7 @@ START_TEST(ntoskrnl) ret = DeleteFileW(filename2); ok(ret, "DeleteFile failed: %u\n", GetLastError());
- cat_okfile(); + winetest_kernel_check();
test_driver3(&ctx); subtest("driver_netio"); @@ -2752,8 +2718,5 @@ START_TEST(ntoskrnl)
out: testsign_cleanup(&ctx); - UnmapViewOfFile(test_data); - CloseHandle(mapping); - CloseHandle(okfile); - DeleteFileA("C:\windows\winetest_ntoskrnl_okfile"); + winetest_kernel_cleanup(); } diff --git a/dlls/ntoskrnl.exe/tests/utils.h b/dlls/ntoskrnl.exe/tests/utils.h deleted file mode 100644 index 6a0e00428a5..00000000000 --- a/dlls/ntoskrnl.exe/tests/utils.h +++ /dev/null @@ -1,257 +0,0 @@ -/* - * ntoskrnl.exe testing framework - * - * Copyright 2015 Sebastian Lackner - * Copyright 2015 Michael Müller - * Copyright 2015 Christian Costa - * Copyright 2020 Paul Gofman for CodeWeavers - * Copyright 2020-2021 Zebediah Figura 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - -static HANDLE okfile; -static LONG successes; -static LONG failures; -static LONG skipped; -static LONG todo_successes; -static LONG todo_failures; -static int todo_level, todo_do_loop; -static int running_under_wine; -static int winetest_debug; -static int winetest_report_success; - -static inline void kvprintf(const char *format, __ms_va_list ap) -{ - static char buffer[512]; - IO_STATUS_BLOCK io; - int len = vsnprintf(buffer, sizeof(buffer), format, ap); - ZwWriteFile(okfile, NULL, NULL, NULL, &io, buffer, len, NULL, NULL); -} - -static inline void WINAPIV kprintf(const char *format, ...) -{ - __ms_va_list valist; - - __ms_va_start(valist, format); - kvprintf(format, valist); - __ms_va_end(valist); -} - -static inline NTSTATUS winetest_init(void) -{ - const struct test_data *data; - SIZE_T size = sizeof(*data); - OBJECT_ATTRIBUTES attr; - UNICODE_STRING string; - IO_STATUS_BLOCK io; - void *addr = NULL; - HANDLE section; - NTSTATUS ret; - - RtlInitUnicodeString(&string, L"\BaseNamedObjects\winetest_ntoskrnl_section"); - /* OBJ_KERNEL_HANDLE is necessary for the file to be accessible from system threads */ - InitializeObjectAttributes(&attr, &string, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, NULL); - if ((ret = ZwOpenSection(§ion, SECTION_MAP_READ, &attr))) - return ret; - - if ((ret = ZwMapViewOfSection(section, NtCurrentProcess(), &addr, - 0, 0, NULL, &size, ViewUnmap, 0, PAGE_READONLY))) - { - ZwClose(section); - return ret; - } - data = addr; - running_under_wine = data->running_under_wine; - winetest_debug = data->winetest_debug; - winetest_report_success = data->winetest_report_success; - - ZwUnmapViewOfSection(NtCurrentProcess(), addr); - ZwClose(section); - - RtlInitUnicodeString(&string, L"\??\C:\windows\winetest_ntoskrnl_okfile"); - return ZwOpenFile(&okfile, FILE_APPEND_DATA | SYNCHRONIZE, &attr, &io, - FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT); -} - -static inline void winetest_cleanup(void) -{ - struct test_data *data; - SIZE_T size = sizeof(*data); - OBJECT_ATTRIBUTES attr; - UNICODE_STRING string; - void *addr = NULL; - HANDLE section; - - if (winetest_debug) - { - kprintf("%04x:ntoskrnl: %d tests executed (%d marked as todo, %d %s), %d skipped.\n", - PsGetCurrentProcessId(), successes + failures + todo_successes + todo_failures, - todo_successes, failures + todo_failures, - (failures + todo_failures != 1) ? "failures" : "failure", skipped ); - } - - RtlInitUnicodeString(&string, L"\BaseNamedObjects\winetest_ntoskrnl_section"); - /* OBJ_KERNEL_HANDLE is necessary for the file to be accessible from system threads */ - InitializeObjectAttributes(&attr, &string, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, NULL); - - if (!ZwOpenSection(§ion, SECTION_MAP_READ | SECTION_MAP_WRITE, &attr)) - { - if (!ZwMapViewOfSection(section, NtCurrentProcess(), &addr, - 0, 0, NULL, &size, ViewUnmap, 0, PAGE_READWRITE)) - { - data = addr; - - InterlockedExchangeAdd(&data->successes, successes); - InterlockedExchangeAdd(&data->failures, failures); - InterlockedExchangeAdd(&data->skipped, skipped); - InterlockedExchangeAdd(&data->todo_successes, todo_successes); - InterlockedExchangeAdd(&data->todo_failures, todo_failures); - - ZwUnmapViewOfSection(NtCurrentProcess(), addr); - } - ZwClose(section); - } - - ZwClose(okfile); -} - -static inline void WINAPIV vok_(const char *file, int line, int condition, const char *msg, __ms_va_list args) -{ - const char *current_file; - - if (!(current_file = drv_strrchr(file, '/')) && - !(current_file = drv_strrchr(file, '\'))) - current_file = file; - else - current_file++; - - if (todo_level) - { - if (condition) - { - kprintf("%s:%d: Test succeeded inside todo block: ", current_file, line); - kvprintf(msg, args); - InterlockedIncrement(&todo_failures); - } - else - { - if (winetest_debug > 0) - { - kprintf("%s:%d: Test marked todo: ", current_file, line); - kvprintf(msg, args); - } - InterlockedIncrement(&todo_successes); - } - } - else - { - if (!condition) - { - kprintf("%s:%d: Test failed: ", current_file, line); - kvprintf(msg, args); - InterlockedIncrement(&failures); - } - else - { - if (winetest_report_success) - kprintf("%s:%d: Test succeeded\n", current_file, line); - InterlockedIncrement(&successes); - } - } -} - -static inline void WINAPIV ok_(const char *file, int line, int condition, const char *msg, ...) -{ - __ms_va_list args; - __ms_va_start(args, msg); - vok_(file, line, condition, msg, args); - __ms_va_end(args); -} - -static inline void vskip_(const char *file, int line, const char *msg, __ms_va_list args) -{ - const char *current_file; - - if (!(current_file = drv_strrchr(file, '/')) && - !(current_file = drv_strrchr(file, '\'))) - current_file = file; - else - current_file++; - - kprintf("%s:%d: Tests skipped: ", current_file, line); - kvprintf(msg, args); - skipped++; -} - -static inline void WINAPIV win_skip_(const char *file, int line, const char *msg, ...) -{ - __ms_va_list args; - __ms_va_start(args, msg); - if (running_under_wine) - vok_(file, line, 0, msg, args); - else - vskip_(file, line, msg, args); - __ms_va_end(args); -} - -static inline void WINAPIV trace_(const char *file, int line, const char *msg, ...) -{ - const char *current_file; - __ms_va_list args; - - if (!(current_file = drv_strrchr(file, '/')) && - !(current_file = drv_strrchr(file, '\'))) - current_file = file; - else - current_file++; - - __ms_va_start(args, msg); - kprintf("%s:%d: ", current_file, line); - kvprintf(msg, args); - __ms_va_end(args); -} - -static inline void winetest_start_todo( int is_todo ) -{ - todo_level = (todo_level << 1) | (is_todo != 0); - todo_do_loop=1; -} - -static inline int winetest_loop_todo(void) -{ - int do_loop=todo_do_loop; - todo_do_loop=0; - return do_loop; -} - -static inline void winetest_end_todo(void) -{ - todo_level >>= 1; -} - -static inline int broken(int condition) -{ - return !running_under_wine && condition; -} - -#define ok(condition, ...) ok_(__FILE__, __LINE__, condition, __VA_ARGS__) -#define todo_if(is_todo) for (winetest_start_todo(is_todo); \ - winetest_loop_todo(); \ - winetest_end_todo()) -#define todo_wine todo_if(running_under_wine) -#define todo_wine_if(is_todo) todo_if((is_todo) && running_under_wine) -#define win_skip(...) win_skip_(__FILE__, __LINE__, __VA_ARGS__) -#define trace(...) trace_(__FILE__, __LINE__, __VA_ARGS__) diff --git a/include/wine/test.h b/include/wine/test.h index 8073071d262..5513022b9c2 100644 --- a/include/wine/test.h +++ b/include/wine/test.h @@ -25,6 +25,9 @@ #include <stdlib.h> #include <windef.h> #include <winbase.h> +#ifdef WINETEST_KERNEL +#include <ddk/wdm.h> +#endif /* WINETEST_KERNEL */ #include <wine/debug.h>
#ifdef __WINE_CONFIG_H @@ -59,6 +62,9 @@ extern int winetest_mute_threshold; /* current platform */ extern const char *winetest_platform;
+/* output file for kernel tests */ +extern HANDLE okfile; + extern void winetest_set_location( const char* file, int line ); extern void winetest_subtest( const char* name ); extern void winetest_ignore_exceptions( BOOL ignore ); @@ -68,6 +74,9 @@ extern void winetest_end_todo(void); extern int winetest_get_mainargs( char*** pargv ); extern LONG winetest_get_failures(void); extern void winetest_add_failures( LONG new_failures ); +extern void winetest_kernel_init( void ); +extern void winetest_kernel_check( void ); +extern void winetest_kernel_cleanup( void ); extern void winetest_wait_child_process( HANDLE process );
#ifdef STANDALONE @@ -214,7 +223,8 @@ DWORD winetest_start_time, winetest_last_time; int winetest_interactive = 0;
/* current platform */ -const char *winetest_platform = "windows"; +static char winetest_platform_buf[256] = "windows"; +const char *winetest_platform = winetest_platform_buf;
/* report successful tests (BOOL) */ int winetest_report_success = 0; @@ -226,7 +236,9 @@ int winetest_mute_threshold = 42; static int winetest_argc; static char** winetest_argv;
+#ifndef WINETEST_KERNEL static const struct test *current_test; /* test currently being run */ +#endif /* WINETEST_KERNEL */
static LONG successes; /* number of successful tests */ static LONG failures; /* number of failures */ @@ -240,9 +252,15 @@ static LONG muted_todo_successes; /* same as todo_successes but silent */ /* counts how many times a given line printed a message */ static LONG line_counters[16384];
+/* output file for driver tests */ +HANDLE okfile; + /* The following data must be kept track of on a per-thread basis */ struct tls_data { +#ifdef WINETEST_KERNEL + HANDLE thread; +#endif /* WINETEST_KERNEL */ const char* current_file; /* file of current check */ int current_line; /* line of current check */ unsigned int todo_level; /* current todo nesting level */ @@ -252,6 +270,38 @@ struct tls_data char context[8][128]; /* data to print before messages */ unsigned int context_count; /* number of context prefixes */ }; + +#ifdef WINETEST_KERNEL + +static KSPIN_LOCK tls_data_lock; +static struct tls_data tls_data_pool[128]; +static DWORD tls_data_count; + +static struct tls_data *get_tls_data(void) +{ + static struct tls_data tls_overflow; + struct tls_data *data; + HANDLE thread = PsGetCurrentThreadId(); + KIRQL irql; + + KeAcquireSpinLock(&tls_data_lock, &irql); + for (data = tls_data_pool; data != tls_data_pool + tls_data_count; ++data) + if (data->thread == thread) break; + if (data == tls_data_pool + ARRAY_SIZE(tls_data_pool)) + data = &tls_overflow; + else if (data == tls_data_pool + tls_data_count) + { + data->thread = thread; + data->str_pos = data->strings; + tls_data_count++; + } + KeReleaseSpinLock(&tls_data_lock, irql); + + return data; +} + +#else /* WINETEST_KERNEL */ + static DWORD tls_index;
static struct tls_data *get_tls_data(void) @@ -277,6 +327,7 @@ static void exit_process( int code ) ExitProcess( code ); }
+#endif /* WINETEST_KERNEL */
void winetest_set_location( const char* file, int line ) { @@ -291,6 +342,36 @@ void winetest_set_location( const char* file, int line ) data->current_line=line; }
+#ifdef WINETEST_KERNEL + +static inline void __winetest_vprintf(const char *format, __ms_va_list ap) +{ + static char buffer[512]; + IO_STATUS_BLOCK io; + int len = vsnprintf(buffer, sizeof(buffer), format, ap); + ZwWriteFile(okfile, NULL, NULL, NULL, &io, buffer, len, NULL, NULL); +} + +static inline void WINAPIV __winetest_printf(const char *format, ...) +{ + __ms_va_list valist; + + __ms_va_start(valist, format); + __winetest_vprintf(format, valist); + __ms_va_end(valist); +} + +const char *winetest_elapsed(void) +{ + if (!winetest_time) return ""; + return "0.000"; +} + +#else /* WINETEST_KERNEL */ + +#define __winetest_printf printf +#define __winetest_vprintf vprintf + const char *winetest_elapsed(void) { DWORD now; @@ -300,15 +381,17 @@ const char *winetest_elapsed(void) return wine_dbg_sprintf( "%.3f", (now - winetest_start_time) / 1000.0); }
+#endif /* WINETEST_KERNEL */ + static void __winetest_cdecl winetest_printf( const char *msg, ... ) __WINE_PRINTF_ATTR(1,2); static void __winetest_cdecl winetest_printf( const char *msg, ... ) { struct tls_data *data = get_tls_data(); __winetest_va_list valist;
- printf( "%s:%d:%s ", data->current_file, data->current_line, winetest_elapsed() ); + __winetest_printf( "%s:%d:%s ", data->current_file, data->current_line, winetest_elapsed() ); __winetest_va_start( valist, msg ); - vprintf( msg, valist ); + __winetest_vprintf( msg, valist ); __winetest_va_end( valist ); } static void __winetest_cdecl winetest_print_context( const char *msgtype ) @@ -318,7 +401,7 @@ static void __winetest_cdecl winetest_print_context( const char *msgtype )
winetest_printf( "%s", msgtype ); for (i = 0; i < data->context_count; ++i) - printf( "%s: ", data->context[i] ); + __winetest_printf( "%s: ", data->context[i] ); }
void winetest_subtest( const char* name ) @@ -372,7 +455,7 @@ int winetest_vok( int condition, const char *msg, __winetest_va_list args ) if (condition) { winetest_print_context( "Test succeeded inside todo block: " ); - vprintf(msg, args); + __winetest_vprintf(msg, args); InterlockedIncrement(&todo_failures); return 0; } @@ -384,7 +467,7 @@ int winetest_vok( int condition, const char *msg, __winetest_va_list args ) if (winetest_debug > 0) { winetest_print_context( "Test marked todo: " ); - vprintf(msg, args); + __winetest_vprintf(msg, args); } InterlockedIncrement(&todo_successes); } @@ -398,17 +481,18 @@ int winetest_vok( int condition, const char *msg, __winetest_va_list args ) if (!condition) { winetest_print_context( "Test failed: " ); - vprintf(msg, args); + __winetest_vprintf(msg, args); InterlockedIncrement(&failures); return 0; } else { - if (winetest_report_success || - (winetest_time && GetTickCount() >= winetest_last_time + 1000)) - { + if (winetest_report_success) winetest_printf("Test succeeded\n"); - } +#ifndef WINETEST_KERNEL + else if (winetest_time && GetTickCount() >= winetest_last_time + 1000) + winetest_printf("Test succeeded\n"); +#endif /* WINETEST_KERNEL */ InterlockedIncrement(&successes); return 1; } @@ -434,7 +518,7 @@ void __winetest_cdecl winetest_trace( const char *msg, ... ) { winetest_print_context( "" ); __winetest_va_start(valist, msg); - vprintf( msg, valist ); + __winetest_vprintf( msg, valist ); __winetest_va_end(valist); } else @@ -446,7 +530,7 @@ void winetest_vskip( const char *msg, __winetest_va_list args ) if (winetest_add_line() < winetest_mute_threshold) { winetest_print_context( "Tests skipped: " ); - vprintf(msg, args); + __winetest_vprintf(msg, args); InterlockedIncrement(&skipped); } else @@ -533,6 +617,155 @@ void winetest_add_failures( LONG new_failures ) InterlockedIncrement( &failures ); }
+struct winetest_kernel_data +{ + char winetest_platform[256]; + int running_under_wine; + int winetest_report_success; + int winetest_debug; + int failures; + int todo_failures; +}; + +#ifdef WINETEST_KERNEL + +static int running_under_wine; + +static inline NTSTATUS winetest_init(void) +{ + const struct winetest_kernel_data *data; + SIZE_T size = sizeof(*data); + OBJECT_ATTRIBUTES attr; + UNICODE_STRING string; + IO_STATUS_BLOCK io; + void *addr = NULL; + HANDLE section; + NTSTATUS ret; + + KeInitializeSpinLock(&tls_data_lock); + + RtlInitUnicodeString(&string, L"\BaseNamedObjects\winetest_kernel_section"); + /* OBJ_KERNEL_HANDLE is necessary for the file to be accessible from system threads */ + InitializeObjectAttributes(&attr, &string, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, NULL); + if ((ret = ZwOpenSection(§ion, SECTION_MAP_READ, &attr))) + return ret; + + if ((ret = ZwMapViewOfSection(section, NtCurrentProcess(), &addr, + 0, 0, NULL, &size, ViewUnmap, 0, PAGE_READONLY))) + { + ZwClose(okfile); + ZwClose(section); + return ret; + } + data = addr; + running_under_wine = data->running_under_wine; + winetest_debug = data->winetest_debug; + winetest_report_success = data->winetest_report_success; + strcpy(winetest_platform_buf, data->winetest_platform); + + ZwUnmapViewOfSection(NtCurrentProcess(), addr); + ZwClose(section); + + RtlInitUnicodeString(&string, L"\??\C:\windows\winetest_kernel_okfile"); + return ZwOpenFile(&okfile, FILE_APPEND_DATA | SYNCHRONIZE, &attr, &io, + FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT); +} + +static inline void winetest_cleanup(void) +{ + struct winetest_kernel_data *data; + SIZE_T size = sizeof(*data); + OBJECT_ATTRIBUTES attr; + UNICODE_STRING string; + void *addr = NULL; + HANDLE section; + + if (winetest_debug) + { + if (muted_todo_successes || muted_skipped || muted_traces) + __winetest_printf( "%04x:ntoskrnl:%s Silenced %d todos, %d skips and %d traces.\n", + (DWORD)(ULONG_PTR)PsGetCurrentProcessId(), winetest_elapsed(), + muted_todo_successes, muted_skipped, muted_traces); + __winetest_printf( "%04x:ntoskrnl:%s %d tests executed (%d marked as todo, %d %s), %d skipped.\n", + (DWORD)(ULONG_PTR)PsGetCurrentProcessId(), winetest_elapsed(), + successes + failures + todo_successes + todo_failures, + todo_successes, failures + todo_failures, + (failures + todo_failures != 1) ? "failures" : "failure", + skipped ); + } + + RtlInitUnicodeString(&string, L"\BaseNamedObjects\winetest_kernel_section"); + /* OBJ_KERNEL_HANDLE is necessary for the file to be accessible from system threads */ + InitializeObjectAttributes(&attr, &string, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, NULL); + + if (!ZwOpenSection(§ion, SECTION_MAP_READ | SECTION_MAP_WRITE, &attr)) + { + if (!ZwMapViewOfSection(section, NtCurrentProcess(), &addr, + 0, 0, NULL, &size, ViewUnmap, 0, PAGE_READWRITE)) + { + data = addr; + + InterlockedExchangeAdd(&data->failures, failures); + InterlockedExchangeAdd(&data->todo_failures, todo_failures); + + ZwUnmapViewOfSection(NtCurrentProcess(), addr); + } + ZwClose(section); + } + + ZwClose(okfile); +} + +#else /* WINETEST_KERNEL */ + +static HANDLE winetest_mapping; +static struct winetest_kernel_data *kernel_data; + +void winetest_kernel_init(void) +{ + winetest_mapping = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, + sizeof(*kernel_data), "Global\winetest_kernel_section"); + ok(!!winetest_mapping, "got error %u\n", GetLastError()); + kernel_data = MapViewOfFile(winetest_mapping, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 1024); + ok(strlen(winetest_platform) < 256, "winetest_platform expectedly large!\n"); + strcpy(kernel_data->winetest_platform, winetest_platform); + kernel_data->running_under_wine = !strcmp(winetest_platform, "wine"); + kernel_data->winetest_report_success = winetest_report_success; + kernel_data->winetest_debug = winetest_debug; + + okfile = CreateFileA("C:\windows\winetest_kernel_okfile", GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL); + ok(okfile != INVALID_HANDLE_VALUE, "failed to create file, error %u\n", GetLastError()); +} + +void winetest_kernel_check(void) +{ + char buffer[512]; + DWORD size; + + SetFilePointer(okfile, 0, NULL, FILE_BEGIN); + + do + { + ReadFile(okfile, buffer, sizeof(buffer), &size, NULL); + printf("%.*s", size, buffer); + } while (size == sizeof(buffer)); + + SetFilePointer(okfile, 0, NULL, FILE_BEGIN); + /* SetEndOfFile(okfile); */ + + InterlockedExchangeAdd(&failures, InterlockedExchange(&kernel_data->failures, 0)); + InterlockedExchangeAdd(&todo_failures, InterlockedExchange(&kernel_data->todo_failures, 0)); +} + +void winetest_kernel_cleanup(void) +{ + UnmapViewOfFile(kernel_data); + CloseHandle(winetest_mapping); + CloseHandle(okfile); + /* DeleteFileA("C:\windows\winetest_kernel_okfile"); */ +} + void winetest_wait_child_process( HANDLE process ) { DWORD ret; @@ -702,6 +935,7 @@ int main( int argc, char **argv ) return run_test(argv[1]); }
+#endif /* WINETEST_KERNEL */ #endif /* STANDALONE */
#endif /* __WINE_WINE_TEST_H */