Перейти к содержанию

Добавляем 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 использовать для генерации и построения исходников)

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

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

Для примера буду показывать на тестовом проекте, который назван 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, всегда разделяя команды друг от друга. У меня не одного такие проблемы возникли – об этой гадости подробнее тут:

Eclipse Community Forums: C / C++ IDE (CDT) » Post-Build Step

https://www.eclipse.org/forums/index.php/t/58614/
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

Comment

programel