Статья
Версия для печати
Обсудить на форуме (19)
Развитие в направлении разработки встроенных систем.

Перевод с англ.: (C) Dale, 10.09.2010.
Оригинал статьи.

Предисловие переводчика

Тестирование программного обеспечения — очень важная часть процесса разработки, оказывающая определяющее влияние на качество результата в целом. Тем не менее его роль долгое время недооценивалась. Книги, изданные в России на данную тему, можно пересчитать по пальцам. Известные мне учебные курсы по подготовке программистов игнорировали тестирование (не удивлюсь, если и неизвестные мне тоже). Многие знакомые мне программисты-практики с немалым стажем также считают тестирование пустой тратой времени, ограничиваясь случайными неформальными проверками.
Ситуация несколько улучшилась с появлением «гибких» (agile) технологий программирования, которые уж совсем никак не могут существовать без тестирования. Появились инструменты для тестирования (некоторые даже доступны бесплатно) и, главное, книги по методикам использования этих инструментов. Начала входить в обиход «разработка через тестирование» (Test-Driven Design, TDD), когда сначала пишутся тесты, и лишь потом — код, который должен этим тестам соответствовать.
Работая в области разработки корпоративных приложений в среде .NET, я настолько привык к TDD, что уже не представляю другого подхода, когда пишется код, не обеспеченный тестами должного количества и качества. Когда же появилась реальная возможность поработать в области firmware, я сразу же почувствовал существенный дискомфорт. Мало того, что пришлось отвыкать от ставшего привычным комфорта ООП и вспоминать полузабытый архаичный C (впрочем, по сравнению с ассемблером даже он кажется великим благом). Ни в одном из многочисленных учебников по программированию микроконтроллеров ни слова не упоминалось о профессиональном тестировании firmware, а у меня успел сложиться устойчивый стереотип: программу, код которой не имеет достаточного покрытия тестами, нельзя считать написанной.
Поиск по русскоязычным форумам и программистов, и специалистов по электронике результатов не принес: так уж традиционно сложилось, что в подавляющем большинстве случаев разработчики оборудования программируют на интуитивном уровне, а программисты плохо представляют, с какого конца брать паяльник. Firmware требует навыков в обеих областях, что сочетается нечасто.
К счастью, поиск в Сети в конце концов начал приносить плоды. Оказалось, что данный вопрос волнует не одного меня, и в данном направлении уже проведена существенная работа и получены обнадеживающие результаты. Перевод одной из статей я выношу на суд читателей.
Примечание. Все ссылки, приведенные авторами в конце статьи, были проверены мною 13.09.2010. Рабочие ссылки оформлены как метассылки, нерабочие - в виде обычного текста.

Развитие в направлении разработки встроенных систем

  Мэтт Флетчер, Уильям Береза, Майк Карлески и Грег Уильямс
  Atomic Object, LLC
  {fletcher, beresa, karlesky, williams}@atomicobjects.com

Резюме

В конце 2005 года мы получили возможность начать наш первый проект по разработке встроенной системы. Мы применяем гибкие технологии в разных областях — от веб-разработки до промышленного тестового оборудования. Основной проблемой в этой новой работе было не освоение новой среды и технологии; проблема крылась в применении гибких технологий к маленькому, но сложному миру встроенных систем. Мы встретились с множеством препятствий: боролись с древностью многочисленных инструментов для встроенных систем, отсутствием интеграции с инструментами, подобными Rake, для обеспечения легкой автоматизации, а также слабой поддержкой объектно-ориентированной разработки. Мы преодолели все эти трудности. Данный отчет подводит итоги годовых усилий по применению наших технологий к разработке встроенных систем.

1. Введение

