Added support for automated testing

This commit is contained in:
Sour 2019-10-16 20:22:45 -04:00
parent accac0f21e
commit 207282ed1e
26 changed files with 642 additions and 56 deletions

View file

@ -30,6 +30,7 @@
#include "MovieManager.h"
#include "BatteryManager.h"
#include "CheatManager.h"
#include "MovieManager.h"
#include "../Utilities/Serializer.h"
#include "../Utilities/Timer.h"
#include "../Utilities/VirtualFile.h"
@ -39,7 +40,6 @@
Console::Console()
{
_settings.reset(new EmuSettings());
KeyManager::SetSettings(_settings.get());
_paused = false;
_pauseOnNextFrame = false;
@ -63,6 +63,7 @@ void Console::Initialize()
_soundMixer.reset(new SoundMixer(this));
_debugHud.reset(new DebugHud());
_cheatManager.reset(new CheatManager(this));
_movieManager.reset(new MovieManager(shared_from_this()));
_videoDecoder->StartThread();
_videoRenderer->StartThread();
@ -82,6 +83,8 @@ void Console::Release()
_saveStateManager.reset();
_soundMixer.reset();
_settings.reset();
_cheatManager.reset();
_movieManager.reset();
}
void Console::Run()
@ -106,6 +109,7 @@ void Console::Run()
_emulationThreadId = std::this_thread::get_id();
_memoryManager->IncMasterClockStartup();
_controlManager->UpdateInputState();
auto lock = _runLock.AcquireSafe();
while(!_stopFlag) {
@ -136,8 +140,6 @@ void Console::Run()
frameLimiter.ProcessFrame();
frameLimiter.WaitForNextFrame();
_controlManager->UpdateControlDevices();
double newFrameDelay = GetFrameDelay();
if(newFrameDelay != frameDelay) {
frameDelay = newFrameDelay;
@ -152,13 +154,14 @@ void Console::Run()
}
_controlManager->UpdateInputState();
_controlManager->UpdateControlDevices();
_internalRegisters->ProcessAutoJoypadRead();
previousFrameCount = _ppu->GetFrameCount();
}
}
MovieManager::Stop();
_movieManager->Stop();
_emulationThreadId = thread::id();
@ -254,7 +257,7 @@ void Console::Reset()
ProcessEvent(EventType::Reset);
_memoryManager->IncMasterClockStartup();
Unlock();
if(debugger) {
@ -584,6 +587,11 @@ shared_ptr<CheatManager> Console::GetCheatManager()
return _cheatManager;
}
shared_ptr<MovieManager> Console::GetMovieManager()
{
return _movieManager;
}
shared_ptr<Cpu> Console::GetCpu()
{
return _cpu;

View file

@ -25,6 +25,7 @@ class SaveStateManager;
class RewindManager;
class BatteryManager;
class CheatManager;
class MovieManager;
enum class MemoryOperationType;
enum class SnesMemoryType;
enum class EventType;
@ -54,7 +55,8 @@ private:
shared_ptr<SaveStateManager> _saveStateManager;
shared_ptr<RewindManager> _rewindManager;
shared_ptr<CheatManager> _cheatManager;
shared_ptr<MovieManager> _movieManager;
thread::id _emulationThreadId;
atomic<uint32_t> _lockCounter;
@ -116,6 +118,7 @@ public:
shared_ptr<DebugHud> GetDebugHud();
shared_ptr<BatteryManager> GetBatteryManager();
shared_ptr<CheatManager> GetCheatManager();
shared_ptr<MovieManager> GetMovieManager();
shared_ptr<Cpu> GetCpu();
shared_ptr<Ppu> GetPpu();

View file

@ -143,7 +143,7 @@ void ControlManager::UpdateInputState()
auto lock = _deviceLock.AcquireSafe();
string log = "";
//string log = "F: " + std::to_string(_console->GetPpu()->GetFrameCount()) + " C:" + std::to_string(_pollCounter) + " ";
for(shared_ptr<BaseControlDevice> &device : _controlDevices) {
device->ClearState();

View file

@ -110,6 +110,7 @@
<ClInclude Include="NtscFilter.h" />
<ClInclude Include="Obc1.h" />
<ClInclude Include="PpuTools.h" />
<ClInclude Include="RecordedRomTest.h" />
<ClInclude Include="RegisterHandlerB.h" />
<ClInclude Include="CpuTypes.h" />
<ClInclude Include="Debugger.h" />
@ -239,6 +240,7 @@
<ClCompile Include="Obc1.cpp" />
<ClCompile Include="Ppu.cpp" />
<ClCompile Include="PpuTools.cpp" />
<ClCompile Include="RecordedRomTest.cpp" />
<ClCompile Include="RegisterHandlerB.cpp" />
<ClCompile Include="RewindData.cpp" />
<ClCompile Include="RewindManager.cpp" />

View file

@ -410,6 +410,9 @@
<ClInclude Include="CheatManager.h">
<Filter>Misc</Filter>
</ClInclude>
<ClInclude Include="RecordedRomTest.h">
<Filter>Misc</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp" />
@ -665,6 +668,9 @@
<ClCompile Include="SnesController.cpp">
<Filter>SNES\Input</Filter>
</ClCompile>
<ClCompile Include="RecordedRomTest.cpp">
<Filter>Misc</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="SNES">

View file

@ -16,9 +16,10 @@
#include "BatteryManager.h"
#include "CheatManager.h"
MesenMovie::MesenMovie(shared_ptr<Console> console)
MesenMovie::MesenMovie(shared_ptr<Console> console, bool forTest)
{
_console = console;
_forTest = forTest;
}
MesenMovie::~MesenMovie()
@ -29,7 +30,9 @@ MesenMovie::~MesenMovie()
void MesenMovie::Stop()
{
if(_playing) {
MessageManager::DisplayMessage("Movies", "MovieEnded");
if(!_forTest) {
MessageManager::DisplayMessage("Movies", "MovieEnded");
}
if(_console->GetSettings()->GetPreferences().PauseOnMovieEnd) {
_console->Pause();
@ -56,7 +59,7 @@ bool MesenMovie::SetInput(BaseControlDevice *device)
_deviceIndex = 0;
}
} else {
MovieManager::Stop();
_console->GetMovieManager()->Stop();
}
return true;
}
@ -73,11 +76,11 @@ vector<uint8_t> MesenMovie::LoadBattery(string extension)
return batteryData;
}
void MesenMovie::ProcessNotification(ConsoleNotificationType type, void * parameter)
void MesenMovie::ProcessNotification(ConsoleNotificationType type, void* parameter)
{
if(type == ConsoleNotificationType::GameLoaded) {
_console->GetControlManager()->RegisterInputProvider(this);
_console->GetControlManager()->SetPollCounter(_lastPollCounter + 1);
_console->GetControlManager()->SetPollCounter(_lastPollCounter);
}
}
@ -140,7 +143,14 @@ bool MesenMovie::Play(VirtualFile &file)
}*/
_originalCheats = _console->GetCheatManager()->GetCheats();
_console->PowerCycle();
controlManager->UpdateControlDevices();
if(!_forTest) {
_console->PowerCycle();
} else {
controlManager->RegisterInputProvider(this);
}
LoadCheats();
stringstream saveStateData;
@ -239,7 +249,9 @@ void MesenMovie::ApplySettings()
inputConfig.Controllers[4].Type = FromString(LoadString(_settings, MovieKeys::Controller5), ControllerTypeNames, ControllerType::None);
emuConfig.Region = FromString(LoadString(_settings, MovieKeys::Region), ConsoleRegionNames, ConsoleRegion::Ntsc);
emuConfig.RamPowerOnState = FromString(LoadString(_settings, MovieKeys::RamPowerOnState), RamStateNames, RamState::AllOnes);
if(!_forTest) {
emuConfig.RamPowerOnState = FromString(LoadString(_settings, MovieKeys::RamPowerOnState), RamStateNames, RamState::AllOnes);
}
emuConfig.PpuExtraScanlinesAfterNmi = LoadInt(_settings, MovieKeys::ExtraScanlinesAfterNmi);
emuConfig.PpuExtraScanlinesBeforeNmi = LoadInt(_settings, MovieKeys::ExtraScanlinesBeforeNmi);

