А именно, мы будем добавлять отличную систему С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 использовать для генерации и построения исходников)
Он позволяет вызвать терминал в любой папке.
Инициализация
Для примера буду показывать на тестовом проекте, который назван 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
Отлично, все файлы создались в нужных местах!
Прям как настоящее TDD!
В сгенерированных файлах весь код инактивирован проверкой определенного макроса. Чтобы это исправить - в настройках сборки, например, Debug определим макрос TEST.
И ещё - добавим заголовочный файл от 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;
В файле test_module_sample.c теперь все хорошо и весь необходимый синтаксис корректно подсвечивается. Изменений вносить в файл не будем, оставим как есть - один игнорируемый тест.
Запустим сборку Debug версии
Что мы видим в окне 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