24. Testing and Debugging

Writing with Inform

WI §24.1 Checking against the Index

Testing a story -- and indeed writing a story so that it is easy to test consistently -- is an art in itself. We should expect that we'll do some preliminary testing, both by running test commands and by playing through the story ourselves, and that we'll then hand on the story to beta-testers who will tell us about faults in the play experience that we haven't been able to see.

Every time Inform builds a new story file, it assembles a vast amount of information about that world, in the form of the Index. Often a visit to the Index is all that's needed to explain a piece of undesired behavior.

Is travel not working as it should? Check the World index and see whether the map shows the rooms arranged the way you thought.

Are objects not showing the behavior you'd expect based on their kind? Check the Kinds index and make sure they've been defined as the kind of thing you expected. For instance, we might find that we've written

The red door is west of Foo and east of Bar.

but not

The red door is a door.

A human reader wouldn't make this mistake, but Inform hasn't actually registered the red door as belonging to the door kind, and consequently has treated it as a room instead. All we need to do is add the kind declaration. The Kinds index will make that obvious.

When an error appears in the Index, there is often a link back to the source text that defined that room or object. If not, there's often at least some information about what rule or phrase might be responsible for it.

WI §24.2 Debugging features to use in source

The TEST command is an extremely useful way of managing a story and continuing to verify that it does everything we want. We can create new test commands of the form

Test me with "up / kill captain eo".
Test eo with "zap eo" holding the ray gun.
Test dinner with "eat bread / eat soup / eat butter" in the Ship Cafeteria.

and we are free to have as many of these tests as we would like. Test commands can call other tests, as well, so we might have a test command such as

Test megatest with "test me / test eo".

A word of warning: if the first command in the test is "again", that will likely repeat the TEST command, sending Inform round in circles forever.

For complicated objects and commands, sometimes it's a good idea to develop the test commands at the same time that we're writing the source code itself. Each time we add a new rule or piece of behavior, we also add to that object's special test command something that will put that new feature to the test. This means that we can keep running the test command as we work and verify that everything is behaving as expected.

Sometimes we need to get a look at what is happening within the source itself. Many of the most annoying bugs come about because we're making some assumptions about what's true in the story world that differ from Inform's assumptions. When that happens, we may need to add something to the source to check that the variables are set to what we think, that certain parts of the source are being reached, and so on.

For instance, suppose we have a phrase like this:

To say key score:
   let count be the number of keys which are not carried by the player;
   if count is greater than 2 and the player is timid:
      say "You're still missing a lot of keys, bucko!"

Now, we expect this to print something, but perhaps it's not doing so when we had anticipated that it would. At some point when we think the count is greater than 2 and the player is timid, at least one of those things is not true. An easy way to check up on this is to add a showme line to the source, like so:

To say key score:
   let count be the number of keys which are not carried by the player;
   showme count;
   if count is greater than 2 and the player is timid:
      say "You're still missing a lot of keys, bucko!"

and this will then check the relevant number and print it to screen when this phrase is called, like so

"count" = number: 1

In this case, it looks like the count is not high enough to trigger the text, so we can concentrate on working out why that might be. Maybe we didn't correctly define something as a key, for instance.

WI §24.3 High-level debugging commands

If an object is not responding in the way we expect, it may be that we're wrong about where it is or about some of its current properties or relations. We can find our current location and the things around us by typing

>SHOWME
Boudoir - room
   four-poster bed - supporter
   yourself - person
   pillow

and similarly we can inquire about the status of a particular object during play by typing SHOWME and the object's name:

>SHOWME BAT
bat - thing
location: on the table in Locker Room
singular-named, improper-named; unlit, inedible, portable, patterned
printed name: "bat"
printed plural name: none
indefinite article: none
description: none
initial appearance: none

This will work even if we're not in the same location as the object we want shown.

Another common type of problem is one in which we type a command but Inform does not perform the action that we were expecting as a result. In some cases, this is because the command we're typing is actually triggering some other action. An easy way to check on this is to type ACTIONS before issuing the command that is behaving unsatisfactorily. Thus:

>ACTIONS
Actions listing on.
>JUMP
[jumping]
You jump on the spot.
[jumping - succeeded]

This tells us how Inform interpreted our input and whether the action was successful or failed for some reason. If the command is being understood as a different command than we expected, that may mean that we have made a mistake in our Understand instructions, and need to double-check these.

Sometimes, however, the action is being correctly understood, but the action rules that are firing are producing a result other than we'd like. If we want to see which rules are running, we can type

