Предисловие В очередном выпуске «Трудов ИСП РАН», состоящем из двух томов, представлены статьи сотрудников и аспирантов Института, посвященные актуальным аспектам направления open source; разным областям применения технологии тестирования программного обеспечения на основе формальных спецификаций; методам композиции и декомпозиции исполняемых UMLмоделей; интеграции методов интеллектуального анализа данных, вывода на основе прецедентов и адаптивного управления; системам классификации; технологиям темпоральных и объектно-реляционных баз данных; технологии оптимистической репликации; опыту разработки операционной системы реального времени для цифрового сигнального процессора. Первый том содержит девять статей. В статье Н.В. Пакулина, А.К. Петренко и др. «Открытые стандарты и новые формы международного сотрудничества» выделяется общий принцип модели движения open source – формирование открытого сообщества. Указывается, что проекты по развитию и продвижению открытых стандартов также удовлетворяют этому принципу и представляют собой новую форму международного сотрудничества. Это утверждение иллюстрируется примерами проектов, в которых принимает участие ИСП РАН. Статья Д. В. Силакова «Текущее состояние и перспективы развития инфраструктуры LSB» посвящена рассмотрению технической стороны разработки стандарта Linux Standard Base и связанной с ним инфраструктуры. Описывается использование базы данных для хранения части информации, входящей в стандарт. Обсуждается процесс генерации на основе этих данных как непосредственно текста стандарта, так и сопутствующих объектов. Рассматриваются задачи по развитию существующей инфраструктуры, которые планируется решить в рамках совместного проекта ИСП РАН и организации Free Standards Group, под эгидой которой проводится разработка стандарта LSB. В статье С.В. Зеленова и Н.В. Пакулина представлен подход к верификации компиляторов, основанный на декомпозиции общей задачи компилятора, и продемонстрированы методы решения выделенных задач. Описываемый подход использовался при верификации различных индустриальных компиляторов и трансляторов. В статье В.В. Гингиной, С.В. Зеленова и С.А Зеленовой «Тестирование трансляторов: проблема построения оракула для генератора кода» описывается общий подход к построению автоматического оракула для тестирования генераторов кода в трансляторах текстов на формальных языках, а также предлагается инструментальная поддержка для практического использования этого подхода. Приводятся результаты практического применения описанного подхода к тестированию генератора кода транслятора описаний схем баз данных на языке SQL. 3
В статье Д.Ю.Кичигина «Об одном методе сокращения набора тестов» излагается метод сокращения набора тестов для регрессионного тестирования, заключающийся в построении модели поведения программы на наборе тестов и последующей фильтрации тестового набора с помощью построенной модели. Модель поведения программы строится в терминах последовательностей системных вызовов, совершенных программой во время своего выполнения. Статья К.А. Власова и А.С. Смачёва «Методика автоматизированной проверки возвращаемых кодов ошибок при тестировании программных интерфейсов» посвящена методам частичной автоматизации создания тестовых наборов, предназначенных для тестирования API. Описаны технические подробности реализации, возникшие проблемы и их решение. В статье В.С. Мутилина «Тестирование компонентов взаимодействующих посредством удаленного вызова методов» описывается метод тестирования компонентов, взаимодействующих посредством удаленного вызова методов. Метод позволяет гарантировать, что будут проверены все различные чередования вызовов методов в системе, приводящие к различным результатам. В работе выделены ограничения, при которых такой перебор различных порядков вызовов методов гарантирует корректность системы. В статье А.С. Камкина «Использование контрактных спецификаций для автоматизации функционального тестирования моделей аппаратного обеспечения» предлагается использовать контрактные спецификации для автоматизации функционального тестирования моделей аппаратного обеспечения, разработанных на таких языках, как VHDL, Verilog, SystemC, SystemVerilog и др. Подробно описываются особенности спецификации аппаратного обеспечения, приводится сравнение предлагаемого подхода с существующими методами спецификации, применяемыми в тестировании аппаратуры. Завершает первый том статья Д.В. Зацепина и В.З. Шнитмана «Особенности применения технологии UniTESK для тестирования функций мобильности в протоколе IPv6», посвященная разработке тестового набора для проверки соответствий реализаций мобильного узла спецификациям протокола Mobile IPv6. В ходе выполнения работы было выявлено несколько особенностей поведения одного из объектов протокола – мобильного узла, которые затрудняют его тестирование в рамках указанной технологии. В статье подробно описаны эти особенности и способы преодоления трудных моментов в условиях ограничений технологии UniTESK и поддерживающего эту технологию инструмента CTesK. Член-корреспондент РАН
4
В.П. Иванников
Открытые стандарты и новые формы международного сотрудничества1 Н.В. Пакулин, А.К. Петренко, О.Л. Петренко, А.А. Сортов, А.В. Хорошилов. Аннотация. Открытые коды (ОК), или Open Source постоянно находятся в сфере внимания специалистов ИТ-индустрии. Зримыми показателями успеха ОК является глобальное наступление ОС Linux или популярность таких международных проектов, как Википедия. В статье выделяется общий принцип модели ОК – формирование открытого сообщества. Проекты по развитию и продвижению открытых стандартов также удовлетворяют этому принципу и представляют собой новую форму международного сотрудничества, что иллюстрируется примерами проектов, в которых принимает участие ИСП РАН.
1. Введение Модель Открытого кода (Open source) – это не только соглашения о формах использования программ или других результатов творческой деятельности людей и целых организаций. Правила использования ОК – это лишь формальная сторона, задающая рамки функционирования модели. Другой, возможно, более важной стороной модели, является свободный, индивидуальный выбор участия в создании и использовании ОК. В мире ОК никому не придет в голову заставить кого бы то ни было что-то создать или что-то использовать вопреки желаниям субъекта. ОК предоставляет человеку или организации возможности участия в процессе, а субъект, соответственно, сам делает выбор, что и как он будет делать, не нарушая оговоренных правил использования ОК. Заметим, что такое партнерство сильно отличается от отношений, которые типичны для мира коммерции, где главными действующими лицами являются заказчик и исполнитель. В коммерческом мире один принимает решение, что нужно сделать (заказчик), а другой вынужден делать это (исполнитель). В то же время, в нашей реальности имеется распространенная модель совместной деятельности, где стороны не навязывают друг другу директив, а сотрудничают, соблюдая при этом свои интересы, и развиваются исходя опять же из собственных интересов. Речь идет о международных программах, в которые страны входят на добровольной основе и двигаются к общей цели, не теряя из виду и свои собственные интересы. 1
Работа частично поддержана грантами РФФИ 05-01-00999 и 06-07-89261. 7
Не удивительно, что проекты, работающие по модели ОК, часто одновременно являются международными, и наоборот, в международных проектах по созданию и/или продвижению программных платформ и новых стандартов используется модель ОК. Это объясняется тем, что модель ОК и международные программы, построенные на добровольном участии стран, по духу близки друг другу, в обоих случаях мы видим суверенных партнеров, которые пытаются достичь общих и своих собственных целей, не ущемляя интересы партнеров. Понятно, что, наряду со сходством, между проектами ОК и международными проектами, имеются и важные различия. Одной из причин таких различий служит существенно более высокая динамика проектов ОК: страны живут в соседстве веками, а проекты ОК длятся от нескольких недель до нескольких лет, но не больше. Высокая динамика означает, что партнеры должны быстро договориться не только о том, что делать, каковы цели и этапы проекта, но и о том, кто и что может и хочет делать. То есть в жизненном цикле проекта появляется существенная составляющая, которая направлена не на результат проекта, а на построение и усовершенствование процесса. При этом, как правило, нет никакого априорно предписанного процесса, а, скорее, выбирается некоторый шаблон, который может адаптироваться для конкретных условий. Акцент в проектах ОК делается на механизмы самоорганизации, которые позволяют найти процесс, приемлемый всем партнерам. Модель ОК не следует трактовать слишком узко и ограничивать ее пригодность только сферой разработки программных систем. По этой модели организуются проекты из различных областей творческой деятельности – составление словарей и энциклопедий (например, сетевая энциклопедия Wikipedia [1], математическая энциклопедия проекта PlanetMath[2]), разработка учебных курсов (например, проекты PlanetMath и PlanetPhysics[3]), живопись и графика (например, открытый проект OpenClip[4]), музыкальное творчество (см. список открытых музыкальных проектов на сайте Remix Commons [5]) и т.д. Трактовка Политики открытого кода в Википедии не ограничивается только созданием программного обеспечения: Политика открытого кода (open source politics) — принцип «распределенной» разработки, использующийся в технологии, искусстве, политике общественных организаций и сетей и, прежде всего, — в создании свободного программного обеспечения.
2. Открытый код и открытые сообщества Особенность проектов по созданию открытого кода заключается в том, что эти проекты выполняются открытым сообществом. Что такое открытое сообщество? Корпорация Microsoft распространяет исходные тексты стандартной библиотеки языка Си вместе со средой разработки MS Visual Studio, и каждый 8
пользователь Visual Studio может легко с ними ознакомиться. Делает ли это стандартную библиотеку языка Си корпорации Microsoft открытым кодом? Нет, потому что у пользователей нет возможности организовать сообщество для развития этой библиотеки. Открытое сообщество строится вокруг информационного ресурса. Это может быть сервер с исходными кодами некоторой программной системы, виртуальная энциклопедия или база стандартов. Люди объединяются в сообщество с целью развивать информационный ресурс – разрабатывать программу, писать и редактировать статьи в энциклопедии или разрабатывать или уточнять спецификации некоторого стандарта. Мы будем называть сообщество открытым, если оно удовлетворяет следующим требованиям: 1. У всех членов сообщества есть доступ к информационному ресурсу, над которым ведётся работа. 2. Сообщество ведёт коммуникацию через доступные извне каналы общения – дискуссионные листы, форумы. 3. Известны правила, которые регулируют отношения людей в сообществе, причём для того, чтобы узнать эти правила, не требуется вступать в сообщество. 4. Для вступления и участия в работе сообщества не требуется выплачивать лицензионные отчисления. 5. Участие в сообществе не ограничивает социальных свобод (в частности, не требуется подписка о неразглашении коммерческой или иной тайны). Участие в открытом сообществе налагает на человека определённые обязательства. Открытое сообщество представляет собой принципиально новую социальную формацию по сравнению с корпоративными или исследовательскими коллективами разработчиков. Прежде всего, это высокая личная ответственность каждого за качество и объём работы. Кроме того, проекты ведутся совместно несколькими независимыми разработчиками, и результат их работы является совместной интеллектуальной собственностью. В условиях современного общества доступ к информационному ресурсу и интеллектуальной собственности определяется юридическим соглашением между пользователем ресурса и его поставщиком или его создателями. В мире ОК роль такого соглашения выполняет лицензия, определяющая правообладателя ресурса или его отдельных частей и регламентирующая условия использования ресурса. Условия лицензии играют ключевую роль в формировании открытого сообщества. Лицензии, запрещающие изменения или требующие сокрытия знаний об устройстве программы, разрушают всякую возможность создать открытое сообщество. И напротив, если лицензия позволяет изменять и дополнять исходные тексты программы, открыто 9
обсуждать архитектуру и детали реализации, то она создаёт достаточные предпосылки, чтобы вокруг программы возникло открытое сообщество. При первом знакомстве с понятием открытых кодов у многих возникает впечатление, что с такими работами можно делать всё, что заблагорассудится – использовать, продавать, изменять. Такая возможность есть в том случае, когда результаты работы передаются в общественное достояние (public domain), однако немногие из тех, кто задействован в работе с ОК, согласны передавать свои работы в общественное достояние с добровольным отказом от своего авторства. Но обозначение авторства на результатах работы, в частности, на исходных текстах программ, влечёт множество дополнительных вопросов, связанных с ответственностью за применение работы автора, имущественными отношениями и просто сохранением формального обозначения авторства в производных работах. В движении открытых кодов нет окончательного понимания того, какие ограничения, налагаемые лицензией, позволяют создать открытое сообщество вокруг защищаемых ею информационных ресурсов. Причина, возможно, заключается в том, что к движению открытых кодов постоянно присоединяются новые игроки – крупные компании и корпорации – со своими юридическими отделами, которые «изобретают» свои лицензии для защиты открываемых исходников. Апологеты ОК сформулировали ряд требований к лицензии, с которыми согласно большинство участников движения открытых кодов (но не все!). Эти требования опубликованы на сайте Open Source Initiative [6], здесь мы приведём их в сокращённом виде: лицензия должна обеспечить свободное распространение, доступ и модификацию открытых кодов, и не должна содержать положений, ущемляющих права отдельных категорий пользователей. Наибольшей популярностью в настоящее время пользуются четыре лицензии: Gnu Public License (GPL) [7], Lesser Gnu Public License [8], Apache License (AL) [9] и BSD License [10]. На сайтах Free Software Foundation [11] и Open Source Initiative [12] приведены перечни лицензий, которые составители сочли удовлетворяющими требованиям открытых кодов. Эти лицензии используются не только для компьютерных программ, но и для документации, книг и даже произведений искусства. Помимо лицензии, на формирование сообщества оказывают влияние правила, регулирующие отношения между участниками. В частности, правила могут описывать процедуру добавления новых членов в сообщество, организационную структуру и механизмы принятия решений. Эти правила могут быть закреплены формально, например, в уставе некоторого консорциума или могут входить в состав неформальной культуры сообщества. Последний вариант наиболее широко распространён. Следование принципам открытых проектов, в особенности, принципу «открытых правил» требует совершенного нового стиля организации рабочего процесса в открытых проектах. Смена привычных стилей управления 10
происходит постепенно, и во многих открытых проектах можно наблюдать те или иные рудименты закрытых методов управления. Например, в проекте по созданию ядра ОС Linux право записи в дерево исходных текстов предоставляется только тем, у кого «есть приемлемый вклад в ядро Linux и достаточное основание в получении непосредственного доступа» [13]. Объём «приемлемого вклада» и «достаточность основания» определяются здравым смыслом администраторов проекта kernel.org, причём они разрешают себе отказывать в выдаче такого разрешения или отмене уже выданного разрешения без объяснения причин. Центр стандартизации Интернета (Internet Engineering Task Force, IETF) [14] вот уже на протяжении 25 лет является главным органом разработки стандартов технологий, поддерживающих сеть Интернет. В частности, именно в IETF приняты стандарты на стек протоколов TCP/IP, протоколы FTP, HTTP, SIP, TELNET, системы обеспечения безопасности в сети Интернет. Центр стандартизации Интернета является примером организации, в которой уже сложилась полностью открытая структура управления – разработаны и зафиксированы правила рабочего процесса, управления рабочим процессом, публикации и отзыва стандартов. Необходимо отметить, что эта структура складывалась на протяжении более чем 20 лет, причём работы по дальнейшему развитию и совершенствованию механизмов управления открытыми проектами в IETF не прекращаются. Открытые сообщества складываются из добровольцев, которые зачастую не имеют материальной заинтересованности в результатах проекта. В силу этой особенности открытых сообществ приказные механизмы управления, как правило, не эффективны – несогласные просто уйдут и приложат свои силы в других проектах. Поэтому в открытых сообществах обязательно возникают элементы самоорганизации, которые позволяют вырабатывать организационные формы, приемлемые для всех участников. В частности, распространены коллегиальные механизмы принятия решений, выборный характер руководства проекта. В соответствии с историей возникновения открытые сообщества можно разделить на две группы – те, что «возникли сами», и те, что «были созданы». К первой группе мы причисляем сообщества, возникшие вокруг небольшой инициативной группы или даже одного человека для создания или развития информационного ресурса. Примером может служить сообщество разработчиков ядра ОС Linux, которое выросло из инициативной группы, поддержавшей в начале 1990-х Линуса Торвальдса в его работе над новой операционной системой для персональных компьютеров. В таких проектах выделяется группа наиболее активных и уважаемых разработчиков, которые зарекомендовали себя в ходе проекта. Они наделяются полномочиями управлять проектом – принимать или отклонять изменения, вносимые в информационный ресурс, планировать разработку и т.д. Важной чертой этих проектов является то, что такая руководящая группа образуется как бы «сама 11
по себе», зачастую с использованием демократических механизмов, таких как выдвижение кандидатов и голосование сообщества. Ко второй группе мы относим сообщества, которые развиваются по принципам открытых сообществ, но изначально были созданы общественными или коммерческими организациями. Начиная с 1998 года, когда компания Netscape открыла исходные тексты своего браузера, всё больше компаний и корпораций передают свои ранее закрытые разработки в мир открытых кодов. При этом компании, как правило, учреждают некоммерческие общественные комитеты для управления открытым проектом. Эти организации выполняют те же самые функции, что и управляющие группы в проектах из первой группы, состав комитета также может формироваться на демократических принципах, но начальный состав комитета определяется компанией-основателем проекта.
3. Основные принципы проектов по разработке и продвижению открытых стандартов Разработка программного обеспечения – это самая распространенная область применения модели ОК, но и здесь есть неожиданные «открытия». Одним из них служит разработка и продвижение открытых стандартов интерфейсов программных и аппаратных систем. Заметим, что текст стандарта (часто говорят «спецификация») больше похож на книгу, чем на программу. Совместная работа над книгой – это совсем другой процесс по сравнению с процессом создания программной системы. Еще меньше сходства между процессами создания ПО и процессами продвижения стандарта. Продвижение стандарта больше похоже на программы пропаганды и агитации в политике или на программы развития культурных связей или распространения знаний. Разработка и продвижение стандартов обладают рядом особенностей, обусловленных природой стандартов. 1. Стандартизацией занимаются не столько отдельные люди, сколько целые организации, поскольку реализация стандарта – это задача, требующая больших ресурсов и, как правило, непосильная для одного человека. 2. В стандартизации часто бывают заинтересованы индустриальные компании. В таком случае в состав группы, разрабатывающей стандарт, делегируются представители компаний. Они получают зарплату за участие в работе над стандартом и отстаивают в стандарте интересы компании-работодателя. 3. Стандарты могут затрагивать национальные интересы отдельных государств, поэтому многие международные органы стандартизации, в том числе и те, что ведут открытые проекты стандартизации, поддерживают сеть базовых организаций или региональных представительств в различных странах. Посредством базовых организаций решаются 12
вопросы коммуникации с правительствами и адаптации стандарта и методов его продвижения к особенностям соответствующих стран. В этом проекты по стандартизации отличаются от большинства проектов ОК, которые являются в чистом виде интернациональными проектами без привязок к национальным структурам. Разработка стандартов также имеет ряд отличий от типичных проектов ОК, направленных на создание программ. Программы разрабатываются инкрементально, небольшими кусками с частыми обновлениями исходных текстов. Проектная документация создаётся редко, даже пользовательская документация на программы, разработанные в проектах ОК, редко существует в полном объёме. Не всегда серьёзное внимание уделяется тестам, которые проверяют функциональную корректность программ. Стандарты разрабатываются иначе. К ним предъявляются жёсткие требования (полнота, непротиворечивость, понятность, однозначность), поэтому изменения в стандарты вносятся, как правило, сразу большими частями, чтобы обеспечить высокое качество стандарта. Стандарты могут содержать отдельные части или главы, поясняющие их назначение и основные идеи, что аналогично проектной документации для программного обеспечения. Тестирование стандарта заключается в разработке реализаций-прототипов, что само по себе может быть гораздо сложнее, чем тестирование программ в проектах ОК. Немаловажной частью продвижения стандарта является задача создания тестовых наборов для проверки соответствия стандарту. Эта задача лежит на стыке областей стандартизации и разработки программных средств. От обычных проектов по созданию ПО разработка тестовых наборов отличается, прежде всего, более глубокой проработкой требований, тщательной реализацией тестового набора, использованием специализированных средств разработки. Тем не менее, несмотря на указанные особенности, мы считаем, что разработку и продвижение открытых стандартов тоже можно рассматривать в русле глобального движения Открытых кодов. Тому есть несколько оснований: 1. Открытость проекта для включения новых участников. Создание и продвижение открытых стандартов осуществляется некоммерческими консорциумами или публичными форумами, которые не взимают плату за участие в работе сообщества. Участие в консорциуме или форуме происходит на добровольной основе. 2. Открытость результатов для ознакомления. Разработанные стандарты, тестовые наборы, демонстрационные и агитационные материалы выкладываются в открытый доступ в сети Интернет, за скачивание и использование результатов проектов не взимается плата. 3. Открытость результатов для использования. Разработанные открытые стандарты можно реализовать без выплаты лицензионных платежей. 13
Можно самостоятельно проводить акции и кампании по продвижению стандарта, для этого не требуется получать специальные разрешения и платить лицензионные отчисления. Открытые сообщества стандартизации демонстрируют одно существенное отличие от закрытых органов стандартизации – они могут начинать процессы стандартизации для предложений, внесённых людьми или организациями, не входящими в сообщество. Это позволяет, в частности, оперативно стандартизировать технологии, разработанные в открытых программных проектах. Продвижение стандартов требует вовлечения в процесс разных групп людей – чиновников, разработчиков программ и конечных пользователей этих программ. Для наибольшего эффекта необходимо сочетать различные активные методы обучения и вовлечения в процесс использования стандарта:
Проведение семинаров по популяризации стандартов, в том числе таких, на которых пользователи смогут попробовать прототипы систем с новыми технологиями.
Распространение бесплатных материалов, а также выступления на конференциях, учебные курсы в университетах и т.п.
Active tutorials – последовательное освоение новой технологии на практических примерах под руководством преподавателей и тренеров.
В начале 2000 года Европейский институт стандартизации телекоммуникаций предложил новую форму продвижения и развития стандартов, которая получила название Plugtests [15]. Это сравнительно новое изобретение в области развития и продвижения стандартов, поэтому мы рассмотрим более подробно. Изначально идея Plugtests заключалась в том, чтобы проверить новые технологии (как правило, в телекоммуникационной сфере – Bluetooth, WiFi, IPv6 и т.д.) на практике. Представители различных компаний-производителей собирались в одном месте и привозили прототипы или уже запущенные в серию устройства. Далее на протяжении нескольких дней они проигрывали различные сценарии использования новой технологии, используя при этом устройства разных компаний. Фактически, они проверяли совместимость устройств между собой. По этой причине Plugtests позиционируются как «Interoperability Events». В последнее время для многих сравнительно новых технологий появились более-менее полные тестовые наборы, что привело к появлению на Plugtests новых действующих лиц – производителей тестовых наборов и новых сценариев работы – прогона тестового набора на образцах продукции представителей промышленных компаний. У этого сценария есть два аспекта: производители проверяют соответствие своей продукции стандартам, а разработчики тестовых наборов проверяют корректность своего тестового набора. 14
Помимо непосредственных участников Plugtests – как промышленников, так и тестировщиков, в этих мероприятиях заинтересована ещё одна категория лиц – авторы стандарта или спецификации новой технологии. На Plugtests приезжают, как правило, инженеры-разработчики, которые досконально разбираются в соответствующих стандартах и имеют опыт их практической реализации, поэтому они могут дать очень ценные отзывы о качестве стандарта, ошибках или неточностях, обнаруженных в ходе работы. Plugtests позволяют за сравнительно небольшую плату (или вовсе бесплатно) провести «живое» тестирование реализаций новых технологий в различных ситуациях, проверить совместимость продукции конкурентов до начала официальных продаж, «обкатать» новые тестовые наборы и провести тестирование соответствия новых реализаций. Помимо этого, Plugtests дают возможность экспертам-практикам и разработчикам стандартов встречаться и обмениваться идеями, прояснять непонятные места в стандарте.
4. Международное сотрудничество открытых стандартов
в
области
4.1. The Austin Group Проблемы обеспечения корректного взаимодействия программных систем от различных производителей актуальны не только в области телекоммуникаций. Аналогичные проблемы возникают также на уровне взаимодействия прикладных программ с операционной системой. Операционные системы одного семейства предоставляют приложениям почти одинаковые сервисы, но это «почти» стоит множества бессонных ночей независимым разработчикам приложений. Так, в 80-х годах XX века данная проблема крайне остро встала среди сообщества пользователей семейства операционных систем UNIX. Разнообразные вариации UNIX-систем обладали одинаковой архитектурой, базировались на общих принципах и имели практически одинаковые программные интерфейсы, но отличались в многочисленных деталях. В результате разработка переносимых приложений являлась настоящим испытанием и требовала от разработчиков приложений огромных финансовых вложений. Стандартизация интерфейса между прикладными программами и операционной системой появилось как естественное решение данной проблемы. Предпринималось множество попыток стандартизации программного интерфейса операционных систем семейства UNIX, но наибольшее признание получил стандарт POSIX (Portable Operating System Interface for Computing Environment). Работа над этим стандартом началась в 1984 году в рамках образованного под эгидой IEEE CS комитета по стандартам для переносимых приложений (Portable Application Standards Committee, PASC). 15
Первая версия стандарта POSIX, имевшая официальное название IEEE Std 1003.1, вышла в 1988 году. Она содержала требования к функциям прикладного интерфейса операционной системы на языке программирования C. В течение последующих нескольких лет стандарт немного доработали, устранили выявленные неточности и в 1990 году выпустили вторую редакцию IEEE Std 1003.1-1990, которая была принята в качестве международного стандарта ISO/IEC 9945-1:1990. Затем основная работа PASC проводилась в подкомитетах и заключалась в разработке дополнений к базовому стандарту. Этот этап развития стандарта POSIX завершился выпуском очередной редакции IEEE Std 1003.1-1996, в которую вошли дополнения IEEE Std 1003.1b-1993 (Realtime Extension), IEEE Std 1003.1c-1995 (Threads) и IEEE Std 1003.1i-1995 (Technical Corrigenda to Realtime Extension). Эта редакция также была утверждена в качестве международного стандарта ISO/IEC 9945_1:1996. Среди других организаций на поле стандартизации UNIX-подобных систем следует выделить The Open Group, образованную в 1993 году посредством слияния X/Open и Open Software Foundation (OSF). В том же году The Open Group получила от Novell права на торговую марку UNIX и начала работу над единой спецификацией UNIX (Single UNIX Specification, SUS). Эта спецификация включала в себя стандарт POSIX и дополняла его достаточно большим набором функций, являющихся устоявшейся практикой среди UNIXподобных систем. Ключевое событие в истории стандартизации UNIX-подобных систем произошло в сентябре 1998 году, когда была образована The Austin Group – рабочая группа по развитию совместного стандарта IEEE Std 1003, ISO/IEC 9945 и Single UNIX Specification. Результатом работы этой группы стал выпуск новой, единой версии всех этих стандартов IEEE Std 1003.1-2001, ISO/IEC 9945:2001 и базовой части Single UNIX Specification version 3. Наиболее важным новшеством в принципах работы The Austin Group стал переход к открытым методам развития стандарта POSIX. Представитель любой коммерческой компании или учебного заведения и любое частное лицо может стать членом The Austin Group и принимать активное участие в процессе стандартизации. Единственное, что для этого требуется сделать – это подписаться на электронный список рассылки The Austin Group [16]. В этом списке рассылки и происходит основная работа группы. Среди обсуждаемых в нем вопросов следует выделить следующие темы: интерпретация требований стандарта и ответы читателям относительно непонятных мест стандарта; обсуждение предложений по уточнению текста стандарта; обсуждение предложений по развитию стандарта (стандартизации новой функциональности, удалению устаревшей и т.д.) 16
В ходе обсуждений участники группы пытаются прийти к соглашению по каждому вопросу и сформировать окончательное решение. Это решение обычно утверждается в ходе телеконференций группы, проходящих на регулярной основе раз в одну-две недели. Протоколы телеконференций публикуются в списке рассылки, что делает их доступными всем участникам. Важную роль в деятельности The Austin Group играет председатель группы (в настоящее время – Andrew Josey из The Open Group). Он отвечает за организацию жизнедеятельности группы, проведение телеконференций, подготовку протоколов и других документов группы. В сфере его ответственности также лежит организация взаимодействия группы с формальными органами ISO и IEEE, необходимого для принятия обновленных версий стандарта как официальных международных стандартов. Текст стандарта Single UNIX Specification является общедоступным на сайте The Open Group [17]. Тексты стандартов IEEE Std 1003 (POSIX) и ISO/IEC 9945 распространяются согласно традиционным принципам распространения стандартов этих организаций. Но так как IEEE Std 1003 (POSIX), ISO/IEC 9945 и Single UNIX Specification являются единым стандартом, то недоступность текстов с логотипами IEEE и ISO/IEC не сильно ограничивает возможности пользователей. Открытость The Austin Group для всех заинтересованных сторон влечет за собой множество преимуществ. Свободная доступность текста стандарта ведет к большему признанию и распространению стандарта в среде разработчиков приложений для UNIX-подобных систем. Большее количество читателей стандарта приводит к более быстрому обнаружению скрытых проблем в стандарте, неочевидных непосредственно рабочей группе. Возможность свободного участия разработчиков операционных систем и разработчиков приложений в обсуждениях рабочей группы по интерпретации требований стандарта и по путям его дальнейшего развития позволяет более полно учитывать мнения различных сторон и скорее находить общепризнанные решения.
4.2. Free Standards Group Стандартизация прикладного программного интерфейса UNIX-подобных операционных систем стала большим шагом вперед на пути достижения переносимости приложений. Стандарт POSIX описывал сервисы, предоставляемые операционной системой, на уровне прикладных программных интерфейсов языка программирования Си. Такой подход никак не ограничивал свободу поставщиков операционных систем по выбору архитектуры системы и способам ее реализации. Поэтому практически все операционные системы, в том числе и не являющиеся наследниками UNIX, в той или иной степени стали поддерживать стандарт POSIX. Одна из наиболее распространенных UNIX-подобных операционных систем – операционная система Linux с самого начала являлась POSIX-совместимой за 17
исключением небольшого числа отдельных несоответствий. Тем не менее, Linux не сумела избежать проблем с переносимостью приложений и фрагментацией рынка. Недостатки POSIX применительно к рассматриваемой ситуации заключаются в следующем. Во-первых, интерфейсов, специфицированных в POSIX, оказывается недостаточно для полноценной разработки всех видов приложений. Если системные приложения и приложения, взаимодействующие с пользователем посредством терминала, могут быть реализованы в рамках интерфейсов POSIX, то приложения с графическим пользовательским интерфейсом и приложения, использующие системную информацию, оказываются вне стандарта POSIX. Во-вторых, описание требований на уровне исходного кода требует перекомпиляции приложений для переноса приложения на другую систему. Это не является большим препятствием при распространении приложений с открытыми исходными кодами, но существенно осложняет распространение приложений в двоичном виде. В последнем случае разработчику для сборки своего приложения требуется иметь доступ ко всем поддерживаемым дистрибутивам. Нелегко приходится и конечному пользователю. Так как среди поставщиков операционной системы Linux нет устоявшейся традиции поддерживать обратную двоичную совместимость компонентов, то любое обновление системы может привести к потере работоспособности отдельных приложений. И даже в тех случаях, когда приложение поставляется с исходными кодами, и пользователю для восстановления его работоспособности требуется только выполнить перекомпиляцию приложения, такая ситуация не приносит пользователю ничего, кроме дополнительной головной боли. По этим причинам сообщество Linux инициировало работу по стандартизации базовых интерфейсов операционной системы на бинарном уровне. Для этого в 1998 году была образована некоммерческая организация Free Standards Group, под эгидой которой началась разработка стандарта Linux Standard Base (LSB). Деятельность по созданию стандарта поддержали Линус Торвальдс, Брюс Перенс, Эрик Раймонд, несколько поставщиков дистрибутивов Linux, разработчики приложений, члены Linux International, а также Джон Хаббард из проекта FreeBSD [18]. Работа Free Standards Group по разработке стандарта LSB следует духу открытости, принятому в среде программного обеспечения с открытым кодом, каким является ОС Linux. В этом вопросе LSB Workgroup (рабочая группа по разработке стандарта LSB) не ограничивается открытостью процесса работы рабочей группы и текста стандарта. В отличие от The Austin Group, в LSB Workgroup открыты все внутренние средства и инструменты по разработке стандарта, а также тестовые наборы для проверки на соответствие. Стандарт LSB генерируется на основе информации о составе интерфейсов, хранящейся в базе данных, и описаний этих интерфейсов из отдельных 18
SGML-файлов. На основе этой же базы данных генерируются дополнительные инструменты, упрощающие разработку приложений на основе стандарта. И база данных, и скрипты, участвующие в генерации, доступны всем на сайте Free Standards Group. Рабочий процесс LSB Workgroup имеет очень много общего с процессом The Austin Group. Обсуждения основных вопросов ведутся в списке рассылки рабочей группы [19], еженедельно проводятся телеконференции, на которых обсуждаются ключевые вопросы. Два раза в год проводятся двух-трехдневные личные встречи участников рабочей группы, посвященные вопросам развития стандарта. Отдельно можно выделить процесс обработки замечаний к стандарту, который выглядит следующим образом. По специальному шаблону оформляется и заносится в базу сообщение, описывающее суть замечания к стандарту. Ответственный за обработку замечаний (в настоящее время момент это Mats Wichmann – сотрудник Intel, экс-глава LSB Workgroup) принимает сообщение на обработку или же задает уточняющие вопросы, если информации в сообщении недостаточно. Помимо вопросов к инициатору сообщения, могут задаваться вопросы другим заинтересованным лицам, получившееся в результате обсуждение также записывается. Информация о зарегистрированных замечаниях появляется в рассылке рабочей группы, кроме того, периодически рассылается дайджест с информацией о последних замечаниях, дискуссиях и принятых по замечаниям действиях. Подписка на рассылки свободная, так что любой желающий может принять участие в обсуждении. После обсуждения замечания принимается решение о внесении изменений в текст стандарта и публикуется «заплатка» к базе данных, содержащая необходимые изменения, которые тоже выносятся на обсуждение. Завершающие действия по обработке замечания – внесение изменений в базу данных и фиксация этих изменений в системе конфигурационного управления – происходят после окончательного одобрения изменений (иногда молчаливого). Являясь некоммерческой организацией, Free Standards Group получает поддержку от многих крупных организаций, заинтересованных в успешном развитии операционной системы Linux. В число активных членов Free Standards Group входят все ключевые производители дистрибутивов ОС Linux (Red Hat, Novell/SuSe, Mandriva, Debian, Red Flag), компании разработчики приложений (MySQL, RealPlayer), а также такие компании, как IBM, Intel, HP, Oracle, AMD и т.д. Free Standards Group уделяет достаточно много внимания вопросам международного сотрудничества. Во всех частях света она старается организовать неформальные центры поддержки и распространения стандарта LSB. В Китае в качестве такого центра выступает China Electronics Standardization Institute (CESI), в Южной Корее – Electronics and Telecommunications Research Institute 19
(ETRI), в Сингапуре – Center for International Cooperation for Computerization (CICC), в Японии – Japan Linux Association (JLA), в России – Институт Системного Программирования РАН (ИСП РАН).
4.3. Стандартизация Linux: сотрудничество с The Austin Group и Free Standards Group Сотрудничество ИСП РАН на поле стандартизации ОС Linux началось в середине 2005 года. На первом этапе проводилась формализация требований стандарта LSB к поведению компонентов ОС Linux, а также разрабатывался систематический тестовый набор OLVER [20], проверяющий различные реализации на соответствие требованиям этого стандарта LSB. Процесс формализации требований и разработки тестового набора базировался на технологии UniTESK [21, 22]. В ходе формализации обнаруживались «дефекты» стандарта – неясные и неточные ограничения, двусмысленности, противоречия между разными частями стандарта, неполное описание функциональности и т.д. Неясные и двусмысленные формулировки стандарта обсуждались и прояснялись с разработчиками стандарта; по противоречивым и ошибочным формулировкам составлялись сообщения с предложениями по улучшению текста стандарта. Для обсуждения замечаний Free Standards Group предоставляет доступ к базе данных (для проекта выбрана Bugzilla [23] – система управления ошибками, распространяемая свободно с открытым кодом), в которой фиксируются все действия по обработке замечаний к стандарту [24]. Дискуссия, которая возникает при обсуждении замечаний, приятые решения и отчет о внесенных изменениях в текст стандарта – все это фиксируется в базе данных. В настоящее время на сайте проекта опубликовано 44 замечания к стандарту LSB [25], по которым подготовлены и отправлены сообщения. Эти замечания приняты к обработке, 23 из них уже обработаны, и соответствующие изменения будут внесены в следующие версии стандартов или отражены в документах, описывающих известные дефекты стандарта. 12 опубликованных замечаний описывают обнаруженные в стандарте опечатки – такие замечания обрабатывались и исправлялись в течение нескольких дней. 13 замечаний относятся к противоречиям в стандарте (например, константам, определяющим скорость работы терминала, в разных частях стандарта соответствовали разные значения2). 19 замечаний выявляют неточности или же ошибки в стандарте (например, при описании функции inflate() использовалась константа Z_BLOCK, которая нигде в стандарте не определялась3, или же неправильное определение констант, описывающих тип мьютекса в тексте стандарта и в заголовочных файлах4). При обработке таких 2
Зарегистрировано под номером 26 в [25], под номером 1294 в [24] Зарегистрировано под номером 267 в [25] , под номером 1518 в [24] 4 Зарегистрировано под номером 398 в [25], , под номером 1432 в [24] 20 3
замечаний часто начинались дискуссии с участием других членов рабочей группы, решения по некоторым замечаниям до сих пор не приняты. При обработке замечаний в The Austin Group база данных для фиксации обсуждений и действий не используется, обсуждение происходит в рассылке рабочей группы, а принятые решения фиксируются в официальном документе, содержащим замечания к стандарту. Этот документ находится в свободном доступе и периодически обновляется [26]. На сайте проекта опубликовано 15 замечаний к стандарту POSIX [27], об этих замечаниях сообщено в The Austin Group. Все замечания приняты, но окончательное решение по замечаниям пока не принято. Хотя исправление противоречий, неточностей и т.д. позволяют улучшить стандарт, для развития и продвижения стандарта не менее важно наличие качественного тестового набора. Только систематическая проверка соответствия ОС требованиям стандарта позволит разработчикам приложений не тратить усилия на проведение тестирования на разных платформах, если эти платформы прошли проверку на соответствие стандарту. Для обеспечения систематичности тестирования на соответствие стандарту LSB при разработке тестового набора была реализована автоматическая оценка покрытия требований, проверенных в ходе тестирования. В тестовый набор входят параметризованные спецификации и тестовые сценарии, что позволяет настраивать тестовую систему на конкретную реализацию – определять проверяемые ограничения в зависимости от конфигурационных опций (наличие некоторой функциональности, дополнительные коды ошибок и т.д.). Все это относится к требованиям, которые стандарт не фиксирует жестко, позволяя разработчикам вносить свои особенности в реализации и оставаться при этом в рамках стандарта. Разработанный тестовый набор также находится в открытом доступе на сайте проекта [20]. В ходе отладки тестового набора на разных реализациях обнаруживались отклонения реализаций от требований стандарта, а также дефекты стандарта, не замеченные на этапе анализа и разработки спецификаций. Сообщения о найденных несоответствиях поведения реализации требованиям стандарта отсылались разработчикам библиотек и дистрибутивов. Процесс обработки ошибок в реализациях в целом аналогичен процессу, применяемому при обработке замечаний к стандарту LSB. Так, например, для обработки ошибок в проекте glibc [28] также используется Bugzilla [29]. В качестве примера ошибок, обнаруженных в ходе разработки тестового набора, можно привести ошибку в текущей версии glibc (glibc-2.4): реализация функции insque() не соответствовала стандарту POSIX. Требуемая инициализация элемента очереди при обращении со значением второго параметра
NULL не была реализована, что приводило к аварийному завершению работы процесса, попытавшегося использовать функцию именно для этого5. Не всегда несоответствие, обнаруженное при тестировании, является ошибкой в реализации. Например, расхождение между требованиями стандарта LSB и поведением функции basename(), обнаруженное при тестировании, являлось следствием ошибки в стандарте LSB6. Все результаты проекта (каталог требований, разработанный в ходе анализа стандарта, спецификации и тестовые сценарии) открыто распространяются на сайте проекта по лицензии Apache 2.0. Интерес к результатам проекта был проявлен со стороны индийских и китайских разработчиков. Для обеспечения прослеживаемости требований в спецификациях используются цитаты из стандартов. Лицензия LSB позволяет цитировать текст стандарта без дополнительных ограничений, а для цитирования стандарта POSIX необходимо было получить разрешение от The Open Group, владеющего правами на текст стандарта. The Open Group дала положительную оценку деятельности ИСП РАН и предоставила все необходимые права на использование текста стандарта в каталоге требований и других компонентах тестового набора, создаваемых в ходе проекта. Взаимодействие ИСП РАН с Free Standards Group происходило более тесно. Открытость стандарта LSB позволила специалистам ИСП РАН изучить в ходе работ по проекту инфраструктуру стандарта и внести предложения по ее улучшению. В результате Free Standards Group решила привлечь ИСП РАН к работам по развитию инфраструктуры стандарта LSB и улучшению сертификационного тестового набора. На новом этапе сотрудничества ИСП РАН с Free Standards Group, намеченном на 2007 год, предполагается привлечение специалистов ИСП РАН к работам по следующим направлениям развития стандарта LSB: разрешение проблем с сертификационным тестовым набором (улучшение покрытия и удобства использования); развитие инфраструктуры и установления связей между ее компонентами (текстом стандарта, тестовыми наборами, дистрибутивами и их компонентами). Таким образом, сотрудничество с The Austin Group и Free Standards Group будет развиваться по трем направлениям – во-первых, это улучшение текста стандартов, уточнение формулировок и устранение противоречий, во-вторых, создание тестового набора, предназначенного для проверки дистрибутивов на соответствие стандарту LSB, и, наконец, развитие инфраструктуры стандарта LSB.
5
Зарегистрировано под номером 2766 в [29] Зарегистрировано под номером 207 в [25], под номером 1430 в [24] 22 6
21
Опыт сотрудничества ИСП РАН с The Austin Group и Free Standards Group продемонстрировал интересную особенность открытых проектов. Когда ИСП РАН начинал проект по разработке тестового набора, предназначенного для тестирования соответствия стандарту LSB, сообщество, образовавшееся вокруг этого проекта, практически не пересекалось с сообществами проектов The Austin Group и Free Standards Group. Только несколько человек из проекта OLVER участвовали в рабочем процессе этих сообществ. В то же время цели проекта OLVER – обеспечение высокой надежности и совместимости платформы Linux с помощью использования открытых стандартов и наукоемких технологий верификации и тестирования – во многом совпадали с целями Free Standards Group и частично пересекались с целями The Austin Group. Это привело к появлению взаимного интереса сообществ OLVER и Free Standards Group и началу активного взаимодействия. Обратная связь, полученная проектом OLVER от сообщества Free Standards Group, позволяет определить приоритетные направления развития тестового набора, а поддержка Free Standards Group помогает распространять результаты проекта OLVER в сообществе Linux. В настоящее время мы наблюдаем взаимопроникновение сообществ OLVER и Free Standards Group, которое обусловлено, в первую очередь, общностью целей этих сообществ. Такая ситуация типична для открытых проектов: открытость проектов позволяет сообществам легко проникать друг в друга, любой член одного сообщества может присоединиться к другому сообществу, если это отвечает его интересам. Если цели проектов оказываются близки, то таких людей становится достаточно много и постепенно это приводит к тому, что проекты начинают обогащать друг друга интересными идеями, наработками, решениями и т.д. При этом происходит взаимное дополнение проектов, приводящее к синергетическому эффекту.
5. Международный проект Go4IT Современная сеть Интернет ведёт своё происхождение от исследовательской сети ARPANET, созданной в конце 1970-х с целью объединения в единую сеть разрозненных исследовательских центров Министерства обороны США. Для решения этой задачи были разработаны новые сетевые технологии, которые впоследствии получили название «Интернет-протокол». Когда в 1981 году принимали стандарт Интернет-протокола, никто не предполагал, что сети на его основе станут глобальными и объединят сотни миллионов узлов. Непредвиденный взрывообразный рост Интернета, начавшийся в 1990-х и продолжающийся по сей день, выявил ряд узких мест, заложенных в Интернет-протокол при его создании, которые ограничивают функциональность глобальной сети. Для их преодоления был разработан новый комплекс сетевых технологий для замены Интернет-протокола, 23
получивший название «Интернет-протокол нового поколения» или, кратко, IPv6. Для того чтобы различать новый и старый Интернет-протоколы, последнему присвоили аббревиатуру IPv4. Наиболее существенным ограничением Интернет-протокола IPv4 является исчерпание адресного пространства. Максимальное число уникальных адресов в глобальной сети составляет около 4-х миллиардов, однако неоднородность распределения адресов по различным регионам привела к тому, что в отдельных странах общее число пользователей Интернета превысило число IPv4-адресов, выделенных стране. В частности, такая ситуация сложилась в странах Евросоюза, России, Китае, наиболее развитых странах Латинской Америки. Глобальная сеть IPv6 может функционировать только при условии корректного функционирования всех составляющих её устройств. Поэтому задача валидации и верификации как стандартов IPv6, так и их реализаций является чрезвычайно актуальной. Если принять во внимание то, что сеть Интернет охватывает весь земной шар, становится очевидной необходимость международной кооперации в этой области. В противном случае может возникнуть угроза появления несовместимых национальных сетевых стандартов, и, как результат, нарушения связности сети. В ноябре 2005 года начался международный проект Go4IT. Проект ставит перед собой цель внедрения современных методов валидации и верификации в процессы создания и разработки открытых стандартов Интернета и, в особенности, IPv6. Для достижения этой цели в проекте Go4IT решаются следующие задачи: Пропагандировать подход к обеспечению качества, основанный на тестировании соответствия стандартам, и соответствующие технологии, такие как TTCN3. Создать сообщества пользователей такого подхода в области открытых стандартов Интернета. Предоставить исполнимые тестовые наборы и инструменты тестирования в свободный доступ для верификации реализаций стандартов Интернета и, в первую очередь, IPv6. Предоставить свободную поддержку этих инструментов. Подготовить окружение для разработки недорогой и открытой тестовой платформы общего назначения. Проект Go4IT выполняется в рамках программы Европейской Комиссии «Structuring the European Research Area – FP6 – Infrastructures». Длительность проекта составит 30 месяцев, с ноября 2005 года по апрель 2008-го. Проект Go4IT объединяет 11 исследовательских и коммерческих организаций Европы, России, Китая и Бразилии для создания программных средств и предоставления услуг по тестированию реализаций IPv6. Со стороны Евросоюза в проекте участвуют: 24
Европейский институт стандартизации телекоммуникаций (ETSI) – крупный международный центр исследований и стандартизации; Национальный исследовательский институт информатики и автоматики (INRIA) – один из ведущих исследовательских центров Франции; Институт открытых взаимодействующих систем Германской Академии Наук (FOCUS); Центр коммуникационных технологий (CETECOM) – ведущая тестовая лаборатория Испании в области тестирования коммуникационных систем. Китай представлен в проекте тремя организациями: Китайская академия телекоммуникационных исследований (CATR) Министерства информационной промышленности КНР – национальный исследовательский центр и тестовая лаборатория; Университет почты и связи (BUPT) – крупнейшее учебное заведение в сфере связи и телекоммуникационных сетей в КНР; Пекинский институт Интернета – исследовательский центр крупной промышленной группы BII Group, первый провайдер в КНР, предоставивший доступ к сетям IPv6. От России в проекте принимает участие Институт системного программирования РАН. ИСП РАН ведёт активные исследования в области тестирования, валидации и верификации различных систем, включая телекоммуникационные и открытые стандарты Интернета, принимает активное участие в работе Российского форума IPv6. Рассмотрим факторы, обусловившие такой выбор стран-участниц проекта. Ведущие индустриальные державы Европы – Франция и Германия – давно исчерпали адресное пространство, выделенное им в Интернете IPv4. По заказам правительства Европейского союза (Еврокомиссии) в Европе были развёрнуты крупные исследовательские сети, в которых отрабатывались различные сценарии построения Интернета нового поколения. Неудивительно, что именно Европейские институты стали организаторами международного проекта по валидации и верификации открытых стандартов Интернета. Как и объединённая Европа, Китай и Россия являются крупными индустриальными державами, которые вплотную столкнулись с проблемами нехватки адресов для сетей, основанных на протоколе IPv4. В КНР есть государственная программа перевода внутренних сетей на IPv6, что и обусловило включение в состав консорциума сразу трёх исследовательских центров Китая. Консорциум Go4IT открыт для приёма новых участников. В отличие от многих индустриальных консорциумов и форумов, за вступление в Go4IT не взимается плата. В консорциуме предусмотрен механизм расширения через включение ассоциированных членов. Предусмотрены три степени 25
вовлечённости ассоциированных членов в работу консорциума Go4IT. Первая степень называется Форум Go4IT и предусматривает полный доступ ко всем информационным ресурсам проекта, включая внутреннюю документацию, бета-версии и исходные коды программных средств, разрабатываемых консорциумом Go4IT. Следующая ступень получила название Мастерская Go4IT и предназначена для тех новых участников консорциума, кто хочет принять участие в разработке тестовой платформы Go4IT или готовы передать в консорциум исходные тексты уже существующих программных средств. В настоящее время в Мастерскую Go4IT входят несколько китайских организаций, которые принимают участие в работах по созданию свободного компилятора TTCN3 (см. далее). Для наиболее активных ассоциированных членов консорциума предусмотрена ещё одна степень вовлечения в работу консорциума. Они могут получить приглашение принять участие в управлении консорциумом. Рабочий процесс консорциума основывается на сайте www.go4-it.eu, на котором размещаются рабочие материалы, документация, форум, исходные файлы программных модулей, разрабатываемых членами консорциума. Материалы пока не выложены в открытый доступ, но это ограничение легко преодолеть, если стать ассоциированным членом консорциума. Достаточно просто участвовать в Форуме Go4IT, чтобы получить возможность скачивать материалы с сайта консорциума. Разрабатываемые программные средства публикуются под открытыми лицензиями (GNU Public License и Apache Public License), можно свободно пользоваться материалами сайта для распространения информации о проекте Go4IT. Рабочий процесс в проекте Go4IT организован по следующей схеме. Задачи проекта разделены на две группы, Package 1 и Package 2. У каждой группы есть ведущая организация, которая координирует действия участников консорциума в рамках своей группы задач. Регулярно, один-два раза в месяц, проводятся телефонные конференции, на которых обсуждаются текущая ситуация и намечаются или уточняются ближайшие планы. Раз в полгода проводятся личные встречи представителей всех участников консорциума. К Package 1 относятся задачи разработки и распространения инструмента тестирования реализаций протокола IPv6. В рамках Package 1 ведутся работы по созданию исполнимого тестового набора для IPv6, который основывается на открытых тестовых спецификациях, опубликованных Европейским институтом стандартизации телекоммуникаций (ETSI). Эти тестовые спецификации разработаны на языке TTCN3. Авторские права на TTCN3 принадлежат ETSI, но сам стандарт опубликован в Интернете, доступен для скачивания всем желающим, и ETSI разрешает создавать трансляторы TTCN3 и средства тестирования на основе TTCN3 без выплат лицензионных отчислений. На данный момент существуют только коммерческие реализации TTCN3, и их использование в Go4IT противоречит целям и задачам консорциума – распространение тестового набора для IPv6 в исходном виде 26
потребует от пользователей приобретать средства компиляции и исполнения TTCN3. Соответственно, одна из задач, стоящих перед Package 1, заключается в том, чтобы построить исполнимый тестовый набор, в котором содержится минимальная зависимость от коммерческих средств работы с TTCN3. Эта задача решается следующим образом: участники Package 1 поставляют тестовый набор в откомпилированном виде и набор свободно распространяемых инструментов для конфигурирования, запуска тестового набора и анализа результатов. Благодаря этому, пользователям тестового набора не требуется приобретать дорогостоящие коммерческие TTCN3-системы. Решение, предлагаемое в Package 1, обладает одним существенным недостатком – тестовый набор поставляется в откомпилированном виде, поэтому пользователи лишены возможности изменять или расширять его. Для этого необходим компилятор TTCN3, а он в состав Package 1 не входит. Как показало исследование потребностей пользователей, проведённое консорциумом Go4IT, существует значительная доля потенциальных пользователей тестового набора для IPv6, которым нужны возможности по изменению или расширению тестового набора. По этой причине было принято решение создать в рамках Go4IT подпроект Package 2 для решения задач по созданию платформы для валидации и верификации открытых стандартов Интернета. В рамках Package 2 ведутся работы по разработке открытой реализации TTCN3 и подготовке на её основе платформы по тестированию телекоммуникационных систем. Бюджет консорциума Go4IT недостаточен для разработки полной TTCN3 системы, поэтому перед Package 2 поставлены следующие задачи: 1. Подготовить полный комплект технических заданий для компилятора TTCN3, библиотек времени исполнения, средств конфигурации и запуска тестов и анализа результатов. 2. Разработать прототипы компилятора и всех подсистем для проверки корректности подготовленных технических заданий. Основная доля работ Package 2 выполняется в Китае. Это объясняется, прежде всего, тем, что в КНР предполагается использовать TTCN3 в качестве основы для национального стандарта языка спецификации тестов. Проектные работы ведутся в основном европейскими участниками консорциума Go4IT, так как у них есть большой опыт работы с TTCN3. ИСП РАН принимает участие преимущественно в Package 2. Роль ИСП РАН в Package 2 заключается в валидации и верификации создаваемой платформы. В частности, задача ИСП РАН – подготовить комплект тестов для компилятора TTCN3 и ряда вспомогательных подсистем платформы. Такой выбор задач для ИСП РАН объясняется тем, что среди участников консорциума ИСП РАН обладает наибольшим опытом практического тестирования, в том числе и компиляторов. В ИСП РАН разрабатываются методы систематического тестирования различных видов систем – программных интерфейсов, 27
реализаций протоколов, компиляторов и встроенных систем. Основная идея, объединяющая эти методы, заключается в том, что тесты извлекаются автоматически из формальных моделей тестируемой системы. Помимо Package 1 и Package 2 в рамках проекта Go4IT ведутся работы по распространению знаний о проекте Go4IT, предлагаемых средствах и сервисах валидации и верификации. Это направление работ возглавляет ИСП РАН. Для обеспечения открытости необходима обратная связь как от участников проекта, так и людей, внешних по отношению к консорциуму. В Go4IT для этой цели запланирована система семинаров в России, которые организует ИСП РАН – базовая организация в проекта Go4IT в стране. Для распространения знаний существует несколько форм, используя которые ИСП РАН пытается добиться наибольшей эффективности в деле популяризации Go4IT и стандартов IPv6. Известно, что мы запоминаем 10% того, что мы читаем; 20% того, что мы слышим; 30% того, что мы видим; 50% того, что мы видим и слышим; 70% того, что мы говорим; 90% того, что мы говорим и делаем. Задача ИСП РАН построить такую систему сотрудничества, при которой каждый может занять свою активную позицию. Первоначальное знакомство российских специалистов с проектом Go4IT происходило на семинаре со следующей программой: 1. Введение в проект Go4T и Европейские рамочные программы. 2. Введение в Интернет протокол нового поколения IPv6. 3. Демонстрационная сессия прототипа тестовой платформы Go4IT. 4. Введение в язык спецификации тестов TTCN3. 5. Обзор архитектуры тестовой платформы Go4IT. 6. Круглый стол – перспективы IPv6 и Go4IT в России. Одна из сложностей в распространении Go4IT состоит в малом числе пользователей Интернет-протокола IPv6 – целевой системы тестовой платформы Go4IT. Для привлечения участников на семинар были задействованы каналы, специфичные для специалистов по сетям IPv6 – потенциальных пользователей Go4IT. Наибольший отклик дало распространение объявления о семинаре через дискуссионный лист российского форума IPv6. При распространении Go4IT особое внимание необходимо уделить практическому применению тестовой платформы. С этой целью в состав материалов семинара был включён прототип тестовой платформы для ознакомления с новыми технологиями тестирования. Другая форма адаптации программы к реальным потребностям пользователей – проведение Plugtests. В апреле 2007 г. планируется провести в России IPv6 Plugtests, на котором любой производитель или владелец оборудования IPv6 сможет проверить соответствие своего оборудования или программной системы набору базовых 28
стандартов IPv6 и, тем самым, познакомиться и испытать «на себе» тестовую платформу Go4IT.
6. Заключение Международные проекты, построенные по модели открытого кода – это новая форма сотрудничества людей, организаций и стран, которая значительно отличается от видов отношений, традиционных для проектов по созданию коммерческого программного обеспечения. Проекты ОК выполняются сообществом добровольцев, и можно констатировать, что к настоящему времени сложились основные принципы организации таких сообществ. В этой статье мы выделили несколько признаков открытого сообщества, которые позволяют вести открытые проекты и добиваться в них значительных результатов. Процессы разработки и продвижения открытых стандартов интерфейсов программных и аппаратных систем мало похожи на процессы создания программного обеспечения. Работа над стандартом больше похожа на написание книги, а продвижение стандарта напоминает программы пропаганды и агитации в политике или программы развития культурных связей и распространения знаний. Тем не менее, проекты по разработке и продвижению открытых стандартов удовлетворяют ключевым свойствам модели открытых кодов: открытость проекта для включения новых участников, открытость результатов для ознакомления, открытость результатов для использования. Такие проекты подчиняются тем же законам открытых сообществ, что проекты по созданию открытых программ. Опыт ИСП РАН показывает, что вхождение в открытые самоорганизующиеся сообщества крупной исследовательской структуры, такой как институт РАН, не составляет труда. При этом, однако, необходимо учитывать специфику и общие принципы развития открытых сообществ, т.к. пренебрежение ими может снизить эффективность участия в открытом сообществе. Литература
[9] Apache License. [HTML] (http://www.apache.org/licenses/LICENSE-2.0.html). [10] BSD License. [HTML] (http://www.opensource.org/licenses/bsd-license.php). [11] Перечень лицензий на сайте Free Software Foundation . [HTML] (http://www.gnu.org/licenses/license-list.html). [12] Перечень лицензий, одобренных Open Sources Initiative. [HTML] (http://www.opensource.org/licenses/index.php). [13] Часто задаваемые вопросы на сайте kernel.org. [HTML] (http://www.kernel.org/faq/#account). [14] Internet Engineering Task Force. [HTML] (http://www.ietf.org/). [15] ETSI Plugtests Service Center. [HTML] (http://www.etsi.org/plugtests/). [16] Электронный список рассылки The Austin Group. [HTML] (http://www.opengroup.org/austin/lists.html). [17] The Open Group. [HTML] (http://www.opengroup.org/). [18] Project Proposal and Call for Participation: The Linux Standard Base (LSB) Project. [HTML] (http://old.lwn.net/1998/0528/a/lsb.html). [19] Linux Standard Base discussion. [HTML] (http://lists.freestandards.org/mailman/listinfo/lsb-discuss). [20] Проект OLVER. [HTML] (http://linuxtesting.ru/). [21] Баранцев А.В., Бурдонов И.Б., Демаков А.В., Зеленов С.В., Косачев А.С., Кулямин В.В., Омельченко В.А., Пакулин Н.В., Петренко А.К., Хорошилов А.В. Подход UniTesK к разработке тестов: достижения и перспективы. // Труды ИСП РАН, №5, 2004. [HTML] (http://www.citforum.ru/SE/testing/unitesk). [22] В. В. Кулямин, Н. В. Пакулин, О. Л. Петренко, А. А. Сортов, А. В. Хорошилов. Формализация требований на практике. // Препринт № 13 ИСП РАН, 2006. [23] Система управления ошибками Bugzilla. [HTML] (http://www.bugzilla.org/). [24] Система управления ошибками в LSB. [HTML] (http://bugs.linuxbase.org/). [25] OLVER, замечания к стандарту LSB [HTML] (http://linuxtesting.ru/results/std_reports_LSB). [26] Список замечаний к части System Interfaces (XSH) стандарта POSIX. [HTML] (http://www.opengroup.org/austin/aardvark/latest/xshbug2.txt). [27] OLVER, замечания к стандарту POSIX [HTML] (http://linuxtesting.ru/results/std_reports_SUSv3). [28] GNU C Library. [HTML] (http://www.gnu.org/software/libc/). [29] Система управления ошибками проекта sourceware.org. [HTML] (http://sourceware.org/bugzilla/).
[1] Проект свободной многоязычной энциклопедии Wikipedia. [HTML] (http://www.wikipedia.org/). [2] Проект PlanetMath – энциклопедия, публикации и учебные курсы по математике. [HTML] (http://planetmath.org/). [3] Проект PlanetPhysics – энциклопедия, публикации и учебные курсы по физике. [HTML] (http://planetphysics.org/) [4] Архив графических работ OpenClipArt для оформления элементов GUI и Webстраниц. [HTML] (http://www.openclipart.org/) [5] Сеть свободных музыкальных проектов Remix Commons. [HTML] (http://www.remixcommons.org/). [6] Open Source Initiative. [HTML] (http://www.opensource.org/index.php). [7] GNU General Public License. [HTML] (http://www.gnu.org/copyleft/gpl.html). [8] GNU Lesser General Public License. [HTML] (http://www.gnu.org/copyleft/lgpl.html).
29
30
Текущее состояние и перспективы развития инфраструктуры LSB Д. В. Силаков
[email protected] Аннотация. В статье рассказывается о технической стороне разработки стандарта Linux Standard Base и связанной с ним инфраструктуре. Описывается использование базы данных для хранения части информации, входящей в стандарт. Обсуждается процесс генерации на основе этих данных как непосредственно текста стандарта, так и сопутствующих объектов – наборов элементарных тестов, заголовочных файлов, отвечающих стандарту LSB, и пр. Также рассматриваются задачи по развитию существующей инфраструктуры, которые планируется решить в рамках совместного проекта ИСП РАН и организации Free Standards Group, под эгидой которой проводится разработка стандарта LSB.
1. Введение Стандарты играют существенную роль в современном мире программного обеспечения, внося единообразие в сложные программные комплексы. Наличие стандартов в области операционных систем позволяет разработчикам создавать приложения, взаимодействующие с операционной системой, не изучая при этом детали реализации объекта взаимодействия. Для написания программы, соответствующей какому-либо стандарту, разработчику необходимо изучить текст этого стандарта. Большинство стандартов попадают к пользователям в виде электронных документов либо напечатанных книг, и объем их достаточно велик. Однако во многих случаях процесс разработки упростится, если некоторые сведения из стандарта будут доступны в какой-то другой форме, более формальной, чем текст, и не требующей изучения человеком. Например, для гарантии того, что в программе будут использоваться только описанные стандартом функции, специфицированные в заданных заголовочных файлах, полезно иметь такие файлы, содержащие описания только стандартных функций. Использование только этих заголовочных файлов при создании программы гарантировало бы отсутствие обращений к функциям, не входящим в стандарт. Но ручное создание таких заголовочных файлов на основе текста стандарта – трудоемкое занятие (например, стандарт LSB [1] содержит описание 18955 функций из 465 заголовочных файлов), и во многих случаях (особенно при разработке 31
небольших приложений) разработчики предпочитают полагаться на свое знание текста стандарта. Вполне возможно, что у создателей стандарта имеется необходимая информация в виде, удобном для разработчика, однако процесс создания большинства стандартов скрыт от постороннего взора. Тексты многих стандартов доступны только за плату, а доступа к данным, которые (возможно) используются при создании текста стандарта, нет. Нетрудно видеть, что такое положение вещей противоречит идеологии open source, согласно которой пользователь должен иметь возможность получить не только готовый продукт, но и все необходимое для его самостоятельной сборки и внесения в него изменений. Конечно, требование открытости относится, прежде всего, к исходным кодам программ, однако многие сторонники open source переносят эту идеологию и на смежные области. И одним из примеров здесь является стандарт LSB, описывающий базовые интерфейсы операционной системы на бинарном уровне. Во Free Standards Group [2], под эгидой которой проводится разработка LSB, используется специфический подход, открывающий всем желающим всю «кухню» по производству стандарта. Каждый может бесплатно получить все необходимое не только для генерации текста стандарта, но и для автоматического создания сопутствующих наборов программ и исходного кода. О том, как устроена инфраструктура, связанная со стандартом LSB, и какие существуют планы по ее развитию, и рассказывается в данной статье.
2. База данных стандарта LSB Во многих случаях работа со стандартом упростилась бы при наличии описания объектов стандарта и их взаимосвязей в виде, более пригодном для обработки различными программными инструментами, чем обычный текст. Так, в примере из введения генерации заголовочных файлов, содержащих только стандартизованные функции, удобно было бы иметь готовые списки заголовочных файлов и функций, а также знать, какие функции описаны в каждом заголовочном файле. Free Standards Group дает возможность получить такую информацию для всех сущностей, определенных в стандарте LSB.
2.1. Информация об объектах, описанных в LSB Для стандарта LSB в качестве хранилища объектов, описываемых стандартом, основной информации о них и связей между ними используется так называемая база данных стандарта LSB (LSB Specification Database). Более точно, в этой базе данных содержится информация о следующих объектах: библиотеки (таблица Library);
32
классы (таблица ClassInfo);
интерфейсы – LSB является бинарным стандартом и описывает
интерфейсы, предоставляемые каждой библиотекой. Все интерфейсы хранятся в таблице Interface и могут иметь следующие типы: o Function (функции); o Data (данные); o Common («общие интерфейсы», к которым относятся stdin, stdout и им подобные); o Alias (интерфейсы, являющиеся синонимами других интерфейсов);
заголовочные файлы (таблица Header);
константы (таблица Constant);
типы данных (таблица Type);
команды (таблица Command; под командами здесь понимаются как встроенные команды shell, так и различные утилиты);
секции исполняемых файлов формата ELF (таблицы ElfSections и SectionTypes);
теги rpm-файлов (таблица RpmTag).
к модулю LSB_Core, однако база данных этого факта никак не отражает – об этом «знают» только скрипты, генерирующие текст стандарта. Связи типа «многие ко многим» реализуются посредством использования отдельных таблиц. Самой сложной структурой обладает взаимосвязь таблиц ClassInfo и Interface. Для ее реализации используется несколько вспомогательных таблиц, содержащих информацию о виртуальных таблицах класса (таблица Vtable; каждый класс может иметь одну либо две виртуальные таблицы) и о наследовании (таблица BaseTypes реализует обычное наследование, при описании множественного наследования используется также таблица VMIBaseTypes). Для удобства интерфейсы и классы, входящие в одну библиотеку, разбиваются на группы согласно их назначению. Информация о таких группах, на которые разбиваются библиотеки, содержится в таблице LibGroup. Так, например, библиотека librt (функции реального времени) разделена на три группы: Shared Memory Objects (функции для работы с разделяемой памятью); Clock (функции для работы с часами);
Timers (функции для работы с таймерами).
Аналогично группируются константы и типы данных, описанные в одном заголовочном файле. Информация о таких группах содержится в таблице HeaderGroup. Так, например, файл rpc/rpc_msg.h, который содержит декларации типов и функций для работы с сообщениями, передаваемыми при удаленном вызове процедур (RPC, Remote Procedure Call), делится на следующие группы: accepted_reply (типы, которые описывают ответ на rpc-запрос, принятый сервером);
Рис.1. ER-диаграмма сущностей, описываемых стандартом LSB. Также в базе данных хранятся связи между указанными объектами – каждая константа привязана к заголовочному файлу, в котором она объявляется, интерфейс – к библиотеке, в которой он содержится, либо к классу, если это метод класса, и т.д. ER-диаграмма существующей базы данных приведена на Рис. 1. Все сущности группируются в так называемые модули согласно своему назначению (например, модуль LSB_Сpp содержит все, относящееся к стандартной библиотеке C++, LSB_Toolkit_Qt3 – все, относящееся к библиотеке Qt3, и т.д.). Информация о форматах файлов ELF и RPM относится 33
rejected_reply (типы, которые описывают ответ на rpc-запрос, отвергнутый сервером);
reply_body (типы, описывающие тело ответа на rpc-запрос);
call_body (типы, описывающие тело rpc-запроса);
rpc_msg (типы, описывающие весь rpc-запрос);
base types (основные типы);
default HeaderGroup (сюда перечисленные выше группы).
относится
все,
не
вошедшее
в
Заметим, что типы одной группы могут быть составными типами, определяемыми через типы других групп. Например, типы из группы rpc_msg – это структуры, содержащие тип из call_body либо reply_body и некоторые дополнительные атрибуты.
34
2.2. Информация об архитектурах
3. Генерация текста стандарта
Стандарт LSB специфицирует интерфейсы операционной системы на бинарном уровне. Естественно, это делает стандарт зависимым от архитектуры аппаратного обеспечения – для каждой архитектуры необходима своя версия стандарта. Поэтому стандарт LSB содержит различные документы для различных аппаратных платформ – например, все, что относится к стандартной библиотеке C++, описывается в документах LSB-CXX-IA32 (для архитектуры IA32), LSB-CXX-AMD64 (для архитектуры AMD64) и т.д (в LSB версии 3.1 поддерживаются семь архитектур - AMD64, IA32, IA64, PPC32, PPC64, S390 и S390X). Кроме того, есть так называемая общая спецификация LSB (LSB Generic Specification), описывающая объекты, которые должны присутствовать во всех LSB-совместимых реализациях, независимо от аппаратной платформы. Для хранения списка аппаратных платформ в схеме базы данных предусмотрена таблица Architecture. Многие объекты, информация о которых содержится в базе данных, включены в спецификации для одних платформ и отсутствуют в спецификациях для других. Кроме того, каждый объект, описываемый стандартом, на каждой платформе может иметь какие-то специфические особенности. Поэтому между таблицами объектов и таблицей архитектур существуют связи «многие ко многим», которые реализуются посредством отдельных таблиц, содержащих пары «идентификатор объекта, идентификатор архитектуры». Эти таблицы также содержат те свойства объектов, которые могут иметь различные значения на различных архитектурах (например, значения констант). Среди идентификаторов архитектур есть одно выделенное значение – «All». Если объект приписан к архитектуре с этим идентификатором, то он должен присутствовать во всех архитектурах, поддерживаемых LSB, и при этом на всех архитектурах его свойства должны быть одинаковы (допускается одно исключение для версий интерфейсов – для интерфейса, приписанного к архитектуре All, можно завести отдельную запись-привязку к конкретной архитектуре, указав там значение версии, не меньшее, чем для архитектуры All). Безусловно, можно было бы обойтись и заведением необходимого количества записей для объекта (по одной записи для каждой поддерживаемой архитектуры), но идентификатор «All» имеет дополнительную смысловую нагрузку – именно объекты, привязанные к архитектуре All, попадают в общую спецификацию LSB. При отсутствии такого идентификатора для каждого объекта приходилось бы проверять, что он привязан ко всем поддерживаемым архитектурам и при этом обладает на каждой из них одними и теми же свойствами. Такие проверки существенно усложнили бы создание генераторов текста стандарта и связанных с ним объектов, о которых пойдет речь в следующих двух разделах.
База данных активно используется при генерации текста стандарта LSB. Ведь основное содержание всех документов LSB – это перечень интерфейсов, которые должны присутствовать в системе, и их описаний. Интерфейсы разбиваются по библиотекам либо по классам; кроме того, внутри каждой библиотеки возможна дополнительная группировка, как было указано в предыдущем разделе. Также указывается, какие заголовочные файлы необходимо подключить для использования того или иного интерфейса, а для интерфейсов-функций приводится их сигнатура. Здесь снова стоит отметить, что стандарт LSB предназначен для обеспечения переносимости приложений на бинарном уровне, а не на уровне исходного кода. Для бинарной переносимости не важно, в каких заголовочных файлах определяются конкретные объекты – главное, чтобы эти объекты находились в заданных стандартом библиотеках. Поэтому в LSB описывается расположение объектов по заголовочным файлам, принятое в большинстве дистрибутивов Linux. Цель этого описания – помочь разработчикам определить местоположение нужного интерфейса в ходе написания прог-раммы; однако действительное место описания объекта в конкретном дистри-бутиве может отличаться от приведенного в LSB, при этом требования стан-дарта не будут нарушены, если объект входит в предписанную библиотеку. Перечни интерфейсов с краткой информацией создаются специальными скриптами на основе сведений из базы данных стандарта LSB. Скрипты создают текст стандарта в формате sgml, используя в некоторых случаях заранее заготовленные шаблоны. На основе сгенерированных sgml-файлов создаются файлы в наиболее распространенных форматах – html, rtf, ps, pdf, а также файлы в текстовом формате (естественно, лишенного такого преимущества остальных форматов, как навигация по ссылкам внутри документа, что полезно при чтении стандарта). На основе соответствующих таблиц также создаются списки констант и определяемых пользователем типов (перечислений, структур и т.п.), сгруппированных по заголовочным файлам, в которых они описаны, список секций исполняемых файлов формата ELF и список тэгов файлов формата RPM. Что касается более детальных описаний интерфейсов (например, описания того, что делает функция), то здесь стандарт LSB в большинстве случаев ссылается на другие стандарты (такие, как POSIX, стандарт ISO/IEC 9899 для языка C и т.д.). Информация о том, где искать описание каждого конкретного объекта, также содержится в базе данных стандарта – для этого заведена отдельная таблица, содержащая все стандарты, на которые есть ссылки из LSB, и каждая запись в таблицах объектов ссылается на запись в таблице стандартов. Соответственно в тексте стандарта рядом с каждым интерфейсом указано, где искать его подробное описание.
35
36
Описания некоторых интерфейсов содержится непосредственно в LSB, но их число очень невелико (на ноябрь 2006 г. – 454 из 32721 интерфейса, включенных в текст стандарта). Эти описания создаются вручную (также в формате sgml) и автоматически включаются в нужные места при генерации стандарта.
4. Генерация зависящих от стандарта объектов Free Standards Group предоставляет средства для генерации не только самого текста стандарта, но и различных объектов, зависящих от этого стандарта. К таким объектам относятся: библиотеки-«заглушки» («stub libraries»);
заголовочные файлы с описаниями объектов, определенных в стандарте LSB;
элементарные тесты для проверки соответствия дистрибутива либо приложения стандарту LSB.
Рассмотрим эти объекты подробнее.
4.1. Библиотеки-«заглушки» и заголовочные файлы Говорить о LSB-совместимости можно применительно не только к дистрибутивам Linux, но и к отдельным программ. Дистрибутив соответствует стандарту LSB, если он содержит все объекты, описываемые LSB, с характеристиками, указанными в стандарте. Программа же является LSBсовместимой, если она в своей работе использует только те объекты, которые описаны в LSB. Таким образом, любая LSB-совместимая программа будет работать в любом дистрибутиве, отвечающим требованиям этого стандарта (поскольку LSB стандартизует интерфейсы на бинарном уровне, а не на уровне исходного кода, то для запуска программы на разных дистрибутивах не требуется перекомпиляция). Однако, даже если программа пишется в дистрибутиве, отвечающим требованиям LSB, нельзя быть уверенным, что она будет LSB-совместимой – ведь соответствие дистрибутива стандарту не означает, что в нем нет каких-то дополнительных интерфейсов или команд, которые могут оказаться задействованы в разрабатываемой программе. Для проверки того факта, что программа является LSB-совместимой, рекомендуется производить тестовую сборку программы в так называемом LSB-совместимом окружении. Это окружение включает в себя библиотеки«заглушки» и заголовочные файлы, для генерации которых предоставляются специальные скрипты. Скрипты создают исходный код для библиотек, описанных в LSB, включая туда все определенные в стандарте LSB интерфейсы с соответствующими сигнатурами. Однако генерируемый исходный код всех интерфейсов-функций 37
не содержит ничего полезного – если функция не возвращает никаких значений, то тело функции просто пусто, в противном случае генерируется конструкция, возвращающая какое-нибудь значение требуемого типа. Таким образом, весь этот исходный код может быть скомпилирован, в результате чего и будут получены библиотеки-«заглушки» – эти библиотеки формально содержат все интерфейсы из стандарта LSB и не содержат никаких других интерфейсов, однако функции, содержащиеся в них, ничего не делают. Помимо библиотек-«заглушек» скрипты создают заголовочные файлы, содержащие описания тех и только тех объектов (типов, констант, функций), которые входят в LSB. При этом свойства этих объектов (размеры типов, значения констант, сигнатуры функций) полностью соответствуют стандарту. Таким образом, если программу удается скомпилировать в таком окружении (т.е. с использованием только библиотек-«заглушек» и сгенерированных заголовочных файлов), то можно гарантировать, что она использует только объекты, описанные в LSB. Однако проводить функциональное тестирование программы все-таки необходимо с использованием реальных библиотек. Поэтому для получения уверенности в том, что программа будет выполнять поставленные перед ней задачи на любой LSB-совместимой платформе, функциональное тестирование программы необходимо проводить на дистрибутиве, соответствующем стандарту LSB. Созданию LSB-совместимых приложений с использованием LSBсовместимого окружения посвящена отдельная книга [3], выпущенная разработчиками стандарта и доступная в электронном виде на сайте Free Standards Group.
4.2. Элементарные тесты Одной из главных задач стандартизации является проведение сертификационного тестирования на соответствие реализации стандарту. LSB, как и большинство промышленных стандартов, имеет большой объем, и написать тесты для проведения сертификации вручную не представляется возможным. Необходима автоматизация процесса создания сертификационного тестового набора. Наличие базы данных стандарта позволяет сгенерировать на ее основе набор элементарных тестов, определяющих, присутствуют ли в системе все объекты, описанные в стандарте LSB, и соответствуют ли их характеристики (значения констант, сигнатуры функций и т.п.) тем, которые указаны в стандарте. В частности, генерируются следующие наборы тестов:
38
cmdchk – проверка того, что в системе присутствуют все описанные в LSB команды, и соответствующие исполняемые файлы находятся в директориях, определенных стандартом (хотя для многих команд имена директорий не указаны);
devchk – проверка наличия в системе всех заголовочных файлов, описанных в LSB, и наличия в этих файлах описаний всех требуемых стандартом констант и типов (функции данный тест не проверяет). Для констант проверяется, верно ли определены их значений, для типов, определяемых в заголовочных файлах, вычисляется размер и сравнивается с тем, который должен быть согласно LSB;
libchk – проверка наличия в системе всех библиотек, описанных в LSB, а также проверка содержимого этих библиотек на соответствие стандарту (тестируется наличие в библиотеке всех необходимых интерфейсов с сигнатурами, определенными в стандарте).
Перечисленные выше тесты нацелены на проверку соответствия дистрибутивов Linux стандарту LSB. Кроме этого, существуют скрипты для генерации тестовых наборов, нацеленных на проверку соответствия стандарту отдельного приложения. К таким тестовым наборам относятся: appchk – статический анализ файла на предмет использования им библиотек и интерфейсов, не входящих в LSB;
dynchk – проверка корректности использования приложением интерфейсов, определенных в LSB (разработка этих тестов еще не завершена);
elfchk – проверка корректности заданного elf-файла (используется в тестах appchk);
rpmchk – проверка корректности заданного rpm-файла (используется в тестах appchk).
Подробнее о перечисленных выше тестовых наборах и использовании базы данных стандарта при их генерации можно узнать в [4]. Несмотря на то, что перечисленные выше тесты не осуществляют никаких сложных проверок, возможность их автоматической генерации существенно упрощает процесс проверки соответствия дистрибутивов и приложений требованиям LSB. Например, если бы разработчикам тестов было доступно только текстовое описание заголовочных файлов со всем содержимым (константами, типами и функциями), то даже для элементарной проверки, выполняемой тестовым набором devchk, им пришлось бы вручную извлекать из текста всю необходимую информацию. А этот процесс может занять довольно продолжительное время (стандарт LSB описывает более 6000 различных констант и более 8000 типов для 411 заголовочных файлов). Кроме того, при таких объемах информации неизбежны ошибки, связанные с человеческим фактором. Более сложные тесты на основе базы данных стандарта создать нельзя, поскольку она содержит далеко не всю информацию, присутствующую в тексте LSB. В частности, нельзя создать тестовый набор для проверки соответствия стандарту поведения функций, а именно такие тесты наиболее 39
важны для сертификационного тестирования. Однако описание поведения функций на данный момент имеется только в виде текста, написанного человеком, и для автоматического создания тестов необходимо проводить формализацию этого текста (пример подхода к формализации приведен в [5], там же имеется краткий обзор других существующих подходов). О планах по добавлению в базу данных стандарта LSB более детальной информации об интерфейсах и о других направлениях развития инфраструктуры LSB можно узнать в следующем разделе.
5. Планы по развитию инфраструктуры LSB В настоящее время в ИСП РАН в рамках совместного проекта с Free Standards Group проводится разработка тестовой инфраструктуры, предназначенной для усовершенствования процесса сертификации на соответствие стандарту LSB. Помимо собственно процесса тестирования, проект затрагивает и многие смежные области, в том числе базу данных стандарта и работающие с ней инструменты.
5.1. Атомарные требования и тестовые наборы В настоящее время достаточно сложно получить информацию о существующих тестовых наборах для проверки соответствия стандарту LSB и о покрытии, которое они обеспечивают. Такую информацию изначально планировалось хранить в базе данных стандарта – для этого в таблице Interface есть поле Itested, показывающее, тестируется ли интерфейс тестовыми наборами, используемыми при сертификационном тестировании. Кроме того, есть таблица TestSuite, содержащая информацию о доступных тестовых наборах, а также таблицы TestInt и TestCmd, показывающие, какие интерфейсы и команды покрываются тестовыми наборами. Однако сейчас нет никакого способа заполнять эти таблицы автоматически – для их заполнения необходимо проводить анализ отчетов о тестировании либо непосредственно тестовых наборов и определять, на тестирование каких интерфейсов и команд нацелен каждый тест. Последний раз такая работа проводилась в середине 2002 года. С тех пор информация, касающаяся тестов и покрытия, в базе данных не обновлялась, и на текущий момент практически полностью устарела. Так, из 32721 интерфейса, включенных в LSB версии 3.1 (учитывая все архитектуры), только 184 помечены как протестированные (Itested=’Yes’) и еще для 1700 содержатся записи в таблице TestInt (что означает, что для 1700 интерфейсов хотя бы один из тестовых наборов содержит какой-то тест). С командами ситуация несколько лучше – из 141 команды, включенной в стандарт, для 90 существуют тесты. Кроме того, наличие всего одного поля Itested и записей в таблице TestInt для каждого интерфейса представляется недостаточным. Ведь в случае, если интерфейс является функцией, для него может существовать целый набор 40
требований, и тестовые наборы могут проверять только часть из них. При существующей структуре определить полноту тестирования каждого конкретного интерфейса невозможно. Для обеспечения более полной информации о покрытии тестами интерфейсов планируется для каждого интерфейса хранить список атомарных требований, и осуществлять привязку тестов не к самим интерфейсам, а к этим атомарным требованиям. Рассматривается возможность хранить атомарные требования не непосредственно в базе данных, а в отдельных xml-файлах; база данных при этом будет содержать только ссылки на соответствующие xml-файлы. Такая организация представляется более предпочтительной, чем размещение всех требований в БД. Последнее увеличит размер БД в несколько раз, в то время для как большинства инструментов, работающих с БД (генераторы текста стандарта, генераторы библиотек-«заглушек» и т.п.) эти данные не нужны. Атомарные требования необходимо выделять из спецификации интерфейса. В LSB такие спецификации либо создаются вручную и хранятся в отдельных файлах формата sgml, либо вместо детального описания содержится ссылка на другой стандарт, где такое описание присутствует. Таким образом, для выделения атомарных требований необходимо анализировать описания интерфейсов, написанные человеком. Естественно, непосредственно анализ описаний автоматизировать трудно, однако планируется предоставить инструмент, который позволит упростить процесс занесения атомарных требований в базу данных. Задачу существенно усложняет наличие множества ссылок на другие документы – для выделения атомарных требований для всех интерфейсов необходимо иметь тексты всех этих стандартов (на данный момент в описаниях интерфейсов содержатся ссылки на 39 различных документов).
5.2. Информация о дистрибутивах Особенностью стандарта LSB является ориентированность на основные дистрибутивы Linux – в стандарт включаются только те объекты, которые присутствуют в последних версиях большинства основных дистрибутивов. (Формально списка «основных дистрибутивов» нет – согласно официальной доктрине, в LSB вносится все то, что «является устоявшейся практикой в мире Linux»; однако в реальной жизни невозможно исследовать все дистрибутивы, и основное внимание уделяется RedHat, SuSe, Mandriva, Asianux, Debian и Ubuntu). Одним из важных аспектов разработки стандарта является отслеживание списка интерфейсов, команд и пр., используемых в различных версиях дистрибутивов. Если какой-то интерфейс либо команда присутствуют почти везде и востребованы разработчиками приложений, но еще не включены в LSB, то они объявляются кандидатами на включение в следующую версию стандарта. Ручная обработка списка интерфейсов и определение кандидатов на 41
включение в стандарт – трудоемкая работа, и в настоящее время назрела потребность в ее автоматизации. Для упрощения работы с дистрибутивами планируется, прежде всего, добавить в базу данных таблицу, содержащую список дистрибутивов (включая различные версии одного и того же дистрибутива). Также планируется добавить таблицу с компонентами, содержащимися в этих дистрибутивах (glibc, zlib, Qt, Gtk и т.д.), и привязку записей из этих таблиц к объектам, описываемых стандартом – библиотекам, заголовочным файлам, интерфейсам и пр. Информацию о составе дистрибутивов (ассортимент и версии входящих в них компонентов, какие интерфейсы/библиотеки/заголовочные файлы содержатся в компонентах и т.п.) планируется заполнять автоматически на основе анализа установленного дистрибутива. В перспективе для занесения сведений об очередной версии дистрибутива в базу данных разработчику необходимо будет лишь запустить утилиты сбора информации о дистрибутиве (естественно, для обеспечения полноты сведений утилиты необходимо запускать на дистрибутиве, установленном в полной комплектации). При наличии информации об основных дистрибутивах и их составе можно будет автоматически определять объекты, являющиеся кандидатами на включение в стандарт, а также объекты, которые в ближайших версиях стандарта следует объявить устаревшими, а впоследствии удалить.
5.3. Поддержка версий LSB В настоящее время база данных содержит информацию только для текущей версии стандарта. Чтобы посмотреть, что входило в какую-либо из предыдущих версий, необходимо взять из CVS-репозитория FSG соответствующую версию базы данных. Также можно сгенерировать и текст стандарта определенной версии, однако для этого необходимо, помимо нужной версии базы данных, взять соответствующие версии скриптов, генерирующих текст стандарта – ведь со временем изменения вносятся как в схему базы данных, так и в сами скрипты. Аналогично, при необходимости сгенерировать объекты, описанные в разд. 4, необходимо взять нужные версии соответствующих скриптов. В рамках совместного проекта ИСП РАН и FSG планируется добавить в базу данных поддержку хранения информации для различных версий стандарта LSB, а также доработать все скрипты, чтобы они могли генерировать файлы, соответствующие определенной версии стандарта. Планируется воссоздать историю изменений стандарта LSB, начиная с версии 3.0. Что касается более ранних версий, то вопрос о предоставлении исторической информации для них остается открытым – в первые годы своего существования стандарт и построенная вокруг него инфраструктура переживали революционные изменения (переход к поддержке нескольких архитектур, 42
разбиение на модули), и хранить историю в полном объеме было вряд ли целесообразно, а ее воссоздание может потребовать значительных усилий.
5.4. Чистка базы данных стандарта LSB В процессе развития стандарта схема базы данных стандарта изменялась не один раз. К сожалению, не все изменения схемы сопровождались соответствующими корректными преобразованиями данных и инструментов, работающих с базой данных. В результате во многих таблицах появились поля, устаревшие с точки зрения структуры базы данных, но по-прежнему используемые частью скриптов. При этом были примеры несогласованности значений устаревших полей со значениями структур, пришедших им на смену. В результате скрипты, использовавшие устаревшие поля, генерировали некорректные данные. Проиллюстрируем сказанное на примере связи между интерфейсами и архитектурами. Первые версии стандарта LSB описывали бинарные интерфейсы только для одной архитектуры IA32, и схема базы данных не позволяла хранить информацию о различных архитектурах. Однако уже в LSB версии 1.2 появилась поддержка второй архитектуры (PPC32), и соответствующие изменения претерпела схема БД. Изначально в таблице Interface, хранящей данные об интерфейсах, было введено специальное поле Iarch, ссылающееся на запись в таблице Architecture. Однако связь между интерфейсами и архитектурами в действительности является связью «многие ко многим»: один интерфейс может быть определен для нескольких архитектур. Для таких интерфейсов предлагалось вводить несколько записей в таблице Interface, по одному для каждой архитектуры (соответственно, эти записи различались значением поля Iarch и, возможно, значениями некоторых архитектурно-зависимых свойств). Практика быстро показала, что решение заводить несколько записей для каждого интерфейса было не очень удачным. При таком подходе во всех таблицах, ссылающихся на интерфейс, приходилось также заводить несколько записей, соответствующих различным записям в таблице интерфейсов. Поскольку большая часть информации об интерфейсе не зависит от архитектуры, во многих таблицах данные просто дублировались, что приводило к неоправданному росту объема данных. В результате для реализации связей «многие ко многим» между таблицей интерфейсов и таблицей архитектур была создана отдельная таблица ArchInt. После этого все связи между интерфейсами и архитектурами вносились в таблицу ArchInt, однако поле Iarch убрано не было – к этому моменту многие инструменты, работающие с базой данных, использовали это поле, а для их быстрой переработки не хватало ресурсов. Кроме того, в таблице Interface уже было более 5000 записей, созданных исключительно для реализации связи «многие ко многим» между интерфейсами и архитектурами. Они не были 43
удалены, а в таблицу ArchInt не была занесена соответствующая им информация. Такой «частичный» переход к использованию таблицы ArchInt привел к тому, что часть информации о связи «многие ко многим» хранилась в этой таблице, а часть задавалась дублированием записей с изменением поля Iarch. Часть инструментов были переписаны с учетом появления новой таблицы; при этом они учитывали как данные из ArchInt, так и поле Iarch (в основном это относится к скриптам, генерирующим текст стандарта – их корректность имеет наивысший приоритет). Вновь написанные инструменты опирались только на таблицу ArchInt, в то время как часть старых инструментов так и не была переписана и использовала только Iarch. Избавление от проблемы со связями между архитектурами и интерфейсами было произведено в ходе чистки схемы и данных базы данных стандарта LSB на первом этапе разработки новой тестовой инфраструктуры LSB, в четвертом квартале 2006 года. В частности, было удалено поле Iarch, все необходимые данные перенесены в таблицу ArchInt и произведены соответствующие изменения скриптов. Помимо этого, были произведены следующие действия по устранению нестыковок и неоднородностей: Унификация названий полей и типов перечислений, имеющих одинаковую семантику в различных таблицах. Например, статус команд в стандарте хранился в поле Command.Cstatus, имевшем тип enum ('Included','Excluded','Builtin','Unknown'), а статус констант в стандарте хранился в поле Constant.Cstd, имевшего тип enum ('Yes','No','SrcOnly'). Теперь все поля, обозначающие статус какого-либо объекта в стандарте, имеют имена, оканчивающиеся на «stdstatus», и для всех таких полей используется одно и то же перечисление.
44
Удаление устаревших либо не используемых полей и таблиц.
Исправление в таблице Type некорректных значений некоторых полей, полученных при автоматическом заполнении базы. Например, для некоторых типов-перечислений объявление типа не было корректно обработано, и тип не был определен в таблице как перечисление, а ключевое слово «enum» просто присоединялось к имени типа.
Согласование типов полей-ссылок с типами полей, на которые они ссылаются.
Удаление дублирующихся индексов и добавление индексов, ускоряющих часто используемые запросы к базе данных (на основе анализа скриптов, работающих с БД). Появление дублирующихся индексов было вызвано особенностями архитектуры СУБД MySQL, которая при создании индекса вида (a,b,c) автоматически создает индексы (a,b) и (a).
6. Заключение Free Standards Group смогла успешно перенести идеологию open source на процесс разработки стандарта LSB, создав инфраструктуру, предоставляющую каждому желающему возможность самостоятельно генерировать текст стандарта и многие связанные с ним объекты. Наличие базы данных стандарта позволяет предоставлять пользователям более удобные способы изучения сущностей, описанных в LSB, чем простое чтение текста. Существующий подход уже доказал свою состоятельность – с его применением создавались все версии стандарта, начиная с 1.0. Естественно, за это время как схема базы данных стандарта LSB, так и использующие эту базу данных скрипты претерпели существенные изменения, однако основные идеи, заложенные в основу используемого подхода, остаются неизменными. Тем не менее, в своем текущем состоянии инфраструктура LSB удовлетворяет не все потребности, возникающие у пользователей, и есть много предложений по ее дальнейшему развитию. Прежде всего, необходимо предоставлять пользователю информацию о дистрибутивах и о наличии в них тех или иных сущностей, описанных в LSB. Также важно предоставить более простые способы получения данных о предыдущих версиях стандарта. Для создания сертификационных наборов, обеспечивающих хорошее покрытие, представляется важным иметь в базе данных более детальную информацию об интерфейсах. Все эти задачи планируется реализовать в рамках совместного проекта ИСП РАН и Free Standards Group по развитию инфраструктуры LSB. Литература [1] Linux Standard Base wiki. https://www.freestandards.org/en/LSB [2] Free Standards Group wiki. http://www.freestandards.org/en/Main_Page [3] Linux Standard Base Team. Building Applications with the Linux Standard Base. 2005. http://lsbbook.gforge.freestandards.org/lsbbook.html [4] Stuart Anderson, Matt Elder. Run-time Testing of LSB Applications. Proceedings of the Linux Symposium, July 2004. http://www.linuxsymposium.org/proceedings/reprints/Reprint-Anderson-OLS2004.pdf [5] А. И. Гриневич, В. В. Кулямин, Д. А. Марковцев, А. К. Петренко, В. В. Рубанов, А. В. Хорошилов. Использование формальных методов для обеспечения соблюдения программных стандартов. Труды Института Системного Программирования. Т.10, с. 51-68, 2006.
45
46
компилятора можно считать одним из важнейших средств обеспечения надёжности программного обеспечения. Основной источник трудностей при верификации компилятора заключается в том, что компилятор принимает на вход данные со сложной структурой, большим количеством внутренних связей и обладающие очень сложной семантикой времени исполнения. Для снижения сложности задачи верификации компилятора мы предлагаем декомпозировать её на отдельные подзадачи, которые в совокупности покрывают всю функциональность компилятора3. Функциональность, типичную для большинства компиляторов, можно условно декомпозировать на следующие задачи:
Верификация компиляторов – систематический подход1 С.В. Зеленов, Н.В. Пакулин Аннотация. Компиляторы преобразуют исходный текст программы на языке высокого уровня в вид, пригодный для исполнения. Ошибки в компиляторе чреваты отказами или ошибками при выполнении исполняемых файлов, построенных компилятором, поэтому обеспечение корректности функционирования компилятора является важнейшей задачей. Одна из особенностей компиляторов заключается в том, что они принимают на вход и выдают данные с очень сложной структурой, поэтому при проведении верификации на практике можно исследовать поведение компилятора лишь на небольшом подмножестве входных программ. В статье представлен подход к верификации компиляторов, основанный на декомпозиции общей задачи компилятора, и представлены методы решения выделенных задач. Представленный подход использовался при верификации различных индустриальных компиляторов и трансляторов.
1. Введение Языки высокого уровня являются основным средством разработки программных систем. Спецификация языка высокого уровня задаёт класс текстов, принадлежащих этому языку, и определяет семантику исполнения программ, написанных на этом языке. Задача перевода текстов с языка высокого уровня в представление, выполнимое на вычислительной системе, решается комплексами программ, которые по традиции называют компиляторами. Ошибки в компиляторах приводят к тому, что выполнение результирующих исполнимых модулей отличается от поведения, определяемого спецификацией языка. Дефекты исполнимых модулей, вызванные ошибками в компиляторе, очень сложно выявлять и исправлять, и в целом корректность исполнимых модулей, построенных некорректным компилятором, вызывает сомнения. Корректность компилятора является необходимым требованием корректной и надёжной работы программного обеспечения, разработанного на соответствующем языке высокого уровня. Тем самым, задачу верификации2 1
Работа частично поддержана грантом РФФИ 05-01-00999. Согласно Модели зрелости процесса разработки (Capability Maturity Model Integration) «верификация» определяется следующим образом: Верификация 2
47
1.
Анализ синтаксической корректности исходного теста программы.
2.
Анализ выполнения контекстных условий в исходном тексте4.
3.
Оптимизация внутреннего представления и генерация выходных данных.
Результатом работы компилятора является объектный модуль на машинном языке или модуль на исполнимом промежуточном языке, таком как байт-код Java и Python или Intermediate Language платформы .NET. Для большинства компиляторов языков программирования, используемых на практике, генерируемый код исполняется в специализированном окружении, которое обобщённо называется run time support. В рамках данной статьи мы будем пользоваться термином «подсистема поддержки исполнения». Подсистема поддержки исполнения тесно связана с компилятором. Например, каждый компилятор Си и Си++ поставляется со своим набором стандартных библиотек, причём библиотеки одного компилятора нельзя использовать для построения исполнимых файлов другим компилятором. Даже в тех случаях, когда промежуточный язык и стандартные библиотеки стандартизированы, существуют тесные зависимости между компилятором и средой исполнения. Например, для исполнения байт-кода Java в виртуальной машине Java компании IBM лучше всего для компиляции использовать компилятор Java именно этой фирмы; аналогично для исполнения байт-кода в виртуальной
обеспечивает соответствие набора результатов работы требованиям, которые для них заданы [1]. 3 Отметим, однако, что предлагаемый подход не отменяет интеграционного тестирования компилятора. 4 Примеры контекстных условий: требование, чтобы все используемые переменные, методы и т.д. были объявлены в коде программы; требование, чтобы аргументы вызова метода по своему количеству и по своим типам соответствовали формальным параметрам вызываемого метода. 48
машине компании Sun следует компилировать исходные тексты компилятором Java, разработанным в Sun. В настоящей работе представлен подход к верификации компиляторов, основанный на технологии автоматизированного тестирования UniTESK[4]. Методы, составляющие представленный подход, построены по единому шаблону формализации требований [9].
2. Систематический подход к верификации функций компилятора Функции компилятора тесно связаны, поэтому при верификации компилятора необходимо проверять корректность каждой из них. В предлагаемом подходе задача верификации компилятора декомпозируется на основе декомпозиции функциональности компилятора. Мы выделяем следующие задачи: 1.
Верификация синтаксического анализатора компилятора.
2.
Верификация семантического анализатора компилятора.
3.
Верификация оптимизаций и генерации кода.
4.
Верификация библиотек поддержки времени исполнения (runtime).
Некорректное функционирование подсистемы поддержки исполнения делает почти бесполезным весь компилятор – результат компиляции будет работать неверно из-за ошибок в этой подсистеме и, следовательно, станет непригодным для нужд пользователя. Тесная связь компилятора и подсистемы поддержки исполнения служит обоснованием того, чтобы включить верификацию подсистемы поддержки исполнения в общую задачу верификации компилятора. Корректность оптимизаций и генерации кода определяет корректность создаваемых транслятором выходных данных. Поэтому верификация этой функциональности является основной задачей верификации компилятора. От корректности синтаксического анализа и анализа контекстных условий зависит корректность основной функциональности транслятора – оптимизирующих преобразований, генерации кода. Поэтому решение задачи верификации анализа синтаксиса и контекстных условий является базой для решения задач верификации остальных функций компилятора. В литературе выделяют два вида верификации – статическая верификация средствами анализа кода и аналитической верификации и динамическая верификация посредством тестирования. Компилятор любого практически полезного языка представляет собой настолько сложную систему, что статической верификации поддаются только отдельные небольшие подсистемы, например, отдельные блоки оптимизации. Для решения задач верификации, поставленных выше, на практике используются методы динамической верификации. 49
В связи с этим при проведении верификации компилятора и его функций необходимо решить следующий набор задач: 1.
2.
автоматизация построения тестов: a.
автоматизация генерации тестовых данных;
b.
автоматизация проверки корректности обработки тестовых данных (проблема построения оракула);
определение критерия завершения верификации.
В данной работе мы предлагаем решать эти задачи на систематической основе. Во всех задачах верификации методы верификации строятся по единой схеме (рис. 1). Анализ документации
Формализация требований
Извлечение тестов
Анализ результатов REPORT
Нормативные документы
Каталог требований
Модель верифицируемой подсистемы
Тестовый набор
Отчёт о тестировании
Рис. 1. Общая схема метода верификации Эта схема метода верификации состоит из нескольких этапов. На первом этапе требования извлекаются из нормативных документов и систематизируются. В результате получается каталог требований, в котором требования сформулированы максимально однозначно, требования классифицированы, и, возможно, установлены связи между отдельными требованиями. Каталог требований используется на последующих этапах. Второй этап нацелен на представление требований в формальном виде. Требования из каталога записываются с использованием того или иного математического формализма. Такая запись требований называется формальной спецификаций или формальной моделью. На третьем этапе на основе построенной модели автоматизировано генерируются тесты. В зависимости от задачи тесты могут быть либо просто тестовыми данными, либо дополнительно содержать оракул для автоматического вынесения вердикта о корректности наблюдаемого поведения компилятора. В результате исполнения тестов строятся отчёты о тестировании. В них содержится информация о том, насколько наблюдаемое поведение 50
LR-анализатор придет в ситуацию, когда на вершине стека будет находиться символ s, а началом текущего входного потока будет последовательность токенов, отвечающая символу X.
компилятора соответствует формальной модели. Вопросы анализа вердиктов, обнаружения дефектов и их исправления выходят за рамки данной статьи.
3. Верификация синтаксического анализатора Синтаксический анализ входного текста является частью функциональности любого транслятора. Парсером мы называем булевскую функцию, заданную на множестве последовательностей токенов и принимающую значение «истина», если последовательность является предложением данного формального языка, и «ложь» – в противном случае. Задачу верификации парсера можно разделить на следующие подзадачи: 1. Проверка того, что парсер принимает предложения, принадлежащие целевому языку. 2.
Проверка того, что парсер отвергает предложения, не принадлежащие целевому языку.
3.1. Критерии полноты тестирования Мы рассматриваем известные алгоритмы синтаксического анализа (LL-анализ и LR-анализ, построенные на основе стека — см. [2]) в качестве алгоритмов, моделирующих поведение парсера. Критерий полноты набора позитивных5 тестов для тестирования LLанализатора: (PLL) Покрытие всех пар вида
Критерий полноты набора негативных6 тестов для тестирования LLанализатора: (NLLR) Покрытие всех пар (нетерминал A; «некорректный» токен t'), где пара (A, t') считается покрытой тогда и только тогда, когда среди тестов имеется последовательность токенов, не принадлежащая целевому языку, такая, что LL-анализатор, обрабатывая эту последовательность, придет в ситуацию, когда после обработки как минимум R «правильных» токенов, на вершине стека будет находиться символ A, а текущим входным символом будет «некорректный» токен t'. Критерий полноты набора негативных тестов для тестирования LRанализатора: (NLRR) Покрытие всех пар (символ s состояния конечного автомата; «некорректный» токен t'), где пара (s, t') считается покрытой тогда и только тогда, когда среди тестов имеется последовательность токенов, не принадлежащая целевому языку, такая, что LR-анализатор, обрабатывая эту последовательность, придет в ситуацию, когда после обработки как минимум R «правильных» токенов, на вершине стека будет находиться символ s, а текущим входным символом будет «некорректный» токен t'.
(нетерминал A, допустимый следующий токен t), где пара (A, t) считается покрытой тогда и только тогда, когда в тестовом наборе существует предложение языка, обрабатывая которое LLанализатор придет в ситуацию, когда на вершине стека будет находиться символ A, а текущим входным символом будет токен t. Критерий полноты набора позитивных тестов для тестирования LRанализатора: (PLR) Покрытие всех пар вида (символ s состояния конечного автомата, помеченный символом X переход из состояния s), где пара (s, X) считается покрытой тогда и только тогда, когда в тестовом наборе существует предложение языка, обрабатывая которое 5
Позитивный тест для парсера – это последовательность токенов, на которой парсер выдает вердикт «истина», т.е. последовательность токенов, являющаяся предложением целевого языка. 51
Существует семейство алгоритмов генерации тестов, удовлетворяющих этим критериям. Их подробное описание можно найти в работе [6]. Следует особо отметить следующее важное свойство предложенных в этой работе алгоритмов генерации негативных тестов: алгоритмы гарантированно строят тесты, не принадлежащие целевому формальному языку. Это свойство позволяет отказаться при построении оракула от использования каких бы то ни было эталонных синтаксических анализаторов. Разработан инструмент SynTesK [14], в котором реализованы алгоритмы, удовлетворяющие критериям PLL и NLL1, а также критерию NLR1 для некоторого специального вида грамматик [6]. Инструмент SynTesK успешно применялся для тестирования компиляторов реальных языков программирования, в т.ч. C и Java [6]. Практические 6
Негативный тест для парсера – это последовательность токенов, на которой парсер выдает вердикт «ложь», т.е. последовательность токенов, не являющаяся предложением целевого языка. 52
результаты этого применения показали эффективность реализованных в SynTesK алгоритмов.
3.2. Регламентирующие документы. Анализ требований В рамках предлагаемого подхода под тестированием парсера понимается проверка соответствия парсера функциональным требованиям. Функциональные требования извлекаются, как правило, из описания стандарта языка. Основное функциональное требование для парсера определяется множеством корректных предложений целевого языка. Обычно это множество описывается при помощи контекстно-свободной грамматики. Часто в тексте стандарта языка уже присутствует соответствующее описание в формальном виде.
3.3. Моделирование Для формального задания модели целевого языка используется представление грамматики языка в форме BNF.
3.4. Извлечение тестов Инструмент SynTesK предоставляет возможность в полностью автоматическом режиме систематически генерировать наборы тестов, удовлетворяющих сформулированным выше критериям покрытия. В качестве входных данных генератор тестов принимает формальное описание грамматики целевого языка в форме BNF.
3.5. Тестирование Автоматический оракул для прогона позитивных тестов устроен так: проверяется, что результатом обработки каждого теста парсером является значение «истина». При применении тестов к реальному компилятору значение парсера «истина» трактуется следующим образом – в выводе компилятора отсутствуют сообщения о синтаксических ошибках. Аналогично устроен оракул для прогона негативных тестов: проверяется, что результатом обработки каждого теста парсером является значение «ложь», то есть в выводе компилятора присутствуют сообщения об ошибках синтаксиса.
4.1. Методы тестирования Метод SemaTesK [3] автоматической генерации тестов для тестирования анализаторов контекстных условий основан на использовании модели атрибутированных деревьев для конструктивного описания статической семантики целевого языка. Метод позволяет получать наборы как позитивных8, так и негативных9 тестов для тестирования анализатора контекстных условий. В методе SemaTesK используется формальное описание контекстных условий в форме зависимостей между вершинами абстрактного синтаксического дерева. Зависимость между некоторыми двумя вершинами индуцируется зависимостями между соответствующими атрибутами этих вершин. Одна из вершин, участвующих в контекстном условии, называется источником, а другая — целью в том смысле, что атрибуты вершины-цели зависят от атрибутов вершины-источника. Этот метод дает возможность локализовать описание одного неформального контекстного условия в пределах одного формального правила, а также позволяет в подавляющем большинстве случаев задавать контекстные условия декларативно10. Построение тестов методом SemaTesK производится в соответствии со следующими критериями покрытия расположения контекстных условий в различных синтаксических контекстах. Критерий полноты набора позитивных тестов для тестирования анализатора контекстных условий: Для каждого контекстного условия R из множества всех контекстных условий набор тестов должен содержать тексты, покрывающие R и такие, что соответствующее множество деревьев содержит все возможные вершины «источник» и «цель» во всех возможных синтаксических контекстах. Критерий полноты набора негативных тестов для тестирования анализатора контекстных условий: 8
4. Тестирование анализаторов контекстных условий Анализатором контекстных условий мы называем булевскую функцию, заданную на множестве предложений данного формального языка7 и принимающую значение «истина», если в предложении выполняются все относящиеся к нему контекстные условия, и «ложь» – если хотя бы одно из этих контекстных условий нарушается. 7
Имеются в виду все синтаксически корректные предложения. 53
Позитивный тест для анализатора контекстных условий – это такое синтаксически корректное предложение, на котором анализатор контекстных условий выдает вердикт «истина», т.е. предложение, корректное с точки зрения статической семантики целевого языка. 9 Негативный тест для анализатора контекстных условий – это такое синтаксически корректное предложение, на котором анализатор контекстных условий выдает вердикт «ложь», т.е. предложение, не корректное с точки зрения статической семантики целевого языка. 10 В частности, данным методом была описана статическая семантика языков C и Java. 54
Для каждой мутации R¯ каждого контекстного условия R из множества всех контекстных условий набор тестов должен содержать множество текстов, покрывающих R¯ и таких, что соответствующее множество деревьев содержит все возможные вершины «источник» и «цель» во всех возможных синтаксических контекстах. Более подробное описание метода SemaTesK можно найти в работе [3]. Следует особо отметить следующее важное свойство метода SemaTesK. Метод дает возможность непосредственно строить тесты, нацеленные на проверку корректности анализа контекстных условий. Это свойство позволяет в отличие от прочих известных методов отказаться при генерации тестов от использования фильтрации (селекции) позитивных/негативных тестов среди множества всех синтаксически корректных предложений данного языка, а также позволяет систематически генерировать тесты, удовлетворяющие адекватным критериям полноты. Метод SemaTesK успешно применялся для тестирования компиляторов реальных языков программирования, в т.ч. C и Java [3]. Практические результаты этого применения показали эффективность метода SemaTesK. Заметим, что особенно перспективным направлением использования метода SemaTesK является генерация тестов для разрабатываемых диалектов языков, так как вносить небольшие изменения в их описания достаточно просто, и таким образом количество ручного труда для приведения тестов в соответствие с очередной редакцией языка не велико.
4.2. Регламентирующие документы. Анализ требований В рамках метода SemaTesK под тестированием анализатора контекстных условий понимается проверка соответствия анализатора контекстных условий функциональным требованиям. Функциональные требования извлекаются, как правило, из описания стандарта языка. Основное функциональное требование для анализатора контекстных условий определяется множеством предложений целевого языка, корректных с точки зрения статической семантики. Обычно это множество задается в стандарте при помощи описания синтаксиса языка в виде контекстно-свободной грамматики, а также при помощи описания контекстных условий в виде неформального текста. Основной работой по анализу требований в рамках метода SemaTesK является извлечение из стандарта языка и составление списка всех контекстных условий.
Модель статической семантики целевого языка задается в виде списка контекстных условий. Для их формального описания используется язык SRL (Semantic Relation Language), специально разработанный для этой цели [3].
4.4. Извлечение тестов Имеется инструментальная поддержка метода SemaTesK, которая позволяет в полностью автоматическом режиме систематически генерировать наборы тестов, удовлетворяющих сформулированным выше критериям покрытия. В качестве входных данных генератор тестов принимает формальное описание структуры абстрактных синтаксических деревьев целевого языка на языке TreeDL, а также описание контекстных условий целевого языка на языке SRL.
4.5. Тестирование Автоматический оракул для прогона позитивных тестов устроен так: проверяется, что результатом обработки каждого теста анализатором контекстных условий является значение «истина». При применении тестов к реальному компилятору значение парсера «истина» трактуется следующим образом – в выводе компилятора отсутствуют сообщения о семантических ошибках. Аналогично устроен оракул для прогона негативных тестов: проверяется, что результатом обработки каждого теста анализатором контекстных условий является значение «ложь», то есть в выводе компилятора присутствуют сообщения об ошибках семантики.
5. Тестирование оптимизаций и генерации кода Back-end’ом мы называем функцию, которая принимает на вход структуру обрабатываемых входных данных в некотором внутреннем представлении и генерирует соответствующие ей требуемые выходные данные. Функциональность back-end’а касается следующих аспектов:
4.3. Моделирование Модель синтаксиса целевого языка задается в виде формального описания на языке TreeDL [5] структуры корректных абстрактных синтаксических деревьев целевого языка. 55
56
анализ внутреннего представления программы, проводящийся с целью обогащения его дополнительной информацией, которая требуется при генерации выходных данных — примерами подобного анализа являются, например, анализ графа потока управления программы, анализ потоков данных в программе и проч.;
трансформации (оптимизации) внутреннего представления программы, проводящиеся с целью улучшения качества выходных данных в соответствии с выбранным критерием (например, размер или скорость работы программы); примерами подобных оптимизаций являются, например, удаление неиспользуемых
частей входных данных, вычисление константных выражений на этапе трансляции, оптимизация размещения аргументов и промежуточных результатов вычислений в регистрах процессора и проч.;
построение результирующего представления в выходном языке.
5.1. Методы тестирования Метод OTK [7] автоматической генерации тестов для тестирования back-end’а основан на построении модели его входных данных на основе из абстрактного описания алгоритма работы back-end’а. Метод OTK позволяет строить модели данных и разрабатывать на их основе генераторы тестов для компилятора, нацеленных на тестирование данного аспекта back-end’а. Модель данных строится на основе абстрактного описания алгоритма работы тестируемого аспекта. Алгоритм формулируется с использованием терминов, обозначающих сущности некоторого подходящего абстрактного представления входных данных, такого как граф потока управления, граф потока данных, таблица символов и пр. Back-end для осуществления своих действий ищет сочетания сущностей абстрактного представления программы, которые удовлетворяют некоторым шаблонам (например, наличие в программе циклов, наличие в теле цикла конструкций с определенными свойствами, наличие в процедуре общих подвыражений, наличие между инструкциями зависимости данных некоторого вида и пр.). При этом могут учитываться сущности лишь части терминов. Для построения модели данных рассматриваются только те термины, которые именуют сущности, задействованные хотя бы в одном шаблоне. Итак, в результате анализа алгоритма выделяются термины и шаблоны, используемые в алгоритме. Далее на основании этой информации описывается множество модельных строительных блоков: каждому термину соответствует свой вид модельного строительного блока;
строительные блоки могут связываться между собой и образовывать структуры, соответствующие шаблонам.
Модельной структурой называется граф, вершины которого – строительные блоки, а ребра – связи между строительными блоками. Модельная структура является абстракцией той части внутреннего представления структуры программы, которая существенна для целевого аспекта back-end’а. Критерий полноты множества тестов формулируется в терминах построенной модели на основе информации о тех шаблонах, которые были выделены при анализе алгоритма. 57
Более подробное описание метода OTK можно найти в работе [7]. Метод OTK успешно применялся при тестировании оптимизирующих компиляторов GCC, Open64, Intel C/Fortran compiler (см. [7]), а также для тестирования оптимизаторов графических моделей [8], реализации протокола IPv6, подсистемы построения отчетов в биллинговой системе, обработчиков XML-логов [4]. Практические результаты этого применения показали эффективность метода OTK.
5.2. Регламентирующие документы. Анализ требований В рамках метода OTK под тестированием back-end’а в трансляторе понимается проверка соответствия семантики выходных данных, созданных back-end’ом, семантике соответствующих входных данных. Структура входных данных извлекается из абстрактного описания алгоритма работы back-end’а (см. выше описание метода тестирования).
5.3. Моделирование Модель структуры входных данных задается в виде формального описания на языке TreeDL [5].
5.4. Извлечение тестов Для извлечения тестов в рамках метода OTK требуется разработать генератор тестов. Генератор тестов состоит из двух компонентов. Первый, называемый итератором, отвечает за последовательную генерацию модельных структур в соответствии с выбранным критерием полноты. Второй компонент, называемый меппером, отвечает за отображение каждой модельной структуры в соответствующую корректную программу на языке входных данных компилятора. В рамках метода OTK предоставляется мощная инструментальная поддержка для формального описания модели данных, а также для разработки всех требующихся компонентов генератора тестов [13]. Генератор, разработанный с помощью OTK, позволяет для целевого аспекта back-end’а в автоматическом режиме систематически генерировать тестовые наборы с целью достижения выбранного критерия полноты.
5.5. Тестирование В рамках метода OTK оракул для тестирования back-end’а в компиляторе автоматически проверяет сохранение семантики выполнения программы во время обработки back-end’ом. Для этого в качестве тестовых воздействий меппером строятся такие программы, семантика выполнения которых полностью представляется их трассой. Такое свойство тестов позволяет свести 58
3.
задачу проверки сохранения семантики к сравнению трассы с некоторой эталонной трассой. Работа такого автоматического оракула заключается в следующем: каждый тест компилируется дважды – тестируемым компилятором и некоторым эталонным компилятором;
обе откомпилированные версии запускаются на исполнение;
полученные трассы сравниваются;
считается, что тест прошел успешно в том и только том случае, если трассы эквивалентны.
a.
Верификация интерфейсов, предоставляемых окружению для взаимодействия с исполняемой системой.
b.
Верификация интерфейса, который предоставляет подсистема поддержки исполнения в исходном языке.
Рассмотрим эти задачи на примере транслятора TTCN3, который преобразует исходный текст TTCN3 в набор файлов на языке Си. В этом случае задачи верификации можно сформулировать следующим образом:
6. Верификация подсистемы поддержки исполнения (runtime support) Рассмотрим несколько примеров подсистем поддержки исполнения. Для низкоуровневых языков, таких как Си и Си++, поддержка исполнения ограничивается тем, что компилятор включает в состав своей поставки стандартные библиотеки этих языков. Компилятор Java переводит исходный текст на языке Java в байт-код. Система поддержки времени исполнения включает в себя виртуальную машину Java, стандартную библиотеку классов Java SDK, в том числе, внутренние классы, обеспечивающие доступ к ресурсам операционной системы (ввод-вывод, потоки, и т.д.), набор средств взаимодействия Java-среды с окружением через JNI. Другой пример относится к языку спецификации тестов TTCN3. Все известные в настоящее компиляторы TTCN3 построены как трансляторы – исходный текст на языке TTCN3 преобразуется в набор файлов на целевом языке (Си или Java), которые затем компилируются соответствующими компиляторами. Система поддержки времени выполнения представляет собой набор библиотек на целевом языке (Си или Java), которые реализуют встроенные возможности языка (таймеры, порты обмена сообщениями, стандартные функции и т.п.), предоставляют интерфейсы расширения (TCI и TRI) как сгенерированному коду, так и окружению, и, в зависимости от поставщика, различные дополнительные возможности. Задачу верификации подсистемы поддержки исполнения можно разделить на следующие подзадачи: интерфейса,
Верификация интерфейсов взаимодействия с окружением. Эта задача может быть разделена на две подзадачи:
1.
Верификация программного сгенерированному коду.
2.
Верификация встроенных возможностей языка и динамической семантики операций, предоставляемых подсистемой поддержки исполнения.
Верифицировать библиотеки непосредственно предоставляемый ими интерфейс Си/Си++.
через
2.
Верификация встроенных возможностей языка заключается в том, чтобы проверить выполнение требований динамической семантики TTCN3 в той части, которая реализована в подсистеме поддержки исполнения – проверка требований к передаче сообщений через порты обмена сообщениями, обработке событий в инструкциях выбора alt, запуску и остановке компонентов и т.д.
3.
Верификация интерфейсов расширения заключается в проверке выполнения требований к стандартным интерфейсам расширения: a.
Обращения к «скомпилированному тестовому набору» (Compiled Test Suite) извне через стандартные интерфейсы TCI и TRI, представленные в виде набора функциональных вызовов в программе на Си.
b.
Обращения к окружению скомпилированного тестового набора, представленные инструкциями и выражениями в исходных текстах на TTCN3, через стандартные интерфейсы TTCN3.
Аналогичные задачи можно сформулировать и для подсистем поддержки времени исполнения языка Java.
предоставляемого
59
1.
60
1.
Верификация библиотеки поддержки исполнения. Необходимо учитывать, что почти все сервисы, предоставляемые поддержкой времени исполнения, представлены как классы Java, поэтому в данном случае верификация представляет собой тестирование Javaклассов.
2.
Верификация встроенных возможностей языка (например, синхронизации доступа) и динамической семантики (например, создания и уничтожения объектов).
3.
Верификация интерфейсов расширения:
a.
Обращения из Java-кода к ресурсам операционной системы через специализированные классы (потоки ввода-вывода, потоки исполнения Thread, класс System и т.д.), обращения из Java к внешним библиотекам, разработанным на других языках (JNI).
b.
Верификация программного интерфейса виртуальной машины Java и доступа к объектам Java через JNI из программ на Си.
6.1. Методы верификации Верификация встроенных возможностей языка основывается на методе OTK (см. предыдущий подраздел). Целевые конструкции моделируются на формальном языке TreeDL, из формальной модели генерируются тестовые программы. Оракул строится на основе сравнения трасс исполнения тестовой программы, откомпилированной тестируемым компилятором, с трассой выполнения программы, откомпилированной на эталонном компиляторе. Далее мы будем рассматривать только вопросы тестирования библиотек, предоставляющих поддержку времени исполнения. Верификация библиотек времени исполнения основывается на методе функционального тестирования с использованием технологии автоматизированного тестирования UniTESK.[4]. Рассмотрим основные особенности UniTesK в приложении к тестированию программных интерфейсов: разделение построения тестовых воздействий и проверки правильности поведения целевой системы: тестовые воздействия строятся в тестовых сценариях, а проверка правильности поведения целевой системы осуществляется в тестовых оракулах;
Для работы с формальными спецификациями и спецификациями тестов разработаны пакеты инструментов CTesK [10] и JavaTESK [12]. Представленный метод использовался при тестировании системы поддержки исполнения компилятора Sun Java 1.4.
6.2. Регламентирующие документы. Анализ требований В рамках предлагаемого подхода под верификацией подсистемы поддержки времени исполнения понимается проверка соответствия этой подсистемы функциональным требованиям. Функциональные требования, как правило, извлекаются из спецификации или стандарта языка или описания эталонной реализации языка. Так, в случае языка TTCN3 функциональные требования представлены в частях 1, 4, 5 и 6 спецификации языка [10]. Требования к подсистеме поддержки исполнения Java приводятся в Java Language Specification, спецификаций Java SDK и Java Native Interface, опубликованных фирмой Sun.
6.3. Моделирование
автоматизированное построение тестовых воздействий;
представление функциональных требований к целевой системе в виде формальных спецификаций;
автоматическая генерация тестовых оракулов из спецификаций;
Требования к программному интерфейсу формализуются как контрактные спецификации. Такой подход к спецификации основан на представлении об описываемой системе как наборе компонентов, взаимодействующих с окружением и между собой. Контрактная спецификация состоит из модельного состояния, которое моделирует внутреннее состояние системы поддержки исполнения, и набора спецификационных функций. Спецификационные функции формализуют требования к обработке входящих и исходящих функциональных вызовов посредством логических выражений в постусловиях. Такой подход к спецификации программных интерфейсов близок к логике Т. Хоара и концепции «проектирования на основе контрактов» Б. Майера. Формальные спецификации программного интерфейса записываются на расширении языков программирования Си [10] или Java [12]. Из формальных спецификаций автоматически генерируются тестовые оракулы – процедуры, которые выносят вердикт о соответствии наблюдаемого поведения системы спецификации.
язык описания формальных спецификаций и тестовых воздействий «близок» к языку, на котором разработана целевая система;
6.4. Извлечение тестов
автоматически требований;
автоматически производится оценка качества покрытия требований при прогоне тестов.
генерируются
критерии
качества
покрытия
Метод тестирования с использованием UniTESK подробно рассмотрен в препринте [9]. 61
Тесты представляют собой конечные автоматы. С каждым переходом в автомате сопоставляется выдача тестового воздействия. Для текстового задания таких автоматов в используемых расширениях языков программирования предусмотрены специальные конструкции (тестовые сценарии, сценарные функции и итераторы), которые позволяют описывать автоматы в компактном виде.
62
6.5. Тестирование При обходе автомата теста автоматически выдаются тестовые воздействия, связанные с текущим переходом автомата, собираются ответы целевой системы и выносится вердикт о соответствии наблюдаемого поведения системы её спецификации. Критерий полноты тестирования формулируется в терминах покрытия спецификации – должны быть покрыты все ветви функциональности, представленные в спецификационных функциях. В ходе тестирования генерируется трасса теста, по которой генерируются отчёты. Отчёты содержат информацию о том, какие функции целевой системы тестировались и были ли выявлены в ходе тестирования расхождения со спецификацией.
7. Заключение В работе представлен систематический подход к верификации компиляторов. Общая задача верификации компилятора декомпозирована на подзадачи верификации отдельных аспектов функциональности компилятора. Декомпозиция задачи верификации позволяет начинать верификацию на ранних этапах создания компилятора и проводить её параллельно разработке. Тем самым, сокращается продолжительность цикла разработки компилятора и снижаются суммарные затраты на тестирование компилятора. Для всех выделенных подзадач предложены методы верификации, построенные в рамках единого подхода тестирования на основе формальных спецификации и моделей. Для всех методов существуют их подробные описания и инструментальная поддержка для их практического применения. Представленный подход использовался при тестировании компиляторов Си и Java, а также трансляторов расширений языков программирования, разработанных в ИСП РАН [4].
[6] С.В. Зеленов, С.А. Зеленова. Генерация позитивных и негативных тестов для парсеров // Программирование, том. 31, №6, 2005, 25–40. [7] С.В. Зеленов, С.А. Зеленова, А.С. Косачев, А.К. Петренко. Применение модельного подхода для автоматического тестирования оптимизирующих компиляторов. [HTML] http://www.citforum.ru/SE/testing/compilers/ [8] С.В. Зеленов, Д.В. Силаков. Автоматическая генерация тестовых данных для оптимизаторов графических моделей. // Труды ИСП РАН, Москва, 2006, т. 9, 129– 141. [9] В. В. Кулямин, Н. В. Пакулин, О. Л. Петренко, А. А. Сортов, А. В. Хорошилов. Формализация требований на практике. Препринт. М.: ИСП РАН, 2006. 50 с. [10] CTesK - инструмент для тестирования программного обеспечения, реализованного на языке C. [HTML] http://www.unitesk.ru/content/category/5/13/32/ [11] ETSI ES 201 873-1 V3.1.1. Methods for Testing and Specification (MTS); The Testing and Test Control Notation version 3; Part 1: TTCN-3 Core Language. Sophia-Antipolis, France: ETSI, 2005. 210 с. [12] JavaTESK - инструмент для тестирования программного обеспечения, реализованного на языке Java. [HTML] http://www.unitesk.ru/content/category/5/25/60/ [13] OTK – инструмент для тестирования программных систем, работающих с данными, имеющими сложную структуру. [HTML] http://www.unitesk.ru/content/category/5/15/34/ [14] SynTESK – инструмент для тестирования синтаксических анализаторов (парсеров) формальных языков. [HTML] http://www.unitesk.ru/content/category/5/16/35/.
Литература [1] CMMI for Systems Engineering/Software Engineering, Version 1.02 (CMMI-SE/SW, V1.02) CMU/SEI-2000-TR-018 ESC-TR-2000-018 November 2000, P. 598 [2] A .Aho, R. Sethi, J. D. Ullman. Compilers. Principles, Techniques, and Tools // Addison-Wesley Publishing Company, Inc. – 1985. [3] М.В. Архипова. Генерация тестов для семантических анализаторов // Вычислительные методы и программирование. 2006, том.7, раздел 2, 55–70. [4] А.В. Баранцев, И.Б. Бурдонов, А.В. Демаков, С.В. Зеленов, А.С. Косачев, В.В. Кулямин, В.А. Омельченко, Н.В. Пакулин, А.К. Петренко, А.В. Хорошилов. Подход UniTesK к разработке тестов: достижения и перспективы. // Труды ИСП РАН, Москва, 2004, т. 5, 121–156. [5] А.В. Демаков. TreeDL: язык описания графовых структур данных и операций над ними. // Вычислительные методы и программирование. 2006, том 7, раздел 2, 117– 127.
63
64
при разработке программных систем высокой сложности, какими являются реальные трансляторы, более практичным подходом является тестирование [1]. При тестировании встают две основные проблемы: 1. Проблема систематического создания тестовых данных для испытания работы тестируемой системы во всех различных ситуациях.
Тестирование трансляторов: проблема построения оракула для генератора кода
2. Проблема построения оракула для вынесения вердикта о корректности работы тестируемой системы.
В.В. Гингина, С.В. Зеленов, С.А. Зеленова Аннотация. В работе описан общий подход к построению автоматического оракула для тестирования генераторов кода в трансляторах текстов на формальных языках, а также предложена инструментальная поддержка для практического использования этого подхода. Приводятся результаты практического применения описанного подхода к тестированию генератора кода транслятора описаний схем баз данных на языке SQL.
1. Введение Развитие компьютерной индустрии и информационных технологий привело к тому, что во все большем числе областей бизнеса обработка документов производится автоматически с помощью компьютеров. Чтобы быть пригодными для компьютерной обработки, данные оформляются в виде формального текста, т.е. текста на некотором формальном языке. Транслятор — это программа, осуществляющая обработку формального текста и перевод его в некоторое другое представление. Примерами трансляторов являются компиляторы и интерпретаторы языков программирования, XML-процессоры, браузеры HTML-страниц, системы поддержки специфицирования и моделирования, текстовые процессоры и издательские системы, серверы запросов СУБД и проч. Трансляторы используются при разработке ПО, при организации работы сетевых приложений, при создании разного рода распределенных информационных систем и т.д. Широкое распространение трансляторов, их повсеместное как прямое, так и косвенное использование в различных областях компьютерной индустрии, включая критические, обуславливает высокие требования к качеству трансляторов. Существует много разных подходов к достижению качества программного обеспечения (ПО). Одним из подходов является проведение аналитического доказательства корректности ПО. Вопросам теоретического обоснования корректности поведения компиляторов на основе использования различных логических исчислений посвящены, например, работы [3], [15], [16]. Однако, 65
Ввиду сложности и насыщенности современных формальных языков, для которых разрабатываются трансляторы, очень велики объемы добротных наборов тестов и сложность анализа результатов тестирования. Поэтому очень актуальлна проблема автоматизации как процесса создания тестовых данных, так и процедуры вынесения вердикта. Наиболее многообещающим в плане автоматизации подходом является тестирование на основе формальных спецификаций и моделей [13]. В трансляторах традиционно выделяют следующие функциональные блоки: front-end, осуществляющий синтаксический разбор и семантический анализ входного текста и построение его внутреннего представления, и back-end, осуществляющий дополнительный анализ входных данных, возможно, некоторые оптимизирующие преобразования, а также собственно генерацию выходного представления (см. Рис. 1). Входной текст Транслятор Front-end Синтаксический разбор
Семантический анализ
Back-end Анализ и оптимизация
Генерация выходного текста
Выходное представление
Рис. 1. Функциональные блоки транслятора Тестирование сложных программных систем нужно проводить как на системном уровне, так и на уровне отдельных модулей и блоков и на уровне проверки отдельных аспектов функциональности. В настоящее время известно 66
большое количество исследовательских работ, посвященных тестированию отдельных функциональных блоков трансляторов (см. раздел 5). Можно утверждать, что для блоков, входящих во front-end транслятора, если рассматривать эти блоки как булевские функции, существуют приемлемые подходы к автоматизации их тестирования — систематической генерации тестов и анализу результатов работы тестируемой системы (см. [2], [4], [9], [14], [17], [21]). В вопросе проверки качества работы back-end’а можно утверждать, что в настоящее время существуют удовлетворительные подходы к решению проблемы автоматического построения тестовых данных (см. [6], [7], [22], [23]). Что же касается проблемы оракула при тестировании backend’а, то существующие практически применимые подходы основаны на идее анализа наблюдаемого поведения выполнения результата трансляции, и таким образом применимы лишь к компиляторам и интерпретаторам (см. [5], [6], [7], [10], [11], [12], [22]). Однако существует большой класс трансляторов, которые продуцируют «неисполнимые» тексты, а значит, к ним не применимы имеющиеся подходы к решению проблемы оракула. В настоящей статье предлагается подход к автоматическому построению тестов для трансляторов, обеспечивающий решение проблемы оракула для генератора выходного представления. Статья состоит из введения, пяти разделов и списка литературы. В первом разделе приводятся предварительные сведения, касающиеся подхода к генерации тестов для оптимизаторов на основе моделей. Во втором разделе дается описание предлагаемого в настоящей статье подхода. В третьем разделе рассматривается пример применения предлагаемого подхода на практике. В четвертом разделе приводится обзор близких работ. В заключение работы обсуждаются достоинства и недостатки, а также направления дальнейшего развития предлагаемого подхода.
2. Предварительные сведения В работе [22] предложен подход к автоматическому тестированию оптимизирующих компиляторов на основе моделей. Модель строится на основе абстрактного описания алгоритма заданной оптимизации. Алгоритм оптимизации формулируется с использованием терминов, обозначающих сущности некоторого подходящего абстрактного представления программы, такого как граф потока управления, граф потока данных, таблица символов и пр. Оптимизатор для осуществления своих трансформаций ищет сочетания сущностей абстрактного представления программы, которые удовлетворяют некоторым шаблонам (например, наличие в программе циклов, наличие в теле цикла конструкций с определенными свойствами, наличие в процедуре общих подвыражений, наличие между инструкциями зависимости данных некоторого вида и пр.). 67
После анализа алгоритма строится модель: каждому термину соответствует свой элемент модели, причем элементы модели могут путем связывания друг с другом образовывать структуры, соответствующие шаблонам. Процесс генерации тестов осуществляется следующим образом. Перебираются (итерируются) различные модельные структуры, которые потом отображаются (меппируются) в соответствующие программы на входном языке компилятора (см. Рис. 2).
Рис. 2. Общая схема работы генератора тестов Такой генератор дает возможность получать тесты на исходном языке, в которых присутствуют конструкции, соответствующие выделенным терминам, в различных комбинациях при минимальном содержании других конструкций языка. Таким образом, генератор оказывается «нацелен» на создание тестов для тестирования именно заданного оптимизатора. В рамках этого подхода оракул проверяет только сохранение семантики выполнения программы во время оптимизации. Для этого в качестве тестовых воздействий на оптимизатор генерируются такие программы, семантика выполнения которых полностью представляется их трассой. Такое свойство тестов позволяет свести задачу проверки сохранения семантики к сравнению трассы выполнения откомпилированного теста с некоторой эталонной трассой. Описанный подход успешно применялся для генерации тестов не только для тестирования компиляторов языков программирования, но также для тестирования оптимизаторов графических моделей [23], реализации протокола IPv6, подсистемы построения отчетов в биллинговой системе, обработчиков XML-логов [18]. 68
3. Построение оракула для генератора кода В подходе к построению оракула для тестированию генераторов кода в трансляторах, предлагаемом в настоящей работе, реализуется классическая идея сравнения реального результата работы тестируемой системы с ожидаемым результатом [1]. В качестве основы для организации тестирования применяется модельный подход, описанный выше в предварительных сведениях. Однако в нашем случае из каждой модельной структуры генератор тестов кроме собственно входных тестовых данных должен создавать также и ожидаемый эталонный результат трансляции — либо полностью, либо лишь некоторую абстрактную выжимку, своего рода систему показателей, характеризующих ключевые свойства выходных данных транслятора. Для этого генератор тестов должен содержать два разных меппера: один для отображения модельной структуры во входные тестовые данные, другой для ее отображения в эталонную систему показателей выходных данных (см. Рис. 3).
представления, оказывается, что одна и та же модель пригодна для моделирования как входных, так и выходных данных. В случае же, когда транслятор сначала осуществляет некоторый анализ входных данных, а лишь затем генерирует выходные данные, содержащие результаты проведенного анализа, модель следует обогатить дополнительными элементами, которые представляют собой соответствующие результаты анализа. При этом итерацию модельных структур следует организовать так, чтобы элементы модели, соответствующие «результатам анализа», конструировались непосредственно в процессе итерации — это возможно, например, если организовать итерацию одних элементов модели в зависимости от уже созданного значения другого элемента модели. Далее мы будем говорить о предложенной схеме генерации тестовых данных в контексте тестирования не всего генератора кода в целом, а лишь некоторого ограниченного его аспекта, поскольку это существенно упрощает модель и облегчает разработку итераторов и мепперов. Итак, генератор тестов вместе с входными данными генерирует также соответствующую эталонную систему показателей выходных данных. При использовании такой схемы генерации тестовых данных анализ правильности работы генератора кода в трансляторе состоит в проверке результата работы транслятора на соответствие созданной генератором тестов эталонной системе показателей (см. Рис. 4).
Рис. 3. Схема генерации входных данных и эталонного результата Для того чтобы было возможно организовать два разных отображения из одной модельной структуры, модель должна строиться с учетом информации не только о входном представлении данных (см. разд. 1), но и об их выходном представлении. В простейшем случае, когда транслятор лишь «переводит» термины входного представления в какие-то соответствующие термины выходного 69
Рис. 4. Схема работы оракула Таким образом, общий процесс тестирования состоит из следующих шагов: 1. Анализируется документация на генератор кода в трансляторе, и выделяются те аспекты его работы, которые требуется протестировать;
70
2. Для каждого аспекта выделяются термины и шаблоны входных данных, строится модель; 3. В рамках данного аспекта анализируется алгоритм генерации кода, модель обогащается дополнительной информацией для моделирования результатов трансляции; 4. Разрабатывается итератор модельных структур; 5. Разрабатываются мепперы:
для отображения модельных структур в предложения входного языка;
для отображения модельных структур в эталонную систему показателей выходных данных; 6. Производится автоматическая генерация тестовых данных:
входные данные транслятора;
эталонная система показателей ожидаемых выходных данных транслятора; 7. Для каждой сгенерированной пары тестовых данных в автоматическом режиме производится оценка правильности работы генератора кода в трансляторе: входные тестовые данные подаются на вход транслятору, полученный результат работы транслятора сравнивается на соответствие эталонной системе показателей.
4. Пример В качестве примера применения предложенного подхода рассмотрим тестирование генератора кода в трансляторе1, который отображает описание схемы БД на языке SQL в описание структуры таблиц на языке RelaxNG [24] и в описание ограничений, наложенных на эту структуру, на языке XML. Ниже описаны модели для трех аспектов работы данного генератора кода: 1. Трансляция основных конструкций структуры БД (таблицы, столбцы, типы данных) без каких-либо ограничений; 2. Трансляция ссылочных ограничений; 3. Трансляция ограничений вида «CHECK». Модели структур входных данных задаются в виде формальных описаний на языке TreeDL [19]. Генераторы тестов разрабатывались на языке
1
Этот транслятор разрабатывался в ИСП РАН в рамках проекта по созданию инструментальной поддержки для автоматической генерации тестовых данных сложной структуры [20]. 71
программирования Java с использованием инструмента OTK (см. [8], [22]), который предоставляет поддержку описанного метода разработки тестов. В первой модели, описывающей структуру БД без ограничений, моделируются понятия: таблицы, столбцы, типы данных SQL и их параметры. UML-диаграмма для этой модели приведена в приложении (см. Рис. 5). В качестве эталонной системы показателей для этого аспекта использовалось описание структуры БД на языке RelaxNG в том виде, в котором его должен сгенерировать транслятор. В данном случае одна и та же модель подходит для моделирования как входных, так и выходных данных: транслятор просто «переводит» такие понятия, как «таблица», «столбец» и т.д., в описание элементов с соответствующими тегами на языке RelaxNG. Работа оракула заключается в текстуальном сравнении RelaxNG-файла – результата работы транслятора с эталонным RelaxNG-файлом. Во второй модели, описывающей ссылочные ограничения, моделируются понятия: таблица, ссылка между двумя таблицами. UML-диаграмма для этой модели приведена в приложении (см. Рис. 6). В качестве эталонной системы показателей для этого аспекта использовалось описание ограничений на языке XML в том виде, в котором его должен сгенерировать транслятор. Для обеспечения корректной генерации входных тестовых данных модель была дополнена информацией о столбцах, а также был введен объемлющий модельный элемент, моделирующий схему БД в целом. При итерации структур зависимостей между таблицами в генераторе тестов применялась вспомогательная абстрактная модель, которая моделирует ациклические графы. Эти графы использовались в качестве «макета» для построения конкретных модельных структур. Работа оракула заключается в текстуальном сравнении XML-файла – результата работы транслятора с эталонным XML-файлом. Третья модель, описывающая ограничения вида «CHECK», моделирует следующие понятия: логические выражения, SQL-предикаты (такие, как «LIKE», «BETWEEN» и т.д.). UML-диаграмма для этой модели приведена в приложении (см. Приложение А). В качестве эталонной системы показателей для этого аспекта использовалось описание ограничений на языке XML в том виде, в котором его должен сгенерировать транслятор. Для обеспечения корректной генерации входных тестовых данных модель была дополнена информацией о столбцах, таблицах и объемлющей схеме, а также, для обеспечения корректности применения конкретных SQLпредикатов к данным, были добавлены типы данных. 72
Работа оракула заключается в текстуальном сравнении XML-файла – результата работы транслятора с эталонным XML-файлом. Отметим, что та часть третьей модели, которая отвечает за моделирование типов данных, была целиком переиспользована из первой модели. Более того, были переиспользованы и соответствующие компоненты генератора тестов. В Таб. 1 приведены размеры формальных описаний разработанных моделей, а также размер кода разработанных компонентов генераторов тестов. Отметим, что подавляющее большинство компонентов генераторов тестов было сгенерировано автоматически.
Модель Структура БД Ссылочные ограничения Ограничения вида «CHECK»
Размер Количество модели элементов (байт / модели строк) 4519 / 36 314
Количество классов
Размер классов (Кбайт / строк)
Всего Вручную Всего Вручную 48
2
6
1165 / 65
16
7
51
6456 / 444
73
7
95 / 2669 25 / 847 148 / 4039
Модель Ограничения вида «CHECK»
33
3816 / 276
Количество классов
10 / 378 20 / 679
Размер классов (байт / строк)
Всего Вручную Всего Вручную 37
2
60 / 1717
Структура БД Ссылочные ограничения Ограничения вида «CHECK»
Количество тестов
Общий объем (Кбайт)
Средний размер теста (байт / строк)
151
826
5471 / 140
38
353
9297 / 235
46
915
19891 / 455
Таб. 3. Некоторые характеристики сгенерированных тестов.
В Таб. 2 приведены размеры переиспользования формальных описаний разработанных моделей, а также размер переиспользованного кода разработанных компонентов генераторов тестов. Как видно из этой таблицы, в модели, описывающей ограничения вида «CHECK», около 60% кода описания модели и около 40% кода компонентов генератора тестов переиспользуется из других моделей. Размер модели (байт / строк)
Модель
8 / 271
Таб. 1. Размер кода разработанных моделей и генераторов тестов.
Количество элементов модели
В Таб. 3 приведены некоторые характеристики сгенерированных тестов для рассмотренных моделей. Как видно из этой таблицы, объем разработанного вручную кода компонентов генераторов тестов примерно на два порядка меньше объема тестов, сгенерированных этими генераторами.
8 / 271
Таб. 2. Размер переиспользованного кода разработанных моделей и генераторов тестов.
73
В результате тестирования рассмотренного в этом примере транслятора с помощью предложенного в данной статье подхода на тестах, сгенерированных на основе представленных в здесь моделей, было обнаружено несколько несоответствий между документацией и реализацией генератора кода в трансляторе. Эти несоответствия связаны с представлением в выходных данных транслятора параметров типов данных и параметров ограничений.
5. Близкие работы Вопросу автоматического тестирования блока синтаксического разбора посвящены работы [4], [9], [14], [21]. Автоматическое тестирование блока анализа статической семантики рассматривалось в [2], [17]. Для этих блоков, понимаемых как булевские функции, в перечисленных работах предлагаются вполне удовлетворительные подходы к автоматизации их тестирования — как систематической генерации тестов, так и анализу результатов работы. Для решения задачи автоматической генерации тестов для back-end’а предлагаются следующие подходы. Один — это генерация различных синтаксически корректных предложений целевого языка с последующим отбором корректных программ методом проверки сгенерированного предложения на соответствие ASM спецификации (см. [6], [7]). Достоинство этого подхода состоит в использовании формальной спецификации для описания класса корректных программ. Однако недостатком подхода является несистематичность и неэффективность процесса построения тестов. Другой подход состоит в генерации тестов на основе модели входных данных backend’а в соответствии с некоторым критерием тестового покрытия, формулируемым в терминах модели (см. [22], [23]). Достоинствами подхода 74
являются использование формальных моделей данных и эффективная генерация тестов. В качестве недостатка можно указать необходимость ручной разработки некоторых компонентов генератора тестов. Для решения задачи построения автоматического оракула для back-end’а предлагаются следующие подходы. Ряд исследователей рассматривает способы построения оракулов для тестирования некоторых специальных видов оптимизаторов, основанные на анализе абстрактного внутреннего представления программы (см. [11], [12]). Их достоинство состоит в четкой нацеленности на проверку оптимизатора. Однако недостатком подобных подходов является то, что, как правило, при тестировании реального компилятора внутреннее представление программы недоступно извне. Попытка избавиться от этого недостатка основана на идее инструментирования объектного кода компилятором [5]. Однако здесь проблема заключается в том, что для реализации этой идеи требуется вмешательство непосредственно в работу компилятора, что обычно невозможно сделать при тестировании реального коммерческого компилятора. Принципиально другим подходом к построению автоматического оракула для back-end’а является предлагаемый в ряде работ оракул, который проверяет сохранение семантики программы в процессе компиляции путем сравнения поведения откомпилированной программы с некоторым эталоном. Имеются исследования, в которых в качестве эталона рассматривается поведение той же программы, но откомпилированной другим «эталонным» компилятором [10]. Близкий подход состоит в сравнении трассы выполнения теста с эталонной трассой [22]. В других исследованиях в качестве эталона рассматривается поведение той же программы, интерпретируемой ASM машиной (см. [6], [7]). Эти подходы являются вполне приемлемыми, однако они годятся лишь для случая тестирования компиляторов и интерпретаторов.
6. Заключение В статье предложен общий подход к построению автоматического оракула для тестирования генераторов кода в трансляторах текстов на формальных языках, а также предложена инструментальная поддержка для практического использования этого подхода. Архитектура оракула основана на идее одновременной генерации как тестовых входных данных для транслятора, так и соответствующей эталонной системы показателей, характеризующих ключевые свойства выходных данных транслятора. Несмотря на то, что в ходе разработки необходимых компонентов генератора тестов фактически требуется разработка своего рода «эталонного генератора кода», количество требуемых для этого ресурсов, как правило, существенно меньше того количества ресурсов, которое требуется на разработку реального 75
генератора кода в тестируемом трансляторе. Экономия достигается за счет следующих факторов: модель, для которой требуется разработать «эталонный генератор кода», является абстракцией исходного языка выходных данных транслятора, для которого разрабатывается реальный генератор кода, и поэтому, как правило, модель существенно меньше и проще реального языка; разрабатываемый «эталонный генератор кода» должен, на самом деле, генерировать не все выходные данные, а лишь абстрактную систему показателей, объем данных которой, как правило, гораздо меньше; при разработке генераторов тестов для нескольких аспектов тестирования одного транслятора одни и те же термины часто присутствуют в моделях для нескольких аспектов тестирования, что обуславливает высокий процент переиспользования кода компонентами генераторов тестов. Направлениями дальнейшего развития предложенного подхода являются: проработка методов построения модели на основе анализа входных и выходных данных транслятора; разработка методов выходных данных;
выделения
эталонных
систем
показателей
разработка методов сравнения реального результата работы транслятора на соответствие эталонной системе показателей выходных данных для различных практически используемых частных видов языков выходных данных трансляторов. Литература [1] B. Beizer. Software Testing Techniques. // van Nostrand Reinhold, 1990. [2] A.G. Duncan, J.S.Hutchison. Using Attributed Grammars to Test Designs and Implementation // In Proceedings of the 5th international conference on Software engineering. Piscataway, NJ, USA: IEEE Press, 1981. 170–178. [3] J. Hannan, F. Pfenning. Compiler Verification in LF. // Proc. 7th Annual IEEE Symposium on Logic in Computer Science (1992) 407–418. [4] J. Harm, R. Lämmel. Two-dimensional Approximation Coverage // Informatica Journal. 24. 2000. № 3. [5] C. Jaramillo, R. Gupta, M.L. Soffa. Comparison Checking: An Approach to Avoid Debugging of Optimized Code. // ACM SIGSOFT 7th Symposium on Foundations of Software Engineering and European Software Engineering Conference, LNCS, 1687 (1999) 268–284. [6] A. Kalinov, A. Kossatchev, M. Posypkin, V. Shishkov. Using ASM Specification for automatic test suite generation for mpC parallel programming language compiler. // Proc. Fourth International Workshop on Action Semantic, AS'2002, BRICS note series NS-02-8 (2002) 99–109. [7] A.S. Kossatchev, P. Kutter, M.A Posypkin. Automated Generation of Strictly Conforming Tests Based on Formal Specification of Dynamic Semantics of the
76
[8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18]
[19] [20] [21] [22] [23] [24]
Programming Language // Programming and Computing Software. July 2004. Volume 30. Issue 4. 218–229. V. Kuliamin, A. Petrenko. Applying Model Based Testing in Different Contexts. // Proceedings of seminar on Perspectives of Model Based Testing, Dagstuhl, Germany, September 2004. R. Lämmel. Grammar testing // In Proc. of Fundamental Approaches Software Engineering. 2029. 2001. 201–216. W. McKeeman. Differential testing for software. // Digital Technical Journal, 10(1):100–107, 1998. T.S. McNerney. Verifying the Correctness of Compiler Transformations on Basic Blocks using Abstract Interpretation. // In Symposium on Partial Evaluation and Semantics-Based Program Manipulation (1991) 106–115. G. Necula. Translation Validation for an Optimizing Compiler. // Proc. ACM SIGPLAN Conference on Programming Language Design and Implementation (2000) 83–95. A.K. Petrenko. Specification Based Testing: Towards Practice // LNCS 2244. 2001. 287–300. P. Purdom. A Sentence Generator For Testing Parsers // BIT. 1972. № 2. 336–375. M. Wand, Zh. Wang. Conditional Lambda-Theories and the Verification of Static Properties of Programs. // Proc. 5th IEEE Symposium on Logic in Computer Science (1990) 321–332. M. Wand. Compiler Correctness for Parallel Languages. // Conference on Functional Programming Languages and Computer Architecture (FPCA) (1995) 120–134. М.В. Архипова. Генерация тестов для семантических анализаторов // Вычислительные методы и программирование. 2006, том.7, раздел 2, 55–70. А.В. Баранцев, И.Б. Бурдонов, А.В. Демаков, С.В. Зеленов, А.С. Косачев, В.В. Кулямин, В.А. Омельченко, Н.В. Пакулин, А.К. Петренко, А.В. Хорошилов. Подход UniTesK к разработке тестов: достижения и перспективы. // Труды ИСП РАН, Москва, 2004, т. 5, 121–156. А.В. Демаков. TreeDL: язык описания графовых структур данных и операций над ними. // Вычислительные методы и программирование. 2006, том 7, раздел 2, 117– 127. А.В. Демаков, С.В. Зеленов, С.А. Зеленова. Генерация тестовых данных сложной структуры с учетом контекстных ограничений. // Труды ИСП РАН, Москва, 2006, т. 9, 83–96. С.В. Зеленов, С.А. Зеленова. Генерация позитивных и негативных тестов для парсеров // Программирование, том. 31, №6, 2005, 25–40. С.В. Зеленов, С.А. Зеленова, А.С. Косачев, А.К. Петренко. Применение модельного подхода для автоматического тестирования оптимизирующих компиляторов // http://www.citforum.ru/SE/testing/compilers/ С.В. Зеленов, Д.В. Силаков. Автоматическая генерация тестовых данных для оптимизаторов графических моделей. // Труды ИСП РАН, Москва, 2006, т. 9, 129– 141. RelaxNG. // http://relaxng.org/
Приложение A. UML-диаграммы моделей
Рис. 5. Модель структуры БД (без ограничений)
Рис. 6. Модель ссылочных ограничений в SQL
77
78
Об одном методе сокращения набора тестов Д.Ю. Кичигин Аннотация. В статье излагается метод сокращения набора тестов для регрессионного тестирования, заключающийся в построении модели поведения программы на наборе тестов и последующей фильтрации тестового набора с помощью построенной модели. Модель поведения программы строится в терминах последовательностей системных вызовов, совершенных программой во время своего выполнения.
1. Введение Темпы развития современного программного обеспечения и особенно увеличение количества вносимых в него модификаций приводят к увеличению затрат на проведение регрессионного тестирования. В связи с этим достаточно актуальной является проблема уменьшения стоимости регрессионного тестирования. Сокращение набора тестов [1,10,13] является одним из способов уменьшения затрат на проведение регрессионного тестирования. Целью задачи сокращения набора тестов является создание набора тестов меньшего размера, но при этом, желательно, с таким же уровнем обнаружения ошибок [1]. Такое сокращение позволяет уменьшить затраты на сопровождение тестового набора и на выполнение на нем тестируемого программного обеспечения [1], что, в свою очередь, позволяет сократить затраты на проведение регрессионного тестирования в целом. Традиционный подход к решению задачи сокращения набора тестов заключается в «отсеивании» тестов из первоначального набора таким образом, чтобы сохранялся уровень его адекватности в терминах некоторого выбранного критерия адекватности, и основывается на использовании статического анализа и/или инструментирования исходного текста программного обеспечения (ПО). При таком подходе решение задачи, как правило, состоит из трех основных этапов. На первом этапе происходит инструментирование кода тестируемого ПО и запуск ПО на наборе тестов. На втором этапе собирается информация о достигнутом покрытии кода. Наконец, на третьем этапе происходит сокращение набора тестов на основе собранной информации о покрытии [1].
79
Современные тенденции разработки программного обеспечения ставят новые условия для методов сокращения набора тестов и во многом ограничивают область применения существующих методов. Во-первых, растет популярность использования готовых программных компонентов. Такие компоненты, как правило, поставляются без исходного текста, что ограничивает применимость методов, основанных на анализе или инструментировании исходного кода программы [1]. Во-вторых, программное обеспечение зачастую разрабатывается с использованием разных языков программирования, что ограничивает доступность средств анализа и инструментирования исходного кода, так как многие из них ориентированы на работу с конкретным языком программирования [1]. В третьих, возрастающая сложность современного программного обеспечения, особенно увеличение его модульности, делает неудобным и повышает затраты на использование средств инструментирования кода, так как в этом случае увеличивается количество инструментируемых модулей и, соответственно, возрастает сложность анализа покрытия. Рассмотренные обстоятельства делают актуальной разработку новых методов, способных работать в указанных условиях. В данной работе предлагается новый метод сокращения набора тестов, основанный на анализе поведения программы на наборе тестов и использующий для этого короткие последовательности системных вызовов. Предлагаемый метод не зависит от языка программирования, на котором производилась разработка ПО, не требует доступа к исходному коду и не предполагает инструментирования кода ПО, что существенно расширяет область его применения и облегчает использование. Статья организована следующим образом. В разделе 2 рассмотрены основные существующие методы сокращения набора тестов. Раздел 3 посвящен описанию предлагаемого метода сокращения набора тестов. В разделе 4 приводится описание проведенного эксперимента и его результатов. В конце работы делаются выводы и рассматриваются направления будущих исследований.
2. Существующие решения Существующие методы сокращения набора тестов [1,10,11,12,13] объединяет общий подход, который состоит в построении набора тестов меньшего размера, но при этом эквивалентного первоначальному набору в терминах выбранного покрытия кода программы. В результате полученный сокращенный набор тестов замещает собой первоначальный и используется для проведения регрессионного тестирования. Существующие методы сокращения набора тестов в основном различаются видом используемого покрытия. В работе [13] для сокращения набора тестов используется покрытие «всех использований» («all-uses» coverage). Это покрытие является частным случаем покрытия ассоциаций вида определение80
использование (definitions-use associations coverage) основанного на модели потока данных в программе [15]. Анализ этого покрытия основывается на изучении вхождений переменных в программу и рассмотрении путей выполнения программы от места определения переменной (variable definition) до места ее использования (variable use). В [10] рассматривается метод сокращения наборов на основе покрытия ребер1 гипотетического графа потока управления программы. Для анализа покрытия ребер используется исходный код программы [14]. В работе [12] для сокращения набора тестов используется покрытие MC/DC (Modified Condition/Decision Coverage). Покрытие MC/DC определяется в терминах языка программирования, используемого при написании программы, и является развитием покрытия условий (conditions coverage) [16]. В [11] предлагается метод сокращения набора тестов, основанный на комбинации покрытия блоков программы и ассоциированных с выполнением теста ресурсозатрат. Блоком называется последовательность выражений программы, не содержащая ветвлений; таким образом, покрытие блоков эквивалентно покрытию выражений. Общей особенностью перечисленных подходов является то, что для их применения необходим доступ и инструментирование исходного либо объектного кода программы, что подвергает эти методы указанным во введении ограничениям. Немного особняком стоит работа [1], где авторы в качестве элементов покрытия используют стек вызовов функций программы (call stack) и указывают, что такой подход может применяться и без доступа к исходному коду программы. Однако в эксперименте, проведенном авторами этой работы, для мониторинга стека вызовов функций требуется знать сигнатуры внутренних функций программы, для получения которых используется доступ к исходному тексту программы. Поэтому, по крайней мере, пока также нельзя говорить, что данная методика применима в условиях указанных выше ограничений.
системы2. Метод не зависит от языка программирования, использованного для разработки тестируемого программного обеспечения, не требует доступа к исходному коду и не предполагает инструментирования кода программного обеспечения.
3.1. Системные вызовы программы
1
Критерий покрытия ребер похож на критерий покрытия ветвлений (decision coverage), но сформулирован в терминах графа потока управления [10]. 81
характеристика
поведения
Системными вызовами (system call) называются функции, входящие в состав программного интерфейса операционной системы. Изначально последовательности системных вызовов были предложены в качестве характеристики поведения программы при решении задачи обнаружения вторжений [2,3,4,5] в области компьютерной безопасности. Последовательности системных вызовов также успешно использовались при поиске потенциальных ошибок в программном обеспечении [17]. При этом было отмечено, что последовательности системных вызовов, произведенных программой во время своего выполнения, являются достаточно информативной характеристикой выполнения программы и позволяют различать, когда выполнение программы идет по разным путям [2]. В данной работе это свойство последовательностей системных вызовов используется для анализа поведения программы на наборе тестов. Мониторинг системных вызовов, вызываемых программой во время своего выполнения, осуществляется командой ktrace, входящей в пакет операционной системы. Результатом работы этой программы является трасса системных вызовов, выполненных программой во время своей работы. На Рис.1 приведен пример трассы для фрагмента программы. … int fd = open(…); for (int i=0; i<3; i++) { read(fd,…); } close(fd); …
3. Описание метода В этой работе рассматривается тестирование программного обеспечения, работающего под управлением операционной системы семейства UNIX. Предлагаемый метод основывается на построении моделей поведения программы на наборе тестов и последующем использовании их для сокращения набора тестов. Для построения модели поведения программы используются последовательности системных вызовов, выполненные программой во время своей работы. Для мониторинга выполнения системных вызовов используется команда ktrace, входящая в состав операционной
как
… open read read read close …
Рис.1 Представление поведения программы с помощью системных вызовов. 2
Хотя в данной работе рассматривается случай операционной системы семейства UNIX, в состав которой входит команда мониторинга системных вызовов, предлагаемый метод может быть распространен на любую операционную систему, имеющую аналогичные средства мониторинга. Например, метод также может применяться в операционных системах семейства Linux, где для сбора трасс системных вызовов существует команда strace. 82
3.2. Модель поведения программы на тесте В предлагаемом методе последовательности системных вызовов используются для построения модели поведения программы на тесте. Для начала введем несколько определений. Трассой системных вызовов программы P для теста t будем называть последовательность системных вызовов, совершенных программой P при выполнении на входных данных тестового случая t. При этом будем подразумевать, что системные вызовы встречаются в трассе в порядке их вызова программой P3. Последовательностью системных вызовов длины K называется любая непрерывная подпоследовательность длины K, встречающаяся в трассе системных вызовов. Множеством последовательностей системных вызовов длины K, соответствующим поведению программы P на тесте t, называется множество всех возможных последовательностей длины K, встречающихся в трассе системных вызовов программы P для теста t. Для получения множества последовательностей системных вызовов фиксированной длины используется техника «скользящего окна» [3] с размером K (размер окна соответствует длине выделяемых последовательностей). Согласно этому подходу, выделение последовательностей происходит следующим образом: в качестве первой последовательности из трассы выбираются K идущих подряд системных вызовов, начиная с первого вызова в трассе; в качестве второй последовательности выбираются K идущих подряд системных вызовов, начиная со второго вызова; и так далее, пока не будет пройдена вся трасса. В качестве модели поведения программы P на тесте t принимается определенное выше множество последовательностей. Рассмотрим пример построения модели поведения программы. Допустим, у нас есть следующая последовательность, состоящая из системных вызовов операционной системы UNIX:
Полученное таким образом множество последовательностей будет являться моделью поведения программы.
3.3. Метод сокращения набора тестов Идея предлагаемого метода сокращения набора тестов достаточно проста. Можно предположить, что при тестировании функциональности и структуры программы встречаются тесты, которые инициируют одни и те же последовательности системных вызовов. Метод основан на этом предположении и состоит в «отсеивании» тех тестов, которые не генерируют новые последовательности системных вызовов, то есть не инициируют нового поведения программы. В предлагаемом методе сокращения набора тестов используется модель поведения программы, построенная в предыдущем разделе. Общий алгоритм работы метода выглядит следующим образом (см. Рис.2):
t := next(T)
Mt := build_model(t)
? Mt MT’
MT’ := MT’ Mt T’ := T’ t
Mt MT’ no
open write write open write close write close.
? T == yes
Тогда результатом выделения последовательностей с помощью скользящего окна размера 4 будет следующее множество последовательностей: open write write open write write open write write open write close open write close write write close write close 3
Mt MT’
finish
Рис.2 Алгоритм работы метода. Здесь T – первоначальный набор тестов; T’ – сокращенный набор тестов, T’ – модель T; t – очередной тест из первоначального набора, t T; Mt поведения программы на тесте t; MT’ – множество моделей поведения программы на тестах из T’.
Команда ktrace предоставляет такую возможность. 83
84
1.
На первом этапе выбирается очередной тест из сокращаемого набора тестов и на нем выполняется тестируемая программа. 2. Во время выполнения программы строится модель Mt ее поведения на выбранном тесте t. 3. На третьем этапе проверяется, входит ли модель Mt в множество моделей MT’ или нет. 4. Если модель Mt не входит в множество моделей MT’, то она добавляется в множество MT’, и тест t добавляется в сокращенный набор тестов T’. 5. Если набор тестов T не исчерпан, то осуществляется переход к шагу 1, в противном случае сокращенный набор тестов T’ считается построенным, и работа метода завершается. Для того чтобы реализовать метод, необходимо предложить операцию сравнения моделей поведения программы. Такая операция должна показывать, когда модели поведения программы совпадают, а когда нет. Будем считать, что модели поведения программы равны, когда их множества последовательностей совпадают, и различны в противном случае. Тогда операция сравнения моделей будет сводиться к операции сравнения множеств последовательностей системных вызовов. Надо заметить, что использование в предложенном методе множества последовательностей системных вызовов вместо трасс системных вызовов позволяет решить проблему циклов в тестируемой программе. Данная проблема изначально возникла при оценке адекватности наборов тестов, основанной на анализе покрытия путей выполнения программы, и состоит в том, что если код программы содержит циклы, то количество различных путей выполнения программы может быть очень большим, что приводит к падению эффективности метода оценки и значительному увеличению затрат на его работу [15]. Аналогичная проблема возникает и в рассматриваемом методе, если в качестве модели поведения программы рассмотривать трассы системных вызовов. В этом случае, если системные вызовы встречаются внутри циклов программы, различное количество возможных повторений цикла может привести к очень большому количеству разных трасс программы. Такая ситуация может исказить результаты сокращения тестового набора, так как поведения программы, различающиеся только количеством выполненных циклов, будут приняты как различные, хотя фактически они не тестируют новую функциональность или структуру программы. Использование множества последовательностей системных вызовов решает эту проблему, так как, даже при произвольном количестве повторений цикла, множество уникальных поведений программы быстро стабилизируется, и модель перестает изменяться.
85
4. Экспериментальное исследование сокращения набора тестов
метода
Целью проводимого эксперимента являлось определение и оценка параметров метода сокращения набора тестов, предложенного в данной работе, на модельных данных. При анализе методов сокращения набора тестов важны следующие параметры: 1. Степень сокращения набора. Это один из наиболее существенных параметров работы метода сокращения, так как этот параметр показывает, насколько сокращаются первоначальный набор тестов и связанные с ним затраты. Чем больше степень сокращения набора тестов, тем сильнее уменьшаются затраты на его выполнение, проверку и сопровождение, тем самым, сокращая затраты на проведение регрессионного тестирования в целом [10]. 2. Уровень обнаружения ошибок. Основной проблемой при сокращении набора тестов является возможное уменьшение его уровня обнаружения ошибок вследствие удаления тестов, обнаруживающих ошибки [10]. Слишком большие потери в уровне обнаружения ошибок могут привести к нецелесообразности сокращения набора тестов. Поэтому важно, чтобы в результате работы метода его уровень обнаружения ошибок по возможности не уменьшался, а если и уменьшался, то незначительно. Целью эксперимента являлось решение следующих задач: 1. определение величины сокращения исходного набора тестов; 2. определение уровня обнаружения ошибок сокращенным набором тестов; 3. сравнение уровня обнаружения ошибок сокращенным набором тестов с уровнем первоначального набора и уровнем, обеспечиваемым методом случайного сокращения набора тестов;
4.1. Условия проведения эксперимента: программы и тестовые наборы
тестовые
Эксперимент проводился в операционной системе FreeBSD 5.3; для сбора трасс использовалась команда ktrace, входящая в состав операционной системы. Длина K используемых последовательностей составляла 15 системных вызовов. Для проведения эксперимента в качестве объекта тестирования использовались программа nametbl, входящая в пакет программ, впервые предложенный в [6] и впоследствии использовавшийся в [7,8,9] для исследования методов оценки адекватности тестовых наборов. Программа nametbl считывает команды из файла и выполняет их с целью тестирования нескольких функций. Тестируемые функции реализуют работу с таблицей символов для некоторого языка программирования. Таблица символов хранит для каждого символа его имя и тип. Команды, считываемые из файла, 86
множество моделей), то для достижения чистоты эксперимента тесты исходного набора были перенумерованы случайным образом. После этого тестируемая программа была выполнена на каждом тесте из пула, и для каждого теста была построена модель выполнения программы. Затем из пула сгенерированных тестов методом случайной выборки было сформировано 35 исходных наборов тестов разного размера, начиная с 50 и заканчивая 1700 с шагом 50. В качестве последнего, 35-го набора был принят набор, содержащий все 1715 тестов из пула. Далее, из исходных наборов с помощью исследуемого метода были построены сокращенные наборы, для каждого из которых были вычислены параметры сокращения: размер сокращенных наборов и коэффициент сокращения. После этого были вычислены уровни обнаружения ошибок сокращенными наборами, и затем результаты были сравнены с уровнями исходных наборов и уровнями, получаемыми при использовании метода случайного сокращения. Для этого из исходных наборов методом случайной выборки были построены наборы такого же размера, как и сокращенные, и были вычислены их уровни обнаружения ошибок.
4.3. Результаты эксперимента Результаты работы метода проиллюстрированы на Рис. 3 – 6: 30
25
Размер сокращенного набора
позволяют добавить в таблицу новый символ, указать тип ранее введенного символа, осуществить поиск символа и распечатать таблицу символов. Общий объем исходного текста программы составляет 356 строк. Основной причиной выбора этой программы для проведения эксперимента послужило то, что ее исходные тексты и руководство по созданию наборов тестов находятся в открытом доступе4, и, таким образом, описываемый эксперимент может быть повторен независимо. Для построения тестов использовалось разбиение входных данных программы nametbl на домены, предложенное в работе [9]. В этой работе было предложено 4 «измерения» входных данных, в трех из которых было выделено 7 доменов, а в четвертом – 5 доменов. В данной работе было использовано это разбиение, в каждом домене было выбрано по одному значению, и в итоге был сгенерирован пул из 1715 разных тестов. Для определения параметров метода были использованы следующие индикаторы: 1. размер сокращенного набора тестов; 2. коэффициент сокращения набора тестов в процентном выражении: 100 (1 – Sizereduced/Sizeinitial), где Sizeinitial означает размер исходного набора тестов, Sizereduced – размер сокращенного набора тестов; 3. количество обнаруженных ошибок; 4. коэффициент обнаружения ошибок в процентном выражении: 100 (FaultsDetectedreduced/FaultsDetectedinitial), где FaultsDetectedinitial означает количество ошибок, обнаруживаемых исходным набором тестов, FaultsDetectedreduced – количество ошибок, обнаруживаемых сокращенным набором тестов. Уровень обнаружения ошибок измерялся исходя из следующего принципа эквивалентности тестовых наборов: два набора тестов считаются эквивалентными при обнаружении некоторой ошибки, если каждый набор содержит по крайней мере один тест, обнаруживающий эту ошибку (в англоязычной литературе такой принцип носит название per-test-suite basis [10]). Такой подход применялся в работах [1, 10].
20
15
10
5
4.2. Проведение эксперимента и результаты 0
50 10 0 15 0 20 0 25 0 30 0 35 0 40 0 45 0 50 0 55 0 60 0 65 0 70 0 75 0 80 0 85 0 90 0 95 0 10 00 10 50 11 00 11 50 12 00 12 50 13 00 13 50 14 00 14 50 15 00 15 50 16 00 16 50 17 00 17 14
Так как стратегия генерации тестовых наборов оказывает косвенное влияние на исследуемый метод (вследствие того, что индивидуальные тесты выбираются из набора в порядке их следования, и, соответственно, в сокращенный набор тестов будет помещен первый тест, расширяющий
Размер исходного набора
Рис. 3 Размер сокращенных наборов тестов.
4
Исходный текст и документация к программе доступны на сайте в интернете по адресу: http://www.chris-lott.org/work/exp/. 87
88
в пределах от 76% до 98.6% в процентном соотношении. По результатам видно, что размер сокращенного набора рос вместе с ростом размера исходного набора и в итоге стабилизировался. Это является закономерной картиной, если учесть, что множество различных путей выполнения программы конечно, и при увеличении числа тестов увеличивается вероятность выполнения всех путей программы.
120
80
120 60 100 % количества обнаруживаемых ошибок
% сокращения размера набора
100
40
20
50 10 0 15 0 20 0 25 0 30 0 35 0 40 0 45 0 50 0 55 0 60 0 65 0 70 0 75 0 80 0 85 0 90 0 95 0 10 00 10 50 11 00 11 50 12 00 12 50 13 00 13 50 14 00 14 50 15 00 15 50 16 00 16 50 17 00 17 14
0
Размер исходного набора
80
60
40
20
Рис. 4 Процент сокращения набора тестов.
95 0 10 00 10 50 11 00 11 50 12 00 12 50 13 00 13 50 14 00 14 50 15 00 15 50 16 00 16 50 17 00 17 14
90 0
80 0 85 0
75 0
70 0
65 0
60 0
55 0
50 0
45 0
40 0
35 0
30 0
25 0
15 0
50 10 0
20 0
0 25
Размер исходного набора Исследуемый метод
Метод случайного сокращения
Количество обнаруживаемых ошибок
20
Рис. 6 Процент обнаружения ошибок наборами тестов. 15
На модельных данных уровень обнаружения ошибок для сокращенных наборов составил от 95.24% до 100% от уровня исходных наборов, т.е. величина падения уровня обнаружения ошибок для исследуемого метода составила менее 4.8%. Средняя величина падения уровня обнаружения ошибок для исследуемого метода составила 1.5%. При этом исследуемый метод проиграл методу случайного сокращения лишь в одном случае; в большинстве остальных случаев исследуемый метод показал выигрыш на 4.3% - 26%. В среднем сокращенные наборы, построенные с помощью исследуемого метода, при проведении эксперимента обнаруживали на 9.8% ошибок больше, чем соответствующие наборы, полученные с помощью метода случайного сокращения.
10
5
90 0 95 0 10 00 10 50 11 00 11 50 12 00 12 50 13 00 13 50 14 00 14 50 15 00 15 50 16 00 16 50 17 00 17 14
80 0 85 0
70 0 75 0
65 0
55 0 60 0
45 0 50 0
35 0 40 0
30 0
20 0 25 0
15 0
50 10 0
0
Размер исходного набора
Исходный набор
Исследуемый метод
Метод случайного сокращения
5. Заключение и направление будущих исследований
Рис. 5 Количество ошибок, обнаруживаемых наборами тестов. Размер сокращенного набора находился в пределах от 12 до 24 тестов в зависимости от размера исходного набора. Величина сокращения находилась 89
В данной работе был представлен метод решения задачи сокращения набора тестов, возникающей в контексте регрессионного тестирования. Данный метод основан на использовании моделей поведения программы, построенных 90
в терминах системных вызовов, которые производятся программой во время своего выполнения. Работа метода была экспериментально исследована на модельных данных. Были исследованы такие параметры работы метода, как величина сокращения набора тестов и уровень обнаружения ошибок. Результаты работы метода были сравнены с результатами метода случайного сокращения набора тестов, и было показано, что исследуемый метод практически не уступает методу случайного сокращения набора тестов, а в большинстве случаев превосходит его. В будущих исследованиях планируется развивать предложенный метод и, в частности, сосредоточиться на следующих направлениях. Во-первых, необходимо более четко охарактеризовать класс программ, к которым может быть применен предложенный метод. Интуитивно понятно, что результаты работы метода зависят от внутреннего устройства программы – частоты и характера обращений тестируемой программы к системным вызовам операционной системы. Например, на преимущественно вычислительных программах, где обращения к системным вызовам происходят редко, или в программном обеспечении баз данных, где разнообразность поведения программы характеризуется скорее характером передаваемых данных, нежели разными путями в графе выполнения программы, исследуемый метод может и не показать хороших результатов. Во-вторых, имеет смысл учитывать не только имена системных вызовов, совершенных программой во время своего выполнения, но и передаваемые в них параметры и возвращаемые значения. Такая информация косвенно учитывает потоки данных в программе и будет полезна при построении модели поведения программы. Наконец, планируется сравнить результаты работы предлагаемого метода не только с методом случайного сокращения наборов тестов, но и с остальными методами, используемыми в данной области. Литература [1] McMaster, S. and Memon, A. M. 2005. Call Stack Coverage for Test Suite Reduction. In Proceedings of the 21st IEEE international Conference on Software Maintenance (ICSM'05) - Volume 00 (September 25 - 30, 2005). ICSM. IEEE Computer Society, Washington, DC, 539-548. [2] S.Forrest, S.A.Hofmeyr, A.Somayaji, and T.A.Longstaff. A Sense of Self for Unix Processes. In Proceedings of the 1996 IEEE Symposium on Research in Security and Privacy, Los Alamitos, CA, 1996. IEEE Computer Society Press. [3] S.A.Hofmeyr, S.Forrest, and A.Somayaji. Intrusion Detection using Sequences of System Calls. Journal of Computer Security, 6:151–180, 1998. [4] C. Warrender, S. Forrest, and B. Pearlmutter, Detecting Intrusions Using System Calls: Alternative Data Models. IEEE Symposium on Security and Privacy, Oakland, CA, 1999, IEEE Computer Society, pp. 133-145. [5] Eleazar Eskin, Andrew Arnold, Mechael Prerau, Leonid Portnoy, Salvatore J. Stolfo. A Geometric Framework for Unsupervised Anomaly Detection: Detecting Intrusions in Unlabeled Data. Applications of Data Mining in Computer Security, Kluwer, 2002.
91
[6] Victor R. Basili and Richard W. Selby. Comparing the Effectiveness of Software Testing Strategies. IEEE Transactions on Software Engineering, SE-13(12):1278-1296, December 1987. [7] E. Kamsties and C.M. Lott, An Emperical Evaluation of Three Defect-Detection Techniques, In: W. Schfer and P. Botella (eds.). Software Engineering - ESEC '95, Springer, 1995, pp. 362-383. [8] M. Wood, M. Roper, A. Brooks, and J. Miller. Comparing and combining software defect detection techniques:a replicated empirical study. In H. S. Mehdi Jazayeri, editor, Proceedings of The Sixth European Software Engineering Conference, volume 1301, pages 262-277. Lecture Notes in Computer Science, September 1997. [9] M. Grindal, B. Lindstrom, A.J. Offutt, and S.F. Andler. An Evaluation of Combination Strategies for Test Case Selection, Technical Report. Technical Report HS-IDA-TR-03001, Department of Computer Science, University of Skovde, 2003. [10] G. Rothermel, M. J. Harrold, J. von Ronne, and C. Hong. Empirical studies of test-suite reduction. Journal of Software Testing, Verification, and Reliability, V. 12, no. 4, December, 2002. [11] X. Ma, Z. He, B. Sheng, C. Ye. A Genetic Algorithm for Test-Suite Reduction. 2005 IEEE International Conference on Systems, Man and Cybernetics. 10-12 Oct. 2005, Vol. 1, pp: 133- 139. [12] J. A. Jones, M. J. Harrold. Test-Suite Reduction and Prioritization for Modified Condition/Decision Coverage. IEEE Transactions on Software Engineering, Vol. 29, No. 3, March, 2003, pp. 195-209. [13] M. J. Harrold, R. Gupta, and M. L. Soffa. A methodology for controlling the size of a test suite. ACM Transactions on Software Engineering and Methodology, 2(3):270-285, July 1993. [14] M. Hutchins, H. Foster, T. Goradia, and T. Ostrand. Experiments on the effectiveness of dataflow- and controlflow-based test adequacy criteria. In Proceedings of the 16th International Conference on Software Engineering, pages 191-200, May 1994. [15] H. Zhu, P. A. V. Hall, and J. H. R. May. Software Unit Test Coverage and Adequacy. ACM Computing Surveys, Vol.29, No.4, pp.366-427, December 1997. [16] Code Coverage Analysis. http://www.bullseye.com/coverage.html, 2002. [17] V. Dallmeier. Detecting Failure-Related Anomalies in Method Call Sequences. Diploma Thesis, Universität des Saarlandes, March, 2005.
92
Методика автоматизированной проверки возвращаемых кодов ошибок при тестировании программных интерфейсов К. А. Власов, А. С. Смачёв
1. Введение В современном мире компьютеры играют всё большую роль, а с ними — и программное обеспечение. В настоящее время наблюдается тенденция ко всё возрастающему усложнению и увеличению программных комплексов, состоящих из различных модулей и подсистем. Но чем больше программа, тем сложнее её отлаживать и проверять на соответствие спецификационным требованиям. Есть области, где ошибки совершенно недопустимы, например, медицина, атомная энергетика. Легко представить, что может натворить небольшая ошибка в программе, управляющей атомным реактором. Поэтому важность задачи верификации программного обеспечения трудно переоценить. Данное исследование было проведено в рамках проекта OLVER (Open Linux VERification) [1], задачей которого была разработка тестового набора, позволяющего выполнять автоматическую проверку дистрибутивов операционной системы Linux на соответствие стандарту LSB (Linux Standard Base). Тестовый сценарий заключается в вызове всех функций из тестируемой подсистемы с заданным набором параметров и проверке возвращаемых значений. Одним из важных пунктов в этом тестировании является проверка кодов ошибок, возвращаемых функциями, а именно: проверка, что функция не возвращает код ошибки, когда она не должна этого делать; проверка, что функция возвращает код ошибки, если это требуется по спецификации; проверка, что в случае ошибки возвращается именно тот код ошибки, который функция должна вернуть согласно требованиям. В процессе работы над тестовым набором задача проверки кодов ошибок возникает для большинства функций, причём обычно эта задача решается однотипно. Поэтому было решено автоматизировать написание исходного 93
кода проверок, насколько это возможно. В данной статье мы расскажем, как была решена эта задача. Во втором разделе будет дана исходная постановка задачи: какие конкретно действия должны быть автоматизированы, и как должна измениться работа по написанию тестов после внедрения этой автоматизации. В третьем разделе описаны технические подробности реализации, возникшие проблемы и их решение. И в заключении рассмотрены возможные пути развития системы в будущем.
2. Постановка задачи При написании тестов для пакета OLVER используется система CTesK [2]. Она включает в себя язык SeC [3] — спецификационное расширение языка C, а также библиотеки для работы с различными типами данных, такими как массив, список, строка и т. д. Добавление новых тестируемых функций в тестовый набор обычно выглядит следующим образом. Сначала программист читает текст стандарта и размечает его, выделяя атомарные требования при помощи конструкций языка HTML и присваивая им уникальные идентификаторы. Затем при открытии в браузере веб-страницы с текстом стандарта автоматически запускается скрипт, написанный на языке JavaScript, который анализирует список отмеченных требований, строит по нему шаблонный код на языке SeC и отображает его на странице. Теперь программисту достаточно просто скопировать этот код в SEC-файлы проекта, и бо́льшая часть рутинной работы оказывается выполненной. Дальнейшая работа — это, в основном, создание тестовых сценариев и написание непосредственно кода проверки для всех атомарных требований, т. е. творческая работа, которая практически не поддаётся автоматизации. В процессе работы над тестовым набором выяснилось, что проверки кодов ошибок, возвращаемых различными тестируемыми функциями, имеют много общего, в результате чего существенная часть времени тратится на одну и ту же работу по организации взаимодействия проверок для разных кодов ошибок друг с другом и с проверками других требований. Поэтому было решено, насколько это возможно, попытаться автоматизировать и эту задачу. Наиболее удобный путь для такого рода автоматизации — это модификация существующего скрипта, чтобы он генерировал шаблонный код ещё и для проверки ошибок. Тем самым будет сохранена общая методика работы по разметке стандарта.
3. Генерируемый SeC-код В первую очередь необходимо чётко определить, как должен выглядеть генерируемый код, что именно и как именно он будет проверять. За основу, разумеется, были взяты созданные к этому времени наработки, написанные вручную. Весь однотипный код удобно заменить набором макросов (они 94
поддерживаются в языке SeC, т. к. он включает в себя все возможности языка C). Это сделало бы исходный текст более наглядным и более простым для написания и отладки. В стандарте LSB присутствуют несколько разных типов требований на поведение функций при возникновении ошибочной ситуации. Возможные варианты поведения функции в случае ошибки могут быть описаны следующим образом: SHALL: в стандарте присутствует фраза «The function shall fail if …», т. е. стандарт требует, чтобы функция всегда обнаруживала описанную ошибочную ситуацию и возвращала в этом случае определённый код ошибки. MAY: в стандарте присутствует фраза «The function may fail if …», т. е. стандарт не требует непременного обнаружения данной ошибочной ситуации, но в случае, если функция всё же реагирует на данную ошибку, её поведение должно быть таким, как описано в стандарте. NEVER: в стандарте присутствует фраза «The function shall not return an error code …», т. е. данный код ошибки не может быть возвращён ни при каких условиях. Эти три типа требований и были взяты за основу. Проверка кодов ошибок должна производиться до начала проверки остальных требований (за исключением, быть может, самых базовых аспектов, обеспечивающих непосредственное функционирование самой тестовой системы — таких как проверки на NULL). Это связано с тем, что если произошла ошибка, то функция не выполнила требуемое от неё действие, и, следовательно, проверки функциональных требований сообщат, что функция работает некорректно, т. е. не удовлетворяет стандарту. Однако ошибка могла быть вызвана тестовым сценарием намеренно (например, специально для того, чтобы проверить, как поведёт себя функция в этом случае), и тогда сообщение о некорректности поведения функции окажется ложной тревогой. Именно поэтому в первую очередь должен отработать блок проверки ошибочных ситуаций, и только если ошибка не была возвращена (и не ожидалась!), управление передаётся дальше, на код проверки основных требований. Для удобства проверка кодов ошибок оформлена в виде блока, ограниченного операторными скобками ERROR_BEGIN и ERROR_END, которые являются макросами. ERROR_BEGIN при этом содержит параметры, глобальные для всего блока (например, в какой переменной хранится собственно код ошибки). Внутри блока располагаются индивидуальные проверки для каждого пункта, упомянутого в стандарте, которые также являются вызовами макросов с определённым набором параметров. Упомянутым выше трём основным типам требований соответствуют макросы ERROR_SHALL, ERROR_MAY и ERROR_NEVER. Помимо них, есть ещё некоторые дополнительные макросы, но о них мы расскажем ниже. 95
Приведём небольшой пример, демонстрирующий, как выглядит блок проверки кодов ошибок в одной из подсистем: Текст стандарта: The pthread_setspecific() function shall fail if: [ENOMEM] Insufficient memory exists to associate the nonNULL value with the key. The pthread_setspecific() function may fail if: [EINVAL] The key value is invalid. These functions shall not return an error code of [EINTR]. Текст спецификации: ERROR_BEGIN(POSIX_PTHREAD_SETSPECIFIC, "pthread_setspecific.04.02", pthread_setspecific_spec != 0, pthread_setspecific_spec) /* * The pthread_setspecific() function shall fail if: * [ENOMEM] * Insufficient memory exists to associate the non-NULL value with * the key. */ ERROR_SHALL(POSIX_PTHREAD_SETSPECIFIC, ENOMEM, "!pthread_setspecific.05.01", /* условие */) /* * The pthread_setspecific() function may fail if: * [EINVAL] * The key value is invalid. */ 96
ERROR_MAY(POSIX_PTHREAD_SETSPECIFIC, EINVAL, "pthread_setspecific.06.01", !containsKey_Map(thread->key_specific, key))
ERROR_END: параметров не требует.
3.1. Конфигурационные константы.
/* * These functions shall not return an error code of [EINTR]. */ ERROR_NEVER(POSIX_PTHREAD_SETSPECIFIC, EINTR, "pthread_setspecific.07") ERROR_END() Параметры макросов имеют следующий смысл: ERROR_BEGIN(ERR_FUNC, REQID, HAS_ERROR, ERROR_VAL): ERR_FUNC — идентификатор функции, используемый в качестве префикса для конфигурационных констант. Например, с префикса POSIX_PTHREAD_SETSPECIFIC начинаются соответствующие конфигурационные константы, такие как: POSIX_PTHREAD_SETSPECIFIC_HAS_EXTRA_ERROR_CODES POSIX_PTHREAD_SETSPECIFIC_FAILS_WITH_EINVAL и др. REQID — идентификатор проверяемого требования (строковая константа). HAS_ERROR — предикат, определяющий условие возникновения ошибки (булевское выражение). ERROR_VAL — код ошибки (обычно это переменная errno или возвращаемое значение функции). ERROR_MAY(ERR_FUNC, ERRNAME, REQID, ERROR_PREDICATE), ERROR_SHALL(ERR_FUNC, ERRNAME, REQID, ERROR_PREDICATE), ERROR_NEVER(ERR_FUNC, ERRNAME, REQID): ERR_FUNC — то же, что и выше. ERRNAME — имя константы, соответствующей ожидаемому коду ошибки. Например, ENOMEM, EINVAL. REQID — идентификатор проверяемого требования. ERROR_PREDICATE — предикат, определяющий условие возникновения ошибки. 97
Стандарт LSB не всегда достаточно чётко определяет ситуации ошибочного завершения функции. В нем допускается, что функции могут возвращать коды ошибок, не описанные в стандарте, а также возвращать описанные коды ошибок в каких-то иных, определяемых реализацией случаях (см. [LSB, System Interfaces, Chapter 2, 2.3 Error Numbers]). На практике же такие случаи достаточно редки. К тому же вполне возможна ситуация, когда ошибочное завершение функции с кодом, не указанным в стандарте, является не предусмотренным разработчиком случаем, а ошибкой реализации. Поэтому была добавлена возможность выбрать в каждом конкретном случае желаемый уровень тестирования — в соответствии со стандартом, или более жёстко. Соответствующие константы именуются XX_HAS_EXTRA_ERROR_CODES и XX_HAS_EXTRA_CONDITION_ON_YY, где XX — идентификатор функции, обычно передаваемый в макросы как параметр ERR_FUNC, а YY — имя константы ошибки. Ситуация с требованиями типа MAY аналогична. В соответствии со стандартом, функция может не возвращать код ошибки, даже если условие выполняется. Однако в большинстве случаев разработчики «предпочитают ясность» и возвращают указанный код, хотя стандарт и не обязывает их к этому. Поэтому была введена специальная конфигурационная константа XX_FAILS_WITH_YY, определяющая, следует ли считать требование нарушенным, если функция не возвращает код ошибки при выполнении условия типа MAY. Как можно заметить, при включённой константе XX_FAILS_WITH_YY требования SHALL и MAY по сути перестают отличаться друг от друга. Фактически, сами макросы ERROR_SHALL и ERROR_MAY реализованы как один макрос ERROR_MAY_SHALL, принимающий на вход ещё один дополнительный аргумент с именем SHALL, который и определяет, должна ли функция возвращать код ошибки, если предикат ERROR_PREDICATE истинен. Соответственно, макрос ERROR_SHALL реализован как вызов ERROR_MAY_SHALL с параметром SHALL, равным true, а ERROR_MAY — как вызов ERROR_MAY_SHALL с параметром SHALL, зависящим от значения константы XX_FAILS_WITH_YY.
3.2. Проверка В макросе ERROR_BEGIN в первую очередь проверяется, что если функция завершилась с ошибкой, то код ошибки не равен EOK (коду, обозначающему отсутствие ошибки). Обычно признаком ошибки является сам факт отличия кода от EOK, и эта проверка превращается в тавтологию. Но бывают и другие случаи, когда, например, признаком ошибки является возвращаемое значение функции, равное -1, и тогда эта проверка необходима. 98
Проверка кодов ошибок не ограничивается случаем, когда функция завершилась с ошибкой. Условие типа SHALL должно обеспечивать также проверку в обратную сторону: если условия вызова функции ошибочны, то функция обязана вернуть ошибку, и если этого не произошло, поведение считается некорректным. Заметим, что независимо от того, успешно ли отработала функция, необходимо проверять все требования к кодам возврата, поскольку выполнение одного требования ещё не означает, что другие требования не нарушены. Таким образом, решение о правильной работе функции должно приниматься только в макросе ERROR_END, не раньше. На первый взгляд, это соображение очевидно, но именно поэтому его так легко упустить из виду. Для каждого требования имеется четыре базовых случая: 1. Ошибка ожидалась и произошла. 2. Ошибка не ожидалась, но произошла. 3. Ошибка ожидалась, но не произошла. 4. Ошибка не ожидалась, и её не было. В каждом из этих случаев есть свои тонкости, которые легко упустить из виду. Возьмём, например, первый случай, когда ошибка ожидалась и произошла. Первым побуждением будет выдать вердикт, что проверка завершилась успешно, и выйти из блока проверки ошибок. Однако этого делать ни в коем случае нельзя, т. к. должны быть проверены абсолютно все требования — то самое «очевидное» соображение, высказанное выше! Ведь даже в случае возврата ожидаемого кода ошибки есть вероятность, что другое требование окажется нарушенным. То же самое относится к последнему случаю, когда ошибка не ожидалась и не произошла. Случай второй, когда ошибка не ожидалась, но произошла. Если это требование типа NEVER, или если это MAY/SHALL, и соответствующая константа XX_HAS_EXTRA_CONDITION_ON_YY равна нулю, то можно констатировать неправильную работу функции. При ненулевом значении этой константы поведение функции не противоречит стандарту. Случай третий, когда ошибка ожидалась, но не произошла. Если это условие типа SHALL или если это MAY, и соответствующая константа XX_FAILS_WITH_YY не равна нулю, то можно констатировать неправильную работу функции. Если же функция вернула другой код ошибки, то поведение также является некорректным. Однако в этом случае дополнительно стоит обратить внимание на причины возникновения этой проблемы. Не исключено, что возвращённый код взялся не «с потолка», а явился следствием того, что оказались выполнены условия возникновения и этой второй ошибки наряду с первой. Если оба требования являются требованиями типа SHALL (или MAY с ненулевой константой XX_FAILS_WITH_YY), то можно утверждать, что в стандарте имеет место противоречие: для одних и тех же условий требуется вернуть одновременно 99
два различных кода. На таких ситуациях мы сейчас остановимся несколько подробнее.
3.3. Пересечение требований Может оказаться, что один и тот же код ошибки встречается в нескольких требованиях. Хорошо, если эти требования лишь дополняют друг друга. Но может сложиться ситуация, когда эти требования противоречат друг другу, а именно, когда одно из них требует, чтобы функция вернула код ошибочного завершения, а другое — утверждает, что в этой ситуации данный код ошибки не может быть возвращён. Такая ситуация может и не встретиться в ходе выполнения тестов, но подобное противоречие условий в любом случае является ошибкой в стандарте. Другая ситуация: под одно и то же условие попадают разные коды ошибок, при этом стандарт для обоих кодов требует обязательного возврата этого кода. В этом случае при выполнении условий появления ошибки тесты зафиксируют некорректность в работе тестируемой системы, независимо от того, какой из этих кодов ошибки был возвращён функцией, так как проверка другого кода зафиксирует некорректность поведения. В такой ситуации обычно из текста стандарта понятно, какой код ошибки должен возвращаться в каждом конкретном случае, и, следовательно, условия возникновения ошибок должны быть дополнены так, чтобы не выполняться одновременно ни в каких ситуациях. Если же из контекста неясно, какой код ошибки должен быть возвращён, налицо явное противоречие в стандарте. Таким образом, пересечение требований является серьёзной проблемой, и разработчик тестов должен обращать на них особое внимание.
3.4. Трёхзначная логика Иногда случается, что мы не во всех ситуациях можем определить, выполняется данное условие или нет. В этом случае логика становится трёхзначной: «да» — «нет» — «не знаю». Конечно, можно было бы поместить проверку внутрь условного ветвления, в котором условие всегда было бы проверяемым, но это значительно усложнило бы читаемость и понятность кода. К тому же для человека такая трёхзначная логика в достаточной мере «естественна». Поэтому к существующим макросам были добавлены ERROR_SHALL3 и ERROR_MAY3, в которых предикат может принимать одно из трёх значений: False_Bool3, True_Bool3, Unknown_Bool3. Наиболее используемы эти макросы в случаях, когда значение проверяемого параметра подсистемы может быть неизвестно в силу закрытости тестовой модели. Пример: /* 100
* [EINVAL] * Invalid speed argument. */ ERROR_MAY3(LSB_CFSETSPEED, EINVAL, "cfsetspeed.04.01", (isKnown_Speed(speed) ? False_Bool3 : not_Bool3(isValid_Speed(speed))) ) Другими частыми случаями употребления этих макросов являются проверки на корректность параметров или на наличие определённых ресурсов. Зачастую стандарт не указывает, как определить некорректность нужного значения, а проверить наличие ресурсов (например, оперативной памяти) не представляется возможным, так как почти никогда не известно точно, какое их количество будет «достаточным». С другой стороны, про отдельные значения может быть известно, что они заведомо корректные или заведомо некорректные. Пример: /* * The confstr() function shall fail if: * [EINVAL] * The value of the name argument is invalid. */ /* [LSB: 18.1.1. Special Requirements] * A value of -1 shall be an invalid "_CS_..." value for confstr(). */ ERROR_SHALL3(POSIX_CONFSTR, EINVAL, "confstr.12", ((name == -1) ? True_Bool3 : Unknown_Bool3) ) В подобных случаях макросы с трёхзначной логикой позволяют естественным образом формализовать условие с помощью одной проверки, что обеспечивает читабельность и наглядность программного кода.
3.5. Оформление непроверяемых требований В силу различных причин проверка некоторых требований может быть затруднена или даже невозможна. Для таких ситуаций были введены два дополнительных макроса. 1. ERROR_UNCHECKABLE. Этот макрос используется в том случае, когда проверка требования не может быть выполнена по каким-либо 101
2.
причинам. Сам макрос просто сигнализирует о том, что данный код ошибки не противоречит стандарту. TODO_ERR(errname). Этот макрос-заглушка используется вместо предиката, определяющего условия возникновения ошибки, в тех случаях, когда проверка в принципе возможна, но либо она чрезмерно сложна, либо её реализация по каким-то причинам на время отложена. Макрос TODO_ERR возвращает истину тогда и только тогда, когда возвращаемый код ошибки совпадает с проверяемым. Таким образом, вместо реальной проверки в данном случае мы имеем тавтологию. Этот макрос подставляется по умолчанию вместо конкретных условий в коде, который генерируется непосредственно из разметки. Это позволяет достаточно быстро и с минимальными усилиями создать компилирующийся и работающий проект для тестирования новой подсистемы.
4. Заключение Несмотря на то, что работа над пакетом OLVER в настоящее время завершается, описанная в данной статье методика будет применяться при написании тестовых наборов, расширяющих и дополняющих существующие сертификационные тесты на соответствие стандарту LSB, поскольку она очень проста в применении, и при этом генерируемый ей код достаточно универсален. Конечно, нужно понимать, что заранее учесть и продумать все мыслимые комбинации различных ошибочных ситуаций невозможно. Всегда может найтись какая-то очень специфичная функция, требующая нестандартного подхода. И именно эта возможность задаёт направление для дальнейшего развития системы. С одной стороны, в подобных случаях можно попросту подходить к каждой функции индивидуально и писать код, не опираясь на сгенерированные шаблоны. Однако если обнаруженная зависимость не единична, а встречается в нескольких функциях, то стоит сначала оценить трудозатраты: возможно, в такой ситуации имеет смысл доработать систему генерации кода так, чтобы она теперь учитывала и эти новые ситуации. Учитывая, что стандарт LSB активно развивается и включает в себя всё больше и больше функций, можно с уверенностью сказать, что описанная в данной статье система будет развиваться и дальше. Литература 1. http://linuxtesting.ru/ 2. CTesK 2.2 Community Edition: Руководство пользователя. http://www.unitesk.ru/download/papers/ctesk/ce/CTesK2.2CEUserGuide.rus.pdf 3. CTesK 2.2 Community Edition: Описание языка SeC. http://www.unitesk.ru/ download/papers/ctesk/ce/CTesK2.2CELanguageReference.rus.pdf
102
Тестирование компонентов, взаимодействующих посредством удаленного вызова методов1 В.С. Мутилин
[email protected] Аннотация. В статье описывается разработанный автором метод тестирования компонентов, взаимодействующих посредством удаленного вызова методов. Метод позволяет гарантировать, что будут проверены все различные чередования вызовов методов в системе, приводящие к различным результатам. В работе выделены ограничения, при которых такой перебор различных порядков вызовов методов гарантирует корректность системы. Показано, что этим ограничениям удовлетворяют системы, разработанные по технологии Enterprise JavaBeans. В отличие от методов проверки моделей (model checking), в предложенном методе перебор осуществляется не для всей системы целиком, а для отдельных тестовых воздействий, что позволяет существенно сократить область перебора.
1. Введение Для разработки сложных современных программных систем широко применяется компонентный подход, в котором система состоит из набора компонентов. Согласно [1], такой компонент представляет собой структурную единицу системы, обладающую четко определенным интерфейсом, который описывает ее зависимости от окружения. Интерфейс состоит из интерфейсных методов, посредством вызова которых с компонентом взаимодействуют другие компоненты и окружение. В данной работе мы будем рассматривать системы, в которых компоненты взаимодействуют посредством удаленного вызова методов, например Java Remote Method Invocation (Java RMI) [2]. Удаленный вызов отличается от обычного тем, что данные вызова передаются по сети, позволяя, тем самым, взаимодействовать между собой компонентам, находящимся на разных компьютерах. Система, построенная на основе такого взаимодействия, является распределенной системой [3] с присущими таким системам сложностями при разработке и тестировании.
1
Традиционно целью тестирования программной системы является проверка того, что ее реальное поведение соответствует ожидаемому поведению. Для осуществления такой проверки требуется некоторым образом воздействовать на систему, подавая входные данные в различных состояниях системы. Соответственно, первой задачей тестирования является генерация таких воздействий. После осуществления воздействия система выдает результаты и переходит в некоторое состояние. Вторая задача – оценить корректность выдаваемых результатов, убедиться, что они соответствуют ожидаемым результатам. В данной работе корректность результатов формулируется в виде формальной спецификации системы на основе подхода Design-By-Contract [4], при котором спецификация системы описывается в виде пред- и постусловий методов, а также инвариантов. Третья задача – оценка полноты тестирования. Решение этой задачи позволяет ответить на вопрос: достаточно ли сгенерированных тестовых воздействий для завершения тестирования? Для решения этих трех задач для распределенных систем требуется учет ряда особенностей. Воздействия на систему требуется производить не только последовательно, но и асинхронно, т.е. подавать их из различных потоков, процессов или с разных машин. Результаты, выдаваемые системой после подачи воздействия, зависят от чередований событий внутри нее. В системах, взаимодействующих посредством удаленного вызова методов, такими событиями являются удаленные вызовы методов. Результат может зависеть от порядка вызова этих методов. Соответственно, метод оценки корректности должен обеспечивать проверку результатов для любых возможных чередований событий. Кроме того, для исчерпывающего тестирования необходимо проверить результаты для различных чередований, т.е. уже недостаточно проверить одно из возможных чередований. Эта особенность должна быть учтена при оценке полноты тестирования. Особо важную роль в распределенных системах играет задача перебора различных чередований событий. В силу того, что появление того или иного порядка событий зависит от многих факторов, таких как планирование процессов в операционной системе, управлять которыми разработчик системы не имеет возможности, нет гарантии того, что данный порядок появится в процессе воздействия на систему. Перебор только части возможных чередований оставляет возможность проявления ошибки на непроверенных чередованиях. Ошибки такого рода крайне тяжело выявлять и исправлять; даже если такая ошибка обнаружена, ее трудно повторить, так как вероятность появления порядка, на котором проявляется ошибка, может быть крайне мала. Целью данной работы является построение метода тестирования, позволяющего гарантировать, что будут проверены все различные чередования событий, приводящие к разным результатам. Задача перебора различных чередований событий традиционно решается в методах проверки моделей (model checking), которые позволяют найти
Работа частично поддержана грантом РФФИ 05-01-00999. 103
104
последовательность событий, нарушающих данную темпоральную формулу или доказать, что такой последовательности не существует. Методы проверки моделей работают с замкнутой системой, т.е. системой, которая не принимает входных воздействий и не выдает реакций. Для верификации других систем, например, предоставляющих программный интерфейс, требуется предоставить окружение, взаимодействующее с системой. От размеров окружения напрямую зависит количество состояний, получающихся в процессе поиска. С другой стороны, от окружения зависит качество верификации, и далеко не всегда можно ограничиться простым окружением. Методы проверки моделей обладают рядом ограничений: 1. 2. 3.
Взрыв количества состояний; Требование функции сравнения состояний; Требование отката.
Взрыв состояний возникает из-за большого количества состояний в программных системах. Кроме того, если система не является замкнутой, то к взрыву часто приводит попытка проанализировать систему с наиболее общим окружением, которое воздействует на систему произвольным образом. Известны методы целенаправленной генерации окружений, например, [5], однако данные методы не применимы для генерации окружений для распределенных систем. Для сокращения пространства перебора широко используются методы редукции частичных порядков [6]. Эти методы основаны на понятии зависимости событий. Зависимости можно выявить до выполнения системы или при выполнении одного из зависимых событий. Эксперименты, проведенные в [6], показывают, что такие методы позволяют значительно сократить пространство перебора. Классические методы проверки моделей для осуществления поиска требуют наличия функции сравнения состояния. Для того чтобы сравнивать состояния, требуется хранить информацию об этих состояниях. Поиск без сохранения состояний (stateless search) [7] позволяет осуществлять поиск для систем со сложными состояниями, которые сложно сохранять, а также для систем с большим количеством состояний. Однако поиск накладывает ограничения на систему – требуется, чтобы в ее пространстве состояний не было циклов. Широко используемой возможностью в методах поиска является возможность отката (возврата) в предшествующее состояние. Такая возможность может обеспечиваться за счет механизмов отката в реализации (например, с использованием специальной виртуальной машины [8]) или с помощью перевыполнения, т.е. сброса системы в начальное состояние и повторного выполнения. Мы сформулируем задачу для случая, когда в реализации нет механизма отката и нет возможности сброса в начальное состояние. Мы будем 105
предполагать, что вместо отката алгоритму поиска предоставляется возможность возврата в одно из состояний, которое принадлежит некоторому множеству, называемому обобщенным состоянием. Мы предполагаем, что последовательность воздействий на систему строится на основе обхода графа состояний конечного автомата [9,10]. Состояния автомата являются множествами исходных состояний системы и называются обобщенными состояниями. Множества выбираются, как разбиения состояний системы на классы эквивалентности. Возможность обобщенного отката реализуется за счет требования сильной связности графа состояний, необходимого для построения обхода графа. Таким образом, обобщенный откат – это путь в графе состояний, ведущий в требуемое обобщенное состояние. В следующем разделе описан пример, на котором будет продемонстрирован метод тестирования. В разделе «Алгоритм поиска» описан алгоритм, позволяющий перебирать различные порядки вызовов методов. Далее следует описание архитектуры, необходимой для его работы.
2. Пример 2.1. Описание системы Рассмотрим пример системы, построенной из компонентов, которые взаимодействуют посредством удаленного вызова методов. Система написана на основе Enterprise Java Beans 3.0 [11]. Система содержит один сеансовый компонент с состоянием (stateful session bean) CompanyManagerBean, реализующий удаленный интерфейс CompanyManager, и один класс сущность (entity) Company (Рис. 1). «interface» CompanyManager editCompany(in id : Integer) : void isActive() : Boolean setActive() : Boolean setInactive() : Boolean removeParent() : Boolean setParent(in id : Integer) : Boolean freeCompany() : void @Entity @Stateful CompanyManagerBean company : Company
Company id : Integer name : String active : Boolean parent : Company
Рис. 1. Система управления компаниями 106
Компонент CompanyManagerBean предоставляет интерфейс работы с иерархиями компаний. У каждой компании имеются уникальный идентификатор, имя, могут иметься активный/неактивный статус и родительскую компанию. Через компонент CompanyManagerBean можно редактировать компании. Клиент запрашивает экземпляр компонента, затем устанавливает компанию, которую собирается редактировать (editCompany), блокируя доступ к данной компании другим экземплярам, редактирует компанию, изменяя статус компании (setActive, setInactive) и родительскую компанию (setParent, removeParent), затем освобождает компанию (freeCompany), делая ее доступной для редактирования другим экземплярам. На иерархию компаний накладываются следующие ограничения: 1. 2.
Компания с активным статусом не может иметь родительскую компанию с неактивным статусом; Иерархия компаний более четырех уровней (глубины) недопустима.
Таким образом, данные ограничения запрещают иерархии, показанные на Рис. 2: (а) – активная компания C2 имеет неактивного родителя C1, (б) – иерархия компаний имеет более четырех уровней. а:
C1
б:
Предварительно метод проверяет, что все дочерние компании имеют неактивный статус. Проверка производится во вспомогательном методе isChildrenInactive, в котором происходит поисковый запрос к базе данных с выбором всех дочерних компаний, т.е. компаний, у которых родительская компания совпадает с данной. Далее, если все дочерние компании неактивны, то возвращается true, статус компании меняется на неактивный, и изменения сохраняются в базу данных (enityManager.merge), иначе возвращается false, и статус компании не изменяется. Обращения к базе данных производятся через специальный компонент EntityManager, который предоставляет методы добавления (persist), изменения (merge), поиска (find) записей в базе данных. Отметим, что для корректности результатов данной работы требуется, чтобы обращения компонентов к общим данным, хранящимся в базе данных, являлись атомарными, т.е. одновременно может выполняться запрос не более чем от одного компонента. В данном примере это требование обеспечивается механизмом транзакций EJB таким образом, что обращения к EntityManager включаются в контекст транзакций. public boolean setInactive() { if (isChildrenInactive()) { company.setActive(false); entityManager.merge(company); return true; } else { return false;
C1
C2 C2
Рис. 3. Реализация метода setInactive
C3
public boolean setParent(int id) { Company newParent = entityManager.find(Company.class, id); if (newParent == null) throw new NoResultException();
– активная компания C4 – неактивная компания
if (newParent.getActive() || !company.getActive()) { company.setParent(newParent); entityManager.merge(company); return true; } else { return false; }
C5
Рис. 2. Примеры запрещенных иерархий Реализация методов компонента CompanyManagerBean состоит в предварительной проверке ограничений и, в случае успеха, изменении состояния компании и сохранении результатов изменения в базе данных. Для примера рассмотрим реализации методов setInactive и setParent. Метод setInactive устанавливает статус компании неактивным (Рис. 3). 107
}
Рис. 4. Реализация метода setParent 108
Метод setParent устанавливает родительскую компанию (Рис. 4). Требуется, чтобы существовала компания с идентификатором указанным в качестве аргумента вызова этого метода. Метод проверяет, что для активной компании нельзя установить неактивного родителя. Т.е. если родитель активный или компания неактивная, то устанавливается новый родитель, и изменения сохраняются в базе данных; иначе изменения не происходят. Рассмотрим один из возможных тестов для компонента CompanyManagerBean. В тесте предполагается, что в начальном состоянии имеются две активные компании без родителей. Имена компаний C1, C2, а идентификаторы 1, 2 соответственно. Тест состоит из двух потоков, выполняющихся одновременно. Оба потока выполняют метод testCase, показанный на Рис. 5, с разными аргументамии. Первый выполняет метод с аргументами 0, 1, 2 а второй – с аргументами 1, 2. Таким образом, первый поток захватывает компанию C1 на редактирование и, в случае успеха, вызывает метод setParent с аргументов – идентификатором компании C2 (будем писать C1.setParent(C2)), затем освобождает компанию. Второй захватывает компанию C2 и, в случае успеха, вызывает метод setInactive (будем писать C2.setInactive()), затем освобождает компанию. Для создания экземпляра компонента CompanyManagerBean используется вспомогательный метод getCompanyManager. public static void testCase(int num, int id, int parent) throws Exception { CompanyManager manager = getCompanyManager(); manager.editCompany(id); try { if (num==0) { manager.setParent(parent); } else { manager.setInactive(); } } finally { manager.freeCompany(); } }
C1 активного ребенка C2. Если сначала выполнится второй поток, а затем первый, то в результате статус компании C2 станет неактивным, но родитель для C1 установлен не будет, так как в момент проверки статус родительской компании C2 будет неактивным. Однако возможен еще и третий вариант, когда проверки в методах setParent и setInactive выполняются до того, как происходят изменения в базе данных, т.е. выполняются методы entityManager.merge. Например, пусть вызовы выполняются в следующей последовательности: t1.editCompany; t1.setParent; s1.find; t2.editCompany; t2.setInactive; s2.find; s1.merge; s2.merge; t1.freeCompany; t2.freeCompany, где префиксы t1, t2 означают, что вызовы происходили из первого и второго потоков соответственно, а s1, s2 – соответствующие экземпляры CompanyManagerBean. Результатом такого выполнения будет установка для C1 родительской компании C2, а для C2 – установка неактивного статуса, что нарушает первое ограничение. а:
C1
C2
в:
C2
C1
б:
C1
C2
г:
C2
C1
а – начальное состояние б, в, г – возможные результаты
Рис. 6. Возможные результаты выполнения теста Таким образом, на данном примере можно видеть, что результат работы системы существенным образом зависит от порядка вызовов удаленных методов. Для полноценной проверки системы необходимо проверить работу системы для различных порядков вызова методов.
2.2. Спецификация
Рис. 5. Тестовый вариант Результаты выполнения данного теста зависят от того, в какой последовательности выполнялись удаленные вызовы методов (рис. 6). Если сначала полностью выполнится первый поток, а затем второй, то в результате для компании C1 будет установлен родитель C2, а статус компании C2 попрежнему останется активным, так как метод isChildrenInactive обнаружит у 109
Для решения задачи спецификации, генерации тестовых данных и оценки покрытия в качестве основы мы будем использовать технологию UniTESK [12,13]. В UniTESK проверка корректности производится на основе задания пред- и постусловий функций, а также инвариантов [4,14]. Дополнительно для обеспечения возможности спецификации параллельных и распределенных систем введено понятие отложенной реакции [15], с помощью которых удается моделировать внутренний недетерминизм в системе. 110
Для рассмотренного примера приведенные ограничения на систему могут быть сформулированы в виде инвариантов на состояние (Рис. 7). В модельном состоянии theCompanies хранятся описания компаний: статус компании и информация о родителях. Инвариант Rule1_InactiveParent проверяет первое ограничение, а Rule2_Level4Hierachy – второе.
обобщенное состояние {(0,0), (0,0), (1,0), (1,1)}. Такое обобщение позволяет значительно сократить количество состояний автомата и, тем самым, длину тестовой последовательности.
invariant Rule1_InactiveParent() { for (Company company: theCompanies) { if (company.isActive()) { if (company.parent!=null && !company.parent.isActive()) return false; } } return true; }
C1
C3
C1
C2
C2
C3
– корневой узел
invariant Rule2_Level4Hierarchy() { for (Company company: theCompanies) { if (company.computeLevel()>4) return false; } return true; }
{(0,0), (0,0), (1,0), (1,1)} – обобщенное состояние
Рис. 8. Обобщение состояний Архитектура UniTESK
Рис. 7. Спецификация ограничений
Обходчик
2.3. Генерация тестовой последовательности Для генерации тестовой последовательности в UniTESK используется обход конечных автоматов [16,17]. Такой способ построения тестовой последовательности хорошо зарекомендовал себя на практике, многие исследователи отмечают высокое качество получаемых тестовых последовательностей [18,19]. В качестве состояний автомата выбирается модельное состояние или его обобщение. Обобщение необходимо для сокращения количества состояний и представляет собой разбиение состояний на классы эквивалентности, рассматриваемые как равные состояния автомата. Переходами автомата являются вызовы одного или более тестируемых методов. Как показано в [20], одним из удачных обобщенных состояний для деревьев является мультимножество чисел детей. Чтобы получить дерево, в нашем примере мы добавим корневой узел, чтобы все компании без родителей были его детьми. Кроме того, мы разделим детей на активных и неактивных, т.е. обобщенным состоянием будет мультимножество пар (число активных детей, число неактивных детей). На Рис. 8 показаны два различных состояния с добавленным корневым узлом. Обоим состояниям соответствует одинаковое 111
Тестовый сценарий абстракция состояний сценарные методы
Спецификация
Неявное описание автомата - getState() - executeTransition() - nextTransition()
Оракул
Медиатор
Тестируемая система
Рис. 9. Архитектура UniTESK
112
В процессе тестирования, используя заданное пользователем обобщенное состояние, специальный компонент тестовой системы – обходчик строит тестовую последовательность на основе обхода всех переходов автомата. Информация для построения переходов автомата задается в тестовом сценарии в виде сценарных методов (Рис. 9). В сценарном методе задаются тестовые воздействия на систему. Это могут быть как простые тестовые воздействия, такие как вызовы методов с различными аргументами, так и сложные, такие как порождение нескольких потоков и вызов последовательности методов. Примером сценарного метода является тестовый вариант на рис. 5 с заданной итерацией параметров id и parent по всем компаниям. Будем ссылаться на этот пример как на сценарный метод scenExample.
2.4. Поиск различных порядков Итерация параметров в сценарном методе происходит с помощью итерационных переменных. Итерационые переменные отличаются от обычных переменных тем, что значения этих переменных вместе с именем сценарного метода определяют стимул автомата. Количество переходов по заданному стимулу в заданном обобщенном состоянии автомата зависит от результатов вызова сценарного метода с заданными значениями итерационных переменных. Если вызов сценарного метода приводит всегда к одному и тому же результату, то автомат имеет единственный переход по заданному стимулу; иначе переходов столько, сколько возможно различных результатов. Для того чтобы получить различные результаты вызова сценарного метода, требуется перебрать различные порядки событий в системе. В рассмотренном примере сценарного метода scenExample с использованием теста testCase возможны три различных результата, соответствующих вызову этого метода с итерационными переменными id=1, parent=2 в начальном состоянии {(0,0), (0,0), (0,2)}: {(0,0), (0,0), (1,1)}, {(0,0), (1,0), (1,0)}, {(0,0), (0,1), (1,0)}. Рассмотрим следующий пример. Предположим, что для рассмотренного примера иерархии компаний написан сценарий, в котором в качестве обобщенного состояния выбрано описанное выше мультимножество. В сценарии, во-первых, заданы сценарные методы для методов интерфейса CompanyManager. Данные сценарные методы позволяют получить разнообразные состояния иерархии: широкие, длинные, с разными конфигурациями активных и неактивных компаний. Эти методы также обеспечивают сильную связность графа автомата. Во-вторых, предположим, что имеется сценарный метод scenExample, описанный на основе метода testCase. Предположим, что обходчик в ходе построения тестовой последовательности попадает в состояние, соответствующее обобщенному состоянию {(0,0), (0,0), (1,1)}, в котором компания C1 – неактивная, а C2 – активная. В этом состоянии обходчик выполняет переход, соответствующий 113
сценарному методу scenExample с итерационными переменными id=1, parent=2. На Рис. 10 показаны возможные пути выполнения без учета вызовов editCompany и freeCompany. Кроме того, на рисунке пунктиром показаны вызовы, перебора которых можно избежать за счет использования методов редукции частичных порядков. В соответствии с постановкой задачи метод поиска не имеет возможности отката в предыдущее состояние, поэтому каждый раз метод поиска проходит один из путей целиком и запоминает пройденное дерево. После этого обходчик продолжает обход автомата, и если метод сообщил, что в состоянии {(0,0), (0,0), (1,1)} есть неперебранные порядки, обходчик возвращается в это состояние. При этом от обходчика требуется лишь то, чтобы каждый переход был пройден столько раз, сколько это нужно методу поиска порядков. Никаких других изменений для обходчика не требуется. t1.setParent s1.find
t2.setInactive s1.find
s1.merge s2.find s2.merge
s1.merge
s2.merge
t2.setInactive
s2.find s2.find s1.find
s2.merge
s2.merge
s1.find
s1.merge
s1.merge
– вершины дерева перебора для состояния:
C1
C2
– вершины дерева перебора для состояния:
C1
C2
Рис. 10. Деревья перебора для обобщенного состояния {(0,0), (0,0), (1,1)} Так как обобщенному состоянию {(0,0), (0,0), (1,1)} соответствует также состояние системы, в котором компания C1 – активная, C2 – неактивная, обобщенный откат может быть осуществлен в это состояние вместо первоначального состояния. Таким образом, поиск будет необходимо продолжить в этом состоянии. На Рис. 10 полужирными линиями показано соответствующее ему дерево различных порядков выполнения. Как можно 114
видеть, в этом дереве отсутствует часть порядков, которые присутствовали в предыдущем состоянии. От метода поиска требуется перебрать все возможные порядки, встречающиеся во всех состояниях, соответствующих одному обобщенному состоянию. В рассматриваемом примере множеством порядков, которые необходимо перебрать, является дерево вершин, выделенных жирными линиями.
3. Алгоритм поиска В данном разделе описан алгоритм поиска различных порядков вызовов методов. Информацию о тестируемой системе алгоритм получает через управляющего (Controller). В начале каждого удаленного вызова метода выполнение этого вызова блокируется, и управление передается управляющему, который получает информацию о вызываемом методе в виде возможного перехода (Transition). Будем говорить, что система находится в глобальном состоянии, если в тестируемой системе не остается активных потоков, т.е. все потоки ожидают команды управляющего. Алгоритму поиска информация предоставляется через интерфейс управляющего (рис. 11). Для обнаружения достижения глобального состояния алгоритм вызывает метод управляющего waitForGlobalState. В глобальном состоянии управляющий выдает информацию о глобальном состоянии (метод getStateInfo), включающую в себя список возможных переходов (метод getTransitionList) и сигнал о завершении взаимодействий с тестируемой системой (метод isEndState). Алгоритм поиска может выполнить один из переходов, вызвав метод applyTransition. public interface Controller { public static interface StateInfo { public List
getTransitionList(); public boolean isEndState(); } public void applyTransition(Transition t); public StateInfo getStateInfo(); public void waitForGlobalState(); } Рис. 11. Интерфейс управляющего Как уже упоминалось ранее, в предлагаемом методе тестирования тестовая последовательность строится на основе обхода графа автомата. Переходы автомата представляют собой тестовые воздействия на тестируемую систему. Состояния – обобщенные состояния. Для построения тестовой последовательности от графа автомата требуется сильная связность, т.е. из любого состояния существует путь в любое другое. 115
Алгоритм поиска различных порядков выполняется для некоторого перехода в автомате в некотором обобщенном состоянии. Каждый раз при выполнении алгоритма система находится в некотором состоянии, соответствующем данному обобщенному состоянию. Для перехода в автомате и состояния системы определим понятие дерева перебора. Вершинами дерева перебора являются глобальные состояния, а дуги в дереве – это возможные переходы из соответствующих глобальных состояний. Пусть заданы обобщенное состояние S, которому принадлежат состояния системы s1,…, sn, и переход автомата a. Каждому состоянию si и переходу a соответствует дерево перебора ti. Будем говорить, что дерево t1 является поддеревом дерева t2, если для любого пути p1 из t1 существует путь p2 в t2, такой что p1 является префиксом p2. Задача алгоритма поиска – перебрать все пути дерева t, содержащего все пути p, такие что для каждого дерева ti найдется такой путь pi, что p является префиксом pi, то есть t = { p | для всех ti существует pi в ti: p префикс pi }. Отметим, что t является поддеревом любого дерева ti. Такое поддерево будем называть максимальным поддеревом деревьев t1,..., tn. После каждого отката алгоритм сохраняет дерево перебранных путей выполнения. На выходе у алгоритма максимальное поддерево деревьев t1,..., tn, соответствующих состояниям s1,…, sn обобщенного состояния S. Алгоритм управляющего выполняется в несколько этапов. На первом этапе вход алгоритма пуст, и алгоритм начинает выполняться в некотором состоянии. На каждом последующем этапе алгоритм получает на входе пройденное дерево переходов, включающее информацию о последнем пройденном пути. Выполнение на каждом последующем этапе может начинаться в другом состоянии, но принадлежащем тому же обобщенному состоянию. На каждом этапе алгоритм пытается пройти новый путь, принадлежащий деревьям предыдущих этапов. Если же все пути уже пройдены, алгоритм проходит произвольный путь и сообщает о завершении поиска. В конце каждого этапа алгоритм выдает обновленное дерево путей, а также информацию о завершении поиска. Отметим, что алгоритм определяет окончание пути по информации от управляющего. В конце каждого перехода алгоритму известно, завершился ли путь. В тестовых наборах, разработанных по технологии UniTESK, завершение пути соответствует завершению выполнения сценарного метода.
3.1. Структуры данных алгоритма Структуры данных, используемые алгоритмом, показаны на Рис. 12. Каждый переход Transition обязательно включает идентификатор. Дерево путей Tree ссылается на корневой узел Node, который включает упорядоченный список переходов transitions, индекс последнего выполненного перехода в этом списке lastIndex и список дочерних вершин children. В начале списка 116
transitions идут выполненные переходы до lastIndex, далее идут невыполненные переходы. Список children содержит дочерние вершины, соответствующие переходам transitions, в том же порядке, размер списка – lastIndex+1.
По завершению цикла, алгоритм возвращает true, если в дереве остались непройденные переходы, и false иначе.
4. Архитектура для EJB 3.0 Всю информацию, необходимую для выполнения перебора различных порядков, алгоритм поиска получает через интерфейс управляющего (Controller). Архитектура, реализующая методы этого интерфейса, показана на Рис. 13.
public class Tree { public Node rootNode = new Node(); } public class Node { List transitions; int lastIndex = -1; List children;//size=lastIndex+1 boolean isEndState = false; } public class Transition {
Search реализует использует
Controller
Рис. 12. Структуры данных алгоритма поиска ControllerImpl
3.2. Этап работы алгоритма
117
ControllerNotification notifyEndState notifyGlobalState registerTransition
InfoCollectorImpl
InfoCollector incActiveThreadCount decActiveThreadCount processTransition R M
I
JVMHook
RM
В начале выполнения этапа у алгоритма имеется пройденное дерево переходов Tree. Текущий узел дерева – его корень. Алгоритм ожидает начального глобального состояния. После прихода в глобальное состояние алгоритм выполняет цикл, пока не достигнет конечного состояния. В цикле алгоритм получает список возможных переходов и выполняет действия в зависимости от режима работы. В режиме поиска алгоритм пытается пройти по новому пути. Алгоритм находит пересечение списка переходов в текущем узле дерева и списка переходов для текущего глобального состояния. Таким образом, находится общее поддерево, т.е. остаются только те переходы, которые имеются для уже пройденного дерева и дерева перебора для конкретного состояния системы. Если в результате пересечения уже пройденный переход не был удален, и поддерево в следующем состоянии дерева по этому переходу имеет не пройденные переходы, то алгоритм проходит по ранее пройденному переходу. Текущая вершина – следующая вершина дерева по пройденному переходу. Иначе, если в текущей вершине имеются непройденные переходы, алгоритм проходит по новому переходу и текущая вершина – новая вершина в дереве. Иначе данное поддерево не имеет непройденных переходов, и тогда алгоритм завершает проход по произвольным переходам в режиме симуляции. В режиме симуляции алгоритм проходит по произвольному пути. В текущем глобальном состоянии выбирается первый из возможных переходов. В конце цикла алгоритм ожидает глобального состояния. По приходу в него он обновляет информацию о состоянии и переходит в начало цикла.
EJBServerInterceptor
...
I
EJBServerInterceptor
Рис. 13. Архитектура для EJB Основные поставщики информации – это EJBServerInterceptor и JVMHook. EJBServerInterceptor работает на сервере приложений и перехватывает удаленные вызовы методов компонентов. Встраивание данного класса происходит с помощью механизма перехватчиков (interceptor), определенных 118
в EJB 3.0 [21]. До выполнения метода компонента выполняются перехватчики, определенные для данного типа компонентов. Механизм перехватчиков используется для реализации таких служб, как управление транзакциями и безопасностью. Нам же от этого механизма требуется только блокировка удаленного вызова метода. EJBServerInterceptor через удаленный интерфейс InfoCollector сообщает информацию о вызываемом методе, создавая по описанию метода переход Transition (processTransition). Вызов при этом блокируется. JVMHook работает на той же JVM, на которой выполняется тест. Этот класс передает информацию о порождаемых в тесте потоках incActiveThreadCount и decActiveThreadCount, а также информацию о завершении теста notifyEndState. Информация о количестве порождаемых потоков необходима для обнаружения начального глобального состояния. Компоненты, разработанные по технологии EJB, обладают рядом важных свойств: 1.
В компонентах не создаются новые потоки. Если с системой работает N клиентов, то для блокировки всех взаимодействий в системе достаточно заблокировать N вызовов методов. 2. В каждый момент для экземпляра компонента может выполняться лишь один метод. В зависимости от настроек сервера приложений другие вызовы либо помещаются в очередь, либо завершаются с исключением. Это гарантирует, что данные экземпляра могут одновременно изменяться лишь одним потоком. Кроме того, мы будем требовать выполнения двух дополнительных ограничений, которые не являются обязательными по спецификации EJB, однако выполняются для большинства систем: 3. Обращения к EntityManager происходят внутри транзакций. Этим обеспечивается атомарность обновления данных в базе данных. 4. Все обращения к разделяемой памяти сосредоточены в EntityManger, т.е. других обращений нет. Последние два ограничения, по сути, требуют, чтобы все обращения к разделяемой памяти были атомарными и контролировались (перехватывались) управляющим. Для систем, удовлетворяющих этим ограничениям, верно, что для любого конкретного состояния системы и воздействий на систему: 1. 2.
Существует единственное дерево перебора; Пути дерева перебора описывают все возможные результаты выполнения.
Таким образом, гарантируется, что если для воздействий, осуществляемых в переходе автомата, система может работать некорректно (несоответствует 119
спецификации), то соответствующий некорректный результат будет обнаружен. Корректность для всей системы в целом зависит от выбранного разработчиком тестов обобщенного состояния и тестовых воздействий, осуществляемых в сценарных методах. При выполнении ряда гипотез [12] можно гарантировать, что вся система корректна. В гипотезах требуется, чтобы система в обобщенных состояниях вела себя «похоже». Важным результатом данной работы является возможность утверждать корректность не только для последовательных, но и распределенных программ.
5. Заключение В работе разработан метод тестирования систем, которые построены на основе компонентов, взаимодействующих с помощью удаленного вызова методов. Метод позволяет использовать удачные решения технологии UniTESK, такие как формулировка требований в виде формальных спецификаций и построение тестовой последовательности на основе обхода автоматов. Вместе с тем, метод гарантирует, что для всех тестовых воздействий, выполняемых в каждом переходе автомата, будут проверены все различные порядки вызовов методов. В работе показано, что для систем, построенных по технологии EJB 3.0, этого достаточно для получения всех возможных результатов выполнения. Тем самым, удается гарантировать корректную работу системы в соответствии со спецификацией вне зависимости от «погодных» условий, в которых работает система, таких как планирование процессов и потоков в операционной системе, а также задержки при сетевых взаимодействиях. Метод тестирования был применен к нескольким системам. Были обнаружены проблемы, которые не проявляются или проявляются редко при запусках без перебора различных порядков. Благодаря тому, что одни и те же тесты можно использовать как с перебором, так и без него, отладку тестов можно проводить без перебора, а затем проводить более тщательное тестирование, используя возможность перебора. Такой подход в проведенных экспериментах позволил значительно сократить время разработки тестов. Использование возможности перебора не для всей системы вместе с окружением целиком, а для отдельных тестовых воздействий позволило значительно сократить пространство перебора. Использование поиска без сохранения состояний позволяет сократить использование памяти. В проведенных экспериментах время работы тестов не превышало нескольких минут. Литература [1] Szyperski C. Component Software Beyond Object-Oriented Programming. Boston, MA: Addison-Wesley and ACM Press, 1998. [2] Документация по Java RMI. http://java.sun.com/j2se/1.5.0/docs/guide/rmi/.
120
[3] Таненбаум Э., М. ван Стеен. Распределенные системы. Принципы и парадигмы. СПб.: Питер, 2003. [4] Bertrand Meyer. Applying 'Design by Contract'. IEEE Computer, vol. 25, No. 10, October 1992, pp. 40–51. [5] O. Tkachuk, M. B. Dwyer and C. S. Pasareanu. Automated Environment Generation for Software Model Checking. Proceedings of the Eighteenth IEEE International Conference on Automated Software Engineering, 2003. [6] P. Godefroid. Partial-Order Methods for the Verification of Concurrent Systems: An Approach to the State-Explosion Problem. Secaucus, NJ, USA: Springer-Verlag, 1996. [7] P. Godefroid. Model Checking for Programming Languages using VeriSoft. Proceedings of the 24th ACM Symposium on Principles of Programming Languages. ACM Press, pp. 174–186, January 1997. [8] G. Brat, K. Havelund, S.-J. Park, and W. Visser. Model Checking Programs. In IEEE International Conference on Automated Software Engineering (ASE), pp. 3–12, September 2000. [9] И.Б. Бурдонов, А.С. Косачев, В.В. Кулямин. Применение конечных автоматов для тестирования программ. Программирование, 26(2):61-73, 2000. [10] D. Lee and M. Yannakakis. Principles and methods of testing finite state machines – a survey. Proceedings of the IEEE, vol. 84, pp. 1090-1123, Berlin, August 1996. [11] R. Monson-Haefel, B. Burke. Enterprise JavaBeans 3.0, Fifth Edition. Sebastopol, CA: O’Reilly, 2006 [12] В.В. Кулямин, А.К. Петренко, А.С. Косачев, И.Б. Бурдонов. Подход UniTESK к разработке тестов. Программирование, 29(6): 25–43, 2003. [13] I. Bourdonov, A. Kossatchev, V. Kuliamin, and A. Petrenko. UniTesK Test Suite Architecture. Proceedings of FME 2002. LNCS 2391, pp. 77–88, Springer-Verlag, 2002. [14] Gary T. Leavens, Albert L. Baker, and Clyde Ruby. Preliminary Design of JML: A Behavioral Interface Specification Language for Java. ACM SIGSOFT Software Engineering Notes, 31(3):1–38, March 2006. [15] Хорошилов А.В. Спецификация и тестирование компонентов с асинхронным интерфейсом. Диссертация на соискание ученой степени кандидата физикоматематических наук. Москва: ИСП РАН, 2006. [16] И. Б. Бурдонов, А. С. Косачев, В. В. Кулямин. Неизбыточные алгоритмы обхода ориентированных графов. Детерминированный случай. Программирование, 29(5):59–69, 2003. [17] И. Б. Бурдонов, А. С. Косачев, В. В. Кулямин. Неизбыточные алгоритмы обхода ориентированных графов. Недетерминированный случай. Программирование, 30(1):2–17, 2004. [18] H. Robinson. Intelligent Test Automation. Software Testing and Quality Engineering, September/October 2000, pp. 24–32. [19] W. Grieskamp, Y. Gurevich, W. Schulte, and M. Veanes. Generating Finite State Machines from Abstract State Machines. ISSTA 2002, International Symposium on Software Testing and Analysis, July 2002. [20] В.С. Мутилин. Паттерны проектирования тестовых сценариев. Труды ИСП РАН, 9: 97–128, 2006. [21] EJB 3.0 Specification, JSR 220 FR. http://java.sun.com/products/ejb/.
121
122
Использование контрактных спецификаций для автоматизации функционального тестирования моделей аппаратного обеспечения А.С. Камкин ([email protected]) Аннотация. Контрактные спецификации в форме пред- и постусловий широко используются в программной инженерии для формального описания интерфейсов программных компонентов. Такие спецификации, с одной стороны, удобны для разработчиков, поскольку хорошо привязываются к архитектуре системы, с другой стороны, на их основе можно автоматически генерировать тестовые оракулы, проверяющие соответствие поведения целевой системы требованиям, описанным в спецификациях. В работе предлагается использовать контрактные спецификации для автоматизации функционального тестирования моделей аппаратного обеспечения, разработанных на таких языках, как VHDL, Verilog, SystemC, SystemVerilog и др. В статье подробно описаны особенности спецификации аппаратного обеспечения, приводится сравнение предлагаемого подхода с существующими методами спецификации, применяемыми в тестировании аппаратуры. В качестве базового подхода используется технология тестирования UniTESK, разработанная в Институте системного программирования РАН.
1. Введение Современный мир не мыслим без огромного разнообразия электронных устройств. Мобильные телефоны, цифровые фотокамеры и переносные компьютеры давно стали неотъемлимыми атрибутами жизни человека. Специальные устройства управляют работой бытовой техники, контролируют бортовые системы самолетов и космических спутников, управляют медицинскими системами жизнеобеспечения. В основе практически всех этих систем лежит полупроводниковая аппаратура — кристаллы интегральных схем, состоящие из миллионов связанных друг с другом микроскопических транзисторов, которые, пропуская через себя электрические токи, реализуют требуемые функции. Чтобы убедиться, что аппаратура работает правильно, то есть реализует именно те функции, которые от нее ожидают пользователи, на практике используют функциональное тестирование. Требования, предъявляемые к качеству тестирования аппаратного обеспечения, очень высоки. Это связано 123
не только с тем, что аппаратура лежит в основе всех информационных и управляющих вычислительных систем, в том числе достаточно критичных к сбоям и ошибкам. Большое влияние на формирование высоких требований оказывают также экономические факторы. В отличие от программного обеспечения, в котором исправление ошибки стоит сравнительно дешево, ошибка в аппаратном обеспечении, обнаруженная несвоевременно, может потребовать перевыпуск и замену продукции, а это сопряжено с очень высокими затратами. Так, известная ошибка в реализации инструкции FDIV микропроцессора Pentium1 [1], заключающаяся в неправильном делении некоторых чисел с плавающей точкой, обошлась компании Intel в 475 миллионов долларов [2, 3]. С другой стороны, требования к срокам тестирования также очень высоки. Важно не затягивать процесс и выпустить продукт на рынок своевременно, пока он не потерял актуальность, и на него существует спрос. Как разработать качественный продукт своевременно, используя ограниченные ресурсы? В настоящее время для проектирования аппаратного обеспечения используются языки моделирования высокого уровня, которые позволяют значительно ускорить процесс разработки за счет автоматической трансляции описания аппаратуры на уровне регистровых передач (RTL, register transfer level) в описание аппаратуры на уровне логических вентелей (gate level). Такие языки называются языками описания аппаратуры (HDL, hardware description languages), а модели, построенные на их основе — HDLмоделями или RTL-моделями2. Языки описания аппаратуры позволяют значительно повысить продуктивность разработки аппаратного обеспечения, но они не страхуют от всех ошибок, поэтому функциональное тестирование по-прежнему остается актуальной и востребованной задачей. При современной сложности аппаратного обеспечения3 невозможно разработать приемлимый набор тестов вручную за разумное время. Необходимы технологии автоматизированной разработки тестов. В настоящее время разработка таких технологий и поддерживающих их инструментов выделилась в отдельную ветвь автоматизации проектирования электроники (EDA, electronic design automation) — автоматизацию тестирования (testbench automation). Основной задачей тестирования является проверка соответствия поведения системы предъявляемым к ней требованиям. Для возможности автоматизации такой проверки требования к системе должны быть представлены в форме, допускающей автоматическую обработку. Такую 1 Pentium — торговая марка нескольких поколений микропроцессоров семейства x86, выпускаемых компанией Intel с 22 марта 1993 года. 2 Именно такие модели являются предметом исследования настоящей работы. 3 Число транзисторов в современных микросхемах достигает сотней миллионов. Согласно закону Мура (Moore) это число возрастает примерно вдвое через каждые 18-24 месяцев. 124
форму представления требований называют формальными спецификациями или просто спецификациями. В работе рассматривается определенный вид спецификаций — контрактные спецификации (contract specifications). Контрактные спецификации и процесс проектирования на их основе (DbC, Design-by-Contract) были введены Бертраном Майером (Bertrand Meyer) в 1986 году в контексте разработки программного обеспечения [4, 5]. Центральная метафора подхода заимствована из бизнеса. Компоненты системы взаимодействуют друг с другом на основе взаимных обязательств (obligations) и выгод (benefits). Если компонент предоставляет окружению некоторую функциональность, он может наложить предусловие (precondition) на ее использование, которое определяет обязательство для клиентских компонентов и выгоду для него. Компонент также может гарантировать выполнение некоторого действия с помощью постусловия (postcondition), которое определяет обязательство для него и выгоду для клиентских компонентов. Почему в своих исследованиях мы выбрали именно контрактные спецификации? Контрактные спецификации, с одной стороны, достаточно удобны для разработчиков, поскольку хорошо привязываются к архитектуре системы, с другой стороны, в силу своего представления стимулируют усилия по созданию независимых от реализации критериев корректности целевой системы [6]. Основное же их преимущество состоит в том, что они позволяют автоматически строить тестовые оракулы, проверяющие соответствие поведения целевой системы требованиям, описанным в спецификациях. Несколько слов о том, как организована статья. Во втором, следующем за введением, разделе даются общие сведения о моделях аппаратного обеспечения и типичной организации аппаратуры. В третьем разделе описывается предлагаемый подход к спецификации и проверке требований к аппаратному обеспечению. Четвертый раздел содержит краткий обзор технологии тестирования UniTESK и инструмента разработки тестов CTESK. В нем также описан способ использования инструмента для спецификации аппаратуры. В пятом разделе приводится сравнение предлагаемого подхода с существующими методами спецификации аппаратного обеспечения. Шестой раздел описывает опыт практического применения подхода. Наконец, в последнем, седьмом разделе делается заключение и очерчиваются направления дальнейших исследований.
2. Mодели аппаратного обеспечения Перед тем как описывать предлагаемый подход, рассмотрим особенности моделей аппаратного обеспечения на таких языках, как VHDL [7], Verilog [8], SystemC [9], SystemVerilog [10] и др. Знание этих особенностей позволяет адекватно адаптировать контрактные спецификации в форме пред- и постусловий для функционального тестирования моделей аппаратного обеспечения. 125
2.1. Особенности моделей аппаратного обеспечения Модели аппаратного обеспечения представляют собой системы из нескольких взаимодействующих модулей. Как и в языках программирования, модули используются для декомпозиции сложной системы на множество независимых или слабо связанных подсистем. У каждого модуля имеется интерфейс — набор входов и выходов, через которые осуществляется соединение модуля с окружением, и реализация, определяющая способ обработки модулем входных сигналов: вычисление значений выходных сигналов и изменение внутреннего состояния. Обработка модулем входных сигналов инициируется событиями со стороны окружения. Под событиями в моделях аппаратного обеспечения понимаются любые изменения уровней сигналов. Поскольку обычно рассматриваются двоичные сигналы, выделяются два основных вида событий: фронт сигнала (posedge, positive edge) — изменение уровня сигнала с низкого на высокий – и срез сигнала (negedge, negative edge) — изменение уровня сигнала с высокого на низкий4. Как правило, каждый модуль состоит из нескольких статически созданных параллельных процессов5, каждый из которых реализует следующий цикл: сначала осуществляется ожидание одного или нескольких событий из заданного набора событий, затем производится их обработка, после чего цикл повторяется. Набор событий, ожидаемых процессом для обработки, называется списком чувствительности (sensitive list) процесса. Будем называть процесс пассивным, если он находится в состоянии ожидания событий, и активным в противном случае. Важной особенностью моделей аппаратного обеспечения является наличие в них понятия времени. Время моделируется целочисленной величиной; можно задавать физический смысл единицы времени. Для описания причинноследственных отношений между событиями, происходящими в одну единицу модельного времени используется понятие дельта-задержки (delta delay). События, между которыми есть дельта-задержка, выполняются последовательно одно за другим, но в одну и ту же единицу модельного времени. Для выполнения моделей аппаратного обеспечения с целью анализа их поведения обычно используется симуляция по событиям (eventdriven simulation). В отличие от симуляции по интервалам времени (timedriven simulation), в которой значения сигналов и внутренние состояния модулей вычисляются через регулярные интервалы времени, в этом способе 4
Мы не рассматриваем здесь разного рода неопределенные значения, часто используемые в моделировании аппаратного обеспечения. 5 В дальнейшем будем называть такие процессы модельными процессами, чтобы отличать их от процессов операционной системы. 126
модель рассматривается только в те моменты времени, когда происходят некоторые события. Работа событийного симулятора (event-driven simulator) состоит в следующем. В начале симуляции модельное время устанавливается в ноль. Далее в цикле, пока есть активные процессы6, выбирается один из них и выполняется до тех пор, пока он не станет пассивным. После того как выполнены все активные процессы, симулятор проверяет, есть ли события, запланированные на текущий момент времени через дельта-задержку или на будущие моменты времени. Если такие события есть, симулятор изменяет модельное время на время ближайшего события, реализует события, запланированные на этот момент времени, перевычисляет множество активных процессов, после чего цикл повторяется. Если таких событий нет, симуляция заканчивается.
2.2. Типичная организация обеспечения
модулей
такт
часы операция аргументы
аппаратного
В дальнейшем будем считать, что спецификация и тестирование моделей аппаратного обеспечения осуществляется на уровне отдельных модулей. В типичном случае работа модуля аппаратного обеспечения управляется сигналом тактового импульса, который для краткости будем называть тактовым сигналом или просто часами. Фронты (или срезы) тактового сигнала разбивают непрерывное время на дискретный набор интервалов, называемых тактами. Поведение модуля на текущем такте определяется значениями входных сигналов и внутренним состоянием модуля. Как правило, часть входов модуля определяет операцию, которую модулю следует выполнить; такие входы будем называть управляющими (control). Другая часть входов определяет аргументы операции; такие входы будем называть информационными (informative). Среди операций, реализуемых модулем, обычно присутствует специальная операция NOP (no operation), означающая бездействие модуля. Модули аппаратного обеспечения могут быть организованы разными способами. В соответствии с длительностью операций, выполняемых модулем, эти операции бывают однотактными и многотактными. По способу организации выполнения операций модули делятся на модули с поочередным выполнением операций, модули с конвейерным выполнением операций и модули с параллельным выполнением операций. Рассмотрим, как осуществляется выполнение модулем однотактной операции. До начала очередного такта окружение устанавливает на соответствующих входах модуля код операции и аргументы. Выполнение операции начинается модулем с началом такта. За этот такт модуль производит необходимые 6
вычисления, изменяет внутреннее состояние и устанавливает значения выходных сигналов, которые окружение может использовать, начиная со следующего такта (Рис. 1).
В начале симуляции активными являются процессы, осуществляющие инициализацию. 127
результат время начало операции
вычисления
конец операции
Рис. 1. Временнáя диаграмма сигналов для однотактной операции. В отличие от однотактной операции, результат многотактной операции вычисляется постепенно, такт за тактом. Пусть операция f выполняется модулем за n тактов, тогда на каждом такте {1, ..., n} модуль выполняет некоторую микрооперацию f, а окружение после окончания каждого такта получает некоторый частичный результат. Представление многотактовой операции f в виде последовательности микроопераций (f1, …, fn) будем называть временнóй декомпозицией f. Теперь несколько слов о способах организации выполнения операций. В модулях с поочередным выполнением операций, как видно из названия, очередную операцию можно подавать на выполнение только после того, как полностью завершена предыдущая. В модулях с конвейерным выполнением операций операции можно подавать последовательно друг за другом, не дожидаясь завершения предыдущей операции. В модулях с параллельным выполнением операций несколько операций можно подавать одновременно. Модули с поочередным и конвейерным выполнением операций объединим общим термином — модули с последовательным выполнением операций, поскольку и в том, и в другом случае операции подаются на выполнение последовательно одна за другой. В дальнейшем будем считать, что модули организованы таким образом, что одновременно выполняемые операции не вступают в конфликты (hazards), то есть не влияют друг на друга. Если взаимное влияние все-таки возможно, 128
требования должны описывать, как подавать операции на выполнение, чтобы избежать возникновения конфликтов. Требования на операцию
3. Спецификация и проверка требований Как отмечалось во введении, для возможности автоматизации проверки соответствия поведения системы требованиям они должны быть представлены в форме, допускающей автоматическую обработку. Такая форма представления требований называется формальными спецификациями или просто спецификациями. Рассмотрим разновидности требований к аппаратному обеспечению.
функциональная декомпозиция
Требования на микрооперации
3.1. Требования к модулям аппаратного обеспечения В общем случае операции являются многотактными, то есть выполняются модулем за несколько тактов. Требования на такие операции бывают двух основных типов: требования на операцию в целом, которые не накладывают ограничений на то, на каком именно такте выполняется та или иная микрооперация, и требования на временнýю композицию операции, в которых фиксируется, на каких тактах выполняются конкретные микрооперации. Требования на операцию в целом допускают определенную свободу в реализации модуля. Не важно, на каком такте выполняется некоторая микрооперация, важно, чтобы после завершения всей операции результат этой микрооперации был доступен окружению. В процессе тестирования требования на операцию в целом проверяются после завершения операции. Требования на временнýю композицию операции являются более жесткими. В них указаны такты, на которых выполняются микрооперации. Обычно при тестировании имеет смысл проверять не то, что микрооперация была выполнена на определенном такте 0, а то, что в конце этого такта соответствующим выходам модуля были присвоены требуемые значения, неважно на каком именно такте {1, …, 0}. При такой трактовке требования на операцию в целом являются частным случаем требований на временнýю композицию; поэтому в дальнейшем мы не будем различать эти типы требований — просто будем считать, что каждому требованию соответствует номер такта, в конце которого его следует проверять.
3.2. Спецификация требований Предлагаемый подход к представлению требований основан на использовании контрактных спецификаций в форме пред- и постусловий. В отличие от классического Design-by-Contract, когда контракты определяются на уровне операций, мы предлагаем определять контракты для отдельных микроопераций, а контракт для операции в целом получать путем временнóй композиции контрактов отдельных микроопераций (Рис. 2.). 129
Спецификация операции временнáя композиция
Спецификации микроопераций
Рис. 2. Построение спецификации отдельной операции. Здесь под микрооперациями мы понимаем некоторым образом выделенные аспекты функциональности операций, реализуемые за один такт работы модуля, а под временнóй композицией контрактов — спецификацию, в которой для каждой микрооперации указан номер такта, в конце которого должен выполняться соответствующий контракт. В общих чертах процесс спецификации требований к отдельной операции состоит в следующем. Определяется предусловие, ограничивающее ситуации, в которых операцию можно подавать на выполнение. На основе анализа документации производится функциональная декомпозиция операции на набор микроопераций. Для каждой микрооперации определяется постусловие, описывающее требования к ней. После этого производится временнáя композиция спецификаций —постусловие каждой микрооперации помечается номером такта, в конце которого оно должно быть выполнено. Таким образом, контракт операции f, для которой выделено n микроопераций, формализуется структурой C=(pre, {(posti, i)i=1,n})7. Для контракта С=(pre, {(posti, i)i=1,n}) введем следующие обозначения. Через preC будем обозначать предусловие операции (pre), через postC,i — постусловие i-ой микрооперации (posti), через C,i — номер такта, в конце которого должно выполняться постусловие i-ой микрооперации (i), через PostC() — конъюнкцию постусловий микроопераций, помеченных тактом , то есть { postC,i | C,i= }.
7
Для наглядности мы не вводим модель данных и не уточняем сигнатуры пред- и постусловий. 130
3.3. Проверка требований После того как требования к модулю формализованы, проверка поведения модуля на соответствие им может осуществляться в процессе тестирования автоматически. Предположим, что в некоторый момент времени t тестируемый модуль выполняет m операций f1, …, fm, которые были поданы на выполнение раньше на 1, …, m соответственно (i 1, i=1, …, m). Пусть C1, …, Cm — контракты операций f1, …, fm соответственно, и в моменты подачи операций f1, …, fm были выполнены предусловия preC1, …, preCm; тогда для проверки правильности поведения модуля в момент времени t необходимо проверить выполнимость предиката PostC1(1) … PostCm(m). Понятно, что для проверки соответствия поведения модуля требованиям также важно уметь строить хорошие тестовые последовательности, но рассмотрение этого вопроса выходит за рамки данной работы.
и итератор тестовых воздействий; для проверки правильности поведения целевой системы — тестовый оракул; для установления связи между тестовой системой и реализацией целевой системы — медиатор. Рассмотрим подробнее каждый из указанных компонентов.
4. Технология тестирования UniTESK В качестве базового подхода в работе используется технология тестирования UniTESK [11], разработанная в Институте системного программирования РАН [12]. Характерными чертами технологии являются использование контрактных спецификаций в форме пред- и постусловий интерфейсных операций и инвариантов типов данных для спецификации требований, а также применение обобщенных конечно-автоматных моделей для построения тестовых последовательностей.
4.1. Архитектура тестовой системы UniTESK Архитектура тестовой системы UniTESK [13] была разработана на основе многолетнего опыта тестирования промышленного программного обеспечения из разных предметных областей и разной степени сложности. Учет этого опыта позволил создать гибкую архитектуру, основанную на следующем разделении задачи тестирования на подзадачи: построение тестовой последовательности, нацеленной на достижение нужного покрытия;
создание единичного тестового воздействия в рамках тестовой последовательности;
установление связи между тестовой системой и реализацией целевой системы;
проверка правильности поведения целевой системы в ответ на единичное тестовое воздействие.
Для решения каждой из этих подзадач предусмотрены специальные компоненты тестовой системы (Рис. 3): для построения тестовой последовательности и создания единичных тестовых воздействий — обходчик 131
Рис. 3. Архитектура тестовой системы UniTESK. Обходчик является библиотечным компонентом тестовой системы UniTESK и предназначен вместе с итератором тестовых воздействий для построения тестовой последовательности. В основе обходчика лежит алгоритм обхода графа состояний обобщенной конечно-автоматной модели целевой системы (конечного автомата, моделирующего целевую систему на некотором уровне абстракции). Обходчики, реализованные в библиотеках инструментов UniTESK, требуют, чтобы обобщенная конечно-автоматная модель целевой системы, была детерминированной8 и имела сильно-связный граф состояний. Итератор тестовых воздействий работает под управлением обходчика и предназначен для перебора в каждом достижимом состоянии конечного автомата допустимых тестовых воздействий. Итератор тестовых воздействий автоматически генерируется из тестового сценария, представляющего собой неявное описание обобщенной конечно-автоматной модели целевой системы. Тестовый оракул оценивает правильность поведения целевой системы в ответ на единичное тестовое воздействие. Он автоматически генерируется на основе формальных спецификаций, описывающих требования к целевой системе в виде пред- и постусловий интерфейсных операций и инвариантов типов данных. 8
Исключение составляет обходчик ndfsm [16], позволяющий обходить графы состояний для некоторого класса недетерминированных конечных автоматов. 132
Медиатор связывает абстрактные формальные спецификации, описывающие требования к целевой системе, с конкретной реализацией целевой системы. Медиатор преобразует единичное тестовое воздействие из спецификационного представления в реализационное, а полученную в ответ реакцию — из реализационного представления в спецификационное. Также медиатор синхронизирует состояние спецификации с состоянием целевой системы. Трасса теста отражает события, происходящие в процессе тестирования. На основе трассы можно автоматически генерировать различные отчеты, помогающие анализировать результаты тестирования.
4.2. Инструмент разработки тестов CTESK Инструмент CTESK [11], который используется в описываемой работе, является реализацией концепции UniTESK для языка программирования C. Для разработки компонентов тестовой системы в нем используется язык SeC (specification extension of C), являющийся расширением ANSI C. Инструмент CTESK включает в себя транслятор из языка SeC в C, библиотеку поддержки тестовой системы, библиотеку спецификационных типов и генераторы отчетов. Компоненты тестовой системы UniTESK реализуются в инструменте CTESK с помощью специальных функций языка SeC, к которым относятся: спецификационные функции — содержат спецификацию непосредственной реакции целевой системы в ответ на единичное тестовое воздействие, а также определение структуры тестового покрытия;
функции отложенных реакций — отложенных реакций целевой системы;
медиаторные функции — связывают спецификационные функции с тестовыми воздействиями на целевую систему, а также реакции целевой системы с функциями отложенных реакций;
функция вычисления обобщенного состояния — вычисляет состояние обобщенной конечно-автоматной модели целевой системы;
сценарные функции — описывают набор тестовых воздействий для каждого достижимого обобщенного состояния.
содержат
спецификацию
В работах [14, 15] подробно описано, как базовая архитектура тестовой системы UniTESK может быть расширена для функционального тестирования моделей аппаратного обеспечения, разработанных на языках Verilog и SystemC. Там же приводятся технические детали, связанные с использованием инструмента CTESK для функционального тестирования таких моделей.
интерфейсом [16]. Эти средства были адаптированы для спецификации модулей аппаратного обеспечения. Рассмотрим подробнее процесс разработки спецификаций. Для каждой операции, реализуемой модулем, пишется спецификационная функция, в которой определяется предусловие операции и структура тестового покрытия. Постусловие спецификационной функции обычно возвращает true, поскольку все проверки, как правило, определяются в постусловиях микроопераций: // спецификация операции specification void operation_spec(...) { // предусловие операции pre { ... } // определение структуры тестового покрытия coverage C { ... } // постусловие операции обычно возвращает true post { return true; } } Для каждой микрооперации, входящей в состав специфицируемой операции, пишется функция отложенной реакции, в которой определяется ее постусловие: // спецификация микрооперации reaction Operation* micro_return(void) { // постусловие микрооперации post { ... } } Далее определяется функция временнóй композиции микроопераций, которая, во-первых, добавляет стимул (операцию вместе с набором аргументов) в очередь стимулов с указанием времени, необходимого для обработки стимула (time), во-вторых, для каждой микрооперации добавляет соответствующую реакцию в очередь реакций с указанием номера такта (относительно текущего времени), в конце которого следует осуществить проверку реакции (ticki): // временная композиция микропераций void operation_time_comp(...) { Operation *descriptor = create_operation(...); // добавление стимула в очередь стимулов register_stimulus(create_stimulus(time, descriptor)));
4.3. Использование CTESK для спецификации аппаратуры Инструмент разработки универсальные средства
тестов CTESK предоставляет достаточно для спецификации систем с асинхронным 133
// добавление реакций в очередь реакций 134
register_reaction(micro1_return, tick1, descriptor); ... register_reaction(micron_return, tickn, descriptor); } Очередь стимулов содержит стимулы, обрабатываемые модулем в текущее время. Для каждого стимула в очереди хранится время, которое он уже обрабатывается. Очередь реакций содержит еще не завершенные микрооперации. Для каждой микрооперации хранится время, через которое микрооперация завершится и можно будет осуществить проверку реакции. Изменение времени осуществляется функцией сдвига времени. После изменения времени вызываются функции обработки очередей стимулов и реакций. Функция обработки очереди стимулов удаляет из очереди полностью обработанные стимулы. Функция обработки очереди реакций регистрирует отложенные реакции для всех завершившихся микроопераций, которые после этого удаляются из очереди. Для иллюстрации синтаксиса языка SeC приведем очень простой пример. Рассмотрим устройство, называемое 8-ми битным счетчиком (Рис. 4).
Рис. 4. Схема входов и выходов 8-ми битного счетчика. Интерфейс счетчика состоит из двух двоичных входов clk и rst и одного 8ми битного выходного регистра cnt. Если уровень сигнала rst низкий, фронт сигнала тактового импульса clk увеличивает счетчик cnt по модулю 256; иначе счетчику присваивается значение 0. Ниже приводится спецификационная функция, описывающая операцию увеличения счетчика, то есть поведение счетчика в ответ на фронт clk при низком уровне сигнала rst. Поскольку операция является простой, спецификация выполнена без привлечения функций отложенных реакций.
// спецификация операции увеличения счетчика specification void increment_spec(counter_8bit *counter) updates cnt = counter->cnt, rst = counter->rst { // предусловие операции pre { return rst == false; } // определение структуры тестового покрытия coverage C { return { SingleBranch, "Single branch" }; } // постусловие операции post { return cnt == (@cnt + 1) % 0xff; } }
5. Сравнение с существующими подходами В данном разделе приводится сравнение предлагаемого подхода с существующими методами спецификации аппаратуры, поддерживаемыми современными языками верификации аппаратуры (HVL, hardware verification languages). Языки верификации аппаратуры, к которым относятся PSL, OpenVera, SystemVerilog и др. [17], включают в себя конструкции языков описания аппаратуры, языков программирования, а также специальные средства, ориентированные на разработку спецификаций и тестов. К последним относятся средства спецификации поведения, определения структуры тестового покрытия и генерации тестовых данных. Мы сравниваем только способы спецификации. Средства спецификации поведения, используемые в современных языках верификации аппаратуры, базируются на темпоральной логике линейного времени (LTL, linear temporal logic) и/или темпоральной логике ветвящегося времени (CTL, computation tree logic) [17]. Языки, по крайней мере, по части спецификации, имеют следующие корни: ForSpec (Intel) [18] (для языков, использующих логику LTL9) и Sugar (IBM) [19] (для языков, использующих логику CTL). Ниже приведена диаграмма, показывающая влияние некоторых языков верификации аппаратуры друг на друга. Логика CTL используется преимущественно для формальной верификации систем. Для целей симуляции и тестирования больший интерес представляет логика линейного времени LTL. Поскольку все языки верификации аппаратуры, в которых используется LTL, имеют схожие средства спецификации, для сравнения с предлагаемым подходом мы будем использовать только один из них — OpenVera [20]. Данный язык поддерживается многими инструментами; кроме того, он является открытым.
9
135
Вариант логики LTL, используемой в ForSpec, называется FTL (ForSpec temporal logic) [18]. 136
LTL
CTL
ForSpec
Sugar
OpenVera
PSL
В примере определяется событие переполнения счетчика e_overflow, а утверждение a_overflow запрещает возникновение такого события. В подходе OpenVera, как и в других подходах на основе темпоральных логик, упор делается на временнýю декомпозицию операций. Для каждой операции сначала выделяется ее временнáя структура — допустимые последовательности событий и задержки между ними; затем определяются предикаты, описывающие отдельные события; после этого предикаты, относящиеся к одному моменту времени, некоторым образом группируются (Рис. 6).
SystemVerilog
Требования на операцию
Рис. 5. Влияние языков верификации аппаратуры друг на друга.
временнáя декомпозиция
Язык OpenVera был разработан в 1995 году компанией Systems Science. Первоначальное название языка — Vera. В 1998 году System Science была поглощена компанией Synopsys. В 2001 году Synopsys сделала язык открытым и переименовала его в OpenVera. Для спецификации поведения OpenVera предоставляет специальный язык формулирования темпоральных утверждений (temporal assertions), который называется OVA (OpenVera assertions) [21, 22]. OVA оперирует с ограниченными по времени последовательностями событий, в которых можно обращаться к прошлому и будущему. Из простых последовательностей можно строить более сложные с помощью логических связок, таких как AND и OR, или используя регулярные выражения. В языке имеются средства объединения темпоральных утверждений в параметризованные библиотеки спецификаций. Проиллюстрируем синтаксис OVA на простом примере. // тактовый сигнал clock negedge(clk) { // граничные значения счетчика bool cnt_00: (cnt == 8'h00); bool cnt_ff: (cnt == 8'hff); // событие переполнения счетчика event e_overflow: cnt_ff #1 cnt_00; }
Требования на события
функциональная композиция
Спецификации событий
Рис. 6. Построения спецификации в подходах на основе темпоральных логик. В подходе, предлагаемом нами, основной акцент ставится на функциональную декомпозицию операций. Первым делом выделяется функциональная структура операции — набор микроопераций; каждая микрооперация специфицируется; после этого производится временнáя композиция спецификаций (Рис. 2). Мы полагаем, что функциональная структура операции более устойчива по сравнению с временнóй. Тем самым, подходы, основанные на функциональной декомпозиции операций, позволяют разрабатывать спецификации более устойчивые к изменениям реализации по сравнению с подходами на основе временнóй декомпозиции. К достоинствам предлагаемого подхода также можно отнести наглядность и простоту. Пред- и постусловия обычно понятнее формул темпоральной логики и не требуют от разработчика тестов каких-нибудь специальных знаний.
// утверждение, запрещающее переполнение счетчика assert a_overflow: forbid(e_overflow); 137
Спецификация операции
138
6. Опыт практического применения подхода Предлагаемый подход был применен на практике при тестировании буфера трансляции адресов (TLB, translation lookaside buffer) микропроцессора c MIPS64-совместимой архитектурой [23, 24]. Буфер трансляции адресов, входящий в состав большинства современных микропроцессоров, предназначен для кэширования таблицы страниц — таблицы операционной системы, хранящей соответствие между номерами виртуальных и физических страниц памяти. Использование такого буфера позволяет значительно увеличить скорость трансляции адресов. Буфер представляет собой ассоциативную память с фиксированным числом записей. Помимо интерфейса для трансляции адресов, он предоставляет интерфейс для чтения и изменения содержимого этой памяти. Трансляция виртуального адреса осуществляется следующим образом. Если буфер содержит запись с нужным номером виртуальной страницы, в выходном регистре модуля формируется соответствующий физический адрес; в противном случае, на одном из выходов модуля устанавливается сигнал, говорящий об отсутствии в буфере требуемой записи.
6.1. Структура и функциональность модуля Рассмотрим устройство тестируемого модуля. Память TLB состоит из 64 ячеек, которые составляют объединенный TLB (JTLB, joint TLB). Кроме того, для повышения производительности модуль содержит дополнительные буферы: TLB данных (DTLB, data TLB) и TLB инструкций (ITLB, instruction TLB). DTLB используется при трансляции адресов данных, ITLB — при трансляции адресов инструкций. Оба буфера содержат по 4 ячейки, содержимое буферов является подмножеством JTLB, обновление происходит по алгоритму LRU (last recently used). Каждая ячейка TLB условно делится на две секции: секция-ключ и секциязначение. Секция-ключ включает в себя спецификатор сегмента памяти (R), номер виртуальной страницы, деленный на два (VPN2), идентификатор процесса (ASID), бит глобальной трансляции адресов (G) и маску страницы (MASK). Секция-значение состоит из двух подсекций, каждая из которых содержит номер физической страницы (PFNi), бит разрешения чтения (Vi), бит разрешения записи (Di) и политику кэширования страницы (Ci). Какая именно подсекция будет использована при трансляции адреса, определяется младшим битом номера виртуальной страницы. Интерфейс тестируемого модуля TLB состоит из 30 входов (16 входов общего назначения, 3 входа DTLB, 4 входа ITLB, 7 входов JTLB) и 31 выходов (6 выходов общего назначения, 9 выходов DTLB, 8 выходов ITLB, 8 выходов
JTLB)10. Функциональность модуля включает операции записи, чтения и проверки наличия ячейки в памяти, а также операции трансляции адресов данных и инструкций. RTL-модель модуля разработана на языке Verilog и составляет 8 000 строк кода. R
12
2 PFN
1
24
VPN2 27 C 3
1
G
ASID
1
8
D V
1 1
PFN 0
C
D V
1 1
24
3
1 1
0
0
0
Рис. 7. Структура ячейки буфера трансляции адресов.
6.2. Разработка спецификаций модуля Основные требования к буферу трансляции адресов были получены в письменной форме от разработчиков модуля. В процессе формализации требования уточнялись в результате общения с разработчиками и чтения технической документации. Следует отметить, что все сформулированные разработчиками требования были легко представлены в форме пред- и постусловий. Проект продемонстрировал удобство и сравнительно небольшую трудоемкость разработки контрактных спецификаций для моделей аппаратного обеспечения. Спецификации были разработаны одним человеком за 2 недели, а их объем составил 2 500 строк кода на SeC. Отметим также, что в результате проекта было найдено несколько ошибок в реализации модуля.
7. Заключение Изначально контрактные спецификации были предложены для описания интерфейсов программных компонентов, но при определенной доработке их вполне можно использовать для описания модулей аппаратного обеспечения. Такие спецификации, с одной стороны, удобны для разработчиков, поскольку хорошо привязываются к архитектуре системы, с другой стороны, на их основе можно автоматически генерировать тестовые оракулы, проверяющие соответствие поведения целевой системы требованиям, описанным в 10
139
MASK
При подсчете числа входов и выходов не учитывался интерфейс JTAG (joint test action group) — стандартный интерфейс, используемый для тестирования аппаратуры с помощью метода граничного сканирования. 140
спецификациях. Практическая апробация подхода в проекте по тестированию буфера трансляции адресов микропроцессора показала удобство представления требований к аппаратуре в форме пред- и постусловий и продемонстрировала сравнительно небольшую трудоемкость разработки спецификаций. На настоящий момент нами получен определенный опыт использования технологии тестирования UniTESK и инструмента CTESK для спецификации и тестирования моделей аппаратного обеспечения. Опыт показывает, что некоторые шаги разработки тестов могут быть полностью или частично автоматизированы. Детальное исследование этого вопроса и разработка инструментальной поддержки для автоматизации шагов разработки тестов является основным направлением дальнейшей работы. Литература
[17] S.A. Edwards. Design and Verification Languages. Technical Report, Columbia University, New York, USA, November 2004. [18] R. Armoni, L. Fix, A. Flaisher, R. Gerth, B. Ginsburg, T. Kanza, A. Landver, S. MadorHaim, E. Singerman, A. Tiemeyer, M. Vardi, and Y. Zbar. The ForSpec Temporal Logic: A New Temporal Property-Specification Language. Tools and Algorithms for Construction and Analysis of Systems, 2002. [19] I. Beer, S. Ben-David, C. Eisner, D. Fisman, A. Gringauze, and Y. Rodeh. The Temporal Logic Sugar. Lecture Notes in Computer Science, 2001. [20] http://www.open-vera.com [21] OpenVera® Language Reference Manual: Assertions. Version 1.4.1, November 2004. [22] OpenVera® Assertions. Blueprint for Productivity and Product Quality. March 2003. (Опубликовано на http://www.synopsys.com/products/simulation/ova_wp.html) [23] http://www.mips.com/content/Products/Architecture/MIPS64 [24] MIPS64™ Architecture For Programmers. Revision 2.0. MIPS Tecnologies Inc., June 9, 2003.
[1] Statistical Analysis of Floating Point Flaw in the Pentium Processor. Intel Corporation, November 1994. [2] B. Beizer. The Pentium Bug – An Industry Watershed. Testing Techniques Newsletter (TTN), TTN Online Edition, September 1995. [3] А. Wolfe. For Intel, It’s a Case of FPU All Over Again. EE Times, May 1997. [4] B. Meyer. Design by Contract. Technical Report TR-EI-12/CO, Interactive Software Engineering Inc., 1986. [5] B. Meyer. Applying `Design by Contract'. IEEE Computer, vol. 25, No. 10, October 1992. [6] А.В. Баранцев, И.Б. Бурдонов, А.В. Демаков, С.В. Зеленов, А.С. Косачев, В.В. Кулямин, В.А. Омельченко, Н.В. Пакулин, А.К. Петренко, А.В. Хорошилов. Подход UniTesK к разработке тестов: достижения и перспективы. (Опубликовано на http://www.citforum.ru/SE/testing/unitesk/) [7] IEEE Standard VHDL Language Reference Manual. IEEE Std 1076-1987. [8] IEEE Standard Hardware Description Language Based on the Verilog Hardware Description Language. IEEE Std 1364-1995. [9] http://www.systemc.org [10] http://www.systemverilog.org [11] http://www.unitesk.com [12] http://www.ispras.ru [13] I. Bourdonov, A. Kossatchev, V. Kuliamin, and A. Petrenko. UniTesK Test Suite Architecture. FME’2002. LNCS 2391, Springer-Verlag, 2002. [14] В.П. Иванников, А.С. Камкин, В.В. Кулямин, А.П. Петренко. Применение технологии UniTESK для функционального тестирования моделей аппаратного обеспечения. Препринт 8, Институт системного программирования РАН, Москва, 2005. (Опубликовано на http://citforum.ru/SE/testing/unitesk_hard/) [15] A. Kamkin. The UniTESK Approach to Specification-Based Validation of Hardware Designs. IEEE-ISoLA’2006: The 2nd International Symposium on Leveraging Applications of Formal Methods, Verification and Validation, November 2006. [16] А.В. Хорошилов. Спецификация и тестирование систем с асинхронным интерфейсом. Препринт 12, Институт системного программирования РАН, Москва, 2006. (Опубликовано на http://www.citforum.ru/SE/testing/asynchronous_interface/)
141
142
Особенности применения технологии UniTESK для тестирования функций мобильности в протоколе IPv6 Д.В. Зацепин, В.З. Шнитман Аннотация. Статья посвящена разработке тестового набора для проверки соответствий реализаций мобильного узла спецификациям протокола Mobile IPv6 [1]. Для построения тестового набора использовалась передовая технология автоматического тестирования UniTESK [2] и инструмент CTesK [3], разработанный на основе этой технологии. В ходе выполнения работы было выявлено несколько особенностей поведения одного из объектов протокола – мобильного узла, которые затрудняют его тестирование в рамках указанной технологии. В статье подробно описаны эти особенности и способы преодоления трудных моментов в условиях ограничений технологии UniTESK и поддерживающего эту технологию инструмента CTesK. Работа выполнялась в Институте системного программирования РАН в рамках проекта «Верификация функций безопасности и мобильности протоколов IP» при поддержке гранта РФФИ № 04-07-90308.
1. Введение В последнее время мобильные устройства получают все большее распространение, и потребность постоянного доступа к сети Интернет таких устройств является естественной. Предполагается, что в недалеком будущем Интернет примет на вооружение протокол нового поколения IPv6 [4]. Для поддержки функций мобильности техническим комитетом по проектированию Интернет (Internet Engineering Task Force, IETF) разработано расширение этого нового протокола, получившее название Mobile IPv6, и выпущен документ RFC 3775 "Mobility Support in IPv6" [1], который можно рассматривать в качестве будущего стандарта (процесс стандартизации этого расширения, как и самого протокола IPv6, на сегодняшний день еще не завершен). Функциональность Mobile IPv6 нацелена на поддержку целого ряда устройств, в частности, персональных компьютеров, ноутбуков, карманных компьютеров, мобильных телефонов, маршрутизаторов и точек доступа для беспроводных сетей. Очевидно, в перспективе ожидается появление большого количества реализаций Mobile IPv6 от различных производителей. 143
Тестирование соответствия стандартам является одним из основных средств обеспечения совместимости реализаций различных производителей и позволяет решать вопросы повышения надежности и отказоустойчивости глобальной сети. В настоящее время отсутствие средств такого тестирования является одной из причин, препятствующих внедрению перспективных технологий в практику. В Институте системного программирования РАН (ИСП РАН) в течение ряда лет ведутся работы по исследованию и развитию методов формального моделирования телекоммуникационных протоколов. В частности, одним из направлений работ по проекту «Верификация функций безопасности и мобильности протоколов IP», который выполнялся при поддержке гранта РФФИ № 04-07-90308, была разработка тестового набора, обеспечивающего проверку соответствия реализаций стандарту Mobile IPv6. В качестве основы для построения такого тестового набора использовался опыт создания и внедрения разработанной в ИСП РАН методологии тестирования на основе формальных спецификаций UniTESK [2]. В статье рассматриваются некоторые особенности протокола Mobile IPv6, затрудняющие тестирование реализаций с помощью этой технологии, а также способы преодоления возникших трудностей в рамках ее ограничений. В качестве объекта тестирования принята реализация основного объекта протокола Mobile IPv6 – мобильного узла (узла, который может совершать перемещения между различными сегментами сети Интернет). Такой выбор обусловлен тем, что тестирование мобильного узла представляет наибольшую сложность, в частности, из-за того, что он является инициатором большинства служебных процедур обмена протокольными сообщениями. Статья содержит восемь разделов, включая введение и заключение. В следующих двух разделах кратко рассматриваются вопросы функционирования протокола Mobile IPv6, а также возможности инструмента CTesK [3], реализующего технологию UniTESK. Четвертый раздел посвящен проблемам тестирования мобильного узла и способам их решения. В пятом разделе описываются структура разработанного тестового набора и этапы исполнения сценарных функций. В шестом разделе рассматриваются способы адаптации инструмента CTesK к предложенной схеме тестового набора. Седьмой раздел посвящен результатам применения разработанного тестового набора для тестирования одной из известных реализаций Mobile IPv6. В заключении приведены основные выводы и результаты работы.
2. Обзор протокола Mobile IPv6 Данный раздел является кратким введением в протокол Mobile IPv6. Как уже отмечалось, этот протокол является расширением протокола нового поколения IPv6, которое позволяет узлу, совершающему перемещения по сети Интернет, все время оставаться доступным по адресу, который он имел в 144
первоначальной (домашней) сети. Такая возможность позволяет избежать потери соединений транспортного уровня при перемещениях межсетевого уровня модели TCP/IP, а также дает возможность другим узлам устанавливать соединение с мобильным узлом (Mobile Node) в то время, когда он находится за пределами домашней сети. Для простоты мы будем рассматривать случай, когда у мобильного узла есть только один домашний адрес. Поведение мобильного узла в случае нескольких домашних адресов можно считать аналогичным.
Для того чтобы этого не происходило, в протоколе Mobile IPv6 вводится специальный вид маршрутизаторов, которые называются домашними агентами (Home Agents). Домашний агент (см. Рис. 2) всегда находится в домашней сети и осуществляет переадресацию пакетов мобильному узлу в то время, когда последний находится за пределами домашней сети. Это осуществляется при помощи механизма, который называется двусторонним туннелированием (Bi-directional Tunneling).
Когда мобильный узел находится в домашней сети, никакой дополнительной функциональности от узлов, участвующих во взаимодействии, не требуется, и мобильный узел ведет себя как обычный IPv6-узел (см. Рис. 1). Когда мобильный узел переходит в сеть, которая обслуживается другими маршрутизаторами и имеет другие сетевые префиксы, без дополнительной поддержки со стороны Mobile IPv6 узел становится недоступным по своему домашнему адресу. Пакеты, отправляемые ему другими узлами, будут попрежнему приходить в домашнюю сеть, и не будут доходить до мобильного узла. Это приведет к потере установленных соединений.
Рис. 2. Мобильный узел вне дома
Рис. 1. Мобильный узел в домашней сети 145
Структура IPv6-пакета подробно описана в RFC 2460 [4]. Однако здесь следует отметить, что в протоколе IPv6 в пакет могут добавляться заголовки расширения, которые содержат некоторую дополнительную информацию о том, как этот пакет должен обрабатываться конечным получателем или промежуточными узлами сети. Заголовки расширения связаны в односвязный список при помощи поля Next Header, которое содержится во всех заголовках уровня IP. Поле Next Header последнего заголовка расширения IPv6-пакета указывает на протокол транспортного уровня, сегмент которого передается в качестве полезной нагрузки. Каждый из заголовков расширения имеет фиксированную часть, которая всегда должна присутствовать в заголовке данного типа, и может содержать дополнительную часть в виде опций. Опции располагаются в конце заголовка и описываются в представлении TLV (TypeLength-Value). 146
Когда мобильный узел попадает во внешнюю сеть, он формирует один или несколько временных адресов (Care-of Addresses) согласно механизму автоматической конфигурации адресов [5], который используется во внешней сети. После этого мобильный узел выбирает один из своих временных адресов и сообщает его домашнему агенту; такой адрес называется основным временным адресом (Primary Care-of Address). Это осуществляется при помощи процедуры, которая называется регистрацией основного временного адреса (Primary Care-of Address Registration). Для этого мобильный узел отправляет домашнему агенту служебное сообщение Binding Update со своего основного временного адреса. В этом сообщении мобильный узел при помощи заголовка расширения Destination Options, содержащего опцию Home Address, указывает свой домашний адрес, для которого выполняется данная процедура. В ответ на сообщение Binding Update домашний агент отправляет служебное сообщение Binding Acknowledgement, которое либо свидетельствует о том, что сообщение Binding Update принято, либо указывает причину, по которой оно было отклонено. Для обеспечения безопасности такого обмена сообщениями используются средства протокола IPsec. Перед отправлением сообщения Binding Acknowledgement домашний агент запоминает связь между домашним адресом мобильного узла и его основным временным адресом в структуре данных, которая носит название кэш привязок (Binding Cache), а сама связь называется привязкой (binding). После установления привязки домашний агент начинает перехватывать пакеты, которые приходят на домашний адрес мобильного узла, и через IPv6туннель пересылать их мобильному узлу на его основной временный адрес. Такой перехват пакетов, направленных мобильному узлу, осуществляется при помощи механизма, называемого Proxy Neighbor Discovery. Суть этого механизма заключается в том, что домашний агент отправляет необходимые сообщения Neighbor Advertisement, используемые в механизмах Address Resolution и Neighbor Unreachability Detection, от имени мобильного узла, но со своим адресом канального уровня. Таким образом, мобильный узел, находясь в чужой сети, через IPv6-туннель с домашним агентом, получает сообщения, которые отправляются ему в домашнюю сеть. Мобильный узел, в свою очередь, поддерживает структуру данных, которая называется Binding Update List. В этой структуре данных содержится информация обо всех привязках и об их состоянии. Перед отправлением пакета в сеть мобильный узел просматривает Binding Update List. Если там есть привязка, для которой зарегистрированный домашний адрес совпадает с адресом отправителя пакета (Source Address), то пакет отправляется не напрямую, а через IPv6-туннель с домашним агентом, указанным в привязке. Домашний агент, получив из IPv6-туннеля такой пакет, отправляет его из домашней сети по обычному маршруту. Таким образом, мобильный узел всегда остается доступным по своему домашнему адресу. 147
Узел, с которым мобильный узел обменивается сообщениями, называется узлом-корреспондентом (Correspondent Node). Для обмена информацией с мобильным узлом при помощи двустороннего туннелирования от узлакорреспондента требуется лишь поддержка базовой функциональности IPv6. Дополнительной поддержки Mobile IPv6 от узла-корреспондента в этом случае не требуется. Однако двустороннее туннелирование создает дополнительный обмен сообщениями и дополнительную нагрузку на домашнего агента. Протокол Mobile IPv6 позволяет этого избежать и производить обмен информацией напрямую (не через домашнюю сеть), если узел-корреспондент поддерживает дополнительную функциональность Mobile IPv6, которая называется оптимизацией маршрута (Route Optimization). Узел-корреспондент, поддерживающий оптимизацию маршрута, так же как и домашний агент, имеет кэш привязок, в котором содержится информация о привязках с мобильными узлами и об их состояниях. У мобильного узла, в свою очередь, в структуре Binding Update List содержится информация о привязках с узлами-корреспондентами. Перед отправлением в сеть пакета, у которого адрес отправителя совпадает с домашним адресом, мобильный узел просматривает Binding Update List, и если находит привязку с узлом-корреспондентом, адрес которого совпадает с адресом получателя пакета, то пакет отправляется напрямую. При этом в качестве адреса отправителя пакета указывается временный адрес, а в сообщение добавляется заголовок расширения Destination Options, который содержит опцию Home Address с домашним адресом мобильного узла. Если же такой привязки нет, то мобильный узел ищет привязку с домашним агентом и отправляет пакет через туннель домашнему агенту, который пересылает его узлу-корреспонденту. При получении пакета с опцией Home Address узел-корреспондент ищет в кэше привязок запись, у которой временный адрес совпадает с адресом отправителя, указанным в пакете, а домашний адрес совпадает с адресом, указанным в опции Home Address. Если такая привязка находится, то узелкорреспондент меняет местами адрес отправителя пакета и адрес, указанный в опции Home Address; затем пакет передается на дальнейшую обработку. При отправлении пакета в сеть узел-корреспондент просматривает кэш привязок и ищет там привязку, у которой домашний адрес мобильного узла совпадает с адресом получателя пакета (Destination Address). Если такая привязка находится, то узел-корреспондент подставляет временный адрес, указанный в привязке, вместо адреса получателя, и в пакет добавляется заголовок расширения Routing Header Type 2, в котором указывается домашний адрес мобильного узла. При получении такого пакета мобильный узел проверяет, что адрес, указанный в заголовке Routing Header Type 2, является его домашним адресом и меняет местами адрес получателя пакета с адресом, указанным в данном заголовке. Затем пакет передается на дальнейшую обработку. Такой механизм 148
обмена сообщениями между мобильным узлом и узлом-корреспондентом (см. Рис. 2) является прозрачным для протоколов верхнего уровня и носит название оптимизации маршрута. Механизм установления привязки с узлом-корреспондентом аналогичен механизму установления привязки с домашним агентом. Для этого мобильный узел отправляет сообщение Binding Update узлу-корреспонденту, в котором содержатся домашний и временный адрес. В некоторых случаях узелкорреспондент должен отправить в ответ сообщение Binding Acknowledgement, подтверждающее установление привязки, хотя обычно этого не требуется. Для обеспечения безопасности механизма установления привязки с узломкорреспондентом в сообщения Binding Update и Binding Acknowledgement добавляется опция Binding Authorization Data, которая содержит криптографическую хэш-сумму этих сообщений. Хэш-сумма вычисляется по алгоритму HMAC_SHA1 с использованием ключа управления привязкой (Kbm), который генерируется перед обменом сообщениями Binding Update и Binding Acknowledgement при помощи процедуры обратной маршрутизируемости (Return Routability Procedure).
Рис. 3. Процедура обратной маршрутизируемости (return routability procedure) Процедура обратной маршрутизируемости выполняется с целью аутентификации узла-корреспондента. Для этого обмен сообщениями с узломкорреспондентом производится по двум маршрутам: напрямую и через домашнюю сеть (см. Рис. 3). А именно, мобильный узел посылает со своего временного адреса сообщение Home Test Init через туннель с домашним агентом, а сообщение Care-of Test Init – напрямую. В ответ на эти сообщения узел-корреспондент посылает сообщения Home Test на домашний адрес и Care-of Test на временный адрес мобильного узла; эти сообщения содержат 149
маркеры home keygen token и care-of keygen token соответственно. При помощи этих маркеров и генерируется ключ управления привязкой (Kbm). Протокол Mobile IPv6 включает в себя несколько вспомогательных служебных процедур обмена сообщениями для мобильного узла: Generic Movement Detection, Dynamic Home Agent Address Discovery и Mobile Prefix Discovery. Эти процедуры используют сообщения протокола ICMPv6. Процедура Generic Movement Detection позволяет отследить перемещение мобильного узла при помощи средств протокола Neighbor Discovery [6]. Для двух оставшихся процедур вводятся четыре новых типа сообщений протокола ICMPv6 [7]. Процедура Dynamic Home Agent Address Discovery позволяет узнать адрес домашнего агента в то время, когда мобильный узел находится за пределами домашней сети. Процедура Mobile Prefix Discovery позволяет мобильному узлу узнать об изменении префиксов в домашней сети в то время, когда мобильный узел находится за ее пределами, и, тем самым, принимать участие в смене префиксов домашней сети (Network Renumbering).
3. Инструмент CTesK Для построения тестового набора в данной работе использовался инструмент CTesK, который позволяет эффективно распараллелить разработку тестового набора и автоматизировать разработку тестовых сценариев. Ниже приведено краткое описание возможностей этого инструмента и обзор технологии UniTESK, которую он реализует. В инструменте CTesK объектом тестирования является целевая система. У этой системы имеется набор интерфейсов, с помощью которых она взаимодействует с окружением. Набор этих интерфейсов изначально определен и специфицирован. Все требования, которые предъявляются к системе, накладываются на наблюдаемые действия системы, которые можно отследить через интерфейсы ее взаимодействия с окружением. Эти требования не затрагивают внутренней реализации системы, т.е. тестирование производится по принципу «черного ящика». При использовании инструмента CTesK требования, предъявляемые к целевой системе, должны быть описаны на формальном языке SeC, который является спецификационным расширением языка C. Впоследствии из таких требований автоматически генерируется исполняемый код, который используется в процессе тестирования. Взаимодействие с целевой системой осуществляется при помощи стимулов и реакций. Стимулами называются средства, при помощи которых можно передавать целевой системе какую-либо информацию. Целевая система может непосредственно возвращать некоторый ответ на стимул, а также изменять свое внутреннее состояние. Такой ответ и изменение состояния, если имеется доступ к состоянию системы, возвращаются в тестовую систему, и производится проверка соответствия поведения системы наложенным на нее 150
требованиям. Требования описываются на формальном языке в виде постусловий. Для стимулов в виде предусловия описываются ограничения, при которых можно вызывать данный стимул. Дополнительно могут накладываться ограничения на возможные значения данных определенного типа в виде инвариантов. Некоторые следствия работы системы и результаты воздействия на нее определенных стимулов могут наблюдаться не сразу, а после завершения внутренних процессов. Такое поведение приводит к тому, что реакция на стимул поступает наружу через какое-то время после воздействия на систему одного или нескольких стимулов, или может даже проявляться без воздействия стимулов на систему. Такую информацию не во всех случаях можно охарактеризовать как ответ на какой-либо стимул, и она называется реакцией (отложенной реакцией) системы. На реакции системы обычно также накладывается ряд требований. Аналогично случаю со стимулами, в инструменте CTesK они описываются в виде пред- и постусловий. Предусловия определяют ограничения на состояния системы, при которых может происходить данная реакция. А постусловия описывают ограничения на саму реакцию, то есть на данные, которые передаются окружению при помощи этой реакции, и на изменение состояния целевой системы, которое может происходить при возникновении такой реакции. Для описания требований к стимулам и реакциям тестовый набор должен обладать информацией о состоянии целевой системы и о его изменениях. Для этого тестовым набором поддерживаются структуры данных, моделирующие это состояние. Далее состояние этих структур данных будет называться модельным состоянием. Если имеется доступ к внутренним структурам данных целевой системы, описывающих это состояние, то возможна синхронизация состояния модели и целевой системы. В таком случае возможна проверка требований, касающихся изменения состояния внутренних структур данных. В противном случае состояние модели и состояние целевой системы изменяются независимо и синхронизируются лишь на начальном этапе тестирования. При этом требования накладываются только на действия системы, наблюдаемые извне. Нарушение требований обработки внутренних структур данных проверяются лишь неявно при последующих некорректных ответах на стимулы и возникновении некорректных реакций. Тестовый набор, разрабатываемый при помощи инструмента CTesK, может быть ориентирован не на конкретную целевую систему, а на группу целевых систем, поведение которых описывается одними и теми же требованиями. Причем некоторые целевые реализации могут предоставлять доступ к внутренним структурам данных, а другие нет. Для обеспечения универсальности в тестовый набор включается специальная часть, которая называется медиатором. Для каждого отдельного стимула медиатор осуществляет преобразование данных из модельного представления тестового набора в представление целевой системы, а для каждой реакции выполняет 151
обратное преобразование. Таким образом, медиатор зависит от конкретной целевой системы. Поэтому, для того чтобы произвести тестирование новой целевой системы, необходимо заново разработать для нее медиаторы. Вся информация о ходе тестирования сохраняется в специальном файле, который называется тестовой трассой. По данным, которые содержатся в этом файле, генерируется отчет об ошибках и покрытии. Трасса хранится в формате XML, что позволяет легко разрабатывать средства для ее анализа. В инструменте CTesK имеются средства для автоматического измерения тестового покрытия. Для этого в описание стимула добавляется специальный блок coverage, в котором, исходя из модельного состояния и параметров вызова стимула, определяется, какую из ветвей функциональности целевой системы задействует данный стимул. Инструмент CTesK включает в себя утилиту, которая после завершения тестирования по информации, содержащейся в тестовой трассе, генерирует подробную статистику и отчет о тестовом покрытии. Стимулы и реакции на языке SeC описываются в виде спецификационных функций. Для стимула такая спецификационная функция включает в себя предусловие, постусловие и блок coverage. Для реакции – только предусловие и постусловие. Для каждой спецификационной функции разрабатывается медиатор, который оформляется в виде функции. Медиатор для стимула имеет два блока: блок call, который отвечает за передачу соответствующего стимула реализации и за получение результата, и блок state, который отвечает за обновление модельного состояния. Для реакции медиатор содержит только блок state, который выполняет аналогичную функцию. В инструменте CTesK термин тестовый сценарий используется для обозначения группы тестовых операций, обычно предназначаемых для тестирования логически связанных аспектов функциональности целевой системы. Если в ходе исполнения тестового сценария обнаруживается ошибка, то тестовый сценарий завершается. Такая мера является вынужденной, так как после возникновения ошибки состояния тестового набора и целевой системы оказываются не синхронизированными. Выполнение тестового сценария заключается в вызовах сценарных функций. Сценарная функция содержит исполняемый код и вызовы стимулов. В ходе выполнения сценарной функции собираются реакции от системы (см. Рис. 4). После завершения сценарной функции запускается механизм, называемый сериализацией. В процессе сериализации перебираются возможные порядки вызовов стимулов и наступления реакций (см. Рис. 5). Чтобы поведение целевой системы могло считаться корректным, требуется, чтобы хотя бы один из порядков удовлетворял требованиям в виде пред- и постусловий стимулов и реакций.
152
промежуточные состояния называются нестационарными. Вызов сценарной функции должен обязательно закончиться в стационарном состоянии, иначе поведение целевой системы является некорректным. Кроме того, все допустимые последовательности сериализации, которые заканчиваются в стационарном состоянии, должны приводить к одному и тому же состоянию. Иначе выполнение тестового сценария будет прервано с сообщением об ошибке. В функциях, выполняющих сериализацию, используются методы сохранения состояния модели, восстановления состояния модели, а также определения, является ли данное состояние стационарным. Если имеется информация о частичном порядке вызовов стимулов и поступления реакций, то можно существенно сократить процесс сериализации по сравнению с полным перебором или даже свести ее к рассмотрению одного единственного варианта. В инструменте CTesK предусмотрены средства для разбиения множества модельных состояний на группы. Такое разбиение называется факторизацией состояний, а полученные обобщенные состояния – состояниями тестового сценария. На выбор факторизации накладывается следующее ограничение: после вызова определенной сценарной функции из определенного начального состояния тестового сценария тестовый сценарий всегда должен перейти в одно и то же конечное состояние. Пример факторизации модельных состояний схематично представлен на Рис. 6.
Рис. 4. Исполнение сценарной функции
Рис. 5. Один из сценариев сериализации
Рис. 6. Переходы в модельных состояниях и состояниях тестового сценария
В процессе проверки одного из порядков сериализации тестовый сценарий может проходить через последовательность состояний модели. Такие 153
154
Для осуществления факторизации разработчик должен предоставить тестовому сценарию функцию, которая по текущему модельному состоянию возвращает состояние тестового сценария. При разработке тестового сценария разработчик определяет начальное модельное состояние и набор сценарных функций. Тестовый сценарий автоматически выбирает порядок вызовов сценарных функций. Такую функциональность осуществляет компонент под названием обходчик. Обходчик перебирает вызовы всех возможных сценарных функций во всех достижимых состояниях тестового сценария. Это осуществляется при помощи обхода графа, вершинам которого соответствуют состояния тестового сценария, а дугам – переходы по вызовам сценарных функций. Такой граф задается для тестового сценария неизбыточным образом путем указания начального состояния тестового сценария, набора сценарных функций и функции, определяющей состояние тестового сценария после выполнения сценарной функции. Инструмент CTesK включает в себя два обходчика: dfsm [8], который реализует обход в глубину графа состояний тестового сценария, и ndfsm [9], использующий жадный алгоритм для выбора ближайшей вершины, в которой применялись не все возможные воздействия. На практике второй обходчик оказывается более эффективным, поэтому он и использовался в разрабатываемом тестовом наборе. Для корректной работы обоих обходчиков граф состояний тестового сценария должен удовлетворять требованиям связности и детерминированности. Связность в данном случае может формулироваться следующим образом: из любого состояния тестового сценария, в которое можно попасть из начального состояния при помощи последовательности вызовов сценарных функций, можно вернуться в начальное состояние при помощи последовательности вызовов сценарных функций. Условие детерминированности означает, что при вызове одной и той же сценарной функции из одного и того же состояния тестового сценария мы всегда попадаем в одно и тоже конечное состояние тестового сценария. Обходчик в своей работе может пользоваться компонентом под названием итератор. Итератор используется, если в сценарной функции на разных этапах ее запуска необходимо вызывать стимулы с различными параметрами. Такую функциональность можно осуществить при помощи написания отдельной сценарной функции для каждой группы параметров, но в таком случае может понадобиться очень большое количество сценарных функций. В языке SeС используется специальная конструкция iterate для перебора параметров стимула. Синтаксис этой конструкции напоминает синтаксис оператора цикла for. Счетчик цикла для этой конструкции называется счетчиком итерации. Таким образом, когда обходчик встречает в коде сценарной функции такую синтаксическую конструкцию, он вызывает итератор. Итератор возвращает значения счетчиков итерации для вызова 155
данной сценарной функции так, чтобы в каждом состоянии тестового сценария осуществлялся перебор всех возможных наборов счетчиков итерации. С использованием итератора обходчик вызывает все сценарные функции со всеми возможными значениями счетчиков итерации во всех достижимых состояниях тестового сценария. Такая автоматизация существенно упрощает разработку тестового сценария.
4. Проблемы тестирования способы их решения
мобильного
узла
и
Реализация функциональности мобильного узла интегрируется в IPv6-стек, и воздействовать на нее можно двумя способами: при помощи отправления сообщений с транспортного уровня и при помощи сообщений с канального уровня. Кроме того, в конкретных реализациях Mobile IPv6 могут предоставляться средства для чтения или изменения внутренних структур данных, описывающих состояние реализации. При сравнении двух различных реализаций эти интерфейсы могут существенно отличаться, так как стандарт не накладывает на них каких-либо ограничений. В некоторых реализациях Mobile IPv6 таких интерфейсов может вообще не быть, поэтому при разработке тестового набора было принято решение не использовать взаимодействие с целевой реализацией при помощи интерфейсов доступа к внутренним управляющим структурам данных. Более того, с целью построения универсального тестового набора, а также для упрощения синхронизации и анализа результатов, полученных в ходе исполнения сценарных функций, воздействия на целевую реализацию при помощи сообщений с транспортного уровня также не используются. Единственный оставшийся способ взаимодействия заключается в обмене сообщениями через сетевой интерфейс. Таким образом, в принятом подходе воздействиями на тестируемую реализацию, или стимулами являются сообщения со стороны тестирующего узла, а ответами реализации или реакциями – сообщения со стороны мобильного узла. Для поддержания постоянной достижимости мобильного узла по его домашнему адресу протокол Mobile IPv6 содержит ряд служебных процедур обмена сообщениями между мобильным узлом и домашним агентом или между мобильным узлом и узлом-корреспондентом, например, процедуру регистрации на домашнем агенте (Home Registration), процедуру регистрации на узле-корреспонденте (Correspondent Registration), процедуру обратной маршрутизируемости (Return Routability Procedure) и так далее. Заметим, что мобильный узел является инициатором выполнения большинства служебных процедур протокола Mobile IPv6. Это создает некоторые дополнительные сложности при организации тестирования. В соответствии с требованиями стандарта RFC 3775 мобильный узел должен инициировать такие процедуры либо после смены точки подключения, либо 156
по истечении определенного временного интервала после какого-либо события, хотя некоторые служебные обмены сообщениями могут инициироваться мобильным узлом в любое время. Таким образом, единственное явное воздействие, которое может заставить мобильный узел начать служебные обмены сообщениями, – это смена точки подключения мобильного узла. Для упрощения схемы тестового стенда и возможности создания для реализации мобильного узла некоторых внештатных ситуаций было принято решение об имитации процесса смены точки его подключения. При этом фактической смены точки подключения мобильного узла не происходит. Тестовый стенд состоит из двух узлов, соединенных сегментом Ethernet. Один из узлов – тестируемый узел. На тестируемом узле статически настраивается и запускается целевая реализация мобильного узла. Второй узел является инструментальным. На этом узле функционирует тестовый набор, который имитирует для тестируемой реализации некоторую виртуальную сеть. Смена точки подключения мобильного узла имитируется при помощи вспомогательных сообщений Router Advertisement. Такие сообщения периодически отправляются тестовым сценарием от имени маршрутизатора в той сети, в которой мобильный узел находится в данный момент времени. Для имитации смены точки подключения мобильного узла тестовый сценарий прекращает отправление сообщений Router Advertisement от имени маршрутизатора в текущей сети и начинает отправление аналогичных сообщений от имени маршрутизатора в новой сети. Для реализации описанной выше модели тестирующий узел должен имитировать функциональность всех узлов, которые принимают участие в обмене сообщениями с мобильным узлом в модели виртуальной сети, т.е. отправлять все необходимые сообщения от имени узлов виртуальной сети. При этом некоторые из таких сообщений являются частью определенных процедур и должны отправляться синхронно в ответ на сообщения от мобильного узла. Не углубляясь в детали, поведение мобильного узла в целом можно охарактеризовать следующим образом. При смене точки подключения мобильный узел должен выполнить несколько служебных обменов сообщениями с домашним агентом и узлами-корреспондентами. В результате этих обменов будут установлены необходимые привязки, и мобильный узел станет доступным по своему домашнему адресу. После этого мобильный узел начинает служебные обмены сообщениями только перед истечением времени жизни привязки, домашнего адреса или временного адреса. Заметим, что после перехода в новую сеть мобильный узел может выполнять некоторые из процедур обмена сообщениями параллельно, что делает его поведение недетерминированным. Например, процедура регистрации на узлекорреспонденте может выполняться параллельно с процедурой Mobile Prefix Discovery. Кроме того, стандарт допускает недетерминированное поведение 157
мобильного узла в рамках одной служебной процедуры. Например, перед регистрацией на узле-корреспонденте мобильный узел может выполнить процедуру обратной маршрутизируемости либо полностью, либо частично, а в некоторых случаях может ее не выполнять вообще. Некоторые сообщения тестируемая реализация должна отправлять через определенное время после поступления стимула. Из-за таких особенностей поведения мобильного узла возникают затруднения с определением группы сообщений, которые можно охарактеризовать как воздействие и отклик на него. Такая группа может охватывать несколько служебных процедур, и для нее будет сложно построить спецификации методом Design-by-Contract [10]. Для того чтобы избежать таких сложностей, используется механизм отложенных реакций инструмента CTesK, который позволяет не указывать в явном виде связь между стимулом и реакцией. Заметим также, что инструмент CTesK, который использовался для разработки тестового набора, позволяет проводить тестирование лишь с использованием стационарных модельных состояний. Однако мобильный узел не имеет стационарных состояний за исключением некоторых внештатных ситуаций. Для преодоления этого ограничения исследовалась возможность применения двух различных подходов. Первый подход заключался в использовании при работе обходчика состояний, близких к стационарным. Если для привязок и адресов использовать достаточно большие значения времени жизни, то состояние мобильного узла, после смены точки подключения и завершения служебных процедур, можно рассматривать как «приближенно» стационарное, которое пригодно для используемого метода тестирования. Слово «приближенно» используется потому, что такие состояния в действительности не являются стационарными. Для того чтобы мобильный узел находился в таком состоянии длительное время, он должен периодически получать от маршрутизатора сообщения Router Advertisement и выполнять процедуру Neighbor Unreachability Detection. Для обхода графа таких состояний можно ввести группу стимулов, которые производят смену точки подключения мобильного узла. Такие стимулы будут отличаться от отдельных сообщений, отправляемых тестовым сценарием, так как будут действовать на всю виртуальную сеть, а не на мобильный узел в отдельности. При воздействии таким стимулом состояние тестируемой реализации явно не изменяется, а изменяется лишь состояние виртуальной сети в целом. Но благодаря обмену сообщениями между узлами виртуальной сети впоследствии изменится и состояние тестируемой реализации. После воздействия одним из таких стимулов мобильный узел должен через некоторое время перейти в приближенно стационарное состояние, а значит, такие стимулы уже можно использовать в инструменте CTesK. Такой подход использовался в ранней версии тестового набора, который проверял только установление привязок с домашним агентом. Однако при расширении этого тестового набора возникли дополнительные трудности, и от 158
использования таких приближенно стационарных состояний пришлось отказаться. Первая проблема заключается в том, что стандарт RFC 3775 не содержит исчерпывающего описания ограничений на то, какие сообщения мобильный узел может отправлять, находясь в определенном состоянии. Более того, стандарт указывает, что некоторые служебные процедуры, такие как формирование временного адреса и регистрация на узле-корреспонденте, мобильный узел может начать в любой момент времени. Вторая проблема возникает, когда при тестировании мобильного узла необходимо использовать небольшие значения времени жизни для привязок. Из-за недетерминированного поведения мобильного узла сложно предсказать время, когда привязка будет установлена, а значит и время, когда реализация мобильного узла должна отправить сообщение Binding Update для обновления активно используемой привязки, время жизни которой вскоре истечет. При использовании небольших значений времени жизни привязки такая реакция может возникнуть во время анализа результатов выполнения сценарной функции, что в свою очередь может привести к ошибке в работе обходчика инструмента CTesK. В результате из-за описанных выше проблем от использования стационарных модельных состояний целевой реализации пришлось отказаться. Основные причины использования стационарного тестирования заключаются в следующем: 1. Использование стационарных состояний позволяет проверить, что тестируемая реализация действительно выполнила все необходимые действия, которые были определены спецификацией. 2. Обходчик может работать лишь со стационарными состояниями. Если же исполнение сценарной функции завершается в нестационарном состоянии, то за время, которое необходимо обходчику для начала выполнения следующей сценарной функции, система может перейти в другое состояние. Это может привести к неполному обходу графа модельных состояний или вынесению вердикта о недетерминированном поведении целевой реализации. Второй подход заключается в отказе от стационарного тестирования. При этом необходимо искусственным образом имитировать работу обходчика для случая стационарного тестирования и следить за выполнением всех необходимых действий, которые определены спецификацией. Чтобы проверить, что тестируемая реализация действительно отправила все необходимые сообщения, используется список действий, которые должна произвести система. Действием является отправление целевой реализацией сообщения определенного типа с определенными значениями некоторых полей; значения других его полей могут быть произвольными. Для каждого действия отводится время, за которое система должна его произвести. Действие может быть отменено при получении какого-либо стимула, если оно еще не было выполнено тестируемой реализацией. 159
В стандарте Mobile IPv6 [1] требования по уровню обязательности выполнения классифицированы в соответствии с RFC 2119 [11]. В предлагаемом подходе для различных групп требований предусмотрены три группы действий: 1. Обязательные действия (obligated) – для требований, обязывающих мобильный узел отправить определенное сообщение (требования, выделенные словами MUST или SHOULD согласно RFC2119). Если тестируемая реализация отправляет такое сообщение в отведенный временной интервал, то сообщается о том, что ее поведение в данном случае удовлетворяет требованию спецификации, которое определяет данное действие. В противном случае выносится вердикт о том, что тестируемая реализация не выполнила данное требование. Для этой группы требований можно задавать альтернативные варианты действий. То есть задаются несколько возможных сообщений для одного требования, и если реализация отправляет одно из таких сообщений, условия требования считаются выполненными. 2. Запрещенные действия (prohibited) – для требований, запрещающих мобильному узлу отправлять определенное сообщение после наступления какого-либо события (требования, выделенные словами MUST NOT или SHOULD NOT). Если данное сообщение было отправлено в выделенный временной интервал, то выносится вердикт о том, что тестируемая реализация нарушила данное требование. В противном случае сообщается о том, что она выполнила требование, определяющее данное действие. 3. Допустимые действия (allowed) – для наблюдения за тем, что мобильный узел реализует некоторую необязательную функциональность протокола (требования, выделенные словом MAY). Если данное сообщение отправлено в отведенный интервал времени, то сообщается о том, что тестируемая реализация поддерживает необязательную функциональность, которая определяется данным требованием. В противном случае ничего не сообщается. Такой подход позволяет явно описывать требования, касающиеся отправления сообщений, и собирать детальную информацию о проверке таких требований, однако предполагает затрату больших усилий при разработке спецификаций, чем в случае со стационарными модельными состояниями. Для того чтобы обеспечить возможность использования стандартного обходчика, в качестве модельных состояний выделяются состояния виртуальной сети в целом, а не группы состояний модели целевой реализации, как это традиционно делается при использовании инструмента CTesK. С учетом того, что в работе имитируются только виртуальные сети со стационарными маршрутизаторами, домашними агентами и узламикорреспондентами, состояние виртуальной сети определяется лишь ее топологией, набором ее узлов и точкой подключения мобильного узла. 160
Топология виртуальной сети и набор узлов задаются перед запуском тестового сценария. Таким образом, состояние виртуальной сети определяется лишь точкой подключения мобильного узла. Такие модельные состояния являются стационарными, так как не зависят от поведения реализации и являются объединениями нескольких приближенно стационарных состояний мобильного узла. Для обеспечения такого соответствия необходимо использовать достаточно большой интервал времени, который отводится на исполнение сценарной функции, чтобы тестируемая реализация успела осуществить переход в приближенно стационарное состояние. Кроме того, необходимо описать все действия, определенные стандартом при данном переходе. Конечное приближенно стационарное состояние мобильного узла после перехода в один и тот же сегмент виртуальной сети зависит от поведения других узлов этой сети. В результате введения описанных выше состояний может существенно ухудшиться качество тестирования, поэтому помимо текущей точки подключения мобильного узла в модельных состояниях должен учитываться также и способ, которым мобильный узел был переведен в данную сеть. Сеть, в которой мобильный узел находился до осуществления смены точки подключения, в модельном состоянии не учитывается, так как для любого способа, которым мобильный узел может быть переведен в данную сеть, при обходе графа модельных состояний автоматически будут произведены переходы из всех сетей, из которых это возможно. В предлагаемом подходе не учитывается также и предыдущая история переходов. В подавляющем большинстве случаев предыдущие переходы не оказывают влияния на поведения мобильного узла, а учет таких переходов экспоненциально увеличивает количество модельных состояний. В качестве сценарных функций в тестовом наборе задаются возможные способы перевода мобильного узла из одной сети в другую. Задача обходчика в таком случае заключается в осуществлении всех возможных переходов для мобильного узла всеми возможными способами. Под различными способами здесь понимается различное поведение узлов виртуальной сети после осуществления смены точки подключения мобильного узла в этой сети, которое определяется отправляемыми ими сообщениями. Для обеспечения полноты тестирования необходимо оказать воздействие всеми возможными стимулами во всех достижимых состояниях, включая начальное состояние. Однако приведение тестируемой реализации мобильного узла в начальное состояние только при помощи обмена сообщениями через сетевой интерфейс не представляется возможным. Это связано с тем, что даже после удаления привязки при возвращении в домашнюю сеть мобильный узел сохраняет некоторую информацию в структурах данных, описывающих его состояние, например, сохраняет адрес домашнего агента. На Рис. 7 изображен пример графа состояний тестового сценария для случая, когда виртуальная сеть состоит только из сегмента 161
домашней сети и одного сегмента внешней сети, и определены только два способа перевода мобильного узла между этими сетями. В состояниях 0, 1 и 2 мобильный узел находится в домашней сети, в состояниях 3 и 4 – во внешней сети. Чтобы отличить способы перевода мобильного узла на рисунке используется разная толщина дуг графа.
Рис. 7. Пример графа состояний тестового сценария Этот пример показывает, что граф состояний тестового сценария не является сильно связным, так как нет дуги, которая возвращала бы тестовый сценарий в начальное состояние. Однако такая дуга необходима для осуществления обходчиком всех возможных переводов мобильного узла из всех достижимых состояний. Это одна из проблем, возникающих при использовании состояний, принятых в нашем подходе. Для гарантированного приведения тестируемой реализации в первоначальное состояние перед выполнением очередного тестового сценария используется перезагрузка целевого узла. Чтобы решить проблему с несвязностью графа состояний, можно было бы ввести стимул, осуществляющий перезагрузку системы, но это приводило бы к большому количеству перезагрузок во время тестирования и заняло бы больше времени на исполнение теста. Кроме того, сложно было бы отследить, чем именно вызвана перезагрузка: нестабильностью тестируемой реализации или воздействием такого стимула. Вместо введения стимула, осуществляющего перезагрузку целевой реализации, в предлагаемом подходе используются два приема: объединение состояний и обеспечение всех возможных воздействий. А именно, начальное состояние мобильного узла объединяется с состоянием «мобильный узел в домашней сети», в которое он попадает после возвращения в домашнюю сеть, 162
при условии, что тестовый сценарий отправит все необходимые сообщения от имени узлов виртуальной сети. Для обеспечения же перебора всех возможных воздействий в начальном состоянии первоначальные тестовые сценарии разделяются на несколько подсценариев. Этот процесс можно описать следующим образом. Сначала, как и обычно, для проверки определенных групп требований стандарта разрабатываются конкретные тестовые сценарии. Затем после определения всех возможных стимулов, необходимых для обеспечения полноты тестирования, для каждого из этих первоначально разработанных тестовых сценариев создается несколько подсценариев, которые отличаются лишь тем, что на целевую реализацию, находящуюся в первоначальном состоянии, осуществляется воздействие одним конкретным стимулом, отличным от первых стимулов в других подсценариях. При этом общее количество создаваемых подсценариев равно количеству возможных стимулов для данного тестового сценария. Таким образом, на реализацию, находящуюся в первоначальном состоянии, осуществляется воздействие всеми возможными стимулами. На Рис. 8 изображены подсценарии, полученные из предыдущего примера при помощи описанных выше действий. Заметим, что у этого графа дуга из состояния 1 в состояние 3, соответствующая первому способу перевода мобильного узла из домашней сети во внешнюю сеть, обозначена пунктиром. Это означает, что обходчик не обязан осуществлять такое воздействие, так как может считать, что оно уже произведено при выполнении перевода мобильного узла из начального состояния первым способом. По этой же причине во втором подсценарии выделена пунктиром дуга из состояния 1 в состояние 4, соответствующая второму способу перевода мобильного узла из домашней сети во внешнюю сеть. Из-за подобных особенностей полученных подсценариев в случае, когда первоначальный тестовый сценарий включает в себя лишь одно воздействие, и, соответственно, имеется только один подсценарий, тестирование может оказаться неполным. Однако, как это видно из Рис. 8, при наличии двух и большего числа подсценариев необходимые воздействия будут гарантированно осуществляться в других подсценариях. Таким образом, для обеспечения полноты тестирования необходимо, чтобы в каждом из начальных сценариев использовалось как минимум два стимула. Следует отметить, что обходчики dfsm [8] и ndfsm [9] инструмента CTesK, благодаря своему внутреннему устройству, всегда осуществляют воздействие, отмеченное на Рис. 8 пунктирной линией, поэтому на практике можно использовать тестовые сценарии, включающие в себя лишь один стимул. В результате в разработанном тестовом наборе используются две группы состояний: состояния виртуальной сети, дополненные указанием способа, которым был произведен последний перевод мобильного узла, и состояния модели тестируемой реализации. Соответственно, имеются две группы стимулов: стимулы, осуществляющие смену точки подключения мобильного 163
узла в виртуальной сети, которые изменяют только состояние виртуальной сети, и сообщения от узлов виртуальной сети, которые изменяют только состояние модели целевой реализации.
Рис. 8. Полученные тестовые подсценарии Реакциями являются сообщения от тестируемой реализации, которые также изменяют только состояние модели целевой реализации. Реакции со стороны виртуальной сети в данном тестовом наборе не предусмотрены. Спецификации разрабатываются только для группы стимулов и реакций, которые работают с состоянием модели целевой реализации. Спецификации состоят из постусловий стимулов и реакций, а также функции, которая следит за истечением временного интервала, отведенного на действия. Последняя функция вызывается в начале работы медиаторов для стимулов и реакций, описывающих сетевые сообщения. В качестве аргумента этой функции передается время, когда пакет был получен на сетевом интерфейсе; 164
на основании этого времени и происходит синхронизация списка действий, то есть удаление действий, время которых истекло.
5. Структура тестового набора и этапы исполнения сценарной функции Как было указано в предыдущем разделе, для реализации предложенной модели тестирования, тестирующий узел должен имитировать функциональность всех узлов, которые принимают участие в обмене сообщениями с мобильным узлом в виртуальной сети. Имитация функциональности узлов виртуальной сети необходима на протяжении всего выполнения тестового сценария, в том числе и во время обработки результатов вызова сценарных функций. Такая необходимость вызвана тем, что при тестировании используются нестационарные модельные состояния, и большинство служебных сообщений, отправляемых мобильным узлом, является частью синхронных процедур обмена сообщениями с одним из узлов сети. Поэтому часть тестового набора, имитирующая данную функциональность, должна быть реализована вне тестового сценария. Схема виртуальной сети и набор ее узлов специфичны для каждого теста. С учетом этого наиболее простое и логичное решение заключается в том, чтобы обособить функциональность, свойственную каждому узлу. Для этого написаны отдельные демоны, каждый из которых полностью имитирует функциональность узла определенного типа: демон маршрутизатора, демон домашнего агента, демон узла-корреспондента. Демоны запускаются в отдельных процессах в начале тестового сценария и функционируют на протяжении всего его выполнения. Это позволяет мобильному узлу отправлять необходимые вспомогательные сообщения во время обработки стимулов и реакций, собранных тестовым сценарием между вызовами сценарных функций. При переходе исполнения от одной сценарной функции к другой изменяется лишь поведение демонов, которое заключается в отправлении или не отправлении синхронных и асинхронных сообщений мобильному узлу, меняются также некоторые поля в этих сообщениях. В процессе исполнения тестового сценария топология виртуальной сети и положение в ней демонов остаются неизменными. Все демоны подключаются к одному и тому же сетевому интерфейсу; это упрощает организацию маршрутизации и внештатного функционирования демонов, которое необходимо для проверки некоторых требований спецификации. Взаимодействие между демонами, там, где это необходимо, должно производится не через сетевой интерфейс, а с использованием других средств, так как все такие сообщения будет получать мобильный узел, если его сетевой интерфейс находится в режиме promiscuous. В предлагаемом тестовом наборе такое взаимодействие необходимо только между демонами узлов-корреспондентов и демонами домашних агентов. Для этого 165
используется общий Binding Update List, в который домашние агенты записывают информацию об установленных привязках. Узлы-корреспонденты используют эту информацию для извлечения из туннеля входящих пакетов и туннелирования исходящих пакетов при обмене сообщениями с мобильным узлом в режиме двустороннего туннелирования. Общая картина выполнения тестового сценария выглядит следующим образом (см. Рис. 9). Перед инициализацией тестового сценария на тестируемом узле должна быть настроена и запущена целевая реализация. Перед началом тестирования запускается процесс под названием кэтчер, который получает и запоминает все сообщения, поступающие на сетевой интерфейс. Затем запускаются необходимые демоны с первоначальными значениями структур данных, которые определяют их поведение. После этого тестовый сценарий выжидает некоторый интервал времени, в течение которого при помощи обмена сообщениями с демонами тестируемая реализация выполняет автоматическую конфигурацию.
Рис. 9. Схема исполнения тестового сценария Выполнение тестового сценария заключается в последовательном вызове сценарных функций. При вызове каждой сценарной функции соответствующим образом изменяются внутренние структуры данных, определяющие поведение демонов. Такое изменение состояния и поведения демонов оказывает воздействие на целевую реализацию мобильного узла и заставляет ее выйти из стационарного состояния. Затем тестовый сценарий некоторое время находится в состоянии ожидания, по истечении которого работа сценарной функции завершается, и тестовый сценарий получает от кэтчера пакеты, собранные за время ожидания и обработки результатов вызова предыдущей сценарной функции. Далее производится анализ полученных пакетов и регистрация соответствующих стимулов и реакций. 166
Таким образом, стимулы, которые должны применяться в данной сценарной функции, задаются неявным образом через поведение демонов. После завершения сценария прекращается работа демонов и кэтчера.
6. Адаптация средств инструмента CTesK предложенной схеме тестового набора
к
В используемом тестовом стенде у инструментального и целевого узла имеется по одному сетевому интерфейсу, которые соединены кросс-кабелем, поэтому пакеты в процессе доставки не перемешиваются. Поскольку в большинстве случаев обмены сообщениями между мобильным узлом и демонами являются синхронными, порядок поступления сообщений может изменяться лишь в том случае, если два сообщения отправляются одним и тем же узлом и являются частью разных параллельно выполняющихся процедур обмена сообщениями. Стандарт RFC 3775 не накладывает никаких ограничений на связь между параллельно выполняющимися процедурами, поэтому такое нарушение порядка не может повлиять на выносимый тестовым сценарием вердикт. Это позволяет в разрабатываемом тестовом наборе не использовать сериализацию. Точнее, при сериализации рассматривается лишь один порядок поступления стимулов и реакций. В выбранной модели тестирования используются две группы стимулов: стимулы, действующие на виртуальную сеть, и стимулы, действующие на тестируемую реализацию. Сбор пакетов не приостанавливается между вызовами сценарных функций, поэтому пакеты, собранные за время обработки результатов вызова сценарной функции, будут рассматриваться после вызова следующей сценарной функции. Это означает, что отключение сериализации приводит к нарушению порядка поступления стимулов. Однако порядок вызова стимулов, воздействующих на тестируемую реализацию, будет сохраняться, а стимулы, воздействующие на сеть, не изменяют структур данных, имитирующих состояние тестируемой реализации. Поэтому такое нарушение порядка поступления стимулов не повлияет на вердикт, выносимый спецификациями. Стандарт RFC 3775 представляет собой обычный текст, из которого можно выделить несколько сотен четких требований. При построении формальных спецификаций в виде пред- и постусловий для сообщений от тестируемой реализации может оказаться, что некоторые постусловия содержат проверку для десятков требований стандарта. Инструмент CTesK в случае нарушения одного из таких требований выдаст немногословный вердикт false. Однако по сообщению, на котором был выдан такой вердикт, не всегда просто понять, какое же именно требование стандарта было нарушено. Чтобы решить эту проблему, введена вспомогательная функция conformance_check, осуществляющая проверку требования стандарта и сбрасывающая в тестовую трассу информацию о результате его проверки, а также о ситуации, в которой 167
эта проверка производилась. По такой информации впоследствии генерируется детальный отчет о проверенных требованиях стандарта. Элементарной реакцией от целевой системы в рассматриваемой модели тестирования является пакет, но многие требования стандарта применяются не ко всему пакету, а к отдельным его заголовкам. Проверку таких требований в инструменте CTesK принято осуществлять в инвариантах типов данных, описывающих заголовки пакета. Однако в тестовом наборе используются одни и те же типы данных для заголовков, содержащихся в пакетах от мобильного узла и в пакетах от демонов. Проверка же должна производится только для пакетов от мобильного узла, так как некорректно построенные заголовки в пакетах от демонов допускаются и используются при тестировании внештатных ситуаций. Поэтому проверка таких требований вынесена за пределы спецификационной функции и происходит еще до регистрации реакций на этапе разбора пакета по заголовкам. Для идентификации пакета в отчете удобно использовать время, когда был получен пакет. Время получения передается стимулам и реакциям вместе с другой информацией о пакете через аргумент спецификационной функции или реакции соответственно. Однако инструмент CTesK не позволяет использовать в предусловии значение аргумента передаваемого реакции, поэтому проверку всех требований пришлось вынести в постусловие реакций. Следует также отметить, что отчеты, которые может генерировать инструмент CTesК, в данной работе не используются. Вместо этого, отчет о проведенном тестировании генерируется отдельной частью тестового набора по тестовой трассе и нескольким вспомогательным файлам. Такие отчеты ориентированы на конкретную задачу, более наглядны и понятны пользователю, не знакомому с технологией UniTESK.
7. Апробация разработанного тестового набора При помощи разработанного тестового набора было проведено тестирование открытой реализации Mobile IPv6 KAME для операционной системы FreeBSD 5.4 (CVS Snapshot 2006.11.13). Во время проведения тестирования эта реализация была одной из наиболее распространенных открытых реализаций Mobile IPv6. При прогоне тестовых сценариев был выявлен ряд нарушений требований стандарта RFC 3775 [1], например: мобильный узел выполняет процедуру Duplicate Address Detection не во всех случаях, когда это необходимо; мобильный узел при смене точки подключения не всегда производит регистрацию основного временного адреса на домашнем агенте; мобильный узел нарушает ограничения темпа отправления сообщений Binding Update; мобильный узел не соблюдает некоторые рекомендации процедуры 168
Generic Movement Detection; мобильный узел не всегда использует корректные значения для времени ожидания ответа на такие сообщения, как Binding Update, Home Test Init, Care-of Test Init, Mobile Prefix Solicitation.
Помимо этого, в некоторых случаях поведение тестируемого мобильного узла было заведомо некорректным, например, некоторые сообщения отправлялись на неправильный адрес канального уровня. Кроме того, исполнение некоторых тестовых сценариев приводило к зависанию или перезагрузке мобильного узла, что свидетельствует о нестабильности целевой реализации. Результаты проведенного тестирования с указанием нарушенных требований стандарта доступны по адресу http://ipv6.ispras.ru/mobility/mn/kame.html.
8. Заключение В статье описаны особенности тестового набора, разработанного для проверки соответствия реализаций мобильного узла спецификации протокола Mobile IPv6 [1]. Тестовый набор разрабатывался при помощи инструмента CTesK, который реализует технологию автоматического тестирования UniTESK. В ходе выполнения работы были выявлены некоторые особенности мобильного узла, которые затрудняют его тестирование с помощью технологии UniTESK, а именно: Мобильный узел является инициатором большинства служебных обменов сообщениями. Некоторые из таких обменов сообщениями можно вызвать неявным образом, создав определенные условия при помощи вспомогательных сообщений. Другие обмены сообщениями мобильный узел может начинать в произвольные моменты времени. В обоих случаях на сообщения со стороны мобильного узла может потребоваться быстрый ответ со стороны тестирующего узла. Спецификация протокола Mobile IPv6 не содержит исчерпывающего описания поведения мобильного узла, поэтому нельзя осуществить перебор всех возможных воздействий во всех состояниях. Спецификация протокола Mobile IPv6 допускает недетерминированное поведение мобильного узла. Однако все эти особенности удалось успешно преодолеть, не выходя за рамки ограничений технологии UniTESK. При разработке тестового набора было обнаружено несколько сложных моментов, связанных с применением инструмента CTesK, а именно: отсутствие у мобильного узла стационарных состояний; сложность использования инвариантов типов данных из-за того, что их проверка производится только автоматически и не может контролироваться; недостаточная информативность при задании формальных спецификаций в виде пред- и постусловий. 169
Для преодоления этих трудностей потребовалось несколько отступить от прямого использования традиционного подхода к разработке тестового набора, задаваемого инструментом CTesK. Тем не менее, благодаря гибкости инструмента, удалось решить поставленную задачу его средствами. Единственное, от чего действительно пришлось отказаться, – это использование стандартных для CTesK средств генерации отчетов о тестировании. Широта и сложность предметной области требует для анализа результатов тестирования существенно большего объема информации, чем предоставляет инструмент. Поэтому был реализован сбор дополнительной информации о ходе тестирования средствами самого тестового набора и разработана система генерации отчета, предназначенная для конкретной задачи. В остальном, несмотря на все выявленные особенности, удалось без существенных трудностей использовать инструмент CTesK для тестирования реализаций мобильного узла. Литература [1] D. Johnson, C. Perkins, J. Arkko "Mobility Support in IPv6", RFC 3775, June 2004. [2] http://www.unitesk.com - сайт, посвященный технологии тестирования UniTESK и реализующим ее инструментам. [3] http://www.unitesk.com/products/ctesk/ – страница инструмента CTesK. [4] S. Deering, R. Hinden, "Internet Protocol, Version 6 (IPv6) Specification", RFC 2460, December 1998. [5] S. Thomson, T. Narten, "IPv6 Stateless Address Autoconfiguration", RFC 2462, December 1998. [6] T. Narten, T. Nordmark, E. and W. Simpson, "Neighbor Discovery for IP Version 6 (IPv6)", RFC 2461, December 1998. [7] A. Conta, S. Deering "Internet Control Message Protocol (ICMPv6) for the Internet Protocol Version 6 (IPv6) Specification", RFC 2463, December 1998. [8] И. Б. Бурдонов, А. С. Косачев, В. В. Кулямин. Неизбыточные алгоритмы обхода ориентированных графов: детерминированный случай. Программирование, т. 29, № 5, стр. 59-69, 2003. [9] А. В. Хорошилов. Отчет о научно-исследовательской работе. Алгоритмы обхода недетерминированных графов, обладающих детерминированным полным остовным подграфом. [10] Bertrand Meyer. Object-Oriented Software Construction, Second Edition. Prentice Hall, 1997. [11] S. Bradner, "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997.
170