Permalink
Show file tree
Hide file tree
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
Showing
4 changed files
with
178 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,6 @@ | ||
*.app | ||
*.beam | ||
*.o | ||
*.so | ||
ebin/jiffy.app | ||
.eunit | ||
deps |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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/ $< | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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. | ||
|