Years ago I started writing Graphene as a small library of 3D
transformation-related math types to be used by GTK (and possibly Clutter,
even if that didn’t pan out until Georges started working
on the Clutter fork inside Mutter).

Graphene’s only requirement is a C99 compiler and a decent toolchain capable
of either taking SSE builtins or support vectorization on appropriately
aligned types. This means that, unless you decide to enable the GObject
types for each Graphene type, Graphene doesn’t really need GLib types or
API—except that’s a bit of a lie.

As I wanted to test what I was doing, Graphene has an optional build time
dependency on GLib for its test suite; the library itself may not use
anything from GLib, but if you want to build and run the test suite then you
need to have GLib installed.

This build time dependency makes testing Graphene on Windows a lot more
complicated than it ought to be. For instance, I need to install a ton of
packages when using the MSYS2 toolchain on the CI instance on AppVeyor,
which takes roughly 6 minutes each for the 32bit and the 64bit builds; and I
can’t build the test suite at all when using MSVC, because then I’d have to
download and build GLib as well—and just to access the GTest API, which I
don’t even like.

What’s wrong with GTest

GTest is kind of problematic—outside of Google hijacking the
name of the API for their own testing framework, which makes looking for it
a pain. GTest is a lot more complicated than a small unit testing API
needs to be, for starters; it was originally written to be used with a
specific harness, gtester, in order to generate a very brief
HTML report using gtester-report, including some
timing information on each unit—except that gtester is now deprecated
because the build system gunk to make it work was terrible to deal with. So,
we pretty much told everyone to stop bothering, add a --tap argument when
calling every test binary, and use the TAP harness in Autotools.

Of course, this means that the testing framework now has a completely
useless output format, and with it, a bunch of default behaviours driven by
said useless output format, and we’re still deciding if we should break
backward compatibility to ensure that the supported output format has a sane
default behaviour.

On top of that, GTest piggybacks on GLib’s own assertion mechanism, which
has two major downsides:

it can be disabled at compile time by defining G_DISABLE_ASSERT before
including glib.h, which, surprise, people tend to use when releasing;
thus, you can’t run tests on builds that would most benefit from a test suite

it literally abort()s the test unit, which breaks any test harness
in existence that does not expect things to SIGABRT midway through
a test suite—which includes GLib’s own deprecated gtester harness

To solve the first problem we added a lot of wrappers around g_assert(),
like g_assert_true() and g_assert_no_error(), that won’t be disabled
depending on your build options and thus won’t break your test suite—and if
your test suite is still using g_assert(), you’re strongly encouraged to
port to the newer API. The second issue is still standing, and makes running
GTest-based test suite under any harness a pain, but especially under a
TAP harness, which requires listing the amount of tests you’ve run, or that
you’re planning to run.

The remaining issues of GTest are the convoluted way to add tests using a
unique path; the bizarre pattern matching API for warnings and errors; the
whole sub-process API that relaunches the test binary and calls a single
test unit in order to allow it to assert safely and capture its output. It’s
very much the GLib test suite, except when it tries to use non-GLib API
internally, like the command line option parser, or its own logging
primitives; it’s also sorely lacking in the GObject/GIO side of things, so
you can’t use standard API to create a mock GObject type, or a mock GFile.

If you want to contribute to GLib, then working on improving the GTest API
would be a good investment of your time; since my project does not depend on
GLib, though, I had the chance of starting with a clean slate.

A clean slate

For the last couple of years I’ve been playing off and on with a small test
framework API, mostly inspired by BDD frameworks like
Mocha and Jasmine. Behaviour Driven Development is
kind of a buzzword, like test driven development, but I particularly like
the idea of describing a test suite in terms of specifications and
expectations: you specify what a piece of code does, and you match results
to your expectations.

The API for describing the test suites is modelled on natural language
(assuming your language is English, sadly):

I’m planning to add some additional output formatters, like JSON and XML.

Using µTest

Ideally, µTest should be used as a sub-module or a Meson sub-project of your
own; if you’re using it as a sub-project, you can tell Meson to build a
static library that won’t get installed on your system, e.g.:

µTest is kind of experimental, and I’m still breaking its API in places, as
a result of documenting it and trying it out, by porting the Graphene test
suite to it. There’s still
a bunch of API that I’d like to land, like custom matchers/formatters for
complex data types, and a decent want to skip a specification or a whole
suite; plus, as I said above, some additional formatted output.

If you have feedback, feel free to open an issue—or a pull request wink
wink nudge nudge.