LINUX.ORG.RU
ФорумTalks

TDD. Рецепты

 ,


1

3

Всех приветствую.
Случилось у меня тут затишье. В очередной раз наследую чужой говнокод (практически обычная моя работа) для допиливания его до работоспособного состояния. И в связи с чем в пустую голову полезли всякие глупости типа TDD (точней на хабре прочитал в свежей статье).

Так как сам я этим не пользуюсь, хотел бы узнать у благородных донов (смердам и быдлам не беспокоится) как этим всем пользоваться. С теорией я вроде как знаком (я умею в википедию и там в поиск всякий). Но вот на практике как это делать чего-то мне не ясно.

Хотелось бы каких-то рецептов по правильной готовке. Желательно применимо к моей специфике. Обычно я все пишу на Си, и чаще всего для МК и всяких SoC. Опять же, чаще всего все завязано на всякие аппаратные интерфейсы и взаимодействие через них с внешним миром и всякими реакциями на внешние раздражители.

Вот к примеру. Начинаю я новый проект на малоизученном SoC-e. ARM, стандартные IP-core и т д. И надобно мне запилить протокол через UART, моргание лампочками через GPIO, управление периферией по I2C, опрос кнопочек и т д. Ну и начинаю я все это реализовывать. Идея есть, описание алгоритмов есть (по двойному нажатию кнопачки, три раза моргаем зеленым свистком, пишем в уарт «че нада», и по i2c отправляем 2 байта). Будет у меня файл main.c с вызовом всего и главным лупом. И куча файлов с «драйверами» для всех интерфейсов (ну там инициализации, обработчики прерываний и т.д.) Как теперь во все это воткнуть TDD?
Как все это будет выглядеть?
Как это запускать и куда смотреть результат?
пихать в рабочий код кучу ifdef с написанием тестов прям в боевом коде. Или писать еще один проект с отдельным main.c где буду дергать функции из боевого дерева?
Как это автоматически запускать и контролировать успешность выполнения?
Как писать тесты для инициализации аппаратной периферии?
как писать тесты для прерываний?

Или может это вся блаж только для жаболюбов? А для встаиваемых систем это ересь?

★★★★★
Ответ на: комментарий от sehellion

Если уж речь о железе, то может стоит железом и тестировать? /thread

Не просто железо, а программируемое железо. И есть варианты когда нужно протестировать не просто работу с железом, а например какой-нидь условный подсчет crc8. Как это сделать в таком проекте? Пилить двойную сборку под платформу разработки с последующим запуском на ней только тестов?

yax123 ★★★★★
() автор топика

Как писать тесты для инициализации аппаратной периферии?
как писать тесты для прерываний?

Для тестов по-нормальному нужно всё это взаимодействие с портами и кнопочками обернуть в какую-то прослойку, чтобы можно было заменить на mock штуки которые будут симулировать поведение продакшен системы. Задачи типовые и должно быть что-то уже готовое.


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

Tark ★★
()
Ответ на: комментарий от yax123

Вот тут уже сложнее. С одной стороны, можно только все библиотеки/драйверы тестировать. С другой, работа главного дерева тоже должна быть как-то проверена. А еще ведь есть и прерывания...
Кстати, Вы по теме еще не листали «Test-Driven Development for Embedded C» / James W.Grennin ?

sehellion ★★★★★
()
Ответ на: комментарий от yax123

И есть варианты когда нужно протестировать не просто работу с железом, а например какой-нидь условный подсчет crc8. Как это сделать в таком проекте? Пилить двойную сборку под платформу разработки с последующим запуском на ней только тестов?

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

Tark ★★
()
Ответ на: комментарий от sehellion

«Test-Driven Development for Embedded C» / James W.Grennin

Мы его листали, но именно, что листали. Правда было это годы назад и осталось ощущение, что ответа на вопрос как все это конкретно организовать я не нашел.

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

Я понимаю, что у всех свои рецепты и пока не пройдешь всю «мморпг» не ясно кого и как качать. Но я надеюсь, что есть такой источник реальных знаний.

yax123 ★★★★★
() автор топика

точней на хабре прочитал в свежей статье

Не читай хабр

Обычно я все пишу на Си, и чаще всего для МК и всяких SoC.

user space? Проще на плюсах и googletest тогда. Или речь вообще не про Линукс?

Короч наши партнёры пилят проект и используют его для более низкоуровнего тестирования. Мы щас как разгребём наш проект, тоже будем допиливать наши тесты в этом направлении. Но, как сказали выше, для тестирования железа без железа не обойтись. https://github.com/labgrid-project

