1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2004-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-module(ei_decode_SUITE).
23
24-include_lib("common_test/include/ct.hrl").
25-include("ei_decode_SUITE_data/ei_decode_test_cases.hrl").
26
27-export([all/0, suite/0,
28         init_per_testcase/2,
29         test_ei_decode_long/1,
30         test_ei_decode_ulong/1,
31         test_ei_decode_longlong/1,
32         test_ei_decode_ulonglong/1,
33         test_ei_decode_char/1,
34         test_ei_decode_nonoptimal/1,
35         test_ei_decode_misc/1,
36         test_ei_decode_utf8_atom/1,
37         test_ei_decode_iodata/1,
38         test_ei_cmp_nc/1]).
39
40suite() -> [{ct_hooks,[ts_install_cth]}].
41
42all() ->
43    [test_ei_decode_long, test_ei_decode_ulong,
44     test_ei_decode_longlong, test_ei_decode_ulonglong,
45     test_ei_decode_char, test_ei_decode_nonoptimal,
46     test_ei_decode_misc, test_ei_decode_utf8_atom,
47     test_ei_decode_iodata, test_ei_cmp_nc].
48
49init_per_testcase(Case, Config) ->
50    runner:init_per_testcase(?MODULE, Case, Config).
51
52%% ---------------------------------------------------------------------------
53
54% NOTE: for historical reasons we don't pach as tight as we can,
55%       we only fill 27 bits in 32 bit INTEGER_EXT
56
57
58%% ######################################################################## %%
59
60test_ei_decode_long(Config) when is_list(Config) ->
61    P = runner:start(Config, ?test_ei_decode_long),
62    send_integers(P),
63    runner:recv_eot(P),
64    ok.
65
66
67%% ######################################################################## %%
68
69test_ei_decode_ulong(Config) when is_list(Config) ->
70    P = runner:start(Config, ?test_ei_decode_ulong),
71    send_integers(P),
72    runner:recv_eot(P),
73    ok.
74
75
76% (*) In practical terms, other values may fit into the ext format
77% i32 is signed 32 bit on C side
78% u32 is unsigned 32 bit on C side
79
80%% ######################################################################## %%
81
82test_ei_decode_longlong(Config) when is_list(Config) ->
83    P = runner:start(Config, ?test_ei_decode_longlong),
84    send_integers2(P),
85    runner:recv_eot(P),
86    ok.
87
88
89%% ######################################################################## %%
90
91test_ei_decode_ulonglong(Config) when is_list(Config) ->
92    P = runner:start(Config, ?test_ei_decode_ulonglong),
93    send_integers2(P),
94    runner:recv_eot(P),
95    ok.
96
97
98%% ######################################################################## %%
99%% A "character" for us is an 8 bit integer, always positive, i.e.
100%% it is unsigned.
101%% FIXME maybe the API should change to use "unsigned char" to be clear?!
102
103test_ei_decode_char(Config) when is_list(Config) ->
104    P = runner:start(Config, ?test_ei_decode_char),
105
106    send_term_as_binary(P,0),
107    send_term_as_binary(P,16#7f),
108    send_term_as_binary(P,16#ff),
109
110    send_term_as_binary(P, []), % illegal type
111
112    runner:recv_eot(P),
113    ok.
114
115
116%% ######################################################################## %%
117
118test_ei_decode_nonoptimal(Config) when is_list(Config) ->
119    P = runner:start(Config, ?test_ei_decode_nonoptimal),
120
121    send_non_optimal_pos(P),			% decode_char
122    send_non_optimal(P),			% decode_long
123    send_non_optimal_pos(P),			% decode_ulong
124    send_non_optimal(P),			% decode_longlong
125    send_non_optimal_pos(P),			% decode_ulonglong
126
127    runner:recv_eot(P),
128    ok.
129
130
131send_non_optimal(P) ->
132    send_non_optimal_pos(P),
133    send_non_optimal_neg(P).
134
135send_non_optimal_pos(P) ->
136    send_raw(P, <<131,97,42>>),
137    send_raw(P, <<131,98,42:32>>),
138    send_raw(P, <<131,110,1,0,42>>),
139    send_raw(P, <<131,110,2,0,42,0>>),
140    send_raw(P, <<131,110,4,0,42,0,0,0>>),
141    send_raw(P, <<131,111,0,0,0,1,0,42>>),
142    send_raw(P, <<131,111,0,0,0,2,0,42,0>>),
143    send_raw(P, <<131,111,0,0,0,3,0,42,0,0>>),
144    send_raw(P, <<131,111,0,0,0,6,0,42,0,0,0,0,0>>),
145    ok.
146
147send_non_optimal_neg(P) ->
148    %   send_raw(P, <<131,97,-42>>),
149    send_raw(P, <<131,98,-42:32>>),
150    send_raw(P, <<131,110,1,1,42>>),
151    send_raw(P, <<131,110,2,1,42,0>>),
152    send_raw(P, <<131,110,4,1,42,0,0,0>>),
153    send_raw(P, <<131,111,0,0,0,1,1,42>>),
154    send_raw(P, <<131,111,0,0,0,2,1,42,0>>),
155    send_raw(P, <<131,111,0,0,0,3,1,42,0,0>>),
156    send_raw(P, <<131,111,0,0,0,6,1,42,0,0,0,0,0>>),
157    ok.
158
159
160%% ######################################################################## %%
161
162test_ei_decode_misc(Config) when is_list(Config) ->
163    P = runner:start(Config, ?test_ei_decode_misc),
164
165    send_term_as_binary(P,0.0),
166    send_term_as_binary(P,-1.0),
167    send_term_as_binary(P,1.0),
168
169    send_term_as_binary(P,false),
170    send_term_as_binary(P,true),
171
172    send_term_as_binary(P,foo),
173    send_term_as_binary(P,''),
174    %%send_term_as_binary(P,'ÅÄÖåäö'),
175    send_latin1_atom_as_binary(P, "ÅÄÖåäö"),
176
177    send_term_as_binary(P,"foo"),
178    send_term_as_binary(P,""),
179    send_term_as_binary(P,"ÅÄÖåäö"),
180
181    send_term_as_binary(P,<<"foo">>),
182    send_term_as_binary(P,<<>>),
183    send_term_as_binary(P,<<"ÅÄÖåäö">>),
184
185    send_term_as_binary(P,<<1, 2, 3:5>>),
186    send_term_as_binary(P,<<1:1>>),
187
188    %    send_term_as_binary(P,{}),
189    %    send_term_as_binary(P,[]),
190
191    runner:recv_eot(P),
192    ok.
193
194%% ######################################################################## %%
195
196test_ei_decode_utf8_atom(Config) ->
197    P = runner:start(Config, ?test_ei_decode_utf8_atom),
198
199    send_latin1_atom_as_binary(P,"å"),
200    send_latin1_atom_as_binary(P,"ä"),
201    send_latin1_atom_as_binary(P,"ö"),
202    send_latin1_atom_as_binary(P,"õ"),
203
204    send_utf8_atom_as_binary(P,[1758]),
205    send_utf8_atom_as_binary(P,[1758,1758]),
206    send_utf8_atom_as_binary(P,[1758,1758,1758]),
207    send_utf8_atom_as_binary(P,[1758,1758,1758,1758]),
208
209    send_latin1_atom_as_binary(P,"a"),
210    send_latin1_atom_as_binary(P,"b"),
211
212    send_term_as_binary(P,'c'),
213    send_term_as_binary(P,'d'),
214
215    runner:recv_eot(P),
216    ok.
217
218
219%% ######################################################################## %%
220
221test_ei_decode_iodata(Config) when is_list(Config) ->
222    P = runner:start(Config, ?test_ei_decode_iodata),
223
224    check_decode_iodata(P, [], true),
225    check_decode_iodata(P, $a, false),
226    check_decode_iodata(P, an_atom, false),
227    check_decode_iodata(P, self(), false),
228    check_decode_iodata(P, [$a,$a], true),
229    check_decode_iodata(P, [$a|$a], false),
230    check_decode_iodata(P, [[$a|$a],$a], false),
231    check_decode_iodata(P, "hej", true),
232    check_decode_iodata(P, ["hej", " ", "hopp"], true),
233    check_decode_iodata(P, <<"hopp san sa">>, true),
234    check_decode_iodata(P, [$a | <<"a">>], true),
235    check_decode_iodata(P, [[[["hej"]]], [$ , <<"hopp">>, $ , "san" | <<" sa">>]], true),
236    check_decode_iodata(P, [[[["hej"]]], [$ , <<"hopp">>, 0, "san" | <<" sa">>]], true),
237    check_decode_iodata(P, [[[["hej"]]], [$ , <<"hopp">>, 256, "san" | <<" sa">>]], false),
238    check_decode_iodata(P, [[[["hej"]]], [$ , <<"hopp">>, -2, "san" | <<" sa">>]], false),
239    check_decode_iodata(P, [[[["hej"]]], [$ , <<"hopp">>, $ , san | <<" sa">>]], false),
240    check_decode_iodata(P, [[[["hej"]]], [$ , <<"hopp">>, $ , "san s" | $a], "  "], false),
241    check_decode_iodata(P, [[[[[[[]|<<"a">>]]]]]], true),
242    check_decode_iodata(P, [[[[[[[[]]]]]]],[[[],[],[]]]], true),
243
244    send_raw(P, <<"done">>),
245    runner:recv_eot(P),
246    ok.
247
248check_decode_iodata(P, Data, Valid) ->
249    io:format("~n~nChecking: ~p~n", [Data]),
250    Expect = case Valid of
251                 true ->
252                     io:format("Expecting decode SUCCESS... ", []),
253                     iolist_to_binary(Data);
254                 false ->
255                     io:format("Expecting decode FAILURE... ", []),
256                     badarg = try
257                                  iolist_to_binary(Data)
258                              catch
259                                  error:badarg ->
260                                      badarg
261                              end,
262                     decode_size_failed
263             end,
264    send_term_as_binary(P, Data),
265    Actual = case runner:get_term(P) of
266                 {bytes, B} when is_binary(B) ->
267                     B;
268                 {bytes, L} when is_list(L) ->
269                     list_to_binary(L);
270                 {term, T} ->
271                     T
272             end,
273    case Expect =:= Actual of
274        true ->
275            io:format("Expected result!~n",[]),
276            ok;
277        false ->
278            io:format("Expect: ~w~nActual: ~w~n", [Expect, Actual]),
279            ct:fail(unexpected_result)
280    end.
281
282%% ######################################################################## %%
283
284%% Should be moved to its own suite...
285
286test_ei_cmp_nc(Config) when is_list(Config) ->
287    P = runner:start(Config, ?test_ei_cmp_nc),
288    R0 = make_ref(),
289    R1 = make_ref(),
290    check_cmp(P, R0, R0),
291    check_cmp(P, R0, R1),
292    check_cmp(P, R1, R0),
293    check_cmp(P, mk_ref({a@b, 4711}, [17, 17, 17]), mk_ref({a@c, 4711}, [17, 17, 17])),
294    check_cmp(P, mk_ref({a@b, 4711}, [17, 17, 17]), mk_ref({a@d, 4711}, [17, 17, 17])),
295    check_cmp(P, mk_ref({a@b, 4711}, [17, 17, 17]), mk_ref({a@bc, 4711}, [17, 17, 17])),
296    check_cmp(P, mk_ref({a@b, 4712}, [17, 17, 17]), mk_ref({a@b, 4711}, [17, 17, 17])),
297    check_cmp(P, mk_ref({a@b, 4711}, [17, 17, 17]), mk_ref({a@b, 4711}, [18, 17, 17])),
298    check_cmp(P, mk_ref({a@b, 4711}, [17, 17, 17]), mk_ref({a@b, 4711}, [17, 18, 17])),
299    check_cmp(P, mk_ref({a@b, 4711}, [17, 17, 17]), mk_ref({a@b, 4711}, [17, 17, 18])),
300    check_cmp(P, mk_ref({a@b, 4711}, [0, 17]), mk_ref({a@b, 4711}, [18])),
301    check_cmp(P, mk_ref({a@b, 4711}, [17, 17]), mk_ref({a@b, 4711}, [17, 18])),
302    check_cmp(P, mk_ref({a@b, 4711}, [0, 0, 0]), mk_ref({a@b, 4711}, [17])),
303    check_cmp(P, mk_ref({a@b, 4711}, [0, 0, 17]), mk_ref({a@b, 4711}, [18, 17])),
304    check_cmp(P, mk_ref({a@b, 4711}, [0, 17, 17]), mk_ref({a@b, 4711}, [18])),
305
306    check_cmp(P, self(), self()),
307    check_cmp(P, self(), whereis(file_server_2)),
308    check_cmp(P, whereis(file_server_2), self()),
309    check_cmp(P, mk_pid({a@b, 4711}, 17, 17), mk_pid({a@c, 4711}, 17, 17)),
310    check_cmp(P, mk_pid({a@b, 4711}, 17, 17), mk_pid({a@d, 4711}, 17, 17)),
311    check_cmp(P, mk_pid({a@b, 4711}, 17, 17), mk_pid({a@bc, 4711}, 17, 17)),
312    check_cmp(P, mk_pid({a@b, 4712}, 17, 17), mk_pid({a@b, 4711}, 17, 17)),
313    check_cmp(P, mk_pid({a@b, 4711}, 17, 17), mk_pid({a@b, 4711}, 18, 17)),
314    check_cmp(P, mk_pid({a@b, 4711}, 17, 17), mk_pid({a@b, 4711}, 17, 18)),
315
316    %% Test full 64-bit pids
317    Vs = [1 bsl 30, 1 bsl 31, (1 bsl 31)-1, (1 bsl 32)-1],
318    [check_cmp(P, mk_pid({a@b, 4711}, A, B), mk_pid({a@b, 4711}, C, D))
319     || A <- Vs, B <- Vs, C <- Vs, D <- Vs],
320
321    Cmd = case os:type() of
322              {win32, _} -> "cmd /q /c true";
323              _ -> "true"
324          end,
325
326    Prt0 = open_port({spawn, Cmd},[]),
327    Prt1 = open_port({spawn, Cmd},[]),
328
329    check_cmp(P, Prt0, Prt0),
330    check_cmp(P, Prt1, Prt0),
331    check_cmp(P, Prt0, Prt1),
332    check_cmp(P, mk_port({a@b, 4711}, 17), mk_port({a@b, 4711}, 17)),
333    check_cmp(P, mk_port({a@b, 4711}, 17), mk_port({a@d, 4711}, 17)),
334    check_cmp(P, mk_port({a@b, 4711}, 17), mk_port({a@bc, 4711}, 17)),
335    check_cmp(P, mk_port({a@b, 4712}, 17), mk_port({a@b, 4711}, 17)),
336    check_cmp(P, mk_port({a@b, 4711}, 17), mk_port({a@b, 4711}, 18)),
337
338    send_raw(P, <<"done">>),
339    runner:recv_eot(P),
340    ok.
341
342mk_pid(Node, Num, Ser) ->
343    erts_test_utils:mk_ext_pid(Node, Num, Ser).
344
345mk_port(Node, Id) ->
346    erts_test_utils:mk_ext_port(Node, Id).
347
348mk_ref(Node, Numbers) ->
349    erts_test_utils:mk_ext_ref(Node, Numbers).
350
351check_cmp(P, A, B) when is_pid(A), is_pid(B) ->
352    check_cmp(P, {cmp_pids, A, B});
353check_cmp(P, A, B) when is_port(A), is_port(B) ->
354    check_cmp(P, {cmp_ports, A, B});
355check_cmp(P, A, B) when is_reference(A), is_reference(B) ->
356    check_cmp(P, {cmp_refs, A, B}).
357
358check_cmp(P, {_, A, B} = Data) ->
359    io:format("~n~nChecking: ~p~n", [Data]),
360    send_term_as_binary(P, Data),
361    {term, Res} = runner:get_term(P),
362    io:format("Res = ~p~n", [Res]),
363    case {{ei_cmp, Res}, {erlang, cmp_nc(A, B)}} of
364        {{ei_cmp, 0}, {erlang, equal}} ->
365            ok;
366        {{ei_cmp, Cmp}, {erlang, less_than}} when is_integer(Cmp),
367                                                  Cmp < 0 ->
368            ok;
369        {{ei_cmp, Cmp}, {erlang, larger_than}} when is_integer(Cmp),
370                                                    Cmp > 0 -> ok;
371        Fail ->
372            ct:fail(Fail)
373    end.
374
375cmp_nc(A, A) ->
376    equal;
377cmp_nc(A, B) when A < B ->
378    less_than;
379cmp_nc(_, _) ->
380    larger_than.
381
382
383%% ######################################################################## %%
384
385send_term_as_binary(Port, Term) when is_port(Port) ->
386    Port ! {self(), {command, term_to_binary(Term)}}.
387
388send_raw(Port, Bin) when is_port(Port) ->
389    Port ! {self(), {command, Bin}}.
390
391send_utf8_atom_as_binary(Port, String) ->
392    Port ! {self(), {command, term_to_binary(uc_atup(String))}}.
393
394send_latin1_atom_as_binary(Port, String) ->
395    Port ! {self(), {command, encode_latin1_atom(String)}}.
396
397send_integers(P) ->
398    send_term_as_binary(P,0),		% SMALL_INTEGER_EXT smallest
399    send_term_as_binary(P,255),		% SMALL_INTEGER_EXT largest
400    send_term_as_binary(P,256),		% INTEGER_EXT smallest pos (*)
401    send_term_as_binary(P,-1),		% INTEGER_EXT largest  neg
402
403    send_term_as_binary(P, 16#07ffffff),	% INTEGER_EXT old largest (28 bits)
404    send_term_as_binary(P,-16#08000000),	% INTEGER_EXT old smallest
405    send_term_as_binary(P, 16#08000000),  % SMALL_BIG_EXT old smallest pos(*)
406    send_term_as_binary(P,-16#08000001),	% SMALL_BIG_EXT old largest neg (*)
407
408    send_term_as_binary(P, 16#7fffffff),	% INTEGER_EXT new largest (32 bits)
409    send_term_as_binary(P,-16#80000000),	% INTEGER_EXT new smallest (32 bis)
410    send_term_as_binary(P, 16#80000000),  % SMALL_BIG_EXT new smallest pos(*)
411    send_term_as_binary(P,-16#80000001),	% SMALL_BIG_EXT new largest neg (*)
412
413    case erlang:system_info({wordsize,external}) of
414        4 ->
415            send_term_as_binary(P, 16#80000000),% SMALL_BIG_EXT u32
416            send_term_as_binary(P, 16#ffffffff),% SMALL_BIG_EXT largest u32
417
418            send_term_as_binary(P, 16#7fffffffffff), % largest  i48
419            send_term_as_binary(P,-16#800000000000), % smallest i48
420            send_term_as_binary(P, 16#ffffffffffff), % largest  u48
421            send_term_as_binary(P, 16#7fffffffffffffff), % largest  i64
422            send_term_as_binary(P,-16#8000000000000000), % smallest i64
423            send_term_as_binary(P, 16#ffffffffffffffff); % largest  u64
424        8 ->
425            send_term_as_binary(P, 16#8000000000000000),% SMALL_BIG_EXT u64
426            % SMALL_BIG_EXT largest u64
427            send_term_as_binary(P, 16#ffffffffffffffff),
428            % largest  i96
429            send_term_as_binary(P, 16#7fffffffffffffffffffffff),
430            % smallest i96
431            send_term_as_binary(P,-16#800000000000000000000000),
432            % largest  u96
433            send_term_as_binary(P, 16#ffffffffffffffffffffffff),
434            % largest  i128
435            send_term_as_binary(P, 16#7fffffffffffffffffffffffffffffff),
436            % smallest i128
437            send_term_as_binary(P,-16#80000000000000000000000000000000),
438            % largest  u128
439            send_term_as_binary(P, 16#ffffffffffffffffffffffffffffffff)
440    end,
441    send_term_as_binary(P, []), % illegal type
442    ok.
443
444send_integers2(P) ->
445    send_term_as_binary(P,0),           % SMALL_INTEGER_EXT smallest
446    send_term_as_binary(P,255),	        % SMALL_INTEGER_EXT largest
447    send_term_as_binary(P,256),	        % INTEGER_EXT smallest pos (*)
448    send_term_as_binary(P,-1),          % INTEGER_EXT largest  neg
449
450    send_term_as_binary(P, 16#07ffffff),     % INTEGER_EXT old largest (28 bits)
451    send_term_as_binary(P,-16#08000000),     % INTEGER_EXT old smallest
452    send_term_as_binary(P, 16#08000000),     % SMALL_BIG_EXT old smallest pos(*)
453    send_term_as_binary(P,-16#08000001),     % SMALL_BIG_EXT old largest neg (*)
454
455    send_term_as_binary(P, 16#7fffffff),     % INTEGER_EXT new largest (32 bits)
456    send_term_as_binary(P,-16#80000000),     % INTEGER_EXT new smallest
457    send_term_as_binary(P, 16#80000000),     % SMALL_BIG_EXT new smallest pos(*)
458    send_term_as_binary(P,-16#80000001),     % SMALL_BIG_EXT new largest neg (*)
459
460    send_term_as_binary(P, 16#ffffffff),     % SMALL_BIG_EXT largest u32
461
462    send_term_as_binary(P, 16#7fffffffffff),     % largest  i48
463    send_term_as_binary(P,-16#800000000000),     % smallest i48
464    send_term_as_binary(P, 16#ffffffffffff),     % largest  u48
465    send_term_as_binary(P, 16#7fffffffffffffff), % largest  i64
466    send_term_as_binary(P,-16#8000000000000000), % smallest i64
467    send_term_as_binary(P, 16#ffffffffffffffff), % largest  u64
468    send_term_as_binary(P, []), % illegal type
469    ok.
470
471encode_latin1_atom(String) ->
472    Len = length(String),
473    %% Use ATOM_EXT (not SMALL_*) to simulate old term_to_binary
474    TagLen = [$d, Len bsr 8, Len band 16#ff],
475    list_to_binary([131, TagLen, String]).
476
477uc_atup(ATxt) ->
478    string_to_atom(ATxt).
479
480string_to_atom(String) ->
481    Utf8List = string_to_utf8_list(String),
482    Len = length(Utf8List),
483    TagLen = case Len < 256 of
484                 true -> [119, Len];
485                 false -> [118, Len bsr 8, Len band 16#ff]
486             end,
487    binary_to_term(list_to_binary([131, TagLen, Utf8List])).
488
489string_to_utf8_list([]) ->
490    [];
491string_to_utf8_list([CP|CPs]) when is_integer(CP),
492                                   0 =< CP,
493                                   CP =< 16#7F ->
494    [CP | string_to_utf8_list(CPs)];
495string_to_utf8_list([CP|CPs]) when is_integer(CP),
496                                   16#80 =< CP,
497                                   CP =< 16#7FF ->
498    [16#C0 bor (CP bsr 6),
499     16#80 bor (16#3F band CP) | string_to_utf8_list(CPs)];
500string_to_utf8_list([CP|CPs]) when is_integer(CP),
501                                   16#800 =< CP,
502                                   CP =< 16#FFFF ->
503    [16#E0 bor (CP bsr 12),
504     16#80 bor (16#3F band (CP bsr 6)),
505     16#80 bor (16#3F band CP) | string_to_utf8_list(CPs)];
506string_to_utf8_list([CP|CPs]) when is_integer(CP),
507                                   16#10000 =< CP,
508                                   CP =< 16#10FFFF ->
509    [16#F0 bor (CP bsr 18),
510     16#80 bor (16#3F band (CP bsr 12)),
511     16#80 bor (16#3F band (CP bsr 6)),
512     16#80 bor (16#3F band CP) | string_to_utf8_list(CPs)].
513