README.org

#+OPTIONS: ^:nil
#+TITLE: Triq

* [[https://gitlab.com/triq/triq][Triq]] QuickCheck for Erlang

** Introduction
   :PROPERTIES:
   :CUSTOM_ID: introduction
   :END:

By and large, the Triq API is modeled closely after QuviQ =eqc=, except you
want to replace any occurrence of =eqc= with =triq=. The main supporting module
is called =triq_dom=, corresponding to eqc's =eqc_gen=.

#+BEGIN_EXPORT html
<a href="https://gitlab.com/triq/triq/pipelines"><img src="https://gitlab.com/triq/triq/badges/master/pipeline.svg"></a>
#+END_EXPORT

This is a fork of Triq that is being run under the ZeroMQ Collaberation
rules, https://rfc.zeromq.org/spec:22 with the one exception being that
it is under the Apache licence.

** Obtaining Triq
   :PROPERTIES:
   :CUSTOM_ID: using-triq
   :END:

*** Installation via package manager
    :PROPERTIES:
    :CUSTOM_ID: installation-via-package-manager
    :END:

To use =triq=, you can add it as a project dependency and let your
package manager of choice handle it:

rebar.config: ={triq, "1.*"}=

erlang.mk: =DEPS = triq=

mix.exs: ={:triq, "~> 1.*"}=

*** Installation from source into =$ERL_LIBS=
    :PROPERTIES:
    :CUSTOM_ID: installation-from-source-into-erl_libs
    :END:

If you want to make =triq= available globally, you can install it from
source into your Erlang installation by adding it in one of your
=$ERL_LIBS= paths. So, it's either somewhere like
=/usr/lib/erlang/lib= or =$HOME/.erl=.

You can either download a tagged release from
=https://gitlab.com/triq/triq/tags= and extract that or clone the
git repo =https://gitlab.com/triq/triq= in the target directory. Once
that's done, cd into the directory and run =make=.

Now, if you start =erl=, you should be able to call functions from the
=triq= module.

#+BEGIN_EXAMPLE
    $ erl
    1> code:which(triq).
    "/usr/lib/erlang/lib/triq/ebin/triq.beam"
    2>
#+END_EXAMPLE

** Writing QuickCheck properties with Triq
    :PROPERTIES:
    :CUSTOM_ID: writing-properties-with-triq
    :END:

To write properties with =triq=, include the header file:

#+BEGIN_EXAMPLE erlang
    -include_lib("triq/include/triq.hrl").
#+END_EXAMPLE

And you're ready to write property tests. An example property could be:

#+BEGIN_EXAMPLE erlang
    prop_append() ->
        ?FORALL({Xs,Ys},{list(int()),list(int())},
                lists:reverse(Xs++Ys)
                ==
                lists:reverse(Ys) ++ lists:reverse(Xs)).
#+END_EXAMPLE

To test this property, run =triq:check/1=, thus:

#+BEGIN_EXAMPLE
    1> triq:check(prop_append()).
    ......................................................................
    ..............................
    Ran 100 tests
    true
    2>
#+END_EXAMPLE

If the test fails, it will try to shrink the result; here is an example:

#+BEGIN_EXAMPLE erlang
    prop_delete() ->
        ?FORALL(L,list(int()),
            ?IMPLIES(L /= [],
                ?FORALL(I,elements(L),
                    ?WHENFAIL(io:format("L=~p, I=~p~n", [L,I]),
                              not lists:member(I,lists:delete(I,L)))))).
#+END_EXAMPLE

Which runs like this:

#+BEGIN_EXAMPLE
    1> triq:check(triq_tests:prop_delete()).
    x....Failed!
    L=[4,5,5], I=5

    Failed after 5 tests with false
    Simplified:
            L = [0,0]
            I = 0
    false
    2>
#+END_EXAMPLE

You can get the values used for the failing test with =counterexample=,
and reuse the same test values with =check/2=:

#+BEGIN_EXAMPLE
    3> A = triq:counterexample(triq_tests:xprop_delete()).
    x.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxFailed!
    L=[3,2,1,1,1], I=1

    Failed after 101 tests with false
    Simplified:
        L = [0,0]
        I = 0
    [[0,0],0]
    4> A.
    [[0,0],0]
    5> triq:check(triq_tests:xprop_delete(), A).
    Failed!
    L=[0,0], I=0

    Failed after 1 tests with false
    Simplified:
        L = [0,0]
        I = 0
    false
    6>
#+END_EXAMPLE

Modules compiled with the =triq.hrl= header, auto-export all functions
named =prop_*=, and have a function added called =check/0= which runs
=triq:check/1= on all the properties in the module.

#+BEGIN_EXAMPLE
    1> mymodule:check().
#+END_EXAMPLE

You can also instruct =triq= to generate EUnit tests for each property
which allow the module to be treated like an ordinary EUnit test
suite. This is highly recommended and avoids the need for =triq= or
generic =qc= support in your build/test tool of choice. To achieve
that, just make sure to include the attribute =-triq(eunit).= at the
top of the module. Thus, the initial =triq.hrl= include would turn
into this:

#+BEGIN_EXAMPLE erlang
    -include_lib("triq/include/triq.hrl").
    -triq(eunit).
#+END_EXAMPLE