aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/common_test/doc/src/common_test_app.xml17
-rw-r--r--lib/common_test/doc/src/ct_hooks.xml125
-rw-r--r--lib/common_test/doc/src/notes.xml15
-rw-r--r--lib/common_test/doc/src/write_test_chapter.xml18
-rw-r--r--lib/common_test/src/ct_framework.erl314
-rw-r--r--lib/common_test/src/ct_groups.erl83
-rw-r--r--lib/common_test/src/ct_hooks.erl77
-rw-r--r--lib/common_test/src/ct_logs.erl14
-rw-r--r--lib/common_test/src/test_server.erl216
-rw-r--r--lib/common_test/src/test_server_ctrl.erl137
-rw-r--r--lib/common_test/src/test_server_node.erl2
-rw-r--r--lib/common_test/src/test_server_sup.erl2
-rw-r--r--lib/common_test/test/Makefile4
-rw-r--r--lib/common_test/test/ct_error_SUITE.erl49
-rw-r--r--lib/common_test/test/ct_hooks_SUITE.erl345
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/all_and_groups_SUITE.erl47
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/all_and_groups_cth.erl100
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_match_state_cth.erl58
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_suite_group_only_cth_SUITE.erl54
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl28
-rw-r--r--lib/common_test/test/ct_tc_repeat_SUITE.erl438
-rw-r--r--lib/common_test/test/ct_tc_repeat_SUITE_data/tc_repeat_SUITE.erl85
-rw-r--r--lib/common_test/test/ct_util_SUITE.erl490
-rw-r--r--lib/common_test/test/ct_util_SUITE_data/ct_util_cth.erl105
-rw-r--r--lib/common_test/test/ct_util_SUITE_data/curr_tc_SUITE.erl59
-rw-r--r--lib/common_test/vsn.mk2
-rw-r--r--lib/compiler/src/beam_asm.erl26
-rw-r--r--lib/crypto/doc/src/notes.xml16
-rw-r--r--lib/crypto/vsn.mk2
-rw-r--r--lib/diameter/doc/src/notes.xml17
-rw-r--r--lib/diameter/src/base/diameter_dist.erl12
-rw-r--r--lib/diameter/src/diameter.appup.src6
-rw-r--r--lib/diameter/vsn.mk2
l---------lib/edoc/doc/edoc.dtd1
l---------lib/edoc/doc/edoc_doclet.hrl1
-rw-r--r--lib/edoc/doc/src/Makefile6
-rw-r--r--lib/edoc/include/edoc_doclet.hrl2
-rw-r--r--lib/edoc/src/edoc.erl2
-rw-r--r--lib/edoc/src/edoc_doclet.erl2
-rw-r--r--lib/erl_interface/doc/src/notes.xml16
-rw-r--r--lib/erl_interface/vsn.mk2
-rw-r--r--lib/inets/doc/src/notes.xml24
-rw-r--r--lib/inets/src/http_server/httpd_example.erl21
-rw-r--r--lib/inets/src/http_server/mod_esi.erl4
-rw-r--r--lib/inets/test/httpd_SUITE.erl165
-rw-r--r--lib/inets/vsn.mk2
-rw-r--r--lib/kernel/src/erts_debug.erl6
-rw-r--r--lib/os_mon/src/cpu_sup.erl4
-rw-r--r--lib/snmp/src/agent/snmpa_agent.erl119
-rw-r--r--lib/snmp/src/agent/snmpa_local_db.erl9
-rw-r--r--lib/snmp/src/misc/snmp_misc.erl118
-rw-r--r--lib/snmp/src/misc/snmp_verbosity.erl16
-rw-r--r--lib/snmp/test/snmp_agent_test.erl42
-rw-r--r--lib/snmp/test/snmp_agent_test_lib.erl348
-rw-r--r--lib/snmp/test/snmp_manager_test.erl185
-rw-r--r--lib/snmp/test/snmp_test_lib.erl98
-rw-r--r--lib/snmp/test/snmp_test_lib.hrl25
-rw-r--r--lib/snmp/test/snmp_test_mgr.erl74
-rw-r--r--lib/snmp/test/snmp_test_mgr_misc.erl291
-rw-r--r--lib/ssh/doc/src/notes.xml25
-rw-r--r--lib/ssh/src/ssh.erl2
-rw-r--r--lib/ssh/src/ssh_channel.erl4
-rw-r--r--lib/ssh/vsn.mk2
-rw-r--r--lib/ssl/src/dtls_handshake.erl2
-rw-r--r--lib/ssl/src/dtls_v1.erl6
-rw-r--r--lib/ssl/src/ssl.erl20
-rw-r--r--lib/ssl/src/ssl_cipher.erl6
-rw-r--r--lib/ssl/src/ssl_cipher_format.erl1247
-rw-r--r--lib/ssl/src/ssl_connection.erl12
-rw-r--r--lib/ssl/src/ssl_handshake.erl8
-rw-r--r--lib/ssl/src/ssl_logger.erl14
-rw-r--r--lib/ssl/src/tls_handshake.erl2
-rw-r--r--lib/ssl/src/tls_handshake_1_3.erl4
-rw-r--r--lib/ssl/test/ssl_cipher_suite_SUITE.erl2
-rw-r--r--lib/ssl/test/ssl_test_lib.erl30
-rw-r--r--lib/ssl/test/x509_test.erl2
l---------lib/syntax_tools/doc/demo.erl1
-rw-r--r--lib/syntax_tools/doc/overview.edoc2
-rw-r--r--lib/syntax_tools/doc/src/Makefile7
79 files changed, 4552 insertions, 1394 deletions
diff --git a/lib/common_test/doc/src/common_test_app.xml b/lib/common_test/doc/src/common_test_app.xml
index 7887a2c3ea..081adeaec7 100644
--- a/lib/common_test/doc/src/common_test_app.xml
+++ b/lib/common_test/doc/src/common_test_app.xml
@@ -72,14 +72,15 @@
<fsummary>Returns the list of all test case groups and test cases
in the module.</fsummary>
<type>
- <v>Tests = [TestCase | {group,GroupName} | {group,GroupName,Properties} | {group,GroupName,Properties,SubGroups}]</v>
+ <v>Tests = [TestCase | {testcase,TestCase,TCRepeatProps} | {group,GroupName} | {group,GroupName,Properties} | {group,GroupName,Properties,SubGroups}]</v>
<v>TestCase = atom()</v>
+ <v>TCRepeatProps = [{repeat,N} | {repeat_until_ok,N} | {repeat_until_fail,N}]</v>
<v>GroupName = atom()</v>
- <v>Properties = [parallel | sequence | Shuffle | {RepeatType,N}] | default</v>
+ <v>Properties = [parallel | sequence | Shuffle | {GroupRepeatType,N}] | default</v>
<v>SubGroups = [{GroupName,Properties} | {GroupName,Properties,SubGroups}]</v>
<v>Shuffle = shuffle | {shuffle,Seed}</v>
<v>Seed = {integer(),integer(),integer()}</v>
- <v>RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail | repeat_until_any_ok | repeat_until_any_fail</v>
+ <v>GroupRepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail | repeat_until_any_ok | repeat_until_any_fail</v>
<v>N = integer() | forever</v>
<v>Reason = term()</v>
</type>
@@ -91,7 +92,8 @@
test suite module to be executed. This list also specifies the
order the cases and groups are executed by <c>Common Test</c>.
A test case is represented by an atom,
- the name of the test case function. A test case group is
+ the name of the test case function, or a <c>testcase</c> tuple
+ indicating that the test case shall be repeated. A test case group is
represented by a <c>group</c> tuple, where <c>GroupName</c>,
an atom, is the name of the group (defined in
<seealso marker="#Module:groups-0"><c>groups/0</c></seealso>).
@@ -121,12 +123,13 @@
<v>GroupDefs = [Group]</v>
<v>Group = {GroupName,Properties,GroupsAndTestCases}</v>
<v>GroupName = atom()</v>
- <v>Properties = [parallel | sequence | Shuffle | {RepeatType,N}]</v>
- <v>GroupsAndTestCases = [Group | {group,GroupName} | TestCase]</v>
+ <v>Properties = [parallel | sequence | Shuffle | {GroupRepeatType,N}]</v>
+ <v>GroupsAndTestCases = [Group | {group,GroupName} | TestCase | {testcase,TestCase,TCRepeatProps}]</v>
<v>TestCase = atom()</v>
+ <v>TCRepeatProps = [{repeat,N} | {repeat_until_ok,N} | {repeat_until_fail,N}]</v>
<v>Shuffle = shuffle | {shuffle,Seed}</v>
<v>Seed = {integer(),integer(),integer()}</v>
- <v>RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail | repeat_until_any_ok | repeat_until_any_fail</v>
+ <v>GroupRepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail | repeat_until_any_ok | repeat_until_any_fail</v>
<v>N = integer() | forever</v>
</type>
diff --git a/lib/common_test/doc/src/ct_hooks.xml b/lib/common_test/doc/src/ct_hooks.xml
index ff0d0117cd..ff9969ebc3 100644
--- a/lib/common_test/doc/src/ct_hooks.xml
+++ b/lib/common_test/doc/src/ct_hooks.xml
@@ -109,6 +109,131 @@
</func>
<func>
+ <name since="OTP @OTP-14746@">Module:post_groups(SuiteName, GroupDefs) -&gt; NewGroupDefs</name>
+ <fsummary>Called after groups/0.</fsummary>
+ <type>
+ <v>SuiteName = atom()</v>
+ <v>GroupDefs = NewGroupDefs = [Group]</v>
+ <v>Group = {GroupName,Properties,GroupsAndTestCases}</v>
+ <v>GroupName = atom()</v>
+ <v>Properties = [parallel | sequence | Shuffle | {GroupRepeatType,N}]</v>
+ <v>GroupsAndTestCases = [Group | {group,GroupName} | TestCase | {testcase,TestCase,TCRepeatProps}]</v>
+ <v>TestCase = atom()</v>
+ <v>TCRepeatProps = [{repeat,N} | {repeat_until_ok,N} | {repeat_until_fail,N}]</v>
+ <v>Shuffle = shuffle | {shuffle,Seed}</v>
+ <v>Seed = {integer(),integer(),integer()}</v>
+ <v>GroupRepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail | repeat_until_any_ok | repeat_until_any_fail</v>
+ <v>N = integer() | forever</v>
+ </type>
+ <desc>
+ <p>OPTIONAL</p>
+
+ <p>This function is called after
+ <seealso marker="common_test#Module:groups-0"><c>groups/0</c></seealso>.
+ It is used to modify the test group definitions, for
+ instance to add or remove groups or change group properties.</p>
+
+ <p><c>GroupDefs</c> is what
+ <seealso marker="common_test#Module:groups-0"><c>groups/0</c></seealso>
+ returned, that is, a list of group definitions.</p>
+
+ <p><c>NewGroupDefs</c> is the possibly modified version of this list.</p>
+
+ <p>This function is called only if the CTH is added before
+ <c>init_per_suite</c> is run. For details, see section
+ <seealso marker="ct_hooks_chapter#scope">CTH Scope</seealso>
+ in the User's Guide.</p>
+
+ <p>Notice that for CTHs that are installed by means of the
+ <seealso marker="common_test#Module:suite-0"><c>suite/0</c></seealso>
+ function, <c>post_groups/2</c> is called before
+ the <seealso marker="#Module:init-2"><c>init/2</c></seealso>
+ hook function. However, for CTHs that are installed by means
+ of the CT start flag,
+ the <seealso marker="#Module:init-2"><c>init/2</c></seealso>
+ function is called first.</p>
+
+ <note>
+ <p>Prior to each test execution, Common Test does a
+ simulated test run in order to count test suites, groups
+ and cases for logging purposes. This causes
+ the <c>post_groups/2</c> hook function to always be called
+ twice. For this reason, side effects are best avoided in
+ this callback.</p>
+ </note>
+ </desc>
+ </func>
+
+ <func>
+ <name since="OTP @OTP-14746@">Module:post_all(SuiteName, Return, GroupDefs) -&gt; NewReturn</name>
+ <fsummary>Called after all/0.</fsummary>
+ <type>
+ <v>SuiteName = atom()</v>
+ <v>Return = NewReturn = Tests | {skip,Reason}</v>
+ <v>Tests = [TestCase | {testcase,TestCase,TCRepeatProps} | {group,GroupName} | {group,GroupName,Properties} | {group,GroupName,Properties,SubGroups}]</v>
+ <v>TestCase = atom()</v>
+ <v>TCRepeatProps = [{repeat,N} | {repeat_until_ok,N} | {repeat_until_fail,N}]</v>
+ <v>GroupName = atom()</v>
+ <v>Properties = GroupProperties | default</v>
+ <v>SubGroups = [{GroupName,Properties} | {GroupName,Properties,SubGroups}]</v>
+ <v>Shuffle = shuffle | {shuffle,Seed}</v>
+ <v>Seed = {integer(),integer(),integer()}</v>
+ <v>GroupRepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail | repeat_until_any_ok | repeat_until_any_fail</v>
+ <v>N = integer() | forever</v>
+ <v>GroupDefs = NewGroupDefs = [Group]</v>
+ <v>Group = {GroupName,GroupProperties,GroupsAndTestCases}</v>
+ <v>GroupProperties = [parallel | sequence | Shuffle | {GroupRepeatType,N}]</v>
+ <v>GroupsAndTestCases = [Group | {group,GroupName} | TestCase]</v>
+ <v>Reason = term()</v>
+ </type>
+ <desc>
+ <p>OPTIONAL</p>
+
+ <p>This function is called after
+ <seealso marker="common_test#Module:all-0"><c>all/0</c></seealso>.
+ It is used to modify the set of test cases and test group to
+ be executed, for instance to add or remove test cases and
+ groups, change group properties, or even skip all tests in
+ the suite.</p>
+
+ <p><c>Return</c> is what
+ <seealso marker="common_test#Module:all-0"><c>all/0</c></seealso>
+ returned, that is, a list of test cases and groups to be
+ executed, or a tuple <c>{skip,Reason}</c>.</p>
+
+ <p><c>GroupDefs</c> is what
+ <seealso marker="common_test#Module:groups-0"><c>groups/0</c></seealso>
+ or the <c>post_groups/2</c> hook returned, that is, a list
+ of group definitions.</p>
+
+ <p><c>NewReturn</c> is the possibly modified version of <c>Return</c>.</p>
+
+ <p>This function is called only if the CTH is added before
+ <c>init_per_suite</c> is run. For details, see section
+ <seealso marker="ct_hooks_chapter#scope">CTH Scope</seealso>
+ in the User's Guide.</p>
+
+ <p>Notice that for CTHs that are installed by means of the
+ <seealso marker="common_test#Module:suite-0"><c>suite/0</c></seealso>
+ function, <c>post_all/2</c> is called before
+ the <seealso marker="#Module:init-2"><c>init/2</c></seealso>
+ hook function. However, for CTHs that are installed by means
+ of the CT start flag,
+ the <seealso marker="#Module:init-2"><c>init/2</c></seealso>
+ function is called first.</p>
+
+ <note>
+ <p>Prior to each test execution, Common Test does a
+ simulated test run in order to count test suites, groups
+ and cases for logging purposes. This causes
+ the <c>post_all/3</c> hook function to always be called
+ twice. For this reason, side effects are best avoided in
+ this callback.</p>
+ </note>
+ </desc>
+ </func>
+
+ <func>
<name since="OTP R14B02">Module:pre_init_per_suite(SuiteName, InitData, CTHState) -&gt; Result</name>
<fsummary>Called before init_per_suite.</fsummary>
<type>
diff --git a/lib/common_test/doc/src/notes.xml b/lib/common_test/doc/src/notes.xml
index c8e0722a0f..9bffc23150 100644
--- a/lib/common_test/doc/src/notes.xml
+++ b/lib/common_test/doc/src/notes.xml
@@ -33,6 +33,21 @@
<file>notes.xml</file>
</header>
+<section><title>Common_Test 1.17.1</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ OTP internal test improvements.</p>
+ <p>
+ Own Id: OTP-15716</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Common_Test 1.17</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/common_test/doc/src/write_test_chapter.xml b/lib/common_test/doc/src/write_test_chapter.xml
index 82dc06834f..5eed748b08 100644
--- a/lib/common_test/doc/src/write_test_chapter.xml
+++ b/lib/common_test/doc/src/write_test_chapter.xml
@@ -455,8 +455,10 @@
GroupDefs = [GroupDef]
GroupDef = {GroupName,Properties,GroupsAndTestCases}
GroupName = atom()
- GroupsAndTestCases = [GroupDef | {group,GroupName} | TestCase]
- TestCase = atom()</pre>
+ GroupsAndTestCases = [GroupDef | {group,GroupName} | TestCase |
+ {testcase,TestCase,TCRepeatProps}]
+ TestCase = atom()
+ TCRepeatProps = [{repeat,N} | {repeat_until_ok,N} | {repeat_until_fail,N}]</pre>
<p><c>GroupName</c> is the name of the group and must be unique within
the test suite module. Groups can be nested, by including a group definition
@@ -464,11 +466,11 @@
<c>Properties</c> is the list of execution
properties for the group. The possible values are as follows:</p>
<pre>
- Properties = [parallel | sequence | Shuffle | {RepeatType,N}]
+ Properties = [parallel | sequence | Shuffle | {GroupRepeatType,N}]
Shuffle = shuffle | {shuffle,Seed}
Seed = {integer(),integer(),integer()}
- RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail |
- repeat_until_any_ok | repeat_until_any_fail
+ GroupRepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail |
+ repeat_until_any_ok | repeat_until_any_fail
N = integer() | forever</pre>
<p><em>Explanations:</em></p>
@@ -481,8 +483,8 @@
Dependencies Between Test Cases and Suites.</p></item>
<tag><c>shuffle</c></tag>
<item><p>The cases in the group are executed in random order.</p></item>
- <tag><c>repeat</c></tag>
- <item><p>Orders <c>Common Test</c> to repeat execution of the cases in the
+ <tag><c>repeat, repeat_until_*</c></tag>
+ <item><p>Orders <c>Common Test</c> to repeat execution of all the cases in the
group a given number of times, or until any, or all, cases fail or succeed.</p></item>
</taglist>
@@ -496,7 +498,7 @@
<c>{group,GroupName}</c> to the <c>all/0</c> list.</p>
<p><em>Example:</em></p>
<pre>
- all() -> [testcase1, {group,group1}, testcase2, {group,group2}].</pre>
+ all() -> [testcase1, {group,group1}, {testcase,testcase2,[{repeat,10}]}, {group,group2}].</pre>
<p>Execution properties with a group tuple in
<c>all/0</c>: <c>{group,GroupName,Properties}</c> can also be specified.
diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl
index 506147474f..bce6420042 100644
--- a/lib/common_test/src/ct_framework.erl
+++ b/lib/common_test/src/ct_framework.erl
@@ -696,9 +696,16 @@ end_tc(Mod,IPTC={init_per_testcase,_Func},_TCPid,Result,Args,Return) ->
end
end;
-end_tc(Mod,Func0,TCPid,Result,Args,Return) ->
+end_tc(Mod,Func00,TCPid,Result,Args,Return) ->
%% in case Mod == ct_framework, lookup the suite name
Suite = get_suite_name(Mod, Args),
+ {OnlyCleanup,Func0} =
+ case Func00 of
+ {cleanup,F0} ->
+ {true,F0};
+ _ ->
+ {false,Func00}
+ end,
{Func,FuncSpec,HookFunc} =
case Func0 of
{end_per_testcase_not_run,F} ->
@@ -742,6 +749,8 @@ end_tc(Mod,Func0,TCPid,Result,Args,Return) ->
case HookFunc of
undefined ->
{ok,Result};
+ _ when OnlyCleanup ->
+ {ok,Result};
_ ->
case ct_hooks:end_tc(Suite,HookFunc,Args,Result,Return) of
'$ct_no_change' ->
@@ -752,6 +761,8 @@ end_tc(Mod,Func0,TCPid,Result,Args,Return) ->
end,
FinalResult =
case get('$test_server_framework_test') of
+ _ when OnlyCleanup ->
+ Result1;
undefined ->
%% send sync notification so that event handlers may print
%% in the log file before it gets closed
@@ -1056,21 +1067,40 @@ group_or_func(Func, _Config) ->
%%% should be returned.
get_suite(Mod, all) ->
- case catch apply(Mod, groups, []) of
- {'EXIT',_} ->
- get_all(Mod, []);
- GroupDefs when is_list(GroupDefs) ->
- case catch ct_groups:find_groups(Mod, all, all, GroupDefs) of
- {error,_} = Error ->
- %% this makes test_server call error_in_suite as first
- %% (and only) test case so we can report Error properly
- [{?MODULE,error_in_suite,[[Error]]}];
- ConfTests ->
- get_all(Mod, ConfTests)
- end;
- _ ->
+ case safe_apply_groups_0(Mod,{ok,[]}) of
+ {ok,GroupDefs} ->
+ try ct_groups:find_groups(Mod, all, all, GroupDefs) of
+ ConfTests when is_list(ConfTests) ->
+ get_all(Mod, ConfTests)
+ catch
+ throw:{error,Error} ->
+ [{?MODULE,error_in_suite,[[{error,Error}]]}];
+ _:Error:S ->
+ [{?MODULE,error_in_suite,[[{error,{Error,S}}]]}]
+ end;
+ {error,{bad_return,_Bad}} ->
E = "Bad return value from "++atom_to_list(Mod)++":groups/0",
- [{?MODULE,error_in_suite,[[{error,list_to_atom(E)}]]}]
+ [{?MODULE,error_in_suite,[[{error,list_to_atom(E)}]]}];
+ {error,{bad_hook_return,Bad}} ->
+ E = "Bad return value from post_groups/2 hook function",
+ [{?MODULE,error_in_suite,[[{error,{list_to_atom(E),Bad}}]]}];
+ {error,{failed,ExitReason}} ->
+ case ct_util:get_testdata({error_in_suite,Mod}) of
+ undefined ->
+ ErrStr = io_lib:format("~n*** ERROR *** "
+ "~w:groups/0 failed: ~p~n",
+ [Mod,ExitReason]),
+ io:format(?def_gl, ErrStr, []),
+ %% save the error info so it doesn't get printed twice
+ ct_util:set_testdata_async({{error_in_suite,Mod},
+ ExitReason});
+ _ExitReason ->
+ ct_util:delete_testdata({error_in_suite,Mod})
+ end,
+ Reason = list_to_atom(atom_to_list(Mod)++":groups/0 failed"),
+ [{?MODULE,error_in_suite,[[{error,Reason}]]}];
+ {error,What} ->
+ [{?MODULE,error_in_suite,[[{error,What}]]}]
end;
%%!============================================================
@@ -1080,54 +1110,74 @@ get_suite(Mod, all) ->
%% group
get_suite(Mod, Group={conf,Props,_Init,TCs,_End}) ->
- Name = ?val(name, Props),
- case catch apply(Mod, groups, []) of
- {'EXIT',_} ->
- [Group];
- GroupDefs when is_list(GroupDefs) ->
- case catch ct_groups:find_groups(Mod, Name, TCs, GroupDefs) of
- {error,_} = Error ->
- %% this makes test_server call error_in_suite as first
- %% (and only) test case so we can report Error properly
- [{?MODULE,error_in_suite,[[Error]]}];
- [] ->
- [];
- ConfTests ->
- case lists:member(skipped, Props) of
- true ->
- %% a *subgroup* specified *only* as skipped (and not
- %% as an explicit test) should not be returned, or
- %% init/end functions for top groups will be executed
- case catch ?val(name, element(2, hd(ConfTests))) of
- Name -> % top group
- ct_groups:delete_subs(ConfTests, ConfTests);
- _ ->
- []
- end;
- false ->
- ConfTests1 = ct_groups:delete_subs(ConfTests,
- ConfTests),
- case ?val(override, Props) of
- undefined ->
- ConfTests1;
- [] ->
- ConfTests1;
- ORSpec ->
- ORSpec1 = if is_tuple(ORSpec) -> [ORSpec];
- true -> ORSpec end,
- ct_groups:search_and_override(ConfTests1,
- ORSpec1, Mod)
- end
- end
- end;
- _ ->
+ case safe_apply_groups_0(Mod,{ok,[Group]}) of
+ {ok,GroupDefs} ->
+ Name = ?val(name, Props),
+ try ct_groups:find_groups(Mod, Name, TCs, GroupDefs) of
+ [] ->
+ [];
+ ConfTests when is_list(ConfTests) ->
+ case lists:member(skipped, Props) of
+ true ->
+ %% a *subgroup* specified *only* as skipped (and not
+ %% as an explicit test) should not be returned, or
+ %% init/end functions for top groups will be executed
+ try ?val(name, element(2, hd(ConfTests))) of
+ Name -> % top group
+ ct_groups:delete_subs(ConfTests, ConfTests);
+ _ -> []
+ catch
+ _:_ -> []
+ end;
+ false ->
+ ConfTests1 = ct_groups:delete_subs(ConfTests,
+ ConfTests),
+ case ?val(override, Props) of
+ undefined ->
+ ConfTests1;
+ [] ->
+ ConfTests1;
+ ORSpec ->
+ ORSpec1 = if is_tuple(ORSpec) -> [ORSpec];
+ true -> ORSpec end,
+ ct_groups:search_and_override(ConfTests1,
+ ORSpec1, Mod)
+ end
+ end
+ catch
+ throw:{error,Error} ->
+ [{?MODULE,error_in_suite,[[{error,Error}]]}];
+ _:Error:S ->
+ [{?MODULE,error_in_suite,[[{error,{Error,S}}]]}]
+ end;
+ {error,{bad_return,_Bad}} ->
E = "Bad return value from "++atom_to_list(Mod)++":groups/0",
- [{?MODULE,error_in_suite,[[{error,list_to_atom(E)}]]}]
+ [{?MODULE,error_in_suite,[[{error,list_to_atom(E)}]]}];
+ {error,{bad_hook_return,Bad}} ->
+ E = "Bad return value from post_groups/2 hook function",
+ [{?MODULE,error_in_suite,[[{error,{list_to_atom(E),Bad}}]]}];
+ {error,{failed,ExitReason}} ->
+ case ct_util:get_testdata({error_in_suite,Mod}) of
+ undefined ->
+ ErrStr = io_lib:format("~n*** ERROR *** "
+ "~w:groups/0 failed: ~p~n",
+ [Mod,ExitReason]),
+ io:format(?def_gl, ErrStr, []),
+ %% save the error info so it doesn't get printed twice
+ ct_util:set_testdata_async({{error_in_suite,Mod},
+ ExitReason});
+ _ExitReason ->
+ ct_util:delete_testdata({error_in_suite,Mod})
+ end,
+ Reason = list_to_atom(atom_to_list(Mod)++":groups/0 failed"),
+ [{?MODULE,error_in_suite,[[{error,Reason}]]}];
+ {error,What} ->
+ [{?MODULE,error_in_suite,[[{error,What}]]}]
end;
%% testcase
get_suite(Mod, Name) ->
- get_seq(Mod, Name).
+ get_seq(Mod, Name).
%%%-----------------------------------------------------------------
@@ -1161,21 +1211,48 @@ get_all_cases1(_, []) ->
%%%-----------------------------------------------------------------
-get_all(Mod, ConfTests) ->
- case catch apply(Mod, all, []) of
- {'EXIT',{undef,[{Mod,all,[],_} | _]}} ->
+get_all(Mod, ConfTests) ->
+ case safe_apply_all_0(Mod) of
+ {ok,AllTCs} ->
+ %% expand group references using ConfTests
+ try ct_groups:expand_groups(AllTCs, ConfTests, Mod) of
+ {error,_} = Error ->
+ [{?MODULE,error_in_suite,[[Error]]}];
+ Tests0 ->
+ Tests = ct_groups:delete_subs(Tests0, Tests0),
+ expand_tests(Mod, Tests)
+ catch
+ throw:{error,Error} ->
+ [{?MODULE,error_in_suite,[[{error,Error}]]}];
+ _:Error:S ->
+ [{?MODULE,error_in_suite,[[{error,{Error,S}}]]}]
+ end;
+ Skip = {skip,_Reason} ->
+ Skip;
+ {error,undef} ->
+ Reason =
+ case code:which(Mod) of
+ non_existing ->
+ list_to_atom(
+ atom_to_list(Mod)++
+ " cannot be compiled or loaded");
+ _ ->
+ list_to_atom(
+ atom_to_list(Mod)++":all/0 is missing")
+ end,
+ %% this makes test_server call error_in_suite as first
+ %% (and only) test case so we can report Reason properly
+ [{?MODULE,error_in_suite,[[{error,Reason}]]}];
+ {error,{bad_return,_Bad}} ->
Reason =
- case code:which(Mod) of
- non_existing ->
- list_to_atom(atom_to_list(Mod)++
- " cannot be compiled or loaded");
- _ ->
- list_to_atom(atom_to_list(Mod)++":all/0 is missing")
- end,
- %% this makes test_server call error_in_suite as first
- %% (and only) test case so we can report Reason properly
+ list_to_atom("Bad return value from "++
+ atom_to_list(Mod)++":all/0"),
[{?MODULE,error_in_suite,[[{error,Reason}]]}];
- {'EXIT',ExitReason} ->
+ {error,{bad_hook_return,Bad}} ->
+ Reason =
+ list_to_atom("Bad return value from post_all/3 hook function"),
+ [{?MODULE,error_in_suite,[[{error,{Reason,Bad}}]]}];
+ {error,{failed,ExitReason}} ->
case ct_util:get_testdata({error_in_suite,Mod}) of
undefined ->
ErrStr = io_lib:format("~n*** ERROR *** "
@@ -1192,28 +1269,8 @@ get_all(Mod, ConfTests) ->
%% this makes test_server call error_in_suite as first
%% (and only) test case so we can report Reason properly
[{?MODULE,error_in_suite,[[{error,Reason}]]}];
- AllTCs when is_list(AllTCs) ->
- case catch save_seqs(Mod,AllTCs) of
- {error,What} ->
- [{?MODULE,error_in_suite,[[{error,What}]]}];
- SeqsAndTCs ->
- %% expand group references in all() using ConfTests
- case catch ct_groups:expand_groups(SeqsAndTCs,
- ConfTests,
- Mod) of
- {error,_} = Error ->
- [{?MODULE,error_in_suite,[[Error]]}];
- Tests ->
- ct_groups:delete_subs(Tests, Tests)
- end
- end;
- Skip = {skip,_Reason} ->
- Skip;
- _ ->
- Reason =
- list_to_atom("Bad return value from "++
- atom_to_list(Mod)++":all/0"),
- [{?MODULE,error_in_suite,[[{error,Reason}]]}]
+ {error,What} ->
+ [{?MODULE,error_in_suite,[[{error,What}]]}]
end.
%%!============================================================
@@ -1571,3 +1628,74 @@ get_html_wrapper(TestName, PrintLabel, Cwd, TableCols, Encoding) ->
%%% -spec get_log_dir() -> {ok,LogDir}
get_log_dir() ->
ct_logs:get_log_dir(true).
+
+%%%-----------------------------------------------------------------
+%%% Call all and group callbacks and post_* hooks with error handling
+safe_apply_all_0(Mod) ->
+ try apply(Mod, all, []) of
+ AllTCs0 when is_list(AllTCs0) ->
+ try save_seqs(Mod,AllTCs0) of
+ SeqsAndTCs when is_list(SeqsAndTCs) ->
+ all_hook(Mod,SeqsAndTCs)
+ catch throw:{error,What} ->
+ {error,What}
+ end;
+ {skip,_}=Skip ->
+ all_hook(Mod,Skip);
+ Bad ->
+ {error,{bad_return,Bad}}
+ catch
+ _:Reason:Stacktrace ->
+ handle_callback_crash(Reason,Stacktrace,Mod,all,{error,undef})
+ end.
+
+all_hook(Mod, All) ->
+ case ct_hooks:all(Mod, All) of
+ AllTCs when is_list(AllTCs) ->
+ {ok,AllTCs};
+ {skip,_}=Skip ->
+ Skip;
+ {fail,Reason} ->
+ {error,Reason};
+ Bad ->
+ {error,{bad_hook_return,Bad}}
+ end.
+
+safe_apply_groups_0(Mod,Default) ->
+ try apply(Mod, groups, []) of
+ GroupDefs when is_list(GroupDefs) ->
+ case ct_hooks:groups(Mod, GroupDefs) of
+ GroupDefs1 when is_list(GroupDefs1) ->
+ {ok,GroupDefs1};
+ {fail,Reason} ->
+ {error,Reason};
+ Bad ->
+ {error,{bad_hook_return,Bad}}
+ end;
+ Bad ->
+ {error,{bad_return,Bad}}
+ catch
+ _:Reason:Stacktrace ->
+ handle_callback_crash(Reason,Stacktrace,Mod,groups,Default)
+ end.
+
+handle_callback_crash(undef,[{Mod,Func,[],_}|_],Mod,Func,Default) ->
+ case ct_hooks:Func(Mod, []) of
+ [] ->
+ Default;
+ List when is_list(List) ->
+ {ok,List};
+ {fail,Reason} ->
+ {error,Reason};
+ Bad ->
+ {error,{bad_hook_return,Bad}}
+ end;
+handle_callback_crash(Reason,Stacktrace,_Mod,_Func,_Default) ->
+ {error,{failed,{Reason,Stacktrace}}}.
+
+expand_tests(Mod, [{testcase,Case,[Prop]}|Tests]) ->
+ [{repeat,{Mod,Case},Prop}|expand_tests(Mod,Tests)];
+expand_tests(Mod,[Test|Tests]) ->
+ [Test|expand_tests(Mod,Tests)];
+expand_tests(_Mod,[]) ->
+ [].
diff --git a/lib/common_test/src/ct_groups.erl b/lib/common_test/src/ct_groups.erl
index d867069dce..f4b12c41c0 100644
--- a/lib/common_test/src/ct_groups.erl
+++ b/lib/common_test/src/ct_groups.erl
@@ -101,23 +101,34 @@ find(Mod, [], TCs, Tests, _Known, _Defs, false) ->
[{Mod,TC}];
({group,_}) ->
[];
+ ({testcase,TC,[Prop]}) when is_atom(TC), TC ==all ->
+ [{repeat,{Mod,TC},Prop}];
({_,_}=TC) when TCs == all ->
[TC];
- (TC) ->
- if is_atom(TC) ->
- Tuple = {Mod,TC},
- case lists:member(Tuple, TCs) of
- true ->
- [Tuple];
- false ->
- case lists:member(TC, TCs) of
- true -> [{Mod,TC}];
- false -> []
- end
- end;
- true ->
- []
- end
+ (TC) when is_atom(TC) ->
+ Tuple = {Mod,TC},
+ case lists:member(Tuple, TCs) of
+ true ->
+ [Tuple];
+ false ->
+ case lists:member(TC, TCs) of
+ true -> [Tuple];
+ false -> []
+ end
+ end;
+ ({testcase,TC,[Prop]}) when is_atom(TC) ->
+ Tuple = {Mod,TC},
+ case lists:member(Tuple, TCs) of
+ true ->
+ [{repeat,Tuple,Prop}];
+ false ->
+ case lists:member(TC, TCs) of
+ true -> [{repeat,Tuple,Prop}];
+ false -> []
+ end
+ end;
+ (_) ->
+ []
end, Tests),
if Cases == [] -> ['NOMATCH'];
true -> Cases
@@ -172,12 +183,19 @@ find(Mod, GrNames, all, [{M,TC} | Gs], Known,
Defs, FindAll) when is_atom(M), M /= group, is_atom(TC) ->
[{M,TC} | find(Mod, GrNames, all, Gs, Known, Defs, FindAll)];
+%% Save test case
+find(Mod, GrNames, all, [{testcase,TC,[Prop]} | Gs], Known,
+ Defs, FindAll) when is_atom(TC) ->
+ [{repeat,{Mod,TC},Prop} | find(Mod, GrNames, all, Gs, Known, Defs, FindAll)];
+
%% Check if test case should be saved
-find(Mod, GrNames, TCs, [TC | Gs], Known,
- Defs, FindAll) when is_atom(TC) orelse
- ((size(TC) == 2) and (element(1,TC) /= group)) ->
+find(Mod, GrNames, TCs, [TC | Gs], Known, Defs, FindAll)
+ when is_atom(TC) orelse
+ ((size(TC) == 3) andalso (element(1,TC) == testcase)) orelse
+ ((size(TC) == 2) and (element(1,TC) /= group)) ->
Case =
- if is_atom(TC) ->
+ case TC of
+ _ when is_atom(TC) ->
Tuple = {Mod,TC},
case lists:member(Tuple, TCs) of
true ->
@@ -188,7 +206,18 @@ find(Mod, GrNames, TCs, [TC | Gs], Known,
false -> []
end
end;
- true ->
+ {testcase,TC0,[Prop]} when is_atom(TC0) ->
+ Tuple = {Mod,TC0},
+ case lists:member(Tuple, TCs) of
+ true ->
+ {repeat,Tuple,Prop};
+ false ->
+ case lists:member(TC0, TCs) of
+ true -> {repeat,{Mod,TC0},Prop};
+ false -> []
+ end
+ end;
+ _ ->
case lists:member(TC, TCs) of
true -> {Mod,TC};
false -> []
@@ -289,12 +318,22 @@ modify_tc_list(GrSpecTs, TSCs, []) ->
modify_tc_list1(GrSpecTs, TSCs);
modify_tc_list(GrSpecTs, _TSCs, _) ->
- [Test || Test <- GrSpecTs, not is_atom(Test)].
+ [Test || Test <- GrSpecTs, not is_atom(Test), element(1,Test)=/=testcase].
modify_tc_list1(GrSpecTs, TSCs) ->
%% remove all cases in group tc list that should not be executed
GrSpecTs1 =
- lists:flatmap(fun(Test) when is_tuple(Test),
+ lists:flatmap(fun(Test={testcase,TC,_}) ->
+ case lists:keysearch(TC, 2, TSCs) of
+ {value,_} ->
+ [Test];
+ _ ->
+ case lists:member(TC, TSCs) of
+ true -> [Test];
+ false -> []
+ end
+ end;
+ (Test) when is_tuple(Test),
(size(Test) > 2) ->
[Test];
(Test={group,_}) ->
diff --git a/lib/common_test/src/ct_hooks.erl b/lib/common_test/src/ct_hooks.erl
index 49587b3edd..97c349578f 100644
--- a/lib/common_test/src/ct_hooks.erl
+++ b/lib/common_test/src/ct_hooks.erl
@@ -22,6 +22,8 @@
%% API Exports
-export([init/1]).
+-export([groups/2]).
+-export([all/2]).
-export([init_tc/3]).
-export([end_tc/5]).
-export([terminate/1]).
@@ -37,7 +39,8 @@
opts = [],
prio = ctfirst }]).
--record(ct_hook_config, {id, module, prio, scope, opts = [], state = []}).
+-record(ct_hook_config, {id, module, prio, scope, opts = [],
+ state = [], groups = []}).
%% -------------------------------------------------------------------------
%% API Functions
@@ -49,6 +52,48 @@ init(Opts) ->
call(get_builtin_hooks(Opts) ++ get_new_hooks(Opts, undefined),
ok, init, []).
+%% Call the post_groups/2 hook callback
+groups(Mod, Groups) ->
+ Info = try proplists:get_value(ct_hooks, Mod:suite(), []) of
+ CTHooks when is_list(CTHooks) ->
+ [{?config_name,CTHooks}];
+ CTHook when is_atom(CTHook) ->
+ [{?config_name,[CTHook]}]
+ catch _:_ ->
+ %% since this might be the first time Mod:suite()
+ %% is called, and it might just fail or return
+ %% something bad, we allow any failure here - it
+ %% will be catched later if there is something
+ %% really wrong.
+ [{?config_name,[]}]
+ end,
+ case call(fun call_generic/3, Info ++ [{'$ct_groups',Groups}], [post_groups, Mod]) of
+ [{'$ct_groups',NewGroups}] ->
+ NewGroups;
+ Other ->
+ Other
+ end.
+
+%% Call the post_all/3 hook callback
+all(Mod, Tests) ->
+ Info = try proplists:get_value(ct_hooks, Mod:suite(), []) of
+ CTHooks when is_list(CTHooks) ->
+ [{?config_name,CTHooks}];
+ CTHook when is_atom(CTHook) ->
+ [{?config_name,[CTHook]}]
+ catch _:_ ->
+ %% just allow any failure here - it will be catched
+ %% later if there is something really wrong.
+ [{?config_name,[]}]
+ end,
+ case call(fun call_generic/3, Info ++ [{'$ct_all',Tests}], [post_all, Mod]) of
+ [{'$ct_all',NewTests}] ->
+ NewTests;
+ Other ->
+ Other
+ end.
+
+%% Called after all suites are done.
-spec terminate(Hooks :: term()) ->
ok.
terminate(Hooks) ->
@@ -80,6 +125,7 @@ init_tc(Mod, init_per_suite, Config) ->
[{?config_name,[]}]
end,
call(fun call_generic/3, Config ++ Info, [pre_init_per_suite, Mod]);
+
init_tc(Mod, end_per_suite, Config) ->
call(fun call_generic/3, Config, [pre_end_per_suite, Mod]);
init_tc(Mod, {init_per_group, GroupName, Properties}, Config) ->
@@ -153,7 +199,7 @@ call_id(#ct_hook_config{ module = Mod, opts = Opts} = Hook, Config, Scope) ->
{Config, Hook#ct_hook_config{ id = Id, scope = scope(Scope)}}.
call_init(#ct_hook_config{ module = Mod, opts = Opts, id = Id, prio = P} = Hook,
- Config,_Meta) ->
+ Config, _Meta) ->
case Mod:init(Id, Opts) of
{ok, NewState} when P =:= undefined ->
{Config, Hook#ct_hook_config{ state = NewState, prio = 0 } };
@@ -184,6 +230,18 @@ call_generic(Hook, Value, Meta) ->
call_generic_fallback(Hook, Value, Meta) ->
do_call_generic(Hook, Value, Meta, true).
+do_call_generic(#ct_hook_config{ module = Mod} = Hook,
+ [{'$ct_groups',Groups}], [post_groups | Args], Fallback) ->
+ NewGroups = catch_apply(Mod, post_groups, Args ++ [Groups],
+ Groups, Fallback),
+ {[{'$ct_groups',NewGroups}], Hook#ct_hook_config{ groups = NewGroups } };
+
+do_call_generic(#ct_hook_config{ module = Mod, groups = Groups} = Hook,
+ [{'$ct_all',Tests}], [post_all | Args], Fallback) ->
+ NewTests = catch_apply(Mod, post_all, Args ++ [Tests, Groups],
+ Tests, Fallback),
+ {[{'$ct_all',NewTests}], Hook};
+
do_call_generic(#ct_hook_config{ module = Mod, state = State} = Hook,
Value, [Function | Args], Fallback) ->
{NewValue, NewState} = catch_apply(Mod, Function, Args ++ [Value, State],
@@ -218,6 +276,12 @@ call([{Hook, call_id, NextFun} | Rest], Config, Meta, Hooks) ->
Rest ++ [{NewId, call_init}]};
ExistingHook when is_tuple(ExistingHook) ->
{Hooks, Rest};
+ _ when hd(Meta)=:=post_groups; hd(Meta)=:=post_all ->
+ %% If CTH is started because of a call from
+ %% groups/2 or all/2, CTH:init/1 must not be
+ %% called (the suite scope should be used).
+ {Hooks ++ [NewHook],
+ Rest ++ [{NewId,NextFun}]};
_ ->
{Hooks ++ [NewHook],
Rest ++ [{NewId, call_init}, {NewId,NextFun}]}
@@ -226,8 +290,8 @@ call([{Hook, call_id, NextFun} | Rest], Config, Meta, Hooks) ->
catch Error:Reason:Trace ->
ct_logs:log("Suite Hook","Failed to start a CTH: ~tp:~tp",
[Error,{Reason,Trace}]),
- call([], {fail,"Failed to start CTH"
- ", see the CT Log for details"}, Meta, Hooks)
+ call([], {fail,"Failed to start CTH, "
+ "see the CT Log for details"}, Meta, Hooks)
end;
call([{HookId, call_init} | Rest], Config, Meta, Hooks) ->
call([{HookId, fun call_init/3} | Rest], Config, Meta, Hooks);
@@ -267,6 +331,10 @@ scope([pre_init_per_suite, SuiteName|_]) ->
[post_end_per_suite, SuiteName];
scope([post_init_per_suite, SuiteName|_]) ->
[post_end_per_suite, SuiteName];
+scope([post_groups, SuiteName|_]) ->
+ [post_groups, SuiteName];
+scope([post_all, SuiteName|_]) ->
+ [post_all, SuiteName];
scope(init) ->
none.
@@ -353,6 +421,7 @@ resort(Calls,Hooks,[F|_R]) when F == pre_end_per_testcase;
F == pre_end_per_suite;
F == post_end_per_suite ->
lists:reverse(resort(Calls,Hooks));
+
resort(Calls,Hooks,_Meta) ->
resort(Calls,Hooks).
diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl
index 814b80b8bd..ca262b350f 100644
--- a/lib/common_test/src/ct_logs.erl
+++ b/lib/common_test/src/ct_logs.erl
@@ -149,7 +149,7 @@ close(Info, StartDir) ->
ok;
CacheBin ->
%% save final version of the log cache to file
- _ = file:write_file(?log_cache_name,CacheBin),
+ write_log_cache(CacheBin),
put(ct_log_cache,undefined)
end
end,
@@ -2022,7 +2022,7 @@ update_all_runs_in_cache(AllRunsData) ->
%% read from file as long as this logger process is alive
put(ct_log_cache,term_to_binary(LogCache));
_ ->
- file:write_file(?log_cache_name,term_to_binary(LogCache))
+ write_log_cache(term_to_binary(LogCache))
end;
SavedLogCache ->
update_all_runs_in_cache(AllRunsData,binary_to_term(SavedLogCache))
@@ -2036,7 +2036,7 @@ update_all_runs_in_cache(AllRunsData, LogCache) ->
%% read from file as long as this logger process is alive
put(ct_log_cache,term_to_binary(LogCache1));
_ ->
- file:write_file(?log_cache_name,term_to_binary(LogCache1))
+ write_log_cache(term_to_binary(LogCache1))
end.
sort_all_runs(Dirs) ->
@@ -2668,7 +2668,7 @@ update_tests_in_cache(TempData,LogCache=#log_cache{tests=Tests}) ->
{_Pid,_Pid} ->
put(ct_log_cache,CacheBin);
_ ->
- file:write_file(?log_cache_name,CacheBin)
+ write_log_cache(CacheBin)
end.
%%
@@ -3400,3 +3400,9 @@ unexpected_io(Pid, _Category, _Importance, Content, CtLogFd, EscChars) ->
Data = io_lib:format("~ts", [lists:foldl(IoFun, [], Content)]),
test_server_io:print_unexpected(Data),
ok.
+
+write_log_cache(LogCacheBin) when is_binary(LogCacheBin) ->
+ TmpFile = ?log_cache_name++".tmp",
+ _ = file:write_file(TmpFile,LogCacheBin),
+ _ = file:rename(TmpFile,?log_cache_name),
+ ok.
diff --git a/lib/common_test/src/test_server.erl b/lib/common_test/src/test_server.erl
index 9eda3f2152..756cd4d692 100644
--- a/lib/common_test/src/test_server.erl
+++ b/lib/common_test/src/test_server.erl
@@ -384,8 +384,8 @@ run_test_case_apply({CaseNum,Mod,Func,Args,Name,RunInit,TimetrapData}) ->
{Result,DetFail,ProcBef,ProcAft}.
-type tc_status() :: 'starting' | 'running' | 'init_per_testcase' |
- 'end_per_testcase' | {'framework',atom(),atom()} |
- 'tc'.
+ 'end_per_testcase' | {'framework',{atom(),atom(),list}} |
+ 'tc'.
-record(st,
{
ref :: reference(),
@@ -653,8 +653,8 @@ handle_tc_exit({testcase_aborted,{user_timetrap_error,_}=Msg,_}, St) ->
#st{config=Config,mf={Mod,Func},pid=Pid} = St,
spawn_fw_call(Mod, Func, Config, Pid, Msg, unknown, self()),
St;
-handle_tc_exit(Reason, #st{status={framework,FwMod,FwFunc},
- config=Config,pid=Pid}=St) ->
+handle_tc_exit(Reason, #st{status={framework,{FwMod,FwFunc,_}=FwMFA},
+ config=Config,mf={Mod,Func},pid=Pid}=St) ->
R = case Reason of
{timetrap_timeout,TVal,_} ->
{timetrap,TVal};
@@ -666,7 +666,7 @@ handle_tc_exit(Reason, #st{status={framework,FwMod,FwFunc},
Other
end,
Error = {framework_error,R},
- spawn_fw_call(FwMod, FwFunc, Config, Pid, Error, unknown, self()),
+ spawn_fw_call(Mod, Func, Config, Pid, {Error,FwMFA}, unknown, self()),
St;
handle_tc_exit(Reason, #st{status=tc,config=Config0,mf={Mod,Func},pid=Pid}=St)
when is_list(Config0) ->
@@ -870,22 +870,48 @@ spawn_fw_call(Mod,EPTC={end_per_testcase,Func},EndConf,Pid,
end,
spawn_link(FwCall);
-spawn_fw_call(FwMod,FwFunc,_,_Pid,{framework_error,FwError},_,SendTo) ->
+spawn_fw_call(Mod,Func,Conf,Pid,{{framework_error,FwError},
+ {FwMod,FwFunc,[A1,A2|_]}=FwMFA},_,SendTo) ->
FwCall =
fun() ->
ct_util:mark_process(),
- test_server_sup:framework_call(report, [framework_error,
- {{FwMod,FwFunc},
- FwError}]),
+ Time =
+ case FwError of
+ {timetrap,TVal} ->
+ TVal/1000;
+ _ ->
+ died
+ end,
+ {Ret,Loc,WarnOrError} =
+ cleanup_after_fw_error(Mod,Func,Conf,Pid,FwError,FwMFA),
Comment =
- lists:flatten(
- io_lib:format("<font color=\"red\">"
- "WARNING! ~w:~tw failed!</font>",
- [FwMod,FwFunc])),
+ case WarnOrError of
+ warn ->
+ group_leader() !
+ {printout,12,
+ "WARNING! ~w:~tw(~w,~tw,...) failed!\n"
+ " Reason: ~tp\n",
+ [FwMod,FwFunc,A1,A2,FwError]},
+ lists:flatten(
+ io_lib:format("<font color=\"red\">"
+ "WARNING! ~w:~tw(~w,~tw,...) "
+ "failed!</font>",
+ [FwMod,FwFunc,A1,A2]));
+ error ->
+ group_leader() !
+ {printout,12,
+ "Error! ~w:~tw(~w,~tw,...) failed!\n"
+ " Reason: ~tp\n",
+ [FwMod,FwFunc,A1,A2,FwError]},
+ lists:flatten(
+ io_lib:format("<font color=\"red\">"
+ "ERROR! ~w:~tw(~w,~tw,...) "
+ "failed!</font>",
+ [FwMod,FwFunc,A1,A2]))
+ end,
%% finished, report back
SendTo ! {self(),fw_notify_done,
- {died,{error,{FwMod,FwFunc,FwError}},
- {FwMod,FwFunc},[],Comment}}
+ {Time,Ret,Loc,[],Comment}}
end,
spawn_link(FwCall);
@@ -930,6 +956,163 @@ spawn_fw_call(Mod,Func,CurrConf,Pid,Error,Loc,SendTo) ->
end,
spawn_link(FwCall).
+cleanup_after_fw_error(_Mod,_Func,Conf,Pid,FwError,
+ {FwMod,FwFunc=init_tc,
+ [Mod,{init_per_testcase,Func}=IPTC|_]}) ->
+ %% Failed during pre_init_per_testcase, the test must be skipped
+ Skip = {auto_skip,{failed,{FwMod,FwFunc,FwError}}},
+ try begin do_end_tc_call(Mod,IPTC, {Pid,Skip,[Conf]}, FwError),
+ do_init_tc_call(Mod,{end_per_testcase_not_run,Func},
+ [Conf],{ok,[Conf]}),
+ do_end_tc_call(Mod,{end_per_testcase_not_run,Func},
+ {Pid,Skip,[Conf]}, FwError) end of
+ _ -> ok
+ catch
+ _:FwEndTCErr ->
+ exit({fw_notify_done,end_tc,FwEndTCErr})
+ end,
+ {Skip,{FwMod,FwFunc},error};
+cleanup_after_fw_error(_Mod,_Func,Conf,Pid,FwError,
+ {FwMod,FwFunc=end_tc,[Mod,{init_per_testcase,Func}|_]}) ->
+ %% Failed during post_init_per_testcase, the test must be skipped
+ Skip = {auto_skip,{failed,{FwMod,FwFunc,FwError}}},
+ try begin do_init_tc_call(Mod,{end_per_testcase_not_run,Func},
+ [Conf],{ok,[Conf]}),
+ do_end_tc_call(Mod,{end_per_testcase_not_run,Func},
+ {Pid,Skip,[Conf]}, FwError) end of
+ _ -> ok
+ catch
+ _:FwEndTCErr ->
+ exit({fw_notify_done,end_tc,FwEndTCErr})
+ end,
+ {Skip,{FwMod,FwFunc},error};
+cleanup_after_fw_error(_Mod,_Func,Conf,Pid,FwError,
+ {FwMod,FwFunc=init_tc,[Mod,{end_per_testcase,Func}|_]}) ->
+ %% Failed during pre_end_per_testcase. Warn about it.
+ {RetVal,Loc} =
+ case {proplists:get_value(tc_status, Conf),
+ proplists:get_value(tc_fail_loc, Conf, unknown)} of
+ {undefined,_} ->
+ {{failed,{FwMod,FwFunc,FwError}},{FwMod,FwFunc}};
+ {E = {failed,_Reason},unknown} ->
+ {E,[{Mod,Func}]};
+ {Result,FailLoc} ->
+ {Result,FailLoc}
+ end,
+ try begin do_end_tc_call(Mod,{end_per_testcase_not_run,Func},
+ {Pid,RetVal,[Conf]}, FwError) end of
+ _ -> ok
+ catch
+ _:FwEndTCErr ->
+ exit({fw_notify_done,end_tc,FwEndTCErr})
+ end,
+ {RetVal,Loc,warn};
+cleanup_after_fw_error(Mod,Func,Conf,Pid,FwError,
+ {FwMod,FwFunc=end_tc,[Mod,{end_per_testcase,Func}|_]}) ->
+ %% Failed during post_end_per_testcase. Warn about it.
+ {RetVal,Report,Loc} =
+ case {proplists:get_value(tc_status, Conf),
+ proplists:get_value(tc_fail_loc, Conf, unknown)} of
+ {undefined,_} ->
+ {{failed,{FwMod,FwFunc,FwError}},
+ {{FwMod,FwError},FwError},
+ {FwMod,FwFunc}};
+ {E = {failed,_Reason},unknown} ->
+ {E,{Mod,Func,E},[{Mod,Func}]};
+ {Result,FailLoc} ->
+ {Result,{Mod,Func,Result},FailLoc}
+ end,
+ try begin do_end_tc_call(Mod,{cleanup,{end_per_testcase_not_run,Func}},
+ {Pid,RetVal,[Conf]}, FwError) end of
+ _ -> ok
+ catch
+ _:FwEndTCErr ->
+ exit({fw_notify_done,end_tc,FwEndTCErr})
+ end,
+ test_server_sup:framework_call(report,[framework_error,Report]),
+ {RetVal,Loc,warn};
+cleanup_after_fw_error(Mod,Func,Conf,Pid,FwError,{FwMod,FwFunc=init_tc,_})
+ when Func =:= init_per_suite; Func =:=init_per_group ->
+ %% Failed during pre_init_per_suite or pre_init_per_group
+ RetVal = {failed,{FwMod,FwFunc,FwError}},
+ try do_end_tc_call(Mod,Func,{Pid,RetVal,[Conf]},FwError) of
+ _ -> ok
+ catch
+ _:FwEndTCErr ->
+ exit({fw_notify_done,end_tc,FwEndTCErr})
+ end,
+ {RetVal,{FwMod,FwFunc},error};
+cleanup_after_fw_error(Mod,Func,Conf,Pid,FwError,{FwMod,FwFunc=end_tc,_})
+ when Func =:= init_per_suite; Func =:=init_per_group ->
+ %% Failed during post_init_per_suite or post_init_per_group
+ RetVal = {failed,{FwMod,FwFunc,FwError}},
+ try do_end_tc_call(Mod,{cleanup,Func},{Pid,RetVal,[Conf]},FwError) of
+ _ -> ok
+ catch
+ _:FwEndTCErr ->
+ exit({fw_notify_done,end_tc,FwEndTCErr})
+ end,
+ ReportFunc =
+ case Func of
+ init_per_group ->
+ case proplists:get_value(tc_group_properties,Conf) of
+ undefined ->
+ {Func,unknown,[]};
+ GProps ->
+ Name = proplists:get_value(name,GProps),
+ {Func,Name,proplists:delete(name,GProps)}
+ end;
+ _ ->
+ Func
+ end,
+ test_server_sup:framework_call(report,[framework_error,
+ {Mod,ReportFunc,RetVal}]),
+ {RetVal,{FwMod,FwFunc},error};
+cleanup_after_fw_error(Mod,Func,Conf,Pid,FwError,{FwMod,FwFunc=init_tc,_})
+ when Func =:= end_per_suite; Func =:=end_per_group ->
+ %% Failed during pre_end_per_suite or pre_end_per_group
+ RetVal = {failed,{FwMod,FwFunc,FwError}},
+ try do_end_tc_call(Mod,Func,{Pid,RetVal,[Conf]},FwError) of
+ _ -> ok
+ catch
+ _:FwEndTCErr ->
+ exit({fw_notify_done,end_tc,FwEndTCErr})
+ end,
+ {RetVal,{FwMod,FwFunc},error};
+cleanup_after_fw_error(Mod,Func,Conf,Pid,FwError,{FwMod,FwFunc=end_tc,_})
+ when Func =:= end_per_suite; Func =:=end_per_group ->
+ %% Failed during post_end_per_suite or post_end_per_group
+ RetVal = {failed,{FwMod,FwFunc,FwError}},
+ try do_end_tc_call(Mod,{cleanup,Func},{Pid,RetVal,[Conf]},FwError) of
+ _ -> ok
+ catch
+ _:FwEndTCErr ->
+ exit({fw_notify_done,end_tc,FwEndTCErr})
+ end,
+ ReportFunc =
+ case Func of
+ end_per_group ->
+ case proplists:get_value(tc_group_properties,Conf) of
+ undefined ->
+ {Func,unknown,[]};
+ GProps ->
+ Name = proplists:get_value(name,GProps),
+ {Func,Name,proplists:delete(name,GProps)}
+ end;
+ _ ->
+ Func
+ end,
+ test_server_sup:framework_call(report,[framework_error,
+ {Mod,ReportFunc,RetVal}]),
+ {RetVal,{FwMod,FwFunc},error};
+cleanup_after_fw_error(_Mod,_Func,_Conf,_Pid,FwError,{FwMod,FwFunc,_}) ->
+ %% This is unexpected
+ test_server_sup:framework_call(report,
+ [framework_error,
+ {{FwMod,FwFunc},
+ FwError}]),
+ {FwError,{FwMod,FwFunc},error}.
+
%% The job proxy process forwards messages between the test case
%% process on a shielded node (and its descendants) and the job process.
%%
@@ -1105,6 +1288,9 @@ run_test_case_eval1(Mod, Func, Args, Name, RunInit, TCCallback) ->
EndConf1 =
user_callback(TCCallback, Mod, Func, 'end', EndConf),
+ %% save updated config in controller loop
+ set_tc_state(tc, EndConf1),
+
%% We can't handle fails or skips here
EndConf2 =
case do_init_tc_call(Mod,{end_per_testcase,Func},
diff --git a/lib/common_test/src/test_server_ctrl.erl b/lib/common_test/src/test_server_ctrl.erl
index 1518c6e8d6..003d08d70d 100644
--- a/lib/common_test/src/test_server_ctrl.erl
+++ b/lib/common_test/src/test_server_ctrl.erl
@@ -1443,6 +1443,8 @@ remove_conf([C={Mod,error_in_suite,_}|Cases], NoConf, Repeats) ->
true ->
remove_conf(Cases, [C|NoConf], Repeats)
end;
+remove_conf([C={repeat,_,_}|Cases], NoConf, _Repeats) ->
+ remove_conf(Cases, [C|NoConf], true);
remove_conf([C|Cases], NoConf, Repeats) ->
remove_conf(Cases, [C|NoConf], Repeats);
remove_conf([], NoConf, true) ->
@@ -2061,6 +2063,14 @@ add_init_and_end_per_suite([SkipCase|Cases], LastMod, LastRef, FwMod)
[SkipCase|add_init_and_end_per_suite(Cases, LastMod, LastRef, FwMod)];
add_init_and_end_per_suite([{conf,_,_,_}=Case|Cases], LastMod, LastRef, FwMod) ->
[Case|add_init_and_end_per_suite(Cases, LastMod, LastRef, FwMod)];
+add_init_and_end_per_suite([{repeat,{Mod,_},_}=Case|Cases], LastMod, LastRef, FwMod)
+ when Mod =/= LastMod, Mod =/= FwMod ->
+ {PreCases, NextMod, NextRef} =
+ do_add_init_and_end_per_suite(LastMod, LastRef, Mod, FwMod),
+ PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod,
+ NextRef, FwMod)];
+add_init_and_end_per_suite([{repeat,_,_}=Case|Cases], LastMod, LastRef, FwMod) ->
+ [Case|add_init_and_end_per_suite(Cases, LastMod, LastRef, FwMod)];
add_init_and_end_per_suite([{Mod,_}=Case|Cases], LastMod, LastRef, FwMod)
when Mod =/= LastMod, Mod =/= FwMod ->
{PreCases, NextMod, NextRef} =
@@ -2138,7 +2148,7 @@ do_add_init_and_end_per_suite(LastMod, LastRef, Mod, FwMod) ->
%% let's call a "fake" end_per_suite if it exists
case erlang:function_exported(FwMod, end_per_suite, 1) of
true ->
- [{conf,LastRef,[{suite,Mod}],
+ [{conf,LastRef,[{suite,LastMod}],
{FwMod,end_per_suite}}|Init];
false ->
[{conf,LastRef,[],{LastMod,end_per_suite}}|Init]
@@ -2926,6 +2936,29 @@ run_test_cases_loop([{conf,_Ref,_Props,_X}=Conf|_Cases0],
Config, _TimetrapData, _Mode, _Status) ->
erlang:error(badarg, [Conf,Config]);
+run_test_cases_loop([{repeat,Case,{RepeatType,N}}|Cases0], Config,
+ TimeTrapData, Mode, Status) ->
+ Ref = make_ref(),
+ Parallel = check_prop(parallel, Mode) =/= false,
+ Sequence = check_prop(sequence, Mode) =/= false,
+ RepeatStop = RepeatType=:=repeat_until_fail
+ orelse RepeatType=:=repeat_until_ok,
+
+ if Parallel andalso RepeatStop ->
+ %% Cannot check results of test case during parallal
+ %% execution, so only RepeatType=:=repeat is allowed in
+ %% combination with parallel groups.
+ erlang:error({illegal_combination,{parallel,RepeatType}});
+ Sequence andalso RepeatStop ->
+ %% Sequence is stop on fail + skip rest, so only
+ %% RepeatType=:=repeat makes sense inside a sequence.
+ erlang:error({illegal_combination,{sequence,RepeatType}});
+ true ->
+ Mode1 = [{Ref,[{repeat,{RepeatType,1,N}}],?now}|Mode],
+ run_test_cases_loop([Case | Cases0], Config, TimeTrapData,
+ Mode1, Status)
+ end;
+
run_test_cases_loop([{Mod,Case}|Cases], Config, TimetrapData, Mode, Status) ->
ActualCfg =
case get(test_server_create_priv_dir) of
@@ -2938,7 +2971,7 @@ run_test_cases_loop([{Mod,Case}|Cases], Config, TimetrapData, Mode, Status) ->
run_test_cases_loop([{Mod,Case,[ActualCfg]}|Cases], Config,
TimetrapData, Mode, Status);
-run_test_cases_loop([{Mod,Func,Args}|Cases], Config, TimetrapData, Mode, Status) ->
+run_test_cases_loop([{Mod,Func,Args}=Case|Cases], Config, TimetrapData, Mode0, Status) ->
{Num,RunInit} =
case FwMod = get_fw_mod(?MODULE) of
Mod when Func == error_in_suite ->
@@ -2948,6 +2981,14 @@ run_test_cases_loop([{Mod,Func,Args}|Cases], Config, TimetrapData, Mode, Status)
run_init}
end,
+ Mode =
+ case Mode0 of
+ [{_,[{repeat,{_,_,_}}],_}|RestMode] ->
+ RestMode;
+ _ ->
+ Mode0
+ end,
+
%% check the current execution mode and save info about the case if
%% detected that printouts to common log files is handled later
@@ -2975,36 +3016,42 @@ run_test_cases_loop([{Mod,Func,Args}|Cases], Config, TimetrapData, Mode, Status)
if is_tuple(RetVal) -> element(1,RetVal);
true -> undefined
end,
- {Failed,Status1} =
+ {Result,Failed,Status1} =
case RetTag of
Skip when Skip==skip; Skip==skipped ->
- {false,update_status(skipped, Mod, Func, Status)};
+ {skipped,false,update_status(skipped, Mod, Func, Status)};
Fail when Fail=='EXIT'; Fail==failed ->
- {true,update_status(failed, Mod, Func, Status)};
+ {failed,true,update_status(failed, Mod, Func, Status)};
_ when Time==died, RetVal=/=ok ->
- {true,update_status(failed, Mod, Func, Status)};
+ {failed,true,update_status(failed, Mod, Func, Status)};
_ ->
- {false,update_status(ok, Mod, Func, Status)}
+ {ok,false,update_status(ok, Mod, Func, Status)}
end,
case check_prop(sequence, Mode) of
false ->
+ {Cases1,Mode1} =
+ check_repeat_testcase(Case,Result,Cases,Mode0),
stop_minor_log_file(),
- run_test_cases_loop(Cases, Config, TimetrapData, Mode, Status1);
+ run_test_cases_loop(Cases1, Config, TimetrapData, Mode1, Status1);
Ref ->
%% the case is in a sequence; we must check the result and
%% determine if the following cases should run or be skipped
if not Failed -> % proceed with next case
+ {Cases1,Mode1} =
+ check_repeat_testcase(Case,Result,Cases,Mode0),
stop_minor_log_file(),
- run_test_cases_loop(Cases, Config, TimetrapData, Mode, Status1);
+ run_test_cases_loop(Cases1, Config, TimetrapData, Mode1, Status1);
true -> % skip rest of cases in sequence
print(minor, "~n*** ~tw failed.~n"
" Skipping all other cases in sequence.",
[Func]),
+ {Cases1,Mode1} =
+ check_repeat_testcase(Case,Result,Cases,Mode0),
Reason = {failed,{Mod,Func}},
- Cases2 = skip_cases_upto(Ref, Cases, Reason, tc,
+ Cases2 = skip_cases_upto(Ref, Cases1, Reason, tc,
Mode, auto_skip_case),
stop_minor_log_file(),
- run_test_cases_loop(Cases2, Config, TimetrapData, Mode, Status1)
+ run_test_cases_loop(Cases2, Config, TimetrapData, Mode1, Status1)
end
end;
%% the test case is being executed in parallel with the main process (and
@@ -3013,7 +3060,8 @@ run_test_cases_loop([{Mod,Func,Args}|Cases], Config, TimetrapData, Mode, Status)
%% io from Pid will be buffered by the test_server_io process and
%% handled later, so we have to save info about the case
queue_test_case_io(undefined, Pid, Num+1, Mod, Func),
- run_test_cases_loop(Cases, Config, TimetrapData, Mode, Status)
+ {Cases1,Mode1} = check_repeat_testcase(Case,ok,Cases,Mode0),
+ run_test_cases_loop(Cases1, Config, TimetrapData, Mode1, Status)
end;
%% TestSpec processing finished
@@ -3452,9 +3500,19 @@ modify_cases_upto1(Ref, {skip,Reason,FType,Mode,SkipType},
T, Orig, Alt)
end;
-%% next is some other case, ignore or copy
-modify_cases_upto1(Ref, {skip,_,_,_,_}=Op, [_Other|T], Orig, Alt) ->
+%% next is a repeated test case
+modify_cases_upto1(Ref, {skip,Reason,_,Mode,SkipType}=Op,
+ [{repeat,{_M,_F}=MF,_Repeat}|T], Orig, Alt) ->
+ modify_cases_upto1(Ref, Op, T, Orig, [{SkipType,{MF,Reason},Mode}|Alt]);
+
+%% next is an already skipped case, ignore or copy
+modify_cases_upto1(Ref, {skip,_,_,_,_}=Op, [{SkipType,_,_}|T], Orig, Alt)
+ when SkipType=:=skip_case; SkipType=:=auto_skip_case ->
modify_cases_upto1(Ref, Op, T, Orig, Alt);
+
+%% next is some other case, mark as skipped or copy
+modify_cases_upto1(Ref, {skip,Reason,_,Mode,SkipType}=Op, [Other|T], Orig, Alt) ->
+ modify_cases_upto1(Ref, Op, T, Orig, [{SkipType,{Other,Reason},Mode}|Alt]);
modify_cases_upto1(Ref, CopyOp, [C|T], Orig, Alt) ->
modify_cases_upto1(Ref, CopyOp, T, [C|Orig], [C|Alt]).
@@ -3842,6 +3900,10 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit,
{died,{timetrap_timeout,TimetrapTimeout}} ->
progress(failed, Num, Mod, Func, GrName, Loc,
timetrap_timeout, TimetrapTimeout, Comment, Style);
+ {died,Reason={auto_skip,_Why}} ->
+ %% died in init_per_testcase or in a hook in this context
+ progress(skip, Num, Mod, Func, GrName, Loc, Reason,
+ Time, Comment, Style);
{died,{Skip,Reason}} when Skip==skip; Skip==skipped ->
%% died in init_per_testcase
progress(skip, Num, Mod, Func, GrName, Loc, Reason,
@@ -4800,6 +4862,14 @@ collect_cases({make,InitMFA,CaseList,FinMFA}, St0, Mode) ->
{error,_Reason} = Error -> Error
end;
+collect_cases({repeat,{Module, Case}, Repeat}, St, Mode) ->
+ case catch collect_case([Case], St#cc{mod=Module}, [], Mode) of
+ {ok, [{Module,Case}], _} ->
+ {ok, [{repeat,{Module, Case}, Repeat}], St};
+ Other ->
+ {error,Other}
+ end;
+
collect_cases({Module, Cases}, St, Mode) when is_list(Cases) ->
case (catch collect_case(Cases, St#cc{mod=Module}, [], Mode)) of
Result = {ok,_,_} ->
@@ -5763,3 +5833,42 @@ encoding(File) ->
E ->
E
end.
+
+check_repeat_testcase(Case,Result,Cases,
+ [{Ref,[{repeat,RepeatData0}],StartTime}|Mode0]) ->
+ case do_update_repeat_data(Result,RepeatData0) of
+ false ->
+ {Cases,Mode0};
+ RepeatData ->
+ {[Case|Cases],[{Ref,[{repeat,RepeatData}],StartTime}|Mode0]}
+ end;
+check_repeat_testcase(_,_,Cases,Mode) ->
+ {Cases,Mode}.
+
+do_update_repeat_data(_,{RT,N,N}) when is_integer(N) ->
+ report_repeat_testcase(N,N),
+ report_stop_repeat_testcase(done,{RT,N}),
+ false;
+do_update_repeat_data(ok,{repeat_until_ok=RT,M,N}) ->
+ report_repeat_testcase(M,N),
+ report_stop_repeat_testcase(RT,{RT,N}),
+ false;
+do_update_repeat_data(failed,{repeat_until_fail=RT,M,N}) ->
+ report_repeat_testcase(M,N),
+ report_stop_repeat_testcase(RT,{RT,N}),
+ false;
+do_update_repeat_data(_,{RT,M,N}) when is_integer(M) ->
+ report_repeat_testcase(M,N),
+ {RT,M+1,N};
+do_update_repeat_data(_,{_,M,N}=RepeatData) ->
+ report_repeat_testcase(M,N),
+ RepeatData.
+
+report_stop_repeat_testcase(Reason,RepVal) ->
+ print(minor, "~n*** Stopping test case repeat operation: ~w", [Reason]),
+ print(1, "Stopping test case repeat operation: ~w", [RepVal]).
+
+report_repeat_testcase(M,forever) ->
+ print(minor, "~n=== Repeated test case: ~w of infinity", [M]);
+report_repeat_testcase(M,N) ->
+ print(minor, "~n=== Repeated test case: ~w of ~w", [M,N]).
diff --git a/lib/common_test/src/test_server_node.erl b/lib/common_test/src/test_server_node.erl
index c11b9071cf..f77d5a4966 100644
--- a/lib/common_test/src/test_server_node.erl
+++ b/lib/common_test/src/test_server_node.erl
@@ -18,7 +18,7 @@
%% %CopyrightEnd%
%%
-module(test_server_node).
--compile(r16).
+-compile(r20).
%%%
%%% The same compiled code for this module must be possible to load
diff --git a/lib/common_test/src/test_server_sup.erl b/lib/common_test/src/test_server_sup.erl
index 26e7534c6c..ab8066a88d 100644
--- a/lib/common_test/src/test_server_sup.erl
+++ b/lib/common_test/src/test_server_sup.erl
@@ -770,7 +770,7 @@ framework_call(Callback,Func,Args,DefaultReturn) ->
end,
case SetTcState of
true ->
- test_server:set_tc_state({framework,Mod,Func});
+ test_server:set_tc_state({framework,{Mod,Func,Args}});
false ->
ok
end,
diff --git a/lib/common_test/test/Makefile b/lib/common_test/test/Makefile
index ecd1f727a2..e510b74d6a 100644
--- a/lib/common_test/test/Makefile
+++ b/lib/common_test/test/Makefile
@@ -74,7 +74,9 @@ MODULES= \
ct_SUITE \
ct_keep_logs_SUITE \
ct_unicode_SUITE \
- ct_auto_clean_SUITE
+ ct_auto_clean_SUITE \
+ ct_util_SUITE \
+ ct_tc_repeat_SUITE
ERL_FILES= $(MODULES:%=%.erl)
HRL_FILES= test_server_test_lib.hrl
diff --git a/lib/common_test/test/ct_error_SUITE.erl b/lib/common_test/test/ct_error_SUITE.erl
index 7468ebe9d9..d31bd26273 100644
--- a/lib/common_test/test/ct_error_SUITE.erl
+++ b/lib/common_test/test/ct_error_SUITE.erl
@@ -648,33 +648,35 @@ test_events(cfg_error) ->
{?eh,tc_start,{cfg_error_11_SUITE,end_per_suite}},
{?eh,tc_done,{cfg_error_11_SUITE,end_per_suite,ok}},
{?eh,tc_start,{cfg_error_12_SUITE,tc1}},
- {?eh,tc_done,{ct_framework,init_tc,{framework_error,{timetrap,500}}}},
- {?eh,test_stats,{13,8,{0,19}}},
+ {?eh,tc_done,{cfg_error_12_SUITE,tc1,
+ {auto_skipped,
+ {failed,{ct_framework,init_tc,{timetrap,500}}}}}},
+ {?eh,test_stats,{13,7,{0,20}}},
{?eh,tc_start,{cfg_error_12_SUITE,tc2}},
{?eh,tc_done,{cfg_error_12_SUITE,tc2,{failed,
{cfg_error_12_SUITE,end_per_testcase,
{timetrap_timeout,500}}}}},
- {?eh,test_stats,{14,8,{0,19}}},
+ {?eh,test_stats,{14,7,{0,20}}},
{?eh,tc_start,{cfg_error_12_SUITE,tc3}},
{?eh,tc_done,{cfg_error_12_SUITE,tc3,ok}},
- {?eh,test_stats,{15,8,{0,19}}},
+ {?eh,test_stats,{15,7,{0,20}}},
{?eh,tc_start,{cfg_error_12_SUITE,tc4}},
{?eh,tc_done,{cfg_error_12_SUITE,tc4,{failed,
{cfg_error_12_SUITE,end_per_testcase,
{timetrap_timeout,500}}}}},
- {?eh,test_stats,{16,8,{0,19}}},
+ {?eh,test_stats,{16,7,{0,20}}},
{?eh,tc_start,{cfg_error_13_SUITE,init_per_suite}},
{?eh,tc_done,{cfg_error_13_SUITE,init_per_suite,ok}},
{?eh,tc_start,{cfg_error_13_SUITE,tc1}},
{?eh,tc_done,{cfg_error_13_SUITE,tc1,ok}},
- {?eh,test_stats,{17,8,{0,19}}},
+ {?eh,test_stats,{17,7,{0,20}}},
{?eh,tc_start,{cfg_error_13_SUITE,end_per_suite}},
{?eh,tc_done,{cfg_error_13_SUITE,end_per_suite,ok}},
{?eh,tc_start,{cfg_error_14_SUITE,init_per_suite}},
{?eh,tc_done,{cfg_error_14_SUITE,init_per_suite,ok}},
{?eh,tc_start,{cfg_error_14_SUITE,tc1}},
{?eh,tc_done,{cfg_error_14_SUITE,tc1,ok}},
- {?eh,test_stats,{18,8,{0,19}}},
+ {?eh,test_stats,{18,7,{0,20}}},
{?eh,tc_start,{cfg_error_14_SUITE,end_per_suite}},
{?eh,tc_done,{cfg_error_14_SUITE,end_per_suite,
{comment,
@@ -728,25 +730,30 @@ test_events(lib_error) ->
{lib_error_1_SUITE,no_lines_throw,{failed,{error,{thrown,catch_me_if_u_can}}}}},
{?eh,test_stats,{0,8,{0,0}}},
{?eh,tc_start,{lib_error_1_SUITE,init_tc_error}},
- {?eh,tc_done,{ct_framework,init_tc,
- {framework_error,{{badmatch,[1,2]},'_'}}}},
- {?eh,test_stats,{0,9,{0,0}}},
+ {?eh,tc_done,{lib_error_1_SUITE,init_tc_error,
+ {auto_skipped,
+ {failed,
+ {ct_framework,init_tc,
+ {{badmatch,[1,2]},'_'}}}}}},
+ {?eh,test_stats,{0,8,{0,1}}},
{?eh,tc_start,{lib_error_1_SUITE,init_tc_exit}},
- {?eh,tc_done,{ct_framework,init_tc,{framework_error,byebye}}},
- {?eh,test_stats,{0,10,{0,0}}},
+ {?eh,tc_done,{lib_error_1_SUITE,init_tc_exit,
+ {auto_skipped,{failed,{ct_framework,init_tc,byebye}}}}},
+ {?eh,test_stats,{0,8,{0,2}}},
{?eh,tc_start,{lib_error_1_SUITE,init_tc_throw}},
- {?eh,tc_done,{ct_framework,init_tc,{framework_error,catch_me_if_u_can}}},
- {?eh,test_stats,{0,11,{0,0}}},
+ {?eh,tc_done,{lib_error_1_SUITE,init_tc_throw,
+ {auto_skipped,{failed,{ct_framework,init_tc,
+ catch_me_if_u_can}}}}},
+ {?eh,test_stats,{0,8,{0,3}}},
{?eh,tc_start,{lib_error_1_SUITE,end_tc_error}},
- {?eh,tc_done,{ct_framework,end_tc,
- {framework_error,{{badmatch,[1,2]},'_'}}}},
- {?eh,test_stats,{0,12,{0,0}}},
+ {?eh,tc_done,{lib_error_1_SUITE,end_tc_error,ok}}, % warning in comment
+ {?eh,test_stats,{1,8,{0,3}}},
{?eh,tc_start,{lib_error_1_SUITE,end_tc_exit}},
- {?eh,tc_done,{ct_framework,end_tc,{framework_error,byebye}}},
- {?eh,test_stats,{0,13,{0,0}}},
+ {?eh,tc_done,{lib_error_1_SUITE,end_tc_exit,ok}}, % warning in comment
+ {?eh,test_stats,{2,8,{0,3}}},
{?eh,tc_start,{lib_error_1_SUITE,end_tc_throw}},
- {?eh,tc_done,{ct_framework,end_tc,{framework_error,catch_me_if_u_can}}},
- {?eh,test_stats,{0,14,{0,0}}},
+ {?eh,tc_done,{lib_error_1_SUITE,end_tc_throw,ok}}, % warning in comment
+ {?eh,test_stats,{3,8,{0,3}}},
{?eh,tc_start,{lib_error_1_SUITE,end_per_suite}},
{?eh,tc_done,{lib_error_1_SUITE,end_per_suite,ok}},
{?eh,test_done,{'DEF','STOP_TIME'}},
diff --git a/lib/common_test/test/ct_hooks_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE.erl
index 44b86b1dfe..340b8f3d52 100644
--- a/lib/common_test/test/ct_hooks_SUITE.erl
+++ b/lib/common_test/test/ct_hooks_SUITE.erl
@@ -73,11 +73,15 @@ all() ->
all(suite) ->
lists:reverse(
[
+ crash_groups, crash_all, bad_return_groups, bad_return_all,
+ illegal_values_groups, illegal_values_all, alter_groups, alter_all,
+ alter_all_to_skip, alter_all_from_skip,
one_cth, two_cth, faulty_cth_no_init, faulty_cth_id_no_init,
faulty_cth_exit_in_init, faulty_cth_exit_in_id,
faulty_cth_exit_in_init_scope_suite, minimal_cth,
minimal_and_maximal_cth, faulty_cth_undef,
scope_per_suite_cth, scope_per_group_cth, scope_suite_cth,
+ scope_suite_group_only_cth,
scope_per_suite_state_cth, scope_per_group_state_cth,
scope_suite_state_cth,
fail_pre_suite_cth, double_fail_pre_suite_cth,
@@ -152,6 +156,11 @@ scope_suite_cth(Config) when is_list(Config) ->
do_test(scope_suite_cth, "ct_scope_suite_cth_SUITE.erl",
[],Config).
+scope_suite_group_only_cth(Config) when is_list(Config) ->
+ do_test(scope_suite_group_only_cth,
+ "ct_scope_suite_group_only_cth_SUITE.erl",
+ [],Config,ok,2,[{group,g1}]).
+
scope_per_group_cth(Config) when is_list(Config) ->
do_test(scope_per_group_cth, "ct_scope_per_group_cth_SUITE.erl",
[],Config).
@@ -304,10 +313,74 @@ repeat_force_stop(Config) ->
[{force_stop,skip_rest},{duration,"000009"}]).
%% Test that expected callbacks, and only those, are called when a test
-%% are fails due to clash in config alias names
+%% fails due to clash in config alias names
config_clash(Config) ->
do_test(config_clash, "config_clash_SUITE.erl", [skip_cth], Config).
+%% Test post_groups and post_all hook callbacks, introduced by OTP-14746
+alter_groups(Config) ->
+ CfgFile = gen_config(?FUNCTION_NAME,
+ [{post_groups_return,[{new_group,[tc1,tc2]}]},
+ {post_all_return,[{group,new_group}]}],Config),
+ do_test(?FUNCTION_NAME, "all_and_groups_SUITE.erl", [all_and_groups_cth],
+ Config, ok, 2, [{config,CfgFile}]).
+
+alter_all(Config) ->
+ CfgFile = gen_config(?FUNCTION_NAME,[{post_all_return,[tc2]}],Config),
+ do_test(?FUNCTION_NAME, "all_and_groups_SUITE.erl", [all_and_groups_cth],
+ Config, ok, 2, [{config,CfgFile}]).
+
+alter_all_from_skip(Config) ->
+ CfgFile = gen_config(?FUNCTION_NAME,[{all_return,{skip,"skipped by all/0"}},
+ {post_all_return,[tc2]}],Config),
+ do_test(?FUNCTION_NAME, "all_and_groups_SUITE.erl", [all_and_groups_cth],
+ Config, ok, 2, [{config,CfgFile}]).
+
+alter_all_to_skip(Config) ->
+ CfgFile = gen_config(?FUNCTION_NAME,
+ [{post_all_return,{skip,"skipped by post_all/3"}}],
+ Config),
+ do_test(?FUNCTION_NAME, "all_and_groups_SUITE.erl", [all_and_groups_cth],
+ Config, ok, 2, [{config,CfgFile}]).
+
+bad_return_groups(Config) ->
+ CfgFile = gen_config(?FUNCTION_NAME,[{post_groups_return,not_a_list}],
+ Config),
+ do_test(?FUNCTION_NAME, "all_and_groups_SUITE.erl", [all_and_groups_cth],
+ Config, ok, 2, [{config,CfgFile}]).
+
+bad_return_all(Config) ->
+ CfgFile = gen_config(?FUNCTION_NAME,[{post_all_return,not_a_list}],
+ Config),
+ do_test(?FUNCTION_NAME, "all_and_groups_SUITE.erl", [all_and_groups_cth],
+ Config, ok, 2, [{config,CfgFile}]).
+
+illegal_values_groups(Config) ->
+ CfgFile = gen_config(?FUNCTION_NAME,
+ [{post_groups_return,[{new_group,[this_test_does_not_exist]},
+ this_is_not_a_group_def]}],
+ Config),
+ do_test(?FUNCTION_NAME, "all_and_groups_SUITE.erl", [all_and_groups_cth],
+ Config, ok, 2, [{config,CfgFile}]).
+
+illegal_values_all(Config) ->
+ CfgFile = gen_config(?FUNCTION_NAME,
+ [{post_all_return,[{group,this_group_does_not_exist},
+ {this_is_not_a_valid_term}]}],
+ Config),
+ do_test(?FUNCTION_NAME, "all_and_groups_SUITE.erl", [all_and_groups_cth],
+ Config, ok, 2, [{config,CfgFile}]).
+
+crash_groups(Config) ->
+ CfgFile = gen_config(?FUNCTION_NAME,[{post_groups_return,crash}],Config),
+ do_test(?FUNCTION_NAME, "all_and_groups_SUITE.erl", [all_and_groups_cth],
+ Config, ok, 2, [{config,CfgFile}]).
+
+crash_all(Config) ->
+ CfgFile = gen_config(?FUNCTION_NAME,[{post_all_return,crash}],Config),
+ do_test(?FUNCTION_NAME, "all_and_groups_SUITE.erl", [all_and_groups_cth],
+ Config, ok, 2, [{config,CfgFile}]).
+
%%%-----------------------------------------------------------------
%%% HELP FUNCTIONS
%%%-----------------------------------------------------------------
@@ -327,6 +400,7 @@ do_test(Tag, {WhatTag,Wildcard}, CTHs, Config, Res, EC, ExtraOpts) ->
filename:join([DataDir,"cth/tests",Wildcard])),
{Opts,ERPid} =
setup([{WhatTag,Files},{ct_hooks,CTHs},{label,Tag}|ExtraOpts], Config),
+
Res = ct_test_support:run(Opts, Config),
Events = ct_test_support:get_events(ERPid, Config),
@@ -352,6 +426,13 @@ reformat(Events, EH) ->
%reformat(Events, _EH) ->
% Events.
+gen_config(Name,KeyVals,Config) ->
+ PrivDir = ?config(priv_dir,Config),
+ File = filename:join(PrivDir,atom_to_list(Name)++".cfg"),
+ ok = file:write_file(File,[io_lib:format("~p.~n",[{Key,Value}])
+ || {Key,Value} <- KeyVals]),
+ File.
+
%%%-----------------------------------------------------------------
%%% TEST EVENTS
%%%-----------------------------------------------------------------
@@ -370,13 +451,16 @@ test_events(one_empty_cth) ->
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
{?eh,cth,{empty_cth,id,[[]]}},
{?eh,cth,{empty_cth,init,[{'_','_','_'},[]]}},
+ %% check that post_groups and post_all comes after init when hook
+ %% is installed with start flag/option.
+ {?eh,cth,{empty_cth,post_groups,[ct_cth_empty_SUITE,[]]}},
+ {?eh,cth,{empty_cth,post_all,[ct_cth_empty_SUITE,[test_case],[]]}},
{?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}},
{?eh,cth,{empty_cth,pre_init_per_suite,
[ct_cth_empty_SUITE,'$proplist',[]]}},
{?eh,cth,{empty_cth,post_init_per_suite,
[ct_cth_empty_SUITE,'$proplist','$proplist',[]]}},
{?eh,tc_done,{ct_cth_empty_SUITE,init_per_suite,ok}},
-
{?eh,tc_start,{ct_cth_empty_SUITE,test_case}},
{?eh,cth,{empty_cth,pre_init_per_testcase,[ct_cth_empty_SUITE,test_case,'$proplist',[]]}},
{?eh,cth,{empty_cth,post_init_per_testcase,[ct_cth_empty_SUITE,test_case,'$proplist','_',[]]}},
@@ -585,6 +669,10 @@ test_events(scope_suite_cth) ->
[
{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ %% check that post_groups and post_all comes before init when hook
+ %% is installed in suite/0
+ {?eh,cth,{'_',post_groups,['_',[]]}},
+ {?eh,cth,{'_',post_all,['_','_',[]]}},
{?eh,tc_start,{ct_scope_suite_cth_SUITE,init_per_suite}},
{?eh,cth,{'_',id,[[]]}},
{?eh,cth,{'_',init,['_',[]]}},
@@ -606,6 +694,34 @@ test_events(scope_suite_cth) ->
{?eh,stop_logging,[]}
];
+test_events(scope_suite_group_only_cth) ->
+ Suite = ct_scope_suite_group_only_cth_SUITE,
+ CTH = empty_cth,
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,1}},
+ %% check that post_groups and post_all comes before init when hook
+ %% is installed in suite/0
+ {?eh,cth,{CTH,post_groups,['_',['_']]}},
+ {negative,
+ {?eh,cth,{CTH,post_all,['_','_','_']}},
+ {?eh,tc_start,{Suite,init_per_suite}}},
+ {?eh,cth,{CTH,id,[[]]}},
+ {?eh,cth,{CTH,init,['_',[]]}},
+ {?eh,cth,{CTH,pre_init_per_suite,[Suite,'$proplist',mystate]}},
+ {?eh,cth,{CTH,post_init_per_suite,[Suite,'$proplist','$proplist',mystate]}},
+ {?eh,tc_done,{Suite,init_per_suite,ok}},
+
+ {?eh,tc_start,{Suite,end_per_suite}},
+ {?eh,cth,{CTH,pre_end_per_suite,[Suite,'$proplist',mystate]}},
+ {?eh,cth,{CTH,post_end_per_suite,[Suite,'$proplist','_',mystate]}},
+ {?eh,cth,{CTH,terminate,[mystate]}},
+ {?eh,tc_done,{Suite,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ];
+
test_events(scope_per_group_cth) ->
[
{?eh,start_logging,{'DEF','RUNDIR'}},
@@ -665,6 +781,8 @@ test_events(scope_suite_state_cth) ->
[
{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,cth,{'_',post_groups,['_',[]]}},
+ {?eh,cth,{'_',post_all,['_','_',[]]}},
{?eh,tc_start,{ct_scope_suite_state_cth_SUITE,init_per_suite}},
{?eh,cth,{'_',id,[[test]]}},
{?eh,cth,{'_',init,['_',[test]]}},
@@ -2313,6 +2431,229 @@ test_events(config_clash) ->
%% Make sure no 'cth_error' events are received!
[{negative,{?eh,cth_error,'_'},E} || E <- Events];
+test_events(alter_groups) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,cth,{empty_cth,id,[[]]}},
+ {?eh,cth,{empty_cth,init,[{'_','_','_'},[]]}},
+ {?eh,cth,{empty_cth,post_groups,[all_and_groups_SUITE,
+ [{new_group,[tc1,tc2]}]]}},
+ {?eh,cth,{empty_cth,post_all,[all_and_groups_SUITE,[{group,new_group}],
+ [{new_group,[tc1,tc2]}]]}},
+ {?eh,start_info,{1,1,2}},
+ {?eh,cth,{empty_cth,post_groups,[all_and_groups_SUITE,
+ [{new_group,[tc1,tc2]}]]}},
+ {?eh,cth,{empty_cth,post_all,[all_and_groups_SUITE,[{group,new_group}],
+ [{new_group,[tc1,tc2]}]]}},
+ {?eh,tc_start,{all_and_groups_SUITE,{init_per_group,new_group,[]}}},
+ {?eh,tc_done,{all_and_groups_SUITE,
+ {init_per_group,new_group,'$proplist'},ok}},
+ {?eh,tc_start,{all_and_groups_SUITE,tc1}},
+ {?eh,tc_done,{all_and_groups_SUITE,tc1,ok}},
+ {?eh,tc_start,{all_and_groups_SUITE,tc2}},
+ {?eh,tc_done,{all_and_groups_SUITE,tc2,ok}},
+ {?eh,tc_start,{all_and_groups_SUITE,{end_per_group,new_group,[]}}},
+ {?eh,tc_done,{all_and_groups_SUITE,
+ {end_per_group,new_group,'$proplist'},ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,cth,{empty_cth,terminate,[[]]}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(alter_all) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,cth,{empty_cth,id,[[]]}},
+ {?eh,cth,{empty_cth,init,[{'_','_','_'},[]]}},
+ {?eh,cth,{empty_cth,post_groups,[all_and_groups_SUITE,
+ [{test_group,[tc1]}]]}},
+ {?eh,cth,{empty_cth,post_all,[all_and_groups_SUITE,[tc2],
+ [{test_group,[tc1]}]]}},
+ {?eh,start_info,{1,1,1}},
+ {?eh,cth,{empty_cth,post_groups,[all_and_groups_SUITE,'_']}},
+ {?eh,cth,{empty_cth,post_all,[all_and_groups_SUITE,[tc2],'_']}},
+ {?eh,tc_start,{all_and_groups_SUITE,tc2}},
+ {?eh,tc_done,{all_and_groups_SUITE,tc2,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,cth,{empty_cth,terminate,[[]]}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(alter_all_from_skip) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,cth,{empty_cth,id,[[]]}},
+ {?eh,cth,{empty_cth,init,[{'_','_','_'},[]]}},
+ {?eh,cth,{empty_cth,post_groups,[all_and_groups_SUITE,
+ [{test_group,[tc1]}]]}},
+ {?eh,cth,{empty_cth,post_all,[all_and_groups_SUITE,[tc2],
+ [{test_group,[tc1]}]]}},
+ {?eh,start_info,{1,1,1}},
+ {?eh,cth,{empty_cth,post_groups,[all_and_groups_SUITE,'_']}},
+ {?eh,cth,{empty_cth,post_all,[all_and_groups_SUITE,[tc2],'_']}},
+ {?eh,tc_start,{all_and_groups_SUITE,tc2}},
+ {?eh,tc_done,{all_and_groups_SUITE,tc2,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,cth,{empty_cth,terminate,[[]]}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(alter_all_to_skip) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,cth,{empty_cth,id,[[]]}},
+ {?eh,cth,{empty_cth,init,[{'_','_','_'},[]]}},
+ {?eh,cth,{empty_cth,post_groups,[all_and_groups_SUITE,
+ [{test_group,[tc1]}]]}},
+ {?eh,cth,{empty_cth,post_all,[all_and_groups_SUITE,
+ {skip,"skipped by post_all/3"},
+ [{test_group,[tc1]}]]}},
+ {?eh,start_info,{1,1,0}},
+ {?eh,cth,{empty_cth,post_groups,[all_and_groups_SUITE,'_']}},
+ {?eh,cth,{empty_cth,post_all,[all_and_groups_SUITE,
+ {skip,"skipped by post_all/3"},
+ '_']}},
+ {?eh,tc_user_skip,{all_and_groups_SUITE,all,"skipped by post_all/3"}},
+ {?eh,cth,{'_',on_tc_skip,[all_and_groups_SUITE,all,
+ {tc_user_skip,"skipped by post_all/3"},
+ []]}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,cth,{empty_cth,terminate,[[]]}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(illegal_values_groups) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,cth,{empty_cth,id,[[]]}},
+ {?eh,cth,{empty_cth,init,[{'_','_','_'},[]]}},
+ {?eh,cth,{empty_cth,post_groups,
+ [all_and_groups_SUITE,
+ [{new_group,[this_test_does_not_exist]},
+ this_is_not_a_group_def]]}},
+ {?eh,start_info,{1,0,0}},
+ {?eh,cth,{empty_cth,post_groups,
+ [all_and_groups_SUITE,
+ [{new_group,[this_test_does_not_exist]},
+ this_is_not_a_group_def]]}},
+ {?eh,tc_start,{ct_framework,error_in_suite}},
+ {?eh,tc_done,{ct_framework,error_in_suite,{failed,{error,'_'}}}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,cth,{empty_cth,terminate,[[]]}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(illegal_values_all) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,cth,{empty_cth,id,[[]]}},
+ {?eh,cth,{empty_cth,init,[{'_','_','_'},[]]}},
+ {?eh,cth,{empty_cth,post_groups,[all_and_groups_SUITE,'_']}},
+ {?eh,cth,{empty_cth,post_all,
+ [all_and_groups_SUITE,
+ [{group,this_group_does_not_exist},
+ {this_is_not_a_valid_term}],'_']}},
+ {?eh,start_info,{1,0,0}},
+ {?eh,cth,{empty_cth,post_groups,[all_and_groups_SUITE,'_']}},
+ {?eh,cth,{empty_cth,post_all,
+ [all_and_groups_SUITE,
+ [{group,this_group_does_not_exist},
+ {this_is_not_a_valid_term}],'_']}},
+ {?eh,tc_start,{ct_framework,error_in_suite}},
+ {?eh,tc_done,
+ {ct_framework,error_in_suite,
+ {failed,
+ {error,'Invalid reference to group this_group_does_not_exist in all_and_groups_SUITE:all/0'}}}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,cth,{empty_cth,terminate,[[]]}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(bad_return_groups) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,cth,{empty_cth,id,[[]]}},
+ {?eh,cth,{empty_cth,init,[{'_','_','_'},[]]}},
+ {?eh,cth,{empty_cth,post_groups,[all_and_groups_SUITE,not_a_list]}},
+ {?eh,start_info,{1,0,0}},
+ {?eh,cth,{empty_cth,post_groups,[all_and_groups_SUITE,not_a_list]}},
+ {?eh,tc_start,{ct_framework,error_in_suite}},
+ {?eh,tc_done,
+ {ct_framework,error_in_suite,
+ {failed,
+ {error,
+ {'Bad return value from post_groups/2 hook function',not_a_list}}}}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,cth,{empty_cth,terminate,[[]]}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(bad_return_all) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,cth,{empty_cth,id,[[]]}},
+ {?eh,cth,{empty_cth,init,[{'_','_','_'},[]]}},
+ {?eh,cth,{empty_cth,post_groups,[all_and_groups_SUITE,'_']}},
+ {?eh,cth,{empty_cth,post_all,[all_and_groups_SUITE,not_a_list,'_']}},
+ {?eh,start_info,{1,0,0}},
+ {?eh,cth,{empty_cth,post_groups,[all_and_groups_SUITE,'_']}},
+ {?eh,cth,{empty_cth,post_all,[all_and_groups_SUITE,not_a_list,'_']}},
+ {?eh,tc_start,{ct_framework,error_in_suite}},
+ {?eh,tc_done,
+ {ct_framework,error_in_suite,
+ {failed,
+ {error,{'Bad return value from post_all/3 hook function',not_a_list}}}}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,cth,{empty_cth,terminate,[[]]}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(crash_groups) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,cth,{empty_cth,id,[[]]}},
+ {?eh,cth,{empty_cth,init,[{'_','_','_'},[]]}},
+ {?eh,cth,{empty_cth,post_groups,[all_and_groups_SUITE,crash]}},
+ {?eh,start_info,{1,0,0}},
+ {?eh,cth,{empty_cth,post_groups,[all_and_groups_SUITE,crash]}},
+ {?eh,tc_start,{ct_framework,error_in_suite}},
+ {?eh,tc_done,{ct_framework,error_in_suite,
+ {failed,
+ {error,"all_and_groups_cth:post_groups/2 CTH call failed"}}}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,cth,{empty_cth,terminate,[[]]}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(crash_all) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,cth,{empty_cth,id,[[]]}},
+ {?eh,cth,{empty_cth,init,[{'_','_','_'},[]]}},
+ {?eh,cth,{empty_cth,post_groups,[all_and_groups_SUITE,'_']}},
+ {?eh,cth,{empty_cth,post_all,[all_and_groups_SUITE,crash,'_']}},
+ {?eh,start_info,{1,0,0}},
+ {?eh,cth,{empty_cth,post_groups,[all_and_groups_SUITE,'_']}},
+ {?eh,cth,{empty_cth,post_all,[all_and_groups_SUITE,crash,'_']}},
+ {?eh,tc_start,{ct_framework,error_in_suite}},
+ {?eh,tc_done,{ct_framework,error_in_suite,
+ {failed,
+ {error,"all_and_groups_cth:post_all/3 CTH call failed"}}}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,cth,{empty_cth,terminate,[[]]}},
+ {?eh,stop_logging,[]}
+ ];
+
test_events(ok) ->
ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/all_and_groups_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/all_and_groups_SUITE.erl
new file mode 100644
index 0000000000..adc86005f9
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/all_and_groups_SUITE.erl
@@ -0,0 +1,47 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-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(all_and_groups_SUITE).
+
+-suite_defaults([{timetrap, {minutes, 10}}]).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("ct.hrl").
+
+init_per_group(_Group,Config) ->
+ Config.
+
+end_per_group(_Group,Config) ->
+ ok.
+
+all() ->
+ ct:get_config(all_return,[{group,test_group}]).
+
+groups() ->
+ [{test_group,[tc1]}].
+
+%% Test cases starts here.
+tc1(Config) ->
+ ok.
+
+tc2(Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/all_and_groups_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/all_and_groups_cth.erl
new file mode 100644
index 0000000000..9ebc00e9de
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/all_and_groups_cth.erl
@@ -0,0 +1,100 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-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(all_and_groups_cth).
+
+
+-include_lib("common_test/src/ct_util.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+%% Send a cth_error event if a callback is called with unexpected arguments
+-define(fail(Info),
+ gen_event:notify(
+ ?CT_EVMGR_REF,
+ #event{ name = cth_error,
+ node = node(),
+ data = {illegal_hook_callback,{?MODULE,?FUNCTION_NAME,Info}}})).
+
+%% CT Hooks
+-compile(export_all).
+
+id(Opts) ->
+ empty_cth:id(Opts).
+
+post_groups(Suite,Groups) ->
+ case empty_cth:post_groups(Suite,ct:get_config(post_groups_return,Groups)) of
+ crash -> error(crash_in_post_groups);
+ R -> R
+ end.
+
+post_all(Suite,Tests,Groups) ->
+ case empty_cth:post_all(Suite,ct:get_config(post_all_return,Tests),Groups) of
+ crash -> error(crash_in_post_all);
+ R -> R
+ end.
+
+init(Id, Opts) ->
+ empty_cth:init(Id, Opts).
+
+pre_init_per_suite(Suite, Config, State) ->
+ empty_cth:pre_init_per_suite(Suite,Config,State).
+
+post_init_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_init_per_suite(Suite,Config,Return,State).
+
+pre_end_per_suite(Suite,Config,State) ->
+ empty_cth:pre_end_per_suite(Suite,Config,State).
+
+post_end_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_end_per_suite(Suite,Config,Return,State).
+
+pre_init_per_group(Suite,Group,Config,State) ->
+ empty_cth:pre_init_per_group(Suite,Group,Config,State).
+
+post_init_per_group(Suite,Group,Config,Return,State) ->
+ empty_cth:post_init_per_group(Suite,Group,Config,Return,State).
+
+pre_end_per_group(Suite,Group,Config,State) ->
+ empty_cth:pre_end_per_group(Suite,Group,Config,State).
+
+post_end_per_group(Suite,Group,Config,Return,State) ->
+ empty_cth:post_end_per_group(Suite,Group,Config,Return,State).
+
+pre_init_per_testcase(Suite,TC,Config,State) ->
+ empty_cth:pre_init_per_testcase(Suite,TC,Config,State).
+
+post_init_per_testcase(Suite,TC,Config,Return,State) ->
+ empty_cth:post_init_per_testcase(Suite,TC,Config,Return,State).
+
+pre_end_per_testcase(Suite,TC,Config,State) ->
+ empty_cth:pre_end_per_testcase(Suite,TC,Config,State).
+
+post_end_per_testcase(Suite,TC,Config,Return,State) ->
+ empty_cth:post_end_per_testcase(Suite,TC,Config,Return,State).
+
+on_tc_fail(Suite,TC,Reason,State) ->
+ empty_cth:on_tc_fail(Suite,TC,Reason,State).
+
+on_tc_skip(Suite,TC,Reason,State) ->
+ empty_cth:on_tc_skip(Suite,TC,Reason,State).
+
+terminate(State) ->
+ empty_cth:terminate(State).
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_match_state_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_match_state_cth.erl
new file mode 100644
index 0000000000..38c9da903d
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_match_state_cth.erl
@@ -0,0 +1,58 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-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(ct_match_state_cth).
+
+
+-include_lib("common_test/src/ct_util.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+-compile(export_all).
+
+id(Opts) ->
+ empty_cth:id(Opts).
+
+post_groups(Suite, Groups) ->
+ empty_cth:post_groups(Suite, Groups).
+
+post_all(Suite, Tests, Groups) ->
+ empty_cth:post_all(Suite, Tests, Groups).
+
+init(Id, Opts) ->
+ empty_cth:init(Id, Opts),
+ {ok,mystate}.
+
+%% In the following, always match against the state value, to ensure
+%% that init has indeed been called before the rest of the hooks.
+pre_init_per_suite(Suite,Config,mystate) ->
+ empty_cth:pre_init_per_suite(Suite,Config,mystate).
+
+post_init_per_suite(Suite,Config,Return,mystate) ->
+ empty_cth:post_init_per_suite(Suite,Config,Return,mystate).
+
+pre_end_per_suite(Suite,Config,mystate) ->
+ empty_cth:pre_end_per_suite(Suite,Config,mystate).
+
+post_end_per_suite(Suite,Config,Return,mystate) ->
+ empty_cth:post_end_per_suite(Suite,Config,Return,mystate).
+
+terminate(mystate) ->
+ empty_cth:terminate(mystate).
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_suite_group_only_cth_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_suite_group_only_cth_SUITE.erl
new file mode 100644
index 0000000000..537c97d3f0
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_suite_group_only_cth_SUITE.erl
@@ -0,0 +1,54 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2016. 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(ct_scope_suite_group_only_cth_SUITE).
+
+-suite_defaults([{timetrap, {minutes, 10}}]).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("ct.hrl").
+
+%% Test server callback functions
+suite() ->
+ [{ct_hooks,[ct_match_state_cth]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+all() ->
+ [test_case].
+
+groups() ->
+ [{g1,[test_case]}].
+
+%% Test cases starts here.
+test_case(Config) when is_list(Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl
index c648367838..60488e84c6 100644
--- a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl
@@ -39,6 +39,9 @@
-export([id/1]).
-export([init/2]).
+-export([post_all/3]).
+-export([post_groups/2]).
+
-export([pre_init_per_suite/3]).
-export([post_init_per_suite/4]).
-export([pre_end_per_suite/3]).
@@ -71,6 +74,31 @@
-record(state, { id = ?MODULE :: term()}).
+%% Called after groups/0.
+%% You can change the return value in this function.
+-spec post_groups(Suite :: atom(), Groups :: list()) -> list().
+post_groups(Suite,Groups) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, post_groups,
+ [Suite,Groups]}}),
+ ct:log("~w:post_groups(~w) called", [?MODULE,Suite]),
+ Groups.
+
+%% Called after all/0.
+%% You can change the return value in this function.
+-spec post_all(Suite :: atom(),
+ Tests :: list(),
+ Groups :: term()) ->
+ list().
+post_all(Suite,Tests,Groups) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, post_all,
+ [Suite,Tests,Groups]}}),
+ ct:log("~w:post_all(~w) called", [?MODULE,Suite]),
+ Tests.
+
%% Always called before any other callback function. Use this to initiate
%% any common state. It should return an state for this CTH.
-spec init(Id :: term(), Opts :: proplists:proplist()) ->
diff --git a/lib/common_test/test/ct_tc_repeat_SUITE.erl b/lib/common_test/test/ct_tc_repeat_SUITE.erl
new file mode 100644
index 0000000000..433b5456fe
--- /dev/null
+++ b/lib/common_test/test/ct_tc_repeat_SUITE.erl
@@ -0,0 +1,438 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-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(ct_tc_repeat_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+-define(eh, ct_test_support_eh).
+
+%%--------------------------------------------------------------------
+%% TEST SERVER CALLBACK FUNCTIONS
+%%--------------------------------------------------------------------
+
+%%--------------------------------------------------------------------
+%% Description: Since Common Test starts another Test Server
+%% instance, the tests need to be performed on a separate node (or
+%% there will be clashes with logging processes etc).
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ DataDir = ?config(data_dir, Config),
+ ct_test_support:init_per_suite([{path_dirs,[DataDir]} | Config]).
+
+end_per_suite(Config) ->
+ ct_test_support:end_per_suite(Config).
+
+init_per_testcase(TestCase, Config) ->
+ ct_test_support:init_per_testcase(TestCase, Config).
+
+end_per_testcase(TestCase, Config) ->
+ ct_test_support:end_per_testcase(TestCase, Config).
+
+
+suite() ->
+ [{timetrap,{minutes,1}}].
+
+all() ->
+ all(suite).
+
+all(suite) ->
+ [
+ repeat,
+ repeat_parallel_until_ok,
+ repeat_parallel_until_fail,
+ repeat_sequence_until_ok,
+ repeat_sequence_until_fail,
+ pick_one_test_from_group,
+ pick_one_test_from_subgroup
+ ].
+
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+%%%-----------------------------------------------------------------
+%%%
+%% Test post_groups and post_all hook callbacks, introduced by OTP-14746
+repeat(Config) ->
+ ok = do_test(?FUNCTION_NAME, "tc_repeat_SUITE", [], [], Config).
+
+repeat_parallel_until_ok(Config) ->
+ {error,{{illegal_combination,{parallel,repeat_until_ok}},_}} =
+ do_test(?FUNCTION_NAME, "tc_repeat_SUITE", [{group,g_parallel_until_ok}],
+ [], Config, 1, []).
+
+repeat_parallel_until_fail(Config) ->
+ {error,{{illegal_combination,{parallel,repeat_until_fail}},_}} =
+ do_test(?FUNCTION_NAME, "tc_repeat_SUITE", [{group,g_parallel_until_fail}],
+ [], Config, 1, []).
+
+repeat_sequence_until_ok(Config) ->
+ {error,{{illegal_combination,{sequence,repeat_until_ok}},_}} =
+ do_test(?FUNCTION_NAME, "tc_repeat_SUITE", [{group,g_sequence_until_ok}],
+ [], Config, 1, []).
+
+repeat_sequence_until_fail(Config) ->
+ {error,{{illegal_combination,{sequence,repeat_until_fail}},_}} =
+ do_test(?FUNCTION_NAME, "tc_repeat_SUITE", [{group,g_sequence_until_fail}],
+ [], Config, 1, []).
+
+pick_one_test_from_group(Config) ->
+ do_test(?FUNCTION_NAME, "tc_repeat_SUITE", [{group,g_mixed},{testcase,tc2}],
+ [], Config, 1, []).
+
+pick_one_test_from_subgroup(Config) ->
+ do_test(?FUNCTION_NAME, "tc_repeat_SUITE",
+ [{group,[[g_mixed,subgroup]]},{testcase,tc2}],
+ [], Config, 1, []).
+
+
+%%%-----------------------------------------------------------------
+%%% HELP FUNCTIONS
+%%%-----------------------------------------------------------------
+
+do_test(Tag, Suite, WTT, CTHs, Config) ->
+ do_test(Tag, Suite, WTT, CTHs, Config, 2, []).
+
+do_test(Tag, Suite0, WTT, CTHs, Config, EC, ExtraOpts) ->
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join([DataDir,Suite0]),
+ {Opts,ERPid} =
+ setup([{suite,Suite}|WTT]++[{ct_hooks,CTHs},{label,Tag}|ExtraOpts],
+ Config),
+ Res = ct_test_support:run(Opts, Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+ %% io:format("~p~n",[Events]),
+
+ ct_test_support:log_events(Tag,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
+
+ TestEvents = events_to_check(Tag, EC),
+ ok = ct_test_support:verify_events(TestEvents, Events, Config),
+ Res.
+
+setup(Test, Config) ->
+ Opts0 = ct_test_support:get_opts(Config),
+ Level = ?config(trace_level, Config),
+ EvHArgs = [{cbm,ct_test_support},{trace_level,Level}],
+ Opts = Opts0 ++ [{event_handler,{?eh,EvHArgs}}|Test],
+ ERPid = ct_test_support:start_event_receiver(Config),
+ {Opts,ERPid}.
+
+reformat(Events, EH) ->
+ ct_test_support:reformat(Events, EH).
+
+gen_config(Name,KeyVals,Config) ->
+ PrivDir = ?config(priv_dir,Config),
+ File = filename:join(PrivDir,atom_to_list(Name)++".cfg"),
+ ok = file:write_file(File,[io_lib:format("~p.~n",[{Key,Value}])
+ || {Key,Value} <- KeyVals]),
+ File.
+
+%%%-----------------------------------------------------------------
+%%% TEST EVENTS
+%%%-----------------------------------------------------------------
+events_to_check(Test) ->
+ %% 2 tests (ct:run_test + script_start) is default
+ events_to_check(Test, 2).
+
+events_to_check(_, 0) ->
+ [];
+events_to_check(Test, N) ->
+ test_events(Test) ++ events_to_check(Test, N-1).
+
+test_events(repeat) ->
+ S = tc_repeat_SUITE,
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,unknown}},
+
+ %% tc1, {repeat,2}
+ {?eh,tc_start,{S,tc1}},
+ {?eh,tc_done,{S,tc1,ok}},
+ {?eh,test_stats,{1,0,{0,0}}},
+ {?eh,tc_start,{S,tc1}},
+ {?eh,tc_done,{S,tc1,ok}},
+ {?eh,test_stats,{2,0,{0,0}}},
+ %% tc2, {repeat_until_ok,3}
+ {?eh,tc_start,{S,tc2}},
+ {?eh,tc_done,{S,tc2,ok}},
+ {?eh,test_stats,{3,0,{0,0}}},
+ %% tc3, {repeat_until_ok,3}
+ {?eh,tc_start,{S,tc3}},
+ {?eh,tc_done,{tc_repeat_SUITE,tc3,
+ {failed,{error,{test_case_failed,always_fail}}}}},
+ {?eh,test_stats,{3,1,{0,0}}},
+ {?eh,tc_start,{S,tc3}},
+ {?eh,tc_done,{S,tc3,{failed,{error,{test_case_failed,always_fail}}}}},
+ {?eh,test_stats,{3,2,{0,0}}},
+ {?eh,tc_start,{S,tc3}},
+ {?eh,tc_done,{S,tc3,{failed,{error,{test_case_failed,always_fail}}}}},
+ {?eh,test_stats,{3,3,{0,0}}},
+ %% tc4, {repeat_until_fail,3}
+ {?eh,tc_start,{S,tc4}},
+ {?eh,tc_done,{S,tc4,ok}},
+ {?eh,test_stats,{4,3,{0,0}}},
+ {?eh,tc_start,{S,tc4}},
+ {?eh,tc_done,{S,tc4,{failed,{error,{test_case_failed,second_time_fail}}}}},
+ {?eh,test_stats,{4,4,{0,0}}},
+ %% g, tc1, {repeat,2}
+ {?eh,tc_start,{S,{init_per_group,g,[]}}},
+ {?eh,tc_done,{S,{init_per_group,g,[]},ok}},
+ {?eh,tc_start,{S,tc1}},
+ {?eh,tc_done,{S,tc1,ok}},
+ {?eh,test_stats,{5,4,{0,0}}},
+ {?eh,tc_start,{S,tc1}},
+ {?eh,tc_done,{S,tc1,ok}},
+ {?eh,test_stats,{6,4,{0,0}}},
+ {?eh,tc_start,{S,{end_per_group,g,[]}}},
+ {?eh,tc_done,{S,{end_per_group,g,[]},ok}},
+ %% g_until_ok, tc2, {repeat_until_ok,3}
+ {?eh,tc_start,{S,{init_per_group,g_until_ok,[]}}},
+ {?eh,tc_done,{S,{init_per_group,g_until_ok,[]},ok}},
+ {?eh,tc_start,{S,tc2}},
+ {?eh,tc_done,{S,tc2,ok}},
+ {?eh,test_stats,{7,4,{0,0}}},
+ {?eh,tc_start,{S,{end_per_group,g_until_ok,[]}}},
+ {?eh,tc_done,{S,{end_per_group,g_until_ok,[]},ok}},
+ %% g_until_fail, tc4, {repeat_until_fail,3}
+ {?eh,tc_start,{S,{init_per_group,g_until_fail,[]}}},
+ {?eh,tc_done,{S,{init_per_group,g_until_fail,[]},ok}},
+ {?eh,tc_start,{S,tc4}},
+ {?eh,tc_done,{S,tc4,ok}},
+ {?eh,test_stats,{8,4,{0,0}}},
+ {?eh,tc_start,{S,tc4}},
+ {?eh,tc_done,{S,tc4,{failed,{error,{test_case_failed,second_time_fail}}}}},
+ {?eh,test_stats,{8,5,{0,0}}},
+ {?eh,tc_start,{S,{end_per_group,g_until_fail,[]}}},
+ {?eh,tc_done,{S,{end_per_group,g_until_fail,[]},ok}},
+ %% g, parallel, tc1, {repeat,2}
+ {parallel,
+ [{?eh,tc_start,{S,{init_per_group,g,[parallel]}}},
+ {?eh,tc_done,{S,{init_per_group,g,[parallel]},ok}},
+ {?eh,tc_start,{S,tc1}},
+ {?eh,tc_done,{S,tc1,ok}},
+ {?eh,test_stats,{9,5,{0,0}}},
+ {?eh,tc_start,{S,tc1}},
+ {?eh,tc_done,{S,tc1,ok}},
+ {?eh,test_stats,{10,5,{0,0}}},
+ {?eh,tc_start,{S,{end_per_group,g,[parallel]}}},
+ {?eh,tc_done,{S,{end_per_group,g,[parallel]},ok}}]},
+ %% g, sequence, tc1, {repeat,2}
+ {?eh,tc_start,{S,{init_per_group,g,[sequence]}}},
+ {?eh,tc_done,{S,{init_per_group,g,[sequence]},ok}},
+ {?eh,tc_start,{S,tc1}},
+ {?eh,tc_done,{S,tc1,ok}},
+ {?eh,test_stats,{11,5,{0,0}}},
+ {?eh,tc_start,{S,tc1}},
+ {?eh,tc_done,{S,tc1,ok}},
+ {?eh,test_stats,{12,5,{0,0}}},
+ {?eh,tc_start,{S,{end_per_group,g,[sequence]}}},
+ {?eh,tc_done,{S,{end_per_group,g,[sequence]},ok}},
+ %% g_sequence_skip_rest,
+ {?eh,tc_start,{S,{init_per_group,g_mixed,[sequence]}}},
+ {?eh,tc_done,{S,{init_per_group,g_mixed,[sequence]},ok}},
+ {?eh,tc_start,{S,tc1}},
+ {?eh,tc_done,{S,tc1,ok}},
+ {?eh,test_stats,{13,5,{0,0}}},
+ {?eh,tc_start,{S,tc1}},
+ {?eh,tc_done,{S,tc1,ok}},
+ {?eh,test_stats,{14,5,{0,0}}},
+ {?eh,tc_start,{S,tc4}},
+ {?eh,tc_done,{S,tc4,ok}},
+ {?eh,test_stats,{15,5,{0,0}}},
+ {?eh,tc_start,{S,tc4}},
+ {?eh,tc_done,{S,tc4,{failed,{error,{test_case_failed,second_time_fail}}}}},
+ {?eh,test_stats,{15,6,{0,0}}},
+ %% ----> fail in sequence, so skip rest
+ {?eh,tc_auto_skip,{S,{tc4,g_mixed}, % last of current repeat tc4
+ {failed,{tc_repeat_SUITE,tc4}}}},
+ {?eh,test_stats,{15,6,{0,1}}},
+ {?eh,tc_auto_skip,{S,{tc1,g_mixed}, % single tc1
+ {failed,{tc_repeat_SUITE,tc4}}}},
+ {?eh,test_stats,{15,6,{0,2}}},
+ {?eh,tc_auto_skip,{S,{tc1,g}, % group g, tc1, {repeat,2}
+ {failed,{tc_repeat_SUITE,tc4}}}},
+ {?eh,test_stats,{15,6,{0,3}}},
+ {?eh,tc_auto_skip,{S,{tc1,subgroup}, % subgroup, single tc1
+ {failed,{tc_repeat_SUITE,tc4}}}},
+ {?eh,test_stats,{15,6,{0,4}}},
+ {?eh,tc_auto_skip,{S,{tc2,subgroup}, % subgroup, tc2, {repeat,2}
+ {failed,{tc_repeat_SUITE,tc4}}}},
+ {?eh,test_stats,{15,6,{0,5}}},
+ {?eh,tc_auto_skip,{S,{tc2,g_mixed}, % tc2, {repeat,2}
+ {failed,{tc_repeat_SUITE,tc4}}}},
+ {?eh,test_stats,{15,6,{0,6}}},
+ {?eh,tc_auto_skip,{S,{tc2,g_mixed}, % single tc2
+ {failed,{tc_repeat_SUITE,tc4}}}},
+ {?eh,test_stats,{15,6,{0,7}}},
+ {?eh,tc_auto_skip,{S,{tc1,g_mixed}, % tc1, {repeat,2}
+ {failed,{tc_repeat_SUITE,tc4}}}},
+ {?eh,test_stats,{15,6,{0,8}}},
+ {?eh,tc_auto_skip,{S,{tc1,g_mixed}, % single tc1
+ {failed,{tc_repeat_SUITE,tc4}}}},
+ {?eh,test_stats,{15,6,{0,9}}},
+ {?eh,tc_start,{S,{end_per_group,g_mixed,'_'}}},
+ {?eh,tc_done,{S,{end_per_group,g_mixed,'_'},ok}},
+ %% done
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(repeat_parallel_until_ok) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,tc_start,{'_',{init_per_group,g_parallel_until_ok,[parallel]}}},
+ {?eh,tc_done,{'_',{init_per_group,g_parallel_until_ok,[parallel]},ok}},
+ {?eh,severe_error,{{illegal_combination,{parallel,repeat_until_ok}},'_'}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(repeat_parallel_until_fail) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,tc_start,{'_',{init_per_group,g_parallel_until_fail,[parallel]}}},
+ {?eh,tc_done,{'_',{init_per_group,g_parallel_until_fail,[parallel]},ok}},
+ {?eh,severe_error,{{illegal_combination,{parallel,repeat_until_fail}},'_'}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(repeat_sequence_until_ok) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,tc_start,{'_',{init_per_group,g_sequence_until_ok,[sequence]}}},
+ {?eh,tc_done,{'_',{init_per_group,g_sequence_until_ok,[sequence]},ok}},
+ {?eh,severe_error,{{illegal_combination,{sequence,repeat_until_ok}},'_'}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(repeat_sequence_until_fail) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,tc_start,{'_',{init_per_group,g_sequence_until_fail,[sequence]}}},
+ {?eh,tc_done,{'_',{init_per_group,g_sequence_until_fail,[sequence]},ok}},
+ {?eh,severe_error,{{illegal_combination,{sequence,repeat_until_fail}},'_'}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(pick_one_test_from_group) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,tc_start,{'_',{init_per_group,g_mixed,[]}}},
+ {?eh,tc_done,{'_',{init_per_group,g_mixed,[]},ok}},
+ {negative,
+ {?eh,tc_start,{'_',tc1}},
+ {?eh,tc_start,{'_',tc2}}}, % single tc2
+ {?eh,tc_done,{'_',tc2,ok}},
+ {?eh,tc_start,{'_',tc2}}, % tc2, {repeat,2}
+ {?eh,tc_done,{'_',tc2,ok}},
+ {?eh,tc_start,{'_',tc2}},
+ {?eh,tc_done,{'_',tc2,ok}},
+ {negative,
+ {?eh,tc_start,{'_',tc1}},
+ {?eh,tc_start,{'_',{end_per_group,g_mixed,[]}}}},
+ {?eh,tc_done,{'_',{end_per_group,g_mixed,[]},ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(pick_one_test_from_subgroup) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,tc_start,{'_',{init_per_group,g_mixed,[]}}},
+ {?eh,tc_done,{'_',{init_per_group,g_mixed,[]},ok}},
+ {negative,
+ {?eh,tc_start,{'_',tc2}},
+ {?eh,tc_start,{'_',{init_per_group,subgroup,[]}}}},
+ {?eh,tc_done,{'_',{init_per_group,subgroup,[]},ok}},
+ {negative,
+ {?eh,tc_start,{'_',tc1}},
+ {?eh,tc_start,{'_',tc2}}}, % tc2, {repeat,2}
+ {?eh,tc_done,{'_',tc2,ok}},
+ {?eh,tc_start,{'_',tc2}},
+ {?eh,tc_done,{'_',tc2,ok}},
+ {negative,
+ {?eh,tc_start,{'_',tc1}},
+ {?eh,tc_start,{'_',{end_per_group,subgroup,[]}}}},
+ {?eh,tc_done,{'_',{end_per_group,subgroup,[]},ok}},
+ {negative,
+ {?eh,tc_start,{'_',tc2}},
+ {?eh,tc_start,{'_',{end_per_group,g_mixed,[]}}}},
+ {?eh,tc_done,{'_',{end_per_group,g_mixed,[]},ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(ok) ->
+ ok.
+
+%% test events help functions
+contains(List) ->
+ fun(Proplist) when is_list(Proplist) ->
+ contains(List,Proplist)
+ end.
+
+contains([{not_in_order,List}|T],Rest) ->
+ contains_parallel(List,Rest),
+ contains(T,Rest);
+contains([{Ele,Pos}|T] = L,[H|T2]) ->
+ case element(Pos,H) of
+ Ele ->
+ contains(T,T2);
+ _ ->
+ contains(L,T2)
+ end;
+contains([Ele|T],[{Ele,_}|T2])->
+ contains(T,T2);
+contains([Ele|T],[Ele|T2])->
+ contains(T,T2);
+contains(List,[_|T]) ->
+ contains(List,T);
+contains([],_) ->
+ match.
+
+contains_parallel([Key | T], Elems) ->
+ contains([Key],Elems),
+ contains_parallel(T,Elems);
+contains_parallel([],_Elems) ->
+ match.
+
+not_contains(List) ->
+ fun(Proplist) when is_list(Proplist) ->
+ [] = [Ele || {Ele,_} <- Proplist,
+ Test <- List,
+ Test =:= Ele]
+ end.
diff --git a/lib/common_test/test/ct_tc_repeat_SUITE_data/tc_repeat_SUITE.erl b/lib/common_test/test/ct_tc_repeat_SUITE_data/tc_repeat_SUITE.erl
new file mode 100644
index 0000000000..f5d960d12f
--- /dev/null
+++ b/lib/common_test/test/ct_tc_repeat_SUITE_data/tc_repeat_SUITE.erl
@@ -0,0 +1,85 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-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(tc_repeat_SUITE).
+
+-suite_defaults([{timetrap, {minutes, 10}}]).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("ct.hrl").
+
+init_per_group(_Group,Config) ->
+ Config.
+
+end_per_group(_Group,Config) ->
+ ok.
+
+all() ->
+ [{testcase,tc1,[{repeat,2}]},
+ {testcase,tc2,[{repeat_until_ok,3}]},
+ {testcase,tc3,[{repeat_until_ok,3}]},
+ {testcase,tc4,[{repeat_until_fail,3}]},
+ {group,g},
+ {group,g_until_ok},
+ {group,g_until_fail},
+ {group,g,[parallel]},
+ {group,g,[sequence]},
+ {group,g_mixed,[sequence]}].
+
+groups() ->
+ [{g,[{testcase,tc1,[{repeat,2}]}]},
+ {g_until_ok,[{testcase,tc2,[{repeat_until_ok,3}]}]},
+ {g_until_fail,[{testcase,tc4,[{repeat_until_fail,3}]}]},
+ {g_parallel_until_ok,[parallel],[{testcase,tc2,[{repeat_until_ok,3}]}]},
+ {g_parallel_until_fail,[parallel],[{testcase,tc1,[{repeat_until_fail,2}]}]},
+ {g_sequence_until_ok,[sequence],[{testcase,tc2,[{repeat_until_ok,3}]}]},
+ {g_sequence_until_fail,[sequence],[{testcase,tc1,[{repeat_until_fail,2}]}]},
+ {g_mixed,[{testcase,tc1,[{repeat,2}]},
+ {testcase,tc4,[{repeat,3}]},
+ tc1,
+ {group,g},
+ {subgroup,[tc1,{testcase,tc2,[{repeat,2}]}]},
+ {testcase,tc2,[{repeat,2}]},
+ tc2,
+ {testcase,tc1,[{repeat,2}]},
+ tc1]}].
+
+%% Test cases starts here.
+tc1(_Config) ->
+ ok.
+
+tc2(_Config) ->
+ ok.
+
+tc3(_Config) ->
+ ct:fail(always_fail).
+
+tc4(Config) ->
+ case ?config(saved_config,Config) of
+ {tc4,_} ->
+ ct:fail(second_time_fail);
+ undefined ->
+ {save_config,Config}
+ end.
+
+tc5(_Config) ->
+ {skip,"just skip this"}.
diff --git a/lib/common_test/test/ct_util_SUITE.erl b/lib/common_test/test/ct_util_SUITE.erl
new file mode 100644
index 0000000000..1d773855da
--- /dev/null
+++ b/lib/common_test/test/ct_util_SUITE.erl
@@ -0,0 +1,490 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-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(ct_util_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+-define(eh, ct_test_support_eh).
+
+%%--------------------------------------------------------------------
+%% TEST SERVER CALLBACK FUNCTIONS
+%%--------------------------------------------------------------------
+
+%%--------------------------------------------------------------------
+%% Description: Since Common Test starts another Test Server
+%% instance, the tests need to be performed on a separate node (or
+%% there will be clashes with logging processes etc).
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ DataDir = ?config(data_dir, Config),
+ CTHs = filelib:wildcard(filename:join(DataDir,"*_cth.erl")),
+ io:format("CTHs: ~p",[CTHs]),
+ [io:format("Compiling ~p: ~p",
+ [FileName,compile:file(FileName,[{outdir,DataDir},debug_info])]) ||
+ FileName <- CTHs],
+ ct_test_support:init_per_suite([{path_dirs,[DataDir]} | Config]).
+
+end_per_suite(Config) ->
+ ct_test_support:end_per_suite(Config).
+
+init_per_testcase(TestCase, Config) ->
+ ct_test_support:init_per_testcase(TestCase, Config).
+
+end_per_testcase(TestCase, Config) ->
+ ct_test_support:end_per_testcase(TestCase, Config).
+
+
+suite() ->
+ [{timetrap,{minutes,1}}].
+
+all() ->
+ all(suite).
+
+all(suite) ->
+ [
+ pre_init_per_suite,
+ post_init_per_suite,
+ pre_end_per_suite,
+ post_end_per_suite,
+ pre_init_per_group,
+ post_init_per_group,
+ pre_end_per_group,
+ post_end_per_group,
+ pre_init_per_testcase,
+ post_init_per_testcase,
+ pre_end_per_testcase,
+ post_end_per_testcase
+ ].
+
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+%%%-----------------------------------------------------------------
+%%%
+pre_init_per_suite(Config) ->
+ CfgFile = gen_config(?FUNCTION_NAME,
+ [{pre_init_per_suite,
+ {curr_tc_SUITE,kill}}],
+ Config),
+ ok = do_test(?FUNCTION_NAME,
+ "curr_tc_SUITE.erl",
+ [{ct_hooks,[ct_util_cth]},{config,CfgFile}],
+ Config).
+
+post_init_per_suite(Config) ->
+ CfgFile = gen_config(?FUNCTION_NAME,
+ [{post_init_per_suite,
+ {curr_tc_SUITE,kill}}],
+ Config),
+ ok = do_test(?FUNCTION_NAME,
+ "curr_tc_SUITE.erl",
+ [{ct_hooks,[ct_util_cth]},{config,CfgFile}],
+ Config).
+
+pre_end_per_suite(Config) ->
+ CfgFile = gen_config(?FUNCTION_NAME,
+ [{pre_end_per_suite,
+ {curr_tc_SUITE,kill}}],
+ Config),
+ ok = do_test(?FUNCTION_NAME,
+ "curr_tc_SUITE.erl",
+ [{ct_hooks,[ct_util_cth]},{config,CfgFile}],
+ Config).
+
+post_end_per_suite(Config) ->
+ CfgFile = gen_config(?FUNCTION_NAME,
+ [{post_end_per_suite,
+ {curr_tc_SUITE,kill}}],
+ Config),
+ ok = do_test(?FUNCTION_NAME,
+ "curr_tc_SUITE.erl",
+ [{ct_hooks,[ct_util_cth]},{config,CfgFile}],
+ Config).
+
+
+pre_init_per_group(Config) ->
+ CfgFile = gen_config(?FUNCTION_NAME,
+ [{pre_init_per_group,
+ {curr_tc_SUITE,g,kill}}],
+ Config),
+ ok = do_test(?FUNCTION_NAME,
+ "curr_tc_SUITE.erl",
+ [{ct_hooks,[ct_util_cth]},{config,CfgFile}],
+ Config).
+
+post_init_per_group(Config) ->
+ CfgFile = gen_config(?FUNCTION_NAME,
+ [{post_init_per_group,
+ {curr_tc_SUITE,g,kill}}],
+ Config),
+ ok = do_test(?FUNCTION_NAME,
+ "curr_tc_SUITE.erl",
+ [{ct_hooks,[ct_util_cth]},{config,CfgFile}],
+ Config).
+
+pre_end_per_group(Config) ->
+ CfgFile = gen_config(?FUNCTION_NAME,
+ [{pre_end_per_group,
+ {curr_tc_SUITE,g,kill}}],
+ Config),
+ ok = do_test(?FUNCTION_NAME,
+ "curr_tc_SUITE.erl",
+ [{ct_hooks,[ct_util_cth]},{config,CfgFile}],
+ Config).
+
+post_end_per_group(Config) ->
+ CfgFile = gen_config(?FUNCTION_NAME,
+ [{post_end_per_group,
+ {curr_tc_SUITE,g,kill}}],
+ Config),
+ ok = do_test(?FUNCTION_NAME,
+ "curr_tc_SUITE.erl",
+ [{ct_hooks,[ct_util_cth]},{config,CfgFile}],
+ Config).
+
+pre_init_per_testcase(Config) ->
+ CfgFile = gen_config(?FUNCTION_NAME,
+ [{pre_init_per_testcase,
+ {curr_tc_SUITE,tc1,kill}}],
+ Config),
+ ok = do_test(?FUNCTION_NAME,
+ "curr_tc_SUITE.erl",
+ [{ct_hooks,[ct_util_cth]},{config,CfgFile}],
+ Config).
+
+post_init_per_testcase(Config) ->
+ CfgFile = gen_config(?FUNCTION_NAME,
+ [{post_init_per_testcase,
+ {curr_tc_SUITE,tc1,{timeout,5000}}}],
+ Config),
+ ok = do_test(?FUNCTION_NAME,
+ "curr_tc_SUITE.erl",
+ [{ct_hooks,[ct_util_cth]},{config,CfgFile}],
+ Config).
+
+pre_end_per_testcase(Config) ->
+ CfgFile = gen_config(?FUNCTION_NAME,
+ [{pre_end_per_testcase,
+ {curr_tc_SUITE,tc1,{timeout,5000}}}],
+ Config),
+ ok = do_test(?FUNCTION_NAME,
+ "curr_tc_SUITE.erl",
+ [{ct_hooks,[ct_util_cth]},{config,CfgFile}],
+ Config).
+
+post_end_per_testcase(Config) ->
+ CfgFile = gen_config(?FUNCTION_NAME,
+ [{post_end_per_testcase,
+ {curr_tc_SUITE,tc1,kill}}],
+ Config),
+ ok = do_test(?FUNCTION_NAME,
+ "curr_tc_SUITE.erl",
+ [{ct_hooks,[ct_util_cth]},{config,CfgFile}],
+ Config).
+
+%%%-----------------------------------------------------------------
+%%% HELP FUNCTIONS
+%%%-----------------------------------------------------------------
+
+do_test(Tag, Suite, RunTestArgs, Config) ->
+ do_test(Tag, Suite, RunTestArgs, Config, 2).
+
+do_test(Tag, Suite0, RunTestArgs, Config, EC) ->
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join([DataDir,Suite0]),
+ {Opts,ERPid} = setup([{suite,Suite}]++[{label,Tag}|RunTestArgs],Config),
+ Res = ct_test_support:run(Opts, Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+ %% io:format("~p~n",[Events]),
+
+ ct_test_support:log_events(Tag,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
+
+ TestEvents = events_to_check(Tag, EC),
+ ok = ct_test_support:verify_events(TestEvents, Events, Config),
+ Res.
+
+setup(Test, Config) ->
+ Opts0 = ct_test_support:get_opts(Config),
+ Level = ?config(trace_level, Config),
+ EvHArgs = [{cbm,ct_test_support},{trace_level,Level}],
+ Opts = Opts0 ++ [{event_handler,{?eh,EvHArgs}}|Test],
+ ERPid = ct_test_support:start_event_receiver(Config),
+ {Opts,ERPid}.
+
+reformat(Events, EH) ->
+ ct_test_support:reformat(Events, EH).
+
+gen_config(Name,KeyVals,Config) ->
+ PrivDir = ?config(priv_dir,Config),
+ File = filename:join(PrivDir,atom_to_list(Name)++".cfg"),
+ ok = file:write_file(File,[io_lib:format("~p.~n",[{Key,Value}])
+ || {Key,Value} <- KeyVals]),
+ File.
+
+%%%-----------------------------------------------------------------
+%%% TEST EVENTS
+%%%-----------------------------------------------------------------
+events_to_check(Test) ->
+ %% 2 tests (ct:run_test + script_start) is default
+ events_to_check(Test, 2).
+
+events_to_check(_, 0) ->
+ [];
+events_to_check(Test, N) ->
+ test_events(Test) ++ events_to_check(Test, N-1).
+
+test_events(IPS) when IPS=:=pre_init_per_suite; IPS=:=post_init_per_suite ->
+ S = curr_tc_SUITE,
+ FwFunc =
+ case IPS of
+ pre_init_per_suite -> init_tc;
+ post_init_per_suite -> end_tc
+ end,
+ E = {failed,{ct_framework,FwFunc,{test_case_failed,hahahahahah}}},
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,4}},
+ {?eh,tc_start,{S,init_per_suite}},
+ {?eh,tc_done,{S,init_per_suite,E}},
+ {?eh,tc_auto_skip,{S,tc1,{failed,{S,init_per_suite,E}}}},
+ {?eh,tc_auto_skip,{S,tc2,{failed,{S,init_per_suite,E}}}},
+ {?eh,tc_auto_skip,{S,{tc1,g},{failed,{S,init_per_suite,E}}}},
+ {?eh,tc_auto_skip,{S,{tc2,g},{failed,{S,init_per_suite,E}}}},
+ {?eh,test_stats,{0,0,{0,4}}},
+ {?eh,tc_auto_skip,{S,end_per_suite,{failed,{S,init_per_suite,E}}}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(EPS) when EPS=:=pre_end_per_suite; EPS=:=post_end_per_suite ->
+ S = curr_tc_SUITE,
+ FwFunc =
+ case EPS of
+ pre_end_per_suite -> init_tc;
+ post_end_per_suite -> end_tc
+ end,
+ E = {failed,{ct_framework,FwFunc,{test_case_failed,hahahahahah}}},
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,4}},
+ {?eh,tc_start,{S,init_per_suite}},
+ {?eh,tc_done,{S,init_per_suite,ok}},
+ {?eh,tc_start,{S,tc1}},
+ {?eh,tc_done,{S,tc1,ok}},
+ {?eh,test_stats,{1,0,{0,0}}},
+ {?eh,tc_start,{S,tc2}},
+ {?eh,tc_done,{S,tc2,ok}},
+ {?eh,test_stats,{2,0,{0,0}}},
+ [{?eh,tc_start,{S,{init_per_group,g,[]}}},
+ {?eh,tc_done,{S,{init_per_group,g,[]},ok}},
+ {?eh,tc_start,{S,tc1}},
+ {?eh,tc_done,{S,tc1,ok}},
+ {?eh,test_stats,{3,0,{0,0}}},
+ {?eh,tc_start,{S,tc2}},
+ {?eh,tc_done,{S,tc2,ok}},
+ {?eh,test_stats,{4,0,{0,0}}},
+ {?eh,tc_start,{S,{end_per_group,g,[]}}},
+ {?eh,tc_done,{S,{end_per_group,g,[]},ok}}],
+ {?eh,tc_start,{S,end_per_suite}},
+ {?eh,tc_done,{S,end_per_suite,E}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(IPG) when IPG=:=pre_init_per_group; IPG=:=post_init_per_group ->
+ S = curr_tc_SUITE,
+ FwFunc =
+ case IPG of
+ pre_init_per_group -> init_tc;
+ post_init_per_group -> end_tc
+ end,
+ E = {failed,{ct_framework,FwFunc,{test_case_failed,hahahahahah}}},
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,4}},
+ {?eh,tc_start,{S,init_per_suite}},
+ {?eh,tc_done,{S,init_per_suite,ok}},
+ {?eh,tc_start,{S,tc1}},
+ {?eh,tc_done,{S,tc1,ok}},
+ {?eh,test_stats,{1,0,{0,0}}},
+ {?eh,tc_start,{S,tc2}},
+ {?eh,tc_done,{S,tc2,ok}},
+ {?eh,test_stats,{2,0,{0,0}}},
+ [{?eh,tc_start,{S,{init_per_group,g,[]}}},
+ {?eh,tc_done,{S,{init_per_group,g,[]},E}},
+ {?eh,tc_auto_skip,{S,{tc1,g},{failed,{S,init_per_group,E}}}},
+ {?eh,tc_auto_skip,{S,{tc2,g},{failed,{S,init_per_group,E}}}},
+ {?eh,test_stats,{2,0,{0,2}}},
+ {?eh,tc_auto_skip,{S,{end_per_group,g},{failed,{S,init_per_group,E}}}}],
+ {?eh,tc_start,{S,end_per_suite}},
+ {?eh,tc_done,{S,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(EPG) when EPG=:=pre_end_per_group; EPG=:=post_end_per_group ->
+ S = curr_tc_SUITE,
+ FwFunc =
+ case EPG of
+ pre_end_per_group -> init_tc;
+ post_end_per_group -> end_tc
+ end,
+ E = {failed,{ct_framework,FwFunc,{test_case_failed,hahahahahah}}},
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,4}},
+ {?eh,tc_start,{S,init_per_suite}},
+ {?eh,tc_done,{S,init_per_suite,ok}},
+ {?eh,tc_start,{S,tc1}},
+ {?eh,tc_done,{S,tc1,ok}},
+ {?eh,test_stats,{1,0,{0,0}}},
+ {?eh,tc_start,{S,tc2}},
+ {?eh,tc_done,{S,tc2,ok}},
+ {?eh,test_stats,{2,0,{0,0}}},
+ [{?eh,tc_start,{S,{init_per_group,g,[]}}},
+ {?eh,tc_done,{S,{init_per_group,g,[]},ok}},
+ {?eh,tc_start,{S,tc1}},
+ {?eh,tc_done,{S,tc1,ok}},
+ {?eh,test_stats,{3,0,{0,0}}},
+ {?eh,tc_start,{S,tc2}},
+ {?eh,tc_done,{S,tc2,ok}},
+ {?eh,test_stats,{4,0,{0,0}}},
+ {?eh,tc_start,{S,{end_per_group,g,[]}}},
+ {?eh,tc_done,{S,{end_per_group,g,[]},E}}],
+ {?eh,tc_start,{S,end_per_suite}},
+ {?eh,tc_done,{S,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(IPTC) when IPTC=:=pre_init_per_testcase;
+ IPTC=:=post_init_per_testcase ->
+ S = curr_tc_SUITE,
+ E = case IPTC of
+ pre_init_per_testcase ->
+ {failed,{ct_framework,init_tc,{test_case_failed,hahahahahah}}};
+ post_init_per_testcase ->
+ {failed,{ct_framework,end_tc,{timetrap,3000}}}
+ end,
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,4}},
+ {?eh,tc_start,{S,init_per_suite}},
+ {?eh,tc_done,{S,init_per_suite,ok}},
+ {?eh,tc_start,{S,tc1}},
+ {?eh,tc_done,{S,tc1,{auto_skipped,E}}},
+ {?eh,test_stats,{0,0,{0,1}}},
+ {?eh,tc_start,{S,tc2}},
+ {?eh,tc_done,{S,tc2,ok}},
+ {?eh,test_stats,{1,0,{0,1}}},
+ [{?eh,tc_start,{S,{init_per_group,g,[]}}},
+ {?eh,tc_done,{S,{init_per_group,g,[]},ok}},
+ {?eh,tc_start,{S,tc1}},
+ {?eh,tc_done,{S,tc1,{auto_skipped,E}}},
+ {?eh,test_stats,{1,0,{0,2}}},
+ {?eh,tc_start,{S,tc2}},
+ {?eh,tc_done,{S,tc2,ok}},
+ {?eh,test_stats,{2,0,{0,2}}},
+ {?eh,tc_start,{S,{end_per_group,g,[]}}},
+ {?eh,tc_done,{S,{end_per_group,g,[]},ok}}],
+ {?eh,tc_start,{S,end_per_suite}},
+ {?eh,tc_done,{S,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(EPTC) when EPTC=:=pre_end_per_testcase; EPTC=:=post_end_per_testcase->
+ S = curr_tc_SUITE,
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,4}},
+ {?eh,tc_start,{S,tc1}},
+ {?eh,tc_done,{S,tc1,ok}},
+ {?eh,test_stats,{1,0,{0,0}}},
+ {?eh,tc_start,{S,tc2}},
+ {?eh,tc_done,{S,tc2,ok}},
+ {?eh,test_stats,{2,0,{0,0}}},
+ [{?eh,tc_start,{S,{init_per_group,g,[]}}},
+ {?eh,tc_done,{S,{init_per_group,g,[]},ok}},
+ {?eh,tc_start,{S,tc1}},
+ {?eh,tc_done,{S,tc1,ok}},
+ {?eh,test_stats,{3,0,{0,0}}},
+ {?eh,tc_start,{S,tc2}},
+ {?eh,tc_done,{S,tc2,ok}},
+ {?eh,test_stats,{4,0,{0,0}}},
+ {?eh,tc_start,{S,{end_per_group,g,[]}}},
+ {?eh,tc_done,{S,{end_per_group,g,[]},ok}}],
+ {?eh,tc_start,{S,end_per_suite}},
+ {?eh,tc_done,{S,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ].
+
+%% test events help functions
+contains(List) ->
+ fun(Proplist) when is_list(Proplist) ->
+ contains(List,Proplist)
+ end.
+
+contains([{not_in_order,List}|T],Rest) ->
+ contains_parallel(List,Rest),
+ contains(T,Rest);
+contains([{Ele,Pos}|T] = L,[H|T2]) ->
+ case element(Pos,H) of
+ Ele ->
+ contains(T,T2);
+ _ ->
+ contains(L,T2)
+ end;
+contains([Ele|T],[{Ele,_}|T2])->
+ contains(T,T2);
+contains([Ele|T],[Ele|T2])->
+ contains(T,T2);
+contains(List,[_|T]) ->
+ contains(List,T);
+contains([],_) ->
+ match.
+
+contains_parallel([Key | T], Elems) ->
+ contains([Key],Elems),
+ contains_parallel(T,Elems);
+contains_parallel([],_Elems) ->
+ match.
+
+not_contains(List) ->
+ fun(Proplist) when is_list(Proplist) ->
+ [] = [Ele || {Ele,_} <- Proplist,
+ Test <- List,
+ Test =:= Ele]
+ end.
diff --git a/lib/common_test/test/ct_util_SUITE_data/ct_util_cth.erl b/lib/common_test/test/ct_util_SUITE_data/ct_util_cth.erl
new file mode 100644
index 0000000000..34c1568a87
--- /dev/null
+++ b/lib/common_test/test/ct_util_SUITE_data/ct_util_cth.erl
@@ -0,0 +1,105 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-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(ct_util_cth).
+
+
+-include_lib("common_test/src/ct_util.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+%% Send a cth_error event if a callback is called with unexpected arguments
+
+%% CT Hooks
+-compile(export_all).
+
+id(Opts) ->
+ erlang:system_time(second).
+
+init(Id, Opts) ->
+ {ok,ok}.
+
+pre_init_per_suite(Suite,Config,State) ->
+ maybe_sleep(?FUNCTION_NAME,Suite),
+ {Config, State}.
+
+post_init_per_suite(Suite,Config,Return,State) ->
+ maybe_sleep(?FUNCTION_NAME,Suite),
+ {Return, State}.
+
+pre_end_per_suite(Suite,Config,State) ->
+ maybe_sleep(?FUNCTION_NAME,Suite),
+ {Config, State}.
+
+post_end_per_suite(Suite,Config,Return,State) ->
+ maybe_sleep(?FUNCTION_NAME,Suite),
+ {Return, State}.
+
+pre_init_per_group(Suite, Group, Config, State) ->
+ maybe_sleep(?FUNCTION_NAME,Suite,Group),
+ {Config,State}.
+
+post_init_per_group(Suite, Group, Config,Return,State) ->
+ maybe_sleep(?FUNCTION_NAME,Suite,Group),
+ {Return,State}.
+
+pre_end_per_group(Suite, Group, Config, State) ->
+ maybe_sleep(?FUNCTION_NAME,Suite,Group),
+ {Config,State}.
+
+post_end_per_group(Suite, Group, Config,Return,State) ->
+ maybe_sleep(?FUNCTION_NAME,Suite,Group),
+ {Return,State}.
+
+pre_init_per_testcase(Suite, TC, Config, State) ->
+ maybe_sleep(?FUNCTION_NAME,Suite,TC),
+ {Config,State}.
+
+post_init_per_testcase(Suite, TC, Config,Return,State) ->
+ maybe_sleep(?FUNCTION_NAME,Suite,TC),
+ {Return,State}.
+
+pre_end_per_testcase(Suite, TC, Config, State) ->
+ maybe_sleep(?FUNCTION_NAME,Suite,TC),
+ {Config,State}.
+
+post_end_per_testcase(Suite, TC, Config,Return,State) ->
+ maybe_sleep(?FUNCTION_NAME,Suite,TC),
+ {Return,State}.
+
+%%%-----------------------------------------------------------------
+maybe_sleep(FuncName,Suite) ->
+ maybe_sleep(FuncName,Suite,undefined).
+maybe_sleep(FuncName,Suite,GroupOrTC) ->
+ case ct:get_config(FuncName) of
+ {Suite,GroupOrTC,Fail} ->
+ fail(Fail);
+ {Suite,Fail} when GroupOrTC=:=undefined ->
+ fail(Fail);
+ _ ->
+ ok
+ end.
+
+fail({timeout,T}) ->
+ timer:sleep(T);
+fail(kill) ->
+ spawn_link(fun() -> ct:fail(hahahahahah) end),
+ timer:sleep(10000).
+
diff --git a/lib/common_test/test/ct_util_SUITE_data/curr_tc_SUITE.erl b/lib/common_test/test/ct_util_SUITE_data/curr_tc_SUITE.erl
new file mode 100644
index 0000000000..b48ba4d24e
--- /dev/null
+++ b/lib/common_test/test/ct_util_SUITE_data/curr_tc_SUITE.erl
@@ -0,0 +1,59 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-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(curr_tc_SUITE).
+
+-suite_defaults([{timetrap, {seconds, 3}}]).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("ct.hrl").
+
+init_per_suite(Config) ->
+ [{?MODULE,?FUNCTION_NAME}] = ct_util:get_testdata(curr_tc),
+ Config.
+
+end_per_suite(Config) ->
+ [{?MODULE,?FUNCTION_NAME}] = ct_util:get_testdata(curr_tc),
+ ok.
+
+init_per_group(_Group,Config) ->
+ [{?MODULE,?FUNCTION_NAME}] = ct_util:get_testdata(curr_tc),
+ Config.
+
+end_per_group(_Group,Config) ->
+ [{?MODULE,?FUNCTION_NAME}] = ct_util:get_testdata(curr_tc),
+ ok.
+
+all() ->
+ [tc1,tc2,{group,g}].
+
+groups() ->
+ [{g,[tc1,tc2]}].
+
+%% Test cases starts here.
+tc1(_Config) ->
+ [{?MODULE,?FUNCTION_NAME}] = ct_util:get_testdata(curr_tc),
+ ok.
+
+tc2(_Config) ->
+ [{?MODULE,?FUNCTION_NAME}] = ct_util:get_testdata(curr_tc),
+ ok.
diff --git a/lib/common_test/vsn.mk b/lib/common_test/vsn.mk
index 23eb8d9656..14a3622a00 100644
--- a/lib/common_test/vsn.mk
+++ b/lib/common_test/vsn.mk
@@ -1 +1 @@
-COMMON_TEST_VSN = 1.17
+COMMON_TEST_VSN = 1.17.1
diff --git a/lib/compiler/src/beam_asm.erl b/lib/compiler/src/beam_asm.erl
index bc1290f6fd..df09dcb06c 100644
--- a/lib/compiler/src/beam_asm.erl
+++ b/lib/compiler/src/beam_asm.erl
@@ -407,14 +407,14 @@ encode_arg({atom, Atom}, Dict0) when is_atom(Atom) ->
{Index, Dict} = beam_dict:atom(Atom, Dict0),
{encode(?tag_a, Index), Dict};
encode_arg({integer, N}, Dict) ->
- %% Conservatily assume that all integers whose absolute
+ %% Conservatively assume that all integers whose absolute
%% value is greater than 1 bsl 128 will be bignums in
%% the runtime system.
if
N >= 1 bsl 128 ->
- encode_arg({literal, N}, Dict);
+ encode_literal(N, Dict);
N =< -(1 bsl 128) ->
- encode_arg({literal, N}, Dict);
+ encode_literal(N, Dict);
true ->
{encode(?tag_i, N), Dict}
end;
@@ -434,7 +434,7 @@ encode_arg({list, List}, Dict0) ->
{L, Dict} = encode_list(List, Dict0, []),
{[encode(?tag_z, 1), encode(?tag_u, length(List))|L], Dict};
encode_arg({float, Float}, Dict) when is_float(Float) ->
- encode_arg({literal,Float}, Dict);
+ encode_literal(Float, Dict);
encode_arg({fr,Fr}, Dict) ->
{[encode(?tag_z, 2),encode(?tag_u, Fr)], Dict};
encode_arg({field_flags,Flags0}, Dict) ->
@@ -442,12 +442,24 @@ encode_arg({field_flags,Flags0}, Dict) ->
{encode(?tag_u, Flags), Dict};
encode_arg({alloc,List}, Dict) ->
encode_alloc_list(List, Dict);
-encode_arg({literal,Lit}, Dict0) ->
- {Index,Dict} = beam_dict:literal(Lit, Dict0),
- {[encode(?tag_z, 4),encode(?tag_u, Index)],Dict};
+encode_arg({literal,Lit}, Dict) ->
+ if
+ Lit =:= [] ->
+ encode_arg(nil, Dict);
+ is_atom(Lit) ->
+ encode_arg({atom,Lit}, Dict);
+ is_integer(Lit) ->
+ encode_arg({integer,Lit}, Dict);
+ true ->
+ encode_literal(Lit, Dict)
+ end;
encode_arg(Int, Dict) when is_integer(Int) ->
{encode(?tag_u, Int),Dict}.
+encode_literal(Literal, Dict0) ->
+ {Index,Dict} = beam_dict:literal(Literal, Dict0),
+ {[encode(?tag_z, 4),encode(?tag_u, Index)],Dict}.
+
%%flag_to_bit(aligned) -> 16#01; %% No longer useful.
flag_to_bit(little) -> 16#02;
flag_to_bit(big) -> 16#00;
diff --git a/lib/crypto/doc/src/notes.xml b/lib/crypto/doc/src/notes.xml
index c0b302734e..195c9d029d 100644
--- a/lib/crypto/doc/src/notes.xml
+++ b/lib/crypto/doc/src/notes.xml
@@ -31,6 +31,22 @@
</header>
<p>This document describes the changes made to the Crypto application.</p>
+<section><title>Crypto 4.4.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed build link error on Windows. Unresolved symbol
+ 'bcmp'.</p>
+ <p>
+ Own Id: OTP-15750 Aux Id: ERL-905 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Crypto 4.4.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/crypto/vsn.mk b/lib/crypto/vsn.mk
index deba17fb66..0a3d9f45e4 100644
--- a/lib/crypto/vsn.mk
+++ b/lib/crypto/vsn.mk
@@ -1 +1 @@
-CRYPTO_VSN = 4.4.1
+CRYPTO_VSN = 4.4.2
diff --git a/lib/diameter/doc/src/notes.xml b/lib/diameter/doc/src/notes.xml
index 5777225ae7..8dcba93273 100644
--- a/lib/diameter/doc/src/notes.xml
+++ b/lib/diameter/doc/src/notes.xml
@@ -43,6 +43,23 @@ first.</p>
<!-- ===================================================================== -->
+<section><title>diameter 2.2.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix inadvertently broad monitor that resulted in
+ gen_server cast messages to hidden nodes from module
+ diameter_dist.</p>
+ <p>
+ Own Id: OTP-15768</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>diameter 2.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/diameter/src/base/diameter_dist.erl b/lib/diameter/src/base/diameter_dist.erl
index 5c29ea95a4..ed23152b8b 100644
--- a/lib/diameter/src/base/diameter_dist.erl
+++ b/lib/diameter/src/base/diameter_dist.erl
@@ -454,7 +454,8 @@ start_link() ->
init([]) ->
ets:new(?NODE_TABLE, [set, named_table]),
ets:new(?SERVICE_TABLE, [bag, named_table]),
- ok = net_kernel:monitor_nodes(true, [{node_type, all}, nodedown_reason]),
+ ok = net_kernel:monitor_nodes(true, [{node_type, visible},
+ nodedown_reason]),
ets:insert(?NODE_TABLE, [{?B(N), N} || N <- [node() | nodes()]]),
abcast({attach, node()}),
{ok, sets:new()}.
@@ -521,5 +522,14 @@ terminate(_, _) ->
%% code_change/3
+%% Old code inadvertently monitored all nodes: start a new
+%% subscription and remove the old one.
+code_change(_OldVsn, State, "2.2") ->
+ ok = net_kernel:monitor_nodes(true, [{node_type, visible},
+ nodedown_reason]),
+ ok = net_kernel:monitor_nodes(false, [{node_type, all},
+ nodedown_reason]),
+ {ok, State};
+
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
diff --git a/lib/diameter/src/diameter.appup.src b/lib/diameter/src/diameter.appup.src
index 52263633fb..bb2a4a8e92 100644
--- a/lib/diameter/src/diameter.appup.src
+++ b/lib/diameter/src/diameter.appup.src
@@ -61,7 +61,8 @@
{"2.1.4", [{restart_application, diameter}]}, %% 20.3
{"2.1.4.1", [{restart_application, diameter}]}, %% 20.3.8.19
{"2.1.5", [{restart_application, diameter}]}, %% 21.0
- {"2.1.6", [{restart_application, diameter}]} %% 21.1
+ {"2.1.6", [{restart_application, diameter}]}, %% 21.1
+ {"2.2", [{update, diameter_dist, {advanced, "2.2"}}]} %% 21.3
],
[
{"0.9", [{restart_application, diameter}]},
@@ -104,6 +105,7 @@
{"2.1.4", [{restart_application, diameter}]},
{"2.1.4.1", [{restart_application, diameter}]},
{"2.1.5", [{restart_application, diameter}]},
- {"2.1.6", [{restart_application, diameter}]}
+ {"2.1.6", [{restart_application, diameter}]},
+ {"2.2", [{restart_application, diameter}]}
]
}.
diff --git a/lib/diameter/vsn.mk b/lib/diameter/vsn.mk
index a900e8f28e..a8fbca5bc8 100644
--- a/lib/diameter/vsn.mk
+++ b/lib/diameter/vsn.mk
@@ -17,5 +17,5 @@
# %CopyrightEnd%
APPLICATION = diameter
-DIAMETER_VSN = 2.2
+DIAMETER_VSN = 2.2.1
APP_VSN = $(APPLICATION)-$(DIAMETER_VSN)$(PRE_VSN)
diff --git a/lib/edoc/doc/edoc.dtd b/lib/edoc/doc/edoc.dtd
new file mode 120000
index 0000000000..43f4b27db6
--- /dev/null
+++ b/lib/edoc/doc/edoc.dtd
@@ -0,0 +1 @@
+../priv/edoc.dtd \ No newline at end of file
diff --git a/lib/edoc/doc/edoc_doclet.hrl b/lib/edoc/doc/edoc_doclet.hrl
new file mode 120000
index 0000000000..4623b18bb4
--- /dev/null
+++ b/lib/edoc/doc/edoc_doclet.hrl
@@ -0,0 +1 @@
+../include/edoc_doclet.hrl \ No newline at end of file
diff --git a/lib/edoc/doc/src/Makefile b/lib/edoc/doc/src/Makefile
index aba94a6802..3e53e75c75 100644
--- a/lib/edoc/doc/src/Makefile
+++ b/lib/edoc/doc/src/Makefile
@@ -79,6 +79,11 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
+INCLUDES_DIR = ../../include
+INCLUDES = $(INCLUDES_DIR)/edoc_doclet.hrl
+
+DTDS_DIR = ../../priv
+DTDS = $(DTDS_DIR)/edoc.dtd
# ----------------------------------------------------
# FLAGS
@@ -135,5 +140,6 @@ release_docs_spec: docs
$(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
$(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
$(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
+ $(INSTALL_DATA) $(INCLUDES) $(DTDS) "$(RELSYSDIR)/doc/html"
release_spec:
diff --git a/lib/edoc/include/edoc_doclet.hrl b/lib/edoc/include/edoc_doclet.hrl
index 1429ee5971..a05a9cb2bc 100644
--- a/lib/edoc/include/edoc_doclet.hrl
+++ b/lib/edoc/include/edoc_doclet.hrl
@@ -43,7 +43,7 @@
%% @type no_app().
%% A value used to mark absence of an Erlang application
%% context. Use the macro `NO_APP' defined in
-%% <a href="../include/edoc_doclet.hrl">`edoc_doclet.hrl'</a>
+%% <a href="edoc_doclet.hrl">`edoc_doclet.hrl'</a>
%% to produce this value.
%% @type doclet_gen() = #doclet_gen{sources = [string()],
diff --git a/lib/edoc/src/edoc.erl b/lib/edoc/src/edoc.erl
index e9d62d3283..62483602aa 100644
--- a/lib/edoc/src/edoc.erl
+++ b/lib/edoc/src/edoc.erl
@@ -734,7 +734,7 @@ get_doc(File) ->
%%
%% @type edoc_module(). The EDoc documentation data for a module,
%% expressed as an XML document in {@link //xmerl. XMerL} format. See
-%% the file <a href="../priv/edoc.dtd">`edoc.dtd'</a> for details.
+%% the file <a href="edoc.dtd">`edoc.dtd'</a> for details.
%%
%% @doc Reads a source code file and extracts EDoc documentation data.
%% Note that without an environment parameter (see {@link get_doc/3}),
diff --git a/lib/edoc/src/edoc_doclet.erl b/lib/edoc/src/edoc_doclet.erl
index 6cb3095507..604291374a 100644
--- a/lib/edoc/src/edoc_doclet.erl
+++ b/lib/edoc/src/edoc_doclet.erl
@@ -62,7 +62,7 @@
%% @spec (Command::doclet_gen() | doclet_toc(), edoc_context()) -> ok
%% @doc Main doclet entry point. See the file <a
-%% href="../include/edoc_doclet.hrl">`edoc_doclet.hrl'</a> for the data
+%% href="edoc_doclet.hrl">`edoc_doclet.hrl'</a> for the data
%% structures used for passing parameters.
%%
%% Also see {@link edoc:layout/2} for layout-related options, and
diff --git a/lib/erl_interface/doc/src/notes.xml b/lib/erl_interface/doc/src/notes.xml
index 32d28b853b..5ad0e2499b 100644
--- a/lib/erl_interface/doc/src/notes.xml
+++ b/lib/erl_interface/doc/src/notes.xml
@@ -31,6 +31,22 @@
</header>
<p>This document describes the changes made to the Erl_interface application.</p>
+<section><title>Erl_Interface 3.11.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix handling of Makefile dependencies so that parallel
+ make works properly.</p>
+ <p>
+ Own Id: OTP-15757</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erl_Interface 3.11.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/erl_interface/vsn.mk b/lib/erl_interface/vsn.mk
index dae6052d55..5e63f75ab5 100644
--- a/lib/erl_interface/vsn.mk
+++ b/lib/erl_interface/vsn.mk
@@ -1,2 +1,2 @@
-EI_VSN = 3.11.1
+EI_VSN = 3.11.2
ERL_INTERFACE_VSN = $(EI_VSN)
diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml
index 91dd9cd6ed..2710ea2f2f 100644
--- a/lib/inets/doc/src/notes.xml
+++ b/lib/inets/doc/src/notes.xml
@@ -33,7 +33,29 @@
<file>notes.xml</file>
</header>
- <section><title>Inets 7.0.6</title>
+ <section><title>Inets 7.0.7</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix the internal handling of the option
+ erl_script_timeout in httpd. If explicit
+ erl_script_timeout value was supplied in seconds it was
+ not correctly converted to millisecond units for internal
+ usage.</p>
+ <p>
+ This change fixes the handling of erl_script_timeout in
+ all possible configuration scenarios.</p>
+ <p>
+ Own Id: OTP-15769 Aux Id: ERIERL-345 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 7.0.6</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/inets/src/http_server/httpd_example.erl b/lib/inets/src/http_server/httpd_example.erl
index 37e4f97bc0..aaa7e428c2 100644
--- a/lib/inets/src/http_server/httpd_example.erl
+++ b/lib/inets/src/http_server/httpd_example.erl
@@ -24,7 +24,7 @@
-export([newformat/3, post_chunked/3, post_204/3]).
%% These are used by the inets test-suite
--export([delay/1, chunk_timeout/3]).
+-export([delay/1, chunk_timeout/3, get_chunks/3]).
print(String) ->
@@ -196,3 +196,22 @@ chunk_timeout(SessionID, _, _StrInt) ->
mod_esi:deliver(SessionID, top("Test chunk encoding timeout")),
timer:sleep(20000),
mod_esi:deliver(SessionID, footer()).
+
+get_chunks(Sid, _Env, In) ->
+ Tokens = string:tokens(In, [$&]),
+ PropList = lists:map(fun(E) ->
+ list_to_tuple(string:tokens(E,[$=])) end,
+ Tokens),
+ HeaderDelay =
+ list_to_integer(proplists:get_value("header_delay", PropList, "0")),
+ ChunkDelay =
+ list_to_integer(proplists:get_value("chunk_delay", PropList, "0")),
+ BadChunkDelay =
+ list_to_integer(proplists:get_value("bad_chunk_delay", PropList, "0")),
+ timer:sleep(HeaderDelay),
+ mod_esi:deliver(Sid, ["Content-Type: text/plain\r\n\r\n"]),
+ mod_esi:deliver(Sid, "Chunk 0 ms\r\n"),
+ timer:sleep(ChunkDelay),
+ mod_esi:deliver(Sid, io_lib:format("Chunk ~p ms\r\n", [ChunkDelay])),
+ timer:sleep(ChunkDelay + BadChunkDelay),
+ mod_esi:deliver(Sid, "BAD Chunk\r\n").
diff --git a/lib/inets/src/http_server/mod_esi.erl b/lib/inets/src/http_server/mod_esi.erl
index f495f12f03..8cbd9798e6 100644
--- a/lib/inets/src/http_server/mod_esi.erl
+++ b/lib/inets/src/http_server/mod_esi.erl
@@ -119,7 +119,7 @@ load("EvalScriptAlias " ++ EvalScriptAlias, []) ->
load("ErlScriptTimeout " ++ Timeout, [])->
case catch list_to_integer(string:strip(Timeout)) of
TimeoutSec when is_integer(TimeoutSec) ->
- {ok, [], {erl_script_timeout, TimeoutSec * 1000}};
+ {ok, [], {erl_script_timeout, TimeoutSec}};
_ ->
{error, ?NICE(string:strip(Timeout) ++
" is an invalid ErlScriptTimeout")}
@@ -500,7 +500,7 @@ kill_esi_delivery_process(Pid) ->
erl_script_timeout(Db) ->
- httpd_util:lookup(Db, erl_script_timeout, ?DEFAULT_ERL_TIMEOUT * 1000).
+ httpd_util:lookup(Db, erl_script_timeout, ?DEFAULT_ERL_TIMEOUT) * 1000.
script_elements(FuncAndInput, Input) ->
case input_type(FuncAndInput) of
diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl
index fcb9ad7905..fc5ca14dcd 100644
--- a/lib/inets/test/httpd_SUITE.erl
+++ b/lib/inets/test/httpd_SUITE.erl
@@ -79,7 +79,10 @@ all() ->
{group, http_not_sup},
{group, https_not_sup},
mime_types_format,
- erl_script_timeout_option
+ erl_script_timeout_default,
+ erl_script_timeout_option,
+ erl_script_timeout_proplist,
+ erl_script_timeout_apache
].
groups() ->
@@ -384,6 +387,10 @@ init_per_testcase(disk_log_bad_file, Config0) ->
ct:timetrap({seconds, 20}),
dbg(disk_log_internal, Config1, init);
+init_per_testcase(erl_script_timeout_default, Config) ->
+ ct:timetrap({seconds, 60}),
+ dbg(erl_script_timeout_default, Config, init);
+
init_per_testcase(Case, Config) ->
ct:timetrap({seconds, 20}),
dbg(Case, Config, init).
@@ -1777,16 +1784,128 @@ mime_types_format(Config) when is_list(Config) ->
{"cpt","application/mac-compactpro"},
{"hqx","application/mac-binhex40"}]} = httpd_conf:load_mime_types(MimeTypes).
+erl_script_timeout_default(Config) when is_list(Config) ->
+ inets:start(),
+ {ok, Pid} = inets:start(httpd,
+ [{port, 0},
+ {server_name,"localhost"},
+ {server_root,"./"},
+ {document_root,"./"},
+ {bind_address, any},
+ {mimetypes, [{"html", "text/html"}]},
+ {modules,[mod_esi]},
+ {erl_script_alias, {"/erl", [httpd_example]}}
+ ]),
+ Info = httpd:info(Pid),
+
+ Port = proplists:get_value(port, Info),
+
+ %% Default erl_script_timeout is 15.
+ %% Verify: 13 =< erl_script_timeout =< 17
+ Url = http_get_url(Port, 500, 13000, 4000),
+
+ {ok, {_, _, Body}} = httpc:request(get, {Url, []}, [{timeout, 45000}], []),
+ ct:log("Response: ~p~n", [Body]),
+ verify_body(Body, 13000),
+ inets:stop().
erl_script_timeout_option(Config) when is_list(Config) ->
inets:start(),
- {ok, Pid} = inets:start(httpd, [{erl_script_timeout, 215},
- {server_name, "test"},
- {port,0},
- {server_root, "."},
- {document_root, "."}]),
+ {ok, Pid} = inets:start(httpd,
+ [{port, 0},
+ {server_name,"localhost"},
+ {server_root,"./"},
+ {document_root,"./"},
+ {bind_address, any},
+ {mimetypes, [{"html", "text/html"}]},
+ {modules,[mod_esi]},
+ {erl_script_timeout, 2},
+ {erl_script_alias, {"/erl", [httpd_example]}}
+ ]),
Info = httpd:info(Pid),
- 215 = proplists:get_value(erl_script_timeout, Info),
+ verify_timeout(Info, 2),
+
+ Port = proplists:get_value(port, Info),
+
+ %% Verify: 1 =< erl_script_timeout =< 3
+ Url = http_get_url(Port, 500, 1000, 2000),
+
+ {ok, {_, _, Body}} = httpc:request(Url),
+ ct:log("Response: ~p~n", [Body]),
+ verify_body(Body, 1000),
+ inets:stop().
+
+erl_script_timeout_proplist(Config) when is_list(Config) ->
+ HttpdConf = filename:join(get_tmp_dir(Config),
+ "httpd_erl_script_timeout_proplist.conf"),
+ ServerConfig =
+ "[{port, 0},\n" ++
+ " {server_name,\"localhost\"},\n" ++
+ " {server_root,\"./\"},\n" ++
+ " {document_root,\"./\"},\n" ++
+ " {bind_address, any},\n" ++
+ " {mimetypes, [{\"html\", \"text/html\"}]},\n" ++
+ " {modules,[mod_esi]},\n" ++
+ " {erl_script_timeout, 5},\n" ++
+ " {erl_script_alias, {\"/erl\", [httpd_example]}}\n" ++
+ "].",
+ ok = file:write_file(HttpdConf, ServerConfig),
+
+ inets:start(),
+ {ok, Pid} = inets:start(httpd,
+ [{proplist_file, HttpdConf}]),
+ Info = httpd:info(Pid),
+ verify_timeout(Info, 5),
+
+ Port = proplists:get_value(port, Info),
+
+ %% Verify: 3 =< erl_script_timeout =< 7
+ Url = http_get_url(Port, 500, 3000, 4000),
+
+ {ok, {_, _, Body}} = httpc:request(Url),
+ ct:log("Response: ~p~n", [Body]),
+ verify_body(Body, 3000),
+ inets:stop().
+
+erl_script_timeout_apache(Config) when is_list(Config) ->
+ HttpdConf = filename:join(get_tmp_dir(Config),
+ "httpd_erl_script_timeout.conf"),
+ MimeTypes = filename:join(get_tmp_dir(Config),
+ "erl_script_timeout_mime_types.conf"),
+
+ MimeTypesConf =
+ "html\n" ++
+ "text/html\n",
+
+ ok = file:write_file(MimeTypes, MimeTypesConf),
+
+ ServerConfig =
+ "Port 0\n" ++
+ "ServerName localhost\n" ++
+ "ServerRoot ./\n" ++
+ "DocumentRoot ./\n" ++
+ "BindAddress 0.0.0.0\n" ++
+ "MimeTypes " ++ MimeTypes ++ "\n" ++
+ "Modules mod_esi\n" ++
+ "ErlScriptTimeout 8\n" ++
+ "ErlScriptAlias /erl httpd_example\n",
+
+ ok = file:write_file(HttpdConf, ServerConfig),
+
+ inets:start(),
+ {ok, Pid} = inets:start(httpd,
+ [{file, HttpdConf}]),
+ Info = httpd:info(Pid),
+ verify_timeout(Info, 8),
+
+ Port = proplists:get_value(port, Info),
+
+ %% Verify: 6 =< erl_script_timeout =< 10
+ Url = http_get_url(Port, 500, 6000, 4000),
+
+ {ok, {_, _, Body}} = httpc:request(Url),
+ ct:log("Response: ~p~n", [Body]),
+ verify_body(Body, 6000),
inets:stop().
@@ -1798,6 +1917,38 @@ url(http, End, Config) ->
{ok,Host} = inet:gethostname(),
?URL_START ++ Host ++ ":" ++ integer_to_list(Port) ++ End.
+http_get_url(Port0, HeaderDelay, ChunkDelay, BadChunkDelay) ->
+ {ok, Host} = inet:gethostname(),
+ Port = integer_to_list(Port0),
+ HD = integer_to_list(HeaderDelay),
+ CD = integer_to_list(ChunkDelay),
+ BD = integer_to_list(BadChunkDelay),
+ "http://" ++ Host ++ ":" ++ Port ++
+ "/erl/httpd_example/get_chunks?header_delay=" ++ HD ++
+ "&chunk_delay=" ++ CD ++
+ "&bad_chunk_delay=" ++ BD.
+
+verify_body(Body, Timeout0) ->
+ Timeout = integer_to_list(Timeout0),
+ Res = string:find(Body, Timeout),
+ ct:log("Result: ~p~n", [Res]),
+ %% Fail if BAD chunk is found.
+ case Res =:= Timeout ++ " ms\r\n" of
+ true ->
+ ok;
+ false ->
+ ct:fail("Unexpected chunk received!")
+ end.
+
+verify_timeout(Info, Expected) ->
+ Timeout = proplists:get_value(erl_script_timeout, Info),
+ case Timeout =:= Expected of
+ true ->
+ ok;
+ false ->
+ ct:fail("Bad Timeout - Expected: ~p Got: ~p", [Expected, Timeout])
+ end.
+
do_max_clients(Config) ->
Version = proplists:get_value(http_version, Config),
Host = proplists:get_value(host, Config),
diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk
index b7ddf39ebd..fd248e793a 100644
--- a/lib/inets/vsn.mk
+++ b/lib/inets/vsn.mk
@@ -19,6 +19,6 @@
# %CopyrightEnd%
APPLICATION = inets
-INETS_VSN = 7.0.6
+INETS_VSN = 7.0.7
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)"
diff --git a/lib/kernel/src/erts_debug.erl b/lib/kernel/src/erts_debug.erl
index c4d276f9e8..e6a30d0b92 100644
--- a/lib/kernel/src/erts_debug.erl
+++ b/lib/kernel/src/erts_debug.erl
@@ -33,6 +33,7 @@
-export([breakpoint/2, disassemble/1, display/1, dist_ext_to_term/2,
flat_size/1, get_internal_state/1, instructions/0,
+ interpreter_size/0,
map_info/1, same/2, set_internal_state/2,
size_shared/1, copy_shared/1, dirty_cpu/2, dirty_io/2, dirty/3,
lcnt_control/1, lcnt_control/2, lcnt_collect/0, lcnt_clear/0,
@@ -118,6 +119,11 @@ get_internal_state(_) ->
instructions() ->
erlang:nif_error(undef).
+-spec interpreter_size() -> pos_integer().
+
+interpreter_size() ->
+ erlang:nif_error(undef).
+
-spec ic(F) -> Result when
F :: function(),
Result :: term().
diff --git a/lib/os_mon/src/cpu_sup.erl b/lib/os_mon/src/cpu_sup.erl
index ba2d89313e..d28f229b3e 100644
--- a/lib/os_mon/src/cpu_sup.erl
+++ b/lib/os_mon/src/cpu_sup.erl
@@ -68,7 +68,7 @@
-type util_cpus() :: 'all' | integer() | [integer()].
-type util_state() :: 'user' | 'nice_user' | 'kernel' | 'wait' | 'idle'.
--type util_value() :: [{util_state(), float()}] | float().
+-type util_value() :: [{util_state(), number()}] | number().
-type util_desc() :: {util_cpus(), util_value(), util_value(), []}.
%%----------------------------------------------------------------------
@@ -122,7 +122,7 @@ util(Args) when is_list (Args) ->
util(_) ->
erlang:error(badarg).
--spec util() -> float() | {'error', any()}.
+-spec util() -> number() | {'error', any()}.
util() ->
case util([]) of
diff --git a/lib/snmp/src/agent/snmpa_agent.erl b/lib/snmp/src/agent/snmpa_agent.erl
index a521b3773b..f280260f47 100644
--- a/lib/snmp/src/agent/snmpa_agent.erl
+++ b/lib/snmp/src/agent/snmpa_agent.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-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.
@@ -1794,9 +1794,8 @@ worker_loop(Master) ->
GbMaxVBs, Extra)
end
catch
- T:E ->
- exit({worker_crash, Req, T, E,
- erlang:get_stacktrace()})
+ C:E:S ->
+ exit({worker_crash, Req, C, E, S})
end,
Master ! worker_available,
HandlePduRes; % For debugging...
@@ -1822,9 +1821,8 @@ worker_loop(Master) ->
get(net_if))
end
catch
- T:E ->
- exit({worker_crash, Req, T, E,
- erlang:get_stacktrace()})
+ C:E:S ->
+ exit({worker_crash, Req, C, E, S})
end,
Master ! worker_available,
SendTrapRes; % For debugging...
@@ -2543,22 +2541,31 @@ process_msg(
process_pdu(#pdu{type='get-request', request_id = ReqId, varbinds=Vbs},
_PduMS, Vsn, MibView, _GbMaxVBs) ->
?vtrace("get ~p",[ReqId]),
- Res = get_err(do_get(MibView, Vbs, false)),
- ?vtrace("get result: "
- "~n ~p",[Res]),
+ OrigRes = do_get(MibView, Vbs, false),
+ Res = get_err(OrigRes),
{ErrStatus, ErrIndex, ResVarbinds} =
if
Vsn =:= 'version-1' -> validate_get_v1(Res);
true -> Res
end,
- ?vtrace("get final result: "
- "~n Error status: ~p"
- "~n Error index: ~p"
- "~n Varbinds: ~p",
- [ErrStatus,ErrIndex,ResVarbinds]),
+ if
+ (ErrStatus =/= noError) ->
+ ?vlog("get final result: "
+ "~n Error status: ~p"
+ "~n Error index: ~p"
+ "~n when"
+ "~n Original Result: "
+ "~n ~p", [ErrStatus, ErrIndex, OrigRes]);
+ true ->
+ ?vtrace("get final result: "
+ "~n Error status: ~p"
+ "~n Error index: ~p"
+ "~n Varbinds: ~p",
+ [ErrStatus, ErrIndex, ResVarbinds])
+ end,
ResponseVarbinds = lists:keysort(#varbind.org_index, ResVarbinds),
?vtrace("response varbinds: "
- "~n ~p",[ResponseVarbinds]),
+ "~n ~p", [ResponseVarbinds]),
make_response_pdu(ReqId, ErrStatus, ErrIndex, Vbs, ResponseVarbinds);
process_pdu(#pdu{type = 'get-next-request', request_id = ReqId, varbinds = Vbs},
@@ -2566,22 +2573,31 @@ process_pdu(#pdu{type = 'get-next-request', request_id = ReqId, varbinds = Vbs},
?vtrace("process get-next-request -> entry with"
"~n ReqId: ~p"
"~n Vbs: ~p"
- "~n MibView: ~p",[ReqId, Vbs, MibView]),
- Res = get_err(do_get_next(MibView, Vbs)),
- ?vtrace("get-next result: "
- "~n ~p",[Res]),
+ "~n MibView: ~p", [ReqId, Vbs, MibView]),
+ OrigRes = do_get_next(MibView, Vbs),
+ Res = get_err(OrigRes),
{ErrStatus, ErrIndex, ResVarbinds} =
if
Vsn =:= 'version-1' -> validate_next_v1(Res, MibView);
true -> Res
end,
- ?vtrace("get-next final result -> validation result:"
- "~n Error status: ~p"
- "~n Error index: ~p"
- "~n Varbinds: ~p",[ErrStatus,ErrIndex,ResVarbinds]),
+ if
+ (ErrStatus =/= noError) ->
+ ?vlog("get-next final result: "
+ "~n Error status: ~p"
+ "~n Error index: ~p"
+ "~n when"
+ "~n Original Result: "
+ "~n ~p", [ErrStatus, ErrIndex, OrigRes]);
+ true ->
+ ?vtrace("get-next final result:"
+ "~n Error status: ~p"
+ "~n Error index: ~p"
+ "~n Varbinds: ~p", [ErrStatus, ErrIndex, ResVarbinds])
+ end,
ResponseVarbinds = lists:keysort(#varbind.org_index, ResVarbinds),
?vtrace("get-next final result -> response varbinds: "
- "~n ~p",[ResponseVarbinds]),
+ "~n ~p", [ResponseVarbinds]),
make_response_pdu(ReqId, ErrStatus, ErrIndex, Vbs, ResponseVarbinds);
process_pdu(#pdu{type = 'get-bulk-request',
@@ -2590,31 +2606,50 @@ process_pdu(#pdu{type = 'get-bulk-request',
error_status = NonRepeaters,
error_index = MaxRepetitions},
PduMS, _Vsn, MibView, GbMaxVBs) ->
- {ErrStatus, ErrIndex, ResponseVarbinds} =
- get_err(do_get_bulk(MibView, NonRepeaters, MaxRepetitions, PduMS, Vbs,
- GbMaxVBs)),
- ?vtrace("get-bulk final result: "
- "~n Error status: ~p"
- "~n Error index: ~p"
- "~n Respons varbinds: ~p",
- [ErrStatus,ErrIndex,ResponseVarbinds]),
+ OrigRes = do_get_bulk(MibView, NonRepeaters, MaxRepetitions, PduMS, Vbs,
+ GbMaxVBs),
+ {ErrStatus, ErrIndex, ResponseVarbinds} = get_err(OrigRes),
+ if
+ (ErrStatus =/= noError) ->
+ ?vlog("get-bulk final result: "
+ "~n Error Status: ~p"
+ "~n Error Index: ~p"
+ "~n when"
+ "~n Original Result: "
+ "~n ~p", [ErrStatus, ErrIndex, OrigRes]);
+ true ->
+ ?vtrace("get-bulk final result: "
+ "~n Error status: ~p"
+ "~n Error index: ~p"
+ "~n Response Varbinds: ~p",
+ [ErrStatus, ErrIndex, ResponseVarbinds])
+ end,
make_response_pdu(ReqId, ErrStatus, ErrIndex, Vbs, ResponseVarbinds);
process_pdu(#pdu{type = 'set-request', request_id = ReqId, varbinds = Vbs},
- _PduMS, Vsn, MibView, _GbMaxVbs)->
- Res = do_set(MibView, Vbs),
- ?vtrace("set result: "
- "~n ~p",[Res]),
+ _PduMS, Vsn, MibView, _GbMaxVbs) ->
+ OrigRes = do_set(MibView, Vbs),
{ErrStatus, ErrIndex} =
if
- Vsn =:= 'version-1' -> validate_err(v2_to_v1, Res);
- true -> Res
+ Vsn =:= 'version-1' -> validate_err(v2_to_v1, OrigRes);
+ true -> OrigRes
end,
- ?vtrace("set final result: "
- "~n Error status: ~p"
- "~n Error index: ~p",[ErrStatus,ErrIndex]),
+ if
+ (ErrStatus =/= noError) ->
+ ?vlog("set final result: "
+ "~n Error Status: ~p"
+ "~n Error Index: ~p"
+ "~n when"
+ "~n Original Result: "
+ "~n ~p", [ErrStatus, ErrIndex, OrigRes]);
+ true ->
+ ?vtrace("set final result: "
+ "~n Error Status: ~p"
+ "~n Error Index: ~p", [ErrStatus, ErrIndex])
+ end,
make_response_pdu(ReqId, ErrStatus, ErrIndex, Vbs, Vbs).
+
%%-----------------------------------------------------------------
%% Transform a value == noSuchInstance | noSuchObject or a
%% Counter64 type to a noSuchName error for the whole pdu.
diff --git a/lib/snmp/src/agent/snmpa_local_db.erl b/lib/snmp/src/agent/snmpa_local_db.erl
index eb67b9cd6f..f481641242 100644
--- a/lib/snmp/src/agent/snmpa_local_db.erl
+++ b/lib/snmp/src/agent/snmpa_local_db.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-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.
@@ -147,12 +147,13 @@ init([Prio, DbDir, DbInitError, Opts]) ->
do_init(Prio, DbDir, DbInitError, Opts) ->
process_flag(priority, Prio),
process_flag(trap_exit, true),
- put(sname,ldb),
- put(verbosity,get_opt(verbosity, Opts, ?default_verbosity)),
+ put(sname, get_opt(sname, Opts, ldb)),
+ put(verbosity, get_opt(verbosity, Opts, ?default_verbosity)),
?vlog("starting",[]),
Dets = dets_open(DbDir, DbInitError, Opts),
Ets = ets:new(?ETS_TAB, [set, protected]),
?vdebug("started",[]),
+ put(started, snmp_misc:formated_timestamp()),
{ok, #state{dets = Dets, ets = Ets}}.
dets_open(DbDir, DbInitError, Opts) ->
@@ -625,7 +626,7 @@ handle_info(Info, State) ->
terminate(Reason, State) ->
- ?vlog("terminate: ~p",[Reason]),
+ ?vlog("terminate: ~p", [Reason]),
close(State).
diff --git a/lib/snmp/src/misc/snmp_misc.erl b/lib/snmp/src/misc/snmp_misc.erl
index 1f847b7a29..0cc04d4056 100644
--- a/lib/snmp/src/misc/snmp_misc.erl
+++ b/lib/snmp/src/misc/snmp_misc.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2015. All Rights Reserved.
+%% Copyright Ericsson AB 1996-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.
@@ -64,8 +64,18 @@
strip_extension_from_filename/2,
str_xor/2,
time/3,
-
- verify_behaviour/2
+
+ verify_behaviour/2,
+
+ %% These are used both for debugging (verbosity printouts)
+ %% and other such "utility" operations.
+ 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
+
]).
@@ -112,6 +122,108 @@ now(sec) ->
erlang:monotonic_time(seconds).
+
+%% ---------------------------------------------------------------------------
+%% # 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 :: os:timestamp()) ->
+ string().
+
+format_timestamp(Now) ->
+ format_long_timestamp(Now).
+
+-spec format_short_timestamp(Now :: os: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 :: os: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 :: os: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 :: os: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 :: os: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).
+ %% case Format of
+ %% short ->
+ %% do_format_short_timestamp(Time, N3);
+ %% long ->
+ %% do_format_long_timestamp(Date, Time, N3)
+ %% end.
+
+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.~.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).
+
+
+
is_crypto_supported(Alg) ->
%% The 'try catch' handles the case when 'crypto' is
%% not present in the system (or not started).
diff --git a/lib/snmp/src/misc/snmp_verbosity.erl b/lib/snmp/src/misc/snmp_verbosity.erl
index edfb52a474..9b2676d048 100644
--- a/lib/snmp/src/misc/snmp_verbosity.erl
+++ b/lib/snmp/src/misc/snmp_verbosity.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2015. All Rights Reserved.
+%% 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.
@@ -70,16 +70,7 @@ print2(_Verbosity,Format,Arguments) ->
timestamp() ->
- format_timestamp(os:timestamp()).
-
-format_timestamp({_N1, _N2, N3} = Now) ->
- {Date, Time} = calendar:now_to_datetime(Now),
- {YYYY,MM,DD} = Date,
- {Hour,Min,Sec} = Time,
- FormatDate =
- io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w ~w",
- [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]),
- lists:flatten(FormatDate).
+ snmp_misc:formated_timestamp().
process_args([], Acc) ->
lists:reverse(Acc);
@@ -155,7 +146,8 @@ image_of_sname(mgr) -> "MGR";
image_of_sname(mgr_misc) -> "MGR_MISC";
image_of_sname(undefined) -> "";
-image_of_sname(V) -> lists:flatten(io_lib:format("~p",[V])).
+image_of_sname(N) when is_list(N) -> N; % Used in testing
+image_of_sname(N) -> lists:flatten(io_lib:format("~p", [N])).
validate(info) -> info;
diff --git a/lib/snmp/test/snmp_agent_test.erl b/lib/snmp/test/snmp_agent_test.erl
index f9c18af6ea..71e3fa3b9a 100644
--- a/lib/snmp/test/snmp_agent_test.erl
+++ b/lib/snmp/test/snmp_agent_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2018. 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.
@@ -1116,15 +1116,15 @@ init_ms(Config, Opts) when is_list(Config) ->
Opts1 = [MasterAgentVerbosity, MibsVerbosity, SymStoreVerbosity | Opts],
[{vsn, v1} | start_v1_agent(Config, Opts1)].
-init_size_check_mse(Config) when is_list(Config) ->
- MibStorage = {mib_storage, [{module, snmpa_mib_storage_ets}]},
- init_size_check_ms(Config, [MibStorage]).
+%% init_size_check_mse(Config) when is_list(Config) ->
+%% MibStorage = {mib_storage, [{module, snmpa_mib_storage_ets}]},
+%% init_size_check_ms(Config, [MibStorage]).
-init_size_check_msd(Config) when is_list(Config) ->
- AgentDbDir = ?GCONF(agent_db_dir, Config),
- MibStorage = {mib_storage, [{module, snmpa_mib_storage_dets},
- {options, [{dir, AgentDbDir}]}]},
- init_size_check_ms(Config, [MibStorage]).
+%% init_size_check_msd(Config) when is_list(Config) ->
+%% AgentDbDir = ?GCONF(agent_db_dir, Config),
+%% MibStorage = {mib_storage, [{module, snmpa_mib_storage_dets},
+%% {options, [{dir, AgentDbDir}]}]},
+%% init_size_check_ms(Config, [MibStorage]).
init_size_check_msm(Config) when is_list(Config) ->
?line AgentNode = ?GCONF(snmp_master, Config),
@@ -5146,12 +5146,21 @@ snmp_framework_mib_3(Config) when is_list(Config) ->
%% Req. SNMP-FRAMEWORK-MIB
snmp_framework_mib_test() ->
?line ["agentEngine"] = get_req(1, [[snmpEngineID,0]]),
+ T1 = snmp_misc:now(ms),
?line [EngineTime] = get_req(2, [[snmpEngineTime,0]]),
+ T2 = snmp_misc:now(ms),
?SLEEP(5000),
+ T3 = snmp_misc:now(ms),
?line [EngineTime2] = get_req(3, [[snmpEngineTime,0]]),
- ?DBG("snmp_framework_mib -> time(s): "
- "~n EngineTime 1 = ~p"
- "~n EngineTime 2 = ~p", [EngineTime, EngineTime2]),
+ T4 = snmp_misc:now(ms),
+ ?PRINT2("snmp_framework_mib -> time(s): "
+ "~n EngineTime 1: ~p"
+ "~n Time to acquire: ~w ms"
+ "~n EngineTime 2: ~p"
+ "~n Time to acquire: ~w ms"
+ "~n => (5 sec sleep between get(snmpEngineTime))"
+ "~n Total time to acquire: ~w ms",
+ [EngineTime, T2-T1, EngineTime2, T4-T3, T4-T1]),
if
(EngineTime+7) < EngineTime2 ->
?line ?FAIL({too_large_diff, EngineTime, EngineTime2});
@@ -5160,11 +5169,18 @@ snmp_framework_mib_test() ->
true ->
ok
end,
+ T5 = snmp_misc:now(ms),
?line case get_req(4, [[snmpEngineBoots,0]]) of
[Boots] when is_integer(Boots) ->
+ T6 = snmp_misc:now(ms),
+ ?PRINT2("snmp_framework_mib -> "
+ "~n boots: ~p"
+ "~n Time to acquire: ~w ms", [Boots, T6-T5]),
ok;
Else ->
- ?FAIL(Else)
+ ?PRINT2("snmp_framework_mib -> failed get proper boots:"
+ "~n ~p", [Else]),
+ ?FAIL({invalid_boots, Else})
end,
ok.
diff --git a/lib/snmp/test/snmp_agent_test_lib.erl b/lib/snmp/test/snmp_agent_test_lib.erl
index c19c88528f..6defdadb5a 100644
--- a/lib/snmp/test/snmp_agent_test_lib.erl
+++ b/lib/snmp/test/snmp_agent_test_lib.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.
@@ -358,22 +358,22 @@ run(Mod, Func, Args, Opts) ->
"~n StdM: ~p",
[M,Vsn,Dir,User,SecLevel,EngineID,CtxEngineID,Community,StdM]),
case snmp_test_mgr:start([%% {agent, snmp_test_lib:hostname()},
- {packet_server_debug,true},
- {debug,true},
- {agent, get(master_host)},
- {ipfamily, get(ipfamily)},
- {agent_udp, 4000},
- {trap_udp, 5000},
- {recbuf,65535},
+ {packet_server_debug, true},
+ {debug, true},
+ {agent, get(master_host)},
+ {ipfamily, get(ipfamily)},
+ {agent_udp, 4000},
+ {trap_udp, 5000},
+ {recbuf, 65535},
quiet,
Vsn,
- {community, Community},
- {user, User},
- {sec_level, SecLevel},
- {engine_id, EngineID},
- {context_engine_id, CtxEngineID},
- {dir, Dir},
- {mibs, mibs(StdM, M)}]) of
+ {community, Community},
+ {user, User},
+ {sec_level, SecLevel},
+ {engine_id, EngineID},
+ {context_engine_id, CtxEngineID},
+ {dir, Dir},
+ {mibs, mibs(StdM, M)}]) of
{ok, _Pid} ->
case (catch apply(Mod, Func, Args)) of
{'EXIT', Reason} ->
@@ -383,10 +383,18 @@ run(Mod, Func, Args, Opts) ->
catch snmp_test_mgr:stop(),
Res
end;
+
+ {error, Reason} ->
+ ?EPRINT2("Failed starting (test) manager: "
+ "~n ~p", [Reason]),
+ catch snmp_test_mgr:stop(),
+ ?line ?FAIL({mgr_start_error, Reason});
+
Err ->
- io:format("Error starting manager: ~p\n", [Err]),
+ ?EPRINT2("Failed starting (test) manager: "
+ "~n ~p", [Err]),
catch snmp_test_mgr:stop(),
- ?line ?FAIL({mgr_start, Err})
+ ?line ?FAIL({mgr_start_failure, Err})
end.
@@ -464,20 +472,24 @@ start_agent(Config, Vsns, Opts) ->
process_flag(trap_exit,true),
+ ?PRINT2("start_agent -> try start snmp app supervisor", []),
{ok, AppSup} = snmp_app_sup:start_link(),
unlink(AppSup),
?DBG("start_agent -> snmp app supervisor: ~p", [AppSup]),
- ?DBG("start_agent -> start master agent",[]),
+ ?PRINT2("start_agent -> try start master agent",[]),
?line Sup = start_sup(Env),
-
- ?DBG("start_agent -> unlink from supervisor", []),
?line unlink(Sup),
+ ?DBG("start_agent -> snmp supervisor: ~p", [Sup]),
+
+ ?PRINT2("start_agent -> try (rpc) start sub agent on ~p", [SaNode]),
?line SaDir = ?config(sa_dir, Config),
- ?DBG("start_agent -> (rpc) start sub on ~p", [SaNode]),
?line {ok, Sub} = start_sub_sup(SaNode, SaDir),
- ?DBG("start_agent -> done",[]),
- ?line [{snmp_sup, {Sup, self()}}, {snmp_sub, Sub} | Config].
+ ?DBG("start_agent -> done", []),
+
+ ?line [{snmp_app_sup, AppSup},
+ {snmp_sup, {Sup, self()}},
+ {snmp_sub, Sub} | Config].
app_agent_env_init(Env0, Opts) ->
@@ -670,35 +682,52 @@ merge_agent_options([{Key, _Value} = Opt|Opts], Options) ->
stop_agent(Config) when is_list(Config) ->
- ?LOG("stop_agent -> entry with"
- "~n Config: ~p",[Config]),
-
- {Sup, Par} = ?config(snmp_sup, Config),
- ?DBG("stop_agent -> attempt to stop (sup) ~p"
- "~n Sup: ~p"
- "~n Par: ~p",
- [Sup,
- (catch process_info(Sup)),
- (catch process_info(Par))]),
-
- _Info = agent_info(Sup),
- ?DBG("stop_agent -> Agent info: "
- "~n ~p", [_Info]),
-
- stop_sup(Sup, Par),
-
- {Sup2, Par2} = ?config(snmp_sub, Config),
- ?DBG("stop_agent -> attempt to stop (sub) ~p"
- "~n Sup2: ~p"
- "~n Par2: ~p",
- [Sup2,
- (catch process_info(Sup2)),
- (catch process_info(Par2))]),
- stop_sup(Sup2, Par2),
-
- ?DBG("stop_agent -> done - now cleanup config", []),
- C1 = lists:keydelete(snmp_sup, 1, Config),
- lists:keydelete(snmp_sub, 1, C1).
+ ?PRINT2("stop_agent -> entry with"
+ "~n Config: ~p",[Config]),
+
+
+ %% Stop the sub-agent (the agent supervisor)
+ {SubSup, SubPar} = ?config(snmp_sub, Config),
+ ?PRINT2("stop_agent -> attempt to stop sub agent (~p)"
+ "~n Sub Sup info: "
+ "~n ~p"
+ "~n Sub Par info: "
+ "~n ~p",
+ [SubSup,
+ (catch process_info(SubSup)),
+ (catch process_info(SubPar))]),
+ stop_sup(SubSup, SubPar),
+ Config2 = lists:keydelete(snmp_sub, 1, Config),
+
+
+ %% Stop the master-agent (the top agent supervisor)
+ {MasterSup, MasterPar} = ?config(snmp_sup, Config),
+ ?PRINT2("stop_agent -> attempt to stop master agent (~p)"
+ "~n Master Sup: "
+ "~n ~p"
+ "~n Master Par: "
+ "~n ~p"
+ "~n Agent Info: "
+ "~n ~p",
+ [MasterSup,
+ (catch process_info(MasterSup)),
+ (catch process_info(MasterPar)),
+ agent_info(MasterSup)]),
+ stop_sup(MasterSup, MasterPar),
+ Config3 = lists:keydelete(snmp_sup, 1, Config2),
+
+
+ %% Stop the top supervisor (of the snmp app)
+ AppSup = ?config(snmp_app_sup, Config),
+ ?PRINT2("stop_agent -> attempt to app sup ~p"
+ "~n App Sup: ~p",
+ [AppSup,
+ (catch process_info(AppSup))]),
+ Config4 = lists:keydelete(snmp_app_sup, 1, Config3),
+
+
+ ?PRINT2("stop_agent -> done", []),
+ Config4.
start_sup(Env) ->
@@ -728,7 +757,6 @@ stop_sup(Pid, _) ->
?LOG("stop_sup -> attempt to stop ~p", [Pid]),
Ref = erlang:monitor(process, Pid),
?LOG("stop_sup -> Ref: ~p", [Ref]),
- %% Pid ! {'EXIT', Parent, shutdown}, % usch
exit(Pid, kill),
await_stopped(Pid, Ref).
@@ -864,13 +892,15 @@ expect(Mod, Line, Type, Enterp, Generic, Specific, ExpVBs) ->
expect2(Mod, Line, Fun).
expect2(Mod, Line, F) ->
- io:format("EXPECT for ~w:~w~n", [Mod, Line]),
+ io_format_expect("for ~w:~w", [Mod, Line]),
case F() of
{error, Reason} ->
- io:format("EXPECT failed at ~w:~w => ~n~p~n", [Mod, Line, Reason]),
+ io_format_expect("failed at ~w:~w => "
+ "~n ~p", [Mod, Line, Reason]),
throw({error, {expect, Mod, Line, Reason}});
Else ->
- io:format("EXPECT result for ~w:~w => ~n~p~n", [Mod, Line, Else]),
+ io_format_expect("result for ~w:~w => "
+ "~n ~p", [Mod, Line, Else]),
Else
end.
@@ -899,20 +929,27 @@ receive_trap(To) ->
end.
+io_format_expect(F) ->
+ io_format_expect(F, []).
+
+io_format_expect(F, A) ->
+ ?PRINT2("EXPECT " ++ F, A).
+
+
do_expect(Expect) when is_atom(Expect) ->
do_expect({Expect, get_timeout()});
do_expect({any_pdu, To})
when is_integer(To) orelse (To =:= infinity) ->
- io:format("EXPECT any PDU~n", []),
+ io_format_expect("any PDU"),
receive_pdu(To);
do_expect({any_trap, To}) ->
- io:format("EXPECT any TRAP within ~w~n", [To]),
+ io_format_expect("any TRAP within ~w", [To]),
receive_trap(To);
do_expect({timeout, To}) ->
- io:format("EXPECT nothing within ~w~n", [To]),
+ io_format_expect("nothing within ~w", [To]),
receive
X ->
{error, {unexpected, X}}
@@ -924,16 +961,16 @@ do_expect({timeout, To}) ->
do_expect({Err, To})
when (is_atom(Err) andalso
((is_integer(To) andalso To > 0) orelse (To =:= infinity))) ->
- io:format("EXPECT error ~w within ~w~n", [Err, To]),
+ io_format_expect("error ~w within ~w", [Err, To]),
do_expect({{error, Err}, To});
do_expect({error, Err}) when is_atom(Err) ->
Check = fun(_, R) -> R end,
- io:format("EXPECT error ~w~n", [Err]),
+ io_format_expect("error ~w", [Err]),
do_expect2(Check, any, Err, any, any, get_timeout());
do_expect({{error, Err}, To}) ->
Check = fun(_, R) -> R end,
- io:format("EXPECT error ~w within ~w~n", [Err, To]),
+ io_format_expect("error ~w within ~w", [Err, To]),
do_expect2(Check, any, Err, any, any, To);
%% exp_varbinds() -> [exp_varbind()]
@@ -943,25 +980,23 @@ do_expect({{error, Err}, To}) ->
%% ExpVBs -> exp_varbinds() | {VbsCondition, exp_varbinds()}
do_expect(ExpVBs) ->
Check = fun(_, R) -> R end,
- io:format("EXPECT 'get-response'"
- "~n with"
- "~n Varbinds: ~p~n", [ExpVBs]),
+ io_format_expect("'get-response'"
+ "~n with"
+ "~n Varbinds: ~p", [ExpVBs]),
do_expect2(Check, 'get-response', noError, 0, ExpVBs, get_timeout()).
do_expect(v2trap, ExpVBs) ->
Check = fun(_, R) -> R end,
- io:format("EXPECT 'snmpv2-trap'"
- "~n with"
- "~n Varbinds: ~p~n", [ExpVBs]),
+ io_format_expect("'snmpv2-trap' with"
+ "~n Varbinds: ~p", [ExpVBs]),
do_expect2(Check, 'snmpv2-trap', noError, 0, ExpVBs, get_timeout());
do_expect(report, ExpVBs) ->
Check = fun(_, R) -> R end,
- io:format("EXPECT 'report'"
- "~n with"
- "~n Varbinds: ~p~n", [ExpVBs]),
+ io_format_expect("'report' with"
+ "~n Varbinds: ~p", [ExpVBs]),
do_expect2(Check, 'report', noError, 0, ExpVBs, get_timeout());
@@ -970,9 +1005,8 @@ do_expect(inform, ExpVBs) ->
do_expect({inform, false}, ExpVBs) ->
Check = fun(_, R) -> R end,
- io:format("EXPECT 'inform-request' (false)"
- "~n with"
- "~n Varbinds: ~p~n", [ExpVBs]),
+ io_format_expect("'inform-request' (false) with"
+ "~n Varbinds: ~p", [ExpVBs]),
do_expect2(Check, 'inform-request', noError, 0, ExpVBs, get_timeout());
do_expect({inform, true}, ExpVBs) ->
@@ -986,9 +1020,8 @@ do_expect({inform, true}, ExpVBs) ->
(_, Err) ->
Err
end,
- io:format("EXPECT 'inform-request' (true)"
- "~n with"
- "~n Varbinds: ~p~n", [ExpVBs]),
+ io_format_expect("'inform-request' (true) with"
+ "~n Varbinds: ~p", [ExpVBs]),
do_expect2(Check, 'inform-request', noError, 0, ExpVBs, get_timeout());
do_expect({inform, {error, EStat, EIdx}}, ExpVBs)
@@ -1003,11 +1036,10 @@ do_expect({inform, {error, EStat, EIdx}}, ExpVBs)
(_, Err) ->
Err
end,
- io:format("EXPECT 'inform-request' (error)"
- "~n with"
- "~n Error Status: ~p"
- "~n Error Index: ~p"
- "~n Varbinds: ~p~n", [EStat, EIdx, ExpVBs]),
+ io_format_expect("'inform-request' (error) with"
+ "~n Error Status: ~p"
+ "~n Error Index: ~p"
+ "~n Varbinds: ~p", [EStat, EIdx, ExpVBs]),
do_expect2(Check, 'inform-request', noError, 0, ExpVBs, get_timeout()).
@@ -1018,26 +1050,23 @@ do_expect(Err, Idx, ExpVBs, To)
when is_atom(Err) andalso
(is_integer(Idx) orelse is_list(Idx) orelse (Idx == any)) ->
Check = fun(_, R) -> R end,
- io:format("EXPECT 'get-response'"
- "~n with"
- "~n Error: ~p"
- "~n Index: ~p"
- "~n Varbinds: ~p"
- "~n within ~w~n", [Err, Idx, ExpVBs, To]),
+ io_format_expect("'get-response' withing ~w ms with"
+ "~n Error: ~p"
+ "~n Index: ~p"
+ "~n Varbinds: ~p", [To, Err, Idx, ExpVBs]),
do_expect2(Check, 'get-response', Err, Idx, ExpVBs, To).
do_expect(Type, Enterp, Generic, Specific, ExpVBs) ->
- do_expect(Type, Enterp, Generic, Specific, ExpVBs, 3500).
+ do_expect(Type, Enterp, Generic, Specific, ExpVBs, get_timeout()).
do_expect(trap, Enterp, Generic, Specific, ExpVBs, To) ->
- io:format("EXPECT trap"
- "~n with"
- "~n Enterp: ~w"
- "~n Generic: ~w"
- "~n Specific: ~w"
- "~n Varbinds: ~w"
- "~n within ~w~n", [Enterp, Generic, Specific, ExpVBs, To]),
+ io_format_expect("trap within ~w ms with"
+ "~n Enterp: ~w"
+ "~n Generic: ~w"
+ "~n Specific: ~w"
+ "~n Varbinds: ~w",
+ [To, Enterp, Generic, Specific, ExpVBs]),
PureE = purify_oid(Enterp),
case receive_trap(To) of
#trappdu{enterprise = PureE,
@@ -1072,46 +1101,46 @@ do_expect2(Check, Type, Err, Idx, ExpVBs, To)
#pdu{type = Type,
error_status = Err,
error_index = Idx} when ExpVBs =:= any ->
- io:format("EXPECT received expected pdu (1)~n", []),
+ io_format_expect("received expected pdu (1)"),
ok;
#pdu{type = Type,
request_id = ReqId,
error_status = Err2,
error_index = Idx} when ExpVBs =:= any ->
- io:format("EXPECT received expected pdu with "
- "unexpected error status (2): "
- "~n Error Status: ~p~n", [Err2]),
+ io_format_expect("received expected pdu with "
+ "unexpected error status (2): "
+ "~n Error Status: ~p", [Err2]),
{error, {unexpected_error_status, Err, Err2, ReqId}};
#pdu{error_status = Err} when (Type =:= any) andalso
(Idx =:= any) andalso
(ExpVBs =:= any) ->
- io:format("EXPECT received expected pdu (3)~n", []),
+ io_format_expect("received expected pdu (3)"),
ok;
#pdu{request_id = ReqId,
error_status = Err2} when (Type =:= any) andalso
(Idx =:= any) andalso
(ExpVBs =:= any) ->
- io:format("EXPECT received expected pdu with "
- "unexpected error status (4): "
- "~n Error Status: ~p~n", [Err2]),
+ io_format_expect("received expected pdu with "
+ "unexpected error status (4): "
+ "~n Error Status: ~p", [Err2]),
{error, {unexpected_error_status, Err, Err2, ReqId}};
#pdu{type = Type,
error_status = Err} when (Idx =:= any) andalso
(ExpVBs =:= any) ->
- io:format("EXPECT received expected pdu (5)~n", []),
+ io_format_expect("received expected pdu (5)", []),
ok;
#pdu{type = Type,
request_id = ReqId,
error_status = Err2} when (Idx =:= any) andalso
(ExpVBs =:= any) ->
- io:format("EXPECT received expected pdu with "
- "unexpected error status (6): "
- "~n Error Status: ~p~n", [Err2]),
+ io_format_expect("received expected pdu with "
+ "unexpected error status (6): "
+ "~n Error Status: ~p", [Err2]),
{error, {unexpected_error_status, Err, Err2, ReqId}};
#pdu{type = Type,
@@ -1120,13 +1149,13 @@ do_expect2(Check, Type, Err, Idx, ExpVBs, To)
error_index = EI} when is_list(Idx) andalso (ExpVBs =:= any) ->
case lists:member(EI, Idx) of
true ->
- io:format("EXPECT received expected pdu with "
- "expected error index (7)~n", []),
+ io_format_expect("received expected pdu with "
+ "expected error index (7)"),
ok;
false ->
- io:format("EXPECT received expected pdu with "
- "unexpected error index (8): "
- "~n Error Index: ~p~n", [EI]),
+ io_format_expect("received expected pdu with "
+ "unexpected error index (8): "
+ "~n Error Index: ~p", [EI]),
{error, {unexpected_error_index, EI, Idx, ReqId}}
end;
@@ -1136,15 +1165,15 @@ do_expect2(Check, Type, Err, Idx, ExpVBs, To)
error_index = EI} when is_list(Idx) andalso (ExpVBs =:= any) ->
case lists:member(EI, Idx) of
true ->
- io:format("EXPECT received expected pdu with "
- "unexpected error status (9): "
- "~n Error Status: ~p~n", [Err2]),
+ io_format_expect("received expected pdu with "
+ "unexpected error status (9): "
+ "~n Error Status: ~p", [Err2]),
{error, {unexpected_error_status, Err, Err2, ReqId}};
false ->
- io:format("EXPECT received expected pdu with "
- "unexpected error (10): "
- "~n Error Status: ~p"
- "~n Error index: ~p~n", [Err2, EI]),
+ io_format_expect("received expected pdu with "
+ "unexpected error (10): "
+ "~n Error Status: ~p"
+ "~n Error index: ~p", [Err2, EI]),
{error, {unexpected_error, {Err, Idx}, {Err2, EI}, ReqId}}
end;
@@ -1152,12 +1181,12 @@ do_expect2(Check, Type, Err, Idx, ExpVBs, To)
request_id = ReqId,
error_status = Err2,
error_index = Idx2} when ExpVBs =:= any ->
- io:format("EXPECT received unexpected pdu with (11) "
- "~n Type: ~p"
- "~n ReqId: ~p"
- "~n Errot status: ~p"
- "~n Error index: ~p"
- "~n", [Type2, ReqId, Err2, Idx2]),
+ io_format_expect("received unexpected pdu with (11) "
+ "~n Type: ~p"
+ "~n ReqId: ~p"
+ "~n Errot status: ~p"
+ "~n Error index: ~p",
+ [Type2, ReqId, Err2, Idx2]),
{error,
{unexpected_pdu,
{Type, Err, Idx}, {Type2, Err2, Idx2}, ReqId}};
@@ -1166,26 +1195,26 @@ do_expect2(Check, Type, Err, Idx, ExpVBs, To)
error_status = Err,
error_index = Idx,
varbinds = VBs} = PDU ->
- io:format("EXPECT received pdu (12): "
- "~n [exp] Type: ~p"
- "~n [exp] Error Status: ~p"
- "~n [exp] Error Index: ~p"
- "~n VBs: ~p"
- "~nwhen"
- "~n ExpVBs: ~p"
- "~n", [Type, Err, Idx, VBs, ExpVBs]),
+ io_format_expect("received pdu (12): "
+ "~n [exp] Type: ~p"
+ "~n [exp] Error Status: ~p"
+ "~n [exp] Error Index: ~p"
+ "~n VBs: ~p"
+ "~nwhen"
+ "~n ExpVBs: ~p",
+ [Type, Err, Idx, VBs, ExpVBs]),
Check(PDU, check_vbs(purify_oids(ExpVBs), VBs));
#pdu{type = Type,
error_status = Err,
varbinds = VBs} = PDU when Idx =:= any ->
- io:format("EXPECT received pdu (13): "
- "~n [exp] Type: ~p"
- "~n [exp] Error Status: ~p"
- "~n VBs: ~p"
- "~nwhen"
- "~n ExpVBs: ~p"
- "~n", [Type, Err, VBs, ExpVBs]),
+ io_format_expect("received pdu (13): "
+ "~n [exp] Type: ~p"
+ "~n [exp] Error Status: ~p"
+ "~n VBs: ~p"
+ "~nwhen"
+ "~n ExpVBs: ~p",
+ [Type, Err, VBs, ExpVBs]),
Check(PDU, check_vbs(purify_oids(ExpVBs), VBs));
#pdu{type = Type,
@@ -1193,15 +1222,15 @@ do_expect2(Check, Type, Err, Idx, ExpVBs, To)
error_status = Err,
error_index = EI,
varbinds = VBs} = PDU when is_list(Idx) ->
- io:format("EXPECT received pdu (14): "
- "~n [exp] Type: ~p"
- "~n ReqId: ~p"
- "~n [exp] Error Status: ~p"
- "~n [exp] Error Index: ~p"
- "~n VBs: ~p"
- "~nwhen"
- "~n ExpVBs: ~p"
- "~n", [Type, ReqId, Err, EI, VBs, ExpVBs]),
+ io_format_expect("received pdu (14): "
+ "~n [exp] Type: ~p"
+ "~n ReqId: ~p"
+ "~n [exp] Error Status: ~p"
+ "~n [exp] Error Index: ~p"
+ "~n VBs: ~p"
+ "~nwhen"
+ "~n ExpVBs: ~p",
+ [Type, ReqId, Err, EI, VBs, ExpVBs]),
PureVBs = purify_oids(ExpVBs),
case lists:member(EI, Idx) of
true ->
@@ -1215,13 +1244,13 @@ do_expect2(Check, Type, Err, Idx, ExpVBs, To)
error_status = Err2,
error_index = Idx2,
varbinds = VBs2} ->
- io:format("EXPECT received unexpected pdu with (15) "
- "~n Type: ~p"
- "~n ReqId: ~p"
- "~n Errot status: ~p"
- "~n Error index: ~p"
- "~n Varbinds: ~p"
- "~n", [Type2, ReqId, Err2, Idx2, VBs2]),
+ io_format_expect("received unexpected pdu with (15) "
+ "~n Type: ~p"
+ "~n ReqId: ~p"
+ "~n Errot status: ~p"
+ "~n Error index: ~p"
+ "~n Varbinds: ~p",
+ [Type2, ReqId, Err2, Idx2, VBs2]),
{error,
{unexpected_pdu,
{Type, Err, Idx, purify_oids(ExpVBs)},
@@ -1229,9 +1258,8 @@ do_expect2(Check, Type, Err, Idx, ExpVBs, To)
ReqId}};
Error ->
- io:format("EXPECT received error (16): "
- "~n Error: ~p"
- "~n", [Error]),
+ io_format_expect("received error (16): "
+ "~n Error: ~p", [Error]),
Error
end.
diff --git a/lib/snmp/test/snmp_manager_test.erl b/lib/snmp/test/snmp_manager_test.erl
index bb9b05b89f..5b0ebf8647 100644
--- a/lib/snmp/test/snmp_manager_test.erl
+++ b/lib/snmp/test/snmp_manager_test.erl
@@ -204,10 +204,15 @@ init_per_testcase(Case, Config) when is_list(Config) ->
Result =
case lists:member(Case, DeprecatedApiCases) of
true ->
- %% ?SKIP(api_no_longer_supported);
{skip, api_no_longer_supported};
false ->
- init_per_testcase2(Case, Config)
+ try init_per_testcase2(Case, Config)
+ catch
+ C:{skip, _} = E:_ when ((C =:= throw) orelse (C =:= exit)) ->
+ E;
+ C:E:_ when ((C =:= throw) orelse (C =:= exit)) ->
+ {skip, {catched, C, E}}
+ end
end,
p(Case, "init_per_testcase end when"
"~n Nodes: ~p"
@@ -326,9 +331,25 @@ init_per_testcase3(Case, Config) ->
true ->
Config
end,
+ %% We don't need to try catch this (init_agent)
+ %% since we have a try catch "higher up"...
Conf2 = init_agent(Conf1),
- Conf3 = init_manager(AutoInform, Conf2),
- Conf4 = init_mgr_user(Conf3),
+ Conf3 = try init_manager(AutoInform, Conf2)
+ catch AC:AE:_ ->
+ %% Ouch we need to clean up:
+ %% The init_agent starts an agent node!
+ init_per_testcase_fail_agent_cleanup(Conf2),
+ throw({skip, {manager_init_failed, AC, AE}})
+ end,
+ Conf4 = try init_mgr_user(Conf3)
+ catch MC:ME:_ ->
+ %% Ouch we need to clean up:
+ %% The init_agent starts an agent node!
+ %% The init_magager starts an manager node!
+ init_per_testcase_fail_manager_cleanup(Conf3),
+ init_per_testcase_fail_agent_cleanup(Conf3),
+ throw({skip, {manager_user_init_failed, MC, ME}})
+ end,
case lists:member(Case, ApiCases02 ++ ApiCases03) of
true ->
init_mgr_user_data2(Conf4);
@@ -339,6 +360,12 @@ init_per_testcase3(Case, Config) ->
Config
end.
+init_per_testcase_fail_manager_cleanup(Conf) ->
+ (catch fin_manager(Conf)).
+
+init_per_testcase_fail_agent_cleanup(Conf) ->
+ (catch fin_agent(Conf)).
+
end_per_testcase(Case, Config) when is_list(Config) ->
p(Case, "end_per_testcase begin when"
"~n Nodes: ~p~n~n", [erlang:nodes()]),
@@ -993,18 +1020,40 @@ notify_started02(Config) when is_list(Config) ->
{config, [{verbosity, log}, {dir, ConfDir}, {db_dir, DbDir}]}],
p("start snmpm client process"),
- Pid1 = ns02_loop1_start(),
+ NumIterations = 5,
+ Pid1 = ns02_client_start(NumIterations),
+
+ p("start snmpm ctrl (starter) process"),
+ Pid2 = ns02_ctrl_start(Opts, NumIterations),
+
+ %% On a reasonably fast machine, one iteration takes approx 4 seconds.
+ %% We measure the first iteration, and then we wait for the remaining
+ %% ones (4 in this case).
+ ApproxStartTime =
+ case ns02_client_await_approx_runtime(Pid1) of
+ {ok, T} ->
+ T;
+ {error, Reason} ->
+ %% Attempt cleanup just in case
+ exit(Pid1, kill),
+ exit(Pid2, kill),
+ ?FAIL(Reason);
+ {skip, Reason} ->
+ %% Attempt cleanup just in case
+ exit(Pid1, kill),
+ exit(Pid2, kill),
+ ?SKIP(Reason)
+ end,
- p("start snmpm starter process"),
- Pid2 = ns02_loop2_start(Opts),
-
- p("await snmpm client process exit"),
+ p("await snmpm client process exit (max ~p+10000 msec)", [ApproxStartTime]),
receive
{'EXIT', Pid1, normal} ->
ok;
{'EXIT', Pid1, Reason1} ->
- ?FAIL(Reason1)
- after 25000 ->
+ ?FAIL({client, Reason1})
+ after ApproxStartTime + 10000 ->
+ exit(Pid1, kill),
+ exit(Pid2, kill),
?FAIL(timeout)
end,
@@ -1013,8 +1062,9 @@ notify_started02(Config) when is_list(Config) ->
{'EXIT', Pid2, normal} ->
ok;
{'EXIT', Pid2, Reason2} ->
- ?FAIL(Reason2)
+ ?FAIL({ctrl, Reason2})
after 5000 ->
+ exit(Pid2, kill),
?FAIL(timeout)
end,
@@ -1022,26 +1072,63 @@ notify_started02(Config) when is_list(Config) ->
ok.
-ns02_loop1_start() ->
- spawn_link(fun() -> ns02_loop1() end).
+ns02_client_start(N) ->
+ Self = self(),
+ spawn_link(fun() -> ns02_client(Self, N) end).
+
+ns02_client_await_approx_runtime(Pid) ->
+ receive
+ {?MODULE, client_time, Time} ->
+ {ok, Time};
+ {'EXIT', Pid, Reason} ->
+ p("client (~p) failed: "
+ "~n ~p", [Pid, Reason]),
+ {error, Reason}
+
+ after 15000 ->
+ %% Either something is *really* wrong or this machine
+ %% is dog slow. Either way, this is a skip-reason...
+ {skip, approx_runtime_timeout}
+ end.
+
-ns02_loop1() ->
- put(tname,ns02_loop1),
+ns02_client(Parent, N) when is_pid(Parent) ->
+ put(tname, ns02_client),
p("starting"),
- ns02_loop1(dummy, snmpm:notify_started(?NS_TIMEOUT), 5).
+ ns02_client_loop(Parent,
+ dummy, snmpm:notify_started(?NS_TIMEOUT),
+ snmp_misc:now(ms), undefined,
+ N).
-ns02_loop1(_Ref, _Pid, 0) ->
- p("done"),
+ns02_client_loop(_Parent, _Ref, _Pid, _Begin, _End, 0) ->
+ %% p("loop -> done"),
exit(normal);
-ns02_loop1(Ref, Pid, N) ->
- p("entry when"
- "~n Ref: ~p"
- "~n Pid: ~p"
- "~n N: ~p", [Ref, Pid, N]),
+ns02_client_loop(Parent, Ref, Pid, Begin, End, N)
+ when is_pid(Parent) andalso is_integer(Begin) andalso is_integer(End) ->
+ %% p("loop -> [~w] inform parent: ~w, ~w => ~w", [N, Begin, End, End-Begin]),
+ Parent ! {?MODULE, client_time, N*(End-Begin)},
+ ns02_client_loop(undefined, Ref, Pid, snmp_misc:now(ms), undefined, N);
+ns02_client_loop(Parent, Ref, Pid, Begin, End, N)
+ when is_integer(Begin) andalso is_integer(End) ->
+ %% p("loop -> [~w] entry when"
+ %% "~n Ref: ~p"
+ %% "~n Pid: ~p"
+ %% "~n Begin: ~p"
+ %% "~n End: ~p", [N, Ref, Pid, Begin, End]),
+ ns02_client_loop(Parent, Ref, Pid, snmp_misc:now(ms), undefined, N);
+ns02_client_loop(Parent, Ref, Pid, Begin, End, N) ->
+ %% p("loop(await message) -> [~w] entry when"
+ %% "~n Ref: ~p"
+ %% "~n Pid: ~p"
+ %% "~n Begin: ~p"
+ %% "~n End: ~p", [N, Ref, Pid, Begin, End]),
receive
{snmpm_started, Pid} ->
p("received expected started message (~w)", [N]),
- ns02_loop1(snmpm:monitor(), dummy, N);
+ ns02_client_loop(Parent,
+ snmpm:monitor(), dummy,
+ Begin, End,
+ N);
{snmpm_start_timeout, Pid} ->
p("unexpected timout"),
?FAIL({unexpected_start_timeout, Pid});
@@ -1049,24 +1136,24 @@ ns02_loop1(Ref, Pid, N) ->
p("received expected DOWN message (~w) with"
"~n Obj: ~p"
"~n Reason: ~p", [N, Obj, Reason]),
- ns02_loop1(dummy, snmpm:notify_started(?NS_TIMEOUT), N-1)
- after 10000 ->
- ?FAIL(timeout)
+ ns02_client_loop(Parent,
+ dummy, snmpm:notify_started(?NS_TIMEOUT),
+ Begin, snmp_misc:now(ms),
+ N-1)
end.
-
-ns02_loop2_start(Opts) ->
- spawn_link(fun() -> ns02_loop2(Opts) end).
+ns02_ctrl_start(Opts, N) ->
+ spawn_link(fun() -> ns02_ctrl(Opts, N) end).
-ns02_loop2(Opts) ->
- put(tname,ns02_loop2),
+ns02_ctrl(Opts, N) ->
+ put(tname, ns02_ctrl),
p("starting"),
- ns02_loop2(Opts, 5).
+ ns02_ctrl_loop(Opts, N).
-ns02_loop2(_Opts, 0) ->
+ns02_ctrl_loop(_Opts, 0) ->
p("done"),
exit(normal);
-ns02_loop2(Opts, N) ->
+ns02_ctrl_loop(Opts, N) ->
p("entry when N: ~p", [N]),
?SLEEP(2000),
p("start manager"),
@@ -1074,7 +1161,7 @@ ns02_loop2(Opts, N) ->
?SLEEP(2000),
p("stop manager"),
snmpm:stop(),
- ns02_loop2(Opts, N-1).
+ ns02_ctrl_loop(Opts, N-1).
%%======================================================================
@@ -5416,15 +5503,14 @@ init_manager(AutoInform, Config) ->
start_manager(Node, Vsns, Conf)
end
catch
- T:E ->
- StackTrace = ?STACK(),
+ C:E:S ->
p("Failure during manager start: "
- "~n Error Type: ~p"
- "~n Error: ~p"
- "~n StackTrace: ~p", [T, E, StackTrace]),
+ "~n Error Class: ~p"
+ "~n Error: ~p"
+ "~n StackTrace: ~p", [C, E, S]),
%% And now, *try* to cleanup
(catch stop_node(Node)),
- ?FAIL({failed_starting_manager, T, E, StackTrace})
+ ?FAIL({failed_starting_manager, C, E, S})
end.
fin_manager(Config) ->
@@ -5432,7 +5518,7 @@ fin_manager(Config) ->
StopMgrRes = stop_manager(Node),
StopCryptoRes = fin_crypto(Node),
StopNode = stop_node(Node),
- p("fin_agent -> stop apps and (mgr node ~p) node results: "
+ p("fin_manager -> stop apps and (mgr node ~p) node results: "
"~n SNMP Mgr: ~p"
"~n Crypto: ~p"
"~n Node: ~p",
@@ -5498,15 +5584,14 @@ init_agent(Config) ->
start_agent(Node, Vsns, Conf)
end
catch
- T:E ->
- StackTrace = ?STACK(),
+ C:E:S ->
p("Failure during agent start: "
- "~n Error Type: ~p"
- "~n Error: ~p"
- "~n StackTrace: ~p", [T, E, StackTrace]),
+ "~n Error Class: ~p"
+ "~n Error: ~p"
+ "~n StackTrace: ~p", [C, E, S]),
%% And now, *try* to cleanup
(catch stop_node(Node)),
- ?FAIL({failed_starting_agent, T, E, StackTrace})
+ ?FAIL({failed_starting_agent, C, E, S})
end.
diff --git a/lib/snmp/test/snmp_test_lib.erl b/lib/snmp/test/snmp_test_lib.erl
index 290f1bc31a..a483690653 100644
--- a/lib/snmp/test/snmp_test_lib.erl
+++ b/lib/snmp/test/snmp_test_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2015. All Rights Reserved.
+%% 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.
@@ -41,7 +41,7 @@
-export([watchdog/3, watchdog_start/1, watchdog_start/2, watchdog_stop/1]).
-export([del_dir/1]).
-export([cover/1]).
--export([p/2, print/5, formated_timestamp/0]).
+-export([p/2, print1/2, print2/2, print/5, formated_timestamp/0]).
%% ----------------------------------------------------------------------
@@ -58,12 +58,67 @@ from(H, [H | T]) -> T;
from(H, [_ | T]) -> from(H, T);
from(_H, []) -> [].
+%% localhost() ->
+%% {ok, Ip} = snmp_misc:ip(net_adm:localhost()),
+%% Ip.
+%% localhost(Family) ->
+%% {ok, Ip} = snmp_misc:ip(net_adm:localhost(), Family),
+%% Ip.
+
localhost() ->
- {ok, Ip} = snmp_misc:ip(net_adm:localhost()),
- Ip.
+ localhost(inet).
+
localhost(Family) ->
- {ok, Ip} = snmp_misc:ip(net_adm:localhost(), Family),
- Ip.
+ case inet:getaddr(net_adm:localhost(), Family) of
+ {ok, {127, _, _, _}} when (Family =:= inet) ->
+ %% Ouch, we need to use something else
+ case inet:getifaddrs() of
+ {ok, IfList} ->
+ which_addr(Family, IfList);
+ {error, Reason1} ->
+ fail({getifaddrs, Reason1}, ?MODULE, ?LINE)
+ end;
+ {ok, {0, _, _, _, _, _, _, _}} when (Family =:= inet6) ->
+ %% Ouch, we need to use something else
+ case inet:getifaddrs() of
+ {ok, IfList} ->
+ which_addr(Family, IfList);
+ {error, Reason1} ->
+ fail({getifaddrs, Reason1}, ?MODULE, ?LINE)
+ end;
+ {ok, Addr} ->
+ Addr;
+ {error, Reason2} ->
+ fail({getaddr, Reason2}, ?MODULE, ?LINE)
+ end.
+
+which_addr(_Family, []) ->
+ fail(no_valid_addr, ?MODULE, ?LINE);
+which_addr(Family, [{"lo", _} | IfList]) ->
+ which_addr(Family, IfList);
+which_addr(Family, [{"docker" ++ _, _} | IfList]) ->
+ which_addr(Family, IfList);
+which_addr(Family, [{"br-" ++ _, _} | IfList]) ->
+ which_addr(Family, IfList);
+which_addr(Family, [{_Name, IfOpts} | IfList]) ->
+ case which_addr2(Family, IfOpts) of
+ {ok, Addr} ->
+ Addr;
+ {error, _} ->
+ which_addr(Family, IfList)
+ end.
+
+which_addr2(_Family, []) ->
+ {error, not_found};
+which_addr2(Family, [{addr, Addr}|_])
+ when (Family =:= inet) andalso (size(Addr) =:= 4) ->
+ {ok, Addr};
+which_addr2(Family, [{addr, Addr}|_])
+ when (Family =:= inet6) andalso (size(Addr) =:= 8) ->
+ {ok, Addr};
+which_addr2(Family, [_|IfOpts]) ->
+ which_addr2(Family, IfOpts).
+
sz(L) when is_list(L) ->
length(L);
@@ -605,19 +660,30 @@ p(Mod, Case) when is_atom(Mod) andalso is_atom(Case) ->
p(F, A) when is_list(F) andalso is_list(A) ->
io:format(user, F ++ "~n", A).
+%% This is just a bog standard printout, with a (formatted) timestamp
+%% prefix and a newline after.
+%% print1 - prints to both standard_io and user.
+%% print2 - prints to just standard_io.
+
+print_format(F, A) ->
+ FTS = snmp_test_lib:formated_timestamp(),
+ io_lib:format("[~s] " ++ F ++ "~n", [FTS | A]).
+
+print1(F, A) ->
+ S = print_format(F, A),
+ io:format("~s", [S]),
+ io:format(user, "~s", [S]).
+
+print2(F, A) ->
+ S = print_format(F, A),
+ io:format("~s", [S]).
+
+
print(Prefix, Module, Line, Format, Args) ->
io:format("*** [~s] ~s ~p ~p ~p:~p *** " ++ Format ++ "~n",
[formated_timestamp(),
Prefix, node(), self(), Module, Line|Args]).
formated_timestamp() ->
- format_timestamp(os:timestamp()).
-
-format_timestamp({_N1, _N2, N3} = Now) ->
- {Date, Time} = calendar:now_to_datetime(Now),
- {YYYY,MM,DD} = Date,
- {Hour,Min,Sec} = Time,
- FormatDate =
- io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w ~w",
- [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]),
- lists:flatten(FormatDate).
+ snmp_misc:formated_timestamp().
+
diff --git a/lib/snmp/test/snmp_test_lib.hrl b/lib/snmp/test/snmp_test_lib.hrl
index 7acebee1f1..335f3fff3c 100644
--- a/lib/snmp/test/snmp_test_lib.hrl
+++ b/lib/snmp/test/snmp_test_lib.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2015. All Rights Reserved.
+%% 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.
@@ -127,24 +127,31 @@
-endif.
-ifdef(snmp_debug).
--define(DBG(F,A),?PRINT("DBG",F,A)).
+-define(DBG(F,A), ?PRINT("DBG", F, A)).
-else.
--define(DBG(F,A),ok).
+-define(DBG(F,A), ok).
-endif.
-ifdef(snmp_log).
--define(LOG(F,A),?PRINT("LOG",F,A)).
+-define(LOG(F,A), ?PRINT("LOG", F, A)).
-else.
--define(LOG(F,A),ok).
+-define(LOG(F,A), ok).
-endif.
-ifdef(snmp_error).
--define(ERR(F,A),?PRINT("ERR",F,A)).
+-define(ERR(F,A), ?PRINT("ERR", F, A)).
-else.
--define(ERR(F,A),ok).
+-define(ERR(F,A), ok).
-endif.
--define(INF(F,A),?PRINT("INF",F,A)).
+-define(INF(F,A), ?PRINT("INF", F, A)).
-define(PRINT(P,F,A),
- snmp_test_lib:print(P,?MODULE,?LINE,F,A)).
+ snmp_test_lib:print(P, ?MODULE, ?LINE, F, A)).
+
+-define(PRINT1(F, A), snmp_test_lib:print1(F, A)).
+-define(EPRINT1(F, A), ?PRINT1("<ERROR> " ++ F, A)).
+
+-define(PRINT2(F, A), snmp_test_lib:print2(F, A)).
+-define(EPRINT2(F, A), ?PRINT2("<ERROR> " ++ F, A)).
+
diff --git a/lib/snmp/test/snmp_test_mgr.erl b/lib/snmp/test/snmp_test_mgr.erl
index 36637d5cf4..73a4d56084 100644
--- a/lib/snmp/test/snmp_test_mgr.erl
+++ b/lib/snmp/test/snmp_test_mgr.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2015. All Rights Reserved.
+%% Copyright Ericsson AB 1996-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.
@@ -20,8 +20,10 @@
-module(snmp_test_mgr).
+
%%----------------------------------------------------------------------
-%% This module implements a simple SNMP manager for Erlang.
+%% This module implements a simple SNMP manager for Erlang. Its used
+%% during by the agent test suite.
%%----------------------------------------------------------------------
%% c(snmp_test_mgr).
@@ -49,16 +51,17 @@
-include_lib("snmp/include/snmp_types.hrl").
-include_lib("snmp/include/STANDARD-MIB.hrl").
-
--record(state,{dbg = true,
- quiet,
- parent,
- timeout = 3500,
- print_traps = true,
- mini_mib,
- packet_server,
- last_sent_pdu,
- last_received_pdu}).
+-include("snmp_test_lib.hrl").
+
+-record(state, {dbg = true,
+ quiet,
+ parent,
+ timeout = 3500,
+ print_traps = true,
+ mini_mib,
+ packet_server,
+ last_sent_pdu,
+ last_received_pdu}).
-define(SERVER, ?MODULE).
-define(PACK_SERV, snmp_test_mgr_misc).
@@ -197,27 +200,28 @@ init({Options, CallerPid}) ->
put(debug, get_value(debug, Options, false)),
d("init -> (~p) extract options",[self()]),
PacksDbg = get_value(packet_server_debug, Options, false),
- io:format("[~w] ~p -> PacksDbg: ~p~n", [?MODULE, self(), PacksDbg]),
+ print("[~w] ~p -> PacksDbg: ~p~n", [?MODULE, self(), PacksDbg]),
RecBufSz = get_value(recbuf, Options, 1024),
- io:format("[~w] ~p -> RecBufSz: ~p~n", [?MODULE, self(), RecBufSz]),
+ print("[~w] ~p -> RecBufSz: ~p~n", [?MODULE, self(), RecBufSz]),
Mibs = get_value(mibs, Options, []),
- io:format("[~w] ~p -> Mibs: ~p~n", [?MODULE, self(), Mibs]),
+ print("[~w] ~p -> Mibs: ~p~n", [?MODULE, self(), Mibs]),
Udp = get_value(agent_udp, Options, 4000),
- io:format("[~w] ~p -> Udp: ~p~n", [?MODULE, self(), Udp]),
+ print("[~w] ~p -> Udp: ~p~n", [?MODULE, self(), Udp]),
User = get_value(user, Options, "initial"),
- io:format("[~w] ~p -> User: ~p~n", [?MODULE, self(), User]),
+ print("[~w] ~p -> User: ~p~n", [?MODULE, self(), User]),
EngineId = get_value(engine_id, Options, "agentEngine"),
- io:format("[~w] ~p -> EngineId: ~p~n", [?MODULE, self(), EngineId]),
+ print("[~w] ~p -> EngineId: ~p~n", [?MODULE, self(), EngineId]),
CtxEngineId = get_value(context_engine_id, Options, EngineId),
- io:format("[~w] ~p -> CtxEngineId: ~p~n", [?MODULE, self(), CtxEngineId]),
+ print("[~w] ~p -> CtxEngineId: ~p~n", [?MODULE, self(), CtxEngineId]),
TrapUdp = get_value(trap_udp, Options, 5000),
- io:format("[~w] ~p -> TrapUdp: ~p~n", [?MODULE, self(), TrapUdp]),
+ print("[~w] ~p -> TrapUdp: ~p~n", [?MODULE, self(), TrapUdp]),
Dir = get_value(dir, Options, "."),
- io:format("[~w] ~p -> Dir: ~p~n", [?MODULE, self(), Dir]),
+ print("[~w] ~p -> Dir: ~p~n", [?MODULE, self(), Dir]),
SecLevel = get_value(sec_level, Options, noAuthNoPriv),
- io:format("[~w] ~p -> SecLevel: ~p~n", [?MODULE, self(), SecLevel]),
+ print("[~w] ~p -> SecLevel: ~p~n", [?MODULE, self(), SecLevel]),
MiniMIB = snmp_mini_mib:create(Mibs),
- io:format("[~w] ~p -> MiniMIB: ~p~n", [?MODULE, self(), MiniMIB]),
+ d("[~w] ~p -> MiniMIB: "
+ "~n ~p", [?MODULE, self(), MiniMIB]),
Version = case lists:member(v2, Options) of
true -> 'version-2';
false ->
@@ -226,19 +230,19 @@ init({Options, CallerPid}) ->
false -> 'version-1'
end
end,
- io:format("[~w] ~p -> Version: ~p~n", [?MODULE, self(), Version]),
+ print("[~w] ~p -> Version: ~p~n", [?MODULE, self(), Version]),
Com = case Version of
'version-3' ->
get_value(context, Options, "");
_ ->
get_value(community, Options, "public")
end,
- io:format("[~w] ~p -> Com: ~p~n", [?MODULE, self(), Com]),
+ print("[~w] ~p -> Com: ~p~n", [?MODULE, self(), Com]),
VsnHdrD =
{Com, User, EngineId, CtxEngineId, mk_seclevel(SecLevel)},
- io:format("[~w] ~p -> VsnHdrD: ~p~n", [?MODULE, self(), VsnHdrD]),
+ print("[~w] ~p -> VsnHdrD: ~p~n", [?MODULE, self(), VsnHdrD]),
IpFamily = get_value(ipfamily, Options, inet),
- io:format("[~w] ~p -> IpFamily: ~p~n", [?MODULE, self(), IpFamily]),
+ print("[~w] ~p -> IpFamily: ~p~n", [?MODULE, self(), IpFamily]),
AgIp = case snmp_misc:assq(agent, Options) of
{value, Tuple4} when is_tuple(Tuple4) andalso
(size(Tuple4) =:= 4) ->
@@ -247,9 +251,9 @@ init({Options, CallerPid}) ->
{ok, Ip} = snmp_misc:ip(Host, IpFamily),
Ip
end,
- io:format("[~w] ~p -> AgIp: ~p~n", [?MODULE, self(), AgIp]),
+ print("[~w] ~p -> AgIp: ~p~n", [?MODULE, self(), AgIp]),
Quiet = lists:member(quiet, Options),
- io:format("[~w] ~p -> Quiet: ~p~n", [?MODULE, self(), Quiet]),
+ print("[~w] ~p -> Quiet: ~p~n", [?MODULE, self(), Quiet]),
PackServ =
start_packet_server(
Quiet, Options, CallerPid, AgIp, Udp, TrapUdp,
@@ -443,7 +447,8 @@ handle_cast({bulk, Args}, State) ->
{noreply, execute_request(bulk, Args, State)};
handle_cast({response, RespPdu}, State) ->
- d("handle_cast -> response request with ~p", [RespPdu]),
+ d("handle_cast -> response request with "
+ "~n ~p", [RespPdu]),
?PACK_SERV:send_pdu(RespPdu, State#state.packet_server),
{noreply, State};
@@ -1126,14 +1131,15 @@ sizeOf(L) when is_list(L) ->
sizeOf(B) when is_binary(B) ->
size(B).
-d(F,A) -> d(get(debug),F,A).
+d(F, A) -> d(get(debug), F, A).
-d(true,F,A) ->
- io:format("*** [~s] MGR_DBG *** " ++ F ++ "~n",
- [formated_timestamp()|A]);
+d(true, F, A) ->
+ print(F, A);
d(_,_F,_A) ->
ok.
+print(F, A) ->
+ ?PRINT2("MGR " ++ F, A).
formated_timestamp() ->
snmp_test_lib:formated_timestamp().
diff --git a/lib/snmp/test/snmp_test_mgr_misc.erl b/lib/snmp/test/snmp_test_mgr_misc.erl
index 274fb5be26..315e3ebd9e 100644
--- a/lib/snmp/test/snmp_test_mgr_misc.erl
+++ b/lib/snmp/test/snmp_test_mgr_misc.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-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.
@@ -38,11 +38,14 @@
-define(SNMP_USE_V3, true).
-include_lib("snmp/include/snmp_types.hrl").
+-include_lib("snmp/src/misc/snmp_verbosity.hrl").
+-include("snmp_test_lib.hrl").
%%----------------------------------------------------------------------
%% The InHandler process will receive messages on the form {snmp_pdu, Pdu}.
%%----------------------------------------------------------------------
+
start_link_packet(
InHandler, AgentIp, UdpPort, TrapUdp, VsnHdr, Version, Dir, BufSz) ->
start_link_packet(
@@ -101,11 +104,11 @@ init_packet(
DbgOptions, IpFamily) ->
put(sname, mgr_misc),
init_debug(DbgOptions),
- {ok, UdpId} =
- gen_udp:open(TrapUdp, [{recbuf,BufSz}, {reuseaddr, true}, IpFamily]),
+ UdpOpts = [{recbuf,BufSz}, {reuseaddr, true}, IpFamily],
+ {ok, UdpId} = gen_udp:open(TrapUdp, UdpOpts),
put(msg_id, 1),
- proc_lib:init_ack(Parent, self()),
init_usm(Version, Dir),
+ proc_lib:init_ack(Parent, self()),
packet_loop(SnmpMgr, UdpId, AgentIp, UdpPort, VsnHdr, Version, []).
init_debug(Dbg) when is_atom(Dbg) ->
@@ -200,86 +203,24 @@ packet_loop(SnmpMgr, UdpId, AgentIp, UdpPort, VsnHdr, Version, MsgData) ->
handle_udp_packet(_V, undefined,
UdpId, Ip, UdpPort,
Bytes, SnmpMgr, AgentIp) ->
- M = (catch snmp_pdus:dec_message_only(Bytes)),
- MsgData3 =
- case M of
- Message when Message#message.version =:= 'version-3' ->
- d("handle_udp_packet -> version 3"),
- case catch handle_v3_msg(Bytes, Message) of
- {ok, NewData, MsgData2} ->
- Msg = Message#message{data = NewData},
- case SnmpMgr of
- {pdu, Pid} ->
- Pdu = get_pdu(Msg),
- d("packet_loop -> "
- "send pdu to manager (~w): ~p", [Pid, Pdu]),
- Pid ! {snmp_pdu, Pdu};
- {msg, Pid} ->
- d("packet_loop -> "
- "send msg to manager (~w): ~p", [Pid, Msg]),
- Pid ! {snmp_msg, Msg, Ip, UdpPort}
- end,
- MsgData2;
- {error, Reason, B} ->
- udp_send(UdpId, AgentIp, UdpPort, B),
- error("Decoding error. Auto-sending Report.\n"
- "Reason: ~w "
- "(UDPport: ~w, Ip: ~w)",
- [Reason, UdpPort, Ip]),
- [];
- {error, Reason} ->
- error("Decoding error. "
- "Bytes: ~w ~n Reason: ~w "
- "(UDPport: ~w, Ip: ~w)",
- [Bytes, Reason, UdpPort, Ip]),
- []
- end;
- Message when is_record(Message, message) ->
- %% v1 or v2c
- d("handle_udp_packet -> version v1 or v2c"),
- case catch snmp_pdus:dec_pdu(Message#message.data) of
- Pdu when is_record(Pdu, pdu) ->
- case SnmpMgr of
- {pdu, Pid} ->
- d("handle_udp_packet -> "
- "send pdu to manager (~w): ~p",
- [Pid, Pdu]),
- Pid ! {snmp_pdu, Pdu};
- {msg, Pid} ->
- d("handle_udp_packet -> "
- "send pdu-msg to manager (~w): ~p",
- [Pid, Pdu]),
- Msg = Message#message{data = Pdu},
- Pid ! {snmp_msg, Msg, Ip, UdpPort}
- end;
- Pdu when is_record(Pdu, trappdu) ->
- case SnmpMgr of
- {pdu, Pid} ->
- d("handle_udp_packet -> "
- "send trap to manager (~w): ~p",
- [Pid, Pdu]),
- Pid ! {snmp_pdu, Pdu};
- {msg, Pid} ->
- d("handle_udp_packet -> "
- "send trap-msg to manager (~w): ~p",
- [Pid, Pdu]),
- Msg = Message#message{data = Pdu},
- Pid ! {snmp_msg, Msg, Ip, UdpPort}
- end;
- Reason ->
- error("Decoding error. "
- "Bytes: ~w ~n Reason: ~w "
- "(UDPport: ~w, Ip: ~w)",
- [Bytes, Reason, UdpPort, Ip])
- end,
- [];
- Reason ->
- error("Decoding error. Bytes: ~w ~n Reason: ~w "
- "(UDPport: ~w, Ip: ~w)",
- [Bytes, Reason, UdpPort, Ip]),
- []
- end,
- MsgData3;
+ try snmp_pdus:dec_message_only(Bytes) of
+ Message when Message#message.version =:= 'version-3' ->
+ d("handle_udp_packet -> version 3"),
+ handle_v3_message(SnmpMgr, UdpId, Ip, UdpPort, AgentIp,
+ Bytes, Message);
+
+ Message when is_record(Message, message) ->
+ d("handle_udp_packet -> version 1 or 2"),
+ handle_v1_or_v2_message(SnmpMgr, UdpId, Ip, UdpPort, AgentIp,
+ Bytes, Message)
+
+ catch
+ Class:Error:_ ->
+ error("Decoding error (~w). Bytes: ~w ~n Error: ~w "
+ "(UDPport: ~w, Ip: ~w)",
+ [Class, Bytes, Error, UdpPort, Ip]),
+ []
+ end;
handle_udp_packet(V, {DiscoReqMsg, From}, _UdpId, _Ip, _UdpPort,
Bytes, _, _AgentIp) ->
DiscoRspMsg = (catch snmp_pdus:dec_message(Bytes)),
@@ -297,6 +238,88 @@ handle_udp_packet(V, {DiscoReqMsg, From}, _UdpId, _Ip, _UdpPort,
[]
end.
+handle_v3_message(Mgr, UdpId, Ip, UdpPort, AgentIp,
+ Bytes, Message) ->
+ try handle_v3_msg(Bytes, Message) of
+ {ok, NewData, MsgData} ->
+ Msg = Message#message{data = NewData},
+ case Mgr of
+ {pdu, Pid} ->
+ Pdu = get_pdu(Msg),
+ d("handle_v3_message -> send pdu to manager (~p): "
+ "~n ~p", [Pid, Pdu]),
+ Pid ! {snmp_pdu, Pdu};
+ {msg, Pid} ->
+ d("handle_v3_message -> send msg to manager (~p): "
+ "~n ~p", [Pid, Msg]),
+ Pid ! {snmp_msg, Msg, Ip, UdpPort}
+ end,
+ MsgData
+
+ catch
+ throw:{error, Reason, B}:_ ->
+ udp_send(UdpId, AgentIp, UdpPort, B),
+ error("Decoding (v3) error. Auto-sending Report.\n"
+ "~n Reason: ~w "
+ "(UDPport: ~w, Ip: ~w)",
+ [Reason, UdpPort, Ip]),
+ [];
+
+ throw:{error, Reason}:_ ->
+ error("Decoding (v3) error. "
+ "~n Bytes: ~w"
+ "~n Reason: ~w "
+ "(UDPport: ~w, Ip: ~w)",
+ [Bytes, Reason, UdpPort, Ip]),
+ [];
+
+ Class:Error:_ ->
+ error("Decoding (v3) error (~w). "
+ "~n Bytes: ~w"
+ "~n Error: ~w "
+ "(UDPport: ~w, Ip: ~w)",
+ [Class, Bytes, Error, UdpPort, Ip]),
+ []
+
+ end.
+
+handle_v1_or_v2_message(Mgr, _UdpId, Ip, UdpPort, _AgentIp,
+ Bytes, Message) ->
+ try snmp_pdus:dec_pdu(Message#message.data) of
+ Pdu when is_record(Pdu, pdu) ->
+ case Mgr of
+ {pdu, Pid} ->
+ d("handle_v1_or_v2_message -> send pdu to manager (~p): "
+ "~n ~p", [Pid, Pdu]),
+ Pid ! {snmp_pdu, Pdu};
+ {msg, Pid} ->
+ d("handle_v1_or_v2_message -> send msg to manager (~p): "
+ "~n ~p", [Pid, Pdu]),
+ Msg = Message#message{data = Pdu},
+ Pid ! {snmp_msg, Msg, Ip, UdpPort}
+ end;
+ Pdu when is_record(Pdu, trappdu) ->
+ case Mgr of
+ {pdu, Pid} ->
+ d("handle_v1_or_v2_message -> send trap-pdu to manager (~p): "
+ "~n ~p", [Pid, Pdu]),
+ Pid ! {snmp_pdu, Pdu};
+ {msg, Pid} ->
+ d("handle_v1_or_v2_message -> send trap-msg to manager (~p): "
+ "~n ~p", [Pid, Pdu]),
+ Msg = Message#message{data = Pdu},
+ Pid ! {snmp_msg, Msg, Ip, UdpPort}
+ end
+
+ catch
+ Class:Error:_ ->
+ error("Decoding (v1 or v2) error (~w): "
+ "~n Bytes: ~w"
+ "~n Error: ~w "
+ "(UDPport: ~w, Ip: ~w)",
+ [Class, Bytes, Error, UdpPort, Ip])
+ end.
+
%% This function assumes that the agent and the manager (thats us)
%% has the same version.
@@ -578,18 +601,100 @@ set_pdu(Msg, RePdu) ->
init_usm('version-3', Dir) ->
+ ?vlog("init_usm -> create (and init) fake \"agent\" table", []),
ets:new(snmp_agent_table, [set, public, named_table]),
ets:insert(snmp_agent_table, {agent_mib_storage, persistent}),
- snmpa_local_db:start_link(normal, Dir, [{verbosity,trace}]),
+ %% The local-db process may *still* be running (from a previous
+ %% test case), on the way down, but not yet dead.
+ %% Either way, before we start it, make sure its dead and *gone*!
+ %% How do we do that without getting hung up? Calling the stop
+ %% function, will not do since it uses Timeout=infinity.
+ ?vlog("init_usm -> ensure (old) fake local-db is dead", []),
+ ensure_local_db_dead(),
+ ?vlog("init_usm -> try start fake local-db", []),
+ case snmpa_local_db:start_link(normal, Dir,
+ [{sname, "MGR-LOCAL-DB"},
+ {verbosity, trace}]) of
+ {ok, Pid} ->
+ ?vlog("started: ~p"
+ "~n ~p", [Pid, process_info(Pid)]);
+ {error, {already_started, Pid}} ->
+ LDBInfo = process_info(Pid),
+ ?vlog("already started: ~p"
+ "~n ~p", [Pid, LDBInfo]),
+ ?FAIL({still_running, snmpa_local_db, LDBInfo});
+ {error, Reason} ->
+ ?FAIL({failed_starting, snmpa_local_db, Reason})
+ end,
NameDb = snmpa_agent:db(snmpEngineID),
+ ?vlog("init_usm -> try set manager engine-id", []),
R = snmp_generic:variable_set(NameDb, "mgrEngine"),
- io:format("~w:init_usm -> engine-id set result: ~p~n", [?MODULE,R]),
+ snmp_verbosity:print(info, info, "init_usm -> engine-id set result: ~p", [R]),
+ ?vlog("init_usm -> try set engine boots (framework-mib)", []),
snmp_framework_mib:set_engine_boots(1),
+ ?vlog("init_usm -> try set engine time (framework-mib)", []),
snmp_framework_mib:set_engine_time(1),
- snmp_user_based_sm_mib:reconfigure(Dir);
+ ?vlog("init_usm -> try usm (mib) reconfigure", []),
+ snmp_user_based_sm_mib:reconfigure(Dir),
+ ?vlog("init_usm -> done", []),
+ ok;
init_usm(_Vsn, _Dir) ->
ok.
+ensure_local_db_dead() ->
+ ensure_dead(whereis(snmpa_local_db), 2000).
+
+ensure_dead(Pid, Timeout) when is_pid(Pid) ->
+ MRef = erlang:monitor(process, Pid),
+ try
+ begin
+ ensure_dead_wait(Pid, MRef, Timeout),
+ ensure_dead_stop(Pid, MRef, Timeout),
+ ensure_dead_kill(Pid, MRef, Timeout),
+ exit(failed_stop_local_db)
+ end
+ catch
+ throw:ok ->
+ ok
+ end;
+ensure_dead(_, _) ->
+ ?vlog("ensure_dead -> already dead", []),
+ ok.
+
+ensure_dead_wait(Pid, MRef, Timeout) ->
+ receive
+ {'DOWN', MRef, process, Pid, _Info} ->
+ ?vlog("ensure_dead_wait -> died peacefully", []),
+ throw(ok)
+ after Timeout ->
+ ?vlog("ensure_dead_wait -> giving up", []),
+ ok
+ end.
+
+ensure_dead_stop(Pid, MRef, Timeout) ->
+ StopPid = spawn(fun() -> snmpa_local_db:stop() end),
+ receive
+ {'DOWN', MRef, process, Pid, _Info} ->
+ ?vlog("ensure_dead -> dead (stopped)", []),
+ throw(ok)
+ after Timeout ->
+ ?vlog("ensure_dead_stop -> giving up", []),
+ exit(StopPid, kill),
+ ok
+ end.
+
+ensure_dead_kill(Pid, MRef, Timeout) ->
+ exit(Pid, kill),
+ receive
+ {'DOWN', MRef, process, Pid, _Info} ->
+ ?vlog("ensure_dead -> dead (killed)", []),
+ throw(ok)
+ after Timeout ->
+ ?vlog("ensure_dead_kill -> giving up", []),
+ ok
+ end.
+
+
display_incomming_message(M) ->
display_message("Incomming",M).
@@ -782,13 +887,13 @@ sz(O) ->
{unknown_size, O}.
d(F) -> d(F, []).
-d(F,A) -> d(get(debug),F,A).
+d(F,A) -> d(get(debug), F, A).
-d(true,F,A) ->
- io:format("*** [~s] MGR_PS_DBG *** " ++ F ++ "~n",
- [formated_timestamp()|A]);
+d(true, F, A) ->
+ print(F, A);
d(_,_F,_A) ->
ok.
-formated_timestamp() ->
- snmp_test_lib:formated_timestamp().
+print(F, A) ->
+ ?PRINT2("MGR_PS " ++ F, A).
+
diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml
index 17f14bdea2..72af24d2fa 100644
--- a/lib/ssh/doc/src/notes.xml
+++ b/lib/ssh/doc/src/notes.xml
@@ -30,6 +30,31 @@
<file>notes.xml</file>
</header>
+<section><title>Ssh 4.7.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The callback <c>ssh_channel:init/1</c> was missing in
+ OTP-21</p>
+ <p>
+ Own Id: OTP-15762</p>
+ </item>
+ <item>
+ <p>
+ If a client was connected to an server on an already open
+ socket, the callback <c>fun(PeerName,FingerPrint)</c> in
+ the <c>accept_callback</c> option passed the local name
+ in the argument PeerName instead of the remote name.</p>
+ <p>
+ Own Id: OTP-15763</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Ssh 4.7.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index 9281bf84a7..ff5aee14d7 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -127,7 +127,7 @@ connect(Socket, UserOptions, NegotiationTimeout) when is_port(Socket),
Options ->
case valid_socket_to_use(Socket, ?GET_OPT(transport,Options)) of
ok ->
- {ok, {Host,_Port}} = inet:sockname(Socket),
+ {ok, {Host,_Port}} = inet:peername(Socket),
Opts = ?PUT_INTERNAL_OPT([{user_pid,self()}, {host,Host}], Options),
ssh_connection_handler:start_connection(client, Socket, Opts, NegotiationTimeout);
{error,SockError} ->
diff --git a/lib/ssh/src/ssh_channel.erl b/lib/ssh/src/ssh_channel.erl
index 443bd05086..1d977e3bc9 100644
--- a/lib/ssh/src/ssh_channel.erl
+++ b/lib/ssh/src/ssh_channel.erl
@@ -58,6 +58,7 @@
State::term()}.
%%% API
-export([start/4, start/5, start_link/4, start_link/5, call/2, call/3,
+ init/1,
cast/2, reply/2, enter_loop/1]).
%%====================================================================
@@ -76,6 +77,9 @@ cast(ChannelPid, Msg) ->
reply(From, Msg) ->
ssh_client_channel:reply(From, Msg).
+init(Args) ->
+ ssh_client_channel:init(Args).
+
start(ConnectionManager, ChannelId, CallBack, CbInitArgs) ->
ssh_client_channel:start(ConnectionManager, ChannelId, CallBack, CbInitArgs).
diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk
index 0f9eee887c..2947f82556 100644
--- a/lib/ssh/vsn.mk
+++ b/lib/ssh/vsn.mk
@@ -1,4 +1,4 @@
#-*-makefile-*- ; force emacs to enter makefile-mode
-SSH_VSN = 4.7.4
+SSH_VSN = 4.7.5
APP_VSN = "ssh-$(SSH_VSN)"
diff --git a/lib/ssl/src/dtls_handshake.erl b/lib/ssl/src/dtls_handshake.erl
index 0a0c6f0c2e..d8c0e30973 100644
--- a/lib/ssl/src/dtls_handshake.erl
+++ b/lib/ssl/src/dtls_handshake.erl
@@ -193,7 +193,7 @@ handle_client_hello(Version,
no_suite ->
?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY);
_ ->
- #{key_exchange := KeyExAlg} = ssl_cipher_format:suite_definition(CipherSuite),
+ #{key_exchange := KeyExAlg} = ssl_cipher_format:suite_bin_to_map(CipherSuite),
case ssl_handshake:select_hashsign({ClientHashSigns, undefined}, Cert, KeyExAlg,
SupportedHashSigns, TLSVersion) of
#alert{} = Alert ->
diff --git a/lib/ssl/src/dtls_v1.erl b/lib/ssl/src/dtls_v1.erl
index b365961a6a..fc9dce02ce 100644
--- a/lib/ssl/src/dtls_v1.erl
+++ b/lib/ssl/src/dtls_v1.erl
@@ -31,18 +31,18 @@
suites(Minor) ->
lists:filter(fun(Cipher) ->
- is_acceptable_cipher(ssl_cipher_format:suite_definition(Cipher))
+ is_acceptable_cipher(ssl_cipher_format:suite_bin_to_map(Cipher))
end,
tls_v1:suites(corresponding_minor_tls_version(Minor))).
all_suites(Version) ->
lists:filter(fun(Cipher) ->
- is_acceptable_cipher(ssl_cipher_format:suite_definition(Cipher))
+ is_acceptable_cipher(ssl_cipher_format:suite_bin_to_map(Cipher))
end,
ssl_cipher:all_suites(corresponding_tls_version(Version))).
anonymous_suites(Version) ->
lists:filter(fun(Cipher) ->
- is_acceptable_cipher(ssl_cipher_format:suite_definition(Cipher))
+ is_acceptable_cipher(ssl_cipher_format:suite_bin_to_map(Cipher))
end,
ssl_cipher:anonymous_suites(corresponding_tls_version(Version))).
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 5da924ef16..74511dce4b 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -789,14 +789,14 @@ cipher_suites() ->
%% Description: Returns all supported cipher suites.
%%--------------------------------------------------------------------
cipher_suites(erlang) ->
- [ssl_cipher_format:erl_suite_definition(Suite) || Suite <- available_suites(default)];
+ [ssl_cipher_format:suite_legacy(Suite) || Suite <- available_suites(default)];
cipher_suites(openssl) ->
- [ssl_cipher_format:openssl_suite_name(Suite) ||
+ [ssl_cipher_format:suite_map_to_openssl_str(ssl_cipher_format:suite_bin_to_map(Suite)) ||
Suite <- available_suites(default)];
cipher_suites(all) ->
- [ssl_cipher_format:erl_suite_definition(Suite) || Suite <- available_suites(all)].
+ [ssl_cipher_format:suite_legacy(Suite) || Suite <- available_suites(all)].
%%--------------------------------------------------------------------
-spec cipher_suites(default | all | anonymous, ssl_record:ssl_version() |
@@ -814,7 +814,7 @@ cipher_suites(Base, Version) when Version == 'dtlsv1.2';
Version == 'dtlsv1'->
cipher_suites(Base, dtls_record:protocol_version(Version));
cipher_suites(Base, Version) ->
- [ssl_cipher_format:suite_definition(Suite) || Suite <- supported_suites(Base, Version)].
+ [ssl_cipher_format:suite_bin_to_map(Suite) || Suite <- supported_suites(Base, Version)].
%%--------------------------------------------------------------------
-spec filter_cipher_suites([erl_cipher_suite()] | [ssl_cipher_format:cipher_suite()] ,
@@ -1166,15 +1166,13 @@ tls_version({3, _} = Version) ->
tls_version({254, _} = Version) ->
dtls_v1:corresponding_tls_version(Version).
-
%%--------------------------------------------------------------------
-spec suite_to_str(erl_cipher_suite()) -> string().
%%
%% Description: Return the string representation of a cipher suite.
%%--------------------------------------------------------------------
suite_to_str(Cipher) ->
- ssl_cipher_format:suite_to_str(Cipher).
-
+ ssl_cipher_format:suite_map_to_str(Cipher).
%%%--------------------------------------------------------------
%%% Internal functions
@@ -1814,10 +1812,10 @@ binary_cipher_suites(Version, []) ->
%% not require explicit configuration
default_binary_suites(Version);
binary_cipher_suites(Version, [Map|_] = Ciphers0) when is_map(Map) ->
- Ciphers = [ssl_cipher_format:suite(C) || C <- Ciphers0],
+ Ciphers = [ssl_cipher_format:suite_map_to_bin(C) || C <- Ciphers0],
binary_cipher_suites(Version, Ciphers);
binary_cipher_suites(Version, [Tuple|_] = Ciphers0) when is_tuple(Tuple) ->
- Ciphers = [ssl_cipher_format:suite(tuple_to_map(C)) || C <- Ciphers0],
+ Ciphers = [ssl_cipher_format:suite_map_to_bin(tuple_to_map(C)) || C <- Ciphers0],
binary_cipher_suites(Version, Ciphers);
binary_cipher_suites(Version, [Cipher0 | _] = Ciphers0) when is_binary(Cipher0) ->
All = ssl_cipher:all_suites(Version) ++
@@ -1832,11 +1830,11 @@ binary_cipher_suites(Version, [Cipher0 | _] = Ciphers0) when is_binary(Cipher0)
end;
binary_cipher_suites(Version, [Head | _] = Ciphers0) when is_list(Head) ->
%% Format: ["RC4-SHA","RC4-MD5"]
- Ciphers = [ssl_cipher_format:openssl_suite(C) || C <- Ciphers0],
+ Ciphers = [ssl_cipher_format:suite_openssl_str_to_map(C) || C <- Ciphers0],
binary_cipher_suites(Version, Ciphers);
binary_cipher_suites(Version, Ciphers0) ->
%% Format: "RC4-SHA:RC4-MD5"
- Ciphers = [ssl_cipher_format:openssl_suite(C) || C <- string:lexemes(Ciphers0, ":")],
+ Ciphers = [ssl_cipher_format:suite_openssl_str_to_map(C) || C <- string:lexemes(Ciphers0, ":")],
binary_cipher_suites(Version, Ciphers).
default_binary_suites(Version) ->
diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl
index 2238b5290d..21db887bb5 100644
--- a/lib/ssl/src/ssl_cipher.erl
+++ b/lib/ssl/src/ssl_cipher.erl
@@ -76,7 +76,7 @@ security_parameters(?TLS_NULL_WITH_NULL_NULL = CipherSuite, SecParams) ->
%%-------------------------------------------------------------------
security_parameters(Version, CipherSuite, SecParams) ->
#{cipher := Cipher, mac := Hash,
- prf := PrfHashAlg} = ssl_cipher_format:suite_definition(CipherSuite),
+ prf := PrfHashAlg} = ssl_cipher_format:suite_bin_to_map(CipherSuite),
SecParams#security_parameters{
cipher_suite = CipherSuite,
bulk_cipher_algorithm = bulk_cipher_algorithm(Cipher),
@@ -91,7 +91,7 @@ security_parameters(Version, CipherSuite, SecParams) ->
security_parameters_1_3(SecParams, CipherSuite) ->
#{cipher := Cipher, prf := PrfHashAlg} =
- ssl_cipher_format:suite_definition(CipherSuite),
+ ssl_cipher_format:suite_bin_to_map(CipherSuite),
SecParams#security_parameters{
cipher_suite = CipherSuite,
bulk_cipher_algorithm = bulk_cipher_algorithm(Cipher),
@@ -549,7 +549,7 @@ filter_suite(#{key_exchange := KeyExchange,
all_filters(Hash, HashFilters) andalso
all_filters(Prf, PrfFilters);
filter_suite(Suite, Filters) ->
- filter_suite(ssl_cipher_format:suite_definition(Suite), Filters).
+ filter_suite(ssl_cipher_format:suite_bin_to_map(Suite), Filters).
%%--------------------------------------------------------------------
-spec filter_suites([ssl:erl_cipher_suite()] | [ssl_cipher_format:cipher_suite()]) ->
diff --git a/lib/ssl/src/ssl_cipher_format.erl b/lib/ssl/src/ssl_cipher_format.erl
index e0df3662ef..887eb6c653 100644
--- a/lib/ssl/src/ssl_cipher_format.erl
+++ b/lib/ssl/src/ssl_cipher_format.erl
@@ -48,48 +48,134 @@
-type openssl_cipher_suite() :: string().
--export([suite_to_str/1, suite_definition/1, suite/1, erl_suite_definition/1,
- openssl_suite/1, openssl_suite_name/1]).
+-export([suite_map_to_bin/1, %% Binary format
+ suite_bin_to_map/1, %% Erlang API format
+ suite_map_to_str/1, %% RFC string
+ suite_str_to_map/1,
+ suite_map_to_openssl_str/1, %% OpenSSL name
+ suite_openssl_str_to_map/1,
+ suite_legacy/1 %% Erlang legacy format
+ ]).
%%--------------------------------------------------------------------
--spec suite_to_str(internal_erl_cipher_suite()) -> string().
+-spec suite_map_to_str(internal_erl_cipher_suite()) -> string().
%%
%% Description: Return the string representation of a cipher suite.
%%--------------------------------------------------------------------
-suite_to_str(#{key_exchange := null,
+suite_map_to_str(#{key_exchange := null,
cipher := null,
mac := null,
prf := null}) ->
"TLS_EMPTY_RENEGOTIATION_INFO_SCSV";
-suite_to_str(#{key_exchange := any,
+suite_map_to_str(#{key_exchange := any,
cipher := Cipher,
mac := aead,
prf := PRF}) ->
"TLS_" ++ string:to_upper(atom_to_list(Cipher)) ++
"_" ++ string:to_upper(atom_to_list(PRF));
-suite_to_str(#{key_exchange := Kex,
+suite_map_to_str(#{key_exchange := Kex,
cipher := Cipher,
mac := aead,
prf := PRF}) ->
"TLS_" ++ string:to_upper(atom_to_list(Kex)) ++
"_WITH_" ++ string:to_upper(atom_to_list(Cipher)) ++
"_" ++ string:to_upper(atom_to_list(PRF));
-suite_to_str(#{key_exchange := Kex,
+suite_map_to_str(#{key_exchange := Kex,
cipher := Cipher,
mac := Mac}) ->
"TLS_" ++ string:to_upper(atom_to_list(Kex)) ++
"_WITH_" ++ string:to_upper(atom_to_list(Cipher)) ++
"_" ++ string:to_upper(atom_to_list(Mac)).
+suite_str_to_map("TLS_EMPTY_RENEGOTIATION_INFO_SCSV") ->
+ #{key_exchange => null,
+ cipher => null,
+ mac => null,
+ prf => null};
+suite_str_to_map(SuiteStr)->
+ Str0 = string:trim(SuiteStr, leading, "TLS_"),
+ case string:split(Str0, "_WITH_") of
+ [Rest] ->
+ tls_1_3_suite_str_to_map(Rest);
+ [Kex| Rest] ->
+ pre_tls_1_3_suite_str_to_map(Kex, Rest)
+ end.
+
+suite_map_to_openssl_str(#{key_exchange := any,
+ mac := aead} = Suite) ->
+ %% TLS 1.3 OpenSSL finally use RFC names
+ suite_map_to_str(Suite);
+suite_map_to_openssl_str(#{key_exchange := null} = Suite) ->
+ %% TLS_EMPTY_RENEGOTIATION_INFO_SCSV
+ suite_map_to_str(Suite);
+suite_map_to_openssl_str(#{key_exchange := Kex,
+ cipher := chacha20_poly1305 = Cipher,
+ mac := aead}) ->
+ openssl_suite_start(string:to_upper(atom_to_list(Kex)))
+ ++ openssl_cipher_name(Kex, string:to_upper(atom_to_list(Cipher)));
+suite_map_to_openssl_str(#{key_exchange := Kex,
+ cipher := Cipher,
+ mac := aead,
+ prf := PRF}) ->
+ openssl_suite_start(string:to_upper(atom_to_list(Kex)))
+ ++ openssl_cipher_name(Kex, string:to_upper(atom_to_list(Cipher))) ++
+ "-" ++ string:to_upper(atom_to_list(PRF));
+suite_map_to_openssl_str(#{key_exchange := Kex,
+ cipher := Cipher,
+ mac := Mac}) ->
+ openssl_suite_start(string:to_upper(atom_to_list(Kex)))
+ ++ openssl_cipher_name(Kex, string:to_upper(atom_to_list(Cipher))) ++
+ "-" ++ string:to_upper(atom_to_list(Mac)).
+
+
+suite_openssl_str_to_map("TLS_" ++ _ = SuiteStr) ->
+ suite_str_to_map(SuiteStr);
+suite_openssl_str_to_map("DHE-RSA-" ++ Rest) ->
+ suite_openssl_str_to_map("DHE-RSA", Rest);
+suite_openssl_str_to_map("DHE-DSS-" ++ Rest) ->
+ suite_openssl_str_to_map("DHE-DSS", Rest);
+suite_openssl_str_to_map("EDH-RSA-" ++ Rest) ->
+ suite_openssl_str_to_map("DHE-RSA", Rest);
+suite_openssl_str_to_map("EDH-DSS-" ++ Rest) ->
+ suite_openssl_str_to_map("DHE-DSS", Rest);
+suite_openssl_str_to_map("DES" ++ _ = Rest) ->
+ suite_openssl_str_to_map("RSA", Rest);
+suite_openssl_str_to_map("AES" ++ _ = Rest) ->
+ suite_openssl_str_to_map("RSA", Rest);
+suite_openssl_str_to_map("RC4" ++ _ = Rest) ->
+ suite_openssl_str_to_map("RSA", Rest);
+suite_openssl_str_to_map("ECDH-RSA-" ++ Rest) ->
+ suite_openssl_str_to_map("ECDH-RSA", Rest);
+suite_openssl_str_to_map("ECDH-ECDSA-" ++ Rest) ->
+ suite_openssl_str_to_map("ECDH-ECDSA", Rest);
+suite_openssl_str_to_map("ECDHE-RSA-" ++ Rest) ->
+ suite_openssl_str_to_map("ECDHE-RSA", Rest);
+suite_openssl_str_to_map("ECDHE-ECDSA-" ++ Rest) ->
+ suite_openssl_str_to_map("ECDHE-ECDSA", Rest);
+suite_openssl_str_to_map("RSA-PSK-" ++ Rest) ->
+ suite_openssl_str_to_map("RSA-PSK", Rest);
+suite_openssl_str_to_map("RSA-" ++ Rest) ->
+ suite_openssl_str_to_map("RSA", Rest);
+suite_openssl_str_to_map("DHE-PSK-" ++ Rest) ->
+ suite_openssl_str_to_map("DHE-PSK", Rest);
+suite_openssl_str_to_map("ECDHE-PSK-" ++ Rest) ->
+ suite_openssl_str_to_map("ECDHE-PSK", Rest);
+suite_openssl_str_to_map("PSK-" ++ Rest) ->
+ suite_openssl_str_to_map("PSK", Rest);
+suite_openssl_str_to_map("SRP-RSA-" ++ Rest) ->
+ suite_openssl_str_to_map("SRP-RSA", Rest);
+suite_openssl_str_to_map("SRP-" ++ Rest) ->
+ suite_openssl_str_to_map("SRP", Rest).
+
%%--------------------------------------------------------------------
--spec suite_definition(cipher_suite()) -> internal_erl_cipher_suite().
+-spec suite_bin_to_map(cipher_suite()) -> internal_erl_cipher_suite().
%%
%% Description: Return erlang cipher suite definition.
%% Note: Currently not supported suites are commented away.
%% They should be supported or removed in the future.
%%-------------------------------------------------------------------
%% TLS v1.1 suites
-suite_definition(?TLS_NULL_WITH_NULL_NULL) ->
+suite_bin_to_map(?TLS_NULL_WITH_NULL_NULL) ->
#{key_exchange => null,
cipher => null,
mac => null,
@@ -97,111 +183,111 @@ suite_definition(?TLS_NULL_WITH_NULL_NULL) ->
%% RFC 5746 - Not a real cipher suite used to signal empty "renegotiation_info" extension
%% to avoid handshake failure from old servers that do not ignore
%% hello extension data as they should.
-suite_definition(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV) ->
+suite_bin_to_map(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV) ->
#{key_exchange => null,
cipher => null,
mac => null,
prf => null};
-suite_definition(?TLS_RSA_WITH_RC4_128_MD5) ->
+suite_bin_to_map(?TLS_RSA_WITH_RC4_128_MD5) ->
#{key_exchange => rsa,
cipher => rc4_128,
mac => md5,
prf => default_prf};
-suite_definition(?TLS_RSA_WITH_RC4_128_SHA) ->
+suite_bin_to_map(?TLS_RSA_WITH_RC4_128_SHA) ->
#{key_exchange => rsa,
cipher => rc4_128,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_RSA_WITH_DES_CBC_SHA) ->
+suite_bin_to_map(?TLS_RSA_WITH_DES_CBC_SHA) ->
#{key_exchange => rsa,
cipher => des_cbc,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_RSA_WITH_3DES_EDE_CBC_SHA) ->
+suite_bin_to_map(?TLS_RSA_WITH_3DES_EDE_CBC_SHA) ->
#{key_exchange => rsa,
cipher => '3des_ede_cbc',
mac => sha,
prf => default_prf};
-suite_definition(?TLS_DHE_DSS_WITH_DES_CBC_SHA) ->
+suite_bin_to_map(?TLS_DHE_DSS_WITH_DES_CBC_SHA) ->
#{key_exchange => dhe_dss,
cipher => des_cbc,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA) ->
+suite_bin_to_map(?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA) ->
#{key_exchange => dhe_dss,
cipher => '3des_ede_cbc',
mac => sha,
prf => default_prf};
-suite_definition(?TLS_DHE_RSA_WITH_DES_CBC_SHA) ->
+suite_bin_to_map(?TLS_DHE_RSA_WITH_DES_CBC_SHA) ->
#{key_exchange => dhe_rsa,
cipher => des_cbc,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA) ->
+suite_bin_to_map(?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA) ->
#{key_exchange => dhe_rsa,
cipher => '3des_ede_cbc',
mac => sha,
prf => default_prf};
%%% TSL V1.1 AES suites
-suite_definition(?TLS_RSA_WITH_AES_128_CBC_SHA) ->
+suite_bin_to_map(?TLS_RSA_WITH_AES_128_CBC_SHA) ->
#{key_exchange => rsa,
cipher => aes_128_cbc,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA) ->
+suite_bin_to_map(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA) ->
#{key_exchange => dhe_dss,
cipher => aes_128_cbc,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_DHE_RSA_WITH_AES_128_CBC_SHA) ->
+suite_bin_to_map(?TLS_DHE_RSA_WITH_AES_128_CBC_SHA) ->
#{key_exchange => dhe_rsa,
cipher => aes_128_cbc,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_RSA_WITH_AES_256_CBC_SHA) ->
+suite_bin_to_map(?TLS_RSA_WITH_AES_256_CBC_SHA) ->
#{key_exchange => rsa,
cipher => aes_256_cbc,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA) ->
+suite_bin_to_map(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA) ->
#{key_exchange => dhe_dss,
cipher => aes_256_cbc,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA) ->
+suite_bin_to_map(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA) ->
#{key_exchange => dhe_rsa,
cipher => aes_256_cbc,
mac => sha,
prf => default_prf};
%% TLS v1.2 suites
-%% suite_definition(?TLS_RSA_WITH_NULL_SHA) ->
+%% suite_bin_to_map(?TLS_RSA_WITH_NULL_SHA) ->
%% {rsa, null, sha, default_prf};
-suite_definition(?TLS_RSA_WITH_AES_128_CBC_SHA256) ->
+suite_bin_to_map(?TLS_RSA_WITH_AES_128_CBC_SHA256) ->
#{key_exchange => rsa,
cipher => aes_128_cbc,
mac => sha256,
prf => default_prf};
-suite_definition(?TLS_RSA_WITH_AES_256_CBC_SHA256) ->
+suite_bin_to_map(?TLS_RSA_WITH_AES_256_CBC_SHA256) ->
#{key_exchange => rsa,
cipher => aes_256_cbc,
mac => sha256,
prf => default_prf};
-suite_definition(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256) ->
+suite_bin_to_map(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256) ->
#{key_exchange => dhe_dss,
cipher => aes_128_cbc,
mac => sha256,
prf => default_prf};
-suite_definition(?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256) ->
+suite_bin_to_map(?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256) ->
#{key_exchange => dhe_rsa,
cipher => aes_128_cbc,
mac => sha256,
prf => default_prf};
-suite_definition(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256) ->
+suite_bin_to_map(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256) ->
#{key_exchange => dhe_dss,
cipher => aes_256_cbc,
mac => sha256,
prf => default_prf};
-suite_definition(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256) ->
+suite_bin_to_map(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256) ->
#{key_exchange => dhe_rsa,
cipher => aes_256_cbc,
mac => sha256,
@@ -213,683 +299,683 @@ suite_definition(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256) ->
%% TLS_DH_RSA_WITH_AES_256_CBC_SHA256 DH_RSA AES_256_CBC SHA256
%%% DH-ANON deprecated by TLS spec and not available
%%% by default, but good for testing purposes.
-suite_definition(?TLS_DH_anon_WITH_RC4_128_MD5) ->
+suite_bin_to_map(?TLS_DH_anon_WITH_RC4_128_MD5) ->
#{key_exchange => dh_anon,
cipher => rc4_128,
mac => md5,
prf => default_prf};
-suite_definition(?TLS_DH_anon_WITH_DES_CBC_SHA) ->
+suite_bin_to_map(?TLS_DH_anon_WITH_DES_CBC_SHA) ->
#{key_exchange => dh_anon,
cipher => des_cbc,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA) ->
+suite_bin_to_map(?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA) ->
#{key_exchange => dh_anon,
cipher => '3des_ede_cbc',
mac => sha,
prf => default_prf};
-suite_definition(?TLS_DH_anon_WITH_AES_128_CBC_SHA) ->
+suite_bin_to_map(?TLS_DH_anon_WITH_AES_128_CBC_SHA) ->
#{key_exchange => dh_anon,
cipher => aes_128_cbc,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_DH_anon_WITH_AES_256_CBC_SHA) ->
+suite_bin_to_map(?TLS_DH_anon_WITH_AES_256_CBC_SHA) ->
#{key_exchange => dh_anon,
cipher => aes_256_cbc,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_DH_anon_WITH_AES_128_CBC_SHA256) ->
+suite_bin_to_map(?TLS_DH_anon_WITH_AES_128_CBC_SHA256) ->
#{key_exchange => dh_anon,
cipher => aes_128_cbc,
mac => sha256,
prf => default_prf};
-suite_definition(?TLS_DH_anon_WITH_AES_256_CBC_SHA256) ->
+suite_bin_to_map(?TLS_DH_anon_WITH_AES_256_CBC_SHA256) ->
#{key_exchange => dh_anon,
cipher => aes_256_cbc,
mac => sha256,
prf => default_prf};
%%% PSK Cipher Suites RFC 4279
-suite_definition(?TLS_PSK_WITH_RC4_128_SHA) ->
+suite_bin_to_map(?TLS_PSK_WITH_RC4_128_SHA) ->
#{key_exchange => psk,
cipher => rc4_128,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_PSK_WITH_3DES_EDE_CBC_SHA) ->
+suite_bin_to_map(?TLS_PSK_WITH_3DES_EDE_CBC_SHA) ->
#{key_exchange => psk,
cipher => '3des_ede_cbc',
mac => sha,
prf => default_prf};
-suite_definition(?TLS_PSK_WITH_AES_128_CBC_SHA) ->
+suite_bin_to_map(?TLS_PSK_WITH_AES_128_CBC_SHA) ->
#{key_exchange => psk,
cipher => aes_128_cbc,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_PSK_WITH_AES_256_CBC_SHA) ->
+suite_bin_to_map(?TLS_PSK_WITH_AES_256_CBC_SHA) ->
#{key_exchange => psk,
cipher => aes_256_cbc,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_DHE_PSK_WITH_RC4_128_SHA) ->
+suite_bin_to_map(?TLS_DHE_PSK_WITH_RC4_128_SHA) ->
#{key_exchange => dhe_psk,
cipher => rc4_128,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA) ->
+suite_bin_to_map(?TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA) ->
#{key_exchange => dhe_psk,
cipher => '3des_ede_cbc',
mac => sha,
prf => default_prf};
-suite_definition(?TLS_DHE_PSK_WITH_AES_128_CBC_SHA) ->
+suite_bin_to_map(?TLS_DHE_PSK_WITH_AES_128_CBC_SHA) ->
#{key_exchange => dhe_psk,
cipher => aes_128_cbc,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_DHE_PSK_WITH_AES_256_CBC_SHA) ->
+suite_bin_to_map(?TLS_DHE_PSK_WITH_AES_256_CBC_SHA) ->
#{key_exchange => dhe_psk,
cipher => aes_256_cbc,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_RSA_PSK_WITH_RC4_128_SHA) ->
+suite_bin_to_map(?TLS_RSA_PSK_WITH_RC4_128_SHA) ->
#{key_exchange => rsa_psk,
cipher => rc4_128,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA) ->
+suite_bin_to_map(?TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA) ->
#{key_exchange => rsa_psk,
cipher => '3des_ede_cbc',
mac => sha,
prf => default_prf};
-suite_definition(?TLS_RSA_PSK_WITH_AES_128_CBC_SHA) ->
+suite_bin_to_map(?TLS_RSA_PSK_WITH_AES_128_CBC_SHA) ->
#{key_exchange => rsa_psk,
cipher => aes_128_cbc,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_RSA_PSK_WITH_AES_256_CBC_SHA) ->
+suite_bin_to_map(?TLS_RSA_PSK_WITH_AES_256_CBC_SHA) ->
#{key_exchange => rsa_psk,
cipher => aes_256_cbc,
mac => sha,
prf => default_prf};
%%% PSK NULL Cipher Suites RFC 4785
-suite_definition(?TLS_PSK_WITH_NULL_SHA) ->
+suite_bin_to_map(?TLS_PSK_WITH_NULL_SHA) ->
#{key_exchange => psk,
cipher => null,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_DHE_PSK_WITH_NULL_SHA) ->
+suite_bin_to_map(?TLS_DHE_PSK_WITH_NULL_SHA) ->
#{key_exchange => dhe_psk,
cipher => null,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_RSA_PSK_WITH_NULL_SHA) ->
+suite_bin_to_map(?TLS_RSA_PSK_WITH_NULL_SHA) ->
#{key_exchange => rsa_psk,
cipher => null,
mac => sha,
prf => default_prf};
%%% TLS 1.2 PSK Cipher Suites RFC 5487
-suite_definition(?TLS_PSK_WITH_AES_128_GCM_SHA256) ->
+suite_bin_to_map(?TLS_PSK_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => psk,
cipher => aes_128_gcm,
mac => aead,
prf => sha256};
-suite_definition(?TLS_PSK_WITH_AES_256_GCM_SHA384) ->
+suite_bin_to_map(?TLS_PSK_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => psk,
cipher => aes_256_gcm,
mac => aead,
prf => sha384};
-suite_definition(?TLS_DHE_PSK_WITH_AES_128_GCM_SHA256) ->
+suite_bin_to_map(?TLS_DHE_PSK_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => dhe_psk,
cipher => aes_128_gcm,
mac => aead,
prf => sha256};
-suite_definition(?TLS_DHE_PSK_WITH_AES_256_GCM_SHA384) ->
+suite_bin_to_map(?TLS_DHE_PSK_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => dhe_psk,
cipher => aes_256_gcm,
mac => aead,
prf => sha384};
-suite_definition(?TLS_RSA_PSK_WITH_AES_128_GCM_SHA256) ->
+suite_bin_to_map(?TLS_RSA_PSK_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => rsa_psk,
cipher => aes_128_gcm,
mac => aead,
prf => sha256};
-suite_definition(?TLS_RSA_PSK_WITH_AES_256_GCM_SHA384) ->
+suite_bin_to_map(?TLS_RSA_PSK_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => rsa_psk,
cipher => aes_256_gcm,
mac => aead,
prf => sha384};
-suite_definition(?TLS_PSK_WITH_AES_128_CBC_SHA256) ->
+suite_bin_to_map(?TLS_PSK_WITH_AES_128_CBC_SHA256) ->
#{key_exchange => psk,
cipher => aes_128_cbc,
mac => sha256,
prf => default_prf};
-suite_definition(?TLS_PSK_WITH_AES_256_CBC_SHA384) ->
+suite_bin_to_map(?TLS_PSK_WITH_AES_256_CBC_SHA384) ->
#{key_exchange => psk,
cipher => aes_256_cbc,
mac => sha384,
prf => default_prf};
-suite_definition(?TLS_DHE_PSK_WITH_AES_128_CBC_SHA256) ->
+suite_bin_to_map(?TLS_DHE_PSK_WITH_AES_128_CBC_SHA256) ->
#{key_exchange => dhe_psk,
cipher => aes_128_cbc,
mac => sha256,
prf => default_prf};
-suite_definition(?TLS_DHE_PSK_WITH_AES_256_CBC_SHA384) ->
+suite_bin_to_map(?TLS_DHE_PSK_WITH_AES_256_CBC_SHA384) ->
#{key_exchange => dhe_psk,
cipher => aes_256_cbc,
mac => sha384,
prf => default_prf};
-suite_definition(?TLS_RSA_PSK_WITH_AES_128_CBC_SHA256) ->
+suite_bin_to_map(?TLS_RSA_PSK_WITH_AES_128_CBC_SHA256) ->
#{key_exchange => rsa_psk,
cipher => aes_128_cbc,
mac => sha256,
prf => default_prf};
-suite_definition(?TLS_RSA_PSK_WITH_AES_256_CBC_SHA384) ->
+suite_bin_to_map(?TLS_RSA_PSK_WITH_AES_256_CBC_SHA384) ->
#{key_exchange => rsa_psk,
cipher => aes_256_cbc,
mac => sha384,
prf => default_prf};
-suite_definition(?TLS_PSK_WITH_NULL_SHA256) ->
+suite_bin_to_map(?TLS_PSK_WITH_NULL_SHA256) ->
#{key_exchange => psk,
cipher => null,
mac => sha256,
prf => default_prf};
-suite_definition(?TLS_PSK_WITH_NULL_SHA384) ->
+suite_bin_to_map(?TLS_PSK_WITH_NULL_SHA384) ->
#{key_exchange => psk,
cipher => null,
mac => sha384,
prf => default_prf};
-suite_definition(?TLS_DHE_PSK_WITH_NULL_SHA256) ->
+suite_bin_to_map(?TLS_DHE_PSK_WITH_NULL_SHA256) ->
#{key_exchange => dhe_psk,
cipher => null,
mac => sha256,
prf => default_prf};
-suite_definition(?TLS_DHE_PSK_WITH_NULL_SHA384) ->
+suite_bin_to_map(?TLS_DHE_PSK_WITH_NULL_SHA384) ->
#{key_exchange => dhe_psk,
cipher => null,
mac => sha384,
prf => default_prf};
-suite_definition(?TLS_RSA_PSK_WITH_NULL_SHA256) ->
+suite_bin_to_map(?TLS_RSA_PSK_WITH_NULL_SHA256) ->
#{key_exchange => rsa_psk,
cipher => null,
mac => sha256,
prf => default_prf};
-suite_definition(?TLS_RSA_PSK_WITH_NULL_SHA384) ->
+suite_bin_to_map(?TLS_RSA_PSK_WITH_NULL_SHA384) ->
#{key_exchange => rsa_psk,
cipher => null,
mac => sha384,
prf => default_prf};
%%% ECDHE PSK Cipher Suites RFC 5489
-suite_definition(?TLS_ECDHE_PSK_WITH_RC4_128_SHA) ->
+suite_bin_to_map(?TLS_ECDHE_PSK_WITH_RC4_128_SHA) ->
#{key_exchange => ecdhe_psk,
cipher => rc4_128,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA) ->
+suite_bin_to_map(?TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA) ->
#{key_exchange => ecdhe_psk,
cipher => '3des_ede_cbc',
mac => sha,
prf => default_prf};
-suite_definition(?TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA) ->
+suite_bin_to_map(?TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA) ->
#{key_exchange => ecdhe_psk,
cipher => aes_128_cbc,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA) ->
+suite_bin_to_map(?TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA) ->
#{key_exchange => ecdhe_psk,
cipher => aes_256_cbc,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256) ->
+suite_bin_to_map(?TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256) ->
#{key_exchange => ecdhe_psk,
cipher => aes_128_cbc,
mac => sha256,
prf => default_prf};
-suite_definition(?TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384) ->
+suite_bin_to_map(?TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384) ->
#{key_exchange => ecdhe_psk,
cipher => aes_256_cbc,
mac => sha384,
prf => default_prf};
-suite_definition(?TLS_ECDHE_PSK_WITH_NULL_SHA256) ->
+suite_bin_to_map(?TLS_ECDHE_PSK_WITH_NULL_SHA256) ->
#{key_exchange => ecdhe_psk,
cipher => null,
mac => sha256,
prf => default_prf};
-suite_definition(?TLS_ECDHE_PSK_WITH_NULL_SHA384) ->
+suite_bin_to_map(?TLS_ECDHE_PSK_WITH_NULL_SHA384) ->
#{key_exchange => ecdhe_psk,
cipher => null, mac => sha384,
prf => default_prf};
%%% ECDHE_PSK with AES-GCM and AES-CCM Cipher Suites, draft-ietf-tls-ecdhe-psk-aead-05
-suite_definition(?TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256) ->
+suite_bin_to_map(?TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => ecdhe_psk,
cipher => aes_128_gcm,
mac => null,
prf => sha256};
-suite_definition(?TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384) ->
+suite_bin_to_map(?TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => ecdhe_psk,
cipher => aes_256_gcm,
mac => null,
prf => sha384};
-suite_definition(?TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256) ->
+suite_bin_to_map(?TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256) ->
#{key_exchange => ecdhe_psk,
cipher => aes_128_ccm,
mac => null,
prf =>sha256};
-suite_definition(?TLS_ECDHE_PSK_WITH_AES_128_CCM_8_SHA256) ->
+suite_bin_to_map(?TLS_ECDHE_PSK_WITH_AES_128_CCM_8_SHA256) ->
#{key_exchange => ecdhe_psk,
cipher => aes_128_ccm_8,
mac => null,
prf =>sha256};
%%% SRP Cipher Suites RFC 5054
-suite_definition(?TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA) ->
+suite_bin_to_map(?TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA) ->
#{key_exchange => srp_anon,
cipher => '3des_ede_cbc',
mac => sha,
prf => default_prf};
-suite_definition(?TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA) ->
+suite_bin_to_map(?TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA) ->
#{key_exchange => srp_rsa,
cipher => '3des_ede_cbc',
mac => sha,
prf => default_prf};
-suite_definition(?TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA) ->
+suite_bin_to_map(?TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA) ->
#{key_exchange => srp_dss,
cipher => '3des_ede_cbc',
mac => sha,
prf => default_prf};
-suite_definition(?TLS_SRP_SHA_WITH_AES_128_CBC_SHA) ->
+suite_bin_to_map(?TLS_SRP_SHA_WITH_AES_128_CBC_SHA) ->
#{key_exchange => srp_anon,
cipher => aes_128_cbc,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA) ->
+suite_bin_to_map(?TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA) ->
#{key_exchange => srp_rsa,
cipher => aes_128_cbc,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA) ->
+suite_bin_to_map(?TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA) ->
#{key_exchange => srp_dss,
cipher => aes_128_cbc,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_SRP_SHA_WITH_AES_256_CBC_SHA) ->
+suite_bin_to_map(?TLS_SRP_SHA_WITH_AES_256_CBC_SHA) ->
#{key_exchange => srp_anon,
cipher => aes_256_cbc,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA) ->
+suite_bin_to_map(?TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA) ->
#{key_exchange => srp_rsa,
cipher => aes_256_cbc,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA) ->
+suite_bin_to_map(?TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA) ->
#{key_exchange => srp_dss,
cipher => aes_256_cbc,
mac => sha,
prf => default_prf};
%% RFC 4492 EC TLS suites
-suite_definition(?TLS_ECDH_ECDSA_WITH_NULL_SHA) ->
+suite_bin_to_map(?TLS_ECDH_ECDSA_WITH_NULL_SHA) ->
#{key_exchange => ecdh_ecdsa,
cipher => null,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_ECDH_ECDSA_WITH_RC4_128_SHA) ->
+suite_bin_to_map(?TLS_ECDH_ECDSA_WITH_RC4_128_SHA) ->
#{key_exchange => ecdh_ecdsa,
cipher => rc4_128,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA) ->
+suite_bin_to_map(?TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA) ->
#{key_exchange => ecdh_ecdsa,
cipher => '3des_ede_cbc',
mac => sha,
prf => default_prf};
-suite_definition(?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA) ->
+suite_bin_to_map(?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA) ->
#{key_exchange => ecdh_ecdsa,
cipher => aes_128_cbc,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA) ->
+suite_bin_to_map(?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA) ->
#{key_exchange => ecdh_ecdsa,
cipher => aes_256_cbc,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_ECDHE_ECDSA_WITH_NULL_SHA) ->
+suite_bin_to_map(?TLS_ECDHE_ECDSA_WITH_NULL_SHA) ->
#{key_exchange => ecdhe_ecdsa,
cipher => null,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_ECDHE_ECDSA_WITH_RC4_128_SHA) ->
+suite_bin_to_map(?TLS_ECDHE_ECDSA_WITH_RC4_128_SHA) ->
#{key_exchange => ecdhe_ecdsa,
cipher => rc4_128,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA) ->
+suite_bin_to_map(?TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA) ->
#{key_exchange => ecdhe_ecdsa,
cipher => '3des_ede_cbc',
mac => sha,
prf => default_prf};
-suite_definition(?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA) ->
+suite_bin_to_map(?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA) ->
#{key_exchange => ecdhe_ecdsa,
cipher => aes_128_cbc,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA) ->
+suite_bin_to_map(?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA) ->
#{key_exchange => ecdhe_ecdsa,
cipher => aes_256_cbc,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_ECDH_RSA_WITH_NULL_SHA) ->
+suite_bin_to_map(?TLS_ECDH_RSA_WITH_NULL_SHA) ->
#{key_exchange => ecdh_rsa,
cipher => null,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_ECDH_RSA_WITH_RC4_128_SHA) ->
+suite_bin_to_map(?TLS_ECDH_RSA_WITH_RC4_128_SHA) ->
#{key_exchange => ecdh_rsa,
cipher => rc4_128,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA) ->
+suite_bin_to_map(?TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA) ->
#{key_exchange => ecdh_rsa,
cipher => '3des_ede_cbc',
mac => sha,
prf => default_prf};
-suite_definition(?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA) ->
+suite_bin_to_map(?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA) ->
#{key_exchange => ecdh_rsa,
cipher => aes_128_cbc,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA) ->
+suite_bin_to_map(?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA) ->
#{key_exchange => ecdh_rsa,
cipher => aes_256_cbc,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_ECDHE_RSA_WITH_NULL_SHA) ->
+suite_bin_to_map(?TLS_ECDHE_RSA_WITH_NULL_SHA) ->
#{key_exchange => ecdhe_rsa,
cipher => null,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_ECDHE_RSA_WITH_RC4_128_SHA) ->
+suite_bin_to_map(?TLS_ECDHE_RSA_WITH_RC4_128_SHA) ->
#{key_exchange => ecdhe_rsa,
cipher => rc4_128,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA) ->
+suite_bin_to_map(?TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA) ->
#{key_exchange => ecdhe_rsa,
cipher => '3des_ede_cbc',
mac => sha,
prf => default_prf};
-suite_definition(?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA) ->
+suite_bin_to_map(?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA) ->
#{key_exchange => ecdhe_rsa,
cipher => aes_128_cbc,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA) ->
+suite_bin_to_map(?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA) ->
#{key_exchange => ecdhe_rsa,
cipher => aes_256_cbc,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_ECDH_anon_WITH_NULL_SHA) ->
+suite_bin_to_map(?TLS_ECDH_anon_WITH_NULL_SHA) ->
#{key_exchange => ecdh_anon,
cipher => null,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_ECDH_anon_WITH_RC4_128_SHA) ->
+suite_bin_to_map(?TLS_ECDH_anon_WITH_RC4_128_SHA) ->
#{key_exchange => ecdh_anon,
cipher => rc4_128,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA) ->
+suite_bin_to_map(?TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA) ->
#{key_exchange => ecdh_anon,
cipher => '3des_ede_cbc',
mac => sha,
prf => default_prf};
-suite_definition(?TLS_ECDH_anon_WITH_AES_128_CBC_SHA) ->
+suite_bin_to_map(?TLS_ECDH_anon_WITH_AES_128_CBC_SHA) ->
#{key_exchange => ecdh_anon,
cipher => aes_128_cbc,
mac => sha,
prf => default_prf};
-suite_definition(?TLS_ECDH_anon_WITH_AES_256_CBC_SHA) ->
+suite_bin_to_map(?TLS_ECDH_anon_WITH_AES_256_CBC_SHA) ->
#{key_exchange => ecdh_anon,
cipher => aes_256_cbc,
mac => sha,
prf => default_prf};
%% RFC 5289 EC TLS suites
-suite_definition(?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256) ->
+suite_bin_to_map(?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256) ->
#{key_exchange => ecdhe_ecdsa,
cipher => aes_128_cbc,
mac => sha256,
prf => sha256};
-suite_definition(?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384) ->
+suite_bin_to_map(?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384) ->
#{key_exchange => ecdhe_ecdsa,
cipher => aes_256_cbc,
mac => sha384,
prf => sha384};
-suite_definition(?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256) ->
+suite_bin_to_map(?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256) ->
#{key_exchange => ecdh_ecdsa,
cipher => aes_128_cbc,
mac => sha256,
prf => sha256};
-suite_definition(?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384) ->
+suite_bin_to_map(?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384) ->
#{key_exchange => ecdh_ecdsa,
cipher => aes_256_cbc,
mac => sha384,
prf => sha384};
-suite_definition(?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256) ->
+suite_bin_to_map(?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256) ->
#{key_exchange => ecdhe_rsa,
cipher => aes_128_cbc,
mac => sha256,
prf => sha256};
-suite_definition(?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384) ->
+suite_bin_to_map(?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384) ->
#{key_exchange => ecdhe_rsa,
cipher => aes_256_cbc,
mac => sha384,
prf => sha384};
-suite_definition(?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256) ->
+suite_bin_to_map(?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256) ->
#{key_exchange => ecdh_rsa,
cipher => aes_128_cbc,
mac => sha256,
prf => sha256};
-suite_definition(?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384) ->
+suite_bin_to_map(?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384) ->
#{key_exchange => ecdh_rsa,
cipher => aes_256_cbc,
mac => sha384,
prf => sha384};
%% RFC 5288 AES-GCM Cipher Suites
-suite_definition(?TLS_RSA_WITH_AES_128_GCM_SHA256) ->
+suite_bin_to_map(?TLS_RSA_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => rsa,
cipher => aes_128_gcm,
mac => aead,
prf => sha256};
-suite_definition(?TLS_RSA_WITH_AES_256_GCM_SHA384) ->
+suite_bin_to_map(?TLS_RSA_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => rsa,
cipher => aes_256_gcm,
mac => aead,
prf => sha384};
-suite_definition(?TLS_DHE_RSA_WITH_AES_128_GCM_SHA256) ->
+suite_bin_to_map(?TLS_DHE_RSA_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => dhe_rsa,
cipher => aes_128_gcm,
mac => aead,
prf => sha256};
-suite_definition(?TLS_DHE_RSA_WITH_AES_256_GCM_SHA384) ->
+suite_bin_to_map(?TLS_DHE_RSA_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => dhe_rsa,
cipher => aes_256_gcm,
mac => aead,
prf => sha384};
-suite_definition(?TLS_DH_RSA_WITH_AES_128_GCM_SHA256) ->
+suite_bin_to_map(?TLS_DH_RSA_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => dh_rsa,
cipher => aes_128_gcm,
mac => aead,
prf => sha256};
-suite_definition(?TLS_DH_RSA_WITH_AES_256_GCM_SHA384) ->
+suite_bin_to_map(?TLS_DH_RSA_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => dh_rsa,
cipher => aes_256_gcm,
mac => aead,
prf => sha384};
-suite_definition(?TLS_DHE_DSS_WITH_AES_128_GCM_SHA256) ->
+suite_bin_to_map(?TLS_DHE_DSS_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => dhe_dss,
cipher => aes_128_gcm,
mac => aead,
prf => sha256};
-suite_definition(?TLS_DHE_DSS_WITH_AES_256_GCM_SHA384) ->
+suite_bin_to_map(?TLS_DHE_DSS_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => dhe_dss,
cipher => aes_256_gcm,
mac => aead,
prf => sha384};
-suite_definition(?TLS_DH_DSS_WITH_AES_128_GCM_SHA256) ->
+suite_bin_to_map(?TLS_DH_DSS_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => dh_dss,
cipher => aes_128_gcm,
mac => null,
prf => sha256};
-suite_definition(?TLS_DH_DSS_WITH_AES_256_GCM_SHA384) ->
+suite_bin_to_map(?TLS_DH_DSS_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => dh_dss,
cipher => aes_256_gcm,
mac => aead,
prf => sha384};
-suite_definition(?TLS_DH_anon_WITH_AES_128_GCM_SHA256) ->
+suite_bin_to_map(?TLS_DH_anon_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => dh_anon,
cipher => aes_128_gcm,
mac => aead,
prf => sha256};
-suite_definition(?TLS_DH_anon_WITH_AES_256_GCM_SHA384) ->
+suite_bin_to_map(?TLS_DH_anon_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => dh_anon,
cipher => aes_256_gcm,
mac => aead,
prf => sha384};
%% RFC 5289 ECC AES-GCM Cipher Suites
-suite_definition(?TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256) ->
+suite_bin_to_map(?TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => ecdhe_ecdsa,
cipher => aes_128_gcm,
mac => aead,
prf => sha256};
-suite_definition(?TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384) ->
+suite_bin_to_map(?TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => ecdhe_ecdsa,
cipher => aes_256_gcm,
mac => aead,
prf => sha384};
-suite_definition(?TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256) ->
+suite_bin_to_map(?TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => ecdh_ecdsa,
cipher => aes_128_gcm,
mac => aead,
prf => sha256};
-suite_definition(?TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384) ->
+suite_bin_to_map(?TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => ecdh_ecdsa,
cipher => aes_256_gcm,
mac => aead,
prf => sha384};
-suite_definition(?TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) ->
+suite_bin_to_map(?TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => ecdhe_rsa,
cipher => aes_128_gcm,
mac => aead,
prf => sha256};
-suite_definition(?TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) ->
+suite_bin_to_map(?TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => ecdhe_rsa,
cipher => aes_256_gcm,
mac => aead,
prf => sha384};
-suite_definition(?TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256) ->
+suite_bin_to_map(?TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256) ->
#{key_exchange => ecdh_rsa,
cipher => aes_128_gcm,
mac => aead,
prf => sha256};
-suite_definition(?TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384) ->
+suite_bin_to_map(?TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384) ->
#{key_exchange => ecdh_rsa,
cipher => aes_256_gcm,
mac => aead,
prf => sha384};
-suite_definition(?TLS_PSK_WITH_AES_128_CCM) ->
+suite_bin_to_map(?TLS_PSK_WITH_AES_128_CCM) ->
#{key_exchange => psk,
cipher => aes_128_ccm,
mac => aead,
prf => sha256};
-suite_definition(?TLS_PSK_WITH_AES_256_CCM) ->
+suite_bin_to_map(?TLS_PSK_WITH_AES_256_CCM) ->
#{key_exchange => psk,
cipher => aes_256_ccm,
mac => aead,
prf => sha256};
-suite_definition(?TLS_DHE_PSK_WITH_AES_128_CCM) ->
+suite_bin_to_map(?TLS_DHE_PSK_WITH_AES_128_CCM) ->
#{key_exchange => dhe_psk,
cipher => aes_128_ccm,
mac => aead,
prf => sha256};
-suite_definition(?TLS_DHE_PSK_WITH_AES_256_CCM) ->
+suite_bin_to_map(?TLS_DHE_PSK_WITH_AES_256_CCM) ->
#{key_exchange => dhe_psk,
cipher => aes_256_ccm,
mac => aead,
prf => sha256};
-suite_definition(?TLS_PSK_WITH_AES_128_CCM_8) ->
+suite_bin_to_map(?TLS_PSK_WITH_AES_128_CCM_8) ->
#{key_exchange => psk,
cipher => aes_128_ccm_8,
mac => aead,
prf => sha256};
-suite_definition(?TLS_PSK_WITH_AES_256_CCM_8) ->
+suite_bin_to_map(?TLS_PSK_WITH_AES_256_CCM_8) ->
#{key_exchange => psk,
cipher => aes_256_ccm_8,
mac => aead,
prf => sha256};
-suite_definition(?TLS_PSK_DHE_WITH_AES_128_CCM_8) ->
+suite_bin_to_map(?TLS_PSK_DHE_WITH_AES_128_CCM_8) ->
#{key_exchange => dhe_psk,
cipher => aes_128_ccm_8,
mac => aead,
prf => sha256};
-suite_definition(?TLS_PSK_DHE_WITH_AES_256_CCM_8) ->
+suite_bin_to_map(?TLS_PSK_DHE_WITH_AES_256_CCM_8) ->
#{key_exchange => dhe_psk,
cipher => aes_256_ccm_8,
mac => aead,
prf => sha256};
-suite_definition(#{key_exchange := psk_dhe,
+suite_bin_to_map(#{key_exchange := psk_dhe,
cipher := aes_256_ccm_8,
mac := aead,
prf := sha256}) ->
?TLS_PSK_DHE_WITH_AES_256_CCM_8;
% draft-agl-tls-chacha20poly1305-04 Chacha20/Poly1305 Suites
-suite_definition(?TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256) ->
+suite_bin_to_map(?TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256) ->
#{key_exchange => ecdhe_rsa,
cipher => chacha20_poly1305,
mac => aead,
prf => sha256};
-suite_definition(?TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256) ->
+suite_bin_to_map(?TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256) ->
#{key_exchange => ecdhe_ecdsa,
cipher => chacha20_poly1305,
mac => aead,
prf => sha256};
-suite_definition(?TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256) ->
+suite_bin_to_map(?TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256) ->
#{key_exchange => dhe_rsa,
cipher => chacha20_poly1305,
mac => aead,
prf => sha256};
%% TLS 1.3 Cipher Suites RFC8446
-suite_definition(?TLS_AES_128_GCM_SHA256) ->
+suite_bin_to_map(?TLS_AES_128_GCM_SHA256) ->
#{key_exchange => any,
cipher => aes_128_gcm,
mac => aead,
prf => sha256};
-suite_definition(?TLS_AES_256_GCM_SHA384) ->
+suite_bin_to_map(?TLS_AES_256_GCM_SHA384) ->
#{key_exchange => any,
cipher => aes_256_gcm,
mac => aead,
prf => sha384};
-suite_definition(?TLS_CHACHA20_POLY1305_SHA256) ->
+suite_bin_to_map(?TLS_CHACHA20_POLY1305_SHA256) ->
#{key_exchange => any,
cipher => chacha20_poly1305,
mac => aead,
prf => sha256}.
-%% suite_definition(?TLS_AES_128_CCM_SHA256) ->
+%% suite_bin_to_map(?TLS_AES_128_CCM_SHA256) ->
%% #{key_exchange => any,
%% cipher => aes_128_ccm,
-%% mac => aead,
+%% mac => aead
%% prf => sha256};
-%% suite_definition(?TLS_AES_128_CCM_8_SHA256) ->
+%% suite_bin_to_map(?TLS_AES_128_CCM_8_SHA256) ->
%% #{key_exchange => any,
%% cipher => aes_128_ccm_8,
%% mac => aead,
%% prf => sha256}.
%%--------------------------------------------------------------------
--spec erl_suite_definition(cipher_suite() | internal_erl_cipher_suite()) -> old_erl_cipher_suite().
+-spec suite_legacy(cipher_suite() | internal_erl_cipher_suite()) -> old_erl_cipher_suite().
%%
%% Description: Return erlang cipher suite definition. Filters last value
%% for now (compatibility reasons).
%%--------------------------------------------------------------------
-erl_suite_definition(Bin) when is_binary(Bin) ->
- erl_suite_definition(suite_definition(Bin));
-erl_suite_definition(#{key_exchange := KeyExchange, cipher := Cipher,
+suite_legacy(Bin) when is_binary(Bin) ->
+ suite_legacy(suite_bin_to_map(Bin));
+suite_legacy(#{key_exchange := KeyExchange, cipher := Cipher,
mac := Hash, prf := Prf}) ->
case Prf of
default_prf ->
@@ -899,1093 +985,896 @@ erl_suite_definition(#{key_exchange := KeyExchange, cipher := Cipher,
end.
%%--------------------------------------------------------------------
--spec suite(internal_erl_cipher_suite()) -> cipher_suite().
+-spec suite_map_to_bin(internal_erl_cipher_suite()) -> cipher_suite().
%%
%% Description: Return TLS cipher suite definition.
%%--------------------------------------------------------------------
%% TLS v1.1 suites
-suite(#{key_exchange := rsa,
+suite_map_to_bin(#{key_exchange := rsa,
cipher := rc4_128,
mac := md5}) ->
?TLS_RSA_WITH_RC4_128_MD5;
-suite(#{key_exchange := rsa,
+suite_map_to_bin(#{key_exchange := rsa,
cipher := rc4_128,
mac := sha}) ->
?TLS_RSA_WITH_RC4_128_SHA;
-suite(#{key_exchange := rsa,
+suite_map_to_bin(#{key_exchange := rsa,
cipher := des_cbc,
mac := sha}) ->
?TLS_RSA_WITH_DES_CBC_SHA;
-suite(#{key_exchange := rsa,
+suite_map_to_bin(#{key_exchange := rsa,
cipher :='3des_ede_cbc',
mac := sha}) ->
?TLS_RSA_WITH_3DES_EDE_CBC_SHA;
-suite(#{key_exchange := dhe_dss,
+suite_map_to_bin(#{key_exchange := dhe_dss,
cipher:= des_cbc,
mac := sha}) ->
?TLS_DHE_DSS_WITH_DES_CBC_SHA;
-suite(#{key_exchange := dhe_dss,
+suite_map_to_bin(#{key_exchange := dhe_dss,
cipher:= '3des_ede_cbc',
mac := sha}) ->
?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA;
-suite(#{key_exchange := dhe_rsa,
+suite_map_to_bin(#{key_exchange := dhe_rsa,
cipher:= des_cbc,
mac := sha}) ->
?TLS_DHE_RSA_WITH_DES_CBC_SHA;
-suite(#{key_exchange := dhe_rsa,
+suite_map_to_bin(#{key_exchange := dhe_rsa,
cipher:= '3des_ede_cbc',
mac := sha}) ->
?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA;
-suite(#{key_exchange := dh_anon,
+suite_map_to_bin(#{key_exchange := dh_anon,
cipher:= rc4_128,
mac := md5}) ->
?TLS_DH_anon_WITH_RC4_128_MD5;
-suite(#{key_exchange := dh_anon,
+suite_map_to_bin(#{key_exchange := dh_anon,
cipher:= des_cbc,
mac := sha}) ->
?TLS_DH_anon_WITH_DES_CBC_SHA;
-suite(#{key_exchange := dh_anon,
+suite_map_to_bin(#{key_exchange := dh_anon,
cipher:= '3des_ede_cbc',
mac := sha}) ->
?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA;
%%% TSL V1.1 AES suites
-suite(#{key_exchange := rsa,
+suite_map_to_bin(#{key_exchange := rsa,
cipher := aes_128_cbc,
mac := sha}) ->
?TLS_RSA_WITH_AES_128_CBC_SHA;
-suite(#{key_exchange := dhe_dss,
+suite_map_to_bin(#{key_exchange := dhe_dss,
cipher := aes_128_cbc,
mac := sha}) ->
?TLS_DHE_DSS_WITH_AES_128_CBC_SHA;
-suite(#{key_exchange := dhe_rsa,
+suite_map_to_bin(#{key_exchange := dhe_rsa,
cipher := aes_128_cbc,
mac := sha}) ->
?TLS_DHE_RSA_WITH_AES_128_CBC_SHA;
-suite(#{key_exchange := dh_anon,
+suite_map_to_bin(#{key_exchange := dh_anon,
cipher := aes_128_cbc,
mac := sha}) ->
?TLS_DH_anon_WITH_AES_128_CBC_SHA;
-suite(#{key_exchange := rsa,
+suite_map_to_bin(#{key_exchange := rsa,
cipher := aes_256_cbc,
mac := sha}) ->
?TLS_RSA_WITH_AES_256_CBC_SHA;
-suite(#{key_exchange := dhe_dss,
+suite_map_to_bin(#{key_exchange := dhe_dss,
cipher := aes_256_cbc,
mac := sha}) ->
?TLS_DHE_DSS_WITH_AES_256_CBC_SHA;
-suite(#{key_exchange := dhe_rsa,
+suite_map_to_bin(#{key_exchange := dhe_rsa,
cipher := aes_256_cbc,
mac := sha}) ->
?TLS_DHE_RSA_WITH_AES_256_CBC_SHA;
-suite(#{key_exchange := dh_anon,
+suite_map_to_bin(#{key_exchange := dh_anon,
cipher := aes_256_cbc,
mac := sha}) ->
?TLS_DH_anon_WITH_AES_256_CBC_SHA;
%% TLS v1.2 suites
-suite(#{key_exchange := rsa,
+suite_map_to_bin(#{key_exchange := rsa,
cipher := aes_128_cbc,
mac := sha256}) ->
?TLS_RSA_WITH_AES_128_CBC_SHA256;
-suite(#{key_exchange := rsa,
+suite_map_to_bin(#{key_exchange := rsa,
cipher := aes_256_cbc,
mac := sha256}) ->
?TLS_RSA_WITH_AES_256_CBC_SHA256;
-suite(#{key_exchange := dhe_dss,
+suite_map_to_bin(#{key_exchange := dhe_dss,
cipher := aes_128_cbc,
mac := sha256}) ->
?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256;
-suite(#{key_exchange := dhe_rsa,
+suite_map_to_bin(#{key_exchange := dhe_rsa,
cipher := aes_128_cbc,
mac := sha256}) ->
?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256;
-suite(#{key_exchange := dhe_dss,
+suite_map_to_bin(#{key_exchange := dhe_dss,
cipher := aes_256_cbc,
mac := sha256}) ->
?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256;
-suite(#{key_exchange := dhe_rsa,
+suite_map_to_bin(#{key_exchange := dhe_rsa,
cipher := aes_256_cbc,
mac := sha256}) ->
?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256;
-suite(#{key_exchange := dh_anon,
+suite_map_to_bin(#{key_exchange := dh_anon,
cipher := aes_128_cbc,
mac := sha256}) ->
?TLS_DH_anon_WITH_AES_128_CBC_SHA256;
-suite(#{key_exchange := dh_anon,
+suite_map_to_bin(#{key_exchange := dh_anon,
cipher := aes_256_cbc,
mac := sha256}) ->
?TLS_DH_anon_WITH_AES_256_CBC_SHA256;
%%% PSK Cipher Suites RFC 4279
-suite(#{key_exchange := psk,
+suite_map_to_bin(#{key_exchange := psk,
cipher := rc4_128,
mac := sha}) ->
?TLS_PSK_WITH_RC4_128_SHA;
-suite(#{key_exchange := psk,
+suite_map_to_bin(#{key_exchange := psk,
cipher := '3des_ede_cbc',
mac := sha}) ->
?TLS_PSK_WITH_3DES_EDE_CBC_SHA;
-suite(#{key_exchange := psk,
+suite_map_to_bin(#{key_exchange := psk,
cipher := aes_128_cbc,
mac := sha}) ->
?TLS_PSK_WITH_AES_128_CBC_SHA;
-suite(#{key_exchange := psk,
+suite_map_to_bin(#{key_exchange := psk,
cipher := aes_256_cbc,
mac := sha}) ->
?TLS_PSK_WITH_AES_256_CBC_SHA;
-suite(#{key_exchange := dhe_psk,
+suite_map_to_bin(#{key_exchange := dhe_psk,
cipher := rc4_128,
mac := sha}) ->
?TLS_DHE_PSK_WITH_RC4_128_SHA;
-suite(#{key_exchange := dhe_psk,
+suite_map_to_bin(#{key_exchange := dhe_psk,
cipher := '3des_ede_cbc',
mac := sha}) ->
?TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA;
-suite(#{key_exchange := dhe_psk,
+suite_map_to_bin(#{key_exchange := dhe_psk,
cipher := aes_128_cbc,
mac := sha}) ->
?TLS_DHE_PSK_WITH_AES_128_CBC_SHA;
-suite(#{key_exchange := dhe_psk,
+suite_map_to_bin(#{key_exchange := dhe_psk,
cipher := aes_256_cbc,
mac := sha}) ->
?TLS_DHE_PSK_WITH_AES_256_CBC_SHA;
-suite(#{key_exchange := rsa_psk,
+suite_map_to_bin(#{key_exchange := rsa_psk,
cipher := rc4_128,
mac := sha}) ->
?TLS_RSA_PSK_WITH_RC4_128_SHA;
-suite(#{key_exchange := rsa_psk,
+suite_map_to_bin(#{key_exchange := rsa_psk,
cipher := '3des_ede_cbc',
mac := sha}) ->
?TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA;
-suite(#{key_exchange := rsa_psk,
+suite_map_to_bin(#{key_exchange := rsa_psk,
cipher := aes_128_cbc,
mac := sha}) ->
?TLS_RSA_PSK_WITH_AES_128_CBC_SHA;
-suite(#{key_exchange := rsa_psk,
+suite_map_to_bin(#{key_exchange := rsa_psk,
cipher := aes_256_cbc,
mac := sha}) ->
?TLS_RSA_PSK_WITH_AES_256_CBC_SHA;
%%% PSK NULL Cipher Suites RFC 4785
-suite(#{key_exchange := psk,
+suite_map_to_bin(#{key_exchange := psk,
cipher := null,
mac := sha}) ->
?TLS_PSK_WITH_NULL_SHA;
-suite(#{key_exchange := dhe_psk,
+suite_map_to_bin(#{key_exchange := dhe_psk,
cipher := null,
mac := sha}) ->
?TLS_DHE_PSK_WITH_NULL_SHA;
-suite(#{key_exchange := rsa_psk,
+suite_map_to_bin(#{key_exchange := rsa_psk,
cipher := null,
mac := sha}) ->
?TLS_RSA_PSK_WITH_NULL_SHA;
%%% TLS 1.2 PSK Cipher Suites RFC 5487
-suite(#{key_exchange := psk,
+suite_map_to_bin(#{key_exchange := psk,
cipher := aes_128_gcm,
mac := aead,
prf := sha256}) ->
?TLS_PSK_WITH_AES_128_GCM_SHA256;
-suite(#{key_exchange := psk,
+suite_map_to_bin(#{key_exchange := psk,
cipher := aes_256_gcm,
mac := aead,
prf := sha384}) ->
?TLS_PSK_WITH_AES_256_GCM_SHA384;
-suite(#{key_exchange := dhe_psk,
+suite_map_to_bin(#{key_exchange := dhe_psk,
cipher := aes_128_gcm,
mac := aead,
prf := sha256}) ->
?TLS_DHE_PSK_WITH_AES_128_GCM_SHA256;
-suite(#{key_exchange := dhe_psk,
+suite_map_to_bin(#{key_exchange := dhe_psk,
cipher := aes_256_gcm,
mac := aead,
prf := sha384}) ->
?TLS_DHE_PSK_WITH_AES_256_GCM_SHA384;
-suite(#{key_exchange := rsa_psk,
+suite_map_to_bin(#{key_exchange := rsa_psk,
cipher := aes_128_gcm,
mac := aead,
prf := sha256}) ->
?TLS_RSA_PSK_WITH_AES_128_GCM_SHA256;
-suite(#{key_exchange := rsa_psk,
+suite_map_to_bin(#{key_exchange := rsa_psk,
cipher := aes_256_gcm,
mac := aead,
prf := sha384}) ->
?TLS_RSA_PSK_WITH_AES_256_GCM_SHA384;
-suite(#{key_exchange := psk,
+suite_map_to_bin(#{key_exchange := psk,
cipher := aes_128_cbc,
mac := sha256}) ->
?TLS_PSK_WITH_AES_128_CBC_SHA256;
-suite(#{key_exchange := psk,
+suite_map_to_bin(#{key_exchange := psk,
cipher := aes_256_cbc,
mac := sha384}) ->
?TLS_PSK_WITH_AES_256_CBC_SHA384;
-suite(#{key_exchange := dhe_psk,
+suite_map_to_bin(#{key_exchange := dhe_psk,
cipher := aes_128_cbc,
mac := sha256}) ->
?TLS_DHE_PSK_WITH_AES_128_CBC_SHA256;
-suite(#{key_exchange := dhe_psk,
+suite_map_to_bin(#{key_exchange := dhe_psk,
cipher := aes_256_cbc,
mac := sha384}) ->
?TLS_DHE_PSK_WITH_AES_256_CBC_SHA384;
-suite(#{key_exchange := rsa_psk,
+suite_map_to_bin(#{key_exchange := rsa_psk,
cipher := aes_128_cbc,
mac := sha256}) ->
?TLS_RSA_PSK_WITH_AES_128_CBC_SHA256;
-suite(#{key_exchange := rsa_psk,
+suite_map_to_bin(#{key_exchange := rsa_psk,
cipher := aes_256_cbc,
mac := sha384}) ->
?TLS_RSA_PSK_WITH_AES_256_CBC_SHA384;
-suite(#{key_exchange := psk,
+suite_map_to_bin(#{key_exchange := psk,
cipher := null,
mac := sha256}) ->
?TLS_PSK_WITH_NULL_SHA256;
-suite(#{key_exchange := psk,
+suite_map_to_bin(#{key_exchange := psk,
cipher := null,
mac := sha384}) ->
?TLS_PSK_WITH_NULL_SHA384;
-suite(#{key_exchange := dhe_psk,
+suite_map_to_bin(#{key_exchange := dhe_psk,
cipher := null,
mac := sha256}) ->
?TLS_DHE_PSK_WITH_NULL_SHA256;
-suite(#{key_exchange := dhe_psk,
+suite_map_to_bin(#{key_exchange := dhe_psk,
cipher := null,
mac := sha384}) ->
?TLS_DHE_PSK_WITH_NULL_SHA384;
-suite(#{key_exchange := rsa_psk,
+suite_map_to_bin(#{key_exchange := rsa_psk,
cipher := null,
mac := sha256}) ->
?TLS_RSA_PSK_WITH_NULL_SHA256;
-suite(#{key_exchange := rsa_psk,
+suite_map_to_bin(#{key_exchange := rsa_psk,
cipher := null,
mac := sha384}) ->
?TLS_RSA_PSK_WITH_NULL_SHA384;
%%% ECDHE PSK Cipher Suites RFC 5489
-suite(#{key_exchange := ecdhe_psk,
+suite_map_to_bin(#{key_exchange := ecdhe_psk,
cipher := rc4_128,
mac := sha}) ->
?TLS_ECDHE_PSK_WITH_RC4_128_SHA;
-suite(#{key_exchange := ecdhe_psk,
+suite_map_to_bin(#{key_exchange := ecdhe_psk,
cipher :='3des_ede_cbc',
mac := sha}) ->
?TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA;
-suite(#{key_exchange := ecdhe_psk,
+suite_map_to_bin(#{key_exchange := ecdhe_psk,
cipher := aes_128_cbc,
mac := sha}) ->
?TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA;
-suite(#{key_exchange := ecdhe_psk,
+suite_map_to_bin(#{key_exchange := ecdhe_psk,
cipher := aes_256_cbc,
mac := sha}) ->
?TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA;
-suite(#{key_exchange := ecdhe_psk,
+suite_map_to_bin(#{key_exchange := ecdhe_psk,
cipher := aes_128_cbc,
mac := sha256}) ->
?TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256;
-suite(#{key_exchange := ecdhe_psk,
+suite_map_to_bin(#{key_exchange := ecdhe_psk,
cipher := aes_256_cbc,
mac := sha384}) ->
?TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384;
-suite(#{key_exchange := ecdhe_psk,
+suite_map_to_bin(#{key_exchange := ecdhe_psk,
cipher := null,
mac := sha256}) ->
?TLS_ECDHE_PSK_WITH_NULL_SHA256;
-suite(#{key_exchange := ecdhe_psk,
+suite_map_to_bin(#{key_exchange := ecdhe_psk,
cipher := null,
mac := sha384}) ->
?TLS_ECDHE_PSK_WITH_NULL_SHA384;
%%% ECDHE_PSK with AES-GCM and AES-CCM Cipher Suites, draft-ietf-tls-ecdhe-psk-aead-05
-suite(#{key_exchange := ecdhe_psk,
+suite_map_to_bin(#{key_exchange := ecdhe_psk,
cipher := aes_128_gcm,
mac := null,
prf := sha256}) ->
?TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256;
-suite(#{key_exchange := ecdhe_psk,
+suite_map_to_bin(#{key_exchange := ecdhe_psk,
cipher := aes_256_gcm,
mac := null,
prf := sha384}) ->
?TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384;
-suite(#{key_exchange := ecdhe_psk,
+suite_map_to_bin(#{key_exchange := ecdhe_psk,
cipher := aes_128_ccm_8,
mac := null,
prf := sha256}) ->
?TLS_ECDHE_PSK_WITH_AES_128_CCM_8_SHA256;
-suite(#{key_exchange := ecdhe_psk,
+suite_map_to_bin(#{key_exchange := ecdhe_psk,
cipher := aes_128_ccm,
mac := null,
prf := sha256}) ->
?TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256;
%%% SRP Cipher Suites RFC 5054
-suite(#{key_exchange := srp_anon,
+suite_map_to_bin(#{key_exchange := srp_anon,
cipher := '3des_ede_cbc',
mac := sha}) ->
?TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA;
-suite(#{key_exchange := srp_rsa,
+suite_map_to_bin(#{key_exchange := srp_rsa,
cipher := '3des_ede_cbc',
mac := sha}) ->
?TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA;
-suite(#{key_exchange := srp_dss,
+suite_map_to_bin(#{key_exchange := srp_dss,
cipher := '3des_ede_cbc',
mac := sha}) ->
?TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA;
-suite(#{key_exchange := srp_anon,
+suite_map_to_bin(#{key_exchange := srp_anon,
cipher := aes_128_cbc,
mac := sha}) ->
?TLS_SRP_SHA_WITH_AES_128_CBC_SHA;
-suite(#{key_exchange := srp_rsa,
+suite_map_to_bin(#{key_exchange := srp_rsa,
cipher := aes_128_cbc,
mac := sha}) ->
?TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA;
-suite(#{key_exchange := srp_dss,
+suite_map_to_bin(#{key_exchange := srp_dss,
cipher := aes_128_cbc,
mac := sha}) ->
?TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA;
-suite(#{key_exchange := srp_anon,
+suite_map_to_bin(#{key_exchange := srp_anon,
cipher := aes_256_cbc,
mac := sha}) ->
?TLS_SRP_SHA_WITH_AES_256_CBC_SHA;
-suite(#{key_exchange := srp_rsa,
+suite_map_to_bin(#{key_exchange := srp_rsa,
cipher := aes_256_cbc,
mac := sha}) ->
?TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA;
-suite(#{key_exchange := srp_dss,
+suite_map_to_bin(#{key_exchange := srp_dss,
cipher := aes_256_cbc,
mac := sha}) ->
?TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA;
%%% RFC 4492 EC TLS suites
-suite(#{key_exchange := ecdh_ecdsa,
+suite_map_to_bin(#{key_exchange := ecdh_ecdsa,
cipher := null,
mac := sha}) ->
?TLS_ECDH_ECDSA_WITH_NULL_SHA;
-suite(#{key_exchange := ecdh_ecdsa,
+suite_map_to_bin(#{key_exchange := ecdh_ecdsa,
cipher := rc4_128,
mac := sha}) ->
?TLS_ECDH_ECDSA_WITH_RC4_128_SHA;
-suite(#{key_exchange := ecdh_ecdsa,
+suite_map_to_bin(#{key_exchange := ecdh_ecdsa,
cipher := '3des_ede_cbc',
mac := sha}) ->
?TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA;
-suite(#{key_exchange := ecdh_ecdsa,
+suite_map_to_bin(#{key_exchange := ecdh_ecdsa,
cipher := aes_128_cbc,
mac := sha}) ->
?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA;
-suite(#{key_exchange := ecdh_ecdsa,
+suite_map_to_bin(#{key_exchange := ecdh_ecdsa,
cipher := aes_256_cbc,
mac := sha}) ->
?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA;
-suite(#{key_exchange := ecdhe_ecdsa,
+suite_map_to_bin(#{key_exchange := ecdhe_ecdsa,
cipher := null,
mac := sha}) ->
?TLS_ECDHE_ECDSA_WITH_NULL_SHA;
-suite(#{key_exchange := ecdhe_ecdsa,
+suite_map_to_bin(#{key_exchange := ecdhe_ecdsa,
cipher := rc4_128,
mac := sha}) ->
?TLS_ECDHE_ECDSA_WITH_RC4_128_SHA;
-suite(#{key_exchange := ecdhe_ecdsa,
+suite_map_to_bin(#{key_exchange := ecdhe_ecdsa,
cipher := '3des_ede_cbc',
mac := sha}) ->
?TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA;
-suite(#{key_exchange := ecdhe_ecdsa,
+suite_map_to_bin(#{key_exchange := ecdhe_ecdsa,
cipher := aes_128_cbc,
mac := sha}) ->
?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA;
-suite(#{key_exchange := ecdhe_ecdsa,
+suite_map_to_bin(#{key_exchange := ecdhe_ecdsa,
cipher := aes_256_cbc,
mac := sha}) ->
?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA;
-suite(#{key_exchange := ecdh_rsa,
+suite_map_to_bin(#{key_exchange := ecdh_rsa,
cipher := null,
mac := sha}) ->
?TLS_ECDH_RSA_WITH_NULL_SHA;
-suite(#{key_exchange := ecdh_rsa,
+suite_map_to_bin(#{key_exchange := ecdh_rsa,
cipher := rc4_128,
mac := sha}) ->
?TLS_ECDH_RSA_WITH_RC4_128_SHA;
-suite(#{key_exchange := ecdh_rsa,
+suite_map_to_bin(#{key_exchange := ecdh_rsa,
cipher := '3des_ede_cbc', mac := sha}) ->
?TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA;
-suite(#{key_exchange := ecdh_rsa,
+suite_map_to_bin(#{key_exchange := ecdh_rsa,
cipher := aes_128_cbc,
mac := sha}) ->
?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA;
-suite(#{key_exchange := ecdh_rsa,
+suite_map_to_bin(#{key_exchange := ecdh_rsa,
cipher := aes_256_cbc,
mac := sha}) ->
?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA;
-suite(#{key_exchange := ecdhe_rsa,
+suite_map_to_bin(#{key_exchange := ecdhe_rsa,
cipher := null,
mac := sha}) ->
?TLS_ECDHE_RSA_WITH_NULL_SHA;
-suite(#{key_exchange := ecdhe_rsa,
+suite_map_to_bin(#{key_exchange := ecdhe_rsa,
cipher := rc4_128,
mac := sha}) ->
?TLS_ECDHE_RSA_WITH_RC4_128_SHA;
-suite(#{key_exchange := ecdhe_rsa,
+suite_map_to_bin(#{key_exchange := ecdhe_rsa,
cipher := '3des_ede_cbc',
mac := sha}) ->
?TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA;
-suite(#{key_exchange := ecdhe_rsa,
+suite_map_to_bin(#{key_exchange := ecdhe_rsa,
cipher := aes_128_cbc,
mac := sha}) ->
?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA;
-suite(#{key_exchange := ecdhe_rsa,
+suite_map_to_bin(#{key_exchange := ecdhe_rsa,
cipher := aes_256_cbc,
mac := sha}) ->
?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA;
-suite(#{key_exchange := ecdh_anon,
+suite_map_to_bin(#{key_exchange := ecdh_anon,
cipher := null,
mac := sha}) ->
?TLS_ECDH_anon_WITH_NULL_SHA;
-suite(#{key_exchange := ecdh_anon,
+suite_map_to_bin(#{key_exchange := ecdh_anon,
cipher := rc4_128,
mac := sha}) ->
?TLS_ECDH_anon_WITH_RC4_128_SHA;
-suite(#{key_exchange := ecdh_anon,
+suite_map_to_bin(#{key_exchange := ecdh_anon,
cipher := '3des_ede_cbc',
mac := sha}) ->
?TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA;
-suite(#{key_exchange := ecdh_anon,
+suite_map_to_bin(#{key_exchange := ecdh_anon,
cipher := aes_128_cbc,
mac := sha}) ->
?TLS_ECDH_anon_WITH_AES_128_CBC_SHA;
-suite(#{key_exchange := ecdh_anon,
+suite_map_to_bin(#{key_exchange := ecdh_anon,
cipher := aes_256_cbc,
mac := sha}) ->
?TLS_ECDH_anon_WITH_AES_256_CBC_SHA;
%%% RFC 5289 EC TLS suites
-suite(#{key_exchange := ecdhe_ecdsa,
+suite_map_to_bin(#{key_exchange := ecdhe_ecdsa,
cipher := aes_128_cbc,
mac:= sha256,
prf := sha256}) ->
?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256;
-suite(#{key_exchange := ecdhe_ecdsa,
+suite_map_to_bin(#{key_exchange := ecdhe_ecdsa,
cipher := aes_256_cbc,
mac := sha384,
prf := sha384}) ->
?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384;
-suite(#{key_exchange := ecdh_ecdsa,
+suite_map_to_bin(#{key_exchange := ecdh_ecdsa,
cipher := aes_128_cbc,
mac := sha256,
prf := sha256}) ->
?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256;
-suite(#{key_exchange := ecdh_ecdsa,
+suite_map_to_bin(#{key_exchange := ecdh_ecdsa,
cipher := aes_256_cbc,
mac := sha384,
prf := sha384}) ->
?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384;
-suite(#{key_exchange := ecdhe_rsa,
+suite_map_to_bin(#{key_exchange := ecdhe_rsa,
cipher := aes_128_cbc,
mac := sha256,
prf := sha256}) ->
?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256;
-suite(#{key_exchange := ecdhe_rsa,
+suite_map_to_bin(#{key_exchange := ecdhe_rsa,
cipher := aes_256_cbc,
mac := sha384,
prf := sha384}) ->
?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384;
-suite(#{key_exchange := ecdh_rsa,
+suite_map_to_bin(#{key_exchange := ecdh_rsa,
cipher := aes_128_cbc,
mac := sha256,
prf := sha256}) ->
?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256;
-suite(#{key_exchange := ecdh_rsa,
+suite_map_to_bin(#{key_exchange := ecdh_rsa,
cipher := aes_256_cbc,
mac := sha384,
prf := sha384}) ->
?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384;
%% RFC 5288 AES-GCM Cipher Suites
-suite(#{key_exchange := rsa,
+suite_map_to_bin(#{key_exchange := rsa,
cipher := aes_128_gcm,
mac := aead,
prf := sha256}) ->
?TLS_RSA_WITH_AES_128_GCM_SHA256;
-suite(#{key_exchange := rsa,
+suite_map_to_bin(#{key_exchange := rsa,
cipher := aes_256_gcm,
mac := aead,
prf := sha384}) ->
?TLS_RSA_WITH_AES_256_GCM_SHA384;
-suite(#{key_exchange := dhe_rsa,
+suite_map_to_bin(#{key_exchange := dhe_rsa,
cipher := aes_128_gcm,
mac := aead,
prf := sha256}) ->
?TLS_DHE_RSA_WITH_AES_128_GCM_SHA256;
-suite(#{key_exchange := dhe_rsa,
+suite_map_to_bin(#{key_exchange := dhe_rsa,
cipher := aes_256_gcm,
mac := aead,
prf := sha384}) ->
?TLS_DHE_RSA_WITH_AES_256_GCM_SHA384;
-suite(#{key_exchange := dh_rsa,
+suite_map_to_bin(#{key_exchange := dh_rsa,
cipher := aes_128_gcm,
mac := aead,
prf := sha256}) ->
?TLS_DH_RSA_WITH_AES_128_GCM_SHA256;
-suite(#{key_exchange := dh_rsa,
+suite_map_to_bin(#{key_exchange := dh_rsa,
cipher := aes_256_gcm,
mac := aead,
prf := sha384}) ->
?TLS_DH_RSA_WITH_AES_256_GCM_SHA384;
-suite(#{key_exchange := dhe_dss,
+suite_map_to_bin(#{key_exchange := dhe_dss,
cipher := aes_128_gcm,
mac := aead,
prf := sha256}) ->
?TLS_DHE_DSS_WITH_AES_128_GCM_SHA256;
-suite(#{key_exchange := dhe_dss,
+suite_map_to_bin(#{key_exchange := dhe_dss,
cipher := aes_256_gcm,
mac := aead,
prf := sha384}) ->
?TLS_DHE_DSS_WITH_AES_256_GCM_SHA384;
-suite(#{key_exchange := dh_dss,
+suite_map_to_bin(#{key_exchange := dh_dss,
cipher := aes_128_gcm,
mac := aead,
prf := sha256}) ->
?TLS_DH_DSS_WITH_AES_128_GCM_SHA256;
-suite(#{key_exchange := dh_dss,
+suite_map_to_bin(#{key_exchange := dh_dss,
cipher := aes_256_gcm,
mac := aead,
prf := sha384}) ->
?TLS_DH_DSS_WITH_AES_256_GCM_SHA384;
-suite(#{key_exchange := dh_anon,
+suite_map_to_bin(#{key_exchange := dh_anon,
cipher := aes_128_gcm,
mac := aead,
prf := sha256}) ->
?TLS_DH_anon_WITH_AES_128_GCM_SHA256;
-suite(#{key_exchange := dh_anon,
+suite_map_to_bin(#{key_exchange := dh_anon,
cipher := aes_256_gcm,
mac := aead,
prf := sha384}) ->
?TLS_DH_anon_WITH_AES_256_GCM_SHA384;
%% RFC 5289 ECC AES-GCM Cipher Suites
-suite(#{key_exchange := ecdhe_ecdsa,
+suite_map_to_bin(#{key_exchange := ecdhe_ecdsa,
cipher := aes_128_gcm,
mac := aead,
prf := sha256}) ->
?TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256;
-suite(#{key_exchange := ecdhe_ecdsa,
+suite_map_to_bin(#{key_exchange := ecdhe_ecdsa,
cipher := aes_256_gcm,
mac := aead,
prf := sha384}) ->
?TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384;
-suite(#{key_exchange := ecdh_ecdsa,
+suite_map_to_bin(#{key_exchange := ecdh_ecdsa,
cipher := aes_128_gcm,
mac := aead,
prf := sha256}) ->
?TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256;
-suite(#{key_exchange := ecdh_ecdsa,
+suite_map_to_bin(#{key_exchange := ecdh_ecdsa,
cipher := aes_256_gcm,
mac := aead,
prf := sha384}) ->
?TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384;
-suite(#{key_exchange := ecdhe_rsa,
+suite_map_to_bin(#{key_exchange := ecdhe_rsa,
cipher := aes_128_gcm,
mac := aead,
prf := sha256}) ->
?TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256;
-suite(#{key_exchange := ecdhe_rsa,
+suite_map_to_bin(#{key_exchange := ecdhe_rsa,
cipher := aes_256_gcm,
mac := aead,
prf := sha384}) ->
?TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384;
-suite(#{key_exchange := ecdh_rsa,
+suite_map_to_bin(#{key_exchange := ecdh_rsa,
cipher := aes_128_gcm,
mac := aead,
prf := sha256}) ->
?TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256;
-suite(#{key_exchange := ecdh_rsa,
+suite_map_to_bin(#{key_exchange := ecdh_rsa,
cipher := aes_256_gcm,
mac := aead,
prf := sha384}) ->
?TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384;
%% draft-agl-tls-chacha20poly1305-04 Chacha20/Poly1305 Suites
-suite(#{key_exchange := ecdhe_rsa,
+suite_map_to_bin(#{key_exchange := ecdhe_rsa,
cipher := chacha20_poly1305,
mac := aead,
prf := sha256}) ->
?TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256;
-suite(#{key_exchange := ecdhe_ecdsa,
+suite_map_to_bin(#{key_exchange := ecdhe_ecdsa,
cipher := chacha20_poly1305,
mac := aead,
prf := sha256}) ->
?TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256;
-suite(#{key_exchange := dhe_rsa,
+suite_map_to_bin(#{key_exchange := dhe_rsa,
cipher := chacha20_poly1305,
mac := aead,
prf := sha256}) ->
?TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256;
%% RFC 6655 - TLS-1.2 cipher suites
-suite(#{key_exchange := psk,
+suite_map_to_bin(#{key_exchange := psk,
cipher := aes_128_ccm,
mac := aead,
prf := sha256}) ->
?TLS_PSK_WITH_AES_128_CCM;
-suite(#{key_exchange := psk,
+suite_map_to_bin(#{key_exchange := psk,
cipher := aes_256_ccm,
mac := aead,
prf := sha256}) ->
?TLS_PSK_WITH_AES_256_CCM;
-suite(#{key_exchange := dhe_psk,
+suite_map_to_bin(#{key_exchange := dhe_psk,
cipher := aes_128_ccm,
mac := aead,
prf := sha256}) ->
?TLS_DHE_PSK_WITH_AES_128_CCM;
-suite(#{key_exchange := dhe_psk,
+suite_map_to_bin(#{key_exchange := dhe_psk,
cipher := aes_256_ccm,
mac := aead,
prf := sha256}) ->
?TLS_DHE_PSK_WITH_AES_256_CCM;
-suite(#{key_exchange := rsa,
+suite_map_to_bin(#{key_exchange := rsa,
cipher := aes_128_ccm,
mac := aead,
prf := sha256}) ->
?TLS_RSA_WITH_AES_128_CCM;
-suite(#{key_exchange := rsa,
+suite_map_to_bin(#{key_exchange := rsa,
cipher := aes_256_ccm,
mac := aead,
prf := sha256}) ->
?TLS_RSA_WITH_AES_256_CCM;
-suite(#{key_exchange := dhe_rsa,
+suite_map_to_bin(#{key_exchange := dhe_rsa,
cipher := aes_128_ccm,
mac := aead,
prf := sha256}) ->
?TLS_DHE_RSA_WITH_AES_128_CCM;
-suite(#{key_exchange := dhe_rsa,
+suite_map_to_bin(#{key_exchange := dhe_rsa,
cipher := aes_256_ccm,
mac := aead,
prf := sha256}) ->
?TLS_DHE_RSA_WITH_AES_256_CCM;
-suite(#{key_exchange := psk,
+suite_map_to_bin(#{key_exchange := psk,
cipher := aes_128_ccm_8,
mac := aead,
prf := sha256}) ->
?TLS_PSK_WITH_AES_128_CCM_8;
-suite(#{key_exchange := psk,
+suite_map_to_bin(#{key_exchange := psk,
cipher := aes_256_ccm_8,
mac := aead,
prf := sha256}) ->
?TLS_PSK_WITH_AES_256_CCM_8;
-suite(#{key_exchange := dhe_psk,
+suite_map_to_bin(#{key_exchange := dhe_psk,
cipher := aes_128_ccm_8,
mac := aead,
prf := sha256}) ->
?TLS_PSK_DHE_WITH_AES_128_CCM_8;
-suite(#{key_exchange := dhe_psk,
+suite_map_to_bin(#{key_exchange := dhe_psk,
cipher := aes_256_ccm_8,
mac := aead,
prf := sha256}) ->
?TLS_PSK_DHE_WITH_AES_256_CCM_8;
-suite(#{key_exchange := rsa,
+suite_map_to_bin(#{key_exchange := rsa,
cipher := aes_128_ccm_8,
mac := aead,
prf := sha256}) ->
?TLS_RSA_WITH_AES_128_CCM_8;
-suite(#{key_exchange := rsa,
+suite_map_to_bin(#{key_exchange := rsa,
cipher := aes_256_ccm_8,
mac := aead,
prf := sha256}) ->
?TLS_RSA_WITH_AES_256_CCM_8;
-suite(#{key_exchange := dhe_rsa,
+suite_map_to_bin(#{key_exchange := dhe_rsa,
cipher := aes_128_ccm_8,
mac := aead,
prf := sha256}) ->
?TLS_DHE_RSA_WITH_AES_128_CCM_8;
-suite(#{key_exchange := dhe_rsa,
+suite_map_to_bin(#{key_exchange := dhe_rsa,
cipher := aes_256_ccm_8,
mac := aead,
prf := sha256}) ->
?TLS_DHE_RSA_WITH_AES_256_CCM_8;
%% TLS 1.3 Cipher Suites RFC8446
-suite(#{key_exchange := any,
+suite_map_to_bin(#{key_exchange := any,
cipher := aes_128_gcm,
mac := aead,
prf := sha256}) ->
?TLS_AES_128_GCM_SHA256;
-suite(#{key_exchange := any,
+suite_map_to_bin(#{key_exchange := any,
cipher := aes_256_gcm,
mac := aead,
prf := sha384}) ->
?TLS_AES_256_GCM_SHA384;
-suite(#{key_exchange := any,
+suite_map_to_bin(#{key_exchange := any,
cipher := chacha20_poly1305,
mac := aead,
prf := sha256}) ->
?TLS_CHACHA20_POLY1305_SHA256.
-%% suite(#{key_exchange := any,
+%% suite_map_to_bin(#{key_exchange := any,
%% cipher := aes_128_ccm,
%% mac := aead,
%% prf := sha256}) ->
%% ?TLS_AES_128_CCM_SHA256;
-%% suite(#{key_exchange := any,
+%% suite_map_to_bin(#{key_exchange := any,
%% cipher := aes_128_ccm_8,
%% mac := aead,
%% prf := sha256}) ->
%% ?TLS_AES_128_CCM_8_SHA256.
-%%--------------------------------------------------------------------
--spec openssl_suite(openssl_cipher_suite()) -> cipher_suite().
-%%
-%% Description: Return TLS cipher suite definition.
-%%--------------------------------------------------------------------
-%% translate constants <-> openssl-strings
-openssl_suite("DHE-RSA-AES256-SHA256") ->
- ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256;
-openssl_suite("DHE-DSS-AES256-SHA256") ->
- ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256;
-openssl_suite("AES256-SHA256") ->
- ?TLS_RSA_WITH_AES_256_CBC_SHA256;
-openssl_suite("DHE-RSA-AES128-SHA256") ->
- ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256;
-openssl_suite("DHE-DSS-AES128-SHA256") ->
- ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256;
-openssl_suite("AES128-SHA256") ->
- ?TLS_RSA_WITH_AES_128_CBC_SHA256;
-openssl_suite("DHE-RSA-AES256-SHA") ->
- ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA;
-openssl_suite("DHE-DSS-AES256-SHA") ->
- ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA;
-openssl_suite("AES256-SHA") ->
- ?TLS_RSA_WITH_AES_256_CBC_SHA;
-openssl_suite("EDH-RSA-DES-CBC3-SHA") ->
- ?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA;
-openssl_suite("EDH-DSS-DES-CBC3-SHA") ->
- ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA;
-openssl_suite("DES-CBC3-SHA") ->
- ?TLS_RSA_WITH_3DES_EDE_CBC_SHA;
-openssl_suite("DHE-RSA-AES128-SHA") ->
- ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA;
-openssl_suite("DHE-DSS-AES128-SHA") ->
- ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA;
-openssl_suite("AES128-SHA") ->
- ?TLS_RSA_WITH_AES_128_CBC_SHA;
-openssl_suite("RC4-SHA") ->
- ?TLS_RSA_WITH_RC4_128_SHA;
-openssl_suite("RC4-MD5") ->
- ?TLS_RSA_WITH_RC4_128_MD5;
-openssl_suite("EDH-RSA-DES-CBC-SHA") ->
- ?TLS_DHE_RSA_WITH_DES_CBC_SHA;
-openssl_suite("DES-CBC-SHA") ->
- ?TLS_RSA_WITH_DES_CBC_SHA;
-%%% SRP Cipher Suites RFC 5054
-
-openssl_suite("SRP-DSS-AES-256-CBC-SHA") ->
- ?TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA;
-openssl_suite("SRP-RSA-AES-256-CBC-SHA") ->
- ?TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA;
-openssl_suite("SRP-DSS-3DES-EDE-CBC-SHA") ->
- ?TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA;
-openssl_suite("SRP-RSA-3DES-EDE-CBC-SHA") ->
- ?TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA;
-openssl_suite("SRP-DSS-AES-128-CBC-SHA") ->
- ?TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA;
-openssl_suite("SRP-RSA-AES-128-CBC-SHA") ->
- ?TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA;
-
-%% RFC 4492 EC TLS suites
-openssl_suite("ECDH-ECDSA-RC4-SHA") ->
- ?TLS_ECDH_ECDSA_WITH_RC4_128_SHA;
-openssl_suite("ECDH-ECDSA-DES-CBC3-SHA") ->
- ?TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA;
-openssl_suite("ECDH-ECDSA-AES128-SHA") ->
- ?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA;
-openssl_suite("ECDH-ECDSA-AES256-SHA") ->
- ?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA;
-openssl_suite("ECDHE-ECDSA-RC4-SHA") ->
- ?TLS_ECDHE_ECDSA_WITH_RC4_128_SHA;
-openssl_suite("ECDHE-ECDSA-DES-CBC3-SHA") ->
- ?TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA;
-openssl_suite("ECDHE-ECDSA-AES128-SHA") ->
- ?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA;
-openssl_suite("ECDHE-ECDSA-AES256-SHA") ->
- ?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA;
-
-openssl_suite("ECDHE-RSA-RC4-SHA") ->
- ?TLS_ECDHE_RSA_WITH_RC4_128_SHA;
-openssl_suite("ECDHE-RSA-DES-CBC3-SHA") ->
- ?TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA;
-openssl_suite("ECDHE-RSA-AES128-SHA") ->
- ?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA;
-openssl_suite("ECDHE-RSA-AES256-SHA") ->
- ?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA;
-
-openssl_suite("ECDH-RSA-RC4-SHA") ->
- ?TLS_ECDH_RSA_WITH_RC4_128_SHA;
-openssl_suite("ECDH-RSA-DES-CBC3-SHA") ->
- ?TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA;
-openssl_suite("ECDH-RSA-AES128-SHA") ->
- ?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA;
-openssl_suite("ECDH-RSA-AES256-SHA") ->
- ?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA;
-
-%% RFC 5289 EC TLS suites
-openssl_suite("ECDHE-ECDSA-AES128-SHA256") ->
- ?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256;
-openssl_suite("ECDHE-ECDSA-AES256-SHA384") ->
- ?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384;
-openssl_suite("ECDH-ECDSA-AES128-SHA256") ->
- ?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256;
-openssl_suite("ECDH-ECDSA-AES256-SHA384") ->
- ?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384;
-openssl_suite("ECDHE-RSA-AES128-SHA256") ->
- ?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256;
-openssl_suite("ECDHE-RSA-AES256-SHA384") ->
- ?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384;
-openssl_suite("ECDH-RSA-AES128-SHA256") ->
- ?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256;
-openssl_suite("ECDH-RSA-AES256-SHA384") ->
- ?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384;
+tls_1_3_suite_str_to_map(CipherStr) ->
+ {Cipher, Mac, Prf} = cipher_str_to_algs(CipherStr, ""),
+ #{key_exchange => any,
+ mac => Mac,
+ cipher => Cipher,
+ prf => Prf
+ }.
-%% RFC 5288 AES-GCM Cipher Suites
-openssl_suite("AES128-GCM-SHA256") ->
- ?TLS_RSA_WITH_AES_128_GCM_SHA256;
-openssl_suite("AES256-GCM-SHA384") ->
- ?TLS_RSA_WITH_AES_256_GCM_SHA384;
-openssl_suite("DHE-RSA-AES128-GCM-SHA256") ->
- ?TLS_DHE_RSA_WITH_AES_128_GCM_SHA256;
-openssl_suite("DHE-RSA-AES256-GCM-SHA384") ->
- ?TLS_DHE_RSA_WITH_AES_256_GCM_SHA384;
-openssl_suite("DH-RSA-AES128-GCM-SHA256") ->
- ?TLS_DH_RSA_WITH_AES_128_GCM_SHA256;
-openssl_suite("DH-RSA-AES256-GCM-SHA384") ->
- ?TLS_DH_RSA_WITH_AES_256_GCM_SHA384;
-openssl_suite("DHE-DSS-AES128-GCM-SHA256") ->
- ?TLS_DHE_DSS_WITH_AES_128_GCM_SHA256;
-openssl_suite("DHE-DSS-AES256-GCM-SHA384") ->
- ?TLS_DHE_DSS_WITH_AES_256_GCM_SHA384;
-openssl_suite("DH-DSS-AES128-GCM-SHA256") ->
- ?TLS_DH_DSS_WITH_AES_128_GCM_SHA256;
-openssl_suite("DH-DSS-AES256-GCM-SHA384") ->
- ?TLS_DH_DSS_WITH_AES_256_GCM_SHA384;
+pre_tls_1_3_suite_str_to_map(KexStr, Rest) ->
+ Kex = algo_str_to_atom(KexStr),
+ [CipherStr, AlgStr] = string:split(Rest, "_", trailing),
+ {Cipher, Mac, Prf} = cipher_str_to_algs(CipherStr, AlgStr),
+ #{key_exchange => Kex,
+ mac => Mac,
+ cipher => Cipher,
+ prf => Prf
+ }.
+
+cipher_str_to_algs(CipherStr, "CCM"= End) -> %% PRE TLS 1.3
+ Cipher = algo_str_to_atom(CipherStr ++ "_" ++ End),
+ {Cipher, aead, sha256};
+cipher_str_to_algs(CipherStr, "8" = End) -> %% PRE TLS 1.3
+ Cipher = algo_str_to_atom(CipherStr ++ "_" ++ End),
+ {Cipher, aead, sha256};
+cipher_str_to_algs(CipherStr, "CHACHA20_POLY1305" = End) -> %% PRE TLS 1.3
+ Cipher = algo_str_to_atom(CipherStr ++ "_" ++ End),
+ {Cipher, aead, sha256};
+cipher_str_to_algs(CipherStr0, "") -> %% TLS 1.3
+ [CipherStr, AlgStr] = string:split(CipherStr0, "_", trailing),
+ Hash = algo_str_to_atom(AlgStr),
+ Cipher = algo_str_to_atom(CipherStr),
+ {Cipher, aead, Hash};
+cipher_str_to_algs(CipherStr, HashStr) -> %% PRE TLS 1.3
+ Hash = algo_str_to_atom(HashStr),
+ Cipher = algo_str_to_atom(CipherStr),
+ case is_aead_cipher(CipherStr) of
+ true ->
+ {Cipher, aead, Hash};
+ false ->
+ {Cipher, Hash, default_prf}
+ end.
-%% RFC 5289 ECC AES-GCM Cipher Suites
-openssl_suite("ECDHE-ECDSA-AES128-GCM-SHA256") ->
- ?TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256;
-openssl_suite("ECDHE-ECDSA-AES256-GCM-SHA384") ->
- ?TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384;
-openssl_suite("ECDH-ECDSA-AES128-GCM-SHA256") ->
- ?TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256;
-openssl_suite("ECDH-ECDSA-AES256-GCM-SHA384") ->
- ?TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384;
-openssl_suite("ECDHE-RSA-AES128-GCM-SHA256") ->
- ?TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256;
-openssl_suite("ECDHE-RSA-AES256-GCM-SHA384") ->
- ?TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384;
-openssl_suite("ECDH-RSA-AES128-GCM-SHA256") ->
- ?TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256;
-openssl_suite("ECDH-RSA-AES256-GCM-SHA384") ->
- ?TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384;
+%% PRE TLS 1.3
+is_aead_cipher("CHACHA20_POLY1305") ->
+ true;
+is_aead_cipher(CipherStr) ->
+ [_, Rest] = string:split(CipherStr, "_", trailing),
+ (Rest == "GCM") orelse (Rest == "CCM") orelse (Rest == "8").
-%% TLS 1.3 Cipher Suites RFC8446
-openssl_suite("TLS_AES_128_GCM_SHA256") ->
- ?TLS_AES_128_GCM_SHA256;
-openssl_suite("TLS_AES_256_GCM_SHA384") ->
- ?TLS_AES_256_GCM_SHA384;
-openssl_suite("TLS_CHACHA20_POLY1305_SHA256") ->
- ?TLS_CHACHA20_POLY1305_SHA256.
-%% openssl_suite("TLS_AES_128_CCM_SHA256") ->
-%% ?TLS_AES_128_CCM_SHA256;
-%% openssl_suite("TLS_AES_128_CCM_8_SHA256") ->
-%% ?TLS_AES_128_CCM_8_SHA256.
+openssl_is_aead_cipher("CHACHA20-POLY1305") ->
+ true;
+openssl_is_aead_cipher(CipherStr) ->
+ case string:split(CipherStr, "-", trailing) of
+ [_, Rest] ->
+ (Rest == "GCM") orelse (Rest == "CCM") orelse (Rest == "8");
+ [_] ->
+ false
+ end.
+algo_str_to_atom(AlgoStr) ->
+ erlang:list_to_existing_atom(string:to_lower(AlgoStr)).
-%%--------------------------------------------------------------------
--spec openssl_suite_name(cipher_suite()) -> openssl_cipher_suite() | internal_erl_cipher_suite().
-%%
-%% Description: Return openssl cipher suite name if possible
-%%-------------------------------------------------------------------
-openssl_suite_name(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA) ->
- "DHE-RSA-AES256-SHA";
-openssl_suite_name(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA) ->
- "DHE-DSS-AES256-SHA";
-openssl_suite_name(?TLS_RSA_WITH_AES_256_CBC_SHA) ->
- "AES256-SHA";
-openssl_suite_name(?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA) ->
- "EDH-RSA-DES-CBC3-SHA";
-openssl_suite_name(?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA) ->
- "EDH-DSS-DES-CBC3-SHA";
-openssl_suite_name(?TLS_RSA_WITH_3DES_EDE_CBC_SHA) ->
- "DES-CBC3-SHA";
-openssl_suite_name( ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA) ->
- "DHE-RSA-AES128-SHA";
-openssl_suite_name(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA) ->
- "DHE-DSS-AES128-SHA";
-openssl_suite_name(?TLS_RSA_WITH_AES_128_CBC_SHA) ->
- "AES128-SHA";
-openssl_suite_name(?TLS_RSA_WITH_RC4_128_SHA) ->
- "RC4-SHA";
-openssl_suite_name(?TLS_RSA_WITH_RC4_128_MD5) ->
- "RC4-MD5";
-openssl_suite_name(?TLS_DHE_RSA_WITH_DES_CBC_SHA) ->
- "EDH-RSA-DES-CBC-SHA";
-openssl_suite_name(?TLS_RSA_WITH_DES_CBC_SHA) ->
- "DES-CBC-SHA";
-openssl_suite_name(?TLS_RSA_WITH_NULL_SHA256) ->
- "NULL-SHA256";
-openssl_suite_name(?TLS_RSA_WITH_AES_128_CBC_SHA256) ->
- "AES128-SHA256";
-openssl_suite_name(?TLS_RSA_WITH_AES_256_CBC_SHA256) ->
- "AES256-SHA256";
-openssl_suite_name(?TLS_DH_DSS_WITH_AES_128_CBC_SHA256) ->
- "DH-DSS-AES128-SHA256";
-openssl_suite_name(?TLS_DH_RSA_WITH_AES_128_CBC_SHA256) ->
- "DH-RSA-AES128-SHA256";
-openssl_suite_name(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256) ->
- "DHE-DSS-AES128-SHA256";
-openssl_suite_name(?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256) ->
- "DHE-RSA-AES128-SHA256";
-openssl_suite_name(?TLS_DH_DSS_WITH_AES_256_CBC_SHA256) ->
- "DH-DSS-AES256-SHA256";
-openssl_suite_name(?TLS_DH_RSA_WITH_AES_256_CBC_SHA256) ->
- "DH-RSA-AES256-SHA256";
-openssl_suite_name(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256) ->
- "DHE-DSS-AES256-SHA256";
-openssl_suite_name(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256) ->
- "DHE-RSA-AES256-SHA256";
-%%% PSK Cipher Suites RFC 4279
+openssl_cipher_name(Kex, "AES_128_CBC" ++ _ = CipherStr) when Kex == rsa;
+ Kex == dhe_rsa;
+ Kex == ecdhe_rsa;
+ Kex == ecdhe_ecdsa ->
+ openssl_name_concat(CipherStr);
+openssl_cipher_name(Kex, "AES_256_CBC" ++ _ = CipherStr) when Kex == rsa;
+ Kex == dhe_rsa;
+ Kex == ecdhe_rsa;
+ Kex == ecdhe_ecdsa ->
+ openssl_name_concat(CipherStr);
+openssl_cipher_name(Kex, "AES_128_CBC" ++ _ = CipherStr) when Kex == srp;
+ Kex == srp_rsa ->
+ lists:append(string:replace(CipherStr, "_", "-", all));
+openssl_cipher_name(Kex, "AES_256_CBC" ++ _ = CipherStr) when Kex == srp;
+ Kex == srp_rsa ->
+ lists:append(string:replace(CipherStr, "_", "-", all));
+openssl_cipher_name(_, "AES_128_CBC" ++ _ = CipherStr) ->
+ openssl_name_concat(CipherStr) ++ "-CBC";
+openssl_cipher_name(_, "AES_256_CBC" ++ _ = CipherStr) ->
+ openssl_name_concat(CipherStr) ++ "-CBC";
+openssl_cipher_name(_, "AES_128_GCM" ++ _ = CipherStr) ->
+ openssl_name_concat(CipherStr) ++ "-GCM";
+openssl_cipher_name(_, "AES_256_GCM" ++ _ = CipherStr) ->
+ openssl_name_concat(CipherStr) ++ "-GCM";
+openssl_cipher_name(_, "RC4" ++ _) ->
+ "RC4";
+openssl_cipher_name(_, CipherStr) ->
+ lists:append(string:replace(CipherStr, "_", "-", all)).
-openssl_suite_name(?TLS_PSK_WITH_AES_256_CBC_SHA) ->
- "PSK-AES256-CBC-SHA";
-openssl_suite_name(?TLS_PSK_WITH_3DES_EDE_CBC_SHA) ->
- "PSK-3DES-EDE-CBC-SHA";
-openssl_suite_name(?TLS_PSK_WITH_AES_128_CBC_SHA) ->
- "PSK-AES128-CBC-SHA";
-openssl_suite_name(?TLS_PSK_WITH_RC4_128_SHA) ->
- "PSK-RC4-SHA";
-%%% SRP Cipher Suites RFC 5054
+openssl_suite_start(Kex) ->
+ case openssl_kex_name(Kex) of
+ "" ->
+ "";
+ Name ->
+ Name ++ "-"
+ end.
-openssl_suite_name(?TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA) ->
- "SRP-RSA-3DES-EDE-CBC-SHA";
-openssl_suite_name(?TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA) ->
- "SRP-DSS-3DES-EDE-CBC-SHA";
-openssl_suite_name(?TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA) ->
- "SRP-RSA-AES-128-CBC-SHA";
-openssl_suite_name(?TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA) ->
- "SRP-DSS-AES-128-CBC-SHA";
-openssl_suite_name(?TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA) ->
- "SRP-RSA-AES-256-CBC-SHA";
-openssl_suite_name(?TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA) ->
- "SRP-DSS-AES-256-CBC-SHA";
+openssl_kex_name("RSA") ->
+ "";
+openssl_kex_name(Kex) ->
+ lists:append(string:replace(Kex, "_", "-", all)).
-%% RFC 4492 EC TLS suites
-openssl_suite_name(?TLS_ECDH_ECDSA_WITH_RC4_128_SHA) ->
- "ECDH-ECDSA-RC4-SHA";
-openssl_suite_name(?TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA) ->
- "ECDH-ECDSA-DES-CBC3-SHA";
-openssl_suite_name(?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA) ->
- "ECDH-ECDSA-AES128-SHA";
-openssl_suite_name(?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA) ->
- "ECDH-ECDSA-AES256-SHA";
+kex_name_from_openssl(Kex) ->
+ lists:append(string:replace(Kex, "-", "_", all)).
-openssl_suite_name(?TLS_ECDHE_ECDSA_WITH_RC4_128_SHA) ->
- "ECDHE-ECDSA-RC4-SHA";
-openssl_suite_name(?TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA) ->
- "ECDHE-ECDSA-DES-CBC3-SHA";
-openssl_suite_name(?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA) ->
- "ECDHE-ECDSA-AES128-SHA";
-openssl_suite_name(?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA) ->
- "ECDHE-ECDSA-AES256-SHA";
+cipher_name_from_openssl("AES128") ->
+ "AES_128_CBC";
+cipher_name_from_openssl("AES256") ->
+ "AES_256_CBC";
+cipher_name_from_openssl("AES128-CBC") ->
+ "AES_128_CBC";
+cipher_name_from_openssl("AES256-CBC") ->
+ "AES_256_CBC";
+cipher_name_from_openssl("AES-128-CBC") ->
+ "AES_128_CBC";
+cipher_name_from_openssl("AES-256-CBC") ->
+ "AES_256_CBC";
+cipher_name_from_openssl("AES128-GCM") ->
+ "AES_128_GCM";
+cipher_name_from_openssl("AES256-GCM") ->
+ "AES_256_GCM";
+cipher_name_from_openssl("RC4") ->
+ "RC4_128";
+cipher_name_from_openssl(Str) ->
+ Str.
-openssl_suite_name(?TLS_ECDH_RSA_WITH_RC4_128_SHA) ->
- "ECDH-RSA-RC4-SHA";
-openssl_suite_name(?TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA) ->
- "ECDH-RSA-DES-CBC3-SHA";
-openssl_suite_name(?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA) ->
- "ECDH-RSA-AES128-SHA";
-openssl_suite_name(?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA) ->
- "ECDH-RSA-AES256-SHA";
+openssl_name_concat(Str0) ->
+ [Str, _] = string:split(Str0, "_", trailing),
+ [Part1, Part2] = string:split(Str, "_", trailing),
+ Part1 ++ Part2.
-openssl_suite_name(?TLS_ECDHE_RSA_WITH_RC4_128_SHA) ->
- "ECDHE-RSA-RC4-SHA";
-openssl_suite_name(?TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA) ->
- "ECDHE-RSA-DES-CBC3-SHA";
-openssl_suite_name(?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA) ->
- "ECDHE-RSA-AES128-SHA";
-openssl_suite_name(?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA) ->
- "ECDHE-RSA-AES256-SHA";
-%% RFC 5289 EC TLS suites
-openssl_suite_name(?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256) ->
- "ECDHE-ECDSA-AES128-SHA256";
-openssl_suite_name(?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384) ->
- "ECDHE-ECDSA-AES256-SHA384";
-openssl_suite_name(?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256) ->
- "ECDH-ECDSA-AES128-SHA256";
-openssl_suite_name(?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384) ->
- "ECDH-ECDSA-AES256-SHA384";
-openssl_suite_name(?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256) ->
- "ECDHE-RSA-AES128-SHA256";
-openssl_suite_name(?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384) ->
- "ECDHE-RSA-AES256-SHA384";
-openssl_suite_name(?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256) ->
- "ECDH-RSA-AES128-SHA256";
-openssl_suite_name(?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384) ->
- "ECDH-RSA-AES256-SHA384";
+suite_openssl_str_to_map(Kex0, Rest) ->
+ Kex = algo_str_to_atom(kex_name_from_openssl(Kex0)),
+ [CipherStr, AlgStr] = string:split(Rest, "-", trailing),
+ {Cipher, Mac, Prf} = openssl_cipher_str_to_algs(CipherStr, AlgStr),
+ #{key_exchange => Kex,
+ mac => Mac,
+ cipher => Cipher,
+ prf => Prf
+ }.
-%% RFC 5288 AES-GCM Cipher Suites
-openssl_suite_name(?TLS_RSA_WITH_AES_128_GCM_SHA256) ->
- "AES128-GCM-SHA256";
-openssl_suite_name(?TLS_RSA_WITH_AES_256_GCM_SHA384) ->
- "AES256-GCM-SHA384";
-openssl_suite_name(?TLS_DHE_RSA_WITH_AES_128_GCM_SHA256) ->
- "DHE-RSA-AES128-GCM-SHA256";
-openssl_suite_name(?TLS_DHE_RSA_WITH_AES_256_GCM_SHA384) ->
- "DHE-RSA-AES256-GCM-SHA384";
-openssl_suite_name(?TLS_DH_RSA_WITH_AES_128_GCM_SHA256) ->
- "DH-RSA-AES128-GCM-SHA256";
-openssl_suite_name(?TLS_DH_RSA_WITH_AES_256_GCM_SHA384) ->
- "DH-RSA-AES256-GCM-SHA384";
-openssl_suite_name(?TLS_DHE_DSS_WITH_AES_128_GCM_SHA256) ->
- "DHE-DSS-AES128-GCM-SHA256";
-openssl_suite_name(?TLS_DHE_DSS_WITH_AES_256_GCM_SHA384) ->
- "DHE-DSS-AES256-GCM-SHA384";
-openssl_suite_name(?TLS_DH_DSS_WITH_AES_128_GCM_SHA256) ->
- "DH-DSS-AES128-GCM-SHA256";
-openssl_suite_name(?TLS_DH_DSS_WITH_AES_256_GCM_SHA384) ->
- "DH-DSS-AES256-GCM-SHA384";
+%% Does only need own implementation PRE TLS 1.3
+openssl_cipher_str_to_algs(CipherStr, "CCM"= End) ->
+ Cipher = algo_str_to_atom(CipherStr ++ "_" ++ End),
+ {Cipher, aead, sha256};
+openssl_cipher_str_to_algs(CipherStr, "8" = End) ->
+ Cipher = algo_str_to_atom(CipherStr ++ "_" ++ End),
+ {Cipher, aead, sha256};
+openssl_cipher_str_to_algs(CipherStr, "POLY1305" = End) ->
+ Cipher = algo_str_to_atom(CipherStr ++ "_" ++ End),
+ {Cipher, aead, sha256};
+openssl_cipher_str_to_algs(CipherStr, HashStr) ->
+ Hash = algo_str_to_atom(HashStr),
+ Cipher = algo_str_to_atom(cipher_name_from_openssl(CipherStr)),
+ case openssl_is_aead_cipher(CipherStr) of
+ true ->
+ {Cipher, aead, Hash};
+ false ->
+ {Cipher, Hash, openssl_prf(Hash)}
+ end.
-%% RFC 5289 ECC AES-GCM Cipher Suites
-openssl_suite_name(?TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256) ->
- "ECDHE-ECDSA-AES128-GCM-SHA256";
-openssl_suite_name(?TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384) ->
- "ECDHE-ECDSA-AES256-GCM-SHA384";
-openssl_suite_name(?TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256) ->
- "ECDH-ECDSA-AES128-GCM-SHA256";
-openssl_suite_name(?TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384) ->
- "ECDH-ECDSA-AES256-GCM-SHA384";
-openssl_suite_name(?TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) ->
- "ECDHE-RSA-AES128-GCM-SHA256";
-openssl_suite_name(?TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) ->
- "ECDHE-RSA-AES256-GCM-SHA384";
-openssl_suite_name(?TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256) ->
- "ECDH-RSA-AES128-GCM-SHA256";
-openssl_suite_name(?TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384) ->
- "ECDH-RSA-AES256-GCM-SHA384";
+openssl_prf(sha256)->
+ sha256;
+openssl_prf(sha384) ->
+ sha384;
+openssl_prf(_) ->
+ default_prf.
-%% ChaCha20-Poly1305 Cipher Suites for Transport Layer Security (TLS) RFC7905
-openssl_suite_name(?TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256) ->
- "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256";
-openssl_suite_name(?TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256) ->
- "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256";
-openssl_suite_name(?TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256) ->
- "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256";
-openssl_suite_name(?TLS_PSK_WITH_CHACHA20_POLY1305_SHA256) ->
- "TLS_PSK_WITH_CHACHA20_POLY1305_SHA256";
-openssl_suite_name(?TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256) ->
- "TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256";
-openssl_suite_name(?TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256) ->
- "TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256";
-openssl_suite_name(?TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256) ->
- "TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256";
-%% TLS 1.3 Cipher Suites RFC8446
-openssl_suite_name(?TLS_AES_128_GCM_SHA256) ->
- "TLS_AES_128_GCM_SHA256";
-openssl_suite_name(?TLS_AES_256_GCM_SHA384) ->
- "TLS_AES_256_GCM_SHA384";
-openssl_suite_name(?TLS_CHACHA20_POLY1305_SHA256) ->
- "TLS_CHACHA20_POLY1305_SHA256";
-%% openssl_suite(?TLS_AES_128_CCM_SHA256) ->
-%% "TLS_AES_128_CCM_SHA256";
-%% openssl_suite(?TLS_AES_128_CCM_8_SHA256) ->
-%% "TLS_AES_128_CCM_8_SHA256";
-%% No oppenssl name
-openssl_suite_name(Cipher) ->
- suite_definition(Cipher).
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index 1e97fe046b..6e325b504c 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -703,7 +703,7 @@ handle_session(#server_hello{cipher_suite = CipherSuite,
handshake_env = #handshake_env{negotiated_protocol = CurrentProtocol} = HsEnv,
connection_env = #connection_env{negotiated_version = ReqVersion} = CEnv} = State0) ->
#{key_exchange := KeyAlgorithm} =
- ssl_cipher_format:suite_definition(CipherSuite),
+ ssl_cipher_format:suite_bin_to_map(CipherSuite),
PremasterSecret = make_premaster_secret(ReqVersion, KeyAlgorithm),
@@ -1573,7 +1573,7 @@ connection_info(#state{static_env = #static_env{protocol_cb = Connection},
connection_env = #connection_env{negotiated_version = {_,_} = Version},
ssl_options = Opts}) ->
RecordCB = record_cb(Connection),
- CipherSuiteDef = #{key_exchange := KexAlg} = ssl_cipher_format:suite_definition(CipherSuite),
+ CipherSuiteDef = #{key_exchange := KexAlg} = ssl_cipher_format:suite_bin_to_map(CipherSuite),
IsNamedCurveSuite = lists:member(KexAlg,
[ecdh_ecdsa, ecdhe_ecdsa, ecdh_rsa, ecdhe_rsa, ecdh_anon]),
CurveInfo = case ECCCurve of
@@ -1584,7 +1584,7 @@ connection_info(#state{static_env = #static_env{protocol_cb = Connection},
end,
[{protocol, RecordCB:protocol_version(Version)},
{session_id, SessionId},
- {cipher_suite, ssl_cipher_format:erl_suite_definition(CipherSuiteDef)},
+ {cipher_suite, ssl_cipher_format:suite_legacy(CipherSuiteDef)},
{selected_cipher_suite, CipherSuiteDef},
{sni_hostname, SNIHostname} | CurveInfo] ++ ssl_options_list(Opts).
@@ -1711,7 +1711,7 @@ resumed_server_hello(#state{session = Session,
server_hello(ServerHello, State0, Connection) ->
CipherSuite = ServerHello#server_hello.cipher_suite,
- #{key_exchange := KeyAlgorithm} = ssl_cipher_format:suite_definition(CipherSuite),
+ #{key_exchange := KeyAlgorithm} = ssl_cipher_format:suite_bin_to_map(CipherSuite),
#state{handshake_env = HsEnv} = State = Connection:queue_handshake(ServerHello, State0),
State#state{handshake_env = HsEnv#handshake_env{kex_algorithm = KeyAlgorithm}}.
@@ -1726,7 +1726,7 @@ handle_peer_cert(Role, PeerCert, PublicKeyInfo,
State1 = State0#state{handshake_env = HsEnv#handshake_env{public_key_info = PublicKeyInfo},
session =
Session#session{peer_certificate = PeerCert}},
- #{key_exchange := KeyAlgorithm} = ssl_cipher_format:suite_definition(CipherSuite),
+ #{key_exchange := KeyAlgorithm} = ssl_cipher_format:suite_bin_to_map(CipherSuite),
State = handle_peer_cert_key(Role, PeerCert, PublicKeyInfo, KeyAlgorithm, State1),
Connection:next_event(certify, no_record, State).
@@ -2728,7 +2728,7 @@ ssl_options_list([ciphers = Key | Keys], [Value | Values], Acc) ->
ssl_options_list(Keys, Values,
[{Key, lists:map(
fun(Suite) ->
- ssl_cipher_format:suite_definition(Suite)
+ ssl_cipher_format:suite_bin_to_map(Suite)
end, Value)}
| Acc]);
ssl_options_list([Key | Keys], [Value | Values], Acc) ->
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index 3a69c86e47..dc037bb1ea 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -182,7 +182,7 @@ client_certificate_verify(OwnCert, MasterSecret, Version,
%% Description: Creates a certificate_request message, called by the server.
%%--------------------------------------------------------------------
certificate_request(CipherSuite, CertDbHandle, CertDbRef, HashSigns, Version) ->
- Types = certificate_types(ssl_cipher_format:suite_definition(CipherSuite), Version),
+ Types = certificate_types(ssl_cipher_format:suite_bin_to_map(CipherSuite), Version),
Authorities = certificate_authorities(CertDbHandle, CertDbRef),
#certificate_request{
certificate_types = Types,
@@ -883,7 +883,7 @@ available_suites(ServerCert, UserSuites, Version, undefined, Curve) ->
filter_unavailable_ecc_suites(Curve, Suites);
available_suites(ServerCert, UserSuites, Version, HashSigns, Curve) ->
Suites = available_suites(ServerCert, UserSuites, Version, undefined, Curve),
- filter_hashsigns(Suites, [ssl_cipher_format:suite_definition(Suite) || Suite <- Suites], HashSigns,
+ filter_hashsigns(Suites, [ssl_cipher_format:suite_bin_to_map(Suite) || Suite <- Suites], HashSigns,
Version, []).
available_signature_algs(undefined, _) ->
@@ -1085,7 +1085,7 @@ add_common_extensions(Version,
{EcPointFormats, EllipticCurves} =
case advertises_ec_ciphers(
- lists:map(fun ssl_cipher_format:suite_definition/1,
+ lists:map(fun ssl_cipher_format:suite_bin_to_map/1,
CipherSuites)) of
true ->
client_ecc_extensions(SupportedECCs);
@@ -2990,7 +2990,7 @@ handle_renegotiation_info(_RecordCB, ConnectionStates, SecureRenegotation) ->
cert_curve(_, _, no_suite) ->
{no_curve, no_suite};
cert_curve(Cert, ECCCurve0, CipherSuite) ->
- case ssl_cipher_format:suite_definition(CipherSuite) of
+ case ssl_cipher_format:suite_bin_to_map(CipherSuite) of
#{key_exchange := Kex} when Kex == ecdh_ecdsa;
Kex == ecdh_rsa ->
OtpCert = public_key:pkix_decode_cert(Cert, otp),
diff --git a/lib/ssl/src/ssl_logger.erl b/lib/ssl/src/ssl_logger.erl
index f497315235..987693b96b 100644
--- a/lib/ssl/src/ssl_logger.erl
+++ b/lib/ssl/src/ssl_logger.erl
@@ -206,10 +206,14 @@ parse_handshake(Direction, #encrypted_extensions{} = EncryptedExtensions) ->
parse_cipher_suites([_|_] = Ciphers) ->
[format_cipher(C) || C <- Ciphers].
-format_cipher(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV) ->
- 'TLS_EMPTY_RENEGOTIATION_INFO_SCSV';
format_cipher(C0) ->
- list_to_atom(ssl_cipher_format:openssl_suite_name(C0)).
+ try ssl_cipher_format:suite_bin_to_map(C0) of
+ Map ->
+ ssl_cipher_format:suite_map_to_str(Map)
+ catch
+ error:function_clause ->
+ format_uknown_cipher_suite(C0)
+ end.
get_client_version(Version, Extensions) ->
CHVersions = maps:get(client_hello_versions, Extensions, undefined),
@@ -436,3 +440,7 @@ number_to_hex(N) ->
H ->
lists:reverse(H)
end.
+
+format_uknown_cipher_suite(<<?BYTE(X), ?BYTE(Y)>>) ->
+ "0x" ++ number_to_hex(X) ++ "0x" ++ number_to_hex(Y).
+
diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl
index e7cee1956b..74ab0d5923 100644
--- a/lib/ssl/src/tls_handshake.erl
+++ b/lib/ssl/src/tls_handshake.erl
@@ -294,7 +294,7 @@ handle_client_hello(Version,
no_suite ->
?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_ciphers);
_ ->
- #{key_exchange := KeyExAlg} = ssl_cipher_format:suite_definition(CipherSuite),
+ #{key_exchange := KeyExAlg} = ssl_cipher_format:suite_bin_to_map(CipherSuite),
case ssl_handshake:select_hashsign({ClientHashSigns, ClientSignatureSchemes},
Cert, KeyExAlg,
SupportedHashSigns,
diff --git a/lib/ssl/src/tls_handshake_1_3.erl b/lib/ssl/src/tls_handshake_1_3.erl
index 20d28c33de..8a4ad922e1 100644
--- a/lib/ssl/src/tls_handshake_1_3.erl
+++ b/lib/ssl/src/tls_handshake_1_3.erl
@@ -887,7 +887,7 @@ calculate_handshake_secrets(ClientKey, SelectedGroup, KeyShare,
tls_v1:server_handshake_traffic_secret(HKDFAlgo, HandshakeSecret, lists:reverse(Messages)),
%% Calculate traffic keys
- #{cipher := Cipher} = ssl_cipher_format:suite_definition(CipherSuite),
+ #{cipher := Cipher} = ssl_cipher_format:suite_bin_to_map(CipherSuite),
{ReadKey, ReadIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, Cipher, ClientHSTrafficSecret),
{WriteKey, WriteIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, Cipher, ServerHSTrafficSecret),
@@ -922,7 +922,7 @@ calculate_traffic_secrets(#state{connection_states = ConnectionStates,
tls_v1:server_application_traffic_secret_0(HKDFAlgo, MasterSecret, lists:reverse(Messages)),
%% Calculate traffic keys
- #{cipher := Cipher} = ssl_cipher_format:suite_definition(CipherSuite),
+ #{cipher := Cipher} = ssl_cipher_format:suite_bin_to_map(CipherSuite),
{ReadKey, ReadIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, Cipher, ClientAppTrafficSecret0),
{WriteKey, WriteIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, Cipher, ServerAppTrafficSecret0),
diff --git a/lib/ssl/test/ssl_cipher_suite_SUITE.erl b/lib/ssl/test/ssl_cipher_suite_SUITE.erl
index bf1bc0e752..8805df7b52 100644
--- a/lib/ssl/test/ssl_cipher_suite_SUITE.erl
+++ b/lib/ssl/test/ssl_cipher_suite_SUITE.erl
@@ -749,7 +749,7 @@ cipher_suite_test(CipherSuite, Version, Config) ->
ssl_test_lib:close(Client).
erlang_cipher_suite(Suite) when is_list(Suite)->
- ssl_cipher_format:suite_definition(ssl_cipher_format:openssl_suite(Suite));
+ ssl_cipher_format:suite_definition(ssl_cipher_format:suite_openssl_str_to_map(Suite));
erlang_cipher_suite(Suite) ->
Suite.
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index b8672f46ba..798bdf0416 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -1341,13 +1341,13 @@ common_ciphers(crypto) ->
common_ciphers(openssl) ->
OpenSslSuites =
string:tokens(string:strip(os:cmd("openssl ciphers"), right, $\n), ":"),
- [ssl_cipher_format:suite_definition(S)
+ [ssl_cipher_format:suite_bin_to_map(S)
|| S <- ssl_cipher:suites(tls_record:highest_protocol_version([])),
- lists:member(ssl_cipher_format:openssl_suite_name(S), OpenSslSuites)
+ lists:member(ssl_cipher_format:suite_map_to_openssl_str(ssl_cipher_format:suite_bin_to_map(S)), OpenSslSuites)
].
available_suites(Version) ->
- [ssl_cipher_format:suite_definition(Suite) ||
+ [ssl_cipher_format:suite_bin_to_map(Suite) ||
Suite <- ssl_cipher:filter_suites(ssl_cipher:suites(Version))].
@@ -1420,7 +1420,7 @@ string_regex_filter(_Str, _Search) ->
false.
ecdh_dh_anonymous_suites(Version) ->
- ssl:filter_cipher_suites([ssl_cipher_format:suite_definition(S) || S <- ssl_cipher:anonymous_suites(Version)],
+ ssl:filter_cipher_suites([ssl_cipher_format:suite_bin_to_map(S) || S <- ssl_cipher:anonymous_suites(Version)],
[{key_exchange,
fun(dh_anon) ->
true;
@@ -1430,7 +1430,7 @@ ecdh_dh_anonymous_suites(Version) ->
false
end}]).
psk_suites({3,_} = Version) ->
- ssl:filter_cipher_suites([ssl_cipher_format:suite_definition(S) || S <- ssl_cipher:psk_suites(Version)], []);
+ ssl:filter_cipher_suites([ssl_cipher_format:suite_bin_to_map(S) || S <- ssl_cipher:psk_suites(Version)], []);
psk_suites(Version) ->
ssl:filter_cipher_suites(psk_suites(dtls_v1:corresponding_tls_version(Version)),
[{cipher,
@@ -1441,7 +1441,7 @@ psk_suites(Version) ->
end}]).
psk_anon_suites({3,_} = Version) ->
- ssl:filter_cipher_suites([ssl_cipher_format:suite_definition(S) || S <- ssl_cipher:psk_suites_anon(Version)],
+ ssl:filter_cipher_suites([ssl_cipher_format:suite_bin_to_map(S) || S <- ssl_cipher:psk_suites_anon(Version)],
[{key_exchange,
fun(psk) ->
true;
@@ -1464,7 +1464,7 @@ psk_anon_suites(Version) ->
srp_suites() ->
- ssl:filter_cipher_suites([ssl_cipher_format:suite_definition(S) || S <- ssl_cipher:srp_suites()],
+ ssl:filter_cipher_suites([ssl_cipher_format:suite_bin_to_map(S) || S <- ssl_cipher:srp_suites()],
[{key_exchange,
fun(srp_rsa) ->
true;
@@ -1472,10 +1472,10 @@ srp_suites() ->
false
end}]).
srp_anon_suites() ->
- ssl:filter_cipher_suites([ssl_cipher_format:suite_definition(S) || S <- ssl_cipher:srp_suites_anon()],
+ ssl:filter_cipher_suites([ssl_cipher_format:suite_bin_to_map(S) || S <- ssl_cipher:srp_suites_anon()],
[]).
srp_dss_suites() ->
- ssl:filter_cipher_suites([ssl_cipher_format:suite_definition(S) || S <- ssl_cipher:srp_suites()],
+ ssl:filter_cipher_suites([ssl_cipher_format:suite_bin_to_map(S) || S <- ssl_cipher:srp_suites()],
[{key_exchange,
fun(srp_dss) ->
true;
@@ -1483,14 +1483,14 @@ srp_dss_suites() ->
false
end}]).
chacha_suites(Version) ->
- [ssl_cipher_format:suite_definition(S) || S <- ssl_cipher:filter_suites(ssl_cipher:chacha_suites(Version))].
+ [ssl_cipher_format:suite_bin_to_map(S) || S <- ssl_cipher:filter_suites(ssl_cipher:chacha_suites(Version))].
rc4_suites(Version) ->
- ssl:filter_cipher_suites([ssl_cipher_format:suite_definition(S) || S <-ssl_cipher:rc4_suites(Version)], []).
+ ssl:filter_cipher_suites([ssl_cipher_format:suite_bin_to_map(S) || S <-ssl_cipher:rc4_suites(Version)], []).
des_suites(Version) ->
- ssl:filter_cipher_suites([ssl_cipher_format:suite_definition(S) || S <-ssl_cipher:des_suites(Version)], []).
+ ssl:filter_cipher_suites([ssl_cipher_format:suite_bin_to_map(S) || S <-ssl_cipher:des_suites(Version)], []).
tuple_to_map({Kex, Cipher, Mac}) ->
#{key_exchange => Kex,
@@ -1941,10 +1941,10 @@ version_flag('dtlsv1') ->
"-dtls1".
filter_suites([Cipher | _] = Ciphers, AtomVersion) when is_list(Cipher)->
- filter_suites([ssl_cipher_format:openssl_suite(S) || S <- Ciphers],
+ filter_suites([ssl_cipher_format:suite_openssl_str_to_map(S) || S <- Ciphers],
AtomVersion);
filter_suites([Cipher | _] = Ciphers, AtomVersion) when is_binary(Cipher)->
- filter_suites([ssl_cipher_format:suite_definition(S) || S <- Ciphers],
+ filter_suites([ssl_cipher_format:suite_bin_to_map(S) || S <- Ciphers],
AtomVersion);
filter_suites(Ciphers0, AtomVersion) ->
Version = tls_version(AtomVersion),
@@ -1956,7 +1956,7 @@ filter_suites(Ciphers0, AtomVersion) ->
++ ssl_cipher:srp_suites_anon()
++ ssl_cipher:rc4_suites(Version),
Supported1 = ssl_cipher:filter_suites(Supported0),
- Supported2 = [ssl_cipher_format:suite_definition(S) || S <- Supported1],
+ Supported2 = [ssl_cipher_format:suite_bin_to_map(S) || S <- Supported1],
[Cipher || Cipher <- Ciphers0, lists:member(Cipher, Supported2)].
-define(OPENSSL_QUIT, "Q\n").
diff --git a/lib/ssl/test/x509_test.erl b/lib/ssl/test/x509_test.erl
index fea01efdaf..faf223ae35 100644
--- a/lib/ssl/test/x509_test.erl
+++ b/lib/ssl/test/x509_test.erl
@@ -22,7 +22,7 @@
-module(x509_test).
- -include_lib("public_key/include/public_key.hrl").
+-include_lib("public_key/include/public_key.hrl").
-export([extensions/1, gen_pem_config_files/3]).
diff --git a/lib/syntax_tools/doc/demo.erl b/lib/syntax_tools/doc/demo.erl
new file mode 120000
index 0000000000..fe40fb65ec
--- /dev/null
+++ b/lib/syntax_tools/doc/demo.erl
@@ -0,0 +1 @@
+../examples/demo.erl \ No newline at end of file
diff --git a/lib/syntax_tools/doc/overview.edoc b/lib/syntax_tools/doc/overview.edoc
index 3111633a99..7be96f1a55 100644
--- a/lib/syntax_tools/doc/overview.edoc
+++ b/lib/syntax_tools/doc/overview.edoc
@@ -26,7 +26,7 @@ library module {@link prettypr}: this is a powerful and flexible generic
pretty printing library, which is also distributed separately.
For a short demonstration of parsing and pretty-printing, simply
-compile the included module <a href="../examples/demo.erl">`demo.erl'</a>,
+compile the included module <a href="demo.erl">`demo.erl'</a>,
and execute `demo:run()' from the Erlang shell. It will compile the
remaining modules and give you further instructions.
diff --git a/lib/syntax_tools/doc/src/Makefile b/lib/syntax_tools/doc/src/Makefile
index d953287bad..b799c76177 100644
--- a/lib/syntax_tools/doc/src/Makefile
+++ b/lib/syntax_tools/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2006-2018. 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.
@@ -82,6 +82,9 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
+EXAMPLES_DIR = ../../examples
+EXAMPLES = $(EXAMPLES_DIR)/demo.erl
+
SPECS_FILES = $(XML_REF3_FILES:%.xml=$(SPECDIR)/specs_%.xml)
TOP_SPECS_FILE = specs.xml
@@ -146,5 +149,7 @@ release_docs_spec: docs
$(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
$(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
$(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
+ $(INSTALL_DIR) "$(RELSYSDIR)/examples"
+ $(INSTALL_DATA) $(EXAMPLES) "$(RELSYSDIR)/doc/html"
release_spec: