diff options
109 files changed, 8577 insertions, 4228 deletions
diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index c5d1786cee..2b15200488 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -751,7 +751,7 @@ typedef struct { <item> <code type="none"> typedef struct { - unsigned size; + size_t size; unsigned char* data; } ErlNifBinary;</code> <p><c>ErlNifBinary</c> contains transient information about an diff --git a/erts/doc/src/socket.xml b/erts/doc/src/socket.xml index c54fe38bfd..2d16ff2074 100644 --- a/erts/doc/src/socket.xml +++ b/erts/doc/src/socket.xml @@ -505,6 +505,14 @@ </func> <func> + <name name="number_of" arity="0" since="OTP @OTP-16309@"/> + <fsummary>Get the number of active sockets.</fsummary> + <desc> + <p>Returns the number of active sockets.</p> + </desc> + </func> + + <func> <name name="open" arity="2" since="OTP 22.0"/> <name name="open" arity="3" since="OTP 22.0"/> <name name="open" arity="4" since="OTP 22.0"/> @@ -900,9 +908,9 @@ <name name="supports" arity="1" clause_i="1" since="OTP 22.0"/> <name name="supports" arity="1" clause_i="2" since="OTP 22.0"/> <name name="supports" arity="1" clause_i="3" since="OTP 22.0"/> - <name name="supports" arity="1" clause_i="4" since="OTP-22.0"/> - <name name="supports" arity="1" clause_i="5" since="@OTP-16153@"/> - <name name="supports" arity="1" clause_i="6" since="@OTP-16153@"/> + <name name="supports" arity="1" clause_i="4" since="OTP 22.0"/> + <name name="supports" arity="1" clause_i="5" since="OTP @OTP-16153@"/> + <name name="supports" arity="1" clause_i="6" since="OTP @OTP-16153@"/> <name name="supports" arity="1" clause_i="7" since="OTP 22.0"/> <name name="supports" arity="2" clause_i="1" since="OTP 22.0"/> <name name="supports" arity="2" clause_i="2" since="OTP 22.0"/> @@ -910,8 +918,8 @@ <name name="supports" arity="2" clause_i="4" since="OTP 22.0"/> <name name="supports" arity="2" clause_i="5" since="OTP 22.0"/> <name name="supports" arity="2" clause_i="6" since="OTP 22.0"/> - <name name="supports" arity="2" clause_i="7" since="@OTP-16153@"/> - <name name="supports" arity="2" clause_i="8" since="@OTP-16153@"/> + <name name="supports" arity="2" clause_i="7" since="OTP @OTP-16153@"/> + <name name="supports" arity="2" clause_i="8" since="OTP @OTP-16153@"/> <name name="supports" arity="2" clause_i="9" since="OTP 22.0"/> <name name="supports" arity="3" clause_i="1" since="OTP 22.0"/> <name name="supports" arity="3" clause_i="2" since="OTP 22.0"/> @@ -928,6 +936,51 @@ </desc> </func> + <func> + <name name="which_sockets" arity="0" since="OTP @OTP-16309@"/> + <name name="which_sockets" arity="1" since="OTP @OTP-16309@"/> + <fsummary>Get the current active sockets.</fsummary> + <desc> + <p>Returns a list of all sockets, according to the + filter rule.</p> + <p>There are several pre-made filter rule(s) and one general: </p> + <taglist> + <tag><c><![CDATA[inet | inet6]]></c></tag> + <item> + <p>Selection based on the domain of the socket. + <br/>Only a subset is valid. </p> + </item> + + <tag><c><![CDATA[stream | dgram | seqpacket]]></c></tag> + <item> + <p>Selection based on the type of the socket. + <br/>Only a subset is valid. </p> + </item> + + <tag><c><![CDATA[sctp | tcp | udp]]></c></tag> + <item> + <p>Selection based on the protocol of the socket. + <br/>Only a subset is valid. </p> + </item> + + <tag><c><![CDATA[pid()]]></c></tag> + <item> + <p>Selection base on which sockets has this pid as + Controlling Process. </p> + </item> + + <tag><c><![CDATA[fun((socket_info()) -> boolean())]]></c></tag> + <item> + <p>The general filter rule. + <br/>A fun that takes the socket info and returns a + <c><![CDATA[boolean()]]></c> + (<c><![CDATA[true]]></c> if the socket sould be included and + <c><![CDATA[false]]></c> if should not). </p> + </item> + </taglist> + </desc> + </func> + </funcs> <section> <title>Examples</title> diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 7f50372532..a85d3f50a4 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -635,6 +635,7 @@ GENERATE += $(TTF_DIR)/driver_tab.c ifeq ($(USE_ESOCK), yes) ESOCK_PRELOAD_BEAM = \ + $(ERL_TOP)/erts/preloaded/ebin/socket_registry.beam \ $(ERL_TOP)/erts/preloaded/ebin/socket.beam \ $(ERL_TOP)/erts/preloaded/ebin/prim_net.beam else diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 84b07252d8..64b183c38e 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -936,6 +936,9 @@ typedef struct { // ERL_NIF_TERM buildDate; BOOLEAN_T dbg; + /* Registry stuff */ + ErlNifPid regPid; + BOOLEAN_T iow; // Where do we send this? Subscription? ErlNifMutex* cntMtx; Uint32 numSockets; @@ -2658,6 +2661,11 @@ static void esock_down_reader(ErlNifEnv* env, ERL_NIF_TERM sockRef, const ErlNifPid* pid); +static void esock_send_reg_add_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef); +static void esock_send_reg_del_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef); + static char* esock_send_wrap_msg(ErlNifEnv* env, ESockDescriptor* descP, ERL_NIF_TERM sockRef, @@ -2676,6 +2684,13 @@ static char* esock_send_msg(ErlNifEnv* env, ERL_NIF_TERM msg, ErlNifEnv* msgEnv); +static ERL_NIF_TERM mk_reg_add_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef); +static ERL_NIF_TERM mk_reg_del_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef); +static ERL_NIF_TERM mk_reg_msg(ErlNifEnv* env, + ERL_NIF_TERM tag, + ERL_NIF_TERM sockRef); static ERL_NIF_TERM mk_abort_msg(ErlNifEnv* env, ERL_NIF_TERM sockRef, ERL_NIF_TERM opRef, @@ -2994,6 +3009,7 @@ ERL_NIF_TERM esock_atom_socket_tag; // This has a "special" name ('$socket') /* *** Local atoms *** */ #define LOCAL_ATOMS \ LOCAL_ATOM_DECL(adaptation_layer); \ + LOCAL_ATOM_DECL(add); \ LOCAL_ATOM_DECL(addr_unreach); \ LOCAL_ATOM_DECL(address); \ LOCAL_ATOM_DECL(adm_prohibited); \ @@ -3009,6 +3025,7 @@ ERL_NIF_TERM esock_atom_socket_tag; // This has a "special" name ('$socket') LOCAL_ATOM_DECL(counter_wrap); \ LOCAL_ATOM_DECL(counters); \ LOCAL_ATOM_DECL(data_in); \ + LOCAL_ATOM_DECL(del); \ LOCAL_ATOM_DECL(dest_unreach); \ LOCAL_ATOM_DECL(do); \ LOCAL_ATOM_DECL(dont); \ @@ -3280,7 +3297,7 @@ ERL_NIF_TERM esock_global_info(ErlNifEnv* env) * domain: The domain of the socket * type: The type of the socket * protocol: The protocol of the socket - * (ctrl: Controlling process of the socket) + * ctrl: Controlling process of the socket) * (readable: Is the socket readable) * (writable: Is the socket writable) * (connected: Is the socket connected) @@ -3297,7 +3314,7 @@ ERL_NIF_TERM esock_socket_info(ErlNifEnv* env, ERL_NIF_TERM domain = esock_socket_info_domain(env, descP); ERL_NIF_TERM type = esock_socket_info_type(env, descP); ERL_NIF_TERM protocol = esock_socket_info_protocol(env, descP); - // ERL_NIF_TERM ctrlPid = MKPID(env, &descP->ctrlPid); + ERL_NIF_TERM ctrlPid = MKPID(env, &descP->ctrlPid); ERL_NIF_TERM readable = BOOL2ATOM(descP->isReadable); ERL_NIF_TERM writable = BOOL2ATOM(descP->isWritable); // ERL_NIF_TERM connected = BOOL2ATOM(descP->isConnected); @@ -3308,6 +3325,7 @@ ERL_NIF_TERM esock_socket_info(ErlNifEnv* env, ERL_NIF_TERM keys[] = {esock_atom_domain, esock_atom_type, esock_atom_protocol, + esock_atom_ctrl, atom_readable, atom_writable, atom_counters, @@ -3317,6 +3335,7 @@ ERL_NIF_TERM esock_socket_info(ErlNifEnv* env, ERL_NIF_TERM vals[] = {domain, type, protocol, + ctrlPid, readable, writable, counters, @@ -5334,6 +5353,9 @@ ERL_NIF_TERM esock_open(ErlNifEnv* env, inc_socket(domain, type, protocol); + /* And finally update the registry */ + esock_send_reg_add_msg(env, res); + return esock_make_ok2(env, res); } @@ -5622,8 +5644,8 @@ ERL_NIF_TERM nif_connect(ErlNifEnv* env, * safe side we do the best we can to avoid complications... */ - MLOCK(descP->readMtx); MLOCK(descP->writeMtx); + MLOCK(descP->readMtx); MLOCK(descP->cfgMtx); res = esock_connect(env, descP, sockRef); @@ -6552,6 +6574,9 @@ BOOLEAN_T esock_accept_accepted(ErlNifEnv* env, accDescP->isReadable = TRUE; accDescP->isWritable = TRUE; + /* And finally update the registry */ + esock_send_reg_add_msg(env, accRef); + *result = esock_make_ok2(env, accRef); return TRUE; @@ -19319,6 +19344,55 @@ size_t my_strnlen(const char *s, size_t maxlen) #endif + + +/* =========================================================================== + * + * Socket Registry message functions + * + * =========================================================================== + */ + +/* Send a (socket) add message to the socket registry process. + * We know that this process *is* alive since the VM would + * terminate otherwise, so there is no need to test if + * the sending fails. + */ +static +void esock_send_reg_add_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef) +{ + ERL_NIF_TERM msg = mk_reg_add_msg(env, sockRef); + + esock_send_msg(env, &data.regPid, msg, NULL); +} + + + +/* Send a (socket) del message to the socket registry process. + * We know that this process *is* alive since the VM would + * terminate otherwise, so there is no need to test if + * the sending fails. + */ +static +void esock_send_reg_del_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef) +{ + ERL_NIF_TERM msg = mk_reg_del_msg(env, sockRef); + + esock_send_msg(env, &data.regPid, msg, NULL); +} + + + + +/* =========================================================================== + * + * Socket user message functions + * + * =========================================================================== + */ + /* Send an counter wrap message to the controlling process: * A message in the form: * @@ -19414,6 +19488,55 @@ char* esock_send_msg(ErlNifEnv* env, +/* *** mk_reg_add_msg *** + * + * Construct a socket add message for the socket registry. + * + * {'$socket', add, Socket} + * + */ +static +ERL_NIF_TERM mk_reg_add_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef) +{ + return mk_reg_msg(env, atom_add, sockRef); +} + + +/* *** mk_reg_del_msg *** + * + * Construct a socket del message for the socket registry. + * + * {'$socket', del, Socket} + * + */ +static +ERL_NIF_TERM mk_reg_del_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef) +{ + return mk_reg_msg(env, atom_del, sockRef); +} + + +/* *** mk_reg_msg *** + * + * Construct a general message for the socket registry. + * Tag is (at this time) either the atom 'add' or the atom 'del'. + * + * {'$socket', Tag, Socket} + * + */ +static +ERL_NIF_TERM mk_reg_msg(ErlNifEnv* env, + ERL_NIF_TERM tag, + ERL_NIF_TERM sockRef) +{ + ERL_NIF_TERM socket = mk_socket(env, sockRef); + + return MKT3(env, esock_atom_socket_tag, tag, socket); +} + + /* *** mk_abort_msg *** * * Create the abort message, which has the following form: @@ -20357,6 +20480,9 @@ void esock_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) MUNLOCK(descP->readMtx); MUNLOCK(descP->writeMtx); + /* And finally update the registry */ + esock_send_reg_del_msg(env, sockRef); + SSDBG( descP, ("SOCKET", "esock_stop -> done (%d, %d)\r\n", descP->sock, fd) ); @@ -20434,8 +20560,9 @@ void inform_waiting_procs(ErlNifEnv* env, SSDBG( descP, ("SOCKET", - "inform_waiting_procs -> abort request %T (from %T)\r\n", - currentP->data.ref, currentP->data.pid) ); + "inform_waiting_procs -> " + "send abort message to waiting %s %T\r\n", + role, currentP->data.pid) ); if (esock_send_abort_msg(env, sockRef, @@ -20860,6 +20987,9 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) data.dbg = extract_debug(env, load_info); data.iow = extract_iow(env, load_info); + esock_extract_pid_from_map(env, load_info, + MKA(env, "registry"), &data.regPid); + /* +++ Global Counters +++ */ data.cntMtx = MCREATE("esock[gcnt]"); data.numSockets = 0; diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index b64e055c3e..6506ddc1e8 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -39,6 +39,13 @@ #include "config.h" #endif +#if defined(HAVE_SCTP_H) +#include <netinet/sctp.h> +#ifndef HAVE_SCTP +# define HAVE_SCTP +#endif +#endif + #ifdef HAVE_SOCKLEN_T # define SOCKLEN_T socklen_t #else @@ -99,7 +106,10 @@ static char* make_sockaddr_ll(ErlNifEnv* env, ERL_NIF_TERM addr, ERL_NIF_TERM* sa); #endif - +static BOOLEAN_T esock_extract_from_map(ErlNifEnv* env, + ERL_NIF_TERM map, + ERL_NIF_TERM key, + ERL_NIF_TERM* val); /* +++ esock_encode_iov +++ * @@ -1635,7 +1645,7 @@ BOOLEAN_T esock_decode_string(ErlNifEnv* env, /* *** esock_extract_bool_from_map *** * * Extract an boolean item from a map. - * + * This function returns the retreived or the provided default value. */ extern BOOLEAN_T esock_extract_bool_from_map(ErlNifEnv* env, @@ -1645,7 +1655,7 @@ BOOLEAN_T esock_extract_bool_from_map(ErlNifEnv* env, { ERL_NIF_TERM val; - if (!GET_MAP_VAL(env, map, key, &val)) + if (!esock_extract_from_map(env, map, key, &val)) return def; if (!IS_ATOM(env, val)) @@ -1659,6 +1669,59 @@ BOOLEAN_T esock_extract_bool_from_map(ErlNifEnv* env, +/* *** esock_extract_pid_from_map *** + * + * Extract a pid item from a map. + * Returns TRUE on success and FALSE on failure (and then + * the pid value will be set to 'undefined'). + * + */ +extern +BOOLEAN_T esock_extract_pid_from_map(ErlNifEnv* env, + ERL_NIF_TERM map, + ERL_NIF_TERM key, + ErlNifPid* pid) +{ + BOOLEAN_T res; + ERL_NIF_TERM val; + + if (!esock_extract_from_map(env, map, key, &val)) { + enif_set_pid_undefined(pid); + res = FALSE; + } else { + if (enif_get_local_pid(env, val, pid)) { + res = TRUE; + } else { + enif_set_pid_undefined(pid); + res = FALSE; + } + } + + return res; +} + + + +/* *** esock_extract_from_map *** + * + * Extract a value from a map. + * Returns true on success and false on failure. + * + */ +static +BOOLEAN_T esock_extract_from_map(ErlNifEnv* env, + ERL_NIF_TERM map, + ERL_NIF_TERM key, + ERL_NIF_TERM* val) +{ + if (!GET_MAP_VAL(env, map, key, val)) + return FALSE; + else + return TRUE; +} + + + /* *** esock_decode_bool *** * * Decode a boolean value. diff --git a/erts/emulator/nifs/common/socket_util.h b/erts/emulator/nifs/common/socket_util.h index 7600b5e521..398fb40a5a 100644 --- a/erts/emulator/nifs/common/socket_util.h +++ b/erts/emulator/nifs/common/socket_util.h @@ -200,6 +200,11 @@ BOOLEAN_T esock_extract_bool_from_map(ErlNifEnv* env, ERL_NIF_TERM key, BOOLEAN_T def); extern +BOOLEAN_T esock_extract_pid_from_map(ErlNifEnv* env, + ERL_NIF_TERM map, + ERL_NIF_TERM key, + ErlNifPid* pid); +extern BOOLEAN_T esock_decode_bool(ERL_NIF_TERM val); extern ERL_NIF_TERM esock_encode_bool(BOOLEAN_T val); diff --git a/erts/emulator/test/socket_SUITE.erl b/erts/emulator/test/socket_SUITE.erl index 44a338a68f..de1afae512 100644 --- a/erts/emulator/test/socket_SUITE.erl +++ b/erts/emulator/test/socket_SUITE.erl @@ -203,6 +203,9 @@ api_to_recvmsg_tcp4/1, api_to_recvmsg_tcp6/1, + %% Socket Registry + reg_s_single_open_and_close_and_count/1, + %% *** Socket Closure *** sc_cpe_socket_cleanup_tcp4/1, sc_cpe_socket_cleanup_tcp6/1, @@ -695,6 +698,7 @@ groups() -> {api_options_udp, [], api_options_udp_cases()}, %% {api_options_sctp, [], api_options_sctp_cases()}, {api_op_with_timeout, [], api_op_with_timeout_cases()}, + {reg, [], reg_simple_cases()}, {socket_close, [], socket_close_cases()}, {sc_ctrl_proc_exit, [], sc_cp_exit_cases()}, {sc_local_close, [], sc_lc_cases()}, @@ -999,6 +1003,13 @@ api_op_with_timeout_cases() -> api_to_recvmsg_tcp6 ]. +%% Socket Registry "simple" test cases +reg_simple_cases() -> + [ + reg_s_single_open_and_close_and_count + ]. + + %% These cases tests what happens when the socket is closed/shutdown, %% locally or remotely. socket_close_cases() -> @@ -22902,6 +22913,319 @@ api_to_recvmsg_tcp6(_Config) when is_list(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% +%% REGISTRY %% +%% %% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% We create a bunch of different sockets and ensure that the registry +%% has the correct info. + +reg_s_single_open_and_close_and_count(suite) -> + []; +reg_s_single_open_and_close_and_count(doc) -> + []; +reg_s_single_open_and_close_and_count(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(reg_s_single_open_and_close_and_count, + fun() -> + ok = reg_s_single_open_and_close_and_count() + end). + + +reg_s_single_open_and_close_and_count() -> + SupportsIPV6 = + case (catch has_support_ipv6()) of + ok -> + true; + _ -> + false + end, + SupportsLOCAL = + case (catch has_support_unix_domain_socket()) of + ok -> + true; + _ -> + false + end, + SupportsSCTP = + case (catch has_support_sctp()) of + ok -> + true; + _ -> + false + end, + InitSockInfos = + [ + {inet, stream, tcp}, + {inet, dgram, udp} + ] ++ + case SupportsIPV6 of + true -> + [ + {inet6, stream, tcp}, + {inet6, dgram, udp} + ]; + false -> + [] + end ++ + case SupportsLOCAL of + true -> + [ + {local, stream, default}, + {local, dgram, default} + ]; + false -> + [] + end ++ + [ + {inet, stream, tcp}, + {inet, dgram, udp} + ] ++ + case SupportsSCTP of + true -> + [ + {inet, seqpacket, sctp}, + {inet, seqpacket, sctp} + ]; + false -> + [] + end ++ + [ + {inet, stream, tcp}, + {inet, dgram, udp} + ] ++ + case SupportsSCTP andalso SupportsIPV6 of + true -> + [ + {inet6, seqpacket, sctp}, + {inet6, seqpacket, sctp} + ]; + false -> + [] + end, + + i("open sockets"), + Socks = + [fun({Domain, Type, Proto}) -> + i("open socket: ~w, ~w, ~w", [Domain, Type, Proto]), + {ok, Sock} = socket:open(Domain, Type, Proto), + Sock + end(InitSockInfo) || InitSockInfo <- InitSockInfos], + + ?SLEEP(1000), + + + %% **** Total Number Of Sockets **** + + NumSocks1 = length(Socks), + NumberOf1 = socket:number_of(), + + i("verify (total) number of sockets(1): ~w, ~w", [NumSocks1, NumberOf1]), + case (NumSocks1 =:= NumberOf1) of + true -> + ok; + false -> + exit({wrong_number_of_sockets1, NumSocks1, NumberOf1}) + end, + + + %% **** Number Of IPv4 TCP Sockets **** + + %% inet, stream, tcp + SiNumTCP = reg_si_num(InitSockInfos, inet, stream, tcp), + SrNumTCP = reg_sr_num(inet, stream, tcp), + + i("verify number of IPv4 TCP sockets: ~w, ~w", [SiNumTCP, SrNumTCP]), + case (SiNumTCP =:= SrNumTCP) of + true -> + ok; + false -> + exit({wrong_number_of_ipv4_tcp_sockets, SiNumTCP, SrNumTCP}) + end, + + + %% **** Number Of IPv4 UDP Sockets **** + + %% inet, dgram, udp + SiNumUDP = reg_si_num(InitSockInfos, inet, dgram, udp), + SrNumUDP = reg_sr_num(inet, dgram, udp), + + i("verify number of IPv4 UDP sockets: ~w, ~w", [SiNumUDP, SrNumUDP]), + case (SiNumUDP =:= SrNumUDP) of + true -> + ok; + false -> + exit({wrong_number_of_ipv4_udp_sockets, SiNumUDP, SrNumUDP}) + end, + + + %% **** Number Of IPv4 SCTP Sockets **** + + %% inet, seqpacket, sctp + SiNumSCTP = reg_si_num(InitSockInfos, inet, seqpacket, sctp), + SrNumSCTP = reg_sr_num(inet, seqpacket, sctp), + + i("verify number of IPv4 SCTP sockets: ~w, ~w", [SiNumSCTP, SrNumSCTP]), + case (SiNumSCTP =:= SrNumSCTP) of + true -> + ok; + false -> + exit({wrong_number_of_sctp_sockets, SiNumSCTP, SrNumSCTP}) + end, + + + %% **** Number Of IPv4 Sockets **** + + %% inet + SiNumINET = reg_si_num(InitSockInfos, inet), + SrNumINET = reg_sr_num(inet), + + i("verify number of IPv4 sockets: ~w, ~w", [SiNumINET, SrNumINET]), + case (SiNumINET =:= SrNumINET) of + true -> + ok; + false -> + exit({wrong_number_of_ipv4_sockets, SiNumINET, SrNumINET}) + end, + + + %% **** Number Of IPv6 Sockets **** + + %% inet6 + SiNumINET6 = reg_si_num(InitSockInfos, inet6), + SrNumINET6 = reg_sr_num(inet6), + + i("verify number of IPv6 sockets: ~w, ~w", [SiNumINET6, SrNumINET6]), + case (SiNumINET6 =:= SrNumINET6) of + true -> + ok; + false -> + exit({wrong_number_of_ipv6_sockets, SiNumINET6, SrNumINET6}) + end, + + + %% **** Number Of Unix Domain Sockets Sockets **** + + %% local + SiNumLOCAL = reg_si_num(InitSockInfos, local), + SrNumLOCAL = reg_sr_num(local), + + i("verify number of Unix Domain Sockets sockets: ~w, ~w", + [SiNumLOCAL, SrNumLOCAL]), + case (SiNumLOCAL =:= SrNumLOCAL) of + true -> + ok; + false -> + exit({wrong_number_of_local_sockets, SiNumLOCAL, SrNumLOCAL}) + end, + + + %% **** Close *all* Sockets then verify Number Of Sockets **** + + i("close sockets"), + lists:foreach(fun(S) -> + i("close socket"), + ok = socket:close(S) + end, Socks), + + ?SLEEP(1000), + + NumSocks2 = 0, + NumberOf2 = socket:number_of(), + + i("verify number of sockets(2): ~w, ~w", [NumSocks2, NumberOf2]), + case (NumSocks2 =:= NumberOf2) of + true -> + ok; + false -> + exit({wrong_number_of_sockets2, NumSocks2, NumberOf2}) + end, + + ok. + + +reg_si_num(SocksInfo, Domain) + when ((Domain =:= inet) orelse (Domain =:= inet6) orelse (Domain =:= local)) -> + reg_si_num(SocksInfo, Domain, undefined, undefined); +reg_si_num(SocksInfo, Type) + when ((Type =:= stream) orelse (Type =:= dgram) orelse (Type =:= seqpacket)) -> + reg_si_num(SocksInfo, undefined, Type, undefined); +reg_si_num(SocksInfo, Proto) + when ((Proto =:= sctp) orelse (Proto =:= tcp) orelse (Proto =:= udp)) -> + reg_si_num(SocksInfo, undefined, undefined, Proto). + +reg_si_num(SocksInfo, Domain, undefined, undefined) -> + F = fun({D, _T, _P}) when (D =:= Domain) -> true; + (_) -> false + end, + reg_si_num2(F, SocksInfo); +reg_si_num(SocksInfo, undefined, Type, undefined) -> + F = fun({_D, T, _P}) when (T =:= Type) -> true; + (_) -> false + end, + reg_si_num2(F, SocksInfo); +reg_si_num(SocksInfo, undefined, undefined, Proto) -> + F = fun({_D, _T, P}) when (P =:= Proto) -> true; + (_) -> false + end, + reg_si_num2(F, SocksInfo); +reg_si_num(SocksInfo, Domain, Type, Proto) -> + F = fun({D, T, P}) when (D =:= Domain) andalso + (T =:= Type) andalso + (P =:= Proto) -> + true; + (_) -> + false + end, + reg_si_num2(F, SocksInfo). + +reg_si_num2(F, SocksInfo) -> + length(lists:filter(F, SocksInfo)). + + +reg_sr_num(Domain) + when ((Domain =:= inet) orelse (Domain =:= inet6)) -> + length(socket:which_sockets(Domain)); +reg_sr_num(Domain) + when (Domain =:= local) -> + reg_sr_num(Domain, undefined, undefined); +reg_sr_num(Type) + when ((Type =:= stream) orelse (Type =:= dgram) orelse (Type =:= seqpacket)) -> + length(socket:which_sockets(Type)); +reg_sr_num(Proto) + when ((Proto =:= sctp) orelse (Proto =:= tcp) orelse (Proto =:= udp)) -> + length(socket:which_sockets(Proto)). + +reg_sr_num(Domain, undefined, undefined) -> + F = fun(#{domain := D}) when (D =:= Domain) -> + true; + (_X) -> + false + end, + reg_sr_num2(F); +reg_sr_num(Domain, Type, Proto) -> + F = fun(#{domain := D, + type := T, + protocol := P}) when (D =:= Domain) andalso + (T =:= Type) andalso + (P =:= Proto) -> + true; + (_X) -> + %% i("reg_sr_num -> not counting: " + %% "~n ~p", [_X]), + false + end, + reg_sr_num2(F). + +reg_sr_num2(F) -> + length(socket:which_sockets(F)). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% %% %% SOCKET CLOSURE %% %% %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam Binary files differindex 3d01d19a67..0507e1a73c 100644 --- a/erts/preloaded/ebin/socket.beam +++ b/erts/preloaded/ebin/socket.beam diff --git a/erts/preloaded/ebin/socket_registry.beam b/erts/preloaded/ebin/socket_registry.beam Binary files differnew file mode 100644 index 0000000000..9e055cbe48 --- /dev/null +++ b/erts/preloaded/ebin/socket_registry.beam diff --git a/erts/preloaded/src/Makefile b/erts/preloaded/src/Makefile index 38b85915cc..d2153f23e1 100644 --- a/erts/preloaded/src/Makefile +++ b/erts/preloaded/src/Makefile @@ -35,6 +35,7 @@ include $(ERL_TOP)/lib/kernel/vsn.mk ifeq ($(USE_ESOCK), yes) PRE_LOADED_ERL_ESOCK_MODULES = \ + socket_registry \ socket \ prim_net else @@ -81,7 +82,7 @@ APP_FILE= erts.app APP_SRC= $(APP_FILE).src APP_TARGET= $(STATIC_EBIN)/$(APP_FILE) ifeq ($(USE_ESOCK), yes) -APP_ESOCK_MODS= prim_net, socket +APP_ESOCK_MODS= prim_net, socket, socket_registry else APP_ESOCK_MODS= endif diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 5984b5bda9..165ffe3522 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -27,6 +27,9 @@ -export([ on_load/0, on_load/1, + number_of/0, + which_sockets/0, which_sockets/1, + ensure_sockaddr/1, debug/1, @@ -148,6 +151,9 @@ ]). +-define(REGISTRY, socket_registry). + + %% The command type has the general form: %% #{ %% command := atom(), @@ -164,10 +170,16 @@ -type socket_counter() :: read_byte | read_fails | read_pkg | read_tries | read_waits | write_byte | write_fails | write_pkg | write_tries | write_waits. --type socket_info() :: #{counters := socket_counters(), +-type socket_info() :: #{domain := domain(), + type := type(), + protocol := protocol(), + ctrl := pid(), + counters := socket_counters(), num_readers := non_neg_integer(), num_writers := non_neg_integer(), - num_acceptors := non_neg_integer()}. + num_acceptors := non_neg_integer(), + writable := boolean(), + readable := boolean()}. -type uint8() :: 0..16#FF. -type uint16() :: 0..16#FFFF. @@ -949,7 +961,60 @@ on_load() -> Extra :: map(). on_load(Extra) -> - ok = erlang:load_nif(atom_to_list(?MODULE), Extra). + %% This is spawned as a system process to prevent init:restart/0 from + %% killing it. + Pid = erts_internal:spawn_system_process(?REGISTRY, start, []), + ok = erlang:load_nif(atom_to_list(?MODULE), Extra#{registry => Pid}). + + +%% *** number_of *** +%% +%% Interface function to the socket registry +%% returns the number of existing (and "alive") sockets. +%% +-spec number_of() -> non_neg_integer(). + +number_of() -> + ?REGISTRY:number_of(). + + +%% *** which_sockets/0,1 *** +%% +%% Interface function to the socket registry +%% Returns a list of all the sockets, accoring to the filter rule. +%% +-spec which_sockets() -> [socket()]. + +which_sockets() -> + ?REGISTRY:which_sockets(fun(_) -> true end). + +-spec which_sockets(FilterRule) -> [socket()] when + FilterRule :: inet | inet6 | + stream | dgram | seqpacket | + sctp | tcp | udp | + pid() | + fun((socket_info()) -> boolean()). + +which_sockets(Domain) + when ((Domain =:= inet) orelse (Domain =:= inet6)) -> + ?REGISTRY:which_sockets(fun(#{domain := D}) when (D =:= Domain) -> true; + (_) -> false end); +which_sockets(Type) + when ((Type =:= stream) orelse (Type =:= dgram) orelse (Type =:= seqpacket)) -> + ?REGISTRY:which_sockets(fun(#{type := T}) when (T =:= Type) -> true; + (_) -> false end); +which_sockets(Proto) + when ((Proto =:= sctp) orelse (Proto =:= tcp) orelse (Proto =:= udp)) -> + ?REGISTRY:which_sockets(fun(#{protocol := P}) when (P =:= Proto) -> true; + (_) -> false end); +which_sockets(CTRL) + when is_pid(CTRL) -> + ?REGISTRY:which_sockets(fun(#{ctrl := C}) when (C =:= CTRL) -> true; + (_) -> false end); +which_sockets(Filter) when is_function(Filter, 1) -> + ?REGISTRY:which_sockets(Filter). + + diff --git a/erts/preloaded/src/socket_registry.erl b/erts/preloaded/src/socket_registry.erl new file mode 100644 index 0000000000..284f90157b --- /dev/null +++ b/erts/preloaded/src/socket_registry.erl @@ -0,0 +1,196 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2019-2019. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +%% ========================================================================= +%% +%% This is a registry process for the socket module. +%% The nif sends info here about all created and deleted socket(s). +%% +%% ========================================================================= + +-module(socket_registry). + +-export([ + start/0, + number_of/0, + which_sockets/1 + ]). + + +-record(esock_info, + { + sock :: socket:socket(), + atime :: integer() % Add time - erlang:monotonic_time(milli_seconds) + }). + + +%% ========================================================================= + +%% This is not a "normal" start function. Instead its the entry +%% function for the socket registry process. +start() -> + erlang:register(?MODULE, self()), + process_flag(trap_exit, true), + loop([]). + +number_of() -> + await_reply( request(number_of) ). + +which_sockets(Filter) when is_function(Filter, 1) -> + await_reply( request({which_sockets, Filter}) ). + + +%% ========================================================================= + +loop(DB) -> + receive + {'$socket', add, Socket} -> + loop( handle_add_socket(DB, Socket) ); + + {'$socket', del, Socket} -> + loop( handle_delete_socket(DB, Socket) ); + + {?MODULE, request, From, ReqId, Req} = _REQ -> + %% erlang:display(REQ), + {NewDB, Reply} = handle_request(DB, Req), + reply(ReqId, From, Reply), + loop(NewDB); + + Msg -> + NewDB = handle_unexpected_msg(DB, Msg), + loop(NewDB) + end. + + +%% ========================================================================= + +handle_add_socket(DB, Sock) -> + [#esock_info{sock = Sock, atime = timestamp()} | DB]. + +handle_delete_socket(DB, Sock) -> + lists:keydelete(Sock, #esock_info.sock, DB). + + +handle_request(DB, number_of) -> + {DB, db_size(DB)}; + +handle_request(DB, {which_sockets, Filter}) -> + {DB, do_which_sockets(DB, Filter)}; + +handle_request(DB, BadRequest) -> + {DB, {error, {bad_request, BadRequest}}}. + + +%% --- + +handle_unexpected_msg(DB, {'EXIT', Pid, Reason}) -> + F = "socket-registry received unexpected exit from ~p:" + "~n ~p", + A = [Pid, Reason], + handle_unexpected_msg(warning, F, A), + DB; +handle_unexpected_msg(DB, X) -> + F = "socket-registry received unexpected:" + "~n ~p", + A = [X], + handle_unexpected_msg(warning, F, A), + DB. + +%% This is "stolen" from the init process. But level is set to warning instead. +%% This "may" not be what you want, but we can always decrease to info instead... +%% Also, we may receive (unexpected) messages that should be classified +%% differently (info, warning, ...)... +%% +%% This is equal to calling logger:[info|warning]/3 which we don't +%% want to do from this process, at least not during +%% system boot. We don't want to call logger:timestamp() +%% either. +%% + +%% handle_unexpected_msg(info, F, A) -> +%% do_handle_unexpected_msg( mk_unexpected_info_msg(F, A) ). +handle_unexpected_msg(warning, F, A) -> + do_handle_unexpected_msg( mk_unexpected_warning_msg(F, A) ). + +do_handle_unexpected_msg(Msg) -> + catch logger ! Msg. + + +%% mk_unexpected_info_msg(F, A) -> +%% mk_unexpected_msg(info, info_msg, F, A). + +mk_unexpected_warning_msg(F, A) -> + mk_unexpected_msg(warning, warning_msg, F, A). + +mk_unexpected_msg(Level, Tag, F, A) -> + Meta = #{pid => self(), + gl => erlang:group_leader(), + time => os:system_time(microsecond), + error_logger => #{tag => Tag}}, + {log, Level, F, A, Meta}. + + +%% --- + +db_size(DB) -> + length(DB). + +do_which_sockets(DB, Filter) -> + try + begin + SocksInfo = + [{Sock, socket:info(Sock)} || #esock_info{sock = Sock} <- DB], + [Sock || {Sock, SockInfo} <- SocksInfo, Filter(SockInfo)] + end + catch + _:_:_ -> + [Sock || #esock_info{sock = Sock} <- DB] + end. + + + +%% ========================================================================= + +request(Req) -> + ReqId = make_ref(), + ReqMsg = {?MODULE, request, self(), ReqId, Req}, + Registry = whoami(), + erlang:send(Registry, ReqMsg), + ReqId. + +reply(ReqId, From, Reply) -> + RepMsg = {?MODULE, reply, self(), ReqId, Reply}, + erlang:send(From, RepMsg). + +await_reply(ReqId) -> + Registry = whoami(), + receive + {?MODULE, reply, Registry, ReqId, Reply} -> + Reply + end. + + +%% ========================================================================= + +whoami() -> + whereis(?MODULE). + +timestamp() -> + erlang:monotonic_time(milli_seconds). diff --git a/lib/common_test/doc/src/Makefile b/lib/common_test/doc/src/Makefile index b5acdc6f95..ae06572752 100644 --- a/lib/common_test/doc/src/Makefile +++ b/lib/common_test/doc/src/Makefile @@ -74,6 +74,7 @@ XML_CHAPTER_FILES = \ ct_master_chapter.xml \ event_handler_chapter.xml \ ct_hooks_chapter.xml \ + ct_property_test_chapter.xml \ dependencies_chapter.xml \ notes.xml diff --git a/lib/common_test/doc/src/cover_chapter.xml b/lib/common_test/doc/src/cover_chapter.xml index 6c180c276c..e05943bb3b 100644 --- a/lib/common_test/doc/src/cover_chapter.xml +++ b/lib/common_test/doc/src/cover_chapter.xml @@ -125,8 +125,12 @@ <section> <title>The Cover Specification File</title> - <p>The following terms are allowed in a cover specification file:</p> + <section> + <title>General Config</title> + + <p>Here follows the general configuration terms that are allowed in a cover specification file:</p> + <pre> %% List of Nodes on which cover will be active during test. %% Nodes = [atom()] @@ -143,7 +147,7 @@ %% Cover analysis level. %% Level = details | overview {level, Level}. - + %% Directories to include in cover. %% Dirs = [string()] {incl_dirs, Dirs}. @@ -169,19 +173,37 @@ %% Mod = [atom()], modules to compile for accumulated analysis {cross,[{Tag,Mods}]}.</pre> - <p>The terms <c>incl_dirs_r</c> and <c>excl_dirs_r</c> tell <c>Common - Test</c> to search the specified directories recursively and include - or exclude any module found during the search. The terms - <c>incl_dirs</c> and <c>excl_dirs</c> result in a - non-recursive search for modules (that is, only modules found in - the specified directories are included or excluded).</p> - <note><p>Directories containing Erlang modules to be included in a code - coverage test must exist in the code server path. Otherwise, - the Cover tool fails to recompile the modules. It is not sufficient to - specify these directories in the cover specification file for - <c>Common Test</c>.</p></note> + <p>The terms <c>incl_dirs_r</c> and <c>excl_dirs_r</c> tell <c>Common + Test</c> to search the specified directories recursively and include + or exclude any module found during the search. The terms + <c>incl_dirs</c> and <c>excl_dirs</c> result in a + non-recursive search for modules (that is, only modules found in + the specified directories are included or excluded).</p> + <note><p>Directories containing Erlang modules to be included in a code + coverage test must exist in the code server path. Otherwise, + the Cover tool fails to recompile the modules. It is not sufficient to + specify these directories in the cover specification file for + <c>Common Test</c>.</p></note> + </section> + + <section> + <title>OTP application Config</title> + + <p>When using a cover specification in the testing of an OTP + application itself, there is a special incl_app directive that + includes the applications modules for the cover compilation.</p> + + <pre> +{incl_app, AppName, Cover:: overview | details}. +</pre> + + <note><p>If you desire to also use some other general cover configuration + together with this option you should insert the AppName in between + the option and its value creating a three tuple. + </p></note> + </section> </section> - + <section> <marker id="cross_cover"/> <title>Cross Cover Analysis</title> diff --git a/lib/common_test/doc/src/ct_property_test.xml b/lib/common_test/doc/src/ct_property_test.xml index 1e01d9a5d7..1690e9962a 100644 --- a/lib/common_test/doc/src/ct_property_test.xml +++ b/lib/common_test/doc/src/ct_property_test.xml @@ -33,30 +33,39 @@ <file>ct_property_test.xml</file> </header> <module since="OTP 17.3">ct_property_test</module> - <modulesummary>EXPERIMENTAL support in Common Test for calling - property-based tests.</modulesummary> + <modulesummary>Support in Common Test for running property-based tests.</modulesummary> <description> - <p>EXPERIMENTAL support in <c>Common Test</c> for calling property-based - tests.</p> + <p>This module helps running property-based tests in the + <c>Common Test</c> framework. One (or more) of the property testing tools + </p> + <list> + <item><url href="http://www.quviq.com">QuickCheck</url>,</item> + <item><url href="https://proper-testing.github.io">PropEr</url> or</item> + <item><url href="https://github.com/krestenkrab/triq">Triq</url></item> + </list> + <p> + is assumed to be installed. + </p> - <p>This module is a first step to run property-based tests in the - <c>Common Test</c> framework. A property testing tool like QuickCheck - or PropEr is assumed to be installed.</p> - - <p>The idea is to have a <c>Common Test</c> test suite calling a property - testing tool with special property test suites as defined by that tool. - The usual Erlang application directory structure is assumed. The tests - are collected in the <c>test</c> directory of the application. The - <c>test</c> directory has a subdirectory <c>property_test</c>, where - everything needed for the property tests is collected.</p> + <p>The idea with this module is to have a <c>Common Test</c> test suite calling + a property testing tool with special property test suites as defined by that tool. + The tests + are collected in the <c>test</c> directory of the application. The + <c>test</c> directory has a subdirectory <c>property_test</c>, where + everything needed for the property tests are collected. + The usual Erlang application directory structure is assumed. + </p> <p>A typical <c>Common Test</c> test suite using <c>ct_property_test</c> - is organized as follows:</p> + is organized as follows:</p> + + <code> +-module(my_prop_test_SUITE). +-compile(export_all). - <pre> - -include_lib("common_test/include/ct.hrl"). +-include_lib("common_test/include/ct.hrl"). all() -> [prop_ftp_case]. @@ -66,51 +75,198 @@ %%%---- test case prop_ftp_case(Config) -> ct_property_test:quickcheck( - ftp_simple_client_server:prop_ftp(Config), + ftp_simple_client_server:prop_ftp(), Config - ).</pre> + ).</code> + <p>and the the property test module (in this example <c>ftp_simple_client_server.erl</c>) + as almost a usual property testing module + (More examples are in <seealso marker="ct_property_test_chapter">the User's Guide</seealso>):</p> + <code> +-module(ftp_simple_client_server). +-export([prop_ftp/0...]). - <warning> - <p>This is experimental code that can be changed or removed anytime - without any warning.</p> - </warning> +-include_lib("common_test/include/ct_property_test.hrl"). +prop_ftp() -> + ?FORALL( .... + </code> </description> <funcs> <func> <name since="OTP 17.3">init_per_suite(Config) -> Config | {skip, Reason}</name> - <fsummary>Initializes Config for property testing.</fsummary> + <fsummary>Initializes and extends Config for property testing.</fsummary> <desc><marker id="init_per_suite-1"/> - <p>Initializes <c>Config</c> for property testing.</p> + <p>Initializes and extends <c>Config</c> for property based testing.</p> <p>This function investigates if support is available for either - Quickcheck, PropEr, or Triq. The options - <c>{property_dir,AbsPath}</c> and <c>{property_test_tool,Tool}</c> - are set in the <c>Config</c> returned.</p> + <url href="http://www.quviq.com">QuickCheck</url>, + <url href="https://proper-testing.github.io">PropEr</url> + or <url href="https://github.com/krestenkrab/triq">Triq</url> and compiles the + properties with the first tool found. + It is supposed to be called in the <c>init_per_suite/1</c> function + in a CommonTest test suite. + </p> + <p>Which tools to check for, and in which order could be set with the option + <c>{prop_tools, list(eqc|proper|triq)}</c> + in the CommonTest configuration <c>Config</c>. The default value is + <c>[eqc, proper, triq]</c> with <c>eqc</c> being the first one searched for. + </p> + <p>If no support is found for any tool, this function returns + <c>{skip, Explanation}</c>. + </p> + <p>If support is found, the option <c>{property_test_tool,ToolModule}</c> with + the selected tool main module name (<c>eqc</c>, <c>proper</c> or <c>triq</c>) + is added to the list <c>Config</c> which then is returned. + </p> + <p>The property tests are assumed to be in a subdirectory named + <c>property_test</c>. + All found Erlang files in that directory are compiled with one of the macros + <c>'EQC'</c>, <c>'PROPER'</c> or <c>'TRIQ'</c> set, depending on which tool + that is first found. This could make parts of the Erlang property tests + code to be included or excluded with the macro directives + <c>-ifdef(Macro).</c> or <c>-ifndef(Macro).</c>. + </p> + <p>The file(s) in the <c>property_test</c> subdirectory could, or should, + include the ct_property_test include file: + </p> + <code> +-include_lib("common_test/include/ct_property_test.hrl"). + </code> + <p>This included file will: + </p> + <list> + <item>Include the correct tool's include file</item> + <item>Set the macro <c>'MOD_eqc'</c> to the correct module name for the + selected tool. That is, the macro <c>'MOD_eqc'</c> is set to either + <c>eqc</c>, <c>proper</c> or <c>triq</c>. + </item> + </list> + </desc> + </func> - <p>The function is intended to be called in function - <c>init_per_suite</c> in the test suite.</p> + <func> + <name since="OTP 17.3">quickcheck(Property, Config) -> true | {fail, Reason}</name> + <fsummary>Calls quickcheck and returns the result in a form suitable for + Common Test.</fsummary> + <desc> + <p>Calls the selected tool's function for running the <c>Property</c>. It is usually and + by historical reasons called quickcheck, and that is why that name is used in + this module (<c>ct_property_test</c>). + </p> + <p>The result is returned in a form suitable for <c>Common Test</c> test suites. + </p> + <p>This function is intended to be called in test cases in test suites. + </p> + </desc> + </func> - <p>The property tests are assumed to be in subdirectory - <c>property_test</c>.</p> + <func> + <name since="">present_result(Module, Cmds, Triple, Config) -> Result</name> + <fsummary>Presents the result of statem property testing</fsummary> + <desc> + <p>Same as <seealso marker="#present_result/5"><c>present_result(Module, Cmds, Triple, Config, [])</c></seealso> + </p> </desc> </func> <func> - <name since="OTP 17.3">quickcheck(Property, Config) -> true | {fail, Reason}</name> - <fsummary>Calls quickcheck and returns the result in a form suitable for - Common Test.</fsummary> - <desc><marker id="quickcheck-2"/> - <p>Calls quickcheck and returns the result in a form suitable for - <c>Common Test</c>.</p> + <name since="">present_result(Module, Cmds, Triple, Config, Options) -> Result</name> + <fsummary>Presents the result of statem property testing</fsummary> + <type> + <v>Module = module()</v> + <d></d> + + <v>Cmds =</v> + <d>the list of commands generated by the property testing tool, for example + by proper:commands/1 or by proper:parallel_commands/1 + </d> + + <v>Triple =</v> + <d>the output from for example proper:run_commands/2 or proper:run_parallel_commands/2</d> + + <v>Config =</v> + <d>the Common Test <seealso marker="common_test#Module:Testcase/1">Config</seealso> in test cases.</d> + + <v>Options = [present_option()]</v> + <v>present_option() = {print_fun, fun(Format,Args)}</v> + <v> | {spec, StatisticsSpec}</v> + <d>The <c>print_fun</c> defines which function to do the actual printout. The default is + <seealso marker="ct#log/2">ct:log/2</seealso>. + The <c>spec</c> defines what statistics are to be printed<!--, see the + <seealso marker="ct_property_test_chapter#spec_present_result">User's Guide</seealso>--> + </d> + + <v>Result = boolean()</v> + <d>Is <c>false</c> if the test failed and is <c>true</c> if the test passed</d> + </type> + <desc> + <p>Presents the result of <i>stateful (statem) property testing</i> using the aggregate function in + PropEr, QuickCheck or other similar property testing tool. + </p> + <p>It is assumed to be called inside the property called by + <seealso marker="#quickcheck/2">quickcheck/2</seealso>:</p> + <code> +... +RunResult = run_parallel_commands(?MODULE, Cmds), +ct_property_test:present_result(?MODULE, Cmds, RunResult, Config) +... + </code> + <p>See the <seealso marker="ct_property_test_chapter#stateful1">User's Guide</seealso> for + an example of the usage and of the default printout. + </p> + <p>The <c>StatisticsSpec</c> is a list of the tuples:</p> + <list> + <item><c>{Title::string(), CollectFun::fun/1}</c></item> + <item><c>{Title::string(), FrequencyFun::/0, CollectFun::fun/1}</c></item> + </list> + <p>Each tuple will produce one table in the order of their places in the list.</p> + <list> + <item><c>Title</c> will be the title of one result table</item> - <p>This function is intended to be called in the test cases in the - test suite.</p> + <item><c>CollectFun</c> is called with one argument: the <c>Cmds</c>. It should return + a list of the values to be counted. The following pre-defined functions exist: + <list> + <item><c>ct_property_test:cmnd_names/1</c> returns a list of commands (function calls) generated in the <c>Cmnd</c> + sequence, without Module, Arguments and other details.</item> + <item><c>ct_property_test:num_calls/1</c> returns a list of the length of commands lists</item> + <item><c>ct_property_test:sequential_parallel/1</c> returns a list with information about sequential and + parallel parts from <c>Tool:parallel_commands/1,2</c></item> + </list> + </item> + + <item><c>FrequencyFun/0</c> returns a fun/1 which is supposed to take a list of items as input, + and return an iolist wich will be printed as the table. Per default, the number of each item is counted + and the percentage is printed for each. The list [a,b,a,a,c] could for example return + <pre> + ["a 60%\n","b 20%\n","c 20%\n"]</pre> + which will be printed by the <c>print_fun</c>. + The default <c>print_fun</c> will print it as: + <pre> + a 60% + b 20% + c 20%</pre> + </item> + </list> + <p>The default <c>StatisticsSpec</c> is:</p> + <list> + <item>For sequential commands: + <code> +[{"Function calls", fun cmnd_names/1}, + {"Length of command sequences", fun print_frequency_ranges/0, + fun num_calls/1}] + </code></item> + <item>For parallel commands: + <code> +[{"Distribution sequential/parallel", fun sequential_parallel/1}, + {"Function calls", fun cmnd_names/1}, + {"Length of command sequences", fun print_frequency_ranges/0, + fun num_calls/1}] + </code></item> + </list> </desc> </func> + </funcs> </erlref> - - diff --git a/lib/common_test/doc/src/ct_property_test_chapter.xml b/lib/common_test/doc/src/ct_property_test_chapter.xml new file mode 100644 index 0000000000..131f3a962d --- /dev/null +++ b/lib/common_test/doc/src/ct_property_test_chapter.xml @@ -0,0 +1,249 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>2019</year><year>2019</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + </legalnotice> + + <title>Common Test's Property Testing Support: ct_property_test</title> + <prepared>Hans Nilsson</prepared> + <docno></docno> + <date></date> + <rev></rev> + <file>ct_property_test_chapter.xml</file> + </header> + + <section> + <marker id="general"></marker> + <title>General</title> + <p> + The <em>Common Test Property Testing Support (ct_property_test)</em> + is an aid to run property based testing tools in Common Test test suites. + </p> + <p> + Basic knowledge of property based testing is assumed in the following. + It is also assumed that at least one of the following property based + testing tools is installed and available in the library path: + </p> + <list> + <item><url href="http://www.quviq.com">QuickCheck</url>,</item> + <item><url href="https://proper-testing.github.io">PropEr</url> or</item> + <item><url href="https://github.com/krestenkrab/triq">Triq</url></item> + </list> + </section> + + <section> + <marker id="supported"></marker> + <title>What Is Supported?</title> + <p>The <seealso marker="ct_property_test#">ct_property_test</seealso> module + does the following: + </p> + <list type="bulleted"> + <item>Compiles the files with property tests in the subdirectory <c>property_test</c> + </item> + <item>Tests properties in those files using the first found Property Testing Tool. + </item> + <item>Saves the results - that is the printouts - in the usual Common Test Log + </item> + </list> + </section> + + + <section> + <title>Introductory Example</title> + <p>Assume that we want to test the lists:sort/1 function. + </p> + <p>We need a property to test the function. In normal way, we create + <c>property_test/ct_prop.erl</c> module in the <c>test</c> directory + in our application: + </p> + + <code> +-module(ct_prop). +-export([prop_sort/0]). + +%%% This will include the .hrl file for the installed testing tool: +-include_lib("common_test/include/ct_property_test.hrl"). + +%%% The property we want to check: +%%% For all possibly unsorted lists, +%%% the result of lists:sort/1 is sorted. +prop_sort() -> + ?FORALL(UnSorted, list(), + is_sorted(lists:sort(UnSorted)) + ). + +%%% Function to check that a list is sorted: +is_sorted([]) -> + true; +is_sorted([_]) -> + true; +is_sorted([H1,H2|SortedTail]) when H1 =< H2 -> + is_sorted([H2|SortedTail]); +is_sorted(_) -> + false. + </code> + + <p>We also need a CommonTest test suite: + </p> + <code> +-module(ct_property_test_SUITE). +-compile(export_all). % Only in tests! + +-include_lib("common_test/include/ct.hrl"). + +all() -> [prop_sort + ]. + +%%% First prepare Config and compile the property tests for the found tool: +init_per_suite(Config) -> + ct_property_test:init_per_suite(Config). + +end_per_suite(Config) -> + Config. + +%%%================================================================ +%%% Test suites +%%% +prop_sort(Config) -> + ct_property_test:quickcheck( + ct_prop:prop_sort(), + Config + ). + </code> + + <p>We run it as usual, for example with ct_run in the OS shell:</p> + <pre> +..../test$ ct_run -suite ct_property_test_SUITE +..... +Common Test: Running make in test directories... + +TEST INFO: 1 test(s), 1 case(s) in 1 suite(s) + +Testing lib.common_test.ct_property_test_SUITE: Starting test, 1 test cases + +---------------------------------------------------- +2019-12-18 10:44:46.293 +Found property tester proper +at "/home/X/lib/proper/ebin/proper.beam" + + +---------------------------------------------------- +2019-12-18 10:44:46.294 +Compiling in "/home/..../test/property_test" + Deleted: ["ct_prop.beam"] + ErlFiles: ["ct_prop.erl"] + MacroDefs: [{d,'PROPER'}] + +Testing lib.common_test.ct_property_test_SUITE: TEST COMPLETE, 1 ok, 0 failed of 1 test cases + +.... + </pre> + </section> + + + <section> + <marker id="stateful1"></marker> + <title>A stateful testing example</title> + <p>Assume a test that generates some parallel stateful commands, and runs 300 tests:</p> + <code> +prop_parallel(Config) -> + numtests(300, + ?FORALL(Cmds, parallel_commands(?MODULE), + begin + RunResult = run_parallel_commands(?MODULE, Cmds), + ct_property_test:present_result(?MODULE, Cmds, RunResult, Config) + end)). + </code> + <p>The + <seealso marker="ct_property_test#present_result/4">ct_property_test:present_result/4</seealso> + is a help function for printing some statistics in the CommonTest log file.</p> + <p>Our example test could for example be a simple test of an ftp server, where we perform get, put + and delete requests, some of them in parallel. Per default, the result has three sections: + </p> + <pre> +*** User 2019-12-11 13:28:17.504 *** + +Distribution sequential/parallel + + 57.7% sequential + 28.0% parallel_2 + 14.3% parallel_1 + + + +*** User 2019-12-11 13:28:17.505 *** + +Function calls + + 44.4% get + 39.3% put + 16.3% delete + + + +*** User 2019-12-11 13:28:17.505 *** + +Length of command sequences + +Range : Number in range +-------:---------------- + 0 - 4: 8 2.7% <-- min=3 + 5 - 9: 44 14.7% +10 - 14: 74 24.7% +15 - 19: 60 20.0% <-- mean=18.7 <-- median=16.0 +20 - 24: 38 12.7% +25 - 29: 26 8.7% +30 - 34: 19 6.3% +35 - 39: 19 6.3% +40 - 44: 8 2.7% +45 - 49: 4 1.3% <-- max=47 + ------ + 300 + </pre> + <p>The first part - <i>Distribution sequential/parallel</i> - shows the distribution in the + sequential and parallel part of the result of parallel_commands/1. See any property testing tool for + an explanation of this function. + The table shows that of all commands (get and put in our case), + 57.7% are executed in the sequential part prior to the parallel part, + 28.0% are executed in the first parallel list and the rest in the second parallel list. + </p> + + <p>The second part - <i>Function calls</i> - shows the distribution of the three calls in the + generated command lists. We see that all of the three calls are executed. If it was so that we + thought that we also generated a fourth call, a table like this shows that we failed with that. + </p> + + <p>The third and final part - <i>Length of command sequences</i> - show statistics of the + generated command sequences. We see that the shortest list has three elementes while the longest + has 47 elements. The mean and median values are also shown. Further we could for example see that + only 2.7% of the lists (that is eight lists) only has three or four elements. + </p> + + </section> + + <!--section> + <marker id="spec_present_result"></marker> + <title>The spec for present_result/5</title> + <p>To be written... + <seealso marker="ct_property_test#present_result/5">present_result/5</seealso> + </p> + </section--> +</chapter> diff --git a/lib/common_test/doc/src/part.xml b/lib/common_test/doc/src/part.xml index 000eb06b82..66dcf75258 100644 --- a/lib/common_test/doc/src/part.xml +++ b/lib/common_test/doc/src/part.xml @@ -48,6 +48,7 @@ <xi:include href="dependencies_chapter.xml"/> <xi:include href="ct_hooks_chapter.xml"/> <xi:include href="why_test_chapter.xml"/> + <xi:include href="ct_property_test_chapter.xml"/> </part> diff --git a/lib/common_test/include/ct_property_test.hrl b/lib/common_test/include/ct_property_test.hrl new file mode 100644 index 0000000000..9d5933fde3 --- /dev/null +++ b/lib/common_test/include/ct_property_test.hrl @@ -0,0 +1,40 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2004-2019. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% +%% + +-ifndef(CT_PROPERTY_TEST_HRL). + -define(CT_PROPERTY_TEST_HRL, true). + + -ifdef(EQC). + -define(MOD_eqc, eqc). + -include_lib("eqc/include/eqc.hrl"). + -else. + -ifdef(PROPER). + -define(MOD_eqc, proper). + -include_lib("proper/include/proper.hrl"). + -else. + -ifdef(TRIQ). + -define(MOD_eqc, triq). + -include_lib("triq/include/triq.hrl"). + -endif. + -endif. + -endif. + +-endif. diff --git a/lib/common_test/src/Makefile b/lib/common_test/src/Makefile index 76689dab8c..ffdef8ec39 100644 --- a/lib/common_test/src/Makefile +++ b/lib/common_test/src/Makefile @@ -96,7 +96,8 @@ HRL_FILES = \ ct_netconfc.hrl EXTERNAL_HRL_FILES = \ ../include/ct.hrl \ - ../include/ct_event.hrl + ../include/ct_event.hrl \ + ../include/ct_property_test.hrl EXTERNAL_INC_PATH = ../include diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl index 367b5f5fdc..0806da9684 100644 --- a/lib/common_test/src/ct_framework.erl +++ b/lib/common_test/src/ct_framework.erl @@ -946,10 +946,6 @@ error_notification(Mod,Func,_Args,{Error,Loc}) -> io_lib:format("{test_case_failed,~tp}", [Reason]); Result -> Result end; - {'EXIT',_Reason} = EXIT -> - io_lib:format("~tP", [EXIT,5]); - {Spec,_Reason} when is_atom(Spec) -> - io_lib:format("~tw", [Spec]); Other -> io_lib:format("~tP", [Other,5]) end, diff --git a/lib/common_test/src/ct_property_test.erl b/lib/common_test/src/ct_property_test.erl index 93642a0970..251a0a4896 100644 --- a/lib/common_test/src/ct_property_test.erl +++ b/lib/common_test/src/ct_property_test.erl @@ -18,26 +18,40 @@ %% %CopyrightEnd% %% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%% %%% -%%% WARNING %%% -%%% %%% -%%% This is experimental code which may be changed or removed %%% -%%% anytime without any warning. %%% -%%% %%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -module(ct_property_test). -%% API +%%% API +%% Main functions -export([init_per_suite/1, - quickcheck/2]). + quickcheck/2 + ]). + +%% Result presentation +-export([present_result/4, present_result/5, + title/2, title/3, + sequential_parallel/1, + cmnd_names/1, + num_calls/1, + print_frequency_ranges/0, + print_frequency/0 + ]). +%%% Mandatory include -include_lib("common_test/include/ct.hrl"). +%%%================================================================ +%%% +%%% API +%%% + +%%%---------------------------------------------------------------- +%%% +%%% Search for a property tester in the lib path, and if found, compile +%%% the property tests +%%% init_per_suite(Config) -> - case which_module_exists([eqc,proper,triq]) of + ToolsToCheck = proplists:get_value(prop_tools, Config, [eqc,proper,triq]), + case which_module_exists(ToolsToCheck) of {ok,ToolModule} -> case code:where_is_file(lists:concat([ToolModule,".beam"])) of non_existing -> @@ -66,12 +80,71 @@ init_per_suite(Config) -> {skip, "No property testing tool found"} end. +%%%---------------------------------------------------------------- +%%% +%%% Call the found property tester (if any) +%%% quickcheck(Property, Config) -> Tool = proplists:get_value(property_test_tool,Config), F = function_name(quickcheck, Tool), mk_ct_return( Tool:F(Property), Tool ). +%%%---------------------------------------------------------------- +%%% +%%% Present a nice table of the statem result +%%% +present_result(Module, Cmds, Triple, Config) -> + present_result(Module, Cmds, Triple, Config, []). + +present_result(Module, Cmds, {H,Sf,Result}, Config, Options0) -> + DefSpec = + if + is_tuple(Cmds) -> + [{"Distribution sequential/parallel", fun sequential_parallel/1}]; + is_list(Cmds) -> + [] + end + ++ [{"Function calls", fun cmnd_names/1}, + {"Length of command sequences", fun print_frequency_ranges/0, fun num_calls/1} + ], + Options = add_default_options(Options0, + [{print_fun, fun ct:log/2}, + {spec, DefSpec} + ]), + do_present_result(Module, Cmds, H, Sf, Result, Config, Options). + + +title(Str, Fun) -> + title(Str, Fun, fun io:format/2). + +title(Str, Fun, PrintFun) -> + fun(L) -> PrintFun("~n~s~n~n~s~n", [Str,Fun(L)]) end. + +print_frequency() -> + fun(L) -> + [io_lib:format("~5.1f% ~p~n",[Pcnt,V]) + || {V,_Num,Pcnt} <- + with_percentage(get_frequencies_no_range(L), length(L)) + ] + end. + +print_frequency_ranges() -> + print_frequency_ranges([{ngroups,10}]). + +print_frequency_ranges(Options0) -> + fun([]) -> + io_lib:format('Empty list!~n',[]); + (L ) -> + try + Options = set_default_print_freq_range_opts(Options0, L), + do_print_frequency_ranges(L, Options) + catch + C:E:S -> + ct:pal("~p:~p ~p:~p~n~p~n~p",[?MODULE,?LINE,C,E,S,L]) + end + end. + %%%================================================================ %%% %%% Local functions @@ -155,3 +228,217 @@ macro_def(triq) -> [{d, 'TRIQ'}]. function_name(quickcheck, triq) -> check; function_name(F, _) -> F. + +%%%================================================================ +%%%================================================================ +%%%================================================================ +%%% +%%% Result presentation part +%%% +do_present_result(_Module, Cmds, _H, _Sf, ok, Config, Options) -> + [PrintFun, Spec] = [proplists:get_value(K,Options) || K <- [print_fun,spec]], + Tool = proplists:get_value(property_test_tool,Config), + AGGREGATE = function_name(aggregate, Tool), + lists:foldr(fun({Title, FreqFun, CollecFun}, Result) -> + Tool:AGGREGATE(title(Title, FreqFun(), PrintFun), + CollecFun(Cmds), + Result); + ({Title, CollecFun}, Result) -> + Tool:AGGREGATE(title(Title, print_frequency(), PrintFun), + CollecFun(Cmds), + Result) + end, true, Spec); + +do_present_result(Module, Cmds, H, Sf, Result, _Config, Options) -> + [PrintFun] = [proplists:get_value(K,Options) || K <- [print_fun]], + PrintFun("Module = ~p,~n" + "Commands = ~p,~n" + "History = ~p,~n" + "FinalDynState = ~p,~n" + "Result = ~p", + [Module, Cmds, H, Sf, Result]), + Result == ok. % Proper dislikes non-boolean results while eqc treats non-true as false. + +%%%================================================================ +cmnd_names(Cs) -> traverse_commands(fun cmnd_name/1, Cs). +cmnd_name(L) -> [F || {set,_Var,{call,_Mod,F,_As}} <- L]. + +num_calls(Cs) -> traverse_commands(fun num_call/1, Cs). +num_call(L) -> [length(L)]. + +sequential_parallel(Cs) -> + traverse_commands(fun(L) -> dup_module(L, sequential) end, + fun(L) -> [dup_module(L1, mkmod("parallel",num(L1,L))) || L1<-L] end, + Cs). +dup_module(L, ModName) -> lists:duplicate(length(L), ModName). +mkmod(PfxStr,N) -> list_to_atom(PfxStr++"_"++integer_to_list(N)). + +%% Meta functions for the aggregate functions +traverse_commands(Fun, L) when is_list(L) -> Fun(L); +traverse_commands(Fun, {Seq, ParLs}) -> Fun(lists:append([Seq|ParLs])). + +traverse_commands(Fseq, _Fpar, L) when is_list(L) -> Fseq(L); +traverse_commands(Fseq, Fpar, {Seq, ParLs}) -> lists:append([Fseq(Seq)|Fpar(ParLs)]). + +%%%================================================================ +-define(middle_dot, 0183). + +set_default_print_freq_range_opts(Opts0, L) -> + add_default_options(Opts0, [{ngroups, 10}, + {min, 0}, + {max, max_in_list(L)} + ]). + +add_default_options(Opts0, DefaultOpts) -> + [set_def_opt(Key,DefVal,Opts0) || {Key,DefVal} <- DefaultOpts]. + +set_def_opt(Key, DefaultValue, Opts) -> + {Key, proplists:get_value(Key, Opts, DefaultValue)}. + +max_in_list(L) -> + case lists:last(L) of + Max when is_integer(Max) -> Max; + {Max,_} -> Max + end. + +do_print_frequency_ranges(L0, Options) -> + [N,Min,Max] = [proplists:get_value(K,Options) || K <- [ngroups, min, max]], + L = if + N>Max -> + %% There will be less than the demanded number of classes, + %% insert one last with zero values in it. That will force + %% the generation of N classes. + L0++[{N,0}]; + N=<Max -> + L0 + end, + try + Interval = round((Max-Min)/N), + IntervalLowerLimits = lists:seq(Min,Max,Interval), + Ranges = [{I,I+Interval-1} || I <- IntervalLowerLimits], + Acc0 = [{Rng,0} || Rng <- Ranges], + Fs0 = get_frequencies(L, Acc0), + SumVal = lists:sum([V||{_,V}<-Fs0]), + Fs = with_percentage(Fs0, SumVal), + DistInfo = [{"min", lists:min(L)}, + {"mean", mean(L)}, + {"median", median(L)}, + {"max", lists:max(L)}], + + Npos_value = num_digits(SumVal), + Npos_range = num_digits(Max), + [%% Table heading: + io_lib:format("Range~*s: ~s~n",[2*Npos_range-2,"", "Number in range"]), + %% Line under heading: + io_lib:format("~*c:~*c~n",[2*Npos_range+3,$-, max(16,Npos_value+10),$- ]), + %% Lines with values: + [io_lib:format("~*w - ~*w: ~*w ~5.1f% ~s~n", + [Npos_range,Rlow, + Npos_range,Rhigh, + Npos_value,Val, + Percent, + cond_prt_vals(DistInfo, Interv) + ]) + || {Interv={Rlow,Rhigh},Val,Percent} <- Fs], + %% Line under the table for the total number of values: + io_lib:format('~*c ~*c~n',[2*Npos_range,32, Npos_value+3,$-]), + %% The total number of values: + io_lib:format('~*c ~*w~n',[2*Npos_range,32, Npos_value,SumVal]) + ] + catch + C:E -> + ct:pal('*** Failed printing (~p:~p) for~n~p~n',[C,E,L]) + end. + +cond_prt_vals(LVs, CurrentInterval) -> + [prt_val(Label, Value, CurrentInterval) || {Label,Value} <- LVs]. + +prt_val(Label, Value, CurrentInterval) -> + case in_interval(Value, CurrentInterval) of + true -> + io_lib:format(" <-- ~s=" ++ if + is_float(Value) -> "~.1f"; + true -> "~p" + end, + [Label,Value]); + false -> + "" + end. + +get_frequencies([{I,Num}|T], [{{Lower,Upper},Cnt}|Acc]) when Lower=<I,I=<Upper -> + get_frequencies(T, [{{Lower,Upper},Cnt+Num}|Acc]); +get_frequencies(L=[{I,_Num}|_], [Ah={{_Lower,Upper},_Cnt}|Acc]) when I>Upper -> + [Ah | get_frequencies(L,Acc)]; +get_frequencies([I|T], Acc) when is_integer(I) -> + get_frequencies([{I,1}|T], Acc); +get_frequencies([], Acc) -> + Acc. + +get_frequencies_no_range([]) -> + io_lib:format("No values~n", []); +get_frequencies_no_range(L) -> + [H|T] = lists:sort(L), + get_frequencies_no_range(T, H, 1, []). + +get_frequencies_no_range([H|T], H, N, Acc) -> + get_frequencies_no_range(T, H, N+1, Acc); +get_frequencies_no_range([H1|T], H, N, Acc) -> + get_frequencies_no_range(T, H1, 1, [{H,N}|Acc]); +get_frequencies_no_range([], H, N, Acc) -> + lists:reverse( + lists:keysort(2, [{H,N}|Acc])). + +%% get_frequencies_percent(L) -> +%% with_percentage(get_frequencies_no_range(L), length(L)). + + +with_percentage(Fs, Sum) -> + [{Rng,Val,100*Val/Sum} || {Rng,Val} <- Fs]. + + +num_digits(I) -> 1+trunc(math:log(I)/math:log(10)). + +num(Elem, List) -> length(lists:takewhile(fun(E) -> E /= Elem end, List)) + 1. + +%%%---- Just for naming an operation for readability +is_odd(I) -> (I rem 2) == 1. + +in_interval(Value, {Rlow,Rhigh}) -> + try + Rlow=<round(Value) andalso round(Value)=<Rhigh + catch + _:_ -> false + end. + +%%%================================================================ +%%% Statistical functions + +%%%---- Mean value +mean(L = [X|_]) when is_number(X) -> + lists:sum(L) / length(L); +mean(L = [{_Value,_Weight}|_]) -> + SumOfWeights = lists:sum([W||{_,W}<-L]), + WeightedSum = lists:sum([W*V||{V,W}<-L]), + WeightedSum / SumOfWeights; +mean(_) -> + undefined. + +%%%---- Median +median(L = [X|_]) when is_number(X) -> + Len = length(L), + case is_odd(Len) of + true -> + hd(lists:nthtail(Len div 2, L)); + false -> + %% 1) L has at least one element (the one in the is_number test). + %% 2) Length is even. + %% => Length >= 2 + [M1,M2|_] = lists:nthtail((Len div 2)-1, L), + (M1+M2) / 2 + end; +%% integer Weights... +median(L = [{_Value,_Weight}|_]) -> + median( lists:append([lists:duplicate(W,V) || {V,W} <- L]) ); +median(_) -> + undefined. + diff --git a/lib/common_test/test/Makefile b/lib/common_test/test/Makefile index e510b74d6a..fae7ce0eb5 100644 --- a/lib/common_test/test/Makefile +++ b/lib/common_test/test/Makefile @@ -76,7 +76,8 @@ MODULES= \ ct_unicode_SUITE \ ct_auto_clean_SUITE \ ct_util_SUITE \ - ct_tc_repeat_SUITE + ct_tc_repeat_SUITE \ + ct_property_test_SUITE ERL_FILES= $(MODULES:%=%.erl) HRL_FILES= test_server_test_lib.hrl diff --git a/lib/common_test/test/ct_property_test_SUITE.erl b/lib/common_test/test/ct_property_test_SUITE.erl new file mode 100644 index 0000000000..1f8c9e08cf --- /dev/null +++ b/lib/common_test/test/ct_property_test_SUITE.erl @@ -0,0 +1,24 @@ +-module(ct_property_test_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). + +all() -> [prop_sort + ]. + +%%% First prepare Config and compile the property tests for the found tool: +init_per_suite(Config) -> + ct_property_test:init_per_suite(Config). + +end_per_suite(Config) -> + Config. + +%%%================================================================ +%%% Test suites +%%% +prop_sort(Config) -> + ct_property_test:quickcheck( + ct_prop:prop_sort(), + Config + ). diff --git a/lib/common_test/test/property_test/ct_prop.erl b/lib/common_test/test/property_test/ct_prop.erl new file mode 100644 index 0000000000..67ab3f3e6b --- /dev/null +++ b/lib/common_test/test/property_test/ct_prop.erl @@ -0,0 +1,18 @@ +-module(ct_prop). +-export([prop_sort/0]). + +-include_lib("common_test/include/ct_property_test.hrl"). + +prop_sort() -> + ?FORALL(UnSorted, list(), + is_sorted(lists:sort(UnSorted)) + ). + +is_sorted([]) -> + true; +is_sorted([_]) -> + true; +is_sorted([H1,H2|SortedTail]) when H1 =< H2 -> + is_sorted([H2|SortedTail]); +is_sorted(_) -> + false. diff --git a/lib/compiler/src/beam_ssa_recv.erl b/lib/compiler/src/beam_ssa_recv.erl index 31b8460525..767242d1e5 100644 --- a/lib/compiler/src/beam_ssa_recv.erl +++ b/lib/compiler/src/beam_ssa_recv.erl @@ -259,6 +259,9 @@ opt_ref_used_is([#b_set{op=call, Vs = update_vars(I, Vs0), opt_ref_used_is(Is, Vs) end; +opt_ref_used_is([#b_set{op=timeout}|_], _Vs) -> + %% Handle "after 0"; wait_timeout has been optimized away. + done; opt_ref_used_is([#b_set{}=I|Is], Vs0) -> Vs = update_vars(I, Vs0), opt_ref_used_is(Is, Vs); diff --git a/lib/compiler/test/receive_SUITE_data/ref_opt/yes_17.erl b/lib/compiler/test/receive_SUITE_data/ref_opt/yes_17.erl new file mode 100644 index 0000000000..421ed825c6 --- /dev/null +++ b/lib/compiler/test/receive_SUITE_data/ref_opt/yes_17.erl @@ -0,0 +1,14 @@ +-module(yes_17). +-export([?MODULE/0,f/0]). + +?MODULE() -> + ok. + +f() -> + Ref = make_ref(), + receive + {Ref,Reply} -> + Reply + after 0 -> + ok + end. diff --git a/lib/compiler/test/receive_SUITE_data/ref_opt/yes_18.erl b/lib/compiler/test/receive_SUITE_data/ref_opt/yes_18.erl new file mode 100644 index 0000000000..92135f53f4 --- /dev/null +++ b/lib/compiler/test/receive_SUITE_data/ref_opt/yes_18.erl @@ -0,0 +1,15 @@ +-module(yes_18). +-export([?MODULE/0,f/2]). + +?MODULE() -> + ok. + +f(Pid, Msg) -> + Ref = make_ref(), + Pid ! Msg, + receive + {Ref,Reply} -> + Reply + after 0 -> + ok + end. diff --git a/lib/crypto/test/crypto_property_test_SUITE.erl b/lib/crypto/test/crypto_property_test_SUITE.erl index 75a3d4872f..18977f733a 100644 --- a/lib/crypto/test/crypto_property_test_SUITE.erl +++ b/lib/crypto/test/crypto_property_test_SUITE.erl @@ -24,8 +24,9 @@ -include_lib("common_test/include/ct.hrl"). -all() -> [encrypt_decrypt__crypto_one_time, - prop__crypto_init_update +all() -> [encrypt_decrypt_one_time, + init_update, + init_update_multi ]. %%% First prepare Config and compile the property tests for the found tool: @@ -38,13 +39,21 @@ end_per_suite(Config) -> %%%================================================================ %%% Test suites %%% -encrypt_decrypt__crypto_one_time(Config) -> +encrypt_decrypt_one_time(Config) -> ct_property_test:quickcheck( crypto_ng_api:prop__crypto_one_time(), Config ). -prop__crypto_init_update(Config) -> + +init_update(Config) -> ct_property_test:quickcheck( crypto_ng_api:prop__crypto_init_update(), Config ). + +init_update_multi(Config) -> + ct_property_test:quickcheck( + crypto_ng_api_stateful:prop__crypto_init_multi(Config), + Config + ). + diff --git a/lib/crypto/test/property_test/crypto_ng_api.erl b/lib/crypto/test/property_test/crypto_ng_api.erl index c3a21b0804..14753a4eba 100644 --- a/lib/crypto/test/property_test/crypto_ng_api.erl +++ b/lib/crypto/test/property_test/crypto_ng_api.erl @@ -23,35 +23,7 @@ -compile(export_all). --proptest(eqc). --proptest([triq,proper]). - --ifndef(EQC). --ifndef(PROPER). --ifndef(TRIQ). -%%-define(EQC,true). --define(PROPER,true). -%%-define(TRIQ,true). --endif. --endif. --endif. - --ifdef(EQC). --include_lib("eqc/include/eqc.hrl"). --define(MOD_eqc,eqc). - --else. --ifdef(PROPER). --include_lib("proper/include/proper.hrl"). --define(MOD_eqc,proper). --else. --ifdef(TRIQ). --define(MOD_eqc,triq). --include_lib("triq/include/triq.hrl"). - --endif. --endif. --endif. +-include_lib("common_test/include/ct_property_test.hrl"). -include("crypto_prop_generators.hrl"). @@ -62,9 +34,12 @@ prop__crypto_one_time() -> numtests(10000, ?FORALL({TextPlain, Cipher, Key, IV}, ?LET(Ciph,cipher(), {text_plain(), Ciph, key(Ciph), iv(Ciph)}), - equal(TextPlain, - full_blocks(TextPlain, Cipher), - decrypt_encrypt_one_time(Cipher, Key, IV, TextPlain)) + begin + R = equal(TextPlain, + full_blocks(TextPlain, Cipher), + decrypt_encrypt_one_time(Cipher, Key, IV, TextPlain)), + prt_inf(Cipher, TextPlain, R) + end ) ). @@ -72,12 +47,25 @@ prop__crypto_init_update() -> numtests(10000, ?FORALL({TextPlain, Cipher, Key, IV}, ?LET(Ciph,cipher(), {text_plain(), Ciph, key(Ciph), iv(Ciph)}), - equal(TextPlain, - full_blocks(TextPlain, Cipher), - decrypt_encrypt_init_update(Cipher, Key, IV, TextPlain)) - ) + begin + R = equal(TextPlain, + full_blocks(TextPlain, Cipher), + decrypt_encrypt_init_update(Cipher, Key, IV, TextPlain)), + prt_inf(Cipher, TextPlain, R) + end) ). +prt_inf(Cipher, TextPlain, R) -> + aggregate(ct_property_test:title("text lengths", + ct_property_test:print_frequency_ranges(), + fun ct:pal/2), + [iolist_size(TextPlain)], + aggregate(ct_property_test:title("ciphers", + ct_property_test:print_frequency(), + fun ct:pal/2), + [Cipher], + R)). + %%%================================================================ %%% Lib diff --git a/lib/crypto/test/property_test/crypto_ng_api_stateful.erl b/lib/crypto/test/property_test/crypto_ng_api_stateful.erl new file mode 100644 index 0000000000..21bbe272dc --- /dev/null +++ b/lib/crypto/test/property_test/crypto_ng_api_stateful.erl @@ -0,0 +1,161 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2004-2017. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% +%% + +-module(crypto_ng_api_stateful). + +-compile(export_all). + +-include_lib("common_test/include/ct_property_test.hrl"). +-include("crypto_prop_generators.hrl"). + +%%%================================================================ +%%% Properties: + +prop__crypto_init_multi(Config) -> + numtests(300, + ?FORALL(Cmds, parallel_commands(?MODULE), + begin + RunResult = run_parallel_commands(?MODULE, Cmds), + ct_property_test:present_result(?MODULE, Cmds, RunResult, Config) + end)). + +%%%================================================================ +-define(call(F,As), {call,?MODULE,F,As}). + +initial_state() -> + #{}. + + +command(S) -> + frequency( + lists:flatten( + [ + {max(0,5-maps:size(S)), + ?call(init_crypto, ?LET(Ciph, cipher(), + [Ciph, key(Ciph), iv(Ciph), block_size(Ciph)]))}, + + [{10, ?call(encrypt, [oneof(refs(S)), binary()]) + } + || refs(S) =/= []], + + [{30, ?call(decrypt, ?LET({Refs,{_,[CT|_]}}, oneof(Sc), + [Refs, CT])) + } + || Sc <- [[R || {_,{_,Cs}} = R <- maps:to_list(S), + Cs =/= []] + ], + Sc =/= [] ], +[] ])). + + +precondition(S, {call,?MODULE,decrypt,[Refs,CrT]}) -> + %% io:format("precondition(1) ~p Args = ~p, S = ~p", [decrypt,[Refs,CrT],S]), + case maps:get(Refs, S) of + {_BlockSize, [CrT|_]} -> + %% io:format(" (sym) true~n",[]), + true; + {_BlockSize, [CrT|_], _} -> + %% io:format(" (dyn) true~n",[]), + true; + _Other -> + %% io:format(" _Other=~p false~n",[_Other]), + false + end; +precondition(_S, {call,?MODULE,_Name,_Args}) -> + %% io:format("precondition ~p Args = ~p, S = ~p~n", [_Name,_Args,_S]), + true. + + +postcondition(_D, _Call, error) -> + %% io:format("postcondition ~p:~p error _Call = ~p~n",[?MODULE,?LINE,_Call]), + false; +postcondition(D, {call,?MODULE,decrypt,[Refs,_CrT]}, Result) -> + #{Refs := {_BlockSize, _CT, PT}} = D, + Size = size(Result), + <<Expect:Size/binary, _/binary>> = PT, + Expect == Result; +postcondition(_D, _Call, _Result) -> + true. + + +symbolic({var,_}) -> true; +symbolic(_) -> false. + + +next_state(S, Refs, {call,?MODULE,init_crypto,[_Cipher, _Key, _IV, BlockSize]}=_C) -> + case symbolic(Refs) of + true -> + S#{Refs => {BlockSize, []} }; + false -> + S#{Refs => {BlockSize, [], <<>>} } + end; + +next_state(S, Res, {call,?MODULE,encrypt,[Refs,Ptxt]}=_C) -> +%% io:format("next_state enrypt Refs = ~p, S = ~p~n", [Refs,S]), + case S of + #{Refs := {BlockSize, Cs}} -> + S#{Refs := {BlockSize, Cs++[Res]}}; + + #{Refs := {BlockSize, Cs,Ps}} -> + S#{Refs := {BlockSize, Cs++[Res], <<Ps/binary,Ptxt/binary>>}} + end; + +next_state(S, Result, {call,?MODULE,decrypt,[Refs,CrT]}=_C) -> +%% io:format("next_state decrypt Refs = ~p, CrT = ~p, S = ~p~n", [Refs,CrT,S]), + case S of + #{Refs := {BlockSize, [CrT|Cs]}} -> + S#{Refs := {BlockSize, Cs}}; + + #{Refs := {BlockSize, [CrT|Cs], Ps0}} -> + Sz = size(Result), + <<Result:Sz/binary,Ps/binary>> = Ps0, + S#{Refs := {BlockSize,Cs,Ps}} + end. + +%%%================================================================ +-define(ok_error(X), + try X of + __R -> %% io:format("~p:~p ok! Result = ~p~n", [?MODULE,?LINE,__R]), + __R + catch + _CC:_EE:_SS -> + io:format("******* ~p:~p ~p ~p~n~p", [?MODULE,?LINE,_CC,_EE,_SS]), + error + end). + +init_crypto(Cipher, Key, IV, _BlockSize) -> + %% io:format("~p:~p init_crypto~n (~p,~n ~p,~n ~p,~n ~p)", [?MODULE,?LINE,Cipher,Key,IV,_BlockSize]), + ?ok_error({ + crypto:crypto_init(Cipher, Key, IV, true), + crypto:crypto_init(Cipher, Key, IV, false) + }). + +encrypt({EncRef,_DecRef}, PlainText) -> + %% io:format("~p:~p encrypt~n (~p,~n ~p) ", [?MODULE,?LINE,EncRef,PlainText]), + ?ok_error(crypto:crypto_update(EncRef, PlainText)). + +decrypt({_EncRef,DecRef}, CT) -> + %% io:format("~p:~p decrypt~n (~p,~n ~p)", [?MODULE,?LINE,DecRef,CT]), + ?ok_error(crypto:crypto_update(DecRef, CT)). + +%%%---------------------------------------------------------------- +refs(S) -> [Refs || {Refs,_} <- maps:to_list(S)]. + diff --git a/lib/crypto/test/property_test/crypto_prop_generators.erl b/lib/crypto/test/property_test/crypto_prop_generators.erl index 5a53a000f0..3a7e9ecb87 100644 --- a/lib/crypto/test/property_test/crypto_prop_generators.erl +++ b/lib/crypto/test/property_test/crypto_prop_generators.erl @@ -21,37 +21,19 @@ -module(crypto_prop_generators). --compile(export_all). - --proptest(eqc). --proptest([triq,proper]). - --ifndef(EQC). --ifndef(PROPER). --ifndef(TRIQ). -%%-define(EQC,true). --define(PROPER,true). -%%-define(TRIQ,true). --endif. --endif. --endif. - --ifdef(EQC). --include_lib("eqc/include/eqc.hrl"). --define(MOD_eqc,eqc). - --else. --ifdef(PROPER). --include_lib("proper/include/proper.hrl"). --define(MOD_eqc,proper). --else. --ifdef(TRIQ). --define(MOD_eqc,triq). --include_lib("triq/include/triq.hrl"). - --endif. --endif. --endif. +-export([text_plain/0, + cipher/0, + key/1, + iv/1, + iolist/0, + mybinary/1, + non_aead_ciphers/0, + block_size/1, + key_length/1, + iv_length/1 + ]). + +-include_lib("common_test/include/ct_property_test.hrl"). %%%================================================================ %%% Generators diff --git a/lib/eunit/src/eunit_surefire.erl b/lib/eunit/src/eunit_surefire.erl index 2b9f82b075..002a069a92 100644 --- a/lib/eunit/src/eunit_surefire.erl +++ b/lib/eunit/src/eunit_surefire.erl @@ -451,6 +451,11 @@ escape_xml([$< | Tail], Acc, ForAttr) -> escape_xml(Tail, [$;, $t, $l, $& | Acc] escape_xml([$> | Tail], Acc, ForAttr) -> escape_xml(Tail, [$;, $t, $g, $& | Acc], ForAttr); escape_xml([$& | Tail], Acc, ForAttr) -> escape_xml(Tail, [$;, $p, $m, $a, $& | Acc], ForAttr); escape_xml([$" | Tail], Acc, true) -> escape_xml(Tail, [$;, $t, $o, $u, $q, $& | Acc], true); % " +escape_xml([Char | Tail], Acc, ForAttr) when + Char == $\n; Char == $\r; Char == $\t -> escape_xml(Tail, [Char | Acc], ForAttr); +%% Strip C0 control codes which are not allowed in XML 1.0 +escape_xml([Char | Tail], Acc, ForAttr) when + 0 =< Char, Char =< 31 -> escape_xml(Tail, Acc, ForAttr); escape_xml([Char | Tail], Acc, ForAttr) when is_integer(Char) -> escape_xml(Tail, [Char | Acc], ForAttr). %% the input may be utf8 or latin1; the resulting list is unicode diff --git a/lib/eunit/test/Makefile b/lib/eunit/test/Makefile index b6ece61b43..7c1e56c867 100644 --- a/lib/eunit/test/Makefile +++ b/lib/eunit/test/Makefile @@ -22,6 +22,7 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk MODULES = \ eunit_SUITE \ + tc0 \ tlatin \ tutf8 diff --git a/lib/eunit/test/eunit_SUITE.erl b/lib/eunit/test/eunit_SUITE.erl index 63bdc6c334..b9f4ea4557 100644 --- a/lib/eunit/test/eunit_SUITE.erl +++ b/lib/eunit/test/eunit_SUITE.erl @@ -21,14 +21,16 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, - app_test/1,appup_test/1,eunit_test/1,surefire_utf8_test/1,surefire_latin_test/1]). + app_test/1,appup_test/1,eunit_test/1,surefire_utf8_test/1,surefire_latin_test/1, + surefire_c0_test/1]). -include_lib("common_test/include/ct.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [app_test, appup_test, eunit_test, surefire_utf8_test, surefire_latin_test]. + [app_test, appup_test, eunit_test, surefire_utf8_test, surefire_latin_test, + surefire_c0_test]. groups() -> []. @@ -65,11 +67,24 @@ surefire_utf8_test(Config) when is_list(Config) -> check_surefire(tutf8), ok. +surefire_c0_test(Config) when is_list(Config) -> + ok = file:set_cwd(proplists:get_value(priv_dir, Config, ".")), + Chars = check_surefire(tc0), + %% Check that these characters were not stripped + true = lists:member($\n, Chars), + true = lists:member($\r, Chars), + true = lists:member($\t, Chars), + ok. + check_surefire(Module) -> File = "TEST-"++atom_to_list(Module)++".xml", file:delete(File), % ignore test result, some fail on purpose eunit:test(Module, [{report,{eunit_surefire,[{dir,"."}]}}]), {ok, Bin} = file:read_file(File), - [_|_] = unicode:characters_to_list(Bin, unicode), - ok.
\ No newline at end of file + Chars = unicode:characters_to_list(Bin, unicode), + %% Check that unicode decoding succeeded + [_|_] = Chars, + %% Check that file is valid XML + xmerl_scan:file(File), + Chars. diff --git a/lib/eunit/test/tc0.erl b/lib/eunit/test/tc0.erl new file mode 100644 index 0000000000..8c90633fc8 --- /dev/null +++ b/lib/eunit/test/tc0.erl @@ -0,0 +1,14 @@ +-module(tc0). + +-include_lib("eunit/include/eunit.hrl"). + +'c0_bad_output_test_'() -> + [{integer_to_list(C), fun() -> io:format("'~c'", [C]) end} + || C <- lists:seq(0, 31)]. + +'c0_bad_description_test_'() -> + [{[C], fun() -> ok end} + || C <- lists:seq(0, 31)]. + +'c0_bad_name__test'() -> + ok. diff --git a/lib/kernel/doc/src/logger_cookbook.xml b/lib/kernel/doc/src/logger_cookbook.xml index 30209f0a90..5fbd9ff2d4 100644 --- a/lib/kernel/doc/src/logger_cookbook.xml +++ b/lib/kernel/doc/src/logger_cookbook.xml @@ -101,7 +101,7 @@ Handler configuration: type: standard_io </code> <p>You can also print the configuration of a specific handler using - <seealso marker="logger#i-1"><c>logger:i(HanderName)</c></seealso>, + <seealso marker="logger#i-1"><c>logger:i(HandlerName)</c></seealso>, or fetch the configuration using <seealso marker="logger#get_handler_config-0"><c>logger:get_handler_config()</c></seealso>, or <seealso marker="logger#get_handler_config-1"><c>logger:get_handler_config(HandlerName)</c></seealso> @@ -141,12 +141,12 @@ Eshell V10.5.3 (abort with ^G) <section> <title>Configure Logger formatter</title> <p>In order to fit better into your existing logging infrastructure Logger can - format its logging messages any way to want to. Either you can use the built-in + format its logging messages any way you want to. Either you can use the built-in formatter, or you can build your own.</p> <section> <title>Single line configuration</title> <p>Since single line logging is the default of the built-in formatter you only have to - provide the empty map as the coniguration. The example below uses the <c>sys.config</c> + provide the empty map as the configuration. The example below uses the <c>sys.config</c> to change the formatter configuration.</p> <code type="erl-repl">$ cat sys.config [{kernel, @@ -174,7 +174,7 @@ ok </section> </section> <section> - <title>Add file and lineno to log entries</title> + <title>Add file and line number to log entries</title> <p>You can change what is printed to the log by using the formatter template:</p> <code type="erl-repl">$ cat sys.config [{kernel, @@ -274,7 +274,7 @@ ok 3> logger:set_primary_config(level, all). ok </code> - <p>It is important that you do not raise the primare log level before adjusting + <p>It is important that you do not raise the primary log level before adjusting the default handler's level as otherwise your standard out may be flooded by debug log messages.</p> <section> @@ -300,7 +300,7 @@ ok <title>Logging</title> <section> <title>What to log and how</title> - <p>The simplest way to do log something is by using the Logger macros and + <p>The simplest way to log something is by using the Logger macros and give a report to the macro. For example if you want to log an error:</p> <code type="erl">?LOG_ERROR(#{ what => http_error, status => 418, src => ClientIP, dst => ServerIP }).</code> <p>This will print the following in the default log:</p> @@ -309,7 +309,7 @@ ok src: {8,8,8,8} status: 418 what: http_error</code> - <p>or the below if you use a single line formatter</p> + <p>or the below if you use a single line formatter:</p> <code>2019-10-10T12:14:11.921843+02:00 error: dst: {8,8,4,4}, src: {8,8,8,8}, status: 418, what: http_error</code> <section> <title>See also</title> @@ -321,7 +321,7 @@ ok <section> <title>Report call-backs and printing of events</title> <p>If you want to do structured logging, but still want to have some control - of how the final log message is structured you can give a <c>report_cb</c> + of how the final log message is formatted you can give a <c>report_cb</c> as part of the metadata with your log event.</p> <code type="erl">ReportCB = fun(#{ what := What, status := Status, src := Src, dst := Dst }) -> {ok, #hostent{ h_name = SrcName }} = inet:gethostbyaddr(Src), @@ -381,7 +381,7 @@ logger:debug("Debug should be logged"). <p>There is a bit of setup needed to allow filters to decide whether a specific process should be allowed to log. This is because the default primary log level is notice and it is enforced before the primary filters. So in order for the pid filter to be useful - have to raise the primary log level to <c>all</c> and then add a level filter that + we have to raise the primary log level to <c>all</c> and then add a level filter that only lets certain messages at or greater than notice through. When the setup is done, it is simple to add a filter that allows a certain pid through. </p> diff --git a/lib/kernel/doc/src/net.xml b/lib/kernel/doc/src/net.xml index 054368250a..d60e1af311 100644 --- a/lib/kernel/doc/src/net.xml +++ b/lib/kernel/doc/src/net.xml @@ -158,10 +158,10 @@ </func> <func> - <name name="getifaddrs" arity="0" since="@OTP-16212@"/> - <name name="getifaddrs" arity="1" clause_i="1" since="@OTP-16212@"/> - <name name="getifaddrs" arity="1" clause_i="2" since="@OTP-16212@"/> - <name name="getifaddrs" arity="2" since="@OTP-16212@"/> + <name name="getifaddrs" arity="0" since="OTP @OTP-16212@"/> + <name name="getifaddrs" arity="1" clause_i="1" since="OTP @OTP-16212@"/> + <name name="getifaddrs" arity="1" clause_i="2" since="OTP @OTP-16212@"/> + <name name="getifaddrs" arity="2" since="OTP @OTP-16212@"/> <fsummary>Get interface addresses.</fsummary> <desc> <p>Get interface addresses.</p> diff --git a/lib/kernel/test/init_SUITE.erl b/lib/kernel/test/init_SUITE.erl index a0154b2694..9d35611d93 100644 --- a/lib/kernel/test/init_SUITE.erl +++ b/lib/kernel/test/init_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2018. All Rights Reserved. +%% Copyright Ericsson AB 1996-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -68,7 +68,7 @@ end_per_group(_GroupName, Config) -> Config. -init_per_testcase(Func, Config) -> +init_per_testcase(_Func, Config) -> Config. end_per_testcase(_Func, _Config) -> @@ -368,7 +368,8 @@ restart(Config) when is_list(Config) -> io:format("SysProcs0=~p~n", [SysProcs0]), [InitPid, PurgerPid, LitCollectorPid, DirtySigNPid, DirtySigHPid, DirtySigMPid, - PrimFilePid] = SysProcs0, + PrimFilePid, + ESockRegPid] = SysProcs0, InitPid = rpc:call(Node, erlang, whereis, [init]), PurgerPid = rpc:call(Node, erlang, whereis, [erts_code_purger]), Procs = rpc:call(Node, erlang, processes, []), @@ -387,7 +388,8 @@ restart(Config) when is_list(Config) -> io:format("SysProcs1=~p~n", [SysProcs1]), [InitPid1, PurgerPid1, LitCollectorPid1, DirtySigNPid1, DirtySigHPid1, DirtySigMPid1, - PrimFilePid1] = SysProcs1, + PrimFilePid1, + ESockRegPid1] = SysProcs1, %% Still the same init process! InitPid1 = rpc:call(Node, erlang, whereis, [init]), @@ -417,6 +419,10 @@ restart(Config) when is_list(Config) -> PrimFileP = pid_to_list(PrimFilePid), PrimFileP = pid_to_list(PrimFilePid1), + %% and same socket_registry helper process! + ESockRegP = pid_to_list(ESockRegPid), + ESockRegP = pid_to_list(ESockRegPid1), + NewProcs0 = rpc:call(Node, erlang, processes, []), NewProcs = NewProcs0 -- SysProcs1, case check_processes(NewProcs, MaxPid) of @@ -444,7 +450,8 @@ restart(Config) when is_list(Config) -> dirty_sig_handler_normal, dirty_sig_handler_high, dirty_sig_handler_max, - prim_file}). + prim_file, + socket_registry}). find_system_processes() -> find_system_procs(processes(), #sys_procs{}). @@ -456,7 +463,8 @@ find_system_procs([], SysProcs) -> SysProcs#sys_procs.dirty_sig_handler_normal, SysProcs#sys_procs.dirty_sig_handler_high, SysProcs#sys_procs.dirty_sig_handler_max, - SysProcs#sys_procs.prim_file]; + SysProcs#sys_procs.prim_file, + SysProcs#sys_procs.socket_registry]; find_system_procs([P|Ps], SysProcs) -> case process_info(P, [initial_call, priority]) of [{initial_call,{erl_init,start,2}},_] -> @@ -483,6 +491,9 @@ find_system_procs([P|Ps], SysProcs) -> [{initial_call,{prim_file,start,0}},_] -> undefined = SysProcs#sys_procs.prim_file, find_system_procs(Ps, SysProcs#sys_procs{prim_file = P}); + [{initial_call,{socket_registry,start,0}},_] -> + undefined = SysProcs#sys_procs.socket_registry, + find_system_procs(Ps, SysProcs#sys_procs{socket_registry = P}); _ -> find_system_procs(Ps, SysProcs) end. diff --git a/lib/megaco/src/app/megaco.erl b/lib/megaco/src/app/megaco.erl index 9ed042401b..2c96ea25d7 100644 --- a/lib/megaco/src/app/megaco.erl +++ b/lib/megaco/src/app/megaco.erl @@ -89,7 +89,16 @@ -export([format_versions/1]). %% Internal --export([format_timestamp/1]). +-export([ + %% These are used both for debugging (verbosity printouts) + %% and other such "utility" operations (and testing). + format_timestamp/1, format_timestamp/2, + format_short_timestamp/1, format_short_timestamp/2, + format_long_timestamp/1, format_long_timestamp/2, + formated_timestamp/0, + formated_short_timestamp/0, + formated_long_timestamp/0 + ]). %% This is for XREF -deprecated([{format_versions, 1, eventually}]). @@ -1005,14 +1014,96 @@ print_trace(Fd, Trace) -> "~n", [Trace]). -format_timestamp({_N1, _N2, N3} = Now) -> - {Date, Time} = calendar:now_to_datetime(Now), +%% --------------------------------------------------------------------------- +%% # formated_timstamp/0, formated_timstamp/1 +%% # format_short_timstamp/0, format_short_timstamp/1 +%% # format_long_timstamp/0, format_long_timstamp/1 +%% +%% Create a formatted timestamp. Short means that it will not include +%% the date in the formatted timestamp. Also it will only include millis. +%% --------------------------------------------------------------------------- + +formated_timestamp() -> + formated_long_timestamp(). + +formated_short_timestamp() -> + format_short_timestamp(os:timestamp()). + +formated_long_timestamp() -> + format_long_timestamp(os:timestamp()). + + +%% --------------------------------------------------------------------------- +%% # format_timstamp/1, format_timstamp/2 +%% # format_short_timstamp/1, format_short_timstamp/2 +%% # format_long_timstamp/1, format_long_timstamp/2 +%% +%% Formats the provided timestamp. Short means that it will not include +%% the date in the formatted timestamp. +%% --------------------------------------------------------------------------- + +-spec format_timestamp(Now :: erlang:timestamp()) -> + string(). + +format_timestamp(Now) -> + format_long_timestamp(Now). + +-spec format_short_timestamp(Now :: erlang:timestamp()) -> + string(). + +format_short_timestamp(Now) -> + N2T = fun(N) -> calendar:now_to_local_time(N) end, + format_timestamp(short, Now, N2T). + +-spec format_long_timestamp(Now :: erlang:timestamp()) -> + string(). + +format_long_timestamp(Now) -> + N2T = fun(N) -> calendar:now_to_local_time(N) end, + format_timestamp(long, Now, N2T). + +-spec format_timestamp(Now :: erlang:timestamp(), + N2T :: function()) -> + string(). + +format_timestamp(Now, N2T) when is_tuple(Now) andalso is_function(N2T) -> + format_long_timestamp(Now, N2T). + +-spec format_short_timestamp(Now :: erlang:timestamp(), + N2T :: function()) -> + string(). + +format_short_timestamp(Now, N2T) when is_tuple(Now) andalso is_function(N2T) -> + format_timestamp(short, Now, N2T). + +-spec format_long_timestamp(Now :: erlang:timestamp(), + N2T :: function()) -> + string(). + +format_long_timestamp(Now, N2T) when is_tuple(Now) andalso is_function(N2T) -> + format_timestamp(long, Now, N2T). + +format_timestamp(Format, {_N1, _N2, N3} = Now, N2T) -> + {Date, Time} = N2T(Now), + do_format_timestamp(Format, Date, Time, N3). + +do_format_timestamp(short, _Date, Time, N3) -> + do_format_short_timestamp(Time, N3); +do_format_timestamp(long, Date, Time, N3) -> + do_format_long_timestamp(Date, Time, N3). + +do_format_long_timestamp(Date, Time, N3) -> {YYYY,MM,DD} = Date, {Hour,Min,Sec} = Time, FormatDate = - io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w 4~w", - [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]), + io_lib:format("~.4w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w.~.3.0w", + [YYYY, MM, DD, Hour, Min, Sec, N3 div 1000]), lists:flatten(FormatDate). +do_format_short_timestamp(Time, N3) -> + {Hour,Min,Sec} = Time, + FormatDate = + io_lib:format("~.2.0w:~.2.0w:~.2.0w.~.3.0w", + [Hour, Min, Sec, N3 div 1000]), + lists:flatten(FormatDate). - diff --git a/lib/megaco/test/Makefile b/lib/megaco/test/Makefile index b4e31765b8..8a595b88af 100644 --- a/lib/megaco/test/Makefile +++ b/lib/megaco/test/Makefile @@ -63,17 +63,6 @@ SOURCE = $(HRL_FILES) $(ERL_FILES) TARGET_FILES = $(MODULES:%=%.$(EMULATOR)) -APP_CASES = app appup - -CODEC_CASES = codec1 codec2 codec3a codec3b codec3c - -MISC_CASES = tid sdp dm conf udp tcp ex timer flex - -OP_CASES = mess mib mreq pending trans actions load - -ALL_CASES = $(APP_CASES) $(CODEC_CASES) $(MISC_CASES) $(OP_CASES) - - EMAKEFILE = Emakefile MAKE_EMAKE = $(wildcard $(ERL_TOP)/make/make_emakefile) @@ -205,150 +194,14 @@ help: @echo " clean " @echo " Remove all targets. " @echo "" - @echo " test" - @echo " Run the entire $(APPLICATION) test-suite. " - @echo "" - @echo " app" - @echo " Run the $(APPLICATION) application sub-test-suite. " - @echo "" - @echo " appup" - @echo " Run the $(APPLICATION) application upgrade (appup) sub-test-suite. " - @echo "" - @echo " conf" - @echo " Run the $(APPLICATION) config sub-test-suite. " - @echo " Checks various aspects of the megaco configuration. " - @echo "" - @echo " codec" - @echo " Run the $(APPLICATION) codec sub-test-suite(s). " - @echo "" - @echo " flex" - @echo " Run the $(APPLICATION) flex-scanner sub-test-suite. " - @echo " This sub-test-suite does not test the function of the scanner itself" - @echo " (that is done by the codec sub-test-suite(s)), instead, this" - @echo " sub-test-suite tests the *handling* of the flex-scanner linked-in driver." - @echo "" - @echo " dm" - @echo " Run the $(APPLICATION) digit-map sub-test-suite. " - @echo "" - @echo " tid" - @echo " Run the $(APPLICATION) binary term-id sub-test-suite. " - @echo "" - @echo " sdp" - @echo " Run the $(APPLICATION) SDP sub-test-suite. " - @echo "" - @echo " action" - @echo " Run the $(APPLICATION) actions sub-test-suite. " - @echo " Actions are building blocks of a $(APPLICATION) message. " - @echo " This sub-test-suite attempts to perform tests related to this, " - @echo " using all the supported codecs. " - @echo "" - @echo " mess" - @echo " Run the $(APPLICATION) message sub-test-suite" - @echo " This is basic message processing test-cases" - @echo "" - @echo " trans" - @echo " Run the $(APPLICATION) transaction sender sub-test-suite" - @echo " This is basic message processing for a megaco application " - @echo " configured to be using the transaction sender. " - @echo "" - @echo " mib" - @echo " Run the $(APPLICATION) $(APPLICATION)-mib sub-test-suite" - @echo " Tests related to the basic support for the $(APPLICATION) mib" - @echo " primarily counters" - @echo "" - @echo " mreq" - @echo " Run the $(APPLICATION) mreq sub-test-suite" - @echo " This is yet another sub-test-suite with message processing " - @echo " test-cases. " - @echo "" - @echo " pending" - @echo " Run the $(APPLICATION) pending-limit sub-test-suite" - @echo "" - @echo " udp" - @echo " Run the $(APPLICATION) UDP transport component sub-test-suite" - @echo "" - @echo " tcp" - @echo " Run the $(APPLICATION) TCP transport component sub-test-suite" - @echo "" - @echo " load" - @echo " Run the $(APPLICATION) load sub-test-suite" - @echo " This sub-test-suite performs load test test-cases using " - @echo " \"high\" message traffic. " - @echo "" - @echo " segment" - @echo " Run the $(APPLICATION) message segmentation sub-test-suite" - @echo " If using the UDP transport protocol, it is possible to send " - @echo " and receive segmented megaco replies (in version 3 of the protocol) " - @echo " This sub-test-suite tests this feature. " - @echo "" - @echo " timer" - @echo " Run the $(APPLICATION) timer sub-test-suite" - @echo " Basic test-suite for the megaco-timers" - @echo "" - @echo " ex" - @echo " Run the $(APPLICATION) example sub-test-suite" - @echo " The $(APPLICATION) application contains one example," - @echo " this is a sub-test-suite based on the code," - @echo "" # ---------------------------------------------------- # Special Targets # ---------------------------------------------------- -aall: make - @echo "make sure epmd is new" - @epmd -kill > /dev/null - @echo "Running all app sub-suites separatelly" - @for i in $(APP_CASES); do \ - echo "SUITE: $$i"; \ - clearmake -V $$i > $$i.log; \ - done - echo "done" - -call: make - @echo "make sure epmd is new" - @epmd -kill > /dev/null - @echo "Running all codec sub-suites separatelly" - @for i in $(CODEC_CASES); do \ - echo "SUITE: $$i"; \ - clearmake -V $$i > $$i.log; \ - done - -mall: make - @echo "make sure epmd is new" - @epmd -kill > /dev/null - @echo "Running all misc sub-suites separatelly" - @for i in $(MISC_CASES); do \ - echo "SUITE: $$i"; \ - clearmake -V $$i > $$i.log; \ - done - -oall: make - @echo "make sure epmd is new" - @epmd -kill > /dev/null - @echo "Running all operation sub-suites separatelly" - @for i in $(OP_CASES); do \ - echo "SUITE: $$i"; \ - clearmake -V $$i > $$i.log; \ - done - -all: make - @echo "make sure epmd is new" - @epmd -kill > /dev/null - @echo "Running all sub-suites separatelly" - @for i in $(ALL_CASES); do \ - echo "SUITE: $$i"; \ - clearmake -V $$i > $$i.log; \ - done - make: targets -test: make - $(MERL) $(ARGS) -sname megaco_test $(ERL_PATH) \ - -s megaco_test_lib t $(SUITE) \ - $(MAYBE_ESTOP) - utest: make $(MERL) $(ARGS) -sname megaco_utest $(ERL_PATH) \ $(MAYBE_ETVIEW) \ @@ -363,305 +216,120 @@ ftest: make decode_compact_prof1: make $(MERL) $(ARGS) -sname megaco_profile_decode_compact $(ERL_PATH) \ - -s megaco_codec_v1_test profile_decode_compact_text_messages \ + -s megaco_codec_v1_SUITE profile_decode_compact_text_messages \ $(ESTOP) decode_compact_flex_prof1: make $(MERL) $(ARGS) -sname megaco_profile_decode_compact_flex $(ERL_PATH) \ - -s megaco_codec_v1_test profile_decode_compact_flex_text_messages \ + -s megaco_codec_v1_SUITE profile_decode_compact_flex_text_messages \ $(ESTOP) decode_compact_prof2: make $(MERL) $(ARGS) -sname megaco_profile_decode_compact $(ERL_PATH) \ - -s megaco_codec_v2_test profile_decode_compact_text_messages \ + -s megaco_codec_v2_SUITE profile_decode_compact_text_messages \ $(ESTOP) decode_compact_flex_prof2: make $(MERL) $(ARGS) -sname megaco_profile_decode_compact_flex $(ERL_PATH) \ - -s megaco_codec_v2_test profile_decode_compact_flex_text_messages \ + -s megaco_codec_v2_SUITE profile_decode_compact_flex_text_messages \ $(ESTOP) decode_pretty_prof1: make $(MERL) $(ARGS) -sname megaco_profile_decode_pretty $(ERL_PATH) \ - -s megaco_codec_v1_test profile_decode_pretty_text_messages \ + -s megaco_codec_v1_SUITE profile_decode_pretty_text_messages \ $(ESTOP) decode_pretty_flex_prof1: make $(MERL) $(ARGS) -sname megaco_profile_decode_pretty_flex $(ERL_PATH) \ - -s megaco_codec_v1_test profile_decode_pretty_flex_text_messages \ + -s megaco_codec_v1_SUITE profile_decode_pretty_flex_text_messages \ $(ESTOP) decode_pretty_prof2: make $(MERL) $(ARGS) -sname megaco_profile_decode_pretty $(ERL_PATH) \ - -s megaco_codec_v2_test profile_decode_pretty_text_messages \ + -s megaco_codec_v2_SUITE profile_decode_pretty_text_messages \ $(ESTOP) decode_pretty_flex_prof2: make $(MERL) $(ARGS) -sname megaco_profile_decode_pretty_flex $(ERL_PATH) \ - -s megaco_codec_v2_test profile_decode_pretty_flex_text_messages \ + -s megaco_codec_v2_SUITE profile_decode_pretty_flex_text_messages \ $(ESTOP) encode_compact_prof1: make $(MERL) $(ARGS) -sname megaco_profile_encode_compact $(ERL_PATH) \ - -s megaco_codec_v1_test profile_encode_compact_text_messages \ + -s megaco_codec_v1_SUITE profile_encode_compact_text_messages \ $(ESTOP) encode_compact_prof2: make $(MERL) $(ARGS) -sname megaco_profile_encode_compact $(ERL_PATH) \ - -s megaco_codec_v2_test profile_encode_compact_text_messages \ + -s megaco_codec_v2_SUITE profile_encode_compact_text_messages \ $(ESTOP) encode_pretty_prof1: make $(MERL) $(ARGS) -sname megaco_profile_encode_pretty $(ERL_PATH) \ - -s megaco_codec_v1_test profile_encode_pretty_text_messages \ + -s megaco_codec_v1_SUITE profile_encode_pretty_text_messages \ $(ESTOP) encode_pretty_prof2: make $(MERL) $(ARGS) -sname megaco_profile_encode_pretty $(ERL_PATH) \ - -s megaco_codec_v2_test profile_encode_pretty_text_messages \ + -s megaco_codec_v2_SUITE profile_encode_pretty_text_messages \ $(ESTOP) ########################## -tickets: make - $(MERL) $(ARGS) -sname megaco_tickets $(ERL_PATH) \ - -s megaco_test_lib tickets $(SUITE) \ - $(ESTOP) - -app: make - $(MERL) $(ARGS) -sname megaco_app $(ERL_PATH) \ - -s megaco_test_lib t megaco_app_test \ - $(ESTOP) - -appup: make - $(MERL) $(ARGS) -sname megaco_appup $(ERL_PATH) \ - -s megaco_test_lib t megaco_appup_test \ - $(ESTOP) - -conf: make - $(MERL) $(ARGS) -sname megaco_config $(ERL_PATH) \ - -s megaco_test_lib t megaco_config_test \ - $(ESTOP) - - -########################## - -codec: make - $(MERL) $(ARGS) -sname megaco_codec $(ERL_PATH) \ - -s megaco_test_lib t megaco_codec_test \ - $(MAYBE_ESTOP) - -codec1: make - $(MERL) $(ARGS) -sname megaco_codec1 $(ERL_PATH) \ - -s megaco_test_lib t megaco_codec_v1_test \ - $(MAYBE_ESTOP) - -codec1_tickets: make - $(MERL) $(ARGS) -sname megaco_codec1_tickets $(ERL_PATH) \ - -s megaco_codec_v1_test tickets \ - $(ESTOP) - -codec2: make - $(MERL) $(ARGS) -sname megaco_codec2 $(ERL_PATH) \ - -s megaco_test_lib t megaco_codec_v2_test \ - $(MAYBE_ESTOP) - -codec2_tickets: make - $(MERL) $(ARGS) -sname megaco_codec2_tickets $(ERL_PATH) \ - -s megaco_codec_v2_test tickets \ - $(ESTOP) - -codec3a: make - $(MERL) $(ARGS) -sname megaco_codec3a $(ERL_PATH) \ - -s megaco_test_lib t megaco_codec_prev3a_test \ - $(MAYBE_ESTOP) - -codec3a_tickets: make - $(MERL) $(ARGS) -sname megaco_codec3a_tickets $(ERL_PATH) \ - -s megaco_codec_prev3a_test tickets \ - $(ESTOP) - -codec3b: make - $(MERL) $(ARGS) -sname megaco_codec3b $(ERL_PATH) \ - -s megaco_test_lib t megaco_codec_prev3b_test \ - $(MAYBE_ESTOP) - -codec3b_tickets: make - $(MERL) $(ARGS) -sname megaco_codec3b_tickets $(ERL_PATH) \ - -s megaco_codec_prev3b_test tickets \ - $(ESTOP) - -codec3c: make - $(MERL) $(ARGS) -sname megaco_codec3c $(ERL_PATH) \ - -s megaco_test_lib t megaco_codec_prev3c_test \ - $(MAYBE_ESTOP) - -codec3c_tickets: make - $(MERL) $(ARGS) -sname megaco_codec3c_tickets $(ERL_PATH) \ - -s megaco_codec_prev3c_test tickets \ - $(ESTOP) - -codec3: make - $(MERL) $(ARGS) -sname megaco_codec3 $(ERL_PATH) \ - -s megaco_test_lib t megaco_codec_v3_test \ - $(MAYBE_ESTOP) - -codec3_tickets: make - $(MERL) $(ARGS) -sname megaco_codec3_tickets $(ERL_PATH) \ - -s megaco_codec_v3_test tickets \ - $(ESTOP) - -codecm: make - $(MERL) $(ARGS) -sname megaco_codec1 $(ERL_PATH) \ - -s megaco_test_lib t megaco_codec_mini_test \ - $(MAYBE_ESTOP) - - -########################## - time1: make $(MERL) $(ARGS) -sname megaco_time1 $(ERL_PATH) \ - -run megaco_codec_v1_test tt $(TT_DIR) \ + -run megaco_codec_v1_SUITE tt $(TT_DIR) \ $(ESTOP) time2: make $(MERL) $(ARGS) -sname megaco_time2 $(ERL_PATH) \ - -run megaco_codec_v2_test tt $(TT_DIR) \ + -run megaco_codec_v2_SUITE tt $(TT_DIR) \ $(ESTOP) timeo1: make $(MERL) $(ARGS) -sname megaco_timeo1 $(ERL_PATH) \ - -run megaco_codec_v1_test tt_official $(TT_DIR) \ + -run megaco_codec_v1_SUITE tt_official $(TT_DIR) \ $(ESTOP) timeo2: make $(MERL) $(ARGS) -sname megaco_timeo2 $(ERL_PATH) \ - -run megaco_codec_v2_test tt_official $(TT_DIR) \ + -run megaco_codec_v2_SUITE tt_official $(TT_DIR) \ $(ESTOP) timeo3: make $(MERL) $(ARGS) -sname megaco_timeo3 $(ERL_PATH) \ - -run megaco_codec_v3_test tt_official $(TT_DIR) \ + -run megaco_codec_v3_SUITE tt_official $(TT_DIR) \ $(ESTOP) timet1: make $(MERL) $(ARGS) -sname megaco_timet1 $(ERL_PATH) \ - -run megaco_codec_v1_test tt_texts $(TT_DIR) \ + -run megaco_codec_v1_SUITE tt_texts $(TT_DIR) \ $(ESTOP) timet2: make $(MERL) $(ARGS) -sname megaco_timet2 $(ERL_PATH) \ - -run megaco_codec_v2_test tt_texts $(TT_DIR) \ + -run megaco_codec_v2_SUITE tt_texts $(TT_DIR) \ $(ESTOP) timet3: make $(MERL) $(ARGS) -sname megaco_timet3 $(ERL_PATH) \ - -run megaco_codec_v3_test tt_texts $(TT_DIR) \ + -run megaco_codec_v3_SUITE tt_texts $(TT_DIR) \ $(ESTOP) timeb1: make $(MERL) $(ARGS) -sname megaco_timeb1 $(ERL_PATH) \ - -run megaco_codec_v1_test tt_bins $(TT_DIR) \ + -run megaco_codec_v1_SUITE tt_bins $(TT_DIR) \ $(ESTOP) timeb2: make $(MERL) $(ARGS) -sname megaco_timeb2 $(ERL_PATH) \ - -run megaco_codec_v2_test tt_bins $(TT_DIR) \ + -run megaco_codec_v2_SUITE tt_bins $(TT_DIR) \ $(ESTOP) timeb3: make $(MERL) $(ARGS) -sname megaco_timeb3 $(ERL_PATH) \ - -run megaco_codec_v3_test tt_bins $(TT_DIR) \ - $(ESTOP) - - -########################## - -flex: make - $(MERL) $(ARGS) -sname megaco_flex $(ERL_PATH) \ - -s megaco_test_lib t megaco_flex_test \ - $(ESTOP) - -dm: make - $(MERL) $(ARGS) -sname megaco_dm $(ERL_PATH) \ - -s megaco_test_lib t megaco_digit_map_test \ - $(ESTOP) - -tid: make - $(MERL) $(ARGS) -sname megaco_tid $(ERL_PATH) \ - -s megaco_test_lib t megaco_binary_term_id_test \ - $(ESTOP) - -sdp: make - $(MERL) $(ARGS) -sname megaco_sdp $(ERL_PATH) \ - -s megaco_test_lib t megaco_sdp_test \ - $(MAYBE_ESTOP) - -actions: make - $(MERL) $(ARGS) -sname megaco_actions $(ERL_PATH) \ - -s megaco_test_lib t megaco_actions_test \ - $(MAYBE_ESTOP) - -mess: make - $(MERL) $(ARGS) -sname megaco_mess $(ERL_PATH) \ - $(MAYBE_ETVIEW) \ - -s megaco_test_lib t megaco_mess_test \ - $(ESTOP) - -trans: make - $(MERL) $(ARGS) -sname megaco_trans $(ERL_PATH) \ - -s megaco_test_lib t megaco_trans_test \ - $(MAYBE_ESTOP) - -mib: make - $(MERL) $(ARGS) -sname megaco_mib $(ERL_PATH) \ - -s megaco_test_lib t megaco_mib_test \ - $(MAYBE_ESTOP) - -mreq: make - $(MERL) $(ARGS) -sname megaco_mreq $(ERL_PATH) \ - -s megaco_test_lib t megaco_mreq_test \ - $(MAYBE_ESTOP) - -pending: make - $(MERL) $(ARGS) -sname megaco_pending $(ERL_PATH) \ - -s megaco_test_lib t megaco_pending_limit_test \ - $(MAYBE_ESTOP) - -pl: make - $(MERL) $(ARGS) -sname megaco_pl $(ERL_PATH) \ - -s megaco_test_lib t megaco_pending_limit_test \ - $(MAYBE_ESTOP) - -udp: make - $(MERL) $(ARGS) -sname megaco_pl $(ERL_PATH) \ - -s megaco_test_lib t megaco_udp_test \ - $(MAYBE_ESTOP) - -tcp: make - $(MERL) $(ARGS) -sname megaco_pl $(ERL_PATH) \ - -s megaco_test_lib t megaco_tcp_test \ - $(MAYBE_ESTOP) - -load: make - $(MERL) $(ARGS) -sname megaco_load $(ERL_PATH) \ - -s megaco_test_lib t megaco_load_test \ - $(MAYBE_ESTOP) - -ex: make - $(MERL) $(ARGS) -sname megaco_ex $(ERL_PATH) \ - -s megaco_test_lib t megaco_examples_test \ - $(MAYBE_ESTOP) - -segment: make - $(MERL) $(ARGS) -sname megaco_segment $(ERL_PATH) \ - -s megaco_test_lib t megaco_segment_test \ - $(ESTOP) - -timer: make - $(MERL) $(ARGS) -sname megaco_timer $(ERL_PATH) \ - -s megaco_test_lib t megaco_timer_test \ + -run megaco_codec_v3_SUITE tt_bins $(TT_DIR) \ $(ESTOP) @@ -669,69 +337,69 @@ timer: make gnuplot_gif: make $(MERL) $(ARGS) -sname megaco_gnuplot_gif $(ERL_PATH) \ - -s megaco_call_flow_test gnuplot_gif \ + -s megaco_call_flow_SUITE gnuplot_gif \ $(ESTOP) display_v1: make $(MERL) $(ARGS) -sname megaco_display_text_msgs_v1 $(ERL_PATH) \ - -s megaco_codec_v1_test display_text_messages \ + -s megaco_codec_v1_SUITE display_text_messages \ $(ESTOP) generate_v1: make $(MERL) $(ARGS) -sname megaco_generate_text_msgs_v1 $(ERL_PATH) \ - -s megaco_codec_v1_test generate_text_messages \ + -s megaco_codec_v1_SUITE generate_text_messages \ $(ESTOP) display_v2: make $(MERL) $(ARGS) -sname megaco_display_text_msgs_v2 $(ERL_PATH) \ - -s megaco_codec_v2_test display_text_messages \ + -s megaco_codec_v2_SUITE display_text_messages \ $(ESTOP) generate_v2: make $(MERL) $(ARGS) -sname megaco_generate_text_msgs_v2 $(ERL_PATH) \ - -s megaco_codec_v2_test generate_text_messages \ + -s megaco_codec_v2_SUITE generate_text_messages \ $(ESTOP) display_prev3a: make $(MERL) $(ARGS) -sname megaco_display_text_msgs_prev3a $(ERL_PATH) \ - -s megaco_codec_prev3a_test display_text_messages \ + -s megaco_codec_prev3a_SUITE display_text_messages \ $(ESTOP) display_prev3b: make $(MERL) $(ARGS) -sname megaco_display_text_msgs_prev3b $(ERL_PATH) \ - -s megaco_codec_prev3b_test display_text_messages \ + -s megaco_codec_prev3b_SUITE display_text_messages \ $(ESTOP) generate_prev3b: make $(MERL) $(ARGS) -sname megaco_generate_text_msgs_prev3b $(ERL_PATH) \ - -s megaco_codec_prev3b_test generate_text_messages \ + -s megaco_codec_prev3b_SUITE generate_text_messages \ $(ESTOP) display_prev3c: make $(MERL) $(ARGS) -sname megaco_display_text_msgs_prev3c $(ERL_PATH) \ - -s megaco_codec_prev3c_test display_text_messages \ + -s megaco_codec_prev3c_SUITE display_text_messages \ $(ESTOP) generate_prev3c: make $(MERL) $(ARGS) -sname megaco_generate_text_msgs_prev3c $(ERL_PATH) \ - -s megaco_codec_prev3c_test generate_text_messages \ + -s megaco_codec_prev3c_SUITE generate_text_messages \ $(ESTOP) display_v3: make $(MERL) $(ARGS) -sname megaco_display_text_msgs_v3 $(ERL_PATH) \ - -s megaco_codec_v3_test display_text_messages \ + -s megaco_codec_v3_SUITE display_text_messages \ $(ESTOP) generate_v3: make $(MERL) $(ARGS) -sname megaco_generate_text_msgs_v3 $(ERL_PATH) \ - -s megaco_codec_v3_test generate_text_messages \ + -s megaco_codec_v3_SUITE generate_text_messages \ $(ESTOP) generate: make $(MERL) $(ARGS) -sname megaco_generate_text_msgs $(ERL_PATH) \ - -s megaco_codec_v1_test generate_text_messages \ - -s megaco_codec_v2_test generate_text_messages \ - -s megaco_codec_v3_test generate_text_messages \ + -s megaco_codec_v1_SUITE generate_text_messages \ + -s megaco_codec_v2_SUITE generate_text_messages \ + -s megaco_codec_v3_SUITE generate_text_messages \ $(ESTOP) node: diff --git a/lib/megaco/test/megaco.spec b/lib/megaco/test/megaco.spec index cab8499835..3c9d23cc08 100644 --- a/lib/megaco/test/megaco.spec +++ b/lib/megaco/test/megaco.spec @@ -1,2 +1 @@ -{suites,"../megaco_test",all}. -{skip_cases,"../megaco_test",megaco_measure_test,[all],"Not yet implemented"}. +{suites, "../megaco_test", all}. diff --git a/lib/megaco/test/megaco_SUITE.erl b/lib/megaco/test/megaco_SUITE.erl deleted file mode 100644 index b55dc68143..0000000000 --- a/lib/megaco/test/megaco_SUITE.erl +++ /dev/null @@ -1,172 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2000-2019. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% %CopyrightEnd% -%% - -%% -%%---------------------------------------------------------------------- -%% Purpose: Test application config -%%---------------------------------------------------------------------- - --module(megaco_SUITE). - --export([ - suite/0, - all/0, - groups/0, - - init_per_suite/1, - end_per_suite/1, - init_per_group/2, - end_per_group/2, - init_per_testcase/2, - end_per_testcase/2, - - t/0, t/1, - init/0 - ]). - --include("megaco_test_lib.hrl"). --include_lib("megaco/include/megaco.hrl"). - -t() -> megaco_test_lib:t(?MODULE). -t(Case) -> megaco_test_lib:t({?MODULE, Case}). - -%% Test server callbacks -init_per_testcase(Case, Config) -> - megaco_test_lib:init_per_testcase(Case, Config). - -end_per_testcase(Case, Config) -> - megaco_test_lib:end_per_testcase(Case, Config). - -init() -> - process_flag(trap_exit, true), - megaco_test_lib:flush(). - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% Top test case - -suite() -> [{ct_hooks, [{ts_install_cth, [{nodenames,1}]}]}]. - -all() -> - [{group, app_test}, - {group, appup_test}, - {group, config}, - {group, flex}, - {group, udp}, - {group, tcp}, - {group, examples}, - {group, digit_map}, - {group, mess}, - {group, measure}, - {group, binary_term_id}, - {group, codec}, - {group, sdp}, - {group, mib}, - {group, trans}, - {group, actions}, - {group, load}, - {group, pending_limit}, - {group, segmented}, - {group, timer}]. - -groups() -> - [{tickets, [], [{group, mess}, {group, codec}]}, - {app_test, [], [{megaco_app_test, all}]}, - {appup_test, [], [{megaco_appup_test, all}]}, - {config, [], [{megaco_config_test, all}]}, - {call_flow, [], [{megaco_call_flow_test, all}]}, - {digit_map, [], [{megaco_digit_map_test, all}]}, - {mess, [], [{megaco_mess_test, all}]}, - {udp, [], [{megaco_udp_test, all}]}, - {tcp, [], [{megaco_tcp_test, all}]}, - {examples, [], [{megaco_examples_test, all}]}, - {measure, [], [{megaco_measure_test, all}]}, - {binary_term_id, [], [{megaco_binary_term_id_test, all}]}, - {codec, [], [{megaco_codec_test, all}]}, - {sdp, [], [{megaco_sdp_test, all}]}, - {mib, [], [{megaco_mib_test, all}]}, - {trans, [], [{megaco_trans_test, all}]}, - {actions, [], [{megaco_actions_test, all}]}, - {load, [], [{megaco_load_test, all}]}, - {pending_limit, [], [{megaco_pending_limit_test, all}]}, - {segmented, [], [{megaco_segment_test, all}]}, - {timer, [], [{megaco_timer_test, all}]}, - {flex, [], [{megaco_flex_test, all}]}]. - -init_per_suite(Config) -> - io:format("~w:init_per_suite -> entry with" - "~n Config: ~p" - "~n OS Type: ~p" - "~n OS Version: ~s" - "~n", - [?MODULE, - Config, - os:type(), - case os:version() of - {Major, Minor, Release} -> - ?F("~w.~w.~w", [Major, Minor, Release]); - Str when is_list(Str) -> - Str - end]), - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Skippable = [{unix, [{darwin, fun(V) when (V > {9, 8, 0}) -> - %% This version is OK: No Skip - false; - (_V) -> - %% This version is *not* ok: Skip - true - end}]}], - case ?OS_BASED_SKIP(Skippable) of - true -> - {skip, "***OLD*** Darwin"}; - false -> - Config - end. - -end_per_group(_GroupName, Config) -> - Config. - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/megaco/test/megaco_actions_test.erl b/lib/megaco/test/megaco_actions_SUITE.erl index 498e5c91cb..c4d245a504 100644 --- a/lib/megaco/test/megaco_actions_test.erl +++ b/lib/megaco/test/megaco_actions_SUITE.erl @@ -24,18 +24,13 @@ %% the action requests list. Do this with all codec's %% that supports partial encode. %%---------------------------------------------------------------------- --module(megaco_actions_test). +-module(megaco_actions_SUITE). -export([ - all/0, - groups/0, - - init_per_group/2, - end_per_group/2, - init_per_testcase/2, - end_per_testcase/2, - - t/0, t/1, + suite/0, all/0, groups/0, + init_per_suite/1, end_per_suite/1, + init_per_group/2, end_per_group/2, + init_per_testcase/2, end_per_testcase/2, pretty_text/1, flex_pretty_text/1, @@ -45,9 +40,9 @@ erl_dist_mc/1 ]). --include("megaco_test_lib.hrl"). -include_lib("megaco/include/megaco.hrl"). -include_lib("megaco/include/megaco_message_v1.hrl"). +-include("megaco_test_lib.hrl"). -define(TEST_VERBOSITY, debug). -define(MGC_VERBOSITY, debug). @@ -81,34 +76,119 @@ -define(MG_APPLY_LOAD(Pid,CntStart), megaco_test_mg:apply_load(Pid,CntStart)). -define(MG_EAR(Pid, Val), megaco_test_mg:encode_ar_first(Pid, Val)). -t() -> megaco_test_lib:t(?MODULE). -t(Case) -> megaco_test_lib:t({?MODULE, Case}). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% Test server callbacks -init_per_testcase(Case, Config) -> - process_flag(trap_exit, true), - megaco_test_lib:init_per_testcase(Case, Config). - -end_per_testcase(Case, Config) -> - process_flag(trap_exit, false), - megaco_test_lib:end_per_testcase(Case, Config). +%%====================================================================== +%% Common Test interface functions +%%====================================================================== -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +suite() -> + [{ct_hooks, [ts_install_cth]}]. all() -> - [pretty_text, flex_pretty_text, compact_text, - flex_compact_text, erl_dist, erl_dist_mc]. + [ + pretty_text, + flex_pretty_text, + compact_text, + flex_compact_text, + erl_dist, + erl_dist_mc + ]. groups() -> []. + + +%% +%% ----- +%% + +init_per_suite(suite) -> + []; +init_per_suite(doc) -> + []; +init_per_suite(Config0) when is_list(Config0) -> + + ?ANNOUNCE_SUITE_INIT(), + + p("init_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + case ?LIB:init_per_suite(Config0) of + {skip, _} = SKIP -> + SKIP; + + Config1 when is_list(Config1) -> + + %% We need a (local) monitor on this node also + megaco_test_sys_monitor:start(), + + p("init_per_suite -> end when" + "~n Config: ~p" + "~n Nodes: ~p", [Config1, erlang:nodes()]), + + Config1 + end. + +end_per_suite(suite) -> []; +end_per_suite(doc) -> []; +end_per_suite(Config0) when is_list(Config0) -> + + p("end_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + megaco_test_sys_monitor:stop(), + Config1 = ?LIB:end_per_suite(Config0), + + p("end_per_suite -> end when" + "~n Nodes: ~p", [erlang:nodes()]), + + Config1. + + +%% +%% ----- +%% + init_per_group(_GroupName, Config) -> Config. end_per_group(_GroupName, Config) -> Config. + + +%% +%% ----- +%% + +init_per_testcase(Case, Config) -> + process_flag(trap_exit, true), + + p("init_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config, erlang:nodes()]), + + megaco_test_global_sys_monitor:reset_events(), + megaco_test_lib:init_per_testcase(Case, Config). + +end_per_testcase(Case, Config) -> + process_flag(trap_exit, false), + + p("end_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config, erlang:nodes()]), + + p("system events during test: " + "~n ~p", [megaco_test_global_sys_monitor:events()]), + + megaco_test_lib:end_per_testcase(Case, Config). + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% pretty_text(suite) -> @@ -201,8 +281,7 @@ req_and_rep(Config, Codec, _Version, EC) when is_list(Config) -> "~n Mg1Node: ~p" "~n Mg2Node: ~p", [MgcNode, Mg1Node, Mg2Node]), - ok = megaco_test_lib:start_nodes([MgcNode, Mg1Node, Mg2Node], - ?FILE, ?LINE), + ok = ?START_NODES([MgcNode, Mg1Node, Mg2Node]), %% Start the MGC and MGs i("req_and_rep -> start the MGC"), @@ -412,3 +491,7 @@ print(_, _, _, _) -> ok. +p(F, A) -> + io:format("*** [~s] ~p ***" + "~n " ++ F ++ "~n", + [?FTS(), self() | A]). diff --git a/lib/megaco/test/megaco_app_test.erl b/lib/megaco/test/megaco_app_SUITE.erl index fff2d8c7d4..2cc52074fe 100644 --- a/lib/megaco/test/megaco_app_test.erl +++ b/lib/megaco/test/megaco_app_SUITE.erl @@ -21,22 +21,29 @@ %% Purpose: Verify the application specifics of the Megaco application %%---------------------------------------------------------------------- --module(megaco_app_test). +-module(megaco_app_SUITE). -export([ - all/0, + suite/0, all/0, + init_per_suite/1, end_per_suite/1, + init_per_group/2, end_per_group/2, + init_per_testcase/2, end_per_testcase/2, app/0, app/1, appup/0, appup/1 ]). -include_lib("common_test/include/ct.hrl"). +-include("megaco_test_lib.hrl"). %%-------------------------------------------------------------------- %% Common Test interface functions ----------------------------------- %%-------------------------------------------------------------------- +suite() -> + [{ct_hooks, [ts_install_cth]}]. + all() -> [ app, @@ -44,6 +51,42 @@ all() -> ]. +%% +%% ----- +%% + +init_per_suite(Config) when is_list(Config) -> + ?ANNOUNCE_SUITE_INIT(), + Config. + +end_per_suite(Config) when is_list(Config) -> + Config. + + + +%% +%% ----- +%% + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + +%% +%% ----- +%% + +init_per_testcase(_Case, Config) when is_list(Config) -> + Config. + +end_per_testcase(_Case, Config) when is_list(Config) -> + Config. + + + %%-------------------------------------------------------------------- %% Test Cases -------------------------------------------------------- %%-------------------------------------------------------------------- diff --git a/lib/megaco/test/megaco_appup_test.erl b/lib/megaco/test/megaco_appup_test.erl deleted file mode 100644 index a06d274844..0000000000 --- a/lib/megaco/test/megaco_appup_test.erl +++ /dev/null @@ -1,100 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2002-2019. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% %CopyrightEnd% -%% -%%---------------------------------------------------------------------- -%% Purpose: Verify the application specifics of the Megaco application -%%---------------------------------------------------------------------- --module(megaco_appup_test). - --export([ - all/0, - groups/0, - - init_per_suite/1, - end_per_suite/1, - - init_per_group/2, - end_per_group/2, - - init_per_testcase/2, - end_per_testcase/2, - - appup_file/1 - ]). - --compile({no_auto_import, [error/1]}). - --include_lib("common_test/include/ct.hrl"). --include("megaco_test_lib.hrl"). - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -all() -> - Cases = - [ - appup_file - ], - Cases. - -groups() -> - []. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -init_per_suite(suite) -> []; -init_per_suite(doc) -> []; -init_per_suite(Config) when is_list(Config) -> - Config. - - -end_per_suite(suite) -> []; -end_per_suite(doc) -> []; -end_per_suite(Config) when is_list(Config) -> - Config. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -%% Test server callbacks -init_per_testcase(_Case, Config) when is_list(Config) -> - Config. - -end_per_testcase(_Case, Config) when is_list(Config) -> - Config. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -%% Perform a simple check of the appup file -appup_file(suite) -> - []; -appup_file(doc) -> - ["Perform a simple check of the appup file"]; -appup_file(Config) when is_list(Config) -> - ok = ?t:appup_test(megaco). - diff --git a/lib/megaco/test/megaco_binary_term_id_test.erl b/lib/megaco/test/megaco_binary_term_id_SUITE.erl index b300379fae..91627c9716 100644 --- a/lib/megaco/test/megaco_binary_term_id_test.erl +++ b/lib/megaco/test/megaco_binary_term_id_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2016. All Rights Reserved. +%% Copyright Ericsson AB 2001-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -23,274 +23,182 @@ %% Purpose: %%---------------------------------------------------------------------- --module(megaco_binary_term_id_test). - -%%---------------------------------------------------------------------- -%% Include files -%%---------------------------------------------------------------------- --include_lib("megaco/include/megaco.hrl"). --include_lib("megaco/src/engine/megaco_message_internal.hrl"). +-module(megaco_binary_term_id_SUITE). %%---------------------------------------------------------------------- %% External exports %%---------------------------------------------------------------------- --export([t/0]). %% Test suite exports --export([all/0,groups/0,init_per_group/2,end_per_group/2, - init_per_testcase/2, end_per_testcase/2]). +-export([ + suite/0, all/0, groups/0, + init_per_suite/1, end_per_suite/1, + init_per_group/2, end_per_group/2, + init_per_testcase/2, end_per_testcase/2, + + te01/1, te02/1, te03/1, te04/1, te05/1, + te06/1, te07/1, te08/1, te09/1, te10/1, + te11/1, te12/1, te13/1, te14/1, te15/1, + te16/1, te17/1, te18/1, te19/1, + + td01/1, td02/1, td03/1, td04/1, td05/1, + td06/1 + ]). %%---------------------------------------------------------------------- -%% Internal exports +%% Include files %%---------------------------------------------------------------------- --export([te01/1,te02/1,te03/1,te04/1,te05/1, - te06/1,te07/1,te08/1,te09/1,te10/1, - te11/1,te12/1,te13/1,te14/1,te15/1, - te16/1,te17/1,te18/1,te19/1]). --export([td01/1,td02/1,td03/1,td04/1,td05/1,td06/1]). +-include_lib("megaco/include/megaco.hrl"). +-include_lib("megaco/src/engine/megaco_message_internal.hrl"). +-include("megaco_test_lib.hrl"). -%% --------------------------------------------------------------------- -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% Top test case + +%%====================================================================== +%% Common Test interface functions +%%====================================================================== + +suite() -> + [{ct_hooks, [ts_install_cth]}]. all() -> - [{group, encode_first}, {group, decode_first}]. + [ + {group, encode_first}, + {group, decode_first} + ]. groups() -> - [{encode_first, [], encode_first_cases()}, - {decode_first, [], decode_first_cases()}]. + [ + {encode_first, [], encode_first_cases()}, + {decode_first, [], decode_first_cases()} + ]. -init_per_group(_GroupName, Config) -> - Config. +encode_first_cases() -> + [ + te01, + te02, + te03, + te04, + te05, + te06, + te07, + te08, + te09, + te10, + te11, + te12, + te13, + te14, + te15, + te16, + te17, + te18, + te19 + ]. -end_per_group(_GroupName, Config) -> - Config. +decode_first_cases() -> + [ + td01, + td02, + td03, + td04, + td05, + td06 + ]. -%% Test server callbacks -init_per_testcase(Case, Config) -> - megaco_test_lib:init_per_testcase(Case, Config). -end_per_testcase(Case, Config) -> - megaco_test_lib:end_per_testcase(Case, Config). +%% +%% ----- +%% -%%====================================================================== -%% External functions -%%====================================================================== +init_per_suite(suite) -> + []; +init_per_suite(doc) -> + []; +init_per_suite(Config0) when is_list(Config0) -> -t() -> - display([do(Case) || Case <- cases()]), - ok. + ?ANNOUNCE_SUITE_INIT(), + p("init_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), -cases() -> encode_first_cases() ++ decode_first_cases(). + case ?LIB:init_per_suite([{sysmon, false}|Config0]) of + {skip, _} = SKIP -> + SKIP; -encode_first_cases() -> -[te01, te02, te03, te04, te05, te06, te07, te08, te09, - te10, te11, te12, te13, te14, te15, te16, te17, te18, - te19]. -decode_first_cases() -> -[td01, td02, td03, td04, td05, td06]. - -do(Case) -> - case doc(Case) of - {'EXIT',_} -> - {Case,error}; - Description -> - io:format("test case ~p~n",[Case]), - case suite(Case) of - {'EXIT',Reason} -> - Res = check_result(Case,Description,ok,{error,Reason}), - {Case,Res}; - {Expected,Result} -> - Res = check_result(Case,Description,Expected,Result), - {Case,Res} - end + Config1 when is_list(Config1) -> + + p("init_per_suite -> end when" + "~n Config: ~p" + "~n Nodes: ~p", [Config1, erlang:nodes()]), + + Config1 end. -doc(Case) -> (catch apply(?MODULE,Case,[doc])). -suite(Case) -> (catch apply(?MODULE,Case,[])). +end_per_suite(suite) -> []; +end_per_suite(doc) -> []; +end_per_suite(Config0) when is_list(Config0) -> -display(R) -> - [display(Case,Result) || {Case,Result} <- R]. + p("end_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), -display(C,error) -> - io:format("Test case ~p failed~n",[C]); -display(C,warning) -> - io:format("Test case ~p conspicuous~n",[C]); -display(C,ok) -> - io:format("Test case ~p succeeded~n",[C]). + Config1 = ?LIB:end_per_suite(Config0), -check(D,ok,{ok,T1,T2,T3}) -> - Result = case check_ok_result(T1,T3) of - ok -> - ok; - {error,Reason} -> - io:format(" => inconsistent result" - "~n Start and end record differ" - "~n ~s" - "~n ~s" - "~n ~w" - "~n", - [D,Reason,T2]), - warning - end, - Result; -check(D,error,{ok,T1,T2,T3}) -> - io:format(" => failed" - "~n ~s" - "~n ~p" - "~n ~p" - "~n ~p" - "~n", - [D,T1,T2,T3]), - error; -check(_D,error,{error,_Reason}) -> - ok; -check(D,ok,{error,Reason}) -> - io:format(" => failed" - "~n ~s" - "~n Failed for reason" - "~n ~p" - "~n", - [D,Reason]), - error. + p("end_per_suite -> end when" + "~n Nodes: ~p", [erlang:nodes()]), -check_result(_C,D,ok,{ok,T1,T2,T3}) -> - Result = case check_ok_result(T1,T3) of - ok -> - io:format(" => succeeded" - "~n ~s" - "~n ~p" - "~n ~w" - "~n ~p", - [D,T1,T2,T3]), - ok; - {error,Reason} -> - io:format(" => inconsistent result" - "~n Start and end record differ" - "~n ~s" - "~n ~s" - "~n ~w", - [D,Reason,T2]), - warning - end, - io:format("~n~n--------------------~n",[]), - Result; -check_result(_C,D,error,{ok,T1,T2,T3}) -> - io:format(" => failed" - "~n ~s" - "~n ~p" - "~n ~p" - "~n ~p" - "~n~n--------------------~n", - [D,T1,T2,T3]), - error; -check_result(_C,D,error,{error,Reason}) -> - io:format(" => succeeded" - "~n ~s" - "~n Operation failed (expectedly) for reason" - "~n ~p" - "~n~n--------------------~n", - [D,Reason]), - ok; -check_result(_C,D,ok,{error,Reason}) -> - io:format(" => failed" - "~n ~s" - "~n Failed for reason" - "~n ~p" - "~n~n--------------------~n", - [D,Reason]), - error. + Config1. -check_ok_result(R,R) when is_record(R,megaco_term_id) -> - ok; % Same record type and same record content -check_ok_result(S,E) when is_record(S,megaco_term_id) andalso - is_record(E,megaco_term_id) -> - Reason = check_megaco_term_id_record(S,E), - {error,Reason}; % Same record type but different record content -check_ok_result(R,R) when is_record(R,'TerminationID') -> - ok; -check_ok_result(S,E) when is_record(S,'TerminationID') andalso - is_record(E,'TerminationID') -> - Reason = check_TerminationID_record(S,E), - {error,Reason}; % Same record type but different record content -check_ok_result(_S,_E) -> - {error,"NOT THE SAME RECORD TYPES"}. % OOPS, Not even the same record type - -check_megaco_term_id_record(#megaco_term_id{contains_wildcards = Cw1, - id = Id1}, - #megaco_term_id{contains_wildcards = Cw2, - id = Id2}) -> - Result = case check_megaco_term_id_cw(Cw1,Cw2) of - ok -> - check_megaco_term_id_id(Id1,Id2); - {error,R1} -> - R2 = check_megaco_term_id_id(Id1,Id2), - io_lib:format("~s~s",[R1,R2]) - end, - lists:flatten(Result). - -check_megaco_term_id_cw(Cw,Cw) -> - ok; -check_megaco_term_id_cw(Cw1,Cw2) -> - R = io_lib:format("~n The 'contains_wildcard' property of the start" - "~n megaco_term_id record " - "~n has value ~w " - "~n but the end record " - "~n has value ~w", - [Cw1,Cw2]), - {error,R}. -check_megaco_term_id_id(Id,Id) -> - ok; -check_megaco_term_id_id(Id1,Id2) -> - R = io_lib:format("~n The 'id' property of the start" - "~n megaco_term_id record " - "~n has value ~w " - "~n but the end record " - "~n has value ~w", - [Id1,Id2]), - {error,R}. +%% +%% ----- +%% +init_per_group(_GroupName, Config) -> + p("init_per_group -> entry with" + "~n Config: ~p" + "~n", [Config]), + Config. -check_TerminationID_record(#'TerminationID'{wildcard = W1, id = Id1}, - #'TerminationID'{wildcard = W2, id = Id2}) -> - Result = case check_TerminationID_w(W1,W2) of - ok -> - check_TerminationID_id(Id1,Id2); - {error,R1} -> - R2 = check_TerminationID_id(Id1,Id2), - io_lib:format("~s~s",[R1,R2]) - end, - lists:flatten(Result). - -check_TerminationID_w(W,W) -> - ok; -check_TerminationID_w(W1,W2) -> - R = io_lib:format("~n The 'wildcard' property of the start" - "~n 'TerminationID' record " - "~n has value ~w " - "~n but the end record " - "~n has value ~w", - [W1,W2]), - {error,R}. +end_per_group(_GroupName, Config) -> + p("end_per_group -> entry with" + "~n Config: ~p" + "~n", [Config]), + Config. -check_TerminationID_id(Id,Id) -> - ok; -check_TerminationID_id(Id1,Id2) -> - R = io_lib:format("~n The 'id' property of the start" - "~n 'TerminationID' record " - "~n has value ~w " - "~n but the end record " - "~n has value ~w", - [Id1,Id2]), - {error,R}. + + +%% +%% ----- +%% + +init_per_testcase(Case, Config) -> + + p("init_per_testcase -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config, erlang:nodes()]), + + megaco_test_lib:init_per_testcase(Case, Config). + +end_per_testcase(Case, Config) -> + + p("end_per_testcase -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config, erlang:nodes()]), + + megaco_test_lib:end_per_testcase(Case, Config). + + +%%====================================================================== +%% External functions +%%====================================================================== %% -------------------------------------------------------- @@ -1012,3 +920,144 @@ decode1(C,T) -> +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +check(D,ok,{ok,T1,T2,T3}) -> + Result = case check_ok_result(T1,T3) of + ok -> + ok; + {error,Reason} -> + io:format(" => inconsistent result" + "~n Start and end record differ" + "~n ~s" + "~n ~s" + "~n ~w" + "~n", + [D,Reason,T2]), + warning + end, + Result; +check(D,error,{ok,T1,T2,T3}) -> + io:format(" => failed" + "~n ~s" + "~n ~p" + "~n ~p" + "~n ~p" + "~n", + [D,T1,T2,T3]), + error; +check(_D,error,{error,_Reason}) -> + ok; +check(D,ok,{error,Reason}) -> + io:format(" => failed" + "~n ~s" + "~n Failed for reason" + "~n ~p" + "~n", + [D,Reason]), + error. + + +check_ok_result(R,R) when is_record(R,megaco_term_id) -> + ok; % Same record type and same record content +check_ok_result(S,E) when is_record(S,megaco_term_id) andalso + is_record(E,megaco_term_id) -> + Reason = check_megaco_term_id_record(S,E), + {error,Reason}; % Same record type but different record content +check_ok_result(R,R) when is_record(R,'TerminationID') -> + ok; +check_ok_result(S,E) when is_record(S,'TerminationID') andalso + is_record(E,'TerminationID') -> + Reason = check_TerminationID_record(S,E), + {error,Reason}; % Same record type but different record content +check_ok_result(_S,_E) -> + {error,"NOT THE SAME RECORD TYPES"}. % OOPS, Not even the same record type + +check_megaco_term_id_record(#megaco_term_id{contains_wildcards = Cw1, + id = Id1}, + #megaco_term_id{contains_wildcards = Cw2, + id = Id2}) -> + Result = case check_megaco_term_id_cw(Cw1,Cw2) of + ok -> + check_megaco_term_id_id(Id1,Id2); + {error,R1} -> + R2 = check_megaco_term_id_id(Id1,Id2), + io_lib:format("~s~s",[R1,R2]) + end, + lists:flatten(Result). + +check_megaco_term_id_cw(Cw,Cw) -> + ok; +check_megaco_term_id_cw(Cw1,Cw2) -> + R = io_lib:format("~n The 'contains_wildcard' property of the start" + "~n megaco_term_id record " + "~n has value ~w " + "~n but the end record " + "~n has value ~w", + [Cw1,Cw2]), + {error,R}. + +check_megaco_term_id_id(Id,Id) -> + ok; +check_megaco_term_id_id(Id1,Id2) -> + R = io_lib:format("~n The 'id' property of the start" + "~n megaco_term_id record " + "~n has value ~w " + "~n but the end record " + "~n has value ~w", + [Id1,Id2]), + {error,R}. + + +check_TerminationID_record(#'TerminationID'{wildcard = W1, id = Id1}, + #'TerminationID'{wildcard = W2, id = Id2}) -> + Result = case check_TerminationID_w(W1,W2) of + ok -> + check_TerminationID_id(Id1,Id2); + {error,R1} -> + R2 = check_TerminationID_id(Id1,Id2), + io_lib:format("~s~s",[R1,R2]) + end, + lists:flatten(Result). + +check_TerminationID_w(W,W) -> + ok; +check_TerminationID_w(W1,W2) -> + R = io_lib:format("~n The 'wildcard' property of the start" + "~n 'TerminationID' record " + "~n has value ~w " + "~n but the end record " + "~n has value ~w", + [W1,W2]), + {error,R}. + +check_TerminationID_id(Id,Id) -> + ok; +check_TerminationID_id(Id1,Id2) -> + R = io_lib:format("~n The 'id' property of the start" + "~n 'TerminationID' record " + "~n has value ~w " + "~n but the end record " + "~n has value ~w", + [Id1,Id2]), + {error,R}. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% p(F) -> +%% p(F, []). + +p(F, A) -> + p(get(sname), F, A). + +p(S, F, A) when is_list(S) -> + io:format("*** [~s] ~p ~s ***" + "~n " ++ F ++ "~n", + [?FTS(), self(), S | A]); +p(_S, F, A) -> + io:format("*** [~s] ~p *** " + "~n " ++ F ++ "~n", + [?FTS(), self() | A]). + + diff --git a/lib/megaco/test/megaco_call_flow_test.erl b/lib/megaco/test/megaco_call_flow_SUITE.erl index 03caf705ba..1f9177d8ae 100644 --- a/lib/megaco/test/megaco_call_flow_test.erl +++ b/lib/megaco/test/megaco_call_flow_SUITE.erl @@ -36,16 +36,13 @@ %% megaco_call_flow_test:gnuplot_code_time_gif(). %%---------------------------------------------------------------------- --module(megaco_call_flow_test). +-module(megaco_call_flow_SUITE). -export([ - all/0, - groups/0, - - init_per_group/2, - end_per_group/2, - init_per_testcase/2, - end_per_testcase/2, + suite/0, all/0, groups/0, + init_per_suite/1, end_per_suite/1, + init_per_group/2, end_per_group/2, + init_per_testcase/2, end_per_testcase/2, pretty/1, compact/1, @@ -55,10 +52,7 @@ ber/1, per/1, standard_erl/1, - compressed_erl/1, - - t/0, t/1 - + compressed_erl/1 ]). -export([ @@ -121,37 +115,134 @@ -include_lib("megaco/include/megaco_message_v1.hrl"). -include("megaco_test_lib.hrl"). -t() -> megaco_test_lib:t(?MODULE). -t(Case) -> megaco_test_lib:t({?MODULE, Case}). -%% Test server callbacks -init_per_testcase(Case, Config) -> - megaco_test_lib:init_per_testcase(Case, Config). +%%====================================================================== +%% Common Test interface functions +%%====================================================================== -end_per_testcase(Case, Config) -> - megaco_test_lib:end_per_testcase(Case, Config). - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% Top test case +suite() -> + [{ct_hooks, [ts_install_cth]}]. all() -> - [{group, text}, {group, binary}, {group, erl}]. + [ + {group, text}, + {group, binary}, + {group, erl} + ]. groups() -> [ - {text, [], [pretty, compact]}, - {flex, [], [pretty_flex, compact_flex]}, - {binary, [], [bin, ber, per]}, - {erl, [], [standard_erl, compressed_erl]} + {text, [], text_cases()}, + {flex, [], flex_cases()}, + {binary, [], binary_cases()}, + {erl, [], erl_cases()} + ]. + +text_cases() -> + [ + pretty, + compact + ]. + +flex_cases() -> + [ + pretty_flex, + compact_flex ]. -init_per_group(_GroupName, Config) -> +binary_cases() -> + [ + bin, + ber, + per + ]. + +erl_cases() -> + [ + standard_erl, + compressed_erl + ]. + + + +%% +%% ----- +%% + +init_per_suite(suite) -> + []; +init_per_suite(doc) -> + []; +init_per_suite(Config0) when is_list(Config0) -> + + ?ANNOUNCE_SUITE_INIT(), + + p("init_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + case ?LIB:init_per_suite(Config0) of + {skip, _} = SKIP -> + SKIP; + + Config1 when is_list(Config1) -> + + %% We need a (local) monitor on this node also + megaco_test_sys_monitor:start(), + + p("init_per_suite -> end when" + "~n Config: ~p" + "~n Nodes: ~p", [Config1, erlang:nodes()]), + + Config1 + end. + +end_per_suite(suite) -> []; +end_per_suite(doc) -> []; +end_per_suite(Config0) when is_list(Config0) -> + + p("end_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + megaco_test_sys_monitor:stop(), + Config1 = ?LIB:end_per_suite(Config0), + + p("end_per_suite -> end when" + "~n Nodes: ~p", [erlang:nodes()]), + + Config1. + + +%% +%% ----- +%% + +init_per_group(Group, Config) -> + ?ANNOUNCE_GROUP_INIT(Group), Config. -end_per_group(_GroupName, Config) -> +end_per_group(_Group, Config) -> Config. + +%% +%% ----- +%% + +init_per_testcase(Case, Config) -> + %% We do *not* reset events with each test case + %% The test cases are so short we don't bother, + %% and also we would drown in mprintouts... + megaco_test_lib:init_per_testcase(Case, Config). + +end_per_testcase(Case, Config) -> + megaco_test_lib:end_per_testcase(Case, Config). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + pretty(suite) -> []; pretty(Config) when is_list(Config) -> @@ -1820,3 +1911,12 @@ gnuplot_data([{Mod, _Opt, _Opt2} = E | Encoders], Dir, Stat, Pos, Names, Arrows) " nohead lt ", Pos, "\n", "set label \" ", Avg, " (avg)\" at ", Len, ",", Avg + 10, "\n"]), gnuplot_data(Encoders, Dir, Stat, Pos + 1, [Name | Names], [Arrow | Arrows]). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +p(F, A) -> + io:format("*** [~s] ***" + "~n " ++ F ++ "~n", + [?FTS() | A]). + diff --git a/lib/megaco/test/megaco_codec_mini_test.erl b/lib/megaco/test/megaco_codec_mini_SUITE.erl index fb23eb4680..12113aae70 100644 --- a/lib/megaco/test/megaco_codec_mini_test.erl +++ b/lib/megaco/test/megaco_codec_mini_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2016. All Rights Reserved. +%% Copyright Ericsson AB 2008-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -23,26 +23,25 @@ %% Purpose: Test mini encoding/decoding (codec) module of Megaco/H.248 %%---------------------------------------------------------------------- --module(megaco_codec_mini_test). +-module(megaco_codec_mini_SUITE). %% ---- -include_lib("megaco/include/megaco.hrl"). -%% -include_lib("megaco/include/megaco_message_v3.hrl"). -include("megaco_test_lib.hrl"). %% ---- --export([t/0, t/1]). +-export([ + suite/0, all/0, groups/0, + init_per_suite/1, end_per_suite/1, + init_per_group/2, end_per_group/2, + init_per_testcase/2, end_per_testcase/2, --export([all/0,groups/0,init_per_group/2,end_per_group/2, - - tickets/0, - otp7672_msg01/1, - otp7672_msg02/1, - - init_per_testcase/2, end_per_testcase/2]). + otp7672_msg02/1 + + ]). %% ---- @@ -50,51 +49,78 @@ -define(SET_DBG(S,D), begin put(severity, S), put(dbg, D) end). -define(RESET_DBG(), begin erase(severity), erase(dbg) end). -expand(RootCase) -> - expand([RootCase], []). - -expand([], Acc) -> - lists:flatten(lists:reverse(Acc)); -expand([Case|Cases], Acc) -> - case (catch apply(?MODULE,Case,[suite])) of - [] -> - expand(Cases, [Case|Acc]); - C when is_list(C) -> - expand(Cases, [expand(C, [])|Acc]); - _ -> - expand(Cases, [Case|Acc]) - end. +%%====================================================================== +%% Common Test interface functions +%%====================================================================== -%% ---- +suite() -> + [{ct_hooks, [ts_install_cth]}]. -t() -> megaco_test_lib:t(?MODULE). -t(Case) -> megaco_test_lib:t({?MODULE, Case}). +all() -> + [{group, tickets}]. -init_per_testcase(Case, Config) -> - C = - case lists:suffix("time_test", atom_to_list(Case)) of - true -> - [{tc_timeout, timer:minutes(10)}|Config]; - false -> - put(verbosity,trc), - Config - end, - megaco_test_lib:init_per_testcase(Case, C). +groups() -> + [ + {tickets, [], tickets_cases()} + ]. -end_per_testcase(Case, Config) -> - erase(verbosity), - megaco_test_lib:end_per_testcase(Case, Config). +tickets_cases() -> + [ + otp7672_msg01, + otp7672_msg02 + ]. -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% Top test case -all() -> - [{group, tickets}]. +%% +%% ----- +%% -groups() -> - [{tickets, [], [otp7672_msg01, otp7672_msg02]}]. +init_per_suite(suite) -> + []; +init_per_suite(doc) -> + []; +init_per_suite(Config0) when is_list(Config0) -> + + ?ANNOUNCE_SUITE_INIT(), + + p("init_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + case ?LIB:init_per_suite([{sysmon, false} | Config0]) of + {skip, _} = SKIP -> + SKIP; + + Config1 when is_list(Config1) -> + + p("init_per_suite -> end when" + "~n Config: ~p" + "~n Nodes: ~p", [Config1, erlang:nodes()]), + + Config1 + end. + +end_per_suite(suite) -> []; +end_per_suite(doc) -> []; +end_per_suite(Config0) when is_list(Config0) -> + + p("end_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + Config1 = ?LIB:end_per_suite(Config0), + + p("end_per_suite -> end when" + "~n Nodes: ~p", [erlang:nodes()]), + + Config1. + + +%% +%% ----- +%% init_per_group(_GroupName, Config) -> Config. @@ -104,31 +130,31 @@ end_per_group(_GroupName, Config) -> -%% ---- +%% +%% ----- +%% + +init_per_testcase(Case, Config) -> + + p("init_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config, erlang:nodes()]), + + put(verbosity,trc), + megaco_test_lib:init_per_testcase(Case, Config). + +end_per_testcase(Case, Config) -> + + p("end_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config, erlang:nodes()]), + + erase(verbosity), + megaco_test_lib:end_per_testcase(Case, Config). + + + -tickets() -> - Flag = process_flag(trap_exit, true), - Cases = expand(tickets), - Fun = fun(Case) -> - C = init_per_testcase(Case, [{tc_timeout, - timer:minutes(10)}]), - io:format("Eval ~w~n", [Case]), - Result = - case (catch apply(?MODULE, Case, [C])) of - {'EXIT', Reason} -> - io:format("~n~p exited:~n ~p~n", - [Case, Reason]), - {error, {Case, Reason}}; - Res -> - Res - end, - end_per_testcase(Case, C), - Result - end, - process_flag(trap_exit, Flag), - lists:map(Fun, Cases). - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # @@ -176,6 +202,12 @@ otp7672(Msg) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +p(F, A) -> + io:format("*** [~s] ~p ***" + "~n " ++ F ++ "~n", + [?FTS(), self() | A]). + + t(F,A) -> p(printable(get(severity),trc),trc,F,A). diff --git a/lib/megaco/test/megaco_codec_prev3a_test.erl b/lib/megaco/test/megaco_codec_prev3a_SUITE.erl index c68c3b12f6..ccb608ffc0 100644 --- a/lib/megaco/test/megaco_codec_prev3a_test.erl +++ b/lib/megaco/test/megaco_codec_prev3a_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2016. All Rights Reserved. +%% Copyright Ericsson AB 2005-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -22,7 +22,7 @@ %%---------------------------------------------------------------------- %% Purpose: Test encoding/decoding (codec) module of Megaco/H.248 %%---------------------------------------------------------------------- --module(megaco_codec_prev3a_test). +-module(megaco_codec_prev3a_SUITE). %% ---- @@ -35,9 +35,11 @@ -export([msgs/0]). -export([rfc3525_msgs_display/0, rfc3525_msgs_test/0]). --export([t/0, t/1]). - --export([all/0,groups/0,init_per_group/2,end_per_group/2, +-export([ + suite/0, all/0, groups/0, + init_per_suite/1, end_per_suite/1, + init_per_group/2, end_per_group/2, + init_per_testcase/2, end_per_testcase/2, pretty_test_msgs/1, @@ -68,8 +70,6 @@ erl_dist_m_test_msgs/1, - tickets/0, - compact_otp4011_msg1/1, compact_otp4011_msg2/1, compact_otp4011_msg3/1, @@ -183,9 +183,8 @@ flex_pretty_otp7431_msg04/1, flex_pretty_otp7431_msg05/1, flex_pretty_otp7431_msg06/1, - flex_pretty_otp7431_msg07/1, - - init_per_testcase/2, end_per_testcase/2]). + flex_pretty_otp7431_msg07/1 + ]). -export([display_text_messages/0]). @@ -214,151 +213,77 @@ -define(A5556, ["11111111", "11111111", "11111111"]). -%% ---- - -display_text_messages() -> - Msgs = msgs1(text) ++ msgs4(text) ++ msgs5(text) ++ msgs6(text), - %% Msgs = msgs5(text) ++ msgs6(text), - megaco_codec_test_lib:display_text_messages(?VERSION, ?EC, Msgs). - - -%% ---- +%%====================================================================== +%% Common Test interface functions +%%====================================================================== -tickets() -> - %% io:format("~w:tickets -> entry~n", [?MODULE]), - megaco_test_lib:tickets(?MODULE). +suite() -> + [{ct_hooks, [ts_install_cth]}]. +all() -> + [ + {group, text}, + {group, binary}, + {group, erl_dist}, + {group, tickets} + ]. -%% ---- - -t() -> megaco_test_lib:t(?MODULE). -t(Case) -> megaco_test_lib:t({?MODULE, Case}). - -init_per_testcase(Case, Config) -> - %% CaseString = io_lib:format("~p", [Case]), - C = - case lists:suffix("time_test", atom_to_list(Case)) of - true -> - [{tc_timeout, timer:minutes(10)}|Config]; - false -> - put(verbosity,trc), - Config - end, - megaco_test_lib:init_per_testcase(Case, C). +groups() -> + [ + {text, [], text_cases()}, + {binary, [], binary_cases()}, + {erl_dist, [], erl_dist_cases()}, + {pretty, [], pretty_cases()}, + {compact, [], compact_cases()}, + {flex_pretty, [], flex_pretty_cases()}, + {flex_compact, [], flex_compact_cases()}, + {bin, [], bin_cases()}, + {ber, [], ber_cases()}, + {per, [], per_cases()}, + {erl_dist_m, [], erl_dist_m_cases()}, + {tickets, [], tickets_cases()}, + {compact_tickets, [], compact_tickets_cases()}, + {flex_compact_tickets, [], flex_compact_tickets_cases()}, + {pretty_tickets, [], pretty_tickets_cases()}, + {flex_pretty_tickets, [], flex_pretty_tickets_cases()} + ]. -end_per_testcase(Case, Config) -> - erase(verbosity), - megaco_test_lib:end_per_testcase(Case, Config). +text_cases() -> + [ + {group, pretty}, + {group, flex_pretty}, + {group, compact}, + {group, flex_compact} + ]. -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% Top test case +binary_cases() -> + [ + {group, bin}, + {group, ber}, + {group, per} + ]. -all() -> - [{group, text}, - {group, binary}, - {group, erl_dist}, - {group, tickets}]. +erl_dist_cases() -> + [ + {group, erl_dist_m} + ]. -groups() -> - [{text, [], - [{group, pretty}, - {group, flex_pretty}, - {group, compact}, - {group, flex_compact}]}, - {binary, [], - [{group, bin}, - {group, ber}, - {group, per}]}, - {erl_dist, [], [{group, erl_dist_m}]}, - {pretty, [], [pretty_test_msgs]}, - {compact, [], [compact_test_msgs]}, - {flex_pretty, [], flex_pretty_cases()}, - {flex_compact, [], flex_compact_cases()}, - {bin, [], [bin_test_msgs]}, - {ber, [], [ber_test_msgs]}, - {per, [], [per_test_msgs]}, - {erl_dist_m, [], [erl_dist_m_test_msgs]}, - {tickets, [], - [{group, compact_tickets}, - {group, flex_compact_tickets}, - {group, pretty_tickets}, - {group, flex_pretty_tickets}]}, - {compact_tickets, [], - [compact_otp4011_msg1, compact_otp4011_msg2, - compact_otp4011_msg3, compact_otp4013_msg1, - compact_otp4085_msg1, compact_otp4085_msg2, - compact_otp4280_msg1, compact_otp4299_msg1, - compact_otp4299_msg2, compact_otp4359_msg1, - compact_otp4920_msg0, compact_otp4920_msg1, - compact_otp4920_msg2, compact_otp4920_msg3, - compact_otp4920_msg4, compact_otp4920_msg5, - compact_otp4920_msg6, compact_otp4920_msg7, - compact_otp4920_msg8, compact_otp4920_msg9, - compact_otp4920_msg10, compact_otp4920_msg11, - compact_otp4920_msg12, compact_otp4920_msg20, - compact_otp4920_msg21, compact_otp4920_msg22, - compact_otp4920_msg23, compact_otp4920_msg24, - compact_otp4920_msg25, compact_otp5186_msg01, - compact_otp5186_msg02, compact_otp5186_msg03, - compact_otp5186_msg04, compact_otp5186_msg05, - compact_otp5186_msg06, compact_otp5793_msg01, - compact_otp5993_msg01, compact_otp5993_msg02, - compact_otp5993_msg03, compact_otp6017_msg01, - compact_otp6017_msg02, compact_otp6017_msg03]}, - {flex_compact_tickets, [], flex_compact_tickets_cases()}, - {pretty_tickets, [], - [pretty_otp4632_msg1, pretty_otp4632_msg2, - pretty_otp4632_msg3, pretty_otp4632_msg4, - pretty_otp4710_msg1, pretty_otp4710_msg2, - pretty_otp4945_msg1, pretty_otp4945_msg2, - pretty_otp4945_msg3, pretty_otp4945_msg4, - pretty_otp4945_msg5, pretty_otp4945_msg6, - pretty_otp4949_msg1, pretty_otp4949_msg2, - pretty_otp4949_msg3, pretty_otp5042_msg1, - pretty_otp5068_msg1, pretty_otp5085_msg1, - pretty_otp5085_msg2, pretty_otp5085_msg3, - pretty_otp5085_msg4, pretty_otp5085_msg5, - pretty_otp5085_msg6, pretty_otp5085_msg7, - pretty_otp5085_msg8, pretty_otp5600_msg1, - pretty_otp5600_msg2, pretty_otp5601_msg1, - pretty_otp5793_msg01, pretty_otp5882_msg01, - pretty_otp6490_msg01, pretty_otp6490_msg02, - pretty_otp6490_msg03, pretty_otp6490_msg04, - pretty_otp6490_msg05, pretty_otp6490_msg06, - pretty_otp7671_msg01, pretty_otp7671_msg02, - pretty_otp7671_msg03, pretty_otp7671_msg04, - pretty_otp7671_msg05, pretty_otp8114_msg01]}, - {flex_pretty_tickets, [], flex_pretty_tickets_cases()}]. - -init_per_group(flex_pretty_tickets, Config) -> - flex_pretty_init(Config); -init_per_group(flex_compact_tickets, Config) -> - flex_compact_init(Config); -init_per_group(flex_compact, Config) -> - flex_compact_init(Config); -init_per_group(flex_pretty, Config) -> - flex_pretty_init(Config); -init_per_group(_GroupName, Config) -> - Config. +pretty_cases() -> + [ + pretty_test_msgs + ]. -end_per_group(flex_pretty_tickets, Config) -> - flex_pretty_finish(Config); -end_per_group(flex_compact_tickets, Config) -> - flex_compact_finish(Config); -end_per_group(flex_compact, Config) -> - flex_compact_finish(Config); -end_per_group(flex_pretty, Config) -> - flex_pretty_finish(Config); -end_per_group(_GroupName, Config) -> - Config. +compact_cases() -> + [ + compact_test_msgs + ]. flex_pretty_cases() -> [ flex_pretty_test_msgs ]. - flex_compact_cases() -> [ flex_compact_test_msgs, @@ -372,6 +297,79 @@ flex_compact_cases() -> flex_compact_dm_timers8 ]. +bin_cases() -> + [ + bin_test_msgs + ]. + +ber_cases() -> + [ + ber_test_msgs + ]. + +per_cases() -> + [ + per_test_msgs + ]. + +erl_dist_m_cases() -> + [ + erl_dist_m_test_msgs + ]. + +tickets_cases() -> + [ + {group, compact_tickets}, + {group, flex_compact_tickets}, + {group, pretty_tickets}, + {group, flex_pretty_tickets} + ]. + +compact_tickets_cases() -> + [ + compact_otp4011_msg1, + compact_otp4011_msg2, + compact_otp4011_msg3, + compact_otp4013_msg1, + compact_otp4085_msg1, + compact_otp4085_msg2, + compact_otp4280_msg1, + compact_otp4299_msg1, + compact_otp4299_msg2, + compact_otp4359_msg1, + compact_otp4920_msg0, + compact_otp4920_msg1, + compact_otp4920_msg2, + compact_otp4920_msg3, + compact_otp4920_msg4, + compact_otp4920_msg5, + compact_otp4920_msg6, + compact_otp4920_msg7, + compact_otp4920_msg8, + compact_otp4920_msg9, + compact_otp4920_msg10, + compact_otp4920_msg11, + compact_otp4920_msg12, + compact_otp4920_msg20, + compact_otp4920_msg21, + compact_otp4920_msg22, + compact_otp4920_msg23, + compact_otp4920_msg24, + compact_otp4920_msg25, + compact_otp5186_msg01, + compact_otp5186_msg02, + compact_otp5186_msg03, + compact_otp5186_msg04, + compact_otp5186_msg05, + compact_otp5186_msg06, + compact_otp5793_msg01, + compact_otp5993_msg01, + compact_otp5993_msg02, + compact_otp5993_msg03, + compact_otp6017_msg01, + compact_otp6017_msg02, + compact_otp6017_msg03 + ]. flex_compact_tickets_cases() -> [ @@ -384,6 +382,52 @@ flex_compact_tickets_cases() -> flex_compact_otp7431_msg07 ]. +pretty_tickets_cases() -> + [ + pretty_otp4632_msg1, + pretty_otp4632_msg2, + pretty_otp4632_msg3, + pretty_otp4632_msg4, + pretty_otp4710_msg1, + pretty_otp4710_msg2, + pretty_otp4945_msg1, + pretty_otp4945_msg2, + pretty_otp4945_msg3, + pretty_otp4945_msg4, + pretty_otp4945_msg5, + pretty_otp4945_msg6, + pretty_otp4949_msg1, + pretty_otp4949_msg2, + pretty_otp4949_msg3, + pretty_otp5042_msg1, + pretty_otp5068_msg1, + pretty_otp5085_msg1, + pretty_otp5085_msg2, + pretty_otp5085_msg3, + pretty_otp5085_msg4, + pretty_otp5085_msg5, + pretty_otp5085_msg6, + pretty_otp5085_msg7, + pretty_otp5085_msg8, + pretty_otp5600_msg1, + pretty_otp5600_msg2, + pretty_otp5601_msg1, + pretty_otp5793_msg01, + pretty_otp5882_msg01, + pretty_otp6490_msg01, + pretty_otp6490_msg02, + pretty_otp6490_msg03, + pretty_otp6490_msg04, + pretty_otp6490_msg05, + pretty_otp6490_msg06, + pretty_otp7671_msg01, + pretty_otp7671_msg02, + pretty_otp7671_msg03, + pretty_otp7671_msg04, + pretty_otp7671_msg05, + pretty_otp8114_msg01 + ]. + flex_pretty_tickets_cases() -> [ flex_pretty_otp5042_msg1, @@ -409,6 +453,115 @@ flex_pretty_tickets_cases() -> ]. + +%% +%% ----- +%% + +init_per_suite(suite) -> + []; +init_per_suite(doc) -> + []; +init_per_suite(Config0) when is_list(Config0) -> + + ?ANNOUNCE_SUITE_INIT(), + + p("init_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + case ?LIB:init_per_suite(Config0) of + {skip, _} = SKIP -> + SKIP; + + Config1 when is_list(Config1) -> + + %% We need a (local) monitor on this node also + megaco_test_sys_monitor:start(), + + p("init_per_suite -> end when" + "~n Config: ~p" + "~n Nodes: ~p", [Config1, erlang:nodes()]), + + Config1 + end. + +end_per_suite(suite) -> []; +end_per_suite(doc) -> []; +end_per_suite(Config0) when is_list(Config0) -> + + p("end_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + megaco_test_sys_monitor:stop(), + Config1 = ?LIB:end_per_suite(Config0), + + p("end_per_suite -> end when" + "~n Nodes: ~p", [erlang:nodes()]), + + Config1. + + +%% +%% ----- +%% + +init_per_group(flex_pretty_tickets = Group, Config) -> + ?ANNOUNCE_GROUP_INIT(Group), + flex_pretty_init(Config); +init_per_group(flex_compact_tickets = Group, Config) -> + ?ANNOUNCE_GROUP_INIT(Group), + flex_compact_init(Config); +init_per_group(flex_compact = Group, Config) -> + ?ANNOUNCE_GROUP_INIT(Group), + flex_compact_init(Config); +init_per_group(flex_pretty = Group, Config) -> + ?ANNOUNCE_GROUP_INIT(Group), + flex_pretty_init(Config); +init_per_group(Group, Config) -> + ?ANNOUNCE_GROUP_INIT(Group), + Config. + +end_per_group(flex_pretty_tickets = _Group, Config) -> + flex_pretty_finish(Config); +end_per_group(flex_compact_tickets = _Group, Config) -> + flex_compact_finish(Config); +end_per_group(flex_compact = _Group, Config) -> + flex_compact_finish(Config); +end_per_group(flex_pretty = _Group, Config) -> + flex_pretty_finish(Config); +end_per_group(_Group, Config) -> + Config. + + + +%% +%% ----- +%% + +init_per_testcase(Case, Config) -> + %% We do *not* reset events with each test case + %% The test cases are so short we don't bother, + %% and also we would drown in mprintouts... + put(verbosity,trc), + megaco_test_lib:init_per_testcase(Case, Config). + +end_per_testcase(Case, Config) -> + erase(verbosity), + megaco_test_lib:end_per_testcase(Case, Config). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +display_text_messages() -> + Msgs = msgs1(text) ++ msgs4(text) ++ msgs5(text) ++ msgs6(text), + %% Msgs = msgs5(text) ++ msgs6(text), + megaco_codec_test_lib:display_text_messages(?VERSION, ?EC, Msgs). + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% pretty_test_msgs(suite) -> @@ -7149,11 +7302,6 @@ printable(_,_) -> false. -p(true,L,F,A) -> - io:format("~s:" ++ F ++ "~n", [image_of(L)|A]); -p(_,_,_,_) -> - ok. - image_of(trc) -> "TRC"; image_of(dbg) -> @@ -7165,3 +7313,16 @@ image_of(err) -> image_of(L) -> io_lib:format("~p",[L]). + +p(true,L,F,A) -> + io:format("~s:" ++ F ++ "~n", [image_of(L)|A]); +p(_,_,_,_) -> + ok. + + +p(F, A) -> + io:format("*** [~s] ***" + "~n " ++ F ++ "~n", + [?FTS() | A]). + + diff --git a/lib/megaco/test/megaco_codec_prev3b_test.erl b/lib/megaco/test/megaco_codec_prev3b_SUITE.erl index 815a484426..dfa899a883 100644 --- a/lib/megaco/test/megaco_codec_prev3b_test.erl +++ b/lib/megaco/test/megaco_codec_prev3b_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2016. All Rights Reserved. +%% Copyright Ericsson AB 2005-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -22,7 +22,7 @@ %%---------------------------------------------------------------------- %% Purpose: Test encoding/decoding (codec) module of Megaco/H.248 %%---------------------------------------------------------------------- --module(megaco_codec_prev3b_test). +-module(megaco_codec_prev3b_SUITE). %% ---- @@ -35,10 +35,12 @@ -export([msgs/0]). -export([rfc3525_msgs_display/0, rfc3525_msgs_test/0]). --export([t/0, t/1]). +-export([ + suite/0, all/0, groups/0, + init_per_suite/1, end_per_suite/1, + init_per_group/2, end_per_group/2, + init_per_testcase/2, end_per_testcase/2, --export([all/0,groups/0,init_per_group/2,end_per_group/2, - pretty_test_msgs/1, compact_test_msgs/1, @@ -68,8 +70,6 @@ erl_dist_m_test_msgs/1, - tickets/0, - compact_otp4011_msg1/1, compact_otp4011_msg2/1, compact_otp4011_msg3/1, @@ -192,9 +192,8 @@ flex_pretty_otp7431_msg04/1, flex_pretty_otp7431_msg05/1, flex_pretty_otp7431_msg06/1, - flex_pretty_otp7431_msg07/1, - - init_per_testcase/2, end_per_testcase/2]). + flex_pretty_otp7431_msg07/1 + ]). -export([display_text_messages/0, generate_text_messages/0]). @@ -224,53 +223,12 @@ -define(A5556, ["11111111", "11111111", "11111111"]). -%% ---- - -display_text_messages() -> - Msgs = msgs1(text) ++ msgs4(text) ++ msgs5(text) ++ msgs6(text), - %% Msgs = msgs1(text), - %% Msgs = msgs4(text), - %% Msgs = msgs5(text), - %% Msgs = msgs6(text), - megaco_codec_test_lib:display_text_messages(?VERSION, ?EC, Msgs). - - -generate_text_messages() -> - Msgs = msgs1(text) ++ msgs4(text) ++ msgs5(text) ++ msgs6(text), - megaco_codec_test_lib:generate_text_messages(?V3, ?VERSION, ?EC, Msgs). - - -%% ---- - -tickets() -> - %% io:format("~w:tickets -> entry~n", [?MODULE]), - megaco_test_lib:tickets(?MODULE). +%%====================================================================== +%% Common Test interface functions +%%====================================================================== - -%% ---- - -t() -> megaco_test_lib:t(?MODULE). -t(Case) -> megaco_test_lib:t({?MODULE, Case}). - -init_per_testcase(Case, Config) -> - %% CaseString = io_lib:format("~p", [Case]), - C = - case lists:suffix("time_test", atom_to_list(Case)) of - true -> - [{tc_timeout, timer:minutes(10)}|Config]; - false -> - put(verbosity,trc), - Config - end, - megaco_test_lib:init_per_testcase(Case, C). - -end_per_testcase(Case, Config) -> - erase(verbosity), - megaco_test_lib:end_per_testcase(Case, Config). - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% Top test case +suite() -> + [{ct_hooks, [ts_install_cth]}]. all() -> [ @@ -281,100 +239,54 @@ all() -> ]. groups() -> - [{text, [], - [{group, pretty}, - {group, flex_pretty}, - {group, compact}, - {group, flex_compact}]}, - {binary, [], - [{group, bin}, - {group, ber}, - {group, per}]}, - {erl_dist, [], [{group, erl_dist_m}]}, - {pretty, [], [pretty_test_msgs]}, - {compact, [], [compact_test_msgs]}, - {flex_pretty, [], flex_pretty_cases()}, - {flex_compact, [], flex_compact_cases()}, - {bin, [], [bin_test_msgs]}, - {ber, [], [ber_test_msgs]}, - {per, [], [per_test_msgs]}, - {erl_dist_m, [], [erl_dist_m_test_msgs]}, - {tickets, [], - [{group, compact_tickets}, - {group, flex_compact_tickets}, - {group, pretty_tickets}, - {group, flex_pretty_tickets}]}, - {compact_tickets, [], - [compact_otp4011_msg1, compact_otp4011_msg2, - compact_otp4011_msg3, compact_otp4013_msg1, - compact_otp4085_msg1, compact_otp4085_msg2, - compact_otp4280_msg1, compact_otp4299_msg1, - compact_otp4299_msg2, compact_otp4359_msg1, - compact_otp4920_msg0, compact_otp4920_msg1, - compact_otp4920_msg2, compact_otp4920_msg3, - compact_otp4920_msg4, compact_otp4920_msg5, - compact_otp4920_msg6, compact_otp4920_msg7, - compact_otp4920_msg8, compact_otp4920_msg9, - compact_otp4920_msg10, compact_otp4920_msg11, - compact_otp4920_msg12, compact_otp4920_msg20, - compact_otp4920_msg21, compact_otp4920_msg22, - compact_otp4920_msg23, compact_otp4920_msg24, - compact_otp4920_msg25, compact_otp5186_msg01, - compact_otp5186_msg02, compact_otp5186_msg03, - compact_otp5186_msg04, compact_otp5186_msg05, - compact_otp5186_msg06, compact_otp5793_msg01, - compact_otp5836_msg01, compact_otp5993_msg01, - compact_otp5993_msg02, compact_otp5993_msg03, - compact_otp6017_msg01, compact_otp6017_msg02, - compact_otp6017_msg03]}, + [ + {text, [], text_cases()}, + {binary, [], binary_cases()}, + {erl_dist, [], erl_dist_cases()}, + {pretty, [], pretty_cases()}, + {compact, [], compact_cases()}, + {flex_pretty, [], flex_pretty_cases()}, + {flex_compact, [], flex_compact_cases()}, + {bin, [], bin_cases()}, + {ber, [], ber_cases()}, + {per, [], per_cases()}, + {erl_dist_m, [], erl_dist_m_cases()}, + {tickets, [], tickets_cases()}, + {compact_tickets, [], compact_tickets_cases()}, {flex_compact_tickets, [], flex_compact_tickets_cases()}, - {pretty_tickets, [], - [pretty_otp4632_msg1, pretty_otp4632_msg2, - pretty_otp4632_msg3, pretty_otp4632_msg4, - pretty_otp4710_msg1, pretty_otp4710_msg2, - pretty_otp4945_msg1, pretty_otp4945_msg2, - pretty_otp4945_msg3, pretty_otp4945_msg4, - pretty_otp4945_msg5, pretty_otp4945_msg6, - pretty_otp4949_msg1, pretty_otp4949_msg2, - pretty_otp4949_msg3, pretty_otp5042_msg1, - pretty_otp5068_msg1, pretty_otp5085_msg1, - pretty_otp5085_msg2, pretty_otp5085_msg3, - pretty_otp5085_msg4, pretty_otp5085_msg5, - pretty_otp5085_msg6, pretty_otp5085_msg7, - pretty_otp5085_msg8, pretty_otp5600_msg1, - pretty_otp5600_msg2, pretty_otp5601_msg1, - pretty_otp5793_msg01, pretty_otp5803_msg01, - pretty_otp5803_msg02, pretty_otp5805_msg01, - pretty_otp5836_msg01, pretty_otp5882_msg01, - pretty_otp6490_msg01, pretty_otp6490_msg02, - pretty_otp6490_msg03, pretty_otp6490_msg04, - pretty_otp6490_msg05, pretty_otp6490_msg06, - pretty_otp7671_msg01, pretty_otp7671_msg02, - pretty_otp7671_msg03, pretty_otp7671_msg04, - pretty_otp7671_msg05, pretty_otp8114_msg01]}, - {flex_pretty_tickets, [], flex_pretty_tickets_cases()}]. - -init_per_group(flex_pretty_tickets, Config) -> - flex_pretty_init(Config); -init_per_group(flex_compact_tickets, Config) -> - flex_compact_init(Config); -init_per_group(flex_compact, Config) -> - flex_compact_init(Config); -init_per_group(flex_pretty, Config) -> - flex_pretty_init(Config); -init_per_group(_GroupName, Config) -> - Config. + {pretty_tickets, [], pretty_tickets_cases()}, + {flex_pretty_tickets, [], flex_pretty_tickets_cases()} + ]. -end_per_group(flex_pretty_tickets, Config) -> - flex_pretty_finish(Config); -end_per_group(flex_compact_tickets, Config) -> - flex_compact_finish(Config); -end_per_group(flex_compact, Config) -> - flex_compact_finish(Config); -end_per_group(flex_pretty, Config) -> - flex_pretty_finish(Config); -end_per_group(_GroupName, Config) -> - Config. +text_cases() -> + [ + {group, pretty}, + {group, flex_pretty}, + {group, compact}, + {group, flex_compact} + ]. + +binary_cases() -> + [ + {group, bin}, + {group, ber}, + {group, per} + ]. + +erl_dist_cases() -> + [ + {group, erl_dist_m} + ]. + +pretty_cases() -> + [ + pretty_test_msgs + ]. + +compact_cases() -> + [ + compact_test_msgs + ]. flex_pretty_cases() -> [ @@ -394,6 +306,81 @@ flex_compact_cases() -> flex_compact_dm_timers8 ]. +bin_cases() -> + [ + bin_test_msgs + ]. + +ber_cases() -> + [ + ber_test_msgs + ]. + +per_cases() -> + [ + per_test_msgs + ]. + +erl_dist_m_cases() -> + [ + erl_dist_m_test_msgs + ]. + +tickets_cases() -> + [ + {group, compact_tickets}, + {group, flex_compact_tickets}, + {group, pretty_tickets}, + {group, flex_pretty_tickets} + ]. + +compact_tickets_cases() -> + [ + compact_otp4011_msg1, + compact_otp4011_msg2, + compact_otp4011_msg3, + compact_otp4013_msg1, + compact_otp4085_msg1, + compact_otp4085_msg2, + compact_otp4280_msg1, + compact_otp4299_msg1, + compact_otp4299_msg2, + compact_otp4359_msg1, + compact_otp4920_msg0, + compact_otp4920_msg1, + compact_otp4920_msg2, + compact_otp4920_msg3, + compact_otp4920_msg4, + compact_otp4920_msg5, + compact_otp4920_msg6, + compact_otp4920_msg7, + compact_otp4920_msg8, + compact_otp4920_msg9, + compact_otp4920_msg10, + compact_otp4920_msg11, + compact_otp4920_msg12, + compact_otp4920_msg20, + compact_otp4920_msg21, + compact_otp4920_msg22, + compact_otp4920_msg23, + compact_otp4920_msg24, + compact_otp4920_msg25, + compact_otp5186_msg01, + compact_otp5186_msg02, + compact_otp5186_msg03, + compact_otp5186_msg04, + compact_otp5186_msg05, + compact_otp5186_msg06, + compact_otp5793_msg01, + compact_otp5836_msg01, + compact_otp5993_msg01, + compact_otp5993_msg02, + compact_otp5993_msg03, + compact_otp6017_msg01, + compact_otp6017_msg02, + compact_otp6017_msg03 + ]. + flex_compact_tickets_cases() -> [ flex_compact_otp7431_msg01, @@ -405,6 +392,56 @@ flex_compact_tickets_cases() -> flex_compact_otp7431_msg07 ]. +pretty_tickets_cases() -> + [ + pretty_otp4632_msg1, + pretty_otp4632_msg2, + pretty_otp4632_msg3, + pretty_otp4632_msg4, + pretty_otp4710_msg1, + pretty_otp4710_msg2, + pretty_otp4945_msg1, + pretty_otp4945_msg2, + pretty_otp4945_msg3, + pretty_otp4945_msg4, + pretty_otp4945_msg5, + pretty_otp4945_msg6, + pretty_otp4949_msg1, + pretty_otp4949_msg2, + pretty_otp4949_msg3, + pretty_otp5042_msg1, + pretty_otp5068_msg1, + pretty_otp5085_msg1, + pretty_otp5085_msg2, + pretty_otp5085_msg3, + pretty_otp5085_msg4, + pretty_otp5085_msg5, + pretty_otp5085_msg6, + pretty_otp5085_msg7, + pretty_otp5085_msg8, + pretty_otp5600_msg1, + pretty_otp5600_msg2, + pretty_otp5601_msg1, + pretty_otp5793_msg01, + pretty_otp5803_msg01, + pretty_otp5803_msg02, + pretty_otp5805_msg01, + pretty_otp5836_msg01, + pretty_otp5882_msg01, + pretty_otp6490_msg01, + pretty_otp6490_msg02, + pretty_otp6490_msg03, + pretty_otp6490_msg04, + pretty_otp6490_msg05, + pretty_otp6490_msg06, + pretty_otp7671_msg01, + pretty_otp7671_msg02, + pretty_otp7671_msg03, + pretty_otp7671_msg04, + pretty_otp7671_msg05, + pretty_otp8114_msg01 + ]. + flex_pretty_tickets_cases() -> [ flex_pretty_otp5042_msg1, @@ -433,7 +470,123 @@ flex_pretty_tickets_cases() -> flex_pretty_otp7431_msg07 ]. - + + +%% +%% ----- +%% + +init_per_suite(suite) -> + []; +init_per_suite(doc) -> + []; +init_per_suite(Config0) when is_list(Config0) -> + + ?ANNOUNCE_SUITE_INIT(), + + p("init_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + case ?LIB:init_per_suite(Config0) of + {skip, _} = SKIP -> + SKIP; + + Config1 when is_list(Config1) -> + + %% We need a (local) monitor on this node also + megaco_test_sys_monitor:start(), + + p("init_per_suite -> end when" + "~n Config: ~p" + "~n Nodes: ~p", [Config1, erlang:nodes()]), + + Config1 + end. + +end_per_suite(suite) -> []; +end_per_suite(doc) -> []; +end_per_suite(Config0) when is_list(Config0) -> + + p("end_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + megaco_test_sys_monitor:stop(), + Config1 = ?LIB:end_per_suite(Config0), + + p("end_per_suite -> end when" + "~n Nodes: ~p", [erlang:nodes()]), + + Config1. + + +%% +%% ----- +%% + +init_per_group(flex_pretty_tickets = Group, Config) -> + ?ANNOUNCE_GROUP_INIT(Group), + flex_pretty_init(Config); +init_per_group(flex_compact_tickets = Group, Config) -> + ?ANNOUNCE_GROUP_INIT(Group), + flex_compact_init(Config); +init_per_group(flex_compact = Group, Config) -> + ?ANNOUNCE_GROUP_INIT(Group), + flex_compact_init(Config); +init_per_group(flex_pretty = Group, Config) -> + ?ANNOUNCE_GROUP_INIT(Group), + flex_pretty_init(Config); +init_per_group(Group, Config) -> + ?ANNOUNCE_GROUP_INIT(Group), + Config. + +end_per_group(flex_pretty_tickets = _Group, Config) -> + flex_pretty_finish(Config); +end_per_group(flex_compact_tickets = _Group, Config) -> + flex_compact_finish(Config); +end_per_group(flex_compact = _Group, Config) -> + flex_compact_finish(Config); +end_per_group(flex_pretty = _Group, Config) -> + flex_pretty_finish(Config); +end_per_group(_Group, Config) -> + Config. + + + +%% +%% ----- +%% + +init_per_testcase(Case, Config) -> + %% We do *not* reset events with each test case + %% The test cases are so short we don't bother, + %% and also we would drown in mprintouts... + put(verbosity,trc), + megaco_test_lib:init_per_testcase(Case, Config). + +end_per_testcase(Case, Config) -> + erase(verbosity), + megaco_test_lib:end_per_testcase(Case, Config). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +display_text_messages() -> + Msgs = msgs1(text) ++ msgs4(text) ++ msgs5(text) ++ msgs6(text), + %% Msgs = msgs1(text), + %% Msgs = msgs4(text), + %% Msgs = msgs5(text), + %% Msgs = msgs6(text), + megaco_codec_test_lib:display_text_messages(?VERSION, ?EC, Msgs). + + +generate_text_messages() -> + Msgs = msgs1(text) ++ msgs4(text) ++ msgs5(text) ++ msgs6(text), + megaco_codec_test_lib:generate_text_messages(?V3, ?VERSION, ?EC, Msgs). + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% pretty_test_msgs(suite) -> @@ -7627,11 +7780,6 @@ printable(_,_) -> false. -p(true,L,F,A) -> - io:format("~s:" ++ F ++ "~n", [image_of(L)|A]); -p(_,_,_,_) -> - ok. - image_of(trc) -> "TRC"; image_of(dbg) -> @@ -7643,3 +7791,16 @@ image_of(err) -> image_of(L) -> io_lib:format("~p",[L]). + + +p(true,L,F,A) -> + io:format("~s:" ++ F ++ "~n", [image_of(L)|A]); +p(_,_,_,_) -> + ok. + + +p(F, A) -> + io:format("*** [~s] ***" + "~n " ++ F ++ "~n", + [?FTS() | A]). + diff --git a/lib/megaco/test/megaco_codec_prev3c_test.erl b/lib/megaco/test/megaco_codec_prev3c_SUITE.erl index f308ba3774..73756f8e5f 100644 --- a/lib/megaco/test/megaco_codec_prev3c_test.erl +++ b/lib/megaco/test/megaco_codec_prev3c_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2016. All Rights Reserved. +%% Copyright Ericsson AB 2006-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -23,7 +23,7 @@ %% Purpose: Test encoding/decoding (codec) module of Megaco/H.248 %%---------------------------------------------------------------------- --module(megaco_codec_prev3c_test). +-module(megaco_codec_prev3c_SUITE). %% ---- @@ -36,10 +36,12 @@ -export([msgs/0]). -export([rfc3525_msgs_display/0, rfc3525_msgs_test/0]). --export([t/0, t/1]). +-export([ + suite/0, all/0, groups/0, + init_per_suite/1, end_per_suite/1, + init_per_group/2, end_per_group/2, + init_per_testcase/2, end_per_testcase/2, --export([all/0,groups/0,init_per_group/2,end_per_group/2, - pretty_test_msgs/1, compact_test_msgs/1, @@ -70,8 +72,6 @@ erl_dist_m_test_msgs/1, - tickets/0, - compact_otp4011_msg1/1, compact_otp4011_msg2/1, compact_otp4011_msg3/1, @@ -194,9 +194,8 @@ flex_pretty_otp7431_msg04/1, flex_pretty_otp7431_msg05/1, flex_pretty_otp7431_msg06/1, - flex_pretty_otp7431_msg07/1, - - init_per_testcase/2, end_per_testcase/2]). + flex_pretty_otp7431_msg07/1 + ]). -export([display_text_messages/0, generate_text_messages/0]). @@ -226,56 +225,12 @@ -define(A5556, ["11111111", "11111111", "11111111"]). -%% ---- - -display_text_messages() -> - Msgs = - msgs1a(text) ++ - msgs5(text) ++ - msgs6(text) ++ - msgs7(text), - megaco_codec_test_lib:display_text_messages(?VERSION, ?EC, Msgs). - - -generate_text_messages() -> - Msgs = - msgs1a(text) ++ - msgs5(text) ++ - msgs6(text) ++ - msgs7(text), - megaco_codec_test_lib:generate_text_messages(?V3, ?VERSION, ?EC, Msgs). - - -%% ---- - -tickets() -> - %% io:format("~w:tickets -> entry~n", [?MODULE]), - megaco_test_lib:tickets(?MODULE). - -%% ---- +%%====================================================================== +%% Common Test interface functions +%%====================================================================== -t() -> megaco_test_lib:t(?MODULE). -t(Case) -> megaco_test_lib:t({?MODULE, Case}). - -init_per_testcase(Case, Config) -> - %% CaseString = io_lib:format("~p", [Case]), - C = - case lists:suffix("time_test", atom_to_list(Case)) of - true -> - [{tc_timeout, timer:minutes(10)}|Config]; - false -> - put(verbosity,trc), - Config - end, - megaco_test_lib:init_per_testcase(Case, C). - -end_per_testcase(Case, Config) -> - erase(verbosity), - megaco_test_lib:end_per_testcase(Case, Config). - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% Top test case +suite() -> + [{ct_hooks, [ts_install_cth]}]. all() -> [ @@ -286,99 +241,54 @@ all() -> ]. groups() -> - [{text, [], - [{group, pretty}, - {group, flex_pretty}, - {group, compact}, - {group, flex_compact}]}, - {binary, [], - [{group, bin}, - {group, ber}, - {group, per}]}, - {erl_dist, [], [{group, erl_dist_m}]}, - {pretty, [], [pretty_test_msgs]}, - {compact, [], [compact_test_msgs]}, - {flex_pretty, [], flex_pretty_cases()}, - {flex_compact, [], flex_compact_cases()}, - {bin, [], [bin_test_msgs]}, - {ber, [], [ber_test_msgs]}, - {per, [], [per_test_msgs]}, - {erl_dist_m, [], [erl_dist_m_test_msgs]}, - {tickets, [], - [{group, compact_tickets}, - {group, flex_compact_tickets}, - {group, pretty_tickets}, - {group, flex_pretty_tickets}]}, - {compact_tickets, [], - [compact_otp4011_msg1, compact_otp4011_msg2, - compact_otp4011_msg3, compact_otp4013_msg1, - compact_otp4085_msg1, compact_otp4085_msg2, - compact_otp4280_msg1, compact_otp4299_msg1, - compact_otp4359_msg1, compact_otp4920_msg0, - compact_otp4920_msg1, compact_otp4920_msg2, - compact_otp4920_msg3, compact_otp4920_msg4, - compact_otp4920_msg5, compact_otp4920_msg6, - compact_otp4920_msg7, compact_otp4920_msg8, - compact_otp4920_msg9, compact_otp4920_msg10, - compact_otp4920_msg11, compact_otp4920_msg12, - compact_otp4920_msg20, compact_otp4920_msg21, - compact_otp4920_msg22, compact_otp4920_msg23, - compact_otp4920_msg24, compact_otp4920_msg25, - compact_otp5186_msg01, compact_otp5186_msg02, - compact_otp5186_msg03, compact_otp5186_msg04, - compact_otp5186_msg05, compact_otp5186_msg06, - compact_otp5793_msg01, compact_otp5836_msg01, - compact_otp5993_msg01, compact_otp5993_msg02, - compact_otp5993_msg03, compact_otp6017_msg01, - compact_otp6017_msg02, compact_otp6017_msg03]}, + [ + {text, [], text_cases()}, + {binary, [], binary_cases()}, + {erl_dist, [], erl_dist_cases()}, + {pretty, [], pretty_cases()}, + {compact, [], compact_cases()}, + {flex_pretty, [], flex_pretty_cases()}, + {flex_compact, [], flex_compact_cases()}, + {bin, [], bin_cases()}, + {ber, [], ber_cases()}, + {per, [], per_cases()}, + {erl_dist_m, [], erl_dist_m_cases()}, + {tickets, [], tickets_cases()}, + {compact_tickets, [], compact_tickets_cases()}, {flex_compact_tickets, [], flex_compact_tickets_cases()}, - {pretty_tickets, [], - [pretty_otp4632_msg1, pretty_otp4632_msg2, - pretty_otp4632_msg3, pretty_otp4632_msg4, - pretty_otp4710_msg1, pretty_otp4710_msg2, - pretty_otp4945_msg1, pretty_otp4945_msg2, - pretty_otp4945_msg3, pretty_otp4945_msg4, - pretty_otp4945_msg5, pretty_otp4945_msg6, - pretty_otp4949_msg1, pretty_otp4949_msg2, - pretty_otp4949_msg3, pretty_otp5042_msg1, - pretty_otp5068_msg1, pretty_otp5085_msg1, - pretty_otp5085_msg2, pretty_otp5085_msg3, - pretty_otp5085_msg4, pretty_otp5085_msg5, - pretty_otp5085_msg6, pretty_otp5085_msg7, - pretty_otp5085_msg8, pretty_otp5600_msg1, - pretty_otp5600_msg2, pretty_otp5601_msg1, - pretty_otp5793_msg01, pretty_otp5803_msg01, - pretty_otp5803_msg02, pretty_otp5805_msg01, - pretty_otp5836_msg01, pretty_otp5882_msg01, - pretty_otp6490_msg01, pretty_otp6490_msg02, - pretty_otp6490_msg03, pretty_otp6490_msg04, - pretty_otp6490_msg05, pretty_otp6490_msg06, - pretty_otp7671_msg01, pretty_otp7671_msg02, - pretty_otp7671_msg03, pretty_otp7671_msg04, - pretty_otp7671_msg05, pretty_otp8114_msg01]}, - {flex_pretty_tickets, [], flex_pretty_tickets_cases()}]. - -init_per_group(flex_pretty_tickets, Config) -> - flex_pretty_init(Config); -init_per_group(flex_compact_tickets, Config) -> - flex_compact_init(Config); -init_per_group(flex_compact, Config) -> - flex_compact_init(Config); -init_per_group(flex_pretty, Config) -> - flex_pretty_init(Config); -init_per_group(_GroupName, Config) -> - Config. + {pretty_tickets, [], pretty_tickets_cases()}, + {flex_pretty_tickets, [], flex_pretty_tickets_cases()} + ]. -end_per_group(flex_pretty_tickets, Config) -> - flex_pretty_finish(Config); -end_per_group(flex_compact_tickets, Config) -> - flex_compact_finish(Config); -end_per_group(flex_compact, Config) -> - flex_compact_finish(Config); -end_per_group(flex_pretty, Config) -> - flex_pretty_finish(Config); -end_per_group(_GroupName, Config) -> - Config. +text_cases() -> + [ + {group, pretty}, + {group, flex_pretty}, + {group, compact}, + {group, flex_compact} + ]. + +binary_cases() -> + [ + {group, bin}, + {group, ber}, + {group, per} + ]. + +erl_dist_cases() -> + [ + {group, erl_dist_m} + ]. + +pretty_cases() -> + [ + pretty_test_msgs + ]. + +compact_cases() -> + [ + compact_test_msgs + ]. flex_pretty_cases() -> [ @@ -398,6 +308,80 @@ flex_compact_cases() -> flex_compact_dm_timers8 ]. +bin_cases() -> + [ + bin_test_msgs + ]. + +ber_cases() -> + [ + ber_test_msgs + ]. + +per_cases() -> + [ + per_test_msgs + ]. + +erl_dist_m_cases() -> + [ + erl_dist_m_test_msgs + ]. + +tickets_cases() -> + [ + {group, compact_tickets}, + {group, flex_compact_tickets}, + {group, pretty_tickets}, + {group, flex_pretty_tickets} + ]. + +compact_tickets_cases() -> + [ + compact_otp4011_msg1, + compact_otp4011_msg2, + compact_otp4011_msg3, + compact_otp4013_msg1, + compact_otp4085_msg1, + compact_otp4085_msg2, + compact_otp4280_msg1, + compact_otp4299_msg1, + compact_otp4359_msg1, + compact_otp4920_msg0, + compact_otp4920_msg1, + compact_otp4920_msg2, + compact_otp4920_msg3, + compact_otp4920_msg4, + compact_otp4920_msg5, + compact_otp4920_msg6, + compact_otp4920_msg7, + compact_otp4920_msg8, + compact_otp4920_msg9, + compact_otp4920_msg10, + compact_otp4920_msg11, + compact_otp4920_msg12, + compact_otp4920_msg20, + compact_otp4920_msg21, + compact_otp4920_msg22, + compact_otp4920_msg23, + compact_otp4920_msg24, + compact_otp4920_msg25, + compact_otp5186_msg01, + compact_otp5186_msg02, + compact_otp5186_msg03, + compact_otp5186_msg04, + compact_otp5186_msg05, + compact_otp5186_msg06, + compact_otp5793_msg01, + compact_otp5836_msg01, + compact_otp5993_msg01, + compact_otp5993_msg02, + compact_otp5993_msg03, + compact_otp6017_msg01, + compact_otp6017_msg02, + compact_otp6017_msg03 + ]. + flex_compact_tickets_cases() -> [ flex_compact_otp4299_msg1, @@ -410,7 +394,57 @@ flex_compact_tickets_cases() -> flex_compact_otp7431_msg07 ]. -flex_pretty_tickets_cases() -> +pretty_tickets_cases() -> + [ + pretty_otp4632_msg1, + pretty_otp4632_msg2, + pretty_otp4632_msg3, + pretty_otp4632_msg4, + pretty_otp4710_msg1, + pretty_otp4710_msg2, + pretty_otp4945_msg1, + pretty_otp4945_msg2, + pretty_otp4945_msg3, + pretty_otp4945_msg4, + pretty_otp4945_msg5, + pretty_otp4945_msg6, + pretty_otp4949_msg1, + pretty_otp4949_msg2, + pretty_otp4949_msg3, + pretty_otp5042_msg1, + pretty_otp5068_msg1, + pretty_otp5085_msg1, + pretty_otp5085_msg2, + pretty_otp5085_msg3, + pretty_otp5085_msg4, + pretty_otp5085_msg5, + pretty_otp5085_msg6, + pretty_otp5085_msg7, + pretty_otp5085_msg8, + pretty_otp5600_msg1, + pretty_otp5600_msg2, + pretty_otp5601_msg1, + pretty_otp5793_msg01, + pretty_otp5803_msg01, + pretty_otp5803_msg02, + pretty_otp5805_msg01, + pretty_otp5836_msg01, + pretty_otp5882_msg01, + pretty_otp6490_msg01, + pretty_otp6490_msg02, + pretty_otp6490_msg03, + pretty_otp6490_msg04, + pretty_otp6490_msg05, + pretty_otp6490_msg06, + pretty_otp7671_msg01, + pretty_otp7671_msg02, + pretty_otp7671_msg03, + pretty_otp7671_msg04, + pretty_otp7671_msg05, + pretty_otp8114_msg01 + ]. + +flex_pretty_tickets_cases() -> [ flex_pretty_otp5042_msg1, flex_pretty_otp5085_msg1, @@ -438,7 +472,125 @@ flex_pretty_tickets_cases() -> flex_pretty_otp7431_msg07 ]. - + +%% +%% ----- +%% + +init_per_suite(suite) -> + []; +init_per_suite(doc) -> + []; +init_per_suite(Config0) when is_list(Config0) -> + + ?ANNOUNCE_SUITE_INIT(), + + p("init_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + case ?LIB:init_per_suite(Config0) of + {skip, _} = SKIP -> + SKIP; + + Config1 when is_list(Config1) -> + + %% We need a (local) monitor on this node also + megaco_test_sys_monitor:start(), + + p("init_per_suite -> end when" + "~n Config: ~p" + "~n Nodes: ~p", [Config1, erlang:nodes()]), + + Config1 + end. + +end_per_suite(suite) -> []; +end_per_suite(doc) -> []; +end_per_suite(Config0) when is_list(Config0) -> + + p("end_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + megaco_test_sys_monitor:stop(), + Config1 = ?LIB:end_per_suite(Config0), + + p("end_per_suite -> end when" + "~n Nodes: ~p", [erlang:nodes()]), + + Config1. + + +%% +%% ----- +%% + +init_per_group(flex_pretty_tickets = Group, Config) -> + ?ANNOUNCE_GROUP_INIT(Group), + flex_pretty_init(Config); +init_per_group(flex_compact_tickets = Group, Config) -> + ?ANNOUNCE_GROUP_INIT(Group), + flex_compact_init(Config); +init_per_group(flex_compact = Group, Config) -> + ?ANNOUNCE_GROUP_INIT(Group), + flex_compact_init(Config); +init_per_group(flex_pretty = Group, Config) -> + ?ANNOUNCE_GROUP_INIT(Group), + flex_pretty_init(Config); +init_per_group(Group, Config) -> + ?ANNOUNCE_GROUP_INIT(Group), + Config. + +end_per_group(flex_pretty_tickets = _Group, Config) -> + flex_pretty_finish(Config); +end_per_group(flex_compact_tickets = _Group, Config) -> + flex_compact_finish(Config); +end_per_group(flex_compact, Config = _Group) -> + flex_compact_finish(Config); +end_per_group(flex_pretty, Config = _Group) -> + flex_pretty_finish(Config); +end_per_group(_Group, Config) -> + Config. + + + +%% +%% ----- +%% + +init_per_testcase(Case, Config) -> + %% We do *not* reset events with each test case + %% The test cases are so short we don't bother, + %% and also we would drown in mprintouts... + put(verbosity,trc), + megaco_test_lib:init_per_testcase(Case, Config). + +end_per_testcase(Case, Config) -> + erase(verbosity), + megaco_test_lib:end_per_testcase(Case, Config). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +display_text_messages() -> + Msgs = + msgs1a(text) ++ + msgs5(text) ++ + msgs6(text) ++ + msgs7(text), + megaco_codec_test_lib:display_text_messages(?VERSION, ?EC, Msgs). + + +generate_text_messages() -> + Msgs = + msgs1a(text) ++ + msgs5(text) ++ + msgs6(text) ++ + msgs7(text), + megaco_codec_test_lib:generate_text_messages(?V3, ?VERSION, ?EC, Msgs). + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% pretty_test_msgs(suite) -> @@ -8123,11 +8275,6 @@ printable(_,_) -> false. -p(true,L,F,A) -> - io:format("~s:" ++ F ++ "~n", [image_of(L)|A]); -p(_,_,_,_) -> - ok. - image_of(trc) -> "TRC"; image_of(dbg) -> @@ -8139,3 +8286,15 @@ image_of(err) -> image_of(L) -> io_lib:format("~p",[L]). + +p(true,L,F,A) -> + io:format("~s:" ++ F ++ "~n", [image_of(L)|A]); +p(_,_,_,_) -> + ok. + + +p(F, A) -> + io:format("*** [~s] ***" + "~n " ++ F ++ "~n", + [?FTS() | A]). + diff --git a/lib/megaco/test/megaco_codec_test.erl b/lib/megaco/test/megaco_codec_test.erl deleted file mode 100644 index 0dfbabcc81..0000000000 --- a/lib/megaco/test/megaco_codec_test.erl +++ /dev/null @@ -1,82 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2003-2019. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% %CopyrightEnd% -%% - -%% -%%---------------------------------------------------------------------- -%% Purpose: Test application config -%%---------------------------------------------------------------------- - --module(megaco_codec_test). - --export([ - all/0, - groups/0, - - init_per_group/2, - end_per_group/2, - init_per_testcase/2, - end_per_testcase/2, - - t/0, t/1, - init/0 - ]). - --include("megaco_test_lib.hrl"). --include_lib("megaco/include/megaco.hrl"). - -t() -> megaco_test_lib:t(?MODULE). -t(Case) -> megaco_test_lib:t({?MODULE, Case}). - -%% Test server callbacks -init_per_testcase(Case, Config) -> - megaco_test_lib:init_per_testcase(Case, Config). - -end_per_testcase(Case, Config) -> - megaco_test_lib:end_per_testcase(Case, Config). - -init() -> - process_flag(trap_exit, true), - megaco_test_lib:flush(). - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% Top test case - -all() -> - [{group, codec}]. - -groups() -> - [{codec, [], - [{megaco_codec_mini_test, all}, - {megaco_codec_v1_test, all}, - {megaco_codec_v2_test, all}, - {megaco_codec_prev3a_test, all}, - {megaco_codec_prev3b_test, all}, - {megaco_codec_prev3c_test, all}, - {megaco_codec_v3_test, all}]}]. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - - diff --git a/lib/megaco/test/megaco_codec_v1_test.erl b/lib/megaco/test/megaco_codec_v1_SUITE.erl index 3548a08984..c458e0b579 100644 --- a/lib/megaco/test/megaco_codec_v1_test.erl +++ b/lib/megaco/test/megaco_codec_v1_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2016. All Rights Reserved. +%% Copyright Ericsson AB 2003-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -23,7 +23,7 @@ %% Purpose: Test encoding/decoding (codec) module of Megaco/H.248 v1 %%---------------------------------------------------------------------- --module(megaco_codec_v1_test). +-module(megaco_codec_v1_SUITE). %% ---- @@ -37,14 +37,15 @@ %% ---- -%% -export([msg/0]). -export([msgs/0]). -export([rfc3525_msgs_display/0, rfc3525_msgs_test/0]). --export([t/0, t/1]). +-export([ + suite/0, all/0, groups/0, + init_per_suite/1, end_per_suite/1, + init_per_group/2, end_per_group/2, + init_per_testcase/2, end_per_testcase/2, --export([all/0,groups/0,init_per_group/2,end_per_group/2, - pretty_test_msgs/1, compact_test_msgs/1, @@ -71,8 +72,6 @@ erl_dist_m_test_msgs/1, - tickets/0, - compact_otp4011_msg1/1, compact_otp4011_msg2/1, compact_otp4011_msg3/1, @@ -184,9 +183,8 @@ flex_pretty_otp7431_msg04/1, flex_pretty_otp7431_msg05/1, flex_pretty_otp7431_msg06/1, - flex_pretty_otp7431_msg07/1, - - init_per_testcase/2, end_per_testcase/2]). + flex_pretty_otp7431_msg07/1 + ]). -export([display_text_messages/0, generate_text_messages/0]). @@ -239,7 +237,340 @@ -define(A5556, ["11111111", "11111111", "11111111"]). -%% ---- +%%====================================================================== +%% Common Test interface functions +%%====================================================================== + +suite() -> + [{ct_hooks, [ts_install_cth]}]. + +all() -> + [ + {group, text}, + {group, binary}, + {group, erl_dist}, + {group, tickets} + ]. + +groups() -> + [ + {text, [], text_cases()}, + {binary, [], binary_cases()}, + {erl_dist, [], erl_dist_cases()}, + {pretty, [], pretty_cases()}, + {compact, [], compact_cases()}, + {flex_pretty, [], flex_pretty_cases()}, + {flex_compact, [], flex_compact_cases()}, + {bin, [], bin_cases()}, + {ber, [], ber_cases()}, + {per, [], per_cases()}, + {erl_dist_m, [], erl_dist_m_cases()}, + {tickets, [], tickets_cases()}, + {compact_tickets, [], compact_tickets_cases()}, + {flex_compact_tickets, [], flex_compact_tickets_cases()}, + {pretty_tickets, [], pretty_tickets_cases()}, + {flex_pretty_tickets, [], flex_pretty_tickets_cases()} + ]. + +text_cases() -> + [ + {group, pretty}, + {group, flex_pretty}, + {group, compact}, + {group, flex_compact} + ]. + +binary_cases() -> + [ + {group, bin}, + {group, ber}, + {group, per} + ]. + +erl_dist_cases() -> + [ + {group, erl_dist_m} + ]. + +pretty_cases() -> + [ + pretty_test_msgs + ]. + +compact_cases() -> + [ + compact_test_msgs + ]. + +flex_pretty_cases() -> + [ + flex_pretty_test_msgs + ]. + +flex_compact_cases() -> + [ + flex_compact_test_msgs, + flex_compact_dm_timers1, + flex_compact_dm_timers2, + flex_compact_dm_timers3, + flex_compact_dm_timers4, + flex_compact_dm_timers5, + flex_compact_dm_timers6 + ]. + +bin_cases() -> + [ + bin_test_msgs + ]. + +ber_cases() -> + [ + ber_test_msgs + ]. + +per_cases() -> + [ + per_test_msgs + ]. + +erl_dist_m_cases() -> + [ + erl_dist_m_test_msgs + ]. + +tickets_cases() -> + [ + {group, compact_tickets}, + {group, pretty_tickets}, + {group, flex_compact_tickets}, + {group, flex_pretty_tickets} + ]. + +compact_tickets_cases() -> + [ + compact_otp4011_msg1, + compact_otp4011_msg2, + compact_otp4011_msg3, + compact_otp4013_msg1, + compact_otp4085_msg1, + compact_otp4085_msg2, + compact_otp4280_msg1, + compact_otp4299_msg1, + compact_otp4299_msg2, + compact_otp4359_msg1, + compact_otp4920_msg0, + compact_otp4920_msg1, + compact_otp4920_msg2, + compact_otp4920_msg3, + compact_otp4920_msg4, + compact_otp4920_msg5, + compact_otp4920_msg6, + compact_otp4920_msg7, + compact_otp4920_msg8, + compact_otp4920_msg9, + compact_otp4920_msg10, + compact_otp4920_msg11, + compact_otp4920_msg12, + compact_otp4920_msg20, + compact_otp4920_msg21, + compact_otp4920_msg22, + compact_otp4920_msg23, + compact_otp4920_msg24, + compact_otp4920_msg25, + compact_otp5186_msg01, + compact_otp5186_msg02, + compact_otp5186_msg03, + compact_otp5186_msg04, + compact_otp5186_msg05, + compact_otp5186_msg06, + compact_otp5793_msg01, + compact_otp5993_msg01, + compact_otp5993_msg02, + compact_otp5993_msg03, + compact_otp6017_msg01, + compact_otp6017_msg02, + compact_otp6017_msg03 + ]. + +flex_compact_tickets_cases() -> + [ + flex_compact_otp7431_msg01a, + flex_compact_otp7431_msg01b, + flex_compact_otp7431_msg02, + flex_compact_otp7431_msg03, + flex_compact_otp7431_msg04, + flex_compact_otp7431_msg05, + flex_compact_otp7431_msg06, + flex_compact_otp7431_msg07 + ]. + +pretty_tickets_cases() -> + [ + pretty_otp4632_msg1, + pretty_otp4632_msg2, + pretty_otp4632_msg3, + pretty_otp4632_msg4, + pretty_otp4710_msg1, + pretty_otp4710_msg2, + pretty_otp4945_msg1, + pretty_otp4945_msg2, + pretty_otp4945_msg3, + pretty_otp4945_msg4, + pretty_otp4945_msg5, + pretty_otp4945_msg6, + pretty_otp4949_msg1, + pretty_otp4949_msg2, + pretty_otp4949_msg3, + pretty_otp5042_msg1, + pretty_otp5068_msg1, + pretty_otp5085_msg1, + pretty_otp5085_msg2, + pretty_otp5085_msg3, + pretty_otp5085_msg4, + pretty_otp5085_msg5, + pretty_otp5085_msg6, + pretty_otp5085_msg7, + pretty_otp5600_msg1, + pretty_otp5600_msg2, + pretty_otp5601_msg1, + pretty_otp5793_msg01, + pretty_otp5882_msg01, + pretty_otp6490_msg01, + pretty_otp6490_msg02, + pretty_otp6490_msg03, + pretty_otp6490_msg04, + pretty_otp6490_msg05, + pretty_otp6490_msg06, + pretty_otp7671_msg01, + pretty_otp7671_msg02, + pretty_otp7671_msg03, + pretty_otp7671_msg04, + pretty_otp7671_msg05 + ]. + +flex_pretty_tickets_cases() -> + [ + flex_pretty_otp5042_msg1, + flex_pretty_otp5085_msg1, + flex_pretty_otp5085_msg2, + flex_pretty_otp5085_msg3, + flex_pretty_otp5085_msg4, + flex_pretty_otp5085_msg5, + flex_pretty_otp5085_msg6, + flex_pretty_otp5085_msg7, + flex_pretty_otp5600_msg1, + flex_pretty_otp5600_msg2, + flex_pretty_otp5601_msg1, + flex_pretty_otp5793_msg01, + flex_pretty_otp7431_msg01, + flex_pretty_otp7431_msg02, + flex_pretty_otp7431_msg03, + flex_pretty_otp7431_msg04, + flex_pretty_otp7431_msg05, + flex_pretty_otp7431_msg06, + flex_pretty_otp7431_msg07 + ]. + + +%% +%% ----- +%% + +init_per_suite(suite) -> + []; +init_per_suite(doc) -> + []; +init_per_suite(Config0) when is_list(Config0) -> + + ?ANNOUNCE_SUITE_INIT(), + + p("init_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + case ?LIB:init_per_suite(Config0) of + {skip, _} = SKIP -> + SKIP; + + Config1 when is_list(Config1) -> + + %% We need a (local) monitor on this node also + megaco_test_sys_monitor:start(), + + p("init_per_suite -> end when" + "~n Config: ~p" + "~n Nodes: ~p", [Config1, erlang:nodes()]), + + Config1 + end. + +end_per_suite(suite) -> []; +end_per_suite(doc) -> []; +end_per_suite(Config0) when is_list(Config0) -> + + p("end_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + megaco_test_sys_monitor:stop(), + Config1 = ?LIB:end_per_suite(Config0), + + p("end_per_suite -> end when" + "~n Nodes: ~p", [erlang:nodes()]), + + Config1. + + +%% +%% ----- +%% + +init_per_group(flex_pretty_tickets = Group, Config) -> + ?ANNOUNCE_GROUP_INIT(Group), + flex_pretty_init(Config); +init_per_group(flex_compact_tickets = Group, Config) -> + ?ANNOUNCE_GROUP_INIT(Group), + flex_compact_init(Config); +init_per_group(flex_compact = Group, Config) -> + ?ANNOUNCE_GROUP_INIT(Group), + flex_compact_init(Config); +init_per_group(flex_pretty = Group, Config) -> + ?ANNOUNCE_GROUP_INIT(Group), + flex_pretty_init(Config); +init_per_group(Group, Config) -> + ?ANNOUNCE_GROUP_INIT(Group), + Config. + +end_per_group(flex_pretty_tickets, Config) -> + flex_pretty_finish(Config); +end_per_group(flex_compact_tickets, Config) -> + flex_compact_finish(Config); +end_per_group(flex_compact, Config) -> + flex_compact_finish(Config); +end_per_group(flex_pretty, Config) -> + flex_pretty_finish(Config); +end_per_group(_GroupName, Config) -> + Config. + + + +%% +%% ----- +%% + +init_per_testcase(Case, Config) -> + %% We do *not* reset events with each test case + %% The test cases are so short we don't bother, + %% and also we would drown in mprintouts... + put(verbosity, trc), + megaco_test_lib:init_per_testcase(Case, Config). + +end_per_testcase(Case, Config) -> + erase(verbosity), + megaco_test_lib:end_per_testcase(Case, Config). + + +%%====================================================================== display_text_messages() -> Msgs = @@ -415,226 +746,6 @@ decode_text_messages(Codec, Config, [Msg|Msgs], Acc) -> decode_text_messages(Codec, Config, Msgs, [Res | Acc]). -%% ---- - -tickets() -> - %% io:format("~w:tickets -> entry~n", [?MODULE]), - megaco_test_lib:tickets(?MODULE). - - -%% ---- - -t() -> megaco_test_lib:t(?MODULE). -t(Case) -> megaco_test_lib:t({?MODULE, Case}). - -init_per_testcase(Case, Config) -> - %% CaseString = io_lib:format("~p", [Case]), - C = - case lists:suffix("time_test", atom_to_list(Case)) of - true -> - [{tc_timeout, timer:minutes(10)}|Config]; - false -> - put(verbosity,trc), - Config - end, - megaco_test_lib:init_per_testcase(Case, C). - -end_per_testcase(Case, Config) -> - erase(verbosity), - megaco_test_lib:end_per_testcase(Case, Config). - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% Top test case - -all() -> - [ - {group, text}, - {group, binary}, - {group, erl_dist}, - {group, tickets} - ]. - -groups() -> - [{text, [], [{group, pretty}, - {group, flex_pretty}, - {group, compact}, - {group, flex_compact}]}, - {binary, [], [{group, bin}, - {group, ber}, - {group, per}]}, - {erl_dist, [], [{group, erl_dist_m}]}, - {pretty, [], [pretty_test_msgs]}, - {compact, [], [compact_test_msgs]}, - {flex_pretty, [], flex_pretty_cases()}, - {flex_compact, [], flex_compact_cases()}, - {bin, [], [bin_test_msgs]}, - {ber, [], [ber_test_msgs]}, - {per, [], [per_test_msgs]}, - {erl_dist_m, [], [erl_dist_m_test_msgs]}, - {tickets, [], [{group, compact_tickets}, - {group, pretty_tickets}, - {group, flex_compact_tickets}, - {group, flex_pretty_tickets}]}, - {compact_tickets, [], [compact_otp4011_msg1, - compact_otp4011_msg2, - compact_otp4011_msg3, - compact_otp4013_msg1, - compact_otp4085_msg1, - compact_otp4085_msg2, - compact_otp4280_msg1, - compact_otp4299_msg1, - compact_otp4299_msg2, - compact_otp4359_msg1, - compact_otp4920_msg0, - compact_otp4920_msg1, - compact_otp4920_msg2, - compact_otp4920_msg3, - compact_otp4920_msg4, - compact_otp4920_msg5, - compact_otp4920_msg6, - compact_otp4920_msg7, - compact_otp4920_msg8, - compact_otp4920_msg9, - compact_otp4920_msg10, - compact_otp4920_msg11, - compact_otp4920_msg12, - compact_otp4920_msg20, - compact_otp4920_msg21, - compact_otp4920_msg22, - compact_otp4920_msg23, - compact_otp4920_msg24, - compact_otp4920_msg25, - compact_otp5186_msg01, - compact_otp5186_msg02, - compact_otp5186_msg03, - compact_otp5186_msg04, - compact_otp5186_msg05, - compact_otp5186_msg06, - compact_otp5793_msg01, - compact_otp5993_msg01, - compact_otp5993_msg02, - compact_otp5993_msg03, - compact_otp6017_msg01, - compact_otp6017_msg02, - compact_otp6017_msg03]}, - {flex_compact_tickets, [], flex_compact_tickets_cases()}, - {pretty_tickets, [], [pretty_otp4632_msg1, - pretty_otp4632_msg2, - pretty_otp4632_msg3, - pretty_otp4632_msg4, - pretty_otp4710_msg1, - pretty_otp4710_msg2, - pretty_otp4945_msg1, - pretty_otp4945_msg2, - pretty_otp4945_msg3, - pretty_otp4945_msg4, - pretty_otp4945_msg5, - pretty_otp4945_msg6, - pretty_otp4949_msg1, - pretty_otp4949_msg2, - pretty_otp4949_msg3, - pretty_otp5042_msg1, - pretty_otp5068_msg1, - pretty_otp5085_msg1, - pretty_otp5085_msg2, - pretty_otp5085_msg3, - pretty_otp5085_msg4, - pretty_otp5085_msg5, - pretty_otp5085_msg6, - pretty_otp5085_msg7, - pretty_otp5600_msg1, - pretty_otp5600_msg2, - pretty_otp5601_msg1, - pretty_otp5793_msg01, - pretty_otp5882_msg01, - pretty_otp6490_msg01, - pretty_otp6490_msg02, - pretty_otp6490_msg03, - pretty_otp6490_msg04, - pretty_otp6490_msg05, - pretty_otp6490_msg06, - pretty_otp7671_msg01, - pretty_otp7671_msg02, - pretty_otp7671_msg03, - pretty_otp7671_msg04, - pretty_otp7671_msg05]}, - {flex_pretty_tickets, [], flex_pretty_tickets_cases()}]. - -init_per_group(flex_pretty_tickets, Config) -> - flex_pretty_init(Config); -init_per_group(flex_compact_tickets, Config) -> - flex_compact_init(Config); -init_per_group(flex_compact, Config) -> - flex_compact_init(Config); -init_per_group(flex_pretty, Config) -> - flex_pretty_init(Config); -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(flex_pretty_tickets, Config) -> - flex_pretty_finish(Config); -end_per_group(flex_compact_tickets, Config) -> - flex_compact_finish(Config); -end_per_group(flex_compact, Config) -> - flex_compact_finish(Config); -end_per_group(flex_pretty, Config) -> - flex_pretty_finish(Config); -end_per_group(_GroupName, Config) -> - Config. - -flex_pretty_cases() -> - [ - flex_pretty_test_msgs - ]. - -flex_compact_cases() -> - [ - flex_compact_test_msgs, - flex_compact_dm_timers1, - flex_compact_dm_timers2, - flex_compact_dm_timers3, - flex_compact_dm_timers4, - flex_compact_dm_timers5, - flex_compact_dm_timers6 - ]. - -flex_compact_tickets_cases() -> - [ - flex_compact_otp7431_msg01a, - flex_compact_otp7431_msg01b, - flex_compact_otp7431_msg02, - flex_compact_otp7431_msg03, - flex_compact_otp7431_msg04, - flex_compact_otp7431_msg05, - flex_compact_otp7431_msg06, - flex_compact_otp7431_msg07 - ]. - -flex_pretty_tickets_cases() -> - [ - flex_pretty_otp5042_msg1, - flex_pretty_otp5085_msg1, - flex_pretty_otp5085_msg2, - flex_pretty_otp5085_msg3, - flex_pretty_otp5085_msg4, - flex_pretty_otp5085_msg5, - flex_pretty_otp5085_msg6, - flex_pretty_otp5085_msg7, - flex_pretty_otp5600_msg1, - flex_pretty_otp5600_msg2, - flex_pretty_otp5601_msg1, - flex_pretty_otp5793_msg01, - flex_pretty_otp7431_msg01, - flex_pretty_otp7431_msg02, - flex_pretty_otp7431_msg03, - flex_pretty_otp7431_msg04, - flex_pretty_otp7431_msg05, - flex_pretty_otp7431_msg06, - flex_pretty_otp7431_msg07 - ]. - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -7145,11 +7256,6 @@ printable(_,_) -> false. -p(true,L,F,A) -> - io:format("~s: " ++ F ++ "~n", [image_of(L)|A]); -p(_,_,_,_) -> - ok. - image_of(trc) -> "T"; image_of(dbg) -> @@ -7162,3 +7268,14 @@ image_of(L) -> io_lib:format("~p",[L]). +p(true,L,F,A) -> + io:format("~s: " ++ F ++ "~n", [image_of(L)|A]); +p(_,_,_,_) -> + ok. + +p(F, A) -> + io:format("*** [~s] ***" + "~n " ++ F ++ "~n", + [?FTS() | A]). + + diff --git a/lib/megaco/test/megaco_codec_v2_test.erl b/lib/megaco/test/megaco_codec_v2_SUITE.erl index fc3b1f886c..8a381804c3 100644 --- a/lib/megaco/test/megaco_codec_v2_test.erl +++ b/lib/megaco/test/megaco_codec_v2_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2016. All Rights Reserved. +%% Copyright Ericsson AB 2003-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -23,7 +23,7 @@ %% Purpose: Test encoding/decoding (codec) module of Megaco/H.248 %%---------------------------------------------------------------------- --module(megaco_codec_v2_test). +-module(megaco_codec_v2_SUITE). %% ---- @@ -36,10 +36,11 @@ -export([msgs/0]). -export([rfc3525_msgs_display/0, rfc3525_msgs_test/0]). --export([t/0, t/1]). - --export([all/0, - groups/0, init_per_group/2, end_per_group/2, +-export([ + suite/0, all/0, groups/0, + init_per_suite/1, end_per_suite/1, + init_per_group/2, end_per_group/2, + init_per_testcase/2, end_per_testcase/2, pretty_test_msgs/1, @@ -70,8 +71,6 @@ erl_dist_m_test_msgs/1, - tickets/0, - compact_otp4011_msg1/1, compact_otp4011_msg2/1, compact_otp4011_msg3/1, @@ -205,9 +204,8 @@ flex_pretty_otp7431_msg04/1, flex_pretty_otp7431_msg05/1, flex_pretty_otp7431_msg06/1, - flex_pretty_otp7431_msg07/1, - - init_per_testcase/2, end_per_testcase/2]). + flex_pretty_otp7431_msg07/1 + ]). -export([display_text_messages/0, generate_text_messages/0]). @@ -249,7 +247,398 @@ -define(A5556, ["11111111", "11111111", "11111111"]). -%% ---- +%%====================================================================== +%% Common Test interface functions +%%====================================================================== + +suite() -> + [{ct_hooks, [ts_install_cth]}]. + +all() -> + [ + {group, text}, + {group, binary}, + {group, erl_dist}, + {group, tickets} + ]. + +groups() -> + [ + {text, [], text_cases()}, + {binary, [], binary_cases()}, + {erl_dist, [], erl_dist_cases()}, + {pretty, [], pretty_cases()}, + {compact, [], compact_cases()}, + {flex_pretty, [], flex_pretty_cases()}, + {flex_compact, [], flex_compact_cases()}, + {bin, [], bin_cases()}, + {ber, [], ber_cases()}, + {per, [], per_cases()}, + {erl_dist_m, [], erl_dist_m_cases()}, + {tickets, [], tickets_cases()}, + {compact_tickets, [], compact_tickets_cases()}, + {flex_compact_tickets, [], flex_compact_tickets_cases()}, + {pretty_tickets, [], pretty_tickets_cases()}, + {flex_pretty_tickets, [], flex_pretty_tickets_cases()} + ]. + + +text_cases() -> + [ + {group, pretty}, + {group, flex_pretty}, + {group, compact}, + {group, flex_compact} + ]. + +binary_cases() -> + [ + {group, bin}, + {group, ber}, + {group, per} + ]. + +erl_dist_cases() -> + [ + {group, erl_dist_m} + ]. + +pretty_cases() -> + [ + pretty_test_msgs + ]. + +compact_cases() -> + [ + compact_test_msgs + ]. + +flex_pretty_cases() -> + [ + flex_pretty_test_msgs + ]. + +flex_compact_cases() -> + [ + flex_compact_test_msgs, + flex_compact_dm_timers1, + flex_compact_dm_timers2, + flex_compact_dm_timers3, + flex_compact_dm_timers4, + flex_compact_dm_timers5, + flex_compact_dm_timers6, + flex_compact_dm_timers7, + flex_compact_dm_timers8 + ]. + +bin_cases() -> + [ + bin_test_msgs + ]. + +ber_cases() -> + [ + ber_test_msgs + ]. + +per_cases() -> + [ + per_test_msgs + ]. + +erl_dist_m_cases() -> + [ + erl_dist_m_test_msgs + ]. + +tickets_cases() -> + [ + {group, compact_tickets}, + {group, pretty_tickets}, + {group, flex_compact_tickets}, + {group, flex_pretty_tickets} + ]. + +compact_tickets_cases() -> + [ + compact_otp4011_msg1, + compact_otp4011_msg2, + compact_otp4011_msg3, + compact_otp4013_msg1, + + compact_otp4085_msg1, + compact_otp4085_msg2, + + compact_otp4280_msg1, + + compact_otp4299_msg1, + compact_otp4299_msg2, + + compact_otp4359_msg1, + + compact_otp4920_msg0, + compact_otp4920_msg1, + compact_otp4920_msg2, + compact_otp4920_msg3, + compact_otp4920_msg4, + compact_otp4920_msg5, + compact_otp4920_msg6, + compact_otp4920_msg7, + compact_otp4920_msg8, + compact_otp4920_msg9, + compact_otp4920_msg10, + compact_otp4920_msg11, + compact_otp4920_msg12, + compact_otp4920_msg20, + compact_otp4920_msg21, + compact_otp4920_msg22, + compact_otp4920_msg23, + compact_otp4920_msg24, + compact_otp4920_msg25, + + compact_otp5186_msg01, + compact_otp5186_msg02, + compact_otp5186_msg03, + compact_otp5186_msg04, + compact_otp5186_msg05, + compact_otp5186_msg06, + + compact_otp5290_msg01, + compact_otp5290_msg02, + + compact_otp5793_msg01, + + compact_otp5993_msg01, + compact_otp5993_msg02, + compact_otp5993_msg03, + + compact_otp6017_msg01, + compact_otp6017_msg02, + compact_otp6017_msg03, + + compact_otp7138_msg01, + compact_otp7138_msg02, + + compact_otp7457_msg01, + compact_otp7457_msg02, + compact_otp7457_msg03, + + compact_otp7534_msg01, + + compact_otp7576_msg01, + + compact_otp7671_msg01 + ]. + +flex_compact_tickets_cases() -> + [ + flex_compact_otp7138_msg01, + flex_compact_otp7138_msg02, + flex_compact_otp7431_msg01, + flex_compact_otp7431_msg02, + flex_compact_otp7431_msg03, + flex_compact_otp7431_msg04, + flex_compact_otp7431_msg05, + flex_compact_otp7431_msg06, + flex_compact_otp7431_msg07, + flex_compact_otp7138_msg02, + flex_compact_otp7457_msg01, + flex_compact_otp7457_msg02, + flex_compact_otp7457_msg03, + flex_compact_otp7534_msg01, + flex_compact_otp7573_msg01, + flex_compact_otp7576_msg01, + flex_compact_otp10998_msg01, + flex_compact_otp10998_msg02, + flex_compact_otp10998_msg03, + flex_compact_otp10998_msg04 + ]. + +pretty_tickets_cases() -> + [ + pretty_otp4632_msg1, + pretty_otp4632_msg2, + pretty_otp4632_msg3, + pretty_otp4632_msg4, + + pretty_otp4710_msg1, + pretty_otp4710_msg2, + + pretty_otp4945_msg1, + pretty_otp4945_msg2, + pretty_otp4945_msg3, + pretty_otp4945_msg4, + pretty_otp4945_msg5, + pretty_otp4945_msg6, + + pretty_otp4949_msg1, + pretty_otp4949_msg2, + pretty_otp4949_msg3, + + pretty_otp5042_msg1, + + pretty_otp5068_msg1, + + pretty_otp5085_msg1, + pretty_otp5085_msg2, + pretty_otp5085_msg3, + pretty_otp5085_msg4, + pretty_otp5085_msg5, + pretty_otp5085_msg6, + pretty_otp5085_msg7, + + pretty_otp5600_msg1, + pretty_otp5600_msg2, + + pretty_otp5601_msg1, + + pretty_otp5793_msg01, + + pretty_otp5882_msg01, + + pretty_otp6490_msg01, + pretty_otp6490_msg02, + pretty_otp6490_msg03, + pretty_otp6490_msg04, + pretty_otp6490_msg05, + pretty_otp6490_msg06, + + pretty_otp7249_msg01, + + pretty_otp7671_msg01, + pretty_otp7671_msg02, + pretty_otp7671_msg03, + pretty_otp7671_msg04, + pretty_otp7671_msg05 + ]. + + +flex_pretty_tickets_cases() -> + [ + flex_pretty_otp5042_msg1, + flex_pretty_otp5085_msg1, + flex_pretty_otp5085_msg2, + flex_pretty_otp5085_msg3, + flex_pretty_otp5085_msg4, + flex_pretty_otp5085_msg5, + flex_pretty_otp5085_msg6, + flex_pretty_otp5085_msg7, + flex_pretty_otp5600_msg1, + flex_pretty_otp5600_msg2, + flex_pretty_otp5601_msg1, + flex_pretty_otp5793_msg01, + flex_pretty_otp7431_msg01, + flex_pretty_otp7431_msg02, + flex_pretty_otp7431_msg03, + flex_pretty_otp7431_msg04, + flex_pretty_otp7431_msg05, + flex_pretty_otp7431_msg06, + flex_pretty_otp7431_msg07 + ]. + + + +%% +%% ----- +%% + +init_per_suite(suite) -> + []; +init_per_suite(doc) -> + []; +init_per_suite(Config0) when is_list(Config0) -> + + ?ANNOUNCE_SUITE_INIT(), + + p("init_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + case ?LIB:init_per_suite(Config0) of + {skip, _} = SKIP -> + SKIP; + + Config1 when is_list(Config1) -> + + %% We need a (local) monitor on this node also + megaco_test_sys_monitor:start(), + + p("init_per_suite -> end when" + "~n Config: ~p" + "~n Nodes: ~p", [Config1, erlang:nodes()]), + + Config1 + end. + +end_per_suite(suite) -> []; +end_per_suite(doc) -> []; +end_per_suite(Config0) when is_list(Config0) -> + + p("end_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + megaco_test_sys_monitor:stop(), + Config1 = ?LIB:end_per_suite(Config0), + + p("end_per_suite -> end when" + "~n Nodes: ~p", [erlang:nodes()]), + + Config1. + + +%% +%% ----- +%% + +init_per_group(flex_pretty_tickets = Group, Config) -> + ?ANNOUNCE_GROUP_INIT(Group), + flex_pretty_init(Config); +init_per_group(flex_compact_tickets = Group, Config) -> + ?ANNOUNCE_GROUP_INIT(Group), + flex_compact_init(Config); +init_per_group(flex_compact = Group, Config) -> + ?ANNOUNCE_GROUP_INIT(Group), + flex_compact_init(Config); +init_per_group(flex_pretty = Group, Config) -> + ?ANNOUNCE_GROUP_INIT(Group), + flex_pretty_init(Config); +init_per_group(Group, Config) -> + ?ANNOUNCE_GROUP_INIT(Group), + Config. + +end_per_group(flex_pretty_tickets = _Group, Config) -> + flex_pretty_finish(Config); +end_per_group(flex_compact_tickets = _Group, Config) -> + flex_compact_finish(Config); +end_per_group(flex_compact = _Group, Config) -> + flex_compact_finish(Config); +end_per_group(flex_pretty = _Group, Config) -> + flex_pretty_finish(Config); +end_per_group(_Group, Config) -> + Config. + + + +%% +%% ----- +%% + +init_per_testcase(Case, Config) -> + %% We do *not* reset events with each test case + %% The test cases are so short we don't bother, + %% and also we would drown in mprintouts... + put(verbosity,trc), + megaco_test_lib:init_per_testcase(Case, Config). + +end_per_testcase(Case, Config) -> + erase(verbosity), + megaco_test_lib:end_per_testcase(Case, Config). + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% display_text_messages() -> Msgs = @@ -258,6 +647,8 @@ display_text_messages() -> megaco_codec_test_lib:display_text_messages(?VERSION, Msgs). +%% ---- + generate_text_messages() -> Msgs = msgs4() ++ @@ -265,7 +656,7 @@ generate_text_messages() -> megaco_codec_test_lib:generate_text_messages(?V2, ?VERSION, ?EC, Msgs). -%% ---- +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% (catch megaco_codec_v2_test:profile_decode_compact_text_message(msg51a)). %% (catch megaco_codec_v2_test:profile_decode_compact_text_message(msg51b)). @@ -345,7 +736,7 @@ profile_decode_text_messages(Slogan, Codec, Config, Msgs0) -> EncodeRes = encode_text_messages(Codec, Config, Msgs, []), Bins = [Bin || {ok, Bin} <- EncodeRes], Fun = fun() -> - decode_text_messages(Codec, Config, Bins, []) + decode_text_messages(Codec, Config, Bins, []) end, %% Make a dry run, just to make sure all modules are loaded: io:format("make a dry run...~n", []), @@ -374,7 +765,7 @@ profile_encode_text_messages(Slogan, Codec, Config) -> profile_encode_text_messages(Slogan, Codec, Config, Msgs0) -> Msgs = [Msg || {_, Msg, _, _} <- Msgs0], Fun = fun() -> - encode_text_messages(Codec, Config, Msgs, []) + encode_text_messages(Codec, Config, Msgs, []) end, %% Make a dry run, just to make sure all modules are loaded: io:format("make a dry run...~n", []), @@ -395,213 +786,6 @@ decode_text_messages(Codec, Config, [Msg|Msgs], Acc) -> decode_text_messages(Codec, Config, Msgs, [Res | Acc]). -%% ---- - - -tickets() -> - %% io:format("~w:tickets -> entry~n", [?MODULE]), - megaco_test_lib:tickets(?MODULE). - - -%% ---- - -t() -> megaco_test_lib:t(?MODULE). -t(Case) -> megaco_test_lib:t({?MODULE, Case}). - -init_per_testcase(Case, Config) -> - %% CaseString = io_lib:format("~p", [Case]), - C = - case lists:suffix("time_test", atom_to_list(Case)) of - true -> - [{tc_timeout, timer:minutes(10)}|Config]; - false -> - put(verbosity,trc), - Config - end, - megaco_test_lib:init_per_testcase(Case, C). - -end_per_testcase(Case, Config) -> - erase(verbosity), - megaco_test_lib:end_per_testcase(Case, Config). - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% Top test case - -all() -> - [{group, text}, - {group, binary}, - {group, erl_dist}, - {group, tickets}]. - -groups() -> - [{text, [], - [{group, pretty}, - {group, flex_pretty}, - {group, compact}, - {group, flex_compact}]}, - {binary, [], - [{group, bin}, - {group, ber}, - {group, per}]}, - {erl_dist, [], [{group, erl_dist_m}]}, - {pretty, [], [pretty_test_msgs]}, - {compact, [], [compact_test_msgs]}, - {flex_pretty, [], flex_pretty_cases()}, - {flex_compact, [], flex_compact_cases()}, - {bin, [], [bin_test_msgs]}, - {ber, [], [ber_test_msgs]}, - {per, [], [per_test_msgs]}, - {erl_dist_m, [], [erl_dist_m_test_msgs]}, - {tickets, [], - [{group, compact_tickets}, - {group, pretty_tickets}, - {group, flex_compact_tickets}, - {group, flex_pretty_tickets}]}, - {compact_tickets, [], - [compact_otp4011_msg1, compact_otp4011_msg2, - compact_otp4011_msg3, compact_otp4013_msg1, - compact_otp4085_msg1, compact_otp4085_msg2, - compact_otp4280_msg1, compact_otp4299_msg1, - compact_otp4299_msg2, compact_otp4359_msg1, - compact_otp4920_msg0, compact_otp4920_msg1, - compact_otp4920_msg2, compact_otp4920_msg3, - compact_otp4920_msg4, compact_otp4920_msg5, - compact_otp4920_msg6, compact_otp4920_msg7, - compact_otp4920_msg8, compact_otp4920_msg9, - compact_otp4920_msg10, compact_otp4920_msg11, - compact_otp4920_msg12, compact_otp4920_msg20, - compact_otp4920_msg21, compact_otp4920_msg22, - compact_otp4920_msg23, compact_otp4920_msg24, - compact_otp4920_msg25, compact_otp5186_msg01, - compact_otp5186_msg02, compact_otp5186_msg03, - compact_otp5186_msg04, compact_otp5186_msg05, - compact_otp5186_msg06, compact_otp5290_msg01, - compact_otp5290_msg02, compact_otp5793_msg01, - compact_otp5993_msg01, compact_otp5993_msg02, - compact_otp5993_msg03, compact_otp6017_msg01, - compact_otp6017_msg02, compact_otp6017_msg03, - compact_otp7138_msg01, compact_otp7138_msg02, - compact_otp7457_msg01, compact_otp7457_msg02, - compact_otp7457_msg03, compact_otp7534_msg01, - compact_otp7576_msg01, compact_otp7671_msg01]}, - {flex_compact_tickets, [], flex_compact_tickets_cases()}, - {pretty_tickets, [], - [pretty_otp4632_msg1, pretty_otp4632_msg2, - pretty_otp4632_msg3, pretty_otp4632_msg4, - pretty_otp4710_msg1, pretty_otp4710_msg2, - pretty_otp4945_msg1, pretty_otp4945_msg2, - pretty_otp4945_msg3, pretty_otp4945_msg4, - pretty_otp4945_msg5, pretty_otp4945_msg6, - pretty_otp4949_msg1, pretty_otp4949_msg2, - pretty_otp4949_msg3, pretty_otp5042_msg1, - pretty_otp5068_msg1, pretty_otp5085_msg1, - pretty_otp5085_msg2, pretty_otp5085_msg3, - pretty_otp5085_msg4, pretty_otp5085_msg5, - pretty_otp5085_msg6, pretty_otp5085_msg7, - pretty_otp5600_msg1, pretty_otp5600_msg2, - pretty_otp5601_msg1, pretty_otp5793_msg01, - pretty_otp5882_msg01, pretty_otp6490_msg01, - pretty_otp6490_msg02, pretty_otp6490_msg03, - pretty_otp6490_msg04, pretty_otp6490_msg05, - pretty_otp6490_msg06, pretty_otp7249_msg01, - pretty_otp7671_msg01, pretty_otp7671_msg02, - pretty_otp7671_msg03, pretty_otp7671_msg04, - pretty_otp7671_msg05]}, - {flex_pretty_tickets, [], flex_pretty_tickets_cases()}]. - -init_per_group(flex_pretty_tickets, Config) -> - flex_pretty_init(Config); -init_per_group(flex_compact_tickets, Config) -> - flex_compact_init(Config); -init_per_group(flex_compact, Config) -> - flex_compact_init(Config); -init_per_group(flex_pretty, Config) -> - flex_pretty_init(Config); -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(flex_pretty_tickets, Config) -> - flex_pretty_finish(Config); -end_per_group(flex_compact_tickets, Config) -> - flex_compact_finish(Config); -end_per_group(flex_compact, Config) -> - flex_compact_finish(Config); -end_per_group(flex_pretty, Config) -> - flex_pretty_finish(Config); -end_per_group(_GroupName, Config) -> - Config. - - - -flex_pretty_cases() -> - [ - flex_pretty_test_msgs - ]. - - -flex_compact_cases() -> - [ - flex_compact_test_msgs, - flex_compact_dm_timers1, - flex_compact_dm_timers2, - flex_compact_dm_timers3, - flex_compact_dm_timers4, - flex_compact_dm_timers5, - flex_compact_dm_timers6, - flex_compact_dm_timers7, - flex_compact_dm_timers8 - ]. - - -flex_compact_tickets_cases() -> - [ - flex_compact_otp7138_msg01, - flex_compact_otp7138_msg02, - flex_compact_otp7431_msg01, - flex_compact_otp7431_msg02, - flex_compact_otp7431_msg03, - flex_compact_otp7431_msg04, - flex_compact_otp7431_msg05, - flex_compact_otp7431_msg06, - flex_compact_otp7431_msg07, - flex_compact_otp7138_msg02, - flex_compact_otp7457_msg01, - flex_compact_otp7457_msg02, - flex_compact_otp7457_msg03, - flex_compact_otp7534_msg01, - flex_compact_otp7573_msg01, - flex_compact_otp7576_msg01, - flex_compact_otp10998_msg01, - flex_compact_otp10998_msg02, - flex_compact_otp10998_msg03, - flex_compact_otp10998_msg04 - ]. - -flex_pretty_tickets_cases() -> - [ - flex_pretty_otp5042_msg1, - flex_pretty_otp5085_msg1, - flex_pretty_otp5085_msg2, - flex_pretty_otp5085_msg3, - flex_pretty_otp5085_msg4, - flex_pretty_otp5085_msg5, - flex_pretty_otp5085_msg6, - flex_pretty_otp5085_msg7, - flex_pretty_otp5600_msg1, - flex_pretty_otp5600_msg2, - flex_pretty_otp5601_msg1, - flex_pretty_otp5793_msg01, - flex_pretty_otp7431_msg01, - flex_pretty_otp7431_msg02, - flex_pretty_otp7431_msg03, - flex_pretty_otp7431_msg04, - flex_pretty_otp7431_msg05, - flex_pretty_otp7431_msg06, - flex_pretty_otp7431_msg07 - ]. - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% pretty_test_msgs(suite) -> @@ -7584,11 +7768,6 @@ printable(_,_) -> false. -p(true,L,F,A) -> - io:format("~s: " ++ F ++ "~n", [image_of(L)|A]); -p(_,_,_,_) -> - ok. - image_of(trc) -> "T"; image_of(dbg) -> @@ -7601,3 +7780,14 @@ image_of(L) -> io_lib:format("~p",[L]). +p(true,L,F,A) -> + io:format("~s: " ++ F ++ "~n", [image_of(L)|A]); +p(_,_,_,_) -> + ok. + + +p(F, A) -> + io:format("*** [~s] ***" + "~n " ++ F ++ "~n", + [?FTS() | A]). + diff --git a/lib/megaco/test/megaco_codec_v3_test.erl b/lib/megaco/test/megaco_codec_v3_SUITE.erl index 9cc3fd6c1c..5f4951acce 100644 --- a/lib/megaco/test/megaco_codec_v3_test.erl +++ b/lib/megaco/test/megaco_codec_v3_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2016. All Rights Reserved. +%% Copyright Ericsson AB 2006-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -23,7 +23,7 @@ %% Purpose: Test encoding/decoding (codec) module of Megaco/H.248 %%---------------------------------------------------------------------- --module(megaco_codec_v3_test). +-module(megaco_codec_v3_SUITE). %% ---- @@ -36,17 +36,24 @@ -export([msgs/0]). -export([rfc3525_msgs_display/0, rfc3525_msgs_test/0]). --export([t/0, t/1]). +-export([ + suite/0, all/0, groups/0, + init_per_suite/1, end_per_suite/1, + init_per_group/2, end_per_group/2, + init_per_testcase/2, end_per_testcase/2, --export([all/0,groups/0,init_per_group/2,end_per_group/2, - pretty_test_msgs/1, - compact_test_msgs/1, - flex_pretty_init/1, - flex_pretty_finish/1, + pretty_test_msgs/1, + + compact_test_msgs/1, + + flex_pretty_init/1, + flex_pretty_finish/1, flex_pretty_test_msgs/1, + flex_compact_init/1, flex_compact_finish/1, flex_compact_test_msgs/1, + flex_compact_dm_timers1/1, flex_compact_dm_timers2/1, flex_compact_dm_timers3/1, @@ -55,13 +62,15 @@ flex_compact_dm_timers6/1, flex_compact_dm_timers7/1, flex_compact_dm_timers8/1, + bin_test_msgs/1, + ber_test_msgs/1, + per_test_msgs/1, + erl_dist_m_test_msgs/1, - tickets/0, - compact_otp4011_msg1/1, compact_otp4011_msg2/1, compact_otp4011_msg3/1, @@ -184,9 +193,8 @@ flex_pretty_otp7431_msg04/1, flex_pretty_otp7431_msg05/1, flex_pretty_otp7431_msg06/1, - flex_pretty_otp7431_msg07/1, - - init_per_testcase/2, end_per_testcase/2]). + flex_pretty_otp7431_msg07/1 + ]). -export([display_text_messages/0, generate_text_messages/0]). @@ -218,161 +226,95 @@ -define(A5556, ["11111111", "11111111", "11111111"]). -%% ---- +%%====================================================================== +%% Common Test interface functions +%%====================================================================== -display_text_messages() -> - Msgs = - msgs7(text) ++ - msgs8(text), - megaco_codec_test_lib:display_text_messages(?VERSION, ?EC, Msgs). - - -generate_text_messages() -> - Msgs = - msgs7(text) ++ - msgs8(text), - megaco_codec_test_lib:generate_text_messages(?V3, ?VERSION, ?EC, Msgs). - - -%% ---- - -tickets() -> - %% io:format("~w:tickets -> entry~n", [?MODULE]), - megaco_test_lib:tickets(?MODULE). - - -%% ---- - -t() -> megaco_test_lib:t(?MODULE). -t(Case) -> megaco_test_lib:t({?MODULE, Case}). - -init_per_testcase(Case, Config) -> - %% CaseString = io_lib:format("~p", [Case]), - C = - case lists:suffix("time_test", atom_to_list(Case)) of - true -> - [{tc_timeout, timer:minutes(10)}|Config]; - false -> - put(verbosity,trc), - Config - end, - megaco_test_lib:init_per_testcase(Case, C). - -end_per_testcase(Case, Config) -> - erase(verbosity), - megaco_test_lib:end_per_testcase(Case, Config). - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% Top test case +suite() -> + [{ct_hooks, [ts_install_cth]}]. all() -> - [{group, text}, + [ + {group, text}, {group, binary}, {group, erl_dist}, - {group, tickets}]. + {group, tickets} + ]. groups() -> - [{text, [], - [{group, pretty}, - {group, flex_pretty}, - {group, compact}, - {group, flex_compact}]}, - {binary, [], - [{group, bin}, - {group, ber}, - {group, per}]}, - {erl_dist, [], [{group, erl_dist_m}]}, - {pretty, [], [pretty_test_msgs]}, - {compact, [], [compact_test_msgs]}, - {flex_pretty, [], flex_pretty_cases()}, - {flex_compact, [], flex_compact_cases()}, - {bin, [], [bin_test_msgs]}, - {ber, [], [ber_test_msgs]}, - {per, [], [per_test_msgs]}, - {erl_dist_m, [], [erl_dist_m_test_msgs]}, - {tickets, [], - [{group, compact_tickets}, - {group, flex_compact_tickets}, - {group, pretty_tickets}, - {group, flex_pretty_tickets}]}, - {compact_tickets, [], - [compact_otp4011_msg1, compact_otp4011_msg2, - compact_otp4011_msg3, compact_otp4013_msg1, - compact_otp4085_msg1, compact_otp4085_msg2, - compact_otp4280_msg1, compact_otp4299_msg1, - compact_otp4359_msg1, compact_otp4920_msg0, - compact_otp4920_msg1, compact_otp4920_msg2, - compact_otp4920_msg3, compact_otp4920_msg4, - compact_otp4920_msg5, compact_otp4920_msg6, - compact_otp4920_msg7, compact_otp4920_msg8, - compact_otp4920_msg9, compact_otp4920_msg10, - compact_otp4920_msg11, compact_otp4920_msg12, - compact_otp4920_msg20, compact_otp4920_msg21, - compact_otp4920_msg22, compact_otp4920_msg23, - compact_otp4920_msg24, compact_otp4920_msg25, - compact_otp5186_msg01, compact_otp5186_msg02, - compact_otp5186_msg03, compact_otp5186_msg04, - compact_otp5186_msg05, compact_otp5186_msg06, - compact_otp5793_msg01, compact_otp5836_msg01, - compact_otp5993_msg01, compact_otp5993_msg02, - compact_otp5993_msg03, compact_otp6017_msg01, - compact_otp6017_msg02, compact_otp6017_msg03]}, + [ + {text, [], text_cases()}, + {binary, [], binary_cases()}, + {erl_dist, [], erl_dist_cases()}, + {pretty, [], pretty_cases()}, + {compact, [], compact_cases()}, + {flex_pretty, [], flex_pretty_cases()}, + {flex_compact, [], flex_compact_cases()}, + {bin, [], bin_cases()}, + {ber, [], ber_cases()}, + {per, [], per_cases()}, + {erl_dist_m, [], erl_dist_m_cases()}, + {tickets, [], tickets_cases()}, + {compact_tickets, [], compact_tickets_cases()}, {flex_compact_tickets, [], flex_compact_tickets_cases()}, - {pretty_tickets, [], - [pretty_otp4632_msg1, pretty_otp4632_msg2, - pretty_otp4632_msg3, pretty_otp4632_msg4, - pretty_otp4710_msg1, pretty_otp4710_msg2, - pretty_otp4945_msg1, pretty_otp4945_msg2, - pretty_otp4945_msg3, pretty_otp4945_msg4, - pretty_otp4945_msg5, pretty_otp4945_msg6, - pretty_otp4949_msg1, pretty_otp4949_msg2, - pretty_otp4949_msg3, pretty_otp5042_msg1, - pretty_otp5068_msg1, pretty_otp5085_msg1, - pretty_otp5085_msg2, pretty_otp5085_msg3, - pretty_otp5085_msg4, pretty_otp5085_msg5, - pretty_otp5085_msg6, pretty_otp5085_msg7, - pretty_otp5085_msg8, pretty_otp5600_msg1, - pretty_otp5600_msg2, pretty_otp5601_msg1, - pretty_otp5793_msg01, pretty_otp5803_msg01, - pretty_otp5803_msg02, pretty_otp5805_msg01, - pretty_otp5836_msg01, pretty_otp5882_msg01, - pretty_otp6490_msg01, pretty_otp6490_msg02, - pretty_otp6490_msg03, pretty_otp6490_msg04, - pretty_otp6490_msg05, pretty_otp6490_msg06, - pretty_otp7671_msg01, pretty_otp7671_msg02, - pretty_otp7671_msg03, pretty_otp7671_msg04, - pretty_otp7671_msg05, pretty_otp8114_msg01]}, - {flex_pretty_tickets, [], flex_pretty_tickets_cases()}]. - -init_per_group(flex_pretty_tickets, Config) -> - flex_pretty_init(Config); -init_per_group(flex_compact_tickets, Config) -> - flex_compact_init(Config); -init_per_group(flex_compact, Config) -> - flex_compact_init(Config); -init_per_group(flex_pretty, Config) -> - flex_pretty_init(Config); -init_per_group(_GroupName, Config) -> - Config. + {pretty_tickets, [], pretty_tickets_cases()}, + {flex_pretty_tickets, [], flex_pretty_tickets_cases()} + ]. -end_per_group(flex_pretty_tickets, Config) -> - flex_pretty_finish(Config); -end_per_group(flex_compact_tickets, Config) -> - flex_compact_finish(Config); -end_per_group(flex_compact, Config) -> - flex_compact_finish(Config); -end_per_group(flex_pretty, Config) -> - flex_pretty_finish(Config); -end_per_group(_GroupName, Config) -> - Config. +text_cases() -> + [ + {group, pretty}, + {group, flex_pretty}, + {group, compact}, + {group, flex_compact} + ]. +binary_cases() -> + [ + {group, bin}, + {group, ber}, + {group, per} + ]. + +erl_dist_cases() -> + [ + {group, erl_dist_m} + ]. -flex_pretty_cases() -> +pretty_cases() -> + [ + pretty_test_msgs + ]. + +compact_cases() -> + [ + compact_test_msgs + ]. + +flex_pretty_cases() -> [ flex_pretty_test_msgs ]. +bin_cases() -> + [ + bin_test_msgs + ]. + +ber_cases() -> + [ + ber_test_msgs + ]. + +per_cases() -> + [ + per_test_msgs + ]. + +erl_dist_m_cases() -> + [ + erl_dist_m_test_msgs + ]. flex_compact_cases() -> [ @@ -387,6 +329,60 @@ flex_compact_cases() -> flex_compact_dm_timers8 ]. +tickets_cases() -> + [ + {group, compact_tickets}, + {group, flex_compact_tickets}, + {group, pretty_tickets}, + {group, flex_pretty_tickets} + ]. + +compact_tickets_cases() -> + [ + compact_otp4011_msg1, + compact_otp4011_msg2, + compact_otp4011_msg3, + compact_otp4013_msg1, + compact_otp4085_msg1, + compact_otp4085_msg2, + compact_otp4280_msg1, + compact_otp4299_msg1, + compact_otp4359_msg1, + compact_otp4920_msg0, + compact_otp4920_msg1, + compact_otp4920_msg2, + compact_otp4920_msg3, + compact_otp4920_msg4, + compact_otp4920_msg5, + compact_otp4920_msg6, + compact_otp4920_msg7, + compact_otp4920_msg8, + compact_otp4920_msg9, + compact_otp4920_msg10, + compact_otp4920_msg11, + compact_otp4920_msg12, + compact_otp4920_msg20, + compact_otp4920_msg21, + compact_otp4920_msg22, + compact_otp4920_msg23, + compact_otp4920_msg24, + compact_otp4920_msg25, + compact_otp5186_msg01, + compact_otp5186_msg02, + compact_otp5186_msg03, + compact_otp5186_msg04, + compact_otp5186_msg05, + compact_otp5186_msg06, + compact_otp5793_msg01, + compact_otp5836_msg01, + compact_otp5993_msg01, + compact_otp5993_msg02, + compact_otp5993_msg03, + compact_otp6017_msg01, + compact_otp6017_msg02, + compact_otp6017_msg03 + ]. + flex_compact_tickets_cases() -> [ flex_compact_otp4299_msg1, @@ -399,7 +395,57 @@ flex_compact_tickets_cases() -> flex_compact_otp7431_msg07 ]. -flex_pretty_tickets_cases() -> +pretty_tickets_cases() -> + [ + pretty_otp4632_msg1, + pretty_otp4632_msg2, + pretty_otp4632_msg3, + pretty_otp4632_msg4, + pretty_otp4710_msg1, + pretty_otp4710_msg2, + pretty_otp4945_msg1, + pretty_otp4945_msg2, + pretty_otp4945_msg3, + pretty_otp4945_msg4, + pretty_otp4945_msg5, + pretty_otp4945_msg6, + pretty_otp4949_msg1, + pretty_otp4949_msg2, + pretty_otp4949_msg3, + pretty_otp5042_msg1, + pretty_otp5068_msg1, + pretty_otp5085_msg1, + pretty_otp5085_msg2, + pretty_otp5085_msg3, + pretty_otp5085_msg4, + pretty_otp5085_msg5, + pretty_otp5085_msg6, + pretty_otp5085_msg7, + pretty_otp5085_msg8, + pretty_otp5600_msg1, + pretty_otp5600_msg2, + pretty_otp5601_msg1, + pretty_otp5793_msg01, + pretty_otp5803_msg01, + pretty_otp5803_msg02, + pretty_otp5805_msg01, + pretty_otp5836_msg01, + pretty_otp5882_msg01, + pretty_otp6490_msg01, + pretty_otp6490_msg02, + pretty_otp6490_msg03, + pretty_otp6490_msg04, + pretty_otp6490_msg05, + pretty_otp6490_msg06, + pretty_otp7671_msg01, + pretty_otp7671_msg02, + pretty_otp7671_msg03, + pretty_otp7671_msg04, + pretty_otp7671_msg05, + pretty_otp8114_msg01 + ]. + +flex_pretty_tickets_cases() -> [ flex_pretty_otp5042_msg1, flex_pretty_otp5085_msg1, @@ -428,6 +474,119 @@ flex_pretty_tickets_cases() -> ]. +%% +%% ----- +%% + +init_per_suite(suite) -> + []; +init_per_suite(doc) -> + []; +init_per_suite(Config0) when is_list(Config0) -> + + ?ANNOUNCE_SUITE_INIT(), + + p("init_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + case ?LIB:init_per_suite(Config0) of + {skip, _} = SKIP -> + SKIP; + + Config1 when is_list(Config1) -> + + %% We need a (local) monitor on this node also + megaco_test_sys_monitor:start(), + + p("init_per_suite -> end when" + "~n Config: ~p" + "~n Nodes: ~p", [Config1, erlang:nodes()]), + + Config1 + end. + +end_per_suite(suite) -> []; +end_per_suite(doc) -> []; +end_per_suite(Config0) when is_list(Config0) -> + + p("end_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + megaco_test_sys_monitor:stop(), + Config1 = ?LIB:end_per_suite(Config0), + + p("end_per_suite -> end when" + "~n Nodes: ~p", [erlang:nodes()]), + + Config1. + + +%% +%% ----- +%% + +init_per_group(flex_pretty_tickets = Group, Config) -> + ?ANNOUNCE_GROUP_INIT(Group), + flex_pretty_init(Config); +init_per_group(flex_compact_tickets = Group, Config) -> + ?ANNOUNCE_GROUP_INIT(Group), + flex_compact_init(Config); +init_per_group(flex_compact = Group, Config) -> + ?ANNOUNCE_GROUP_INIT(Group), + flex_compact_init(Config); +init_per_group(flex_pretty = Group, Config) -> + ?ANNOUNCE_GROUP_INIT(Group), + flex_pretty_init(Config); +init_per_group(Group, Config) -> + ?ANNOUNCE_GROUP_INIT(Group), + Config. + +end_per_group(flex_pretty_tickets = _Group, Config) -> + flex_pretty_finish(Config); +end_per_group(flex_compact_tickets = _Group, Config) -> + flex_compact_finish(Config); +end_per_group(flex_compact = _Group, Config) -> + flex_compact_finish(Config); +end_per_group(flex_pretty = _Group, Config) -> + flex_pretty_finish(Config); +end_per_group(_Group, Config) -> + Config. + + +%% +%% ----- +%% + +init_per_testcase(Case, Config) -> + %% We do *not* reset events with each test case + %% The test cases are so short we don't bother, + %% and also we would drown in mprintouts... + put(verbosity,trc), + megaco_test_lib:init_per_testcase(Case, Config). + +end_per_testcase(Case, Config) -> + erase(verbosity), + megaco_test_lib:end_per_testcase(Case, Config). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +display_text_messages() -> + Msgs = + msgs7(text) ++ + msgs8(text), + megaco_codec_test_lib:display_text_messages(?VERSION, ?EC, Msgs). + + +generate_text_messages() -> + Msgs = + msgs7(text) ++ + msgs8(text), + megaco_codec_test_lib:generate_text_messages(?V3, ?VERSION, ?EC, Msgs). + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% pretty_test_msgs(suite) -> @@ -8254,11 +8413,6 @@ printable(_,_) -> false. -p(true,L,F,A) -> - io:format("~s:" ++ F ++ "~n", [image_of(L)|A]); -p(_,_,_,_) -> - ok. - image_of(trc) -> "TRC"; image_of(dbg) -> @@ -8270,3 +8424,15 @@ image_of(err) -> image_of(L) -> io_lib:format("~p",[L]). + +p(true,L,F,A) -> + io:format("~s:" ++ F ++ "~n", [image_of(L)|A]); +p(_,_,_,_) -> + ok. + + +p(F, A) -> + io:format("*** [~s] ***" + "~n " ++ F ++ "~n", + [?FTS() | A]). + diff --git a/lib/megaco/test/megaco_config_test.erl b/lib/megaco/test/megaco_config_SUITE.erl index d46806927a..4d34e09d0d 100644 --- a/lib/megaco/test/megaco_config_test.erl +++ b/lib/megaco/test/megaco_config_SUITE.erl @@ -23,68 +23,25 @@ %% Purpose: Test application config %%---------------------------------------------------------------------- --module(megaco_config_test). +-module(megaco_config_SUITE). -export([ - all/0, - groups/0, - - init_per_group/2, - end_per_group/2, - init_per_testcase/2, - end_per_testcase/2, + suite/0, all/0, groups/0, + init_per_suite/1, end_per_suite/1, + init_per_group/2, end_per_group/2, + init_per_testcase/2, end_per_testcase/2, config/1, transaction_id_counter_mg/1, transaction_id_counter_mgc/1, otp_7216/1, otp_8167/1, - otp_8183/1, - - t/0, t/1 + otp_8183/1 ]). --include("megaco_test_lib.hrl"). -include_lib("megaco/include/megaco.hrl"). -include_lib("megaco/src/app/megaco_internal.hrl"). - -t() -> megaco_test_lib:t(?MODULE). -t(Case) -> megaco_test_lib:t({?MODULE, Case}). - -min(M) -> timer:minutes(M). - -%% Test server callbacks -init_per_testcase(Case, Config) when (Case =:= otp_7216) orelse - (Case =:= otp_8167) orelse - (Case =:= otp_8183) -> - i("try starting megaco_config"), - case megaco_config:start_link() of - {ok, _} -> - C = lists:keydelete(tc_timeout, 1, Config), - do_init_per_testcase(Case, [{tc_timeout, min(3)}|C]); - {error, Reason} -> - i("Failed starting megaco_config: " - "~n ~p", [Reason]), - {skip, ?F("Failed starting config: ~p", [Reason])} - end; -init_per_testcase(Case, Config) -> - C = lists:keydelete(tc_timeout, 1, Config), - do_init_per_testcase(Case, [{tc_timeout, min(3)}|C]). - -do_init_per_testcase(Case, Config) -> - process_flag(trap_exit, true), - megaco_test_lib:init_per_testcase(Case, Config). - -end_per_testcase(Case, Config) when (Case =:= otp_7216) orelse - (Case =:= otp_8167) orelse - (Case =:= otp_8183) -> - (catch megaco_config:stop()), - process_flag(trap_exit, false), - megaco_test_lib:end_per_testcase(Case, Config); -end_per_testcase(Case, Config) -> - process_flag(trap_exit, false), - megaco_test_lib:end_per_testcase(Case, Config). - +-include("megaco_test_lib.hrl"). -record(command, {id, desc, cmd, verify}). @@ -92,8 +49,12 @@ end_per_testcase(Case, Config) -> -define(NUM_CNT_PROCS, 100). -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% Top test case +%%====================================================================== +%% Common Test interface functions +%%====================================================================== + +suite() -> + [{ct_hooks, [ts_install_cth]}]. all() -> [ @@ -108,11 +69,7 @@ groups() -> {tickets, [], tickets_cases()} ]. -init_per_group(_GroupName, Config) -> - Config. -end_per_group(_GroupName, Config) -> - Config. transaction_id_counter_cases() -> [ @@ -128,6 +85,113 @@ tickets_cases() -> ]. + +%% +%% ----- +%% + +init_per_suite(Config0) when is_list(Config0) -> + + p("init_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + case ?LIB:init_per_suite(Config0) of + {skip, _} = SKIP -> + SKIP; + + Config1 when is_list(Config1) -> + + %% We need a (local) monitor on this node also + megaco_test_sys_monitor:start(), + + p("init_per_suite -> end when" + "~n Config: ~p" + "~n Nodes: ~p", [Config1, erlang:nodes()]), + + Config1 + end. + +end_per_suite(Config0) when is_list(Config0) -> + + p("end_per_suite -> entry with" + "~n Config0: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + megaco_test_sys_monitor:stop(), + Config1 = ?LIB:end_per_suite(Config0), + + p("end_per_suite -> end when" + "~n Nodes: ~p", [erlang:nodes()]), + Config1. + + + +%% +%% ----- +%% + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + +%% +%% ----- +%% + +%% Test server callbacks +init_per_testcase(Case, Config) when (Case =:= otp_7216) orelse + (Case =:= otp_8167) orelse + (Case =:= otp_8183) -> + i("init_per_testcase -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config, erlang:nodes()]), + + megaco_test_global_sys_monitor:reset_events(), + + i("try starting megaco_config"), + case megaco_config:start_link() of + {ok, _} -> + C = lists:keydelete(tc_timeout, 1, Config), + do_init_per_testcase(Case, [{tc_timeout, min(3)}|C]); + {error, Reason} -> + i("Failed starting megaco_config: " + "~n ~p", [Reason]), + {skip, ?F("Failed starting config: ~p", [Reason])} + end; +init_per_testcase(Case, Config) -> + C = lists:keydelete(tc_timeout, 1, Config), + do_init_per_testcase(Case, [{tc_timeout, min(3)}|C]). + +do_init_per_testcase(Case, Config) -> + process_flag(trap_exit, true), + megaco_test_lib:init_per_testcase(Case, Config). + +min(M) -> timer:minutes(M). + + +end_per_testcase(Case, Config) when (Case =:= otp_7216) orelse + (Case =:= otp_8167) orelse + (Case =:= otp_8183) -> + p("end_per_testcase -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config, erlang:nodes()]), + + p("system events during test: " + "~n ~p", [megaco_test_global_sys_monitor:events()]), + + (catch megaco_config:stop()), + process_flag(trap_exit, false), + megaco_test_lib:end_per_testcase(Case, Config); +end_per_testcase(Case, Config) -> + process_flag(trap_exit, false), + megaco_test_lib:end_per_testcase(Case, Config). + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Config test case @@ -1158,7 +1222,12 @@ p(F) -> p(F, []). p(F, A) -> - io:format("[~w] " ++ F ++ "~n", [get(tc)|A]). + case get(tc) of + undefined -> + io:format(F ++ "~n", A); + TC -> + io:format("[~w] " ++ F ++ "~n", [TC|A]) + end. i(F) -> diff --git a/lib/megaco/test/megaco_digit_map_test.erl b/lib/megaco/test/megaco_digit_map_SUITE.erl index e03d38497c..866751afbf 100644 --- a/lib/megaco/test/megaco_digit_map_test.erl +++ b/lib/megaco/test/megaco_digit_map_SUITE.erl @@ -22,16 +22,13 @@ %%---------------------------------------------------------------------- %% Purpose: Verify the application specifics of the Megaco application %%---------------------------------------------------------------------- --module(megaco_digit_map_test). +-module(megaco_digit_map_SUITE). -export([ - all/0, - groups/0, - - init_per_group/2, - end_per_group/2, - init_per_testcase/2, - end_per_testcase/2, + suite/0, all/0, groups/0, + init_per_suite/1, end_per_suite/1, + init_per_group/2, end_per_group/2, + init_per_testcase/2, end_per_testcase/2, otp_5750_01/1, otp_5750_02/1, @@ -40,28 +37,21 @@ otp_5826_02/1, otp_5826_03/1, otp_7449_1/1, - otp_7449_2/1, + otp_7449_2/1 - t/0, t/1 ]). -include("megaco_test_lib.hrl"). -t() -> megaco_test_lib:t(?MODULE). -t(Case) -> megaco_test_lib:t({?MODULE, Case}). - - -%% Test server callbacks -init_per_testcase(Case, Config) -> - megaco_test_lib:init_per_testcase(Case, Config). -end_per_testcase(Case, Config) -> - megaco_test_lib:end_per_testcase(Case, Config). +%%====================================================================== +%% Common Test interface functions +%%====================================================================== - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +suite() -> + [{ct_hooks, [ts_install_cth]}]. all() -> [ @@ -77,13 +67,6 @@ groups() -> {otp_7449, [], otp_7449_cases()} ]. -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - tickets_cases() -> [ {group, otp_5750}, @@ -117,13 +100,92 @@ otp_7449_cases() -> ]. -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% ----- +%% + +init_per_suite(suite) -> + []; +init_per_suite(doc) -> + []; +init_per_suite(Config0) when is_list(Config0) -> + + ?ANNOUNCE_SUITE_INIT(), + + p("init_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + case ?LIB:init_per_suite(Config0) of + {skip, _} = SKIP -> + SKIP; + + Config1 when is_list(Config1) -> + + %% We need a (local) monitor on this node also + megaco_test_sys_monitor:start(), + + p("init_per_suite -> end when" + "~n Config: ~p" + "~n Nodes: ~p", [Config1, erlang:nodes()]), + + Config1 + end. + +end_per_suite(suite) -> []; +end_per_suite(doc) -> []; +end_per_suite(Config0) when is_list(Config0) -> + + p("end_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + megaco_test_sys_monitor:stop(), + Config1 = ?LIB:end_per_suite(Config0), + p("end_per_suite -> end when" + "~n Nodes: ~p", [erlang:nodes()]), + Config1. +%% +%% ----- +%% + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + + +%% +%% ----- +%% + +init_per_testcase(Case, Config) -> + + p("init_per_testcase -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config, erlang:nodes()]), + + megaco_test_global_sys_monitor:reset_events(), + + megaco_test_lib:init_per_testcase(Case, Config). + +end_per_testcase(Case, Config) -> + + p("end_per_testcase -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config, erlang:nodes()]), + + p("system events during test: " + "~n ~p", [megaco_test_global_sys_monitor:events()]), + + megaco_test_lib:end_per_testcase(Case, Config). @@ -675,3 +737,23 @@ dm_test(Exec, Verify) -> tde(DM, Evs) -> megaco:test_digit_event(DM, Evs). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% p(F) -> +%% p(F, []). + +p(F, A) -> + p(get(sname), F, A). + +p(S, F, A) when is_list(S) -> + io:format("*** [~s] ~p ~s ***" + "~n " ++ F ++ "~n", + [?FTS(), self(), S | A]); +p(_S, F, A) -> + io:format("*** [~s] ~p *** " + "~n " ++ F ++ "~n", + [?FTS(), self() | A]). + + diff --git a/lib/megaco/test/megaco_examples_test.erl b/lib/megaco/test/megaco_examples_SUITE.erl index fdf9fe29ff..549840e280 100644 --- a/lib/megaco/test/megaco_examples_test.erl +++ b/lib/megaco/test/megaco_examples_SUITE.erl @@ -23,20 +23,16 @@ %% Purpose: Test application config %%---------------------------------------------------------------------- --module(megaco_examples_test). +-module(megaco_examples_SUITE). -export([ - all/0, - groups/0, + suite/0, all/0, groups/0, + init_per_suite/1, end_per_suite/1, + init_per_group/2, end_per_group/2, + init_per_testcase/2, end_per_testcase/2, - init_per_group/2, - end_per_group/2, - init_per_testcase/2, - end_per_testcase/2, + simple/1 - simple/1, - - t/0, t/1 ]). @@ -44,27 +40,124 @@ -include_lib("megaco/include/megaco.hrl"). -include_lib("megaco/include/megaco_message_v1.hrl"). --define(LIB, megaco_test_lib). -t() -> megaco_test_lib:t(?MODULE). -t(Case) -> megaco_test_lib:t({?MODULE, Case}). +%%====================================================================== +%% Common Test interface functions +%%====================================================================== + +suite() -> + [{ct_hooks, [ts_install_cth]}]. + +all() -> + [ + simple + ]. + +groups() -> + []. + + + +%% +%% ----- +%% + +init_per_suite(suite) -> + []; +init_per_suite(doc) -> + []; +init_per_suite(Config0) when is_list(Config0) -> + + ?ANNOUNCE_SUITE_INIT(), + + p("init_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + case ?LIB:init_per_suite(Config0) of + {skip, _} = SKIP -> + SKIP; + + Config1 when is_list(Config1) -> + + %% We need a (local) monitor on this node also + megaco_test_sys_monitor:start(), + + p("init_per_suite -> end when" + "~n Config: ~p" + "~n Nodes: ~p", [Config1, erlang:nodes()]), + + Config1 + end. + +end_per_suite(suite) -> []; +end_per_suite(doc) -> []; +end_per_suite(Config0) when is_list(Config0) -> + + p("end_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + megaco_test_sys_monitor:stop(), + Config1 = ?LIB:end_per_suite(Config0), + + p("end_per_suite -> end when" + "~n Nodes: ~p", [erlang:nodes()]), + + Config1. + + +%% +%% ----- +%% + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + +%% +%% ----- +%% -%% Test server callbacks init_per_testcase(Case, Config) -> + + p("init_per_testcase -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config, erlang:nodes()]), + + megaco_test_global_sys_monitor:reset_events(), + put(dbg,true), purge_examples(), load_examples(), megaco:enable_trace(max, io), + megaco_test_lib:init_per_testcase(Case, Config). end_per_testcase(Case, Config) -> + + p("end_per_testcase -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config, erlang:nodes()]), + + p("system events during test: " + "~n ~p", [megaco_test_global_sys_monitor:events()]), + purge_examples(), erase(dbg), megaco:disable_trace(), + megaco_test_lib:end_per_testcase(Case, Config). + example_modules() -> - [megaco_simple_mg, megaco_simple_mgc]. + [ + megaco_simple_mg, + megaco_simple_mgc + ]. load_examples() -> case code:lib_dir(megaco) of @@ -93,22 +186,10 @@ purge_examples() -> end. -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% Top test case - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -all() -> - [simple]. - -groups() -> - []. - -init_per_group(_GroupName, Config) -> - Config. -end_per_group(_GroupName, Config) -> - Config. +%% ------------------ simple ------------------------ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% simple(suite) -> []; @@ -364,6 +445,22 @@ users(Proxy) -> +%% p(F) -> +%% p(F, []). + +p(F, A) -> + p(get(sname), F, A). + +p(S, F, A) when is_list(S) -> + io:format("*** [~s] ~p ~s ***" + "~n " ++ F ++ "~n", + [?FTS(), self(), S | A]); +p(_S, F, A) -> + io:format("*** [~s] ~p *** " + "~n " ++ F ++ "~n", + [?FTS(), self() | A]). + + d(F) -> d(F, []). d(F, A) -> diff --git a/lib/megaco/test/megaco_flex_test.erl b/lib/megaco/test/megaco_flex_SUITE.erl index 27c46a3b47..27d58b97c2 100644 --- a/lib/megaco/test/megaco_flex_test.erl +++ b/lib/megaco/test/megaco_flex_SUITE.erl @@ -22,22 +22,20 @@ %%---------------------------------------------------------------------- %% Purpose: Test various aspects of the flex scanner handling %% -%% Test: ts:run(megaco, megaco_flex_test, [batch]). +%% Test: ts:run(megaco, megaco_flex_SUITE, [batch]). %% %%---------------------------------------------------------------------- --module(megaco_flex_test). +-module(megaco_flex_SUITE). -include("megaco_test_lib.hrl"). -export([ - t/0, t/1, - + suite/0, all/0, groups/0, + init_per_suite/1, end_per_suite/1, + init_per_group/2, end_per_group/2, init_per_testcase/2, end_per_testcase/2, - all/0,groups/0,init_per_group/2,end_per_group/2, - init_per_suite/1, end_per_suite/1, - plain/1, port_exit/1, garbage_in/1 @@ -45,54 +43,118 @@ ]). -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -t() -> megaco_test_lib:t(?MODULE). -t(Case) -> megaco_test_lib:t({?MODULE, Case}). - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%====================================================================== +%% Common Test interface functions +%%====================================================================== -init_per_testcase(Case, Config) -> - megaco_test_lib:init_per_testcase(Case, Config). - -end_per_testcase(Case, Config) -> - megaco_test_lib:end_per_testcase(Case, Config). - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +suite() -> + [{ct_hooks, [ts_install_cth]}]. all() -> - [plain, port_exit, garbage_in]. + [ + plain, + port_exit, + garbage_in + ]. groups() -> []. -init_per_group(_GroupName, Config) -> - Config. -end_per_group(_GroupName, Config) -> - Config. +%% +%% ----- +%% init_per_suite(suite) -> []; init_per_suite(doc) -> []; -init_per_suite(Config) when is_list(Config) -> - case megaco_flex_scanner:is_enabled() of - true -> - Config; - false -> - ?SKIP(flex_scanner_not_enabled) +init_per_suite(Config0) when is_list(Config0) -> + + ?ANNOUNCE_SUITE_INIT(), + + p("init_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + case ?LIB:init_per_suite(Config0) of + {skip, _} = SKIP -> + SKIP; + + Config1 when is_list(Config1) -> + + case megaco_flex_scanner:is_enabled() of + true -> + + %% We need a (local) monitor on this node also + megaco_test_sys_monitor:start(), + + p("init_per_suite -> end when" + "~n Config: ~p" + "~n Nodes: ~p", [Config1, erlang:nodes()]), + + Config1; + false -> + ?SKIP(flex_scanner_not_enabled) + end end. end_per_suite(suite) -> []; end_per_suite(doc) -> []; -end_per_suite(Config) when is_list(Config) -> +end_per_suite(Config0) when is_list(Config0) -> + + p("end_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + megaco_test_sys_monitor:stop(), + Config1 = ?LIB:end_per_suite(Config0), + + p("end_per_suite -> end when" + "~n Nodes: ~p", [erlang:nodes()]), + + Config1. + + +%% +%% ----- +%% + +init_per_group(_GroupName, Config) -> Config. +end_per_group(_GroupName, Config) -> + Config. + + +%% +%% ----- +%% + +init_per_testcase(Case, Config) -> + + p("init_per_testcase -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config, erlang:nodes()]), + + megaco_test_global_sys_monitor:reset_events(), + + megaco_test_lib:init_per_testcase(Case, Config). + +end_per_testcase(Case, Config) -> + + p("end_per_testcase -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config, erlang:nodes()]), + + p("system events during test: " + "~n ~p", [megaco_test_global_sys_monitor:events()]), + + megaco_test_lib:end_per_testcase(Case, Config). + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -216,8 +278,13 @@ p(F) -> p(F, []). p(F, A) -> - TC = get(tc), - io:format("*** [~s] ~p ~w ***" - "~n " ++ F ++ "~n", - [?FTS(), self(), TC | A]). + case get(tc) of + undefined -> + io:format("*** [~s] ~p ***" + "~n" ++ F ++ "~n", [?FTS(), self() | A]); + TC -> + io:format("*** [~s] ~p ~w ***" + "~n " ++ F ++ "~n", + [?FTS(), self(), TC | A]) + end. diff --git a/lib/megaco/test/megaco_load_test.erl b/lib/megaco/test/megaco_load_SUITE.erl index 9cce9e70ce..e5dc5a027a 100644 --- a/lib/megaco/test/megaco_load_test.erl +++ b/lib/megaco/test/megaco_load_SUITE.erl @@ -22,16 +22,13 @@ %%---------------------------------------------------------------------- %% Purpose: Verify the application specifics of the Megaco application %%---------------------------------------------------------------------- --module(megaco_load_test). +-module(megaco_load_SUITE). -export([ - all/0, - groups/0, - - init_per_group/2, - end_per_group/2, - init_per_testcase/2, - end_per_testcase/2, + suite/0, all/0, groups/0, + init_per_suite/1, end_per_suite/1, + init_per_group/2, end_per_group/2, + init_per_testcase/2, end_per_testcase/2, single_user_light_load/1, single_user_medium_load/1, @@ -41,9 +38,8 @@ multi_user_light_load/1, multi_user_medium_load/1, multi_user_heavy_load/1, - multi_user_extreme_load/1, + multi_user_extreme_load/1 - t/0, t/1 ]). -export([ @@ -52,9 +48,9 @@ ]). --include("megaco_test_lib.hrl"). -include_lib("megaco/include/megaco.hrl"). -include_lib("megaco/include/megaco_message_v1.hrl"). +-include("megaco_test_lib.hrl"). -define(TEST_VERBOSITY, debug). -define(MGC_VERBOSITY, silence). @@ -86,13 +82,109 @@ -define(MG_SET_VERBOSITY(Pid, V), megaco_test_mg:verbosity(Pid, V)). -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%====================================================================== +%% Common Test interface functions +%%====================================================================== + +suite() -> + [{ct_hooks, [ts_install_cth]}]. + +all() -> + [ + {group, single}, + {group, multi} + ]. + +groups() -> + [ + {single, [], single_cases()}, + {multi, [], multi_cases()} + ]. -t() -> megaco_test_lib:t(?MODULE). -t(Case) -> megaco_test_lib:t({?MODULE, Case}). +single_cases() -> + [ + single_user_light_load, + single_user_medium_load, + single_user_heavy_load, + single_user_extreme_load + ]. + +multi_cases() -> + [ + multi_user_light_load, + multi_user_medium_load, + multi_user_heavy_load, + multi_user_extreme_load + ]. + + + +%% +%% ----- +%% + +init_per_suite(suite) -> + []; +init_per_suite(doc) -> + []; +init_per_suite(Config0) when is_list(Config0) -> + + ?ANNOUNCE_SUITE_INIT(), + + p("init_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + case ?LIB:init_per_suite(Config0) of + {skip, _} = SKIP -> + SKIP; + + Config1 when is_list(Config1) -> + + %% We need a (local) monitor on this node also + megaco_test_sys_monitor:start(), + + p("init_per_suite -> end when" + "~n Config: ~p" + "~n Nodes: ~p", [Config1, erlang:nodes()]), + + Config1 + end. + +end_per_suite(suite) -> []; +end_per_suite(doc) -> []; +end_per_suite(Config0) when is_list(Config0) -> + + p("end_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + megaco_test_sys_monitor:stop(), + Config1 = ?LIB:end_per_suite(Config0), + + p("end_per_suite -> end when" + "~n Nodes: ~p", [erlang:nodes()]), + + Config1. + + +%% +%% ----- +%% + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + + +%% +%% ----- +%% -%% Test server callbacks init_per_testcase(single_user_light_load = Case, Config) -> C = lists:keydelete(tc_timeout, 1, Config), do_init_per_testcase(Case, [{tc_timeout, min(2)}|C]); @@ -122,48 +214,25 @@ init_per_testcase(Case, Config) -> do_init_per_testcase(Case, Config) -> process_flag(trap_exit, true), - megaco_test_lib:init_per_testcase(Case, Config). - -end_per_testcase(Case, Config) -> - process_flag(trap_exit, false), - megaco_test_lib:end_per_testcase(Case, Config). + p("init_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config, erlang:nodes()]), -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -all() -> - [ - {group, single}, - {group, multi} - ]. + megaco_test_global_sys_monitor:reset_events(), + megaco_test_lib:init_per_testcase(Case, Config). -groups() -> - [ - {single, [], single_cases()}, - {multi, [], multi_cases()} - ]. +end_per_testcase(Case, Config) -> + process_flag(trap_exit, false), -single_cases() -> - [ - single_user_light_load, - single_user_medium_load, - single_user_heavy_load, - single_user_extreme_load - ]. + p("end_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config, erlang:nodes()]), -multi_cases() -> - [ - multi_user_light_load, - multi_user_medium_load, - multi_user_heavy_load, - multi_user_extreme_load - ]. - -init_per_group(_GroupName, Config) -> - Config. + p("system events during test: " + "~n ~p", [megaco_test_global_sys_monitor:events()]), -end_per_group(_GroupName, Config) -> - Config. + megaco_test_lib:end_per_testcase(Case, Config). @@ -694,6 +763,11 @@ maybe_display_system_info(_) -> min(M) -> timer:minutes(M). +p(F, A) -> + io:format("*** [~s] ~p ***" + "~n " ++ F ++ "~n", + [?FTS(), self() | A]). + i(F) -> i(F, []). diff --git a/lib/megaco/test/megaco_mess_test.erl b/lib/megaco/test/megaco_mess_SUITE.erl index 46aeabd679..bb4e3eb1ee 100644 --- a/lib/megaco/test/megaco_mess_test.erl +++ b/lib/megaco/test/megaco_mess_SUITE.erl @@ -22,23 +22,14 @@ %%---------------------------------------------------------------------- %% Purpose: Verify the implementation of the ITU-T protocol H.248 %%---------------------------------------------------------------------- -%% Run the entire test suite with: -%% -%% megaco_test_lib:t(megaco_test). -%% megaco_test_lib:t({megaco_test, all}). -%% -%% Or parts of it: -%% -%% megaco_test_lib:t({megaco_test, accept}). -%%---------------------------------------------------------------------- --module(megaco_mess_test). -%% -compile(export_all). +-module(megaco_mess_SUITE). + -export([ - all/0, groups/0, + suite/0, all/0, groups/0, init_per_suite/1, end_per_suite/1, - init_per_group/2, end_per_group/2, - init_per_testcase/2, end_per_testcase/2, + init_per_group/2, end_per_group/2, + init_per_testcase/2, end_per_testcase/2, connect/1, @@ -346,24 +337,13 @@ -define(GERCV(T, VF, TO), {expect_receive, T, {VF, TO}}). -min(M) -> ?MINS(M). - -%% Test server callbacks -init_per_testcase(otp_7189 = Case, Config) -> - C = lists:keydelete(tc_timeout, 1, Config), - megaco_test_lib:init_per_testcase(Case, [{tc_timeout, min(2)} |C]); -init_per_testcase(request_and_no_reply = Case, Config) -> - C = lists:keydelete(tc_timeout, 1, Config), - megaco_test_lib:init_per_testcase(Case, [{tc_timeout, min(2)} |C]); -init_per_testcase(Case, Config) -> - C = lists:keydelete(tc_timeout, 1, Config), - megaco_test_lib:init_per_testcase(Case, [{tc_timeout, min(1)} |C]). -end_per_testcase(Case, Config) -> - megaco_test_lib:end_per_testcase(Case, Config). +%%====================================================================== +%% Common Test interface functions +%%====================================================================== - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +suite() -> + [{ct_hooks, [ts_install_cth]}]. all() -> [ @@ -376,87 +356,188 @@ all() -> groups() -> [ - {request_and_reply, [], - [request_and_reply_plain, - request_and_no_reply, - request_and_reply_pending_ack_no_pending, - request_and_reply_pending_ack_one_pending, - single_trans_req_and_reply, - single_trans_req_and_reply_sendopts, - request_and_reply_and_ack, - request_and_reply_and_no_ack, - request_and_reply_and_late_ack, - trans_req_and_reply_and_req]}, - {pending_ack, [], - [pending_ack_plain, - request_and_pending_and_late_reply]}, - {tickets, [], - [otp_4359, - otp_4836, - otp_5805, - otp_5881, - otp_5887, - otp_6253, - otp_6275, - otp_6276, - {group, otp_6442}, - {group, otp_6865}, - otp_7189, - otp_7259, - otp_7713, - {group, otp_8183}, - otp_8212]}, - {otp_6442, [], - [otp_6442_resend_request1, - otp_6442_resend_request2, - otp_6442_resend_reply1, - otp_6442_resend_reply2]}, - {otp_6865, [], - [otp_6865_request_and_reply_plain_extra1, - otp_6865_request_and_reply_plain_extra2]}, - {otp_8183, [], [otp_8183_request1]} + {request_and_reply, [], request_and_reply_cases()}, + {pending_ack, [], pending_ack_cases()}, + {tickets, [], tickets_cases()}, + {otp6442, [], otp6442_cases()}, + {otp6865, [], otp6865_cases()}, + {otp8183, [], otp8183_cases()} ]. +request_and_reply_cases() -> + [ + request_and_reply_plain, + request_and_no_reply, + request_and_reply_pending_ack_no_pending, + request_and_reply_pending_ack_one_pending, + single_trans_req_and_reply, + single_trans_req_and_reply_sendopts, + request_and_reply_and_ack, + request_and_reply_and_no_ack, + request_and_reply_and_late_ack, + trans_req_and_reply_and_req + ]. -init_per_suite(Config) -> - io:format("~w:init_per_suite -> entry with" - "~n Config: ~p" - "~n OS Type: ~p" - "~n OS Version: ~s" - "~n", - [?MODULE, - Config, - os:type(), - case os:version() of - {Major, Minor, Release} -> - ?F("~w.~w.~w", [Major, Minor, Release]); - Str when is_list(Str) -> - Str - end]), - Config. +pending_ack_cases() -> + [ + pending_ack_plain, + request_and_pending_and_late_reply + ]. + +tickets_cases() -> + [ + otp_4359, + otp_4836, + otp_5805, + otp_5881, + otp_5887, + otp_6253, + otp_6275, + otp_6276, + {group, otp6442}, + {group, otp6865}, + otp_7189, + otp_7259, + otp_7713, + {group, otp8183}, + otp_8212 + ]. + +otp6442_cases() -> + [ + otp_6442_resend_request1, + otp_6442_resend_request2, + otp_6442_resend_reply1, + otp_6442_resend_reply2 + ]. + +otp6865_cases() -> + [ + otp_6865_request_and_reply_plain_extra1, + otp_6865_request_and_reply_plain_extra2 + ]. + +otp8183_cases() -> + [ + otp_8183_request1 + ]. -end_per_suite(_Config) -> - io:format("~w:end_per_suite -> entry with" - "~n _Config: ~p" - "~n", [?MODULE, _Config]), - ok. -init_per_group(_GroupName, Config) -> - io:format("~w:init_per_group -> entry with" - "~n _GroupName: ~p" - "~n Config: ~p" - "~n", [?MODULE, _GroupName, Config]), +%% +%% ----- +%% + +init_per_suite(suite) -> + []; +init_per_suite(doc) -> + []; +init_per_suite(Config0) when is_list(Config0) -> + + ?ANNOUNCE_SUITE_INIT(), + + p("init_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + case ?LIB:init_per_suite(Config0) of + {skip, _} = SKIP -> + SKIP; + + Config1 when is_list(Config1) -> + + %% We need a (local) monitor on this node also + megaco_test_sys_monitor:start(), + + p("init_per_suite -> end when" + "~n Config: ~p" + "~n Nodes: ~p", [Config1, erlang:nodes()]), + + Config1 + end. + +end_per_suite(suite) -> []; +end_per_suite(doc) -> []; +end_per_suite(Config0) when is_list(Config0) -> + + p("end_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + megaco_test_sys_monitor:stop(), + Config1 = ?LIB:end_per_suite(Config0), + + p("end_per_suite -> end when" + "~n Nodes: ~p", [erlang:nodes()]), + + Config1. + + +%% +%% ----- +%% + +init_per_group(Group, Config) -> + ?ANNOUNCE_GROUP_INIT(Group), + + p("init_per_group -> entry with" + "~n Config: ~p" + "~n", [Config]), + Config. end_per_group(_GroupName, Config) -> - io:format("~w:end_per_group -> entry with" - "~n _GroupName: ~p" - "~n Config: ~p" - "~n", [?MODULE, _GroupName, Config]), + p("end_per_group -> entry with" + "~n Config: ~p" + "~n", [Config]), Config. + +%% +%% ----- +%% + +init_per_testcase(Case, Config) -> + + p("init_per_testcase -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config, erlang:nodes()]), + + init_per_testcase2(Case, Config). + +init_per_testcase2(otp_7189 = Case, Config) -> + C = lists:keydelete(tc_timeout, 1, Config), + init_per_testcase3(Case, [{tc_timeout, min(2)} |C]); +init_per_testcase2(request_and_no_reply = Case, Config) -> + C = lists:keydelete(tc_timeout, 1, Config), + init_per_testcase3(Case, [{tc_timeout, min(2)} |C]); +init_per_testcase2(Case, Config) -> + C = lists:keydelete(tc_timeout, 1, Config), + init_per_testcase3(Case, [{tc_timeout, min(1)} |C]). + +init_per_testcase3(Case, Config) -> + megaco_test_global_sys_monitor:reset_events(), + megaco_test_lib:init_per_testcase(Case, Config). + + +end_per_testcase(Case, Config) -> + + p("end_per_testcase -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config, erlang:nodes()]), + + p("system events during test: " + "~n ~p", [megaco_test_global_sys_monitor:events()]), + + megaco_test_lib:end_per_testcase(Case, Config). + + + +min(M) -> ?MINS(M). + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% connect(suite) -> @@ -13937,3 +14018,23 @@ mtime() -> {A,B,C} = erlang:timestamp(), A*1000000000+B*1000+(C div 1000). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% p(F) -> +%% p(F, []). + +p(F, A) -> + p(get(sname), F, A). + +p(S, F, A) when is_list(S) -> + io:format("*** [~s] ~p ~s ***" + "~n " ++ F ++ "~n", + [?FTS(), self(), S | A]); +p(_S, F, A) -> + io:format("*** [~s] ~p *** " + "~n " ++ F ++ "~n", + [?FTS(), self() | A]). + + diff --git a/lib/megaco/test/megaco_mib_test.erl b/lib/megaco/test/megaco_mib_SUITE.erl index 9d1d408f18..8763145bbc 100644 --- a/lib/megaco/test/megaco_mib_test.erl +++ b/lib/megaco/test/megaco_mib_SUITE.erl @@ -22,17 +22,14 @@ %%---------------------------------------------------------------------- %% Purpose: Verify the application specifics of the Megaco application %%---------------------------------------------------------------------- --module(megaco_mib_test). --export([ - t/0, t/1, +-module(megaco_mib_SUITE). - all/0, - groups/0, - init_per_testcase/2, - end_per_testcase/2, - init_per_group/2, - end_per_group/2, +-export([ + suite/0, all/0, groups/0, + init_per_suite/1, end_per_suite/1, + init_per_group/2, end_per_group/2, + init_per_testcase/2, end_per_testcase/2, plain/1, connect/1, @@ -51,9 +48,9 @@ handle_trans_ack/5 ]). --include("megaco_test_lib.hrl"). -include_lib("megaco/include/megaco.hrl"). -include_lib("megaco/include/megaco_message_v1.hrl"). +-include("megaco_test_lib.hrl"). -define(TEST_VERBOSITY, info). % silence | info | debug -define(MGC_VERBOSITY, info). @@ -73,14 +70,98 @@ state = initiated, load_counter = 0}). -t() -> megaco_test_lib:t(?MODULE). -t(Case) -> megaco_test_lib:t({?MODULE, Case}). +%%====================================================================== +%% Common Test interface functions +%%====================================================================== + +suite() -> + [{ct_hooks, [ts_install_cth]}]. + +all() -> + [ + plain, + connect, + traffic + ]. + +groups() -> + []. + + + +%% +%% ----- +%% + +init_per_suite(suite) -> + []; +init_per_suite(doc) -> + []; +init_per_suite(Config0) when is_list(Config0) -> + + ?ANNOUNCE_SUITE_INIT(), + + p("init_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + case ?LIB:init_per_suite(Config0) of + {skip, _} = SKIP -> + SKIP; + + Config1 when is_list(Config1) -> + + %% We need a (local) monitor on this node also + megaco_test_sys_monitor:start(), + + p("init_per_suite -> end when" + "~n Config: ~p" + "~n Nodes: ~p", [Config1, erlang:nodes()]), + + Config1 + end. + +end_per_suite(suite) -> []; +end_per_suite(doc) -> []; +end_per_suite(Config0) when is_list(Config0) -> + + p("end_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + megaco_test_sys_monitor:stop(), + Config1 = ?LIB:end_per_suite(Config0), + + p("end_per_suite -> end when" + "~n Nodes: ~p", [erlang:nodes()]), + + Config1. + + +%% +%% ----- +%% + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + + +%% +%% ----- +%% -%% Test server callbacks init_per_testcase(Case, Config) -> - progress("init_per_testcase -> ~w", [Case]), process_flag(trap_exit, true), + + progress("init_per_testcase -> ~w", [Case]), + + megaco_test_global_sys_monitor:reset_events(), + case Case of traffic -> Conf0 = lists:keydelete(tc_timeout, 1, Config), @@ -91,24 +172,14 @@ init_per_testcase(Case, Config) -> end. end_per_testcase(Case, Config) -> - progress("end_per_testcase -> ~w", [Case]), process_flag(trap_exit, false), - megaco_test_lib:end_per_testcase(Case, Config). + progress("end_per_testcase -> ~w", [Case]), -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + p("system events during test: " + "~n ~p", [megaco_test_global_sys_monitor:events()]), -all() -> - [plain, connect, traffic]. - -groups() -> - []. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. + megaco_test_lib:end_per_testcase(Case, Config). @@ -232,8 +303,7 @@ connect(Config) when is_list(Config) -> "~n MgcNode: ~p" "~n Mg1Node: ~p" "~n Mg2Node: ~p", [MgcNode, Mg1Node, Mg2Node]), - ok = megaco_test_lib:start_nodes([MgcNode, Mg1Node, Mg2Node], - ?FILE, ?LINE), + ok = ?START_NODES([MgcNode, Mg1Node, Mg2Node]), %% Start the MGC and MGs ET = [{text,tcp}, {text,udp}, {binary,tcp}, {binary,udp}], @@ -343,9 +413,7 @@ traffic(Config) when is_list(Config) -> "~n Mg3Node: ~p" "~n Mg4Node: ~p", [MgcNode, Mg1Node, Mg2Node, Mg3Node, Mg4Node]), - ok = megaco_test_lib:start_nodes([MgcNode, - Mg1Node, Mg2Node, Mg3Node, Mg4Node], - ?FILE, ?LINE), + ok = ?START_NODES([MgcNode, Mg1Node, Mg2Node, Mg3Node, Mg4Node]), %% Start the MGC and MGs i("traffic -> start the MGC"), @@ -1682,6 +1750,22 @@ which_local_addr2([_|T]) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% p(F) -> +%% p(F, []). + +p(F, A) -> + p(get(sname), F, A). + +p(S, F, A) when is_list(S) -> + io:format("*** [~s] ~p ~s ***" + "~n " ++ F ++ "~n", + [?FTS(), self(), S | A]); +p(_S, F, A) -> + io:format("*** [~s] ~p *** " + "~n " ++ F ++ "~n", + [?FTS(), self() | A]). + + i(F) -> i(F, []). diff --git a/lib/megaco/test/megaco_mreq_test.erl b/lib/megaco/test/megaco_mreq_SUITE.erl index ba20329ab3..9596389d2b 100644 --- a/lib/megaco/test/megaco_mreq_test.erl +++ b/lib/megaco/test/megaco_mreq_SUITE.erl @@ -22,27 +22,22 @@ %%---------------------------------------------------------------------- %% Purpose: Verify the application specifics of the Megaco application %%---------------------------------------------------------------------- --module(megaco_mreq_test). +-module(megaco_mreq_SUITE). -export([ - all/0, - groups/0, - - init_per_group/2, - end_per_group/2, - init_per_testcase/2, - end_per_testcase/2, + suite/0, all/0, groups/0, + init_per_suite/1, end_per_suite/1, + init_per_group/2, end_per_group/2, + init_per_testcase/2, end_per_testcase/2, req_and_rep/1, req_and_pending/1, - req_and_cancel/1, - - t/0, t/1 + req_and_cancel/1 ]). --include("megaco_test_lib.hrl"). -include_lib("megaco/include/megaco.hrl"). -include_lib("megaco/include/megaco_message_v1.hrl"). +-include("megaco_test_lib.hrl"). -define(TEST_VERBOSITY, debug). -define(MGC_VERBOSITY, debug). @@ -76,29 +71,77 @@ -define(MG_APPLY_LOAD(Pid,CntStart), megaco_test_mg:apply_load(Pid,CntStart)). -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%====================================================================== +%% Common Test interface functions +%%====================================================================== -t() -> megaco_test_lib:t(?MODULE). -t(Case) -> megaco_test_lib:t({?MODULE, Case}). +suite() -> + [{ct_hooks, [ts_install_cth]}]. +all() -> + [ + req_and_rep, + req_and_pending, + req_and_cancel + ]. -%% Test server callbacks -init_per_testcase(Case, Config) -> - process_flag(trap_exit, true), - megaco_test_lib:init_per_testcase(Case, Config). +groups() -> + []. -end_per_testcase(Case, Config) -> - process_flag(trap_exit, false), - megaco_test_lib:end_per_testcase(Case, Config). -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% ----- +%% -all() -> - [req_and_rep, req_and_pending, req_and_cancel]. +init_per_suite(suite) -> + []; +init_per_suite(doc) -> + []; +init_per_suite(Config0) when is_list(Config0) -> -groups() -> - []. + ?ANNOUNCE_SUITE_INIT(), + + p("init_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + case ?LIB:init_per_suite(Config0) of + {skip, _} = SKIP -> + SKIP; + + Config1 when is_list(Config1) -> + + %% We need a (local) monitor on this node also + megaco_test_sys_monitor:start(), + + p("init_per_suite -> end when" + "~n Config: ~p" + "~n Nodes: ~p", [Config1, erlang:nodes()]), + + Config1 + end. + +end_per_suite(suite) -> []; +end_per_suite(doc) -> []; +end_per_suite(Config0) when is_list(Config0) -> + + p("end_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + megaco_test_sys_monitor:stop(), + Config1 = ?LIB:end_per_suite(Config0), + + p("end_per_suite -> end when" + "~n Nodes: ~p", [erlang:nodes()]), + + Config1. + + +%% +%% ----- +%% init_per_group(_GroupName, Config) -> Config. @@ -108,6 +151,30 @@ end_per_group(_GroupName, Config) -> +init_per_testcase(Case, Config) -> + process_flag(trap_exit, true), + + p("init_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config, erlang:nodes()]), + + megaco_test_global_sys_monitor:reset_events(), + megaco_test_lib:init_per_testcase(Case, Config). + +end_per_testcase(Case, Config) -> + process_flag(trap_exit, false), + + p("end_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config, erlang:nodes()]), + + p("system events during test: " + "~n ~p", [megaco_test_global_sys_monitor:events()]), + + megaco_test_lib:end_per_testcase(Case, Config). + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% req_and_rep(suite) -> @@ -451,6 +518,11 @@ sleep(X) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +p(F, A) -> + io:format("*** [~s] ~p ***" + "~n " ++ F ++ "~n", + [?FTS(), self() | A]). + i(F) -> i(F, []). diff --git a/lib/megaco/test/megaco_pending_limit_test.erl b/lib/megaco/test/megaco_pending_limit_SUITE.erl index e0c0468d99..ca802023c2 100644 --- a/lib/megaco/test/megaco_pending_limit_test.erl +++ b/lib/megaco/test/megaco_pending_limit_SUITE.erl @@ -24,11 +24,14 @@ %% Testing the xxxOriginatingPendingLimit property of the %% root package %%---------------------------------------------------------------------- --module(megaco_pending_limit_test). +-module(megaco_pending_limit_SUITE). + +-export([ + suite/0, all/0, groups/0, + init_per_suite/1, end_per_suite/1, + init_per_group/2, end_per_group/2, + init_per_testcase/2, end_per_testcase/2, --export([t/0, t/1]). --export([init_per_testcase/2, end_per_testcase/2]). --export([all/0,groups/0,init_per_group/2,end_per_group/2, sent_timer_late_reply/1, sent_timer_exceeded/1, sent_timer_exceeded_long/1, @@ -69,9 +72,9 @@ ]). -endif. --include("megaco_test_lib.hrl"). -include_lib("megaco/include/megaco.hrl"). -include_lib("megaco/include/megaco_message_v1.hrl"). +-include("megaco_test_lib.hrl"). -define(TEST_VERBOSITY, debug). -define(MGC_VERBOSITY, debug). @@ -125,38 +128,142 @@ -define(MG_GRP_REQ(Pid,N), megaco_test_mg:group_requests(Pid,N)). -define(MG_ECC(Pid, M, T, F), megaco_test_mg:enable_test_code(Pid,M,T,F)). -t() -> megaco_test_lib:t(?MODULE). -t(Case) -> megaco_test_lib:t({?MODULE, Case}). +%%====================================================================== +%% Common Test interface functions +%%====================================================================== + +suite() -> + [{ct_hooks, [ts_install_cth]}]. + +all() -> + [ + {group, sent}, + {group, recv}, + {group, tickets} + ]. + +groups() -> + [ + {sent, [], sent_cases()}, + {recv, [], recv_cases()}, + {tickets, [], tickets_cases()} + ]. + +sent_cases() -> + [ + sent_timer_late_reply, + sent_timer_exceeded, + sent_timer_exceeded_long, + sent_resend_late_reply, + sent_resend_exceeded, + sent_resend_exceeded_long + ]. + +recv_cases() -> + [ + recv_limit_exceeded1, + recv_limit_exceeded2 + ]. + +tickets_cases() -> + [ + otp_4956, + otp_5310, + otp_5619 + ]. + + + +%% +%% ----- +%% + +init_per_suite(suite) -> + []; +init_per_suite(doc) -> + []; +init_per_suite(Config0) when is_list(Config0) -> + + ?ANNOUNCE_SUITE_INIT(), + + p("init_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + case ?LIB:init_per_suite(Config0) of + {skip, _} = SKIP -> + SKIP; + + Config1 when is_list(Config1) -> + + %% We need a (local) monitor on this node also + megaco_test_sys_monitor:start(), + + p("init_per_suite -> end when" + "~n Config: ~p" + "~n Nodes: ~p", [Config1, erlang:nodes()]), + + Config1 + end. + +end_per_suite(suite) -> []; +end_per_suite(doc) -> []; +end_per_suite(Config0) when is_list(Config0) -> + + p("end_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + megaco_test_sys_monitor:stop(), + Config1 = ?LIB:end_per_suite(Config0), + + p("end_per_suite -> end when" + "~n Nodes: ~p", [erlang:nodes()]), + + Config1. + + +%% +%% ----- +%% + +init_per_group(Group, Config) -> + ?ANNOUNCE_GROUP_INIT(Group), + Config. + +end_per_group(_Group, Config) -> + Config. + + + +%% +%% ----- +%% -%% Test server callbacks init_per_testcase(Case, Config) -> process_flag(trap_exit, true), + + p("init_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config, erlang:nodes()]), + + megaco_test_global_sys_monitor:reset_events(), megaco_test_lib:init_per_testcase(Case, Config). end_per_testcase(Case, Config) -> process_flag(trap_exit, false), - megaco_test_lib:end_per_testcase(Case, Config). -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + p("end_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config, erlang:nodes()]), -all() -> - [{group, sent}, {group, recv}, {group, tickets}]. + p("system events during test: " + "~n ~p", [megaco_test_global_sys_monitor:events()]), + + megaco_test_lib:end_per_testcase(Case, Config). -groups() -> - [{sent, [], - [sent_timer_late_reply, sent_timer_exceeded, - sent_timer_exceeded_long, sent_resend_late_reply, - sent_resend_exceeded, sent_resend_exceeded_long]}, - {recv, [], - [recv_limit_exceeded1, recv_limit_exceeded2]}, - {tickets, [], [otp_4956, otp_5310, otp_5619]}]. - -init_per_group(_GroupName, Config) -> - Config. -end_per_group(_GroupName, Config) -> - Config. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% %%% @@ -2074,6 +2181,12 @@ sleep(X) -> receive after X -> ok end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +p(F, A) -> + io:format("*** [~s] ~p ***" + "~n " ++ F ++ "~n", + [?FTS(), self() | A]). + + i(F) -> i(F, []). diff --git a/lib/megaco/test/megaco_sdp_test.erl b/lib/megaco/test/megaco_sdp_SUITE.erl index 4065b6e629..3d417323c7 100644 --- a/lib/megaco/test/megaco_sdp_test.erl +++ b/lib/megaco/test/megaco_sdp_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2016. All Rights Reserved. +%% Copyright Ericsson AB 2001-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -23,31 +23,23 @@ %% Purpose: Test application config %%---------------------------------------------------------------------- --module(megaco_sdp_test). - --export([all/0,groups/0,init_per_group/2,end_per_group/2, - decode_encode/1, - - otp8123/1, +-module(megaco_sdp_SUITE). +-export([ + suite/0, all/0, groups/0, + init_per_suite/1, end_per_suite/1, + init_per_group/2, end_per_group/2, init_per_testcase/2, end_per_testcase/2, - t/0, t/1]). + decode_encode/1, + otp8123/1 + ]). --include("megaco_test_lib.hrl"). -include_lib("megaco/include/megaco.hrl"). -include_lib("megaco/include/megaco_message_v1.hrl"). -include_lib("megaco/include/megaco_sdp.hrl"). +-include("megaco_test_lib.hrl"). -t() -> megaco_test_lib:t(?MODULE). -t(Case) -> megaco_test_lib:t({?MODULE, Case}). - -%% Test server callbacks -init_per_testcase(Case, Config) -> - megaco_test_lib:init_per_testcase(Case, Config). - -end_per_testcase(Case, Config) -> - megaco_test_lib:end_per_testcase(Case, Config). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -55,21 +47,114 @@ end_per_testcase(Case, Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +suite() -> + [{ct_hooks, [ts_install_cth]}]. + all() -> - [decode_encode, {group, tickets}]. + [ + decode_encode, + {group, tickets} + ]. groups() -> - [{tickets, [], [otp8123]}]. + [ + {tickets, [], tickets_cases()} + ]. + +tickets_cases() -> + [ + otp8123 + ]. + + +%% +%% ----- +%% + +init_per_suite(suite) -> + []; +init_per_suite(doc) -> + []; +init_per_suite(Config0) when is_list(Config0) -> + + ?ANNOUNCE_SUITE_INIT(), + + p("init_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + case ?LIB:init_per_suite([{sysmon, false}|Config0]) of + {skip, _} = SKIP -> + SKIP; + + Config1 when is_list(Config1) -> + + p("init_per_suite -> end when" + "~n Config: ~p" + "~n Nodes: ~p", [Config1, erlang:nodes()]), + + Config1 + end. + +end_per_suite(suite) -> []; +end_per_suite(doc) -> []; +end_per_suite(Config0) when is_list(Config0) -> + + p("end_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + Config1 = ?LIB:end_per_suite(Config0), + + p("end_per_suite -> end when" + "~n Nodes: ~p", [erlang:nodes()]), + + Config1. + + + +%% +%% ----- +%% init_per_group(_GroupName, Config) -> + p("init_per_group -> entry with" + "~n Config: ~p" + "~n", [Config]), Config. end_per_group(_GroupName, Config) -> + p("end_per_group -> entry with" + "~n Config: ~p" + "~n", [Config]), Config. +%% +%% ----- +%% + +init_per_testcase(Case, Config) -> + + p("init_per_testcase -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config, erlang:nodes()]), + + megaco_test_lib:init_per_testcase(Case, Config). + +end_per_testcase(Case, Config) -> + + p("end_per_testcase -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config, erlang:nodes()]), + + megaco_test_lib:end_per_testcase(Case, Config). + + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% decode_encode(suite) -> @@ -1285,5 +1370,21 @@ i2s(S) when is_list(S) -> S. -%% error(Reason) -> -%% throw({error, Reason}). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% p(F) -> +%% p(F, []). + +p(F, A) -> + p(get(sname), F, A). + +p(S, F, A) when is_list(S) -> + io:format("*** [~s] ~p ~s ***" + "~n " ++ F ++ "~n", + [?FTS(), self(), S | A]); +p(_S, F, A) -> + io:format("*** [~s] ~p *** " + "~n " ++ F ++ "~n", + [?FTS(), self() | A]). + + diff --git a/lib/megaco/test/megaco_segment_test.erl b/lib/megaco/test/megaco_segment_SUITE.erl index 47f708a14b..0d3cc660f2 100644 --- a/lib/megaco/test/megaco_segment_test.erl +++ b/lib/megaco/test/megaco_segment_SUITE.erl @@ -22,13 +22,15 @@ %%---------------------------------------------------------------------- %% Purpose: Test the segment package introduced in v3 of the megaco std. %%---------------------------------------------------------------------- --module(megaco_segment_test). --export([t/0, t/1]). --export([init_per_testcase/2, end_per_testcase/2]). --export([all/0,groups/0,init_per_group/2,end_per_group/2, +-module(megaco_segment_SUITE). + +-export([ + suite/0, all/0, groups/0, + init_per_suite/1, end_per_suite/1, + init_per_group/2, end_per_group/2, + init_per_testcase/2, end_per_testcase/2, - send_segmented_msg_plain1/1, send_segmented_msg_plain2/1, send_segmented_msg_plain3/1, @@ -56,42 +58,131 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -t() -> megaco_test_lib:t(?MODULE). -t(Case) -> megaco_test_lib:t({?MODULE, Case}). +%%====================================================================== +%% Common Test interface functions +%%====================================================================== + +suite() -> + [{ct_hooks, [ts_install_cth]}]. + +all() -> + [ + {group, send}, + {group, recv} + ]. + +groups() -> + [ + {send, [], send_cases()}, + {recv, [], recv_cases()} + ]. + +send_cases() -> + [ + send_segmented_msg_plain1, + send_segmented_msg_plain2, + send_segmented_msg_plain3, + send_segmented_msg_plain4, + send_segmented_msg_ooo1, + send_segmented_msg_missing_seg_reply1, + send_segmented_msg_missing_seg_reply2 + ]. + +recv_cases() -> + [ + recv_segmented_msg_plain, + recv_segmented_msg_ooo_seg, + recv_segmented_msg_missing_seg1, + recv_segmented_msg_missing_seg2 + ]. + + + +%% +%% ----- +%% + +init_per_suite(suite) -> + []; +init_per_suite(doc) -> + []; +init_per_suite(Config0) when is_list(Config0) -> + + ?ANNOUNCE_SUITE_INIT(), + + p("init_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + case ?LIB:init_per_suite(Config0) of + {skip, _} = SKIP -> + SKIP; + + Config1 when is_list(Config1) -> + + %% We need a (local) monitor on this node also + megaco_test_sys_monitor:start(), + + p("init_per_suite -> end when" + "~n Config: ~p" + "~n Nodes: ~p", [Config1, erlang:nodes()]), + + Config1 + end. + +end_per_suite(suite) -> []; +end_per_suite(doc) -> []; +end_per_suite(Config0) when is_list(Config0) -> + + p("end_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + megaco_test_sys_monitor:stop(), + Config1 = ?LIB:end_per_suite(Config0), + + p("end_per_suite -> end when" + "~n Nodes: ~p", [erlang:nodes()]), + + Config1. + + + +%% +%% ----- +%% + +init_per_group(Group, Config) -> + ?ANNOUNCE_GROUP_INIT(Group), + Config. + +end_per_group(_Group, Config) -> + Config. + -%% Test server callbacks init_per_testcase(Case, Config) -> process_flag(trap_exit, true), + + p("init_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config, erlang:nodes()]), + + megaco_test_global_sys_monitor:reset_events(), megaco_test_lib:init_per_testcase(Case, Config). end_per_testcase(Case, Config) -> process_flag(trap_exit, false), - megaco_test_lib:end_per_testcase(Case, Config). - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + p("end_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config, erlang:nodes()]), -all() -> - [{group, send}, {group, recv}]. + p("system events during test: " + "~n ~p", [megaco_test_global_sys_monitor:events()]), -groups() -> - [{send, [], - [send_segmented_msg_plain1, send_segmented_msg_plain2, - send_segmented_msg_plain3, send_segmented_msg_plain4, - send_segmented_msg_ooo1, - send_segmented_msg_missing_seg_reply1, - send_segmented_msg_missing_seg_reply2]}, - {recv, [], - [recv_segmented_msg_plain, recv_segmented_msg_ooo_seg, - recv_segmented_msg_missing_seg1, - recv_segmented_msg_missing_seg2]}]. - -init_per_group(_GroupName, Config) -> - Config. + megaco_test_lib:end_per_testcase(Case, Config). -end_per_group(_GroupName, Config) -> - Config. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -7737,6 +7828,12 @@ sleep(X) -> receive after X -> ok end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +p(F, A) -> + io:format("*** [~s] ~p ***" + "~n " ++ F ++ "~n", + [?FTS(), self() | A]). + + i(F) -> i(F, []). diff --git a/lib/megaco/test/megaco_tcp_test.erl b/lib/megaco/test/megaco_tcp_SUITE.erl index cc66a40f43..5981e34f19 100644 --- a/lib/megaco/test/megaco_tcp_test.erl +++ b/lib/megaco/test/megaco_tcp_SUITE.erl @@ -22,7 +22,7 @@ %%---------------------------------------------------------------------- %% Purpose: %%---------------------------------------------------------------------- --module(megaco_tcp_test). +-module(megaco_tcp_SUITE). %%---------------------------------------------------------------------- %% Include files @@ -37,7 +37,11 @@ %% External exports %%---------------------------------------------------------------------- -export([ - all/0,groups/0,init_per_group/2,end_per_group/2, + suite/0, all/0, groups/0, + init_per_suite/1, end_per_suite/1, + init_per_group/2, end_per_group/2, + init_per_testcase/2, end_per_testcase/2, + start_normal/1, start_invalid_opt/1, start_and_stop/1, @@ -47,11 +51,8 @@ accept_process/1, accept_supervisor/1, connection_supervisor/1, - tcp_server/1, - - init_per_testcase/2, end_per_testcase/2, + tcp_server/1 - t/0, t/1 ]). %%---------------------------------------------------------------------- @@ -78,55 +79,25 @@ %%====================================================================== -%% External functions +%% Common Test interface functions %%====================================================================== -%%---------------------------------------------------------------------- -%% Function: t/0 -%% Description: Run all test cases -%%---------------------------------------------------------------------- -t() -> megaco_test_lib:t(?MODULE). - - -%%---------------------------------------------------------------------- -%% Function: t/1 -%% Description: Run the specified test cases -%%---------------------------------------------------------------------- -t(Case) -> megaco_test_lib:t({?MODULE, Case}). - - -%%====================================================================== -%% Test server callbacks -%%====================================================================== -%%---------------------------------------------------------------------- -%% Function: init_per_testcase/2 -%% Description: -%%---------------------------------------------------------------------- -init_per_testcase(Case, Config) -> - megaco_test_lib:init_per_testcase(Case, Config). +suite() -> + [{ct_hooks, [ts_install_cth]}]. -%%---------------------------------------------------------------------- -%% Function: end_per_testcase/2 -%% Description: -%%---------------------------------------------------------------------- -end_per_testcase(Case, Config) -> - megaco_test_lib:end_per_testcase(Case, Config). - - -%%====================================================================== -%% Test case definitions -%%====================================================================== all() -> [ {group, start}, {group, sending}, - {group, errors} + {group, error} ]. groups() -> - [{start, [], start_cases()}, + [ + {start, [], start_cases()}, {sending, [], sending_cases()}, - {errors, [], errors_cases()}]. + {error, [], error_cases()} + ]. start_cases() -> [ @@ -141,7 +112,7 @@ sending_cases() -> block_unblock ]. -errors_cases() -> +error_cases() -> [ socket_failure, accept_process, @@ -151,13 +122,95 @@ errors_cases() -> ]. -init_per_group(_GroupName, Config) -> +%% +%% ----- +%% + +init_per_suite(suite) -> + []; +init_per_suite(doc) -> + []; +init_per_suite(Config0) when is_list(Config0) -> + + ?ANNOUNCE_SUITE_INIT(), + + p("init_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + case ?LIB:init_per_suite(Config0) of + {skip, _} = SKIP -> + SKIP; + + Config1 when is_list(Config1) -> + + %% We need a (local) monitor on this node also + megaco_test_sys_monitor:start(), + + p("init_per_suite -> end when" + "~n Config: ~p" + "~n Nodes: ~p", [Config1, erlang:nodes()]), + + Config1 + end. + +end_per_suite(suite) -> []; +end_per_suite(doc) -> []; +end_per_suite(Config0) when is_list(Config0) -> + + p("end_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + megaco_test_sys_monitor:stop(), + Config1 = ?LIB:end_per_suite(Config0), + + p("end_per_suite -> end when" + "~n Nodes: ~p", [erlang:nodes()]), + + Config1. + + +%% +%% ----- +%% + +init_per_group(Group, Config) -> + ?ANNOUNCE_GROUP_INIT(Group), Config. end_per_group(_GroupName, Config) -> Config. +%% +%% ----- +%% + +init_per_testcase(Case, Config) -> + + p("init_per_testcase -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config, erlang:nodes()]), + + megaco_test_global_sys_monitor:reset_events(), + + megaco_test_lib:init_per_testcase(Case, Config). + +end_per_testcase(Case, Config) -> + + p("end_per_testcase -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config, erlang:nodes()]), + + p("system events during test: " + "~n ~p", [megaco_test_global_sys_monitor:events()]), + + megaco_test_lib:end_per_testcase(Case, Config). + + + + %% ------------------ start ------------------------ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1240,9 +1293,9 @@ p(S, F, A) when is_list(S) -> "~n " ++ F ++ "~n", [?FTS(), self(), S | A]); p(_S, F, A) -> - io:format("*** [~s] ~p ~s *** " + io:format("*** [~s] ~p *** " "~n " ++ F ++ "~n", - [?FTS(), self(), "undefined" | A]). + [?FTS(), self() | A]). ms() -> diff --git a/lib/megaco/test/megaco_test_global_sys_monitor.erl b/lib/megaco/test/megaco_test_global_sys_monitor.erl new file mode 100644 index 0000000000..119b1854c2 --- /dev/null +++ b/lib/megaco/test/megaco_test_global_sys_monitor.erl @@ -0,0 +1,235 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2019-2019. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +-module(megaco_test_global_sys_monitor). + +-export([start/0, stop/0, + reset_events/0, + events/0, + log/1]). +-export([init/1]). + +-include("megaco_test_lib.hrl"). + +-define(NAME, ?MODULE). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +start() -> + Parent = self(), + proc_lib:start(?MODULE, init, [Parent]). + +stop() -> + cast(stop). + +%% This does not reset the global counter but the "collector" +%% See events for more info. +reset_events() -> + call(reset_events). + +events() -> + call(events). + +log(Event) -> + cast({node(), Event}). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +init(Parent) -> + process_flag(priority, high), + case global:register_name(?NAME, self()) of + yes -> + info_msg("Starting as ~p (on ~p)", [self(), node()]), + proc_lib:init_ack(Parent, {ok, self()}), + loop(#{parent => Parent, ev_cnt => 0, evs => []}); + no -> + warning_msg("Already started", []), + proc_lib:init_ack(Parent, {error, already_started}), + exit(normal) + end. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +loop(State) -> + receive + {?MODULE, stop} -> + warning_msg("Stopping with ~w events counted", + [maps:get(ev_cnt, State)]), + exit(normal); + + {?MODULE, Ref, From, reset_events} -> + TotEvCnt = maps:get(ev_cnt, State), + EvCnt = length(maps:get(evs, State)), + info_msg("Reset events when" + "~n Total Number of Events: ~p" + "~n Current Number of Events: ~p", + [TotEvCnt, EvCnt]), + From ! {?MODULE, Ref, {ok, {TotEvCnt, EvCnt}}}, + loop(State#{evs => []}); + + {?MODULE, Ref, From, events} -> + Evs = maps:get(evs, State), + From ! {?MODULE, Ref, lists:reverse(Evs)}, + loop(State); + + {?MODULE, {Node, Event}} -> + State2 = process_event(State, Node, Event), + loop(State2); + + {nodedown = Event, Node} -> + State2 = process_event(State, Node, Event), + loop(State2); + + _ -> + loop(State) + end. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +process_event(State, Node, {Pid, TS, Tag, Info}) -> + process_system_event(State, Node, Pid, TS, Tag, Info); + +process_event(State, Node, {TS, starting}) -> + FTS = megaco:format_timestamp(TS), + info_msg("System Monitor starting on node ~p at ~s", [Node, FTS]), + if + (Node =/= node()) -> + erlang:monitor_node(Node, true); + true -> + ok + end, + State; + +process_event(State, Node, {TS, stopping}) -> + FTS = ?FTS(TS), + info_msg("System Monitor stopping on node ~p at ~s", [Node, FTS]), + if + (Node =/= node()) -> + erlang:monitor_node(Node, false); + true -> + ok + end, + State; + +process_event(State, Node, {TS, already_started}) -> + FTS = megaco:format_timestamp(TS), + info_msg("System Monitor already started on node ~p at ~s", [Node, FTS]), + State; + +process_event(State, Node, nodedown) -> + info_msg("Node ~p down", [Node]), + State; + +process_event(State, Node, Event) -> + warning_msg("Received unknown event from node ~p:" + "~n ~p", [Node, Event]), + State. + + +%% System Monitor events +%% We only *count* system events +process_system_event(#{ev_cnt := Cnt, evs := Evs} = State, + Node, Pid, TS, long_gc = Ev, Info) -> + print_system_event(f("Long GC (~w)", [length(Evs)]), Node, Pid, TS, Info), + State#{ev_cnt => Cnt + 1, evs => [{Node, Ev} | Evs]}; +process_system_event(#{ev_cnt := Cnt, evs := Evs} = State, + Node, Pid, TS, long_schedule = Ev, Info) -> + print_system_event(f("Long Schedule (~w)", [length(Evs)]), Node, Pid, TS, Info), + State#{ev_cnt => Cnt + 1, evs => [{Node, Ev} | Evs]}; +process_system_event(#{ev_cnt := Cnt, evs := Evs} = State, + Node, Pid, TS, large_heap = Ev, Info) -> + print_system_event(f("Large Heap (~w)", [length(Evs)]), Node, Pid, TS, Info), + State#{ev_cnt => Cnt + 1, evs => [{Node, Ev} | Evs]}; +process_system_event(#{ev_cnt := Cnt, evs := Evs} = State, + Node, Pid, TS, busy_port = Ev, Info) -> + print_system_event(f("Busy port (~w)", [length(Evs)]), Node, Pid, TS, Info), + State#{ev_cnt => Cnt + 1, evs => [{Node, Ev} | Evs]}; +process_system_event(#{ev_cnt := Cnt, evs := Evs} = State, + Node, Pid, TS, busy_dist_port = Ev, Info) -> + print_system_event(f("Busy dist port (~w)", [length(Evs)]), + Node, Pid, TS, Info), + State#{ev_cnt => Cnt + 1, evs => [{Node, Ev} | Evs]}; + +%% And everything else +process_system_event(State, Node, Pid, TS, Tag, Info) -> + Pre = f("Unknown Event '~p'", [Tag]), + print_system_event(Pre, Node, Pid, TS, Info), + State. + + +print_system_event(Pre, Node, Pid, TS, Info) -> + FTS = megaco:format_timestamp(TS), + warning_msg("~s from ~p (~p) at ~s:" + "~n ~p", [Pre, Node, Pid, FTS, Info]). + +f(F, A) -> + lists:flatten(io_lib:format(F, A)). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +cast(Msg) -> + try global:send(?NAME, {?MODULE, Msg}) of + Pid when is_pid(Pid) -> + ok + catch + C:E:_ -> + {error, {catched, C, E}} + end. + +call(Req) -> + call(Req, infinity). + +call(Req, Timeout) -> + Ref = make_ref(), + try global:send(?NAME, {?MODULE, Ref, self(), Req}) of + Pid when is_pid(Pid) -> + receive + {?MODULE, Ref, Rep} -> + Rep + after Timeout -> + {error, timeout} + end + catch + C:E:_ -> + {error, {catched, C, E}} + end. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +info_msg(F, A) -> + error_logger:info_msg(format_msg(F, A), []). + +warning_msg(F, A) -> + error_logger:warning_msg(format_msg(F, A), []). + + +format_msg(F, A) -> + f("~n" ++ + "****** MEGACO TEST GLOBAL SYSTEM MONITOR ******~n~n" ++ + F ++ + "~n~n", + A). + diff --git a/lib/megaco/test/megaco_test_lib.erl b/lib/megaco/test/megaco_test_lib.erl index 3d26a8585f..f45d499199 100644 --- a/lib/megaco/test/megaco_test_lib.erl +++ b/lib/megaco/test/megaco_test_lib.erl @@ -42,34 +42,28 @@ flush/0, still_alive/1, - watchdog/2, display_alloc_info/0, display_system_info/1, display_system_info/2, display_system_info/3, - tickets/1, prepare_test_case/5, - t/1, - groups/1, - init_suite/2, - end_suite/2, - init_group/3, - end_group/3, - t/2, - init_per_testcase/2, - end_per_testcase/2, - proxy_start/1, proxy_start/2, mk_nodes/1, - start_nodes/3 + start_nodes/3, + start_node/3 + ]). +-export([init_per_suite/1, end_per_suite/1, + init_per_testcase/2, end_per_testcase/2]). --export([do_eval/4, proxy_init/2]). +-export([proxy_init/2]). -include("megaco_test_lib.hrl"). +-record('REASON', {mod, line, desc}). + %% ---------------------------------------------------------------- %% Time related function @@ -202,18 +196,6 @@ os_based_skip_check(OsName, OsNames) -> %% {Mod, Fun, ExpectedRes, ActualRes} %%---------------------------------------------------------------------- -tickets([Mod]) -> - tickets(Mod); -tickets(Mod) when is_atom(Mod) -> - %% p("tickets -> entry with" - %% "~n Mod: ~p", [Mod]), - Res0 = t({Mod, {group, tickets}, Mod:groups()}, default_config()), - Res = lists:flatten(Res0), - %% p("tickets(~w) -> Res: ~p~n", [Mod, Res]), - display_result(Res), - Res. - - display_alloc_info() -> io:format("Allocator memory information:~n", []), AllocInfo = alloc_info(), @@ -287,282 +269,6 @@ alloc_instance_mem_info(Key, InstanceInfo) -> end. -t([Case]) when is_atom(Case) -> - %% p("t -> entry with" - %% "~n [Case]: [~p]", [Case]), - t(Case); -t(Case) -> - %% p("t -> entry with" - %% "~n Case: ~p", [Case]), - process_flag(trap_exit, true), - MEM = fun() -> case (catch erlang:memory()) of - {'EXIT', _} -> - []; - Res -> - Res - end - end, - Alloc1 = alloc_info(), - Mem1 = MEM(), - Res = lists:flatten(t(Case, default_config())), - Alloc2 = alloc_info(), - Mem2 = MEM(), - display_result(Res, Alloc1, Mem1, Alloc2, Mem2), - Res. - - -groups(Mod) when is_atom(Mod) -> - try Mod:groups() of - Groups when is_list(Groups) -> - Groups; - BadGroups -> - exit({bad_groups, Mod, BadGroups}) - catch - _:_ -> - [] - end. - -init_suite(Mod, Config) -> - Mod:init_per_suite(Config). - -end_suite(Mod, Config) -> - Mod:end_per_suite(Config). - -init_group(Mod, Group, Config) -> - Mod:init_per_group(Group, Config). - -end_group(Mod, Group, Config) -> - Mod:init_per_group(Group, Config). - -%% This is for sub-SUITEs -t({_Mod, {NewMod, all}, _Groups}, _Config) when is_atom(NewMod) -> - %% p("t(all) -> entry with" - %% "~n NewMod: ~p", [NewMod]), - t(NewMod); -t({Mod, {group, Name} = Group, Groups}, Config) - when is_atom(Mod) andalso is_atom(Name) andalso is_list(Groups) -> - %% p("t(group) -> entry with" - %% "~n Mod: ~p" - %% "~n Name: ~p" - %% "~n Groups: ~p" - %% "~n Config: ~p", [Mod, Name, Groups, Config]), - case lists:keysearch(Name, 1, Groups) of - {value, {Name, _Props, GroupsAndCases}} -> - try init_group(Mod, Name, Config) of - Config2 when is_list(Config2) -> - Res = [t({Mod, Case, Groups}, Config2) || - Case <- GroupsAndCases], - (catch end_group(Mod, Name, Config2)), - Res; - Error -> - io:format(" => group (~w) init failed: ~p~n", - [Name, Error]), - [{failed, {Mod, Group}, Error}] - catch - exit:{skip, SkipReason} -> - io:format(" => skipping group: ~p~n", [SkipReason]), - [{skip, {Mod, Group}, SkipReason, 0}]; - error:undef -> - [t({Mod, Case, Groups}, Config) || - Case <- GroupsAndCases]; - T:E -> - [{failed, {Mod, Group}, {T,E}, 0}] - end; - false -> - exit({unknown_group, Mod, Name, Groups}) - end; -t({Mod, Fun, _}, Config) - when is_atom(Mod) andalso is_atom(Fun) -> - %% p("t -> entry with" - %% "~n Mod: ~p" - %% "~n Fun: ~p" - %% "~n Config: ~p", [Mod, Fun, Config]), - try apply(Mod, Fun, [suite]) of - [] -> - io:format("Eval: ~p:", [{Mod, Fun}]), - Res = eval(Mod, Fun, Config), - {R, _, _, _} = Res, - io:format(" ~p~n", [R]), - Res; - - Cases when is_list(Cases) -> - io:format("Expand: ~p ...~n", [{Mod, Fun}]), - Map = fun(Case) when is_atom(Case) -> {Mod, Case}; - (Case) -> Case - end, - t(lists:map(Map, Cases), Config); - - Error -> - io:format("Ignoring: ~p: ~p~n", [{Mod, Fun}, Error]), - [{failed, {Mod, Fun}, Error, 0}] - - catch - error:undef -> - io:format("Undefined: ~p~n", [{Mod, Fun}]), - [{nyi, {Mod, Fun}, ok, 0}] - - - end; -t(Mod, Config) when is_atom(Mod) -> - %% p("t -> entry with" - %% "~n Mod: ~p" - %% "~n Config: ~p", [Mod, Config]), - %% This is assumed to be a test suite, so we start by calling - %% the top test suite function(s) (all/0 and groups/0). - try Mod:all() of - Cases when is_list(Cases) -> - %% The list may contain atoms (actual test cases) and - %% group-tuples (a tuple naming a group of test cases). - %% A group is defined by the (optional) groups/0 function. - Groups = groups(Mod), - try init_suite(Mod, Config) of - Config2 when is_list(Config2) -> - Res = [t({Mod, Case, Groups}, Config2) || Case <- Cases], - (catch end_suite(Mod, Config2)), - Res; - Error -> - io:format(" => suite init failed: ~p~n", [Error]), - [{failed, {Mod, init_per_suite}, Error}] - catch - exit:{skip, SkipReason} -> - io:format(" => skipping suite: ~p~n", [SkipReason]), - [{skip, {Mod, init_per_suite}, SkipReason, 0}]; - error:undef -> - [t({Mod, Case, Groups}, Config) || Case <- Cases]; - T:E -> - io:format(" => failed suite: ~p~n", [{T,E}]), - [{failed, {Mod, init_per_suite}, {T,E}, 0}] - end; - - Crap -> - Crap - - catch - error:undef -> - io:format("Undefined: ~p~n", [{Mod, all}]), - [{nyi, {Mod, all}, ok, 0}] - - end; -t(Bad, _Config) -> - [{badarg, Bad, ok, 0}]. - -eval(Mod, Fun, Config) -> - TestCase = {?MODULE, Mod, Fun}, - Label = lists:concat(["TEST CASE: ", Fun]), - megaco:report_event(40, ?MODULE, Mod, Label ++ " started", - [TestCase, Config]), - global:register_name(megaco_test_case_sup, self()), - Flag = process_flag(trap_exit, true), - put(megaco_test_server, true), - Config2 = Mod:init_per_testcase(Fun, Config), - Pid = spawn_link(fun() -> do_eval(self(), Mod, Fun, Config2) end), - R = wait_for_evaluator(Pid, Mod, Fun, Config2, []), - Mod:end_per_testcase(Fun, Config2), - erase(megaco_test_server), - global:unregister_name(megaco_test_case_sup), - process_flag(trap_exit, Flag), - R. - --record('REASON', {mod, line, desc}). - -wait_for_evaluator(Pid, Mod, Fun, Config, Errors) -> - wait_for_evaluator(Pid, Mod, Fun, Config, Errors, 0). -wait_for_evaluator(Pid, Mod, Fun, Config, Errors, AccTime) -> - %% p("wait_for_evaluator -> " - %% "~n Pid: ~p" - %% "~n Mod: ~p" - %% "~n Fun: ~p" - %% "~n Config: ~p" - %% "~n Errors: ~p" - %% "~n AccTime: ~p", - %% [Pid, Mod, Fun, Config, Errors, AccTime]), - TestCase = {?MODULE, Mod, Fun}, - Label = lists:concat(["TEST CASE: ", Fun]), - receive - {done, Pid, ok, Time} when Errors =:= [] -> - megaco:report_event(40, Mod, ?MODULE, Label ++ " ok", - [TestCase, Config]), - {ok, {Mod, Fun}, Errors, Time}; - {done, Pid, ok, Time} -> - megaco:report_event(40, Mod, ?MODULE, Label ++ " failed", - [TestCase, Config]), - {failed, {Mod, Fun}, Errors, Time}; - {done, Pid, {ok, _}, Time} when Errors =:= [] -> - megaco:report_event(40, Mod, ?MODULE, Label ++ " ok", - [TestCase, Config]), - {ok, {Mod, Fun}, Errors, Time}; - {done, Pid, {ok, _}, Time} -> - megaco:report_event(40, Mod, ?MODULE, Label ++ " failed", - [TestCase, Config]), - {failed, {Mod, Fun}, Errors, Time}; - {done, Pid, Fail, Time} -> - megaco:report_event(20, Mod, ?MODULE, Label ++ " failed", - [TestCase, Config, {return, Fail}, Errors]), - {failed, {Mod,Fun}, Fail, Time}; - {'EXIT', Pid, {skip, Reason}, Time} -> - megaco:report_event(20, Mod, ?MODULE, Label ++ " skipped", - [TestCase, Config, {skip, Reason}]), - {skip, {Mod, Fun}, Errors, Time}; - {'EXIT', Pid, Reason, Time} -> - megaco:report_event(20, Mod, ?MODULE, Label ++ " crashed", - [TestCase, Config, {'EXIT', Reason}]), - {crashed, {Mod, Fun}, [{'EXIT', Reason} | Errors], Time}; - {fail, Pid, Reason, Time} -> - wait_for_evaluator(Pid, Mod, Fun, Config, - Errors ++ [Reason], AccTime + Time) - end. - -do_eval(ReplyTo, Mod, Fun, Config) -> - %% p("do_eval -> " - %% "~n ReplyTo: ~p" - %% "~n Mod: ~p" - %% "~n Fun: ~p" - %% "~n Config: ~p", [ReplyTo, Mod, Fun, Config]), - display_system_info("before", Mod, Fun), - T1 = os:timestamp(), - try Mod:Fun(Config) of - Res -> - %% p("do_eval -> done" - %% "~n Res: ~p", [Res]), - T2 = os:timestamp(), - Time = timer:now_diff(T2, T1), - display_tc_time(Time), - display_system_info("after", Mod, Fun), - ReplyTo ! {done, self(), Res, Time} - catch - error:undef -> - %% p("do_eval -> error - undef", []), - ReplyTo ! {'EXIT', self(), undef, 0}; - exit:{skip, Reason} -> - %% p("do_eval -> exit - skipped" - %% "~n Reason: ~p", [Reason]), - T2 = os:timestamp(), - Time = timer:now_diff(T2, T1), - display_tc_time(Time), - display_system_info("after (skipped)", Mod, Fun), - ReplyTo ! {'EXIT', self(), {skip, Reason}, Time}; - exit:{suite_failed, Reason} -> - %% p("do_eval -> exit - suite-failed" - %% "~n Reason: ~p", [Reason]), - T2 = os:timestamp(), - Time = timer:now_diff(T2, T1), - display_tc_time(Time), - display_system_info("after (failed)", Mod, Fun), - ReplyTo ! {done, self(), Reason, Time} - - end, - unlink(ReplyTo), - exit(shutdown). - - -display_tc_time(Time) -> - io:format("~n" - "~n*********************************************" - "~n" - "~nTest case completion time: ~.3f sec (~w)" - "~n", [(Time / 1000000), Time]), - ok. - display_system_info(WhenStr) -> display_system_info(WhenStr, undefined, undefined). @@ -605,141 +311,7 @@ display_system_info(WhenStr, ModFuncStr) -> ProcMemBin, ProcMemTot]), ok. -display_result(Res, Alloc1, Mem1, Alloc2, Mem2) -> - io:format("~nAllocator info: ~n", []), - display_alloc(Alloc1, Alloc2), - io:format("~nMemory info: ~n", []), - display_memory(Mem1, Mem2), - display_result(Res). - -display_alloc([], []) -> - io:format("-~n", []), - ok; -display_alloc(A1, A2) -> - do_display_alloc(A1, A2). - -do_display_alloc([], _) -> - ok; -do_display_alloc([{Alloc, Mem1}|AllocInfo1], AllocInfo2) -> - Mem2 = - case lists:keysearch(Alloc, 1, AllocInfo2) of - {value, {_, Val}} -> - Val; - false -> - undefined - end, - io:format("~15w: ~10w -> ~w~n", [Alloc, Mem1, Mem2]), - do_display_alloc(AllocInfo1, AllocInfo2). - -display_memory([], []) -> - io:format("-~n", []), - ok; -display_memory(Mem1, Mem2) -> - do_display_memory(Mem1, Mem2). - -do_display_memory([], _) -> - ok; -do_display_memory([{Key, Mem1}|MemInfo1], MemInfo2) -> - Mem2 = - case lists:keysearch(Key, 1, MemInfo2) of - {value, {_, Val}} -> - Val; - false -> - undefined - end, - io:format("~15w: ~10w -> ~w~n", [Key, Mem1, Mem2]), - do_display_memory(MemInfo1, MemInfo2). - -display_result([]) -> - io:format("OK~n", []); -display_result(Res) when is_list(Res) -> - Ok = [{MF, Time} || {ok, MF, _, Time} <- Res], - Nyi = [MF || {nyi, MF, _, _Time} <- Res], - SkippedGrps = [{{M,G}, Reason} || - {skip, {M, {group, G}}, Reason, _Time} <- Res], - SkippedCases = [{MF, Reason} || - {skip, {_M, F} = MF, Reason, _Time} <- Res, - is_atom(F)], - FailedGrps = [{{M,G}, Reason} || - {failed, {M, {group, G}}, Reason, _Time} <- Res], - FailedCases = [{MF, Reason} || - {failed, {_M, F} = MF, Reason, _Time} <- Res, - is_atom(F)], - Crashed = [{MF, Reason} || {crashed, MF, Reason, _Time} <- Res], - display_summery(Ok, Nyi, - SkippedGrps, SkippedCases, - FailedGrps, FailedCases, - Crashed), - display_ok(Ok), - display_skipped("groups", SkippedGrps), - display_skipped("test cases", SkippedCases), - display_failed("groups", FailedGrps), - display_failed("test cases", FailedCases), - display_crashed(Crashed). - -display_summery(Ok, Nyi, - SkippedGrps, SkippedCases, - FailedGrps, FailedCases, - Crashed) -> - io:format("~nTest case summery:~n", []), - display_summery(Ok, "test case", "successfull"), - display_summery(Nyi, "test case", "not yet implemented"), - display_summery(SkippedGrps, "group", "skipped"), - display_summery(SkippedCases, "test case", "skipped"), - display_summery(FailedGrps, "group", "failed"), - display_summery(FailedCases, "test case", "failed"), - display_summery(Crashed, "test case", "crashed"), - io:format("~n", []). - - -display_summery(Res, Kind, Info) -> - Len = length(Res), - if - Len =:= 1 -> - display_summery(Len, Kind ++ " " ++ Info); - true -> - display_summery(Len, Kind ++ "s " ++ Info) - end. - -display_summery(Len, Info) -> - io:format(" ~w ~s~n", [Len, Info]). - -display_ok([]) -> - ok; -display_ok(Ok) -> - io:format("Ok test cases:~n", []), - F = fun({{M, F}, Time}) -> - io:format(" ~w : ~w => ~.2f sec~n", [M, F, Time / 1000000]) - end, - lists:foreach(F, Ok), - io:format("~n", []). - -display_skipped(_, []) -> - ok; -display_skipped(Pre, Skipped) -> - io:format("Skipped ~s:~n", [Pre]), - F = fun({X, Reason}) -> io:format(" ~p => ~p~n", [X, Reason]) end, - lists:foreach(F, Skipped), - io:format("~n", []). - - -display_failed(_, []) -> - ok; -display_failed(Pre, Failed) -> - io:format("Failed ~s:~n", [Pre]), - F = fun({X, Reason}) -> io:format(" ~p => ~p~n", [X, Reason]) end, - lists:foreach(F, Failed), - io:format("~n", []). - -display_crashed([]) -> - ok; -display_crashed(Crashed) -> - io:format("Crashed test cases:~n", []), - F = fun({MF, Reason}) -> io:format(" ~p => ~p~n", [MF, Reason]) end, - lists:foreach(F, Crashed), - io:format("~n", []). - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Verify that the actual result of a test case matches the exected one @@ -787,6 +359,7 @@ fatal_skip(Actual, File, Line) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Flush the message queue and return its messages + flush() -> receive Msg -> @@ -798,18 +371,9 @@ flush() -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Check if process is alive and kicking + still_alive(Pid) -> - case catch erlang:is_process_alive(Pid) of % New BIF in Erlang/OTP R5 - true -> - true; - false -> - false; - {'EXIT', _} -> % Pre R5 backward compatibility - case process_info(Pid, message_queue_len) of - undefined -> false; - _ -> true - end - end. + erlang:is_process_alive(Pid). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -871,6 +435,86 @@ pprint(F, A) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Test server callbacks + +init_per_suite(Config) -> + + io:format("Host info:" + "~n OS Type: ~p" + "~n OS Version: ~s" + "~n", + [os:type(), + case os:version() of + {Major, Minor, Release} -> + ?F("~w.~w.~w", [Major, Minor, Release]); + Str when is_list(Str) -> + Str + end]), + + %% We have some crap machines that causes random test case failures + %% for no obvious reason. So, attempt to identify those without actually + %% checking for the host name... + %% We have two "machines" we are checking for. Both are old installations + %% running on really slow VMs (the host machines are old and tired). + LinuxVersionVerify = + fun(V) when (V > {3,6,11}) -> + false; % OK - No skip + (V) when (V =:= {3,6,11}) -> + case string:trim(os:cmd("cat /etc/issue")) of + "Fedora release 16 " ++ _ -> % Stone age Fedora => Skip + true; + _ -> + false + end; + (V) when (V > {2,6,24}) -> + false; % OK - No skip + (_) -> + %% We are specifically checking for + %% a *really* old gento... + case string:find(string:strip(os:cmd("uname -a")), "gentoo") of + nomatch -> + false; + _ -> % Stone age gentoo => Skip + true + end + end, + DarwinVersionVerify = + fun(V) when (V > {9, 8, 0}) -> + %% This version is OK: No Skip + false; + (_V) -> + %% This version is *not* ok: Skip + true + end, + COND = [{unix, [{linux, LinuxVersionVerify}, {darwin, DarwinVersionVerify}]}], + case os_based_skip(COND) of + true -> + {skip, "Unstable host and/or os (or combo thererof)"}; + false -> + maybe_start_global_sys_monitor(Config), + Config + end. + +%% We start the global system monitor unless explicitly disabled +maybe_start_global_sys_monitor(Config) -> + case lists:keysearch(sysmon, 1, Config) of + {value, {sysmon, false}} -> + ok; + _ -> + megaco_test_global_sys_monitor:start() + end. + +end_per_suite(Config) when is_list(Config) -> + + case lists:keysearch(sysmon, 1, Config) of + {value, {sysmon, false}} -> + ok; + _ -> + megaco_test_global_sys_monitor:stop() + end, + + Config. + + init_per_testcase(_Case, Config) -> Pid = group_leader(), Name = megaco_global_logger, @@ -918,54 +562,21 @@ set_kill_timer(Config) -> ConfigTime when is_integer(ConfigTime) -> ConfigTime end, - Dog = - case get(megaco_test_server) of - true -> - spawn_link(?MODULE, watchdog, [self(), Time]); - _ -> - test_server:timetrap(Time) - end, + Dog = test_server:timetrap(Time), [{kill_timer, Dog}|Config] end. reset_kill_timer(Config) -> - DogKiller = - case get(megaco_test_server) of - true -> - fun(P) when is_pid(P) -> P ! stop; - (_) -> ok - end; - _ -> - fun(Ref) -> test_server:timetrap_cancel(Ref) end - end, case lists:keysearch(kill_timer, 1, Config) of {value, {kill_timer, Dog}} -> - DogKiller(Dog), + test_server:timetrap_cancel(Dog), lists:keydelete(kill_timer, 1, Config); _ -> Config end. -watchdog(Pid, Time) -> - _ = os:timestamp(), - receive - stop -> - ok - after Time -> - case (catch process_info(Pid)) of - undefined -> - ok; - Info -> - ?LOG("<ERROR> Watchdog in test case timed out " - "for ~p after ~p min" - "~n~p" - "~n", - [Pid, Time div (1000*60), Info]), - exit(Pid, kill) - end - end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1005,12 +616,6 @@ lookup_config(Key,Config) -> [] end. -default_config() -> - [{nodes, default_nodes()}, {ts, megaco}]. - -default_nodes() -> - mk_nodes(3, []). - mk_nodes(N) when (N > 0) -> mk_nodes(N, []). @@ -1030,15 +635,23 @@ mk_node(N, Name, Host) -> node_to_name_and_host(Node) -> string:tokens(atom_to_list(Node), [$@]). -start_nodes([Node | Nodes], File, Line) -> + +start_nodes(Nodes, File, Line) when is_list(Nodes) -> + lists:foreach(fun(N) -> start_node(N, File, Line) end, Nodes). + +start_node(Node, File, Line) -> case net_adm:ping(Node) of pong -> p("node ~p already running", [Node]), - start_nodes(Nodes, File, Line); + ok; pang -> [Name, Host] = node_to_name_and_host(Node), + Pa = filename:dirname(code:which(?MODULE)), + Args = " -pa " ++ Pa ++ + " -s " ++ atom_to_list(megaco_test_sys_monitor) ++ " start" ++ + " -s global sync", p("try start node ~p", [Node]), - case slave:start_link(Host, Name) of + case slave:start_link(Host, Name, Args) of {ok, NewNode} when NewNode =:= Node -> p("node ~p started - now set path, cwd and sync", [Node]), Path = code:get_path(), @@ -1047,14 +660,13 @@ start_nodes([Node | Nodes], File, Line) -> ok = rpc:call(Node, file, set_cwd, [Cwd]), true = rpc:call(Node, code, set_path, [Path]), {_, []} = rpc:multicall(global, sync, []), - start_nodes(Nodes, File, Line); + ok; Other -> p("failed starting node ~p: ~p", [Node, Other]), fatal_skip({cannot_start_node, Node, Other}, File, Line) end - end; -start_nodes([], _File, _Line) -> - ok. + end. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/lib/megaco/test/megaco_test_lib.hrl b/lib/megaco/test/megaco_test_lib.hrl index 546ecaab9a..4c49f89a8e 100644 --- a/lib/megaco/test/megaco_test_lib.hrl +++ b/lib/megaco/test/megaco_test_lib.hrl @@ -23,23 +23,29 @@ %% Purpose: Define common macros for testing %%---------------------------------------------------------------------- +-ifndef(APPLICATION). +-define(APPLICATION, megaco). +-endif. + +-define(LIB, megaco_test_lib). + -define(APPLY(Proxy, Fun), Proxy ! {apply, Fun}). -define(LOG(Format, Args), - megaco_test_lib:log(Format, Args, ?MODULE, ?LINE)). + ?LIB:log(Format, Args, ?MODULE, ?LINE)). -define(ERROR(Reason), - megaco_test_lib:error(Reason, ?MODULE, ?LINE)). + ?LIB:error(Reason, ?MODULE, ?LINE)). -define(OS_BASED_SKIP(Skippable), - megaco_test_lib:os_based_skip(Skippable)). + ?LIB:os_based_skip(Skippable)). -define(NON_PC_TC_MAYBE_SKIP(Config, Condition), - megaco_test_lib:non_pc_tc_maybe_skip(Config, Condition, ?MODULE, ?LINE)). + ?LIB:non_pc_tc_maybe_skip(Config, Condition, ?MODULE, ?LINE)). -define(SKIP(Reason), - megaco_test_lib:skip(Reason, ?MODULE, ?LINE)). + ?LIB:skip(Reason, ?MODULE, ?LINE)). -define(VERIFYL(Expected, Expr), fun(A,B) when list(A), list(B) -> @@ -69,22 +75,34 @@ end()). -define(RECEIVE(Expected), - ?VERIFY(Expected, megaco_test_lib:flush())). + ?VERIFY(Expected, ?LIB:flush())). -define(MULTI_RECEIVE(Expected), - ?VERIFY(lists:sort(Expected), lists:sort(megaco_test_lib:flush()))). + ?VERIFY(lists:sort(Expected), lists:sort(?LIB:flush()))). -define(ACQUIRE_NODES(N, Config), - megaco_test_lib:prepare_test_case([init, {stop_app, megaco}], - N, Config, ?FILE, ?LINE)). + ?LIB:prepare_test_case([init, {stop_app, megaco}], + N, Config, ?FILE, ?LINE)). +-define(START_NODE(Node), ?LIB:start_node(Node, ?FILE, ?LINE)). +-define(START_NODES(Nodes), ?LIB:start_nodes(Nodes, ?FILE, ?LINE)). --define(SLEEP(MSEC), megaco_test_lib:sleep(MSEC)). --define(HOURS(T), megaco_test_lib:hours(T)). --define(MINS(T), megaco_test_lib:minutes(T)). +-define(SLEEP(MSEC), ?LIB:sleep(MSEC)). +-define(HOURS(T), ?LIB:hours(T)). +-define(MINS(T), ?LIB:minutes(T)). -define(MINUTES(T), ?MINS(T)). --define(SECS(T), megaco_test_lib:seconds(T)). +-define(SECS(T), ?LIB:seconds(T)). -define(SECONDS(T), ?SECS(T)). -define(FTS(), megaco:format_timestamp(erlang:timestamp())). -define(FTS(TS), megaco:format_timestamp(TS)). -define(F(F,A), lists:flatten(io_lib:format(F, A))). + +-define(ANNOUNCE_SUITE_INIT(), + io:format(user, "~n*** ~s *** suite ~w init~n~n", [?FTS(), ?MODULE])). +-define(ANNOUNCE_GROUP_INIT(GR), + io:format(user, "~n*** ~s *** group ~w:~w init~n~n", + [?FTS(), ?MODULE, GR])). +-define(ANNOUNCE_CASE_INIT(C), + io:format(user, "~n*** ~s *** case ~w:~w init~n~n", + [?FTS(), ?MODULE, C])). + diff --git a/lib/megaco/test/megaco_test_sys_monitor.erl b/lib/megaco/test/megaco_test_sys_monitor.erl new file mode 100644 index 0000000000..da9e89a501 --- /dev/null +++ b/lib/megaco/test/megaco_test_sys_monitor.erl @@ -0,0 +1,94 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2019-2019. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +-module(megaco_test_sys_monitor). + +-export([start/0, stop/0, + init/1]). + +-define(NAME, ?MODULE). +-define(GSM, megaco_test_global_sys_monitor). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +start() -> + Parent = self(), + proc_lib:start(?MODULE, init, [Parent]). + +stop() -> + case whereis(?NAME) of + Pid when is_pid(Pid) -> + Pid ! {?MODULE, self(), stop}, + receive + {?MODULE, Pid, stop} -> + ok + end; + _ -> + ok + end. + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +init(Parent) -> + process_flag(priority, high), + try register(?NAME, self()) of + true -> + global:sync(), + MonSettings = [ + busy_port, + busy_dist_port, + {long_gc, 1000}, + {long_schedule, 1000}, + {large_heap, 8*1024*1024} % 8 MB + ], + erlang:system_monitor(self(), MonSettings), + ?GSM:log({erlang:timestamp(), starting}), + proc_lib:init_ack(Parent, {ok, self()}), + loop(#{parent => Parent}) + catch + _:_:_ -> + ?GSM:log({erlang:timestamp(), already_started}), + proc_lib:init_ack(Parent, {error, already_started}), + exit(normal) + end. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +loop(State) -> + receive + {monitor, Pid, Tag, Info} -> + ?GSM:log({Pid, erlang:timestamp(), Tag, Info}), + loop(State); + + {?MODULE, From, stop} -> + ?GSM:log({erlang:timestamp(), stopping}), + From ! {?MODULE, self(), stop}, + exit(normal); + + _ -> + loop(State) + end. + + + diff --git a/lib/megaco/test/megaco_timer_test.erl b/lib/megaco/test/megaco_timer_SUITE.erl index 87aa51b8de..c150dad897 100644 --- a/lib/megaco/test/megaco_timer_test.erl +++ b/lib/megaco/test/megaco_timer_SUITE.erl @@ -22,19 +22,21 @@ %%---------------------------------------------------------------------- %% Purpose: Verify the application specifics of the Megaco application %%---------------------------------------------------------------------- --module(megaco_timer_test). +-module(megaco_timer_SUITE). -compile({no_auto_import,[error/1]}). -export([ - t/0, t/1, - init_per_testcase/2, end_per_testcase/2, - all/0,groups/0,init_per_group/2,end_per_group/2, + suite/0, all/0, groups/0, + init_per_suite/1, end_per_suite/1, + init_per_group/2, end_per_group/2, + init_per_testcase/2, end_per_testcase/2, + simple_init/1, simple_usage/1, integer_timer_start_and_expire/1, - integer_timer_start_and_stop/1%% , -%% incr_timer/1 + integer_timer_start_and_stop/1 + ]). -export([ @@ -49,13 +51,98 @@ -define(TEST_VERBOSITY, info). % silence | info | debug -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%====================================================================== +%% Common Test interface functions +%%====================================================================== + +suite() -> + [{ct_hooks, [ts_install_cth]}]. + +all() -> + [{group, simple}, {group, integer_timer}]. + +groups() -> + [ + {simple, [], simple_cases()}, + {integer_timer, [], integer_timer_cases()} + ]. + +simple_cases() -> + [ + simple_init, + simple_usage + ]. + +integer_timer_cases() -> + [ + integer_timer_start_and_expire, + integer_timer_start_and_stop + ]. -t() -> megaco_test_lib:t(?MODULE). -t(Case) -> megaco_test_lib:t({?MODULE, Case}). -%% Test server callbacks +%% +%% ----- +%% + +init_per_suite(suite) -> + []; +init_per_suite(doc) -> + []; +init_per_suite(Config0) when is_list(Config0) -> + + ?ANNOUNCE_SUITE_INIT(), + + p("init_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + case ?LIB:init_per_suite([{sysmon, false} | Config0]) of + {skip, _} = SKIP -> + SKIP; + + Config1 when is_list(Config1) -> + + p("init_per_suite -> end when" + "~n Config: ~p" + "~n Nodes: ~p", [Config1, erlang:nodes()]), + + Config1 + end. + +end_per_suite(suite) -> []; +end_per_suite(doc) -> []; +end_per_suite(Config0) when is_list(Config0) -> + + p("end_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + Config1 = ?LIB:end_per_suite(Config0), + + p("end_per_suite -> end when" + "~n Nodes: ~p", [erlang:nodes()]), + + Config1. + + +%% +%% ----- +%% + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + + +%% +%% ----- +%% + %% init_per_testcase(multi_user_extreme_load = Case, Config) -> %% C = lists:keydelete(tc_timeout, 1, Config), %% do_init_per_testcase(Case, [{tc_timeout, min(20)}|C]); @@ -64,15 +151,21 @@ init_per_testcase(Case, Config) -> do_init_per_testcase(Case, Config) -> process_flag(trap_exit, true), + + p("init_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config, erlang:nodes()]), + {ok, _Pid} = megaco_monitor:start_link(), megaco_test_lib:init_per_testcase(Case, [{monitor_running, true}|Config]). - + end_per_testcase(Case, Config) -> - io:format("end_per_testcase -> entry with" - "~n Case: ~p" - "~n Config: ~p" - "~n", [Case, Config]), process_flag(trap_exit, false), + + p("end_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config, erlang:nodes()]), + case lists:keydelete(monitor_running, 1, Config) of Config -> megaco_test_lib:end_per_testcase(Case, Config); @@ -82,32 +175,6 @@ end_per_testcase(Case, Config) -> end. -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -all() -> - [{group, simple}, {group, integer_timer}]. - -groups() -> - [{simple, [], - [simple_init, simple_usage]}, -%, incr_timer - {integer_timer, [], - [integer_timer_start_and_expire, - integer_timer_start_and_stop]}]. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -%% incr_timer(suite) -> -%% Cases = -%% [ -%% ], -%% Cases. - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -151,7 +218,7 @@ simple_init(Config) when is_list(Config) -> d("unexpected result: " "~n Expected: ~p" "~n Actual: ~p", [A, B]), - error({unexpected_result, A, B}) + unexpected_result(A, B) end, VerifyTMR = fun(false, Tmr) -> @@ -243,7 +310,7 @@ simple_usage(Config) when is_list(Config) -> fun(A, A) -> ok; (A, B) -> - error({unexpected_result, A, B}) + unexpected_result(A, B) end, @@ -334,7 +401,7 @@ integer_timer_start_and_expire(Config) when is_list(Config) -> ok after Timeout + 500 -> tmr_stop(Ref), - error(no_timeout) + no_timeout() end, i("done", []), @@ -360,7 +427,7 @@ integer_timer_start_and_stop(Config) when is_list(Config) -> receive {timeout, Timeout} -> i("unexpected premature timer expire"), - error(bad_timeout) + bad_timeout() after Timeout - 100 -> i("try stop timer"), case tmr_stop(Ref) of @@ -377,7 +444,7 @@ integer_timer_start_and_stop(Config) when is_list(Config) -> %% Make sure it does not reach us after we attempted to stop it. receive {timeout, Timeout} -> - error(unexpected_timeout) + unexpected_timeout() after Timeout -> ok end, @@ -390,7 +457,8 @@ integer_timer_start_and_stop(Config) when is_list(Config) -> tmr_start(Timeout) -> Pid = self(), - megaco_monitor:apply_after(?MODULE, timeout, [Pid, Timeout, get(tc)], Timeout). + megaco_monitor:apply_after(?MODULE, timeout, + [Pid, Timeout, get(tc)], Timeout). tmr_stop(Ref) -> megaco_monitor:cancel_apply_after(Ref). @@ -407,21 +475,29 @@ timeout(Pid, Timeout, Tc) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% tim() -> -%% {A,B,C} = erlang:now(), -%% A*1000000000+B*1000+(C div 1000). +unexpected_result(A, B) -> + error({unexpected_result, A, B}). -%% min(M) -> timer:minutes(M). +no_timeout() -> + error(no_timeout). -%% sleep(X) -> receive after X -> ok end. +bad_timeout() -> + error(bad_timeout). + +unexpected_timeout() -> + error(unexpected_timeout). -error(Reason) -> throw({error, Reason}). -%% error_msg(F,A) -> error_logger:error_msg(F ++ "~n",A). +error(Reason) -> throw({error, Reason}). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +p(F, A) -> + io:format("*** [~s] ~p ***" + "~n " ++ F ++ "~n", + [?FTS(), self() | A]). + i(F) -> i(F, []). diff --git a/lib/megaco/test/megaco_trans_test.erl b/lib/megaco/test/megaco_trans_SUITE.erl index f88b163c0a..c1c333a305 100644 --- a/lib/megaco/test/megaco_trans_test.erl +++ b/lib/megaco/test/megaco_trans_SUITE.erl @@ -22,15 +22,14 @@ %%---------------------------------------------------------------------- %% Purpose: Verify that the transaction sender works with acks. %% -%% Test: ts:run(megaco, megaco_trans_test, [batch]). +%% Test: ts:run(megaco, megaco_trans_SUITE, [batch]). %% %%---------------------------------------------------------------------- --module(megaco_trans_test). +-module(megaco_trans_SUITE). -%% -compile(export_all). -export([ - all/0, - groups/0, + suite/0, all/0, groups/0, + init_per_suite/1, end_per_suite/1, init_per_group/2, end_per_group/2, init_per_testcase/2, end_per_testcase/2, @@ -63,14 +62,13 @@ otp_7192_1/1, otp_7192_2/1, - otp_7192_3/1, - - t/0, t/1 + otp_7192_3/1 + ]). --include("megaco_test_lib.hrl"). -include_lib("megaco/include/megaco.hrl"). -include_lib("megaco/include/megaco_message_v1.hrl"). +-include("megaco_test_lib.hrl"). -define(VERSION, 1). -define(TEST_VERBOSITY, debug). @@ -120,33 +118,23 @@ -define(MG_ACK_INFO(Pid,To), ?MG:ack_info(Pid,To)). -define(MG_REP_INFO(Pid,To), ?MG:rep_info(Pid,To)). -t() -> megaco_test_lib:t(?MODULE). -t(Case) -> megaco_test_lib:t({?MODULE, Case}). +%%====================================================================== +%% Common Test interface functions +%%====================================================================== -%% Test server callbacks -init_per_testcase(multi_ack_maxcount = Case, Config) -> - process_flag(trap_exit, true), - C = lists:keydelete(tc_timeout, 1, Config), - megaco_test_lib:init_per_testcase(Case, [{tc_timeout,timer:minutes(10)}|C]); -init_per_testcase(Case, Config) -> - process_flag(trap_exit, true), - megaco_test_lib:init_per_testcase(Case, Config). - -end_per_testcase(Case, Config) -> - process_flag(trap_exit, false), - megaco_test_lib:end_per_testcase(Case, Config). - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +suite() -> + [{ct_hooks, [ts_install_cth]}]. all() -> - [{group, ack}, + [ + {group, ack}, {group, trans_req}, {group, trans_req_and_ack}, {group, pending}, {group, reply}, - {group, tickets}]. + {group, tickets} + ]. groups() -> [ @@ -213,13 +201,106 @@ otp_7192_cases() -> otp_7192_3 ]. -init_per_group(_GroupName, Config) -> + + + +%% +%% ----- +%% + +init_per_suite(suite) -> + []; +init_per_suite(doc) -> + []; +init_per_suite(Config0) when is_list(Config0) -> + + ?ANNOUNCE_SUITE_INIT(), + + p("init_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + case ?LIB:init_per_suite(Config0) of + {skip, _} = SKIP -> + SKIP; + + Config1 when is_list(Config1) -> + + %% We need a (local) monitor on this node also + megaco_test_sys_monitor:start(), + + p("init_per_suite -> end when" + "~n Config: ~p" + "~n Nodes: ~p", [Config1, erlang:nodes()]), + + Config1 + end. + +end_per_suite(suite) -> []; +end_per_suite(doc) -> []; +end_per_suite(Config0) when is_list(Config0) -> + + p("end_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + megaco_test_sys_monitor:stop(), + Config1 = ?LIB:end_per_suite(Config0), + + p("end_per_suite -> end when" + "~n Nodes: ~p", [erlang:nodes()]), + + Config1. + + +%% +%% ----- +%% + +init_per_group(Group, Config) -> + ?ANNOUNCE_GROUP_INIT(Group), Config. end_per_group(_GroupName, Config) -> Config. + +%% +%% ----- +%% + +init_per_testcase(multi_ack_maxcount = Case, Config) -> + C = lists:keydelete(tc_timeout, 1, Config), + init_per_testcase2(Case, [{tc_timeout,timer:minutes(10)}|C]); +init_per_testcase(Case, Config) -> + process_flag(trap_exit, true), + init_per_testcase2(Case, Config). + +init_per_testcase2(Case, Config) -> + process_flag(trap_exit, true), + + p("init_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config, erlang:nodes()]), + + megaco_test_global_sys_monitor:reset_events(), + megaco_test_lib:init_per_testcase(Case, Config). + +end_per_testcase(Case, Config) -> + process_flag(trap_exit, false), + + p("end_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config, erlang:nodes()]), + + p("system events during test: " + "~n ~p", [megaco_test_global_sys_monitor:events()]), + + megaco_test_lib:end_per_testcase(Case, Config). + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% single_ack(suite) -> @@ -6950,10 +7031,10 @@ multi_trans_req_and_ack_and_reply(Config) when is_list(Config) -> {?MODULE, mtraaar_mgc_verify_handle_connect, []}). -define(mtraaar_mgc_verify_service_change_req_fun(Mid), {?MODULE, mtraaar_mgc_verify_service_change_req, [Mid]}). --define(mtraaar_mgc_verify_notify_req_fun(), - {?MODULE, mtraaar_mgc_verify_notify_request, []}). --define(mtraaar_mgc_verify_notify_reply_fun(), - {?MODULE, mtraaar_mgc_verify_notify_reply, []}). +-define(mtraaar_mgc_verify_notify_req_fun(N), + {?MODULE, mtraaar_mgc_verify_notify_request, [N]}). +-define(mtraaar_mgc_verify_notify_reply_fun(N), + {?MODULE, mtraaar_mgc_verify_notify_reply, [N]}). -define(mtraaar_mgc_verify_ack_fun(), {?MODULE, mtraaar_mgc_verify_ack, []}). -define(mtraaar_mgc_verify_handle_disconnect_fun(), @@ -6963,10 +7044,14 @@ multi_trans_req_and_ack_and_reply(Config) when is_list(Config) -> fun mtraaar_mgc_verify_handle_connect/1). -define(mtraaar_mgc_verify_service_change_req_fun(Mid), mtraaar_mgc_verify_service_change_req_fun(Mid)). --define(mtraaar_mgc_verify_notify_req_fun(), - mtraaar_mgc_verify_notify_request_fun()). --define(mtraaar_mgc_verify_notify_reply_fun(), - fun mtraaar_mgc_verify_notify_reply/1). +-define(mtraaar_mgc_verify_notify_req_fun(N), + fun(Request) -> + mtraaar_mgc_verify_notify_request(N, Request) + end). +-define(mtraaar_mgc_verify_notify_reply_fun(N), + fun(Reply) -> + mtraaar_mgc_verify_notify_reply(N, Reply) + end). -define(mtraaar_mgc_verify_ack_fun(), fun mtraaar_mgc_verify_ack/1). -define(mtraaar_mgc_verify_handle_disconnect_fun(), @@ -6983,13 +7068,20 @@ mtraaar_mgc_event_sequence(text, tcp) -> {transport_module, megaco_tcp} ], Tid = #megaco_term_id{id = ["00000000","00000000","01101101"]}, - NR = fun(Cid, Rid) -> - [mtraaar_mgc_notify_request_ar(Rid, Tid, Cid)] - end, + SNR = fun(Cid, Rid) -> + [mtraaar_mgc_notify_request_ar(Rid, Tid, Cid)] + end, ConnectVerify = ?mtraaar_mgc_verify_handle_connect_fun(), ServiceChangeReqVerify = ?mtraaar_mgc_verify_service_change_req_fun(Mid), - NotifyReqVerify = ?mtraaar_mgc_verify_notify_req_fun(), - NotifyReplyVerify = ?mtraaar_mgc_verify_notify_reply_fun(), + %% NotifyReqVerify = ?mtraaar_mgc_verify_notify_req_fun(), + NReqV = + fun(N) -> + ?mtraaar_mgc_verify_notify_req_fun(N) + end, + %% NotifyReplyVerify = ?mtraaar_mgc_verify_notify_reply_fun(), + NRepV = fun(N) -> + ?mtraaar_mgc_verify_notify_reply_fun(N) + end, AckVerify = ?mtraaar_mgc_verify_ack_fun(), DiscoVerify = ?mtraaar_mgc_verify_handle_disconnect_fun(), EvSeq = [ @@ -7004,18 +7096,19 @@ mtraaar_mgc_event_sequence(text, tcp) -> %% ANNOUNCE READY {trigger, fun() -> CTRL ! announce_mgc end}, - {megaco_callback, handle_connect, ConnectVerify}, - {megaco_callback, handle_trans_request, ServiceChangeReqVerify}, - {megaco_callback, handle_trans_request, NotifyReqVerify}, - {megaco_callback, handle_trans_request, NotifyReqVerify}, - {megaco_callback, handle_trans_request, NotifyReqVerify}, - {megaco_update_conn_info, request_timer, 1000}, - {megaco_cast, NR(1,1), []}, + {megaco_callback, handle_connect, ConnectVerify}, + {megaco_callback, handle_trans_request, ServiceChangeReqVerify}, + {megaco_callback, handle_trans_request, NReqV(1)}, + {megaco_callback, handle_trans_request, NReqV(2)}, + {megaco_callback, handle_trans_request, NReqV(3)}, + {megaco_update_conn_info, request_timer, 1000}, + {sleep, 1000}, + {megaco_cast, SNR(1,1), []}, {megaco_callback, [{handle_trans_ack, 3, AckVerify}, - {handle_trans_request, 3, NotifyReqVerify}, - {handle_trans_reply, 1, NotifyReplyVerify}]}, - {megaco_callback, handle_disconnect, DiscoVerify}, + {handle_trans_request, 3, NReqV(4)}, + {handle_trans_reply, 1, NRepV(1)}]}, + {megaco_callback, handle_disconnect, DiscoVerify}, {sleep, 1000}, megaco_stop_user, megaco_stop @@ -7098,15 +7191,11 @@ mtraaar_mgc_verify_service_change_req(Else, _Mid) -> ErrReply = {discard_ack, ED}, {error, Else, ErrReply}. -mtraaar_mgc_verify_notify_request_fun() -> - fun(Ev) -> - mtraaar_mgc_verify_notify_request(Ev) - end. - mtraaar_mgc_verify_notify_request( + N, {handle_trans_request, _, ?VERSION, [AR]}) -> - io:format("mtraaar_mgc_verify_notify_request -> ok" - "~n AR: ~p~n", [AR]), + io:format("mtraaar_mgc_verify_notify_request -> [~w] ok" + "~n AR: ~p~n", [N, AR]), case AR of #'ActionRequest'{contextId = 1 = Cid, commandRequests = [CR]} -> @@ -7137,26 +7226,28 @@ mtraaar_mgc_verify_notify_request( ErrReply = {discard_ack, ED}, {error, AR, ErrReply} end; -mtraaar_mgc_verify_notify_request(Else) -> - io:format("mtraaar_mgc_verify_notify_request -> unknown" - "~n Else: ~p~n", [Else]), +mtraaar_mgc_verify_notify_request(N, Else) -> + io:format("mtraaar_mgc_verify_notify_request -> [~w] unknown" + "~n Else: ~p~n", [N, Else]), ED = mtraaar_err_desc(Else), ErrReply = {discard_ack, ED}, {error, Else, ErrReply}. -mtraaar_mgc_verify_notify_reply({handle_trans_reply, _CH, ?VERSION, +mtraaar_mgc_verify_notify_reply(N, + {handle_trans_reply, _CH, ?VERSION, {ok, [AR]}, _}) -> - io:format("mtraaar_mgc_verify_notify_reply -> ok" - "~n AR: ~p~n", [AR]), + io:format("mtraaar_mgc_verify_notify_reply -> [~w] ok" + "~n AR: ~p~n", [N, AR]), {ok, AR, ok}; -mtraaar_mgc_verify_notify_reply({handle_trans_reply, CH, ?VERSION, +mtraaar_mgc_verify_notify_reply(N, + {handle_trans_reply, CH, ?VERSION, UnknownResult, _}) -> - io:format("mtraaar_mgc_verify_notify_reply -> unknown result" - "~n UnknownResult: ~p~n", [UnknownResult]), + io:format("mtraaar_mgc_verify_notify_reply -> [~w] unknown result" + "~n UnknownResult: ~p~n", [N, UnknownResult]), {error, {unknown_reply_result, UnknownResult, CH}, ok}; -mtraaar_mgc_verify_notify_reply(Else) -> - io:format("mtraaar_mgc_verify_notify_reply -> unknown" - "~n Else: ~p~n", [Else]), +mtraaar_mgc_verify_notify_reply(N, Else) -> + io:format("mtraaar_mgc_verify_notify_reply -> [~w] unknown" + "~n Else: ~p~n", [N, Else]), {error, {unknown_reply, Else}, ok}. mtraaar_mgc_verify_ack({handle_trans_ack, CH, ?VERSION, ok, kalle}) -> @@ -7237,10 +7328,15 @@ mtraaar_mgc_notify_reply_ar(Cid, TermId) -> fun mtraaar_mg_verify_handle_connect/1). -define(mtraaar_mg_verify_service_change_reply_fun(), fun mtraaar_mg_verify_service_change_reply/1). --define(mtraaar_mg_verify_notify_req_fun(), - mtraaar_mgc_verify_notify_request_fun()). --define(mtraaar_mg_verify_notify_reply_fun(), - fun mtraaar_mg_verify_notify_reply/1). +-define(mtraaar_mg_verify_notify_req_fun(N), + %% We reuse the mgc code... + fun(Request) -> + mtraaar_mgc_verify_notify_request(N, Request) + end). +-define(mtraaar_mg_verify_notify_reply_fun(N), + fun(Reply) -> + mtraaar_mg_verify_notify_reply(N, Reply) + end). -endif. mtraaar_mg_event_sequence(text, tcp) -> @@ -7253,13 +7349,21 @@ mtraaar_mg_event_sequence(text, tcp) -> ], ServiceChangeReq = [mtraaar_mg_service_change_request_ar(Mid, 1)], Tid = #megaco_term_id{id = ["00000000","00000000","01101101"]}, - NR = fun(Cid, Rid) -> - [mtraaar_mg_notify_request_ar(Rid, Tid, Cid)] - end, + SNR = fun(Cid, Rid) -> + [mtraaar_mg_notify_request_ar(Rid, Tid, Cid)] + end, ConnectVerify = ?mtraaar_mg_verify_handle_connect_fun(), ServiceChangeReplyVerify = ?mtraaar_mg_verify_service_change_reply_fun(), - NotifyReqVerify = ?mtraaar_mg_verify_notify_req_fun(), - NotifyReplyVerify = ?mtraaar_mg_verify_notify_reply_fun(), + %% NotifyReqVerify = ?mtraaar_mg_verify_notify_req_fun(), + NReqV = + fun(N) -> + ?mtraaar_mg_verify_notify_req_fun(N) + end, + %% NotifyReplyVerify = ?mtraaar_mg_verify_notify_reply_fun(), + NRepV = + fun(N) -> + ?mtraaar_mg_verify_notify_reply_fun(N) + end, EvSeq = [ {debug, true}, megaco_start, @@ -7286,20 +7390,24 @@ mtraaar_mg_event_sequence(text, tcp) -> {megaco_update_conn_info, trans_ack, true}, {megaco_update_conn_info, trans_req, true}, {megaco_conn_info, all}, - {megaco_cast, NR(1,1), []}, - {megaco_cast, NR(1,2), []}, - {megaco_cast, NR(1,3), []}, - {megaco_callback, handle_trans_reply, NotifyReplyVerify}, - {megaco_callback, handle_trans_reply, NotifyReplyVerify}, - {megaco_callback, handle_trans_reply, NotifyReplyVerify}, + {megaco_cast, SNR(1,1), []}, + {sleep, 100}, + {megaco_cast, SNR(1,2), []}, + {sleep, 100}, + {megaco_cast, SNR(1,3), []}, + {megaco_callback, handle_trans_reply, NRepV(1)}, + {megaco_callback, handle_trans_reply, NRepV(2)}, + {megaco_callback, handle_trans_reply, NRepV(3)}, {megaco_update_conn_info, trans_timer, 120000}, - {megaco_cast, NR(2,1), []}, - {megaco_cast, NR(2,2), []}, - {megaco_cast, NR(2,3), []}, - {megaco_callback, handle_trans_request, NotifyReqVerify}, - {megaco_callback, handle_trans_reply, NotifyReplyVerify}, - {megaco_callback, handle_trans_reply, NotifyReplyVerify}, - {megaco_callback, handle_trans_reply, NotifyReplyVerify}, + {megaco_cast, SNR(2,1), []}, + {sleep, 100}, + {megaco_cast, SNR(2,2), []}, + {sleep, 100}, + {megaco_cast, SNR(2,3), []}, + {megaco_callback, handle_trans_request, NReqV(1)}, + {megaco_callback, handle_trans_reply, NRepV(4)}, + {megaco_callback, handle_trans_reply, NRepV(5)}, + {megaco_callback, handle_trans_reply, NRepV(6)}, {sleep, 3000}, megaco_stop_user, megaco_stop, @@ -7356,47 +7464,34 @@ mtraaar_mg_verify_service_change_reply(Else) -> "~n Else: ~p~n", [Else]), {error, Else, ok}. -%% mtraaar_mg_verify_notify_request_fun() -> -%% fun(Ev) -> -%% mtraaar_mg_verify_notify_request(Ev) -%% end. - -%% mtraaar_mg_verify_notify_request( -%% {handle_trans_request, _, ?VERSION, [AR]}) -> -%% io:format("mtraaar_mg_verify_notify_request -> ok" -%% "~n AR: ~p~n", [AR]), -%% case AR of -%% #'ActionRequest'{contextId = 1 = Cid, -%% commandRequests = [CR]} -> -%% #'CommandRequest'{command = Cmd} = CR, -%% {notifyReq, NR} = Cmd, -%% #'NotifyRequest'{terminationID = [Tid], -%% observedEventsDescriptor = OED, -%% errorDescriptor = asn1_NOVALUE} = NR, -%% #'ObservedEventsDescriptor'{observedEventLst = [OE]} = OED, -%% #'ObservedEvent'{eventName = "al/of"} = OE, -%% Reply = {discard_ack, [mtraaar_mg_notify_reply_ar(Cid, Tid)]}, -%% {ok, AR, Reply}; -%% _ -> -%% ED = mtraaar_err_desc(AR), -%% ErrReply = {discard_ack, ED}, -%% {error, AR, ErrReply} -%% end; -%% mtraaar_mg_verify_notify_request(Else) -> -%% io:format("mtraaar_mg_verify_notify_request -> unknown" -%% "~n Else: ~p~n", [Else]), -%% ED = mtraaar_err_desc(Else), -%% ErrReply = {discard_ack, ED}, -%% {error, Else, ErrReply}. - -mtraaar_mg_verify_notify_reply({handle_trans_reply, _CH, ?VERSION, - {ok, [AR]}, _}) -> - io:format("mtraaar_mg_verify_notify_reply -> ok" - "~n AR: ~p~n", [AR]), +mtraaar_mg_verify_notify_reply(N, + {handle_trans_reply, + _CH, + ?VERSION, + {ok, [AR]}, + _}) -> + io:format("mtraaar_mg_verify_notify_reply -> [~w] ok" + "~n AR: ~p~n", [N, AR]), {ok, AR, ok}; -mtraaar_mg_verify_notify_reply(Else) -> - io:format("mtraaar_mg_verify_notify_reply -> unknown" - "~n Else: ~p~n", [Else]), +mtraaar_mg_verify_notify_reply(N, + {handle_trans_reply, + _CH, + ?VERSION, + ERROR, + _}) -> + io:format("mtraaar_mg_verify_notify_reply -> [~w] reply error" + "~n ERROR: ~p~n", [N, ERROR]), + {error, ERROR, ok}; +mtraaar_mg_verify_notify_reply(N, + Else) when is_tuple(Else) -> + io:format("mtraaar_mg_verify_notify_reply -> [~w] ~w instead of ~w" + "~n Else: ~p" + "~n", [N, element(1, Else), handle_trans_reply, Else]), + {error, Else, ok}; +mtraaar_mg_verify_notify_reply(N, + Else) -> + io:format("mtraaar_mg_verify_notify_reply -> [~w] unknown" + "~n Else: ~p~n", [N, Else]), {error, Else, ok}. mtraaar_mg_service_change_request_ar(_Mid, Cid) -> @@ -7408,18 +7503,6 @@ mtraaar_mg_service_change_request_ar(_Mid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -%% mtraaar_mg_service_change_request_msg(Mid, TransId, Cid) -> -%% AR = mtraaar_mg_service_change_request_ar(Mid, Cid), -%% TR = cre_transReq(TransId, [AR]), -%% Trans = cre_transaction(TR), -%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), -%% cre_megacoMessage(Mess). - -%% mtraaar_mg_notify_reply_ar(Cid, TermId) -> -%% NR = cre_notifyReply([TermId]), -%% CR = cre_cmdReply(NR), -%% cre_actionReply(Cid, [CR]). - mtraaar_mg_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "22000000"), Ev = cre_obsEvent("al/of", TT), @@ -7429,14 +7512,6 @@ mtraaar_mg_notify_request_ar(Rid, Tid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -%% mtraaar_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> -%% AR = mtraaar_mg_notify_request_ar(Rid, TermId, Cid), -%% TR = cre_transReq(TransId, [AR]), -%% Trans = cre_transaction(TR), -%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), -%% cre_megacoMessage(Mess). - - %% %% Common functions for the multi_trans_req_timeout test case %% @@ -9388,6 +9463,11 @@ sleep(X) -> receive after X -> ok end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +p(F, A) -> + io:format("*** [~s] ~p ***" + "~n " ++ F ++ "~n", + [?FTS(), self() | A]). + %% e(F) -> %% e(F, []). @@ -9422,32 +9502,11 @@ printable(_,_) -> false. print2(true, P, F, A) -> - TS = erlang:timestamp(), - TC = get(tc), S = ?F("*** [~s] ~s ~p ~w ***" "~n " ++ F ++ "~n" - "~n", [megaco:format_timestamp(TS), P, self(), TC | A]), - io:format("~s", [S]), - io:format(user, "~s", [S]); + "~n", [?FTS(), P, self(), get(tc) | A]), + io:format("~s", [S]); print2(_, _, _, _) -> ok. -p(F, A) -> - io:format("*** [~s] ***" - "~n " ++ F ++ "~n", - [megaco:format_timestamp(erlang:timestamp()) | A]). - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -%% random_init() -> -%% {A,B,C} = erlang:timestamp(), -%% random:seed(A,B,C). - -%% random() -> -%% 10 * random:uniform(50). - -%% apply_load_timer() -> -%% erlang:send_after(random(), self(), apply_load_timeout). - diff --git a/lib/megaco/test/megaco_udp_test.erl b/lib/megaco/test/megaco_udp_SUITE.erl index 39ff44709e..26783d5faf 100644 --- a/lib/megaco/test/megaco_udp_test.erl +++ b/lib/megaco/test/megaco_udp_SUITE.erl @@ -22,7 +22,7 @@ %%---------------------------------------------------------------------- %% Purpose: %%---------------------------------------------------------------------- --module(megaco_udp_test). +-module(megaco_udp_SUITE). %%---------------------------------------------------------------------- %% Include files @@ -35,17 +35,21 @@ %% External exports %%---------------------------------------------------------------------- -export([ - all/0,groups/0,init_per_group/2,end_per_group/2, + suite/0, all/0, groups/0, + init_per_suite/1, end_per_suite/1, + init_per_group/2, end_per_group/2, + init_per_testcase/2, end_per_testcase/2, + start_normal/1, start_invalid_opt/1, start_and_stop/1, sendreceive/1, block_unblock/1, - socket_failure/1, - init_per_testcase/2, end_per_testcase/2, - t/0, t/1 + socket_failure/1 + ]). + %%---------------------------------------------------------------------- %% Internal exports %%---------------------------------------------------------------------- @@ -69,61 +73,136 @@ %%====================================================================== -%% External functions +%% Common Test interface functions %%====================================================================== -%%---------------------------------------------------------------------- -%% Function: t/0 -%% Description: Run all test cases -%%---------------------------------------------------------------------- -t() -> megaco_test_lib:t(?MODULE). +suite() -> + [{ct_hooks, [ts_install_cth]}]. -%%---------------------------------------------------------------------- -%% Function: t/1 -%% Description: Run the specified test cases -%%---------------------------------------------------------------------- -t(Case) -> megaco_test_lib:t({?MODULE, Case}). - +all() -> + [ + {group, start}, + {group, sending}, + {group, error} + ]. -%%====================================================================== -%% Test server callbacks -%%====================================================================== -%%---------------------------------------------------------------------- -%% Function: init_per_testcase/2 -%% Description: -%%---------------------------------------------------------------------- -init_per_testcase(Case, Config) -> - megaco_test_lib:init_per_testcase(Case, Config). +groups() -> + [ + {start, [], start_cases()}, + {sending, [], sending_cases()}, + {error, [], error_cases()} + ]. +start_cases() -> + [ + start_normal, + start_invalid_opt, + start_and_stop + ]. -%%---------------------------------------------------------------------- -%% Function: end_per_testcase/2 -%% Description: -%%---------------------------------------------------------------------- -end_per_testcase(Case, Config) -> - megaco_test_lib:end_per_testcase(Case, Config). +sending_cases() -> + [ + sendreceive, + block_unblock + ]. +error_cases() -> + [ + socket_failure + ]. -%%====================================================================== -%% Test case definitions -%%====================================================================== -all() -> - [{group, start}, {group, sending}, {group, errors}]. -groups() -> - [{start, [], - [start_normal, start_invalid_opt, start_and_stop]}, - {sending, [], [sendreceive, block_unblock]}, - {errors, [], [socket_failure]}]. +%% +%% ----- +%% -init_per_group(_GroupName, Config) -> +init_per_suite(suite) -> + []; +init_per_suite(doc) -> + []; +init_per_suite(Config0) when is_list(Config0) -> + + ?ANNOUNCE_SUITE_INIT(), + + p("init_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + case ?LIB:init_per_suite(Config0) of + {skip, _} = SKIP -> + SKIP; + + Config1 when is_list(Config1) -> + + %% We need a (local) monitor on this node also + megaco_test_sys_monitor:start(), + + p("init_per_suite -> end when" + "~n Config: ~p" + "~n Nodes: ~p", [Config1, erlang:nodes()]), + + Config1 + end. + +end_per_suite(suite) -> []; +end_per_suite(doc) -> []; +end_per_suite(Config0) when is_list(Config0) -> + + p("end_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), + + megaco_test_sys_monitor:stop(), + Config1 = ?LIB:end_per_suite(Config0), + + p("end_per_suite -> end when" + "~n Nodes: ~p", [erlang:nodes()]), + + Config1. + + +%% +%% ----- +%% + +init_per_group(Group, Config) -> + ?ANNOUNCE_GROUP_INIT(Group), Config. -end_per_group(_GroupName, Config) -> +end_per_group(_Group, Config) -> Config. + +%% +%% ----- +%% + +init_per_testcase(Case, Config) -> + + p("init_per_testcase -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config, erlang:nodes()]), + + megaco_test_global_sys_monitor:reset_events(), + + megaco_test_lib:init_per_testcase(Case, Config). + +end_per_testcase(Case, Config) -> + + p("end_per_testcase -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config, erlang:nodes()]), + + p("system events during test: " + "~n ~p", [megaco_test_global_sys_monitor:events()]), + + megaco_test_lib:end_per_testcase(Case, Config). + + + + %% ================================================= %% %% ------------------ start ------------------------ @@ -1213,9 +1292,9 @@ p(S, F, A) when is_list(S) -> "~n " ++ F ++ "~n", [?FTS(), self(), S | A]); p(_S, F, A) -> - io:format("*** [~s] ~p ~s *** " + io:format("*** [~s] ~p *** " "~n " ++ F ++ "~n", - [?FTS(), self(), "undefined" | A]). + [?FTS(), self() | A]). ms() -> diff --git a/lib/megaco/test/modules.mk b/lib/megaco/test/modules.mk index 32b35d6123..3ec3ce7368 100644 --- a/lib/megaco/test/modules.mk +++ b/lib/megaco/test/modules.mk @@ -2,7 +2,7 @@ # %CopyrightBegin% # -# Copyright Ericsson AB 2001-2016. All Rights Reserved. +# Copyright Ericsson AB 2001-2019. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -25,43 +25,16 @@ COVER_SPEC_FILE = megaco.cover BEHAVIOUR_MODULES = \ megaco_test_generator -MODULES = \ +TEST_UTIL_MODULES = \ $(BEHAVIOUR_MODULES) \ - megaco_SUITE \ - megaco_app_test \ - megaco_appup_test \ - megaco_actions_test \ - megaco_binary_term_id_test \ - megaco_call_flow_test \ - megaco_codec_test \ megaco_codec_test_lib \ megaco_codec_flex_lib \ - megaco_codec_v1_test \ - megaco_codec_v2_test \ - megaco_codec_prev3a_test \ - megaco_codec_prev3b_test \ - megaco_codec_prev3c_test \ - megaco_codec_v3_test \ - megaco_codec_mini_test \ - megaco_config_test \ - megaco_digit_map_test \ - megaco_examples_test \ - megaco_flex_test \ - megaco_load_test \ - megaco_mess_test \ megaco_mess_user_test \ megaco_mess_otp8212_test \ - megaco_mib_test \ - megaco_mreq_test \ - megaco_pending_limit_test \ megaco_profile \ - megaco_segment_test \ - megaco_sdp_test \ megaco_tc_controller \ - megaco_tcp_test \ - megaco_timer_test \ - megaco_trans_test \ - megaco_udp_test \ + megaco_test_global_sys_monitor \ + megaco_test_sys_monitor \ megaco_test_generator_lib \ megaco_test_megaco_generator \ megaco_test_tcp_generator \ @@ -77,6 +50,37 @@ MODULES = \ megaco_test_msg_v3_lib \ megaco_test_lib +SUITE_MODULES = \ + megaco_actions_SUITE \ + megaco_app_SUITE \ + megaco_binary_term_id_SUITE \ + megaco_call_flow_SUITE \ + megaco_codec_mini_SUITE \ + megaco_codec_v1_SUITE \ + megaco_codec_v2_SUITE \ + megaco_codec_prev3a_SUITE \ + megaco_codec_prev3b_SUITE \ + megaco_codec_prev3c_SUITE \ + megaco_codec_v3_SUITE \ + megaco_config_SUITE \ + megaco_digit_map_SUITE \ + megaco_examples_SUITE \ + megaco_flex_SUITE \ + megaco_load_SUITE \ + megaco_mess_SUITE \ + megaco_mib_SUITE \ + megaco_mreq_SUITE \ + megaco_pending_limit_SUITE \ + megaco_sdp_SUITE \ + megaco_segment_SUITE \ + megaco_tcp_SUITE \ + megaco_timer_SUITE \ + megaco_trans_SUITE \ + megaco_udp_SUITE + +MODULES = \ + $(TEST_UTIL_MODULES) \ + $(SUITE_MODULES) INTERNAL_HRL_FILES = \ megaco_test_lib.hrl diff --git a/lib/snmp/doc/src/notes.xml b/lib/snmp/doc/src/notes.xml index e376330809..dd1c535d01 100644 --- a/lib/snmp/doc/src/notes.xml +++ b/lib/snmp/doc/src/notes.xml @@ -80,6 +80,32 @@ </section> + <section><title>SNMP 5.4.3.1</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Its now possible to remove selected varbinds (from the + final message) when sending a notification. This is done + by setting the 'value' (in the varbind(s) of the varbinds + list) to '?NOTIFICATION_IGNORE_VB_VALUE'.</p> + <p> + Own Id: OTP-16349 Aux Id: ERIERL-444 </p> + </item> + <item> + <p> + Its now possible to specify that an oid shall be + "truncated" (trailing ".0" to be removed) when sending an + notification.</p> + <p> + Own Id: OTP-16360 Aux Id: ERIERL-451 </p> + </item> + </list> + </section> + +</section> + <section><title>SNMP 5.4.3</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/ssh/src/ssh.app.src b/lib/ssh/src/ssh.app.src index 6846979202..21e3604400 100644 --- a/lib/ssh/src/ssh.app.src +++ b/lib/ssh/src/ssh.app.src @@ -50,4 +50,3 @@ "public_key-1.6.1", "stdlib-3.4.1" ]}]}. - diff --git a/lib/ssh/src/ssh_connection.erl b/lib/ssh/src/ssh_connection.erl index bef1927f53..73aeda8efc 100644 --- a/lib/ssh/src/ssh_connection.erl +++ b/lib/ssh/src/ssh_connection.erl @@ -1368,3 +1368,4 @@ request_reply_or_data(#channel{local_id = ChannelId, user = ChannelPid}, false -> {[{channel_data, ChannelPid, Reply}], Connection} end. + diff --git a/lib/ssh/test/property_test/ssh_eqc_client_info_timing.erl b/lib/ssh/test/property_test/ssh_eqc_client_info_timing.erl index f4b521356f..dfc94f830c 100644 --- a/lib/ssh/test/property_test/ssh_eqc_client_info_timing.erl +++ b/lib/ssh/test/property_test/ssh_eqc_client_info_timing.erl @@ -23,37 +23,7 @@ -compile(export_all). --proptest(eqc). --proptest([triq,proper]). - --ifndef(EQC). --ifndef(PROPER). --ifndef(TRIQ). --define(EQC,true). -%%-define(PROPER,true). -%%-define(TRIQ,true). --endif. --endif. --endif. - --ifdef(EQC). --include_lib("eqc/include/eqc.hrl"). --define(MOD_eqc,eqc). - --else. --ifdef(PROPER). --include_lib("proper/include/proper.hrl"). --define(MOD_eqc,proper). - --else. --ifdef(TRIQ). --define(MOD_eqc,triq). --include_lib("triq/include/triq.hrl"). - --endif. --endif. --endif. - +-include_lib("common_test/include/ct_property_test.hrl"). %%% Properties: diff --git a/lib/ssh/test/property_test/ssh_eqc_client_server.erl b/lib/ssh/test/property_test/ssh_eqc_client_server.erl index acb0faa0c7..4c890c9dd2 100644 --- a/lib/ssh/test/property_test/ssh_eqc_client_server.erl +++ b/lib/ssh/test/property_test/ssh_eqc_client_server.erl @@ -23,8 +23,6 @@ -compile(export_all). --proptest([proper]). - -ifndef(PROPER). -else. %% Only use proper @@ -35,10 +33,9 @@ %% However, with access to eqc it ought to be quite easy to re-enable eqc by %% studying the diff. --include_lib("proper/include/proper.hrl"). --define(MOD_eqc,proper). - -include_lib("common_test/include/ct.hrl"). +-include_lib("common_test/include/ct_property_test.hrl"). + %% Limit the testing time on CI server... this needs to be improved in % from total budget. -define(TESTINGTIME(Prop), eqc:testing_time(30,Prop)). @@ -102,20 +99,20 @@ %% To be called as eqc:quickcheck( ssh_eqc_client_server:prop_seq() ). prop_seq() -> error_logger:tty(false), - ?TESTINGTIME(do_prop_seq(?SSH_DIR)). + ?TESTINGTIME(do_prop_seq(?SSH_DIR,[])). %% To be called from a common_test test suite prop_seq(CT_Config) -> error_logger:tty(false), - do_prop_seq(full_path(?SSH_DIR, CT_Config)). + do_prop_seq(full_path(?SSH_DIR, CT_Config), CT_Config). -do_prop_seq(DataDir) -> +do_prop_seq(DataDir, CT_Config) -> setup_rsa(DataDir), ?FORALL(Cmds,commands(?MODULE), begin {H,Sf,Result} = run_commands(?MODULE,Cmds,[{data_dir,DataDir}]), - present_result(?MODULE, Cmds, {H,Sf,Result}, Result==ok) + ct_property_test:present_result(?MODULE, Cmds, {H,Sf,Result}, CT_Config, []) end). full_path(SSHdir, CT_Config) -> @@ -124,37 +121,37 @@ full_path(SSHdir, CT_Config) -> %%%---- prop_parallel() -> error_logger:tty(false), - ?TESTINGTIME(do_prop_parallel(?SSH_DIR)). + ?TESTINGTIME(do_prop_parallel(?SSH_DIR,[])). %% To be called from a common_test test suite prop_parallel(CT_Config) -> error_logger:tty(false), - do_prop_parallel(full_path(?SSH_DIR, CT_Config)). + do_prop_parallel(full_path(?SSH_DIR, CT_Config), CT_Config). -do_prop_parallel(DataDir) -> +do_prop_parallel(DataDir, CT_Config) -> setup_rsa(DataDir), ?FORALL(Cmds,parallel_commands(?MODULE), begin {H,Sf,Result} = run_parallel_commands(?MODULE,Cmds,[{data_dir,DataDir}]), - present_result(?MODULE, Cmds, {H,Sf,Result}, Result==ok) + ct_property_test:present_result(?MODULE, Cmds, {H,Sf,Result}, CT_Config, []) end). %%%---- %% prop_parallel_multi() -> -%% ?TESTINGTIME(do_prop_parallel_multi(?SSH_DIR)). +%% ?TESTINGTIME(do_prop_parallel_multi(?SSH_DIR, [])). %% %% To be called from a common_test test suite %% prop_parallel_multi(CT_Config) -> -%% do_prop_parallel_multi(full_path(?SSH_DIR, CT_Config)). +%% do_prop_parallel_multi(full_path(?SSH_DIR, CT_Config), CT_Config). -%% do_prop_parallel_multi(DataDir) -> +%% do_prop_parallel_multi(DataDir, CT_Config) -> %% setup_rsa(DataDir), %% ?FORALL(Repetitions,?SHRINK(1,[10]), %% ?FORALL(Cmds,parallel_commands(?MODULE), %% ?ALWAYS(Repetitions, %% begin %% {H,Sf,Result} = run_parallel_commands(?MODULE,Cmds,[{data_dir,DataDir}]), -%% present_result(?MODULE, Cmds, {H,Sf,Result}, Result==ok) +%% ct_property_test:present_result(?MODULE, Cmds, {H,Sf,Result}, CT_Config, []) %% end))). %%%================================================================ @@ -476,178 +473,6 @@ is_ok(_) -> true. ensure_string({A,B,C,D}) -> lists:flatten(io_lib:format("~w.~w.~w.~w",[A,B,C,D])); ensure_string(X) -> X. -%%%---------------------------------------------------------------- -present_result(_Module, Cmds, _Triple, true) -> - aggregate(with_title("Distribution sequential/parallel"), sequential_parallel(Cmds), - aggregate(with_title("Function calls"), cmnd_names(Cmds), - aggregate(with_title("Message sizes"), empty_msgs(Cmds), - aggregate(print_frequencies(), message_sizes(Cmds), - aggregate(title("Length of command sequences",print_frequencies()), num_calls(Cmds), - true))))); - -present_result(Module, Cmds, Triple, false) -> - pretty_comands(Module, Cmds, Triple, [{show_states,true}], false), - false. % Proper dislikes non-boolean results while eqc treats non-true as false. - -pretty_comands(Module, Cmds, Triple, Opts, Bool) -> - ct:log("Module = ~p,~n Cmds = ~p,~n Triple = ~p,~n Opts = ~p,~n Bool = ~p",[Module, Cmds, Triple, Opts, Bool]). - - - -cmnd_names(Cs) -> traverse_commands(fun cmnd_name/1, Cs). -cmnd_name(L) -> [F || {set,_Var,{call,_Mod,F,_As}} <- L]. - -empty_msgs(Cs) -> traverse_commands(fun empty_msg/1, Cs). -empty_msg(L) -> [empty || {set,_,{call,_,ssh_send,[_,_,Msg]}} <- L, - size(Msg)==0]. - -message_sizes(Cs) -> traverse_commands(fun message_size/1, Cs). -message_size(L) -> [size(Msg) || {set,_,{call,_,ssh_send,[_,_,Msg]}} <- L]. - -num_calls(Cs) -> traverse_commands(fun num_call/1, Cs). -num_call(L) -> [length(L)]. - -sequential_parallel(Cs) -> - traverse_commands(fun(L) -> dup_module(L, sequential) end, - fun(L) -> [dup_module(L1, mkmod("parallel",num(L1,L))) || L1<-L] end, - Cs). -dup_module(L, ModName) -> lists:duplicate(length(L), ModName). -mkmod(PfxStr,N) -> list_to_atom(PfxStr++"_"++integer_to_list(N)). - -%% Meta functions for the aggregate functions -traverse_commands(Fun, L) when is_list(L) -> Fun(L); -traverse_commands(Fun, {Seq, ParLs}) -> Fun(lists:append([Seq|ParLs])). - -traverse_commands(Fseq, _Fpar, L) when is_list(L) -> Fseq(L); -traverse_commands(Fseq, Fpar, {Seq, ParLs}) -> lists:append([Fseq(Seq)|Fpar(ParLs)]). - -%%%---------------- -%% PrintMethod([{term(), int()}]) -> any(). -print_frequencies() -> print_frequencies(10). - -print_frequencies(Ngroups) -> fun([]) -> io:format('Empty list!~n',[]); - (L ) -> - try - M = lists:last(L), - Max = if is_integer(M) -> M; - is_tuple(M) -> element(1,L) - end, - print_frequencies(L,Ngroups,0,Max) - catch - C:E:S -> - ct:pal("~p:~p ~p:~p~n~p~n~p",[?MODULE,?LINE,C,E,S,L]) - end - end. - - -print_frequencies(Ngroups, MaxValue) -> fun(L) -> print_frequencies(L,Ngroups,0,MaxValue) end. - -print_frequencies(L, N, Min, Max) when N>Max -> print_frequencies(L++[{N,0}], N, Min, N); -print_frequencies(L, N, Min, Max0) -> - try - Interval = round((Max0-Min)/N), - Max = Max0 + (Max0 rem Interval), - IntervalUpperLimits = - lists:reverse( - [Max | tl(lists:reverse(lists:seq(Min,Max,Interval)))] - ), - {Acc0,_} = lists:mapfoldl(fun(Upper,Lower) -> - {{{Lower,Upper},0}, Upper+1} - end, hd(IntervalUpperLimits), tl(IntervalUpperLimits)), - Fs0 = get_frequencies(L, Acc0), - SumVal = lists:sum([V||{_,V}<-Fs0]), - Fs = with_percentage(Fs0, SumVal), - Mean = mean(L), - Median = median(L), - Npos_value = num_digits(SumVal), - Npos_range = num_digits(Max), - io:format("Range~*s: ~s~n",[2*Npos_range-2,"", "Number in range"]), - io:format("~*c:~*c~n",[2*Npos_range+3,$-, max(16,Npos_value+10),$- ]), - [begin - io:format("~*w - ~*w: ~*w ~5.1f%",[Npos_range,Rlow, - Npos_range,Rhigh, - Npos_value,Val, - Percent]), - [io:format(" <-- mean=~.1f",[Mean]) || in_interval(Mean, Interval)], - [io:format(" <-- median=" ++ - if - is_float(Median) -> "~.1f"; - true -> "~p" - end, [Median]) || in_interval(Median, Interval)], - io:nl() - end - || {Interval={Rlow,Rhigh},Val,Percent} <- Fs], - io:format('~*c ~*c~n',[2*Npos_range,32,Npos_value+2,$-]), - io:format('~*c ~*w~n',[2*Npos_range,32,Npos_value,SumVal]) - catch - C:E -> - io:format('*** Faild printing (~p:~p) for~n~p~n',[C,E,L]) - end. - -get_frequencies([{I,Num}|T], [{{Lower,Upper},Cnt}|Acc]) when Lower=<I,I=<Upper -> - get_frequencies(T, [{{Lower,Upper},Cnt+Num}|Acc]); -get_frequencies(L=[{I,_Num}|_], [Ah={{_Lower,Upper},_Cnt}|Acc]) when I>Upper -> - [Ah | get_frequencies(L,Acc)]; -get_frequencies([I|T], Acc) when is_integer(I) -> - get_frequencies([{I,1}|T], Acc); -get_frequencies([], Acc) -> - Acc. - -with_percentage(Fs, Sum) -> - [{Rng,Val,100*Val/Sum} || {Rng,Val} <- Fs]. - - -title(Str, Fun) -> - fun(L) -> - io:format('~s~n',[Str]), - Fun(L) - end. - -num_digits(I) -> 1+trunc(math:log(I)/math:log(10)). - -num(Elem, List) -> length(lists:takewhile(fun(E) -> E /= Elem end, List)) + 1. - -%%%---- Just for naming an operation for readability -is_odd(I) -> (I rem 2) == 1. - -in_interval(Value, {Rlow,Rhigh}) -> - try - Rlow=<round(Value) andalso round(Value)=<Rhigh - catch - _:_ -> false - end. - -%%%================================================================ -%%% Statistical functions - -%%%---- Mean value -mean(L = [X|_]) when is_number(X) -> - lists:sum(L) / length(L); -mean(L = [{_Value,_Weight}|_]) -> - SumOfWeights = lists:sum([W||{_,W}<-L]), - WeightedSum = lists:sum([W*V||{V,W}<-L]), - WeightedSum / SumOfWeights; -mean(_) -> - undefined. - -%%%---- Median -median(L = [X|_]) when is_number(X) -> - case is_odd(length(L)) of - true -> - hd(lists:nthtail(length(L) div 2, L)); - false -> - %% 1) L has at least on element (the when test). - %% 2) Length is even. - %% => Length >= 2 - [M1,M2|_] = lists:nthtail((length(L) div 2)-1, L), - (M1+M2) / 2 - end; -%% integer Weights... -median(L = [{_Value,_Weight}|_]) -> - median( lists:append([lists:duplicate(W,V) || {V,W} <- L]) ); -median(_) -> - undefined. - %%%================================================================ %%% The rest is taken and modified from ssh_test_lib.erl setup_rsa(Dir) -> diff --git a/lib/ssh/test/property_test/ssh_eqc_encode_decode.erl b/lib/ssh/test/property_test/ssh_eqc_encode_decode.erl index 165274241c..6470fa5f3d 100644 --- a/lib/ssh/test/property_test/ssh_eqc_encode_decode.erl +++ b/lib/ssh/test/property_test/ssh_eqc_encode_decode.erl @@ -23,36 +23,7 @@ -compile(export_all). --proptest(eqc). --proptest([triq,proper]). - --ifndef(EQC). --ifndef(PROPER). --ifndef(TRIQ). --define(EQC,true). -%%-define(PROPER,true). -%%-define(TRIQ,true). --endif. --endif. --endif. - --ifdef(EQC). --include_lib("eqc/include/eqc.hrl"). --define(MOD_eqc,eqc). - --else. --ifdef(PROPER). --include_lib("proper/include/proper.hrl"). --define(MOD_eqc,proper). - --else. --ifdef(TRIQ). --define(MOD_eqc,triq). --include_lib("triq/include/triq.hrl"). - --endif. --endif. --endif. +-include_lib("common_test/include/ct_property_test.hrl"). %% Public key records: -include_lib("public_key/include/public_key.hrl"). diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml index 0ad2d8da0b..7424566adb 100644 --- a/lib/ssl/doc/src/ssl.xml +++ b/lib/ssl/doc/src/ssl.xml @@ -256,6 +256,26 @@ <name name="reason"/> </datatype> + <datatype> + <name name="bloom_filter_window_size"/> + </datatype> + + <datatype> + <name name="bloom_filter_hash_functions"/> + </datatype> + + <datatype> + <name name="bloom_filter_bits"/> + </datatype> + + <datatype> + <name name="client_session_tickets"/> + </datatype> + + <datatype> + <name name="server_session_tickets"/> + </datatype> + <datatype_title>TLS/DTLS OPTION DESCRIPTIONS - COMMON for SERVER and CLIENT</datatype_title> <datatype> @@ -720,7 +740,13 @@ fun(srp, Username :: string(), UserState :: term()) -> <name name="ssl_imp"/> <desc><p>Deprecated since OTP-17, has no affect.</p></desc> </datatype> - + + <datatype> + <name name="session_tickets"/> + <desc><p>Configures the session ticket functionalty in TLS 1.3 client and server. </p> + </desc> + </datatype> + <datatype_title>TLS/DTLS OPTION DESCRIPTIONS - CLIENT</datatype_title> <datatype> @@ -907,7 +933,39 @@ fun(srp, Username :: string(), UserState :: term()) -> </p> </desc> </datatype> - + + <datatype> + <name name="client_session_tickets"/> + <desc> + <p>Configures the session ticket functionality. Allowed values are <c>disabled</c>, + <c>manual</c> and <c>auto</c>. If it is set to <c>manual</c> the client will send + the ticket information to user process in a 3-tuple:</p> + <p><c>{ssl, session_ticket, {SNI, TicketData}}</c></p> + <p>where <c>SNI</c> is the ServerNameIndication and <c>TicketData</c> is the extended + ticket data that can be used in subsequent session resumptions.</p> + <p>If it is set to <c>auto</c>, the client automatically handles received tickets and tries + to use them when making new TLS connections (session resumption with pre-shared keys).</p> + <note><p>This option is supported by TLS 1.3 and above. See also + <seealso marker="ssl:using_ssl#session-tickets-and-session-resumption-in-tls-1.3"> + SSL's Users Guide, Session Tickets and Session Resumption in TLS 1.3</seealso> + </p></note> + </desc> + </datatype> + + <datatype> + <name name="use_ticket"/> + <desc> + <p>Configures the session tickets to be used for session resumption. It is a mandatory + option in <c>manual</c> mode (<c>session_tickets = manual</c>).</p> + <note><p>Session tickets are only sent to user if option <em>session_tickets</em> is set + to <c>manual</c></p> + <p>This option is supported by TLS 1.3 and above. See also + <seealso marker="ssl:using_ssl#session-tickets-and-session-resumption-in-tls-1.3"> + SSL's Users Guide, Session Tickets and Session Resumption in TLS 1.3</seealso> + </p></note> + </desc> + </datatype> + <datatype_title>TLS/DTLS OPTION DESCRIPTIONS - SERVER </datatype_title> @@ -1113,6 +1171,55 @@ fun(srp, Username :: string(), UserState :: term()) -> </p> </desc> </datatype> + + <datatype> + <name name="server_session_tickets"/> + <desc> + <p>Configures the session ticket functionality. Allowed values are <c>disabled</c>, + <c>stateful</c> and <c>stateless</c>.</p> + <p>If it is set to <c>stateful</c> or + <c>stateless</c>, session resumption with pre-shared keys is enabled and the server will + send stateful or stateless session tickets to the client after successful connections.</p> + <p>A stateful session ticket is a database reference to internal state information. + A stateless session ticket is a self-encrypted binary that contains both cryptographic keying + material and state data. + </p> + <note><p>This option is supported by TLS 1.3 and above. See also + <seealso marker="ssl:using_ssl#session-tickets-and-session-resumption-in-tls-1.3"> + SSL's Users Guide, Session Tickets and Session Resumption in TLS 1.3</seealso> + </p></note> + </desc> + </datatype> + + <datatype> + <name name="anti_replay"/> + <desc> + <p>Configures the server's built-in anti replay feature based on Bloom filters.</p> + + <p>Allowed values are the pre-defined <c>'10k'</c>, <c>'100k'</c> or a custom 3-tuple that + defines the properties of the bloom filters: <c>{WindowSize, HashFunctions, Bits}</c>. + <c>WindowSize</c> is the number of seconds after the current Bloom filter is rotated + and also the window size used for freshness checks. <c>HashFunctions</c> is the number + hash functions and <c>Bits</c> is the number of bits in the bit vector. + <c>'10k'</c> and <c>'100k'</c> are simple defaults with the following properties:</p> + <list type="bulleted"> + <item><p><c>'10k'</c>: Bloom filters can hold 10000 elements with 3% probability of + false positives. <c>WindowSize</c>: 10, <c>HashFunctions</c>: 5, <c>Bits:</c> + 72985 (8.91 KiB).</p></item> + <item><p><c>'100k'</c>: Bloom filters can hold 100000 elements with 3% probability of + false positives. <c>WindowSize</c>: 10, <c>HashFunctions</c>: 5, <c>Bits</c>: + 729845 (89.09 KiB).</p></item> + </list> + + <note><p>This option is supported by TLS 1.3 and above and only with stateless session tickets. + Ticket lifetime, the number of tickets sent by the server and the maximum number of tickets + stored by the server in stateful mode are configured by + <seealso marker="ssl:ssl_app#configuration">application variables</seealso>. See also + <seealso marker="ssl:using_ssl#anti-replay-protection-in-tls-1.3"> + SSL's Users Guide, Anti-Replay Protection in TLS 1.3</seealso> + </p></note> + </desc> + </datatype> </datatypes> <!-- diff --git a/lib/ssl/doc/src/ssl_app.xml b/lib/ssl/doc/src/ssl_app.xml index 129fc57862..64d9f55fa9 100644 --- a/lib/ssl/doc/src/ssl_app.xml +++ b/lib/ssl/doc/src/ssl_app.xml @@ -52,7 +52,8 @@ <section> <title>CONFIGURATION</title> - <p>The application environment configuration parameters in this section + <p> + The application environment configuration parameters in this section are defined for the SSL application. For more information about configuration parameters, see the <seealso marker="kernel:application">application(3)</seealso> @@ -138,7 +139,7 @@ </item> <tag><c><![CDATA[internal_active_n = integer() <optional>]]></c></tag> - <item> + <item> <p> For TLS connections this value is used to handle the internal socket. As the implementation was changed from an @@ -150,6 +151,47 @@ Added in ssl-9.1 (OTP-21.2). </p> </item> + + <tag><c><![CDATA[server_session_tickets_amount = integer() <optional>]]></c></tag> + <item> + <p> + Number of session tickets sent by the server. It must be greater than 0. + Defaults to 3. + </p> + </item> + + <tag><c><![CDATA[server_session_ticket_lifetime = integer() <optional>]]></c></tag> + <item> + <p> + Lifetime of session tickets sent by the server. Servers must not use + any value greater than 604800 seconds (7 days). Expired tickets are automatically removed. + Defaults to 7200 seconds (2 hours). + </p> + </item> + + <tag><c><![CDATA[server_session_ticket_store_size = integer() <optional>]]></c></tag> + <item> + <p> + Sets the maximum size of the server session ticket store (stateful tickets). + Defaults to 1000. Size limit is enforced by dropping old tickets. + </p> + </item> + + <tag><c><![CDATA[client_session_ticket_lifetime = integer() <optional>]]></c></tag> + <item> + <p> + Lifetime of session tickets in the client ticket store. Expired tickets are + automatically removed. Defaults to 7200 seconds (2 hours). + </p> + </item> + + <tag><c><![CDATA[client_session_ticket_store_size = integer() <optional>]]></c></tag> + <item> + <p> + Sets the maximum size of the client session ticket store. + Defaults to 1000. Size limit is enforced by dropping old tickets. + </p> + </item> </taglist> </section> diff --git a/lib/ssl/doc/src/standards_compliance.xml b/lib/ssl/doc/src/standards_compliance.xml index 9df48b99d3..98542a797b 100644 --- a/lib/ssl/doc/src/standards_compliance.xml +++ b/lib/ssl/doc/src/standards_compliance.xml @@ -126,10 +126,8 @@ <section> <title>TLS 1.3</title> - <p>OTP-22 introduces basic support for TLS 1.3. Basic functionality - covers a simple TLS 1.3 handshake with support of the mandatory extensions - (supported_groups, signature_algorithms, key_share, supported_versions and - signature_algorithms_cert). The current implementation supports a selective set of cryptographic algorithms:</p> + <p>OTP-22 introduces support for TLS 1.3. + The current implementation supports a selective set of cryptographic algorithms:</p> <list type="bulleted"> <item>Key Exchange: ECDHE</item> <item>Groups: all standard groups supported for the Diffie-Hellman key exchange</item> @@ -142,7 +140,8 @@ </list> <p>Other notable features:</p> <list type="bulleted"> - <item>PSK and session resumption not supported</item> + <item>PSK and session resumption is supported (stateful and stateless tickets)</item> + <item>Anti-replay protection using Bloom-filters with stateless tickets</item> <item>Early data and 0-RTT not supported</item> <item>Key and Initialization Vector Update not supported</item> </list> @@ -221,8 +220,8 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">PSK with (EC)DHE</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.2</em></cell> </row> <row> @@ -243,8 +242,8 @@ </url> </cell> <cell align="left" valign="middle"></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.2</em></cell> </row> <row> @@ -265,26 +264,26 @@ </url> </cell> <cell align="left" valign="middle"></cell> - <cell align="left" valign="middle"><em>PC</em></cell> - <cell align="left" valign="middle"><em>22</em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.2</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">supported_groups extension</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">signature_algorithms extension</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">pre_shared_key extension</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.2</em></cell> </row> <row> @@ -300,8 +299,8 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">server_name (RFC6066)</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>PC</em></cell> + <cell align="left" valign="middle"><em>22.2</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -378,14 +377,14 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">pre_shared_key (RFC8446)</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.2</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">psk_key_exchange_modes (RFC8446)</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.2</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -439,8 +438,8 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">server_name (RFC6066)</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>PC</em></cell> + <cell align="left" valign="middle"><em>22.2</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -517,14 +516,14 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">pre_shared_key (RFC8446)</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.2</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">psk_key_exchange_modes (RFC8446)</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.2</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -576,8 +575,8 @@ </url> </cell> <cell align="left" valign="middle"><em>Client</em></cell> - <cell align="left" valign="middle"><em>PC</em></cell> - <cell align="left" valign="middle"><em>22.1</em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.2</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -594,8 +593,8 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">pre_shared_key (RFC8446)</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.2</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -607,8 +606,8 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle"><em>Server</em></cell> - <cell align="left" valign="middle"><em>PC</em></cell> - <cell align="left" valign="middle"><em>22</em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.2</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -625,8 +624,8 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">pre_shared_key (RFC8446)</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.2</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -1121,14 +1120,14 @@ </url> </cell> <cell align="left" valign="middle"><em>Client</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.2</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle"><em>Server</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.2</em></cell> </row> <row> @@ -1155,14 +1154,14 @@ </url> </cell> <cell align="left" valign="middle"><em>Client</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.2</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle"><em>Server</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.2</em></cell> </row> <row> @@ -1172,14 +1171,14 @@ </url> </cell> <cell align="left" valign="middle"><em>Client</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.2</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle"><em>Server</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.2</em></cell> </row> <row> @@ -1189,14 +1188,14 @@ </url> </cell> <cell align="left" valign="middle"><em>Client</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.2</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle"><em>Server</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.2</em></cell> </row> <row> @@ -1675,8 +1674,8 @@ </url> </cell> <cell align="left" valign="middle"><em>Client</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>PC</em></cell> + <cell align="left" valign="middle"><em>22.2</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -1688,8 +1687,8 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle"><em>Server</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>PC</em></cell> + <cell align="left" valign="middle"><em>22.2</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -1914,8 +1913,8 @@ </url> </cell> <cell align="left" valign="middle"><em></em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.2</em></cell> </row> <row> @@ -1925,8 +1924,8 @@ </url> </cell> <cell align="left" valign="middle"><em></em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.2</em></cell> </row> <row> @@ -1936,8 +1935,8 @@ </url> </cell> <cell align="left" valign="middle"><em></em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.2</em></cell> </row> <row> @@ -1947,8 +1946,8 @@ </url> </cell> <cell align="left" valign="middle"><em></em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.2</em></cell> </row> <row> @@ -2080,8 +2079,8 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle"><em>MUST send and use these extensions</em></cell> - <cell align="left" valign="middle"><em>PC</em></cell> - <cell align="left" valign="middle"><em>22</em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.2</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -2110,14 +2109,14 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">"pre_shared_key" is REQUIRED for PSK key agreement</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.2</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">"psk_key_exchange_modes" is REQUIRED for PSK key agreement</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.2</em></cell> </row> <row> @@ -2282,8 +2281,8 @@ </url> </cell> <cell align="left" valign="middle"><em></em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.2</em></cell> </row> <row> @@ -2304,8 +2303,8 @@ </url> </cell> <cell align="left" valign="middle"><em></em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.2</em></cell> </row> <row> diff --git a/lib/ssl/doc/src/using_ssl.xml b/lib/ssl/doc/src/using_ssl.xml index f2f9b66a31..411f3461f9 100644 --- a/lib/ssl/doc/src/using_ssl.xml +++ b/lib/ssl/doc/src/using_ssl.xml @@ -231,6 +231,213 @@ ssl:connect("localhost", 9999, <p>See also <seealso marker="crypto:engine_load#engine_load"> crypto documentation</seealso> </p> - </section> - + </section> + + <section> + <title>Session Tickets and Session Resumption in TLS 1.3</title> + + <p> + TLS 1.3 introduces a new secure way of resuming sessions by using session tickets. + A session ticket is an opaque data structure that is sent in the pre_shared_key extension of + a ClientHello, when a client attempts to resume a session with keying material from a + previous successful handshake.</p> + <p>Session tickets can be stateful or stateless. A stateful session ticket is a database reference + (session ticket store) and used with stateful servers, while a stateless ticket + is a self-encrypted and self-authenticated data structure with cryptographic keying material and + state data, enabling session resumption with stateless servers.</p> + <p>The choice between stateful or stateless depends on the server requirements as the session tickets + are opaque for the clients. Generally, stateful tickets are smaller and the server can guarantee + that tickets are only used once. Stateless tickets contain additional data, require less storage + on the server side, but they offer different guarantees against anti-replay. See also + <seealso marker="ssl:using_ssl#anti-replay-protection-in-tls-1.3"> + Anti-Replay Protection in TLS 1.3</seealso> + </p> + <p>Session tickets are sent by servers on newly estalished TLS connections. + The number of tickets sent and their lifetime are configurable by application variables. See also + <seealso marker="ssl:ssl_app#configuration"> SSL's configuration</seealso>.</p> + <p>Session tickets are protected by application traffic keys, and in stateless + tickets, the opaque data structure itself is self-encrypted.</p> + + <p>An example with automatic and manual session resumption:</p> + + <p><em>Step 1 (server):</em> Start the server:</p> + <code type="erl"> + {ok, _} = application:ensure_all_started(ssl). + LOpts = [{certfile, "cert.pem"}, + {keyfile, "key.pem"}, + {versions, ['tlsv1.2','tlsv1.3']}, + {session_tickets, stateless}]. + {ok, LSock} = ssl:listen(8001, LOpts). + {ok, CSock} = ssl:transport_accept(LSock). + </code> + + <p><em>Step 2 (client):</em> Start the client and connect to server:</p> + <code type="erl"> + {ok, _} = application:ensure_all_started(ssl). + COpts = [{cacertfile, "cert.pem"}, + {versions, ['tlsv1.2','tlsv1.3']}, + {log_level, debug}, + {session_tickets, auto}]. + ssl:connect("localhost", 8001, COpts). + </code> + + <p><em>Step 3 (server):</em> Start the TLS handshake:</p> + <code type="erl"> + ssl:handshake(CSock). + </code> + + <p>A connection is established using a full handshake. + Below is a summary of the exchanged messages:</p> + <code type="erl"> + <![CDATA[>>>]]> TLS 1.3 Handshake, ClientHello ... + <![CDATA[<<<]]> TLS 1.3 Handshake, ServerHello ... + <![CDATA[<<<]]> Handshake, EncryptedExtensions ... + <![CDATA[<<<]]> Handshake, Certificate ... + <![CDATA[<<<]]> Handshake, CertificateVerify ... + <![CDATA[<<<]]> Handshake, Finished ... + <![CDATA[>>>]]> Handshake, Finished ... + <![CDATA[<<<]]> Post-Handshake, NewSessionTicket ... + </code> + + <p>At this point the client has stored the received session tickets and ready to use them when + establishing new connections to the same server.</p> + + <p><em>Step 4 (server):</em> Accept a new connection on the server:</p> + <code type="erl"> + {ok, CSock2} = ssl:transport_accept(LSock). + </code> + + <p><em>Step 5 (client):</em> Make a new connection:</p> + <code type="erl"> + ssl:connect("localhost", 8001, COpts). + </code> + + <p><em>Step 6 (server):</em> Start the handshake:</p> + <code type="erl"> + ssl:handshake(CSock2). + </code> + + <p>The second connection is a session resumption using keying material + from the previous handshake:</p> + <code type="erl"> + <![CDATA[>>>]]> TLS 1.3 Handshake, ClientHello ... + <![CDATA[<<<]]> TLS 1.3 Handshake, ServerHello ... + <![CDATA[<<<]]> Handshake, EncryptedExtensions ... + <![CDATA[<<<]]> Handshake, Finished ... + <![CDATA[>>>]]> Handshake, Finished ... + <![CDATA[<<<]]> Post-Handshake, NewSessionTicket ... + </code> + + <p>Manual handling of session tickets is also supported. In manual mode, it is the + responsibility of the client to handle received session tickets.</p> + + <p><em>Step 7 (server):</em> Accept a new connection on the server:</p> + <code type="erl"> + {ok, CSock3} = ssl:transport_accept(LSock). + </code> + + <p><em>Step 8 (client):</em> Make a new connection to server:</p> + <code type="erl"> + {ok, _} = application:ensure_all_started(ssl). + COpts2 = [{cacertfile, "cert.pem"}, + {versions, ['tlsv1.2','tlsv1.3']}, + {log_level, debug}, + {session_tickets, manual}]. + ssl:connect("localhost", 8001, COpts). + </code> + + <p><em>Step 9 (server):</em> Start the handshake:</p> + <code type="erl"> + ssl:handshake(CSock3). + </code> + + <p>After the handshake is performed, the user process receives messages with the tickets + sent by the server.</p> + + <p><em>Step 10 (client):</em> Receive a new session ticket:</p> + <code type="erl"> + Ticket = receive {ssl, session_ticket, {_, TicketData}} -> TicketData end. + </code> + + <p><em>Step 11 (server):</em> Accept a new connection on the server:</p> + <code type="erl"> + {ok, CSock4} = ssl:transport_accept(LSock). + </code> + + <p><em>Step 12 (client):</em> Initiate a new connection to the server with the session ticket + received in Step 10:</p> + <code type="erl"> + {ok, _} = application:ensure_all_started(ssl). + COpts2 = [{cacertfile, "cert.pem"}, + {versions, ['tlsv1.2','tlsv1.3']}, + {log_level, debug}, + {session_tickets, manual}, + {use_ticket, [Ticket]}]. + ssl:connect("localhost", 8001, COpts). + </code> + + <p><em>Step 13 (server):</em> Start the handshake:</p> + <code type="erl"> + ssl:handshake(CSock3). + </code> + </section> + + <section> + <title>Anti-Replay Protection in TLS 1.3</title> + + <p>The TLS 1.3 protocol does not provide inherent protection for replay of 0-RTT data but + describes mechanisms that SHOULD be implemented by compliant server implementations. + The implementation of TLS 1.3 in the SSL application employs all standard methods + to prevent potential threats. + </p> + <p><em>Single-use tickets</em></p> + <p>This mechanism is available with stateful session tickets. Session tickets can + only be used once, subsequent use of the same ticket results in a full handshake. + Stateful servers enforce this rule by maintaining a database of outstanding valid + tickets.</p> + + <p><em>Client Hello Recording</em></p> + <p>This mechanism is available with stateless session tickets. The server records + a unique value derived from the ClientHello (PSK binder) in a given time window. The + ticket's age is verified by using both the "obsfuscated_ticket_age" and an additional + timestamp encrypted in the ticket data. As the used datastore allows false positives, + apparent replays will be answered by doing a full 1-RTT handshake.</p> + + <p><em>Freshness Checks</em></p> + <p>This mechanism is available with the stateless session tickets. As the ticket data + has an embedded timestamp, the server can determine if a ClientHello was sent reasonably + recently and accept the 0-RTT handshake, otherwise if falls back to a full 1-RTT + handshake. This mechanism is tightly coupled with the previous one, it prevents storing an + unlimited number of ClientHellos.</p> + + <p>The current implementation uses a pair of Bloom filters to implement the last two mechanisms. + Bloom filters are fast, memory-efficient, probabilistic data structures that can tell + if an element may be in a set or if it is definitely not in the set.</p> + + <p>If the option <seealso marker="ssl:ssl#type-anti_replay">anti_replay</seealso> + is defined in the server, a pair of Bloom filters (<em>current</em> and + <em>old</em>) are used to record incoming ClientHello messages (it is the unique + binder value that is actually stored). + The <em>current</em> Bloom filter is used for <c>WindowSize</c> seconds to store new + elements. At the end of the time window the Bloom filters are rotated + (the <em>current</em> Bloom filter becomes the <em>old</em> and an empty Bloom filter + is set as <em>current</em>. + </p> + + <p>The Anti-Replay protection feature in statless servers executes in the following steps + when a new ClientHello is received:</p> + <list type="bulleted"> + <item><p>Reported ticket age (obfuscated ticket age) shall be + less than ticket lifetime.</p></item> + <item><p>Actual ticket age shall be less than the ticket lifetime (statless session + tickets contain the servers timestamp when the ticket was issued).</p></item> + <item><p>Ticket shall be used within specified time window (freshness checks).</p></item> + <item><p>If all above checks passed both <em>current</em> and <em>old</em> Bloom filters + are checked to detect if binder was already seen. Being a probabilistic data structure, + false positives can occur and they trigger a full handshake.</p></item> + <item><p>If the binder is not seen, the binder is validated. If the binder is valid, + the server proceeds with the 0-RTT handshake.</p></item> + </list> + + </section> </chapter> diff --git a/lib/ssl/src/dtls_packet_demux.erl b/lib/ssl/src/dtls_packet_demux.erl index d3cbc364bf..cc3efa33ed 100644 --- a/lib/ssl/src/dtls_packet_demux.erl +++ b/lib/ssl/src/dtls_packet_demux.erl @@ -27,12 +27,24 @@ -include_lib("kernel/include/logger.hrl"). %% API --export([start_link/5, active_once/3, accept/2, sockname/1, close/1, - get_all_opts/1, set_all_opts/2, get_sock_opts/2, set_sock_opts/2]). +-export([start_link/5, + active_once/3, + accept/2, + sockname/1, + close/1, + get_all_opts/1, + set_all_opts/2, + get_sock_opts/2, + set_sock_opts/2, + getstat/2]). %% gen_server callbacks --export([init/1, handle_call/3, handle_cast/2, handle_info/2, - terminate/2, code_change/3]). +-export([init/1, + handle_call/3, + handle_cast/2, + handle_info/2, + terminate/2, + code_change/3]). -record(state, {active_n, @@ -71,9 +83,11 @@ get_sock_opts(PacketSocket, SplitSockOpts) -> get_all_opts(PacketSocket) -> call(PacketSocket, get_all_opts). set_sock_opts(PacketSocket, Opts) -> - call(PacketSocket, {set_sock_opts, Opts}). + call(PacketSocket, {set_sock_opts, Opts}). set_all_opts(PacketSocket, Opts) -> call(PacketSocket, {set_all_opts, Opts}). +getstat(PacketSocket, Opts) -> + call(PacketSocket, {getstat, Opts}). %%%=================================================================== %%% gen_server callbacks @@ -146,7 +160,10 @@ handle_call({set_sock_opts, {SocketOpts, NewEmOpts}}, _, #state{listener = Socke {reply, ok, State#state{emulated_options = EmOpts}}; handle_call({set_all_opts, {SocketOpts, NewEmOpts, SslOpts}}, _, #state{listener = Socket} = State) -> set_socket_opts(Socket, SocketOpts), - {reply, ok, State#state{emulated_options = NewEmOpts, dtls_options = SslOpts}}. + {reply, ok, State#state{emulated_options = NewEmOpts, dtls_options = SslOpts}}; +handle_call({getstat, Options}, _, #state{listener = Socket, transport = {TransportCb, _,_,_,_}} = State) -> + Stats = dtls_socket:getstat(TransportCb, Socket, Options), + {reply, Stats, State}. handle_cast({active_once, Client, Pid}, State0) -> State = handle_active_once(Client, Pid, State0), diff --git a/lib/ssl/src/dtls_socket.erl b/lib/ssl/src/dtls_socket.erl index f7b1968064..81d9f3dcca 100644 --- a/lib/ssl/src/dtls_socket.erl +++ b/lib/ssl/src/dtls_socket.erl @@ -22,9 +22,24 @@ -include("ssl_internal.hrl"). -include("ssl_api.hrl"). --export([send/3, listen/2, accept/3, connect/4, socket/4, setopts/3, getopts/3, getstat/3, - peername/2, sockname/2, port/2, close/2]). --export([emulated_options/0, emulated_options/1, internal_inet_values/0, default_inet_values/0, default_cb_info/0]). +-export([send/3, + listen/2, + accept/3, + connect/4, + socket/4, + setopts/3, + getopts/3, + getstat/3, + peername/2, + sockname/2, + port/2, + close/2]). + +-export([emulated_options/0, + emulated_options/1, + internal_inet_values/0, + default_inet_values/0, + default_cb_info/0]). send(Transport, {{IP,Port},Socket}, Data) -> Transport:send(Socket, IP, Port, Data). @@ -81,6 +96,8 @@ connect(Address, Port, #config{transport_info = {Transport, _, _, _, _} = CbInfo Error end. +close(_, dtls) -> + ok; close(gen_udp, {_Client, _Socket}) -> ok; close(Transport, {_Client, Socket}) -> @@ -150,8 +167,12 @@ getopts(gen_udp, {_,Socket}, Options) -> inet:getopts(Socket, Options); getopts(Transport, Socket, Options) -> Transport:getopts(Socket, Options). -getstat(gen_udp, {_,Socket}, Options) -> - inet:getstat(Socket, Options); +getstat(gen_udp, Pid, Options) when is_pid(Pid) -> + dtls_packet_demux:getstat(Pid, Options); +getstat(gen_udp, {_,{_, Socket}}, Options) -> + inet:getstat(Socket, Options); +getstat(gen_udp, Socket, Options) -> + inet:getstat(Socket, Options); getstat(Transport, Socket, Options) -> Transport:getstat(Socket, Options). peername(_, undefined) -> diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index aae79d7625..af6c50f0c1 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -312,7 +312,8 @@ {hibernate_after, hibernate_after()} | {padding_check, padding_check()} | {beast_mitigation, beast_mitigation()} | - {ssl_imp, ssl_imp()}. + {ssl_imp, ssl_imp()} | + {session_tickets, session_tickets()}. -type protocol() :: tls | dtls. -type handshake_completion() :: hello | full. @@ -352,6 +353,17 @@ -type psk_identity() :: string(). -type log_alert() :: boolean(). -type logging_level() :: logger:level(). +-type client_session_tickets() :: disabled | manual | auto. +-type server_session_tickets() :: disabled | stateful | stateless. +-type session_tickets() :: client_session_tickets() | server_session_tickets(). +-type bloom_filter_window_size() :: integer(). +-type bloom_filter_hash_functions() :: integer(). +-type bloom_filter_bits() :: integer(). +-type anti_replay() :: '10k' | '100k' | + {bloom_filter_window_size(), %% number of seconds in time window + bloom_filter_hash_functions(), %% k - number of hash functions + bloom_filter_bits()}. %% m - number of bits in bit vector +-type use_ticket() :: [binary()]. %% ------------------------------------------------------------------------------------------------------- @@ -366,8 +378,10 @@ {srp_identity, client_srp_identity()} | {server_name_indication, sni()} | {customize_hostname_check, customize_hostname_check()} | - {signature_algs, client_signature_algs()} | - {fallback, fallback()}. + {signature_algs, client_signature_algs()} | + {fallback, fallback()} | + {session_tickets, client_session_tickets()} | + {use_ticket, use_ticket()}. -type client_verify_type() :: verify_type(). -type client_reuse_session() :: session_id(). @@ -408,7 +422,9 @@ {honor_cipher_order, honor_cipher_order()} | {honor_ecc_order, honor_ecc_order()} | {client_renegotiation, client_renegotiation()}| - {signature_algs, server_signature_algs()}. + {signature_algs, server_signature_algs()} | + {session_tickets, server_session_tickets()} | + {anti_replay, anti_replay()}. -type server_cacerts() :: [public_key:der_encoded()]. -type server_cafile() :: file:filename(). @@ -553,7 +569,7 @@ listen(_Port, []) -> listen(Port, Options0) -> try {ok, Config} = handle_options(Options0, server), - do_listen(Port, Config, connection_cb(Options0)) + do_listen(Port, Config, Config#config.connection_cb) catch Error = {error, _} -> Error @@ -797,8 +813,10 @@ close(#sslsocket{pid = [TLSPid|_]}, close(#sslsocket{pid = [TLSPid|_]}, Timeout) when is_pid(TLSPid), (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) -> ssl_connection:close(TLSPid, {close, Timeout}); +close(#sslsocket{pid = {dtls = ListenSocket, #config{transport_info={Transport,_,_,_,_}}}}, _) -> + dtls_socket:close(Transport, ListenSocket); close(#sslsocket{pid = {ListenSocket, #config{transport_info={Transport,_,_,_,_}}}}, _) -> - Transport:close(ListenSocket). + tls_socket:close(Transport, ListenSocket). %%-------------------------------------------------------------------- -spec send(SslSocket, Data) -> ok | {error, reason()} when @@ -1243,11 +1261,19 @@ getstat(Socket) -> %% %% Description: Get one or more statistic options for a socket. %%-------------------------------------------------------------------- -getstat(#sslsocket{pid = {Listen, #config{transport_info = {Transport, _, _, _, _}}}}, Options) when is_port(Listen), is_list(Options) -> +getstat(#sslsocket{pid = {dtls, #config{transport_info = {Transport, _, _, _, _}, + dtls_handler = {Listner, _}}}}, + Options) when is_list(Options) -> + dtls_socket:getstat(Transport, Listner, Options); +getstat(#sslsocket{pid = {Listen, #config{transport_info = {Transport, _, _, _, _}}}}, + Options) when is_port(Listen), is_list(Options) -> tls_socket:getstat(Transport, Listen, Options); - -getstat(#sslsocket{pid = [Pid|_], fd = {Transport, Socket, _, _}}, Options) when is_pid(Pid), is_list(Options) -> - tls_socket:getstat(Transport, Socket, Options). +getstat(#sslsocket{pid = [Pid|_], fd = {Transport, Socket, _, _}}, + Options) when is_pid(Pid), is_list(Options) -> + tls_socket:getstat(Transport, Socket, Options); +getstat(#sslsocket{pid = [Pid|_], fd = {Transport, Socket, _}}, + Options) when is_pid(Pid), is_list(Options) -> + dtls_socket:getstat(Transport, Socket, Options). %%--------------------------------------------------------------- -spec shutdown(SslSocket, How) -> ok | {error, reason()} when @@ -1543,7 +1569,19 @@ process_options({[{K0,V} = E|T], S, Counter}, OptionsMap0, Env) -> process_options({T, [E|S], Counter}, OptionsMap0, Env) end. - +handle_option(anti_replay = Option, unbound, OptionsMap, #{rules := Rules}) -> + Value = validate_option(Option, default_value(Option, Rules)), + OptionsMap#{Option => Value}; +handle_option(anti_replay = Option, Value0, + #{session_tickets := SessionTickets} = OptionsMap, #{rules := Rules}) -> + assert_option_dependency(Option, session_tickets, [SessionTickets], [stateless]), + case SessionTickets of + stateless -> + Value = validate_option(Option, Value0), + OptionsMap#{Option => Value}; + _ -> + OptionsMap#{Option => default_value(Option, Rules)} + end; handle_option(cacertfile = Option, unbound, #{cacerts := CaCerts, verify := Verify, verify_fun := VerifyFun} = OptionsMap, _Env) @@ -1631,25 +1669,21 @@ handle_option(reuse_sessions = Option, unbound, OptionsMap, #{rules := Rules}) - handle_option(reuse_sessions = Option, Value0, OptionsMap, _Env) -> Value = validate_option(Option, Value0), OptionsMap#{Option => Value}; -handle_option(anti_replay = Option, unbound, OptionsMap, #{rules := Rules}) -> - Value = validate_option(Option, default_value(Option, Rules)), - OptionsMap#{Option => Value}; -handle_option(anti_replay = Option, Value0, - #{session_tickets := SessionTickets} = OptionsMap, #{rules := Rules}) -> - case SessionTickets of - stateless -> - Value = validate_option(Option, Value0), - OptionsMap#{Option => Value}; - _ -> - OptionsMap#{Option => default_value(Option, Rules)} -end; handle_option(server_name_indication = Option, unbound, OptionsMap, #{host := Host, - role := Role}) -> + role := Role}) -> Value = default_option_role(client, server_name_indication_default(Host), Role), OptionsMap#{Option => Value}; handle_option(server_name_indication = Option, Value0, OptionsMap, _Env) -> Value = validate_option(Option, Value0), OptionsMap#{Option => Value}; +handle_option(session_tickets = Option, unbound, OptionsMap, #{rules := Rules}) -> + Value = validate_option(Option, default_value(Option, Rules)), + OptionsMap#{Option => Value}; +handle_option(session_tickets = Option, Value0, #{versions := Versions} = OptionsMap, #{role := Role}) -> + assert_option_dependency(Option, versions, Versions, ['tlsv1.3']), + assert_role_value(Role, Option, Value0, [disabled, stateful, stateless], [disabled, manual, auto]), + Value = validate_option(Option, Value0), + OptionsMap#{Option => Value}; handle_option(signature_algs = Option, unbound, #{versions := [HighestVersion|_]} = OptionsMap, #{role := Role}) -> Value = handle_hashsigns_option( @@ -1838,6 +1872,45 @@ assert_role(Type, _, Key, _) -> throw({error, {option, Type, Key}}). +assert_role_value(client, Option, Value, _, ClientValues) -> + case lists:member(Value, ClientValues) of + true -> + ok; + false -> + %% throw({error, {option, client, Option, Value, ClientValues}}) + throw({error, {options, role, {Option, {Value, {client, ClientValues}}}}}) + end; +assert_role_value(server, Option, Value, ServerValues, _) -> + case lists:member(Value, ServerValues) of + true -> + ok; + false -> + %% throw({error, {option, server, Option, Value, ServerValues}}) + throw({error, {options, role, {Option, {Value, {server, ServerValues}}}}}) + end. + + +assert_option_dependency(Option, OptionDep, Values0, AllowedValues) -> + %% special handling for version + Values = + case OptionDep of + versions -> + lists:map(fun tls_record:protocol_version/1, Values0); + _ -> + Values0 + end, + Set1 = sets:from_list(Values), + Set2 = sets:from_list(AllowedValues), + case sets:size(sets:intersection(Set1, Set2)) > 0 of + true -> + ok; + false -> + %% Message = build_error_message(Option, OptionDep, AllowedValues), + %% throw({error, {options, Message}}) + throw({error, {options, dependency, {Option, {OptionDep, AllowedValues}}}}) + end. + + validate_option(versions, Versions) -> validate_versions(Versions, Versions); validate_option(verify, Value) @@ -2084,7 +2157,7 @@ validate_option(cb_info, {V1, V2, V3, V4, V5} = Value) when is_atom(V1), validate_option(use_ticket, Value) when is_list(Value) -> Value; validate_option(session_tickets, Value) when Value =:= disabled orelse - Value =:= enabled orelse + Value =:= manual orelse Value =:= auto orelse Value =:= stateless orelse Value =:= stateful -> @@ -2192,7 +2265,7 @@ validate_versions([Ver| _], Versions) -> throw({error, {options, {Ver, {versions, Versions}}}}). tls_validate_versions([], Versions) -> - Versions; + tls_validate_version_gap(Versions); tls_validate_versions([Version | Rest], Versions) when Version == 'tlsv1.3'; Version == 'tlsv1.2'; Version == 'tlsv1.1'; @@ -2202,6 +2275,22 @@ tls_validate_versions([Version | Rest], Versions) when Version == 'tlsv1.3'; tls_validate_versions([Ver| _], Versions) -> throw({error, {options, {Ver, {versions, Versions}}}}). +%% Do not allow configuration of TLS 1.3 with a gap where TLS 1.2 is not supported +%% as that configuration can trigger the built in version downgrade protection +%% mechanism and the handshake can fail with an Illegal Parameter alert. +tls_validate_version_gap(Versions) -> + case lists:member('tlsv1.3', Versions) of + true when length(Versions) >= 2 -> + case lists:member('tlsv1.2', Versions) of + true -> + Versions; + false -> + throw({error, {options, missing_version, {'tlsv1.2', {versions, Versions}}}}) + end; + _ -> + Versions + end. + dtls_validate_versions([], Versions) -> Versions; dtls_validate_versions([Version | Rest], Versions) when Version == 'dtlsv1'; diff --git a/lib/ssl/src/ssl_admin_sup.erl b/lib/ssl/src/ssl_admin_sup.erl index 01ddbb7686..77af5807c4 100644 --- a/lib/ssl/src/ssl_admin_sup.erl +++ b/lib/ssl/src/ssl_admin_sup.erl @@ -89,8 +89,9 @@ session_and_cert_manager_child_spec() -> ticket_store_spec() -> Name = tls_client_ticket_store, - %% TODO do not hardcode storage size and lifetime - StartFunc = {tls_client_ticket_store, start_link, [20,10]}, + Size = client_session_ticket_store_size(), + Lifetime = client_session_ticket_lifetime(), + StartFunc = {tls_client_ticket_store, start_link, [Size,Lifetime]}, Restart = permanent, Shutdown = 4000, Modules = [tls_client_ticket_store], @@ -104,3 +105,21 @@ session_cb_init_args() -> _ -> [] end. + +client_session_ticket_store_size() -> + case application:get_env(ssl, client_session_ticket_store_size) of + {ok, Size} when is_integer(Size) andalso + Size > 0 -> + Size; + _ -> + 1000 + end. + +client_session_ticket_lifetime() -> + case application:get_env(ssl, client_session_ticket_lifetime) of + {ok, Size} when is_integer(Size) andalso + Size > 0 -> + Size; + _ -> + 7200 + end. diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl index c66367919a..ed11cfd985 100644 --- a/lib/ssl/src/tls_connection.erl +++ b/lib/ssl/src/tls_connection.erl @@ -512,7 +512,7 @@ close(downgrade, _,_,_,_) -> ok; %% Other close(_, Socket, Transport, _,_) -> - Transport:close(Socket). + tls_socket:close(Transport, Socket). protocol_name() -> "TLS". @@ -1385,7 +1385,7 @@ handle_new_session_ticket(#new_session_ticket{ticket_nonce = Nonce} = NewSession ssl_options = #{session_tickets := SessionTickets, server_name_indication := SNI}, connection_env = #connection_env{user_application = {_, User}}}) - when SessionTickets =:= enabled -> + when SessionTickets =:= manual -> #{security_parameters := SecParams} = ssl_record:current_connection_state(ConnectionStates, read), HKDF = SecParams#security_parameters.prf_algorithm, diff --git a/lib/ssl/src/tls_connection_1_3.erl b/lib/ssl/src/tls_connection_1_3.erl index 6fb96b1f5a..21c094a5c0 100644 --- a/lib/ssl/src/tls_connection_1_3.erl +++ b/lib/ssl/src/tls_connection_1_3.erl @@ -217,8 +217,6 @@ wait_ee(internal, #change_cipher_spec{}, State, _Module) -> tls_connection:next_event(?FUNCTION_NAME, no_record, State); wait_ee(internal, #encrypted_extensions{} = EE, State0, _Module) -> case tls_handshake_1_3:do_wait_ee(EE, State0) of - #alert{} = Alert -> - ssl_connection:handle_own_alert(Alert, {3,4}, wait_ee, State0); {State1, NextState} -> tls_connection:next_event(NextState, no_record, State1) end; diff --git a/lib/ssl/src/tls_handshake_1_3.erl b/lib/ssl/src/tls_handshake_1_3.erl index 119ed75d36..be1e2a7c31 100644 --- a/lib/ssl/src/tls_handshake_1_3.erl +++ b/lib/ssl/src/tls_handshake_1_3.erl @@ -189,7 +189,7 @@ certificate(OwnCert, CertDbHandle, CertDbRef, _CRContext, Role) -> certificate_request_context = <<>>, certificate_list = CertList}}; {error, Error} when Role =:= server -> - {error, {no_suitable_certificates, Error}}; + {error, ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {no_suitable_certificates, Error})}; {error, _Error} when Role =:= client -> %% The client MUST send a Certificate message if and only if the server %% has requested client authentication via a CertificateRequest message @@ -229,9 +229,8 @@ certificate_verify(PrivateKey, SignatureScheme, algorithm = SignatureScheme, signature = Signature }}; - {error, badarg} -> - {error, badarg} - + {error, #alert{} = Alert} -> + {error, Alert} end. @@ -467,7 +466,7 @@ sign(THash, Context, HashAlgo, #'ECPrivateKey'{} = PrivateKey) -> {ok, Signature} catch error:badarg -> - {error, badarg} + {error, ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, badarg)} end; sign(THash, Context, HashAlgo, PrivateKey) -> Content = build_content(Context, THash), @@ -482,7 +481,7 @@ sign(THash, Context, HashAlgo, PrivateKey) -> {ok, Signature} catch error:badarg -> - {error, badarg} + {error, ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, badarg)} end. @@ -493,7 +492,7 @@ verify(THash, Context, HashAlgo, Signature, {?'id-ecPublicKey', PublicKey, Publi {ok, Result} catch error:badarg -> - {error, badarg} + {error, ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, badarg)} end; verify(THash, Context, HashAlgo, Signature, {?rsaEncryption, PublicKey, _PubKeyParams}) -> Content = build_content(Context, THash), @@ -508,7 +507,7 @@ verify(THash, Context, HashAlgo, Signature, {?rsaEncryption, PublicKey, _PubKeyP {ok, Result} catch error:badarg -> - {error, badarg} + {error, ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, badarg)} end. @@ -606,18 +605,8 @@ do_start(#client_hello{cipher_suites = ClientCiphers, Maybe(session_resumption(NextStateTuple, PSK)) end catch - {Ref, {insufficient_security, no_suitable_groups}} -> - ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_groups); - {Ref, illegal_parameter} -> - ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); - {Ref, no_suitable_cipher} -> - ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_cipher); - {Ref, {insufficient_security, no_suitable_signature_algorithm}} -> - ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, "No suitable signature algorithm"); - {Ref, {insufficient_security, no_suitable_public_key}} -> - ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_public_key); - {Ref, no_application_protocol} -> - ?ALERT_REC(?FATAL, ?NO_APPLICATION_PROTOCOL) + {Ref, #alert{} = Alert} -> + Alert end; %% TLS Client do_start(#server_hello{cipher_suite = SelectedCipherSuite, @@ -696,8 +685,8 @@ do_start(#server_hello{cipher_suite = SelectedCipherSuite, {State, wait_sh} catch - {Ref, {illegal_parameter, Reason}} -> - ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER, Reason) + {Ref, #alert{} = Alert} -> + Alert end. @@ -757,10 +746,8 @@ do_negotiated({start_handshake, PSK0}, {State9, NextState} catch - {Ref, badarg} -> - ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {digitally_sign, badarg}); - {Ref, {no_suitable_certificates, Reason}} -> - ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {no_suitable_certificates, Reason}) + {Ref, #alert{} = Alert} -> + Alert end. @@ -769,17 +756,9 @@ do_wait_cert(#certificate_1_3{} = Certificate, State0) -> try Maybe(process_certificate(Certificate, State0)) catch - {Ref, {certificate_required, State}} -> - {?ALERT_REC(?FATAL, ?CERTIFICATE_REQUIRED, certificate_required), State}; - {Ref, {{certificate_unknown, Reason}, State}} -> - {?ALERT_REC(?FATAL, ?CERTIFICATE_UNKNOWN, Reason), State}; - {Ref, {{internal_error, Reason}, State}} -> - {?ALERT_REC(?FATAL, ?INTERNAL_ERROR, Reason), State}; - {Ref, {{handshake_failure, Reason}, State}} -> - {?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, Reason), State}; + {Ref, #alert{} = Alert} -> + {Alert, State0}; {Ref, {#alert{} = Alert, State}} -> - {Alert, State}; - {#alert{} = Alert, State} -> {Alert, State} end. @@ -790,12 +769,8 @@ do_wait_cv(#certificate_verify_1_3{} = CertificateVerify, State0) -> State1 = Maybe(verify_signature_algorithm(State0, CertificateVerify)), Maybe(verify_certificate_verify(State1, CertificateVerify)) catch - {Ref, {{bad_certificate, Reason}, State}} -> - {?ALERT_REC(?FATAL, ?BAD_CERTIFICATE, {bad_certificate, Reason}), State}; - {Ref, {badarg, State}} -> - {?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {verify, badarg}), State}; - {Ref, {{handshake_failure, Reason}, State}} -> - {?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, {handshake_failure, Reason}), State} + {Ref, {#alert{} = Alert, State}} -> + {Alert, State} end. %% TLS Server @@ -813,11 +788,11 @@ do_wait_finished(#finished{verify_data = VerifyData}, State3 = ssl_record:step_encryption_state(State2), %% Send session ticket - maybe_send_session_ticket(State3, 3) + maybe_send_session_ticket(State3) catch - {Ref, decrypt_error} -> - ?ALERT_REC(?FATAL, ?DECRYPT_ERROR, decrypt_error) + {Ref, #alert{} = Alert} -> + Alert end; %% TLS Client do_wait_finished(#finished{verify_data = _VerifyData}, @@ -846,12 +821,8 @@ do_wait_finished(#finished{verify_data = _VerifyData}, ssl_record:step_encryption_state(State5) catch - {Ref, decrypt_error} -> - ?ALERT_REC(?FATAL, ?DECRYPT_ERROR, decrypt_error); - {Ref, badarg} -> - ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {digitally_sign, badarg}); - {Ref, {no_suitable_certificates, Reason}} -> - ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {no_suitable_certificates, Reason}) + {Ref, #alert{} = Alert} -> + Alert end. @@ -909,16 +880,8 @@ do_wait_sh(#server_hello{cipher_suite = SelectedCipherSuite, catch {Ref, {State, StateName, ServerHello}} -> {State, StateName, ServerHello}; - {Ref, {insufficient_security, no_suitable_groups}} -> - ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_groups); - {Ref, illegal_parameter} -> - ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); - {Ref, no_suitable_cipher} -> - ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_cipher); - {Ref, {insufficient_security, no_suitable_signature_algorithm}} -> - ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, "No suitable signature algorithm"); - {Ref, {insufficient_security, no_suitable_public_key}} -> - ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_public_key) + {Ref, #alert{} = Alert} -> + Alert end. @@ -939,16 +902,6 @@ do_wait_ee(#encrypted_extensions{extensions = Extensions}, State0) -> {State1, wait_cert_cr} catch - {Ref, {insufficient_security, no_suitable_groups}} -> - ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_groups); - {Ref, illegal_parameter} -> - ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); - {Ref, no_suitable_cipher} -> - ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_cipher); - {Ref, {insufficient_security, no_suitable_signature_algorithm}} -> - ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, "No suitable signature algorithm"); - {Ref, {insufficient_security, no_suitable_public_key}} -> - ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_public_key); {Ref, {State, StateName}} -> {State, StateName} end. @@ -959,14 +912,8 @@ do_wait_cert_cr(#certificate_1_3{} = Certificate, State0) -> try Maybe(process_certificate(Certificate, State0)) catch - {Ref, {certificate_required, _State}} -> - ?ALERT_REC(?FATAL, ?CERTIFICATE_REQUIRED, certificate_required); - {Ref, {{certificate_unknown, Reason}, _State}} -> - ?ALERT_REC(?FATAL, ?CERTIFICATE_UNKNOWN, Reason); - {Ref, {{internal_error, Reason}, _State}} -> - ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, Reason); - {Ref, {{handshake_failure, Reason}, _State}} -> - ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, Reason); + {Ref, #alert{} = Alert} -> + {Alert, State0}; {Ref, {#alert{} = Alert, State}} -> {Alert, State} end; @@ -975,30 +922,11 @@ do_wait_cert_cr(#certificate_request_1_3{} = CertificateRequest, State0) -> try Maybe(process_certificate_request(CertificateRequest, State0)) catch - {Ref, {certificate_required, _State}} -> - ?ALERT_REC(?FATAL, ?CERTIFICATE_REQUIRED, certificate_required); - {Ref, {{certificate_unknown, Reason}, _State}} -> - ?ALERT_REC(?FATAL, ?CERTIFICATE_UNKNOWN, Reason); - {Ref, {illegal_parameter, Reason}} -> - ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER, Reason); - {Ref, {{internal_error, Reason}, _State}} -> - ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, Reason); - {Ref, {{handshake_failure, Reason}, _State}} -> - ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, Reason) + {Ref, #alert{} = Alert} -> + {Alert, State0} end. - -%% TODO: Remove this function! -%% not_implemented(State, Reason) -> -%% {error, {not_implemented, State, Reason}}. - -%% not_implemented(update_secrets, State0, Reason) -> -%% State1 = calculate_traffic_secrets(State0), -%% State = ssl_record:step_encryption_state(State1), -%% {error, {not_implemented, State, Reason}}. - - %% For reasons of backward compatibility with middleboxes (see %% Appendix D.4), the HelloRetryRequest message uses the same structure %% as the ServerHello, but with Random set to the special value of the @@ -1056,8 +984,8 @@ maybe_queue_cert_cert_cv(#state{connection_states = _ConnectionStates0, State = Maybe(maybe_queue_cert_verify(Certificate, State1)), {ok, State} catch - {Ref, badarg} -> - {error, badarg} + {Ref, #alert{} = Alert} -> + {error, Alert} end. @@ -1076,8 +1004,8 @@ maybe_queue_cert_verify(_Certificate, CertificateVerify = Maybe(certificate_verify(CertPrivateKey, SignatureScheme, State, client)), {ok, tls_connection:queue_handshake(CertificateVerify, State)} catch - {Ref, badarg} -> - {error, badarg} + {Ref, #alert{} = Alert} -> + {error, Alert} end. @@ -1104,7 +1032,7 @@ validate_client_finished(#state{connection_states = ConnectionStates, compare_verify_data(Data, Data) -> ok; compare_verify_data(_, _) -> - {error, decrypt_error}. + {error, ?ALERT_REC(?FATAL, ?DECRYPT_ERROR, decrypt_error)}. send_hello_retry_request(#state{connection_states = ConnectionStates0} = State0, @@ -1170,6 +1098,16 @@ maybe_send_certificate_verify(#state{session = #session{sign_alg = SignatureSche end. +maybe_send_session_ticket(State) -> + Number = case application:get_env(ssl, server_session_tickets_amount) of + {ok, Size} when is_integer(Size) andalso + Size > 0 -> + Size; + _ -> + 3 + end, + maybe_send_session_ticket(State, Number). +%% maybe_send_session_ticket(#state{ssl_options = #{session_tickets := disabled}} = State, _) -> %% Do nothing! State; @@ -1229,7 +1167,7 @@ process_certificate(#certificate_1_3{ %% secrets. State1 = calculate_traffic_secrets(State0), State = ssl_record:step_encryption_state(State1), - {error, {certificate_required, State}}; + {error, {?ALERT_REC(?FATAL, ?CERTIFICATE_REQUIRED, certificate_required), State}}; process_certificate(#certificate_1_3{certificate_list = Certs0}, #state{ssl_options = #{signature_algs := SignAlgs, @@ -1261,8 +1199,8 @@ process_certificate(#certificate_1_3{certificate_list = Certs0}, false -> State1 = calculate_traffic_secrets(State0), State = ssl_record:step_encryption_state(State1), - {error, {{handshake_failure, - "Client certificate uses unsupported signature algorithm"}, State}} + {error, {?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, + "Client certificate uses unsupported signature algorithm"), State}} end. @@ -1326,9 +1264,9 @@ validate_certificate_chain(Certs, CertDbHandle, CertDbRef, catch error:{badmatch,{error, {asn1, Asn1Reason}}} -> %% ASN-1 decode of certificate somehow failed - {error, {certificate_unknown, {failed_to_decode_certificate, Asn1Reason}}}; + {error, ?ALERT_REC(?FATAL, ?CERTIFICATE_UNKNOWN, {failed_to_decode_certificate, Asn1Reason})}; error:OtherReason -> - {error, {internal_error, {unexpected_error, OtherReason}}} + {error, ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {unexpected_error, OtherReason})} end. @@ -1435,13 +1373,13 @@ get_pre_shared_key(undefined, _, HKDFAlgo, _) -> get_pre_shared_key(_, undefined, HKDFAlgo, _) -> {ok, binary:copy(<<0>>, ssl_cipher:hash_size(HKDFAlgo))}; %% Session resumption -get_pre_shared_key(enabled = SessionTickets, UseTicket, HKDFAlgo, SelectedIdentity) -> +get_pre_shared_key(manual = SessionTickets, UseTicket, HKDFAlgo, SelectedIdentity) -> TicketData = get_ticket_data(self(), SessionTickets, UseTicket), case choose_psk(TicketData, SelectedIdentity) of undefined -> %% full handshake, default PSK {ok, binary:copy(<<0>>, ssl_cipher:hash_size(HKDFAlgo))}; illegal_parameter -> - {error, illegal_parameter}; + {error, ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)}; {_, PSK} -> {ok, PSK} end; @@ -1453,7 +1391,7 @@ get_pre_shared_key(auto = SessionTickets, UseTicket, HKDFAlgo, SelectedIdentity) {ok, binary:copy(<<0>>, ssl_cipher:hash_size(HKDFAlgo))}; illegal_parameter -> tls_client_ticket_store:unlock_tickets(self(), UseTicket), - {error, illegal_parameter}; + {error, ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)}; {Key, PSK} -> tls_client_ticket_store:remove_tickets([Key]), %% Remove single-use ticket tls_client_ticket_store:unlock_tickets(self(), UseTicket -- [Key]), @@ -1741,8 +1679,8 @@ verify_signature_algorithm(#state{ false -> State1 = calculate_traffic_secrets(State0), State = ssl_record:step_encryption_state(State1), - {error, {{handshake_failure, - "CertificateVerify uses unsupported signature algorithm"}, State}} + {error, {?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, + "CertificateVerify uses unsupported signature algorithm"), State}} end. @@ -1786,11 +1724,12 @@ verify_certificate_verify(#state{ {ok, false} -> State1 = calculate_traffic_secrets(State0), State = ssl_record:step_encryption_state(State1), - {error, {{handshake_failure, "Failed to verify CertificateVerify"}, State}}; - {error, badarg} -> + {error, {?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, + "Failed to verify CertificateVerify"), State}}; + {error, #alert{} = Alert} -> State1 = calculate_traffic_secrets(State0), State = ssl_record:step_encryption_state(State1), - {error, {badarg, State}} + {error, {Alert, State}} end. @@ -1812,7 +1751,7 @@ peer_context_string(client) -> %% server MUST abort the handshake with a "handshake_failure" or an %% "insufficient_security" alert. select_common_groups(_, []) -> - {error, {insufficient_security, no_suitable_groups}}; + {error, ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_groups)}; select_common_groups(ServerGroups, ClientGroups) -> Fun = fun(E) -> lists:member(E, ClientGroups) end, case lists:filter(Fun, ServerGroups) of @@ -1843,7 +1782,7 @@ select_common_groups(ServerGroups, ClientGroups) -> validate_client_key_share(_ ,[]) -> ok; validate_client_key_share([], _) -> - {error, illegal_parameter}; + {error, ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)}; validate_client_key_share([G|ClientGroups], [{_, G, _}|ClientShares]) -> validate_client_key_share(ClientGroups, ClientShares); validate_client_key_share([_|ClientGroups], [_|_] = ClientShares) -> @@ -1851,6 +1790,8 @@ validate_client_key_share([_|ClientGroups], [_|_] = ClientShares) -> %% Verify that selected group is offered by the client. +validate_server_key_share([], _) -> + {error, ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)}; validate_server_key_share([G|_ClientGroups], {_, G, _}) -> ok; validate_server_key_share([_|ClientGroups], {_, _, _} = ServerKeyShare) -> @@ -1858,17 +1799,17 @@ validate_server_key_share([_|ClientGroups], {_, _, _} = ServerKeyShare) -> validate_selected_group(SelectedGroup, [SelectedGroup|_]) -> - {error, {illegal_parameter, - "Selected group sent by the server shall not correspond to a group" - " which was provided in the key_share extension"}}; + {error, ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER, + "Selected group sent by the server shall not correspond to a group" + " which was provided in the key_share extension")}; validate_selected_group(SelectedGroup, ClientGroups) -> case lists:member(SelectedGroup, ClientGroups) of true -> ok; false -> - {error, {illegal_parameter, - "Selected group sent by the server shall correspond to a group" - " which was provided in the supported_groups extension"}} + {error, ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER, + "Selected group sent by the server shall correspond to a group" + " which was provided in the supported_groups extension")} end. @@ -1920,7 +1861,7 @@ get_server_public_key({key_share_entry, Group, PublicKey}) -> handle_alpn(undefined, _) -> {ok, undefined}; handle_alpn([], _) -> - {error, no_application_protocol}; + {error, ?ALERT_REC(?FATAL, ?NO_APPLICATION_PROTOCOL)}; handle_alpn([_|_], undefined) -> {ok, undefined}; handle_alpn([ServerProtocol|T], ClientProtocols) -> @@ -1933,7 +1874,7 @@ handle_alpn([ServerProtocol|T], ClientProtocols) -> select_cipher_suite(_, [], _) -> - {error, no_suitable_cipher}; + {error, ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_cipher)}; %% If honor_cipher_order is set to true, use the server's preference for %% cipher suite selection. select_cipher_suite(true, ClientCiphers, ServerCiphers) -> @@ -1956,7 +1897,7 @@ validate_cipher_suite(Cipher, ClientCiphers) -> true -> ok; false -> - {error, illegal_parameter} + {error, ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)} end. @@ -1982,9 +1923,9 @@ check_cert_sign_algo(SignAlgo, SignHash, _, ClientSignAlgsCert) -> %% DSA keys are not supported by TLS 1.3 select_sign_algo(dsa, _ClientSignAlgs, _ServerSignAlgs) -> - {error, {insufficient_security, no_suitable_public_key}}; + {error, ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_public_key)}; select_sign_algo(_, [], _) -> - {error, {insufficient_security, no_suitable_signature_algorithm}}; + {error, ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_signature_algorithm)}; select_sign_algo(PublicKeyAlgo, [C|ClientSignAlgs], ServerSignAlgs) -> {_, S, _} = ssl_cipher:scheme_to_components(C), %% RSASSA-PKCS1-v1_5 and Legacy algorithms are not defined for use in signed @@ -2007,7 +1948,7 @@ select_sign_algo(PublicKeyAlgo, [C|ClientSignAlgs], ServerSignAlgs) -> do_check_cert_sign_algo(_, _, []) -> - {error, {insufficient_security, no_suitable_signature_algorithm}}; + {error, ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_signature_algorithm)}; do_check_cert_sign_algo(SignAlgo, SignHash, [Scheme|T]) -> {Hash, Sign, _Curve} = ssl_cipher:scheme_to_components(Scheme), case compare_sign_algos(SignAlgo, SignHash, Sign, Hash) of @@ -2285,7 +2226,7 @@ get_ticket_data(_, undefined, _) -> undefined; get_ticket_data(_, _, undefined) -> undefined; -get_ticket_data(_, enabled, UseTicket) -> +get_ticket_data(_, manual, UseTicket) -> process_user_tickets(UseTicket); get_ticket_data(Pid, auto, UseTicket) -> tls_client_ticket_store:get_tickets(Pid, UseTicket). diff --git a/lib/ssl/src/tls_server_session_ticket.erl b/lib/ssl/src/tls_server_session_ticket.erl index e30205f4b8..fc3904366e 100644 --- a/lib/ssl/src/tls_server_session_ticket.erl +++ b/lib/ssl/src/tls_server_session_ticket.erl @@ -31,7 +31,7 @@ -include("ssl_cipher.hrl"). %% API --export([start_link/3, +-export([start_link/4, new/3, use/4 ]). @@ -52,12 +52,12 @@ %%%=================================================================== %%% API %%%=================================================================== --spec start_link(atom(), integer(), tuple()) -> {ok, Pid :: pid()} | +-spec start_link(atom(), integer(), integer(), tuple()) -> {ok, Pid :: pid()} | {error, Error :: {already_started, pid()}} | {error, Error :: term()} | ignore. -start_link(Mode, Lifetime, AntiReplay) -> - gen_server:start_link(?MODULE, [Mode, Lifetime, AntiReplay], []). +start_link(Mode, Lifetime, TicketStoreSize, AntiReplay) -> + gen_server:start_link(?MODULE, [Mode, Lifetime, TicketStoreSize, AntiReplay], []). new(Pid, Prf, MasterSecret) -> gen_server:call(Pid, {new_session_ticket, Prf, MasterSecret}, infinity). @@ -142,14 +142,14 @@ format_status(_Opt, Status) -> %%% Internal functions %%%=================================================================== -inital_state([stateless, Lifetime, undefined]) -> +inital_state([stateless, Lifetime, _, undefined]) -> #state{nonce = 0, stateless = #{seed => {crypto:strong_rand_bytes(16), crypto:strong_rand_bytes(32)}, window => undefined}, lifetime = Lifetime }; -inital_state([stateless, Lifetime, {Window, K, M}]) -> +inital_state([stateless, Lifetime, _, {Window, K, M}]) -> erlang:send_after(Window * 1000, self(), rotate_bloom_filters), #state{nonce = 0, stateless = #{bloom_filter => tls_bloom_filter:new(K, M), @@ -158,14 +158,14 @@ inital_state([stateless, Lifetime, {Window, K, M}]) -> window => Window}, lifetime = Lifetime }; -inital_state([stateful, Lifetime|_]) -> +inital_state([stateful, Lifetime, TicketStoreSize|_]) -> %% statfeful servers replay %% protection is that it saves %% all valid tickets #state{lifetime = Lifetime, nonce = 0, stateful = #{db => stateful_store(), - max => 1000, + max => TicketStoreSize, ref_index => #{} } }. diff --git a/lib/ssl/src/tls_socket.erl b/lib/ssl/src/tls_socket.erl index 031665bfe8..7d022f9bf3 100644 --- a/lib/ssl/src/tls_socket.erl +++ b/lib/ssl/src/tls_socket.erl @@ -35,7 +35,8 @@ getstat/3, peername/2, sockname/2, - port/2]). + port/2, + close/2]). -export([split_options/1, get_socket_opts/3]). @@ -77,8 +78,9 @@ listen(Transport, Port, #config{transport_info = {Transport, _, _, _, _}, case Transport:listen(Port, Options ++ internal_inet_values()) of {ok, ListenSocket} -> {ok, Tracker} = inherit_tracker(ListenSocket, EmOpts, SslOpts), - %% TODO not hard code - {ok, SessionHandler} = session_tickets_tracker(7200, SslOpts), + LifeTime = get_ticket_lifetime(), + TicketStoreSize = get_ticket_store_size(), + {ok, SessionHandler} = session_tickets_tracker(LifeTime, TicketStoreSize, SslOpts), Trackers = [{option_tracker, Tracker}, {session_tickets_tracker, SessionHandler}], Socket = #sslsocket{pid = {ListenSocket, Config#config{trackers = Trackers}}}, check_active_n(EmOpts, Socket), @@ -232,6 +234,11 @@ port(gen_tcp, Socket) -> port(Transport, Socket) -> Transport:port(Socket). +close(gen_tcp, Socket) -> + inet:close(Socket); +close(Transport, Socket) -> + Transport:close(Socket). + emulated_options() -> [mode, packet, active, header, packet_size]. @@ -249,15 +256,16 @@ inherit_tracker(ListenSocket, EmOpts, #{erl_dist := false} = SslOpts) -> inherit_tracker(ListenSocket, EmOpts, #{erl_dist := true} = SslOpts) -> ssl_listen_tracker_sup:start_child_dist([ListenSocket, EmOpts, SslOpts]). -session_tickets_tracker(_, #{erl_dist := false, - session_tickets := disabled}) -> +session_tickets_tracker(_, _, #{erl_dist := false, + session_tickets := disabled}) -> {ok, disabled}; -session_tickets_tracker(Lifetime, #{erl_dist := false, +session_tickets_tracker(Lifetime, TicketStoreSize, #{erl_dist := false, session_tickets := Mode, anti_replay := AntiReplay}) -> - tls_server_session_ticket_sup:start_child([Mode, Lifetime, AntiReplay]); -session_tickets_tracker(Lifetime, #{erl_dist := true, session_tickets := Mode}) -> - tls_server_session_ticket_sup:start_child_dist([Mode, Lifetime]). + tls_server_session_ticket_sup:start_child([Mode, Lifetime, TicketStoreSize, AntiReplay]); +session_tickets_tracker(Lifetime, TicketStoreSize, #{erl_dist := true, + session_tickets := Mode}) -> + tls_server_session_ticket_sup:start_child_dist([Mode, Lifetime, TicketStoreSize]). get_emulated_opts(TrackerPid) -> @@ -463,3 +471,20 @@ validate_inet_option(active, Value) throw({error, {options, {active,Value}}}); validate_inet_option(_, _) -> ok. + +get_ticket_lifetime() -> + case application:get_env(ssl, server_session_ticket_lifetime) of + {ok, Seconds} when is_integer(Seconds) andalso + Seconds =< 604800 -> %% MUST be less than 7 days + Seconds; + _ -> + 7200 %% Default 2 hours + end. + +get_ticket_store_size() -> + case application:get_env(ssl, server_session_ticket_store_size) of + {ok, Size} when is_integer(Size) -> + Size; + _ -> + 1000 + end. diff --git a/lib/ssl/test/dtls_api_SUITE.erl b/lib/ssl/test/dtls_api_SUITE.erl index 37130abcbf..a89e754219 100644 --- a/lib/ssl/test/dtls_api_SUITE.erl +++ b/lib/ssl/test/dtls_api_SUITE.erl @@ -38,7 +38,8 @@ groups() -> api_tests() -> [ - dtls_listen_owner_dies + dtls_listen_owner_dies, + dtls_listen_close ]. init_per_suite(Config0) -> @@ -113,7 +114,7 @@ dtls_listen_owner_dies(Config) when is_list(Config) -> {ok, LSocket} = ssl:listen(Port, [{protocol, dtls} | ServerOpts]), spawn(fun() -> {ok, ASocket} = ssl:transport_accept(LSocket), - Result0 = ssl:handshake(ASocket), + _ = ssl:handshake(ASocket), receive {ssl, Socket, "from client"} -> ssl:send(Socket, "from server"), @@ -129,3 +130,18 @@ dtls_listen_owner_dies(Config) when is_list(Config) -> end. +dtls_listen_close() -> + [{doc, "Test that you close a DTLS 'listner' socket"}]. + +dtls_listen_close(Config) when is_list(Config) -> + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {_, ServerNode, _Hostname} = ssl_test_lib:run_where(Config), + + Port = ssl_test_lib:inet_port(ServerNode), + {ok, ListenSocket} = ssl:listen(Port, [{protocol, dtls} | ServerOpts]), + ok = ssl:close(ListenSocket). + +%%-------------------------------------------------------------------- +%% Internal functions ------------------------------------------------ +%%-------------------------------------------------------------------- + diff --git a/lib/ssl/test/ssl_api_SUITE.erl b/lib/ssl/test/ssl_api_SUITE.erl index d2fe83a58e..b393ba2f9b 100644 --- a/lib/ssl/test/ssl_api_SUITE.erl +++ b/lib/ssl/test/ssl_api_SUITE.erl @@ -109,7 +109,8 @@ gen_api_tests() -> options_not_proplist, invalid_options, cb_info, - log_alert + log_alert, + getstat ]. handshake_paus_tests() -> @@ -135,7 +136,13 @@ tls13_group() -> [ supported_groups, honor_server_cipher_order_tls13, - honor_client_cipher_order_tls13 + honor_client_cipher_order_tls13, + client_options_negative_version_gap, + client_options_negative_dependency_version, + client_options_negative_dependency_stateless, + client_options_negative_dependency_role, + server_options_negative_version_gap, + server_options_negative_dependency_role ]. @@ -517,7 +524,7 @@ handshake_continue_tls13_client(Config) when is_list(Config) -> {from, self()}, {mfa, {ssl_test_lib, send_recv_result_active, []}}, {options, ssl_test_lib:ssl_options([{handshake, hello}, - {session_tickets, enabled}, + {session_tickets, manual}, {use_ticket, [DummyTicket]}, {versions, ['tlsv1.3', 'tlsv1.2', @@ -1747,6 +1754,60 @@ honor_client_cipher_order_tls13(Config) when is_list(Config) -> prf => sha384}). %%-------------------------------------------------------------------- +client_options_negative_version_gap() -> + [{doc,"Test client options with faulty version gap."}]. +client_options_negative_version_gap(Config) when is_list(Config) -> + start_client_negative(Config, [{versions, ['tlsv1', 'tlsv1.3']}], + {options, missing_version, + {'tlsv1.2', {versions,[tlsv1, 'tlsv1.3']}}}). + +%%-------------------------------------------------------------------- +client_options_negative_dependency_version() -> + [{doc,"Test client options with faulty version dependency."}]. +client_options_negative_dependency_version(Config) when is_list(Config) -> + start_client_negative(Config, [{versions, ['tlsv1.1', 'tlsv1.2']}, + {session_tickets, manual}], + {options,dependency, + {session_tickets,{versions,['tlsv1.3']}}}). + +%%-------------------------------------------------------------------- +client_options_negative_dependency_stateless() -> + [{doc,"Test client options with faulty 'session_tickets' option."}]. +client_options_negative_dependency_stateless(Config) when is_list(Config) -> + start_client_negative(Config, [{versions, ['tlsv1.2', 'tlsv1.3']}, + {anti_replay, '10k'}, + {session_tickets, manual}], + {options,dependency, + {anti_replay,{session_tickets,[stateless]}}}). + + +%%-------------------------------------------------------------------- +client_options_negative_dependency_role() -> + [{doc,"Test client options with faulty role."}]. +client_options_negative_dependency_role(Config) when is_list(Config) -> + start_client_negative(Config, [{versions, ['tlsv1.2', 'tlsv1.3']}, + {session_tickets, stateless}], + {options,role, + {session_tickets,{stateless,{client,[disabled,manual,auto]}}}}). + +%%-------------------------------------------------------------------- +server_options_negative_version_gap() -> + [{doc,"Test server options with faulty version gap."}]. +server_options_negative_version_gap(Config) when is_list(Config) -> + start_server_negative(Config, [{versions, ['tlsv1', 'tlsv1.3']}], + {options, missing_version, + {'tlsv1.2', {versions,[tlsv1, 'tlsv1.3']}}}). + +%%-------------------------------------------------------------------- +server_options_negative_dependency_role() -> + [{doc,"Test server options with faulty role."}]. +server_options_negative_dependency_role(Config) when is_list(Config) -> + start_server_negative(Config, [{versions, ['tlsv1.2', 'tlsv1.3']}, + {session_tickets, manual}], + {options,role, + {session_tickets,{manual,{server,[disabled,stateful,stateless]}}}}). + +%%-------------------------------------------------------------------- honor_server_cipher_order_tls13() -> [{doc,"Test API honor server cipher order in TLS 1.3."}]. honor_server_cipher_order_tls13(Config) when is_list(Config) -> @@ -1770,6 +1831,31 @@ honor_server_cipher_order_tls13(Config) when is_list(Config) -> cipher => aes_128_gcm, mac => aead, prf => sha256}). +%%-------------------------------------------------------------------- +getstat() -> + [{doc, "Test that you use ssl:getstat on an TLS socket"}]. + +getstat(Config) when is_list(Config) -> + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Port0 = ssl_test_lib:inet_port(ServerNode), + {ok, ListenSocket} = ssl:listen(Port0, [ServerOpts]), + {ok, _} = ssl:getstat(ListenSocket), + ssl:close(ListenSocket), + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, ssl_getstat, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, ssl_getstat, []}}, + {options, ClientOpts}]), + ssl_test_lib:check_result(Server, ok, Client, ok). %%-------------------------------------------------------------------- @@ -2084,6 +2170,28 @@ honor_cipher_order(Config, Honor, ServerCiphers, ClientCiphers, Expected) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). +start_client_negative(Config, Options, Error) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, 0}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, connection_info_result, []}}, + {options, Options ++ ClientOpts}]), + ct:pal("Actual: ~p~nExpected: ~p", [Client, {connect_failed, Error}]), + {connect_failed, Error} = Client. + +start_server_negative(Config, Options, Error) -> + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {_, ServerNode, _} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, connection_info_result, []}}, + {options, Options ++ ServerOpts}]), + + ct:pal("Actual: ~p~nExpected: ~p", [Server,Error]), + Error = Server. + connection_info_result(Socket) -> {ok, Info} = ssl:connection_information(Socket, [protocol, selected_cipher_suite]), {ok, {proplists:get_value(protocol, Info), proplists:get_value(selected_cipher_suite, Info)}}. @@ -2102,3 +2210,18 @@ der_input_opts(Opts) -> CaCert end, ssl_test_lib:pem_to_der(CaCertsfile)), {Cert, {Asn1Type, Key}, CaCerts, DHParams}. + +ssl_getstat(Socket) -> + ssl:send(Socket, "From Erlang to Erlang"), + {ok, Stats} = ssl:getstat(Socket), + List = lists:dropwhile(fun({_, 0}) -> + true; + ({_, _}) -> + false + end, Stats), + case List of + [] -> + nok; + _ -> + ok + end. diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index 355cd31070..fbe2d4f6b0 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -439,11 +439,11 @@ tls_versions_option(Config) when is_list(Config) -> ssl_test_lib:check_result(Server, ok, Client, ok), Server ! listen, - + Versions = remove_supported_versions(Available, Supported), ErrClient = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, - {options, [{versions , Available -- Supported} | ClientOpts]}]), + {options, [{versions , Versions} | ClientOpts]}]), receive {Server, _} -> ok @@ -515,3 +515,14 @@ version_option_test(Config, Version) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). +remove_supported_versions(Available, Supported) -> + Versions0 = Available -- Supported, + case lists:member('tlsv1.3', Versions0) andalso + not lists:member('tlsv1.2', Versions0) of + true -> + %% If 'tlsv1.2' is removed, remove also 'tlsv1.3' + Versions0 -- ['tlsv1.3']; + _ -> + Versions0 + end. + diff --git a/lib/ssl/test/ssl_packet_SUITE.erl b/lib/ssl/test/ssl_packet_SUITE.erl index b71be64787..e6b4108fb2 100644 --- a/lib/ssl/test/ssl_packet_SUITE.erl +++ b/lib/ssl/test/ssl_packet_SUITE.erl @@ -2009,7 +2009,8 @@ packet(Config, Data, Send, Recv, Quantity, Packet, Active) -> Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, {from, self()}, {mfa, {?MODULE, Send ,[Data, Quantity]}}, - {options, [{packet, Packet}, {nodelay, true}| ServerOpts]}]), + {options, [{packet, Packet}, {nodelay, true}| ServerOpts] + ++ ssl_test_lib:bigger_buffers()}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, {host, Hostname}, @@ -2018,7 +2019,7 @@ packet(Config, Data, Send, Recv, Quantity, Packet, Active) -> {options, [{active, Active}, {nodelay, true}, {packet, Packet} | - ClientOpts]}]), + ClientOpts] ++ ssl_test_lib:bigger_buffers()}]), ssl_test_lib:check_result(Client, ok), diff --git a/lib/ssl/test/ssl_payload_SUITE.erl b/lib/ssl/test/ssl_payload_SUITE.erl index 91043408b7..f9241ee2a2 100644 --- a/lib/ssl/test/ssl_payload_SUITE.erl +++ b/lib/ssl/test/ssl_payload_SUITE.erl @@ -27,6 +27,7 @@ -define(TIMEOUT, 600000). + %%-------------------------------------------------------------------- %% Common Test interface functions ----------------------------------- %%-------------------------------------------------------------------- @@ -523,7 +524,7 @@ server_echos_passive( [{node, ServerNode}, {port, 0}, {from, self()}, {mfa, {?MODULE, echoer, [Length]}}, - {options, [{active, false}, {mode, binary} | ServerOpts]}]), + {options, [{active, false}, {mode, binary} | ServerOpts] ++ ssl_test_lib:bigger_buffers()}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client( @@ -531,7 +532,7 @@ server_echos_passive( {host, Hostname}, {from, self()}, {mfa, {?MODULE, sender, [Data]}}, - {options, [{active, false}, {mode, binary} | ClientOpts]}]), + {options, [{active, false}, {mode, binary} | ClientOpts] ++ ssl_test_lib:bigger_buffers() }]), %% ssl_test_lib:check_result(Server, ok, Client, ok), %% @@ -569,7 +570,7 @@ server_echos_active_once( [{node, ServerNode}, {port, 0}, {from, self()}, {mfa, {?MODULE, echoer_active_once, [Length]}}, - {options, [{active, once}, {mode, binary} | ServerOpts]}]), + {options, [{active, once}, {mode, binary} | ServerOpts] ++ ssl_test_lib:bigger_buffers()}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client( @@ -577,7 +578,7 @@ server_echos_active_once( {host, Hostname}, {from, self()}, {mfa, {?MODULE, sender_active_once, [Data]}}, - {options, [{active, once}, {mode, binary} | ClientOpts]}]), + {options, [{active, once}, {mode, binary} | ClientOpts] ++ ssl_test_lib:bigger_buffers() }]), %% ssl_test_lib:check_result(Server, ok, Client, ok), %% @@ -593,7 +594,7 @@ server_echos_active( [{node, ServerNode}, {port, 0}, {from, self()}, {mfa, {?MODULE, echoer_active, [Length]}}, - {options, [{active, true}, {mode, binary} | ServerOpts]}]), + {options, [{active, true}, {mode, binary} | ServerOpts] ++ ssl_test_lib:bigger_buffers()}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client( @@ -601,7 +602,7 @@ server_echos_active( {host, Hostname}, {from, self()}, {mfa, {?MODULE, sender_active, [Data]}}, - {options, [{active, true}, {mode, binary} | ClientOpts]}]), + {options, [{active, true}, {mode, binary} | ClientOpts] ++ ssl_test_lib:bigger_buffers()}]), %% ssl_test_lib:check_result(Server, ok, Client, ok), %% @@ -616,7 +617,7 @@ client_echos_passive( [{node, ServerNode}, {port, 0}, {from, self()}, {mfa, {?MODULE, sender, [Data]}}, - {options, [{active, false}, {mode, binary} | ServerOpts]}]), + {options, [{active, false}, {mode, binary} | ServerOpts] ++ ssl_test_lib:bigger_buffers()}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client( @@ -624,7 +625,7 @@ client_echos_passive( {host, Hostname}, {from, self()}, {mfa, {?MODULE, echoer, [Length]}}, - {options, [{active, false}, {mode, binary} | ClientOpts]}]), + {options, [{active, false}, {mode, binary} | ClientOpts] ++ ssl_test_lib:bigger_buffers()}]), %% ssl_test_lib:check_result(Server, ok, Client, ok), %% @@ -640,7 +641,7 @@ client_echos_passive_chunk( [{node, ServerNode}, {port, 0}, {from, self()}, {mfa, {?MODULE, sender, [Data]}}, - {options, [{active, false}, {mode, binary} | ServerOpts]}]), + {options, [{active, false}, {mode, binary} | ServerOpts] ++ ssl_test_lib:bigger_buffers()}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client( @@ -648,7 +649,7 @@ client_echos_passive_chunk( {host, Hostname}, {from, self()}, {mfa, {?MODULE, echoer_chunk, [Length]}}, - {options, [{active, false}, {mode, binary} | ClientOpts]}]), + {options, [{active, false}, {mode, binary} | ClientOpts] ++ ssl_test_lib:bigger_buffers()}]), %% ssl_test_lib:check_result(Server, ok, Client, ok), %% @@ -664,7 +665,7 @@ client_echos_active_once( [{node, ServerNode}, {port, 0}, {from, self()}, {mfa, {?MODULE, sender_active_once, [Data]}}, - {options, [{active, once}, {mode, binary} | ServerOpts]}]), + {options, [{active, once}, {mode, binary} | ServerOpts] ++ ssl_test_lib:bigger_buffers()}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client( @@ -672,7 +673,7 @@ client_echos_active_once( {host, Hostname}, {from, self()}, {mfa, {?MODULE, echoer_active_once, [Length]}}, - {options,[{active, once}, {mode, binary} | ClientOpts]}]), + {options,[{active, once}, {mode, binary} | ClientOpts] ++ ssl_test_lib:bigger_buffers()}]), %% ssl_test_lib:check_result(Server, ok, Client, ok), %% @@ -687,7 +688,7 @@ client_echos_active( [{node, ServerNode}, {port, 0}, {from, self()}, {mfa, {?MODULE, sender_active, [Data]}}, - {options, [{active, true}, {mode, binary} | ServerOpts]}]), + {options, [{active, true}, {mode, binary} | ServerOpts] ++ ssl_test_lib:bigger_buffers()}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client( @@ -695,7 +696,7 @@ client_echos_active( {host, Hostname}, {from, self()}, {mfa, {?MODULE, echoer_active, [Length]}}, - {options, [{active, true}, {mode, binary} | ClientOpts]}]), + {options, [{active, true}, {mode, binary} | ClientOpts] ++ ssl_test_lib:bigger_buffers()}]), % ssl_test_lib:check_result(Server, ok, Client, ok), %% @@ -811,3 +812,4 @@ echo_active(Socket, Size) -> echo_active(Socket, Size - byte_size(Data)) end. + diff --git a/lib/ssl/test/ssl_session_ticket_SUITE.erl b/lib/ssl/test/ssl_session_ticket_SUITE.erl index 83e8e3214d..96b0fb5c2a 100644 --- a/lib/ssl/test/ssl_session_ticket_SUITE.erl +++ b/lib/ssl/test/ssl_session_ticket_SUITE.erl @@ -475,7 +475,7 @@ erlang_client_erlang_server_multiple_tickets(Config) when is_list(Config) -> ServerTicketMode = proplists:get_value(server_ticket_mode, Config), %% Configure session tickets - ClientOpts = [{session_tickets, enabled}, {log_level, debug}, + ClientOpts = [{session_tickets, manual}, {log_level, debug}, {versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0], ServerOpts = [{session_tickets, ServerTicketMode}, {log_level, debug}, {versions, ['tlsv1.2','tlsv1.3']}|ServerOpts0], @@ -540,7 +540,7 @@ erlang_client_openssl_server_hrr_multiple_tickets(Config) when is_list(Config) - KeyFile = proplists:get_value(keyfile, ServerOpts), %% Configure session tickets - ClientOpts = [{session_tickets, enabled}, {log_level, debug}, + ClientOpts = [{session_tickets, manual}, {log_level, debug}, {versions, ['tlsv1.2','tlsv1.3']}, {supported_groups,[secp256r1, x25519]}|ClientOpts0], @@ -596,7 +596,7 @@ erlang_client_erlang_server_multiple_tickets_2hash(Config) when is_list(Config) ServerTicketMode = proplists:get_value(server_ticket_mode, Config), %% Configure session tickets - ClientOpts = [{session_tickets, enabled}, {log_level, debug}, + ClientOpts = [{session_tickets, manual}, {log_level, debug}, {versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0], ServerOpts = [{session_tickets, ServerTicketMode}, {log_level, debug}, {versions, ['tlsv1.2','tlsv1.3']}|ServerOpts0], diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl index 6218857e05..a362838f90 100644 --- a/lib/ssl/test/ssl_test_lib.erl +++ b/lib/ssl/test/ssl_test_lib.erl @@ -64,7 +64,9 @@ start_server(Args) -> Result = spawn_link(Node, ?MODULE, run_server, [Args]), receive {listen, up} -> - Result + Result; + {error, Error} -> + Error end. run_server(Opts) -> @@ -73,10 +75,15 @@ run_server(Opts) -> Pid = proplists:get_value(from, Opts), Transport = proplists:get_value(transport, Opts, ssl), ct:log("~p:~p~nssl:listen(~p, ~p)~n", [?MODULE,?LINE, Port, Options]), - {ok, ListenSocket} = Transport:listen(Port, Options), - Pid ! {listen, up}, - send_selected_port(Pid, Port, ListenSocket), - run_server(ListenSocket, Opts). + %% {ok, ListenSocket} = Transport:listen(Port, Options), + case Transport:listen(Port, Options) of + {ok, ListenSocket} -> + Pid ! {listen, up}, + send_selected_port(Pid, Port, ListenSocket), + run_server(ListenSocket, Opts); + Error -> + Pid ! Error + end. run_server(ListenSocket, Opts) -> Accepters = proplists:get_value(accepters, Opts, 1), @@ -2253,8 +2260,9 @@ check_sane_openssl_version(Version) -> false -> false end. -check_sane_openssl_renegotaite(Config, Version) when Version == 'tlsv1.1'; - Version == 'tlsv1.2' -> +check_sane_openssl_renegotaite(Config, Version) when Version == 'tlsv1'; + Version == 'tlsv1.1'; + Version == 'tlsv1.2' -> case os:cmd("openssl version") of "OpenSSL 1.0.1c" ++ _ -> {skip, "Known renegotiation bug in OpenSSL"}; @@ -2264,7 +2272,9 @@ check_sane_openssl_renegotaite(Config, Version) when Version == 'tlsv1.1'; {skip, "Known renegotiation bug in OpenSSL"}; "OpenSSL 1.0.1 " ++ _ -> {skip, "Known renegotiation bug in OpenSSL"}; - _ -> + "LibreSSL 3.0.2" ++ _ -> + {skip, "Known renegotiation bug in OpenSSL"}; + _ -> check_sane_openssl_renegotaite(Config) end; check_sane_openssl_renegotaite(Config, 'sslv3') -> @@ -2889,3 +2899,15 @@ openssl_sane_dtls_session_reuse() -> _-> openssl_sane_dtls() end. + +-define(BIG_BUF, 10000000). +%% Workaround data delivery issues on solaris | openbsd when kernel buffers are small +bigger_buffers() -> + case os:type() of + {unix,sunos} -> + [{recbuf, ?BIG_BUF},{sndbuf, ?BIG_BUF}]; + {unix,openbsd} -> + [{recbuf, ?BIG_BUF},{sndbuf, ?BIG_BUF}]; + _ -> + [] + end. diff --git a/lib/stdlib/doc/src/uri_string.xml b/lib/stdlib/doc/src/uri_string.xml index ad443486c5..54ea647f03 100644 --- a/lib/stdlib/doc/src/uri_string.xml +++ b/lib/stdlib/doc/src/uri_string.xml @@ -265,7 +265,7 @@ <fsummary>Syntax-based normalization.</fsummary> <desc> <p>Same as <c>normalize/1</c> but with an additional - <c><anno>Options</anno></c> parameter, that controls if the normalized URI + <c><anno>Options</anno></c> parameter, that controls whether the normalized URI shall be returned as an uri_map(). There is one supported option: <c>return_map</c>. </p> @@ -332,6 +332,47 @@ </func> <func> + <name name="resolve" arity="2" since="OTP @OTP-16321@"/> + <fsummary>Relative resolution.</fsummary> + <desc> + <p>Convert a <c><anno>RefURI</anno></c> reference that might be relative + to a given base URI into the parsed components of the reference's target, + which can then be recomposed to form the target URI.</p> + <p><em>Example:</em></p> + <pre> +1> <input>uri_string:resolve("/abs/ol/ute", "http://localhost/a/b/c?q").</input> +"http://localhost/abs/ol/ute" +2> uri_string:resolve("../relative", "http://localhost/a/b/c?q"). +"http://localhost/a/relative" +3> uri_string:resolve("http://localhost/full", "http://localhost/a/b/c?q"). +"http://localhost/full" +4> uri_string:resolve(#{path => "path", query => "xyz"}, "http://localhost/a/b/c?q"). +"http://localhost/a/b/path?xyz" + </pre> + </desc> + </func> + + <func> + <name name="resolve" arity="3" since="OTP @OTP-16321@"/> + <fsummary>Relative resolution.</fsummary> + <desc> + <p>Same as <c>resolve/2</c> but with an additional + <c><anno>Options</anno></c> parameter, that controls whether the target URI + shall be returned as an uri_map(). + There is one supported option: <c>return_map</c>. + </p> + <p><em>Example:</em></p> + <pre> +1> <input>uri_string:resolve("/abs/ol/ute", "http://localhost/a/b/c?q", [return_map]).</input> +#{host => "localhost",path => "/abs/ol/ute",scheme => "http"} +2> uri_string:resolve(#{path => "/abs/ol/ute"}, #{scheme => "http", +2> host => "localhost", path => "/a/b/c?q"}, [return_map]). +#{host => "localhost",path => "/abs/ol/ute",scheme => "http"} + </pre> + </desc> + </func> + + <func> <name name="transcode" arity="2" since="OTP 21.0"/> <fsummary>Transcode URI.</fsummary> <desc> diff --git a/lib/stdlib/src/stdlib.app.src b/lib/stdlib/src/stdlib.app.src index 06e33a23cc..6ade386159 100644 --- a/lib/stdlib/src/stdlib.app.src +++ b/lib/stdlib/src/stdlib.app.src @@ -111,4 +111,3 @@ {runtime_dependencies, ["sasl-3.0","kernel-6.0","erts-10.6.2","crypto-3.3", "compiler-5.0"]} ]}. - diff --git a/lib/stdlib/src/uri_string.erl b/lib/stdlib/src/uri_string.erl index d33dc89af8..18ee8375c7 100644 --- a/lib/stdlib/src/uri_string.erl +++ b/lib/stdlib/src/uri_string.erl @@ -228,7 +228,7 @@ %%------------------------------------------------------------------------- -export([compose_query/1, compose_query/2, dissect_query/1, normalize/1, normalize/2, parse/1, - recompose/1, transcode/2]). + recompose/1, resolve/2, resolve/3, transcode/2]). -export_type([error/0, uri_map/0, uri_string/0]). @@ -306,7 +306,8 @@ normalize(URIMap) -> -spec normalize(URI, Options) -> NormalizedURI when URI :: uri_string() | uri_map(), Options :: [return_map], - NormalizedURI :: uri_string() | uri_map(). + NormalizedURI :: uri_string() | uri_map() + | error(). normalize(URIMap, []) when is_map(URIMap) -> recompose(normalize_map(URIMap)); normalize(URIMap, [return_map]) when is_map(URIMap) -> @@ -376,6 +377,45 @@ recompose(Map) -> %%------------------------------------------------------------------------- +%% Resolve URIs +%%------------------------------------------------------------------------- +-spec resolve(RefURI, BaseURI) -> TargetURI when + RefURI :: uri_string() | uri_map(), + BaseURI :: uri_string() | uri_map(), + TargetURI :: uri_string() + | error(). +resolve(URIMap, BaseURIMap) -> + resolve(URIMap, BaseURIMap, []). + + +-spec resolve(RefURI, BaseURI, Options) -> TargetURI when + RefURI :: uri_string() | uri_map(), + BaseURI :: uri_string() | uri_map(), + Options :: [return_map], + TargetURI :: uri_string() | uri_map() + | error(). +resolve(URIMap, BaseURIMap, Options) when is_map(URIMap) -> + case resolve_map(URIMap, BaseURIMap) of + TargetURIMap when is_map(TargetURIMap) -> + case Options of + [return_map] -> + TargetURIMap; + [] -> + recompose(TargetURIMap) + end; + Error -> + Error + end; +resolve(URIString, BaseURIMap, Options) -> + case parse(URIString) of + URIMap when is_map(URIMap) -> + resolve(URIMap, BaseURIMap, Options); + Error -> + Error + end. + + +%%------------------------------------------------------------------------- %% Transcode URIs %%------------------------------------------------------------------------- -spec transcode(URIString, Options) -> Result when @@ -1707,6 +1747,69 @@ maybe_to_list(Comp) -> Comp. encode_port(Port) -> integer_to_binary(Port). + +%%------------------------------------------------------------------------- +%% Helper functions for resolve +%%------------------------------------------------------------------------- + +resolve_map(URIMap=#{scheme := _}, _) -> + normalize_path_segment(URIMap); +resolve_map(URIMap, #{scheme := _}=BaseURIMap) -> + resolve_map(URIMap, BaseURIMap, resolve_path_type(URIMap)); +resolve_map(_URIMap, BaseURIMap) when is_map(BaseURIMap) -> + {error,invalid_scheme,""}; +resolve_map(URIMap, BaseURIString) -> + case parse(BaseURIString) of + BaseURIMap = #{scheme := _} -> + resolve_map(URIMap, BaseURIMap, resolve_path_type(URIMap)); + BaseURIMap when is_map(BaseURIMap) -> + {error,invalid_scheme,""}; + Error -> + Error + end. + +resolve_path_type(URIMap) -> + case iolist_to_binary(maps:get(path, URIMap, <<>>)) of + <<>> -> empty_path; + <<$/,_/bits>> -> absolute_path; + _ -> relative_path + end. + +resolve_map(URI=#{host := _}, #{scheme := Scheme}, _) -> + normalize_path_segment(URI#{scheme => Scheme}); +resolve_map(URI, BaseURI, empty_path) -> + Keys = case maps:is_key(query, URI) of + true -> [scheme, userinfo, host, port, path]; + false -> [scheme, userinfo, host, port, path, query] + end, + maps:merge(URI, maps:with(Keys, BaseURI)); +resolve_map(URI, BaseURI, absolute_path) -> + normalize_path_segment(maps:merge( + URI, + maps:with([scheme, userinfo, host, port], BaseURI))); +resolve_map(URI=#{path := Path}, BaseURI, relative_path) -> + normalize_path_segment(maps:merge( + URI#{path => merge_paths(Path, BaseURI)}, + maps:with([scheme, userinfo, host, port], BaseURI))). + +merge_paths(Path, BaseURI=#{path := BasePath0}) -> + case {BaseURI, iolist_size(BasePath0)} of + {#{host := _}, 0} -> + merge_paths_absolute(Path); + _ -> + case string:split(BasePath0, <<$/>>, trailing) of + [BasePath, _] when is_binary(Path) -> unicode:characters_to_binary([BasePath, $/, Path]); + [BasePath, _] when is_list(Path) -> unicode:characters_to_list([BasePath, $/, Path]); + [_] -> Path + end + end. + +merge_paths_absolute(Path) when is_binary(Path) -> + <<$/, Path/binary>>; +merge_paths_absolute(Path) when is_list(Path) -> + unicode:characters_to_list([$/, Path]). + + %%------------------------------------------------------------------------- %% Helper functions for transcode %%------------------------------------------------------------------------- diff --git a/lib/stdlib/test/uri_string_SUITE.erl b/lib/stdlib/test/uri_string_SUITE.erl index ddaead9c7c..55ba75bd5f 100644 --- a/lib/stdlib/test/uri_string_SUITE.erl +++ b/lib/stdlib/test/uri_string_SUITE.erl @@ -44,6 +44,8 @@ recompose_query/1, recompose_parse_query/1, recompose_path/1, recompose_parse_path/1, recompose_autogen/1, parse_recompose_autogen/1, + resolve_normal_examples/1, resolve_abnormal_examples/1, + resolve_base_uri/1, resolve_return_map/1, transcode_basic/1, transcode_options/1, transcode_mixed/1, transcode_negative/1, compose_query/1, compose_query_latin1/1, compose_query_negative/1, dissect_query/1, dissect_query_negative/1, @@ -123,6 +125,10 @@ all() -> recompose_parse_path, recompose_autogen, parse_recompose_autogen, + resolve_normal_examples, + resolve_abnormal_examples, + resolve_base_uri, + resolve_return_map, transcode_basic, transcode_options, transcode_mixed, @@ -825,6 +831,68 @@ parse_recompose_autogen(_Config) -> Tests = generate_test_vectors(uri_combinations()), lists:map(fun run_test_parse_recompose/1, Tests). +resolve_normal_examples(_Config) -> + BaseURI = <<"http://a/b/c/d;p?q">>, + <<"g:h">> = uri_string:resolve(<<"g:h">>, BaseURI), + <<"http://a/b/c/g">> = uri_string:resolve(<<"g">>, BaseURI), + <<"http://a/b/c/g">> = uri_string:resolve(<<"./g">>, BaseURI), + <<"http://a/b/c/g/">> = uri_string:resolve(<<"g/">>, BaseURI), + <<"http://a/g">> = uri_string:resolve(<<"/g">>, BaseURI), + <<"http://g">> = uri_string:resolve(<<"//g">>, BaseURI), + <<"http://a/b/c/d;p?y">> = uri_string:resolve(<<"?y">>, BaseURI), + <<"http://a/b/c/g?y">> = uri_string:resolve(<<"g?y">>, BaseURI), + <<"http://a/b/c/d;p?q#s">> = uri_string:resolve(<<"#s">>, BaseURI), + <<"http://a/b/c/g#s">> = uri_string:resolve(<<"g#s">>, BaseURI), + <<"http://a/b/c/g?y#s">> = uri_string:resolve(<<"g?y#s">>, BaseURI), + <<"http://a/b/c/;x">> = uri_string:resolve(<<";x">>, BaseURI), + <<"http://a/b/c/g;x">> = uri_string:resolve(<<"g;x">>, BaseURI), + <<"http://a/b/c/g;x?y#s">> = uri_string:resolve(<<"g;x?y#s">>, BaseURI), + <<"http://a/b/c/d;p?q">> = uri_string:resolve(<<"">>, BaseURI), + <<"http://a/b/c/">> = uri_string:resolve(<<".">>, BaseURI), + <<"http://a/b/c/">> = uri_string:resolve(<<"./">>, BaseURI), + <<"http://a/b/">> = uri_string:resolve(<<"..">>, BaseURI), + <<"http://a/b/">> = uri_string:resolve(<<"../">>, BaseURI), + <<"http://a/b/g">> = uri_string:resolve(<<"../g">>, BaseURI), + <<"http://a/">> = uri_string:resolve(<<"../..">>, BaseURI), + <<"http://a/">> = uri_string:resolve(<<"../../">>, BaseURI), + <<"http://a/g">> = uri_string:resolve(<<"../../g">>, BaseURI). + +resolve_abnormal_examples(_Config) -> + BaseURI = <<"http://a/b/c/d;p?q">>, + <<"http://a/g">> = uri_string:resolve(<<"../../../g">>, BaseURI), + <<"http://a/g">> = uri_string:resolve(<<"../../../../g">>, BaseURI), + <<"http://a/g">> = uri_string:resolve(<<"/./g">>, BaseURI), + <<"http://a/g">> = uri_string:resolve(<<"/../g">>, BaseURI), + <<"http://a/b/c/g.">> = uri_string:resolve(<<"g.">>, BaseURI), + <<"http://a/b/c/.g">> = uri_string:resolve(<<".g">>, BaseURI), + <<"http://a/b/c/g..">> = uri_string:resolve(<<"g..">>, BaseURI), + <<"http://a/b/c/..g">> = uri_string:resolve(<<"..g">>, BaseURI), + <<"http://a/b/g">> = uri_string:resolve(<<"./../g">>, BaseURI), + <<"http://a/b/c/g/">> = uri_string:resolve(<<"./g/.">>, BaseURI), + <<"http://a/b/c/g/h">> = uri_string:resolve(<<"g/./h">>, BaseURI), + <<"http://a/b/c/h">> = uri_string:resolve(<<"g/../h">>, BaseURI), + <<"http://a/b/c/g;x=1/y">> = uri_string:resolve(<<"g;x=1/./y">>, BaseURI), + <<"http://a/b/c/y">> = uri_string:resolve(<<"g;x=1/../y">>, BaseURI), + <<"http://a/b/c/g?y/./x">> = uri_string:resolve(<<"g?y/./x">>, BaseURI), + <<"http://a/b/c/g?y/../x">> = uri_string:resolve(<<"g?y/../x">>, BaseURI), + <<"http://a/b/c/g#s/./x">> = uri_string:resolve(<<"g#s/./x">>, BaseURI), + <<"http://a/b/c/g#s/../x">> = uri_string:resolve(<<"g#s/../x">>, BaseURI), + <<"http:g">> = uri_string:resolve(<<"http:g">>, BaseURI). %% for strict parsers + +resolve_base_uri(_Config) -> + %% The scheme is required (RFC3986 5.2.1). + {error,invalid_scheme,""} = uri_string:resolve("g", #{}), + {error,invalid_scheme,""} = uri_string:resolve("g", "/b/c/d"), + "foo:g" = uri_string:resolve("g", "foo:"), + "foo://a/g" = uri_string:resolve("g", "foo://a"), + "foo:/g" = uri_string:resolve("g", "foo:/a"), + "foo://a/b/c/g" = uri_string:resolve("g", "foo://a/b/c/d;p?y#f"). + +resolve_return_map(_Config) -> + BaseURI = <<"http://a/b/c/d;p?q">>, + #{scheme := <<"http">>,host := <<"a">>,path := <<"/b/c/g">>} = + uri_string:resolve(<<"g">>, BaseURI, [return_map]). + transcode_basic(_Config) -> <<"foo%C3%B6bar"/utf8>> = uri_string:transcode(<<"foo%00%00%00%F6bar"/utf32>>, [{in_encoding, utf32},{out_encoding, utf8}]), diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el index 429471a0e0..838c41a090 100644 --- a/lib/tools/emacs/erlang.el +++ b/lib/tools/emacs/erlang.el @@ -1949,7 +1949,8 @@ The format is described in the documentation of `erlang-man-dirs'." (defcustom erlang-man-download-url "http://erlang.org/download/otp_doc_man_22.1.tar.gz" "The URL from which the erlang-man-download function will - download Erlang man pages ") + download Erlang man pages " + :type 'string) (defun erlang-man-user-local-emacs-dir () "Returns the directory where man pages that are downloaded by diff --git a/make/otp_version_tickets b/make/otp_version_tickets index 76050459e4..b8220e1a87 100644 --- a/make/otp_version_tickets +++ b/make/otp_version_tickets @@ -1,7 +1 @@ -OTP-16333 -OTP-16371 -OTP-16373 -OTP-16375 -OTP-16376 -OTP-16378 -OTP-16379 +DEVELOPMENT diff --git a/make/otp_version_tickets_in_merge b/make/otp_version_tickets_in_merge index 4caf7a8125..76050459e4 100644 --- a/make/otp_version_tickets_in_merge +++ b/make/otp_version_tickets_in_merge @@ -1 +1,7 @@ -OTP-16301 +OTP-16333 +OTP-16371 +OTP-16373 +OTP-16375 +OTP-16376 +OTP-16378 +OTP-16379 diff --git a/otp_versions.table b/otp_versions.table index 80d69b0bed..32daecb7ed 100644 --- a/otp_versions.table +++ b/otp_versions.table @@ -1,6 +1,7 @@ OTP-22.2.2 : crypto-4.6.4 erts-10.6.2 ssh-4.8.2 stdlib-3.11.1 # asn1-5.0.9 common_test-1.18.1 compiler-7.5 debugger-4.2.8 dialyzer-4.1.1 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.11 erl_interface-3.13.1 et-1.6.4 eunit-2.4 ftp-1.0.4 hipe-3.19.2 inets-7.1.2 jinterface-1.10.1 kernel-6.5.1 megaco-3.18.7 mnesia-4.16.2 observer-2.9.3 odbc-2.12.4 os_mon-2.5.1 parsetools-2.1.8 public_key-1.7.1 reltool-0.8 runtime_tools-1.14 sasl-3.4.1 snmp-5.4.5 ssl-9.5.1 syntax_tools-2.2.1 tftp-1.0.2 tools-3.3 wx-1.9 xmerl-1.3.23 : OTP-22.2.1 : erts-10.6.1 snmp-5.4.5 ssl-9.5.1 # asn1-5.0.9 common_test-1.18.1 compiler-7.5 crypto-4.6.3 debugger-4.2.8 dialyzer-4.1.1 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.11 erl_interface-3.13.1 et-1.6.4 eunit-2.4 ftp-1.0.4 hipe-3.19.2 inets-7.1.2 jinterface-1.10.1 kernel-6.5.1 megaco-3.18.7 mnesia-4.16.2 observer-2.9.3 odbc-2.12.4 os_mon-2.5.1 parsetools-2.1.8 public_key-1.7.1 reltool-0.8 runtime_tools-1.14 sasl-3.4.1 ssh-4.8.1 stdlib-3.11 syntax_tools-2.2.1 tftp-1.0.2 tools-3.3 wx-1.9 xmerl-1.3.23 : OTP-22.2 : common_test-1.18.1 compiler-7.5 crypto-4.6.3 debugger-4.2.8 dialyzer-4.1.1 erl_docgen-0.11 erl_interface-3.13.1 erts-10.6 eunit-2.4 ftp-1.0.4 hipe-3.19.2 inets-7.1.2 kernel-6.5.1 megaco-3.18.7 mnesia-4.16.2 observer-2.9.3 public_key-1.7.1 snmp-5.4.4 ssh-4.8.1 ssl-9.5 stdlib-3.11 tftp-1.0.2 tools-3.3 wx-1.9 xmerl-1.3.23 # asn1-5.0.9 diameter-2.2.1 edoc-0.11 eldap-1.2.8 et-1.6.4 jinterface-1.10.1 odbc-2.12.4 os_mon-2.5.1 parsetools-2.1.8 reltool-0.8 runtime_tools-1.14 sasl-3.4.1 syntax_tools-2.2.1 : +OTP-22.1.8.1 : snmp-5.4.3.1 # asn1-5.0.9 common_test-1.18 compiler-7.4.9 crypto-4.6.2 debugger-4.2.7 dialyzer-4.1 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.10 erl_interface-3.13 erts-10.5.6 et-1.6.4 eunit-2.3.8 ftp-1.0.3 hipe-3.19.1 inets-7.1.1 jinterface-1.10.1 kernel-6.5 megaco-3.18.6 mnesia-4.16.1 observer-2.9.2 odbc-2.12.4 os_mon-2.5.1 parsetools-2.1.8 public_key-1.7 reltool-0.8 runtime_tools-1.14 sasl-3.4.1 ssh-4.8 ssl-9.4 stdlib-3.10 syntax_tools-2.2.1 tftp-1.0.1 tools-3.2.1 wx-1.8.9 xmerl-1.3.22 : OTP-22.1.8 : erts-10.5.6 # asn1-5.0.9 common_test-1.18 compiler-7.4.9 crypto-4.6.2 debugger-4.2.7 dialyzer-4.1 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.10 erl_interface-3.13 et-1.6.4 eunit-2.3.8 ftp-1.0.3 hipe-3.19.1 inets-7.1.1 jinterface-1.10.1 kernel-6.5 megaco-3.18.6 mnesia-4.16.1 observer-2.9.2 odbc-2.12.4 os_mon-2.5.1 parsetools-2.1.8 public_key-1.7 reltool-0.8 runtime_tools-1.14 sasl-3.4.1 snmp-5.4.3 ssh-4.8 ssl-9.4 stdlib-3.10 syntax_tools-2.2.1 tftp-1.0.1 tools-3.2.1 wx-1.8.9 xmerl-1.3.22 : OTP-22.1.7 : compiler-7.4.9 erts-10.5.5 # asn1-5.0.9 common_test-1.18 crypto-4.6.2 debugger-4.2.7 dialyzer-4.1 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.10 erl_interface-3.13 et-1.6.4 eunit-2.3.8 ftp-1.0.3 hipe-3.19.1 inets-7.1.1 jinterface-1.10.1 kernel-6.5 megaco-3.18.6 mnesia-4.16.1 observer-2.9.2 odbc-2.12.4 os_mon-2.5.1 parsetools-2.1.8 public_key-1.7 reltool-0.8 runtime_tools-1.14 sasl-3.4.1 snmp-5.4.3 ssh-4.8 ssl-9.4 stdlib-3.10 syntax_tools-2.2.1 tftp-1.0.1 tools-3.2.1 wx-1.8.9 xmerl-1.3.22 : OTP-22.1.6 : compiler-7.4.8 crypto-4.6.2 erts-10.5.4 snmp-5.4.3 # asn1-5.0.9 common_test-1.18 debugger-4.2.7 dialyzer-4.1 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.10 erl_interface-3.13 et-1.6.4 eunit-2.3.8 ftp-1.0.3 hipe-3.19.1 inets-7.1.1 jinterface-1.10.1 kernel-6.5 megaco-3.18.6 mnesia-4.16.1 observer-2.9.2 odbc-2.12.4 os_mon-2.5.1 parsetools-2.1.8 public_key-1.7 reltool-0.8 runtime_tools-1.14 sasl-3.4.1 ssh-4.8 ssl-9.4 stdlib-3.10 syntax_tools-2.2.1 tftp-1.0.1 tools-3.2.1 wx-1.8.9 xmerl-1.3.22 : |