Atomic Object разрабатывал ПО в течение шести лет с использованием экстремального программирования. Мы пишем небольшие тесты, затем немного кода, снова тесты и опять немного кода. Разработка через тестирование (TDD) в нашем офисе — не факультативная технология. Мы "тест-инфицированы" и гордимся этим.
Два года назад фирма Savant Automation поручила нам переписать программное обеспечение для двух из полудюжины плат, управляющих производимыми этой фирмой автоматическими тележками. Эти тележки передвигаются по складам и хранилищам, следуя по запрограммированному маршруту. Несколько из нас в Atomic Object надеялись изучить программирование встроенных систем. Мы с энтузиазмом взялись за этот проект.
TDD не является привычным в мире встроенных систем. Немногие, в их числе James Grenning [1], Micah Dowty [2] и Nancy Van Schooenderwoert [3], ввели TDD и другие гибкие технологии в практику разработки встроенных систем, но множество инженеров-разработчиков firmware попросту не проявляют интерес к гибким технологиям или же не верят, что они применимы к разработке встроенных систем. Поэтому мы хотели не только поработать с интересными встроенными системами, но и, основываясь на опыте предыдущей работы, внедрить технологии тестирования в новую для нас область и продвинуть гибкую разработку встроенных систем.
Этот отчет подводит итог нашей годовой работы с платами фирмы Savant. В разделе 2 описывается наш опыт создания firmware для первой системы — платы «управления скоростью». Раздел 3 описывает вторую систему, плату «монитора батареи», а также изменения и улучшения, сделанные нами на основе опыта работы с платой скорости. И в заключение мы обсуждаем наиболее важный из усвоенных нами уроков: нет никаких реальных препятствий к применению мощных гибких технологий в области разработки встроенных систем разработчиками, которые в этом действительно заинтересованы.

2. Управление скоростью.

Сначала фирма Savant поручила нам работу над платой «управление скоростью». Роль управления скоростью в системе — принять скорость и направление движения с бортового компьютера тележки и преобразовать в реальное движение тележки. Для этого управление скоростью должно преобразовать скорость, заданную в миллиметрах в секунду, в соответствующее напряжение на двигателях тележки. Кроме того, действительная скорость должна приближаться к желаемой постепенно, в противном случае тележка не будет разгоняться и тормозить плавно. Управление скоростью также является частью контура безопасности тележки, так что если плата обнаруживает проблему, связанную со скоростью (например, не удается достичь требуемой скорости из-за препятствия), то она должна задействовать механизм безопасности и остановить тележку. Наконец, управление скоростью отвечает за оповещение остальных компонентов системы о своем состоянии посредством цифровых выходов и сообщений шины CAN [4]. Мы начали работу над платой управления скоростью осенью 2005 года.

2.1. Начальная разработка.

Управление скоростью осуществляется микропроцессором Microchip PIC18F4480, который имеет 768 байт RAM и 16 кбайт флеш-памяти [5]. В начале проекта мы решили использовать для разработки firmware компилятор и симулятор от Microchip. Компилятор от Microchip ограничивал нас использованием языков C и ассемблера для разработки. Ruby был введен в проект в виде скрипта построения Rake [6]. Мы решили использовать Rake вместо make или IDE от Microchip, поскольку считаем, что Rake эффективнее, удобнее и полезнее, чем make. В предыдущих проектах мы добились больших успехов, используя Ruby для автоматизации; Rake позволял легко интегрировать программы генерации кода на Ruby в наш процесс построения.

2.2. Модульное тестирование.

