9. Hints and Tips

This chapter is intended to contain tips for situations that you need some help with, but it is nowhere near complete at this time.

9.1. cgreen-mocker - Automated Mocking

Are you starting out with Cgreen on a largish legacy system? And there are loads and loads of functions to mock to get a unit under test?

You could try the cgreen-mocker that is supplied as a contributed part of the Cgreen source distribution.

It is a Python program that parses C language header files and tries to create a corresponding .mock file where each function declaration is replaced with a call to mock().

Usage:
  cgreen-mocker.py <headerfile> { <cpp_directive> }
     <headerfile>: file with function declarations that you want
                   to mock
     <cpp_directive>: any 'cpp' directive but most useful is e.g.
                      "-I <directory>" to ensure cpp finds files.

So given a header file containing lines like

extern Value make_integer_value(int integer);
extern Value make_string_value(const char *string);

cgreen-mocker will, given that there are no errors, print something like this on the screen:

Value make_integer_value(int integer) {
  return mock(integer);
}

Value make_string_value(const char *string) {
  return mock(string);
}

Of course, you would pipe this output to a file.

To use cgreen-mocker you need Python and pycparser. The latter can be found at [https://github.com/eliben/pycparser] or can easily be installed with

$ pip install pycparser
cgreen-mocker is an unsupported contribution to the Cgreen project by Thomas Nilefalk.

9.2. Compiler Error Messages

Sometimes you might get cryptic and strange error messages from the compiler. Since Cgreen uses some C/C++ macro magic this can happen and the error messages might not be straight forward to interpret.

Here are some examples, but the exact messages differ between compilers and versions.

Compiler error message

Probable cause…​

"contextFor<X>" is undeclared here

Missing Describe(<X>);

undefined reference to 'AfterEach_For_<X>'

Missing AfterEach(<X>)

CgreenSpec<X><Y>__ is undeclared

Missing test subject/context in the Ensure of a BDD style test

use of undeclared identifier 'contextFor<X>'

Missing Describe(<X>);

9.3. Signed, Unsigned, Hex and Byte

Cgreen attempts to handle primitive type comparisons with a single constraint, is_equal_to(). This means that it must store the actual and expected values in a form that will accomodate all possible values that primitive types might take, typically an intptr_t.

This might sometimes cause unexpected comparisons since all actual values will be cast to match intptr_t, which is a signed value. E.g.

Ensure(Char, can_compare_byte) {
  char chars[4] = {0xaa, 0xaa, 0xaa, 0};
  assert_that(chars[0], is_equal_to(0xaa));
}

On a system which considers char to be signed this will cause the following Cgreen assertion error:

char_tests.c:11: Failure: Char -> can_compare_byte
        Expected [chars[0]] to [equal] [0xaa]
                actual value:                   [-86]
                expected value:                 [170]

This is caused by the C rules forcing an implicit cast of the signed char to intptr_t by sign-extension. This might not be what you expected. The correct solution, by any standard, is to cast the actual value to unsigned char which will then be interpreted correctly. And the test passes.

Casting to unsigned will not always suffice since that is interpreted as unsigned int which will cause a sign-extension from the signed char and might or might not work depending on the size of int on your machine.

In order to reveal what really happens you might want to see the actual and expected values in hex. This can easily be done with the is_equal_to_hex().

Ensure(Char, can_compare_byte) {
  char chars[4] = {0xaa, 0xaa, 0xaa, 0};
  assert_that(chars[0], is_equal_to_hex(0xaa));
}

This might make the mistake easier to spot:

char_tests.c:11: Failure: Char -> can_compare_byte
        Expected [chars[0]] to [equal] [0xaa]
        actual value:                   [0xfffffffffffffaa]
        expected value:                 [0xaa]

9.4. Cgreen and Coverage

Cgreen is compatible with coverage tools, in particular gcov/lcov. So generating coverage data for your application should be straight forward.

This is what you need to do (using gcc or clang):

  • compile with -ftest-coverage and -fprofile-arcs

  • run tests

  • lcov --directory . --capture --output-file coverage.info

  • genhtml -o coverage coverage.info

Your coverage data will be available in coverage/index.html.

9.5. Garbled Output

If the output from your Cgreen based tests appear garbled or duplicated, this can be caused by the way Cgreen terminates its test-running child process. In many unix-like environments the termination of a child process should be done with _exit(). However, this interfers severily with the ability to collect coverage data. As this is important for many of us, Cgreen instead terminates its child process with the much cruder exit() (note: no underscore).

Under rare circumstances this might have the unwanted effect of output becoming garbled and/or duplicated.

If this happens you can change that behaviour using an environment variable CGREEN_CHILD_EXIT_WITH__EXIT (note: two underscores). If set, Cgreen will terminate its test-running child process with the more POSIX-compliant _exit(). But as mentioned before, this is, at least at this point in time, incompatible with collecting coverage data.

So, it’s coverage or POSIX-correct child exits and guaranteed output consistency. You can’t have both…​