View file

@ -25,6 +25,7 @@ private:
vector<CheatCode> _originalCheats;
std::unordered_map<string, string> _settings;
string _filename;
bool _forTest;
private:
void ParseSettings(stringstream &data);
@ -40,7 +41,7 @@ private:
bool LoadCheat(string cheatData, CheatCode &code);
public:
MesenMovie(shared_ptr<Console> console);
MesenMovie(shared_ptr<Console> console, bool silent);
virtual ~MesenMovie();
bool Play(VirtualFile &file) override;

View file

@ -7,18 +7,20 @@
#include "MesenMovie.h"
#include "MovieRecorder.h"
shared_ptr<IMovie> MovieManager::_player;
shared_ptr<MovieRecorder> MovieManager::_recorder;
void MovieManager::Record(RecordMovieOptions options, shared_ptr<Console> console)
MovieManager::MovieManager(shared_ptr<Console> console)
{
shared_ptr<MovieRecorder> recorder(new MovieRecorder(console));
_console = console;
}
void MovieManager::Record(RecordMovieOptions options)
{
shared_ptr<MovieRecorder> recorder(new MovieRecorder(_console));
if(recorder->Record(options)) {
_recorder = recorder;
}
}
void MovieManager::Play(VirtualFile file, shared_ptr<Console> console)
void MovieManager::Play(VirtualFile file, bool forTest)
{
vector<uint8_t> fileData;
if(file.IsValid() && file.ReadFile(fileData)) {
@ -30,13 +32,15 @@ void MovieManager::Play(VirtualFile file, shared_ptr<Console> console)
vector<string> files = reader.GetFileList();
if(std::find(files.begin(), files.end(), "GameSettings.txt") != files.end()) {
player.reset(new MesenMovie(console));
player.reset(new MesenMovie(_console, forTest));
}
}
if(player && player->Play(file)) {
_player = player;
MessageManager::DisplayMessage("Movies", "MoviePlaying", file.GetFileName());
if(!forTest) {
MessageManager::DisplayMessage("Movies", "MoviePlaying", file.GetFileName());
}
}
}
}

