Найти в Дзене
ProgramEl

Добавляем Unit-тестирование в проекты STM32CubeIDE

Оглавление

А именно, мы будем добавлять отличную систему Сeedling. Данная система содержит в себе сразу два инструмента - Unity - непосредственно для проведения и написания тестов и CMock для генерации объектов-заглушек. Но самая большая заслуга данного пакета - простота во всех аспектах - начиная от генерации тестируемых модулей и до релиза проекта. Использование Сeedling превращает рутинное TDD (Разработка через тестирование) или TLD(если захочется так) в обычный рабочий процесс.

Как именно использовать данные инструменты:

  • Свежее переиздание Test Driven Development for Embedded C (Pragmatic Programmers) от James W. Grenning
  • Немного устаревшая(Ceedling уже не генерирует rake в тестируемом проекте и некоторые команды изменились), но все еще хорошая статья от Dmitry Frank https://dmitryfrank.com/articles/unit_testing_embedded_c_applications
  • Конечно же, GIT разработчиков https://github.com/ThrowTheSwitch. Там найдется самая всеобъемлющая информация по использованию их инструментов, например, CMock, Ceedling.

Ну, и конечно, когда-то я соберусь с силами написать небольшую заметку

Предположим, что уже знакомы с Ceedling и нужно лишь его как-то прикрутить к нашим проектам, желательно, чтобы тесты запускались сами при старте сборки.

Как инициализировать тестирование в уже существующем проекте
http://www.electronvector.com/blog/add-unit-tests-to-your-current-project-with-ceedling

Настройка окружения

Терминал

У Eclipse, на базе которой построен STM32CubeIDE, есть сложность с вызовом терминала в рабочей директории - он может открыть терминал, но он будет в папке с установленным STM32CubeIDE. Для удобства советую установить TM Terminal (А лучше работать в VSCode, где уже терминал поддерживается, а IDE использовать для генерации и построения исходников)

-2

Он позволяет вызвать терминал в любой папке.

Инициализация

Для примера буду показывать на тестовом проекте, который назван programel_prj. Для инициализации системы тестирования введем следующие команды в открывшимся терминале

E:\programel_prj>cd ../
E:\>ceedling new programel_prj

Welcome to Ceedling!
create programel_prj/project.yml

Project 'programel_prj' created!
- Execute 'ceedling help' from programel_prj to view available test & build task
s

Здесь мы переходим выше на уровень и вводим имя проекта идентичное созданному проекту. Это можно делать и в проектах, которые уже созданы были ранее.

Был создан файл project.yml и папка src. Папку src можно удалить. Если файлы не отобразились в окне с проектом можно обновить через F5.

Правка автогенератора

После инициализации проекта Ceedling, стоит произвести некоторые изменения в файле project.yml. Зачем? (Предположим, что инициализация проекта производится с помощью кодогенератора)

Структура проекта в STM32CubeIDE генерируется сама. После завершения настройки пинов, интерфейсов и т.д. в *.ioc-файле следует запуск кодогенератора. После чего будет произведено построение проекта с определенной структурой. Мы же хотим применить TDD подход при разработке новых модулей в проекте. Удобно, если сам Ceedling будет создавать заголовочные файлы и исходники в нужных местах.

В первую очередь добавим следующее в project.yml после секции с :project::

:project:
:use_exceptions: FALSE
:use_test_preprocessor: TRUE
:use_auxiliary_dependencies: TRUE
:build_root: build
# :release_build: TRUE
:test_file_prefix: test_
:which_ceedling: gem
:ceedling_version: 0.31.1
:default_tasks:
- test:all

:module_generator:
:project_root: ./
:source_root: Core/Src/
:inc_root: Core/Inc/
:test_root: test/

Значит, мы добавили :module_generator: в котором определили где именно будут создаваться новые файлы модуля. Можно и название у папки test сменить, но меня это название устроило. Необходимо сообщить об этом и в секции :paths:

