You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

261 rivejä
4.5 KiB

  1. % This file is part of Jiffy released under the MIT license.
  2. % See the LICENSE file for more information.
  3. -module(jiffy_11_property_tests).
  4. -ifdef(HAVE_EQC).
  5. -compile(export_all).
  6. -include_lib("eqc/include/eqc.hrl").
  7. -include_lib("eunit/include/eunit.hrl").
  8. -include("jiffy_util.hrl").
  9. property_test_() ->
  10. [
  11. run(prop_enc_dec),
  12. run(prop_enc_dec_pretty),
  13. run(prop_dec_trailer),
  14. run(prop_enc_no_crash),
  15. run(prop_dec_no_crash_bin),
  16. run(prop_dec_no_crash_any)
  17. ] ++ map_props().
  18. -ifndef(JIFFY_NO_MAPS).
  19. map_props() ->
  20. [
  21. run(prop_map_enc_dec)
  22. ].
  23. -else.
  24. map_props() ->
  25. [].
  26. -endif.
  27. prop_enc_dec() ->
  28. ?FORALL(Data, json(), begin
  29. Data == jiffy:decode(jiffy:encode(Data))
  30. end).
  31. prop_dec_trailer() ->
  32. ?FORALL({T1, Comb, T2}, {json(), combiner(), json()},
  33. begin
  34. B1 = iolist_to_binary(jiffy:encode(T1)),
  35. B2 = iolist_to_binary(jiffy:encode(T2)),
  36. Bin = <<B1/binary, Comb/binary, B2/binary>>,
  37. {has_trailer, T1, Rest} = jiffy:decode(Bin, [return_trailer]),
  38. T2 = jiffy:decode(Rest),
  39. true
  40. end
  41. ).
  42. prop_enc_dec_pretty() ->
  43. ?FORALL(Data, json(),
  44. begin
  45. Data == jiffy:decode(jiffy:encode(Data, [pretty]))
  46. end
  47. ).
  48. -ifndef(JIFFY_NO_MAPS).
  49. prop_map_enc_dec() ->
  50. ?FORALL(Data, json(),
  51. begin
  52. MapData = to_map_ejson(Data),
  53. MapData == jiffy:decode(jiffy:encode(MapData), [return_maps])
  54. end
  55. ).
  56. -endif.
  57. prop_enc_no_crash() ->
  58. ?FORALL(Data, any(), begin catch jiffy:encode(Data), true end).
  59. prop_dec_no_crash_any() ->
  60. ?FORALL(Data, any(), begin catch jiffy:decode(Data), true end).
  61. prop_dec_no_crash_bin() ->
  62. ?FORALL(Data, binary(), begin catch jiffy:decode(Data), true end).
  63. opts() ->
  64. [
  65. {numtests, [1000]}
  66. ].
  67. apply_opts(Prop) ->
  68. apply_opts(Prop, opts()).
  69. apply_opts(Prop, []) ->
  70. Prop;
  71. apply_opts(Prop, [{Name, Args} | Rest]) ->
  72. NewProp = erlang:apply(eqc, Name, Args ++ [Prop]),
  73. apply_opts(NewProp, Rest).
  74. log(F, A) ->
  75. io:format(standard_error, F, A).
  76. run(Name) ->
  77. Prop = apply_opts(?MODULE:Name()),
  78. {msg("~s", [Name]), [
  79. {timeout, 300, ?_assert(eqc:quickcheck(Prop))}
  80. ]}.
  81. -ifndef(JIFFY_NO_MAPS).
  82. to_map_ejson({Props}) ->
  83. NewProps = [{K, to_map_ejson(V)} || {K, V} <- Props],
  84. maps:from_list(NewProps);
  85. to_map_ejson(Vals) when is_list(Vals) ->
  86. [to_map_ejson(V) || V <- Vals];
  87. to_map_ejson(Val) ->
  88. Val.
  89. -endif.
  90. % Random any term generation
  91. any() ->
  92. ?SIZED(Size, any(Size)).
  93. any(0) ->
  94. any_value();
  95. any(S) ->
  96. oneof(any_value_types() ++ [
  97. ?LAZY(any_list(S)),
  98. ?LAZY(any_tuple(S))
  99. ]).
  100. any_value() ->
  101. oneof(any_value_types()).
  102. any_value_types() ->
  103. [
  104. largeint(),
  105. int(),
  106. real(),
  107. atom(),
  108. binary()
  109. ].
  110. any_list(0) ->
  111. [];
  112. any_list(Size) ->
  113. ListSize = Size div 5,
  114. vector(ListSize, any(Size div 2)).
  115. any_tuple(0) ->
  116. {};
  117. any_tuple(Size) ->
  118. ?LET(L, any_list(Size), list_to_tuple(L)).
  119. % JSON Generation
  120. json() ->
  121. ?SIZED(Size, json(Size)).
  122. json(0) ->
  123. oneof([
  124. json_null(),
  125. json_true(),
  126. json_false(),
  127. json_number(),
  128. json_string()
  129. ]);
  130. json(Size) ->
  131. frequency([
  132. {1, json_null()},
  133. {1, json_true()},
  134. {1, json_false()},
  135. {1, json_number()},
  136. {1, json_string()},
  137. {5, ?LAZY(json_array(Size))},
  138. {5, ?LAZY(json_object(Size))}
  139. ]).
  140. json_null() ->
  141. null.
  142. json_true() ->
  143. true.
  144. json_false() ->
  145. false.
  146. json_number() ->
  147. oneof([largeint(), int(), real()]).
  148. json_string() ->
  149. utf8().
  150. json_array(0) ->
  151. [];
  152. json_array(Size) ->
  153. ArrSize = Size div 5,
  154. vector(ArrSize, json(Size div 2)).
  155. json_object(0) ->
  156. {[]};
  157. json_object(Size) ->
  158. ObjSize = Size div 5,
  159. {vector(ObjSize, {json_string(), json(Size div 2)})}.
  160. combiner() ->
  161. ?SIZED(
  162. Size,
  163. ?LET(
  164. L,
  165. vector((Size div 4) + 1, oneof([$\r, $\n, $\t, $\s])),
  166. list_to_binary(L)
  167. )
  168. ).
  169. atom() ->
  170. ?LET(L, ?SIZED(Size, vector(Size rem 254, char())), list_to_atom(L)).
  171. %% XXX: Add generators
  172. %
  173. % We should add generators that generate JSON binaries directly
  174. % so we can test things that aren't produced by the encoder.
  175. %
  176. % We should also have a version of the JSON generator that inserts
  177. % errors into the JSON that we can test for.
  178. -endif.