Save 37% off PRO during our Black Friday Sale! »

A Brief Introduction to Dialyzer and Proper

A Brief Introduction to Dialyzer and Proper

3e09fee7b359be847ed5fa48f524a3d3?s=128

Christopher Meiklejohn

February 02, 2014
Tweet

Transcript

  1. Dialyzer / PropEr Christopher Meiklejohn Basho Technologies, Inc.

  2. Static analysis tool for Erlang Dialyzer

  3. Based on success typings Dialyzer

  4. -opaque gcounter() :: orddict:orddict(). ! -type gcounter_op() :: increment |

    {increment, pos_integer()}. ! %% @doc Create a new, empty `gcounter()' -spec new() -> gcounter(). new() -> orddict:new(). ! %% @doc Create a `gcounter()' with an initial update -spec new(term(), pos_integer()) -> gcounter(). new(Id, Count) when is_integer(Count), Count > 0 -> {ok, Cnt} = update({increment, Count}, Id, new()), Cnt.
  5. %% @doc Create a `gcounter()' with an initial update -spec

    new(term(), atom()) -> gcounter(). new(Id, Count) when is_integer(Count), Count > 0 -> {ok, Cnt} = update({increment, Count}, Id, new()), Cnt.
  6. riak_dt_gcounter.erl:63: Invalid type specification for function riak_dt_gcounter:new/2. The success typing

    is (_,pos_integer()) -> riak_dt_gcounter:gcounter()
  7. %% @doc Create a `gcounter()' with an initial update -spec

    new(riak_dt:actor(), pos_integer()) -> gcounter(). new(Id, Count) when is_integer(Count), Count > 0 -> {ok, Cnt} = update({increment, Count}, Id, new()), Cnt. ! %% @doc `increment' the entry in `GCnt' for `Actor' by 1 or `{increment, Amt}'. %% returns an updated `gcounter()' or error if `Amt' is not a `pos_integer()' -spec update(gcounter_op(), riak_dt:actor(), gcounter()) -> {ok, gcounter()}. update(increment, Actor, GCnt) -> {ok, increment_by(1, Actor, GCnt)}; update({increment, Amount}, Actor, GCnt) when is_integer(Amount), Amount > 0 -> {ok, increment_by(Amount, Actor, GCnt)}.
  8. riak_dt_gcounter.erl:64: Function new/2 has no local return riak_dt_gcounter.erl:65: The call

    riak_dt_gcounter:update({'incremet',pos_integer()},Id::any(),riak_dt_g counter:gcounter()) breaks the contract (gcounter_op(),riak_dt:actor(),gcounter()) -> {‘ok',gcounter()}
  9. %% @doc Create a `gcounter()' with an initial update -spec

    new(riak_dt:actor(), pos_integer()) -> gcounter(). new(Id, Count) when is_integer(Count), Count > 0 -> {ok, Cnt} = update({increment, Count}, Id, new()), Cnt. ! %% @doc `increment' the entry in `GCnt' for `Actor' by 1 or `{increment, Amt}'. %% returns an updated `gcounter()' or error if `Amt' is not a `pos_integer()' -spec update(gcounter_op(), riak_dt:actor(), gcounter()) -> {ok, gcounter()}. update(increment, Actor, GCnt) -> {ok, increment_by(1, Actor, GCnt)}.
  10. riak_dt_gcounter.erl:64: Function new/2 has no local return riak_dt_pncounter.erl:216: The created

    fun has no local return
  11. Demo

  12. Property-based testing tool PropEr

  13. Inspired by QuickCheck; licensed under GPL PropEr

  14. Full integration with Erlang specifications PropEr

  15. Simple

  16. -module(simple_props). ! %% Properties are automatically exported. -include_lib("proper/include/proper.hrl"). ! %%

    Functions that start with prop_ are considered properties prop_t2b_b2t() -> ?FORALL(T, term(), T =:= binary_to_term(term_to_binary(T))).
  17. 1> c(simple_props). {ok,simple_props} 2> proper:quickcheck(simple_props:prop_t2b_b2t()). ................................................... ................................................. OK: Passed 100

    test(s) true
  18. Stack

  19. -spec new() -> stack(_T). new() -> {0, []}. ! -spec

    push(T, stack(T)) -> stack(T). push(X, {N,Elems}) -> {N+1, [X|Elems]}. ! -spec pop(stack(T)) -> {T,stack(T)}. pop({0, []}) -> throw(stack_empty); pop({N, [Top|Rest]}) when N > 0 -> {Top, {N-1,Rest}}.
  20. prop_push_pop() -> ?FORALL({X,S}, {integer(),stack(integer())}, begin {Y,_} = pop(push(X,S)), X =:=

    Y end).
  21. Eshell V5.10.3 (abort with ^G) 1> proper:quickcheck(stack:prop_push_pop()). ...................................................................... .............................. OK:

    Passed 100 test(s). true
  22. Lists

  23. prop_reverse() -> ?FORALL(X, list(integer()), lists:reverse(lists:reverse(X)) == X).

  24. 7> proper:quickcheck(lists_proper:prop_reverse()). ...................................................................... .............................. OK: Passed 100 test(s). true

  25. prop_delete() -> ?FORALL(L, non_empty(list(integer())), ?FORALL(X, elements(L), not lists:member(X, (lists:delete(X, L))))).

  26. 16> proper:quickcheck(lists_proper:prop_delete()). ........! Failed: After 9 test(s). [-1,-1] -1 !

    Shrinking (0 time(s)) [-1,-1] -1 false 17>
  27. 17> proper:quickcheck(lists_proper:prop_delete()). ..................................! Failed: After 35 test(s). [2,-10,-7,13,70,2] 2 !

    Shrinking ......(6 time(s)) [2,2] 2 false
  28. prop_delete() -> ?FORALL(L, non_empty(list(integer())), ?FORALL(X, elements(L), lists:foldl(fun(Y, Acc) -> case

    Y of X -> Acc + 1; _ -> Acc end end, 0, L) - 1 == lists:foldl(fun(Y, Acc) -> case Y of X -> Acc + 1; _ -> Acc end end, 0, lists:delete(X, L)))).
  29. 22> proper:quickcheck(lists_proper:prop_delete()). ...................................................................... .............................. OK: Passed 100 test(s). true

  30. Thanks!