1. 5
  1.  

  2. 4

    This is a good start, but halting after the first test failure is pretty limiting in practice.

    I wrote greatest to have test infrastructure that was straightforward to use (you #include "greatest.h" and add a few lines to initialize it), but it still provides most of the functionality one might expect from a real test runner that isn’t 3 lines of code – running tests with arguments, running a subset of tests based on a name, printing a report after the test batch, printing “expected (X) but got (Y)” when assertions fail, grouping tests into named suites (optional), and so on.

    It’s still small (the most recent release is 806 LOC according to SLOCCount, and I intend to always keep it under 1,000), has no memory allocation, and actively avoids dependencies – I’ve used it on embedded systems, and it’s been used to test OS kernels.

    1. 1

      Neat! I need to have a look but would be nice to have it optionally output tap formatted text. I might get a bug up my but to try this out for my next c project just to show people at work you can unit test c stuff. Been using libtap in the past but its not the greatest (pun not intentional honest!). I’m particularly interested that you have no memory allocation, that makes me want to try it in a kernel module outputting to dmesg.

      1. 1

        Rather than having it support tap directly, I would rather include a wrapper script that converts its output. The greatest output format is consistent, and mostly a superset of the info presented in tap.

        It already has an awk script (greenest) that adds terminal escapes for color highlighting. It doesn’t belong in the core library because not everything is a Unix terminal.

        1. 1

          Ah, suppose thats fair enough. I’ll poke at it a bit with a sharp stick and see what part of the bears claw I get trying to use it.

          So do you use this to output over stuff like gpio i2c type setups?

          1. 1

            Yes, it’ll work if stdout is directed to a UART, but whenever possible I design my embedded projects so that the majority of the code can be meaningfully tested off the hardware. First, bench test code exercises the device drivers independently of the application logic (e.g. for a NOR flash chip, do an erase, a read, a write, a read in a scratch location), to check that the drivers work. These get written while bringing up the hardware. Then, host tests check the logic using simulated hardware (e.g. if various sequences of storage operations run on an in-memory buffer that behaves like NOR flash, does everything behave as expected?) – these tests can run in CI, and run with generated input as part of property-based testing. Finally, integration tests run real scenarios on the hardware, stressing things to check for integration issues like timing-based bugs.

    2. 1

      I really liked the footnote about the do {} while(0); in the macro! I always had a gut feeling our multiline macros were wrong somehow, but it gets explained nicely here.