1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1998-2016. 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-module(bif_SUITE).
21-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
22	 init_per_group/2,end_per_group/2]).
23
24-export([
25	  spawn1/1, spawn2/1, spawn3/1, spawn4/1,
26
27
28	  spawn_link1/1, spawn_link2/1, spawn_link3/1, spawn_link4/1,
29
30
31	  spawn_opt2/1, spawn_opt3/1, spawn_opt4/1, spawn_opt5/1,
32
33	  spawn_failures/1,
34
35	  run_fun/1,
36	  decode_packet_delim/1,
37	  wilderness/1]).
38
39-export([init_per_testcase/2, end_per_testcase/2]).
40
41-include_lib("common_test/include/ct.hrl").
42
43init_per_testcase(_Case, Config) ->
44    Config.
45
46end_per_testcase(_Case, _Config) ->
47    ok.
48
49suite() ->
50    [{ct_hooks,[ts_install_cth]},
51     {timetrap,{minutes,1}}].
52
53all() ->
54    [{group, spawn_tests}, {group, spawn_link_tests},
55     {group, spawn_opt_tests}, spawn_failures, wilderness].
56
57groups() ->
58    [{spawn_tests, [], [spawn1, spawn2, spawn3, spawn4]},
59     {spawn_link_tests, [],
60      [spawn_link1, spawn_link2, spawn_link3, spawn_link4]},
61     {spawn_opt_tests, [],
62      [spawn_opt2, spawn_opt3, spawn_opt4, spawn_opt5]}].
63
64init_per_suite(Config) ->
65    Config.
66
67end_per_suite(_Config) ->
68    ok.
69
70init_per_group(_GroupName, Config) ->
71    Config.
72
73end_per_group(_GroupName, Config) ->
74    Config.
75
76
77%% Test spawn/1.
78spawn1(Config) when is_list(Config) ->
79    Node = node(),
80    Parent = self(),
81    {_, _, FA, _} = fetch_proc_vals(self()),
82
83    %% spawn
84    P = spawn(fun() -> Parent ! {self(), fetch_proc_vals(self())} end),
85    receive
86	{P, PV} ->
87	    Node = node(P),
88	    check_proc_vals(false, normal, FA, 0, PV)
89    end,
90    ok.
91
92%% Test spawn/2.
93spawn2(Config) when is_list(Config) ->
94    {ok, Node} = start_node(spawn2),
95
96    Parent = self(),
97    {_, _, FA, _} = fetch_proc_vals(self()),
98
99    %% spawn_link
100    P = spawn(Node,
101	      fun() -> Parent ! {self(), fetch_proc_vals(self())} end),
102    receive
103	{P, PV} ->
104	    Node = node(P),
105	    check_proc_vals(false, normal, FA, 0, PV)
106    end,
107
108    true = stop_node(Node),
109    ok.
110
111
112%% Test spawn/3.
113spawn3(Config) when is_list(Config) ->
114    Node = node(),
115
116    Parent = self(),
117    {_, _, FA, _} = fetch_proc_vals(self()),
118
119    %% spawn_link
120    P = spawn(?MODULE,
121	      run_fun,
122	      [fun() ->
123		       Parent ! {self(), fetch_proc_vals(self())}
124	       end]),
125    receive
126	{P, PV} ->
127	    Node = node(P),
128	    check_proc_vals(false, normal, FA, 0, PV)
129    end,
130    ok.
131
132%% Test spawn/4.
133spawn4(Config) when is_list(Config) ->
134    {ok, Node} = start_node(spawn4),
135
136    Parent = self(),
137    {_, _, FA, _} = fetch_proc_vals(self()),
138
139    %% spawn_link
140    P = spawn(Node,
141	      ?MODULE,
142	      run_fun,
143	      [fun() ->
144		       Parent ! {self(), fetch_proc_vals(self())}
145	       end]),
146    receive
147	{P, PV} ->
148	    Node = node(P),
149	    check_proc_vals(false, normal, FA, 0, PV)
150    end,
151
152    true = stop_node(Node),
153    ok.
154
155
156
157%% Test spawn_link/1.
158spawn_link1(Config) when is_list(Config) ->
159    Node = node(),
160    Parent = self(),
161    {_, _, FA, _} = fetch_proc_vals(self()),
162
163    %% spawn_link
164    P = spawn_link(fun() -> Parent ! {self(), fetch_proc_vals(self())} end),
165    receive
166	{P, PV} ->
167	    Node = node(P),
168	    check_proc_vals(true, normal, FA, 0, PV)
169    end,
170    ok.
171
172%% Test spawn_link/2.
173spawn_link2(Config) when is_list(Config) ->
174    {ok, Node} = start_node(spawn_link2),
175
176    Parent = self(),
177    {_, _, FA, _} = fetch_proc_vals(self()),
178
179    %% spawn_link
180    P = spawn_link(Node,
181		   fun() -> Parent ! {self(), fetch_proc_vals(self())} end),
182    receive
183	{P, PV} ->
184	    Node = node(P),
185	    check_proc_vals(true, normal, FA, 0, PV)
186    end,
187
188    true = stop_node(Node),
189    ok.
190
191%% Test spawn_link/3.
192spawn_link3(Config) when is_list(Config) ->
193    Node = node(),
194
195    Parent = self(),
196    {_, _, FA, _} = fetch_proc_vals(self()),
197
198    %% spawn_link
199    P = spawn_link(?MODULE,
200		   run_fun,
201		   [fun() ->
202			    Parent ! {self(), fetch_proc_vals(self())}
203		    end]),
204    receive
205	{P, PV} ->
206	    Node = node(P),
207	    check_proc_vals(true, normal, FA, 0, PV)
208    end,
209    ok.
210
211%% Test spawn_link/4.
212spawn_link4(Config) when is_list(Config) ->
213    {ok, Node} = start_node(spawn_link4),
214
215    Parent = self(),
216    {_, _, FA, _} = fetch_proc_vals(self()),
217
218    %% spawn_link
219    P = spawn_link(Node,
220		   ?MODULE,
221		   run_fun,
222		   [fun() ->
223			    Parent ! {self(), fetch_proc_vals(self())}
224		    end]),
225    receive
226	{P, PV} ->
227	    Node = node(P),
228	    check_proc_vals(true, normal, FA, 0, PV)
229    end,
230
231    true = stop_node(Node),
232    ok.
233
234
235%% Test spawn_opt/2.
236spawn_opt2(Config) when is_list(Config) ->
237    Node = node(),
238    Parent = self(),
239    {_, _, FA, _} = fetch_proc_vals(self()),
240
241    P1 = spawn_opt(fun() ->
242			   Parent ! {self(), fetch_proc_vals(self())}
243		   end,
244		   [{fullsweep_after, 0},{min_heap_size, 1000},
245		    link, {priority, max}]),
246    receive
247	{P1, PV1} ->
248	    Node = node(P1),
249	    check_proc_vals(true, max, 0, 1000, PV1)
250    end,
251    P2 = spawn_opt(fun() -> Parent ! {self(), fetch_proc_vals(self())} end,
252		   [{min_heap_size, 10}]),
253    receive
254	{P2, PV2} ->
255	    Node = node(P2),
256	    check_proc_vals(false, normal, FA, 10, PV2)
257    end,
258    ok.
259
260%% Test spawn_opt/3.
261spawn_opt3(Config) when is_list(Config) ->
262    {ok, Node} = start_node(spawn_opt3),
263    Parent = self(),
264    {_, _, FA, _} = fetch_proc_vals(self()),
265    P1 = spawn_opt(Node,
266		   fun() ->
267			   Parent ! {self(), fetch_proc_vals(self())}
268		   end,
269		   [{fullsweep_after,0}, {min_heap_size,1000},
270		    link, {priority, max}]),
271    receive
272	{P1, PV1} ->
273	    Node = node(P1),
274	    check_proc_vals(true, max, 0, 1000, PV1)
275    end,
276    P2 = spawn_opt(Node,
277		   fun() -> Parent ! {self(), fetch_proc_vals(self())} end,
278		   [{min_heap_size, 10}]),
279    receive
280	{P2, PV2} ->
281	    Node = node(P2),
282	    check_proc_vals(false, normal, FA, 10, PV2)
283    end,
284    true = stop_node(Node),
285    ok.
286
287%% Test spawn_opt/4.
288spawn_opt4(Config) when is_list(Config) ->
289    Node = node(),
290    Parent = self(),
291    {_, _, FA, _} = fetch_proc_vals(self()),
292    P1 = spawn_opt(?MODULE,
293		   run_fun,
294		   [fun() ->
295			    Parent ! {self(), fetch_proc_vals(self())}
296		    end],
297		   [{fullsweep_after,0}, {min_heap_size,1000},
298		    link, {priority, max}]),
299    receive
300	{P1, PV1} ->
301	    Node = node(P1),
302	    check_proc_vals(true, max, 0, 1000, PV1)
303    end,
304    P2 = spawn_opt(?MODULE,
305		   run_fun,
306		   [fun() ->
307			    Parent ! {self(), fetch_proc_vals(self())}
308		    end],
309		   [{min_heap_size, 10}]),
310    receive
311	{P2, PV2} ->
312	    Node = node(P2),
313	    check_proc_vals(false, normal, FA, 10, PV2)
314    end,
315    ok.
316
317%% Test spawn_opt/5.
318spawn_opt5(Config) when is_list(Config) ->
319    {ok, Node} = start_node(spawn_opt5),
320    Parent = self(),
321    {_, _, FA, _} = fetch_proc_vals(self()),
322    P1 = spawn_opt(Node,
323		   ?MODULE,
324		   run_fun,
325		   [fun() ->
326			    Parent ! {self(), fetch_proc_vals(self())}
327		    end],
328		   [{fullsweep_after,0}, {min_heap_size,1000},
329		    link, {priority, max}]),
330    receive
331	{P1, PV1} ->
332	    Node = node(P1),
333	    check_proc_vals(true, max, 0, 1000, PV1)
334    end,
335    P2 = spawn_opt(Node,
336		   ?MODULE,
337		   run_fun,
338		   [fun() ->
339			    Parent ! {self(), fetch_proc_vals(self())}
340		    end],
341		   [{min_heap_size, 10}]),
342    receive
343	{P2, PV2} ->
344	    Node = node(P2),
345	    check_proc_vals(false, normal, FA, 10, PV2)
346    end,
347    true = stop_node(Node),
348    ok.
349
350%% Test failure behavior of spawn bifs.
351spawn_failures(Config) when is_list(Config) ->
352    ThisNode = node(),
353    {ok, Node} = start_node(spawn_remote_failure),
354
355    %% unknown nodes
356    io:format("Testing unknown nodes~n", []),
357    CrashPid1 = (catch spawn_opt('unknown@node',
358				 erlang,
359				 nodes,
360				 [],
361				 [])),
362    true = is_pid(CrashPid1),
363    ThisNode = node(CrashPid1),
364    CrashPid2 = (catch spawn_opt('unknown@node',
365				 fun () -> erlang:nodes() end,
366				 [])),
367    true = is_pid(CrashPid2),
368    ThisNode = node(CrashPid2),
369
370    CrashPid3 = (catch spawn('unknown@node',
371			     erlang,
372			     nodes,
373			     [])),
374    true = is_pid(CrashPid3),
375    ThisNode = node(CrashPid3),
376    CrashPid4 = (catch spawn('unknown@node',
377			     fun () -> erlang:nodes() end)),
378    true = is_pid(CrashPid4),
379    ThisNode = node(CrashPid4),
380
381    OTE = process_flag(trap_exit,true),
382    CrashPid5 = (catch spawn_link('unknown@node',
383				  erlang,
384				  nodes,
385				  [])),
386    receive
387	{'EXIT', CrashPid5, noconnection} ->
388	    true = is_pid(CrashPid5),
389	    ThisNode = node(CrashPid5)
390    end,
391    CrashPid6 = (catch spawn_link('unknown@node',
392				  fun () -> erlang:nodes() end)),
393    receive
394	{'EXIT', CrashPid6, noconnection} ->
395	    true = is_pid(CrashPid6),
396	    ThisNode = node(CrashPid6)
397    end,
398    process_flag(trap_exit,OTE),
399    case OTE of
400	false ->
401	    receive
402		{'EXIT', P, R} ->
403		    ct:fail({'EXIT', P, R})
404	    after 0 ->
405		    ok
406	    end;
407	_ ->
408	    ok
409    end,
410
411    %% bad node
412    io:format("Testing bad nodes~n", []),
413    {'EXIT', {badarg, _}} = (catch spawn_opt("Node",erlang,nodes,[],[])),
414    {'EXIT', {badarg, _}} = (catch spawn_opt("Node",
415					     fun () ->
416						     erlang:nodes()
417					     end,
418					     [])),
419    {'EXIT', {badarg, _}} = (catch spawn_link("Node",
420					      fun () ->
421						      erlang:nodes()
422					      end)),
423    {'EXIT', {badarg, _}} = (catch spawn("Node",erlang,nodes,[])),
424    {'EXIT', {badarg, _}} = (catch spawn("Node",
425					 fun () ->
426						 erlang:nodes()
427					 end)),
428
429    %% bad module
430    io:format("Testing bad modules~n", []),
431    {'EXIT', {badarg, _}} = (catch spawn_opt(Node,"erlang",nodes,[],[])),
432    {'EXIT', {badarg, _}} = (catch spawn_opt("erlang",nodes,[],[])),
433    {'EXIT', {badarg, _}} = (catch spawn_link(Node,"erlang",nodes,[])),
434    {'EXIT', {badarg, _}} = (catch spawn_link("erlang",nodes,[])),
435    {'EXIT', {badarg, _}} = (catch spawn(Node,"erlang",nodes,[])),
436    {'EXIT', {badarg, _}} = (catch spawn("erlang",nodes,[])),
437
438    %% bad function
439    io:format("Testing bad functions~n", []),
440    {'EXIT', {badarg, _}} = (catch spawn_opt(Node,erlang,"nodes",[],[])),
441    {'EXIT', {badarg, _}} = (catch spawn_opt(Node,not_a_fun,[])),
442    {'EXIT', {badarg, _}} = (catch spawn_opt(erlang,"nodes",[],[])),
443    {'EXIT', {badarg, _}} = (catch spawn_opt(not_a_fun,[])),
444    {'EXIT', {badarg, _}} = (catch spawn_link(Node,erlang,"nodes",[])),
445    {'EXIT', {badarg, _}} = (catch spawn_link(Node,not_a_fun)),
446    {'EXIT', {badarg, _}} = (catch spawn_link(erlang,"nodes",[])),
447    {'EXIT', {badarg, _}} = (catch spawn_link(not_a_fun)),
448    {'EXIT', {badarg, _}} = (catch spawn(Node,erlang,"nodes",[])),
449    {'EXIT', {badarg, _}} = (catch spawn(Node,not_a_fun)),
450    {'EXIT', {badarg, _}} = (catch spawn(erlang,"nodes",[])),
451    {'EXIT', {badarg, _}} = (catch spawn(not_a_fun)),
452
453
454    %% bad argument
455    io:format("Testing bad arguments~n", []),
456    {'EXIT', {badarg, _}} = (catch spawn_opt(Node,erlang,nodes,[a|b],[])),
457    {'EXIT', {badarg, _}} = (catch spawn_opt(erlang,nodes,[a|b],[])),
458    {'EXIT', {badarg, _}} = (catch spawn_link(Node,erlang,nodes,[a|b])),
459    {'EXIT', {badarg, _}} = (catch spawn_link(erlang,nodes,[a|b])),
460    {'EXIT', {badarg, _}} = (catch spawn(Node,erlang,nodes,[a|b])),
461    {'EXIT', {badarg, _}} = (catch spawn(erlang,nodes,[a|b])),
462
463    %% bad option
464    io:format("Testing bad options~n", []),
465    {'EXIT', {badarg, _}} = (catch spawn_opt(Node,erlang,nodes,[],[a|b])),
466    {'EXIT', {badarg, _}} = (catch spawn_opt(erlang,nodes,[],[a|b])),
467
468
469    true = stop_node(Node),
470    ok.
471
472check_proc_vals(Link, Priority, FullsweepAfter, MinHeapSize, {Ls, P, FA, HS}) ->
473    Link = lists:member(self(), Ls),
474    Priority = P,
475    FullsweepAfter = FA,
476    true = (HS >= MinHeapSize),
477    ok.
478
479fetch_proc_vals(Pid) ->
480    PI = process_info(Pid),
481    {value,{links, Ls}} = lists:keysearch(links, 1, PI),
482    {value,{priority,P}} = lists:keysearch(priority, 1, PI),
483    {value,{garbage_collection,Gs}} =
484	lists:keysearch(garbage_collection, 1, PI),
485    {value,{fullsweep_after,FA}} =
486	lists:keysearch(fullsweep_after, 1, Gs),
487    {value,{heap_size,HS}} = lists:keysearch(heap_size, 1, PI),
488    {Ls, P, FA, HS}.
489
490%% Test erlang:packet_delim/3 with {line_delimiter,0} option.
491decode_packet_delim(Config) when is_list(Config) ->
492    {ok,<<"abc",0>>,<<"efg",0>>} =
493        erlang:decode_packet(line, <<"abc",0,"efg",0>>, [{line_delimiter, 0}]),
494    {more, undefined} = erlang:decode_packet(line, <<"abc",0,"efg",0>>, []).
495
496%% This testcase should probably be moved somewhere else
497
498%% Test that memory allocation command line options affecting the
499%% wilderness of the heap are interpreted correct by the emulator.
500wilderness(Config) when is_list(Config) ->
501    OKParams = {512, 8},
502    Alloc = erlang:system_info(allocator),
503    io:format("Test server allocator info:~n~p", [Alloc]),
504    Result = case Alloc of
505		 {Allocator, _, _, _} when Allocator == glibc;
506					   Allocator == dlmalloc ->
507		     run_wilderness_test(OKParams, OKParams),
508		     {comment,
509		      "Allocator used: " ++ atom_to_list(Allocator)};
510		 {OtherAllocator, _, _, _} ->
511		     {skipped,
512		      "Only run when glibc is used. "
513		      "Allocator used: "
514		      ++ atom_to_list(OtherAllocator)}
515	     end,
516    Result.
517
518run_wilderness_test({Set_tt, Set_tp}, {Exp_tt, Exp_tp}) ->
519    Self = self(),
520    Ref = make_ref(),
521    SuiteDir = filename:dirname(code:which(?MODULE)),
522    {ok, Node} = test_server:start_node(allocator_test,
523					slave,
524					[{args,
525					  " -pa "
526					  ++ SuiteDir
527					  ++" +MYtt "++to_string(Set_tt)
528					  ++" +MYtp "++to_string(Set_tp)},
529					 {linked, false}]),
530    spawn(Node, fun () ->
531			Self ! {Ref, erlang:system_info(allocator)}
532		end),
533    receive
534	{Ref, {A, V, F, S}} ->
535	    Ett = Exp_tt*1024,
536	    Etp = Exp_tp*1024,
537	    io:format("Test allocator info:~n~p",
538		      [{A, V, F, S}]),
539	    {value, {sys_alloc, SA_Opts}}
540		= lists:keysearch(sys_alloc, 1, S),
541	    {value, {tt, Ett}} = lists:keysearch(tt, 1, SA_Opts),
542	    {value, {tp, Etp}} = lists:keysearch(tp, 1, SA_Opts)
543    end,
544    stop_node(Node).
545
546to_string(X) when is_integer(X) ->
547    integer_to_list(X);
548to_string(X) when is_atom(X) ->
549    atom_to_list(X);
550to_string(X) when is_list(X) ->
551    X.
552
553get_nodenames(N, T) ->
554    get_nodenames(N, T, []).
555
556get_nodenames(0, _, Acc) ->
557    Acc;
558get_nodenames(N, T, Acc) ->
559    {A, B, C} = now(),
560    get_nodenames(N-1, T, [list_to_atom(atom_to_list(?MODULE)
561					++ "-"
562					++ atom_to_list(T)
563					++ "-"
564					++ integer_to_list(A)
565					++ "-"
566					++ integer_to_list(B)
567					++ "-"
568					++ integer_to_list(C)) | Acc]).
569
570start_node(TestCase) ->
571    [Name] = get_nodenames(1, TestCase),
572    Pa = filename:dirname(code:which(?MODULE)),
573    test_server:start_node(Name, slave, [{args, "-pa " ++ Pa}]).
574
575stop_node(Node) ->
576    true = test_server:stop_node(Node).
577
578run_fun(Fun) ->
579    Fun().
580