%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1997-2010. 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%
%%
%%% Purpose : Produces reports in HTML from the outcome of test suite runs.
-module(ts_reports).
-export([make_index/0, make_master_index/2, make_progress_index/2]).
-export([count_cases/1, year/0, current_time/0]).
-include_lib("kernel/include/file.hrl").
-include("ts.hrl").
-compile({no_auto_import,[error/1]}).
-import(filename, [basename/1, rootname/1]).
-import(ts_lib, [error/1]).
%% Make master index page which points out index pages for all platforms.
make_master_index(Dir, Vars) ->
IndexName = filename:join(Dir, "index.html"),
{ok, Index0} = make_master_index1(directories(Dir), master_header(Vars)),
Index = [Index0|master_footer()],
io:put_chars("Updating " ++ IndexName ++ "... "),
ok = ts_lib:force_write_file(IndexName, Index),
io:put_chars("done\n").
make_master_index1([Dir|Rest], Result) ->
NewResult =
case catch read_variables(Dir) of
{'EXIT',{{bad_installation,Reason},_}} ->
io:put_chars("Failed to read " ++ filename:join(Dir,?variables)++
": " ++ Reason ++ " - Ignoring this directory\n"),
Result;
Vars ->
Platform = ts_lib:var(platform_label, Vars),
case make_index(Dir, Vars, false) of
{ok, Summary} ->
make_master_index(Platform, Dir, Summary, Result);
{error, _} ->
Result
end
end,
make_master_index1(Rest, NewResult);
make_master_index1([], Result) ->
{ok, Result}.
make_progress_index(Dir, Vars) ->
IndexName = filename:join(Dir, "index.html"),
io:put_chars("Updating " ++ IndexName ++ "... "),
Index0=progress_header(Vars),
ts_lib:force_delete(IndexName),
Dirs=find_progress_runs(Dir),
Index1=[Index0|make_progress_links(Dirs, [])],
IndexF=[Index1|progress_footer()],
ok = ts_lib:force_write_file(IndexName, IndexF),
io:put_chars("done\n").
find_progress_runs(Dir) ->
case file:list_dir(Dir) of
{ok, Dirs0} ->
Dirs1= [filename:join(Dir,X) || X <- Dirs0,
filelib:is_dir(filename:join(Dir,X))],
lists:sort(Dirs1);
_ ->
[]
end.
name_from_vars(Dir, Platform) ->
VarFile=filename:join([Dir, Platform, "variables"]),
case file:consult(VarFile) of
{ok, Vars} ->
ts_lib:var(platform_id, Vars);
_Other ->
Platform
end.
make_progress_links([], Acc) ->
Acc;
make_progress_links([RDir|Rest], Acc) ->
Dir=filename:basename(RDir),
Platforms=[filename:basename(X) ||
X <- find_progress_runs(RDir)],
PlatformLinks=[""++name_from_vars(RDir, X)++"
" ||
X <- Platforms],
LinkName=Dir++"/index.html",
Link =
[
"
\n",
"", Dir, " | ", "\n",
"", PlatformLinks, " | ", "\n"
],
make_progress_links(Rest, [Link|Acc]).
read_variables(Dir) ->
case file:consult(filename:join(Dir, ?variables)) of
{ok, Vars} -> Vars;
{error, Reason} ->
erlang:error({bad_installation,file:format_error(Reason)}, [Dir])
end.
make_master_index(Platform, Dirname, {Succ, Fail, UserSkip,AutoSkip}, Result) ->
Link = filename:join(filename:basename(Dirname), "index.html"),
FailStr =
if Fail > 0 ->
["",
integer_to_list(Fail),""];
true ->
integer_to_list(Fail)
end,
AutoSkipStr =
if AutoSkip > 0 ->
["",
integer_to_list(AutoSkip),""];
true -> integer_to_list(AutoSkip)
end,
[Result,
"
\n",
"", Platform, " | ", "\n",
make_row(integer_to_list(Succ), false),
make_row(FailStr, false),
make_row(integer_to_list(UserSkip), false),
make_row(AutoSkipStr, false),
"
\n"].
%% Make index page which points out individual test suites for a single platform.
make_index() ->
{ok, Pwd} = file:get_cwd(),
Vars = read_variables(Pwd),
make_index(Pwd, Vars, true).
make_index(Dir, Vars, IncludeLast) ->
IndexName = filename:absname("index.html", Dir),
io:put_chars("Updating " ++ IndexName ++ "... "),
case catch make_index1(Dir, IndexName, Vars, IncludeLast) of
{'EXIT', Reason} ->
io:put_chars("CRASHED!\n"),
io:format("~p~n", [Reason]),
{error, Reason};
{error, Reason} ->
io:put_chars("FAILED\n"),
io:format("~p~n", [Reason]),
{error, Reason};
{ok, Summary} ->
io:put_chars("done\n"),
{ok, Summary};
Err ->
io:format("Unknown internal error. Please report.\n(Err: ~p, ID: 1)",
[Err]),
{error, Err}
end.
make_index1(Dir, IndexName, Vars, IncludeLast) ->
Logs0 = ts_lib:interesting_logs(Dir),
Logs =
case IncludeLast of
true -> add_last_name(Logs0);
false -> Logs0
end,
{ok, {Index0, Summary}} = make_index(Logs, header(Vars), 0, 0, 0, 0, 0),
Index = [Index0|footer()],
case ts_lib:force_write_file(IndexName, Index) of
ok ->
{ok, Summary};
{error, Reason} ->
error({index_write_error, Reason})
end.
make_index([Name|Rest], Result, TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt) ->
case ts_lib:last_test(Name) of
false ->
%% Silently skip.
make_index(Rest, Result, TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt);
Last ->
case count_cases(Last) of
{Succ, Fail, USkip, ASkip} ->
Cov =
case file:read_file(filename:join(Last,?cover_total)) of
{ok,Bin} ->
TotCoverage = binary_to_term(Bin),
io_lib:format("~w %",[TotCoverage]);
_error ->
""
end,
Link = filename:join(basename(Name), basename(Last)),
JustTheName = rootname(basename(Name)),
NotBuilt = not_built(JustTheName),
NewResult = [Result, make_index1(JustTheName,
Link, Succ, Fail, USkip, ASkip,
NotBuilt, Cov, false)],
make_index(Rest, NewResult, TotSucc+Succ, TotFail+Fail,
UserSkip+USkip, AutoSkip+ASkip, TotNotBuilt+NotBuilt);
error ->
make_index(Rest, Result, TotSucc, TotFail, UserSkip, AutoSkip,
TotNotBuilt)
end
end;
make_index([], Result, TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt) ->
{ok, {[Result|make_index1("Total", no_link,
TotSucc, TotFail, UserSkip, AutoSkip,
TotNotBuilt, "", true)],
{TotSucc, TotFail, UserSkip, AutoSkip}}}.
make_index1(SuiteName, Link, Success, Fail, UserSkip, AutoSkip, NotBuilt, Coverage, Bold) ->
Name = test_suite_name(SuiteName),
FailStr =
if Fail > 0 ->
["",
integer_to_list(Fail),""];
true ->
integer_to_list(Fail)
end,
AutoSkipStr =
if AutoSkip > 0 ->
["",
integer_to_list(AutoSkip),""];
true -> integer_to_list(AutoSkip)
end,
["\n",
"",
case Link of
no_link ->
["", Name|""];
_Other ->
CrashDumpName = SuiteName ++ "_erl_crash.dump",
CrashDumpLink =
case filelib:is_file(CrashDumpName) of
true ->
[" (CrashDump)"];
false ->
""
end,
LogFile = filename:join(Link, ?suitelog_name ++ ".html"),
["", Name, "\n", CrashDumpLink,
" | \n"]
end,
make_row(integer_to_list(Success), Bold),
make_row(FailStr, Bold),
make_row(integer_to_list(UserSkip), Bold),
make_row(AutoSkipStr, Bold),
make_row(integer_to_list(NotBuilt), Bold),
make_row(Coverage, Bold),
"
\n"].
make_row(Row, true) ->
["", Row|" | "];
make_row(Row, false) ->
["", Row|" | "].
not_built(BaseName) ->
Dir = filename:join("..", BaseName++"_test"),
Erl = length(filelib:wildcard(filename:join(Dir,"*_SUITE.erl"))),
Beam = length(filelib:wildcard(filename:join(Dir,"*_SUITE.beam"))),
Erl-Beam.
%% Add the log file directory for the very last test run (according to
%% last_name).
add_last_name(Logs) ->
case file:read_file("last_name") of
{ok, Bin} ->
Name = filename:dirname(lib:nonl(binary_to_list(Bin))),
case lists:member(Name, Logs) of
true -> Logs;
false -> [Name|Logs]
end;
_ ->
Logs
end.
term_to_text(Term) ->
lists:flatten(io_lib:format("~p.\n", [Term])).
test_suite_name(Name) ->
ts_lib:initial_capital(Name) ++ " suite".
directories(Dir) ->
{ok, Files} = file:list_dir(Dir),
[filename:join(Dir, X) || X <- Files,
filelib:is_dir(filename:join(Dir, X))].
%%% Headers and footers.
header(Vars) ->
Platform = ts_lib:var(platform_id, Vars),
["\n"
"\n"
"\n",
"\n",
"Test Results for ", Platform, "\n",
"\n",
body_tag(),
"\n",
"\n",
"Test Results for ", Platform, "
\n",
"\n",
"\n",
"\n",
"\n",
"Family | \n",
"Successful | \n",
"Failed | \n",
"User Skipped | \n"
"Auto Skipped | \n"
"Missing Suites | \n"
"Coverage | \n"
"\n"].
footer() ->
["
\n"
"\n"
"\n"
"
\n"
"\n"
"Copyright © ", year(),
" Open Telecom Platform
\n"
"Updated: ", current_time(), "
\n"
"\n"
"
\n"
"\n"
"