:paths:
:test:
- +:test/**
- -:test/support
:source:
- Core/Inc/**
- Core/Src/**

(Необязательно) В секции :cmock: с добавим дополнительный заголовочный файл с определением стандартных типов (uint8_t, int32_fast_t и т.д.)

:cmock:
:mock_prefix: mock_
:when_no_prototypes: :warn
:enforce_strict_ordering: TRUE
:plugins:
- :ignore
- :callback
:treat_as:
uint8: HEX8
uint16: HEX16
uint32: UINT32
int8: INT8
bool: UINT8
:includes:
- <stdint.h>

Создание модуля

Создадим модуль module_sample. Для этого введем в TM Terminal следующее:

E:\programel_prj>ceedling module:create[module_sample]
File Core/Src/module_sample.c created
File Core/Inc/module_sample.h created
File test/test_module_sample.c created
Generate Complete

Отлично, все файлы создались в нужных местах!

-3

Прям как настоящее TDD!

-4

В сгенерированных файлах весь код инактивирован проверкой определенного макроса. Чтобы это исправить - в настройках сборки, например, Debug определим макрос TEST.

-5

И ещё - добавим заголовочный файл от Unity(unity.h) в папку Inc. Это позволит нам использовать автодополнение при написании непосредственно самих тестов. Данный файл у меня располагается по следующему адресу - E:\Ruby30-x64\lib\ruby\gems\3.0.0\gems\ceedling-0.31.1\vendor\unity\src\

Автоматический прогон тестов при сборке

Чтобы тестирование прогонялось автоматически при каждой сборке - нужно добавить пару комманд в build steps. Все команды в мэйкфайлах выполняться в папках Debug или Relase - в зависимости от выбранной целевой сборки. Чтобы выполнить тестирование - нужно перейти выше по каталогу (в основную папку с проектом) и выполнить команду ceedling. Для добавления пользовательских команд, которые должны быть выполнены до или после сборки есть специальные поля ввода - pre/post build steps. Команды можно объединять в цепочки через специальные знаки (см. Chaining Commands в поисковике).

Мне было бы удобнее в post-build, но в моей версии STM32CubeIDE встроены специфичные генераторы make-файлов. Они форматируют строку post-build, всегда разделяя команды друг от друга. У меня не одного такие проблемы возникли - об этой гадости подробнее тут:

https://www.eclipse.org/forums/index.php/t/58614/Eclipse Community Forums: C / C++ IDE (CDT) » Post-Build Step

cd ../; ceedling test;

-6

В файле test_module_sample.c теперь все хорошо и весь необходимый синтаксис корректно подсвечивается. Изменений вносить в файл не будем, оставим как есть - один игнорируемый тест.

-7

Запустим сборку Debug версии

-8

Что мы видим в окне Console:

15:59:53 **** Build of configuration Debug for project programel_prj ****
make -j12 all
cd ../; ceedling test;


Test 'test_module_sample.c'
---------------------------
Generating runner for test_module_sample.c...
Compiling test_module_sample_runner.c...
Compiling test_module_sample.c...
Compiling unity.c...
Compiling module_sample.c...
Compiling cmock.c...
Linking test_module_sample.out...
Running test_module_sample.out...

--------------------
IGNORED TEST SUMMARY
--------------------
[test_module_sample.c]
Test: test_module_sample_NeedToImplement
At line (17): "Need to Implement module_sample"

--------------------
OVERALL TEST SUMMARY
--------------------
TESTED: 1
PASSED: 0
FAILED: 0
IGNORED: 1


arm-none-eabi-gcc "../Drivers/STM32F0xx_HAL_D..............

Отлично! Выполнился(был проигнорирован) один тест. Мы прикрутили тестирование к нашему проекту, теперь можно достаточно удобно пользоваться инструментом тестирования при разработке своих проектов в STM32CubeIDE