Мы начали разработку firmware с предварительной разработки модульных тестов, как мы делали со всеми другими проектами. Наша среда разработки была настроена таким образом, чтобы тесты для каждой логической группы (функции АЦП, ввода и вывода CAN и т. п.) компилировались в свой собственный исполнимый тестовый модуль. Во время тестирования среда построения перекомпилировала необходимые модули и затем использовала симулятор Microchip PIC для выполнения каждого тестового файла. Каждый тест выводил статус успешного завершения или ошибки после выполнения; вывод тестов собирался симулятором и анализировался, выдавая нам общий статус теста: красная или зеленая полоска.
Для большей поддержки модульного тестирования мы интегрировали скрипт на Ruby под названием Argent [7] в наш процесс построения. Argent весьма похож на препроцессор C в том, что он читает входной файл и ищет в нем специфические директивы. Обычно директивы требуют, чтобы Argent запустил другой скрипт Ruby, но это может быть и встроенный код Ruby. Вывод кода Ruby помещается обратно в исходный файл. Мы настроили нашу систему построения на запуск Argent для каждого тестового файла по мере компиляции. Argent сканирует код в файле, ищет в нем сигнатуры тестовых функций и генерирует код в функции main для выполнения каждого теста.
Некоторое время мы использовали среду модульного тестирования на C embunit [8], но затем отказались от нее по причине неэффективности. В частности, из-за ограниченного объема RAM и ROM нашего микропроцессора мы могли скомпилировать лишь очень немного модульных тестов в один исполнимый модуль. Хотя мы могли бы обойти это ограничение путем разбиения тестовых наборов на большее количество исполнимых модулей, мы предпочли вместо этого разработать фреймворк для модульного тестирования, ориентированный на наши нужды. Результат — компактная среда модульного тестирования, которую мы назвали Unity.
То, что мы были ограничены языком C во встроенной среде, не помешало нам разрабатывать firmware таким же образом, как мы разрабатывали бы любое другое приложение. Весь код разрабатывался по принципу «сначала тест»; наши тесты помогли нам точно определить, за что отвечает каждый модуль и от каких других модулей он зависит. Каждый раз, когда тестировать модуль становилось слишком сложно (например, установка условий для теста требовала слишком больших усилий), мы переносили трудную работу в другой модуль. В результате код на C выглядел и воспринимался как код, написанный на Java или Ruby, который мы разрабатывали в других проектах.
Наши модульные тесты интенсивно использовали mock-объекты. Мы убедились в чрезвычайной полезности mock-объектов в других наших проектах — они позволяют легко разделять и тестировать роли различных компонентов системы [9]. В объектно-ориентированном языке мы обычно вставляем mock в тестируемые объекты посредством constructor injection. Поскольку в данном случае мы использовали C, мы не могли применить constructor injection, поэтому взамен мы просто манипулировали тем, какие объектные файлы компоновались с нашими тестовыми файлами. В окончательном коде каждый компонент был скомпонован с реальными объектными файлами, однако в тестовом коде тестируемый модуль компоновался с объектными mock-файлами.
Рассмотрим модуль, ответственный за получение результата последнего аналого-цифрового преобразования, накопление его в текущем среднем и запуск следующей аналоговой выборки. В подобной ситуации мы считаем, что выполнение выборки аналоговых данных и вычисление среднего — это слишком много ответственности для одного модуля: задать исчерпывающий набор тестов для всех этих функций было бы слишком трудно. Решение — вынести работу с АЦП в один модуль, а вычисление среднего — в другой.
На рис. 1 приведен листинг примера модульного теста, а на рис. 2 — соответствующий функциональный код. Теперь тесты и код как для управления АЦП, так и для вычисления среднего имеют малый размер, легки для понимания и благодаря сборщику данных не знают друг о друге.

Код: (C)
void testShouldCollectResultAverageItAndStartNextConversion()
{
  // mock expectations
  int result = 800;
  ADC_GetResult_ExpectAndReturn(result);
  Averager_AddResult_Expect(result);
  ADC_Start_Expect();
  // run the functional code
  ADCGatherer_CollectData();
  // mock verification is automatically done in tearDown()
}
Рис. 1. Пример модульного теста для сборщика данных с АЦП.

Код: (C)
void ADCGatherer_CollectData(void)
{
  int result = ADC_GetResult();
  Averager_AddResult(result);
  ADC_Start();
}
Рис. 2. Функциональный код сборщика данных с АЦП.

2.3. Паттерн «модель-проводник-оборудование».

