aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/asn1/src/asn1ct_gen_ber_bin_v2.erl2
-rw-r--r--lib/common_test/doc/src/ct_hooks_chapter.xml2
-rw-r--r--lib/common_test/src/ct.erl27
-rw-r--r--lib/common_test/src/ct_event.erl16
-rw-r--r--lib/common_test/src/cth_surefire.erl53
-rw-r--r--lib/compiler/doc/src/compile.xml6
-rw-r--r--lib/compiler/src/compile.erl28
-rw-r--r--lib/compiler/test/compile_SUITE.erl35
-rw-r--r--lib/edoc/src/edoc_data.erl5
-rw-r--r--lib/edoc/src/edoc_lib.erl4
-rw-r--r--lib/edoc/vsn.mk2
-rw-r--r--lib/erl_docgen/priv/xsl/db_html.xsl21
-rw-r--r--lib/erl_docgen/priv/xsl/db_man.xsl26
-rw-r--r--lib/erl_docgen/priv/xsl/db_pdf.xsl20
-rw-r--r--lib/eunit/doc/overview.edoc8
-rw-r--r--lib/eunit/include/eunit.hrl13
-rw-r--r--lib/eunit/src/eunit.app.src1
-rw-r--r--lib/eunit/src/eunit.erl7
-rw-r--r--lib/eunit/src/eunit_data.erl39
-rw-r--r--lib/eunit/src/eunit_lib.erl94
-rw-r--r--lib/eunit/src/eunit_surefire.erl32
-rw-r--r--lib/eunit/src/eunit_test.erl26
-rw-r--r--lib/eunit/src/eunit_tty.erl59
-rw-r--r--lib/eunit/vsn.mk2
-rw-r--r--lib/ic/doc/src/ic_clib.xml14
-rw-r--r--lib/kernel/doc/src/inet.xml2
-rw-r--r--lib/kernel/examples/uds_dist/c_src/uds_drv.c4
-rw-r--r--lib/kernel/src/code.erl13
-rw-r--r--lib/kernel/src/code_server.erl4
-rw-r--r--lib/kernel/src/rpc.erl2
-rw-r--r--lib/kernel/test/erl_prim_loader_SUITE.erl8
-rw-r--r--lib/os_mon/c_src/cpu_sup.c14
-rw-r--r--lib/os_mon/c_src/memsup.c25
-rw-r--r--lib/os_mon/c_src/win32sysinfo.c30
-rw-r--r--lib/runtime_tools/c_src/trace_ip_drv.c8
-rw-r--r--lib/stdlib/src/escript.erl18
-rw-r--r--lib/stdlib/src/gen_server.erl2
-rw-r--r--lib/stdlib/test/escript_SUITE.erl149
-rw-r--r--lib/stdlib/test/escript_SUITE_data/archive_script/archive_script_main2.erl17
-rw-r--r--lib/stdlib/test/escript_SUITE_data/archive_script_file_access/archive_script_file_access.erl105
-rw-r--r--lib/stdlib/test/gen_server_SUITE.erl22
41 files changed, 784 insertions, 181 deletions
diff --git a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl
index 597fb0030b..3ccfca3784 100644
--- a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl
+++ b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl
@@ -419,7 +419,7 @@ gen_decode_selected(Erules,Type,FuncName) ->
" {Tlv,_} = ?RT_BER:decode(Bin2",asn1ct_gen:nif_parameter(),"),",nl]),
emit("{ok,"),
gen_decode_selected_type(Erules,Type),
- emit(["};",nl," Err -> exit({error,{selctive_decode,Err}})",nl,
+ emit(["};",nl," Err -> exit({error,{selective_decode,Err}})",nl,
" end.",nl]).
gen_decode_selected_type(_Erules,TypeDef) ->
diff --git a/lib/common_test/doc/src/ct_hooks_chapter.xml b/lib/common_test/doc/src/ct_hooks_chapter.xml
index 1dbb841fb0..014507c886 100644
--- a/lib/common_test/doc/src/ct_hooks_chapter.xml
+++ b/lib/common_test/doc/src/ct_hooks_chapter.xml
@@ -453,7 +453,7 @@ terminate(State) ->
<cell>Captures all test results and outputs them as surefire XML into
a file. The file which is created is by default called junit_report.xml.
The name can be by setting the path option for this hook. e.g.
- <code>-ct_hooks cth_surefix [{path,"/tmp/report.xml"}]</code>
+ <code>-ct_hooks cth_surefire [{path,"/tmp/report.xml"}]</code>
Surefire XML can forinstance be used by Jenkins to display test
results.</cell>
</row>
diff --git a/lib/common_test/src/ct.erl b/lib/common_test/src/ct.erl
index 571d99029f..6373634812 100644
--- a/lib/common_test/src/ct.erl
+++ b/lib/common_test/src/ct.erl
@@ -66,7 +66,8 @@
capture_start/0, capture_stop/0, capture_get/0, capture_get/1,
fail/1, fail/2, comment/1, comment/2, make_priv_dir/0,
testcases/2, userdata/2, userdata/3,
- timetrap/1, get_timetrap_info/0, sleep/1]).
+ timetrap/1, get_timetrap_info/0, sleep/1,
+ notify/2, sync_notify/2]).
%% New API for manipulating with config handlers
-export([add_config/2, remove_config/2]).
@@ -1047,3 +1048,27 @@ sleep({seconds,Ss}) ->
sleep(trunc(Ss * 1000));
sleep(Time) ->
test_server:adjusted_sleep(Time).
+
+%%%-----------------------------------------------------------------
+%%% @spec notify(Name,Data) -> ok
+%%% Name = atom()
+%%% Data = term()
+%%%
+%%% @doc <p>Sends a asynchronous notification of type <c>Name</c> with
+%%% <c>Data</c>to the common_test event manager. This can later be
+%%% caught by any installed event manager. </p>
+%%% @see //stdlib/gen_event
+notify(Name,Data) ->
+ ct_event:notify(Name, Data).
+
+%%%-----------------------------------------------------------------
+%%% @spec sync_notify(Name,Data) -> ok
+%%% Name = atom()
+%%% Data = term()
+%%%
+%%% @doc <p>Sends a synchronous notification of type <c>Name</c> with
+%%% <c>Data</c>to the common_test event manager. This can later be
+%%% caught by any installed event manager. </p>
+%%% @see //stdlib/gen_event
+sync_notify(Name,Data) ->
+ ct_event:sync_notify(Name, Data).
diff --git a/lib/common_test/src/ct_event.erl b/lib/common_test/src/ct_event.erl
index 3e79898ad1..998be35fda 100644
--- a/lib/common_test/src/ct_event.erl
+++ b/lib/common_test/src/ct_event.erl
@@ -31,7 +31,7 @@
%% API
-export([start_link/0, add_handler/0, add_handler/1, stop/0]).
--export([notify/1, sync_notify/1]).
+-export([notify/1, notify/2, sync_notify/1,sync_notify/2]).
-export([is_alive/0]).
%% gen_event callbacks
@@ -90,6 +90,13 @@ notify(Event) ->
end.
%%--------------------------------------------------------------------
+%% Function: notify(Name,Data) -> ok
+%% Description: Asynchronous notification to event manager.
+%%--------------------------------------------------------------------
+notify(Name, Data) ->
+ notify(#event{ name = Name, data = Data}).
+
+%%--------------------------------------------------------------------
%% Function: sync_notify(Event) -> ok
%% Description: Synchronous notification to event manager.
%%--------------------------------------------------------------------
@@ -102,6 +109,13 @@ sync_notify(Event) ->
end.
%%--------------------------------------------------------------------
+%% Function: sync_notify(Name,Data) -> ok
+%% Description: Synchronous notification to event manager.
+%%--------------------------------------------------------------------
+sync_notify(Name,Data) ->
+ sync_notify(#event{ name = Name, data = Data}).
+
+%%--------------------------------------------------------------------
%% Function: is_alive() -> true | false
%% Description: Check if Event Manager is alive.
%%--------------------------------------------------------------------
diff --git a/lib/common_test/src/cth_surefire.erl b/lib/common_test/src/cth_surefire.erl
index c42f956b3a..d04a8b07db 100644
--- a/lib/common_test/src/cth_surefire.erl
+++ b/lib/common_test/src/cth_surefire.erl
@@ -49,9 +49,12 @@ init(Path, Opts) ->
properties = proplists:get_value(properties,Opts,[]),
timer = now() }.
-pre_init_per_suite(Suite,Config,State) ->
+pre_init_per_suite(Suite,Config,#state{ test_cases = [] } = State) ->
{Config, init_tc(State#state{ curr_suite = Suite, curr_suite_ts = now() },
- Config) }.
+ Config) };
+pre_init_per_suite(Suite,Config,State) ->
+ %% Have to close the previous suite
+ pre_init_per_suite(Suite,Config,close_suite(State)).
post_init_per_suite(_Suite,Config, Result, State) ->
{Result, end_tc(init_per_suite,Config,Result,State)}.
@@ -59,11 +62,7 @@ post_init_per_suite(_Suite,Config, Result, State) ->
pre_end_per_suite(_Suite,Config,State) -> {Config, init_tc(State, Config)}.
post_end_per_suite(_Suite,Config,Result,State) ->
- NewState = end_tc(end_per_suite,Config,Result,State),
- TCs = NewState#state.test_cases,
- Suite = get_suite(NewState, TCs),
- {Result, State#state{ test_cases = [],
- test_suites = [Suite | State#state.test_suites]}}.
+ {Result, end_tc(end_per_suite,Config,Result,State)}.
pre_init_per_group(Group,Config,State) ->
{Config, init_tc(State#state{ curr_group = [Group|State#state.curr_group]},
@@ -90,7 +89,12 @@ on_tc_fail(_TC, Res, State) ->
{fail,lists:flatten(io_lib:format("~p",[Res]))} },
State#state{ test_cases = [NewTC | tl(TCs)]}.
+on_tc_skip(Tc,{Type,Reason} = Res, State) when Type == tc_auto_skip ->
+ do_tc_skip(Res, end_tc(Tc,[],Res,init_tc(State,[])));
on_tc_skip(_Tc, Res, State) ->
+ do_tc_skip(Res, State).
+
+do_tc_skip(Res, State) ->
TCs = State#state.test_cases,
TC = hd(State#state.test_cases),
NewTC = TC#testcase{
@@ -98,9 +102,11 @@ on_tc_skip(_Tc, Res, State) ->
{skipped,lists:flatten(io_lib:format("~p",[Res]))} },
State#state{ test_cases = [NewTC | tl(TCs)]}.
+init_tc(State, Config) when is_list(Config) == false ->
+ State#state{ timer = now(), tc_log = "" };
init_tc(State, Config) ->
State#state{ timer = now(),
- tc_log = proplists:get_value(tc_logfile, Config)}.
+ tc_log = proplists:get_value(tc_logfile, Config, [])}.
end_tc(Func, Config, Res, State) when is_atom(Func) ->
end_tc(atom_to_list(Func), Config, Res, State);
@@ -118,26 +124,35 @@ end_tc(Name, _Config, _Res, State = #state{ curr_suite = Suite,
name = Name,
time = TimeTakes,
failure = passed }| State#state.test_cases]}.
-
-get_suite(State, TCs) ->
+close_suite(#state{ test_cases = [] } = State) ->
+ State;
+close_suite(#state{ test_cases = TCs } = State) ->
Total = length(TCs),
Succ = length(lists:filter(fun(#testcase{ failure = F }) ->
F == passed
end,TCs)),
Fail = Total - Succ,
TimeTaken = timer:now_diff(now(),State#state.curr_suite_ts) / 1000000,
- #testsuite{ name = atom_to_list(State#state.curr_suite),
- package = State#state.package,
- time = io_lib:format("~f",[TimeTaken]),
- timestamp = now_to_string(State#state.curr_suite_ts),
- errors = Fail, tests = Total, testcases = lists:reverse(TCs) }.
-
-terminate(State) ->
- {ok,D} = file:open(State#state.filepath,[write]),
+ Suite = #testsuite{ name = atom_to_list(State#state.curr_suite),
+ package = State#state.package,
+ time = io_lib:format("~f",[TimeTaken]),
+ timestamp = now_to_string(State#state.curr_suite_ts),
+ errors = Fail, tests = Total,
+ testcases = lists:reverse(TCs) },
+ State#state{ test_cases = [],
+ test_suites = [Suite | State#state.test_suites]}.
+
+terminate(State = #state{ test_cases = [] }) ->
+ {ok,D} = file:open(State#state.filepath,[write,{encoding,utf8}]),
io:format(D, "<?xml version=\"1.0\" encoding= \"UTF-8\" ?>", []),
io:format(D, to_xml(State), []),
catch file:sync(D),
- catch file:close(D).
+ catch file:close(D);
+terminate(State) ->
+ %% Have to close the last suite
+ terminate(close_suite(State)).
+
+
to_xml(#testcase{ group = Group, classname = CL, log = L, name = N, time = T, timestamp = TS, failure = F}) ->
["<testcase ",
diff --git a/lib/compiler/doc/src/compile.xml b/lib/compiler/doc/src/compile.xml
index b577e5ca4f..be9eb1cd75 100644
--- a/lib/compiler/doc/src/compile.xml
+++ b/lib/compiler/doc/src/compile.xml
@@ -294,6 +294,12 @@ module.beam: module.erl \
describing what it is doing.</p>
</item>
+ <tag><c>{source,FileName}</c></tag>
+ <item>
+ <p>Sets the value of the source, as returned by
+ <c>module_info(compile)</c>.</p>
+ </item>
+
<tag><c>{outdir,Dir}</c></tag>
<item>
<p>Sets a new directory for the object code. The current
diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl
index 9b505ad15c..7365706b94 100644
--- a/lib/compiler/src/compile.erl
+++ b/lib/compiler/src/compile.erl
@@ -146,10 +146,17 @@ env_default_opts() ->
do_compile(Input, Opts0) ->
Opts = expand_opts(Opts0),
- Self = self(),
- Serv = spawn_link(fun() -> internal(Self, Input, Opts) end),
+ {Pid,Ref} =
+ spawn_monitor(fun() ->
+ exit(try
+ internal(Input, Opts)
+ catch
+ error:Reason ->
+ {error,Reason}
+ end)
+ end),
receive
- {Serv,Rep} -> Rep
+ {'DOWN',Ref,process,Pid,Rep} -> Rep
end.
expand_opts(Opts0) ->
@@ -242,15 +249,12 @@ format_error({module_name,Mod,Filename}) ->
errors=[],
warnings=[]}).
-internal(Master, Input, Opts) ->
- Master ! {self(), try internal(Input, Opts)
- catch error:Reason -> {error, Reason}
- end}.
-
-internal({forms,Forms}, Opts) ->
- {_,Ps} = passes(forms, Opts),
- internal_comp(Ps, "", "", #compile{code=Forms,options=Opts,
- mod_options=Opts});
+internal({forms,Forms}, Opts0) ->
+ {_,Ps} = passes(forms, Opts0),
+ Source = proplists:get_value(source, Opts0, ""),
+ Opts1 = proplists:delete(source, Opts0),
+ Compile = #compile{code=Forms,options=Opts1,mod_options=Opts1},
+ internal_comp(Ps, Source, "", Compile);
internal({file,File}, Opts) ->
{Ext,Ps} = passes(file, Opts),
Compile = #compile{options=Opts,mod_options=Opts},
diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl
index 512fa0e4ac..da53a6ba9c 100644
--- a/lib/compiler/test/compile_SUITE.erl
+++ b/lib/compiler/test/compile_SUITE.erl
@@ -25,7 +25,7 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
app_test/1,
- file_1/1, module_mismatch/1, big_file/1, outdir/1,
+ file_1/1, forms_2/1, module_mismatch/1, big_file/1, outdir/1,
binary/1, makedep/1, cond_and_ifdef/1, listings/1, listings_big/1,
other_output/1, package_forms/1, encrypted_abstr/1,
bad_record_use1/1, bad_record_use2/1, strict_record/1,
@@ -42,7 +42,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
test_lib:recompile(?MODULE),
- [app_test, file_1, module_mismatch, big_file, outdir,
+ [app_test, file_1, forms_2, module_mismatch, big_file, outdir,
binary, makedep, cond_and_ifdef, listings, listings_big,
other_output, package_forms, encrypted_abstr,
{group, bad_record_use}, strict_record,
@@ -76,6 +76,9 @@ app_test(Config) when is_list(Config) ->
file_1(Config) when is_list(Config) ->
?line Dog = test_server:timetrap(test_server:minutes(5)),
+
+ process_flag(trap_exit, true),
+
?line {Simple, Target} = files(Config, "file_1"),
?line {ok, Cwd} = file:get_cwd(),
?line ok = file:set_cwd(filename:dirname(Target)),
@@ -102,9 +105,37 @@ file_1(Config) when is_list(Config) ->
%% Cleanup.
?line ok = file:delete(Target),
?line ok = file:del_dir(filename:dirname(Target)),
+
+ %% There should not be any messages in the messages.
+ receive
+ Any ->
+ ?t:fail({unexpected,Any})
+ after 10 ->
+ ok
+ end,
+
?line test_server:timetrap_cancel(Dog),
ok.
+forms_2(Config) when is_list(Config) ->
+ Src = "/foo/bar",
+ AbsSrc = filename:absname(Src),
+ {ok,simple,Binary} = compile:forms([{attribute,1,module,simple}],
+ [binary,{source,Src}]),
+ code:load_binary(simple, Src, Binary),
+ Info = simple:module_info(compile),
+
+ %% Test that the proper source is returned.
+ AbsSrc = proplists:get_value(source, Info),
+
+ %% Ensure that the options are not polluted with 'source'.
+ [] = proplists:get_value(options, Info),
+
+ %% Cleanup.
+ true = code:delete(simple),
+ false = code:purge(simple),
+ ok.
+
module_mismatch(Config) when is_list(Config) ->
?line DataDir = ?config(data_dir, Config),
?line File = filename:join(DataDir, "wrong_module_name.erl"),
diff --git a/lib/edoc/src/edoc_data.erl b/lib/edoc/src/edoc_data.erl
index aad0b14371..624f9177a2 100644
--- a/lib/edoc/src/edoc_data.erl
+++ b/lib/edoc/src/edoc_data.erl
@@ -167,7 +167,10 @@ callbacks(Es, Module, Env, Opts) ->
case lists:any(fun (#entry{name = {behaviour_info, 1}}) -> true;
(_) -> false
end,
- Es) of
+ Es)
+ orelse
+ lists:keymember(callback, 1, Module#module.attributes)
+ of
true ->
try (Module#module.name):behaviour_info(callbacks) of
Fs ->
diff --git a/lib/edoc/src/edoc_lib.erl b/lib/edoc/src/edoc_lib.erl
index 7fd8358add..90fb8a679c 100644
--- a/lib/edoc/src/edoc_lib.erl
+++ b/lib/edoc/src/edoc_lib.erl
@@ -469,6 +469,10 @@ uri_get("ftp:" ++ Path) ->
uri_get("//" ++ Path) ->
Msg = io_lib:format("cannot access network-path: '//~s'.", [Path]),
{error, Msg};
+uri_get([C, $:, $/ | _]=Path) when C >= $A, C =< $Z; C >= $a, C =< $z ->
+ uri_get_file(Path); % special case for Windows
+uri_get([C, $:, $\ | _]=Path) when C >= $A, C =< $Z; C >= $a, C =< $z ->
+ uri_get_file(Path); % special case for Windows
uri_get(URI) ->
case is_relative_uri(URI) of
true ->
diff --git a/lib/edoc/vsn.mk b/lib/edoc/vsn.mk
index b8f33894f1..2f403212c8 100644
--- a/lib/edoc/vsn.mk
+++ b/lib/edoc/vsn.mk
@@ -1 +1 @@
-EDOC_VSN = 0.7.9.1
+EDOC_VSN = 0.7.10
diff --git a/lib/erl_docgen/priv/xsl/db_html.xsl b/lib/erl_docgen/priv/xsl/db_html.xsl
index 7cf5465f90..4bc5abb364 100644
--- a/lib/erl_docgen/priv/xsl/db_html.xsl
+++ b/lib/erl_docgen/priv/xsl/db_html.xsl
@@ -1817,7 +1817,14 @@
<xsl:choose>
<xsl:when test="ancestor::cref">
- <a name="{substring-before(nametext, '(')}"><span class="bold_code"><xsl:value-of select="ret"/><xsl:text> </xsl:text><xsl:value-of select="nametext"/></span></a><br/>
+ <a name="{substring-before(nametext, '(')}">
+ <span class="bold_code">
+ <xsl:value-of select="ret"/>
+ <xsl:call-template name="maybe-space-after-ret">
+ <xsl:with-param name="s" select="ret"/>
+ </xsl:call-template>
+ <xsl:value-of select="nametext"/>
+ </span></a><br/>
</xsl:when>
<xsl:when test="ancestor::erlref">
<xsl:variable name="fname">
@@ -1845,6 +1852,18 @@
</xsl:template>
+ <xsl:template name="maybe-space-after-ret">
+ <xsl:param name="s"/>
+ <xsl:variable name="last_char"
+ select="substring($s, string-length($s), 1)"/>
+ <xsl:choose>
+ <xsl:when test="$last_char != '*'">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:template>
+
+
<!-- Type -->
<xsl:template match="type">
<xsl:param name="partnum"/>
diff --git a/lib/erl_docgen/priv/xsl/db_man.xsl b/lib/erl_docgen/priv/xsl/db_man.xsl
index 5234ba6bd0..33808859c7 100644
--- a/lib/erl_docgen/priv/xsl/db_man.xsl
+++ b/lib/erl_docgen/priv/xsl/db_man.xsl
@@ -3,7 +3,7 @@
#
# %CopyrightBegin%
#
- # Copyright Ericsson AB 2009-2011. All Rights Reserved.
+ # Copyright Ericsson AB 2009-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -758,10 +758,32 @@
<xsl:template name="name">
<xsl:text>&#10;.B&#10;</xsl:text>
- <xsl:apply-templates/>
+ <xsl:choose>
+ <xsl:when test="ancestor::cref">
+ <xsl:value-of select="ret"/>
+ <xsl:call-template name="maybe-space-after-ret">
+ <xsl:with-param name="s" select="ret"/>
+ </xsl:call-template>
+ <xsl:value-of select="nametext"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:apply-templates/>
+ </xsl:otherwise>
+ </xsl:choose>
<xsl:text>&#10;.br</xsl:text>
</xsl:template>
+ <xsl:template name="maybe-space-after-ret">
+ <xsl:param name="s"/>
+ <xsl:variable name="last_char"
+ select="substring($s, string-length($s), 1)"/>
+ <xsl:choose>
+ <xsl:when test="$last_char != '*'">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:template>
+
<!-- Type -->
<xsl:template match="type">
diff --git a/lib/erl_docgen/priv/xsl/db_pdf.xsl b/lib/erl_docgen/priv/xsl/db_pdf.xsl
index bf17406d84..da96052462 100644
--- a/lib/erl_docgen/priv/xsl/db_pdf.xsl
+++ b/lib/erl_docgen/priv/xsl/db_pdf.xsl
@@ -3,7 +3,7 @@
#
# %CopyrightBegin%
#
- # Copyright Ericsson AB 2009-2011. All Rights Reserved.
+ # Copyright Ericsson AB 2009-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -1424,7 +1424,13 @@
<xsl:param name="partnum"/>
<xsl:choose>
<xsl:when test="ancestor::cref">
- <fo:block id="{generate-id(nametext)}"><xsl:value-of select="ret"/><xsl:text></xsl:text><xsl:value-of select="nametext"/></fo:block>
+ <fo:block id="{generate-id(nametext)}">
+ <xsl:value-of select="ret"/>
+ <xsl:call-template name="maybe-space-after-ret">
+ <xsl:with-param name="s" select="ret"/>
+ </xsl:call-template>
+ <xsl:value-of select="nametext"/>
+ </fo:block>
</xsl:when>
<xsl:otherwise>
<fo:block id="{generate-id(.)}"><xsl:value-of select="."/></fo:block>
@@ -1432,6 +1438,16 @@
</xsl:choose>
</xsl:template>
+ <xsl:template name="maybe-space-after-ret">
+ <xsl:param name="s"/>
+ <xsl:variable name="last_char"
+ select="substring($s, string-length($s), 1)"/>
+ <xsl:choose>
+ <xsl:when test="$last_char != '*'">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:template>
<!-- Type -->
<xsl:template match="type">
diff --git a/lib/eunit/doc/overview.edoc b/lib/eunit/doc/overview.edoc
index ad449cb6fc..b4af31ae6a 100644
--- a/lib/eunit/doc/overview.edoc
+++ b/lib/eunit/doc/overview.edoc
@@ -723,8 +723,12 @@ A <em>simple test object</em> is one of the following:
```fun some_function/0'''
```fun some_module:some_function/0'''
</li>
- <li>A pair of atoms `{ModuleName, FunctionName}', referring to the
- function `ModuleName:FunctionName/0'</li>
+ <li>A tuple `{test, ModuleName, FunctionName}', where `ModuleName' and
+ `FunctionName' are atoms, referring to the function
+ `ModuleName:FunctionName/0'</li>
+ <li>(Obsolete) A pair of atoms `{ModuleName, FunctionName}', equivalent to
+ `{test, ModuleName, FunctionName}' if nothing else matches first. This
+ might be removed in a future version.</li>
<li>A pair `{LineNumber, SimpleTest}', where `LineNumber' is a
nonnegative integer and `SimpleTest' is another simple test
object. `LineNumber' should indicate the source line of the test.
diff --git a/lib/eunit/include/eunit.hrl b/lib/eunit/include/eunit.hrl
index db68d8ae60..fba840c3bd 100644
--- a/lib/eunit/include/eunit.hrl
+++ b/lib/eunit/include/eunit.hrl
@@ -25,11 +25,12 @@
%% will become undefined. NODEBUG also implies NOASSERT, unless testing
%% is enabled.
%%
-%% If including this file causes TEST to be defined, then NOASSERT will
-%% be undefined, even if it was previously defined and even if NODEBUG
-%% is defined. If both ASSERT and NOASSERT are defined before the file
-%% is included, then ASSERT takes precedence, and NOASSERT will become
-%% undefined regardless of TEST.
+%% Defining NOASSERT disables asserts. NODEBUG implies NOASSERT unless
+%% testing is enabled. If including this file causes TEST to be defined,
+%% then NOASSERT will be undefined, even if it was previously defined and
+%% even if NODEBUG is defined. If both ASSERT and NOASSERT are defined
+%% before the file is included, then ASSERT takes precedence, and NOASSERT
+%% will become undefined regardless of TEST.
%%
%% After including this file, EUNIT will be defined if and only if TEST
%% is defined.
@@ -127,9 +128,9 @@
current_function)))).
-endif.
--ifdef(NOASSERT).
%% The plain assert macro should be defined to do nothing if this file
%% is included when debugging/testing is turned off.
+-ifdef(NOASSERT).
-ifndef(assert).
-define(assert(BoolExpr),ok).
-endif.
diff --git a/lib/eunit/src/eunit.app.src b/lib/eunit/src/eunit.app.src
index 5e16dfa2ce..431abac98b 100644
--- a/lib/eunit/src/eunit.app.src
+++ b/lib/eunit/src/eunit.app.src
@@ -14,7 +14,6 @@
eunit_striptests,
eunit_surefire,
eunit_test,
- eunit_tests,
eunit_tty]},
{registered,[]},
{applications, [kernel,stdlib]},
diff --git a/lib/eunit/src/eunit.erl b/lib/eunit/src/eunit.erl
index 95857e83c8..51846d73b3 100644
--- a/lib/eunit/src/eunit.erl
+++ b/lib/eunit/src/eunit.erl
@@ -139,7 +139,7 @@ test(Tests, Options) ->
%% @private
%% @doc See {@link test/2}.
test(Server, Tests, Options) ->
- Listeners = [eunit_tty:start(Options) | listeners(Options)],
+ Listeners = listeners(Options),
Serial = eunit_serial:start(Listeners),
case eunit_server:start_test(Server, Serial, Tests, Options) of
{ok, Reference} -> test_run(Reference, Listeners);
@@ -194,7 +194,10 @@ submit(Server, T, Options) ->
eunit_server:start_test(Server, Dummy, T, Options).
listeners(Options) ->
- Ps = start_listeners(proplists:get_all_values(report, Options)),
+ %% note that eunit_tty must always run, because it sends the final
+ %% {result,...} message that the test_run() function is waiting for
+ Ls = [{eunit_tty, Options} | proplists:get_all_values(report, Options)],
+ Ps = start_listeners(Ls),
%% the event_log option is for debugging, to view the raw events
case proplists:get_value(event_log, Options) of
undefined ->
diff --git a/lib/eunit/src/eunit_data.erl b/lib/eunit/src/eunit_data.erl
index 392d378a0e..0350f9bf6e 100644
--- a/lib/eunit/src/eunit_data.erl
+++ b/lib/eunit/src/eunit_data.erl
@@ -83,6 +83,7 @@
%% SimpleTest = TestFunction | {Line::integer(), SimpleTest}
%%
%% TestFunction = () -> any()
+%% | {test, M::moduleName(), F::functionName()}
%% | {M::moduleName(), F::functionName()}.
%%
%% AbstractTestFunction = (X::any()) -> any()
@@ -95,7 +96,6 @@
%%
%% @type moduleName() = atom()
%% @type functionName() = atom()
-%% @type arity() = integer()
%% @type appName() = atom()
%% @type fileName() = string()
@@ -156,8 +156,9 @@ iter_prev(#iter{prev = [T | Ts]} = I) ->
%% @spec (tests()) -> none | {testItem(), tests()}
%% @type testItem() = #test{} | #group{}
%% @throws {bad_test, term()}
-%% | {generator_failed, exception()}
-%% | {no_such_function, eunit_lib:mfa()}
+%% | {generator_failed, {{M::atom(),F::atom(),A::integer()},
+%% exception()}}
+%% | {no_such_function, mfa()}
%% | {module_not_found, moduleName()}
%% | {application_not_found, appName()}
%% | {file_read_error, {Reason::atom(), Message::string(),
@@ -221,17 +222,27 @@ parse({foreachx, P, S1, C1, Ps} = T)
[] ->
{data, []}
end;
-parse({generator, F} = T) when is_function(F) ->
+parse({generator, F}) when is_function(F) ->
+ {module, M} = erlang:fun_info(F, module),
+ {name, N} = erlang:fun_info(F, name),
+ {arity, A} = erlang:fun_info(F, arity),
+ parse({generator, F, {M,N,A}});
+parse({generator, F, {M,N,A}} = T)
+ when is_function(F), is_atom(M), is_atom(N), is_integer(A) ->
check_arity(F, 0, T),
%% use run_testfun/1 to handle wrapper exceptions
case eunit_test:run_testfun(F) of
{ok, T1} ->
+ case eunit_lib:is_not_test(T1) of
+ true -> throw({bad_generator, {{M,N,A}, T1}});
+ false -> ok
+ end,
{data, T1};
{error, {Class, Reason, Trace}} ->
- throw({generator_failed, {Class, Reason, Trace}})
+ throw({generator_failed, {{M,N,A}, {Class, Reason, Trace}}})
end;
parse({generator, M, F}) when is_atom(M), is_atom(F) ->
- parse({generator, eunit_test:function_wrapper(M, F)});
+ parse({generator, eunit_test:mf_wrapper(M, F), {M,F,0}});
parse({inorder, T}) ->
group(#group{tests = T, order = inorder});
parse({inparallel, T}) ->
@@ -421,8 +432,11 @@ parse_simple(F) ->
parse_function(F) when is_function(F) ->
check_arity(F, 0, F),
#test{f = F, location = eunit_lib:fun_parent(F)};
-parse_function({M,F}) when is_atom(M), is_atom(F) ->
- #test{f = eunit_test:function_wrapper(M, F), location = {M, F, 0}};
+parse_function({test, M, F}) when is_atom(M), is_atom(F) ->
+ #test{f = eunit_test:mf_wrapper(M, F), location = {M, F, 0}};
+parse_function({M, F}) when is_atom(M), is_atom(F) ->
+ %% {M,F} is now considered obsolete; use {test,M,F} instead
+ parse_function({test, M, F});
parse_function(F) ->
bad_test(F).
@@ -580,7 +594,7 @@ testfuns(Es, M, TestSuffix, GeneratorSuffix) ->
N = atom_to_list(F),
case lists:suffix(TestSuffix, N) of
true ->
- [{M,F} | Fs];
+ [{test, M, F} | Fs];
false ->
case lists:suffix(GeneratorSuffix, N) of
true ->
@@ -723,6 +737,7 @@ data_test_() ->
Tests = [T,T,T],
[?_assertMatch(ok, eunit:test(T)),
?_assertMatch(error, eunit:test(Fail)),
+ ?_assertMatch(ok, eunit:test({test, ?MODULE, trivial_test})),
?_assertMatch(ok, eunit:test({generator, fun () -> Tests end})),
?_assertMatch(ok, eunit:test({generator, fun generator/0})),
?_assertMatch(ok, eunit:test({generator, ?MODULE, generator_exported_})),
@@ -740,6 +755,12 @@ data_test_() ->
%%?_test({foreach, Setup, [T, T, T]})
].
+trivial_test() ->
+ ok.
+
+trivial_generator_test_() ->
+ [?_test(ok)].
+
lazy_test_() ->
{spawn, [?_test(undefined = put(count, 0)),
lazy_gen(7),
diff --git a/lib/eunit/src/eunit_lib.erl b/lib/eunit/src/eunit_lib.erl
index 1c41e229c5..ea9e944d7e 100644
--- a/lib/eunit/src/eunit_lib.erl
+++ b/lib/eunit/src/eunit_lib.erl
@@ -30,7 +30,8 @@
-export([dlist_next/1, uniq/1, fun_parent/1, is_string/1, command/1,
command/2, command/3, trie_new/0, trie_store/2, trie_match/2,
split_node/1, consult_file/1, list_dir/1, format_exit_term/1,
- format_exception/1, format_exception/2, format_error/1]).
+ format_exception/1, format_exception/2, format_error/1,
+ is_not_test/1]).
%% Type definitions for describing exceptions
@@ -39,13 +40,10 @@
%%
%% @type exceptionClass() = error | exit | throw
%%
-%% @type stackTrace() = [{moduleName(), functionName(),
-%% arity() | argList()}]
+%% @type stackTrace() = [{moduleName(), functionName(), arity() | argList()}]
%%
%% @type moduleName() = atom()
%% @type functionName() = atom()
-%% @type arity() = integer()
-%% @type mfa() = {moduleName(), functionName(), arity()}
%% @type argList() = [term()]
%% @type fileName() = string()
@@ -59,8 +57,9 @@ format_exception({Class,Term,Trace}, Depth)
when is_atom(Class), is_list(Trace) ->
case is_stacktrace(Trace) of
true ->
- io_lib:format("~w:~P\n~s",
- [Class, Term, Depth, format_stacktrace(Trace)]);
+ io_lib:format("~s**~w:~s",
+ [format_stacktrace(Trace), Class,
+ format_term(Term, Depth)]);
false ->
format_term(Term, Depth)
end;
@@ -86,6 +85,12 @@ analyze_exit_term(Term) ->
is_stacktrace([]) ->
true;
+is_stacktrace([{M,F,A,L}|Fs])
+ when is_atom(M), is_atom(F), is_integer(A), is_list(L) ->
+ is_stacktrace(Fs);
+is_stacktrace([{M,F,As,L}|Fs])
+ when is_atom(M), is_atom(F), is_list(As), is_list(L) ->
+ is_stacktrace(Fs);
is_stacktrace([{M,F,A}|Fs]) when is_atom(M), is_atom(F), is_integer(A) ->
is_stacktrace(Fs);
is_stacktrace([{M,F,As}|Fs]) when is_atom(M), is_atom(F), is_list(As) ->
@@ -96,10 +101,11 @@ is_stacktrace(_) ->
format_stacktrace(Trace) ->
format_stacktrace(Trace, "in function", "in call from").
-format_stacktrace([{M,F,A}|Fs], Pre, Pre1) when is_integer(A) ->
- [io_lib:fwrite(" ~s ~w:~w/~w\n", [Pre, M, F, A])
+format_stacktrace([{M,F,A,L}|Fs], Pre, Pre1) when is_integer(A) ->
+ [io_lib:fwrite("~s ~w:~w/~w~s\n",
+ [Pre, M, F, A, format_stacktrace_location(L)])
| format_stacktrace(Fs, Pre1, Pre1)];
-format_stacktrace([{M,F,As}|Fs], Pre, Pre1) when is_list(As) ->
+format_stacktrace([{M,F,As,L}|Fs], Pre, Pre1) when is_list(As) ->
A = length(As),
C = case is_op(M,F,A) of
true when A =:= 1 ->
@@ -112,12 +118,23 @@ format_stacktrace([{M,F,As}|Fs], Pre, Pre1) when is_list(As) ->
false ->
io_lib:fwrite("~w(~s)", [F,format_arglist(As)])
end,
- [io_lib:fwrite(" ~s ~w:~w/~w\n called as ~s\n",
- [Pre,M,F,A,C])
+ [io_lib:fwrite("~s ~w:~w/~w~s\n called as ~s\n",
+ [Pre,M,F,A,format_stacktrace_location(L),C])
| format_stacktrace(Fs,Pre1,Pre1)];
+format_stacktrace([{M,F,As}|Fs], Pre, Pre1) ->
+ format_stacktrace([{M,F,As,[]}|Fs], Pre, Pre1);
format_stacktrace([],_Pre,_Pre1) ->
"".
+format_stacktrace_location(Location) ->
+ File = proplists:get_value(file, Location),
+ Line = proplists:get_value(line, Location),
+ if File =/= undefined, Line =/= undefined ->
+ io_lib:format(" (~s, line ~w)", [File, Line]);
+ true ->
+ ""
+ end.
+
format_arg(A) ->
io_lib:format("~P",[A,15]).
@@ -139,9 +156,13 @@ is_op(_M, _F, _A) ->
format_error({bad_test, Term}) ->
error_msg("bad test descriptor", "~P", [Term, 15]);
-format_error({generator_failed, Exception}) ->
- error_msg("test generator failed", "~s",
- [format_exception(Exception)]);
+format_error({bad_generator, {{M,F,A}, Term}}) ->
+ error_msg(io_lib:format("result from generator ~w:~w/~w is not a test",
+ [M,F,A]),
+ "~P", [Term, 15]);
+format_error({generator_failed, {{M,F,A}, Exception}}) ->
+ error_msg(io_lib:format("test generator ~w:~w/~w failed",[M,F,A]),
+ "~s", [format_exception(Exception)]);
format_error({no_such_function, {M,F,A}})
when is_atom(M), is_atom(F), is_integer(A) ->
error_msg(io_lib:format("no such function: ~w:~w/~w", [M,F,A]),
@@ -158,14 +179,55 @@ format_error({setup_failed, Exception}) ->
format_error({cleanup_failed, Exception}) ->
error_msg("context cleanup failed", "~s",
[format_exception(Exception)]);
+format_error({{bad_instantiator, {{M,F,A}, Term}}, _DummyException}) ->
+ error_msg(io_lib:format("result from instantiator ~w:~w/~w is not a test",
+ [M,F,A]),
+ "~P", [Term, 15]);
format_error({instantiation_failed, Exception}) ->
error_msg("instantiation of subtests failed", "~s",
[format_exception(Exception)]).
error_msg(Title, Fmt, Args) ->
- Msg = io_lib:format("::"++Fmt, Args), % gets indentation right
+ Msg = io_lib:format("**"++Fmt, Args), % gets indentation right
io_lib:fwrite("*** ~s ***\n~s\n\n", [Title, Msg]).
+-ifdef(TEST).
+format_exception_test_() ->
+ [?_assertMatch(
+ "\nymmud:rorre"++_,
+ lists:reverse(lists:flatten(
+ format_exception(try erlang:error(dummy)
+ catch C:R -> {C, R, erlang:get_stacktrace()}
+ end)))),
+ ?_assertMatch(
+ "\nymmud:rorre"++_,
+ lists:reverse(lists:flatten(
+ format_exception(try erlang:error(dummy, [a])
+ catch C:R -> {C, R, erlang:get_stacktrace()}
+ end))))].
+-endif.
+
+%% ---------------------------------------------------------------------
+%% detect common return values that are definitely not tests
+
+is_not_test(T) ->
+ case T of
+ ok -> true;
+ error -> true;
+ true -> true;
+ false -> true;
+ undefined -> true;
+ {ok, _} -> true;
+ {error, _} -> true;
+ {'EXIT', _} -> true;
+ N when is_number(N) -> true;
+ [N|_] when is_number(N) -> true;
+ X when is_binary(X) -> true;
+ X when is_pid(X) -> true;
+ X when is_port(X) -> true;
+ X when is_reference(X) -> true;
+ _ -> false
+ end.
%% ---------------------------------------------------------------------
%% Deep list iterator; accepts improper lists/sublists, and also accepts
diff --git a/lib/eunit/src/eunit_surefire.erl b/lib/eunit/src/eunit_surefire.erl
index 2a6cbca14d..46b8c8b503 100644
--- a/lib/eunit/src/eunit_surefire.erl
+++ b/lib/eunit/src/eunit_surefire.erl
@@ -156,9 +156,33 @@ handle_end(test, Data, St) ->
St#state{testsuites=store_suite(NewTestSuite, TestSuites)}.
%% Cancel group does not give information on the individual cancelled test case
-%% We ignore this event
-handle_cancel(group, _Data, St) ->
- St;
+%% We ignore this event...
+handle_cancel(group, Data, St) ->
+ %% ...except when it tells us that a fixture setup or cleanup failed.
+ case proplists:get_value(reason, Data) of
+ {abort, {SomethingFailed, Exception}}
+ when SomethingFailed =:= setup_failed;
+ SomethingFailed =:= cleanup_failed ->
+ [GroupId|_] = proplists:get_value(id, Data),
+ TestSuites = St#state.testsuites,
+ TestSuite = lookup_suite_by_group_id(GroupId, TestSuites),
+
+ %% We don't have any proper name. Let's give all the
+ %% clues that we have.
+ Name = case SomethingFailed of
+ setup_failed -> "fixture setup ";
+ cleanup_failed -> "fixture cleanup "
+ end
+ ++ io_lib:format("~p", [proplists:get_value(id, Data)]),
+ Desc = format_desc(proplists:get_value(desc, Data)),
+ TestCase = #testcase{
+ name = Name, description = Desc,
+ time = 0, output = <<>>},
+ NewTestSuite = add_testcase_to_testsuite({error, Exception}, TestCase, TestSuite),
+ St#state{testsuites=store_suite(NewTestSuite, TestSuites)};
+ _ ->
+ St
+ end;
handle_cancel(test, Data, St) ->
%% Retrieve existing test suite:
[GroupId|_] = proplists:get_value(id, Data),
@@ -232,7 +256,7 @@ write_reports(TestSuites, XmlDir) ->
write_report(#testsuite{name = Name} = TestSuite, XmlDir) ->
Filename = filename:join(XmlDir, lists:flatten(["TEST-", escape_suitename(Name)], ".xml")),
- case file:open(Filename, [write, raw]) of
+ case file:open(Filename, [write,{encoding,utf8}]) of
{ok, FileDescriptor} ->
try
write_report_to(TestSuite, FileDescriptor)
diff --git a/lib/eunit/src/eunit_test.erl b/lib/eunit/src/eunit_test.erl
index bca49ae626..9cf40a738d 100644
--- a/lib/eunit/src/eunit_test.erl
+++ b/lib/eunit/src/eunit_test.erl
@@ -21,8 +21,7 @@
-module(eunit_test).
--export([run_testfun/1, function_wrapper/2, enter_context/4,
- multi_setup/1]).
+-export([run_testfun/1, mf_wrapper/2, enter_context/4, multi_setup/1]).
-include("eunit.hrl").
@@ -43,8 +42,12 @@ get_stacktrace(Ts) ->
prune_trace([{eunit_data, _, _} | Rest], Tail) ->
prune_trace(Rest, Tail);
+prune_trace([{eunit_data, _, _, _} | Rest], Tail) ->
+ prune_trace(Rest, Tail);
prune_trace([{?MODULE, _, _} | _Rest], Tail) ->
Tail;
+prune_trace([{?MODULE, _, _, _} | _Rest], Tail) ->
+ Tail;
prune_trace([T | Ts], Tail) ->
[T | prune_trace(Ts, Tail)];
prune_trace([], Tail) ->
@@ -258,7 +261,7 @@ macro_test_() ->
%% @type wrapperError() = {no_such_function, mfa()}
%% | {module_not_found, moduleName()}
-function_wrapper(M, F) ->
+mf_wrapper(M, F) ->
fun () ->
try M:F()
catch
@@ -289,12 +292,12 @@ fail(Term) ->
wrapper_test_() ->
{"error handling in function wrapper",
[?_assertException(throw, {module_not_found, eunit_nonexisting},
- run_testfun(function_wrapper(eunit_nonexisting,test))),
+ run_testfun(mf_wrapper(eunit_nonexisting,test))),
?_assertException(throw,
{no_such_function, {?MODULE,nonexisting_test,0}},
- run_testfun(function_wrapper(?MODULE,nonexisting_test))),
+ run_testfun(mf_wrapper(?MODULE,nonexisting_test))),
?_test({error, {error, undef, _T}}
- = run_testfun(function_wrapper(?MODULE,wrapper_test_exported_)))
+ = run_testfun(mf_wrapper(?MODULE,wrapper_test_exported_)))
]}.
%% this must be exported (done automatically by the autoexport transform)
@@ -319,6 +322,17 @@ enter_context(Setup, Cleanup, Instantiate, Callback) ->
R ->
try Instantiate(R) of
T ->
+ case eunit_lib:is_not_test(T) of
+ true ->
+ catch throw(error), % generate a stack trace
+ {module,M} = erlang:fun_info(Instantiate, module),
+ {name,N} = erlang:fun_info(Instantiate, name),
+ {arity,A} = erlang:fun_info(Instantiate, arity),
+ context_error({bad_instantiator, {{M,N,A},T}},
+ error, badarg);
+ false ->
+ ok
+ end,
try Callback(T) %% call back to client code
after
%% Always run cleanup; client may be an idiot
diff --git a/lib/eunit/src/eunit_tty.erl b/lib/eunit/src/eunit_tty.erl
index e3e7b710b2..f21b2da3d3 100644
--- a/lib/eunit/src/eunit_tty.erl
+++ b/lib/eunit/src/eunit_tty.erl
@@ -44,6 +44,7 @@ start(Options) ->
init(Options) ->
St = #state{verbose = proplists:get_bool(verbose, Options)},
+ put(no_tty, proplists:get_bool(no_tty, Options)),
receive
{start, _Reference} ->
if St#state.verbose -> print_header();
@@ -59,30 +60,30 @@ terminate({ok, Data}, St) ->
Cancel = proplists:get_value(cancel, Data, 0),
if Fail =:= 0, Skip =:= 0, Cancel =:= 0 ->
if Pass =:= 0 ->
- io:fwrite(" There were no tests to run.\n");
+ fwrite(" There were no tests to run.\n");
true ->
if St#state.verbose -> print_bar();
true -> ok
end,
if Pass =:= 1 ->
- io:fwrite(" Test passed.\n");
+ fwrite(" Test passed.\n");
true ->
- io:fwrite(" All ~w tests passed.\n", [Pass])
+ fwrite(" All ~w tests passed.\n", [Pass])
end
end,
sync_end(ok);
true ->
print_bar(),
- io:fwrite(" Failed: ~w. Skipped: ~w. Passed: ~w.\n",
- [Fail, Skip, Pass]),
+ fwrite(" Failed: ~w. Skipped: ~w. Passed: ~w.\n",
+ [Fail, Skip, Pass]),
if Cancel =/= 0 ->
- io:fwrite("One or more tests were cancelled.\n");
+ fwrite("One or more tests were cancelled.\n");
true -> ok
end,
sync_end(error)
end;
terminate({error, Reason}, _St) ->
- io:fwrite("Internal error: ~P.\n", [Reason, 25]),
+ fwrite("Internal error: ~P.\n", [Reason, 25]),
sync_end(error).
sync_end(Result) ->
@@ -93,10 +94,10 @@ sync_end(Result) ->
end.
print_header() ->
- io:fwrite("======================== EUnit ========================\n").
+ fwrite("======================== EUnit ========================\n").
print_bar() ->
- io:fwrite("=======================================================\n").
+ fwrite("=======================================================\n").
handle_begin(group, Data, St) ->
@@ -170,18 +171,18 @@ handle_cancel(test, Data, St) ->
indent(N) when is_integer(N), N >= 1 ->
- io:put_chars(lists:duplicate(N * 2, $\s));
+ fwrite(lists:duplicate(N * 2, $\s));
indent(_N) ->
ok.
print_group_start(I, Desc) ->
indent(I),
- io:fwrite("~s\n", [Desc]).
+ fwrite("~s\n", [Desc]).
print_group_end(I, Time) ->
if Time > 0 ->
indent(I),
- io:fwrite("[done in ~.3f s]\n", [Time/1000]);
+ fwrite("[done in ~.3f s]\n", [Time/1000]);
true ->
ok
end.
@@ -198,9 +199,9 @@ print_test_begin(I, Data) ->
end,
case proplists:get_value(source, Data) of
{Module, Name, _Arity} ->
- io:fwrite("~s:~s ~s~s...", [Module, L, Name, D]);
+ fwrite("~s:~s ~s~s...", [Module, L, Name, D]);
_ ->
- io:fwrite("~s~s...", [L, D])
+ fwrite("~s~s...", [L, D])
end.
print_test_end(Data) ->
@@ -208,36 +209,35 @@ print_test_end(Data) ->
T = if Time > 0 -> io_lib:fwrite("[~.3f s] ", [Time/1000]);
true -> ""
end,
- io:fwrite("~sok\n", [T]).
+ fwrite("~sok\n", [T]).
print_test_error({error, Exception}, Data) ->
Output = proplists:get_value(output, Data),
- io:fwrite("*failed*\n::~s",
- [eunit_lib:format_exception(Exception)]),
+ fwrite("*failed*\n~s", [eunit_lib:format_exception(Exception)]),
case Output of
<<>> ->
- io:put_chars("\n\n");
+ fwrite("\n\n");
<<Text:800/binary, _:1/binary, _/binary>> ->
- io:fwrite(" output:<<\"~s\">>...\n\n", [Text]);
+ fwrite(" output:<<\"~s\">>...\n\n", [Text]);
_ ->
- io:fwrite(" output:<<\"~s\">>\n\n", [Output])
+ fwrite(" output:<<\"~s\">>\n\n", [Output])
end;
print_test_error({skipped, Reason}, _) ->
- io:fwrite("*did not run*\n::~s\n", [format_skipped(Reason)]).
+ fwrite("*did not run*\n::~s\n", [format_skipped(Reason)]).
format_skipped({module_not_found, M}) ->
- io_lib:format("missing module: ~w", [M]);
+ io_lib:fwrite("missing module: ~w", [M]);
format_skipped({no_such_function, {M,F,A}}) ->
- io_lib:format("no such function: ~w:~w/~w", [M,F,A]).
+ io_lib:fwrite("no such function: ~w:~w/~w", [M,F,A]).
print_test_cancel(Reason) ->
- io:fwrite(format_cancel(Reason)).
+ fwrite(format_cancel(Reason)).
print_group_cancel(_I, {blame, _}) ->
ok;
print_group_cancel(I, Reason) ->
indent(I),
- io:fwrite(format_cancel(Reason)).
+ fwrite(format_cancel(Reason)).
format_cancel(undefined) ->
"*skipped*\n";
@@ -253,3 +253,12 @@ format_cancel({exit, Reason}) ->
[Reason, 15]);
format_cancel({abort, Reason}) ->
eunit_lib:format_error(Reason).
+
+fwrite(String) ->
+ fwrite(String, []).
+
+fwrite(String, Args) ->
+ case get(no_tty) of
+ false -> io:fwrite(String, Args);
+ true -> ok
+ end.
diff --git a/lib/eunit/vsn.mk b/lib/eunit/vsn.mk
index 445c070e96..174d197117 100644
--- a/lib/eunit/vsn.mk
+++ b/lib/eunit/vsn.mk
@@ -1 +1 @@
-EUNIT_VSN = 2.2.2
+EUNIT_VSN = 2.2.3
diff --git a/lib/ic/doc/src/ic_clib.xml b/lib/ic/doc/src/ic_clib.xml
index b557c4b5f6..ebeaabae91 100644
--- a/lib/ic/doc/src/ic_clib.xml
+++ b/lib/ic/doc/src/ic_clib.xml
@@ -4,7 +4,7 @@
<cref>
<header>
<copyright>
- <year>2003</year><year>2009</year>
+ <year>2003</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -41,7 +41,7 @@
</section>
<funcs>
<func>
- <name><ret>CORBA_Environment*</ret><nametext>CORBA_Environment_alloc(int inbufsz, int outbufsz)</nametext></name>
+ <name><ret>CORBA_Environment *</ret><nametext>CORBA_Environment_alloc(int inbufsz, int outbufsz)</nametext></name>
<fsummary>Allocate environment data.</fsummary>
<desc>
<p>This function is used to allocate and initiate the
@@ -79,14 +79,14 @@
</desc>
</func>
<func>
- <name><ret>CORBA_char*</ret><nametext>CORBA_string_alloc(CORBA_unsigned_long len)</nametext></name>
+ <name><ret>CORBA_char *</ret><nametext>CORBA_string_alloc(CORBA_unsigned_long len)</nametext></name>
<fsummary>Allocate a string.</fsummary>
<desc>
<p>Allocates a (simple) CORBA character string of length <c>len + 1</c>.</p>
</desc>
</func>
<func>
- <name><ret>CORBA_wchar*</ret><nametext>CORBA_wstring_alloc(CORBA_unsigned_long len)</nametext></name>
+ <name><ret>CORBA_wchar *</ret><nametext>CORBA_wstring_alloc(CORBA_unsigned_long len)</nametext></name>
<fsummary>Allocate a wide string.</fsummary>
<desc>
<p>Allocates a CORBA wide string of length <c>len + 1</c>.</p>
@@ -101,7 +101,7 @@
</section>
<funcs>
<func>
- <name><ret>CORBA_char*</ret><nametext>CORBA_exception_id(CORBA_Environment *env)</nametext></name>
+ <name><ret>CORBA_char *</ret><nametext>CORBA_exception_id(CORBA_Environment *env)</nametext></name>
<fsummary>Get exception identity.</fsummary>
<desc>
<p>Returns the exception identity if an exception is set, otherwise
@@ -109,7 +109,7 @@
</desc>
</func>
<func>
- <name><ret>void*</ret><nametext>CORBA_exception_value(CORBA_Environment *env)</nametext></name>
+ <name><ret>void *</ret><nametext>CORBA_exception_value(CORBA_Environment *env)</nametext></name>
<fsummary>Get exception value.</fsummary>
<desc>
<p>Returns the exception value, if an exception is set, otherwise
@@ -160,7 +160,7 @@
</desc>
</func>
<func>
- <name><ret>oe_map_t*</ret><nametext>oe_merge_maps(oe_map_t *maps, int size)</nametext></name>
+ <name><ret>oe_map_t *</ret><nametext>oe_merge_maps(oe_map_t *maps, int size)</nametext></name>
<fsummary>Merge an array of server maps to one single map.</fsummary>
<desc>
<p>Merge an array of server maps to one single map.</p>
diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml
index 096ddfd847..b727960d96 100644
--- a/lib/kernel/doc/src/inet.xml
+++ b/lib/kernel/doc/src/inet.xml
@@ -452,8 +452,8 @@ fe80::204:acff:fe17:bf38
Scans every byte in received data-packets and checks if the 8 bit
is set in any of them. Information is retrieved with
<c>inet:getopts/2</c>.
- <note>Deprecated! Will be removed in Erlang/OTP R16.</note>
</p>
+ <p>Note that the <c>bit8</c> option is deprecated and will be removed in Erlang/OTP R16.</p>
</item>
<tag><c>{broadcast, Boolean}</c>(UDP sockets)</tag>
diff --git a/lib/kernel/examples/uds_dist/c_src/uds_drv.c b/lib/kernel/examples/uds_dist/c_src/uds_drv.c
index 9327ab19dc..9ad6b85a0f 100644
--- a/lib/kernel/examples/uds_dist/c_src/uds_drv.c
+++ b/lib/kernel/examples/uds_dist/c_src/uds_drv.c
@@ -967,7 +967,7 @@ static void *my_malloc(size_t size)
void *ptr;
if ((ptr = driver_alloc(size)) == NULL) {
- erl_exit(1,"Could not allocate %d bytes of memory",(int) size);
+ erl_exit(1,"Could not allocate %lu bytes of memory",(unsigned long) size);
}
return ptr;
}
@@ -977,7 +977,7 @@ static void *my_realloc(void *ptr, size_t size)
void erl_exit(int, char *, ...);
void *nptr;
if ((nptr = driver_realloc(ptr, size)) == NULL) {
- erl_exit(1,"Could not reallocate %d bytes of memory",(int) size);
+ erl_exit(1,"Could not reallocate %lu bytes of memory",(unsigned long) size);
}
return nptr;
}
diff --git a/lib/kernel/src/code.erl b/lib/kernel/src/code.erl
index b7fda69ce0..363072951e 100644
--- a/lib/kernel/src/code.erl
+++ b/lib/kernel/src/code.erl
@@ -63,7 +63,7 @@
which/1,
where_is_file/1,
where_is_file/2,
- set_primary_archive/3,
+ set_primary_archive/4,
clash/0]).
-export_type([load_error_rsn/0, load_ret/0]).
@@ -107,7 +107,7 @@
%% unstick_mod(Module) -> true
%% is_sticky(Module) -> boolean()
%% which(Module) -> Filename | loaded_ret_atoms() | non_existing
-%% set_primary_archive((FileName, Bin, FileInfo) -> ok | {error, Reason}
+%% set_primary_archive((FileName, ArchiveBin, FileInfo, ParserFun) -> ok | {error, Reason}
%% clash() -> ok prints out number of clashes
%%----------------------------------------------------------------------------
@@ -481,13 +481,16 @@ where_is_file(Path, File) when is_list(Path), is_list(File) ->
-spec set_primary_archive(ArchiveFile :: file:filename(),
ArchiveBin :: binary(),
- FileInfo :: file:file_info())
+ FileInfo :: file:file_info(),
+ ParserFun :: fun())
-> 'ok' | {'error', atom()}.
-set_primary_archive(ArchiveFile0, ArchiveBin, #file_info{} = FileInfo)
+set_primary_archive(ArchiveFile0, ArchiveBin, #file_info{} = FileInfo,
+ ParserFun)
when is_list(ArchiveFile0), is_binary(ArchiveBin) ->
ArchiveFile = filename:absname(ArchiveFile0),
- case call({set_primary_archive, ArchiveFile, ArchiveBin, FileInfo}) of
+ case call({set_primary_archive, ArchiveFile, ArchiveBin, FileInfo,
+ ParserFun}) of
{ok, []} ->
ok;
{ok, _Mode, Ebins} ->
diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl
index a2db7c9790..00ad923466 100644
--- a/lib/kernel/src/code_server.erl
+++ b/lib/kernel/src/code_server.erl
@@ -394,8 +394,8 @@ handle_call(stop,{_From,_Tag}, S) ->
handle_call({is_cached,_File}, {_From,_Tag}, S=#state{cache=no_cache}) ->
{reply, no, S};
-handle_call({set_primary_archive, File, ArchiveBin, FileInfo}, {_From,_Tag}, S=#state{mode=Mode}) ->
- case erl_prim_loader:set_primary_archive(File, ArchiveBin, FileInfo) of
+handle_call({set_primary_archive, File, ArchiveBin, FileInfo, ParserFun}, {_From,_Tag}, S=#state{mode=Mode}) ->
+ case erl_prim_loader:set_primary_archive(File, ArchiveBin, FileInfo, ParserFun) of
{ok, Files} ->
{reply, {ok, Mode, Files}, S};
{error, _Reason} = Error ->
diff --git a/lib/kernel/src/rpc.erl b/lib/kernel/src/rpc.erl
index e214ffa404..a3fc57a124 100644
--- a/lib/kernel/src/rpc.erl
+++ b/lib/kernel/src/rpc.erl
@@ -286,7 +286,7 @@ call(N,M,F,A) ->
Reason :: term(),
Timeout :: timeout().
-call(N,M,F,A,_Timeout) when node() =:= N -> %% Optimize local call
+call(N,M,F,A,infinity) when node() =:= N -> %% Optimize local call
local_call(M,F,A);
call(N,M,F,A,infinity) ->
do_call(N, {call,M,F,A,group_leader()}, infinity);
diff --git a/lib/kernel/test/erl_prim_loader_SUITE.erl b/lib/kernel/test/erl_prim_loader_SUITE.erl
index 6f4f27d594..72239641e9 100644
--- a/lib/kernel/test/erl_prim_loader_SUITE.erl
+++ b/lib/kernel/test/erl_prim_loader_SUITE.erl
@@ -426,7 +426,9 @@ primary_archive(Config) when is_list(Config) ->
ExpectedEbins = [Archive, DictDir ++ "/ebin", DummyDir ++ "/ebin"],
io:format("ExpectedEbins: ~p\n", [ExpectedEbins]),
?line {ok, FileInfo} = prim_file:read_file_info(Archive),
- ?line {ok, Ebins} = rpc:call(Node, erl_prim_loader, set_primary_archive, [Archive, ArchiveBin, FileInfo]),
+ ?line {ok, Ebins} = rpc:call(Node, erl_prim_loader, set_primary_archive,
+ [Archive, ArchiveBin, FileInfo,
+ fun escript:parse_file/1]),
?line ExpectedEbins = lists:sort(Ebins), % assert
?line {ok, TopFiles2} = rpc:call(Node, erl_prim_loader, list_dir, [Archive]),
@@ -435,7 +437,9 @@ primary_archive(Config) when is_list(Config) ->
?line ok = test_archive(Node, Archive, DictDir, BeamName),
%% Cleanup
- ?line {ok, []} = rpc:call(Node, erl_prim_loader, set_primary_archive, [undefined, undefined, undefined]),
+ ?line {ok, []} = rpc:call(Node, erl_prim_loader, set_primary_archive,
+ [undefined, undefined, undefined,
+ fun escript:parse_file/1]),
?line stop_node(Node),
?line ok = file:delete(Archive),
ok.
diff --git a/lib/os_mon/c_src/cpu_sup.c b/lib/os_mon/c_src/cpu_sup.c
index 9c5f9a6aa5..a0432b3093 100644
--- a/lib/os_mon/c_src/cpu_sup.c
+++ b/lib/os_mon/c_src/cpu_sup.c
@@ -458,8 +458,18 @@ static void error(char* err_msg) {
* if we get error here we have trouble,
* silence unnecessary warnings
*/
- if(write(FD_ERR, err_msg, strlen(err_msg)));
- if(write(FD_ERR, "\n", 1));
+ char buffer[256] = "[os_mon] cpu supervisor port (cpu_sup): ";
+ int i = strlen(buffer), j = 0;
+ int n = strlen(err_msg);
+
+ while(i < 253 && j < n) {
+ buffer[i++] = err_msg[j++];
+ }
+ buffer[i++] = '\r';
+ buffer[i++] = '\n';
+
+ /* try to use one write only */
+ if(write(FD_ERR, buffer, i));
exit(-1);
}
diff --git a/lib/os_mon/c_src/memsup.c b/lib/os_mon/c_src/memsup.c
index 078f20ff98..593a066f98 100644
--- a/lib/os_mon/c_src/memsup.c
+++ b/lib/os_mon/c_src/memsup.c
@@ -493,7 +493,7 @@ get_basic_mem(unsigned long *tot, unsigned long *used, unsigned long *pagesize){
#elif defined(__linux__) && !defined(_SC_AVPHYS_PAGES)
memory_ext me;
if (get_mem_procfs(&me) < 0) {
- print_error("ProcFS read error.");
+ print_error("ProcFS read error");
exit(1);
}
*tot = me.total;
@@ -582,7 +582,7 @@ message_loop(int erlin_fd)
* Wait for command from Erlang
*/
if ((res = read(erlin_fd, &cmdLen, 1)) < 0) {
- print_error("Error reading from Erlang.");
+ print_error("Error reading from Erlang");
return;
}
@@ -603,19 +603,19 @@ message_loop(int erlin_fd)
break;
case 0:
- print_error("Erlang has closed.");
+ print_error("Erlang has closed");
return;
default:
- print_error("Error reading from Erlang.");
+ print_error("Error reading from Erlang");
return;
} /* switch() */
} else { /* cmdLen != 1 */
- print_error("Invalid command length (%d) received.", cmdLen);
+ print_error("Invalid command length (%d) received", cmdLen);
return;
}
} else { /* Erlang end closed */
- print_error("Erlang has closed.");
+ print_error("Erlang has closed");
return;
}
}
@@ -641,15 +641,12 @@ static void
print_error(const char *format,...)
{
va_list args;
+ char buffer[256];
va_start(args, format);
- fprintf(stderr, "%s: ", program_name);
- vfprintf(stderr, format, args);
+ vsnprintf(buffer, 256, format, args);
va_end(args);
- fprintf(stderr, " \n");
+ /* try to use one write only */
+ fprintf(stderr, "[os_mon] memory supervisor port (memsup): %s\r\n", buffer);
+ fflush(stderr);
}
-
-
-
-
-
diff --git a/lib/os_mon/c_src/win32sysinfo.c b/lib/os_mon/c_src/win32sysinfo.c
index 2a155aae87..9d4587393f 100644
--- a/lib/os_mon/c_src/win32sysinfo.c
+++ b/lib/os_mon/c_src/win32sysinfo.c
@@ -89,6 +89,7 @@ typedef BOOL (WINAPI *tfpGetDiskFreeSpaceEx)(LPCTSTR, PULARGE_INTEGER,PULARGE_IN
static tfpGetDiskFreeSpaceEx fpGetDiskFreeSpaceEx;
+static void print_error(const char *msg);
static void
return_answer(char* value)
{
@@ -98,7 +99,7 @@ return_answer(char* value)
res = write(1,(char*) &bytes,1);
if (res != 1) {
- fprintf(stderr,"win32sysinfo:Error writing to pipe");
+ print_error("Error writing to pipe");
exit(1);
}
@@ -107,9 +108,8 @@ return_answer(char* value)
while (left > 0)
{
res = write(1, value+bytes-left, left);
- if (res <= 0)
- {
- fprintf(stderr,"win32sysinfo:Error writing to pipe");
+ if (res <= 0) {
+ print_error("Error writing to pipe");
exit(1);
}
left -= res;
@@ -248,7 +248,6 @@ message_loop()
char cmd[512];
int res;
- fprintf(stderr,"in message_loop\n");
/* Startup ACK. */
return_answer(OK);
while (1)
@@ -257,12 +256,12 @@ message_loop()
* Wait for command from Erlang
*/
if ((res = read(0, &cmdLen, 1)) < 0) {
- fprintf(stderr,"win32sysinfo:Error reading from Erlang.");
+ print_error("Error reading from Erlang");
return;
}
if (res != 1){ /* Exactly one byte read ? */
- fprintf(stderr,"win32sysinfo:Erlang has closed.");
+ print_error("Erlang has closed");
return;
}
if ((res = read(0, &cmd, cmdLen)) == cmdLen){
@@ -291,11 +290,11 @@ message_loop()
return_answer("xEND");
}
else if (res == 0) {
- fprintf(stderr,"win32sysinfo:Erlang has closed.");
+ print_error("Erlang has closed");
return;
}
else {
- fprintf(stderr,"win32sysinfo:Error reading from Erlang.");
+ print_error("Error reading from Erlang");
return;
}
}
@@ -309,10 +308,9 @@ int main(int argc, char ** argv){
message_loop();
return 0;
}
-
-
-
-
-
-
-
+static void
+print_error(const char *msg) {
+ /* try to use one write only */
+ fprintf(stderr, "[os_mon] win32 supervisor port (win32sysinfo): %s\r\n", msg);
+ fflush(stderr);
+}
diff --git a/lib/runtime_tools/c_src/trace_ip_drv.c b/lib/runtime_tools/c_src/trace_ip_drv.c
index 7f7ab8dd9d..6b77128761 100644
--- a/lib/runtime_tools/c_src/trace_ip_drv.c
+++ b/lib/runtime_tools/c_src/trace_ip_drv.c
@@ -590,8 +590,8 @@ static void *my_alloc(size_t size)
void *ret;
if ((ret = driver_alloc(size)) == NULL) {
/* May or may not work... */
- fprintf(stderr, "Could not allocate %d bytes of memory in %s.",
- (int) size, __FILE__);
+ fprintf(stderr, "Could not allocate %lu bytes of memory in %s.",
+ (unsigned long) size, __FILE__);
exit(1);
}
return ret;
@@ -605,8 +605,8 @@ static ErlDrvBinary *my_alloc_binary(int size)
ErlDrvBinary *ret;
if ((ret = driver_alloc_binary(size)) == NULL) {
/* May or may not work... */
- fprintf(stderr, "Could not allocate a binary of %d bytes in %s.",
- (int) size, __FILE__);
+ fprintf(stderr, "Could not allocate a binary of %lu bytes in %s.",
+ (unsigned long) size, __FILE__);
exit(1);
}
return ret;
diff --git a/lib/stdlib/src/escript.erl b/lib/stdlib/src/escript.erl
index 27e70ac4d4..498d850df3 100644
--- a/lib/stdlib/src/escript.erl
+++ b/lib/stdlib/src/escript.erl
@@ -22,7 +22,7 @@
-export([script_name/0, create/2, extract/2]).
%% Internal API.
--export([start/0, start/1]).
+-export([start/0, start/1, parse_file/1]).
%%-----------------------------------------------------------------------
@@ -346,7 +346,8 @@ parse_and_run(File, Args, Options) ->
case Source of
archive ->
{ok, FileInfo} = file:read_file_info(File),
- case code:set_primary_archive(File, FormsOrBin, FileInfo) of
+ case code:set_primary_archive(File, FormsOrBin, FileInfo,
+ fun escript:parse_file/1) of
ok when CheckOnly ->
case code:load_file(Module) of
{module, _} ->
@@ -396,6 +397,19 @@ parse_and_run(File, Args, Options) ->
%% Parse script
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Only used as callback by erl_prim_loader
+parse_file(File) ->
+ try parse_file(File, false) of
+ {_Source, _Module, FormsOrBin, _HasRecs, _Mode}
+ when is_binary(FormsOrBin) ->
+ {ok, FormsOrBin};
+ _ ->
+ {error, no_archive_bin}
+ catch
+ throw:Reason ->
+ {error, Reason}
+ end.
+
parse_file(File, CheckOnly) ->
{HeaderSz, NextLineNo, Fd, Sections} =
parse_header(File, false),
diff --git a/lib/stdlib/src/gen_server.erl b/lib/stdlib/src/gen_server.erl
index 59c6d240ba..04308a51b7 100644
--- a/lib/stdlib/src/gen_server.erl
+++ b/lib/stdlib/src/gen_server.erl
@@ -270,7 +270,7 @@ enter_loop(Mod, Options, State) ->
enter_loop(Mod, Options, State, self(), infinity).
enter_loop(Mod, Options, State, ServerName = {Scope, _})
- when Scope == local; Scope == local ->
+ when Scope == local; Scope == global ->
enter_loop(Mod, Options, State, ServerName, infinity);
enter_loop(Mod, Options, State, ServerName = {via, _, _}) ->
diff --git a/lib/stdlib/test/escript_SUITE.erl b/lib/stdlib/test/escript_SUITE.erl
index 7ed1ee742a..38c085616d 100644
--- a/lib/stdlib/test/escript_SUITE.erl
+++ b/lib/stdlib/test/escript_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -29,6 +29,7 @@
module_script/1,
beam_script/1,
archive_script/1,
+ archive_script_file_access/1,
epp/1,
create_and_extract/1,
foldl/1,
@@ -44,7 +45,8 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[basic, errors, strange_name, emulator_flags,
module_script, beam_script, archive_script, epp,
- create_and_extract, foldl, overflow].
+ create_and_extract, foldl, overflow,
+ archive_script_file_access].
groups() ->
[].
@@ -356,7 +358,7 @@ beam_script(Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Create an archive file containing two entire applications plus two
%% alternate main modules. Generate a new escript containing the archive
-%% (with .app and .beam files and ) and the escript header.
+%% (with .app and .beam files and) and the escript header.
archive_script(Config) when is_list(Config) ->
%% Copy the orig files to priv_dir
@@ -464,6 +466,147 @@ archive_script(Config) when is_list(Config) ->
ok.
+%% Test the correction of OTP-10071
+%% The errors identified are
+%%
+%% a) If primary archive was named "xxx", then a file in the same
+%% directory named "xxxyyy" would be interpreted as a file named yyy
+%% inside the archive.
+%%
+%% b) erl_prim_loader did not correctly create and normalize absolute
+%% paths for primary archive and files inside it, so unless given
+%% with exact same path files inside the archive would not be
+%% found. E.g. if escript was started as ./xxx then "xxx/file"
+%% would not be found since erl_prim_loader would try to match
+%% /full/path/to/xxx with /full/path/to/./xxx. Same problem with
+%% ../. Also, the use of symlinks in the path to the archive would
+%% cause problems.
+%%
+%% c) Depending on how the primary archive was built,
+%% erl_prim_loader:list_dir/1 would sometimes return an empty string
+%% inside the file list. This was a virtual element representing the
+%% top directory of the archive. This shall not occur.
+%%
+archive_script_file_access(Config) when is_list(Config) ->
+ %% Copy the orig files to priv_dir
+ DataDir = ?config(data_dir, Config),
+ PrivDir = ?config(priv_dir, Config),
+
+ MainMod = "archive_script_file_access",
+ MainSrc = MainMod ++ ".erl",
+ MainBeam = MainMod ++ ".beam",
+
+ Archive = filename:join([PrivDir, "archive_script_file_access.zip"]),
+ ?line {ok, _} = zip:create(Archive, ["archive_script_file_access"],
+ [{compress, []}, {cwd, DataDir}]),
+ ?line {ok, _} = zip:extract(Archive, [{cwd, PrivDir}]),
+ TopDir = filename:join([PrivDir, "archive_script_file_access"]),
+
+ %% Compile the code
+ ?line ok = compile_files([MainSrc], TopDir, TopDir),
+
+ %% First, create a file structure which will be included in the archive:
+ %%
+ %% dir1/
+ %% dir1/subdir1/
+ %% dir1/subdir1/file1
+ %%
+ {ok, OldDir} = file:get_cwd(),
+ ok = file:set_cwd(TopDir),
+ DummyDir = "dir1",
+ DummySubDir = filename:join(DummyDir, "subdir1"),
+ RelDummyFile = filename:join(DummySubDir, "file1"),
+ DummyFile = filename:join(TopDir,RelDummyFile),
+ ok = filelib:ensure_dir(DummyFile),
+ ok = file:write_file(DummyFile, ["foo\nbar\nbaz"]),
+
+ %% 1. Create zip archive by adding the dummy file and the beam
+ %% file as binaries to zip.
+ %%
+ %% This used to provoke the following issues when the script was run as
+ %% "./<script_name>":
+ %% a. erl_prim_loader:read_file_info/1 returning 'error'
+ %% b. erl_prim_loader:list_dir/1 returning {ok, ["dir1", [], "file1"]}
+ %% leading to an infinite loop in reltool_target:spec_dir/1
+ Files1 =
+ lists:map(fun(Filename) ->
+ {ok, Bin} = file:read_file(Filename),
+ {Filename,Bin}
+ end,
+ [RelDummyFile,MainBeam]),
+ {ok, {"mem", Bin1}} = zip:create("mem", Files1, [memory]),
+
+ %% Create the escript
+ ScriptName1 = "archive_script_file_access1",
+ Script1 = filename:join([PrivDir, ScriptName1]),
+ Flags = "-escript main " ++ MainMod,
+ ok = escript:create(Script1,[shebang,{emu_args,Flags},{archive,Bin1}]),
+ ok = file:change_mode(Script1,8#00744),
+
+ %% If supported, create a symlink to the script. This is used to
+ %% test error b) described above this test case.
+ SymlinkName1 = "symlink_to_"++ScriptName1,
+ Symlink1 = filename:join([PrivDir, SymlinkName1]),
+ file:make_symlink(ScriptName1,Symlink1), % will fail if not supported
+
+ %% Also add a dummy file in the same directory with the same name
+ %% as the script except is also has an extension. This used to
+ %% test error a) described above this test case.
+ ok = file:write_file(Script1 ++ ".extension",
+ <<"same name as script, but with extension">>),
+
+ %% Change to script's directory and run it as "./<script_name>"
+ ok = file:set_cwd(PrivDir),
+ run(PrivDir, "./" ++ ScriptName1 ++ " " ++ ScriptName1,
+ [<<"ExitCode:0">>]),
+ ok = file:set_cwd(TopDir),
+
+
+ %% 2. Create zip archive by letting zip read the files from the file system
+ %%
+ %% The difference compared to the archive_script_file_access1 is
+ %% that this will have a file element for each directory in the
+ %% archive - while archive_script_file_access1 will only have a
+ %% file element per regular file.
+ Files2 = [DummyDir,MainBeam],
+ {ok, {"mem", Bin2}} = zip:create("mem", Files2, [memory]),
+
+ %% Create the escript
+ ScriptName2 = "archive_script_file_access2",
+ Script2 = filename:join([PrivDir, ScriptName2]),
+ ok = escript:create(Script2,[shebang,{emu_args,Flags},{archive,Bin2}]),
+ ok = file:change_mode(Script2,8#00744),
+
+ %% Also add a dummy file in the same directory with the same name
+ %% as the script except is also has an extension. This used to
+ %% test error a) described above this test case.
+ ok = file:write_file(Script2 ++ ".extension",
+ <<"same name as script, but with extension">>),
+
+ %% If supported, create a symlink to the script. This is used to
+ %% test error b) described above this test case.
+ SymlinkName2 = "symlink_to_"++ScriptName2,
+ Symlink2 = filename:join([PrivDir, SymlinkName2]),
+ file:make_symlink(ScriptName2,Symlink2), % will fail if not supported
+
+ %% Change to script's directory and run it as "./<script_name>"
+ ok = file:set_cwd(PrivDir),
+ run(PrivDir, "./" ++ ScriptName2 ++ " " ++ ScriptName2,
+ [<<"ExitCode:0">>]),
+
+ %% 3. If symlinks are supported, run one of the scripts via a symlink.
+ %%
+ %% This is in order to test error b) described above this test case.
+ case file:read_link(Symlink2) of
+ {ok,_} ->
+ run(PrivDir, "./" ++ SymlinkName2 ++ " " ++ ScriptName2,
+ [<<"ExitCode:0">>]);
+ _ -> % not supported
+ ok
+ end,
+ ok = file:set_cwd(OldDir).
+
+
compile_app(TopDir, AppName) ->
AppDir = filename:join([TopDir, AppName]),
SrcDir = filename:join([AppDir, "src"]),
diff --git a/lib/stdlib/test/escript_SUITE_data/archive_script/archive_script_main2.erl b/lib/stdlib/test/escript_SUITE_data/archive_script/archive_script_main2.erl
index de56579998..431a51b0e5 100644
--- a/lib/stdlib/test/escript_SUITE_data/archive_script/archive_script_main2.erl
+++ b/lib/stdlib/test/escript_SUITE_data/archive_script/archive_script_main2.erl
@@ -21,6 +21,8 @@
-export([main/1]).
+-include_lib("kernel/include/file.hrl").
+
-define(DUMMY, archive_script_dummy).
-define(DICT, archive_script_dict).
@@ -32,7 +34,7 @@ main(MainArgs) ->
io:format("dummy:~p\n",[[E || E <- ErlArgs, element(1, E) =:= ?DUMMY]]),
%% Start the applications
- {error, {not_started, ?DICT}} = application:start(archive_script_dummy),
+ {error, {not_started, ?DICT}} = application:start(?DUMMY),
ok = application:start(?DICT),
ok = application:start(?DUMMY),
@@ -57,4 +59,17 @@ main(MainArgs) ->
ok = ?DICT:erase(Tab, Key),
error = ?DICT:find(Tab, Key),
ok = ?DICT:erase(Tab),
+
+ %% Check mtime related caching bug with escript/primary archive files
+ Escript = escript:script_name(),
+ {ok, FileInfo} = file:read_file_info(Escript),
+ %% Modify mtime of archive file and try to reload module
+ FileInfo2 = FileInfo#file_info{mtime=calendar:now_to_local_time(now())},
+ ok = file:write_file_info(Escript, FileInfo2),
+ Module = ?DICT,
+ {file, _} = code:is_loaded(Module),
+ true = code:delete(Module),
+ false = code:is_loaded(Module),
+ {module, Module} = code:ensure_loaded(Module),
+
ok.
diff --git a/lib/stdlib/test/escript_SUITE_data/archive_script_file_access/archive_script_file_access.erl b/lib/stdlib/test/escript_SUITE_data/archive_script_file_access/archive_script_file_access.erl
new file mode 100644
index 0000000000..b03c8ba70d
--- /dev/null
+++ b/lib/stdlib/test/escript_SUITE_data/archive_script_file_access/archive_script_file_access.erl
@@ -0,0 +1,105 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(archive_script_file_access).
+-behaviour(escript).
+
+-export([main/1]).
+
+-include_lib("kernel/include/file.hrl").
+
+main([RelArchiveFile]) ->
+
+ AbsArchiveFile = filename:absname(RelArchiveFile),
+ DotSlashArchiveFile = "./" ++ RelArchiveFile,
+
+ Beam = atom_to_list(?MODULE) ++ ".beam",
+ AbsBeam = filename:join(AbsArchiveFile,Beam),
+ RelBeam = filename:join(RelArchiveFile,Beam),
+ DotSlashBeam = filename:join(DotSlashArchiveFile,Beam),
+ Dir = "dir1",
+ AbsDir = filename:join(AbsArchiveFile,Dir),
+ RelDir = filename:join(RelArchiveFile,Dir),
+ DotSlashDir = filename:join(DotSlashArchiveFile,Dir),
+ SubDir = "subdir1",
+ AbsSubDir = filename:join(AbsDir,SubDir),
+ RelSubDir = filename:join(RelDir,SubDir),
+ DotSlashSubDir = filename:join(DotSlashDir,SubDir),
+
+ {ok,List1} = erl_prim_loader:list_dir(AbsArchiveFile),
+ {ok,List1} = erl_prim_loader:list_dir(RelArchiveFile),
+ {ok,List1} = erl_prim_loader:list_dir(DotSlashArchiveFile),
+ {ok,List1} = erl_prim_loader:list_dir(AbsArchiveFile ++ "/"),
+ {ok,List1} = erl_prim_loader:list_dir(AbsArchiveFile ++ "/."),
+ {ok,List1} = erl_prim_loader:list_dir(filename:join([AbsDir,".."])),
+ {ok,List1} = erl_prim_loader:list_dir(filename:join([RelDir,".."])),
+ {ok,List1} = erl_prim_loader:list_dir(filename:join([DotSlashDir,".."])),
+ {ok,List1} = erl_prim_loader:list_dir(filename:join([AbsSubDir,"..",".."])),
+ {ok,List1} = erl_prim_loader:list_dir(filename:join([RelSubDir,"..",".."])),
+ {ok,List1} = erl_prim_loader:list_dir(filename:join([DotSlashSubDir,"..",".."])),
+ false = lists:member([],List1),
+
+ %% If symlinks are supported on this platform...
+ RelSymlinkArchiveFile = "symlink_to_" ++ RelArchiveFile,
+ case file:read_link(RelSymlinkArchiveFile) of
+ {ok,_} ->
+ DotSlashSymlinkArchiveFile = "./" ++ RelSymlinkArchiveFile,
+ AbsSymlinkArchiveFile=filename:join(filename:dirname(AbsArchiveFile),
+ RelSymlinkArchiveFile),
+ {ok,List1} = erl_prim_loader:list_dir(AbsSymlinkArchiveFile),
+ {ok,List1} = erl_prim_loader:list_dir(RelSymlinkArchiveFile),
+ {ok,List1} = erl_prim_loader:list_dir(DotSlashSymlinkArchiveFile);
+ _ -> % not supported
+ ok
+ end,
+
+
+ {ok,List2} = erl_prim_loader:list_dir(AbsDir),
+ {ok,List2} = erl_prim_loader:list_dir(RelDir),
+ {ok,List2} = erl_prim_loader:list_dir(DotSlashDir),
+ false = lists:member([],List2),
+
+ error = erl_prim_loader:list_dir(AbsBeam),
+ error = erl_prim_loader:list_dir(RelBeam),
+ error = erl_prim_loader:list_dir(DotSlashBeam),
+
+ error = erl_prim_loader:get_file(AbsArchiveFile),
+ error = erl_prim_loader:get_file(RelArchiveFile),
+ error = erl_prim_loader:get_file(DotSlashArchiveFile),
+ error = erl_prim_loader:get_file(AbsArchiveFile ++ "/"),
+ error = erl_prim_loader:get_file(AbsArchiveFile ++ "/."),
+ {ok,Bin,AbsBeam} = erl_prim_loader:get_file(AbsBeam),
+ {ok,Bin,RelBeam} = erl_prim_loader:get_file(RelBeam),
+ {ok,Bin,DotSlashBeam} = erl_prim_loader:get_file(DotSlashBeam),
+
+ {ok,#file_info{type=directory}=DFI} =
+ erl_prim_loader:read_file_info(AbsArchiveFile),
+ {ok,DFI} = erl_prim_loader:read_file_info(RelArchiveFile),
+ {ok,DFI} = erl_prim_loader:read_file_info(DotSlashArchiveFile),
+ {ok,DFI} = erl_prim_loader:read_file_info(AbsArchiveFile ++ "/"),
+ {ok,DFI} = erl_prim_loader:read_file_info(AbsArchiveFile ++ "/."),
+ {ok,#file_info{type=regular}=RFI} = erl_prim_loader:read_file_info(AbsBeam),
+ {ok,RFI} = erl_prim_loader:read_file_info(RelBeam),
+ {ok,RFI} = erl_prim_loader:read_file_info(DotSlashBeam),
+
+ F = AbsArchiveFile ++ ".extension",
+ error = erl_prim_loader:list_dir(F),
+ {ok,_,_} = erl_prim_loader:get_file(F),
+ {ok,#file_info{type=regular}} = erl_prim_loader:read_file_info(F),
+
+ ok.
diff --git a/lib/stdlib/test/gen_server_SUITE.erl b/lib/stdlib/test/gen_server_SUITE.erl
index cdf15ba017..48ef7e55ed 100644
--- a/lib/stdlib/test/gen_server_SUITE.erl
+++ b/lib/stdlib/test/gen_server_SUITE.erl
@@ -37,7 +37,8 @@
% spawn export
-export([spec_init_local/2, spec_init_global/2, spec_init_via/2,
- spec_init_default_timeout/2, spec_init_anonymous/1,
+ spec_init_default_timeout/2, spec_init_global_default_timeout/2,
+ spec_init_anonymous/1,
spec_init_anonymous_default_timeout/1,
spec_init_not_proc_lib/1, cast_fast_messup/0]).
@@ -749,7 +750,7 @@ spec_init(suite) ->
spec_init(Config) when is_list(Config) ->
OldFlag = process_flag(trap_exit, true),
-
+
?line {ok, Pid0} = start_link(spec_init_local, [{ok, my_server}, []]),
?line ok = gen_server:call(Pid0, started_p),
?line ok = gen_server:call(Pid0, stop),
@@ -819,6 +820,14 @@ spec_init(Config) when is_list(Config) ->
test_server:fail(gen_server_did_not_die)
end,
+ %% Before the OTP-10130 fix this failed because a timeout message
+ %% was generated as the spawned process crashed because a {global, Name}
+ %% was matched as a timeout value instead of matching on scope.
+ {ok, _PidHurra} =
+ start_link(spec_init_global_default_timeout, [{ok, hurra}, []]),
+ timer:sleep(1000),
+ ok = gen_server:call(_PidHurra, started_p),
+
?line Pid5 =
erlang:spawn_link(?MODULE, spec_init_not_proc_lib, [[]]),
receive
@@ -1125,6 +1134,15 @@ spec_init_default_timeout({ok, Name}, Options) ->
%% Supervised init can occur here ...
gen_server:enter_loop(?MODULE, Options, {}, {local, Name}).
+%% OTP-10130, A bug was introduced where global scope was not matched when
+%% enter_loop/4 was called (no timeout).
+spec_init_global_default_timeout({ok, Name}, Options) ->
+ process_flag(trap_exit, true),
+ global:register_name(Name, self()),
+ proc_lib:init_ack({ok, self()}),
+ %% Supervised init can occur here ...
+ gen_server:enter_loop(?MODULE, Options, {}, {global, Name}).
+
spec_init_anonymous(Options) ->
process_flag(trap_exit, true),
proc_lib:init_ack({ok, self()}),