>RULES
Rules tracing now switched on. Type "rules off" to switch it off again, or "rules all" to include even rules which do not apply.
>JUMP
[Rule "announce items from multiple object lists rule" applies.]
[Rule "set pronouns from items from multiple object lists rule" applies.]
[Rule "before stage rule" applies.]
[Rule "instead stage rule" applies.]
[Rule "investigate player's awareness before action rule" applies.]
[Rule "player aware of his own actions rule" applies.]
[Rule "check stage rule" applies.]
[Rule "carry out stage rule" applies.]
[Rule "after stage rule" applies.]
[Rule "investigate player's awareness after action rule" applies.]
[Rule "report stage rule" applies.]
[Rule "report jumping rule" applies.]
You jump on the spot.
[Rule "last specific action-processing rule" applies.]
[Rule "A first turn sequence rule" applies.]
[Rule "every turn stage rule" applies.]
[Rule "A last turn sequence rule" applies.]
[Rule "notify score changes rule" applies.]
>

As we can see, RULES produces a lot of output, much of which is probably irrelevant to whatever problem we're tracking down. Nonetheless, knowing exactly which rule is printing undesirable output is helpful, especially if that rule comes out of an extension or some other source that we did not write ourselves: this output has told us that the text we saw came from the "report jumping rule".

To find out more about what is going on in specific rules, we can also turn to the Index tab under Actions and click through to that specific action. From there we will be able to see which rules are included, what responses they're writing, and where they were defined in the source text.

SCENES lists which scenes are currently playing and which are complete. This is valuable if scene-triggered events are not happening when we expect them to.

RANDOM sets the random number generator to a predictable seed. If we include this in a test command, it will guarantee that the subsequent behavior of the story is consistent across multiple playthroughs, which is helpful if we're trying to test something to do with, say, randomly wandering non-player characters.

RELATIONS lists all the relations defined in the story, except for things like support and containment that are part of the world model and are so numerous that the output would be overwhelming.

RESPONSES lists all the named responses established by all the extensions currently included. This can be informative, or it can be a bit overwhelming. Except where responses have been changed at runtime, the same information is available in a different form in the Index on Actions. If we're interested in a particular single response, digging into the actions index is probably the easiest way to find it.

If, however, we want a rapid overview of all the responses provided by a given extension (perhaps an extension we are ourselves writing), the RESPONSES command can be a help.

WI §24.4 Low-level debugging commands

There are also several debugging commands going back to the early days of interactive fiction, and relating in a simple way to objects and places. These can still come in handy for a quick and dirty resolution of a problem during gameplay, and are as follows.

PURLOIN moves an object to your possession, no matter where it is on the map, like so:

>PURLOIN TABLE
[Purloined.]
>I
You are carrying:
a table

Note that purloin does not consider the usual rules about whether something can be taken. In this case, we've just moved the table to our inventory even though it is a fixed in place supporter that could not be taken in the normal course of events.

Because purloin works on things that are far away as well as things that are close, it has to do a lot of extra parsing work and may take a moment or two to complete if we try it in a very large story. It is generally more efficient to give the player the relevant object using a testing command, like this:

Test me with "drop table" holding the table.

Nonetheless, there are occasionally times when we're halfway into a 2000-move story and suddenly realize we implemented a vital object in the wrong room, making the story unsolvable. We could fix the bug, press replay and return to this story state fairly quickly, but if we don't feel like waiting even that long, PURLOIN will resolve the issue.

ABSTRACT is PURLOIN's less useful cousin, allowing the player to move an object from one place to a specified other place, as in

Bar
You can see a table here.
>ABSTRACT KEY TO TABLE
[Abstracted.]
>LOOK
Bar
You can see a table (on which is a key) here.

GONEAR transports the player instantly to the vicinity of the named object, so for instance

>GONEAR GRAIN
Fertile Plain
You can see some grain here.

As a debugging command, this isn't protected in the ways that commands usually are. It's possible to type GONEAR NORTH and produce a run-time error when Inform tries to move the player into the object that represents the compass. Again, except in cases where we're tracing a problem very deep in an already running story, it is usually more practical to write a test command to put the player in the correct situation, as in

Test me with "eat grain" in the Fertile Plain.

VERIFY checks that the story file is intact rather than damaged, but it is hard to think of an occasion when this would be likely to arise within the Inform application. The command is a holdover from a time when data transfer was much slower and more error-prone, and it was plausible to have a story file of just a few hundred KB corrupted during transmission.

TREE creates a list of object containment. It is similar to SHOWME, but less elegant and thorough.

SCOPE lists the objects that are currently in scope for the player, which is to say, things that could be referred to when we're typing a typical command. Thus:

Bar
You can see a table here.
>SCOPE
1: yourself (574631)
2: a table (574759)

The following numbers are object IDs for these objects, which can distinguish items with identical names. It is likely that the output of this will not be terribly interesting or different from checking SHOWME, except in cases where the author is deliberately changing the scope to be something other than "the set of things that are visible in the room with the player right now". This usually involves the Deciding the scope of something activity (see the chapter on Activities).

SHOWHEAP shows how many bytes are currently free. This is usually not helpful.

