Подтвердить что ты не робот

Драйвер устройств устройства тестирования

У меня есть ситуация, когда мне нужно написать некоторые модульные тесты для некоторых драйверов устройств для встроенного оборудования. Код довольно старый и большой и, к сожалению, не имеет большого количества тестов. Прямо сейчас, единственный способ тестирования - полностью скомпилировать ОС, загрузить его на устройство, использовать в реальных сценариях и сказать, что "он работает". Нет возможности проверить отдельные компоненты.

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

Сделав это, я надеюсь получить данные о покрытии unit test для драйверов и уговорить разработчиков написать тесты, чтобы увеличить охват их драйверов.

Одна вещь, которая возникает для меня, - написать встроенные приложения, которые запускаются в ОС, и реализовать код драйвера, а затем передать результаты обратно на тестовый жгут. У устройства есть несколько интерфейсов, которые я могу использовать, чтобы, вероятно, управлять приложением с моего тестового ПК, чтобы я мог реализовать код.

Любые другие предложения или идеи будут очень оценены.


Обновление. Хотя это может быть не совсем точная терминология, когда я говорю модульное тестирование, я имел в виду возможность тестировать/выполнять код без необходимости компилировать все драйверы OS + и загружать его на устройство. Если бы мне пришлось это сделать, я бы назвал это интеграцией/системным тестированием.

Проблема в том, что части оборудования, которые у нас есть, ограничены, и они часто используются разработчиками при исправлении ошибок и т.д. Чтобы сохранить один выделенный и подключенный к машине, на которой выполняется CI-сервер и автоматическое тестирование, может быть нет нет на данном этапе. Вот почему я ищу способы протестировать драйвер, не имея на самом деле все, и загрузить его на устройство.


Резюме

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

Было бы также иметь смысл иметь небольшую программу, находящуюся в пространстве приложения на устройстве, которая предоставляет API, который может использовать драйвер через последовательный порт или USB, чтобы мясо unit test можно было записать на ПК, который будет связываться с оборудованием и запускать тест.

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

Спасибо всем за ваши ответы.

4b9b3361

Ответ 1

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

В вашей ситуации вам приходят несколько подходов:

  • Добавить обработчики ioctl: каждый код выполняет конкретный unit test
  • С условной компиляцией добавьте main() в драйвер, который проводит тесты функциональных блоков в драйвере и выводит результаты на stdout.
  • Для начальной легкости отладки, возможно, это может быть сделано многоплатформенным, поэтому вам не нужно отлаживать целевое оборудование.
  • Возможно, условный код также может эмулировать устройство в стиле loopback.

Ответ 2

Код, который действительно зависит от аппаратного обеспечения ( низкий уровень стека драйверов в многоуровневой архитектуре), не может быть действительно протестирован нигде, кроме аппаратного обеспечения, или высококачественного моделирования аппаратного обеспечения.

Если ваш драйвер имеет некоторый компонент более высокого уровня, который не полагается непосредственно на аппаратное обеспечение (например, обработчик протокола для отправки сообщений на оборудование в определенном формате), и если эта часть хорошо самодостаточен в коде, тогда вы можете выполнить единичный тест, который будет выполняться отдельно в рамках модульного теста на основе ПК.

Возвращаясь к самому низкому уровню - если он зависит от аппаратного обеспечения, тогда тестовое устройство должно включать аппаратное обеспечение. Вы можете создать тестовое устройство, которое включает аппаратное обеспечение, драйвер и некоторое тестовое программное обеспечение. Главное, я думаю, это получить нормальный код приложения продукта из теста и вместо этого ввести код теста. Тест-код может систематически тестировать все функции драйвера и угловые шкафы (которые не могут применяться кодом приложения), а также может очень сильно затормозить драйвер за короткий промежуток времени (который, вероятно, не работает). Таким образом, это более эффективное использование вашего ограниченного оборудования, чем просто запуск приложения, и дает лучшие результаты.

Если вы можете получить ПК в цикле, тогда ПК может помочь в тестировании. Например. если вы пишете драйвер последовательного порта для встроенного устройства, вы можете:

  • Введите тестовый код для встроенного устройства, которое отправляет различные известные потоки данных.
  • Подключите его к последовательному порту ПК, запустите тестовый код, который проверяет передаваемые потоки данных.
  • То же самое в другом направлении - ПК отправляет данные; встроенное устройство принимает его и проверяет, и уведомляет ПК о любых ошибках.
  • Тесты могут передавать данные на полной скорости и воспроизводить с различными таймингами байтов (однажды я нашел микроконтроллер UART-силиконовый баг, который появился только в том случае, если байты были отправлены с задержкой ~ 5 мс между байтами).

Вы можете сделать аналогичную вещь с драйвером Ethernet, драйвером Wi-Fi.

