1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2005-2020. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21%%%-------------------------------------------------------------------
22%%% File    : erl_print_SUITE.erl
23%%% Author  : Rickard Green <rickard.s.green@ericsson.com>
24%%% Description :
25%%%
26%%% Created : 10 Mar 2005 by Rickard Green <rickard.s.green@ericsson.com>
27%%%-------------------------------------------------------------------
28-module(erl_print_SUITE).
29-author('rickard.s.green@ericsson.com').
30
31-export([all/0, suite/0, init_per_testcase/2, end_per_testcase/2]).
32
33-export([erlang_display/1, integer/1, float/1,
34         string/1, character/1, snprintf/1, quote/1]).
35
36-include_lib("common_test/include/ct.hrl").
37
38suite() ->
39    [{ct_hooks,[ts_install_cth]},
40     {timetrap, {minutes, 10}}].
41
42all() ->
43    [erlang_display, integer, float, string, character,
44     snprintf, quote].
45
46init_per_testcase(Case, Config) ->
47    [{testcase, Case}|Config].
48
49end_per_testcase(_Case, _Config) ->
50    ok.
51
52%%
53%%
54%% Test cases
55%%
56%%
57
58erlang_display(Config) when is_list(Config) ->
59    put(erlang_display_test, ok),
60    OAIS = erts_debug:set_internal_state(available_internal_state, true),
61
62    %% atoms
63    chk_display(atom, "atom"),
64    chk_display(true, "true"),
65    chk_display(false, "false"),
66    chk_display('DOWN', "'DOWN'"),
67    chk_display('EXIT', "'EXIT'"),
68    chk_display('asdDofw $@{}][', "'asdDofw $@{}]['"),
69
70    %% integers
71    chk_display(0, "0"),
72    chk_display(1, "1"),
73    chk_display(4711, "4711"),
74    chk_display(((1 bsl 27) - 1), "134217727"),
75    chk_display((1 bsl 27), "134217728"),
76    chk_display((1 bsl 32), "4294967296"),
77    chk_display(11111111111, "11111111111"),
78    chk_display((1 bsl 59) - 1, "576460752303423487"),
79    chk_display(1 bsl 59, "576460752303423488"),
80    chk_display(111111111111111111111, "111111111111111111111"),
81    chk_display(123456789012345678901234567890,
82                "123456789012345678901234567890"),
83    chk_display(1 bsl 10000, str_1_bsl_10000()),
84    chk_display(-1, "-1"),
85    chk_display(-4711, "-4711"),
86    chk_display(-(1 bsl 27), "-134217728"),
87    chk_display(-((1 bsl 27) + 1), "-134217729"),
88    chk_display(-(1 bsl 32), "-4294967296"),
89    chk_display(-11111111111, "-11111111111"),
90    chk_display(-(1 bsl 59), "-576460752303423488"),
91    chk_display(-((1 bsl 59) + 1), "-576460752303423489"),
92    chk_display(-111111111111111111111, "-111111111111111111111"),
93    chk_display(-123456789012345678901234567890,
94                "-123456789012345678901234567890"),
95    chk_display(-(1 bsl 10000), [$- | str_1_bsl_10000()]),
96
97    MyCre = my_cre(),
98
99    %% pids
100    chk_display(mk_pid_xstr({node(), MyCre}, 4711, 42)),
101    chk_display(mk_pid_xstr({node(), oth_cre(MyCre)}, 4711, 42)),
102    chk_display(mk_pid_xstr({node(), oth_cre(oth_cre(MyCre))}, 4711, 42)),
103
104    chk_display(mk_pid_xstr({a@b, MyCre}, 4711, 42)),
105    chk_display(mk_pid_xstr({a@b, oth_cre(MyCre)}, 4711, 42)),
106    chk_display(mk_pid_xstr({a@b, oth_cre(oth_cre(MyCre))}, 4711, 42)),
107
108    %% ports
109    chk_display(mk_port_xstr({node(), MyCre}, 4711)),
110    chk_display(mk_port_xstr({node(), oth_cre(MyCre)}, 4711)),
111    chk_display(mk_port_xstr({node(), oth_cre(oth_cre(MyCre))}, 4711)),
112
113    chk_display(mk_port_xstr({c@d, MyCre}, 4711)),
114    chk_display(mk_port_xstr({c@d, oth_cre(MyCre)}, 4711)),
115    chk_display(mk_port_xstr({c@d, oth_cre(oth_cre(MyCre))}, 4711)),
116
117    %% refs
118    chk_display(mk_ref_xstr({node(), MyCre}, [1,2,3])),
119    chk_display(mk_ref_xstr({node(), oth_cre(MyCre)}, [1,2,3])),
120    chk_display(mk_ref_xstr({node(), oth_cre(oth_cre(MyCre))}, [1,2,3])),
121
122    chk_display(mk_ref_xstr({e@f, MyCre},[1,2,3] )),
123    chk_display(mk_ref_xstr({e@f, oth_cre(MyCre)}, [1,2,3])),
124    chk_display(mk_ref_xstr({e@f, oth_cre(oth_cre(MyCre))}, [1,2,3])),
125
126    %% Compund terms
127    {Pid, PidStr} = mk_pid_xstr({x@y, oth_cre(MyCre)}, 4712, 41),
128    {Port, PortStr} = mk_port_xstr({x@y, oth_cre(MyCre)}, 4712),
129    {Ref, RefStr} = mk_ref_xstr({e@f, oth_cre(MyCre)}, [11,12,13]),
130
131    chk_display({atom,-4711,Ref,{"hej",[Pid,222222222222222222222222,Port,4711]}},
132                "{atom,-4711,"++RefStr++",{\"hej\",["++PidStr++",222222222222222222222222,"++PortStr++",4711]}}"),
133    chk_display({{{{{{{{{{{{{{{{{{{{{{{hi}}}}}}}}}}}}}}}}}}}}}}},
134                "{{{{{{{{{{{{{{{{{{{{{{{hi}}}}}}}}}}}}}}}}}}}}}}}"),
135    chk_display([[[[[[[[[[[[[[[[[[[[[[[yo]]]]]]]]]]]]]]]]]]]]]]],
136                "[[[[[[[[[[[[[[[[[[[[[[[yo]]]]]]]]]]]]]]]]]]]]]]]"),
137    chk_display({[{[{[{[{[{[{[{[{[{[{[{[ii]}]}]}]}]}]}]}]}]}]}]}]},
138                "{[{[{[{[{[{[{[{[{[{[{[{[ii]}]}]}]}]}]}]}]}]}]}]}]}"),
139    chk_display([], "[]"), % Not really a compound term :)
140    chk_display([a|b], "[a|b]"),
141    chk_display([a,b,c|z], "[a,b,c|z]"),
142    chk_display([a,b,c], "[a,b,c]"),
143    chk_display([Pid,Port,Ref],
144                "["++PidStr++","++PortStr++","++RefStr++"]"),
145    chk_display("abcdefghijklmnopqrstuvwxyz",
146                "\"abcdefghijklmnopqrstuvwxyz\""),
147    chk_display("ABCDEFGHIJKLMNOPQRSTUVWXYZ",
148                "\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\""),
149    chk_display("H E J", "\"H E J\""),
150    chk_display("asdDofw $@{}][", "\"asdDofw $@{}][\""),
151
152    %%
153    %% TODO: Check binaries, fun and floats...
154    %%
155
156    erts_debug:set_internal_state(available_internal_state, OAIS),
157    ok = get(erlang_display_test).
158
159get_chnl_no(NodeName) when is_atom(NodeName) ->
160    erts_debug:get_internal_state({channel_number, NodeName}).
161
162chk_display(Term, Expect) when is_list(Expect) ->
163    Dstr = erts_debug:display(Term),
164    case Expect ++ io_lib:nl() of
165        Dstr ->
166            io:format("Test of \"~p\" succeeded.~n"
167                      "  Expected and got: ~s~n",
168                      [Term, io_lib:write_string(Dstr)]);
169        DoExpect ->
170            io:format("***~n"
171                      "*** Test of \"~p\" failed!~n"
172                      "***       Expected: ~s~n"
173                      "***            Got: ~s~n"
174                      "***~n",
175                      [Term,
176                       io_lib:write_string(DoExpect),
177                       io_lib:write_string(Dstr)]),
178            put(erlang_display_test, failed)
179    end.
180
181chk_display({Term, Expect}) ->
182    chk_display(Term, Expect).
183
184mk_pid_xstr({NodeName, Creation}, Number, Serial) ->
185    Pid = mk_pid({NodeName, Creation}, Number, Serial),
186    XStr = "<" ++ integer_to_list(get_chnl_no(NodeName))
187    ++ "." ++ integer_to_list(Number)
188    ++ "." ++ integer_to_list(Serial) ++ ">",
189    {Pid, XStr}.
190
191mk_port_xstr({NodeName, Creation}, Number) ->
192    Port = mk_port({NodeName, Creation}, Number),
193    XStr = "#Port<" ++ integer_to_list(get_chnl_no(NodeName))
194    ++ "." ++ integer_to_list(Number) ++ ">",
195    {Port, XStr}.
196
197mk_ref_xstr({NodeName, Creation}, Numbers) ->
198    Ref = mk_ref({NodeName, Creation}, Numbers),
199    XStr = "#Ref<" ++ integer_to_list(get_chnl_no(NodeName))
200    ++ ref_numbers_xstr(Numbers) ++ ">",
201    {Ref, XStr}.
202
203ref_numbers_xstr([]) ->
204    [];
205ref_numbers_xstr([N | Ns]) ->
206    ref_numbers_xstr(Ns) ++ "." ++ integer_to_list(N).
207
208-define(TESTCASE_IMPL(T), T(A) -> default_testcase_impl(A)).
209
210?TESTCASE_IMPL(integer).
211?TESTCASE_IMPL(float).
212?TESTCASE_IMPL(string).
213?TESTCASE_IMPL(character).
214?TESTCASE_IMPL(snprintf).
215?TESTCASE_IMPL(quote).
216
217%%
218%%
219%% Auxiliary functions
220%%
221%%
222
223default_testcase_impl(Config) when is_list(Config) -> run_case(Config).
224
225-define(TESTPROG, "erl_print_tests").
226-define(FAILED_MARKER, $E,$P,$-,$T,$E,$S,$T,$-,$F,$A,$I,$L,$U,$R,$E).
227-define(SKIPPED_MARKER, $E,$P,$-,$T,$E,$S,$T,$-,$S,$K,$I,$P).
228-define(SUCCESS_MARKER, $E,$P,$-,$T,$E,$S,$T,$-,$S,$U,$C,$C,$E,$S,$S).
229-define(PID_MARKER, $E,$P,$-,$T,$E,$S,$T,$-,$P,$I,$D).
230
231port_prog_killer(EProc, OSProc) when is_pid(EProc), is_list(OSProc) ->
232    process_flag(trap_exit, true),
233    Ref = erlang:monitor(process, EProc),
234    receive
235        {'DOWN', Ref, _, _, Reason} when is_tuple(Reason),
236                                         element(1, Reason)
237                                         == timetrap_timeout ->
238            Cmd = "kill -9 " ++ OSProc,
239            io:format("Test case timed out. "
240                      "Trying to kill port program.~n"
241                      "  Executing: ~p~n", [Cmd]),
242            case os:cmd(Cmd) of
243                [] ->
244                    ok;
245                OsCmdRes ->
246                    io:format("             ~s", [OsCmdRes])
247            end;
248        {'DOWN', Ref, _, _, _} ->
249            %% OSProc is assumed to have terminated by itself
250            ok
251    end.
252
253get_line(_Port, eol, Data) ->
254    Data;
255get_line(Port, noeol, Data) ->
256    receive
257        {Port, {data, {Flag, NextData}}} ->
258            get_line(Port, Flag, Data ++ NextData);
259        {Port, eof} ->
260            ct:fail(port_prog_unexpectedly_closed)
261    end.
262
263read_case_data(Port, TestCase) ->
264    receive
265        {Port, {data, {eol, [?SUCCESS_MARKER]}}} ->
266            ok;
267        {Port, {data, {Flag, [?SUCCESS_MARKER | CommentStart]}}} ->
268            {comment, get_line(Port, Flag, CommentStart)};
269        {Port, {data, {Flag, [?SKIPPED_MARKER | CommentStart]}}} ->
270            {skipped, get_line(Port, Flag, CommentStart)};
271        {Port, {data, {Flag, [?FAILED_MARKER | ReasonStart]}}} ->
272            ct:fail(get_line(Port, Flag, ReasonStart));
273        {Port, {data, {eol, [?PID_MARKER | PidStr]}}} ->
274            io:format("Port program pid: ~s~n", [PidStr]),
275            CaseProc = self(),
276            _ = list_to_integer(PidStr), % Sanity check
277            spawn_opt(fun () ->
278                              port_prog_killer(CaseProc, PidStr)
279                      end,
280                      [{priority, max}, link]),
281            read_case_data(Port, TestCase);
282        {Port, {data, {Flag, LineStart}}} ->
283            io:format("~s~n", [get_line(Port, Flag, LineStart)]),
284            read_case_data(Port, TestCase);
285        {Port, eof} ->
286            ct:fail(port_prog_unexpectedly_closed)
287    end.
288
289run_case(Config) ->
290    run_case(Config, "").
291
292run_case(Config, TestArgs) ->
293    run_case(Config, TestArgs, fun (_Port) -> ok end).
294
295run_case(Config, TestArgs, Fun) ->
296    Test = atom_to_list(proplists:get_value(testcase, Config)),
297    TestProg = filename:join([proplists:get_value(data_dir, Config),
298                              ?TESTPROG
299                              ++ "."
300                              ++ atom_to_list(erlang:system_info(threads))]),
301    Cmd = TestProg ++ " " ++ Test ++ " " ++ TestArgs,
302    case catch open_port({spawn, Cmd}, [stream,
303                                        use_stdio,
304                                        stderr_to_stdout,
305                                        eof,
306                                        {line, 1024}]) of
307        Port when is_port(Port) ->
308            Fun(Port),
309            CaseResult = read_case_data(Port, Test),
310            receive
311                {Port, eof} ->
312                    ok
313            end,
314            CaseResult;
315        Error ->
316            ct:fail({open_port_failed, Error})
317    end.
318
319
320-define(VERSION_MAGIC,       131).
321
322-define(ATOM_EXT,            100).
323-define(REFERENCE_EXT,       101).
324-define(PORT_EXT,            102).
325-define(PID_EXT,             103).
326-define(NEW_REFERENCE_EXT,   114).
327-define(NEW_PID_EXT,         $X).
328-define(NEW_PORT_EXT,        $Y).
329-define(NEWER_REFERENCE_EXT, $Z).
330
331uint32_be(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 32 ->
332    [(Uint bsr 24) band 16#ff,
333     (Uint bsr 16) band 16#ff,
334     (Uint bsr 8) band 16#ff,
335     Uint band 16#ff];
336uint32_be(Uint) ->
337    exit({badarg, uint32_be, [Uint]}).
338
339
340uint16_be(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 16 ->
341    [(Uint bsr 8) band 16#ff,
342     Uint band 16#ff];
343uint16_be(Uint) ->
344    exit({badarg, uint16_be, [Uint]}).
345
346uint8(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 8 ->
347    Uint band 16#ff;
348uint8(Uint) ->
349    exit({badarg, uint8, [Uint]}).
350
351
352
353mk_pid({NodeName, Creation}, Number, Serial) when is_atom(NodeName) ->
354    mk_pid({atom_to_list(NodeName), Creation}, Number, Serial);
355mk_pid({NodeName, Creation}, Number, Serial) ->
356    case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
357                                              ?NEW_PID_EXT,
358                                              ?ATOM_EXT,
359                                              uint16_be(length(NodeName)),
360                                              NodeName,
361                                              uint32_be(Number),
362                                              uint32_be(Serial),
363                                              uint32_be(Creation)])) of
364        Pid when is_pid(Pid) ->
365            Pid;
366        {'EXIT', {badarg, _}} ->
367            exit({badarg, mk_pid, [{NodeName, Creation}, Number, Serial]});
368        Other ->
369            exit({unexpected_binary_to_term_result, Other})
370    end.
371
372mk_port({NodeName, Creation}, Number) when is_atom(NodeName) ->
373    mk_port({atom_to_list(NodeName), Creation}, Number);
374mk_port({NodeName, Creation}, Number) ->
375    case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
376                                              ?NEW_PORT_EXT,
377                                              ?ATOM_EXT,
378                                              uint16_be(length(NodeName)),
379                                              NodeName,
380                                              uint32_be(Number),
381                                              uint32_be(Creation)])) of
382        Port when is_port(Port) ->
383            Port;
384        {'EXIT', {badarg, _}} ->
385            exit({badarg, mk_port, [{NodeName, Creation}, Number]});
386        Other ->
387            exit({unexpected_binary_to_term_result, Other})
388    end.
389
390mk_ref({NodeName, Creation}, Numbers) when is_atom(NodeName),
391                                           is_integer(Creation),
392                                           is_list(Numbers) ->
393    mk_ref({atom_to_list(NodeName), Creation}, Numbers);
394mk_ref({NodeName, Creation}, Numbers) when is_list(NodeName),
395                                           is_integer(Creation),
396                                           is_list(Numbers) ->
397    case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
398                                              ?NEWER_REFERENCE_EXT,
399                                              uint16_be(length(Numbers)),
400                                              ?ATOM_EXT,
401                                              uint16_be(length(NodeName)),
402                                              NodeName,
403                                              uint32_be(Creation),
404                                              lists:map(fun (N) ->
405                                                                uint32_be(N)
406                                                        end,
407                                                        Numbers)])) of
408        Ref when is_reference(Ref) ->
409            Ref;
410        {'EXIT', {badarg, _}} ->
411            exit({badarg, mk_ref, [{NodeName, Creation}, Numbers]});
412        Other ->
413            exit({unexpected_binary_to_term_result, Other})
414    end.
415
416my_cre() -> erlang:system_info(creation).
417
418oth_cre(N) when N >= 0, N < (1 bsl 32) ->
419    (N rem ((1 bsl 32) - 1)) + 1;
420oth_cre(N) ->
421    exit({invalid_creation, N}).
422
423str_1_bsl_10000() ->
424    "19950631168807583848837421626835850838234968318861924548520089498529438830221946631919961684036194597899331129423209124271556491349413781117593785932096323957855730046793794526765246551266059895520550086918193311542508608460618104685509074866089624888090489894838009253941633257850621568309473902556912388065225096643874441046759871626985453222868538161694315775629640762836880760732228535091641476183956381458969463899410840960536267821064621427333394036525565649530603142680234969400335934316651459297773279665775606172582031407994198179607378245683762280037302885487251900834464581454650557929601414833921615734588139257095379769119277800826957735674444123062018757836325502728323789270710373802866393031428133241401624195671690574061419654342324638801248856147305207431992259611796250130992860241708340807605932320161268492288496255841312844061536738951487114256315111089745514203313820202931640957596464756010405845841566072044962867016515061920631004186422275908670900574606417856951911456055068251250406007519842261898059237118054444788072906395242548339221982707404473162376760846613033778706039803413197133493654622700563169937455508241780972810983291314403571877524768509857276937926433221599399876886660808368837838027643282775172273657572744784112294389733810861607423253291974813120197604178281965697475898164531258434135959862784130128185406283476649088690521047580882615823961985770122407044330583075869039319604603404973156583208672105913300903752823415539745394397715257455290510212310947321610753474825740775273986348298498340756937955646638621874569499279016572103701364433135817214311791398222983845847334440270964182851005072927748364550578634501100852987812389473928699540834346158807043959118985815145779177143619698728131459483783202081474982171858011389071228250905826817436220577475921417653715687725614904582904992461028630081535583308130101987675856234343538955409175623400844887526162643568648833519463720377293240094456246923254350400678027273837755376406726898636241037491410966718557050759098100246789880178271925953381282421954028302759408448955014676668389697996886241636313376393903373455801407636741877711055384225739499110186468219696581651485130494222369947714763069155468217682876200362777257723781365331611196811280792669481887201298643660768551639860534602297871557517947385246369446923087894265948217008051120322365496288169035739121368338393591756418733850510970271613915439590991598154654417336311656936031122249937969999226781732358023111862644575299135758175008199839236284615249881088960232244362173771618086357015468484058622329792853875623486556440536962622018963571028812361567512543338303270029097668650568557157505516727518899194129711337690149916181315171544007728650573189557450920330185304847113818315407324053319038462084036421763703911550639789000742853672196280903477974533320468368795868580237952218629120080742819551317948157624448298518461509704888027274721574688131594750409732115080498190455803416826949787141316063210686391511681774304792596709376".
425