UVV ★★★★★
()
Ответ на: комментарий от Tark

Я понимаю что тестирования всякого много. Но мне интересно тестирование программно-аппаратных решений. Что можно тестировать и как. Кто такие «моки»?

yax123 ★★★★★
() автор топика
Ответ на: комментарий от yax123

Кто такие «моки»?

Ты плохо читал статью по TDD ;)

mock - это фейковый объект, который ведёт себя как настоящий. Т.е. ему можно выставлять возвращаемые значения и ожидания, таким образом ты тестишь свой софт.

UVV ★★★★★
()
Ответ на: комментарий от UVV

Не читай хабр

Я лучше UVV читать не буду. пользы ноль!

user space? Проще на плюсах и googletest тогда. Или речь вообще не про Линукс?

Ну я даже не знаю. В начальном тексте я привел пример гипотетического проекта. Про линукс я там ничего не сказал.

Короч наши партнёры пилят проект и используют его для более низкоуровнего тестирования.

Офигенно полезное предложение. Пойду лучше хабру почитаю.

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

yax123 ★★★★★
() автор топика
Ответ на: комментарий от UVV

Ты плохо читал статью по TDD ;)

Я и не претендую на экспертизу.

mock - это фейковый объект, который ведёт себя как настоящий. Т.е. ему можно выставлять возвращаемые значения и ожидания, таким образом ты тестишь свой софт.

Очень хорошо. Как конкретно обычно обмазывают этими моками реальный проект? Хотя бы на примере uint8_t crc8(uint8 *buf, uint8_t size);

yax123 ★★★★★
() автор топика
Ответ на: комментарий от yax123

Очень хорошо. Как конкретно обычно обмазывают этими моками реальный проект? Хотя бы на примере uint8_t crc8(uint8 *buf, uint8_t size);

На этом примере тест будет заключатся в вызове этой функции N(например 10) раз с разными параметрами и сравнением с известным правильным результатом для этих параметров. Подключаешь исходник с этой функцией отдельно к исходнику тестов который будет компилироваться в отдельный исполняемый файл и оттуда вызываешь её.

Tark ★★
()
Последнее исправление: Tark (всего исправлений: 2)
Ответ на: комментарий от Tark

Подключаешь исходник с этой функцией отдельно к исходнику тестов который будет компилироваться в отдельный исполняемый файл и оттуда вызываешь её.

embedded же. Где цеплять-то? На «железке» или локально на компе?

Никто не гарантирует что одинаковый код будет работать одинаково на AMD_64 и на AVR32 (банально из-за разной разрядности, а всякие алгоритмы быстрого выичсления CRC по таблице и прочая «магия» могут это этого «пострадать»)

Как вариант - в протеусе можно накидать схему с контроллером, залить в него прошивку и смотреть. Но я ХЗ что он умеет эмулировать, когда я последний раз видел седьмой протеус он умел AVR эмулировать и мне этого для академических целей хватало. Не знаю как там у современных версий.

RiseOfDeath ★★★★
()
Последнее исправление: RiseOfDeath (всего исправлений: 3)
Ответ на: комментарий от RiseOfDeath

Цеплять на компе, запускать через эмулятор. А вообще мы же тут все с онтопиком связанные, нужно использовать такие типы, чтобы по возможности программа была кроссплатформенная.
А насчет crc, это так-то математическая функция без сайд эффектов, её один раз написал и забыл на сто лет, а юнит-тесты в основном для бизнес-логики нужны.

Tark ★★
()
Ответ на: комментарий от RiseOfDeath

И вообще яваскрипт же как-то тестируют, а оно работает на всём начиная с 32 битного ARM9 до intel itanium. Понятно что сама CRC8 может использовать аппаратные хаки, но моргание по кнопочке то нормально написано.

Tark ★★
()
Последнее исправление: Tark (всего исправлений: 1)
Ответ на: комментарий от Tark

насчет crc, это так-то математическая функция без сайд эффектов

Это когда она «математическая функция (у которой есть существенный минут - полиномы долго считать). А вот на счет быстрого алгоритма по табличным значениям (не знаю как он правильно называется) это уже „магия“ (хотя где-то на хабре видел объяснение как но работает, но я его не понял), которая может внезапно сломаться (ну тут пример, возможно не самый удачный, т.к. ломаться нечему, но все же)

Конкретно я говорю про вот эту реализацию:

