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    Cmd = case os:type() of
317              {win32, _} -> "cmd /q /c true";
318              _ -> "true"
319          end,
320
321    Prt0 = open_port({spawn, Cmd},[]),
322    Prt1 = open_port({spawn, Cmd},[]),
323
324    check_cmp(P, Prt0, Prt0),
325    check_cmp(P, Prt1, Prt0),
326    check_cmp(P, Prt0, Prt1),
327    check_cmp(P, mk_port({a@b, 4711}, 17), mk_port({a@b, 4711}, 17)),
328    check_cmp(P, mk_port({a@b, 4711}, 17), mk_port({a@d, 4711}, 17)),
329    check_cmp(P, mk_port({a@b, 4711}, 17), mk_port({a@bc, 4711}, 17)),
330    check_cmp(P, mk_port({a@b, 4712}, 17), mk_port({a@b, 4711}, 17)),
331    check_cmp(P, mk_port({a@b, 4711}, 17), mk_port({a@b, 4711}, 18)),
332
333    send_raw(P, <<"done">>),
334    runner:recv_eot(P),
335    ok.
336
337mk_pid(Node, Num, Ser) ->
338    erts_test_utils:mk_ext_pid(Node, Num, Ser).
339
340mk_port(Node, Id) ->
341    erts_test_utils:mk_ext_port(Node, Id).
342
343mk_ref(Node, Numbers) ->
344    erts_test_utils:mk_ext_ref(Node, Numbers).
345
346check_cmp(P, A, B) when is_pid(A), is_pid(B) ->
347    check_cmp(P, {cmp_pids, A, B});
348check_cmp(P, A, B) when is_port(A), is_port(B) ->
349    check_cmp(P, {cmp_ports, A, B});
350check_cmp(P, A, B) when is_reference(A), is_reference(B) ->
351    check_cmp(P, {cmp_refs, A, B}).
352
353check_cmp(P, {_, A, B} = Data) ->
354    io:format("~n~nChecking: ~p~n", [Data]),
355    send_term_as_binary(P, Data),
356    {term, Res} = runner:get_term(P),
357    io:format("Res = ~p~n", [Res]),
358    case {{ei_cmp, Res}, {erlang, cmp_nc(A, B)}} of
359        {{ei_cmp, 0}, {erlang, equal}} ->
360            ok;
361        {{ei_cmp, Cmp}, {erlang, less_than}} when is_integer(Cmp),
362                                                  Cmp < 0 ->
363            ok;
364        {{ei_cmp, Cmp}, {erlang, larger_than}} when is_integer(Cmp),
365                                                    Cmp > 0 -> ok;
366        Fail ->
367            ct:fail(Fail)
368    end.
369
370cmp_nc(A, A) ->
371    equal;
372cmp_nc(A, B) when A < B ->
373    less_than;
374cmp_nc(_, _) ->
375    larger_than.
376
377
378%% ######################################################################## %%
379
380send_term_as_binary(Port, Term) when is_port(Port) ->
381    Port ! {self(), {command, term_to_binary(Term)}}.
382
383send_raw(Port, Bin) when is_port(Port) ->
384    Port ! {self(), {command, Bin}}.
385
386send_utf8_atom_as_binary(Port, String) ->
387    Port ! {self(), {command, term_to_binary(uc_atup(String))}}.
388
389send_latin1_atom_as_binary(Port, String) ->
390    Port ! {self(), {command, encode_latin1_atom(String)}}.
391
392send_integers(P) ->
393    send_term_as_binary(P,0),		% SMALL_INTEGER_EXT smallest
394    send_term_as_binary(P,255),		% SMALL_INTEGER_EXT largest
395    send_term_as_binary(P,256),		% INTEGER_EXT smallest pos (*)
396    send_term_as_binary(P,-1),		% INTEGER_EXT largest  neg
397
398    send_term_as_binary(P, 16#07ffffff),	% INTEGER_EXT old largest (28 bits)
399    send_term_as_binary(P,-16#08000000),	% INTEGER_EXT old smallest
400    send_term_as_binary(P, 16#08000000),  % SMALL_BIG_EXT old smallest pos(*)
401    send_term_as_binary(P,-16#08000001),	% SMALL_BIG_EXT old largest neg (*)
402
403    send_term_as_binary(P, 16#7fffffff),	% INTEGER_EXT new largest (32 bits)
404    send_term_as_binary(P,-16#80000000),	% INTEGER_EXT new smallest (32 bis)
405    send_term_as_binary(P, 16#80000000),  % SMALL_BIG_EXT new smallest pos(*)
406    send_term_as_binary(P,-16#80000001),	% SMALL_BIG_EXT new largest neg (*)
407
408    case erlang:system_info({wordsize,external}) of
409        4 ->
410            send_term_as_binary(P, 16#80000000),% SMALL_BIG_EXT u32
411            send_term_as_binary(P, 16#ffffffff),% SMALL_BIG_EXT largest u32
412
413            send_term_as_binary(P, 16#7fffffffffff), % largest  i48
414            send_term_as_binary(P,-16#800000000000), % smallest i48
415            send_term_as_binary(P, 16#ffffffffffff), % largest  u48
416            send_term_as_binary(P, 16#7fffffffffffffff), % largest  i64
417            send_term_as_binary(P,-16#8000000000000000), % smallest i64
418            send_term_as_binary(P, 16#ffffffffffffffff); % largest  u64
419        8 ->
420            send_term_as_binary(P, 16#8000000000000000),% SMALL_BIG_EXT u64
421            % SMALL_BIG_EXT largest u64
422            send_term_as_binary(P, 16#ffffffffffffffff),
423            % largest  i96
424            send_term_as_binary(P, 16#7fffffffffffffffffffffff),
425            % smallest i96
426            send_term_as_binary(P,-16#800000000000000000000000),
427            % largest  u96
428            send_term_as_binary(P, 16#ffffffffffffffffffffffff),
429            % largest  i128
430            send_term_as_binary(P, 16#7fffffffffffffffffffffffffffffff),
431            % smallest i128
432            send_term_as_binary(P,-16#80000000000000000000000000000000),
433            % largest  u128
434            send_term_as_binary(P, 16#ffffffffffffffffffffffffffffffff)
435    end,
436    send_term_as_binary(P, []), % illegal type
437    ok.
438
439send_integers2(P) ->
440    send_term_as_binary(P,0),           % SMALL_INTEGER_EXT smallest
441    send_term_as_binary(P,255),	        % SMALL_INTEGER_EXT largest
442    send_term_as_binary(P,256),	        % INTEGER_EXT smallest pos (*)
443    send_term_as_binary(P,-1),          % INTEGER_EXT largest  neg
444
445    send_term_as_binary(P, 16#07ffffff),     % INTEGER_EXT old largest (28 bits)
446    send_term_as_binary(P,-16#08000000),     % INTEGER_EXT old smallest
447    send_term_as_binary(P, 16#08000000),     % SMALL_BIG_EXT old smallest pos(*)
448    send_term_as_binary(P,-16#08000001),     % SMALL_BIG_EXT old largest neg (*)
449
450    send_term_as_binary(P, 16#7fffffff),     % INTEGER_EXT new largest (32 bits)
451    send_term_as_binary(P,-16#80000000),     % INTEGER_EXT new smallest
452    send_term_as_binary(P, 16#80000000),     % SMALL_BIG_EXT new smallest pos(*)
453    send_term_as_binary(P,-16#80000001),     % SMALL_BIG_EXT new largest neg (*)
454
455    send_term_as_binary(P, 16#ffffffff),     % SMALL_BIG_EXT largest u32
456
457    send_term_as_binary(P, 16#7fffffffffff),     % largest  i48
458    send_term_as_binary(P,-16#800000000000),     % smallest i48
459    send_term_as_binary(P, 16#ffffffffffff),     % largest  u48
460    send_term_as_binary(P, 16#7fffffffffffffff), % largest  i64
461    send_term_as_binary(P,-16#8000000000000000), % smallest i64
462    send_term_as_binary(P, 16#ffffffffffffffff), % largest  u64
463    send_term_as_binary(P, []), % illegal type
464    ok.
465
466encode_latin1_atom(String) ->
467    Len = length(String),
468    %% Use ATOM_EXT (not SMALL_*) to simulate old term_to_binary
469    TagLen = [$d, Len bsr 8, Len band 16#ff],
470    list_to_binary([131, TagLen, String]).
471
472uc_atup(ATxt) ->
473    string_to_atom(ATxt).
474
475string_to_atom(String) ->
476    Utf8List = string_to_utf8_list(String),
477    Len = length(Utf8List),
478    TagLen = case Len < 256 of
479                 true -> [119, Len];
480                 false -> [118, Len bsr 8, Len band 16#ff]
481             end,
482    binary_to_term(list_to_binary([131, TagLen, Utf8List])).
483
484string_to_utf8_list([]) ->
485    [];
486string_to_utf8_list([CP|CPs]) when is_integer(CP),
487                                   0 =< CP,
488                                   CP =< 16#7F ->
489    [CP | string_to_utf8_list(CPs)];
490string_to_utf8_list([CP|CPs]) when is_integer(CP),
491                                   16#80 =< CP,
492                                   CP =< 16#7FF ->
493    [16#C0 bor (CP bsr 6),
494     16#80 bor (16#3F band CP) | string_to_utf8_list(CPs)];
495string_to_utf8_list([CP|CPs]) when is_integer(CP),
496                                   16#800 =< CP,
497                                   CP =< 16#FFFF ->
498    [16#E0 bor (CP bsr 12),
499     16#80 bor (16#3F band (CP bsr 6)),
500     16#80 bor (16#3F band CP) | string_to_utf8_list(CPs)];
501string_to_utf8_list([CP|CPs]) when is_integer(CP),
502                                   16#10000 =< CP,
503                                   CP =< 16#10FFFF ->
504    [16#F0 bor (CP bsr 18),
505     16#80 bor (16#3F band (CP bsr 12)),
506     16#80 bor (16#3F band (CP bsr 6)),
507     16#80 bor (16#3F band CP) | string_to_utf8_list(CPs)].
508