View file

@ -18,13 +18,16 @@ public:
class MovieManager
{
private:
static shared_ptr<IMovie> _player;
static shared_ptr<MovieRecorder> _recorder;
shared_ptr<Console> _console;
shared_ptr<IMovie> _player;
shared_ptr<MovieRecorder> _recorder;
public:
static void Record(RecordMovieOptions options, shared_ptr<Console> console);
static void Play(VirtualFile file, shared_ptr<Console> console);
static void Stop();
static bool Playing();
static bool Recording();
MovieManager(shared_ptr<Console> console);
void Record(RecordMovieOptions options);
void Play(VirtualFile file, bool silent = false);
void Stop();
bool Playing();
bool Recording();
};

View file

@ -1500,12 +1500,6 @@ void Ppu::ProcessWindowMaskSettings(uint8_t value, uint8_t offset)
void Ppu::SendFrame()
{
_console->GetNotificationManager()->SendNotification(ConsoleNotificationType::PpuFrameDone);
if(_skipRender) {
return;
}
uint16_t width = _useHighResOutput ? 512 : 256;
uint16_t height = _useHighResOutput ? 478 : 239;
@ -1517,6 +1511,12 @@ void Ppu::SendFrame()
memset(_currentBuffer + width * (height - bottom), 0, width * bottom * sizeof(uint16_t));
}
_console->GetNotificationManager()->SendNotification(ConsoleNotificationType::PpuFrameDone);
if(_skipRender) {
return;
}
bool isRewinding = _console->GetRewindManager()->IsRewinding();
#ifdef LIBRETRO
_console->GetVideoDecoder()->UpdateFrameSync(_currentBuffer, width, height, _frameCount, isRewinding);

284
Core/RecordedRomTest.cpp Normal file
View file