static uint32_t crc32_tab[] = {
	0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
	0xe963a535, 0x9e6495a3,	0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
	0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
	0xf3b97148, 0x84be41de,	0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
	0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,	0x14015c4f, 0x63066cd9,
	0xfa0f3d63, 0x8d080df5,	0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
	0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,	0x35b5a8fa, 0x42b2986c,
	0xdbbbc9d6, 0xacbcf940,	0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
	0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
	0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
	0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,	0x76dc4190, 0x01db7106,
	0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
	0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
	0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
	0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
	0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
	0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
	0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
	0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
	0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
	0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
	0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
	0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
	0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
	0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
	0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
	0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
	0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
	0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
	0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
	0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
	0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
	0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
	0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
	0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
	0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
	0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
	0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
	0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
	0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
	0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
	0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
	0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};

uint32_t
crc32(uint32_t crc, const void *buf, size_t size)
{
	const uint8_t *p;

	p = buf;
	crc = crc ^ ~0U;

	while (size--)
		crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);

	return crc ^ ~0U;
}
RiseOfDeath ★★★★
()
Последнее исправление: RiseOfDeath (всего исправлений: 3)
Ответ на: комментарий от Tark

Подключаешь исходник с этой функцией отдельно к исходнику тестов который будет компилироваться в отдельный исполняемый файл и оттуда вызываешь её.

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

Как тогда будет выглядеть исходники тестов? Для каждого теста свой отдельный файл с исходным кодом и отдельным Makefile? Или городить один огромный файл тестов и цеплять к нему все что нужно тестировать? Ну ладно это простой пример.

А если посложней типа обработчика прерываний? Вызываем его, а он из регистров должен читать данные и флаги? Городить отдельный заголовок для вызова как обычную функцию. А команды чтения из регистров ifdef-ами заменять на вызовы моков?

yax123 ★★★★★
() автор топика
Последнее исправление: yax123 (всего исправлений: 1)
Ответ на: комментарий от yax123

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

Ну да, если у тебя рядом с main оно всё лежит, то не заработает. А если не рядом с main то какая разница что еще рядом лежит, оно же линкером даже не подтянется в исполняемый файл.

Как тогда будет выглядеть исходники тестов? Для каждого теста свой отдельный файл с исходным кодом и отдельным Makefile? Или городить один огромный файл тестов и цеплять к нему все что нужно тестировать? Ну ладно это простой пример.

Огромный файл тестов, который через инклюды подключает отдельные тесты.

А если посложней типа обработчика прерываний? Вызываем его, а он из регистров должен читать данные и флаги? Городить отдельный заголовок для вызова как обычную функцию. А команды чтения из регистров ifdef-ами заменять на вызовы моков?

От кода не зависит поступит прерывание или нет, так что смысл городить оггород? Сделай вообще мок файл обработчика прерываний который под каждый отдельный случай делает будто есть какое-то прерывание. Я вообще больше по вебу и data science, так что в embedded почти нуль, но у вас определенно должны быть фреймворки для этого, 21 век уже.
П.С.
Не делай ifdef, код не должен знать, что его тестируют.

Tark ★★
()
Последнее исправление: Tark (всего исправлений: 1)
Ответ на: комментарий от RiseOfDeath

Я не настоящий сварщик, но тут же конкретно указан тип и сколько в нем байт, оно же работать везде будет. Я правда хз как тут с big endian и little endian.

Tark ★★
()
Последнее исправление: Tark (всего исправлений: 1)

Как по мне - основа любых адекватных модульных тестов - мокап подсистем друг для друга, без этого будет фэ. Т.е. как смокать прерывания - я х3, а вот как смокать аппаратную периферию - надо отгородить этот процесс набором интерфейсов, которые для остальных подсистем в тестах надо будет замокать.

Я без понятия, как оно там в embeded, если на виртуальных функциях всё ещё экономят, то наверное не стоит и браться, если ты не мастер статического полиморфизма.

В случае параллельных систем, надо просто тестировать все комбинации когда и что произошло (до/после), а ошибки синхронизации всё равно придётся искать методом пристального взгляда или санитайзерами/хэлгриндами. Но при этом, проще гонять тесты под санитайзером, чем конечное приложение, да ещё и на железке.

Ещё, такой момент - не обязательно тестировать всё, практика показывает, что если построить для i/o годный интерфейс, то обычно, достаточно протестировать бизнесс логику, которая может быть низкоуровневой, например генерация какого нибудь сигнала, но всё равно является таковой. А вот реализацию i/o, гораздо(в разы, на порядки) дешевле обычно протестировать вручную.

