浏览代码

Replace etap with eunit for the test suite

Finally got off my butt and started using EUnit.
pull/65/head
Paul J. Davis 11 年前
父节点
当前提交
11ab973810
共有 18 个文件被更改,包括 380 次插入885 次删除
  1. +0
    -30
      test/001-yajl-tests.t
  2. +0
    -21
      test/002-literals.t
  3. +0
    -15
      test/008-halfword.t
  4. +0
    -29
      test/010-short-doubles.t
  5. +0
    -612
      test/etap.erl
  6. +33
    -0
      test/jiffy_01_yajl_tests.erl
  7. +28
    -0
      test/jiffy_02_literal_tests.erl
  8. +42
    -26
      test/jiffy_03_number_tests.erl
  9. +72
    -52
      test/jiffy_04_string_tests.erl
  10. +31
    -11
      test/jiffy_05_array_tests.erl
  11. +31
    -11
      test/jiffy_06_object_tests.erl
  12. +31
    -11
      test/jiffy_07_compound_tests.erl
  13. +16
    -0
      test/jiffy_08_halfword_tests.erl
  14. +11
    -8
      test/jiffy_09_reg_issue_24_tests.erl
  15. +34
    -0
      test/jiffy_10_short_double_tests.erl
  16. +26
    -15
      test/jiffy_11_proper_tests.erl
  17. +25
    -0
      test/jiffy_util.hrl
  18. +0
    -44
      test/util.erl

+ 0
- 30
test/001-yajl-tests.t 查看文件

@ -1,30 +0,0 @@
#! /usr/bin/env escript
% This file is part of Jiffy released under the MIT license.
% See the LICENSE file for more information.
main([]) ->
code:add_pathz("test"),
code:add_pathz("ebin"),
Cases = read_cases(),
etap:plan(length(Cases)),
lists:foreach(fun(Case) -> test(Case) end, Cases),
etap:end_tests().
test({Name, Json, {error, _}=Erl}) ->
etap:is((catch jiffy:decode(Json)), Erl, Name);
test({Name, Json, Erl}) ->
etap:is(jiffy:decode(Json), Erl, Name).
read_cases() ->
CasesPath = filename:join(["test", "cases", "*.json"]),
FileNames = lists:sort(filelib:wildcard(CasesPath)),
lists:map(fun(F) -> make_pair(F) end, FileNames).
make_pair(FileName) ->
{ok, Json} = file:read_file(FileName),
{BaseName, _} = lists:splitwith(fun(C) -> C /= $. end, FileName),
ErlFname = BaseName ++ ".eterm",
{ok, [Term]} = file:consult(ErlFname),
{BaseName, Json, Term}.

+ 0
- 21
test/002-literals.t 查看文件

@ -1,21 +0,0 @@
#! /usr/bin/env escript
% This file is part of Jiffy released under the MIT license.
% See the LICENSE file for more information.
main([]) ->
code:add_pathz("ebin"),
code:add_pathz("test"),
etap:plan(6),
etap:is(jiffy:decode(<<"true">>), true, "DEC: true -> true"),
etap:is(jiffy:encode(true), <<"true">>, "ENC: true -> true"),
etap:is(jiffy:decode(<<"false">>), false, "DEC: false -> false"),
etap:is(jiffy:encode(false), <<"false">>, "ENC: false -> false"),
etap:is(jiffy:decode(<<"null">>), null, "DEC: null -> null"),
etap:is(jiffy:encode(null), <<"null">>, "ENC: null -> null"),
etap:end_tests().

+ 0
- 15
test/008-halfword.t 查看文件

@ -1,15 +0,0 @@
#! /usr/bin/env escript
% This file is part of Jiffy released under the MIT license.
% See the LICENSE file for more information.
main([]) ->
code:add_pathz("ebin"),
code:add_pathz("test"),
etap:plan(unknown),
etap:is(jiffy:decode(<<"1">>) =:= 1, true, "1 =:= 1"),
etap:is(jiffy:decode(<<"1">>) == 1, true, "1 == 1"),
etap:end_tests().

+ 0
- 29
test/010-short-doubles.t 查看文件

@ -1,29 +0,0 @@
#! /usr/bin/env escript
% This file is part of Jiffy released under the MIT license.
% See the LICENSE file for more information.
filename() -> "test/cases/short-doubles.txt".
main([]) ->
code:add_pathz("ebin"),
code:add_pathz("test"),
etap:plan(100000),
etap:diag("Loading test cases..."),
{ok, Cases} = file:consult(filename()),
etap:diag("Running tests..."),
ok = run_tests(Cases),
etap:end_tests().
run_tests([]) ->
ok;
run_tests([Double | Rest]) ->
RoundTrip = jiffy:decode(jiffy:encode(Double)),
Desc = lists:flatten(io_lib:format("~e", [Double])),
etap:is(RoundTrip, Double, "Roundtrip: " ++ Desc),
run_tests(Rest).

+ 0
- 612
test/etap.erl 查看文件

