Skip to content
Permalink
Browse files
Implement PropEr tests for JSON parsing
Big thanks to Dmitry Groshev [1] for help getting PropEr tests into
Jiffy. These generate valid EJSON to check parsing as well as check
that random inputs don't cause segfaults or other nasty effects.

Future improvements would be to write a JSON generator as well as a
version that introduces known errors into the binary for checking known
parsing errors as well.

[1] https://github.com/si14

Fixes: #10
  • Loading branch information
davisp committed Nov 14, 2011
1 parent 4cb0736 commit ffa2fafa28f85cdfb596c3d57e3025ac474e513d
Showing 4 changed files with 178 additions and 5 deletions.
@@ -1,4 +1,6 @@
*.app
*.beam
*.o
*.so
ebin/jiffy.app
.eunit
deps
@@ -1,11 +1,30 @@

all: build

%.beam: %.erl
erlc -o test/ $<
clean:
./rebar clean
rm -rf logs
rm -rf .eunit

deps: ./deps/
./rebar get-deps update-deps


build: c_src/decoder.c
build: deps
./rebar compile

check: test/etap.beam test/util.beam

etap: test/etap.beam test/util.beam
prove test/*.t


eunit:
./rebar eunit skip_deps=true


check: etap eunit


%.beam: %.erl
erlc -o test/ $<

@@ -1,3 +1,8 @@
{deps, [
{proper, ".*", {git, "https://github.com/manopapad/proper.git", "master"}}
]}.


{port_sources, ["c_src/*.c"]}.
{so_name, "jiffy.so"}.
{port_envs, [
@@ -15,3 +20,12 @@
{"darwin10.*-32$", "CXXFLAGS", "-m32"},
{"darwin10.*-32$", "LDFLAGS", "-arch i386"}
]}.


{eunit_opts, [
verbose,
{report, {
eunit_surefire, [{dir,"."}]
}}
]}.

@@ -0,0 +1,138 @@
% This file is part of Jiffy released under the MIT license.
% See the LICENSE file for more information.

-module(jiffy_tests).

-include_lib("proper/include/proper.hrl").
-include_lib("eunit/include/eunit.hrl").


proper_test_() ->
PropErOpts = [
{to_file, user},
{max_size, 60},
{numtests, 1000}
],
{timeout, 3600, ?_assertEqual([], proper:module(jiffy_tests, PropErOpts))}.


prop_encode_decode() ->
?FORALL(Data, json(),
begin
%io:format(standard_error, "Data: ~p~n", [Data]),
ExpData = conv_keys(Data),
ExpData == jiffy:decode(jiffy:encode(Data))
end
).

prop_encode_not_crash() ->
?FORALL(Data, any(), begin catch jiffy:encode(Data), true end).

prop_decode_not_crash_bin() ->
?FORALL(Data, binary(), begin catch jiffy:decode(Data), true end).

prop_decode_not_crash_any() ->
?FORALL(Data, any(), begin catch jiffy:decode(Data), true end).


% JSON Generation


json_null() ->
null.


json_boolean() ->
oneof([true, false]).


json_number() ->
oneof([integer(), float()]).


json_string() ->
escaped_utf8_bin().


json_key() ->
oneof([json_string(), escaped_atom()]).


json_list(S) when S =< 0 ->
[];
json_list(S) ->
?LETSHRINK(
[ListSize],
[integer(0, S)],
vector(ListSize, json_text(S - ListSize))
).


json_object(S) when S =< 0 ->
{[]};
json_object(S) ->
?LETSHRINK(
[ObjectSize],
[integer(0, S)],
{vector(ObjectSize, {json_key(), json_text(S - ObjectSize)})}
).


json_value() ->
oneof([
json_null(),
json_boolean(),
json_string(),
json_number()
]).


json_text(S) when S > 0 ->
?LAZY(oneof([
json_list(S),
json_object(S)
]));
json_text(_) ->
json_value().


json() ->
?SIZED(S, json_text(S)).


escaped_atom() ->
?LET(A, atom(),
unicode:characters_to_binary(atom_to_list(A), unicode, utf8)
).


escaped_utf8_bin() ->
?SUCHTHAT(Bin,
?LET(S, ?SUCHTHAT(L, list(escaped_char()), L /= []),
unicode:characters_to_binary(S, unicode, utf8)),
is_binary(Bin)
).


escaped_char() ->
?LET(C, char(),
case C of
$" -> "\\\"";
C -> C
end
).


% Atoms get munged to binaries in the round trip.
conv_keys({Props}) ->
{lists:map(fun({K, V}) -> {conv_key(K), conv_keys(V)} end, Props)};
conv_keys(Vals) when is_list(Vals) ->
lists:map(fun(V) -> conv_keys(V) end, Vals);
conv_keys(Other) ->
Other.

conv_key(K) when is_atom(K) ->
list_to_binary(atom_to_list(K));
conv_key(K) ->
K.

0 comments on commit ffa2faf

Please sign in to comment.