понедельник, 25 июля 2011 г.

meter-webapp --- система учёта показаний водных счётчиков

Общие сведения

Каждый месяц мне, как и другим жителям моего многоквартирного дома, приходится проделывать процедуру под названием "заполнение справки о расходах холодной и горячей воды". Данная процедура включает в себя заполнение бланка, в котором указываются: ФИО, номер квартиры, номера счётчиков холодной и горячей воды, предыдущие показания счётчиков холодной и горячей воды, дата подачи предыдущих показаний и, разумеется, текущие показания счётчиков холодной и горячей воды. Вдобавок, бланк заполняется в двух экземплярах. Ясно, что вся информация кроме текущих показаний либо не меняется вообще, либо может быть вычислена на основе уже имеющихся данных, причём эту задачу совсем несложно автоматизировать. Именно этой цели и служит web-приложение meter-webapp. Ещё одна приятная мелочь - email-напоминалка, которая отслеживает подал человек показания счётчиков в текущем месяце или нет.

Краткая техническая информация
Тестирование
  • Модульные тесты для всех пользовательских ORM-классов, порождённых от Class::DBI::*
  • Тест, проверяющий функционирование ORM-классов в комплексе
  • Тест, проверяющий работу web-приложения на самом вернем уровне
Все тесты самостоятельно создают для себя всё необходимое окружение и убирают за собой после завершения. Тесты первых двух типов создают для себя временную БД со всеми необходимыми таблицами. Результат манипуляции с данными проверяется при помощи модуля Test::DatabaseRow.

Функциональный тест также создаёт временную БД, инициализирует её, генерирует конфигурационный файл Apache-а, запускает instance на выделенном порту и полностью имитирует работу нескольких пользователей с сайтом: регистрацию, логин, настройку информации по квартире, подачу показаний и т.д. Все вышеперечисленное осуществляется при помощи модуля Test::WWW::Mechanize. После завершения теста, все временные файлы удаляются.

Если выставить переменную окружения - DONT_CLEANUP, то все временные конфиги, базы данных будут оставлены для ручного изучения и анализа.

На данный момент набор тестов покрывает не все случаи и будет расширяться.

Что дальше
  • Если получится, то "распространить среди жильцов нашего ЖЭКа" (C).
  • Do TODO

четверг, 28 апреля 2011 г.

Параллельный вейвлетный компрессор изображений EPSILON

EPSILON - это параллельный вейвлетный компрессор изображений. Программа поддерживает более 30 различных вейвлетных фильтров и имеет механизм для добавления новых. Кроме того, EPSILON поддерживает очень большие изображения (больше 4Gb) и позволяет точно задавать желаемую степень сжатия заранее.

К настоящему моменту имеются три механизма для распараллеливания:
  • POSIX threads
  • MPI (Message Passing Interface)
  • Собственное кластерное решение на базе TCP/IP
Метод распараллеливания задаётся опцией во время компиляции (имеются HOW-TO по запуску MPI и кластерной версии). Можно также собрать и простую, последовательную версию программы.

Архитектурно EPSILON состоит из двух частей: библиотеки libepsilon и программы epsilon, которая использует данную библиотеку. Таким образом, libepsilon может использоваться (и фактически используется) независимо.

Программа epsilon поддерживает два графических формата: PGM и PPM. Сжатое изображение сохраняется в собственном формате PSI. PSI-файл состоит из последовательности блоков, соответствующих блокам исходного изображения. Каждый блок абсолютно независим от остальных и имеет свой заголовок и CRC. Таким образом, даже если все блоки в файле кроме одного будут полностью испорчены или утрачены, то этот единственный блок распакуется без проблем и встанет на своё место в изображении.

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

Уже сжатое изображение можно дополнительно усечь (дожать). Благодаря технологии известной как embedded wavelet coding, алгоритм усечения выглядит следующим образом: пробежаться по всем сжатым блокам и усечь (буквально) каждый до необходимого размера. Единственное дополнительное действие - пересчёт CRC усечённого блока. Интересно отметить, что сжатое, а затем усечённое изображение ничем не будет отличаться от изначально сжатого до размера усечённого.

Начиная с версии 0.9.1 в EPSILON появился свой тестовый фреймворк, написанный на Perl. К настоящему моменту имются два теста: быстрый - quick.t и полный - verification.t

