FreeSource: WINE/WritingTests

Написание тестов в системе Wine

Вступление

Для чего нужны тесты и зачем разработка под Wine требует их написания?

Сперва стоит уточнить, что мы занимаемся разработкой, доработкой и поддержкой нашего продукта WINE@Etersoft, который базируется на свободном проекте Wine.

Но 80% работы – это системная отладка: поиск и безжалостное уничтожение ошибок, бесконечная шлифовка кода Wine, высшая форма которой является признание получившегося патча сообществом Wine и включение его в cvs. Поэтому тесты необходимы. На нынешнем этапе развития Wine уже реализовано порядка 20 000 функций WinAPI и невозможно прикладывать отдельно каждый патч и проверять – вносит ли он полезные изменения и что более важно – не ломает ли он уже работающее? Сегодня практически стало стандартом, когда вместе с отправкой патча высылается тест, наглядно демонстрирующий изменения, вносимые исправлением. Но в ряде случаев очень приветствуется и простое написание тестов, демонстрирующие ошибки или различия реализации Win API в ОС разных версий, так как это позволяет «задокументировать» проблему и иметь надёжный способ её повторить ( ведь не всем же нравится читать описание на пол экрана как проявляется бага, а затем ещё пол часа тыкать мышкой :) ).

Хорошо написанный тест чётко показывает различие в реализации API в Wine и Windows, он может прогоняться автоматически, т.к. должен быть интегрирован в систему тестирования Wine. «Наша» ситуация – это тест ИДЕАЛЬНО проходит в Windows, но однозначно и постоянно падает в одном и том же месте, будучи запущенным в Wine, только после этого можно приступать к исправлению ошибки. Кстати, грамотно написанный тест автоматически убавляет 50% работы, потому что часто не сложно исправить ошибку, но очень трудно локализовать её.

И ещё одно: существующий тест – это весьма весомый аргумент при принятии исправлений, который позволяет на тратить много слов на объяснения вида «Но как это работает, и почему в Windows это устроено так неординарно (глупо)?!". Плюс, если кто-то поломает что-то в том же модуле, в котором Вы вносили исправления, то тест лишает Вас головной боли вида: «От чего отлаженный механизм перестал работать?», – и позволяет быстро найти вредителя, откладывая патчи и прогоняя тест. Поверьте – хороший тест сэкономит Вам кучу времени и нервов, а это очень дорогие вещи сейчас.

Main part

TOOLS

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

void traсe( const char *msg, ... )

– аналог вездесущего printf(), с той лишь разницей, что printf() в тестах использовать строго НЕ РЕКОМЕНДУЕТСЯ, т.к. printf() просто выводит текст, а trace() так же просто его выводит, но при выводе указывает модуль и имя функции откуда вызван, поэтому printf() просто неинформативен. Во время написания тестов забудь про него.

int ok( int condition, const char *msg, ... )

– модификация trace(). Самая главная функция, без неё тест – не тест, а вся соль в первом параметре. Туда передаётся логическое условие, если это условие TRUE, то функция ничего не выводит и счётчик УСПЕШНО пройденных тестов инкрементируется. Если FALSE, то функция дальше работает как trace(), просто выводит всё что ей запихнули, но самое главное – предваряет вывод строчки отображением ругательства «test failed: ваша строка» – данная строчка – это заклинание, которое позволяет находить среди горы информации, переданной в поток вывода, тесты, которые упали. Счётчик ПРОВАЛЕННЫХ тестов также плюсуется.

Пример:

ok(!result && Get Last Error()==ERROR_MORE_DATA, «expected %i, got %d\n», ERROR_MORE_DATA, Get Last Error());

В случае провала функция выведет : expected ERROR_MORE_DATA ( в виде константы), got Get Last Error() (выведет номер ошибки).

MAKE CROSSTEST

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

Случай первый:

Допустим мы хотим написать тест для виджета(контроля) Listbox, Вы обнаружили ошибку, что ListBox неверно реагирует на сообщение LB_ADDSTRING. Наши действия:

  1. Разведать где находится test case для listbox. Агентура сообщает, что это находится в том же модуле где и сам listbox, только в каталоге tests. Итоговый путь: dlls/user32/tests/listbox.c.
  2. Найти функцию, куда следует добавить наши 2 – 3 строчки, если же такой функции нет, то дополнительное задание – придумать ей имя и её создать. В нашем случае это будет функция static void test_listbox_height(void).

  3. Собственно тестируем :
id = Send Message( 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.

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

Share your ideas

Часто, после написания теста, есть смысл поделиться им с сообществом разработчиков 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

Страницы, ссылающиеся на данную: WINE

WINE/Разработчику