C++17ification.
C++17ification! - Filesystem interaction now uses std::filesystem::path. - Usage of const char*, std::string have been changed to std::string_view where appropriate. - Usage of printf-style functions changed to use fmt.
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2019 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -23,10 +23,10 @@
|
||||
#include "xenia/base/platform_win.h"
|
||||
#endif // XE_COMPILER_MSVC
|
||||
|
||||
DEFINE_string(test_path, "src/xenia/cpu/ppc/testing/",
|
||||
"Directory scanned for test files.", "Other");
|
||||
DEFINE_string(test_bin_path, "src/xenia/cpu/ppc/testing/bin/",
|
||||
"Directory with binary outputs of the test files.", "Other");
|
||||
DEFINE_path(test_path, "src/xenia/cpu/ppc/testing/",
|
||||
"Directory scanned for test files.", "Other");
|
||||
DEFINE_path(test_bin_path, "src/xenia/cpu/ppc/testing/bin/",
|
||||
"Directory with binary outputs of the test files.", "Other");
|
||||
DEFINE_transient_string(test_name, "", "Specifies test name.", "General");
|
||||
|
||||
namespace xe {
|
||||
@@ -49,43 +49,45 @@ struct TestCase {
|
||||
|
||||
class TestSuite {
|
||||
public:
|
||||
TestSuite(const std::wstring& src_file_path) : src_file_path(src_file_path) {
|
||||
name = src_file_path.substr(src_file_path.find_last_of(xe::kPathSeparator) +
|
||||
1);
|
||||
name = ReplaceExtension(name, L"");
|
||||
map_file_path = xe::to_wstring(cvars::test_bin_path) + name + L".map";
|
||||
bin_file_path = xe::to_wstring(cvars::test_bin_path) + name + L".bin";
|
||||
TestSuite(const std::filesystem::path& src_file_path)
|
||||
: src_file_path_(src_file_path) {
|
||||
auto name = src_file_path.filename();
|
||||
name = name.replace_extension();
|
||||
|
||||
name_ = xe::path_to_utf8(name);
|
||||
map_file_path_ = cvars::test_bin_path / name.replace_extension(".map");
|
||||
bin_file_path_ = cvars::test_bin_path / name.replace_extension(".bin");
|
||||
}
|
||||
|
||||
bool Load() {
|
||||
if (!ReadMap(map_file_path)) {
|
||||
XELOGE("Unable to read map for test %ls", src_file_path.c_str());
|
||||
if (!ReadMap()) {
|
||||
XELOGE("Unable to read map for test %s",
|
||||
xe::path_to_utf8(src_file_path_).c_str());
|
||||
return false;
|
||||
}
|
||||
if (!ReadAnnotations(src_file_path)) {
|
||||
XELOGE("Unable to read annotations for test %ls", src_file_path.c_str());
|
||||
if (!ReadAnnotations()) {
|
||||
XELOGE("Unable to read annotations for test %s",
|
||||
xe::path_to_utf8(src_file_path_).c_str());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::wstring name;
|
||||
std::wstring src_file_path;
|
||||
std::wstring map_file_path;
|
||||
std::wstring bin_file_path;
|
||||
std::vector<TestCase> test_cases;
|
||||
const std::string& name() const { return name_; }
|
||||
const std::filesystem::path& src_file_path() const { return src_file_path_; }
|
||||
const std::filesystem::path& map_file_path() const { return map_file_path_; }
|
||||
const std::filesystem::path& bin_file_path() const { return bin_file_path_; }
|
||||
std::vector<TestCase>& test_cases() { return test_cases_; }
|
||||
|
||||
private:
|
||||
std::wstring ReplaceExtension(const std::wstring& path,
|
||||
const std::wstring& new_extension) {
|
||||
std::wstring result = path;
|
||||
auto last_dot = result.find_last_of('.');
|
||||
result.replace(result.begin() + last_dot, result.end(), new_extension);
|
||||
return result;
|
||||
}
|
||||
std::string name_;
|
||||
std::filesystem::path src_file_path_;
|
||||
std::filesystem::path map_file_path_;
|
||||
std::filesystem::path bin_file_path_;
|
||||
std::vector<TestCase> test_cases_;
|
||||
|
||||
TestCase* FindTestCase(const std::string& name) {
|
||||
for (auto& test_case : test_cases) {
|
||||
TestCase* FindTestCase(const std::string_view name) {
|
||||
for (auto& test_case : test_cases_) {
|
||||
if (test_case.name == name) {
|
||||
return &test_case;
|
||||
}
|
||||
@@ -93,8 +95,8 @@ class TestSuite {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ReadMap(const std::wstring& map_file_path) {
|
||||
FILE* f = fopen(xe::to_string(map_file_path).c_str(), "r");
|
||||
bool ReadMap() {
|
||||
FILE* f = filesystem::OpenFile(map_file_path_, "r");
|
||||
if (!f) {
|
||||
return false;
|
||||
}
|
||||
@@ -114,15 +116,16 @@ class TestSuite {
|
||||
}
|
||||
std::string address(line_buffer, t_test_ - line_buffer);
|
||||
std::string name(t_test_ + strlen(" t test_"));
|
||||
test_cases.emplace_back(START_ADDRESS + std::stoul(address, 0, 16), name);
|
||||
test_cases_.emplace_back(START_ADDRESS + std::stoul(address, 0, 16),
|
||||
name);
|
||||
}
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReadAnnotations(const std::wstring& src_file_path) {
|
||||
bool ReadAnnotations() {
|
||||
TestCase* current_test_case = nullptr;
|
||||
FILE* f = fopen(xe::to_string(src_file_path).c_str(), "r");
|
||||
FILE* f = filesystem::OpenFile(src_file_path_, "r");
|
||||
if (!f) {
|
||||
return false;
|
||||
}
|
||||
@@ -141,8 +144,8 @@ class TestSuite {
|
||||
std::string label(start + strlen("test_"), strchr(start, ':'));
|
||||
current_test_case = FindTestCase(label);
|
||||
if (!current_test_case) {
|
||||
XELOGE("Test case %s not found in corresponding map for %ls",
|
||||
label.c_str(), src_file_path.c_str());
|
||||
XELOGE("Test case %s not found in corresponding map for %s",
|
||||
label.c_str(), xe::path_to_utf8(src_file_path_).c_str());
|
||||
return false;
|
||||
}
|
||||
} else if (strlen(start) > 3 && start[0] == '#' && start[1] == '_') {
|
||||
@@ -157,8 +160,8 @@ class TestSuite {
|
||||
value.erase(value.end() - 1);
|
||||
}
|
||||
if (!current_test_case) {
|
||||
XELOGE("Annotation outside of test case in %ls",
|
||||
src_file_path.c_str());
|
||||
XELOGE("Annotation outside of test case in %s",
|
||||
xe::path_to_utf8(src_file_path_).c_str());
|
||||
return false;
|
||||
}
|
||||
current_test_case->annotations.emplace_back(key, value);
|
||||
@@ -172,21 +175,20 @@ class TestSuite {
|
||||
|
||||
class TestRunner {
|
||||
public:
|
||||
TestRunner() {
|
||||
memory_size = 64 * 1024 * 1024;
|
||||
memory.reset(new Memory());
|
||||
memory->Initialize();
|
||||
TestRunner() : memory_size_(64 * 1024 * 1024) {
|
||||
memory_.reset(new Memory());
|
||||
memory_->Initialize();
|
||||
}
|
||||
|
||||
~TestRunner() {
|
||||
thread_state.reset();
|
||||
processor.reset();
|
||||
memory.reset();
|
||||
thread_state_.reset();
|
||||
processor_.reset();
|
||||
memory_.reset();
|
||||
}
|
||||
|
||||
bool Setup(TestSuite& suite) {
|
||||
// Reset memory.
|
||||
memory->Reset();
|
||||
memory_->Reset();
|
||||
|
||||
std::unique_ptr<xe::cpu::backend::Backend> backend;
|
||||
if (!backend) {
|
||||
@@ -205,23 +207,24 @@ class TestRunner {
|
||||
}
|
||||
|
||||
// Setup a fresh processor.
|
||||
processor.reset(new Processor(memory.get(), nullptr));
|
||||
processor->Setup(std::move(backend));
|
||||
processor->set_debug_info_flags(DebugInfoFlags::kDebugInfoAll);
|
||||
processor_.reset(new Processor(memory_.get(), nullptr));
|
||||
processor_->Setup(std::move(backend));
|
||||
processor_->set_debug_info_flags(DebugInfoFlags::kDebugInfoAll);
|
||||
|
||||
// Load the binary module.
|
||||
auto module = std::make_unique<xe::cpu::RawModule>(processor.get());
|
||||
if (!module->LoadFile(START_ADDRESS, suite.bin_file_path)) {
|
||||
XELOGE("Unable to load test binary %ls", suite.bin_file_path.c_str());
|
||||
auto module = std::make_unique<xe::cpu::RawModule>(processor_.get());
|
||||
if (!module->LoadFile(START_ADDRESS, suite.bin_file_path())) {
|
||||
XELOGE("Unable to load test binary %s",
|
||||
xe::path_to_utf8(suite.bin_file_path).c_str());
|
||||
return false;
|
||||
}
|
||||
processor->AddModule(std::move(module));
|
||||
processor_->AddModule(std::move(module));
|
||||
|
||||
processor->backend()->CommitExecutableRange(START_ADDRESS,
|
||||
START_ADDRESS + 1024 * 1024);
|
||||
processor_->backend()->CommitExecutableRange(START_ADDRESS,
|
||||
START_ADDRESS + 1024 * 1024);
|
||||
|
||||
// Add dummy space for memory.
|
||||
processor->memory()->LookupHeap(0)->AllocFixed(
|
||||
processor_->memory()->LookupHeap(0)->AllocFixed(
|
||||
0x10001000, 0xEFFF, 0,
|
||||
kMemoryAllocationReserve | kMemoryAllocationCommit,
|
||||
kMemoryProtectRead | kMemoryProtectWrite);
|
||||
@@ -230,8 +233,8 @@ class TestRunner {
|
||||
uint32_t stack_size = 64 * 1024;
|
||||
uint32_t stack_address = START_ADDRESS - stack_size;
|
||||
uint32_t pcr_address = stack_address - 0x1000;
|
||||
thread_state.reset(
|
||||
new ThreadState(processor.get(), 0x100, stack_address, pcr_address));
|
||||
thread_state_.reset(
|
||||
new ThreadState(processor_.get(), 0x100, stack_address, pcr_address));
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -244,15 +247,15 @@ class TestRunner {
|
||||
}
|
||||
|
||||
// Execute test.
|
||||
auto fn = processor->ResolveFunction(test_case.address);
|
||||
auto fn = processor_->ResolveFunction(test_case.address);
|
||||
if (!fn) {
|
||||
XELOGE("Entry function not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto ctx = thread_state->context();
|
||||
auto ctx = thread_state_->context();
|
||||
ctx->lr = 0xBCBCBCBC;
|
||||
fn->Call(thread_state.get(), uint32_t(ctx->lr));
|
||||
fn->Call(thread_state_.get(), uint32_t(ctx->lr));
|
||||
|
||||
// Assert test state expectations.
|
||||
bool result = CheckTestResults(test_case);
|
||||
@@ -267,7 +270,7 @@ class TestRunner {
|
||||
}
|
||||
|
||||
bool SetupTestState(TestCase& test_case) {
|
||||
auto ppc_context = thread_state->context();
|
||||
auto ppc_context = thread_state_->context();
|
||||
for (auto& it : test_case.annotations) {
|
||||
if (it.first == "REGISTER_IN") {
|
||||
size_t space_pos = it.second.find(" ");
|
||||
@@ -279,7 +282,7 @@ class TestRunner {
|
||||
auto address_str = it.second.substr(0, space_pos);
|
||||
auto bytes_str = it.second.substr(space_pos + 1);
|
||||
uint32_t address = std::strtoul(address_str.c_str(), nullptr, 16);
|
||||
auto p = memory->TranslateVirtual(address);
|
||||
auto p = memory_->TranslateVirtual(address);
|
||||
const char* c = bytes_str.c_str();
|
||||
while (*c) {
|
||||
while (*c == ' ') ++c;
|
||||
@@ -298,9 +301,7 @@ class TestRunner {
|
||||
}
|
||||
|
||||
bool CheckTestResults(TestCase& test_case) {
|
||||
auto ppc_context = thread_state->context();
|
||||
|
||||
char actual_value[2048];
|
||||
auto ppc_context = thread_state_->context();
|
||||
|
||||
bool any_failed = false;
|
||||
for (auto& it : test_case.annotations) {
|
||||
@@ -308,9 +309,9 @@ class TestRunner {
|
||||
size_t space_pos = it.second.find(" ");
|
||||
auto reg_name = it.second.substr(0, space_pos);
|
||||
auto reg_value = it.second.substr(space_pos + 1);
|
||||
if (!ppc_context->CompareRegWithString(reg_name.c_str(),
|
||||
reg_value.c_str(), actual_value,
|
||||
xe::countof(actual_value))) {
|
||||
std::string actual_value;
|
||||
if (!ppc_context->CompareRegWithString(
|
||||
reg_name.c_str(), reg_value.c_str(), actual_value)) {
|
||||
any_failed = true;
|
||||
XELOGE("Register %s assert failed:\n", reg_name.c_str());
|
||||
XELOGE(" Expected: %s == %s\n", reg_name.c_str(), reg_value.c_str());
|
||||
@@ -321,7 +322,7 @@ class TestRunner {
|
||||
auto address_str = it.second.substr(0, space_pos);
|
||||
auto bytes_str = it.second.substr(space_pos + 1);
|
||||
uint32_t address = std::strtoul(address_str.c_str(), nullptr, 16);
|
||||
auto base_address = memory->TranslateVirtual(address);
|
||||
auto base_address = memory_->TranslateVirtual(address);
|
||||
auto p = base_address;
|
||||
const char* c = bytes_str.c_str();
|
||||
while (*c) {
|
||||
@@ -348,19 +349,18 @@ class TestRunner {
|
||||
return !any_failed;
|
||||
}
|
||||
|
||||
size_t memory_size;
|
||||
std::unique_ptr<Memory> memory;
|
||||
std::unique_ptr<Processor> processor;
|
||||
std::unique_ptr<ThreadState> thread_state;
|
||||
size_t memory_size_;
|
||||
std::unique_ptr<Memory> memory_;
|
||||
std::unique_ptr<Processor> processor_;
|
||||
std::unique_ptr<ThreadState> thread_state_;
|
||||
};
|
||||
|
||||
bool DiscoverTests(std::wstring& test_path,
|
||||
std::vector<std::wstring>& test_files) {
|
||||
bool DiscoverTests(const std::filesystem::path& test_path,
|
||||
std::vector<std::filesystem::path>& test_files) {
|
||||
auto file_infos = xe::filesystem::ListFiles(test_path);
|
||||
for (auto& file_info : file_infos) {
|
||||
if (file_info.name != L"." && file_info.name != L".." &&
|
||||
file_info.name.rfind(L".s") == file_info.name.size() - 2) {
|
||||
test_files.push_back(xe::join_paths(test_path, file_info.name));
|
||||
if (file_info.name.extension() == ".s") {
|
||||
test_files.push_back(test_path / file_info.name);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -401,14 +401,16 @@ void ProtectedRunTest(TestSuite& test_suite, TestRunner& runner,
|
||||
#endif // XE_COMPILER_MSVC
|
||||
}
|
||||
|
||||
bool RunTests(const std::wstring& test_name) {
|
||||
bool RunTests(const std::string_view test_name) {
|
||||
int result_code = 1;
|
||||
int failed_count = 0;
|
||||
int passed_count = 0;
|
||||
|
||||
auto test_path_root =
|
||||
xe::fix_path_separators(xe::to_wstring(cvars::test_path));
|
||||
std::vector<std::wstring> test_files;
|
||||
XELOGI("Haswell instruction usage {}.",
|
||||
cvars::use_haswell_instructions ? "enabled" : "disabled");
|
||||
|
||||
auto test_path_root = cvars::test_path;
|
||||
std::vector<std::filesystem::path> test_files;
|
||||
if (!DiscoverTests(test_path_root, test_files)) {
|
||||
return false;
|
||||
}
|
||||
@@ -423,11 +425,12 @@ bool RunTests(const std::wstring& test_name) {
|
||||
bool load_failed = false;
|
||||
for (auto& test_path : test_files) {
|
||||
TestSuite test_suite(test_path);
|
||||
if (!test_name.empty() && test_suite.name != test_name) {
|
||||
if (!test_name.empty() && test_suite.name() != test_name) {
|
||||
continue;
|
||||
}
|
||||
if (!test_suite.Load()) {
|
||||
XELOGE("TEST SUITE %ls FAILED TO LOAD", test_path.c_str());
|
||||
XELOGE("TEST SUITE %s FAILED TO LOAD",
|
||||
xe::path_to_utf8(test_path).c_str());
|
||||
load_failed = true;
|
||||
continue;
|
||||
}
|
||||
@@ -440,9 +443,9 @@ bool RunTests(const std::wstring& test_name) {
|
||||
XELOGI("%d tests loaded.", (int)test_suites.size());
|
||||
TestRunner runner;
|
||||
for (auto& test_suite : test_suites) {
|
||||
XELOGI("%ls.s:", test_suite.name.c_str());
|
||||
XELOGI("%s.s:", xe::path_to_utf8(test_suite.name()).c_str());
|
||||
|
||||
for (auto& test_case : test_suite.test_cases) {
|
||||
for (auto& test_case : test_suite.test_cases()) {
|
||||
XELOGI(" - %s", test_case.name.c_str());
|
||||
ProtectedRunTest(test_suite, runner, test_case, failed_count,
|
||||
passed_count);
|
||||
@@ -459,9 +462,9 @@ bool RunTests(const std::wstring& test_name) {
|
||||
return failed_count ? false : true;
|
||||
}
|
||||
|
||||
int main(const std::vector<std::wstring>& args) {
|
||||
int main(const std::vector<std::string>& args) {
|
||||
// Grab test name, if present.
|
||||
std::wstring test_name;
|
||||
std::string test_name;
|
||||
if (args.size() >= 2) {
|
||||
test_name = args[1];
|
||||
}
|
||||
@@ -473,5 +476,5 @@ int main(const std::vector<std::wstring>& args) {
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
DEFINE_ENTRY_POINT(L"xenia-cpu-ppc-test", xe::cpu::test::main, "[test name]",
|
||||
DEFINE_ENTRY_POINT("xenia-cpu-ppc-test", xe::cpu::test::main, "[test name]",
|
||||
"test_name");
|
||||
|
||||
Reference in New Issue
Block a user