Показаны сообщения с ярлыком boost. Показать все сообщения
Показаны сообщения с ярлыком boost. Показать все сообщения

воскресенье, ноября 09, 2008

Русский перевод Boost::test документации


Я посмотрел на статистику Google Analytics моего блога и обнаружил, что одними из самых популярных страниц в нём являются статьи про библиотеку Boost (про unit testing и сборник ссылок на русские переводы документации по Boost). Так что раз люди это ищут, они это получат :) Ниже мой перевод куска документации с введением в юнит тестирование при помощи Boost::test.

Hello the testing world. Введение в тестирование с использованием Unit Test Framework.

Как программа должна сообщать об ошибках? Обычный способ - это показ сообщения об ошибке:

if( something_bad_detected )
std::cout << "something bad has been detected"
<< std::endl;
Однако, чтобы узнать, случилась ли ошибка, такой способ требует проверки вывода программы после каждого запуска. Поскольку юнит-тестовые программы запускаются обычно часто (как часть процесса регрессионного тестирования), то ручная проверка на наличие сообщений об ошибках - это слишком неэффективное решение. Фреймворки для тестирования, такие как GNU/expect, могут делать эти проверки автоматически, но они слишком сложны для простых тестов.


Лучше и проще для тестирующей программы использовать следующий метод сообщения об ошибках - возвращать EXIT_SUCCESS (как правило 0) в случае отсутствия ошибки и EXIT_FAILURE в случае её обнаружения. Это позволит скрипту для регрессионных тестов автоматически определять успех/провал теста. В дальнейшем скрипт может создать HTML таблицу с результатами тестов, послать email или сделать что-то ещё без необходимости изменять сам C++ код юнит тестов.


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


Unit Test Framework из библиотеки Boost создан для автоматизации таких задач. Функция main() из библиотеки сделана для того, чтобы освободить пользователя от заботы об обнаружении ошибок и генерации отчётов. У пользователей есть возможность использовать средства библиотеки для выполнения сложных проверок. Давайте взглянем на следующую простую программу юнит тестирования:


#include <my_class.hpp>
int main( int, char* [] )
{
  my_class test_object( "qwerty" );
  return test_object.is_valid() ?
    EXIT_SUCCESS : EXIT_FAILURE;
}


С этим юнит тестом есть несколько проблем.


Вам нужно конвертировать результат вызова is_valid() в нужный код возврата. Если при конструировании test_object или при вызове is_valid() возникнет исключение, программа завершится аварийно. Вы не увидите никаких сообщений, если запустите тест вручную. Unit Test Framework решает все эти проблемы. Для его использования эта программа должна быть изменена следующим образом:


#include <my_class.hpp>
#define BOOST_TEST_MODULE MyTest
#include <boost/test/unit_test.hpp>

BOOST_AUTO_TEST_CASE( my_test )
{
  my_class test_object( "qwerty" );
  BOOST_CHECK( test_object.is_valid() );
}


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


#define BOOST_TEST_MODULE MyTest
#include <boost/test/unit_test.hpp>

int add( int i, int j ) { return i+j; }

BOOST_AUTO_TEST_CASE( my_test )
{
  // seven ways to detect and report the same error:
  BOOST_CHECK( add( 2,2 ) == 4 ); // #1 continues on error
  BOOST_REQUIRE( add( 2,2 ) == 4 );// #2 throws on error
  if( add( 2,2 ) != 4 )
  BOOST_ERROR( "Ouch..." ); // #3 continues on error
  if( add( 2,2 ) != 4 )
  BOOST_FAIL( "Ouch..." );  // #4 throws on error
  if( add( 2,2 ) != 4 ) throw "Ouch...";// #5 throws 
  // on error 
  BOOST_CHECK_MESSAGE( add( 2,2 ) == 4, // #6 continues
  // on error
  "add(..) result: " << add( 2,2 ) );
  BOOST_CHECK_EQUAL( add( 2,2 ), 4 ); // #7 continues
  // on error
}


(1)

В этом варианте используется BOOST_CHECK, который показывает сообщение об ошибке (по умолчанию он его выводит в std::cout), которое включает в себя выражение, не прошедшее проверку, имя файла с исходниками, и строку в нём. А также он увеличивает счётчик ошибок. При завершении программы счётчик ошибок будет автоматически показан средствами Unit Test Framework.

(2)

Этот вариант использует BOOST_REQUIRE, средство, во всём подобное #1, за исключением того, что после вывода сообщения об ошибке генерируется исключение, отлавливаемое затем Unit Test Framework. Это используется тогда, когда возникшая ошибка настолько серьёзна, что делает дальнейшее выполнение программы бессмысленным. BOOST_REQUIRE отличается от макроса assert() стандартной библиотеки тем, что перенаправляет обнаруженную ошибку в унифицированную процедуру генерации отчётов из Unit Test Framework.

(3)

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

(4)

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

(5)

Этот вариант бросает исключение, отлавливаемое затем Unit Test Framework. Показываемое сообщение об ошибке будет более информативным, если исключение было унаследовано от std::exception, или является char* или std::string.

(6)

Этот вариант использует средство BOOST_CHECK_MESSAGE, похожее на #1, за исключением того, что подобно варианту #3 показывает альтернативное сообщение об ошибке, описанное во втором аргументе.

(7)

Этот вариант использует средство BOOST_CHECK_EQUAL, похожее на #1. Лучше всего это подходит для проверки на равенство двух переменных, так как в случае несовпадения показывает оба значения.


P.S. Для того, чтобы увидеть, что этот Unit Test Framework выводит в случае обнаружения ошибки я исправил первую проверку на заведомо неправильную:
BOOST_CHECK( add( 2,2 ) == 5 );

