Automated Testing for Open Watcom C/C++ and DOS

UPDATE 2022-10-21: Added update on 86Box’s porting status

One of the hobby projects I’ve been poking at again is written for DOS using Open Watcom C/C++ (v2 fork), and, being as averse to drudgework (and spoiled by modern tooling) as I am, I wanted some automated testing… so I wound up doing another blog-worthy survey of the field.

Unit Testing

Funny enough, there are actually several easy-to-use unit test frameworks that will build perfectly well with Open Watcom C/C++ v1.9 inside DOSBox… and that is the first test. There’s no point testing anything further if it’s not compatible in the first place.

TL;DR: greatest is the greatest.

greatest (ISC License) (My Recommendation)

This is probably the best bang for your buck if you want a simple test harness for your DOS projects. Just drop greatest.h into your project, add a few GREATEST_MAIN_* macro invocations to your test .c file, and start using RUN_TEST or RUN_SUITE and ASSERT_* macros.

#include "greatest.h"

TEST test_func(/* arguments */) {
    ASSERTm("Test string must not be empty", strlen(some_arg));
	
    /* ... */
    ASSERT(foo);
    ASSERT_EQ(bar, other_arg);
	
    /* ... */
    ASSERT_STR_EQ(baz, some_arg);
    PASS();
}

GREATEST_MAIN_DEFS();

int main(int argc, char **argv) {
    GREATEST_MAIN_BEGIN();

    RUN_TESTp(test_func, /* args */);
    greatest_set_test_suffix("other_args");
    RUN_TESTp(test_func, /* other args */);
    /* ... */

    GREATEST_MAIN_END();
}
screenshot

It’s also surprisingly featureful for something so simple:

  • It claims to work with any C89 compiler, does no dynamic allocation, and I had no problem building and running a real-mode executable from it inside DOSBox.
  • It supports passing a userdata argument to a test function with RUN_TEST1, so you can define your test once and then feed it various different inputs.
  • If you’ve got a compiler which supports the requisite C99 feature, you can use RUN_TESTp to invoke a test function with an arbitrary number of arguments (And I did use RUN_TESTp in my real-mode test .exe)
  • It provides SET_SETUP and SET_TEARDOWN macros which accept userdata arguments.
  • It’ll handle --help for you, but can also be invoked as a library for integration into a larger executable.
  • It’ll give you the familiar “one period per successful test, but verbose output for failing tests” output by default but there are also contrib scripts for colourizing the output or converting to TAP format.
  • It supports filtering which tests get run and listing registered tests from the command line.
  • It supports macros to randomly shuffle the order of tests or test suites to reveal hidden data dependencies.

For something that works with Open Watcom C/C++ v1.9, the experience is surprisingly reminiscent of Python’s standard library unittest module.

While it lacks some “check this and show the values if they fail” assertions I would have preferred, such as greater/less than comparisons (ironic for a library named “greatest”), there’s a PR which would add them that you can grab instead and the author is considering them for the next version. (Version 1.5.0 added ASSERT_GT, ASSERT_GTE, ASSERT_LT, and ASSERT_LTE.)

The only wart I noticed is how the assertion macros interact with helper functions. (The assertion macros work by returning enum values, so helper functions which use them have to have a return type of enum greatest_test_res and you have to wrap calls to them in CALL() to conditionally propagate the return.) That said, that’s a minor problem and I am a fan of making good use of return values to avoid hidden control flow and unnecessary side-effects.

All in all, a very nice little harness to choose for retro-C projects… or for any C projects that don’t need something fancier, really.

Unity (MIT License)

This is the next step up the complexity ladder but it’s primarily designed for embedded development, so its features are more poorly aligned for desktop development.

  • It’s split into three files (two headers and a .c file)
  • It doesn’t handle argument parsing (No --help, no test filtering)
  • The introductory documentation has a lot of “Here’s how to do this with microcontrollers and your favourite non-Watcom build system” content that can get you side-tracked if you’re not careful.
  • Because it’s designed to make microcontrollers first-class citizens, it’s got a lot of ASSERT macros which are just more specialized versions of what greatest offers. (eg. TEST_ASSERT_EQUAL_HEX8)
  • It expects you to have one setup and teardown function per file, rather than providing a macro to register them with arguments to pass in, so you may have more code duplication.
  • I couldn’t find any evidence that it provides an equivalent to greater’s RUN_TESTp for calling a template function with varying arguments.
  • The assertion macros are more verbose. (TEST_ASSERT_TRUE_MESSAGE instead of TEST_ASSERTm)
  • Its default configuration depends on more of the standard library, so I had to remove some of my compiler flags intended for size optimization to get it to build.

