Here are some guidelines on how to use Concuerror as part of an EUnit
suite.
First of all, the “order” in which you run the two tools is
significant: e.g. invoking Concuerror with the test/0
function
generated by EUnit is a bad idea, for at least two reasons:
you probably don’t want to test all of EUnit’s boilerplate code
systematically and
the default test function generated by EUnit runs all tests, one
after another; as a result, systematic testing will have to explore
a number of schedulings that is the product of every individual
test’s schedulings! You should use Concuerror on single tests
instead.
Suppose you have the following EUnit tests:
- module ( eunit_sample ).
- include_lib ( "eunit/include/eunit.hrl" ).
%%==============================================================================
foo_test () ->
? assert ( true ).
msg_test () ->
P = self (),
spawn ( fun () -> P ! foo end ),
spawn ( fun () -> P ! bar end ),
receive
Msg -> ? assert ( foo , Msg )
end .
reg_test () ->
Fun =
fun () ->
receive message -> ok
after 100 -> timeout
end
end ,
P = spawn ( Fun ),
register ( p , P ),
p ! message ,
? assert ( true ).
For this suite, running EUnit yields:
$ erlc eunit_sample.erl
$ erl -noinput -s eunit_sample test -s erlang halt
All 3 tests passed.
It is fairly easy to see, however, that the two latter tests can fail
in particular interleavings .
Invoking Concuerror on msg_test/0
easily finds the error:
$ concuerror -m eunit_sample -t msg_test
Concuerror v0.17 ( ...) started at 08 Oct 2017 16:19:48
[ ...]
Errors were found! ( check concuerror_report.txt)
Done at 08 Oct 2017 16:23:49 ( Exit status: error)
Summary: 1 errors, 2/2 interleavings explored
The output file contains the trace:
Concuerror v0.17 (...) started at 08 Oct 2017 16:23:49
Options:
[...]
################################################################################
Interleaving #2
--------------------------------------------------------------------------------
Errors found:
* At step 6 process P exited abnormally
Reason:
{{assertEqual,[{module,eunit_sample},
{line,15},
{expression,"Msg"},
{expected,foo},
{value,bar}]},
[...]}
Stacktrace:
[...]
--------------------------------------------------------------------------------
Event trace:
1: P: P.1 = erlang:spawn(erlang, apply, [#Fun<eunit_sample.'-msg_test/0-fun-0-'.0>,[]])
in erlang.erl line 2693
2: P: P.2 = erlang:spawn(erlang, apply, [#Fun<eunit_sample.'-msg_test/0-fun-1-'.0>,[]])
in erlang.erl line 2693
3: P.2: bar = P ! bar
in eunit_sample.erl line 13
4: P.2: exits normally
5: P: receives message (bar)
in eunit_sample.erl line 14
6: P: exits abnormally ({{assertEqual,[{module,eunit_sample},{line,15},..]}})
7: P.1: foo = P ! foo
in eunit_sample.erl line 12
8: P.1: exits normally
################################################################################
Exploration completed!
################################################################################
[...]
Info:
--------------------------------------------------------------------------------
* Automatically instrumented module io_lib
* Automatically instrumented module gen_server
* Automatically instrumented module eunit_sample
* Automatically instrumented module erlang
* You can see pairs of racing instructions (in the report and '--graph') with '--show_races true'
################################################################################
Done at 08 Oct 2017 16:23:49 (Exit status: error)
Summary: 1 errors, 2/2 interleavings explored
Notice that Concuerror instrumented only some very basic modules. In
contrast, invoking Concuerror on the test/0
function that is
automatically generated by EUnit (and is automatically tested by
Concuerror), yields:
$ concuerror -m eunit_sample
Concuerror v0.17 (...) started at 08 Oct 2017 16:28:38
Writing results in concuerror_report.txt
* Info: Automatically instrumented module io_lib
* Info: Automatically instrumented module gen_server
* Info: Automatically instrumented module eunit_sample
* Info: Automatically instrumented module eunit
* Info: Automatically instrumented module proplists
* Info: Automatically instrumented module eunit_tty
* Info: Automatically instrumented module eunit_listener
* Info: Automatically instrumented module erlang
* Info: Automatically instrumented module eunit_serial
* Info: Automatically instrumented module sets
* Info: Automatically instrumented module lists
* Info: Automatically instrumented module eunit_lib
* Info: Automatically instrumented module gb_trees
* Info: Automatically instrumented module dict
* Info: Automatically instrumented module eunit_server
* Warning: Your test seems to try to set up an EUnit server. This is a bad idea, for at least two reasons: 1) you probably don't want to test all of EUnit's boilerplate code systematically and 2) the default test function generated by EUnit runs all tests, one after another; as a result, systematic testing will have to explore a number of schedulings that is the product of every individual test's schedulings! You should use Concuerror on single tests instead.
* Info: Automatically instrumented module queue
* Info: Automatically instrumented module eunit_proc
* Tip: Increase '--print_depth' if output/graph contains "...".
* Error: Stop testing on first error. (Check '-h keep_going').
* Error: Concuerror does not support calls to built-in erlang:statistics/1 (found in eunit_proc.erl line 324).
If you cannot avoid its use, please contact the developers.
Stacktrace:
[{eunit_proc,with_timeout,3,[{file,"eunit_proc.erl"},{line,324}]},
{eunit_proc,run_group,2,[{file,"eunit_proc.erl"},{line,549}]},
{eunit_proc,child_process,2,[{file,"eunit_proc.erl"},{line,353}]}]
Done at 08 Oct 2017 16:28:39 (Exit status: fail)
Summary: 1 errors, 1/1 interleavings explored
Concuerror does not support calls to
erlang:statistics/1
. Additionally, it emits a Warning that this
particular use is problematic.
Fortunately, the EUnit suite can be extended to also run the tests
with Concuerror:
%%==============================================================================
- define ( concuerror_options , [{ module , ? MODULE }, quiet ]).
foo_concuerror_test () ->
? assertEqual ( ok , concuerror : run ([{ test , foo_test }| ? concuerror_options ])).
msg_concuerror_test () ->
? assertEqual ( ok , concuerror : run ([{ test , msg_test }| ? concuerror_options ])).
reg_concuerror_test () ->
? assertEqual ( ok , concuerror : run ([{ test , reg_test }| ? concuerror_options ])).
Invoking Concuerror again, yields:
$ erlc eunit_sample.erl
$ erl -noinput -s eunit_sample test -s erlang halt
eunit_sample: msg_concuerror_test...* failed*
in function eunit_sample:'-msg_concuerror_test/0-fun-0-' /0 ( /home/stavros/git/Concuerror/tests-real/suites/options/eunit/eunit_sample.erl, line 38)
** error:{ assertEqual,[{ module,eunit_sample} ,
{ line,38} ,
{ expression,"concuerror : run ( [ { test , msg_test } | ? concuerror_options ] )" } ,
{ expected,ok} ,
{ value,error}]}
output:<<"" >>
eunit_sample: reg_concuerror_test...* failed*
in function eunit_sample:'-reg_concuerror_test/0-fun-0-' /0 ( /home/stavros/git/Concuerror/tests-real/suites/options/eunit/eunit_sample.erl, line 41)
** error:{ assertEqual,[{ module,eunit_sample} ,
{ line,41} ,
{ expression,"concuerror : run ( [ { test , reg_test } | ? concuerror_options ] )" } ,
{ expected,ok} ,
{ value,error}]}
output:<<"" >>
=======================================================
Failed: 2. Skipped: 0. Passed: 4.
The second and third tests have failed as expected and if run
individually you can debug them from the report.
Have fun and keep testing!