Если вы тестируете драйвер устройства хранения данных, например, для EEPROM или Flash-чипа, тогда ПК не может участвовать в этом же способе. В этом случае ваш тестовый жгут может протестировать всевозможные условия записи (однобайтовые, блокированные) и проверить целостность данных с использованием всех видов условий чтения.

Ответ 3

У меня была эта точная задача всего два месяца назад.

Позвольте мне угадать: Вероятно, у вас есть "фрагменты" кода, которые описывают детали низкого уровня на устройстве. Вы знаете, что эти фрагменты работают, но вы не можете охватить их, потому что они зависят от драйверов устройств.

Аналогичным образом, нет смысла тестировать каждую отдельную строку по отдельности. Они никогда не запускаются изолированно, и ваш unit test будет выглядеть как зеркальное отражение производственного кода. Например, если вы хотите запустить устройство, вам необходимо создать соединение, передать ему определенную команду низкого уровня reset, затем инициализировать параметр struct и т.д. И т.д. И если вам нужно добавить часть конфигурации, это может потребовать от вас отключить ее, добавить конфигурацию, а затем взять ее в Интернете. Такие вещи.

Вы НЕ хотите тестировать материал низкого уровня. Тогда ваши юнит-тесты будут отражать только то, как вы предполагаете, что устройство работает без подтверждения чего-либо.

Ключевым моментом здесь является создание трех элементов: контроллера, абстракции и реализации адаптера этой абстракции. В Cpp, Java или С# вы должны создать либо базовый класс, либо интерфейс для представления этой абстракции. Я предполагаю, что вы создали интерфейс. Вы разбиваете фрагменты на атомные операции. Например, вы создаете метод "start" и "add (parameter)" в интерфейсе. Вы добавляете свои фрагменты в адаптер устройства. Контроллер воздействует на адаптер через интерфейс.

Определите фрагменты логики в фрагментах, которые вы разместили в адаптере. Затем вам нужно решить, насколько логична эта логика - низкий уровень (подробности обработки протокола и т.д.), Или же это логика, которая должна принадлежать контроллеру.

Затем вы можете проверить в два этапа: * Попросите простую панель тестовой панели, которая действует на конкретный адаптер. Это используется для подтверждения того, что адаптер действительно работает. Это начинается, когда вы нажимаете "start". Например, если вы нажмете "входить в автономный режим", "передать (192)" и "выходить в онлайн" последовательно, чтобы устройство отвечало так, как ожидалось. Это ваш интеграционный тест.

Вы не unit test детали в адаптере. Вы проверяете его вручную, потому что единственным критерием успеха является то, как устройство реагирует.

Однако контроллер полностью протестирован. Он имеет только зависимость от абстракции, которая издевается над вашим тестовым кодом. Таким образом, ваш код не зависит от вашего драйвера устройства, потому что конкретный адаптер не задействован.

Затем вы пишете модульные тесты, чтобы подтвердить, что, например, метод "Add (1)" фактически вызывает "Go offline", затем "Transmit (1)", а затем "Go online" на издеваемую абстракцию.

Задача здесь - провести различие между адаптером и контроллером. Что происходит? Что для меня работало, так это сначала создать вышеупомянутую тестовую панель, а затем манипулировать ею через нее.

Адаптатор должен скрывать детали, которые вам нужно изменить, только если устройство изменилось.

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

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

Ответ 4

Я бы рекомендовал для тестирования на основе приложений. Даже если строительные леса могут быть сложными и дорогостоящими, здесь можно многое выиграть:

  • сбой только один раз, а не одна система
  • способность использовать стандартный набор инструментов (отладчик, проверка памяти...)
  • преодолеть ограничение доступности оборудования
  • более быстрая обратная связь: нет установки на устройстве, просто компилируйте и протестируйте
  • ...

Что касается именования, это можно назвать тестированием компонентов.

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

Ответ 5

Словарь

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

Затем вы выходите из модульного тестирования. Может быть, вы могли бы использовать одно из этих выражений?

  • Автоматизированное тестирование: тестирование происходит без ввода пользователя (в отличие от Manual).
  • Интеграция: тестирование нескольких компонентов вместе (в отличие от тестирования Unit).
    В более широком масштабе, если вы тестируете целую систему, а не только несколько компонентов вместе, это называется System.

ДОБАВЛЕНО после комментариев и обновлений в вопросе:

  • Тестирование компонентов: например, тестирование интеграции или тестирование системы, но в еще меньшем масштабе.
    Примечание. Все три теста интеграции компонентов - один и тот же набор проблем в разных масштабах. Напротив, Unit Testing не (см. Ниже).

Преимущества "реального" модульного тестирования

При тестировании Integration- (или System- или Component-), безусловно, интересно получить некоторую обратную связь, например, покрытие тестирования. Это, безусловно, полезно сделать.

