diff --git a/hank/HInterface.hx b/hank/HInterface.hx index 95a8b7e..d0ceab1 100644 --- a/hank/HInterface.hx +++ b/hank/HInterface.hx @@ -20,6 +20,10 @@ class HInterface { } } + public function getVariable(v: String) { + return interp.variables[v]; + } + /** Run a pre-processed block of Haxe embedded in a Hank story. **/ diff --git a/tests/HankAssert.hx b/hank/HankAssert.hx similarity index 98% rename from tests/HankAssert.hx rename to hank/HankAssert.hx index 1971b4d..2c521a2 100644 --- a/tests/HankAssert.hx +++ b/hank/HankAssert.hx @@ -1,4 +1,4 @@ -package tests; +package hank; import utest.Assert; diff --git a/hank/Output.hx b/hank/Output.hx index 766e186..2af8a4e 100644 --- a/hank/Output.hx +++ b/hank/Output.hx @@ -59,14 +59,12 @@ class Output { var peekLine = buffer.peekLine().unwrap(); if (peekLine.length < endSegment) { var text = buffer.takeLine().unwrap(); - trace(text); if (text.length > 0) { parts.push(Text(text)); } break; } else { var text = buffer.take(endSegment); - trace(text); parts.push(Text(text)); } } else { @@ -143,4 +141,14 @@ class Output { } } + + public function format(): String { + var fullOutput = ''; + + for (part in parts) { + + } + + return fullOutput; + } } \ No newline at end of file diff --git a/hank/Story.hx b/hank/Story.hx index 63420d6..de7f5c3 100644 --- a/hank/Story.hx +++ b/hank/Story.hx @@ -1,18 +1,59 @@ package hank; -import hank.HInterface; +using HankAST.ASTExtension; +import hank.HankAST.ExprType; + +/** + Possible states of the story being executed. +**/ +enum StoryFrame { + HasText(text: String); + HasChoices(choices: Array); + Finished; +} /** Runtime interpreter for Hank stories. **/ +@:allow(hank.StoryTestCase) class Story { var hInterface: HInterface; + var random: Random; + + var ast: HankAST; + var exprIndex: Int; + + var parser: Parser; + + public function new(script: String, ?randomSeed: Int) { + random = new Random(randomSeed); + + parser = new Parser(); + ast = parser.parseFile(script); + // viewCounts = new ViewCounts(ast); - public function new() { var variables = [ - 'story' => this + 'story' => this/*, + 'viewCounts' => viewCounts + */ ]; hInterface = new HInterface(variables); + + exprIndex = ast.findFile(script); + } + + public function nextFrame(): StoryFrame { + switch (ast[exprIndex].expr) { + case EOutput(output): + return HasText(output.format()); + default: + return Finished; + } + return Finished; + } + + public function choose(choiceIndex: Int): String { + return ''; } /** Parse and run embedded Hank script on the fly. **/ diff --git a/hank/StoryTestCase.hx b/hank/StoryTestCase.hx new file mode 100644 index 0000000..167cc46 --- /dev/null +++ b/hank/StoryTestCase.hx @@ -0,0 +1,66 @@ +package hank; + +import hank.Story.StoryFrame; + +class StoryTestCase extends utest.Test { + private function validateAgainstTranscript(storyFile: String, transcriptFile: String, fullTranscript: Bool = true) { + + var transcriptLines = sys.io.File.getContent(transcriptFile).split('\n'); + // If the transcript starts with a random seed, make sure the story uses that seed + var randomSeed = null; + if (StringTools.startsWith(transcriptLines[0], '@')) { + randomSeed = Std.parseInt(transcriptLines[0].substr(1)); + transcriptLines.remove(transcriptLines[0]); + } + + var story: Story = new Story(storyFile, randomSeed); + + var i = 0; + while (i < transcriptLines.length) { + var line = transcriptLines[i]; + var frame = story.nextFrame(); + + // Allow white-box story testing through variable checks prefixed with # + if (StringTools.startsWith(line, "#")) { + var parts = StringTools.trim(line.substr(1)).split(':'); + HankAssert.equals(StringTools.trim(parts[1]), Std.string(story.hInterface.getVariable(parts[0]))); + } + if (StringTools.startsWith(line, "*")) { + // Collect the expected set of choices from the transcript. + var choices = new Array(); + do { + choices.push(line.substr(2)); + + line = transcriptLines[++i]; + } while (line != null && StringTools.startsWith(line, "*")); + + // Assert that the storyframe is a corresponding HasChoices enum + HankAssert.equals(HasChoices(choices), frame); + + continue; + } else if (StringTools.startsWith(line, ">")) { + // Make the choice given, and check for expected output. + line = StringTools.ltrim(line.substr(1)); + var firstColonIdx = line.indexOf(':'); + var index = Std.parseInt(line.substr(0, firstColonIdx))-1; + var expectedOutput = StringTools.trim(line.substr(firstColonIdx+1)); + // trace('expecting: ${expectedOutput}'); + var output = story.choose(index); + // trace('got: ${output}'); + HankAssert.equals(expectedOutput, output); + } + else if (line.length > 0) { + // Assert that the story's next frame is HasText(line) + // trace('${line} from ${frame}'); + HankAssert.equals(HasText(line), frame); + } + + i += 1; + } + + if (fullTranscript) { + // After all transcript lines are validated, there should be nothing left in the story flow! + HankAssert.equals(StoryFrame.Finished, story.nextFrame()); + } + } +} \ No newline at end of file diff --git a/tests/HankBufferTest.hx b/tests/HankBufferTest.hx index db8e489..79e8e87 100644 --- a/tests/HankBufferTest.hx +++ b/tests/HankBufferTest.hx @@ -5,6 +5,7 @@ using hank.Extensions.OptionExtender; import haxe.ds.Option; import hank.HankBuffer; import hank.HankBuffer.Position; +import hank.HankAssert; class HankBufferTest extends utest.Test { var file: HankBuffer; diff --git a/tests/ParserTest.hx b/tests/ParserTest.hx index 2e79ef2..29ba7d3 100644 --- a/tests/ParserTest.hx +++ b/tests/ParserTest.hx @@ -1,9 +1,10 @@ package tests; import hank.Parser; -import hank.Parser.HankAST; +import hank.HankAST; import hank.Output; import hank.Alt; +import hank.HankAssert; class ParserTest extends utest.Test { var ast: HankAST; @@ -80,7 +81,7 @@ class ParserTest extends utest.Test { ToggleOutput(new Output([Text(".")]), false), ToggleOutput(new Output([Text(" that changes after a choice is made!")]), true) ]))); - trace(nextExpr()); + // TODO test the last one } diff --git a/tests/StoryTest.hx b/tests/StoryTest.hx new file mode 100644 index 0000000..78d7b4b --- /dev/null +++ b/tests/StoryTest.hx @@ -0,0 +1,26 @@ +package tests; + +class StoryTest extends hank.StoryTestCase { + function testAllExamples() { + var exampleFolders = sys.FileSystem.readDirectory('examples'); + + // Iterate through every example in the examples folder + for (folder in exampleFolders) { + trace('Running tests for example "${folder}"'); + var files = sys.FileSystem.readDirectory('examples/${folder}'); + + for (file in files) { + if (StringTools.endsWith(file, '.hlog')) { + trace(' Running ${file}'); + + var disabled = file.indexOf("disabled") != -1; + var debug = file.indexOf("debug") != -1; + var partial = file.indexOf("partial") != -1; + if (!disabled) { + validateAgainstTranscript('examples/${folder}/main.hank', 'examples/${folder}/${file}', !partial); + } + } + } + } + } +} \ No newline at end of file diff --git a/tests/TestMain.hx b/tests/TestMain.hx index 5712d2d..cff52c2 100644 --- a/tests/TestMain.hx +++ b/tests/TestMain.hx @@ -3,6 +3,6 @@ import utest.Test; class TestMain extends Test { public static function main() { - utest.UTest.run([new HInterfaceTest(), new HankBufferTest(), new ParserTest()]); + utest.UTest.run([new HInterfaceTest(), new HankBufferTest(), new ParserTest(), new StoryTest()]); } } \ No newline at end of file