В этом случае программа выдала следующее:
Running 1 test case… 

c:/boosttest/unittests.cpp(12): error in “my_test”: check add( 2,2 ) == 5 failed

понедельник, сентября 29, 2008

Boost на русском


Я уже писал как сложно найти русскую документацию по библиотекам boost для c++. Для удобства я решил собрать всё краткие введения в различные библиотеки этого пакета на русском, найденные в рунете, которые показались мне более-менее полезными, чтобы получить общее представление о конкретной библиотеке. Дальнейшее изучение возможностей этих библиотек всё равно требует чтения документации на английском.
Итак:


  • Boost Smart pointers. Умные указатели, Boost::Shared_ptr, Boost::Weak_ptr, Boost::Intrusive_ptr.
  • Boost::Multi_index - улучшенный вариант контейнеров STL.
  • Boost::Spirit - библиотека для создания парсеров. Пример использования Spirit 2.
  • Boost::Regex - использование регулярных выражений в С++. Ещё одно введение в библиотеку - Boost это просто. Часть 1. Boost.Regex на хабре.
  • Boost::Xpressive - альтернатива Boost::Regex. Также работа с регулярными выражениями. Это описание и 4 предыдущих найдены на блоге Alno’s Blog: C++, Java и Rails
  • Boost::any - обобщенный контейнер, позволяющий хранить разнородные данные в одном и том же объекте.
  • Boost::Asio - для создания кроссплатформенного кода для работы с сетью. Это из блога Alex Ott’а. Еще нашёл перевод туториала по boost::asio
  • Boost::threads - библиотека для работы с потоками. Это перевод статьи первого автора этой библиотеки Уильяма Кемпфа в виде PDF. Статья старая, 2002 г, с тех пор библиотека была почти полностью переписана, хотя внешние интерфейсы изменились не сильно, так что общее представление из статьи получить всё ещё можно. 
  • Boost.DateTime - Библиотека по работе со временем.
  • На сайте sources.ru выложена кое-какая документация по библиотекам: smart_ptr, bind (работа с функторами и предикатами в алгоритмах), function (работа с указателями на функции), signal (для реализации паттернов «Команда» или «Действие» из книги банды четырёх), lexical_cast(преобразование любых типов в строки и из строк в любой тип).
  • Ещё несколько достаточно кривых введений в библиотеки boost есть на сайте solarix.ru. В частности кое-что про lexical_cast, format (форматирование строк, замена printf), regex(регулярные выражения), tokenizer (разбор строк на лексемы), и еще несколько. Но перевод ужасный. Правда есть примеры, из которых кое-что можно понять.
  • В википедии есть примеры по библиотекам uBLAS (линейная алгебра для матриц и векоров),random (генерация псевдослучайных чисел), Spirit (про него выше уже есть ссылка, в которой всё расписано более подробно), Regex, Graph (алгоритмы на графах).
  • И наконец мой собственный перевод документации Boost::test для юнит тестирования .
Вот пока всё, что я нашёл. Найду ещё что-то, добавлю.

пятница, сентября 05, 2008

Использование библиотек из Boost Libraries в своих проектах


В прошлом посте про юнит тестирование на С++ я упомянул про фреймворк boost::test. Сейчас активно с ним играюсь, пытаюсь найти всю возможную информацию про него и вообще про boost в целом. Как оказалось, информации на русском в сети очень мало - какие-то обрывки переводов документации здесь и это почти всё. Хуже того, и в офлайне с книгами тоже всё плохо. Есть книжка про Boost Graph Library из серии “Библиотека программиста”. Но это всего лишь об одной библиотеке из почти сотни. Наверное, есть главы с кратким описанием библиотеки в толстых книгах про шаблоны С++, хотя не уверен. Так что без чтения документации на английском видимо никак не обойтись.

На английском есть книга Beyond the C++ Standard Library: An Introduction to Boost. Я точно знаю, что где-то есть её электронные копии, но пока их не нашёл. Так что пока основной источник знаний для меня - документация с сайта www.boost.org, благо она достаточно неплохая, её по крайней мере в отличие от MSDN читать можно. Я скачал последнюю версию boost’а - 1.36.0, весит правда немало - zip файл порядка 55 Мб.

Чтобы построить бинарные файлы из исходников нужно ещё отдельно скачать утилиту Boost Jam оттуда же с сайта. В принципе для очень многих библиотек никаких бинарных файлов и вовсе не нужно - чтобы их использовать достаточно вставить  в свой проект строчку типа

#include <boost/SOME_LIB.hpp>


и всё. Но некоторые библиотеки, например boost::regex для встраивания перлообразных регулярных выражений в C++ код всё же требуют бинарных .lib файлов. Кроме того, в поставку boost’а входят исходники несколько утилит, которые компилируются также при помощи Boost Jam (bjam.exe).


В частности есть весьма полезная утилитка bcp. Если не хочется включать в проект все исходники Boost’а для всех возможных библиотек (а их ОЧЕНЬ много), с помощью этой утилиты можно выдрать только необходимые. Я наткнулся на неё благодаря этой статье в блоге Alex Ott’а.


То есть если нужны, к примеру, только unit test’ы, то при помощи команды:


> bcp boost/test/unit_test.hpp c:\tmp\boost_test


мы получим в каталоге c:\tmp\boost_test всё, что нужно для фреймворка для юнит тестирования. Остаётся включить полученное в свой проект и можно вовсю “экстремально программировать” :)


Что почитать ещё:
P.S. Подсказали, где в сети лежит Beyond the C++ Standard Library: An Introduction to Boost.