Построение конечных автоматов с Test-Driven Development во встроенном C
Конечные автоматы являются фундаментальными для многих встроенных систем, однако их разработка может быть сложной. Эта статья описывает проверенные стратегии построения программного обеспечения конечных автоматов (КА) с использованием Test-Driven Development (TDD), с фокусом на реализации на C, подходящих для сред с ограниченными ресурсами.
В ядре модель КА состоит из состояний, переходов и действий. Состояния представляют условия, переходы определяют путь из одного состояния в другое — обычно инициируемые событием — а действия захватывают поведение, выполняемое при входе, выходе или во время перехода.
В UML конечных автоматах действия могут быть прикреплены к входу/выходу состояния, самому переходу или внутренним реакциям. Все формализмы КА предполагают модель выполнения Run-To-Completion (RTC): каждое событие полностью обрабатывается — включая действия выхода, действия перехода и действия входа — прежде чем будет обработано следующее событие.
Прежде чем погрузиться в стратегии TDD, важно понять, почему TDD имеет значение. TDD — это инкрементальный подход к разработке, который предписывает написание проваливающегося юнит-теста до любого продакшн-кода. Тесты маленькие, автоматизированные и служат спецификацией желаемого поведения. Цикл TDD — основанный на «Test-Driven Development for Embedded C» Джеймса Греннинга — включает:
- Написать маленький проваливающийся тест.
- Запустить все тесты; новый тест должен провалиться.
- Написать минимальный код для прохождения теста.
- Снова запустить все тесты, чтобы подтвердить прохождение нового теста.
- Рефакторить для устранения дублирования и улучшения ясности.
Рассмотрим диаграмму КА на Рисунке 1. Она начинается в StateA и, при получении события Alpha, переходит в StateB, выполняя xStateA(), effect() и nStateB() в последовательности. Как мы валидируем, что это поведение корректно?
Рисунок 1. Базовый конечный автомат (Источник: VortexMakes)
Самый прямой подход тестирования включает верификацию таблицы переходов КА. Это создает один тест-кейс на состояние, стимулируя КА соответствующими событиями и утверждая результирующее состояние и выполненные действия. Сложные действия заслуживают выделенных тестов. (См. статью «Testing state machines using unit test» для глубокого обсуждения.)
Каждый тест следует четырехфазному паттерну xUnit:
- Setup — установить предусловия (например, текущее состояние, событие, ожидаемое целевое состояние и последовательность действий).
- Exercise — запустить КА с событием.
- Verify — утверждать, что результаты соответствуют ожиданиям.
- Cleanup — сбросить КА в начальное состояние (опционально).
Хотя эта стратегия хорошо работает для простых КА, более сложное поведение — такое как цепочка переходов, где эффект наблюдаем только после нескольких шагов — требует функционального сценарийного тестирования. Вместо изолированных проверок состояний мы тестируем полную последовательность, которая исполняет необходимые состояния, события и действия.
Рисунок 2 иллюстрирует это с КА DoWhile, имитирующим традиционный do-while цикл. Машина инициализируется значениями по умолчанию через x = 0 и output = 0, устанавливает счетчик цикла с x++ или x = (x > 0) ? x-: x, и итерирует с i++ до тех пор, пока гард i == x не провалится, после чего выводит output = i.
Рисунок 2. Конечный автомат DoWhile (Источник: VortexMakes)
До кодирования мы создаем список тестов, производный из спецификации. Для DoWhile начальный список включает:
- Все данные установлены в значения по умолчанию при инициализации.
- Инкремент атрибута X.
- Декремент атрибута X.
- Исполнение однократного цикла.
- Исполнение множественных итераций.
- Обработка сценария без итераций.
- Проверка условий выхода за границы.
Мы используем Ceedling как систему сборки и Unity как легкий тестовый harness для C-кода. КА определен в DoWhile.h и DoWhile.c, а тестовый набор находится в test_DoWhile.c. Ниже приведен краткий фрагмент, иллюстрирующий тест-кейс «одна итерация».
Code Listing 1: Тест одной итерации (Источник: VortexMakes)
Тест инициализирует КА, устанавливает счетчик итераций в ноль, отправляет событие Up для инкремента, запускает событие Start и наконец отправляет Alpha для выполнения одной итерации цикла. Утверждения проверяют, что атрибут out равен количеству итераций и что КА остается в StateC.
Расширение того же теста демонстрирует множественные итерации (см. Code Listing 2). Отдельный тест (Listing 3) подтверждает, что КА не выполняет итераций, когда Alpha получен без предварительного события Up.
Code Listing 2: Тест множественных итераций (Источник: VortexMakes)
Code Listing 3: Тест без итераций (Источник: VortexMakes)
Детали реализации не в фокусе этой статьи, но фрагменты из DoWhile.c и DoWhile.h иллюстрируют чистый switch-case подход к обработке состояний.
Code Listing 4: Фрагмент реализации DoWhile (Источник: VortexMakes)
Code Listing 5: Фрагмент спецификации DoWhile (Источник: VortexMakes)
Подводя итог, TDD предоставляет две взаимодополняющие стратегии для разработки конечных автоматов: (1) тестирование переходов по состояниям, и (2) функциональное сценарийное тестирование, покрывающее множественные состояния и действия. Эти методы не зависят от языка, инструментов и особенно хорошо подходят для встроенных систем, где доминирует логика на основе состояний.
Поскольку C остается лингва франка встроенного ПО, сочетание его с Ceedling и Unity дает надежный TDD workflow. Принятие этих практик ведет к ПО, которое более гибкое, поддерживаемое и повторно используемое, чем конвенциональные подходы.
Весь код и модели доступны в GitHub репозитории https://github.com/leanfrancucci/sm-tdd.git.
Встроенный
- Частые проблемы станков с ЧПУ и экспертные решения
- Освоение вертикально-фрезерных станков: преимущества, операции и почему они обходят горизонтальные
- Повышенная надежность CNC: современные фрезерные инструменты дают скорость, точность и низкие затраты
- Boy Machines: продвижение мелкосерийного производства передовой технологией
- Как избежать типичных ошибок при покупке б/у станков с ЧПУ
- ИИ в тестовой автоматизации: революция в обеспечении качества для современных ERP-систем
- Проект Accelerate: стратегическое аутсорсинг для стартапов
- Тренды производства 2021: Часть 2 — инсайты от Make UK
- Достигайте идеальных финишей с точными шлифовальными станками Okamoto
- Что может изготовить ЧПУ-станок: безграничные возможности