pon4ik ★★★★★
()

Или может это вся блаж только для жаболюбов?

Эта «блажь» для тех, кто может создать тестовую среду - условно говоря, можно программно задать входное воздействие и программно же проконтролировать реакцию тестируемой системы. Если ты этого не можешь (т.е. тебе нужно пальцами нажимать кнопки и глазами смотреть на светодиоды), она не для тебя.

А для встаиваемых систем это ересь?

На том уровне, на котором работаешь ты - пожалуй, да. Уровнем выше (чистый софт) пишутся юнит-тесты разного вида, уровнем ниже (VHDL/Verilog) пишутся тестбенчи.

Может, у тебя получится подавать воздействия и проверять реакцию через JTAG или еще что-то?

tailgunner ★★★★★
()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от pon4ik

не обязательно тестировать всё, практика показывает, что если построить для i/o годный интерфейс

Насколько я могу судить, ТС как раз пишет то, что потом скрывается за «годным интерфейсом I/O».

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

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

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

Вот можно порекомендовать какой нибудь в меру простой парсер написать используя TDD. Вот взять к примеру SBE(который simple binary encoding).

pon4ik ★★★★★
()
Ответ на: комментарий от pon4ik


много умных слов (без сарказма), но я так и не понял, как все таки лучше строить проект, чтобы он был покрыт тестами?
Как я это пока понимаю:
1. Все функции по возможности в отдельные файлы (чтобы не тянулось всякое нерезолвенное барахло).
2. Для тестирования отдельный проект к которому цепляются все необходимые части боевого проекта. Получается собирать надо под тест-платформу (а она как правило x86 в отличии от боевой которая может быть ARM/AVR/C166/MSP/ я много других умных слов еще знаю).
3. тестирование «чистой математики» тривиально (пример с crc8).
4. Тестирование взаимодействия с аппаратурой уже сложно. Там везде есть обмен с регистрами. Вроде как надо вместо них подставлять вызовы мок-функций, но подсунуть их можно только ifdef (фейл - типа прога не должна знать что ее тестирует, с чем я согласен), либо делать абстрактную прокладку и при сборке тестов реализацию подменять на наши моки (это получится мегамонстр, либо на крестах писать, но это не везде пройдет. Например CC7A не умеет в кресты).

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

yax123 ★★★★★
() автор топика
Ответ на: комментарий от pon4ik

в том же ядрышке, все лепят таблицы коллбэков переопределяемых для подсистем и имеют уважение

Это не имеет никакого отношения к тестированию и никак не помогает тестировать, например, драйверы.

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

Что именно ты видел и как бы покрывал это тестами?

Вот можно порекомендовать какой нибудь в меру простой парсер написать используя TDD

Парсеры - это просто счастье с точки зрения тестирования.

tailgunner ★★★★★
()
Ответ на: комментарий от yax123

Таблицы виртуальных функций можно и на C делать, просто вручную.

Самая правильная мысль в твоём ответе:

делать абстрактную прокладку и при сборке тестов реализацию подменять на наши моки

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

Протестировать можно почти всё, вопрос в соотношении трудозатраты/выгода. Применять какой то приём ради его применения это скользкая дорожка.

pon4ik ★★★★★
()
Ответ на: комментарий от tailgunner

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

в целом могу. Что мешает взять какой-нидь usb-gpio и «фигачить» по кнопкам и смотреть «лампочки». Но это будет тестирование на уровне тестируем все изделие в целом. А я хочу протестировать отдельные части. Или взаимодействие этих частей, да еще с их взаимодействием с аппаратурой. теоретически ничего не мешает мне по jtag-у (опять же если он там есть) (ответ писал до твоего дополнения) устанавливать значение регистров исполнять код и смотреть результаты (в регистрах или памяти). Но я чет даже представлять не хочу какого размера монстр в результате получится. Опять же есть случаи когда jtag-а вообще нет.

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

yax123 ★★★★★
() автор топика
Ответ на: комментарий от tailgunner

Сложно вспомнить конкретику, извини.

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

Сумбурненько, но детали вспоминать не охота сейчас.

pon4ik ★★★★★
()
Ответ на: комментарий от pon4ik

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