Going from greatest to this reminds me of going from POSIX to Java or Windows APIs.

On the plus side, it does have some advantages:

  • It comes with a lot of documentation, including a printable cheat sheet for the assert macros.
  • If you don’t mind the lack of “develop DOS software on DOS” purity, it includes some ruby scripts to generate test boilerplate for you.
  • It has a few “automatically pretty-print on failure” assertions that greater is missing, like array and bitfield equality tests.

…but still no less/greater than pretty-print macros!

Once you ignore the flood of details in the introduction that are irrelevant to DOS retro-hobby development, it becomes pretty clear that writing a Unity test suite is almost identical to writing a greatest test suite.

Drop the three Unity files somewhere your compiler can find them, tweak your makefile to also build and link unity.c, and then write a little test program:

#include "unity.h"

void setUp(void) {}     /* Required or it'll fail to link */
void tearDown(void) {}  /* Required or it'll fail to link */

void test_a(void) {
    TEST_ASSERT_TRUE_MESSAGE(strlen(some_arg), "Test string must not be empty");
	
    /* ... */
    TEST_ASSERT_TRUE(foo);
    TEST_ASSERT_EQUAL_UINT(bar, other_arg);
	
    /* ... */
    TEST_ASSERT_EQUAL_STRING(baz, some_arg);
}

int main(void) {
    UNITY_BEGIN();

    RUN_TEST(test_a);
    /* ... */

    return UNITY_END();
}
screenshot

Final verdict: It’s certainly nicer than JTN002 – MinUnit (Heck, I could write something better than that) and it’s probably the best choice for testing embedded C, but it’s not for DOS retro-computing.

Minctest (Zlib License)

For the most part, this is unarguably a worse choice than the previous two options, having fewer assertion types than either, less supporting documentation, no support for parameterized tests, and no helper for command-line options… but it does do two things which I think are valuable:

  • It shows pass/fail counts for individual assertions within a test case.
  • It lets you easily and obviously set custom display names for tests.

Again, it’s a fairly simple API. One minctest.h file to include, and then you write tests like this:

#include "minctest.h"

void test_a() {
    lok(1 == 1);

    /* ... */
    lsequal("foo", "foo");

    /* ... */
    lequal(2, 2);
    lfequal(3.0, 3.0);
}

int main(int argc, char *argv[]) {
    lrun("Test A", test_a);

    /* ... */
    lresults();
    return lfails != 0;
}

In the end, it’s not something I’d use for anything when greatest and Unity exist, but it’s a good data point for test suite UI design.

Others…

CuTest (zlib/libpng License)

CuTest (zlib/libpng License) does pass my initial triage question of “Does this cross-build from the Linux version of Open Watcom C/C++ 1.9 and run as a real-mode EXE under DOSBox?” …but that’s it.

First, it’s the only option that worked, but failed with a Stack Overflow error until I raised Open Watcom’s notoriously small default stack size with the OPTION STACK linker directive.

Second, and most damningly, the documentation and distribution archive front-load too much complexity… especially when, from what I can tell, it still produces a worse overall experience than greatest.

For that reason, I only ran the example provided in cutest-1.5.zip and this is what it looks like in DOSBox:

I checked µnit, µTest, utest.h, FCTX, and siu’s MinUnit but they all failed the “Will the distributed source build for real-mode DOS with Open Watcom 1.9 without patching?” test.

I also checked Labrat, CUnit, Check, Embedded Unit, and cmocka, but they were even more complex than CuTest to get going, so I didn’t even bother checking whether they would compile.

(It also doesn’t help that some of these rejected options are under the Unlicense, which multiple parties have criticized, and which doesn’t reliably account for “when in doubt, protect the rightsholder from their own ignorance” provisions in jurisdictions like Germany the way CC0 would. See this review of the CC0 for details.)

Snapshot Testing

