Files
libvirtualhid/tests/fixtures/windows_broker_client_test_hooks.cpp

274 lines
8.0 KiB
C++

/**
* @file tests/fixtures/windows_broker_client_test_hooks.cpp
* @brief Windows broker client test hook definitions.
*/
// local includes
#include "fixtures/windows_broker_client_test_hooks.hpp"
// system includes
#include <Windows.h>
#include <winsvc.h>
// standard includes
#include <array>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <span>
#include <string_view>
#include <utility>
namespace {
constexpr auto broker_process_id = 42UL;
struct FakeBrokerClientState {
lvh::detail::test::BrokerServiceScenario scenario =
lvh::detail::test::BrokerServiceScenario::success;
DWORD last_error = ERROR_ACCESS_DENIED;
std::uint32_t closed_pipe_handles = 0;
std::uint32_t closed_service_handles = 0;
std::uint32_t create_attempts = 0;
std::uint32_t sleep_attempts = 0;
std::uint32_t wait_attempts = 0;
bool transacted = false;
};
FakeBrokerClientState &fake_state() {
static FakeBrokerClientState state;
return state;
}
HANDLE fake_pipe_handle() {
static std::byte storage {};
return &storage;
}
SC_HANDLE fake_service_manager_handle() {
static SC_HANDLE__ storage {};
return &storage;
}
SC_HANDLE fake_service_handle() {
static SC_HANDLE__ storage {};
return &storage;
}
BOOL WINAPI fake_close_handle(HANDLE) {
++fake_state().closed_pipe_handles;
return TRUE;
}
BOOL WINAPI fake_close_service_handle(SC_HANDLE) {
++fake_state().closed_service_handles;
return TRUE;
}
DWORD WINAPI fake_get_last_error() {
return fake_state().last_error;
}
void WINAPI fake_set_last_error(DWORD error) {
fake_state().last_error = error;
}
HANDLE WINAPI fake_create_file_a(
LPCSTR,
DWORD,
DWORD,
LPSECURITY_ATTRIBUTES,
DWORD,
DWORD,
HANDLE
) {
using enum lvh::detail::test::BrokerServiceScenario;
++fake_state().create_attempts;
const auto scenario = fake_state().scenario;
if ((scenario == pipe_unavailable_once && fake_state().create_attempts == 1U) || scenario == pipe_never_available || scenario == pipe_service_missing || scenario == pipe_service_stopped || scenario == pipe_service_stop_pending || scenario == pipe_service_query_failure || scenario == pipe_service_manager_unavailable) {
fake_state().last_error = ERROR_FILE_NOT_FOUND;
return INVALID_HANDLE_VALUE;
}
if (scenario == pipe_access_denied) {
fake_state().last_error = ERROR_ACCESS_DENIED;
return INVALID_HANDLE_VALUE;
}
if (fake_state().create_attempts == 1U && (scenario == pipe_busy_once || scenario == pipe_busy_timeout_once || scenario == pipe_busy_disappears_once || scenario == pipe_busy_failure)) {
fake_state().last_error = ERROR_PIPE_BUSY;
return INVALID_HANDLE_VALUE;
}
return fake_pipe_handle();
}
void WINAPI fake_sleep(DWORD) {
++fake_state().sleep_attempts;
// The deterministic retry test must not wait in real time.
}
BOOL WINAPI fake_wait_named_pipe_a(LPCSTR, DWORD) {
using enum lvh::detail::test::BrokerServiceScenario;
++fake_state().wait_attempts;
switch (fake_state().scenario) {
case pipe_busy_once:
return TRUE;
case pipe_busy_timeout_once:
fake_state().last_error = ERROR_SEM_TIMEOUT;
return FALSE;
case pipe_busy_disappears_once:
fake_state().last_error = ERROR_FILE_NOT_FOUND;
return FALSE;
case pipe_busy_failure:
default:
fake_state().last_error = ERROR_ACCESS_DENIED;
return FALSE;
}
}
BOOL WINAPI fake_get_named_pipe_server_process_id(HANDLE, PULONG process_id) {
using enum lvh::detail::test::BrokerServiceScenario;
if (fake_state().scenario == pipe_process_failure) {
return FALSE;
}
*process_id = fake_state().scenario == zero_pipe_process ? 0UL : broker_process_id;
return TRUE;
}
SC_HANDLE WINAPI fake_open_service_manager(LPCWSTR, LPCWSTR, DWORD) {
using enum lvh::detail::test::BrokerServiceScenario;
if (fake_state().scenario == service_manager_failure || fake_state().scenario == pipe_service_manager_unavailable) {
return nullptr;
}
return fake_service_manager_handle();
}
SC_HANDLE WINAPI fake_open_service(SC_HANDLE, LPCWSTR, DWORD) {
using enum lvh::detail::test::BrokerServiceScenario;
if (fake_state().scenario == pipe_service_missing) {
fake_state().last_error = ERROR_SERVICE_DOES_NOT_EXIST;
return nullptr;
}
if (fake_state().scenario == service_failure) {
return nullptr;
}
return fake_service_handle();
}
BOOL WINAPI fake_query_service_status(
SC_HANDLE,
SC_STATUS_TYPE,
LPBYTE buffer,
DWORD,
LPDWORD bytes_needed
) {
using enum lvh::detail::test::BrokerServiceScenario;
if (fake_state().scenario == service_query_failure || fake_state().scenario == pipe_service_query_failure) {
return FALSE;
}
SERVICE_STATUS_PROCESS status {};
if (fake_state().scenario == service_stopped || fake_state().scenario == pipe_service_stopped) {
status.dwCurrentState = SERVICE_STOPPED;
} else if (fake_state().scenario == pipe_service_stop_pending) {
status.dwCurrentState = SERVICE_STOP_PENDING;
} else {
status.dwCurrentState = SERVICE_RUNNING;
}
status.dwProcessId = fake_state().scenario == service_process_mismatch ? broker_process_id + 1UL : broker_process_id;
*bytes_needed = sizeof(status);
std::memcpy(buffer, &status, sizeof(status));
return TRUE;
}
BOOL WINAPI fake_set_named_pipe_handle_state(HANDLE, LPDWORD, LPDWORD, LPDWORD) {
return TRUE;
}
BOOL WINAPI fake_transact_named_pipe(
HANDLE,
std::byte *,
DWORD,
std::byte *response,
DWORD response_size,
LPDWORD bytes_read,
LPOVERLAPPED
) {
std::memset(response, 0, response_size);
*bytes_read = response_size;
fake_state().transacted = true;
return TRUE;
}
} // namespace
#define CloseHandle fake_close_handle
#define CloseServiceHandle fake_close_service_handle
#define CreateFileA fake_create_file_a
#define GetLastError fake_get_last_error
#define GetNamedPipeServerProcessId fake_get_named_pipe_server_process_id
#define OpenSCManagerW fake_open_service_manager
#define OpenServiceW fake_open_service
#define QueryServiceStatusEx fake_query_service_status
#define SetLastError fake_set_last_error
#define SetNamedPipeHandleState fake_set_named_pipe_handle_state
#define Sleep fake_sleep
#define TransactNamedPipe fake_transact_named_pipe
#define WaitNamedPipeA fake_wait_named_pipe_a
#define call_bytes call_bytes_for_windows_broker_client_test_hooks
#define make_request_header make_request_header_for_windows_broker_client_test_hooks
#define response_status response_status_for_windows_broker_client_test_hooks
#include "../../src/platform/windows/windows_broker_client.cpp"
#undef CloseHandle
#undef CloseServiceHandle
#undef CreateFileA
#undef GetLastError
#undef GetNamedPipeServerProcessId
#undef OpenSCManagerW
#undef OpenServiceW
#undef QueryServiceStatusEx
#undef SetLastError
#undef SetNamedPipeHandleState
#undef Sleep
#undef TransactNamedPipe
#undef WaitNamedPipeA
#undef call_bytes
#undef make_request_header
#undef response_status
namespace lvh::detail::test {
BrokerServiceVerificationResult verify_broker_service_scenario(
BrokerServiceScenario scenario
) {
fake_state() = {
.scenario = scenario,
};
std::array<std::byte, 1> request {};
std::array<std::byte, 1> response {};
auto status = windows_broker::call_bytes_for_windows_broker_client_test_hooks(
std::span<const std::byte> {request},
std::span<std::byte> {response},
"Test broker request"
);
return {
.status = std::move(status),
.closed_pipe_handles = fake_state().closed_pipe_handles,
.closed_service_handles = fake_state().closed_service_handles,
.create_attempts = fake_state().create_attempts,
.sleep_attempts = fake_state().sleep_attempts,
.wait_attempts = fake_state().wait_attempts,
.last_error = fake_state().last_error,
.transacted = fake_state().transacted,
};
}
} // namespace lvh::detail::test