1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1997-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-module(list_bif_SUITE).
22-include_lib("common_test/include/ct.hrl").
23
24-export([all/0, suite/0]).
25-export([hd_test/1,tl_test/1,t_length/1,t_list_to_pid/1,
26         t_list_to_ref/1, t_list_to_ext_pidportref/1,
27         t_list_to_port/1,t_list_to_float/1,t_list_to_integer/1]).
28
29
30suite() ->
31    [{ct_hooks,[ts_install_cth]},
32     {timetrap, {minutes, 1}}].
33
34
35all() ->
36    [hd_test, tl_test, t_length, t_list_to_pid, t_list_to_port,
37     t_list_to_ref, t_list_to_ext_pidportref,
38     t_list_to_float, t_list_to_integer].
39
40%% Tests list_to_integer and string:to_integer
41t_list_to_integer(Config) when is_list(Config) ->
42    {'EXIT',{badarg,_}} = (catch list_to_integer("12373281903728109372810937209817320981321ABC")),
43    12373281903728109372810937209817320981321 = (catch list_to_integer("12373281903728109372810937209817320981321")),
44    12373 = (catch list_to_integer("12373")),
45    -12373 =  (catch list_to_integer("-12373")),
46    12373 = (catch list_to_integer("+12373")),
47    {'EXIT',{badarg,_}} = ( catch list_to_integer(abc)),
48    {'EXIT',{badarg,_}} = (catch list_to_integer("")),
49    {12373281903728109372810937209817320981321,"ABC"} = string:to_integer("12373281903728109372810937209817320981321ABC"),
50    {-12373281903728109372810937209817320981321,"ABC"} = string:to_integer("-12373281903728109372810937209817320981321ABC"),
51    {12,[345]} = string:to_integer([$1,$2,345]),
52    {error, badarg} = string:to_integer([$1,$2,a]),
53    {error,no_integer} = string:to_integer([$A]),
54    {error,badarg} = string:to_integer($A),
55    ok.
56
57%% Test hd/1 with correct and incorrect arguments.
58hd_test(Config) when is_list(Config) ->
59    $h = hd(id("hejsan")),
60    case catch hd(id($h)) of
61        {'EXIT', {badarg, _}} -> ok;
62        Res ->
63            ct:fail("hd/1 with incorrect args succeeded.~nResult: ~p", [Res])
64    end,
65    ok.
66
67
68%% Test tl/1 with correct and incorrect arguments.
69tl_test(Config) when is_list(Config) ->
70    "ejsan" = tl(id("hejsan")),
71    case catch tl(id(104)) of
72        {'EXIT', {badarg, _}} ->
73            ok;
74        Res ->
75            ct:fail("tl/1 with incorrect args succeeded.~nResult: ~p", [Res])
76    end,
77    ok.
78
79
80%% Test length/1 with correct and incorrect arguments.
81
82t_length(Config) when is_list(Config) ->
83    0 = length(""),
84    0 = length([]),
85    1 = length([1]),
86    2 = length([1,a]),
87    2 = length("ab"),
88    3 = length("abc"),
89    4 = length(id([x|"abc"])),
90    6 = length("hejsan"),
91    {'EXIT',{badarg,_}} = (catch length(id([a,b|c]))),
92    case catch length({tuple}) of
93        {'EXIT', {badarg, _}} ->
94            ok;
95        Res ->
96            ct:fail("length/1 with incorrect args succeeded.~nResult: ~p", [Res])
97    end,
98    ok.
99
100
101%% Test list_to_pid/1 with correct and incorrect arguments.
102
103t_list_to_pid(Config) when is_list(Config) ->
104    Me = self(),
105    MyListedPid = pid_to_list(Me),
106    Me = list_to_pid(MyListedPid),
107    case catch list_to_pid(id("Incorrect list")) of
108        {'EXIT', {badarg, _}} ->
109            ok;
110        Res ->
111            ct:fail("list_to_pid/1 with incorrect arg succeeded.~n"
112                    "Result: ~p", [Res])
113    end,
114    ok.
115
116%% Test list_to_port/1 with correct and incorrect arguments.
117
118t_list_to_port(Config) when is_list(Config) ->
119    Me = hd(erlang:ports()),
120    MyListedPid = port_to_list(Me),
121    Me = list_to_port(MyListedPid),
122    case catch list_to_port(id("Incorrect list")) of
123        {'EXIT', {badarg, _}} ->
124            ok;
125        Res ->
126            ct:fail("list_to_port/1 with incorrect arg succeeded.~n"
127                    "Result: ~p", [Res])
128    end,
129    ok.
130
131t_list_to_ref(Config) when is_list(Config) ->
132    Ref = make_ref(),
133    RefStr = ref_to_list(Ref),
134    Ref = list_to_ref(RefStr),
135    case catch list_to_ref(id("Incorrect list")) of
136        {'EXIT', {badarg, _}} ->
137            ok;
138        Res ->
139            ct:fail("list_to_ref/1 with incorrect arg succeeded.~n"
140                    "Result: ~p", [Res])
141    end,
142    ok.
143
144%% Test list_to_pid/port/ref for external pids/ports/refs.
145t_list_to_ext_pidportref(Config) when is_list(Config) ->
146    {ok, Node} = slave:start(net_adm:localhost(), t_list_to_ext_pidportref),
147    Pid = rpc:call(Node, erlang, self, []),
148    Port = hd(rpc:call(Node, erlang, ports, [])),
149    Ref = rpc:call(Node, erlang, make_ref, []),
150
151    PidStr  = pid_to_list(Pid),
152    PortStr = port_to_list(Port),
153    RefStr  = ref_to_list(Ref),
154
155    Pid2  = list_to_pid(PidStr),
156    Port2 = list_to_port(PortStr),
157    Ref2  = list_to_ref(RefStr),
158
159    %% Local roundtrips of externals work from OTP-23
160    %% as even though 'creation' is missing in the string formats
161    %% we know the 'creation' of the connected node and list_to_* use that.
162    true = (Pid =:= Pid2),
163    true = (Port =:= Port2),
164    true = (Ref =:= Ref2),
165    true = (Pid == Pid2),
166    true = (Port == Port2),
167    true = (Ref == Ref2),
168
169    %% And it works when sent back to the same node instance,
170    %% which was connected when list_to_* were called.
171    true = rpc:call(Node, erlang, '=:=', [Pid, Pid2]),
172    true = rpc:call(Node, erlang, '==',  [Pid, Pid2]),
173    true = rpc:call(Node, erlang, '=:=', [Port, Port2]),
174    true = rpc:call(Node, erlang, '==',  [Port, Port2]),
175    true = rpc:call(Node, erlang, '=:=', [Ref, Ref2]),
176    true = rpc:call(Node, erlang, '==',  [Ref, Ref2]),
177
178    %% Make sure no ugly comparison with 0-creation as wildcard is done.
179    Pid0 = make_0_creation(Pid),
180    Port0 = make_0_creation(Port),
181    Ref0 = make_0_creation(Ref),
182    false = (Pid =:= Pid0),
183    false = (Port =:= Port0),
184    false = (Ref =:= Ref0),
185    false = (Pid == Pid0),
186    false = (Port == Port0),
187    false = (Ref == Ref0),
188
189    %% Check 0-creations are converted to local node creations
190    %% when sent to matching node name.
191    true = rpc:call(Node, erlang, '=:=', [Pid, Pid0]),
192    true = rpc:call(Node, erlang, '==',  [Pid, Pid0]),
193    true = rpc:call(Node, erlang, '=:=', [Port, Port0]),
194    true = rpc:call(Node, erlang, '==',  [Port, Port0]),
195    true = rpc:call(Node, erlang, '=:=', [Ref, Ref0]),
196    true = rpc:call(Node, erlang, '==',  [Ref, Ref0]),
197
198    slave:stop(Node),
199    ok.
200
201-define(NEW_PID_EXT, 88).
202-define(NEW_PORT_EXT, 89).
203-define(NEWER_REFERENCE_EXT, 90).
204
205%% Copy pid/port/ref but set creation=0
206make_0_creation(X) when is_pid(X); is_port(X); is_reference(X) ->
207    B = term_to_binary(X),
208    Sz = byte_size(B),
209    B2 = case B of
210             <<131, ?NEW_PID_EXT, _/binary>> ->
211                 PreSz = Sz - 4,
212                 <<_:PreSz/binary, Cr:32>> = B,
213                 true = (Cr =/= 0),
214                 <<B:PreSz/binary, 0:32>>;
215             <<131, ?NEW_PORT_EXT, _/binary>> ->
216                 PreSz = Sz - 4,
217                 <<_:PreSz/binary, Cr:32>> = B,
218                 true = (Cr =/= 0),
219                 <<B:PreSz/binary, 0:32>>;
220             <<131, ?NEWER_REFERENCE_EXT, Len:16, _/binary>> ->
221                 PostSz = Len*4,
222                 PreSz = Sz - (4 + PostSz),
223                 <<_:PreSz/binary, Cr:32, PostFix:PostSz/binary>> = B,
224                 true = (Cr =/= 0),
225                 <<B:PreSz/binary, 0:32, PostFix/binary>>
226         end,
227    binary_to_term(B2).
228
229
230%% Test list_to_float/1 with correct and incorrect arguments.
231
232t_list_to_float(Config) when is_list(Config) ->
233    5.89000 = list_to_float(id("5.89")),
234    5.89898 = list_to_float(id("5.89898")),
235    case catch list_to_float(id("58")) of
236        {'EXIT', {badarg, _}} -> ok;
237        Res ->
238            ct:fail("list_to_float with incorrect arg succeeded.~nResult: ~p", [Res])
239    end,
240    ok.
241
242id(I) -> I.
243