Полный тест для каждого из тестовых изображений перебирает все 4 версии EPSILON (generic, pthreads, cluster, mpi), все вейвлетные фильтры, все поддерживаемые размеры блоков и все допустимые режимы работы. Для каждой полученной комбинации (на данный момент их 7680) производится сжатие, распаковка и расчёт PSNR между исходным и восстановленным изображениями. Если полученный PSNR меньше ожидаемого, то тест сохраняет для дальнейшего анализа полученные файлы и список использованных опций.

EPSILON используется в составе OpenSource GIS-библиотеки GDAL: http://www.gdal.org/frmt_epsilon.html

Пример использования EPSILON на реальной спутниковой фотографии: http://www.gaia-gis.it/raster_benchmark/color-ortho-epsilon.html

EPSILON распространяется по лицензии GPL3 или LGPL3 (по выбору).
Страница проекта: http://epsilon-project.sourceforge.net/

пятница, 22 апреля 2011 г.

Встраиваемый лабиринт на Lua + C

Введение

Lua - интересный скриптовый язык специально разработанный для встраивания в приложения, написанные на других языках программирования, в основном C и C++. История Lua начинает свой отсчёт в 1993 году. Язык разрабатывается в стенах Католического университета Рио-де-Жанейро (Pontifical Catholic University of Rio de Janeiro in Brazil) силами Роберто Иерусалимского (Roberto Ierusalimschy), Вальдемара Селеса (Waldemar Celes), Луис Энрике ди Фигейреду (Luiz Henrique de Figueiredo), а также страниями сообщества Lua-программистов.

Первое, что бросается в глаза при знакомстве с Lua - это крайний минимализм синтаксиса и встроенных библиотек. Позиция авторов в этом вопросе вполне понятна - распухни Lua до размеров Perl-а, Python-а или Ruby - не занять ему той ниши, которую он так прочно удерживает в мире языков программирования. Однако чрезмерный аскетизм приводит к интересным эффектам: вот, к примеру, wiki-страница с одного из центральных сайтов Lua-сообщества, на которой приводится 10 (!) различных реализаций функции split() на Lua. Все реализации немного отличаются друг от друга: есть ли поддержка регулярных выражений или нет, допускается ли пустой разделитель или нет, возвращается ли результат или сохраняется по ссылке, что идёт первым аргументом - сама строка или разделитель. Хочется надеяться, что http://luaforge.net - своего рода аналог CPAN-а для Perl, решит эту проблему и сообщество наконец-таки определится с функцией split().

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

Пример

Лёгкость встраивания Lua в C и наоборот - просто поражает! Пример, который приводится ниже, состоит из двух частей: Lua и C. Lua-часть - maze_dfs.lua генерирует лабиринт (алгоритм Depth-First-Search) заданной ширины и высоты, а C-часть - maze_generator.c выводит его на экран. Для полноты картины, maze_generator.c предоставляет функцию external_rand_function(), которую Lua-часть программы вызывает для получения случайных чисел. Таким образом, С и Lua-код вызывают друг друга в рамках одной и той же программы.

Всё взаимодействие с Lua происходит через стек. C-код вначале помещает в него имя вызываемой функции, а затем и её аргументы. При вызове функции указывается сколько аргументов было передано и сколько ожидается результатов. Наверняка Форт-программисты найдут этот механизм очень знакомым!

