rewrite from lager
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

244 行
7.8 KiB

4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
  1. %% -------------------------------------------------------------------
  2. %%
  3. %% trunc_io_eqc: QuickCheck test for trunc_io:format with maxlen
  4. %%
  5. %% Copyright (c) 2011-2012 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, prop_format/0, prop_equivalence/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, 60,
  35. {spawn,
  36. [
  37. {timeout, 30, ?_assertEqual(true, eqc:quickcheck(eqc:testing_time(14, ?QC_OUT(prop_format()))))},
  38. {timeout, 30, ?_assertEqual(true, eqc:quickcheck(eqc:testing_time(14, ?QC_OUT(prop_equivalence()))))}
  39. ]
  40. }}.
  41. %%====================================================================
  42. %% Shell helpers
  43. %%====================================================================
  44. test() ->
  45. test(100).
  46. test(N) ->
  47. quickcheck(numtests(N, prop_format())).
  48. check() ->
  49. check(prop_format(), current_counterexample()).
  50. %%====================================================================
  51. %% Generators
  52. %%====================================================================
  53. gen_fmt_args() ->
  54. list(oneof([gen_print_str(),
  55. "~~",
  56. {"~10000000.p", gen_any(5)},
  57. {"~w", gen_any(5)},
  58. {"~s", oneof([gen_print_str(), gen_atom(), gen_quoted_atom(), gen_print_bin(), gen_iolist(5)])},
  59. {"~1000000.P", gen_any(5), 4},
  60. {"~W", gen_any(5), 4},
  61. {"~i", gen_any(5)},
  62. {"~B", nat()},
  63. {"~b", nat()},
  64. {"~X", nat(), "0x"},
  65. {"~x", nat(), "0x"},
  66. {"~.10#", nat()},
  67. {"~.10+", nat()},
  68. {"~.36B", nat()},
  69. {"~1000000.62P", gen_any(5), 4},
  70. {"~c", gen_char()},
  71. {"~tc", gen_char()},
  72. {"~f", real()},
  73. {"~10.f", real()},
  74. {"~g", real()},
  75. {"~10.g", real()},
  76. {"~e", real()},
  77. {"~10.e", real()}
  78. ])).
  79. %% Generates a printable string
  80. gen_print_str() ->
  81. ?LET(Xs, list(char()), [X || X <- Xs, io_lib:printable_list([X]), X /= $~, X < 256]).
  82. gen_print_bin() ->
  83. ?LET(Xs, gen_print_str(), list_to_binary(Xs)).
  84. gen_any(MaxDepth) ->
  85. oneof([largeint(),
  86. gen_atom(),
  87. gen_quoted_atom(),
  88. nat(),
  89. %real(),
  90. binary(),
  91. gen_bitstring(),
  92. gen_pid(),
  93. gen_port(),
  94. gen_ref(),
  95. gen_fun()] ++
  96. [?LAZY(list(gen_any(MaxDepth - 1))) || MaxDepth /= 0] ++
  97. [?LAZY(gen_tuple(gen_any(MaxDepth - 1))) || MaxDepth /= 0]).
  98. gen_iolist(0) ->
  99. [];
  100. gen_iolist(Depth) ->
  101. list(oneof([gen_char(), gen_print_str(), gen_print_bin(), gen_iolist(Depth - 1)])).
  102. gen_atom() ->
  103. elements([abc, def, ghi]).
  104. gen_quoted_atom() ->
  105. elements(['abc@bar', '@bar', '10gen']).
  106. gen_bitstring() ->
  107. ?LET(XS, binary(), <<XS/binary, 1:7>>).
  108. gen_tuple(Gen) ->
  109. ?LET(Xs, list(Gen), list_to_tuple(Xs)).
  110. gen_max_len() -> %% Generate length from 3 to whatever. Needs space for ... in output
  111. ?LET(Xs, int(), 3 + abs(Xs)).
  112. gen_pid() ->
  113. ?LAZY(spawn(fun() -> ok end)).
  114. gen_port() ->
  115. ?LAZY(begin
  116. Port = erlang:open_port({spawn, "true"}, []),
  117. catch (erlang:port_close(Port)),
  118. Port
  119. end).
  120. gen_ref() ->
  121. ?LAZY(make_ref()).
  122. gen_fun() ->
  123. ?LAZY(fun() -> ok end).
  124. gen_char() ->
  125. oneof(lists:seq($A, $z)).
  126. %%====================================================================
  127. %% Property
  128. %%====================================================================
  129. %% Checks that trunc_io:format produces output less than or equal to MaxLen
  130. prop_format() ->
  131. ?FORALL({FmtArgs, MaxLen}, {gen_fmt_args(), gen_max_len()},
  132. begin
  133. %% Because trunc_io will print '...' when its running out of
  134. %% space, even if the remaining space is less than 3, it
  135. %% doesn't *exactly* stick to the specified limit.
  136. %% Also, since we don't truncate terms not printed with
  137. %% ~p/~P/~w/~W/~s, we also need to calculate the wiggle room
  138. %% for those. Hence the fudge factor calculated below.
  139. FudgeLen = calculate_fudge(FmtArgs, 50),
  140. {FmtStr, Args} = build_fmt_args(FmtArgs),
  141. try
  142. Str = lists:flatten(rumTruncIo:format(FmtStr, Args, MaxLen)),
  143. ?WHENFAIL(begin
  144. io:format(user, "FmtStr: ~p\n", [FmtStr]),
  145. io:format(user, "Args: ~p\n", [Args]),
  146. io:format(user, "FudgeLen: ~p\n", [FudgeLen]),
  147. io:format(user, "MaxLen: ~p\n", [MaxLen]),
  148. io:format(user, "ActLen: ~p\n", [length(Str)]),
  149. io:format(user, "Str: ~p\n", [Str])
  150. end,
  151. %% Make sure the result is a printable list
  152. %% and if the format string is less than the length,
  153. %% the result string is less than the length.
  154. conjunction([{printable, Str == "" orelse
  155. io_lib:printable_list(Str)},
  156. {length, length(FmtStr) > MaxLen orelse
  157. length(Str) =< MaxLen + FudgeLen}]))
  158. catch
  159. _:Err ->
  160. io:format(user, "\nException: ~p\n", [Err]),
  161. io:format(user, "FmtStr: ~p\n", [FmtStr]),
  162. io:format(user, "Args: ~p\n", [Args]),
  163. false
  164. end
  165. end).
  166. %% Checks for equivalent formatting to io_lib
  167. prop_equivalence() ->
  168. ?FORALL(FmtArgs, gen_fmt_args(),
  169. begin
  170. {FmtStr, Args} = build_fmt_args(FmtArgs),
  171. Expected = lists:flatten(io_lib:format(FmtStr, Args)),
  172. Actual = lists:flatten(rumTruncIo:format(FmtStr, Args, 10485760)),
  173. ?WHENFAIL(begin
  174. io:format(user, "FmtStr: ~p\n", [FmtStr]),
  175. io:format(user, "Args: ~p\n", [Args]),
  176. io:format(user, "Expected: ~p\n", [Expected]),
  177. io:format(user, "Actual: ~p\n", [Actual])
  178. end,
  179. Expected == Actual)
  180. end).
  181. %%====================================================================
  182. %% Internal helpers
  183. %%====================================================================
  184. %% Build a tuple of {Fmt, Args} from a gen_fmt_args() return
  185. build_fmt_args(FmtArgs) ->
  186. F = fun({Fmt, Arg}, {FmtStr0, Args0}) ->
  187. {FmtStr0 ++ Fmt, Args0 ++ [Arg]};
  188. ({Fmt, Arg1, Arg2}, {FmtStr0, Args0}) ->
  189. {FmtStr0 ++ Fmt, Args0 ++ [Arg1, Arg2]};
  190. (Str, {FmtStr0, Args0}) ->
  191. {FmtStr0 ++ Str, Args0}
  192. end,
  193. lists:foldl(F, {"", []}, FmtArgs).
  194. calculate_fudge([], Acc) ->
  195. Acc;
  196. calculate_fudge([{"~62P", _Arg, _Depth} | T], Acc) ->
  197. calculate_fudge(T, Acc + 62);
  198. calculate_fudge([{Fmt, Arg} | T], Acc) when
  199. Fmt == "~f"; Fmt == "~10.f";
  200. Fmt == "~g"; Fmt == "~10.g";
  201. Fmt == "~e"; Fmt == "~10.e";
  202. Fmt == "~x"; Fmt == "~X";
  203. Fmt == "~B"; Fmt == "~b"; Fmt == "~36B";
  204. Fmt == "~.10#"; Fmt == "~10+" ->
  205. calculate_fudge(T, Acc + length(lists:flatten(io_lib:format(Fmt, [Arg]))));
  206. calculate_fudge([_ | T], Acc) ->
  207. calculate_fudge(T, Acc).
  208. -endif. % (EQC).
  209. -endif. % (TEST).