Raspberry Pico: Unit Test Framework for Your Projects
Tags: raspberrypi, pico, testing
Published: 2021-05-10 05:16:04

The Raspberry Pico is a new microcontroller launched in February 2021. The community was excited about this new board, and several people started amazing projects. I grabbed two boards early on, and while still working on my Arduino based robot, did the usual blinking led and potentiometer tutorials.

The Pico captured me, I wanted more than just run demos. So, I decided to start library development for a shift register and a temperature sensor. When developing a library, I want to have tests for several reasons. First, I like to use TDD and start with writing a test that will cover a n new feature before its implementation. Second, once you have a substantial test suite, it helps you to keep the library in a working shape when you refactor its code base.

In this article, I will show how to install and use the unit testing framework [cmocka](https://cmocka.org/). We will see the basic boilerplate code and an example for testing a Raspberry Pico program.

_This article originally appeared at [my blog](https://admantium.com/blog/pico03_testing_with_cmocka/)._

## Installation

Grab the CMocka source from the official [cmocka mirror](https://cmocka.org/files/1.1/). Then, extract the tar, compile and install. The steps in a nutshell:

```bash
wget https://cmocka.org/files/1.1/cmocka-1.1.5.tar.xz
tar xvf cmocka-1.1.5.tar.xz
cd cmocka-1.1.5
mkdir build
cd build
cmake ..
make
```

The `make` step should show this output:

```bash
Scanning dependencies of target cmocka
[  4%] Building C object src/CMakeFiles/cmocka.dir/cmocka.c.o
[  9%] Linking C shared library libcmocka.so
[  9%] Built target cmocka
Scanning dependencies of target assert_macro_test
[ 13%] Building C object example/CMakeFiles/assert_macro_test.dir/assert_macro.c.o
...
[ 95%] Building C object example/mock/uptime/CMakeFiles/uptime.dir/uptime.c.o
[100%] Linking C executable uptime
[100%] Built target uptime
```

If all goes well, you can install the compiled libraries in your system.

```bash
sudo make install

[  9%] Built target cmocka
...
[100%] Built target uptime
Install the project...
-- Install configuration: ""
-- Installing: /usr/local/lib/pkgconfig/cmocka.pc
-- Installing: /usr/local/lib/cmake/cmocka/cmocka-config.cmake
-- Installing: /usr/local/lib/cmake/cmocka/cmocka-config-version.cmake
-- Installing: /usr/local/include/cmocka.h
-- Installing: /usr/local/include/cmocka_pbc.h
-- Installing: /usr/local/lib/libcmocka.so.0.7.0
-- Installing: /usr/local/lib/libcmocka.so.0
-- Installing: /usr/local/lib/libcmocka.so
```

The files will be installed at /usr/local/lib.

## Unit Test Example

Let’s write a very basic unit test example.

```c
/*
* ---------------------------------------
* Copyright (c) Sebastian Günther 2021  |
*                                       |
* devcon@admantium.com                  |
*                                       |
* SPDX-License-Identifier: BSD-3-Clause |
* ---------------------------------------
*/
#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <cmocka.h>

static void test_integers(void** state) {
  assert_int_equal(1,1);
}

int main(int argc, char* argv[]) {
  const struct CMUnitTest tests[] = {
    cmocka_unit_test(test_integers),
  };

  return cmocka_run_group_tests(tests, NULL, NULL);
}
```

The important things here:

- Always include all four libraries: `<stdarg.h>, <stddef.h>, <setjmp.h>, <cmocka.h>`
- Define test cases as functions that receive an argument `void** state`
- The test functions include different type of assert statements, shown here is `assert_int_equal` see the [official documentation](https://api.cmocka.org/group__cmocka__asserts.html) for the full list of asserts.
- In the `main` function, add all defined test functions to the struct `CMUnitTest tests[]`

## Running Tests

To invoke that test on the CLI, you will need to add CMocka installation path to the environment variable export LD_LIBRARY_PATH.

```bash
export LD_LIBRARY_PATH=/usr/local/lib:${LD_LIBRARY_PATH}
```

Then, run your compiler and link to the CMocka library. I'm using `clang` in the following example.

```bash
clang -std=c18  -l cmocka simple.test.c -o tests.bin
```

Finally, you can run the test, and see formatted output that shows which tests were successful.

```bash
$> ./test.bin

[==========] Running 1 test(s).
[ RUN      ] test_integers
[       OK ] test_integers
[==========] 1 test(s) run.
[  PASSED  ] 1 test(s).
```

## Testing a Pico Program

Now that we have setup the testing framework, let’s use it to write tests for our Pico programs. At the time of writing this article, I was developing a library for working with shift registers. The library exposes a struct object that defines the pin layout, and several functions for setting bits or a bitmask to the shift register. I will not cover the entire library, but just highlight two test cases that show the essential how-to. Go to Github to see the entire [rp2040-shift-register-74HC595 library](https://github.com/admantium-sg/rp2040-shift-register-74HC595)).

### ShiftRegister Struct: Definition and Testing

The shift register is controlled by three input pins:

- Serial (SER): Set a single bit, low or high
- Serial Clock (SRCLK): Send a clock signal that will write the active SER bit to the shift register
- Register Clock (RCLK): Send a clock signal to copy the contents of the shift register into the storage register

These pins are defined in the following `struct` object.

```c
typedef struct ShiftRegister
{
  u_int8_t SERIAL_PIN;
  u_int8_t SHIFT_REGISTER_CLOCK_PIN;
  u_int8_t STORAGE_REGISTER_CLOCK_PIN;
} ShiftRegister;
```

The first test is about initializing a shift register and see that it's defined pined are correctly defined inside the struct. We will use the familiar `assert_int_equal` test.

```c
void test_shift_register_config(void **state)
{
  ShiftRegister reg = {14, 11, 12};
  assert_int_equal(reg.SERIAL_PIN, 14);
  assert_int_equal(reg.SHIFT_REGISTER_CLOCK_PIN, 11);
  assert_int_equal(reg.STORAGE_REGISTER_CLOCK_PIN, 12);
}
```

Running the tests gives this output:

```bash
Runing Tests
[==========] Running 1 test(s).
[ RUN ] test_shift_register_config
[ OK ] test_shift_register_config
[==========] 1 test(s) run.
[ PASSED ] 1 test(s).
```

### Writing a single bit

The most basic function is to write a single bit into the shift register. To keep track of this, the register object holds two state variables: The `serial_pin_state` and the `shift_register_state`. If a new bit is written with the `write_bit` function, the state will be updated accordingly.

To implement this, we first add the state variables to the `ShiftRegister`.

```c
typedef u_int8_t bitmask;

typedef struct ShiftRegister;
{
  bool serial_pin_state;
  u_int8_t shift_register_state;
} ShiftRegister;
```

Then, we implement the `write_bit` function. This function sets the `serial_pin_state` to the given bit. If this bit is a 1, `shift_register_state` will shift right and add a `1`, if the bit is a 0, it will just shift right.

```c
bool write_bit(ShiftRegister *reg, bool b,)
{
  reg->serial_pin_state = b;
  (b) ? (reg->register_state += 0b10) : (reg->register_state <<= 0b01);
  return b;
}

```

For testing, we will write two bits: `1` followed by `0`. After each step, we test the `pin_state` is set correctly. Finally, we test that the resulting bitmask is correct. To receive the bitmask representation of the shift register, the method `print_shift_register` is called, and its compared to a string object. The test method uses `assert_memory_equal`, a convenient test method to test that any types are equal.

```c
void test_write_bit(void **state)
{
  ShiftRegister reg = {14, 11, 12};
  write_bit(1, &reg);
  assert_int_equal(reg.serial_pin_state, 1);
  write_bit(0, &reg);
  assert_int_equal(reg.serial_pin_state, 0);

  printf("Shift Register: %s\n", print_shift_register(&reg));
  assert_memory_equal(print_shift_register(&reg), ®"01000000", 8);
}
```

All tests are passed:

```bash
Running Tests
[==========] Running 2 test(s).
[ RUN ] test_shift_register_config
[ OK ] test_shift_register_config
[ RUN ] test_write_bit
Shift Register: 01000000
[ OK ] test_write_bit
[==========] 2 test(s) run.
[ PASSED ] 2 test(s).
```

## Conclusion

This article introduced the CMocka unit testing framework for C programs. I showed how to compile, install and use it. Then, two examples were shown. The first example showed the necessary boilerplate code that you need to run a CMocka test. The 2nd example showed how to use CMocka for testing Pico code, but with a grain of salt: At the time of writing, I had no experience how to test that the hardware signals were transmitted from the Pico. In a future article about library design, I will cover this issue and detail how to test both the library function "as-is" and the hardware side. In my opinion, unit-testing helps you to write better code: By writing a test before the implementation, you structure the feature upfront, and when you have a substantial test suite, you can better maintain and refactor your code.