aboutsummaryrefslogtreecommitdiffstats
path: root/lib/stdlib/test/epp_SUITE.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/stdlib/test/epp_SUITE.erl')
-rw-r--r--lib/stdlib/test/epp_SUITE.erl1148
1 files changed, 1148 insertions, 0 deletions
diff --git a/lib/stdlib/test/epp_SUITE.erl b/lib/stdlib/test/epp_SUITE.erl
new file mode 100644
index 0000000000..67e20fd2e1
--- /dev/null
+++ b/lib/stdlib/test/epp_SUITE.erl
@@ -0,0 +1,1148 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1998-2009. 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(epp_SUITE).
+-export([all/1]).
+
+-export([rec_1/1, predef_mac/1,
+ upcase_mac/1, upcase_mac_1/1, upcase_mac_2/1,
+ variable/1, variable_1/1, otp_4870/1, otp_4871/1, otp_5362/1,
+ pmod/1, not_circular/1, skip_header/1, otp_6277/1, otp_7702/1,
+ otp_8130/1]).
+
+-export([epp_parse_erl_form/2]).
+
+%%
+%% Define to run outside of test server
+%%
+%-define(STANDALONE,1).
+
+-ifdef(STANDALONE).
+-compile(export_all).
+-define(line, put(line, ?LINE), ).
+-define(config(A,B),config(A,B)).
+%% -define(t, test_server).
+-define(t, io).
+config(priv_dir, _) ->
+ filename:absname("./epp_SUITE_priv");
+config(data_dir, _) ->
+ filename:absname("./epp_SUITE_data").
+-else.
+-include("test_server.hrl").
+-export([init_per_testcase/2, fin_per_testcase/2]).
+
+% Default timetrap timeout (set in init_per_testcase).
+-define(default_timeout, ?t:minutes(1)).
+
+init_per_testcase(_, Config) ->
+ ?line Dog = ?t:timetrap(?default_timeout),
+ [{watchdog, Dog} | Config].
+fin_per_testcase(_, Config) ->
+ Dog = ?config(watchdog, Config),
+ test_server:timetrap_cancel(Dog),
+ ok.
+-endif.
+
+all(doc) ->
+ ["Test cases for epp."];
+all(suite) ->
+ [rec_1, upcase_mac, predef_mac, variable, otp_4870, otp_4871, otp_5362,
+ pmod, not_circular, skip_header, otp_6277, otp_7702, otp_8130].
+
+rec_1(doc) ->
+ ["Recursive macros hang or crash epp (OTP-1398)."];
+rec_1(suite) ->
+ [];
+rec_1(Config) when is_list(Config) ->
+ ?line File = filename:join(?config(data_dir, Config), "mac.erl"),
+ ?line {ok, List} = epp_parse_file(File, [], []),
+ %% we should encounter errors
+ ?line {value, _} = lists:keysearch(error, 1, List),
+ ?line check_errors(List),
+ ok.
+
+%%% Here is a little reimplementation of epp:parse_file, which times out
+%%% after 4 seconds if the epp server doesn't respond. If we use the
+%%% regular epp:parse_file, the test case will time out, and then epp
+%%% server will go on growing until we dump core.
+epp_parse_file(File, Inc, Predef) ->
+ {ok, Epp} = epp:open(File, Inc, Predef),
+ List = collect_epp_forms(Epp),
+ epp:close(Epp),
+ {ok, List}.
+
+collect_epp_forms(Epp) ->
+ Result = epp_parse_erl_form(Epp),
+ case Result of
+ {error, _Error} ->
+ [Result | collect_epp_forms(Epp)];
+ {ok, Form} ->
+ [Form | collect_epp_forms(Epp)];
+ {eof, _} ->
+ [Result]
+ end.
+
+epp_parse_erl_form(Epp) ->
+ P = spawn(?MODULE, epp_parse_erl_form, [Epp, self()]),
+ receive
+ {P, Result} ->
+ Result
+ after 4000 ->
+ exit(Epp, kill),
+ exit(P, kill),
+ timeout
+ end.
+
+epp_parse_erl_form(Epp, Parent) ->
+ Parent ! {self(), epp:parse_erl_form(Epp)}.
+
+check_errors([]) ->
+ ok;
+check_errors([{error, Info} | Rest]) ->
+ ?line {Line, Mod, Desc} = Info,
+ ?line case Line of
+ I when is_integer(I) -> ok;
+ {L,C} when is_integer(L), is_integer(C), C >= 1 -> ok
+ end,
+ ?line Str = lists:flatten(Mod:format_error(Desc)),
+ ?line [Str] = io_lib:format("~s", [Str]),
+ check_errors(Rest);
+check_errors([_ | Rest]) ->
+ check_errors(Rest).
+
+upcase_mac(doc) ->
+ ["Check that uppercase macro names are implicitly quoted (OTP-2608)"];
+upcase_mac(suite) ->
+ [upcase_mac_1, upcase_mac_2].
+
+upcase_mac_1(doc) ->
+ [];
+upcase_mac_1(suite) ->
+ [];
+upcase_mac_1(Config) when is_list(Config) ->
+ ?line File = filename:join(?config(data_dir, Config), "mac2.erl"),
+ ?line {ok, List} = epp:parse_file(File, [], []),
+ ?line [_, {attribute, _, plupp, Tuple} | _] = List,
+ ?line Tuple = {1, 1, 3, 3},
+ ok.
+
+upcase_mac_2(doc) ->
+ [];
+upcase_mac_2(suite) ->
+ [];
+upcase_mac_2(Config) when is_list(Config) ->
+ ?line File = filename:join(?config(data_dir, Config), "mac2.erl"),
+ ?line {ok, List} = epp:parse_file(File, [], [{p, 5}, {'P', 6}]),
+ ?line [_, {attribute, _, plupp, Tuple} | _] = List,
+ ?line Tuple = {5, 5, 6, 6},
+ ok.
+
+predef_mac(doc) ->
+ [];
+predef_mac(suite) ->
+ [];
+predef_mac(Config) when is_list(Config) ->
+ ?line File = filename:join(?config(data_dir, Config), "mac3.erl"),
+ ?line {ok, List} = epp:parse_file(File, [], []),
+ ?line [_,
+ {attribute, LineCol1, l, Line1},
+ {attribute, _, f, File},
+ {attribute, _, machine1, _},
+ {attribute, _, module, mac3},
+ {attribute, _, m, mac3},
+ {attribute, _, ms, "mac3"},
+ {attribute, _, machine2, _}
+ | _] = List,
+ ?line case LineCol1 of
+ Line1 -> ok;
+ {Line1,_} -> ok
+ end,
+ ok.
+
+variable(doc) ->
+ ["Check variable as first file component of the include directives."];
+variable(suite) ->
+ [variable_1].
+
+variable_1(doc) ->
+ [];
+variable_1(suite) ->
+ [];
+variable_1(Config) when is_list(Config) ->
+ ?line DataDir = ?config(data_dir, Config),
+ ?line File = filename:join(DataDir, "variable_1.erl"),
+ ?line true = os:putenv("VAR", DataDir),
+ %% variable_1.erl includes variable_1_include.hrl and
+ %% variable_1_include_dir.hrl.
+ ?line {ok, List} = epp:parse_file(File, [], []),
+ ?line {value, {attribute,_,a,{value1,value2}}} =
+ lists:keysearch(a,3,List),
+ ok.
+
+otp_4870(doc) ->
+ ["undef without module declaration"];
+otp_4870(suite) ->
+ [];
+otp_4870(Config) when is_list(Config) ->
+ Ts = [{otp_4870,
+ <<"-undef(foo).
+ ">>,
+ []}],
+ ?line [] = check(Config, Ts),
+ ok.
+
+otp_4871(doc) ->
+ ["crashing erl_scan"];
+otp_4871(suite) ->
+ [];
+otp_4871(Config) when is_list(Config) ->
+ ?line Dir = ?config(priv_dir, Config),
+ ?line File = filename:join(Dir, "otp_4871.erl"),
+ ?line ok = file:write_file(File, "-module(otp_4871)."),
+ %% Testing crash in erl_scan. Unfortunately there currently is
+ %% no known way to crash erl_scan so it is emulated by killing the
+ %% file io server. This assumes lots of things about how
+ %% the processes are started and how monitors are set up,
+ %% so there are some sanity checks before killing.
+ ?line {ok,Epp} = epp:open(File, []),
+ timer:sleep(1),
+ ?line {current_function,{epp,_,_}} = process_info(Epp, current_function),
+ ?line {monitored_by,[Io]} = process_info(Epp, monitored_by),
+ ?line {current_function,{file_io_server,_,_}} =
+ process_info(Io, current_function),
+ ?line exit(Io, emulate_crash),
+ timer:sleep(1),
+ ?line {error,{_Line,epp,cannot_parse}} = otp_4871_parse_file(Epp),
+ ?line epp:close(Epp),
+ ok.
+
+otp_4871_parse_file(Epp) ->
+ case epp:parse_erl_form(Epp) of
+ {ok,_} -> otp_4871_parse_file(Epp);
+ Other -> Other
+ end.
+
+otp_5362(doc) ->
+ ["OTP-5362. The -file attribute is recognized."];
+otp_5362(suite) ->
+ [];
+otp_5362(Config) when is_list(Config) ->
+ Dir = ?config(priv_dir, Config),
+
+ Copts = [return, strong_validation,{i,Dir}],
+
+ File_Incl = filename:join(Dir, "incl_5362.erl"),
+ File_Incl2 = filename:join(Dir, "incl2_5362.erl"),
+ File_Incl3 = filename:join(Dir, "incl3_5362.erl"),
+ Incl = <<"-module(incl_5362).
+
+ -include(\"incl2_5362.erl\").
+
+ -include_lib(\"incl3_5362.erl\").
+
+ hi(There) -> % line 7
+ a.
+ ">>,
+ Incl2 = <<"-file(\"some.file\", 100).
+
+ foo(Bar) -> % line 102
+ foo.
+ ">>,
+ Incl3 = <<"glurk(Foo) -> % line 1
+ bar.
+ ">>,
+ ?line ok = file:write_file(File_Incl, Incl),
+ ?line ok = file:write_file(File_Incl2, Incl2),
+ ?line ok = file:write_file(File_Incl3, Incl3),
+
+ ?line {ok, incl_5362, InclWarnings} = compile:file(File_Incl, Copts),
+ ?line true = message_compare(
+ [{File_Incl3,[{{1,1},erl_lint,{unused_function,{glurk,1}}},
+ {{1,7},erl_lint,{unused_var,'Foo'}}]},
+ {File_Incl,[{{7,15},erl_lint,{unused_function,{hi,1}}},
+ {{7,18},erl_lint,{unused_var,'There'}}]},
+ {"some.file",[{{102,16},erl_lint,{unused_function,{foo,1}}},
+ {{102,20},erl_lint,{unused_var,'Bar'}}]}],
+ lists:usort(InclWarnings)),
+
+ file:delete(File_Incl),
+ file:delete(File_Incl2),
+ file:delete(File_Incl3),
+
+ %% A -file attribute referring back to the including file.
+ File_Back = filename:join(Dir, "back_5362.erl"),
+ File_Back_hrl = filename:join(Dir, "back_5362.hrl"),
+ Back = <<"-module(back_5362).
+
+ -compile(export_all).
+
+ -file(?FILE, 1).
+ -include(\"back_5362.hrl\").
+
+ foo(V) -> % line 4
+ bar.
+ ">>,
+ Back_hrl = [<<"
+ -file(\"">>,File_Back,<<"\", 2).
+ ">>],
+
+ ?line ok = file:write_file(File_Back, Back),
+ ?line ok = file:write_file(File_Back_hrl, list_to_binary(Back_hrl)),
+
+ ?line {ok, back_5362, BackWarnings} = compile:file(File_Back, Copts),
+ ?line true = message_compare(
+ [{File_Back,[{{4,19},erl_lint,{unused_var,'V'}}]}],
+ BackWarnings),
+ file:delete(File_Back),
+ file:delete(File_Back_hrl),
+
+ %% Set filename but keep line.
+ File_Change = filename:join(Dir, "change_5362.erl"),
+ Change = [<<"-module(change_5362).
+
+ -file(?FILE, 100).
+
+ -compile(export_all).
+
+ -file(\"other.file\", ?LINE). % like an included file...
+ foo(A) -> % line 105
+ bar.
+
+ -file(\"">>,File_Change,<<"\", 1000).
+
+ bar(B) -> % line 1002
+ foo.
+ ">>],
+
+ ?line ok = file:write_file(File_Change, list_to_binary(Change)),
+
+ ?line {ok, change_5362, ChangeWarnings} =
+ compile:file(File_Change, Copts),
+ ?line true = message_compare(
+ [{File_Change,[{{1002,21},erl_lint,{unused_var,'B'}}]},
+ {"other.file",[{{105,21},erl_lint,{unused_var,'A'}}]}],
+ lists:usort(ChangeWarnings)),
+
+ file:delete(File_Change),
+
+ %% -file attribute ending with a blank (not a newline).
+ File_Blank = filename:join(Dir, "blank_5362.erl"),
+
+ Blank = <<"-module(blank_5362).
+
+ -compile(export_all).
+
+ -
+ file(?FILE, 18). q(Q) -> foo. % line 18
+
+ a(A) -> % line 20
+ 1.
+
+ -file(?FILE, 42).
+
+ b(B) -> % line 44
+ 2.
+
+ -file(?FILE, ?LINE). c(C) -> % line 47
+ 3.
+ ">>,
+ ?line ok = file:write_file(File_Blank, Blank),
+ ?line {ok, blank_5362, BlankWarnings} = compile:file(File_Blank, Copts),
+ ?line true = message_compare(
+ [{File_Blank,[{{18,3},erl_lint,{unused_var,'Q'}},
+ {{20,18},erl_lint,{unused_var,'A'}},
+ {{44,18},erl_lint,{unused_var,'B'}},
+ {{47,3},erl_lint,{unused_var,'C'}}]}],
+ lists:usort(BlankWarnings)),
+ file:delete(File_Blank),
+
+ %% __FILE__ is set by inclusion and by -file attribute
+ FILE_incl = filename:join(Dir, "file_5362.erl"),
+ FILE_incl1 = filename:join(Dir, "file_incl_5362.erl"),
+ FILE = <<"-module(file_5362).
+
+ -export([ff/0, ii/0]).
+
+ -include(\"file_incl_5362.erl\").
+
+ -file(\"other_file\", 100).
+
+ ff() ->
+ ?FILE.">>,
+ FILE1 = <<"ii() -> ?FILE.
+ ">>,
+ FILE_Mod = file_5362,
+ ?line ok = file:write_file(FILE_incl, FILE),
+ ?line ok = file:write_file(FILE_incl1, FILE1),
+ FILE_Copts = [return, {i,Dir},{outdir,Dir}],
+ ?line {ok, file_5362, []} = compile:file(FILE_incl, FILE_Copts),
+ AbsFile = filename:rootname(FILE_incl, ".erl"),
+ ?line {module, FILE_Mod} = code:load_abs(AbsFile, FILE_Mod),
+ ?line II = FILE_Mod:ii(),
+ ?line "file_incl_5362.erl" = filename:basename(II),
+ ?line FF = FILE_Mod:ff(),
+ ?line "other_file" = filename:basename(FF),
+ code:purge(file_5362),
+
+ file:delete(FILE_incl),
+ file:delete(FILE_incl1),
+
+ ok.
+
+pmod(Config) when is_list(Config) ->
+ ?line DataDir = ?config(data_dir, Config),
+ ?line Pmod = filename:join(DataDir, "pmod.erl"),
+ ?line case epp:parse_file([Pmod], [], []) of
+ {ok,Forms} ->
+ %% ?line io:format("~p\n", [Forms]),
+ ?line [] = [F || {error,_}=F <- Forms],
+ ok
+ end,
+ ok.
+
+not_circular(Config) when is_list(Config) ->
+ %% Used to generate a compilation error, wrongly saying that it
+ %% was a circular definition.
+
+ Ts = [{circular_1,
+ <<"-define(S(S), ??S).\n"
+ "t() -> \"string\" = ?S(string), ok.\n">>,
+ ok}],
+ ?line [] = run(Config, Ts),
+ ok.
+
+skip_header(doc) ->
+ ["Skip some bytes in the beginning of the file."];
+skip_header(suite) ->
+ [];
+skip_header(Config) when is_list(Config) ->
+ ?line PrivDir = ?config(priv_dir, Config),
+ ?line File = filename:join([PrivDir, "epp_test_skip_header.erl"]),
+ ?line ok = file:write_file(File,
+ <<"some bytes
+ in the beginning of the file
+ that should be skipped
+ -module(epp_test_skip_header).
+ -export([main/1]).
+
+ main(_) -> ?MODULE.
+
+ ">>),
+ ?line {ok, Fd} = file:open(File, [read]),
+ ?line io:get_line(Fd, ''),
+ ?line io:get_line(Fd, ''),
+ ?line io:get_line(Fd, ''),
+ ?line {ok, Epp} = epp:open(list_to_atom(File), Fd, 4, [], []),
+
+ ?line Forms = epp:parse_file(Epp),
+ ?line [] = [Reason || {error, Reason} <- Forms],
+ ?line ok = epp:close(Epp),
+ ?line ok = file:close(Fd),
+
+ ok.
+
+otp_6277(doc) ->
+ ["?MODULE before module declaration."];
+otp_6277(suite) ->
+ [];
+otp_6277(Config) when is_list(Config) ->
+ Ts = [{otp_6277,
+ <<"-undef(ASSERT).
+ -define(ASSERT, ?MODULE).
+
+ ?ASSERT().">>,
+ [{error,{{4,16},epp,{undefined,'MODULE'}}}]}],
+ ?line [] = check(Config, Ts),
+ ok.
+
+otp_7702(doc) ->
+ ["OTP-7702. Wrong line number in stringifying macro expansion."];
+otp_7702(suite) ->
+ [];
+otp_7702(Config) when is_list(Config) ->
+ Dir = ?config(priv_dir, Config),
+ File = filename:join(Dir, "file_7702.erl"),
+ Contents = <<"-module(file_7702).
+
+ -export([t/0]).
+
+ -define(RECEIVE(Msg,Body),
+ receive
+ Msg -> Body;
+ M ->
+ exit({unexpected_message,M,on_line,?LINE,was_expecting,??Msg})
+ after 10000 ->
+ exit({timeout,on_line,?LINE,was_expecting,??Msg})
+ end).
+ t() ->
+ ?RECEIVE(foo, bar).">>,
+ ?line ok = file:write_file(File, Contents),
+ ?line {ok, file_7702, []} =
+ compile:file(File, [debug_info,return,{outdir,Dir}]),
+
+ BeamFile = filename:join(Dir, "file_7702.beam"),
+ {ok, AC} = beam_lib:chunks(BeamFile, [abstract_code]),
+
+ {file_7702,[{abstract_code,{_,Forms}}]} = AC,
+ Fun = fun(Attrs) ->
+ {line, L} = erl_parse:get_attribute(Attrs, line),
+ L
+ end,
+ Forms2 = [erl_lint:modify_line(Form, Fun) || Form <- Forms],
+ ?line
+ [{attribute,1,file,_},
+ _,
+ _,
+ {function,_,t,0,
+ [{clause,_,[],[],
+ [{'receive',14,
+ [_,
+ {clause,14,
+ [{var,14,'M'}],
+ [],
+ [{_,_,_,
+ [{tuple,14,
+ [{atom,14,unexpected_message},
+ {var,14,'M'},
+ {atom,14,on_line},
+ {integer,14,14},
+ {atom,14,was_expecting},
+ {string,14,"foo"}]}]}]}],
+ {integer,14,10000},
+ [{call,14,
+ {atom,14,exit},
+ [{tuple,14,
+ [{atom,14,timeout},
+ {atom,14,on_line},
+ {integer,14,14},
+ {atom,14,was_expecting},
+ {string,14,"foo"}]}]}]}]}]},
+ {eof,14}] = Forms2,
+
+ file:delete(File),
+ file:delete(BeamFile),
+
+ ok.
+
+otp_8130(doc) ->
+ ["OTP-8130. Misc tests."];
+otp_8130(suite) ->
+ [];
+otp_8130(Config) when is_list(Config) ->
+ true = os:putenv("epp_inc1", "stdlib"),
+ Ts = [{otp_8130_1,
+ %% The scanner handles UNICODE in a special way. Hopefully
+ %% temporarily.
+ <<"-define(M(A), ??A). "
+ "t() -> "
+ " \"{ 34 , [ $1 , 2730 ] , \\\"34\\\" , X . a , 2730 }\" = "
+ " ?M({34,\"1\\x{aaa}\",\"34\",X.a,$\\x{aaa}}), ok. ">>,
+ ok},
+
+ {otp_8130_2,
+ <<"-define(M(A), ??B). "
+ "t() -> B = 18, 18 = ?M(34), ok. ">>,
+ ok},
+
+ {otp_8130_2a,
+ <<"-define(m(A), ??B). "
+ "t() -> B = 18, 18 = ?m(34), ok. ">>,
+ ok},
+
+ {otp_8130_3,
+ <<"-define(M1(A, B), {A,B}).\n"
+ "t0() -> 1.\n"
+ "t() ->\n"
+ " {2,7} =\n"
+ " ?M1(begin 1 = fun() -> 1 end(),\n" % Bug -R13B01
+ " 2 end,\n"
+ " 7),\n"
+ " {2,7} =\n"
+ " ?M1(begin 1 = fun t0/0(),\n"
+ " 2 end,\n"
+ " 7),\n"
+ " {2,7} =\n"
+ " ?M1(begin 2 = byte_size(<<\"34\">>),\n"
+ " 2 end,\n"
+ " 7),\n"
+ " R2 = math:sqrt(2.0),\n"
+ " {2,7} =\n"
+ " ?M1(begin yes = if R2 > 1 -> yes end,\n"
+ " 2 end,\n"
+ " 7),\n"
+ " {2,7} =\n"
+ " ?M1(begin yes = case R2 > 1 of true -> yes end,\n"
+ " 2 end,\n"
+ " 7),\n"
+ " {2,7} =\n"
+ " ?M1(begin yes = receive 1 -> 2 after 0 -> yes end,\n"
+ " 2 end,\n"
+ " 7),\n"
+ " {2,7} =\n"
+ " ?M1(begin yes = try 1 of 1 -> yes after foo end,\n"
+ " 2 end,\n"
+ " 7),\n"
+ "ok.\n">>,
+ ok},
+
+ {otp_8130_4,
+ <<"-define(M3(), A).\n"
+ "t() -> A = 1, ?M3(), ok.\n">>,
+ ok},
+
+ {otp_8130_5,
+ <<"-include_lib(\"$epp_inc1/include/qlc.hrl\").\n"
+ "t() -> [1] = qlc:e(qlc:q([X || X <- [1]])), ok.\n">>,
+ ok},
+
+ {otp_8130_6,
+ <<"-include_lib(\"kernel/include/file.hrl\").\n"
+ "t() -> 14 = (#file_info{size = 14})#file_info.size, ok.\n">>,
+ ok},
+
+ {otp_8130_7,
+ <<"-record(b, {b}).\n"
+ "-define(A, {{a,#b.b.\n"
+ "t() -> {{a,2}} = ?A}}, ok.">>,
+ ok},
+
+ {otp_8130_8,
+ <<"\n-define(A(B), B).\n"
+ "-undef(A).\n"
+ "-define(A, ok).\n"
+ "t() -> ?A.\n">>,
+ ok},
+ {otp_8130_9,
+ <<"-define(a, 1).\n"
+ "-define(b, {?a,?a}).\n"
+ "t() -> ?b.\n">>,
+ {1,1}}
+
+ ],
+ ?line [] = run(Config, Ts),
+
+ Cs = [{otp_8130_c1,
+ <<"-define(M1(A), if\n"
+ "A =:= 1 -> B;\n"
+ "true -> 2\n"
+ "end).\n"
+ "t() -> {?M1(1), ?M1(2)}. \n">>,
+ {errors,[{{5,13},erl_lint,{unbound_var,'B'}},
+ {{5,21},erl_lint,{unbound_var,'B'}}],
+ []}},
+
+ {otp_8130_c2,
+ <<"-define(M(A), A).\n"
+ "t() -> ?M(1\n">>,
+ {errors,[{{2,9},epp,{arg_error,'M'}}],[]}},
+
+ {otp_8130_c3,
+ <<"-define(M(A), A).\n"
+ "t() -> ?M.\n">>,
+ {errors,[{{2,9},epp,{mismatch,'M'}}],[]}},
+
+ {otp_8130_c4,
+ <<"-define(M(A), A).\n"
+ "t() -> ?M(1, 2).\n">>,
+ {errors,[{{2,9},epp,{mismatch,'M'}}],[]}},
+
+ {otp_8130_c5,
+ <<"-define(M(A), A).\n"
+ "t() -> ?M().\n">>,
+ {errors,[{{2,9},epp,{mismatch,'M'}}],[]}},
+
+ {otp_8130_c6,
+ <<"-define(M3(), A).\n"
+ "t() -> A = 1, ?3.14159}.\n">>,
+ {errors,[{{2,16},epp,{call,"?3.14159"}}],[]}},
+
+ {otp_8130_c7,
+ <<"\nt() -> ?A.\n">>,
+ {errors,[{{2,9},epp,{undefined,'A'}}],[]}},
+
+ {otp_8130_c8,
+ <<"\n-include_lib(\"$apa/foo.hrl\").\n">>,
+ {errors,[{{2,2},epp,{include,lib,"$apa/foo.hrl"}}],[]}},
+
+
+ {otp_8130_c9,
+ <<"-define(S, ?S).\n"
+ "t() -> ?S.\n">>,
+ {errors,[{{2,9},epp,{circular,'S'}}],[]}},
+
+ {otp_8130_c10,
+ <<"\n-file.">>,
+ {errors,[{{2,2},epp,{bad,file}}],[]}},
+
+ {otp_8130_c11,
+ <<"\n-include_lib 92.">>,
+ {errors,[{{2,2},epp,{bad,include_lib}}],[]}},
+
+ {otp_8130_c12,
+ <<"\n-include_lib(\"kernel/include/fopp.hrl\").\n">>,
+ {errors,[{{2,2},epp,{include,lib,"kernel/include/fopp.hrl"}}],[]}},
+
+ {otp_8130_c13,
+ <<"\n-include(foo).\n">>,
+ {errors,[{{2,2},epp,{bad,include}}],[]}},
+
+ {otp_8130_c14,
+ <<"\n-undef({foo}).\n">>,
+ {errors,[{{2,2},epp,{bad,undef}}],[]}},
+
+ {otp_8130_c15,
+ <<"\n-define(a, 1).\n"
+ "-define(a, 1).\n">>,
+ {errors,[{{3,9},epp,{redefine,a}}],[]}},
+
+ {otp_8130_c16,
+ <<"\n-define(A, 1).\n"
+ "-define(A, 1).\n">>,
+ {errors,[{{3,9},epp,{redefine,'A'}}],[]}},
+
+ {otp_8130_c17,
+ <<"\n-define(A(B), B).\n"
+ "-define(A, 1).\n">>,
+ {errors,[{{3,9},epp,{redefine,'A'}}],[]}},
+
+ {otp_8130_c18,
+ <<"\n-define(A, 1).\n"
+ "-define(A(B), B).\n">>,
+ {errors,[{{3,9},epp,{redefine,'A'}}],[]}},
+
+ {otp_8130_c19,
+ <<"\n-define(a(B), B).\n"
+ "-define(a, 1).\n">>,
+ {errors,[{{3,9},epp,{redefine,a}}],[]}},
+
+ {otp_8130_c20,
+ <<"\n-define(a, 1).\n"
+ "-define(a(B), B).\n">>,
+ {errors,[{{3,9},epp,{redefine,a}}],[]}},
+
+ {otp_8130_c21,
+ <<"\n-define(A(B, B), B).\n">>,
+ {errors,[{{2,2},epp,{bad,define}}],[]}},
+
+ {otp_8130_c22,
+ <<"\n-define(a(B, B), B).\n">>,
+ {errors,[{{2,2},epp,{bad,define}}],[]}},
+
+ {otp_8130_c23,
+ <<"\n-file(?b, 3).\n">>,
+ {errors,[{{2,8},epp,{undefined,b}}],[]}},
+
+ {otp_8130_c24,
+ <<"\n-include(\"no such file.erl\").\n">>,
+ {errors,[{{2,2},epp,{include,file,"no such file.erl"}}],[]}}
+
+ ],
+ ?line [] = compile(Config, Cs),
+
+ Cks = [{otp_check_1,
+ <<"\n-include_lib(\"epp_test.erl\").\n">>,
+ [{error,{{2,2},epp,{depth,"include_lib"}}}]},
+
+ {otp_check_2,
+ <<"\n-include(\"epp_test.erl\").\n">>,
+ [{error,{{2,2},epp,{depth,"include"}}}]}
+ ],
+ ?line [] = check(Config, Cks),
+
+ ?line Dir = ?config(priv_dir, Config),
+ ?line File = filename:join(Dir, "otp_8130.erl"),
+ ?line ok = file:write_file(File,
+ "-module(otp_8130).\n"
+ "-define(a, 3.14).\n"
+ "t() -> ?a.\n"),
+ ?line {ok,Epp} = epp:open(File, []),
+ ?line ['BASE_MODULE','BASE_MODULE_STRING','BEAM','FILE','LINE',
+ 'MACHINE','MODULE','MODULE_STRING'] = macs(Epp),
+ ?line {ok,[{'-',_},{atom,_,file}|_]} = epp:scan_erl_form(Epp),
+ ?line {ok,[{'-',_},{atom,_,module}|_]} = epp:scan_erl_form(Epp),
+ ?line {ok,[{atom,_,t}|_]} = epp:scan_erl_form(Epp),
+ ?line {eof,_} = epp:scan_erl_form(Epp),
+ ?line ['BASE_MODULE','BASE_MODULE_STRING','BEAM','FILE','LINE',
+ 'MACHINE','MODULE','MODULE_STRING',a] = macs(Epp),
+ ?line epp:close(Epp),
+
+ %% escript
+ ModuleStr = "any_name",
+ Module = list_to_atom(ModuleStr),
+ fun() ->
+ PreDefMacros = [{'MODULE', Module, redefine},
+ {'MODULE_STRING', ModuleStr, redefine},
+ a, {b,2}],
+ ?line {ok,Epp2} = epp:open(File, [], PreDefMacros),
+ ?line [{atom,_,true}] = macro(Epp2, a),
+ ?line [{integer,_,2}] = macro(Epp2, b),
+ ?line false = macro(Epp2, c),
+ ?line epp:close(Epp2)
+ end(),
+ fun() ->
+ PreDefMacros = [{a,b,c}],
+ ?line {error,{bad,{a,b,c}}} = epp:open(File, [], PreDefMacros)
+ end(),
+ fun() ->
+ PreDefMacros = [a, {a,1}],
+ ?line {error,{redefine,a}} = epp:open(File, [], PreDefMacros)
+ end(),
+ fun() ->
+ PreDefMacros = [{a,1},a],
+ ?line {error,{redefine,a}} = epp:open(File, [], PreDefMacros)
+ end(),
+
+ ?line {error,enoent} = epp:open("no such file", []),
+ ?line {error,enoent} = epp:parse_file("no such file", [], []),
+
+ _ = ifdef(Config),
+
+ ok.
+
+macs(Epp) ->
+ Macros = epp:macro_defs(Epp), % not documented
+ lists:sort([MName || {{atom,MName},_} <- Macros]).
+
+macro(Epp, N) ->
+ case lists:keyfind({atom,N}, 1, epp:macro_defs(Epp)) of
+ false -> false;
+ {{atom,N},{_,V}} -> V
+ end.
+
+ifdef(Config) ->
+ Cs = [{ifdef_c1,
+ <<"-ifdef(a).\n"
+ "a bug.\n"
+ "-else.\n"
+ "-ifdef(A).\n"
+ "a bug.\n"
+ "-endif.\n"
+ "-else.\n"
+ "t() -> ok.\n"
+ "-endif.">>,
+ {errors,[{{7,2},epp,{illegal,"repeated",'else'}}],[]}},
+
+ {ifdef_c2,
+ <<"-define(a, true).\n"
+ "-ifdef(a).\n"
+ "a bug.\n"
+ "-endif.">>,
+ {errors,[{{3,3},erl_parse,["syntax error before: ","bug"]}],[]}},
+
+ {ifdef_c3,
+ <<"-define(a, true).\n"
+ "-ifdef(a).\n"
+ "-endif">>,
+
+ {errors,[{{3,2},epp,{bad,endif}},
+ {{3,7},epp,{illegal,"unterminated",ifdef}}],
+ []}},
+
+ {ifdef_c4,
+ <<"\n-ifdef a.\n"
+ "-endif.\n">>,
+ {errors,[{{2,2},epp,{bad,ifdef}}],[]}},
+
+ {ifdef_c5,
+ <<"-ifdef(a).\n"
+ "-else.\n"
+ "-endif.\n"
+ "-endif.\n">>,
+ {errors,[{{4,2},epp,{illegal,"unbalanced",endif}}],[]}},
+
+ {ifdef_c6,
+ <<"-ifdef(a).\n"
+ "-else.\n"
+ "-endif.\n"
+ "-else.\n">>,
+ {errors,[{{4,2},epp,{illegal,"unbalanced",'else'}}],[]}},
+
+ {ifdef_c7,
+ <<"-ifndef(a).\n"
+ "-else\n"
+ "foo bar\n"
+ "-else.\n"
+ "t() -> a.\n"
+ "-endif.\n">>,
+ {errors,[{{2,2},epp,{bad,else}}],[]}},
+
+ {ifdef_c8,
+ <<"-ifdef(a).\n"
+ "-foo bar.">>,
+ {errors,[{{2,10},epp,{illegal,"unterminated",ifdef}}],[]}},
+
+ {ifdef_c9,
+ <<"-ifdef(a).\n"
+ "3.3e12000.\n"
+ "-endif.\n">>,
+ []},
+
+ {ifdef_c10,
+ <<"\nt() -> 3.3e12000.\n">>,
+ {errors,[{{2,8},erl_scan,{illegal,float}},
+ {{2,17},erl_parse,["syntax error before: ","'.'"]}], % ...
+ []}},
+
+ {ifndef_c1,
+ <<"-ifndef(a).\n"
+ "-ifndef(A).\n"
+ "t() -> ok.\n"
+ "-endif.\n"
+ "-else.\n"
+ "a bug.\n"
+ "-else.\n"
+ "a bug.\n"
+ "-endif.">>,
+ {errors,[{{7,2},epp,{illegal,"repeated",'else'}}],[]}},
+
+ {ifndef_c3,
+ <<"-ifndef(a).\n"
+ "-endif">>,
+
+ {errors,[{{2,2},epp,{bad,endif}},
+ {{2,7},epp,{illegal,"unterminated",ifndef}}],
+ []}},
+
+ {ifndef_c4,
+ <<"\n-ifndef a.\n"
+ "-endif.\n">>,
+ {errors,[{{2,2},epp,{bad,ifndef}}],[]}},
+
+ {define_c5,
+ <<"-\ndefine a.\n">>,
+ {errors,[{{2,1},epp,{bad,define}}],[]}},
+
+ {define_c6,
+ <<"\n-if.\n"
+ "-endif.\n">>,
+ {errors,[{{2,2},epp,{'NYI','if'}}],[]}},
+
+ {define_c7,
+ <<"-ifndef(a).\n"
+ "-elif.\n"
+ "-endif.\n">>,
+ {errors,[{{2,2},epp,{'NYI',elif}}],[]}},
+
+ {define_c7,
+ <<"-ifndef(a).\n"
+ "-if.\n"
+ "-elif.\n"
+ "-endif.\n"
+ "-endif.\n"
+ "t() -> a.\n">>,
+ {errors,[{{2,2},epp,{'NYI','if'}}],[]}}
+ ],
+ ?line [] = compile(Config, Cs),
+
+ Ts = [{ifdef_1,
+ <<"-ifdef(a).\n"
+ "a bug.\n"
+ "-else.\n"
+ "-ifdef(A).\n"
+ "a bug.\n"
+ "-endif.\n"
+ "t() -> ok.\n"
+ "-endif.">>,
+ ok},
+
+ {ifdef_2,
+ <<"-define(a, true).\n"
+ "-ifdef(a).\n"
+ "-define(A, true).\n"
+ "-ifdef(A).\n"
+ "t() -> ok.\n"
+ "-else.\n"
+ "a bug.\n"
+ "-endif.\n"
+ "-else.\n"
+ "a bug.\n"
+ "-endif.">>,
+ ok},
+
+ {ifdef_3,
+ <<"\n-define(a, true).\n"
+ "-ifndef(a).\n"
+ "a bug.\n"
+ "-else.\n"
+ "-define(A, true).\n"
+ "-ifndef(A).\n"
+ "a bug.\n"
+ "-else.\n"
+ "t() -> ok.\n"
+ "-endif.\n"
+ "-endif.">>,
+ ok},
+
+ {ifdef_4,
+ <<"-ifdef(a).\n"
+ "a bug.\n"
+ "-ifdef(a).\n"
+ "a bug.\n"
+ "-else.\n"
+ "-endif.\n"
+ "-ifdef(A).\n"
+ "a bug.\n"
+ "-endif.\n"
+ "-else.\n"
+ "t() -> ok.\n"
+ "-endif.">>,
+ ok},
+
+ {ifdef_5,
+ <<"-ifdef(a).\n"
+ "-ifndef(A).\n"
+ "a bug.\n"
+ "-else.\n"
+ "-endif.\n"
+ "a bug.\n"
+ "-else.\n"
+ "t() -> ok.\n"
+ "-endif.">>,
+ ok},
+
+ {ifdef_6,
+ <<"-ifdef(a).\n"
+ "-if(A).\n"
+ "a bug.\n"
+ "-else.\n"
+ "-endif.\n"
+ "a bug.\n"
+ "-else.\n"
+ "t() -> ok.\n"
+ "-endif.">>,
+ ok}
+
+ ],
+ ?line [] = run(Config, Ts).
+
+check(Config, Tests) ->
+ eval_tests(Config, fun check_test/2, Tests).
+
+compile(Config, Tests) ->
+ eval_tests(Config, fun compile_test/2, Tests).
+
+run(Config, Tests) ->
+ eval_tests(Config, fun run_test/2, Tests).
+
+eval_tests(Config, Fun, Tests) ->
+ F = fun({N,P,E}, BadL) ->
+ %% io:format("Testing ~p~n", [P]),
+ Return = Fun(Config, P),
+ case message_compare(E, Return) of
+ true ->
+ BadL;
+ false ->
+ ?t:format("~nTest ~p failed. Expected~n ~p~n"
+ "but got~n ~p~n", [N, E, Return]),
+ fail()
+ end
+ end,
+ lists:foldl(F, [], Tests).
+
+
+check_test(Config, Test) ->
+ Filename = 'epp_test.erl',
+ ?line PrivDir = ?config(priv_dir, Config),
+ ?line File = filename:join(PrivDir, Filename),
+ ?line ok = file:write_file(File, Test),
+ ?line case epp:parse_file(File, [PrivDir], []) of
+ {ok,Forms} ->
+ [E || E={error,_} <- Forms];
+ {error,Error} ->
+ Error
+ end.
+
+compile_test(Config, Test0) ->
+ Test = [<<"-module(epp_test). -compile(export_all). ">>, Test0],
+ Filename = 'epp_test.erl',
+ ?line PrivDir = ?config(priv_dir, Config),
+ ?line File = filename:join(PrivDir, Filename),
+ ?line ok = file:write_file(File, Test),
+ Opts = [export_all,return,nowarn_unused_record,{outdir,PrivDir}],
+ case compile_file(File, Opts) of
+ {ok, Ws} -> warnings(File, Ws);
+ Else -> Else
+ end.
+
+warnings(File, Ws) ->
+ case lists:append([W || {F, W} <- Ws, F =:= File]) of
+ [] -> [];
+ L -> {warnings, L}
+ end.
+
+compile_file(File, Opts) ->
+ case compile:file(File, Opts) of
+ {ok, _M, Ws} -> {ok, Ws};
+ {error, FEs, []} -> {errors, errs(FEs, File), []};
+ {error, FEs, [{File,Ws}]} -> {error, errs(FEs, File), Ws}
+ end.
+
+errs([{File,Es}|L], File) ->
+ Es ++ errs(L, File);
+errs([_|L], File) ->
+ errs(L, File);
+errs([], _File) ->
+ [].
+
+run_test(Config, Test0) ->
+ Test = [<<"-module(epp_test). -compile(export_all). ">>, Test0],
+ Filename = "epp_test.erl",
+ ?line PrivDir = ?config(priv_dir, Config),
+ ?line File = filename:join(PrivDir, Filename),
+ ?line ok = file:write_file(File, Test),
+ Opts = [return, {i,PrivDir},{outdir,PrivDir}],
+ ?line {ok, epp_test, []} = compile:file(File, Opts),
+ AbsFile = filename:rootname(File, ".erl"),
+ ?line {module, epp_test} = code:load_abs(AbsFile, epp_test),
+ ?line Reply = epp_test:t(),
+ code:purge(epp_test),
+ Reply.
+
+fail() ->
+ io:format("failed~n"),
+ test_server:fail().
+
+message_compare(T, T) ->
+ true;
+message_compare(T1, T2) ->
+ ln(T1) =:= T2.
+
+%% Replaces locations like {Line,Column} with Line.
+ln({warnings,L}) ->
+ {warnings,ln0(L)};
+ln({errors,EL,WL}) ->
+ {errors,ln0(EL),ln0(WL)};
+ln(L) ->
+ ln0(L).
+
+ln0(L) ->
+ lists:keysort(1, ln1(L)).
+
+ln1([]) ->
+ [];
+ln1([{File,Ms}|MsL]) when is_list(File) ->
+ [{File,ln0(Ms)}|ln1(MsL)];
+ln1([M|Ms]) ->
+ [ln2(M)|ln1(Ms)].
+
+ln2({{L,_C},Mod,Mess}) ->
+ {L,Mod,Mess};
+ln2({error,M}) ->
+ {error,ln2(M)};
+ln2(M) ->
+ M.