Подтвердить что ты не робот

Какой лучший способ для unit test параллельного кода Erlang?

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

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

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

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

4b9b3361

Ответ 1

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

Также стоит упомянуть Dialyzer (DIscrepancy AnaLYZer для ERlang) (Документы, Учебник, Руководство), который является инструментом для статического анализа кода для обнаружение ошибок. Это также поддерживает обнаружение некоторых concurrency -errors (см. бумага).

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

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

Ответ 2

Вопрос немного расплывчатый ( "Erlang является параллельным, проверяйте его с помощью Erlang!" ), но я попытаюсь немного разобраться.

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

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

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).

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

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

Ответ 3

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

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

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

Также посмотрите проект ProTest: http://www.protest-project.eu

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

Ответ 4

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

Ответ 5

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

Ответ 6

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

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