diff options
Diffstat (limited to 'lib')
149 files changed, 5098 insertions, 2760 deletions
diff --git a/lib/common_test/Makefile b/lib/common_test/Makefile index e16efd0c9d..aecec1a50d 100644 --- a/lib/common_test/Makefile +++ b/lib/common_test/Makefile @@ -25,12 +25,12 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk # ifeq ($(findstring linux,$(TARGET)),linux) -SUB_DIRECTORIES = doc/src src +SUB_DIRECTORIES = doc/src src priv else ifeq ($(findstring solaris,$(TARGET)),solaris) -SUB_DIRECTORIES = doc/src src +SUB_DIRECTORIES = doc/src src priv else -SUB_DIRECTORIES = doc/src src +SUB_DIRECTORIES = doc/src src priv endif endif diff --git a/lib/common_test/doc/src/run_test.xml b/lib/common_test/doc/src/run_test.xml index d609c4287f..2f0a94afba 100644 --- a/lib/common_test/doc/src/run_test.xml +++ b/lib/common_test/doc/src/run_test.xml @@ -45,6 +45,11 @@ flags start an Erlang node prepared for running Common Test in a particular mode.</p> + <p>There is an interface function that corresponds to this program, + called <c>ct:run_test/1</c>, for starting Common Test from the Erlang + shell (or an Erlang program). Please see the <c>ct</c> man page for + details.</p> + <p><c>run_test</c> also accepts Erlang emulator flags. These are used when <c>run_test</c> calls <c>erl</c> to start the Erlang node (making it possible to e.g. add directories to the code server path, @@ -72,6 +77,8 @@ <p>it prints all valid start flags to stdout.</p> </description> + <marker id="run_test"></marker> + <section> <title>Run tests from command line</title> <pre> @@ -83,6 +90,7 @@ [-userconfig CallbackModule1 ConfigString1 and CallbackModule2 ConfigString2 and .. and CallbackModuleN ConfigStringN] [-decrypt_key Key] | [-decrypt_file KeyFile] + [-label Label] [-logdir LogDir] [-silent_connections [ConnType1 ConnType2 .. ConnTypeN]] [-stylesheet CSSFile] @@ -107,6 +115,7 @@ [-userconfig CallbackModule1 ConfigString1 and CallbackModule2 ConfigString2 and .. and CallbackModuleN ConfigStringN] [-decrypt_key Key] | [-decrypt_file KeyFile] + [-label Label] [-logdir LogDir] [-allow_user_terms] [-silent_connections [ConnType1 ConnType2 .. ConnTypeN]] @@ -128,12 +137,12 @@ <title>Run tests in web based GUI</title> <pre> run_test -vts [-browser Browser] + [-dir TestDir1 TestDir2 .. TestDirN] | + [-suite Suite [[-group Group] [-case Case]]] [-config ConfigFile1 ConfigFile2 .. ConfigFileN] [-userconfig CallbackModule1 ConfigString1 and CallbackModule2 ConfigString2 and .. and CallbackModuleN ConfigStringN] [-decrypt_key Key] | [-decrypt_file KeyFile] - [-dir TestDir1 TestDir2 .. TestDirN] | - [-suite Suite [[-group Group] [-case Case]]] [-include InclDir1 InclDir2 .. InclDirN] [-no_auto_compile] [-muliply_timetraps Multiplier] diff --git a/lib/common_test/doc/src/run_test_chapter.xml b/lib/common_test/doc/src/run_test_chapter.xml index 207df7f5b5..1efff25f5b 100644 --- a/lib/common_test/doc/src/run_test_chapter.xml +++ b/lib/common_test/doc/src/run_test_chapter.xml @@ -21,7 +21,7 @@ </legalnotice> - <title>Running Test Suites</title> + <title>Running Tests</title> <prepared>Peter Andersson, Kenneth Lundin</prepared> <docno></docno> <date></date> @@ -130,6 +130,8 @@ <p>Other flags that may be used with <c>run_test</c>:</p> <list> <item><c><![CDATA[-logdir <dir>]]></c>, specifies where the HTML log files are to be written.</item> + <item><c><![CDATA[-label <name_of_test_run>]]></c>, associates the test run with a name that gets printed + in the overview HTML log files.</item> <item><c>-refresh_logs</c>, refreshes the top level HTML index files.</item> <item><c>-vts</c>, start web based GUI (see below).</item> <item><c>-shell</c>, start interactive shell mode (see below).</item> @@ -335,43 +337,72 @@ <marker id="test_specifications"></marker> <title>Using test specifications</title> - <p>The most expressive way to specify what to test is to use a so - called test specification. A test specification is a sequence of - Erlang terms. The terms may be declared in a text file or passed - to the test server at runtime as a list (see <c>run_testspec/1</c> - in the manual page for <c>ct</c>). There are two general types - of terms: configuration terms and test specification terms.</p> - <p>With configuration terms it is possible to import configuration - data (similar to <c>run_test -config</c>), specify HTML log - directories (similar to <c>run_test -logdir</c>), give aliases - to test nodes and test directories (to make a specification - easier to read and maintain), enable code coverage analysis - (see the <seealso marker="cover_chapter#cover">Code Coverage - Analysis</seealso> chapter) and specify event_handler plugins + <p>The most flexible way to specify what to test, is to use a so + called test specification. A test specification is a sequence of + Erlang terms. The terms may be declared in a text file or passed + to the test server at runtime as a list + (see <c>run_testspec/1</c> in the manual page + for <c>ct</c>). There are two general types of terms: + configuration terms and test specification terms.</p> + <p>With configuration terms it is possible to e.g. label the test + run (similar to <c>run_test -label</c>), evaluate arbitrary expressions + before starting a test, import configuration + data (similar to + <c>run_test -config/-userconfig</c>), specify HTML log directories (similar + to + <c>run_test -logdir</c>), give aliases to test nodes and test + directories (to make a specification easier to read and + maintain), enable code coverage analysis (see + the <seealso marker="cover_chapter#cover">Code Coverage + Analysis</seealso> chapter) and specify event_handler plugins (see the <seealso marker="event_handler_chapter#event_handling"> - Event Handling</seealso> chapter). There is also a term - for specifying include directories that should be passed on - to the compiler when automatic compilation is performed - (similar to <c>run_test -include</c>, see above).</p> - <p>With test specification terms it is possible to state exactly which - tests should run and in which order. A test term specifies either - one or more suites or one or more test cases. An arbitrary number of test - terms may be declared in sequence. A test term can also specify one or - more test suites or test cases to be skipped. Skipped suites and cases - are not executed and show up in the HTML test log as SKIPPED.</p> - - <note><p>It is not yet possible to specify test case groups in - test specifications. This will be supported in a soon upcoming - release.</p></note> + Event Handling</seealso> chapter). There is also a term for + specifying include directories that should be passed on to the + compiler when automatic compilation is performed (similar + to <c>run_test -include</c>, see above).</p> + <p>With test specification terms it is possible to state exactly + which tests should run and in which order. A test term specifies + either one or more suites, one or more test case groups, or one + or more test cases in a group or suite.</p> + <p>An arbitrary number of test terms may be declared in sequence. + Common Test will compile the terms into one or more tests to be + performed in one resulting test run. Note that a term that + specifies a set of test cases will "swallow" one that only + specifies a subset of these cases. E.g. the result of merging + one term that specifies that all cases in suite S should be + executed, with another term specifying only test case X and Y in + S, is a test of all cases in S. However, if a term specifying + test case X and Y in S is merged with a term specifying case Z + in S, the result is a test of X, Y and Z in S.</p> + <p>A test term can also specify one or more test suites, groups, + or test cases to be skipped. Skipped suites, groups and cases + are not executed and show up in the HTML test log files as + SKIPPED.</p> + <p>When a test case group is specified, the resulting test + executes the + <c>init_per_group</c> function, followed by all test cases and + sub groups (including their configuration functions), and + finally the <c>end_per_group</c> function. Also if particular + test cases in a group are specified, <c>init_per_group</c> + and <c>end_per_group</c> for the group in question are + called. If a group which is defined (in <c>Suite:group/0</c>) to + be a sub group of another group, is specified (or particular test + cases of a sub group are), Common Test will call the configuration + functions for the top level groups as well as for the sub group + in question (making it possible to pass configuration data all + the way from <c>init_per_suite</c> down to the test cases in the + sub group).</p> <p>Below is the test specification syntax. Test specifications can - be used to run tests both in a single test host environment and in - a distributed Common Test environment (Large Scale Testing). The init term, - as well as node parameters, are only relevant in the latter (see the - <seealso marker="ct_master_chapter#test_specifications">Large Scale Testing</seealso> - chapter for information). For details on the event_handler term, see the - <seealso marker="event_handler_chapter#event_handling">Event Handling</seealso> - chapter.</p> + be used to run tests both in a single test host environment and + in a distributed Common Test environment (Large Scale + Testing). The node parameters in the init term are only + relevant in the latter (see the + <seealso marker="ct_master_chapter#test_specifications">Large + Scale Testing</seealso> chapter for information). For details on + the event_handler term, see the + <seealso marker="event_handler_chapter#event_handling">Event + Handling</seealso> chapter.</p> <p>Config terms:</p> <pre> {node, NodeAlias, Node}. @@ -379,11 +410,17 @@ {init, InitOptions}. {init, [NodeAlias], InitOptions}. + {label, Label}. + {label, NodeRefs, Label}. + {multiply_timetraps, N}. + {multiply_timetraps, NodeRefs, N}. + {scale_timetraps, Bool}. + {scale_timetraps, NodeRefs, Bool}. {cover, CoverSpecFile}. - {cover, NodeRef, CoverSpecFile}. + {cover, NodeRefs, CoverSpecFile}. {include, IncludeDirs}. {include, NodeRefs, IncludeDirs}. @@ -409,6 +446,12 @@ {suites, DirRef, Suites}. {suites, NodeRefs, DirRef, Suites}. + {groups, DirRef, Suite, Groups}. + {groups, NodeRefsDirRef, Suite, Groups}. + + {groups, DirRef, Suite, Group, {cases,Cases}}. + {groups, NodeRefsDirRef, Suite, Group, {cases,Cases}}. + {cases, DirRef, Suite, Cases}. {cases, NodeRefs, DirRef, Suite, Cases}. @@ -437,6 +480,9 @@ InitArgs = [term()] DirRef = DirAlias | Dir Suites = atom() | [atom()] | all + Suite = atom() + Groups = atom() | [atom()] | all + Group = atom() Cases = atom() | [atom()] | all Comment = string() | "" </pre> diff --git a/lib/common_test/priv/Makefile.in b/lib/common_test/priv/Makefile.in index f144847a29..6372bbc8d5 100644 --- a/lib/common_test/priv/Makefile.in +++ b/lib/common_test/priv/Makefile.in @@ -56,8 +56,9 @@ ifneq ($(findstring win32,$(TARGET)),win32) # # Files # -FILES = vts.tool run_test.in -SCRIPTS = install.sh +FILES = +SCRIPTS = +IMAGES = tile1.jpg # # Rules @@ -83,14 +84,12 @@ include $(ERL_TOP)/make/otp_release_targets.mk ifeq ($(XNIX),true) release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv/bin - $(INSTALL_SCRIPT) $(SCRIPTS) $(RELSYSDIR) - $(INSTALL_DATA) $(FILES) $(RELSYSDIR)/priv + $(INSTALL_DIR) $(RELSYSDIR)/priv + $(INSTALL_DATA) $(FILES) $(IMAGES) $(RELSYSDIR)/priv else release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv/bin - $(INSTALL_SCRIPT) $(SCRIPTS) $(RELSYSDIR) - $(INSTALL_DATA) $(FILES) $(RELSYSDIR)/priv + $(INSTALL_DIR) $(RELSYSDIR)/priv + $(INSTALL_DATA) $(FILES) $(IMAGES) $(RELSYSDIR)/priv endif release_docs_spec: @@ -105,6 +104,7 @@ else # Files # FILES = vts.tool +IMAGES = tile1.jpg # # Rules @@ -123,8 +123,8 @@ clean: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv/bin - $(INSTALL_DATA) $(FILES) $(RELSYSDIR)/priv + $(INSTALL_DIR) $(RELSYSDIR)/priv + $(INSTALL_DATA) $(FILES) $(IMAGES) $(RELSYSDIR)/priv release_docs_spec: diff --git a/lib/common_test/priv/tile1.jpg b/lib/common_test/priv/tile1.jpg Binary files differnew file mode 100644 index 0000000000..8749383716 --- /dev/null +++ b/lib/common_test/priv/tile1.jpg diff --git a/lib/common_test/src/ct.erl b/lib/common_test/src/ct.erl index 0d82a86e7d..8ae175f10d 100644 --- a/lib/common_test/src/ct.erl +++ b/lib/common_test/src/ct.erl @@ -138,10 +138,10 @@ run(TestDirs) -> %%%----------------------------------------------------------------- %%% @spec run_test(Opts) -> Result %%% Opts = [OptTuples] -%%% OptTuples = {config,CfgFiles} | {dir,TestDirs} | {suite,Suites} | -%%% {userconfig, UserConfig} | -%%% {testcase,Cases} | {group,Groups} | {spec,TestSpecs} | -%%% {allow_user_terms,Bool} | {logdir,LogDir} | +%%% OptTuples = {dir,TestDirs} | {suite,Suites} | {group,Groups} | +%%% {testcase,Cases} | {spec,TestSpecs} | {label,Label} | +%%% {config,CfgFiles} | {userconfig, UserConfig} | +%%% {allow_user_terms,Bool} | {logdir,LogDir} | %%% {silent_connections,Conns} | {stylesheet,CSSFile} | %%% {cover,CoverSpecFile} | {step,StepOpts} | %%% {event_handler,EventHandlers} | {include,InclDirs} | @@ -149,15 +149,16 @@ run(TestDirs) -> %%% {repeat,N} | {duration,DurTime} | {until,StopTime} | %%% {force_stop,Bool} | {decrypt,DecryptKeyOrFile} | %%% {refresh_logs,LogDir} | {basic_html,Bool} -%%% CfgFiles = [string()] | string() -%%% UserConfig = [{CallbackMod,CfgStrings}] | {CallbackMod,CfgStrings} -%%% CallbackMod = atom() -%%% CfgStrings = [string()] | string() %%% TestDirs = [string()] | string() %%% Suites = [string()] | string() %%% Cases = [atom()] | atom() %%% Groups = [atom()] | atom() %%% TestSpecs = [string()] | string() +%%% Label = string() | atom() +%%% CfgFiles = [string()] | string() +%%% UserConfig = [{CallbackMod,CfgStrings}] | {CallbackMod,CfgStrings} +%%% CallbackMod = atom() +%%% CfgStrings = [string()] | string() %%% LogDir = string() %%% Conns = all | [atom()] %%% CSSFile = string() @@ -177,7 +178,8 @@ run(TestDirs) -> %%% DecryptFile = string() %%% Result = [TestResult] | {error,Reason} %%% @doc Run tests as specified by the combination of options in <code>Opts</code>. -%%% The options are the same as those used with the <code>run_test</code> program. +%%% The options are the same as those used with the +%%% <seealso marker="run_test#run_test"><code>run_test</code></seealso> program. %%% Note that here a <code>TestDir</code> can be used to point out the path to %%% a <code>Suite</code>. Note also that the option <code>testcase</code> %%% corresponds to the <code>-case</code> option in the <code>run_test</code> diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl index 3dd1026f13..f2ca023cff 100644 --- a/lib/common_test/src/ct_framework.erl +++ b/lib/common_test/src/ct_framework.erl @@ -27,8 +27,12 @@ -export([init_tc/3, end_tc/3, get_suite/2, report/2, warn/1]). -export([error_notification/4]). +-export([overview_html_header/1]). + -export([error_in_suite/1, ct_init_per_group/2, ct_end_per_group/2]). +-export([make_all_conf/3, make_conf/5]). + -include("ct_event.hrl"). -include("ct_util.hrl"). @@ -101,7 +105,8 @@ init_tc1(Mod,Func,[Config0],DoInit) when is_list(Config0) -> [{saved_config,{LastFunc,SavedConfig}} | lists:keydelete(saved_config,1,Config0)]; {{LastSuite,InitOrEnd},SavedConfig} when InitOrEnd == init_per_suite ; - InitOrEnd == end_per_suite -> % last suite + InitOrEnd == end_per_suite -> + %% last suite [{saved_config,{LastSuite,SavedConfig}} | lists:keydelete(saved_config,1,Config0)]; undefined -> @@ -649,7 +654,7 @@ get_suite(Mod, all) -> {'EXIT',_} -> get_all(Mod, []); GroupDefs when is_list(GroupDefs) -> - case catch check_groups(Mod, GroupDefs) of + case catch 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 @@ -664,102 +669,193 @@ get_suite(Mod, all) -> %%!============================================================ %%! Note: The handling of sequences in get_suite/2 and get_all/2 -%%! is deprecated and should be removed after OTP R13! +%%! is deprecated and should be removed at some point... %%!============================================================ -get_suite(Mod, Name) -> - %% Name may be name of a group or a test case. If it's a group, - %% it should be expanded to list of cases (in a conf term) +%% group +get_suite(Mod, Group={conf,Props,_Init,TCs,_End}) -> + Name = proplists:get_value(name, Props), case catch apply(Mod, groups, []) of {'EXIT',_} -> - get_seq(Mod, Name); + [Group]; GroupDefs when is_list(GroupDefs) -> - case catch check_groups(Mod, GroupDefs) of + case catch 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]]}]; + [] -> + {error,{invalid_group_spec,Name}}; ConfTests -> - - %%! --- Thu Jun 3 19:13:22 2010 --- peppe was here! - %%! HEERE! - %%! Must be able to search recursively for group Name, - %%! this only handles top level groups! - - FindConf = fun({conf,Props,_,_,_}) -> - case proplists:get_value(name, Props) of - Name -> true; - _ -> false - end - end, - case lists:filter(FindConf, ConfTests) of - [] -> % must be a test case - get_seq(Mod, Name); - [ConfTest|_] -> - ConfTest + 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 proplists:get_value(name, element(2, hd(ConfTests))) of + Name -> % top group + ConfTests; + _ -> + [] + end; + false -> + ConfTests end end; _ -> E = "Bad return value from "++atom_to_list(Mod)++":groups/0", [{?MODULE,error_in_suite,[[{error,list_to_atom(E)}]]}] - end. + end; -check_groups(_Mod, []) -> - []; -check_groups(Mod, Defs) -> - check_groups(Mod, Defs, Defs, []). +%% testcase +get_suite(Mod, Name) -> + get_seq(Mod, Name). -check_groups(Mod, [TC | Gs], Defs, Levels) when is_atom(TC), length(Levels)>0 -> - [TC | check_groups(Mod, Gs, Defs, Levels)]; +%%%----------------------------------------------------------------- -check_groups(Mod, [{group,SubName} | Gs], Defs, Levels) when is_atom(SubName) -> - case lists:member(SubName, Levels) of - true -> - E = "Cyclic reference to group "++atom_to_list(SubName)++ - " in "++atom_to_list(Mod)++":groups/0", - throw({error,list_to_atom(E)}); - false -> - case find_group(Mod, SubName, Defs) of - {error,_} = Error -> - throw(Error); - G -> - [check_groups(Mod, [G], Defs, Levels) | - check_groups(Mod, Gs, Defs, Levels)] - end +find_groups(Mod, Name, TCs, GroupDefs) -> + Found = find(Mod, Name, TCs, GroupDefs, [], GroupDefs, false), + Trimmed = trim(Found), + delete_subs(Trimmed, Trimmed). + +find(Mod, all, _TCs, [{Name,Props,Tests} | Gs], Known, Defs, _) -> + cyclic_test(Mod, Name, Known), + [make_conf(Mod, Name, Props, + find(Mod, all, all, Tests, [Name | Known], Defs, true)) | + find(Mod, all, all, Gs, [], Defs, true)]; + +find(Mod, Name, TCs, [{Name,Props,Tests} | _Gs], Known, Defs, false) + when is_atom(Name), is_list(Props), is_list(Tests) -> + cyclic_test(Mod, Name, Known), + case TCs of + all -> + [make_conf(Mod, Name, Props, + find(Mod, Name, TCs, Tests, [Name | Known], Defs, true))]; + _ -> + Tests1 = [TC || TC <- TCs, + lists:member(TC, Tests) == true], + [make_conf(Mod, Name, Props, Tests1)] end; -check_groups(Mod, [{Name,Tests} | Gs], Defs, Levels) when is_atom(Name), - is_list(Tests) -> - check_groups(Mod, [{Name,[],Tests} | Gs], Defs, Levels); - -check_groups(Mod, [{Name,Props,Tests} | Gs], Defs, Levels) when is_atom(Name), - is_list(Props), - is_list(Tests) -> - {TestSpec,Levels1} = - case Levels of - [] -> - {check_groups(Mod, Tests, Defs, [Name]),[]}; - _ -> - {check_groups(Mod, Tests, Defs, [Name|Levels]),Levels} - end, - [make_conf(Mod, Name, Props, TestSpec) | - check_groups(Mod, Gs, Defs, Levels1)]; +find(Mod, Name, TCs, [{Name1,Props,Tests} | Gs], Known, Defs, false) + when is_atom(Name1), is_list(Props), is_list(Tests) -> + cyclic_test(Mod, Name1, Known), + [make_conf(Mod, Name1, Props, + find(Mod, Name, TCs, Tests, [Name1 | Known], Defs, false)) | + find(Mod, Name, TCs, Gs, [], Defs, false)]; + +find(Mod, Name, _TCs, [{Name,_Props,_Tests} | _Gs], _Known, _Defs, true) + when is_atom(Name) -> + E = "Duplicate groups named "++atom_to_list(Name)++" in "++ + atom_to_list(Mod)++":groups/0", + throw({error,list_to_atom(E)}); + +find(Mod, Name, all, [{Name1,Props,Tests} | Gs], Known, Defs, true) + when is_atom(Name1), is_list(Props), is_list(Tests) -> + cyclic_test(Mod, Name1, Known), + [make_conf(Mod, Name1, Props, + find(Mod, Name, all, Tests, [Name1 | Known], Defs, true)) | + find(Mod, Name, all, Gs, [], Defs, true)]; + +find(Mod, Name, TCs, [{group,Name1} | Gs], Known, Defs, Found) when is_atom(Name1) -> + find(Mod, Name, TCs, [expand(Mod, Name1, Defs) | Gs], Known, Defs, Found); + +find(Mod, Name, TCs, [{Name1,Tests} | Gs], Known, Defs, Found) + when is_atom(Name1), is_list(Tests) -> + find(Mod, Name, TCs, [{Name1,[],Tests} | Gs], Known, Defs, Found); + +find(Mod, Name, TCs, [TC | Gs], Known, Defs, false) when is_atom(TC) -> + find(Mod, Name, TCs, Gs, Known, Defs, false); -check_groups(Mod, [BadTerm | _Gs], _Defs, Levels) -> - Where = if length(Levels) == 0 -> +find(Mod, Name, TCs, [TC | Gs], Known, Defs, true) when is_atom(TC) -> + [TC | find(Mod, Name, TCs, Gs, Known, Defs, true)]; + +find(Mod, _Name, _TCs, [BadTerm | _Gs], Known, _Defs, _Found) -> + Where = if length(Known) == 0 -> atom_to_list(Mod)++":groups/0"; true -> - "group "++atom_to_list(lists:last(Levels))++ + "group "++atom_to_list(lists:last(Known))++ " in "++atom_to_list(Mod)++":groups/0" end, Term = io_lib:format("~p", [BadTerm]), E = "Bad term "++lists:flatten(Term)++" in "++Where, throw({error,list_to_atom(E)}); -check_groups(_Mod, [], _Defs, _) -> +find(_Mod, _Name, _TCs, [], _Known, _Defs, false) -> + ['$NOMATCH']; + +find(_Mod, _Name, _TCs, [], _Known, _Defs, _Found) -> + []. + +delete_subs([Conf | Confs], All) -> + All1 = delete_conf(Conf, All), + case is_sub(Conf, All1) of + true -> + delete_subs(Confs, All1); + false -> + delete_subs(Confs, All) + end; + +delete_subs([], All) -> + All. + +delete_conf({conf,Props,_,_,_}, Confs) -> + Name = proplists:get_value(name, Props), + [Conf || Conf = {conf,Props0,_,_,_} <- Confs, + Name =/= proplists:get_value(name, Props0)]. + +is_sub({conf,Props,_,_,_}=Conf, [{conf,_,_,Tests,_} | Confs]) -> + Name = proplists:get_value(name, Props), + case lists:any(fun({conf,Props0,_,_,_}) -> + case proplists:get_value(name, Props0) of + N when N == Name -> + true; + _ -> + false + end; + (_) -> + false + end, Tests) of + true -> + true; + false -> + is_sub(Conf, Tests) or is_sub(Conf, Confs) + end; + +is_sub(Conf, [_TC | Tests]) -> + is_sub(Conf, Tests); + +is_sub(_Conf, []) -> + false. + +trim(['$NOMATCH' | Tests]) -> + trim(Tests); + +trim([{conf,Props,Init,Tests,End} | Confs]) -> + case trim(Tests) of + [] -> + trim(Confs); + Trimmed -> + [{conf,Props,Init,Trimmed,End} | trim(Confs)] + end; + +trim([TC | Tests]) -> + [TC | trim(Tests)]; + +trim([]) -> []. -find_group(Mod, Name, Defs) -> +cyclic_test(Mod, Name, Names) -> + case lists:member(Name, Names) of + true -> + E = "Cyclic reference to group "++atom_to_list(Name)++ + " in "++atom_to_list(Mod)++":groups/0", + throw({error,list_to_atom(E)}); + false -> + ok + end. + +expand(Mod, Name, Defs) -> case lists:keysearch(Name, 1, Defs) of {value,Def} -> Def; @@ -769,7 +865,48 @@ find_group(Mod, Name, Defs) -> throw({error,list_to_atom(E)}) end. +make_all_conf(Dir, Mod, _Props) -> + case code:is_loaded(Mod) of + false -> + code:load_abs(filename:join(Dir,atom_to_list(Mod))); + _ -> + ok + end, + make_all_conf(Mod). + +make_all_conf(Mod) -> + case catch apply(Mod, groups, []) of + {'EXIT',_} -> + {error,{invalid_group_definition,Mod}}; + GroupDefs when is_list(GroupDefs) -> + case catch 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]]}]; + [] -> + {error,{invalid_group_spec,Mod}}; + ConfTests -> + [{conf,Props,Init,all,End} || {conf,Props,Init,_,End} <- ConfTests] + end + end. + +make_conf(Dir, Mod, Name, Props, TestSpec) -> + case code:is_loaded(Mod) of + false -> + code:load_abs(filename:join(Dir,atom_to_list(Mod))); + _ -> + ok + end, + make_conf(Mod, Name, Props, TestSpec). + make_conf(Mod, Name, Props, TestSpec) -> + case code:is_loaded(Mod) of + false -> + code:load_file(Mod); + _ -> + ok + end, {InitConf,EndConf} = case erlang:function_exported(Mod,init_per_group,2) of true -> @@ -780,6 +917,7 @@ make_conf(Mod, Name, Props, TestSpec) -> end, {conf,[{name,Name}|Props],InitConf,TestSpec,EndConf}. +%%%----------------------------------------------------------------- get_all(Mod, ConfTests) -> case catch apply(Mod, all, []) of @@ -1095,4 +1233,31 @@ add_data_dir(File,Config) when is_list(File) -> File end. +%%%----------------------------------------------------------------- +%%% @spec overview_html_header(TestName) -> Header +overview_html_header(TestName) -> + TestName1 = lists:flatten(io_lib:format("~p", [TestName])), + Label = case application:get_env(common_test, test_label) of + {ok,Lbl} when Lbl =/= undefined -> + "<H1><FONT color=\"green\">" ++ Lbl ++ "</FONT></H1>\n"; + _ -> + "" + end, + Bgr = case ct_logs:basic_html() of + true -> + ""; + false -> + CTPath = code:lib_dir(common_test), + TileFile = filename:join(filename:join(CTPath,"priv"),"tile1.jpg"), + " background=\"" ++ TileFile ++ "\"" + end, + + ["<html>\n", + "<head><title>Test ", TestName1, " results</title>\n", + "<meta http-equiv=\"cache-control\" content=\"no-cache\">\n", + "</head>\n", + "<body", Bgr, " bgcolor=\"white\" text=\"black\" ", + "link=\"blue\" vlink=\"purple\" alink=\"red\">\n", + Label, + "<H2>Results from test ", TestName1, "</H2>\n"]. diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl index 5683d06aa7..f8ae7202e6 100644 --- a/lib/common_test/src/ct_logs.erl +++ b/lib/common_test/src/ct_logs.erl @@ -36,7 +36,8 @@ -export([make_all_suites_index/1,make_all_runs_index/1]). %% Logging stuff directly from testcase --export([tc_log/3,tc_print/3,tc_pal/3]). +-export([tc_log/3,tc_print/3,tc_pal/3, + basic_html/0]). %% Simulate logger process for use without ct environment running -export([simulate/0]). @@ -57,7 +58,7 @@ -define(table_color2,"#E4F0FE"). -define(table_color3,"#F0F8FF"). --define(testname_width, 70). +-define(testname_width, 60). -define(abs(Name), filename:absname(Name)). @@ -716,7 +717,7 @@ make_last_run_index1(StartTime,IndexName) -> [Log]; Logs -> case read_totals_file(?totals_name) of - {_Node,Logs0,_Totals} -> + {_Node,_Lbl,Logs0,_Totals} -> insert_dirs(Logs,Logs0); _ -> %% someone deleted the totals file!? @@ -728,10 +729,15 @@ make_last_run_index1(StartTime,IndexName) -> {ok,Bin} -> binary_to_term(Bin); _ -> [] end, - {ok,Index0,Totals} = make_last_run_index(Logs1, index_header(StartTime), + Label = case application:get_env(common_test, test_label) of + {ok,Lbl} -> Lbl; + _ -> undefined + end, + {ok,Index0,Totals} = make_last_run_index(Logs1, + index_header(Label,StartTime), 0, 0, 0, 0, 0, Missing), %% write current Totals to file, later to be used in all_runs log - write_totals_file(?totals_name,Logs1,Totals), + write_totals_file(?totals_name,Label,Logs1,Totals), Index = [Index0|index_footer()], case force_write_file(IndexName, Index) of ok -> @@ -761,7 +767,7 @@ make_last_run_index([Name|Rest], Result, TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt, Missing); LastLogDir -> SuiteName = filename:rootname(filename:basename(Name)), - case make_one_index_entry(SuiteName, LastLogDir, false, Missing) of + case make_one_index_entry(SuiteName, LastLogDir, "-", false, Missing) of {Result1,Succ,Fail,USkip,ASkip,NotBuilt} -> %% for backwards compatibility AutoSkip1 = case catch AutoSkip+ASkip of @@ -780,18 +786,18 @@ make_last_run_index([], Result, TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuil {ok, [Result|total_row(TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt, false)], {TotSucc,TotFail,UserSkip,AutoSkip,TotNotBuilt}}. -make_one_index_entry(SuiteName, LogDir, All, Missing) -> +make_one_index_entry(SuiteName, LogDir, Label, All, Missing) -> case count_cases(LogDir) of {Succ,Fail,UserSkip,AutoSkip} -> NotBuilt = not_built(SuiteName, LogDir, All, Missing), - NewResult = make_one_index_entry1(SuiteName, LogDir, Succ, Fail, + NewResult = make_one_index_entry1(SuiteName, LogDir, Label, Succ, Fail, UserSkip, AutoSkip, NotBuilt, All), {NewResult,Succ,Fail,UserSkip,AutoSkip,NotBuilt}; error -> error end. -make_one_index_entry1(SuiteName, Link, Success, Fail, UserSkip, AutoSkip, +make_one_index_entry1(SuiteName, Link, Label, Success, Fail, UserSkip, AutoSkip, NotBuilt, All) -> LogFile = filename:join(Link, ?suitelog_name ++ ".html"), CrashDumpName = SuiteName ++ "_erl_crash.dump", @@ -803,7 +809,7 @@ make_one_index_entry1(SuiteName, Link, Success, Fail, UserSkip, AutoSkip, false -> "" end, - {Timestamp,Node,AllInfo} = + {Lbl,Timestamp,Node,AllInfo} = case All of {true,OldRuns} -> [_Prefix,NodeOrDate|_] = string:tokens(Link,"."), @@ -811,20 +817,21 @@ make_one_index_entry1(SuiteName, Link, Success, Fail, UserSkip, AutoSkip, 0 -> "-"; _ -> NodeOrDate end, - N = ["<TD ALIGN=right>",Node1,"</TD>\n"], + N = ["<TD ALIGN=right><FONT SIZE=-1>",Node1,"</FONT></TD>\n"], CtRunDir = filename:dirname(filename:dirname(Link)), - T = ["<TD>",timestamp(CtRunDir),"</TD>\n"], + L = ["<TD ALIGN=center><FONT SIZE=-1><B>",Label,"</FONT></B></TD>\n"], + T = ["<TD><FONT SIZE=-1>",timestamp(CtRunDir),"</FONT></TD>\n"], CtLogFile = filename:join(CtRunDir,?ct_log_name), OldRunsLink = case OldRuns of [] -> "none"; _ -> "<A HREF=\""++?all_runs_name++"\">Old Runs</A>" end, - A=["<TD><A HREF=\"",CtLogFile,"\">CT Log</A></TD>\n", - "<TD>",OldRunsLink,"</TD>\n"], - {T,N,A}; + A=["<TD><FONT SIZE=-1><A HREF=\"",CtLogFile,"\">CT Log</A></FONT></TD>\n", + "<TD><FONT SIZE=-1>",OldRunsLink,"</FONT></TD>\n"], + {L,T,N,A}; false -> - {"","",""} + {"","","",""} end, NotBuiltStr = if NotBuilt == 0 -> @@ -851,7 +858,8 @@ make_one_index_entry1(SuiteName, Link, Success, Fail, UserSkip, AutoSkip, {UserSkip+AutoSkip,integer_to_list(UserSkip),ASStr} end, ["<TR valign=top>\n", - "<TD><A HREF=\"",LogFile,"\">",SuiteName,"</A>",CrashDumpLink,"</TD>\n", + "<TD><FONT SIZE=-1><A HREF=\"",LogFile,"\">",SuiteName,"</A>",CrashDumpLink,"</FONT></TD>\n", + Lbl, Timestamp, "<TD ALIGN=right>",integer_to_list(Success),"</TD>\n", "<TD ALIGN=right>",FailStr,"</TD>\n", @@ -862,12 +870,14 @@ make_one_index_entry1(SuiteName, Link, Success, Fail, UserSkip, AutoSkip, AllInfo, "</TR>\n"]. total_row(Success, Fail, UserSkip, AutoSkip, NotBuilt, All) -> - {TimestampCell,AllInfo} = + {Label,TimestampCell,AllInfo} = case All of - true -> - {"<TD> </TD>\n","<TD> </TD>\n<TD> </TD>\n"}; + true -> + {"<TD> </TD>\n", + "<TD> </TD>\n", + "<TD> </TD>\n<TD> </TD>\n"}; false -> - {"",""} + {"","",""} end, {AllSkip,UserSkipStr,AutoSkipStr} = @@ -877,6 +887,7 @@ total_row(Success, Fail, UserSkip, AutoSkip, NotBuilt, All) -> end, ["<TR valign=top>\n", "<TD><B>Total</B></TD>", + Label, TimestampCell, "<TD ALIGN=right><B>",integer_to_list(Success),"<B></TD>\n", "<TD ALIGN=right><B>",integer_to_list(Fail),"<B></TD>\n", @@ -937,13 +948,21 @@ term_to_text(Term) -> %%% Headers and footers. -index_header(StartTime) -> - [header("Test Results " ++ format_time(StartTime)) | +index_header(Label, StartTime) -> + Head = + case Label of + undefined -> + header("Test Results", format_time(StartTime)); + _ -> + header("Test Results for \"" ++ Label ++ "\"", + format_time(StartTime)) + end, + [Head | ["<CENTER>\n", "<P><A HREF=\"",?ct_log_name,"\">Common Test Framework Log</A></P>", "<TABLE border=\"3\" cellpadding=\"5\" " "BGCOLOR=\"",?table_color3,"\">\n" - "<th><B>Name</B></th>\n", + "<th><B>Test Name</B></th>\n", "<th><font color=\"",?table_color3,"\">_</font>Ok" "<font color=\"",?table_color3,"\">_</font></th>\n" "<th>Failed</th>\n", @@ -952,13 +971,17 @@ index_header(StartTime) -> "\n"]]. all_suites_index_header() -> + {ok,Cwd} = file:get_cwd(), + LogDir = filename:basename(Cwd), + AllRuns = "All test runs in \"" ++ LogDir ++ "\"", [header("Test Results") | ["<CENTER>\n", - "<A HREF=\"",?all_runs_name,"\">All Test Runs in this directory</A>\n", + "<A HREF=\"",?all_runs_name,"\">",AllRuns,"</A>\n", "<br><br>\n", "<TABLE border=\"3\" cellpadding=\"5\" " "BGCOLOR=\"",?table_color2,"\">\n" - "<th>Name</th>\n", + "<th>Test Name</th>\n", + "<th>Label</th>\n", "<th>Test Run Started</th>\n", "<th><font color=\"",?table_color2,"\">_</font>Ok" "<font color=\"",?table_color2,"\">_</font></th>\n" @@ -971,13 +994,17 @@ all_suites_index_header() -> "\n"]]. all_runs_header() -> - [header("All test runs in current directory") | + {ok,Cwd} = file:get_cwd(), + LogDir = filename:basename(Cwd), + Title = "All test runs in \"" ++ LogDir ++ "\"", + [header(Title) | ["<CENTER><TABLE border=\"3\" cellpadding=\"5\" " "BGCOLOR=\"",?table_color1,"\">\n" "<th><B>History</B></th>\n" "<th><B>Node</B></th>\n" + "<th><B>Label</B></th>\n" "<th>Tests</th>\n" - "<th><B>Names</B></th>\n" + "<th><B>Test Names</B></th>\n" "<th>Total</th>\n" "<th><font color=\"",?table_color1,"\">_</font>Ok" "<font color=\"",?table_color1,"\">_</font></th>\n" @@ -987,12 +1014,23 @@ all_runs_header() -> "\n"]]. header(Title) -> + header1(Title, ""). +header(Title, SubTitle) -> + header1(Title, SubTitle). + +header1(Title, SubTitle) -> + SubTitleHTML = if SubTitle =/= "" -> + ["<CENTER>\n", + "<H2>" ++ SubTitle ++ "</H2>\n", + "</CENTER>\n<BR>\n"]; + true -> "<BR>\n" + end, ["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n" "<!-- autogenerated by '"++atom_to_list(?MODULE)++"'. -->\n" "<HTML>\n", "<HEAD>\n", - "<TITLE>" ++ Title ++ "</TITLE>\n", + "<TITLE>" ++ Title ++ " " ++ SubTitle ++ "</TITLE>\n", "<META HTTP-EQUIV=\"CACHE-CONTROL\" CONTENT=\"NO-CACHE\">\n", "</HEAD>\n", @@ -1004,6 +1042,7 @@ header(Title) -> "<CENTER>\n", "<H1>" ++ Title ++ "</H1>\n", "</CENTER>\n", + SubTitleHTML, "<!-- ---- CONTENT ---- -->\n"]. @@ -1013,19 +1052,28 @@ index_footer() -> footer() -> ["<P><CENTER>\n" - "<HR>\n" - "<P><FONT SIZE=-1>\n" - "Copyright © ", year(), - " <A HREF=\"http://erlang.ericsson.se\">Open Telecom Platform</A><BR>\n" - "Updated: <!date>", current_time(), "<!/date><BR>\n" - "</FONT>\n" - "</CENTER>\n" - "</body>\n"]. + "<BR><BR>\n" + "<HR>\n" + "<P><FONT SIZE=-1>\n" + "Copyright © ", year(), + " <A HREF=\"http://erlang.ericsson.se\">Open Telecom Platform</A><BR>\n" + "Updated: <!date>", current_time(), "<!/date><BR>\n" + "</FONT>\n" + "</CENTER>\n" + "</body>\n"]. body_tag() -> - "<body bgcolor=\"#FFFFFF\" text=\"#000000\" link=\"#0000FF\"" - "vlink=\"#800080\" alink=\"#FF0000\">\n". + case basic_html() of + true -> + "<body bgcolor=\"#FFFFFF\" text=\"#000000\" link=\"#0000FF\" " + "vlink=\"#800080\" alink=\"#FF0000\">\n"; + false -> + CTPath = code:lib_dir(common_test), + TileFile = filename:join(filename:join(CTPath,"priv"),"tile1.jpg"), + "<body background=\"" ++ TileFile ++ "\" bgcolor=\"#FFFFFF\" text=\"#000000\" link=\"#0000FF\" " + "vlink=\"#800080\" alink=\"#FF0000\">\n" + end. current_time() -> format_time(calendar:local_time()). @@ -1217,7 +1265,7 @@ runentry(Dir, BasicHtml) -> TotalsFile = filename:join(Dir,?totals_name), TotalsStr = case read_totals_file(TotalsFile) of - {Node,Logs,{TotSucc,TotFail,UserSkip,AutoSkip,NotBuilt}} -> + {Node,Label,Logs,{TotSucc,TotFail,UserSkip,AutoSkip,NotBuilt}} -> TotFailStr = if TotFail > 0 -> ["<FONT color=\"red\">", @@ -1263,6 +1311,7 @@ runentry(Dir, BasicHtml) -> end, Total = TotSucc+TotFail+AllSkip, A = ["<TD ALIGN=center><FONT SIZE=-1>",Node,"</FONT></TD>\n", + "<TD ALIGN=center><FONT SIZE=-1><B>",Label,"</B></FONT></TD>\n", "<TD ALIGN=right>",NoOfTests,"</TD>\n"], B = if BasicHtml -> ["<TD ALIGN=center><FONT SIZE=-1>",TestNamesTrunc,"</FONT></TD>\n"]; @@ -1283,17 +1332,19 @@ runentry(Dir, BasicHtml) -> end, Index = filename:join(Dir,?index_name), ["<TR>\n" - "<TD><A HREF=\"",Index,"\">",timestamp(Dir),"</A>",TotalsStr,"</TD>\n" + "<TD><FONT SIZE=-1><A HREF=\"",Index,"\">",timestamp(Dir),"</A>",TotalsStr,"</FONT></TD>\n" "</TR>\n"]. -write_totals_file(Name,Logs,Totals) -> +write_totals_file(Name,Label,Logs,Totals) -> AbsName = ?abs(Name), notify_and_lock_file(AbsName), force_write_file(AbsName, term_to_binary({atom_to_list(node()), - Logs,Totals})), + Label,Logs,Totals})), notify_and_unlock_file(AbsName). +%% this function needs to convert from old formats to new so that old +%% test results (prev ct versions) can be listed together with new read_totals_file(Name) -> AbsName = ?abs(Name), notify_and_lock_file(AbsName), @@ -1303,12 +1354,23 @@ read_totals_file(Name) -> case catch binary_to_term(Bin) of {'EXIT',_Reason} -> % corrupt file {"-",[],undefined}; - R = {Node,Ls,Tot} -> + {Node,Label,Ls,Tot} -> % all info available + Label1 = case Label of + undefined -> "-"; + _ -> Label + end, + case Tot of + {_Ok,_Fail,_USkip,_ASkip,_NoBuild} -> % latest format + {Node,Label1,Ls,Tot}; + {TotSucc,TotFail,AllSkip,NotBuilt} -> + {Node,Label1,Ls,{TotSucc,TotFail,AllSkip,undefined,NotBuilt}} + end; + {Node,Ls,Tot} -> % no label found case Tot of - {_,_,_,_,_} -> % latest format - R; + {_Ok,_Fail,_USkip,_ASkip,_NoBuild} -> % latest format + {Node,"-",Ls,Tot}; {TotSucc,TotFail,AllSkip,NotBuilt} -> - {Node,Ls,{TotSucc,TotFail,AllSkip,undefined,NotBuilt}} + {Node,"-",Ls,{TotSucc,TotFail,AllSkip,undefined,NotBuilt}} end; %% for backwards compatibility {Ls,Tot} -> {"-",Ls,Tot}; @@ -1411,7 +1473,7 @@ make_all_suites_index1(When,AllSuitesLogDirs) -> make_all_suites_index2(IndexName,AllSuitesLogDirs) -> {ok,Index0,_Totals} = make_all_suites_index3(AllSuitesLogDirs, all_suites_index_header(), - 0, 0, 0, 0, 0), + 0, 0, 0, 0, 0, []), Index = [Index0|index_footer()], case force_write_file(IndexName, Index) of ok -> @@ -1421,14 +1483,25 @@ make_all_suites_index2(IndexName,AllSuitesLogDirs) -> end. make_all_suites_index3([{SuiteName,[LastLogDir|OldDirs]}|Rest], - Result, TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt) -> + Result, TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt, + Labels) -> [EntryDir|_] = filename:split(LastLogDir), Missing = case file:read_file(filename:join(EntryDir,?missing_suites_info)) of {ok,Bin} -> binary_to_term(Bin); _ -> [] end, - case make_one_index_entry(SuiteName, LastLogDir, {true,OldDirs}, Missing) of + {Label,Labels1} = + case proplists:get_value(EntryDir, Labels) of + undefined -> + case read_totals_file(filename:join(EntryDir,?totals_name)) of + {_,Lbl,_,_} -> {Lbl,[{EntryDir,Lbl}|Labels]}; + _ -> {"-",[{EntryDir,"-"}|Labels]} + end; + Lbl -> + {Lbl,Labels} + end, + case make_one_index_entry(SuiteName, LastLogDir, Label, {true,OldDirs}, Missing) of {Result1,Succ,Fail,USkip,ASkip,NotBuilt} -> %% for backwards compatibility AutoSkip1 = case catch AutoSkip+ASkip of @@ -1437,13 +1510,13 @@ make_all_suites_index3([{SuiteName,[LastLogDir|OldDirs]}|Rest], end, make_all_suites_index3(Rest, [Result|Result1], TotSucc+Succ, TotFail+Fail, UserSkip+USkip, AutoSkip1, - TotNotBuilt+NotBuilt); + TotNotBuilt+NotBuilt,Labels1); error -> make_all_suites_index3(Rest, Result, TotSucc, TotFail, - UserSkip, AutoSkip, TotNotBuilt) + UserSkip, AutoSkip, TotNotBuilt,Labels1) end; make_all_suites_index3([], Result, TotSucc, TotFail, UserSkip, AutoSkip, - TotNotBuilt) -> + TotNotBuilt,_) -> {ok, [Result|total_row(TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt,true)], {TotSucc,TotFail,UserSkip,AutoSkip,TotNotBuilt}}. diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl index 6a9c42d1b9..c5bfd01642 100644 --- a/lib/common_test/src/ct_run.erl +++ b/lib/common_test/src/ct_run.erl @@ -45,7 +45,8 @@ -define(abs(Name), filename:absname(Name)). -define(testdir(Name, Suite), ct_util:get_testdir(Name, Suite)). --record(opts, {vts, +-record(opts, {label, + vts, shell, cover, coverspec, @@ -153,14 +154,16 @@ script_start(Args) -> Result end, stop_trace(Tracing), + timer:sleep(1000), Res. script_start1(Parent, Args) -> %% read general start flags + Label = get_start_opt(label, fun([Lbl]) -> Lbl end, Args), Vts = get_start_opt(vts, true, Args), Shell = get_start_opt(shell, true, Args), Cover = get_start_opt(cover, fun([CoverFile]) -> ?abs(CoverFile) end, Args), - LogDir = get_start_opt(logdir, fun([LogD]) -> LogD end, ".", Args), + LogDir = get_start_opt(logdir, fun([LogD]) -> LogD end, Args), MultTT = get_start_opt(multiply_timetraps, fun([MT]) -> list_to_integer(MT) end, 1, Args), ScaleTT = get_start_opt(scale_timetraps, @@ -229,7 +232,7 @@ script_start1(Parent, Args) -> application:set_env(common_test, basic_html, true) end, - StartOpts = #opts{vts = Vts, shell = Shell, cover = Cover, + StartOpts = #opts{label = Label, vts = Vts, shell = Shell, cover = Cover, logdir = LogDir, event_handlers = EvHandlers, include = IncludeDirs, silent_connections = SilentConns, @@ -288,6 +291,9 @@ script_start2(StartOpts = #opts{vts = undefined, TS -> SpecStartOpts = get_data_for_node(TS, node()), + Label = choose_val(StartOpts#opts.label, + SpecStartOpts#opts.label), + LogDir = choose_val(StartOpts#opts.logdir, SpecStartOpts#opts.logdir), @@ -303,7 +309,8 @@ script_start2(StartOpts = #opts{vts = undefined, SpecStartOpts#opts.include]), application:set_env(common_test, include, AllInclude), - {TS,StartOpts#opts{testspecs = Specs, + {TS,StartOpts#opts{label = Label, + testspecs = Specs, cover = Cover, logdir = LogDir, config = SpecStartOpts#opts.config, @@ -317,29 +324,30 @@ script_start2(StartOpts = #opts{vts = undefined, end, %% read config/userconfig from start flags InitConfig = ct_config:prepare_config_list(Args), + TheLogDir = which(logdir, Opts#opts.logdir), case {TestSpec,Terms} of {_,{error,_}=Error} -> Error; {[],_} -> {error,no_testspec_specified}; {undefined,_} -> % no testspec used - case check_and_install_configfiles(InitConfig, - which(logdir,Opts#opts.logdir), + case check_and_install_configfiles(InitConfig, TheLogDir, Opts#opts.event_handlers) of ok -> % go on read tests from start flags - script_start3(Opts#opts{config=InitConfig}, Args); + script_start3(Opts#opts{config=InitConfig, + logdir=TheLogDir}, Args); Error -> Error end; {_,_} -> % testspec used %% merge config from start flags with config from testspec AllConfig = merge_vals([InitConfig, Opts#opts.config]), - case check_and_install_configfiles(AllConfig, - which(logdir,Opts#opts.logdir), + case check_and_install_configfiles(AllConfig, TheLogDir, Opts#opts.event_handlers) of ok -> % read tests from spec {Run,Skip} = ct_testspec:prepare_tests(Terms, node()), - do_run(Run, Skip, Opts#opts{config=AllConfig}, Args); + do_run(Run, Skip, Opts#opts{config=AllConfig, + logdir=TheLogDir}, Args); Error -> Error end @@ -348,11 +356,12 @@ script_start2(StartOpts = #opts{vts = undefined, script_start2(StartOpts, Args) -> %% read config/userconfig from start flags InitConfig = ct_config:prepare_config_list(Args), - case check_and_install_configfiles(InitConfig, - which(logdir,StartOpts#opts.logdir), + LogDir = which(logdir, StartOpts#opts.logdir), + case check_and_install_configfiles(InitConfig, LogDir, StartOpts#opts.event_handlers) of ok -> % go on read tests from start flags - script_start3(StartOpts#opts{config=InitConfig}, Args); + script_start3(StartOpts#opts{config=InitConfig, + logdir=LogDir}, Args); Error -> Error end. @@ -427,8 +436,12 @@ script_start4(#opts{vts = true, config = Config, event_handlers = EvHandlers, end, [], Config), vts:init_data(ConfigFiles, EvHandlers, ?abs(LogDir), Tests); -script_start4(#opts{shell = true, config = Config, event_handlers = EvHandlers, +script_start4(#opts{label = Label, shell = true, config = Config, + event_handlers = EvHandlers, logdir = LogDir, testspecs = Specs}, _Args) -> + %% label - used by ct_logs + application:set_env(common_test, test_label, Label), + InstallOpts = [{config,Config},{event_handler,EvHandlers}], if Config == [] -> ok; @@ -613,9 +626,13 @@ run_test(StartOpts) when is_list(StartOpts) -> end. run_test1(StartOpts) -> + %% label + Label = get_start_opt(label, fun(Lbl) when is_list(Lbl) -> Lbl; + (Lbl) when is_atom(Lbl) -> atom_to_list(Lbl) + end, StartOpts), %% logdir LogDir = get_start_opt(logdir, fun(LD) when is_list(LD) -> LD end, - ".", StartOpts), + StartOpts), %% config & userconfig CfgFiles = ct_config:get_config_file_list(StartOpts), @@ -711,7 +728,8 @@ run_test1(StartOpts) -> %% stepped execution Step = get_start_opt(step, value, StartOpts), - Opts = #opts{cover = Cover, step = Step, logdir = LogDir, config = CfgFiles, + Opts = #opts{label = Label, + cover = Cover, step = Step, logdir = LogDir, config = CfgFiles, event_handlers = EvHandlers, include = Include, silent_connections = SilentConns, stylesheet = Stylesheet, @@ -747,6 +765,8 @@ run_spec_file(Relaxed, exit(CTReason); TS -> SpecOpts = get_data_for_node(TS, node()), + Label = choose_val(Opts#opts.label, + SpecOpts#opts.label), LogDir = choose_val(Opts#opts.logdir, SpecOpts#opts.logdir), AllConfig = merge_vals([CfgFiles, SpecOpts#opts.config]), @@ -766,8 +786,9 @@ run_spec_file(Relaxed, which(logdir,LogDir), AllEvHs) of ok -> - Opts1 = Opts#opts{cover = Cover, - logdir = LogDir, + Opts1 = Opts#opts{label = Label, + cover = Cover, + logdir = which(logdir, LogDir), config = AllConfig, event_handlers = AllEvHs, include = AllInclude, @@ -775,7 +796,7 @@ run_spec_file(Relaxed, multiply_timetraps = MultTT, scale_timetraps = ScaleTT}, {Run,Skip} = ct_testspec:prepare_tests(TS, node()), - do_run(Run, Skip, Opts1, StartOpts); + reformat_result(catch do_run(Run, Skip, Opts1, StartOpts)); {error,GCFReason} -> exit(GCFReason) end @@ -785,9 +806,11 @@ run_prepared(Run, Skip, Opts = #opts{logdir = LogDir, config = CfgFiles, event_handlers = EvHandlers}, StartOpts) -> - case check_and_install_configfiles(CfgFiles, LogDir, EvHandlers) of + LogDir1 = which(logdir, LogDir), + case check_and_install_configfiles(CfgFiles, LogDir1, EvHandlers) of ok -> - do_run(Run, Skip, Opts, StartOpts); + reformat_result(catch do_run(Run, Skip, Opts#opts{logdir = LogDir1}, + StartOpts)); {error,Reason} -> exit(Reason) end. @@ -816,6 +839,8 @@ check_config_file(Callback, File)-> run_dir(Opts = #opts{logdir = LogDir, config = CfgFiles, event_handlers = EvHandlers}, StartOpts) -> + LogDir1 = which(logdir, LogDir), + Opts1 = Opts#opts{logdir = LogDir1}, AbsCfgFiles = lists:map(fun({Callback,FileList})-> case code:is_loaded(Callback) of @@ -834,7 +859,7 @@ run_dir(Opts = #opts{logdir = LogDir, check_config_file(Callback, File) end, FileList)} end, CfgFiles), - case install([{config,AbsCfgFiles},{event_handler,EvHandlers}], LogDir) of + case install([{config,AbsCfgFiles},{event_handler,EvHandlers}], LogDir1) of ok -> ok; {error,IReason} -> exit(IReason) end, @@ -842,7 +867,7 @@ run_dir(Opts = #opts{logdir = LogDir, {value,{_,Dirs=[Dir|_]}} when not is_integer(Dir), length(Dirs)>1 -> %% multiple dirs (no suite) - do_run(tests(Dirs), [], Opts, StartOpts); + reformat_result(catch do_run(tests(Dirs), [], Opts1, StartOpts)); false -> % no dir %% fun for converting suite name to {Dir,Mod} tuple S2M = fun(S) when is_list(S) -> @@ -857,12 +882,15 @@ run_dir(Opts = #opts{logdir = LogDir, case listify(proplists:get_value(group, StartOpts, [])) ++ listify(proplists:get_value(testcase, StartOpts, [])) of [] -> - do_run(tests(Dir, listify(Mod)), [], Opts, StartOpts); + reformat_result(catch do_run(tests(Dir, listify(Mod)), + [], Opts1, StartOpts)); GsAndCs -> - do_run(tests(Dir, Mod, GsAndCs), [], Opts, StartOpts) + reformat_result(catch do_run(tests(Dir, Mod, GsAndCs), + [], Opts1, StartOpts)) end; {value,{_,Suites}} -> - do_run(tests(lists:map(S2M, Suites)), [], Opts, StartOpts); + reformat_result(catch do_run(tests(lists:map(S2M, Suites)), + [], Opts1, StartOpts)); _ -> exit(no_tests_specified) end; @@ -875,17 +903,22 @@ run_dir(Opts = #opts{logdir = LogDir, case listify(proplists:get_value(group, StartOpts, [])) ++ listify(proplists:get_value(testcase, StartOpts, [])) of [] -> - do_run(tests(Dir, listify(Mod)), [], Opts, StartOpts); + reformat_result(catch do_run(tests(Dir, listify(Mod)), + [], Opts1, StartOpts)); GsAndCs -> - do_run(tests(Dir, Mod, GsAndCs), [], Opts, StartOpts) + reformat_result(catch do_run(tests(Dir, Mod, GsAndCs), + [], Opts1, StartOpts)) end; {value,{_,Suites=[Suite|_]}} when is_list(Suite) -> Mods = lists:map(fun(Str) -> list_to_atom(Str) end, Suites), - do_run(tests(delistify(Dir), Mods), [], Opts, StartOpts); + reformat_result(catch do_run(tests(delistify(Dir), Mods), + [], Opts1, StartOpts)); {value,{_,Suites}} -> - do_run(tests(delistify(Dir), Suites), [], Opts, StartOpts); + reformat_result(catch do_run(tests(delistify(Dir), Suites), + [], Opts1, StartOpts)); false -> % no suite, only dir - do_run(tests(listify(Dir)), [], Opts, StartOpts) + reformat_result(catch do_run(tests(listify(Dir)), + [], Opts1, StartOpts)) end end. @@ -925,28 +958,30 @@ run_testspec1(TestSpec) -> EnvInclude++Opts#opts.include end, application:set_env(common_test, include, AllInclude), - - case check_and_install_configfiles(Opts#opts.config, - which(logdir,Opts#opts.logdir), + LogDir1 = which(logdir,Opts#opts.logdir), + case check_and_install_configfiles(Opts#opts.config, LogDir1, Opts#opts.event_handlers) of ok -> - Opts1 = Opts#opts{testspecs = [TestSpec], + Opts1 = Opts#opts{testspecs = [], + logdir = LogDir1, include = AllInclude}, {Run,Skip} = ct_testspec:prepare_tests(TS, node()), - do_run(Run, Skip, Opts1, []); + reformat_result(catch do_run(Run, Skip, Opts1, [])); {error,GCFReason} -> exit(GCFReason) end end. -get_data_for_node(#testspec{logdir=LogDirs, - cover=CoverFs, - config=Cfgs, - userconfig=UsrCfgs, - event_handler=EvHs, - include=Incl, - multiply_timetraps=MTs, - scale_timetraps=STs}, Node) -> +get_data_for_node(#testspec{label = Labels, + logdir = LogDirs, + cover = CoverFs, + config = Cfgs, + userconfig = UsrCfgs, + event_handler = EvHs, + include = Incl, + multiply_timetraps = MTs, + scale_timetraps = STs}, Node) -> + Label = proplists:get_value(Node, Labels), LogDir = case proplists:get_value(Node, LogDirs) of undefined -> "."; Dir -> Dir @@ -958,7 +993,8 @@ get_data_for_node(#testspec{logdir=LogDirs, [CBF || {N,CBF} <- UsrCfgs, N==Node], EvHandlers = [{H,A} || {N,H,A} <- EvHs, N==Node], Include = [I || {N,I} <- Incl, N==Node], - #opts{logdir = LogDir, + #opts{label = Label, + logdir = LogDir, cover = Cover, config = ConfigFiles, event_handlers = EvHandlers, @@ -1023,21 +1059,26 @@ delistify(E) -> E. %%% @equiv ct:run/3 run(TestDir, Suite, Cases) -> install([]), - do_run(tests(TestDir, Suite, Cases), []). + reformat_result(catch do_run(tests(TestDir, Suite, Cases), [])). %%%----------------------------------------------------------------- %%% @hidden %%% @equiv ct:run/2 run(TestDir, Suite) when is_list(TestDir), is_integer(hd(TestDir)) -> install([]), - do_run(tests(TestDir, Suite), []). + reformat_result(catch do_run(tests(TestDir, Suite), [])). %%%----------------------------------------------------------------- %%% @hidden %%% @equiv ct:run/1 run(TestDirs) -> install([]), - do_run(tests(TestDirs), []). + reformat_result(catch do_run(tests(TestDirs), [])). + +reformat_result({user_error,Reason}) -> + {error,Reason}; +reformat_result(Result) -> + Result. suite_to_test(Suite) -> {filename:dirname(Suite),list_to_atom(filename:rootname(filename:basename(Suite)))}. @@ -1092,7 +1133,17 @@ do_run(Tests, Misc, LogDir) when is_list(Misc) -> do_run(Tests, [], Opts1#opts{logdir = LogDir}, []). do_run(Tests, Skip, Opts, Args) -> - #opts{cover = Cover} = Opts, + #opts{label = Label, cover = Cover} = Opts, + + %% label - used by ct_logs + TestLabel = + if Label == undefined -> undefined; + is_atom(Label) -> atom_to_list(Label); + is_list(Label) -> Label; + true -> undefined + end, + application:set_env(common_test, test_label, TestLabel), + case code:which(test_server) of non_existing -> exit({error,no_path_to_test_server}); @@ -1156,10 +1207,19 @@ do_run(Tests, Skip, Opts, Args) -> true -> SavedErrors = save_make_errors(SuiteMakeErrors), ct_repeat:log_loop_info(Args), - {Tests1,Skip1} = final_tests(Tests,[],Skip,SavedErrors), - R = do_run_test(Tests1, Skip1, Opts1), - ct_util:stop(normal), - R; + + {Tests1,Skip1} = final_tests(Tests,Skip,SavedErrors), + + R = (catch do_run_test(Tests1, Skip1, Opts1)), + case R of + {EType,_} = Error when EType == user_error ; + EType == error -> + ct_util:stop(clean), + exit(Error); + _ -> + ct_util:stop(normal), + R + end; false -> io:nl(), ct_util:stop(clean), @@ -1316,8 +1376,22 @@ suite_tuples([{TestDir,Suite,_} | Tests]) when is_atom(Suite) -> suite_tuples([]) -> []. -final_tests([{TestDir,Suites,_}|Tests], - Final, Skip, Bad) when is_list(Suites), is_atom(hd(Suites)) -> +final_tests(Tests, Skip, Bad) -> + + %%! --- Thu Jun 24 15:47:27 2010 --- peppe was here! + %%! io:format(user, "FINAL0 = ~p~nSKIP0 = ~p~n", [Tests, Skip]), + + {Tests1,Skip1} = final_tests1(Tests, [], Skip, Bad), + Skip2 = final_skip(Skip1, []), + + + %%! --- Thu Jun 24 15:47:27 2010 --- peppe was here! + %%! io:format(user, "FINAL1 = ~p~nSKIP1 = ~p~n", [Tests1, Skip2]), + + {Tests1,Skip2}. + +final_tests1([{TestDir,Suites,_}|Tests], Final, Skip, Bad) when + is_list(Suites), is_atom(hd(Suites)) -> % Separate = % fun(S,{DoSuite,Dont}) -> % case lists:keymember({TestDir,S},1,Bad) of @@ -1336,9 +1410,9 @@ final_tests([{TestDir,Suites,_}|Tests], Skip1 = [{TD,S,"Make failed"} || {{TD,S},_} <- Bad, S1 <- Suites, S == S1, TD == TestDir], Final1 = [{TestDir,S,all} || S <- Suites], - final_tests(Tests, lists:reverse(Final1)++Final, Skip++Skip1, Bad); + final_tests1(Tests, lists:reverse(Final1)++Final, Skip++Skip1, Bad); -final_tests([{TestDir,all,all}|Tests], Final, Skip, Bad) -> +final_tests1([{TestDir,all,all}|Tests], Final, Skip, Bad) -> MissingSuites = case lists:keysearch({TestDir,all}, 1, Bad) of {value,{_,Failed}} -> @@ -1348,26 +1422,58 @@ final_tests([{TestDir,all,all}|Tests], Final, Skip, Bad) -> end, Missing = [{TestDir,S,"Make failed"} || S <- MissingSuites], Final1 = [{TestDir,all,all}|Final], - final_tests(Tests, Final1, Skip++Missing, Bad); + final_tests1(Tests, Final1, Skip++Missing, Bad); -final_tests([{TestDir,Suite,Cases}|Tests], - Final, Skip, Bad) when Cases==[]; Cases==all -> - final_tests([{TestDir,[Suite],all}|Tests], Final, Skip, Bad); +final_tests1([{TestDir,Suite,Cases}|Tests], Final, Skip, Bad) when + Cases==[]; Cases==all -> + final_tests1([{TestDir,[Suite],all}|Tests], Final, Skip, Bad); -final_tests([{TestDir,Suite,Cases}|Tests], Final, Skip, Bad) -> +final_tests1([{TestDir,Suite,GrsOrCs}|Tests], Final, Skip, Bad) when + is_list(GrsOrCs) -> case lists:keymember({TestDir,Suite}, 1, Bad) of - false -> - Do = {TestDir,Suite,Cases}, - final_tests(Tests, [Do|Final], Skip, Bad); true -> - Do = {TestDir,Suite,Cases}, - Skip1 = Skip ++ [{TestDir,Suite,Cases,"Make failed"}], - final_tests(Tests, [Do|Final], Skip1, Bad) + Skip1 = Skip ++ [{TestDir,Suite,all,"Make failed"}], + final_tests1(Tests, [{TestDir,Suite,all}|Final], Skip1, Bad); + false -> + GrsOrCs1 = + lists:flatmap( + %% for now, only flat group defs are allowed as + %% start options and test spec terms + fun({all,all}) -> + ct_framework:make_all_conf(TestDir, + Suite, []); + ({skipped,Group,TCs}) -> + [ct_framework:make_conf(TestDir, Suite, + Group, [skipped], TCs)]; + ({Group,TCs}) -> + [ct_framework:make_conf(TestDir, Suite, + Group, [], TCs)]; + (TC) -> + [TC] + end, GrsOrCs), + Do = {TestDir,Suite,GrsOrCs1}, + final_tests1(Tests, [Do|Final], Skip, Bad) end; -final_tests([], Final, Skip, _Bad) -> +final_tests1([], Final, Skip, _Bad) -> {lists:reverse(Final),Skip}. +final_skip([{TestDir,Suite,{all,all},Reason}|Skips], Final) -> + SkipConf = ct_framework:make_conf(TestDir, Suite, all, [], all), + Skip = {TestDir,Suite,SkipConf,Reason}, + final_skip(Skips, [Skip|Final]); + +final_skip([{TestDir,Suite,{Group,TCs},Reason}|Skips], Final) -> + Conf = ct_framework:make_conf(TestDir, Suite, Group, [], TCs), + Skip = {TestDir,Suite,Conf,Reason}, + final_skip(Skips, [Skip|Final]); + +final_skip([Skip|Skips], Final) -> + final_skip(Skips, [Skip|Final]); + +final_skip([], Final) -> + lists:reverse(Final). + continue([]) -> true; continue(_MakeErrors) -> @@ -1489,6 +1595,7 @@ do_run_test(Tests, Skip, Opts) -> _ -> false end, + %% let test_server expand the test tuples and count no of cases {Suites,NoOfCases} = count_test_cases(Tests, Skip), Suites1 = delete_dups(Suites), @@ -1544,19 +1651,30 @@ count_test_cases(Tests, Skip) -> TSPid = test_server_ctrl:start_get_totals(SendResult), Ref = erlang:monitor(process, TSPid), add_jobs(Tests, Skip, #opts{}, []), - {Suites,NoOfCases} = count_test_cases1(length(Tests), 0, [], Ref), + Counted = (catch count_test_cases1(length(Tests), 0, [], Ref)), erlang:demonitor(Ref, [flush]), - test_server_ctrl:stop_get_totals(), - {Suites,NoOfCases}. + case Counted of + {error,{test_server_died}} = Error -> + throw(Error); + {error,Reason} -> + unlink(whereis(test_server_ctrl)), + test_server_ctrl:stop(), + throw({user_error,Reason}); + Result -> + test_server_ctrl:stop_get_totals(), + Result + end. count_test_cases1(0, N, Suites, _) -> {lists:flatten(Suites), N}; count_test_cases1(Jobs, N, Suites, Ref) -> receive + {_,{error,_Reason} = Error} -> + throw(Error); {no_of_cases,{Ss,N1}} -> count_test_cases1(Jobs-1, add_known(N,N1), [Ss|Suites], Ref); - {'DOWN', Ref, _, _, _} -> - {[],0} + {'DOWN', Ref, _, _, Info} -> + throw({error,{test_server_died,Info}}) end. add_known(unknown, _) -> @@ -1604,13 +1722,36 @@ add_jobs([{TestDir,Suite,all}|Tests], Skip, Opts, CleanUp) -> Error end; -%% group -add_jobs([{TestDir,Suite,[{GroupName,_Cases}]}|Tests], Skip, Opts, CleanUp) when - is_atom(GroupName) -> - add_jobs([{TestDir,Suite,GroupName}|Tests], Skip, Opts, CleanUp); -add_jobs([{TestDir,Suite,{GroupName,_Cases}}|Tests], Skip, Opts, CleanUp) when - is_atom(GroupName) -> - add_jobs([{TestDir,Suite,GroupName}|Tests], Skip, Opts, CleanUp); +%% group (= conf case in test_server) +add_jobs([{TestDir,Suite,Confs}|Tests], Skip, Opts, CleanUp) when + element(1, hd(Confs)) == conf -> + Group = fun(Conf) -> proplists:get_value(name, element(2, Conf)) end, + TestCases = fun(Conf) -> element(4, Conf) end, + TCTestName = fun(all) -> ""; + ([C]) when is_atom(C) -> "." ++ atom_to_list(C); + (Cs) when is_list(Cs) -> ".cases" + end, + GrTestName = + case Confs of + [Conf] -> + "." ++ atom_to_list(Group(Conf)) ++ TCTestName(TestCases(Conf)); + _ -> + ".groups" + end, + TestName = get_name(TestDir) ++ "." ++ atom_to_list(Suite) ++ GrTestName, + case maybe_interpret(Suite, init_per_group, Opts) of + ok -> + case catch test_server_ctrl:add_conf_with_skip(TestName, Suite, Confs, + skiplist(TestDir,Skip)) of + {'EXIT',_} -> + CleanUp; + _ -> + wait_for_idle(), + add_jobs(Tests, Skip, Opts, [Suite|CleanUp]) + end; + Error -> + Error + end; %% test case add_jobs([{TestDir,Suite,[Case]}|Tests], Skip, Opts, CleanUp) when is_atom(Case) -> diff --git a/lib/common_test/src/ct_testspec.erl b/lib/common_test/src/ct_testspec.erl index 0f68b062f6..f5069427a2 100644 --- a/lib/common_test/src/ct_testspec.erl +++ b/lib/common_test/src/ct_testspec.erl @@ -185,7 +185,15 @@ prepare_cases(Node,Dir,Suite,Cases) -> {[{{Node,Dir},{Suite,all}}],SkipAll}; Skipped -> %% note: this adds a test even if only skip is specified - PrepC = lists:foldr(fun({C,{skip,_Cmt}},Acc) -> + PrepC = lists:foldr(fun({{G,Cs},{skip,_Cmt}}, Acc) when + is_atom(G) -> + case lists:keymember(G, 1, Cases) of + true -> + Acc; + false -> + [{skipped,G,Cs}|Acc] + end; + ({C,{skip,_Cmt}},Acc) -> case lists:member(C,Cases) of true -> Acc; @@ -194,7 +202,7 @@ prepare_cases(Node,Dir,Suite,Cases) -> end; (C,Acc) -> [C|Acc] end, [], Cases), - {{{Node,Dir},{Suite,PrepC}},Skipped} + {{{Node,Dir},{Suite,PrepC}},Skipped} end. get_skipped_suites(Node,Dir,Suites) -> @@ -210,7 +218,7 @@ get_skipped_cases(Node,Dir,Suite,Cases) -> case lists:keysearch(all,1,Cases) of {value,{all,{skip,Cmt}}} -> [{{Node,Dir},{Suite,Cmt}}]; - false -> + _ -> get_skipped_cases1(Node,Dir,Suite,Cases) end. @@ -432,6 +440,15 @@ save_nodes(Nodes,Spec=#testspec{nodes=NodeRefs}) -> list_nodes(#testspec{nodes=NodeRefs}) -> lists:map(fun({_Ref,Node}) -> Node end, NodeRefs). + + +%% --------------------------------------------------------- +%% / \ +%% | When adding tests, remember to update valid_terms/0 also! | +%% \ / +%% --------------------------------------------------------- + + %% Associate a "global" logdir with all nodes %% except those with specific logdir, e.g: %% ["/tmp/logdir",{ct1@finwe,"/tmp/logdir2"}] @@ -457,6 +474,24 @@ add_tests([{logdir,Node,Dir}|Ts],Spec) -> add_tests([{logdir,Dir}|Ts],Spec) -> add_tests([{logdir,all_nodes,Dir}|Ts],Spec); +%% --- label --- +add_tests([{label,all_nodes,Lbl}|Ts],Spec) -> + Labels = Spec#testspec.label, + Tests = [{label,N,Lbl} || N <- list_nodes(Spec), + lists:keymember(ref2node(N,Spec#testspec.nodes), + 1,Labels) == false], + add_tests(Tests++Ts,Spec); +add_tests([{label,Nodes,Lbl}|Ts],Spec) when is_list(Nodes) -> + Ts1 = separate(Nodes,label,[Lbl],Ts,Spec#testspec.nodes), + add_tests(Ts1,Spec); +add_tests([{label,Node,Lbl}|Ts],Spec) -> + Labels = Spec#testspec.label, + Labels1 = [{ref2node(Node,Spec#testspec.nodes),Lbl} | + lists:keydelete(ref2node(Node,Spec#testspec.nodes),1,Labels)], + add_tests(Ts,Spec#testspec{label=Labels1}); +add_tests([{label,Lbl}|Ts],Spec) -> + add_tests([{label,all_nodes,Lbl}|Ts],Spec); + %% --- cover --- add_tests([{cover,all_nodes,File}|Ts],Spec) -> Tests = lists:map(fun(N) -> {cover,N,File} end, list_nodes(Spec)), @@ -878,8 +913,13 @@ skip_suites(_Node,_Dir,[],_Cmt,Tests) -> skip_suites(Node,Dir,S,Cmt,Tests) -> skip_suites(Node,Dir,[S],Cmt,Tests). -skip_groups(Node,Dir,Suite,Group,Case,Cmt,Tests) when is_atom(Group) -> - skip_groups(Node,Dir,Suite,[Group],[Case],Cmt,Tests); +skip_groups(Node,Dir,Suite,Group,all,Cmt,Tests) when is_atom(Group) -> + skip_groups(Node,Dir,Suite,[Group],all,Cmt,Tests); +skip_groups(Node,Dir,Suite,Group,Cases,Cmt,Tests) when is_atom(Group) -> + skip_groups(Node,Dir,Suite,[Group],Cases,Cmt,Tests); +skip_groups(Node,Dir,Suite,Groups,Case,Cmt,Tests) when is_atom(Case), + Case =/= all -> + skip_groups(Node,Dir,Suite,Groups,[Case],Cmt,Tests); skip_groups(Node,Dir,Suite,Groups,Cases,Cmt,Tests) when ((Cases == all) or is_list(Cases)) and is_list(Groups) -> Suites = @@ -1001,22 +1041,34 @@ valid_terms() -> {cover,3}, {config,2}, {config,3}, - {userconfig, 2}, - {userconfig, 3}, + {userconfig,2}, + {userconfig,3}, {alias,3}, {logdir,2}, {logdir,3}, + {label,2}, + {label,3}, {event_handler,2}, {event_handler,3}, {event_handler,4}, + {multiply_timetraps,2}, + {multiply_timetraps,3}, + {scale_timetraps,2}, + {scale_timetraps,3}, {include,2}, {include,3}, {suites,3}, {suites,4}, + {groups,4}, + {groups,5}, + {groups,6}, {cases,4}, {cases,5}, {skip_suites,4}, {skip_suites,5}, + {skip_groups,5}, + {skip_groups,6}, + {skip_groups,7}, {skip_cases,5}, {skip_cases,6} ]. diff --git a/lib/common_test/src/ct_util.erl b/lib/common_test/src/ct_util.erl index eddaf4c8b9..0a434666fa 100644 --- a/lib/common_test/src/ct_util.erl +++ b/lib/common_test/src/ct_util.erl @@ -619,7 +619,6 @@ get_testdir(Dir, Suite) when is_list(Suite) -> get_testdir(Dir, _) -> get_testdir(Dir, all). - %%%----------------------------------------------------------------- %%% @spec %%% diff --git a/lib/common_test/src/ct_util.hrl b/lib/common_test/src/ct_util.hrl index 54eed29415..ee973f6220 100644 --- a/lib/common_test/src/ct_util.hrl +++ b/lib/common_test/src/ct_util.hrl @@ -30,6 +30,7 @@ -record(testspec, {spec_dir, nodes=[], init=[], + label=[], logdir=["."], cover=[], config=[], diff --git a/lib/common_test/test/Makefile b/lib/common_test/test/Makefile index 97ded5eb9a..3fb0d627a0 100644 --- a/lib/common_test/test/Makefile +++ b/lib/common_test/test/Makefile @@ -32,6 +32,7 @@ MODULES= \ ct_event_handler_SUITE \ ct_groups_test_1_SUITE \ ct_groups_test_2_SUITE \ + ct_testspec_1_SUITE \ ct_skip_SUITE \ ct_error_SUITE \ ct_test_server_if_1_SUITE \ diff --git a/lib/common_test/test/ct_groups_test_1_SUITE_data/groups_1/test/groups_11_SUITE.erl b/lib/common_test/test/ct_groups_test_1_SUITE_data/groups_1/test/groups_11_SUITE.erl index c6d50443d0..c69400e938 100644 --- a/lib/common_test/test/ct_groups_test_1_SUITE_data/groups_1/test/groups_11_SUITE.erl +++ b/lib/common_test/test/ct_groups_test_1_SUITE_data/groups_1/test/groups_11_SUITE.erl @@ -99,6 +99,7 @@ init_per_group(Group, Config) -> {Grs,_} = grs_and_tcs(), case lists:member(Group, Grs) of true -> + ct:comment(Group), init = ?config(suite,Config), [{Group,Group} | Config]; false -> @@ -109,6 +110,7 @@ end_per_group(Group, Config) -> {Grs,_} = grs_and_tcs(), case lists:member(Group, Grs) of true -> + ct:comment(Group), init = ?config(suite,Config), Group = ?config(Group,Config), ok; diff --git a/lib/common_test/test/ct_groups_test_2_SUITE.erl b/lib/common_test/test/ct_groups_test_2_SUITE.erl index 56e0ac30c7..c4371501b3 100644 --- a/lib/common_test/test/ct_groups_test_2_SUITE.erl +++ b/lib/common_test/test/ct_groups_test_2_SUITE.erl @@ -60,7 +60,7 @@ all(doc) -> ["Run smoke tests of Common Test."]; all(suite) -> - [missing_conf, testspec_1, repeat_1]. + [missing_conf, repeat_1]. %%-------------------------------------------------------------------- %% TEST CASES @@ -88,25 +88,6 @@ missing_conf(Config) when is_list(Config) -> %%%----------------------------------------------------------------- %%% -testspec_1(Config) when is_list(Config) -> - DataDir = ?config(data_dir, Config), - - TestSpec = filename:join(DataDir, "specs/groups_2.1.spec"), - - {Opts,ERPid} = setup({spec,TestSpec}, Config), - ok = ct_test_support:run(Opts, Config), - Events = ct_test_support:get_events(ERPid, Config), - - ct_test_support:log_events(testspec_1, - reformat(Events, ?eh), - ?config(priv_dir, Config)), - - TestEvents = events_to_check(testspec_1), - ok = ct_test_support:verify_events(TestEvents, Events, Config). - -%%%----------------------------------------------------------------- -%%% - repeat_1(Config) when is_list(Config) -> DataDir = ?config(data_dir, Config), @@ -173,9 +154,6 @@ test_events(missing_conf) -> {?eh,stop_logging,[]} ]; -test_events(testspec_1) -> - []; - test_events(repeat_1) -> [ {?eh,start_logging,{'DEF','RUNDIR'}}, diff --git a/lib/common_test/test/ct_groups_test_2_SUITE_data/specs/groups_2.1.spec b/lib/common_test/test/ct_groups_test_2_SUITE_data/specs/groups_2.1.spec deleted file mode 100644 index 24d778ac44..0000000000 --- a/lib/common_test/test/ct_groups_test_2_SUITE_data/specs/groups_2.1.spec +++ /dev/null @@ -1,26 +0,0 @@ - -{config, "../cfgs/groups_2.1.cfg"}. -{alias, groups_2, "../groups_2"}. - -{suites, groups_2, groups_21_SUITE}. -%{skip_groups, groups_2, groups_21_SUITE, -% [test_group_1b, test_group_7], "Skip tg_1b & tg_7"}. -{skip_cases, groups_2, groups_21_SUITE, - [testcase_1b, testcase_3a], "Skip tc_1b & tc_3a"}. - -{groups, groups_2, groups_22_SUITE, - test_group_1a}. -{skip_cases, groups_2, groups_22_SUITE, - testcase_1a, "Skip tc_1a"}. - -{groups, groups_2, groups_22_SUITE, - test_group_1b}. -{skip_cases, groups_2, groups_22_SUITE, - testcase_1b, "Skip tc_1b"}. -%{skip_groups, groups_2, groups_21_SUITE, -% [test_group_3], "Skip tg_3"}. - -{groups, groups_2, groups_22_SUITE, - test_group_5}. -{skip_cases, groups_2, groups_22_SUITE, - [testcase_7a, testcase_7b], "Skip tc_7a & tc_7b"}. diff --git a/lib/common_test/test/ct_test_support.erl b/lib/common_test/test/ct_test_support.erl index 6a75c8f789..7bfb9ffb49 100644 --- a/lib/common_test/test/ct_test_support.erl +++ b/lib/common_test/test/ct_test_support.erl @@ -23,7 +23,7 @@ %%% -module(ct_test_support). --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). -include_lib("common_test/include/ct_event.hrl"). -export([init_per_suite/1, init_per_suite/2, end_per_suite/1, @@ -111,8 +111,9 @@ init_per_testcase(_TestCase, Config) -> case lists:keysearch(master, 1, Config) of false-> test_server:format("See Common Test logs here:\n\n" - "<a href=\"file://~s/all_runs.html\">~s/all_runs.html</a>", - [LogDir,LogDir]); + "<a href=\"file://~s/all_runs.html\">~s/all_runs.html</a>\n" + "<a href=\"file://~s/index.html\">~s/index.html</a>", + [LogDir,LogDir,LogDir,LogDir]); {value, _}-> test_server:format("See CT Master Test logs here:\n\n" "<a href=\"file://~s/master_runs.html\">~s/master_runs.html</a>", diff --git a/lib/common_test/test/ct_testspec_1_SUITE.erl b/lib/common_test/test/ct_testspec_1_SUITE.erl new file mode 100644 index 0000000000..dc399bfb4c --- /dev/null +++ b/lib/common_test/test/ct_testspec_1_SUITE.erl @@ -0,0 +1,433 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2009-2010. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%%%------------------------------------------------------------------- +%%% File: ct_testspec_1_SUITE +%%% +%%% Description: +%%% Test test specifications +%%% +%%% The suites used for the test are located in the data directory. +%%%------------------------------------------------------------------- +-module(ct_testspec_1_SUITE). + +-compile(export_all). + +-include_lib("test_server/include/test_server.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) -> + Config1 = ct_test_support:init_per_suite(Config), + Config1. + +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). + +all(doc) -> + ["Run smoke tests of Common Test."]; + +all(suite) -> + [all_suites, skip_all_suites, + suite, skip_suite, + all_testcases, skip_all_testcases, + testcase, skip_testcase, + all_groups, skip_all_groups, + group, skip_group, + group_all_testcases, skip_group_all_testcases, + group_testcase, skip_group_testcase, + topgroup, + subgroup, skip_subgroup, + subgroup_all_testcases, skip_subgroup_all_testcases, + subgroup_testcase, skip_subgroup_testcase, + sub_skipped_by_top, + testcase_in_multiple_groups]. + +%%-------------------------------------------------------------------- +%% TEST CASES +%%-------------------------------------------------------------------- + +%%%----------------------------------------------------------------- +%%% + +all_suites(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + + TestDir = filename:join(DataDir, "suites_1"), + TestSpec = [{label,"all_suites"}, + {suites,TestDir,all}], + + setup_and_execute(all_suites, TestSpec, Config). + +skip_all_suites(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + + TestDir = filename:join(DataDir, "suites_1"), + TestSpec = [{label,skip_all_suites}, + {suites,TestDir,all}, + {skip_suites,TestDir,all,"SKIPPED!"}], + + setup_and_execute(skip_all_suites, TestSpec, Config). + +%%%----------------------------------------------------------------- +%%% + +suite(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + + TestDir = filename:join(DataDir, "suites_1"), + TestSpec = [{label,undefined}, + {suites,TestDir,simple_1_SUITE}], + + setup_and_execute(suite, TestSpec, Config). + +skip_suite(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + + TestDir = filename:join(DataDir, "suites_1"), + TestSpec = [{suites,TestDir,[simple_1_SUITE,simple_2_SUITE]}, + {skip_suites,TestDir,simple_1_SUITE,"SKIPPED!"}], + + setup_and_execute(skip_suite, TestSpec, Config). + +%%%----------------------------------------------------------------- +%%% + +all_testcases(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + + TestDir = filename:join(DataDir, "suites_1"), + TestSpec = [{cases,TestDir,simple_1_SUITE,all}], + + setup_and_execute(all_testcases, TestSpec, Config). + +skip_all_testcases(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + + TestDir = filename:join(DataDir, "suites_1"), + TestSpec = [{suites,TestDir,[simple_1_SUITE]}, + {skip_cases,TestDir,simple_1_SUITE,all,"SKIPPED!"}], + + setup_and_execute(skip_all_testcases, TestSpec, Config). + +%%%----------------------------------------------------------------- +%%% + +testcase(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + + TestDir = filename:join(DataDir, "suites_1"), + TestSpec = [{cases,TestDir,simple_1_SUITE,tc1}], + + setup_and_execute(testcase, TestSpec, Config). + +skip_testcase(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + + TestDir = filename:join(DataDir, "suites_1"), + TestSpec = [{cases,TestDir,simple_1_SUITE,[tc1,tc2]}, + {cases,TestDir,simple_2_SUITE,[tc2,tc1]}, + {skip_cases,TestDir,simple_1_SUITE,[tc1],"SKIPPED!"}, + {skip_cases,TestDir,simple_2_SUITE,tc2,"SKIPPED!"}], + + setup_and_execute(skip_testcase, TestSpec, Config). + +%%%----------------------------------------------------------------- +%%% + +all_groups(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + + TestDir = filename:join(DataDir, "groups_1"), + TestSpec = [{groups,TestDir,groups_11_SUITE,all}], + + setup_and_execute(all_groups, TestSpec, Config). + +skip_all_groups(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + + TestDir = filename:join(DataDir, "groups_1"), + TestSpec = [{groups,TestDir,groups_11_SUITE,all}, + {skip_groups,TestDir,groups_11_SUITE,all,"SKIPPED!"}], + + setup_and_execute(skip_all_groups, TestSpec, Config). + +%%%----------------------------------------------------------------- +%%% + +group(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + + TestDir = filename:join(DataDir, "groups_1"), + TestSpec = [{groups,TestDir,groups_11_SUITE,test_group_1a}], + + setup_and_execute(group, TestSpec, Config). + +skip_group(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + + TestDir = filename:join(DataDir, "groups_1"), + TestSpec = [{groups,TestDir,groups_11_SUITE,[test_group_1a, + test_group_1b]}, + {skip_groups,TestDir,groups_11_SUITE, + [test_group_1b,test_group_2,test_group_7],"SKIPPED!"}], + + setup_and_execute(skip_group, TestSpec, Config). + + +%%%----------------------------------------------------------------- +%%% + +group_all_testcases(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + + TestDir = filename:join(DataDir, "groups_1"), + TestSpec = [{groups,TestDir,groups_11_SUITE,test_group_1a,{cases,all}}], + + setup_and_execute(group_all_testcases, TestSpec, Config). + +skip_group_all_testcases(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + + TestDir = filename:join(DataDir, "groups_1"), + TestSpec = [{groups,TestDir,groups_11_SUITE,[test_group_1a, + test_group_1b]}, + {skip_groups,TestDir,groups_11_SUITE, + test_group_1b,{cases,all},"SKIPPED!"}, + {skip_groups,TestDir,groups_11_SUITE, + test_group_1a,{cases,all},"SKIPPED!"}], + + setup_and_execute(skip_group_all_testcases, TestSpec, Config). + +%%%----------------------------------------------------------------- +%%% + +group_testcase(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + + TestDir = filename:join(DataDir, "groups_1"), + TestSpec = [{groups,TestDir,groups_11_SUITE,test_group_1a,{cases,testcase_1a}}], + + setup_and_execute(group_testcase, TestSpec, Config). + +skip_group_testcase(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + + TestDir = filename:join(DataDir, "groups_1"), + TestSpec = [{groups,TestDir,groups_11_SUITE,test_group_1a, + {cases,[testcase_1a,testcase_1b]}}, + {groups,TestDir,groups_11_SUITE,test_group_1b, + {cases,[testcase_1b,testcase_1a]}}, + {skip_groups,TestDir,groups_11_SUITE, + test_group_1a,{cases,testcase_1b},"SKIPPED!"}, + {skip_groups,TestDir,groups_11_SUITE, + test_group_1b,{cases,[testcase_1a]},"SKIPPED!"}], + + setup_and_execute(skip_group_testcase, TestSpec, Config). + +%%%----------------------------------------------------------------- +%%% + +topgroup(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + + TestDir = filename:join(DataDir, "groups_1"), + TestSpec = [{groups,TestDir,groups_12_SUITE,test_group_2}, + {groups,TestDir,groups_12_SUITE,test_group_4}], + + setup_and_execute(topgroup, TestSpec, Config). + +%%%----------------------------------------------------------------- +%%% + +subgroup(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + + TestDir = filename:join(DataDir, "groups_1"), + TestSpec = [{groups,TestDir,groups_12_SUITE,test_group_3}], + + setup_and_execute(subgroup, TestSpec, Config). + +skip_subgroup(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + + TestDir = filename:join(DataDir, "groups_1"), + TestSpec = [{groups,TestDir,groups_12_SUITE,[test_group_4]}, + {skip_groups,TestDir,groups_12_SUITE, + test_group_8,"SKIPPED!"}], + + setup_and_execute(skip_subgroup, TestSpec, Config). + +%%%----------------------------------------------------------------- +%%% + +subgroup_all_testcases(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + + TestDir = filename:join(DataDir, "groups_1"), + TestSpec = [{groups,TestDir,groups_12_SUITE, + test_group_5,{cases,all}}, + {groups,TestDir,groups_12_SUITE, + test_group_3,{cases,all}}], + + setup_and_execute(subgroup_all_testcases, TestSpec, Config). + +skip_subgroup_all_testcases(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + + TestDir = filename:join(DataDir, "groups_1"), + TestSpec = [{groups,TestDir,groups_12_SUITE,test_group_4}, + {skip_groups,TestDir,groups_12_SUITE, + test_group_5,{cases,all},"SKIPPED!"}], + + setup_and_execute(skip_subgroup_all_testcases, TestSpec, Config). + +%%%----------------------------------------------------------------- +%%% + +subgroup_testcase(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + + TestDir = filename:join(DataDir, "groups_1"), + TestSpec = [{groups,TestDir,groups_12_SUITE, + test_group_7,{cases,testcase_7a}}, + {groups,TestDir,groups_12_SUITE, + test_group_3,{cases,testcase_3b}}], + + setup_and_execute(subgroup_testcase, TestSpec, Config). + +skip_subgroup_testcase(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + + TestDir = filename:join(DataDir, "groups_1"), + TestSpec = [{groups,TestDir,groups_12_SUITE,test_group_5}, + {skip_groups,TestDir,groups_12_SUITE, + test_group_7,{cases,[testcase_7a,testcase_7b]},"SKIPPED!"}], + + setup_and_execute(skip_subgroup_testcase, TestSpec, Config). + +%%%----------------------------------------------------------------- +%%% + +%%! +%%! Somewhat weird result from this one: +%%! +sub_skipped_by_top(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + + TestDir = filename:join(DataDir, "groups_1"), + TestSpec = [{groups,TestDir,groups_12_SUITE,test_group_5}, + {skip_groups,TestDir,groups_12_SUITE,test_group_4,"SKIPPED!"}], + + setup_and_execute(sub_skipped_by_top, TestSpec, Config). + +%%%----------------------------------------------------------------- +%%% + +testcase_in_multiple_groups(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + + TestDir = filename:join(DataDir, "groups_1"), + TestSpec = [{cases,TestDir,groups_12_SUITE,[testcase_1a,testcase_1b]}, + {skip_cases,TestDir,groups_12_SUITE,[testcase_1b],"SKIPPED!"}], + + setup_and_execute(testcase_in_multiple_groups, TestSpec, Config). + +%%%----------------------------------------------------------------- +%%% HELP FUNCTIONS +%%%----------------------------------------------------------------- + +setup_and_execute(TCName, TestSpec, Config) -> + SpecFile = create_spec_file(?config(priv_dir, Config), + TCName, TestSpec), + TestTerms = + case lists:keymember(label, 1, TestSpec) of + true -> [{spec,SpecFile}]; + false -> [{spec,SpecFile},{label,TCName}] + end, + {Opts,ERPid} = setup(TestTerms, Config), + ok = ct_test_support:run(Opts, Config), + TestSpec1 = [{logdir,proplists:get_value(logdir,Opts)}, + {label,proplists:get_value(label,TestTerms)} | TestSpec], + ok = ct_test_support:run(ct, run_testspec, [TestSpec1], Config), + Events = ct_test_support:get_events(ERPid, Config), + + ct_test_support:log_events(TCName, + reformat(Events, ?eh), + ?config(priv_dir, Config)), + + TestEvents = events_to_check(TCName), + ok = ct_test_support:verify_events(TestEvents, Events, Config). + +create_spec_file(SpecDir, TCName, TestSpec) -> + FileName = filename:join(SpecDir, + atom_to_list(TCName)++".spec"), + {ok,Dev} = file:open(FileName, [write]), + [io:format(Dev, "~p.~n", [Term]) || Term <- TestSpec], + file:close(Dev), + io:format("~nTest spec created here~n~n<a href=\"file://~s\">~s</a>~n", + [FileName,FileName]), + FileName. + +setup(Test, Config) when is_tuple(Test) -> + setup([Test], Config); +setup(Tests, Config) -> + Opts0 = ct_test_support:get_opts(Config), + Level = ?config(trace_level, Config), + EvHArgs = [{cbm,ct_test_support},{trace_level,Level}], + Opts = Opts0 ++ Tests ++ [{event_handler,{?eh,EvHArgs}}], + ERPid = ct_test_support:start_event_receiver(Config), + {Opts,ERPid}. + +reformat(Events, EH) -> + ct_test_support:reformat(Events, EH). +%reformat(Events, _EH) -> +% Events. + +%%%----------------------------------------------------------------- +%%% 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(_) -> + [ + ]. diff --git a/lib/common_test/test/ct_testspec_1_SUITE_data/groups_1/groups_11_SUITE.erl b/lib/common_test/test/ct_testspec_1_SUITE_data/groups_1/groups_11_SUITE.erl new file mode 100644 index 0000000000..4f11d8a0e8 --- /dev/null +++ b/lib/common_test/test/ct_testspec_1_SUITE_data/groups_1/groups_11_SUITE.erl @@ -0,0 +1,281 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2009-2010. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +-module(groups_11_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). + +%%==================================================================== +%% COMMON TEST CALLBACK FUNCTIONS +%%==================================================================== + +suite() -> + [{timetrap,{minutes,1}}]. + +groups() -> + [ + {test_group_1a, [testcase_1a,testcase_1b]}, + + {test_group_1b, [], [testcase_1a,testcase_1b]}, + + {test_group_2, [], [testcase_2a, + + {test_group_3, [], [testcase_3a, + testcase_3b]}, + testcase_2b]}, + + {test_group_4, [{test_group_5, [], [testcase_5a, + + {group, test_group_6}, + + testcase_5b]}]}, + {test_group_6, [{group, test_group_7}]}, + + {test_group_7, [testcase_7a,testcase_7b]} + ]. + +all() -> + [testcase_1, + {group, test_group_1a}, + {group, test_group_1b}, + testcase_2, + {group, test_group_2}, + testcase_3, + {group, test_group_4}]. + +%% this func only for internal test purposes +grs_and_tcs() -> + {[ + test_group_1a, test_group_1b, + test_group_2, test_group_3, + test_group_4, test_group_5, + test_group_6, test_group_7 + ], + [ + testcase_1, + testcase_1a, testcase_1b, + testcase_2, + testcase_2a, testcase_2b, + testcase_3a, testcase_3b, + testcase_3, + testcase_5a, testcase_5b, + testcase_7a, testcase_7b + ]}. + +%%-------------------------------------------------------------------- +%% Suite Configuration +%%-------------------------------------------------------------------- + +init_per_suite(Config) -> + [{suite,init}|Config]. + +end_per_suite(Config) -> + init = ?config(suite,Config). + +%%-------------------------------------------------------------------- +%% Group Configuration +%%-------------------------------------------------------------------- + +init_per_group(Group, Config) -> + [{name,Group}] = ?config(tc_group_properties,Config), + {Grs,_} = grs_and_tcs(), + case lists:member(Group, Grs) of + true -> + ct:comment(Group), + init = ?config(suite,Config), + [{Group,Group} | Config]; + false -> + ct:fail({bad_group,Group}) + end. + +end_per_group(Group, Config) -> + {Grs,_} = grs_and_tcs(), + case lists:member(Group, Grs) of + true -> + ct:comment(Group), + init = ?config(suite,Config), + Group = ?config(Group,Config), + ok; + false -> + ct:fail({bad_group,Group}) + end. + +%%-------------------------------------------------------------------- +%% Testcase Configuration +%%-------------------------------------------------------------------- + +init_per_testcase(TestCase, Config) -> + {_,TCs} = grs_and_tcs(), + case lists:member(TestCase, TCs) of + true -> + init = ?config(suite,Config), + [{TestCase,TestCase} | Config]; + false -> + ct:fail({unknown_testcase,TestCase}) + end. + +end_per_testcase(TestCase, Config) -> + {_,TCs} = grs_and_tcs(), + case lists:member(TestCase, TCs) of + true -> + init = ?config(suite,Config), + TestCase = ?config(TestCase,Config), + ok; + false -> + ct:fail({unknown_testcase,TestCase}) + end. + + +%%-------------------------------------------------------------------- +%% Testcases +%%-------------------------------------------------------------------- + +testcase_1() -> + []. +testcase_1(Config) -> + init = ?config(suite,Config), + testcase_1 = ?config(testcase_1,Config), + ok. + +testcase_1a() -> + []. +testcase_1a(Config) -> + init = ?config(suite,Config), + case ?config(test_group_1a,Config) of + test_group_1a -> ok; + _ -> + case ?config(test_group_1b,Config) of + test_group_1b -> ok; + _ -> ct:fail(no_group_data) + end + end, + testcase_1a = ?config(testcase_1a,Config), + ok. +testcase_1b() -> + []. +testcase_1b(Config) -> + init = ?config(suite,Config), + case ?config(test_group_1a,Config) of + test_group_1a -> ok; + _ -> + case ?config(test_group_1b,Config) of + test_group_1b -> ok; + _ -> ct:fail(no_group_data) + end + end, + undefined = ?config(testcase_1a,Config), + testcase_1b = ?config(testcase_1b,Config), + ok. + +testcase_2() -> + []. +testcase_2(Config) -> + init = ?config(suite,Config), + undefined = ?config(test_group_1a,Config), + undefined = ?config(test_group_1b,Config), + testcase_2 = ?config(testcase_2,Config), + ok. + +testcase_2a() -> + []. +testcase_2a(Config) -> + init = ?config(suite,Config), + test_group_2 = ?config(test_group_2,Config), + testcase_2a = ?config(testcase_2a,Config), + ok. +testcase_2b() -> + []. +testcase_2b(Config) -> + init = ?config(suite,Config), + test_group_2 = ?config(test_group_2,Config), + undefined = ?config(testcase_2a,Config), + testcase_2b = ?config(testcase_2b,Config), + ok. + +testcase_3a() -> + []. +testcase_3a(Config) -> + init = ?config(suite,Config), + test_group_2 = ?config(test_group_2,Config), + test_group_3 = ?config(test_group_3,Config), + undefined = ?config(testcase_2b,Config), + testcase_3a = ?config(testcase_3a,Config), + ok. +testcase_3b() -> + []. +testcase_3b(Config) -> + init = ?config(suite,Config), + test_group_2 = ?config(test_group_2,Config), + test_group_3 = ?config(test_group_3,Config), + undefined = ?config(testcase_3a,Config), + testcase_3b = ?config(testcase_3b,Config), + ok. + +testcase_3() -> + []. +testcase_3(Config) -> + init = ?config(suite,Config), + undefined = ?config(test_group_2,Config), + undefined = ?config(test_group_3,Config), + testcase_3 = ?config(testcase_3,Config), + ok. + +testcase_5a() -> + []. +testcase_5a(Config) -> + init = ?config(suite,Config), + undefined = ?config(test_group_3,Config), + test_group_4 = ?config(test_group_4,Config), + test_group_5 = ?config(test_group_5,Config), + undefined = ?config(testcase_3,Config), + testcase_5a = ?config(testcase_5a,Config), + ok. +testcase_5b() -> + []. +testcase_5b(Config) -> + init = ?config(suite,Config), + test_group_4 = ?config(test_group_4,Config), + test_group_5 = ?config(test_group_5,Config), + undefined = ?config(testcase_5a,Config), + testcase_5b = ?config(testcase_5b,Config), + ok. + +testcase_7a() -> + []. +testcase_7a(Config) -> + init = ?config(suite,Config), + undefined = ?config(test_group_3,Config), + test_group_4 = ?config(test_group_4,Config), + test_group_5 = ?config(test_group_5,Config), + test_group_6 = ?config(test_group_6,Config), + test_group_7 = ?config(test_group_7,Config), + testcase_7a = ?config(testcase_7a,Config), + ok. +testcase_7b() -> + []. +testcase_7b(Config) -> + init = ?config(suite,Config), + test_group_4 = ?config(test_group_4,Config), + test_group_5 = ?config(test_group_5,Config), + test_group_6 = ?config(test_group_6,Config), + test_group_7 = ?config(test_group_7,Config), + undefined = ?config(testcase_7a,Config), + testcase_7b = ?config(testcase_7b,Config), + ok. diff --git a/lib/common_test/test/ct_testspec_1_SUITE_data/groups_1/groups_12_SUITE.erl b/lib/common_test/test/ct_testspec_1_SUITE_data/groups_1/groups_12_SUITE.erl new file mode 100644 index 0000000000..69c06f9b83 --- /dev/null +++ b/lib/common_test/test/ct_testspec_1_SUITE_data/groups_1/groups_12_SUITE.erl @@ -0,0 +1,344 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2009-2010. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +-module(groups_12_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). + +%%==================================================================== +%% COMMON TEST CALLBACK FUNCTIONS +%%==================================================================== + +suite() -> + [{timetrap,{minutes,1}}]. + +groups() -> + [ + {test_group_1a, [shuffle], [testcase_1a,testcase_1b,testcase_1c]}, + + {test_group_1b, [parallel], [testcase_1a,testcase_1b]}, + + {test_group_2, [parallel], [testcase_2a, + + {test_group_3, [{repeat,2}], + [testcase_3a, testcase_3b]}, + + testcase_2b]}, + + {test_group_4, [{test_group_5, [parallel], [testcase_5a, + + {group, test_group_6}, + + testcase_5b]}]}, + + {test_group_6, [parallel], [{group, test_group_7}, + {group, test_group_8}]}, + + {test_group_7, [sequence], [testcase_7a,testcase_7b]}, + + {test_group_8, [shuffle,sequence], [testcase_8a,testcase_8b]} + ]. + +all() -> + [{group, test_group_1a}, + {group, test_group_1b}, + testcase_1, + testcase_2, + {group, test_group_2}, + testcase_3, + {group, test_group_4}]. + +%% this func only for internal test purposes +grs_and_tcs() -> + {[ + test_group_1a, test_group_1b, + test_group_2, test_group_3, + test_group_4, test_group_5, + test_group_6, test_group_7, + test_group_8 + ], + [ + testcase_1a, testcase_1b, testcase_1c, + testcase_1, + testcase_2, + testcase_2a, testcase_2b, + testcase_3a, testcase_3b, + testcase_3, + testcase_5a, testcase_5b, + testcase_7a, testcase_7b, + testcase_8a, testcase_8b + ]}. + +%%-------------------------------------------------------------------- +%% Suite Configuration +%%-------------------------------------------------------------------- + +init_per_suite(Config) -> + [{suite,init}|Config]. + +end_per_suite(Config) -> + init = ?config(suite,Config). + +%%-------------------------------------------------------------------- +%% Group Configuration +%%-------------------------------------------------------------------- + +init_per_group(Group, Config) -> + Cmt = + case {Group,?config(tc_group_properties,Config)} of + {test_group_1a,[{shuffle,S},{name,test_group_1a}]} -> + io_lib:format("shuffled, ~w", [S]); + {test_group_1b,[{name,test_group_1b},parallel]} -> "parallel"; + {test_group_2,[{name,test_group_2},parallel]} -> "parallel"; + {test_group_3,[{name,test_group_3},{repeat,2}]} -> "repeat 2"; + {test_group_3,[{name,test_group_3}]} -> "repeat 1"; + {test_group_4,[{name,test_group_4}]} -> ok; + {test_group_5,[{name,test_group_5},parallel]} -> "parallel"; + {test_group_6,[{name,test_group_6},parallel]} -> "parallel"; + {test_group_7,[{name,test_group_7},sequence]} -> "sequence"; + {test_group_8,[{shuffle,_},{name,test_group_8},sequence]} -> + "shuffle & sequence" + end, + {Grs,_} = grs_and_tcs(), + case lists:member(Group, Grs) of + true -> + init = ?config(suite,Config), + ct:comment(io_lib:format("~w, ~s", [Group,Cmt])), + [{Group,Group} | Config]; + false -> + ct:fail({bad_group,Group}) + end. + +end_per_group(Group, Config) -> + {Grs,_} = grs_and_tcs(), + case lists:member(Group, Grs) of + true -> + init = ?config(suite,Config), + Group = ?config(Group,Config), + ok; + false -> + ct:fail({bad_group,Group}) + end. + +%%-------------------------------------------------------------------- +%% Testcase Configuration +%%-------------------------------------------------------------------- + +init_per_testcase(TestCase, Config) -> + {_,TCs} = grs_and_tcs(), + case lists:member(TestCase, TCs) of + true -> + init = ?config(suite,Config), + [{TestCase,TestCase} | Config]; + false -> + ct:fail({unknown_testcase,TestCase}) + end. + +end_per_testcase(TestCase, Config) -> + {_,TCs} = grs_and_tcs(), + case lists:member(TestCase, TCs) of + true -> + init = ?config(suite,Config), + TestCase = ?config(TestCase,Config), + ok; + false -> + ct:fail({unknown_testcase,TestCase}) + end. + + +%%-------------------------------------------------------------------- +%% Testcases +%%-------------------------------------------------------------------- + +testcase_1a() -> + []. +testcase_1a(Config) -> + init = ?config(suite,Config), + case ?config(test_group_1a,Config) of + test_group_1a -> ok; + _ -> + case ?config(test_group_1b,Config) of + test_group_1b -> ok; + _ -> ct:fail(no_group_data) + end + end, + testcase_1a = ?config(testcase_1a,Config), + ok. +testcase_1b() -> + []. +testcase_1b(Config) -> + init = ?config(suite,Config), + case ?config(test_group_1a,Config) of + test_group_1a -> ok; + _ -> + case ?config(test_group_1b,Config) of + test_group_1b -> ok; + _ -> ct:fail(no_group_data) + end + end, + undefined = ?config(testcase_1a,Config), + testcase_1b = ?config(testcase_1b,Config), + ok. + +testcase_1c() -> + []. +testcase_1c(Config) -> + init = ?config(suite,Config), + case ?config(test_group_1a,Config) of + test_group_1a -> ok; + _ -> + case ?config(test_group_1b,Config) of + test_group_1b -> ok; + _ -> ct:fail(no_group_data) + end + end, + undefined = ?config(testcase_1b,Config), + testcase_1c = ?config(testcase_1c,Config), + ok. + +testcase_1() -> + []. +testcase_1(Config) -> + init = ?config(suite,Config), + testcase_1 = ?config(testcase_1,Config), + ok. + +testcase_2() -> + []. +testcase_2(Config) -> + init = ?config(suite,Config), + undefined = ?config(test_group_1a,Config), + undefined = ?config(test_group_1b,Config), + testcase_2 = ?config(testcase_2,Config), + ok. + +testcase_2a() -> + []. +testcase_2a(Config) -> + init = ?config(suite,Config), + test_group_2 = ?config(test_group_2,Config), + testcase_2a = ?config(testcase_2a,Config), + ok. +testcase_2b() -> + []. +testcase_2b(Config) -> + init = ?config(suite,Config), + test_group_2 = ?config(test_group_2,Config), + undefined = ?config(testcase_2a,Config), + testcase_2b = ?config(testcase_2b,Config), + ok. + +testcase_3a() -> + []. +testcase_3a(Config) -> + init = ?config(suite,Config), + test_group_2 = ?config(test_group_2,Config), + test_group_3 = ?config(test_group_3,Config), + undefined = ?config(testcase_2b,Config), + testcase_3a = ?config(testcase_3a,Config), + ok. +testcase_3b() -> + []. +testcase_3b(Config) -> + init = ?config(suite,Config), + test_group_2 = ?config(test_group_2,Config), + test_group_3 = ?config(test_group_3,Config), + undefined = ?config(testcase_3a,Config), + testcase_3b = ?config(testcase_3b,Config), + ok. + +testcase_3() -> + []. +testcase_3(Config) -> + init = ?config(suite,Config), + undefined = ?config(test_group_2,Config), + undefined = ?config(test_group_3,Config), + testcase_3 = ?config(testcase_3,Config), + ok. + +testcase_5a() -> + []. +testcase_5a(Config) -> + init = ?config(suite,Config), + undefined = ?config(test_group_3,Config), + test_group_4 = ?config(test_group_4,Config), + test_group_5 = ?config(test_group_5,Config), + undefined = ?config(testcase_3,Config), + testcase_5a = ?config(testcase_5a,Config), + %% increase chance the done event will come + %% during execution of subgroup (could be + %% tricky to handle) + timer:sleep(3), + ok. +testcase_5b() -> + []. +testcase_5b(Config) -> + init = ?config(suite,Config), + test_group_4 = ?config(test_group_4,Config), + test_group_5 = ?config(test_group_5,Config), + undefined = ?config(testcase_5a,Config), + testcase_5b = ?config(testcase_5b,Config), + ok. + +testcase_7a() -> + []. +testcase_7a(Config) -> + init = ?config(suite,Config), + undefined = ?config(test_group_3,Config), + test_group_4 = ?config(test_group_4,Config), + test_group_5 = ?config(test_group_5,Config), + test_group_6 = ?config(test_group_6,Config), + test_group_7 = ?config(test_group_7,Config), + testcase_7a = ?config(testcase_7a,Config), + ok. +testcase_7b() -> + []. +testcase_7b(Config) -> + init = ?config(suite,Config), + test_group_4 = ?config(test_group_4,Config), + test_group_5 = ?config(test_group_5,Config), + test_group_6 = ?config(test_group_6,Config), + test_group_7 = ?config(test_group_7,Config), + undefined = ?config(testcase_7a,Config), + testcase_7b = ?config(testcase_7b,Config), + ok. + +testcase_8a() -> + []. +testcase_8a(Config) -> + init = ?config(suite,Config), + undefined = ?config(test_group_3,Config), + test_group_4 = ?config(test_group_4,Config), + test_group_5 = ?config(test_group_5,Config), + test_group_6 = ?config(test_group_6,Config), + test_group_8 = ?config(test_group_8,Config), + testcase_8a = ?config(testcase_8a,Config), + ok. +testcase_8b() -> + []. +testcase_8b(Config) -> + init = ?config(suite,Config), + test_group_4 = ?config(test_group_4,Config), + test_group_5 = ?config(test_group_5,Config), + test_group_6 = ?config(test_group_6,Config), + test_group_8 = ?config(test_group_8,Config), + undefined = ?config(testcase_8a,Config), + testcase_8b = ?config(testcase_8b,Config), + ok. diff --git a/lib/common_test/test/ct_testspec_1_SUITE_data/groups_2/groups_21_SUITE.erl b/lib/common_test/test/ct_testspec_1_SUITE_data/groups_2/groups_21_SUITE.erl new file mode 100644 index 0000000000..2533ac8e84 --- /dev/null +++ b/lib/common_test/test/ct_testspec_1_SUITE_data/groups_2/groups_21_SUITE.erl @@ -0,0 +1,281 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2010. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +-module(groups_21_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). + +%%==================================================================== +%% COMMON TEST CALLBACK FUNCTIONS +%%==================================================================== + +suite() -> + [{timetrap,{minutes,1}}]. + +groups() -> + [ + {test_group_1a, [testcase_1a,testcase_1b]}, + + {test_group_1b, [], [testcase_1a,testcase_1b]}, + + {test_group_2, [], [testcase_2a, + + {test_group_3, [], [testcase_3a, + testcase_3b]}, + testcase_2b]}, + + {test_group_4, [{test_group_5, [], [testcase_5a, + + {group, test_group_6}, + + testcase_5b]}]}, + + {test_group_6, [{group, test_group_7}]}, + + {test_group_7, [testcase_7a,testcase_7b]} + ]. + +all() -> + [testcase_1, + {group, test_group_1a}, + {group, test_group_1b}, + testcase_2, + {group, test_group_2}, + testcase_3, + {group, test_group_4}]. + +%% this func only for internal test purposes +grs_and_tcs() -> + {[ + test_group_1a, test_group_1b, + test_group_2, test_group_3, + test_group_4, test_group_5, + test_group_6, test_group_7 + ], + [ + testcase_1, + testcase_1a, testcase_1b, + testcase_2, + testcase_2a, testcase_2b, + testcase_3a, testcase_3b, + testcase_3, + testcase_5a, testcase_5b, + testcase_7a, testcase_7b + ]}. + +%%-------------------------------------------------------------------- +%% Suite Configuration +%%-------------------------------------------------------------------- + +init_per_suite(Config) -> + [{suite,init}|Config]. + +end_per_suite(Config) -> + init = ?config(suite,Config). + +%%-------------------------------------------------------------------- +%% Group Configuration +%%-------------------------------------------------------------------- + +init_per_group(Group, Config) -> + [{name,Group}] = ?config(tc_group_properties,Config), + {Grs,_} = grs_and_tcs(), + case lists:member(Group, Grs) of + true -> + ct:comment(io_lib:format("~w", [Group])), + init = ?config(suite,Config), + [{Group,Group} | Config]; + false -> + ct:fail({bad_group,Group}) + end. + +end_per_group(Group, Config) -> + {Grs,_} = grs_and_tcs(), + case lists:member(Group, Grs) of + true -> + init = ?config(suite,Config), + Group = ?config(Group,Config), + ok; + false -> + ct:fail({bad_group,Group}) + end. + +%%-------------------------------------------------------------------- +%% Testcase Configuration +%%-------------------------------------------------------------------- + +init_per_testcase(TestCase, Config) -> + {_,TCs} = grs_and_tcs(), + case lists:member(TestCase, TCs) of + true -> + init = ?config(suite,Config), + [{TestCase,TestCase} | Config]; + false -> + ct:fail({unknown_testcase,TestCase}) + end. + +end_per_testcase(TestCase, Config) -> + {_,TCs} = grs_and_tcs(), + case lists:member(TestCase, TCs) of + true -> + init = ?config(suite,Config), + TestCase = ?config(TestCase,Config), + ok; + false -> + ct:fail({unknown_testcase,TestCase}) + end. + + +%%-------------------------------------------------------------------- +%% Testcases +%%-------------------------------------------------------------------- + +testcase_1() -> + []. +testcase_1(Config) -> + init = ?config(suite,Config), + testcase_1 = ?config(testcase_1,Config), + ok. + +testcase_1a() -> + []. +testcase_1a(Config) -> + init = ?config(suite,Config), + case ?config(test_group_1a,Config) of + test_group_1a -> ok; + _ -> + case ?config(test_group_1b,Config) of + test_group_1b -> ok; + _ -> ct:fail(no_group_data) + end + end, + testcase_1a = ?config(testcase_1a,Config), + ok. +testcase_1b() -> + []. +testcase_1b(Config) -> + init = ?config(suite,Config), + case ?config(test_group_1a,Config) of + test_group_1a -> ok; + _ -> + case ?config(test_group_1b,Config) of + test_group_1b -> ok; + _ -> ct:fail(no_group_data) + end + end, + undefined = ?config(testcase_1a,Config), + testcase_1b = ?config(testcase_1b,Config), + ok. + +testcase_2() -> + []. +testcase_2(Config) -> + init = ?config(suite,Config), + undefined = ?config(test_group_1a,Config), + undefined = ?config(test_group_1b,Config), + testcase_2 = ?config(testcase_2,Config), + ok. + +testcase_2a() -> + []. +testcase_2a(Config) -> + init = ?config(suite,Config), + test_group_2 = ?config(test_group_2,Config), + testcase_2a = ?config(testcase_2a,Config), + ok. +testcase_2b() -> + []. +testcase_2b(Config) -> + init = ?config(suite,Config), + test_group_2 = ?config(test_group_2,Config), + undefined = ?config(testcase_2a,Config), + testcase_2b = ?config(testcase_2b,Config), + ok. + +testcase_3a() -> + []. +testcase_3a(Config) -> + init = ?config(suite,Config), + test_group_2 = ?config(test_group_2,Config), + test_group_3 = ?config(test_group_3,Config), + undefined = ?config(testcase_2b,Config), + testcase_3a = ?config(testcase_3a,Config), + ok. +testcase_3b() -> + []. +testcase_3b(Config) -> + init = ?config(suite,Config), + test_group_2 = ?config(test_group_2,Config), + test_group_3 = ?config(test_group_3,Config), + undefined = ?config(testcase_3a,Config), + testcase_3b = ?config(testcase_3b,Config), + ok. + +testcase_3() -> + []. +testcase_3(Config) -> + init = ?config(suite,Config), + undefined = ?config(test_group_2,Config), + undefined = ?config(test_group_3,Config), + testcase_3 = ?config(testcase_3,Config), + ok. + +testcase_5a() -> + []. +testcase_5a(Config) -> + init = ?config(suite,Config), + undefined = ?config(test_group_3,Config), + test_group_4 = ?config(test_group_4,Config), + test_group_5 = ?config(test_group_5,Config), + undefined = ?config(testcase_3,Config), + testcase_5a = ?config(testcase_5a,Config), + ok. +testcase_5b() -> + []. +testcase_5b(Config) -> + init = ?config(suite,Config), + test_group_4 = ?config(test_group_4,Config), + test_group_5 = ?config(test_group_5,Config), + undefined = ?config(testcase_5a,Config), + testcase_5b = ?config(testcase_5b,Config), + ok. + +testcase_7a() -> + []. +testcase_7a(Config) -> + init = ?config(suite,Config), + undefined = ?config(test_group_3,Config), + test_group_4 = ?config(test_group_4,Config), + test_group_5 = ?config(test_group_5,Config), + test_group_6 = ?config(test_group_6,Config), + test_group_7 = ?config(test_group_7,Config), + testcase_7a = ?config(testcase_7a,Config), + ok. +testcase_7b() -> + []. +testcase_7b(Config) -> + init = ?config(suite,Config), + test_group_4 = ?config(test_group_4,Config), + test_group_5 = ?config(test_group_5,Config), + test_group_6 = ?config(test_group_6,Config), + test_group_7 = ?config(test_group_7,Config), + undefined = ?config(testcase_7a,Config), + testcase_7b = ?config(testcase_7b,Config), + ok. diff --git a/lib/common_test/test/ct_testspec_1_SUITE_data/groups_2/groups_22_SUITE.erl b/lib/common_test/test/ct_testspec_1_SUITE_data/groups_2/groups_22_SUITE.erl new file mode 100644 index 0000000000..cd517876df --- /dev/null +++ b/lib/common_test/test/ct_testspec_1_SUITE_data/groups_2/groups_22_SUITE.erl @@ -0,0 +1,314 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2010. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +-module(groups_22_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). + +%%==================================================================== +%% COMMON TEST CALLBACK FUNCTIONS +%%==================================================================== + +suite() -> + [{timetrap,{minutes,1}}]. + +groups() -> + [ + {test_group_1a, [shuffle], [testcase_1a,testcase_1b,testcase_1c]}, + + {test_group_1b, [parallel], [testcase_1a,testcase_1b]}, + + {test_group_2, [parallel], [testcase_2a, + + {test_group_3, [{repeat,1}], + [testcase_3a, testcase_3b]}, + + testcase_2b]}, + + {test_group_4, [{test_group_5, [parallel], [testcase_5a, + + {group, test_group_6}, + + testcase_5b]}]}, + + {test_group_6, [parallel], [{group, test_group_7}]}, + + {test_group_7, [sequence], [testcase_7a,testcase_7b]} + ]. + +all() -> + [{group, test_group_1a}, + {group, test_group_1b}, + testcase_1, + testcase_2, + {group, test_group_2}, + testcase_3, + {group, test_group_4}]. + +%% this func only for internal test purposes +grs_and_tcs() -> + {[ + test_group_1a, test_group_1b, + test_group_2, test_group_3, + test_group_4, test_group_5, + test_group_6, test_group_7 + ], + [ + testcase_1a, testcase_1b, testcase_1c, + testcase_1, + testcase_2, + testcase_2a, testcase_2b, + testcase_3a, testcase_3b, + testcase_3, + testcase_5a, testcase_5b, + testcase_7a, testcase_7b + ]}. + +%%-------------------------------------------------------------------- +%% Suite Configuration +%%-------------------------------------------------------------------- + +init_per_suite(Config) -> + [{suite,init}|Config]. + +end_per_suite(Config) -> + init = ?config(suite,Config). + +%%-------------------------------------------------------------------- +%% Group Configuration +%%-------------------------------------------------------------------- + +init_per_group(Group, Config) -> + Cmt = + case {Group,?config(tc_group_properties,Config)} of + {test_group_1a,[{shuffle,S},{name,test_group_1a}]} -> + io_lib:format("shuffled, ~w", [S]); + {test_group_1b,[{name,test_group_1b},parallel]} -> "parallel"; + {test_group_2,[{name,test_group_2},parallel]} -> "parallel"; + {test_group_3,[{name,test_group_3},{repeat,1}]} -> "repeat 1"; + {test_group_3,[{name,test_group_3}]} -> "repeat 0"; + {test_group_4,[{name,test_group_4}]} -> ok; + {test_group_5,[{name,test_group_5},parallel]} -> "parallel"; + {test_group_6,[{name,test_group_6},parallel]} -> "parallel"; + {test_group_7,[{name,test_group_7},sequence]} -> "sequence" + end, + {Grs,_} = grs_and_tcs(), + case lists:member(Group, Grs) of + true -> + init = ?config(suite,Config), + ct:comment(io_lib:format("~w, ~s", [Group,Cmt])), + [{Group,Group} | Config]; + false -> + ct:fail({bad_group,Group}) + end. + +end_per_group(Group, Config) -> + {Grs,_} = grs_and_tcs(), + case lists:member(Group, Grs) of + true -> + init = ?config(suite,Config), + Group = ?config(Group,Config), + ok; + false -> + ct:fail({bad_group,Group}) + end. + +%%-------------------------------------------------------------------- +%% Testcase Configuration +%%-------------------------------------------------------------------- + +init_per_testcase(TestCase, Config) -> + {_,TCs} = grs_and_tcs(), + case lists:member(TestCase, TCs) of + true -> + init = ?config(suite,Config), + [{TestCase,TestCase} | Config]; + false -> + ct:fail({unknown_testcase,TestCase}) + end. + +end_per_testcase(TestCase, Config) -> + {_,TCs} = grs_and_tcs(), + case lists:member(TestCase, TCs) of + true -> + init = ?config(suite,Config), + TestCase = ?config(TestCase,Config), + ok; + false -> + ct:fail({unknown_testcase,TestCase}) + end. + + +%%-------------------------------------------------------------------- +%% Testcases +%%-------------------------------------------------------------------- + +testcase_1a() -> + []. +testcase_1a(Config) -> + init = ?config(suite,Config), + case ?config(test_group_1a,Config) of + test_group_1a -> ok; + _ -> + case ?config(test_group_1b,Config) of + test_group_1b -> ok; + _ -> ct:fail(no_group_data) + end + end, + testcase_1a = ?config(testcase_1a,Config), + ok. +testcase_1b() -> + []. +testcase_1b(Config) -> + init = ?config(suite,Config), + case ?config(test_group_1a,Config) of + test_group_1a -> ok; + _ -> + case ?config(test_group_1b,Config) of + test_group_1b -> ok; + _ -> ct:fail(no_group_data) + end + end, + undefined = ?config(testcase_1a,Config), + testcase_1b = ?config(testcase_1b,Config), + ok. + +testcase_1c() -> + []. +testcase_1c(Config) -> + init = ?config(suite,Config), + case ?config(test_group_1a,Config) of + test_group_1a -> ok; + _ -> + case ?config(test_group_1b,Config) of + test_group_1b -> ok; + _ -> ct:fail(no_group_data) + end + end, + undefined = ?config(testcase_1b,Config), + testcase_1c = ?config(testcase_1c,Config), + ok. + +testcase_1() -> + []. +testcase_1(Config) -> + init = ?config(suite,Config), + testcase_1 = ?config(testcase_1,Config), + ok. + +testcase_2() -> + []. +testcase_2(Config) -> + init = ?config(suite,Config), + undefined = ?config(test_group_1a,Config), + undefined = ?config(test_group_1b,Config), + testcase_2 = ?config(testcase_2,Config), + ok. + +testcase_2a() -> + []. +testcase_2a(Config) -> + init = ?config(suite,Config), + test_group_2 = ?config(test_group_2,Config), + testcase_2a = ?config(testcase_2a,Config), + ok. +testcase_2b() -> + []. +testcase_2b(Config) -> + init = ?config(suite,Config), + test_group_2 = ?config(test_group_2,Config), + undefined = ?config(testcase_2a,Config), + testcase_2b = ?config(testcase_2b,Config), + ok. + +testcase_3a() -> + []. +testcase_3a(Config) -> + init = ?config(suite,Config), + test_group_2 = ?config(test_group_2,Config), + test_group_3 = ?config(test_group_3,Config), + undefined = ?config(testcase_2b,Config), + testcase_3a = ?config(testcase_3a,Config), + ok. +testcase_3b() -> + []. +testcase_3b(Config) -> + init = ?config(suite,Config), + test_group_2 = ?config(test_group_2,Config), + test_group_3 = ?config(test_group_3,Config), + undefined = ?config(testcase_3a,Config), + testcase_3b = ?config(testcase_3b,Config), + ok. + +testcase_3() -> + []. +testcase_3(Config) -> + init = ?config(suite,Config), + undefined = ?config(test_group_2,Config), + undefined = ?config(test_group_3,Config), + testcase_3 = ?config(testcase_3,Config), + ok. + +testcase_5a() -> + []. +testcase_5a(Config) -> + init = ?config(suite,Config), + undefined = ?config(test_group_3,Config), + test_group_4 = ?config(test_group_4,Config), + test_group_5 = ?config(test_group_5,Config), + undefined = ?config(testcase_3,Config), + testcase_5a = ?config(testcase_5a,Config), + %% increase chance the done event will come + %% during execution of subgroup (could be + %% tricky to handle) + timer:sleep(3), + ok. +testcase_5b() -> + []. +testcase_5b(Config) -> + init = ?config(suite,Config), + test_group_4 = ?config(test_group_4,Config), + test_group_5 = ?config(test_group_5,Config), + undefined = ?config(testcase_5a,Config), + testcase_5b = ?config(testcase_5b,Config), + ok. + +testcase_7a() -> + []. +testcase_7a(Config) -> + init = ?config(suite,Config), + undefined = ?config(test_group_3,Config), + test_group_4 = ?config(test_group_4,Config), + test_group_5 = ?config(test_group_5,Config), + test_group_6 = ?config(test_group_6,Config), + test_group_7 = ?config(test_group_7,Config), + testcase_7a = ?config(testcase_7a,Config), + ok. +testcase_7b() -> + []. +testcase_7b(Config) -> + init = ?config(suite,Config), + test_group_4 = ?config(test_group_4,Config), + test_group_5 = ?config(test_group_5,Config), + test_group_6 = ?config(test_group_6,Config), + test_group_7 = ?config(test_group_7,Config), + undefined = ?config(testcase_7a,Config), + testcase_7b = ?config(testcase_7b,Config), + ok. diff --git a/lib/common_test/test/ct_testspec_1_SUITE_data/suites_1/simple_1_SUITE.erl b/lib/common_test/test/ct_testspec_1_SUITE_data/suites_1/simple_1_SUITE.erl new file mode 100644 index 0000000000..b789851134 --- /dev/null +++ b/lib/common_test/test/ct_testspec_1_SUITE_data/suites_1/simple_1_SUITE.erl @@ -0,0 +1,146 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2008-2010. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +-module(simple_1_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). + +%%-------------------------------------------------------------------- +%% COMMON TEST CALLBACK FUNCTIONS +%%-------------------------------------------------------------------- + +%%-------------------------------------------------------------------- +%% Function: suite() -> Info +%% +%% Info = [tuple()] +%% List of key/value pairs. +%% +%% Description: Returns list of tuples to set default properties +%% for the suite. +%% +%% Note: The suite/0 function is only meant to be used to return +%% default data values, not perform any other operations. +%%-------------------------------------------------------------------- +suite() -> + [ + {timetrap,{seconds,10}} + ]. + +%%-------------------------------------------------------------------- +%% Function: init_per_suite(Config0) -> +%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1} +%% +%% Config0 = Config1 = [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Reason = term() +%% The reason for skipping the suite. +%% +%% Description: Initialization before the suite. +%% +%% Note: This function is free to add any key/value pairs to the Config +%% variable, but should NOT alter/remove any existing entries. +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + [{ips,ips_data} | Config]. + +%%-------------------------------------------------------------------- +%% Function: end_per_suite(Config0) -> void() | {save_config,Config1} +%% +%% Config0 = Config1 = [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% +%% Description: Cleanup after the suite. +%%-------------------------------------------------------------------- +end_per_suite(Config) -> + ips_data = ?config(ips, Config). + +%%-------------------------------------------------------------------- +%% Function: init_per_testcase(TestCase, Config0) -> +%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1} +%% +%% TestCase = atom() +%% Name of the test case that is about to run. +%% Config0 = Config1 = [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Reason = term() +%% The reason for skipping the test case. +%% +%% Description: Initialization before each test case. +%% +%% Note: This function is free to add any key/value pairs to the Config +%% variable, but should NOT alter/remove any existing entries. +%%-------------------------------------------------------------------- +init_per_testcase(TestCase, Config) -> + [{TestCase,{TestCase,data}} | Config]. + +%%-------------------------------------------------------------------- +%% Function: end_per_testcase(TestCase, Config0) -> +%% void() | {save_config,Config1} +%% +%% TestCase = atom() +%% Name of the test case that is finished. +%% Config0 = Config1 = [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% +%% Description: Cleanup after each test case. +%%-------------------------------------------------------------------- +end_per_testcase(TestCase, Config) -> + {TestCase,data} = ?config(TestCase, Config). + +%%-------------------------------------------------------------------- +%% Function: all() -> TestCases | {skip,Reason} +%% +%% TestCases = [TestCase | {sequence,SeqName}] +%% TestCase = atom() +%% Name of a test case. +%% SeqName = atom() +%% Name of a test case sequence. +%% Reason = term() +%% The reason for skipping all test cases. +%% +%% Description: Returns the list of test cases that are to be executed. +%%-------------------------------------------------------------------- +all() -> + [tc1, + tc2]. + + +%%-------------------------------------------------------------------- +%% TEST CASES +%%-------------------------------------------------------------------- + +tc1() -> + [{userdata,{info, "This is a testcase"}}]. + +tc1(Config) -> + ips_data = ?config(ips, Config), + {tc1,data} = ?config(tc1, Config), + ok. + +tc2() -> + [{timetrap,5000}]. + +tc2(Config) -> + ips_data = ?config(ips, Config), + undefined = ?config(tc1, Config), + {tc2,data} = ?config(tc2, Config), + ok. diff --git a/lib/common_test/test/ct_testspec_1_SUITE_data/suites_1/simple_2_SUITE.erl b/lib/common_test/test/ct_testspec_1_SUITE_data/suites_1/simple_2_SUITE.erl new file mode 100644 index 0000000000..eb7e9cdf7b --- /dev/null +++ b/lib/common_test/test/ct_testspec_1_SUITE_data/suites_1/simple_2_SUITE.erl @@ -0,0 +1,146 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2008-2010. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +-module(simple_2_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). + +%%-------------------------------------------------------------------- +%% COMMON TEST CALLBACK FUNCTIONS +%%-------------------------------------------------------------------- + +%%-------------------------------------------------------------------- +%% Function: suite() -> Info +%% +%% Info = [tuple()] +%% List of key/value pairs. +%% +%% Description: Returns list of tuples to set default properties +%% for the suite. +%% +%% Note: The suite/0 function is only meant to be used to return +%% default data values, not perform any other operations. +%%-------------------------------------------------------------------- +suite() -> + [ + {timetrap,{seconds,10}} + ]. + +%%-------------------------------------------------------------------- +%% Function: init_per_suite(Config0) -> +%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1} +%% +%% Config0 = Config1 = [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Reason = term() +%% The reason for skipping the suite. +%% +%% Description: Initialization before the suite. +%% +%% Note: This function is free to add any key/value pairs to the Config +%% variable, but should NOT alter/remove any existing entries. +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + [{ips,ips_data} | Config]. + +%%-------------------------------------------------------------------- +%% Function: end_per_suite(Config0) -> void() | {save_config,Config1} +%% +%% Config0 = Config1 = [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% +%% Description: Cleanup after the suite. +%%-------------------------------------------------------------------- +end_per_suite(Config) -> + ips_data = ?config(ips, Config). + +%%-------------------------------------------------------------------- +%% Function: init_per_testcase(TestCase, Config0) -> +%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1} +%% +%% TestCase = atom() +%% Name of the test case that is about to run. +%% Config0 = Config1 = [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Reason = term() +%% The reason for skipping the test case. +%% +%% Description: Initialization before each test case. +%% +%% Note: This function is free to add any key/value pairs to the Config +%% variable, but should NOT alter/remove any existing entries. +%%-------------------------------------------------------------------- +init_per_testcase(TestCase, Config) -> + [{TestCase,{TestCase,data}} | Config]. + +%%-------------------------------------------------------------------- +%% Function: end_per_testcase(TestCase, Config0) -> +%% void() | {save_config,Config1} +%% +%% TestCase = atom() +%% Name of the test case that is finished. +%% Config0 = Config1 = [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% +%% Description: Cleanup after each test case. +%%-------------------------------------------------------------------- +end_per_testcase(TestCase, Config) -> + {TestCase,data} = ?config(TestCase, Config). + +%%-------------------------------------------------------------------- +%% Function: all() -> TestCases | {skip,Reason} +%% +%% TestCases = [TestCase | {sequence,SeqName}] +%% TestCase = atom() +%% Name of a test case. +%% SeqName = atom() +%% Name of a test case sequence. +%% Reason = term() +%% The reason for skipping all test cases. +%% +%% Description: Returns the list of test cases that are to be executed. +%%-------------------------------------------------------------------- +all() -> + [tc1, + tc2]. + + +%%-------------------------------------------------------------------- +%% TEST CASES +%%-------------------------------------------------------------------- + +tc1() -> + [{userdata,{info, "This is a testcase"}}]. + +tc1(Config) -> + ips_data = ?config(ips, Config), + {tc1,data} = ?config(tc1, Config), + ok. + +tc2() -> + [{timetrap,5000}]. + +tc2(Config) -> + ips_data = ?config(ips, Config), + undefined = ?config(tc1, Config), + {tc2,data} = ?config(tc2, Config), + ok. diff --git a/lib/common_test/vsn.mk b/lib/common_test/vsn.mk index 2947c6a152..413ef21df3 100644 --- a/lib/common_test/vsn.mk +++ b/lib/common_test/vsn.mk @@ -1,3 +1,3 @@ -COMMON_TEST_VSN = 1.5 +COMMON_TEST_VSN = 1.5.1 diff --git a/lib/compiler/src/beam_dict.erl b/lib/compiler/src/beam_dict.erl index 4ffe8bc606..a1f994dfbd 100644 --- a/lib/compiler/src/beam_dict.erl +++ b/lib/compiler/src/beam_dict.erl @@ -33,7 +33,7 @@ exports = [] :: [{label(), arity(), label()}], locals = [] :: [{label(), arity(), label()}], imports = gb_trees:empty() :: gb_tree(), %{{M,F,A},Index} - strings = [] :: [string()], %String pool + strings = <<>> :: binary(), %String pool lambdas = [], %[{...}] literals = dict:new() :: dict(), %Format: {Literal,Number} next_atom = 1 :: pos_integer(), @@ -119,10 +119,11 @@ import(Mod0, Name0, Arity, #asm{imports=Imp0,next_import=NextIndex}=D0) string(Str, Dict) when is_list(Str) -> #asm{strings=Strings,string_offset=NextOffset} = Dict, - case old_string(Str, Strings) of + StrBin = list_to_binary(Str), + case old_string(StrBin, Strings) of none -> - NewDict = Dict#asm{strings=Strings++Str, - string_offset=NextOffset+length(Str)}, + NewDict = Dict#asm{strings = <<Strings/binary,StrBin/binary>>, + string_offset=NextOffset+byte_size(StrBin)}, {NextOffset,NewDict}; Offset when is_integer(Offset) -> {NextOffset-Offset,Dict} @@ -187,7 +188,7 @@ import_table(#asm{imports=Imp,next_import=NumImports}) -> ImpTab = [MFA || {MFA,_} <- Sorted], {NumImports,ImpTab}. --spec string_table(bdict()) -> {non_neg_integer(), [string()]}. +-spec string_table(bdict()) -> {non_neg_integer(), binary()}. string_table(#asm{strings=Strings,string_offset=Size}) -> {Size,Strings}. @@ -217,15 +218,12 @@ literal_table(#asm{literals=Tab,next_literal=NumLiterals}) -> my_term_to_binary(Term) -> term_to_binary(Term, [{minor_version,1}]). -%% Search for string Str in the string pool Pool. +%% Search for binary string Str in the binary string pool Pool. %% old_string(Str, Pool) -> none | Index --spec old_string(string(), [string()]) -> 'none' | pos_integer(). - -old_string([C|Str]=Str0, [C|Pool]) -> - case lists:prefix(Str, Pool) of - true -> length(Pool)+1; - false -> old_string(Str0, Pool) - end; -old_string([_|_]=Str, [_|Pool]) -> - old_string(Str, Pool); -old_string([_|_], []) -> none. +-spec old_string(binary(), binary()) -> 'none' | pos_integer(). + +old_string(Str, Pool) -> + case binary:match(Pool, Str) of + nomatch -> none; + {Start,_Length} -> byte_size(Pool) - Start + end. diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl index 4642fb68b3..ed7a9144a8 100644 --- a/lib/compiler/src/compile.erl +++ b/lib/compiler/src/compile.erl @@ -41,8 +41,7 @@ -type option() :: atom() | {atom(), term()} | {'d', atom(), term()}. --type line() :: integer(). --type err_info() :: {line(), module(), term()}. %% ErrorDescriptor +-type err_info() :: {erl_scan:line(), module(), term()}. %% ErrorDescriptor -type errors() :: [{file:filename(), [err_info()]}]. -type warnings() :: [{file:filename(), [err_info()]}]. -type mod_ret() :: {'ok', module()} @@ -70,7 +69,7 @@ file(File) -> file(File, ?DEFAULT_OPTIONS). --spec file(module() | file:filename(), [option()]) -> comp_ret(). +-spec file(module() | file:filename(), [option()] | option()) -> comp_ret(). file(File, Opts) when is_list(Opts) -> do_compile({file,File}, Opts++env_default_opts()); @@ -88,6 +87,8 @@ forms(Forms, Opt) when is_atom(Opt) -> %% would have generated a Beam file, false otherwise (if only a binary or a %% listing file would have been generated). +-spec output_generated([option()]) -> boolean(). + output_generated(Opts) -> noenv_output_generated(Opts++env_default_opts()). @@ -96,6 +97,8 @@ output_generated(Opts) -> %% for default options. %% +-spec noenv_file(module() | file:filename(), [option()] | option()) -> comp_ret(). + noenv_file(File, Opts) when is_list(Opts) -> do_compile({file,File}, Opts); noenv_file(File, Opt) -> @@ -106,6 +109,8 @@ noenv_forms(Forms, Opts) when is_list(Opts) -> noenv_forms(Forms, Opt) when is_atom(Opt) -> noenv_forms(Forms, [Opt|?DEFAULT_OPTIONS]). +-spec noenv_output_generated([option()]) -> boolean(). + noenv_output_generated(Opts) -> any(fun ({save_binary,_F}) -> true; (_Other) -> false @@ -216,16 +221,16 @@ format_error({module_name,Mod,Filename}) -> [Mod,Filename]). %% The compile state record. --record(compile, {filename="", - dir="", - base="", - ifile="", - ofile="", +-record(compile, {filename="" :: file:filename(), + dir="" :: file:filename(), + base="" :: file:filename(), + ifile="" :: file:filename(), + ofile="" :: file:filename(), module=[], code=[], core_code=[], abstract_code=[], %Abstract code for debugger. - options=[], + options=[] :: [option()], errors=[], warnings=[]}). @@ -362,7 +367,7 @@ mpf(Ms) -> [{File,[M || {F,M} <- Ms, F =:= File]} || File <- lists:usort([F || {F,_} <- Ms])]. -%% passes(form|file, [Option]) -> [{Name,PassFun}] +%% passes(forms|file, [Option]) -> [{Name,PassFun}] %% Figure out which passes that need to be run. passes(forms, Opts) -> @@ -828,7 +833,6 @@ get_core_transforms(Opts) -> [M || {core_transform,M} <- Opts]. core_transforms(St) -> %% The options field holds the complete list of options at this - Ts = get_core_transforms(St#compile.options), foldl_core_transforms(St, Ts). @@ -1172,12 +1176,12 @@ write_binary(Name, Bin, St) -> %% report_errors(State) -> ok %% report_warnings(State) -> ok -report_errors(St) -> - case member(report_errors, St#compile.options) of +report_errors(#compile{options=Opts,errors=Errors}) -> + case member(report_errors, Opts) of true -> foreach(fun ({{F,_L},Eds}) -> list_errors(F, Eds); ({F,Eds}) -> list_errors(F, Eds) end, - St#compile.errors); + Errors); false -> ok end. diff --git a/lib/compiler/test/compilation_SUITE.erl b/lib/compiler/test/compilation_SUITE.erl index d4843c9eba..9c06740816 100644 --- a/lib/compiler/test/compilation_SUITE.erl +++ b/lib/compiler/test/compilation_SUITE.erl @@ -46,7 +46,7 @@ all(suite) -> trycatch_4, opt_crash, otp_5404,otp_5436,otp_5481,otp_5553,otp_5632, otp_5714,otp_5872,otp_6121,otp_6121a,otp_6121b, - otp_7202,otp_7345,on_load + otp_7202,otp_7345,on_load,string_table ]. -define(comp(N), @@ -596,4 +596,15 @@ otp_7345(ObjRef, _RdEnv, Args) -> 10}, id(LlUnitdataReq). +%% Check the generation of the string table. + +string_table(Config) when is_list(Config) -> + ?line DataDir = ?config(data_dir, Config), + ?line File = filename:join(DataDir, "string_table.erl"), + ?line {ok,string_table,Beam,[]} = compile:file(File, [return, binary]), + ?line {ok,{string_table,[StringTableChunk]}} = beam_lib:chunks(Beam, ["StrT"]), + ?line {"StrT", <<"stringabletringtable">>} = StringTableChunk, + ok. + + id(I) -> I. diff --git a/lib/compiler/test/compilation_SUITE_data/string_table.erl b/lib/compiler/test/compilation_SUITE_data/string_table.erl new file mode 100644 index 0000000000..1da1d015dd --- /dev/null +++ b/lib/compiler/test/compilation_SUITE_data/string_table.erl @@ -0,0 +1,8 @@ +-module(string_table). +-export([f/1, g/1]). + +f(<<"string">>) -> string; +f(<<"stringtable">>) -> stringtable. + +g(<<"stringtable">>) -> stringtable; +g(<<"table">>) -> table. diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl index 39512d27e1..19fa495b7d 100644 --- a/lib/crypto/src/crypto.erl +++ b/lib/crypto/src/crypto.erl @@ -195,8 +195,8 @@ sha_final(_Context) -> ?nif_stub. %% %% MD5_MAC %% --spec md5_mac(iodata(), iodata()) -> binary. --spec md5_mac_96(iodata(), iodata()) -> binary. +-spec md5_mac(iodata(), iodata()) -> binary(). +-spec md5_mac_96(iodata(), iodata()) -> binary(). md5_mac(Key, Data) -> md5_mac_n(Key,Data,16). @@ -209,8 +209,8 @@ md5_mac_n(_Key,_Data,_MacSz) -> ?nif_stub. %% %% SHA_MAC %% --spec sha_mac(iodata(), iodata()) -> binary. --spec sha_mac_96(iodata(), iodata()) -> binary. +-spec sha_mac(iodata(), iodata()) -> binary(). +-spec sha_mac_96(iodata(), iodata()) -> binary(). sha_mac(Key, Data) -> sha_mac_n(Key,Data,20). diff --git a/lib/debugger/src/dbg_icmd.erl b/lib/debugger/src/dbg_icmd.erl index 7ccb9793a3..a26b16c82d 100644 --- a/lib/debugger/src/dbg_icmd.erl +++ b/lib/debugger/src/dbg_icmd.erl @@ -94,7 +94,7 @@ break_p(Mod, Line, Le, Bs) -> Bool = case Cond of null -> true; {CM, CN} -> - try apply(CM, CN, [Bs]) of + try CM:CN(Bs) of true -> true; false -> false; _Term -> false @@ -245,7 +245,7 @@ handle_int_msg({attached, AttPid}, Status, _Bs, %% Tell attached process in which module evalution is located if - Le==1 -> + Le =:= 1 -> tell_attached({attached, undefined, -1, get(trace)}); true -> tell_attached({attached, M, Line, get(trace)}), @@ -269,7 +269,7 @@ handle_int_msg(detached, _Status, _Bs, _Ieval) -> handle_int_msg({old_code,Mod}, Status, Bs, #ieval{level=Le,module=M}=Ieval) -> if - Status==idle, Le==1 -> + Status =:= idle, Le =:= 1 -> erase([Mod|db]), put(cache, []); true -> @@ -352,9 +352,9 @@ set_stack_trace(true) -> set_stack_trace(all); set_stack_trace(Flag) -> if - Flag==false -> + Flag =:= false -> put(stack, []); - Flag==no_tail; Flag==all -> + Flag =:= no_tail; Flag =:= all -> ignore end, put(trace_stack, Flag), @@ -367,7 +367,7 @@ bindings(Bs, nostack) -> Bs; bindings(Bs, SP) -> case dbg_ieval:stack_level() of - Le when SP>Le -> + Le when SP > Le -> Bs; _ -> dbg_ieval:bindings(SP) @@ -377,7 +377,6 @@ messages() -> {messages, Msgs} = erlang:process_info(get(self), messages), Msgs. - %%==================================================================== %% Evaluating expressions within process context %%==================================================================== @@ -398,7 +397,7 @@ eval_restricted({From,_Mod,Cmd,SP}, Bs) -> From ! {self(), {eval_rsp, Rsp}} end. -eval_nonrestricted({From,Mod,Cmd,SP}, Bs, #ieval{level=Le}) when SP<Le-> +eval_nonrestricted({From,Mod,Cmd,SP}, Bs, #ieval{level=Le}) when SP < Le-> %% Evaluate in stack eval_restricted({From, Mod, Cmd, SP}, Bs), Bs; @@ -424,15 +423,15 @@ eval_nonrestricted({From, _Mod, Cmd, _SP}, Bs, eval_nonrestricted_1({match,_,{var,_,Var},Expr}, Bs, Ieval) -> {value,Res,Bs2} = dbg_ieval:eval_expr(Expr, Bs, Ieval#ieval{last_call=false}), - Bs3 = case lists:keysearch(Var, 1, Bs) of - {value, {Var,_Value}} -> + Bs3 = case lists:keyfind(Var, 1, Bs) of + {Var,_Value} -> lists:keyreplace(Var, 1, Bs2, {Var,Res}); false -> [{Var,Res} | Bs2] end, {Res,Bs3}; eval_nonrestricted_1({var,_,Var}, Bs, _Ieval) -> - Res = case lists:keysearch(Var, 1, Bs) of - {value, {Var, Value}} -> Value; + Res = case lists:keyfind(Var, 1, Bs) of + {Var, Value} -> Value; false -> unbound end, {Res,Bs}; @@ -458,7 +457,6 @@ parse_cmd(Cmd, LineNo) -> {ok,Forms} = erl_parse:parse_exprs(Tokens), Forms. - %%==================================================================== %% Library functions for attached process handling %%==================================================================== @@ -470,13 +468,12 @@ tell_attached(Msg) -> AttPid ! {self(), Msg} end. - %%==================================================================== %% get_binding/2 %%==================================================================== get_binding(Var, Bs) -> - case lists:keysearch(Var, 1, Bs) of - {value, {Var, Value}} -> {value, Value}; + case lists:keyfind(Var, 1, Bs) of + {Var, Value} -> {value, Value}; false -> unbound end. diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl index c13fda7ac1..476dfd8796 100644 --- a/lib/debugger/src/dbg_ieval.erl +++ b/lib/debugger/src/dbg_ieval.erl @@ -120,9 +120,9 @@ check_exit_msg({'EXIT', Int, Reason}, _Bs, #ieval{level=Le}) -> %% This *must* be interpreter which has terminated, %% we are not linked to anyone else if - Le==1 -> + Le =:= 1 -> exit(Reason); - Le>1 -> + Le > 1 -> exit({Int, Reason}) end; check_exit_msg({'DOWN',_,_,_,Reason}, Bs, @@ -139,9 +139,9 @@ check_exit_msg({'DOWN',_,_,_,Reason}, Bs, %% importance in this case %% If we don't save them, however, post-mortem analysis %% of the process isn't possible - undefined when Le==1 -> % died outside interpreted code + undefined when Le =:= 1 -> % died outside interpreted code {}; - undefined when Le>1 -> + undefined when Le > 1 -> StackBin = term_to_binary(get(stack)), {{Mod, Li}, Bs, StackBin}; @@ -152,9 +152,9 @@ check_exit_msg({'DOWN',_,_,_,Reason}, Bs, dbg_iserver:cast(get(int), {set_exit_info,self(),ExitInfo}), if - Le==1 -> + Le =:= 1 -> exit(Reason); - Le>1 -> + Le > 1 -> exit({get(self), Reason}) end; check_exit_msg(_Msg, _Bs, _Ieval) -> @@ -271,7 +271,7 @@ meta_loop(Debugged, Bs, #ieval{level=Le} = Ieval) -> end; %% Re-entry to Meta from non-interpreted code - {re_entry, Debugged, {eval,{M,F,As}}} when Le==1 -> + {re_entry, Debugged, {eval,{M,F,As}}} when Le =:= 1 -> %% Reset process dictionary %% This is really only necessary if the process left %% interpreted code at a call level > 1 @@ -346,7 +346,7 @@ push(MFA, Bs, #ieval{level=Le,module=Cm,line=Li,last_call=Lc}) -> [] -> put(stack, [Entry]); [_Entry|Entries] -> put(stack, [Entry|Entries]) end; - _ -> % all | no_tail when Lc==false + _ -> % all | no_tail when Lc =:= false put(stack, [Entry|get(stack)]) end. @@ -413,10 +413,10 @@ sublist(L, Start, Length) -> lists:sublist(L, Start, Length). fix_stacktrace2([{_,{{M,F,As1},_,_}}, {_,{{M,F,As2},_,_}}|_]) - when length(As1)==length(As2) -> + when length(As1) =:= length(As2) -> [{M,F,As1}]; fix_stacktrace2([{_,{{Fun,As1},_,_}}, {_,{{Fun,As2},_,_}}|_]) - when length(As1)==length(As2) -> + when length(As1) =:= length(As2) -> [{Fun,As1}]; fix_stacktrace2([{_,{MFA,_,_}}|Entries]) -> [MFA|fix_stacktrace2(Entries)]; @@ -465,9 +465,9 @@ stack_frame(SP, Dir, [{SP, _}|Stack]) -> case Stack of [{Le, {_MFA,Where,Bs}}|_] -> {Le, Where, Bs}; - [] when Dir==up -> + [] when Dir =:= up -> top; - [] when Dir==down -> + [] when Dir =:= down -> bottom end; stack_frame(SP, Dir, [_Entry|Stack]) -> @@ -509,7 +509,7 @@ trace(What, Args, true) -> end, io_lib:format(" (~w) receive " ++ Tail, [Le]); - received when Args==null -> + received when Args =:= null -> io_lib:format("~n", []); received -> % Args=Msg io_lib:format("~n<== ~p~n", [Args]); @@ -586,8 +586,8 @@ eval_mfa(Debugged, M, F, As, Ieval) -> end. eval_function(Mod, Fun, As0, Bs0, _Called, Ieval) when is_function(Fun); - Mod==?MODULE, - Fun==eval_fun -> + Mod =:= ?MODULE, + Fun =:= eval_fun -> #ieval{level=Le, line=Li, last_call=Lc} = Ieval, case lambda(Fun, As0) of {Cs,Module,Name,As,Bs} -> @@ -657,7 +657,7 @@ eval_function(Mod, Name, As0, Bs0, Called, Ieval) -> lambda(eval_fun, [Cs,As,Bs,{Mod,Name}=F]) -> %% Fun defined in interpreted code, called from outside if - length(element(3,hd(Cs))) == length(As) -> + length(element(3,hd(Cs))) =:= length(As) -> db_ref(Mod), %% Adds ref between module and process {Cs,Mod,Name,As,Bs}; true -> @@ -672,7 +672,7 @@ lambda(Fun, As) when is_function(Fun) -> {env, [{Mod,Name},Bs,Cs]} = erlang:fun_info(Fun, env), {arity, Arity} = erlang:fun_info(Fun, arity), if - length(As) == Arity -> + length(As) =:= Arity -> db_ref(Mod), %% Adds ref between module and process {Cs,Mod,Name,As,Bs}; true -> @@ -731,7 +731,7 @@ db_ref(Mod) -> ModDb -> Node = node(get(int)), DbRef = if - Node/=node() -> {Node,ModDb}; + Node =/= node() -> {Node,ModDb}; true -> ModDb end, put([Mod|db], DbRef), @@ -741,13 +741,12 @@ db_ref(Mod) -> DbRef end. - cache(Key, Data) -> put(cache, lists:sublist([{Key,Data}|get(cache)], 5)). cached(Key) -> - case lists:keysearch(Key, 1, get(cache)) of - {value,{Key,Data}} -> Data; + case lists:keyfind(Key, 1, get(cache)) of + {Key,Data} -> Data; false -> false end. @@ -844,7 +843,7 @@ expr({'try',Line,Es,CaseCs,CatchCs,[]}, Bs0, Ieval0) -> case_clauses(Val, CaseCs, Bs, try_clause, Ieval) end catch - Class:Reason when CatchCs=/=[] -> + Class:Reason when CatchCs =/= [] -> catch_clauses({Class,Reason,[]}, CatchCs, Bs0, Ieval) end; expr({'try',Line,Es,CaseCs,CatchCs,As}, Bs0, Ieval0) -> @@ -1498,10 +1497,7 @@ guard(Gs, Bs) -> or_guard(Gs, Bs). or_guard([G|Gs], Bs) -> %% Short-circuit OR. - case and_guard(G, Bs) of - true -> true; - false -> or_guard(Gs, Bs) - end; + and_guard(G, Bs) orelse or_guard(Gs, Bs); or_guard([], _) -> false. and_guard([G|Gs], Bs) -> @@ -1598,8 +1594,7 @@ match1({bin,_,Fs}, B, Bs0, BBs0) when is_bitstring(B) -> try eval_bits:match_bits(Fs, B, Bs1, BBs, fun(L, R, Bs) -> match1(L, R, Bs, BBs) end, fun(E, Bs) -> expr(E, Bs, #ieval{}) end, - false) of - Match -> Match + false) catch _:_ -> throw(nomatch) end; @@ -1687,7 +1682,7 @@ merge_bindings([{Name,V}|B1s], B2s, Ieval) -> case binding(Name, B2s) of {value,V} -> % Already there, and the same merge_bindings(B1s, B2s, Ieval); - {value,_} when Name=='_' -> % Already there, but anonymous + {value,_} when Name =:= '_' -> % Already there, but anonymous B2s1 = lists:keydelete('_', 1, B2s), [{Name,V}|merge_bindings(B1s, B2s1, Ieval)]; {value,_} -> % Already there, but different => badmatch diff --git a/lib/debugger/src/dbg_iload.erl b/lib/debugger/src/dbg_iload.erl index ec54c646c8..2ae0c333da 100644 --- a/lib/debugger/src/dbg_iload.erl +++ b/lib/debugger/src/dbg_iload.erl @@ -26,7 +26,7 @@ %%-------------------------------------------------------------------- %% load_mod(Mod, File, Binary, Db) -> {ok, Mod} -%% Mod = atom() +%% Mod = module() %% File = string() Source file (including path) %% Binary = binary() %% Db = ETS identifier @@ -408,8 +408,7 @@ expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,fault}},[_]=As}) -> {dbg,Line,fault,expr_list(As)}; expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,exit}},[_]=As}) -> {dbg,Line,exit,expr_list(As)}; -expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,apply}},As0}) - when length(As0) == 3 -> +expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,apply}},[_,_,_]=As0}) -> As = expr_list(As0), {apply,Line,As}; expr({call,Line,{remote,_,{atom,_,Mod},{atom,_,Func}},As0}) -> @@ -517,15 +516,9 @@ expr(Other) -> %% here as sys_pre_expand has transformed source. is_guard_test({op,_,Op,L,R}) -> - case erl_internal:comp_op(Op, 2) of - true -> is_gexpr_list([L,R]); - false -> false - end; + erl_internal:comp_op(Op, 2) andalso is_gexpr_list([L,R]); is_guard_test({call,_,{remote,_,{atom,_,erlang},{atom,_,Test}},As}) -> - case erl_internal:type_test(Test, length(As)) of - true -> is_gexpr_list(As); - false -> false - end; + erl_internal:type_test(Test, length(As)) andalso is_gexpr_list(As); is_guard_test({atom,_,true}) -> true; is_guard_test(_) -> false. @@ -542,22 +535,12 @@ is_gexpr({call,_,{remote,_,{atom,_,erlang},{atom,_,F}},As}) -> Ar = length(As), case erl_internal:guard_bif(F, Ar) of true -> is_gexpr_list(As); - false -> - case erl_internal:arith_op(F, Ar) of - true -> is_gexpr_list(As); - false -> false - end + false -> erl_internal:arith_op(F, Ar) andalso is_gexpr_list(As) end; is_gexpr({op,_,Op,A}) -> - case erl_internal:arith_op(Op, 1) of - true -> is_gexpr(A); - false -> false - end; + erl_internal:arith_op(Op, 1) andalso is_gexpr(A); is_gexpr({op,_,Op,A1,A2}) -> - case erl_internal:arith_op(Op, 2) of - true -> is_gexpr_list([A1,A2]); - false -> false - end; + erl_internal:arith_op(Op, 2) andalso is_gexpr_list([A1,A2]); is_gexpr(_) -> false. is_gexpr_list(Es) -> lists:all(fun (E) -> is_gexpr(E) end, Es). diff --git a/lib/debugger/src/dbg_iserver.erl b/lib/debugger/src/dbg_iserver.erl index 4c1e9ccb7b..59188d83a2 100644 --- a/lib/debugger/src/dbg_iserver.erl +++ b/lib/debugger/src/dbg_iserver.erl @@ -155,11 +155,8 @@ handle_call(get_stack_trace, _From, State) -> %% Retrieving information handle_call(snapshot, _From, State) -> - Reply = lists:map(fun(Proc) -> - {Proc#proc.pid, Proc#proc.function, - Proc#proc.status, Proc#proc.info} - end, - State#state.procs), + Reply = [{Proc#proc.pid, Proc#proc.function, + Proc#proc.status, Proc#proc.info} || Proc <- State#state.procs], {reply, Reply, State}; handle_call({get_meta, Pid}, _From, State) -> Reply = case get_proc({pid, Pid}, State#state.procs) of @@ -181,21 +178,21 @@ handle_call({get_attpid, Pid}, _From, State) -> %% Breakpoint handling handle_call({new_break, Point, Options}, _From, State) -> - case lists:keysearch(Point, 1, State#state.breaks) of + case lists:keymember(Point, 1, State#state.breaks) of false -> Break = {Point, Options}, send_all([subscriber, meta, attached], {new_break, Break}, State), Breaks = keyinsert(Break, 1, State#state.breaks), {reply, ok, State#state{breaks=Breaks}}; - {value, _Break} -> + true -> {reply, {error, break_exists}, State} end; handle_call(all_breaks, _From, State) -> {reply, State#state.breaks, State}; handle_call({all_breaks, Mod}, _From, State) -> Reply = lists:filter(fun({{M,_L}, _Options}) -> - if M==Mod -> true; true -> false end + M =/= Mod end, State#state.breaks), {reply, Reply, State}; @@ -276,7 +273,7 @@ handle_call({contents, Mod, Pid}, _From, State) -> Db = State#state.db, [{{Mod, refs}, ModDbs}] = ets:lookup(Db, {Mod, refs}), ModDb = if - Pid==any -> hd(ModDbs); + Pid =:= any -> hd(ModDbs); true -> lists:foldl(fun(T, not_found) -> [{T, Pids}] = ets:lookup(Db, T), @@ -295,7 +292,7 @@ handle_call({raw_contents, Mod, Pid}, _From, State) -> Db = State#state.db, [{{Mod, refs}, ModDbs}] = ets:lookup(Db, {Mod, refs}), ModDb = if - Pid==any -> hd(ModDbs); + Pid =:= any -> hd(ModDbs); true -> lists:foldl(fun(T, not_found) -> [{T, Pids}] = ets:lookup(Db, T), @@ -360,15 +357,15 @@ handle_cast({set_stack_trace, Flag}, State) -> %% Retrieving information handle_cast(clear, State) -> Procs = lists:filter(fun(#proc{status=Status}) -> - if Status==exit -> false; true -> true end + Status =/= exit end, State#state.procs), {noreply, State#state{procs=Procs}}; %% Breakpoint handling handle_cast({delete_break, Point}, State) -> - case lists:keysearch(Point, 1, State#state.breaks) of - {value, _Break} -> + case lists:keymember(Point, 1, State#state.breaks) of + true -> send_all([subscriber, meta, attached], {delete_break, Point}, State), Breaks = lists:keydelete(Point, 1, State#state.breaks), @@ -377,8 +374,8 @@ handle_cast({delete_break, Point}, State) -> {noreply, State} end; handle_cast({break_option, Point, Option, Value}, State) -> - case lists:keysearch(Point, 1, State#state.breaks) of - {value, {Point, Options}} -> + case lists:keyfind(Point, 1, State#state.breaks) of + {Point, Options} -> N = case Option of status -> 1; action -> 2; @@ -399,7 +396,7 @@ handle_cast(no_break, State) -> handle_cast({no_break, Mod}, State) -> send_all([subscriber, meta, attached], {no_break, Mod}, State), Breaks = lists:filter(fun({{M, _L}, _O}) -> - if M==Mod -> false; true -> true end + M =/= Mod end, State#state.breaks), {noreply, State#state{breaks=Breaks}}; @@ -409,7 +406,7 @@ handle_cast({set_status, Meta, Status, Info}, State) -> {true, Proc} = get_proc({meta, Meta}, State#state.procs), send_all(subscriber, {new_status, Proc#proc.pid, Status, Info}, State), if - Status==break -> + Status =:= break -> auto_attach(break, State#state.auto, Proc); true -> ignore end, @@ -526,11 +523,10 @@ code_change(_OldVsn, State, _Extra) -> %% Internal functions %%==================================================================== -auto_attach(Why, Auto, Proc) when is_record(Proc, proc) -> - case Proc#proc.attpid of - AttPid when is_pid(AttPid) -> ignore; - undefined -> - auto_attach(Why, Auto, Proc#proc.pid) +auto_attach(Why, Auto, #proc{attpid = Attpid, pid = Pid}) -> + case Attpid of + undefined -> auto_attach(Why, Auto, Pid); + _ when is_pid(Attpid) -> ignore end; auto_attach(Why, Auto, Pid) when is_pid(Pid) -> case Auto of @@ -545,7 +541,7 @@ auto_attach(Why, Auto, Pid) when is_pid(Pid) -> keyinsert(Tuple1, N, [Tuple2|Tuples]) -> if - element(N, Tuple1)<element(N, Tuple2) -> + element(N, Tuple1) < element(N, Tuple2) -> [Tuple1, Tuple2|Tuples]; true -> [Tuple2 | keyinsert(Tuple1, N, Tuples)] @@ -576,7 +572,7 @@ send_all([], _Msg, _State) -> ok; send_all(subscriber, Msg, State) -> send_all(State#state.subs, Msg); send_all(meta, Msg, State) -> - Metas = lists:map(fun(Proc) -> Proc#proc.meta end, State#state.procs), + Metas = [Proc#proc.meta || Proc <- State#state.procs], send_all(Metas, Msg); send_all(attached, Msg, State) -> AttPids= mapfilter(fun(Proc) -> @@ -600,7 +596,7 @@ get_proc({Type, Pid}, Procs) -> meta -> #proc.meta; attpid -> #proc.attpid end, - case lists:keysearch(Pid, Index, Procs) of - {value, Proc} -> {true, Proc}; - false -> false + case lists:keyfind(Pid, Index, Procs) of + false -> false; + Proc -> {true, Proc} end. diff --git a/lib/debugger/src/dbg_ui_break_win.erl b/lib/debugger/src/dbg_ui_break_win.erl index a56fe36828..0c1e25e703 100644 --- a/lib/debugger/src/dbg_ui_break_win.erl +++ b/lib/debugger/src/dbg_ui_break_win.erl @@ -268,12 +268,7 @@ handle_event({gs, _Id, click, _Data, ["Ok"|_]}, WinInfo) -> IndexL -> Funcs = WinInfo#winInfo.funcs, Breaks = - lists:map(fun(Index) -> - Func = lists:nth(Index+1, - Funcs), - [Mod | Func] - end, - IndexL), + [[Mod|lists:nth(Index+1, Funcs)] || Index <- IndexL], {break, Breaks, enable} end end; diff --git a/lib/debugger/src/dbg_ui_filedialog_win.erl b/lib/debugger/src/dbg_ui_filedialog_win.erl index f7d76076a5..79ccf20946 100644 --- a/lib/debugger/src/dbg_ui_filedialog_win.erl +++ b/lib/debugger/src/dbg_ui_filedialog_win.erl @@ -202,8 +202,7 @@ handle_event({gs, 'Files', doubleclick, _Data, _Arg}, WinInfo) -> handle_event({gs, _Id, click, select, _Arg}, _WinInfo) -> {select, gs:read('Selection', text)}; handle_event({gs, _Id, click, multiselect, _Arg}, WinInfo) -> - Files = lists:map(fun(File) -> untag(File) end, - gs:read('Files', items)), + Files = [untag(File) || File <- gs:read('Files', items)], {multiselect, WinInfo#winInfo.cwd, Files}; handle_event({gs, _Id, click, filter, _Arg}, WinInfo) -> {Cwd, Pattern} = update_win(gs:read('Filter', text), @@ -286,7 +285,7 @@ max_existing([Name | Names]) -> max_existing(Dir, [Name | Names]) -> Dir2 = filename:join(Dir, Name), case filelib:is_file(Dir2, erl_prim_loader) of - true when Names==[] -> {Dir2, []}; + true when Names =:= [] -> {Dir2, []}; true -> max_existing(Dir2, Names); false -> {Dir, [Name | Names]} end. @@ -309,11 +308,8 @@ extra_filter([], _Dir, _Fun) -> []. get_subdirs(Dir) -> case erl_prim_loader:list_dir(Dir) of {ok, FileNames} -> - X = lists:filter(fun(FileName) -> - File = filename:join(Dir, FileName), - filelib:is_dir(File, erl_prim_loader) - end, - FileNames), + X = [FN || FN <- FileNames, + filelib:is_dir(filename:join(Dir, FN), erl_prim_loader)], lists:sort(X); _Error -> [] @@ -335,4 +331,3 @@ compare([], [$/|File]) -> File; compare(_, _) -> error. - diff --git a/lib/debugger/src/dbg_ui_mon.erl b/lib/debugger/src/dbg_ui_mon.erl index 8888075124..82fe210968 100644 --- a/lib/debugger/src/dbg_ui_mon.erl +++ b/lib/debugger/src/dbg_ui_mon.erl @@ -162,7 +162,7 @@ init2(CallingPid, Mode, SFile, GS) -> CallingPid ! {initialization_complete, self()}, if - SFile==default -> + SFile =:= default -> loop(State3); true -> loop(load_settings(SFile, State3)) @@ -226,7 +226,7 @@ loop(State) -> gui_cmd(stopped, State); %% From the GUI - GuiEvent when is_tuple(GuiEvent), element(1, GuiEvent)==gs -> + GuiEvent when is_tuple(GuiEvent), element(1, GuiEvent) =:= gs -> Cmd = dbg_ui_mon_win:handle_event(GuiEvent,State#state.win), State2 = gui_cmd(Cmd, State), loop(State2); @@ -269,7 +269,7 @@ gui_cmd(ignore, State) -> State; gui_cmd(stopped, State) -> if - State#state.starter==true -> int:stop(); + State#state.starter =:= true -> int:stop(); true -> int:auto_attach(false) end, exit(stop); @@ -413,9 +413,9 @@ gui_cmd({'Trace Window', TraceWin}, State) -> State2; gui_cmd({'Auto Attach', When}, State) -> if - When==[] -> int:auto_attach(false); + When =:= [] -> int:auto_attach(false); true -> - Flags = lists:map(fun(Name) -> map(Name) end, When), + Flags = [map(Name) || Name <- When], int:auto_attach(Flags, trace_function(State)) end, State; @@ -676,7 +676,7 @@ load_settings2(Settings, State) -> Break, int:break(Mod, Line), if - Status==inactive -> + Status =:= inactive -> int:disable_break(Mod, Line); true -> ignore end, @@ -700,12 +700,8 @@ save_settings(SFile, State) -> int:auto_attach(), int:stack_trace(), State#state.backtrace, - lists:map(fun(Mod) -> - int:file(Mod) - end, - int:interpreted()), + [int:file(Mod) || Mod <- int:interpreted()], int:all_breaks()}, - Binary = term_to_binary({debugger_settings, Settings}), case file:write_file(SFile, Binary) of ok -> @@ -720,13 +716,12 @@ save_settings(SFile, State) -> %%==================================================================== registered_name(Pid) -> - %% Yield in order to give Pid more time to register its name timer:sleep(200), Node = node(Pid), if - Node==node() -> + Node =:= node() -> case erlang:process_info(Pid, registered_name) of {registered_name, Name} -> Name; _ -> undefined diff --git a/lib/debugger/src/dbg_ui_mon_win.erl b/lib/debugger/src/dbg_ui_mon_win.erl index ba2f94c550..66e59a822a 100644 --- a/lib/debugger/src/dbg_ui_mon_win.erl +++ b/lib/debugger/src/dbg_ui_mon_win.erl @@ -224,8 +224,7 @@ select(MenuItem, Bool) -> %%-------------------------------------------------------------------- add_module(WinInfo, Menu, Mod) -> Modules = WinInfo#winInfo.modules, - case lists:keysearch(Mod, #moduleInfo.module, Modules) of - {value, _ModInfo} -> WinInfo; + case lists:keymember(Mod, #moduleInfo.module, Modules) of false -> %% Create a menu for the module Font = dbg_ui_win:font(normal), @@ -244,7 +243,8 @@ add_module(WinInfo, Menu, Mod) -> gs:config(WinInfo#winInfo.listbox, {add, Mod}), ModInfo = #moduleInfo{module=Mod, menubtn=MenuBtn}, - WinInfo#winInfo{modules=[ModInfo | Modules]} + WinInfo#winInfo{modules=[ModInfo | Modules]}; + true -> WinInfo end. %%-------------------------------------------------------------------- @@ -491,14 +491,13 @@ handle_event({gs, _Id, click, autoattach, _Arg}, WinInfo) -> %% Process grid handle_event({gs, _Id, keypress, _Data, [Key|_]}, WinInfo) when - Key=='Up'; Key=='Down' -> - Dir = if Key=='Up' -> up; Key=='Down' -> down end, + Key =:= 'Up'; Key =:= 'Down' -> + Dir = if Key =:= 'Up' -> up; Key =:= 'Down' -> down end, Row = move(WinInfo, Dir), - if Row>1 -> WinInfo2 = highlight(WinInfo, Row), - {value, #procInfo{pid=Pid}} = - lists:keysearch(Row, #procInfo.row, WinInfo#winInfo.processes), + #procInfo{pid=Pid} = + lists:keyfind(Row, #procInfo.row, WinInfo#winInfo.processes), {focus, Pid, WinInfo2}; true -> ignore @@ -515,10 +514,9 @@ handle_event(_GSEvent, _WinInfo) -> move(WinInfo, Dir) -> Row = WinInfo#winInfo.focus, Last = WinInfo#winInfo.row, - if - Dir==up, Row>1 -> Row-1; - Dir==down, Row<Last -> Row+1; + Dir =:= up, Row > 1 -> Row-1; + Dir =:= down, Row < Last -> Row+1; true -> Row end. @@ -533,7 +531,6 @@ highlight(WinInfo, Row) -> GridLine2 = gs:read(Grid, {obj_at_row, Row}), gs:config(GridLine2, {fg, white}), WinInfo#winInfo{focus=Row}. - %%==================================================================== %% Internal functions @@ -545,7 +542,7 @@ configure(WinInfo, {W, H}) -> Dx = NewW - gs:read(Grid, width), Dy = H-42 - gs:read(Grid, height), if - (Dx+Dy)=/=0 -> + (Dx+Dy) =/= 0 -> gs:config(Grid, [{width, NewW}, {height, H-30}]), Cols = calc_columnwidths(NewW), gs:config(Grid, Cols); @@ -555,10 +552,9 @@ configure(WinInfo, {W, H}) -> calc_columnwidths(Width) -> W = if - Width=<?Wg -> ?Wg; + Width =< ?Wg -> ?Wg; true -> Width end, - First = lists:map(fun (X) -> round(X) end, - [0.13*W, 0.27*W, 0.18*W, 0.18*W]), + First = [round(X) || X <- [0.13*W, 0.27*W, 0.18*W, 0.18*W]], Last = W - lists:sum(First) - 30, {columnwidths, First++[Last]}. diff --git a/lib/debugger/src/dbg_ui_trace_win.erl b/lib/debugger/src/dbg_ui_trace_win.erl index c6f041a63d..82d4199630 100644 --- a/lib/debugger/src/dbg_ui_trace_win.erl +++ b/lib/debugger/src/dbg_ui_trace_win.erl @@ -147,17 +147,17 @@ configure(WinInfo, TraceWin) -> H = gs:read(Win, height), H2 = if - Bu1==close, Bu2==open -> + Bu1 =:= close, Bu2 =:= open -> resize_button_area(open, width, W-4), gs:config('ButtonArea', {height, 30}), H+30; - Bu1==open, Bu2==close -> + Bu1 =:= open, Bu2 =:= close -> gs:config('ButtonArea', [{width, 0}, {height, 0}]), H-30; true -> H end, H3 = if - Ev1==close, Ev2==open, Bi1==open -> + Ev1 =:= close, Ev2 =:= open, Bi1 =:= open -> Wnew1 = round((W-10-4)/2), % W = window/2 - rb - pads Hbi1 = gs:read('BindArea', height), % H = bind area h resize_eval_area(open, width, Wnew1), @@ -167,25 +167,25 @@ configure(WinInfo, TraceWin) -> resize_bind_area(open, width, Wnew1-gs:read('BindArea', width)), H2; - Ev1==close, Ev2==open, Bi1==close -> + Ev1 =:= close, Ev2 =:= open, Bi1 =:= close -> resize_eval_area(open, width, W-4), resize_eval_area(open, height, 200), H2+200; - Ev1==open, Ev2==close, Bi1==open -> + Ev1 =:= open, Ev2 =:= close, Bi1 =:= open -> gs:config('EvalArea', [{width,0}, {height,0}]), gs:config('RB3', [{width, 0}, {height, 0}]), Wnew2 = W-4, resize_bind_area(open, width, Wnew2-gs:read('BindArea', width)), H2; - Ev1==open, Ev2==close, Bi1==close -> + Ev1 =:= open, Ev2 =:= close, Bi1 =:= close -> Hs1 = gs:read('EvalArea', height), gs:config('EvalArea', [{width, 0}, {height, 0}]), H2-Hs1; true -> H2 end, H4 = if - Bi1==close, Bi2==open, Ev2==open -> + Bi1 =:= close, Bi2 =:= open, Ev2 =:= open -> Wnew3 = round((W-10-4)/2), % W = window/2 - rb - pads Hs2 = gs:read('EvalArea', height), % H = eval area h resize_bind_area(open, width, Wnew3), @@ -194,29 +194,29 @@ configure(WinInfo, TraceWin) -> resize_eval_area(open, width, Wnew3-gs:read('EvalArea', width)), H3; - Bi1==close, Bi2==open, Ev2==close -> + Bi1 =:= close, Bi2 =:= open, Ev2 =:= close -> resize_bind_area(open, width, W-4), resize_bind_area(open, height, 200), H3+200; - Bi1==open, Bi2==close, Ev2==open -> + Bi1 =:= open, Bi2 =:= close, Ev2 =:= open -> gs:config('BindArea', [{width, 0}, {height, 0}]), gs:config('RB3', [{width, 0}, {height, 0}]), Wnew4 = W-4, resize_eval_area(open, width, Wnew4-gs:read('EvalArea', width)), H3; - Bi1==open, Bi2==close, Ev2==close -> + Bi1 =:= open, Bi2 =:= close, Ev2 =:= close -> Hbi2 = gs:read('BindArea', height), gs:config('BindArea', [{width, 0}, {height, 0}]), H3-Hbi2; true -> H3 end, H5 = if - Tr1==close, Tr2==open -> + Tr1 =:= close, Tr2 =:= open -> resize_trace_area(open, width, W-4), resize_trace_area(open, height, 200), H4+200; - Tr1==open, Tr2==close -> + Tr1 =:= open, Tr2 =:= close -> Hf = gs:read('TraceArea', height), gs:config('TraceArea', [{width, 0}, {height, 0}]), H4-Hf; @@ -226,10 +226,10 @@ configure(WinInfo, TraceWin) -> RB1old = rb1(OldFlags), RB1new = rb1(NewFlags), if - RB1old==close, RB1new==open -> + RB1old =:= close, RB1new =:= open -> gs:config('RB1', [{width, W-4}, {height, 10}]), gs:config(Win, {height, gs:read(Win, height)+10}); - RB1old==open, RB1new==close -> + RB1old =:= open, RB1new =:= close -> gs:config('RB1', [{width, 0}, {height, 0}, lower]), gs:config(Win, {height, gs:read(Win, height)-10}); true -> ignore @@ -237,10 +237,10 @@ configure(WinInfo, TraceWin) -> RB2old = rb2(OldFlags), RB2new = rb2(NewFlags), if - RB2old==close, RB2new==open -> + RB2old =:= close, RB2new =:= open -> gs:config('RB2', [{width, W-4}, {height, 10}]), gs:config(Win, {height,gs:read(Win, height)+10}); - RB2old==open, RB2new==close -> + RB2old =:= open, RB2new =:= close -> gs:config('RB2', [{width, 0}, {height, 0}, lower]), gs:config(Win, {height, gs:read(Win, height)-10}); true -> ignore @@ -301,15 +301,15 @@ select(MenuItem, Bool) -> %% Cond = null | {Mod, Func} %%-------------------------------------------------------------------- add_break(WinInfo, Menu, {{Mod,Line},[Status|_Options]}=Break) -> - case lists:keysearch(Mod, 1, WinInfo#winInfo.editors) of - {value, {Mod, Editor}} -> + case lists:keyfind(Mod, 1, WinInfo#winInfo.editors) of + {Mod, Editor} -> add_break_to_code(Editor, Line, Status); false -> ignore end, add_break_to_menu(WinInfo, Menu, Break). add_break_to_code(Editor, Line, Status) -> - Color = if Status==active -> red; Status==inactive -> blue end, + Color = if Status =:= active -> red; Status =:= inactive -> blue end, config_editor(Editor, [{overwrite,{{Line,0},"-@- "}}, {fg,{{{Line,0},{Line,lineend}}, Color}}]). @@ -330,8 +330,8 @@ add_break_to_menu(WinInfo, Menu, {Point, [Status|_Options]=Options}) -> %% Cond = null | {Mod, Func} %%-------------------------------------------------------------------- update_break(WinInfo, {{Mod,Line},[Status|_Options]}=Break) -> - case lists:keysearch(Mod, 1, WinInfo#winInfo.editors) of - {value, {Mod, Editor}} -> + case lists:keyfind(Mod, 1, WinInfo#winInfo.editors) of + {Mod, Editor} -> add_break_to_code(Editor, Line, Status); false -> ignore end, @@ -352,8 +352,8 @@ update_break_in_menu(WinInfo, {Point, [Status|_Options]=Options}) -> %% Point = {Mod, Line} %%-------------------------------------------------------------------- delete_break(WinInfo, {Mod,Line}=Point) -> - case lists:keysearch(Mod, 1, WinInfo#winInfo.editors) of - {value, {Mod, Editor}} -> delete_break_from_code(Editor, Line); + case lists:keyfind(Mod, 1, WinInfo#winInfo.editors) of + {Mod, Editor} -> delete_break_from_code(Editor, Line); false -> ignore end, delete_break_from_menu(WinInfo, Point). @@ -379,11 +379,11 @@ clear_breaks(WinInfo) -> clear_breaks(WinInfo, all). clear_breaks(WinInfo, Mod) -> Remove = if - Mod==all -> WinInfo#winInfo.breaks; + Mod =:= all -> WinInfo#winInfo.breaks; true -> lists:filter(fun(#breakInfo{point={Mod2,_L}}) -> if - Mod2==Mod -> true; + Mod2 =:= Mod -> true; true -> false end end, @@ -450,8 +450,8 @@ display(Arg) -> %% Note: remove_code/2 should not be used for currently shown module. %%-------------------------------------------------------------------- is_shown(WinInfo, Mod) -> - case lists:keysearch(Mod, 1, WinInfo#winInfo.editors) of - {value, {Mod, Editor}} -> + case lists:keyfind(Mod, 1, WinInfo#winInfo.editors) of + {Mod, Editor} -> gs:config(Editor, raise), {true, WinInfo#winInfo{editor={Mod, Editor}}}; false -> false @@ -459,24 +459,22 @@ is_shown(WinInfo, Mod) -> show_code(WinInfo, Mod, Contents) -> Editors = WinInfo#winInfo.editors, - {Flag, Editor} = case lists:keysearch(Mod, 1, Editors) of - {value, {Mod, Ed}} -> {existing, Ed}; + {Flag, Editor} = case lists:keyfind(Mod, 1, Editors) of + {Mod, Ed} -> {existing, Ed}; false -> {new, code_editor()} end, - %% Insert code and update breakpoints, if any config_editor(Editor, [raise, clear]), show_code(Editor, Contents), lists:foreach(fun(BreakInfo) -> case BreakInfo#breakInfo.point of - {Mod2, Line} when Mod2==Mod -> + {Mod2, Line} when Mod2 =:= Mod -> Status = BreakInfo#breakInfo.status, add_break_to_code(Editor, Line,Status); _Point -> ignore end end, WinInfo#winInfo.breaks), - case Flag of existing -> WinInfo#winInfo{editor={Mod, Editor}}; @@ -485,7 +483,7 @@ show_code(WinInfo, Mod, Contents) -> editors=[{Mod, Editor} | Editors]} end. -show_code(Editor, Text) when length(Text)>1500 -> +show_code(Editor, Text) when length(Text) > 1500 -> %% Add some text at a time so that other processes may get scheduled Str = string:sub_string(Text, 1, 1500), config_editor(Editor, {insert,{'end', Str}}), @@ -494,21 +492,19 @@ show_code(Editor, Text) -> config_editor(Editor, {insert,{'end',Text}}). show_no_code(WinInfo) -> - {value, {'$top', Editor}} = - lists:keysearch('$top', 1, WinInfo#winInfo.editors), + {'$top', Editor} = lists:keyfind('$top', 1, WinInfo#winInfo.editors), gs:config(Editor, raise), WinInfo#winInfo{editor={'$top', Editor}}. remove_code(WinInfo, Mod) -> Editors = WinInfo#winInfo.editors, - case lists:keysearch(Mod, 1, Editors) of - {value, {Mod, Editor}} -> + case lists:keyfind(Mod, 1, Editors) of + {Mod, Editor} -> gs:destroy(Editor), WinInfo#winInfo{editors=lists:keydelete(Mod, 1, Editors)}; false -> WinInfo end. - %%-------------------------------------------------------------------- %% mark_line(WinInfo, Line, How) -> WinInfo @@ -522,7 +518,7 @@ mark_line(WinInfo, Line, How) -> mark_line2(Editor, WinInfo#winInfo.marked_line, false), mark_line2(Editor, Line, How), if - Line/=0 -> config_editor(Editor, {vscrollpos, Line-5}); + Line =/= 0 -> config_editor(Editor, {vscrollpos, Line-5}); true -> ignore end, WinInfo#winInfo{marked_line=Line}. @@ -537,7 +533,7 @@ mark_line2(Editor, Line, How) -> false -> " " end, Font = if - How==false -> dbg_ui_win:font(normal); + How =:= false -> dbg_ui_win:font(normal); true -> dbg_ui_win:font(bold) end, config_editor(Editor, [{overwrite, {{Line,5}, Prefix}}, @@ -558,10 +554,10 @@ select_line(WinInfo, Line) -> %% help window, it must be checked that it is correct Size = gs:read(Editor, size), if - Line==0 -> + Line =:= 0 -> select_line(Editor, WinInfo#winInfo.selected_line, false), WinInfo#winInfo{selected_line=0}; - Line<Size -> + Line < Size -> select_line(Editor, Line, true), config_editor(Editor, {vscrollpos, Line-5}), WinInfo#winInfo{selected_line=Line}; @@ -712,10 +708,10 @@ handle_event({gs, Editor, buttonpress, code_editor, _Arg}, WinInfo) -> {Row, _} -> {Mod, _Editor} = WinInfo#winInfo.editor, Point = {Mod, Row}, - case lists:keysearch(Point, #breakInfo.point, + case lists:keymember(Point, #breakInfo.point, WinInfo#winInfo.breaks) of - {value, _BreakInfo} -> {break, Point, delete}; - false -> {break, Point, add} + false -> {break, Point, add}; + true -> {break, Point, delete} end; {Row2, _} -> select_line(Editor, Row2, true), @@ -776,7 +772,7 @@ code_editor() -> code_editor(Name, W, H) -> Editor = if - Name==null -> gs:editor('CodeArea', []); + Name =:= null -> gs:editor('CodeArea', []); true -> gs:editor(Name, 'CodeArea', []) end, gs:config(Editor, [{x,5}, {y,30}, {width,W}, {height,H}, @@ -814,8 +810,8 @@ buttons() -> {'Where','WhereButton'}, {'Up','UpButton'}, {'Down','DownButton'}]. is_button(Name) -> - case lists:keysearch(Name, 1, buttons()) of - {value, {Name, Button}} -> {true, Button}; + case lists:keyfind(Name, 1, buttons()) of + {Name, Button} -> {true, Button}; false -> false end. @@ -847,7 +843,7 @@ resize_button_area(open, width, Diff) -> eval_area({Ev,Bi}, X, Y, FrameOpts, Win) -> {W,H} = if - Ev==open -> {289,200}; + Ev =:= open -> {289,200}; true -> {0,0} end, Font = dbg_ui_win:font(normal), @@ -870,7 +866,7 @@ eval_area({Ev,Bi}, X, Y, FrameOpts, Win) -> {font_style,{{{1,0},'end'},Font}}]), gs:config('EvalEditor', {enable, false}), if - Ev==open, Bi==close -> resize_eval_area(Ev, width, 257); + Ev =:= open, Bi =:= close -> resize_eval_area(Ev, width, 257); true -> ignore end. @@ -891,7 +887,7 @@ resize_eval_area(open, Key, Diff) -> bind_area({Ev,Bi}, X, Y, FrameOpts, Win) -> {W,H} = if - Bi==open -> {249,200}; + Bi =:= open -> {249,200}; true -> {0,0} end, gs:frame('BindArea', Win, @@ -908,7 +904,7 @@ bind_area({Ev,Bi}, X, Y, FrameOpts, Win) -> {text,{1,"Name"}}, {text,{2,"Value"}}, {font,Font}]), gs:config('BindGrid', {rows,{1,1}}), if - Bi==open, Ev==close -> resize_bind_area(Bi, width, 297); + Bi =:= open, Ev =:= close -> resize_bind_area(Bi, width, 297); true -> ignore end. @@ -993,15 +989,15 @@ resizebar(Flag, Name, X, Y, W, H, Obj) -> rb1({_Bu,Ev,Bi,Tr}) -> if - Ev==close, Bi==close, Tr==close -> close; + Ev =:= close, Bi =:= close, Tr =:= close -> close; true -> open end. rb2({_Bu,Ev,Bi,Tr}) -> if - Tr==open -> + Tr =:= open -> if - Ev==close, Bi==close -> close; + Ev =:= close, Bi =:= close -> close; true -> open end; true -> close @@ -1009,7 +1005,7 @@ rb2({_Bu,Ev,Bi,Tr}) -> rb3({_Bu,Ev,Bi,_Tr}) -> if - Ev==open, Bi==open -> open; + Ev =:= open, Bi =:= open -> open; true -> close end. @@ -1067,7 +1063,7 @@ configure(WinInfo, NewW, NewH) -> %% Adjust width unless it is unchanged or less than minimum width if - OldW/=NewW -> + OldW =/= NewW -> {Dcode,Deval,Dbind} = configure_widths(OldW,NewW,Flags), resize_code_area(WinInfo, width, Dcode), case rb1(Flags) of @@ -1090,7 +1086,7 @@ configure(WinInfo, NewW, NewH) -> %% Adjust height unless it is unchanged or less than minimum height if - OldH/=NewH -> + OldH =/= NewH -> {Dcode2,Deval2,Dtrace} = configure_heights(OldH,NewH,Flags), resize_code_area(WinInfo, height, Dcode2), resize_eval_area(Ev, height, Deval2), @@ -1117,11 +1113,11 @@ configure_widths(OldW, NewW, Flags) -> %% Check how much the frames can be resized in reality Limits = if %% Window larger - NewW>OldW -> + NewW > OldW -> if - Ev==open,Bi==open -> {0,Diff,Diff}; - Ev==open -> {0,Diff,0}; - Bi==open -> {0,0,Diff}; + Ev =:= open, Bi =:= open -> {0,Diff,Diff}; + Ev =:= open -> {0,Diff,0}; + Bi =:= open -> {0,0,Diff}; true -> {Diff,0,0} end; @@ -1129,12 +1125,12 @@ configure_widths(OldW, NewW, Flags) -> %% and current size OldW>NewW -> if - Ev==open,Bi==open -> + Ev =:= open, Bi =:= open -> {0, gs:read('EvalArea',width)-204, gs:read('BindArea',width)-112}; - Ev==open -> {0,Diff,0}; - Bi==open -> {0,0,Diff}; + Ev =:= open -> {0,Diff,0}; + Bi =:= open -> {0,0,Diff}; true -> {Diff,0,0} end end, @@ -1142,13 +1138,13 @@ configure_widths(OldW, NewW, Flags) -> case Limits of %% No Shell or Bind frame, larger window - {T,0,0} when NewW>OldW -> {T,0,0}; + {T,0,0} when NewW > OldW -> {T,0,0}; %% No Shell or Bind frame, smaller window - {T,0,0} when OldW>NewW -> {-T,0,0}; + {T,0,0} when OldW > NewW -> {-T,0,0}; %% Window larger; divide Diff among the frames and return result - {_,Sf,B} when NewW>OldW -> + {_,Sf,B} when NewW > OldW -> {_,Sf2,B2} = divide([{0,0},{0,Sf},{0,B}],Diff), {Sf2+B2,Sf2,B2}; @@ -1171,33 +1167,33 @@ configure_heights(OldH, NewH, Flags) -> %% Check how much the frames can be resized in reality {T,Sf,Ff} = if %% Window larger - NewH>OldH -> + NewH > OldH -> {Diff, if - Ev==close, Bi==close -> 0; + Ev =:= close, Bi =:= close -> 0; true -> Diff end, if - Tr==open -> Diff; + Tr =:= open -> Diff; true -> 0 end}; %% Window smaller; get difference between min size %% and current size - OldH>NewH -> + OldH > NewH -> {gs:read('CodeArea',height)-100, if - Ev==close, Bi==close -> 0; + Ev =:= close, Bi =:= close -> 0; true -> if - Ev==open -> + Ev =:= open -> gs:read('EvalArea',height)-100; - Bi==open -> + Bi =:= open -> gs:read('BindArea',height)-100 end end, if - Tr==open -> gs:read('TraceArea',height)-100; + Tr =:= open -> gs:read('TraceArea',height)-100; true -> 0 end} end, @@ -1251,10 +1247,8 @@ divide(L, Diff) -> if %% All of Diff has been distributed - D==0 -> {T,S,F}; - + D =:= 0 -> {T,S,F}; true -> - %% For each element, try to add as much as possible of D {NewT,Dt} = divide2(D,T,Tmax), {NewS,Ds} = divide2(D,S,Smax), @@ -1296,25 +1290,25 @@ resize(WinInfo, ResizeBar) -> rblimits('RB2',W,H), rblimits('RB3',W,H)). -resizeloop(WI, RB, Prev, {Min1,Max1},{Min2,Max2},{Min3,Max3}) -> +resizeloop(WI, RB, Prev, {Min1,Max1}, {Min2,Max2}, {Min3,Max3}) -> receive - {gs,_,motion,_,[_,Y]} when RB=='RB1', Y>Min1,Y<Max1 -> + {gs,_,motion,_,[_,Y]} when RB =:= 'RB1', Y > Min1, Y < Max1 -> gs:config('RB1', {y,Y}), - resizeloop(WI, RB, Y, {Min1,Max1},{Min2,Max2},{Min3,Max3}); - {gs,_,motion,_,_} when RB=='RB1' -> - resizeloop(WI, RB, Prev,{Min1,Max1},{Min2,Max2},{Min3,Max3}); + resizeloop(WI, RB, Y, {Min1,Max1}, {Min2,Max2}, {Min3,Max3}); + {gs,_,motion,_,_} when RB =:= 'RB1' -> + resizeloop(WI, RB, Prev, {Min1,Max1}, {Min2,Max2}, {Min3,Max3}); - {gs,_,motion,_,[_,Y]} when RB=='RB2', Y>Min2,Y<Max2 -> + {gs,_,motion,_,[_,Y]} when RB =:= 'RB2', Y > Min2, Y < Max2 -> gs:config('RB2', {y,Y}), - resizeloop(WI, RB, Y, {Min1,Max1},{Min2,Max2},{Min3,Max3}); - {gs,_,motion,_,_} when RB=='RB2' -> - resizeloop(WI, RB, Prev,{Min1,Max1},{Min2,Max2},{Min3,Max3}); + resizeloop(WI, RB, Y, {Min1,Max1}, {Min2,Max2}, {Min3,Max3}); + {gs,_,motion,_,_} when RB =:= 'RB2' -> + resizeloop(WI, RB, Prev, {Min1,Max1}, {Min2,Max2}, {Min3,Max3}); - {gs,_,motion,_,[X,_]} when RB=='RB3', X>Min3,X<Max3 -> + {gs,_,motion,_,[X,_]} when RB =:= 'RB3', X > Min3, X < Max3 -> gs:config('RB3', {x,X}), - resizeloop(WI, RB, X, {Min1,Max1},{Min2,Max2},{Min3,Max3}); - {gs,_,motion,_,_} when RB=='RB3' -> - resizeloop(WI, RB, Prev,{Min1,Max1},{Min2,Max2},{Min3,Max3}); + resizeloop(WI, RB, X, {Min1,Max1}, {Min2,Max2}, {Min3,Max3}); + {gs,_,motion,_,_} when RB =:= 'RB3' -> + resizeloop(WI, RB, Prev, {Min1,Max1}, {Min2,Max2}, {Min3,Max3}); {gs,_,buttonrelease,_,_} -> resize_win(WI, RB, Prev) @@ -1329,7 +1323,7 @@ resize_win(WinInfo, 'RB1', Y) -> %% Resize Code, Evaluator and Binding areas resize_code_area(WinInfo, height, -Diff), if - S==close, Bi==close, F==open -> + S =:= close, Bi =:= close, F =:= open -> resize_trace_area(open, height, Diff); true -> resize_eval_area(S, height, Diff), @@ -1388,7 +1382,7 @@ rblimits('RB1',_W,H) -> RB2 = gs:read('RB2',height), FF = gs:read('TraceArea',height), Max = case RB2 of - 0 when FF/=0 -> + 0 when FF =/= 0 -> H-112; _ -> Y = gs:read('RB2',y), @@ -1397,18 +1391,14 @@ rblimits('RB1',_W,H) -> {Min,Max}; rblimits('RB2',_W,H) -> - - %% TraceFrame should not have height <100 + %% TraceFrame should not have height < 100 Max = H-112, - %% Min is decided by a minimum distance to 'RB1' Y = gs:read('RB1',y), Min = erlang:min(Max,Y+140), - {Min,Max}; rblimits('RB3',W,_H) -> - %% Neither CodeArea nor BindArea should occupy %% less than 1/3 of the total window width and EvalFrame should %% be at least 289 pixels wide @@ -1484,7 +1474,7 @@ helpwin_action(gotoline, default, AttPid, _Editor, Data, Win) -> end, Data; helpwin_action(search, case_sensitive, _AttPid, _Ed, {Pos, CS}, _Win) -> - Bool = if CS==true -> false; CS==false -> true end, + Bool = if CS =:= true -> false; CS =:= false -> true end, {Pos, Bool}; helpwin_action(search, default, _AttPid, Editor, {Pos, CS}, Win) -> gs:config(lbl(Win), {label, {text, ""}}), @@ -1517,13 +1507,9 @@ search(Str, Editor, Max, {Row, Col}, CS) -> lowercase(true, Str) -> Str; lowercase(false, Str) -> - lists:map(fun(Char) -> - if - Char>=$A, Char=<$Z -> Char+32; - true -> Char - end - end, - Str). + [if Char >= $A, Char =< $Z -> Char+32; + true -> Char + end || Char <- Str]. mark_string(Editor, {Row, Col}, Str) -> Between = {{Row,Col}, {Row,Col+length(Str)}}, @@ -1540,10 +1526,9 @@ unmark_string(Editor, {Row, Col}) -> {fg, {Between, black}}]). helpwin(Type, GS, {X, Y}) -> - W = 200, Pad=10, Wbtn = 50, + W = 200, Pad = 10, Wbtn = 50, - Title = - case Type of search -> "Search"; gotoline -> "Go To Line" end, + Title = case Type of search -> "Search"; gotoline -> "Go To Line" end, Win = gs:window(GS, [{title, Title}, {x, X}, {y, Y}, {width, W}, {destroy, true}]), diff --git a/lib/debugger/src/dbg_ui_view.erl b/lib/debugger/src/dbg_ui_view.erl index 075275f196..7350a830a8 100644 --- a/lib/debugger/src/dbg_ui_view.erl +++ b/lib/debugger/src/dbg_ui_view.erl @@ -21,9 +21,6 @@ %% External exports -export([start/2]). -%% Internal exports --export([init/3]). - -record(state, {gs, % term() Graphics system id win, % term() Attach process window data coords, % {X,Y} Mouse point position @@ -42,7 +39,7 @@ start(GS, Mod) -> Title = "View Module " ++ atom_to_list(Mod), case dbg_ui_winman:is_started(Title) of true -> ignore; - false -> spawn(?MODULE, init, [GS, Mod, Title]) + false -> spawn(fun () -> init(GS, Mod, Title) end) end. @@ -51,7 +48,6 @@ start(GS, Mod) -> %%==================================================================== init(GS, Mod, Title) -> - %% Subscribe to messages from the interpreter int:subscribe(), diff --git a/lib/debugger/src/dbg_ui_win.erl b/lib/debugger/src/dbg_ui_win.erl index 74ff2503ab..9bed6a1ec5 100644 --- a/lib/debugger/src/dbg_ui_win.erl +++ b/lib/debugger/src/dbg_ui_win.erl @@ -24,7 +24,6 @@ create_menus/2, select/2, selected/1, add_break/2, update_break/2, delete_break/1, motion/2 - ]). -record(break, {mb, smi, emi, dimi, demi}). @@ -49,11 +48,11 @@ init() -> font(Style) -> GS = init(), Style2 = if - Style==normal -> []; + Style =:= normal -> []; true -> [Style] end, case gs:read(GS, {choose_font, {screen,Style2,12}}) of - Font when element(1, Font)==screen -> + Font when element(1, Font) =:= screen -> Font; _ -> gs:read(GS, {choose_font, {courier,Style2,12}}) @@ -168,8 +167,7 @@ select(MenuItem, Bool) -> %%-------------------------------------------------------------------- selected(Menu) -> Children = gs:read(Menu, children), - Selected = lists:filter(fun(Child) -> gs:read(Child, select) end, - Children), + Selected = [gs:read(Child, select) || Child <- Children], lists:map(fun(Child) -> {text, Name} = gs:read(Child, label), list_to_atom(Name) diff --git a/lib/debugger/src/dbg_ui_winman.erl b/lib/debugger/src/dbg_ui_winman.erl index 71023cd0d6..398735a7ca 100644 --- a/lib/debugger/src/dbg_ui_winman.erl +++ b/lib/debugger/src/dbg_ui_winman.erl @@ -120,9 +120,9 @@ init(_Arg) -> {ok, #state{}}. handle_call({is_started, Title}, _From, State) -> - Reply = case lists:keysearch(Title, #win.title, State#state.wins) of - {value, Win} -> {true, Win#win.win}; - false -> false + Reply = case lists:keyfind(Title, #win.title, State#state.wins) of + false -> false; + Win -> {true, Win#win.win} end, {reply, Reply, State}. @@ -134,8 +134,8 @@ handle_cast({insert, Pid, Title, Win}, State) -> handle_cast({clear_process, Title}, State) -> OldWins = State#state.wins, - Wins = case lists:keysearch(Title, #win.title, OldWins) of - {value, #win{owner=Pid}} -> + Wins = case lists:keyfind(Title, #win.title, OldWins) of + #win{owner=Pid} -> Msg = {dbg_ui_winman, destroy}, Pid ! Msg, lists:keydelete(Title, #win.title, OldWins); @@ -147,7 +147,7 @@ handle_cast({clear_process, Title}, State) -> handle_info({'EXIT', Pid, _Reason}, State) -> [Mon | _Wins] = State#state.wins, if - Pid==Mon#win.owner -> {stop, normal, State}; + Pid =:= Mon#win.owner -> {stop, normal, State}; true -> Wins2 = lists:keydelete(Pid, #win.owner, State#state.wins), inform_all(Wins2), diff --git a/lib/debugger/src/dbg_wx_break_win.erl b/lib/debugger/src/dbg_wx_break_win.erl index 5dafb0fbe6..78733c98c8 100644 --- a/lib/debugger/src/dbg_wx_break_win.erl +++ b/lib/debugger/src/dbg_wx_break_win.erl @@ -206,11 +206,7 @@ handle_event(#wx{id=OKorListBox, event=#wxCommand{type=OkorDoubleClick}}, OkorDoubleClick =:= command_listbox_doubleclicked -> Mod = wxComboBox:getValue(Text), {_, IndexL} = wxListBox:getSelections(LB), - Breaks = lists:map(fun(Index) -> - Func = lists:nth(Index+1, Funcs), - [list_to_atom(Mod) | Func] - end, - IndexL), + Breaks = [[list_to_atom(Mod)|lists:nth(Index+1, Funcs)] || Index <- IndexL], wxDialog:destroy(Win), {break, Breaks, enable}; handle_event(#wx{id=?wxID_OK},#winInfo{win=Win,text=Text, entries=Es, trigger=Trigger}) -> diff --git a/lib/debugger/src/dbg_wx_interpret.erl b/lib/debugger/src/dbg_wx_interpret.erl index f711ba679d..ffcfbcf36b 100644 --- a/lib/debugger/src/dbg_wx_interpret.erl +++ b/lib/debugger/src/dbg_wx_interpret.erl @@ -115,11 +115,11 @@ interpret_all(Dir, [File0|Files], Mode, Window, Errors) -> interpret_all(_Dir, [], _Mode, _Window, []) -> true; interpret_all(Dir, [], _Mode, Window, Errors) -> - Msg = lists:map(fun(Name) -> - File = filename:join(Dir, Name), - Error = format_error(int:interpretable(File)), - ["\n ",Name,": ",Error] - end, Errors), + Msg = [begin + File = filename:join(Dir, Name), + Error = format_error(int:interpretable(File)), + ["\n ",Name,": ",Error] + end || Name <- Errors], All = ["Error when interpreting: ", Msg], dbg_wx_win:confirm(Window, lists:flatten(All)), true. diff --git a/lib/debugger/src/dbg_wx_mon.erl b/lib/debugger/src/dbg_wx_mon.erl index 3f55c38d35..6bdec994b1 100644 --- a/lib/debugger/src/dbg_wx_mon.erl +++ b/lib/debugger/src/dbg_wx_mon.erl @@ -170,7 +170,7 @@ init2(CallingPid, Mode, SFile, GS) -> CallingPid ! {initialization_complete, self()}, if - SFile==default -> + SFile =:= default -> loop(State3); true -> loop(load_settings(SFile, State3)) @@ -268,7 +268,7 @@ gui_cmd(ignore, State) -> State; gui_cmd(stopped, State) -> if - State#state.starter==true -> int:stop(); + State#state.starter =:= true -> int:stop(); true -> int:auto_attach(false) end, exit(stop); @@ -420,9 +420,9 @@ gui_cmd({'Trace Window', TraceWin}, State) -> State2; gui_cmd({'Auto Attach', When}, State) -> if - When==[] -> int:auto_attach(false); + When =:= [] -> int:auto_attach(false); true -> - Flags = lists:map(fun(Name) -> map(Name) end, When), + Flags = [map(Name) || Name <- When], int:auto_attach(Flags, trace_function(State)) end, State; @@ -691,12 +691,12 @@ load_settings2(Settings, State) -> Break, int:break(Mod, Line), if - Status==inactive -> + Status =:= inactive -> int:disable_break(Mod, Line); true -> ignore end, if - Action/=enable -> + Action =/= enable -> int:action_at_break(Mod,Line,Action); true -> ignore end, @@ -715,10 +715,7 @@ save_settings(SFile, State) -> int:auto_attach(), int:stack_trace(), State#state.backtrace, - lists:map(fun(Mod) -> - int:file(Mod) - end, - int:interpreted()), + [int:file(Mod) || Mod <- int:interpreted()], int:all_breaks()}, Binary = term_to_binary({debugger_settings, Settings}), @@ -741,7 +738,7 @@ registered_name(Pid) -> Node = node(Pid), if - Node==node() -> + Node =:= node() -> case erlang:process_info(Pid, registered_name) of {registered_name, Name} -> Name; _ -> undefined diff --git a/lib/debugger/src/dbg_wx_mon_win.erl b/lib/debugger/src/dbg_wx_mon_win.erl index 8ad4f4213f..04c3501b8c 100644 --- a/lib/debugger/src/dbg_wx_mon_win.erl +++ b/lib/debugger/src/dbg_wx_mon_win.erl @@ -266,8 +266,7 @@ select(MenuItem, Bool) -> add_module(WinInfo, MenuName, Mod) -> Win = WinInfo#winInfo.window, Modules = WinInfo#winInfo.modules, - case lists:keysearch(Mod, #moduleInfo.module, Modules) of - {value, _ModInfo} -> WinInfo; + case lists:keymember(Mod, #moduleInfo.module, Modules) of false -> %% Create a menu for the module Menu = get(MenuName), @@ -284,8 +283,9 @@ add_module(WinInfo, MenuName, Mod) -> wxListBox:append(WinInfo#winInfo.listbox, atom_to_list(Mod)), ModInfo = #moduleInfo{module=Mod, menubtn={Menu,MenuBtn}}, - WinInfo#winInfo{modules=[ModInfo | Modules]} - end. + WinInfo#winInfo{modules=[ModInfo | Modules]}; + true -> WinInfo + end. %%-------------------------------------------------------------------- %% delete_module(WinInfo, Mod) -> WinInfo @@ -559,8 +559,7 @@ handle_event(#wx{event=#wxCommand{type=command_checkbox_clicked}}, handle_event(#wx{event=#wxList{type=command_list_item_selected, itemIndex=Row}}, WinInfo) -> #winInfo{processes=Pids} = WinInfo, - {value, #procInfo{pid=Pid}} = - lists:keysearch(Row, #procInfo.row, Pids), + #procInfo{pid=Pid} = lists:keyfind(Row, #procInfo.row, Pids), {focus, Pid, WinInfo#winInfo{focus=Row}}; handle_event(#wx{event=#wxList{type=command_list_item_activated}}, _WinInfo) -> diff --git a/lib/debugger/src/dbg_wx_trace.erl b/lib/debugger/src/dbg_wx_trace.erl index f9fdf593c4..6675ea33e7 100644 --- a/lib/debugger/src/dbg_wx_trace.erl +++ b/lib/debugger/src/dbg_wx_trace.erl @@ -180,12 +180,12 @@ init_contents(Breaks, State) -> State#state{win=Win}. -loop(#state{meta=Meta} = State) -> +loop(#state{meta=Meta, win=Win} = State) -> receive %% From the GUI main window - GuiEvent when element(1, GuiEvent)==wx -> + GuiEvent when element(1, GuiEvent) =:= wx -> Cmd = wx:batch(fun() -> - dbg_wx_trace_win:handle_event(GuiEvent,State#state.win) + dbg_wx_trace_win:handle_event(GuiEvent,Win) end), State2 = gui_cmd(Cmd, State), loop(State2); @@ -211,11 +211,11 @@ loop(#state{meta=Meta} = State) -> %% From the dbg_wx_winman process (Debugger window manager) {dbg_ui_winman, update_windows_menu, Data} -> - Window = dbg_wx_trace_win:get_window(State#state.win), + Window = dbg_wx_trace_win:get_window(Win), dbg_wx_winman:update_windows_menu(Window,Data), loop(State); {dbg_ui_winman, destroy} -> - dbg_wx_trace_win:stop(State#state.win), + dbg_wx_trace_win:stop(Win), exit(stop) end. @@ -269,7 +269,7 @@ gui_cmd('Continue', State) -> int:meta(State#state.meta, continue), {Status, Mod, Line} = State#state.status, if - Status==wait_break -> + Status =:= wait_break -> Win = dbg_wx_trace_win:unmark_line(State#state.win), gui_enable_functions(wait_running), State#state{win=Win, status={wait_running,Mod,Line}}; @@ -291,7 +291,7 @@ gui_cmd('Stop', State) -> int:meta(State#state.meta, stop), {Status, Mod, Line} = State#state.status, if - Status==wait_running -> + Status =:= wait_running -> Win = dbg_wx_trace_win:mark_line(State#state.win, Line, break), gui_enable_functions(wait_break), @@ -421,7 +421,7 @@ gui_cmd('Function Break...', State) -> gui_cmd('Enable All', State) -> Breaks = int:all_breaks(), ThisMod = State#state.cm, - lists:foreach(fun ({{Mod, Line}, _Options}) when Mod==ThisMod -> + lists:foreach(fun ({{Mod, Line}, _Options}) when Mod =:= ThisMod -> int:enable_break(Mod, Line); (_Break) -> ignore @@ -431,7 +431,7 @@ gui_cmd('Enable All', State) -> gui_cmd('Disable All', State) -> Breaks = int:all_breaks(), ThisMod = State#state.cm, - lists:foreach(fun ({{Mod, Line}, _Options}) when Mod==ThisMod -> + lists:foreach(fun ({{Mod, Line}, _Options}) when Mod =:= ThisMod -> int:disable_break(Mod, Line); (_Break) -> ignore @@ -458,7 +458,7 @@ gui_cmd({'Trace Window', TraceWin}, State) -> Win = dbg_wx_trace_win:configure(State#state.win, TraceWin), {Status,_,_} = State#state.status, if - Status==break; Status==wait_break -> + Status =:= break; Status =:= wait_break -> gui_enable_btrace(Trace, State#state.stack_trace); true -> ignore end, @@ -467,7 +467,7 @@ gui_cmd({'Stack Trace', [Name]}, State) -> int:meta(State#state.meta, stack_trace, map(Name)), {Status,_,_} = State#state.status, if - Status==break; Status==wait_break -> + Status =:= break; Status =:= wait_break -> gui_enable_btrace(State#state.trace, map(Name)); true -> ignore end, @@ -490,9 +490,9 @@ gui_cmd('Debugger', State) -> gui_cmd({user_command, Cmd}, State) -> {Status, _Mod, _Line} = State#state.status, if - Status==break; - Status==wait_break; - Status==wait_running -> + Status =:= break; + Status =:= wait_break; + Status =:= wait_running -> Cm = State#state.cm, Arg = case State#state.stack of {Cur, Max} when Cur<Max -> {Cm, Cmd, Cur}; @@ -531,14 +531,14 @@ add_break(WI, Coords, Type, Mod, Line) -> int_cmd({interpret, Mod}, State) -> if - Mod==State#state.cm -> + Mod =:= State#state.cm -> State#state{cm_obsolete=true}; true -> State end; int_cmd({no_interpret, Mod}, State) -> if - Mod==State#state.cm -> + Mod =:= State#state.cm -> State#state{cm_obsolete=true}; true -> Win = dbg_wx_trace_win:remove_code(State#state.win, Mod), @@ -584,7 +584,7 @@ meta_cmd({re_entry, dbg_ieval, eval_fun}, State) -> meta_cmd({re_entry, Mod, _Func}, State) -> Obs = State#state.cm_obsolete, case State#state.cm of - Mod when Obs==true -> + Mod when Obs =:= true -> Win = gui_load_module(State#state.win, Mod,State#state.pid), State#state{win=Win, cm_obsolete=false}; Mod -> State; @@ -630,11 +630,11 @@ meta_cmd({func_at, Mod, Line, Cur}, State) -> gui_enable_functions(idle), dbg_wx_trace_win:display(State#state.win, idle), State#state{win=Win, cm=Mod, status={idle,Mod,Line}, stack=Stack}; -meta_cmd({wait_at, Mod, Line, Cur}, #state{status={Status,_,_}}=State) - when Status/=init, Status/=break -> +meta_cmd({wait_at, Mod, Line, Cur}, #state{status={Status,_,_}, win=Win}=State) + when Status =/= init, Status =/= break -> Stack = {Cur,Cur}, gui_enable_functions(wait_running), - dbg_wx_trace_win:display(State#state.win, {wait,Mod,Line}), + dbg_wx_trace_win:display(Win, {wait,Mod,Line}), State#state{status={wait_running,Mod,Line}, stack=Stack}; meta_cmd({wait_at, Mod, Line, Cur}, State) -> Stack = {Cur,Cur}, @@ -675,7 +675,7 @@ meta_cmd({stack_trace, Flag}, State) -> gui_enable_updown(Flag, State#state.stack), {Status,_,_} = State#state.status, if - Status==break; Status==wait_break -> + Status =:= break; Status =:= wait_break -> gui_enable_btrace(State#state.trace, Flag); true -> ignore end, @@ -813,7 +813,7 @@ gui_enable_functions(Status) -> gui_enable_updown(Flag, Stack) -> {Enable, Disable} = if - Flag==false -> {[], ['Up', 'Down']}; + Flag =:= false -> {[], ['Up', 'Down']}; true -> case Stack of {1,1} -> {[], ['Up', 'Down']}; @@ -826,14 +826,14 @@ gui_enable_updown(Flag, Stack) -> dbg_wx_trace_win:enable(Enable, true), dbg_wx_trace_win:enable(Disable, false), if - Enable==[] -> dbg_wx_trace_win:enable(['Where'], false); + Enable =:= [] -> dbg_wx_trace_win:enable(['Where'], false); true -> dbg_wx_trace_win:enable(['Where'], true) end. gui_enable_btrace(Trace, StackTrace) -> Bool = if - Trace==false -> false; - StackTrace==false -> false; + Trace =:= false -> false; + StackTrace =:= false -> false; true -> true end, dbg_wx_trace_win:enable(['Back Trace'], Bool). diff --git a/lib/debugger/src/dbg_wx_trace_win.erl b/lib/debugger/src/dbg_wx_trace_win.erl index 2b4a1164ad..720b913024 100755 --- a/lib/debugger/src/dbg_wx_trace_win.erl +++ b/lib/debugger/src/dbg_wx_trace_win.erl @@ -410,11 +410,11 @@ clear_breaks(WinInfo) -> clear_breaks(WinInfo, all). clear_breaks(WinInfo, Mod) -> Remove = if - Mod==all -> WinInfo#winInfo.breaks; + Mod =:= all -> WinInfo#winInfo.breaks; true -> lists:filter(fun(#breakInfo{point={Mod2,_L}}) -> if - Mod2==Mod -> true; + Mod2 =:= Mod -> true; true -> false end end, @@ -481,8 +481,8 @@ display(#winInfo{window=Win, sb=Sb},Arg) -> %% Note: remove_code/2 should not be used for currently shown module. %%-------------------------------------------------------------------- is_shown(WinInfo, Mod) -> - case lists:keysearch(Mod, 1, WinInfo#winInfo.editors) of - {value, {Mod, Editor}} -> + case lists:keyfind(Mod, 1, WinInfo#winInfo.editors) of + {Mod, Editor} -> gs:config(Editor, raise), %% BUGBUG {true, WinInfo#winInfo{editor={Mod, Editor}}}; false -> false @@ -494,7 +494,7 @@ show_code(WinInfo = #winInfo{editor={_, Ed}}, Mod, Contents) -> lists:foreach(fun(BreakInfo) -> case BreakInfo#breakInfo.point of - {Mod2, Line} when Mod2==Mod -> + {Mod2, Line} when Mod2 =:= Mod -> Status = BreakInfo#breakInfo.status, dbg_wx_code:add_break_to_code(Ed, Line,Status); _Point -> ignore @@ -540,10 +540,10 @@ select_line(WinInfo, Line) -> %% help window, it must be checked that it is correct Size = dbg_wx_code:get_no_lines(Ed), if - Line==0 -> + Line =:= 0 -> dbg_wx_code:goto_line(Ed,1), WinInfo#winInfo{selected_line=0}; - Line<Size -> + Line < Size -> dbg_wx_code:goto_line(Ed,Line), WinInfo#winInfo{selected_line=Line}; true -> @@ -764,8 +764,8 @@ handle_event(#wx{event=#wxStyledText{type=stc_doubleclick}}, WinInfo = #winInfo{editor={Mod,Ed}}) -> Line = wxStyledTextCtrl:getCurrentLine(Ed), Point = {Mod, Line+1}, - case lists:keysearch(Point, #breakInfo.point,WinInfo#winInfo.breaks) of - {value, _BreakInfo} -> {break, Point, delete}; + case lists:keymember(Point, #breakInfo.point, WinInfo#winInfo.breaks) of + true -> {break, Point, delete}; false -> {break, Point, add} end; @@ -837,7 +837,7 @@ handle_event(#wx{id=?SEARCH_ENTRY, event=#wxCommand{cmdString=Str}}, %% Button area handle_event(#wx{id=ID, event=#wxCommand{type=command_button_clicked}},_Wi) -> - {value, {Button, _}} = lists:keysearch(ID, 2, buttons()), + {Button, _} = lists:keyfind(ID, 2, buttons()), Button; %% Evaluator area @@ -908,8 +908,8 @@ buttons() -> {'Where',?WhereButton}, {'Up',?UpButton}, {'Down',?DownButton}]. is_button(Name) -> - case lists:keysearch(Name, 1, buttons()) of - {value, {Name, Button}} -> {true, Button}; + case lists:keyfind(Name, 1, buttons()) of + {Name, Button} -> {true, Button}; false -> false end. diff --git a/lib/debugger/src/dbg_wx_view.erl b/lib/debugger/src/dbg_wx_view.erl index 6d34e5650c..8ff89a4847 100644 --- a/lib/debugger/src/dbg_wx_view.erl +++ b/lib/debugger/src/dbg_wx_view.erl @@ -23,9 +23,6 @@ %% External exports -export([start/2]). -%% Internal exports --export([init/4]). - -record(state, {gs, % term() Graphics system id win, % term() Attach process window data coords, % {X,Y} Mouse point position @@ -46,7 +43,7 @@ start(GS, Mod) -> true -> ignore; false -> Env = wx:get_env(), - spawn_link(?MODULE, init, [GS, Env, Mod, Title]) + spawn_link(fun () -> init(GS, Env, Mod, Title) end) end. @@ -84,7 +81,7 @@ loop(State) -> receive %% From the GUI main window - GuiEvent when element(1, GuiEvent)==wx -> + GuiEvent when element(1, GuiEvent) =:= wx -> Cmd = wx:batch(fun() -> dbg_wx_trace_win:handle_event(GuiEvent, State#state.win) end), @@ -167,7 +164,7 @@ gui_cmd('Function Break...', State) -> gui_cmd('Enable All', State) -> Breaks = int:all_breaks(), ThisMod = State#state.mod, - lists:foreach(fun ({{Mod, Line}, _Options}) when Mod==ThisMod -> + lists:foreach(fun ({{Mod, Line}, _Options}) when Mod =:= ThisMod -> int:enable_break(Mod, Line); (_Break) -> ignore @@ -177,7 +174,7 @@ gui_cmd('Enable All', State) -> gui_cmd('Disable All', State) -> Breaks = int:all_breaks(), ThisMod = State#state.mod, - lists:foreach(fun ({{Mod, Line}, _Options}) when Mod==ThisMod -> + lists:foreach(fun ({{Mod, Line}, _Options}) when Mod =:= ThisMod -> int:disable_break(Mod, Line); (_Break) -> ignore @@ -214,21 +211,19 @@ add_break(GS, Coords, Type, Mod, Line) -> %%--Commands from the interpreter------------------------------------- -int_cmd({new_break, {{Mod,_Line},_Options}=Break}, #state{mod=Mod}=State) -> - Win = dbg_wx_trace_win:add_break(State#state.win, 'Break', Break), - State#state{win=Win}; -int_cmd({delete_break, {Mod,_Line}=Point}, #state{mod=Mod}=State) -> - Win = dbg_wx_trace_win:delete_break(State#state.win, Point), - State#state{win=Win}; -int_cmd({break_options, {{Mod,_Line},_Options}=Break}, #state{mod=Mod}=State) -> - Win = dbg_wx_trace_win:update_break(State#state.win, Break), - State#state{win=Win}; -int_cmd(no_break, State) -> - Win = dbg_wx_trace_win:clear_breaks(State#state.win), - State#state{win=Win}; -int_cmd({no_break, _Mod}, State) -> - Win = dbg_wx_trace_win:clear_breaks(State#state.win), - State#state{win=Win}; +int_cmd({new_break, {{Mod,_Line},_Options}=Break}, + #state{mod = Mod, win = Win}=State) -> + State#state{win = dbg_wx_trace_win:add_break(Win, 'Break', Break)}; +int_cmd({delete_break, {Mod,_Line}=Point}, + #state{mod = Mod, win = Win}=State) -> + State#state{win = dbg_wx_trace_win:delete_break(Win, Point)}; +int_cmd({break_options, {{Mod,_Line},_Options}=Break}, + #state{mod = Mod, win = Win}=State) -> + State#state{win = dbg_wx_trace_win:update_break(Win, Break)}; +int_cmd(no_break, #state{win = Win}=State) -> + State#state{win = dbg_wx_trace_win:clear_breaks(Win)}; +int_cmd({no_break, _Mod}, #state{win = Win}=State) -> + State#state{win = dbg_wx_trace_win:clear_breaks(Win)}; int_cmd(_, State) -> State. diff --git a/lib/debugger/src/dbg_wx_winman.erl b/lib/debugger/src/dbg_wx_winman.erl index 1daabe3435..d0ddfeb51a 100755 --- a/lib/debugger/src/dbg_wx_winman.erl +++ b/lib/debugger/src/dbg_wx_winman.erl @@ -118,9 +118,9 @@ init([]) -> {ok, #state{}}. handle_call({is_started, Title}, _From, State) -> - Reply = case lists:keysearch(Title, #win.title, State#state.wins) of - {value, Win} -> {true, Win#win.win}; - false -> false + Reply = case lists:keyfind(Title, #win.title, State#state.wins) of + false -> false; + Win -> {true, Win#win.win} end, {reply, Reply, State}. @@ -132,8 +132,8 @@ handle_cast({insert, Pid, Title, Win}, State) -> handle_cast({clear_process, Title}, State) -> OldWins = State#state.wins, - Wins = case lists:keysearch(Title, #win.title, OldWins) of - {value, #win{owner=Pid}} -> + Wins = case lists:keyfind(Title, #win.title, OldWins) of + #win{owner=Pid} -> Msg = {dbg_ui_winman, destroy}, Pid ! Msg, lists:keydelete(Title, #win.title, OldWins); @@ -145,7 +145,7 @@ handle_cast({clear_process, Title}, State) -> handle_info({'EXIT', Pid, _Reason}, State) -> [Mon | _Wins] = State#state.wins, if - Pid==Mon#win.owner -> {stop, normal, State}; + Pid =:= Mon#win.owner -> {stop, normal, State}; true -> Wins2 = lists:keydelete(Pid, #win.owner, State#state.wins), inform_all(Wins2), diff --git a/lib/debugger/src/i.erl b/lib/debugger/src/i.erl index 7c2fb22946..476a53482e 100644 --- a/lib/debugger/src/i.erl +++ b/lib/debugger/src/i.erl @@ -31,7 +31,6 @@ iv() -> Vsn = string:substr(filename:basename(code:lib_dir(debugger)), 10), list_to_atom(Vsn). - %% ------------------------------------------- %% Start a new graphical monitor. @@ -288,10 +287,9 @@ ia(X,Y,Z) -> %% ------------------------------------------- ia(Pid,Fnk) -> - case lists:keysearch(Pid, 1, int:snapshot()) of - {value, _PidTuple} -> - int:attach(Pid,Fnk); - false -> no_proc + case lists:keymember(Pid, 1, int:snapshot()) of + false -> no_proc; + true -> int:attach(Pid,Fnk) end. ia(X,Y,Z,Fnk) -> diff --git a/lib/debugger/src/int.erl b/lib/debugger/src/int.erl index eeb4df4a8e..9ee2102a19 100644 --- a/lib/debugger/src/int.erl +++ b/lib/debugger/src/int.erl @@ -261,7 +261,7 @@ del_break_in(Mod, Func, Arity) when is_atom(Mod), is_atom(Func), is_integer(Arit end. first_lines(Clauses) -> - lists:map(fun(Clause) -> first_line(Clause) end, Clauses). + [first_line(Clause) || Clause <- Clauses]. first_line({clause,_L,_Vars,_,Exprs}) -> first_line(Exprs); @@ -469,10 +469,10 @@ contents(Mod, Pid) -> %% Arity = integer() %%-------------------------------------------------------------------- functions(Mod) -> - lists:filter(fun([module_info, _Arity]) -> false; - (_Func) -> true - end, - dbg_iserver:call({functions, Mod})). + [F || F <- dbg_iserver:call({functions, Mod}), functions_1(F)]. + +functions_1([module_info, _Arity]) -> false; +functions_1(_Func) -> true. %%==================================================================== diff --git a/lib/docbuilder/src/docb_edoc_xml_cb.erl b/lib/docbuilder/src/docb_edoc_xml_cb.erl index 4dba843341..f5cfc0fe18 100644 --- a/lib/docbuilder/src/docb_edoc_xml_cb.erl +++ b/lib/docbuilder/src/docb_edoc_xml_cb.erl @@ -478,31 +478,29 @@ otp_xmlify_a_href("#"++_ = Marker, Es0) -> % <seealso marker="#what"> otp_xmlify_a_href("http:"++_ = URL, Es0) -> % external URL {URL, Es0}; otp_xmlify_a_href("OTPROOT"++AppRef, Es0) -> % <.. marker="App:FileRef - case split(AppRef, "/") of - [AppS, "doc", FileRef1] -> - FileRef = AppS++":"++otp_xmlify_a_fileref(FileRef1, AppS), - [#xmlText{value=Str0} = T] = Es0, - Str = case split(Str0, "/") of - %% //Application - [AppS2] -> - %% AppS2 can differ from AppS - %% Example: xmerl/XMerL - AppS2; - [_AppS,ModRef] -> - case split(ModRef, ":") of - %% //Application/Module - [Module] -> - Module++"(3)"; - %% //Application/Module:Type() - [_Module,_Type] -> - ModRef - end; - %% //Application/Module:Function/Arity - [_AppS,ModFunc,Arity] -> - ModFunc++"/"++Arity - end, - {FileRef, [T#xmlText{value=Str}]} - end; + [AppS, "doc", FileRef1] = split(AppRef, "/"), + FileRef = AppS++":"++otp_xmlify_a_fileref(FileRef1, AppS), + [#xmlText{value=Str0} = T] = Es0, + Str = case split(Str0, "/") of + %% //Application + [AppS2] -> + %% AppS2 can differ from AppS + %% Example: xmerl/XMerL + AppS2; + [_AppS,ModRef] -> + case split(ModRef, ":") of + %% //Application/Module + [Module] -> + Module++"(3)"; + %% //Application/Module:Type() + [_Module,_Type] -> + ModRef + end; + %% //Application/Module:Function/Arity + [_AppS,ModFunc,Arity] -> + ModFunc++"/"++Arity + end, + {FileRef, [T#xmlText{value=Str}]}; otp_xmlify_a_href("../"++File, Es0) -> %% Special case: This kind of relative path is used on some %% places within i.e. EDoc and refers to a file within the same @@ -639,13 +637,13 @@ otp_xmlify_table([#xmlText{} = E|Es]) -> [E | otp_xmlify_table(Es)]; otp_xmlify_table([#xmlElement{name=tbody} = E|Es]) -> otp_xmlify_table(E#xmlElement.content)++otp_xmlify_table(Es); -otp_xmlify_table([#xmlElement{name=tr} = E|Es]) -> +otp_xmlify_table([#xmlElement{name=tr, content=Content}|Es]) -> %% Insert newlines between table rows - otp_xmlify_table(E#xmlElement.content)++[{br,[]}]++otp_xmlify_table(Es); -otp_xmlify_table([#xmlElement{name=th} = E|Es]) -> - [{em, E#xmlElement.content} | otp_xmlify_table(Es)]; -otp_xmlify_table([#xmlElement{name=td} = E|Es]) -> - otp_xmlify_e(E#xmlElement.content) ++ otp_xmlify_table(Es); + otp_xmlify_table(Content)++[{br,[]}]++otp_xmlify_table(Es); +otp_xmlify_table([#xmlElement{name=th, content=Content}|Es]) -> + [{em, Content} | otp_xmlify_table(Es)]; +otp_xmlify_table([#xmlElement{name=td, content=Content}|Es]) -> + otp_xmlify_e(Content) ++ otp_xmlify_table(Es); otp_xmlify_table([]) -> []. @@ -1155,8 +1153,8 @@ get_text(#xmlElement{content=[E]}) -> %% text_only(Es) -> Ts %% Takes a list of xmlElement and xmlText and return a lists of xmlText. -text_only([#xmlElement{} = E |Es]) -> - text_only(E#xmlElement.content) ++ text_only(Es); +text_only([#xmlElement{content = Content}|Es]) -> + text_only(Content) ++ text_only(Es); text_only([#xmlText{} = E |Es]) -> [E | text_only(Es)]; text_only([]) -> diff --git a/lib/docbuilder/src/docb_html.erl b/lib/docbuilder/src/docb_html.erl index 9aea4c8a66..bdfc5ea876 100644 --- a/lib/docbuilder/src/docb_html.erl +++ b/lib/docbuilder/src/docb_html.erl @@ -283,16 +283,16 @@ rule([term|_], {_, [ID], _}, Opts) -> ID, "</strong></em> "]}, Opts}; TermList -> - case lists:keysearch(ID, 1, TermList) of + case lists:keyfind(ID, 1, TermList) of false -> {{drop, ["<em><strong>", ID, "</strong></em> "]}, Opts}; - {value, {ID, Name, _Description, _Resp}} -> + {ID, Name, _Description, _Resp} -> {{drop, ["<em><strong>", Name, "</strong></em> "]}, Opts}; - {value, {ID, Name, _Description}} -> + {ID, Name, _Description} -> {{drop, [ "<em><strong>", Name, "</strong></em> "]}, Opts} @@ -306,16 +306,16 @@ rule([term|_], {_, [ID], _}, Opts) -> TermList -> PartApplication = docb_util:lookup_option(part_application, Opts), - case lists:keysearch(ID, 1, TermList) of + case lists:keyfind(ID, 1, TermList) of false -> {{drop, ["<a href=\"", PartApplication, "_term.html#", ID, "\">", ID, "</a> "]}, Opts}; - {value, {ID, Name, _Description, _Resp}} -> + {ID, Name, _Description, _Resp} -> {{drop, ["<a href=\"", PartApplication, "_term.html#", ID, "\">", Name, "</a> "]}, Opts}; - {value, {ID, Name, _Description}} -> + {ID, Name, _Description} -> {{drop, ["<a href=\"", PartApplication, "_term.html#", ID, "\">", Name, "</a> "]}, Opts} @@ -331,17 +331,16 @@ rule([cite|_], {_, [ID], _}, Opts) -> {{drop, ["<em><strong>", ID, "</strong></em> "]}, Opts}; CiteList -> - case lists:keysearch(ID, 1, CiteList) of + case lists:keyfind(ID, 1, CiteList) of false -> {{drop, ["<em><strong>", ID, "</strong></em> "]}, Opts}; - - {value, {ID, Name, _Description, _Resp}} -> + {ID, Name, _Description, _Resp} -> {{drop, ["<em><strong>", Name, "</strong></em> "]}, Opts}; - {value, {ID, Name, _Description}} -> + {ID, Name, _Description} -> {{drop, ["<em><strong>", Name, "</strong></em> "]}, Opts} @@ -355,18 +354,18 @@ rule([cite|_], {_, [ID], _}, Opts) -> CiteList -> PartApp = docb_util:lookup_option(part_application, Opts), - case lists:keysearch(ID, 1, CiteList) of + case lists:keyfind(ID, 1, CiteList) of false -> {{drop, ["<a href=\"", PartApp, "_cite.html#", ID, "\">", ID, "</a> "]}, Opts}; - {value, {ID, Name, _Description, _Resp}} -> + {ID, Name, _Description, _Resp} -> {{drop, ["<a href=\"", PartApp, "_cite.html#", ID, "\">", Name, "</a> "]}, Opts}; - {value, {ID, Name, _Description}} -> + {ID, Name, _Description} -> {{drop, ["<a href=\"", PartApp, "_cite.html#", ID, "\">", Name, "</a> "]}, @@ -376,7 +375,7 @@ rule([cite|_], {_, [ID], _}, Opts) -> end; rule([code|_], {_, [Type], [{pcdata, _, Code}]}, Opts) -> - case lists:member(Type,["ERL","C","NONE"]) of + case lists:member(Type, ["ERL","C","NONE"]) of true -> {{drop, ["\n<div class=\"example\"><pre>\n", docb_html_util:element_cdata_to_html(Code), "\n</pre></div>\n"]}, Opts}; diff --git a/lib/docbuilder/src/docb_html_util.erl b/lib/docbuilder/src/docb_html_util.erl index b2951706ea..02ce8b52a7 100644 --- a/lib/docbuilder/src/docb_html_util.erl +++ b/lib/docbuilder/src/docb_html_util.erl @@ -136,7 +136,6 @@ copy_pics(Src, Dest, Opts) -> Dir = code:lib_dir(docbuilder), InFile = filename:join([Dir, "etc", Src]), OutFile = docb_util:outfile(Dest, "", Opts), - case filelib:last_modified(OutFile) of 0 -> % File doesn't exist file:copy(InFile, OutFile); @@ -156,10 +155,10 @@ copy_pics(Src, Dest, Opts) -> %%--Resolve header data------------------------------------------------- extract_header_data(Key, {header, [], List}) -> - case lists:keysearch(Key, 1, List) of - {value, {Key, [], []}} -> + case lists:keyfind(Key, 1, List) of + {Key, [], []} -> ""; - {value, {Key, [], [{pcdata, [], Value}]}} -> + {Key, [], [{pcdata, [], Value}]} -> pcdata_to_html(Value); false -> "" @@ -253,7 +252,7 @@ make_anchor_href(HRef) -> {ok, [HRef]} -> %% No `#' in HRef, i.e. only path make_anchor_href(HRef, ""); - {ok, [Path, Fragment]}-> + {ok, [Path, Fragment]} -> make_anchor_href(Path, Fragment) end. @@ -398,10 +397,10 @@ count_sections([]) -> %%--Make a ToC---------------------------------------------------------- format_toc(Toc) -> - lists:map(fun({Number, Title}) -> - [Number, " <a href = \"#", Number, - "\">", Title, "</a><br/>\n"] - end, Toc). + [format_toc1(T) || T <- Toc]. + +format_toc1({Number, Title}) -> + [Number, " <a href = \"#", Number, "\">", Title, "</a><br/>\n"]. %%--Convert HTML ISO Latin 1 characters to ordinary characters---------- diff --git a/lib/docbuilder/src/docb_main.erl b/lib/docbuilder/src/docb_main.erl index ef21f65557..87a1401a02 100644 --- a/lib/docbuilder/src/docb_main.erl +++ b/lib/docbuilder/src/docb_main.erl @@ -55,7 +55,7 @@ process(File, Opts) -> %% If no target format is specified, assume HTML: Tos = if - Tos0==[] -> [html]; + Tos0 =:= [] -> [html]; true -> Tos0 end, @@ -327,12 +327,8 @@ verify(Tree) -> verify(Tree, [], 1). verify({pcdata, Optional, _}, Path, Level) -> verify_optional(Optional, Path, Level); verify({Tag, Optional, Args}, Path, Level) when is_list(Args) -> - case verify_optional(Optional, Path, Level) of - true -> - verify_list(Args, [Tag|Path], Level); - false -> - false - end; + verify_optional(Optional, Path, Level) + andalso verify_list(Args, [Tag|Path], Level); verify(Other, Path, Level) -> verify_error(Other, Path, Level). @@ -342,12 +338,7 @@ verify_error(X, Path, Level) -> false. verify_list([H|T], Path, Level) -> - case verify(H, Path, Level) of - true -> - verify_list(T, Path, Level +1); - false -> - false - end; + verify(H, Path, Level) andalso verify_list(T, Path, Level + 1); verify_list([], _, _) -> true. @@ -419,7 +410,7 @@ edit1_list(_Tag, [], _Op) -> %% Actual transformation of tree structure to desired format. transform(From, To, Opts, File, Tree) -> Filter = if - To==html; To==kwic -> + To =:= html; To =:= kwic -> list_to_atom("docb_tr_" ++ atom_to_list(From) ++ [$2|atom_to_list(To)]); true -> @@ -427,7 +418,7 @@ transform(From, To, Opts, File, Tree) -> [$2|atom_to_list(To)]) end, - case catch apply(Filter, transform, [File, Tree, Opts]) of + case catch Filter:transform(File, Tree, Opts) of %% R5C {'EXIT', {undef, [{Filter, transform, [File, Tree, Opts]}|_]}}-> @@ -459,9 +450,9 @@ transform(From, To, Opts, File, Tree) -> finish_transform(Tree, File, Opts, Filter) -> {Str, NewOpts} = pp(Tree, [], 1, Filter, Opts), Extension = - case catch apply(Filter, extension, [NewOpts]) of + case catch Filter:extension(NewOpts) of {'EXIT', _} -> - apply(Filter, extension, []); + Filter:extension(); Others -> Others end, @@ -606,7 +597,7 @@ include_all(Fd) -> eof -> []; ListOfChars -> - lists:append(ListOfChars, include_all(Fd)) + ListOfChars ++ include_all(Fd) end. extract(File, Fd, StartTag, StopTag, State) -> diff --git a/lib/docbuilder/src/docb_pretty_format.erl b/lib/docbuilder/src/docb_pretty_format.erl index 0c4fb0507b..25dcd8987b 100644 --- a/lib/docbuilder/src/docb_pretty_format.erl +++ b/lib/docbuilder/src/docb_pretty_format.erl @@ -47,7 +47,7 @@ term(Term) -> %% the next line to need an "extra" indent!). term([], Indent) -> {Indent, [$[,$]]}; -term(L, Indent) when list(L) -> +term(L, Indent) when is_list(L) -> case is_string(L) of true -> {Indent, io_lib:write_string(L)}; @@ -59,7 +59,7 @@ term(L, Indent) when list(L) -> write_simple_list(L, Indent) end end; -term(T, Indent) when tuple(T) -> +term(T, Indent) when is_tuple(T) -> case complex_tuple(T) of true -> write_complex_tuple(T, Indent); diff --git a/lib/docbuilder/src/docb_tr_application2html.erl b/lib/docbuilder/src/docb_tr_application2html.erl index 4084cfe6ba..d8cb214d0a 100644 --- a/lib/docbuilder/src/docb_tr_application2html.erl +++ b/lib/docbuilder/src/docb_tr_application2html.erl @@ -119,8 +119,8 @@ transform(File, {application, _Attrs, [Header|Rest]}, Opts0) -> case docb_main:parse1("fascicules", Opts0) of {ok, Parse} -> FascData = get_fasc_data(Parse), - case lists:keysearch(File, 1, FascData) of - {value, {_, _, "YES", _}} -> + case lists:keyfind(File, 1, FascData) of + {_, _, "YES", _} -> OrigFile = docb_util:outfile(File++"_frame", ".html", Opts0), @@ -167,7 +167,7 @@ concat_files([File|Rest], Body, Opts) -> %% Remove the reference manual header [{Ref, [], [_Hdr| NewBody]}] = NewParse, RefParse = [{Ref, [], NewBody}], - lists:append(Body, concat_files(Rest, RefParse, Opts)); + Body ++ concat_files(Rest, RefParse, Opts); errors -> errors end; @@ -216,7 +216,7 @@ make_toc([{lib, Attrs, More}|Rest]) -> make_toc([{com, Attrs, More}|Rest]) -> [{com, Attrs, More}|make_toc(Rest)]; make_toc([{_Tag, _Attrs, More}|Rest]) -> - lists:append(make_toc(More), make_toc(Rest)). + make_toc(More) ++ make_toc(Rest). rule([module|_], {_, [File], _}) -> {"<small><a target=\"document\" href=\"" ++ @@ -280,9 +280,7 @@ get_fasc_data({fascicules, _, Fascs}) -> Fascs). get_avals(Atts) -> - lists:map(fun(Tuple) -> - element(3, Tuple) end, - Atts). + [element(3, Tuple) || Tuple <- Atts]. get_pc_text([{pcdata, _, Text}]) -> Text. diff --git a/lib/docbuilder/src/docb_tr_cite2html.erl b/lib/docbuilder/src/docb_tr_cite2html.erl index 4ecbfa4e91..77f1c4e636 100644 --- a/lib/docbuilder/src/docb_tr_cite2html.erl +++ b/lib/docbuilder/src/docb_tr_cite2html.erl @@ -39,24 +39,22 @@ purge_body([], _) -> purge_body([{pcdata,_Attrs,_More}|Rest], CiteList) -> purge_body(Rest, CiteList); purge_body([{cite,[{"ID","CDATA",ID}],More}|Rest], CiteList) -> - case lists:keysearch(ID, 1, CiteList) of + case lists:keyfind(ID, 1, CiteList) of false -> [{cite, [{"NAME","CDATA",ID}, {"ID","CDATA",ID}], More}| purge_body(Rest, CiteList)]; - {value, {ID, Name, _Description, _Responsible}} -> + {ID, Name, _Description, _Responsible} -> [{cite, [{"NAME","CDATA",Name}, {"ID","CDATA",ID}], More}| purge_body(Rest, CiteList)]; - {value, {ID, Name, _Description}} -> + {ID, Name, _Description} -> [{cite, [{"NAME","CDATA",Name}, {"ID","CDATA",ID}], More}| purge_body(Rest, CiteList)] end; purge_body([{_Tag,_Attrs,More}|Rest], CiteList) -> - lists:append(purge_body(More, CiteList), - purge_body(Rest, CiteList)). + purge_body(More, CiteList) ++ purge_body(Rest, CiteList). rule([header|_], _) -> {drop, ""}; - rule(_, _) -> {drop, ""}. @@ -91,19 +89,19 @@ rule([cite|_], {_,[Name,ID], [{citedef,[],[{pcdata,[],Def}]}]}, Opts) -> false -> []; Value -> Value end, - case lists:keysearch(ID, 1, CiteList) of + case lists:keyfind(ID, 1, CiteList) of false -> {{drop,"\n<dt><a name=\"" ++ ID ++ "\">" ++ "<strong>" ++ ID ++ "</strong></a></dt>\n<dd>" ++ docb_html_util:pcdata_to_html(Def) ++ "\n</dd>\n"}, Opts}; - {value, {ID, Name, Description, _Responsible}} -> + {ID, Name, Description, _Responsible} -> docb_util:message(warning, "Global cite ~s overriding local", [ID]), {{drop,"\n<dt><a name=\"" ++ ID ++ "\">" ++ "<strong>" ++ Name ++ "</strong></a></dt>\n<dd>" ++ docb_html_util:pcdata_to_html(Description) ++ "\n</dd>\n"}, Opts}; - {value, {ID, Name, Description}} -> + {ID, Name, Description} -> docb_util:message(warning, "Global cite ~s overriding local", [ID]), {{drop,"\n<dt><a name=\"" ++ ID ++ "\">" ++ @@ -117,19 +115,19 @@ rule([cite|_], {_,[Name,ID],_}, Opts) -> false -> []; Value -> Value end, - case lists:keysearch(ID, 1, CiteList) of + case lists:keyfind(ID, 1, CiteList) of false -> docb_util:message(error, "The cite ~s has no definition", [ID]), {{drop,"\n<dt><a name=\"" ++ ID ++ "\">" ++ "<strong>" ++ ID ++ "</strong></a></dt>\n<dd>" ++ "??" ++ "\n</dd>\n"}, Opts}; - {value, {ID, Name, Description, _Responsible}} -> + {ID, Name, Description, _Responsible} -> {{drop,"\n<dt><a name=\"" ++ ID ++ "\">" ++ "<strong>" ++ Name ++ "</strong></a></dt>\n<dd>" ++ docb_html_util:pcdata_to_html(Description) ++ "\n</dd>\n"}, Opts}; - {value, {ID, Name, Description}} -> + {ID, Name, Description} -> {{drop,"\n<dt><a name=\"" ++ ID ++ "\">" ++ "<strong>" ++ Name ++ "</strong></a></dt>\n<dd>" ++ docb_html_util:pcdata_to_html(Description) ++ "\n</dd>\n"}, Opts} diff --git a/lib/docbuilder/src/docb_tr_index2html.erl b/lib/docbuilder/src/docb_tr_index2html.erl index bbf419f3ef..312342add2 100644 --- a/lib/docbuilder/src/docb_tr_index2html.erl +++ b/lib/docbuilder/src/docb_tr_index2html.erl @@ -73,9 +73,7 @@ prune_flat([], _) -> []. keep_pcdata(Trees) -> - lists:filter(fun({pcdata, _, _}) -> true; - (_) -> false - end, Trees). + [T || T = {pcdata, _, _} <- Trees]. new_trees(FileFuncs) -> Files0 = [{File, RefType} || {File, RefType, _} <- FileFuncs], diff --git a/lib/docbuilder/src/docb_tr_part2html.erl b/lib/docbuilder/src/docb_tr_part2html.erl index dd44c4a8df..30befe8432 100644 --- a/lib/docbuilder/src/docb_tr_part2html.erl +++ b/lib/docbuilder/src/docb_tr_part2html.erl @@ -93,8 +93,8 @@ transform(File, {part, _Attrs, [Header| Rest]}, Opts0) -> case docb_main:parse1("fascicules", Opts0) of {ok, Parse} -> FascData = get_fasc_data(Parse), - case lists:keysearch(File, 1, FascData) of - {value, {_, _, "YES", _}} -> + case lists:keyfind(File, 1, FascData) of + {_, _, "YES", _} -> OrigFile = docb_util:outfile(File++"_frame", ".html", Opts0), @@ -137,7 +137,7 @@ concat_files([File | Rest], Body, ChLevel, Opts, TP, TOpts, Ext) -> case docb_main:parse1(File, Opts) of {ok, Parse} -> {TopTag, Attrs, [Header = {header, _, HeaderContents} | More]} = Parse, - {value,{title,_,Title}} = lists:keysearch(title,1,HeaderContents), + {title,_,Title} = lists:keyfind(title,1,HeaderContents), NewMore = [{section, [], [{title, [], Title}| More]}], NewParse = {TopTag, Attrs, [Header| NewMore]}, if @@ -156,9 +156,8 @@ concat_files([File | Rest], Body, ChLevel, Opts, TP, TOpts, Ext) -> docb_html_util:number(NewParse, integer_to_list(ChLevel), File), {_, [], [_| NewBody]} = NumberTree, - lists:append(Body, - concat_files(Rest, NewBody, ChLevel+1, Opts, - TP, TOpts, Ext)); + Body ++ concat_files(Rest, NewBody, ChLevel+1, Opts, + TP, TOpts, Ext); errors -> throw({error,"Parse error when building chapter "++File}) end; @@ -232,9 +231,7 @@ get_fasc_data({fascicules, _, Fascs}) -> Fascs). get_avals(Atts) -> - lists:map(fun(Tuple) -> - element(3, Tuple) end, - Atts). + [element(3, Tuple) || Tuple <- Atts]. get_pc_text([{pcdata, _, Text}]) -> Text. diff --git a/lib/docbuilder/src/docb_tr_term2html.erl b/lib/docbuilder/src/docb_tr_term2html.erl index 0a993cebb1..a3c4a5312a 100644 --- a/lib/docbuilder/src/docb_tr_term2html.erl +++ b/lib/docbuilder/src/docb_tr_term2html.erl @@ -39,24 +39,22 @@ purge_body([], _) -> purge_body([{pcdata,_Attrs,_More}|Rest], TermList) -> purge_body(Rest, TermList); purge_body([{term,[{"ID","CDATA",ID}],More}|Rest], TermList) -> - case lists:keysearch(ID, 1, TermList) of + case lists:keyfind(ID, 1, TermList) of false -> [{term,[{"NAME","CDATA",ID},{"ID","CDATA",ID}],More}| purge_body(Rest, TermList)]; - {value, {ID, Name, _Description, _Responsible}} -> + {ID, Name, _Description, _Responsible} -> [{term,[{"NAME","CDATA",Name},{"ID","CDATA",ID}],More}| purge_body(Rest, TermList)]; - {value, {ID, Name, _Description}} -> + {ID, Name, _Description} -> [{term,[{"NAME","CDATA",Name},{"ID","CDATA",ID}],More}| purge_body(Rest, TermList)] end; purge_body([{_Tag,_Attrs,More}|Rest], TermList) -> - lists:append(purge_body(More, TermList), - purge_body(Rest, TermList)). + purge_body(More, TermList) ++ purge_body(Rest, TermList). rule([header|_], _) -> {drop, ""}; - rule(_, _) -> {drop, ""}. @@ -82,19 +80,19 @@ rule([term|_], {_,[Name,ID],[{termdef,[],[{pcdata,[],Def}]}]}, Opts) -> false -> []; Value -> Value end, - case lists:keysearch(ID, 1, TermList) of + case lists:keyfind(ID, 1, TermList) of false -> {{drop,"\n<dt><a name=\"" ++ ID ++ "\">" ++ "<strong>" ++ ID ++ "</strong></a>\n</dt>\n<dd>" ++ docb_html_util:pcdata_to_html(Def) ++ "\n</dd>\n"}, Opts}; - {value, {ID, Name, Description, _Responsible}} -> + {ID, Name, Description, _Responsible} -> docb_util:message(warning, "Global term ~s overriding local", [ID]), {{drop,"\n<dt><a name=\"" ++ ID ++ "\">" ++ "<strong>" ++ Name ++ "</strong></a></dt>\n<dd>" ++ docb_html_util:pcdata_to_html(Description) ++ "\n</dd>\n"}, Opts}; - {value, {ID, Name, Description}} -> + {ID, Name, Description} -> docb_util:message(warning, "Global term ~s overriding local", [ID]), {{drop, "\n<dt><a name=\"" ++ ID ++ "\">" ++ @@ -107,19 +105,19 @@ rule([term|_], {_,[Name,ID],_}, Opts) -> false -> []; Value -> Value end, - case lists:keysearch(ID, 1, TermList) of + case lists:keyfind(ID, 1, TermList) of false -> docb_util:message(error, "The term ~s has no definition", [ID]), {{drop, "\n<dt><a name=\"" ++ ID ++ "\">" ++ "<strong>" ++ ID ++ "</strong></a></dt>\n<dd>" ++ "??" ++ "\n</dd>\n"}, Opts}; - {value, {ID, Name, Description, _Responsible}} -> + {ID, Name, Description, _Responsible} -> {{drop, "\n<dt><a name=\"" ++ ID ++ "\">" ++ "<strong>" ++ Name ++ "</strong></a></dt>\n<dd>" ++ docb_html_util:pcdata_to_html(Description) ++ "\n</dd>\n"}, Opts}; - {value, {ID, Name, Description}} -> + {ID, Name, Description} -> {{drop, "\n<dt><a name=\"" ++ ID ++ "\">" ++ "<strong>" ++ Name ++ "</strong></a></dt>\n<dd>" ++ docb_html_util:pcdata_to_html(Description) ++ "\n</dd>\n"}, Opts} diff --git a/lib/docbuilder/src/docb_transform.erl b/lib/docbuilder/src/docb_transform.erl index a432038adf..9c7561b07b 100644 --- a/lib/docbuilder/src/docb_transform.erl +++ b/lib/docbuilder/src/docb_transform.erl @@ -135,8 +135,8 @@ parse([Opt | _RawOpts], _Opts) -> get_defs(Type, File, Opts) -> Key = {defs,Type}, {PrevDefs, Opts2} = - case lists:keysearch(Key, 1, Opts) of - {value, {_, Defs0}} -> + case lists:keyfind(Key, 1, Opts) of + {_, Defs0} -> {Defs0, lists:keydelete(Key, 1, Opts)}; false -> {[], Opts} diff --git a/lib/docbuilder/src/docb_util.erl b/lib/docbuilder/src/docb_util.erl index 59673ef3a4..9b2eec7733 100644 --- a/lib/docbuilder/src/docb_util.erl +++ b/lib/docbuilder/src/docb_util.erl @@ -61,7 +61,7 @@ html_snippet(What, Opts) -> case lookup_option(html_mod, Opts) of false -> ""; Module -> - case catch apply(Module, What, []) of + case catch Module:What() of HTML when is_list(HTML) -> HTML; {'EXIT', {undef, _}} -> @@ -82,7 +82,7 @@ html_snippet(What, Arg, Opts) -> case lookup_option(html_mod, Opts) of false -> ""; Module -> - case catch apply(Module, What, [Arg]) of + case catch Module:What(Arg) of HTML when is_list(HTML) -> HTML; {'EXIT', {undef, _}} -> @@ -106,8 +106,8 @@ html_snippet(What, Arg, Opts) -> %% lookup_option(Opt, Opts) -> Value | false lookup_option(Opt, Opts) -> - case lists:keysearch(Opt, 1, Opts) of - {value, {Opt,Value}} -> Value; + case lists:keyfind(Opt, 1, Opts) of + {Opt,Value} -> Value; false -> false end. diff --git a/lib/docbuilder/src/docb_xmerl_tree_cb.erl b/lib/docbuilder/src/docb_xmerl_tree_cb.erl index d57f55bff8..bc62069230 100644 --- a/lib/docbuilder/src/docb_xmerl_tree_cb.erl +++ b/lib/docbuilder/src/docb_xmerl_tree_cb.erl @@ -188,8 +188,8 @@ attrs(DTD, Tag, GivenAttrs) -> merge_attrs(Tag, default_attrs(DTD, Tag), GivenAttrs). merge_attrs(Tag, [{NameA, Type, DefVal}|Default], GivenAttrs) -> - Val = case lists:keysearch(NameA, #xmlAttribute.name, GivenAttrs) of - {value, #xmlAttribute{value=Val0}} -> Val0; + Val = case lists:keyfind(NameA, #xmlAttribute.name, GivenAttrs) of + #xmlAttribute{value=Val0} -> Val0; false -> DefVal end, Attr = {attr_name(NameA), Type, attr_val(Type, Val)}, diff --git a/lib/edoc/src/edoc_lib.erl b/lib/edoc/src/edoc_lib.erl index 47e61f7932..5b7fb1e0d2 100644 --- a/lib/edoc/src/edoc_lib.erl +++ b/lib/edoc/src/edoc_lib.erl @@ -472,7 +472,7 @@ uri_get_file(File0) -> uri_get_http(URI) -> %% Try using option full_result=false - case catch {ok, http:request(get, {URI,[]}, [], + case catch {ok, httpc:request(get, {URI,[]}, [], [{full_result, false}])} of {'EXIT', _} -> uri_get_http_r10(URI); @@ -482,7 +482,7 @@ uri_get_http(URI) -> uri_get_http_r10(URI) -> %% Try most general form of request - Result = (catch {ok, http:request(get, {URI,[]}, [], [])}), + Result = (catch {ok, httpc:request(get, {URI,[]}, [], [])}), uri_get_http_1(Result, URI). uri_get_http_1(Result, URI) -> diff --git a/lib/erl_interface/doc/src/ei_connect.xml b/lib/erl_interface/doc/src/ei_connect.xml index abf705f9e2..927395d1bf 100644 --- a/lib/erl_interface/doc/src/ei_connect.xml +++ b/lib/erl_interface/doc/src/ei_connect.xml @@ -54,7 +54,7 @@ the operation, if the primitive does not complete within the time specified, the function will return an error and <c><![CDATA[erl_errno]]></c> will be set to <c><![CDATA[ETIMEDOUT]]></c>. With - communication primitive is ment an operation on the socket, like + communication primitive is meant an operation on the socket, like <c><![CDATA[connect]]></c>, <c><![CDATA[accept]]></c>, <c><![CDATA[recv]]></c> or <c><![CDATA[send]]></c>.</p> <p>Obviously the timeouts are for implementing fault tolerance, not to keep hard realtime promises. The <c><![CDATA[_tmo]]></c> functions diff --git a/lib/erl_interface/src/legacy/erl_marshal.c b/lib/erl_interface/src/legacy/erl_marshal.c index c57c552b90..18315bfbd3 100644 --- a/lib/erl_interface/src/legacy/erl_marshal.c +++ b/lib/erl_interface/src/legacy/erl_marshal.c @@ -26,6 +26,7 @@ #include <ctype.h> #include <sys/types.h> #include <string.h> +#include <limits.h> #include "erl_interface.h" #include "erl_marshal.h" @@ -178,26 +179,14 @@ int erl_encode_it(ETERM *ep, unsigned char **ext, int dist) return 0; case ERL_INTEGER: - i = ep->uval.ival.i; - /* ERL_SMALL_BIG */ - if ((i > ERL_MAX) || (i < ERL_MIN)) { - *(*ext)++ = ERL_SMALL_BIG_EXT; - *(*ext)++ = 4; /* four bytes */ - if ((*(*ext)++ = ((i>>31) & 0x01))) /* sign byte */ - i = -i; - *(*ext)++ = i & 0xff; /* LSB first */ - *(*ext)++ = (i >> 8) & 0xff; - *(*ext)++ = (i >> 16) & 0xff; - *(*ext)++ = (i >> 24) & 0x7f; /* Don't include the sign bit */ - return 0; - } + i = ep->uval.ival.i; /* SMALL_INTEGER */ if ((i < 256) && (i >= 0)) { *(*ext)++ = ERL_SMALL_INTEGER_EXT; *(*ext)++ = i & 0xff; return 0; } - /* INTEGER */ + /* R14B: Use all 32 bits of INTEGER_EXT */ *(*ext)++ = ERL_INTEGER_EXT; *(*ext)++ = (i >> 24) & 0xff; *(*ext)++ = (i >> 16) & 0xff; @@ -208,23 +197,23 @@ int erl_encode_it(ETERM *ep, unsigned char **ext, int dist) case ERL_U_INTEGER: u = ep->uval.uival.u; /* ERL_U_SMALL_BIG */ - if (u > ERL_MAX) { - *(*ext)++ = ERL_SMALL_BIG_EXT; - *(*ext)++ = 4; /* four bytes */ - *(*ext)++ = 0; /* sign byte */ - *(*ext)++ = u & 0xff; /* LSB first */ - *(*ext)++ = (u >> 8) & 0xff; - *(*ext)++ = (u >> 16) & 0xff; - *(*ext)++ = (u >> 24) & 0xff; - return 0; + if ((int)u < 0) { + *(*ext)++ = ERL_SMALL_BIG_EXT; + *(*ext)++ = 4; /* four bytes */ + *(*ext)++ = 0; /* sign byte */ + *(*ext)++ = u & 0xff; /* LSB first */ + *(*ext)++ = (u >> 8) & 0xff; + *(*ext)++ = (u >> 16) & 0xff; + *(*ext)++ = (u >> 24) & 0xff; + return 0; } /* SMALL_INTEGER */ - if ((u < 256) && (u >= 0)) { + if (u < 256) { *(*ext)++ = ERL_SMALL_INTEGER_EXT; *(*ext)++ = u & 0xff; return 0; } - /* INTEGER */ + /* R14B: Use all 32 bits of INTEGER_EXT */ *(*ext)++ = ERL_INTEGER_EXT; *(*ext)++ = (u >> 24) & 0xff; *(*ext)++ = (u >> 16) & 0xff; @@ -234,29 +223,28 @@ int erl_encode_it(ETERM *ep, unsigned char **ext, int dist) case ERL_LONGLONG: l = ep->uval.llval.i; /* ERL_SMALL_BIG */ - if ((l > ((long long) ERL_MAX)) || - (l < ((long long) ERL_MIN))) { - *(*ext)++ = ERL_SMALL_BIG_EXT; - *(*ext)++ = 8; /* eight bytes */ - if ((*(*ext)++ = ((l>>63) & 0x01))) /* sign byte */ + if (l > ((long long) INT_MAX) || l < ((long long) INT_MIN)) { + *(*ext)++ = ERL_SMALL_BIG_EXT; + *(*ext)++ = 8; + if ((*(*ext)++ = (l<0))) /* sign byte */ l = -l; - *(*ext)++ = l & 0xff; /* LSB first */ + *(*ext)++ = l & 0xff; /* LSB first */ *(*ext)++ = (l >> 8) & 0xff; *(*ext)++ = (l >> 16) & 0xff; - *(*ext)++ = (l >> 24) & 0xff; - *(*ext)++ = (l >> 32) & 0xff; - *(*ext)++ = (l >> 40) & 0xff; - *(*ext)++ = (l >> 48) & 0xff; - *(*ext)++ = (l >> 56) & 0x7f; /* Don't include the sign bit */ + *(*ext)++ = (l >> 24) & 0xff; + *(*ext)++ = (l >> 32) & 0xff; + *(*ext)++ = (l >> 40) & 0xff; + *(*ext)++ = (l >> 48) & 0xff; + *(*ext)++ = (l >> 56) & 0xff; return 0; } /* SMALL_INTEGER */ if ((l < 256) && (l >= 0)) { - *(*ext)++ = ERL_SMALL_INTEGER_EXT; - *(*ext)++ = l & 0xff; - return 0; + *(*ext)++ = ERL_SMALL_INTEGER_EXT; + *(*ext)++ = l & 0xff; + return 0; } - /* INTEGER */ + /* R14B: Use all 32 bits of INTEGER_EXT */ *(*ext)++ = ERL_INTEGER_EXT; *(*ext)++ = (l >> 24) & 0xff; *(*ext)++ = (l >> 16) & 0xff; @@ -267,7 +255,7 @@ int erl_encode_it(ETERM *ep, unsigned char **ext, int dist) case ERL_U_LONGLONG: ul = ep->uval.ullval.u; /* ERL_U_SMALL_BIG */ - if (ul > ((unsigned long long) ERL_MAX)) { + if (ul > ((unsigned long long) INT_MAX)) { *(*ext)++ = ERL_SMALL_BIG_EXT; *(*ext)++ = 8; /* eight bytes */ *(*ext)++ = 0; /* sign byte */ @@ -287,7 +275,7 @@ int erl_encode_it(ETERM *ep, unsigned char **ext, int dist) *(*ext)++ = ul & 0xff; return 0; } - /* INTEGER */ + /* R14B: Use all 32 bits of INTEGER_EXT */ *(*ext)++ = ERL_INTEGER_EXT; *(*ext)++ = (ul >> 24) & 0xff; *(*ext)++ = (ul >> 16) & 0xff; @@ -732,11 +720,6 @@ static ETERM *erl_decode_it(unsigned char **ext) if (arity > 8) goto big_truncate; - if (arity == 8 && ((*ext)[7] & 0x80) && sign) { - /* MSB already occupied ! */ - goto big_truncate; - } - if (arity == 4 && ((*ext)[3] & 0x80) && !sign) { /* It will fit into an unsigned int !! */ u = (((*ext)[3] << 24)|((*ext)[2])<< 16|((*ext)[1]) << 8 |(**ext)); @@ -747,14 +730,10 @@ static ETERM *erl_decode_it(unsigned char **ext) return ep; } else if (arity == 4 && !((*ext)[3] & 0x80)) { /* It will fit into an int !! - * Note: It comes in "one's-complement notation" */ - if (sign) - i = (int) (~(((*ext)[3] << 24) | ((*ext)[2])<< 16 | - ((*ext)[1]) << 8 | (**ext)) | (unsigned int) sign); - else - i = (int) (((*ext)[3] << 24) | ((*ext)[2])<< 16 | - ((*ext)[1]) << 8 | (**ext)); + i = (int) (((*ext)[3] << 24) | ((*ext)[2])<< 16 | + ((*ext)[1]) << 8 | (**ext)); + if (sign) i = -i; ERL_TYPE(ep) = ERL_INTEGER; ep->uval.ival.i = i; *ext += arity; @@ -780,8 +759,10 @@ static ETERM *erl_decode_it(unsigned char **ext) for(x = 0 ; x < arity ; x++) { l |= ((long long)(*ext)[x]) << ((long long)(8*x)); } - - if (sign) l = (long long) (~l | (unsigned long long) sign); + if (sign) { + l = -l; + if (l > 0) goto big_truncate; + } ERL_TYPE(ep) = ERL_LONGLONG; ep->uval.llval.i = l; diff --git a/lib/erl_interface/src/misc/ei_decode_term.c b/lib/erl_interface/src/misc/ei_decode_term.c index ddcbfa5a9a..75c5dc9460 100644 --- a/lib/erl_interface/src/misc/ei_decode_term.c +++ b/lib/erl_interface/src/misc/ei_decode_term.c @@ -34,7 +34,6 @@ int ei_decode_ei_term(const char* buf, int* index, ei_term* term) const char* s = buf + *index, * s0 = s; int len, i, n, sign; char c; - double f; if (term == NULL) return -1; c = term->ei_type = get8(s); diff --git a/lib/erl_interface/src/prog/erl_call.c b/lib/erl_interface/src/prog/erl_call.c index f0d638324d..93b84cbb36 100644 --- a/lib/erl_interface/src/prog/erl_call.c +++ b/lib/erl_interface/src/prog/erl_call.c @@ -812,7 +812,8 @@ static int get_module(char **mbuf, char **mname) *mname = (char *) calloc(i+1, sizeof(char)); memcpy(*mname, start, i); } - free(mbuf); /* Allocated in read_stdin() */ + if (*mbuf) + free(*mbuf); /* Allocated in read_stdin() */ return len; diff --git a/lib/erl_interface/test/ei_decode_SUITE.erl b/lib/erl_interface/test/ei_decode_SUITE.erl index c6858b45ad..09a37409f2 100644 --- a/lib/erl_interface/test/ei_decode_SUITE.erl +++ b/lib/erl_interface/test/ei_decode_SUITE.erl @@ -222,14 +222,16 @@ send_integers(P) -> ?line send_term_as_binary(P,256), % INTEGER_EXT smallest pos (*) ?line send_term_as_binary(P,-1), % INTEGER_EXT largest neg - ?line send_term_as_binary(P, 16#07ffffff), % INTEGER_EXT largest (28 bits) - ?line send_term_as_binary(P,-16#08000000), % INTEGER_EXT smallest - ?line send_term_as_binary(P, 16#08000000), % SMALL_BIG_EXT smallest pos(*) - ?line send_term_as_binary(P,-16#08000001), % SMALL_BIG_EXT largest neg (*) - - ?line send_term_as_binary(P, 16#7fffffff), % SMALL_BIG_EXT largest i32 - ?line send_term_as_binary(P,-16#80000000), % SMALL_BIG_EXT smallest i32 - + ?line send_term_as_binary(P, 16#07ffffff), % INTEGER_EXT old largest (28 bits) + ?line send_term_as_binary(P,-16#08000000), % INTEGER_EXT old smallest + ?line send_term_as_binary(P, 16#08000000), % SMALL_BIG_EXT old smallest pos(*) + ?line send_term_as_binary(P,-16#08000001), % SMALL_BIG_EXT old largest neg (*) + + ?line send_term_as_binary(P, 16#7fffffff), % INTEGER_EXT new largest (32 bits) + ?line send_term_as_binary(P,-16#80000000), % INTEGER_EXT new smallest (32 bis) + ?line send_term_as_binary(P, 16#80000000), % SMALL_BIG_EXT new smallest pos(*) + ?line send_term_as_binary(P,-16#80000001), % SMALL_BIG_EXT new largest neg (*) + case erlang:system_info(wordsize) of 4 -> ?line send_term_as_binary(P, 16#80000000),% SMALL_BIG_EXT u32 @@ -266,15 +268,17 @@ send_integers2(P) -> ?line send_term_as_binary(P,255), % SMALL_INTEGER_EXT largest ?line send_term_as_binary(P,256), % INTEGER_EXT smallest pos (*) ?line send_term_as_binary(P,-1), % INTEGER_EXT largest neg + + ?line send_term_as_binary(P, 16#07ffffff), % INTEGER_EXT old largest (28 bits) + ?line send_term_as_binary(P,-16#08000000), % INTEGER_EXT old smallest + ?line send_term_as_binary(P, 16#08000000), % SMALL_BIG_EXT old smallest pos(*) + ?line send_term_as_binary(P,-16#08000001), % SMALL_BIG_EXT old largest neg (*) - ?line send_term_as_binary(P, 16#07ffffff), % INTEGER_EXT largest (28 bits) - ?line send_term_as_binary(P,-16#08000000), % INTEGER_EXT smallest - ?line send_term_as_binary(P, 16#08000000), % SMALL_BIG_EXT smallest pos(*) - ?line send_term_as_binary(P,-16#08000001), % SMALL_BIG_EXT largest neg (*) + ?line send_term_as_binary(P, 16#7fffffff), % INTEGER_EXT new largest (32 bits) + ?line send_term_as_binary(P,-16#80000000), % INTEGER_EXT new smallest + ?line send_term_as_binary(P, 16#80000000), % SMALL_BIG_EXT new smallest pos(*) + ?line send_term_as_binary(P,-16#80000001), % SMALL_BIG_EXT new largest neg (*) - ?line send_term_as_binary(P, 16#7fffffff), % SMALL_BIG_EXT largest i32 - ?line send_term_as_binary(P,-16#80000000), % SMALL_BIG_EXT smallest i32 - ?line send_term_as_binary(P, 16#80000000),% SMALL_BIG_EXT u32 ?line send_term_as_binary(P, 16#ffffffff),% SMALL_BIG_EXT largest u32 ?line send_term_as_binary(P, 16#7fffffffffff), % largest i48 diff --git a/lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c b/lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c index 5447e2deb3..b349138ae9 100644 --- a/lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c +++ b/lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c @@ -246,13 +246,23 @@ TESTCASE(test_ei_decode_long) EI_DECODE_2 (decode_long, 5, long, 256); EI_DECODE_2 (decode_long, 5, long, -1); + /* Old 28 bit limits for INTEGER_EXT */ EI_DECODE_2 (decode_long, 5, long, 0x07ffffff); EI_DECODE_2 (decode_long, 5, long, -0x08000000); - EI_DECODE_2 (decode_long, 7, long, 0x08000000); - EI_DECODE_2 (decode_long, 7, long, -0x08000001); + EI_DECODE_2 (decode_long, 5, long, 0x08000000); + EI_DECODE_2 (decode_long, 5, long, -0x08000001); - EI_DECODE_2 (decode_long, 7, long, 0x7fffffff); - EI_DECODE_2 (decode_long, 7, long, -ll(0x80000000)); /* Strange :-( */ + /* New 32 bit limits for INTEGER_EXT */ + EI_DECODE_2 (decode_long, 5, long, 0x7fffffff); + EI_DECODE_2 (decode_long, 5, long, -ll(0x80000000)); /* Strange :-( */ + if (sizeof(long) > 4) { + EI_DECODE_2(decode_long, 7, long, 0x80000000); + EI_DECODE_2(decode_long, 7, long, -ll(0x80000001)); + } + else { + EI_DECODE_2_FAIL(decode_long, 7, long, 0x80000000); + EI_DECODE_2_FAIL(decode_long, 7, long, -ll(0x80000001)); + } EI_DECODE_2_FAIL(decode_long, 7, long, 0x80000000); EI_DECODE_2_FAIL(decode_long, 7, long, 0xffffffff); @@ -280,11 +290,13 @@ TESTCASE(test_ei_decode_ulong) EI_DECODE_2 (decode_ulong, 5, unsigned long, 0x07ffffff); EI_DECODE_2_FAIL(decode_ulong, 5, unsigned long, -0x08000000); - EI_DECODE_2 (decode_ulong, 7, unsigned long, 0x08000000); - EI_DECODE_2_FAIL(decode_ulong, 7, unsigned long, -0x08000001); + EI_DECODE_2 (decode_ulong, 5, unsigned long, 0x08000000); + EI_DECODE_2_FAIL(decode_ulong, 5, unsigned long, -0x08000001); - EI_DECODE_2 (decode_ulong, 7, unsigned long, 0x7fffffff); - EI_DECODE_2_FAIL(decode_ulong, 7, unsigned long, -ll(0x80000000)); + EI_DECODE_2 (decode_ulong, 5, unsigned long, 0x7fffffff); + EI_DECODE_2_FAIL(decode_ulong, 5, unsigned long, -ll(0x80000000)); + EI_DECODE_2 (decode_ulong, 7, unsigned long, 0x80000000); + EI_DECODE_2_FAIL(decode_ulong, 7, unsigned long, -ll(0x80000001)); if (sizeof(long) > 4) { EI_DECODE_2 (decode_ulong, 11, unsigned long, ll(0x8000000000000000)); @@ -319,13 +331,14 @@ TESTCASE(test_ei_decode_longlong) EI_DECODE_2 (decode_longlong, 5, EI_LONGLONG, 0x07ffffff); EI_DECODE_2 (decode_longlong, 5, EI_LONGLONG, -0x08000000); - EI_DECODE_2 (decode_longlong, 7, EI_LONGLONG, 0x08000000); - EI_DECODE_2 (decode_longlong, 7, EI_LONGLONG, -0x08000001); - - EI_DECODE_2 (decode_longlong, 7, EI_LONGLONG, 0x7fffffff); - EI_DECODE_2 (decode_longlong, 7, EI_LONGLONG, -ll(0x80000000)); + EI_DECODE_2 (decode_longlong, 5, EI_LONGLONG, 0x08000000); + EI_DECODE_2 (decode_longlong, 5, EI_LONGLONG, -0x08000001); + EI_DECODE_2 (decode_longlong, 5, EI_LONGLONG, 0x7fffffff); + EI_DECODE_2 (decode_longlong, 5, EI_LONGLONG, -ll(0x80000000)); EI_DECODE_2 (decode_longlong, 7, EI_LONGLONG, 0x80000000); + EI_DECODE_2 (decode_longlong, 7, EI_LONGLONG, -ll(0x80000001)); + EI_DECODE_2 (decode_longlong, 7, EI_LONGLONG, 0xffffffff); EI_DECODE_2 (decode_longlong, 9, EI_LONGLONG, ll(0x7fffffffffff)); @@ -352,13 +365,14 @@ TESTCASE(test_ei_decode_ulonglong) EI_DECODE_2 (decode_ulonglong, 5, EI_ULONGLONG, 0x07ffffff); EI_DECODE_2_FAIL(decode_ulonglong, 5, EI_ULONGLONG, -0x08000000); - EI_DECODE_2 (decode_ulonglong, 7, EI_ULONGLONG, 0x08000000); - EI_DECODE_2_FAIL(decode_ulonglong, 7, EI_ULONGLONG, -0x08000001); - - EI_DECODE_2 (decode_ulonglong, 7, EI_ULONGLONG, 0x7fffffff); - EI_DECODE_2_FAIL(decode_ulonglong, 7, EI_ULONGLONG, -ll(0x80000000)); + EI_DECODE_2 (decode_ulonglong, 5, EI_ULONGLONG, 0x08000000); + EI_DECODE_2_FAIL(decode_ulonglong, 5, EI_ULONGLONG, -0x08000001); + EI_DECODE_2 (decode_ulonglong, 5, EI_ULONGLONG, 0x7fffffff); + EI_DECODE_2_FAIL(decode_ulonglong, 5, EI_ULONGLONG, -ll(0x80000000)); EI_DECODE_2 (decode_ulonglong, 7, EI_ULONGLONG, 0x80000000); + EI_DECODE_2_FAIL(decode_ulonglong, 7, EI_ULONGLONG, -0x80000001); + EI_DECODE_2 (decode_ulonglong, 7, EI_ULONGLONG, 0xffffffff); EI_DECODE_2 (decode_ulonglong, 9, EI_ULONGLONG, ll(0x7fffffffffff)); diff --git a/lib/erl_interface/test/erl_eterm_SUITE_data/eterm_test.c b/lib/erl_interface/test/erl_eterm_SUITE_data/eterm_test.c index 6b2ec8f766..f273efd532 100644 --- a/lib/erl_interface/test/erl_eterm_SUITE_data/eterm_test.c +++ b/lib/erl_interface/test/erl_eterm_SUITE_data/eterm_test.c @@ -63,32 +63,108 @@ TESTCASE(build_terms) report(1); } +static int abs_and_sign(ETERM* v, unsigned long long* av, int* sign) +{ + long long sv; + switch (ERL_TYPE(v)) { + case ERL_INTEGER: sv = ERL_INT_VALUE(v); break; + case ERL_U_INTEGER: *av = ERL_INT_UVALUE(v); *sign = 0; return 1; + case ERL_LONGLONG: sv = ERL_LL_VALUE(v); break; + case ERL_U_LONGLONG: *av = ERL_LL_UVALUE(v); *sign = 0; return 1; + default: return 0; + } + if (sv < 0) { + *av = -sv; + *sign = 1; + } + else { + *av = sv; + *sign = 0; + } + return 1; +} + +/* Shouldn't erl_match() cope with this? +*/ +static int eq_ints(ETERM* a, ETERM* b) +{ + unsigned long long a_abs, b_abs; + int a_sign, b_sign; + return abs_and_sign(a, &a_abs, &a_sign) && abs_and_sign(b, &b_abs, &b_sign) + && (a_abs == b_abs) && (a_sign == b_sign); +} + +static void encode_decode(ETERM* original, const char* text) +{ + static unsigned char encoded[16*1024]; + ETERM* new_terms; + int bytes = erl_encode(original, encoded); + + if (bytes == 0) { + fail("failed to encode terms"); + } + else if (bytes > sizeof(encoded)) { + fail("encoded terms buffer overflow"); + } + else if ((new_terms = erl_decode(encoded)) == NULL) { + fail("failed to decode terms"); + } + else if (!erl_match(original, new_terms) && !eq_ints(original, new_terms)) { + erl_print_term(stderr, original); + fprintf(stderr, "(%i) != (%i)", ERL_TYPE(original), ERL_TYPE(new_terms)); + erl_print_term(stderr, new_terms); + fprintf(stderr, " [%s]\r\n", text); + fail("decoded terms didn't match original"); + } + erl_free_term(original); + erl_free_term(new_terms); +} /* * Converts an Erlang term to the external term format and back again. */ TESTCASE(round_trip_conversion) { - ETERM* original; - ETERM* new_terms; - char encoded[16*1024]; - int n; + int n, i; erl_init(NULL, 0); - original = all_types(); - if (erl_encode(original, encoded) == 0) + encode_decode(all_types(), "ALL"); + { - fail("failed to encode terms"); - } else if ((new_terms = erl_decode(encoded)) == NULL) + int v; + for (v = 8; v; v <<= 1) { + for (i=-4; i<4; i++) { + encode_decode(erl_mk_int(v+i), "INT"); + encode_decode(erl_mk_int(-(v+i)), "NEG INT"); + } + } + } { - fail("failed to decode terms"); - } else if (!erl_match(original, new_terms)) + unsigned int v; + for (v = 8; v; v <<= 1) { + for (i=-4; i<4; i++) { + encode_decode(erl_mk_uint(v+i), "UINT"); + } + } + } { - fail("decoded terms didn't match original"); + long long v; + for (v = 8; v; v <<= 1) { + for (i=-4; i<4; i++) { + encode_decode(erl_mk_longlong(v+i), "LONGLONG"); + encode_decode(erl_mk_longlong(-(v+i)), "NEG LONGLONG"); + } + } + } + { + unsigned long long v; + for (v = 8; v; v <<= 1) { + for (i=-4; i<4; i++) { + encode_decode(erl_mk_ulonglong(v+i), "ULONGLONG"); + } + } } - erl_free_term(original); - erl_free_term(new_terms); report(1); } diff --git a/lib/eunit/doc/overview.edoc b/lib/eunit/doc/overview.edoc index 2583f0be25..be05a13fba 100644 --- a/lib/eunit/doc/overview.edoc +++ b/lib/eunit/doc/overview.edoc @@ -913,7 +913,7 @@ To make the descriptions simpler, we first list some definitions: <td>`CleanupX'</td><td>`(X::any(), R::any()) -> any()'</td> </tr> <tr> -<td>`Instantiator'</td><td>`((R::any()) -> Tests) | {with, [AbstractTestFun::((any()) -> any())]}'</td> +<td>`Instantiator'</td><td>`((R::any()) -> Tests | {with, [AbstractTestFun::((any()) -> any())]}'</td> </tr> <tr> <td>`Where'</td><td>`local | spawn | {spawn, Node::atom()}'</td> diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl index 9df3dedb45..1f0247a040 100644 --- a/lib/hipe/cerl/erl_bif_types.erl +++ b/lib/hipe/cerl/erl_bif_types.erl @@ -2337,8 +2337,7 @@ type(lists, flatten, 1, Xs) -> t_list(); false -> X2 = type(lists, flatten, 1, [t_inf(X1, t_list())]), - t_sup(t_list(t_subtract(X1, t_list())), - X2) + t_sup(t_list(t_subtract(X1, t_list())), X2) end end end); @@ -2349,10 +2348,20 @@ type(lists, flatmap, 2, Xs) -> true -> t_nil(); false -> case check_fun_application(F, [t_list_elements(List)]) of - ok -> - case t_is_cons(List) of - true -> t_nonempty_list(t_list_elements(t_fun_range(F))); - false -> t_list(t_list_elements(t_fun_range(F))) + ok -> + R = t_fun_range(F), + case t_is_nil(R) of + true -> t_nil(); + false -> + Elems = t_list_elements(R), + case t_is_cons(List) of + true -> + case t_is_subtype(t_nil(), R) of + true -> t_list(Elems); + false -> t_nonempty_list(Elems) + end; + false -> t_list(Elems) + end end; error -> case t_is_cons(List) of diff --git a/lib/hipe/rtl/hipe_rtl_primops.erl b/lib/hipe/rtl/hipe_rtl_primops.erl index 560e0259f8..0361053676 100644 --- a/lib/hipe/rtl/hipe_rtl_primops.erl +++ b/lib/hipe/rtl/hipe_rtl_primops.erl @@ -701,9 +701,9 @@ gen_cons(Dst, [Arg1, Arg2]) -> %% Increase refcount %% fe->refc++; %% -%% Link to the process off_heap.funs list -%% funp->next = p->off_heap.funs; -%% p->off_heap.funs = funp; +%% Link to the process off_heap list +%% funp->next = p->off_heap.first; +%% p->off_heap.first = funp; %% %% Tag the thing %% return make_fun(funp); @@ -729,9 +729,9 @@ gen_mkfun([Dst], {_Mod, _FunId, _Arity} = MFidA, MagicNr, Index, FreeVars) -> %% fe->refc++; SkeletonCode = gen_fun_thing_skeleton(HP, MFidA, NumFree, MagicNr, Index), - %% Link to the process off_heap.funs list - %% funp->next = p->off_heap.funs; - %% p->off_heap.funs = funp; + %% Link to the process off_heap list + %% funp->next = p->off_heap.first; + %% p->off_heap.first = funp; LinkCode = gen_link_closure(HP), %% Tag the thing and increase the heap_pointer. @@ -797,14 +797,14 @@ gen_link_closure(FUNP) -> end. gen_link_closure_private(FUNP) -> - %% Link to the process off_heap.funs list - %% funp->next = p->off_heap.funs; - %% p->off_heap.funs = funp; + %% Link fun to the process off_heap list + %% funp->next = p->off_heap.first; + %% p->off_heap.first = funp; FunsVar = hipe_rtl:mk_new_reg(), - [load_p_field(FunsVar,?P_OFF_HEAP_FUNS), + [load_p_field(FunsVar,?P_OFF_HEAP_FIRST), hipe_rtl:mk_store(FUNP, hipe_rtl:mk_imm(?EFT_NEXT), FunsVar), - store_p_field(FUNP,?P_OFF_HEAP_FUNS)]. + store_p_field(FUNP,?P_OFF_HEAP_FIRST)]. gen_link_closure_non_private(_FUNP) -> []. diff --git a/lib/hipe/rtl/hipe_tagscheme.erl b/lib/hipe/rtl/hipe_tagscheme.erl index dc44b803a1..c0b6dfad8a 100644 --- a/lib/hipe/rtl/hipe_tagscheme.erl +++ b/lib/hipe/rtl/hipe_tagscheme.erl @@ -849,9 +849,9 @@ create_refc_binary(Base, Size, Flags, Dst) -> heap_arch_spec(HP) -> Tmp1 = hipe_rtl:mk_new_reg(), % MSO state - [hipe_rtl_arch:pcb_load(Tmp1, ?P_OFF_HEAP_MSO), + [hipe_rtl_arch:pcb_load(Tmp1, ?P_OFF_HEAP_FIRST), set_field_from_pointer({proc_bin, next}, HP, Tmp1), - hipe_rtl_arch:pcb_store(?P_OFF_HEAP_MSO, HP)]. + hipe_rtl_arch:pcb_store(?P_OFF_HEAP_FIRST, HP)]. test_heap_binary(Binary, TrueLblName, FalseLblName) -> Tmp1 = hipe_rtl:mk_new_reg_gcsafe(), diff --git a/lib/inviso/src/inviso.erl b/lib/inviso/src/inviso.erl index de42926ffe..0eda06a5c2 100644 --- a/lib/inviso/src/inviso.erl +++ b/lib/inviso/src/inviso.erl @@ -129,7 +129,7 @@ start(Options) -> add_node(Reference) ->
gen_server:call(?CONTROLLER,{add_nodes,[node()],[],Reference,any_ref},?CALL_TIMEOUT).
-add_node(Reference,Options) when list(Options) ->
+add_node(Reference,Options) when is_list(Options) -> gen_server:call(?CONTROLLER,{add_nodes,[node()],Options,Reference,any_ref},?CALL_TIMEOUT).
%% -----------------------------------------------------------------------------
@@ -142,7 +142,7 @@ add_node(Reference,Options) when list(Options) -> add_node_if_ref(Reference) ->
gen_server:call(?CONTROLLER,{add_nodes,[node()],[],Reference,if_ref},?CALL_TIMEOUT).
-add_node_if_ref(Reference,Options) when list(Options) ->
+add_node_if_ref(Reference,Options) when is_list(Options) -> gen_server:call(?CONTROLLER,{add_nodes,[node()],Options,Reference,if_ref},?CALL_TIMEOUT).
%% -----------------------------------------------------------------------------
@@ -156,10 +156,10 @@ add_node_if_ref(Reference,Options) when list(Options) -> %% system. By speicifying node() as the node where the runtime component shall
%% be started. The return value will then follow the rules of non distributed
%% returnvalues and not have a node indicator.
-add_nodes(Nodes,Reference) when list(Nodes) ->
+add_nodes(Nodes,Reference) when is_list(Nodes) -> gen_server:call(?CONTROLLER,{add_nodes,Nodes,[],Reference,any_ref},?CALL_TIMEOUT).
-add_nodes(Nodes,Reference,Options) when list(Nodes),list(Options) ->
+add_nodes(Nodes,Reference,Options) when is_list(Nodes),is_list(Options) -> gen_server:call(?CONTROLLER,{add_nodes,Nodes,Options,Reference,any_ref},?CALL_TIMEOUT).
%% -----------------------------------------------------------------------------
@@ -168,10 +168,10 @@ add_nodes(Nodes,Reference,Options) when list(Nodes),list(Options) -> %%
%% As add_nodes/2,/3 but will only connect to an already existing runtime component
%% if its reference is the same as the one given as argument.
-add_nodes_if_ref(Nodes,Reference) when list(Nodes) ->
+add_nodes_if_ref(Nodes,Reference) when is_list(Nodes) -> gen_server:call(?CONTROLLER,{add_nodes,Nodes,[],Reference,if_ref},?CALL_TIMEOUT).
-add_nodes_if_ref(Nodes,Reference,Options) when list(Nodes),list(Options) ->
+add_nodes_if_ref(Nodes,Reference,Options) when is_list(Nodes),is_list(Options) -> gen_server:call(?CONTROLLER,{add_nodes,Nodes,Options,Reference,if_ref},?CALL_TIMEOUT).
%% -----------------------------------------------------------------------------
@@ -182,9 +182,9 @@ add_nodes_if_ref(Nodes,Reference,Options) when list(Nodes),list(Options) -> %%
%% Change options on all or specified Nodes. This may result in for instance
%% reinitialization of overloadcheck.
-change_options(Options) when list(Options) ->
+change_options(Options) when is_list(Options) -> gen_server:call(?CONTROLLER,{change_options,all,Options},?CALL_TIMEOUT).
-change_options(Nodes,Options) when list(Nodes),list(Options) ->
+change_options(Nodes,Options) when is_list(Nodes),is_list(Options) -> gen_server:call(?CONTROLLER,{change_options,Nodes,Options},?CALL_TIMEOUT).
%% -----------------------------------------------------------------------------
@@ -234,7 +234,7 @@ change_options(Nodes,Options) when list(Nodes),list(Options) -> init_tracing(TracerDataList) ->
gen_server:call(?CONTROLLER,{init_tracing,TracerDataList},?CALL_TIMEOUT).
-init_tracing(Nodes,TracerData) when list(Nodes) ->
+init_tracing(Nodes,TracerData) when is_list(Nodes) -> gen_server:call(?CONTROLLER,{init_tracing,Nodes,TracerData},?CALL_TIMEOUT).
%% -----------------------------------------------------------------------------
@@ -270,10 +270,10 @@ stop_tracing(Nodes) when is_list(Nodes) -> clear() ->
gen_server:call(?CONTROLLER,{clear,all,[]},?CALL_TIMEOUT).
-clear(Nodes) when list(Nodes) ->
+clear(Nodes) when is_list(Nodes) -> gen_server:call(?CONTROLLER,{clear,Nodes,[]},?CALL_TIMEOUT).
-clear(Nodes,Options) when list(Nodes),list(Options) ->
+clear(Nodes,Options) when is_list(Nodes),is_list(Options) -> gen_server:call(?CONTROLLER,{clear,Nodes,Options},?CALL_TIMEOUT).
%% -----------------------------------------------------------------------------
@@ -355,9 +355,9 @@ stop_all() -> tp(Nodes,Module,Function,Arity,MatchSpec,Opts) ->
trace_pattern(Nodes,[{Module,Function,Arity,MatchSpec,Opts}],[global]).
-tp(Nodes,Module,Function,Arity,MatchSpec) when list(Nodes) ->
+tp(Nodes,Module,Function,Arity,MatchSpec) when is_list(Nodes) -> trace_pattern(Nodes,[{Module,Function,Arity,MatchSpec,[]}],[global]);
-tp(Module,Function,Arity,MatchSpec,Opts) when atom(Module) ->
+tp(Module,Function,Arity,MatchSpec,Opts) when is_atom(Module) -> trace_pattern(all,[{Module,Function,Arity,MatchSpec,Opts}],[global]).
tp(Module,Function,Arity,MatchSpec) ->
@@ -384,9 +384,9 @@ tp(PatternList) -> tpl(Nodes,Module,Function,Arity,MatchSpec,Opts) ->
trace_pattern(Nodes,[{Module,Function,Arity,MatchSpec,Opts}],[local]).
-tpl(Nodes,Module,Function,Arity,MatchSpec) when list(Nodes) ->
+tpl(Nodes,Module,Function,Arity,MatchSpec) when is_list(Nodes) -> trace_pattern(Nodes,[{Module,Function,Arity,MatchSpec,[]}],[local]);
-tpl(Module,Function,Arity,MatchSpec,Opts) when atom(Module) ->
+tpl(Module,Function,Arity,MatchSpec,Opts) when is_atom(Module) -> trace_pattern(all,[{Module,Function,Arity,MatchSpec,Opts}],[local]).
tpl(Module,Function,Arity,MatchSpec) ->
@@ -417,12 +417,12 @@ ctp(Nodes,Module,Function,Arity) -> ctp(Module,Function,Arity) ->
trace_pattern(all,[{Module,Function,Arity,false,[only_loaded]}],[global]).
-ctp(Nodes,PatternList) when list(PatternList) ->
+ctp(Nodes,PatternList) when is_list(PatternList) -> trace_pattern(Nodes,
lists:map(fun({M,F,A})->{M,F,A,false,[only_loaded]} end,PatternList),
[global]).
-ctp(PatternList) when list(PatternList) ->
+ctp(PatternList) when is_list(PatternList) -> trace_pattern(all,
lists:map(fun({M,F,A})->{M,F,A,false,[only_loaded]} end,PatternList),
[global]).
@@ -445,12 +445,12 @@ ctpl(Nodes,Module,Function,Arity) -> ctpl(Module,Function,Arity) ->
trace_pattern(all,[{Module,Function,Arity,false,[only_loaded]}],[local]).
-ctpl(Nodes,PatternList) when list(PatternList) ->
+ctpl(Nodes,PatternList) when is_list(PatternList) -> trace_pattern(Nodes,
lists:map(fun({M,F,A})->{M,F,A,false,[only_loaded]} end,PatternList),
[local]).
-ctpl(PatternList) when list(PatternList) ->
+ctpl(PatternList) when is_list(PatternList) -> trace_pattern(all,
lists:map(fun({M,F,A})->{M,F,A,false,[only_loaded]} end,PatternList),
[local]).
@@ -485,19 +485,19 @@ trace_pattern(Nodes,Patterns,FlagList) -> %% specifying a certain pid at all nodes. Or an empty TraceConfList for all
%% nodes.
%% When calling several nodes, the nodes are called in parallel.
-tf(Nodes,PidSpec,FlagList) when list(Nodes),list(FlagList) ->
+tf(Nodes,PidSpec,FlagList) when is_list(Nodes),is_list(FlagList) -> trace_flags(Nodes,[{PidSpec, FlagList}],true).
-tf(Nodes,TraceConfList) when list(Nodes),list(TraceConfList) ->
+tf(Nodes,TraceConfList) when is_list(Nodes),is_list(TraceConfList) -> trace_flags(Nodes,TraceConfList,true);
-tf(PidSpec,FlagList) when list(FlagList) ->
+tf(PidSpec,FlagList) when is_list(FlagList) -> trace_flags(all,[{PidSpec,FlagList}],true).
-tf(ArgList) when list(ArgList) -> % This one has triple functionality!
+tf(ArgList) when is_list(ArgList) -> % This one has triple functionality! case ArgList of
- [{_Process,Flags}|_] when list(Flags),atom(hd(Flags))-> % A call to all nodes.
+ [{_Process,Flags}|_] when is_list(Flags),is_atom(hd(Flags))-> % A call to all nodes. trace_flags(all,ArgList,true);
- [{_Node,TraceConfList}|_] when list(TraceConfList),tuple(hd(TraceConfList)) ->
+ [{_Node,TraceConfList}|_] when is_list(TraceConfList),is_tuple(hd(TraceConfList)) -> trace_flags(ArgList,true);
[{_Node,_TraceConfList,_How}|_] ->
trace_flags(ArgList);
@@ -517,15 +517,15 @@ tf(ArgList) when list(ArgList) -> % This one has triple functionality %% The functions without a Nodes argument means all nodes, in a non-distributed
%% environment it means the local node.
%% When calling several nodes, the nodes are called in parallel.
-ctf(Nodes,PidSpec,FlagList) when list(Nodes),list(FlagList) ->
+ctf(Nodes,PidSpec,FlagList) when is_list(Nodes),is_list(FlagList) -> trace_flags(Nodes,[{PidSpec,FlagList}],false).
-ctf(Nodes,TraceConfList) when list(Nodes),list(TraceConfList) ->
+ctf(Nodes,TraceConfList) when is_list(Nodes),is_list(TraceConfList) -> trace_flags(Nodes,TraceConfList,false);
-ctf(PidSpec,FlagList) when list(FlagList) ->
+ctf(PidSpec,FlagList) when is_list(FlagList) -> trace_flags(all,[{PidSpec,FlagList}],false).
-ctf(TraceConfList) when list(TraceConfList) ->
+ctf(TraceConfList) when is_list(TraceConfList) -> trace_flags(all,TraceConfList,false).
%% -----------------------------------------------------------------------------
@@ -668,10 +668,10 @@ init_tpm(Nodes,Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc) -> tpm(Mod,Func,Arity,MS) ->
tpm(all,Mod,Func,Arity,MS).
-tpm(Nodes,Mod,Func,Arity,MS) when integer(Arity) ->
+tpm(Nodes,Mod,Func,Arity,MS) when is_integer(Arity) -> gen_server:call(?CONTROLLER,
{meta_pattern,Nodes,{tpm,[Mod,Func,Arity,MS]}});
-tpm(Mod,Func,Arity,MS,CallFunc) when integer(Arity) ->
+tpm(Mod,Func,Arity,MS,CallFunc) when is_integer(Arity) -> tpm(all,Mod,Func,Arity,MS,CallFunc).
tpm(Nodes,Mod,Func,Arity,MS,CallFunc) ->
@@ -695,11 +695,11 @@ tpm(Nodes,Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc) -> tpm_tracer(Mod,Func,Arity,MS) ->
tpm_tracer(all,Mod,Func,Arity,MS).
-tpm_tracer(Nodes,Mod,Func,Arity,MS) when integer(Arity) ->
+tpm_tracer(Nodes,Mod,Func,Arity,MS) when is_integer(Arity) -> gen_server:call(?CONTROLLER,
{meta_pattern,Nodes,{tpm_tracer,[Mod,Func,Arity,MS]}},
?CALL_TIMEOUT);
-tpm_tracer(Mod,Func,Arity,MS,CallFunc) when integer(Arity) ->
+tpm_tracer(Mod,Func,Arity,MS,CallFunc) when is_integer(Arity) -> tpm_tracer(all,Mod,Func,Arity,MS,CallFunc).
tpm_tracer(Nodes,Mod,Func,Arity,MS,CallFunc) ->
@@ -878,7 +878,7 @@ cancel_suspension() -> %% Status=running|{suspended,SReason}
%%
%% Get Status form all or specified runtime components.
-get_status(Nodes) when list(Nodes) ->
+get_status(Nodes) when is_list(Nodes) -> gen_server:call(?CONTROLLER,{get_status,Nodes},?CALL_TIMEOUT).
get_status() ->
@@ -918,7 +918,7 @@ get_tracerdata(Nodes) when is_list(Nodes) -> list_logs() ->
gen_server:call(?CONTROLLER,list_logs,?CALL_TIMEOUT).
-list_logs(TracerDataOrNodesList) when list(TracerDataOrNodesList) ->
+list_logs(TracerDataOrNodesList) when is_list(TracerDataOrNodesList) -> gen_server:call(?CONTROLLER,{list_logs,TracerDataOrNodesList},?CALL_TIMEOUT).
%% ------------------------------------------------------------------------------
@@ -960,17 +960,17 @@ list_logs(TracerDataOrNodesList) when list(TracerDataOrNodesList) -> %% Note that the client process using this function will wait until all files
%% are moved. The job can be cancelled, causing any already copied files to be
%% removed, by simply terminating the waiting client process.
-fetch_log(DestDir,Prefix) when list(DestDir),list(Prefix) ->
+fetch_log(DestDir,Prefix) when is_list(DestDir),is_list(Prefix) -> gen_server:call(?CONTROLLER,{fetch_log,node(),all,DestDir,Prefix},infinity).
-fetch_log(ToNode,DestDir,Prefix) when atom(ToNode),list(DestDir),list(Prefix) ->
+fetch_log(ToNode,DestDir,Prefix) when is_atom(ToNode),is_list(DestDir),is_list(Prefix) -> gen_server:call(?CONTROLLER,{fetch_log,ToNode,all,DestDir,Prefix},infinity);
-fetch_log(LogSpecList,DestDir,Prefix) when list(LogSpecList),list(DestDir),list(Prefix) ->
+fetch_log(LogSpecList,DestDir,Prefix) when is_list(LogSpecList),is_list(DestDir),is_list(Prefix) -> gen_server:call(?CONTROLLER,{fetch_log,node(),LogSpecList,DestDir,Prefix},infinity).
fetch_log(ToNode,LogSpecList,DestDir,Prefix)
- when atom(ToNode),list(LogSpecList),list(DestDir),list(Prefix) ->
+ when is_atom(ToNode),is_list(LogSpecList),is_list(DestDir),is_list(Prefix) -> gen_server:call(?CONTROLLER,{fetch_log,ToNode,LogSpecList,DestDir,Prefix},infinity).
%% ------------------------------------------------------------------------------
diff --git a/lib/inviso/src/inviso_c.erl b/lib/inviso/src/inviso_c.erl index ecbd5b0778..b1597a7f35 100644 --- a/lib/inviso/src/inviso_c.erl +++ b/lib/inviso/src/inviso_c.erl @@ -100,7 +100,7 @@ init({_Parent,Options}) -> end. %% ------------------------------------------------------------------------------ -handle_call({subscribe,Pid},_From,LD) when pid(Pid) -> +handle_call({subscribe,Pid},_From,LD) when is_pid(Pid) -> MRef=erlang:monitor(process,Pid), {reply,ok,LD#state{subscribers=[{Pid,MRef}|LD#state.subscribers]}}; handle_call({subscribe,Faulty},_From,LD) -> @@ -131,7 +131,7 @@ handle_call({change_options,Nodes,Opts},_From,LD) -> end; handle_call({init_tracing,TracerDataList},_From,LD) -> {reply,adapt_reply(LD,do_init_tracing(TracerDataList,LD)),LD}; -handle_call({init_tracing,Nodes,TracerData},_From,LD) when list(Nodes) -> +handle_call({init_tracing,Nodes,TracerData},_From,LD) when is_list(Nodes) -> TracerDataList= lists:map(fun(N)->{N,TracerData} end,started_trace_nodes(Nodes,LD)), {reply,adapt_reply(LD,do_init_tracing(TracerDataList,LD)),LD}; @@ -173,7 +173,7 @@ handle_call({fetch_log,ToNode,Spec,Dest,Prefix},From,LD) -> end; handle_call({delete_log,NodesOrNodeSpecs},_From,LD) -> {reply,adapt_reply(LD,do_delete_log(NodesOrNodeSpecs,LD)),LD}; -handle_call({delete_log,Nodes,Specs},_From,LD) when list(Nodes) -> +handle_call({delete_log,Nodes,Specs},_From,LD) when is_list(Nodes) -> Reply=do_delete_log(lists:map(fun(N)->{N,Specs} end,Nodes),LD), {reply,adapt_reply(LD,Reply),LD}; handle_call({delete_log,FaultyNodes,_Specs},_From,LD) -> @@ -283,7 +283,7 @@ do_change_option(Nodes,Options,LD) -> do_change_option_2([Node|Tail],Options,LD,Replies) -> case get_node_rec(Node,LD) of - Rec when record(Rec,node) -> + Rec when is_record(Rec,node) -> Answer=?RUNTIME:change_options(Rec#node.pid,Options), do_change_option_2(Tail,Options,LD,[{Node,Answer}|Replies]); Error -> @@ -333,7 +333,7 @@ do_init_tracing_2(What,_LD,_) -> %% Returns {ok,Reply} or {error,Reason}. distribute_tp(all,Patterns,FlagList,LD) -> distribute_tp(started_trace_nodes(all,LD),Patterns,FlagList,LD); -distribute_tp(Nodes,Patterns,FlagList,LD) when list(Nodes) -> +distribute_tp(Nodes,Patterns,FlagList,LD) when is_list(Nodes) -> RTpids=lists:map(fun(N)->case get_node_rec(N,LD) of #node{pid=Pid} -> {Pid,N}; @@ -354,7 +354,7 @@ distribute_tp(Faulty,_,_,_) -> %% Returns {ok,Reply} or {error,Reason}. distribute_tf(all,Args,How,LD) -> distribute_tf(started_trace_nodes(all,LD),Args,How,LD); -distribute_tf(Nodes,Args,How,LD) when list(Nodes) -> +distribute_tf(Nodes,Args,How,LD) when is_list(Nodes) -> RTpids=lists:map(fun(Node)-> case get_node_rec(Node,LD) of #node{pid=Pid} -> @@ -369,7 +369,7 @@ distribute_tf(Faulty,_,_,_) -> {error,{badarg,Faulty}}. %% As above but specific args for each node. -distribute_tf(NodeArgs,How,LD) when list(NodeArgs) -> +distribute_tf(NodeArgs,How,LD) when is_list(NodeArgs) -> RTpidArgs=lists:map(fun({Node,Args})-> case get_node_rec(Node,LD) of #node{pid=Pid} -> @@ -384,7 +384,7 @@ distribute_tf(Faulty,_,_) -> {error,{badarg,Faulty}}. %% As above but both specific args for each node and How (set or remove flag). -distribute_tf(NodeArgHows,LD) when list(NodeArgHows) -> +distribute_tf(NodeArgHows,LD) when is_list(NodeArgHows) -> RTpidArgHows= lists:map(fun({Node,Args,How}) -> case get_node_rec(Node,LD) of @@ -405,7 +405,7 @@ distribute_tf(Faulty,_) -> %% Returns {ok,Reply} or {error,Reason}. distribute_metapattern(all,Args,LD) -> distribute_metapattern(started_trace_nodes(all,LD),Args,LD); -distribute_metapattern(Nodes,Args,LD) when list(Nodes) -> +distribute_metapattern(Nodes,Args,LD) when is_list(Nodes) -> RTpids=lists:map(fun(N)->case get_node_rec(N,LD) of #node{pid=Pid} -> {Pid,N}; @@ -480,7 +480,7 @@ do_cancel_suspension_2(Faulty,_,_) -> %% Return {ok,Reply} or {error,Reason}. do_stop_tracing(all,LD) -> do_stop_tracing(started_trace_nodes(all,LD),LD); -do_stop_tracing(Nodes,LD) when list(Nodes) -> +do_stop_tracing(Nodes,LD) when is_list(Nodes) -> RTpids=lists:map(fun(N)->case get_node_rec(N,LD) of #node{pid=Pid} -> {Pid,N}; @@ -580,7 +580,7 @@ do_list_logs_2(Other,_LD,_Replies) -> %% proper strings. do_fetch_log(ToNode,all,Dest,Prefix,From,LD) -> do_fetch_log(ToNode,started_trace_nodes(all,LD),Dest,Prefix,From,LD); -do_fetch_log(ToNode,Specs,Dest,Prefix,From,LD) when list(Dest),list(Prefix) -> +do_fetch_log(ToNode,Specs,Dest,Prefix,From,LD) when is_list(Dest),is_list(Prefix) -> CollectPid=spawn_link(ToNode,?MODULE,log_rec_init,[self(),Dest,Prefix,From]), do_fetch_log_2(Specs,LD,CollectPid,[],[]); do_fetch_log(_ToNode,_Specs,Dest,Prefix,From,_LD) -> @@ -639,7 +639,7 @@ do_delete_log(NodeSpecs,LD) -> LD,[]); false -> if - list(NodeSpecs),list(hd(NodeSpecs)) -> % A list of files. + is_list(NodeSpecs),is_list(hd(NodeSpecs)) -> % A list of files. do_delete_log_2(lists:map(fun(N)->{N,NodeSpecs} end, started_trace_nodes(all,LD)), LD,[]); @@ -785,9 +785,9 @@ check_options(Options, Context) -> check_options_2([],_Context,Result) -> {ok,Result}; -check_options_2([{subscribe,Pid}|OptionsTail],start,Result) when pid(Pid) -> +check_options_2([{subscribe,Pid}|OptionsTail],start,Result) when is_pid(Pid) -> check_options_2(OptionsTail,start,[{subscribe,Pid}|Result]); -check_options_2([{unsubscribe,Pid}|OptionsTail],start,Result) when pid(Pid) -> +check_options_2([{unsubscribe,Pid}|OptionsTail],start,Result) when is_pid(Pid) -> check_options_2(OptionsTail,start,[{unsubscribe,Pid}|Result]); check_options_2([{dependency,How}|OptionsTail],Context,Result) -> check_options_2(OptionsTail,Context,[{dependency,How}|Result]); @@ -819,12 +819,12 @@ initiate_state(Options) -> LD1#state{distributed=true} end. -initiate_state_2([{subscribe,Proc}|Tail],LD) when pid(Proc);atom(Proc)-> +initiate_state_2([{subscribe,Proc}|Tail],LD) when is_pid(Proc);is_atom(Proc)-> MRef=erlang:monitor(process,Proc), initiate_state_2(Tail,LD#state{subscribers=[{Proc,MRef}|LD#state.subscribers]}); -initiate_state_2([Opt|Tail],LD) when tuple(Opt),size(Opt)>=1 -> +initiate_state_2([Opt|Tail],LD) when is_tuple(Opt),size(Opt)>=1 -> initiate_state_2(Tail,initiate_state_3(element(1,Opt),Opt,LD)); -initiate_state_2([Opt|Tail],LD) when atom(Opt) -> +initiate_state_2([Opt|Tail],LD) when is_atom(Opt) -> initiate_state_2(Tail,initiate_state_3(Opt,Opt,LD)); initiate_state_2([_|Tail],LD) -> initiate_state_2(Tail,LD); @@ -853,9 +853,9 @@ initiate_state_is_rt_option(_) -> false. %% or more values associated with the Parameter, or just an atom. merge_options([], Options) -> Options; -merge_options([T|DefaultTail],Options) when tuple(T),size(T)>=1 -> +merge_options([T|DefaultTail],Options) when is_tuple(T),size(T)>=1 -> merge_options(DefaultTail,merge_options_2(element(1,T),T,Options)); -merge_options([Param|DefaultTail],Options) when atom(Param) -> +merge_options([Param|DefaultTail],Options) when is_atom(Param) -> merge_options(DefaultTail,merge_options_2(Param,Param,Options)); merge_options([_|DefaultTail],Options) -> % Strange, bad default option! merge_options(DefaultTail,Options). @@ -868,7 +868,7 @@ merge_options_2(Param,Opt,Options) -> [Opt|Options] end. -merge_options_find(Param,[T|_]) when tuple(T),element(1,T)==Param -> +merge_options_find(Param,[T|_]) when is_tuple(T),element(1,T)==Param -> true; merge_options_find(Param,[Param|_]) -> true; @@ -883,7 +883,7 @@ merge_options_find(_,[]) -> %% It also checks the formatting of the tracerdata since runtime components %% does not accept too badly formatted tracerdata. %% Returns {ok,NewTraceData} or {error,Reason}. -check_modify_tracerdata(TracerData,LoopData) when list(TracerData) -> +check_modify_tracerdata(TracerData,LoopData) when is_list(TracerData) -> case lists:keysearch(trace,1,TracerData) of {value,{_,TraceTD}} -> % Examine the trace part. case check_modify_tracerdata(TraceTD,LoopData) of @@ -908,7 +908,7 @@ check_modify_tracerdata({relayer,Collector},LoopData) when is_atom(Collector) -> end; check_modify_tracerdata({Type,Data},_LoopData) when Type==ip;Type==file -> {ok,{Type,Data}}; -check_modify_tracerdata({Handler,Data},_LoopData) when function(Handler) -> +check_modify_tracerdata({Handler,Data},_LoopData) when is_function(Handler) -> {ok,{Handler,Data}}; check_modify_tracerdata(Data,_LoopData) -> {error,{bad_tracerdata,Data}}. @@ -999,7 +999,7 @@ set_node_rec_2(Rec,[NodeRec|Tail]) -> %% Help function finding a node record for Node in a list of #node or in loopdata. %% Returns the #node in question or {error,not_an_added_node}. -get_node_rec(Node,NodeList) when list(NodeList) -> +get_node_rec(Node,NodeList) when is_list(NodeList) -> get_node_rec_2(Node,NodeList); get_node_rec(Node,#state{nodes=NodeList}) -> get_node_rec_2(Node,NodeList). @@ -1016,7 +1016,7 @@ get_node_rec_2(Node,[_NodeRec|Tail]) -> %% structure. Returns a new list of #node or a new loopdata structure. delete_node_rec(Node,LD=#state{nodes=NodeList}) -> LD#state{nodes=delete_node_rec_2(Node,NodeList)}; -delete_node_rec(Node,NodeList) when list(NodeList) -> +delete_node_rec(Node,NodeList) when is_list(NodeList) -> delete_node_rec_2(Node,NodeList). delete_node_rec_2(_,[]) -> @@ -1059,7 +1059,7 @@ log_rec_init(Parent,Dest,Prefix,From={ClientPid,_}) -> Fetchers), CMRef=erlang:monitor(process,ClientPid), % Monitor the client. case log_rec_loop(Dest,Prefix,RTs,InitialReplies,CMRef) of - Reply when list(Reply) -> % It is an ok value. + Reply when is_list(Reply) -> % It is an ok value. gen_server:reply(From,{ok,Reply}); {error,Reason} -> gen_server:reply(From,{error,Reason}); diff --git a/lib/inviso/src/inviso_lfm.erl b/lib/inviso/src/inviso_lfm.erl index 362176c776..085048518c 100644 --- a/lib/inviso/src/inviso_lfm.erl +++ b/lib/inviso/src/inviso_lfm.erl @@ -87,13 +87,13 @@ %% close the outfile.
%%
%% Using merge/2 assumes you want to use default handlers writing to a file.
-merge(Files,OutputFile) when list(OutputFile) ->
+merge(Files,OutputFile) when is_list(OutputFile) -> merge(Files,fun outfile_opener/1,fun outfile_writer/4,fun outfile_closer/1,OutputFile,off).
-merge(Files,WorkHandlerFun,HandlerData) when function(WorkHandlerFun) ->
+merge(Files,WorkHandlerFun,HandlerData) when is_function(WorkHandlerFun) -> merge(Files,void,WorkHandlerFun,void,HandlerData,off);
-merge(Files,OutputFile,Dbg) when list(OutputFile) ->
+merge(Files,OutputFile,Dbg) when is_list(OutputFile) -> merge(Files,fun outfile_opener/1,fun outfile_writer/4,fun outfile_closer/1,OutputFile,Dbg).
-merge(Files,WorkHandlerFun,HandlerData,Dbg) when function(WorkHandlerFun) ->
+merge(Files,WorkHandlerFun,HandlerData,Dbg) when is_function(WorkHandlerFun) -> merge(Files,void,WorkHandlerFun,void,HandlerData,Dbg).
merge(Files,BeginHandlerFun,WorkHandlerFun,EndHandlerFun,HandlerData) ->
merge(Files,BeginHandlerFun,WorkHandlerFun,EndHandlerFun,HandlerData,off).
@@ -123,7 +123,7 @@ init_receiver(From,Files,BeginHandlerFun,WorkHandlerFun,EndHandlerFun,HandlerDat {ok,Readers} ->
process_flag(trap_exit,true),
if
- function(BeginHandlerFun) ->
+ is_function(BeginHandlerFun) -> case catch BeginHandlerFun(HandlerData) of
{ok,NewHandlerData} ->
init_receiver_2(From,WorkHandlerFun,EndHandlerFun,
@@ -145,7 +145,7 @@ init_receiver_2(From,WorkHandlerFun,EndHandlerFun,HandlerData,Dbg,Readers) -> {Reply,NewHandlerData}=
loop(From,WorkHandlerFun,HandlerData,NewReaders,EntryStruct,Dbg,0),
if
- function(EndHandlerFun) ->
+ is_function(EndHandlerFun) -> case EndHandlerFun(NewHandlerData) of
ok ->
From ! {reply,self(),Reply};
@@ -207,7 +207,7 @@ find_oldest_entry(EntryStruct) -> case list_all_entries(EntryStruct) of
[] -> % The we are done!
done;
- EntryList when list(EntryList) -> % Find smallest timestamp in here then.
+ EntryList when is_list(EntryList) -> % Find smallest timestamp in here then. {Pid,Node,PidMappings,_TS,Term}=
lists:foldl(fun({P,N,PMap,TS1,T},{_P,_N,_PMap,TS0,_T}) when TS1<TS0 ->
{P,N,PMap,TS1,T};
diff --git a/lib/inviso/src/inviso_lfm_tpfreader.erl b/lib/inviso/src/inviso_lfm_tpfreader.erl index d0db4b6d02..6de4d11fe0 100644 --- a/lib/inviso/src/inviso_lfm_tpfreader.erl +++ b/lib/inviso/src/inviso_lfm_tpfreader.erl @@ -60,9 +60,9 @@ %% File=string()
%% Spawn on this function to start a reader process for trace-port generated
%% logfiles, possibly with inviso-generated ti-files.
-init(RecPid,LogFiles=[Tuple|_]) when tuple(Tuple) -> % Only one LogFiles.
+init(RecPid,LogFiles=[Tuple|_]) when is_tuple(Tuple) -> % Only one LogFiles. init(RecPid,[LogFiles]);
-init(RecPid,FileStruct) when list(FileStruct) ->
+init(RecPid,FileStruct) when is_list(FileStruct) -> logfiles_loop(RecPid,FileStruct).
%% -----------------------------------------------------------------------------
@@ -135,7 +135,7 @@ read_traceport_file(FileName,FD) -> case file:read(FD,5) of % Trace-port file entries start with 5 bytes.
{ok,<<0,Size:32>>} -> % Each entry in a traceport file begins.
case file:read(FD,Size) of
- {ok,Bin} when binary(Bin),size(Bin)=:=Size ->
+ {ok,Bin} when is_binary(Bin),size(Bin)=:=Size -> try binary_to_term(Bin) of
Term -> % Bin was a properly formatted term!
{ok,Term}
@@ -244,7 +244,7 @@ find_timestamp_in_term(_) -> % Don't know if there is a timestamp %% (1) plain straight raw binary files.
handle_ti_file(FileStruct) ->
case lists:keysearch(ti_log,1,FileStruct) of
- {value,{_,[FileName]}} when list(FileName) -> % There is one ti-file in this set.
+ {value,{_,[FileName]}} when is_list(FileName) -> % There is one ti-file in this set. case file:open(FileName,[read,raw,binary]) of
{ok,FD} ->
TIdAlias=ets:new(list_to_atom("inviso_ti_atab_"++pid_to_list(self())),
@@ -308,9 +308,9 @@ handle_ti_file_2(FD,TIdAlias,TIdUnalias) -> handle_logfiles(FileStruct) ->
handle_logfiles_2(lists:keysearch(trace_log,1,FileStruct)).
-handle_logfiles_2({value,{_,[FileName]}}) when list(FileName)-> % One single plain file.
+handle_logfiles_2({value,{_,[FileName]}}) when is_list(FileName)-> % One single plain file. [FileName];
-handle_logfiles_2({value,{_,Files}}) when list(Files) -> % A wrap-set.
+handle_logfiles_2({value,{_,Files}}) when is_list(Files) -> % A wrap-set. handle_logfile_sort_wrapset(Files);
handle_logfiles_2(_) ->
[]. % Pretend there were no files otherwise.
diff --git a/lib/inviso/src/inviso_tool.erl b/lib/inviso/src/inviso_tool.erl index 7126ba4387..05158f58fe 100644 --- a/lib/inviso/src/inviso_tool.erl +++ b/lib/inviso/src/inviso_tool.erl @@ -267,9 +267,9 @@ stop(UntouchedNodes) -> %% tracing.
reconnect_nodes() ->
gen_server:call(?MODULE,{reconnect_nodes,local_runtime},?CALL_TIMEOUT).
-reconnect_nodes(Node) when atom(Node) ->
+reconnect_nodes(Node) when is_atom(Node) -> reconnect_nodes([Node]);
-reconnect_nodes(Nodes) when list(Nodes) ->
+reconnect_nodes(Nodes) when is_list(Nodes) -> gen_server:call(?MODULE,{reconnect_nodes,Nodes},?CALL_TIMEOUT).
%% -----------------------------------------------------------------------------
@@ -572,7 +572,7 @@ reactivator_reply(TPid,Counter) -> init(Config) ->
case fetch_configuration(Config) of % From conf-file and Config.
- {ok,LD} when record(LD,ld) ->
+ {ok,LD} when is_record(LD,ld) -> case start_inviso_at_c_node(LD) of
{ok,CPid} ->
LD2=start_runtime_components(LD),
@@ -650,7 +650,7 @@ start_runtime_components_2([],_,LD) -> start_runtime_components_mk_opts(Node,{M,F,Args}) ->
case catch apply(M,F,[Node|Args]) of
- {ok,Opts} when list(Opts) ->
+ {ok,Opts} when is_list(Opts) -> start_runtime_component_mk_opts_add_dependency(Opts);
_ ->
[?DEFAULT_DEPENDENCY]
@@ -698,7 +698,7 @@ handle_call({reconnect_nodes,Nodes},_From,LD) -> {reply,
build_reconnect_nodes_reply(Nodes,Nodes2,NodesErr,NewLD#ld.nodes),
NewLD};
- list(Nodes) ->
+ is_list(Nodes) -> {reply,
{ok,build_reconnect_nodes_reply(Nodes,Nodes2,NodesErr,NewLD#ld.nodes)},
NewLD}
@@ -711,7 +711,7 @@ handle_call({start_session,MoreTDGargs},_From,LD=#ld{session_state=SState}) -> case is_tracing(SState) of
false -> % No session running.
if
- list(MoreTDGargs) ->
+ is_list(MoreTDGargs) -> DateTime=calendar:universal_time(),
{M,F,Args}=LD#ld.tdg,
TDGargs=inviso_tool_lib:mk_tdg_args(DateTime,MoreTDGargs++Args),
@@ -757,15 +757,15 @@ handle_call({reinitiate_session,Nodes},_From,LD=#ld{session_state=SState}) -> end;
handle_call({restore_session,{FileName,MoreTDGargs}},_From,LD=#ld{chl=OldCHL})
- when list(MoreTDGargs) ->
+ when is_list(MoreTDGargs) -> case is_tracing(LD#ld.session_state) of
false ->
case catch make_absolute_path(FileName,LD#ld.dir) of
- AbsFileName when list(AbsFileName) ->
+ AbsFileName when is_list(AbsFileName) -> case file:read_file(AbsFileName) of
{ok,Bin} ->
if
- list(MoreTDGargs) ->
+ is_list(MoreTDGargs) -> case catch replace_history_chl(OldCHL,
binary_to_term(Bin)) of
{ok,CHL} -> % The file was well formatted.
@@ -803,7 +803,7 @@ handle_call({restore_session,MoreTDGargs},_From,LD=#ld{chl=CHL}) -> case history_exists_chl(CHL) of
true -> % There is a history to redo.
if
- list(MoreTDGargs) ->
+ is_list(MoreTDGargs) -> case h_restore_session(MoreTDGargs,LD) of
{ok,{SessionNr,ReturnVal,NewLD}} ->
{reply,
@@ -879,7 +879,7 @@ handle_call({sync_atc,{TC,Id,Vars,TimeOut}},_From,LD=#ld{session_state=SState}) case is_tracing(SState) of
true ->
if
- integer(TimeOut);TimeOut==infinity ->
+ is_integer(TimeOut);TimeOut==infinity -> case h_sync_atc(TC,Id,Vars,TimeOut,LD) of
{ok,NewLD,Result} ->
{reply,Result,NewLD};
@@ -897,7 +897,7 @@ handle_call({sync_rtc,{TC,Vars,TimeOut}},_From,LD=#ld{session_state=SState}) -> case is_tracing(SState) of
true ->
if
- integer(TimeOut);TimeOut==infinity ->
+ is_integer(TimeOut);TimeOut==infinity -> case h_sync_rtc(TC,Vars,TimeOut,LD) of
{ok,NewLD,Result} ->
{reply,Result,NewLD};
@@ -929,7 +929,7 @@ handle_call({sync_dtc,{TC,Id,TimeOut}},_From,LD=#ld{session_state=SState}) -> case is_tracing(SState) of % Check that we are tracing now.
true ->
if
- integer(TimeOut);TimeOut==infinity ->
+ is_integer(TimeOut);TimeOut==infinity -> case h_sync_dtc(TC,Id,TimeOut,LD) of
{ok,NewLD,Result} ->
{reply,Result,NewLD};
@@ -947,7 +947,7 @@ handle_call({inviso,{Cmd,Args}},_From,LD=#ld{session_state=SState}) -> case is_tracing(SState) of
true ->
if
- list(Args) ->
+ is_list(Args) -> case h_inviso(Cmd,Args,LD) of
{ok,{Reply,NewLD}} ->
{reply,Reply,NewLD};
@@ -1156,7 +1156,7 @@ h_reconnect_nodes(local_runtime,LD=#ld{nodes=NodesD}) -> % Non-distributed. _ -> % Allready connected!
{ok,{[],{error,already_connected},LD}}
end;
-h_reconnect_nodes(Nodes,LD=#ld{nodes=NodesD}) when list(Nodes) ->
+h_reconnect_nodes(Nodes,LD=#ld{nodes=NodesD}) when is_list(Nodes) -> {Nodes2,NodesErr}=
lists:foldl(fun(N,{Nodes2,NodesErr})->
case get_state_nodes(N,NodesD) of
@@ -1246,7 +1246,7 @@ h_start_session_ctp_all_2([],Errors,Nodes) -> %% Help function doing the actual init_tracing.
h_start_session_2(undefined,TracerData,_Errors) -> % Non distributed case.
case inviso:init_tracing(TracerData) of
- {ok,LogResult} when list(LogResult) ->
+ {ok,LogResult} when is_list(LogResult) -> {ok,{ok,LogResult}};
{error,already_initated} -> % Perhaps adopted!?
{ok,{error,already_initiated}}; % Not necessarily wrong.
@@ -1360,7 +1360,7 @@ h_reinitiate_session_2(local_runtime,NodesD,undefined) -> % Non distributed case _ ->
{ok,{[],{error,already_in_session}}}
end;
-h_reinitiate_session_2(Nodes,NodesD,CNode) when list(Nodes) ->
+h_reinitiate_session_2(Nodes,NodesD,CNode) when is_list(Nodes) -> {ok,lists:foldl(fun(N,{Nodes2,NodesErr})->
case get_state_nodes(N,NodesD) of
{inactive,running} -> % Only ok case.
@@ -1515,7 +1515,7 @@ h_atc(TC,Id,Vars,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL},Nodes) -> case check_bindings(Vars,TraceCase) of
{ok,Bindings} -> % Necessary vars exists in Vars.
if
- list(Nodes) -> % Nodes predefined.
+ is_list(Nodes) -> % Nodes predefined. h_atc_2(TC,Id,CNode,CHL,LD,TraceCase,Bindings,Nodes);
true -> % Use all tracing and running nodes.
Nodes1=get_nodenames_running_nodes(LD#ld.nodes),
@@ -1769,13 +1769,13 @@ h_reactivate(Node,CNode) -> h_save_history(HDir,Dir,FileName,SortedLog) ->
Dir0=
if
- list(HDir) -> % There is a history dir specified.
+ is_list(HDir) -> % There is a history dir specified. HDir; % Use it then.
true ->
Dir % Else use the tool dir.
end,
case catch make_absolute_path(FileName,Dir0) of
- AbsFileName when list(AbsFileName) ->
+ AbsFileName when is_list(AbsFileName) -> Log2=build_saved_history_data(SortedLog), % Remove stopped tracecases.
case file:write_file(AbsFileName,term_to_binary(Log2)) of
ok ->
@@ -1801,7 +1801,7 @@ h_get_autostart_data(local_runtime,_,Dependency,ASD,M,F,TDGargs,OptsG) -> Opts=[Dependency|lists:keydelete(dependency,1,Opts0)],
{ok,{ASD,{ok,{Opts,{tdg,{M,F,CompleteTDGargs}}}}}};
-h_get_autostart_data(Nodes,CNode,Dependency,ASD,M,F,TDGargs,OptsG) when list(Nodes) ->
+h_get_autostart_data(Nodes,CNode,Dependency,ASD,M,F,TDGargs,OptsG) when is_list(Nodes) -> {ok,{ASD,h_get_autostart_data_2(Nodes,CNode,Dependency,M,F,TDGargs,OptsG)}};
h_get_autostart_data(Nodes,_CNode,_Dependency,_ASD,_M,_F,_TDGargs,_OptsG) ->
{error,{badarg,Nodes}}.
@@ -2144,14 +2144,14 @@ expand_module_regexps(Args,_RegExpNode,_Nodes,false) -> {ok,Args};
expand_module_regexps([PatternList],RegExpNode,Nodes,{tp,1,1}) ->
case catch expand_module_regexps_tp(PatternList,RegExpNode,Nodes) of
- NewPatternList when list(NewPatternList) ->
+ NewPatternList when is_list(NewPatternList) -> {ok,[NewPatternList]};
{error,Reason} ->
{error,Reason}
end;
expand_module_regexps([PatternList],RegExpNode,Nodes,{ctp,1,1}) ->
case catch expand_module_regexps_ctp(PatternList,RegExpNode,Nodes) of
- NewPatternList when list(NewPatternList) ->
+ NewPatternList when is_list(NewPatternList) -> {ok,[NewPatternList]};
{error,Reason} ->
{error,Reason}
@@ -2164,9 +2164,9 @@ expand_module_regexps([M,F,Arity],RegExpNode,Nodes,{ctp,3,1}) -> expand_module_regexps([[{M,F,Arity}]],RegExpNode,Nodes,{ctp,1,1}).
-expand_module_regexps_tp([E={M,_,_,_,_}|Rest],RegExpNode,Nodes) when atom(M) ->
+expand_module_regexps_tp([E={M,_,_,_,_}|Rest],RegExpNode,Nodes) when is_atom(M) -> [E|expand_module_regexps_tp(Rest,RegExpNode,Nodes)];
-expand_module_regexps_tp([{M,F,Arity,MS,Opts}|Rest],RegExpNode,Nodes) when list(M);tuple(M) ->
+expand_module_regexps_tp([{M,F,Arity,MS,Opts}|Rest],RegExpNode,Nodes) when is_list(M);is_tuple(M) -> case inviso_tool_lib:expand_module_names([RegExpNode],
M,
[{expand_only_at,RegExpNode}]) of
@@ -2193,9 +2193,9 @@ expand_module_regexps_tp_2([M|MRest],F,Arity,MS,Opts,Rest,RegExpNode,Nodes) -> expand_module_regexps_tp_2([],_,_,_,_,Rest,RegExpNode,Nodes) ->
expand_module_regexps_tp(Rest,RegExpNode,Nodes).
-expand_module_regexps_ctp([E={M,_,_}|Rest],RegExpNode,Nodes) when atom(M) ->
+expand_module_regexps_ctp([E={M,_,_}|Rest],RegExpNode,Nodes) when is_atom(M) -> [E|expand_module_regexps_ctp(Rest,RegExpNode,Nodes)];
-expand_module_regexps_ctp([{M,F,Arity}|Rest],RegExpNode,Nodes) when list(M);tuple(M) ->
+expand_module_regexps_ctp([{M,F,Arity}|Rest],RegExpNode,Nodes) when is_list(M);is_tuple(M) -> case inviso_tool_lib:expand_module_names([RegExpNode],
M,
[{expand_only_at,RegExpNode}]) of
@@ -2450,7 +2450,7 @@ fetch_configuration(Config) -> %% Returns {ok,FileName} or 'false'. The latter if no name could be determined.
fetch_config_filename(Config) ->
case catch lists:keysearch(config_file,1,Config) of
- {value,{_,FName}} when list(FName) ->
+ {value,{_,FName}} when is_list(FName) -> {ok,FName};
_ -> % No filename in the start argument.
fetch_config_filename_2()
@@ -2458,7 +2458,7 @@ fetch_config_filename(Config) -> fetch_config_filename_2() ->
case application:get_env(inviso_tool_config_file) of
- {ok,FName} when list(FName) ->
+ {ok,FName} when is_list(FName) -> {ok,FName};
_ -> % Application parameter not specified.
false % Means no config file will be used.
@@ -2499,14 +2499,14 @@ read_config_list_2(LD,Terms,Tag) -> %% Function updating a named field in a record. Returns a new record. Note that
%% this function must be maintained due the fact that field names are removed
%% at compile time.
-update_ld_record(LD,nodes,Value) when record(LD,ld) ->
+update_ld_record(LD,nodes,Value) when is_record(LD,ld) -> case mk_nodes(Value) of
{ok,NodesD} ->
LD#ld{nodes=NodesD};
error ->
LD
end;
-update_ld_record(LD,Tag,Value) when record(LD,ld) ->
+update_ld_record(LD,Tag,Value) when is_record(LD,ld) -> Index=
case Tag of
c_node -> % atom()
@@ -2546,7 +2546,7 @@ update_ld_record(LD,Tag,Value) when record(LD,ld) -> %% ActivationFileName=DeactivationFileName=string()
read_trace_case_definitions(LD) ->
case LD#ld.tc_def_file of
- TCfileName when list(TCfileName) ->
+ TCfileName when is_list(TCfileName) -> case catch file:consult(TCfileName) of
{ok,Terms} ->
Dir=LD#ld.dir, % The working directory of the tool.
@@ -2636,8 +2636,8 @@ get_status(CNode,Nodes) -> %% We can end up here if a session is stopped with this node suspended.
%% Returns a nodes database structure filled with the nodes Nodes.
-mk_nodes(Nodes) when list(Nodes) ->
- {ok,lists:map(fun(N) when atom(N)->{N,down} end,Nodes)};
+mk_nodes(Nodes) when is_list(Nodes) -> + {ok,lists:map(fun(N) when is_atom(N)->{N,down} end,Nodes)}; mk_nodes(local_runtime) -> % The non-distributed case.
down;
mk_nodes(_Nodes) ->
@@ -2783,7 +2783,7 @@ set_suspended_nodes(_,{up,{State,_}}) -> %% This function is called when reactivation is completed. Hence it moves the
%% node to no longer suspended. Note this can mean that the node is either
%% tracing or inactive. Reactivation is not allowed for a node have trace_failure.
-set_running_nodes(Node,NodesD) when list(NodesD) ->
+set_running_nodes(Node,NodesD) when is_list(NodesD) -> case lists:keysearch(Node,1,NodesD) of
{value,{_,AvailableStatus}} ->
lists:keyreplace(Node,1,NodesD,{Node,set_running_nodes_2(AvailableStatus)});
@@ -2902,7 +2902,7 @@ get_available_nodes([]) -> %% suspended or not.
%% Returns {State,Status} | reactivating | down
%% where
-get_state_nodes(Node,NodesD) when list(NodesD) ->
+get_state_nodes(Node,NodesD) when is_list(NodesD) -> case lists:keysearch(Node,1,NodesD) of
{value,{_,AvailableStatus}} ->
get_state_nodes_2(AvailableStatus);
diff --git a/lib/inviso/src/inviso_tool_lib.erl b/lib/inviso/src/inviso_tool_lib.erl index 20a8b509ae..7953acedd6 100644 --- a/lib/inviso/src/inviso_tool_lib.erl +++ b/lib/inviso/src/inviso_tool_lib.erl @@ -83,7 +83,7 @@ inviso_cmd(NodeName,Func,Args) -> %% In the non-distributed case the singlenode_expansion will be returned.
expand_module_names(_Nodes,Mod={_,'_'},_) ->
{error,{faulty_regexp_combination,Mod}};
-expand_module_names(Nodes,{DirStr,ModStr},Opts) when list(DirStr), list(ModStr) ->
+expand_module_names(Nodes,{DirStr,ModStr},Opts) when is_list(DirStr), is_list(ModStr) -> case expand_module_names_special_regexp(DirStr) of
{ok,NewDirStr} ->
case expand_module_names_special_regexp(ModStr) of
@@ -97,11 +97,11 @@ expand_module_names(Nodes,{DirStr,ModStr},Opts) when list(DirStr), list(ModStr) end;
expand_module_names(_,'_',_Opts) -> % If we want to trace all modules
wildcard; % we shall not expand it.
-expand_module_names(_Nodes,Mod,_Opts) when atom(Mod) ->
+expand_module_names(_Nodes,Mod,_Opts) when is_atom(Mod) -> module; % If it is an atom, no expansion.
expand_module_names(Nodes,"*",Opts) -> % Treat this as a reg.exp.
expand_module_names(Nodes,".*",Opts);
-expand_module_names(Nodes,ModStr,Opts) when list(ModStr) ->
+expand_module_names(Nodes,ModStr,Opts) when is_list(ModStr) -> case expand_module_names_special_regexp(ModStr) of
{ok,NewModStr} ->
expand_module_names_2(Nodes,NewModStr,Opts);
@@ -115,7 +115,7 @@ expand_module_names_2(Nodes,ModStr,Opts) -> case get_expand_regexp_at_opts(Opts) of
{ok,Node} -> % Expansion only at this node.
case inviso_rt_lib:expand_regexp([Node],ModStr,Opts) of
- [{Node,Modules}] when list(Modules) ->
+ [{Node,Modules}] when is_list(Modules) -> {singlenode_expansion,Modules};
[{Node,_}] -> % Most likely badrpc.
{error,{faulty_node,Node}}
@@ -130,7 +130,7 @@ expand_module_names_2(Nodes,DirStr,ModStr,Opts) -> case get_expand_regexp_at_opts(Opts) of
{ok,Node} -> % Expansion only at this node.
case inviso_rt_lib:expand_regexp([Node],DirStr,ModStr,Opts) of
- [{Node,Modules}] when list(Modules) ->
+ [{Node,Modules}] when is_list(Modules) -> {singlenode_expansion,Modules};
[{Node,_}] -> % Most likely badrpc.
{error,{faulty_node,Node}}
@@ -186,12 +186,12 @@ make_patterns(Catches,Opts,Dbg,NodeModsOrMods,F,A,MS) -> make_patterns_2(Catches,OwnArg,Dbg,NodeModsOrMods,F,A,MS)
end.
-make_patterns_2(Catches,OwnArg,Dbg,[{Node,Mods}|Rest],F,A,MS) when list(Mods) ->
+make_patterns_2(Catches,OwnArg,Dbg,[{Node,Mods}|Rest],F,A,MS) when is_list(Mods) -> TPs=make_patterns_3(Catches,OwnArg,Dbg,Mods,F,A,MS,[]),
[{Node,join_patterns(TPs)}|make_patterns_2(Catches,OwnArg,Dbg,Rest,F,A,MS)];
make_patterns_2(Catches,OwnArg,Dbg,[{_Node,_}|Rest],F,A,MS) -> % badrpc!?
make_patterns_2(Catches,OwnArg,Dbg,Rest,F,A,MS);
-make_patterns_2(Catches,OwnArg,Dbg,Modules,F,A,MS) when list(Modules) ->
+make_patterns_2(Catches,OwnArg,Dbg,Modules,F,A,MS) when is_list(Modules) -> TPs=make_patterns_3(Catches,OwnArg,Dbg,Modules,F,A,MS,[]),
join_patterns(TPs);
make_patterns_2(_,_,_,[],_,_,_) ->
@@ -330,7 +330,7 @@ get_datetime_from_tdg_args([DateTime|_]) -> %% Returns a list.
get_ownarg_opts(Opts) ->
case lists:keysearch(arg,1,Opts) of
- {value,{_,OwnArg}} when list(OwnArg) ->
+ {value,{_,OwnArg}} when is_list(OwnArg) -> OwnArg;
{value,{_,OwnArg}} ->
[OwnArg];
@@ -350,7 +350,7 @@ get_disable_safety_opts(Opts) -> get_expand_regexp_at_opts(Opts) ->
case lists:keysearch(expand_only_at,1,Opts) of
- {value,{_,Node}} when atom(Node) ->
+ {value,{_,Node}} when is_atom(Node) -> {ok,Node};
_ ->
false
diff --git a/lib/jinterface/java_src/Makefile b/lib/jinterface/java_src/Makefile index 37a57352ad..22c55328b8 100644 --- a/lib/jinterface/java_src/Makefile +++ b/lib/jinterface/java_src/Makefile @@ -35,7 +35,21 @@ VSN=$(JINTERFACE_VSN) SPECIAL_TARGETS = +TARGET_FILES= $(POM_TARGET) +SPECIAL_TARGETS = + +POM_FILE= pom.xml + +POM_TARGET= ../$(POM_FILE) +POM_SRC= $(POM_FILE).src + +# ---------------------------------------------------- +# Special Build Targets +# ---------------------------------------------------- + +$(POM_TARGET): $(POM_SRC) ../vsn.mk + sed -e 's;%VSN%;$(VSN);' $< > $@ # ---------------------------------------------------- # Default Subdir Targets @@ -43,7 +57,7 @@ SPECIAL_TARGETS = .PHONY: debug opt instr release docs release_docs tests release_tests clean depend -debug opt instr release docs release_docs tests release_tests clean depend: +debug opt instr release docs release_docs tests release_tests clean depend: $(TARGET_FILES) set -e; set -x; \ case "$(MAKE)" in *clearmake*) tflag="-T";; *) tflag="";; esac; \ if test -f com/ericsson/otp/erlang/ignore_config_record.inf; then xflag=$$tflag; fi; \ diff --git a/lib/jinterface/java_src/pom.xml.src b/lib/jinterface/java_src/pom.xml.src new file mode 100644 index 0000000000..cef49b735a --- /dev/null +++ b/lib/jinterface/java_src/pom.xml.src @@ -0,0 +1,106 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>org.erlang.otp</groupId> + <artifactId>jinterface</artifactId> + <packaging>jar</packaging> + <version>%VSN%</version> + <name>jinterface</name> + <description> + Jinterface Java package contains java classes, which help you integrate programs written in Java with Erlang. + Erlang is a programming language designed at the Ericsson Computer Science Laboratory. + </description> + <url>http://erlang.org/</url> + <licenses> + <license> + <name>ERLANG PUBLIC LICENSE 1.1</name> + <url>http://www.erlang.org/EPLICENSE</url> + <distribution>repo</distribution> + </license> + </licenses> + <scm> + <connection>git://github.com/erlang/otp.git</connection> + <developerConnection>git://github.com/erlang/otp.git</developerConnection> + <url>http://github.com/erlang/otp</url> + </scm> + <developers> + <developer> + <email>[email protected]</email> + </developer> + </developers> + <organization> + <name>Open Source Erlang</name> + <url>http://www.erlang.org/</url> + </organization> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <source>1.5</source> + <target>1.5</target> + </configuration> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <executions> + <execution> + <phase>generate-sources</phase> + <goals> + <goal>add-source</goal> + </goals> + <configuration> + <sources> + <source>java_src</source> + </sources> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + <distributionManagement> + <repository> + <id>ossrh</id> + <url>http://oss.sonatype.org/service/local/staging/deploy/maven2</url> + </repository> + <snapshotRepository> + <id>ossrh</id> + <url>http://oss.sonatype.org/content/repositories/snapshots</url> + </snapshotRepository> + </distributionManagement> + <profiles> + <profile> + <id>release-sign-artifacts</id> + <activation> + <property> + <name>performRelease</name> + <value>true</value> + </property> + </activation> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-gpg-plugin</artifactId> + <version>1.0-alpha-4</version> + <executions> + <execution> + <id>sign-artifacts</id> + <phase>verify</phase> + <goals> + <goal>sign</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> + </profile> + </profiles> + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> +</project> diff --git a/lib/kernel/doc/src/code.xml b/lib/kernel/doc/src/code.xml index 19e1d3221c..b8db509fa8 100644 --- a/lib/kernel/doc/src/code.xml +++ b/lib/kernel/doc/src/code.xml @@ -54,7 +54,7 @@ for and tries to load the module.</p> </item> </list> - <p>To prevent accidentaly reloading modules affecting the Erlang + <p>To prevent accidentally reloading modules affecting the Erlang runtime system itself, the <c>kernel</c>, <c>stdlib</c> and <c>compiler</c> directories are considered <em>sticky</em>. This means that the system issues a warning and rejects the request if diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml index a9ceac0bcf..2044b074ee 100644 --- a/lib/kernel/doc/src/file.xml +++ b/lib/kernel/doc/src/file.xml @@ -1229,7 +1229,7 @@ f.txt: {person, "kalle", 25}. </item> <tag><c>{no_translation, unicode, latin1}</c></tag> <item> - <p>The file is was opened with another <c>encoding</c> than <c>latin1</c> and the data on the file can not be translated to the byte-oriented data that this function returns.</p> + <p>The file was opened with another <c>encoding</c> than <c>latin1</c> and the data in the file can not be translated to the byte-oriented data that this function returns.</p> </item> </taglist> </desc> diff --git a/lib/kernel/doc/src/gen_sctp.xml b/lib/kernel/doc/src/gen_sctp.xml index 3a8011e28b..fb09092f1c 100644 --- a/lib/kernel/doc/src/gen_sctp.xml +++ b/lib/kernel/doc/src/gen_sctp.xml @@ -1173,7 +1173,7 @@ client_loop(S, Peer1, Port1, AssocId1, Peer2, Port2, AssocId2) -> <title>SEE ALSO</title> <p><seealso marker="inet">inet(3)</seealso>, <seealso marker="gen_tcp">gen_tcp(3)</seealso>, - <seealso marker="gen_udp">gen_upd(3)</seealso>, + <seealso marker="gen_udp">gen_udp(3)</seealso>, <url href="http://www.rfc-archive.org/getrfc.php?rfc=2960">RFC2960</url> (Stream Control Transmission Protocol), <url href="http://tools.ietf.org/html/draft-ietf-tsvwg-sctpsocket-13">Sockets API Extensions for SCTP.</url></p> <marker id="authors"></marker> diff --git a/lib/kernel/internal_doc/distribution_handshake.txt b/lib/kernel/internal_doc/distribution_handshake.txt index f64ebe0302..6a3ee22ed3 100644 --- a/lib/kernel/internal_doc/distribution_handshake.txt +++ b/lib/kernel/internal_doc/distribution_handshake.txt @@ -11,7 +11,7 @@ The TCP/IP distribution uses a handshake which expects a connection based protocol, i.e. the protocol does not include any authentication after the handshake procedure. -This is not entirelly safe, as it is vulnerable against takeover +This is not entirely safe, as it is vulnerable against takeover attacks, but it is a tradeoff between fair safety and performance. The cookies are never sent in cleartext and the handshake procedure @@ -23,7 +23,7 @@ random numbers. DEFINITIONS ----------- -A challenge is a 32 bit integer number in big endian. Below the function +A challenge is a 32 bit integer number in big endian order. Below the function gen_challenge() returns a random 32 bit integer used as a challenge. A digest is a (16 bytes) MD5 hash of [the Challenge (as text) concatenated @@ -46,19 +46,19 @@ The cookies are text strings that can be viewed as passwords. Every message in the handshake starts with a 16 bit big endian integer which contains the length of the message (not counting the two initial bytes). In erlang this corresponds to the gen_tcp option {packet, 2}. Note that after -the handshake, the distribution switches to 4 byte backet headers. +the handshake, the distribution switches to 4 byte packet headers. THE HANDSHAKE IN DETAIL ----------------------- -Imagine two nodes, node A, which initiates the handshake and node B, whitch +Imagine two nodes, node A, which initiates the handshake and node B, which accepts the connection. 1) connect/accept: A connects to B via TCP/IP and B accepts the connection. 2) send_name/receive_name: A sends an initial identification to B. B receives the message. The message looks -like this (every "square" beeing one byte and the packet header removed): +like this (every "square" being one byte and the packet header removed): +---+--------+--------+-----+-----+-----+-----+-----+-----+-...-+-----+ |'n'|Version0|Version1|Flag0|Flag1|Flag2|Flag3|Name0|Name1| ... |NameN| @@ -67,7 +67,7 @@ like this (every "square" beeing one byte and the packet header removed): The 'n' is just a message tag, Version0 & Version1 is the distribution version selected by node A, based on information from EPMD. (16 bit big endian) -Flag0 ... Flag3 is capability flags, the capabilities defined in dist.hrl. +Flag0 ... Flag3 are capability flags, the capabilities defined in dist.hrl. (32 bit big endian) Name0 ... NameN is the full nodename of A, as a string of bytes (the packet length denotes how long it is). @@ -91,9 +91,9 @@ alive: A connection to the node is already active, which either means This is the format of the status message: -+---+-------+-------+ ... +-------+ ++---+-------+-------+-...-+-------+ |'s'|Status0|Status1| ... |StatusN| -+---+-------+-------+ ... +-------+ ++---+-------+-------+-...-+-------+ 's' is the message tag Status0 ... StatusN is the status as a string (not terminated) @@ -111,35 +111,35 @@ initially sent from A to B, with the addition of a 32 bit challenge: +---+--------+--------+-----+-----+-----+-----+-----+-----+-----+-----+--- |'n'|Version0|Version1|Flag0|Flag1|Flag2|Flag3|Chal0|Chal1|Chal2|Chal3| -+---+--------+--------+-----+-----+-----+-----+-----+-----+---- +-----+--- ++---+--------+--------+-----+-----+-----+-----+-----+-----+-----+-----+--- ------+-----+-...-+-----+ Name0|Name1| ... |NameN| ------+-----+-... +-----+ -Where Chal0 ... Chal3 is the challenge as a 32 bit biog endian integer +Where Chal0 ... Chal3 is the challenge as a 32 bit big endian integer and the other fields are B's version, flags and full nodename. 5) send_challenge_reply/recv_challenge_reply: Now A has generated -a digest and it's own challenge. Those are sent together in a package +a digest and its own challenge. Those are sent together in a package to B: -+---+-----+-----+-----+-----+-----+-----+-----+-----+ -|'r'|Chal0|Chal1|Chal2|Chal3|Dige0|Dige1|Dige2|Dige3| -+---+-----+-----+-----+-----+-----+-----+---- +-----+ ++---+-----+-----+-----+-----+-----+-----+-----+-----+-...-+------+ +|'r'|Chal0|Chal1|Chal2|Chal3|Dige0|Dige1|Dige2|Dige3| ... |Dige15| ++---+-----+-----+-----+-----+-----+-----+-----+-----+-...-+------+ Where 'r' is the tag, Chal0 ... Chal3 is A's challenge for B to handle and -Dige0 ... Dige3 is the digest that A constructed from the challenge B sent +Dige0 ... Dige15 is the digest that A constructed from the challenge B sent in the previous step. 6) recv_challenge_ack/send_challenge_ack: B checks that the digest received from A is correct and generates a digest from the challenge received from A. The digest is then sent to A. The message looks like this: -+---+-----+-----+-----+-----+ -|'a'|Dige0|Dige1|Dige2|Dige3| -+---+-----+-----+---- +-----+ ++---+-----+-----+-----+-----+-...-+------+ +|'a'|Dige0|Dige1|Dige2|Dige3| ... |Dige15| ++---+-----+-----+-----+-----+-...-+------+ -Where 'a' is the tag and Dige0 ... Dige3 is the digest calculated by B +Where 'a' is the tag and Dige0 ... Dige15 is the digest calculated by B for A's challenge. 7) A checks the digest from B and the connection is up. @@ -206,7 +206,7 @@ Currently the following capability flags are defined: %% The node implements distributed process monitoring. -define(DFLAG_DIST_MONITOR,8). -%% The node uses separate tag for fun's (labmdas) in the distribution protocol. +%% The node uses separate tag for fun's (lambdas) in the distribution protocol. -define(DFLAG_FUN_TAGS,16). An R6 erlang node implements all of the above, while a C or Java node only diff --git a/lib/kernel/src/auth.erl b/lib/kernel/src/auth.erl index 7fe30ae828..5c7fe2421d 100644 --- a/lib/kernel/src/auth.erl +++ b/lib/kernel/src/auth.erl @@ -50,6 +50,8 @@ %% Exported functions %%---------------------------------------------------------------------- +-spec start_link() -> {'ok',pid()} | {'error', term()} | 'ignore'. + start_link() -> gen_server:start_link({local, auth}, auth, [], []). @@ -134,7 +136,9 @@ init([]) -> %% The net kernel will let all message to the auth server %% through as is --type calls() :: 'echo' | 'sync_cookie' | {'set_cookie', node(), term()}. +-type calls() :: 'echo' | 'sync_cookie' + | {'get_cookie', node()} + | {'set_cookie', node(), term()}. -spec handle_call(calls(), {pid(), term()}, state()) -> {'reply', 'hello' | 'true' | 'nocookie' | cookie(), state()}. diff --git a/lib/kernel/src/code.erl b/lib/kernel/src/code.erl index 42d4818f08..ec256d5806 100644 --- a/lib/kernel/src/code.erl +++ b/lib/kernel/src/code.erl @@ -304,6 +304,8 @@ do_start(Flags) -> true -> ok end, + % Quietly load the native code for all modules loaded so far. + catch load_native_code_for_all_loaded(), Ok2; Other -> Other @@ -496,3 +498,19 @@ has_ext(Ext, Extlen,File) -> to_path(X) -> filename:join(packages:split(X)). + +-spec load_native_code_for_all_loaded() -> ok. +load_native_code_for_all_loaded() -> + Architecture = erlang:system_info(hipe_architecture), + ChunkName = hipe_unified_loader:chunk_name(Architecture), + lists:foreach(fun({Module, BeamFilename}) -> + case code:is_module_native(Module) of + false -> + case beam_lib:chunks(BeamFilename, [ChunkName]) of + {ok,{_,[{_,Bin}]}} when is_binary(Bin) -> + load_native_partial(Module, Bin); + {error, beam_lib, _} -> ok + end; + true -> ok + end + end, all_loaded()). diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl index 08b6477c9d..c9437df258 100644 --- a/lib/kernel/test/code_SUITE.erl +++ b/lib/kernel/test/code_SUITE.erl @@ -31,7 +31,7 @@ where_is_file_cached/1, where_is_file_no_cache/1, purge_stacktrace/1, mult_lib_roots/1, bad_erl_libs/1, code_archive/1, code_archive2/1, on_load/1, - on_load_embedded/1, on_load_errors/1]). + on_load_embedded/1, on_load_errors/1, native_early_modules/1]). -export([init_per_testcase/2, fin_per_testcase/2, init_per_suite/1, end_per_suite/1, @@ -53,7 +53,7 @@ all(suite) -> where_is_file_no_cache, where_is_file_cached, purge_stacktrace, mult_lib_roots, bad_erl_libs, code_archive, code_archive2, on_load, on_load_embedded, - on_load_errors]. + on_load_errors, native_early_modules]. init_per_suite(Config) -> %% The compiler will no longer create a Beam file if @@ -1333,6 +1333,34 @@ do_on_load_error(ReturnValue) -> ?line {undef,[{on_load_error,main,[]}|_]} = Exit end. +native_early_modules(suite) -> []; +native_early_modules(doc) -> ["Test that the native code of early loaded modules is loaded"]; +native_early_modules(Config) when is_list(Config) -> + case erlang:system_info(hipe_architecture) of + undefined -> + {skip,"Native code support is not enabled"}; + Architecture -> + native_early_modules_1(Architecture) + end. + +native_early_modules_1(Architecture) -> + ?line {lists, ListsBinary, _ListsFilename} = code:get_object_code(lists), + ?line ChunkName = hipe_unified_loader:chunk_name(Architecture), + ?line NativeChunk = beam_lib:chunks(ListsBinary, [ChunkName]), + ?line IsHipeCompiled = case NativeChunk of + {ok,{_,[{_,Bin}]}} when is_binary(Bin) -> true; + {error, beam_lib, _} -> false + end, + case IsHipeCompiled of + false -> + {skip,"OTP apparently not configured with --enable-native-libs"}; + true -> + ?line true = lists:all(fun code:is_module_native/1, + [ets,file,filename,gb_sets,gb_trees, + hipe_unified_loader,lists,os,packages]), + ok + end. + %%----------------------------------------------------------------- %% error_logger handler. %% (Copied from stdlib/test/proc_lib_SUITE.erl.) diff --git a/lib/kernel/test/gen_udp_SUITE.erl b/lib/kernel/test/gen_udp_SUITE.erl index bd5685952e..fa1991872b 100644 --- a/lib/kernel/test/gen_udp_SUITE.erl +++ b/lib/kernel/test/gen_udp_SUITE.erl @@ -34,12 +34,12 @@ -export([send_to_closed/1, buffer_size/1, binary_passive_recv/1, bad_address/1, - read_packets/1, open_fd/1]). + read_packets/1, open_fd/1, connect/1]). all(suite) -> [send_to_closed, buffer_size, binary_passive_recv, bad_address, read_packets, - open_fd]. + open_fd, connect]. init_per_testcase(_Case, Config) -> ?line Dog=test_server:timetrap(?default_timeout), @@ -408,3 +408,20 @@ start_node(Name) -> stop_node(Node) -> ?t:stop_node(Node). + + +connect(suite) -> + []; +connect(doc) -> + ["Test that connect/3 has effect"]; +connect(Config) when is_list(Config) -> + Addr = {127,0,0,1}, + {ok,S1} = gen_udp:open(0), + {ok,P1} = inet:port(S1), + {ok,S2} = gen_udp:open(0), + ok = inet:setopts(S2, [{active,false}]), + ok = gen_udp:close(S1), + ok = gen_udp:connect(S2, Addr, P1), + ok = gen_udp:send(S2, <<16#deadbeef:32>>), + {error,econnrefused} = gen_udp:recv(S2, 0, 5), + ok. diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk index 7dd99b37dd..9a191f9aeb 100644 --- a/lib/kernel/vsn.mk +++ b/lib/kernel/vsn.mk @@ -17,4 +17,4 @@ # %CopyrightEnd% # -KERNEL_VSN = 2.14 +KERNEL_VSN = 2.14.1 diff --git a/lib/megaco/doc/src/megaco.xml b/lib/megaco/doc/src/megaco.xml index ae9e250965..b9bf414299 100644 --- a/lib/megaco/doc/src/megaco.xml +++ b/lib/megaco/doc/src/megaco.xml @@ -662,7 +662,7 @@ megaco_incr_timer() = #megaco_incr_timer{} <taglist> <tag><c><![CDATA[none]]></c></tag> <item> - <p>Do not segment outgoing reply messages. This is usefull when + <p>Do not segment outgoing reply messages. This is useful when either it is known that messages are never to large or that the transport protocol can handle such things on its own (e.g. TCP or SCTP).</p> @@ -1182,7 +1182,7 @@ megaco_incr_timer() = #megaco_incr_timer{} <taglist> <tag><c><![CDATA[none]]></c></tag> <item> - <p>Do not segment outgoing reply messages. This is usefull when + <p>Do not segment outgoing reply messages. This is useful when either it is known that messages are never to large or that the transport protocol can handle such things on its own (e.g. TCP or SCTP).</p> diff --git a/lib/megaco/doc/src/megaco_flex_scanner.xml b/lib/megaco/doc/src/megaco_flex_scanner.xml index eb206e5d13..18c40bb71a 100644 --- a/lib/megaco/doc/src/megaco_flex_scanner.xml +++ b/lib/megaco/doc/src/megaco_flex_scanner.xml @@ -128,7 +128,7 @@ megaco_version() = integer() >= 1 <v>Boolean = boolean()</v> </type> <desc> - <p>Checks if a port is a flex scanner port or not (usefull when + <p>Checks if a port is a flex scanner port or not (useful when if a port exits). </p> <marker id="scan"></marker> diff --git a/lib/megaco/doc/src/megaco_user.xml b/lib/megaco/doc/src/megaco_user.xml index 7332fa684d..7987ed3392 100644 --- a/lib/megaco/doc/src/megaco_user.xml +++ b/lib/megaco/doc/src/megaco_user.xml @@ -504,7 +504,7 @@ protocol_version() = integer() ]]></code> not, is also included in the <c><![CDATA[UserReply]]></c>. </p> <p>The <c><![CDATA[ReplyData]]></c> defaults to <c><![CDATA[megaco:lookup(ConnHandle, reply_data)]]></c>, - but may be explicitely overridden by a + but may be explicitly overridden by a <c><![CDATA[megaco:cast/3]]></c> option in order to forward info about the calling context of the originating process.</p> <p>At <c><![CDATA[success()]]></c>, the <c><![CDATA[UserReply]]></c> either contains:</p> diff --git a/lib/odbc/doc/src/databases.xml b/lib/odbc/doc/src/databases.xml index 9776736909..a6ba0e5245 100644 --- a/lib/odbc/doc/src/databases.xml +++ b/lib/odbc/doc/src/databases.xml @@ -258,7 +258,7 @@ when p >= 16 </cell> that contains more than one SQL query. For example, the following SQLServer-specific statement creates a procedure that returns a result set containing information about employees - that work at the department and and a result set listing the + that work at the department and a result set listing the customers of that department. </p> <code type="none"> CREATE PROCEDURE DepartmentInfo (@DepartmentID INT) AS diff --git a/lib/reltool/doc/src/reltool.xml b/lib/reltool/doc/src/reltool.xml index 0c2b7d2a2b..598594145a 100644 --- a/lib/reltool/doc/src/reltool.xml +++ b/lib/reltool/doc/src/reltool.xml @@ -48,8 +48,8 @@ <c>root_dir</c> is the root directory of the analysed system and it defaults to the system executing <c>reltool</c>. Applications may also be located outside <c>root_dir</c>. <c>lib_dirs</c> - defines additional library directories where applications - additional may reside and it defaults to the the directories + defines library directories where additional applications + may reside and it defaults to the directories listed by the operating system environment variable <c>ERL_LIBS</c>. See the module <c>code</c> for more info. Finally single modules and entire applications may be read from @@ -58,10 +58,10 @@ <p>Some configuration parameters control the behavior of Reltool on system (<c>sys</c>) level. Others provide control on application (<c>app</c>) level and yet others are on module - (<c>mod</c>) level. Module level parameters overrides application - level parameters and application level parameters overrides system + (<c>mod</c>) level. Module level parameters override application + level parameters and application level parameters override system level parameters. Escript <c>escript</c> level parameters - overrides system level parameters.</p> + override system level parameters.</p> <p>The following top level <c>options</c> are supported:</p> @@ -74,7 +74,7 @@ a name of a <c>file</c> containing a sys tuple.</p> </item> - <tag><c>trap_exit></c></tag> + <tag><c>trap_exit</c></tag> <item> <p>This option controls the error handling behavior of <c>reltool</c>. By default the window processes traps @@ -123,10 +123,10 @@ defaults to <c>all</c> which means that if an application is included (either explicitly or implicitly) all modules in that application will be included. This implies that both modules - that exists on the <c>ebin</c> directory of the application, + that exist in the <c>ebin</c> directory of the application, as well as modules that are named in the <c>app</c> file will be included. If the parameter is set to <c>ebin</c>, both - modules on the <c>ebin</c> directory and derived modules are + modules in the <c>ebin</c> directory and derived modules are included. If the parameter is set to <c>app</c>, both modules in the <c>app</c> file and derived modules are included. <c>derived</c> means that only modules that are used by other @@ -138,13 +138,13 @@ <item> <p>This parameter controls the application and escript inclusion policy. It defaults to <c>derived</c> which means - that the applications that not have any explicit + that the applications that do not have any explicit <c>incl_cond</c> setting, will only be included if any other (explicitly or implicitly included) application uses it. The value <c>include</c> implies that all applications and - escripts that that not have any explicit <c>incl_cond</c> + escripts that do not have any explicit <c>incl_cond</c> setting will be included. <c>exclude</c> implies that all - applications and escripts) that that not have any explicit + applications and escripts) that do not have any explicit <c>incl_cond</c> setting will be excluded.</p> </item> @@ -158,7 +158,7 @@ <tag><c>rel</c></tag> <item> <p>Release specific configuration. Each release maps to a - <c>rel</c>, <c>script</c> and <c>boot </c> file. See the + <c>rel</c>, <c>script</c> and <c>boot</c> file. See the module <c>systools</c> for more info about the details. Each release has a name, a version and a set of applications with a few release specific parameters such as type and included @@ -168,7 +168,7 @@ <tag><c>relocatable</c></tag> <item> <p>This parameter controls whether the <c>erl</c> executable - in the target system automatically should determine where it + in the target system should automatically determine where it is installed or if it should use a hardcoded path to the installation. In the latter case the target system must be installed with <c>reltool:install/2</c> before it can be @@ -182,7 +182,7 @@ <p>The creation of the specification for a target system is performed in two steps. In the first step a complete specification is generated. It will likely contain much more - files than you are interested in your customized target + files than you are interested in in your customized target system. In the second step the specification will be filtered according to your filters. There you have the ability to specify filters per application as well as system wide @@ -224,12 +224,12 @@ <tag><c>incl_sys_filters</c></tag> <item> <p>This parameter normally contains a list of regular - expressions that controls which files in the system that + expressions that controls which files in the system should be included. Each file in the target system must match at least one of the listed regular expressions in order to be included. Further the files may not match any filter in <c>excl_sys_filters</c> in order to be included. Which - application files that should be included are controlled with + application files should be included is controlled with the parameters <c>incl_app_filters</c> and <c>excl_app_filters</c>. This parameter defaults to <c>[".*"]</c>.</p> @@ -238,8 +238,8 @@ <tag><c>excl_sys_filters</c></tag> <item> <p>This parameter normally contains a list of regular - expressions that controls which files in the system that not - should be included in the target system. In order to be + expressions that controls which files in the system should + not be included in the target system. In order to be included, a file must match some filter in <c>incl_sys_filters</c> but not any filter in <c>excl_sys_filters</c>. This parameter defaults to @@ -260,7 +260,7 @@ <item> <p>This parameter normally contains a list of regular expressions that controls which application specific files - that not should be included in the target system. In order to + should not be included in the target system. In order to be included, a file must match some filter in <c>incl_app_filters</c> but not any filter in <c>excl_app_filters</c>. This parameter defaults to @@ -271,8 +271,8 @@ <item> <p>This parameter normally contains a list of regular expressions that controls which top level directories in an - application that should be included in an archive file (as - opposed of beeing included as a regular directory outside the + application should be included in an archive file (as + opposed to being included as a regular directory outside the archive). Each top directory in the application must match at least one of the listed regular expressions in order to be included. Further the files may not match any filter in @@ -284,7 +284,7 @@ <item> <p>This parameter normally contains a list of regular expressions that controls which top level directories in an - application that not should be included in an archive file. In + application should not be included in an archive file. In order to be included in the application archive, a top directory must match some filter in <c>incl_archive_filters</c> but not any filter in <c>excl_archive_filters</c>. This @@ -295,15 +295,15 @@ <item> <p>This parameter contains a list of options that are given to <c>zip:create/3</c> when application specific files are - packaged into an archive. All options are not supported. The - most useful options in this context, are the ones that - controls which types of files that should be compressed. This + packaged into an archive. Only a subset of the options are + supported. The most useful options in this context are the ones + that control which types of files should be compressed. This parameter defaults to <c>[]</c>.</p> </item> </taglist> - <p>On application (<c>escript</c>) level,the following options are + <p>On application (<c>escript</c>) level, the following options are supported:</p> <taglist> @@ -314,7 +314,7 @@ </item> </taglist> - <p>On application (<c>app</c>) level,the following options are + <p>On application (<c>app</c>) level, the following options are supported:</p> <taglist> @@ -322,8 +322,8 @@ <item> <p>The version of the application. In an installed system there may exist several versions of an application. The <c>vsn</c> parameter - controls which version of the application that will be choosen. If it - is omitted, the latest version will be choosen.</p> + controls which version of the application will be chosen. If it + is omitted, the latest version will be chosen.</p> </item> <tag><c>mod</c></tag> <item> @@ -380,7 +380,7 @@ </item> </taglist> - <p>On module (<c>mod</c>) level,the following options are + <p>On module (<c>mod</c>) level, the following options are supported:</p> <taglist> @@ -391,9 +391,9 @@ will be used to control whether the module is included or not. The value of <c>incl_cond</c> overrides the module inclusion policy. <c>include</c> implies that the module is included, while - <c>exclude</c> implies that the module not is included. - <c>derived</c> implies that the is included if any included uses the - module.</p> + <c>exclude</c> implies that the module is not included. + <c>derived</c> implies that the module is included if it is used + by any other included module.</p> </item> <tag><c>debug_info</c></tag> <item> @@ -546,12 +546,12 @@ target_spec() = [target_spec()] files are by default copied to the target system. The <c>releases</c> directory contains generated <c>rel</c>, <c>script</c>, and <c>boot</c> files. The <c>lib</c> directory - contains the applications. Which applications that are included + contains the applications. Which applications are included and if they should be customized (archived, stripped from debug info etc.) is specified with various configuration parameters. The files in the <c>bin</c> directory are copied from the <c>erts-vsn/bin</c> directory, but only those files - that was originally included in <c>bin</c> directory of the + that were originally included in the <c>bin</c> directory of the source system.</p> <p>If the configuration parameter <c>relocatable</c> was set to @@ -584,10 +584,10 @@ target_spec() = [target_spec()] <v>Reason = reason()</v> </type> <desc><p>Get reltool configuration. Normally, only the explicit - configuration parameters with values that differs from their + configuration parameters with values that differ from their defaults are interesting. But the builtin default values can be returned by setting <c>InclDefaults</c> to <c>true</c>. The - derived configuration can be return by setting + derived configuration can be returned by setting <c>InclDerived</c> to <c>true</c>.</p></desc> </func> @@ -705,7 +705,7 @@ target_spec() = [target_spec()] <v>Reason = reason()</v> </type> <desc><p>Start a server process with options. The server process - identity can be given as argument to several other functions in the + identity can be given as an argument to several other functions in the API.</p></desc> </func> diff --git a/lib/reltool/doc/src/reltool_usage.xml b/lib/reltool/doc/src/reltool_usage.xml index 885828d1f0..0a053a014e 100644 --- a/lib/reltool/doc/src/reltool_usage.xml +++ b/lib/reltool/doc/src/reltool_usage.xml @@ -44,7 +44,7 @@ <section> <title>System window</title> <p>The system window is started with the function - <c>reltool:start/1</c>. At startup the tool will process the all + <c>reltool:start/1</c>. At startup the tool will process all <c>beam</c> files and <c>app</c> files in order to find out dependencies between applications and their modules. Once all this information has been derived, it will be possible to explore the tool.</p> @@ -67,29 +67,29 @@ <section> <title>Libraries</title> <p>On the library page it is possible to control which sources - that the tool will use. The page is organized as a tree which + the tool will use. The page is organized as a tree which can be expanded and collapsed by clicking on the little symbol in the beginning of the expandable/collapsible lines.</p> <p>The <c>Root directory</c> can be edited by selecting the line where the path of the root directory is displayed and - clicking with the right mouse button. Choose edit in the menu - that pops up. </p> + clicking the right mouse button. Choose edit in the menu + that pops up.</p> <p>Library directories can be added, edited or deleted. This is done by selecting the line where the path to a library - directory is displayed and clicking with the right mouse + directory is displayed and clicking the right mouse button. Choose add, edit or delete in the menu that pops up. New library directories can also be added by selecting the - line <c>Library directories</c> and clicking with the right + line <c>Library directories</c> and clicking the right mouse button. Choose add in the menu that pops up.</p> <p>Escript files can be added, edited or deleted. This is done by selecting the line where the path to an escript file is - displayed and clicking with the right mouse button. Choose + displayed and clicking the right mouse button. Choose add, edit or delete in the menu that pops up. New escripts can also be added by selecting the line <c>Escript files</c> and - clicking with the right mouse button. Choose add in the menu + clicking the right mouse button. Choose add in the menu that pops up.</p> <p>When libraries and escripts are expanded, the names of @@ -102,7 +102,7 @@ <p>On the system settings page it is possible to control some global settings that are used as defaults for all applications. Set the <c>Application inclusion policy</c> to - <c>include</c> to include all applications that not are + <c>include</c> to include all applications that are not explicitly excluded. See <c>incl_cond</c> (application inclusion) and <c>mod_cond</c> (module inclusion) in the reference manual for the module <c>reltool</c> for more @@ -131,14 +131,14 @@ <p>The symbols in front of the application names are intended to describe the status of the application. There are error - symbols and warning symbols that means that there are - something that needs attention. The tick symbol means that the + and warning symbols to signalize that there is + something which needs attention. The tick symbol means that the application is included or derived and no problem has been detected. The cross symbol means that the application is excluded or available and no problem has been detected. Applications with error symbols are listed first in - each category, then comes the warnings and the normal ones - (ticks and crosses) are found at the end.</p> + each category and are followed by the warnings and the + normal ones (ticks and crosses) at the end.</p> <p>Double click on an application to launch its application window.</p> @@ -175,8 +175,8 @@ </item> <item> <p><c>Save configuration</c> - Saves the current - configuration to file. Normally, only the explictit - configuration parameters with values that differs from their + configuration to file. Normally, only the explicit + configuration parameters with values that differ from their defaults are saved. But the configuration with or without default values and with or without derived values may also be saved.</p> @@ -204,21 +204,21 @@ <p>It is possible to perform some limited manipulations of the graph. Nodes can be moved, selected, locked or deleted. Move a single node or the entire graph by moving the mouse while the - left mouse button is pressed. A node is can be locked into a fix + left mouse button is pressed. A node can be locked into a fix position by holding down the shift button when the left mouse button is released. Select several nodes by moving the mouse - while the control key and the left mouse button i + while the control key and the left mouse button are pressed. Selected nodes can be locked, unlocked or deleted by - klicking on a suitable button.</p> + clicking on a suitable button.</p> <p>The algorithm that is used to draw a graph with as few crossed links as possible is called force graph. A force graph - consists of nodes and directed link between nodes. Each node is + consists of nodes and directed links between nodes. Each node is associated with a repulsive force that pushes nodes away from each other. This force can be adjusted with the left slider or with the mouse wheel. Each link is associated with an attractive - force that pulls the nodes nearer each other. This force can be - adjusted with the right slider. If this force becomes to strong, + force that pulls the nodes nearer to each other. This force can be + adjusted with the right slider. If this force becomes too strong, the graph will be unstable. The third parameter that can be adjusted is the length of the links. It is adjusted with the middle slider.</p> @@ -256,7 +256,7 @@ <p>Select version of the application in the <c>Source selection policy</c> part of the page. By default the latest version of the application is selected, but it is possible to override this by - explicitly select another version.</p> + explicitly selecting another version.</p> <p>By default the <c>Application inclusion policy</c> on system level is used for all applications. Set the value to @@ -272,10 +272,10 @@ want actually used modules to be included. Set it to <c>app</c> if you, besides derived modules, also want the modules listed in the app file to be included. Set it to <c>ebin</c> if you, besides - derived modules, also want the modules that exists as beam files - on the ebin directory to be included. Set it to <c>all</c> if you + derived modules, also want the modules that exist as beam files + in the ebin directory to be included. Set it to <c>all</c> if you want all modules to be included, that is the union of modules - found on the ebin directory and listed in the app file.</p> + found in the ebin directory and listed in the app file.</p> <p>The application settings page is rather incomplete.</p> </section> @@ -299,16 +299,16 @@ undone.</p> <p>The symbols in front of the module names are intended to - describe the status of the module. There are error symbols - and warning symbols that means that there are something that needs + describe the status of the module. There are error and + and warning symbols to signalize that there is something that needs attention. The tick symbol means that the module is included or derived and no problem has been detected. The cross symbol means that the module is excluded or available and no problem has been detected. Modules with error symbols are listed - first in each category, then comes the warnings and the normal - ones (ticks and crosses) are found at the end.</p> + first in each category and are followed by warnings and the + normal ones (ticks and crosses) at the end.</p> - <p>Double click on an module to launch its module window.</p> + <p>Double click on a module to launch its module window.</p> </section> @@ -323,7 +323,7 @@ <c>app</c> file. If the application includes other applications, these are listed under <c>Included</c>. These applications are listed in the <c>included_applications</c> part of the <c>app</c> - file. If the application uses modules other applications, these + file. If the application uses other applications, these are listed under <c>Uses</c>.</p> <p>Double click on an application name to launch an application window.</p> @@ -336,7 +336,7 @@ <p>There are two categories of modules on the <c>Module dependencies</c> page. If the module is used by other modules, these are listed under <c>Modules used by others</c>. If the - module uses modules other modules, these are listed under <c>Used + module uses other modules, these are listed under <c>Used modules</c>.</p> <p>Double click on an module name to launch a module window.</p> @@ -365,8 +365,8 @@ <p>There are two categories of modules on the <c>Dependencies</c> page. If the module is used by other modules, these are listed - under <c>Modules used by others</c>. If the module uses modules - other modules, these are listed under <c>Used modules</c>.</p> + under <c>Modules used by others</c>. If the module uses other + modules, these are listed under <c>Used modules</c>.</p> <p>Double click on an module name to launch a module window.</p> @@ -378,7 +378,7 @@ <p>On the <c>Code</c> page the Erlang source code is displayed. It is possible to search forwards and backwards for text in the module. Enter a regular expression in the <c>Find</c> field and - press enter. It is also possible to goto a certain line on the + press enter. It is also possible to go to a certain line in the module. The <c>Back</c> button can be used to go back to the previous position.</p> diff --git a/lib/snmp/doc/src/snmpa.xml b/lib/snmp/doc/src/snmpa.xml index 1be6abe6dd..f546724a78 100644 --- a/lib/snmp/doc/src/snmpa.xml +++ b/lib/snmp/doc/src/snmpa.xml @@ -1233,7 +1233,7 @@ snmp_agent:register_subagent(SA1,[1,2,3], SA2). </type> <desc> <p>Restart the worker process of a multi-threaded agent.</p> - <p>This is a utility function, that can be usefull when + <p>This is a utility function, that can be useful when e.g. debugging instrumentation functions.</p> <marker id="restart_set_worker"></marker> @@ -1249,7 +1249,7 @@ snmp_agent:register_subagent(SA1,[1,2,3], SA2). </type> <desc> <p>Restart the set worker process of a multi-threaded agent.</p> - <p>This is a utility function, that can be usefull when + <p>This is a utility function, that can be useful when e.g. debugging instrumentation functions.</p> <marker id="verbosity"></marker> diff --git a/lib/ssl/doc/src/Makefile b/lib/ssl/doc/src/Makefile index d6788c1633..3119d37af0 100644 --- a/lib/ssl/doc/src/Makefile +++ b/lib/ssl/doc/src/Makefile @@ -37,7 +37,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN) # Target Specs # ---------------------------------------------------- XML_APPLICATION_FILES = refman.xml -XML_REF3_FILES = ssl.xml old_ssl.xml +XML_REF3_FILES = ssl.xml old_ssl.xml ssl_session_cache_api.xml XML_REF6_FILES = ssl_app.xml XML_PART_FILES = release_notes.xml usersguide.xml @@ -45,9 +45,7 @@ XML_CHAPTER_FILES = \ ssl_protocol.xml \ using_ssl.xml \ pkix_certs.xml \ - create_certs.xml \ ssl_distribution.xml \ - licenses.xml \ notes.xml BOOK_FILES = book.xml diff --git a/lib/ssl/doc/src/book.xml b/lib/ssl/doc/src/book.xml index 9122addb74..85d6b56b26 100644 --- a/lib/ssl/doc/src/book.xml +++ b/lib/ssl/doc/src/book.xml @@ -28,9 +28,6 @@ <rev>A</rev> <file>book.sgml</file> </header> - <insidecover> - <include file="insidecover"></include> - </insidecover> <pagetext>SSL Application</pagetext> <preamble> <contents level="2"></contents> diff --git a/lib/ssl/doc/src/create_certs.xml b/lib/ssl/doc/src/create_certs.xml deleted file mode 100644 index 79cc8a0537..0000000000 --- a/lib/ssl/doc/src/create_certs.xml +++ /dev/null @@ -1,148 +0,0 @@ -<?xml version="1.0" encoding="latin1" ?> -<!DOCTYPE chapter SYSTEM "chapter.dtd"> - -<chapter> - <header> - <copyright> - <year>2003</year><year>2009</year> - <holder>Ericsson AB. All Rights Reserved.</holder> - </copyright> - <legalnotice> - The contents of this file are subject to the Erlang Public License, - Version 1.1, (the "License"); you may not use this file except in - compliance with the License. You should have received a copy of the - Erlang Public License along with this software. If not, it can be - retrieved online at http://www.erlang.org/. - - Software distributed under the License is distributed on an "AS IS" - basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See - the License for the specific language governing rights and limitations - under the License. - - </legalnotice> - - <title>Creating Certificates</title> - <prepared>UAB/F/P Peter Högfeldt</prepared> - <docno></docno> - <date>2003-06-16</date> - <rev>A</rev> - <file>create_certs.xml</file> - </header> - <p>Here we consider the creation of example certificates. - </p> - - <section> - <title>The openssl Command</title> - <p>The <c>openssl</c> command is a utility that comes with the - OpenSSL distribution. It provides a variety of subcommands. Each - subcommand is invoked as</p> - <code type="none"><![CDATA[ - openssl subcmd <options and arguments> ]]></code> - <p>where <c>subcmd</c> denotes the subcommand in question. - </p> - <p>We shall use the following subcommands to create certificates for - the purpose of testing Erlang/OTP SSL: - </p> - <list type="bulleted"> - <item><em>req</em> to create certificate requests and a - self-signed certificates, - </item> - <item><em>ca</em> to create certificates from certificate requests.</item> - </list> - <p>We create the following certificates: - </p> - <list type="bulleted"> - <item>the <em>erlangCA</em> root certificate (a self-signed - certificate), </item> - <item>the <em>otpCA</em> certificate signed by the <em>erlangCA</em>, </item> - <item>a client certificate signed by the <em>otpCA</em>, and</item> - <item>a server certificate signed by the <em>otpCA</em>.</item> - </list> - - <section> - <title>The openssl configuration file</title> - <p>An <c>openssl</c> configuration file consist of a number of - sections, where each section starts with one line containing - <c>[ section_name ]</c>, where <c>section_name</c> is the name - of the section. The first section of the file is either - unnamed, or is named <c>[ default ]</c>. For further details - see the OpenSSL config(5) manual page. - </p> - <p>The required sections for the subcommands we are going to - use are as follows: - </p> - <table> - <row> - <cell align="left" valign="middle">subcommand</cell> - <cell align="left" valign="middle">required/default section</cell> - <cell align="left" valign="middle">override command line option</cell> - <cell align="left" valign="middle">configuration file option</cell> - </row> - <row> - <cell align="left" valign="middle">req</cell> - <cell align="left" valign="middle">[req]</cell> - <cell align="left" valign="middle">-</cell> - <cell align="left" valign="middle"><c>-config FILE</c></cell> - </row> - <row> - <cell align="left" valign="middle">ca</cell> - <cell align="left" valign="middle">[ca]</cell> - <cell align="left" valign="middle"><c>-name section</c></cell> - <cell align="left" valign="middle"><c>-config FILE</c></cell> - </row> - <tcaption>openssl subcommands to use</tcaption> - </table> - </section> - - <section> - <title>Creating the Erlang root CA</title> - <p>The Erlang root CA is created with the command</p> - <code type="none"> - openssl req -new -x509 -config /some/path/req.cnf \\ - -keyout /some/path/key.pem -out /some/path/cert.pem </code> - <p>where the option <c>-new</c> indicates that we want to create - a new certificate request and the option <c>-x509</c> implies - that a self-signed certificate is created. - </p> - </section> - - <section> - <title>Creating the OTP CA</title> - <p>The OTP CA is created by first creating a certificate request - with the command</p> - <code type="none"> - openssl req -new -config /some/path/req.cnf \\ - -keyout /some/path/key.pem -out /some/path/req.pem </code> - <p>and the ask the Erlang CA to sign it:</p> - <code type="none"> - openssl ca -batch -notext -config /some/path/req.cnf \\ - -extensions ca_cert -in /some/path/req.pem -out /some/path/cert.pem </code> - <p>where the option <c>-extensions</c> refers to a section in the - configuration file saying that it should create a CA certificate, - and not a plain user certificate. - </p> - <p>The <c>client</c> and <c>server</c> certificates are created - similarly, except that the option <c>-extensions</c> then has the - value <c>user_cert</c>. - </p> - </section> - </section> - - <section> - <title>An Example</title> - <p>The following module <c>create_certs</c> is used by the Erlang/OTP - SSL application for generating certificates to be used in tests. The - source code is also found in <c>ssl-X.Y.Z/examples/certs/src</c>. - </p> - <p>The purpose of the <c>create_certs:all/1</c> function is to make - it possible to provide from the <c>erl</c> command line, the - full path name of the <c>openssl</c> command. - </p> - <p>Note that the module creates temporary OpenSSL configuration files - for the <c>req</c> and <c>ca</c> subcommands. - </p> - <codeinclude file="../../examples/certs/src/make_certs.erl" tag="" type="erl"></codeinclude> - </section> -</chapter> - - diff --git a/lib/ssl/doc/src/insidecover.xml b/lib/ssl/doc/src/insidecover.xml deleted file mode 100644 index 4f3f5e5951..0000000000 --- a/lib/ssl/doc/src/insidecover.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0" encoding="latin1" ?> -<!DOCTYPE bookinsidecover SYSTEM "bookinsidecover.dtd"> - -<bookinsidecover> -The Erlang/OTP SSL application includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/). Copyright (c) 1998-2002 The OpenSSL Project. All rights reserved. <br></br> -This product includes cryptographic software written by Eric Young ([email protected]). This product includes software written by Tim Hudson ([email protected]). Copyright (C) 1995-1998 Eric Young ([email protected]). All rights reserved. <br></br> -For further OpenSSL and SSLeay license information se the chapter <bold>Licenses</bold> -. <vfill></vfill> - <br></br> - <tt>http://www.erlang.org</tt> - <br></br> -</bookinsidecover> - - diff --git a/lib/ssl/doc/src/licenses.xml b/lib/ssl/doc/src/licenses.xml deleted file mode 100644 index 0969f9ad6e..0000000000 --- a/lib/ssl/doc/src/licenses.xml +++ /dev/null @@ -1,156 +0,0 @@ -<?xml version="1.0" encoding="latin1" ?> -<!DOCTYPE chapter SYSTEM "chapter.dtd"> - -<chapter> - <header> - <copyright> - <year>2003</year><year>2009</year> - <holder>Ericsson AB. All Rights Reserved.</holder> - </copyright> - <legalnotice> - The contents of this file are subject to the Erlang Public License, - Version 1.1, (the "License"); you may not use this file except in - compliance with the License. You should have received a copy of the - Erlang Public License along with this software. If not, it can be - retrieved online at http://www.erlang.org/. - - Software distributed under the License is distributed on an "AS IS" - basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See - the License for the specific language governing rights and limitations - under the License. - - </legalnotice> - - <title>Licenses</title> - <prepared>Peter Högfeldt</prepared> - <docno></docno> - <date>2003-05-26</date> - <rev>A</rev> - <file>licenses.xml</file> - </header> - <p> <marker id="licenses"></marker> -This chapter contains in extenso versions - of the OpenSSL and SSLeay licenses. - </p> - - <section> - <title>OpenSSL License</title> - <code type="none"> -/* ==================================================================== - * Copyright (c) 1998-2002 The OpenSSL Project. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. All advertising materials mentioning features or use of this - * software must display the following acknowledgment: - * "This product includes software developed by the OpenSSL Project - * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" - * - * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - * endorse or promote products derived from this software without - * prior written permission. For written permission, please contact - * [email protected]. - * - * 5. Products derived from this software may not be called "OpenSSL" - * nor may "OpenSSL" appear in their names without prior written - * permission of the OpenSSL Project. - * - * 6. Redistributions of any form whatsoever must retain the following - * acknowledgment: - * "This product includes software developed by the OpenSSL Project - * for use in the OpenSSL Toolkit (http://www.openssl.org/)" - * - * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY - * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - * ==================================================================== - * - * This product includes cryptographic software written by Eric Young - * ([email protected]). This product includes software written by Tim - * Hudson ([email protected]). - * - */ </code> - </section> - - <section> - <title>SSLeay License</title> - <code type="none"> -/* Copyright (C) 1995-1998 Eric Young ([email protected]) - * All rights reserved. - * - * This package is an SSL implementation written - * by Eric Young ([email protected]). - * The implementation was written so as to conform with Netscapes SSL. - * - * This library is free for commercial and non-commercial use as long as - * the following conditions are aheared to. The following conditions - * apply to all code found in this distribution, be it the RC4, RSA, - * lhash, DES, etc., code; not just the SSL code. The SSL documentation - * included with this distribution is covered by the same copyright terms - * except that the holder is Tim Hudson ([email protected]). - * - * Copyright remains Eric Young's, and as such any Copyright notices in - * the code are not to be removed. - * If this package is used in a product, Eric Young should be given attribution - * as the author of the parts of the library used. - * This can be in the form of a textual message at program startup or - * in documentation (online or textual) provided with the package. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * "This product includes cryptographic software written by - * Eric Young ([email protected])" - * The word 'cryptographic' can be left out if the rouines from the library - * being used are not cryptographic related :-). - * 4. If you include any Windows specific code (or a derivative thereof) from - * the apps directory (application code) you must include an acknowledgement: - * "This product includes software written by Tim Hudson ([email protected])" - * - * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * The licence and distribution terms for any publically available version or - * derivative of this code cannot be changed. i.e. this code cannot simply be - * copied and put under another distribution licence - * [including the GNU Public Licence.] - */ </code> - </section> -</chapter> - - diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml index 151b685941..95e968aa22 100644 --- a/lib/ssl/doc/src/notes.xml +++ b/lib/ssl/doc/src/notes.xml @@ -819,7 +819,7 @@ <title>Fixed Bugs and Malfunctions</title> <list type="bulleted"> <item> - <p>When a file descriptor was marked for closing, and and + <p>When a file descriptor was marked for closing, and end-of-file condition had already been detected, the file descriptor was never closed.</p> <p>Own Id: OTP-5093 Aux Id: seq8806 </p> diff --git a/lib/ssl/doc/src/refman.xml b/lib/ssl/doc/src/refman.xml index 9658e229eb..68f84660f3 100644 --- a/lib/ssl/doc/src/refman.xml +++ b/lib/ssl/doc/src/refman.xml @@ -45,7 +45,8 @@ </description> <xi:include href="ssl_app.xml"/> <xi:include href="ssl.xml"/> - <xi:include href="old_ssl.xml"/> + <xi:include href="old_ssl.xml"/> + <xi:include href="ssl_session_cache_api.xml"/> </application> diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml index 75aa8f2fe9..def61bcf03 100644 --- a/lib/ssl/doc/src/ssl.xml +++ b/lib/ssl/doc/src/ssl.xml @@ -21,13 +21,6 @@ </legalnotice> <title>ssl</title> - <prepared>Ingela Anderton Andin</prepared> - <responsible>Ingela Anderton Andin</responsible> - <docno></docno> - <approved></approved> - <checked></checked> - <date>2003-03-25</date> - <rev></rev> <file>ssl.xml</file> </header> <module>ssl</module> @@ -185,8 +178,17 @@ end {bad_cert, cert_expired}, {bad_cert, invalid_issuer}, {bad_cert, invalid_signature}, {bad_cert, name_not_permitted}, {bad_cert, cert_revoked} (not implemented yet), - {bad_cert, unknown_critical_extension} or {bad_cert, term()} (Will - be relevant later when an option is added for the user to be able to verify application specific extensions.) + {bad_cert, unknown_critical_extension} or {bad_cert, term()} + </item> + + + <tag>{validate_extensions_fun, fun()}</tag> + <item> + This options makes it possible to supply a fun to validate + possible application specific certificate extensions + during the certificat path validation. This option + will be better documented onec the public_key API is more + mature. </item> <tag>{depth, integer()}</tag> @@ -231,7 +233,8 @@ end </item> <tag>{ssl_imp, ssl_imp()}</tag> - <item>Specify which ssl implementation you want to use. + <item>Specify which ssl implementation you want to use. Defaults to + new. </item> <tag>{reuse_sessions, boolean()}</tag> @@ -248,6 +251,15 @@ end certificate, Compression is an enumeration integer and CipherSuite of type ciphersuite(). </item> + + <tag>{secure_renegotiate, boolean()}</tag> + <item>Specifies if to reject renegotiation attempt that does + not live up to RFC 5746. By default secure_renegotiate is + set to false e.i. secure renegotiation will be used if possible + but it will fallback to unsecure renegotiation if the peer + does not support RFC 5746. + </item> + </taglist> </section> @@ -414,7 +426,6 @@ end <type> <v>Socket = sslsocket()</v> <v>Cert = binary()</v> - <v>Subject = term()</v> </type> <desc> <p>The peer certificate is returned as a DER encoded binary. diff --git a/lib/ssl/doc/src/ssl_app.xml b/lib/ssl/doc/src/ssl_app.xml index fac2053532..2ba6f48611 100644 --- a/lib/ssl/doc/src/ssl_app.xml +++ b/lib/ssl/doc/src/ssl_app.xml @@ -22,38 +22,11 @@ </legalnotice> <title>ssl</title> - <prepared>Peter Högfeldt</prepared> - <responsible>Peter Högfeldt</responsible> - <docno></docno> - <approved>Peter Högfeldt</approved> - <checked>Peter Högfeldt</checked> - <date>2005-03-10</date> - <rev>E</rev> <file>ssl_app.sgml</file> </header> <app>ssl</app> - <appsummary>The SSL Application</appsummary> - <description> - <p>The Secure Socket Layer (SSL) application provides secure - socket communication over TCP/IP. Note that this documentation - is mainly valid for the old ssl implementation and will - be replaced in a future release. - </p> - </description> - - <section> - <title>Warning</title> - <p>In previous versions of Erlang/OTP SSL it was advised, as a - work-around, to set the operating system environment variable - <c>SSL_CERT_FILE</c> to point at a file containing CA - certificates. That variable is no longer needed, and is not - recognised by Erlang/OTP SSL any more. - </p> - <p>However, the OpenSSL package does interpret that environment - variable. Hence a setting of that variable might have - unpredictable effects on the Erlang/OTP SSL application. It is - therefore adviced to not used that environment variable at all.</p> - </section> + <appsummary>The SSL application provides secure communication over + sockets.</appsummary> <section> <title>Environment</title> @@ -63,115 +36,43 @@ </p> <p>Note that the environment parameters can be set on the command line, for instance,</p> - <p><c>erl ... -ssl protocol_version '[sslv2,sslv3]' ...</c>. + <p><c>erl ... -ssl protocol_version '[sslv3, tlsv1]' ...</c>. </p> <taglist> - <tag><c><![CDATA[ephemeral_rsa = true | false <optional>]]></c></tag> + <tag><c><![CDATA[protocol_version = [sslv3|tlsv1] <optional>]]></c>.</tag> <item> - <p>Enables all SSL servers (those that listen and accept) - to use ephemeral RSA key generation when a clients connect with - weak handshake cipher specifications, that need equally weak - ciphers from the server (i.e. obsolete restrictions on export - ciphers). Default is <c>false</c>. - </p> + <p>Protocol that will be supported by started clients and + servers. If this option is not set it will default to all + protocols currently supported by the erlang ssl application. + Note that this option may be overridden by the version option + to ssl:connect/[2,3] and ssl:listen/2. + </p> </item> - <tag><c><![CDATA[debug = true | false <optional>]]></c></tag> - <item> - <p>Causes debug information to be written to standard - output. Default is <c>false</c>. - </p> - </item> - <tag><c><![CDATA[debugdir = path() | false <optional>]]></c></tag> - <item> - <p>Causes debug information output controlled by <c>debug</c> - and <c>msgdebug</c> to be printed to a file named - <c><![CDATA[ssl_esock.<pid>.log]]></c> in the directory specified by - <c>debugdir</c>, where <c><![CDATA[<pid>]]></c> is the operating system - specific textual representation of the process identifier - of the external port program of the SSL application. Default - is <c>false</c>, i.e. no log file is produced. - </p> - </item> - <tag><c><![CDATA[msgdebug = true | false <optional>]]></c></tag> - <item> - <p>Sets <c>debug = true</c> and causes also the contents - of low level messages to be printed to standard output. - Default is <c>false</c>. - </p> - </item> - <tag><c><![CDATA[port_program = string() | false <optional>]]></c></tag> - <item> - <p>Name of port program. The default is <c>ssl_esock</c>. - </p> - </item> - <tag><c><![CDATA[protocol_version = [sslv2|sslv3|tlsv1] <optional>]]></c>.</tag> + + <tag><c><![CDATA[session_lifetime = integer() <optional>]]></c></tag> <item> - <p>Name of protocols to use. If this option is not set, - all protocols are assumed, i.e. the default value is - <c>[sslv2, sslv3, tlsv1]</c>. - </p> + <p>The lifetime of session data in seconds. + </p> </item> - <tag><c><![CDATA[proxylsport = integer() | false <optional>]]></c></tag> + + <tag><c><![CDATA[session_cb = atom() <optional>]]></c></tag> <item> - <p>Define the port number of the listen port of the - SSL port program. Almost never is this option needed. + <p> + Name of session cache callback module that implements + the ssl_session_cache_api behavior, defaults to + ssl_session_cache.erl. </p> </item> - <tag><c><![CDATA[proxylsbacklog = integer() | false <optional>]]></c></tag> + + <tag><c><![CDATA[session_cb_init_args = list() <optional>]]></c></tag> <item> - <p>Set the listen queue size of the listen port of the - SSL port program. The default is 128. - </p> + <p> + List of arguments to the init function in session cache + callback module, defaults to []. + </p> </item> - </taglist> - </section> - - <section> - <title>OpenSSL libraries</title> - <p>The current implementation of the Erlang SSL application is - based on the <em>OpenSSL</em> package version 0.9.7 or higher. - There are source and binary releases on the web. - </p> - <p>Source releases of OpenSSL can be downloaded from the <url href="http://www.openssl.org">OpenSSL</url> project home page, - or mirror sites listed there. - </p> - <p>The same URL also contains links to some compiled binaries and - libraries of OpenSSL (see the <c>Related/Binaries</c> menu) of - which the <url href="http://www.shininglightpro.com/search.php?searchname=Win32+OpenSSL">Shining Light Productions Win32 and OpenSSL</url> pages are of - interest for the Win32 user. - </p> - <p>For some Unix flavours there are binary packages available - on the net. - </p> - <p>If you cannot find a suitable binary OpenSSL package, you - have to fetch an OpenSSL source release and compile it. - </p> - <p>You then have to compile and install the libraries - <c>libcrypto.so</c> and <c>libssl.so</c> (Unix), or the - libraries <c>libeay32.dll</c> and <c>ssleay32.dll</c> (Win32). - </p> - <p>For Unix The <c>ssl_esock</c> port program is delivered linked - to OpenSSL libraries in <c>/usr/local/lib</c>, but the default - dynamic linking will also accept libraries in <c>/lib</c> and - <c>/usr/lib</c>. - </p> - <p>If that is not applicable to the particular Unix operating - system used, the example <c>Makefile</c> in the SSL - <c>priv/obj</c> directory, should be used as a guide to - relinking the final version of the port program. - </p> - <p>For <c>Win32</c> it is only required that the libraries can be - found from the <c>PATH</c> environment variable, or that they - reside in the appropriate <c>SYSTEM32</c> directory; hence no - particular relinking is need. Hence no example <c>Makefile</c> - for Win32 is provided.</p> - </section> - <section> - <title>Restrictions</title> - <p>Users must be aware of export restrictions and patent rights - concerning cryptographic software. - </p> + </taglist> </section> <section> @@ -180,5 +81,3 @@ </section> </appref> - - diff --git a/lib/ssl/doc/src/ssl_distribution.xml b/lib/ssl/doc/src/ssl_distribution.xml index c743cd67a3..4067fb8a22 100644 --- a/lib/ssl/doc/src/ssl_distribution.xml +++ b/lib/ssl/doc/src/ssl_distribution.xml @@ -32,7 +32,13 @@ <file>ssl_distribution.xml</file> </header> <p>This chapter describes how the Erlang distribution can use - SSL to get additional verification and security.</p> + SSL to get additional verification and security. + + <note><p>Note this + documentation is written for the old ssl implementation and + will be updated for the new one once this functionallity is + supported by the new implementation.</p></note> + </p> <section> <title>Introduction</title> diff --git a/lib/ssl/doc/src/ssl_protocol.xml b/lib/ssl/doc/src/ssl_protocol.xml index 3dc2332795..726b9a4eeb 100644 --- a/lib/ssl/doc/src/ssl_protocol.xml +++ b/lib/ssl/doc/src/ssl_protocol.xml @@ -13,337 +13,138 @@ compliance with the License. You should have received a copy of the Erlang Public License along with this software. If not, it can be retrieved online at http://www.erlang.org/. - + Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. - + </legalnotice> - <title>The SSL Protocol</title> - <prepared>Peter Högfeldt</prepared> - <docno></docno> - <date>2003-04-28</date> - <rev>PA2</rev> + <title>Transport Layer Security (TLS) and its predecessor, Secure Socket Layer (SSL)</title> <file>ssl_protocol.xml</file> </header> - <p>Here we provide a short introduction to the SSL protocol. We only - consider those part of the protocol that are important from a - programming point of view. - </p> - <p>For a very good general introduction to SSL and TLS see the book - <cite id="rescorla"></cite>. - </p> - <p><em>Outline:</em></p> - <list type="bulleted"> - <item>Two types of connections - connection: handshake, data transfer, and - shutdown - - SSL/TLS protocol - server must have certificate - what the the - server sends to the client - client may verify the server - - server may ask client for certificate - what the client sends to - the server - server may then verify the client - verification - - certificate chains - root certificates - public keys - key - agreement - purpose of certificate - references</item> - </list> + + <p>The erlang ssl application currently supports SSL 3.0 and TLS 1.0 + RFC 2246, and will in the future also support later versions of TLS. + SSL 2.0 is not supported. + </p> - <section> - <title>SSL Connections</title> - <p>The SSL protocol is implemented on top of the TCP/IP protocol. - From an endpoint view it also has the same type of connections - as that protocol, almost always created by calls to socket - interface functions <em>listen</em>, <em>accept</em> and - <em>connect</em>. The endpoints are <em>servers</em> and - <em>clients</em>. - </p> - <p>A <em>server</em><em>listen</em>s for connections on a - specific address and port. This is done once. The server then - <em>accept</em>s each connections on that same address and - port. This is typically done indefinitely many times. - </p> - <p>A <em>client</em> connects to a server on a specific address - and port. For each purpose this is done once. - </p> - <p>For a plain TCP/IP connection the establishment of a connection - (through an accept or a connect) is followed by data transfer between - the client and server, finally ended by a connection close. - </p> - <p>An SSL connection also consists of data transfer and connection - close, However, the data transfer contains encrypted data, and - in order to establish the encryption parameters, the data - transfer is preceded by an SSL <em>handshake</em>. In this - handshake the server plays a dominant role, and the main - instrument used in achieving a valid SSL connection is the - server's <em>certificate</em>. We consider certificates in the - next section, and the SSL handshake in a subsequent section.</p> - </section> + <p>By default erlang ssl is run over the TCP/IP protocol even + though you could plug in an other reliable transport protocol + with the same API as gen_tcp.</p> + + <p>If a client and server wants to use an upgrade mechanism, such as + defined by RFC2817, to upgrade a regular TCP/IP connection to a ssl + connection the erlang ssl API supports this. This can be useful for + things such as supporting HTTP and HTTPS on the same port and + implementing virtual hosting. + </p> <section> - <title>Certificates</title> - <p>A certificate is similar to a driver's license, or a - passport. The holder of the certificate is called the - <em>subject</em>. First of all the certificate identifies the - subject in terms of the name of the subject, its postal address, - country name, company name (if applicable), etc. - </p> - <p>Although a driver's license is always issued by a well-known and - distinct authority, a certificate may have an <em>issuer</em> - that is not so well-known. Therefore a certificate also always - contains information on the issuer of the certificate. That - information is of the same type as the information on the - subject. The issuer of a certificate also signs the certificate - with a <em>digital signature</em> (the signature is an inherent - part of the certificate), which allow others to verify that the - issuer really is the issuer of the certificate. - </p> - <p>Now that a certificate can be checked by verifying the - signature of the issuer, the question is how to trust the - issuer. The answer to this question is to require that there is - a certificate for the issuer as well. That issuer has in turn an - issuer, which must also have a certificate, and so on. This - <em>certificate chain</em> has to have en end, which then must - be a certificate that is trusted by other means. We shall cover - this problem of <em>authentication</em> in a subsequent - section. - </p> + <title>Security overview</title> + + <p>To achive authentication and privacy the client and server will + perform a TLS Handshake procedure before transmitting or receiving + any data. During the handshake they agree on a protocol version and + cryptographic algorithms, they generate shared secrets using public + key cryptographics and optionally authenticate each other with + digital certificates.</p> </section> - + <section> - <title>Encryption Algorithms</title> - <p>An encryption algorithm is a mathematical algorithm for - encryption and decryption of messages (arrays of bytes, - say). The algorithm as such is always required to be publicly - known, otherwise its strength cannot be evaluated, and hence it - cannot be used reliably. The secrecy of an encrypted message is - not achieved by the secrecy of the algorithm used, but by the - secrecy of the <em>keys</em> used as input to the encryption and - decryption algorithms. For an account of cryptography in general - see <cite id="schneier"></cite>. - </p> - <p>There are two classes of encryption algorithms: <em>symmetric key</em> algorithms and <em>public key</em> algorithms. Both - types of algorithms are used in the SSL protocol. - </p> - <p>In the sequel we assume holders of keys keep them secret (except - public keys) and that they in that sense are trusted. How a - holder of a secret key is proved to be the one it claims to be - is a question of <em>authentication</em>, which, in the context - of the SSL protocol, is described in a section further below. - </p> - - <section> - <title>Symmetric Key Algorithms</title> - <p>A <em>symmetric key</em> algorithm has one key only. The key - is used for both encryption and decryption. Obviously the key - of a symmetric key algorithm must always be kept secret by the - users of the key. DES is an example of a symmetric key - algorithm. - </p> - <p>Symmetric key algorithms are fast compared to public key - algorithms. They are therefore typically used for encrypting - bulk data. - </p> - </section> - - <section> - <title>Public Key Algorithms</title> - <p>A <em>public key</em> algorithm has two keys. Any of the two - keys can be used for encryption. A message encrypted with one - of the keys, can only be decrypted with the other key. One of - the keys is public (known to the world), while the other key - is private (i.e. kept secret) by the owner of the two keys. - </p> - <p>RSA is an example of a public key algorithm. - </p> - <p>Public key algorithms are slow compared to symmetric key - algorithms, and they are therefore seldom used for bulk data - encryption. They are therefore only used in cases where the - fact that one key is public and the other is private, provides - features that cannot be provided by symmetric algorithms. - </p> - </section> - - <section> - <title>Digital Signature Algorithms</title> - <p>An interesting feature of a public key algorithm is that its - public and private keys can both be used for encryption. - Anyone can use the public key to encrypt a message, and send - that message to the owner of the private key, and be sure of - that only the holder of the private key can decrypt the - message. - </p> - <p>On the other hand, the owner of the private key can encrypt a - message with the private key, thus obtaining an encrypted - message that can decrypted by anyone having the public key. - </p> - <p>The last approach can be used as a digital signature - algorithm. The holder of the private key signs an array of - bytes by performing a specified well-known <em>message digest algorithm</em> to compute a hash of the array, encrypts the - hash value with its private key, an then presents the original - array, the name of the digest algorithm, and the encryption of - the hash value as a <em>signed array of bytes</em>. - </p> - <p>Now anyone having the public key, can decrypt the encrypted - hash value with that key, compute the hash with the specified - digest algorithm, and check that the hash values compare equal - in order to verify that the original array was indeed signed - by the holder of the private key. - </p> - <p>What we have accounted for so far is by no means all that can - be said about digital signatures (see <cite id="schneier"></cite>for - further details). - </p> - </section> - - <section> - <title>Message Digests Algorithms</title> - <p>A message digest algorithm is a hash function that accepts - an array bytes of arbitrary but finite length of input, and - outputs an array of bytes of fixed length. Such an algorithm - is also required to be very hard to invert. - </p> - <p>MD5 (16 bytes output) and SHA1 (20 bytes output) are examples - of message digest algorithms. - </p> - </section> + <title>Data Privacy and Integrity</title> + + <p>A <em>symmetric key</em> algorithm has one key only. The key is + used for both encryption and decryption. These algoritms are fast + compared to public key algorithms (using two keys, a public and a + private one) and are therefore typically used for encrypting bulk + data. + </p> + + <p>The keys for the symmetric encryption are generated uniquely + for each connection and are based on a secret negotiated + in the TLS handshake. </p> + + <p>The TLS handsake protocol and data transfer is run on top of + the TLS Record Protocol that uses a keyed-hash MAC (Message + Authenticity Code), or HMAC, to protect the message's data + integrity. From the TLS RFC "A Message Authentication Code is a + one-way hash computed from a message and some secret data. It is + difficult to forge without knowing the secret data. Its purpose is + to detect if the message has been altered." + </p> + </section> - <section> - <title>SSL Handshake</title> - <p>The main purpose of the handshake performed before an an SSL - connection is established is to negotiate the encryption - algorithm and key to be used for the bulk data transfer between - the client and the server. We are writing <em>the</em> key, - since the algorithm to choose for bulk encryption one of the - symmetric algorithms. - </p> - <p>There is thus only one key to agree upon, and obviously that - key has to be kept secret between the client and the server. To - obtain that the handshake has to be encrypted as well. - </p> - <p>The SSL protocol requires that the server always sends its - certificate to the client in the beginning of the handshake. The - client then retrieves the server's public key from the - certificate, which means that the client can use the server's - public key to encrypt messages to the server, and the server can - decrypt those messages with its private key. Similarly, the - server can encrypt messages to the client with its private key, - and the client can decrypt messages with the server's public - key. It is thus is with the server's public and private keys - that messages in the handshake are encrypted and decrypted, and - hence the key agreed upon for symmetric encryption of bulk data - can be kept secret (there are more things to consider to really - keep it secret, see <cite id="rescorla"></cite>). - </p> - <p>The above indicates that the server does not care who is - connecting, and that only the client has the possibility to - properly identify the server based on the server's certificate. - That is indeed true in the minimal use of the protocol, but it - is possible to instruct the server to request the certificate of - the client, in order to have a means to identify the client, but - it is by no means required to establish an SSL connection. - </p> - <p>If a server request the client certificate, it verifies, as a - part of the protocol, that the client really holds the private - key of the certificate by sending the client a string of bytes - to encrypt with its private key, which the server then decrypts - with the client's public key, the result of which is compared - with the original string of bytes (a similar procedure is always - performed by the client when it has received the server's - certificate). - </p> - <p>The way clients and servers <em>authenticate</em> each other, - i.e. proves that their respective peers are what they claim to - be, is the topic of the next section. - </p> - </section> + <section> + <title>Digital Certificates</title> + <p>A certificate is similar to a driver's license, or a + passport. The holder of the certificate is called the + <em>subject</em>. The certificate is signed + with the private key of the issuer of the certificate. A chain + of trust is build by having the issuer in its turn being + certified by an other certificate and so on until you reach the + so called root certificate that is self signed e.i. issued + by itself.</p> + + <p>Certificates are issued by <em>certification + authorities</em> (<em>CA</em>s) only. There are a handful of + top CAs in the world that issue root certificates. You can + examine the certificates of several of them by clicking + through the menus of your web browser. + </p> + </section> + + <section> + <title>Authentication of Sender</title> + + <p>Authentication of the sender is done by public key path + validation as defined in RFC 3280. Simplified that means that + each certificate in the certificate chain is issued by the one + before, the certificates attributes are valid ones, and the + root cert is a trusted cert that is present in the trusted + certs database kept by the peer.</p> + + <p>The server will always send a certificate chain as part of + the TLS handshake, but the client will only send one if + the server requests it. If the client does not have + an appropriate certificate it may send an "empty" certificate + to the server.</p> + + <p>The client may choose to accept some path evaluation errors + for instance a web browser may ask the user if they want to + accept an unknown CA root certificate. The server, if it request + a certificate, will on the other hand not accept any path validation + errors. It is configurable if the server should accept + or reject an "empty" certificate as response to + a certificate request.</p> + </section> + + <section> + <title>TLS Sessions</title> + + <p>From the TLS RFC "A TLS session is an association between a + client and a server. Sessions are created by the handshake + protocol. Sessions define a set of cryptographic security + parameters, which can be shared among multiple + connections. Sessions are used to avoid the expensive negotiation + of new security parameters for each connection."</p> - <section> - <title>Authentication</title> - <p>As we have already seen the reception of a certificate from a - peer is not enough to prove that the peer is authentic. More - certificates are needed, and we have to consider how certificates - are issued and on what grounds. - </p> - <p>Certificates are issued by <em>certification authorities</em> - (<em>CA</em>s) only. They issue certificates both for other CAs - and ordinary users (which are not CAs). - </p> - <p>Certain CAs are <em>top CAs</em>, i.e. they do not have a - certificate issued by another CA. Instead they issue their own - certificate, where the subject and issuer part of the - certificate are identical (such a certificate is called a - self-signed certificate). A top CA has to be well-known, and has - to have a publicly available policy telling on what grounds it - issues certificates. - </p> - <p>There are a handful of top CAs in the world. You can examine the - certificates of several of them by clicking through the menus of - your web browser. - </p> - <p>A top CA typically issues certificates for other CAs, called - <em>intermediate CAs</em>, but possibly also to ordinary users. Thus - the certificates derivable from a top CA constitute a tree, where - the leaves of the tree are ordinary user certificates. - </p> - <p>A <em>certificate chain</em> is an ordered sequence of - certificates, <c>C1, C2, ..., Cn</c>, say, where <c>C1</c> is a - top CA certificate, and where <c>Cn</c> is an ordinary user - certificate, and where the holder of <c>C1</c> is the issuer of - <c>C2</c>, the holder of <c>C2</c> is the issuer of <c>C3</c>, - ..., and the holder of <c>Cn-1</c> is the issuer of <c>Cn</c>, - the ordinary user certificate. The holders of <c>C2, C3, ..., Cn-1</c> are then intermediate CAs. - </p> - <p>Now to verify that a certificate chain is unbroken we have to - take the public key from each certificate <c>Ck</c>, and apply - that key to decrypt the signature of certificate <c>Ck-1</c>, - thus obtaining the message digest computed by the holder of the - <c>Ck</c> certificate, compute the real message digest of the - <c>Ck-1</c> certificate and compare the results. If they compare - equal the link of the chain between <c>Ck</c> and <c>Ck-1</c> is - considered to unbroken. This is done for each link k = 1, 2, - ..., n-1. If all links are found to be unbroken, the user - certificate <c>Cn</c> is considered authenticated. - </p> + <p>Session data is by default kept by the ssl application in a + memory storage hence session data will be lost at application + restart or takeover. Users may define their own callback module + to handle session data storage if persistent data storage is + required. Session data will also be invalidated after 24 hours + from it was saved, for security reasons. It is of course + possible to configure the amount of time the session data should be + saved.</p> - <section> - <title>Trusted Certificates</title> - <p>Now that there is a way to authenticate a certificate by - checking that all links of a certificate chain are unbroken, - the question is how you can be sure to trust the certificates - in the chain, and in particular the top CA certificate of the - chain. - </p> - <p>To provide an answer to that question consider the - perspective of a client, which have just received the - certificate of the server. In order to authenticate the server - the client has to construct a certificate chain and to prove - that the chain is unbroken. The client has to have a set of CA - certificates (top CA or intermediate CA certificates) not - obtained from the server, but obtained by other means. Those - certificates are kept <c>locally</c> by the client, and are - trusted by the client. - </p> - <p>More specifically, the client does not really have to have - top CA certificates in its local storage. In order to - authenticate a server it is sufficient for the client to - posses the trusted certificate of the issuer of the server - certificate. - </p> - <p>Now that is not the whole story. A server can send an - (incomplete) certificate chain to its client, and then the - task of the client is to construct a certificate chain that - begins with a trusted certificate and ends with the server's - certificate. (A client can also send a chain to its server, - provided the server requested the client's certificate.) - </p> - <p>All this means that an unbroken certificate chain begins with - a trusted certificate (top CA or not), and ends with the peer - certificate. That is the end of the chain is obtained from the - peer, but the beginning of the chain is obtained from local - storage, which is considered trusted. - </p> - </section> - </section> -</chapter> + <p>Ssl clients will by default try to reuse an available session, + ssl servers will by default agree to reuse sessions when clients + ask to do so.</p> + + </section> + </chapter> diff --git a/lib/ssl/doc/src/ssl_session_cache_api.xml b/lib/ssl/doc/src/ssl_session_cache_api.xml new file mode 100644 index 0000000000..7b70c6cf34 --- /dev/null +++ b/lib/ssl/doc/src/ssl_session_cache_api.xml @@ -0,0 +1,158 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE erlref SYSTEM "erlref.dtd"> + +<erlref> + <header> + <copyright> + <year>1999</year><year>2010</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + <title>ssl</title> + <file>ssl_session_cache_api.xml</file> + </header> + <module>ssl_session_cache_api</module> + <modulesummary>Defines the API for the TLS session cache so + that the datastorge scheme can be replaced by + defining a new callback module implementing this API.</modulesummary> + + <section> + <title>Common Data Types</title> + + <p>The following data types are used in the functions below: + </p> + + <p><c>cache_ref() = opaque()</c></p> + + <p><c>key() = {partialkey(), session_id()}</c></p> + + <p><c>partialkey() = opaque()</c></p> + + <p><c>session_id() = binary()</c></p> + + <p><c>session() = opaque()</c></p> + + </section> + + <funcs> + + <func> + <name>delete(Cache, Key) -> _</name> + <fsummary></fsummary> + <type> + <v> Cache = cache_ref()</v> + <v> Key = key()</v> + </type> + <desc> + <p> Delets a cache entry. Will only be called from the cache + handling process. + </p> + </desc> + </func> + + <func> + <name>foldl(Fun, Acc0, Cache) -> Acc</name> + <fsummary></fsummary> + <type> + <v></v> + </type> + <desc> + <p>Calls Fun(Elem, AccIn) on successive elements of the + cache, starting with AccIn == Acc0. Fun/2 must return a new + accumulator which is passed to the next call. The function returns + the final value of the accumulator. Acc0 is returned if the cache is + empty. + </p> + </desc> + </func> + + <func> + <name>init() -> opaque() </name> + <fsummary>Return cache reference</fsummary> + <type> + <v></v> + </type> + <desc> + <p>Performes possible initializations of the cache and returns + a reference to it that will be used as parameter to the other + api functions. Will be called by the cache handling processes + init function, hence puting the same requierments on it as + a normal process init function. + </p> + </desc> + </func> + + <func> + <name>lookup(Cache, Key) -> Entry</name> + <fsummary> Looks up a cach entry.</fsummary> + <type> + <v> Cache = cache_ref()</v> + <v> Key = key()</v> + <v> Entry = session() | undefined </v> + </type> + <desc> + <p>Looks up a cach entry. Should be callable from any + process. + </p> + </desc> + </func> + + <func> + <name>select_session(Cache, PartialKey) -> [session()]</name> + <fsummary>>Selects sessions that could be reused.</fsummary> + <type> + <v> Cache = cache_ref()</v> + <v> PartialKey = partialkey()</v> + <v> Session = session()</v> + </type> + <desc> + <p>Selects sessions that could be reused. Should be callable + from any process. + </p> + </desc> + </func> + + <func> + <name>terminate(Cache) -> _</name> + <fsummary>Called by the process that handles the cache when it + is aboute to terminat.</fsummary> + <type> + <v>Cache = term() - as returned by init/0</v> + </type> + <desc> + <p>Takes care of possible cleanup that is needed when the + cache handling process terminates. + </p> + </desc> + </func> + + <func> + <name>update(Cache, Key, Session) -> _</name> + <fsummary> Caches a new session or updates a already cached one.</fsummary> + <type> + <v> Cache = cache_ref()</v> + <v> Key = key()</v> + <v> Session = session()</v> + </type> + <desc> + <p> Caches a new session or updates a already cached one. Will + only be called from the cache handling process. + </p> + </desc> + </func> + + </funcs> + +</erlref> diff --git a/lib/ssl/doc/src/usersguide.xml b/lib/ssl/doc/src/usersguide.xml index 76ee13dd23..6528c00a0b 100644 --- a/lib/ssl/doc/src/usersguide.xml +++ b/lib/ssl/doc/src/usersguide.xml @@ -23,35 +23,17 @@ <title>SSL User's Guide</title> <prepared>OTP Team</prepared> - <docno></docno> <date>2003-05-26</date> - <rev>B</rev> <file>usersguide.sgml</file> </header> <description> <p>The <em>SSL</em> application provides secure communication over - sockets. Note that this users guide was written for - the old ssl implementation that is now being phased out and - will be replaced by a new guide in a future release. - </p> - <p>This product includes software developed by the OpenSSL Project for - use in the OpenSSL Toolkit (http://www.openssl.org/). - </p> - <p>This product includes cryptographic software written by Eric Young - ([email protected]). - </p> - <p>This product includes software written by Tim Hudson - ([email protected]). - </p> - <p>For full OpenSSL and SSLeay license texts, see <seealso marker="licenses#licenses">Licenses</seealso>. + sockets. </p> </description> <xi:include href="ssl_protocol.xml"/> <xi:include href="using_ssl.xml"/> - <xi:include href="pkix_certs.xml"/> - <xi:include href="create_certs.xml"/> <xi:include href="ssl_distribution.xml"/> - <xi:include href="licenses.xml"/> </part> diff --git a/lib/ssl/doc/src/using_ssl.xml b/lib/ssl/doc/src/using_ssl.xml new file mode 100644 index 0000000000..4bdd8f97b4 --- /dev/null +++ b/lib/ssl/doc/src/using_ssl.xml @@ -0,0 +1,149 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>2003</year><year>2009</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + + <title>Using the SSL API</title> + <file>using_ssl.xml</file> + </header> + + <section> + <title>General information</title> + <p>To see relevant version information for ssl you can + call ssl:versions/0</p> + + <p>To see all supported cipher suites + call ssl:cipher_suites/0. Note that available cipher suites + for a connection will depend on your certificate. It is also + possible to specify a specific cipher suite(s) that you + want your connection to use. Default is to use the strongest + available.</p> + + </section> + + <section> + <title>Setting up connections</title> + + <p>Here follows some small example of how to set up client/server connections + using the erlang shell. The returned value of the sslsocket has been abbreviated with + <c>[...]</c> as it can be fairly large and is opaque.</p> + + <section> + <title>Minmal example</title> + + <note><p> The minimal setup is not the most secure setup of ssl.</p> + </note> + + <p> Start server side</p> + <code type="erl">1 server> ssl:start(). +ok</code> + + <p>Create a ssl listen socket</p> + <code type="erl">2 server> {ok, ListenSocket} = +ssl:listen(9999, [{certfile, "cert.pem"}, {keyfile, "key.pem"},{reuseaddr, true}]). +{ok,{sslsocket, [...]}}</code> + + <p>Do a transport accept on the ssl listen socket</p> + <code type="erl">3 server> {ok, Socket} = ssl:transport_accept(ListenSocket). +{ok,{sslsocket, [...]}}</code> + + <p>Start client side</p> + <code type="erl">1 client> ssl:start(). +ok</code> + + <code type="erl">2 client> {ok, Socket} = ssl:connect("localhost", 9999, [], infinity). +{ok,{sslsocket, [...]}}</code> + + <p>Do the ssl handshake</p> + <code type="erl">4 server> ok = ssl:ssl_accept(Socket). +ok</code> + + <p>Send a messag over ssl</p> + <code type="erl">5 server> ssl:send(Socket, "foo"). +ok</code> + + <p>Flush the shell message queue to see that we got the message + sent on the server side</p> + <code type="erl">3 client> flush(). +Shell got {ssl,{sslsocket,[...]},"foo"} +ok</code> + </section> + + <section> + <title>Upgrade example</title> + + <note><p> To upgrade a TCP/IP connection to a ssl connection the + client and server have to aggre to do so. Agreement + may be accompliced by using a protocol such the one used by HTTP + specified in RFC 2817.</p> </note> + + <p>Start server side</p> + <code type="erl">1 server> ssl:start(). +ok</code> + + <p>Create a normal tcp listen socket</p> + <code type="erl">2 server> {ok, ListenSocket} = gen_tcp:listen(9999, [{reuseaddr, true}]). +{ok, #Port<0.475>}</code> + + <p>Accept client connection</p> + <code type="erl">3 server> {ok, Socket} = gen_tcp:accept(ListenSocket). +{ok, #Port<0.476>}</code> + + <p>Start client side</p> + <code type="erl">1 client> ssl:start(). +ok</code> + + <code type="erl">2 client> {ok, Socket} = gen_tcp:connect("localhost", 9999, [], infinity).</code> + + <p>Make sure active is set to false before trying + to upgrade a connection to a ssl connection, otherwhise + ssl handshake messages may be deliverd to the wrong process.</p> + <code type="erl">4 server> inet:setopts(Socket, [{active, false}]). +ok</code> + + <p>Do the ssl handshake.</p> + <code type="erl">5 server> {ok, SSLSocket} = ssl:ssl_accept(Socket, [{cacertfile, "cacerts.pem"}, +{certfile, "cert.pem"}, {keyfile, "key.pem"}]). +{ok,{sslsocket,[...]}}</code> + + <p> Upgrade to a ssl connection. Note that the client and server + must agree upon the upgrade and the server must call + ssl:accept/2 before the client calls ssl:connect/3.</p> + <code type="erl">3 client>{ok, SSLSocket} = ssl:connect(Socket, [{cacertfile, "cacerts.pem"}, +{certfile, "cert.pem"}, {keyfile, "key.pem"}], infinity). +{ok,{sslsocket,[...]}}</code> + + <p>Send a messag over ssl</p> + <code type="erl">4 client> ssl:send(SSLSocket, "foo"). +ok</code> + + <p>Set active true on the ssl socket</p> + <code type="erl">4 server> ssl:setopts(SSLSocket, [{active, true}]). +ok</code> + + <p>Flush the shell message queue to see that we got the message + sent on the client side</p> + <code type="erl">5 server> flush(). +Shell got {ssl,{sslsocket,[...]},"foo"} +ok</code> + </section> + </section> + </chapter> diff --git a/lib/ssl/doc/src/using_ssl.xmlsrc b/lib/ssl/doc/src/using_ssl.xmlsrc deleted file mode 100644 index ba74dcfef4..0000000000 --- a/lib/ssl/doc/src/using_ssl.xmlsrc +++ /dev/null @@ -1,113 +0,0 @@ -<?xml version="1.0" encoding="latin1" ?> -<!DOCTYPE chapter SYSTEM "chapter.dtd"> - -<chapter> - <header> - <copyright> - <year>2003</year><year>2009</year> - <holder>Ericsson AB. All Rights Reserved.</holder> - </copyright> - <legalnotice> - The contents of this file are subject to the Erlang Public License, - Version 1.1, (the "License"); you may not use this file except in - compliance with the License. You should have received a copy of the - Erlang Public License along with this software. If not, it can be - retrieved online at http://www.erlang.org/. - - Software distributed under the License is distributed on an "AS IS" - basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See - the License for the specific language governing rights and limitations - under the License. - - </legalnotice> - - <title>Using the SSL application</title> - <prepared>Peter Högfeldt</prepared> - <docno></docno> - <date>2003-04-23</date> - <rev>PA2</rev> - <file>using_ssl.xml</file> - </header> - <p>Here we provide an introduction to using the Erlang/OTP SSL - application, which is accessed through the <c>ssl</c> interface - module. - </p> - <p>We also present example code in the Erlang module - <c>client_server</c>, also provided in the directory - <c>ssl-X.Y.Z/examples</c>, with source code in <c>src</c> and the - compiled module in <c>ebin</c> of that directory. - </p> - - <section> - <title>The ssl Module</title> - <p>The <c>ssl</c> module provides the user interface to the Erlang/OTP - SSL application. The interface functions provided are very similar - to those provided by the <c>gen_tcp</c> and <c>inet</c> modules. - </p> - <p>Servers use the interface functions <c>listen</c> and - <c>accept</c>. The <c>listen</c> function specifies a TCP port - to to listen to, and each call to the <c>accept</c> function - establishes an incoming connection. - </p> - <p>Clients use the <c>connect</c> function which specifies the address - and port of a server to connect to, and a successful call establishes - such a connection. - </p> - <p>The <c>listen</c> and <c>connect</c> functions have almost all - the options that the corresponding functions in <c>gen_tcp/</c> have, - but there are also additional options specific to the SSL protocol. - </p> - <p>The most important SSL specific option is the <c>cacertfile</c> - option which specifies a local file containing trusted CA - certificates which are and used for peer authentication. This - option is used by clients and servers in case they want to - authenticate their peers. - </p> - <p>The <c>certfile</c> option specifies a local path to a file - containing the certificate of the holder of the connection - endpoint. In case of a server endpoint this option is mandatory - since the contents of the sever certificate is needed in the - the handshake preceding the establishment of a connection. - </p> - <p>Similarly, the <c>keyfile</c> option points to a local file - containing the private key of the holder of the endpoint. If the - <c>certfile</c> option is present, this option has to be - specified as well, unless the private key is provided in the - same file as specified by the <c>certfile</c> option (a - certificate and a private key can thus coexist in the same file). - </p> - <p>The <c>verify</c> option specifies how the peer should be verified: - </p> - <taglist> - <tag>0</tag> - <item>Do not verify the peer,</item> - <tag>1</tag> - <item>Verify peer,</item> - <tag>2</tag> - <item>Verify peer, fail the verification if the peer has no - certificate. </item> - </taglist> - <p>The <c>depth</c> option specifies the maximum length of the - verification certificate chain. Depth = 0 means the peer - certificate, depth = 1 the CA certificate, depth = 2 the next CA - certificate etc. If the verification process does not find a - trusted CA certificate within the maximum length, the verification - fails. - </p> - <p>The <c>ciphers</c> option specifies which ciphers to use (a - string of colon separated cipher names). To obtain a list of - available ciphers, evaluate the <c>ssl:ciphers/0</c> function - (the SSL application has to be running). - </p> - </section> - - <section> - <title>A Client-Server Example</title> - <p>Here is a simple client server example. - </p> - <codeinclude file="../../examples/src/client_server.erl" tag="" type="erl"></codeinclude> - </section> -</chapter> - - - diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 35d27713b8..df4cd7c84d 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -34,10 +34,12 @@ %% Should be deprecated as soon as old ssl is removed %%-deprecated({pid, 1, next_major_release}). +-deprecated({peercert, 2, next_major_release}). -include("ssl_int.hrl"). -include("ssl_internal.hrl"). -include("ssl_record.hrl"). +-include("ssl_cipher.hrl"). -include_lib("public_key/include/public_key.hrl"). @@ -49,11 +51,11 @@ }). %%-------------------------------------------------------------------- -%% Function: start([, Type]) -> ok +-spec start() -> ok. +-spec start(permanent | transient | temporary) -> ok. %% -%% Type = permanent | transient | temporary -%% -%% Description: Starts the ssl application. Default type +%% Description: Utility function that starts the ssl, +%% crypto and public_key applications. Default type %% is temporary. see application(3) %%-------------------------------------------------------------------- start() -> @@ -67,7 +69,7 @@ start(Type) -> application:start(ssl, Type). %%-------------------------------------------------------------------- -%% Function: stop() -> ok +-spec stop() -> ok. %% %% Description: Stops the ssl application. %%-------------------------------------------------------------------- @@ -75,7 +77,8 @@ stop() -> application:stop(ssl). %%-------------------------------------------------------------------- -%% Function: connect(Address, Port, Options[, Timeout]) -> {ok, Socket} +-spec connect(host() | port(), port_num(), list()) -> {ok, #sslsocket{}}. +-spec connect(host() | port(), port_num(), list(), timeout()) -> {ok, #sslsocket{}}. %% %% Description: Connect to a ssl server. %%-------------------------------------------------------------------- @@ -101,13 +104,13 @@ connect(Socket, SslOptions0, Timeout) when is_port(Socket) -> {error, Reason} end; -connect(Address, Port, Options) -> - connect(Address, Port, Options, infinity). +connect(Host, Port, Options) -> + connect(Host, Port, Options, infinity). -connect(Address, Port, Options0, Timeout) -> +connect(Host, Port, Options0, Timeout) -> case proplists:get_value(ssl_imp, Options0, new) of new -> - new_connect(Address, Port, Options0, Timeout); + new_connect(Host, Port, Options0, Timeout); old -> %% Allow the option reuseaddr to be present %% so that new and old ssl can be run by the same @@ -115,13 +118,14 @@ connect(Address, Port, Options0, Timeout) -> %% that hardcodes reuseaddr to true in its portprogram. Options1 = proplists:delete(reuseaddr, Options0), Options = proplists:delete(ssl_imp, Options1), - old_connect(Address, Port, Options, Timeout); + old_connect(Host, Port, Options, Timeout); Value -> {error, {eoptions, {ssl_imp, Value}}} end. %%-------------------------------------------------------------------- -%% Function: listen(Port, Options) -> {ok, ListenSock} | {error, Reason} +-spec listen(port_num(), list()) ->{ok, #sslsocket{}} | {error, reason()}. + %% %% Description: Creates a ssl listen socket. %%-------------------------------------------------------------------- @@ -144,7 +148,8 @@ listen(Port, Options0) -> end. %%-------------------------------------------------------------------- -%% Function: transport_accept(ListenSocket[, Timeout]) -> {ok, Socket}. +-spec transport_accept(#sslsocket{}) -> {ok, #sslsocket{}}. +-spec transport_accept(#sslsocket{}, timeout()) -> {ok, #sslsocket{}}. %% %% Description: Performs transport accept on a ssl listen socket %%-------------------------------------------------------------------- @@ -182,8 +187,8 @@ transport_accept(#sslsocket{} = ListenSocket, Timeout) -> ssl_broker:transport_accept(Pid, ListenSocket, Timeout). %%-------------------------------------------------------------------- -%% Function: ssl_accept(ListenSocket[, Timeout]) -> {ok, Socket} | -%% {error, Reason} +-spec ssl_accept(#sslsocket{}) -> {ok, #sslsocket{}} | {error, reason()}. +-spec ssl_accept(#sslsocket{}, timeout()) -> {ok, #sslsocket{}} | {error, reason()}. %% %% Description: Performs accept on a ssl listen socket. e.i. performs %% ssl handshake. @@ -217,7 +222,7 @@ ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket) -> end. %%-------------------------------------------------------------------- -%% Function: close() -> ok +-spec close(#sslsocket{}) -> term(). %% %% Description: Close a ssl connection %%-------------------------------------------------------------------- @@ -230,7 +235,7 @@ close(Socket = #sslsocket{}) -> ssl_broker:close(Socket). %%-------------------------------------------------------------------- -%% Function: send(Socket, Data) -> ok | {error, Reason} +-spec send(#sslsocket{}, iolist()) -> ok | {error, reason()}. %% %% Description: Sends data over the ssl connection %%-------------------------------------------------------------------- @@ -242,7 +247,8 @@ send(#sslsocket{} = Socket, Data) -> ssl_broker:send(Socket, Data). %%-------------------------------------------------------------------- -%% Function: recv(Socket, Length [,Timeout]) -> {ok, Data} | {error, reason} +-spec recv(#sslsocket{}, integer()) -> {ok, binary()| list()} | {error, reason()}. +-spec recv(#sslsocket{}, integer(), timeout()) -> {ok, binary()| list()} | {error, reason()}. %% %% Description: Receives data when active = false %%-------------------------------------------------------------------- @@ -256,8 +262,8 @@ recv(Socket = #sslsocket{}, Length, Timeout) -> ssl_broker:recv(Socket, Length, Timeout). %%-------------------------------------------------------------------- -%% Function: controlling_process(Socket, NewOwner) -> ok | {error, Reason} -%% +-spec controlling_process(#sslsocket{}, pid()) -> ok | {error, reason()}. +%% %% Description: Changes process that receives the messages when active = true %% or once. %%-------------------------------------------------------------------- @@ -270,11 +276,8 @@ controlling_process(Socket, NewOwner) when is_pid(NewOwner) -> ssl_broker:controlling_process(Socket, NewOwner). %%-------------------------------------------------------------------- -%% Function: connection_info(Socket) -> {ok, {Protocol, CipherSuite}} | -%% {error, Reason} -%% Protocol = sslv3 | tlsv1 | tlsv1.1 -%% CipherSuite = {KeyExchange, Chipher, Hash, Exportable} -%% +-spec connection_info(#sslsocket{}) -> {ok, {tls_atom_version(), erl_cipher_suite()}} | + {error, reason()}. %% %% Description: Returns ssl protocol and cipher used for the connection %%-------------------------------------------------------------------- @@ -286,9 +289,9 @@ connection_info(#sslsocket{} = Socket) -> ssl_broker:connection_info(Socket). %%-------------------------------------------------------------------- -%% Function: peercert(Socket[, Opts]) -> {ok, Cert} | {error, Reason} +-spec peercert(#sslsocket{}) ->{ok, der_cert()} | {error, reason()}. %% -%% Description: +%% Description: Returns the peercert. %%-------------------------------------------------------------------- peercert(Socket) -> peercert(Socket, []). @@ -342,9 +345,9 @@ select_part(plain, {ok, Cert}, Opts) -> end. %%-------------------------------------------------------------------- -%% Function: peername(Socket) -> {ok, {Address, Port}} | {error, Reason} +-spec peername(#sslsocket{}) -> {ok, {tuple(), port_num()}} | {error, reason()}. %% -%% Description: +%% Description: same as inet:peername/1. %%-------------------------------------------------------------------- peername(#sslsocket{fd = new_ssl, pid = Pid}) -> ssl_connection:peername(Pid); @@ -354,9 +357,10 @@ peername(#sslsocket{} = Socket) -> ssl_broker:peername(Socket). %%-------------------------------------------------------------------- -%% Function: cipher_suites() -> -%% -%% Description: +-spec cipher_suites() -> [erl_cipher_suite()]. +-spec cipher_suites(erlang | openssl) -> [erl_cipher_suite()] | [string()]. + +%% Description: Returns all supported cipher suites. %%-------------------------------------------------------------------- cipher_suites() -> cipher_suites(erlang). @@ -370,7 +374,7 @@ cipher_suites(openssl) -> [ssl_cipher:openssl_suite_name(S) || S <- ssl_cipher:suites(Version)]. %%-------------------------------------------------------------------- -%% Function: getopts(Socket, OptTags) -> {ok, Options} | {error, Reason} +-spec getopts(#sslsocket{}, [atom()]) -> {ok, [{atom(), term()}]}| {error, reason()}. %% %% Description: %%-------------------------------------------------------------------- @@ -383,7 +387,7 @@ getopts(#sslsocket{} = Socket, Options) -> ssl_broker:getopts(Socket, Options). %%-------------------------------------------------------------------- -%% Function: setopts(Socket, Options) -> ok | {error, Reason} +-spec setopts(#sslsocket{}, [{atom(), term()}]) -> ok | {error, reason()}. %% %% Description: %%-------------------------------------------------------------------- @@ -398,8 +402,8 @@ setopts(#sslsocket{} = Socket, Options) -> ssl_broker:setopts(Socket, Options). %%--------------------------------------------------------------- -%% Function: shutdown(Socket, How) -> ok | {error, Reason} -%% +-spec shutdown(#sslsocket{}, read | write | read_write) -> ok | {error, reason()}. +%% %% Description: Same as gen_tcp:shutdown/2 %%-------------------------------------------------------------------- shutdown(#sslsocket{pid = {ListenSocket, #config{cb={CbMod,_, _, _}}}, fd = new_ssl}, How) -> @@ -408,8 +412,8 @@ shutdown(#sslsocket{pid = Pid, fd = new_ssl}, How) -> ssl_connection:shutdown(Pid, How). %%-------------------------------------------------------------------- -%% Function: sockname(Socket) -> {ok, {Address, Port}} | {error, Reason} -%% +-spec sockname(#sslsocket{}) -> {ok, {tuple(), port_num()}} | {error, reason()}. +%% %% Description: Same as inet:sockname/1 %%-------------------------------------------------------------------- sockname(#sslsocket{fd = new_ssl, pid = {ListenSocket, _}}) -> @@ -423,9 +427,9 @@ sockname(#sslsocket{} = Socket) -> ssl_broker:sockname(Socket). %%--------------------------------------------------------------- -%% Function: seed(Data) -> ok | {error, edata} +-spec seed(term()) ->term(). %% -%% Description: +%% Description: Only used by old ssl. %%-------------------------------------------------------------------- %% TODO: crypto:seed ? seed(Data) -> @@ -433,20 +437,17 @@ seed(Data) -> ssl_server:seed(Data). %%--------------------------------------------------------------- -%% Function: session_id(Socket) -> {ok, PropList} | {error, Reason} +-spec session_info(#sslsocket{}) -> {ok, list()} | {error, reason()}. %% -%% Description: +%% Description: Returns list of session info currently [{session_id, session_id(), +%% {cipher_suite, cipher_suite()}] %%-------------------------------------------------------------------- session_info(#sslsocket{pid = Pid, fd = new_ssl}) -> ssl_connection:session_info(Pid). %%--------------------------------------------------------------- -%% Function: versions() -> [{SslAppVer, SupportedSslVer, AvailableSslVsn}] -%% -%% SslAppVer = string() - t.ex: ssl-4.0 -%% SupportedSslVer = [SslVer] -%% AvailableSslVsn = [SSLVer] -%% SSLVer = sslv3 | tlsv1 | 'tlsv1.1' +-spec versions() -> [{{ssl_app, string()}, {supported, [tls_version()]}, + {available, [tls_version()]}}]. %% %% Description: Returns a list of relevant versions. %%-------------------------------------------------------------------- @@ -456,7 +457,11 @@ versions() -> AvailableVsns = ?DEFAULT_SUPPORTED_VERSIONS, [{ssl_app, ?VSN}, {supported, SupportedVsns}, {available, AvailableVsns}]. - +%%--------------------------------------------------------------- +-spec renegotiate(#sslsocket{}) -> ok | {error, reason()}. +%% +%% Description: +%%-------------------------------------------------------------------- renegotiate(#sslsocket{pid = Pid, fd = new_ssl}) -> ssl_connection:renegotiation(Pid). @@ -660,7 +665,7 @@ validate_option(secure_renegotiate, Value) when Value == true; Value == false -> Value; validate_option(renegotiate_at, Value) when is_integer(Value) -> - min(Value, ?DEFAULT_RENEGOTIATE_AT); + erlang:min(Value, ?DEFAULT_RENEGOTIATE_AT); validate_option(debug, Value) when is_list(Value); Value == true -> Value; @@ -890,10 +895,6 @@ version() -> end, {ok, {SSLVsn, CompVsn, LibVsn}}. -min(N,M) when N < M -> - N; -min(_, M) -> - M. %% Only used to remove exit messages from old ssl %% First is a nonsense clause to provide some diff --git a/lib/ssl/src/ssl_alert.erl b/lib/ssl/src/ssl_alert.erl index db9a883654..eb1228afa4 100644 --- a/lib/ssl/src/ssl_alert.erl +++ b/lib/ssl/src/ssl_alert.erl @@ -32,6 +32,17 @@ -export([alert_txt/1, reason_code/2]). +%%==================================================================== +%% Internal application API +%%==================================================================== +%%-------------------------------------------------------------------- +-spec reason_code(#alert{}, client | server) -> closed | esslconnect | + esslaccept | string(). +%% +%% Description: Returns the error reason that will be returned to the +%% user. +%%-------------------------------------------------------------------- + reason_code(#alert{description = ?CLOSE_NOTIFY}, _) -> closed; reason_code(#alert{description = ?HANDSHAKE_FAILURE}, client) -> @@ -41,10 +52,19 @@ reason_code(#alert{description = ?HANDSHAKE_FAILURE}, server) -> reason_code(#alert{description = Description}, _) -> description_txt(Description). +%%-------------------------------------------------------------------- +-spec alert_txt(#alert{}) -> string(). +%% +%% Description: Returns the error string for given alert. +%%-------------------------------------------------------------------- + alert_txt(#alert{level = Level, description = Description, where = {Mod,Line}}) -> Mod ++ ":" ++ integer_to_list(Line) ++ ":" ++ level_txt(Level) ++" "++ description_txt(Description). +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- level_txt(?WARNING) -> "Warning:"; level_txt(?FATAL) -> @@ -96,8 +116,3 @@ description_txt(?USER_CANCELED) -> "user canceled"; description_txt(?NO_RENEGOTIATION) -> "no renegotiation". - - - - - diff --git a/lib/ssl/src/ssl_app.erl b/lib/ssl/src/ssl_app.erl index 6ca1c42631..d9a354086d 100644 --- a/lib/ssl/src/ssl_app.erl +++ b/lib/ssl/src/ssl_app.erl @@ -27,14 +27,16 @@ -export([start/2, stop/1]). -%% start/2(Type, StartArgs) -> {ok, Pid} | {ok, Pid, State} | -%% {error, Reason} -%% +%%-------------------------------------------------------------------- +-spec start(normal | {takeover, node()} | {failover, node()}, list()) -> + {ok, pid()} | {ok, pid(), term()} | {error, term()}. +%%-------------------------------------------------------------------- start(_Type, _StartArgs) -> ssl_sup:start_link(). -%% stop(State) -> void() -%% +%-------------------------------------------------------------------- +-spec stop(term())-> ok. +%%-------------------------------------------------------------------- stop(_State) -> ok. diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl index 9aa31ae8a4..8a79f75725 100644 --- a/lib/ssl/src/ssl_certificate.erl +++ b/lib/ssl/src/ssl_certificate.erl @@ -46,6 +46,14 @@ %% Internal application API %%==================================================================== +%%-------------------------------------------------------------------- +-spec trusted_cert_and_path([der_cert()], certdb_ref(), boolean()) -> + {der_cert(), [der_cert()], list()}. +%% +%% Description: Extracts the root cert (if not presents tries to +%% look it up, if not found {bad_cert, unknown_ca} will be added verification +%% errors. Returns {RootCert, Path, VerifyErrors} +%%-------------------------------------------------------------------- trusted_cert_and_path(CertChain, CertDbRef, Verify) -> [Cert | RestPath] = lists:reverse(CertChain), {ok, OtpCert} = public_key:pkix_decode_cert(Cert, otp), @@ -84,19 +92,31 @@ trusted_cert_and_path(CertChain, CertDbRef, Verify) -> end end. - +%%-------------------------------------------------------------------- +-spec certificate_chain(undefined | binary(), certdb_ref()) -> + {error, no_cert} | [der_cert()]. +%% +%% Description: Return the certificate chain to send to peer. +%%-------------------------------------------------------------------- certificate_chain(undefined, _CertsDbRef) -> {error, no_cert}; certificate_chain(OwnCert, CertsDbRef) -> {ok, ErlCert} = public_key:pkix_decode_cert(OwnCert, otp), certificate_chain(ErlCert, OwnCert, CertsDbRef, [OwnCert]). - +%%-------------------------------------------------------------------- +-spec file_to_certificats(string()) -> [der_cert()]. +%% +%% Description: Return list of DER encoded certificates. +%%-------------------------------------------------------------------- file_to_certificats(File) -> {ok, List} = ssl_manager:cache_pem_file(File), [Bin || {cert, Bin, not_encrypted} <- List]. - - -%% Validates ssl/tls specific extensions +%%-------------------------------------------------------------------- +-spec validate_extensions([#'Extension'{}], term(), [#'Extension'{}], + boolean(), list(), client | server) -> {[#'Extension'{}], term(), list()}. +%% +%% Description: Validates ssl/tls specific extensions +%%-------------------------------------------------------------------- validate_extensions([], ValidationState, UnknownExtensions, _, AccErr, _) -> {UnknownExtensions, ValidationState, AccErr}; @@ -119,21 +139,42 @@ validate_extensions([Extension | Rest], ValidationState, UnknownExtensions, validate_extensions(Rest, ValidationState, [Extension | UnknownExtensions], Verify, AccErr, Role). +%%-------------------------------------------------------------------- +-spec is_valid_key_usage(list(), term()) -> boolean(). +%% +%% Description: Checks if Use is a valid key usage. +%%-------------------------------------------------------------------- is_valid_key_usage(KeyUse, Use) -> lists:member(Use, KeyUse). - select_extension(_, []) -> +%%-------------------------------------------------------------------- +-spec select_extension(term(), list()) -> undefined | #'Extension'{}. +%% +%% Description: Selects the extension identified by Id if present in +%% a list of extensions. +%%-------------------------------------------------------------------- +select_extension(_, []) -> undefined; select_extension(Id, [#'Extension'{extnID = Id} = Extension | _]) -> Extension; select_extension(Id, [_ | Extensions]) -> select_extension(Id, Extensions). +%%-------------------------------------------------------------------- +-spec extensions_list(asn1_NOVALUE | list()) -> list(). +%% +%% Description: Handles that +%%-------------------------------------------------------------------- extensions_list(asn1_NOVALUE) -> []; extensions_list(Extensions) -> Extensions. +%%-------------------------------------------------------------------- +-spec signature_type(term()) -> rsa | dsa . +%% +%% Description: +%%-------------------------------------------------------------------- signature_type(RSA) when RSA == ?sha1WithRSAEncryption; RSA == ?md5WithRSAEncryption -> rsa; diff --git a/lib/ssl/src/ssl_certificate_db.erl b/lib/ssl/src/ssl_certificate_db.erl index b8c3c6f6b7..e953821057 100644 --- a/lib/ssl/src/ssl_certificate_db.erl +++ b/lib/ssl/src/ssl_certificate_db.erl @@ -22,7 +22,7 @@ %%---------------------------------------------------------------------- -module(ssl_certificate_db). - +-include("ssl_internal.hrl"). -include_lib("public_key/include/public_key.hrl"). -export([create/0, remove/1, add_trusted_certs/3, @@ -34,8 +34,7 @@ %%==================================================================== %%-------------------------------------------------------------------- -%% Function: create() -> Db -%% Db = term() - Reference to the crated database +-spec create() -> certdb_ref(). %% %% Description: Creates a new certificate db. %% Note: lookup_trusted_cert/3 may be called from any process but only @@ -47,8 +46,7 @@ create() -> ets:new(ssl_pid_to_file, [bag, private])]. %%-------------------------------------------------------------------- -%% Function: delete(Db) -> _ -%% Db = Database refererence as returned by create/0 +-spec remove(certdb_ref()) -> term(). %% %% Description: Removes database db %%-------------------------------------------------------------------- @@ -56,11 +54,10 @@ remove(Dbs) -> lists:foreach(fun(Db) -> true = ets:delete(Db) end, Dbs). %%-------------------------------------------------------------------- -%% Function: lookup_trusted_cert(Ref, SerialNumber, Issuer) -> {BinCert,DecodedCert} -%% Ref = ref() +-spec lookup_trusted_cert(reference(), serialnumber(), issuer()) -> {der_cert(), #'OTPCertificate'{}}. + %% SerialNumber = integer() %% Issuer = {rdnSequence, IssuerAttrs} -%% BinCert = binary() %% %% Description: Retrives the trusted certificate identified by %% <SerialNumber, Issuer>. Ref is used as it is specified @@ -78,11 +75,7 @@ lookup_cached_certs(File) -> ets:lookup(certificate_db_name(), {file, File}). %%-------------------------------------------------------------------- -%% Function: add_trusted_certs(Pid, File, Db) -> {ok, Ref} -%% Pid = pid() -%% File = string() -%% Db = Database refererence as returned by create/0 -%% Ref = ref() +-spec add_trusted_certs(pid(), string(), certdb_ref()) -> {ok, certdb_ref()}. %% %% Description: Adds the trusted certificates from file <File> to the %% runtime database. Returns Ref that should be handed to lookup_trusted_cert @@ -103,7 +96,7 @@ add_trusted_certs(Pid, File, [CertsDb, FileToRefDb, PidToFileDb]) -> {ok, Ref}. %%-------------------------------------------------------------------- -%% Function: cache_pem_file(Pid, File, Db) -> FileContent +-spec cache_pem_file(pid(), string(), certdb_ref()) -> term(). %% %% Description: Cache file as binary in DB %%-------------------------------------------------------------------- @@ -114,7 +107,8 @@ cache_pem_file(Pid, File, [CertsDb, _FileToRefDb, PidToFileDb]) -> Res. %%-------------------------------------------------------------------- -%% Function: remove_trusted_certs(Pid, Db) -> _ +-spec remove_trusted_certs(pid(), certdb_ref()) -> term(). + %% %% Description: Removes trusted certs originating from %% the file associated to Pid from the runtime database. @@ -144,11 +138,9 @@ remove_trusted_certs(Pid, [CertsDb, FileToRefDb, PidToFileDb]) -> end. %%-------------------------------------------------------------------- -%% Function: issuer_candidate() -> {Key, Candidate} | no_more_candidates +-spec issuer_candidate(no_candidate | cert_key()) -> + {cert_key(), der_cert()} | no_more_candidates. %% -%% Candidate -%% -%% %% Description: If a certificat does not define its issuer through %% the extension 'ce-authorityKeyIdentifier' we can %% try to find the issuer in the database over known diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl index 2a71df8ee1..a6e80047c2 100644 --- a/lib/ssl/src/ssl_cipher.erl +++ b/lib/ssl/src/ssl_cipher.erl @@ -40,11 +40,8 @@ -compile(inline). %%-------------------------------------------------------------------- -%% Function: security_parameters(CipherSuite, SecParams) -> -%% #security_parameters{} -%% -%% CipherSuite - as defined in ssl_cipher.hrl -%% SecParams - #security_parameters{} +-spec security_parameters(erl_cipher_suite(), #security_parameters{}) -> + #security_parameters{}. %% %% Description: Returns a security parameters record where the %% cipher values has been updated according to <CipherSuite> @@ -63,15 +60,11 @@ security_parameters(CipherSuite, SecParams) -> hash_size = hash_size(Hash)}. %%-------------------------------------------------------------------- -%% Function: cipher(Method, CipherState, Mac, Data) -> -%% {Encrypted, UpdateCipherState} -%% -%% Method - integer() (as defined in ssl_cipher.hrl) -%% CipherState, UpdatedCipherState - #cipher_state{} -%% Data, Encrypted - binary() +-spec cipher(cipher_enum(), #cipher_state{}, binary(), binary()) -> + {binary(), #cipher_state{}}. %% -%% Description: Encrypts the data and the mac using method, updating -%% the cipher state +%% Description: Encrypts the data and the MAC using chipher described +%% by cipher_enum() and updating the cipher state %%------------------------------------------------------------------- cipher(?NULL, CipherState, <<>>, Fragment) -> GenStreamCipherList = [Fragment, <<>>], @@ -125,15 +118,11 @@ block_cipher(Fun, BlockSz, #cipher_state{key=Key, iv=IV} = CS0, {T, CS0#cipher_state{iv=NextIV}}. %%-------------------------------------------------------------------- -%% Function: decipher(Method, CipherState, Mac, Data, Version) -> -%% {Decrypted, UpdateCipherState} -%% -%% Method - integer() (as defined in ssl_cipher.hrl) -%% CipherState, UpdatedCipherState - #cipher_state{} -%% Data, Encrypted - binary() +-spec decipher(cipher_enum(), integer(), #cipher_state{}, binary(), tls_version()) -> + {binary(), #cipher_state{}}. %% -%% Description: Decrypts the data and the mac using method, updating -%% the cipher state +%% Description: Decrypts the data and the MAC using cipher described +%% by cipher_enum() and updating the cipher state. %%------------------------------------------------------------------- decipher(?NULL, _HashSz, CipherState, Fragment, _) -> {Fragment, <<>>, CipherState}; @@ -192,10 +181,7 @@ block_decipher(Fun, #cipher_state{key=Key, iv=IV} = CipherState0, end. %%-------------------------------------------------------------------- -%% Function: suites(Version) -> [Suite] -%% -%% Version = version() -%% Suite = binary() from ssl_cipher.hrl +-spec suites(tls_version()) -> [cipher_suite()]. %% %% Description: Returns a list of supported cipher suites. %%-------------------------------------------------------------------- @@ -205,19 +191,9 @@ suites({3, N}) when N == 1; N == 2 -> ssl_tls1:suites(). %%-------------------------------------------------------------------- -%% Function: suite_definition(CipherSuite) -> -%% {KeyExchange, Cipher, Hash} -%% -%% -%% CipherSuite - as defined in ssl_cipher.hrl -%% KeyExchange - rsa | dh_anon | dhe_dss | dhe_rsa | kerb5 -%% -%% Cipher - null | rc4_128 | idea_cbc | des_cbc | '3des_ede_cbc' -%% des40_cbc | aes_128_cbc | aes_256_cbc -%% Hash - null | md5 | sha +-spec suite_definition(cipher_suite()) -> erl_cipher_suite(). %% -%% Description: Returns a security parameters tuple where the -%% cipher values has been updated according to <CipherSuite> +%% Description: Return erlang cipher suite definition. %% Note: Currently not supported suites are commented away. %% They should be supported or removed in the future. %%------------------------------------------------------------------- @@ -261,6 +237,12 @@ suite_definition(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA) -> suite_definition(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA) -> {dhe_rsa, aes_256_cbc, sha}. +%%-------------------------------------------------------------------- +-spec suite(erl_cipher_suite()) -> cipher_suite(). +%% +%% Description: Return TLS cipher suite definition. +%%-------------------------------------------------------------------- + %% TLS v1.1 suites %%suite({rsa, null, md5}) -> %% ?TLS_RSA_WITH_NULL_MD5; @@ -309,7 +291,11 @@ suite({dhe_rsa, aes_256_cbc, sha}) -> %% suite({dh_anon, aes_256_cbc, sha}) -> %% ?TLS_DH_anon_WITH_AES_256_CBC_SHA. - +%%-------------------------------------------------------------------- +-spec openssl_suite(openssl_cipher_suite()) -> cipher_suite(). +%% +%% Description: Return TLS cipher suite definition. +%%-------------------------------------------------------------------- %% translate constants <-> openssl-strings openssl_suite("DHE-RSA-AES256-SHA") -> ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA; @@ -339,7 +325,11 @@ 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. - +%%-------------------------------------------------------------------- +-spec openssl_suite_name(cipher_suite()) -> openssl_cipher_suite(). +%% +%% Description: Return openssl cipher suite name. +%%------------------------------------------------------------------- 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) -> @@ -372,6 +362,11 @@ openssl_suite_name(?TLS_RSA_WITH_DES_CBC_SHA) -> openssl_suite_name(Cipher) -> suite_definition(Cipher). +%%-------------------------------------------------------------------- +-spec filter(undefined | binary(), [cipher_suite()]) -> [cipher_suite()]. +%% +%% Description: . +%%------------------------------------------------------------------- filter(undefined, Ciphers) -> Ciphers; filter(DerCert, Ciphers) -> diff --git a/lib/ssl/src/ssl_cipher.hrl b/lib/ssl/src/ssl_cipher.hrl index 80fe527f45..19de709d9c 100644 --- a/lib/ssl/src/ssl_cipher.hrl +++ b/lib/ssl/src/ssl_cipher.hrl @@ -26,6 +26,14 @@ -ifndef(ssl_cipher). -define(ssl_cipher, true). +-type cipher() :: null |rc4_128 | idea_cbc | des40_cbc | des_cbc | '3des_ede_cbc' + | aes_128_cbc | aes_256_cbc. +-type hash() :: sha | md5. +-type erl_cipher_suite() :: {key_algo(), cipher(), hash()}. +-type cipher_suite() :: binary(). +-type cipher_enum() :: integer(). +-type openssl_cipher_suite() :: string(). + %%% SSL cipher protocol %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -define(CHANGE_CIPHER_SPEC_PROTO, 1). % _PROTO to not clash with % SSL record protocol diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index 07fa101ed4..5b4b129e30 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -98,12 +98,17 @@ #'DHParameter'{prime = ?DEFAULT_DIFFIE_HELLMAN_PRIME, base = ?DEFAULT_DIFFIE_HELLMAN_GENERATOR}). +-type state_name() :: hello | abbreviated | certify | cipher | connection. +-type gen_fsm_state_return() :: {next_state, state_name(), #state{}} | + {next_state, state_name(), #state{}, timeout()} | + {stop, term(), #state{}}. + %%==================================================================== %% Internal application API %%==================================================================== %%-------------------------------------------------------------------- -%% Function: send(Pid, Data) -> ok | {error, Reason} +-spec send(pid(), iolist()) -> ok | {error, reason()}. %% %% Description: Sends data over the ssl connection %%-------------------------------------------------------------------- @@ -112,15 +117,16 @@ send(Pid, Data) -> erlang:iolist_to_binary(Data)}, infinity). %%-------------------------------------------------------------------- -%% Function: recv(Socket, Length Timeout) -> {ok, Data} | {error, reason} +-spec recv(pid(), integer(), timeout()) -> + {ok, binary() | list()} | {error, reason()}. %% %% Description: Receives data when active = false %%-------------------------------------------------------------------- recv(Pid, Length, Timeout) -> sync_send_all_state_event(Pid, {recv, Length}, Timeout). %%-------------------------------------------------------------------- -%% Function: : connect(Host, Port, Socket, Options, -%% User, CbInfo, Timeout) -> {ok, Socket} +-spec connect(host(), port_num(), port(), list(), pid(), tuple(), timeout()) -> + {ok, #sslsocket{}} | {error, reason()}. %% %% Description: Connect to a ssl server. %%-------------------------------------------------------------------- @@ -132,8 +138,8 @@ connect(Host, Port, Socket, Options, User, CbInfo, Timeout) -> {error, ssl_not_started} end. %%-------------------------------------------------------------------- -%% Function: accept(Port, Socket, Opts, User, -%% CbInfo, Timeout) -> {ok, Socket} | {error, Reason} +-spec ssl_accept(port_num(), port(), list(), pid(), tuple(), timeout()) -> + {ok, #sslsocket{}} | {error, reason()}. %% %% Description: Performs accept on a ssl listen socket. e.i. performs %% ssl handshake. @@ -147,7 +153,7 @@ ssl_accept(Port, Socket, Opts, User, CbInfo, Timeout) -> end. %%-------------------------------------------------------------------- -%% Function: handshake(SslSocket, Timeout) -> ok | {error, Reason} +-spec handshake(#sslsocket{}, timeout()) -> ok | {error, reason()}. %% %% Description: Starts ssl handshake. %%-------------------------------------------------------------------- @@ -159,7 +165,8 @@ handshake(#sslsocket{pid = Pid}, Timeout) -> Error end. %-------------------------------------------------------------------- -%% Function: socket_control(Pid) -> {ok, SslSocket} | {error, Reason} +-spec socket_control(port(), pid(), atom()) -> + {ok, #sslsocket{}} | {error, reason()}. %% %% Description: Set the ssl process to own the accept socket %%-------------------------------------------------------------------- @@ -172,7 +179,7 @@ socket_control(Socket, Pid, CbModule) -> end. %%-------------------------------------------------------------------- -%% Function: close() -> ok +-spec close(pid()) -> ok | {error, reason()}. %% %% Description: Close a ssl connection %%-------------------------------------------------------------------- @@ -185,7 +192,7 @@ close(ConnectionPid) -> end. %%-------------------------------------------------------------------- -%% Function: shutdown(Socket, How) -> ok | {error, Reason} +-spec shutdown(pid(), atom()) -> ok | {error, reason()}. %% %% Description: Same as gen_tcp:shutdown/2 %%-------------------------------------------------------------------- @@ -193,7 +200,7 @@ shutdown(ConnectionPid, How) -> sync_send_all_state_event(ConnectionPid, {shutdown, How}). %%-------------------------------------------------------------------- -%% Function: new_user(ConnectionPid, User) -> ok | {error, Reason} +-spec new_user(pid(), pid()) -> ok | {error, reason()}. %% %% Description: Changes process that receives the messages when active = true %% or once. @@ -201,28 +208,28 @@ shutdown(ConnectionPid, How) -> new_user(ConnectionPid, User) -> sync_send_all_state_event(ConnectionPid, {new_user, User}). %%-------------------------------------------------------------------- -%% Function: sockname(ConnectionPid) -> {ok, {Address, Port}} | {error, Reason} +-spec sockname(pid()) -> {ok, {tuple(), port_num()}} | {error, reason()}. %% %% Description: Same as inet:sockname/1 %%-------------------------------------------------------------------- sockname(ConnectionPid) -> sync_send_all_state_event(ConnectionPid, sockname). %%-------------------------------------------------------------------- -%% Function: peername(ConnectionPid) -> {ok, {Address, Port}} | {error, Reason} +-spec peername(pid()) -> {ok, {tuple(), port_num()}} | {error, reason()}. %% %% Description: Same as inet:peername/1 %%-------------------------------------------------------------------- peername(ConnectionPid) -> sync_send_all_state_event(ConnectionPid, peername). %%-------------------------------------------------------------------- -%% Function: get_opts(ConnectionPid, OptTags) -> {ok, Options} | {error, Reason} +-spec get_opts(pid(), list()) -> {ok, list()} | {error, reason()}. %% %% Description: Same as inet:getopts/2 %%-------------------------------------------------------------------- get_opts(ConnectionPid, OptTags) -> sync_send_all_state_event(ConnectionPid, {get_opts, OptTags}). %%-------------------------------------------------------------------- -%% Function: setopts(Socket, Options) -> ok | {error, Reason} +-spec set_opts(pid(), list()) -> ok | {error, reason()}. %% %% Description: Same as inet:setopts/2 %%-------------------------------------------------------------------- @@ -230,8 +237,7 @@ set_opts(ConnectionPid, Options) -> sync_send_all_state_event(ConnectionPid, {set_opts, Options}). %%-------------------------------------------------------------------- -%% Function: info(ConnectionPid) -> {ok, {Protocol, CipherSuite}} | -%% {error, Reason} +-spec info(pid()) -> {ok, {atom(), tuple()}} | {error, reason()}. %% %% Description: Returns ssl protocol and cipher used for the connection %%-------------------------------------------------------------------- @@ -239,7 +245,7 @@ info(ConnectionPid) -> sync_send_all_state_event(ConnectionPid, info). %%-------------------------------------------------------------------- -%% Function: session_info(ConnectionPid) -> {ok, PropList} | {error, Reason} +-spec session_info(pid()) -> {ok, list()} | {error, reason()}. %% %% Description: Returns info about the ssl session %%-------------------------------------------------------------------- @@ -247,7 +253,7 @@ session_info(ConnectionPid) -> sync_send_all_state_event(ConnectionPid, session_info). %%-------------------------------------------------------------------- -%% Function: peercert(ConnectionPid) -> {ok, Cert} | {error, Reason} +-spec peer_certificate(pid()) -> {ok, binary()} | {error, reason()}. %% %% Description: Returns the peer cert %%-------------------------------------------------------------------- @@ -255,7 +261,7 @@ peer_certificate(ConnectionPid) -> sync_send_all_state_event(ConnectionPid, peer_certificate). %%-------------------------------------------------------------------- -%% Function: renegotiation(ConnectionPid) -> ok | {error, Reason} +-spec renegotiation(pid()) -> ok | {error, reason()}. %% %% Description: Starts a renegotiation of the ssl session. %%-------------------------------------------------------------------- @@ -267,7 +273,8 @@ renegotiation(ConnectionPid) -> %%==================================================================== %%-------------------------------------------------------------------- -%% Function: start_link() -> {ok,Pid} | ignore | {error,Error} +-spec start_link(atom(), host(), port_num(), port(), list(), pid(), tuple()) -> + {ok, pid()} | ignore | {error, reason()}. %% %% Description: Creates a gen_fsm process which calls Module:init/1 to %% initialize. To ensure a synchronized start-up procedure, this function @@ -281,10 +288,9 @@ start_link(Role, Host, Port, Socket, Options, User, CbInfo) -> %% gen_fsm callbacks %%==================================================================== %%-------------------------------------------------------------------- -%% Function: init(Args) -> {ok, StateName, State} | -%% {ok, StateName, State, Timeout} | -%% ignore | -%% {stop, StopReason} +-spec init(list()) -> {ok, state_name(), #state{}} + | {ok, state_name(), #state{}, timeout()} | + ignore | {stop, term()}. %% Description:Whenever a gen_fsm is started using gen_fsm:start/[3,4] or %% gen_fsm:start_link/3,4, this function is called by the new process to %% initialize. @@ -309,11 +315,7 @@ init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, end. %%-------------------------------------------------------------------- -%% Function: -%% state_name(Event, State) -> {next_state, NextStateName, NextState}| -%% {next_state, NextStateName, -%% NextState, Timeout} | -%% {stop, Reason, NewState} +%% -spec state_name(event(), #state{}) -> gen_fsm_state_return() %% %% Description:There should be one instance of this function for each %% possible state name. Whenever a gen_fsm receives an event sent @@ -322,6 +324,9 @@ init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, %% the event. It is also called if a timeout occurs. %% %%-------------------------------------------------------------------- +-spec hello(start | #hello_request{} | #client_hello{} | #server_hello{} | term(), + #state{}) -> gen_fsm_state_return(). +%%-------------------------------------------------------------------- hello(start, #state{host = Host, port = Port, role = client, ssl_options = SslOpts, transport_cb = Transport, socket = Socket, @@ -359,49 +364,30 @@ hello(#hello_request{}, #state{role = client} = State0) -> hello(#server_hello{cipher_suite = CipherSuite, compression_method = Compression} = Hello, - #state{session = Session0 = #session{session_id = OldId}, + #state{session = #session{session_id = OldId}, connection_states = ConnectionStates0, role = client, negotiated_version = ReqVersion, - host = Host, port = Port, renegotiation = {Renegotiation, _}, - ssl_options = SslOptions, - session_cache = Cache, - session_cache_cb = CacheCb} = State0) -> + ssl_options = SslOptions} = State0) -> case ssl_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of - {Version, NewId, ConnectionStates1} -> + {Version, NewId, ConnectionStates} -> {KeyAlgorithm, _, _} = ssl_cipher:suite_definition(CipherSuite), PremasterSecret = make_premaster_secret(ReqVersion, KeyAlgorithm), - State1 = State0#state{key_algorithm = KeyAlgorithm, - negotiated_version = Version, - connection_states = ConnectionStates1, - premaster_secret = PremasterSecret}, + State = State0#state{key_algorithm = KeyAlgorithm, + negotiated_version = Version, + connection_states = ConnectionStates, + premaster_secret = PremasterSecret}, case ssl_session:is_new(OldId, NewId) of true -> - Session = Session0#session{session_id = NewId, - cipher_suite = CipherSuite, - compression_method = Compression}, - {Record, State} = next_record(State1#state{session = Session}), - next_state(certify, Record, State); + handle_new_session(NewId, CipherSuite, Compression, State); false -> - Session = CacheCb:lookup(Cache, {{Host, Port}, NewId}), - case ssl_handshake:master_secret(Version, Session, - ConnectionStates1, client) of - {_, ConnectionStates2} -> - {Record, State} = - next_record(State1#state{ - connection_states = ConnectionStates2, - session = Session}), - next_state(abbreviated, Record, State); - #alert{} = Alert -> - handle_own_alert(Alert, Version, hello, State1), - {stop, normal, State1} - end + handle_resumed_session(NewId, State#state{connection_states = ConnectionStates}) end; #alert{} = Alert -> handle_own_alert(Alert, ReqVersion, hello, State0), @@ -431,7 +417,10 @@ hello(Hello = #client_hello{client_version = ClientVersion}, hello(Msg, State) -> handle_unexpected_message(Msg, hello, State). - +%%-------------------------------------------------------------------- +-spec abbreviated(#hello_request{} | #finished{} | term(), + #state{}) -> gen_fsm_state_return(). +%%-------------------------------------------------------------------- abbreviated(#hello_request{}, State0) -> {Record, State} = next_record(State0), next_state(hello, Record, State); @@ -477,6 +466,11 @@ abbreviated(#finished{verify_data = Data} = Finished, abbreviated(Msg, State) -> handle_unexpected_message(Msg, abbreviated, State). +%%-------------------------------------------------------------------- +-spec certify(#hello_request{} | #certificate{} | #server_key_exchange{} | + #certificate_request{} | #server_hello_done{} | #client_key_exchange{} | term(), + #state{}) -> gen_fsm_state_return(). +%%-------------------------------------------------------------------- certify(#hello_request{}, State0) -> {Record, State} = next_record(State0), next_state(hello, Record, State); @@ -646,6 +640,10 @@ certify(#client_key_exchange{exchange_keys = #client_diffie_hellman_public{ certify(Msg, State) -> handle_unexpected_message(Msg, certify, State). +%%-------------------------------------------------------------------- +-spec cipher(#hello_request{} | #certificate_verify{} | #finished{} | term(), + #state{}) -> gen_fsm_state_return(). +%%-------------------------------------------------------------------- cipher(#hello_request{}, State0) -> {Record, State} = next_record(State0), next_state(hello, Record, State); @@ -676,31 +674,14 @@ cipher(#finished{verify_data = Data} = Finished, role = Role, session = #session{master_secret = MasterSecret} = Session0, - tls_handshake_hashes = Hashes0, - connection_states = ConnectionStates0} = State) -> + tls_handshake_hashes = Hashes0} = State) -> case ssl_handshake:verify_connection(Version, Finished, opposite_role(Role), MasterSecret, Hashes0) of verified -> Session = register_session(Role, Host, Port, Session0), - case Role of - client -> - ConnectionStates = ssl_record:set_server_verify_data(current_both, Data, ConnectionStates0), - next_state_connection(cipher, ack_connection(State#state{session = Session, - connection_states = ConnectionStates})); - server -> - ConnectionStates1 = ssl_record:set_client_verify_data(current_read, Data, ConnectionStates0), - {ConnectionStates, Hashes} = - finalize_handshake(State#state{ - connection_states = ConnectionStates1, - session = Session}, cipher), - next_state_connection(cipher, ack_connection(State#state{connection_states = - ConnectionStates, - session = Session, - tls_handshake_hashes = - Hashes})) - end; + cipher_role(Role, Data, Session, State); #alert{} = Alert -> handle_own_alert(Alert, Version, cipher, State), {stop, normal, State} @@ -709,7 +690,10 @@ cipher(#finished{verify_data = Data} = Finished, cipher(Msg, State) -> handle_unexpected_message(Msg, cipher, State). - +%%-------------------------------------------------------------------- +-spec connection(#hello_request{} | #client_hello{} | term(), + #state{}) -> gen_fsm_state_return(). +%%-------------------------------------------------------------------- connection(#hello_request{}, #state{host = Host, port = Port, socket = Socket, ssl_options = SslOpts, @@ -736,30 +720,22 @@ connection(#client_hello{} = Hello, #state{role = server} = State) -> connection(Msg, State) -> handle_unexpected_message(Msg, connection, State). %%-------------------------------------------------------------------- -%% Function: -%% handle_event(Event, StateName, State) -> {next_state, NextStateName, -%% NextState} | -%% {next_state, NextStateName, -%% NextState, Timeout} | -%% {stop, Reason, NewState} +-spec handle_event(term(), state_name(), #state{}) -> gen_fsm_state_return(). +%% %% Description: Whenever a gen_fsm receives an event sent using %% gen_fsm:send_all_state_event/2, this function is called to handle -%% the event. +%% the event. Not currently used! %%-------------------------------------------------------------------- handle_event(_Event, StateName, State) -> {next_state, StateName, State}. %%-------------------------------------------------------------------- -%% Function: -%% handle_sync_event(Event, From, StateName, -%% State) -> {next_state, NextStateName, NextState} | -%% {next_state, NextStateName, NextState, -%% Timeout} | -%% {reply, Reply, NextStateName, NextState}| -%% {reply, Reply, NextStateName, NextState, -%% Timeout} | -%% {stop, Reason, NewState} | -%% {stop, Reason, Reply, NewState} +-spec handle_sync_event(term(), from(), state_name(), #state{}) -> + gen_fsm_state_return() | + {reply, reply(), state_name(), #state{}} | + {reply, reply(), state_name(), #state{}, timeout()} | + {stop, reason(), reply(), #state{}}. +%% %% Description: Whenever a gen_fsm receives an event sent using %% gen_fsm:sync_send_all_state_event/2,3, this function is called to handle %% the event. @@ -936,11 +912,11 @@ handle_sync_event(peer_certificate, _, StateName, {reply, {ok, Cert}, StateName, State}. %%-------------------------------------------------------------------- -%% Function: -%% handle_info(Info,StateName,State)-> {next_state, NextStateName, NextState}| -%% {next_state, NextStateName, NextState, -%% Timeout} | -%% {stop, Reason, NewState} +-spec handle_info(msg(),state_name(), #state{}) -> + {next_state, state_name(), #state{}}| + {next_state, state_name(), #state{}, timeout()} | + {stop, reason(), #state{}}. +%% %% Description: This function is called by a gen_fsm when it receives any %% other message than a synchronous or asynchronous event %% (or a system message). @@ -1000,7 +976,8 @@ handle_info(Msg, StateName, State) -> {next_state, StateName, State}. %%-------------------------------------------------------------------- -%% Function: terminate(Reason, StateName, State) -> void() +-spec terminate(reason(), state_name(), #state{}) -> term(). +%% %% Description:This function is called by a gen_fsm when it is about %% to terminate. It should be the opposite of Module:init/1 and do any %% necessary cleaning up. When it returns, the gen_fsm terminates with @@ -1027,7 +1004,8 @@ terminate(_Reason, _StateName, #state{transport_cb = Transport, Transport:close(Socket). %%-------------------------------------------------------------------- -%% Function: +-spec code_change(term(), state_name(), #state{}, list()) -> {ok, state_name(), #state{}}. +%% %% code_change(OldVsn, StateName, State, Extra) -> {ok, StateName, NewState} %% Description: Convert process state when code is changed %%-------------------------------------------------------------------- @@ -1039,24 +1017,17 @@ code_change(_OldVsn, StateName, State, _Extra) -> %%-------------------------------------------------------------------- start_fsm(Role, Host, Port, Socket, Opts, User, {CbModule, _,_, _} = CbInfo, Timeout) -> - case ssl_connection_sup:start_child([Role, Host, Port, Socket, - Opts, User, CbInfo]) of - {ok, Pid} -> - case socket_control(Socket, Pid, CbModule) of - {ok, SslSocket} -> - case handshake(SslSocket, Timeout) of - ok -> - {ok, SslSocket}; - {error, Reason} -> - {error, Reason} - end; - {error, Reason} -> - {error, Reason} - end; - {error, Reason} -> - {error, Reason} + try + {ok, Pid} = ssl_connection_sup:start_child([Role, Host, Port, Socket, + Opts, User, CbInfo]), + {ok, SslSocket} = socket_control(Socket, Pid, CbModule), + ok = handshake(SslSocket, Timeout), + {ok, SslSocket} + catch + error:{badmatch, {error, _} = Error} -> + Error end. - + ssl_init(SslOpts, Role) -> {ok, CertDbRef, CacheRef, OwnCert} = init_certificates(SslOpts, Role), PrivateKey = @@ -1267,6 +1238,33 @@ do_server_hello(#server_hello{cipher_suite = CipherSuite, {stop, normal, State0} end. +handle_new_session(NewId, CipherSuite, Compression, #state{session = Session0} = State0) -> + Session = Session0#session{session_id = NewId, + cipher_suite = CipherSuite, + compression_method = Compression}, + {Record, State} = next_record(State0#state{session = Session}), + next_state(certify, Record, State). + +handle_resumed_session(SessId, #state{connection_states = ConnectionStates0, + negotiated_version = Version, + host = Host, port = Port, + session_cache = Cache, + session_cache_cb = CacheCb} = State0) -> + Session = CacheCb:lookup(Cache, {{Host, Port}, SessId}), + case ssl_handshake:master_secret(Version, Session, + ConnectionStates0, client) of + {_, ConnectionStates1} -> + {Record, State} = + next_record(State0#state{ + connection_states = ConnectionStates1, + session = Session}), + next_state(abbreviated, Record, State); + #alert{} = Alert -> + handle_own_alert(Alert, Version, hello, State0), + {stop, normal, State0} + end. + + client_certify_and_key_exchange(#state{negotiated_version = Version} = State0) -> try do_client_certify_and_key_exchange(State0) of @@ -1354,9 +1352,7 @@ key_exchange(#state{role = server, key_algorithm = Algo, transport_cb = Transport } = State) when Algo == dhe_dss; - Algo == dhe_dss_export; - Algo == dhe_rsa; - Algo == dhe_rsa_export -> + Algo == dhe_rsa -> Keys = public_key:gen_key(Params), ConnectionState = @@ -1402,9 +1398,7 @@ key_exchange(#state{role = client, socket = Socket, transport_cb = Transport, tls_handshake_hashes = Hashes0} = State) when Algorithm == dhe_dss; - Algorithm == dhe_dss_export; - Algorithm == dhe_rsa; - Algorithm == dhe_rsa_export -> + Algorithm == dhe_rsa -> Msg = ssl_handshake:key_exchange(client, {dh, DhPubKey}), {BinMsg, ConnectionStates1, Hashes1} = encode_handshake(Msg, Version, ConnectionStates0, Hashes0), @@ -1545,6 +1539,21 @@ verify_dh_params(Signed, Hash, {?'id-dsa', PublicKey, PublicKeyParams}) -> public_key:verify_signature(Hash, none, Signed, PublicKey, PublicKeyParams). +cipher_role(client, Data, Session, #state{connection_states = ConnectionStates0} = State) -> + ConnectionStates = ssl_record:set_server_verify_data(current_both, Data, ConnectionStates0), + next_state_connection(cipher, ack_connection(State#state{session = Session, + connection_states = ConnectionStates})); + +cipher_role(server, Data, Session, #state{connection_states = ConnectionStates0} = State) -> + ConnectionStates1 = ssl_record:set_client_verify_data(current_read, Data, ConnectionStates0), + {ConnectionStates, Hashes} = + finalize_handshake(State#state{connection_states = ConnectionStates1, + session = Session}, cipher), + next_state_connection(cipher, ack_connection(State#state{connection_states = + ConnectionStates, + session = Session, + tls_handshake_hashes = + Hashes})). encode_alert(#alert{} = Alert, Version, ConnectionStates) -> ?DBG_TERM(Alert), ssl_record:encode_alert_record(Alert, Version, ConnectionStates). diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index f7e3e392ec..fcc30f6137 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -42,16 +42,15 @@ encode_handshake/3, init_hashes/0, update_hashes/2, decrypt_premaster_secret/2]). +-type tls_handshake() :: #client_hello{} | #server_hello{} | #server_hello_done{} | +#certificate{} | #client_key_exchange{} | #finished{} | #certificate_verify{}. + %%==================================================================== %% Internal application API %%==================================================================== %%-------------------------------------------------------------------- -%% Function: client_hello(Host, Port, ConnectionStates, SslOpts, Cert, Renegotiation) -> -%% #client_hello{} -%% Host -%% Port -%% ConnectionStates = #connection_states{} -%% SslOpts = #ssl_options{} +-spec client_hello(host(), port_num(), #connection_states{}, + #ssl_options{}, binary(), boolean()) -> #client_hello{}. %% %% Description: Creates a client hello message. %%-------------------------------------------------------------------- @@ -79,13 +78,8 @@ client_hello(Host, Port, ConnectionStates, #ssl_options{versions = Versions, }. %%-------------------------------------------------------------------- -%% Function: server_hello(SessionId, Version, -%% ConnectionStates, Renegotiation) -> #server_hello{} -%% SessionId -%% Version -%% ConnectionStates -%% Renegotiation -%% +-spec server_hello(session_id(), tls_version(), #connection_states{}, + boolean()) -> #server_hello{}. %% %% Description: Creates a server hello message. %%-------------------------------------------------------------------- @@ -103,7 +97,7 @@ server_hello(SessionId, Version, ConnectionStates, Renegotiation) -> }. %%-------------------------------------------------------------------- -%% Function: hello_request() -> #hello_request{} +-spec hello_request() -> #hello_request{}. %% %% Description: Creates a hello request message sent by server to %% trigger renegotiation. @@ -112,15 +106,12 @@ hello_request() -> #hello_request{}. %%-------------------------------------------------------------------- -%% Function: hello(Hello, Info, Renegotiation) -> -%% {Version, Id, NewConnectionStates} | -%% #alert{} -%% -%% Hello = #client_hello{} | #server_hello{} -%% Info = ConnectionStates | {Port, #ssl_options{}, Session, -%% Cahce, CahceCb, ConnectionStates} -%% ConnectionStates = #connection_states{} -%% Renegotiation = boolean() +-spec hello(#server_hello{} | #client_hello{}, #ssl_options{}, + #connection_states{} | {port_num(), #session{}, cache_ref(), + atom(), #connection_states{}, binary()}, + boolean()) -> {tls_version(), session_id(), #connection_states{}}| + {tls_version(), {resumed | new, session_id()}, + #connection_states{}} | #alert{}. %% %% Description: Handles a recieved hello message %%-------------------------------------------------------------------- @@ -183,12 +174,9 @@ hello(#client_hello{client_version = ClientVersion, random = Random, end. %%-------------------------------------------------------------------- -%% Function: certify(Certs, CertDbRef, MaxPathLen) -> -%% {PeerCert, PublicKeyInfo} | #alert{} -%% -%% Certs = #certificate{} -%% CertDbRef = reference() -%% MaxPathLen = integer() | nolimit +-spec certify(#certificate{}, term(), integer() | nolimit, + verify_peer | verify_none, fun(), fun(), + client | server) -> {der_cert(), public_key_info()} | #alert{}. %% %% Description: Handles a certificate handshake message %%-------------------------------------------------------------------- @@ -244,10 +232,7 @@ certify(#certificate{asn1_certificates = ASN1Certs}, CertDbRef, end. %%-------------------------------------------------------------------- -%% Function: certificate(OwnCert, CertDbRef, Role) -> #certificate{} -%% -%% OwnCert = binary() -%% CertDbRef = term() as returned by ssl_certificate_db:create() +-spec certificate(der_cert(), term(), client | server) -> #certificate{}. %% %% Description: Creates a certificate message. %%-------------------------------------------------------------------- @@ -273,10 +258,10 @@ certificate(OwnCert, CertDbRef, server) -> end. %%-------------------------------------------------------------------- -%% Function: client_certificate_verify(Cert, ConnectionStates) -> -%% #certificate_verify{} | ignore -%% Cert = #'OTPcertificate'{} -%% ConnectionStates = #connection_states{} +-spec client_certificate_verify(undefined | der_cert(), binary(), + tls_version(), key_algo(), private_key(), + {binary(), binary()}) -> + #certificate_verify{} | ignore. %% %% Description: Creates a certificate_verify message, called by the client. %%-------------------------------------------------------------------- @@ -298,10 +283,9 @@ client_certificate_verify(OwnCert, MasterSecret, Version, Algorithm, end. %%-------------------------------------------------------------------- -%% Function: certificate_verify(Signature, PublicKeyInfo) -> valid | #alert{} -%% -%% Signature = binary() -%% PublicKeyInfo = {Algorithm, PublicKey, PublicKeyParams} +-spec certificate_verify(binary(), public_key_info(), tls_version(), + binary(), key_algo(), + {binary(), binary()}) -> valid | #alert{}. %% %% Description: Checks that the certificate_verify message is valid. %%-------------------------------------------------------------------- @@ -320,13 +304,19 @@ certificate_verify(Signature, {_, PublicKey, _}, Version, end; certificate_verify(Signature, {_, PublicKey, PublicKeyParams}, Version, MasterSecret, dhe_dss = Algorithm, {_, Hashes0}) -> - Hashes = calc_certificate_verify(Version, MasterSecret, - Algorithm, Hashes0), - public_key:verify_signature(Hashes, sha, Signature, PublicKey, PublicKeyParams). + Hashes = calc_certificate_verify(Version, MasterSecret, + Algorithm, Hashes0), + case public_key:verify_signature(Hashes, none, Signature, PublicKey, PublicKeyParams) of + true -> + valid; + false -> + ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE) + end. + %%-------------------------------------------------------------------- -%% Function: certificate_request(ConnectionStates, CertDbRef) -> -%% #certificate_request{} +-spec certificate_request(#connection_states{}, certdb_ref()) -> + #certificate_request{}. %% %% Description: Creates a certificate_request message, called by the server. %%-------------------------------------------------------------------- @@ -342,11 +332,12 @@ certificate_request(ConnectionStates, CertDbRef) -> }. %%-------------------------------------------------------------------- -%% Function: key_exchange(Role, Secret, Params) -> -%% #client_key_exchange{} | #server_key_exchange{} -%% -%% Secret - -%% Params - +-spec key_exchange(client | server, + {premaster_secret, binary(), public_key_info()} | + {dh, binary()} | + {dh, binary(), #'DHParameter'{}, key_algo(), + binary(), binary(), private_key()}) -> + #client_key_exchange{} | #server_key_exchange{}. %% %% Description: Creates a keyexchange message. %%-------------------------------------------------------------------- @@ -382,15 +373,9 @@ key_exchange(server, {dh, {<<?UINT32(Len), PublicKey:Len/binary>>, _}, signed_params = Signed}. %%-------------------------------------------------------------------- -%% Function: master_secret(Version, Session/PremasterSecret, -%% ConnectionStates, Role) -> -%% {MasterSecret, NewConnectionStates} | #alert{} -%% Version = #protocol_version{} -%% Session = #session{} (session contains master secret) -%% PremasterSecret = binary() -%% ConnectionStates = #connection_states{} -%% Role = client | server -%% +-spec master_secret(tls_version(), #session{} | binary(), #connection_states{}, + client | server) -> {binary(), #connection_states{}} | #alert{}. +%% %% Description: Sets or calculates the master secret and calculate keys, %% updating the pending connection states. The Mastersecret and the update %% connection states are returned or an alert if the calculation fails. @@ -427,9 +412,8 @@ master_secret(Version, PremasterSecret, ConnectionStates, Role) -> end. %%-------------------------------------------------------------------- -%% Function: finished(Version, Role, MacSecret, Hashes) -> #finished{} -%% -%% ConnectionStates = #connection_states{} +-spec finished(tls_version(), client | server, binary(), {binary(), binary()}) -> + #finished{}. %% %% Description: Creates a handshake finished message %%------------------------------------------------------------------- @@ -438,15 +422,8 @@ finished(Version, Role, MasterSecret, {Hashes, _}) -> % use the current hashes calc_finished(Version, Role, MasterSecret, Hashes)}. %%-------------------------------------------------------------------- -%% Function: verify_connection(Finished, Role, -%% MasterSecret, Hashes) -> verified | #alert{} -%% -%% Finished = #finished{} -%% Role = client | server - the role of the process that sent the finished -%% message. -%% MasterSecret = binary() -%% Hashes = binary() - {md5_hash, sha_hash} -%% +-spec verify_connection(tls_version(), #finished{}, client | server, binary(), + {binary(), binary()}) -> verified | #alert{}. %% %% Description: Checks the ssl handshake finished message to verify %% the connection. @@ -462,17 +439,18 @@ verify_connection(Version, #finished{verify_data = Data}, _E -> ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE) end. - +%%-------------------------------------------------------------------- +-spec server_hello_done() -> #server_hello_done{}. +%% +%% Description: Creates a server hello done message. +%%-------------------------------------------------------------------- server_hello_done() -> #server_hello_done{}. %%-------------------------------------------------------------------- -%% Function: encode_handshake(HandshakeRec) -> BinHandshake -%% HandshakeRec = #client_hello | #server_hello{} | server_hello_done | -%% #certificate{} | #client_key_exchange{} | #finished{} | -%% #client_certify_request{} +-spec encode_handshake(tls_handshake(), tls_version(), key_algo()) -> binary(). %% -%% encode a handshake packet to binary +%% Description: Encode a handshake packet to binary %%-------------------------------------------------------------------- encode_handshake(Package, Version, KeyAlg) -> SigAlg = sig_alg(KeyAlg), @@ -481,12 +459,11 @@ encode_handshake(Package, Version, KeyAlg) -> [MsgType, ?uint24(Len), Bin]. %%-------------------------------------------------------------------- -%% Function: get_tls_handshake(Data, Buffer) -> Result -%% Result = {[#handshake{}], [Raw], NewBuffer} -%% Data = Buffer = NewBuffer = Raw = binary() +-spec get_tls_handshake(binary(), binary(), key_algo(), tls_version()) -> + {[tls_handshake()], [binary()], binary()}. %% %% Description: Given buffered and new data from ssl_record, collects -%% and returns it as a list of #handshake, also returns leftover +%% and returns it as a list of handshake messages, also returns leftover %% data. %%-------------------------------------------------------------------- get_tls_handshake(Data, <<>>, KeyAlg, Version) -> @@ -495,6 +472,9 @@ get_tls_handshake(Data, Buffer, KeyAlg, Version) -> get_tls_handshake_aux(list_to_binary([Buffer, Data]), KeyAlg, Version, []). +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- get_tls_handshake_aux(<<?BYTE(Type), ?UINT24(Length), Body:Length/binary,Rest/binary>>, KeyAlg, Version, Acc) -> @@ -504,9 +484,6 @@ get_tls_handshake_aux(<<?BYTE(Type), ?UINT24(Length), get_tls_handshake_aux(Data, _KeyAlg, _Version, Acc) -> {lists:reverse(Acc), Data}. -%%-------------------------------------------------------------------- -%%% Internal functions -%%-------------------------------------------------------------------- verify_bool(verify_peer) -> true; verify_bool(verify_none) -> diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl index fdc0c33750..ddace02dea 100644 --- a/lib/ssl/src/ssl_internal.hrl +++ b/lib/ssl/src/ssl_internal.hrl @@ -23,6 +23,8 @@ -ifndef(ssl_internal). -define(ssl_internal, true). +-include_lib("public_key/include/public_key.hrl"). + %% basic binary constructors -define(BOOLEAN(X), X:8/unsigned-big-integer). -define(BYTE(X), X:8/unsigned-big-integer). @@ -88,6 +90,28 @@ active = true }). +-type reason() :: term(). +-type reply() :: term(). +-type msg() :: term(). +-type from() :: term(). +-type host() :: string() | tuple(). +-type port_num() :: integer(). +-type session_id() :: binary(). +-type tls_version() :: {integer(), integer()}. +-type tls_atom_version() :: sslv3 | tlsv1. +-type cache_ref() :: term(). +-type certdb_ref() :: term(). +-type key_algo() :: rsa | dhe_rsa | dhe_dss. +-type enum_algo() :: integer(). +-type public_key() :: #'RSAPublicKey'{} | integer(). +-type public_key_params() :: #'Dss-Parms'{} | term(). +-type public_key_info() :: {enum_algo(), public_key(), public_key_params()}. +-type der_cert() :: binary(). +-type private_key() :: #'RSAPrivateKey'{} | #'DSAPrivateKey'{}. +-type issuer() :: tuple(). +-type serialnumber() :: integer(). +-type cert_key() :: {reference(), integer(), issuer()}. + -endif. % -ifdef(ssl_internal). diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl index 19bdcfa1f5..af30f78dbf 100644 --- a/lib/ssl/src/ssl_manager.erl +++ b/lib/ssl/src/ssl_manager.erl @@ -24,8 +24,10 @@ -module(ssl_manager). -behaviour(gen_server). +-include("ssl_internal.hrl"). + %% Internal application API --export([start_link/0, start_link/1, +-export([start_link/1, connection_init/2, cache_pem_file/1, lookup_trusted_cert/3, issuer_candidate/1, client_session_id/3, server_session_id/3, register_session/2, register_session/3, invalidate_session/2, @@ -58,21 +60,25 @@ %% API %%==================================================================== %%-------------------------------------------------------------------- -%% Function: start_link() -> {ok,Pid} | ignore | {error,Error} +-spec start_link(list()) -> {ok, pid()} | ignore | {error, term()}. +%% %% Description: Starts the server %%-------------------------------------------------------------------- -start_link() -> - gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). start_link(Opts) -> gen_server:start_link({local, ?MODULE}, ?MODULE, [Opts], []). %%-------------------------------------------------------------------- -%% Function: -%% Description: +-spec connection_init(string(), client | server) -> {ok, reference(), cache_ref()}. +%% +%% Description: Do necessary initializations for a new connection. %%-------------------------------------------------------------------- connection_init(TrustedcertsFile, Role) -> call({connection_init, TrustedcertsFile, Role}). - +%%-------------------------------------------------------------------- +-spec cache_pem_file(string()) -> {ok, term()}. +%% +%% Description: Cach a pem file and +%%-------------------------------------------------------------------- cache_pem_file(File) -> case ssl_certificate_db:lookup_cached_certs(File) of [{_,Content}] -> @@ -80,48 +86,51 @@ cache_pem_file(File) -> [] -> call({cache_pem, File}) end. - %%-------------------------------------------------------------------- -%% Function: -%% Description: +-spec lookup_trusted_cert(reference(), serialnumber(), issuer()) -> + {der_cert(), #'OTPCertificate'{}}. +%% +%% Description: Lookup the trusted cert with Key = {reference(), serialnumber(), issuer()}. %%-------------------------------------------------------------------- lookup_trusted_cert(Ref, SerialNumber, Issuer) -> ssl_certificate_db:lookup_trusted_cert(Ref, SerialNumber, Issuer). - %%-------------------------------------------------------------------- -%% Function: -%% Description: +-spec issuer_candidate(cert_key()) -> {cert_key(), der_cert()} | no_more_candidates. +%% +%% Description: Return next issuer candidate. %%-------------------------------------------------------------------- issuer_candidate(PrevCandidateKey) -> ssl_certificate_db:issuer_candidate(PrevCandidateKey). - %%-------------------------------------------------------------------- -%% Function: -%% Description: +-spec client_session_id(host(), port_num(), #ssl_options{}) -> session_id(). +%% +%% Description: Select a session id for the client. %%-------------------------------------------------------------------- client_session_id(Host, Port, SslOpts) -> call({client_session_id, Host, Port, SslOpts}). - + %%-------------------------------------------------------------------- -%% Function: -%% Description: +-spec server_session_id(host(), port_num(), #ssl_options{}) -> session_id(). +%% +%% Description: Select a session id for the server. %%-------------------------------------------------------------------- server_session_id(Port, SuggestedSessionId, SslOpts) -> call({server_session_id, Port, SuggestedSessionId, SslOpts}). %%-------------------------------------------------------------------- -%% Function: -%% Description: +-spec register_session(host(), port_num(), #session{}) -> ok. +%% +%% Description: Make the session available for reuse. %%-------------------------------------------------------------------- register_session(Host, Port, Session) -> cast({register_session, Host, Port, Session}). register_session(Port, Session) -> cast({register_session, Port, Session}). - %%-------------------------------------------------------------------- -%% Function: -%% Description: +-spec invalidate_session(host(), port_num(), #session{}) -> ok. +%% +%% Description: Make the session unavilable for reuse. %%-------------------------------------------------------------------- invalidate_session(Host, Port, Session) -> cast({invalidate_session, Host, Port, Session}). @@ -134,10 +143,9 @@ invalidate_session(Port, Session) -> %%==================================================================== %%-------------------------------------------------------------------- -%% Function: init(Args) -> {ok, State} | -%% {ok, State, Timeout} | -%% ignore | -%% {stop, Reason} +-spec init(list()) -> {ok, #state{}} | {ok, #state{}, timeout()} | + ignore | {stop, term()}. +%% %% Description: Initiates the server %%-------------------------------------------------------------------- init([Opts]) -> @@ -156,12 +164,13 @@ init([Opts]) -> session_validation_timer = Timer}}. %%-------------------------------------------------------------------- -%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} | -%% {reply, Reply, State, Timeout} | -%% {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, Reply, State} | -%% {stop, Reason, State} +-spec handle_call(msg(), from(), #state{}) -> {reply, reply(), #state{}} | + {reply, reply(), #state{}, timeout()} | + {noreply, #state{}} | + {noreply, #state{}, timeout()} | + {stop, reason(), reply(), #state{}} | + {stop, reason(), #state{}}. +%% %% Description: Handling call messages %%-------------------------------------------------------------------- handle_call({{connection_init, "", _Role}, Pid}, _From, @@ -207,9 +216,10 @@ handle_call({{cache_pem, File},Pid}, _, State = #state{certificate_db = Db}) -> {reply, {error, Reason}, State} end. %%-------------------------------------------------------------------- -%% Function: handle_cast(Msg, State) -> {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} +-spec handle_cast(msg(), #state{}) -> {noreply, #state{}} | + {noreply, #state{}, timeout()} | + {stop, reason(), #state{}}. +%% %% Description: Handling cast messages %%-------------------------------------------------------------------- handle_cast({register_session, Host, Port, Session}, @@ -243,9 +253,10 @@ handle_cast({invalidate_session, Port, #session{session_id = ID}}, {noreply, State}. %%-------------------------------------------------------------------- -%% Function: handle_info(Info, State) -> {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} +-spec handle_info(msg(), #state{}) -> {noreply, #state{}} | + {noreply, #state{}, timeout()} | + {stop, reason(), #state{}}. +%% %% Description: Handling all non call/cast messages %%-------------------------------------------------------------------- handle_info(validate_sessions, #state{session_cache_cb = CacheCb, @@ -278,7 +289,8 @@ handle_info(_Info, State) -> {noreply, State}. %%-------------------------------------------------------------------- -%% Function: terminate(Reason, State) -> void() +-spec terminate(reason(), #state{}) -> term(). +%% %% Description: This function is called by a gen_server when it is about to %% terminate. It should be the opposite of Module:init/1 and do any necessary %% cleaning up. When it returns, the gen_server terminates with Reason. @@ -294,7 +306,8 @@ terminate(_Reason, #state{certificate_db = Db, ok. %%-------------------------------------------------------------------- -%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState} +-spec code_change(term(), #state{}, list()) -> {ok, #state{}}. +%% %% Description: Convert process state when code is changed %%-------------------------------------------------------------------- code_change(_OldVsn, State, _Extra) -> @@ -339,4 +352,3 @@ session_validation({{{Host, Port}, _}, Session}, LifeTime) -> session_validation({{Port, _}, Session}, LifeTime) -> validate_session(Port, Session, LifeTime), LifeTime. - diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl index 6b7cffaa7d..90615c22a1 100644 --- a/lib/ssl/src/ssl_record.erl +++ b/lib/ssl/src/ssl_record.erl @@ -66,10 +66,9 @@ %%==================================================================== %% Internal application API %%==================================================================== + %%-------------------------------------------------------------------- -%% Function: init_connection_states(Role) -> #connection_states{} -%% Role = client | server -%% Random = binary() +-spec init_connection_states(client | server) -> #connection_states{}. %% %% Description: Creates a connection_states record with appropriate %% values for the initial SSL connection setup. @@ -85,9 +84,8 @@ init_connection_states(Role) -> }. %%-------------------------------------------------------------------- -%% Function: current_connection_state(States, Type) -> #connection_state{} -%% States = #connection_states{} -%% Type = read | write +-spec current_connection_state(#connection_states{}, read | write) -> + #connection_state{}. %% %% Description: Returns the instance of the connection_state record %% that is currently defined as the current conection state. @@ -100,9 +98,8 @@ current_connection_state(#connection_states{current_write = Current}, Current. %%-------------------------------------------------------------------- -%% Function: pending_connection_state(States, Type) -> #connection_state{} -%% States = #connection_states{} -%% Type = read | write +-spec pending_connection_state(#connection_states{}, read | write) -> + #connection_state{}. %% %% Description: Returns the instance of the connection_state record %% that is currently defined as the pending conection state. @@ -115,14 +112,11 @@ pending_connection_state(#connection_states{pending_write = Pending}, Pending. %%-------------------------------------------------------------------- -%% Function: update_security_params(Params, States) -> -%% #connection_states{} -%% Params = #security_parameters{} -%% States = #connection_states{} +-spec update_security_params(#security_parameters{}, #security_parameters{}, + #connection_states{}) -> #connection_states{}. %% %% Description: Creates a new instance of the connection_states record -%% where the pending states gets its security parameters -%% updated to <Params>. +%% where the pending states gets its security parameters updated. %%-------------------------------------------------------------------- update_security_params(ReadParams, WriteParams, States = #connection_states{pending_read = Read, @@ -135,14 +129,10 @@ update_security_params(ReadParams, WriteParams, States = WriteParams} }. %%-------------------------------------------------------------------- -%% Function: set_mac_secret(ClientWriteMacSecret, -%% ServerWriteMacSecret, Role, States) -> -%% #connection_states{} -%% MacSecret = binary() -%% States = #connection_states{} -%% Role = server | client +-spec set_mac_secret(binary(), binary(), client | server, + #connection_states{}) -> #connection_states{}. %% -%% update the mac_secret field in pending connection states +%% Description: update the mac_secret field in pending connection states %%-------------------------------------------------------------------- set_mac_secret(ClientWriteMacSecret, ServerWriteMacSecret, client, States) -> set_mac_secret(ServerWriteMacSecret, ClientWriteMacSecret, States); @@ -159,12 +149,9 @@ set_mac_secret(ReadMacSecret, WriteMacSecret, %%-------------------------------------------------------------------- -%% Function: set_master_secret(MasterSecret, States) -> -%% #connection_states{} -%% MacSecret = -%% States = #connection_states{} +-spec set_master_secret(binary(), #connection_state{}) -> #connection_states{}. %% -%% Set master_secret in pending connection states +%% Description: Set master_secret in pending connection states %%-------------------------------------------------------------------- set_master_secret(MasterSecret, States = #connection_states{pending_read = Read, @@ -180,12 +167,9 @@ set_master_secret(MasterSecret, States#connection_states{pending_read = Read1, pending_write = Write1}. %%-------------------------------------------------------------------- -%% Function: set_renegotiation_flag(Flag, States) -> -%% #connection_states{} -%% Flag = boolean() -%% States = #connection_states{} +-spec set_renegotiation_flag(boolean(), #connection_states{}) -> #connection_states{}. %% -%% Set master_secret in pending connection states +%% Description: Set secure_renegotiation in pending connection states %%-------------------------------------------------------------------- set_renegotiation_flag(Flag, #connection_states{ current_read = CurrentRead0, @@ -203,13 +187,11 @@ set_renegotiation_flag(Flag, #connection_states{ pending_write = PendingWrite}. %%-------------------------------------------------------------------- -%% Function: set_client_verify_data(State, Data, States) -> -%% #connection_states{} -%% State = atom() -%% Data = binary() -%% States = #connection_states{} +-spec set_client_verify_data(current_read | current_write | current_both, + binary(), #connection_states{})-> + #connection_states{}. %% -%% Set verify data in connection states. +%% Description: Set verify data in connection states. %%-------------------------------------------------------------------- set_client_verify_data(current_read, Data, #connection_states{current_read = CurrentRead0, @@ -235,15 +217,12 @@ set_client_verify_data(current_both, Data, CurrentWrite = CurrentWrite0#connection_state{client_verify_data = Data}, ConnectionStates#connection_states{current_read = CurrentRead, current_write = CurrentWrite}. - %%-------------------------------------------------------------------- -%% Function: set_server_verify_data(State, Data, States) -> -%% #connection_states{} -%% State = atom() -%% Data = binary() -%% States = #connection_states{} +-spec set_server_verify_data(current_read | current_write | current_both, + binary(), #connection_states{})-> + #connection_states{}. %% -%% Set verify data in pending connection states. +%% Description: Set verify data in pending connection states. %%-------------------------------------------------------------------- set_server_verify_data(current_write, Data, #connection_states{pending_read = PendingRead0, @@ -273,10 +252,8 @@ set_server_verify_data(current_both, Data, current_write = CurrentWrite}. %%-------------------------------------------------------------------- -%% Function: activate_pending_connection_state(States, Type) -> -%% #connection_states{} -%% States = #connection_states{} -%% Type = read | write +-spec activate_pending_connection_state(#connection_states{}, read | write) -> + #connection_states{}. %% %% Description: Creates a new instance of the connection_states record %% where the pending state of <Type> has been activated. @@ -308,11 +285,9 @@ activate_pending_connection_state(States = }. %%-------------------------------------------------------------------- -%% Function: set_pending_cipher_state(States, ClientState, -%% ServerState, Role) -> -%% #connection_states{} -%% ClientState = ServerState = #cipher_state{} -%% States = #connection_states{} +-spec set_pending_cipher_state(#connection_states{}, #cipher_state{}, + #cipher_state{}, client | server) -> + #connection_states{}. %% %% Description: Set the cipher state in the specified pending connection state. %%-------------------------------------------------------------------- @@ -331,12 +306,10 @@ set_pending_cipher_state(#connection_states{pending_read = Read, pending_write = Write#connection_state{cipher_state = ClientState}}. %%-------------------------------------------------------------------- -%% Function: get_tls_record(Data, Buffer) -> Result -%% Result = {[#tls_compressed{}], NewBuffer} -%% Data = Buffer = NewBuffer = binary() -%% -%% Description: given old buffer and new data from TCP, packs up a records -%% and returns it as a list of #tls_compressed, also returns leftover +-spec get_tls_records(binary(), binary()) -> {[binary()], binary()}. +%% +%% Description: Given old buffer and new data from TCP, packs up a records +%% and returns it as a list of tls_compressed binaries also returns leftover %% data %%-------------------------------------------------------------------- get_tls_records(Data, <<>>) -> @@ -399,8 +372,7 @@ get_tls_records_aux(Data, Acc) -> {lists:reverse(Acc), Data}. %%-------------------------------------------------------------------- -%% Function: protocol_version(Version) -> #protocol_version{} -%% Version = atom() +-spec protocol_version(tls_atom_version()) -> tls_version(). %% %% Description: Creates a protocol version record from a version atom %% or vice versa. @@ -420,8 +392,7 @@ protocol_version({3, 1}) -> protocol_version({3, 0}) -> sslv3. %%-------------------------------------------------------------------- -%% Function: protocol_version(Version1, Version2) -> #protocol_version{} -%% Version1 = Version2 = #protocol_version{} +-spec lowest_protocol_version(tls_version(), tls_version()) -> tls_version(). %% %% Description: Lowes protocol version of two given versions %%-------------------------------------------------------------------- @@ -436,8 +407,7 @@ lowest_protocol_version(Version = {M,_}, lowest_protocol_version(_,Version) -> Version. %%-------------------------------------------------------------------- -%% Function: protocol_version(Versions) -> #protocol_version{} -%% Versions = [#protocol_version{}] +-spec highest_protocol_version([tls_version()]) -> tls_version(). %% %% Description: Highest protocol version present in a list %%-------------------------------------------------------------------- @@ -459,9 +429,8 @@ highest_protocol_version(_, [Version | Rest]) -> highest_protocol_version(Version, Rest). %%-------------------------------------------------------------------- -%% Function: supported_protocol_versions() -> Versions -%% Versions = [#protocol_version{}] -%% +-spec supported_protocol_versions() -> [tls_version()]. +%% %% Description: Protocol versions supported %%-------------------------------------------------------------------- supported_protocol_versions() -> @@ -487,8 +456,7 @@ supported_protocol_versions([_|_] = Vsns) -> Vsns. %%-------------------------------------------------------------------- -%% Function: is_acceptable_version(Version) -> true | false -%% Version = #protocol_version{} +-spec is_acceptable_version(tls_version()) -> boolean(). %% %% Description: ssl version 2 is not acceptable security risks are too big. %%-------------------------------------------------------------------- @@ -499,7 +467,7 @@ is_acceptable_version(_) -> false. %%-------------------------------------------------------------------- -%% Function: compressions() -> binary() +-spec compressions() -> binary(). %% %% Description: return a list of compressions supported (currently none) %%-------------------------------------------------------------------- @@ -507,8 +475,8 @@ compressions() -> [?byte(?NULL)]. %%-------------------------------------------------------------------- -%% Function: decode_cipher_text(CipherText, ConnectionStates0) -> -%% {Plain, ConnectionStates} +-spec decode_cipher_text(#ssl_tls{}, #connection_states{}) -> + {#ssl_tls{}, #connection_states{}}. %% %% Description: Decode cipher text %%-------------------------------------------------------------------- diff --git a/lib/ssl/src/ssl_session.erl b/lib/ssl/src/ssl_session.erl index bcb10daf69..e9755cb0e1 100644 --- a/lib/ssl/src/ssl_session.erl +++ b/lib/ssl/src/ssl_session.erl @@ -32,11 +32,10 @@ -define(GEN_UNIQUE_ID_MAX_TRIES, 10). +-type seconds() :: integer(). + %%-------------------------------------------------------------------- -%% Function: is_new(ClientSuggestedId, ServerDecidedId) -> true | false -%% -%% ClientSuggestedId = binary() -%% ServerDecidedId = binary() +-spec is_new(binary(), binary()) -> boolean(). %% %% Description: Checks if the session id decided by the server is a %% new or resumed sesion id. @@ -45,17 +44,11 @@ is_new(<<>>, _) -> true; is_new(SessionId, SessionId) -> false; -is_new(_, _) -> +is_new(_ClientSuggestion, _ServerDecision) -> true. %%-------------------------------------------------------------------- -%% Function: id(ClientInfo, Cache, CacheCb) -> SessionId -%% -%% ClientInfo = {HostIP, Port, SslOpts} -%% HostIP = ipadress() -%% Port = integer() -%% CacheCb = atom() -%% SessionId = binary() +-spec id({host(), port_num(), #ssl_options{}}, cache_ref(), atom()) -> binary(). %% %% Description: Should be called by the client side to get an id %% for the client hello message. @@ -69,14 +62,8 @@ id(ClientInfo, Cache, CacheCb) -> end. %%-------------------------------------------------------------------- -%% Function: id(Port, SuggestedSessionId, ReuseFun, CacheCb, -%% SecondLifeTime) -> SessionId -%% -%% Port = integer() -%% SuggestedSessionId = SessionId = binary() -%% ReuseFun = fun(SessionId, PeerCert, Compression, CipherSuite) -> -%% true | false -%% CacheCb = atom() +-spec id(port_num(), binary(), #ssl_options{}, cache_ref(), + atom(), seconds()) -> binary(). %% %% Description: Should be called by the server side to get an id %% for the server hello message. @@ -95,10 +82,7 @@ id(Port, SuggestedSessionId, #ssl_options{reuse_sessions = ReuseEnabled, new_id(Port, ?GEN_UNIQUE_ID_MAX_TRIES, Cache, CacheCb) end. %%-------------------------------------------------------------------- -%% Function: valid_session(Session, LifeTime) -> true | false -%% -%% Session = #session{} -%% LifeTime = integer() - seconds +-spec valid_session(#session{}, seconds()) -> boolean(). %% %% Description: Check that the session has not expired %%-------------------------------------------------------------------- diff --git a/lib/ssl/src/ssl_session_cache.erl b/lib/ssl/src/ssl_session_cache.erl index 1f2d1fc7d3..823bf7acfa 100644 --- a/lib/ssl/src/ssl_session_cache.erl +++ b/lib/ssl/src/ssl_session_cache.erl @@ -22,13 +22,16 @@ -behaviour(ssl_session_cache_api). +-include("ssl_handshake.hrl"). +-include("ssl_internal.hrl"). + -export([init/1, terminate/1, lookup/2, update/3, delete/2, foldl/3, select_session/2]). +-type key() :: {{host(), port_num()}, session_id()} | {port_num(), session_id()}. + %%-------------------------------------------------------------------- -%% Function: init() -> Cache -%% -%% Cache - Reference to the cash (opaque) +-spec init(list()) -> cache_ref(). %% Returns reference to the cache (opaque) %% %% Description: Return table reference. Called by ssl_manager process. %%-------------------------------------------------------------------- @@ -36,9 +39,7 @@ init(_) -> ets:new(cache_name(), [set, protected]). %%-------------------------------------------------------------------- -%% Function: terminate(Cache) -> -%% -%% Cache - as returned by create/0 +-spec terminate(cache_ref()) -> any(). %% %% %% Description: Handles cache table at termination of ssl manager. %%-------------------------------------------------------------------- @@ -46,9 +47,7 @@ terminate(Cache) -> ets:delete(Cache). %%-------------------------------------------------------------------- -%% Function: lookup(Cache, Key) -> Session | undefined -%% Cache - as returned by create/0 -%% Session = #session{} +-spec lookup(cache_ref(), key()) -> #session{} | undefined. %% %% Description: Looks up a cach entry. Should be callable from any %% process. @@ -62,9 +61,7 @@ lookup(Cache, Key) -> end. %%-------------------------------------------------------------------- -%% Function: update(Cache, Key, Session) -> _ -%% Cache - as returned by create/0 -%% Session = #session{} +-spec update(cache_ref(), key(), #session{}) -> any(). %% %% Description: Caches a new session or updates a already cached one. %% Will only be called from the ssl_manager process. @@ -73,11 +70,7 @@ update(Cache, Key, Session) -> ets:insert(Cache, {Key, Session}). %%-------------------------------------------------------------------- -%% Function: delete(Cache, HostIP, Port, Id) -> _ -%% Cache - as returned by create/0 -%% HostIP = Host = string() | ipadress() -%% Port = integer() -%% Id = +-spec delete(cache_ref(), key()) -> any(). %% %% Description: Delets a cache entry. %% Will only be called from the ssl_manager process. @@ -86,28 +79,19 @@ delete(Cache, Key) -> ets:delete(Cache, Key). %%-------------------------------------------------------------------- -%% Function: foldl(Fun, Acc0, Cache) -> Acc -%% -%% Fun - fun() -%% Acc0 - term() -%% Cache - cache_ref() -%% +-spec foldl(fun(), term(), cache_ref()) -> term(). %% %% Description: Calls Fun(Elem, AccIn) on successive elements of the %% cache, starting with AccIn == Acc0. Fun/2 must return a new %% accumulator which is passed to the next call. The function returns -%% the final value of the accumulator. Acc0 is returned if the cache is -%% empty. -%% Should be callable from any process +%% the final value of the accumulator. Acc0 is returned if the cache +%% is empty.Should be callable from any process %%-------------------------------------------------------------------- foldl(Fun, Acc0, Cache) -> ets:foldl(Fun, Acc0, Cache). %%-------------------------------------------------------------------- -%% Function: select_session(Cache, PartialKey) -> [Sessions] -%% -%% Cache - as returned by create/0 -%% PartialKey - opaque Key = {PartialKey, SessionId} +-spec select_session(cache_ref(), {host(), port_num()} | port_num()) -> [#session{}]. %% %% Description: Selects a session that could be reused. Should be callable %% from any process. @@ -119,6 +103,5 @@ select_session(Cache, PartialKey) -> %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- - cache_name() -> ssl_otp_session_cache. diff --git a/lib/ssl/src/ssl_ssl3.erl b/lib/ssl/src/ssl_ssl3.erl index 88b801d46b..375adf263a 100644 --- a/lib/ssl/src/ssl_ssl3.erl +++ b/lib/ssl/src/ssl_ssl3.erl @@ -38,6 +38,8 @@ %% Internal application API %%==================================================================== +-spec master_secret(binary(), binary(), binary()) -> binary(). + master_secret(PremasterSecret, ClientRandom, ServerRandom) -> ?DBG_HEX(PremasterSecret), ?DBG_HEX(ClientRandom), @@ -57,6 +59,8 @@ master_secret(PremasterSecret, ClientRandom, ServerRandom) -> ?DBG_HEX(B), B. +-spec finished(client | server, binary(), {binary(), binary()}) -> binary(). + finished(Role, MasterSecret, {MD5Hash, SHAHash}) -> %% draft-ietf-tls-ssl-version3-00 - 5.6.9 Finished %% struct { @@ -75,6 +79,8 @@ finished(Role, MasterSecret, {MD5Hash, SHAHash}) -> SHA = handshake_hash(?SHA, MasterSecret, Sender, SHAHash), <<MD5/binary, SHA/binary>>. +-spec certificate_verify(key_algo(), binary(), {binary(), binary()}) -> binary(). + certificate_verify(Algorithm, MasterSecret, {MD5Hash, SHAHash}) when Algorithm == rsa; Algorithm == dhe_rsa -> %% md5_hash @@ -94,6 +100,8 @@ certificate_verify(dhe_dss, MasterSecret, {_, SHAHash}) -> %% SHA(handshake_messages + master_secret + pad_1)); handshake_hash(?SHA, MasterSecret, undefined, SHAHash). +-spec mac_hash(integer(), binary(), integer(), integer(), integer(), binary()) -> binary(). + mac_hash(Method, Mac_write_secret, Seq_num, Type, Length, Fragment) -> %% draft-ietf-tls-ssl-version3-00 - 5.2.3.1 %% hash(MAC_write_secret + pad_2 + @@ -113,6 +121,10 @@ mac_hash(Method, Mac_write_secret, Seq_num, Type, Length, Fragment) -> ?DBG_HEX(Mac), Mac. +-spec setup_keys(binary(), binary(), binary(), binary(), + integer(), integer(), binary()) -> {binary(), binary(), binary(), + binary(), binary(), binary()}. + setup_keys(MasterSecret, ServerRandom, ClientRandom, HS, KML, _EKML, IVS) -> KeyBlock = generate_keyblock(MasterSecret, ServerRandom, ClientRandom, 2*(HS+KML+IVS)), @@ -136,6 +148,8 @@ setup_keys(MasterSecret, ServerRandom, ClientRandom, HS, KML, _EKML, IVS) -> {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey, ServerWriteKey, ClientIV, ServerIV}. +-spec suites() -> [cipher_suite()]. + suites() -> [ ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA, diff --git a/lib/ssl/src/ssl_tls1.erl b/lib/ssl/src/ssl_tls1.erl index 70db632835..d1bc0730ba 100644 --- a/lib/ssl/src/ssl_tls1.erl +++ b/lib/ssl/src/ssl_tls1.erl @@ -36,6 +36,8 @@ %% Internal application API %%==================================================================== +-spec master_secret(binary(), binary(), binary()) -> binary(). + master_secret(PreMasterSecret, ClientRandom, ServerRandom) -> %% RFC 2246 & 4346 - 8.1 %% master_secret = PRF(pre_master_secret, %% "master secret", ClientHello.random + @@ -43,6 +45,8 @@ master_secret(PreMasterSecret, ClientRandom, ServerRandom) -> prf(PreMasterSecret, <<"master secret">>, [ClientRandom, ServerRandom], 48). +-spec finished(client | server, binary(), {binary(), binary()}) -> binary(). + finished(Role, MasterSecret, {MD5Hash, SHAHash}) -> %% RFC 2246 & 4346 - 7.4.9. Finished %% struct { @@ -56,6 +60,7 @@ finished(Role, MasterSecret, {MD5Hash, SHAHash}) -> SHA = hash_final(?SHA, SHAHash), prf(MasterSecret, finished_label(Role), [MD5, SHA], 12). +-spec certificate_verify(key_algo(), {binary(), binary()}) -> binary(). certificate_verify(Algorithm, {MD5Hash, SHAHash}) when Algorithm == rsa; Algorithm == dhe_rsa -> @@ -65,7 +70,11 @@ certificate_verify(Algorithm, {MD5Hash, SHAHash}) when Algorithm == rsa; certificate_verify(dhe_dss, {_, SHAHash}) -> hash_final(?SHA, SHAHash). - + +-spec setup_keys(binary(), binary(), binary(), integer(), + integer(), integer()) -> {binary(), binary(), binary(), + binary(), binary(), binary()}. + setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize, KeyMatLen, IVSize) -> %% RFC 2246 - 6.3. Key calculation @@ -112,6 +121,9 @@ setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize, %% {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey, %% ServerWriteKey, undefined, undefined}. +-spec mac_hash(integer(), binary(), integer(), integer(), tls_version(), + integer(), binary()) -> binary(). + mac_hash(Method, Mac_write_secret, Seq_num, Type, {Major, Minor}, Length, Fragment) -> %% RFC 2246 & 4346 - 6.2.3.1. @@ -132,6 +144,8 @@ mac_hash(Method, Mac_write_secret, Seq_num, Type, {Major, Minor}, ?DBG_HEX(Mac), Mac. +-spec suites() -> [cipher_suite()]. + suites() -> [ ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA, diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl index 40715dbf30..dd0818827a 100644 --- a/lib/ssl/test/ssl_test_lib.erl +++ b/lib/ssl/test/ssl_test_lib.erl @@ -325,6 +325,10 @@ make_dsa_cert(Config) -> [{server_dsa_opts, [{ssl_imp, new},{reuseaddr, true}, {cacertfile, ServerCaCertFile}, {certfile, ServerCertFile}, {keyfile, ServerKeyFile}]}, + {server_dsa_verify_opts, [{ssl_imp, new},{reuseaddr, true}, + {cacertfile, ServerCaCertFile}, + {certfile, ServerCertFile}, {keyfile, ServerKeyFile}, + {verify, verify_peer}]}, {client_dsa_opts, [{ssl_imp, new},{reuseaddr, true}, {cacertfile, ClientCaCertFile}, {certfile, ClientCertFile}, {keyfile, ClientKeyFile}]} diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl index d2a4ca8db5..75cfce0052 100644 --- a/lib/ssl/test/ssl_to_openssl_SUITE.erl +++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl @@ -309,7 +309,7 @@ tls1_erlang_server_openssl_client_dsa_cert(suite) -> tls1_erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) -> process_flag(trap_exit, true), ClientOpts = ?config(client_dsa_opts, Config), - ServerOpts = ?config(server_dsa_opts, Config), + ServerOpts = ?config(server_dsa_verify_opts, Config), {_, ServerNode, _} = ssl_test_lib:run_where(Config), @@ -398,7 +398,7 @@ ssl3_erlang_server_openssl_client_dsa_cert(suite) -> ssl3_erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) -> process_flag(trap_exit, true), ClientOpts = ?config(client_dsa_opts, Config), - ServerOpts = ?config(server_dsa_opts, Config), + ServerOpts = ?config(server_dsa_verify_opts, Config), {_, ServerNode, _} = ssl_test_lib:run_where(Config), diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk index 74b1cf4c78..254ee8b986 100644 --- a/lib/ssl/vsn.mk +++ b/lib/ssl/vsn.mk @@ -17,12 +17,14 @@ # %CopyrightEnd% # -SSL_VSN = 4.0 +SSL_VSN = 4.0.1 -TICKETS = OTP-8587\ - OTP-8695 +TICKETS = OTP-8721 -#TICKETS = OTP-8679 \ +#TICKETS_4.0 = OTP-8587\ +# OTP-8695 + +#TICKETS_3.11.1 = OTP-8679 \ # OTP-7047 \ # OTP-7049 \ # OTP-8568 \ diff --git a/lib/stdlib/doc/src/beam_lib.xml b/lib/stdlib/doc/src/beam_lib.xml index 27308e02f3..adc411e272 100644 --- a/lib/stdlib/doc/src/beam_lib.xml +++ b/lib/stdlib/doc/src/beam_lib.xml @@ -341,6 +341,7 @@ chunkref() = chunkname() | chunkid()</code> <v>Beam1 = Beam2 = beam()</v> <v>Reason = {modules_different, Module1, Module2}</v> <v> | {chunks_different, ChunkId}</v> + <v> | different_chunks</v> <v> | Reason1 -- see info/1</v> <v> Module1 = Module2 = atom()</v> <v> ChunkId = chunkid()</v> diff --git a/lib/stdlib/doc/src/filename.xml b/lib/stdlib/doc/src/filename.xml index 0cf82fa48b..fe6c6f898e 100644 --- a/lib/stdlib/doc/src/filename.xml +++ b/lib/stdlib/doc/src/filename.xml @@ -49,7 +49,7 @@ <title>DATA TYPES</title> <code type="none"> name() = string() | atom() | DeepList - DeepList = [char() | atom() | DeepList]</code> +DeepList = [char() | atom() | DeepList]</code> </section> <funcs> <func> diff --git a/lib/stdlib/doc/src/lists.xml b/lib/stdlib/doc/src/lists.xml index a273a2301f..b3ad7aaf46 100644 --- a/lib/stdlib/doc/src/lists.xml +++ b/lib/stdlib/doc/src/lists.xml @@ -48,7 +48,7 @@ <item><p>if x <c>F</c> y and y <c>F</c> x then x = y (<c>F</c> is antisymmetric);</p> </item> - <item><p>if x <c>F</c> y and and y <c>F</c> z then x <c>F</c> z + <item><p>if x <c>F</c> y and y <c>F</c> z then x <c>F</c> z (<c>F</c> is transitive);</p> </item> <item><p>x <c>F</c> y or y <c>F</c> x (<c>F</c> is total).</p> diff --git a/lib/stdlib/doc/src/sofs.xml b/lib/stdlib/doc/src/sofs.xml index 8c8ae51262..729df1e678 100644 --- a/lib/stdlib/doc/src/sofs.xml +++ b/lib/stdlib/doc/src/sofs.xml @@ -210,7 +210,7 @@ X[i] to Y[i] and S a subset of X[1] × ... × X[n]. The <marker id="multiple_relative_product"></marker><em>multiple - relative product</em> of TR and and S is defined to be the + relative product</em> of TR and S is defined to be the set {z : z = ((x[1], ..., x[n]), (y[1],...,y[n])) for some (x[1], ..., x[n]) in S and for some (x[i], y[i]) in R[i], diff --git a/lib/stdlib/doc/src/timer.xml b/lib/stdlib/doc/src/timer.xml index 0b6807dd6c..1b34e71490 100644 --- a/lib/stdlib/doc/src/timer.xml +++ b/lib/stdlib/doc/src/timer.xml @@ -202,18 +202,33 @@ </func> <func> <name>tc(Module, Function, Arguments) -> {Time, Value}</name> - <fsummary>Measure the real time it takes to evaluate <c>apply(Module, Function, Arguments)</c></fsummary> + <name>tc(Fun, Arguments) -> {Time, Value}</name> + <fsummary>Measure the real time it takes to evaluate <c>apply(Module, + Function, Arguments)</c> or <c>apply(Fun, Arguments)</c></fsummary> <type> <v>Module = Function = atom()</v> + <v>Fun = fun()</v> <v>Arguments = [term()]</v> <v>Time = integer() in microseconds</v> <v>Value = term()</v> </type> <desc> - <p>Evaluates <c>apply(Module, Function, Arguments)</c> and measures - the elapsed real time. Returns <c>{Time, Value}</c>, where - <c>Time</c> is the elapsed real time in <em>microseconds</em>, - and <c>Value</c> is what is returned from the apply.</p> + <p></p> + <taglist> + <tag><c>tc/3</c></tag> + <item> + <p>Evaluates <c>apply(Module, Function, Arguments)</c> and measures + the elapsed real time as reported by <c>now/0</c>. + Returns <c>{Time, Value}</c>, where + <c>Time</c> is the elapsed real time in <em>microseconds</em>, + and <c>Value</c> is what is returned from the apply.</p> + </item> + <tag><c>tc/2</c></tag> + <item> + <p>Evaluates <c>apply(Fun, Arguments)</c>. Otherwise works + like <c>tc/3</c>.</p> + </item> + </taglist> </desc> </func> <func> diff --git a/lib/stdlib/src/beam_lib.erl b/lib/stdlib/src/beam_lib.erl index 91ff2438c6..e612bf71e7 100644 --- a/lib/stdlib/src/beam_lib.erl +++ b/lib/stdlib/src/beam_lib.erl @@ -105,6 +105,7 @@ | info_rsn(). -type cmp_rsn() :: {'modules_different', module(), module()} | {'chunks_different', chunkid()} + | 'different_chunks' | info_rsn(). %%------------------------------------------------------------------------- diff --git a/lib/stdlib/src/escript.erl b/lib/stdlib/src/escript.erl index d26443f277..99e454f593 100644 --- a/lib/stdlib/src/escript.erl +++ b/lib/stdlib/src/escript.erl @@ -24,35 +24,41 @@ %% Internal API. -export([start/0, start/1]). --include_lib("kernel/include/file.hrl"). +%%----------------------------------------------------------------------- -define(SHEBANG, "/usr/bin/env escript"). -define(COMMENT, "This is an -*- erlang -*- file"). --record(state, {file, - module, +%%----------------------------------------------------------------------- + +-type mode() :: 'compile' | 'debug' | 'interpret' | 'run'. +-type source() :: 'archive' | 'beam' | 'text'. + +-record(state, {file :: file:filename(), + module :: module(), forms_or_bin, - source, - n_errors, - mode, - exports_main, - has_records}). --record(sections, {type, - shebang, - comment, - emu_args, - body}). --record(extract_options, {compile_source}). + source :: source(), + n_errors :: non_neg_integer(), + mode :: mode(), + exports_main :: boolean(), + has_records :: boolean()}). -type shebang() :: string(). -type comment() :: string(). -type emu_args() :: string(). --type escript_filename() :: string(). --type filename() :: string(). + +-record(sections, {type, + shebang :: shebang(), + comment :: comment(), + emu_args :: emu_args(), + body}). + +-record(extract_options, {compile_source}). + -type zip_file() :: - filename() - | {filename(), binary()} - | {filename(), binary(), #file_info{}}. + file:filename() + | {file:filename(), binary()} + | {file:filename(), binary(), file:file_info()}. -type zip_create_option() :: term(). -type section() :: shebang @@ -60,13 +66,15 @@ | comment | {comment, comment()} | {emu_args, emu_args()} - | {source, filename() | binary()} - | {beam, filename() | binary()} - | {archive, filename() | binary()} + | {source, file:filename() | binary()} + | {beam, file:filename() | binary()} + | {archive, file:filename() | binary()} | {archive, [zip_file()], [zip_create_option()]}. +%%----------------------------------------------------------------------- + %% Create a complete escript file with both header and body --spec create(escript_filename() | binary, [section()]) -> +-spec create(file:filename() | binary, [section()]) -> ok | {ok, binary()} | {error, term()}. create(File, Options) when is_list(Options) -> @@ -149,7 +157,9 @@ prepare(BadOptions, _) -> -type section_name() :: shebang | comment | emu_args | body . -type extract_option() :: compile_source | {section, [section_name()]}. --spec extract(filename(), [extract_option()]) -> {ok, [section()]} | {error, term()}. +-spec extract(file:filename(), [extract_option()]) -> + {ok, [section()]} | {error, term()}. + extract(File, Options) when is_list(File), is_list(Options) -> try EO = parse_extract_options(Options, @@ -239,6 +249,7 @@ normalize_section(Name, Chars) -> {Name, Chars}. -spec script_name() -> string(). + script_name() -> [ScriptName|_] = init:get_plain_arguments(), ScriptName. @@ -248,10 +259,12 @@ script_name() -> %% -spec start() -> no_return(). + start() -> start([]). -spec start([string()]) -> no_return(). + start(EscriptOptions) -> try %% Commands run using -run or -s are run in a process @@ -484,18 +497,12 @@ find_first_body_line(Fd, HeaderSz0, LineNo, KeepFirst, Sections) -> classify_line(Line) -> case Line of - [$\#, $\! | _] -> - shebang; - [$P, $K | _] -> - archive; - [$F, $O, $R, $1 | _] -> - beam; - [$%, $%, $\! | _] -> - emu_args; - [$% | _] -> - comment; - _ -> - undefined + "#!" ++ _ -> shebang; + "PK" ++ _ -> archive; + "FOR1" ++ _ -> beam; + "%%!" ++ _ -> emu_args; + "%" ++ _ -> comment; + _ -> undefined end. guess_type(Line) -> @@ -531,7 +538,6 @@ parse_archive(S, File, HeaderSz) -> end, list_to_atom(lists:reverse(RevBase2)) end, - S#state{source = archive, mode = run, module = Mod, @@ -587,8 +593,8 @@ parse_source(S, File, Fd, StartLine, HeaderSz, CheckOnly) -> epp_parse_file2(Epp, S2, [ModForm, FileForm], OptModRes); {error, _} -> epp_parse_file2(Epp, S2, [FileForm], OptModRes); - {eof,LastLine} -> - S#state{forms_or_bin = [FileForm, {eof,LastLine}]} + {eof, _LastLine} = Eof -> + S#state{forms_or_bin = [FileForm, Eof]} end, ok = epp:close(Epp), ok = file:close(Fd), @@ -683,8 +689,8 @@ epp_parse_file2(Epp, S, Forms, Parsed) -> io:format("~s:~w: ~s\n", [S#state.file,Ln,Mod:format_error(Args)]), epp_parse_file(Epp, S#state{n_errors = S#state.n_errors + 1}, [Form | Forms]); - {eof,LastLine} -> - S#state{forms_or_bin = lists:reverse([{eof, LastLine} | Forms])} + {eof, _LastLine} = Eof -> + S#state{forms_or_bin = lists:reverse([Eof | Forms])} end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/lib/stdlib/src/timer.erl b/lib/stdlib/src/timer.erl index 6ee837c3e6..24e14caa69 100644 --- a/lib/stdlib/src/timer.erl +++ b/lib/stdlib/src/timer.erl @@ -22,7 +22,7 @@ send_after/3, send_after/2, exit_after/3, exit_after/2, kill_after/2, kill_after/1, apply_interval/4, send_interval/3, send_interval/2, - cancel/1, sleep/1, tc/3, now_diff/2, + cancel/1, sleep/1, tc/2, tc/3, now_diff/2, seconds/1, minutes/1, hours/1, hms/3]). -export([start_link/0, start/0, @@ -98,6 +98,17 @@ sleep(T) -> after T -> ok end. + +%% +%% Measure the execution time (in microseconds) for Fun(Args). +%% +-spec tc(function(), [_]) -> {time(), term()}. +tc(F, A) -> + Before = erlang:now(), + Val = (catch apply(F, A)), + After = erlang:now(), + {now_diff(After, Before), Val}. + %% %% Measure the execution time (in microseconds) for an MFA. %% diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl index 1f68c6b8c4..5d7e558601 100644 --- a/lib/stdlib/test/ets_SUITE.erl +++ b/lib/stdlib/test/ets_SUITE.erl @@ -63,7 +63,8 @@ meta_lookup_unnamed_read/1, meta_lookup_unnamed_write/1, meta_lookup_named_read/1, meta_lookup_named_write/1, meta_newdel_unnamed/1, meta_newdel_named/1]). --export([smp_insert/1, smp_fixed_delete/1, smp_unfix_fix/1, smp_select_delete/1, otp_8166/1]). +-export([smp_insert/1, smp_fixed_delete/1, smp_unfix_fix/1, smp_select_delete/1, + otp_8166/1, otp_8732/1]). -export([exit_large_table_owner/1, exit_many_large_table_owner/1, exit_many_tables_owner/1, @@ -130,7 +131,7 @@ all(suite) -> t_select_delete, t_ets_dets, memory, t_bucket_disappears, select_fail,t_insert_new, t_repair_continuation, otp_5340, otp_6338, - otp_6842_select_1000, otp_7665, + otp_6842_select_1000, otp_7665, otp_8732, meta_wb, grow_shrink, grow_pseudo_deleted, shrink_pseudo_deleted, meta_smp, @@ -392,7 +393,7 @@ memory(Config) when is_list(Config) -> ?line erts_debug:set_internal_state(available_internal_state, true), ?line ok = chk_normal_tab_struct_size(), ?line L = [T1,T2,T3,T4] = fill_sets_int(1000), - ?line XRes1 = adjust_xmem(L, {16862,16072,16072,16078}), + ?line XRes1 = adjust_xmem(L, {14862,14072,14072,14078}), ?line Res1 = {?S(T1),?S(T2),?S(T3),?S(T4)}, ?line lists:foreach(fun(T) -> Before = ets:info(T,size), @@ -403,7 +404,7 @@ memory(Config) when is_list(Config) -> [Key, ets:info(T,type), Before, ets:info(T,size), Objs]) end, L), - ?line XRes2 = adjust_xmem(L, {16849,16060,16048,16054}), + ?line XRes2 = adjust_xmem(L, {14851,14062,14052,14058}), ?line Res2 = {?S(T1),?S(T2),?S(T3),?S(T4)}, ?line lists:foreach(fun(T) -> Before = ets:info(T,size), @@ -414,7 +415,7 @@ memory(Config) when is_list(Config) -> [Key, ets:info(T,type), Before, ets:info(T,size), Objs]) end, L), - ?line XRes3 = adjust_xmem(L, {16836,16048,16024,16030}), + ?line XRes3 = adjust_xmem(L, {14840,14052,14032,14038}), ?line Res3 = {?S(T1),?S(T2),?S(T3),?S(T4)}, ?line lists:foreach(fun(T) -> ?line ets:delete_all_objects(T) @@ -5049,8 +5050,14 @@ verify_table_load(T) -> end. - - +otp_8732(doc) -> ["ets:select on a tree with NIL key object"]; +otp_8732(Config) when is_list(Config) -> + Tab = ets:new(noname,[ordered_set]), + filltabstr(Tab,999), + ets:insert(Tab,{[],"nasty NIL object"}), + ?line [] = ets:match(Tab,{'_',nomatch}), %% Will hang if bug not fixed + ok. + smp_select_delete(suite) -> []; smp_select_delete(doc) -> diff --git a/lib/stdlib/test/timer_simple_SUITE.erl b/lib/stdlib/test/timer_simple_SUITE.erl index 021a22c61b..6aa2b7b945 100644 --- a/lib/stdlib/test/timer_simple_SUITE.erl +++ b/lib/stdlib/test/timer_simple_SUITE.erl @@ -224,11 +224,19 @@ cancel2(Config) when is_list(Config) -> tc(doc) -> "Test sleep/1 and tc/3."; tc(suite) -> []; tc(Config) when is_list(Config) -> - % This should both sleep and tc - ?line {Res, ok} = timer:tc(timer, sleep, [500]), - ?line ok = if - Res < 500*1000 -> {too_early, Res}; % Too early - Res > 800*1000 -> {too_late, Res}; % Too much time + % This should both sleep and tc/3 + ?line {Res1, ok} = timer:tc(timer, sleep, [500]), + ?line ok = if + Res1 < 500*1000 -> {too_early, Res1}; % Too early + Res1 > 800*1000 -> {too_late, Res1}; % Too much time + true -> ok + end, + + % This should both sleep and tc/2 + ?line {Res2, ok} = timer:tc(fun(T) -> timer:sleep(T) end, [500]), + ?line ok = if + Res2 < 500*1000 -> {too_early, Res2}; % Too early + Res2 > 800*1000 -> {too_late, Res2}; % Too much time true -> ok end, diff --git a/lib/stdlib/vsn.mk b/lib/stdlib/vsn.mk index f081bd60e2..02ea6d9d68 100644 --- a/lib/stdlib/vsn.mk +++ b/lib/stdlib/vsn.mk @@ -17,5 +17,4 @@ # %CopyrightEnd% # -STDLIB_VSN = 1.17 - +STDLIB_VSN = 1.17.1 diff --git a/lib/test_server/src/test_server_ctrl.erl b/lib/test_server/src/test_server_ctrl.erl index 1245c10a01..72f274d63a 100644 --- a/lib/test_server/src/test_server_ctrl.erl +++ b/lib/test_server/src/test_server_ctrl.erl @@ -151,10 +151,12 @@ %%% OPERATOR INTERFACE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -export([add_spec/1, add_dir/2, add_dir/3]). --export([add_module/1, add_module/2, add_case/2, add_case/3, add_cases/2, - add_cases/3]). +-export([add_module/1, add_module/2, + add_conf/3, + add_case/2, add_case/3, add_cases/2, add_cases/3]). -export([add_dir_with_skip/3, add_dir_with_skip/4, add_tests_with_skip/3]). -export([add_module_with_skip/2, add_module_with_skip/3, + add_conf_with_skip/4, add_case_with_skip/3, add_case_with_skip/4, add_cases_with_skip/3, add_cases_with_skip/4]). -export([jobs/0, run_test/1, wait_finish/0, idle_notify/1, @@ -236,9 +238,16 @@ add_dir(Name, Dir, Pattern) -> add_module(Mod) when is_atom(Mod) -> add_job(atom_to_list(Mod), {Mod,all}). + add_module(Name, Mods) when is_list(Mods) -> add_job(cast_to_list(Name), lists:map(fun(Mod) -> {Mod,all} end, Mods)). +add_conf(Name, Mod, Conf) when is_tuple(Conf) -> + add_job(cast_to_list(Name), {Mod,[Conf]}); + +add_conf(Name, Mod, Confs) when is_list(Confs) -> + add_job(cast_to_list(Name), {Mod,Confs}). + add_case(Mod, Case) when is_atom(Mod), is_atom(Case) -> add_job(atom_to_list(Mod), {Mod,Case}). @@ -283,6 +292,12 @@ add_module_with_skip(Mod, Skip) when is_atom(Mod) -> add_module_with_skip(Name, Mods, Skip) when is_list(Mods) -> add_job(cast_to_list(Name), lists:map(fun(Mod) -> {Mod,all} end, Mods), Skip). +add_conf_with_skip(Name, Mod, Conf, Skip) when is_tuple(Conf) -> + add_job(cast_to_list(Name), {Mod,[Conf]}, Skip); + +add_conf_with_skip(Name, Mod, Confs, Skip) when is_list(Confs) -> + add_job(cast_to_list(Name), {Mod,Confs}, Skip). + add_case_with_skip(Mod, Case, Skip) when is_atom(Mod), is_atom(Case) -> add_job(atom_to_list(Mod), {Mod,Case}, Skip). @@ -1549,8 +1564,8 @@ temp_nodename([Chr|Base], Acc) -> %% of cases can not be calculated and NoOfCases = unknown. count_test_cases(TopCases, SkipCases) when is_list(TopCases) -> case collect_all_cases(TopCases, SkipCases) of - {error,_} -> - error; + {error,_Why} = Error -> + Error; TestSpec -> {get_suites(TestSpec, []), case remove_conf(TestSpec) of @@ -1656,22 +1671,31 @@ do_test_cases(TopCases, SkipCases, put(test_server_case_num, 0), TestSpec = add_init_and_end_per_suite(TestSpec0, undefined, undefined), + TI = get_target_info(), print(1, "Starting test~s", [print_if_known(N, {", ~w test cases",[N]}, {" (with repeated test cases)",[]})]), - test_server_sup:framework_call(report, [tests_start, - {get(test_server_name),N}]), - print(html, - "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n" - "<!-- autogenerated by '"++atom_to_list(?MODULE)++"'. -->\n" - "<html>\n" - "<head><title>Test ~p results</title>\n" - "<meta http-equiv=\"cache-control\" content=\"no-cache\">\n" - "</head>\n" - "<body bgcolor=\"white\" text=\"black\" " - "link=\"blue\" vlink=\"purple\" alink=\"red\">" - "<h2>Results from test ~p</h2>\n", - [get(test_server_name),get(test_server_name)]), + Test = get(test_server_name), + test_server_sup:framework_call(report, [tests_start,{Test,N}]), + + Header = + case test_server_sup:framework_call(overview_html_header, [Test], "") of + "" -> + TestName = lists:flatten(io_lib:format("~p", [Test])), + ["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n", + "<!-- autogenerated by '", atom_to_list(?MODULE), "'. -->\n", + "<html>\n", + "<head><title>Test ", TestName, " results</title>\n", + "<meta http-equiv=\"cache-control\" content=\"no-cache\">\n", + "</head>\n", + "<body bgcolor=\"white\" text=\"black\" ", + "link=\"blue\" vlink=\"purple\" alink=\"red\">", + "<h2>Results from test ", TestName, "</h2>\n"]; + Html -> + ["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n", + "<!-- autogenerated by '", atom_to_list(?MODULE), "'. -->\n" | Html] + end, + print(html, Header, []), print_timestamp(html, "Test started at "), print(html, "<p>Host:<br>\n"), @@ -1702,7 +1726,7 @@ do_test_cases(TopCases, SkipCases, [?suitelog_name,?coverlog_name]), print(html,"<p>~s" "<p>\n" - "<table border=3 cellpadding=5>" + "<table bgcolor=\"white\" border=\"3\" cellpadding=\"5\">" "<tr><th>Num</th><th>Module</th><th>Case</th><th>Log</th>" "<th>Time</th><th>Result</th><th>Comment</th></tr>\n", [print_if_known(N, {"Suite contains ~p test cases.\n",[N]}, @@ -2052,10 +2076,6 @@ run_test_cases(TestSpec, Config, TimetrapData) -> html_convert_modules(TestSpec, Config), - %%! For readable tracing... - %%! Config1 = [{data_dir,""},{priv_dir,""},{nodes,[]}], - %%! run_test_cases_loop(TestSpec, [[]], TimetrapData, [], []), - run_test_cases_loop(TestSpec, [Config], TimetrapData, [], []), maybe_get_privdir(), @@ -3537,7 +3557,7 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit, Where, case test_server_sup:framework_call(warn, [nodes], true) of true -> case catch controller_call(kill_slavenodes) of - {'EXIT',_}=Exit -> + {'EXIT',_} = Exit -> print(minor, "WARNING: There might be slavenodes left in the" " system. I tried to kill them, but I failed: ~p\n", @@ -4417,7 +4437,7 @@ collect_all_cases(Top, Skip) when is_list(Skip) -> Result = case collect_cases(Top, #cc{mod=[],skip=Skip}) of {ok,Cases,_St} -> Cases; - Other -> Other + Other -> Other end, Result. @@ -4429,9 +4449,9 @@ collect_cases([Case|Cs0], St0) -> case collect_cases(Cs0, St1) of {ok,FlatCases2,St} -> {ok,FlatCases1 ++ FlatCases2,St}; - {error,_Reason}=Error -> Error + {error,_Reason} = Error -> Error end; - {error,_Reason}=Error -> Error + {error,_Reason} = Error -> Error end; @@ -4467,39 +4487,55 @@ collect_cases({conf,Props,InitMF,CaseList,FinF}, St) when is_atom(FinF) -> Props1 -> collect_cases({conf,Props1,InitMF,CaseList,{St#cc.mod,FinF}}, St) end; -collect_cases({conf,Props,InitMF,CaseList,FinMF}, St0) -> - case collect_cases(CaseList, St0) of - {ok,[],_St}=Empty -> - Empty; - {ok,FlatCases,St} -> +collect_cases({conf,Props,InitMF,CaseList,FinMF} = Conf, St) -> + case init_props(Props) of + {error,_} -> + {ok,[],St}; + Props1 -> Ref = make_ref(), - case in_skip_list(InitMF, St#cc.skip) of - {true,Comment} -> + Skips = St#cc.skip, + case in_skip_list({St#cc.mod,Conf}, Skips) of + {true,Comment} -> % conf init skipped {ok,[{skip_case,{conf,Ref,InitMF,Comment}} | - FlatCases ++ [{conf,Ref,[],FinMF}]],St}; + [] ++ [{conf,Ref,[],FinMF}]],St}; + {true,Name,Comment} when is_atom(Name) -> % all cases skipped + {ok,[{skip_case,{{St#cc.mod,{group,Name}},Comment}}],St}; + {true,ToSkip,_} when is_list(ToSkip) -> % some cases skipped + case collect_cases(CaseList, + St#cc{skip=ToSkip++Skips}) of + {ok,[],_St} = Empty -> + Empty; + {ok,FlatCases,St1} -> + {ok,[{conf,Ref,Props1,InitMF} | + FlatCases ++ [{conf,Ref, + keep_name(Props1), + FinMF}]],St1#cc{skip=Skips}}; + {error,_Reason} = Error -> + Error + end; false -> - case init_props(Props) of - {error,_} -> - {ok,[],St}; - Props1 -> + case collect_cases(CaseList, St) of + {ok,[],_St} = Empty -> + Empty; + {ok,FlatCases,St1} -> {ok,[{conf,Ref,Props1,InitMF} | FlatCases ++ [{conf,Ref, keep_name(Props1), - FinMF}]],St} + FinMF}]],St1}; + {error,_Reason} = Error -> + Error end - end; - {error,_Reason}=Error -> - Error + end end; collect_cases({make,InitMFA,CaseList,FinMFA}, St0) -> case collect_cases(CaseList, St0) of - {ok,[],_St}=Empty -> Empty; + {ok,[],_St} = Empty -> Empty; {ok,FlatCases,St} -> Ref = make_ref(), {ok,[{make,Ref,InitMFA}|FlatCases ++ [{make,Ref,FinMFA}]],St}; - {error,_Reason}=Error -> Error + {error,_Reason} = Error -> Error end; collect_cases({Module, Cases}, St) when is_list(Cases) -> @@ -4517,8 +4553,11 @@ collect_cases({_Mod,_Case,_Args}=Spec, St) -> collect_case(Spec, St); collect_cases(Case, St) when is_atom(Case), is_atom(St#cc.mod) -> collect_case({St#cc.mod,Case}, St); -collect_cases(Other, _St) -> - {error,{bad_subtest_spec,Other}}. +collect_cases(Other, St) -> + {error,{bad_subtest_spec,St#cc.mod,Other}}. + +collect_case({Mod,{conf,_,_,_,_}=Conf}, St) -> + collect_case_invoke(Mod, Conf, [], St); collect_case(MFA, St) -> case in_skip_list(MFA, St#cc.skip) of @@ -4555,6 +4594,7 @@ collect_case_invoke(Mod, Case, MFA, St) -> collect_subcases(Mod, Case, MFA, St, Suite) -> case Suite of [] when Case == all -> {ok,[],St}; + [] when element(1, Case) == conf -> {ok,[],St}; [] -> {ok,[MFA],St}; %%%! --- START Kept for backwards compatibilty --- %%%! Requirements are not used @@ -4565,6 +4605,8 @@ collect_subcases(Mod, Case, MFA, St, Suite) -> %%%! --- END Kept for backwards compatibilty --- {Skip,Reason} when Skip==skip; Skip==skipped -> {ok,[{skip_case,{MFA,Reason}}],St}; + {error,Reason} -> + throw(Reason); SubCases -> collect_case_subcases(Mod, Case, SubCases, St) end. @@ -4626,6 +4668,47 @@ check_deny_req(Req, DenyList) -> false -> granted end. +in_skip_list({Mod,{conf,Props,InitMF,_CaseList,_FinMF}}, SkipList) -> + case in_skip_list(InitMF, SkipList) of + {true,_} = Yes -> + Yes; + _ -> + case proplists:get_value(name, Props) of + undefined -> + false; + Name -> + ToSkip = + lists:flatmap( + fun({M,{conf,SProps,_,SCaseList,_},Cmt}) when + M == Mod -> + case proplists:get_value(name, SProps) of + all -> + [{M,all,Cmt}]; + Name -> + case SCaseList of + all -> + [{M,all,Cmt}]; + _ -> + [{M,F,Cmt} || F <- SCaseList] + end; + _ -> + [] + end; + (_) -> + [] + end, SkipList), + case ToSkip of + [] -> + false; + _ -> + case lists:keysearch(all, 2, ToSkip) of + {value,{_,_,Cmt}} -> {true,Name,Cmt}; + _ -> {true,ToSkip,""} + end + end + end + end; + in_skip_list({Mod,Func,_Args}, SkipList) -> in_skip_list({Mod,Func}, SkipList); in_skip_list({Mod,Func}, [{Mod,Funcs,Comment}|SkipList]) when is_list(Funcs) -> diff --git a/lib/test_server/vsn.mk b/lib/test_server/vsn.mk index ee3c957d59..4c3df28814 100644 --- a/lib/test_server/vsn.mk +++ b/lib/test_server/vsn.mk @@ -1,2 +1,2 @@ -TEST_SERVER_VSN = 3.4 +TEST_SERVER_VSN = 3.4.1 diff --git a/lib/tv/src/tv_pg_gridfcns.erl b/lib/tv/src/tv_pg_gridfcns.erl index ab88e2864f..3d23c8a69f 100644 --- a/lib/tv/src/tv_pg_gridfcns.erl +++ b/lib/tv/src/tv_pg_gridfcns.erl @@ -205,8 +205,8 @@ resize_grid(NewWidth, NewHeight, ProcVars) -> check_nof_cols(ColsShown, (NofColsShown - NofCols), ColFrameIds, ColIds, RowIds, NofRows, RowHeight, FgColor, BgColor ), - clear_fields(lists:nthtail(NofColsShown, NewColIds), - lists:nthtail(NofRowsShown, NewRowIds)), + clear_fields(safe_nthtail(NofColsShown, NewColIds), + safe_nthtail(NofRowsShown, NewRowIds)), RowsToUpdate = lists:sublist(NewRowIds, NofRowsShown), @@ -279,7 +279,7 @@ resize_grid_column(RealCol, VirtualCol, Xdiff, ProcVars) -> % Update the ColWidths list. TempColWidths = lists:sublist(ColWidths, VirtualCol - 1) ++ - [NewWidthOfCol | lists:nthtail(VirtualCol, ColWidths)], + [NewWidthOfCol | safe_nthtail(VirtualCol, ColWidths)], % Check the other columns, whether a new column has to be created. ColsShown = compute_cols_shown(FirstColShown, TempColWidths, GridWidth, @@ -455,8 +455,8 @@ scroll_grid_horizontally(NewFirstColShown, ProcVars) -> refresh_visible_rows(RowsToUpdate, NewFirstColShown, NofColsShown, RowDataList, ListAsStr), % Clear fields currently not visible. - clear_fields(lists:nthtail(NofColsShown, NewColIds), - lists:nthtail(NofRowsShown, NewRowIds)), + clear_fields(safe_nthtail(NofColsShown, NewColIds), + safe_nthtail(NofRowsShown, NewRowIds)), NewGridP = GridP#grid_params{nof_cols = NewNofCols, @@ -1565,7 +1565,7 @@ update_col_widths(ColsShown, ColWidths, FirstColShown, DefaultColWidth) -> if NecessaryNofVirtualCols > NofVirtualCols -> TailNo = NofVirtualCols - FirstColShown + 1, % Always >= 0 !!! - NewColWidths ++ lists:nthtail(TailNo, ColsShown); + NewColWidths ++ safe_nthtail(TailNo, ColsShown); true -> NewColWidths end. @@ -1653,7 +1653,7 @@ compute_cols_shown(FirstColShown, ColWidths, GridWidth, _NofCols, DefaultColWidt ColWidthsLength < FirstColShown -> []; true -> - lists:nthtail(FirstColShown - 1, ColWidths) + safe_nthtail(FirstColShown - 1, ColWidths) end, compute_cols_shown(UsedColWidths, GridWidth, DefaultColWidth). @@ -1894,3 +1894,21 @@ extract_ids_for_one_row(N, [ColIds | Tail]) -> %%%--------------------------------------------------------------------- %%% END of functions used to create the grid. %%%--------------------------------------------------------------------- + + +%%====================================================================== +%% Function: +%% +%% Return Value: +%% +%% Description: +%% +%% Parameters: +%%====================================================================== + + +safe_nthtail(_, []) -> []; +safe_nthtail(1, [_|T]) -> T; +safe_nthtail(N, [_|T]) when N > 1 -> + safe_nthtail(N - 1, T); +safe_nthtail(0, L) when is_list(L) -> L. |