aboutsummaryrefslogtreecommitdiffstats
path: root/lib/test_server
diff options
context:
space:
mode:
authorLars Thorsen <[email protected]>2015-05-20 10:12:46 +0200
committerLars Thorsen <[email protected]>2015-05-20 10:12:46 +0200
commit7b81ed532898c735ad8901723df373e5f0246127 (patch)
tree5f69565b5016fb08d3bd62ecaf53fa12d4cb7c51 /lib/test_server
parentfd63f2b7108e2607e43d709e6d3885e4a7406ec8 (diff)
parent5bd9afcd35d1a5c46dce8e75c650841a3061f45b (diff)
downloadotp-7b81ed532898c735ad8901723df373e5f0246127.tar.gz
otp-7b81ed532898c735ad8901723df373e5f0246127.tar.bz2
otp-7b81ed532898c735ad8901723df373e5f0246127.zip
Merge branch 'master' of git-server:otp
Conflicts: lib/orber/src/orber.app.src
Diffstat (limited to 'lib/test_server')
-rw-r--r--lib/test_server/doc/src/example_chapter.xml28
-rw-r--r--lib/test_server/doc/src/test_server.xml66
-rw-r--r--lib/test_server/include/test_server.hrl2
-rw-r--r--lib/test_server/src/erl2html2.erl48
-rw-r--r--lib/test_server/src/test_server.app.src6
-rw-r--r--lib/test_server/src/test_server.erl6
-rw-r--r--lib/test_server/src/ts.erl18
-rw-r--r--lib/test_server/test/erl2html2_SUITE.erl66
-rw-r--r--lib/test_server/test/erl2html2_SUITE_data/include/header3.hrl1
-rw-r--r--lib/test_server/test/erl2html2_SUITE_data/m1.erl6
-rw-r--r--lib/test_server/vsn.mk2
11 files changed, 119 insertions, 130 deletions
diff --git a/lib/test_server/doc/src/example_chapter.xml b/lib/test_server/doc/src/example_chapter.xml
index 0ebc85da09..6bc0cfaebe 100644
--- a/lib/test_server/doc/src/example_chapter.xml
+++ b/lib/test_server/doc/src/example_chapter.xml
@@ -47,7 +47,7 @@
-define(default_timeout, ?t:minutes(1)).
init_per_testcase(_Case, Config) ->
- ?line Dog=?t:timetrap(?default_timeout),
+ Dog=?t:timetrap(?default_timeout),
[{watchdog, Dog}|Config].
end_per_testcase(_Case, Config) ->
Dog=?config(watchdog, Config),
@@ -72,8 +72,8 @@ not_started_func1(suite) ->
not_started_func1(doc) ->
["Testing function 1 when application is not started"].
not_started_func1(Config) when list(Config) ->
- ?line {error, not_started} = myapp:func1(dummy_ref,1),
- ?line {error, not_started} = myapp:func1(dummy_ref,2),
+ {error, not_started} = myapp:func1(dummy_ref,1),
+ {error, not_started} = myapp:func1(dummy_ref,2),
ok.
not_started_func2(suite) ->
@@ -81,8 +81,8 @@ not_started_func2(suite) ->
not_started_func2(doc) ->
["Testing function 2 when application is not started"].
not_started_func2(Config) when list(Config) ->
- ?line {error, not_started} = myapp:func2(dummy_ref,1),
- ?line {error, not_started} = myapp:func2(dummy_ref,2),
+ {error, not_started} = myapp:func2(dummy_ref,1),
+ {error, not_started} = myapp:func2(dummy_ref,2),
ok.
@@ -90,7 +90,7 @@ not_started_func2(Config) when list(Config) ->
start(doc) ->
["Testing start of my application."];
start(Config) when list(Config) ->
- ?line Ref = myapp:start(),
+ Ref = myapp:start(),
case erlang:whereis(my_main_process) of
Pid when pid(Pid) ->
[{myapp_ref,Ref}|Config];
@@ -105,9 +105,9 @@ func1(suite) ->
func1(doc) ->
["Test that func1 returns ok when argument is 1 and error if argument is 2"];
func1(Config) when list(Config) ->
- ?line Ref = ?config(myapp_ref,Config),
- ?line ok = myapp:func1(Ref,1),
- ?line error = myapp:func1(Ref,2),
+ Ref = ?config(myapp_ref,Config),
+ ok = myapp:func1(Ref,1),
+ error = myapp:func1(Ref,2),
ok.
func2(suite) ->
@@ -115,17 +115,17 @@ func2(suite) ->
func2(doc) ->
["Test that func1 returns ok when argument is 3 and error if argument is 4"];
func2(Config) when list(Config) ->
- ?line Ref = ?config(myapp_ref,Config),
- ?line ok = myapp:func2(Ref,3),
- ?line error = myapp:func2(Ref,4),
+ Ref = ?config(myapp_ref,Config),
+ ok = myapp:func2(Ref,3),
+ error = myapp:func2(Ref,4),
ok.
%% No specification clause needed for a cleanup function in a conf case!!!
stop(doc) ->
["Testing termination of my application"];
stop(Config) when list(Config) ->
- ?line Ref = ?config(myapp_ref,Config),
- ?line ok = myapp:stop(Ref),
+ Ref = ?config(myapp_ref,Config),
+ ok = myapp:stop(Ref),
case erlang:whereis(my_main_process) of
undefined ->
lists:keydelete(myapp_ref,1,Config);
diff --git a/lib/test_server/doc/src/test_server.xml b/lib/test_server/doc/src/test_server.xml
index ed5569e1fe..b98e434c03 100644
--- a/lib/test_server/doc/src/test_server.xml
+++ b/lib/test_server/doc/src/test_server.xml
@@ -811,46 +811,12 @@ Only valid for peer nodes. Note that slave nodes always
</func>
</funcs>
- <section>
- <title>TEST SUITE LINE NUMBERS</title>
- <p>If a test case fails, the test server can report the exact line
- number at which it failed. There are two ways of doing this,
- either by using the <c>line</c> macro or by using the
- <c>test_server_line</c> parse transform.
- </p>
- <p>The <c>line</c> macro is described under TEST SUITE SUPPORT
- MACROS below. The <c>line</c> macro will only report the last line
- executed when a test case failed.
- </p>
- <p>The <c>test_server_line</c> parse transform is activated by
- including the headerfile <c>test_server_line.hrl</c> in the test
- suite. When doing this, it is important that the
- <c>test_server_line</c> module is in the code path of the erlang
- node compiling the test suite. The parse transform will report a
- history of a maximum of 10 lines when a test case
- fails. Consecutive lines in the same function are not shown.
- </p>
- <p>The attribute <c>-no_lines(FuncList).</c> can be used in the
- test suite to exclude specific functions from the parse
- transform. This is necessary e.g. for functions that are executed
- on old (i.e. &lt;R10B) OTP releases. <c>FuncList = [{Func,Arity}]</c>.
- </p>
- <p>If both the <c>line</c> macro and the parse transform is used in
- the same module, the parse transform will overrule the macro.
- </p>
- </section>
<section>
<title>TEST SUITE SUPPORT MACROS</title>
<p>There are some macros defined in the <c>test_server.hrl</c>
that are quite useful for test suite programmers:
</p>
- <p>The <em>line</em> macro, is quite
- essential when writing test cases. It tells the test server
- exactly what line of code that is being executed, so that it can
- report this line back if the test case fails. Use this macro at
- the beginning of every test case line of code.
- </p>
<p>The <em>config</em> macro, is used to
retrieve information from the <c>Config</c> variable sent to all
test cases. It is used with two arguments, where the first is the
@@ -867,24 +833,20 @@ Only valid for peer nodes. Note that slave nodes always
<item>Whatever added by conf test cases or
<c>init_per_testcase/2</c></item>
</list>
- <p>Examples of the <c>line</c> and <c>config</c> macros can be
- seen in the Examples chapter in the user's guide.
- </p>
- <p>If the <c>line_trace</c> macro is defined, you will get a
- timestamp (<c>erlang:now()</c>) in your minor log for each
- <c>line</c> macro in your suite. This way you can at any time see
- which line is currently being executed, and when the line was
- called.
- </p>
- <p>The <c>line_trace</c> macro can also be used together with the
- <c>test_server_line</c> parse transform described above. A
- timestamp will then be written for each line in the suite, except
- for functions stated in the <c>-no_lines</c> attribute.
- </p>
- <p>The <c>line_trace</c> macro can e.g. be defined as a compile
- option, like this:
- <br></br>
-<c>erlc -W -Dline_trace my_SUITE.erl</c></p>
+ <p>Examples of the <c>config</c> macro can be seen in the Examples chapter
+ in the user's guide.</p>
+ <p>The <em>line</em> and <em>line_trace</em> macros are deprecated, see
+ below.</p>
+ </section>
+
+ <section>
+ <title>TEST SUITE LINE NUMBERS</title>
+ <p>In the past, ERTS did not produce line numbers when generating
+ stacktraces, test_server was thus unable to provide them when reporting
+ test failures. It had instead two different mecanisms to do it: either by
+ using the <c>line</c> macro or by using the <c>test_server_line</c> parse
+ transform. Both are deprecated and should not be used in new tests
+ anymore.</p>
</section>
</erlref>
diff --git a/lib/test_server/include/test_server.hrl b/lib/test_server/include/test_server.hrl
index 36e7e1f83d..f206374116 100644
--- a/lib/test_server/include/test_server.hrl
+++ b/lib/test_server/include/test_server.hrl
@@ -21,7 +21,7 @@
-line_trace(true).
-define(line,
io:format(lists:concat([?MODULE,",",integer_to_list(?LINE),": ~p"]),
- [erlang:now()]),).
+ [erlang:monotonic_time()-erlang:system_info(start_time)]),).
-else.
-define(line,).
-endif.
diff --git a/lib/test_server/src/erl2html2.erl b/lib/test_server/src/erl2html2.erl
index 2e443c7b8b..b0b5c40965 100644
--- a/lib/test_server/src/erl2html2.erl
+++ b/lib/test_server/src/erl2html2.erl
@@ -109,27 +109,26 @@ parse_file(File, InclPath) ->
Error
end.
-parse_preprocessed_file(Epp,File,InCorrectFile) ->
+parse_preprocessed_file(Epp, File, InCorrectFile) ->
case epp:parse_erl_form(Epp) of
{ok,Form} ->
case Form of
{attribute,_,file,{File,_}} ->
- parse_preprocessed_file(Epp,File,true);
+ parse_preprocessed_file(Epp, File, true);
{attribute,_,file,{_OtherFile,_}} ->
- parse_preprocessed_file(Epp,File,false);
+ parse_preprocessed_file(Epp, File, false);
{function,L,F,A,Cs} when InCorrectFile ->
{CLs,LastCL} = find_clause_lines(Cs, []),
- Clauses = [{clause,get_line(CL)} ||
- {clause,CL,_,_,_} <- tl(CLs)],
- [{atom_to_list(F),A,get_line(L),get_line(LastCL)} | Clauses] ++
- parse_preprocessed_file(Epp,File,true);
+ %% tl(CLs) cause we know the start line already
+ [{atom_to_list(F),A,get_line(L),LastCL} | tl(CLs)] ++
+ parse_preprocessed_file(Epp, File, true);
_ ->
- parse_preprocessed_file(Epp,File,InCorrectFile)
+ parse_preprocessed_file(Epp, File, InCorrectFile)
end;
{error,Reason={_L,epp,{undefined,_Macro,none}}} ->
throw({error,Reason,InCorrectFile});
{error,_Reason} ->
- parse_preprocessed_file(Epp,File,InCorrectFile);
+ parse_preprocessed_file(Epp, File, InCorrectFile);
{eof,_Location} ->
[]
end.
@@ -150,9 +149,8 @@ parse_non_preprocessed_file(Epp, File, Location) ->
try erl_syntax:revert(Tree) of
{function,L,F,A,Cs} ->
{CLs,LastCL} = find_clause_lines(Cs, []),
- Clauses = [{clause,get_line(CL)} ||
- {clause,CL,_,_,_} <- tl(CLs)],
- [{atom_to_list(F),A,get_line(L),get_line(LastCL)} | Clauses] ++
+ %% tl(CLs) cause we know the start line already
+ [{atom_to_list(F),A,get_line(L),LastCL} | tl(CLs)] ++
parse_non_preprocessed_file(Epp, File, Location1);
_ ->
parse_non_preprocessed_file(Epp, File, Location1)
@@ -172,17 +170,21 @@ get_line(Anno) ->
%%% Find the line number of the last expression in the function
find_clause_lines([{clause,CL,_Params,_Op,Exprs}], CLs) -> % last clause
try tuple_to_list(lists:last(Exprs)) of
- [_Type,ExprLine | _] ->
- {lists:reverse([{clause,CL}|CLs]), ExprLine};
+ [_Type,ExprLine | _] when is_integer(ExprLine) ->
+ {lists:reverse([{clause,get_line(CL)}|CLs]), get_line(ExprLine)};
+ [tree,_ | Exprs1] ->
+ find_clause_lines([{clause,CL,undefined,undefined,Exprs1}], CLs);
+ [macro,{_var,ExprLine,_MACRO} | _] when is_integer(ExprLine) ->
+ {lists:reverse([{clause,get_line(CL)}|CLs]), get_line(ExprLine)};
_ ->
- {lists:reverse([{clause,CL}|CLs]), CL}
+ {lists:reverse([{clause,get_line(CL)}|CLs]), get_line(CL)}
catch
_:_ ->
- {lists:reverse([{clause,CL}|CLs]), CL}
+ {lists:reverse([{clause,get_line(CL)}|CLs]), get_line(CL)}
end;
find_clause_lines([{clause,CL,_Params,_Op,_Exprs} | Cs], CLs) ->
- find_clause_lines(Cs, [{clause,CL}|CLs]).
+ find_clause_lines(Cs, [{clause,get_line(CL)}|CLs]).
%%%-----------------------------------------------------------------
%%% Add a link target for each line and one for each function definition.
@@ -190,18 +192,18 @@ build_html(SFd,DFd,Encoding,FuncsAndCs) ->
build_html(SFd,DFd,Encoding,file:read_line(SFd),1,FuncsAndCs,
false,undefined).
-%% function start line found
-build_html(SFd,DFd,Enc,{ok,Str},L0,[{F,A,L0,LastL}|FuncsAndCs],
- _IsFuncDef,_FAndLastL) ->
- FALink = test_server_ctrl:uri_encode(F++"-"++integer_to_list(A),utf8),
- file:write(DFd,["<a name=\"",to_raw_list(FALink,Enc),"\"/>"]),
- build_html(SFd,DFd,Enc,{ok,Str},L0,FuncsAndCs,true,{F,LastL});
%% line of last expression in function found
build_html(SFd,DFd,Enc,{ok,Str},LastL,FuncsAndCs,_IsFuncDef,{F,LastL}) ->
LastLineLink = test_server_ctrl:uri_encode(F++"-last_expr",utf8),
file:write(DFd,["<a name=\"",
to_raw_list(LastLineLink,Enc),"\"/>"]),
build_html(SFd,DFd,Enc,{ok,Str},LastL,FuncsAndCs,true,undefined);
+%% function start line found
+build_html(SFd,DFd,Enc,{ok,Str},L0,[{F,A,L0,LastL}|FuncsAndCs],
+ _IsFuncDef,_FAndLastL) ->
+ FALink = test_server_ctrl:uri_encode(F++"-"++integer_to_list(A),utf8),
+ file:write(DFd,["<a name=\"",to_raw_list(FALink,Enc),"\"/>"]),
+ build_html(SFd,DFd,Enc,{ok,Str},L0,FuncsAndCs,true,{F,LastL});
build_html(SFd,DFd,Enc,{ok,Str},L,[{clause,L}|FuncsAndCs],
_IsFuncDef,FAndLastL) ->
build_html(SFd,DFd,Enc,{ok,Str},L,FuncsAndCs,true,FAndLastL);
diff --git a/lib/test_server/src/test_server.app.src b/lib/test_server/src/test_server.app.src
index 5538e8b851..bdd9d28444 100644
--- a/lib/test_server/src/test_server.app.src
+++ b/lib/test_server/src/test_server.app.src
@@ -32,7 +32,7 @@
test_server_break_process]},
{applications, [kernel,stdlib]},
{env, []},
- {runtime_dependencies, ["tools-2.6.14","stdlib-2.0","runtime_tools-1.8.14",
- "observer-2.0","kernel-3.0","inets-5.10",
- "syntax_tools-1.6.16","erts-7.0"]}]}.
+ {runtime_dependencies, ["tools-2.8","stdlib-2.5","runtime_tools-1.8.16",
+ "observer-2.1","kernel-4.0","inets-6.0",
+ "syntax_tools-1.7","erts-7.0"]}]}.
diff --git a/lib/test_server/src/test_server.erl b/lib/test_server/src/test_server.erl
index 7cb9c4bb5a..785e687b92 100644
--- a/lib/test_server/src/test_server.erl
+++ b/lib/test_server/src/test_server.erl
@@ -2491,11 +2491,7 @@ appup_test(App) ->
%% Checks wether the module is natively compiled or not.
is_native(Mod) ->
- case catch Mod:module_info(native_addresses) of
- [_|_] -> true;
- _Other -> false
- end.
-
+ (catch Mod:module_info(native)) =:= true.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% comment(String) -> ok
diff --git a/lib/test_server/src/ts.erl b/lib/test_server/src/ts.erl
index 469593e947..85f97656ff 100644
--- a/lib/test_server/src/ts.erl
+++ b/lib/test_server/src/ts.erl
@@ -262,18 +262,28 @@ run_all(_Vars) ->
run_some([], _Opts) ->
ok;
-run_some([{App,Mod}|Apps], Opts) ->
+run_some(Apps, Opts) ->
+ case proplists:get_value(test_category, Opts) of
+ bench ->
+ check_and_run(fun(Vars) -> ts_benchmark:run(Apps, Opts, Vars) end);
+ _Other ->
+ run_some1(Apps, Opts)
+ end.
+
+run_some1([], _Opts) ->
+ ok;
+run_some1([{App,Mod}|Apps], Opts) ->
case run(App, Mod, Opts) of
ok -> ok;
Error -> io:format("~p: ~p~n",[{App,Mod},Error])
end,
- run_some(Apps, Opts);
-run_some([App|Apps], Opts) ->
+ run_some1(Apps, Opts);
+run_some1([App|Apps], Opts) ->
case run(App, Opts) of
ok -> ok;
Error -> io:format("~p: ~p~n",[App,Error])
end,
- run_some(Apps, Opts).
+ run_some1(Apps, Opts).
%% This can be used from command line. Both App and
%% TestCategory must be specified. App may be 'all'
diff --git a/lib/test_server/test/erl2html2_SUITE.erl b/lib/test_server/test/erl2html2_SUITE.erl
index 908985c879..796b84dedd 100644
--- a/lib/test_server/test/erl2html2_SUITE.erl
+++ b/lib/test_server/test/erl2html2_SUITE.erl
@@ -130,15 +130,7 @@ groups() ->
%% @end
%%--------------------------------------------------------------------
all() ->
- [m1].
-
-%%--------------------------------------------------------------------
-%% @spec TestCase() -> Info
-%% Info = [tuple()]
-%% @end
-%%--------------------------------------------------------------------
-m1() ->
- [].
+ [macros_defined, macros_undefined].
%%--------------------------------------------------------------------
%% @spec TestCase(Config0) ->
@@ -149,19 +141,29 @@ m1() ->
%% Comment = term()
%% @end
%%--------------------------------------------------------------------
-m1(Config) ->
- {Src,Dst} = convert_module("m1",Config),
+macros_defined(Config) ->
+ %% let erl2html2 use epp as parser
+ DataDir = ?config(data_dir,Config),
+ InclDir = filename:join(DataDir, "include"),
+ {Src,Dst} = convert_module("m1",[InclDir],Config),
{true,L} = check_line_numbers(Src,Dst),
- ok = check_link_targets(Src,Dst,L,[{baz,0}]),
+ ok = check_link_targets(Src,Dst,L,[{baz,0}],[]),
ok.
-convert_module(Mod,Config) ->
+macros_undefined(Config) ->
+ %% let erl2html2 use epp_dodger as parser
+ {Src,Dst} = convert_module("m1",[],Config),
+ {true,L} = check_line_numbers(Src,Dst),
+ ok = check_link_targets(Src,Dst,L,[{baz,0}],[{quux,0}]),
+ ok.
+
+convert_module(Mod,InclDirs,Config) ->
DataDir = ?config(data_dir,Config),
PrivDir = ?config(priv_dir,Config),
Src = filename:join(DataDir,Mod++".erl"),
Dst = filename:join(PrivDir,Mod++".erl.html"),
io:format("<a href=\"~s\">~s</a>\n",[Src,filename:basename(Src)]),
- ok = erl2html2:convert(Src, Dst, [], "<html><body>"),
+ ok = erl2html2:convert(Src, Dst, InclDirs, "<html><body>"),
io:format("<a href=\"~s\">~s</a>\n",[Dst,filename:basename(Dst)]),
{Src,Dst}.
@@ -229,36 +231,46 @@ check_line_number(Last,Line,OrigLine) ->
%% function.
%% The test module has -compile(export_all), so all functions are
%% found by listing the exported ones.
-check_link_targets(Src,Dst,L,RmFncs) ->
+check_link_targets(Src,Dst,L,RmFncs,ShouldRemain) ->
Mod = list_to_atom(filename:basename(filename:rootname(Src))),
Exports = Mod:module_info(exports)--[{module_info,0},{module_info,1}|RmFncs],
- {ok,{[],L},_} = xmerl_sax_parser:file(Dst,
- [{event_fun,fun sax_event/3},
- {event_state,{Exports,0}}]),
+ LastExprFuncs = [Func || {Func,_A} <- Exports],
+ {ok,{FAs,Fs,L},_} =
+ xmerl_sax_parser:file(Dst,
+ [{event_fun,fun sax_event/3},
+ {event_state,{Exports,LastExprFuncs,0}}]),
+ true = (length(FAs) == length(ShouldRemain)),
+ [] = [FA || FA <- FAs, not lists:member(FA,ShouldRemain)],
+ [] = [F || F <- Fs, not lists:keymember(F,1,ShouldRemain)],
ok.
sax_event(Event,_Loc,State) ->
sax_event(Event,State).
-sax_event({startElement,_Uri,"a",_QN,Attrs},{Exports,PrevLine}) ->
+sax_event({startElement,_Uri,"a",_QN,Attrs},{Exports,LastExprFuncs,PrevLine}) ->
{_,_,"name",Name} = lists:keyfind("name",3,Attrs),
case catch list_to_integer(Name) of
Line when is_integer(Line) ->
case PrevLine + 1 of
Line ->
-% erlang:display({found_line,Line}),
- {Exports,Line};
+ {Exports,LastExprFuncs,Line};
Other ->
ct:fail({unexpected_line_number_target,Other})
end;
{'EXIT',_} ->
- {match,[FStr,AStr]} =
- re:run(Name,"^(.*)-([0-9]+)$",[{capture,all_but_first,list}]),
+ {match,[FStr,EndStr]} =
+ re:run(Name,"^(.*)-(last_expr|[0-9]+)$",
+ [{capture,all_but_first,list}]),
F = list_to_atom(http_uri:decode(FStr)),
- A = list_to_integer(AStr),
-% erlang:display({found_fnc,F,A}),
- A = proplists:get_value(F,Exports),
- {lists:delete({F,A},Exports),PrevLine}
+ case EndStr of
+ "last_expr" ->
+ true = lists:member(F,LastExprFuncs),
+ {Exports,lists:delete(F,LastExprFuncs),PrevLine};
+ _ ->
+ A = list_to_integer(EndStr),
+ A = proplists:get_value(F,Exports),
+ {lists:delete({F,A},Exports),LastExprFuncs,PrevLine}
+ end
end;
sax_event(_,State) ->
State.
diff --git a/lib/test_server/test/erl2html2_SUITE_data/include/header3.hrl b/lib/test_server/test/erl2html2_SUITE_data/include/header3.hrl
new file mode 100644
index 0000000000..2a20850a3a
--- /dev/null
+++ b/lib/test_server/test/erl2html2_SUITE_data/include/header3.hrl
@@ -0,0 +1 @@
+-define(EPP_SWITCH, on).
diff --git a/lib/test_server/test/erl2html2_SUITE_data/m1.erl b/lib/test_server/test/erl2html2_SUITE_data/m1.erl
index 156f1d0a51..1d405963a5 100644
--- a/lib/test_server/test/erl2html2_SUITE_data/m1.erl
+++ b/lib/test_server/test/erl2html2_SUITE_data/m1.erl
@@ -7,9 +7,15 @@
-include("header1.hrl").
-include("header2.hrl").
+-include("header3.hrl").
-define(MACRO1,value).
+%% This macro is used to select parser in erl2html2.
+%% If EPP_SWITCH is defined epp is used, else epp_dodger.
+epp_switch() ->
+ ?EPP_SWITCH.
+
%%% Comment
foo(x) ->
%% Comment
diff --git a/lib/test_server/vsn.mk b/lib/test_server/vsn.mk
index 2a2ed2b3b0..fd9e4e6d74 100644
--- a/lib/test_server/vsn.mk
+++ b/lib/test_server/vsn.mk
@@ -1 +1 @@
-TEST_SERVER_VSN = 3.8.1
+TEST_SERVER_VSN = 3.9