So:
--
-- test jump tag
--
function test_jump()
local rc
local scene = [[
<scene Name="frogger">
<narrative Name="lane_one">
<snippet>truck approaching</snippet>
<jump name="lane_two" />
<snippet>***splat***</snippet>
</narrative>
<narrative Name="lane_two">
<snippet> phew </snippet>
</narrative>
</scene>
]]
local narrator = Narrator:new()
local stage = TestStage:new()
narrator:load(scene)
end
So, if the jump works, Frogger winds up in lane two and dodges the truck. If not ... well I expect he gets used to it after a while. Anyway, this test fails with the same error as the last one. So now we need to fix that error. That means telling Narrative about jump tags
function Narrative:do_item(raw_item)
local item = nil
local auto_cut = false
assert(raw_item.label ~= nil)
local label = raw_item.label:lower()
if label == "snippet" then
item = Snippet:new(raw_item)
if item.image ~= nil then
auto_cut = true
end
elseif label == "jump" then
-- jump code to go here
Another TDD convention is to make the least change to fix the error. So that won't make the jump work, but it will get us past the current error. And in fact the test now passes. Mainly because we haven't checked to see if the jump works.
So let's add a play() call to the test.
-- play the scene. I expect an auto-cut after the jump
-- which means play should return true
--
rc = narrator:play(stage)
assert_true(rc)
And that fails again. play() consumes the entire narrative and drops off the end thinking "my work here is done". So I need an item in the input queue with a cut=true element. (I've only documented cut for snippets, and they remain the only element where the XML parser accepts cut as an attribute - another reason for making Cut an element in in its own right).
The Jump class is about as simple as it gets. At least if you ignore the XML parsing code which I don't want to get into right now. It's based on the 5.1 example on the Lua wiki if anyone's interested). Anyway: Jump class:
Jump = Jump or {}
Jump.__index = Jump
function Jump:new(xml)
local self = { }
setmetatable(self, Option)
self.target = xml.xarg.target
self.cut = true
return self
end
return Jump
Following on, we just need to create an instance in the Narrative loop
elseif label == "jump" then
item = Jump:new(raw_item)
A bit of fiddling and ...the test fails! I've told Narrative where to put the Jump object, but not told Narrator what to do with it.
I can get that error to go away (and the test to pass) by adding a couple of lines to Narrator
while true do
self.index = self.index + 1
item = self.narrative.items[self.index]
if item == nil then
return false
end
--
-- anything can set an image, it seems
-- (argument for making "image" a narrative element?
-- would solve the cut problem, certainly)
--
print("item.type = " .. item.type)
if item.type == "snippet" then
self:do_snippet(interface, item)
elseif item.type == "image" then
interface:set_background(item:filename())
elseif item.type == "jump" then
-- code to go here
And that passes the test. On the other hand, we aren't testing yet to see if the jump is executed. We need to add to the test case for that...
--
-- OK: we just did the jump. Next play should run the new narrative
--
rc = narrator:play(stage)
assert_false(rc) -- this should be the end
assert_equal(1, stage:num_oq()) -- expect one item in output Q
assert_equal("text", stage:oq(1).type)
assert_not_equal( -- A tragedy averted?
"*** Splat ***",
stage:oq(1).value
)
assert_equal( -- just to make sure
"phew",
stage:oq(1).value
)
Alas, poor Frogger...
1) Failure (narrator_tests.test_jump):
test_narrator.lua:637: '*** Splat ***' not expected but was one
So, finally, I get to the business of making the actual jump work. We already change narratives in the option code. Rather than copy and paste that, let's put it in its own function
function Narrator:change_narrative(name)
self.narrative = self.scene.narratives[ name ]
self.index = 0
end
I'm tempted to add some error checking to that, but it'll do for now.
elseif item.type == "jump" then
self:change_narrative(item.target)
And that doesn't work. It's not jumping and it's not returning false. Putting some checks into the function suddenly looks like a good idea
function Narrator:change_narrative(target)
if target == nil then
error("Narrator:change_narrative: target is nil!")
end
local temp = self.scene.narratives[ target ]
if temp == nil then
error("Narrator:change_narrative: narrative '" .. target .. "' i
s nil")
end
self.narrative = temp
self.index = 0
end
Re-running the tests, I get
117 Assertions checked.
1) Error! (narrator_tests.test_jump):
../Scripts/Narrator.lua:38: Narrator:change_narrative: target is nil!
So the target attribute isn't being set - which turns out to be because the XML in the test is wrong. I changed my mind about the attribute that specifies the destination narrative, changing the attribute name from "name" to "target". I didn't update the test XML however.
Once that's fixed, all is well.
I'm tempted to add some validation code the the Jump constructor to catch the case if it happens again, maybe write a few test cases to make sure it handles the error cases correctly... but I'll leave that until such time as XML errors become a problem. I still have Conditional options to get working!
No comments:
Post a Comment