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, другие части не являются обязательными.
Напомним, что ключевые слова должны начинаться с первой позиции строки.
context with Ada.Text_IO; use Ada.Text_IO;
хотя можно использовать и свои процедуры с теми же именами.
define Exit_Status : Integer; -- used by all the test cases function Result_Is_Correct (R : Result_Type) : Boolean is begin ... end Result_Is_Correct;
Каждое описание испытания описывает одно испытание. Для 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‐а:
-p setting Определяет как отображаются тесты, которые прошли. off без вывода. numbers - номер теста, затем слово PASS titles - Номер, заголовок и слово PASS full - Номер, заголовок и слово PASS и с новой строки объяснение. -f setting аналогично для провалившихся тестов По умолчанию full.
Примеры:
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.