В ходе создания firmware для платы управления скоростью оказалось, что мы следовали общему шаблону разработки при реализации каждой функции firmware. Для каждой функции (например, выборка АЦП, ввод данных с шины CAN, цифровой выход и т. п.) мы разбивали код на три компонента верхнего уровня: модель, реализующую логику функции, оборудование, производящее необходимые действия, и проводник посередине. Роль проводника заключается в передаче событий и данных между моделью и оборудованием. Мы называем этот паттерн разработки Модель-Проводник-Оборудование (Model-Conductor-Hardware, MCH) [10]. Это не означает, что каждая функция состоит лишь из трех компонентов — модель и оборудование обычно полагаются на вспомогательные модули.
Применение MCH позволило нам улучшить процесс разработки, поскольку тестировать стало проще. Системная логика и аппаратное управление четко разделены и управляются третьей частью. Это позволило нам легко создать mock для каждого компонента; имея же mock для каждого компонента, установка условий для каждого тестируемого модуля была тривиальной. Обсужденный ранее тест на рис. 1 демонстрирует, как mock-объекты (в данном случае mock для АЦП и вычисления среднего) делают наши тесты легкими для понимания.

2.4. Опыт разработки управления скоростью.

В разработке управления скоростью тоже не обошлось без препятствий. В течение девяти месяцев нам удалось внедрить базовые технологии. В основном.
Одной из наших оплошностей было то, что мы не сделали большинство необходимых mock-объектов. Часто создаваемые нами mock-объекты содержали недостаточно строгие утверждения; такие вещи, как принудительный подсчет и упорядочение вызовов, не были сделаны. Даже хуже того, иногда mock-объекты даже не использовались вовсе (обычно модели в тестах проводника). Тесты, которые не использовали mock-объекты, порой становилось трудно понимать и поддерживать. Поразмыслив над этим, мы пришли к выводу, что проблема кроется в ручной, а не автоматической генерации mock-объектов. Создание mock-объектов вручную утомительно и чревато ошибками. Поскольку ручной процесс столь обременителен, это вводит в искушение избегать использовать их потенциал на полную мощь. Именно это и произошло в процессе разработки платы управления скоростью.
Наша зависимость от компилятора и симулятора от Microchip стала для нас постоянным источником разочарований. Компилятор-то работал, — в том смысле, что он генерировал код, — но при этом он постоянно делал самовольные предположения, причем совершенно молча. Результатом явились несколько долгих и весьма неприятных сессий отладки. Симулятор оказался еще хуже. Во-первых, симулятор оказался неспособен к симуляции периферии микроконтроллера или прерываний от таймера. Это значило, что наши тесты не могли быть столь тщательными, как нам хотелось бы. Во-вторых, симулятор можно было вызывать только посредством GUI. Поэтому для запуска наших автоматических модульных тестов скрипт AutoHotkey [11] принимал на себя управление нашей системой, запускал тесты и собирал результаты. Поскольку AutoHotkey управлял GUI, любое внешнее вмешательство (например, пользователь, переключивший фокус на другое окно) прерывало скрипт, и он выпадал по ошибке. К концу проекта запуск полного набора тестов занимал десятки минут, что явно долговато для ожидания результата.
Что мы действительно упустили с платой управления скоростью — это автоматические системные тесты. Подобно модульным тестам, системные тесты позволяют определить, как некоторая функция должна работать от начала до конца. Например, простой, но эффективный тест управления скоростью показал бы, что, когда выдается команда скорости, плата подает правильное выходное напряжение на двигатели. Системный тест подталкивает нас разрабатывать минимальное количество кода, необходимое для выполнения определенной функции.
Системный тест служит также автоматическим регрессионным тестом. Наша разрабатываемая плата скорости была подключена к замечательной тестовой установке: она включала в себя цифровые переключатели, аналоговые джойстики и светодиоды, позволяющие пользователю легко контролировать состояние платы. Тестовая установка отлично подходила для демонстрации платы, но не годилась для подтверждения того, что каждый аспект системы по-прежнему работает. Если бы мы располагали и модульными, и системными тестами, входящими в систему непрерывной интеграции, мы были бы оповещены о нарушенных функциях максимально оперативно. Отсутствие у нас автоматических системных тестов наиболее огорчало нас, когда мы завершали работу над платой управления скоростью.
Был и положительный момент: работа над платой управления скоростью доказала нам, что мы можем взять технологии, которые изучили для разработки приложений, и применить их для разработки встроенных систем. Мы показали, что можем:
  • Вести разработку методом «сначала модульное тестирование».
  • Автоматизировать построение ПО и запуск тестов.
  • Использовать mock-объекты для поддержки модульного тестирования.
  • Писать код на C способом, допускающим тщательное тестирование.
