Каков наилучший способ для модульного тестирования параллельного кода Erlang? - PullRequest
17 голосов
/ 20 декабря 2008

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

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

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

Кто-нибудь нашел хороший способ протестировать параллельный код в Erlang?

Ответы [ 6 ]

9 голосов
/ 10 октября 2011

Я только что нашел отличное недавно разработанное (по состоянию на 2011 г.) программное обеспечение для тестирования параллельных приложений Erlang под названием Concuerror . На нем есть несколько бумаг и хранилище на github . Очевидно, он работает, используя собственный планировщик и систематически проверяя различные чередования между процессами.

Также стоит упомянуть Dialyzer (Различия AnaLYZer для ERlang) ( Papers , Tutorial , Manual ), который является инструментом для статического анализа кода на наличие ошибок. Также имеется поддержка для обнаружения некоторых ошибок параллелизма (см. paper ).

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

PS. Для несовпадающих частей EUnit и QuickCheck (есть также бесплатная версия) должны работать нормально.

5 голосов
/ 16 января 2009

Вопрос немного расплывчатый («Эрланг параллелен, проверь его с Эрлангом!»), Но я попытаюсь уточнить.

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

Частью красоты Erlang является способность сделать параллелизм прозрачным. Рассмотрим следующий пример (функция, которая распараллеливает суммирование списка списков):

deep_sum(ListOfLists) ->
     Parent = self(),
     [spawn(fun() -> Parent ! lists:sum(List) end) || List <- ListOfLists],
     lists:sum([receive Sum -> Sum end || _ <- ListOfLists]).

Обычно вы тестируете это с помощью очень простого теста EUnit:

deep_sum_test() ->
     ?assertEqual(0,  deep_sum([0,  0,  0,  0])),
     ?assertEqual(40, deep_sum([10, 10, 10, 10]).

Теперь, скажем, у нас есть немного более явный API для этой функциональности: пул процессов в качестве аргумента:

deep_sum(Pool, ListOfLists) ->
     distribute_lists(Pool, ListOfLists),
     lists:sum([receive Sum -> Sum end || _ <- ListOfLists]).

distribute_lists(Pool, ListOfLists) -> distribute_lists(Pool, Pool, ListOfLists).

distribute_lists([P|Pool], All, [L|ListOfLists]) ->
     P ! {self(), L},
     distribute_lists(Pool, All, ListOfLists);
distribute_lists([], All, ListOfLists) ->
     distribute_lists(All, All, ListOfLists);
distribute_lists(_Pool, _All, []) ->
     ok.

При тестировании мы имеем дело с подделкой этого пула процессов:

deep_sum_test() ->
     Pool = [spawn_link(fun() -> fake_pool(1) end) || _ <- lists:seq(1, 3)],
     ?assertEqual(4, deep_sum(Pool, [lists:seq(1, 3) || _ <- list:seq(1, 4)]),
     ?assertEqual(7, deep_sum(Pool, [lists:seq(1, 3) || _ <- list:seq(1, 7)]),
     [P ! stop || P <- Pool].

fake_pool(CannedResponse) ->
     receive
         {From, _L} -> From ! CannedResponse;
         stop -> ok
     end,
     fake_pool(CannedResponse).

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

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

2 голосов
/ 07 января 2009

Единственный известный мне инструмент, который вы можете использовать для тестирования программ Erlang в параллельных сценариях, это QuickCheck: http://www.quviq.com/

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

К сожалению, это коммерческий инструмент

Также взгляните на проект ProTest: http://www.protest -project.eu

Обновление: проект ProTest выпустил опрос, "Результаты для: Опрос инструментов тестирования Erlang для проекта ProTest"

1 голос
/ 20 декабря 2008

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

0 голосов
/ 11 января 2009

Вы можете выделить часть, которую вы хотите проверить с помощью макетов, используя это: http://github.com/charpi/erl_mock/tree/master

У меня нет опыта работы с erl_mock.

0 голосов
/ 20 декабря 2008

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

...