1. Тема дня
Как отделить прикладную логику от ESP-IDF, STM32 HAL и реального железа, чтобы запускать сотни тестов на Linux/Windows за секунды. Правильная цепочка:
чистая логика на ПК
→ unit-тесты компонентов на ESP32/STM32
→ интеграционные тесты
→ HIL на Raspberry Pi
→ тесты всей системыГлавная мысль: чем меньше модуль знает о GPIO, UART, FreeRTOS и HAL, тем проще доказать, что его логика работает правильно.
2. Зачем это нужно
Без платы можно тестировать:
EC25:
AT-парсер, fragmented UART input, timeout, state machine.
Светофор:
debounce, AC pulse window, RED/YELLOW/GREEN, conflict.
RS-485/CAN:
CRC, codec, Modbus parser, CAN ID map.
Fault manager:
классификация ошибок, retry limits, degraded mode.
Конфигурация:
defaults, validation, migration, A/B выбор.3. Теория
Host unit tests
Запускаются на ПК и должны быть быстрыми, детерминированными, без реального времени, без FreeRTOS-задач и без регистров MCU. Тестируем:
phase_detector_update()
at_parser_feed()
modbus_crc16()
config_validate()
fault_policy_decide()
mqtt_message_encode()Target unit tests
Запускаются на ESP32/STM32 и проверяют уже FreeRTOS, allocator, hardware timers, GPIO loopback, UART loopback, DMA, порядок callback/interrupt. HIL
Проверяет физику: оптопары, EC25, I2C, CAN/RS-485, питание, watchdog, OTA rollback.
Паттерн core + adapter
Плохо:
void phase_detector_update(void)
{
int red = gpio_get_level(RED_GPIO);
int64_t now = esp_timer_get_time();
xQueueSend(phase_queue, ...);
ESP_LOGI(TAG, "Phase changed");
}Лучше:
input_driver:
GPIO/expander/ADC → raw_mask
phase_detector_core:
raw_mask + timestamp → phase_event_t
phase_service:
phase_event_t → FreeRTOS queue/MQTT/CANПаттерн step()
Бесконечную задачу тестировать неудобно:
void modem_task(void *arg)
{
while (1) {
modem_service_step(&s_modem, platform_time_us());
vTaskDelay(pdMS_TO_TICKS(10));
}
}Теперь test вызывает modem_service_step() с виртуальным временем.
Время как аргумент
Плохо:
return esp_timer_get_time() - s_start_us > 5000000;Лучше:
bool timeout_expired(int64_t now_us, int64_t start_us, int64_t timeout_us)
{
return now_us - start_us >= timeout_us;
}4. Типичные ошибки
- Тестировать функцию вместе с HAL.
- Использовать реальные задержки sleep(5).
- Проверять внутренние индексы вместо поведения.
- Тестировать только happy path.
- Mock всегда возвращает успех.
- Писать отдельную копию алгоритма в тесте.
- Считать coverage доказательством качества.
5. Практическое задание
Создай host-compatible phase_detector_core. Интерфейс:
typedef enum {
PHASE_NONE = 0,
PHASE_RED,
PHASE_YELLOW,
PHASE_GREEN,
PHASE_CONFLICT,
} phase_t;
typedef struct {
bool initialized;
uint8_t stable_mask;
uint8_t candidate_mask;
uint64_t candidate_since_us;
uint32_t debounce_us;
uint32_t sequence;
} phase_detector_t;
typedef struct {
bool emitted;
phase_t phase;
uint8_t mask;
uint32_t sequence;
uint64_t timestamp_us;
} phase_event_t;Тесты:
- RED emitted only after debounce.
- same stable phase is not emitted twice.
- short GREEN glitch does not replace RED.
- stable GREEN replaces RED.
- RED+GREEN is CONFLICT.6. Что попробовать дальше
- Добавить тесты для AT-парсера EC25.
- Добавить config migration tests.
- Добавить fault manager tests.
- Подключить host tests в CI перед сборкой прошивки.
Задание
Проверьте debounce без sleep. Опишите последовательность raw masks и timestamps, доказывающую, что короткий GREEN glitch не заменяет устойчивый RED.
Критерии самопроверки: Использовать virtual time и публичное поведение; не опираться на internal indices и не дублировать алгоритм в тесте.
Показать ответ автора
Передать чистому detector явные timestamps и masks: установить устойчивый RED, внедрить GREEN короче настроенного debounce interval, затем вернуть RED. Проверить наблюдаемые phase events и отсутствие ложного GREEN event.
Задание
Отнесите AT parser test, UART DMA loopback test и modem power-loss test к host, target или HIL. Объясните, что каждый способен доказать.
Критерии самопроверки: Указать границу и ограничение каждого теста; host mock не доказывает физический signal timing и реальное modem recovery.
Показать ответ автора
AT parsing с переданными byte fragments — host test чистой логики parser. UART DMA loopback — target test peripheral/driver behavior. Modem power loss — HIL test оборудования и recovery integration.