Hi Nordic Devzone,
I started developing a product around nrf52840 and nrf Connect SDK. It was my first time using Zephyr and the SDK, so after a few weeks of getting into it, I started to write some firmware, and I would now like to implement unit tests. I started following the instructions on this page : https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/test_and_optimize/testing_unity_cmock.html
I tried the example_test fine, but I am a bit stuck on how best to do it on my files. I started on some of my simplest files, ui.h/ui.c :
ui.h:
#ifndef UI_H_ #define UI_H_ #include <zephyr/kernel.h> #include <zephyr/sys/printk.h> #include <zephyr/device.h> #include <zephyr/drivers/pwm.h> #include <zephyr/logging/log.h> #define STEP_SIZE PWM_USEC(2000) #define UI_COLOR_MAX 10000 #define UI_LED_INTENSITY_LOW 200 typedef struct { uint16_t red; /* Intensity 0-UI_COLOR_MAX */ uint16_t green; /* Intensity 0-UI_COLOR_MAX */ uint16_t blue; /* Intensity 0-UI_COLOR_MAX */ } ui_rgb_t; uint8_t ui_init(); uint8_t ui_set_rgb_on(ui_rgb_t *color); uint8_t ui_set_rgb_off(); #endif /* UI_H_ */
ui.c :
#include <include/ui.h> LOG_MODULE_REGISTER(UI, LOG_LEVEL_DBG); static const struct pwm_dt_spec red_pwm_led = PWM_DT_SPEC_GET(DT_ALIAS(pwmled1red)); static const struct pwm_dt_spec green_pwm_led = PWM_DT_SPEC_GET(DT_ALIAS(pwmled0green)); static const struct pwm_dt_spec blue_pwm_led = PWM_DT_SPEC_GET(DT_ALIAS(pwmled2blue)); ui_rgb_t off = {0, 0, 0}; uint8_t ui_set_rgb_on(ui_rgb_t *color) { int ret; uint32_t r, g, b; if (color->red >= UI_COLOR_MAX) { color->red = UI_COLOR_MAX; } if (color->green >= UI_COLOR_MAX) { color->green = UI_COLOR_MAX; } if (color->blue >= UI_COLOR_MAX) { color->blue = UI_COLOR_MAX; } r = red_pwm_led.period / UI_COLOR_MAX * color->red; ret = pwm_set_pulse_dt(&red_pwm_led, r); if (ret != 0) { LOG_ERR("Error %d: red write failed.", ret); LOG_HEXDUMP_ERR(color, sizeof(ui_rgb_t), "Error while writing color :"); return 0; } g = green_pwm_led.period / UI_COLOR_MAX * color->green; ret = pwm_set_pulse_dt(&green_pwm_led, g); if (ret != 0) { LOG_ERR("Error %d: green write failed.", ret); LOG_HEXDUMP_ERR(color, sizeof(ui_rgb_t), "Error while writing color :"); return 0; } b = blue_pwm_led.period / UI_COLOR_MAX * color->blue; ret = pwm_set_pulse_dt(&blue_pwm_led, b); if (ret != 0) { LOG_ERR("Error %d: blue write failed.", ret); LOG_HEXDUMP_ERR(color, sizeof(ui_rgb_t), "Error while writing color :"); return 0; } return 1; } uint8_t ui_set_rgb_off() { ui_rgb_t off; off.red = 0; off.green = 0; off.blue = 0; return ui_set_rgb_on(&off); } uint8_t ui_init() { if (!device_is_ready(red_pwm_led.dev) || !device_is_ready(green_pwm_led.dev) || !device_is_ready(blue_pwm_led.dev)) { LOG_ERR("Error: one or more PWM devices not ready"); return 0; } return 1; }
I wanted first to test the ui_init function, so I started writing this ui_test.c file based on example_test.c :
/* * Copyright (c) 2019 Nordic Semiconductor ASA * * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause */ #include <unity.h> #include <ui.h> #include <stdbool.h> #include "device/cmock_device.h" #include "pwm/cmock_pwm.h" static const struct pwm_dt_spec red_pwm_led = PWM_DT_SPEC_GET(DT_ALIAS(pwmled1red)); static const struct pwm_dt_spec green_pwm_led = PWM_DT_SPEC_GET(DT_ALIAS(pwmled0green)); static const struct pwm_dt_spec blue_pwm_led = PWM_DT_SPEC_GET(DT_ALIAS(pwmled2blue)); void setUp(void) { } void test_ui_init(void) { int err; __cmock_device_is_ready_ExpectAndReturn(red_pwm_led.dev, 0); __cmock_device_is_ready_ExpectAndReturn(green_pwm_led.dev, 0); __cmock_device_is_ready_ExpectAndReturn(blue_pwm_led.dev, 0); //__cmock_foo_execute_ExpectAndReturn(0); err = ui_init(NULL); TEST_ASSERT_EQUAL(1, err); } /* It is required to be added to each test. That is because unity's * main may return nonzero, while zephyr's main currently must * return 0 in all cases (other values are reserved). */ extern int unity_main(void); int main(void) { (void)unity_main(); return 0; }
I mocked the device_is_ready function by adding
cmock_handle($ENV{ZEPHYR_BASE}/include/zephyr/device.h device)to the CMakeLists.txt file, and then I wanted to use
__cmock_device_is_ready_ExpectAndReturnfunction. I wondered what to use as the "device" argument, and figured I'd declare the leds the same way I did when using my board. However, obviously, the native_posix board does not have such leds so it doesn't work.
Is my general approach correct ? Do you have any tips on testing such functions that use hardware-related functions ?
Thanks in advance.
Nicolas Goualard