Skip to content
Browse files

Test and bug fixes for th_util and th_iter

  • Loading branch information...
1 parent 4280b7e commit 146df700b17e55b455d785034bab16f9a0c7494f @jamii committed Aug 16, 2011
Showing with 173 additions and 17 deletions.
  1. +72 −3 src/th_iter.erl
  2. +26 −0 src/th_test.erl
  3. +61 −0 src/th_util.erl
  4. +14 −14 src/types.hrl
View
75 src/th_iter.erl
@@ -2,11 +2,15 @@
-module(th_iter).
--export([to_list/1, map/2, take/2, foreach/2, flatten/1]).
+-include_lib("proper/include/proper.hrl").
+
+-export([to_list/1, from_list/1, map/2, take/2, foreach/2, flatten/1]).
-type iter(X) :: fun(() -> done | {X, iter(X)}).
-export_types([iter/1]).
+% --- api ---
+
-spec to_list(iter(X)) -> list(X).
to_list(Iter) ->
case Iter() of
@@ -16,9 +20,20 @@ to_list(Iter) ->
[Head | to_list(Iter2)]
end.
+-spec from_list(list(X)) -> iter(X).
+from_list(List) ->
+ fun () ->
+ case List of
+ [] ->
+ done;
+ [Head | List2] ->
+ {Head, from_list(List2)}
+ end
+ end.
+
-spec map(fun((X) -> Y), iter(X)) -> iter(Y).
map(F, Iter) ->
- fun () ->
+ fun () ->
case Iter() of
done ->
done;
@@ -68,6 +83,60 @@ flatten(Iter) ->
Push = push(lists:flatten(List), flatten(Iter2)),
Push();
{Elem, Iter2} ->
- {Elem, Iter2}
+ {Elem, flatten(Iter2)}
end
end.
+
+% --- tests ---
+
+prop_list_to_list() ->
+ ?FORALL(List, list(),
+ to_list(from_list(List)) == List
+ ).
+
+prop_map() ->
+ ?FORALL(List, list(),
+ begin
+ F = fun(Elem) -> {Elem, Elem} end,
+ to_list(map(F,from_list(List))) == lists:map(F, List)
+ end
+ ).
+
+prop_take() ->
+ ?FORALL({List, N}, {list(), non_neg_integer()},
+ take(N, from_list(List)) == lists:sublist(List, 1, N)
+ ).
+
+prop_foreach() ->
+ ?FORALL(List, list(),
+ begin
+ foreach(
+ fun (Elem) ->
+ self() ! {prop_foreach, Elem}
+ end,
+ from_list(List)),
+ lists:foreach(
+ fun (Elem) ->
+ receive
+ {prop_foreach, Elem2} ->
+ Elem = Elem2
+ after 0 ->
+ error(prop_foreach)
+ end
+ end,
+ List),
+ true
+ end
+ ).
+
+prop_flatten() ->
+ ?FORALL(List, list(),
+ to_list(flatten(from_list(List))) == lists:flatten(List)
+ ).
+
+prop_flatten2() ->
+ ?FORALL(List, list(list()),
+ to_list(flatten(from_list(List))) == lists:flatten(List)
+ ).
+
+% --- end ---
View
26 src/th_test.erl
@@ -0,0 +1,26 @@
+% test utils
+
+-module(th_test).
+
+-include("conf.hrl").
+-include("types.hrl").
+
+-include_lib("proper/include/proper.hrl").
+
+-export([ip_address/0, address/0, hex/0, 'end'/0]).
+
+-define(MAX_PORT, 65535).
+
+ip_address() ->
+ {integer(0, 255), integer(0, 255), integer(0, 255), integer(0, 255)}.
+
+address() ->
+ #address{host=ip_address(), port=integer(0, ?MAX_PORT)}.
+
+hex() ->
+ ?LET(Chars, vector(round(?END_BITS / 4), oneof("0123456789ABCDEFabcdef")),
+ iolist_to_binary(Chars)
+ ).
+
+'end'() ->
+ {'end', binary(round(?END_BITS / 8))}.
View
61 src/th_util.erl
@@ -5,6 +5,8 @@
-include("conf.hrl").
-include("types.hrl").
+-include_lib("proper/include/proper.hrl").
+
-export([address_to_binary/1, binary_to_address/1, binary_to_end/1, hex_to_end/1, end_to_hex/1, to_end/1, random_end/0, random_end/1, distance/2, to_bits/1]).
-export([tap_to_json/1, json_to_tap/1]).
-export([ensure_started/1, set_nth/3]).
@@ -117,4 +119,63 @@ json_to_subtap({struct, Json}) ->
Has = proplists:get_value(<<"has">>, Json, []),
#subtap{is=Is, has=Has}.
+% --- tests ---
+
+prop_address_to_address() ->
+ ?FORALL(Address, th_test:address(),
+ binary_to_address(address_to_binary(Address)) == Address
+ ).
+
+to_lower(Hex) ->
+ binary:list_to_bin(string:to_lower(binary:bin_to_list(Hex))).
+
+prop_hex_to_hex() ->
+ ?FORALL(Hex, th_test:hex(),
+ end_to_hex(hex_to_end(Hex)) == to_lower(Hex)
+ ).
+
+prop_distance_self() ->
+ ?FORALL(End, th_test:'end'(),
+ distance(End, End) == 0
+ ).
+
+% can this be rewritten to be more obvious? don't like the potential for off-by-one
+prop_distance_single() ->
+ ?FORALL({End1, Pos}, {th_test:'end'(), integer(0, ?END_BITS - 1)},
+ begin
+ {'end', <<Prefix:Pos/bitstring, Bit:1/integer, Suffix/bitstring>>} = End1,
+ End2 = {'end', <<Prefix/bitstring, (1-Bit):1/integer, Suffix/bitstring>>},
+ distance(End1, End2) == round(math:pow(2, ?END_BITS - Pos - 1))
+ end
+ ).
+
+prop_random_end() ->
+ ?FORALL(Len, integer(0, ?END_BITS),
+ ?FORALL(Prefix, bitstring(Len),
+ begin
+ {'end', <<Prefix2:Len/bitstring, _/bitstring>>} = random_end(Prefix),
+ Prefix == Prefix2
+ end
+ )
+ ).
+
+prop_set_nth_get() ->
+ ?FORALL({List, Value}, {non_empty(list()), term()},
+ ?FORALL(Pos, integer(1, length(List)),
+ lists:nth(Pos, set_nth(Pos, List, Value)) == Value
+ )
+ ).
+
+prop_set_nth_id() ->
+ ?FORALL(List, non_empty(list()),
+ ?FORALL(Pos, integer(1, length(List)),
+ set_nth(Pos, List, lists:nth(Pos, List)) == List
+ )
+ ).
+
+prop_tap_to_tap() ->
+ ?FORALL(Tap, tap(),
+ json_to_tap(tap_to_json(Tap)) == Tap
+ ).
+
% --- end ---
View
28 src/types.hrl
@@ -4,22 +4,22 @@
-type ip_address() :: {integer(), integer(), integer(), integer()}.
--record(address, {
- host :: ip_address(),
- port :: integer()
- }).
--type address() :: #address{}.
+-record(address, {host, port}).
+-type address() :: #address{
+ host :: ip_address(),
+ port :: integer()
+ }.
-type 'end'() :: {'end', binary()}.
-type bits() :: list(boolean()).
--record(tap, {
- subtaps :: list(subtap)
- }).
--type tap() :: #tap{}.
+-record(tap, {subtap}).
+-type tap() :: #tap{
+ subtaps :: list(subtap())
+ }.
--record(subtap, {
- is :: list({Key :: th_telex:json_string(), Value :: th_telex:json()}),
- has :: list(Key :: th_telex:json_string())
- }).
--type subtap() :: #subtap{}.
+-record(subtap, {is, has}).
+-type subtap() :: #subtap{
+ is :: list({Key :: th_telex:json_string(), Value :: th_telex:json()}),
+ has :: list(Key :: th_telex:json_string())
+ }.

0 comments on commit 146df70

Please sign in to comment.
Something went wrong with that request. Please try again.