aboutsummaryrefslogblamecommitdiffstats
path: root/erts/emulator/test/send_term_SUITE.erl
blob: 4deae63ce749d390e65e6b8e0137f385d2eb188b (plain) (tree)
1
2
3
4


                   
                                                        
















                                                                         


                                                                    


                                           
                                                    




                                                                      
                                  


                                  
                                         
 
         
            



            





                         
                                     
           

                                    
           
 






                                        












                                                                         
















                                                                            
                                                          













                                                                      





























                                                           

                                          



                                              

                                 





































































































































                                                                                



                                                                             










































































































                                                                                         
%%
%% %CopyrightBegin%
%% 
%% Copyright Ericsson AB 2005-2014. All Rights Reserved.
%% 
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%% 
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%% 
%% %CopyrightEnd%
%%

-module(send_term_SUITE).

-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 
	 init_per_group/2,end_per_group/2,basic/1]).
-export([init_per_testcase/2,end_per_testcase/2]).

-export([generate_external_terms_files/1]).

-include_lib("test_server/include/test_server.hrl").

init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
    Dog=?t:timetrap(?t:minutes(3)),
    [{watchdog, Dog}|Config].

end_per_testcase(_Func, Config) ->
    Dog=?config(watchdog, Config),
    ?t:timetrap_cancel(Dog).

suite() -> [{ct_hooks,[ts_install_cth]}].

all() -> 
    [basic].

groups() -> 
    [].

init_per_suite(Config) ->
    Config.

end_per_suite(_Config) ->
    ok.

init_per_group(_GroupName, Config) ->
    Config.

end_per_group(_GroupName, Config) ->
    Config.


