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 33-module(lager_trunc_io). 34-author('matthias@corelatus.se'). 35%% And thanks to Chris Newcombe for a bug fix 36-export([format/3, format/4, print/2, print/3, fprint/2, fprint/3, safe/2]). % interface functions 37-version("$Id: trunc_io.erl,v 1.11 2009-02-23 12:01:06 matthias Exp $"). 38 39-ifdef(TEST). 40-export([perf/0, perf/3, perf1/0, test/0, test/2]). % testing functions 41-include_lib("eunit/include/eunit.hrl"). 42-endif. 43 44-type option() :: {'depth', integer()} 45 | {'lists_as_strings', boolean()} 46 | {'force_strings', boolean()}. 47-type options() :: [option()]. 48 49-record(print_options, { 50 %% negative depth means no depth limiting 51 depth = -1 :: integer(), 52 %% whether to print lists as strings, if possible 53 lists_as_strings = true :: boolean(), 54 %% force strings, or binaries to be printed as a string, 55 %% even if they're not printable 56 force_strings = false :: boolean() 57 }). 58 59format(Fmt, Args, Max) -> 60 format(Fmt, Args, Max, []). 61 62format(Fmt, Args, Max, Options) -> 63 try lager_format:format(Fmt, Args, Max, Options) 64 catch 65 _What:_Why -> 66 erlang:error(badarg, [Fmt, Args]) 67 end. 68 69%% @doc Returns an flattened list containing the ASCII representation of the given 70%% term. 71-spec fprint(term(), pos_integer()) -> string(). 72fprint(Term, Max) -> 73 fprint(Term, Max, []). 74 75 76%% @doc Returns an flattened list containing the ASCII representation of the given 77%% term. 78-spec fprint(term(), pos_integer(), options()) -> string(). 79fprint(T, Max, Options) -> 80 {L, _} = print(T, Max, prepare_options(Options, #print_options{})), 81 lists:flatten(L). 82 83%% @doc Same as print, but never crashes. 84%% 85%% This is a tradeoff. Print might conceivably crash if it's asked to 86%% print something it doesn't understand, for example some new data 87%% type in a future version of Erlang. If print crashes, we fall back 88%% to io_lib to format the term, but then the formatting is 89%% depth-limited instead of length limited, so you might run out 90%% memory printing it. Out of the frying pan and into the fire. 91%% 92-spec safe(term(), pos_integer()) -> {string(), pos_integer()} | {string()}. 93safe(What, Len) -> 94 case catch print(What, Len) of 95 {L, Used} when is_list(L) -> {L, Used}; 96 _ -> {"unable to print" ++ io_lib:write(What, 99)} 97 end. 98 99%% @doc Returns {List, Length} 100-spec print(term(), pos_integer()) -> {iolist(), pos_integer()}. 101print(Term, Max) -> 102 print(Term, Max, []). 103 104%% @doc Returns {List, Length} 105-spec print(term(), pos_integer(), options() | #print_options{}) -> {iolist(), pos_integer()}. 106print(Term, Max, Options) when is_list(Options) -> 107 %% need to convert the proplist to a record 108 print(Term, Max, prepare_options(Options, #print_options{})); 109 110print(Term, _Max, #print_options{force_strings=true}) when not is_list(Term), not is_binary(Term), not is_atom(Term) -> 111 erlang:error(badarg); 112 113print(_, Max, _Options) when Max < 0 -> {"...", 3}; 114print(_, _, #print_options{depth=0}) -> {"...", 3}; 115 116 117%% @doc We assume atoms, floats, funs, integers, PIDs, ports and refs never need 118%% to be truncated. This isn't strictly true, someone could make an 119%% arbitrarily long bignum. Let's assume that won't happen unless someone 120%% is being malicious. 121%% 122print(Atom, _Max, #print_options{force_strings=NoQuote}) when is_atom(Atom) -> 123 L = atom_to_list(Atom), 124 R = case atom_needs_quoting_start(L) andalso not NoQuote of 125 true -> lists:flatten([$', L, $']); 126 false -> L 127 end, 128 {R, length(R)}; 129 130print(<<>>, _Max, #print_options{depth=1}) -> 131 {"<<>>", 4}; 132print(Bin, _Max, #print_options{depth=1}) when is_binary(Bin) -> 133 {"<<...>>", 7}; 134print(<<>>, _Max, Options) -> 135 case Options#print_options.force_strings of 136 true -> 137 {"", 0}; 138 false -> 139 {"<<>>", 4} 140 end; 141 142print(Binary, 0, _Options) when is_bitstring(Binary) -> 143 {"<<..>>", 6}; 144 145print(Bin, Max, _Options) when is_binary(Bin), Max < 2 -> 146 {"<<...>>", 7}; 147print(Binary, Max, Options) when is_binary(Binary) -> 148 B = binary_to_list(Binary, 1, lists:min([Max, byte_size(Binary)])), 149 {Res, Length} = case Options#print_options.lists_as_strings orelse 150 Options#print_options.force_strings of 151 true -> 152 Depth = Options#print_options.depth, 153 MaxSize = (Depth - 1) * 4, 154 %% check if we need to truncate based on depth 155 In = case Depth > -1 andalso MaxSize < length(B) andalso 156 not Options#print_options.force_strings of 157 true -> 158 string:substr(B, 1, MaxSize); 159 false -> B 160 end, 161 MaxLen = case Options#print_options.force_strings of 162 true -> 163 Max; 164 false -> 165 %% make room for the leading doublequote 166 Max - 1 167 end, 168 try alist(In, MaxLen, Options) of 169 {L0, Len0} -> 170 case Options#print_options.force_strings of 171 false -> 172 case B /= In of 173 true -> 174 {[$", L0, "..."], Len0+4}; 175 false -> 176 {[$"|L0], Len0+1} 177 end; 178 true -> 179 {L0, Len0} 180 end 181 catch 182 throw:{unprintable, C} -> 183 Index = string:chr(In, C), 184 case Index > 1 andalso Options#print_options.depth =< Index andalso 185 Options#print_options.depth > -1 andalso 186 not Options#print_options.force_strings of 187 true -> 188 %% print first Index-1 characters followed by ... 189 {L0, Len0} = alist_start(string:substr(In, 1, Index - 1), Max - 1, Options), 190 {L0++"...", Len0+3}; 191 false -> 192 list_body(In, Max-4, dec_depth(Options), true) 193 end 194 end; 195 _ -> 196 list_body(B, Max-4, dec_depth(Options), true) 197 end, 198 case Options#print_options.force_strings of 199 true -> 200 {Res, Length}; 201 _ -> 202 {["<<", Res, ">>"], Length+4} 203 end; 204 205%% bitstrings are binary's evil brother who doesn't end on an 8 bit boundary. 206%% This makes printing them extremely annoying, so list_body/list_bodyc has 207%% some magic for dealing with the output of bitstring_to_list, which returns 208%% a list of integers (as expected) but with a trailing binary that represents 209%% the remaining bits. 210print({inline_bitstring, B}, _Max, _Options) when is_bitstring(B) -> 211 Size = bit_size(B), 212 <<Value:Size>> = B, 213 ValueStr = integer_to_list(Value), 214 SizeStr = integer_to_list(Size), 215 {[ValueStr, $:, SizeStr], length(ValueStr) + length(SizeStr) +1}; 216print(BitString, Max, Options) when is_bitstring(BitString) -> 217 BL = case byte_size(BitString) > Max of 218 true -> 219 binary_to_list(BitString, 1, Max); 220 _ -> 221 R = erlang:bitstring_to_list(BitString), 222 {Bytes, [Bits]} = lists:splitwith(fun erlang:is_integer/1, R), 223 %% tag the trailing bits with a special tuple we catch when 224 %% list_body calls print again 225 Bytes ++ [{inline_bitstring, Bits}] 226 end, 227 {X, Len0} = list_body(BL, Max - 4, dec_depth(Options), true), 228 {["<<", X, ">>"], Len0 + 4}; 229 230print(Float, _Max, _Options) when is_float(Float) -> 231 %% use the same function io_lib:format uses to print floats 232 %% float_to_list is way too verbose. 233 L = io_lib_format:fwrite_g(Float), 234 {L, length(L)}; 235 236print(Fun, Max, _Options) when is_function(Fun) -> 237 L = erlang:fun_to_list(Fun), 238 case length(L) > Max of 239 true -> 240 S = erlang:max(5, Max), 241 Res = string:substr(L, 1, S) ++ "..>", 242 {Res, length(Res)}; 243 _ -> 244 {L, length(L)} 245 end; 246 247print(Integer, _Max, _Options) when is_integer(Integer) -> 248 L = integer_to_list(Integer), 249 {L, length(L)}; 250 251print(Pid, _Max, _Options) when is_pid(Pid) -> 252 L = pid_to_list(Pid), 253 {L, length(L)}; 254 255print(Ref, _Max, _Options) when is_reference(Ref) -> 256 L = erlang:ref_to_list(Ref), 257 {L, length(L)}; 258 259print(Port, _Max, _Options) when is_port(Port) -> 260 L = erlang:port_to_list(Port), 261 {L, length(L)}; 262 263print({'$lager_record', Name, Fields}, Max, Options) -> 264 Leader = "#" ++ atom_to_list(Name) ++ "{", 265 {RC, Len} = record_fields(Fields, Max - length(Leader) + 1, dec_depth(Options)), 266 {[Leader, RC, "}"], Len + length(Leader) + 1}; 267 268print(Tuple, Max, Options) when is_tuple(Tuple) -> 269 {TC, Len} = tuple_contents(Tuple, Max-2, Options), 270 {[${, TC, $}], Len + 2}; 271 272print(List, Max, Options) when is_list(List) -> 273 case Options#print_options.lists_as_strings orelse 274 Options#print_options.force_strings of 275 true -> 276 alist_start(List, Max, dec_depth(Options)); 277 _ -> 278 {R, Len} = list_body(List, Max - 2, dec_depth(Options), false), 279 {[$[, R, $]], Len + 2} 280 end; 281 282print(Map, Max, Options) -> 283 case erlang:is_builtin(erlang, is_map, 1) andalso erlang:is_map(Map) of 284 true -> 285 {MapBody, Len} = map_body(Map, Max - 3, dec_depth(Options)), 286 {[$#, ${, MapBody, $}], Len + 3}; 287 false -> 288 error(badarg, [Map, Max, Options]) 289 end. 290 291%% Returns {List, Length} 292tuple_contents(Tuple, Max, Options) -> 293 L = tuple_to_list(Tuple), 294 list_body(L, Max, dec_depth(Options), true). 295 296%% Format the inside of a list, i.e. do not add a leading [ or trailing ]. 297%% Returns {List, Length} 298list_body([], _Max, _Options, _Tuple) -> {[], 0}; 299list_body(_, Max, _Options, _Tuple) when Max < 4 -> {"...", 3}; 300list_body(_, _Max, #print_options{depth=0}, _Tuple) -> {"...", 3}; 301list_body([H], Max, Options=#print_options{depth=1}, _Tuple) -> 302 print(H, Max, Options); 303list_body([H|_], Max, Options=#print_options{depth=1}, Tuple) -> 304 {List, Len} = print(H, Max-4, Options), 305 Sep = case Tuple of 306 true -> $,; 307 false -> $| 308 end, 309 {[List ++ [Sep | "..."]], Len + 4}; 310list_body([H|T], Max, Options, Tuple) -> 311 {List, Len} = print(H, Max, Options), 312 {Final, FLen} = list_bodyc(T, Max - Len, Options, Tuple), 313 {[List|Final], FLen + Len}; 314list_body(X, Max, Options, _Tuple) -> %% improper list 315 {List, Len} = print(X, Max - 1, Options), 316 {[$|,List], Len + 1}. 317 318list_bodyc([], _Max, _Options, _Tuple) -> {[], 0}; 319list_bodyc(_, Max, _Options, _Tuple) when Max < 5 -> {",...", 4}; 320list_bodyc(_, _Max, #print_options{depth=1}, true) -> {",...", 4}; 321list_bodyc(_, _Max, #print_options{depth=1}, false) -> {"|...", 4}; 322list_bodyc([H|T], Max, #print_options{depth=Depth} = Options, Tuple) -> 323 {List, Len} = print(H, Max, dec_depth(Options)), 324 {Final, FLen} = list_bodyc(T, Max - Len - 1, dec_depth(Options), Tuple), 325 Sep = case Depth == 1 andalso not Tuple of 326 true -> $|; 327 _ -> $, 328 end, 329 {[Sep, List|Final], FLen + Len + 1}; 330list_bodyc(X, Max, Options, _Tuple) -> %% improper list 331 {List, Len} = print(X, Max - 1, Options), 332 {[$|,List], Len + 1}. 333 334map_body(Map, Max, #print_options{depth=Depth}) when Max < 4; Depth =:= 0 -> 335 case erlang:map_size(Map) of 336 0 -> {[], 0}; 337 _ -> {"...", 3} 338 end; 339map_body(Map, Max, Options) -> 340 case maps:to_list(Map) of 341 [] -> 342 {[], 0}; 343 [{Key, Value} | Rest] -> 344 {KeyStr, KeyLen} = print(Key, Max - 4, Options), 345 DiffLen = KeyLen + 4, 346 {ValueStr, ValueLen} = print(Value, Max - DiffLen, Options), 347 DiffLen2 = DiffLen + ValueLen, 348 {Final, FLen} = map_bodyc(Rest, Max - DiffLen2, dec_depth(Options)), 349 {[KeyStr, " => ", ValueStr | Final], DiffLen2 + FLen} 350 end. 351 352map_bodyc([], _Max, _Options) -> 353 {[], 0}; 354map_bodyc(_Rest, Max,#print_options{depth=Depth}) when Max < 5; Depth =:= 0 -> 355 {",...", 4}; 356map_bodyc([{Key, Value} | Rest], Max, Options) -> 357 {KeyStr, KeyLen} = print(Key, Max - 5, Options), 358 DiffLen = KeyLen + 5, 359 {ValueStr, ValueLen} = print(Value, Max - DiffLen, Options), 360 DiffLen2 = DiffLen + ValueLen, 361 {Final, FLen} = map_bodyc(Rest, Max - DiffLen2, dec_depth(Options)), 362 {[$,, KeyStr, " => ", ValueStr | Final], DiffLen2 + FLen}. 363 364%% The head of a list we hope is ascii. Examples: 365%% 366%% [65,66,67] -> "ABC" 367%% [65,0,67] -> "A"[0,67] 368%% [0,65,66] -> [0,65,66] 369%% [65,b,66] -> "A"[b,66] 370%% 371alist_start([], _Max, #print_options{force_strings=true}) -> {"", 0}; 372alist_start([], _Max, _Options) -> {"[]", 2}; 373alist_start(_, Max, _Options) when Max < 4 -> {"...", 3}; 374alist_start(_, _Max, #print_options{depth=0}) -> {"[...]", 5}; 375alist_start(L, Max, #print_options{force_strings=true} = Options) -> 376 alist(L, Max, Options); 377%alist_start([H|_T], _Max, #print_options{depth=1}) when is_integer(H) -> {[$[, H, $|, $., $., $., $]], 7}; 378alist_start([H|T], Max, Options) when is_integer(H), H >= 16#20, H =< 16#7e -> % definitely printable 379 try alist([H|T], Max -1, Options) of 380 {L, Len} -> 381 {[$"|L], Len + 1} 382 catch 383 throw:{unprintable, _} -> 384 {R, Len} = list_body([H|T], Max-2, Options, false), 385 {[$[, R, $]], Len + 2} 386 end; 387alist_start([H|T], Max, Options) when is_integer(H), H >= 16#a0, H =< 16#ff -> % definitely printable 388 try alist([H|T], Max -1, Options) of 389 {L, Len} -> 390 {[$"|L], Len + 1} 391 catch 392 throw:{unprintable, _} -> 393 {R, Len} = list_body([H|T], Max-2, Options, false), 394 {[$[, R, $]], Len + 2} 395 end; 396alist_start([H|T], Max, Options) when H =:= $\t; H =:= $\n; H =:= $\r; H =:= $\v; H =:= $\e; H=:= $\f; H=:= $\b -> 397 try alist([H|T], Max -1, Options) of 398 {L, Len} -> 399 {[$"|L], Len + 1} 400 catch 401 throw:{unprintable, _} -> 402 {R, Len} = list_body([H|T], Max-2, Options, false), 403 {[$[, R, $]], Len + 2} 404 end; 405alist_start(L, Max, Options) -> 406 {R, Len} = list_body(L, Max-2, Options, false), 407 {[$[, R, $]], Len + 2}. 408 409alist([], _Max, #print_options{force_strings=true}) -> {"", 0}; 410alist([], _Max, _Options) -> {"\"", 1}; 411alist(_, Max, #print_options{force_strings=true}) when Max < 4 -> {"...", 3}; 412alist(_, Max, #print_options{force_strings=false}) when Max < 5 -> {"...\"", 4}; 413alist([H|T], Max, Options = #print_options{force_strings=false,lists_as_strings=true}) when H =:= $"; H =:= $\\ -> 414 %% preserve escaping around quotes 415 {L, Len} = alist(T, Max-1, Options), 416 {[$\\,H|L], Len + 2}; 417alist([H|T], Max, Options) when is_integer(H), H >= 16#20, H =< 16#7e -> % definitely printable 418 {L, Len} = alist(T, Max-1, Options), 419 {[H|L], Len + 1}; 420alist([H|T], Max, Options) when is_integer(H), H >= 16#a0, H =< 16#ff -> % definitely printable 421 {L, Len} = alist(T, Max-1, Options), 422 {[H|L], Len + 1}; 423alist([H|T], Max, Options) when H =:= $\t; H =:= $\n; H =:= $\r; H =:= $\v; H =:= $\e; H=:= $\f; H=:= $\b -> 424 {L, Len} = alist(T, Max-1, Options), 425 case Options#print_options.force_strings of 426 true -> 427 {[H|L], Len + 1}; 428 _ -> 429 {[escape(H)|L], Len + 1} 430 end; 431alist([H|T], Max, #print_options{force_strings=true} = Options) when is_integer(H) -> 432 {L, Len} = alist(T, Max-1, Options), 433 {[H|L], Len + 1}; 434alist([H|T], Max, Options = #print_options{force_strings=true}) when is_binary(H); is_list(H) -> 435 {List, Len} = print(H, Max, Options), 436 case (Max - Len) =< 0 of 437 true -> 438 %% no more room to print anything 439 {List, Len}; 440 false -> 441 %% no need to decrement depth, as we're in printable string mode 442 {Final, FLen} = alist(T, Max - Len, Options), 443 {[List|Final], FLen+Len} 444 end; 445alist(_, _, #print_options{force_strings=true}) -> 446 erlang:error(badarg); 447alist([H|_L], _Max, _Options) -> 448 throw({unprintable, H}); 449alist(H, _Max, _Options) -> 450 %% improper list 451 throw({unprintable, H}). 452 453%% is the first character in the atom alphabetic & lowercase? 454atom_needs_quoting_start([H|T]) when H >= $a, H =< $z -> 455 atom_needs_quoting(T); 456atom_needs_quoting_start(_) -> 457 true. 458 459atom_needs_quoting([]) -> 460 false; 461atom_needs_quoting([H|T]) when (H >= $a andalso H =< $z); 462 (H >= $A andalso H =< $Z); 463 (H >= $0 andalso H =< $9); 464 H == $@; H == $_ -> 465 atom_needs_quoting(T); 466atom_needs_quoting(_) -> 467 true. 468 469-spec prepare_options(options(), #print_options{}) -> #print_options{}. 470prepare_options([], Options) -> 471 Options; 472prepare_options([{depth, Depth}|T], Options) when is_integer(Depth) -> 473 prepare_options(T, Options#print_options{depth=Depth}); 474prepare_options([{lists_as_strings, Bool}|T], Options) when is_boolean(Bool) -> 475 prepare_options(T, Options#print_options{lists_as_strings = Bool}); 476prepare_options([{force_strings, Bool}|T], Options) when is_boolean(Bool) -> 477 prepare_options(T, Options#print_options{force_strings = Bool}). 478 479dec_depth(#print_options{depth=Depth} = Options) when Depth > 0 -> 480 Options#print_options{depth=Depth-1}; 481dec_depth(Options) -> 482 Options. 483 484escape($\t) -> "\\t"; 485escape($\n) -> "\\n"; 486escape($\r) -> "\\r"; 487escape($\e) -> "\\e"; 488escape($\f) -> "\\f"; 489escape($\b) -> "\\b"; 490escape($\v) -> "\\v". 491 492record_fields([], _, _) -> 493 {"", 0}; 494record_fields(_, Max, #print_options{depth=D}) when Max < 4; D == 0 -> 495 {"...", 3}; 496record_fields([{Field, Value}|T], Max, Options) -> 497 {ExtraChars, Terminator} = case T of 498 [] -> 499 {1, []}; 500 _ -> 501 {2, ","} 502 end, 503 {FieldStr, FieldLen} = print(Field, Max - ExtraChars, Options), 504 {ValueStr, ValueLen} = print(Value, Max - (FieldLen + ExtraChars), Options), 505 {Final, FLen} = record_fields(T, Max - (FieldLen + ValueLen + ExtraChars), dec_depth(Options)), 506 {[FieldStr++"="++ValueStr++Terminator|Final], FLen + FieldLen + ValueLen + ExtraChars}. 507 508 509-ifdef(TEST). 510%%-------------------- 511%% The start of a test suite. So far, it only checks for not crashing. 512-spec test() -> ok. 513test() -> 514 test(trunc_io, print). 515 516-spec test(atom(), atom()) -> ok. 517test(Mod, Func) -> 518 Simple_items = [atom, 1234, 1234.0, {tuple}, [], [list], "string", self(), 519 <<1,2,3>>, make_ref(), fun() -> ok end], 520 F = fun(A) -> 521 Mod:Func(A, 100), 522 Mod:Func(A, 2), 523 Mod:Func(A, 20) 524 end, 525 526 G = fun(A) -> 527 case catch F(A) of 528 {'EXIT', _} -> exit({failed, A}); 529 _ -> ok 530 end 531 end, 532 533 lists:foreach(G, Simple_items), 534 535 Tuples = [ {1,2,3,a,b,c}, {"abc", def, 1234}, 536 {{{{a},b,c,{d},e}},f}], 537 538 Lists = [ [1,2,3,4,5,6,7], lists:seq(1,1000), 539 [{a}, {a,b}, {a, [b,c]}, "def"], [a|b], [$a|$b] ], 540 541 542 lists:foreach(G, Tuples), 543 lists:foreach(G, Lists). 544 545-spec perf() -> ok. 546perf() -> 547 {New, _} = timer:tc(trunc_io, perf, [trunc_io, print, 1000]), 548 {Old, _} = timer:tc(trunc_io, perf, [io_lib, write, 1000]), 549 io:fwrite("New code took ~p us, old code ~p\n", [New, Old]). 550 551-spec perf(atom(), atom(), integer()) -> done. 552perf(M, F, Reps) when Reps > 0 -> 553 test(M,F), 554 perf(M,F,Reps-1); 555perf(_,_,_) -> 556 done. 557 558%% Performance test. Needs a particularly large term I saved as a binary... 559-spec perf1() -> {non_neg_integer(), non_neg_integer()}. 560perf1() -> 561 {ok, Bin} = file:read_file("bin"), 562 A = binary_to_term(Bin), 563 {N, _} = timer:tc(trunc_io, print, [A, 1500]), 564 {M, _} = timer:tc(io_lib, write, [A]), 565 {N, M}. 566 567format_test() -> 568 %% simple format strings 569 ?assertEqual("foobar", lists:flatten(format("~s", [["foo", $b, $a, $r]], 50))), 570 ?assertEqual("[\"foo\",98,97,114]", lists:flatten(format("~p", [["foo", $b, $a, $r]], 50))), 571 ?assertEqual("[\"foo\",98,97,114]", lists:flatten(format("~P", [["foo", $b, $a, $r], 10], 50))), 572 ?assertEqual("[[102,111,111],98,97,114]", lists:flatten(format("~w", [["foo", $b, $a, $r]], 50))), 573 574 %% complex ones 575 ?assertEqual(" foobar", lists:flatten(format("~10s", [["foo", $b, $a, $r]], 50))), 576 ?assertEqual("f", lists:flatten(format("~1s", [["foo", $b, $a, $r]], 50))), 577 ?assertEqual("[\"foo\",98,97,114]", lists:flatten(format("~22p", [["foo", $b, $a, $r]], 50))), 578 ?assertEqual("[\"foo\",98,97,114]", lists:flatten(format("~22P", [["foo", $b, $a, $r], 10], 50))), 579 ?assertEqual("**********", lists:flatten(format("~10W", [["foo", $b, $a, $r], 10], 50))), 580 ?assertEqual("[[102,111,111],98,97,114]", lists:flatten(format("~25W", [["foo", $b, $a, $r], 10], 50))), 581 % Note these next two diverge from io_lib:format; the field width is 582 % ignored, when it should be used as max line length. 583 ?assertEqual("[\"foo\",98,97,114]", lists:flatten(format("~10p", [["foo", $b, $a, $r]], 50))), 584 ?assertEqual("[\"foo\",98,97,114]", lists:flatten(format("~10P", [["foo", $b, $a, $r], 10], 50))), 585 ok. 586 587atom_quoting_test() -> 588 ?assertEqual("hello", lists:flatten(format("~p", [hello], 50))), 589 ?assertEqual("'hello world'", lists:flatten(format("~p", ['hello world'], 50))), 590 ?assertEqual("'Hello world'", lists:flatten(format("~p", ['Hello world'], 50))), 591 ?assertEqual("hello_world", lists:flatten(format("~p", ['hello_world'], 50))), 592 ?assertEqual("'node@127.0.0.1'", lists:flatten(format("~p", ['node@127.0.0.1'], 50))), 593 ?assertEqual("node@nohost", lists:flatten(format("~p", [node@nohost], 50))), 594 ?assertEqual("abc123", lists:flatten(format("~p", [abc123], 50))), 595 ok. 596 597sane_float_printing_test() -> 598 ?assertEqual("1.0", lists:flatten(format("~p", [1.0], 50))), 599 ?assertEqual("1.23456789", lists:flatten(format("~p", [1.23456789], 50))), 600 ?assertEqual("1.23456789", lists:flatten(format("~p", [1.234567890], 50))), 601 ?assertEqual("0.3333333333333333", lists:flatten(format("~p", [1/3], 50))), 602 ?assertEqual("0.1234567", lists:flatten(format("~p", [0.1234567], 50))), 603 ok. 604 605float_inside_list_test() -> 606 ?assertEqual("[97,38.233913133184835,99]", lists:flatten(format("~p", [[$a, 38.233913133184835, $c]], 50))), 607 ?assertError(badarg, lists:flatten(format("~s", [[$a, 38.233913133184835, $c]], 50))), 608 ok. 609 610quote_strip_test() -> 611 ?assertEqual("\"hello\"", lists:flatten(format("~p", ["hello"], 50))), 612 ?assertEqual("hello", lists:flatten(format("~s", ["hello"], 50))), 613 ?assertEqual("hello", lists:flatten(format("~s", [hello], 50))), 614 ?assertEqual("hello", lists:flatten(format("~p", [hello], 50))), 615 ?assertEqual("'hello world'", lists:flatten(format("~p", ['hello world'], 50))), 616 ?assertEqual("hello world", lists:flatten(format("~s", ['hello world'], 50))), 617 ok. 618 619binary_printing_test() -> 620 ?assertEqual("<<>>", lists:flatten(format("~p", [<<>>], 50))), 621 ?assertEqual("", lists:flatten(format("~s", [<<>>], 50))), 622 ?assertEqual("<<..>>", lists:flatten(format("~p", [<<"hi">>], 0))), 623 ?assertEqual("<<...>>", lists:flatten(format("~p", [<<"hi">>], 1))), 624 ?assertEqual("<<\"hello\">>", lists:flatten(format("~p", [<<$h, $e, $l, $l, $o>>], 50))), 625 ?assertEqual("<<\"hello\">>", lists:flatten(format("~p", [<<"hello">>], 50))), 626 ?assertEqual("<<104,101,108,108,111>>", lists:flatten(format("~w", [<<"hello">>], 50))), 627 ?assertEqual("<<1,2,3,4>>", lists:flatten(format("~p", [<<1, 2, 3, 4>>], 50))), 628 ?assertEqual([1,2,3,4], lists:flatten(format("~s", [<<1, 2, 3, 4>>], 50))), 629 ?assertEqual("hello", lists:flatten(format("~s", [<<"hello">>], 50))), 630 ?assertEqual("hello\nworld", lists:flatten(format("~s", [<<"hello\nworld">>], 50))), 631 ?assertEqual("<<\"hello\\nworld\">>", lists:flatten(format("~p", [<<"hello\nworld">>], 50))), 632 ?assertEqual("<<\"\\\"hello world\\\"\">>", lists:flatten(format("~p", [<<"\"hello world\"">>], 50))), 633 ?assertEqual("<<\"hello\\\\world\">>", lists:flatten(format("~p", [<<"hello\\world">>], 50))), 634 ?assertEqual("<<\"hello\\\\\world\">>", lists:flatten(format("~p", [<<"hello\\\world">>], 50))), 635 ?assertEqual("<<\"hello\\\\\\\\world\">>", lists:flatten(format("~p", [<<"hello\\\\world">>], 50))), 636 ?assertEqual("<<\"hello\\bworld\">>", lists:flatten(format("~p", [<<"hello\bworld">>], 50))), 637 ?assertEqual("<<\"hello\\tworld\">>", lists:flatten(format("~p", [<<"hello\tworld">>], 50))), 638 ?assertEqual("<<\"hello\\nworld\">>", lists:flatten(format("~p", [<<"hello\nworld">>], 50))), 639 ?assertEqual("<<\"hello\\rworld\">>", lists:flatten(format("~p", [<<"hello\rworld">>], 50))), 640 ?assertEqual("<<\"hello\\eworld\">>", lists:flatten(format("~p", [<<"hello\eworld">>], 50))), 641 ?assertEqual("<<\"hello\\fworld\">>", lists:flatten(format("~p", [<<"hello\fworld">>], 50))), 642 ?assertEqual("<<\"hello\\vworld\">>", lists:flatten(format("~p", [<<"hello\vworld">>], 50))), 643 ?assertEqual(" hello", lists:flatten(format("~10s", [<<"hello">>], 50))), 644 ?assertEqual("[a]", lists:flatten(format("~s", [<<"[a]">>], 50))), 645 ?assertEqual("[a]", lists:flatten(format("~s", [[<<"[a]">>]], 50))), 646 647 ok. 648 649bitstring_printing_test() -> 650 ?assertEqual("<<1,2,3,1:7>>", lists:flatten(format("~p", 651 [<<1, 2, 3, 1:7>>], 100))), 652 ?assertEqual("<<1:7>>", lists:flatten(format("~p", 653 [<<1:7>>], 100))), 654 ?assertEqual("<<1,2,3,...>>", lists:flatten(format("~p", 655 [<<1, 2, 3, 1:7>>], 12))), 656 ?assertEqual("<<1,2,3,...>>", lists:flatten(format("~p", 657 [<<1, 2, 3, 1:7>>], 13))), 658 ?assertEqual("<<1,2,3,1:7>>", lists:flatten(format("~p", 659 [<<1, 2, 3, 1:7>>], 14))), 660 ?assertEqual("<<..>>", lists:flatten(format("~p", [<<1:7>>], 0))), 661 ?assertEqual("<<...>>", lists:flatten(format("~p", [<<1:7>>], 1))), 662 ?assertEqual("[<<1>>,<<2>>]", lists:flatten(format("~p", [[<<1>>, <<2>>]], 663 100))), 664 ?assertEqual("{<<1:7>>}", lists:flatten(format("~p", [{<<1:7>>}], 50))), 665 ok. 666 667list_printing_test() -> 668 ?assertEqual("[]", lists:flatten(format("~p", [[]], 50))), 669 ?assertEqual("[]", lists:flatten(format("~w", [[]], 50))), 670 ?assertEqual("", lists:flatten(format("~s", [[]], 50))), 671 ?assertEqual("...", lists:flatten(format("~s", [[]], -1))), 672 ?assertEqual("[[]]", lists:flatten(format("~p", [[[]]], 50))), 673 ?assertEqual("[13,11,10,8,5,4]", lists:flatten(format("~p", [[13,11,10,8,5,4]], 50))), 674 ?assertEqual("\"\\rabc\"", lists:flatten(format("~p", [[13,$a, $b, $c]], 50))), 675 ?assertEqual("[1,2,3|4]", lists:flatten(format("~p", [[1, 2, 3|4]], 50))), 676 ?assertEqual("[...]", lists:flatten(format("~p", [[1, 2, 3,4]], 4))), 677 ?assertEqual("[1,...]", lists:flatten(format("~p", [[1, 2, 3, 4]], 6))), 678 ?assertEqual("[1,...]", lists:flatten(format("~p", [[1, 2, 3, 4]], 7))), 679 ?assertEqual("[1,2,...]", lists:flatten(format("~p", [[1, 2, 3, 4]], 8))), 680 ?assertEqual("[1|4]", lists:flatten(format("~p", [[1|4]], 50))), 681 ?assertEqual("[1]", lists:flatten(format("~p", [[1]], 50))), 682 ?assertError(badarg, lists:flatten(format("~s", [[1|4]], 50))), 683 ?assertEqual("\"hello...\"", lists:flatten(format("~p", ["hello world"], 10))), 684 ?assertEqual("hello w...", lists:flatten(format("~s", ["hello world"], 10))), 685 ?assertEqual("hello world\r\n", lists:flatten(format("~s", ["hello world\r\n"], 50))), 686 ?assertEqual("\rhello world\r\n", lists:flatten(format("~s", ["\rhello world\r\n"], 50))), 687 ?assertEqual("\"\\rhello world\\r\\n\"", lists:flatten(format("~p", ["\rhello world\r\n"], 50))), 688 ?assertEqual("[13,104,101,108,108,111,32,119,111,114,108,100,13,10]", lists:flatten(format("~w", ["\rhello world\r\n"], 60))), 689 ?assertEqual("...", lists:flatten(format("~s", ["\rhello world\r\n"], 3))), 690 ?assertEqual("[22835963083295358096932575511191922182123945984,...]", 691 lists:flatten(format("~p", [ 692 [22835963083295358096932575511191922182123945984, 693 22835963083295358096932575511191922182123945984]], 9))), 694 ?assertEqual("[22835963083295358096932575511191922182123945984,...]", 695 lists:flatten(format("~p", [ 696 [22835963083295358096932575511191922182123945984, 697 22835963083295358096932575511191922182123945984]], 53))), 698 %%improper list 699 ?assertEqual("[1,2,3|4]", lists:flatten(format("~P", [[1|[2|[3|4]]], 5], 50))), 700 ?assertEqual("[1|1]", lists:flatten(format("~P", [[1|1], 5], 50))), 701 ?assertEqual("[9|9]", lists:flatten(format("~p", [[9|9]], 50))), 702 ok. 703 704iolist_printing_test() -> 705 ?assertEqual("iolist: HelloIamaniolist", 706 lists:flatten(format("iolist: ~s", [[$H, $e, $l, $l, $o, "I", ["am", [<<"an">>], [$i, $o, $l, $i, $s, $t]]]], 1000))), 707 ?assertEqual("123...", 708 lists:flatten(format("~s", [[<<"123456789">>, "HellIamaniolist"]], 6))), 709 ?assertEqual("123456...", 710 lists:flatten(format("~s", [[<<"123456789">>, "HellIamaniolist"]], 9))), 711 ?assertEqual("123456789H...", 712 lists:flatten(format("~s", [[<<"123456789">>, "HellIamaniolist"]], 13))), 713 ?assertEqual("123456789HellIamaniolist", 714 lists:flatten(format("~s", [[<<"123456789">>, "HellIamaniolist"]], 30))), 715 716 ok. 717 718tuple_printing_test() -> 719 ?assertEqual("{}", lists:flatten(format("~p", [{}], 50))), 720 ?assertEqual("{}", lists:flatten(format("~w", [{}], 50))), 721 ?assertError(badarg, lists:flatten(format("~s", [{}], 50))), 722 ?assertEqual("{...}", lists:flatten(format("~p", [{foo}], 1))), 723 ?assertEqual("{...}", lists:flatten(format("~p", [{foo}], 2))), 724 ?assertEqual("{...}", lists:flatten(format("~p", [{foo}], 3))), 725 ?assertEqual("{...}", lists:flatten(format("~p", [{foo}], 4))), 726 ?assertEqual("{...}", lists:flatten(format("~p", [{foo}], 5))), 727 ?assertEqual("{foo,...}", lists:flatten(format("~p", [{foo,bar}], 6))), 728 ?assertEqual("{foo,...}", lists:flatten(format("~p", [{foo,bar}], 7))), 729 ?assertEqual("{foo,...}", lists:flatten(format("~p", [{foo,bar}], 9))), 730 ?assertEqual("{foo,bar}", lists:flatten(format("~p", [{foo,bar}], 10))), 731 ?assertEqual("{22835963083295358096932575511191922182123945984,...}", 732 lists:flatten(format("~w", [ 733 {22835963083295358096932575511191922182123945984, 734 22835963083295358096932575511191922182123945984}], 10))), 735 ?assertEqual("{22835963083295358096932575511191922182123945984,...}", 736 lists:flatten(format("~w", [ 737 {22835963083295358096932575511191922182123945984, 738 bar}], 10))), 739 ?assertEqual("{22835963083295358096932575511191922182123945984,...}", 740 lists:flatten(format("~w", [ 741 {22835963083295358096932575511191922182123945984, 742 22835963083295358096932575511191922182123945984}], 53))), 743 ok. 744 745map_printing_test() -> 746 case erlang:is_builtin(erlang, is_map, 1) of 747 true -> 748 ?assertEqual("#{}", lists:flatten(format("~p", [maps:new()], 50))), 749 ?assertEqual("#{}", lists:flatten(format("~p", [maps:new()], 3))), 750 ?assertEqual("#{}", lists:flatten(format("~w", [maps:new()], 50))), 751 ?assertError(badarg, lists:flatten(format("~s", [maps:new()], 50))), 752 ?assertEqual("#{...}", lists:flatten(format("~p", [maps:from_list([{bar, foo}])], 1))), 753 ?assertEqual("#{...}", lists:flatten(format("~p", [maps:from_list([{bar, foo}])], 6))), 754 ?assertEqual("#{bar => ...}", lists:flatten(format("~p", [maps:from_list([{bar, foo}])], 7))), 755 ?assertEqual("#{bar => ...}", lists:flatten(format("~p", [maps:from_list([{bar, foo}])], 9))), 756 ?assertEqual("#{bar => foo}", lists:flatten(format("~p", [maps:from_list([{bar, foo}])], 10))), 757 ?assertEqual("#{bar => ...,...}", lists:flatten(format("~p", [maps:from_list([{bar, foo}, {foo, bar}])], 9))), 758 ?assertEqual("#{bar => foo,...}", lists:flatten(format("~p", [maps:from_list([{bar, foo}, {foo, bar}])], 10))), 759 ?assertEqual("#{bar => foo,...}", lists:flatten(format("~p", [maps:from_list([{bar, foo}, {foo, bar}])], 17))), 760 ?assertEqual("#{bar => foo,foo => ...}", lists:flatten(format("~p", [maps:from_list([{bar, foo}, {foo, bar}])], 18))), 761 ?assertEqual("#{bar => foo,foo => ...}", lists:flatten(format("~p", [maps:from_list([{bar, foo}, {foo, bar}])], 19))), 762 ?assertEqual("#{bar => foo,foo => ...}", lists:flatten(format("~p", [maps:from_list([{bar, foo}, {foo, bar}])], 20))), 763 ?assertEqual("#{bar => foo,foo => bar}", lists:flatten(format("~p", [maps:from_list([{bar, foo}, {foo, bar}])], 21))), 764 ?assertEqual("#{22835963083295358096932575511191922182123945984 => ...}", 765 lists:flatten(format("~w", [ 766 maps:from_list([{22835963083295358096932575511191922182123945984, 767 22835963083295358096932575511191922182123945984}])], 10))), 768 ?assertEqual("#{22835963083295358096932575511191922182123945984 => ...}", 769 lists:flatten(format("~w", [ 770 maps:from_list([{22835963083295358096932575511191922182123945984, 771 bar}])], 10))), 772 ?assertEqual("#{22835963083295358096932575511191922182123945984 => ...}", 773 lists:flatten(format("~w", [ 774 maps:from_list([{22835963083295358096932575511191922182123945984, 775 bar}])], 53))), 776 ?assertEqual("#{22835963083295358096932575511191922182123945984 => bar}", 777 lists:flatten(format("~w", [ 778 maps:from_list([{22835963083295358096932575511191922182123945984, 779 bar}])], 54))), 780 ok; 781 false -> 782 ok 783 end. 784 785unicode_test() -> 786 ?assertEqual([231,167,129], lists:flatten(format("~s", [<<231,167,129>>], 50))), 787 ?assertEqual([31169], lists:flatten(format("~ts", [<<231,167,129>>], 50))), 788 ok. 789 790depth_limit_test() -> 791 ?assertEqual("{...}", lists:flatten(format("~P", [{a, [b, [c, [d]]]}, 1], 50))), 792 ?assertEqual("{a,...}", lists:flatten(format("~P", [{a, [b, [c, [d]]]}, 2], 50))), 793 ?assertEqual("{a,[...]}", lists:flatten(format("~P", [{a, [b, [c, [d]]]}, 3], 50))), 794 ?assertEqual("{a,[b|...]}", lists:flatten(format("~P", [{a, [b, [c, [d]]]}, 4], 50))), 795 ?assertEqual("{a,[b,[...]]}", lists:flatten(format("~P", [{a, [b, [c, [d]]]}, 5], 50))), 796 ?assertEqual("{a,[b,[c|...]]}", lists:flatten(format("~P", [{a, [b, [c, [d]]]}, 6], 50))), 797 ?assertEqual("{a,[b,[c,[...]]]}", lists:flatten(format("~P", [{a, [b, [c, [d]]]}, 7], 50))), 798 ?assertEqual("{a,[b,[c,[d]]]}", lists:flatten(format("~P", [{a, [b, [c, [d]]]}, 8], 50))), 799 ?assertEqual("{a,[b,[c,[d]]]}", lists:flatten(format("~P", [{a, [b, [c, [d]]]}, 9], 50))), 800 801 ?assertEqual("{a,{...}}", lists:flatten(format("~P", [{a, {b, {c, {d}}}}, 3], 50))), 802 ?assertEqual("{a,{b,...}}", lists:flatten(format("~P", [{a, {b, {c, {d}}}}, 4], 50))), 803 ?assertEqual("{a,{b,{...}}}", lists:flatten(format("~P", [{a, {b, {c, {d}}}}, 5], 50))), 804 ?assertEqual("{a,{b,{c,...}}}", lists:flatten(format("~P", [{a, {b, {c, {d}}}}, 6], 50))), 805 ?assertEqual("{a,{b,{c,{...}}}}", lists:flatten(format("~P", [{a, {b, {c, {d}}}}, 7], 50))), 806 ?assertEqual("{a,{b,{c,{d}}}}", lists:flatten(format("~P", [{a, {b, {c, {d}}}}, 8], 50))), 807 808 case erlang:is_builtin(erlang, is_map, 1) of 809 true -> 810 ?assertEqual("#{a => #{...}}", 811 lists:flatten(format("~P", 812 [maps:from_list([{a, maps:from_list([{b, maps:from_list([{c, d}])}])}]), 2], 50))), 813 ?assertEqual("#{a => #{b => #{...}}}", 814 lists:flatten(format("~P", 815 [maps:from_list([{a, maps:from_list([{b, maps:from_list([{c, d}])}])}]), 3], 50))), 816 ?assertEqual("#{a => #{b => #{c => d}}}", 817 lists:flatten(format("~P", 818 [maps:from_list([{a, maps:from_list([{b, maps:from_list([{c, d}])}])}]), 4], 50))), 819 820 ?assertEqual("#{}", lists:flatten(format("~P", [maps:new(), 1], 50))), 821 ?assertEqual("#{...}", lists:flatten(format("~P", [maps:from_list([{1,1}, {2,2}, {3,3}]), 1], 50))), 822 ?assertEqual("#{1 => 1,...}", lists:flatten(format("~P", [maps:from_list([{1,1}, {2,2}, {3,3}]), 2], 50))), 823 ?assertEqual("#{1 => 1,2 => 2,...}", lists:flatten(format("~P", [maps:from_list([{1,1}, {2,2}, {3,3}]), 3], 50))), 824 ?assertEqual("#{1 => 1,2 => 2,3 => 3}", lists:flatten(format("~P", [maps:from_list([{1,1}, {2,2}, {3,3}]), 4], 50))), 825 826 ok; 827 false -> 828 ok 829 end, 830 831 ?assertEqual("{\"a\",[...]}", lists:flatten(format("~P", [{"a", ["b", ["c", ["d"]]]}, 3], 50))), 832 ?assertEqual("{\"a\",[\"b\",[[...]|...]]}", lists:flatten(format("~P", [{"a", ["b", ["c", ["d"]]]}, 6], 50))), 833 ?assertEqual("{\"a\",[\"b\",[\"c\",[\"d\"]]]}", lists:flatten(format("~P", [{"a", ["b", ["c", ["d"]]]}, 9], 50))), 834 835 ?assertEqual("[...]", lists:flatten(format("~P", [[1, 2, 3], 1], 50))), 836 ?assertEqual("[1|...]", lists:flatten(format("~P", [[1, 2, 3], 2], 50))), 837 ?assertEqual("[1,2|...]", lists:flatten(format("~P", [[1, 2, 3], 3], 50))), 838 ?assertEqual("[1,2,3]", lists:flatten(format("~P", [[1, 2, 3], 4], 50))), 839 840 ?assertEqual("{1,...}", lists:flatten(format("~P", [{1, 2, 3}, 2], 50))), 841 ?assertEqual("{1,2,...}", lists:flatten(format("~P", [{1, 2, 3}, 3], 50))), 842 ?assertEqual("{1,2,3}", lists:flatten(format("~P", [{1, 2, 3}, 4], 50))), 843 844 ?assertEqual("{1,...}", lists:flatten(format("~P", [{1, 2, 3}, 2], 50))), 845 ?assertEqual("[1,2|...]", lists:flatten(format("~P", [[1, 2, <<3>>], 3], 50))), 846 ?assertEqual("[1,2,<<...>>]", lists:flatten(format("~P", [[1, 2, <<3>>], 4], 50))), 847 ?assertEqual("[1,2,<<3>>]", lists:flatten(format("~P", [[1, 2, <<3>>], 5], 50))), 848 849 ?assertEqual("<<...>>", lists:flatten(format("~P", [<<0, 0, 0, 0>>, 1], 50))), 850 ?assertEqual("<<0,...>>", lists:flatten(format("~P", [<<0, 0, 0, 0>>, 2], 50))), 851 ?assertEqual("<<0,0,...>>", lists:flatten(format("~P", [<<0, 0, 0, 0>>, 3], 50))), 852 ?assertEqual("<<0,0,0,...>>", lists:flatten(format("~P", [<<0, 0, 0, 0>>, 4], 50))), 853 ?assertEqual("<<0,0,0,0>>", lists:flatten(format("~P", [<<0, 0, 0, 0>>, 5], 50))), 854 855 %% this is a seriously weird edge case 856 ?assertEqual("<<\" \"...>>", lists:flatten(format("~P", [<<32, 32, 32, 0>>, 2], 50))), 857 ?assertEqual("<<\" \"...>>", lists:flatten(format("~P", [<<32, 32, 32, 0>>, 3], 50))), 858 ?assertEqual("<<\" \"...>>", lists:flatten(format("~P", [<<32, 32, 32, 0>>, 4], 50))), 859 ?assertEqual("<<32,32,32,0>>", lists:flatten(format("~P", [<<32, 32, 32, 0>>, 5], 50))), 860 ?assertEqual("<<32,32,32,0>>", lists:flatten(format("~p", [<<32, 32, 32, 0>>], 50))), 861 862 %% depth limiting for some reason works in 4 byte chunks on printable binaries? 863 ?assertEqual("<<\"hell\"...>>", lists:flatten(format("~P", [<<"hello world">>, 2], 50))), 864 ?assertEqual("<<\"abcd\"...>>", lists:flatten(format("~P", [<<$a, $b, $c, $d, $e, 0>>, 2], 50))), 865 866 %% I don't even know... 867 ?assertEqual("<<>>", lists:flatten(format("~P", [<<>>, 1], 50))), 868 ?assertEqual("<<>>", lists:flatten(format("~W", [<<>>, 1], 50))), 869 870 ?assertEqual("{abc,<<\"abc\\\"\">>}", lists:flatten(format("~P", [{abc,<<"abc\"">>}, 4], 50))), 871 872 ok. 873 874print_terms_without_format_string_test() -> 875 ?assertError(badarg, format({hello, world}, [], 50)), 876 ?assertError(badarg, format([{google, bomb}], [], 50)), 877 ?assertError(badarg, format([$h,$e,$l,$l,$o, 3594], [], 50)), 878 ?assertEqual("helloworld", lists:flatten(format([$h,$e,$l,$l,$o, "world"], [], 50))), 879 ?assertEqual("hello", lists:flatten(format(<<"hello">>, [], 50))), 880 ?assertEqual("hello", lists:flatten(format('hello', [], 50))), 881 ?assertError(badarg, format(<<1, 2, 3, 1:7>>, [], 100)), 882 ?assertError(badarg, format(65535, [], 50)), 883 ok. 884 885improper_io_list_test() -> 886 ?assertEqual(">hello", lists:flatten(format('~s', [[$>|<<"hello">>]], 50))), 887 ?assertEqual(">hello", lists:flatten(format('~ts', [[$>|<<"hello">>]], 50))), 888 ?assertEqual("helloworld", lists:flatten(format('~ts', [[<<"hello">>|<<"world">>]], 50))), 889 ok. 890 891-endif. 892