From 20bb6bbbc71cf5efbeb449ce1376238dca89ff1d Mon Sep 17 00:00:00 2001 From: Susan Davis Date: Mon, 22 Jun 2026 16:53:28 -0400 Subject: [PATCH 01/14] fix url --- manual/modules/lib/pages/testing.adoc | 739 +++++++++++++++++++++++++- 1 file changed, 730 insertions(+), 9 deletions(-) diff --git a/manual/modules/lib/pages/testing.adoc b/manual/modules/lib/pages/testing.adoc index 45cfb64..93bb3bf 100644 --- a/manual/modules/lib/pages/testing.adoc +++ b/manual/modules/lib/pages/testing.adoc @@ -1062,7 +1062,7 @@ them fails, rather than having to investigate further. (test *) (set up *) (stoppable) (enter #cloakroom) - (stoppable) (perform [put #cloak #on #hook]) + (stoppable) (try [put #cloak #on #hook]) (run *) (stoppable) (enter #bar) @@ -1077,6 +1077,25 @@ your test easier to understand. Also note that we don't need to bother walking to the Foyer and then south to the Foyer Bar; just arriving there suffices for the test. +[source] +---- +#warn-about-blunderinga +(test *) +(set up *) + (stoppable) (enter #bar) + +(run *) + ~(player can see) + (collect words) + (stoppable) (try [dance]) + (into [in the dark | $]) + ~(message has been trampled) +---- + +We get one warning about not screwing around in the darkness before +there are any consequences; it's only on the second action in the dark +that things get screwed up. So let's test that case as well: + [source] ---- #destroy-message @@ -1087,7 +1106,9 @@ south to the Foyer Bar; just arriving there suffices for the test. (run *) ~(message has been trampled) - (stoppable) (try [dance]) + (collect words) + (stoppable) (try [dance]) + (into [blundering | $]) (message has been trampled) ---- @@ -1096,6 +1117,62 @@ rather than leaving them in `(run *)`: the test is testing that it's the _second_ time that you do something foolish in the dark that wipes out the message, not the first time when you're warned. +However, when we run this, it fails! The release 2 version of _Cloak +of Darkness_ implements the "don't screw around in the darkness" +warnings using `(select) . . . (or) . . . (stopping)`, which doesn't +reset between tests. (As of lDialog does not have a mechanism for resetting a +`(select)`.) _Cloak of Darkness_ wasn't written with unit tests in +mind, and not designing for testability has left us with some +"de-testable" code. + +To fix it, we'll need to edit `cloak.dg`, bump it up to `(story +release 3)`, and rewrite the offending code, which is the `(prevent +$Action)` predicate in `#bar`. The `(select)` only has two states, +so it's easy to replace it with a global flag, like this: + +[source] +---- +~(warned the player about darkness) + +(prevent $Action) + (current room #bar) + ~(player can see) + ~(command $Action) + ($Action = [$Verb | $]) + ~($Verb is one of [go leave enter look]) + (if) (warned the player about darkness) (then) + Blundering around in the dark isn't a good idea! + (now) (message has been trampled) + (else) + In the dark? You could easily disturb something. + (now) (warned the player about darkness) + (endif) +---- + +...and once we add `(now) ~(warned the player about darkness)` to +`(clean up $)`, the test works. While we're at it, we should have +`#warn-about-blundering` check the status of our new flag: + +[source] +---- +#warn-about-blunderinga +(test *) +(set up *) + (stoppable) (enter #bar) + +(run *) + ~(player can see) + (collect words) + (stoppable) (try [dance]) + (into [in the dark | $]) + ~(message has been trampled) + (warned the player about darkness) +---- + +This doesn't mean that you can't use `(select)` in your game if you +want to test it, just that you limit its use to displaying text to the +player, and not hang important side effects on any of the choices. + [source] ---- #score-for-seeing-message @@ -1115,11 +1192,11 @@ out the message, not the first time when you're warned. (current score 2) ---- -And now we finally get to win! +Now we finally get to win! ...except that we don't. Release 2 of _Cloak of Darkness_ doesn't -actually let you read the message. To fix that, we need to bump up -`cloak.dg` to `(story release 3)`, and add the following: +actually let you read the message! To fix that, we need to edit our +release 3 version of `cloak.dg` to add the following: [source] ---- @@ -1130,7 +1207,7 @@ actually let you read the message. To fix that, we need to bump up ...and _now_ we get to win. -Let's test the verb that Release 2 was expecting, to be safe: +Let's test the verb that release 2 was expecting, to be safe: [source] ---- @@ -1180,7 +1257,649 @@ Finally, let's test the defeat condition: (current score 1) ---- -...and with that, we've written, in Dialog, a full set of tests for our game. +...and with that, we've written, in Dialog, a full set of unit tests +for our game. + +You'll notice that we didn't actually look at much of the game text in +any of those tests -- just enough to validate that our program code is +correct, and preferring to test programatically rather than to look at +the output. We can look at a little bit of the code with `(collect +words)`, but it's awkward to examine long stretches of text, or to +concern ourselves overmuch with how the output is presented to the +user. For tests like that, it's better to use all-up testing, with a +real interpreter. + +[#integ] +== All-Up Tests in the Interpreter + +Unit testing tests small units of your code, and validates that it's +doing what you expected it to do, and that you didn't break anything +by making changes. Unit tests are small, quick to write, and lightning +fast to execute. On modern hardware, you can execute hundreds or +thousands of them in a second (up to 16383 per test file), and get +answers very quickly. + +End-to-end, or all-up, tests, sometimes called _integration tests_, +test your final game code in a real environment that simulates real +player input. Strictly speaking, integration tests concern themselves +with whether you've hooked together all of the modules of your game +correctly, but the name frequently sticks to any testing of your full +program. They take longer to write, they're more fragile, and they +take longer to compile and execute than unit tests do, but the payoff +is that what you're testing is exactly what the player will see, and +it's easier to test chunks of text in the interpreter than it is in a +unit test. + +Having both kinds of tests is important, as each style can find +problems that are difficult for the other kind to turn up. + +[#diff] +=== Simple Tests With `diff` + +The simplest way to test in the interpreter is simply to feed a +predefined input file to it, capture the output in a text file, and +compare it with a known good file from a good run of the game. For +this, we use the built-in `diff` tool that's present on Mac and Linux +systems. (For Windows users, see the xref:ROOT:software.adoc[Software +Page].) + +The first thing that we need to do is to create the input file that +we're going to feed to the interpreter, containing the commands that +a winning player would type in the course of their playthrough: + +[source] +---- +w +hang cloak on hook +e +s +read message +---- + +(Yes, _Cloak of Darkness_ really is just five moves long if you know +the solution.) + +Now we can run the game, taking the input from our walkthrough file: + +---- +$ dgdebug -u cloak.dg stdlib.dg < cloak.in +Hurrying through the rainswept November night, you're glad to see the bright +lights of the Opera House. It's surprising that there aren't more people about +but, hey, what do you expect in a cheap demo game...? + +Cloak of Darkness +A port of Roger Firth's reference game by Linus Åkesson. +Release 3. Serial number DEBUG. +Dialog Interactive Debugger (dgdebug) version 1c/02. Library version 1.2.3. + +Foyer of the Opera House +<[me] You> are standing in a spacious hall, splendidly decorated in red and +gold, with glittering chandeliers overhead. The entrance from the street is to +the , and there are doorways and . + +> w +You walk west. + +Cloakroom +The walls of this small room were clearly once lined with hooks, though now +<[small brass hook] only one> remains. The exit is a door to the . + +> hang cloak on hook +(first attempting to remove the velvet cloak) +You take off the velvet cloak. + +You put the velvet cloak on the small brass hook. + +(Your score has gone up by one point.) + +> e +You walk east. + +Foyer of the Opera House +<[me] You> are standing in a spacious hall, splendidly decorated in red and +gold, with glittering chandeliers overhead. The entrance from the street is to +the , and there are doorways and . + +> s +You walk south. + +Foyer Bar +The bar, much rougher than you'd have guessed after the opulence of the foyer +to the north, is completely empty. There seems to be some sort of <[scrawled +message] message> scrawled in the sawdust on the floor. + +> read message +The message, neatly marked in the sawdust, reads... + + *** You have won *** + +Game over. You scored 2 points out of 2. + +Would you like to: + the last move, + a saved position, + the program, + or from the beginning? +> +---- + +The `<` character on the command line tells the interpreter to get its +input from the file you mention next, `cloak.in`, rather than from the +keyboard. There's a corresponding `>` character that diverts the +output from the screen into a file, so `dgdebug -u cloak.dg stdlib.dg +< cloak.in > cloak.out` would dump all of that output into a file +named `cloak.out`. (In fact, we're going to do precisely that in a +moment.) + +One thing that jumps out at us, though, is that the banner printed at +the start of the game specifies the game release, serial number, +compiler version, and library version. That's fine if you're running +the game as a one-off, but for an automated test, you really don't +want your test to fail every time you update the game, the Dialog +software, or the standard library. We can make that header go away by +adding a file, `no-banner.dg`, as follows: + +[source] +---- +%% no-banner.dg +(banner) +---- + +Now if you include that before `stdlib.dg`, it will override the +`(banner)` predicate that prints the banner with all of that +changeable information, and your tests won't fail just because you +upgraded something. + +If your game overrides `(program entry point)` or `(banner)`, and you +want to specifically test your opening text, you can leave out +`no-banner` and get your input from `/dev/null`, which will cause the +game to exit immediately: + +---- +$ dgdebug -u su-101.dg delphinus-crew.dg crew.dg eridanus.dg controls.dg \ +anomaly.dg hyperspace.dg ship-damage.dg eva.dg mtd.dg shuttle.dg \ +scanner.dg probe.dg systems.dg no-weapons.dg no-damage.dg \ +sensor-display.dg sensor.dg arc.dg maneuver.dg schema.dg sector.dg \ +bearing.dg grid.dg d6.dg time.dg utils.dg stdlib.dg < /dev/null + +Our galaxy is an endless ocean of stars. Plying that ocean are the +ships of the Stellar Union. Their mandate: + + * Keep the peace among the stars. + * Explore the galaxy and its many planets. + * Follow knowledge like a sinking star, + beyond the utmost bound of intelligent thought. + +Upholding that mandate is the starship Eridanus, on a three-year +mission of discovery. + +That Untravelled World, Whose Margin Fades +Stellar Union episode 101 (release 1), by Susan Davis. +Type CREDITS for a full list of credits. + +"Captain's log, Earth date March 5, 2273. The Eridanus has been sent +to reestablish contact with SUS Delphinus, which has been out of +contact with Stellar Command for over three weeks. In her last +communication with Starbase 37, she reported that she was +investigating a spatial anomaly in Quadrant IV/103-37. We are +proceeding to that quadrant to search for our missing sister ship, and +to render assistance if necessary." + +You press the "End Recording" button on the arm of your captain's chair, +and the recording light goes out. + +Bridge (in the captain's chair) +The bridge is much smaller than is typical for a Stellar Union +starship. Aside from the usual captain's chair behind the helm and +navigation stations, there are only two other workstations: one for +engineering and one for the science officer. A pair of double doors +leads aft. + +Chief Tesfaye is sitting at the helm station. Ensign Washington is +sitting at the navigation station. Senior Lieutenant V'ronek is +sitting at the science station. + +Through the forward windows, you can see the stars gently streaking +toward you as the ship moves at faster-than-light speed. + +"Now entering quadrant IV/103-37," Ensign Washington says. + +> +---- + +Having suppressed the banner, we can create an output file with +`dgdebug -u cloak.dg no-banner.dg stdlib.dg < cloak.dg > +cloak.out`. That will give us the output we saw earlier, minus the +banner, in a file that we can later test against. Since we've reviewed +it and seen that the output is exactly what we expect, we can "bless" +it as being the correct output that we expect by copying it to another +file -- we'll call it `cloak.gold` -- which we can compare against any +other versions of `cloak.out` that are generated by future versions of +our game. + +The basic tool to compare files is `diff`, which will succeed silently +if the files are the same, or fail noisily, showing just how the two +files differ. (There's another tool, `meld`, which does the same +thing, but gives more elaborate output. For testing purposes, the two +are interchangeable.) `diff cloak.out cloak.gold` will tell you +whether your game is producing the output that you expect. + +In summary, you can test a game such as _Cloak of Darkness_ through +the debugger like this: + +[source] +---- +$ dgdebug -u cloak.dg no-banner.dg stdlib.dg < cloak.in > cloak.out +$ diff cloak.out cloak.gold +---- + +If you get no output back from that, then your game passed its test. + +One final caveat: all of the above assumes that you're on a Linux or +Macintosh system. Windows users will either use a slightly different +set of tools and syntax, or will need to run a tool such as `cygwin` +that emulates a Linux shell and its associated commands. + +[#branches] +==== Testing Multiple Branches + +Remarkably, for such a small game, _Cloak of Darkness_ is a game with +multiple endings: you can preserve the message in the sawdust and win, +or you can accidentally destroy it, and lose. The two endings are +mutually exclusive, so there's no single walkthrough that can cover +them both. + +We'll need to set up a second input file to handle the other (losing) +ending. We'll rename our original walkthrough from `cloak.in` to +`win.in`, and create a new walkthrough, `lose.in` as follows: + +[source] +---- +n +s +dance +dance +n +w +hang cloak on hook +e +s +read message +---- + +When we run it once, we'll get a `lose.out` file that we can inspect, +and copy to `lose.gold` if everything is as we expect. Then our +sequence for testing both paths will be: + +[source] +---- +$ dgdebug -u cloak.dg no-banner.dg stdlib.dg < win.in > win.out +$ diff win.out win.gold +$ dgdebug -u cloak.dg no-banner.dg stdlib.dg < lose.in > lose.out +$ diff lose.out lose.gold +---- + +Empty output from running all of the above means that the game has +succeeded on both branches of its walkthrough. + +[#multiplatform] +==== Testing For Multiple Platforms + +One of Dialog's strengths is that it can produce games playable on +multiple platforms: on a Z-machine interpreter, in a web browser, or +on vintage computers such as the Commodore 64. (The C64 is the only +supported vintage platform as of Dialog 1c/02 and Å-machine 1.0.2, but +others may be added later.) In principle, a Dialog game should run +equally well on all platforms, so long as it fits within the resource +limitations of the Z-machine or the Commodore 64. In practice, testing +on each of the platforms on which you intend to release your game is a +good practice. + +Up till now, we've been using the debugger to test; adding three more +platforms to that, times two plot branches, will give us eight +different tests to run in total: win-debugger, win-zmachine, win-web, +win-c64, lose-debugger, lose-zmachine, lose-web, and lose-c64. We're +using the same input for each platform in each branch, so we only need +`win.in` and `lose.in` and not eight different input files. + +We've already worked out how to test with the debugger, so all we have +to do is rename the relevant files: + +[source] +---- +$ dgdebug -u cloak.dg no-banner.dg stdlib.dg < win.in > win-debugger.out +$ diff win-debugger.out win-debugger.gold +$ dgdebug -u cloak.dg no-banner.dg stdlib.dg < lose.in > lose-debugger.out +$ diff lose-debugger.out lose-debugger.gold +---- + +For the Z-machine, we'll need to run the compiler to compile our game +into Z-machine code. _Cloak of Darkness_ is small enough to fit in a +version 5 Z-machine file (`z5`); larger games might go in a `z8` or a +`zblorb`. Again, for testing purposes, we'll also need to pull in +`no-banner.dg`. + +[source] +---- +$ dialogc -t z5 cloak.dg no-banner.dg stdlib.dg -o cloak.z5 +---- + +...and that will give us `cloak.z5` which will run in any Z-machine +interpreter. + +To actually test our code in the interpreter, we'll need two other +pieces of software: the `frotz` interpreter (specifically, its +`dfrotz` variant), and `echofrotz.py` from the Dialog source +distribution. You'll find `echofrotz.py` in the `bin` directory when +you unpack the Dialog source. See the xref:ROOT:software.adoc[Software Page] +for information about where to find the Dialog source code, and +`frotz`. You'll also need to have a Python interpreter installed; see +youru system documentation about how to do that. + +Now we can run `echofrotz` to test our Z-machine test cases: + +[source] +---- +$ echofrotz.py -m -q cloak-test.z5 win-zmachine.out +$ diff win-zmachine.out win-zmachine.gold +$ echofrotz.py -m -q cloak-test.z5 lose-zmachine.out +$ diff lose-zmachine.out lose-zmachine.gold +---- + +We'll need a copy of the Å-machine distribution in order to test the +Å-machine version of our game on both the web and the Commodore 64. +See the xref:ROOT:software.adoc[Software Page] for where you can find it. +Specifically, you'll need the `aamrun` and `aambox` utilities from it, +installed somewhere in your path. + +We have two scripts to actually run the games: `aamrun.py`, which runs +the web interpreter, and `6502run.py`, which runs the Commodore 64 +executable in an emulator. They're found in the `bin` directory in the +Dialog source distribution. + +[source] +---- +$ dialogc -t aa cloak.dg no-banner.dg stdlib.dg -o cloak.aastory +$ aamrun.py cloak.aastory win-web.out +$ diff win-web.out win-web.gold +$ aamrun.py cloak.aastory lose-web.out +$ diff lose-web.out lose-web.gold +$ 6502run.py cloak.aastory win-c64.out +$ diff win-c64.out win-c64.gold +$ 6502run.py cloak.aastory lose-c64.out +$ diff lose-c64.out lose-c64.gold +---- + +...and with that, we've thoroughly tested our game, with both unit and +in-browser tests. + +[#regtest] +=== Using `regtest.py` + +Another alternative for doing all-up tests is the `regtest.py` script, +by Andrew Plotkin. Like the other Python scripts that we've been +using, it can be found in the `bin` directory in the Dialog source +distribution, or from +link:https://github.com/erkyrath/plotex/blob/master/regtest.py[https://github.com/erkyrath/plotex/blob/master/regtest.py]. +`regtest.py` mashes together the `.in` and `.out` files +that we used in our simple `diff` tests into a single `.regtest` file, +and lets you run multiple tests out of the same file. + +For example, if we wanted to test both the win and lose conditions +from _Cloak of Darkness_, we could write something like this: + +---- +* win + +Hurrying through the rainswept November night, you're glad to see the bright +lights of the Opera House. It's surprising that there aren't more people about +but, hey, what do you expect in a cheap demo game...? + +You are standing in a spacious hall, splendidly decorated in red and gold, with +glittering chandeliers overhead. The entrance from the street is to the north, +and there are doorways south and west. + +> w + +You walk west. + +The walls of this small room were clearly once lined with hooks, though now only +one remains. The exit is a door to the east. + +> x me + +You have no possessions. You're wearing a velvet cloak. + +> remove cloak + +You take off the velvet cloak. + +> put cloak on hook + +You put the velvet cloak on the small brass hook. + +(Your score has gone up by one point.) + +> e + +You walk east. + +You are standing in a spacious hall, splendidly decorated in red and gold, with +glittering chandeliers overhead. The entrance from the street is to the north, +and there are doorways south and west. + +> x gold + +(I only understood you as far as wanting to examine something.) + +> s + +You walk south. + +The bar, much rougher than you'd have guessed after the opulence of the foyer to +the north, is completely empty. There seems to be some sort of message scrawled +in the sawdust on the floor. + +> x message + +The message, neatly marked in the sawdust, reads... + +Game over. You scored 2 points out of 2. + +Would you like to: + UNDO the last move, + RESTORE a saved position, + QUIT the program, + or RESTART from the beginning? +> quit +Thanks for playing! + +* lose + +Hurrying through the rainswept November night, you're glad to see the bright +lights of the Opera House. It's surprising that there aren't more people about +but, hey, what do you expect in a cheap demo game...? + +You are standing in a spacious hall, splendidly decorated in red and gold, with +glittering chandeliers overhead. The entrance from the street is to the north, +and there are doorways south and west. + +> n + +You've only just arrived, and besides, the weather outside seems to be getting +worse. + +> s + +You walk south. + +In the dark +You are surrounded by darkness. + +> dance + +In the dark? You could easily disturb something. + +> dance + +Blundering around in the dark isn't a good idea! + +> n + +You feel your way north. + +You are standing in a spacious hall, splendidly decorated in red and gold, with +glittering chandeliers overhead. The entrance from the street is to the north, +and there are doorways south and west. + +> w + +You walk west. + +The walls of this small room were clearly once lined with hooks, though now only +one remains. The exit is a door to the east. + +> put cloak on hook + +(first attempting to remove the velvet cloak) +You put the velvet cloak on the small brass hook. + +(Your score has gone up by one point.) + +> e + +You walk east. + +You are standing in a spacious hall, splendidly decorated in red and gold, with +glittering chandeliers overhead. The entrance from the street is to the north, +and there are doorways south and west. + +> s + +You walk south. + +The bar, much rougher than you'd have guessed after the opulence of the foyer to +the north, is completely empty. There seems to be some sort of message scrawled +in the sawdust on the floor. + +> x message + +The message has been carelessly trampled, making it difficult to read. You can +just distinguish the words... + +Game over. You scored 1 point out of 2. + +Would you like to: + UNDO the last move, + RESTORE a saved position, + QUIT the program, + or RESTART from the beginning? +---- + +You'll notice that we've skipped a couple of lines of output. Unlike +`diff`, which doesn't understand what it has been asked to compare, +and which needs to see every single character be the same, +`regtest.py` lets you skip over lines of output that are less +important. In this case, the victory and defeat messages begin with +asterisks, and `regtest.py` uses asterisks to identify different test +cases, and to set up other options, so we have to leave them out. + +When we run the above, with + +[source] +---- +$ regtest.py --game cloak.z5 --interpreter dfrotz cloak.regtest +---- + +we'll get success. If anything failed, we'd get a list of failures, +and a total count of the number of things that failed. + +`regtest.py` runs the same game file in the same interpreter for all +of the tests in a given `.regtest` file, so we can't write four +separate sets of `win` and `lose` in the same file. But we can make +the `.regtest` file match the output from all four of our platforms, +and run the same `.regtest` multiple times. + +You can find the full documentation for `regtest.py` +link:https://eblong.com/zarf/plotex/regtest.html[here], including +several examples. + +[#skein] +=== `dgt` and the Skein + +A drawback of both `diff` and `regtest.py` is that they're designed to +walk through a game in one single path, and show the output from only +that path. As we saw earier, testing multiple paths involves setting +up a separate test for each path, with input and "blessed" output +files, and if you make a change to part of your game, you'll have to +update all of your different test files to reflect it. + +A solution to that is Howard Ship's `dgt` tool, which includes a +feature called the _Skein_. The Skein allows you to interactively walk +through your game through multiple branching paths, and "bless" each +branch's output when it's correct. You can then re-run the Skein after +making changes to your game, and confirm that you didn't break anything. + +For details on `dgt` and the Skein, see the `dgt` documentation. Details +on where to find the `dgt` software can be found on the +xref:ROOT:software.adoc[Software Page]. + +[#make] +== Pulling It All Together With `make` + +In our previous examples, we typed lots of commands to build and test +our games. But having to remember a long sequence of commands is +error-prone -- it's easy to forget one, or to leave out an option +somewhere -- and having to type them all in every time that you change +something is time-consuming, and can tempt you to skip testing steps. +A better solution is to automate your project build, so that you can +run the unit tests, compile your code, and run your all-up tests for +all of your platforms and plot branches with a single command. + +[#makefile] +=== `Makefile` Basics + +TODO + +=== Testing With `make` + +TODO + +=== Building Releases + +TODO + +[#dgt] +=== Building With `dgt` Instead + +If you're using the `dgt` tool to test with the Skein (or even if +you're not), you can use `dgt` as your build tool, instead of `make`. +`dgt`'s build facilities are simpler to use than `make`'s, but less +powerful. If you're making a typical game that's implemented in a +single `.dg` file, then `dgt` might be a more convenient option. If +you're using lots of extensions, need to customize your build, or want +a different project layout than `dgt` requires, you might need to +stick with `make`. See the `dgt` documentation for details. + +[#debugging] +== Troubleshooting Broken Tests + +TODO + +[#pitfalls] +=== Simple Pitfalls + +TODO + +[#output] +=== Diagnostic Output + +TODO + +[#dgdebug] +=== Using `dgdebug` and its Predicates + +TODO [#summary] == Summary @@ -1196,7 +1915,9 @@ Here's a summary of the advice from this chapter: - Start each test from a known state, and reset to that state with `(set up $)` and `(clean up $)` before running the next test. - Use fixtures to keep your actual test cases simple. -- Test in depth, using both unit and integration tests. +- Design for testability, and don't write "de-testable" code that + can't easily be tested. +- Test in depth, using both unit and all-up tests. - Automate your testing process with `make` or `dgt`. - Test on all platforms for which you'll be releasing: Z-machine, web, and vintage computing hardware. @@ -1206,5 +1927,5 @@ Here's a summary of the advice from this chapter: Software testing is a much bigger topic than just the introduction to it presented here. More good advice about it can be found in books on -the topic, particularly ones focused on modern automated testing, and +the topic, particularly those focused on modern automated testing, and on test-driven development. From 6973a5aff0b7821d5c7fdc9b10759882b00448c3 Mon Sep 17 00:00:00 2001 From: Susan Davis Date: Mon, 22 Jun 2026 17:08:28 -0400 Subject: [PATCH 02/14] more text --- manual/modules/lib/pages/testing.adoc | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/manual/modules/lib/pages/testing.adoc b/manual/modules/lib/pages/testing.adoc index 93bb3bf..e47e429 100644 --- a/manual/modules/lib/pages/testing.adoc +++ b/manual/modules/lib/pages/testing.adoc @@ -1827,12 +1827,15 @@ several examples. [#skein] === `dgt` and the Skein -A drawback of both `diff` and `regtest.py` is that they're designed to -walk through a game in one single path, and show the output from only -that path. As we saw earier, testing multiple paths involves setting -up a separate test for each path, with input and "blessed" output -files, and if you make a change to part of your game, you'll have to -update all of your different test files to reflect it. +A drawback of using `diff` is that it only really walks through a game +in one single path, and shows the output from only that path. As we +saw earier, testing multiple paths involves setting up a separate test +for each path, with input and "blessed" output files, and if you make +a change to part of your game, you'll have to update all of your +different test files to reflect it. `regtest.py` lets you run multiple +test cases that can account for multiple branches, but you're still +doing a lot of manual management, and still potentially need to fix +the same change in multiple places. A solution to that is Howard Ship's `dgt` tool, which includes a feature called the _Skein_. The Skein allows you to interactively walk @@ -1859,7 +1862,14 @@ all of your platforms and plot branches with a single command. [#makefile] === `Makefile` Basics -TODO +`make` is the standard build utility on Linux, the Macintosh, and other +systems. At the time of writing, it is fifty years old, and Dialog +itself is built using `make`. There are a lot of features and concepts +in `make`; here, we'll cover only the bare minimum needed to test and +build a Dialog game. + +`make` is controlled by a `Makefile`, which it'll expect to find in +the current directory. ( === Testing With `make` From 8aefa8b41f3a8e37277be20cf9eb813000bc944d Mon Sep 17 00:00:00 2001 From: Susan Davis Date: Mon, 22 Jun 2026 17:13:40 -0400 Subject: [PATCH 03/14] more text --- manual/modules/lib/pages/testing.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manual/modules/lib/pages/testing.adoc b/manual/modules/lib/pages/testing.adoc index e47e429..8df1219 100644 --- a/manual/modules/lib/pages/testing.adoc +++ b/manual/modules/lib/pages/testing.adoc @@ -1869,7 +1869,7 @@ in `make`; here, we'll cover only the bare minimum needed to test and build a Dialog game. `make` is controlled by a `Makefile`, which it'll expect to find in -the current directory. ( +the current directory. === Testing With `make` From 968d103434e7f486b7fa1feb4939f5a8783d5e6c Mon Sep 17 00:00:00 2001 From: Susan Davis Date: Tue, 23 Jun 2026 09:37:27 -0400 Subject: [PATCH 04/14] test all three platforms --- test/regtest/Makefile | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/test/regtest/Makefile b/test/regtest/Makefile index 5326ffe..beac7f7 100644 --- a/test/regtest/Makefile +++ b/test/regtest/Makefile @@ -1,15 +1,29 @@ REGTEST=../../bin/regtest.py -v -DIALOGC=../../src/dialogc -t zblorb +DIALOGC=../../src/dialogc +STDLIB=../../stdlib.dg +AAMRUN=../../bin/aamrun.py +AAMBOX=../../bin/6502run.py -all: test +all: test clean -cloak.zblorb: cloak.dg - $(DIALOGC) cloak.dg ../../stdlib.dg +test: test-zmachine test-web test-c64 -test: cloak.zblorb - $(REGTEST) --game cloak.zblorb --interpreter dfrotz cloak.regtest +%.zblorb: %.dg + $(DIALOGC) -t zblorb $< $(STDLIB) + +%.aastory: %.dg + $(DIALOGC) -t aa $< $(STDLIB) + +test-zmachine: cloak.zblorb cloak.regtest + $(REGTEST) --interpreter dfrotz --game $^ + +test-web: cloak.aastory cloak.regtest + $(REGTEST) --interpreter $(AAMRUN) --game $^ + +test-c64: cloak.aastory cloak.regtest + $(REGTEST) --interpreter $(AAMBOX) --game $^ clean: - rm -f *.zblorb + rm -f *.zblorb *.aastory -.PHONY: all test clean +.PHONY: all test clean test-zmachine test-web test-c64 From 2685ce37868bef939de355c0f0fb0ce26355918e Mon Sep 17 00:00:00 2001 From: Susan Davis Date: Tue, 23 Jun 2026 09:39:09 -0400 Subject: [PATCH 05/14] be explicit about filenames --- test/regtest/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/regtest/Makefile b/test/regtest/Makefile index beac7f7..16cd507 100644 --- a/test/regtest/Makefile +++ b/test/regtest/Makefile @@ -9,10 +9,10 @@ all: test clean test: test-zmachine test-web test-c64 %.zblorb: %.dg - $(DIALOGC) -t zblorb $< $(STDLIB) + $(DIALOGC) -t zblorb $< $(STDLIB) -o $@ %.aastory: %.dg - $(DIALOGC) -t aa $< $(STDLIB) + $(DIALOGC) -t aa $< $(STDLIB) -o $@ test-zmachine: cloak.zblorb cloak.regtest $(REGTEST) --interpreter dfrotz --game $^ From 4ecffe7ff7437aba7e527d2a20a47b826e76a3c8 Mon Sep 17 00:00:00 2001 From: Susan Davis Date: Tue, 23 Jun 2026 09:46:22 -0400 Subject: [PATCH 06/14] cloak only needs a z5 --- test/regtest/Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/regtest/Makefile b/test/regtest/Makefile index 16cd507..656f1c1 100644 --- a/test/regtest/Makefile +++ b/test/regtest/Makefile @@ -8,13 +8,13 @@ all: test clean test: test-zmachine test-web test-c64 -%.zblorb: %.dg - $(DIALOGC) -t zblorb $< $(STDLIB) -o $@ +%.z5: %.dg + $(DIALOGC) -t z5 $< $(STDLIB) -o $@ %.aastory: %.dg $(DIALOGC) -t aa $< $(STDLIB) -o $@ -test-zmachine: cloak.zblorb cloak.regtest +test-zmachine: cloak.z5 cloak.regtest $(REGTEST) --interpreter dfrotz --game $^ test-web: cloak.aastory cloak.regtest @@ -24,6 +24,6 @@ test-c64: cloak.aastory cloak.regtest $(REGTEST) --interpreter $(AAMBOX) --game $^ clean: - rm -f *.zblorb *.aastory + rm -f *.z5 *.aastory .PHONY: all test clean test-zmachine test-web test-c64 From ce0e38f0cb5792899f9c5f2191755a1a44ef6734 Mon Sep 17 00:00:00 2001 From: Susan Davis Date: Tue, 23 Jun 2026 10:47:43 -0400 Subject: [PATCH 07/14] Makefile basics --- manual/modules/lib/pages/testing.adoc | 115 +++++++++++++++++++++++++- test/regtest/Makefile | 7 +- 2 files changed, 120 insertions(+), 2 deletions(-) diff --git a/manual/modules/lib/pages/testing.adoc b/manual/modules/lib/pages/testing.adoc index 8df1219..ffce679 100644 --- a/manual/modules/lib/pages/testing.adoc +++ b/manual/modules/lib/pages/testing.adoc @@ -1869,7 +1869,120 @@ in `make`; here, we'll cover only the bare minimum needed to test and build a Dialog game. `make` is controlled by a `Makefile`, which it'll expect to find in -the current directory. +the current directory. A `Makefile` is a list of _rules_, each of which +can have one or more _commands_ associated with it; it can also define +_variables_ for its own use. + +A rule consists of a _target_ and a list of _prerequisites_, separated +by a colon. Here's a simple rule, with target `test` and three prerequisites: + +[source] +---- +test: test-zmachine test-web test-c64 +---- + +If we were to put that in a `Makefile` and run `make test`, `make` +would try to build `test-zmachine`, `test-web`, and `test-c64` in +response. If `make` can't find a rule to build one of the prerequisites, +and can't find a file of that name, it will fail and complain about +that. If we don't specify anything when you run `make`, it'll try +to build a target named `all`. + +Targets can be phony, like `all` and `test`, or they can be the names +of actual files. Here's an example of a rule for making `cloak.z5`: + +[source] +---- +cloak.zblorb: cloak.dg + ../../src/dialogc -t zblorb cloak.dg ../../stdlib.dg -o cloak.zblorb +---- + +Now if we try `make cloak.z5`, it will run the compiler with those +arguments, which will produce `cloak.z5` as its output. If we run +`make cloak.z5` again, it will see that `cloak.z5` is already there, +and do nothing. But since `cloak.dg` is a prerequisite, if we edit +`cloak.dg` to make changes, and run `make cloak.z5` again, now `make` +will run the compiler again, because the time stamp on the +prerequisite `cloak.dg` is now newer than the target file. You can +have multiple prerequisites for a target; `make` will run the commands +again if any of them are newer than the target. + +You'll note that the command is indented. You must indent every +command in a rule *with a tab*, and not with spaces. `make` won't +recognize a command as a command if it the line doesn't start with a +tab character. + +For a phony target, such as `test`, `make` will always run. You can +declare a target to be phony by making it a prerequisite of the phony +target `.PHONY`, like this: + +[source] +---- +all: test clean + +test: test-unit test-zmachine test-web test-c64 + +clean: + rm -rf *.zblorb *.aastory + +.PHONY: all test clean test-unit +---- + +`make` also lets you define variables. This is handy when you're +dealing with files in other directories, or if you have a command or +library that appears in many rules, which you might want to change in +only one place if its location or arguments need to change. Variables +are declared using an `=` sign, and they're referenced in parentheses +after a `$` sign, like this: + +[source] +---- +REGTEST=../../bin/regtest.py -v +DIALOGC=../../src/dialogc +STDLIB=../../stdlib.dg + +cloak.z5: cloak.dg + $(DIALOGC) -t zblorb cloak.dg $(STDLIB) -o cloak.zblorb + +test-zmachine: cloak.zblorb cloak.regtest + $(REGTEST) --interpreter dfrotz --game cloak.zblorb cloak.regtest +---- + +`make` defines a number of special variables for use in commands: + +- `$@` refers to the current target. + +- `$<` refers to the first (leftmost) prerequisite in the list. + +- `$^` refers to all of the prerequisites, however many there are. + +So we could rewrite the above as + +[source] +---- +cloak.zblorb: cloak.dg + $(DIALOGC) -t zblorb $< $(STDLIB) -o $@ + +test-zmachine: cloak.zblorb cloak.regtest + $(REGTEST) --interpreter dfrotz --game $^ +---- + +Finally, `make` has a special pattern for making one file out of +another, when they only differ by their extensions. We could write a +general rule for compiling _any_ file into an identically named +`.zblorb`, for example, and use it to test multiple games at once: + +[source] +---- +%.zblorb: $.dg + $(DIALOGC) -t zblorb $< $(STDLIB) -o $@ + +test-cloak-zmachine: cloak.zblorb cloak.regtest + $(REGTEST) --interpreter dfrotz --game $^ + +test-impossible-zmachine: impossible.zblorb impossible.regtest + $(REGTEST) --interpreter dfrotz --game $^ +---- === Testing With `make` diff --git a/test/regtest/Makefile b/test/regtest/Makefile index 656f1c1..6ea1216 100644 --- a/test/regtest/Makefile +++ b/test/regtest/Makefile @@ -3,10 +3,12 @@ DIALOGC=../../src/dialogc STDLIB=../../stdlib.dg AAMRUN=../../bin/aamrun.py AAMBOX=../../bin/6502run.py +DGDEBUG=../../src/dgdebug -u +UNIT=../../unit.dg all: test clean -test: test-zmachine test-web test-c64 +test: test-unit test-zmachine test-web test-c64 %.z5: %.dg $(DIALOGC) -t z5 $< $(STDLIB) -o $@ @@ -14,6 +16,9 @@ test: test-zmachine test-web test-c64 %.aastory: %.dg $(DIALOGC) -t aa $< $(STDLIB) -o $@ +test-unit: cloak-tests.dg cloak.dg + $(DGDEBUG) $^ $(UNIT) + test-zmachine: cloak.z5 cloak.regtest $(REGTEST) --interpreter dfrotz --game $^ From b87138bb96f802d476ebb80d03c02324f2d91c6a Mon Sep 17 00:00:00 2001 From: Susan Davis Date: Tue, 23 Jun 2026 11:17:47 -0400 Subject: [PATCH 08/14] fix build --- test/regtest/Makefile | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/test/regtest/Makefile b/test/regtest/Makefile index 6ea1216..656f1c1 100644 --- a/test/regtest/Makefile +++ b/test/regtest/Makefile @@ -3,12 +3,10 @@ DIALOGC=../../src/dialogc STDLIB=../../stdlib.dg AAMRUN=../../bin/aamrun.py AAMBOX=../../bin/6502run.py -DGDEBUG=../../src/dgdebug -u -UNIT=../../unit.dg all: test clean -test: test-unit test-zmachine test-web test-c64 +test: test-zmachine test-web test-c64 %.z5: %.dg $(DIALOGC) -t z5 $< $(STDLIB) -o $@ @@ -16,9 +14,6 @@ test: test-unit test-zmachine test-web test-c64 %.aastory: %.dg $(DIALOGC) -t aa $< $(STDLIB) -o $@ -test-unit: cloak-tests.dg cloak.dg - $(DGDEBUG) $^ $(UNIT) - test-zmachine: cloak.z5 cloak.regtest $(REGTEST) --interpreter dfrotz --game $^ From 2cc0a66df07de296762c97520501aa0266854e9f Mon Sep 17 00:00:00 2001 From: Susan Davis Date: Tue, 23 Jun 2026 11:21:22 -0400 Subject: [PATCH 09/14] fewer phonies --- test/regtest/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/regtest/Makefile b/test/regtest/Makefile index 656f1c1..eaf8a7f 100644 --- a/test/regtest/Makefile +++ b/test/regtest/Makefile @@ -26,4 +26,4 @@ test-c64: cloak.aastory cloak.regtest clean: rm -f *.z5 *.aastory -.PHONY: all test clean test-zmachine test-web test-c64 +.PHONY: all test clean From 870f73f6e167f2ba42bb263edca2c74800788bc2 Mon Sep 17 00:00:00 2001 From: Susan Davis Date: Tue, 23 Jun 2026 12:09:09 -0400 Subject: [PATCH 10/14] disable c64 tests to keep build from breaking --- manual/modules/lib/pages/testing.adoc | 14 ++++++++++++-- test/regtest/Makefile | 2 +- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/manual/modules/lib/pages/testing.adoc b/manual/modules/lib/pages/testing.adoc index f5bd2a4..3e6997a 100644 --- a/manual/modules/lib/pages/testing.adoc +++ b/manual/modules/lib/pages/testing.adoc @@ -2011,7 +2011,16 @@ powerful. If you're making a typical game that's implemented in a single `.dg` file, then `dgt` might be a more convenient option. If you're using lots of extensions, need to customize your build, or want a different project layout than `dgt` requires, you might need to -stick with `make`. See the `dgt` documentation for details. +stick with `make`. See the `dgt` documentation for details. + +[#beta] +== Exploratory Testing + +TODO + +=== Finding Beta Testers + +TODO [#debugging] == Troubleshooting Broken Tests @@ -2049,7 +2058,8 @@ Here's a summary of the advice from this chapter: - Use fixtures to keep your actual test cases simple. - Design for testability, and don't write "de-testable" code that can't easily be tested. -- Test in depth, using both unit and all-up tests. +- Test in depth, using both unit and all-up tests, and supplementing + your automated tests with exploratory (beta) testing. - Automate your testing process with `make` or `dgt`. - Test on all platforms for which you'll be releasing: Z-machine, web, and vintage computing hardware. diff --git a/test/regtest/Makefile b/test/regtest/Makefile index eaf8a7f..2ef317b 100644 --- a/test/regtest/Makefile +++ b/test/regtest/Makefile @@ -6,7 +6,7 @@ AAMBOX=../../bin/6502run.py all: test clean -test: test-zmachine test-web test-c64 +test: test-zmachine test-web # test-c64 %.z5: %.dg $(DIALOGC) -t z5 $< $(STDLIB) -o $@ From d9af3f7e471360fa4144fbb6e192c853dcc682c0 Mon Sep 17 00:00:00 2001 From: Susan Davis Date: Wed, 24 Jun 2026 13:25:00 -0400 Subject: [PATCH 11/14] quit at end of lose test --- manual/modules/lib/pages/testing.adoc | 2 +- test/regtest/cloak.regtest | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/manual/modules/lib/pages/testing.adoc b/manual/modules/lib/pages/testing.adoc index 3e6997a..48ebecd 100644 --- a/manual/modules/lib/pages/testing.adoc +++ b/manual/modules/lib/pages/testing.adoc @@ -1995,7 +1995,7 @@ test-impossible-zmachine: impossible.zblorb impossible.regtest === Testing With `make` -TODO + === Building Releases diff --git a/test/regtest/cloak.regtest b/test/regtest/cloak.regtest index 9b7496b..ad83b2e 100644 --- a/test/regtest/cloak.regtest +++ b/test/regtest/cloak.regtest @@ -144,3 +144,6 @@ Would you like to: QUIT the program, or RESTART from the beginning? +> quit + +Thanks for playing! From 80f6c9e37db46cb0e471c927d0f76a1158275412 Mon Sep 17 00:00:00 2001 From: Susan Davis Date: Wed, 24 Jun 2026 17:46:20 -0400 Subject: [PATCH 12/14] more text --- manual/modules/lib/pages/testing.adoc | 109 +++++++++++++++++++++++++- 1 file changed, 108 insertions(+), 1 deletion(-) diff --git a/manual/modules/lib/pages/testing.adoc b/manual/modules/lib/pages/testing.adoc index 48ebecd..ffba8ea 100644 --- a/manual/modules/lib/pages/testing.adoc +++ b/manual/modules/lib/pages/testing.adoc @@ -1995,11 +1995,118 @@ test-impossible-zmachine: impossible.zblorb impossible.regtest === Testing With `make` +Using `make` to automate your testing is fairly straightforward. First +of all, you'll want to run your unit tests. Because they run quickly, +in the debugger, you can actually run them _before_ compiling your +code, which a change from most other compiled languages. +For this example, we'll use `regtest.py`, but `make` could equally be +used to manage testing with `diff`. + +[source] +---- +DGDEBUG = dgdebug -u +UNIT = unit.dg +STDLIB = stdlib.dg + +all: test + +test: test-unit + +test-unit: cloak-tests.dg cloak.dg + $(DGDEBUG) $^ $(UNIT) $(STDLIB) + +.PHONY: all test +---- + +Next, we'll test with the Z-machine. We'll need to compile into either +a `.z5` or a `.zblorb`. Compared to a `.z5` or `.z8`, a `.zblorb` +allows you to store some metadata such as a cover image. We don't have +one of those for _Cloak of Darkness_, so we'll just make a `.z5`. + +Because we're producing a new file, we'll also want another target to +get rid of it once we're done with it. By convention, `clean` is the +usual name for a target that cleans out files that the compiler +builds. Some projects also have a `distclean`, which also gets rid of +the final product, or a `tidy`, which gets rid of only intermediate +files, but we don't need either. + +Adding targets for building and testing on Z-machine, and a `clean`, +gives us this: + +[source] +---- +DGDEBUG = dgdebug -u +UNIT = unit.dg +STDLIB = stdlib.dg +DIALOGC = dialogc +REGTEST = regtest.py -v +DFROTZ = dfrotz + +all: test + +%.z5: %.dg $(STDLIB) test-unit + $(DIALOGC) -t z5 $< $(STDLIB) -o $@ + +test: test-unit test-zmachine + +test-unit: cloak-tests.dg cloak.dg + $(DGDEBUG) $^ $(UNIT) $(STDLIB) + +test-zmachine: cloak.z5 cloak.regtest + $(REGTEST) --interpreter $(DFROTZ) --game $^ + +clean: + rm -f *.z5 + +.PHONY: all test clean +---- + +Extending the above to also test the Å-machine is equally straightforward: + +[source] +---- +DGDEBUG = dgdebug -u +UNIT = unit.dg +STDLIB = stdlib.dg +DIALOGC = dialogc +REGTEST = regtest.py -v +DFROTZ = dfrotz +AAMRUN = aamrun.py +AAMBOX = 6502run.py + +all: test + +%.z5: %.dg $(STDLIB) test-unit + $(DIALOGC) -t z5 $< $(STDLIB) -o $@ + +%.aastory: %.dg $(STDLIB) test-unit + $(DIALOGC) -t aa $< $(STDLIB) -o $@ + +test: test-unit test-zmachine test-web test-6502 + +test-unit: cloak-tests.dg cloak.dg + $(DGDEBUG) $^ $(UNIT) $(STDLIB) + +test-zmachine: cloak.z5 cloak.regtest + $(REGTEST) --interpreter $(DFROTZ) --game $^ + +test-web: cloak.aastory cloak.regtest + $(REGTEST) --interpreter $(AAMRUN) --game $^ + +test-6502: cloak.aastory cloak.regtest + $(REGTEST) --interpreter $(AAMBOX) --game $^ + +clean: + rm -f *.z5 *.aastory + +.PHONY: all test clean +---- === Building Releases -TODO +Finally, the main point of using `make` is to build your game so that +your players can play it. We've already built the Z-machine version [#dgt] === Building With `dgt` Instead From 74aeb2ff03bdbdf358272798156c61f38a2e8d65 Mon Sep 17 00:00:00 2001 From: Susan Davis Date: Wed, 24 Jun 2026 18:15:12 -0400 Subject: [PATCH 13/14] finish the section --- manual/modules/lib/pages/testing.adoc | 61 +++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/manual/modules/lib/pages/testing.adoc b/manual/modules/lib/pages/testing.adoc index ffba8ea..22ae949 100644 --- a/manual/modules/lib/pages/testing.adoc +++ b/manual/modules/lib/pages/testing.adoc @@ -2083,7 +2083,7 @@ all: test %.aastory: %.dg $(STDLIB) test-unit $(DIALOGC) -t aa $< $(STDLIB) -o $@ -test: test-unit test-zmachine test-web test-6502 +test: test-unit test-zmachine test-web test-c64 test-unit: cloak-tests.dg cloak.dg $(DGDEBUG) $^ $(UNIT) $(STDLIB) @@ -2094,7 +2094,7 @@ test-zmachine: cloak.z5 cloak.regtest test-web: cloak.aastory cloak.regtest $(REGTEST) --interpreter $(AAMRUN) --game $^ -test-6502: cloak.aastory cloak.regtest +test-c64: cloak.aastory cloak.regtest $(REGTEST) --interpreter $(AAMBOX) --game $^ clean: @@ -2106,7 +2106,62 @@ clean: === Building Releases Finally, the main point of using `make` is to build your game so that -your players can play it. We've already built the Z-machine version +your players can play it. We've already built the Z-machine version, +but we'll need to use `aambundle` to turn our `.aastory` file into web +and/or Commodore 64 executables: + +[source] +---- +DGDEBUG = dgdebug -u +UNIT = unit.dg +STDLIB = stdlib.dg +DIALOGC = dialogc +REGTEST = regtest.py -v +DFROTZ = dfrotz +AAMRUN = aamrun.py +AAMBOX = 6502run.py +AAMBUNDLE = aambundle + +all: cloak.z5 web c64 + +%.z5: %.dg $(STDLIB) test-unit + $(DIALOGC) -t z5 $< $(STDLIB) -o $@ + +%.aastory: %.dg $(STDLIB) test-unit + $(DIALOGC) -t aa $< $(STDLIB) -o $@ + +test: test-unit test-zmachine test-web test-c64 + +test-unit: cloak-tests.dg cloak.dg + $(DGDEBUG) $^ $(UNIT) $(STDLIB) + +test-zmachine: cloak.z5 cloak.regtest + $(REGTEST) --interpreter $(DFROTZ) --game $^ + +test-web: cloak.aastory cloak.regtest + $(REGTEST) --interpreter $(AAMRUN) --game $^ + +test-c64: cloak.aastory cloak.regtest + $(REGTEST) --interpreter $(AAMBOX) --game $^ + +web: cloak.aastory test-web + rm -rf $@ + $(AAMBUNDLE) -t web $< -o $@ + +c64: cloak.aastory test-c64 + rm -rf $@ + $(AAMBUNDLE) -t c64 $< -o $@ + +clean: + rm -rf *.z5 *.aastory web c64 + +.PHONY: all test clean +---- + +And we're done! We can run `make` to build our release, `make test` to +just run the tests, or `make clean` to clean everything up. Note that +our chain of prerequisites will cause the tests to be run whenever we +try to build our release. [#dgt] === Building With `dgt` Instead From 8c8ec3cfc2ebc03d68077180852b73c88bada299 Mon Sep 17 00:00:00 2001 From: Susan Davis Date: Wed, 24 Jun 2026 18:15:54 -0400 Subject: [PATCH 14/14] ready for merge --- manual/modules/lib/pages/testing.adoc | 29 --------------------------- 1 file changed, 29 deletions(-) diff --git a/manual/modules/lib/pages/testing.adoc b/manual/modules/lib/pages/testing.adoc index 22ae949..a0a56be 100644 --- a/manual/modules/lib/pages/testing.adoc +++ b/manual/modules/lib/pages/testing.adoc @@ -2175,35 +2175,6 @@ you're using lots of extensions, need to customize your build, or want a different project layout than `dgt` requires, you might need to stick with `make`. See the `dgt` documentation for details. -[#beta] -== Exploratory Testing - -TODO - -=== Finding Beta Testers - -TODO - -[#debugging] -== Troubleshooting Broken Tests - -TODO - -[#pitfalls] -=== Simple Pitfalls - -TODO - -[#output] -=== Diagnostic Output - -TODO - -[#dgdebug] -=== Using `dgdebug` and its Predicates - -TODO - [#summary] == Summary