aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/asn1/src/asn1_db.erl4
-rw-r--r--lib/asn1/src/asn1ct.erl3
-rw-r--r--lib/asn1/test/asn1_test_lib.erl53
-rw-r--r--lib/common_test/src/ct_gen_conn.erl10
-rw-r--r--lib/common_test/src/ct_logs.erl17
-rw-r--r--lib/common_test/src/ct_telnet.erl88
-rw-r--r--lib/common_test/test/ct_config_SUITE.erl53
-rw-r--r--lib/common_test/test/ct_netconfc_SUITE_data/netconfc_remote_SUITE.erl4
-rw-r--r--lib/common_test/test/ct_netconfc_SUITE_data/ns.erl6
-rw-r--r--lib/compiler/doc/src/notes.xml17
-rw-r--r--lib/compiler/src/beam_block.erl98
-rw-r--r--lib/compiler/src/beam_dead.erl7
-rw-r--r--lib/compiler/src/beam_jump.erl22
-rw-r--r--lib/compiler/src/compile.erl20
-rw-r--r--lib/compiler/test/beam_block_SUITE.erl27
-rw-r--r--lib/compiler/test/beam_jump_SUITE.erl19
-rw-r--r--lib/compiler/test/match_SUITE.erl13
-rw-r--r--lib/compiler/vsn.mk2
-rw-r--r--lib/crypto/c_src/crypto.c2
-rw-r--r--lib/debugger/doc/src/Makefile2
-rw-r--r--lib/dialyzer/doc/src/notes.xml14
-rw-r--r--lib/dialyzer/src/dialyzer.erl2
-rw-r--r--lib/dialyzer/src/dialyzer_dataflow.erl62
-rw-r--r--lib/dialyzer/test/Makefile1
-rw-r--r--lib/dialyzer/test/abstract_SUITE.erl102
-rw-r--r--lib/dialyzer/test/dialyzer_common.erl11
-rw-r--r--lib/dialyzer/test/map_SUITE_data/src/mand_remote_val/a.erl9
-rw-r--r--lib/dialyzer/test/map_SUITE_data/src/mand_remote_val/b.erl3
-rw-r--r--lib/dialyzer/vsn.mk2
-rw-r--r--lib/diameter/src/Makefile2
-rw-r--r--lib/edoc/src/edoc_extract.erl14
-rw-r--r--lib/edoc/src/edoc_lib.erl2
-rw-r--r--lib/edoc/src/edoc_tags.erl11
-rwxr-xr-xlib/erl_docgen/priv/bin/xref_mod_app.escript2
-rw-r--r--lib/erl_docgen/priv/dtd/erlref.dtd2
-rw-r--r--lib/erl_docgen/priv/xsl/db_html.xsl4
-rw-r--r--lib/erl_docgen/src/docgen_edoc_xml_cb.erl142
-rw-r--r--lib/gs/doc/src/Makefile2
-rw-r--r--lib/hipe/cerl/erl_types.erl20
-rw-r--r--lib/ic/doc/src/Makefile2
-rw-r--r--lib/inets/doc/src/mod_esi.xml2
-rw-r--r--lib/inets/doc/src/notes.xml33
-rw-r--r--lib/inets/src/ftp/ftp.erl6
-rw-r--r--lib/inets/src/http_server/httpd_conf.erl12
-rw-r--r--lib/inets/src/http_server/httpd_example.erl11
-rw-r--r--lib/inets/src/http_server/mod_esi.erl66
-rw-r--r--lib/inets/src/inets_app/inets.appup.src6
-rw-r--r--lib/inets/test/httpd_SUITE.erl60
-rw-r--r--lib/inets/test/inets_SUITE.erl14
-rw-r--r--lib/inets/test/inets_sup_SUITE.erl38
-rw-r--r--lib/inets/vsn.mk2
-rw-r--r--lib/jinterface/doc/src/Makefile2
-rw-r--r--lib/kernel/doc/src/notes.xml35
-rw-r--r--lib/kernel/src/code_server.erl26
-rw-r--r--lib/kernel/src/erl_epmd.erl6
-rw-r--r--lib/kernel/src/inet_tcp_dist.erl2
-rw-r--r--lib/kernel/src/os.erl64
-rw-r--r--lib/kernel/test/gen_sctp_SUITE.erl148
-rw-r--r--lib/kernel/test/os_SUITE.erl40
-rw-r--r--lib/kernel/vsn.mk2
-rw-r--r--lib/observer/doc/src/notes.xml17
-rw-r--r--lib/observer/src/observer_perf_wx.erl40
-rw-r--r--lib/observer/vsn.mk2
-rw-r--r--lib/odbc/configure.in2
-rw-r--r--lib/odbc/test/odbc_connect_SUITE.erl188
-rw-r--r--lib/odbc/test/odbc_data_type_SUITE.erl421
-rw-r--r--lib/odbc/test/odbc_query_SUITE.erl464
-rw-r--r--lib/odbc/test/odbc_start_SUITE.erl37
-rw-r--r--lib/odbc/test/odbc_test_lib.erl6
-rw-r--r--lib/reltool/src/reltool_server.erl151
-rw-r--r--lib/reltool/test/reltool_server_SUITE.erl37
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/x-1.0/ebin/x.app7
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/x-1.0/src/x1.erl5
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/y-1.0/ebin/y.app8
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/y-1.0/src/y1.erl5
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/z-1.0/ebin/z.app7
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/z-1.0/src/z1.erl5
-rw-r--r--lib/sasl/test/release_handler_SUITE.erl16
-rw-r--r--lib/sasl/test/rh_test_lib.erl2
-rw-r--r--lib/snmp/src/agent/snmpa_conf.erl16
-rw-r--r--lib/snmp/test/modules.mk1
-rw-r--r--lib/snmp/test/snmp_SUITE.erl4
-rw-r--r--lib/snmp/test/snmp_agent_conf_test.erl210
-rw-r--r--lib/ssh/doc/src/notes.xml40
-rw-r--r--lib/ssh/src/ssh_auth.erl231
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl109
-rw-r--r--lib/ssh/src/ssh_dbg.erl37
-rw-r--r--lib/ssh/src/ssh_io.erl52
-rw-r--r--lib/ssh/test/ssh_basic_SUITE.erl84
-rw-r--r--lib/ssh/vsn.mk2
-rw-r--r--lib/ssl/doc/src/notes.xml20
-rw-r--r--lib/ssl/src/ssl.appup.src10
-rw-r--r--lib/ssl/src/ssl_connection.erl7
-rw-r--r--lib/ssl/src/ssl_handshake.erl175
-rw-r--r--lib/ssl/src/ssl_tls_dist_proxy.erl2
-rw-r--r--lib/ssl/test/ssl_ECC_SUITE.erl60
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl43
-rw-r--r--lib/ssl/test/ssl_certificate_verify_SUITE.erl123
-rw-r--r--lib/ssl/test/ssl_dist_SUITE.erl350
-rw-r--r--lib/ssl/test/ssl_test_lib.erl16
-rw-r--r--lib/ssl/vsn.mk2
-rw-r--r--lib/stdlib/doc/src/gen_statem.xml18
-rw-r--r--lib/stdlib/doc/src/notes.xml15
-rw-r--r--lib/stdlib/src/ets.erl8
-rw-r--r--lib/stdlib/src/gen_statem.erl13
-rw-r--r--lib/stdlib/src/shell.erl4
-rw-r--r--lib/stdlib/test/ets_SUITE.erl13
-rw-r--r--lib/stdlib/test/shell_SUITE.erl17
-rw-r--r--lib/stdlib/vsn.mk2
-rw-r--r--lib/syntax_tools/doc/specs/.gitignore1
-rw-r--r--lib/syntax_tools/doc/src/Makefile8
-rw-r--r--lib/syntax_tools/doc/src/specs.xml14
-rw-r--r--lib/syntax_tools/src/erl_comment_scan.erl10
-rw-r--r--lib/syntax_tools/src/erl_prettypr.erl21
-rw-r--r--lib/syntax_tools/src/erl_recomment.erl4
-rw-r--r--lib/syntax_tools/src/erl_syntax_lib.erl4
-rw-r--r--lib/syntax_tools/src/erl_tidy.erl9
-rw-r--r--lib/syntax_tools/src/igor.erl6
-rw-r--r--lib/syntax_tools/test/syntax_tools_SUITE.erl10
-rw-r--r--lib/syntax_tools/test/syntax_tools_SUITE_data/erl_tidy_tilde.erl13
-rw-r--r--lib/tools/doc/src/notes.xml15
-rw-r--r--lib/tools/emacs/erlang-skels.el12
-rw-r--r--lib/tools/src/fprof.erl11
-rw-r--r--lib/tools/src/xref_base.erl4
-rw-r--r--lib/tools/test/xref_SUITE.erl17
-rw-r--r--lib/tools/vsn.mk2
-rw-r--r--lib/wx/api_gen/wx_extra/wxListCtrl.erl62
-rw-r--r--lib/wx/api_gen/wx_extra/wxXmlResource.erl6
-rw-r--r--lib/wx/c_src/wxe_main.cpp6
-rw-r--r--lib/wx/doc/specs/.gitignore1
-rw-r--r--lib/wx/doc/src/Makefile6
-rw-r--r--lib/wx/doc/src/specs.xml232
-rw-r--r--lib/wx/include/wx.hrl434
-rw-r--r--lib/wx/src/gen/wxListCtrl.erl62
-rw-r--r--lib/wx/src/gen/wxXmlResource.erl6
-rw-r--r--lib/wx/src/wx_object.erl84
136 files changed, 3669 insertions, 1946 deletions
diff --git a/lib/asn1/src/asn1_db.erl b/lib/asn1/src/asn1_db.erl
index 557eca0ffd..869ea310aa 100644
--- a/lib/asn1/src/asn1_db.erl
+++ b/lib/asn1/src/asn1_db.erl
@@ -106,7 +106,9 @@ loop(#state{parent = Parent, monitor = MRef, table = Table,
loop(State);
{save, OutFile, Mod} ->
[{_,Mtab}] = ets:lookup(Table, Mod),
- ok = ets:tab2file(Mtab, OutFile),
+ TempFile = OutFile ++ ".#temp",
+ ok = ets:tab2file(Mtab, TempFile),
+ ok = file:rename(TempFile, OutFile),
loop(State);
{From, {new, Mod, Erule}} ->
[] = ets:lookup(Table, Mod), %Assertion.
diff --git a/lib/asn1/src/asn1ct.erl b/lib/asn1/src/asn1ct.erl
index dd269f095d..8783b5418d 100644
--- a/lib/asn1/src/asn1ct.erl
+++ b/lib/asn1/src/asn1ct.erl
@@ -221,9 +221,8 @@ check_pass(#st{code=M,file=File,includes=Includes,
{error,St#st{error=Reason}}
end.
-save_pass(#st{code=M,erule=Erule,dbfile=DbFile}=St) ->
+save_pass(#st{code=M,erule=Erule}=St) ->
ok = asn1ct_check:storeindb(#state{erule=Erule}, M),
- asn1_db:dbsave(DbFile,M#module.name),
{ok,St}.
parse_listing(#st{code=Code,outfile=OutFile0}=St) ->
diff --git a/lib/asn1/test/asn1_test_lib.erl b/lib/asn1/test/asn1_test_lib.erl
index 1b4c3b3c77..dc614db4f2 100644
--- a/lib/asn1/test/asn1_test_lib.erl
+++ b/lib/asn1/test/asn1_test_lib.erl
@@ -34,12 +34,16 @@ run_dialyzer() ->
compile(File, Config, Options) -> compile_all([File], Config, Options).
-compile_all(Files, Config, Options) ->
+compile_all(Files, Config, Options0) ->
DataDir = proplists:get_value(data_dir, Config),
CaseDir = proplists:get_value(case_dir, Config),
- [compile_file(filename:join(DataDir, F), [{outdir, CaseDir},
- debug_info|Options])
- || F <- Files],
+ Options = [{outdir,CaseDir},debug_info|Options0],
+
+ Comp = fun(F) ->
+ compile_file(filename:join(DataDir, F), Options)
+ end,
+ p_run(Comp, Files),
+
dialyze(Files, Options),
ok.
@@ -94,9 +98,9 @@ compile_file(File, Options) ->
try
ok = asn1ct:compile(File, [warnings_as_errors|Options])
catch
- Class:Reason ->
- ct:print("Failed to compile ~s\n", [File]),
- erlang:error({compile_failed, {File, Options}, {Class, Reason}})
+ _:Reason ->
+ ct:print("Failed to compile ~s\n~p", [File,Reason]),
+ error
end.
compile_erlang(Mod, Config, Options) ->
@@ -219,3 +223,38 @@ ber_get_len(<<0:1,L:7,T/binary>>) ->
ber_get_len(<<1:1,Octets:7,T0/binary>>) ->
<<L:Octets/unit:8,T/binary>> = T0,
{L,T}.
+
+%% p_run(fun(Data) -> ok|error, List) -> ok
+%% Will fail the test case if there were any errors.
+
+p_run(Test, List) ->
+ S = erlang:system_info(schedulers),
+ N = case test_server:is_cover() of
+ false ->
+ S + 1;
+ true ->
+ %% Cover is running. Using too many processes
+ %% could slow us down.
+ min(S, 4)
+ end,
+ %%io:format("p_run: ~p parallel processes\n", [N]),
+ p_run_loop(Test, List, N, [], 0).
+
+p_run_loop(_, [], _, [], Errors) ->
+ case Errors of
+ 0 -> ok;
+ N -> ct:fail({N,errors})
+ end;
+p_run_loop(Test, [H|T], N, Refs, Errors) when length(Refs) < N ->
+ {_,Ref} = erlang:spawn_monitor(fun() -> exit(Test(H)) end),
+ p_run_loop(Test, T, N, [Ref|Refs], Errors);
+p_run_loop(Test, List, N, Refs0, Errors0) ->
+ receive
+ {'DOWN',Ref,process,_,Res} ->
+ Errors = case Res of
+ ok -> Errors0;
+ error -> Errors0+1
+ end,
+ Refs = Refs0 -- [Ref],
+ p_run_loop(Test, List, N, Refs, Errors)
+ end.
diff --git a/lib/common_test/src/ct_gen_conn.erl b/lib/common_test/src/ct_gen_conn.erl
index 23ba1ab981..8b59d3ab23 100644
--- a/lib/common_test/src/ct_gen_conn.erl
+++ b/lib/common_test/src/ct_gen_conn.erl
@@ -27,7 +27,7 @@
-export([start/4, stop/1, get_conn_pid/1, check_opts/1]).
-export([call/2, call/3, return/2, do_within_time/2]).
--export([log/3, start_log/1, cont_log/2, end_log/0]).
+-export([log/3, start_log/1, cont_log/2, cont_log_no_timestamp/2, end_log/0]).
%%----------------------------------------------------------------------
%% Exported types
@@ -175,6 +175,14 @@ cont_log(Format,Args) ->
log(cont_log,[Format,Args]).
%%%-----------------------------------------------------------------
+%%% @spec cont_log_no_timestamp(Format,Args) -> ok
+%%%
+%%% @doc Log activities on the current connection (tool-internal use only).
+%%% @see ct_logs:cont_log/2
+cont_log_no_timestamp(Format,Args) ->
+ log(cont_log_no_timestamp,[Format,Args]).
+
+%%%-----------------------------------------------------------------
%%% @spec end_log() -> ok
%%%
%%% @doc Log activities on the current connection (tool-internal use only).
diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl
index 53245c596a..9282a9f81d 100644
--- a/lib/common_test/src/ct_logs.erl
+++ b/lib/common_test/src/ct_logs.erl
@@ -32,7 +32,7 @@
-export([init/2, close/2, init_tc/1, end_tc/1]).
-export([register_groupleader/2, unregister_groupleader/1]).
-export([get_log_dir/0, get_log_dir/1]).
--export([log/3, start_log/1, cont_log/2, end_log/0]).
+-export([log/3, start_log/1, cont_log/2, cont_log_no_timestamp/2, end_log/0]).
-export([set_stylesheet/2, clear_stylesheet/1]).
-export([add_external_logs/1, add_link/3]).
-export([make_last_run_index/0]).
@@ -373,6 +373,20 @@ cont_log(Format,Args) ->
ok.
%%%-----------------------------------------------------------------
+%%% @spec cont_log_no_timestamp(Format,Args) -> ok
+%%%
+%%% @doc Adds information about an activity (tool-internal use only).
+%%%
+%%% @see start_log/1
+%%% @see end_log/0
+cont_log_no_timestamp([],[]) ->
+ ok;
+cont_log_no_timestamp(Format,Args) ->
+ cast({log,sync,self(),group_leader(),ct_internal,?MAX_IMPORTANCE,
+ [{Format,Args}],true}),
+ ok.
+
+%%%-----------------------------------------------------------------
%%% @spec end_log() -> ok
%%%
%%% @doc Ends the logging of an activity (tool-internal use only).
@@ -595,7 +609,6 @@ div_header(Class,Printer) ->
div_footer() ->
"</pre></div>\n<pre>".
-
maybe_log_timestamp() ->
{MS,S,US} = ?now,
case get(log_timestamp) of
diff --git a/lib/common_test/src/ct_telnet.erl b/lib/common_test/src/ct_telnet.erl
index 8fb411ec4f..34d27ed5f4 100644
--- a/lib/common_test/src/ct_telnet.erl
+++ b/lib/common_test/src/ct_telnet.erl
@@ -954,7 +954,7 @@ log(#state{name=Name,teln_pid=TelnPid,host=Host,port=Port},
true ->
ok;
false ->
- ct_gen_conn:cont_log(String,Args)
+ ct_gen_conn:cont_log_no_timestamp(String,Args)
end;
ForcePrint == true ->
@@ -965,7 +965,7 @@ log(#state{name=Name,teln_pid=TelnPid,host=Host,port=Port},
%% called
ct_gen_conn:log(heading(Action,Name1),String,Args);
false ->
- ct_gen_conn:cont_log(String,Args)
+ ct_gen_conn:cont_log_no_timestamp(String,Args)
end
end
end.
@@ -1224,7 +1224,6 @@ teln_expect1(Name,Pid,Data,Pattern,Acc,EO=#eo{idle_timeout=IdleTO,
EOMod = if TotalTO /= infinity -> EO#eo{total_timeout=trunc(TotalTO)};
true -> EO
end,
-
ExpectFun = case EOMod#eo.seq of
true -> fun() ->
seq_expect(Name,Pid,Data,Pattern,Acc,EOMod)
@@ -1247,38 +1246,34 @@ teln_expect1(Name,Pid,Data,Pattern,Acc,EO=#eo{idle_timeout=IdleTO,
true ->
IdleTO
end,
+ {PatOrPats1,Acc1,Rest1} = case NotFinished of
+ {nomatch,Rest0} ->
+ %% one expect
+ {Pattern,[],Rest0};
+ {continue,Pats0,Acc0,Rest0} ->
+ %% sequence
+ {Pats0,Acc0,Rest0}
+ end,
case timer:tc(ct_gen_conn, do_within_time, [Fun,BreakAfter]) of
- {_,{error,Reason}} ->
+ {_,{error,Reason}} ->
%% A timeout will occur when the telnet connection
%% is idle for EO#eo.idle_timeout milliseconds.
+ if Rest1 /= [] ->
+ log(name_or_pid(Name,Pid)," ~ts",[Rest1]);
+ true ->
+ ok
+ end,
{error,Reason};
{_,{ok,Data1}} when TotalTO == infinity ->
- case NotFinished of
- {nomatch,Rest} ->
- %% One expect
- teln_expect1(Name,Pid,Rest++Data1,
- Pattern,[],EOMod);
- {continue,Patterns1,Acc1,Rest} ->
- %% Sequence
- teln_expect1(Name,Pid,Rest++Data1,
- Patterns1,Acc1,EOMod)
- end;
+ teln_expect1(Name,Pid,Rest1++Data1,PatOrPats1,Acc1,EOMod);
{Elapsed,{ok,Data1}} ->
TVal = TotalTO - (Elapsed/1000),
if TVal =< 0 ->
{error,timeout};
true ->
EO1 = EO#eo{total_timeout = TVal},
- case NotFinished of
- {nomatch,Rest} ->
- %% One expect
- teln_expect1(Name,Pid,Rest++Data1,
- Pattern,[],EO1);
- {continue,Patterns1,Acc1,Rest} ->
- %% Sequence
- teln_expect1(Name,Pid,Rest++Data1,
- Patterns1,Acc1,EO1)
- end
+ teln_expect1(Name,Pid,Rest1++Data1,
+ PatOrPats1,Acc1,EO1)
end
end
end.
@@ -1416,14 +1411,14 @@ match_lines(Name,Pid,Data,Patterns,EO) ->
case one_line(Data,[]) of
{noline,Rest} when FoundPrompt=/=false ->
%% This is the line including the prompt
- case match_line(Name,Pid,Rest,Patterns,FoundPrompt,EO) of
+ case match_line(Name,Pid,Rest,Patterns,FoundPrompt,false,EO) of
nomatch ->
{nomatch,prompt};
{Tag,Match} ->
{Tag,Match,[]}
end;
{noline,Rest} when EO#eo.prompt_check==false ->
- case match_line(Name,Pid,Rest,Patterns,false,EO) of
+ case match_line(Name,Pid,Rest,Patterns,false,false,EO) of
nomatch ->
{nomatch,Rest};
{Tag,Match} ->
@@ -1432,7 +1427,7 @@ match_lines(Name,Pid,Data,Patterns,EO) ->
{noline,Rest} ->
{nomatch,Rest};
{Line,Rest} ->
- case match_line(Name,Pid,Line,Patterns,false,EO) of
+ case match_line(Name,Pid,Line,Patterns,false,true,EO) of
nomatch ->
match_lines(Name,Pid,Rest,Patterns,EO);
{Tag,Match} ->
@@ -1440,45 +1435,50 @@ match_lines(Name,Pid,Data,Patterns,EO) ->
end
end.
-
%% For one line, match each pattern
-match_line(Name,Pid,Line,Patterns,FoundPrompt,EO) ->
- match_line(Name,Pid,Line,Patterns,FoundPrompt,EO,match).
+match_line(Name,Pid,Line,Patterns,FoundPrompt,Terminated,EO) ->
+ match_line(Name,Pid,Line,Patterns,FoundPrompt,Terminated,EO,match).
-match_line(Name,Pid,Line,[prompt|Patterns],false,EO,RetTag) ->
- match_line(Name,Pid,Line,Patterns,false,EO,RetTag);
-match_line(Name,Pid,Line,[prompt|_Patterns],FoundPrompt,_EO,RetTag) ->
+match_line(Name,Pid,Line,[prompt|Patterns],false,Term,EO,RetTag) ->
+ match_line(Name,Pid,Line,Patterns,false,Term,EO,RetTag);
+match_line(Name,Pid,Line,[prompt|_Patterns],FoundPrompt,_Term,_EO,RetTag) ->
log(name_or_pid(Name,Pid)," ~ts",[Line]),
log(name_or_pid(Name,Pid),"PROMPT: ~ts",[FoundPrompt]),
{RetTag,{prompt,FoundPrompt}};
-match_line(Name,Pid,Line,[{prompt,PromptType}|_Patterns],FoundPrompt,_EO,RetTag)
- when PromptType==FoundPrompt ->
+match_line(Name,Pid,Line,[{prompt,PromptType}|_Patterns],FoundPrompt,_Term,
+ _EO,RetTag) when PromptType==FoundPrompt ->
log(name_or_pid(Name,Pid)," ~ts",[Line]),
log(name_or_pid(Name,Pid),"PROMPT: ~ts",[FoundPrompt]),
{RetTag,{prompt,FoundPrompt}};
-match_line(Name,Pid,Line,[{prompt,PromptType}|Patterns],FoundPrompt,EO,RetTag)
+match_line(Name,Pid,Line,[{prompt,PromptType}|Patterns],FoundPrompt,Term,
+ EO,RetTag)
when PromptType=/=FoundPrompt ->
- match_line(Name,Pid,Line,Patterns,FoundPrompt,EO,RetTag);
-match_line(Name,Pid,Line,[{Tag,Pattern}|Patterns],FoundPrompt,EO,RetTag) ->
+ match_line(Name,Pid,Line,Patterns,FoundPrompt,Term,EO,RetTag);
+match_line(Name,Pid,Line,[{Tag,Pattern}|Patterns],FoundPrompt,Term,EO,RetTag) ->
case re:run(Line,Pattern,[{capture,all,list}]) of
nomatch ->
- match_line(Name,Pid,Line,Patterns,FoundPrompt,EO,RetTag);
+ match_line(Name,Pid,Line,Patterns,FoundPrompt,Term,EO,RetTag);
{match,Match} ->
log(name_or_pid(Name,Pid),"MATCH: ~ts",[Line]),
{RetTag,{Tag,Match}}
end;
-match_line(Name,Pid,Line,[Pattern|Patterns],FoundPrompt,EO,RetTag) ->
+match_line(Name,Pid,Line,[Pattern|Patterns],FoundPrompt,Term,EO,RetTag) ->
case re:run(Line,Pattern,[{capture,all,list}]) of
nomatch ->
- match_line(Name,Pid,Line,Patterns,FoundPrompt,EO,RetTag);
+ match_line(Name,Pid,Line,Patterns,FoundPrompt,Term,EO,RetTag);
{match,Match} ->
log(name_or_pid(Name,Pid),"MATCH: ~ts",[Line]),
{RetTag,Match}
end;
-match_line(Name,Pid,Line,[],FoundPrompt,EO,match) ->
- match_line(Name,Pid,Line,EO#eo.haltpatterns,FoundPrompt,EO,halt);
-match_line(Name,Pid,Line,[],_FoundPrompt,_EO,halt) ->
+match_line(Name,Pid,Line,[],FoundPrompt,Term,EO,match) ->
+ match_line(Name,Pid,Line,EO#eo.haltpatterns,FoundPrompt,Term,EO,halt);
+%% print any terminated line that can not be matched
+match_line(Name,Pid,Line,[],_FoundPrompt,true,_EO,halt) ->
log(name_or_pid(Name,Pid)," ~ts",[Line]),
+ nomatch;
+%% if there's no line termination, Line is saved as Rest (above) and will
+%% be printed later
+match_line(_Name,_Pid,_Line,[],_FoundPrompt,false,_EO,halt) ->
nomatch.
one_line([$\n|Rest],Line) ->
diff --git a/lib/common_test/test/ct_config_SUITE.erl b/lib/common_test/test/ct_config_SUITE.erl
index 9879e0f20d..cbbfe408a8 100644
--- a/lib/common_test/test/ct_config_SUITE.erl
+++ b/lib/common_test/test/ct_config_SUITE.erl
@@ -113,10 +113,14 @@ userconfig_static(Config) when is_list(Config) ->
["config_static_SUITE"]).
userconfig_dynamic(Config) when is_list(Config) ->
- run_test(config_dynamic_SUITE,
- Config,
- {userconfig, {config_driver, "config_server"}},
- ["config_dynamic_SUITE"]).
+ case skip_dynamic() of
+ true -> {skip,"TimeWarpingOS"};
+ false ->
+ run_test(config_dynamic_SUITE,
+ Config,
+ {userconfig, {config_driver, "config_server"}},
+ ["config_dynamic_SUITE"])
+ end.
testspec_legacy(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
@@ -147,16 +151,20 @@ testspec_static(Config) when is_list(Config) ->
file:delete(filename:join(ConfigDir, "spec_static.spec")).
testspec_dynamic(Config) when is_list(Config) ->
- DataDir = ?config(data_dir, Config),
- ConfigDir = ?config(config_dir, Config),
- make_spec(DataDir, ConfigDir, "spec_dynamic.spec",
- [config_dynamic_SUITE],
- [{userconfig, {config_driver, "config_server"}}]),
- run_test(config_dynamic_SUITE,
- Config,
- {spec, filename:join(ConfigDir, "spec_dynamic.spec")},
- []),
- file:delete(filename:join(ConfigDir, "spec_dynamic.spec")).
+ case skip_dynamic() of
+ true -> {skip,"TimeWarpingOS"};
+ false ->
+ DataDir = ?config(data_dir, Config),
+ ConfigDir = ?config(config_dir, Config),
+ make_spec(DataDir, ConfigDir, "spec_dynamic.spec",
+ [config_dynamic_SUITE],
+ [{userconfig, {config_driver, "config_server"}}]),
+ run_test(config_dynamic_SUITE,
+ Config,
+ {spec, filename:join(ConfigDir, "spec_dynamic.spec")},
+ []),
+ file:delete(filename:join(ConfigDir, "spec_dynamic.spec"))
+ end.
@@ -198,6 +206,23 @@ setup_env(Test, Config, CTConfig) ->
reformat_events(Events, EH) ->
ct_test_support:reformat(Events, EH).
+
+%%%-----------------------------------------------------------------
+%%% Test related to 'localtime' will often fail if the test host is
+%%% time warping, so let's just skip the 'dynamic' tests then.
+skip_dynamic() ->
+ case os:getenv("TS_EXTRA_PLATFORM_LABEL") of
+ TSExtraPlatformLabel when is_list(TSExtraPlatformLabel) ->
+ case string:str(TSExtraPlatformLabel,"TimeWarpingOS") of
+ 0 -> false;
+ _ -> true
+ end;
+ _ ->
+ false
+ end.
+
+
+
%%%-----------------------------------------------------------------
%%% TEST EVENTS
%%%-----------------------------------------------------------------
diff --git a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc_remote_SUITE.erl b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc_remote_SUITE.erl
index a65275da43..f2580ad8e9 100644
--- a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc_remote_SUITE.erl
+++ b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc_remote_SUITE.erl
@@ -83,7 +83,9 @@ end_per_suite(Config) ->
%% Running the netconf server in a remote node, test that the client
%% process terminates if the remote node goes down.
remote_crash(Config) ->
- {ok,Node} = ct_slave:start(nc_remote_crash),
+ {ok,Node} = ct_slave:start(nc_remote_crash,[{boot_timeout,15},
+ {init_timeout,15},
+ {startup_timeout,15}]),
Pa = filename:dirname(code:which(?NS)),
true = rpc:call(Node,code,add_patha,[Pa]),
rpc:call(Node,code,load_file,[crypto]),
diff --git a/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl b/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl
index e62bc617fa..2412ea6aba 100644
--- a/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl
+++ b/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl
@@ -302,10 +302,14 @@ table_trans(Fun,Args) ->
S ->
apply(Fun,Args);
Pid ->
+ Ref = erlang:monitor(process,Pid),
Pid ! {table_trans,Fun,Args,self()},
receive
{table_trans_done,Result} ->
- Result
+ erlang:demonitor(Ref,[flush]),
+ Result;
+ {'DOWN',Ref,process,Pid,Reason} ->
+ exit({main_ns_proc_died,Reason})
after 20000 ->
exit(table_trans_timeout)
end
diff --git a/lib/compiler/doc/src/notes.xml b/lib/compiler/doc/src/notes.xml
index e25cdc580c..92b3d809fc 100644
--- a/lib/compiler/doc/src/notes.xml
+++ b/lib/compiler/doc/src/notes.xml
@@ -32,6 +32,23 @@
<p>This document describes the changes made to the Compiler
application.</p>
+<section><title>Compiler 7.0.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ A literal binary matching regression was introduced in
+ 19.0 where a match could fail to resolve to the right
+ clause. This has now been fixed.</p>
+ <p>
+ Own Id: OTP-13738</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Compiler 7.0</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/compiler/src/beam_block.erl b/lib/compiler/src/beam_block.erl
index 85d332c56e..ec41925beb 100644
--- a/lib/compiler/src/beam_block.erl
+++ b/lib/compiler/src/beam_block.erl
@@ -159,14 +159,43 @@ find_fixpoint(OptFun, Is0) ->
end.
%% move_allocates(Is0) -> Is
-%% Move allocate instructions upwards in the instruction stream, in the
-%% hope of getting more possibilities for optimizing away moves later.
+%% Move allocate instructions upwards in the instruction stream
+%% (within the same block), in the hope of getting more possibilities
+%% for optimizing away moves later.
%%
-%% NOTE: Moving allocation instructions is only safe because it is done
-%% immediately after code generation so that we KNOW that if {x,X} is
-%% initialized, all x registers with lower numbers are also initialized.
-%% That assumption may not be true after other optimizations, such as
-%% the beam_utils:live_opt/1 optimization.
+%% For example, we can transform the following instructions:
+%%
+%% get_tuple_element x(1) Element => x(2)
+%% allocate_zero StackSize 3 %% x(0), x(1), x(2) are live
+%%
+%% to the following instructions:
+%%
+%% allocate_zero StackSize 2 %% x(0) and x(1) are live
+%% get_tuple_element x(1) Element => x(2)
+%%
+%% NOTE: Since the beam_reorder pass has been run, it is no longer
+%% safe to assume that if x(N) is initialized, then all lower-numbered
+%% x registers are also initialized.
+%%
+%% For example, in general it is not safe to transform the following
+%% instructions:
+%%
+%% get_tuple_element x(0) Element => x(1)
+%% allocate_zero StackSize 3 %x(0), x(1), x(2) are live
+%%
+%% to the following instructions:
+%%
+%% allocate_zero StackSize 3
+%% get_tuple_element x(0) Element => x(1)
+%%
+%% The transformation is safe if and only if x(1) has been
+%% initialized previously. Unfortunately, beam_reorder may have moved
+%% a get_tuple_element instruction so that x(1) is not always
+%% initialized when this code is reached. To find whether or not x(1)
+%% is initialized, we would need to analyze all code preceding these
+%% two instructions (across branches). Since we currently don't have
+%% any practical mechanism for doing that, we will have to
+%% conservatively assume that the transformation is unsafe.
move_allocates([{block,Bl0}|Is]) ->
Bl = move_allocates_1(reverse(Bl0), []),
@@ -175,27 +204,19 @@ move_allocates([I|Is]) ->
[I|move_allocates(Is)];
move_allocates([]) -> [].
-move_allocates_1([{set,[],[],{alloc,_,_}=Alloc}|Is0], Acc0) ->
- {Is,Acc} = move_allocates_2(Alloc, Is0, Acc0),
- move_allocates_1(Is, Acc);
+move_allocates_1([I|Is], [{set,[],[],{alloc,Live0,Info}}|Acc]=Acc0) ->
+ case {alloc_may_pass(I),alloc_live_regs(I, Live0)} of
+ {false,_} ->
+ move_allocates_1(Is, [I|Acc0]);
+ {true,not_possible} ->
+ move_allocates_1(Is, [I|Acc0]);
+ {true,Live} when is_integer(Live) ->
+ A = {set,[],[],{alloc,Live,Info}},
+ move_allocates_1(Is, [A,I|Acc])
+ end;
move_allocates_1([I|Is], Acc) ->
move_allocates_1(Is, [I|Acc]);
-move_allocates_1([], Is) -> Is.
-
-move_allocates_2({alloc,Live,Info}, [{set,[],[],{alloc,Live0,Info0}}|Is], Acc) ->
- Live = Live0, % Assertion.
- Alloc = {alloc,Live,combine_alloc(Info0, Info)},
- move_allocates_2(Alloc, Is, Acc);
-move_allocates_2({alloc,Live,Info}=Alloc0, [I|Is]=Is0, Acc) ->
- case alloc_may_pass(I) of
- false ->
- {Is0,[{set,[],[],Alloc0}|Acc]};
- true ->
- Alloc = {alloc,alloc_live_regs(I, Live),Info},
- move_allocates_2(Alloc, Is, [I|Acc])
- end;
-move_allocates_2(Alloc, [], Acc) ->
- {[],[{set,[],[],Alloc}|Acc]}.
+move_allocates_1([], Acc) -> Acc.
alloc_may_pass({set,_,_,{alloc,_,_}}) -> false;
alloc_may_pass({set,_,_,{set_tuple_element,_}}) -> false;
@@ -204,9 +225,6 @@ alloc_may_pass({set,_,_,put_list}) -> false;
alloc_may_pass({set,_,_,put}) -> false;
alloc_may_pass({set,_,_,_}) -> true.
-combine_alloc({_,Ns,Nh1,Init}, {_,nostack,Nh2,[]}) ->
- {zero,Ns,beam_utils:combine_heap_needs(Nh1, Nh2),Init}.
-
%% opt([Instruction]) -> [Instruction]
%% Optimize the instruction stream inside a basic block.
@@ -393,10 +411,19 @@ eliminate_use_of_from_reg([I]=Is, From, _To, Acc) ->
%% opt_alloc(Instructions) -> Instructions'
%% Optimises all allocate instructions.
+opt_alloc([{set,[],[],{alloc,Live0,Info0}},
+ {set,[],[],{alloc,Live,Info}}|Is]) ->
+ Live = Live0, %Assertion.
+ Alloc = combine_alloc(Info0, Info),
+ I = {set,[],[],{alloc,Live,Alloc}},
+ opt_alloc([I|Is]);
opt_alloc([{set,[],[],{alloc,R,{_,Ns,Nh,[]}}}|Is]) ->
[{set,[],[],opt_alloc(Is, Ns, Nh, R)}|Is];
opt_alloc([I|Is]) -> [I|opt_alloc(Is)];
opt_alloc([]) -> [].
+
+combine_alloc({_,Ns,Nh1,Init}, {_,nostack,Nh2,[]}) ->
+ {zero,Ns,beam_utils:combine_heap_needs(Nh1, Nh2),Init}.
%% opt_alloc(Instructions, FrameSize, HeapNeed, LivingRegs) -> [Instr]
%% Generates the optimal sequence of instructions for
@@ -445,13 +472,14 @@ count_ones(Bits, Acc) ->
alloc_live_regs({set,Ds,Ss,_}, Regs0) ->
Rset = x_live(Ss, x_dead(Ds, (1 bsl Regs0)-1)),
- live_regs(Rset).
+ live_regs(0, Rset).
-live_regs(Regs) ->
- live_regs_1(0, Regs).
-
-live_regs_1(N, 0) -> N;
-live_regs_1(N, Regs) -> live_regs_1(N+1, Regs bsr 1).
+live_regs(N, 0) ->
+ N;
+live_regs(N, Regs) when Regs band 1 =:= 1 ->
+ live_regs(N+1, Regs bsr 1);
+live_regs(_, _) ->
+ not_possible.
x_dead([{x,N}|Rs], Regs) -> x_dead(Rs, Regs band (bnot (1 bsl N)));
x_dead([_|Rs], Regs) -> x_dead(Rs, Regs);
diff --git a/lib/compiler/src/beam_dead.erl b/lib/compiler/src/beam_dead.erl
index b01f58f683..6f6d742293 100644
--- a/lib/compiler/src/beam_dead.erl
+++ b/lib/compiler/src/beam_dead.erl
@@ -272,17 +272,14 @@ backward([{jump,{f,To}}=J|[{bif,Op,_,Ops,Reg}|Is]=Is0], D, Acc) ->
catch
throw:not_possible -> backward(Is0, D, [J|Acc])
end;
-backward([{test,bs_start_match2,F,Live,[R,_]=Args,Ctxt}|Is], D,
+backward([{test,bs_start_match2,F,_,[R,_],Ctxt}=I|Is], D,
[{test,bs_match_string,F,[Ctxt,Bs]},
{test,bs_test_tail2,F,[Ctxt,0]}|Acc0]=Acc) ->
- {f,To0} = F,
- To = shortcut_bs_start_match(To0, R, D),
case beam_utils:is_killed(Ctxt, Acc0, D) of
true ->
- Eq = {test,is_eq_exact,{f,To},[R,{literal,Bs}]},
+ Eq = {test,is_eq_exact,F,[R,{literal,Bs}]},
backward(Is, D, [Eq|Acc0]);
false ->
- I = {test,bs_start_match2,{f,To},Live,Args,Ctxt},
backward(Is, D, [I|Acc])
end;
backward([{test,bs_start_match2,{f,To0},Live,[Src|_]=Info,Dst}|Is], D, Acc) ->
diff --git a/lib/compiler/src/beam_jump.erl b/lib/compiler/src/beam_jump.erl
index 09cd3aa2d4..48b5a32814 100644
--- a/lib/compiler/src/beam_jump.erl
+++ b/lib/compiler/src/beam_jump.erl
@@ -167,12 +167,18 @@ share_1([{label,L}=Lbl|Is], Dict0, Seq, Acc) ->
end;
share_1([{func_info,_,_,_}=I|Is], _, [], Acc) ->
reverse(Is, [I|Acc]);
+share_1([{'catch',_,_}=I|Is], Dict0, Seq, Acc) ->
+ Dict = clean_non_sharable(Dict0),
+ share_1(Is, Dict, [I|Seq], Acc);
share_1([{'try',_,_}=I|Is], Dict0, Seq, Acc) ->
Dict = clean_non_sharable(Dict0),
share_1(Is, Dict, [I|Seq], Acc);
share_1([{try_case,_}=I|Is], Dict0, Seq, Acc) ->
Dict = clean_non_sharable(Dict0),
share_1(Is, Dict, [I|Seq], Acc);
+share_1([{catch_end,_}=I|Is], Dict0, Seq, Acc) ->
+ Dict = clean_non_sharable(Dict0),
+ share_1(Is, Dict, [I|Seq], Acc);
share_1([I|Is], Dict, Seq, Acc) ->
case is_unreachable_after(I) of
false ->
@@ -182,18 +188,18 @@ share_1([I|Is], Dict, Seq, Acc) ->
end.
clean_non_sharable(Dict) ->
- %% We are passing in or out of a 'try' block. Remove
- %% sequences that should not shared over the boundaries
- %% of a 'try' block. Since the end of the sequence must match,
- %% the only possible match between a sequence outside and
- %% a sequence inside the 'try' block is a sequence that ends
- %% with an instruction that causes an exception. Any sequence
- %% that causes an exception must contain a line/1 instruction.
+ %% We are passing in or out of a 'catch' or 'try' block. Remove
+ %% sequences that should not be shared over the boundaries of the
+ %% block. Since the end of the sequence must match, the only
+ %% possible match between a sequence outside and a sequence inside
+ %% the 'catch'/'try' block is a sequence that ends with an
+ %% instruction that causes an exception. Any sequence that causes
+ %% an exception must contain a line/1 instruction.
maps:filter(fun(K, _V) -> sharable_with_try(K) end, Dict).
sharable_with_try([{line,_}|_]) ->
%% This sequence may cause an exception and may potentially
- %% match a sequence on the other side of the 'try' block
+ %% match a sequence on the other side of the 'catch'/'try' block
%% boundary.
false;
sharable_with_try([_|Is]) ->
diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl
index 82ff8a95f3..e951a25e04 100644
--- a/lib/compiler/src/compile.erl
+++ b/lib/compiler/src/compile.erl
@@ -43,6 +43,10 @@
-type abstract_code() :: [erl_parse:abstract_form()].
+%% Internal representations used for 'from_asm' and 'from_beam' compilation can
+%% also be valid, but have no relevant types defined.
+-type forms() :: abstract_code() | cerl:c_module().
+
-type option() :: atom() | {atom(), term()} | {'d', atom(), term()}.
-type err_info() :: {erl_anno:line() | 'none',
@@ -88,7 +92,7 @@ file(File, Opt) ->
forms(Forms) -> forms(Forms, ?DEFAULT_OPTIONS).
--spec forms(abstract_code(), [option()] | option()) -> comp_ret().
+-spec forms(forms(), [option()] | option()) -> comp_ret().
forms(Forms, Opts) when is_list(Opts) ->
do_compile({forms,Forms}, [binary|Opts++env_default_opts()]);
@@ -116,7 +120,7 @@ noenv_file(File, Opts) when is_list(Opts) ->
noenv_file(File, Opt) ->
noenv_file(File, [Opt|?DEFAULT_OPTIONS]).
--spec noenv_forms(abstract_code(), [option()] | option()) -> comp_ret().
+-spec noenv_forms(forms(), [option()] | option()) -> comp_ret().
noenv_forms(Forms, Opts) when is_list(Opts) ->
do_compile({forms,Forms}, [binary|Opts]);
@@ -236,6 +240,8 @@ format_error({epp,E}) ->
epp:format_error(E);
format_error(write_error) ->
"error writing file";
+format_error({write_error, Error}) ->
+ io_lib:format("error writing file: ~ts", [file:format_error(Error)]);
format_error({rename,From,To,Error}) ->
io_lib:format("failed to rename ~ts to ~ts: ~ts",
[From,To,file:format_error(Error)]);
@@ -1206,7 +1212,7 @@ makedep_output(#compile{code=Code,options=Opts,ofile=Ofile}=St) ->
end,
{ok,St}
catch
- exit:_ ->
+ error:_ ->
%% Couldn't write to output Makefile.
Err = {St#compile.ifile,[{none,?MODULE,write_error}]},
{error,St#compile{errors=St#compile.errors++[Err]}}
@@ -1479,8 +1485,8 @@ save_binary_1(St) ->
end,
{error,St#compile{errors=St#compile.errors ++ Es}}
end;
- {error,_Error} ->
- Es = [{Tfile,[{none,compile,write_error}]}],
+ {error,Error} ->
+ Es = [{Tfile,[{none,compile,{write_error,Error}}]}],
{error,St#compile{errors=St#compile.errors ++ Es}}
end.
@@ -1628,8 +1634,8 @@ listing(LFun, Ext, St) ->
LFun(Lf, Code),
ok = file:close(Lf),
{ok,St};
- {error,_Error} ->
- Es = [{Lfile,[{none,compile,write_error}]}],
+ {error,Error} ->
+ Es = [{Lfile,[{none,compile,{write_error,Error}}]}],
{error,St#compile{errors=St#compile.errors ++ Es}}
end.
diff --git a/lib/compiler/test/beam_block_SUITE.erl b/lib/compiler/test/beam_block_SUITE.erl
index 4bcb252833..9fcb6e497d 100644
--- a/lib/compiler/test/beam_block_SUITE.erl
+++ b/lib/compiler/test/beam_block_SUITE.erl
@@ -21,7 +21,8 @@
-export([all/0,suite/0,groups/0,init_per_suite/1,end_per_suite/1,
init_per_group/2,end_per_group/2,
- get_map_elements/1,otp_7345/1,move_opt_across_gc_bif/1]).
+ get_map_elements/1,otp_7345/1,move_opt_across_gc_bif/1,
+ erl_202/1]).
%% The only test for the following functions is that
%% the code compiles and is accepted by beam_validator.
@@ -37,7 +38,8 @@ groups() ->
[{p,[parallel],
[get_map_elements,
otp_7345,
- move_opt_across_gc_bif
+ move_opt_across_gc_bif,
+ erl_202
]}].
init_per_suite(Config) ->
@@ -135,6 +137,27 @@ positive(speaking) ->
paris([], P) -> P + 1.
+
+%% See https://bugs.erlang.org/browse/ERL-202.
+%% Test that move_allocates/1 in beam_block doesn't move allocate
+%% when it would not be safe.
+
+-record(erl_202_r1, {y}).
+-record(erl_202_r2, {x}).
+
+erl_202(_Config) ->
+ Ref = make_ref(),
+ Ref = erl_202({{1,2},Ref}, 42),
+
+ {Ref} = erl_202({7,8}, #erl_202_r1{y=#erl_202_r2{x=Ref}}),
+
+ ok.
+
+erl_202({{_, _},X}, _) ->
+ X;
+erl_202({_, _}, #erl_202_r1{y=R2}) ->
+ {R2#erl_202_r2.x}.
+
%%%
%%% The only test of the following code is that it compiles.
%%%
diff --git a/lib/compiler/test/beam_jump_SUITE.erl b/lib/compiler/test/beam_jump_SUITE.erl
index 0b13adaff2..088f63606c 100644
--- a/lib/compiler/test/beam_jump_SUITE.erl
+++ b/lib/compiler/test/beam_jump_SUITE.erl
@@ -21,7 +21,7 @@
-export([all/0,suite/0,groups/0,init_per_suite/1,end_per_suite/1,
init_per_group/2,end_per_group/2,
- undefined_label/1]).
+ undefined_label/1,ambiguous_catch_try_state/1]).
suite() ->
[{ct_hooks,[ts_install_cth]}].
@@ -32,7 +32,8 @@ all() ->
groups() ->
[{p,[parallel],
- [undefined_label
+ [undefined_label,
+ ambiguous_catch_try_state
]}].
init_per_suite(Config) ->
@@ -57,3 +58,17 @@ flights(0, [], []) when [], 0; 0.0, [], false ->
clark;
flights(_, Reproduction, introduction) when false, Reproduction ->
responsible.
+
+%% [ERL-209] beam_jump would share 'catch' blocks, causing an
+%% ambiguous_catch_try_state error in beam_validator.
+
+ambiguous_catch_try_state(_Config) ->
+ {{'EXIT',{{case_clause,song},_}},{'EXIT',{{case_clause,song},_}}} =
+ checks(42),
+ ok.
+
+river() -> song.
+
+checks(Wanted) ->
+ %% Must be one line to cause the unsafe optimization.
+ {catch case river() of sheet -> begin +Wanted, if "da" -> Wanted end end end, catch case river() of sheet -> begin + Wanted, if "da" -> Wanted end end end}.
diff --git a/lib/compiler/test/match_SUITE.erl b/lib/compiler/test/match_SUITE.erl
index 31402ac717..127679ba69 100644
--- a/lib/compiler/test/match_SUITE.erl
+++ b/lib/compiler/test/match_SUITE.erl
@@ -24,7 +24,7 @@
pmatch/1,mixed/1,aliases/1,non_matching_aliases/1,
match_in_call/1,untuplify/1,shortcut_boolean/1,letify_guard/1,
selectify/1,underscore/1,match_map/1,map_vars_used/1,
- coverage/1,grab_bag/1]).
+ coverage/1,grab_bag/1,literal_binary/1]).
-include_lib("common_test/include/ct.hrl").
@@ -40,7 +40,7 @@ groups() ->
match_in_call,untuplify,
shortcut_boolean,letify_guard,selectify,
underscore,match_map,map_vars_used,coverage,
- grab_bag]}].
+ grab_bag,literal_binary]}].
init_per_suite(Config) ->
@@ -574,6 +574,15 @@ grab_bag_remove_failure([{stretch,_,Mi}=Stretch | Specs], Unit, _MaxFailure) ->
ok
end.
+%% Regression in 19.0, reported by Alexei Sholik
+literal_binary(_Config) ->
+ 3 = literal_binary_match(bar,<<"y">>),
+ ok.
+
+literal_binary_match(bar, <<"x">>) -> 1;
+literal_binary_match(_, <<"x">>) -> 2;
+literal_binary_match(_, <<"y">>) -> 3;
+literal_binary_match(_, _) -> fail.
id(I) -> I.
diff --git a/lib/compiler/vsn.mk b/lib/compiler/vsn.mk
index 23dd4bd4b1..334784657e 100644
--- a/lib/compiler/vsn.mk
+++ b/lib/compiler/vsn.mk
@@ -1 +1 @@
-COMPILER_VSN = 7.0
+COMPILER_VSN = 7.0.1
diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c
index 7183c395ae..d0044fe723 100644
--- a/lib/crypto/c_src/crypto.c
+++ b/lib/crypto/c_src/crypto.c
@@ -749,7 +749,7 @@ static ERL_NIF_TERM algo_hash[8]; /* increase when extending the list */
static int algo_pubkey_cnt;
static ERL_NIF_TERM algo_pubkey[7]; /* increase when extending the list */
static int algo_cipher_cnt;
-static ERL_NIF_TERM algo_cipher[20]; /* increase when extending the list */
+static ERL_NIF_TERM algo_cipher[21]; /* increase when extending the list */
static void init_algorithms_types(ErlNifEnv* env)
{
diff --git a/lib/debugger/doc/src/Makefile b/lib/debugger/doc/src/Makefile
index 6c9617ca69..0f724b6f17 100644
--- a/lib/debugger/doc/src/Makefile
+++ b/lib/debugger/doc/src/Makefile
@@ -114,7 +114,7 @@ release_docs_spec: docs
$(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
$(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
$(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- (/bin/cp -rf $(HTMLDIR) "$(RELSYSDIR)/doc")
+ ($(CP) -rf $(HTMLDIR) "$(RELSYSDIR)/doc")
$(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
$(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
$(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
diff --git a/lib/dialyzer/doc/src/notes.xml b/lib/dialyzer/doc/src/notes.xml
index a5a52fee61..6400072b1f 100644
--- a/lib/dialyzer/doc/src/notes.xml
+++ b/lib/dialyzer/doc/src/notes.xml
@@ -32,6 +32,20 @@
<p>This document describes the changes made to the Dialyzer
application.</p>
+<section><title>Dialyzer 3.0.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Fix a map related bug.</p>
+ <p>
+ Own Id: OTP-13709 Aux Id: ERL-177, PR-1115 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Dialyzer 3.0</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/dialyzer/src/dialyzer.erl b/lib/dialyzer/src/dialyzer.erl
index bcac8afe64..7f86520c06 100644
--- a/lib/dialyzer/src/dialyzer.erl
+++ b/lib/dialyzer/src/dialyzer.erl
@@ -282,7 +282,7 @@ cl_check_log(none) ->
cl_check_log(Output) ->
io:format(" Check output file `~s' for details\n", [Output]).
--spec format_warning(raw_warning()) -> string().
+-spec format_warning(raw_warning() | dial_warning()) -> string().
format_warning(W) ->
format_warning(W, basename).
diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl
index 9399789464..963c953447 100644
--- a/lib/dialyzer/src/dialyzer_dataflow.erl
+++ b/lib/dialyzer/src/dialyzer_dataflow.erl
@@ -978,12 +978,21 @@ handle_case(Tree, Map, State) ->
false -> State1
end,
Map2 = join_maps_begin(Map1),
- {MapList, State3, Type} =
+ {MapList, State3, Type, Warns} =
handle_clauses(Clauses, Arg, ArgType, ArgType, State2,
- [], Map2, [], []),
+ [], Map2, [], [], []),
+ %% Non-Erlang BEAM languages, such as Elixir, expand language constructs
+ %% into case statements. In that case, we do not want to warn on
+ %% individual clauses not matching unless none of them can.
+ SupressForced = is_compiler_generated(cerl:get_ann(Tree))
+ andalso not (t_is_none(Type)),
+ State4 = lists:foldl(fun({T,R,M,F}, S) ->
+ state__add_warning(
+ S,T,R,M,F andalso (not SupressForced))
+ end, State3, Warns),
Map3 = join_maps_end(MapList, Map2),
debug_pp_map(Map3),
- {State3, Map3, Type}
+ {State4, Map3, Type}
end.
%%----------------------------------------
@@ -1082,22 +1091,24 @@ handle_receive(Tree, Map, State) ->
RaceListSize + 1, State);
false -> State
end,
- {MapList, State2, ReceiveType} =
+ {MapList, State2, ReceiveType, Warns} =
handle_clauses(Clauses, ?no_arg, t_any(), t_any(), State1, [], Map,
- [], []),
+ [], [], []),
+ State3 = lists:foldl(fun({T,R,M,F}, S) -> state__add_warning(S,T,R,M,F) end,
+ State2, Warns),
Map1 = join_maps(MapList, Map),
- {State3, Map2, TimeoutType} = traverse(Timeout, Map1, State2),
- Opaques = State3#state.opaques,
+ {State4, Map2, TimeoutType} = traverse(Timeout, Map1, State3),
+ Opaques = State4#state.opaques,
case (t_is_atom(TimeoutType, Opaques) andalso
(t_atom_vals(TimeoutType, Opaques) =:= ['infinity'])) of
true ->
- {State3, Map2, ReceiveType};
+ {State4, Map2, ReceiveType};
false ->
Action = cerl:receive_action(Tree),
- {State4, Map3, ActionType} = traverse(Action, Map, State3),
+ {State5, Map3, ActionType} = traverse(Action, Map, State4),
Map4 = join_maps([Map3, Map1], Map),
Type = t_sup(ReceiveType, ActionType),
- {State4, Map4, Type}
+ {State5, Map4, Type}
end.
%%----------------------------------------
@@ -1245,7 +1256,7 @@ handle_tuple(Tree, Map, State) ->
%% Clauses
%%
handle_clauses([C|Left], Arg, ArgType, OrigArgType, State, CaseTypes, MapIn,
- Acc, ClauseAcc) ->
+ Acc, ClauseAcc, WarnAcc0) ->
IsRaceAnalysisEnabled = is_race_analysis_enabled(State),
State1 =
case IsRaceAnalysisEnabled of
@@ -1258,8 +1269,8 @@ handle_clauses([C|Left], Arg, ArgType, OrigArgType, State, CaseTypes, MapIn,
State);
false -> State
end,
- {State2, ClauseMap, BodyType, NewArgType} =
- do_clause(C, Arg, ArgType, OrigArgType, MapIn, State1),
+ {State2, ClauseMap, BodyType, NewArgType, WarnAcc} =
+ do_clause(C, Arg, ArgType, OrigArgType, MapIn, State1, WarnAcc0),
{NewClauseAcc, State3} =
case IsRaceAnalysisEnabled of
true ->
@@ -1277,9 +1288,9 @@ handle_clauses([C|Left], Arg, ArgType, OrigArgType, State, CaseTypes, MapIn,
false -> {[BodyType|CaseTypes], [ClauseMap|Acc]}
end,
handle_clauses(Left, Arg, NewArgType, OrigArgType, State3,
- NewCaseTypes, MapIn, NewAcc, NewClauseAcc);
+ NewCaseTypes, MapIn, NewAcc, NewClauseAcc, WarnAcc);
handle_clauses([], _Arg, _ArgType, _OrigArgType, State, CaseTypes, _MapIn, Acc,
- ClauseAcc) ->
+ ClauseAcc, WarnAcc) ->
State1 =
case is_race_analysis_enabled(State) of
true ->
@@ -1289,9 +1300,9 @@ handle_clauses([], _Arg, _ArgType, _OrigArgType, State, CaseTypes, _MapIn, Acc,
RaceListSize + 1, State);
false -> State
end,
- {lists:reverse(Acc), State1, t_sup(CaseTypes)}.
+ {lists:reverse(Acc), State1, t_sup(CaseTypes), WarnAcc}.
-do_clause(C, Arg, ArgType0, OrigArgType, Map, State) ->
+do_clause(C, Arg, ArgType0, OrigArgType, Map, State, Warns) ->
Pats = cerl:clause_pats(C),
Guard = cerl:clause_guard(C),
Body = cerl:clause_body(C),
@@ -1323,7 +1334,7 @@ do_clause(C, Arg, ArgType0, OrigArgType, Map, State) ->
[cerl_prettypr:format(C), format_type(ArgType0, State1)]),
case state__warning_mode(State1) of
false ->
- {State1, Map, t_none(), ArgType0};
+ {State1, Map, t_none(), ArgType0, Warns};
true ->
{Msg, Force} =
case t_is_none(ArgType0) of
@@ -1403,8 +1414,7 @@ do_clause(C, Arg, ArgType0, OrigArgType, Map, State) ->
{record_match, _} -> ?WARN_MATCHING;
{pattern_match_cov, _} -> ?WARN_MATCHING
end,
- {state__add_warning(State1, WarnType, C, Msg, Force),
- Map, t_none(), ArgType0}
+ {State1, Map, t_none(), ArgType0, [{WarnType, C, Msg, Force}|Warns]}
end;
{Map2, PatTypes} ->
Map3 =
@@ -1437,9 +1447,9 @@ do_clause(C, Arg, ArgType0, OrigArgType, Map, State) ->
false ->
{guard_fail_pat, [PatString, format_type(ArgType0, State1)]}
end,
- State2 =
+ Warn =
case Reason of
- none -> state__add_warning(State1, ?WARN_MATCHING, C, DefaultMsg);
+ none -> {?WARN_MATCHING, C, DefaultMsg, false};
{FailGuard, Msg} ->
case is_compiler_generated(cerl:get_ann(FailGuard)) of
false ->
@@ -1448,15 +1458,15 @@ do_clause(C, Arg, ArgType0, OrigArgType, Map, State) ->
{neg_guard_fail, _} -> ?WARN_MATCHING;
{opaque_guard, _} -> ?WARN_OPAQUE
end,
- state__add_warning(State1, WarnType, FailGuard, Msg);
+ {WarnType, FailGuard, Msg, false};
true ->
- state__add_warning(State1, ?WARN_MATCHING, C, Msg)
+ {?WARN_MATCHING, C, Msg, false}
end
end,
- {State2, Map, t_none(), NewArgType};
+ {State1, Map, t_none(), NewArgType, [Warn|Warns]};
Map4 ->
{RetState, RetMap, BodyType} = traverse(Body, Map4, State1),
- {RetState, RetMap, BodyType, NewArgType}
+ {RetState, RetMap, BodyType, NewArgType, Warns}
end
end.
diff --git a/lib/dialyzer/test/Makefile b/lib/dialyzer/test/Makefile
index f43e04dd59..0d8fba438c 100644
--- a/lib/dialyzer/test/Makefile
+++ b/lib/dialyzer/test/Makefile
@@ -12,6 +12,7 @@ AUXILIARY_FILES=\
dialyzer_common.erl\
file_utils.erl\
dialyzer_SUITE.erl\
+ abstract_SUITE.erl\
plt_SUITE.erl
# ----------------------------------------------------
diff --git a/lib/dialyzer/test/abstract_SUITE.erl b/lib/dialyzer/test/abstract_SUITE.erl
new file mode 100644
index 0000000000..269db3e836
--- /dev/null
+++ b/lib/dialyzer/test/abstract_SUITE.erl
@@ -0,0 +1,102 @@
+%% This suite contains cases that cannot be written
+%% in Erlang itself and must be done via the abstract
+%% format.
+
+-module(abstract_SUITE).
+
+-include_lib("common_test/include/ct.hrl").
+-include("dialyzer_test_constants.hrl").
+
+-export([suite/0, all/0, init_per_suite/0, init_per_suite/1]).
+-export([generated_case/1]).
+
+suite() ->
+ [{timetrap, {minutes, 1}}].
+all() ->
+ [generated_case].
+
+init_per_suite() ->
+ [{timetrap, ?plt_timeout}].
+init_per_suite(Config) ->
+ OutDir = ?config(priv_dir, Config),
+ case dialyzer_common:check_plt(OutDir) of
+ fail -> {skip, "Plt creation/check failed."};
+ ok -> [{dialyzer_options, []}|Config]
+ end.
+
+generated_case(Config) when is_list(Config) ->
+ %% Equivalent to:
+ %%
+ %% -module(foo).
+ %% -export(bar).
+ %% bar() ->
+ %% Arg = sample,
+ %% case Arg of
+ %% #{} -> map;
+ %% _ -> Arg:fn()
+ %% end.
+ %%
+ %% Except the case statement and its clauses are marked as autogenerated.
+ [] =
+ test([{attribute,1,module,foo},
+ {attribute,2,export,[{bar,0}]},
+ {function,3,bar,0,
+ [{clause,3,[],[],
+ [{match,4,{var,4,'Arg'},{atom,4,sample}},
+ {'case',[{location,5},{generated,true}],{var,5,'Arg'},
+ [{clause,[{location,6},{generated,true}],[{map,6,[]}],[],
+ [{atom,6,map}]},
+ {clause,[{location,7},{generated,true}],[{var,7,'_'}],[],
+ [{call,7,{remote,7,{var,7,'Arg'},{atom,7,fn}},[]}]}]}]}]}],
+ Config, [], []),
+ %% With the first clause not auto-generated
+ [{warn_matching,{_,6},_}] =
+ test([{attribute,1,module,foo},
+ {attribute,2,export,[{bar,0}]},
+ {function,3,bar,0,
+ [{clause,3,[],[],
+ [{match,4,{var,4,'Arg'},{atom,4,sample}},
+ {'case',[{location,5},{generated,true}],{var,5,'Arg'},
+ [{clause,6,[{map,6,[]}],[],
+ [{atom,6,map}]},
+ {clause,[{location,7},{generated,true}],[{var,7,'_'}],[],
+ [{call,7,{remote,7,{var,7,'Arg'},{atom,7,fn}},[]}]}]}]}]}],
+ Config, [], []),
+ %% With Arg set to [] so neither clause matches
+ [{warn_return_no_exit,{_,3},_},
+ {warn_matching,{_,6},_},
+ {warn_failing_call,{_,7},_}] =
+ test([{attribute,1,module,foo},
+ {attribute,2,export,[{bar,0}]},
+ {function,3,bar,0,
+ [{clause,3,[],[],
+ [{match,4,{var,4,'Arg'},{nil,4}},
+ {'case',[{location,5},{generated,true}],{var,5,'Arg'},
+ [{clause,[{location,6},{generated,true}],[{map,6,[]}],[],
+ [{atom,6,map}]},
+ {clause,[{location,7},{generated,true}],[{var,7,'_'}],[],
+ [{call,7,{remote,7,{var,7,'Arg'},{atom,7,fn}},[]}]}]}]}]}],
+ Config, [], []),
+ ok.
+
+test(Prog, Config, COpts, DOpts) ->
+ {ok, BeamFile} = compile(Config, Prog, COpts),
+ run_dialyzer(Config, succ_typings, [BeamFile], DOpts).
+
+compile(Config, Prog, CompileOpts) ->
+ OutDir = ?config(priv_dir, Config),
+ Opts = [{outdir, OutDir}, debug_info, return_errors | CompileOpts],
+ {ok, Module, Source} = compile:forms(Prog, Opts),
+ BeamFile = filename:join([OutDir, lists:concat([Module, ".beam"])]),
+ ok = file:write_file(BeamFile, Source),
+ {ok, BeamFile}.
+
+run_dialyzer(Config, Analysis, Files, Opts) ->
+ OutDir = ?config(priv_dir, Config),
+ PltFilename = dialyzer_common:plt_file(OutDir),
+ dialyzer:run([{analysis_type, Analysis},
+ {files, Files},
+ {init_plt, PltFilename},
+ {check_plt, false},
+ {from, byte_code} |
+ Opts]).
diff --git a/lib/dialyzer/test/dialyzer_common.erl b/lib/dialyzer/test/dialyzer_common.erl
index d2b1026c06..48083a2731 100644
--- a/lib/dialyzer/test/dialyzer_common.erl
+++ b/lib/dialyzer/test/dialyzer_common.erl
@@ -7,7 +7,7 @@
-module(dialyzer_common).
--export([check_plt/1, check/4, create_all_suites/0, new_tests/2]).
+-export([check_plt/1, check/4, create_all_suites/0, new_tests/2, plt_file/1]).
-include_lib("kernel/include/file.hrl").
@@ -39,7 +39,7 @@
check_plt(OutDir) ->
io:format("Checking plt:"),
- PltFilename = filename:join(OutDir, ?plt_filename),
+ PltFilename = plt_file(OutDir),
case file:read_file_info(PltFilename) of
{ok, _} -> dialyzer_check_plt(PltFilename);
{error, _ } ->
@@ -63,6 +63,11 @@ check_plt(OutDir) ->
end
end.
+-spec plt_file(string()) -> string().
+
+plt_file(OutDir) ->
+ filename:join(OutDir, ?plt_filename).
+
dialyzer_check_plt(PltFilename) ->
try dialyzer:run([{analysis_type, plt_check},
{init_plt, PltFilename}]) of
@@ -119,7 +124,7 @@ build_plt(PltFilename) ->
'same' | {differ, [term()]}.
check(TestCase, Opts, Dir, OutDir) ->
- PltFilename = filename:join(OutDir, ?plt_filename),
+ PltFilename = plt_file(OutDir),
SrcDir = filename:join(Dir, ?input_files_directory),
ResDir = filename:join(Dir, ?result_files_directory),
Filename = filename:join(SrcDir, atom_to_list(TestCase)),
diff --git a/lib/dialyzer/test/map_SUITE_data/src/mand_remote_val/a.erl b/lib/dialyzer/test/map_SUITE_data/src/mand_remote_val/a.erl
new file mode 100644
index 0000000000..827984b20b
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/src/mand_remote_val/a.erl
@@ -0,0 +1,9 @@
+-module(a).
+-export([to_map/1, to_map/2]).
+-type t() :: #{type := b:t()}.
+
+-spec to_map(t()) -> map().
+to_map(Resource) -> to_map(Resource, #{}).
+
+-spec to_map(t(), map()) -> map().
+to_map(_, Map) when is_map(Map) -> #{}.
diff --git a/lib/dialyzer/test/map_SUITE_data/src/mand_remote_val/b.erl b/lib/dialyzer/test/map_SUITE_data/src/mand_remote_val/b.erl
new file mode 100644
index 0000000000..31f9bb6b3e
--- /dev/null
+++ b/lib/dialyzer/test/map_SUITE_data/src/mand_remote_val/b.erl
@@ -0,0 +1,3 @@
+-module(b).
+-export_type([t/0]).
+-type t() :: binary().
diff --git a/lib/dialyzer/vsn.mk b/lib/dialyzer/vsn.mk
index 077fe01e85..b9a28afdd9 100644
--- a/lib/dialyzer/vsn.mk
+++ b/lib/dialyzer/vsn.mk
@@ -1 +1 @@
-DIALYZER_VSN = 3.0
+DIALYZER_VSN = 3.0.1
diff --git a/lib/diameter/src/Makefile b/lib/diameter/src/Makefile
index 7f61620fc1..6bf748a727 100644
--- a/lib/diameter/src/Makefile
+++ b/lib/diameter/src/Makefile
@@ -123,7 +123,7 @@ ERL_COMPILE_FLAGS += \
# erl/hrl from dictionary file.
gen/diameter_gen_%.erl gen/diameter_gen_%.hrl: dict/%.dia
$(dia_verbose) \
- ../bin/diameterc -o gen -i $(EBIN) $<
+ escript ../bin/diameterc -o gen -i $(EBIN) $<
opt: $(TARGET_FILES)
diff --git a/lib/edoc/src/edoc_extract.erl b/lib/edoc/src/edoc_extract.erl
index e7a4c36ca4..2d6cb04a6d 100644
--- a/lib/edoc/src/edoc_extract.erl
+++ b/lib/edoc/src/edoc_extract.erl
@@ -32,9 +32,9 @@
%% %% @headerfile "edoc.hrl" (disabled until it can be made private)
-include("edoc.hrl").
-%% @type filename() = file:filename().
-%% @type proplist() = proplists:property().
-%% @type syntaxTree() = erl_syntax:syntaxTree().
+%% @type filename() = //kernel/file:filename().
+%% @type proplist() = //stdlib/proplists:property().
+%% @type syntaxTree() = //syntax_tools/erl_syntax:syntaxTree().
%% @spec source(File::filename(), Env::edoc_env(), Options::proplist())
%% -> {ModuleName, edoc:edoc_module()}
@@ -639,11 +639,11 @@ file_macros(_Context, Env) ->
%%
%% The idea is to mimic how the @type tag works.
%% Using @type:
-%% @type t() = t1(). Some docs of t/0;
-%% Further docs of t/0.
+%%```@type t() = t1(). Some docs of t/0;
+%% Further docs of t/0.'''
%% The same thing using -type:
-%% -type t() :: t1(). % Some docs of t/0;
-%% Further docs of t/0.
+%%```-type t() :: t1(). % Some docs of t/0;
+%% Further docs of t/0.'''
find_type_docs(Forms0, Comments, Env, File) ->
Tree = erl_recomment:recomment_forms(Forms0, Comments),
Forms = preprocess_forms(Tree),
diff --git a/lib/edoc/src/edoc_lib.erl b/lib/edoc/src/edoc_lib.erl
index dcc239f6b4..cc0a8d0b94 100644
--- a/lib/edoc/src/edoc_lib.erl
+++ b/lib/edoc/src/edoc_lib.erl
@@ -935,7 +935,7 @@ get_doc_env(Opts) ->
%% Modules = [atom()]
%% proplist() = [term()]
%%
-%% @type proplist() = proplists:property().
+%% @type proplist() = //stdlib/proplists:property().
%% @type edoc_env(). Environment information needed by EDoc for
%% generating references. The data representation is not documented.
%%
diff --git a/lib/edoc/src/edoc_tags.erl b/lib/edoc/src/edoc_tags.erl
index 9e2e41e902..93f423b906 100644
--- a/lib/edoc/src/edoc_tags.erl
+++ b/lib/edoc/src/edoc_tags.erl
@@ -213,8 +213,10 @@ filter_tags([#tag{name = N, line = L} = T | Ts], Tags, Where, Ts1) ->
true ->
filter_tags(Ts, Tags, Where, [T | Ts1]);
false ->
- [warning(L, Where, "tag @~s not recognized.", [N]) ||
- Where =/= no],
+ case Where of
+ no -> ok;
+ _ -> warning(L, Where, "tag @~s not recognized.", [N])
+ end,
filter_tags(Ts, Tags, Where, Ts1)
end;
filter_tags([], _, _, Ts) ->
@@ -451,7 +453,7 @@ check_type(#tag{line = L, data = Data}, P0, Ls, Ts) ->
check_type(#t_def{type = Type}, P, Ls, Ts) ->
check_type(Type, P, Ls, Ts);
check_type(#t_type{name = Name, args = Args}, P, Ls, Ts) ->
- _ = check_used_type(Name, Args, P, Ls),
+ check_used_type(Name, Args, P, Ls),
check_types3(Args++Ts, P, Ls);
check_type(#t_var{}, P, Ls, Ts) ->
check_types3(Ts, P, Ls);
@@ -503,7 +505,8 @@ check_used_type(#t_name{name = N, module = Mod}=Name, Args, P, LocalTypes) ->
false ->
#parms{warn = W, line = L, file = File} = P,
%% true = ets:insert(DT, TypeName),
- [type_warning(L, File, "missing type", N, NArgs) || W]
+ _ = [type_warning(L, File, "missing type", N, NArgs) || W],
+ ok
end.
type_warning(Line, File, S, N, NArgs) ->
diff --git a/lib/erl_docgen/priv/bin/xref_mod_app.escript b/lib/erl_docgen/priv/bin/xref_mod_app.escript
index ac4278bf22..4a418fe144 100755
--- a/lib/erl_docgen/priv/bin/xref_mod_app.escript
+++ b/lib/erl_docgen/priv/bin/xref_mod_app.escript
@@ -84,7 +84,7 @@ preloaded(TopDir) ->
%% It's OK if too much data is generated as long as all applications
%% and all modules are mentioned.
appmods(D) ->
- ErlFiles = filelib:wildcard(filename:join([D,"src","*.erl"])),
+ ErlFiles = filelib:wildcard(filename:join([D,"src","**","*.erl"])),
AppV = filename:basename(D),
App = case string:rstr(AppV, "-") of
0 -> AppV;
diff --git a/lib/erl_docgen/priv/dtd/erlref.dtd b/lib/erl_docgen/priv/dtd/erlref.dtd
index 835407520a..615b88b61a 100644
--- a/lib/erl_docgen/priv/dtd/erlref.dtd
+++ b/lib/erl_docgen/priv/dtd/erlref.dtd
@@ -29,7 +29,7 @@
<!-- `name' is used in common.refs.dtd and must therefore
be defined in each *ref. dtd -->
-<!ELEMENT name (#PCDATA) >
+<!ELEMENT name (#PCDATA|seealso)* >
<!ATTLIST name name CDATA #IMPLIED
arity CDATA #IMPLIED
clause_i CDATA #IMPLIED
diff --git a/lib/erl_docgen/priv/xsl/db_html.xsl b/lib/erl_docgen/priv/xsl/db_html.xsl
index fe6c7d3c28..edab8e1c7e 100644
--- a/lib/erl_docgen/priv/xsl/db_html.xsl
+++ b/lib/erl_docgen/priv/xsl/db_html.xsl
@@ -2007,10 +2007,10 @@
</xsl:variable>
<xsl:choose>
<xsl:when test="ancestor::datatype">
- <a name="type-{$fname}"><span class="bold_code"><xsl:value-of select="."/></span></a><br/>
+ <a name="type-{$fname}"></a><span class="bold_code"><xsl:apply-templates/></span><br/>
</xsl:when>
<xsl:otherwise>
- <a name="{$fname}-{$arity}"><span class="bold_code"><xsl:value-of select="."/></span></a><br/>
+ <a name="{$fname}-{$arity}"></a><span class="bold_code"><xsl:apply-templates/></span><br/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
diff --git a/lib/erl_docgen/src/docgen_edoc_xml_cb.erl b/lib/erl_docgen/src/docgen_edoc_xml_cb.erl
index 0ac7985a48..4f3af1f767 100644
--- a/lib/erl_docgen/src/docgen_edoc_xml_cb.erl
+++ b/lib/erl_docgen/src/docgen_edoc_xml_cb.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -563,7 +563,14 @@ otp_xmlify_a_fileref(FileRef1, AppS) ->
true ->
case split(Marker0, "-") of
[Func,Arity] ->
- Func++"/"++Arity;
+ try list_to_integer(Arity) of
+ _ ->
+ Func++"/"++Arity
+ catch
+ _:_ ->
+ %% This is "type-"<a-type>.
+ Marker0
+ end;
_ ->
Marker0
end
@@ -831,19 +838,49 @@ local_types([]) -> [];
local_types(Es) ->
local_defs2(get_elem(localdef, Es)).
+-define(LOCAL_TYPES, edoc_local_defs).
+
local_defs2([]) -> [];
local_defs2(Es) ->
+ case collect_local_types(Es) of
+ [] -> local_defs3(Es);
+ LocalTypes ->
+ ?LOCAL_TYPES = ets:new(?LOCAL_TYPES, [named_table]),
+ true = ets:insert(?LOCAL_TYPES, LocalTypes),
+ try
+ local_defs3(Es)
+ after
+ ets:delete(?LOCAL_TYPES)
+ end
+ end.
+
+local_defs3(Es) ->
{type,[?NL | [{v, localdef2(E)} || E <- Es]]}.
+%% Does not work well for parametrized types.
+collect_local_types(Es) ->
+ lists:append([collect_local_type(E) || E <- Es]).
+
+collect_local_type(#xmlElement{content = Es}) ->
+ case get_elem(typevar, Es) of
+ [] ->
+ [{t_abstype(get_content(abstype, Es))}];
+ [_] ->
+ []
+ end.
+
%% Like localdef/1, but does not use label_anchor/2 -- we don't want any
%% markers or em tags in <v> tag, plain text only!
+%% When used stand-alone, EDoc generates links to local types. An ETS
+%% table holds local types, to avoid generating links to them.
localdef2(#xmlElement{content = Es}) ->
- case get_elem(typevar, Es) of
- [] ->
- t_utype(get_elem(type, Es));
- [V] ->
- t_var(V) ++ [" = "] ++ t_utype(get_elem(type, Es))
- end.
+ Var = case get_elem(typevar, Es) of
+ [] ->
+ [t_abstype(get_content(abstype, Es))];
+ [V] ->
+ t_var(V)
+ end,
+ Var ++ [" = "] ++ t_utype(get_elem(type, Es)).
type_name(#xmlElement{content = Es}) ->
t_name(get_elem(erlangName, get_content(typedef, Es))).
@@ -855,10 +892,9 @@ types(Ts) ->
typedecl(Name, #xmlElement{content = Es}) ->
TypedefEs = get_content(typedef, Es),
Id = "type-"++Name,
- [{tag, typedef(TypedefEs)},
+ [{tag, [{marker,[{id,Id}],[]}] ++ typedef(TypedefEs)},
?NL,
- {item, [{marker,[{id,Id}],[]} |
- local_defs(get_elem(localdef, TypedefEs)) ++ fulldesc(Es)]},
+ {item, local_defs(get_elem(localdef, TypedefEs)) ++ fulldesc(Es)},
?NL].
typedef(Es) ->
@@ -866,14 +902,14 @@ typedef(Es) ->
++ seq(fun t_utype_elem/1, get_content(argtypes, Es), [")"])),
case get_elem(type, Es) of
[] ->
- [{tt, Name}];
+ Name;
Type ->
- [{tt, Name ++ [" = "] ++ t_utype(Type)}]
+ Name ++ [" = "] ++ t_utype(Type)
end.
-local_defs([]) -> [];
+local_defs([]) -> [{p,[]}];
local_defs(Es) ->
- [?NL, {ul, [{li, [{tt, localdef(E)}]} || E <- Es]}].
+ [?NL, {ul, [{li, [{p, localdef(E)}]} || E <- Es]}].
localdef(E = #xmlElement{content = Es}) ->
Var = case get_elem(typevar, Es) of
@@ -917,6 +953,7 @@ seealso_module(Es) ->
Es1 ->
{section,[{title,["See also"]},{p,seq(fun see/1, Es1, [])}]}
end.
+
seealso_function(Es) ->
case get_elem(see, Es) of
[] -> [];
@@ -988,7 +1025,14 @@ t_name([E]) ->
end.
t_utype([E]) ->
- t_utype_elem(E).
+ flatten_type(t_utype_elem(E)).
+
+%% Make sure see also are top elements of lists.
+flatten_type(T) ->
+ [case is_integer(E) of
+ true -> [E];
+ false -> E
+ end || E <- lists:flatten(T)].
t_utype_elem(E=#xmlElement{content = Es}) ->
case get_attrval(name, E) of
@@ -1021,16 +1065,14 @@ t_type([#xmlElement{name = tuple, content = Es}]) ->
t_tuple(Es);
t_type([#xmlElement{name = 'fun', content = Es}]) ->
t_fun(Es);
-t_type([#xmlElement{name = abstype, content = Es}]) ->
- t_abstype(Es);
+t_type([E = #xmlElement{name = abstype, content = Es}]) ->
+ t_abstype(E, Es);
t_type([#xmlElement{name = union, content = Es}]) ->
t_union(Es);
t_type([#xmlElement{name = record, content = Es}]) ->
t_record(Es);
t_type([#xmlElement{name = map, content = Es}]) ->
- t_map(Es);
-t_type([#xmlElement{name = map_field, content = Es}]) ->
- t_map_field(Es).
+ t_map(Es).
t_var(E) ->
[get_attrval(name, E)].
@@ -1065,35 +1107,53 @@ t_fun(Es) ->
t_record([E|Es]) ->
["#", get_attrval(value, E), "{"++ seq(fun t_field/1, Es) ++"}"].
+
t_field(#xmlElement{name=field, content=[Atom,Type]}) ->
[get_attrval(value, Atom), "="] ++ t_utype_elem(Type).
t_map(Es) ->
- ["#{"] ++ seq(fun t_utype_elem/1, Es, ["}"]).
+ ["#{"] ++ seq(fun t_map_field/1, Es, ["}"]).
+
+t_map_field(E = #xmlElement{name = map_field, content = [K,V]}) ->
+ KElem = t_utype_elem(K),
+ VElem = t_utype_elem(V),
+ AS = case get_attrval(assoc_type, E) of
+ "assoc" -> " => ";
+ "exact" -> " := "
+ end,
+ [KElem ++ AS ++ VElem].
-t_map_field([K,V]) ->
- [t_utype_elem(K) ++ " => " ++ t_utype_elem(V)].
+t_abstype(E, Es) ->
+ see_type(E, t_abstype(Es)).
t_abstype(Es) ->
- case split_at_colon(t_name(get_elem(erlangName, Es)),[]) of
- {Mod,Type} ->
- [Type, "("] ++
- seq(fun t_utype_elem/1, get_elem(type, Es), [")"]) ++
- [" (see module ", Mod, ")"];
- Type ->
- [Type, "("] ++
- seq(fun t_utype_elem/1, get_elem(type, Es), [")"])
+ Name = t_name(get_elem(erlangName, Es)),
+ [Name, "("] ++ seq(fun t_utype_elem/1, get_elem(type, Es), [")"]).
+
+see_type(E, Es0) ->
+ case get_attrval(href, E) of
+ [] -> Es0;
+ Href0 ->
+ try
+ false = is_local_type(Es0),
+ %% Fails for parametrized types:
+ Text = #xmlText{value = lists:append(Es0)},
+ {Href, Es} = otp_xmlify_a_href(Href0, [Text]),
+ [{seealso, [{marker, Href}], Es}]
+ catch
+ _:_ ->
+ Es0
+ end
end.
-%% Split at one colon, but not at two (or more)
-split_at_colon([$:,$:|_]=Rest,Acc) ->
- lists:reverse(Acc)++Rest;
-split_at_colon([$:|Type],Acc) ->
- {lists:reverse(Acc),Type};
-split_at_colon([Char|Rest],Acc) ->
- split_at_colon(Rest,[Char|Acc]);
-split_at_colon([],Acc) ->
- lists:reverse(Acc).
+is_local_type(Es) ->
+ try
+ [_] = ets:lookup(?LOCAL_TYPES, Es),
+ true
+ catch
+ _:_->
+ false
+ end.
t_union(Es) ->
seq(fun t_utype_elem/1, Es, " | ", []).
diff --git a/lib/gs/doc/src/Makefile b/lib/gs/doc/src/Makefile
index b270bc84fe..a24ec77e31 100644
--- a/lib/gs/doc/src/Makefile
+++ b/lib/gs/doc/src/Makefile
@@ -148,7 +148,7 @@ release_docs_spec: docs
$(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
$(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
$(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- (/bin/cp -rf $(HTMLDIR) "$(RELSYSDIR)/doc")
+ ($(CP) -rf $(HTMLDIR) "$(RELSYSDIR)/doc")
$(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
$(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
$(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl
index 7826dada9d..243bb6e25d 100644
--- a/lib/hipe/cerl/erl_types.erl
+++ b/lib/hipe/cerl/erl_types.erl
@@ -1664,10 +1664,12 @@ t_map(Pairs0, DefK0, DefV0) ->
%% define(DEBUG, true).
try
validate_map_elements(Pairs)
- catch error:badarg -> error(badarg, [Pairs0,DefK0,DefV0]);
- error:{badarg, E} -> error({badarg, E}, [Pairs0,DefK0,DefV0])
+ catch error:badarg -> error(badarg, [Pairs0,DefK0,DefV0])
end,
- ?map(Pairs, DefK, DefV).
+ case map_pairs_are_none(Pairs) of
+ true -> ?none;
+ false -> ?map(Pairs, DefK, DefV)
+ end.
normalise_map_optionals([], _, _) -> [];
normalise_map_optionals([E={K,?opt,?none}|T], DefK, DefV) ->
@@ -1684,7 +1686,6 @@ normalise_map_optionals([E={K,?opt,V}|T], DefK, DefV) ->
normalise_map_optionals([E|T], DefK, DefV) ->
[E|normalise_map_optionals(T, DefK, DefV)].
-validate_map_elements([{_,?mand,?none}|_]) -> error({badarg, none_in_mand});
validate_map_elements([{K1,_,_}|Rest=[{K2,_,_}|_]]) ->
case is_singleton_type(K1) andalso K1 < K2 of
false -> error(badarg);
@@ -1697,6 +1698,10 @@ validate_map_elements([{K,_,_}]) ->
end;
validate_map_elements([]) -> true.
+map_pairs_are_none([]) -> false;
+map_pairs_are_none([{_,?mand,?none}|_]) -> true;
+map_pairs_are_none([_|Ps]) -> map_pairs_are_none(Ps).
+
-spec t_is_map(erl_type()) -> boolean().
t_is_map(Type) ->
@@ -2833,12 +2838,7 @@ t_inf(?map(_, ADefK, ADefV) = A, ?map(_, BDefK, BDefV) = B, _Opaques) ->
%% becomes mandatory in the infinumum
(K, _, V1, _, V2) -> {K, ?mand, t_inf(V1, V2)}
end, A, B),
- %% If the infinimum of any mandatory values is ?none, the entire map infinimum
- %% is ?none.
- case lists:any(fun({_,?mand,?none})->true; ({_,_,_}) -> false end, Pairs) of
- true -> t_none();
- false -> t_map(Pairs, t_inf(ADefK, BDefK), t_inf(ADefV, BDefV))
- end;
+ t_map(Pairs, t_inf(ADefK, BDefK), t_inf(ADefV, BDefV));
t_inf(?matchstate(Pres1, Slots1), ?matchstate(Pres2, Slots2), _Opaques) ->
?matchstate(t_inf(Pres1, Pres2), t_inf(Slots1, Slots2));
t_inf(?nil, ?nil, _Opaques) -> ?nil;
diff --git a/lib/ic/doc/src/Makefile b/lib/ic/doc/src/Makefile
index c9691df7af..19f12ac6b9 100644
--- a/lib/ic/doc/src/Makefile
+++ b/lib/ic/doc/src/Makefile
@@ -225,7 +225,7 @@ release_docs_spec: docs
$(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
$(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
$(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- (/bin/cp -rf $(HTMLDIR) "$(RELSYSDIR)/doc")
+ ($(CP) -rf $(HTMLDIR) "$(RELSYSDIR)/doc")
$(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
$(INSTALL_DATA) $(MAN3_FILES) "$(RELEASE_PATH)/man/man3"
diff --git a/lib/inets/doc/src/mod_esi.xml b/lib/inets/doc/src/mod_esi.xml
index 8279fdc824..006fca1bdf 100644
--- a/lib/inets/doc/src/mod_esi.xml
+++ b/lib/inets/doc/src/mod_esi.xml
@@ -61,7 +61,7 @@
<tag><c>{server_port, integer()}</c></tag>
<item><p>Servers port number.</p></item>
- <tag><c>{request_method, "GET | "PUT" | "DELETE | "POST" | "PATCH"}</c></tag>
+ <tag><c>{request_method, "GET | "PUT" | "DELETE" | "POST" | "PATCH"}</c></tag>
<item><p>HTTP request method.</p></item>
<tag><c>{remote_adress, inet:ip_address()} </c></tag>
diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml
index 5321203511..caa5a083a3 100644
--- a/lib/inets/doc/src/notes.xml
+++ b/lib/inets/doc/src/notes.xml
@@ -33,7 +33,38 @@
<file>notes.xml</file>
</header>
- <section><title>Inets 6.3</title>
+ <section><title>Inets 6.3.2</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ PUT and DELETE support has been added to mod_esi</p>
+ <p>
+ Own Id: OTP-13688 Aux Id: seq13149 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 6.3.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ A debug message was accidently left enabled in the ftp
+ client.</p>
+ <p>
+ Own Id: OTP-13712 Aux Id: seq13143 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 6.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/inets/src/ftp/ftp.erl b/lib/inets/src/ftp/ftp.erl
index bbf25f8e90..8bad91bf98 100644
--- a/lib/inets/src/ftp/ftp.erl
+++ b/lib/inets/src/ftp/ftp.erl
@@ -106,8 +106,8 @@
-type common_reason() :: 'econn' | 'eclosed' | term().
-type file_write_error_reason() :: term(). % See file:write for more info
-%%-define(DBG(F,A), 'n/a').
--define(DBG(F,A), io:format(F,A)).
+-define(DBG(F,A), 'n/a').
+%%-define(DBG(F,A), io:format(F,A)).
%%%=========================================================================
%%% API - CLIENT FUNCTIONS
@@ -2099,7 +2099,7 @@ handle_ctrl_result({pos_prel, _}, #state{caller = {transfer_data, Bin}}
%%--------------------------------------------------------------------------
%% Default
-handle_ctrl_result({Status, Lines}, #state{client = From} = State)
+handle_ctrl_result({Status, _Lines}, #state{client = From} = State)
when From =/= undefined ->
ctrl_result_response(Status, State, {error, Status}).
diff --git a/lib/inets/src/http_server/httpd_conf.erl b/lib/inets/src/http_server/httpd_conf.erl
index e5182ca23c..9e54f2b2c5 100644
--- a/lib/inets/src/http_server/httpd_conf.erl
+++ b/lib/inets/src/http_server/httpd_conf.erl
@@ -395,7 +395,8 @@ validate_properties(Properties) ->
%% That is, if property A depends on property B.
%% The only sunch preperty at this time is bind_address that depends
%% on ipfamily.
-validate_properties2(Properties) ->
+validate_properties2(Properties0) ->
+ Properties = fix_ipfamily(Properties0),
case proplists:get_value(bind_address, Properties) of
undefined ->
case proplists:get_value(sock_type, Properties, ip_comm) of
@@ -422,6 +423,15 @@ validate_properties2(Properties) ->
end
end.
+fix_ipfamily(Properties) ->
+ case proplists:get_value(ipfamily, Properties) of
+ undefined ->
+ Properties;
+ IpFamily ->
+ NewProps = proplists:delete(ipfamily, Properties),
+ [{ipfamily, validate_ipfamily(IpFamily)} | NewProps]
+ end.
+
add_inet_defaults(Properties) ->
case proplists:get_value(ipfamily, Properties) of
undefined ->
diff --git a/lib/inets/src/http_server/httpd_example.erl b/lib/inets/src/http_server/httpd_example.erl
index 424d269859..c893b10dca 100644
--- a/lib/inets/src/http_server/httpd_example.erl
+++ b/lib/inets/src/http_server/httpd_example.erl
@@ -20,7 +20,7 @@
%%
-module(httpd_example).
-export([print/1]).
--export([get/2, post/2, yahoo/2, test1/2, get_bin/2, peer/2]).
+-export([get/2, put/2, post/2, yahoo/2, test1/2, get_bin/2, peer/2]).
-export([newformat/3]).
%% These are used by the inets test-suite
@@ -59,6 +59,11 @@ get(_Env,[]) ->
get(Env,Input) ->
default(Env,Input).
+put(Env,{Input,_Body}) ->
+ default(Env,Input);
+put(Env,Input) ->
+ default(Env,Input).
+
get_bin(_Env,_Input) ->
[list_to_binary(header()),
list_to_binary(top("GET Example")),
@@ -94,7 +99,7 @@ default(Env,Input) ->
io_lib:format("~p",[httpd:parse_query(Input)]),"\n",
footer()].
-peer(Env, Input) ->
+peer(Env, _Input) ->
Header =
case proplists:get_value(peer_cert, Env) of
undefined ->
@@ -161,7 +166,7 @@ sleep(T) -> receive after T -> ok end.
%% ------------------------------------------------------
-chunk_timeout(SessionID, _, StrInt) ->
+chunk_timeout(SessionID, _, _StrInt) ->
mod_esi:deliver(SessionID, "Tranfer-Encoding:chunked/html\r\n\r\n"),
mod_esi:deliver(SessionID, top("Test chunk encoding timeout")),
timer:sleep(20000),
diff --git a/lib/inets/src/http_server/mod_esi.erl b/lib/inets/src/http_server/mod_esi.erl
index 2800250727..b21af1418c 100644
--- a/lib/inets/src/http_server/mod_esi.erl
+++ b/lib/inets/src/http_server/mod_esi.erl
@@ -241,7 +241,7 @@ alias_match_str(Alias, eval_script_alias) ->
%%------------------------ Erl mechanism --------------------------------
erl(#mod{method = Method} = ModData, ESIBody, Modules)
- when (Method =:= "GET") orelse (Method =:= "HEAD") ->
+ when (Method =:= "GET") orelse (Method =:= "HEAD") orelse (Method =:= "DELETE") ->
?hdrt("erl", [{method, Method}]),
case httpd_util:split(ESIBody,":|%3A|/",2) of
{ok, [ModuleName, FuncAndInput]} ->
@@ -264,35 +264,32 @@ erl(#mod{method = Method} = ModData, ESIBody, Modules)
{proceed, [{status,{400, none, BadRequest}} | ModData#mod.data]}
end;
-erl(#mod{request_uri = ReqUri,
- method = "PUT",
- http_version = Version,
- data = Data}, _ESIBody, _Modules) ->
- ?hdrt("erl", [{method, put}]),
- {proceed, [{status,{501,{"PUT", ReqUri, Version},
- ?NICE("Erl mechanism doesn't support method PUT")}}|
- Data]};
-
-erl(#mod{request_uri = ReqUri,
- method = "DELETE",
- http_version = Version,
- data = Data}, _ESIBody, _Modules) ->
- ?hdrt("erl", [{method, delete}]),
- {proceed,[{status,{501,{"DELETE", ReqUri, Version},
- ?NICE("Erl mechanism doesn't support method DELETE")}}|
- Data]};
-
-erl(#mod{request_uri = ReqUri,
- method = "PATCH",
- http_version = Version,
- data = Data}, _ESIBody, _Modules) ->
- ?hdrt("erl", [{method, patch}]),
- {proceed, [{status,{501,{"PATCH", ReqUri, Version},
- ?NICE("Erl mechanism doesn't support method PATCH")}}|
- Data]};
+erl(#mod{method = "PUT", entity_body = Body} = ModData,
+ ESIBody, Modules) ->
+ case httpd_util:split(ESIBody,":|%3A|/",2) of
+ {ok, [ModuleName, FuncAndInput]} ->
+ case httpd_util:split(FuncAndInput,"[\?/]",2) of
+ {ok, [FunctionName, Input]} ->
+ generate_webpage(ModData, ESIBody, Modules,
+ list_to_atom(ModuleName),
+ FunctionName, {Input,Body},
+ [{entity_body, Body} |
+ script_elements(FuncAndInput, Input)]);
+ {ok, [FunctionName]} ->
+ generate_webpage(ModData, ESIBody, Modules,
+ list_to_atom(ModuleName),
+ FunctionName, {undefined,Body},
+ [{entity_body, Body} |
+ script_elements(FuncAndInput, "")]);
+ {ok, BadRequest} ->
+ {proceed,[{status,{400,none, BadRequest}} |
+ ModData#mod.data]}
+ end;
+ {ok, BadRequest} ->
+ {proceed, [{status,{400, none, BadRequest}} | ModData#mod.data]}
+ end;
-erl(#mod{method = "POST",
- entity_body = Body} = ModData, ESIBody, Modules) ->
+erl(#mod{method = "POST", entity_body = Body} = ModData, ESIBody, Modules) ->
?hdrt("erl", [{method, post}]),
case httpd_util:split(ESIBody,":|%3A|/",2) of
{ok,[ModuleName, Function]} ->
@@ -301,7 +298,16 @@ erl(#mod{method = "POST",
Function, Body, [{entity_body, Body}]);
{ok, BadRequest} ->
{proceed,[{status, {400, none, BadRequest}} | ModData#mod.data]}
- end.
+ end;
+
+erl(#mod{request_uri = ReqUri,
+ method = "PATCH",
+ http_version = Version,
+ data = Data}, _ESIBody, _Modules) ->
+ ?hdrt("erl", [{method, patch}]),
+ {proceed, [{status,{501,{"PATCH", ReqUri, Version},
+ ?NICE("Erl mechanism doesn't support method PATCH")}}|
+ Data]}.
generate_webpage(ModData, ESIBody, [all], Module, FunctionName,
Input, ScriptElements) ->
diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src
index 3a31daeb20..d4d21f6774 100644
--- a/lib/inets/src/inets_app/inets.appup.src
+++ b/lib/inets/src/inets_app/inets.appup.src
@@ -18,10 +18,16 @@
%% %CopyrightEnd%
{"%VSN%",
[
+ {<<"6.3.1">>,
+ [{load_module, mod_esi, soft_purge, soft_purge, []}
+ ]},
{<<"6\\..*">>,[{restart_application, inets}]},
{<<"5\\..*">>,[{restart_application, inets}]}
],
[
+ {<<"6.3.1">>,
+ [{load_module, mod_esi, soft_purge, soft_purge, []}
+ ]},
{<<"6\\..*">>,[{restart_application, inets}]},
{<<"5\\..*">>,[{restart_application, inets}]}
]
diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl
index 3194b5ad3d..28e77151f2 100644
--- a/lib/inets/test/httpd_SUITE.erl
+++ b/lib/inets/test/httpd_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -119,8 +119,10 @@ groups() ->
]},
{htaccess, [], [htaccess_1_1, htaccess_1_0, htaccess_0_9]},
{security, [], [security_1_1, security_1_0]}, %% Skip 0.9 as causes timing issus in test code
- {http_1_1, [], [host, chunked, expect, cgi, cgi_chunked_encoding_test,
- trace, range, if_modified_since, mod_esi_chunk_timeout] ++ http_head() ++ http_get() ++ load()},
+ {http_1_1, [],
+ [host, chunked, expect, cgi, cgi_chunked_encoding_test,
+ trace, range, if_modified_since, mod_esi_chunk_timeout,
+ esi_put] ++ http_head() ++ http_get() ++ load()},
{http_1_0, [], [host, cgi, trace] ++ http_head() ++ http_get() ++ load()},
{http_0_9, [], http_head() ++ http_get() ++ load()}
].
@@ -283,20 +285,50 @@ init_per_testcase(Case, Config) when Case == host; Case == trace ->
http_1_1 ->
httpd_1_1
end,
- [{version_cb, Cb} | proplists:delete(version_cb, Config)];
+ dbg(
+ Case,
+ [{version_cb, Cb} | proplists:delete(version_cb, Config)],
+ init);
init_per_testcase(range, Config) ->
ct:timetrap({seconds, 20}),
DocRoot = proplists:get_value(doc_root, Config),
create_range_data(DocRoot),
- Config;
+ dbg(range, Config, init);
-init_per_testcase(_, Config) ->
+init_per_testcase(Case, Config) ->
ct:timetrap({seconds, 20}),
- Config.
-
-end_per_testcase(_Case, _Config) ->
- ok.
+ dbg(Case, Config, init).
+
+end_per_testcase(Case, Config) ->
+ dbg(Case, Config, 'end').
+
+
+dbg(Case, Config, Status) ->
+ Cases = [esi_put],
+ case lists:member(Case, Cases) of
+ true ->
+ case Status of
+ init ->
+ dbg:tracer(),
+ dbg:p(all, c),
+ dbg:tpl(httpd_example, cx),
+ dbg:tpl(mod_esi, generate_webpage, cx),
+ io:format("dbg: started~n"),
+ Config;
+ 'end' ->
+ io:format("dbg: stopped~n"),
+ dbg:stop_clear(),
+ ok
+ end;
+ false ->
+ case Status of
+ init ->
+ Config;
+ 'end' ->
+ ok
+ end
+ end.
%%-------------------------------------------------------------------------
%% Test cases starts here.
@@ -765,6 +797,14 @@ esi(Config) when is_list(Config) ->
ok = http_status("GET /cgi-bin/erl/httpd_example:peer ",
Config, [{statuscode, 200},
{header, "peer-cert-exist", peer(Config)}]).
+
+%%-------------------------------------------------------------------------
+esi_put() ->
+ [{doc, "Test mod_esi PUT"}].
+
+esi_put(Config) when is_list(Config) ->
+ ok = http_status("PUT /cgi-bin/erl/httpd_example/put/123342234123 ",
+ Config, [{statuscode, 200}]).
%%-------------------------------------------------------------------------
mod_esi_chunk_timeout(Config) when is_list(Config) ->
diff --git a/lib/inets/test/inets_SUITE.erl b/lib/inets/test/inets_SUITE.erl
index 5eaf3a28a0..38b8229389 100644
--- a/lib/inets/test/inets_SUITE.erl
+++ b/lib/inets/test/inets_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2015. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -212,11 +212,19 @@ start_httpd(Config) when is_list(Config) ->
Pids0 = [ServicePid || {_, ServicePid} <- inets:services()],
true = lists:member(Pid0, Pids0),
[_|_] = inets:services_info(),
-
inets:stop(httpd, Pid0),
ct:sleep(500),
+ Pids1 = [ServicePid || {_, ServicePid} <- inets:services()],
+ false = lists:member(Pid0, Pids1),
+ {ok, Pid0b} =
+ inets:start(httpd, [{port, 0}, {ipfamily, inet6fb4} | HttpdConf]),
+ Pids0b = [ServicePid || {_, ServicePid} <- inets:services()],
+ true = lists:member(Pid0b, Pids0b),
+ [_|_] = inets:services_info(),
+ inets:stop(httpd, Pid0b),
+ ct:sleep(500),
Pids1 = [ServicePid || {_, ServicePid} <- inets:services()],
- false = lists:member(Pid0, Pids1),
+ false = lists:member(Pid0b, Pids1),
{ok, Pid1} =
inets:start(httpd, [{port, 0}, {ipfamily, inet} | HttpdConf],
stand_alone),
diff --git a/lib/inets/test/inets_sup_SUITE.erl b/lib/inets/test/inets_sup_SUITE.erl
index 5b8b1463c8..1e664337e6 100644
--- a/lib/inets/test/inets_sup_SUITE.erl
+++ b/lib/inets/test/inets_sup_SUITE.erl
@@ -33,7 +33,7 @@ suite() ->
all() ->
[default_tree, ftpc_worker, tftpd_worker,
- httpd_subtree, httpd_subtree_profile,
+ httpd_config, httpd_subtree, httpd_subtree_profile,
httpc_subtree].
groups() ->
@@ -52,9 +52,32 @@ end_per_suite(_) ->
inets:stop(),
ok.
-init_per_testcase(httpd_subtree, Config) ->
+init_per_testcase(httpd_config = TC, Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ Dir = filename:join(PrivDir, TC),
+ ok = file:make_dir(Dir),
+
+ FallbackConfig = [{port, 0},
+ {server_name,"www.test"},
+ {modules, [mod_get]},
+ {server_root, Dir},
+ {document_root, Dir},
+ {bind_address, any},
+ {ipfamily, inet6fb4}],
+ try
+ inets:stop(),
+ inets:start(),
+ inets:start(httpd, FallbackConfig),
+ Config
+ catch
+ _:Reason ->
+ inets:stop(),
+ exit({failed_starting_inets, Reason})
+ end;
+
+init_per_testcase(httpd_subtree = TC, Config) ->
PrivDir = proplists:get_value(priv_dir, Config),
- Dir = filename:join(PrivDir, "root"),
+ Dir = filename:join(PrivDir, TC),
ok = file:make_dir(Dir),
SimpleConfig = [{port, 0},
@@ -75,9 +98,9 @@ init_per_testcase(httpd_subtree, Config) ->
exit({failed_starting_inets, Reason})
end;
-init_per_testcase(httpd_subtree_profile, Config) ->
+init_per_testcase(httpd_subtree_profile = TC, Config) ->
PrivDir = proplists:get_value(priv_dir, Config),
- Dir = filename:join(PrivDir, "root"),
+ Dir = filename:join(PrivDir, TC),
ok = file:make_dir(Dir),
SimpleConfig = [{port, 0},
@@ -193,6 +216,11 @@ tftpd_worker(Config) when is_list(Config) ->
[] = supervisor:which_children(tftp_sup),
ok.
+httpd_config() ->
+ [{doc, "Makes sure the httpd config works for inet6fb4."}].
+httpd_config(Config) when is_list(Config) ->
+ do_httpd_subtree(Config, default).
+
httpd_subtree() ->
[{doc, "Makes sure the httpd sub tree is correct."}].
httpd_subtree(Config) when is_list(Config) ->
diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk
index 543e0d44fd..747724a86b 100644
--- a/lib/inets/vsn.mk
+++ b/lib/inets/vsn.mk
@@ -19,6 +19,6 @@
# %CopyrightEnd%
APPLICATION = inets
-INETS_VSN = 6.3
+INETS_VSN = 6.3.2
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)"
diff --git a/lib/jinterface/doc/src/Makefile b/lib/jinterface/doc/src/Makefile
index cd1e61a795..508c8e01b5 100644
--- a/lib/jinterface/doc/src/Makefile
+++ b/lib/jinterface/doc/src/Makefile
@@ -166,7 +166,7 @@ release_docs_spec: docs
$(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
$(INSTALL_DIR) "$(RELSYSDIR)/doc/html/java/$(JAVA_PKG_PATH)"
$(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- (/bin/cp -rf ../html "$(RELSYSDIR)/doc")
+ ($(CP) -rf ../html "$(RELSYSDIR)/doc")
# $(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTML_FILES) \
# "$(RELSYSDIR)/doc/html"
diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml
index f37433110c..3d35f6f57f 100644
--- a/lib/kernel/doc/src/notes.xml
+++ b/lib/kernel/doc/src/notes.xml
@@ -31,6 +31,41 @@
</header>
<p>This document describes the changes made to the Kernel application.</p>
+<section><title>Kernel 5.0.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ When calling os:cmd from a process that has set trap_exit
+ to true an 'EXIT' message would be left in the message
+ queue. This bug was introduced in kernel vsn 5.0.1.</p>
+ <p>
+ Own Id: OTP-13813</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Kernel 5.0.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix a os:cmd bug where creating a background job using
+ &amp; would cause os:cmd to hang until the background job
+ terminated or closed its stdout and stderr file
+ descriptors. This bug has existed from kernel 5.0.</p>
+ <p>
+ Own Id: OTP-13741</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Kernel 5.0</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl
index 6174136507..48541ec500 100644
--- a/lib/kernel/src/code_server.erl
+++ b/lib/kernel/src/code_server.erl
@@ -135,10 +135,14 @@ split_paths([], _S, Path, Paths) ->
-spec call(term()) -> term().
call(Req) ->
+ Ref = erlang:monitor(process, ?MODULE),
?MODULE ! {code_call, self(), Req},
receive
{?MODULE, Reply} ->
- Reply
+ erlang:demonitor(Ref,[flush]),
+ Reply;
+ {'DOWN',Ref,process,_,_} ->
+ exit({'DOWN',code_server,Req})
end.
reply(Pid, Res) ->
@@ -933,14 +937,20 @@ del_ebin(Dir) ->
filename:join(del_ebin_1(filename:split(Dir))).
del_ebin_1([Parent,App,"ebin"]) ->
- Ext = archive_extension(),
- case filename:basename(Parent, Ext) of
- Parent ->
- %% Plain directory.
+ case filename:basename(Parent) of
+ [] ->
+ %% Parent is the root directory
[Parent,App];
- Archive ->
- %% Archive.
- [Archive]
+ _ ->
+ Ext = archive_extension(),
+ case filename:basename(Parent, Ext) of
+ Parent ->
+ %% Plain directory.
+ [Parent,App];
+ Archive ->
+ %% Archive.
+ [Archive]
+ end
end;
del_ebin_1([H|T]) ->
[H|del_ebin_1(T)];
diff --git a/lib/kernel/src/erl_epmd.erl b/lib/kernel/src/erl_epmd.erl
index f8ef4a475d..7bc9e2ede3 100644
--- a/lib/kernel/src/erl_epmd.erl
+++ b/lib/kernel/src/erl_epmd.erl
@@ -103,6 +103,10 @@ names(EpmdAddr) ->
register_node(Name, PortNo) ->
register_node(Name, PortNo, inet).
+register_node(Name, PortNo, inet_tcp) ->
+ register_node(Name, PortNo, inet);
+register_node(Name, PortNo, inet6_tcp) ->
+ register_node(Name, PortNo, inet6);
register_node(Name, PortNo, Family) ->
gen_server:call(erl_epmd, {register, Name, PortNo, Family}, infinity).
@@ -403,8 +407,6 @@ select_best_version(L1, _H1, _L2, H2) when L1 > H2 ->
0;
select_best_version(_L1, H1, L2, _H2) when L2 > H1 ->
0;
-select_best_version(_L1, H1, L2, _H2) when L2 > H1 ->
- 0;
select_best_version(_L1, H1, _L2, H2) ->
erlang:min(H1, H2).
diff --git a/lib/kernel/src/inet_tcp_dist.erl b/lib/kernel/src/inet_tcp_dist.erl
index 3ca62cc1f1..3084bd599a 100644
--- a/lib/kernel/src/inet_tcp_dist.erl
+++ b/lib/kernel/src/inet_tcp_dist.erl
@@ -77,7 +77,7 @@ gen_listen(Driver, Name) ->
TcpAddress = get_tcp_address(Driver, Socket),
{_,Port} = TcpAddress#net_address.address,
ErlEpmd = net_kernel:epmd_module(),
- case ErlEpmd:register_node(Name, Port) of
+ case ErlEpmd:register_node(Name, Port, Driver) of
{ok, Creation} ->
{ok, {Socket, TcpAddress, Creation}};
Error ->
diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl
index f0ad26b1f2..05bbf1069e 100644
--- a/lib/kernel/src/os.erl
+++ b/lib/kernel/src/os.erl
@@ -226,11 +226,13 @@ extensions() ->
Command :: atom() | io_lib:chars().
cmd(Cmd) ->
validate(Cmd),
- {SpawnCmd, SpawnOpts, SpawnInput} = mk_cmd(os:type(), Cmd),
+ {SpawnCmd, SpawnOpts, SpawnInput, Eot} = mk_cmd(os:type(), Cmd),
Port = open_port({spawn, SpawnCmd}, [binary, stderr_to_stdout,
- stream, in, eof, hide | SpawnOpts]),
+ stream, in, hide | SpawnOpts]),
+ MonRef = erlang:monitor(port, Port),
true = port_command(Port, SpawnInput),
- Bytes = get_data(Port, []),
+ Bytes = get_data(Port, MonRef, Eot, []),
+ demonitor(MonRef, [flush]),
String = unicode:characters_to_list(Bytes),
if %% Convert to unicode list if possible otherwise return bytes
is_list(String) -> String;
@@ -243,7 +245,7 @@ mk_cmd({win32,Wtype}, Cmd) ->
{false,_} -> lists:concat(["cmd /c", Cmd]);
{Cspec,_} -> lists:concat([Cspec," /c",Cmd])
end,
- {Command, [], []};
+ {Command, [], [], <<>>};
mk_cmd(OsType,Cmd) when is_atom(Cmd) ->
mk_cmd(OsType, atom_to_list(Cmd));
mk_cmd(_,Cmd) ->
@@ -252,7 +254,8 @@ mk_cmd(_,Cmd) ->
{"/bin/sh -s unix:cmd", [out],
%% We insert a new line after the command, in case the command
%% contains a comment character.
- ["(", unicode:characters_to_binary(Cmd), "\n); exit\n"]}.
+ ["(", unicode:characters_to_binary(Cmd), "\n); echo \"\^D\"\n"],
+ <<$\^D>>}.
validate(Atom) when is_atom(Atom) ->
ok;
@@ -267,21 +270,44 @@ validate1([List|Rest]) when is_list(List) ->
validate1([]) ->
ok.
-get_data(Port, Sofar) ->
+get_data(Port, MonRef, Eot, Sofar) ->
receive
{Port, {data, Bytes}} ->
- get_data(Port, [Sofar,Bytes]);
- {Port, eof} ->
- Port ! {self(), close},
- receive
- {Port, closed} ->
- true
- end,
- receive
- {'EXIT', Port, _} ->
- ok
- after 1 -> % force context switch
- ok
- end,
+ case eot(Bytes, Eot) of
+ more ->
+ get_data(Port, MonRef, Eot, [Sofar,Bytes]);
+ Last ->
+ Port ! {self(), close},
+ flush_until_closed(Port),
+ flush_exit(Port),
+ iolist_to_binary([Sofar, Last])
+ end;
+ {'DOWN', MonRef, _, _ , _} ->
+ flush_exit(Port),
iolist_to_binary(Sofar)
end.
+
+eot(_Bs, <<>>) ->
+ more;
+eot(Bs, Eot) ->
+ case binary:match(Bs, Eot) of
+ nomatch -> more;
+ {Pos, _} ->
+ binary:part(Bs,{0, Pos})
+ end.
+
+flush_until_closed(Port) ->
+ receive
+ {Port, {data, _Bytes}} ->
+ flush_until_closed(Port);
+ {Port, closed} ->
+ true
+ end.
+
+flush_exit(Port) ->
+ receive
+ {'EXIT', Port, _} ->
+ ok
+ after 1 -> % force context switch
+ ok
+ end.
diff --git a/lib/kernel/test/gen_sctp_SUITE.erl b/lib/kernel/test/gen_sctp_SUITE.erl
index f836b2aa94..620ab235a0 100644
--- a/lib/kernel/test/gen_sctp_SUITE.erl
+++ b/lib/kernel/test/gen_sctp_SUITE.erl
@@ -117,7 +117,11 @@ xfer_min(Config) when is_list(Config) ->
Stream = 0,
Data = <<"The quick brown fox jumps over a lazy dog 0123456789">>,
Loopback = {127,0,0,1},
+ StatOpts =
+ [recv_avg,recv_cnt,recv_max,recv_oct,
+ send_avg,send_cnt,send_max,send_oct],
{ok,Sb} = gen_sctp:open([{type,seqpacket}]),
+ {ok,SbStat1} = inet:getstat(Sb, StatOpts),
{ok,Pb} = inet:port(Sb),
ok = gen_sctp:listen(Sb, true),
@@ -212,6 +216,8 @@ xfer_min(Config) when is_list(Config) ->
assoc_id=SbAssocId}} =
recv_event(log_ok(gen_sctp:recv(Sb, infinity))),
ok = gen_sctp:close(Sa),
+ {ok,SbStat2} = inet:getstat(Sb, StatOpts),
+ [] = filter_stat_eq(SbStat1, SbStat2),
ok = gen_sctp:close(Sb),
receive
@@ -220,6 +226,18 @@ xfer_min(Config) when is_list(Config) ->
end,
ok.
+filter_stat_eq([], []) ->
+ [];
+filter_stat_eq([{Tag,Val1}=Stat|SbStat1], [{Tag,Val2}|SbStat2]) ->
+ if
+ Val1 == Val2 ->
+ [Stat|filter_stat_eq(SbStat1, SbStat2)];
+ true ->
+ filter_stat_eq(SbStat1, SbStat2)
+ end.
+
+
+
%% Minimal data transfer in active mode.
xfer_active(Config) when is_list(Config) ->
Timeout = 2000,
@@ -383,26 +401,28 @@ def_sndrcvinfo(Config) when is_list(Config) ->
assoc_id=S2AssocId} = S2AssocChange =
log_ok(gen_sctp:connect(S2, Loopback, P1, [])),
?LOGVAR(S2AssocChange),
- case recv_event(log_ok(gen_sctp:recv(S1))) of
- {Loopback,P2,
- #sctp_assoc_change{
- state=comm_up,
- error=0,
- assoc_id=S1AssocId}} ->
- ?LOGVAR(S1AssocId);
- {Loopback,P2,
- #sctp_paddr_change{
- state=addr_confirmed,
- error=0,
- assoc_id=S1AssocId}} ->
- ?LOGVAR(S1AssocId),
+ S1AssocId =
+ case recv_event(log_ok(gen_sctp:recv(S1))) of
{Loopback,P2,
#sctp_assoc_change{
state=comm_up,
error=0,
- assoc_id=S1AssocId}} =
- recv_event(log_ok(gen_sctp:recv(S1)))
- end,
+ assoc_id=AssocId}} ->
+ AssocId;
+ {Loopback,P2,
+ #sctp_paddr_change{
+ state=addr_confirmed,
+ error=0,
+ assoc_id=AssocId}} ->
+ {Loopback,P2,
+ #sctp_assoc_change{
+ state=comm_up,
+ error=0,
+ assoc_id=AssocId}} =
+ recv_event(log_ok(gen_sctp:recv(S1))),
+ AssocId
+ end,
+ ?LOGVAR(S1AssocId),
#sctp_sndrcvinfo{
ppid=17, context=0, timetolive=0} = %, assoc_id=S1AssocId} =
@@ -1055,6 +1075,7 @@ peeloff(Config, SockOpts) when is_list(Config) ->
Addr = {127,0,0,1},
Stream = 0,
Timeout = 333,
+ StartTime = timestamp(),
S1 = socket_open([{ifaddr,Addr}|SockOpts], Timeout),
?LOGVAR(S1),
P1 = socket_call(S1, get_port),
@@ -1077,7 +1098,7 @@ peeloff(Config, SockOpts) when is_list(Config) ->
state=comm_up,
assoc_id=AssocId2}}} -> AssocId2
after Timeout ->
- socket_bailout([S1,S2])
+ socket_bailout([S1,S2], StartTime)
end,
?LOGVAR(S2Ai),
S1Ai =
@@ -1087,7 +1108,7 @@ peeloff(Config, SockOpts) when is_list(Config) ->
state=comm_up,
assoc_id=AssocId1}}} -> AssocId1
after Timeout ->
- socket_bailout([S1,S2])
+ socket_bailout([S1,S2], StartTime)
end,
?LOGVAR(S1Ai),
%%
@@ -1095,13 +1116,13 @@ peeloff(Config, SockOpts) when is_list(Config) ->
receive
{S1,{Addr,P2,S1Ai,Stream,<<"Number one">>}} -> ok
after Timeout ->
- socket_bailout([S1,S2])
+ socket_bailout([S1,S2], StartTime)
end,
socket_call(S2, {send,Socket1,S1Ai,Stream,<<"Number two">>}),
receive
{S2,{Addr,P1,S2Ai,Stream,<<"Number two">>}} -> ok
after Timeout ->
- socket_bailout([S1,S2])
+ socket_bailout([S1,S2], StartTime)
end,
%%
S3 = socket_peeloff(Socket1, S1Ai, SockOpts, Timeout),
@@ -1120,31 +1141,31 @@ peeloff(Config, SockOpts) when is_list(Config) ->
receive
{S2,{Addr,P3,S2Ai,Stream,<<"Number three">>}} -> ok
after Timeout ->
- socket_bailout([S1,S2,S3])
+ socket_bailout([S1,S2,S3], StartTime)
end,
socket_call(S3, {send,Socket2,S2Ai,Stream,<<"Number four">>}),
receive
{S3,{Addr,P2,S3Ai,Stream,<<"Number four">>}} -> ok
after Timeout ->
- socket_bailout([S1,S2,S3])
+ socket_bailout([S1,S2,S3], StartTime)
end,
%%
inet:i(sctp),
- socket_close_verbose(S1),
- socket_close_verbose(S2),
+ socket_close_verbose(S1, StartTime),
+ socket_close_verbose(S2, StartTime),
receive
{S3,{Addr,P2,#sctp_shutdown_event{assoc_id=S3Ai_X}}} ->
match_unless_solaris(S3Ai, S3Ai_X)
after Timeout ->
- socket_bailout([S3])
+ socket_bailout([S3], StartTime)
end,
receive
{S3,{Addr,P2,#sctp_assoc_change{state=shutdown_comp,
assoc_id=S3Ai}}} -> ok
after Timeout ->
- socket_bailout([S3])
+ socket_bailout([S3], StartTime)
end,
- socket_close_verbose(S3),
+ socket_close_verbose(S3, StartTime),
[] = flush(),
ok.
@@ -1156,6 +1177,7 @@ buffers(Config) when is_list(Config) ->
Addr = {127,0,0,1},
Stream = 1,
Timeout = 3333,
+ StartTime = timestamp(),
S1 = socket_open([{ip,Addr}], Timeout),
?LOGVAR(S1),
P1 = socket_call(S1, get_port),
@@ -1174,7 +1196,7 @@ buffers(Config) when is_list(Config) ->
state=comm_up,
assoc_id=AssocId2}}} -> AssocId2
after Timeout ->
- socket_bailout([S1,S2])
+ socket_bailout([S1,S2], StartTime)
end,
S1Ai =
receive
@@ -1183,7 +1205,7 @@ buffers(Config) when is_list(Config) ->
state=comm_up,
assoc_id=AssocId1}}} -> AssocId1
after Timeout ->
- socket_bailout([S1,S2])
+ socket_bailout([S1,S2], StartTime)
end,
%%
socket_call(S1, {setopts,[{recbuf,Limit}]}),
@@ -1197,22 +1219,22 @@ buffers(Config) when is_list(Config) ->
receive
{S1,{Addr,P2,S1Ai,Stream,Data}} -> ok
after Timeout ->
- socket_bailout([S1,S2])
+ socket_bailout([S1,S2], StartTime)
end,
%%
- socket_close_verbose(S1),
+ socket_close_verbose(S1, StartTime),
receive
{S2,{Addr,P1,#sctp_shutdown_event{assoc_id=S2Ai}}} -> ok
after Timeout ->
- socket_bailout([S2])
+ socket_bailout([S2], StartTime)
end,
receive
{S2,{Addr,P1,#sctp_assoc_change{state=shutdown_comp,
assoc_id=S2Ai}}} -> ok
after Timeout ->
- socket_bailout([S2])
+ socket_bailout([S2], StartTime)
end,
- socket_close_verbose(S2),
+ socket_close_verbose(S2, StartTime),
[] = flush(),
ok.
@@ -1521,8 +1543,8 @@ socket_peeloff(Socket, AssocId, SocketOpts, Timeout) ->
end,
s_start(Starter, Timeout).
-socket_close_verbose(S) ->
- History = socket_history(socket_close(S)),
+socket_close_verbose(S, StartTime) ->
+ History = socket_history(socket_close(S), StartTime),
io:format("socket_close ~p:~n ~p.~n", [S,History]),
History.
@@ -1535,19 +1557,19 @@ socket_call(S, Request) ->
%% socket_get(S, Key) ->
%% s_req(S, {get,Key}).
-socket_bailout([S|Ss]) ->
- History = socket_history(socket_close(S)),
+socket_bailout([S|Ss], StartTime) ->
+ History = socket_history(socket_close(S), StartTime),
io:format("bailout ~p:~n ~p.~n", [S,History]),
- socket_bailout(Ss);
-socket_bailout([]) ->
+ socket_bailout(Ss, StartTime);
+socket_bailout([], _) ->
io:format("flush: ~p.~n", [flush()]),
ct:fail(socket_bailout).
-socket_history({State,Flush}) ->
+socket_history({State,Flush}, StartTime) ->
{lists:keysort(
2,
lists:flatten(
- [[{Key,Val} || Val <- Vals]
+ [[{Key,{TS-StartTime,Val}} || {TS,Val} <- Vals]
|| {Key,Vals} <- gb_trees:to_list(State)])),
Flush}.
@@ -1610,14 +1632,12 @@ s_loop(Socket, Timeout, Parent, Handler, State) ->
{Parent,Ref,exit} ->
ok = gen_sctp:close(Socket),
Key = exit,
- Val = {now(),Socket},
- NewState = gb_push(Key, Val, State),
+ NewState = gb_push(Key, Socket, State),
Parent ! {self(),Ref,{NewState,flush()}};
{Parent,Ref,{Msg}} ->
Result = Handler(Msg),
Key = req,
- Val = {now(),{Msg,Result}},
- NewState = gb_push(Key, Val, State),
+ NewState = gb_push(Key, {Msg,Result}, State),
Parent ! {self(),Ref,Result},
s_loop(Socket, Timeout, Parent, Handler, NewState);
%% {Parent,Ref,{get,Key}} ->
@@ -1627,16 +1647,15 @@ s_loop(Socket, Timeout, Parent, Handler, State) ->
{[#sctp_sndrcvinfo{stream=Stream,assoc_id=AssocId}=SRI],Data}}
when not is_tuple(Data) ->
case gb_get({assoc_change,AssocId}, State) of
- [{_,{Addr,Port,
- #sctp_assoc_change{
- state=comm_up,
- inbound_streams=Is}}}|_]
+ [{Addr,Port,
+ #sctp_assoc_change{
+ state=comm_up,
+ inbound_streams=Is}}|_]
when 0 =< Stream, Stream < Is-> ok;
[] -> ok
end,
Key = {msg,AssocId,Stream},
- Val = {now(),{Addr,Port,SRI,Data}},
- NewState = gb_push(Key, Val, State),
+ NewState = gb_push(Key, {Addr,Port,SRI,Data}, State),
Parent ! {self(),{Addr,Port,AssocId,Stream,Data}},
again(Socket),
s_loop(Socket, Timeout, Parent, Handler, NewState);
@@ -1647,13 +1666,12 @@ s_loop(Socket, Timeout, Parent, Handler, State) ->
[] -> ok
end,
Key = {assoc_change,AssocId},
- Val = {now(),{Addr,Port,SAC}},
case {gb_get(Key, State),St} of
{[],_} -> ok;
- {[{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_],_}
+ {[{Addr,Port,#sctp_assoc_change{state=comm_up}}|_],_}
when St =:= comm_lost; St =:= shutdown_comp -> ok
end,
- NewState = gb_push(Key, Val, State),
+ NewState = gb_push(Key, {Addr,Port,SAC}, State),
Parent ! {self(),{Addr,Port,SAC}},
again(Socket),
s_loop(Socket, Timeout, Parent, Handler, NewState);
@@ -1667,14 +1685,13 @@ s_loop(Socket, Timeout, Parent, Handler, State) ->
[] -> ok
end,
case {gb_get({assoc_change,AssocId}, State),St} of
- {[{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_],_}
+ {[{Addr,Port,#sctp_assoc_change{state=comm_up}}|_],_}
when St =:= addr_available;
St =:= addr_confirmed -> ok;
{[],addr_confirmed} -> ok
end,
Key = {paddr_change,AssocId},
- Val = {now(),{Addr,Port,SPC}},
- NewState = gb_push(Key, Val, State),
+ NewState = gb_push(Key, {Addr,Port,SPC}, State),
again(Socket),
s_loop(Socket, Timeout, Parent, Handler, NewState);
{sctp,Socket,Addr,Port,
@@ -1684,12 +1701,11 @@ s_loop(Socket, Timeout, Parent, Handler, State) ->
[] -> ok
end,
case gb_get({assoc_change,AssocId}, State) of
- [{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_] -> ok;
+ [{Addr,Port,#sctp_assoc_change{state=comm_up}}|_] -> ok;
[] -> ok
end,
Key = {shutdown_event,AssocId},
- Val = {now(),{Addr,Port}},
- NewState = gb_push(Key, Val, State),
+ NewState = gb_push(Key, {Addr,Port}, State),
Parent ! {self(), {Addr,Port,SSE}},
again(Socket),
s_loop(Socket, Timeout, Parent, Handler, NewState);
@@ -1707,11 +1723,12 @@ again(Socket) ->
end.
gb_push(Key, Val, GBT) ->
+ TS = timestamp(),
case gb_trees:lookup(Key, GBT) of
none ->
- gb_trees:insert(Key, [Val], GBT);
+ gb_trees:insert(Key, [{TS,Val}], GBT);
{value,V} ->
- gb_trees:update(Key, [Val|V], GBT)
+ gb_trees:update(Key, [{TS,Val}|V], GBT)
end.
gb_get(Key, GBT) ->
@@ -1719,7 +1736,7 @@ gb_get(Key, GBT) ->
none ->
[];
{value,V} ->
- V
+ [Val || {_TS,Val} <- V]
end.
match_unless_solaris(A, B) ->
@@ -1727,3 +1744,6 @@ match_unless_solaris(A, B) ->
{unix,sunos} -> B;
_ -> A = B
end.
+
+timestamp() ->
+ erlang:monotonic_time().
diff --git a/lib/kernel/test/os_SUITE.erl b/lib/kernel/test/os_SUITE.erl
index 2a1e5016ec..19ab3713a1 100644
--- a/lib/kernel/test/os_SUITE.erl
+++ b/lib/kernel/test/os_SUITE.erl
@@ -24,7 +24,8 @@
init_per_testcase/2,end_per_testcase/2]).
-export([space_in_cwd/1, quoting/1, cmd_unicode/1, space_in_name/1, bad_command/1,
find_executable/1, unix_comment_in_command/1, deep_list_command/1,
- large_output_command/1, perf_counter_api/1]).
+ large_output_command/1, background_command/0, background_command/1,
+ message_leak/1, perf_counter_api/1]).
-include_lib("common_test/include/ct.hrl").
@@ -35,7 +36,8 @@ suite() ->
all() ->
[space_in_cwd, quoting, cmd_unicode, space_in_name, bad_command,
find_executable, unix_comment_in_command, deep_list_command,
- large_output_command, perf_counter_api].
+ large_output_command, background_command, message_leak,
+ perf_counter_api].
groups() ->
[].
@@ -52,6 +54,13 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
+init_per_testcase(background_command, Config) ->
+ case os:type() of
+ {win32, _} ->
+ {skip,"Should not work on windows"};
+ _ ->
+ Config
+ end;
init_per_testcase(_TC,Config) ->
Config.
@@ -261,13 +270,38 @@ deep_list_command(Config) when is_list(Config) ->
%% FYI: [$e, $c, "ho"] =:= io_lib:format("ec~s", ["ho"])
ok.
-%% Test to take sure that the correct data is
+%% Test to make sure that the correct data is
%% received when doing large commands.
large_output_command(Config) when is_list(Config) ->
%% Maximum allowed on windows is 8192, so we test well below that
AAA = lists:duplicate(7000, $a),
comp(AAA,os:cmd("echo " ++ AAA)).
+%% Test that it is possible on unix to start a background task using os:cmd.
+background_command() ->
+ [{timetrap, {seconds, 5}}].
+background_command(_Config) ->
+ %% This testcase fails when the os:cmd takes
+ %% longer then the 5 second timeout
+ os:cmd("sleep 10&").
+
+%% Test that message does not leak to the calling process
+message_leak(_Config) ->
+ process_flag(trap_exit, true),
+
+ os:cmd("echo hello"),
+ [] = receive_all(),
+
+ case os:type() of
+ {unix, _} ->
+ os:cmd("while true; do echo hello; done&"),
+ [] = receive_all();
+ _ ->
+ ok % Cannot background on non-unix
+ end,
+
+ process_flag(trap_exit, false).
+
%% Test that the os:perf_counter api works as expected
perf_counter_api(_Config) ->
diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk
index e7d422d03c..cdd200a234 100644
--- a/lib/kernel/vsn.mk
+++ b/lib/kernel/vsn.mk
@@ -1 +1 @@
-KERNEL_VSN = 5.0
+KERNEL_VSN = 5.0.2
diff --git a/lib/observer/doc/src/notes.xml b/lib/observer/doc/src/notes.xml
index 505d0dcc89..f79f75fead 100644
--- a/lib/observer/doc/src/notes.xml
+++ b/lib/observer/doc/src/notes.xml
@@ -32,6 +32,23 @@
<p>This document describes the changes made to the Observer
application.</p>
+<section><title>Observer 2.2.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed a crash happening when observing another node, who
+ have a different number of schedulers than the current
+ one.</p>
+ <p>
+ Own Id: OTP-13702 Aux Id: ERL-171 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Observer 2.2</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/observer/src/observer_perf_wx.erl b/lib/observer/src/observer_perf_wx.erl
index 1010a6af4c..6a1aac92c7 100644
--- a/lib/observer/src/observer_perf_wx.erl
+++ b/lib/observer/src/observer_perf_wx.erl
@@ -235,12 +235,14 @@ terminate(_Event, #state{appmon=Pid}) ->
code_change(_, _, State) ->
State.
-restart_fetcher(Node, #state{appmon=Old, panel=Panel, time=#ti{fetch=Freq}=Ti}=State) ->
+restart_fetcher(Node, #state{appmon=Old, panel=Panel, time=#ti{fetch=Freq}=Ti, wins=Wins0}=State) ->
catch Old ! exit,
Me = self(),
Pid = spawn_link(Node, observer_backend, fetch_stats, [Me, round(1000/Freq)]),
wxWindow:refresh(Panel),
- precalc(State#state{active=true, appmon=Pid, samples=reset_data(), time=Ti#ti{tick=0}}).
+ Wins = [W#win{state=undefined} || W <- Wins0],
+ precalc(State#state{active=true, appmon=Pid, samples=reset_data(),
+ wins=Wins, time=Ti#ti{tick=0}}).
reset_data() ->
{0, queue:new()}.
@@ -253,18 +255,25 @@ add_data(Stats, {N, Q}, Wins, _, Active) ->
add_data_1([#win{state={_,St}}|_]=Wins0, Last, N, {Drop, Q}, Active)
when St /= undefined ->
- {Wins, Stat} =
- lists:mapfoldl(fun(Win0, Entry) ->
- {Win1,Stat} = add_data_2(Win0, Last, Entry),
- case Active of
- true ->
- Win = add_data_3(Win1, N, Drop, Stat, Q),
- {Win, Stat};
- false ->
- {Win1, Stat}
- end
- end, #{}, Wins0),
- {Wins, {N,queue:in(Stat#{}, Q)}};
+ try
+ {Wins, Stat} =
+ lists:mapfoldl(fun(Win0, Entry) ->
+ {Win1,Stat} = add_data_2(Win0, Last, Entry),
+ case Active of
+ true ->
+ Win = add_data_3(Win1, N, Drop, Stat, Q),
+ {Win, Stat};
+ false ->
+ {Win1, Stat}
+ end
+ end, #{}, Wins0),
+ {Wins, {N,queue:in(Stat#{}, Q)}}
+ catch no_scheduler_change ->
+ {[Win#win{state=init_data(Id, Last),
+ info = info(Id, Last)}
+ || #win{name=Id}=Win <- Wins0], {0,queue:new()}}
+ end;
+
add_data_1(Wins, Stats, 1, {_, Q}, _) ->
{[Win#win{state=init_data(Id, Stats),
info = info(Id, Stats)}
@@ -409,7 +418,8 @@ collect_data(utilz, MemInfo, Max) ->
calc_delta([{Id, WN, TN}|Ss], [{Id, WP, TP}|Ps]) ->
[100*(WN-WP) div (TN-TP)|calc_delta(Ss, Ps)];
-calc_delta([], []) -> [].
+calc_delta([], []) -> [];
+calc_delta(_, _) -> throw(no_scheduler_change).
precalc(#state{samples=Data0, paint=Paint, time=Ti, wins=Wins0}=State) ->
Wins = [precalc(Ti, Data0, Paint, Win) || Win <- Wins0],
diff --git a/lib/observer/vsn.mk b/lib/observer/vsn.mk
index f214810199..9a7c6a546f 100644
--- a/lib/observer/vsn.mk
+++ b/lib/observer/vsn.mk
@@ -1 +1 @@
-OBSERVER_VSN = 2.2
+OBSERVER_VSN = 2.2.1
diff --git a/lib/odbc/configure.in b/lib/odbc/configure.in
index cb2b23d534..d26daa5eda 100644
--- a/lib/odbc/configure.in
+++ b/lib/odbc/configure.in
@@ -147,7 +147,7 @@ AC_SUBST(THR_LIBS)
odbc_lib_link_success=no
AC_SUBST(TARGET_FLAGS)
case $host_os in
- darwin1[[0-4]].*|darwin[[0-9]].*)
+ darwin1[[0-5]].*|darwin[[0-9]].*)
TARGET_FLAGS="-DUNIX"
if test ! -d "$with_odbc" || test "$with_odbc" = "yes"; then
ODBC_LIB= -L"/usr/lib"
diff --git a/lib/odbc/test/odbc_connect_SUITE.erl b/lib/odbc/test/odbc_connect_SUITE.erl
index 5727c1ca50..261dfc6f20 100644
--- a/lib/odbc/test/odbc_connect_SUITE.erl
+++ b/lib/odbc/test/odbc_connect_SUITE.erl
@@ -89,6 +89,7 @@ init_per_suite(Config) when is_list(Config) ->
[{auto_commit, off}] ++ odbc_test_lib:platform_options()) of
{ok, Ref} ->
odbc:disconnect(Ref),
+ ct:timetrap(?default_timeout),
[{tableName, odbc_test_lib:unique_table_name()} | Config];
_ ->
{skip, "ODBC is not properly setup"}
@@ -129,11 +130,8 @@ init_per_testcase(_TestCase, Config) ->
init_per_testcase_common(Config).
init_per_testcase_common(Config) ->
- test_server:format("ODBCINI = ~p~n", [os:getenv("ODBCINI")]),
- Dog = test_server:timetrap(?default_timeout),
- Temp = lists:keydelete(connection_ref, 1, Config),
- NewConfig = lists:keydelete(watchdog, 1, Temp),
- [{watchdog, Dog} | NewConfig].
+ ct:pal("ODBCINI = ~p~n", [os:getenv("ODBCINI")]),
+ lists:keydelete(connection_ref, 1, Config).
%%--------------------------------------------------------------------
%% Function: end_per_testcase(Case, Config) -> _
@@ -153,25 +151,22 @@ end_per_testcase(_TestCase, Config) ->
end_per_testcase_common(Config).
end_per_testcase_common(Config) ->
- Table = ?config(tableName, Config),
+ Table = proplists:get_value(tableName, Config),
{ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()),
Result = odbc:sql_query(Ref, "DROP TABLE " ++ Table),
io:format("Drop table: ~p ~p~n", [Table, Result]),
- odbc:disconnect(Ref),
- Dog = ?config(watchdog, Config),
- test_server:timetrap_cancel(Dog).
+ odbc:disconnect(Ref).
%%-------------------------------------------------------------------------
%% Test cases starts here.
%%-------------------------------------------------------------------------
-commit(doc)->
- ["Test the use of explicit commit"];
-commit(suite) -> [];
+commit()->
+ [{doc,"Test the use of explicit commit"}].
commit(Config) ->
{ok, Ref} = odbc:connect(?RDBMS:connection_string(),
[{auto_commit, off}] ++ odbc_test_lib:platform_options()),
- Table = ?config(tableName, Config),
+ Table = proplists:get_value(tableName, Config),
TransStr = transaction_support_str(?RDBMS),
{updated, _} =
@@ -205,14 +200,13 @@ commit(Config) ->
ok = odbc:disconnect(Ref).
%%-------------------------------------------------------------------------
-rollback(doc)->
- ["Test the use of explicit rollback"];
-rollback(suite) -> [];
+rollback()->
+ [{doc,"Test the use of explicit rollback"}].
rollback(Config) ->
{ok, Ref} = odbc:connect(?RDBMS:connection_string(),
[{auto_commit, off}] ++ odbc_test_lib:platform_options()),
- Table = ?config(tableName, Config),
+ Table = proplists:get_value(tableName, Config),
TransStr = transaction_support_str(?RDBMS),
@@ -245,9 +239,8 @@ rollback(Config) ->
ok = odbc:disconnect(Ref).
%%-------------------------------------------------------------------------
-not_explicit_commit(doc) ->
- ["Test what happens if you try using commit on a auto_commit connection."];
-not_explicit_commit(suite) -> [];
+not_explicit_commit() ->
+ [{doc,"Test what happens if you try using commit on a auto_commit connection."}].
not_explicit_commit(_Config) ->
{ok, Ref} =
odbc:connect(?RDBMS:connection_string(), [{auto_commit, on}] ++
@@ -256,19 +249,17 @@ not_explicit_commit(_Config) ->
ok = odbc:disconnect(Ref).
%%-------------------------------------------------------------------------
-not_exist_db(doc) ->
- ["Tests valid data format but invalid data in the connection parameters."];
-not_exist_db(suite) -> [];
+not_exist_db() ->
+ [{doc,"Tests valid data format but invalid data in the connection parameters."}].
not_exist_db(_Config) ->
{error, _} = odbc:connect("DSN=foo;UID=bar;PWD=foobar",
odbc_test_lib:platform_options()),
%% So that the odbc control server can be stoped "in the correct way"
- test_server:sleep(100).
+ ct:sleep(100).
%%-------------------------------------------------------------------------
-no_c_executable(doc) ->
- "Test what happens if the port-program can not be found";
-no_c_executable(suite) -> [];
+no_c_executable() ->
+ [{doc,"Test what happens if the port-program can not be found"}].
no_c_executable(_Config) ->
process_flag(trap_exit, true),
Dir = filename:nativename(filename:join(code:priv_dir(odbc),
@@ -293,9 +284,8 @@ no_c_executable(_Config) ->
end.
%%------------------------------------------------------------------------
-port_dies(doc) ->
- "Tests what happens if the port program dies";
-port_dies(suite) -> [];
+port_dies() ->
+ [{doc,"Tests what happens if the port program dies"}].
port_dies(_Config) ->
{ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()),
{status, _} = process_info(Ref, status),
@@ -307,7 +297,7 @@ port_dies(_Config) ->
%% Wait for exit_status from port 5000 ms (will not get a exit
%% status in this case), then wait a little longer to make sure
%% the port and the controlprocess has had time to terminate.
- test_server:sleep(10000),
+ ct:sleep(10000),
undefined = process_info(Ref, status);
[] ->
ct:fail([erlang:port_info(P, name) || P <- erlang:ports()])
@@ -315,9 +305,8 @@ port_dies(_Config) ->
%%-------------------------------------------------------------------------
-control_process_dies(doc) ->
- "Tests what happens if the Erlang control process dies";
-control_process_dies(suite) -> [];
+control_process_dies() ->
+ [{doc,"Tests what happens if the Erlang control process dies"}].
control_process_dies(_Config) ->
{ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()),
process_flag(trap_exit, true),
@@ -326,7 +315,7 @@ control_process_dies(_Config) ->
[Port] ->
{connected, Ref} = erlang:port_info(Port, connected),
exit(Ref, kill),
- test_server:sleep(500),
+ ct:sleep(500),
undefined = erlang:port_info(Port, connected);
%% Check for c-program still running, how?
[] ->
@@ -334,9 +323,8 @@ control_process_dies(_Config) ->
end.
%%-------------------------------------------------------------------------
-client_dies_normal(doc) ->
- ["Client dies with reason normal."];
-client_dies_normal(suite) -> [];
+client_dies_normal() ->
+ [{doc,"Client dies with reason normal."}].
client_dies_normal(Config) when is_list(Config) ->
Pid = spawn(?MODULE, client_normal, [self()]),
@@ -352,7 +340,7 @@ client_dies_normal(Config) when is_list(Config) ->
{'DOWN', MonitorReference, _Type, _Object, _Info} ->
ok
after 5000 ->
- test_server:fail(control_process_not_stopped)
+ ct:fail(control_process_not_stopped)
end.
client_normal(Pid) ->
@@ -366,9 +354,8 @@ client_normal(Pid) ->
%%-------------------------------------------------------------------------
-client_dies_timeout(doc) ->
- ["Client dies with reason timeout."];
-client_dies_timeout(suite) -> [];
+client_dies_timeout() ->
+ [{doc,"Client dies with reason timeout."}].
client_dies_timeout(Config) when is_list(Config) ->
Pid = spawn(?MODULE, client_timeout, [self()]),
@@ -384,7 +371,7 @@ client_dies_timeout(Config) when is_list(Config) ->
{'DOWN', MonitorReference, _Type, _Object, _Info} ->
ok
after 5000 ->
- test_server:fail(control_process_not_stopped)
+ ct:fail(control_process_not_stopped)
end.
client_timeout(Pid) ->
@@ -398,9 +385,8 @@ client_timeout(Pid) ->
%%-------------------------------------------------------------------------
-client_dies_error(doc) ->
- ["Client dies with reason error."];
-client_dies_error(suite) -> [];
+client_dies_error() ->
+ [{doc,"Client dies with reason error."}].
client_dies_error(Config) when is_list(Config) ->
Pid = spawn(?MODULE, client_error, [self()]),
@@ -416,7 +402,7 @@ client_dies_error(Config) when is_list(Config) ->
{'DOWN', MonitorReference, _Type, _Object, _Info} ->
ok
after 5000 ->
- test_server:fail(control_process_not_stopped)
+ ct:fail(control_process_not_stopped)
end.
client_error(Pid) ->
@@ -430,9 +416,8 @@ client_error(Pid) ->
%%-------------------------------------------------------------------------
-connect_timeout(doc) ->
- ["Test the timeout for the connect function."];
-connect_timeout(suite) -> [];
+connect_timeout() ->
+ [{doc,"Test the timeout for the connect function."}].
connect_timeout(Config) when is_list(Config) ->
{'EXIT',timeout} = (catch odbc:connect(?RDBMS:connection_string(),
[{timeout, 0}] ++
@@ -442,10 +427,9 @@ connect_timeout(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-connect_port_timeout(doc) ->
- ["Test the timeout for the port program to connect back to the odbc "
- "application within the connect function."];
-connect_port_timeout(suite) -> [];
+connect_port_timeout() ->
+ [{"Test the timeout for the port program to connect back to the odbc "
+ "application within the connect function."}].
connect_port_timeout(Config) when is_list(Config) ->
%% Application environment var 'port_timeout' has been set to 0 by
%% init_per_testcase/2.
@@ -453,15 +437,14 @@ connect_port_timeout(Config) when is_list(Config) ->
odbc_test_lib:platform_options()).
%%-------------------------------------------------------------------------
-timeout(doc) ->
- ["Test that timeouts don't cause unwanted behavior sush as receiving"
- " an anwser to a previously tiemed out query."];
-timeout(suite) -> [];
+timeout() ->
+ [{"Test that timeouts don't cause unwanted behavior sush as receiving"
+ " an anwser to a previously tiemed out query."}].
timeout(Config) when is_list(Config) ->
{ok, Ref} = odbc:connect(?RDBMS:connection_string(),
[{auto_commit, off}]),
- Table = ?config(tableName, Config),
+ Table = proplists:get_value(tableName, Config),
TransStr = transaction_support_str(?RDBMS),
@@ -512,7 +495,7 @@ update_table_timeout(Table, TimeOut, Pid) ->
{'EXIT', timeout} ->
Pid ! timout_occurred;
{updated, 1} ->
- test_server:fail(database_locker_failed)
+ ct:fail(database_locker_failed)
end,
receive
@@ -537,15 +520,14 @@ update_table_timeout(Table, TimeOut, Pid) ->
ok = odbc:disconnect(Ref).
%%-------------------------------------------------------------------------
-many_timeouts(doc) ->
- ["Tests that many consecutive timeouts lead to that the connection "
- "is shutdown."];
-many_timeouts(suite) -> [];
+many_timeouts() ->
+ [{doc, "Tests that many consecutive timeouts lead to that the connection "
+ "is shutdown."}].
many_timeouts(Config) when is_list(Config) ->
{ok, Ref} = odbc:connect(?RDBMS:connection_string(),
[{auto_commit, off}] ++ odbc_test_lib:platform_options()),
- Table = ?config(tableName, Config),
+ Table = proplists:get_value(tableName, Config),
TransStr = transaction_support_str(?RDBMS),
{updated, _} =
@@ -592,19 +574,18 @@ loop_many_timouts(Ref, UpdateQuery, TimeOut) ->
{'EXIT',timeout} ->
loop_many_timouts(Ref, UpdateQuery, TimeOut);
{updated, 1} ->
- test_server:fail(database_locker_failed);
+ ct:fail(database_locker_failed);
{error, connection_closed} ->
ok
end.
%%-------------------------------------------------------------------------
-timeout_reset(doc) ->
- ["Check that the number of consecutive timouts is reset to 0 when "
- "a successful call to the database is made."];
-timeout_reset(suite) -> [];
+timeout_reset() ->
+ [{doc, "Check that the number of consecutive timouts is reset to 0 when "
+ "a successful call to the database is made."}].
timeout_reset(Config) when is_list(Config) ->
{ok, Ref} = odbc:connect(?RDBMS:connection_string(),
[{auto_commit, off}] ++ odbc_test_lib:platform_options()),
- Table = ?config(tableName, Config),
+ Table = proplists:get_value(tableName, Config),
TransStr = transaction_support_str(?RDBMS),
{updated, _} =
@@ -688,21 +669,20 @@ loop_timout_reset(Ref, UpdateQuery, TimeOut, NumTimeouts) ->
loop_timout_reset(Ref, UpdateQuery,
TimeOut, NumTimeouts - 1);
{updated, 1} ->
- test_server:fail(database_locker_failed);
+ ct:fail(database_locker_failed);
{error, connection_closed} ->
- test_server:fail(connection_closed_premature)
+ ct:fail(connection_closed_premature)
end.
%%-------------------------------------------------------------------------
-disconnect_on_timeout(doc) ->
- ["Check that disconnect after a time out works properly"];
-disconnect_on_timeout(suite) -> [];
+disconnect_on_timeout() ->
+ [{doc,"Check that disconnect after a time out works properly"}].
disconnect_on_timeout(Config) when is_list(Config) ->
{ok, Ref} = odbc:connect(?RDBMS:connection_string(),
[{auto_commit, off}] ++ odbc_test_lib:platform_options()),
- Table = ?config(tableName, Config),
+ Table = proplists:get_value(tableName, Config),
TransStr = transaction_support_str(?RDBMS),
{updated, _} =
@@ -726,7 +706,7 @@ disconnect_on_timeout(Config) when is_list(Config) ->
ok ->
ok = odbc:commit(Ref, commit);
nok ->
- test_server:fail(database_locker_failed)
+ ct:fail(database_locker_failed)
end.
update_table_disconnect_on_timeout(Table, TimeOut, Pid) ->
@@ -744,14 +724,13 @@ update_table_disconnect_on_timeout(Table, TimeOut, Pid) ->
end.
%%-------------------------------------------------------------------------
-connection_closed(doc) ->
- ["Checks that you get an appropriate error message if you try to"
- " use a connection that has been closed"];
-connection_closed(suite) -> [];
+connection_closed() ->
+ [{doc, "Checks that you get an appropriate error message if you try to"
+ " use a connection that has been closed"}].
connection_closed(Config) when is_list(Config) ->
{ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()),
- Table = ?config(tableName, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
"CREATE TABLE " ++ Table ++
@@ -771,14 +750,13 @@ connection_closed(Config) when is_list(Config) ->
{error, connection_closed} = odbc:commit(Ref, commit).
%%-------------------------------------------------------------------------
-disable_scrollable_cursors(doc) ->
- ["Test disabling of scrollable cursors."];
-disable_scrollable_cursors(suite) -> [];
+disable_scrollable_cursors() ->
+ [{doc,"Test disabling of scrollable cursors."}].
disable_scrollable_cursors(Config) when is_list(Config) ->
{ok, Ref} = odbc:connect(?RDBMS:connection_string(),
[{scrollable_cursors, off}]),
- Table = ?config(tableName, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -792,10 +770,10 @@ disable_scrollable_cursors(Config) when is_list(Config) ->
NextResult = ?RDBMS:selected_ID(1, next),
- test_server:format("Expected: ~p~n", [NextResult]),
+ ct:pal("Expected: ~p~n", [NextResult]),
Result = odbc:next(Ref),
- test_server:format("Got: ~p~n", [Result]),
+ ct:pal("Got: ~p~n", [Result]),
NextResult = Result,
{error, scrollable_cursors_disabled} = odbc:first(Ref),
@@ -809,15 +787,14 @@ disable_scrollable_cursors(Config) when is_list(Config) ->
{selected, _ColNames,[]} = odbc:select(Ref, next, 1).
%%-------------------------------------------------------------------------
-return_rows_as_lists(doc)->
- ["Test the option that a row may be returned as a list instead "
- "of a tuple. Too be somewhat backward compatible."];
-return_rows_as_lists(suite) -> [];
+return_rows_as_lists()->
+ [{doc,"Test the option that a row may be returned as a list instead "
+ "of a tuple. Too be somewhat backward compatible."}].
return_rows_as_lists(Config) when is_list(Config) ->
{ok, Ref} = odbc:connect(?RDBMS:connection_string(),
[{tuple_row, off}] ++ odbc_test_lib:platform_options()),
- Table = ?config(tableName, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -854,29 +831,28 @@ return_rows_as_lists(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-api_missuse(doc)->
- ["Test that behaviour of the control process if the api is abused"];
-api_missuse(suite) -> [];
+api_missuse()->
+ [{doc,"Test that behaviour of the control process if the api is abused"}].
api_missuse(Config) when is_list(Config)->
{ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()),
%% Serious programming fault, connetion will be shut down
gen_server:call(Ref, {self(), foobar, 10}, infinity),
- test_server:sleep(10),
+ ct:sleep(10),
undefined = process_info(Ref, status),
{ok, Ref2} = odbc:connect(?RDBMS:connection_string(),
odbc_test_lib:platform_options()),
%% Serious programming fault, connetion will be shut down
gen_server:cast(Ref2, {self(), foobar, 10}),
- test_server:sleep(10),
+ ct:sleep(10),
undefined = process_info(Ref2, status),
{ok, Ref3} = odbc:connect(?RDBMS:connection_string(),
odbc_test_lib:platform_options()),
%% Could be an innocent misstake the connection lives.
Ref3 ! foobar,
- test_server:sleep(10),
+ ct:sleep(10),
{status, _} = process_info(Ref3, status).
transaction_support_str(mysql) ->
@@ -886,13 +862,13 @@ transaction_support_str(_) ->
%%-------------------------------------------------------------------------
-extended_errors(doc)->
- ["Test the extended errors connection option: When off; the old behaviour of just an error "
- "string is returned on error. When on, the error string is replaced by a 3 element tuple "
- "that also exposes underlying ODBC provider error codes."];
-extended_errors(suite) -> [];
+extended_errors()->
+ [{doc,
+ "Test the extended errors connection option: When off; the old behaviour of just an error "
+ "string is returned on error. When on, the error string is replaced by a 3 element tuple "
+ "that also exposes underlying ODBC provider error codes."}].
extended_errors(Config) when is_list(Config)->
- Table = ?config(tableName, Config),
+ Table = proplists:get_value(tableName, Config),
{ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()),
{updated, _} = odbc:sql_query(Ref, "create table " ++ Table ++" ( id integer, data varchar(10))"),
diff --git a/lib/odbc/test/odbc_data_type_SUITE.erl b/lib/odbc/test/odbc_data_type_SUITE.erl
index c88c00725e..a3a4bc78eb 100644
--- a/lib/odbc/test/odbc_data_type_SUITE.erl
+++ b/lib/odbc/test/odbc_data_type_SUITE.erl
@@ -120,6 +120,7 @@ init_per_suite(Config) when is_list(Config) ->
false ->
case (catch odbc:start()) of
ok ->
+ ct:timetrap(?default_timeout),
[{tableName, odbc_test_lib:unique_table_name()}| Config];
_ ->
{skip, "ODBC not startable"}
@@ -191,23 +192,22 @@ init_per_testcase(Case, Config) ->
common_init_per_testcase(Case, Config) ->
PlatformOptions = odbc_test_lib:platform_options(),
- case atom_to_list(Case) of
- "binary" ++ _ ->
- {ok, Ref} = odbc:connect(?RDBMS:connection_string(),
- [{binary_strings, on}] ++ PlatformOptions);
- LCase when LCase == "utf8";
- LCase == "nchar";
- LCase == "nvarchar" ->
- {ok, Ref} = odbc:connect(?RDBMS:connection_string(),
- [{binary_strings, on}] ++ PlatformOptions);
- _ ->
- {ok, Ref} = odbc:connect(?RDBMS:connection_string(), PlatformOptions)
- end,
+ {ok, Ref} =
+ case atom_to_list(Case) of
+ "binary" ++ _ ->
+ odbc:connect(?RDBMS:connection_string(),
+ [{binary_strings, on}] ++ PlatformOptions);
+ LCase when LCase == "utf8";
+ LCase == "nchar";
+ LCase == "nvarchar" ->
+ odbc:connect(?RDBMS:connection_string(),
+ [{binary_strings, on}] ++ PlatformOptions);
+ _ ->
+ odbc:connect(?RDBMS:connection_string(), PlatformOptions)
+ end,
odbc_test_lib:strict(Ref, ?RDBMS),
- Dog = test_server:timetrap(?default_timeout),
- Temp = lists:keydelete(connection_ref, 1, Config),
- NewConfig = lists:keydelete(watchdog, 1, Temp),
- [{watchdog, Dog}, {connection_ref, Ref} | NewConfig].
+ NewConfig = lists:keydelete(connection_ref, 1, Config),
+ [{connection_ref, Ref} | NewConfig].
is_fixed_upper_limit(mysql) ->
false;
@@ -231,28 +231,23 @@ is_supported_bit(_) ->
%% Description: Cleanup after each test case
%%--------------------------------------------------------------------
end_per_testcase(_TestCase, Config) ->
- Ref = ?config(connection_ref, Config),
+ Ref = proplists:get_value(connection_ref, Config),
ok = odbc:disconnect(Ref),
%% Clean up if needed
- Table = ?config(tableName, Config),
+ Table = proplists:get_value(tableName, Config),
{ok, NewRef} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()),
odbc:sql_query(NewRef, "DROP TABLE " ++ Table),
- odbc:disconnect(NewRef),
- Dog = ?config(watchdog, Config),
- test_server:timetrap_cancel(Dog),
- ok.
+ odbc:disconnect(NewRef).
%%-------------------------------------------------------------------------
%% Test cases starts here.
%%-------------------------------------------------------------------------
-char_fixed_lower_limit(doc) ->
- ["Tests fixed length char data type lower boundaries."];
-char_fixed_lower_limit(suite) ->
- [];
+char_fixed_lower_limit() ->
+ [{doc,"Tests fixed length char data type lower boundaries."}].
char_fixed_lower_limit(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
%% Below limit
{error, _} =
@@ -287,18 +282,16 @@ char_fixed_lower_limit(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-char_fixed_upper_limit(doc) ->
- ["Tests fixed length char data type upper boundaries."];
-char_fixed_upper_limit(suite) ->
- [];
+char_fixed_upper_limit() ->
+ [{doc,"Tests fixed length char data type upper boundaries."}].
char_fixed_upper_limit(Config) when is_list(Config) ->
case ?RDBMS of
postgres ->
{skip, "Limit unknown"};
_ ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
%% Upper limit
{updated, _} = % Value == 0 || -1 driver dependent!
@@ -337,14 +330,12 @@ char_fixed_upper_limit(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-char_fixed_padding(doc) ->
- ["Tests that data that is shorter than the given size is padded "
- "with blanks."];
-char_fixed_padding(suite) ->
- [];
+char_fixed_padding() ->
+ [{doc, "Tests that data that is shorter than the given size is padded "
+ "with blanks."}].
char_fixed_padding(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
%% Data should be padded with blanks
{updated, _} = % Value == 0 || -1 driver dependent!
@@ -365,13 +356,11 @@ char_fixed_padding(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-varchar_lower_limit(doc) ->
- ["Tests variable length char data type lower boundaries."];
-varchar_lower_limit(suite) ->
- [];
+varchar_lower_limit() ->
+ [{doc,"Tests variable length char data type lower boundaries."}].
varchar_lower_limit(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
%% Below limit
{error, _} =
@@ -405,13 +394,11 @@ varchar_lower_limit(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-varchar_upper_limit(doc) ->
- ["Tests variable length char data type upper boundaries."];
-varchar_upper_limit(suite) ->
- [];
+varchar_upper_limit() ->
+ [{doc,"Tests variable length char data type upper boundaries."}].
varchar_upper_limit(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
case ?RDBMS of
oracle ->
@@ -455,14 +442,12 @@ varchar_upper_limit(Config) when is_list(Config) ->
end.
%%-------------------------------------------------------------------------
-varchar_no_padding(doc) ->
- ["Tests that data that is shorter than the given max size is not padded "
- "with blanks."];
-varchar_no_padding(suite) ->
- [];
+varchar_no_padding() ->
+ [{doc, "Tests that data that is shorter than the given max size is not padded "
+ "with blanks."}].
varchar_no_padding(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
%% Data should NOT be padded with blanks
{updated, _} = % Value == 0 || -1 driver dependent!
@@ -481,13 +466,11 @@ varchar_no_padding(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-text_lower_limit(doc) ->
- ["Tests 'long' char data type lower boundaries."];
-text_lower_limit(suite) ->
- [];
+text_lower_limit() ->
+ [{doc,"Tests 'long' char data type lower boundaries."}].
text_lower_limit(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -504,15 +487,13 @@ text_lower_limit(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-text_upper_limit(doc) ->
- [];
-text_upper_limit(suite) ->
- [];
+text_upper_limit() ->
+ [{doc,"Tests 'text' char data type upper boundaries."}].
text_upper_limit(Config) when is_list(Config) ->
{skip,"Consumes too much resources" }.
-%% Ref = ?config(connection_ref, Config),
-%% Table = ?config(tableName, Config),
+%% Ref = proplists:get_value(connection_ref, Config),
+%% Table = proplists:get_value(tableName, Config),
%% {updated, _} = % Value == 0 || -1 driver dependent!
%% odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -534,13 +515,11 @@ text_upper_limit(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-binary_char_fixed_lower_limit(doc) ->
- ["Tests fixed length char data type lower boundaries."];
-binary_char_fixed_lower_limit(suite) ->
- [];
+binary_char_fixed_lower_limit() ->
+ [{doc,"Tests fixed length char data type lower boundaries."}].
binary_char_fixed_lower_limit(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
%% Below limit
{error, _} =
@@ -579,18 +558,16 @@ binary_char_fixed_lower_limit(Config) when is_list(Config) ->
++ "')").
%%-------------------------------------------------------------------------
-binary_char_fixed_upper_limit(doc) ->
- ["Tests fixed length char data type upper boundaries."];
-binary_char_fixed_upper_limit(suite) ->
- [];
+binary_char_fixed_upper_limit() ->
+ [{doc,"Tests fixed length char data type upper boundaries."}].
binary_char_fixed_upper_limit(Config) when is_list(Config) ->
case ?RDBMS of
postgres ->
{skip, "Limit unknown"};
_ ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
%% Upper limit
{updated, _} = % Value == 0 || -1 driver dependent!
@@ -630,14 +607,12 @@ binary_char_fixed_upper_limit(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-binary_char_fixed_padding(doc) ->
- ["Tests that data that is shorter than the given size is padded "
- "with blanks."];
-binary_char_fixed_padding(suite) ->
- [];
+binary_char_fixed_padding() ->
+ [{doc, "Tests that data that is shorter than the given size is padded "
+ "with blanks."}].
binary_char_fixed_padding(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
%% Data should be padded with blanks
{updated, _} = % Value == 0 || -1 driver dependent!
@@ -658,13 +633,11 @@ binary_char_fixed_padding(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-binary_varchar_lower_limit(doc) ->
- ["Tests variable length char data type lower boundaries."];
-binary_varchar_lower_limit(suite) ->
- [];
+binary_varchar_lower_limit() ->
+ [{doc,"Tests variable length char data type lower boundaries."}].
binary_varchar_lower_limit(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
%% Below limit
{error, _} =
@@ -701,13 +674,11 @@ binary_varchar_lower_limit(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-binary_varchar_upper_limit(doc) ->
- ["Tests variable length char data type upper boundaries."];
-binary_varchar_upper_limit(suite) ->
- [];
+binary_varchar_upper_limit() ->
+ [{doc,"Tests variable length char data type upper boundaries."}].
binary_varchar_upper_limit(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
case ?RDBMS of
oracle ->
@@ -750,14 +721,12 @@ binary_varchar_upper_limit(Config) when is_list(Config) ->
end.
%%-------------------------------------------------------------------------
-binary_varchar_no_padding(doc) ->
- ["Tests that data that is shorter than the given max size is not padded "
- "with blanks."];
-binary_varchar_no_padding(suite) ->
- [];
+binary_varchar_no_padding() ->
+ [{doc,"Tests that data that is shorter than the given max size is not padded "
+ "with blanks."}].
binary_varchar_no_padding(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
%% Data should NOT be padded with blanks
{updated, _} = % Value == 0 || -1 driver dependent!
@@ -776,13 +745,11 @@ binary_varchar_no_padding(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-binary_text_lower_limit(doc) ->
- ["Tests 'long' char data type lower boundaries."];
-binary_text_lower_limit(suite) ->
- [];
+binary_text_lower_limit() ->
+ [{doc,"Tests 'long' char data type lower boundaries."}].
binary_text_lower_limit(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -799,15 +766,13 @@ binary_text_lower_limit(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-binary_text_upper_limit(doc) ->
- [];
-binary_text_upper_limit(suite) ->
- [];
+binary_text_upper_limit() ->
+ [{doc,"Tests text char data type upper boundaries."}].
binary_text_upper_limit(Config) when is_list(Config) ->
{skip,"Consumes too much resources" }.
-%% Ref = ?config(connection_ref, Config),
-%% Table = ?config(tableName, Config),
+%% Ref = proplists:get_value(connection_ref, Config),
+%% Table = proplists:get_value(tableName, Config),
%% {updated, _} = % Value == 0 || -1 driver dependent!
%% odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -830,17 +795,15 @@ binary_text_upper_limit(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-tiny_int_lower_limit(doc) ->
- ["Tests integer of type tinyint."];
-tiny_int_lower_limit(suite) ->
- [];
+tiny_int_lower_limit() ->
+ [{doc,"Tests integer of type tinyint."}].
tiny_int_lower_limit(Config) when is_list(Config) ->
case ?RDBMS of
postgres ->
{skip, "Type tiniyint not supported"};
_ ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -864,17 +827,15 @@ tiny_int_lower_limit(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-tiny_int_upper_limit(doc) ->
- ["Tests integer of type tinyint."];
-tiny_int_upper_limit(suite) ->
- [];
+tiny_int_upper_limit() ->
+ [{doc,"Tests integer of type tinyint."}].
tiny_int_upper_limit(Config) when is_list(Config) ->
case ?RDBMS of
postgres ->
{skip, "Type tiniyint not supported"};
_ ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -898,13 +859,11 @@ tiny_int_upper_limit(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-small_int_lower_limit(doc) ->
- ["Tests integer of type smallint."];
-small_int_lower_limit(suite) ->
- [];
+small_int_lower_limit() ->
+ [{doc,"Tests integer of type smallint."}].
small_int_lower_limit(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -927,13 +886,11 @@ small_int_lower_limit(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-small_int_upper_limit(doc) ->
- ["Tests integer of type smallint."];
-small_int_upper_limit(suite) ->
- [];
+small_int_upper_limit() ->
+ [{doc,"Tests integer of type smallint."}].
small_int_upper_limit(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -955,13 +912,11 @@ small_int_upper_limit(Config) when is_list(Config) ->
++ "')").
%%-------------------------------------------------------------------------
-int_lower_limit(doc) ->
- ["Tests integer of type int."];
-int_lower_limit(suite) ->
- [];
+int_lower_limit() ->
+ [{doc,"Tests integer of type int."}].
int_lower_limit(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -983,13 +938,11 @@ int_lower_limit(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-int_upper_limit(doc) ->
- ["Tests integer of type int."];
-int_upper_limit(suite) ->
- [];
+int_upper_limit() ->
+ [{doc,"Tests integer of type int."}].
int_upper_limit(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -1011,13 +964,11 @@ int_upper_limit(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-big_int_lower_limit(doc) ->
- ["Tests integer of type bigint"];
-big_int_lower_limit(suite) ->
- [];
+big_int_lower_limit() ->
+ [{doc,"Tests integer of type bigint"}].
big_int_lower_limit(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -1040,13 +991,11 @@ big_int_lower_limit(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-big_int_upper_limit(doc) ->
- ["Tests integer of type bigint."];
-big_int_upper_limit(suite) ->
- [];
+big_int_upper_limit() ->
+ [{doc,"Tests integer of type bigint."}].
big_int_upper_limit(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -1068,17 +1017,13 @@ big_int_upper_limit(Config) when is_list(Config) ->
++ "')").
%%-------------------------------------------------------------------------
-bit_false(doc) ->
- [""];
-bit_false(suite) ->
- [];
bit_false(Config) when is_list(Config) ->
case ?RDBMS of
oracle ->
{skip, "Not supported by driver"};
_ ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -1102,17 +1047,13 @@ bit_false(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-bit_true(doc) ->
- [""];
-bit_true(suite) ->
- [];
bit_true(Config) when is_list(Config) ->
case ?RDBMS of
oracle ->
{skip, "Not supported by driver"};
_ ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
@@ -1136,14 +1077,11 @@ bit_true(Config) when is_list(Config) ->
end.
%%-------------------------------------------------------------------------
-float_lower_limit(doc) ->
- [""];
-float_lower_limit(suite) ->
- [];
+
float_lower_limit(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
case ?RDBMS of
mysql ->
@@ -1186,13 +1124,10 @@ float_lower_limit(Config) when is_list(Config) ->
end.
%%-------------------------------------------------------------------------
-float_upper_limit(doc) ->
- [""];
-float_upper_limit(suite) ->
- [];
+
float_upper_limit(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
case ?RDBMS of
mysql ->
@@ -1218,13 +1153,11 @@ float_upper_limit(Config) when is_list(Config) ->
end.
%%-------------------------------------------------------------------------
-float_zero(doc) ->
- ["Test the float value zero."];
-float_zero(suite) ->
- [];
+float_zero() ->
+ [{doc,"Test the float value zero."}].
float_zero(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -1237,13 +1170,11 @@ float_zero(Config) when is_list(Config) ->
SelectResult =
odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table).
%%-------------------------------------------------------------------------
-real_zero(doc) ->
- ["Test the real value zero."];
-real_zero(suite) ->
- [];
+real_zero() ->
+ [{doc,"Test the real value zero."}].
real_zero(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
case ?RDBMS of
oracle ->
@@ -1262,13 +1193,11 @@ real_zero(Config) when is_list(Config) ->
odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table)
end.
%%------------------------------------------------------------------------
-dec_long(doc) ->
- [""];
dec_long(suit) ->
[];
dec_long(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -1281,13 +1210,11 @@ dec_long(Config) when is_list(Config) ->
odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
["FIELD"] = odbc_test_lib:to_upper(Fields).
%%------------------------------------------------------------------------
-dec_double(doc) ->
- [""];
dec_double(suit) ->
[];
dec_double(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -1329,13 +1256,11 @@ dec_double(Config) when is_list(Config) ->
["FIELD"] = odbc_test_lib:to_upper(Fields2).
%%------------------------------------------------------------------------
-dec_bignum(doc) ->
- [""];
dec_bignum(suit) ->
[];
dec_bignum(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -1361,13 +1286,11 @@ dec_bignum(Config) when is_list(Config) ->
odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
["FIELD"] = odbc_test_lib:to_upper(Fields1).
%%------------------------------------------------------------------------
-num_long(doc) ->
- [""];
num_long(suit) ->
[];
num_long(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -1380,13 +1303,11 @@ num_long(Config) when is_list(Config) ->
odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
["FIELD"] = odbc_test_lib:to_upper(Fields).
%%------------------------------------------------------------------------
-num_double(doc) ->
- [""];
num_double(suit) ->
[];
num_double(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -1426,13 +1347,11 @@ num_double(Config) when is_list(Config) ->
odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
["FIELD"] = odbc_test_lib:to_upper(Fields2).
%%------------------------------------------------------------------------
-num_bignum(doc) ->
- [""];
num_bignum(suit) ->
[];
num_bignum(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -1459,13 +1378,13 @@ num_bignum(Config) when is_list(Config) ->
["FIELD"] = odbc_test_lib:to_upper(Fields1).
%%------------------------------------------------------------------------
-utf8(doc) ->
- ["Test unicode support"];
+utf8() ->
+ [{doc,"Test unicode support"}].
utf8(suit) ->
[];
utf8(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ "(FIELD text)"),
@@ -1487,30 +1406,30 @@ utf8(Config) when is_list(Config) ->
end,
Latin1Data),
- test_server:format("UnicodeIn: ~p ~n",[UnicodeIn]),
+ ct:pal("UnicodeIn: ~p ~n",[UnicodeIn]),
{updated, _} = odbc:param_query(Ref,"INSERT INTO " ++ Table ++ "(FIELD) values(?)",
[{{sql_varchar,50}, UnicodeIn}]),
{selected,_,UnicodeOut} = odbc:sql_query(Ref,"SELECT * FROM " ++ Table),
- test_server:format("UnicodeOut: ~p~n", [UnicodeOut]),
+ ct:pal("UnicodeOut: ~p~n", [UnicodeOut]),
Result = lists:map(fun({Char}) ->
unicode:characters_to_list(Char,utf8)
end, UnicodeOut),
- test_server:format("Result: ~p ~n", [Result]),
+ ct:pal("Result: ~p ~n", [Result]),
Latin1Data = Result.
%%------------------------------------------------------------------------
-nchar(doc) ->
- ["Test unicode nchar support in sqlserver"];
+nchar() ->
+ [{doc,"Test unicode nchar support in sqlserver"}].
nchar(suit) ->
[];
nchar(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -1520,13 +1439,13 @@ nchar(Config) when is_list(Config) ->
%%------------------------------------------------------------------------
-nvarchar(doc) ->
- ["Test 'unicode' nvarchar support"];
+nvarchar() ->
+ [{doc,"Test 'unicode' nvarchar support"}].
nvarchar(suit) ->
[];
nvarchar(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -1535,13 +1454,11 @@ nvarchar(Config) when is_list(Config) ->
w_char_support(Ref, Table, sql_wlongvarchar, 50).
%%------------------------------------------------------------------------
-timestamp(doc) ->
- [""];
timestamp(suit) ->
[];
timestamp(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -1582,21 +1499,21 @@ w_char_support(Ref, Table, CharType, Size) ->
end,
Latin1Data),
- test_server:format("UnicodeIn (utf 16): ~p ~n",[UnicodeIn]),
+ ct:pal("UnicodeIn (utf 16): ~p ~n",[UnicodeIn]),
{updated, _} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ "(FIELD) values(?)",
[{{CharType, Size},UnicodeIn}]),
{selected,_,UnicodeOut} = odbc:sql_query(Ref,"SELECT * FROM " ++ Table),
- test_server:format("UnicodeOut: ~p~n", [UnicodeOut]),
+ ct:pal("UnicodeOut: ~p~n", [UnicodeOut]),
PadResult = lists:map(fun({Unicode}) ->
unicode:characters_to_list(Unicode,{utf16,little})
end,
UnicodeOut),
- test_server:format("Result: ~p~n", [PadResult]),
+ ct:pal("Result: ~p~n", [PadResult]),
Result = lists:map(fun(Str) -> string:strip(Str) end, PadResult),
diff --git a/lib/odbc/test/odbc_query_SUITE.erl b/lib/odbc/test/odbc_query_SUITE.erl
index 5f719b7287..c283872965 100644
--- a/lib/odbc/test/odbc_query_SUITE.erl
+++ b/lib/odbc/test/odbc_query_SUITE.erl
@@ -113,6 +113,7 @@ init_per_suite(Config) when is_list(Config) ->
false ->
case (catch odbc:start()) of
ok ->
+ ct:timetrap(?default_timeout),
[{tableName, odbc_test_lib:unique_table_name()}| Config];
_ ->
{skip, "ODBC not startable"}
@@ -144,10 +145,10 @@ end_per_suite(_Config) ->
init_per_testcase(_Case, Config) ->
{ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()),
odbc_test_lib:strict(Ref, ?RDBMS),
- Dog = test_server:timetrap(?default_timeout),
- Temp = lists:keydelete(connection_ref, 1, Config),
- NewConfig = lists:keydelete(watchdog, 1, Temp),
- [{watchdog, Dog}, {connection_ref, Ref} | NewConfig].
+
+ NewConfig = lists:keydelete(connection_ref, 1, Config),
+
+ [{connection_ref, Ref} | NewConfig].
%%--------------------------------------------------------------------
%% Function: end_per_testcase(Case, Config) -> _
@@ -158,27 +159,23 @@ init_per_testcase(_Case, Config) ->
%% Description: Cleanup after each test case
%%--------------------------------------------------------------------
end_per_testcase(_Case, Config) ->
- Ref = ?config(connection_ref, Config),
+ Ref = proplists:get_value(connection_ref, Config),
ok = odbc:disconnect(Ref),
%% Clean up if needed
- Table = ?config(tableName, Config),
+ Table = proplists:get_value(tableName, Config),
{ok, NewRef} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()),
odbc:sql_query(NewRef, "DROP TABLE " ++ Table),
- odbc:disconnect(NewRef),
- Dog = ?config(watchdog, Config),
- test_server:timetrap_cancel(Dog),
- ok.
+ odbc:disconnect(NewRef).
%%-------------------------------------------------------------------------
%% Test cases starts here.
%%-------------------------------------------------------------------------
-stored_proc(doc)->
- ["Test stored proc with OUT param"];
-stored_proc(suite) -> [];
+stored_proc()->
+ [{doc, "Test stored proc with OUT param"}].
stored_proc(Config) when is_list(Config) ->
case ?RDBMS of
X when X == oracle; X == postgres->
- Ref = ?config(connection_ref, Config),
+ Ref = proplists:get_value(connection_ref, Config),
{updated, _} =
odbc:sql_query(Ref,
?RDBMS:stored_proc_integer_out()),
@@ -192,12 +189,11 @@ stored_proc(Config) when is_list(Config) ->
{skip, "stored proc not yet supported"}
end.
-sql_query(doc)->
- ["Test the common cases"];
-sql_query(suite) -> [];
+sql_query()->
+ [{doc, "Test the common cases"}].
sql_query(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -235,14 +231,14 @@ sql_query(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-select_count(doc) ->
- ["Tests select_count/[2,3]'s timeout, "
- " select_count's functionality will be better tested by other tests "
- " such as first."];
+select_count() ->
+ [{doc, "Tests select_count/[2,3]'s timeout, "
+ " select_count's functionality will be better tested by other tests "
+ " such as first."}].
select_count(sute) -> [];
select_count(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = odbc:sql_query(Ref,
"CREATE TABLE " ++ Table ++
@@ -257,12 +253,11 @@ select_count(Config) when is_list(Config) ->
(catch odbc:select_count(Ref, "SELECT * FROM ", -1)),
ok.
%%-------------------------------------------------------------------------
-first(doc) ->
- ["Tests first/[1,2]"];
-first(suite) -> [];
+first() ->
+ [doc, {"Tests first/[1,2]"}].
first(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = odbc:sql_query(Ref,
"CREATE TABLE " ++ Table ++
@@ -284,12 +279,11 @@ first(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-last(doc) ->
- ["Tests last/[1,2]"];
-last(suite) -> [];
+last() ->
+ [{doc, "Tests last/[1,2]"}].
last(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = odbc:sql_query(Ref,
"CREATE TABLE " ++ Table ++
@@ -311,12 +305,11 @@ last(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-next(doc) ->
- ["Tests next/[1,2]"];
-next(suite) -> [];
+next() ->
+ [{doc, "Tests next/[1,2]"}].
next(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = odbc:sql_query(Ref,
"CREATE TABLE " ++ Table ++
@@ -337,12 +330,11 @@ next(Config) when is_list(Config) ->
{'EXIT', {function_clause, _}} = (catch odbc:next(Ref, -1)),
ok.
%%-------------------------------------------------------------------------
-prev(doc) ->
- ["Tests prev/[1,2]"];
-prev(suite) -> [];
+prev() ->
+ [{doc, "Tests prev/[1,2]"}].
prev(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = odbc:sql_query(Ref,
"CREATE TABLE " ++ Table ++
@@ -366,12 +358,12 @@ prev(Config) when is_list(Config) ->
{'EXIT', {function_clause, _}} = (catch odbc:prev(Ref, -1)),
ok.
%%-------------------------------------------------------------------------
-select_next(doc) ->
- ["Tests select/[4,5] with CursorRelation = next "];
+select_next() ->
+ [{doc, "Tests select/[4,5] with CursorRelation = next "}].
select_next(suit) -> [];
select_next(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = odbc:sql_query(Ref,
"CREATE TABLE " ++ Table ++
@@ -407,12 +399,12 @@ select_next(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-select_relative(doc) ->
- ["Tests select/[4,5] with CursorRelation = relative "];
+select_relative() ->
+ [{doc, "Tests select/[4,5] with CursorRelation = relative "}].
select_relative(suit) -> [];
select_relative(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = odbc:sql_query(Ref,
"CREATE TABLE " ++ Table ++
@@ -448,12 +440,12 @@ select_relative(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-select_absolute(doc) ->
- ["Tests select/[4,5] with CursorRelation = absolute "];
+select_absolute() ->
+ [{doc, "Tests select/[4,5] with CursorRelation = absolute "}].
select_absolute(suit) -> [];
select_absolute(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = odbc:sql_query(Ref,
"CREATE TABLE " ++ Table ++
@@ -482,12 +474,11 @@ select_absolute(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-create_table_twice(doc) ->
- ["Test what happens if you try to create the same table twice."];
-create_table_twice(suite) -> [];
+create_table_twice() ->
+ [{doc, "Test what happens if you try to create the same table twice."}].
create_table_twice(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -501,12 +492,11 @@ create_table_twice(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-delete_table_twice(doc) ->
- ["Test what happens if you try to delete the same table twice."];
-delete_table_twice(suite) -> [];
+delete_table_twice() ->
+ [{doc, "Test what happens if you try to delete the same table twice."}].
delete_table_twice(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -518,12 +508,12 @@ delete_table_twice(Config) when is_list(Config) ->
ok.
%-------------------------------------------------------------------------
-duplicate_key(doc) ->
- ["Test what happens if you try to use the same key twice"];
+duplicate_key() ->
+ [{doc, "Test what happens if you try to use the same key twice"}].
duplicate_key(suit) -> [];
duplicate_key(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -539,13 +529,12 @@ duplicate_key(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-not_connection_owner(doc) ->
- ["Test what happens if a process that did not start the connection"
- " tries to acess it."];
-not_connection_owner(suite) -> [];
+not_connection_owner() ->
+ [{doc, "Test what happens if a process that did not start the connection"
+ " tries to acess it."}].
not_connection_owner(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
spawn_link(?MODULE, not_owner, [self(), Ref, Table]),
@@ -564,12 +553,11 @@ not_owner(Pid, Ref, Table) ->
Pid ! continue.
%%-------------------------------------------------------------------------
-no_result_set(doc) ->
- ["Tests what happens if you try to use a function that needs an "
- "associated result set when there is none."];
-no_result_set(suite) -> [];
+no_result_set() ->
+ [{doc, "Tests what happens if you try to use a function that needs an "
+ "associated result set when there is none."}].
no_result_set(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
+ Ref = proplists:get_value(connection_ref, Config),
{error, result_set_does_not_exist} = odbc:first(Ref),
{error, result_set_does_not_exist} = odbc:last(Ref),
@@ -582,13 +570,11 @@ no_result_set(Config) when is_list(Config) ->
odbc:select(Ref, {relative, 2}, 1),
ok.
%%-------------------------------------------------------------------------
-query_error(doc) ->
- ["Test what happens if there is an error in the query."];
-query_error(suite) ->
- [];
+query_error() ->
+ [{doc, "Test what happens if there is an error in the query."}].
query_error(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -605,15 +591,13 @@ query_error(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-multiple_select_result_sets(doc) ->
- ["Test what happens if you have a batch of select queries."];
-multiple_select_result_sets(suite) ->
- [];
+multiple_select_result_sets() ->
+ [{doc, "Test what happens if you have a batch of select queries."}].
multiple_select_result_sets(Config) when is_list(Config) ->
case ?RDBMS of
sqlserver ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -640,16 +624,14 @@ multiple_select_result_sets(Config) when is_list(Config) ->
end.
%%-------------------------------------------------------------------------
-multiple_mix_result_sets(doc) ->
- ["Test what happens if you have a batch of select and other type of"
- " queries."];
-multiple_mix_result_sets(suite) ->
- [];
+multiple_mix_result_sets() ->
+ [{doc, "Test what happens if you have a batch of select and other type of"
+ " queries."}].
multiple_mix_result_sets(Config) when is_list(Config) ->
case ?RDBMS of
sqlserver ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -674,15 +656,13 @@ multiple_mix_result_sets(Config) when is_list(Config) ->
{skip, "multiple result_set not supported"}
end.
%%-------------------------------------------------------------------------
-multiple_result_sets_error(doc) ->
- ["Test what happens if one of the batched queries fails."];
-multiple_result_sets_error(suite) ->
- [];
+multiple_result_sets_error() ->
+ [{doc, "Test what happens if one of the batched queries fails."}].
multiple_result_sets_error(Config) when is_list(Config) ->
case ?RDBMS of
sqlserver ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -709,15 +689,13 @@ multiple_result_sets_error(Config) when is_list(Config) ->
end.
%%-------------------------------------------------------------------------
-param_insert_tiny_int(doc)->
- ["Test insertion of tiny ints by parameterized queries."];
-param_insert_tiny_int(suite) ->
- [];
+param_insert_tiny_int()->
+ [{doc,"Test insertion of tiny ints by parameterized queries."}].
param_insert_tiny_int(Config) when is_list(Config) ->
case ?RDBMS of
sqlserver ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -746,13 +724,11 @@ param_insert_tiny_int(Config) when is_list(Config) ->
{skip, "Type tiniyint not supported"}
end.
%%-------------------------------------------------------------------------
-param_insert_small_int(doc)->
- ["Test insertion of small ints by parameterized queries."];
-param_insert_small_int(suite) ->
- [];
+param_insert_small_int()->
+ [{doc,"Test insertion of small ints by parameterized queries."}].
param_insert_small_int(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -778,13 +754,11 @@ param_insert_small_int(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-param_insert_int(doc)->
- ["Test insertion of ints by parameterized queries."];
-param_insert_int(suite) ->
- [];
+param_insert_int()->
+ [{doc,"Test insertion of ints by parameterized queries."}].
param_insert_int(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -810,13 +784,11 @@ param_insert_int(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-param_insert_integer(doc)->
- ["Test insertion of integers by parameterized queries."];
-param_insert_integer(suite) ->
- [];
+param_insert_integer()->
+ [{doc,"Test insertion of integers by parameterized queries."}].
param_insert_integer(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -842,13 +814,11 @@ param_insert_integer(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-param_insert_decimal(doc)->
- ["Test insertion of decimal numbers by parameterized queries."];
-param_insert_decimal(suite) ->
- [];
+param_insert_decimal()->
+ [{doc,"Test insertion of decimal numbers by parameterized queries."}].
param_insert_decimal(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -893,13 +863,11 @@ param_insert_decimal(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-param_insert_numeric(doc)->
- ["Test insertion of numeric numbers by parameterized queries."];
-param_insert_numeric(suite) ->
- [];
+param_insert_numeric()->
+ [{doc,"Test insertion of numeric numbers by parameterized queries."}].
param_insert_numeric(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -944,13 +912,11 @@ param_insert_numeric(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-param_insert_char(doc)->
- ["Test insertion of fixed length string by parameterized queries."];
-param_insert_char(suite) ->
- [];
+param_insert_char()->
+ [{doc,"Test insertion of fixed length string by parameterized queries."}].
param_insert_char(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -980,13 +946,11 @@ param_insert_char(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-param_insert_character(doc)->
- ["Test insertion of fixed length string by parameterized queries."];
-param_insert_character(suite) ->
- [];
+param_insert_character()->
+ [{doc,"Test insertion of fixed length string by parameterized queries."}].
param_insert_character(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -1017,13 +981,11 @@ param_insert_character(Config) when is_list(Config) ->
ok.
%%------------------------------------------------------------------------
-param_insert_char_varying(doc)->
- ["Test insertion of variable length strings by parameterized queries."];
-param_insert_char_varying(suite) ->
- [];
+param_insert_char_varying()->
+ [{doc,"Test insertion of variable length strings by parameterized queries."}].
param_insert_char_varying(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -1054,13 +1016,11 @@ param_insert_char_varying(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-param_insert_character_varying(doc)->
- ["Test insertion of variable length strings by parameterized queries."];
-param_insert_character_varying(suite) ->
- [];
+param_insert_character_varying()->
+ [{doc,"Test insertion of variable length strings by parameterized queries."}].
param_insert_character_varying(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -1091,13 +1051,11 @@ param_insert_character_varying(Config) when is_list(Config) ->
[{{sql_varchar, 10}, ["1", 2]}])),
ok.
%%-------------------------------------------------------------------------
-param_insert_float(doc)->
- ["Test insertion of floats by parameterized queries."];
-param_insert_float(suite) ->
- [];
+param_insert_float()->
+ [{doc,"Test insertion of floats by parameterized queries."}].
param_insert_float(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -1120,7 +1078,7 @@ param_insert_float(Config) when is_list(Config) ->
true ->
ok;
false ->
- test_server:fail(float_numbers_do_not_match)
+ ct:fail(float_numbers_do_not_match)
end,
{'EXIT',{badarg,odbc,param_query,'Params'}} =
@@ -1130,13 +1088,11 @@ param_insert_float(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-param_insert_real(doc)->
- ["Test insertion of real numbers by parameterized queries."];
-param_insert_real(suite) ->
- [];
+param_insert_real()->
+ [{doc,"Test insertion of real numbers by parameterized queries."}].
param_insert_real(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -1161,7 +1117,7 @@ param_insert_real(Config) when is_list(Config) ->
true ->
ok;
false ->
- test_server:fail(real_numbers_do_not_match)
+ ct:fail(real_numbers_do_not_match)
end,
{'EXIT',{badarg,odbc,param_query,'Params'}} =
@@ -1171,13 +1127,11 @@ param_insert_real(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-param_insert_double(doc)->
- ["Test insertion of doubles by parameterized queries."];
-param_insert_double(suite) ->
- [];
+param_insert_double()->
+ [{doc,"Test insertion of doubles by parameterized queries."}].
param_insert_double(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -1200,7 +1154,7 @@ param_insert_double(Config) when is_list(Config) ->
true ->
ok;
false ->
- test_server:fail(double_numbers_do_not_match)
+ ct:fail(double_numbers_do_not_match)
end,
{'EXIT',{badarg,odbc,param_query,'Params'}} =
@@ -1210,13 +1164,11 @@ param_insert_double(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-param_insert_mix(doc)->
- ["Test insertion of a mixture of datatypes by parameterized queries."];
-param_insert_mix(suite) ->
- [];
+param_insert_mix()->
+ [{doc,"Test insertion of a mixture of datatypes by parameterized queries."}].
param_insert_mix(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -1237,13 +1189,11 @@ param_insert_mix(Config) when is_list(Config) ->
odbc:sql_query(Ref, "SELECT * FROM " ++ Table),
ok.
%%-------------------------------------------------------------------------
-param_update(doc)->
- ["Test parameterized update query."];
-param_update(suite) ->
- [];
+param_update()->
+ [{doc,"Test parameterized update query."}].
param_update(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -1272,12 +1222,12 @@ param_update(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-delete_nonexisting_row(doc) -> % OTP-5759
- ["Make a delete...where with false conditions (0 rows deleted). ",
- "This used to give an error message (see ticket OTP-5759)."];
+delete_nonexisting_row() -> % OTP-5759
+ [{doc, "Make a delete...where with false conditions (0 rows deleted). ",
+ "This used to give an error message (see ticket OTP-5759)."}].
delete_nonexisting_row(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref, "CREATE TABLE " ++ Table
@@ -1301,13 +1251,11 @@ delete_nonexisting_row(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-param_delete(doc) ->
- ["Test parameterized delete query."];
-param_delete(suite) ->
- [];
+param_delete() ->
+ [{doc,"Test parameterized delete query."}].
param_delete(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -1336,13 +1284,11 @@ param_delete(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-param_select(doc) ->
- ["Test parameterized select query."];
-param_select(suite) ->
- [];
+param_select() ->
+ [{doc,"Test parameterized select query."}].
param_select(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -1366,13 +1312,11 @@ param_select(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-param_select_empty_params(doc) ->
- ["Test parameterized select query with no parameters."];
-param_select_empty_params(suite) ->
- [];
+param_select_empty_params() ->
+ [{doc,"Test parameterized select query with no parameters."}].
param_select_empty_params(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -1396,13 +1340,11 @@ param_select_empty_params(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-param_delete_empty_params(doc) ->
- ["Test parameterized delete query with no parameters."];
-param_delete_empty_params(suite) ->
- [];
+param_delete_empty_params() ->
+ [{doc,"Test parameterized delete query with no parameters."}].
param_delete_empty_params(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -1430,13 +1372,11 @@ param_delete_empty_params(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-describe_integer(doc) ->
- ["Test describe_table/[2,3] for integer columns."];
-describe_integer(suite) ->
- [];
+describe_integer() ->
+ [{doc,"Test describe_table/[2,3] for integer columns."}].
describe_integer(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -1449,13 +1389,11 @@ describe_integer(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-describe_string(doc) ->
- ["Test describe_table/[2,3] for string columns."];
-describe_string(suite) ->
- [];
+describe_string() ->
+ [{doc,"Test describe_table/[2,3] for string columns."}].
describe_string(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -1470,13 +1408,11 @@ describe_string(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-describe_floating(doc) ->
- ["Test describe_table/[2,3] for floting columns."];
-describe_floating(suite) ->
- [];
+describe_floating() ->
+ [{doc,"Test describe_table/[2,3] for floting columns."}].
describe_floating(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -1490,14 +1426,12 @@ describe_floating(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-describe_dec_num(doc) ->
- ["Test describe_table/[2,3] for decimal and numerical columns"];
-describe_dec_num(suite) ->
- [];
+describe_dec_num() ->
+ [{doc,"Test describe_table/[2,3] for decimal and numerical columns"}].
describe_dec_num(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} =
odbc:sql_query(Ref,
@@ -1511,14 +1445,12 @@ describe_dec_num(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-describe_timestamp(doc) ->
- ["Test describe_table/[2,3] for tinmestap columns"];
-describe_timestamp(suite) ->
- [];
+describe_timestamp() ->
+ [{doc,"Test describe_table/[2,3] for tinmestap columns"}].
describe_timestamp(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{updated, _} = % Value == 0 || -1 driver dependent!
odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
@@ -1530,14 +1462,12 @@ describe_timestamp(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-describe_no_such_table(doc) ->
- ["Test what happens if you try to describe a table that does not exist."];
-describe_no_such_table(suite) ->
- [];
+describe_no_such_table() ->
+ [{doc,"Test what happens if you try to describe a table that does not exist."}].
describe_no_such_table(Config) when is_list(Config) ->
- Ref = ?config(connection_ref, Config),
- Table = ?config(tableName, Config),
+ Ref = proplists:get_value(connection_ref, Config),
+ Table = proplists:get_value(tableName, Config),
{error, _ } = odbc:describe_table(Ref, Table),
ok.
@@ -1549,10 +1479,10 @@ describe_no_such_table(Config) when is_list(Config) ->
is_driver_error(Error) ->
case is_list(Error) of
true ->
- test_server:format("Driver error ~p~n", [Error]),
+ ct:pal("Driver error ~p~n", [Error]),
ok;
false ->
- test_server:fail(Error)
+ ct:fail(Error)
end.
is_supported_multiple_resultsets(sqlserver) ->
true;
diff --git a/lib/odbc/test/odbc_start_SUITE.erl b/lib/odbc/test/odbc_start_SUITE.erl
index f055eeb60e..310b92ca29 100644
--- a/lib/odbc/test/odbc_start_SUITE.erl
+++ b/lib/odbc/test/odbc_start_SUITE.erl
@@ -49,6 +49,7 @@ init_per_suite(Config) ->
_ ->
%% Make sure odbc is not already started
odbc:stop(),
+ ct:timetrap(?TIMEOUT),
[{tableName, odbc_test_lib:unique_table_name()} | Config]
end
end.
@@ -74,11 +75,9 @@ end_per_suite(_Config) ->
%% variable, but should NOT alter/remove any existing entries.
%% Description: Initialization before each test case
%%--------------------------------------------------------------------
-init_per_testcase(_TestCase, Config0) ->
- test_server:format("ODBCINI = ~p~n", [os:getenv("ODBCINI")]),
- Config = lists:keydelete(watchdog, 1, Config0),
- Dog = test_server:timetrap(?TIMEOUT),
- [{watchdog, Dog} | Config].
+init_per_testcase(_TestCase, Config) ->
+ ct:pal("ODBCINI = ~p~n", [os:getenv("ODBCINI")]),
+ Config.
%%--------------------------------------------------------------------
%% Function: end_per_testcase(TestCase, Config) -> _
@@ -88,15 +87,8 @@ init_per_testcase(_TestCase, Config0) ->
%% A list of key/value pairs, holding the test case configuration.
%% Description: Cleanup after each test case
%%--------------------------------------------------------------------
-end_per_testcase(_TestCase, Config) ->
- Dog = ?config(watchdog, Config),
- case Dog of
- undefined ->
- ok;
- _ ->
- test_server:timetrap_cancel(Dog)
- end.
-
+end_per_testcase(_TestCase, _Config) ->
+ ok.
%%--------------------------------------------------------------------
%% Function: all(Clause) -> TestCases
%% Clause - atom() - suite | doc
@@ -135,10 +127,8 @@ app(Config) when is_list(Config) ->
appup(Config) when is_list(Config) ->
ok = ?t:appup_test(odbc).
-start(doc) ->
- ["Test start/stop of odbc"];
-start(suite) ->
- [];
+start() ->
+ [{doc,"Test start/stop of odbc"}].
start(Config) when is_list(Config) ->
PlatformOptions = odbc_test_lib:platform_options(),
{error,odbc_not_started} = odbc:connect(?RDBMS:connection_string(),
@@ -153,9 +143,9 @@ start(Config) when is_list(Config) ->
start_odbc(transient),
start_odbc(permanent);
{error, odbc_not_started} ->
- test_server:fail(start_failed);
+ ct:fail(start_failed);
Error ->
- test_server:format("Connection failed: ~p~n", [Error]),
+ ct:pal("Connection failed: ~p~n", [Error]),
{skip, "ODBC is not properly setup"}
end.
@@ -166,13 +156,12 @@ start_odbc(Type) ->
ok = odbc:disconnect(Ref),
odbc:stop();
{error, odbc_not_started} ->
- test_server:fail(start_failed)
+ ct:fail(start_failed)
end.
-long_connection_line(doc)->
- ["Test a connection line longer than 127 characters"];
-long_connection_line(suite) -> [];
+long_connection_line()->
+ [{doc,"Test a connection line longer than 127 characters"}].
long_connection_line(_Config) ->
odbc:start(),
String133 = "unknown_odbc_parameter=01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789",
diff --git a/lib/odbc/test/odbc_test_lib.erl b/lib/odbc/test/odbc_test_lib.erl
index 37c2249303..cf82d4d32a 100644
--- a/lib/odbc/test/odbc_test_lib.erl
+++ b/lib/odbc/test/odbc_test_lib.erl
@@ -61,13 +61,13 @@ odbc_check() ->
end.
check_row_count(Count, Count) ->
- test_server:format("Correct row count Count: ~p~n", [Count]),
+ ct:pal("Correct row count Count: ~p~n", [Count]),
true;
check_row_count(_, undefined) ->
- test_server:format("Undefined row count ~n", []),
+ ct:pal("Undefined row count ~n", []),
true;
check_row_count(Expected, Count) ->
- test_server:format("Incorrect row count Expected ~p Got ~p~n",
+ ct:pal("Incorrect row count Expected ~p Got ~p~n",
[Expected, Count]),
false.
diff --git a/lib/reltool/src/reltool_server.erl b/lib/reltool/src/reltool_server.erl
index 21a4485f94..89e90670cf 100644
--- a/lib/reltool/src/reltool_server.erl
+++ b/lib/reltool/src/reltool_server.erl
@@ -530,9 +530,14 @@ analyse(#state{sys=Sys} = S, Apps, Status) ->
%% Write all #app to app_tab and all #mod to mod_tab.
Status2 = apps_init_is_included(S, Apps, RelApps, Status),
+ %% For each application that is not (directly or indirectly) part
+ %% of a release, but still has #app.is_included==true, propagate
+ %% is_included to the dependencies specified in the .app files.
+ app_propagate_is_included(S),
+
%% For each module that has #mod.is_included==true, propagate
%% is_included to the modules it uses.
- propagate_is_included(S),
+ mod_propagate_is_included(S),
%% Insert reverse dependencies - i.e. for each
%% #mod{name=Mod, uses_mods=[UsedMod]},
@@ -565,31 +570,34 @@ apps_in_rels(Rels, Apps) ->
apps_in_rel(#rel{name = RelName, rel_apps = RelApps}, Apps) ->
Mandatory = [{RelName, kernel}, {RelName, stdlib}],
- Other =
+ Explicit0 = [{RelName, AppName} || #rel_app{name=AppName} <- RelApps],
+ Explicit = Mandatory ++ Explicit0,
+ Deps =
[{RelName, AppName} ||
RA <- RelApps,
- AppName <- [RA#rel_app.name |
+ AppName <-
+ case lists:keyfind(RA#rel_app.name,
+ #app.name,
+ Apps) of
+ App=#app{info = #app_info{applications = AA}} ->
%% Included applications in rel shall overwrite included
%% applications in .app. I.e. included applications in
%% .app shall only be used if it is not defined in rel.
- case RA#rel_app.incl_apps of
- undefined ->
- case lists:keyfind(RA#rel_app.name,
- #app.name,
- Apps) of
- #app{info = #app_info{incl_apps = IA}} ->
- IA;
- false ->
- reltool_utils:throw_error(
- "Release ~tp uses non existing "
- "application ~w",
- [RelName,RA#rel_app.name])
- end;
- IA ->
- IA
- end],
- not lists:keymember(AppName, 2, Mandatory)],
- more_apps_in_rels(Mandatory ++ Other, Apps, []).
+ IA = case RA#rel_app.incl_apps of
+ undefined ->
+ (App#app.info)#app_info.incl_apps;
+ RelIA ->
+ RelIA
+ end,
+ AA ++ IA;
+ false ->
+ reltool_utils:throw_error(
+ "Release ~tp uses non existing "
+ "application ~w",
+ [RelName,RA#rel_app.name])
+ end,
+ not lists:keymember(AppName, 2, Explicit)],
+ more_apps_in_rels(Deps, Apps, Explicit).
more_apps_in_rels([{RelName, AppName} = RA | RelApps], Apps, Acc) ->
case lists:member(RA, Acc) of
@@ -597,8 +605,8 @@ more_apps_in_rels([{RelName, AppName} = RA | RelApps], Apps, Acc) ->
more_apps_in_rels(RelApps, Apps, Acc);
false ->
case lists:keyfind(AppName, #app.name, Apps) of
- #app{info = #app_info{applications = InfoApps}} ->
- Extra = [{RelName, N} || N <- InfoApps],
+ #app{info = #app_info{applications = AA, incl_apps=IA}} ->
+ Extra = [{RelName, N} || N <- AA++IA],
Acc2 = more_apps_in_rels(Extra, Apps, [RA | Acc]),
more_apps_in_rels(RelApps, Apps, Acc2);
false ->
@@ -610,7 +618,6 @@ more_apps_in_rels([{RelName, AppName} = RA | RelApps], Apps, Acc) ->
more_apps_in_rels([], _Apps, Acc) ->
Acc.
-
apps_init_is_included(S, Apps, RelApps, Status) ->
lists:foldl(fun(App, AccStatus) ->
app_init_is_included(S, App, RelApps, AccStatus)
@@ -745,6 +752,100 @@ false_to_undefined(Bool) ->
_ -> Bool
end.
+get_no_rel_apps_and_dependencies(S) ->
+ ets:select(S#state.app_tab, [{#app{name='$1',
+ is_included=true,
+ info=#app_info{applications='$2',
+ incl_apps='$3',
+ _='_'},
+ rels=[],
+ _='_'},
+ [],
+ [{{'$1','$2','$3'}}]}]).
+
+app_propagate_is_included(S) ->
+ lists:foreach(
+ fun({AppName,DepNames1,DepNames2}) ->
+ app_mark_is_included(S,AppName,DepNames1++DepNames2)
+ end,
+ get_no_rel_apps_and_dependencies(S)).
+
+app_mark_is_included(#state{app_tab=AppTab, mod_tab=ModTab, sys=Sys}=S,UsedByName,[AppName|AppNames]) ->
+ case ets:lookup(AppTab, AppName) of
+ [A] ->
+ case A#app.is_included of
+ undefined ->
+ %% Not yet marked => mark and propagate
+ A2 =
+ case A#app.incl_cond of
+ include ->
+ A#app{is_pre_included = true,
+ is_included = true};
+ exclude ->
+ A#app{is_pre_included = false,
+ is_included = false};
+ AppInclCond when AppInclCond==undefined;
+ AppInclCond==derived ->
+ A#app{is_included = true}
+ end,
+ ets:insert(AppTab, A2),
+
+ ModCond =
+ case A#app.mod_cond of
+ undefined -> Sys#sys.mod_cond;
+ _ -> A#app.mod_cond
+ end,
+ Filter =
+ fun(M) ->
+ case ModCond of
+ all -> true;
+ app -> M#mod.is_app_mod;
+ ebin -> M#mod.is_ebin_mod;
+ derived -> false;
+ none -> false
+ end
+ end,
+ Mods = lists:filter(Filter, A#app.mods),
+ %% Mark the modules of this app, but no need to go
+ %% recursive on modules since this is done in
+ %% mod_mark_is_included.
+ [case M#mod.is_included of
+ undefined ->
+ M2 =
+ case M#mod.incl_cond of
+ include ->
+ M#mod{is_pre_included = true,
+ is_included = true};
+ exclude ->
+ M#mod{is_pre_included = false,
+ is_included = false};
+ ModInclCond when ModInclCond==undefined;
+ ModInclCond==derived ->
+ M#mod{is_included = true}
+ end,
+ ets:insert(ModTab, M2);
+ _ ->
+ ok
+ end || M <- Mods],
+
+ %% Go recursive on dependencies
+ #app{info=#app_info{applications=DepNames1,
+ incl_apps=DepNames2}} = A,
+ app_mark_is_included(S,AppName,DepNames1++DepNames2);
+ _ ->
+ %% Already marked
+ ok
+ end;
+ [] ->
+ %% Missing app
+ reltool_utils:throw_error(
+ "Application ~tp uses non existing application ~w",
+ [UsedByName,AppName])
+ end,
+ app_mark_is_included(S, UsedByName, AppNames);
+app_mark_is_included(_S, _UsedByName, []) ->
+ ok.
+
%% Return the list for {ModName, UsesModNames} for all modules where
%% #mod.is_included==true.
get_all_mods_and_dependencies(S) ->
@@ -755,7 +856,7 @@ get_all_mods_and_dependencies(S) ->
[],
[{{'$1','$2'}}]}]).
-propagate_is_included(S) ->
+mod_propagate_is_included(S) ->
case lists:flatmap(
fun({ModName,UsesModNames}) ->
mod_mark_is_included(S,ModName,UsesModNames,[])
diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl
index e9bd46fb27..e8dfea94da 100644
--- a/lib/reltool/test/reltool_server_SUITE.erl
+++ b/lib/reltool/test/reltool_server_SUITE.erl
@@ -142,6 +142,7 @@ all() ->
save_config,
dependencies,
mod_incl_cond_derived,
+ dep_in_app_not_xref,
use_selected_vsn,
use_selected_vsn_relative_path,
non_standard_vsn_id,
@@ -408,7 +409,6 @@ create_release_sort(Config) ->
{app,tools,[{mod_cond,app},{incl_cond,include}]}
]},
%% Generate release
-
?msym({ok, {release, {RelName1, RelVsn},
{erts, _},
[{kernel, _},
@@ -2304,6 +2304,7 @@ dependencies(Config) ->
ok.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Test that incl_cond on mod level overwrites mod_cond on app level
%% Uses same test applications as dependencies/1 above
mod_incl_cond_derived(Config) ->
@@ -2346,6 +2347,40 @@ mod_incl_cond_derived(Config) ->
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% ERL-167, OTP-11993: For applications that are not included in a
+%% release spec ('rel'), dependencies in the .app files are not
+%% considered - only those found with xref.
+dep_in_app_not_xref(Config) ->
+ RelName = "Just testing...",
+ RelVsn = "1.0",
+ Sys =
+ {sys,
+ [
+ {lib_dirs,[filename:join(datadir(Config),"dep_in_app_not_xref")]},
+ {incl_cond,exclude},
+ {incl_archive_filters,[]},
+ {erts,[{incl_cond,exclude}]},
+ {boot_rel, RelName},
+ {rel, RelName, RelVsn, [kernel, stdlib]},
+ {app,kernel,[{incl_cond,include}]},
+ {app,stdlib,[{incl_cond,include}]},
+ {app,x,[{incl_cond,include}]},
+ {app,y,[{incl_cond,derived}]},
+ {app,z,[{incl_cond,derived}]}
+ ]},
+
+ TargetDir = filename:join([?WORK_DIR, "target_dep_in_app_not_xref"]),
+ ?m(ok, reltool_utils:recursive_delete(TargetDir)),
+ ?m(ok, file:make_dir(TargetDir)),
+ ?log("SPEC: ~p\n", [reltool:get_target_spec([{config, Sys}])]),
+ ok = ?m(ok, reltool:create_target([{config, Sys}], TargetDir)),
+ ?log("~p~n",[file:list_dir(filename:join([TargetDir,"lib"]))]),
+
+ ?m(true, filelib:is_dir(filename:join([TargetDir,"lib","y-1.0"]))),
+ ?m(true, filelib:is_dir(filename:join([TargetDir,"lib","z-1.0"]))),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
use_selected_vsn(Config) ->
LibDir1 = filename:join(datadir(Config),"use_selected_vsn"),
B1Dir = filename:join(LibDir1,"b-1.0"),
diff --git a/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/x-1.0/ebin/x.app b/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/x-1.0/ebin/x.app
new file mode 100644
index 0000000000..fe922e3a41
--- /dev/null
+++ b/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/x-1.0/ebin/x.app
@@ -0,0 +1,7 @@
+% -*-erlang-*-
+{application, x,
+ [{description, "Main application in reltool dependency test"},
+ {vsn, "1.0"},
+ {modules, [x1]},
+ {registered, []},
+ {applications, [kernel, stdlib, y]}]}.
diff --git a/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/x-1.0/src/x1.erl b/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/x-1.0/src/x1.erl
new file mode 100644
index 0000000000..bf1e7f9279
--- /dev/null
+++ b/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/x-1.0/src/x1.erl
@@ -0,0 +1,5 @@
+-module(x1).
+-compile(export_all).
+
+f() ->
+ ok.
diff --git a/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/y-1.0/ebin/y.app b/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/y-1.0/ebin/y.app
new file mode 100644
index 0000000000..a21cfe6c21
--- /dev/null
+++ b/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/y-1.0/ebin/y.app
@@ -0,0 +1,8 @@
+% -*-erlang-*-
+{application, y,
+ [{description, "Library application in reltool dependency test"},
+ {vsn, "1.0"},
+ {modules, [y1]},
+ {registered, []},
+ {applications, [kernel, stdlib]},
+ {included_applications, [z]}]}.
diff --git a/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/y-1.0/src/y1.erl b/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/y-1.0/src/y1.erl
new file mode 100644
index 0000000000..902a7e21f3
--- /dev/null
+++ b/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/y-1.0/src/y1.erl
@@ -0,0 +1,5 @@
+-module(y1).
+-compile(export_all).
+
+f() ->
+ ok.
diff --git a/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/z-1.0/ebin/z.app b/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/z-1.0/ebin/z.app
new file mode 100644
index 0000000000..437a0968e9
--- /dev/null
+++ b/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/z-1.0/ebin/z.app
@@ -0,0 +1,7 @@
+% -*-erlang-*-
+{application, z,
+ [{description, "Library application in reltool dependency test"},
+ {vsn, "1.0"},
+ {modules, [z1]},
+ {registered, []},
+ {applications, [kernel, stdlib]}]}.
diff --git a/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/z-1.0/src/z1.erl b/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/z-1.0/src/z1.erl
new file mode 100644
index 0000000000..97ef90b87f
--- /dev/null
+++ b/lib/reltool/test/reltool_server_SUITE_data/dep_in_app_not_xref/z-1.0/src/z1.erl
@@ -0,0 +1,5 @@
+-module(z1).
+-compile(export_all).
+
+f() ->
+ ok.
diff --git a/lib/sasl/test/release_handler_SUITE.erl b/lib/sasl/test/release_handler_SUITE.erl
index ad58bb6b7b..10d2539b7f 100644
--- a/lib/sasl/test/release_handler_SUITE.erl
+++ b/lib/sasl/test/release_handler_SUITE.erl
@@ -1063,6 +1063,12 @@ otp_9395_check_and_purge(cleanup,_Conf) ->
%% OTP-9395 - performance problems when there are MANY processes
%% Upgrade which updates many modules (brutal_purge)
otp_9395_update_many_mods(Conf) when is_list(Conf) ->
+
+ %% "nain" is very slow - it fails this test quite often due to a
+ %% long sys call
+ %% /proc/cpuinfo: "clock: 1249MHz"
+ inet:gethostname() == {ok,"nain"} andalso throw({skip,"slow test host"}),
+
%% Set some paths
PrivDir = priv_dir(Conf),
Dir = filename:join(PrivDir,"otp_9395_update_many_mods"),
@@ -1162,6 +1168,12 @@ otp_9395_update_many_mods(cleanup,_Conf) ->
%% OTP-9395 - performance problems when there are MANY processes
%% Upgrade which removes many modules (brutal_purge)
otp_9395_rm_many_mods(Conf) when is_list(Conf) ->
+
+ %% "nain" is very slow - it fails this test quite often due to a
+ %% long sys call
+ %% /proc/cpuinfo: "clock: 1249MHz"
+ inet:gethostname() == {ok,"nain"} andalso throw({skip,"slow test host"}),
+
%% Set some paths
PrivDir = priv_dir(Conf),
Dir = filename:join(PrivDir,"otp_9395_rm_many_mods"),
@@ -1920,7 +1932,7 @@ wait_nodes_up(Nodes, Tag) ->
wait_nodes_up(Nodes0, Tag, Apps) ->
?t:format("wait_nodes_up(~p, ~p, ~p):",[Nodes0, Tag, Apps]),
Nodes = fix_nodes(Nodes0),
- wait_nodes_up(Nodes, Tag, lists:umerge(Apps,[kernel,stdlib,sasl]), 30).
+ wait_nodes_up(Nodes, Tag, lists:umerge(Apps,[kernel,stdlib,sasl]), 60).
fix_nodes([{Node,InitPid}|Nodes]) ->
[{Node,InitPid} | fix_nodes(Nodes)];
@@ -1962,7 +1974,7 @@ wait_nodes_up(Nodes, Tag, Apps, N) ->
?t:format("",[]),
ok;
_ ->
- timer:sleep(1000),
+ timer:sleep(2000),
wait_nodes_up(Pang, Tag, Apps, N-1)
end.
diff --git a/lib/sasl/test/rh_test_lib.erl b/lib/sasl/test/rh_test_lib.erl
index 11935496d8..dacd8b6b9f 100644
--- a/lib/sasl/test/rh_test_lib.erl
+++ b/lib/sasl/test/rh_test_lib.erl
@@ -18,7 +18,7 @@ cmd(Cmd,Args,Env) ->
case open_port({spawn_executable, Cmd}, [{args,Args},{env,Env}]) of
Port when is_port(Port) ->
unlink(Port),
- erlang:port_close(Port),
+ catch erlang:port_close(Port), % migth already be closed, so catching
ok;
Error ->
Error
diff --git a/lib/snmp/src/agent/snmpa_conf.erl b/lib/snmp/src/agent/snmpa_conf.erl
index 94325a8ed6..fc5116dac9 100644
--- a/lib/snmp/src/agent/snmpa_conf.erl
+++ b/lib/snmp/src/agent/snmpa_conf.erl
@@ -154,7 +154,7 @@ do_write_agent_conf(Fd, {intAgentMaxPacketSize = Tag, Val} ) ->
do_write_agent_conf(Fd, {snmpEngineMaxMessageSize = Tag, Val} ) ->
io:format(Fd, "{~w, ~w}.~n", [Tag, Val]);
do_write_agent_conf(Fd, {snmpEngineID = Tag, Val} ) ->
- io:format(Fd, "{~w, \"~s\"}.~n", [Tag, Val]);
+ io:format(Fd, "{~w, ~p}.~n", [Tag, Val]);
do_write_agent_conf(_Fd, Crap) ->
error({bad_agent_config, Crap}).
@@ -758,9 +758,9 @@ do_write_usm_conf(
PrivP, PrivKeyC, OwnPrivKeyC,
Public, AuthKey, PrivKey}) ->
io:format(Fd, "{", []),
- io:format(Fd, "\"~s\", ", [EngineID]),
- io:format(Fd, "\"~s\", ", [UserName]),
- io:format(Fd, "\"~s\", ", [SecName]),
+ io:format(Fd, "~p, ", [EngineID]),
+ io:format(Fd, "~p, ", [UserName]),
+ io:format(Fd, "~p, ", [SecName]),
io:format(Fd, "~w, ", [Clone]),
io:format(Fd, "~w, ", [AuthP]),
do_write_usm2(Fd, AuthKeyC, ", "),
@@ -859,15 +859,15 @@ do_write_vacm_conf(
{vacmSecurityToGroup,
SecModel, SecName, GroupName}) ->
io:format(
- Fd, "{vacmSecurityToGroup, ~w, \"~s\", \"~s\"}.~n",
+ Fd, "{vacmSecurityToGroup, ~w, ~p, ~p}.~n",
[SecModel, SecName, GroupName]);
do_write_vacm_conf(
Fd,
{vacmAccess,
GroupName, Prefix, SecModel, SecLevel, Match, RV, WV, NV}) ->
io:format(
- Fd, "{vacmAccess, \"~s\", \"~s\", ~w, ~w, ~w, "
- "\"~s\", \"~s\", \"~s\"}.~n",
+ Fd, "{vacmAccess, ~p, ~p, ~w, ~w, ~w, "
+ "~p, ~p, ~p}.~n",
[GroupName, Prefix, SecModel, SecLevel,
Match, RV, WV, NV]);
do_write_vacm_conf(
@@ -875,7 +875,7 @@ do_write_vacm_conf(
{vacmViewTreeFamily,
ViewIndex, ViewSubtree, ViewStatus, ViewMask}) ->
io:format(
- Fd, "{vacmViewTreeFamily, \"~s\", ~w, ~w, ~w}.~n",
+ Fd, "{vacmViewTreeFamily, ~p, ~w, ~w, ~w}.~n",
[ViewIndex, ViewSubtree, ViewStatus, ViewMask]);
do_write_vacm_conf(_Fd, Crap) ->
error({bad_vacm_config, Crap}).
diff --git a/lib/snmp/test/modules.mk b/lib/snmp/test/modules.mk
index 87539f88f7..0f54e67c65 100644
--- a/lib/snmp/test/modules.mk
+++ b/lib/snmp/test/modules.mk
@@ -31,6 +31,7 @@ SUITE_MODULES = \
snmp_agent_mibs_test \
snmp_agent_nfilter_test \
snmp_agent_test \
+ snmp_agent_conf_test \
snmp_agent_test_lib \
snmp_manager_config_test \
snmp_manager_user \
diff --git a/lib/snmp/test/snmp_SUITE.erl b/lib/snmp/test/snmp_SUITE.erl
index 3b9219739b..05bd86253b 100644
--- a/lib/snmp/test/snmp_SUITE.erl
+++ b/lib/snmp/test/snmp_SUITE.erl
@@ -81,7 +81,8 @@ groups() ->
{group, note_store_test}]},
{agent, [], [{group, mibs_test},
{group, nfilter_test},
- {group, agent_test},
+ {group, agent_test},
+ {group, agent_conf_test},
{group, snmpnet_test}]},
{manager, [], [{group, manager_config_test},
{group, manager_user_test},
@@ -97,6 +98,7 @@ groups() ->
{mibs_test, [], [{snmp_agent_mibs_test, all}]},
{nfilter_test, [], [{snmp_agent_nfilter_test, all}]},
{agent_test, [], [{snmp_agent_test, all}]},
+ {agent_conf_test, [], [{snmp_agent_conf_test, all}]},
{snmpnet_test, [], [{snmp_to_snmpnet_SUITE, all}]},
{manager_config_test, [], [{snmp_manager_config_test, all}]},
{manager_user_test, [], [{snmp_manager_user_test, all}]},
diff --git a/lib/snmp/test/snmp_agent_conf_test.erl b/lib/snmp/test/snmp_agent_conf_test.erl
new file mode 100644
index 0000000000..0a22bd47d1
--- /dev/null
+++ b/lib/snmp/test/snmp_agent_conf_test.erl
@@ -0,0 +1,210 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2003-2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(snmp_agent_conf_test).
+
+%%----------------------------------------------------------------------
+%% Include files
+%%----------------------------------------------------------------------
+
+%-include_lib("test_server/include/test_server.hrl").
+%-include("snmp_test_lib.hrl").
+-include_lib("common_test/include/ct.hrl").
+
+-export([
+ all/0,
+ groups/0,
+ init_per_suite/1,
+ end_per_suite/1,
+
+ check_agent/1,
+ check_usm/1,
+ check_vacm/1
+ ]).
+
+
+all() -> [
+ check_agent,
+ check_usm,
+ check_vacm
+ ].
+
+
+groups() ->
+ [].
+
+
+init_per_suite(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ PrivSubdir = filename:join(PrivDir, "snmp_agent_conf_test"),
+ ok = filelib:ensure_dir(filename:join(PrivSubdir, "dummy")),
+ [{priv_subdir, PrivSubdir} | Config].
+
+end_per_suite(_Config) ->
+ ok.
+
+%%======================================================================
+%% Test data
+%%======================================================================
+
+engine_ids() -> [
+ "plain eid",
+ "here\"eid",
+ "comes\neid",
+ "trouble\0eid",
+ binary_to_list(<<"中国引擎标识符"/utf8>>)
+].
+
+snmp_admin_strings() -> [
+ "plain string",
+ "heres\"eid",
+ "trouble\neid",
+ binary_to_list(<<"中国引擎标识符"/utf8>>)
+].
+
+
+%%======================================================================
+%% Test functions
+%%======================================================================
+
+
+check_agent(Config) ->
+ Dir = ?config(priv_subdir, Config),
+ lists:foreach(
+ fun(EngineId) -> check_agent_by_engineid(Dir, EngineId) end,
+ engine_ids()
+ ),
+ ok.
+
+check_agent_by_engineid(Dir, EngineId) ->
+ WEntries = [
+ snmpa_conf:agent_entry(intAgentIpAddress, {0,0,0,0}),
+ snmpa_conf:agent_entry(intAgentUDPPort, 161),
+ snmpa_conf:agent_entry(snmpEngineMaxMessageSize, 484),
+ snmpa_conf:agent_entry(snmpEngineID, EngineId)
+ ],
+
+ ok = snmpa_conf:write_agent_config(Dir, WEntries),
+ {ok, REntries} = snmpa_conf:read_agent_config(Dir),
+
+ true = is_subset(WEntries, REntries),
+ ok.
+
+%%======================================================================
+
+check_usm(Config) ->
+ Dir = ?config(priv_subdir, Config),
+ EngineId = hd(engine_ids()),
+ UserName = hd(snmp_admin_strings()),
+ SecName = hd(snmp_admin_strings()),
+
+ %% vary engine id
+ lists:foreach(
+ fun(EngineId_) -> check_usm_by_params(Dir, EngineId_, UserName, SecName) end,
+ engine_ids()
+ ),
+
+ %% vary user name
+ lists:foreach(
+ fun(UserName_) -> check_usm_by_params(Dir, EngineId, UserName_, SecName) end,
+ snmp_admin_strings()
+ ),
+
+ %% vary sec name
+ lists:foreach(
+ fun(SecName_) -> check_usm_by_params(Dir, EngineId, UserName, SecName_) end,
+ snmp_admin_strings()
+ ),
+
+ ok.
+
+check_usm_by_params(Dir, EngineId, UserName, SecName) ->
+ WEntries = [
+ snmpa_conf:usm_entry(
+ EngineId,
+ UserName,
+ SecName,
+ zeroDotZero,
+ usmNoAuthProtocol, % authproto
+ "", "",
+ usmNoPrivProtocol, % privproto
+ "", "", "",
+ [], %AuthKey
+ []) %PrivKey
+ ],
+
+ ok = snmpa_conf:write_usm_config(Dir, WEntries),
+ {ok, REntries} = snmpa_conf:read_usm_config(Dir),
+
+ true = is_subset(WEntries, REntries),
+ ok.
+
+%%======================================================================
+
+check_vacm(Config) ->
+ Dir = ?config(priv_subdir, Config),
+
+ %% vary sec name
+ lists:foreach(
+ fun(SecName_) -> check_vacm_by_params(Dir, SecName_) end,
+ snmp_admin_strings()
+ ),
+
+ ok.
+
+
+check_vacm_by_params(Dir, SecName) ->
+ WEntries = [
+ %% SecModel, SecName, GroupName
+ snmpa_conf:vacm_s2g_entry(usm, SecName, SecName),
+ %% GroupName,Prefix,SecModel,SecLevel,Match,ReadView,WriteView,NotifyView
+ snmpa_conf:vacm_acc_entry(SecName, "", any, noAuthNoPriv, exact, "all", "all", "all")
+ ],
+
+ ok = snmpa_conf:write_vacm_config(Dir, WEntries),
+ {ok, REntries} = snmpa_conf:read_vacm_config(Dir),
+
+ true = is_subset(WEntries, REntries),
+ ok.
+
+
+
+%%======================================================================
+
+
+%% additional tests needed:
+% check_context()
+% check_community()
+% check_standard()
+% check_target_addr()
+% check_target_params()
+% check_notify()
+
+
+%%======================================================================
+%% Local utility functions
+%%======================================================================
+
+is_subset(List1, List2) ->
+ io:format("Check ~p is subset of ~p\n", [List1, List2]),
+ sets:is_subset(
+ sets:from_list(List1),
+ sets:from_list(List2)
+ ).
diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml
index fd15c334a3..f9d11b2a60 100644
--- a/lib/ssh/doc/src/notes.xml
+++ b/lib/ssh/doc/src/notes.xml
@@ -30,6 +30,30 @@
<file>notes.xml</file>
</header>
+<section><title>Ssh 4.3.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ SSH client does not any longer retry a bad password given
+ as option to ssh:connect et al.</p>
+ <p>
+ Own Id: OTP-13674 Aux Id: TR-HU92273 </p>
+ </item>
+ <item>
+ <p>
+ Removed possible hanging risk for a certain timing
+ sequence when communicating client and server executes on
+ the same node.</p>
+ <p>
+ Own Id: OTP-13715</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Ssh 4.3</title>
<section><title>Improvements and New Features</title>
@@ -108,6 +132,22 @@
</section>
+<section><title>Ssh 4.2.2.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ SSH client does not any longer retry a bad password given
+ as option to ssh:connect et al.</p>
+ <p>
+ Own Id: OTP-13674 Aux Id: TR-HU92273 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Ssh 4.2.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl
index 49eec8072f..fb5e086656 100644
--- a/lib/ssh/src/ssh_auth.erl
+++ b/lib/ssh/src/ssh_auth.erl
@@ -31,12 +31,111 @@
-export([publickey_msg/1, password_msg/1, keyboard_interactive_msg/1,
service_request_msg/1, init_userauth_request_msg/1,
userauth_request_msg/1, handle_userauth_request/3,
- handle_userauth_info_request/3, handle_userauth_info_response/2
+ handle_userauth_info_request/2, handle_userauth_info_response/2
]).
%%--------------------------------------------------------------------
%%% Internal application API
%%--------------------------------------------------------------------
+%%%----------------------------------------------------------------
+userauth_request_msg(#ssh{userauth_methods = ServerMethods,
+ userauth_supported_methods = UserPrefMethods, % Note: this is not documented as supported for clients
+ userauth_preference = ClientMethods0
+ } = Ssh0) ->
+ case sort_select_mthds(ClientMethods0, UserPrefMethods, ServerMethods) of
+ [] ->
+ Msg = #ssh_msg_disconnect{code = ?SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE,
+ description = "Unable to connect using the available authentication methods",
+ language = "en"},
+ {disconnect, Msg, ssh_transport:ssh_packet(Msg, Ssh0)};
+
+ [{Pref,Module,Function,Args} | Prefs] ->
+ Ssh = case Pref of
+ "keyboard-interactive" -> Ssh0;
+ _ -> Ssh0#ssh{userauth_preference = Prefs}
+ end,
+ case Module:Function(Args ++ [Ssh]) of
+ {not_ok, Ssh1} ->
+ userauth_request_msg(Ssh1#ssh{userauth_preference = Prefs});
+ Result ->
+ {Pref,Result}
+ end
+ end.
+
+
+
+sort_select_mthds(Clients, undefined, Servers) ->
+ %% User has not expressed an opinion via option "auth_methods", use the server's prefs
+ sort_select_mthds1(Clients, Servers, string:tokens(?SUPPORTED_AUTH_METHODS,","));
+
+sort_select_mthds(Clients, Users0, Servers0) ->
+ %% The User has an opinion, use the intersection of that and the Servers whishes but
+ %% in the Users order
+ sort_select_mthds1(Clients, string:tokens(Users0,","), Servers0).
+
+
+sort_select_mthds1(Clients, Users0, Servers0) ->
+ Servers = unique(Servers0),
+ Users = unique(Users0),
+ [C || Key <- Users,
+ lists:member(Key, Servers),
+ C <- Clients,
+ element(1,C) == Key].
+
+unique(L) ->
+ lists:reverse(
+ lists:foldl(fun(E,Acc) ->
+ case lists:member(E,Acc) of
+ true -> Acc;
+ false -> [E|Acc]
+ end
+ end, [], L)).
+
+
+%%%---- userauth_request_msg "callbacks"
+password_msg([#ssh{opts = Opts, io_cb = IoCb,
+ user = User, service = Service} = Ssh0]) ->
+ {Password,Ssh} =
+ case proplists:get_value(password, Opts) of
+ undefined when IoCb == ssh_no_io ->
+ {not_ok, Ssh0};
+ undefined ->
+ {IoCb:read_password("ssh password: ",Ssh0), Ssh0};
+ PW ->
+ %% If "password" option is given it should not be tried again
+ {PW, Ssh0#ssh{opts = lists:keyreplace(password,1,Opts,{password,not_ok})}}
+ end,
+ case Password of
+ not_ok ->
+ {not_ok, Ssh};
+ _ ->
+ ssh_transport:ssh_packet(
+ #ssh_msg_userauth_request{user = User,
+ service = Service,
+ method = "password",
+ data =
+ <<?BOOLEAN(?FALSE),
+ ?STRING(unicode:characters_to_binary(Password))>>},
+ Ssh)
+ end.
+
+%% See RFC 4256 for info on keyboard-interactive
+keyboard_interactive_msg([#ssh{user = User,
+ opts = Opts,
+ service = Service} = Ssh]) ->
+ case proplists:get_value(password, Opts) of
+ not_ok ->
+ {not_ok,Ssh}; % No need to use a failed pwd once more
+ _ ->
+ ssh_transport:ssh_packet(
+ #ssh_msg_userauth_request{user = User,
+ service = Service,
+ method = "keyboard-interactive",
+ data = << ?STRING(<<"">>),
+ ?STRING(<<>>) >> },
+ Ssh)
+ end.
+
publickey_msg([Alg, #ssh{user = User,
session_id = SessionId,
service = Service,
@@ -48,7 +147,7 @@ publickey_msg([Alg, #ssh{user = User,
StrAlgo = atom_to_list(Alg),
case encode_public_key(StrAlgo, ssh_transport:extract_public_key(PrivKey)) of
not_ok ->
- not_ok;
+ {not_ok, Ssh};
PubKeyBlob ->
SigData = build_sig_data(SessionId,
User, Service, PubKeyBlob, StrAlgo),
@@ -65,52 +164,15 @@ publickey_msg([Alg, #ssh{user = User,
Ssh)
end;
_Error ->
- not_ok
- end.
-
-password_msg([#ssh{opts = Opts, io_cb = IoCb,
- user = User, service = Service} = Ssh]) ->
- Password = case proplists:get_value(password, Opts) of
- undefined ->
- user_interaction(IoCb, Ssh);
- PW ->
- PW
- end,
- case Password of
- not_ok ->
- not_ok;
- _ ->
- ssh_transport:ssh_packet(
- #ssh_msg_userauth_request{user = User,
- service = Service,
- method = "password",
- data =
- <<?BOOLEAN(?FALSE),
- ?STRING(unicode:characters_to_binary(Password))>>},
- Ssh)
+ {not_ok, Ssh}
end.
-user_interaction(ssh_no_io, _) ->
- not_ok;
-user_interaction(IoCb, Ssh) ->
- IoCb:read_password("ssh password: ", Ssh).
-
-
-%% See RFC 4256 for info on keyboard-interactive
-keyboard_interactive_msg([#ssh{user = User,
- service = Service} = Ssh]) ->
- ssh_transport:ssh_packet(
- #ssh_msg_userauth_request{user = User,
- service = Service,
- method = "keyboard-interactive",
- data = << ?STRING(<<"">>),
- ?STRING(<<>>) >> },
- Ssh).
-
+%%%----------------------------------------------------------------
service_request_msg(Ssh) ->
ssh_transport:ssh_packet(#ssh_msg_service_request{name = "ssh-userauth"},
Ssh#ssh{service = "ssh-userauth"}).
+%%%----------------------------------------------------------------
init_userauth_request_msg(#ssh{opts = Opts} = Ssh) ->
case user_name(Opts) of
{ok, User} ->
@@ -140,34 +202,9 @@ init_userauth_request_msg(#ssh{opts = Opts} = Ssh) ->
description = ErrStr})
end.
-userauth_request_msg(#ssh{userauth_preference = []} = Ssh) ->
- Msg = #ssh_msg_disconnect{code =
- ?SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE,
- description = "Unable to connect using the available"
- " authentication methods",
- language = "en"},
- {disconnect, Msg, ssh_transport:ssh_packet(Msg, Ssh)};
-
-userauth_request_msg(#ssh{userauth_methods = Methods,
- userauth_preference = [{Pref, Module,
- Function, Args} | Prefs]}
- = Ssh0) ->
- Ssh = Ssh0#ssh{userauth_preference = Prefs},
- case lists:member(Pref, Methods) of
- true ->
- case Module:Function(Args ++ [Ssh]) of
- not_ok ->
- userauth_request_msg(Ssh);
- Result ->
- {Pref,Result}
- end;
- false ->
- userauth_request_msg(Ssh)
- end.
-
-
-handle_userauth_request(#ssh_msg_service_request{name =
- Name = "ssh-userauth"},
+%%%----------------------------------------------------------------
+%%% called by server
+handle_userauth_request(#ssh_msg_service_request{name = Name = "ssh-userauth"},
_, Ssh) ->
{ok, ssh_transport:ssh_packet(#ssh_msg_service_accept{name = Name},
Ssh#ssh{service = "ssh-connection"})};
@@ -319,21 +356,28 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User,
partial_success = false}, Ssh)}.
-
-handle_userauth_info_request(
- #ssh_msg_userauth_info_request{name = Name,
- instruction = Instr,
- num_prompts = NumPrompts,
- data = Data}, IoCb,
- #ssh{opts = Opts} = Ssh) ->
+%%%----------------------------------------------------------------
+%%% keyboard-interactive client
+handle_userauth_info_request(#ssh_msg_userauth_info_request{name = Name,
+ instruction = Instr,
+ num_prompts = NumPrompts,
+ data = Data},
+ #ssh{opts = Opts,
+ io_cb = IoCb
+ } = Ssh) ->
PromptInfos = decode_keyboard_interactive_prompts(NumPrompts,Data),
- Responses = keyboard_interact_get_responses(IoCb, Opts,
- Name, Instr, PromptInfos),
- {ok,
- ssh_transport:ssh_packet(
- #ssh_msg_userauth_info_response{num_responses = NumPrompts,
- data = Responses}, Ssh)}.
+ case keyboard_interact_get_responses(IoCb, Opts, Name, Instr, PromptInfos) of
+ not_ok ->
+ not_ok;
+ Responses ->
+ {ok,
+ ssh_transport:ssh_packet(
+ #ssh_msg_userauth_info_response{num_responses = NumPrompts,
+ data = Responses}, Ssh)}
+ end.
+%%%----------------------------------------------------------------
+%%% keyboard-interactive server
handle_userauth_info_response(#ssh_msg_userauth_info_response{num_responses = 1,
data = <<?UINT32(Sz), Password:Sz/binary>>},
#ssh{opts = Opts,
@@ -369,11 +413,6 @@ method_preference(Algs) ->
[{"publickey", ?MODULE, publickey_msg, [A]} | Acc]
end,
[{"password", ?MODULE, password_msg, []},
- {"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []},
- {"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []},
- {"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []},
- {"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []},
- {"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []},
{"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []}
],
Algs).
@@ -473,6 +512,9 @@ keyboard_interact_get_responses(IoCb, Opts, Name, Instr, PromptInfos) ->
proplists:get_value(password, Opts, undefined), IoCb, Name,
Instr, PromptInfos, Opts, NumPrompts).
+
+keyboard_interact_get_responses(_, _, not_ok, _, _, _, _, _, _) ->
+ not_ok;
keyboard_interact_get_responses(_, undefined, Password, _, _, _, _, _,
1) when Password =/= undefined ->
[Password]; %% Password auth implemented with keyboard-interaction and passwd is known
@@ -486,17 +528,18 @@ keyboard_interact_get_responses(true, Fun, _Pwd, _IoCb, Name, Instr, PromptInfos
keyboard_interact_fun(Fun, Name, Instr, PromptInfos, NumPrompts).
keyboard_interact(IoCb, Name, Instr, Prompts, Opts) ->
- if Name /= "" -> IoCb:format("~s~n", [Name]);
- true -> ok
- end,
- if Instr /= "" -> IoCb:format("~s~n", [Instr]);
- true -> ok
- end,
+ write_if_nonempty(IoCb, Name),
+ write_if_nonempty(IoCb, Instr),
lists:map(fun({Prompt, true}) -> IoCb:read_line(Prompt, Opts);
({Prompt, false}) -> IoCb:read_password(Prompt, Opts)
end,
Prompts).
+write_if_nonempty(_, "") -> ok;
+write_if_nonempty(_, <<>>) -> ok;
+write_if_nonempty(IoCb, Text) -> IoCb:format("~s~n",[Text]).
+
+
keyboard_interact_fun(KbdInteractFun, Name, Instr, PromptInfos, NumPrompts) ->
Prompts = lists:map(fun({Prompt, _Echo}) -> Prompt end,
PromptInfos),
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index e952a333ff..f9f4c82351 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -428,7 +428,12 @@ init_connection(server, C = #connection{}, Opts) ->
init_ssh_record(Role, Socket, Opts) ->
{ok, PeerAddr} = inet:peername(Socket),
KeyCb = proplists:get_value(key_cb, Opts, ssh_file),
- AuthMethods = proplists:get_value(auth_methods, Opts, ?SUPPORTED_AUTH_METHODS),
+ AuthMethods = proplists:get_value(auth_methods,
+ Opts,
+ case Role of
+ server -> ?SUPPORTED_AUTH_METHODS;
+ client -> undefined
+ end),
S0 = #ssh{role = Role,
key_cb = KeyCb,
opts = Opts,
@@ -794,9 +799,13 @@ handle_event(_, #ssh_msg_userauth_banner{message = Msg}, {userauth,client}, D) -
handle_event(_, #ssh_msg_userauth_info_request{} = Msg, {userauth_keyboard_interactive, client},
#data{ssh_params = Ssh0} = D) ->
- {ok, {Reply, Ssh}} = ssh_auth:handle_userauth_info_request(Msg, Ssh0#ssh.io_cb, Ssh0),
- send_bytes(Reply, D),
- {next_state, {userauth_keyboard_interactive_info_response,client}, D#data{ssh_params = Ssh}};
+ case ssh_auth:handle_userauth_info_request(Msg, Ssh0) of
+ {ok, {Reply, Ssh}} ->
+ send_bytes(Reply, D),
+ {next_state, {userauth_keyboard_interactive_info_response,client}, D#data{ssh_params = Ssh}};
+ not_ok ->
+ {next_state, {userauth,client}, D, [{next_event, internal, Msg}]}
+ end;
handle_event(_, #ssh_msg_userauth_info_response{} = Msg, {userauth_keyboard_interactive, server}, D) ->
case ssh_auth:handle_userauth_info_response(Msg, D#data.ssh_params) of
@@ -819,7 +828,18 @@ handle_event(_, Msg = #ssh_msg_userauth_failure{}, {userauth_keyboard_interactiv
D = D0#data{ssh_params = Ssh0#ssh{userauth_preference=Prefs}},
{next_state, {userauth,client}, D, [{next_event, internal, Msg}]};
-handle_event(_, Msg=#ssh_msg_userauth_failure{}, {userauth_keyboard_interactive_info_response, client}, D) ->
+handle_event(_, Msg=#ssh_msg_userauth_failure{}, {userauth_keyboard_interactive_info_response, client},
+ #data{ssh_params = Ssh0} = D0) ->
+ Opts = Ssh0#ssh.opts,
+ D = case proplists:get_value(password, Opts) of
+ undefined ->
+ D0;
+ _ ->
+ D0#data{ssh_params =
+ Ssh0#ssh{opts =
+ lists:keyreplace(password,1,Opts,
+ {password,not_ok})}} % FIXME:intermodule dependency
+ end,
{next_state, {userauth,client}, D, [{next_event, internal, Msg}]};
handle_event(_, Msg=#ssh_msg_userauth_success{}, {userauth_keyboard_interactive_info_response, client}, D) ->
@@ -1006,13 +1026,13 @@ handle_event({call,From}, get_print_info, StateName, D) ->
{keep_state_and_data, [{reply,From,Reply}]};
handle_event({call,From}, {connection_info, Options}, _, D) ->
- Info = ssh_info(Options, D, []),
+ Info = fold_keys(Options, fun conn_info/2, D),
{keep_state_and_data, [{reply,From,Info}]};
handle_event({call,From}, {channel_info,ChannelId,Options}, _, D) ->
case ssh_channel:cache_lookup(cache(D), ChannelId) of
#channel{} = Channel ->
- Info = ssh_channel_info(Options, Channel, []),
+ Info = fold_keys(Options, fun chann_info/2, Channel),
{keep_state_and_data, [{reply,From,Info}]};
undefined ->
{keep_state_and_data, [{reply,From,[]}]}
@@ -1206,8 +1226,9 @@ handle_event(internal, prepare_next_packet, _, D) ->
Sz when Sz >= Enough ->
self() ! {D#data.transport_protocol, D#data.socket, <<>>};
_ ->
- inet:setopts(D#data.socket, [{active, once}])
+ ok
end,
+ inet:setopts(D#data.socket, [{active, once}]),
keep_state_and_data;
handle_event(info, {CloseTag,Socket}, StateName,
@@ -1315,12 +1336,10 @@ terminate(shutdown, StateName, State0) ->
State = send_msg(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION,
description = "Application shutdown"},
State0),
-timer:sleep(400), %% FIXME!!! gen_tcp:shutdown instead
finalize_termination(StateName, State);
%% terminate({shutdown,Msg}, StateName, State0) when is_record(Msg,ssh_msg_disconnect)->
%% State = send_msg(Msg, State0),
-%% timer:sleep(400), %% FIXME!!! gen_tcp:shutdown instead
%% finalize_termination(StateName, Msg, State);
terminate({shutdown,_R}, StateName, State) ->
@@ -1635,7 +1654,6 @@ new_channel_id(#data{connection_state = #connection{channel_id_seed = Id} =
disconnect(Msg=#ssh_msg_disconnect{description=Description}, _StateName, State0) ->
State = send_msg(Msg, State0),
disconnect_fun(Description, State),
-timer:sleep(400),
{stop, {shutdown,Description}, State}.
%%%----------------------------------------------------------------
@@ -1644,43 +1662,43 @@ counterpart_versions(NumVsn, StrVsn, #ssh{role = server} = Ssh) ->
counterpart_versions(NumVsn, StrVsn, #ssh{role = client} = Ssh) ->
Ssh#ssh{s_vsn = NumVsn , s_version = StrVsn}.
-ssh_info([], _State, Acc) ->
- Acc;
-ssh_info([client_version | Rest], #data{ssh_params = #ssh{c_vsn = IntVsn,
- c_version = StringVsn}} = State, Acc) ->
- ssh_info(Rest, State, [{client_version, {IntVsn, StringVsn}} | Acc]);
-
-ssh_info([server_version | Rest], #data{ssh_params =#ssh{s_vsn = IntVsn,
- s_version = StringVsn}} = State, Acc) ->
- ssh_info(Rest, State, [{server_version, {IntVsn, StringVsn}} | Acc]);
-ssh_info([peer | Rest], #data{ssh_params = #ssh{peer = Peer}} = State, Acc) ->
- ssh_info(Rest, State, [{peer, Peer} | Acc]);
-ssh_info([sockname | Rest], #data{socket = Socket} = State, Acc) ->
- {ok, SockName} = inet:sockname(Socket),
- ssh_info(Rest, State, [{sockname, SockName}|Acc]);
-ssh_info([user | Rest], #data{auth_user = User} = State, Acc) ->
- ssh_info(Rest, State, [{user, User}|Acc]);
-ssh_info([ _ | Rest], State, Acc) ->
- ssh_info(Rest, State, Acc).
-
-
-ssh_channel_info([], _, Acc) ->
- Acc;
+%%%----------------------------------------------------------------
+conn_info(client_version, #data{ssh_params=S}) -> {S#ssh.c_vsn, S#ssh.c_version};
+conn_info(server_version, #data{ssh_params=S}) -> {S#ssh.s_vsn, S#ssh.s_version};
+conn_info(peer, #data{ssh_params=S}) -> S#ssh.peer;
+conn_info(user, D) -> D#data.auth_user;
+conn_info(sockname, D) -> {ok, SockName} = inet:sockname(D#data.socket),
+ SockName;
+%% dbg options ( = not documented):
+conn_info(socket, D) -> D#data.socket;
+conn_info(chan_ids, D) ->
+ ssh_channel:cache_foldl(fun(#channel{local_id=Id}, Acc) ->
+ [Id | Acc]
+ end, [], cache(D)).
-ssh_channel_info([recv_window | Rest], #channel{recv_window_size = WinSize,
- recv_packet_size = Packsize
- } = Channel, Acc) ->
- ssh_channel_info(Rest, Channel, [{recv_window, {{win_size, WinSize},
- {packet_size, Packsize}}} | Acc]);
-ssh_channel_info([send_window | Rest], #channel{send_window_size = WinSize,
- send_packet_size = Packsize
- } = Channel, Acc) ->
- ssh_channel_info(Rest, Channel, [{send_window, {{win_size, WinSize},
- {packet_size, Packsize}}} | Acc]);
-ssh_channel_info([ _ | Rest], Channel, Acc) ->
- ssh_channel_info(Rest, Channel, Acc).
+%%%----------------------------------------------------------------
+chann_info(recv_window, C) ->
+ {{win_size, C#channel.recv_window_size},
+ {packet_size, C#channel.recv_packet_size}};
+chann_info(send_window, C) ->
+ {{win_size, C#channel.send_window_size},
+ {packet_size, C#channel.send_packet_size}};
+%% dbg options ( = not documented):
+chann_info(pid, C) ->
+ C#channel.user.
+%%%----------------------------------------------------------------
+%% Assisting meta function for the *_info functions
+fold_keys(Keys, Fun, Extra) ->
+ lists:foldr(fun(Key, Acc) ->
+ try Fun(Key, Extra) of
+ Value -> [{Key,Value}|Acc]
+ catch
+ _:_ -> Acc
+ end
+ end, [], Keys).
+%%%----------------------------------------------------------------
log_error(Reason) ->
Report = io_lib:format("Erlang ssh connection handler failed with reason:~n"
" ~p~n"
@@ -1689,7 +1707,6 @@ log_error(Reason) ->
[Reason, erlang:get_stacktrace()]),
error_logger:error_report(Report).
-
%%%----------------------------------------------------------------
not_connected_filter({connection_reply, _Data}) -> true;
not_connected_filter(_) -> false.
diff --git a/lib/ssh/src/ssh_dbg.erl b/lib/ssh/src/ssh_dbg.erl
index 480795cfc7..bd6bc0335b 100644
--- a/lib/ssh/src/ssh_dbg.erl
+++ b/lib/ssh/src/ssh_dbg.erl
@@ -24,6 +24,7 @@
-export([messages/0,
messages/1,
+ messages/2,
stop/0
]).
@@ -36,12 +37,16 @@
writer,
acc = []}).
%%%================================================================
-messages() -> messages(fun(String,_D) -> io:format(String) end).
-%% messages() -> messages(fun(String,Acc) -> [String|Acc] end)
+messages() ->
+ messages(fun(String,_D) -> io:format(String) end).
messages(Write) when is_function(Write,2) ->
+ messages(Write, fun(X) -> X end).
+
+messages(Write, MangleArg) when is_function(Write,2),
+ is_function(MangleArg,1) ->
catch dbg:start(),
- setup_tracer(Write),
+ setup_tracer(Write, MangleArg),
dbg:p(new,c),
dbg_ssh_messages().
@@ -63,18 +68,30 @@ msg_formater({trace,_Pid,return_from,{ssh_message,encode,1},_Res}, D) ->
msg_formater({trace,_Pid,call,{ssh_message,decode,_}}, D) ->
D;
msg_formater({trace,Pid,return_from,{ssh_message,decode,1},Msg}, D) ->
- fmt("~nRECV ~p ~s~n", [Pid,wr_record(shrink_bin(Msg))], D);
+ fmt("~n~p RECV ~s~n", [Pid,wr_record(shrink_bin(Msg))], D);
msg_formater({trace,_Pid,call,{ssh_transport,select_algorithm,_}}, D) ->
D;
msg_formater({trace,Pid,return_from,{ssh_transport,select_algorithm,3},{ok,Alg}}, D) ->
- fmt("~nALGORITHMS ~p~n~s~n", [Pid, wr_record(Alg)], D);
+ fmt("~n~p ALGORITHMS~n~s~n", [Pid, wr_record(Alg)], D);
+
+
+msg_formater({trace,Pid,send,{tcp,Sock,Bytes},Pid}, D) ->
+ fmt("~n~p TCP SEND on ~p~n ~p~n", [Pid,Sock, shrink_bin(Bytes)], D);
+
+msg_formater({trace,Pid,send,{tcp,Sock,Bytes},Dest}, D) ->
+ fmt("~n~p TCP SEND from ~p TO ~p~n ~p~n", [Pid,Sock,Dest, shrink_bin(Bytes)], D);
msg_formater({trace,Pid,send,ErlangMsg,Dest}, D) ->
- fmt("~nERL MSG ~p SEND TO ~p~n ~p~n", [Pid,Dest, shrink_bin(ErlangMsg)], D);
+ fmt("~n~p ERL MSG SEND TO ~p~n ~p~n", [Pid,Dest, shrink_bin(ErlangMsg)], D);
+
+
+msg_formater({trace,Pid,'receive',{tcp,Sock,Bytes}}, D) ->
+ fmt("~n~p TCP RECEIVE on ~p~n ~p~n", [Pid,Sock,shrink_bin(Bytes)], D);
msg_formater({trace,Pid,'receive',ErlangMsg}, D) ->
- fmt("~nERL MSG ~p RECIEVE~n ~p~n", [Pid,shrink_bin(ErlangMsg)], D);
+ fmt("~n~p ERL MSG RECEIVE~n ~p~n", [Pid,shrink_bin(ErlangMsg)], D);
+
msg_formater(M, D) ->
fmt("~nDBG ~n~p~n", [shrink_bin(M)], D).
@@ -87,8 +104,10 @@ fmt(Fmt, Args, D=#data{writer=Write,acc=Acc}) ->
D#data{acc = Write(io_lib:format(Fmt, Args), Acc)}.
%%%----------------------------------------------------------------
-setup_tracer(Write) ->
- Handler = fun msg_formater/2,
+setup_tracer(Write, MangleArg) ->
+ Handler = fun(Arg, D) ->
+ msg_formater(MangleArg(Arg), D)
+ end,
InitialData = #data{writer = Write},
{ok,_} = dbg:tracer(process, {Handler, InitialData}),
ok.
diff --git a/lib/ssh/src/ssh_io.erl b/lib/ssh/src/ssh_io.erl
index 026d0f6151..1d8f370884 100644
--- a/lib/ssh/src/ssh_io.erl
+++ b/lib/ssh/src/ssh_io.erl
@@ -31,56 +31,55 @@ read_line(Prompt, Ssh) ->
format("~s", [listify(Prompt)]),
proplists:get_value(user_pid, Ssh) ! {self(), question},
receive
- Answer ->
+ Answer when is_list(Answer) ->
Answer
end.
yes_no(Prompt, Ssh) ->
- io:format("~s [y/n]?", [Prompt]),
+ format("~s [y/n]?", [Prompt]),
proplists:get_value(user_pid, Ssh#ssh.opts) ! {self(), question},
receive
- Answer ->
+ %% I can't see that the atoms y and n are ever received, but it must
+ %% be investigated before removing
+ y -> yes;
+ n -> no;
+
+ Answer when is_list(Answer) ->
case trim(Answer) of
"y" -> yes;
"n" -> no;
"Y" -> yes;
"N" -> no;
- y -> yes;
- n -> no;
_ ->
- io:format("please answer y or n\n"),
+ format("please answer y or n\n",[]),
yes_no(Prompt, Ssh)
end
end.
-read_password(Prompt, Ssh) ->
+read_password(Prompt, #ssh{opts=Opts}) -> read_password(Prompt, Opts);
+read_password(Prompt, Opts) when is_list(Opts) ->
format("~s", [listify(Prompt)]),
- case is_list(Ssh) of
- false ->
- proplists:get_value(user_pid, Ssh#ssh.opts) ! {self(), user_password};
- _ ->
- proplists:get_value(user_pid, Ssh) ! {self(), user_password}
- end,
+ proplists:get_value(user_pid, Opts) ! {self(), user_password},
receive
- Answer ->
- case Answer of
- "" ->
- read_password(Prompt, Ssh);
- Pass -> Pass
- end
+ Answer when is_list(Answer) ->
+ case trim(Answer) of
+ "" ->
+ read_password(Prompt, Opts);
+ Pwd ->
+ Pwd
+ end
end.
-listify(A) when is_atom(A) ->
- atom_to_list(A);
-listify(L) when is_list(L) ->
- L;
-listify(B) when is_binary(B) ->
- binary_to_list(B).
format(Fmt, Args) ->
io:format(Fmt, Args).
+%%%================================================================
+listify(A) when is_atom(A) -> atom_to_list(A);
+listify(L) when is_list(L) -> L;
+listify(B) when is_binary(B) -> binary_to_list(B).
+
trim(Line) when is_list(Line) ->
lists:reverse(trim1(lists:reverse(trim1(Line))));
@@ -93,6 +92,3 @@ trim1([$\r|Cs]) -> trim(Cs);
trim1([$\n|Cs]) -> trim(Cs);
trim1([$\t|Cs]) -> trim(Cs);
trim1(Cs) -> Cs.
-
-
-
diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl
index 733414e23a..d52d453007 100644
--- a/lib/ssh/test/ssh_basic_SUITE.erl
+++ b/lib/ssh/test/ssh_basic_SUITE.erl
@@ -50,7 +50,12 @@
inet6_option/1,
inet_option/1,
internal_error/1,
- known_hosts/1,
+ known_hosts/1,
+ login_bad_pwd_no_retry1/1,
+ login_bad_pwd_no_retry2/1,
+ login_bad_pwd_no_retry3/1,
+ login_bad_pwd_no_retry4/1,
+ login_bad_pwd_no_retry5/1,
misc_ssh_options/1,
openssh_zlib_basic_test/1,
packet_size_zero/1,
@@ -100,7 +105,8 @@ all() ->
daemon_opt_fd,
multi_daemon_opt_fd,
packet_size_zero,
- ssh_info_print
+ ssh_info_print,
+ {group, login_bad_pwd_no_retry}
].
groups() ->
@@ -116,7 +122,13 @@ groups() ->
{dsa_pass_key, [], [pass_phrase]},
{rsa_pass_key, [], [pass_phrase]},
{key_cb, [], [key_callback, key_callback_options]},
- {internal_error, [], [internal_error]}
+ {internal_error, [], [internal_error]},
+ {login_bad_pwd_no_retry, [], [login_bad_pwd_no_retry1,
+ login_bad_pwd_no_retry2,
+ login_bad_pwd_no_retry3,
+ login_bad_pwd_no_retry4,
+ login_bad_pwd_no_retry5
+ ]}
].
@@ -1090,6 +1102,72 @@ ssh_info_print(Config) ->
%%--------------------------------------------------------------------
+%% Check that a basd pwd is not tried more times. Could cause lock-out
+%% on server
+
+login_bad_pwd_no_retry1(Config) ->
+ login_bad_pwd_no_retry(Config, "keyboard-interactive,password").
+
+login_bad_pwd_no_retry2(Config) ->
+ login_bad_pwd_no_retry(Config, "password,keyboard-interactive").
+
+login_bad_pwd_no_retry3(Config) ->
+ login_bad_pwd_no_retry(Config, "password,publickey,keyboard-interactive").
+
+login_bad_pwd_no_retry4(Config) ->
+ login_bad_pwd_no_retry(Config, "password,other,keyboard-interactive").
+
+login_bad_pwd_no_retry5(Config) ->
+ login_bad_pwd_no_retry(Config, "password,other,keyboard-interactive,password,password").
+
+
+
+
+
+login_bad_pwd_no_retry(Config, AuthMethods) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
+ file:make_dir(UserDir),
+ SysDir = proplists:get_value(data_dir, Config),
+
+ Parent = self(),
+ PwdFun = fun(_, _, _, undefined) -> {false, 1};
+ (_, _, _, _) -> Parent ! retry_bad_pwd,
+ false
+ end,
+
+ {DaemonRef, _Host, Port} =
+ ssh_test_lib:daemon([{system_dir, SysDir},
+ {user_dir, UserDir},
+ {auth_methods, AuthMethods},
+ {user_passwords, [{"foo","somepwd"}]},
+ {pwdfun, PwdFun}
+ ]),
+
+ ConnRes = ssh:connect("localhost", Port,
+ [{silently_accept_hosts, true},
+ {user, "foo"},
+ {password, "badpwd"},
+ {user_dir, UserDir},
+ {user_interaction, false}]),
+
+ receive
+ retry_bad_pwd ->
+ ssh:stop_daemon(DaemonRef),
+ {fail, "Retry bad password"}
+ after 0 ->
+ case ConnRes of
+ {error,"Unable to connect using the available authentication methods"} ->
+ ssh:stop_daemon(DaemonRef),
+ ok;
+ {ok,Conn} ->
+ ssh:close(Conn),
+ ssh:stop_daemon(DaemonRef),
+ {fail, "Connect erroneosly succeded"}
+ end
+ end.
+
+%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
%% Due to timing the error message may or may not be delivered to
diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk
index b165928877..575c1af3a9 100644
--- a/lib/ssh/vsn.mk
+++ b/lib/ssh/vsn.mk
@@ -1,5 +1,5 @@
#-*-makefile-*- ; force emacs to enter makefile-mode
-SSH_VSN = 4.3
+SSH_VSN = 4.3.1
APP_VSN = "ssh-$(SSH_VSN)"
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml
index 3b6f988a2d..9d68ee0eee 100644
--- a/lib/ssl/doc/src/notes.xml
+++ b/lib/ssl/doc/src/notes.xml
@@ -28,6 +28,26 @@
<p>This document describes the changes made to the SSL application.</p>
+<section><title>SSL 8.0.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The TLS/SSL protocol version selection for the SSL server
+ has been corrected to follow RFC 5246 Appendix E.1
+ especially in case where the list of supported versions
+ has gaps. Now the server selects the highest protocol
+ version it supports that is not higher than what the
+ client supports.</p>
+ <p>
+ Own Id: OTP-13753 Aux Id: seq13150 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>SSL 8.0</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src
index 11728128c4..fadc67ef80 100644
--- a/lib/ssl/src/ssl.appup.src
+++ b/lib/ssl/src/ssl.appup.src
@@ -1,14 +1,20 @@
%% -*- erlang -*-
{"%VSN%",
[
- {<<"7\\..*">>, [{restart_application, ssl}]},
+ {<<"^8[.]0$">>,
+ [{load_module, ssl_handshake, soft_purge, soft_purge, []}
+ ]},
+ {<<"^7[.][^.].*">>, [{restart_application, ssl}]},
{<<"6\\..*">>, [{restart_application, ssl}]},
{<<"5\\..*">>, [{restart_application, ssl}]},
{<<"4\\..*">>, [{restart_application, ssl}]},
{<<"3\\..*">>, [{restart_application, ssl}]}
],
[
- {<<"7\\..*">>, [{restart_application, ssl}]},
+ {<<"^8[.]0$">>,
+ [{load_module, ssl_handshake, soft_purge, soft_purge, []}
+ ]},
+ {<<"^7[.][^.].*">>, [{restart_application, ssl}]},
{<<"6\\..*">>, [{restart_application, ssl}]},
{<<"5\\..*">>, [{restart_application, ssl}]},
{<<"4\\..*">>, [{restart_application, ssl}]},
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index 53282998d0..adee59393e 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -528,13 +528,12 @@ certify(internal, #server_key_exchange{exchange_keys = Keys},
end
end;
-certify(internal, #certificate_request{hashsign_algorithms = HashSigns},
+certify(internal, #certificate_request{} = CertRequest,
#state{session = #session{own_certificate = Cert},
- key_algorithm = KeyExAlg,
+ role = client,
ssl_options = #ssl_options{signature_algs = SupportedHashSigns},
negotiated_version = Version} = State0, Connection) ->
-
- case ssl_handshake:select_hashsign(HashSigns, Cert, KeyExAlg, SupportedHashSigns, Version) of
+ case ssl_handshake:select_hashsign(CertRequest, Cert, SupportedHashSigns, Version) of
#alert {} = Alert ->
Connection:handle_own_alert(Alert, Version, certify, State0);
NegotiatedHashSign ->
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index 9c3fe9d73b..081efda768 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -74,7 +74,7 @@
]).
%% MISC
--export([select_version/3, prf/6, select_hashsign/5,
+-export([select_version/3, prf/6, select_hashsign/4, select_hashsign/5,
select_hashsign_algs/3,
premaster_secret/2, premaster_secret/3, premaster_secret/4]).
@@ -581,7 +581,7 @@ prf({3,_N}, PRFAlgo, Secret, Label, Seed, WantedLength) ->
{atom(), atom()} | undefined | #alert{}.
%%
-%% Description: Handles signature_algorithms extension
+%% Description: Handles signature_algorithms hello extension (server)
%%--------------------------------------------------------------------
select_hashsign(_, undefined, _, _, _Version) ->
{null, anon};
@@ -593,14 +593,17 @@ select_hashsign(HashSigns, Cert, KeyExAlgo,
select_hashsign(#hash_sign_algos{hash_sign_algos = HashSigns}, Cert, KeyExAlgo, SupportedHashSigns,
{Major, Minor}) when Major >= 3 andalso Minor >= 3 ->
#'OTPCertificate'{tbsCertificate = TBSCert} = public_key:pkix_decode_cert(Cert, otp),
- #'OTPSubjectPublicKeyInfo'{algorithm = {_,Algo, _}} = TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo,
- Sign = cert_sign(Algo),
- case lists:filter(fun({sha, dsa = S}) when S == Sign ->
- true;
- ({_, dsa}) ->
- false;
- ({_, _} = Algos) ->
- is_acceptable_hash_sign(Algos, Sign, KeyExAlgo, SupportedHashSigns);
+ #'OTPCertificate'{tbsCertificate = TBSCert,
+ signatureAlgorithm = {_,SignAlgo, _}} = public_key:pkix_decode_cert(Cert, otp),
+ #'OTPSubjectPublicKeyInfo'{algorithm = {_, SubjAlgo, _}} =
+ TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo,
+
+ Sign = sign_algo(SignAlgo),
+ SubSing = sign_algo(SubjAlgo),
+
+ case lists:filter(fun({_, S} = Algos) when S == Sign ->
+ is_acceptable_hash_sign(Algos, Sign,
+ SubSing, KeyExAlgo, SupportedHashSigns);
(_) ->
false
end, HashSigns) of
@@ -613,6 +616,49 @@ select_hashsign(_, Cert, _, _, Version) ->
#'OTPCertificate'{tbsCertificate = TBSCert} = public_key:pkix_decode_cert(Cert, otp),
#'OTPSubjectPublicKeyInfo'{algorithm = {_,Algo, _}} = TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo,
select_hashsign_algs(undefined, Algo, Version).
+%%--------------------------------------------------------------------
+-spec select_hashsign(#certificate_request{}, binary(),
+ [atom()], ssl_record:ssl_version()) ->
+ {atom(), atom()} | #alert{}.
+
+%%
+%% Description: Handles signature algorithms selection for certificate requests (client)
+%%--------------------------------------------------------------------
+select_hashsign(#certificate_request{}, undefined, _, {Major, Minor}) when Major >= 3 andalso Minor >= 3->
+ %% There client does not have a certificate and will send an empty reply, the server may fail
+ %% or accept the connection by its own preference. No signature algorihms needed as there is
+ %% no certificate to verify.
+ {undefined, undefined};
+
+select_hashsign(#certificate_request{hashsign_algorithms = #hash_sign_algos{hash_sign_algos = HashSigns},
+ certificate_types = Types}, Cert, SupportedHashSigns,
+ {Major, Minor}) when Major >= 3 andalso Minor >= 3->
+ #'OTPCertificate'{tbsCertificate = TBSCert} = public_key:pkix_decode_cert(Cert, otp),
+ #'OTPCertificate'{tbsCertificate = TBSCert,
+ signatureAlgorithm = {_,SignAlgo, _}} = public_key:pkix_decode_cert(Cert, otp),
+ #'OTPSubjectPublicKeyInfo'{algorithm = {_, SubjAlgo, _}} =
+ TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo,
+
+ Sign = sign_algo(SignAlgo),
+ SubSign = sign_algo(SubjAlgo),
+
+ case is_acceptable_cert_type(SubSign, HashSigns, Types) andalso is_supported_sign(Sign, HashSigns) of
+ true ->
+ case lists:filter(fun({_, S} = Algos) when S == SubSign ->
+ is_acceptable_hash_sign(Algos, SupportedHashSigns);
+ (_) ->
+ false
+ end, HashSigns) of
+ [] ->
+ ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_signature_algorithm);
+ [HashSign | _] ->
+ HashSign
+ end;
+ false ->
+ ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_signature_algorithm)
+ end;
+select_hashsign(#certificate_request{}, Cert, _, Version) ->
+ select_hashsign(undefined, Cert, undefined, [], Version).
%%--------------------------------------------------------------------
-spec select_hashsign_algs({atom(), atom()}| undefined, oid(), ssl_record:ssl_version()) ->
@@ -648,6 +694,7 @@ select_hashsign_algs(undefined, ?rsaEncryption, _) ->
select_hashsign_algs(undefined, ?'id-dsa', _) ->
{sha, dsa}.
+
%%--------------------------------------------------------------------
-spec master_secret(atom(), ssl_record:ssl_version(), #session{} | binary(), #connection_states{},
client | server) -> {binary(), #connection_states{}} | #alert{}.
@@ -1143,11 +1190,13 @@ certificate_types(_, {N, M}) when N >= 3 andalso M >= 3 ->
end;
certificate_types({KeyExchange, _, _, _}, _) when KeyExchange == rsa;
+ KeyExchange == dh_rsa;
KeyExchange == dhe_rsa;
KeyExchange == ecdhe_rsa ->
<<?BYTE(?RSA_SIGN)>>;
-certificate_types({KeyExchange, _, _, _}, _) when KeyExchange == dhe_dss;
+certificate_types({KeyExchange, _, _, _}, _) when KeyExchange == dh_dss;
+ KeyExchange == dhe_dss;
KeyExchange == srp_dss ->
<<?BYTE(?DSS_SIGN)>>;
@@ -1256,8 +1305,40 @@ handle_server_hello_extensions(RecordCB, Random, CipherSuite, Compression,
end.
select_version(RecordCB, ClientVersion, Versions) ->
- ServerVersion = RecordCB:highest_protocol_version(Versions),
- RecordCB:lowest_protocol_version(ClientVersion, ServerVersion).
+ do_select_version(RecordCB, ClientVersion, Versions).
+
+do_select_version(_, ClientVersion, []) ->
+ ClientVersion;
+do_select_version(RecordCB, ClientVersion, [Version | Versions]) ->
+ case RecordCB:is_higher(Version, ClientVersion) of
+ true ->
+ %% Version too high for client - keep looking
+ do_select_version(RecordCB, ClientVersion, Versions);
+ false ->
+ %% Version ok for client - look for a higher
+ do_select_version(RecordCB, ClientVersion, Versions, Version)
+ end.
+%%
+do_select_version(_, _, [], GoodVersion) ->
+ GoodVersion;
+do_select_version(
+ RecordCB, ClientVersion, [Version | Versions], GoodVersion) ->
+ BetterVersion =
+ case RecordCB:is_higher(Version, ClientVersion) of
+ true ->
+ %% Version too high for client
+ GoodVersion;
+ false ->
+ %% Version ok for client
+ case RecordCB:is_higher(Version, GoodVersion) of
+ true ->
+ %% Use higher version
+ Version;
+ false ->
+ GoodVersion
+ end
+ end,
+ do_select_version(RecordCB, ClientVersion, Versions, BetterVersion).
renegotiation_info(_, client, _, false) ->
#renegotiation_info{renegotiated_connection = undefined};
@@ -2164,27 +2245,73 @@ distpoints_lookup([DistPoint | Rest], Issuer, Callback, CRLDbHandle) ->
[{DistPoint, {CRL, public_key:der_decode('CertificateList', CRL)}} || CRL <- CRLs]
end.
-cert_sign(?rsaEncryption) ->
+sign_algo(?rsaEncryption) ->
rsa;
-cert_sign(?'id-ecPublicKey') ->
+sign_algo(?'id-ecPublicKey') ->
ecdsa;
-cert_sign(?'id-dsa') ->
+sign_algo(?'id-dsa') ->
dsa;
-cert_sign(Alg) ->
+sign_algo(Alg) ->
{_, Sign} =public_key:pkix_sign_types(Alg),
Sign.
-is_acceptable_hash_sign({_, Sign} = Algos, Sign, _, SupportedHashSigns) ->
- is_acceptable_hash_sign(Algos, SupportedHashSigns);
-is_acceptable_hash_sign(Algos,_, KeyExAlgo, SupportedHashSigns) when KeyExAlgo == dh_ecdsa;
- KeyExAlgo == ecdh_rsa;
- KeyExAlgo == ecdh_ecdsa ->
+is_acceptable_hash_sign(Algos, _, _, KeyExAlgo, SupportedHashSigns) when
+ KeyExAlgo == dh_dss;
+ KeyExAlgo == dh_rsa;
+ KeyExAlgo == dh_ecdsa ->
+ %% dh_* could be called only dh in TLS-1.2
+ is_acceptable_hash_sign(Algos, SupportedHashSigns);
+is_acceptable_hash_sign(Algos, rsa, ecdsa, ecdh_rsa, SupportedHashSigns) ->
+ is_acceptable_hash_sign(Algos, SupportedHashSigns);
+is_acceptable_hash_sign({_, rsa} = Algos, rsa, _, dhe_rsa, SupportedHashSigns) ->
+ is_acceptable_hash_sign(Algos, SupportedHashSigns);
+is_acceptable_hash_sign({_, rsa} = Algos, rsa, rsa, ecdhe_rsa, SupportedHashSigns) ->
+ is_acceptable_hash_sign(Algos, SupportedHashSigns);
+is_acceptable_hash_sign({_, rsa} = Algos, rsa, rsa, rsa, SupportedHashSigns) ->
+ is_acceptable_hash_sign(Algos, SupportedHashSigns);
+is_acceptable_hash_sign({_, rsa} = Algos, rsa, _, srp_rsa, SupportedHashSigns) ->
is_acceptable_hash_sign(Algos, SupportedHashSigns);
-is_acceptable_hash_sign(_,_,_,_) ->
- false.
+is_acceptable_hash_sign({_, rsa} = Algos, rsa, _, rsa_psk, SupportedHashSigns) ->
+ is_acceptable_hash_sign(Algos, SupportedHashSigns);
+is_acceptable_hash_sign({_, dsa} = Algos, dsa, _, dhe_dss, SupportedHashSigns) ->
+ is_acceptable_hash_sign(Algos, SupportedHashSigns);
+is_acceptable_hash_sign({_, dsa} = Algos, dsa, _, srp_dss, SupportedHashSigns) ->
+ is_acceptable_hash_sign(Algos, SupportedHashSigns);
+is_acceptable_hash_sign({_, ecdsa} = Algos, ecdsa, _, dhe_ecdsa, SupportedHashSigns) ->
+ is_acceptable_hash_sign(Algos, SupportedHashSigns);
+is_acceptable_hash_sign({_, ecdsa} = Algos, ecdsa, ecdsa, ecdhe_ecdsa, SupportedHashSigns) ->
+ is_acceptable_hash_sign(Algos, SupportedHashSigns);
+is_acceptable_hash_sign(_, _, _, KeyExAlgo, _) when
+ KeyExAlgo == psk;
+ KeyExAlgo == dhe_psk;
+ KeyExAlgo == srp_anon;
+ KeyExAlgo == dh_anon;
+ KeyExAlgo == ecdhe_anon
+ ->
+ true;
+is_acceptable_hash_sign(_,_, _,_,_) ->
+ false.
+
is_acceptable_hash_sign(Algos, SupportedHashSigns) ->
lists:member(Algos, SupportedHashSigns).
+is_acceptable_cert_type(Sign, _HashSigns, Types) ->
+ lists:member(sign_type(Sign), binary_to_list(Types)).
+
+is_supported_sign(Sign, HashSigns) ->
+ [] =/= lists:dropwhile(fun({_, S}) when S =/= Sign ->
+ true;
+ (_)->
+ false
+ end, HashSigns).
+sign_type(rsa) ->
+ ?RSA_SIGN;
+sign_type(dsa) ->
+ ?DSS_SIGN;
+sign_type(ecdsa) ->
+ ?ECDSA_SIGN.
+
+
bad_key(#'DSAPrivateKey'{}) ->
unacceptable_dsa_key;
bad_key(#'RSAPrivateKey'{}) ->
diff --git a/lib/ssl/src/ssl_tls_dist_proxy.erl b/lib/ssl/src/ssl_tls_dist_proxy.erl
index a920f54ed2..08947f24dd 100644
--- a/lib/ssl/src/ssl_tls_dist_proxy.erl
+++ b/lib/ssl/src/ssl_tls_dist_proxy.erl
@@ -117,7 +117,7 @@ handle_call({listen, Driver, Name}, _From, State) ->
{ok, WorldTcpAddress} = get_tcp_address(World),
{_,Port} = WorldTcpAddress#net_address.address,
ErlEpmd = net_kernel:epmd_module(),
- case ErlEpmd:register_node(Name, Port) of
+ case ErlEpmd:register_node(Name, Port, Driver) of
{ok, Creation} ->
{reply, {ok, {Socket, TcpAddress, Creation}},
State#state{listen={Socket, World}}};
diff --git a/lib/ssl/test/ssl_ECC_SUITE.erl b/lib/ssl/test/ssl_ECC_SUITE.erl
index b8a03f578d..69ac9908fa 100644
--- a/lib/ssl/test/ssl_ECC_SUITE.erl
+++ b/lib/ssl/test/ssl_ECC_SUITE.erl
@@ -159,42 +159,42 @@ end_per_testcase(_TestCase, Config) ->
client_ecdh_server_ecdh(Config) when is_list(Config) ->
COpts = proplists:get_value(client_ecdh_rsa_opts, Config),
- SOpts = proplists:get_value(server_ecdh_rsa_verify_opts, Config),
+ SOpts = proplists:get_value(server_ecdh_rsa_opts, Config),
basic_test(COpts, SOpts, Config).
client_ecdh_server_rsa(Config) when is_list(Config) ->
COpts = proplists:get_value(client_ecdh_rsa_opts, Config),
- SOpts = proplists:get_value(server_ecdh_rsa_verify_opts, Config),
+ SOpts = proplists:get_value(server_opts, Config),
basic_test(COpts, SOpts, Config).
client_rsa_server_ecdh(Config) when is_list(Config) ->
- COpts = proplists:get_value(client_ecdh_rsa_opts, Config),
- SOpts = proplists:get_value(server_ecdh_rsa_verify_opts, Config),
+ COpts = proplists:get_value(client_opts, Config),
+ SOpts = proplists:get_value(server_ecdh_rsa_opts, Config),
basic_test(COpts, SOpts, Config).
client_rsa_server_rsa(Config) when is_list(Config) ->
- COpts = proplists:get_value(client_verification_opts, Config),
- SOpts = proplists:get_value(server_verification_opts, Config),
+ COpts = proplists:get_value(client_opts, Config),
+ SOpts = proplists:get_value(server_opts, Config),
basic_test(COpts, SOpts, Config).
client_ecdsa_server_ecdsa(Config) when is_list(Config) ->
COpts = proplists:get_value(client_ecdsa_opts, Config),
- SOpts = proplists:get_value(server_ecdsa_verify_opts, Config),
+ SOpts = proplists:get_value(server_ecdsa_opts, Config),
basic_test(COpts, SOpts, Config).
client_ecdsa_server_rsa(Config) when is_list(Config) ->
COpts = proplists:get_value(client_ecdsa_opts, Config),
- SOpts = proplists:get_value(server_ecdsa_verify_opts, Config),
+ SOpts = proplists:get_value(server_opts, Config),
basic_test(COpts, SOpts, Config).
client_rsa_server_ecdsa(Config) when is_list(Config) ->
- COpts = proplists:get_value(client_ecdsa_opts, Config),
- SOpts = proplists:get_value(server_ecdsa_verify_opts, Config),
+ COpts = proplists:get_value(client_opts, Config),
+ SOpts = proplists:get_value(server_ecdsa_opts, Config),
basic_test(COpts, SOpts, Config).
client_ecdsa_server_ecdsa_with_raw_key(Config) when is_list(Config) ->
COpts = proplists:get_value(client_ecdsa_opts, Config),
- SOpts = proplists:get_value(server_ecdsa_verify_opts, Config),
+ SOpts = proplists:get_value(server_ecdsa_opts, Config),
ServerCert = proplists:get_value(certfile, SOpts),
ServerKeyFile = proplists:get_value(keyfile, SOpts),
{ok, PemBin} = file:read_file(ServerKeyFile),
@@ -244,20 +244,20 @@ basic_test(ClientCert, ClientKey, ClientCA, ServerCert, ServerKey, ServerCA, Con
check_result(Server, SType, Client, CType),
close(Server, Client).
-start_client(openssl, Port, CA, OwnCa, Cert, Key, Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- NewCA = new_ca(filename:join(PrivDir, "new_ca.pem"), CA, OwnCa),
+start_client(openssl, Port, PeerCA, OwnCa, Cert, Key, _Config) ->
+ CA = new_openssl_ca("openssl_client_ca", PeerCA, OwnCa),
Version = tls_record:protocol_version(tls_record:highest_protocol_version([])),
Exe = "openssl",
Args = ["s_client", "-verify", "2", "-port", integer_to_list(Port),
ssl_test_lib:version_flag(Version),
- "-cert", Cert, "-CAfile", NewCA,
+ "-cert", Cert, "-CAfile", CA,
"-key", Key, "-host","localhost", "-msg", "-debug"],
OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
true = port_command(OpenSslPort, "Hello world"),
OpenSslPort;
-start_client(erlang, Port, CA, _, Cert, Key, Config) ->
+start_client(erlang, Port, PeerCA, OwnCa, Cert, Key, Config) ->
+ CA = new_ca("erlang_client_ca", PeerCA, OwnCa),
{ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
{host, Hostname},
@@ -267,20 +267,19 @@ start_client(erlang, Port, CA, _, Cert, Key, Config) ->
{cacertfile, CA},
{certfile, Cert}, {keyfile, Key}]}]).
-start_server(openssl, CA, OwnCa, Cert, Key, Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- NewCA = new_ca(filename:join(PrivDir, "new_ca.pem"), CA, OwnCa),
-
+start_server(openssl, PeerCA, OwnCa, Cert, Key, _Config) ->
+ CA = new_openssl_ca("openssl_server_ca", PeerCA, OwnCa),
Port = ssl_test_lib:inet_port(node()),
Version = tls_record:protocol_version(tls_record:highest_protocol_version([])),
Exe = "openssl",
Args = ["s_server", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version),
- "-verify", "2", "-cert", Cert, "-CAfile", NewCA,
+ "-verify", "2", "-cert", Cert, "-CAfile", CA,
"-key", Key, "-msg", "-debug"],
OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
true = port_command(OpenSslPort, "Hello world"),
{OpenSslPort, Port};
-start_server(erlang, CA, _, Cert, Key, Config) ->
+start_server(erlang, PeerCA, OwnCa, Cert, Key, Config) ->
+ CA = new_ca("erlang_server_ca", PeerCA, OwnCa),
{_, ServerNode, _} = ssl_test_lib:run_where(Config),
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
{from, self()},
@@ -291,7 +290,8 @@ start_server(erlang, CA, _, Cert, Key, Config) ->
[{verify, verify_peer}, {cacertfile, CA},
{certfile, Cert}, {keyfile, Key}]}]),
{Server, ssl_test_lib:inet_port(Server)}.
-start_server_with_raw_key(erlang, CA, _, Cert, Key, Config) ->
+start_server_with_raw_key(erlang, PeerCA, OwnCa, Cert, Key, Config) ->
+ CA = new_ca("erlang_server_ca", PeerCA, OwnCa),
{_, ServerNode, _} = ssl_test_lib:run_where(Config),
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
{from, self()},
@@ -336,17 +336,27 @@ close(Client, Server) ->
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
-%% Work around OpenSSL bug, apparently the same bug as we had fixed in
-%% 11629690ba61f8e0c93ef9b2b6102fd279825977
new_ca(FileName, CA, OwnCa) ->
{ok, P1} = file:read_file(CA),
E1 = public_key:pem_decode(P1),
{ok, P2} = file:read_file(OwnCa),
E2 = public_key:pem_decode(P2),
+ Pem = public_key:pem_encode(E1 ++E2),
+ file:write_file(FileName, Pem),
+ FileName.
+
+new_openssl_ca(FileName, CA, OwnCa) ->
+ {ok, P1} = file:read_file(CA),
+ E1 = public_key:pem_decode(P1),
+ {ok, P2} = file:read_file(OwnCa),
+ E2 = public_key:pem_decode(P2),
case os:cmd("openssl version") of
"OpenSSL 1.0.1p-freebsd" ++ _ ->
Pem = public_key:pem_encode(E1 ++E2),
file:write_file(FileName, Pem);
+ "LibreSSL" ++ _ ->
+ Pem = public_key:pem_encode(E1 ++E2),
+ file:write_file(FileName, Pem);
_ ->
Pem = public_key:pem_encode(E2 ++E1),
file:write_file(FileName, Pem)
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index efa5faa218..665dbb1df3 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -408,8 +408,13 @@ init_per_testcase(TestCase, Config) when TestCase == tls_ssl_accept_timeout;
ssl_test_lib:ct_log_supported_protocol_versions(Config),
ct:timetrap({seconds, 15}),
Config;
-init_per_testcase(clear_pem_cache, Config) ->
+init_per_testcase(TestCase, Config) when TestCase == clear_pem_cache;
+ TestCase == der_input;
+ TestCase == defaults ->
ssl_test_lib:ct_log_supported_protocol_versions(Config),
+ %% White box test need clean start
+ ssl:stop(),
+ ssl:start(),
ct:timetrap({seconds, 20}),
Config;
init_per_testcase(raw_ssl_option, Config) ->
@@ -567,8 +572,8 @@ prf(Config) when is_list(Config) ->
connection_info() ->
[{doc,"Test the API function ssl:connection_information/1"}].
connection_info(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
@@ -1144,8 +1149,8 @@ cipher_suites_mix() ->
cipher_suites_mix(Config) when is_list(Config) ->
CipherSuites = [{ecdh_rsa,aes_128_cbc,sha256,sha256}, {rsa,aes_128_cbc,sha}],
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
@@ -4409,14 +4414,14 @@ run_suites(Ciphers, Version, Config, Type) ->
{ClientOpts, ServerOpts} =
case Type of
rsa ->
- {ssl_test_lib:ssl_options(client_opts, Config),
- ssl_test_lib:ssl_options(server_opts, Config)};
+ {ssl_test_lib:ssl_options(client_verification_opts, Config),
+ ssl_test_lib:ssl_options(server_verification_opts, Config)};
dsa ->
- {ssl_test_lib:ssl_options(client_opts, Config),
+ {ssl_test_lib:ssl_options(client_verification_opts, Config),
ssl_test_lib:ssl_options(server_dsa_opts, Config)};
anonymous ->
%% No certs in opts!
- {ssl_test_lib:ssl_options(client_opts, Config),
+ {ssl_test_lib:ssl_options(client_verification_opts, Config),
ssl_test_lib:ssl_options(server_anon, Config)};
psk ->
{ssl_test_lib:ssl_options(client_psk, Config),
@@ -4440,31 +4445,31 @@ run_suites(Ciphers, Version, Config, Type) ->
{ssl_test_lib:ssl_options(client_srp_dsa, Config),
ssl_test_lib:ssl_options(server_srp_dsa, Config)};
ecdsa ->
- {ssl_test_lib:ssl_options(client_opts, Config),
+ {ssl_test_lib:ssl_options(client_verification_opts, Config),
ssl_test_lib:ssl_options(server_ecdsa_opts, Config)};
ecdh_rsa ->
- {ssl_test_lib:ssl_options(client_opts, Config),
+ {ssl_test_lib:ssl_options(client_verification_opts, Config),
ssl_test_lib:ssl_options(server_ecdh_rsa_opts, Config)};
rc4_rsa ->
- {ssl_test_lib:ssl_options(client_opts, Config),
+ {ssl_test_lib:ssl_options(client_verification_opts, Config),
[{ciphers, Ciphers} |
- ssl_test_lib:ssl_options(server_opts, Config)]};
+ ssl_test_lib:ssl_options(server_verification_opts, Config)]};
rc4_ecdh_rsa ->
- {ssl_test_lib:ssl_options(client_opts, Config),
+ {ssl_test_lib:ssl_options(client_verification_opts, Config),
[{ciphers, Ciphers} |
ssl_test_lib:ssl_options(server_ecdh_rsa_opts, Config)]};
rc4_ecdsa ->
- {ssl_test_lib:ssl_options(client_opts, Config),
+ {ssl_test_lib:ssl_options(client_verification_opts, Config),
[{ciphers, Ciphers} |
ssl_test_lib:ssl_options(server_ecdsa_opts, Config)]};
des_dhe_rsa ->
- {ssl_test_lib:ssl_options(client_opts, Config),
+ {ssl_test_lib:ssl_options(client_verification_opts, Config),
[{ciphers, Ciphers} |
- ssl_test_lib:ssl_options(server_opts, Config)]};
+ ssl_test_lib:ssl_options(server_verification_opts, Config)]};
des_rsa ->
- {ssl_test_lib:ssl_options(client_opts, Config),
+ {ssl_test_lib:ssl_options(client_verification_opts, Config),
[{ciphers, Ciphers} |
- ssl_test_lib:ssl_options(server_opts, Config)]}
+ ssl_test_lib:ssl_options(server_verification_opts, Config)]}
end,
Result = lists:map(fun(Cipher) ->
diff --git a/lib/ssl/test/ssl_certificate_verify_SUITE.erl b/lib/ssl/test/ssl_certificate_verify_SUITE.erl
index 20165c70f0..c83c513eb3 100644
--- a/lib/ssl/test/ssl_certificate_verify_SUITE.erl
+++ b/lib/ssl/test/ssl_certificate_verify_SUITE.erl
@@ -65,9 +65,10 @@ tests() ->
cert_expired,
invalid_signature_client,
invalid_signature_server,
- extended_key_usage_verify_peer,
- extended_key_usage_verify_none,
- critical_extension_verify_peer,
+ extended_key_usage_verify_client,
+ extended_key_usage_verify_server,
+ critical_extension_verify_client,
+ critical_extension_verify_server,
critical_extension_verify_none].
error_handling_tests()->
@@ -122,6 +123,8 @@ init_per_testcase(TestCase, Config) when TestCase == cert_expired;
ssl:clear_pem_cache(),
init_per_testcase(common, Config);
init_per_testcase(_TestCase, Config) ->
+ ssl:stop(),
+ ssl:start(),
ssl_test_lib:ct_log_supported_protocol_versions(Config),
ct:timetrap({seconds, 5}),
Config.
@@ -136,7 +139,7 @@ end_per_testcase(_TestCase, Config) ->
verify_peer() ->
[{doc,"Test option verify_peer"}].
verify_peer(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
Active = proplists:get_value(active, Config),
ReceiveFunction = proplists:get_value(receive_function, Config),
@@ -190,7 +193,7 @@ server_verify_client_once() ->
[{doc,"Test server option verify_client_once"}].
server_verify_client_once(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_opts, []),
ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
Active = proplists:get_value(active, Config),
ReceiveFunction = proplists:get_value(receive_function, Config),
@@ -230,7 +233,7 @@ server_require_peer_cert_ok() ->
server_require_peer_cert_ok(Config) when is_list(Config) ->
ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}
| ssl_test_lib:ssl_options(server_verification_opts, Config)],
- ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
Active = proplists:get_value(active, Config),
ReceiveFunction = proplists:get_value(receive_function, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
@@ -259,7 +262,7 @@ server_require_peer_cert_fail() ->
server_require_peer_cert_fail(Config) when is_list(Config) ->
ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}
| ssl_test_lib:ssl_options(server_verification_opts, Config)],
- BadClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
+ BadClientOpts = ssl_test_lib:ssl_options(client_opts, []),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
@@ -290,7 +293,7 @@ server_require_peer_cert_partial_chain() ->
server_require_peer_cert_partial_chain(Config) when is_list(Config) ->
ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}
| ssl_test_lib:ssl_options(server_verification_opts, Config)],
- ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
{ok, ClientCAs} = file:read_file(proplists:get_value(cacertfile, ClientOpts)),
@@ -325,13 +328,13 @@ server_require_peer_cert_allow_partial_chain() ->
server_require_peer_cert_allow_partial_chain(Config) when is_list(Config) ->
ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}
| ssl_test_lib:ssl_options(server_verification_opts, Config)],
- ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Active = proplists:get_value(active, Config),
ReceiveFunction = proplists:get_value(receive_function, Config),
- {ok, ServerCAs} = file:read_file(proplists:get_value(cacertfile, ServerOpts)),
- [{_,_,_}, {_, IntermidiateCA, _}] = public_key:pem_decode(ServerCAs),
+ {ok, ClientCAs} = file:read_file(proplists:get_value(cacertfile, ClientOpts)),
+ [{_,_,_}, {_, IntermidiateCA, _}] = public_key:pem_decode(ClientCAs),
PartialChain = fun(CertChain) ->
case lists:member(IntermidiateCA, CertChain) of
@@ -367,7 +370,7 @@ server_require_peer_cert_do_not_allow_partial_chain() ->
server_require_peer_cert_do_not_allow_partial_chain(Config) when is_list(Config) ->
ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}
| ssl_test_lib:ssl_options(server_verification_opts, Config)],
- ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
{ok, ServerCAs} = file:read_file(proplists:get_value(cacertfile, ServerOpts)),
@@ -408,7 +411,7 @@ server_require_peer_cert_partial_chain_fun_fail() ->
server_require_peer_cert_partial_chain_fun_fail(Config) when is_list(Config) ->
ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}
| ssl_test_lib:ssl_options(server_verification_opts, Config)],
- ClientOpts = proplists:get_value(client_verification_opts, Config),
+ ClientOpts = proplists:get_value(client_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
{ok, ServerCAs} = file:read_file(proplists:get_value(cacertfile, ServerOpts)),
@@ -448,7 +451,7 @@ verify_fun_always_run_client() ->
verify_fun_always_run_client(Config) when is_list(Config) ->
ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
{from, self()},
@@ -492,7 +495,7 @@ verify_fun_always_run_client(Config) when is_list(Config) ->
verify_fun_always_run_server() ->
[{doc,"Verify that user verify_fun is always run (for valid and valid_peer not only unknown_extension)"}].
verify_fun_always_run_server(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
@@ -524,9 +527,7 @@ verify_fun_always_run_server(Config) when is_list(Config) ->
{from, self()},
{mfa, {ssl_test_lib,
no_result, []}},
- {options,
- [{verify, verify_peer}
- | ClientOpts]}]),
+ {options, ClientOpts}]),
%% Client error may be {tls_alert, "handshake failure" } or closed depending on timing
%% this is not a bug it is a circumstance of how tcp works!
@@ -544,7 +545,7 @@ cert_expired() ->
cert_expired(Config) when is_list(Config) ->
ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
PrivDir = proplists:get_value(priv_dir, Config),
KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"),
@@ -607,11 +608,11 @@ two_digits_str(N) ->
lists:flatten(io_lib:format("~p", [N])).
%%--------------------------------------------------------------------
-extended_key_usage_verify_peer() ->
- [{doc,"Test cert that has a critical extended_key_usage extension in verify_peer mode"}].
+extended_key_usage_verify_server() ->
+ [{doc,"Test cert that has a critical extended_key_usage extension in verify_peer mode for server"}].
-extended_key_usage_verify_peer(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config),
+extended_key_usage_verify_server(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
PrivDir = proplists:get_value(priv_dir, Config),
Active = proplists:get_value(active, Config),
@@ -660,7 +661,7 @@ extended_key_usage_verify_peer(Config) when is_list(Config) ->
{host, Hostname},
{from, self()},
{mfa, {ssl_test_lib, ReceiveFunction, []}},
- {options, [{verify, verify_peer}, {active, Active} |
+ {options, [{verify, verify_none}, {active, Active} |
NewClientOpts]}]),
ssl_test_lib:check_result(Server, ok, Client, ok),
@@ -669,12 +670,12 @@ extended_key_usage_verify_peer(Config) when is_list(Config) ->
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
-extended_key_usage_verify_none() ->
- [{doc,"Test cert that has a critical extended_key_usage extension in verify_none mode"}].
+extended_key_usage_verify_client() ->
+ [{doc,"Test cert that has a critical extended_key_usage extension in client verify_peer mode"}].
-extended_key_usage_verify_none(Config) when is_list(Config) ->
+extended_key_usage_verify_client(Config) when is_list(Config) ->
ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
PrivDir = proplists:get_value(priv_dir, Config),
Active = proplists:get_value(active, Config),
ReceiveFunction = proplists:get_value(receive_function, Config),
@@ -730,11 +731,11 @@ extended_key_usage_verify_none(Config) when is_list(Config) ->
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
-critical_extension_verify_peer() ->
+critical_extension_verify_server() ->
[{doc,"Test cert that has a critical unknown extension in verify_peer mode"}].
-critical_extension_verify_peer(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config),
+critical_extension_verify_server(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
PrivDir = proplists:get_value(priv_dir, Config),
Active = proplists:get_value(active, Config),
@@ -766,7 +767,7 @@ critical_extension_verify_peer(Config) when is_list(Config) ->
{host, Hostname},
{from, self()},
{mfa, {ssl_test_lib, ReceiveFunction, []}},
- {options, [{verify, verify_peer}, {active, Active} | NewClientOpts]}]),
+ {options, [{verify, verify_none}, {active, Active} | NewClientOpts]}]),
%% This certificate has a critical extension that we don't
%% understand. Therefore, verification should fail.
@@ -775,14 +776,60 @@ critical_extension_verify_peer(Config) when is_list(Config) ->
ssl_test_lib:close(Server),
ok.
+%%--------------------------------------------------------------------
+
+critical_extension_verify_client() ->
+ [{doc,"Test cert that has a critical unknown extension in verify_peer mode"}].
+
+critical_extension_verify_client(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
+ Active = proplists:get_value(active, Config),
+ ReceiveFunction = proplists:get_value(receive_function, Config),
+
+ KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"),
+ NewCertName = integer_to_list(erlang:unique_integer()) ++ ".pem",
+
+ ServerCertFile = proplists:get_value(certfile, ServerOpts),
+ NewServerCertFile = filename:join([PrivDir, "server", NewCertName]),
+ add_critical_netscape_cert_type(ServerCertFile, NewServerCertFile, KeyFile),
+ NewServerOpts = [{certfile, NewServerCertFile} | proplists:delete(certfile, ServerOpts)],
+
+ ClientCertFile = proplists:get_value(certfile, ClientOpts),
+ NewClientCertFile = filename:join([PrivDir, "client", NewCertName]),
+ add_critical_netscape_cert_type(ClientCertFile, NewClientCertFile, KeyFile),
+ NewClientOpts = [{certfile, NewClientCertFile} | proplists:delete(certfile, ClientOpts)],
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server_error(
+ [{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, ReceiveFunction, []}},
+ {options, [{verify, verify_none}, {active, Active} | NewServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client_error(
+ [{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, ReceiveFunction, []}},
+ {options, [{verify, verify_peer}, {active, Active} | NewClientOpts]}]),
+
+ %% This certificate has a critical extension that we don't
+ %% understand. Therefore, verification should fail.
+ tcp_delivery_workaround(Server, {error, {tls_alert, "unsupported certificate"}},
+ Client, {error, {tls_alert, "unsupported certificate"}}),
+ ssl_test_lib:close(Server),
+ ok.
%%--------------------------------------------------------------------
critical_extension_verify_none() ->
[{doc,"Test cert that has a critical unknown extension in verify_none mode"}].
critical_extension_verify_none(Config) when is_list(Config) ->
ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
PrivDir = proplists:get_value(priv_dir, Config),
Active = proplists:get_value(active, Config),
ReceiveFunction = proplists:get_value(receive_function, Config),
@@ -1070,7 +1117,7 @@ client_with_cert_cipher_suites_handshake(Config) when is_list(Config) ->
server_verify_no_cacerts() ->
[{doc,"Test server must have cacerts if it wants to verify client"}].
server_verify_no_cacerts(Config) when is_list(Config) ->
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
+ ServerOpts = proplists:delete(cacertfile, ssl_test_lib:ssl_options(server_opts, Config)),
{_, ServerNode, _} = ssl_test_lib:run_where(Config),
Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
{from, self()},
@@ -1084,7 +1131,7 @@ server_verify_no_cacerts(Config) when is_list(Config) ->
unknown_server_ca_fail() ->
[{doc,"Test that the client fails if the ca is unknown in verify_peer mode"}].
unknown_server_ca_fail(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_opts, []),
ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
@@ -1128,7 +1175,7 @@ unknown_server_ca_fail(Config) when is_list(Config) ->
unknown_server_ca_accept_verify_none() ->
[{doc,"Test that the client succeds if the ca is unknown in verify_none mode"}].
unknown_server_ca_accept_verify_none(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_opts, []),
ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
@@ -1153,7 +1200,7 @@ unknown_server_ca_accept_verify_peer() ->
[{doc, "Test that the client succeds if the ca is unknown in verify_peer mode"
" with a verify_fun that accepts the unknown ca error"}].
unknown_server_ca_accept_verify_peer(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
+ ClientOpts =ssl_test_lib:ssl_options(client_opts, []),
ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
@@ -1192,7 +1239,7 @@ unknown_server_ca_accept_verify_peer(Config) when is_list(Config) ->
unknown_server_ca_accept_backwardscompatibility() ->
[{doc,"Test that old style verify_funs will work"}].
unknown_server_ca_accept_backwardscompatibility(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_opts, []),
ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
diff --git a/lib/ssl/test/ssl_dist_SUITE.erl b/lib/ssl/test/ssl_dist_SUITE.erl
index 5ebf9bb2de..8740e8c8f0 100644
--- a/lib/ssl/test/ssl_dist_SUITE.erl
+++ b/lib/ssl/test/ssl_dist_SUITE.erl
@@ -109,11 +109,11 @@ common_end(_, _Config) ->
basic() ->
[{doc,"Test that two nodes can connect via ssl distribution"}].
basic(Config) when is_list(Config) ->
- NH1 = start_ssl_node(Config),
+ gen_dist_test(basic_test, Config).
+
+basic_test(NH1, NH2, _) ->
Node1 = NH1#node_handle.nodename,
- NH2 = start_ssl_node(Config),
Node2 = NH2#node_handle.nodename,
-
pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end),
[Node2] = apply_on_ssl_node(NH1, fun () -> nodes() end),
@@ -161,18 +161,16 @@ basic(Config) when is_list(Config) ->
ok
end
end)
- end,
- stop_ssl_node(NH1),
- stop_ssl_node(NH2),
- success(Config).
+ end.
%%--------------------------------------------------------------------
payload() ->
[{doc,"Test that send a lot of data between the ssl distributed noes"}].
payload(Config) when is_list(Config) ->
- NH1 = start_ssl_node(Config),
+ gen_dist_test(payload_test, Config).
+
+payload_test(NH1, NH2, _) ->
Node1 = NH1#node_handle.nodename,
- NH2 = start_ssl_node(Config),
Node2 = NH2#node_handle.nodename,
pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end),
@@ -204,10 +202,8 @@ payload(Config) when is_list(Config) ->
ok
end
end)
- end,
- stop_ssl_node(NH1),
- stop_ssl_node(NH2),
- success(Config).
+ end.
+
%%--------------------------------------------------------------------
plain_options() ->
[{doc,"Test specifying additional options"}].
@@ -218,20 +214,17 @@ plain_options(Config) when is_list(Config) ->
"client_verify verify_none server_verify verify_none "
"server_depth 1 client_depth 1 "
"server_hibernate_after 500 client_hibernate_after 500",
+ gen_dist_test(plain_options_test, [{additional_dist_opts, DistOpts} | Config]).
- NH1 = start_ssl_node([{additional_dist_opts, DistOpts} | Config]),
+plain_options_test(NH1, NH2, _) ->
Node1 = NH1#node_handle.nodename,
- NH2 = start_ssl_node([{additional_dist_opts, DistOpts} | Config]),
Node2 = NH2#node_handle.nodename,
pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end),
[Node2] = apply_on_ssl_node(NH1, fun () -> nodes() end),
- [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end),
+ [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end).
- stop_ssl_node(NH1),
- stop_ssl_node(NH2),
- success(Config).
%%--------------------------------------------------------------------
plain_verify_options() ->
[{doc,"Test specifying additional options"}].
@@ -240,20 +233,18 @@ plain_verify_options(Config) when is_list(Config) ->
"client_secure_renegotiate true "
"server_reuse_sessions true client_reuse_sessions true "
"server_hibernate_after 500 client_hibernate_after 500",
+ gen_dist_test(plain_verify_options_test, [{additional_dist_opts, DistOpts} | Config]).
- NH1 = start_ssl_node([{additional_dist_opts, DistOpts} | Config]),
+plain_verify_options_test(NH1, NH2, _) ->
Node1 = NH1#node_handle.nodename,
- NH2 = start_ssl_node([{additional_dist_opts, DistOpts} | Config]),
Node2 = NH2#node_handle.nodename,
-
+
pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end),
-
+
[Node2] = apply_on_ssl_node(NH1, fun () -> nodes() end),
- [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end),
+ [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end).
+
- stop_ssl_node(NH1),
- stop_ssl_node(NH2),
- success(Config).
%%--------------------------------------------------------------------
nodelay_option() ->
[{doc,"Test specifying dist_nodelay option"}].
@@ -265,6 +256,7 @@ nodelay_option(Config) ->
after
application:unset_env(kernel, dist_nodelay)
end.
+%%--------------------------------------------------------------------
listen_port_options() ->
[{doc, "Test specifying listening ports"}].
@@ -285,32 +277,39 @@ listen_port_options(Config) when is_list(Config) ->
#node_handle{} ->
%% If the node was able to start, it didn't take the port
%% option into account.
+ stop_ssl_node(NH1),
exit(unexpected_success)
catch
exit:{accept_failed, timeout} ->
%% The node failed to start, as expected.
ok
end,
-
+
%% Try again, now specifying a high max port.
PortOpt2 = "-kernel inet_dist_listen_min " ++ integer_to_list(Port1) ++
- " inet_dist_listen_max 65535",
+ " inet_dist_listen_max 65535",
NH2 = start_ssl_node([{additional_dist_opts, PortOpt2} | Config]),
- Node2 = NH2#node_handle.nodename,
- Name2 = lists:takewhile(fun(C) -> C =/= $@ end, atom_to_list(Node2)),
- {ok, NodesPorts2} = apply_on_ssl_node(NH2, fun net_adm:names/0),
- {Name2, Port2} = lists:keyfind(Name2, 1, NodesPorts2),
-
- %% The new port should be higher:
- if Port2 > Port1 ->
- ok;
- true ->
- error({port, Port2, not_higher_than, Port1})
+
+ try
+ Node2 = NH2#node_handle.nodename,
+ Name2 = lists:takewhile(fun(C) -> C =/= $@ end, atom_to_list(Node2)),
+ {ok, NodesPorts2} = apply_on_ssl_node(NH2, fun net_adm:names/0),
+ {Name2, Port2} = lists:keyfind(Name2, 1, NodesPorts2),
+
+ %% The new port should be higher:
+ if Port2 > Port1 ->
+ ok;
+ true ->
+ error({port, Port2, not_higher_than, Port1})
+ end
+ catch
+ _:Reason ->
+ stop_ssl_node(NH2),
+ ct:fail(Reason)
end,
-
- stop_ssl_node(NH1),
stop_ssl_node(NH2),
success(Config).
+
%%--------------------------------------------------------------------
listen_options() ->
[{doc, "Test inet_dist_listen_options"}].
@@ -329,28 +328,25 @@ do_listen_options(Prio, Config) ->
end,
Options = "-kernel inet_dist_listen_options " ++ PriorityString,
-
- NH1 = start_ssl_node([{additional_dist_opts, Options} | Config]),
- NH2 = start_ssl_node([{additional_dist_opts, Options} | Config]),
- Node2 = NH2#node_handle.nodename,
-
+ gen_dist_test(listen_options_test, [{prio, Prio}, {additional_dist_opts, Options} | Config]).
+
+listen_options_test(NH1, NH2, Config) ->
+ Prio = proplists:get_value(prio, Config),
+ Node2 = NH2#node_handle.nodename,
pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end),
PrioritiesNode1 =
apply_on_ssl_node(NH1, fun get_socket_priorities/0),
PrioritiesNode2 =
apply_on_ssl_node(NH2, fun get_socket_priorities/0),
-
+
Elevated1 = [P || P <- PrioritiesNode1, P =:= Prio],
- ?t:format("Elevated1: ~p~n", [Elevated1]),
+ ct:pal("Elevated1: ~p~n", [Elevated1]),
Elevated2 = [P || P <- PrioritiesNode2, P =:= Prio],
- ?t:format("Elevated2: ~p~n", [Elevated2]),
+ ct:pal("Elevated2: ~p~n", [Elevated2]),
[_|_] = Elevated1,
- [_|_] = Elevated2,
+ [_|_] = Elevated2.
- stop_ssl_node(NH1),
- stop_ssl_node(NH2),
- success(Config).
%%--------------------------------------------------------------------
connect_options() ->
[{doc, "Test inet_dist_connect_options"}].
@@ -369,9 +365,11 @@ do_connect_options(Prio, Config) ->
end,
Options = "-kernel inet_dist_connect_options " ++ PriorityString,
+ gen_dist_test(connect_options_test,
+ [{prio, Prio}, {additional_dist_opts, Options} | Config]).
- NH1 = start_ssl_node([{additional_dist_opts, Options} | Config]),
- NH2 = start_ssl_node([{additional_dist_opts, Options} | Config]),
+connect_options_test(NH1, NH2, Config) ->
+ Prio = proplists:get_value(prio, Config),
Node2 = NH2#node_handle.nodename,
pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end),
@@ -382,17 +380,14 @@ do_connect_options(Prio, Config) ->
apply_on_ssl_node(NH2, fun get_socket_priorities/0),
Elevated1 = [P || P <- PrioritiesNode1, P =:= Prio],
- ?t:format("Elevated1: ~p~n", [Elevated1]),
+ ct:pal("Elevated1: ~p~n", [Elevated1]),
Elevated2 = [P || P <- PrioritiesNode2, P =:= Prio],
- ?t:format("Elevated2: ~p~n", [Elevated2]),
+ ct:pal("Elevated2: ~p~n", [Elevated2]),
%% Node 1 will have a socket with elevated priority.
[_|_] = Elevated1,
%% Node 2 will not, since it only applies to outbound connections.
- [] = Elevated2,
+ [] = Elevated2.
- stop_ssl_node(NH1),
- stop_ssl_node(NH2),
- success(Config).
%%--------------------------------------------------------------------
use_interface() ->
[{doc, "Test inet_dist_use_interface"}].
@@ -403,22 +398,28 @@ use_interface(Config) when is_list(Config) ->
%% Start a node, and get the port number it's listening on.
NH1 = start_ssl_node([{additional_dist_opts, Options} | Config]),
- Node1 = NH1#node_handle.nodename,
- Name = lists:takewhile(fun(C) -> C =/= $@ end, atom_to_list(Node1)),
- {ok, NodesPorts} = apply_on_ssl_node(NH1, fun net_adm:names/0),
- {Name, Port} = lists:keyfind(Name, 1, NodesPorts),
-
- %% Now find the socket listening on that port, and check its sockname.
- Sockets = apply_on_ssl_node(
- NH1,
- fun() ->
- [inet:sockname(P) ||
- P <- inet_ports(),
- {ok, Port} =:= (catch inet:port(P))]
- end),
- %% And check that it's actually listening on localhost.
- [{ok,{{127,0,0,1},Port}}] = Sockets,
-
+
+ try
+ Node1 = NH1#node_handle.nodename,
+ Name = lists:takewhile(fun(C) -> C =/= $@ end, atom_to_list(Node1)),
+ {ok, NodesPorts} = apply_on_ssl_node(NH1, fun net_adm:names/0),
+ {Name, Port} = lists:keyfind(Name, 1, NodesPorts),
+
+ %% Now find the socket listening on that port, and check its sockname.
+ Sockets = apply_on_ssl_node(
+ NH1,
+ fun() ->
+ [inet:sockname(P) ||
+ P <- inet_ports(),
+ {ok, Port} =:= (catch inet:port(P))]
+ end),
+ %% And check that it's actually listening on localhost.
+ [{ok,{{127,0,0,1},Port}}] = Sockets
+ catch
+ _:Reason ->
+ stop_ssl_node(NH1),
+ ct:fail(Reason)
+ end,
stop_ssl_node(NH1),
success(Config).
%%--------------------------------------------------------------------
@@ -430,11 +431,11 @@ verify_fun_fail(Config) when is_list(Config) ->
"\"{ssl_dist_SUITE,verify_fail_always,{}}\" "
"client_verify verify_peer client_verify_fun "
"\"{ssl_dist_SUITE,verify_fail_always,{}}\" ",
+ gen_dist_test(verify_fun_fail_test, [{additional_dist_opts, DistOpts} | Config]).
- NH1 = start_ssl_node([{additional_dist_opts, DistOpts} | Config]),
- NH2 = start_ssl_node([{additional_dist_opts, DistOpts} | Config]),
+verify_fun_fail_test(NH1, NH2, _) ->
Node2 = NH2#node_handle.nodename,
-
+
pang = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end),
[] = apply_on_ssl_node(NH1, fun () -> nodes() end),
@@ -446,25 +447,9 @@ verify_fun_fail(Config) when is_list(Config) ->
%% On the server node, it wouldn't run, because the server didn't
%% request a certificate from the client.
undefined =
- apply_on_ssl_node(NH2, fun () -> ets:info(verify_fun_ran) end),
+ apply_on_ssl_node(NH2, fun () -> ets:info(verify_fun_ran) end).
- stop_ssl_node(NH1),
- stop_ssl_node(NH2),
- success(Config).
-verify_fail_always(_Certificate, _Event, _State) ->
- %% Create an ETS table, to record the fact that the verify function ran.
- %% Spawn a new process, to avoid the ETS table disappearing.
- Parent = self(),
- spawn(
- fun() ->
- ets:new(verify_fun_ran, [public, named_table]),
- ets:insert(verify_fun_ran, {verify_fail_always_ran, true}),
- Parent ! go_ahead,
- timer:sleep(infinity)
- end),
- receive go_ahead -> ok end,
- {fail, bad_certificate}.
%%--------------------------------------------------------------------
verify_fun_pass() ->
@@ -476,10 +461,10 @@ verify_fun_pass(Config) when is_list(Config) ->
"server_fail_if_no_peer_cert true "
"client_verify verify_peer client_verify_fun "
"\"{ssl_dist_SUITE,verify_pass_always,{}}\" ",
+ gen_dist_test(verify_fun_pass_test, [{additional_dist_opts, DistOpts} | Config]).
- NH1 = start_ssl_node([{additional_dist_opts, DistOpts} | Config]),
+verify_fun_pass_test(NH1, NH2, _) ->
Node1 = NH1#node_handle.nodename,
- NH2 = start_ssl_node([{additional_dist_opts, DistOpts} | Config]),
Node2 = NH2#node_handle.nodename,
pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end),
@@ -494,25 +479,8 @@ verify_fun_pass(Config) when is_list(Config) ->
%% requested and verified the client's certificate because we
%% passed fail_if_no_peer_cert.
[{verify_pass_always_ran, true}] =
- apply_on_ssl_node(NH2, fun () -> ets:tab2list(verify_fun_ran) end),
+ apply_on_ssl_node(NH2, fun () -> ets:tab2list(verify_fun_ran) end).
- stop_ssl_node(NH1),
- stop_ssl_node(NH2),
- success(Config).
-
-verify_pass_always(_Certificate, _Event, State) ->
- %% Create an ETS table, to record the fact that the verify function ran.
- %% Spawn a new process, to avoid the ETS table disappearing.
- Parent = self(),
- spawn(
- fun() ->
- ets:new(verify_fun_ran, [public, named_table]),
- ets:insert(verify_fun_ran, {verify_pass_always_ran, true}),
- Parent ! go_ahead,
- timer:sleep(infinity)
- end),
- receive go_ahead -> ok end,
- {valid, State}.
%%--------------------------------------------------------------------
crl_check_pass() ->
[{doc,"Test crl_check with non-revoked certificate"}].
@@ -520,10 +488,10 @@ crl_check_pass(Config) when is_list(Config) ->
DistOpts = "-ssl_dist_opt client_crl_check true",
NewConfig =
[{many_verify_opts, true}, {additional_dist_opts, DistOpts}] ++ Config,
+ gen_dist_test(crl_check_pass_test, NewConfig).
- NH1 = start_ssl_node(NewConfig),
+crl_check_pass_test(NH1, NH2, Config) ->
Node1 = NH1#node_handle.nodename,
- NH2 = start_ssl_node(NewConfig),
Node2 = NH2#node_handle.nodename,
PrivDir = ?config(priv_dir, Config),
@@ -533,11 +501,7 @@ crl_check_pass(Config) when is_list(Config) ->
pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end),
[Node2] = apply_on_ssl_node(NH1, fun () -> nodes() end),
- [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end),
-
- stop_ssl_node(NH1),
- stop_ssl_node(NH2),
- success(Config).
+ [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end).
%%--------------------------------------------------------------------
crl_check_fail() ->
@@ -549,10 +513,9 @@ crl_check_fail(Config) when is_list(Config) ->
%% The server uses a revoked certificate.
{server_cert_dir, "revoked"},
{additional_dist_opts, DistOpts}] ++ Config,
+ gen_dist_test(crl_check_fail_test, NewConfig).
- NH1 = start_ssl_node(NewConfig),
- %%Node1 = NH1#node_handle.nodename,
- NH2 = start_ssl_node(NewConfig),
+crl_check_fail_test(NH1, NH2, Config) ->
Node2 = NH2#node_handle.nodename,
PrivDir = ?config(priv_dir, Config),
@@ -562,11 +525,7 @@ crl_check_fail(Config) when is_list(Config) ->
pang = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end),
[] = apply_on_ssl_node(NH1, fun () -> nodes() end),
- [] = apply_on_ssl_node(NH2, fun () -> nodes() end),
-
- stop_ssl_node(NH1),
- stop_ssl_node(NH2),
- success(Config).
+ [] = apply_on_ssl_node(NH2, fun () -> nodes() end).
%%--------------------------------------------------------------------
crl_check_best_effort() ->
@@ -576,22 +535,18 @@ crl_check_best_effort(Config) when is_list(Config) ->
"server_verify verify_peer server_crl_check best_effort",
NewConfig =
[{many_verify_opts, true}, {additional_dist_opts, DistOpts}] ++ Config,
+ gen_dist_test(crl_check_best_effort_test, NewConfig).
+crl_check_best_effort_test(NH1, NH2, _Config) ->
%% We don't have the correct CRL at hand, but since crl_check is
%% best_effort, we accept it anyway.
- NH1 = start_ssl_node(NewConfig),
Node1 = NH1#node_handle.nodename,
- NH2 = start_ssl_node(NewConfig),
Node2 = NH2#node_handle.nodename,
pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end),
[Node2] = apply_on_ssl_node(NH1, fun () -> nodes() end),
- [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end),
-
- stop_ssl_node(NH1),
- stop_ssl_node(NH2),
- success(Config).
+ [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end).
%%--------------------------------------------------------------------
crl_cache_check_pass() ->
@@ -605,20 +560,16 @@ crl_cache_check_pass(Config) when is_list(Config) ->
"\"{ssl_dist_SUITE,{\\\"" ++ NodeDir ++ "\\\",[]}}\"",
NewConfig =
[{many_verify_opts, true}, {additional_dist_opts, DistOpts}] ++ Config,
+ gen_dist_test(crl_cache_check_pass_test, NewConfig).
- NH1 = start_ssl_node(NewConfig),
+crl_cache_check_pass_test(NH1, NH2, _) ->
Node1 = NH1#node_handle.nodename,
- NH2 = start_ssl_node(NewConfig),
Node2 = NH2#node_handle.nodename,
pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end),
[Node2] = apply_on_ssl_node(NH1, fun () -> nodes() end),
- [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end),
-
- stop_ssl_node(NH1),
- stop_ssl_node(NH2),
- success(Config).
+ [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end).
%%--------------------------------------------------------------------
crl_cache_check_fail() ->
@@ -636,44 +587,31 @@ crl_cache_check_fail(Config) when is_list(Config) ->
{server_cert_dir, "revoked"},
{additional_dist_opts, DistOpts}] ++ Config,
- NH1 = start_ssl_node(NewConfig),
- NH2 = start_ssl_node(NewConfig),
- Node2 = NH2#node_handle.nodename,
+ gen_dist_test(crl_cache_check_fail_test, NewConfig).
+crl_cache_check_fail_test(NH1, NH2, _) ->
+ Node2 = NH2#node_handle.nodename,
pang = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end),
[] = apply_on_ssl_node(NH1, fun () -> nodes() end),
- [] = apply_on_ssl_node(NH2, fun () -> nodes() end),
-
- stop_ssl_node(NH1),
- stop_ssl_node(NH2),
- success(Config).
-
-%% ssl_crl_cache_api callbacks
-lookup(_DistributionPoint, _DbHandle) ->
- not_available.
-
-select({rdnSequence, NameParts}, {NodeDir, _}) ->
- %% Extract the CN from the issuer name...
- [CN] = [CN ||
- [#'AttributeTypeAndValue'{
- type = ?'id-at-commonName',
- value = <<_, _, CN/binary>>}] <- NameParts],
- %% ...and use that as the directory name to find the CRL.
- error_logger:info_report([{found_cn, CN}]),
- CRLFile = filename:join([NodeDir, CN, "crl.pem"]),
- {ok, PemBin} = file:read_file(CRLFile),
- PemEntries = public_key:pem_decode(PemBin),
- CRLs = [ CRL || {'CertificateList', CRL, not_encrypted}
- <- PemEntries],
- CRLs.
-
-fresh_crl(_DistributionPoint, CRL) ->
- CRL.
-
+ [] = apply_on_ssl_node(NH2, fun () -> nodes() end).
%%--------------------------------------------------------------------
%%% Internal functions -----------------------------------------------
%%--------------------------------------------------------------------
+gen_dist_test(Test, Config) ->
+ NH1 = start_ssl_node(Config),
+ NH2 = start_ssl_node(Config),
+ try
+ ?MODULE:Test(NH1, NH2, Config)
+ catch
+ _:Reason ->
+ stop_ssl_node(NH1),
+ stop_ssl_node(NH2),
+ ct:fail(Reason)
+ end,
+ stop_ssl_node(NH1),
+ stop_ssl_node(NH2),
+ success(Config).
%% ssl_node side api
%%
@@ -742,13 +680,15 @@ stop_ssl_node(#node_handle{connection_handler = Handler,
receive
{'DOWN', Mon, process, Handler, Reason} ->
case Reason of
- normal -> ok;
- _ -> exit(Reason)
+ normal ->
+ ok;
+ _ ->
+ ct:pal("Down ~p ~n", [Reason])
end
end;
Error ->
erlang:demonitor(Mon, [flush]),
- exit(Error)
+ ct:pal("Warning ~p ~n", [Error])
end.
start_ssl_node(Config) ->
@@ -1226,3 +1166,53 @@ vsn(App) ->
after
application:stop(ssl)
end.
+
+verify_fail_always(_Certificate, _Event, _State) ->
+ %% Create an ETS table, to record the fact that the verify function ran.
+ %% Spawn a new process, to avoid the ETS table disappearing.
+ Parent = self(),
+ spawn(
+ fun() ->
+ ets:new(verify_fun_ran, [public, named_table]),
+ ets:insert(verify_fun_ran, {verify_fail_always_ran, true}),
+ Parent ! go_ahead,
+ timer:sleep(infinity)
+ end),
+ receive go_ahead -> ok end,
+ {fail, bad_certificate}.
+
+verify_pass_always(_Certificate, _Event, State) ->
+ %% Create an ETS table, to record the fact that the verify function ran.
+ %% Spawn a new process, to avoid the ETS table disappearing.
+ Parent = self(),
+ spawn(
+ fun() ->
+ ets:new(verify_fun_ran, [public, named_table]),
+ ets:insert(verify_fun_ran, {verify_pass_always_ran, true}),
+ Parent ! go_ahead,
+ timer:sleep(infinity)
+ end),
+ receive go_ahead -> ok end,
+ {valid, State}.
+
+%% ssl_crl_cache_api callbacks
+lookup(_DistributionPoint, _DbHandle) ->
+ not_available.
+
+select({rdnSequence, NameParts}, {NodeDir, _}) ->
+ %% Extract the CN from the issuer name...
+ [CN] = [CN ||
+ [#'AttributeTypeAndValue'{
+ type = ?'id-at-commonName',
+ value = <<_, _, CN/binary>>}] <- NameParts],
+ %% ...and use that as the directory name to find the CRL.
+ error_logger:info_report([{found_cn, CN}]),
+ CRLFile = filename:join([NodeDir, CN, "crl.pem"]),
+ {ok, PemBin} = file:read_file(CRLFile),
+ PemEntries = public_key:pem_decode(PemBin),
+ CRLs = [ CRL || {'CertificateList', CRL, not_encrypted}
+ <- PemEntries],
+ CRLs.
+
+fresh_crl(_DistributionPoint, CRL) ->
+ CRL.
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index 27c670cdc2..fd8af5efaa 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -385,7 +385,9 @@ cert_options(Config) ->
SNIServerAKeyFile = filename:join([proplists:get_value(priv_dir, Config), "a.server", "key.pem"]),
SNIServerBCertFile = filename:join([proplists:get_value(priv_dir, Config), "b.server", "cert.pem"]),
SNIServerBKeyFile = filename:join([proplists:get_value(priv_dir, Config), "b.server", "key.pem"]),
- [{client_opts, []},
+ [{client_opts, [{cacertfile, ClientCaCertFile},
+ {certfile, ClientCertFile},
+ {keyfile, ClientKeyFile}]},
{client_verification_opts, [{cacertfile, ServerCaCertFile},
{certfile, ClientCertFile},
{keyfile, ClientKeyFile},
@@ -394,7 +396,7 @@ cert_options(Config) ->
{certfile, ClientCertFileDigitalSignatureOnly},
{keyfile, ClientKeyFile},
{ssl_imp, new}]},
- {server_opts, [{ssl_imp, new},{reuseaddr, true},
+ {server_opts, [{ssl_imp, new},{reuseaddr, true}, {cacertfile, ServerCaCertFile},
{certfile, ServerCertFile}, {keyfile, ServerKeyFile}]},
{server_anon, [{ssl_imp, new},{reuseaddr, true}, {ciphers, anonymous_suites()}]},
{client_psk, [{ssl_imp, new},{reuseaddr, true},
@@ -494,7 +496,7 @@ make_ecdsa_cert(Config) ->
{cacertfile, ServerCaCertFile},
{certfile, ServerCertFile}, {keyfile, ServerKeyFile}]},
{server_ecdsa_verify_opts, [{ssl_imp, new},{reuseaddr, true},
- {cacertfile, ServerCaCertFile},
+ {cacertfile, ClientCaCertFile},
{certfile, ServerCertFile}, {keyfile, ServerKeyFile},
{verify, verify_peer}]},
{client_ecdsa_opts, [{ssl_imp, new},{reuseaddr, true},
@@ -519,7 +521,7 @@ make_ecdh_rsa_cert(Config) ->
{cacertfile, ServerCaCertFile},
{certfile, ServerCertFile}, {keyfile, ServerKeyFile}]},
{server_ecdh_rsa_verify_opts, [{ssl_imp, new},{reuseaddr, true},
- {cacertfile, ServerCaCertFile},
+ {cacertfile, ClientCaCertFile},
{certfile, ServerCertFile}, {keyfile, ServerKeyFile},
{verify, verify_peer}]},
{client_ecdh_rsa_opts, [{ssl_imp, new},{reuseaddr, true},
@@ -815,6 +817,12 @@ rsa_suites(CounterPart) ->
true;
({ecdhe_rsa, _, _}) when ECC == true ->
true;
+ ({rsa, _, _, _}) ->
+ true;
+ ({dhe_rsa, _, _,_}) ->
+ true;
+ ({ecdhe_rsa, _, _,_}) when ECC == true ->
+ true;
(_) ->
false
end,
diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk
index 3b51fa8c6b..6afac59109 100644
--- a/lib/ssl/vsn.mk
+++ b/lib/ssl/vsn.mk
@@ -1 +1 @@
-SSL_VSN = 8.0
+SSL_VSN = 8.0.1
diff --git a/lib/stdlib/doc/src/gen_statem.xml b/lib/stdlib/doc/src/gen_statem.xml
index c57a31fa21..ed44eef912 100644
--- a/lib/stdlib/doc/src/gen_statem.xml
+++ b/lib/stdlib/doc/src/gen_statem.xml
@@ -1262,9 +1262,9 @@ handle_event(_, _, State, Data) ->
<v>&nbsp;&nbsp;Vsn = term()</v>
<v>OldState = NewState = term()</v>
<v>Extra = term()</v>
- <v>Result = {NewCallbackMode,NewState,NewData} | Reason</v>
+ <v>Result = {CallbackMode,NewState,NewData} | Reason</v>
<v>
- NewCallbackMode =
+ CallbackMode =
<seealso marker="#type-callback_mode">callback_mode()</seealso>
</v>
<v>
@@ -1321,11 +1321,19 @@ handle_event(_, _, State, Data) ->
<p>
If successful, the function must return the updated
internal state in an
- <c>{NewCallbackMode,NewState,NewData}</c> tuple.
+ <c>{CallbackMode,NewState,NewData}</c> tuple.
</p>
<p>
- If the function returns <c>Reason</c>, the ongoing
- upgrade fails and rolls back to the old release.</p>
+ If the function returns a failure <c>Reason</c>, the ongoing
+ upgrade fails and rolls back to the old release.
+ Note that <c>Reason</c> can not be a 3-tuple since that
+ will be regarded as a
+ <c>{CallbackMode,NewState,NewData}</c> tuple,
+ and that a tuple matching <c>{ok,_}</c>
+ is an invalid failure <c>Reason</c>.
+ It is recommended to use an atom as <c>Reason</c> since
+ it will be wrapped in an <c>{error,Reason}</c> tuple.
+ </p>
<p>
This function can use
<seealso marker="erts:erlang#throw/1"><c>erlang:throw/1</c></seealso>
diff --git a/lib/stdlib/doc/src/notes.xml b/lib/stdlib/doc/src/notes.xml
index ad2599c5a0..d8fec1147f 100644
--- a/lib/stdlib/doc/src/notes.xml
+++ b/lib/stdlib/doc/src/notes.xml
@@ -31,6 +31,21 @@
</header>
<p>This document describes the changes made to the STDLIB application.</p>
+<section><title>STDLIB 3.0.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Correct a bug regarding typed records in the Erlang
+ shell. The bug was introduced in OTP-19.0. </p>
+ <p>
+ Own Id: OTP-13719 Aux Id: ERL-182 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>STDLIB 3.0</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/stdlib/src/ets.erl b/lib/stdlib/src/ets.erl
index 3f74e01692..20de06fd0b 100644
--- a/lib/stdlib/src/ets.erl
+++ b/lib/stdlib/src/ets.erl
@@ -232,20 +232,20 @@ match(_) ->
match_object(_, _) ->
erlang:nif_error(undef).
--spec match_object(Tab, Pattern, Limit) -> {[Match], Continuation} |
+-spec match_object(Tab, Pattern, Limit) -> {[Object], Continuation} |
'$end_of_table' when
Tab :: tab(),
Pattern :: match_pattern(),
Limit :: pos_integer(),
- Match :: [term()],
+ Object :: tuple(),
Continuation :: continuation().
match_object(_, _, _) ->
erlang:nif_error(undef).
--spec match_object(Continuation) -> {[Match], Continuation} |
+-spec match_object(Continuation) -> {[Object], Continuation} |
'$end_of_table' when
- Match :: [term()],
+ Object :: tuple(),
Continuation :: continuation().
match_object(_) ->
diff --git a/lib/stdlib/src/gen_statem.erl b/lib/stdlib/src/gen_statem.erl
index 23bddafeed..c02e6a1a19 100644
--- a/lib/stdlib/src/gen_statem.erl
+++ b/lib/stdlib/src/gen_statem.erl
@@ -219,9 +219,10 @@
OldState :: state(),
OldData :: data(),
Extra :: term()) ->
- {NewCallbackMode :: callback_mode(),
+ {CallbackMode :: callback_mode(),
NewState :: state(),
- NewData :: data()}.
+ NewData :: data()} |
+ (Reason :: term()).
%% Format the callback module state in some sensible that is
%% often condensed way. For StatusOption =:= 'normal' the perferred
@@ -630,11 +631,11 @@ system_code_change(
Result -> Result
end
of
- {NewCallbackMode,NewState,NewData} ->
- callback_mode(NewCallbackMode) orelse
- error({callback_mode,NewCallbackMode}),
+ {CallbackMode,NewState,NewData} ->
+ callback_mode(CallbackMode) orelse
+ error({callback_mode,CallbackMode}),
{ok,
- S#{callback_mode := NewCallbackMode,
+ S#{callback_mode := CallbackMode,
state := NewState,
data := NewData}};
{ok,_} = Error ->
diff --git a/lib/stdlib/src/shell.erl b/lib/stdlib/src/shell.erl
index 82a3a2be4f..28f37ef8bf 100644
--- a/lib/stdlib/src/shell.erl
+++ b/lib/stdlib/src/shell.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2015. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -768,6 +768,8 @@ used_records({call,_,{atom,_,record_info},[A,{atom,_,Name}]}) ->
{name, Name, A};
used_records({call,Line,{tuple,_,[M,F]},As}) ->
used_records({call,Line,{remote,Line,M,F},As});
+used_records({type,_,record,[{atom,_,Name}|Fs]}) ->
+ {name, Name, Fs};
used_records(T) when is_tuple(T) ->
{expr, tuple_to_list(T)};
used_records(E) ->
diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl
index 8c1c625676..b02d17bdb6 100644
--- a/lib/stdlib/test/ets_SUITE.erl
+++ b/lib/stdlib/test/ets_SUITE.erl
@@ -47,6 +47,7 @@
fixtable_next/1, fixtable_insert/1, rename/1, rename_unnamed/1, evil_rename/1,
update_element/1, update_counter/1, evil_update_counter/1, partly_bound/1, match_heavy/1]).
-export([update_counter_with_default/1]).
+-export([update_counter_table_growth/1]).
-export([member/1]).
-export([memory/1]).
-export([select_fail/1]).
@@ -100,6 +101,7 @@
heavy_lookup_element_do/1, member_do/1, otp_5340_do/1, otp_7665_do/1, meta_wb_do/1,
do_heavy_concurrent/1, tab2file2_do/2, exit_large_table_owner_do/2,
types_do/1, sleeper/0, memory_do/1, update_counter_with_default_do/1,
+ update_counter_table_growth_do/1,
ms_tracee_dummy/1, ms_tracee_dummy/2, ms_tracee_dummy/3, ms_tracee_dummy/4
]).
@@ -137,6 +139,7 @@ all() ->
rename, rename_unnamed, evil_rename, update_element,
update_counter, evil_update_counter,
update_counter_with_default, partly_bound,
+ update_counter_table_growth,
match_heavy, {group, fold}, member, t_delete_object,
t_init_table, t_whitebox, t_delete_all_objects,
t_insert_list, t_test_ms, t_select_delete, t_ets_dets,
@@ -1955,6 +1958,16 @@ update_counter_with_default_do(Opts) ->
ok.
+update_counter_table_growth(_Config) ->
+ repeat_for_opts(update_counter_table_growth_do).
+
+update_counter_table_growth_do(Opts) ->
+ Set = ets_new(b, [set | Opts]),
+ [ets:update_counter(Set, N, {2, 1}, {N, 1}) || N <- lists:seq(1,10000)],
+ OrderedSet = ets_new(b, [ordered_set | Opts]),
+ [ets:update_counter(OrderedSet, N, {2, 1}, {N, 1}) || N <- lists:seq(1,10000)],
+ ok.
+
%% Check that a first-next sequence always works on a fixed table.
fixtable_next(Config) when is_list(Config) ->
repeat_for_opts(fixtable_next_do, [write_concurrency,all_types]).
diff --git a/lib/stdlib/test/shell_SUITE.erl b/lib/stdlib/test/shell_SUITE.erl
index dc82e92876..c409a6949b 100644
--- a/lib/stdlib/test/shell_SUITE.erl
+++ b/lib/stdlib/test/shell_SUITE.erl
@@ -30,7 +30,7 @@
progex_bit_syntax/1, progex_records/1,
progex_lc/1, progex_funs/1,
otp_5990/1, otp_6166/1, otp_6554/1,
- otp_7184/1, otp_7232/1, otp_8393/1, otp_10302/1]).
+ otp_7184/1, otp_7232/1, otp_8393/1, otp_10302/1, otp_13719/1]).
-export([ start_restricted_from_shell/1,
start_restricted_on_command_line/1,restricted_local/1]).
@@ -91,7 +91,7 @@ groups() ->
progex_funs]},
{tickets, [],
[otp_5990, otp_6166, otp_6554, otp_7184,
- otp_7232, otp_8393, otp_10302]}].
+ otp_7232, otp_8393, otp_10302, otp_13719]}].
init_per_suite(Config) ->
Config.
@@ -2810,6 +2810,19 @@ otp_10302(Config) when is_list(Config) ->
test_server:stop_node(Node),
ok.
+otp_13719(Config) when is_list(Config) ->
+ Test = <<"-module(otp_13719).
+ -record(bar, {}).
+ -record(foo, {bar :: #bar{}}).">>,
+ File = filename("otp_13719.erl", Config),
+ Beam = filename("otp_13719.beam", Config),
+ ok = compile_file(Config, File, Test, []),
+ RR = "rr(\"" ++ Beam ++ "\"). #foo{}.",
+ "[bar,foo]\n#foo{bar = undefined}.\n" = t(RR),
+ file:delete(filename("test.beam", Config)),
+ file:delete(File),
+ ok.
+
scan(B) ->
F = fun(Ts) ->
case erl_parse:parse_term(Ts) of
diff --git a/lib/stdlib/vsn.mk b/lib/stdlib/vsn.mk
index 46e3ceac03..41037b8f53 100644
--- a/lib/stdlib/vsn.mk
+++ b/lib/stdlib/vsn.mk
@@ -1 +1 @@
-STDLIB_VSN = 3.0
+STDLIB_VSN = 3.0.1
diff --git a/lib/syntax_tools/doc/specs/.gitignore b/lib/syntax_tools/doc/specs/.gitignore
new file mode 100644
index 0000000000..322eebcb06
--- /dev/null
+++ b/lib/syntax_tools/doc/specs/.gitignore
@@ -0,0 +1 @@
+specs_*.xml
diff --git a/lib/syntax_tools/doc/src/Makefile b/lib/syntax_tools/doc/src/Makefile
index ff4f3f78ff..e55222e59c 100644
--- a/lib/syntax_tools/doc/src/Makefile
+++ b/lib/syntax_tools/doc/src/Makefile
@@ -81,10 +81,15 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
+SPECS_FILES = $(XML_REF3_FILES:%.xml=$(SPECDIR)/specs_%.xml)
+
+TOP_SPECS_FILE = specs.xml
+
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
XML_FLAGS +=
+SPECS_FLAGS = -I../../include
DVIPS_FLAGS +=
# ----------------------------------------------------
@@ -93,7 +98,7 @@ DVIPS_FLAGS +=
$(HTMLDIR)/%.gif: %.gif
$(INSTALL_DATA) $< $@
-docs: pdf html man
+docs: man pdf html
$(TOP_PDF_FILE): $(XML_FILES)
@@ -120,6 +125,7 @@ clean clean_docs:
rm -f $(MAN3DIR)/*
rm -f $(XML_REF3_FILES) $(XML_CHAPTER_FILES) *.html
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
+ rm -f $(SPECDIR)/*
rm -f errs core *~
# ----------------------------------------------------
diff --git a/lib/syntax_tools/doc/src/specs.xml b/lib/syntax_tools/doc/src/specs.xml
new file mode 100644
index 0000000000..04c3a494e7
--- /dev/null
+++ b/lib/syntax_tools/doc/src/specs.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<specs xmlns:xi="http://www.w3.org/2001/XInclude">
+ <xi:include href="../specs/specs_epp_dodger.xml"/>
+ <xi:include href="../specs/specs_erl_comment_scan.xml"/>
+ <xi:include href="../specs/specs_erl_prettypr.xml"/>
+ <xi:include href="../specs/specs_erl_recomment.xml"/>
+ <xi:include href="../specs/specs_erl_syntax.xml"/>
+ <xi:include href="../specs/specs_erl_syntax_lib.xml"/>
+ <xi:include href="../specs/specs_erl_tidy.xml"/>
+ <xi:include href="../specs/specs_igor.xml"/>
+ <xi:include href="../specs/specs_merl.xml"/>
+ <xi:include href="../specs/specs_merl_transform.xml"/>
+ <xi:include href="../specs/specs_prettypr.xml"/>
+</specs>
diff --git a/lib/syntax_tools/src/erl_comment_scan.erl b/lib/syntax_tools/src/erl_comment_scan.erl
index 03429d4d42..b5ac564146 100644
--- a/lib/syntax_tools/src/erl_comment_scan.erl
+++ b/lib/syntax_tools/src/erl_comment_scan.erl
@@ -30,8 +30,14 @@
%% =====================================================================
--type comment() :: {integer(), integer(), integer(), [string()]}.
--type commentLine() :: {integer(), integer(), integer(), string()}.
+-type comment() :: {Line:: integer(),
+ Column:: integer(),
+ Indentation :: integer(),
+ Text :: [string()]}.
+-type commentLine() :: {Line :: integer(),
+ Column :: integer(),
+ Indent :: integer(),
+ Text :: string()}.
%% =====================================================================
%% @spec file(FileName::file:filename()) -> [Comment]
diff --git a/lib/syntax_tools/src/erl_prettypr.erl b/lib/syntax_tools/src/erl_prettypr.erl
index f1615b2610..df0d78031c 100644
--- a/lib/syntax_tools/src/erl_prettypr.erl
+++ b/lib/syntax_tools/src/erl_prettypr.erl
@@ -195,10 +195,16 @@ format(Node) ->
%% =====================================================================
%% @spec format(Tree::syntaxTree(), Options::[term()]) -> string()
-%% syntaxTree() = erl_syntax:syntaxTree()
%%
-%% @type hook() = (syntaxTree(), context(), Continuation) -> document()
-%% Continuation = (syntaxTree(), context()) -> document().
+%% @type syntaxTree() = erl_syntax:syntaxTree().
+%%
+%% An abstract syntax tree. See the {@link erl_syntax} module for
+%% details.
+%%
+%% @type hook() = (syntaxTree(), context(), Continuation) ->
+%% prettypr:document()
+%% Continuation = (syntaxTree(), context()) ->
+%% prettypr:document().
%%
%% A call-back function for user-controlled formatting. See {@link
%% format/2}.
@@ -277,7 +283,7 @@ format(Node, Options) ->
%% =====================================================================
-%% @spec best(Tree::syntaxTree()) -> empty | document()
+%% @spec best(Tree::syntaxTree()) -> empty | prettypr:document()
%% @equiv best(Tree, [])
-spec best(erl_syntax:syntaxTree()) -> 'empty' | prettypr:document().
@@ -288,7 +294,7 @@ best(Node) ->
%% =====================================================================
%% @spec best(Tree::syntaxTree(), Options::[term()]) ->
-%% empty | document()
+%% empty | prettypr:document()
%%
%% @doc Creates a fixed "best" abstract layout for a syntax tree. This
%% is similar to the `layout/2' function, except that here, the final
@@ -310,7 +316,7 @@ best(Node, Options) ->
%% =====================================================================
-%% @spec layout(Tree::syntaxTree()) -> document()
+%% @spec layout(Tree::syntaxTree()) -> prettypr:document()
%% @equiv layout(Tree, [])
-spec layout(erl_syntax:syntaxTree()) -> prettypr:document().
@@ -320,8 +326,7 @@ layout(Node) ->
%% =====================================================================
-%% @spec layout(Tree::syntaxTree(), Options::[term()]) -> document()
-%% document() = prettypr:document()
+%% @spec layout(Tree::syntaxTree(), Options::[term()]) -> prettypr:document()
%%
%% @doc Creates an abstract document layout for a syntax tree. The
%% result represents a set of possible layouts (cf. module `prettypr').
diff --git a/lib/syntax_tools/src/erl_recomment.erl b/lib/syntax_tools/src/erl_recomment.erl
index c1141b2bc6..1d23034991 100644
--- a/lib/syntax_tools/src/erl_recomment.erl
+++ b/lib/syntax_tools/src/erl_recomment.erl
@@ -30,6 +30,9 @@
-export([recomment_forms/2, quick_recomment_forms/2, recomment_tree/2]).
+%% @type syntaxTree() = erl_syntax:syntaxTree(). An abstract syntax
+%% tree. See the {@link erl_syntax} module for details.
+
%% =====================================================================
%% @spec quick_recomment_forms(Forms, Comments::[Comment]) ->
%% syntaxTree()
@@ -55,7 +58,6 @@ quick_recomment_forms(Tree, Cs) ->
%% =====================================================================
%% @spec recomment_forms(Forms, Comments::[Comment]) -> syntaxTree()
%%
-%% syntaxTree() = erl_syntax:syntaxTree()
%% Forms = syntaxTree() | [syntaxTree()]
%% Comment = {Line, Column, Indentation, Text}
%% Line = integer()
diff --git a/lib/syntax_tools/src/erl_syntax_lib.erl b/lib/syntax_tools/src/erl_syntax_lib.erl
index 9815559779..5aecf5d774 100644
--- a/lib/syntax_tools/src/erl_syntax_lib.erl
+++ b/lib/syntax_tools/src/erl_syntax_lib.erl
@@ -280,7 +280,7 @@ mapfoldl(_, S, []) ->
%% =====================================================================
%% @spec variables(syntaxTree()) -> set(atom())
%%
-%% set(T) = //stdlib/sets:set(T)
+%% @type set(T) = //stdlib/sets:set(T)
%%
%% @doc Returns the names of variables occurring in a syntax tree, The
%% result is a set of variable names represented by atoms. Macro names
@@ -1955,7 +1955,7 @@ analyze_application(Node) ->
%% =====================================================================
-%% @spec analyze_type_application(Node::syntaxTree()) -> typeName()
+%% @spec analyze_type_application(Node::syntaxTree()) -> TypeName
%%
%% TypeName = {atom(), integer()}
%% | {ModuleName, {atom(), integer()}}
diff --git a/lib/syntax_tools/src/erl_tidy.erl b/lib/syntax_tools/src/erl_tidy.erl
index f2de12b410..5d3fc6f062 100644
--- a/lib/syntax_tools/src/erl_tidy.erl
+++ b/lib/syntax_tools/src/erl_tidy.erl
@@ -36,6 +36,11 @@
%% been reasonably well tested, but the possibility of errors remains.
%% Keep backups of your original code safely stored, until you feel
%% confident that the new, modified code can be trusted.
+%%
+%% @type syntaxTree() = erl_syntax:syntaxTree(). An abstract syntax
+%% tree. See the {@link erl_syntax} module for details.
+%%
+%% @type filename() = file:filename().
-module(erl_tidy).
@@ -79,7 +84,6 @@ dir(Dir) ->
%% =====================================================================
%% @spec dir(Directory::filename(), Options::[term()]) -> ok
-%% filename() = file:filename()
%%
%% @doc Tidies Erlang source files in a directory and its
%% subdirectories.
@@ -414,7 +418,7 @@ write_module(Tree, Name, Opts) ->
print_module(Tree, Opts) ->
Printer = proplists:get_value(printer, Opts),
- io:format(Printer(Tree, Opts)).
+ io:put_chars(Printer(Tree, Opts)).
output(FD, Printer, Tree, Opts) ->
io:put_chars(FD, Printer(Tree, Opts)),
@@ -513,7 +517,6 @@ module(Forms) ->
%% @spec module(Forms, Options::[term()]) -> syntaxTree()
%%
%% Forms = syntaxTree() | [syntaxTree()]
-%% syntaxTree() = erl_syntax:syntaxTree()
%%
%% @doc Tidies a syntax tree representation of a module
%% definition. The given `Forms' may be either a single
diff --git a/lib/syntax_tools/src/igor.erl b/lib/syntax_tools/src/igor.erl
index 1d14bd7c3a..943250e5cd 100644
--- a/lib/syntax_tools/src/igor.erl
+++ b/lib/syntax_tools/src/igor.erl
@@ -151,7 +151,8 @@ default_printer(Tree, Options) ->
%% @spec parse_transform(Forms::[syntaxTree()], Options::[term()]) ->
%% [syntaxTree()]
%%
-%% syntaxTree() = erl_syntax:syntaxTree()
+%% @type syntaxTree() = erl_syntax:syntaxTree(). An abstract syntax
+%% tree. See the {@link erl_syntax} module for details.
%%
%% @doc Allows Igor to work as a component of the Erlang compiler.
%% Including the term `{parse_transform, igor}' in the
@@ -212,7 +213,7 @@ merge(Name, Files) ->
%% @spec merge(Name::atom(), Files::[filename()], Options::[term()]) ->
%% [filename()]
%%
-%% filename() = file:filename()
+%% @type filename() = file:filename()
%%
%% @doc Merges source code files to a single file. `Name'
%% specifies the name of the resulting module - not the name of the
@@ -367,6 +368,7 @@ merge_files(Name, Files, Options) ->
%% @spec merge_files(Name::atom(), Sources::[Forms],
%% Files::[filename()], Options::[term()]) ->
%% {syntaxTree(), [stubDescriptor()]}
+%%
%% Forms = syntaxTree() | [syntaxTree()]
%%
%% @doc Merges source code files and syntax trees to a single syntax
diff --git a/lib/syntax_tools/test/syntax_tools_SUITE.erl b/lib/syntax_tools/test/syntax_tools_SUITE.erl
index b935d42bb7..43c17e9f1f 100644
--- a/lib/syntax_tools/test/syntax_tools_SUITE.erl
+++ b/lib/syntax_tools/test/syntax_tools_SUITE.erl
@@ -27,14 +27,14 @@
%% Test cases
-export([app_test/1,appup_test/1,smoke_test/1,revert/1,revert_map/1,
t_abstract_type/1,t_erl_parse_type/1,t_epp_dodger/1,
- t_comment_scan/1,t_igor/1]).
+ t_comment_scan/1,t_igor/1,t_erl_tidy/1]).
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[app_test,appup_test,smoke_test,revert,revert_map,
t_abstract_type,t_erl_parse_type,t_epp_dodger,
- t_comment_scan,t_igor].
+ t_comment_scan,t_igor,t_erl_tidy].
groups() ->
[].
@@ -237,6 +237,12 @@ t_igor(Config) when is_list(Config) ->
ok.
+t_erl_tidy(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ File = filename:join(DataDir,"erl_tidy_tilde.erl"),
+ ok = erl_tidy:file(File, [{stdout, true}]),
+ ok.
+
test_comment_scan([],_) -> ok;
test_comment_scan([File|Files],DataDir) ->
Filename = filename:join(DataDir,File),
diff --git a/lib/syntax_tools/test/syntax_tools_SUITE_data/erl_tidy_tilde.erl b/lib/syntax_tools/test/syntax_tools_SUITE_data/erl_tidy_tilde.erl
new file mode 100644
index 0000000000..888264bad6
--- /dev/null
+++ b/lib/syntax_tools/test/syntax_tools_SUITE_data/erl_tidy_tilde.erl
@@ -0,0 +1,13 @@
+%%
+%% File: erl_tidy_tilde.erl
+%% Author: Mark Bucciarelli
+%% Created: 2016-06-05
+%%
+
+-module(erl_tidy_tilde).
+
+-export([start/0]).
+
+start() ->
+ io:put_chars("tilde characters ('~')in source were "
+ "breaking erl_tidy\n").
diff --git a/lib/tools/doc/src/notes.xml b/lib/tools/doc/src/notes.xml
index 0b8a2be715..a0a817c0f2 100644
--- a/lib/tools/doc/src/notes.xml
+++ b/lib/tools/doc/src/notes.xml
@@ -31,6 +31,21 @@
</header>
<p>This document describes the changes made to the Tools application.</p>
+<section><title>Tools 2.8.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Correct a bug when adding multiple modules to an Xref
+ server. The bug was introduced in OTP-19.0. </p>
+ <p>
+ Own Id: OTP-13708 Aux Id: ERL-173 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Tools 2.8.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/tools/emacs/erlang-skels.el b/lib/tools/emacs/erlang-skels.el
index ce26c83295..c1152f31a4 100644
--- a/lib/tools/emacs/erlang-skels.el
+++ b/lib/tools/emacs/erlang-skels.el
@@ -903,7 +903,7 @@ Please see the function `tempo-define-template'.")
"%% gen_statem:start_link/[3,4], this function is called by the new" n
"%% process to initialize." n
(erlang-skel-separator-end 2)
- "-spec init(Args :: term()) -> " n>
+ "-spec init(Args :: term()) ->" n>
"{gen_statem:callback_mode()," n>
"State :: term(), Data :: term()} |" n>
"{gen_statem:callback_mode()," n>
@@ -927,7 +927,7 @@ Please see the function `tempo-define-template'.")
"-spec state_name(" n>
"gen_statem:event_type(), Msg :: term()," n>
"Data :: term()) ->" n>
- "gen_statem:state_function_result(). " n
+ "gen_statem:state_function_result()." n
"state_name({call,Caller}, _Msg, Data) ->" n>
"{next_state, state_name, Data, [{reply,Caller,ok}]}." n
n
@@ -940,7 +940,7 @@ Please see the function `tempo-define-template'.")
"-spec handle_event(" n>
"gen_statem:event_type(), Msg :: term()," n>
"State :: term(), Data :: term()) ->" n>
- "gen_statem:handle_event_result(). " n
+ "gen_statem:handle_event_result()." n
"handle_event({call,From}, _Msg, State, Data) ->" n>
"{next_state, State, Data, [{reply,From,ok}]}." n
n
@@ -965,9 +965,11 @@ Please see the function `tempo-define-template'.")
"-spec code_change(" n>
"OldVsn :: term() | {down,term()}," n>
"State :: term(), Data :: term(), Extra :: term()) ->" n>
- "{ok, NewState :: term(), NewData :: term()}." n
+ "{gen_statem:callback_mode()," n>
+ "NewState :: term(), NewData :: term()} |" n>
+ "(Reason :: term())." n
"code_change(_OldVsn, State, Data, _Extra) ->" n>
- "{ok, State, Data}." n
+ "{state_functions, State, Data}." n
n
(erlang-skel-double-separator-start 3)
"%%% Internal functions" n
diff --git a/lib/tools/src/fprof.erl b/lib/tools/src/fprof.erl
index 8db23dd151..1291a3e5ec 100644
--- a/lib/tools/src/fprof.erl
+++ b/lib/tools/src/fprof.erl
@@ -1636,6 +1636,11 @@ trace_handler({trace_ts, Pid, gc_major_start, _Func, TS} = Trace, Table, _, Dump
dump_stack(Dump, get(Pid), Trace),
trace_gc_start(Table, Pid, TS),
TS;
+
+trace_handler({trace_ts, Pid, gc_start, _Func, TS} = Trace, Table, _, Dump) ->
+ dump_stack(Dump, get(Pid), Trace),
+ trace_gc_start(Table, Pid, TS),
+ TS;
%%
%% gc_end
@@ -1648,6 +1653,12 @@ trace_handler({trace_ts, Pid, gc_major_end, _Func, TS} = Trace, Table, _, Dump)
dump_stack(Dump, get(Pid), Trace),
trace_gc_end(Table, Pid, TS),
TS;
+
+trace_handler({trace_ts, Pid, gc_end, _Func, TS} = Trace, Table, _, Dump) ->
+ dump_stack(Dump, get(Pid), Trace),
+ trace_gc_end(Table, Pid, TS),
+ TS;
+
%%
%% link
trace_handler({trace_ts, Pid, link, _OtherPid, TS} = Trace,
diff --git a/lib/tools/src/xref_base.erl b/lib/tools/src/xref_base.erl
index bb9815f9b0..f298a1ce81 100644
--- a/lib/tools/src/xref_base.erl
+++ b/lib/tools/src/xref_base.erl
@@ -746,7 +746,7 @@ read_a_module({Dir, BaseName}, AppName, Builtins, Verbose, Warnings, Mode) ->
message(Warnings, no_debug_info, [File]),
no;
{ok, M, Data, UnresCalls0} ->
- message(Verbose, done, [File]),
+ message(Verbose, done_file, [File]),
%% Remove duplicates. Identical unresolved calls on the
%% same line are counted as _one_ unresolved call.
UnresCalls = usort(UnresCalls0),
@@ -1842,6 +1842,8 @@ message(true, What, Arg) ->
set_up ->
io:format("Setting up...", Arg);
done ->
+ io:format("done~n", Arg);
+ done_file ->
io:format("done reading ~ts~n", Arg);
error ->
io:format("error~n", Arg);
diff --git a/lib/tools/test/xref_SUITE.erl b/lib/tools/test/xref_SUITE.erl
index ce30fb711a..01dbac6ecb 100644
--- a/lib/tools/test/xref_SUITE.erl
+++ b/lib/tools/test/xref_SUITE.erl
@@ -50,7 +50,7 @@
-export([analyze/1, basic/1, md/1, q/1, variables/1, unused_locals/1]).
--export([format_error/1, otp_7423/1, otp_7831/1, otp_10192/1]).
+-export([format_error/1, otp_7423/1, otp_7831/1, otp_10192/1, otp_13708/1]).
-import(lists, [append/2, flatten/1, keysearch/3, member/2, sort/1, usort/1]).
@@ -82,7 +82,7 @@ groups() ->
fun_mfa_r14, fun_mfa_vars, qlc]},
{analyses, [],
[analyze, basic, md, q, variables, unused_locals]},
- {misc, [], [format_error, otp_7423, otp_7831, otp_10192]}].
+ {misc, [], [format_error, otp_7423, otp_7831, otp_10192, otp_13708]}].
init_per_suite(Conf) when is_list(Conf) ->
@@ -2393,6 +2393,19 @@ otp_10192(Conf) when is_list(Conf) ->
xref:stop(s),
ok.
+%% OTP-10192. Allow filenames with character codes greater than 126.
+otp_13708(Conf) when is_list(Conf) ->
+ {ok, _} = start(s),
+ ok = xref:set_default(s, [{verbose, true}]),
+ {ok, []} = xref:q(s,"E"),
+ xref:stop(s),
+
+ CopyDir = ?copydir,
+ Dir = fname(CopyDir,"lib_test"),
+ {ok, _} = start(s),
+ ok = xref:set_library_path(s, [Dir], [{verbose, true}]),
+ xref:stop(s).
+
%%%
%%% Utilities
%%%
diff --git a/lib/tools/vsn.mk b/lib/tools/vsn.mk
index 8c889cbe4e..e6287b0430 100644
--- a/lib/tools/vsn.mk
+++ b/lib/tools/vsn.mk
@@ -1 +1 @@
-TOOLS_VSN = 2.8.4
+TOOLS_VSN = 2.8.5
diff --git a/lib/wx/api_gen/wx_extra/wxListCtrl.erl b/lib/wx/api_gen/wx_extra/wxListCtrl.erl
index acdb69fdeb..355a4cdfd1 100644
--- a/lib/wx/api_gen/wx_extra/wxListCtrl.erl
+++ b/lib/wx/api_gen/wx_extra/wxListCtrl.erl
@@ -43,33 +43,36 @@ SortItems>>
<<EXPORT:wxListCtrl new/0, new/1, new/2 wxListCtrl:EXPORT>>
<<wxListCtrl_new_0
-%% @spec () -> wxListCtrl()
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlwxlistctrl">external documentation</a>.
+-spec new() -> wxListCtrl().
new() ->
wxe_util:construct(~s, <<>>).
wxListCtrl_new_0>>
<<wxListCtrl_new_2
-%% @spec (Parent::wxWindow:wxWindow()) -> wxListCtrl()
-%% @equiv new(Parent, [])
+-spec new(Parent) -> wxListCtrl() when
+ Parent::wxWindow:wxWindow().
new(Parent)
when is_record(Parent, wx_ref) ->
new(Parent, []).
-%% @spec (Parent::wxWindow:wxWindow(), [Option]) -> wxListCtrl()
-%% Option = {winid, integer()} |
-%% {pos, {X::integer(),Y::integer()}} |
-%% {size, {W::integer(),H::integer()}} |
-%% {style, integer()} |
-%% {validator, wx:wx()} |
-%% {onGetItemText, OnGetItemText} |
-%% {onGetItemAttr, OnGetItemAttr} |
-%% {onGetItemColumnImage, OnGetItemColumnImage}
+%% @doc Creates a listctrl with optional callback functions:
%%
-%% OnGetItemText = (This, Item, Column) -> wxString()
-%% OnGetItemAttr = (This, Item) -> wxListItemAttr()
+%% OnGetItemText = (This, Item, Column) -> unicode:charlist()
+%% OnGetItemAttr = (This, Item) -> wxListItemAttr:wxListItemAttr()
%% OnGetItemColumnImage = (This, Item, Column) -> integer()
-%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlwxlistctrl">external documentation</a>.
+%%
+%% See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlwxlistctrl">external documentation</a>.
+-spec new(Parent, [Option]) -> wxListCtrl() when
+ Parent::wxWindow:wxWindow(),
+ Option::{winid, integer()} |
+ {pos, {X::integer(),Y::integer()}} |
+ {size, {W::integer(),H::integer()}} |
+ {style, integer()} |
+ {validator, wx:wx_object()} |
+ {onGetItemText, function()} |
+ {onGetItemAttr, function()} |
+ {onGetItemColumnImage, function()}.
new(#wx_ref{type=ParentT,ref=ParentRef}, Options)
when is_list(Options)->
@@ -101,26 +104,27 @@ wxListCtrl_new_2>>
<<EXPORT:Create create/2, create/3 Create:EXPORT>>
<<Create
-%% @spec (This::wxListCtrl(), Parent::wxWindow:wxWindow()) -> bool()
%% @equiv create(This,Parent, [])
+-spec create(This, Parent) -> wxListCtrl() when
+ This::wxWindow:wxWindow(),
+ Parent::wxWindow:wxWindow().
create(This,Parent)
when is_record(This, wx_ref),is_record(Parent, wx_ref) ->
create(This,Parent, []).
-%% @spec (This::wxListCtrl(), Parent::wxWindow:wxWindow(), [Option]) -> bool()
-%% Option = {winid, integer()} |
-%% {pos, {X::integer(),Y::integer()}} |
-%% {size, {W::integer(),H::integer()}} |
-%% {style, integer()} |
-%% {validator, wx:wx()} |
-%% {onGetItemText, OnGetItemText} |
-%% {onGetItemAttr, OnGetItemAttr} |
-%% {onGetItemColumnImage, OnGetItemColumnImage}
-%%
-%% OnGetItemText = (This, Item, Column) -> wxString()
-%% OnGetItemAttr = (This, Item) -> wxListItemAttr()
-%% OnGetItemColumnImage = (This, Item, Column) -> integer()
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlcreate">external documentation</a>.
+-spec create(This, Parent, [Option]) -> wxListCtrl() when
+ This::wxWindow:wxWindow(),
+ Parent::wxWindow:wxWindow(),
+ Option::{winid, integer()} |
+ {pos, {X::integer(),Y::integer()}} |
+ {size, {W::integer(),H::integer()}} |
+ {style, integer()} |
+ {validator, wx:wx_object()} |
+ {onGetItemText, function()} |
+ {onGetItemAttr, function()} |
+ {onGetItemColumnImage, function()}.
+
create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef}, Options)
when is_list(Options) ->
?CLASS(ThisT,wxListCtrl),
diff --git a/lib/wx/api_gen/wx_extra/wxXmlResource.erl b/lib/wx/api_gen/wx_extra/wxXmlResource.erl
index 7700e2333e..b29ffba7c6 100644
--- a/lib/wx/api_gen/wx_extra/wxXmlResource.erl
+++ b/lib/wx/api_gen/wx_extra/wxXmlResource.erl
@@ -21,8 +21,6 @@
<<EXPORT:xrcctrl xrcctrl/3 xrcctrl:EXPORT>>
<<xrcctrl
-%% @spec (Window::wxWindow:wxWindow(),Name::string(), Type::atom()) -> wx:wxObject()
-
%% @doc Looks up a control with Name in a window created with XML
%% resources. You can use it to set/get values from controls.
%% The object is type casted to <b>Type</b>.
@@ -32,6 +30,10 @@
%% true = wxXmlResource:loadDialog(Xrc, Dlg, Frame, "controls_dialog"), <br />
%% LCtrl = xrcctrl(Dlg, "controls_listctrl", wxListCtrl), <br />
%% wxListCtrl:insertColumn(LCtrl, 0, "Name", [{width, 200}]), <br />
+-spec xrcctrl(Window, Name, Type) -> wx:wx_object() when
+ Window::wxWindow:wxWindow(),
+ Name::string(),
+ Type::atom().
xrcctrl(Window = #wx_ref{}, Name, Type) when is_list(Name), is_atom(Type) ->
%% Func Id ~s
diff --git a/lib/wx/c_src/wxe_main.cpp b/lib/wx/c_src/wxe_main.cpp
index 6fcde42eb5..c7565e33bd 100644
--- a/lib/wx/c_src/wxe_main.cpp
+++ b/lib/wx/c_src/wxe_main.cpp
@@ -67,6 +67,7 @@ int load_native_gui()
int start_native_gui(wxe_data *sd)
{
int res;
+ ErlDrvThreadOpts *opts = NULL;
wxe_status_m = erl_drv_mutex_create((char *) "wxe_status_m");
wxe_status_c = erl_drv_cond_create((char *)"wxe_status_c");
@@ -78,8 +79,11 @@ int start_native_gui(wxe_data *sd)
res = erl_drv_steal_main_thread((char *)"wxwidgets",
&wxe_thread,wxe_main_loop,(void *) sd->pdl,NULL);
#else
+ opts = erl_drv_thread_opts_create((char *)"wx thread");
+ opts->suggested_stack_size = 8192;
res = erl_drv_thread_create((char *)"wxwidgets",
- &wxe_thread,wxe_main_loop,(void *) sd->pdl,NULL);
+ &wxe_thread,wxe_main_loop,(void *) sd->pdl,opts);
+ erl_drv_thread_opts_destroy(opts);
#endif
if(res == 0) {
erl_drv_mutex_lock(wxe_status_m);
diff --git a/lib/wx/doc/specs/.gitignore b/lib/wx/doc/specs/.gitignore
new file mode 100644
index 0000000000..322eebcb06
--- /dev/null
+++ b/lib/wx/doc/specs/.gitignore
@@ -0,0 +1 @@
+specs_*.xml
diff --git a/lib/wx/doc/src/Makefile b/lib/wx/doc/src/Makefile
index cae2f9fe4e..23890f47f0 100644
--- a/lib/wx/doc/src/Makefile
+++ b/lib/wx/doc/src/Makefile
@@ -63,10 +63,15 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
+SPECS_FILES = $(XML_REF3_FILES:%.xml=$(SPECDIR)/specs_%.xml)
+
+TOP_SPECS_FILE = specs.xml
+
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
XML_FLAGS +=
+SPECS_FLAGS = -I../../include -I../../src
DVIPS_FLAGS +=
# ----------------------------------------------------
@@ -111,6 +116,7 @@ clean clean_docs:
rm -rf $(HTMLDIR)/*
rm -f $(MAN3DIR)/*
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
+ rm -f $(SPECDIR)/*
rm -f errs core *~ ../html/edoc-info
rm -f $(XML_REF3_FILES) $(XML_CHAPTER_FILES) *.html
diff --git a/lib/wx/doc/src/specs.xml b/lib/wx/doc/src/specs.xml
new file mode 100644
index 0000000000..2e19baafc4
--- /dev/null
+++ b/lib/wx/doc/src/specs.xml
@@ -0,0 +1,232 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<specs xmlns:xi="http://www.w3.org/2001/XInclude">
+ <xi:include href="../specs/specs_wx.xml"/>
+ <xi:include href="../specs/specs_wx_object.xml"/>
+ <xi:include href="../specs/specs_wxAcceleratorEntry.xml"/>
+ <xi:include href="../specs/specs_wxAcceleratorTable.xml"/>
+ <xi:include href="../specs/specs_wxActivateEvent.xml"/>
+ <xi:include href="../specs/specs_wxArtProvider.xml"/>
+ <xi:include href="../specs/specs_wxAuiDockArt.xml"/>
+ <xi:include href="../specs/specs_wxAuiManager.xml"/>
+ <xi:include href="../specs/specs_wxAuiManagerEvent.xml"/>
+ <xi:include href="../specs/specs_wxAuiNotebook.xml"/>
+ <xi:include href="../specs/specs_wxAuiNotebookEvent.xml"/>
+ <xi:include href="../specs/specs_wxAuiPaneInfo.xml"/>
+ <xi:include href="../specs/specs_wxAuiSimpleTabArt.xml"/>
+ <xi:include href="../specs/specs_wxAuiTabArt.xml"/>
+ <xi:include href="../specs/specs_wxBitmapButton.xml"/>
+ <xi:include href="../specs/specs_wxBitmapDataObject.xml"/>
+ <xi:include href="../specs/specs_wxBitmap.xml"/>
+ <xi:include href="../specs/specs_wxBoxSizer.xml"/>
+ <xi:include href="../specs/specs_wxBrush.xml"/>
+ <xi:include href="../specs/specs_wxBufferedDC.xml"/>
+ <xi:include href="../specs/specs_wxBufferedPaintDC.xml"/>
+ <xi:include href="../specs/specs_wxButton.xml"/>
+ <xi:include href="../specs/specs_wxCalendarCtrl.xml"/>
+ <xi:include href="../specs/specs_wxCalendarDateAttr.xml"/>
+ <xi:include href="../specs/specs_wxCalendarEvent.xml"/>
+ <xi:include href="../specs/specs_wxCaret.xml"/>
+ <xi:include href="../specs/specs_wxCheckBox.xml"/>
+ <xi:include href="../specs/specs_wxCheckListBox.xml"/>
+ <xi:include href="../specs/specs_wxChildFocusEvent.xml"/>
+ <xi:include href="../specs/specs_wxChoicebook.xml"/>
+ <xi:include href="../specs/specs_wxChoice.xml"/>
+ <xi:include href="../specs/specs_wxClientDC.xml"/>
+ <xi:include href="../specs/specs_wxClipboard.xml"/>
+ <xi:include href="../specs/specs_wxClipboardTextEvent.xml"/>
+ <xi:include href="../specs/specs_wxCloseEvent.xml"/>
+ <xi:include href="../specs/specs_wxColourData.xml"/>
+ <xi:include href="../specs/specs_wxColourDialog.xml"/>
+ <xi:include href="../specs/specs_wxColourPickerCtrl.xml"/>
+ <xi:include href="../specs/specs_wxColourPickerEvent.xml"/>
+ <xi:include href="../specs/specs_wxComboBox.xml"/>
+ <xi:include href="../specs/specs_wxCommandEvent.xml"/>
+ <xi:include href="../specs/specs_wxContextMenuEvent.xml"/>
+ <xi:include href="../specs/specs_wxControl.xml"/>
+ <xi:include href="../specs/specs_wxControlWithItems.xml"/>
+ <xi:include href="../specs/specs_wxCursor.xml"/>
+ <xi:include href="../specs/specs_wxDataObject.xml"/>
+ <xi:include href="../specs/specs_wxDateEvent.xml"/>
+ <xi:include href="../specs/specs_wxDatePickerCtrl.xml"/>
+ <xi:include href="../specs/specs_wxDC.xml"/>
+ <xi:include href="../specs/specs_wxDCOverlay.xml"/>
+ <xi:include href="../specs/specs_wxDialog.xml"/>
+ <xi:include href="../specs/specs_wxDirDialog.xml"/>
+ <xi:include href="../specs/specs_wxDirPickerCtrl.xml"/>
+ <xi:include href="../specs/specs_wxDisplayChangedEvent.xml"/>
+ <xi:include href="../specs/specs_wxEraseEvent.xml"/>
+ <xi:include href="../specs/specs_wxEvent.xml"/>
+ <xi:include href="../specs/specs_wxEvtHandler.xml"/>
+ <xi:include href="../specs/specs_wxFileDataObject.xml"/>
+ <xi:include href="../specs/specs_wxFileDialog.xml"/>
+ <xi:include href="../specs/specs_wxFileDirPickerEvent.xml"/>
+ <xi:include href="../specs/specs_wxFilePickerCtrl.xml"/>
+ <xi:include href="../specs/specs_wxFindReplaceData.xml"/>
+ <xi:include href="../specs/specs_wxFindReplaceDialog.xml"/>
+ <xi:include href="../specs/specs_wxFlexGridSizer.xml"/>
+ <xi:include href="../specs/specs_wxFocusEvent.xml"/>
+ <xi:include href="../specs/specs_wxFontData.xml"/>
+ <xi:include href="../specs/specs_wxFontDialog.xml"/>
+ <xi:include href="../specs/specs_wxFont.xml"/>
+ <xi:include href="../specs/specs_wxFontPickerCtrl.xml"/>
+ <xi:include href="../specs/specs_wxFontPickerEvent.xml"/>
+ <xi:include href="../specs/specs_wxFrame.xml"/>
+ <xi:include href="../specs/specs_wxGauge.xml"/>
+ <xi:include href="../specs/specs_wxGBSizerItem.xml"/>
+ <xi:include href="../specs/specs_wxGenericDirCtrl.xml"/>
+ <xi:include href="../specs/specs_wxGLCanvas.xml"/>
+ <xi:include href="../specs/specs_wxGraphicsBrush.xml"/>
+ <xi:include href="../specs/specs_wxGraphicsContext.xml"/>
+ <xi:include href="../specs/specs_wxGraphicsFont.xml"/>
+ <xi:include href="../specs/specs_wxGraphicsMatrix.xml"/>
+ <xi:include href="../specs/specs_wxGraphicsObject.xml"/>
+ <xi:include href="../specs/specs_wxGraphicsPath.xml"/>
+ <xi:include href="../specs/specs_wxGraphicsPen.xml"/>
+ <xi:include href="../specs/specs_wxGraphicsRenderer.xml"/>
+ <xi:include href="../specs/specs_wxGridBagSizer.xml"/>
+ <xi:include href="../specs/specs_wxGridCellAttr.xml"/>
+ <xi:include href="../specs/specs_wxGridCellBoolEditor.xml"/>
+ <xi:include href="../specs/specs_wxGridCellBoolRenderer.xml"/>
+ <xi:include href="../specs/specs_wxGridCellChoiceEditor.xml"/>
+ <xi:include href="../specs/specs_wxGridCellEditor.xml"/>
+ <xi:include href="../specs/specs_wxGridCellFloatEditor.xml"/>
+ <xi:include href="../specs/specs_wxGridCellFloatRenderer.xml"/>
+ <xi:include href="../specs/specs_wxGridCellNumberEditor.xml"/>
+ <xi:include href="../specs/specs_wxGridCellNumberRenderer.xml"/>
+ <xi:include href="../specs/specs_wxGridCellRenderer.xml"/>
+ <xi:include href="../specs/specs_wxGridCellStringRenderer.xml"/>
+ <xi:include href="../specs/specs_wxGridCellTextEditor.xml"/>
+ <xi:include href="../specs/specs_wxGrid.xml"/>
+ <xi:include href="../specs/specs_wxGridEvent.xml"/>
+ <xi:include href="../specs/specs_wxGridSizer.xml"/>
+ <xi:include href="../specs/specs_wxHelpEvent.xml"/>
+ <xi:include href="../specs/specs_wxHtmlEasyPrinting.xml"/>
+ <xi:include href="../specs/specs_wxHtmlLinkEvent.xml"/>
+ <xi:include href="../specs/specs_wxHtmlWindow.xml"/>
+ <xi:include href="../specs/specs_wxIconBundle.xml"/>
+ <xi:include href="../specs/specs_wxIcon.xml"/>
+ <xi:include href="../specs/specs_wxIconizeEvent.xml"/>
+ <xi:include href="../specs/specs_wxIdleEvent.xml"/>
+ <xi:include href="../specs/specs_wxImage.xml"/>
+ <xi:include href="../specs/specs_wxImageList.xml"/>
+ <xi:include href="../specs/specs_wxInitDialogEvent.xml"/>
+ <xi:include href="../specs/specs_wxJoystickEvent.xml"/>
+ <xi:include href="../specs/specs_wxKeyEvent.xml"/>
+ <xi:include href="../specs/specs_wxLayoutAlgorithm.xml"/>
+ <xi:include href="../specs/specs_wxListbook.xml"/>
+ <xi:include href="../specs/specs_wxListBox.xml"/>
+ <xi:include href="../specs/specs_wxListCtrl.xml"/>
+ <xi:include href="../specs/specs_wxListEvent.xml"/>
+ <xi:include href="../specs/specs_wxListItemAttr.xml"/>
+ <xi:include href="../specs/specs_wxListItem.xml"/>
+ <xi:include href="../specs/specs_wxListView.xml"/>
+ <xi:include href="../specs/specs_wxLocale.xml"/>
+ <xi:include href="../specs/specs_wxLogNull.xml"/>
+ <xi:include href="../specs/specs_wxMask.xml"/>
+ <xi:include href="../specs/specs_wxMaximizeEvent.xml"/>
+ <xi:include href="../specs/specs_wxMDIChildFrame.xml"/>
+ <xi:include href="../specs/specs_wxMDIClientWindow.xml"/>
+ <xi:include href="../specs/specs_wxMDIParentFrame.xml"/>
+ <xi:include href="../specs/specs_wxMemoryDC.xml"/>
+ <xi:include href="../specs/specs_wxMenuBar.xml"/>
+ <xi:include href="../specs/specs_wxMenu.xml"/>
+ <xi:include href="../specs/specs_wxMenuEvent.xml"/>
+ <xi:include href="../specs/specs_wxMenuItem.xml"/>
+ <xi:include href="../specs/specs_wxMessageDialog.xml"/>
+ <xi:include href="../specs/specs_wxMiniFrame.xml"/>
+ <xi:include href="../specs/specs_wxMirrorDC.xml"/>
+ <xi:include href="../specs/specs_wxMouseCaptureChangedEvent.xml"/>
+ <xi:include href="../specs/specs_wxMouseCaptureLostEvent.xml"/>
+ <xi:include href="../specs/specs_wxMouseEvent.xml"/>
+ <xi:include href="../specs/specs_wxMoveEvent.xml"/>
+ <xi:include href="../specs/specs_wxMultiChoiceDialog.xml"/>
+ <xi:include href="../specs/specs_wxNavigationKeyEvent.xml"/>
+ <xi:include href="../specs/specs_wxNotebook.xml"/>
+ <xi:include href="../specs/specs_wxNotebookEvent.xml"/>
+ <xi:include href="../specs/specs_wxNotifyEvent.xml"/>
+ <xi:include href="../specs/specs_wxOverlay.xml"/>
+ <xi:include href="../specs/specs_wxPageSetupDialogData.xml"/>
+ <xi:include href="../specs/specs_wxPageSetupDialog.xml"/>
+ <xi:include href="../specs/specs_wxPaintDC.xml"/>
+ <xi:include href="../specs/specs_wxPaintEvent.xml"/>
+ <xi:include href="../specs/specs_wxPaletteChangedEvent.xml"/>
+ <xi:include href="../specs/specs_wxPalette.xml"/>
+ <xi:include href="../specs/specs_wxPanel.xml"/>
+ <xi:include href="../specs/specs_wxPasswordEntryDialog.xml"/>
+ <xi:include href="../specs/specs_wxPen.xml"/>
+ <xi:include href="../specs/specs_wxPickerBase.xml"/>
+ <xi:include href="../specs/specs_wxPopupTransientWindow.xml"/>
+ <xi:include href="../specs/specs_wxPopupWindow.xml"/>
+ <xi:include href="../specs/specs_wxPostScriptDC.xml"/>
+ <xi:include href="../specs/specs_wxPreviewCanvas.xml"/>
+ <xi:include href="../specs/specs_wxPreviewControlBar.xml"/>
+ <xi:include href="../specs/specs_wxPreviewFrame.xml"/>
+ <xi:include href="../specs/specs_wxPrintData.xml"/>
+ <xi:include href="../specs/specs_wxPrintDialogData.xml"/>
+ <xi:include href="../specs/specs_wxPrintDialog.xml"/>
+ <xi:include href="../specs/specs_wxPrinter.xml"/>
+ <xi:include href="../specs/specs_wxPrintout.xml"/>
+ <xi:include href="../specs/specs_wxPrintPreview.xml"/>
+ <xi:include href="../specs/specs_wxProgressDialog.xml"/>
+ <xi:include href="../specs/specs_wxQueryNewPaletteEvent.xml"/>
+ <xi:include href="../specs/specs_wxRadioBox.xml"/>
+ <xi:include href="../specs/specs_wxRadioButton.xml"/>
+ <xi:include href="../specs/specs_wxRegion.xml"/>
+ <xi:include href="../specs/specs_wxSashEvent.xml"/>
+ <xi:include href="../specs/specs_wxSashLayoutWindow.xml"/>
+ <xi:include href="../specs/specs_wxSashWindow.xml"/>
+ <xi:include href="../specs/specs_wxScreenDC.xml"/>
+ <xi:include href="../specs/specs_wxScrollBar.xml"/>
+ <xi:include href="../specs/specs_wxScrolledWindow.xml"/>
+ <xi:include href="../specs/specs_wxScrollEvent.xml"/>
+ <xi:include href="../specs/specs_wxScrollWinEvent.xml"/>
+ <xi:include href="../specs/specs_wxSetCursorEvent.xml"/>
+ <xi:include href="../specs/specs_wxShowEvent.xml"/>
+ <xi:include href="../specs/specs_wxSingleChoiceDialog.xml"/>
+ <xi:include href="../specs/specs_wxSizeEvent.xml"/>
+ <xi:include href="../specs/specs_wxSizer.xml"/>
+ <xi:include href="../specs/specs_wxSizerFlags.xml"/>
+ <xi:include href="../specs/specs_wxSizerItem.xml"/>
+ <xi:include href="../specs/specs_wxSlider.xml"/>
+ <xi:include href="../specs/specs_wxSpinButton.xml"/>
+ <xi:include href="../specs/specs_wxSpinCtrl.xml"/>
+ <xi:include href="../specs/specs_wxSpinEvent.xml"/>
+ <xi:include href="../specs/specs_wxSplashScreen.xml"/>
+ <xi:include href="../specs/specs_wxSplitterEvent.xml"/>
+ <xi:include href="../specs/specs_wxSplitterWindow.xml"/>
+ <xi:include href="../specs/specs_wxStaticBitmap.xml"/>
+ <xi:include href="../specs/specs_wxStaticBox.xml"/>
+ <xi:include href="../specs/specs_wxStaticBoxSizer.xml"/>
+ <xi:include href="../specs/specs_wxStaticLine.xml"/>
+ <xi:include href="../specs/specs_wxStaticText.xml"/>
+ <xi:include href="../specs/specs_wxStatusBar.xml"/>
+ <xi:include href="../specs/specs_wxStdDialogButtonSizer.xml"/>
+ <xi:include href="../specs/specs_wxStyledTextCtrl.xml"/>
+ <xi:include href="../specs/specs_wxStyledTextEvent.xml"/>
+ <xi:include href="../specs/specs_wxSysColourChangedEvent.xml"/>
+ <xi:include href="../specs/specs_wxSystemOptions.xml"/>
+ <xi:include href="../specs/specs_wxSystemSettings.xml"/>
+ <xi:include href="../specs/specs_wxTaskBarIcon.xml"/>
+ <xi:include href="../specs/specs_wxTaskBarIconEvent.xml"/>
+ <xi:include href="../specs/specs_wxTextAttr.xml"/>
+ <xi:include href="../specs/specs_wxTextCtrl.xml"/>
+ <xi:include href="../specs/specs_wxTextDataObject.xml"/>
+ <xi:include href="../specs/specs_wxTextEntryDialog.xml"/>
+ <xi:include href="../specs/specs_wxToggleButton.xml"/>
+ <xi:include href="../specs/specs_wxToolBar.xml"/>
+ <xi:include href="../specs/specs_wxToolbook.xml"/>
+ <xi:include href="../specs/specs_wxToolTip.xml"/>
+ <xi:include href="../specs/specs_wxTopLevelWindow.xml"/>
+ <xi:include href="../specs/specs_wxTreebook.xml"/>
+ <xi:include href="../specs/specs_wxTreeCtrl.xml"/>
+ <xi:include href="../specs/specs_wxTreeEvent.xml"/>
+ <xi:include href="../specs/specs_wxUpdateUIEvent.xml"/>
+ <xi:include href="../specs/specs_wxWindowCreateEvent.xml"/>
+ <xi:include href="../specs/specs_wxWindowDC.xml"/>
+ <xi:include href="../specs/specs_wxWindowDestroyEvent.xml"/>
+ <xi:include href="../specs/specs_wxWindow.xml"/>
+ <xi:include href="../specs/specs_wxXmlResource.xml"/>
+ <xi:include href="../specs/specs_wx_misc.xml"/>
+ <xi:include href="../specs/specs_glu.xml"/>
+ <xi:include href="../specs/specs_gl.xml"/>
+</specs>
diff --git a/lib/wx/include/wx.hrl b/lib/wx/include/wx.hrl
index af7cca7ed2..30baeebb18 100644
--- a/lib/wx/include/wx.hrl
+++ b/lib/wx/include/wx.hrl
@@ -33,94 +33,51 @@
%% Here comes the definitions of all event records.
%% they contain the event type and possible some extra information.
--record(wxInitDialog, {type :: wxInitDialogEventType()}). %% Callback event: {@link wxInitDialogEvent}
--type wxInitDialogEventType() :: 'init_dialog'.
--type wxInitDialog() :: #wxInitDialog{}. %% Callback event: {@link wxInitDialogEvent}
-
--record(wxClose, {type :: wxCloseEventType()}). %% Callback event: {@link wxCloseEvent}
--type wxCloseEventType() :: 'close_window' | 'end_session' | 'query_end_session'.
--type wxClose() :: #wxClose{}. %% Callback event: {@link wxCloseEvent}
-
--record(wxStyledText,{type :: wxStyledTextEventType(), %% Callback event: {@link wxStyledTextEvent}
- position :: integer(),
- key :: integer(),
- modifiers :: integer(),
- modificationType :: integer(),
- text :: unicode:chardata(),
- length :: integer(),
- linesAdded :: integer(),
- line :: integer(),
- foldLevelNow :: integer(),
- foldLevelPrev :: integer(),
- margin :: integer(),
- message :: integer(),
- wParam :: integer(),
- lParam :: integer(),
- listType :: integer(),
- x :: integer(),
- y :: integer(),
- dragText :: unicode:chardata(),
- dragAllowMove :: boolean(),
- dragResult :: wx:wx_enum()}).
--type wxStyledTextEventType() :: 'stc_change' | 'stc_styleneeded' | 'stc_charadded' | 'stc_savepointreached' | 'stc_savepointleft' | 'stc_romodifyattempt' | 'stc_key' | 'stc_doubleclick' | 'stc_updateui' | 'stc_modified' | 'stc_macrorecord' | 'stc_marginclick' | 'stc_needshown' | 'stc_painted' | 'stc_userlistselection' | 'stc_uridropped' | 'stc_dwellstart' | 'stc_dwellend' | 'stc_start_drag' | 'stc_drag_over' | 'stc_do_drop' | 'stc_zoom' | 'stc_hotspot_click' | 'stc_hotspot_dclick' | 'stc_calltip_click' | 'stc_autocomp_selection'.
--type wxStyledText() :: #wxStyledText{}. %% Callback event: {@link wxStyledTextEvent}
-
--record(wxFileDirPicker,{type :: wxFileDirPickerEventType(), %% Callback event: {@link wxFileDirPickerEvent}
- path :: unicode:chardata()}).
--type wxFileDirPickerEventType() :: 'command_filepicker_changed' | 'command_dirpicker_changed'.
--type wxFileDirPicker() :: #wxFileDirPicker{}. %% Callback event: {@link wxFileDirPickerEvent}
-
--record(wxNotebook,{type :: wxNotebookEventType(), %% Callback event: {@link wxNotebookEvent}
- nSel :: integer(),
- nOldSel :: integer()}).
--type wxNotebookEventType() :: 'command_notebook_page_changed' | 'command_notebook_page_changing'.
--type wxNotebook() :: #wxNotebook{}. %% Callback event: {@link wxNotebookEvent}
+-record(wxFocus,{type :: wxFocusEventType(), %% Callback event: {@link wxFocusEvent}
+ win :: wxWindow:wxWindow()}).
+-type wxFocusEventType() :: 'set_focus' | 'kill_focus'.
+-type wxFocus() :: #wxFocus{}. %% Callback event: {@link wxFocusEvent}
--record(wxIdle, {type :: wxIdleEventType()}). %% Callback event: {@link wxIdleEvent}
--type wxIdleEventType() :: 'idle'.
--type wxIdle() :: #wxIdle{}. %% Callback event: {@link wxIdleEvent}
+-record(wxCommand,{type :: wxCommandEventType(), %% Callback event: {@link wxCommandEvent}
+ cmdString :: unicode:chardata(),
+ commandInt :: integer(),
+ extraLong :: integer()}).
+-type wxCommandEventType() :: 'command_button_clicked' | 'command_checkbox_clicked' | 'command_choice_selected' | 'command_listbox_selected' | 'command_listbox_doubleclicked' | 'command_text_updated' | 'command_text_enter' | 'command_menu_selected' | 'command_slider_updated' | 'command_radiobox_selected' | 'command_radiobutton_selected' | 'command_scrollbar_updated' | 'command_vlbox_selected' | 'command_combobox_selected' | 'command_tool_rclicked' | 'command_tool_enter' | 'command_checklistbox_toggled' | 'command_togglebutton_clicked' | 'command_left_click' | 'command_left_dclick' | 'command_right_click' | 'command_set_focus' | 'command_kill_focus' | 'command_enter'.
+-type wxCommand() :: #wxCommand{}. %% Callback event: {@link wxCommandEvent}
-record(wxColourPicker,{type :: wxColourPickerEventType(), %% Callback event: {@link wxColourPickerEvent}
colour :: wx:wx_colour()}).
-type wxColourPickerEventType() :: 'command_colourpicker_changed'.
-type wxColourPicker() :: #wxColourPicker{}. %% Callback event: {@link wxColourPickerEvent}
--record(wxSplitter, {type :: wxSplitterEventType()}). %% Callback event: {@link wxSplitterEvent}
--type wxSplitterEventType() :: 'command_splitter_sash_pos_changed' | 'command_splitter_sash_pos_changing' | 'command_splitter_doubleclicked' | 'command_splitter_unsplit'.
--type wxSplitter() :: #wxSplitter{}. %% Callback event: {@link wxSplitterEvent}
-
--record(wxSash,{type :: wxSashEventType(), %% Callback event: {@link wxSashEvent}
- edge :: wx:wx_enum(),
- dragRect :: {X::integer(), Y::integer(), W::integer(), H::integer()},
- dragStatus :: wx:wx_enum()}).
--type wxSashEventType() :: 'sash_dragged'.
--type wxSash() :: #wxSash{}. %% Callback event: {@link wxSashEvent}
-
-record(wxHelp, {type :: wxHelpEventType()}). %% Callback event: {@link wxHelpEvent}
-type wxHelpEventType() :: 'help' | 'detailed_help'.
-type wxHelp() :: #wxHelp{}. %% Callback event: {@link wxHelpEvent}
--record(wxDisplayChanged, {type :: wxDisplayChangedEventType()}). %% Callback event: {@link wxDisplayChangedEvent}
--type wxDisplayChangedEventType() :: 'display_changed'.
--type wxDisplayChanged() :: #wxDisplayChanged{}. %% Callback event: {@link wxDisplayChangedEvent}
-
--record(wxMouseCaptureLost, {type :: wxMouseCaptureLostEventType()}). %% Callback event: {@link wxMouseCaptureLostEvent}
--type wxMouseCaptureLostEventType() :: 'mouse_capture_lost'.
--type wxMouseCaptureLost() :: #wxMouseCaptureLost{}. %% Callback event: {@link wxMouseCaptureLostEvent}
+-record(wxSpin,{type :: wxSpinEventType(), %% Callback event: {@link wxSpinEvent}
+ commandInt :: integer()}).
+-type wxSpinEventType() :: 'command_spinctrl_updated' | 'spin_up' | 'spin_down' | 'spin'.
+-type wxSpin() :: #wxSpin{}. %% Callback event: {@link wxSpinEvent}
--record(wxFontPicker,{type :: wxFontPickerEventType(), %% Callback event: {@link wxFontPickerEvent}
- font :: wxFont:wxFont()}).
--type wxFontPickerEventType() :: 'command_fontpicker_changed'.
--type wxFontPicker() :: #wxFontPicker{}. %% Callback event: {@link wxFontPickerEvent}
+-record(wxAuiManager,{type :: wxAuiManagerEventType(), %% Callback event: {@link wxAuiManagerEvent}
+ manager :: wxAuiManager:wxAuiManager(),
+ pane :: wxAuiPaneInfo:wxAuiPaneInfo(),
+ button :: integer(),
+ veto_flag :: boolean(),
+ canveto_flag :: boolean(),
+ dc :: wxDC:wxDC()}).
+-type wxAuiManagerEventType() :: 'aui_pane_button' | 'aui_pane_close' | 'aui_pane_maximize' | 'aui_pane_restore' | 'aui_pane_activated' | 'aui_render' | 'aui_find_manager'.
+-type wxAuiManager() :: #wxAuiManager{}. %% Callback event: {@link wxAuiManagerEvent}
--record(wxFocus,{type :: wxFocusEventType(), %% Callback event: {@link wxFocusEvent}
- win :: wxWindow:wxWindow()}).
--type wxFocusEventType() :: 'set_focus' | 'kill_focus'.
--type wxFocus() :: #wxFocus{}. %% Callback event: {@link wxFocusEvent}
+-record(wxDate,{type :: wxDateEventType(), %% Callback event: {@link wxDateEvent}
+ date :: wx:wx_datetime()}).
+-type wxDateEventType() :: 'date_changed'.
+-type wxDate() :: #wxDate{}. %% Callback event: {@link wxDateEvent}
--record(wxPaletteChanged, {type :: wxPaletteChangedEventType()}). %% Callback event: {@link wxPaletteChangedEvent}
--type wxPaletteChangedEventType() :: 'palette_changed'.
--type wxPaletteChanged() :: #wxPaletteChanged{}. %% Callback event: {@link wxPaletteChangedEvent}
+-record(wxIconize,{type :: wxIconizeEventType(), %% Callback event: {@link wxIconizeEvent}
+ iconized :: boolean()}).
+-type wxIconizeEventType() :: 'iconize'.
+-type wxIconize() :: #wxIconize{}. %% Callback event: {@link wxIconizeEvent}
-record(wxScroll,{type :: wxScrollEventType(), %% Callback event: {@link wxScrollEvent}
commandInt :: integer(),
@@ -128,38 +85,41 @@
-type wxScrollEventType() :: 'scroll_top' | 'scroll_bottom' | 'scroll_lineup' | 'scroll_linedown' | 'scroll_pageup' | 'scroll_pagedown' | 'scroll_thumbtrack' | 'scroll_thumbrelease' | 'scroll_changed'.
-type wxScroll() :: #wxScroll{}. %% Callback event: {@link wxScrollEvent}
--record(wxChildFocus, {type :: wxChildFocusEventType()}). %% Callback event: {@link wxChildFocusEvent}
--type wxChildFocusEventType() :: 'child_focus'.
--type wxChildFocus() :: #wxChildFocus{}. %% Callback event: {@link wxChildFocusEvent}
+-record(wxSplitter, {type :: wxSplitterEventType()}). %% Callback event: {@link wxSplitterEvent}
+-type wxSplitterEventType() :: 'command_splitter_sash_pos_changed' | 'command_splitter_sash_pos_changing' | 'command_splitter_doubleclicked' | 'command_splitter_unsplit'.
+-type wxSplitter() :: #wxSplitter{}. %% Callback event: {@link wxSplitterEvent}
--record(wxAuiNotebook,{type :: wxAuiNotebookEventType(), %% Callback event: {@link wxAuiNotebookEvent}
- old_selection :: integer(),
- selection :: integer(),
- drag_source :: wxAuiNotebook:wxAuiNotebook()}).
--type wxAuiNotebookEventType() :: 'command_auinotebook_page_close' | 'command_auinotebook_page_changed' | 'command_auinotebook_page_changing' | 'command_auinotebook_button' | 'command_auinotebook_begin_drag' | 'command_auinotebook_end_drag' | 'command_auinotebook_drag_motion' | 'command_auinotebook_allow_dnd' | 'command_auinotebook_tab_middle_down' | 'command_auinotebook_tab_middle_up' | 'command_auinotebook_tab_right_down' | 'command_auinotebook_tab_right_up' | 'command_auinotebook_page_closed' | 'command_auinotebook_drag_done' | 'command_auinotebook_bg_dclick'.
--type wxAuiNotebook() :: #wxAuiNotebook{}. %% Callback event: {@link wxAuiNotebookEvent}
+-record(wxPaletteChanged, {type :: wxPaletteChangedEventType()}). %% Callback event: {@link wxPaletteChangedEvent}
+-type wxPaletteChangedEventType() :: 'palette_changed'.
+-type wxPaletteChanged() :: #wxPaletteChanged{}. %% Callback event: {@link wxPaletteChangedEvent}
--record(wxSize,{type :: wxSizeEventType(), %% Callback event: {@link wxSizeEvent}
- size :: {W::integer(), H::integer()},
- rect :: {X::integer(), Y::integer(), W::integer(), H::integer()}}).
--type wxSizeEventType() :: 'size'.
--type wxSize() :: #wxSize{}. %% Callback event: {@link wxSizeEvent}
+-record(wxNotebook,{type :: wxNotebookEventType(), %% Callback event: {@link wxNotebookEvent}
+ nSel :: integer(),
+ nOldSel :: integer()}).
+-type wxNotebookEventType() :: 'command_notebook_page_changed' | 'command_notebook_page_changing'.
+-type wxNotebook() :: #wxNotebook{}. %% Callback event: {@link wxNotebookEvent}
--record(wxCommand,{type :: wxCommandEventType(), %% Callback event: {@link wxCommandEvent}
- cmdString :: unicode:chardata(),
- commandInt :: integer(),
- extraLong :: integer()}).
--type wxCommandEventType() :: 'command_button_clicked' | 'command_checkbox_clicked' | 'command_choice_selected' | 'command_listbox_selected' | 'command_listbox_doubleclicked' | 'command_text_updated' | 'command_text_enter' | 'command_menu_selected' | 'command_slider_updated' | 'command_radiobox_selected' | 'command_radiobutton_selected' | 'command_scrollbar_updated' | 'command_vlbox_selected' | 'command_combobox_selected' | 'command_tool_rclicked' | 'command_tool_enter' | 'command_checklistbox_toggled' | 'command_togglebutton_clicked' | 'command_left_click' | 'command_left_dclick' | 'command_right_click' | 'command_set_focus' | 'command_kill_focus' | 'command_enter'.
--type wxCommand() :: #wxCommand{}. %% Callback event: {@link wxCommandEvent}
+-record(wxContextMenu,{type :: wxContextMenuEventType(), %% Callback event: {@link wxContextMenuEvent}
+ pos :: {X::integer(), Y::integer()}}).
+-type wxContextMenuEventType() :: 'context_menu'.
+-type wxContextMenu() :: #wxContextMenu{}. %% Callback event: {@link wxContextMenuEvent}
--record(wxMaximize, {type :: wxMaximizeEventType()}). %% Callback event: {@link wxMaximizeEvent}
--type wxMaximizeEventType() :: 'maximize'.
--type wxMaximize() :: #wxMaximize{}. %% Callback event: {@link wxMaximizeEvent}
+-record(wxFontPicker,{type :: wxFontPickerEventType(), %% Callback event: {@link wxFontPickerEvent}
+ font :: wxFont:wxFont()}).
+-type wxFontPickerEventType() :: 'command_fontpicker_changed'.
+-type wxFontPicker() :: #wxFontPicker{}. %% Callback event: {@link wxFontPickerEvent}
--record(wxSpin,{type :: wxSpinEventType(), %% Callback event: {@link wxSpinEvent}
- commandInt :: integer()}).
--type wxSpinEventType() :: 'command_spinctrl_updated' | 'spin_up' | 'spin_down' | 'spin'.
--type wxSpin() :: #wxSpin{}. %% Callback event: {@link wxSpinEvent}
+-record(wxChildFocus, {type :: wxChildFocusEventType()}). %% Callback event: {@link wxChildFocusEvent}
+-type wxChildFocusEventType() :: 'child_focus'.
+-type wxChildFocus() :: #wxChildFocus{}. %% Callback event: {@link wxChildFocusEvent}
+
+-record(wxTaskBarIcon, {type :: wxTaskBarIconEventType()}). %% Callback event: {@link wxTaskBarIconEvent}
+-type wxTaskBarIconEventType() :: 'taskbar_move' | 'taskbar_left_down' | 'taskbar_left_up' | 'taskbar_right_down' | 'taskbar_right_up' | 'taskbar_left_dclick' | 'taskbar_right_dclick'.
+-type wxTaskBarIcon() :: #wxTaskBarIcon{}. %% Callback event: {@link wxTaskBarIconEvent}
+
+-record(wxWindowDestroy, {type :: wxWindowDestroyEventType()}). %% Callback event: {@link wxWindowDestroyEvent}
+-type wxWindowDestroyEventType() :: 'destroy'.
+-type wxWindowDestroy() :: #wxWindowDestroy{}. %% Callback event: {@link wxWindowDestroyEvent}
-record(wxMenu,{type :: wxMenuEventType(), %% Callback event: {@link wxMenuEvent}
menuId :: integer(),
@@ -167,30 +127,63 @@
-type wxMenuEventType() :: 'menu_open' | 'menu_close' | 'menu_highlight'.
-type wxMenu() :: #wxMenu{}. %% Callback event: {@link wxMenuEvent}
+-record(wxActivate,{type :: wxActivateEventType(), %% Callback event: {@link wxActivateEvent}
+ active :: boolean()}).
+-type wxActivateEventType() :: 'activate' | 'activate_app' | 'hibernate'.
+-type wxActivate() :: #wxActivate{}. %% Callback event: {@link wxActivateEvent}
+
+-record(wxGrid,{type :: wxGridEventType(), %% Callback event: {@link wxGridEvent}
+ row :: integer(),
+ col :: integer(),
+ x :: integer(),
+ y :: integer(),
+ selecting :: boolean(),
+ control :: boolean(),
+ meta :: boolean(),
+ shift :: boolean(),
+ alt :: boolean()}).
+-type wxGridEventType() :: 'grid_cell_left_click' | 'grid_cell_right_click' | 'grid_cell_left_dclick' | 'grid_cell_right_dclick' | 'grid_label_left_click' | 'grid_label_right_click' | 'grid_label_left_dclick' | 'grid_label_right_dclick' | 'grid_row_size' | 'grid_col_size' | 'grid_range_select' | 'grid_cell_change' | 'grid_select_cell' | 'grid_editor_shown' | 'grid_editor_hidden' | 'grid_editor_created' | 'grid_cell_begin_drag'.
+-type wxGrid() :: #wxGrid{}. %% Callback event: {@link wxGridEvent}
+
+-record(wxPaint, {type :: wxPaintEventType()}). %% Callback event: {@link wxPaintEvent}
+-type wxPaintEventType() :: 'paint'.
+-type wxPaint() :: #wxPaint{}. %% Callback event: {@link wxPaintEvent}
+
-record(wxShow,{type :: wxShowEventType(), %% Callback event: {@link wxShowEvent}
show :: boolean()}).
-type wxShowEventType() :: 'show'.
-type wxShow() :: #wxShow{}. %% Callback event: {@link wxShowEvent}
--record(wxWindowDestroy, {type :: wxWindowDestroyEventType()}). %% Callback event: {@link wxWindowDestroyEvent}
--type wxWindowDestroyEventType() :: 'destroy'.
--type wxWindowDestroy() :: #wxWindowDestroy{}. %% Callback event: {@link wxWindowDestroyEvent}
-
--record(wxContextMenu,{type :: wxContextMenuEventType(), %% Callback event: {@link wxContextMenuEvent}
- pos :: {X::integer(), Y::integer()}}).
--type wxContextMenuEventType() :: 'context_menu'.
--type wxContextMenu() :: #wxContextMenu{}. %% Callback event: {@link wxContextMenuEvent}
-
--record(wxActivate,{type :: wxActivateEventType(), %% Callback event: {@link wxActivateEvent}
- active :: boolean()}).
--type wxActivateEventType() :: 'activate' | 'activate_app' | 'hibernate'.
--type wxActivate() :: #wxActivate{}. %% Callback event: {@link wxActivateEvent}
+-record(wxStyledText,{type :: wxStyledTextEventType(), %% Callback event: {@link wxStyledTextEvent}
+ position :: integer(),
+ key :: integer(),
+ modifiers :: integer(),
+ modificationType :: integer(),
+ text :: unicode:chardata(),
+ length :: integer(),
+ linesAdded :: integer(),
+ line :: integer(),
+ foldLevelNow :: integer(),
+ foldLevelPrev :: integer(),
+ margin :: integer(),
+ message :: integer(),
+ wParam :: integer(),
+ lParam :: integer(),
+ listType :: integer(),
+ x :: integer(),
+ y :: integer(),
+ dragText :: unicode:chardata(),
+ dragAllowMove :: boolean(),
+ dragResult :: wx:wx_enum()}).
+-type wxStyledTextEventType() :: 'stc_change' | 'stc_styleneeded' | 'stc_charadded' | 'stc_savepointreached' | 'stc_savepointleft' | 'stc_romodifyattempt' | 'stc_key' | 'stc_doubleclick' | 'stc_updateui' | 'stc_modified' | 'stc_macrorecord' | 'stc_marginclick' | 'stc_needshown' | 'stc_painted' | 'stc_userlistselection' | 'stc_uridropped' | 'stc_dwellstart' | 'stc_dwellend' | 'stc_start_drag' | 'stc_drag_over' | 'stc_do_drop' | 'stc_zoom' | 'stc_hotspot_click' | 'stc_hotspot_dclick' | 'stc_calltip_click' | 'stc_autocomp_selection'.
+-type wxStyledText() :: #wxStyledText{}. %% Callback event: {@link wxStyledTextEvent}
--record(wxMove,{type :: wxMoveEventType(), %% Callback event: {@link wxMoveEvent}
- pos :: {X::integer(), Y::integer()},
- rect :: {X::integer(), Y::integer(), W::integer(), H::integer()}}).
--type wxMoveEventType() :: 'move'.
--type wxMove() :: #wxMove{}. %% Callback event: {@link wxMoveEvent}
+-record(wxAuiNotebook,{type :: wxAuiNotebookEventType(), %% Callback event: {@link wxAuiNotebookEvent}
+ old_selection :: integer(),
+ selection :: integer(),
+ drag_source :: wxAuiNotebook:wxAuiNotebook()}).
+-type wxAuiNotebookEventType() :: 'command_auinotebook_page_close' | 'command_auinotebook_page_changed' | 'command_auinotebook_page_changing' | 'command_auinotebook_button' | 'command_auinotebook_begin_drag' | 'command_auinotebook_end_drag' | 'command_auinotebook_drag_motion' | 'command_auinotebook_allow_dnd' | 'command_auinotebook_tab_middle_down' | 'command_auinotebook_tab_middle_up' | 'command_auinotebook_tab_right_down' | 'command_auinotebook_tab_right_up' | 'command_auinotebook_page_closed' | 'command_auinotebook_drag_done' | 'command_auinotebook_bg_dclick'.
+-type wxAuiNotebook() :: #wxAuiNotebook{}. %% Callback event: {@link wxAuiNotebookEvent}
-record(wxList,{type :: wxListEventType(), %% Callback event: {@link wxListEvent}
code :: integer(),
@@ -201,9 +194,9 @@
-type wxListEventType() :: 'command_list_begin_drag' | 'command_list_begin_rdrag' | 'command_list_begin_label_edit' | 'command_list_end_label_edit' | 'command_list_delete_item' | 'command_list_delete_all_items' | 'command_list_key_down' | 'command_list_insert_item' | 'command_list_col_click' | 'command_list_col_right_click' | 'command_list_col_begin_drag' | 'command_list_col_dragging' | 'command_list_col_end_drag' | 'command_list_item_selected' | 'command_list_item_deselected' | 'command_list_item_right_click' | 'command_list_item_middle_click' | 'command_list_item_activated' | 'command_list_item_focused' | 'command_list_cache_hint'.
-type wxList() :: #wxList{}. %% Callback event: {@link wxListEvent}
--record(wxClipboardText, {type :: wxClipboardTextEventType()}). %% Callback event: {@link wxClipboardTextEvent}
--type wxClipboardTextEventType() :: 'command_text_copy' | 'command_text_cut' | 'command_text_paste'.
--type wxClipboardText() :: #wxClipboardText{}. %% Callback event: {@link wxClipboardTextEvent}
+-record(wxUpdateUI, {type :: wxUpdateUIEventType()}). %% Callback event: {@link wxUpdateUIEvent}
+-type wxUpdateUIEventType() :: 'update_ui'.
+-type wxUpdateUI() :: #wxUpdateUI{}. %% Callback event: {@link wxUpdateUIEvent}
-record(wxScrollWin,{type :: wxScrollWinEventType(), %% Callback event: {@link wxScrollWinEvent}
commandInt :: integer(),
@@ -211,92 +204,81 @@
-type wxScrollWinEventType() :: 'scrollwin_top' | 'scrollwin_bottom' | 'scrollwin_lineup' | 'scrollwin_linedown' | 'scrollwin_pageup' | 'scrollwin_pagedown' | 'scrollwin_thumbtrack' | 'scrollwin_thumbrelease'.
-type wxScrollWin() :: #wxScrollWin{}. %% Callback event: {@link wxScrollWinEvent}
--record(wxIconize,{type :: wxIconizeEventType(), %% Callback event: {@link wxIconizeEvent}
- iconized :: boolean()}).
--type wxIconizeEventType() :: 'iconize'.
--type wxIconize() :: #wxIconize{}. %% Callback event: {@link wxIconizeEvent}
+-record(wxJoystick,{type :: wxJoystickEventType(), %% Callback event: {@link wxJoystickEvent}
+ pos :: {X::integer(), Y::integer()},
+ zPosition :: integer(),
+ buttonChange :: integer(),
+ buttonState :: integer(),
+ joyStick :: integer()}).
+-type wxJoystickEventType() :: 'joy_button_down' | 'joy_button_up' | 'joy_move' | 'joy_zmove'.
+-type wxJoystick() :: #wxJoystick{}. %% Callback event: {@link wxJoystickEvent}
--record(wxUpdateUI, {type :: wxUpdateUIEventType()}). %% Callback event: {@link wxUpdateUIEvent}
--type wxUpdateUIEventType() :: 'update_ui'.
--type wxUpdateUI() :: #wxUpdateUI{}. %% Callback event: {@link wxUpdateUIEvent}
+-record(wxWindowCreate, {type :: wxWindowCreateEventType()}). %% Callback event: {@link wxWindowCreateEvent}
+-type wxWindowCreateEventType() :: 'create'.
+-type wxWindowCreate() :: #wxWindowCreate{}. %% Callback event: {@link wxWindowCreateEvent}
--record(wxMouse,{type :: wxMouseEventType(), %% Callback event: {@link wxMouseEvent}
+-record(wxClose, {type :: wxCloseEventType()}). %% Callback event: {@link wxCloseEvent}
+-type wxCloseEventType() :: 'close_window' | 'end_session' | 'query_end_session'.
+-type wxClose() :: #wxClose{}. %% Callback event: {@link wxCloseEvent}
+
+-record(wxKey,{type :: wxKeyEventType(), %% Callback event: {@link wxKeyEvent}
x :: integer(),
y :: integer(),
- leftDown :: boolean(),
- middleDown :: boolean(),
- rightDown :: boolean(),
+ keyCode :: integer(),
controlDown :: boolean(),
shiftDown :: boolean(),
altDown :: boolean(),
metaDown :: boolean(),
- wheelRotation :: integer(),
- wheelDelta :: integer(),
- linesPerAction :: integer()}).
--type wxMouseEventType() :: 'left_down' | 'left_up' | 'middle_down' | 'middle_up' | 'right_down' | 'right_up' | 'motion' | 'enter_window' | 'leave_window' | 'left_dclick' | 'middle_dclick' | 'right_dclick' | 'mousewheel'.
--type wxMouse() :: #wxMouse{}. %% Callback event: {@link wxMouseEvent}
-
--record(wxTree,{type :: wxTreeEventType(), %% Callback event: {@link wxTreeEvent}
- item :: integer(),
- itemOld :: integer(),
- pointDrag :: {X::integer(), Y::integer()}}).
--type wxTreeEventType() :: 'command_tree_begin_drag' | 'command_tree_begin_rdrag' | 'command_tree_begin_label_edit' | 'command_tree_end_label_edit' | 'command_tree_delete_item' | 'command_tree_get_info' | 'command_tree_set_info' | 'command_tree_item_expanded' | 'command_tree_item_expanding' | 'command_tree_item_collapsed' | 'command_tree_item_collapsing' | 'command_tree_sel_changed' | 'command_tree_sel_changing' | 'command_tree_key_down' | 'command_tree_item_activated' | 'command_tree_item_right_click' | 'command_tree_item_middle_click' | 'command_tree_end_drag' | 'command_tree_state_image_click' | 'command_tree_item_gettooltip' | 'command_tree_item_menu'.
--type wxTree() :: #wxTree{}. %% Callback event: {@link wxTreeEvent}
-
--record(wxSysColourChanged, {type :: wxSysColourChangedEventType()}). %% Callback event: {@link wxSysColourChangedEvent}
--type wxSysColourChangedEventType() :: 'sys_colour_changed'.
--type wxSysColourChanged() :: #wxSysColourChanged{}. %% Callback event: {@link wxSysColourChangedEvent}
+ scanCode :: boolean(),
+ uniChar :: integer(),
+ rawCode :: integer(),
+ rawFlags :: integer()}).
+-type wxKeyEventType() :: 'char' | 'char_hook' | 'key_down' | 'key_up'.
+-type wxKey() :: #wxKey{}. %% Callback event: {@link wxKeyEvent}
--record(wxNavigationKey,{type :: wxNavigationKeyEventType(), %% Callback event: {@link wxNavigationKeyEvent}
- flags :: integer(),
- focus :: wxWindow:wxWindow()}).
--type wxNavigationKeyEventType() :: 'navigation_key'.
--type wxNavigationKey() :: #wxNavigationKey{}. %% Callback event: {@link wxNavigationKeyEvent}
+-record(wxIdle, {type :: wxIdleEventType()}). %% Callback event: {@link wxIdleEvent}
+-type wxIdleEventType() :: 'idle'.
+-type wxIdle() :: #wxIdle{}. %% Callback event: {@link wxIdleEvent}
-record(wxQueryNewPalette, {type :: wxQueryNewPaletteEventType()}). %% Callback event: {@link wxQueryNewPaletteEvent}
-type wxQueryNewPaletteEventType() :: 'query_new_palette'.
-type wxQueryNewPalette() :: #wxQueryNewPalette{}. %% Callback event: {@link wxQueryNewPaletteEvent}
--record(wxMouseCaptureChanged, {type :: wxMouseCaptureChangedEventType()}). %% Callback event: {@link wxMouseCaptureChangedEvent}
--type wxMouseCaptureChangedEventType() :: 'mouse_capture_changed'.
--type wxMouseCaptureChanged() :: #wxMouseCaptureChanged{}. %% Callback event: {@link wxMouseCaptureChangedEvent}
-
-record(wxHtmlLink,{type :: wxHtmlLinkEventType(), %% Callback event: {@link wxHtmlLinkEvent}
linkInfo :: wx:wx_wxHtmlLinkInfo()}).
-type wxHtmlLinkEventType() :: 'command_html_link_clicked'.
-type wxHtmlLink() :: #wxHtmlLink{}. %% Callback event: {@link wxHtmlLinkEvent}
--record(wxKey,{type :: wxKeyEventType(), %% Callback event: {@link wxKeyEvent}
- x :: integer(),
- y :: integer(),
- keyCode :: integer(),
- controlDown :: boolean(),
- shiftDown :: boolean(),
- altDown :: boolean(),
- metaDown :: boolean(),
- scanCode :: boolean(),
- uniChar :: integer(),
- rawCode :: integer(),
- rawFlags :: integer()}).
--type wxKeyEventType() :: 'char' | 'char_hook' | 'key_down' | 'key_up'.
--type wxKey() :: #wxKey{}. %% Callback event: {@link wxKeyEvent}
+-record(wxInitDialog, {type :: wxInitDialogEventType()}). %% Callback event: {@link wxInitDialogEvent}
+-type wxInitDialogEventType() :: 'init_dialog'.
+-type wxInitDialog() :: #wxInitDialog{}. %% Callback event: {@link wxInitDialogEvent}
--record(wxTaskBarIcon, {type :: wxTaskBarIconEventType()}). %% Callback event: {@link wxTaskBarIconEvent}
--type wxTaskBarIconEventType() :: 'taskbar_move' | 'taskbar_left_down' | 'taskbar_left_up' | 'taskbar_right_down' | 'taskbar_right_up' | 'taskbar_left_dclick' | 'taskbar_right_dclick'.
--type wxTaskBarIcon() :: #wxTaskBarIcon{}. %% Callback event: {@link wxTaskBarIconEvent}
+-record(wxMaximize, {type :: wxMaximizeEventType()}). %% Callback event: {@link wxMaximizeEvent}
+-type wxMaximizeEventType() :: 'maximize'.
+-type wxMaximize() :: #wxMaximize{}. %% Callback event: {@link wxMaximizeEvent}
--record(wxGrid,{type :: wxGridEventType(), %% Callback event: {@link wxGridEvent}
- row :: integer(),
- col :: integer(),
- x :: integer(),
- y :: integer(),
- selecting :: boolean(),
- control :: boolean(),
- meta :: boolean(),
- shift :: boolean(),
- alt :: boolean()}).
--type wxGridEventType() :: 'grid_cell_left_click' | 'grid_cell_right_click' | 'grid_cell_left_dclick' | 'grid_cell_right_dclick' | 'grid_label_left_click' | 'grid_label_right_click' | 'grid_label_left_dclick' | 'grid_label_right_dclick' | 'grid_row_size' | 'grid_col_size' | 'grid_range_select' | 'grid_cell_change' | 'grid_select_cell' | 'grid_editor_shown' | 'grid_editor_hidden' | 'grid_editor_created' | 'grid_cell_begin_drag'.
--type wxGrid() :: #wxGrid{}. %% Callback event: {@link wxGridEvent}
+-record(wxClipboardText, {type :: wxClipboardTextEventType()}). %% Callback event: {@link wxClipboardTextEvent}
+-type wxClipboardTextEventType() :: 'command_text_copy' | 'command_text_cut' | 'command_text_paste'.
+-type wxClipboardText() :: #wxClipboardText{}. %% Callback event: {@link wxClipboardTextEvent}
+
+-record(wxTree,{type :: wxTreeEventType(), %% Callback event: {@link wxTreeEvent}
+ item :: integer(),
+ itemOld :: integer(),
+ pointDrag :: {X::integer(), Y::integer()}}).
+-type wxTreeEventType() :: 'command_tree_begin_drag' | 'command_tree_begin_rdrag' | 'command_tree_begin_label_edit' | 'command_tree_end_label_edit' | 'command_tree_delete_item' | 'command_tree_get_info' | 'command_tree_set_info' | 'command_tree_item_expanded' | 'command_tree_item_expanding' | 'command_tree_item_collapsed' | 'command_tree_item_collapsing' | 'command_tree_sel_changed' | 'command_tree_sel_changing' | 'command_tree_key_down' | 'command_tree_item_activated' | 'command_tree_item_right_click' | 'command_tree_item_middle_click' | 'command_tree_end_drag' | 'command_tree_state_image_click' | 'command_tree_item_gettooltip' | 'command_tree_item_menu'.
+-type wxTree() :: #wxTree{}. %% Callback event: {@link wxTreeEvent}
+
+-record(wxErase,{type :: wxEraseEventType(), %% Callback event: {@link wxEraseEvent}
+ dc :: wxDC:wxDC()}).
+-type wxEraseEventType() :: 'erase_background'.
+-type wxErase() :: #wxErase{}. %% Callback event: {@link wxEraseEvent}
+
+-record(wxSash,{type :: wxSashEventType(), %% Callback event: {@link wxSashEvent}
+ edge :: wx:wx_enum(),
+ dragRect :: {X::integer(), Y::integer(), W::integer(), H::integer()},
+ dragStatus :: wx:wx_enum()}).
+-type wxSashEventType() :: 'sash_dragged'.
+-type wxSash() :: #wxSash{}. %% Callback event: {@link wxSashEvent}
-record(wxCalendar,{type :: wxCalendarEventType(), %% Callback event: {@link wxCalendarEvent}
wday :: wx:wx_enum(),
@@ -304,42 +286,52 @@
-type wxCalendarEventType() :: 'calendar_sel_changed' | 'calendar_day_changed' | 'calendar_month_changed' | 'calendar_year_changed' | 'calendar_doubleclicked' | 'calendar_weekday_clicked'.
-type wxCalendar() :: #wxCalendar{}. %% Callback event: {@link wxCalendarEvent}
--record(wxWindowCreate, {type :: wxWindowCreateEventType()}). %% Callback event: {@link wxWindowCreateEvent}
--type wxWindowCreateEventType() :: 'create'.
--type wxWindowCreate() :: #wxWindowCreate{}. %% Callback event: {@link wxWindowCreateEvent}
+-record(wxMouse,{type :: wxMouseEventType(), %% Callback event: {@link wxMouseEvent}
+ x :: integer(),
+ y :: integer(),
+ leftDown :: boolean(),
+ middleDown :: boolean(),
+ rightDown :: boolean(),
+ controlDown :: boolean(),
+ shiftDown :: boolean(),
+ altDown :: boolean(),
+ metaDown :: boolean(),
+ wheelRotation :: integer(),
+ wheelDelta :: integer(),
+ linesPerAction :: integer()}).
+-type wxMouseEventType() :: 'left_down' | 'left_up' | 'middle_down' | 'middle_up' | 'right_down' | 'right_up' | 'motion' | 'enter_window' | 'leave_window' | 'left_dclick' | 'middle_dclick' | 'right_dclick' | 'mousewheel'.
+-type wxMouse() :: #wxMouse{}. %% Callback event: {@link wxMouseEvent}
--record(wxDate,{type :: wxDateEventType(), %% Callback event: {@link wxDateEvent}
- date :: wx:wx_datetime()}).
--type wxDateEventType() :: 'date_changed'.
--type wxDate() :: #wxDate{}. %% Callback event: {@link wxDateEvent}
+-record(wxSize,{type :: wxSizeEventType(), %% Callback event: {@link wxSizeEvent}
+ size :: {W::integer(), H::integer()},
+ rect :: {X::integer(), Y::integer(), W::integer(), H::integer()}}).
+-type wxSizeEventType() :: 'size'.
+-type wxSize() :: #wxSize{}. %% Callback event: {@link wxSizeEvent}
--record(wxAuiManager,{type :: wxAuiManagerEventType(), %% Callback event: {@link wxAuiManagerEvent}
- manager :: wxAuiManager:wxAuiManager(),
- pane :: wxAuiPaneInfo:wxAuiPaneInfo(),
- button :: integer(),
- veto_flag :: boolean(),
- canveto_flag :: boolean(),
- dc :: wxDC:wxDC()}).
--type wxAuiManagerEventType() :: 'aui_pane_button' | 'aui_pane_close' | 'aui_pane_maximize' | 'aui_pane_restore' | 'aui_pane_activated' | 'aui_render' | 'aui_find_manager'.
--type wxAuiManager() :: #wxAuiManager{}. %% Callback event: {@link wxAuiManagerEvent}
+-record(wxSysColourChanged, {type :: wxSysColourChangedEventType()}). %% Callback event: {@link wxSysColourChangedEvent}
+-type wxSysColourChangedEventType() :: 'sys_colour_changed'.
+-type wxSysColourChanged() :: #wxSysColourChanged{}. %% Callback event: {@link wxSysColourChangedEvent}
--record(wxJoystick,{type :: wxJoystickEventType(), %% Callback event: {@link wxJoystickEvent}
+-record(wxDisplayChanged, {type :: wxDisplayChangedEventType()}). %% Callback event: {@link wxDisplayChangedEvent}
+-type wxDisplayChangedEventType() :: 'display_changed'.
+-type wxDisplayChanged() :: #wxDisplayChanged{}. %% Callback event: {@link wxDisplayChangedEvent}
+
+-record(wxMove,{type :: wxMoveEventType(), %% Callback event: {@link wxMoveEvent}
pos :: {X::integer(), Y::integer()},
- zPosition :: integer(),
- buttonChange :: integer(),
- buttonState :: integer(),
- joyStick :: integer()}).
--type wxJoystickEventType() :: 'joy_button_down' | 'joy_button_up' | 'joy_move' | 'joy_zmove'.
--type wxJoystick() :: #wxJoystick{}. %% Callback event: {@link wxJoystickEvent}
+ rect :: {X::integer(), Y::integer(), W::integer(), H::integer()}}).
+-type wxMoveEventType() :: 'move'.
+-type wxMove() :: #wxMove{}. %% Callback event: {@link wxMoveEvent}
--record(wxPaint, {type :: wxPaintEventType()}). %% Callback event: {@link wxPaintEvent}
--type wxPaintEventType() :: 'paint'.
--type wxPaint() :: #wxPaint{}. %% Callback event: {@link wxPaintEvent}
+-record(wxNavigationKey,{type :: wxNavigationKeyEventType(), %% Callback event: {@link wxNavigationKeyEvent}
+ flags :: integer(),
+ focus :: wxWindow:wxWindow()}).
+-type wxNavigationKeyEventType() :: 'navigation_key'.
+-type wxNavigationKey() :: #wxNavigationKey{}. %% Callback event: {@link wxNavigationKeyEvent}
--record(wxErase,{type :: wxEraseEventType(), %% Callback event: {@link wxEraseEvent}
- dc :: wxDC:wxDC()}).
--type wxEraseEventType() :: 'erase_background'.
--type wxErase() :: #wxErase{}. %% Callback event: {@link wxEraseEvent}
+-record(wxFileDirPicker,{type :: wxFileDirPickerEventType(), %% Callback event: {@link wxFileDirPickerEvent}
+ path :: unicode:chardata()}).
+-type wxFileDirPickerEventType() :: 'command_filepicker_changed' | 'command_dirpicker_changed'.
+-type wxFileDirPicker() :: #wxFileDirPicker{}. %% Callback event: {@link wxFileDirPickerEvent}
-record(wxSetCursor,{type :: wxSetCursorEventType(), %% Callback event: {@link wxSetCursorEvent}
x :: integer(),
@@ -348,6 +340,14 @@
-type wxSetCursorEventType() :: 'set_cursor'.
-type wxSetCursor() :: #wxSetCursor{}. %% Callback event: {@link wxSetCursorEvent}
+-record(wxMouseCaptureChanged, {type :: wxMouseCaptureChangedEventType()}). %% Callback event: {@link wxMouseCaptureChangedEvent}
+-type wxMouseCaptureChangedEventType() :: 'mouse_capture_changed'.
+-type wxMouseCaptureChanged() :: #wxMouseCaptureChanged{}. %% Callback event: {@link wxMouseCaptureChangedEvent}
+
+-record(wxMouseCaptureLost, {type :: wxMouseCaptureLostEventType()}). %% Callback event: {@link wxMouseCaptureLostEvent}
+-type wxMouseCaptureLostEventType() :: 'mouse_capture_lost'.
+-type wxMouseCaptureLost() :: #wxMouseCaptureLost{}. %% Callback event: {@link wxMouseCaptureLostEvent}
+
-type event() :: wxActivate() | wxAuiManager() | wxAuiNotebook() | wxCalendar() | wxChildFocus() | wxClipboardText() | wxClose() | wxColourPicker() | wxCommand() | wxContextMenu() | wxDate() | wxDisplayChanged() | wxErase() | wxFileDirPicker() | wxFocus() | wxFontPicker() | wxGrid() | wxHelp() | wxHtmlLink() | wxIconize() | wxIdle() | wxInitDialog() | wxJoystick() | wxKey() | wxList() | wxMaximize() | wxMenu() | wxMouse() | wxMouseCaptureChanged() | wxMouseCaptureLost() | wxMove() | wxNavigationKey() | wxNotebook() | wxPaint() | wxPaletteChanged() | wxQueryNewPalette() | wxSash() | wxScroll() | wxScrollWin() | wxSetCursor() | wxShow() | wxSize() | wxSpin() | wxSplitter() | wxStyledText() | wxSysColourChanged() | wxTaskBarIcon() | wxTree() | wxUpdateUI() | wxWindowCreate() | wxWindowDestroy().
-type wxEventType() :: wxActivateEventType() | wxAuiManagerEventType() | wxAuiNotebookEventType() | wxCalendarEventType() | wxChildFocusEventType() | wxClipboardTextEventType() | wxCloseEventType() | wxColourPickerEventType() | wxCommandEventType() | wxContextMenuEventType() | wxDateEventType() | wxDisplayChangedEventType() | wxEraseEventType() | wxFileDirPickerEventType() | wxFocusEventType() | wxFontPickerEventType() | wxGridEventType() | wxHelpEventType() | wxHtmlLinkEventType() | wxIconizeEventType() | wxIdleEventType() | wxInitDialogEventType() | wxJoystickEventType() | wxKeyEventType() | wxListEventType() | wxMaximizeEventType() | wxMenuEventType() | wxMouseCaptureChangedEventType() | wxMouseCaptureLostEventType() | wxMouseEventType() | wxMoveEventType() | wxNavigationKeyEventType() | wxNotebookEventType() | wxPaintEventType() | wxPaletteChangedEventType() | wxQueryNewPaletteEventType() | wxSashEventType() | wxScrollEventType() | wxScrollWinEventType() | wxSetCursorEventType() | wxShowEventType() | wxSizeEventType() | wxSpinEventType() | wxSplitterEventType() | wxStyledTextEventType() | wxSysColourChangedEventType() | wxTaskBarIconEventType() | wxTreeEventType() | wxUpdateUIEventType() | wxWindowCreateEventType() | wxWindowDestroyEventType().
diff --git a/lib/wx/src/gen/wxListCtrl.erl b/lib/wx/src/gen/wxListCtrl.erl
index d1a063d900..851686062a 100644
--- a/lib/wx/src/gen/wxListCtrl.erl
+++ b/lib/wx/src/gen/wxListCtrl.erl
@@ -94,31 +94,34 @@ parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
-type wxListCtrl() :: wx:wx_object().
-%% @spec () -> wxListCtrl()
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlwxlistctrl">external documentation</a>.
+-spec new() -> wxListCtrl().
new() ->
wxe_util:construct(?wxListCtrl_new_0, <<>>).
-%% @spec (Parent::wxWindow:wxWindow()) -> wxListCtrl()
-%% @equiv new(Parent, [])
+-spec new(Parent) -> wxListCtrl() when
+ Parent::wxWindow:wxWindow().
new(Parent)
when is_record(Parent, wx_ref) ->
new(Parent, []).
-%% @spec (Parent::wxWindow:wxWindow(), [Option]) -> wxListCtrl()
-%% Option = {winid, integer()} |
-%% {pos, {X::integer(),Y::integer()}} |
-%% {size, {W::integer(),H::integer()}} |
-%% {style, integer()} |
-%% {validator, wx:wx()} |
-%% {onGetItemText, OnGetItemText} |
-%% {onGetItemAttr, OnGetItemAttr} |
-%% {onGetItemColumnImage, OnGetItemColumnImage}
+%% @doc Creates a listctrl with optional callback functions:
%%
-%% OnGetItemText = (This, Item, Column) -> wxString()
-%% OnGetItemAttr = (This, Item) -> wxListItemAttr()
+%% OnGetItemText = (This, Item, Column) -> unicode:charlist()
+%% OnGetItemAttr = (This, Item) -> wxListItemAttr:wxListItemAttr()
%% OnGetItemColumnImage = (This, Item, Column) -> integer()
-%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlwxlistctrl">external documentation</a>.
+%%
+%% See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlwxlistctrl">external documentation</a>.
+-spec new(Parent, [Option]) -> wxListCtrl() when
+ Parent::wxWindow:wxWindow(),
+ Option::{winid, integer()} |
+ {pos, {X::integer(),Y::integer()}} |
+ {size, {W::integer(),H::integer()}} |
+ {style, integer()} |
+ {validator, wx:wx_object()} |
+ {onGetItemText, function()} |
+ {onGetItemAttr, function()} |
+ {onGetItemColumnImage, function()}.
new(#wx_ref{type=ParentT,ref=ParentRef}, Options)
when is_list(Options)->
@@ -185,26 +188,27 @@ clearAll(#wx_ref{type=ThisT,ref=ThisRef}) ->
<<ThisRef:32/?UI>>).
-%% @spec (This::wxListCtrl(), Parent::wxWindow:wxWindow()) -> bool()
%% @equiv create(This,Parent, [])
+-spec create(This, Parent) -> wxListCtrl() when
+ This::wxWindow:wxWindow(),
+ Parent::wxWindow:wxWindow().
create(This,Parent)
when is_record(This, wx_ref),is_record(Parent, wx_ref) ->
create(This,Parent, []).
-%% @spec (This::wxListCtrl(), Parent::wxWindow:wxWindow(), [Option]) -> bool()
-%% Option = {winid, integer()} |
-%% {pos, {X::integer(),Y::integer()}} |
-%% {size, {W::integer(),H::integer()}} |
-%% {style, integer()} |
-%% {validator, wx:wx()} |
-%% {onGetItemText, OnGetItemText} |
-%% {onGetItemAttr, OnGetItemAttr} |
-%% {onGetItemColumnImage, OnGetItemColumnImage}
-%%
-%% OnGetItemText = (This, Item, Column) -> wxString()
-%% OnGetItemAttr = (This, Item) -> wxListItemAttr()
-%% OnGetItemColumnImage = (This, Item, Column) -> integer()
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlcreate">external documentation</a>.
+-spec create(This, Parent, [Option]) -> wxListCtrl() when
+ This::wxWindow:wxWindow(),
+ Parent::wxWindow:wxWindow(),
+ Option::{winid, integer()} |
+ {pos, {X::integer(),Y::integer()}} |
+ {size, {W::integer(),H::integer()}} |
+ {style, integer()} |
+ {validator, wx:wx_object()} |
+ {onGetItemText, function()} |
+ {onGetItemAttr, function()} |
+ {onGetItemColumnImage, function()}.
+
create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef}, Options)
when is_list(Options) ->
?CLASS(ThisT,wxListCtrl),
diff --git a/lib/wx/src/gen/wxXmlResource.erl b/lib/wx/src/gen/wxXmlResource.erl
index ae02c74751..aa65b8b04e 100644
--- a/lib/wx/src/gen/wxXmlResource.erl
+++ b/lib/wx/src/gen/wxXmlResource.erl
@@ -334,8 +334,6 @@ unload(#wx_ref{type=ThisT,ref=ThisRef},Filename)
<<ThisRef:32/?UI,(byte_size(Filename_UC)):32/?UI,(Filename_UC)/binary, 0:(((8- ((0+byte_size(Filename_UC)) band 16#7)) band 16#7))/unit:8>>).
-%% @spec (Window::wxWindow:wxWindow(),Name::string(), Type::atom()) -> wx:wxObject()
-
%% @doc Looks up a control with Name in a window created with XML
%% resources. You can use it to set/get values from controls.
%% The object is type casted to <b>Type</b>.
@@ -345,6 +343,10 @@ unload(#wx_ref{type=ThisT,ref=ThisRef},Filename)
%% true = wxXmlResource:loadDialog(Xrc, Dlg, Frame, "controls_dialog"), <br />
%% LCtrl = xrcctrl(Dlg, "controls_listctrl", wxListCtrl), <br />
%% wxListCtrl:insertColumn(LCtrl, 0, "Name", [{width, 200}]), <br />
+-spec xrcctrl(Window, Name, Type) -> wx:wx_object() when
+ Window::wxWindow:wxWindow(),
+ Name::string(),
+ Type::atom().
xrcctrl(Window = #wx_ref{}, Name, Type) when is_list(Name), is_atom(Type) ->
%% Func Id ?wxXmlResource_xrcctrl
diff --git a/lib/wx/src/wx_object.erl b/lib/wx/src/wx_object.erl
index 40ee308358..40170b6eb1 100644
--- a/lib/wx/src/wx_object.erl
+++ b/lib/wx/src/wx_object.erl
@@ -55,7 +55,7 @@
%% When stop is returned in one of the functions above with Reason =
%% normal | shutdown | Term, terminate(State) is called. It lets the
%% user module clean up, it is always called when server terminates or
-%% when wxObject() in the driver is deleted. If the Parent process
+%% when wx_object() in the driver is deleted. If the Parent process
%% terminates the Module:terminate/2 function is called. <br/>
%% terminate(Reason, State)
%%
@@ -171,58 +171,59 @@
%% -----------------------------------------------------------------
-%% @spec (Mod, Args, Options) -> wxWindow:wxWindow()
-%% Mod = atom()
-%% Args = term()
-%% Options = [{timeout, Timeout} | {debug, [Flag]}]
-%% Flag = trace | log | {logfile, File} | statistics | debug
%% @doc Starts a generic wx_object server and invokes Mod:init(Args) in the
%% new process.
+-spec start(Mod, Args, Options) -> wxWindow:wxWindow() | {error, term()} when
+ Mod::atom(),
+ Args::term(),
+ Flag::trace | log | {logfile, string()} | statistics | debug,
+ Options::[{timeout, timeout()} | {debug, [Flag]}].
start(Mod, Args, Options) ->
gen_response(gen:start(?MODULE, nolink, Mod, Args, [get(?WXE_IDENTIFIER)|Options])).
-
-%% @spec (Name, Mod, Args, Options) -> wxWindow:wxWindow()
-%% Name = {local, atom()}
-%% Mod = atom()
-%% Args = term()
-%% Options = [{timeout, Timeout} | {debug, [Flag]}]
-%% Flag = trace | log | {logfile, File} | statistics | debug
+
%% @doc Starts a generic wx_object server and invokes Mod:init(Args) in the
%% new process.
+-spec start(Name, Mod, Args, Options) -> wxWindow:wxWindow() | {error, term()} when
+ Name::{local, atom()},
+ Mod::atom(),
+ Args::term(),
+ Flag::trace | log | {logfile, string()} | statistics | debug,
+ Options::[{timeout, timeout()} | {debug, [Flag]}].
start(Name, Mod, Args, Options) ->
gen_response(gen:start(?MODULE, nolink, Name, Mod, Args, [get(?WXE_IDENTIFIER)|Options])).
-%% @spec (Mod, Args, Options) -> wxWindow:wxWindow()
-%% Mod = atom()
-%% Args = term()
-%% Options = [{timeout, Timeout} | {debug, [Flag]}]
-%% Flag = trace | log | {logfile, File} | statistics | debug
%% @doc Starts a generic wx_object server and invokes Mod:init(Args) in the
%% new process.
+-spec start_link(Mod, Args, Options) -> wxWindow:wxWindow() | {error, term()} when
+ Mod::atom(),
+ Args::term(),
+ Flag::trace | log | {logfile, string()} | statistics | debug,
+ Options::[{timeout, timeout()} | {debug, [Flag]}].
start_link(Mod, Args, Options) ->
gen_response(gen:start(?MODULE, link, Mod, Args, [get(?WXE_IDENTIFIER)|Options])).
-%% @spec (Name, Mod, Args, Options) -> wxWindow:wxWindow()
-%% Name = {local, atom()}
-%% Mod = atom()
-%% Args = term()
-%% Options = [{timeout, Timeout} | {debug, [Flag]}]
-%% Flag = trace | log | {logfile, File} | statistics | debug
%% @doc Starts a generic wx_object server and invokes Mod:init(Args) in the
%% new process.
+-spec start_link(Name, Mod, Args, Options) -> wxWindow:wxWindow() | {error, term()} when
+ Name::{local, atom()},
+ Mod::atom(),
+ Args::term(),
+ Flag::trace | log | {logfile, string()} | statistics | debug,
+ Options::[{timeout, timeout()} | {debug, [Flag]}].
start_link(Name, Mod, Args, Options) ->
gen_response(gen:start(?MODULE, link, Name, Mod, Args, [get(?WXE_IDENTIFIER)|Options])).
-
+
gen_response({ok, Pid}) ->
receive {ack, Pid, Ref = #wx_ref{}} -> Ref end;
gen_response(Reply) ->
Reply.
-%% @spec (Ref::wxObject()|atom()|pid()) -> ok
%% @doc Stops a generic wx_object server with reason 'normal'.
%% Invokes terminate(Reason,State) in the server. The call waits until
%% the process is terminated. If the process does not exist, an
%% exception is raised.
+-spec stop(Obj) -> ok when
+ Obj::wx:wx_object()|atom()|pid().
stop(Ref = #wx_ref{state=Pid}) when is_pid(Pid) ->
try
gen:stop(Pid)
@@ -236,11 +237,14 @@ stop(Name) when is_atom(Name) orelse is_pid(Name) ->
erlang:error({ExitReason, {?MODULE, stop, [Name]}})
end.
-%% @spec (Ref::wxObject()|atom()|pid(), Reason::term(), Timeout::timeout()) -> ok
%% @doc Stops a generic wx_object server with the given Reason.
%% Invokes terminate(Reason,State) in the server. The call waits until
%% the process is terminated. If the call times out, or if the process
%% does not exist, an exception is raised.
+-spec stop(Obj, Reason, Timeout) -> ok when
+ Obj::wx:wx_object()|atom()|pid(),
+ Reason::term(),
+ Timeout::timeout().
stop(Ref = #wx_ref{state=Pid}, Reason, Timeout) when is_pid(Pid) ->
try
gen:stop(Pid, Reason, Timeout)
@@ -254,12 +258,14 @@ stop(Name, Reason, Timeout) when is_atom(Name) orelse is_pid(Name) ->
erlang:error({ExitReason, {?MODULE, stop, [Name, Reason, Timeout]}})
end.
-%% @spec (Ref::wxObject()|atom()|pid(), Request::term()) -> term()
%% @doc Make a call to a wx_object server.
%% The call waits until it gets a result.
%% Invokes handle_call(Request, From, State) in the server
+-spec call(Obj, Request) -> term() when
+ Obj::wx:wx_object()|atom()|pid(),
+ Request::term().
call(Ref = #wx_ref{state=Pid}, Request) when is_pid(Pid) ->
- try
+ try
{ok,Res} = gen:call(Pid, '$gen_call', Request, infinity),
Res
catch _:Reason ->
@@ -272,10 +278,13 @@ call(Name, Request) when is_atom(Name) orelse is_pid(Name) ->
catch _:Reason ->
erlang:error({Reason, {?MODULE, call, [Name, Request]}})
end.
-
-%% @spec (Ref::wxObject()|atom()|pid(), Request::term(), Timeout::integer()) -> term()
+
%% @doc Make a call to a wx_object server with a timeout.
%% Invokes handle_call(Request, From, State) in server
+-spec call(Obj, Request, Timeout) -> term() when
+ Obj::wx:wx_object()|atom()|pid(),
+ Request::term(),
+ Timeout::integer().
call(Ref = #wx_ref{state=Pid}, Request, Timeout) when is_pid(Pid) ->
try
{ok,Res} = gen:call(Pid, '$gen_call', Request, Timeout),
@@ -291,10 +300,11 @@ call(Name, Request, Timeout) when is_atom(Name) orelse is_pid(Name) ->
erlang:error({Reason, {?MODULE, call, [Name, Request, Timeout]}})
end.
-%% @spec (Ref::wxObject()|atom()|pid(), Request::term()) -> ok
%% @doc Make a cast to a wx_object server.
%% Invokes handle_cast(Request, State) in the server
-
+-spec cast(Obj, Request) -> ok when
+ Obj::wx:wx_object()|atom()|pid(),
+ Request::term().
cast(#wx_ref{state=Pid}, Request) when is_pid(Pid) ->
Pid ! {'$gen_cast',Request},
ok;
@@ -302,21 +312,23 @@ cast(Name, Request) when is_atom(Name) orelse is_pid(Name) ->
Name ! {'$gen_cast',Request},
ok.
-%% @spec (Ref::wxObject()) -> pid()
%% @doc Get the pid of the object handle.
+-spec get_pid(Obj) -> pid() when
+ Obj::wx:wx_object()|atom()|pid().
get_pid(#wx_ref{state=Pid}) when is_pid(Pid) ->
Pid.
-%% @spec (Ref::wxObject(), pid()) -> wxObject()
%% @doc Sets the controlling process of the object handle.
+-spec set_pid(Obj, pid()) -> wx:wx_object() when
+ Obj::wx:wx_object()|atom()|pid().
set_pid(#wx_ref{}=R, Pid) when is_pid(Pid) ->
R#wx_ref{state=Pid}.
%% -----------------------------------------------------------------
%% Send a reply to the client.
%% -----------------------------------------------------------------
-%% @spec (From::tuple(), Reply::term()) -> pid()
%% @doc Get the pid of the object handle.
+-spec reply({pid(), Tag::term()}, Reply::term()) -> pid().
reply({To, Tag}, Reply) ->
catch To ! {Tag, Reply}.