как раз наоборот все производители во всю пытаются пропихивать свои HAL-ы на свои поделки. И похоже одна из причин как раз удобство тестирования. Но я не видел ни разу чтобы это кто-то использовал. Мысля то в целом годная. Делаем абстрактный интерфейс к аппаратуре и фигачим кроссплатформенный код. Для перехода на другой чип/архитектуру подсовываем специфичную реализацию методов и у нас опять все работает после правки конфига и перекомпиляции. Но каждый мудический производитель чипов фигачит свою версию регистрового интерфейса к аппаратуре. И получается, что сделать универсальный абстрактный слой неполучается. У одного можно задавать 1,5 стоповых бита, а у другого уже нет. И как это разрулить неясно. Есть конечно конгломераты разных чипов который собраны из одинаковых ipcore, но это не правило, а исключение.

yax123 ★★★★★
() автор топика
Ответ на: комментарий от yax123

Что мешает взять какой-нидь usb-gpio и «фигачить» по кнопкам и смотреть «лампочки». Но это будет тестирование на уровне тестируем все изделие в целом

Всё изделие целиком тоже можно разрабатывать с использованием TDD.

А я хочу протестировать отдельные части.

И что мешает запустить только часть софта?

теоретически ничего не мешает мне по jtag-у (опять же если он там есть) (ответ писал до твоего дополнения) устанавливать значение регистров исполнять код и смотреть результаты (в регистрах или памяти). Но я чет даже представлять не хочу какого размера монстр в результате получится.

Монстр может получиться, если регистровый интерфейс сложный, но даже монстр может стоить усилий на свое написание.

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

TDD - тупо баззворд, и можно использовать по сути TDD, не называя его так. Но автоматическо тестирование в эмбеддеде организовать сложно или, скорее, накладные расходы на такое тестирование выглядят неоправданными.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

И что мешает запустить только часть софта?

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

Как мне написать это обработчик чтобы он мог одинаково работать и в 8-разрадном МК. И в тестовом окружении на x86?

как мне все это обмазать моками?

yax123 ★★★★★
() автор топика
Ответ на: комментарий от pon4ik

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

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

Так же и у ТС - безусловно, можно написать имитационную среду, в которой его код будет работать с минимальными изменениями. Но цена написания этой среды запретительно высока.

tailgunner ★★★★★
()
Ответ на: комментарий от yax123

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

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

pon4ik ★★★★★
()
Ответ на: комментарий от pon4ik

В общем, если клиент готов платить, то сделай за его счёт учебный проект,

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

yax123 ★★★★★
() автор топика
Ответ на: комментарий от tailgunner

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

имитатор драйвера

что бы не путаться в новоязе - это будет fake.

Утверждждения вроде «можно» стоят недорого.

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

pon4ik ★★★★★
()
Последнее исправление: pon4ik (всего исправлений: 1)
Ответ на: комментарий от yax123

Вот мне надо протестировать мой обработчик прерываний. Максимально изолированно от всего и в автоматическом режиме.

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

Как мне написать это обработчик чтобы он мог одинаково работать и в 8-разрадном МК. И в тестовом окружении на x86?

Здесь вопрос надо ставить по-другому: «каково должно быть мое тестовое окружение на ПК, чтобы код с МК работал на нем с минимальными изменениями?». Собственно, главная здравая идея TDD - это проектирование кода так, чтобы его было легче тестировать. В случае эмбеддеда - максимальная изоляция кода, который для тестирования требует настоящего устройства (или сложной эмуляции). Если на устройство завязан вообще весь код, остается только тестирование на готовом эмуляторе или реальном устройстве.

tailgunner ★★★★★
()
Ответ на: комментарий от yax123

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

Да не обязательно, подключать можно весь файл, функция ведь не подтянется если не используется.

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

Посмотри на веб, там архитектура заточена под проекты живущие 5-10 лет и над которыми работает мильон индусов. И вот там сейчас популярна такая вещь, как DI, и в общем вместо хидера с настоящими функциями ты для тестирования можешь подключать хидер с мок функциями, в крайнем случае ifdef будет в подключении хидеров, но должны быть уже стандартные решения.

Вообще тестирование штука ресурсозатратная, как и кроссплатформенность. Кода тестов обычно в несколько раз больше чем непосредственно кода программы. И вообще, взгляни на яваскрипт, оно одинаково работает на всех архитектурах и из под любого браузера, но накладные расходы тоже не маленькие.

Tark ★★
()
Последнее исправление: Tark (всего исправлений: 1)
Ответ на: комментарий от pon4ik

имитатор драйвера

что бы не путаться в новоязе - это будет fake.

Ну да, чтобы не путаться, давай придумаем для существующей сущности еще одно название.

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

Ну вот тебе годная «либа для моков» - qemu. Прикинь стоимость написания эмулятора устройства.

