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.

186 lines
6.4 KiB

14 years ago
  1. %% -------------------------------------------------------------------
  2. %%
  3. %% trunc_io_eqc: QuickCheck test for trunc_io:format with maxlen
  4. %%
  5. %% Copyright (c) 2007-2011 Basho Technologies, Inc. All Rights Reserved.
  6. %%
  7. %% This file is provided to you under the Apache License,
  8. %% Version 2.0 (the "License"); you may not use this file
  9. %% except in compliance with the License. You may obtain
  10. %% a copy of the License at
  11. %%
  12. %% http://www.apache.org/licenses/LICENSE-2.0
  13. %%
  14. %% Unless required by applicable law or agreed to in writing,
  15. %% software distributed under the License is distributed on an
  16. %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  17. %% KIND, either express or implied. See the License for the
  18. %% specific language governing permissions and limitations
  19. %% under the License.
  20. %%
  21. %% -------------------------------------------------------------------
  22. -module(trunc_io_eqc).
  23. -ifdef(TEST).
  24. -ifdef(EQC).
  25. -export([test/0, test/1, check/0]).
  26. -include_lib("eqc/include/eqc.hrl").
  27. -include_lib("eunit/include/eunit.hrl").
  28. -define(QC_OUT(P),
  29. eqc:on_output(fun(Str, Args) -> io:format(user, Str, Args) end, P)).
  30. %%====================================================================
  31. %% eunit test
  32. %%====================================================================
  33. eqc_test_() ->
  34. {timeout, 300,
  35. {spawn,
  36. [?_assertEqual(true, quickcheck(numtests(500, ?QC_OUT(prop_format()))))]
  37. }}.
  38. %%====================================================================
  39. %% Shell helpers
  40. %%====================================================================
  41. test() ->
  42. test(100).
  43. test(N) ->
  44. quickcheck(numtests(N, prop_format())).
  45. check() ->
  46. check(prop_format(), current_counterexample()).
  47. %%====================================================================
  48. %% Generators
  49. %%====================================================================
  50. gen_fmt_args() ->
  51. list(oneof([gen_print_str(),
  52. "~~",
  53. {"~p", gen_any(5)},
  54. {"~w", gen_any(5)},
  55. {"~s", gen_print_str()},
  56. {"~P", gen_any(5), 4},
  57. {"~W", gen_any(5), 4},
  58. {"~i", gen_any(5)},
  59. {"~B", nat()},
  60. {"~b", nat()},
  61. {"~X", nat(), "0x"},
  62. {"~x", nat(), "0x"},
  63. {"~.10#", nat()},
  64. {"~.10+", nat()},
  65. {"~.36B", nat()},
  66. {"~62P", gen_any(5), 4},
  67. {"~c", gen_char()},
  68. {"~tc", gen_char()}
  69. %{"~f", real()}, %% floats like to make the fudge limit fail, so don't enable them
  70. %{"~10.f", real()},
  71. %{"~g", real()},
  72. %{"~10.g", real()},
  73. %{"~e", real()},
  74. %{"~10.e", real()}
  75. ])).
  76. %% Generates a printable string
  77. gen_print_str() ->
  78. ?LET(Xs, list(char()), [X || X <- Xs, io_lib:printable_list([X]), X /= $~]).
  79. gen_any(MaxDepth) ->
  80. oneof([largeint(),
  81. gen_atom(),
  82. nat(),
  83. %real(),
  84. binary(),
  85. gen_pid(),
  86. gen_port(),
  87. gen_ref(),
  88. gen_fun()] ++
  89. [?LAZY(list(gen_any(MaxDepth - 1))) || MaxDepth /= 0] ++
  90. [?LAZY(gen_tuple(gen_any(MaxDepth - 1))) || MaxDepth /= 0]).
  91. gen_atom() ->
  92. elements([abc, def, ghi]).
  93. gen_tuple(Gen) ->
  94. ?LET(Xs, list(Gen), list_to_tuple(Xs)).
  95. gen_max_len() -> %% Generate length from 3 to whatever. Needs space for ... in output
  96. ?LET(Xs, int(), 3 + abs(Xs)).
  97. gen_pid() ->
  98. ?LAZY(spawn(fun() -> ok end)).
  99. gen_port() ->
  100. ?LAZY(begin
  101. Port = erlang:open_port({spawn, "true"}, []),
  102. erlang:port_close(Port),
  103. Port
  104. end).
  105. gen_ref() ->
  106. ?LAZY(make_ref()).
  107. gen_fun() ->
  108. ?LAZY(fun() -> ok end).
  109. gen_char() ->
  110. oneof(lists:seq($A, $z)).
  111. %%====================================================================
  112. %% Property
  113. %%====================================================================
  114. %% Checks that trunc_io:format produces output less than or equal to MaxLen
  115. prop_format() ->
  116. ?FORALL({FmtArgs, MaxLen}, {gen_fmt_args(), gen_max_len()},
  117. begin
  118. FudgeLen = 50, %% trunc_io does not correctly calc safe size of pid/port/numbers/funs
  119. {FmtStr, Args} = build_fmt_args(FmtArgs),
  120. try
  121. Str = lists:flatten(lager_trunc_io:format(FmtStr, Args, MaxLen)),
  122. ?WHENFAIL(begin
  123. io:format(user, "FmtStr: ~p\n", [FmtStr]),
  124. io:format(user, "Args: ~p\n", [Args]),
  125. io:format(user, "FudgeLen: ~p\n", [FudgeLen]),
  126. io:format(user, "MaxLen: ~p\n", [MaxLen]),
  127. io:format(user, "ActLen: ~p\n", [length(Str)]),
  128. io:format(user, "Str: ~p\n", [Str])
  129. end,
  130. %% Make sure the result is a printable list
  131. %% and if the format string is less than the length,
  132. %% the result string is less than the length.
  133. conjunction([{printable, Str == "" orelse
  134. io_lib:printable_list(Str)},
  135. {length, length(FmtStr) > MaxLen orelse
  136. length(Str) =< MaxLen + FudgeLen}]))
  137. catch
  138. _:Err ->
  139. io:format(user, "\nException: ~p\n", [Err]),
  140. io:format(user, "FmtStr: ~p\n", [FmtStr]),
  141. io:format(user, "Args: ~p\n", [Args]),
  142. false
  143. end
  144. end).
  145. %%====================================================================
  146. %% Internal helpers
  147. %%====================================================================
  148. %% Build a tuple of {Fmt, Args} from a gen_fmt_args() return
  149. build_fmt_args(FmtArgs) ->
  150. F = fun({Fmt, Arg}, {FmtStr0, Args0}) ->
  151. {FmtStr0 ++ Fmt, Args0 ++ [Arg]};
  152. ({Fmt, Arg1, Arg2}, {FmtStr0, Args0}) ->
  153. {FmtStr0 ++ Fmt, Args0 ++ [Arg1, Arg2]};
  154. (Str, {FmtStr0, Args0}) ->
  155. {FmtStr0 ++ Str, Args0}
  156. end,
  157. lists:foldl(F, {"", []}, FmtArgs).
  158. -endif. % (EQC).
  159. -endif. % (TEST).