Отмечу, что хоть лабиринт и возвращается в виде ASCII-картинки (строки), Lua позволяет передавать в обе стороны структуры произвольной сложности.
Если в вашей системе ещё не установлен Lua (в том числе development-часть) самое время сделать это прямо сейчас. Вот пример для Debian Lenny (см. примечание):
sudo apt-get install lua5.1 liblua5.1-0-dev
Примечание: для того чтобы установить Lua 5.1 в Debian Lenny потребуется добавить в /etc/apt/sources.list репозиторий с backport-ами:
deb http://backports.debian.org/debian-backports lenny-backports main
Компиляция программы:
gcc -o maze_generator -Wall `pkg-config lua5.1 --libs --cflags` maze_generator.c
Запуск:
./maze_generator ./maze_dfs.lua 15 10
Результат:
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| | | | |
+ + + + + + +---+---+---+ + +---+---+---+ +
| | | | | | | | | |
+---+---+---+---+ + +---+ +---+ + + + + + +
| | | | | | | | |
+ +---+---+ +---+---+ +---+ +---+ +---+---+ +---+
| | | | | | | |
+---+---+ + +---+ + + +---+ +---+ +---+---+ +
| | | | | | | | |
+ +---+---+---+ +---+---+---+ + +---+---+ +---+ +
| | | | | | | |
+ +---+---+ +---+ + + + + + + +---+ + +
| | | | | | | | |
+ + +---+---+---+---+ +---+---+---+---+---+ +---+---+
| | | | | | | | |
+ +---+ + +---+ + + + + +---+ + + + +
| | | | | | | | | | |
+---+ + +---+ +---+---+ + + + +---+---+---+ +
| | | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Понятно, при условии неизменности интерфейса, Lua-часть можно редактировать не меняя C-программы. К примеру, можно реализовать другой алгоритм генерации лабиринта и передать его в качестве аргумента вместо maze_dfs.lua

Заключение

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

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

Ссылки

понедельник, 14 марта 2011 г.

Perl::Critic + Subversion = внедрение единых практик кодирования в команде

Язык Perl хорошо известен той степенью свободы (a.k.a. TIMTOWTDI), которую он даёт программисту в выборе способа решения той или иной задачи. У этой медали, к сожалению, есть и оборотная сторона, которая может проявиться при командной разработке крупных проектов. Если в команде нет единых практик кодирования и каждый из разработчиков придерживается принципа TIMTOWTDI, то новичку в таком коллективе не позавидуешь.

В 2005 году активный участник Perl-сообщества Дамиан Конвей (Damian Conway) опубликовал книгу Perl Best Practices, в которой собрал и структурировал 256 рекомендаций по написанию понятного, надёжного и поддерживаемого Perl-кода. Краткую шпаргалку с выжимкой из книги можно скачать отсюда.

Годом позже, Jeffrey Thalhammer и группа товарищей выпустила Perl::Critic — гибкий и расширяемый фреймворк, позволяющий автоматизировать проверку Perl-кода на предмет его соответствия большей части рекомендаций из книги Конвея, а также многих других полезных практик.

Perl::Critic подаётся под разными соусами: во-первых, в комплекте с модулем поставляется одноимённая утилита — perlcritic, во-вторых, проверку кода можно оформить в виде тестов с помощью Test::Perl::Critic либоTest::Perl::Critic::Progressive, в-третьих, критик легко интегрируется в VIM и Emacs.

В этом рецепте я расскажу о том как проверять Perl-код на лету при коммите в Subversion-репозиторий. Bon Appétit!

Читать дальше на habrahabr.ru →

вторник, 1 марта 2011 г.

Pulling Strings with Puppet: Configuration Management Made Easy

Pulling Strings with Puppet: Configuration Management Made Easy // 2008
James Turnbull

Puppet - набирающая популярность система централизованного конфигурирования серверов или иначе говоря, continious datacenter automation tool. Конфигурация описывается на специализированном декларативном языке (который в свою очередь базируется на Ruby) и размещается на Puppet-сервере. Клиенты (Puppet-агенты) периодически опрашивают сервер и загружают настройки, если они обновились. Скачав свою конфигурацию, клиентская машина пытается её применить. Важно отметить, что в случае если фактическая конфигурация клиентской машины по каким-то причинам разойдётся с эталонной, то клиентская машина сама попытается её откорректировать без обращения к серверу.

Puppet (марионетка англ.) умеет устанавливать пакеты, запускать/останавливать/мониторить сервисы, загружать конфигурационные файлы (в т.ч. с использованием шаблонов ERB), создавать пользователей и группы и т.д.

Нельзя не отметить, что Puppet ещё достаточно молодой продукт. В связи с этим, книги по данной тематике очень быстро устаревают. Так версия Puppet, описываемая в данной книге 0.22.4 (2007 год) довольно сильно отличается от текущего состояния 2.6.5 (2011 год). Тем не менее, книга будет полезна тем, кто ищет решение для задачи централизованного конфигурирования серверов.

Ещё один отзыв

