Индустрия программирования

Тестовый пакет



4.1. Основные принципы аттестации

Исходя из общих критериев качества и требований к пакетам тестов были выбраны следующие
принципы разработки пакета аттестационных тестов для проверки компиляторов Си++ на
соответствие стандарту:
  • Набор аттестационных тестов должен покрывать весь стандарт языка
    C++.
  • Программы, входящие в пакет, должны быть независимыми в том смысле,
    что результаты компиляции или запуска любой из них не должны влиять на
    условия или результаты компиляции или запуска любой другой тестовой
    программы.
  • Каждая тестовая программа необходима для проверки соответствия между


    реализацией некоторой языковой конструкции и ее описанием в стандарте
    языка C++.

4.2. Пакет тестов

Общая структура

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

Тестовый пакет построен иерархически в соответствии со структурой текста стандарта языка C++.
Тесты, содержащиеся в определенном каталоге (включая подкаталоги), должны тестировать
языковые ситуации, описанные в соответствующей части стандарта C++.

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

Группы тестов, соответствующие частям стандарта языка С++

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

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

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

Спецификации тестов: создание и реализация

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

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

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

Создание спецификаций А-тестов: атрибуты тестов и таблицы решений

Спецификации для А-тестов выражены в виде таблиц решений для наборов тестовых
атрибутов.

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


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

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

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

Несмотря на эти замечания, разработка спецификаций для А-тестов включает в себя следующие
четыре шага:

  1. Определяется набор тестовых атрибутов. Атрибут теста - это любой атрибут или свойство
    языковой конструкции или выражения, встречающиеся в рассматриваемой части стандарта, или
    любое свойство контекста данной конструкции, которое может рассматриваться как предмет
    отдельного тестирования.
  2. Для каждого тестового атрибута определяется набор возможных значений. Каждое значение
    должно удовлетворять следующим двум требованиям:
    • значение присутствует в стандарте,
    • значение определяет отдельную тестируемую сущность.

  3. Определяется набор эффектов, получаемых при компиляции. Каждый эффект должен
    удовлетворять следующим двум требованиям:
    • существует возможность определения наличия или отсутствие данного
      эффекта,
    • данный эффект должен быть как можно проще.

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


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

Создание спецификаций для Б-тестов: "Б-значения" и "Ограничения"

Спецификации для Б-тестов и методы их создания отличаются от соответствующих методов для А-
тестов. Каждая такая спецификация состоит из двух частей.

Первая часть, называемая "Б-значения", состоит из набора ошибочных значений тестовых
атрибутов, определенных в соответствующей спецификации А-тестов. Каждое из ошибочных
значений должно удовлетворять следующим свойствам:

  • значение присутствует в стандарте языка С++,
  • значение определять отдельную тестируемую сущность.

Вторая часть называется "Ограничения" и состоит из формулировок ограничений, накладываемых
на программы языка С++ и явно выделенных в соответствующей части стандарта или логически
следующих из ее текста. Для некоторых ограничений явно указывается пути их нарушения.

Каждое ограничение, накладываемое на программы языка С++, может быть отражено, как в части
"Б-значения", так и в части "Ограничения", но не в обеих сразу.

Спецификация Б-тестов не предполагала создания таблиц решений, и единственным их эффектом
являлась диагностика в процессе компиляции. Это означает, что Б-тесты являются тестами этапа
компиляции. По крайней мере один тест был создан для каждого неправильного значения
тестовых атрибутов. Аналогично, для каждого ограничения был создан по крайней мере один
тест.

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

Гипотезы

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

С другой стороны, принципы и результаты оценки важности атрибутов должны быть хорошо


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

Для того, чтобы объяснить способ, которым оценивалась величина важности значений атрибутов,
необходимо явно сформулировать гипотезы о тестируемых свойствах реализации языка С++. Одна
из таких гипотез, называемая "Гипотеза об относительной корректности реализации", относится
ко всему пакету тестов. Эта гипотеза, называемая в дальнейшем ГОКР, формулируется так: "Когда
проверяется взаимодействие между различными языковыми конструкциями, предполагается, что
каждая из этих конструкций реализована правильно. Отсюда вытекает, что конкретное
представление данных конструкций не важно".

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

Поддержка версий