@ -0,0 +1,284 @@
#include "stdafx.h"
#include "RecordedRomTest.h"
#include "Console.h"
#include "EmuSettings.h"
#include "MessageManager.h"
#include "Ppu.h"
#include "MovieManager.h"
#include "BaseCartridge.h"
#include "NotificationManager.h"
#include "../Utilities/VirtualFile.h"
#include "../Utilities/FolderUtilities.h"
#include "../Utilities/md5.h"
#include "../Utilities/ZipWriter.h"
#include "../Utilities/ZipReader.h"
#include "../Utilities/ArchiveReader.h"
RecordedRomTest::RecordedRomTest(shared_ptr<Console> console)
{
if(console) {
_console = console;
} else {
_console.reset(new Console());
_console->Initialize();
}
Reset();
}
RecordedRomTest::~RecordedRomTest()
{
Reset();
}
void RecordedRomTest::SaveFrame()
{
bool highRes = _ppu->IsHighResOutput();
uint16_t width = highRes ? 512 : 256;
uint16_t height = highRes ? 478 : 239;
uint16_t* ppuFrameBuffer = _ppu->GetScreenBuffer();
uint8_t md5Hash[16];
GetMd5Sum(md5Hash, ppuFrameBuffer, width*height*sizeof(uint16_t));
if(memcmp(_previousHash, md5Hash, 16) == 0 && _currentCount < 255) {
_currentCount++;
} else {
uint8_t* hash = new uint8_t[16];
memcpy(hash, md5Hash, 16);
_screenshotHashes.push_back(hash);
if(_currentCount > 0) {
_repetitionCount.push_back(_currentCount);
}
_currentCount = 1;
memcpy(_previousHash, md5Hash, 16);
_signal.Signal();
}
}
void RecordedRomTest::ValidateFrame()
{
bool highRes = _ppu->IsHighResOutput();
uint16_t width = highRes ? 512 : 256;
uint16_t height = highRes ? 478 : 239;
uint16_t* ppuFrameBuffer = _ppu->GetScreenBuffer();
uint8_t md5Hash[16];
GetMd5Sum(md5Hash, ppuFrameBuffer, width * height * sizeof(uint16_t));
if(_currentCount == 0) {
_currentCount = _repetitionCount.front();
_repetitionCount.pop_front();
_screenshotHashes.pop_front();
}
_currentCount--;
if(memcmp(_screenshotHashes.front(), md5Hash, 16) != 0) {
_badFrameCount++;
//_console->BreakIfDebugging();
}
if(_currentCount == 0 && _repetitionCount.empty()) {
//End of test
if(memcmp(_screenshotHashes.front(), md5Hash, 16) == 0) {
_badFrameCount = 0;
}
_runningTest = false;
_signal.Signal();
}
}
void RecordedRomTest::ProcessNotification(ConsoleNotificationType type, void* parameter)
{
switch(type) {
case ConsoleNotificationType::PpuFrameDone:
if(_recording) {
SaveFrame();
} else if(_runningTest) {
ValidateFrame();
}
break;
default:
break;
}
}
void RecordedRomTest::Reset()
{
memset(_previousHash, 0xFF, 16);
_currentCount = 0;
_repetitionCount.clear();
for(uint8_t* hash : _screenshotHashes) {
delete[] hash;
}
_screenshotHashes.clear();
_runningTest = false;
_recording = false;
_badFrameCount = 0;
}
void RecordedRomTest::Record(string filename, bool reset)
{
_console->GetNotificationManager()->RegisterNotificationListener(shared_from_this());
_filename = filename;
string mrtFilename = FolderUtilities::CombinePath(FolderUtilities::GetFolderName(filename), FolderUtilities::GetFilename(filename, false) + ".mrt");
_file.open(mrtFilename, ios::out | ios::binary);
if(_file) {
_console->Lock();
Reset();
VideoConfig videoCfg = _console->GetSettings()->GetVideoConfig();
videoCfg.DisableFrameSkipping = true;
_console->GetSettings()->SetVideoConfig(videoCfg);
EmulationConfig emuCfg = _console->GetSettings()->GetEmulationConfig();
emuCfg.RamPowerOnState = RamState::AllZeros;
_console->GetSettings()->SetEmulationConfig(emuCfg);
//Start recording movie alongside with screenshots
RecordMovieOptions options;
string movieFilename = FolderUtilities::CombinePath(FolderUtilities::GetFolderName(filename), FolderUtilities::GetFilename(filename, false) + ".mmo");
memcpy(options.Filename, movieFilename.c_str(), std::max(1000, (int)movieFilename.size()));
options.RecordFrom = reset ? RecordMovieFrom::StartWithSaveData : RecordMovieFrom::CurrentState;
_console->GetMovieManager()->Record(options);
_ppu = _console->GetPpu().get();
_recording = true;
_console->Unlock();
}
}
int32_t RecordedRomTest::Run(string filename)
{
_console->GetNotificationManager()->RegisterNotificationListener(shared_from_this());
EmuSettings* settings = _console->GetSettings().get();
string testName = FolderUtilities::GetFilename(filename, false);
VirtualFile testMovie(filename, "TestMovie.msm");
VirtualFile testRom(filename, "TestRom.sfc");
ZipReader zipReader;
zipReader.LoadArchive(filename);
stringstream testData;
zipReader.GetStream("TestData.mrt", testData);
if(testData && testMovie.IsValid() && testRom.IsValid()) {
char header[3];
testData.read((char*)&header, 3);
if(memcmp((char*)&header, "MRT", 3) != 0) {
//Invalid test file
return false;
}
Reset();
uint32_t hashCount;
testData.read((char*)&hashCount, sizeof(uint32_t));
for(uint32_t i = 0; i < hashCount; i++) {
uint8_t repeatCount = 0;
testData.read((char*)&repeatCount, sizeof(uint8_t));
_repetitionCount.push_back(repeatCount);
uint8_t* screenshotHash = new uint8_t[16];
testData.read((char*)screenshotHash, 16);
_screenshotHashes.push_back(screenshotHash);
}
_currentCount = _repetitionCount.front();
_repetitionCount.pop_front();
VideoConfig cfg = settings->GetVideoConfig();
cfg.DisableFrameSkipping = true;
settings->SetVideoConfig(cfg);
EmulationConfig emuCfg = _console->GetSettings()->GetEmulationConfig();
emuCfg.RamPowerOnState = RamState::AllZeros;
_console->GetSettings()->SetEmulationConfig(emuCfg);
//Start playing movie
if(_console->LoadRom(testRom, VirtualFile(""))) {
settings->SetFlag(EmulationFlags::MaximumSpeed);
_console->GetMovieManager()->Play(testMovie, true);
_ppu = _console->GetPpu().get();
_runningTest = true;
_runThread = std::thread([=]() {
_console->Run();
});
_signal.Wait();
_console->Stop(false);
//_console->Release();
_runThread.join();
_runningTest = false;
} else {
//Something went wrong when loading the rom
return -2;
}
settings->ClearFlag(EmulationFlags::MaximumSpeed);
return _badFrameCount;
}
return -1;
}
void RecordedRomTest::Stop()
{
if(_recording) {
Save();
}
Reset();
}
void RecordedRomTest::Save()
{
//Wait until the next frame is captured to end the recording
_signal.Wait();
_repetitionCount.push_back(_currentCount);
_recording = false;
//Stop playing/recording the movie
_console->GetMovieManager()->Stop();
_file.write("MRT", 3);
uint32_t hashCount = (uint32_t)_screenshotHashes.size();
_file.write((char*)&hashCount, sizeof(uint32_t));
for(uint32_t i = 0; i < hashCount; i++) {
_file.write((char*)&_repetitionCount[i], sizeof(uint8_t));
_file.write((char*)&_screenshotHashes[i][0], 16);
}
_file.close();
ZipWriter writer;
writer.Initialize(_filename);
string mrtFilename = FolderUtilities::CombinePath(FolderUtilities::GetFolderName(_filename), FolderUtilities::GetFilename(_filename, false) + ".mrt");
writer.AddFile(mrtFilename, "TestData.mrt");
std::remove(mrtFilename.c_str());
string mmoFilename = FolderUtilities::CombinePath(FolderUtilities::GetFolderName(_filename), FolderUtilities::GetFilename(_filename, false) + ".mmo");
writer.AddFile(mmoFilename, "TestMovie.msm");
std::remove(mmoFilename.c_str());
writer.AddFile(_console->GetCartridge()->GetRomInfo().RomFile.GetFilePath(), "TestRom.sfc");
writer.Save();
MessageManager::DisplayMessage("Test", "TestFileSavedTo", FolderUtilities::GetFilename(_filename, true));
}