If you’re testing something like a wrapper for the int 10h Video BIOS APIs, the simplest way to assert the correct results is:

  1. Clear the screen
  2. Assert that the screen buffer is in the expected state
  3. Do some drawing
  4. Check that screen buffer’s contents match a saved copy of what it should look like

Not only does this allow testing on real hardware without setting up some kind of fancy VGA capture setup, it also makes the test more generally applicable.

  • There are rendering differences between VGA and earlier color graphics adapters, but, because EGA and VGA achieve the default 80×25 color text mode by emulating CGA text mode, the data your test reads out of the screen buffer will be the same regardless.
  • DOSBox uses different fonts when you switch the machine= setting between different graphics adapters.
  • Screenshotting is affected by things like the aspect and scaler settings in your DOSBox configuration.
  • Taking a screenshot of an emulator window requires different APIs on different host platforms if the emulator wasn’t specifically built to be scriptable in that way.
  • Comparing VGA output on real hardware is even more difficult because producing and then capturing VGA output is a digital-to-analog-to-digital conversion.

On the other hand, reading the screen buffer is just a matter of synthesizing a pointer to the right memory address, and then reading out the correct number of bytes. (For CGA text mode or an emulation of it like EGA and VGA do, the address is B800:0000 and, for the default 80×25 text mode, the length is 4000 bytes.)

Doing it this way allows you to run your testing entirely within DOS by writing a simple harness around your code which sets up the screen before and dumps the memory to file after, ready to be compared like any other “Are these null-containing buffers equal?” check. (Which means you can easily run the test in any DOS environment. Great for portability testing.)

I haven’t had time to try this yet, but, if you’re running on EGA or later, where 80×25 text mode has access to four different display pages, it may even be possible to do it right inside one of the unit test runners I examined above without interfering with the test runner output, by switching away from the default page before running the test and back after to prevent it from erasing the runner’s status output.

(The main thing I need to check is whether B0000h gets remapped to the active page. If it doesn’t, then the utility of using page flipping to run the snapshot tests inside the unit test hardness would be limited to int 10h APIs that follow the active page.)

I’ll probably put up some sample code once I get to writing this part of the testing infrastructure for my project.

Functional Testing

Concept

Functional testing for a DOS program is complicated, because there is so little abstraction from the underlying platform. In fact, I think that, in the general case, it’s only worth the trouble if you run the test driver outside the DOS system under test.

This means one of two things:

  1. Run the program under test in an emulator, with the functional test harness running on the host operating system.
  2. Run the program under test on real hardware, with the functional test harness running on another machine.

To support both cases, and to avoid the needless complexity and potential for mistakes that would come from using a TSR, the solution I’ve chosen is to instrument the program to be tested with a simple serial console. Then, the same mechanism can work with real DOS machines (just use a null modem cable and, if your development machine is modern, a USB-Serial adapter) or with any DOS emulator that provides a means to connect an emulated serial port to the host system.

The serial console needn’t be complicated. Just log messages which the test harness can assert for correctness, and add a means to substitute input that would usually be provided by the user.

For additional testing, I’ll probably implement some kind of way to have the program under test stop at a machine-readable prompt to continue, so the harness can deterministically capture screenshots and complement the snapshot unit testing above with snapshot integration testing.

With DOSBox, it’s also easy for the test harness to assert for the presence, absence, and/or contents of on-disk files without having to mount or otherwise interact with an emulator’s disk images.

Research

The emulators which look to be capable of bidirectional COM port redirection to the host OS include DOSBox, QEMU, VirtualBox, and Bochs.

PCjs has some kind of serial console support but I haven’t had time to figure out whether it can be exposed to a separate process in the way I need.

I haven’t been in a hurry to investigate DOSEMU compatibility, since it’s more like Wine for DOS programs than a full-blown emulator and, aside from being especially thorough in my compatibility testing, I don’t see testing in it gaining anything if I’m already testing with DOSBox.

Either way, DOSBox should do well for quick and dirty testing, akin to testing a Windows program under Wine, and QEMU or VirtualBox should work for testing real DOS on fake hardware, but testing how software interacts with quirky hardware is another story.

