Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

582 строки
24 KiB

14 лет назад
13 лет назад
14 лет назад
14 лет назад
14 лет назад
14 лет назад
14 лет назад
14 лет назад
14 лет назад
14 лет назад
14 лет назад
14 лет назад
14 лет назад
14 лет назад
14 лет назад
14 лет назад
14 лет назад
14 лет назад
14 лет назад
13 лет назад
13 лет назад
14 лет назад
14 лет назад
14 лет назад
14 лет назад
14 лет назад
14 лет назад
14 лет назад
13 лет назад
14 лет назад
14 лет назад
13 лет назад
14 лет назад
13 лет назад
13 лет назад
13 лет назад
13 лет назад
13 лет назад
  1. %% ``The contents of this file are subject to the Erlang Public License,
  2. %% Version 1.1, (the "License"); you may not use this file except in
  3. %% compliance with the License. You should have received a copy of the
  4. %% Erlang Public License along with your Erlang distribution. If not, it can be
  5. %% retrieved via the world wide web at http://www.erlang.org/.
  6. %%
  7. %% Software distributed under the License is distributed on an "AS IS"
  8. %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
  9. %% the License for the specific language governing rights and limitations
  10. %% under the License.
  11. %%
  12. %% The Initial Developer of the Original Code is Corelatus AB.
  13. %% Portions created by Corelatus are Copyright 2003, Corelatus
  14. %% AB. All Rights Reserved.''
  15. %%
  16. %% @doc Module to print out terms for logging. Limits by length rather than depth.
  17. %%
  18. %% The resulting string may be slightly larger than the limit; the intention
  19. %% is to provide predictable CPU and memory consumption for formatting
  20. %% terms, not produce precise string lengths.
  21. %%
  22. %% Typical use:
  23. %%
  24. %% trunc_io:print(Term, 500).
  25. %%
  26. %% Source license: Erlang Public License.
  27. %% Original author: Matthias Lang, <tt>matthias@corelatus.se</tt>
  28. %%
  29. %% Various changes to this module, most notably the format/3 implementation
  30. %% were added by Andrew Thompson `<andrew@basho.com>'. The module has been renamed
  31. %% to avoid conflicts with the vanilla module.
  32. -module(lager_trunc_io).
  33. -author('matthias@corelatus.se').
  34. %% And thanks to Chris Newcombe for a bug fix
  35. -export([format/3, format/4, print/2, print/3, fprint/2, fprint/3, safe/2]). % interface functions
  36. -version("$Id: trunc_io.erl,v 1.11 2009-02-23 12:01:06 matthias Exp $").
  37. -ifdef(TEST).
  38. -export([perf/0, perf/3, perf1/0, test/0, test/2]). % testing functions
  39. -include_lib("eunit/include/eunit.hrl").
  40. -endif.
  41. -type option() :: {'depth', integer()}
  42. | {'lists_as_strings', boolean()}
  43. | {'force_strings', boolean()}.
  44. -type options() :: [option()].
  45. -record(print_options, {
  46. %% negative depth means no depth limiting
  47. depth = -1 :: integer(),
  48. %% whether to print lists as strings, if possible
  49. lists_as_strings = true :: boolean(),
  50. %% force strings, or binaries to be printed as a string,
  51. %% even if they're not printable
  52. force_strings = false :: boolean()
  53. }).
  54. format(Fmt, Args, Max) ->
  55. format(Fmt, Args, Max, []).
  56. format(Fmt, Args, Max, Options) ->
  57. try lager_format:format(Fmt, Args, Max, Options) of
  58. Result -> Result
  59. catch
  60. _:_ ->
  61. erlang:error(badarg, [Fmt, Args])
  62. end.
  63. %% @doc Returns an flattened list containing the ASCII representation of the given
  64. %% term.
  65. -spec fprint(term(), pos_integer()) -> string().
  66. fprint(Term, Max) ->
  67. fprint(Term, Max, []).
  68. %% @doc Returns an flattened list containing the ASCII representation of the given
  69. %% term.
  70. -spec fprint(term(), pos_integer(), options()) -> string().
  71. fprint(T, Max, Options) ->
  72. {L, _} = print(T, Max, prepare_options(Options, #print_options{})),
  73. lists:flatten(L).
  74. %% @doc Same as print, but never crashes.
  75. %%
  76. %% This is a tradeoff. Print might conceivably crash if it's asked to
  77. %% print something it doesn't understand, for example some new data
  78. %% type in a future version of Erlang. If print crashes, we fall back
  79. %% to io_lib to format the term, but then the formatting is
  80. %% depth-limited instead of length limited, so you might run out
  81. %% memory printing it. Out of the frying pan and into the fire.
  82. %%
  83. -spec safe(term(), pos_integer()) -> {string(), pos_integer()} | {string()}.
  84. safe(What, Len) ->
  85. case catch print(What, Len) of
  86. {L, Used} when is_list(L) -> {L, Used};
  87. _ -> {"unable to print" ++ io_lib:write(What, 99)}
  88. end.
  89. %% @doc Returns {List, Length}
  90. -spec print(term(), pos_integer()) -> {iolist(), pos_integer()}.
  91. print(Term, Max) ->
  92. print(Term, Max, []).
  93. %% @doc Returns {List, Length}
  94. -spec print(term(), pos_integer(), options() | #print_options{}) -> {iolist(), pos_integer()}.
  95. print(Term, Max, Options) when is_list(Options) ->
  96. %% need to convert the proplist to a record
  97. print(Term, Max, prepare_options(Options, #print_options{}));
  98. print(Term, _Max, #print_options{force_strings=true}) when not is_list(Term), not is_binary(Term), not is_atom(Term) ->
  99. erlang:error(badarg);
  100. print(_, Max, _Options) when Max < 0 -> {"...", 3};
  101. print(_, _, #print_options{depth=0}) -> {"...", 3};
  102. print(Tuple, Max, Options) when is_tuple(Tuple) ->
  103. {TC, Len} = tuple_contents(Tuple, Max-2, Options),
  104. {[${, TC, $}], Len + 2};
  105. %% @doc We assume atoms, floats, funs, integers, PIDs, ports and refs never need
  106. %% to be truncated. This isn't strictly true, someone could make an
  107. %% arbitrarily long bignum. Let's assume that won't happen unless someone
  108. %% is being malicious.
  109. %%
  110. print(Atom, _Max, #print_options{force_strings=NoQuote}) when is_atom(Atom) ->
  111. L = atom_to_list(Atom),
  112. R = case atom_needs_quoting_start(L) andalso not NoQuote of
  113. true -> lists:flatten([$', L, $']);
  114. false -> L
  115. end,
  116. {R, length(R)};
  117. print(<<>>, _Max, _Options) ->
  118. {"<<>>", 4};
  119. print(Binary, 0, _Options) when is_bitstring(Binary) ->
  120. {"<<..>>", 6};
  121. print(Binary, Max, Options) when is_binary(Binary) ->
  122. B = binary_to_list(Binary, 1, lists:min([Max, byte_size(Binary)])),
  123. {L, Len} = case Options#print_options.lists_as_strings orelse
  124. Options#print_options.force_strings of
  125. true ->
  126. alist_start(B, Max-4, Options);
  127. _ ->
  128. list_body(B, Max-4, Options, false)
  129. end,
  130. {Res, Length} = case L of
  131. [91, X, 93] ->
  132. {X, Len - 2};
  133. X ->
  134. {X, Len}
  135. end,
  136. case Options#print_options.force_strings of
  137. true ->
  138. {Res, Length};
  139. _ ->
  140. {["<<", Res, ">>"], Length+4}
  141. end;
  142. %% bitstrings are binary's evil brother who doesn't end on an 8 bit boundary.
  143. %% This makes printing them extremely annoying, so list_body/list_bodyc has
  144. %% some magic for dealing with the output of bitstring_to_list, which returns
  145. %% a list of integers (as expected) but with a trailing binary that represents
  146. %% the remaining bits.
  147. print(BitString, Max, Options) when is_bitstring(BitString) ->
  148. case byte_size(BitString) > Max of
  149. true ->
  150. BL = binary_to_list(BitString, 1, Max);
  151. _ ->
  152. BL = erlang:bitstring_to_list(BitString)
  153. end,
  154. {X, Len0} = list_body(BL, Max - 4, Options, false),
  155. {["<<", X, ">>"], Len0 + 4};
  156. print(Float, _Max, _Options) when is_float(Float) ->
  157. %% use the same function io_lib:format uses to print floats
  158. %% float_to_list is way too verbose.
  159. L = io_lib_format:fwrite_g(Float),
  160. {L, length(L)};
  161. print(Fun, Max, _Options) when is_function(Fun) ->
  162. L = erlang:fun_to_list(Fun),
  163. case length(L) > Max of
  164. true ->
  165. S = erlang:max(5, Max),
  166. Res = string:substr(L, 1, S) ++ "..>",
  167. {Res, length(Res)};
  168. _ ->
  169. {L, length(L)}
  170. end;
  171. print(Integer, _Max, _Options) when is_integer(Integer) ->
  172. L = integer_to_list(Integer),
  173. {L, length(L)};
  174. print(Pid, _Max, _Options) when is_pid(Pid) ->
  175. L = pid_to_list(Pid),
  176. {L, length(L)};
  177. print(Ref, _Max, _Options) when is_reference(Ref) ->
  178. L = erlang:ref_to_list(Ref),
  179. {L, length(L)};
  180. print(Port, _Max, _Options) when is_port(Port) ->
  181. L = erlang:port_to_list(Port),
  182. {L, length(L)};
  183. print(List, Max, Options) when is_list(List) ->
  184. case Options#print_options.lists_as_strings orelse
  185. Options#print_options.force_strings of
  186. true ->
  187. alist_start(List, Max, dec_depth(Options));
  188. _ ->
  189. {R, Len} = list_body(List, Max - 2, dec_depth(Options), false),
  190. {[$[, R, $]], Len + 2}
  191. end.
  192. %% Returns {List, Length}
  193. tuple_contents(Tuple, Max, Options) ->
  194. L = tuple_to_list(Tuple),
  195. list_body(L, Max, dec_depth(Options), true).
  196. %% Format the inside of a list, i.e. do not add a leading [ or trailing ].
  197. %% Returns {List, Length}
  198. list_body([], _Max, _Options, _Tuple) -> {[], 0};
  199. list_body(_, Max, _Options, _Tuple) when Max < 4 -> {"...", 3};
  200. list_body(_, _Max, #print_options{depth=0}, _Tuple) -> {"...", 3};
  201. list_body([B], _Max, _Options, _Tuple) when is_bitstring(B), not is_binary(B) ->
  202. Size = bit_size(B),
  203. <<Value:Size>> = B,
  204. ValueStr = integer_to_list(Value),
  205. SizeStr = integer_to_list(Size),
  206. {[ValueStr, $:, SizeStr], length(ValueStr) + length(SizeStr) +1};
  207. list_body([H|T], Max, Options, Tuple) ->
  208. {List, Len} = print(H, Max, Options),
  209. {Final, FLen} = list_bodyc(T, Max - Len, Options, Tuple),
  210. {[List|Final], FLen + Len};
  211. list_body(X, Max, Options, _Tuple) -> %% improper list
  212. {List, Len} = print(X, Max - 1, Options),
  213. {[$|,List], Len + 1}.
  214. list_bodyc([], _Max, _Options, _Tuple) -> {[], 0};
  215. list_bodyc(_, Max, _Options, _Tuple) when Max < 5 -> {",...", 4};
  216. list_bodyc([B], _Max, _Options, _Tuple) when is_bitstring(B), not is_binary(B) ->
  217. Size = bit_size(B),
  218. <<Value:Size>> = B,
  219. ValueStr = integer_to_list(Value),
  220. SizeStr = integer_to_list(Size),
  221. {[$, , ValueStr, $:, SizeStr], length(ValueStr) + length(SizeStr) +2};
  222. list_bodyc([H|T], Max, #print_options{depth=Depth} = Options, Tuple) ->
  223. {List, Len} = print(H, Max, dec_depth(Options)),
  224. {Final, FLen} = list_bodyc(T, Max - Len - 1, Options, Tuple),
  225. Sep = case Depth == 1 andalso not Tuple of
  226. true -> $|;
  227. _ -> $,
  228. end,
  229. {[Sep, List|Final], FLen + Len + 1};
  230. list_bodyc(X, Max, Options, _Tuple) -> %% improper list
  231. {List, Len} = print(X, Max - 1, Options),
  232. {[$|,List], Len + 1}.
  233. %% The head of a list we hope is ascii. Examples:
  234. %%
  235. %% [65,66,67] -> "ABC"
  236. %% [65,0,67] -> "A"[0,67]
  237. %% [0,65,66] -> [0,65,66]
  238. %% [65,b,66] -> "A"[b,66]
  239. %%
  240. alist_start([], _Max, #print_options{force_strings=true}) -> {"", 0};
  241. alist_start([], _Max, _Options) -> {"[]", 2};
  242. alist_start(_, Max, _Options) when Max < 4 -> {"...", 3};
  243. alist_start(_, _Max, #print_options{depth=0}) -> {"[...]", 5};
  244. alist_start(L, Max, #print_options{force_strings=true} = Options) ->
  245. alist(L, Max, Options);
  246. alist_start([H|T], Max, Options) when is_integer(H), H >= 16#20, H =< 16#7e -> % definitely printable
  247. try alist([H|T], Max -1, Options) of
  248. {L, Len} ->
  249. {[$"|L], Len + 1}
  250. catch
  251. throw:unprintable ->
  252. {R, Len} = list_body([H|T], Max-2, Options, false),
  253. {[$[, R, $]], Len + 2}
  254. end;
  255. alist_start([H|T], Max, Options) when H =:= 9; H =:= 10; H =:= 13 ->
  256. try alist([H|T], Max -1, Options) of
  257. {L, Len} ->
  258. {[$"|L], Len + 1}
  259. catch
  260. throw:unprintable ->
  261. {R, Len} = list_body([H|T], Max-2, Options, false),
  262. {[$[, R, $]], Len + 2}
  263. end;
  264. alist_start(L, Max, Options) ->
  265. {R, Len} = list_body(L, Max-2, Options, false),
  266. {[$[, R, $]], Len + 2}.
  267. alist([], _Max, #print_options{force_strings=true}) -> {"", 0};
  268. alist([], _Max, _Options) -> {"\"", 1};
  269. alist(_, Max, #print_options{force_strings=true}) when Max < 4 -> {"...", 3};
  270. alist(_, Max, #print_options{force_strings=false}) when Max < 5 -> {"...\"", 4};
  271. alist([H|T], Max, Options) when is_integer(H), H >= 16#20, H =< 16#7e -> % definitely printable
  272. {L, Len} = alist(T, Max-1, Options),
  273. {[H|L], Len + 1};
  274. alist([H|T], Max, Options) when H =:= 9; H =:= 10; H =:= 13 ->
  275. {L, Len} = alist(T, Max-1, Options),
  276. case Options#print_options.force_strings of
  277. true ->
  278. {[H|L], Len + 1};
  279. _ ->
  280. {[escape(H)|L], Len + 1}
  281. end;
  282. alist([H|T], Max, #print_options{force_strings=true} = Options) when is_integer(H) ->
  283. {L, Len} = alist(T, Max-1, Options),
  284. {[H|L], Len + 1};
  285. alist(_, _, #print_options{force_strings=true}) ->
  286. erlang:error(badarg);
  287. alist(_L, _Max, _Options) ->
  288. throw(unprintable).
  289. %% is the first character in the atom alphabetic & lowercase?
  290. atom_needs_quoting_start([H|T]) when H >= $a, H =< $z ->
  291. atom_needs_quoting(T);
  292. atom_needs_quoting_start(_) ->
  293. true.
  294. atom_needs_quoting([]) ->
  295. false;
  296. atom_needs_quoting([H|T]) when (H >= $a andalso H =< $z);
  297. (H >= $A andalso H =< $Z);
  298. (H >= $0 andalso H =< $9);
  299. H == $@; H == $_ ->
  300. atom_needs_quoting(T);
  301. atom_needs_quoting(_) ->
  302. true.
  303. -spec prepare_options(options(), #print_options{}) -> #print_options{}.
  304. prepare_options([], Options) ->
  305. Options;
  306. prepare_options([{depth, Depth}|T], Options) when is_integer(Depth) ->
  307. prepare_options(T, Options#print_options{depth=Depth});
  308. prepare_options([{lists_as_strings, Bool}|T], Options) when is_boolean(Bool) ->
  309. prepare_options(T, Options#print_options{lists_as_strings = Bool});
  310. prepare_options([{force_strings, Bool}|T], Options) when is_boolean(Bool) ->
  311. prepare_options(T, Options#print_options{force_strings = Bool}).
  312. dec_depth(#print_options{depth=Depth} = Options) when Depth > 0 ->
  313. Options#print_options{depth=Depth-1};
  314. dec_depth(Options) ->
  315. Options.
  316. escape(9) -> "\\t";
  317. escape(10) -> "\\n";
  318. escape(13) -> "\\r".
  319. -ifdef(TEST).
  320. %%--------------------
  321. %% The start of a test suite. So far, it only checks for not crashing.
  322. -spec test() -> ok.
  323. test() ->
  324. test(trunc_io, print).
  325. -spec test(atom(), atom()) -> ok.
  326. test(Mod, Func) ->
  327. Simple_items = [atom, 1234, 1234.0, {tuple}, [], [list], "string", self(),
  328. <<1,2,3>>, make_ref(), fun() -> ok end],
  329. F = fun(A) ->
  330. Mod:Func(A, 100),
  331. Mod:Func(A, 2),
  332. Mod:Func(A, 20)
  333. end,
  334. G = fun(A) ->
  335. case catch F(A) of
  336. {'EXIT', _} -> exit({failed, A});
  337. _ -> ok
  338. end
  339. end,
  340. lists:foreach(G, Simple_items),
  341. Tuples = [ {1,2,3,a,b,c}, {"abc", def, 1234},
  342. {{{{a},b,c,{d},e}},f}],
  343. Lists = [ [1,2,3,4,5,6,7], lists:seq(1,1000),
  344. [{a}, {a,b}, {a, [b,c]}, "def"], [a|b], [$a|$b] ],
  345. lists:foreach(G, Tuples),
  346. lists:foreach(G, Lists).
  347. -spec perf() -> ok.
  348. perf() ->
  349. {New, _} = timer:tc(trunc_io, perf, [trunc_io, print, 1000]),
  350. {Old, _} = timer:tc(trunc_io, perf, [io_lib, write, 1000]),
  351. io:fwrite("New code took ~p us, old code ~p\n", [New, Old]).
  352. -spec perf(atom(), atom(), integer()) -> done.
  353. perf(M, F, Reps) when Reps > 0 ->
  354. test(M,F),
  355. perf(M,F,Reps-1);
  356. perf(_,_,_) ->
  357. done.
  358. %% Performance test. Needs a particularly large term I saved as a binary...
  359. -spec perf1() -> {non_neg_integer(), non_neg_integer()}.
  360. perf1() ->
  361. {ok, Bin} = file:read_file("bin"),
  362. A = binary_to_term(Bin),
  363. {N, _} = timer:tc(trunc_io, print, [A, 1500]),
  364. {M, _} = timer:tc(io_lib, write, [A]),
  365. {N, M}.
  366. format_test() ->
  367. %% simple format strings
  368. ?assertEqual("foobar", lists:flatten(format("~s", [["foo", $b, $a, $r]], 50))),
  369. ?assertEqual("[\"foo\",98,97,114]", lists:flatten(format("~p", [["foo", $b, $a, $r]], 50))),
  370. ?assertEqual("[\"foo\",98,97,114]", lists:flatten(format("~P", [["foo", $b, $a, $r], 10], 50))),
  371. ?assertEqual("[[102,111,111],98,97,114]", lists:flatten(format("~w", [["foo", $b, $a, $r]], 50))),
  372. %% complex ones
  373. ?assertEqual(" foobar", lists:flatten(format("~10s", [["foo", $b, $a, $r]], 50))),
  374. ?assertEqual(" [\"foo\",98,97,114]", lists:flatten(format("~22p", [["foo", $b, $a, $r]], 50))),
  375. ?assertEqual(" [\"foo\",98,97,114]", lists:flatten(format("~22P", [["foo", $b, $a, $r], 10], 50))),
  376. ?assertEqual("**********", lists:flatten(format("~10W", [["foo", $b, $a, $r], 10], 50))),
  377. ?assertEqual("[[102,111,111],98,97,114]", lists:flatten(format("~25W", [["foo", $b, $a, $r], 10], 50))),
  378. ok.
  379. atom_quoting_test() ->
  380. ?assertEqual("hello", lists:flatten(format("~p", [hello], 50))),
  381. ?assertEqual("'hello world'", lists:flatten(format("~p", ['hello world'], 50))),
  382. ?assertEqual("'Hello world'", lists:flatten(format("~p", ['Hello world'], 50))),
  383. ?assertEqual("hello_world", lists:flatten(format("~p", ['hello_world'], 50))),
  384. ?assertEqual("'node@127.0.0.1'", lists:flatten(format("~p", ['node@127.0.0.1'], 50))),
  385. ?assertEqual("node@nohost", lists:flatten(format("~p", [node@nohost], 50))),
  386. ?assertEqual("abc123", lists:flatten(format("~p", [abc123], 50))),
  387. ok.
  388. sane_float_printing_test() ->
  389. ?assertEqual("1.0", lists:flatten(format("~p", [1.0], 50))),
  390. ?assertEqual("1.23456789", lists:flatten(format("~p", [1.23456789], 50))),
  391. ?assertEqual("1.23456789", lists:flatten(format("~p", [1.234567890], 50))),
  392. ?assertEqual("0.3333333333333333", lists:flatten(format("~p", [1/3], 50))),
  393. ?assertEqual("0.1234567", lists:flatten(format("~p", [0.1234567], 50))),
  394. ok.
  395. float_inside_list_test() ->
  396. ?assertEqual("[97,38.233913133184835,99]", lists:flatten(format("~p", [[$a, 38.233913133184835, $c]], 50))),
  397. ?assertError(badarg, lists:flatten(format("~s", [[$a, 38.233913133184835, $c]], 50))),
  398. ok.
  399. quote_strip_test() ->
  400. ?assertEqual("\"hello\"", lists:flatten(format("~p", ["hello"], 50))),
  401. ?assertEqual("hello", lists:flatten(format("~s", ["hello"], 50))),
  402. ?assertEqual("hello", lists:flatten(format("~s", [hello], 50))),
  403. ?assertEqual("hello", lists:flatten(format("~p", [hello], 50))),
  404. ?assertEqual("'hello world'", lists:flatten(format("~p", ['hello world'], 50))),
  405. ?assertEqual("hello world", lists:flatten(format("~s", ['hello world'], 50))),
  406. ok.
  407. binary_printing_test() ->
  408. ?assertEqual("<<>>", lists:flatten(format("~p", [<<>>], 50))),
  409. ?assertEqual("<<..>>", lists:flatten(format("~p", [<<"hi">>], 0))),
  410. ?assertEqual("<<...>>", lists:flatten(format("~p", [<<"hi">>], 1))),
  411. ?assertEqual("<<\"hello\">>", lists:flatten(format("~p", [<<$h, $e, $l, $l, $o>>], 50))),
  412. ?assertEqual("<<\"hello\">>", lists:flatten(format("~p", [<<"hello">>], 50))),
  413. ?assertEqual("<<104,101,108,108,111>>", lists:flatten(format("~w", [<<"hello">>], 50))),
  414. ?assertEqual("<<1,2,3,4>>", lists:flatten(format("~p", [<<1, 2, 3, 4>>], 50))),
  415. ?assertEqual([1,2,3,4], lists:flatten(format("~s", [<<1, 2, 3, 4>>], 50))),
  416. ?assertEqual("hello", lists:flatten(format("~s", [<<"hello">>], 50))),
  417. ?assertEqual("hello\nworld", lists:flatten(format("~s", [<<"hello\nworld">>], 50))),
  418. ?assertEqual("<<\"hello\\nworld\">>", lists:flatten(format("~p", [<<"hello\nworld">>], 50))),
  419. ?assertEqual(" hello", lists:flatten(format("~10s", [<<"hello">>], 50))),
  420. ok.
  421. bitstring_printing_test() ->
  422. ?assertEqual("<<1,2,3,1:7>>", lists:flatten(format("~p",
  423. [<<1, 2, 3, 1:7>>], 100))),
  424. ?assertEqual("<<1:7>>", lists:flatten(format("~p",
  425. [<<1:7>>], 100))),
  426. ?assertEqual("<<1,2,3,...>>", lists:flatten(format("~p",
  427. [<<1, 2, 3, 1:7>>], 12))),
  428. ?assertEqual("<<1,2,3,...>>", lists:flatten(format("~p",
  429. [<<1, 2, 3, 1:7>>], 13))),
  430. ?assertEqual("<<1,2,3,1:7>>", lists:flatten(format("~p",
  431. [<<1, 2, 3, 1:7>>], 14))),
  432. ?assertEqual("<<..>>", lists:flatten(format("~p", [<<1:7>>], 0))),
  433. ?assertEqual("<<...>>", lists:flatten(format("~p", [<<1:7>>], 1))),
  434. ?assertEqual("[<<1>>,<<2>>]", lists:flatten(format("~p", [[<<1>>, <<2>>]],
  435. 100))),
  436. ok.
  437. list_printing_test() ->
  438. ?assertEqual("[]", lists:flatten(format("~p", [[]], 50))),
  439. ?assertEqual("[]", lists:flatten(format("~w", [[]], 50))),
  440. ?assertEqual("", lists:flatten(format("~s", [[]], 50))),
  441. ?assertEqual("...", lists:flatten(format("~s", [[]], -1))),
  442. ?assertEqual("[[]]", lists:flatten(format("~p", [[[]]], 50))),
  443. ?assertEqual("[13,11,10,8,5,4]", lists:flatten(format("~p", [[13,11,10,8,5,4]], 50))),
  444. ?assertEqual("\"\\rabc\"", lists:flatten(format("~p", [[13,$a, $b, $c]], 50))),
  445. ?assertEqual("[1,2,3|4]", lists:flatten(format("~p", [[1, 2, 3|4]], 50))),
  446. ?assertEqual("[...]", lists:flatten(format("~p", [[1, 2, 3,4]], 4))),
  447. ?assertEqual("[1,...]", lists:flatten(format("~p", [[1, 2, 3, 4]], 6))),
  448. ?assertEqual("[1,...]", lists:flatten(format("~p", [[1, 2, 3, 4]], 7))),
  449. ?assertEqual("[1,2,...]", lists:flatten(format("~p", [[1, 2, 3, 4]], 8))),
  450. ?assertEqual("[1|4]", lists:flatten(format("~p", [[1|4]], 50))),
  451. ?assertEqual("[1]", lists:flatten(format("~p", [[1]], 50))),
  452. ?assertError(badarg, lists:flatten(format("~s", [[1|4]], 50))),
  453. ?assertEqual("\"hello...\"", lists:flatten(format("~p", ["hello world"], 10))),
  454. ?assertEqual("hello w...", lists:flatten(format("~s", ["hello world"], 10))),
  455. ?assertEqual("hello world\r\n", lists:flatten(format("~s", ["hello world\r\n"], 50))),
  456. ?assertEqual("\rhello world\r\n", lists:flatten(format("~s", ["\rhello world\r\n"], 50))),
  457. ?assertEqual("\"\\rhello world\\r\\n\"", lists:flatten(format("~p", ["\rhello world\r\n"], 50))),
  458. ?assertEqual("[13,104,101,108,108,111,32,119,111,114,108,100,13,10]", lists:flatten(format("~w", ["\rhello world\r\n"], 60))),
  459. ?assertEqual("...", lists:flatten(format("~s", ["\rhello world\r\n"], 3))),
  460. ?assertEqual("[22835963083295358096932575511191922182123945984,...]",
  461. lists:flatten(format("~p", [
  462. [22835963083295358096932575511191922182123945984,
  463. 22835963083295358096932575511191922182123945984]], 9))),
  464. ?assertEqual("[22835963083295358096932575511191922182123945984,...]",
  465. lists:flatten(format("~p", [
  466. [22835963083295358096932575511191922182123945984,
  467. 22835963083295358096932575511191922182123945984]], 53))),
  468. ok.
  469. tuple_printing_test() ->
  470. ?assertEqual("{}", lists:flatten(format("~p", [{}], 50))),
  471. ?assertEqual("{}", lists:flatten(format("~w", [{}], 50))),
  472. ?assertError(badarg, lists:flatten(format("~s", [{}], 50))),
  473. ?assertEqual("{...}", lists:flatten(format("~p", [{foo}], 1))),
  474. ?assertEqual("{...}", lists:flatten(format("~p", [{foo}], 2))),
  475. ?assertEqual("{...}", lists:flatten(format("~p", [{foo}], 3))),
  476. ?assertEqual("{...}", lists:flatten(format("~p", [{foo}], 4))),
  477. ?assertEqual("{...}", lists:flatten(format("~p", [{foo}], 5))),
  478. ?assertEqual("{foo,...}", lists:flatten(format("~p", [{foo,bar}], 6))),
  479. ?assertEqual("{foo,...}", lists:flatten(format("~p", [{foo,bar}], 7))),
  480. ?assertEqual("{foo,...}", lists:flatten(format("~p", [{foo,bar}], 9))),
  481. ?assertEqual("{foo,bar}", lists:flatten(format("~p", [{foo,bar}], 10))),
  482. ?assertEqual("{22835963083295358096932575511191922182123945984,...}",
  483. lists:flatten(format("~w", [
  484. {22835963083295358096932575511191922182123945984,
  485. 22835963083295358096932575511191922182123945984}], 10))),
  486. ?assertEqual("{22835963083295358096932575511191922182123945984,...}",
  487. lists:flatten(format("~w", [
  488. {22835963083295358096932575511191922182123945984,
  489. bar}], 10))),
  490. ?assertEqual("{22835963083295358096932575511191922182123945984,...}",
  491. lists:flatten(format("~w", [
  492. {22835963083295358096932575511191922182123945984,
  493. 22835963083295358096932575511191922182123945984}], 53))),
  494. ok.
  495. unicode_test() ->
  496. ?assertEqual([231,167,129], lists:flatten(format("~s", [<<231,167,129>>], 50))),
  497. ?assertEqual([31169], lists:flatten(format("~ts", [<<231,167,129>>], 50))),
  498. ok.
  499. depth_limit_test() ->
  500. ?assertEqual("{...}", lists:flatten(format("~P", [{a, [b, [c, [d]]]}, 1], 50))),
  501. ?assertEqual("{a,...}", lists:flatten(format("~P", [{a, [b, [c, [d]]]}, 2], 50))),
  502. ?assertEqual("{a,[...]}", lists:flatten(format("~P", [{a, [b, [c, [d]]]}, 3], 50))),
  503. ?assertEqual("{a,[b|...]}", lists:flatten(format("~P", [{a, [b, [c, [d]]]}, 4], 50))),
  504. ?assertEqual("{a,[b,[...]]}", lists:flatten(format("~P", [{a, [b, [c, [d]]]}, 5], 50))),
  505. ?assertEqual("{a,[b,[c|...]]}", lists:flatten(format("~P", [{a, [b, [c, [d]]]}, 6], 50))),
  506. ?assertEqual("{a,[b,[c,[...]]]}", lists:flatten(format("~P", [{a, [b, [c, [d]]]}, 7], 50))),
  507. ?assertEqual("{a,[b,[c,[d]]]}", lists:flatten(format("~P", [{a, [b, [c, [d]]]}, 8], 50))),
  508. ?assertEqual("{a,[b,[c,[d]]]}", lists:flatten(format("~P", [{a, [b, [c, [d]]]}, 9], 50))),
  509. ?assertEqual("{a,{...}}", lists:flatten(format("~P", [{a, {b, {c, {d}}}}, 3], 50))),
  510. ?assertEqual("{a,{b,...}}", lists:flatten(format("~P", [{a, {b, {c, {d}}}}, 4], 50))),
  511. ?assertEqual("{a,{b,{...}}}", lists:flatten(format("~P", [{a, {b, {c, {d}}}}, 5], 50))),
  512. ?assertEqual("{a,{b,{c,...}}}", lists:flatten(format("~P", [{a, {b, {c, {d}}}}, 6], 50))),
  513. ?assertEqual("{a,{b,{c,{...}}}}", lists:flatten(format("~P", [{a, {b, {c, {d}}}}, 7], 50))),
  514. ?assertEqual("{a,{b,{c,{d}}}}", lists:flatten(format("~P", [{a, {b, {c, {d}}}}, 8], 50))),
  515. ?assertEqual("{\"a\",[...]}", lists:flatten(format("~P", [{"a", ["b", ["c", ["d"]]]}, 3], 50))),
  516. ?assertEqual("{\"a\",[\"b\",[[...]|...]]}", lists:flatten(format("~P", [{"a", ["b", ["c", ["d"]]]}, 6], 50))),
  517. ?assertEqual("{\"a\",[\"b\",[\"c\",[\"d\"]]]}", lists:flatten(format("~P", [{"a", ["b", ["c", ["d"]]]}, 9], 50))),
  518. ok.
  519. -endif.