47
Core/RecordedRomTest.h Normal file
View file

@ -0,0 +1,47 @@
#pragma once
#include "stdafx.h"
#include <deque>
#include "INotificationListener.h"
#include "../Utilities/AutoResetEvent.h"
class VirtualFile;
class Console;
class Ppu;
class RecordedRomTest : public INotificationListener, public std::enable_shared_from_this<RecordedRomTest>
{
private:
shared_ptr<Console> _console;
Ppu* _ppu;
bool _recording = false;
bool _runningTest = false;
int _badFrameCount = 0;
uint8_t _previousHash[16] = {};
std::deque<uint8_t*> _screenshotHashes;
std::deque<uint8_t> _repetitionCount;
uint8_t _currentCount = 0;
std::thread _runThread;
string _filename;
ofstream _file;
AutoResetEvent _signal;
private:
void Reset();
__declspec(noinline) void ValidateFrame();
void SaveFrame();
void Save();
public:
RecordedRomTest(shared_ptr<Console> console = nullptr);
virtual ~RecordedRomTest();
void ProcessNotification(ConsoleNotificationType type, void* parameter) override;
void Record(string filename, bool reset);
int32_t Run(string filename);
void Stop();
};

View file

@ -151,7 +151,7 @@ bool SaveStateManager::LoadState(istream &stream, bool hashCheckRequired)
//Stop any movie that might have been playing/recording if a state is loaded
//(Note: Loading a state is disabled in the UI while a movie is playing/recording)
MovieManager::Stop();
_console->GetMovieManager()->Stop();
_console->Deserialize(stream, fileFormatVersion);

View file

@ -266,7 +266,7 @@ struct EmulationConfig
uint32_t PpuExtraScanlinesBeforeNmi = 0;
uint32_t PpuExtraScanlinesAfterNmi = 0;
uint32_t GsuClockSpeed = 0;
uint32_t GsuClockSpeed = 100;
RamState RamPowerOnState = RamState::Random;
};

View file