Пакет тестов строится и изменяется методом "от версии к версии". Любая конечная версия пакета
тестов является результатом точного последовательного выполнения следующих шагов:

  1. Строится и документиpуется список различий между стандартами C и C ++. Каждое
    различие снабжается соответствующей ссылкой на стандарт языка C++.
  2. Для каждого различия выполняется следующая последовательность шагов:
    • Каждое различие анализируется, чтобы определить может ли оно быть
      основой для аттестационных тестов. Возможно, что некоторые различия
      являются следствиями других различий или имеют информационное и
      технологическое, а не нормативное значение.
    • Для каждого различия, отбираемого, как основа для аттестационного
      тестирования, определяется набор разделов стандарта C++ соответствующий
      рассматриваемой языковой ситуации.
    • Для каждого раздела стандарта C++ определяются атрибуты тестов и
      соответствующие значения, а затем создается таблица решений. Эти действия
      для различных разделов могут быть выполнены независимо.



  3. Если в течение выполнения шага 2. найдены новые различия между C и C++, они
    объединяются в отдельный список различий. Этот список может быть добавлен к первоначальному
    списку различий и тогда процесс создания набора тестов должен быть повторно начат с шага 2.,
    или он может сохраняться как отдельный список, который нужно использовать при создании
    следующей версии набора тестов. Выбор между этими альтернативами зависит от большого
    количества неформальных причин, включая доступные ресурсы и значимости данных различий.
  4. На основе спецификаций, полученных в течение шага 3, создаются тесты. Для
    дополнительного сокращения числа тестов могут использоваться формальные и неформальные
    методы. (То есть тесты создаются только для выбранных, а не для всех спецификаций) Этот шаг
    может быть повторен несколько раз для той же самой версии набора тестов (и, поэтому, для того
    же самого набора спецификации); каждый раз невыбранные спецификации выбираются, чтобы
    получить более полную версию пакета тестов.

4.3. Свойства тестов

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

Классы тестов

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

А-тесты:

Тест относится к классу А-тестов, если он является правильной выполнимой (в соответствии со
стандартом языка C++) программой. А-тесты проверяют способность реализации обрабатывать
правильные с точки зрения стандарта программы правильным образом. А-тест считается успешно
завершенным, если его компиляция прошла успешно и вызов получившегося кода прошел без
ошибок. причем ошибками в последнем случае являются не только ошибки, выданные
соответствующей реализацией, но и сообщения самой программы о том, что данная реализация
обработала ее не соответствующем стандарту способом.

Б-тесты:

Тест относится к классу Б-тестов если он является неправильной (нарушающей требования
стандарта языка C++) программой.


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

Полученные тесты являются компиляционными тестами, содержащими нарушения стандарта,
проверяемые на этапе компиляции. Это ограничение основано на следующих допущениях:

  1. Для любого ограничения, налагаемого стандартом языка C++, которое может быть
    проверенно на этапе компиляции, и для любой реализации компилятора с языка C++, есть только
    два случая: либо реализация способна обнаружить нарушения этого ограничения во время
    компиляции, либо получается потенциально исполнимый код, содержащий это нарушение.
    Последний случай считается несоответствием стандарту C++, даже если в процессе выполнения
    программы появились сообщения об ошибках (ошибках исполнения кода программы). Ситуация,
    при которой реализация проверяет данные ограничения в процессе выполнения программы,
    кажется практически невероятной.
  2. Даже если реализация сама способна исправить ошибки программы, она должна выдать
    сообщения о них, и конечный код должен отличаться от кода, полученного в ситуации, когда
    ошибок нет.
  3. Все исключительные ситуации (как вызываемые так и обрабатываемые) могут быть проверены
    с помощью А-тестов. Соответствующие тесты должны содержать соответствующую функцию
    обработки исключительной ситуации, и во всем другом быть обычными А-тестами.
  4. Для других ошибок этапа выполнения (которые не сводятся к обработке исключительных
    ситуаций) стандарт языка C++ не содержит ограничений (касающихся поведения реализации),
    которые позволяют написать аттестационные тесты.

Общие требования к тестам

Основные требования, предъявляемые к тестам, следующие:

  1. Необходим отдельный тест для каждой спецификации. Однако, выяснено, что это
    требование не может быть выполнено и, следовательно, должно стать целью. Основными
    причинами этого стали:
    • На этапе проектирования пакета тестов трудно оценить человеческие и


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

  2. Стандарты написания тестов должны ограничивать использование некоторых языковых
    конструкций, во всех случаях кроме случая, когда данная конструкция является тестируемой.
    Каждый тест должен быть написан как можно проще и короче.
  3. Тесты должны быть просмотрены и отлажены для того, чтобы устранить ошибки и установить
    соответствие между тестами и целями тестирования.

Специальные Требования к А-тестам

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

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

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

Специальные Требования к Б-тестам

  1. Каждый Б-тест должен содержать ровно одну ошибку, соответствующую спецификации
    теста.
  2. Все Б-тесты в пакете тестов являются тестами процесса компиляции. (То есть, тест прошел если
    его компиляция не удалась, и наоборот) Это предположение не полностью правильно, но оно
    основано на общепринятой модели компилятора для языка фон Неймана и позволяет
    организовывать естественную и удобную среду тестирования для B-tests.


Содержание раздела