Test interaction between SetThreadExecutionState(), PowerSetRequest(), and this parameter.
Signed-off-by: Chip Davis cdavis@codeweavers.com ---
Notes: v2: Test that, when a thread terminates, its execution state is cleaned up.
configure.ac | 1 + dlls/powrprof/tests/Makefile.in | 4 + dlls/powrprof/tests/powrprof.c | 219 ++++++++++++++++++++++++++++++++ include/powrprof.h | 5 + 4 files changed, 229 insertions(+) create mode 100644 dlls/powrprof/tests/Makefile.in create mode 100644 dlls/powrprof/tests/powrprof.c
diff --git a/configure.ac b/configure.ac index f45c078674c2..b277642e316f 100644 --- a/configure.ac +++ b/configure.ac @@ -3616,6 +3616,7 @@ WINE_CONFIG_MAKEFILE(dlls/pdh/tests) WINE_CONFIG_MAKEFILE(dlls/photometadatahandler) WINE_CONFIG_MAKEFILE(dlls/pidgen) WINE_CONFIG_MAKEFILE(dlls/powrprof) +WINE_CONFIG_MAKEFILE(dlls/powrprof/tests) WINE_CONFIG_MAKEFILE(dlls/printui) WINE_CONFIG_MAKEFILE(dlls/prntvpt) WINE_CONFIG_MAKEFILE(dlls/propsys) diff --git a/dlls/powrprof/tests/Makefile.in b/dlls/powrprof/tests/Makefile.in new file mode 100644 index 000000000000..87aa06be5026 --- /dev/null +++ b/dlls/powrprof/tests/Makefile.in @@ -0,0 +1,4 @@ +TESTDLL = powrprof.dll +IMPORTS = powrprof + +C_SRCS = powrprof.c diff --git a/dlls/powrprof/tests/powrprof.c b/dlls/powrprof/tests/powrprof.c new file mode 100644 index 000000000000..d744895cc484 --- /dev/null +++ b/dlls/powrprof/tests/powrprof.c @@ -0,0 +1,219 @@ +/* + * Copyright 2019 Chip Davis 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 + * + */ + +#include <stdarg.h> +#include <stdio.h> + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winternl.h" +#include "powrprof.h" + +#include "wine/test.h" + +HANDLE (WINAPI *pPowerCreateRequest)(REASON_CONTEXT *); +BOOL (WINAPI *pPowerSetRequest)(HANDLE, POWER_REQUEST_TYPE); +BOOL (WINAPI *pPowerClearRequest)(HANDLE, POWER_REQUEST_TYPE); + +static void test_system_execution_state(void) +{ + EXECUTION_STATE es, old_es; + NTSTATUS status; + + status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es)); + ok(status == STATUS_SUCCESS, "status %08x\n", status); + + old_es = SetThreadExecutionState(ES_SYSTEM_REQUIRED); + todo_wine ok(old_es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", old_es); + + old_es = es; + status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es)); + ok(status == STATUS_SUCCESS, "status %08x\n", status); + ok(es == old_es, "unexpected execution state 0x%08x vs 0x%08x\n", es, old_es); + + old_es = SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED); + todo_wine ok(old_es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", old_es); + + status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es)); + ok(status == STATUS_SUCCESS, "status %08x\n", status); + todo_wine ok(es & ES_DISPLAY_REQUIRED, "unexpected execution state 0x%08x\n", es); + + old_es = SetThreadExecutionState(ES_CONTINUOUS); + ok(old_es == (ES_CONTINUOUS|ES_DISPLAY_REQUIRED), "unexpected execution state 0x%08x\n", old_es); + + status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es)); + ok(status == STATUS_SUCCESS, "status %08x\n", status); + + old_es = SetThreadExecutionState(ES_DISPLAY_REQUIRED); + ok(old_es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", old_es); + + old_es = es; + status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es)); + ok(status == STATUS_SUCCESS, "status %08x\n", status); + ok(es == old_es, "unexpected execution state 0x%08x vs 0x%08x\n", es, old_es); +} + +static HANDLE events[2]; + +static DWORD CALLBACK execution_state_thread(LPVOID param) +{ + EXECUTION_STATE old_es; + + SetThreadExecutionState(ES_SYSTEM_REQUIRED); + SignalObjectAndWait(events[0], events[1], INFINITE, FALSE); + + old_es = SetThreadExecutionState(ES_DISPLAY_REQUIRED); + ok(old_es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", old_es); + SignalObjectAndWait(events[0], events[1], INFINITE, FALSE); + + old_es = SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED); + ok(old_es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", old_es); + SignalObjectAndWait(events[0], events[1], INFINITE, FALSE); + + old_es = SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED); + ok(old_es == (ES_CONTINUOUS|ES_SYSTEM_REQUIRED), "unexpected execution state 0x%08x\n", old_es); + SignalObjectAndWait(events[0], events[1], INFINITE, FALSE); + + return 0; +} + +static void test_system_execution_state_other_thread(void) +{ + HANDLE thread; + EXECUTION_STATE base_es, es; + NTSTATUS status; + + status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &base_es, sizeof(base_es)); + ok(status == STATUS_SUCCESS, "status %08x\n", status); + trace("base execution state = 0x%08x\n", base_es); + + events[0] = CreateEventW(NULL, FALSE, FALSE, NULL); + events[1] = CreateEventW(NULL, FALSE, FALSE, NULL); + thread = CreateThread(NULL, 0, execution_state_thread, NULL, 0, NULL); + ok(thread != NULL, "Failed to create thread, err %u\n", GetLastError()); + + WaitForSingleObject(events[0], INFINITE); + status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es)); + ok(status == STATUS_SUCCESS, "status %08x\n", status); + ok(es == base_es, "unexpected execution state 0x%08x\n", es); + es = SetThreadExecutionState(0); + ok(es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", es); + SignalObjectAndWait(events[1], events[0], INFINITE, FALSE); + + status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es)); + ok(status == STATUS_SUCCESS, "status %08x\n", status); + ok(es == base_es, "unexpected execution state 0x%08x\n", es); + es = SetThreadExecutionState(0); + ok(es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", es); + SignalObjectAndWait(events[1], events[0], INFINITE, FALSE); + + status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es)); + ok(status == STATUS_SUCCESS, "status %08x\n", status); + todo_wine ok(es & ES_SYSTEM_REQUIRED, "unexpected execution state 0x%08x\n", es); + es = SetThreadExecutionState(0); + todo_wine ok(es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", es); + SignalObjectAndWait(events[1], events[0], INFINITE, FALSE); + + status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es)); + ok(status == STATUS_SUCCESS, "status %08x\n", status); + todo_wine ok(es & ES_DISPLAY_REQUIRED, "unexpected execution state 0x%08x\n", es); + es = SetThreadExecutionState(0); + todo_wine ok(es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", es); + SignalObjectAndWait(events[1], thread, INFINITE, FALSE); + + status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es)); + ok(status == STATUS_SUCCESS, "status %08x\n", status); + ok(es == base_es, "unexpected execution state 0x%08x\n", es); + es = SetThreadExecutionState(0); + todo_wine ok(es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", es); + + CloseHandle(thread); + CloseHandle(events[0]); + CloseHandle(events[1]); +} + +static void test_system_execution_state_power_request() +{ + HANDLE req; + REASON_CONTEXT reason; + BOOL ret; + NTSTATUS status; + EXECUTION_STATE base_es, es; + static const WCHAR reasonW[] = {'W', 'i', 'n', 'e', ' ', 't', 'e', 's', 't', 0}; + + if (!pPowerCreateRequest) + { + win_skip("Power request objects unavailable\n"); + return; + } + + status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &base_es, sizeof(base_es)); + ok(status == STATUS_SUCCESS, "status %08x\n", status); + trace("base execution state = 0x%08x\n", base_es); + + reason.Version = 0; + reason.Flags = POWER_REQUEST_CONTEXT_SIMPLE_STRING; + reason.Reason.SimpleReasonString = reasonW; + req = pPowerCreateRequest(&reason); + todo_wine ok(req != INVALID_HANDLE_VALUE, "err %u\n", GetLastError()); + + ret = pPowerSetRequest(req, PowerRequestSystemRequired); + todo_wine ok(ret, "err %u\n", GetLastError()); + + status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es)); + ok(status == STATUS_SUCCESS, "status %08x\n", status); + todo_wine ok(es & ES_SYSTEM_REQUIRED, "unexpected execution state 0x%08x\n", es); + + ret = pPowerClearRequest(req, PowerRequestSystemRequired); + todo_wine ok(ret, "err %u\n", GetLastError()); + + status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es)); + ok(status == STATUS_SUCCESS, "status %08x\n", status); + ok(!(es & ES_SYSTEM_REQUIRED) || (base_es & ES_SYSTEM_REQUIRED), "unexpected execution state 0x%08x\n", es); + + ret = pPowerSetRequest(req, PowerRequestDisplayRequired); + todo_wine ok(ret, "err %u\n", GetLastError()); + + status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es)); + ok(status == STATUS_SUCCESS, "status %08x\n", status); + todo_wine ok(es & ES_DISPLAY_REQUIRED, "unexpected execution state 0x%08x\n", es); + + ret = CloseHandle(req); + todo_wine ok(ret, "err %u\n", GetLastError()); + + status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es)); + ok(status == STATUS_SUCCESS, "status %08x\n", status); + ok(!(es & ES_DISPLAY_REQUIRED) || (base_es & ES_DISPLAY_REQUIRED), "unexpected execution state 0x%08x\n", es); +} + +START_TEST(powrprof) +{ + HMODULE hkernel32; + + hkernel32 = GetModuleHandleA("kernel32.dll"); + pPowerCreateRequest = (void *)GetProcAddress(hkernel32, "PowerCreateRequest"); + pPowerSetRequest = (void *)GetProcAddress(hkernel32, "PowerSetRequest"); + pPowerClearRequest = (void *)GetProcAddress(hkernel32, "PowerClearRequest"); + + test_system_execution_state(); + test_system_execution_state_other_thread(); + test_system_execution_state_power_request(); +} diff --git a/include/powrprof.h b/include/powrprof.h index 318188e465e5..421d9d4fc763 100644 --- a/include/powrprof.h +++ b/include/powrprof.h @@ -146,6 +146,11 @@ extern "C" {
typedef BOOLEAN (CALLBACK* PWRSCHEMESENUMPROC)(UINT, DWORD, LPWSTR, DWORD, LPWSTR, PPOWER_POLICY, LPARAM);
+#ifndef WINE_NTSTATUS_DECLARED +#define WINE_NTSTATUS_DECLARED +typedef LONG NTSTATUS; +#endif + NTSTATUS WINAPI CallNtPowerInformation(POWER_INFORMATION_LEVEL, PVOID, ULONG, PVOID, ULONG); BOOLEAN WINAPI CanUserWritePwrScheme(VOID); BOOLEAN WINAPI DeletePwrScheme(UINT);