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).