25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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