Ok, I'm out to break this down... So I'll comment on the parts and then anyone jump in and comment on my comments, if you like.
Code:
#charset "us-ascii"
#include <adv3.h>
#include <en_us.h>
//directions-for-use code snipped
/*
* The 'list tests' and 'list tests fully' commands can be used to list
* your test scripts from within the running game.
*/
#ifdef __DEBUG
Now this makes the contents only work if we're in debugging mode --which is automatic when we haven't created a for-release version?
Code:
DefineSystemAction(ListTests)
execSystemAction
Immediately I have a question, in that I cannot see what calls this method. It seems to be the primary driver for executing the testing script, so I would expect it to be linked in from the TEST X verb rule, but as far as I can tell, it's not.
So how does TADS know to fire this method? -- are we overwriting some existing TADS thing called SystemAction? -- Must be, I suppose.
Code:
{
This is a check to see that we're not ready to go.
Code:
if(allTests.lst.length == 0)
{
reportFailure('There are no test scripts defined in this game. ');
exit;
}
I say that SystemAction must be an existing TADS method because this seems to take over the handling of commands.
[research]
Oh, it's a class. That's weird.
Quote:
System action. These actions are for out-of-game meta-verbs (save, restore,
undo). These verbs take no objects, must be performed by the player (thus by
the player character, not an NPC), and consume no game clock time.
class SystemAction : IAction
Ah! We're defining a system action called ListTests. Uh-HUH. That makes some sense.
Code:
foreach(local testObj in allTests.lst)
{
"<<testObj.testName>>";
if(gAction.fully)
{
": ";
foreach(local txt in testObj.testList)
"<<txt>>/";
}
"\n";
}
}
fully = nil
;
And now we have the verb for it. All right, all is well in the universe.
Code:
VerbRule(ListTests)
('list' | 'l') 'tests' (| 'fully' -> fully)
: ListTestsAction
verbPhrase = 'list/listing test scripts'
;
/*
* The 'test X' command can be used with any Test object defined in the source code:
*/
DefineLiteralAction(Test)
execAction()
{
local target = getLiteral().toLower();
local script = allTests.valWhich({x: x.testName.toLower == target});
if (script)
script.run();
Ok, so the TEST X whirligig seems to run off this thing called script.run(), which in turn is a deep mystery.
Script is got from (I'm standing back and squinting more than understanding here)
pulling together everything in allTests, so it is presumably a list of allTest objects,
allTest being an object defined below and having a .run() method.
Code:
else
"Test sequence not found. ";
}
;
VerbRule(Test)
'test' singleLiteral
: TestAction
verbPhrase = 'test/testing (what)'
;
The individual test object... but in fact a class. So, Test is a class,
individual Test objects are not named, but pulled from a file, whereas
the list of unnamed Test objects does have a name, allTest.
Code:
class Test: object
testName = 'nil'
testList = [ 'z' ]
testFile = 'TEST_' + testName + '.TCMD'
Well, I don't understand what .run() is doing here rather than in allTests,
but anyway it seems somehow to fall through from the list to the individual
items in that list.
Code:
run
{
"Testing sequence: \"<<testName>>\". ";
local out = File.openTextFile(testFile, FileAccessWrite);
testList.forEach({x: out.writeFile('>' + x + '\n')});
out.closeFile();
setScriptFile(testFile);
Well that's just writing the actions out to the output file.
Code:
}
;
allTests: object
lst()
// list handling...
{
if (lst_ == nil)
initLst();
return lst_;
What if the list is empty? then initialize it and return the initialized version...
presuming lst_ is modified by initLst().
Code:
}
initLst()
{
lst_ = new Vector(50);
Ok, there it is.
Code:
local obj = firstObj();
while (obj != nil)
*while*? not *if*? ... that means sometimes this doesn't work to make obj not nil?
Code:
{
if(obj.ofKind(Test))
lst_.append(obj);
obj = nextObj(obj);
Ah, ok... we're filtering for only Test type objects. Which is why we use while.
Code:
}
lst_ = lst_.toList();
I have no idea what that does, but presumably it converts a string
to a list or something. Parses it, sorta.
Code:
}
valWhich(cond)
{
if (lst_ == nil)
initLst();
return lst_.valWhich(cond);
Ok, my brain just exploded. This is dealing with the tail end of the list in some tricky recursive fashion?
So, if the tail of the list is empty, we initialize it and presumably parse in the next line.
Then, whether it's empty or not, I guess, we call ourselves. Which means if the tail is empty, we initialize it and parse in the next line...
And we keep doing that until the line is all parsed in, and then we return-return-return everything in list form?
Ah, this is used in the original SystemAction thing, when we go looking
for a script of a specific name. So I guess it filters by name, going through the entire list of scripts available, recursively.
Code:
}
lst_ = nil
But none of this has happened yet, or it's already happened, so we set the tail to nil.
Code:
;
#endif // __DEBUG
End of the debugging-only block.
Fine, but where was the part where the script actions were actually run through the game?
Going for a second round...
Nope, not finding it. I mean, presumably this thing must somewhere actually
implement the verbs contained in the test objects, but it looks to me like it
has to be done in the .run() method and it's not done there. So I've got no
clue. Ideas, anyone?
Where does this thing get around to actually executing the commands?