Debugger: Added "test runner" headless mode to run Lua scripts w/o UI

This commit is contained in:
Sour 2018-04-14 21:53:52 -04:00
parent 1e47897ca6
commit fd4a8cbf43
12 changed files with 190 additions and 64 deletions

View file

@ -360,9 +360,10 @@ void Console::ResetComponents(bool softReset)
_controlManager->UpdateInputState();
}
void Console::Stop()
void Console::Stop(int stopCode)
{
_stop = true;
_stopCode = stopCode;
shared_ptr<Debugger> debugger = _debugger;
if(debugger) {
@ -510,6 +511,7 @@ void Console::Run()
}
} catch(const std::runtime_error &ex) {
crashed = true;
_stopCode = -1;
MessageManager::DisplayMessage("Error", "GameCrash", ex.what());
}
@ -726,6 +728,11 @@ void Console::SetNextFrameOverclockStatus(bool disabled)
Instance->_disableOcNextFrame = disabled;
}
int32_t Console::GetStopCode()
{
return _stopCode;
}
HdPackData* Console::GetHdData()
{
return Instance->_hdData.get();

View file

@ -58,6 +58,7 @@ class Console
bool _stop = false;
bool _running = false;
int32_t _stopCode = 0;
bool _disableOcNextFrame = false;
@ -77,7 +78,9 @@ class Console
void SaveBatteries();
void Run();
void Stop();
void Stop(int stopCode = 0);
int32_t GetStopCode();
void RunSingleFrame();
bool UpdateHdPackMode();

View file

@ -81,6 +81,7 @@ int LuaApi::GetLibrary(lua_State *lua)
{ "log", LuaApi::Log },
{ "displayMessage", LuaApi::DisplayMessage },
{ "reset", LuaApi::Reset },
{ "stop", LuaApi::Stop },
{ "breakExecution", LuaApi::Break },
{ "resume", LuaApi::Resume },
{ "execute", LuaApi::Execute },
@ -464,6 +465,15 @@ int LuaApi::Reset(lua_State *lua)
return l.ReturnCount();
}
int LuaApi::Stop(lua_State *lua)
{
LuaCallHelper l(lua);
int32_t stopCode = l.ReadInteger(0);
checkminparams(0);
Console::GetInstance()->Stop(stopCode);
return l.ReturnCount();
}
int LuaApi::Break(lua_State *lua)
{
LuaCallHelper l(lua);

View file

@ -41,6 +41,7 @@ public:
static int DisplayMessage(lua_State *lua);
static int Reset(lua_State *lua);
static int Stop(lua_State *lua);
static int Break(lua_State *lua);
static int Resume(lua_State *lua);
static int Execute(lua_State *lua);

View file

@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Mesen.GUI
{
class CommandLineHelper
{
public static List<string> PreprocessCommandLineArguments(string[] args, bool toLower)
{
var switches = new List<string>();
for(int i = 0; i < args.Length; i++) {
if(args[i] != null) {
string arg = args[i].Trim();
if(arg.StartsWith("--")) {
arg = "/" + arg.Substring(2);
} else if(arg.StartsWith("-")) {
arg = "/" + arg.Substring(1);
}
if(toLower) {
arg = arg.ToLowerInvariant();
}
switches.Add(arg);
}
}
return switches;
}
public static void GetRomPathFromCommandLine(List<string> switches, out string romPath, out List<string> luaScriptsToLoad)
{
Func<string, bool, string> getValidPath = (string path, bool forLua) => {
path = path.Trim();
if(path.ToLower().EndsWith(".lua") == forLua) {
try {
if(!File.Exists(path)) {
//Try loading file as a relative path to the folder Mesen was started from
path = Path.Combine(Program.OriginalFolder, path);
}
if(File.Exists(path)) {
return path;
}
} catch { }
}
return null;
};
//Check if any Lua scripts were specified
luaScriptsToLoad = new List<string>();
foreach(string arg in switches) {
string path = getValidPath(arg, true);
if(path != null) {
luaScriptsToLoad.Add(path);
}
}
romPath = null;
foreach(string arg in switches) {
string path = getValidPath(arg, false);
if(path != null) {
romPath = path;
break;
}
}
}
}
}

View file

@ -101,7 +101,11 @@ namespace Mesen.GUI.Debugger
if(_openedWindows.Count == 0) {
//All windows have been closed, disable debugger
DebugWorkspaceManager.Clear();
InteropEmu.DebugRelease();
Task.Run(() => {
//Run this in another thread to avoid deadlocks when this is called within a notification handler
InteropEmu.DebugRelease();
});
}
}