Мы считаем, что решили сложные и интересные задачи. Мы изучили такие вещи, как ПИД-контроллеры, АЦП, обмен данными по шине CAN, — те вещи, которые часто уже сделаны за вас при разработке приложений. Проект управления скоростью был действительно увлекателен.

3. Монитор батареи.

Разработка firmware для «платы монитора батареи» началась в июле 2006 года. Ее назначение — полный отчет о состоянии заряда батареи тележки. Используя комбинацию из двух аналоговых входов от батареи и внутреннюю таблицу поиска, плата батареи сообщала текущий уровень заряда через цифровые выходные линии и сообщения шины CAN. Потребителем этой информации был главный компьютер тележки, который принимал решение, следует ли остановить тележку для зарядки, либо она уже зарядилась и должна вернуться к работе. На плате стоял микроконтроллер Microchip PIC18F2480. Модель 2480 имеет те же спецификации, что и 4480 на плате управления скоростью, но с другим форм-фактором. Мы планировали использовать некоторые новые идеи в плате батареи, поэтому принялись за дело с энтузиазмом.

3.1. Системное тестирование.

Наиболее важной целью для нас, помимо разработки качественного программного обеспечения, было внедрение в наш рабочий процесс разработки через автоматизированное системное тестирование. Как обсуждалось в предыдущем разделе, системное тестирование предоставляет те же возможности, что и модульное тестирование, но на более высоком уровне, ориентированном на пользователя. Чтобы сделать системное тестирование возможным, было необходимо программное и аппаратное обеспечение для выполнения и поддержки тестов. Для создания подходящего стенда для тестирования нам потребовалось несколько устройств. Мы приобрели:
  • программатор для PIC;
  • miniLAB 1008, устройство цифрового и аналогового ввода/вывода через USB [12];
  • PCAN-USB, устройство для передачи данных по шине CAN;
  • преобразователь USB в последовательный интерфейс;
  • небольшой шкаф и USB концентратор, чтобы связать это все воедино.
Все эти устройства были аккуратно установлены в шкаф. Образцовая плата батареи была смонтирована сверху шкафа, около дюжины проводов протянуто от платы внутрь. В сравнении со стендом для тестирования платы скорости с ее светодиодами и переключателями, стенд для платы батареи выглядел скучновато. Но он оказался гораздо более эффективным в работе.
Программное обеспечение для проведения тестирования подобрать удалось легко: мы решили использовать Systir [14], доморощенную среду тестирования на основе Ruby. Systir базируется на выразительном синтаксисе Ruby, чтобы помочь пользователю создавать и писать тесты на языке предметной области. Язык, созданный нами для тестов платы батареи, фокусировался на симуляции особых состояний батареи и считывании выходной информации платы как с обычных цифровых линий, так и современных сообщений CAN. MiniLAB и PCAN-USB были выбраны для тестового стенда, поскольку ими можно управлять программно посредством библиотек на C. Мы сделали их функциональность доступной системным тестам посредством встроенных в Ruby расширений C.
Нам потребовалось немного аппаратного и программного обеспечения для подготовки нашего тестового стенда, но как только он был готов, мы могли создавать тесты наподобие показанного на рис. 3. Этот тест показывает, что плата батареи правильно выдает мгновенный уровень заряда (в отличие от среднего уровня заряда) при считывании известного напряжения батареи.

