[llvm-cov] Emit a summary in the report directory's index

llvm-cov writes out an index file in '-output-dir' mode, albeit not a
very informative one. Try to fix that by using the CoverageReport API to
include some basic summary information in the index file.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@281011 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Vedant Kumar 2016-09-09 01:32:55 +00:00
parent 85e096da4e
commit 84dc7510c9
8 changed files with 154 additions and 35 deletions

View File

@ -35,9 +35,9 @@ int main() { // TEXT: [[@LINE]]| 161|int main(
// RUN: FileCheck -check-prefixes=TEXT,WHOLE-FILE -input-file %t.dir/coverage/tmp/showLineExecutionCounts.cpp.txt %s
// RUN: FileCheck -check-prefixes=TEXT,FILTER -input-file %t.dir/functions.txt %s
//
// Test index creation.
// RUN: FileCheck -check-prefix=INDEX -input-file %t.dir/index.txt %s
// INDEX: showLineExecutionCounts.cpp.txt
// RUN: llvm-cov export %S/Inputs/lineExecutionCounts.covmapping -instr-profile %t.profdata -name=main 2>/dev/null > %t.export.json
// RUN: FileCheck -input-file %t.export.json %S/Inputs/lineExecutionCounts.json
// RUN: cat %t.export.json | %python -c "import json, sys; json.loads(sys.stdin.read())"
//
// Test html output.
// RUN: llvm-cov show %S/Inputs/lineExecutionCounts.covmapping -format html -o %t.html.dir -instr-profile %t.profdata -filename-equivalence %s
@ -69,7 +69,24 @@ int main() { // TEXT: [[@LINE]]| 161|int main(
// HTML: <td class='line-number'><a name='L[[@LINE-44]]'><pre>[[@LINE-44]]</pre></a></td><td class='covered-line'><pre>161</pre></td><td class='code'><pre>}
// HTML-WHOLE-FILE: <td class='line-number'><a name='L[[@LINE-44]]'><pre>[[@LINE-44]]</pre></a></td><td class='uncovered-line'></td><td class='code'><pre>// after
// HTML-FILTER-NOT: <td class='line-number'><a name='L[[@LINE-45]]'><pre>[[@LINE-45]]</pre></a></td><td class='uncovered-line'></td><td class='code'><pre>// after
// RUN: llvm-cov export %S/Inputs/lineExecutionCounts.covmapping -instr-profile %t.profdata -name=main 2>/dev/null > %t.export.json
// RUN: FileCheck -input-file %t.export.json %S/Inputs/lineExecutionCounts.json
// RUN: cat %t.export.json | %python -c "import json, sys; json.loads(sys.stdin.read())"
//
// Test index creation.
// RUN: FileCheck -check-prefix=TEXT-INDEX -input-file %t.dir/index.txt %s
// TEXT-INDEX: Filename
// TEXT-INDEX-NEXT: ---
// TEXT-INDEX-NEXT: {{.*}}showLineExecutionCounts.cpp
//
// RUN: FileCheck -check-prefix HTML-INDEX -input-file %t.html.dir/index.html %s
// HTML-INDEX-LABEL: <table>
// HTML-INDEX: <td class='column-entry'>Filename</td>
// HTML-INDEX: <td class='column-entry'>Region Coverage</td>
// HTML-INDEX: <td class='column-entry'>Function Coverage</td>
// HTML-INDEX: <td class='column-entry'>Line Coverage</td>
// HTML-INDEX: <a href='coverage{{.*}}showLineExecutionCounts.cpp.html'{{.*}}showLineExecutionCounts.cpp</a>
// HTML-INDEX: <td class='column-entry-red'>
// HTML-INDEX: 70.00% (7/10)
// HTML-INDEX: <td class='column-entry-green'>
// HTML-INDEX: 100.00% (1/1)
// HTML-INDEX: <td class='column-entry-yellow'>
// HTML-INDEX: 80.00% (16/20)
// HTML-INDEX: TOTALS

View File

@ -15,6 +15,11 @@ STYLE-DAG: .source-name-title
STYLE-DAG: .centered
STYLE-DAG: .expansion-view
STYLE-DAG: .line-number
STYLE-DAG: .light-row
STYLE-DAG: .column-entry
STYLE-DAG: .column-entry-yellow
STYLE-DAG: .column-entry-red
STYLE-DAG: .column-entry-green
STYLE-DAG: .covered-line
STYLE-DAG: .uncovered-line
STYLE-DAG: .tooltip

View File

@ -679,7 +679,7 @@ int CodeCoverageTool::show(int argc, const char **argv,
// Create an index out of the source files.
if (ViewOpts.hasOutputDirectory()) {
if (Error E = Printer->createIndexFile(SourceFiles)) {
if (Error E = Printer->createIndexFile(SourceFiles, *Coverage)) {
error("Could not create index file!", toString(std::move(E)));
return 1;
}

View File

@ -142,7 +142,8 @@ public:
virtual void closeViewFile(OwnedStream OS) = 0;
/// \brief Create an index which lists reports for the given source files.
virtual Error createIndexFile(ArrayRef<StringRef> SourceFiles) = 0;
virtual Error createIndexFile(ArrayRef<StringRef> SourceFiles,
const coverage::CoverageMapping &Coverage) = 0;
/// @}
};

View File

@ -11,11 +11,13 @@
///
//===----------------------------------------------------------------------===//
#include "CoverageReport.h"
#include "SourceCoverageViewHTML.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/Path.h"
using namespace llvm;
@ -73,7 +75,7 @@ const char *BeginHeader =
const char *CSSForCoverage =
R"(.red {
background-color: #FFD0D0;
background-color: #ffd0d0;
}
.cyan {
background-color: cyan;
@ -110,6 +112,25 @@ pre {
table {
border-collapse: collapse;
}
.light-row {
background: #ffffff;
border: 1px solid #dbdbdb;
}
.column-entry {
text-align: right;
}
.column-entry-yellow {
text-align: right;
background-color: #ffffd0;
}
.column-entry-red {
text-align: right;
background-color: #ffd0d0;
}
.column-entry-green {
text-align: right;
background-color: #d0ffd0;
}
.line-number {
text-align: right;
color: #aaa;
@ -284,16 +305,88 @@ void CoveragePrinterHTML::closeViewFile(OwnedStream OS) {
emitEpilog(*OS.get());
}
Error CoveragePrinterHTML::createIndexFile(ArrayRef<StringRef> SourceFiles) {
/// Emit column labels for the table in the index.
static void emitColumnLabelsForIndex(raw_ostream &OS) {
SmallVector<std::string, 4> Columns;
for (const char *Label :
{"Filename", "Region Coverage", "Function Coverage", "Line Coverage"})
Columns.emplace_back(tag("td", Label, "column-entry"));
OS << tag("tr", join(Columns.begin(), Columns.end(), ""));
}
/// Render a file coverage summary (\p FCS) in a table row. If \p IsTotals is
/// false, link the summary to \p SF.
void CoveragePrinterHTML::emitFileSummary(raw_ostream &OS, StringRef SF,
const FileCoverageSummary &FCS,
bool IsTotals) const {
SmallVector<std::string, 4> Columns;
// Format a coverage triple and add the result to the list of columns.
auto AddCoverageTripleToColumn = [&Columns](unsigned Hit, unsigned Total,
float Pctg) {
std::string S;
{
raw_string_ostream RSO{S};
RSO << format("%*.2f", 7, Pctg) << "% (" << Hit << '/' << Total << ')';
}
const char *CellClass = "column-entry-yellow";
if (Pctg < 80.0)
CellClass = "column-entry-red";
else if (Hit == Total)
CellClass = "column-entry-green";
Columns.emplace_back(tag("td", tag("pre", S, "code"), CellClass));
};
// Simplify the display file path, and wrap it in a link if requested.
std::string Filename;
SmallString<128> LinkTextStr(sys::path::relative_path(FCS.Name));
sys::path::remove_dots(LinkTextStr, /*remove_dot_dots=*/true);
sys::path::native(LinkTextStr);
std::string LinkText = escape(LinkTextStr, Opts);
if (IsTotals) {
Filename = LinkText;
} else {
std::string LinkTarget =
escape(getOutputPath(SF, "html", /*InToplevel=*/false), Opts);
Filename = a(LinkTarget, LinkText);
}
Columns.emplace_back(tag("td", tag("pre", Filename, "code")));
AddCoverageTripleToColumn(
FCS.RegionCoverage.NumRegions - FCS.RegionCoverage.NotCovered,
FCS.RegionCoverage.NumRegions, FCS.RegionCoverage.getPercentCovered());
AddCoverageTripleToColumn(FCS.FunctionCoverage.Executed,
FCS.FunctionCoverage.NumFunctions,
FCS.FunctionCoverage.getPercentCovered());
AddCoverageTripleToColumn(
FCS.LineCoverage.NumLines - FCS.LineCoverage.NotCovered,
FCS.LineCoverage.NumLines, FCS.LineCoverage.getPercentCovered());
OS << tag("tr", join(Columns.begin(), Columns.end(), ""), "light-row");
}
Error CoveragePrinterHTML::createIndexFile(
ArrayRef<StringRef> SourceFiles,
const coverage::CoverageMapping &Coverage) {
// Emit the default stylesheet.
auto CSSOrErr = createOutputStream("style", "css", /*InToplevel=*/true);
if (Error E = CSSOrErr.takeError())
return E;
OwnedStream CSS = std::move(CSSOrErr.get());
CSS->operator<<(CSSForCoverage);
// Emit a file index along with some coverage statistics.
auto OSOrErr = createOutputStream("index", "html", /*InToplevel=*/true);
if (Error E = OSOrErr.takeError())
return E;
auto OS = std::move(OSOrErr.get());
raw_ostream &OSRef = *OS.get();
// Emit a table containing links to reports for each file in the covmapping.
assert(Opts.hasOutputDirectory() && "No output directory for index file");
emitPrelude(OSRef, Opts, getPathToStyle(""));
// Emit some basic information about the coverage report.
if (Opts.hasProjectTitle())
OSRef << BeginProjectTitleDiv
<< tag("span", escape(Opts.ProjectTitle, Opts)) << EndProjectTitleDiv;
@ -305,28 +398,19 @@ Error CoveragePrinterHTML::createIndexFile(ArrayRef<StringRef> SourceFiles) {
<< tag("span", escape(Opts.CreatedTimeStr, Opts))
<< EndCreatedTimeDiv;
OSRef << LineBreak;
// Emit a table containing links to reports for each file in the covmapping.
CoverageReport Report(Opts, Coverage);
OSRef << BeginCenteredDiv << BeginTable;
OSRef << BeginSourceNameDiv << "Index" << EndSourceNameDiv;
for (StringRef SF : SourceFiles) {
SmallString<128> LinkTextStr(sys::path::relative_path(SF));
sys::path::remove_dots(LinkTextStr, /*remove_dot_dots=*/true);
sys::path::native(LinkTextStr);
std::string LinkText = escape(sys::path::relative_path(LinkTextStr), Opts);
std::string LinkTarget =
escape(getOutputPath(SF, "html", /*InToplevel=*/false), Opts);
OSRef << tag("tr", tag("td", tag("pre", a(LinkTarget, LinkText), "code")));
}
emitColumnLabelsForIndex(OSRef);
FileCoverageSummary Totals("TOTALS");
auto FileReports = Report.prepareFileReports(Totals, SourceFiles);
for (unsigned I = 0, E = FileReports.size(); I < E; ++I)
emitFileSummary(OSRef, SourceFiles[I], FileReports[I]);
emitFileSummary(OSRef, "Totals", Totals, /*IsTotals=*/true);
OSRef << EndTable << EndCenteredDiv;
emitEpilog(OSRef);
// Emit the default stylesheet.
auto CSSOrErr = createOutputStream("style", "css", /*InToplevel=*/true);
if (Error E = CSSOrErr.takeError())
return E;
OwnedStream CSS = std::move(CSSOrErr.get());
CSS->operator<<(CSSForCoverage);
return Error::success();
}

View File

@ -18,6 +18,8 @@
namespace llvm {
struct FileCoverageSummary;
/// \brief A coverage printer for html output.
class CoveragePrinterHTML : public CoveragePrinter {
public:
@ -26,10 +28,16 @@ public:
void closeViewFile(OwnedStream OS) override;
Error createIndexFile(ArrayRef<StringRef> SourceFiles) override;
Error createIndexFile(ArrayRef<StringRef> SourceFiles,
const coverage::CoverageMapping &Coverage) override;
CoveragePrinterHTML(const CoverageViewOptions &Opts)
: CoveragePrinter(Opts) {}
private:
void emitFileSummary(raw_ostream &OS, StringRef SF,
const FileCoverageSummary &FCS,
bool IsTotals = false) const;
};
/// \brief A code coverage view which supports html-based rendering.

View File

@ -11,6 +11,7 @@
///
//===----------------------------------------------------------------------===//
#include "CoverageReport.h"
#include "SourceCoverageViewText.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallString.h"
@ -27,15 +28,17 @@ void CoveragePrinterText::closeViewFile(OwnedStream OS) {
OS->operator<<('\n');
}
Error CoveragePrinterText::createIndexFile(ArrayRef<StringRef> SourceFiles) {
Error CoveragePrinterText::createIndexFile(
ArrayRef<StringRef> SourceFiles,
const coverage::CoverageMapping &Coverage) {
auto OSOrErr = createOutputStream("index", "txt", /*InToplevel=*/true);
if (Error E = OSOrErr.takeError())
return E;
auto OS = std::move(OSOrErr.get());
raw_ostream &OSRef = *OS.get();
for (StringRef SF : SourceFiles)
OSRef << getOutputPath(SF, "txt", /*InToplevel=*/false) << '\n';
CoverageReport Report(Opts, Coverage);
Report.renderFileReports(OSRef);
return Error::success();
}

View File

@ -26,7 +26,8 @@ public:
void closeViewFile(OwnedStream OS) override;
Error createIndexFile(ArrayRef<StringRef> SourceFiles) override;
Error createIndexFile(ArrayRef<StringRef> SourceFiles,
const coverage::CoverageMapping &Coverage) override;
CoveragePrinterText(const CoverageViewOptions &Opts)
: CoveragePrinter(Opts) {}