[erlang-questions] How to mimic Erlang nodes in PropEr statem test

Fred Hebert <>
Fri Jan 25 14:03:06 CET 2019


On 01/23, Nyirő Gergő wrote:
>
>Right now I have only one gen_server (crdt_server) per node, but I would
>like to add an event handler (crdt_event) which could persist the content
>of the crdt set or log the executed commands.
>

Okay, so that's essentially duplicating the messages: send them to the 
server, but you want to asynchronously log with another process. Do I 
understand this right?

>crdt_event could be registered with a fix name then all the
>crdt_server processes
>in the proper statem test will send the events to the same event handler, so
>the event has to be extended with a reference to the sender (e.g: the name
>or pid of the crdt_server). It seems too complicated for my situation.

Is the event handler only required for the tests? If so, that can make 
sense. If you're working with the same design for your production 
system, it would generally be a better idea to have one process that 
handles the logging per CRDT server, assuming what you end up with is 
one file per server, in order.

Using the same handler for multiple servers is a possibility, but you 
have a lot more risk and complexity—as you noted, you'd need to start 
identifying each message, rather than just giving the event manager a 
destination where to output traffic—while coupling the runtime of 
unrelated files.

>
>Other solution would be to pass the NameOrPid of the crdt_event processes to
>crdt_server processes, but a process registry could be a more elegant solution.
>

This is a possibility. There are other options such as:

- Each crdt_server is spawned under a crdt_sup. The crdt_sup works by 
  booting two processes: crdt_log (first) and crdt_server (second). Each 
  is passed a name that identifies the server ("crdt_1", `node_a`, or 
  whatever)
- By using a dynamic process registry like gproc (see 
  https://hex.pm/packages/gproc) and replacing the name registration 
  from {local, Name} to {via, gproc, {n, l, {crdt_log, Name}}} and {via, 
  gproc, {n, l, {crdt_server, Name}}} you can give them unique names 
  that they can "know"
- On every call the crdt_server wants to log, it can call a function 
  from the crdt_log module, such as crdt_log:do_log(Name, Contents)

If you implement the latter function as:

    -module(crdt_log).
    
    ...
    
    start_link(Name) ->
        %% {n, l, Name} stands for "{name, local, ActualName}" but gproc 
        %% uses shorthand
        gen_server:start_link({via, gproc, {n, l, {crdt_log, Name}}},
                              ?MODULE, [], []).
    
    ...
    
    do_log(Name, Event) ->
        gen_server:cast({via, gproc, {n, l, {crdt_log, Name}}}, Event).
    
    ...

Then you'll be able to call the log process from within the other one 
and send asynchronous events this way. You can then consider both 
processes as one unit that fits under one supervisor. For your test, you 
now want to boot 3 of these supervisors instead of only 3 servers, and 
you should get a rather transparent layer added otherwise.


>Should I use ct_property_test [1] instead?
>

This is an experimental call or module to integrate the results of your 
properties to the common test flow. You're free to use it if you want to 
integrate everything into CT.

I personally maintain the rebar3 plugin for PropEr and I tend to prefer 
it for some of the additional features it adds:

- meta functions 
  https://github.com/ferd/rebar3_proper#per-properties-meta-functions 
  (which I might need to contribute upstream to PropEr instead)
- The ability to run a `rebar3 proper --retry` to get the last failing 
  case run again
- The `rebar3 proper --store` call, which can be called after a failure 
  to store the failing cases in a file that can be used to test 
  regressions
- The `rebar3 proper --regressions` switch, which replays the previously 
  stored couterexamples.

You can, of course, get all of this by doing more manual work in CT or 
by transcribing counterexamples by hand into regression tests. I 
personally just use the proper and CT commands distinctly, and instead 
add a rebar3 alias for both:

    {alias, [{check, [ct, proper]}]}.

Which then lets me call 'rebar3 check' and runs both the CT and the 
PropEr test suites.

Regards,
Fred.


More information about the erlang-questions mailing list