Exit before second main with -funittest

jfondren julian.fondren at gmail.com
Fri Jul 30 02:06:32 UTC 2021


On Friday, 30 July 2021 at 01:01:02 UTC, Brian Tiffin wrote:
> Is this good, bad or indifferent (a right left choice, first 
> one doesn't matter)?

I think you're opening yourself up to errors where some program 
state persists from one run of main to another. You could think 
that some set of flags works fine because it tests OK, but then 
it doesn't work at the CLI with fresh state. This seems more 
appropriate for an external tester, which could also run multiple 
mains in parallel to save on time, etc.

But you could also have a smarter test runner instead of a magic 
unittest, https://dlang.org/spec/traits.html#getUnitTests has an 
example of replacing the built-in test runner. That could exit 
before main for you, or fork and run CLI tests in a separate 
process (even if just to avoid persistent state, with the parent 
just waiting for the child to finish).

Quick example of that:

```d
module udatest;

struct testCLI {
}

unittest {
     // unrelated test
     assert(true);
}

@testCLI unittest {
     main(["exe", "world"]);
}

@testCLI unittest {
     main(["exe", "note: static int wasn't altered"]);
}

unittest {
     import std.exception : enforce;

     main(["exe"]);
     enforce(false, new Exception("dummy"));
}

@testCLI unittest {
     main(["exe", "up to main#2 now"]);
}

version (unittest) {
     bool tester() {
         import std.meta : AliasSeq, Filter;
         import std.traits : getUDAs;
         import core.sys.posix.unistd : fork;
         import core.sys.posix.sys.wait : waitpid;
         import core.sys.posix.stdlib : _Exit;
         import std.stdio;

         alias cli = AliasSeq!(__traits(getUnitTests, udatest));
         static foreach (i; 0 .. cli.length) {
             writefln!"Test %d/%d"(i + 1, cli.length);
             try {
                 if (getUDAs!(cli[i], testCLI).length) {
                     if (auto child = fork) {
                         int res;
                         assert(-1 != waitpid(child, &res, 0));
                         assert(res == 0);
                     } else {
                         cli[i]();
                         stdout.flush;
                         stderr.flush;
                         _Exit(0);
                     }
                 } else
                     cli[i]();
             } catch (Exception e) {
                 writeln(e);
             }
         }
         return false;
     }

     shared static this() {
         import core.runtime : Runtime;

         Runtime.moduleUnitTester = &tester;
     }
}

void main(string[] args) {
     import std.stdio : writefln;

     static int n = 0;
     writefln!"main#%d called with %s"(++n, args[1 .. $]);
}
```


More information about the Digitalmars-d-learn mailing list