WritingTests
Написание тестов в системе Wine
Вступление
Сперва стоит уточнить, что мы занимаемся разработкой, доработкой и поддержкой нашего продукта WINE@Etersoft, который базируется на свободном проекте Wine.
Но 80% работы - это системная отладка: поиск и безжалостное уничтожение ошибок, бесконечная шлифовка кода Wine, высшая форма которой является признание получившегося патча сообществом Wine и включение его в cvs. Поэтому тесты необходимы. На нынешнем этапе развития Wine уже реализовано порядка 20 000 функций WinAPI и невозможно прикладывать отдельно каждый патч и проверять - вносит ли он полезные изменения и что более важно - не ломает ли он уже работающее? Сегодня практически стало стандартом, когда вместе с отправкой патча высылается тест, наглядно демонстрирующий изменения, вносимые исправлением. Но в ряде случаев очень приветствуется и простое написание тестов, демонстрирующие ошибки или различия реализации WinAPI в ОС разных версий, так как это позволяет "задокументировать" проблему и иметь надёжный способ её повторить ( ведь не всем же нравится читать описание на пол экрана как проявляется бага, а затем ещё пол часа тыкать мышкой :) ).
Хорошо написанный тест чётко показывает различие в реализации API в Wine и Windows, он может прогоняться автоматически, т.к. должен быть интегрирован в систему тестирования Wine. "Наша" ситуация - это тест ИДЕАЛЬНО проходит в Windows, но однозначно и постоянно падает в одном и том же месте, будучи запущенным в Wine, только после этого можно приступать к исправлению ошибки. Кстати, грамотно написанный тест автоматически убавляет 50% работы, потому что часто не сложно исправить ошибку, но очень трудно локализовать её.
И ещё одно: существующий тест - это весьма весомый аргумент при принятии исправлений, который позволяет на тратить много слов на объяснения вида "Но как это работает, и почему в Windows это устроено так неординарно (глупо)?!". Плюс, если кто-то поломает что-то в том же модуле, в котором Вы вносили исправления, то тест лишает Вас головной боли вида: "От чего отлаженный механизм перестал работать?", - и позволяет быстро найти вредителя, откладывая патчи и прогоняя тест. Поверьте - хороший тест сэкономит Вам кучу времени и нервов, а это очень дорогие вещи сейчас.
Main part
TOOLS
Прежде чем рассказывать про написание тестов, было бы неплохо поговорить о функциях, которыми мы будем пользоваться повсеместно, их немного, но они незаменимы.
- аналог вездесущего printf(), с той лишь разницей, что printf() в тестах использовать строго НЕ РЕКОМЕНДУЕТСЯ, т.к. printf() просто выводит текст, а trace() так же просто его выводит, но при выводе указывает модуль и имя функции откуда вызван, поэтому printf() просто неинформативен. Во время написания тестов забудь про него.
- модификация trace(). Самая главная функция, без неё тест - не тест, а вся соль в первом параметре. Туда передаётся логическое условие, если это условие TRUE, то функция ничего не выводит и счётчик УСПЕШНО пройденных тестов инкрементируется. Если FALSE, то функция дальше работает как trace(), просто выводит всё что ей запихнули, но самое главное - предваряет вывод строчки отображением ругательства "test failed: ваша строка" - данная строчка - это заклинание, которое позволяет находить среди горы информации, переданной в поток вывода, тесты, которые упали. Счётчик ПРОВАЛЕННЫХ тестов также плюсуется.
Пример:
ok(!result && GetLastError()==ERROR_MORE_DATA, "expected %i, got %d\n", ERROR_MORE_DATA, GetLastError());
В случае провала функция выведет : expected ERROR_MORE_DATA ( в виде константы), got GetLastError() (выведет номер ошибки).
MAKE CROSSTEST
Первая часть повествования будет рассказывать о том как внедриться в уже готовые тесты и вероломно затестировать то что надо. Для начала было бы неплохо найти куда пихать свой тест. Обычно это дело тривиальное, но иногда случаются проблемы. Рассмотрим три случая.
Допустим мы хотим написать тест для виджета(контроля) Listbox, Вы обнаружили ошибку , что ListBox неверно реагирует на сообщение LB_ADDSTRING. Наши действия:
- Разведать где находится test case для listbox. Агентура сообщает, что это находится в том же модуле где и сам listbox, только в каталоге tests. Итоговый путь: dlls/user32/tests/listbox.c.
- Найти функцию, куда следует добавить наши 2 - 3 строчки, если же такой функции нет, то дополнительное задание - придумать ей имя и её создать. В нашем случае это будет функция static void test_listbox_height(void).
- Собственно тестируем :
- id = SendMessage( hList, LB_ADDSTRING, 0, (LPARAM) "hi");
- ok( id == 0, "item id wrong\n");
Правда всё просто?
Ремарка: Если же пришлось создавать свою функцию, то чтобы она запустилась при прогоне теста, следует в самом конце модуля внутри функции START_TEST(имя тестового модуля) добавить вызов своей функции:
START_TEST(listbox)
{
- test_listbox_height();
}
Компилируем командой make crosstest. Исправляем ошибки. Компилируем ещё раз. Запускаем, говоря ww user32_test.exe.so listbox, и радуемся результату:
listbox: 345 tests executed (0 marked as todo, 0 failures), 0 skipped.
Допустим мы хотим написать тест для модуля, а каталоге ../tests/такого_модуля_нет.c . Что же тогда делать? А искать по тем модулям которые есть. Делается это несложно - ищутся вызовы функций, которые требуется протестировать, в тестовых модулях. Просматриваются объёмные тестовые модули, аналогов которых нет в исходной библиотеке.
Пример:
Мы хотим протестировать сообщения, которые семантически относятся к модулю mdi.c в user32.dll. В каталоге tests такого модуля нет, но есть весьма объёмный модуль msg.c, и там как раз есть функция static void test_mdi_messages(void), в которой тестируется функциональность mdi.c. Туда и пишем наш тест.
(Это когда вообще всё плохо).
Если Вы читаете этот случай, то это говорит о том, что для модуля, который Вы хотите протестировать, нет test case.
Что ж - мы программисты, поэтому будем делать это ручками. А как - мы это рассмотрим во второй части данной главы, которая будет полностью посвящена созданию своих тестов. Если Вы только начинаете писать тесты, то я крайне советую в целях обучения написать пару-тройку тестов для тех модулей, которые уже есть, благодаря этому Вы приобретёте достаточно навыков для написания собственных тестов.
Часто, после написания теста, есть смысл поделиться им с сообществом разработчиков wine. Несколько слов об оформлении тестов для отправки их в рассылку.
Глобально в Wine тесты делятся на два вила:
1. Тест успешно проходит в Windows и в Wine.
2. Тест успешно проходит в Windows, но проваливается в Wine.
Все остальные тесты считаются по-умолчанию неверными.
Тесты из первой группы являются регресс-тестами, либо тестами-пояснениями. Никакого особого оформления к ним не требуется.
Исключение правда составляют тесты на последовательность (функция ok_sequence(...)), где последний параметр-флаг должен быть выставлен как FALSE, что говорит о том, что тестируемое поведение не требует доработки и соответствует поведению эталона
Для оформления тестов второй группы существует несколько несложных правил:
1. Если для тестирования используется функция ok(...), то перед её вызовом необходимо поставить оператор todo_wine{ }, это будет символизировать то, что функционирование Wine, при данных условиях, отличается от работы Windows и Wine следует доработать.
2. Если для тестирования используется функция ok_sequence(...), то последний параметр-флаг должен быть выставлен как TRUE, по тем же причинам, что и в пункте 1.
Несколько правил об отправке тестов в рассылку:
1. Если отправляется тест из второй группы вместе с исправлением неправильной работы Wine одним патчем, то todo_wine или TRUE в ok_sequence(...) выставлять не следует, но стоит описать в теле письма, то что добавляется тест и сразу исправление кода.
2. Если отправляется только тест из второй группы отдельным письмом, то он оформляется в соответствии с правилами.
3. Если отправляется только тест из второй группы отдельным письмом, а затем следующим письмом исправление, основанное на этом тесте, то в исправлении должны быть строчки, убирающие todo_wine из теста, либо выставляющие FALSE флагу функции ok_sequence(...).
Описание создания тестов из оригинальной документации:
http://www.winehq.org/site/docs/winedev-guide/testing