@ -104,7 +104,7 @@ void ShortcutKeyHandler::CheckMappedKeys()
shared_ptr<EmuSettings> settings = _console->GetSettings();
bool isNetplayClient = false; //TODO GameClient::Connected();
//bool isMovieActive = MovieManager::Playing() || MovieManager::Recording();
bool isMovieRecording = MovieManager::Recording();
bool isMovieRecording = _console->GetMovieManager()->Recording();
//Let the UI handle these shortcuts
for(uint64_t i = (uint64_t)EmulatorShortcut::TakeScreenshot; i < (uint64_t)EmulatorShortcut::ShortcutCount; i++) {

View file

@ -79,7 +79,6 @@ public:
if(_console) {
if(IsPressed(SystemActionManager::Buttons::ResetButton)) {
_console->Reset();
_console->GetControlManager()->UpdateInputState();
}
if(IsPressed(SystemActionManager::Buttons::PowerButton)) {
_console->PowerCycle();

View file

@ -60,6 +60,7 @@ extern "C" {
DllExport void __stdcall InitDll()
{
_console.reset(new Console());
KeyManager::SetSettings(_console->GetSettings().get());
}
DllExport void __stdcall InitializeEmu(const char* homeFolder, void *windowHandle, void *viewerHandle, bool noAudio, bool noVideo, bool noInput)
@ -240,6 +241,7 @@ extern "C" {
std::cout << "Running: " << testRoms[i] << std::endl;
_console.reset(new Console());
KeyManager::SetSettings(_console->GetSettings().get());
_console->Initialize();
_console->LoadRom((VirtualFile)testRoms[i], VirtualFile());

View file

@ -470,6 +470,7 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='PGO Profile|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='PGO Optimize|x64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="TestApiWrapper.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">

View file

@ -40,5 +40,8 @@
<ClCompile Include="RecordApiWrapper.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="TestApiWrapper.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View file

@ -17,13 +17,13 @@ extern "C"
DllExport void __stdcall WaveStop() { _console->GetSoundMixer()->StopRecording(); }
DllExport bool __stdcall WaveIsRecording() { return _console->GetSoundMixer()->IsRecording(); }
DllExport void __stdcall MoviePlay(char* filename) { MovieManager::Play(string(filename), _console); }
DllExport void __stdcall MovieStop() { MovieManager::Stop(); }
DllExport bool __stdcall MoviePlaying() { return MovieManager::Playing(); }
DllExport bool __stdcall MovieRecording() { return MovieManager::Recording(); }
DllExport void __stdcall MoviePlay(char* filename) { _console->GetMovieManager()->Play(string(filename)); }
DllExport void __stdcall MovieStop() { _console->GetMovieManager()->Stop(); }
DllExport bool __stdcall MoviePlaying() { return _console->GetMovieManager()->Playing(); }
DllExport bool __stdcall MovieRecording() { return _console->GetMovieManager()->Recording(); }
DllExport void __stdcall MovieRecord(RecordMovieOptions *options)
{
RecordMovieOptions opt = *options;
MovieManager::Record(opt, _console);
_console->GetMovieManager()->Record(opt);
}
}

View file

@ -0,0 +1,31 @@
#include "stdafx.h"
#include "../Core/RecordedRomTest.h"
#include "../Core/Console.h"
extern shared_ptr<Console> _console;
shared_ptr<RecordedRomTest> _recordedRomTest;
extern "C"
{
DllExport int32_t __stdcall RunRecordedTest(char* filename, bool inBackground)
{
shared_ptr<RecordedRomTest> romTest(new RecordedRomTest(inBackground ? nullptr : _console));
return romTest->Run(filename);
}
DllExport void __stdcall RomTestRecord(char* filename, bool reset)
{
_recordedRomTest.reset(new RecordedRomTest(_console));
_recordedRomTest->Record(filename, reset);
}
DllExport void __stdcall RomTestStop()
{
if(_recordedRomTest) {
_recordedRomTest->Stop();
_recordedRomTest.reset();
}
}
DllExport bool __stdcall RomTestRecording() { return _recordedRomTest != nullptr; }
}

View file

@ -112,6 +112,7 @@
this.toolStripMenuItem3 = new System.Windows.Forms.ToolStripSeparator();
this.mnuPreferences = new System.Windows.Forms.ToolStripMenuItem();
this.mnuTools = new System.Windows.Forms.ToolStripMenuItem();
this.mnuCheats = new System.Windows.Forms.ToolStripMenuItem();
this.mnuMovies = new System.Windows.Forms.ToolStripMenuItem();
this.mnuPlayMovie = new System.Windows.Forms.ToolStripMenuItem();
this.mnuRecordMovie = new System.Windows.Forms.ToolStripMenuItem();
@ -124,6 +125,11 @@
this.mnuAviRecord = new System.Windows.Forms.ToolStripMenuItem();
this.mnuAviStop = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem11 = new System.Windows.Forms.ToolStripSeparator();
this.mnuTests = new System.Windows.Forms.ToolStripMenuItem();
this.mnuTestRun = new System.Windows.Forms.ToolStripMenuItem();
this.mnuTestRecord = new System.Windows.Forms.ToolStripMenuItem();
this.mnuTestStop = new System.Windows.Forms.ToolStripMenuItem();
this.mnuRunAllTests = new System.Windows.Forms.ToolStripMenuItem();
this.mnuLogWindow = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem7 = new System.Windows.Forms.ToolStripSeparator();
this.mnuRandomGame = new System.Windows.Forms.ToolStripMenuItem();
@ -152,7 +158,6 @@
this.mnuAbout = new System.Windows.Forms.ToolStripMenuItem();
this.pnlRenderer = new System.Windows.Forms.Panel();
this.ctrlRecentGames = new Mesen.GUI.Controls.ctrlRecentGames();
this.mnuCheats = new System.Windows.Forms.ToolStripMenuItem();
this.mnuMain.SuspendLayout();
this.pnlRenderer.SuspendLayout();
this.SuspendLayout();
@ -787,6 +792,7 @@
this.mnuSoundRecorder,
this.mnuVideoRecorder,
this.toolStripMenuItem11,
this.mnuTests,
this.mnuLogWindow,
this.toolStripMenuItem7,
this.mnuRandomGame,
@ -798,6 +804,13 @@
this.mnuTools.DropDownOpening += new System.EventHandler(this.mnuTools_DropDownOpening);
this.mnuTools.DropDownOpened += new System.EventHandler(this.mnu_DropDownOpened);
//
// mnuCheats
//
this.mnuCheats.Image = global::Mesen.GUI.Properties.Resources.CheatCode;
this.mnuCheats.Name = "mnuCheats";
this.mnuCheats.Size = new System.Drawing.Size(182, 22);
this.mnuCheats.Text = "Cheats";
//
// mnuMovies
//
this.mnuMovies.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
@ -813,7 +826,7 @@
//
this.mnuPlayMovie.Image = global::Mesen.GUI.Properties.Resources.MediaPlay;
this.mnuPlayMovie.Name = "mnuPlayMovie";
this.mnuPlayMovie.Size = new System.Drawing.Size(152, 22);
this.mnuPlayMovie.Size = new System.Drawing.Size(120, 22);
this.mnuPlayMovie.Text = "Play...";
this.mnuPlayMovie.Click += new System.EventHandler(this.mnuPlayMovie_Click);
//
@ -821,7 +834,7 @@
//
this.mnuRecordMovie.Image = global::Mesen.GUI.Properties.Resources.Record;
this.mnuRecordMovie.Name = "mnuRecordMovie";
this.mnuRecordMovie.Size = new System.Drawing.Size(152, 22);
this.mnuRecordMovie.Size = new System.Drawing.Size(120, 22);
this.mnuRecordMovie.Text = "Record...";
this.mnuRecordMovie.Click += new System.EventHandler(this.mnuRecordMovie_Click);
//
@ -829,7 +842,7 @@
//
this.mnuStopMovie.Image = global::Mesen.GUI.Properties.Resources.MediaStop;
this.mnuStopMovie.Name = "mnuStopMovie";
this.mnuStopMovie.Size = new System.Drawing.Size(152, 22);
this.mnuStopMovie.Size = new System.Drawing.Size(120, 22);
this.mnuStopMovie.Text = "Stop";
this.mnuStopMovie.Click += new System.EventHandler(this.mnuStopMovie_Click);
//
@ -895,6 +908,41 @@
this.toolStripMenuItem11.Name = "toolStripMenuItem11";
this.toolStripMenuItem11.Size = new System.Drawing.Size(179, 6);
//
// mnuTests
//
this.mnuTests.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.mnuTestRun,
this.mnuTestRecord,
this.mnuTestStop,
this.mnuRunAllTests});
this.mnuTests.Name = "mnuTests";
this.mnuTests.Size = new System.Drawing.Size(182, 22);
this.mnuTests.Text = "Tests";
//
// mnuTestRun
//
this.mnuTestRun.Name = "mnuTestRun";
this.mnuTestRun.Size = new System.Drawing.Size(152, 22);
this.mnuTestRun.Text = "Run...";
//
// mnuTestRecord
//
this.mnuTestRecord.Name = "mnuTestRecord";
this.mnuTestRecord.Size = new System.Drawing.Size(152, 22);
this.mnuTestRecord.Text = "Record...";
//
// mnuTestStop
//
this.mnuTestStop.Name = "mnuTestStop";
this.mnuTestStop.Size = new System.Drawing.Size(152, 22);
this.mnuTestStop.Text = "Stop recording";
//
// mnuRunAllTests
//
this.mnuRunAllTests.Name = "mnuRunAllTests";
this.mnuRunAllTests.Size = new System.Drawing.Size(152, 22);
this.mnuRunAllTests.Text = "Run all tests";
//
// mnuLogWindow
//
this.mnuLogWindow.Image = global::Mesen.GUI.Properties.Resources.LogWindow;
@ -1115,13 +1163,6 @@
this.ctrlRecentGames.TabIndex = 1;
this.ctrlRecentGames.Visible = false;
//
// mnuCheats
//
this.mnuCheats.Image = global::Mesen.GUI.Properties.Resources.CheatCode;
this.mnuCheats.Name = "mnuCheats";
this.mnuCheats.Size = new System.Drawing.Size(182, 22);
this.mnuCheats.Text = "Cheats";
//
// frmMain
//
this.AllowDrop = true;
@ -1273,5 +1314,10 @@
private System.Windows.Forms.ToolStripMenuItem mnuRegisterViewer;
private System.Windows.Forms.ToolStripMenuItem mnuRandomGame;
private System.Windows.Forms.ToolStripMenuItem mnuCheats;
private System.Windows.Forms.ToolStripMenuItem mnuTests;
private System.Windows.Forms.ToolStripMenuItem mnuTestRun;
private System.Windows.Forms.ToolStripMenuItem mnuTestRecord;
private System.Windows.Forms.ToolStripMenuItem mnuTestStop;
private System.Windows.Forms.ToolStripMenuItem mnuRunAllTests;
}
}

