1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2005-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 21-module(send_term_SUITE). 22 23-export([all/0, suite/0, basic/1]). 24 25-export([generate_external_terms_files/1]). 26 27-include_lib("common_test/include/ct.hrl"). 28 29suite() -> 30 [{ct_hooks,[ts_install_cth]}, 31 {timetrap, {minutes, 3}}]. 32 33all() -> 34 [basic]. 35 36basic(Config) when is_list(Config) -> 37 Drv = "send_term_drv", 38 P = start_driver(Config, Drv), 39 40 [] = term(P, 0), 41 Self = self(), 42 {blurf,42,[],[-42,{}|"abc"++P],"kalle",3.1416,Self,#{}} = term(P, 1), 43 44 Map41 = maps:from_list([{blurf, 42}, 45 {[], [-42,{}|"abc"++P]}, 46 {"kalle", 3.1416}, 47 {Self, #{}}]), 48 Map41 = term(P, 41), 49 50 Map42 = maps:from_list([{42, []}, 51 {[-42,{}|"abc"++P], "kalle"}, 52 {3.1416, Self}, 53 {#{}, blurf}]), 54 Map42 = term(P, 42), 55 Deep = lists:seq(0, 199), 56 Deep = term(P, 2), 57 {B1,B2} = term(P, 3), 58 B1 = list_to_binary(lists:seq(0, 255)), 59 B2 = list_to_binary(lists:seq(23, 255-17)), 60 61 %% Pid sending. We need another process. 62 Child = spawn_link(fun() -> 63 erlang:port_command(P, [4]) 64 end), 65 {Self,Child} = receive_any(), 66 67 %% ERL_DRV_EXT2TERM 68 ExpectExt2Term = expected_ext2term_drv(proplists:get_value(data_dir, Config)), 69 ExpectExt2Term = term(P, 5), 70 71 %% ERL_DRV_INT, ERL_DRV_UINT 72 case erlang:system_info({wordsize, external}) of 73 4 -> 74 {-1, 4294967295} = term(P, 6); 75 8 -> 76 {-1, 18446744073709551615} = term(P, 6) 77 end, 78 79 %% ERL_DRV_BUF2BINARY 80 ExpectedBinTup = {<<>>, 81 <<>>, 82 list_to_binary(lists:duplicate(17,17)), 83 list_to_binary(lists:duplicate(1024,17))}, 84 ExpectedBinTup = term(P, 7), 85 86 %% single terms 87 Singles = [{[], 8}, % ERL_DRV_NIL 88 {'', 9}, % ERL_DRV_ATOM 89 {an_atom, 10}, % ERL_DRV_ATOM 90 {-4711, 11}, % ERL_DRV_INT 91 {4711, 12}, % ERL_DRV_UINT 92 {P, 13}, % ERL_DRV_PORT 93 {<<>>, 14}, % ERL_DRV_BINARY 94 {<<"hejsan">>, 15}, % ERL_DRV_BINARY 95 {<<>>, 16}, % ERL_DRV_BUF2BINARY 96 {<<>>, 17}, % ERL_DRV_BUF2BINARY 97 {<<"hoppsan">>, 18}, % ERL_DRV_BUF2BINARY 98 {"", 19}, % ERL_DRV_STRING 99 {"", 20}, % ERL_DRV_STRING 100 {"hippsan", 21}, % ERL_DRV_STRING 101 {{}, 22}, % ERL_DRV_TUPLE 102 {[], 23}, % ERL_DRV_LIST 103 {Self, 24}, % ERL_DRV_PID 104 {[], 25}, % ERL_DRV_STRING_CONS 105 {[], 27}, % ERL_DRV_EXT2TERM 106 {18446744073709551615, 28}, % ERL_DRV_UINT64 107 {20233590931456, 29}, % ERL_DRV_UINT64 108 {4711, 30}, % ERL_DRV_UINT64 109 {0, 31}, % ERL_DRV_UINT64 110 {9223372036854775807, 32}, % ERL_DRV_INT64 111 {20233590931456, 33}, % ERL_DRV_INT64 112 {4711, 34}, % ERL_DRV_INT64 113 {0, 35}, % ERL_DRV_INT64 114 {-1, 36}, % ERL_DRV_INT64 115 {-4711, 37}, % ERL_DRV_INT64 116 {-20233590931456, 38}, % ERL_DRV_INT64 117 {-9223372036854775808, 39}, 118 {#{}, 40}], % ERL_DRV_MAP 119 {Terms, Ops} = lists:unzip(Singles), 120 Terms = term(P,Ops), 121 122 AFloat = term(P, 26), % ERL_DRV_FLOAT 123 true = AFloat < 0.001, 124 true = AFloat > -0.001, 125 126 %% Failure cases. 127 [] = term(P, 127), 128 receive 129 Any -> 130 ct:fail("Unexpected: ~p\n", [Any]) 131 after 0 -> 132 ok 133 end, 134 135 ok = chk_temp_alloc(), 136 137 %% In a private heap system, verify that there are no binaries 138 %% left for the process. 139 erlang:garbage_collect(), %Get rid of binaries. 140 case erlang:system_info(heap_type) of 141 private -> 142 {binary,[]} = process_info(self(), binary); 143 _ -> ok 144 end, 145 146 stop_driver(P, Drv), 147 ok. 148 149term(P, Op) -> 150 erlang:port_command(P, [Op]), 151 receive_any(). 152 153receive_any() -> 154 receive 155 Any -> Any 156 end. 157 158chk_temp_alloc() -> 159 %% Verify that we haven't got any outstanding temp_alloc allocations. 160 case erts_debug:alloc_blocks_size(temp_alloc) of 161 undefined -> ok; 162 0 -> ok 163 end. 164 165%% Start/stop drivers. 166start_driver(Config, Name) -> 167 Path = proplists:get_value(data_dir, Config), 168 erl_ddll:start(), 169 ok = load_driver(Path, Name), 170 open_port({spawn, Name}, []). 171 172load_driver(Dir, Driver) -> 173 case erl_ddll:load_driver(Dir, Driver) of 174 ok -> ok; 175 {error, Error} = Res -> 176 io:format("~s\n", [erl_ddll:format_error(Error)]), 177 Res 178 end. 179 180stop_driver(Port, Name) -> 181 true = erlang:port_close(Port), 182 receive 183 {Port,Message} -> 184 ct:fail({strange_message_from_port,Message}) 185 after 0 -> 186 ok 187 end, 188 189 %% Unload the driver. 190 ok = erl_ddll:unload_driver(Name), 191 ok = erl_ddll:stop(). 192 193get_external_terms(DataDir) -> 194 {ok, Bin} = file:read_file([DataDir, "ext_terms.bin"]), 195 binary_to_term(Bin). 196 197expected_ext2term_drv(DataDir) -> 198 make_expected_ext2term_drv(get_external_terms(DataDir)). 199 200make_expected_ext2term_drv([]) -> 201 []; 202make_expected_ext2term_drv([T|Ts]) -> 203 [{T, T} | make_expected_ext2term_drv(Ts)]. 204 205%% 206%% Generation of send_term_SUITE_data/ext_terms.h and 207%% send_term_SUITE_data/ext_terms.bin 208%% 209%% These files should normally not need to be regenerated, 210%% but we may want that if we introduce new types or make 211%% backward incompatible changes to the external format. 212%% 213 214generate_external_terms_files(BaseDir) -> 215 {ok,Node} = slave:start(hostname(), a_node), 216 RPid = rpc:call(Node, erlang, self, []), 217 true = is_pid(RPid), 218 RRef = rpc:call(Node, erlang, make_ref, []), 219 true = is_reference(RRef), 220 RPort = hd(rpc:call(Node, erlang, ports, [])), 221 true = is_port(RPort), 222 slave:stop(Node), 223 Terms = [{4711, -4711, [an_atom, "a list"]}, 224 [1000000000000000000000,-1111111111111111, "blupp!", blipp], 225 {RPid, {RRef, RPort}, self(), hd(erlang:ports()), make_ref()}, 226 {{}, [], [], fun () -> ok end, <<"hej hopp trallalaaaaaaaaaaaaaaa">>}, 227 [44444444444444444444444,-44444444444, "b!", blippppppp], 228 {4711, RPid, {RRef, RPort}, -4711, [an_atom, "a list"]}, 229 {RPid, {RRef, RPort}, hd(processes()), hd(erlang:ports())}, 230 {4711, -4711, [an_atom, "a list"]}, 231 {4711, -4711, [atom, "list"]}, 232 {RPid, {RRef, RPort}, hd(processes()), hd(erlang:ports())}, 233 {4444444444444444444,-44444, {{{{{{{{{{{{}}}}}}}}}}}}, make_ref()}, 234 {444444444444444444444,-44444, [[[[[[[[[[[1]]]]]]]]]]], make_ref()}, 235 {444444444444444444,-44444, {{{{{{{{{{{{2}}}}}}}}}}}}, make_ref()}, 236 {4444444444444444444444,-44444, {{{{{{{{{{{{3}}}}}}}}}}}}, make_ref()}, 237 {44444444444444444444,-44444, {{{{{{{{{{{{4}}}}}}}}}}}}, make_ref()}, 238 {4444444444444444,-44444, [[[[[[[[[[[5]]]]]]]]]]], make_ref()}, 239 {444444444444444444444,-44444, {{{{{{{{{{{{6}}}}}}}}}}}}, make_ref()}, 240 {444444444444444,-44444, {{{{{{{{{{{{7}}}}}}}}}}}}, make_ref()}, 241 {4444444444444444444,-44444, {{{{{{{{{{{{8}}}}}}}}}}}}, make_ref()}, 242 #{}, 243 #{1 => 11, 2 => 22, 3 => 33}, 244 maps:from_list([{K,K*11} || K <- lists:seq(1,100)])], 245 ok = file:write_file(filename:join([BaseDir, 246 "send_term_SUITE_data", 247 "ext_terms.bin"]), 248 term_to_binary(Terms, [compressed])), 249 {ok, IoDev} = file:open(filename:join([BaseDir, 250 "send_term_SUITE_data", 251 "ext_terms.h"]), 252 [write]), 253 write_ext_terms_h(IoDev, Terms), 254 file:close(IoDev). 255 256write_ext_terms_h(IoDev, Terms) -> 257 write_license(IoDev), 258 io:format(IoDev, "#ifndef EXT_TERMS_H__~n",[]), 259 io:format(IoDev, "#define EXT_TERMS_H__~n",[]), 260 {ExtTerms, MaxSize} = make_ext_terms(Terms), 261 io:format(IoDev, 262 "static struct {~n" 263 " unsigned char ext[~p];~n" 264 " int ext_size;~n" 265 " unsigned char cext[~p];~n" 266 " int cext_size;~n" 267 "} ext_terms[] = {~n",[MaxSize, MaxSize]), 268 E = write_ext_terms_h(IoDev, ExtTerms, 0), 269 io:format(IoDev, "};~n",[]), 270 io:format(IoDev, "#define NO_OF_EXT_TERMS ~p~n", [E]), 271 io:format(IoDev, "#endif~n",[]). 272 273make_ext_terms([]) -> 274 {[], 0}; 275make_ext_terms([T|Ts]) -> 276 E = term_to_binary(T), 277 ESz = size(E), 278 CE = term_to_binary(T, [compressed]), 279 CESz = size(CE), 280 true = CESz =< ESz, % Assertion 281 {ExtTerms, MaxSize} = make_ext_terms(Ts), 282 NewMaxSize = case MaxSize < ESz of 283 true -> ESz; 284 false -> MaxSize 285 end, 286 {[{E, ESz, CE, CESz} | ExtTerms], NewMaxSize}. 287 288write_ext_terms_h(IoDev, [], N) -> 289 io:format(IoDev, "~n",[]), 290 N; 291write_ext_terms_h(IoDev, [ET|ETs], 0) -> 292 write_ext_term(IoDev, ET), 293 write_ext_terms_h(IoDev, ETs, 1); 294write_ext_terms_h(IoDev, [ET|ETs], N) -> 295 io:format(IoDev, ",~n",[]), 296 write_ext_term(IoDev, ET), 297 write_ext_terms_h(IoDev, ETs, N+1). 298 299write_ext_term(IoDev, {E, ESz, CE, CESz}) -> 300 ESz = write_bytes(IoDev, " {{", binary_to_list(E), 0), 301 io:format(IoDev, 302 ",~n" 303 " ~p,~n", 304 [ESz]), 305 CESz = write_bytes(IoDev, " {", binary_to_list(CE), 0), 306 io:format(IoDev, 307 ",~n" 308 " ~p}", 309 [CESz]). 310 311write_bytes(IoDev, _, [], N) -> 312 io:format(IoDev, "}",[]), 313 N; 314write_bytes(IoDev, Prefix, [B|Bs], N) -> 315 io:format(IoDev, "~s~w", [Prefix, B]), 316 write_bytes(IoDev, ",", Bs, N+1). 317 318write_license(IoDev) -> 319 S = "/* ``Licensed under the Apache License, Version 2.0 (the \"License\");~n" 320 " * you may not use this file except in compliance with the License.~n" 321 " * You may obtain a copy of the License at~n" 322 " * ~n" 323 " * http://www.apache.org/licenses/LICENSE-2.0~n" 324 " * ~n" 325 " * Unless required by applicable law or agreed to in writing, software~n" 326 " * distributed under the License is distributed on an \"AS IS\" BASIS,~n" 327 " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.~n" 328 " * See the License for the specific language governing permissions and~n" 329 " * limitations under the License.~n" 330 " * ~n" 331 " * The Initial Developer of the Original Code is Ericsson AB.~n" 332 " * Portions created by Ericsson are Copyright 2007, Ericsson AB.~n" 333 " * All Rights Reserved.''~n" 334 " * ~n" 335 " * $Id$~n" 336 " */~n" 337 "~n" 338 "/*~n" 339 " * Do not modify this file. This file and ext_terms.bin were~n" 340 " * automatically generated by send_term_SUITE:generate_external_terms_files/1~n" 341 " * and needs to be consistent with each other.~n" 342 " */~n", 343 io:format(IoDev, S, []). 344 345 346hostname() -> 347 hostname(atom_to_list(node())). 348 349hostname([$@ | Hostname]) -> 350 list_to_atom(Hostname); 351hostname([_C | Cs]) -> 352 hostname(Cs). 353