TG: Test Driver Generator.TG помогает вам тестировать программные компоненты, генерируя программы, которые автоматически проводят тесты. Эта документация соответствует версии TG 3.1. Test Driver это программа, которая тестирует часть программного обеспечения. Когда вы хотите оттестировать что-то, например, какой-то пакет на языке Ада, обычно вам приходится написать большое число отдельных элементарных тестов, которые выполняются один за другим. Каждый тест обычно состоит из вызова, который передает какие-то данные в подпрограмму пакета, и последующего анализа получаемого результата. Назовем этот элементарный тест - испытанием (test case). Обычно Test Driver простая, но очень длинная программа. В основном она состоит из бесконечного повторения похожих кусочков - испытаний. Желательно бы иметь программу строящую подобные тесты (Test driver-ы). TG как раз и является такой программой. Вы передаете ей описания тестов в специальном формате, называемом тестовым скриптом, а tg преобразует его в текст на языке Ада, который затем может быть скомпилирован и собран в готовую программу. TG работает с языком Ада 95. Предположим вам нужно проверить функцию, которая подсчитывает символы '&' в строке. function Count_Ampersand (Str : in String) return Natural; Одним из испытаний, может быть вызов этой функции с аргументом "bc&&&abc" и проверка, что результат равен трем. Этот код может выглядеть как:
Put ("Testing three ampersands in the middle... ");
begin
Count := Count_Ampersand ("abc&&&abc");
if Count = 3 then
Put_Line ("pass.");
else
Put_Line ("fail.");
end if;
exception
when others =>
Put_Line ("fail.");
end;
Слишком много кода для одного теста. На языке TG это выглядит как
***** Testing three ampersands in the middle...
test Count := Count_Ampersand ("abc&&&abc");
pass Count = 3
TG преобразует это в текст, приведенный ранее и получает готовую к выполнению программу. Как объяснялось ранее TG рассматривает ТЕСТ как последовательность испытаний. ТЕСТ подготавливается как файл с тестовым скриптом. Вы готовите тестовый скрипт в формате описываемом в данном документе. То, что вы тестируете, называется тестируемый элемент (test item). Это может быть одной подпрограммой, пакетом или целой системой. Мы говорим, что тест выполняется прогоном Test Drive программы. Аналогично, единичное испытание проходит как выполнение кода получаемого из описания испытания. Важнейшим моментом является вызов тестируемого элемента. Мы называем это "тестовым вызовом". Test driver-у может потребоваться некоторая подготовительная работа перед выполнением "тестового вызова", а затем он анализирует полученные результаты. Есть три типа результата: результат тестового вызова, результат испытания и результат всего теста.
В терминах TG "тестовый скрипт" означает машинно-обрабатываемое законченное описание теста. Это описание включает только самое необходимое для построения test driver-а. TG по данному описанию формирует готовую Ада программу. Написание тестового скрипта вместо написания test driver-а вручную, не только упрощает построение теста, но и делает описание тестов более единообразным. В тестовом скрипте можно выделить две секции - глобальную и секцию испытаний. Естественно, параметры для всего теста описаны в глобальной секции, а в секции испытаний описаны отдельные испытания. Файл содержащий тестовый скрипт имеет расширение ".ts". Тестовый скрипт в основном состоит из кусочков Ада кода в вперемешку со специальными словами, которые говорят TG куда эти кусочки надо поместить в готовом test driver. Основная идея в том, что специальные слова должны начинаться в первой позиции строки. Затем идет код на языке Ада, который может продолжаться любое количество строк, при условии, что эти строки начинаются с пробела. Строка, которая не начинается с пробела, обозначает окончание кусочка. Например
prepare Result := 0;
Done := False;
if not Initialized then
Initialize;
end if;
test ...
Кусочек, который будет подготовительной частью (prepare) начинается с "Result :=" и кончается "end if;", занимая 6 строк. За ним следует часть теста (test). Значение этих частей обьясняется далее. TG не чувствителен к регистру, так же как и язык Ада. Это значит, вы можете писать ключевые слова TG в любом регистре. Комментарии начинаются с '--' как и в языке Ада. Однако, если комментарий начинается не с первой позиции TG воспримет это, как чать текста на Аде и перенесет его в генерируемую программу. В глобальной секции возможны следующие элементы: fail_handling, error_handling, context, exceptions и define. Мы опишем их последовательно, хотя в скрипте из порядок не играет роли. Кроме части context, другие части не являются обязательными. Напомним, что ключевые слова должны начинаться с первой позиции строки.
Каждое описание испытания описывает одно испытание. Для TG испытание характеризуется, как
Шаблон одного испытания может быть описан как ***** заголовок test-case define definitions prepare preparations test test-statement pass [ path ] [ , predicate ] cleanup cleanup-code Значения частей define, prepare и cleanup мы уже описали. Их наличие не обязательно. Части с заголовком испытания, test и pass необходимы. Опишем их подробнее. Начало испытания определяется "словом" *****, которое играет роль визуального маркера в скрипте. Остаток строки это название испытания. Оно должно вкратце описывать что будет тестироваться. Например ***** function List_Length: List of length zero TG нумерует испытания начиная с единицы. Вы можете нумеровать испытания в заголовке, чтобы легче было найти нужное. TG распознает номер испытания в круглых скобках в начале заголовка, например ***** (17) function List_Length: List of length zero Конечно, легко сбиться с правильного счет, поэтому есть специальный режим Test Script Mode для редактора Emacs. Если введенные цифры не соответствуют порядку TG предупреждает об этом во время трансляции. Любой код за языке Ада может быть тестовым вызовом. Но всеже лучше, если будет единственный оператор. Это позволит точно определить, что пошло не так во время теста. Результат лучше расположить в переменной, чтобы потом проверить на корректность. Например test Result := Test_Item (Some_Parameter); Может быть любое число проверок результата тестового вызова. Тест проходит если ЛЮБАЯ из проверок выполнилась. Проверка может быть записана в одной из следующих форм pass path pass predicate pass path, predicate Где path определяет путь программы. => обозначает выполнение без исключений. exception exception-name возбуждение исключения exception-name (имя исключения должно быть определено заранее). Отсутствие path равнозначно =>. predicate должно быть логическим выражением и может занимать несколько строк. Если не задано, то равно True. Если заданы и path и predicate, то тест проходит, если выполняется predicate и путь программы заданный path. Пример:
pass Number_Of_Elements = 5
pass exception Constraint_Error
pass exception IO_Exceptions.Name_Error, Analyze_Result
pass Status = True
and then Is_Empty (List)
pass =>, Max = 10.23 -- `=>' is not required here
pass => -- the simplest pass-clause
Вы можете вставлять любой код между испытаниями при помощи части code. Например, можно инициализировать пакет перед тестированием. Синтаксис прост: code lines TG заключает строки кода в блок, перехватывая исключения. Если возбуждается исключение, driver генерирует состояние ошибки. Глобальная настройка error_handling указывает будет-ли продолжаться выполнение после такого исключения. Пример:
code Init;
Put_Line ("Package initialized.");
if Tasking_Status /= Running then
Put_Line ("Tasking is off.");
end if;
Put_Line ("Now continuing/starting with the test cases.");
Синтаксис команды следующий tg [options] script_file [driver_file] В простейшем варианте tg воспринимает .ts скрипт как единственный аргумент. TG транслирует скрит в программу на Аде и сохраняет ее в файле с таким же именем и расширением .adb. Например tg demo.ts Производит файл demo.adb. Но вы можете явно указать выходной файл tg demo.ts driver.adb Опции задают вывод driver-а:
Примеры: tg -p full -f full demo.ts tg -p off demo.ts Результатом работы TG является исходный код программы на языке Ада, так называемый test driver. Вы компилируете его, собираете с тестируемым элементом и выполняете полученную программу для проведения теста. Вы можете заглянуть внутрь этого исходного кода, чтобы узнать как TG собирает ваш тест, но это не обязательно. Результат работы TG не предназначен чтобы быть легко читаемым. Если вам понадобится что-то изменить, вы должны менять исходный текст скрипта, а не сгенерированный TG текст. Однако есть некоторые внутренние функции и особенности, которые могут вам пригодится при написании тестов. Они описаны далее. Test driver генерируемый TG имеет следующую структуру:
-- заголовочный комментарий
with ...; use ...; -- из спецификаторов контекста
procedure <Имя_Скрипта> is
package Driver_Internals is
-- ...
end Driver_Internals;
-- ...
-- глобальные описания
-- ...
package body Driver_Internals is
-- ...
end Driver_Internals;
begin
-- ...
-- код испытаний
-- ...
exception
-- обработчики исключений
end <Имя_Скрипта>;
Вложенный пакет Driver_Internals содержит переменные состояния и подпрограммы для доступа к ним. Мы перечислим и расскажем о них в следующей секции. Пакет Driver_Internals, расположенный в тестовой программе экспортирует следующие описания: - function Passed return Boolean; function Failed return Boolean; Эти функции предоставляют результат текущего испытания. Вы можете использовать из в секции cleanup и последующих секциях, чтобы выбрать различные действия в зависимости от результата теста. - function Taken_Path return String; Пусть выполнения программы в последнем тесте, возвращает "=>" при отсутствии исключений, либо имя исключения если оно произошло. - function Path_Was (Path : in String) return Boolean; Функция сравнивающая путь выполнения (см выше) с данным. - Program_Terminate : exception; Это исключение возбуждается в обработчиках и перехватывается в самом конце. Вы можете возбудить его, если хотите. Другие процедуры пакета Driver_Internals предназначены только для внутреннего использования. TG преобразует скрипт ***** X = 3 define Result : Positive; test Result := Subject(3); pass exception Another_Error в следующую программу
-- Test Case (3) X = 3
declare
Result : Positive;
begin -- test case
begin -- test part
Result := Subject(3);
Driver_Internals.Set_Path ("=>");
exception
when Another_Error =>
Driver_Internals.Set_Path ("Another_Error");
when E: others =>
Driver_Internals.Set_Path (Ada.Exceptions.Exception_Name (E));
end; -- test part
begin -- result part
if Driver_Internals.Path_Was ("Another_Error") then
Driver_Internals.Test_Case_Passed := True;
Put_Line ("(3) pass.");
else
Driver_Internals.Test_Case_Passed := False;
Driver_Internals.Fail_Result := True;
Put_Line ("(3) X = 3");
Put_Line (" ...FAIL.");
Put_Line (" (" & "path `"
& Driver_Internals.Taken_Path
& "' when `Another_Error' was expected"
& ")");
end if;
exception
when Driver_Internals.Program_Terminate =>
raise;
when E: others =>
Driver_Internals.Unexpected_Error := True;
Put_Line ("ERROR: exception "
& Ada.Exceptions.Exception_Name (E)
& " raised in result part of test case 3.");
end; -- result part
end; -- test case
Допустим мы хотим оттестировать функцию Subject из пакета Under_Test. package Under_Test is Strange_Error, Another_Error, Illegal_Parameter : exception; function Subject (X : in Positive) return Positive; end Under_Test; Функция Subject должна вернуть единицу, если X = 1 и возбудить исключение Strange_Error, Another_Error или Illegal_Parameter если X равен 2, 3 или болше трех соответственно. Следующий тест описывает эту функциональность:
-- FILE: example.ts
context with Text_IO; use Text_IO;
with Under_Test; use Under_Test;
exceptions Strange_Error, Another_Error, Illegal_Parameter;
***** X = 1
define Result : Positive;
test Result := Subject(1);
pass Result = 1
***** X = 2
define Result : Positive;
test Result := Subject(2);
pass exception Strange_Error
***** X = 3
define Result : Positive;
test Result := Subject(3);
pass exception Another_Error
***** X = 4
define Result : Positive;
test Result := Subject(4);
pass exception Illegal_Parameter
***** X = Positive'Last
define Result : Positive;
test Result := Subject(Positive'Last);
pass exception Illegal_Parameter
Вы транслируете файл example.ts командой tg example.ts и получаете в результате файл example.adb. Затем вы компилируете этот файл и собираете с пакетом Under_Test. Исполнение полученной программы дает следующий результат (1) pass. (2) pass. (3) pass. (4) pass. (5) pass. Total test result: pass. Теперь предположим при исполнении test case (3) функция Subject возбуждает Illegal_Parameter. Тогда результат выполнения будет
(1) pass.
(2) pass.
(3) X = 3
...FAIL.
(path `Illegal_Parameter' when `Another_Error' was expected)
(4) pass.
(5) pass.
Total test result: FAIL.
| ||||||