Unfortunately, I’m still looking for a way to make this work with the emulators most suitable for that. PCem and its derivatives, 86Box and VARCem, are currently the state of the art in trying to accurately reproduce vintage hardware via emulation, but none of them have serial port pass-through support.

  • PCem’s developer has stated a lack of time to implement serial port pass-through but is willing to accept patches. It supports emulating an NE2000 network adaptor, but the only TSR I’ve yet found for redirecting COM ports over a network socket is paid proprietary software. (Though this TSR may be something I can MacGyver into doing the job. If I can, I’ll have to contact the author to ask for clarification on the license though.)
  • 86Box was forked before the frontend was rewritten to be cross-platform, the developers are still soliciting an experienced contributor to port over PCem’s new SDL+wxWidgets frontend, and I don’t know if it was forked before or after the NE2000 support landed. 86Box is now cross-platform and easily available to Linux users on Flathub. Its SLiRP networking option functions equivalently to the NAT option in VirtualBox and supports emulating 12 vintage NICs including the NE2000.
  • VARCem doesn’t offer non-Windows binaries and it was apparently forked from 86Box, so all the same caveats probably apply.

Finding any information on whether serial support has been added to 86Box or VARCem since the fork is complicated by how PCem-lineage emulators provide dummy/unconnected serial and parallel ports to emulated OSes to ensure that the observed behaviour of the hardware for a selected system is 100% accurate.

So far, my best hope for testing handling of hardware quirks is that a half-functional test harness could be rigged up by running 86Box inside Wine (assuming it works in Wine), using its LPT-to-file support and inotify to handle sending data from the program under test to the harness (assuming 86Box doesn’t force block-wise buffering on it), and using XTestFakeKeyEvent to send control inputs from the harness to the program under test. (Obviously, this would only work on X11-based desktops capable of using Wine to run x86-based Windows applications.)

Whatever I come up with, if it’s easy enough to generalize, I hope to release it as a reusable test framework once I’ve got something useful.

Posted in Retrocomputing | 2 Comments

Retrocomputing Category Announcement

While working on an upcoming post (a run-down of C unit test frameworks that are easy to use under Open Watcom C/C++), I realized that I’m getting enough of these retrocomputing resource posts that mentioning them from each other is no longer a viable option.

…so, I decided to go back and add a new subcategory. If you are interested in my lists of retrocomputing resources, give the “Retrocomputing” category link at the bottom of the post a click.

Posted in Retrocomputing | Leave a comment

A Major QtExceptHook Update

For anyone who uses QtExceptHook, my port of the old gtkexcepthook.py script to PyQt5, I have an announcement I think you’ll appreciate… I just made a major update.

If you don’t use QtExceptHook, it’s a single Python file you can drop into your PyQt5 projects so that uncaught exceptions will appear as a user-friendly dialog box which harvests both the traceback and all the local variables at each level of the call stack. It also has your choice of a “copy to clipboard” button or a “report bug” button which invokes a traceback-sending callback of your choice.

…so, what features did I add?

  • In exchange for bumping the minimum Python version to 3.5, it now relies on the Python standard library’s TracebackException to generate the traceback and variable dumps, removing all the most opaque code I inherited and massively reducing the surface area for bugs that might not show up under basic testing.
  • It’s now fully documented in Sphinx-dialect ReStructuredText, in case you want to expose it in your API documentation. (Though it does assume you’ll be using the sphinx-autodoc-typehints and sphinx-qt-documentation extensions.)
  • The dialog is now resizable and the traceback preview now scrolls horizontally instead of wrapping.
  • I did a major refactoring of the Qt-specific parts of the script to make them simpler and more readable.
  • The example SMTP bug-reporting code now has error handling.
  • Qt’s QCommandLineParser is now used to provide a --report-button option so you can run the demo code both with and without a bug-reporting callback without having to edit the file.
  • The file is now TODO-free.

Enjoy. 🙂

Posted in Geek Stuff | Leave a comment

Making a 2-button Trackball Useful on Modern Linux

I was the kind of nerdy kid who read computer catalogues for fun before his tenth birthday, and one of the nerdy things I always wanted was a Logitech Trackman Marble… an early optical trackball with a distinctive patterned ball for the sensor to detect. (So distinctive that they still use it on the modern ones.)