Оценка по шкале ИМХО: 4

вторник, 18 января 2011 г.

VIM + Devel::Cover - оценка степени покрытия Perl-кода тестами в одно касание

Введение

Значение инструментов для оценки степени покрытия кода тестами зачастую недооценивают: есть мнение, что если код и так хорошо покрыт, то отчёт всего лишь подтвердит и без того известный факт, ну а если кодовая база практически не протестирована, то скудные 5% покрытия наврядли добавят оптимизма разработчикам.

На практике же, даже при нулевом изначальном покрытии, подобный инструмент способен стать серьёзным подспорьем в ежедневной работе: повысить эффективность труда и уменьшить количество дефектов.

В этой статье рассказывается об интеграции VIM-а и модуля Devel::Cover. Первоначальная настройка потребует некоторых усилий, однако этот труд многократно окупится в дальнейшей работе: после того как создание отчёта станет вопросом нажатия пары кнопок, тестирование "невслепую" войдёт в привычку.

Devel::Cover на примере

Для начала, построим отчёт вручную. Итак, на входе имеем модуль Quux.pm и тест для него quux.t. Для запуска тестов с измерением степени покрытия, достаточно задать переменную окружения HARNESS_PERL_SWITCHES следующим образом (есть и другие способы, см. perldoc Devel::Cover):
export HARNESS_PERL_SWITCHES="-MDevel::Cover"
Затем запускаем тест (в этом примере предполагается, что и тест и модуль находятся в одной папке, откуда и запускается команда prove):
prove quux.t
В результате, в текущей директории появится папка cover_db/ - база данных с информацией о покрытии кода. Для создания HTML-отчёта на основе этих данных, необходимо запустить следующую команду (из той-же директории):
cover
Вот как выглядит результат cover_db/coverage.html:


Видно, что в отчёт попал и сам тест quux.t. Для того чтобы этого не происходило достаточно передать команде cover опцию -ignore_re "[.]t$". В отчёте также фигурирует показатель степени покрытия кода документацией (pod-coverage). Если эта информация не нужна, то её также можно отключить (см. perldoc Devel::Cover, параметр -coverage).

В отчёте представлены 5 метрик для каждого файла:
  • stmt --- % выполненных строк кода
  • bran --- % выполненных ветвей условных операторов
  • cond --- % сработавших комбинаций в составных логических условиях
  • sub --- % выполненных подпрограмм
  • pod --- % подпрограмм, имеющих POD-документацию
Столбец time показывает сколько времени прошло в каждом из файлов, а total - агрегирует перечисленные выше показатели.

Если навести мышью на ячейку, то появится всплывающая подсказка вида "N/M", где M - это общее количество тестируемых объектов (к примеру, для столбца stmt - это общее количество строк кода в файле), а N - количество протестированных объектов (для stmt - количество выполненных строк кода).

Если перейти по ссылке в ячейке, то будет показан подробный отчёт по данной метрике. Вот, к примеру, как в нашем примере выглядит bran-отчёт:


Красным отмечены невыполнившиеся ветви кода.

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

Стратегии тестирования

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

Отмечу, что достичь 100%-го sub-покрытия довольно просто, причём даже на нетривиальных модулях, чего не скажешь об остальных видах покрытия. Вообще, не стоит обманывать себя мыслью, что 100% покрытие кода даст 100%-ную защиту от дефектов. Во-первых, это не так, а во-вторых, достичь 100%-го bran- и cond-покрытия в реальной жизни бывает очень непросто.

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

Ещё один факт, на который следует обратить внимание заключается в том, что показатель bran-покрытия не учитывает контекст. К примеру, пусть в методе имеются два отдельных условных оператора if(). Тест по-честному проверяет каждое из условий в состояниях TRUE и FALSE, что в результате даёт 100% bran-покрытие. Однако, тест не проверяет что будет, если условие в первом if-е вычислилось как TRUE, а во-втором как FALSE в то время как это может иметь решающее значение для логики работы программы.

Таким образом, не стоит во чтобы то ни стало стремиться к заветным 100% во всех колонках: зачастую это неоправдано и к тому же всё равно не даёт никаких гарантий.

