diff options
Diffstat (limited to 'src/Godosa.Core.Tests/Runs/Scripts/ScriptParserTests.cs')
| -rw-r--r-- | src/Godosa.Core.Tests/Runs/Scripts/ScriptParserTests.cs | 111 |
1 files changed, 111 insertions, 0 deletions
diff --git a/src/Godosa.Core.Tests/Runs/Scripts/ScriptParserTests.cs b/src/Godosa.Core.Tests/Runs/Scripts/ScriptParserTests.cs new file mode 100644 index 0000000..352ff03 --- /dev/null +++ b/src/Godosa.Core.Tests/Runs/Scripts/ScriptParserTests.cs @@ -0,0 +1,111 @@ +using Godosa.Core.Runs.Scripts; +using Xunit; + +namespace Godosa.Core.Tests.Runs.Scripts; + +public class ScriptParserTests +{ + private static ScriptFile Parse(string text) => ScriptParser.Parse("main.steps", text); + + private static string Errors(ScriptFile f) => string.Join(" | ", f.Errors.Select(e => e.ToString())); + + [Fact] + public void Header_AndStatements() + { + var f = Parse(""" + # comment + format 1 + mode player + seed 7 + vars ui/${res}.vars + click 10,20 + wait dialog-open 2s + expect quest(1) >= accepted + set x = "a b" + include ch/one.steps + call rest(8, inn) + checkpoint done + """); + Assert.Equal("", Errors(f)); + Assert.Equal((1, RunMode.Player, 7UL), (f.Format, f.Mode, f.Seed!.Value)); + Assert.Equal(["ui/${res}.vars"], f.VarFiles); + var v = Assert.IsType<VerbNode>(f.Body[0]); + Assert.Equal(("click", "10,20", 6), (v.Verb, v.Args, v.At.Line)); + var w = Assert.IsType<WaitNode>(f.Body[1]); + Assert.Equal(("dialog-open", 120), (w.Condition, w.Timeout)); + Assert.Equal("quest(1) >= accepted", Assert.IsType<ExpectNode>(f.Body[2]).Condition); + var s = Assert.IsType<SetNode>(f.Body[3]); + Assert.Equal(("x", "a b"), (s.Name, s.Value)); + Assert.Equal("ch/one.steps", Assert.IsType<IncludeNode>(f.Body[4]).Path); + var c = Assert.IsType<CallNode>(f.Body[5]); + Assert.Equal("rest", c.Name); + Assert.Equal(["8", "inn"], c.Args); + Assert.Equal("done", Assert.IsType<CheckpointNode>(f.Body[6]).Name); + } + + [Fact] + public void Blocks_NestByIndent() + { + var f = Parse(""" + format 1 + mode debug + setup: + cheat gold 5 + step talk-virgil: + click 1,2 + try: + key R + def rest(hours): + key R + wait-frames ${hours} + key Q + """); + Assert.Equal("", Errors(f)); + var setup = Assert.IsType<BlockNode>(f.Body[0]); + Assert.Equal((BlockKind.Setup, 1), (setup.Kind, setup.Body.Count)); + var step = Assert.IsType<BlockNode>(f.Body[1]); + Assert.Equal((BlockKind.Step, "talk-virgil", 2), (step.Kind, step.Label, step.Body.Count)); + Assert.Equal(BlockKind.Try, Assert.IsType<BlockNode>(step.Body[1]).Kind); + var def = Assert.IsType<DefNode>(f.Body[2]); + Assert.Equal(("rest", 2), (def.Name, def.Body.Count)); + Assert.Equal(["hours"], def.Params); + Assert.Equal("Q", Assert.IsType<VerbNode>(f.Body[3]).Args); + } + + [Theory] + [InlineData("mode player\nclick 1,1", "main.steps:1: first line must be 'format N'")] + [InlineData("format 9\nmode player", "main.steps:1: unknown format 9 (latest 1)")] + [InlineData("format 1\nmode sneaky", "main.steps:2: mode must be player or debug")] + [InlineData("format 1\nmode debug\nclick 1,1\nseed 3", "main.steps:4: header line after steps")] + [InlineData("format 1\nmode debug\nwait dialog-open 0.01s", "main.steps:3: bad timeout '0.01s'")] + [InlineData("format 1\nmode debug\nwait 2s", "main.steps:3: wait needs a condition and a timeout")] + [InlineData("format 1\nmode debug\nstep a:\n click 1,1", "main.steps:4: indent not a multiple of 2")] + [InlineData("format 1\nmode debug\nstep a:\n\tclick 1,1", "main.steps:4: tab in indentation")] + [InlineData("format 1\nmode debug\nstep a:\nclick 1,1", "main.steps:3: empty block")] + [InlineData("format 1\nmode debug\nloop:\n click 1,1", "main.steps:3: unknown block 'loop:'")] + [InlineData("format 1\nmode debug\nclick 1,1\n key R", "main.steps:4: unexpected indent")] + [InlineData("format 1\nmode debug\nlog \"abc", "main.steps:3: unterminated quote")] + [InlineData("format 1\nmode debug\nset 9x = 1", "main.steps:3: bad set (set name = value)")] + public void Errors_NameTheLine(string text, string error) => Assert.Contains(error, Errors(Parse(text))); + + // Review focus 5: CRLF, BOM and trailing spaces parse as LF. + [Fact] + public void CrlfBomTrailingSpaces_SameAsLf() + { + var lf = Parse("format 1\nmode debug\nstep a:\n click 1,2\n"); + var crlf = Parse("\uFEFFformat 1 \r\nmode debug\r\nstep a: \r\n click 1,2 \r\n"); + Assert.Equal("", Errors(crlf)); + var a = Assert.IsType<BlockNode>(lf.Body[0]); + var b = Assert.IsType<BlockNode>(crlf.Body[0]); + Assert.Equal(a.Text, b.Text); + Assert.Equal(((VerbNode)a.Body[0]).Text, ((VerbNode)b.Body[0]).Text); + Assert.Equal(((VerbNode)a.Body[0]).At, ((VerbNode)b.Body[0]).At); + } + + [Fact] + public void ReportsEveryError_NotOnlyTheFirst() + { + var f = Parse("format 1\nmode debug\nwait x 2m\nset = 1\nloop:\n key R"); + Assert.Equal(3, f.Errors.Count); + } +} |