Well, I got one in a box of nerdy hand-me-downs, but it’s the one I was eyeing as a kid, with two buttons and no scroll wheel… that won’t do.

Here’s a little script, adapted from someone else who had a similar problem with a Kensington trackball, which adds the following features:

  • Adjust pointer acceleration so use with a three-monitor spread isn’t an exercise in frustration
  • Press and hold the left button for 300ms without moving the ball to produce a right-button press (This seems to work perfectly well to allow me to do both left- and right-button dragging)
  • The physical right button is remapped as the middle button for clicking purposes.
  • Press and hold the physical right button to use the trackball as a scroll wheel
  • Adjust sensitivity of emulated scroll wheel to be usable for things like tab-switching without physical detents.
  • Enable both vertical and horizontal scrolling on the emulated scroll wheel.
  • Work around the cheap chinese USB-PS/2 adapter using the same name for both keyboard and mouse devices so it’s still possible to reference the pointer device by name. (To find the name of yours, run xinput in a terminal.)

It’s not perfect, since you can’t middle-click drag, and I still need to get some ScratchX to fix some scratches on the ball itself that cause an occasional hitch in its motion, but, aside from that, it’s surprisingly comfortable to use.

I’m not sure how much I’ll actually use it, but it’s nice to have it useful. Enjoy. 🙂

Posted in Geek Stuff | Leave a comment

Fanfiction – A Prince’s Duty

…and, in case you’re not into Naruto, here’s another one I had waiting in the wings:

A Prince’s Duty by BonusPoints

For all that Ranma ½ has a gender-transformative curse as such a central element of its narrative, there aren’t enough Ranma ½ fics that both attempt to focus on how it affects the characters psychologically and succeed at doing it.

Even worse, it’s almost impossible to find any stories that give Prince Herb of the Musk significant character focus, psychological or otherwise, since he’s from the half of the manga that never got adapted into the anime… this is one such fic. Not only that, it’s a story where Herb is a main character, the story starts out being told from his perspective, and it leans about as far toward “character piece” as you can go.

It goes into lots of satisfying detail on Herb’s motivations and thought processes. Just as important, it gives him opportunities to show that, when not being pissed off by Ranma, he’s as skilled in diplomatic discourse and as interested in acquiring politically important information as is appropriate for someone of his station, and it makes good use of the opportunity to use that princely objectivity to get a second perspective on Ranma’s canon situation. That is what a character piece should be… especially when you understand your characters and your readers well enough to use a line like this.

Herb was finding actively revealing her curse to be something too embarrassing to so much as attempt. She would leave that for Ranma to figure out.

It also has an interesting thread that never had time to play out in full before the story ended, but still helps to grease the wheels and make things interesting. Upon encountering Ranma at the Nekohanten, Lime jumps to the mistaken conclusion that Herb has secretly always been a girl and that “he” came to Japan to pursue Ranma as someone who can be a wife in public and a husband in private. Mint tries to correct him, but gets convinced instead, and Herb mistakes their innocent loyalty to him for a proper understanding of the situation.

It also doesn’t skimp on giving some character to anyone who gets a scene from their perspective. For example:

Ryoga’s idea of conversation seemed to consist mainly of bringing up random subjects, then looking around with a flushed face, before repeating the process over again. Mint flinched at the raw ineptitude the bandanna-clad youth was exhibiting. It reminded him of the very first time he had seen a woman. ‘What’s his excuse,’ the Musk warrior wondered.

That said, within the scope of what did get written, I think it’s reasonably obvious that the intent was to build up to a Ranma-Herb pairing built around only Herb’s female form being close enough to human to be able to have children with other humans. I’m not sure how I feel about that. I’m not specifically against such a pairing, but even something this well-written does flirt with feeling contrived in how it keeps the misunderstanding about Herb’s true gender going. (Even if it does make for a very elegant way for Ranma to unknowingly reveal the solution to Herb’s problem in chapter 7.)

Despite all that, what I found most noteworthy on re-reading it was actually one of the least memorable things… It’s one of those rare stories that truly knows how to use flashbacks and time cuts properly, bouncing back and forth without making me reluctant to follow. Usually, flashbacks prompt at least a bit of resistance, because they pull me away from what they’ve primed me to care about next… but, in this case, the story is staying close enough to canon , and we already have a solid understanding of how canon unfolds, and the writing carefully steers the reader to care about the character development in a way that doesn’t tie it to the events too strongly. As a result, carefully jumping back and forth in time can make the revelation of Herb’s character feel more consistent.