SHOWVERB (verbname) lists the Understand information associated with a particular verb. Similar information, in a vastly more palatable form, is available in Index / Actions / Commands, so the one time SHOWVERB becomes useful is when Inform is considering the understand lines in the wrong order and producing a result we didn't want: SHOWVERB will show us the order in which the lines are being assessed. The challenge will then be to add conditions to the Understand lines to move them into the correct order.

Finally, TRACE (and its more advanced stages TRACE 2, TRACE 3, TRACE 4, and TRACE 5) will reveal things, more things than we ever wanted to know, about the assumptions being made by the parser when it takes in a command. In practice this information is almost never useful to an Inform 7 author.

There is no guarantee that any of these commands will make life better or that they won't crash the story or put it into an unwinnable state. There is also no absolute guarantee that they won't be withdrawn entirely from future versions of Inform. Consider them as Old High Magic, and treat accordingly.

WI §24.5 Adding new testing verbs and Release for Testing

As we saw in Chapter 2, we can mark some of our source text so that it will not be included in a finished story. This means that we can add special testing commands available to the author but not available to our eventual players. This is a good way to add our own suite of testing verbs to a story beyond the "Test me with…" features already described.

Here are some types of testing verbs that can be useful to add:

Chapter jumps. We might create test commands that took us to a later stage of the story (perhaps doing more setup than "Test me…" alone can handle).

Status information. We might create a test command that would show us status information beyond what's covered in the Standard Rules. For instance, if we had a story that heavily modeled the moods of other characters and we wanted to be able to check those moods at any time, we might add a SHOWMOOD command that would tell us about a character's emotional state.

Puzzle satisfaction lists. Some simulation-rich stories offer puzzles that can be solved in a variety of ways: for instance, a sealed glass box that can be smashed with any object that has been marked with the properties "hard" and "heavy". Later, we might want to be able to check which in-story objects would work as a solution to this puzzle, so we might create a command like

Listing hammers is an action out of world applying to nothing.
Understand "list hammers" as listing hammers.
Carry out listing hammers:
   say "These things can break the glass: [line break]";
   repeat with item running through portable hard heavy things:
      say "[item][line break]";

so that we can review that there are enough objects available and that the list doesn't include anything it shouldn't. In a small story this kind of thing is pretty easy to keep track of in the author's head. Large stories can contain thousands of objects, however, at which point it becomes valuable to have an automated method of verification.

Just occasionally, we might also want to build a version of a story that will allow beta-testers access to the debugging commands. This is especially relevant for long stories: if we're testing a story with a lot of playtime and the testers have already thoroughly reviewed the first portion of the story, we might want to let them have access to testing commands that fast-forward to later sections.

To do this, we can use the "Release for Testing" feature. Release for testing builds a version of the story that does include testing commands and any sections labeled "Not for release".

WI §24.6 Testing for thoroughness

The presence of actual bugs or defects is not the only thing we want to consider when testing a story. We may also want to check whether we have built the story with a consistent amount of depth.

Are there descriptions for everything the player might look at? If we've implemented special verbs, do they have appropriate reactions for all the different objects? If most objects in a story about restaurant reviewing have a special response to being tasted, for instance, it might be disappointing for the player to encounter late-added objects that don't.

Checking implementation thoroughness can be a laborious process, but there are a few things we can do to automate it. For instance, we might add to a not-for-release section a rule that checks for certain properties:

When play begins (this is the run property checks at the start of play rule):
   repeat with item running through things:
      if description of the item is "":
         say "[item] has no description."

This will confront us with a reminder of what we still need to fill in every time we start up the story.

There are also some extensions that are designed to assist with this, notably the massive Object Response Tests by Juhana Leinonen. Object Response Tests allows us to try out a long list of commands against any object in the story, so that we can quickly identify ones with nonsensical replies.

WI §24.7 Commands for beta-testers

Inform includes a command that is especially designed to help beta-testers report flaws: namely, TRANSCRIPT. A tester can type TRANSCRIPT (or just SCRIPT) at the beginning of the story in order to start generating a recording of everything that happens. She can then add her own annotations when something buggy or otherwise notable occurs (for instance by typing a standard symbol, such as *, followed by a note).

When she then sends us the completed transcript, we can look through for these symbols and note the problems the tester found in the context of the rest of the story's behavior. Having information about how she reached that position typically makes it much easier to reproduce the problem than if she gave only a general account of it.

WI §24.8 Help from the user community

Sometimes we get really stuck on a problem and despite all our best efforts cannot figure out how to solve it.

Fortunately, Inform has a lively and helpful community of users who are often willing to assist other authors. The easiest way to reach these users is to make a post at the intfiction forum at

https://intfiction.org/

and in particular to post Inform-related problems under the topic Inform 7 Development. Where possible, it's a good idea to post the example source that is causing trouble, and to make it as short as possible so that prospective helpers will not have to read any more than necessary in order to pinpoint the problem.

The user community is also a good place to find beta-testers who can try out our work and give feedback.