Всем известна польза юнит-тестирования. Прежде всего, написание тестов одновременно с кодом позволяет раньше выявлять ошибки и не тратить впоследствии время на трудоемкую комплексную отладку. В случае embedded-разработки у юнит-тестирования есть особенности, связанные, во-первых, с тем, что код выполняется где-то глубоко в недрах устройства и взаимодействовать с ним довольно сложно, и, во-вторых, код сильно завязан на целевое железо. Если в проекте есть фрагменты, не зависящие от аппаратуры и при этом реализующие достаточно сложную логику, для них применение модульных тестов даст наибольшую выгоду. Например, это может быть реализация какого-то протокола передачи данных, различные расчеты или управляющий конечный автомат.  Существует три способа запуска юнит-тестов для встраиваемых платформ: В этой публикации приведен способ настройки юнит-тестов в среде STM32CubeIDE, основанной на Eclipse и предназначенной для разработки для контроллеров семейства STM32. Язык разработки – С, но сами тесты пишутся на С++. Тесты будут запускаться на хост-машине c Windows с использованием Cygwin. В качестве тестового фреймворка используется Google Test. Результаты будут отображаться в специальном окне плагина для юнит-тестирования, и их можно будет запустить одной кнопкой из проекта для STM32:  Описанный способ подойдет и для других сред разработки на основе Eclipse, если конечно добрые производители не слишком сильно их урезали в угоду удобству разработчиков. Также этот метод будет работать и c CubeIDE под Linux, при этом не потребуется возиться с Cygwin. Устанавливаем Cygwin, версия x86. В инсталляторе выбираем дополнительные пакеты: gcc-core, g++, binutils, automake, autoconf, cmake, libtool, gdb, make. Можно ставить последние стабильные версии пакетов.  Также нужно прописать переменные среды:  PATH: …;C:\<path_to_Cygwin>\Cygwin\bin; C:\<path_to_Cygwin>\Cygwin\lib
classpath: C:\<path_to_Cygwin>\Cygwin\lib Среда устанавливается как обычно. Желательно ставить CubeIDE после Cygwin, потому что в этом случае Cube сам подхватит существующий Cygwin тулчейн. Сначала создадим проект С++ для x86 Cygwin платформы. Он нам понадобится, чтобы, во-первых, проверить работоспособность тулчейна, а во-вторых, мы будем использовать его как «донора» конфигурации сборки для основного проекта.  Выбираем File > New > C/C++ Project. Выбираем C++ Managed Build. Создаем проект типа hello world для тулчейна Cygwin GCC:  Далее нужно будет выбрать, какие конфигурации сборки создавать. Достаточно только Debug.
Теперь можно проверить, что проект собирается, выбрав Project > Build All. Также желательно проверить и отладку под Cygwin, запустив Run > Debug As > Local C/C++ Application. Приложение выведет «Hello world» в консоль внутри CubeIDE. Для того, чтобы отладчик мог показывать исполняемые строки в файлах исходного кода, нужно настроить отображение путей. В окне Window > Preferences во вкладке С/С++ > Debug нужно выбрать Source Lookup Path и добавить новое отображение: Add > Path Mapping. В окне нужно назвать как-нибудь новое отображение и добавить строчки для дисков, которые есть в системе:   Для красивого запуска тестов нам также понадобится плагин для Eclipse с поддержкой юнит-тестов для С++. Он ставится прямо из STM32CubeIDE: меню Help > Install New Software, далее выбрать репозиторий Eclipse Repository и установить плагин С/С++ Unit Testing Support.  Исходный код библиотеки можно взять по ссылке: https://github.com/google/googletest/tree/release-1.8.1
Распаковываем исходники, заходим в директорию googletest-release-1.8.1 с помощью Cygwin terminal, и запускаем: После успешной сборки файл статической библиотеки будет лежать в ./googlemock/lib/libgtest.a, а заголовочные файлы будут находиться в каталоге ./googletest/include/gtest/. Их нужно будет скопировать в наш проект (или прописать путь к этим файлам в настройках проекта). Проект для отладочной платы STM32L476G-DISCO. Пример будет не слишком изощренным – на плате есть два светодиода, пусть показывают двоичный счетчик от 00 до 11. Реализуем для счетчика отдельный модуль, описанный в паре .h и .c файлов, и напишем для него тест.
Проект можно создавать как обычно, с помощью конфигуратора Cube, главное убедиться, что выводы PB2 и PE8 настроены как цифровые выходы. При создании проекта лучше будет указать тип – С++, это понадобится для компиляции тестов (основной код будет по-прежнему компилироваться С-компилятором). Сконвертировать проект из C можно будет и позже, нажав на название проекта ПКМ и выбрав «Convert to C++». Для компиляции под МК и для тестов нам понадобятся две разные конфигурации сборки. В этих конфигурациях будут собираться разные наборы файлов – в основную попадут модули для работы с железом и тестируемые модули, а в тестовую – те же тестируемые модули и файлы тестов. Поэтому создадим в корне проекта разные каталоги – Application c кодом приложения для МК (можно просто переименовать директорию Src, которую создал Cube), Common для модулей, не зависящих от железа (которые мы будем тестировать) и Tests для тестов. Директории можно исключать из сборки, кликнув ПКМ по их названию, меню Resource Configuration > Exclude from build. Добавим в каталог Common наш модуль счетчика: (led_counter.h): led_counter.c: Директории Common и Tests нужно добавить в путь поиска include-файлов: свойства проекта (Properties) > С/С++ General > Paths and Symbols > Includes. Добавим в main работу со светодиодами  main.c: Проект должен компилироваться и запускаться, а светодиоды – мигать. Теперь то, ради чего все затевалось. Создадим новую конфигурацию сборки через свойства проекта – Properties > C/C++ Build > Settings > Manage Configurations. CubeIDE просто так не даст создать конфигурацию для сборки под Cygwin, поэтому скопируем ее из проекта, который мы создали ранее:  Теперь нужно переключиться на эту конфигурацию и настроить пути к файлам исходников и заголовочным файлам. В свойствах проекта во вкладке Paths and Symbols прописываем (при добавлении записи лучше ставить галку в поле «add to all languages»): Далее копируем в проект библиотеку gtest – файл .a в директорию Tests/Lib, а заголовочные файлы в папке gtest – в папку Tests/Inc. В папке Tests создаем новый файл main.cpp, в котором будут запускаться тесты. Его содержимое стандартное: main.cpp: Также для проверки работы сетапа создадим один тест, который будет проверять, что в нашем окружении размер указателя 32 бита (мы хотим убедиться, что он такой же, как и на микроконтроллере, для этого мы ставили 32-битный Cygwin). Создаем такой файл теста test_platform.cpp: Теперь, если проект запустить как обычное С++ Application, в отладочном выводе будет сообщение от Google Test о том, что все тесты пройдены. Структура проекта должна иметь примерно такой вид:
 Теперь напишем тесты для нашего модуля светодиодного счетчика. Файлы тестов можно расположить в папке Tests: Чтобы результаты тестов отображались в красивом окошке, нужно создать новую конфигурацию запуска в меню Run > Debug Configurations. Установленный плагин позволяет создавать конфигурации типа C/C++ Unit. Создадим ее, назовем Run Tests, выберем используемую конфигурацию сборки «Test» и снимем галку «stop on startup at» на вкладке Debugger. После этого конфигурацию можно запустить. Для появления окна с результатами его нужно выбрать в Window > Show View > Other > C/C++ > C/C++ Unit.  Готово! Теперь проект можно компилировать и запускать под целевой МК как обычно. Когда нужно будет запустить локальные тесты, при запуске конфигурации Run Tests проект автоматически будет пересобран под x86, среда выполнит тесты и покажет результат. Embedded developer