In fact, until Akane accidentally locks Ranma’s curse rather than Herb doing it on purpose, and makes Herb pause to think about what he was about to do, it’s believable that this could have been showing a different perspective on canon events. (A scene which is then repeated from Ranma’s perspective without it feeling boring or repetitive… like I said, this is how you do character writing.) For how brief it is, I think this moment is the most important to the plot, because, when set in a narrative which will build on it rather than quashing it, it’s one of those little “for want of a nail” moments. In this case, “for want of a nail, a bad first impression was lost and a spark of sympathy gained. For want of a bad first impression and a lack of sympathy, a prince’s hostility and lack of introspection was lost. For want of hostility and a lack of introspection, conversations were prompted. All for want of a horseshoe nail.”

I also find it amusing to see a scene where, when Ranma is puzzled at Herb neither wanting to kill nor woo him, and Herb points out that Ranma isn’t the catch he thinks he is, Ranma retreats to insults when his ego is threatened. Why? Because, it’s the first time I can remember seeing a scene where you have a male character nursing a bruised ego… in a way that strongly reminds me of how tsundere female characters react to emotional threats. (And then Lime interrupts, giving Herb a chance to realize the value in learning to not let himself be goaded by that, thus continuing a pattern of keeping things from degenerating without it feeling artificial, contrived, or forced.)

Back when I first discovered it just shy of a decade ago, I felt that it was the epitome of everything I wanted in such a fic, and it’s a real shame it’ll never be finished, but at least we got half a novel worth of text out of it. I do worry that how it was handling Herb and Ranma was steering into some grade-A tightrope-walking, but it didn’t jump the shark within what actually got written, so I’m giving it a 4.9 out of 5 for the too-convenient-for-the-author ways in which the miscommunication about Herb’s true gender persisted.

I just wish that it had stayed on the more psychological feel it had in chapters 1 through 5. I know characters like Ryoga had to come in eventually, and chapters 6 and 7 were when the other shoe dropped… but a guy can dream.

P.S. The amount of care and caution it spends on the interactions between Ranma and Herb reminds me of another unfinished fic that I really need to re-read and review at some point: Kunoification by Ozzallos.

Posted in Fanfiction | Leave a comment

Fanfiction – A Drop of Poison

Since I blogged about something I don’t recommend, I shouldn’t keep people waiting for something I do recommend:

A Drop of Poison by Angel of Snapdragons

This is one of my old favourites. The gist is that, because Iruka is unconscious when Naruto defeats Mizuki at the beginning of the series, things get very different in a very entertaining way.

First, Naruto discovers that some of his clones which he didn’t dispel have copies of the scroll, which he rushes to transcribe… in the process, learning that missed detail about how Kage Bunshin can transfer memories. Then, he discovers that he’s going to have to go back to the academy, but one of his clones discovers that they can fool the civilians into treating them well using Henge.

Throw in a chakra-draining technique from the scroll which is dangerous to flesh-and-blood ninja without the Akado bloodline, but perfectly safe as a way for Kage Bunshin to extend their lifespans indefinitely, and Naruto hatches a plan to set up two cover identities who can continue to learn at the academy even when teachers try to sabotage him.

…and then he decides to make another two identities to satisfy an impulsive desire to know what life is like for civilians and realizes how productive Kage Bunshin can make him when used for training and scavenging equipment.

I like how the author manages to casually justify this plan being a success. First, Iruka drops by and recognizes that Naruto is up to something, only to get talked into joining in the harmless little conspiracy. Thus, there’s a competent advisor to make sure Naruto can pull it off. Then, Iruka notices something off about Naruto’s hand seals and realizes that his transformation technique isn’t actually the standard Henge but some full-blown physical transformation that Naruto managed to cobble together from intentionally broken instructions. Then, one of Naruto’s clones finds an abandoned mansion that their new chakra draining technique allows them to gain entry to so they can use it as a hideout. …and then Iruka gets Sarutobi’s tacit approval on the promise that the plan he promised Naruto not to speak about isn’t illegal and has a good chance of working.

