From 76e841903e439067d55cdbebb814e7ce86034826 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 12 Aug 2010 16:57:02 +0200 Subject: Add test suite for jinterface --- lib/jinterface/test/nc_SUITE.erl | 736 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 736 insertions(+) create mode 100644 lib/jinterface/test/nc_SUITE.erl (limited to 'lib/jinterface/test/nc_SUITE.erl') diff --git a/lib/jinterface/test/nc_SUITE.erl b/lib/jinterface/test/nc_SUITE.erl new file mode 100644 index 0000000000..82dd3c2535 --- /dev/null +++ b/lib/jinterface/test/nc_SUITE.erl @@ -0,0 +1,736 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2004-2010. 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(nc_SUITE). + + +-include("test_server.hrl"). +-include("test_server_line.hrl"). + + +-export([all/1, + init_per_suite/1, + end_per_suite/1, + init_per_testcase/2, + end_per_testcase/2]). + +-export([pid_roundtrip/1, + port_roundtrip/1, + ref_roundtrip/1, + new_float/1, + old_stuff/1, + binary_roundtrip/1, + decompress_roundtrip/1, + compress_roundtrip/1, + integer_roundtrip/1, + fun_roundtrip/1, + lists_roundtrip/1, + lists_roundtrip_2/1, + lists_iterator/1, + unicode/1, + unicode_list_to_string/1, + unicode_string_to_list/1, + connect/1]). + + +%% Top of cases + +all(doc) -> []; +all(suite) -> [pid_roundtrip, + port_roundtrip, + ref_roundtrip, + new_float, + old_stuff, + binary_roundtrip, + decompress_roundtrip, + compress_roundtrip, + integer_roundtrip, + fun_roundtrip, + lists_roundtrip, + lists_roundtrip_2, + lists_iterator, + unicode, + unicode_list_to_string, + unicode_string_to_list, + connect]. + + + + +init_per_suite(Config) when is_list(Config) -> + jitu:init_all(Config). + +end_per_suite(Config) -> + jitu:finish_all(Config). + + + +%% Add/remove watchdog before/after each test case. +%% +init_per_testcase(Case, Config) -> + T = case atom_to_list(Case) of + "unicode"++_ -> 240; + _ -> 20 + end, + WatchDog = test_server:timetrap(test_server:seconds(T)), + [{watchdog, WatchDog}| Config]. + +end_per_testcase(_Case, Config) -> + WatchDog = ?config(watchdog, Config), + test_server:timetrap_cancel(WatchDog). + + +%% +%% Test cases +%% + +pid_roundtrip(doc) -> []; +pid_roundtrip(suite) -> []; +pid_roundtrip(Config) when is_list(Config)-> + ThisNode = {node(), erlang:system_info(creation)}, + RemNode = {gurka@sallad, 2}, + do_echo([self(), + mk_pid(ThisNode, 4711, 4711), + mk_pid(ThisNode, 32767, 8191), + mk_pid(RemNode, 4711, 4711), + mk_pid(RemNode, 32767, 8191)], + Config). + +fun_roundtrip(doc) -> []; +fun_roundtrip(suite) -> []; +fun_roundtrip(Config) when is_list(Config)-> + do_echo([fun(A, B) -> A + B end, + fun(A) -> lists:reverse(A) end, + fun() -> ok end, + fun fun_roundtrip/1], + Config). + +port_roundtrip(doc) -> []; +port_roundtrip(suite) -> []; +port_roundtrip(Config) when is_list(Config)-> + ThisNode = {node(), erlang:system_info(creation)}, + RemNode = {gurka@sallad, 2}, + do_echo([hd(erlang:ports()), + mk_port(ThisNode, 4711), + mk_port(ThisNode, 268435455), + mk_port(RemNode, 4711), + mk_port(RemNode, 268435455)], + Config). + +ref_roundtrip(doc) -> []; +ref_roundtrip(suite) -> []; +ref_roundtrip(Config) when is_list(Config)-> + ThisNode = {node(), erlang:system_info(creation)}, + RemNode = {gurka@sallad, 2}, + do_echo([make_ref(), + mk_ref(ThisNode, [4711]), + mk_ref(ThisNode, [4711, 4711, 4711]), + mk_ref(ThisNode, [262143, 4294967295, 4294967295]), + mk_ref(RemNode, [4711]), + mk_ref(RemNode, [4711, 4711, 4711]), + mk_ref(RemNode, [262143, 4294967295, 4294967295])], + Config). + +new_float(doc) -> []; +new_float(suite) -> []; +new_float(Config) when is_list(Config)-> + Two16 = float(1 bsl 16), + X = math:sqrt(2), + Floats = lists:reverse(seq(1/X, 63, fun(Y) -> Y / Two16 end), + [0.0|seq(X, 63, fun(Y) -> Y * Two16 end)]), + io:format("~w", [Floats]), + do_echo(Floats, Config). + +old_stuff(doc) -> []; +old_stuff(suite) -> []; +old_stuff(Config) when is_list(Config)-> + Terms = [0.0,math:sqrt(2)], + OutTrans = + fun (D) -> + {self(),term_to_binary(D, [{minor_version,0}]),binary} + end, + InTrans = + fun (Echoer, D, {Echoer,D,binary}) -> + ok + end, + do_echo(Terms, Config, OutTrans, InTrans). + +binary_roundtrip(doc) -> []; +binary_roundtrip(suite) -> []; +binary_roundtrip(Config) when is_list(Config) -> + do_echo([<<17>>, + <<1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17>>, + <<3:2>>, + <<3,7:3>>, + <<>>], + Config). + +decompress_roundtrip(doc) -> []; +decompress_roundtrip(suite) -> []; +decompress_roundtrip(Config) when is_list(Config) -> + Terms = + [0.0, + math:sqrt(2), + <<1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,31:5>>, + make_ref()], + OutTrans = + fun (D) -> + {self(),term_to_binary(D, [compressed]),binary} + end, + InTrans = + fun (Echoer, D, {Echoer,D,binary}) -> + ok + end, + do_echo(Terms, Config, OutTrans, InTrans). + +compress_roundtrip(doc) -> []; +compress_roundtrip(suite) -> []; +compress_roundtrip(Config) when is_list(Config) -> + Terms = + [0.0, + math:sqrt(2), + <<1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,31:5>>, + make_ref()], + OutTrans = + fun (D) -> + {self(),D,compress} + end, + InTrans = + fun (Echoer, D, {Echoer,B,compress}) -> + D = binary_to_term(B) + end, + do_echo(Terms, Config, OutTrans, InTrans). + + + +integer_roundtrip(doc) -> []; +integer_roundtrip(suite) -> []; +integer_roundtrip(Config) when is_list(Config) -> + Xs = [1 bsl X || X <- [26,27,28,29,30,31,32,33, + 62,63,64,65, + 126,127,128,129]], + Terms = [0,1,-1,-2] + ++lists:flatmap(fun (X) -> [X-2,X-1,X,-X+1,-X,-X-1] end, + Xs), + io:format("~w", [Terms]), + OutTrans = + fun (V) -> + {self(),V,bigint} + end, + InTrans = + fun (Echoer, V, {Echoer,{V,W,X,L,U},bigint}) -> + Bitlength = bitlength(V), + {w,W} = {w,signum(V) * Bitlength}, + Y = V band 16#FFFFffffFFFFffff, + {y,Y} = {y,X band 16#FFFFffffFFFFffff}, + {l,L} = {l,if V =:= X, Bitlength < 64 -> 1; + true -> 0 end}, + {u,U} = {u,if V =:= Y, V >= 0, Bitlength =< 64 -> 1; + true -> 0 end} + end, + do_echo(Terms, Config, OutTrans, InTrans). + +signum(V) when is_integer(V), V > 0 -> 1; +signum(V) when is_integer(V), V < 0 -> -1; +signum(0) -> 0. + +bitlength(0) -> + 0; +bitlength(-1) -> + 0; +bitlength(V) when is_integer(V) -> + 1 + bitlength(V bsr 1). + + + +lists_roundtrip(doc) -> []; +lists_roundtrip(suite) -> []; +lists_roundtrip(Config) when is_list(Config) -> + Ls = [lists:seq(1,10), + lists:seq(11,17)++last_tail, + [{default}], + [car|cdr], + [[]], + []], + do_echo(Ls, Config). + + + +lists_roundtrip_2(doc) -> []; +lists_roundtrip_2(suite) -> []; +lists_roundtrip_2(Config) when is_list(Config) -> + Ls = [{[a,b],tail}, + {[c,d,e],tail}, + {[],tail}, + {[f],tail}, + {[g,h|i],tail}, + {[j,k,l|m],tail}, + {[n|o],tail}, + {[z,1,2,3,4],tail3}, + {[z,5,6,7],tail3}, + {[z,8,9],tail3}, + {[z,10],tail3}, + {[],tail3}, + {[z,11,12,13,14|15],tail3}, + {[z,16,17,18|19],tail3}, + {[z,20,21|22],tail3}, + {[z,23|24],tail3}, + {[z|25],tail3}, + {"abc123",sub3atom}, + {"abc",sub3atom} + ], + Trans = + fun ([_|T], tail) -> + T; + (L, tail) when is_list(L) -> + null; + ([_,_,_|T], tail3) -> + T; + (L, tail3) when is_list(L) -> + null; + ([_,_,_|L], sub3atom) -> + list_to_atom(L) + end, + OutTrans = + fun ({L,Twist}) -> + {self(),L,Twist} + end, + InTrans = + fun (Echoer, {L,Twist}, {Echoer,X,Twist}) -> + Y = Trans(L, Twist), + io:format("## ~w ~w ~w ~w~n", [L,Twist,X,Y]), + X = Y + end, + do_echo(Ls, Config, OutTrans, InTrans). + + + + +lists_iterator(doc) -> []; +lists_iterator(suite) -> []; +lists_iterator(Config) when is_list(Config) -> + Ls = [["able ","was ","I ","ere ","I ","saw ","elba"]], + do_echo(Ls, Config, + fun (L) -> {self(),L,strcat} end, + fun (Echoer, L, {Echoer,X,strcat}) -> + io:format("## ~p ~p~n", [L,X]), + X = lists:flatten(L) + end). + + + +unicode(doc) -> []; +unicode(suite) -> []; +unicode(Config) when is_list(Config) -> + S1 = "plain ascii", + S2 = "iso-latin едц с", + S3 = "Codepoints... едц \x{1000}", + S4 = [0,1,31,32,63,64,127,128,255], + S5 = [0,1,127,128,255,256,16#d7ff, + 16#e000,16#fffd,16#10000,16#10ffff], + Ss = [S1,S2,S3,S4,S5], + do_echo(unicode_cp_gen([{S,valid} || S <- Ss] ++ cp_gen(71)), Config, + fun ({L,invalid}) -> {self(),L,utf8}; + ({L,Tag}) -> {self(),L,Tag}; + ({L}) -> {self(),L} + end, + fun (Echoer, {L,invalid}=Out, {Echoer,X,utf8}=In) -> + case L of + X -> ok; + _ -> + ?t:fail({mismatch,Out,In}) + end; + (Echoer, {L,Tag}=Out, {Echoer,X,Tag}=In) -> + case unicode:characters_to_binary(L, utf8) of + X -> ok; + _ -> + ?t:fail({mismatch,Out,In}) + end; + (Echoer, {L}=Out, {Echoer,X}=In) -> + case L of + X -> ok; + _ -> + ?t:fail({mismatch,Out,In}) + end + end, ["unicode"]). + +%% Lazy wrapper to lazy list +unicode_cp_gen([{S,valid}|Ss]) -> + [{S,utf8},{S}|unicode_cp_gen(Ss)]; +unicode_cp_gen([{S,invalid}=St|Ss]) -> + [St,{S}|unicode_cp_gen(Ss)]; +unicode_cp_gen([]) -> + []; +unicode_cp_gen(Cont) when is_function(Cont, 0) -> + fun () -> + unicode_cp_gen(Cont()) + end. + + + +unicode_list_to_string(doc) -> []; +unicode_list_to_string(suite) -> []; +unicode_list_to_string(Config) when is_list(Config) -> + do_echo(cp_gen(73), Config, + fun ({L,_}) -> {self(),L,to_string_neg_int_list} end, + fun (Echoer, {L,invalid}=Out, {Echoer,X,_}=In) -> + case L of + X -> ok; + _ -> + ?t:fail({mismatch,Out,In}) + end; + (Echoer, {L,valid}=Out, {Echoer,X,_}=In) -> + B = unicode:characters_to_binary(L, unicode, {utf16,big}), + case [-D || <> <= B] of + X -> ok; + _ -> + ?t:fail({mismatch,Out,In}) + end + end). + + + +unicode_string_to_list(doc) -> []; +unicode_string_to_list(suite) -> []; +unicode_string_to_list(Config) when is_list(Config) -> + do_echo(cp_gen(79), Config, + fun ({L,_}) -> {self(),L,to_neg_int_list} end, + fun (Echoer, {L,invalid}=Out, {Echoer,X,_}=In) -> + case L of + X -> ok; + _ -> + ?t:fail({mismatch,Out,In}) + end; + (Echoer, {L,valid}=Out, {Echoer,X,_}=In) -> + case [-C || C <- L] of + X -> ok; + _ -> + ?t:fail({mismatch,Out,In}) + end + end, ["unicode"]). + + +%% Lazy list +cp_gen(N) -> + cp_gen(N, -1, 16#110000). + +cp_gen(N, Start, End) -> + cp_gen(N, Start, End, cp_validity(Start), [], 0, [], 0). + +cp_gen(N, U, End, PrevValidity, Acc, Len, Ss, Ls) when Len >= N -> + cp_gen(N, U, End, PrevValidity, [], 0, [{Acc,PrevValidity}|Ss], Ls+1); +cp_gen(N, U, End, PrevValidity, Acc, Len, Ss, Ls) when Ls >= N -> + Ss ++ fun () -> + cp_gen(N, U, End, PrevValidity, Acc, Len, [], 0) + end; +cp_gen(_, U, End, _, Acc, _, Ss, _) when U > End -> + [{Acc,valid}|Ss]; +cp_gen(N, U, End, PrevValidity, Acc, Len, Ss, Ls) -> + Validity = cp_validity(U), + NextU = U+1, + {NextAcc,NextLen} = case Validity of + valid -> {[U|Acc],Len+1}; + invalid -> {Acc,Len} + end, + {NextSs,NextLs} = case Validity of + PrevValidity -> {Ss,Ls}; + valid -> {[{[U-1],PrevValidity}|Ss],Ls+1}; + invalid -> {[{[U],Validity}|Ss],Ls+1} + end, + cp_gen(N, NextU, End, Validity, NextAcc, NextLen, NextSs, NextLs). + +cp_validity(UnicodeCP) -> + try <> of + _ -> valid + catch + error:_ -> invalid + end. + + + +connect(doc) -> []; +connect(suite) -> []; +connect(Config) when is_list(Config) -> + WD = filename:dirname(code:which(?MODULE)), + {ok,Other} = ?t:start_node(make_name(), slave, [{args,"-pa "++WD}]), + Action = + fun (Pid) -> + JName = node(Pid), + Hidden = [JName], + Pid ! {self(),Other}, + receive + {Pid,Other,true} -> + ok; + Unexpected1 -> + ?t:fail({result,Unexpected1}) + end, + Hidden = erlang:nodes(hidden), + Hidden = rpc:call(Other, erlang, nodes, [hidden]), + true = + rpc:call(Other, erlang, disconnect_node, [JName]), + [] = + rpc:call(Other, erlang, nodes, [hidden]), + Hidden = erlang:nodes(hidden), + %% Again + receive after 2000 -> ok end, + %% We have no way of knowing when the Java node + %% detects the nodedown. + Pid ! {self(),Other}, + receive + {Pid,Other,true} -> + ok; + Unexpected2-> + ?t:fail({result,Unexpected2}) + end, + Hidden = rpc:call(Other, erlang, nodes, [hidden]) + end, + run_server(connection_server, Config, Action, []). + + + +seq(_, 0, _) -> + []; +seq(X, N, Fun) -> + [X|seq(Fun(X), N-1, Fun)]. + +do_echo(DataList, Config) -> + do_echo(DataList, Config, + fun (D) -> % OutTrans + {self(),D} + end, + fun (Echoer, D, {Echoer,D}) -> % InTrans + ok + end, []). + +do_echo(DataList, Config, OutTrans, InTrans) -> + do_echo(DataList, Config, OutTrans, InTrans, []). + +do_echo(DataList, Config, OutTrans, InTrans, ExtraArgs) + when is_list(DataList), is_list(Config) -> + run_server(echo_server, Config, + fun (Echoer) -> + echo_loop(DataList, Echoer, OutTrans, InTrans, []) + end, + ExtraArgs). + +echo_loop([D|Ds], Echoer, OutTrans, InTrans, TermAcc) -> + OutMsg = OutTrans(D), + Echoer ! OutMsg, + io:format("echo_server ~p: ~p ! ~P~n", [self(),Echoer,OutMsg,10]), + receive + Reply -> + io:format("echo_server ~p: receive ~P~n", + [self(),Reply,10]), + InTrans(Echoer, D, Reply) + end, + Term = case OutMsg of + {_, T, _} -> T; + {_, T} -> T + end, + echo_loop(Ds, Echoer, OutTrans, InTrans, [Term | TermAcc]); +echo_loop([], Echoer, _, _, TermAcc) -> + check_terms(Echoer, TermAcc); +%% Lazy list +echo_loop(Cont, Echoer, OutTrans, InTrans, TermAcc) + when is_function(Cont, 0) -> + check_terms(Echoer, TermAcc), + OutMsg = Echoer ! {self(),undefined,hash_clear}, + io:format("echo_server ~p: ~p ! ~P~n", [self(),Echoer,OutMsg,10]), + receive + {Echoer,hash_cleared,hash_clear}=Reply -> + io:format("echo_server ~p: receive ~P~n", + [self(),Reply,10]), + ok; + Other -> + io:format("echo_server_terms unexpected ~p: receive ~P~n", + [self(),Other,10]), + ?t:fail({unexpected, Other}) + end, + echo_loop(Cont(), Echoer, OutTrans, InTrans, []). + +check_terms(Echoer, [Term | Rest]) -> + OutMsg = {self(),Term,hash_lookup}, + Echoer ! OutMsg, + io:format("check_terms ~p: ~p ! ~P~n", [self(),Echoer,OutMsg,10]), + receive + {Echoer,true,hash_lookup} = ReplyMsg -> + io:format("check_terms ~p: receive ~P~n", + [self(),ReplyMsg,10]), + check_terms(Echoer, Rest); + Other -> + io:format("check_terms unexpected ~p: receive ~P~n", + [self(),Other,10]), + ?t:fail({unexpected, Other}) + end; +check_terms(_, []) -> + ok. + +run_server(Server, Config, Action, ExtraArgs) -> + Name = make_name(), + true = register(Name, self()), + JName = make_name(), + spawn_link(fun () -> + ok = jitu:java(?config(java, Config), + ?config(data_dir, Config), + atom_to_list(Server), + [JName, + erlang:get_cookie(), + node(), + Name]++ExtraArgs + ), + %,"-DOtpConnection.trace=3"), + Name ! {done, JName} + end), + receive + {Server, JName, Pid} -> + ?t:format("~w: ~p (~p)~n", + [Server, Pid, node(Pid)]), + ?t:format("nodes(hidden): ~p~n", + [nodes(hidden)]), + Action(Pid), + Pid ! bye, + receive + {done, JName} -> + ok + end; + Other -> + ?t:fail({unexpected,Other}) + end. + +%% +%% Utils... +%% + +make_name() -> + {A, B, C} = now(), + list_to_atom(atom_to_list(?MODULE) + ++ "-" ++ integer_to_list(A) + ++ "-" ++ integer_to_list(B) + ++ "-" ++ integer_to_list(C)). + + + +-define(VERSION_MAGIC, 131). + +-define(ATOM_EXT, 100). +-define(REFERENCE_EXT, 101). +-define(PORT_EXT, 102). +-define(PID_EXT, 103). +-define(NEW_REFERENCE_EXT, 114). + +uint32_be(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 32 -> + [(Uint bsr 24) band 16#ff, + (Uint bsr 16) band 16#ff, + (Uint bsr 8) band 16#ff, + Uint band 16#ff]; +uint32_be(Uint) -> + exit({badarg, uint32_be, [Uint]}). + + +uint16_be(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 16 -> + [(Uint bsr 8) band 16#ff, + Uint band 16#ff]; +uint16_be(Uint) -> + exit({badarg, uint16_be, [Uint]}). + +uint8(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 8 -> + Uint band 16#ff; +uint8(Uint) -> + exit({badarg, uint8, [Uint]}). + + + +mk_pid({NodeName, Creation}, Number, Serial) when is_atom(NodeName) -> + mk_pid({atom_to_list(NodeName), Creation}, Number, Serial); +mk_pid({NodeName, Creation}, Number, Serial) -> + case catch binary_to_term(list_to_binary([?VERSION_MAGIC, + ?PID_EXT, + ?ATOM_EXT, + uint16_be(length(NodeName)), + NodeName, + uint32_be(Number), + uint32_be(Serial), + uint8(Creation)])) of + Pid when is_pid(Pid) -> + Pid; + {'EXIT', {badarg, _}} -> + exit({badarg, mk_pid, [{NodeName, Creation}, Number, Serial]}); + Other -> + exit({unexpected_binary_to_term_result, Other}) + end. + +mk_port({NodeName, Creation}, Number) when is_atom(NodeName) -> + mk_port({atom_to_list(NodeName), Creation}, Number); +mk_port({NodeName, Creation}, Number) -> + case catch binary_to_term(list_to_binary([?VERSION_MAGIC, + ?PORT_EXT, + ?ATOM_EXT, + uint16_be(length(NodeName)), + NodeName, + uint32_be(Number), + uint8(Creation)])) of + Port when is_port(Port) -> + Port; + {'EXIT', {badarg, _}} -> + exit({badarg, mk_port, [{NodeName, Creation}, Number]}); + Other -> + exit({unexpected_binary_to_term_result, Other}) + end. + +mk_ref({NodeName, Creation}, Numbers) when is_atom(NodeName), + is_integer(Creation), + is_list(Numbers) -> + mk_ref({atom_to_list(NodeName), Creation}, Numbers); +mk_ref({NodeName, Creation}, [Number]) when is_list(NodeName), + is_integer(Creation), + is_integer(Number) -> + case catch binary_to_term(list_to_binary([?VERSION_MAGIC, + ?REFERENCE_EXT, + ?ATOM_EXT, + uint16_be(length(NodeName)), + NodeName, + uint32_be(Number), + uint8(Creation)])) of + Ref when is_reference(Ref) -> + Ref; + {'EXIT', {badarg, _}} -> + exit({badarg, mk_ref, [{NodeName, Creation}, [Number]]}); + Other -> + exit({unexpected_binary_to_term_result, Other}) + end; +mk_ref({NodeName, Creation}, Numbers) when is_list(NodeName), + is_integer(Creation), + is_list(Numbers) -> + case catch binary_to_term(list_to_binary([?VERSION_MAGIC, + ?NEW_REFERENCE_EXT, + uint16_be(length(Numbers)), + ?ATOM_EXT, + uint16_be(length(NodeName)), + NodeName, + uint8(Creation), + lists:map(fun (N) -> + uint32_be(N) + end, + Numbers)])) of + Ref when is_reference(Ref) -> + Ref; + {'EXIT', {badarg, _}} -> + exit({badarg, mk_ref, [{NodeName, Creation}, Numbers]}); + Other -> + exit({unexpected_binary_to_term_result, Other}) + end. -- cgit v1.2.3