diff --git a/src/jsx.erl b/src/jsx.erl index b87d44a..475fdd7 100644 --- a/src/jsx.erl +++ b/src/jsx.erl @@ -31,9 +31,19 @@ -export([is_json/1, is_json/2]). -export([format/1, format/2]). +%% if testing is enabled, export load_tests/1 so all modules may use it +-ifdef(test). +-export([load_tests/1]). +-endif. + %% types for function specifications -include("./include/jsx_types.hrl"). +-ifdef(test). +-include_lib("eunit/include/eunit.hrl"). +-endif. + + %% opts record -record(opts, { comments = false, @@ -206,4 +216,44 @@ detect_encoding(Bin, Opts) -> fun(end_stream) -> {error, badjson} ; (Stream) -> detect_encoding(<>, Opts) end - }. \ No newline at end of file + }. + + +%% eunit tests +-ifdef(test). + +load_tests(Path) -> + %% search the specified directory for any files with the .test ending + TestSpecs = filelib:wildcard("*.test", Path), + Tests = load_tests(TestSpecs, Path, []). + +load_tests([], _Dir, Acc) -> + lists:reverse(Acc); +load_tests([Test|Rest], Dir, Acc) -> + %% should alert about badly formed tests eventually, but for now just skip over them + case file:consult(Dir ++ "/" ++ Test) of + {ok, TestSpec} -> + try + load_tests(Rest, Dir, [parse_test(TestSpec, [])] ++ Acc) + catch _:_ -> + load_tests(Rest, Dir, Acc) + end + ; {error, _Reason} -> + load_tests(Rest, Dir, Acc) + end. + + +%% if the json, erlang or jsx values are lists, assume they're a path to a file that should +%% be read with file:read_file/1 +parse_test([{Key, Path}|Rest], Test) when is_list(Path) -> + case lists:member(Key, [json, erlang, jsx]) of + true -> + case file:read_file(Path) of + {ok, Bin} -> parse_test(Rest, [{Key, Bin}] ++ Test) + ; {error, Reason} -> {error, Reason} + end + ; false -> + parse_test(Rest, [{Key, Path}] ++ Test) + end. + +-endif. \ No newline at end of file diff --git a/src/jsx_encoder.erl b/src/jsx_encoder.erl index f690409..65a6fac 100644 --- a/src/jsx_encoder.erl +++ b/src/jsx_encoder.erl @@ -29,6 +29,11 @@ -include("./include/jsx_types.hrl"). +-ifdef(test). +-include_lib("eunit/include/eunit.hrl"). +-endif. + + -spec term_to_json(JSON::json(), Opts::encoder_opts()) -> binary(). @@ -114,7 +119,7 @@ float_to_decimal(Num) when is_float(Num) -> {F, E} = extract(<>), {R, S, MP, MM} = initial_vals(F, E), K = ceiling(math:log10(abs(Num)) - 1.0e-10), - Round = F band 1 =:= 1, + Round = F band 1 =:= 0, {Dpoint, Digits} = scale(R, S, MP, MM, K, Round), if Num >= 0 -> format(Dpoint, Digits) ; Num < 0 -> "-" ++ format(Dpoint, Digits) @@ -172,7 +177,7 @@ generate(RT, S, MP, MM, Round) -> TC1 = case Round of true -> (R =< MM); false -> (R < MM) end, TC2 = case Round of true -> (R + MP >= S); false -> (R + MP > S) end, case TC1 of - false -> case TC2 of + false -> case TC2 of false -> [D | generate(R * 10, S, MP * 10, MM * 10, Round)] ; true -> [D + 1] end @@ -268,5 +273,22 @@ to_hex(10) -> $a; to_hex(X) -> X + $0. +%% eunit tests +-ifdef(test). - \ No newline at end of file +jsx_escape_test_() -> + [ + {"json string escaping", ?_assert(json_escape(<<"\"\\\b\f\n\r\t">>) =:= <<"\\\"\\\\\\b\\f\\n\\r\\t">>)}, + {"json string hex escape", ?_assert(json_escape(<<1, 2, 3, 11, 26, 30, 31>>) =:= <<"\\u0001\\u0002\\u0003\\u000b\\u001a\\u001e\\u001f">>)} + ]. + +jsx_nice_decimal_test_() -> + [ + {"0.0 to decimal", ?_assert(list_to_float(float_to_decimal(0.0)) =:= 0.0)}, + {"1.0 to decimal", ?_assert(list_to_float(float_to_decimal(1.0)) =:= 1.0)}, + {"-1.0 to decimal", ?_assert(list_to_float(float_to_decimal(-1.0)) =:= -1.0)}, + {"really long float to decimal", ?_assert(list_to_float(float_to_decimal(3.1234567890987654321)) =:= 3.1234567890987654321)}, + {"1.0e23", ?_assert(float_to_decimal(1.0e23) =:= "1.0e23")} + ]. + +-endif. \ No newline at end of file