@ -1,612 +0,0 @@
%% Copyright (c) 2008-2009 Nick Gerakines <nick@gerakines.net>
%%
%% Permission is hereby granted, free of charge, to any person
%% obtaining a copy of this software and associated documentation
%% files (the "Software"), to deal in the Software without
%% restriction, including without limitation the rights to use,
%% copy, modify, merge, publish, distribute, sublicense, and/or sell
%% copies of the Software, and to permit persons to whom the
%% Software is furnished to do so, subject to the following
%% conditions:
%%
%% The above copyright notice and this permission notice shall be
%% included in all copies or substantial portions of the Software.
%%
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
%% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
%% OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
%% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
%% HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
%% WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
%% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
%% OTHER DEALINGS IN THE SOFTWARE.
%%
%% @author Nick Gerakines <nick@gerakines.net> [http://socklabs.com/]
%% @author Jeremy Wall <jeremy@marzhillstudios.com>
%% @version 0.3.4
%% @copyright 2007-2008 Jeremy Wall, 2008-2009 Nick Gerakines
%% @reference http://testanything.org/wiki/index.php/Main_Page
%% @reference http://en.wikipedia.org/wiki/Test_Anything_Protocol
%% @todo Finish implementing the skip directive.
%% @todo Document the messages handled by this receive loop.
%% @todo Explain in documentation why we use a process to handle test input.
%% @doc etap is a TAP testing module for Erlang components and applications.
%% This module allows developers to test their software using the TAP method.
%%
%% <blockquote cite="http://en.wikipedia.org/wiki/Test_Anything_Protocol"><p>
%% TAP, the Test Anything Protocol, is a simple text-based interface between
%% testing modules in a test harness. TAP started life as part of the test
%% harness for Perl but now has implementations in C/C++, Python, PHP, Perl
%% and probably others by the time you read this.
%% </p></blockquote>
%%
%% The testing process begins by defining a plan using etap:plan/1, running
%% a number of etap tests and then calling eta:end_tests/0. Please refer to
%% the Erlang modules in the t directory of this project for example tests.
-module(etap).
-vsn("0.3.4").
-export([
ensure_test_server/0,
start_etap_server/0,
test_server/1,
msg/1, msg/2,
diag/1, diag/2,
expectation_mismatch_message/3,
plan/1,
end_tests/0,
not_ok/2, ok/2, is_ok/2, is/3, isnt/3, any/3, none/3,
fun_is/3, expect_fun/3, expect_fun/4,
is_greater/3,
skip/1, skip/2,
datetime/1,
skip/3,
bail/0, bail/1,
test_state/0, failure_count/0
]).
-export([
contains_ok/3,
is_before/4
]).
-export([
is_pid/2,
is_alive/2,
is_mfa/3
]).
-export([
loaded_ok/2,
can_ok/2, can_ok/3,
has_attrib/2, is_attrib/3,
is_behaviour/2
]).
-export([
dies_ok/2,
lives_ok/2,
throws_ok/3
]).
-record(test_state, {
planned = 0,
count = 0,
pass = 0,
fail = 0,
skip = 0,
skip_reason = ""
}).
%% @spec plan(N) -> Result
%% N = unknown | skip | {skip, string()} | integer()
%% Result = ok
%% @doc Create a test plan and boot strap the test server.
plan(unknown) ->
ensure_test_server(),
etap_server ! {self(), plan, unknown},
ok;
plan(skip) ->
io:format("1..0 # skip~n");
plan({skip, Reason}) ->
io:format("1..0 # skip ~s~n", [Reason]);
plan(N) when is_integer(N), N > 0 ->
ensure_test_server(),
etap_server ! {self(), plan, N},
ok.
%% @spec end_tests() -> ok
%% @doc End the current test plan and output test results.
%% @todo This should probably be done in the test_server process.
end_tests() ->
case whereis(etap_server) of
undefined -> self() ! true;
_ -> etap_server ! {self(), state}
end,
State = receive X -> X end,
if
State#test_state.planned == -1 ->
io:format("1..~p~n", [State#test_state.count]);
true ->
ok
end,
case whereis(etap_server) of
undefined -> ok;
_ -> etap_server ! done, ok
end.
bail() ->
bail("").
bail(Reason) ->
etap_server ! {self(), diag, "Bail out! " ++ Reason},
etap_server ! done, ok,
ok.
%% @spec test_state() -> Return
%% Return = test_state_record() | {error, string()}
%% @doc Return the current test state
test_state() ->
etap_server ! {self(), state},
receive
X when is_record(X, test_state) -> X
after
1000 -> {error, "Timed out waiting for etap server reply.~n"}
end.
%% @spec failure_count() -> Return
%% Return = integer() | {error, string()}
%% @doc Return the current failure count
failure_count() ->
case test_state() of
#test_state{fail=FailureCount} -> FailureCount;
X -> X
end.
%% @spec msg(S) -> ok
%% S = string()
%% @doc Print a message in the test output.
msg(S) -> etap_server ! {self(), diag, S}, ok.
%% @spec msg(Format, Data) -> ok
%% Format = atom() | string() | binary()
%% Data = [term()]
%% UnicodeList = [Unicode]
%% Unicode = int()
%% @doc Print a message in the test output.
%% Function arguments are passed through io_lib:format/2.
msg(Format, Data) -> msg(io_lib:format(Format, Data)).
%% @spec diag(S) -> ok
%% S = string()
%% @doc Print a debug/status message related to the test suite.
diag(S) -> msg("# " ++ S).
%% @spec diag(Format, Data) -> ok
%% Format = atom() | string() | binary()
%% Data = [term()]
%% UnicodeList = [Unicode]
%% Unicode = int()
%% @doc Print a debug/status message related to the test suite.
%% Function arguments are passed through io_lib:format/2.
diag(Format, Data) -> diag(io_lib:format(Format, Data)).
%% @spec expectation_mismatch_message(Got, Expected, Desc) -> ok
%% Got = any()
%% Expected = any()
%% Desc = string()
%% @doc Print an expectation mismatch message in the test output.
expectation_mismatch_message(Got, Expected, Desc) ->
msg(" ---"),
msg(" description: ~p", [Desc]),
msg(" found: ~p", [Got]),
msg(" wanted: ~p", [Expected]),
msg(" ..."),
ok.
% @spec evaluate(Pass, Got, Expected, Desc) -> Result
%% Pass = true | false
%% Got = any()
%% Expected = any()
%% Desc = string()
%% Result = true | false
%% @doc Evaluate a test statement, printing an expectation mismatch message
%% if the test failed.
evaluate(Pass, Got, Expected, Desc) ->
case mk_tap(Pass, Desc) of
false ->
expectation_mismatch_message(Got, Expected, Desc),
false;
true ->
true
end.
%% @spec ok(Expr, Desc) -> Result
%% Expr = true | false
%% Desc = string()
%% Result = true | false
%% @doc Assert that a statement is true.
ok(Expr, Desc) -> evaluate(Expr == true, Expr, true, Desc).
%% @spec not_ok(Expr, Desc) -> Result
%% Expr = true | false
%% Desc = string()
%% Result = true | false
%% @doc Assert that a statement is false.
not_ok(Expr, Desc) -> evaluate(Expr == false, Expr, false, Desc).
%% @spec is_ok(Expr, Desc) -> Result
%% Expr = any()
%% Desc = string()
%% Result = true | false
%% @doc Assert that two values are the same.
is_ok(Expr, Desc) -> evaluate(Expr == ok, Expr, ok, Desc).
%% @spec is(Got, Expected, Desc) -> Result
%% Got = any()
%% Expected = any()
%% Desc = string()
%% Result = true | false
%% @doc Assert that two values are the same.
is(Got, Expected, Desc) -> evaluate(Got == Expected, Got, Expected, Desc).
%% @spec isnt(Got, Expected, Desc) -> Result
%% Got = any()
%% Expected = any()
%% Desc = string()
%% Result = true | false
%% @doc Assert that two values are not the same.
isnt(Got, Expected, Desc) -> evaluate(Got /= Expected, Got, Expected, Desc).
%% @spec is_greater(ValueA, ValueB, Desc) -> Result
%% ValueA = number()
%% ValueB = number()
%% Desc = string()
%% Result = true | false
%% @doc Assert that an integer is greater than another.
is_greater(ValueA, ValueB, Desc) when is_integer(ValueA), is_integer(ValueB) ->
mk_tap(ValueA > ValueB, Desc).
%% @spec any(Got, Items, Desc) -> Result
%% Got = any()
%% Items = [any()]
%% Desc = string()
%% Result = true | false
%% @doc Assert that an item is in a list.
any(Got, Items, Desc) when is_function(Got) ->
is(lists:any(Got, Items), true, Desc);
any(Got, Items, Desc) ->
is(lists:member(Got, Items), true, Desc).
%% @spec none(Got, Items, Desc) -> Result
%% Got = any()
%% Items = [any()]
%% Desc = string()
%% Result = true | false
%% @doc Assert that an item is not in a list.
none(Got, Items, Desc) when is_function(Got) ->
is(lists:any(Got, Items), false, Desc);
none(Got, Items, Desc) ->
is(lists:member(Got, Items), false, Desc).
%% @spec fun_is(Fun, Expected, Desc) -> Result
%% Fun = function()
%% Expected = any()
%% Desc = string()
%% Result = true | false
%% @doc Use an anonymous function to assert a pattern match.
fun_is(Fun, Expected, Desc) when is_function(Fun) ->
is(Fun(Expected), true, Desc).
%% @spec expect_fun(ExpectFun, Got, Desc) -> Result
%% ExpectFun = function()
%% Got = any()
%% Desc = string()
%% Result = true | false
%% @doc Use an anonymous function to assert a pattern match, using actual
%% value as the argument to the function.
expect_fun(ExpectFun, Got, Desc) ->
evaluate(ExpectFun(Got), Got, ExpectFun, Desc).
%% @spec expect_fun(ExpectFun, Got, Desc, ExpectStr) -> Result
%% ExpectFun = function()
%% Got = any()
%% Desc = string()
%% ExpectStr = string()
%% Result = true | false
%% @doc Use an anonymous function to assert a pattern match, using actual
%% value as the argument to the function.
expect_fun(ExpectFun, Got, Desc, ExpectStr) ->
evaluate(ExpectFun(Got), Got, ExpectStr, Desc).
%% @equiv skip(TestFun, "")
skip(TestFun) when is_function(TestFun) ->
skip(TestFun, "").
%% @spec skip(TestFun, Reason) -> ok
%% TestFun = function()
%% Reason = string()
%% @doc Skip a test.
skip(TestFun, Reason) when is_function(TestFun), is_list(Reason) ->
begin_skip(Reason),
catch TestFun(),
end_skip(),
ok.
%% @spec skip(Q, TestFun, Reason) -> ok
%% Q = true | false | function()
%% TestFun = function()
%% Reason = string()
%% @doc Skips a test conditionally. The first argument to this function can
%% either be the 'true' or 'false' atoms or a function that returns 'true' or
%% 'false'.
skip(QFun, TestFun, Reason) when is_function(QFun), is_function(TestFun), is_list(Reason) ->
case QFun() of
true -> begin_skip(Reason), TestFun(), end_skip();
_ -> TestFun()
end,
ok;
skip(Q, TestFun, Reason) when is_function(TestFun), is_list(Reason), Q == true ->
begin_skip(Reason),
TestFun(),
end_skip(),
ok;
skip(_, TestFun, Reason) when is_function(TestFun), is_list(Reason) ->
TestFun(),
ok.
%% @private
begin_skip(Reason) ->
etap_server ! {self(), begin_skip, Reason}.
%% @private
end_skip() ->
etap_server ! {self(), end_skip}.
%% @spec contains_ok(string(), string(), string()) -> true | false
%% @doc Assert that a string is contained in another string.
contains_ok(Source, String, Desc) ->
etap:isnt(
string:str(Source, String),
0,
Desc
).
%% @spec is_before(string(), string(), string(), string()) -> true | false
%% @doc Assert that a string comes before another string within a larger body.
is_before(Source, StringA, StringB, Desc) ->
etap:is_greater(
string:str(Source, StringB),
string:str(Source, StringA),
Desc
).
%% @doc Assert that a given variable is a pid.
is_pid(Pid, Desc) when is_pid(Pid) -> etap:ok(true, Desc);
is_pid(_, Desc) -> etap:ok(false, Desc).
%% @doc Assert that a given process/pid is alive.
is_alive(Pid, Desc) ->
etap:ok(erlang:is_process_alive(Pid), Desc).
%% @doc Assert that the current function of a pid is a given {M, F, A} tuple.
is_mfa(Pid, MFA, Desc) ->
etap:is({current_function, MFA}, erlang:process_info(Pid, current_function), Desc).
%% @spec loaded_ok(atom(), string()) -> true | false
%% @doc Assert that a module has been loaded successfully.
loaded_ok(M, Desc) when is_atom(M) ->
etap:fun_is(fun({module, _}) -> true; (_) -> false end, code:load_file(M), Desc).
%% @spec can_ok(atom(), atom()) -> true | false
%% @doc Assert that a module exports a given function.
can_ok(M, F) when is_atom(M), is_atom(F) ->
Matches = [X || {X, _} <- M:module_info(exports), X == F],
etap:ok(Matches > 0, lists:concat([M, " can ", F])).
%% @spec can_ok(atom(), atom(), integer()) -> true | false
%% @doc Assert that a module exports a given function with a given arity.
can_ok(M, F, A) when is_atom(M); is_atom(F), is_number(A) ->
Matches = [X || X <- M:module_info(exports), X == {F, A}],
etap:ok(Matches > 0, lists:concat([M, " can ", F, "/", A])).
%% @spec has_attrib(M, A) -> true | false
%% M = atom()
%% A = atom()
%% @doc Asserts that a module has a given attribute.
has_attrib(M, A) when is_atom(M), is_atom(A) ->
etap:isnt(
proplists:get_value(A, M:module_info(attributes), 'asdlkjasdlkads'),
'asdlkjasdlkads',
lists:concat([M, " has attribute ", A])
).
%% @spec has_attrib(M, A. V) -> true | false
%% M = atom()
%% A = atom()
%% V = any()
%% @doc Asserts that a module has a given attribute with a given value.
is_attrib(M, A, V) when is_atom(M) andalso is_atom(A) ->
etap:is(
proplists:get_value(A, M:module_info(attributes)),
[V],
lists:concat([M, "'s ", A, " is ", V])
).
%% @spec is_behavior(M, B) -> true | false
%% M = atom()
%% B = atom()
%% @doc Asserts that a given module has a specific behavior.
is_behaviour(M, B) when is_atom(M) andalso is_atom(B) ->
is_attrib(M, behaviour, B).
%% @doc Assert that an exception is raised when running a given function.
dies_ok(F, Desc) ->
case (catch F()) of
{'EXIT', _} -> etap:ok(true, Desc);
_ -> etap:ok(false, Desc)
end.
%% @doc Assert that an exception is not raised when running a given function.
lives_ok(F, Desc) ->
etap:is(try_this(F), success, Desc).
%% @doc Assert that the exception thrown by a function matches the given exception.
throws_ok(F, Exception, Desc) ->
try F() of
_ -> etap:ok(nok, Desc)
catch
_:E ->
etap:is(E, Exception, Desc)
end.
%% @private
%% @doc Run a function and catch any exceptions.
try_this(F) when is_function(F, 0) ->
try F() of
_ -> success
catch
throw:E -> {throw, E};
error:E -> {error, E};
exit:E -> {exit, E}
end.
%% @private
%% @doc Start the etap_server process if it is not running already.
ensure_test_server() ->
case whereis(etap_server) of
undefined ->
proc_lib:start(?MODULE, start_etap_server,[]);
_ ->
diag("The test server is already running.")
end.
%% @private
%% @doc Start the etap_server loop and register itself as the etap_server
%% process.
start_etap_server() ->
catch register(etap_server, self()),
proc_lib:init_ack(ok),
etap:test_server(#test_state{
planned = 0,
count = 0,
pass = 0,
fail = 0,
skip = 0,
skip_reason = ""
}).
%% @private
%% @doc The main etap_server receive/run loop. The etap_server receive loop
%% responds to seven messages apperatining to failure or passing of tests.
%% It is also used to initiate the testing process with the {_, plan, _}
%% message that clears the current test state.
test_server(State) ->
NewState = receive
{_From, plan, unknown} ->
io:format("# Current time local ~s~n", [datetime(erlang:localtime())]),
io:format("# Using etap version ~p~n", [ proplists:get_value(vsn, proplists:get_value(attributes, etap:module_info())) ]),
State#test_state{
planned = -1,
count = 0,
pass = 0,
fail = 0,
skip = 0,
skip_reason = ""
};
{_From, plan, N} ->
io:format("# Current time local ~s~n", [datetime(erlang:localtime())]),
io:format("# Using etap version ~p~n", [ proplists:get_value(vsn, proplists:get_value(attributes, etap:module_info())) ]),
io:format("1..~p~n", [N]),
State#test_state{
planned = N,
count = 0,
pass = 0,
fail = 0,
skip = 0,
skip_reason = ""
};
{_From, begin_skip, Reason} ->
State#test_state{
skip = 1,
skip_reason = Reason
};
{_From, end_skip} ->
State#test_state{
skip = 0,
skip_reason = ""
};
{_From, pass, Desc} ->
FullMessage = skip_diag(
" - " ++ Desc,
State#test_state.skip,
State#test_state.skip_reason
),
io:format("ok ~p ~s~n", [State#test_state.count + 1, FullMessage]),
State#test_state{
count = State#test_state.count + 1,
pass = State#test_state.pass + 1
};
{_From, fail, Desc} ->
FullMessage = skip_diag(
" - " ++ Desc,
State#test_state.skip,
State#test_state.skip_reason
),
io:format("not ok ~p ~s~n", [State#test_state.count + 1, FullMessage]),
State#test_state{
count = State#test_state.count + 1,
fail = State#test_state.fail + 1
};
{From, state} ->
From ! State,
State;
{_From, diag, Message} ->
io:format("~s~n", [Message]),
State;
{From, count} ->
From ! State#test_state.count,
State;
{From, is_skip} ->
From ! State#test_state.skip,
State;
done ->
exit(normal)
end,
test_server(NewState).
%% @private
%% @doc Process the result of a test and send it to the etap_server process.
mk_tap(Result, Desc) ->
IsSkip = lib:sendw(etap_server, is_skip),
case [IsSkip, Result] of
[_, true] ->
etap_server ! {self(), pass, Desc},
true;
[1, _] ->
etap_server ! {self(), pass, Desc},
true;
_ ->
etap_server ! {self(), fail, Desc},
false
end.
%% @private
%% @doc Format a date/time string.
datetime(DateTime) ->
{{Year, Month, Day}, {Hour, Min, Sec}} = DateTime,
io_lib:format("~4.10.0B-~2.10.0B-~2.10.0B ~2.10.0B:~2.10.0B:~2.10.0B", [Year, Month, Day, Hour, Min, Sec]).
%% @private
%% @doc Craft an output message taking skip/todo into consideration.
skip_diag(Message, 0, _) ->
Message;
skip_diag(_Message, 1, "") ->
" # SKIP";
skip_diag(_Message, 1, Reason) ->
" # SKIP : " ++ Reason.

+ 33
- 0
test/jiffy_01_yajl_tests.erl 查看文件

@ -0,0 +1,33 @@
% This file is part of Jiffy released under the MIT license.
% See the LICENSE file for more information.
-module(jiffy_01_yajl_tests).
-include_lib("proper/include/proper.hrl").
-include_lib("eunit/include/eunit.hrl").
yajl_test_() ->
Cases = read_cases(),
[gen(Case) || Case <- Cases].
gen({Name, Json, {error, _}=Erl}) ->
{Name, ?_assertThrow(Erl, jiffy:decode(Json))};
gen({Name, Json, Erl}) ->
{Name, ?_assertEqual(Erl, jiffy:decode(Json))}.
read_cases() ->
CasesPath = filename:join(["..", "test", "cases", "*.json"]),
FileNames = lists:sort(filelib:wildcard(CasesPath)),
lists:map(fun(F) -> make_pair(F) end, FileNames).
make_pair(FileName) ->
{ok, Json} = file:read_file(FileName),
BaseName = filename:rootname(FileName),
ErlFname = BaseName ++ ".eterm",
{ok, [Term]} = file:consult(ErlFname),
{filename:basename(BaseName), Json, Term}.

+ 28
- 0
test/jiffy_02_literal_tests.erl 查看文件

@ -0,0 +1,28 @@
% This file is part of Jiffy released under the MIT license.
% See the LICENSE file for more information.
-module(jiffy_02_literal_tests).
-include_lib("proper/include/proper.hrl").
-include_lib("eunit/include/eunit.hrl").
true_test_() ->
{"true", [
{"Decode", ?_assertEqual(true, jiffy:decode(<<"true">>))},
{"Encode", ?_assertEqual(<<"true">>, jiffy:encode(true))}
]}.
false_test_() ->
{"false", [
{"Decode", ?_assertEqual(false, jiffy:decode(<<"false">>))},
{"Encode", ?_assertEqual(<<"false">>, jiffy:encode(false))}
]}.
null_test_() ->
{"null", [
{"Decode", ?_assertEqual(null, jiffy:decode(<<"null">>))},
{"Encode", ?_assertEqual(<<"null">>, jiffy:encode(null))}
]}.

test/003-numbers.t → test/jiffy_03_number_tests.erl 查看文件

@ -1,28 +1,48 @@
#! /usr/bin/env escript
% This file is part of Jiffy released under the MIT license. % This file is part of Jiffy released under the MIT license.
% See the LICENSE file for more information. % See the LICENSE file for more information.
main([]) ->
code:add_pathz("ebin"),
code:add_pathz("test"),
-module(jiffy_03_number_tests).
etap:plan(59 + 2 * length(double_conversion_tests())),
util:test_good(good()),
util:test_errors(errors()),
run_double_conversion_tests(),
etap:end_tests().
-include_lib("proper/include/proper.hrl").
-include_lib("eunit/include/eunit.hrl").
-include("jiffy_util.hrl").
run_double_conversion_tests() ->
lists:foreach(fun(Double) ->
Descr = io_lib:format("~f", [Double]),
etap:is(jiffy:decode(jiffy:encode(Double)), Double, Descr),
NegDouble = -1.0 * Double,
NegDescr = io_lib:format("~f", [NegDouble]),
etap:is(jiffy:decode(jiffy:encode(NegDouble)), NegDouble, NegDescr)
end, double_conversion_tests()).
good() ->
number_success_test_() ->
[gen(ok, Case) || Case <- cases(ok)].
number_failure_test_() ->
[gen(error, Case) || Case <- cases(error)].
number_double_test_() ->
[gen(floats, Case) || Case <- cases(floats)].
gen(ok, {J, E}) ->
gen(ok, {J, E, J});
gen(ok, {J1, E, J2}) ->
{msg("~s", [J1]), [
{"Decode", ?_assertEqual(E, dec(J1))},
{"Encode", ?_assertEqual(J2, enc(E))}
]};
gen(error, J) ->
{msg("Error: ~s", [J]), [
?_assertThrow({error, _}, dec(J))
]};
gen(floats, F) ->
NegF = -1.0 * F,
{msg("float round trip - ~p", [F]), [
{"Pos", ?_assertEqual(F, dec(enc(F)))},
{"Neg", ?_assertEqual(NegF, dec(enc(NegF)))}
]}.
cases(ok) ->
[ [
{<<"0">>, 0}, {<<"0">>, 0},
{<<"-0">>, 0, <<"0">>}, {<<"-0">>, 0, <<"0">>},
@ -64,10 +84,9 @@ good() ->
{<<"1.5E3">>, 1500.0, <<"1500.0">>}, {<<"1.5E3">>, 1500.0, <<"1500.0">>},
{<<"2.5E-1">>, 0.25, <<"0.25">>}, {<<"2.5E-1">>, 0.25, <<"0.25">>},
{<<"-0.325E+2">>, -32.5, <<"-32.5">>} {<<"-0.325E+2">>, -32.5, <<"-32.5">>}
].
];
errors() ->
cases(error) ->
[ [
<<"02">>, <<"02">>,
<<"-01">>, <<"-01">>,
@ -80,10 +99,9 @@ errors() ->
<<"1-E2">>, <<"1-E2">>,
<<"2E +3">>, <<"2E +3">>,
<<"1EA">> <<"1EA">>
].
];
double_conversion_tests() ->
cases(floats) ->
[ [
0.0, 0.0,
0.00000001, 0.00000001,
@ -114,5 +132,3 @@ double_conversion_tests() ->
1111111111111111111111.0, 1111111111111111111111.0,
11111111111111111111111.0 11111111111111111111111.0
]. ].

test/004-strings.t → test/jiffy_04_string_tests.erl 查看文件

@ -1,21 +1,60 @@
#! /usr/bin/env escript
% This file is part of Jiffy released under the MIT license. % This file is part of Jiffy released under the MIT license.
% See the LICENSE file for more information. % See the LICENSE file for more information.
main([]) ->
code:add_pathz("ebin"),
code:add_pathz("test"),
-module(jiffy_04_string_tests).
etap:plan(115),
util:test_good(good()),
util:test_good(uescaped(), [uescape]),
util:test_errors(errors()),
test_utf8(utf8_cases()),
-include_lib("proper/include/proper.hrl").
-include_lib("eunit/include/eunit.hrl").
-include("jiffy_util.hrl").
etap:end_tests().
good() ->
string_success_test_() ->
[gen(ok, Case) || Case <- cases(ok)].
string_uescaped_test_() ->
[gen(uescaped, Case) || Case <- cases(uescaped)].
string_error_test_() ->
[gen(error, Case) || Case <- cases(error)].
string_utf8_test_() ->
[gen(utf8, Case) || Case <- cases(utf8)].
gen(ok, {J, E}) ->
gen(ok, {J, E, J});
gen(ok, {J1, E, J2}) ->
{msg("ok - ~s", [J1]), [
{"Decode", ?_assertEqual(E, dec(J1))},
{"Encode", ?_assertEqual(J2, enc(E))}
]};
gen(uescaped, {J, E}) ->
{msg("uescape - ~s", [J]), [
{"Decode", ?_assertEqual(E, dec(J))},
{"Encode", ?_assertEqual(J, enc(E, [uescape]))}
]};
gen(error, J) ->
{msg("error - ~s", [J]), [
?_assertThrow({error, _}, dec(J))
]};
gen(utf8, {Case, Fixed}) ->
Case2 = <<34, Case/binary, 34>>,
Fixed2 = <<34, Fixed/binary, 34>>,
{msg("UTF-8: ~s", [hex(Case)]), [
?_assertThrow({error, invalid_string}, jiffy:encode(Case)),
?_assertEqual(Fixed2, jiffy:encode(Case, [force_utf8])),
?_assertThrow({error, {_, invalid_string}}, jiffy:decode(Case2))
]}.
cases(ok) ->
[ [
{<<"\"\"">>, <<"">>}, {<<"\"\"">>, <<"">>},
{<<"\"/\"">>, <<"/">>}, {<<"\"/\"">>, <<"/">>},
@ -32,9 +71,9 @@ good() ->
}, },
{<<"\"\\uFFFF\"">>, <<239,191,191>>, <<34,239,191,191,34>>}, {<<"\"\\uFFFF\"">>, <<239,191,191>>, <<34,239,191,191,34>>},
{<<"\"\\uFFFE\"">>, <<239,191,190>>, <<34,239,191,190,34>>} {<<"\"\\uFFFE\"">>, <<239,191,190>>, <<34,239,191,190,34>>}
].
];
uescaped() ->
cases(uescaped) ->
[ [
{ {
<<"\"\\u8CA8\\u5481\\u3002\\u0091\\u0091\"">>, <<"\"\\u8CA8\\u5481\\u3002\\u0091\\u0091\"">>,
@ -48,9 +87,9 @@ uescaped() ->
<<"\"\\uD83D\\uDE0A\"">>, <<"\"\\uD83D\\uDE0A\"">>,
<<240, 159, 152, 138>> <<240, 159, 152, 138>>
} }
].
];
errors() ->
cases(error) ->
[ [
"\"", "\"",
<<"\"foo">>, <<"\"foo">>,
@ -59,30 +98,9 @@ errors() ->
<<"\"\\uD834foo\\uDD1E\"">>, <<"\"\\uD834foo\\uDD1E\"">>,
% CouchDB-345 % CouchDB-345
<<34,78,69,73,77,69,78,32,70,216,82,82,32,70,65,69,78,33,34>> <<34,78,69,73,77,69,78,32,70,216,82,82,32,70,65,69,78,33,34>>
].
];
test_utf8([]) ->
ok;
test_utf8([{Case, Fixed} | Rest]) ->
etap:fun_is(
fun({error, invalid_string}) -> true; (Else) -> Else end,
(catch jiffy:encode(Case)),
lists:flatten(io_lib:format("Invalid utf-8: ~p", [Case]))
),
etap:fun_is(
fun(Fixed) -> true; (Else) -> Else end,
jiffy:encode(Case, [force_utf8]),
lists:flatten(io_lib:format("Fixed correctly: ~p", [Fixed]))
),
Case2 = <<34, Case/binary, 34>>,
etap:fun_is(
fun({error, {_, invalid_string}}) -> true; (Else) -> Else end,
(catch jiffy:decode(Case2)),
lists:flatten(io_lib:format("Invalid utf-8: ~p", [Case2]))
),
test_utf8(Rest).
utf8_cases() ->
cases(utf8) ->
[ [
% Stray continuation byte % Stray continuation byte
{<<16#C2, 16#81, 16#80>>, <<16#C2, 16#81, 16#EF, 16#BF, 16#BD>>}, {<<16#C2, 16#81, 16#80>>, <<16#C2, 16#81, 16#EF, 16#BF, 16#BD>>},
@ -110,18 +128,20 @@ utf8_cases() ->
{<<16#FC, 16#80, 16#80, 16#80, 16#80>>, <<16#EF, 16#BF, 16#BD>>}, {<<16#FC, 16#80, 16#80, 16#80, 16#80>>, <<16#EF, 16#BF, 16#BD>>},
% No data in high bits. % No data in high bits.
{<<16#C0, 16#80>>, <<"\"\\u0000\"">>},
{<<16#C1, 16#80>>, <<"\"\\u0000\"">>},
{<<16#E0, 16#80, 16#80>>, <<"\"\\u0000\"">>},
{<<16#E0, 16#90, 16#80>>, <<"\"\\u0000\"">>},
{<<16#F0, 16#80, 16#80, 16#80>>, <<"\"\\u0000\"">>},
{<<16#F0, 16#88, 16#80, 16#80>>, <<"\"\\u0000\"">>},
{<<16#F8, 16#80, 16#80, 16#80, 16#80>>, <<"\"\\u0000\"">>},
{<<16#F8, 16#84, 16#80, 16#80, 16#80>>, <<"\"\\u0000\"">>},
{<<16#FC, 16#80, 16#80, 16#80, 16#80, 16#80>>, <<"\"\\u0000\"">>},
{<<16#FC, 16#82, 16#80, 16#80, 16#80, 16#80>>, <<"\"\\u0000\"">>}
{<<16#C0, 16#80>>, <<"\\u0000">>},
{<<16#C1, 16#80>>, <<"@">>},
{<<16#E0, 16#80, 16#80>>, <<"\\u0000">>},
{<<16#E0, 16#90, 16#80>>, <<16#D0, 16#80>>},
{<<16#F0, 16#80, 16#80, 16#80>>, <<"\\u0000">>},
{<<16#F0, 16#88, 16#80, 16#80>>, <<16#E8, 16#80, 16#80>>},
% UTF-8-like sequenecs of greater than 4 bytes
% aren't valid and are replaced with a single
% replacement 0xFFFD character.
{<<16#F8, 16#80, 16#80, 16#80, 16#80>>, <<16#EF, 16#BF, 16#BD>>},
{<<16#F8, 16#84, 16#80, 16#80, 16#80>>, <<16#EF, 16#BF, 16#BD>>},
{<<16#FC, 16#80, 16#80, 16#80, 16#80, 16#80>>, <<16#EF, 16#BF, 16#BD>>},
{<<16#FC, 16#82, 16#80, 16#80, 16#80, 16#80>>, <<16#EF, 16#BF, 16#BD>>}
]. ].

test/005-arrays.t → test/jiffy_05_array_tests.erl 查看文件

@ -1,17 +1,37 @@
#! /usr/bin/env escript
% This file is part of Jiffy released under the MIT license. % This file is part of Jiffy released under the MIT license.
% See the LICENSE file for more information. % See the LICENSE file for more information.
main([]) ->
code:add_pathz("ebin"),
code:add_pathz("test"),
-module(jiffy_05_array_tests).
etap:plan(18),
util:test_good(good()),
util:test_errors(errors()),
etap:end_tests().
good() ->
-include_lib("proper/include/proper.hrl").
-include_lib("eunit/include/eunit.hrl").
-include("jiffy_util.hrl").
array_success_test_() ->
[gen(ok, Case) || Case <- cases(ok)].
array_failure_test_() ->
[gen(error, Case) || Case <- cases(error)].
gen(ok, {J, E}) ->
gen(ok, {J, E, J});
gen(ok, {J1, E, J2}) ->
{msg("~s", [J1]), [
{"Decode", ?_assertEqual(E, dec(J1))},
{"Encode", ?_assertEqual(J2, enc(E))}
]};
gen(error, J) ->
{msg("Error: ~s", [J]), [
?_assertThrow({error, _}, dec(J))
]}.
cases(ok) ->
[ [
{<<"[]">>, []}, {<<"[]">>, []},
{<<"[\t[\n]\r]">>, [[]], <<"[[]]">>}, {<<"[\t[\n]\r]">>, [[]], <<"[[]]">>},
@ -23,9 +43,9 @@ good() ->
[<<194, 161>>, <<195, 188>>], [<<194, 161>>, <<195, 188>>],
<<"[\"", 194, 161, "\",\"", 195, 188, "\"]">> <<"[\"", 194, 161, "\",\"", 195, 188, "\"]">>
} }
].
];
errors() ->
cases(error) ->
[ [
<<"[">>, <<"[">>,
<<"]">>, <<"]">>,

test/006-maps.t → test/jiffy_06_object_tests.erl 查看文件

@ -1,17 +1,37 @@
#! /usr/bin/env escript
% This file is part of Jiffy released under the MIT license. % This file is part of Jiffy released under the MIT license.
% See the LICENSE file for more information. % See the LICENSE file for more information.
main([]) ->
code:add_pathz("ebin"),
code:add_pathz("test"),
-module(jiffy_06_object_tests).
etap:plan(15),
util:test_good(good()),
util:test_errors(errors()),
etap:end_tests().
good() ->
-include_lib("proper/include/proper.hrl").
-include_lib("eunit/include/eunit.hrl").
-include("jiffy_util.hrl").
object_success_test_() ->
[gen(ok, Case) || Case <- cases(ok)].
object_failure_test_() ->
[gen(error, Case) || Case <- cases(error)].
gen(ok, {J, E}) ->
gen(ok, {J, E, J});
gen(ok, {J1, E, J2}) ->
{msg("~s", [J1]), [
{"Decode", ?_assertEqual(E, dec(J1))},
{"Encode", ?_assertEqual(J2, enc(E))}
]};
gen(error, J) ->
{msg("Error: ~s", [J]), [
?_assertThrow({error, _}, dec(J))
]}.
cases(ok) ->
[ [
{<<"{}">>, {[]}}, {<<"{}">>, {[]}},
{<<"{\"foo\": \"bar\"}">>, {<<"{\"foo\": \"bar\"}">>,
@ -20,9 +40,9 @@ good() ->
{<<"\n\n{\"foo\":\r \"bar\",\n \"baz\"\t: 123 }">>, {<<"\n\n{\"foo\":\r \"bar\",\n \"baz\"\t: 123 }">>,
{[{<<"foo">>, <<"bar">>}, {<<"baz">>, 123}]}, {[{<<"foo">>, <<"bar">>}, {<<"baz">>, 123}]},
<<"{\"foo\":\"bar\",\"baz\":123}">>} <<"{\"foo\":\"bar\",\"baz\":123}">>}
].
];
errors() ->
cases(error) ->
[ [
<<"{">>, <<"{">>,
<<"{,}">>, <<"{,}">>,

test/007-compound.t → test/jiffy_07_compound_tests.erl 查看文件

@ -1,17 +1,37 @@
#! /usr/bin/env escript
% This file is part of Jiffy released under the MIT license. % This file is part of Jiffy released under the MIT license.
% See the LICENSE file for more information. % See the LICENSE file for more information.
main([]) ->
code:add_pathz("ebin"),
code:add_pathz("test"),
-module(jiffy_07_compound_tests).
etap:plan(12),
util:test_good(good()),
util:test_errors(errors()),
etap:end_tests().
good() ->
-include_lib("proper/include/proper.hrl").
-include_lib("eunit/include/eunit.hrl").
-include("jiffy_util.hrl").
compound_success_test_() ->
[gen(ok, Case) || Case <- cases(ok)].
compound_failure_test_() ->
[gen(error, Case) || Case <- cases(error)].
gen(ok, {J, E}) ->
gen(ok, {J, E, J});
gen(ok, {J1, E, J2}) ->
{msg("~s", [J1]), [
{"Decode", ?_assertEqual(E, dec(J1))},
{"Encode", ?_assertEqual(J2, enc(E))}
]};
gen(error, J) ->
{msg("Error: ~s", [J]), [
?_assertThrow({error, _}, dec(J))
]}.
cases(ok) ->
[ [
{<<"[{}]">>, [{[]}]}, {<<"[{}]">>, [{[]}]},
{<<"{\"foo\":[123]}">>, {[{<<"foo">>, [123]}]}}, {<<"{\"foo\":[123]}">>, {[{<<"foo">>, [123]}]}},
@ -32,9 +52,9 @@ good() ->
null null
] ]
} }
].
];
errors() ->
cases(error) ->
[ [
<<"[{}">>, <<"[{}">>,
<<"}]">> <<"}]">>

+ 16
- 0
test/jiffy_08_halfword_tests.erl 查看文件

@ -0,0 +1,16 @@
% This file is part of Jiffy released under the MIT license.
% See the LICENSE file for more information.
-module(jiffy_08_halfword_tests).
-include_lib("proper/include/proper.hrl").
-include_lib("eunit/include/eunit.hrl").
-include("jiffy_util.hrl").
numerical_identity_test_() ->
[
{"1 == 1", ?_assert(jiffy:decode(<<"1">>) == 1)},
{"1 =:= 1", ?_assert(jiffy:decode(<<"1">>) =:= 1)}
].

test/009-reg-issue-24.t → test/jiffy_09_reg_issue_24_tests.erl 查看文件

@ -1,15 +1,18 @@
#! /usr/bin/env escript
% This file is part of Jiffy released under the MIT license. % This file is part of Jiffy released under the MIT license.
% See the LICENSE file for more information. % See the LICENSE file for more information.
main([]) ->
code:add_pathz("ebin"),
code:add_pathz("test"),
-module(jiffy_09_reg_issue_24_tests).
etap:plan(1),
jiffy:encode(big_doc(), [uescape]),
etap:is(1, 1, "No segfault encoding large doc with uescapes."),
etap:end_tests().
-include_lib("proper/include/proper.hrl").
-include_lib("eunit/include/eunit.hrl").
-include("jiffy_util.hrl").
no_segfault_test_() ->
{"no segfault", [
?_assert(begin jiffy:encode(big_doc(), [uescape]), true end)
]}.
big_doc() -> big_doc() ->

+ 34
- 0
test/jiffy_10_short_double_tests.erl 查看文件

@ -0,0 +1,34 @@
% This file is part of Jiffy released under the MIT license.
% See the LICENSE file for more information.
-module(jiffy_10_short_double_tests).
-include_lib("proper/include/proper.hrl").
-include_lib("eunit/include/eunit.hrl").
-include("jiffy_util.hrl").
filename() -> "../test/cases/short-doubles.txt".
short_double_test() ->
{ok, Fd} = file:open(filename(), [read, binary, raw]),
{"all doubles round trip", ?assertEqual(0, run(Fd, 0))}.
run(Fd, Acc) ->
case file:read_line(Fd) of
{ok, Data} ->
V1 = re:replace(iolist_to_binary(Data), <<"\.\n">>, <<"">>),
V2 = iolist_to_binary(V1),
V3 = <<34, V2/binary, 34>>,
R = jiffy:encode(jiffy:decode(V3)),
case R == V3 of
true -> run(Fd, Acc);
false -> run(Fd, Acc + 1)
end;
eof ->
Acc
end.

test/jiffy_tests.erl → test/jiffy_11_proper_tests.erl 查看文件

@ -1,24 +1,35 @@
% This file is part of Jiffy released under the MIT license. % This file is part of Jiffy released under the MIT license.
% See the LICENSE file for more information. % See the LICENSE file for more information.
-module(jiffy_tests).
-module(jiffy_11_proper_tests).
-ifdef(JIFFY_DEV). -ifdef(JIFFY_DEV).
-include_lib("proper/include/proper.hrl"). -include_lib("proper/include/proper.hrl").
-include_lib("eunit/include/eunit.hrl"). -include_lib("eunit/include/eunit.hrl").
-include("jiffy_util.hrl").
proper_test_() ->
PropErOpts = [
{to_file, user},
opts() ->
[
{max_size, 15}, {max_size, 15},
{numtests, 1000} {numtests, 1000}
],
{timeout, 3600, ?_assertEqual([], proper:module(jiffy_tests, PropErOpts))}.
prop_encode_decode() ->
].
run(Name) ->
{msg("~s", [Name]), [
{timeout, 3600, ?_assert(proper:quickcheck(?MODULE:Name(), opts()))}
]}.
proper_encode_decode_test_() ->
{timeout, 3600, [
run(prop_enc_dec),
run(prop_enc_dec_pretty),
run(prop_enc_no_crash),
run(prop_dec_no_crash_bin),
run(prop_dec_no_crash_any)
]}.
prop_enc_dec() ->
?FORALL(Data, json(), ?FORALL(Data, json(),
begin begin
%io:format(standard_error, "Data: ~p~n", [Data]), %io:format(standard_error, "Data: ~p~n", [Data]),
@ -35,7 +46,7 @@ to_map_ejson(Vals) when is_list(Vals) ->
to_map_ejson(Val) -> to_map_ejson(Val) ->
Val. Val.
prop_map_encode_decode() ->
prop_map_enc_dec() ->
?FORALL(Data, json(), ?FORALL(Data, json(),
begin begin
MapData = to_map_ejson(Data), MapData = to_map_ejson(Data),
@ -44,20 +55,20 @@ prop_map_encode_decode() ->
). ).
-endif. -endif.
prop_encode_decode_pretty() ->
prop_enc_dec_pretty() ->
?FORALL(Data, json(), ?FORALL(Data, json(),
begin begin
Data == jiffy:decode(jiffy:encode(Data, [pretty])) Data == jiffy:decode(jiffy:encode(Data, [pretty]))
end end
). ).
prop_encode_not_crash() ->
prop_enc_no_crash() ->
?FORALL(Data, any(), begin catch jiffy:encode(Data), true end). ?FORALL(Data, any(), begin catch jiffy:encode(Data), true end).
prop_decode_not_crash_bin() ->
prop_dec_no_crash_bin() ->
?FORALL(Data, binary(), begin catch jiffy:decode(Data), true end). ?FORALL(Data, binary(), begin catch jiffy:decode(Data), true end).
prop_decode_not_crash_any() ->
prop_dec_no_crash_any() ->
?FORALL(Data, any(), begin catch jiffy:decode(Data), true end). ?FORALL(Data, any(), begin catch jiffy:decode(Data), true end).

+ 25
- 0
test/jiffy_util.hrl 查看文件

@ -0,0 +1,25 @@
msg(Fmt, Args) ->
M1 = io_lib:format(Fmt, Args),
M2 = re:replace(M1, <<"\r">>, <<"\\\\r">>, [global]),
M3 = re:replace(M2, <<"\n">>, <<"\\\\n">>, [global]),
M4 = re:replace(M3, <<"\t">>, <<"\\\\t">>, [global]),
iolist_to_binary(M4).
hex(Bin) when is_binary(Bin) ->
H1 = [io_lib:format("16#~2.16.0B",[X]) || <<X:8>> <= Bin],
H2 = string:join(H1, ", "),
lists:flatten(io_lib:format("<<~s>>", [lists:flatten(H2)])).
dec(V) ->
jiffy:decode(V).
enc(V) ->
iolist_to_binary(jiffy:encode(V)).
enc(V, Opts) ->
iolist_to_binary(jiffy:encode(V, Opts)).

+ 0
- 44
test/util.erl 查看文件

@ -1,44 +0,0 @@
-module(util).
-export([test_good/1, test_good/2, test_errors/1]).
test_good(Cases) ->
test_good(Cases, []).
test_good(Cases, Options) ->
lists:foreach(fun(Case) -> check_good(Case, Options) end, Cases).
test_errors(Cases) ->
lists:foreach(fun(Case) -> check_error(Case) end, Cases).
ok_dec(J, _E) ->
lists:flatten(io_lib:format("Decoded ~p.", [J])).
ok_enc(E, _J) ->
lists:flatten(io_lib:format("Encoded ~p", [E])).
do_encode(E, Options) ->
iolist_to_binary(jiffy:encode(E, Options)).
error_mesg(J) ->
lists:flatten(io_lib:format("Decoding ~p returns an error.", [J])).
check_good({J, E}, Options) ->
etap:is(jiffy:decode(J), E, ok_dec(J, E)),
etap:is(do_encode(E, Options), J, ok_enc(E, J));
check_good({J, E, J2}, Options) ->
etap:is(jiffy:decode(J), E, ok_dec(J, E)),
etap:is(do_encode(E, Options), J2, ok_enc(E, J2)).
check_error({J, E}) ->
etap:fun_is(
fun({error, E1}) when E1 == E -> true; (E1) -> E1 end,
(catch jiffy:decode(J)),
error_mesg(J)
);
check_error(J) ->
etap:fun_is(
fun({error, _}) -> true; (Else) -> Else end,
(catch jiffy:decode(J)),
error_mesg(J)
).

正在加载...
取消
保存