Over the course of the 200,000+ words, things get really developed, to the point where his use of Kage Bunshin winds up becoming a network of traders cum spies. (Sarutobi is finally let in on it in the wake of the Chunin Exam)

Yes, it sounds overpowered, but I’ve just summarized the most memorable aspects of over 200,000 words, and this is one of those stories that isn’t about how the hero wins, but how many novel things can happen as a side-effect of the hero being an ordinary person handed a superpower. I also like that Iruka is a main character. I haven’t run across enough fics that both hook my interest for other reasons and give him that kind of focus.

In fact, that’s probably the thing I like most about this story. While it is a bit of a curb-stomp story (though not as much as you’d think, given how hard it is to find Root when they start sabotaging their competition), it’s also a character story. When Iruka agrees to help Naruto, it’s preceded by a nice amount of introspection into how he sees the world. When Sarutobi agrees to the plan on the condition that Iruka take responsibility for keeping it from going awry, we see the final thing that tipped the scales: the prospect of forcing Iruka to break his promise to Naruto after Sarutobi failed to keep his promise to Minato about how Naruto should be treated… though it does drift away from the character focus somewhat during the Chunin exam.

It also has nice little touches I’ve never seen used before, like this:

Suddenly, the chūnin teacher realized that if things continued as they were now, and Naruto kept on in the academy, this might be the place where he’d end up. This simple thought of a prankster like the boy working around that much paperwork chilled his bones like never before, and pushed him to make sure that his protégé would never end up like this.

All in all, it’s a story that is flawed in that it doesn’t realize and capitalize on all the things it initially gets right, so the character focus gets diluted a bit, but it’s still an entertaining and very distinctive story that I remembered and came back to.

I forgot to take notes for a rating when I was re-reading it, but I estimate it’s probably a 4.5 out of 5.

Posted in Fanfiction | 1 Comment

Fanfiction – Mastermind Hunting

When I was in my teen years, I read a lot of stuff that isn’t as good as I remember. Normally, I don’t review it but, for a change, let’s review something that’s a bit of a study in contrasts. It’s an interesting way to get some perspective.

Mastermind Hunting by Louis IX is a story that stuck in my memory for its distinctive details, even as I forgot how deeply flawed it was.

On the broadest level, you could say that the first half to two thirds of this 616,225-word story matches the synopsis: A “Where in the world is Harry Potter” story …but even that is flawed.

The first story arc does have Harry and the Dursleys wandering through Europe and the Middle East… but neither Dumbledore nor anyone else in the wizarding world is actively pursuing them in any significant fashion. You’ve got roughly a novel of a pre-Hogwarts wandless magic prodigy wandering through 1980s and 1990s muggle world events… not really being chased by muggle intelligence services so much as wandering in and out of intrigue because they have no idea what he’s capable of.

