Files
SourMesen 7f418e352a Clang: Fixed all compilation warnings (#241)
-Some warnings were suppressed (undefined-inline and return-type-c-linkage)
-Warnings as errors was enabled to make CI builds fail when warnings are introduced
-Slightly altered the behavior of SnesCpu::IdleOrRead, but this should match what ares does
2026-08-01 13:40:07 +09:00

182 lines
5.4 KiB
C++

#include "Common.h"
#include "Core/Shared/RecordedRomTest.h"
#include "Core/Shared/Emulator.h"
#include "Core/Shared/EmuSettings.h"
#include "Utilities/FolderUtilities.h"
#include "Utilities/StringUtilities.h"
extern unique_ptr<Emulator> _emu;
shared_ptr<RecordedRomTest> _recordedRomTest;
extern "C"
{
DllExport RomTestResult __stdcall RunRecordedTest(const char* filename, bool inBackground)
{
if(inBackground) {
unique_ptr<Emulator> emu(new Emulator());
emu->Initialize(false);
emu->GetSettings()->SetFlag(EmulationFlags::TestMode);
shared_ptr<RecordedRomTest> romTest(new RecordedRomTest(emu.get(), true));
RomTestResult result = romTest->Run(filename);
emu->Release();
return result;
} else {
shared_ptr<RecordedRomTest> romTest(new RecordedRomTest(_emu.get(), false));
return romTest->Run(filename);
}
}
DllExport uint64_t __stdcall RunTest(char* filename, uint32_t address, MemoryType memType)
{
unique_ptr<Emulator> emu(new Emulator());
emu->Initialize();
emu->GetSettings()->SetFlag(EmulationFlags::TestMode);
emu->GetSettings()->GetGameboyConfig().Model = GameboyModel::Gameboy;
emu->GetSettings()->GetGameboyConfig().RamPowerOnState = RamState::AllZeros;
emu->LoadRom((VirtualFile)filename, VirtualFile());
emu->GetSettings()->SetFlag(EmulationFlags::MaximumSpeed);
while(emu->GetFrameCount() < 500) {
std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(10));
}
ConsoleMemoryInfo memInfo = emu->GetMemory(memType);
uint8_t* memBuffer = (uint8_t*)memInfo.Memory;
uint64_t result = memBuffer[address];
for(int i = 1; i < 8; i++) {
if(address + i < memInfo.Size) {
result |= ((uint64_t)memBuffer[address + i] << (8 * i));
} else {
break;
}
}
emu->Stop(false);
emu->Release();
return result;
}
DllExport void __stdcall RomTestRecord(char* filename, bool reset)
{
_recordedRomTest.reset(new RecordedRomTest(_emu.get(), false));
_recordedRomTest->Record(filename, reset);
}
DllExport void __stdcall RomTestStop()
{
if(_recordedRomTest) {
_recordedRomTest->Stop();
_recordedRomTest.reset();
}
}
DllExport bool __stdcall RomTestRecording()
{
return _recordedRomTest != nullptr;
}
DllExport bool __stdcall RunCiTests(string testFolder, bool enableDebugger)
{
std::replace(testFolder.begin(), testFolder.end(), '\\', '/');
if(!StringUtilities::EndsWith(testFolder, "/")) {
testFolder += "/";
}
//GBA tests can't run without the GBA BIOS, skip all of them
vector<string> foldersToSkip = { "GBA/" };
unordered_set<string> testsToSkip = {
//These GB tests fail because the test runner can't use the official GB boot rom
"GB/GBEmulatorShootout/hacktix/bully_gbc.mtp",
"GB/GBEmulatorShootout/hacktix/bully.mtp",
"GB/GBEmulatorShootout/mooneye/acceptance/boot_div-dmgABCmgb.mtp",
"GB/GBEmulatorShootout/mooneye/acceptance/boot_hwio-dmgABCmgb.mtp",
"GB/GBEmulatorShootout/mooneye/acceptance/serial/boot_sclk_align-dmgABCmgb.mtp",
"GB/GBEmulatorShootout/mooneye/misc/boot_div-cgbABCDE.mtp",
//SGB tests can't run properly without the SGB ROM
"SNES/sgb_packet_test.mtp",
"SNES/sgb-ext-test.mtp",
"SNES/sgb-mlt-test.mtp"
};
vector<string> tests = FolderUtilities::GetFilesInFolder(testFolder, { ".mtp" }, true, 5);
vector<string> testsToRun;
for(string& test : tests) {
string testPath = test.substr(testFolder.size());
std::replace(testPath.begin(), testPath.end(), '\\', '/');
bool include = true;
for(string& folderToSkip : foldersToSkip) {
if(StringUtilities::StartsWith(testPath, folderToSkip.c_str())) {
include = false;
break;
}
}
if(testsToSkip.find(testPath) != testsToSkip.end()) {
include = false;
}
if(include) {
testsToRun.push_back(test);
}
}
FolderUtilities::SetHomeFolder(FolderUtilities::CombinePath(testFolder, "MesenHomeFolder"));
std::cout << "== CI test mode ==\n";
int threadCount = std::thread::hardware_concurrency();
int skipCount = (int)(tests.size() - testsToRun.size());
std::cout << "Running on " << threadCount << " threads\n";
std::cout << "Test count: " << tests.size() << "\n";
std::cout << "Tests to skip: " << skipCount << "\n\n";
std::atomic<int> testNumber = 0;
std::atomic<int> failCount = 0;
std::atomic<int> passCount = 0;
std::atomic<int> progress = 0;
vector<thread> threads(threadCount);
for(int i = 0; i < threadCount; i++) {
threads[i] = std::thread([&]() {
while(true) {
int nextTest = testNumber++;
if(nextTest >= testsToRun.size()) {
break;
}
RomTestResult result = RunRecordedTest(testsToRun[nextTest].c_str(), true);
if(result.State == RomTestState::Failed) {
failCount++;
string testPath = testsToRun[nextTest].substr(testFolder.size());
std::cout << ("\n== FAILED: " + testPath + "\n");
} else {
passCount++;
}
int newProgress = (failCount + passCount) * 100 / (uint32_t)testsToRun.size();
if(newProgress > progress) {
std::cout << ("\rRunning... (" + std::to_string(newProgress) + "%)");
progress = newProgress;
}
}
});
}
for(int i = 0; i < threadCount; i++) {
threads[i].join();
}
std::cout << "\n\n===================\n";
std::cout << "Passed: " << passCount << "\n";
std::cout << "Failed: " << failCount << "\n";
std::cout << "Skipped: " << skipCount << "\n";
std::cout << "===================\n";
return failCount == 0;
}
}