Ещё одно полезное применение Devel::Cover - помощь при ручном тестировании. Представьте себе большую монолитную, сильно-связанную программу, "вклиниться" в которую традиционными способами затруднительно. В такой ситуации построить отчёт можно следующим образом:
perl -MDevel::Cover yourprog args
cover
Автоматизация процесса

Следующим шагом автоматизируем запуск тестов, построение отчёта, открытие браузера и удаление временных файлов (если они больше не требуются). Скрипт test-coverage-report.pl осуществляет все вышеперечисленные операции.

Пример использования:
$ ./test-coverage-report.pl --input-file quux.t --browser-cmd=/usr/bin/google-chrome --browser-args '--new-window'

quux....ok
All tests successful.
Files=1, Tests=3, 2 wallclock secs ( 1.01 cusr + 0.04 csys = 1.05 CPU)
Reading database from /tmp/quux-qbIB


---------------------------- ------ ------ ------ ------ ------ ------ ------
File stmt bran cond sub pod time total
---------------------------- ------ ------ ------ ------ ------ ------ ------
Quux.pm 94.3 87.5 80.0 87.5 0.0 46.0 86.4
quux.t 100.0 n/a n/a 100.0 n/a 54.0 100.0
Total 97.3 87.5 80.0 94.7 0.0 100.0 92.7
---------------------------- ------ ------ ------ ------ ------ ------ ------


Writing HTML output to /tmp/quux-qbIB/coverage.html ...
done.
В текущем сеансе браузера создано новое окно.
Coverage report is generated in '/tmp/quux-qbIB'. Press 'Y' (default) to cleanup this directory or 'N' if you want to keep it. [Y]
Y
удален `/tmp/quux-qbIB/Quux-pm.html'
удален `/tmp/quux-qbIB/cover.12'
удален `/tmp/quux-qbIB/Quux-pm--condition.html'
удален `/tmp/quux-qbIB/cover.css'
удален `/tmp/quux-qbIB/structure/159a56006bd3bae11c68f2dfb7609a8d'
удален `/tmp/quux-qbIB/structure/7c2bd0b808c91b847c598f3960c48eee'
удален каталог: `/tmp/quux-qbIB/structure'
удален каталог: `/tmp/quux-qbIB/runs'
удален `/tmp/quux-qbIB/Quux-pm--branch.html'
удален `/tmp/quux-qbIB/Quux-pm--subroutine.html'
удален `/tmp/quux-qbIB/coverage.html'
удален каталог: `/tmp/quux-qbIB'
Для того, чтобы этот скрипт заработал, потребуется установить следующие Perl-модули:
Остальные зависимости являются built-in модулями.

Интеграция с VIM-ом

И, наконец, последний штрих: добавим в vimrc заклинание, вызывающие этот скрипт для текущего файла. Вот оно:
map ,c    <Esc>:!/path/to/test-coverage-report.pl --input-file % --browser-cmd=/usr/bin/google-chrome --browser-args='--new-window'<CR>
map ,C <Esc>:!/path/to/test-coverage-report.pl --input-file % --browser-cmd=/usr/bin/google-chrome --browser-args='--new-window' --prove-args='--verbose'<CR>
Комбинация ,c запустит тест, построит отчёт, откроет заглавную страницу в браузере, а затем спросит удалять сгенерированные файлы или нет. По умолчанию (просто ENTER) файлы будут удалены. Вариант ,C делает ровным счётом тоже самое, но запускает prove в verbose режиме. Таким образом, для построения отчёта достаточно открыть vim-ом файл quux.t и нажать ,c.

Для ещё большей гибкости, можно написать свою обёртку для команды prove, которая, к примеру, может по имени Perl-модуля автоматически находить тест для него в определённой папке. Таким образом, ,c можно будет сказать как на самом модуле Quux.pm так и на тесте для него quux.t даже не переключая буфер!

Выводы

Интеграция модуля Devel::Cover с VIM-ом выводит к кончикам пальцев очень мощный и полезный инструмент, который способен стать серьёзным подспорьем в каждодневной работе, а благодаря простоте и удобству, тестирование невслепую очень быстро войдёт в привычку.

Ссылки
Приложение

Архив с тестовыми файлами, скриптом и отчётом: vim_plus_devel_cover_files.tar.gz

Статья опубликована на портале CITForum