Код: (C)
set_charge_level_output_to_instantaneous
set_battery_voltage_to 4.5
see_charge_level_of 7
set_battery_voltage_to 1.1
see_charge_level_of 2
Рис. 3. Системный тест уровня заряда батареи.

С правильной комбинацией аппаратного и программного обеспечения наши системные тесты были легкими в написании, четко инкапсулировали отдельные функции, запрошенные пользователем, обеспечивали возможность регрессионного тестирования и позволяли нам применять подход к разработке через тестирование.

3.2. Лучший toolchain

Вторым изменением, которое мы сделали в начале разработки платы батареи, был выбор инструментария. В этот раз мы воспользовались версией Embedded Workbench для PIC от IAR Systems [15] взамен инструментов от Microchip. Набор включает быстрый современный компилятор, настраиваемый симулятор, корорый можно запускать из командной строки, и богатый набор библиотек. Кроме того, ранее один из авторов имел положительный опыт работы с инструментарием от IAR для платформы ARM.
С каждым днем мы начинали ценить новые инструменты все больше и больше. Компилятор обеспечивал простой и легкий доступ к внутренним аппаратным средствам и в случае ошибок давал нам осмысленные сообщения о том, где именно кроется проблема. Компоновщик был интеллектуальным и хорошо экономил драгоценное пространство кода. Заголовочные файлы для доступа к аппаратным регистрам содержали чрезвычайно удобные макросы, помогающие делать код аппаратного уровня легко читаемым. Но нашим фаворитом стал симулятор командной строки. Нам больше не нужно было ждать минуты для запуска нашего набора тестов! Новый симулятор сэкономил нам массу времени и нервов.
Недостатком смены toolchain'ов для нас стала потеря доступа к библиотекам от Microchip для доступа к встроенной периферии, такой как модули АЦП или CAN. Библиотеки определяли, например, простые функции для конфигурирования и чтения АЦП. Несмотря на это, располагая документацией от Microchip [5] и собственным опытом, полученным от работы над предыдущей платой, мы без проблем восстановили нужную нам функциональность. Еще одним недостатком была цена новых инструментов. Однако в конце мы поняли, что они стоят каждого затраченного пенса.

3.3. Автоматически генерируемые mock'и