…and that’s a recurring theme that doesn’t help the story. It feels like the movie Shanghai Nights was still fresh in Louis IX’s memory but he didn’t properly understand how to make that sort of “alternative history for real-world people and events” writing work. (Rule #1: Shanghai Nights works because it’s dedicated to being a comedy. This just feels like the author coming up with implausible alternative causes for real-world events because it makes him feel clever.)

You could also say that recurring theme is “[Insert scene about some tangent] …but that’s not important to our story.”

That said, some of those tangents are also why I remember it, because they’re so unlike anything I’ve ever seen before… such as young Harry accidentally apparating into a particle accelerator, going a bit Doctor Manhattan, crossing paths with some mesoamerican gods, and recovering but with eyeballs made of magically contained seawater… in the process, creating a digital clone of himself.

Those are all things which could make an interesting story… maybe even an interesting Harry Potter story… but not here and not all in the same story. In Mastermind Hunting, elements which would need to be the central focus of a story to be done properly are set up, then allowed to languish… and yet I kept reading to the end, which has to say something for Louis IX’s writing instincts.

It’s also one of those “the guy who wrote this is either a teenage boy or a sociopath” stories, where the author clearly doesn’t understand the problem with certain writing decisions… first and foremost, how liberally Harry uses mind magic to fix problems. Harry ran into a terrorist or death eater sympathizer? Let’s just mind-rape him to install a better moral compass. Sure, its interpretation of an “inner world”-style occlumency is creative, but tons of stories have done that and some in more creative ways. (Though its interpretation of how apparation works and why wizards have trouble doing it over long distances is more novel.)

As you might expect from such an author, narrative structure also isn’t ideal. By Chapter 22, it feels like it should be nearing the end of an arc, but no… and at the end, Voldemort gets defeated with two large chapters to go, and then Umbridge is introduced, only to be gotten rid of before the chapter is over so two more brief “It’s not over yet” arcs can be done.

Spoiler: It involves letting the readers become convinced that a very major character is dead, then having Harry fail to use a Time Turner to prevent her death, then having Harry’s digital copy (who became a flesh-and-blood copy and then spent off-screen time becoming an unrecognizably divergent OC) get a better time-travel solution from the Department of Mysteries and engage in a cheap knock off of a James Bond thriller that seems to be intended as a tacked-on way to close out plot threads that were allowed to languish so much that I’d honestly forgotten they needed to be closed.

It’s kind of impressive that, in less than the entirely of the second-last chapter, he managed to kill off my ability to care as far as he did.

…which reminds me of another problem that isn’t a one-off thing: Not understanding how much preparation goes into keeping the death of a named character from feeling cheep and ill-fitting. Even J.K. Rowling fumbled that in killing off Cedric Diggory after four books of letting the readers get used to the idea of Voldemort being about as effective as a Saturday morning cartoon villain.

Anyway, next point… “crossing paths with some mesoamerican gods”. This includes not only mesoamerican gods, but also Wadjet, and the Archangel Raphael, and, like the mind magic and character death, they give a strong sense that the author didn’t think deeply enough on the implications and effects of including them. Harry Potter canon was careful to avoid leaning toward or against anything religious beyond mentioning the witch burnings and, even if you’re going with the easy choice of old Celtic gods, it takes a lot of skill to keep divine beings from destroying the Harry Potter atmosphere.

I also don’t like that they take away his metamorphmagus ability at the end because it was a gift he “doesn’t need anymore”. That sort of “implicitly lording it over the mortals” is why I don’t like gods in settings in the first place. Aside from that, why did Louis IX think it was even necessary? Because it made him too overpowered without Voldemort to balance him? That’s the mark of a bad author who doesn’t understand that “even Superman can’t punch clinical depression”. (If Wolverine’s healing ability didn’t break X-Men as a story, then Harry keeping his metamorph abilities wouldn’t here. I find that the best conflicts are the kind that superpowers can’t solve.)

…it doesn’t help that, for a final story arc and a third “it ain’t over yet” in two chapters, Louis IX decided to reaffirm his perception that it doesn’t irreparably tarnish your hero for them to invent a “stop being evil” mind virus. (I have only read the spin-offs, but I get a strong impression that was the core evil of the infamous “The Conversion Bureau” fanfic in the My Little Pony fandom.)

Again, “either a teenage male or a sociopath”… but, given how the final chapter falls apart into a mish-mash of “isn’t this cool?” stuff, and the touches of “didn’t think about the implications” that show up in other ways, I’m leaning toward teenage male. If you want to see sociopath, look at fics by Jared Ornstead under his Perfect Lionheart identity, like Partially Kissed Hero or Chunin Exam Day… those start out “definitely gonna hook you” great and then screw with you… and that’s not the only reason to think sociopath. Nugar wrote a great analysis (mirror) of why those two fics exude sociopathy.

However, despite all those flaws, I didn’t find it too difficult to re-read it when preparing to write this. Trial by Tenderness by Cevn McGuire also has a similar number of memorable elements, but I had to give up on it when I tried to re-read and review it. It also helps that Mastermind Hunting is one of those rare ultra-long stories that’s actually complete, rather than just dying when the author’s muse did, like with Guardian, Millennium, and Big Human on Campus by Black_Dragon6.

In the end, it’s a hard one to rate because it’s too sloppy for 4 out of 5 to feel right, but I didn’t get the firm sense of “I don’t want to put this down, but I can’t figure out why” that 3.5 out of 5 would suggest. I’d say 3.8 out of 5 if you stop reading before Harry’s special abilities are taken away in the wake of Voldemort’s defeat, or 3.6 if you continue reading until the end.

Posted in Otaku Stuff | 1 Comment