%%%-------------------------------------------------------------------
%%% @author Siri Hansen <[email protected]>
%%% @copyright (C) 2012, Siri Hansen
%%% @doc
%%%
%%% @end
%%% Created : 15 Nov 2012 by Siri Hansen <[email protected]>
%%%-------------------------------------------------------------------
-module(erl2html2_SUITE).
-compile(export_all).
-include_lib("common_test/include/ct.hrl").
-define(HEADER,
["<!DOCTYPE HTML PUBLIC",
"\"-//W3C//DTD HTML 3.2 Final//EN\">\n",
"<!-- autogenerated by 'erl2html2' -->\n",
"<html>\n",
"<head><title>Module ", Src, "</title>\n",
"<meta http-equiv=\"cache-control\" ",
"content=\"no-cache\">\n",
"</head>\n",
"<body bgcolor=\"white\" text=\"black\" ",
"link=\"blue\" vlink=\"purple\" alink=\"red\">\n"]).
%%--------------------------------------------------------------------
%% @spec suite() -> Info
%% Info = [tuple()]
%% @end
%%--------------------------------------------------------------------
suite() ->
[{timetrap,{seconds,30}},
{ct_hooks,[ts_install_cth,test_server_test_lib]}].
%%--------------------------------------------------------------------
%% @spec init_per_suite(Config0) ->
%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
%% Config0 = Config1 = [tuple()]
%% Reason = term()
%% @end
%%--------------------------------------------------------------------
init_per_suite(Config) ->
Config.
%%--------------------------------------------------------------------
%% @spec end_per_suite(Config0) -> void() | {save_config,Config1}
%% Config0 = Config1 = [tuple()]
%% @end
%%--------------------------------------------------------------------
end_per_suite(_Config) ->
ok.
%%--------------------------------------------------------------------
%% @spec init_per_group(GroupName, Config0) ->
%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
%% GroupName = atom()
%% Config0 = Config1 = [tuple()]
%% Reason = term()
%% @end
%%--------------------------------------------------------------------
init_per_group(_GroupName, Config) ->
Config.
%%--------------------------------------------------------------------
%% @spec end_per_group(GroupName, Config0) ->
%% void() | {save_config,Config1}
%% GroupName = atom()
%% Config0 = Config1 = [tuple()]
%% @end
%%--------------------------------------------------------------------
end_per_group(_GroupName, _Config) ->
ok.
%%--------------------------------------------------------------------
%% @spec init_per_testcase(TestCase, Config0) ->
%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
%% TestCase = atom()
%% Config0 = Config1 = [tuple()]
%% Reason = term()
%% @end
%%--------------------------------------------------------------------
init_per_testcase(_TestCase, Config) ->
Config.
%%--------------------------------------------------------------------
%% @spec end_per_testcase(TestCase, Config0) ->
%% void() | {save_config,Config1} | {fail,Reason}
%% TestCase = atom()
%% Config0 = Config1 = [tuple()]
%% Reason = term()
%% @end
%%--------------------------------------------------------------------
end_per_testcase(_TestCase, _Config) ->
ok.
%%--------------------------------------------------------------------
%% @spec groups() -> [Group]
%% Group = {GroupName,Properties,GroupsAndTestCases}
%% GroupName = atom()
%% Properties = [parallel | sequence | Shuffle | {RepeatType,N}]
%% GroupsAndTestCases = [Group | {group,GroupName} | TestCase]
%% TestCase = atom()
%% Shuffle = shuffle | {shuffle,{integer(),integer(),integer()}}
%% RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail |
%% repeat_until_any_ok | repeat_until_any_fail
%% N = integer() | forever
%% @end
%%--------------------------------------------------------------------
groups() ->
[].
%%--------------------------------------------------------------------
%% @spec all() -> GroupsAndTestCases | {skip,Reason}
%% GroupsAndTestCases = [{group,GroupName} | TestCase]
%% GroupName = atom()
%% TestCase = atom()
%% Reason = term()
%% @end
%%--------------------------------------------------------------------
all() ->
[m1].
%%--------------------------------------------------------------------
%% @spec TestCase() -> Info
%% Info = [tuple()]
%% @end
%%--------------------------------------------------------------------
m1() ->
[].
%%--------------------------------------------------------------------
%% @spec TestCase(Config0) ->
%% ok | exit() | {skip,Reason} | {comment,Comment} |
%% {save_config,Config1} | {skip_and_save,Reason,Config1}
%% Config0 = Config1 = [tuple()]
%% Reason = term()
%% Comment = term()
%% @end
%%--------------------------------------------------------------------
m1(Config) ->
{Src,Dst} = convert_module("m1",Config),
{true,L} = check_line_numbers(Src,Dst),
ok = check_link_targets(Src,Dst,L,[{baz,0}]),
ok.
convert_module(Mod,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>"),
io:format("<a href=\"~s\">~s</a>\n",[Dst,filename:basename(Dst)]),
{Src,Dst}.
%% Check that there are the same number of lines in each file, and
%% that all line numbers are displayed in the dst file.
check_line_numbers(Src,Dst) ->
{ok,SFd} = file:open(Src,[read]),
{ok,DFd} = file:open(Dst,[read]),
{ok,SN} = count_src_lines(SFd,1),
ok = file:close(SFd),
{ok,DN} = read_dst_line_numbers(DFd),
ok = file:close(DFd),
{SN == DN,SN}.
count_src_lines(Fd,N) ->
case io:get_line(Fd,"") of
eof ->
{ok,N};
{error,Reason} ->
{error,Reason,N};
_Line ->
count_src_lines(Fd,N+1)
end.
read_dst_line_numbers(Fd) ->
"<html><body><pre>\n" = io:get_line(Fd,""),
read_dst_line_numbers(Fd,0).
read_dst_line_numbers(Fd,Last) when is_integer(Last) ->
case io:get_line(Fd,"") of
eof ->
{ok,Last};
{error,Reason} ->
{error,Reason,Last};
"</pre>"++_ ->
{ok,Last};
"</body>"++_ ->
{ok,Last};
Line ->
%% erlang:display(Line),
Num = check_line_number(Last,Line,Line),
read_dst_line_numbers(Fd,Num)
end.
check_line_number(Last,Line,OrigLine) ->
case Line of
"<a name="++_ ->
[$>|Rest] = lists:dropwhile(fun($>) -> false; (_) -> true end,Line),
check_line_number(Last,Rest,OrigLine);
_ ->
[N |_] = string:tokens(Line,":"),
% erlang:display(N),
Num =
try list_to_integer(string:strip(N))
catch _:_ -> ct:fail({no_line_number_after,Last,OrigLine})
end,
if Num == Last+1 ->
Num;
true ->
ct:fail({unexpected_integer,Num,Last})
end
end.
%% Check that there is one link target for each line and one for each
%% 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) ->
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}}]),
ok.
sax_event(Event,_Loc,State) ->
sax_event(Event,State).
sax_event({startElement,_Uri,"a",_QN,Attrs},{Exports,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};
Other ->
ct:fail({unexpected_line_number_target,Other})
end;
{'EXIT',_} ->
{match,[FStr,AStr]} =
re:run(Name,"^(.*)-([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}
end;
sax_event(_,State) ->
State.