Files
windows-user-space-emulator/src/windows-emulator-test/emulation_test_utils.hpp
2025-04-18 08:15:17 +02:00

189 lines
5.6 KiB
C++

#pragma once
#include <cstdlib>
#include <gtest/gtest.h>
#include <windows_emulator.hpp>
#include <network/static_socket_factory.hpp>
#define ASSERT_NOT_TERMINATED(win_emu) \
do \
{ \
ASSERT_FALSE((win_emu).process.exit_status.has_value()); \
} while (false)
#define ASSERT_TERMINATED_WITH_STATUS(win_emu, status) \
do \
{ \
ASSERT_TRUE((win_emu).process.exit_status.has_value()); \
ASSERT_EQ(*(win_emu).process.exit_status, status); \
} while (false)
#define ASSERT_TERMINATED_SUCCESSFULLY(win_emu) ASSERT_TERMINATED_WITH_STATUS(win_emu, STATUS_SUCCESS)
namespace test
{
inline bool enable_verbose_logging()
{
const auto* env = getenv("EMULATOR_VERBOSE");
return env && (env == "1"sv || env == "true"sv);
}
inline std::filesystem::path get_emulator_root()
{
const auto* env = getenv("EMULATOR_ROOT");
if (!env)
{
throw std::runtime_error("No EMULATOR_ROOT set!");
}
return env;
}
struct sample_configuration
{
bool print_time{false};
};
inline application_settings get_sample_app_settings(const sample_configuration& config)
{
application_settings settings{.application = "C:\\test-sample.exe"};
if (config.print_time)
{
settings.arguments.emplace_back(u"-time");
}
return settings;
}
inline windows_emulator create_emulator(emulator_settings settings, emulator_callbacks callbacks = {})
{
const auto is_verbose = enable_verbose_logging();
if (is_verbose)
{
settings.disable_logging = false;
}
settings.emulation_root = get_emulator_root();
settings.path_mappings["C:\\a.txt"] =
std::filesystem::temp_directory_path() / ("emulator-test-file-" + std::to_string(getpid()) + ".txt");
return windows_emulator{
settings,
std::move(callbacks),
emulator_interfaces{
.socket_factory = network::create_static_socket_factory(),
},
};
}
inline windows_emulator create_sample_emulator(emulator_settings settings, const sample_configuration& config = {},
emulator_callbacks callbacks = {})
{
const auto is_verbose = enable_verbose_logging();
if (is_verbose)
{
settings.disable_logging = false;
// settings.verbose_calls = true;
}
settings.emulation_root = get_emulator_root();
settings.path_mappings["C:\\a.txt"] =
std::filesystem::temp_directory_path() / ("emulator-test-file-" + std::to_string(getpid()) + ".txt");
return windows_emulator{
get_sample_app_settings(config),
settings,
std::move(callbacks),
emulator_interfaces{
.socket_factory = network::create_static_socket_factory(),
},
};
}
inline windows_emulator create_sample_emulator(const sample_configuration& config = {})
{
emulator_settings settings{
.disable_logging = true,
.use_relative_time = true,
};
return create_sample_emulator(std::move(settings), config);
}
inline windows_emulator create_empty_emulator()
{
emulator_settings settings{
.disable_logging = true,
.use_relative_time = true,
};
return create_emulator(std::move(settings));
}
inline void bisect_emulation(windows_emulator& emu)
{
utils::buffer_serializer start_state{};
emu.serialize(start_state);
emu.start();
const auto limit = emu.get_executed_instructions();
const auto reset_emulator = [&] {
utils::buffer_deserializer deserializer{start_state};
emu.deserialize(deserializer);
};
const auto get_state_for_count = [&](const size_t count) {
reset_emulator();
emu.start(count);
utils::buffer_serializer state{};
emu.serialize(state);
return state;
};
const auto has_diff_after_count = [&](const size_t count) {
const auto s1 = get_state_for_count(count);
const auto s2 = get_state_for_count(count);
return s1.get_diff(s2).has_value();
};
if (!has_diff_after_count(static_cast<size_t>(limit)))
{
puts("Emulation has no diff");
}
auto upper_bound = limit;
decltype(upper_bound) lower_bound = 0;
printf("Bounds: %" PRIx64 " - %" PRIx64 "\n", lower_bound, upper_bound);
while (lower_bound + 1 < upper_bound)
{
const auto diff = (upper_bound - lower_bound);
const auto pivot = lower_bound + (diff / 2);
const auto has_diff = has_diff_after_count(static_cast<size_t>(pivot));
auto* bound = has_diff ? &upper_bound : &lower_bound;
*bound = pivot;
printf("Bounds: %" PRIx64 " - %" PRIx64 "\n", lower_bound, upper_bound);
}
(void)get_state_for_count(static_cast<size_t>(lower_bound));
const auto rip = emu.emu().read_instruction_pointer();
printf("Diff detected after 0x%" PRIx64 " instructions at 0x%" PRIx64 " (%s)\n", lower_bound, rip,
emu.mod_manager.find_name(rip));
}
}