aboutsummaryrefslogtreecommitdiffziptar.gz
path: root/src/Godosa.Core.Tests/Runs/Scripts/ScriptParserTests.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Godosa.Core.Tests/Runs/Scripts/ScriptParserTests.cs')
-rw-r--r--src/Godosa.Core.Tests/Runs/Scripts/ScriptParserTests.cs111
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);
+ }
+}