View file

@ -4,6 +4,7 @@ using Mesen.GUI.Debugger;
using Mesen.GUI.Debugger.Workspace;
using Mesen.GUI.Emulation;
using Mesen.GUI.Forms.Config;
using Mesen.GUI.Interop;
using Mesen.GUI.Updates;
using Mesen.GUI.Utilities;
using System;
@ -54,6 +55,10 @@ namespace Mesen.GUI.Forms
protected override void OnShown(EventArgs e)
{
base.OnShown(e);
#if HIDETESTMENU
mnuTests.Visible = false;
#endif
EmuApi.InitDll();
bool showUpgradeMessage = UpdateHelper.PerformUpgrade();
@ -211,6 +216,17 @@ namespace Mesen.GUI.Forms
mnuMain.Focus();
}
}
#if !HIDETESTMENU
if(keyData == Keys.Pause && EmuRunner.IsRunning()) {
if(TestApi.RomTestRecording()) {
TestApi.RomTestStop();
} else {
TestApi.RomTestRecord(ConfigManager.TestFolder + "\\" + EmuApi.GetRomInfo().GetRomName() + ".mtp", true);
}
}
#endif
return base.ProcessCmdKey(ref msg, keyData);
}
@ -311,6 +327,11 @@ namespace Mesen.GUI.Forms
mnuScriptWindow.Click += (s, e) => { DebugWindowManager.OpenDebugWindow(DebugWindow.ScriptWindow); };
mnuRegisterViewer.Click += (s, e) => { DebugWindowManager.OpenDebugWindow(DebugWindow.RegisterViewer); };
mnuTestRun.Click += (s, e) => { RomTestHelper.RunTest(); };
mnuTestRecord.Click += (s, e) => { RomTestHelper.RecordTest(); };
mnuTestStop.Click += (s, e) => { RomTestHelper.StopRecording(); };
mnuRunAllTests.Click += (s, e) => { RomTestHelper.RunAllTests(); };
UpdateDebuggerMenu();
}

