using Godosa.Core.Runs.Scripts; using Xunit; using static Godosa.Core.Tests.Runs.Scripts.ScriptRunnerTests; namespace Godosa.Core.Tests.Runs.Scripts; public class ScriptRunnerStructureTests { [Fact] public void Output_DefaultLabels_StepsLevelShowsEveryStep() { var (_, labels) = Run("format 1\nmode player\nstep a:\n key R\n log \"hi\"\nkey Q", new FakeGame()); Assert.Equal(["hi", "[main.steps:3 a] done f=2 (+2)", "digest 2 0.033 0000000000000002"], labels); var (_, steps) = Run("format 1\nmode player\noutput steps\nstep a:\n key R\nkey Q", new FakeGame()); Assert.Equal(["[main.steps:5 a] key R done f=2 (+2)", "[main.steps:4 a] done f=2 (+2)", "digest 2 0.033 0000000000000002", "[main.steps:6] key Q done f=4 (+2)"], steps); } [Fact] public void Output_ScopedToIncludedFile() { var (_, output) = Run("format 1\nmode player\ninclude loud.steps\nkey Q", new FakeGame(), files: new() { ["loud.steps"] = "format 1\noutput steps\nkey R" }); Assert.Equal(["[loud.steps:3] key R done f=2 (+2)"], output); // key Q at labels level: silent } [Fact] public void Output_FloorCannotBeLowered() { var (_, output) = Run("format 1\nmode player\noutput quiet\nkey R", new FakeGame(), new ScriptRunOptions { Floor = OutputLevel.Steps }); Assert.Equal(["[main.steps:4] key R done f=2 (+2)"], output); } [Fact] public void DefCall_BindsParams_ParamUnknownOutside() { var g = new FakeGame(); var a = new FakeAction(VerbKind.Debug, _ => new ActionStart(Status: StepStatus.Done), min: 1, max: 3); g.ActionTable["cheat"] = a; var (ok, _) = Run("format 1\nmode debug\ndef d(x):\n set local = in\n cheat ${x} ${local}\ncall d(gold)", g); Assert.Equal(RunOutcome.Passed, ok.Outcome); Assert.Equal(["gold", "in"], a.Begun[0]); var (bad, output) = Run("format 1\nmode debug\ndef d(x):\n cheat ${x}\ncall d(gold)\ncheat ${x}", g); Assert.Equal(RunOutcome.Invalid, bad.Outcome); Assert.Contains(output, l => l == "main.steps:6: unresolved variable ${x}"); } [Fact] public void Try_FaultInsideContinuesAfterTheBlock_NonDeterministic() { var g = new FakeGame(); g.ProbeFns["never"] = _ => RunValue.Of(false); var (r, output) = Run("format 1\nmode player\ntry:\n wait never 3\n key A\nkey B", g, new ScriptRunOptions { Deterministic = false }); Assert.Equal(RunOutcome.Passed, r.Outcome); Assert.Contains(output, l => l == "try failed at main.steps:4: timeout after 3 frames: never (never=0)"); Assert.DoesNotContain(g.Sent, s => s.Event.Key == "A"); Assert.Contains(g.Sent, s => s.Event.Key == "B"); } // Review focus 1: a fault while a button is down releases it. [Fact] public void Fault_ReleasesHeldButtonAndKey() { var g = new FakeGame(); g.ProbeFns["never"] = _ => RunValue.Of(false); g.ActionTable["walk-to"] = new FakeAction(VerbKind.Player, _ => new ActionStart(new InputPlan([new PlannedInput(0, new InputEvent(InputKind.KeyDown, Key: "Shift")), new PlannedInput(0, new InputEvent(InputKind.Down, 9, 9)), new PlannedInput(5, new InputEvent(InputKind.Up, 9, 9)), new PlannedInput(5, new InputEvent(InputKind.KeyUp, Key: "Shift"))])), polls => StepStatus.Running, 1, 1); g.OnAdvance = f => { if (f == 2) throw new InvalidOperationException("crash"); }; var (r, _) = Run("format 1\nmode player\nwalk-to 9,9", g); Assert.Equal(RunOutcome.Failed, r.Outcome); Assert.Contains(g.Sent, s => s.Event == new InputEvent(InputKind.Up, 9, 9)); Assert.Contains(g.Sent, s => s.Event == new InputEvent(InputKind.KeyUp, Key: "Shift")); } [Fact] public void Setup_RunsFirstAtFrameZero() { var g = new FakeGame(); var order = new List(); g.ActionTable["cheat"] = new FakeAction(VerbKind.Debug, a => { order.Add($"cheat@{g.Frame}"); return new ActionStart(Status: StepStatus.Done); }, min: 1, max: 3); Run("format 1\nmode player\nsetup:\n cheat stats max\nkey R", g); Assert.Equal(["cheat@0"], order); } }