Groundwork for rewriting + testing Story

This commit is contained in:
2019-03-17 18:09:29 -06:00
parent 952d7c9d0d
commit 00f74bdc46
9 changed files with 156 additions and 9 deletions

View File

@@ -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.
**/

View File

@@ -1,4 +1,4 @@
package tests;
package hank;
import utest.Assert;

View File

@@ -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;
}
}

View File

@@ -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<String>);
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. **/

66
hank/StoryTestCase.hx Normal file
View File

@@ -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<String>();
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());
}
}
}

View File

@@ -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;

View File

@@ -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
}

26
tests/StoryTest.hx Normal file
View File

@@ -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);
}
}
}
}
}
}

View File

@@ -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()]);
}
}