19
UI/Interop/TestApi.cs Normal file
View file

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace Mesen.GUI.Interop
{
class TestApi
{
private const string DllPath = "MesenSCore.dll";
[DllImport(DllPath)] public static extern Int32 RunRecordedTest([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))]string filename, [MarshalAs(UnmanagedType.I1)]bool inBackground);
[DllImport(DllPath)] public static extern void RomTestRecord([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))]string filename, [MarshalAs(UnmanagedType.I1)]bool reset);
[DllImport(DllPath)] public static extern void RomTestStop();
[DllImport(DllPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool RomTestRecording();
}
}

View file

@ -835,6 +835,7 @@
</Compile>
<Compile Include="Interop\DebugState.cs" />
<Compile Include="Interop\RecordApi.cs" />
<Compile Include="Interop\TestApi.cs" />
<Compile Include="Updates\frmDownloadProgress.cs">
<SubType>Form</SubType>
</Compile>
@ -873,6 +874,7 @@
<Compile Include="Utilities\Md5Helper.cs" />
<Compile Include="Updates\UpdateHelper.cs" />
<Compile Include="Utilities\RandomGameHelper.cs" />
<Compile Include="Utilities\RomTestHelper.cs" />
<Compile Include="Utilities\XmlColor.cs" />
<None Include="Resources\GsuDebugger.png" />
<None Include="Resources\Sa1Debugger.png" />

View file

@ -0,0 +1,92 @@
using Mesen.GUI.Config;
using Mesen.GUI.Forms;
using Mesen.GUI.Interop;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Mesen.GUI.Utilities
{
public static class RomTestHelper
{
public static void RunTest()
{
using(OpenFileDialog ofd = new OpenFileDialog()) {
ofd.SetFilter(ResourceHelper.GetMessage("FilterTest"));
ofd.InitialDirectory = ConfigManager.TestFolder;
if(ofd.ShowDialog(frmMain.Instance) == DialogResult.OK) {
int result = TestApi.RunRecordedTest(ofd.FileName, false);
if(result == 0) {
MessageBox.Show("Test passed.", "", MessageBoxButtons.OK, MessageBoxIcon.Information);
} else {
MessageBox.Show("ERROR: Test failed! (" + result + ")", "", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}
public static void RecordTest()
{
using(SaveFileDialog sfd = new SaveFileDialog()) {
sfd.SetFilter(ResourceHelper.GetMessage("FilterTest"));
sfd.InitialDirectory = ConfigManager.TestFolder;
sfd.FileName = EmuApi.GetRomInfo().GetRomName() + ".mtp";
if(sfd.ShowDialog(frmMain.Instance) == DialogResult.OK) {
TestApi.RomTestRecord(sfd.FileName, true);
}
}
}
public static void RunAllTests()
{
Task.Run(() => {
ConcurrentDictionary<string, int> results = new ConcurrentDictionary<string, int>();
List<string> testFiles = Directory.EnumerateFiles(ConfigManager.TestFolder, "*.mtp", SearchOption.AllDirectories).ToList();
Parallel.ForEach(testFiles, new ParallelOptions() { MaxDegreeOfParallelism = 16 }, (string testFile) => {
int result = TestApi.RunRecordedTest(testFile, true);
results[Path.GetFileNameWithoutExtension(testFile)] = result;
});
frmMain.Instance.BeginInvoke((Action)(() => {
EmuApi.WriteLogEntry("==================");
List<string> failedTests = new List<string>();
foreach(var kvp in results) {
if(kvp.Value > 0) {
EmuApi.WriteLogEntry("[Test] Failed: " + kvp.Key + " (" + kvp.Value.ToString() + ")");
failedTests.Add(kvp.Key);
} else {
EmuApi.WriteLogEntry("[Test] Passed: " + kvp.Key);
}
}
EmuApi.WriteLogEntry("==================");
if(failedTests.Count > 0) {
EmuApi.WriteLogEntry("Tests passed: " + (testFiles.Count - failedTests.Count));
EmuApi.WriteLogEntry("Tests failed: " + failedTests.Count);
foreach(string failedTest in failedTests) {
EmuApi.WriteLogEntry(" Failed: " + failedTest);
}
} else {
EmuApi.WriteLogEntry("All " + testFiles.Count + " tests passed!");
}
EmuApi.WriteLogEntry("==================");
new frmLogWindow().Show();
}));
});
}
public static void StopRecording()
{
TestApi.RomTestStop();
}
}
}