Несколько следующих месяцев мы потратили на разработку предварительных системных и модульных тестов для платы батареи. Системные тесты внушили команде гораздо больше уверенности, а новые инструменты помогли поставить цикл разработки на поток. Мы также начали разработку «сначала проводник» в подражание технологии «сначала представление» [16][17], используемой в нашем офисе в других проектах. Технология «сначала проводник» заставила нас сначала писать тесты для модулей проводника, поскольку именно эти тесты определяли, что нам нужно, и только то, что нам нужно от модулей оборудования и модели. Ввиду ограниченного набора функций платы батареи у нас было не так уж много возможностей для оттачивания этой технологии. После того, как мы создали несколько групп модель-проводник-оборудование, все нужды дизайна верхнего уровня были удовлетворены; большая часть работы над вспомогательными модулями модели и оборудования была выполнена.
Необходимость во множестве вспомогательных модулей модели и оборудования выявила одну слабость в нашей системе: все наши mock'и по-прежнему создавались вручную. Как упоминалось в разделе 2.4, создаваемые вручную mock'и повлекли практику плохого тестирования для платы управления скоростью. Мы подошли к вопросу надлежащего использования mock'ов в плате батареи более тщательно, но необходимость создания их вручную по-прежнему досаждала.
Чтобы исправить ситуацию, один из авторов потратил выходные на создание скрипта на Ruby для автоматической генерации mock'ов для каждого модуля системы. Скрипт генерации mock'ов сканировал заголовочный файл и использовал регулярные выражения для поиска различных типов сигнатур функций. В зависимости от типа найденной функции (например, функция, не имеющая параметров и ничего не возвращающая; не имеющая параметров, но возвращающая некоторое значение; и т. д.) скрипт генерировал mock-версию функции, которая могла быть запрограммирована наподобие того, как это делается в jMock [18]. Все функции, обнаруженные в одном заголовочном файле, преобразуются в mock'и, которые при необходимости могут включаться в тесты.
Мы встроили скрипт генерации mock'ов в систему построения таким образом, чтобы mock'и регенерировались каждый раз при построении системы. Поскольку каждый тест компилировался в свой собственный загрузочный модуль, скрипт построения компоновал тест с тестируемым модулем и всеми mock'ами. Таким образом, при каждом выполнении теста все обращения тестируемого кода к другим модулям отслеживались mock'ами. Если производился неожиданный или некорректный вызов другого модуля, mock сообщал об ошибке. Такое поведение полностью окупилось, когда мы перешли от ручной генерации mock'ов к новой системе: наши mock'и обнаружили, что некоторый код делал вызовы, которые не следовало делать.
mock'и также помогли нам соблюсти принцип единой ответственности. Каждый раз, когда тест для фрагмента теста становился слишком сложным, мы обычно могли найти хороший способ вынести часть сложности в другой модуль. Без автоматических mock'ов всегда возникали затруднения, связанные с добавлением другого модуля. Но наши скрипты делали всю тяжелую работу за нас, производя построение mock'ов.

3.4. Опыт разработки монитора батареи.

Продолжительность проекта монитора батареи была короткой по сравнению с управлением скоростью; над монитором батареи мы работали четыре месяца, тогда как над управлением скоростью — девять. Несмотря на сокращенный срок, мы извлекли больше уроков качественной разработки встроенных систем на проекте платы батареи, чем на управлении скоростью. Мы успешно внедрили лучший набор инструментов, методику разработки, управляемой системными тестами, и автоматическую генерацию mock'ов.
Однако скрипты построения все еще нуждались в усовершенствовании. Скрипты были не особенно интеллектуальны; благодаря нашим настройкам каждый раз, когда менялся заголовочный файл, система перестраивалась почти полностью. Это было обусловлено не какими-то безумными зависимостями в коде, а потому, что наши скрипты построения включали каждый mock в каждый тест. Большинству тестов нужно было не более одного-двух mock'ов, поэтому включение всех mock'ов было неэффективным. Наш проект был небольшим, поэтому данная проблема не была чересчур болезненной, но на будущее наши скрипты нуждались в переработке на предмет включения только необходимых mock'ов в каждый из тестов.

4. Дальнейшее направление работы и заключение.

Два проекта от Savant были благодатными для нас в том смысле, что системы имели четко выраженные входы и выходы: информация о состоянии батареи и компьютера вводится; директивы к основным компонентам тележки выводятся. Это делало роль наших системных тестов очевидной. В следующий раз хотелось бы приобрести опыт работы с другой платой, с не столь легко тестируемыми функциями. Мы убеждены, что наша стратегия модульного тестирования может оставаться примерно той же, но применение системных тестов к устройству без легко тестируемого интерфейса будет интересной задачей.
Сейчас мы вовлечены в новый встроенный проект для другого заказчика; в этот раз firmware управляет устройством для измерения цвета. В настоящий момент в минимальном объеме реализованы подпрограммы создания и обработки исключительных ситуаций на C, поддержка модульного тестирования для них, а также расширения на Ruby для взаимодействия с устройством через USB. Все они облегчают команде разработку и тестирование firmware как на модульном, так и на системном уровнях.
Некоторое время назад мы занялись применением гибких технологий в области встроенных систем. Два года спустя мы успешно разработали наш набор стандартных методик, дающих прекрасные результаты. Наша комбинация подходов «сначала системные тесты» и «сначала модульные тесты» действительно помогла нам добиться легендарного «чистого кода, который работает». Мы поняли, что в области встроенных систем нет ничего особенного, что могло бы помешать нам использовать те же самые технологии, которые мы использовали при разработке настольных или веб-приложений. Смотря в будущее, мы рассматриваем системные тесты, модульные тесты и mock'и как основную часть нашего процесса разработки встроенных систем. Все это помогло нам стать продуктивными, довольными жизнью программистами, получающими удовольствие от процесса разработки встроенных систем.