Такие же функции, так же возвращают коды возвратов.

Ну да, всего-то взаимозависимые коды возврата. Делов-то.

tailgunner ★★★★★
()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от pon4ik

Я думаю, если кто-то так и делает, то в паре топиков на лоре он тебе всю мякотку не расскажет никак.

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

Возможно у меня не хватает фантазии на маленькое, дешевое, но элегантное и действенное решение.

yax123 ★★★★★
() автор топика
Ответ на: комментарий от Tark

Посмотри на веб,

я не хочу смотреть веб.

Вообще тестирование штука ресурсозатратная

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

yax123 ★★★★★
() автор топика
Ответ на: комментарий от yax123

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

Именно так. Поэтому во взрослых средах разработки есть готовые имитаторы и эмуляторы, и скриптовые языки для управления всем этим добром.

tailgunner ★★★★★
()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от yax123

Много твоих не игрушечных embeded проектов на гитхабе(без стёба, просто интересна ситуация)?

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

pon4ik ★★★★★
()
Ответ на: комментарий от tailgunner

Именно так. Поэтому во взрослых средах разработки есть готовые имитаторы и эмулятор, и скриптовые языки для управления всем этим добром.

Опять про взрослые среды. Хочу конкретный пример как это реализовано, в любой среде на ваш выбор. Хочу увидеть пример где в проекте есть отдельная цель для тестирования встраиваемого ПО при помощи любых средств (хоть эмулятор в среде, хоть jtag в девборде, хоть qemu).

А то мне это напоминает:

Утверждждения вроде «можно» стоят недорого.

yax123 ★★★★★
() автор топика
Ответ на: комментарий от pon4ik

Много твоих не игрушечных embeded проектов на гитхабе

Я не пользуюсь гитхабом. Все мои проекты узкоспецифичноспециализированные. А домашние поделки не стоят того чтобы ими размахивать на всю сеть (они интересны только мне и только в моих условиях, да еще их очень мало, быстро из любительства пришлось прыгнуть в проф.применение).

yax123 ★★★★★
() автор топика
Последнее исправление: yax123 (всего исправлений: 1)
Ответ на: комментарий от tailgunner

Боюсь ты не понял мой спич, у меня всегда с этим проблемы.

Попробую ещё раз. В нормально спроектированном ПО, подсистемы общаются через чётко выраженные программные интерфейсы. Так вот, тебе не надо эмулировать «железо», достаточно сделать управляемую из вне реализацию его программного интерфейса.

Кейс того же теста на параллельный запуск шины и диска:

Possitive 
Запустить диск.
Запустить шину.
Ожидаем вызов опроса готовности шины, возвращает "готов".
Ожидаем вызов начала инициализации диска.

Negative
Запустить диск.
Запустить шину.
Ожидаем вызов опроса готовности шины, возвращает "не готов".
Ожидаем вызов опроса готовности шины, через таймаут T возвращает "не готов"
Ожидаем вызов коллбэка "сбой инциализации дисковой подсистемы"

SlowBus
Запустить диск.
Запустить шину.
Ожидаем вызов опроса готовности шины, возвращает "не готов".
Ожидаем вызов опроса готовности шины, через таймаут T возвращает "готов готов"
Ожидаем вызов начала инициализации диска.
pon4ik ★★★★★
()
Ответ на: комментарий от yax123

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

pon4ik ★★★★★
()
Ответ на: комментарий от yax123

Хочу конкретный пример как это реализовано, в любой среде на ваш выбор.

Мне неизвестно о таких проектах в открытом доступе. Если бы я искал, я бы начал со встраиваемых libc вроде newlib - они точно тестируются на имитаторе; то же про gcc - там на имитаторе тестируются бинари.

А то мне это напоминает:

Утверждждения вроде «можно» стоят недорого.

Да, это так. Извини, что я не смог соответствовать уровню твоих запросов.

tailgunner ★★★★★
()
Ответ на: комментарий от pon4ik

Боюсь ты не понял мой спич, у меня всегда с этим проблемы.

Аналогично.

нормально спроектированном ПО, подсистемы общаются через чётко выраженные программные интерфейсы. Так вот, тебе не надо эмулировать «железо», достаточно сделать управляемую из вне реализацию его программного интерфейса.

Да, конечно. Но 1)сколько-нибудь полезная реализация этого программного интерфейса является фактически эмулятором железа 2) вообще-то речь идет о том, как тестировать код, который находится ниже интерфейса, а не выше его.

tailgunner ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.