Введение
Все знают преимущества модульного тестирования.
Прежде всего, написание тестов одновременно с кодом позволяет раньше выявлять ошибки и не тратить впоследствии время на трудоемкую сложную отладку.
В случае встраиваемой разработки юнит-тестирование имеет особенности, связанные, во-первых, с тем, что код исполняется где-то глубоко в недрах устройства и с ним достаточно сложно взаимодействовать, а, во-вторых, код сильно завязан к целевому оборудованию.
Если в проекте есть фрагменты, независимые от аппаратного обеспечения и в то же время реализующие достаточно сложную логику, то использование модульных тестов принесет для них наибольшую пользу.
Например, это может быть реализация какого-то протокола передачи данных, различных вычислений или управляющего конечного автомата.
Существует три способа запуска модульных тестов для встраиваемых платформ:
- Запускайте непосредственно на целевой платформе.
В этом случае вы сможете работать с аппаратной частью устройства, и код будет работать точно так же, как и в боевых условиях.
Однако для тестирования потребуется физический доступ к устройству.
Кроме того, цикл тестирования будет достаточно длительным из-за необходимости постоянно загружать код на устройство.
- Запустить на эмуляторе.
Этот метод хорош главным образом тем, что позволяет работать даже тогда, когда целевая платформа недоступна (например, потому, что она еще не сделана).
Недостатками являются ограниченная точность воспроизведения поведения оборудования (и окружающего мира), а также сложность создания такого эмулятора.
- Запустите на хост-компьютере (локально).
Вы не сможете работать с оборудованием (вместо этого можно использовать тестовые заглушки), но тесты будут запускаться и выполняться быстро, и вам не понадобится доступ к целевому устройству.
Хорошим примером использования этого метода является тестирование реализации на микроконтроллере некоторого вычислительного алгоритма, который сам по себе не зависит от аппаратной части, а использует данные с датчиков устройства.
Тестировать алгоритм на реальном источнике данных будет очень неудобно; Гораздо лучше записать эти измерения один раз и провести тесты на сохраненных данных.
Об этом сценарии с локальным запуском тестов и пойдет речь дальше.
Тесты будут запускаться на хост-компьютере Windows с использованием Cygwin. Google Test используется в качестве среды тестирования.
Результаты будут отображаться в специальном окне плагина модульного тестирования, а запустить их можно одной кнопкой из проекта для STM32:
Описанный способ подходит и для других сред разработки на базе Eclipse, если, конечно, хорошие производители не слишком их урезали ради удобства разработчиков.
Этот метод также будет работать с CubeIDE под Linux, без необходимости возиться с Cygwin.
Тебе понадобится
- Cygwin 3.0.7 x86 (поскольку тесты проводятся для 32-битного микроконтроллера, мы также будем использовать 32-битную среду на 64-битной платформе)
- STM32CubeIDE 1.0.2 для Windows.
- Платформа тестирования Google 1.8.1
Установка Cygwin и STM32CubeIDE
Сигвин
Установите Cygwin версии x86. В установщике выберите дополнительные пакеты: gcc-core, g++, binutils, automake, autoconf, cmake, libtool, gdb, make. Вы можете установить последние стабильные версии пакетов.
Вам также необходимо установить переменные среды: ПУТЬ: …;C:\ \Сигвин\бин; С:\ \Cygwin\lib путь к классам: С:\ \Cygwin\lib
STM32CubeIDE
Среда устанавливается как обычно.Целесообразно устанавливать CubeIDE после Cygwin, поскольку в этом случае Cube сам подхватит существующую цепочку инструментов Cygwin. Сначала давайте создадим проект C++ для платформы Cygwin x86. Он нам понадобится, чтобы, во-первых, проверить работоспособность тулчейна, а во-вторых, мы будем использовать его в качестве «донора» конфигурации сборки для основного проекта.
Выберите «Файл» > «Создать» > «Проект C/C++».
Выберите Управляемая сборка C++.
Давайте создадим проект hello world для набора инструментов Cygwin GCC:
Далее вам нужно будет выбрать, какие конфигурации сборки создавать.
Просто отладки достаточно.
Теперь вы можете убедиться, что проект собирается, выбрав «Проект» > «Построить все».
Также рекомендуется проверить отладку в Cygwin, запустив «Выполнить» > «Отладка как» > «Локальное приложение C/C++».
Приложение выведет «Hello world» на консоль внутри CubeIDE. Чтобы отладчик показывал исполняемые строки в файлах исходного кода, необходимо настроить отображение путей.
В окне «Окно» > «Настройки» на вкладке C/C++ > «Отладка» вам необходимо выбрать «Путь поиска источника» и добавить новое сопоставление: «Добавить» > «Сопоставление пути».
В окне нужно как-нибудь назвать новое отображение и добавить строки для дисков, которые есть в системе:
- \cygdrive\c - C:\
- \cygdrive\g - G:\
Чтобы красиво запускать тесты, нам также понадобится плагин для Eclipse с поддержкой модульных тестов для C++.
Он устанавливается непосредственно из STM32CubeIDE: меню «Справка» > «Установить новое программное обеспечение», затем выберите репозиторий Eclipse и установите плагин поддержки модульного тестирования C/C++.
Создание тестовой библиотеки Google
Исходный код библиотеки можно найти по следующей ссылке: https://github.com/google/googletest/tree/release-1.8.1 Распакуйте исходники, перейдите в каталог googletest-release-1.8.1 с помощью терминала Cygwin и запустите:После успешной сборки файл статической библиотеки будет расположен в каталоге .cmake .
make
/googlemock/lib/libgtest.a, а файлы заголовков — в каталоге .
/googletest/include/gtest/.
Их нужно будет скопировать в наш проект (или указать путь к этим файлам в настройках проекта).
Создание проекта для STM32
Проект макетной платы STM32L476G-DISCO. Пример не будет слишком сложным — на плате есть два светодиода, пусть они показывают двоичный счетчик от 00 до 11. Для счетчика реализуем отдельный модуль, описанный в паре файлов .h и .
c, и напишем тест на это.
Проект можно создать как обычно с помощью конфигуратора Cube, только убедитесь, что контакты PB2 и PE8 настроены как цифровые выходы.
При создании проекта лучше указать тип — C++, это понадобится для компиляции тестов (основной код все равно будет компилироваться компилятором C).
Вы можете преобразовать проект из C позже, щелкнув правой кнопкой мыши имя проекта и выбрав «Преобразовать в C++».
Для компиляции под МК и для тестов нам понадобятся две разные конфигурации сборки.
В этих конфигурациях будут собраны разные наборы файлов — основной будет содержать модули для работы с оборудованием и тестируемые модули, а тестовая конфигурация будет содержать те же тестируемые модули и тестовые файлы.
Поэтому в корне проекта создадим разные директории — Application с кодом приложения для МК (можно просто переименовать директорию Src, которую создал Cube), Common для аппаратно-независимых модулей (которые мы будем тестировать) и Tests для тесты.
Каталоги можно исключить из сборки, щелкнув правой кнопкой мыши по их имени, меню «Конфигурация ресурса» > «Исключить из сборки».
Добавим наш модуль счетчика в каталог Common: Кодled_counter (led_counter.h): #ifndef LED_COUNTER_H_
#define LED_COUNTER_H_
#include <stdint.h>
void Led_Counter_Init();
uint8_t Led_Counter_Get_Next();
#endif /* LED_COUNTER_H_ */
светодиод_counter.c: #include "led_counter.h"
static uint8_t led_cnt_state = 0;
void Led_Counter_Init()
{
led_cnt_state = 0;
}
uint8_t Led_Counter_Get_Next()
{
if(++led_cnt_state > 3)
led_cnt_state = 0;
return led_cnt_state;
}
Каталоги Common и Tests необходимо добавить в путь поиска включаемых файлов: свойства проекта (Свойства) > Общие C/C++ > Пути и символы > Включения.
Добавим работу со светодиодами в основной Фрагмент main.c основной.
с: …
/* USER CODE BEGIN Includes */
#include "led_counter.h"
/* USER CODE END Includes */
…
int main(void)
{
…
/* USER CODE BEGIN WHILE */
Led_Counter_Init();
uint8_t led_state = 0;
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
led_state = Led_Counter_Get_Next();
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, led_state & (1<<0));
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_8, led_state & (1<<1));
HAL_Delay(500);
}
/* USER CODE END 3 */
…
}
Проект должен скомпилироваться и запуститься, а светодиоды должны мигнуть.
Написание тестов
Вот ради этого все и началось.Давайте создадим новую конфигурацию сборки через свойства проекта — Свойства > Сборка C/C++ > Настройки > Управление конфигурациями.
CubeIDE просто не даст создать конфигурацию для сборки под Cygwin, поэтому скопируем ее из созданного нами ранее проекта:
Теперь вам нужно переключиться на эту конфигурацию и настроить пути к исходным файлам и файлам заголовков.
В свойствах проекта во вкладке «Пути и символы» пишем (при добавлении записи лучше поставить галочку напротив «добавить ко всем языкам»):
- Включает — Tests/Inc, Common/Inc
- Библиотеки — gtest
- Пути к библиотекам — Тесты/Библиотеки
- Местоположение источника - / /Общий и / /Тесты (заменить с названием проекта)
a в каталог Tests/Lib, а файлы заголовков из папки gtest — в папку Tests/Inc. В папке Tests создайте новый файл main.cpp, в котором будут запускаться тесты.
Его содержимое стандартное: основной.
cpp: /*
* Unit tests main file
*/
#include "gtest/gtest.h"
int main(int argc, char *argv[])
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
Также для проверки работы установки мы создадим один тест, который проверит, что в нашей среде размер указателя составляет 32 бита (мы хотим убедиться, что он такой же, как и на микроконтроллере, для этого мы установили 32-битный).
немного Сигвин).
Создайте следующий тестовый файл test_platform.cpp: #include "gtest/gtest.h"
TEST(PlatformTest, TestPointerSize)
{
//Check pointer size is 32 bit
ASSERT_EQ(sizeof(void*)*8, 32U);
}
Теперь, если проект запускается как обычное приложение C++, выходные данные отладки будут содержать сообщение от Google Test о том, что все тесты пройдены.
Структура проекта должна выглядеть примерно так:
Теперь напишем тесты для нашего модуля светодиодного счетчика.
Тестовые файлы могут находиться в папке Tests: test_led_counter.cpp #include "gtest/gtest.h"
extern "C" {
#include "led_counter.h"
}
// Test fixture
class LedCounterTest: public ::testing::Test
{
protected:
void SetUp()
{
Led_Counter_Init();
}
};
// Check initial value
TEST_F(LedCounterTest, TestInitialValue)
{
Led_Counter_Init();
ASSERT_EQ(Led_Counter_Get_Next(), 1);
}
// Check how value is incremented
TEST_F(LedCounterTest, TestIncrementValue)
{
Led_Counter_Init();
unsigned int val = Led_Counter_Get_Next();
for(int i=0;i<1;i++)
{
ASSERT_EQ(Led_Counter_Get_Next(), ++val);
}
}
// Check how value return to 0 after 3
TEST_F(LedCounterTest, TestZeroCrossing)
{
Led_Counter_Init();
for(int i=0;i<3;i++)
{
Led_Counter_Get_Next();
}
ASSERT_EQ(Led_Counter_Get_Next(), 0);
}
Чтобы результаты теста отображались в красивом окне, необходимо создать новую конфигурацию запуска в меню «Выполнить» > «Отладочные конфигурации».
Установленный плагин позволяет создавать конфигурации типа модуля C/C++.
Давайте создадим его, назовем его «Выполнить тесты», выберите конфигурацию сборки «Тест», которая будет использоваться, и снимите флажок «Остановить при запуске при» на вкладке «Отладчик».
После этого можно запускать настройку.
Чтобы появилось окно с результатами, вам необходимо выбрать его в меню «Окно» > «Показать вид» > «Другое» > «C/C++ > C/C++ Unit».
Готовый! Теперь проект можно скомпилировать и запустить под целевым МК как обычно.
Когда вам нужно запустить локальные тесты, при запуске конфигурации «Выполнить тесты» проект будет автоматически пересобран под x86, среда запустит тесты и покажет результат.
Литература
- Дж.
Греннинг.
Test-Driven Development for Embedded C. — это фундаментальная работа по модульному тестированию встроенных систем и использованию методологии TDD.
- https://uncannier.com/unit-testing-of-embedded-firmware-part-1-software-confucius/ — Модульное тестирование кода микроконтроллера на x86 в Code Composer Studio от Texas Instruments, фреймворк CppUTest.
- http://blog.atollic.com/why-running-your-embedded-arm-cortex-code-on-a-host-pc-is-a-good-thing — статья о том, почему может быть полезно запускать код микроконтроллера на настольной платформе
-
Разработчик Мобильных Приложений
19 Oct, 24 -
Как Не Учить Английские Слова
19 Oct, 24 -
Новые Комментарии
19 Oct, 24 -
It-Квест 4
19 Oct, 24 -
Google Выпускает Mod_Pagespeed Для Apache
19 Oct, 24 -
Какой Блог Выбрать?
19 Oct, 24 -
Phatch — Пакетная Обработка Изображений
19 Oct, 24 -
Диспетчер Задач На Javascript
19 Oct, 24