There are a few things going on here that are slightly orthogonal, but are all uncontroversial progress that had to happen eventually: test runner options, logging, factoring out duplication in `TestResult`...
The implementation is slightly awkward, and will be much more so when we'll want to count characters for nicer terminal layout. It's maybe not the best UX anyway. We'll think about alternatives.
Note that:
- This allows us to remove some very temporary hacky code from day 4.
- This includes a refactoring to hide `TestTree`, which could in theory have been made separate.
- This reverts a lot of 1163889.
Ideally we'd similarly add the ability for later tests to use the results of earlier ones. But this would probably require much heavier type family usage.
There are some drawbacks:
- No properly lazy golden tests. This would in principle be nice when e.g. using `pretty-simple`.
- Because tests can be created dynamically, they can't be listed up front without running them. This presumably makes filtering slightly more annoying to use in practice.
- Terminal output is less compact than tasty, both horizontally and vertically. There appears to be no way to change this.
- We end up defining an orphan `Monoid (TestDefM '[] () ())` instance, to avoid changing much downstream code. Note though that this is not strictly necessary, and could potentially be contributed upstream.
- There's a warning about threads in GHCI which we can't seem to disable.
- The license forbids use in commercial projects without sponsoring. Thankfully that doesn't apply here.
Anyway, it's generally very impressive. It simplifies a few things for us, and will particularly help when we come to want to specify dependencies between tests.
For now this applies to Haskell only, and it may turn out to be tricky for the Rust implementation.
In practice, the limitation hasn't turned out to be important, and we could even go the other way and use `Integer` everywhere. This does however at least help with debugging, as well as just being conceptually right.
The `nil` and `(/\)` functions are intended to be overloaded to work for other list-like things in a later commit, and from there we will investigate using `OverloadedLists` and `RebindableSyntax` to recover standard list syntax, although there are probably limitations due to `(:)` being special.