basic(Config) when is_list(Config) ->
    Drv = "send_term_drv",
    ?line P = start_driver(Config, Drv),

    ?line [] = term(P, 0),
    ?line Self = self(),
    {blurf,42,[],[-42,{}|"abc"++P],"kalle",3.1416,Self,#{}} = term(P, 1),

    Map41 = maps:from_list([{blurf, 42},
			    {[], [-42,{}|"abc"++P]},
			    {"kalle", 3.1416},
			    {Self, #{}}]),
    Map41 = term(P, 41),

    Map42 = maps:from_list([{42, []},
			    {[-42,{}|"abc"++P], "kalle"},
			    {3.1416, Self},
			    {#{}, blurf}]),
    Map42 = term(P, 42),
    ?line Deep = lists:seq(0, 199),
    ?line Deep = term(P, 2),
    ?line {B1,B2} = term(P, 3),
    ?line B1 = list_to_binary(lists:seq(0, 255)),
    ?line B2 = list_to_binary(lists:seq(23, 255-17)),

    %% Pid sending. We need another process.
    ?line Child = spawn_link(fun() ->
				     erlang:port_command(P, [4])
			     end),
    ?line {Self,Child} = receive_any(),

    %% ERL_DRV_EXT2TERM
    ?line ExpectExt2Term = expected_ext2term_drv(?config(data_dir, Config)),
    ?line ExpectExt2Term = term(P, 5),

    %% ERL_DRV_INT, ERL_DRV_UINT
    ?line case erlang:system_info({wordsize, external}) of
	      4 ->
		  ?line {-1, 4294967295} = term(P, 6);
	      8 ->
		  ?line {-1, 18446744073709551615} = term(P, 6)
	  end,

    %% ERL_DRV_BUF2BINARY
    ?line ExpectedBinTup = {<<>>,
			    <<>>,
			    list_to_binary(lists:duplicate(17,17)),
			    list_to_binary(lists:duplicate(1024,17))},
    ?line ExpectedBinTup = term(P, 7),

    %% single terms
    Singles = [{[], 8}, % ERL_DRV_NIL
               {'', 9}, % ERL_DRV_ATOM
               {an_atom, 10}, % ERL_DRV_ATOM
               {-4711, 11}, % ERL_DRV_INT
               {4711, 12}, % ERL_DRV_UINT
               {P, 13}, % ERL_DRV_PORT
               {<<>>, 14}, % ERL_DRV_BINARY
               {<<"hejsan">>, 15}, % ERL_DRV_BINARY
               {<<>>, 16}, % ERL_DRV_BUF2BINARY
               {<<>>, 17}, % ERL_DRV_BUF2BINARY
               {<<"hoppsan">>, 18}, % ERL_DRV_BUF2BINARY
               {"", 19}, % ERL_DRV_STRING
               {"", 20}, % ERL_DRV_STRING
               {"hippsan", 21}, % ERL_DRV_STRING
               {{}, 22}, % ERL_DRV_TUPLE
               {[], 23}, % ERL_DRV_LIST
               {Self, 24}, % ERL_DRV_PID
               {[], 25}, % ERL_DRV_STRING_CONS
               {[], 27}, % ERL_DRV_EXT2TERM
               {18446744073709551615, 28}, % ERL_DRV_UINT64
               {20233590931456, 29}, % ERL_DRV_UINT64
               {4711, 30}, % ERL_DRV_UINT64
               {0, 31}, % ERL_DRV_UINT64
               {9223372036854775807, 32}, % ERL_DRV_INT64
               {20233590931456, 33}, % ERL_DRV_INT64
               {4711, 34}, % ERL_DRV_INT64
               {0, 35}, % ERL_DRV_INT64
               {-1, 36}, % ERL_DRV_INT64
               {-4711, 37}, % ERL_DRV_INT64
               {-20233590931456, 38}, % ERL_DRV_INT64
               {-9223372036854775808, 39},
	       {#{}, 40}], % ERL_DRV_MAP
    ?line {Terms, Ops} = lists:unzip(Singles),
    ?line Terms = term(P,Ops),

    AFloat = term(P, 26), % ERL_DRV_FLOAT
    ?line true = AFloat < 0.001,
    ?line true = AFloat > -0.001,

    %% Failure cases.
    ?line [] = term(P, 127),
    ?line receive
	      Any ->
		  ?line io:format("Unexpected: ~p\n", [Any]),
		  ?line ?t:fail()
	  after 0 ->
		  ok
	  end,

    ?line ok = chk_temp_alloc(),

    %% In a private heap system, verify that there are no binaries
    %% left for the process.
    ?line erlang:garbage_collect(),		%Get rid of binaries.
    case erlang:system_info(heap_type) of
	private ->
	    ?line {binary,[]} = process_info(self(), binary);
	_ -> ok
    end,

    ?line stop_driver(P, Drv),
    ok.

term(P, Op) ->
    erlang:port_command(P, [Op]),
    receive_any().

receive_any() ->
    receive
	Any -> Any
    end.

chk_temp_alloc() ->
    case erlang:system_info({allocator,temp_alloc}) of
	false ->
	    %% Temp alloc is not enabled
	    ?line ok;
	TIL ->
	    %% Verify that we havn't got anything allocated by temp_alloc
	    lists:foreach(
	      fun ({instance, _, TI}) ->
		      ?line {value, {mbcs, MBCInfo}}
			  = lists:keysearch(mbcs, 1, TI),
		      ?line {value, {blocks, 0, _, _}}
			  = lists:keysearch(blocks, 1, MBCInfo),
		      ?line {value, {sbcs, SBCInfo}}
			  = lists:keysearch(sbcs, 1, TI),
		      ?line {value, {blocks, 0, _, _}}
			  = lists:keysearch(blocks, 1, SBCInfo)
	      end,
	      TIL),
	    ?line ok
    end.
	    

%% Start/stop drivers.
start_driver(Config, Name) ->
    Path = ?config(data_dir, Config),
    erl_ddll:start(),
    ok = load_driver(Path, Name),
    open_port({spawn, Name}, []).

load_driver(Dir, Driver) ->
    case erl_ddll:load_driver(Dir, Driver) of
	ok -> ok;
	{error, Error} = Res ->
	    io:format("~s\n", [erl_ddll:format_error(Error)]),
	    Res
    end.

stop_driver(Port, Name) ->
    ?line true = erlang:port_close(Port),
    receive
	{Port,Message} ->
	    ?t:fail({strange_message_from_port,Message})
    after 0 ->
	    ok
    end,

    %% Unload the driver.
    ok = erl_ddll:unload_driver(Name),
    ?line ok = erl_ddll:stop().

get_external_terms(DataDir) ->    
    {ok, Bin} = file:read_file([DataDir, "ext_terms.bin"]),
    binary_to_term(Bin).

expected_ext2term_drv(DataDir) ->
    make_expected_ext2term_drv(get_external_terms(DataDir)).

make_expected_ext2term_drv([]) ->
    [];
make_expected_ext2term_drv([T|Ts]) ->
    [{T, T} | make_expected_ext2term_drv(Ts)].

%%
%% Generation of send_term_SUITE_data/ext_terms.h and
%% send_term_SUITE_data/ext_terms.bin
%%
%% These files should normally not need to be regenerated,
%% but we may want that if we introduce new types or make
%% backward incompatible changes to the external format.
%%

generate_external_terms_files(BaseDir) ->
    {ok,Node} = slave:start(hostname(), a_node),
    RPid = rpc:call(Node, erlang, self, []),
    true = is_pid(RPid),
    RRef = rpc:call(Node, erlang, make_ref, []),
    true = is_reference(RRef),
    RPort = hd(rpc:call(Node, erlang, ports, [])),
    true = is_port(RPort),
    slave:stop(Node),
    Terms =
	[{4711, -4711, [an_atom, "a list"]},
	 [1000000000000000000000,-1111111111111111, "blupp!", blipp],
	 {RPid, {RRef, RPort}, self(), hd(erlang:ports()), make_ref()},
	 {{}, [], [], fun () -> ok end, <<"hej hopp trallalaaaaaaaaaaaaaaa">>},
	 [44444444444444444444444,-44444444444, "b!", blippppppp],
	 {4711, RPid, {RRef, RPort}, -4711, [an_atom, "a list"]},
	 {RPid, {RRef, RPort}, hd(processes()), hd(erlang:ports())},
	 {4711, -4711, [an_atom, "a list"]},
	 {4711, -4711, [atom, "list"]},
	 {RPid, {RRef, RPort}, hd(processes()), hd(erlang:ports())},
	 {4444444444444444444,-44444, {{{{{{{{{{{{}}}}}}}}}}}}, make_ref()},
	 {444444444444444444444,-44444, [[[[[[[[[[[1]]]]]]]]]]], make_ref()},
	 {444444444444444444,-44444, {{{{{{{{{{{{2}}}}}}}}}}}}, make_ref()},
	 {4444444444444444444444,-44444, {{{{{{{{{{{{3}}}}}}}}}}}}, make_ref()},
	 {44444444444444444444,-44444, {{{{{{{{{{{{4}}}}}}}}}}}}, make_ref()},
	 {4444444444444444,-44444, [[[[[[[[[[[5]]]]]]]]]]], make_ref()},
	 {444444444444444444444,-44444, {{{{{{{{{{{{6}}}}}}}}}}}}, make_ref()},
	 {444444444444444,-44444, {{{{{{{{{{{{7}}}}}}}}}}}}, make_ref()},
	 {4444444444444444444,-44444, {{{{{{{{{{{{8}}}}}}}}}}}}, make_ref()},
         #{},
	 #{1 => 11, 2 => 22, 3 => 33},
	 maps:from_list([{K,K*11} || K <- lists:seq(1,100)])],
    ok = file:write_file(filename:join([BaseDir,
					"send_term_SUITE_data",
					"ext_terms.bin"]),
			 term_to_binary(Terms, [compressed])),
    {ok, IoDev} = file:open(filename:join([BaseDir,
					   "send_term_SUITE_data",
					   "ext_terms.h"]),
			    [write]),
    write_ext_terms_h(IoDev, Terms),
    file:close(IoDev).

write_ext_terms_h(IoDev, Terms) ->
    write_license(IoDev),
    io:format(IoDev, "#ifndef EXT_TERMS_H__~n",[]),
    io:format(IoDev, "#define EXT_TERMS_H__~n",[]),
    {ExtTerms, MaxSize} = make_ext_terms(Terms),
    io:format(IoDev,
	      "static struct {~n"
	      "  unsigned char ext[~p];~n"
	      "  int ext_size;~n"
	      "  unsigned char cext[~p];~n"
	      "  int cext_size;~n"
	      "} ext_terms[] = {~n",[MaxSize, MaxSize]),
    E = write_ext_terms_h(IoDev, ExtTerms, 0),
    io:format(IoDev, "};~n",[]),
    io:format(IoDev, "#define NO_OF_EXT_TERMS ~p~n", [E]),
    io:format(IoDev, "#endif~n",[]).

make_ext_terms([]) ->
    {[], 0};
make_ext_terms([T|Ts]) ->
    E = term_to_binary(T),
    ESz = size(E),
    CE = term_to_binary(T, [compressed]),
    CESz = size(CE),
    true = CESz =< ESz, % Assertion
    {ExtTerms, MaxSize} = make_ext_terms(Ts),
    NewMaxSize = case MaxSize < ESz of
		     true -> ESz;
		     false -> MaxSize
		 end,
    {[{E, ESz, CE, CESz} | ExtTerms], NewMaxSize}.

write_ext_terms_h(IoDev, [], N) ->
    io:format(IoDev, "~n",[]),
    N;
write_ext_terms_h(IoDev, [ET|ETs], 0) ->
    write_ext_term(IoDev, ET),
    write_ext_terms_h(IoDev, ETs, 1);
write_ext_terms_h(IoDev, [ET|ETs], N) ->
    io:format(IoDev, ",~n",[]),
    write_ext_term(IoDev, ET),
    write_ext_terms_h(IoDev, ETs, N+1).

write_ext_term(IoDev, {E, ESz, CE, CESz}) ->
    ESz = write_bytes(IoDev, "  {{", binary_to_list(E), 0),
    io:format(IoDev,
	      ",~n"
	      "   ~p,~n",
	      [ESz]),
    CESz = write_bytes(IoDev, "   {", binary_to_list(CE), 0),
    io:format(IoDev,
	      ",~n"
	      "   ~p}",
	      [CESz]).

write_bytes(IoDev, _, [], N) ->
    io:format(IoDev, "}",[]),
    N;
write_bytes(IoDev, Prefix, [B|Bs], N) ->
    io:format(IoDev, "~s~w", [Prefix, B]),
    write_bytes(IoDev, ",", Bs, N+1).

write_license(IoDev) ->
    S =	"/* ``The contents of this file are subject to the Erlang Public License,~n"
	" * Version 1.1, (the \"License\"); you may not use this file except in~n"
	" * compliance with the License. You should have received a copy of the~n"
	" * Erlang Public License along with this software. If not, it can be~n"
	" * retrieved via the world wide web at http://www.erlang.org/.~n"
	" * ~n"
	" * Software distributed under the License is distributed on an \"AS IS\"~n"
	" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See~n"
	" * the License for the specific language governing rights and limitations~n"
	" * under the License.~n"
	" * ~n"
	" * The Initial Developer of the Original Code is Ericsson AB.~n"
	" * Portions created by Ericsson are Copyright 2007, Ericsson AB.~n"
	" * All Rights Reserved.''~n"
	" * ~n"
	" *     $Id$~n"
	" */~n"
	"~n"
	"/*~n"
	" * Do not modify this file. This file and ext_terms.bin were~n"
	" * automatically generated by send_term_SUITE:generate_external_terms_files/1~n"
	" * and needs to be consistent with each other.~n"
	" */~n",
    io:format(IoDev, S, []).


hostname() ->    
    hostname(atom_to_list(node())).

hostname([$@ | Hostname]) ->
    list_to_atom(Hostname);
hostname([_C | Cs]) ->
    hostname(Cs).