View file

@ -82,28 +82,7 @@ namespace Mesen.GUI.Forms
this.Resize += ResizeRecentGames;
this.FormClosed += (s, e) => Application.RemoveMessageFilter(this);
}
public List<string> PreprocessCommandLineArguments(string[] args, bool toLower)
{
var switches = new List<string>();
for(int i = 0; i < args.Length; i++) {
if(args[i] != null) {
string arg = args[i].Trim();
if(arg.StartsWith("--")) {
arg = "/" + arg.Substring(2);
} else if(arg.StartsWith("-")) {
arg = "/" + arg.Substring(1);
}
if(toLower) {
arg = arg.ToLowerInvariant();
}
switches.Add(arg);
}
}
return switches;
}
public void ProcessCommandLineArguments(List<string> switches, bool forStartup)
{
if(forStartup) {
@ -121,42 +100,13 @@ namespace Mesen.GUI.Forms
public void LoadGameFromCommandLine(List<string> switches)
{
Func<string, bool, string> getValidPath = (string path, bool forLua) => {
path = path.Trim();
if(path.ToLower().EndsWith(".lua") == forLua) {
try {
if(!File.Exists(path)) {
//Try loading file as a relative path to the folder Mesen was started from
path = Path.Combine(Program.OriginalFolder, path);
}
if(File.Exists(path)) {
return path;
}
} catch { }
}
return null;
};
string romPath;
CommandLineHelper.GetRomPathFromCommandLine(switches, out romPath, out _luaScriptsToLoad);
//Check if any Lua scripts were specified
_luaScriptsToLoad = new List<string>();
foreach(string arg in switches) {
string path = getValidPath(arg, true);
if(path != null) {
_luaScriptsToLoad.Add(path);
}
}
if(romPath != null) {
this.LoadFile(romPath);
} else {
bool fileLoaded = false;
foreach(string arg in switches) {
string path = getValidPath(arg, false);
if(path != null) {
this.LoadFile(path);
fileLoaded = true;
break;
}
}
if(!fileLoaded) {
if(_emuThread == null) {
//When no ROM is loaded, only process Lua scripts if a ROM was specified as a command line param
_luaScriptsToLoad.Clear();
@ -196,7 +146,7 @@ namespace Mesen.GUI.Forms
InteropEmu.ScreenSize originalSize = InteropEmu.GetScreenSize(false);
VideoInfo.ApplyConfig();
this.ProcessCommandLineArguments(this.PreprocessCommandLineArguments(_commandLineArgs, true), true);
this.ProcessCommandLineArguments(CommandLineHelper.PreprocessCommandLineArguments(_commandLineArgs, true), true);
VideoInfo.ApplyConfig();
InteropEmu.ScreenSize newSize = InteropEmu.GetScreenSize(false);
if(originalSize.Width != newSize.Width || originalSize.Height != newSize.Height) {
@ -267,7 +217,7 @@ namespace Mesen.GUI.Forms
this.Size = ConfigManager.Config.WindowSize.Value;
}
this.LoadGameFromCommandLine(this.PreprocessCommandLineArguments(_commandLineArgs, false));
this.LoadGameFromCommandLine(CommandLineHelper.PreprocessCommandLineArguments(_commandLineArgs, false));
this.menuStrip.VisibleChanged += new System.EventHandler(this.menuStrip_VisibleChanged);
this.UpdateRendererLocation();
@ -281,7 +231,7 @@ namespace Mesen.GUI.Forms
//This is needed when DPI display settings is not set to 100%
_enableResize = true;
ProcessFullscreenSwitch(this.PreprocessCommandLineArguments(_commandLineArgs, true));
ProcessFullscreenSwitch(CommandLineHelper.PreprocessCommandLineArguments(_commandLineArgs, true));
}
protected override void OnClosing(CancelEventArgs e)

View file

@ -238,6 +238,7 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="CommandLineHelper.cs" />
<Compile Include="Config\DebuggerShortcutsConfig.cs" />
<Compile Include="Config\MovieRecordInfo.cs" />
<Compile Include="Config\ConfigAttributes.cs" />
@ -1166,6 +1167,7 @@
<Compile Include="ResourceManager.cs" />
<Compile Include="RuntimeChecker.cs" />
<Compile Include="SingleInstance.cs" />
<Compile Include="TestRunner.cs" />
<None Include="Resources\SwitchView.png" />
<None Include="Resources\SelectAll.png" />
<None Include="Resources\Paste.png" />

View file

@ -27,6 +27,7 @@ namespace Mesen.GUI
[DllImport(DLLPath)] public static extern void SetFullscreenMode([MarshalAs(UnmanagedType.I1)]bool fullscreen, IntPtr windowHandle, UInt32 monitorWidth, UInt32 monitorHeight);
[DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool IsRunning();
[DllImport(DLLPath)] public static extern Int32 GetStopCode();
[DllImport(DLLPath)] public static extern void LoadROM([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string filename, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string patchFile);
[DllImport(DLLPath)] public static extern void AddKnownGameFolder([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string folder);

View file

@ -129,6 +129,11 @@ namespace Mesen.GUI
return;
}
if(CommandLineHelper.PreprocessCommandLineArguments(args, true).Contains("/testrunner")) {
Environment.ExitCode = TestRunner.Run(args);
return;
}
using(SingleInstance singleInstance = new SingleInstance()) {
if(singleInstance.FirstInstance || !ConfigManager.Config.PreferenceInfo.SingleInstance) {
frmMain frmMain = new frmMain(args);
@ -137,8 +142,8 @@ namespace Mesen.GUI
singleInstance.ArgumentsReceived += (object sender, ArgumentsReceivedEventArgs e) => {
if(frmMain.IsHandleCreated) {
frmMain.BeginInvoke((MethodInvoker)(() => {
frmMain.ProcessCommandLineArguments(frmMain.PreprocessCommandLineArguments(e.Args, true), false);
frmMain.LoadGameFromCommandLine(frmMain.PreprocessCommandLineArguments(e.Args, false));
frmMain.ProcessCommandLineArguments(CommandLineHelper.PreprocessCommandLineArguments(e.Args, true), false);
frmMain.LoadGameFromCommandLine(CommandLineHelper.PreprocessCommandLineArguments(e.Args, false));
}));
}
};

72
GUI.NET/TestRunner.cs Normal file
View file

@ -0,0 +1,72 @@
using Mesen.GUI.Config;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace Mesen.GUI
{
internal class TestRunner
{
internal static int Run(string[] args)
{
ConfigManager.DoNotSaveSettings = true;
string romPath;
List<string> luaScriptsToLoad;
CommandLineHelper.GetRomPathFromCommandLine(CommandLineHelper.PreprocessCommandLineArguments(args, false), out romPath, out luaScriptsToLoad);
if(romPath == null) {
//No rom specified
return -1;
}
List<string> lcArgs = CommandLineHelper.PreprocessCommandLineArguments(args, true);
int timeout = 100; //100 seconds
string timeoutArg = lcArgs.Find(arg => arg.StartsWith("/timeout="));
if(timeoutArg != null) {
int timeoutValue;
if(Int32.TryParse(timeoutArg.Substring(timeoutArg.IndexOf("=") + 1), out timeoutValue)) {
timeout = timeoutValue;
}
}
ConfigManager.ProcessSwitches(lcArgs);
ConfigManager.Config.ApplyConfig();
InteropEmu.SetFlag(EmulationFlags.ConsoleMode, true);
InteropEmu.InitializeEmu(ConfigManager.HomeFolder, IntPtr.Zero, IntPtr.Zero, true, true, true);
InteropEmu.LoadROM(romPath, string.Empty);
foreach(string luaScript in luaScriptsToLoad) {
try {
string script = File.ReadAllText(luaScript);
InteropEmu.DebugLoadScript(luaScript, script);
} catch { }
}
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
Task.Run(() => {
InteropEmu.Run();
});
InteropEmu.SetFlag(EmulationFlags.ForceMaxSpeed, true);
sw.Start();
int result = -1;
while(sw.ElapsedMilliseconds < timeout * 1000) {
System.Threading.Thread.Sleep(100);
if(!InteropEmu.IsRunning()) {
result = InteropEmu.GetStopCode();
break;
}
}
InteropEmu.Stop();
InteropEmu.Release();
return result;
}
}
}

View file

@ -132,6 +132,7 @@ namespace InteropEmu {
DllExport bool __stdcall IsRunning() { return Console::IsRunning(); }
DllExport int32_t __stdcall GetStopCode() { return Console::GetInstance()->GetStopCode(); }
DllExport void __stdcall LoadROM(char* filename, char* patchFile) { Console::LoadROM((string)filename, (string)patchFile); }
DllExport void __stdcall AddKnownGameFolder(char* folder) { FolderUtilities::AddKnownGameFolder(folder); }