Но это очень сложно (прочитайте "очень дорого" ), чтобы добиться прогресса за какой-то момент, так что Я предлагаю вам использовать дополнительные подходы, такие как добавление некоторых реальных тестов единиц. Зачем?

  • очень сложно имитировать условия края или ошибки. (Примеры: компьютерные часы пересекают день или год во время транзакции, сетевой кабель отключен от сети, тогда мощность падает вниз на какой-то компонент или всю систему, диск заполнен). Используя Unit Testing, потому что вы имитируете эти условия, а не пытаетесь воспроизвести их, это намного проще. Unit Testing - ваш единственный шанс получить действительно хорошее покрытие кода.
  • Тестирование интеграции требует времени (из-за доступа к внешним ресурсам). Вы можете выполнить тысячи unit test во время выполнения одного теста интеграции. Поэтому тестирование многих комбинаций возможно только с помощью Unit Tests...
  • Требуется доступ к определенным ресурсам (аппаратное обеспечение, лицензия и т.д.), тестирование интеграции часто ограничено по времени или шкале. Если ресурсы разделяются другими проектами, каждый проект может использовать их только в течение нескольких часов в день. Даже с исключительным доступом, возможно, только одна машина может его использовать, поэтому вы не можете запускать тесты параллельно. Или ваша компания может купить ресурс (лицензию или оборудование) для производства, но не иметь его (или достаточно рано) для разработки...

Ответ 6

У меня была аналогичная проблема два или три года назад. Я портировал драйвер устройства из VxWorks в Integrity. Мы изменили только зависящие от операционной системы части драйвера, но это был критически важный для безопасности проект, поэтому все модульные тесты, интеграционные тесты переделаны. Мы использовали инструмент автоматического тестирования под названием LDRA testbed для наших модульных тестов. 99% наших модульных тестов выполняются на компьютерах Windows с помощью Microsoft Compilers. Теперь я объясню, как это сделать

Ну, во-первых, когда вы проводите модульное тестирование, вы тестируете программное обеспечение. Когда вы включаете реальное устройство в свои тесты, вы также тестируете устройство. Иногда могут возникнуть проблемы с оборудованием или документацией на оборудование. Когда вы разрабатываете программное обеспечение, если вы четко описали поведение каждой функции, очень легко выполнить модульное тестирование, например, подумайте о функции;

readMessageTime(int messageNo, int* time); 
//This function calculates the message location, if the location is valid, 
//it reads    the time information 
address=calculateMessageAddr(messageNo); 
if(address!=NULL) { 
    read(address+TIME_OFFSET,time); 
    return success; 
} 
else { 
return failure; 
} 

Хорошо, здесь вы просто проверяете, будет ли readMessageTime делать то, что он должен делать. Вам не нужно проверять, вычисляет ли расчетMessageAddr правильный результат или читает, читает правильный адрес. Это зависит от некоторых других модульных тестов. Итак, что вам нужно сделать, это записать заглушки для вычисленияMessageAddr и прочитать (функция ОС) и проверить, вызывает ли он функции с правильными параметрами. Это так, если вы не получаете доступ к памяти непосредственно от своего драйвера. Вы можете протестировать любой код драйвера без какой-либо ОС или устройства с этим менталитетом.

Если вы сопоставили память устройства непосредственно в своем пространстве памяти, а драйвер устройства читает и записывает в память устройства, так как это его собственная память, он становится немного сложнее. Используя автоматические инструменты тестирования, теперь вам нужно посмотреть значения указателей и определить критерии прохождения/падения в соответствии со значениями этих указателей. если вы читаете значение из памяти, вы должны определить ожидаемое значение. В некоторых случаях это может быть сложно.

Есть еще одна проблема, разработчики всегда путают в модульном тестировании таких драйверов, как:

 readMessageTime(int messageNo, int* time); 
 //This function calculates the message location, if the location is valid,
 //it does some jobs to make the device ready to read then 
 //it reads the time information 
 address=calculateMessageAddr(messageNo); 
 if(address!=NULL) { 
      do_smoething(); // Get the device ready to read!    
      do_something_else() // do some other stuff so you can read the result in 3us.
      status=NOT_READY;
      while(status==NOT_READY) // mustn't be longer than 3us.
           status=read(address+TIME_OFFSET,time); 
      return success; 
  } else 
  { 
  return failure; 
  } 

Здесь do_something и do_something_else выполняют некоторые задания на устройстве, чтобы сделать его готовым к чтению. Разработчики всегда спрашивают себя: "Что делать, если устройство не готовится навсегда, а мой код имеет тупик здесь", и они склонны тестировать этот тип устройства на устройстве.

Ну, вы должны доверять изготовителю устройства и техническому автору. Если они говорят, что устройство будет готово в 1-2us, вам не нужно об этом беспокоиться. Если ваш код не работает здесь, вы должны сообщить об этом производителю устройства, это не ваша работа, чтобы найти обходной путь, чтобы превзойти эту проблему. Вы видели мою мысль?

Надеюсь, это поможет....