5. Благодарности.

Мы благодарим Мэтта Вернера из Savant Automation за предоставленную возможность заняться разработкой встроенных систем, а также Чеда Фаулера и Карла Эриксона, убедивших нас поделиться своим опытом с сообществом.

Ссылки

[1] J. Grenning, “Extreme programming and embedded software development,” Object Mentor,” Article, Mar. 2004. [Online]. Available: http://www.objectmentor.com/resources/articles/EmbeddedXp.pdf
[2] M. Dowty, “Test driven development of embedded systems using existing software test infrastructure,” University of Colorado at Boulder, Tech. Rep., Mar. 2004. [Online]. Available: http://svn.navi.cx/misc/trunk/docs/papers/embedded-test-driven-development.pdf
[3] N. Van Schooenderwoert, “Embedded agile: A case study in numbers,” Dr. Dobb’s, Nov. 2006. [Online]. Available: http://www.ddj.com/dept/architect/193501924
[4] CAN Specification Version 2.0, Robert Bosch GmbH, Sept. 1991.
[5] PIC18F2480/2580/4480/4580 Data Sheet, Microchip, Apr. 2007.
[6] J. Weirich. Rake – Ruby make. [Online]. Available: http://rake.rubyforge.org
[7] C. J. O’Neill. argent-codegen — a Ruby code generation tool. [Online]. Available: http://rubyforge.org/projects/argent/
[8] Embedded unit. [Online]. Available: https://sourceforge.net/projects/embunit/
[9] S. Freeman, N. Pryce, T. Mackinnon, and J. Walnes, “Mock roles, not objects,” in Proc. OOPSLA 2004, 2004.
[10] M. Karlesky, W. Bereza, and C. Erickson, “Effective test driven development for embedded software,” in Proc. IEEE Electro/Information Technology Conference, May 2006.
[11] Autohotkey. [Online]. Available: http://www.autohotkey.com/
[12] miniLAB 1008. Measurement Computing. [Online]. Available: http://www.measurementcomputing.com/
[13] PCAN-USB. PEAK System. [Online]. Available: http://www.peak-system.com/db/gb/pcanusb_gb.html
[14] D. Crosby, K. Fox, and M. Alles. System testing in Ruby. Atomic Object. [Online]. Available: http://atomicobject.com/pages/System+Testing+in+Ruby
[15] C/C++ compilers and debuggers for PIC18. IAR Systems. [Online]. Available: http://www.iar.com/p6058/p6058_eng.php
[16] M. Alles, D. Crosby, C. Erickson, B. Harleton, M. Marsiglia, G. Pattison, and C. Steinstra, “Presenter First: Organizing complex GUI applications for testdriven development,” in Proc. Agile ’06, July 2006, pp. 276–288.
[17] D. Crosby and C. Erickson, “Big, complex, and tested? Just say ’when’: Software development using Presenter First,” Better Software Magazine, Feb. 2007. [Online]. Available: atomicobject.com/files/BigComplexTested Feb07.pdf
[18] S. Freeman, T. Mackinnon, N. Pryce, and J. Walnes. jMock — a lightweight mock object library for Java. [Online]. Available: http://www.jmock.org/
Версия для печати
Обсудить на форуме (19)