Index: test/tools/llvm-cov/multiple-files.test
===================================================================
--- test/tools/llvm-cov/multiple-files.test
+++ test/tools/llvm-cov/multiple-files.test
@@ -7,3 +7,15 @@
// CHECK-NEXT: {{^}}b{{[/\\]}}c{{[/\\]}}f4.c
// CHECK-NEXT: {{^}}b{{[/\\]}}f3.c
// CHECK-NEXT: {{^}}f1.c
+
+// RUN: llvm-cov show -format=html %S/Inputs/multiple-files.covmapping -instr-profile=%t.profdata -o %t.dir
+// RUN: FileCheck -check-prefixes=HTML-INDEX -input-file=%t.dir/index.html %s
+
+// HTML-INDEX:
Project directory: |
+// HTML-INDEX: {{[/\\]}}tmp{{[/\\]}}coverage |
+// HTML-INDEX: Function coverage: |
+// HTML-INDEX: Line coverage: |
+// HTML-INDEX: {{[/\\]}}a |
+// HTML-INDEX: {{[/\\]}}b{{[/\\]}}c |
+// HTML-INDEX: {{[/\\]}}b |
+// HTML-INDEX: {{[/\\]}} |
Index: test/tools/llvm-cov/native_separators.c
===================================================================
--- test/tools/llvm-cov/native_separators.c
+++ test/tools/llvm-cov/native_separators.c
@@ -14,7 +14,7 @@
// RUN: FileCheck -check-prefixes=HTML -input-file=%t.dir/coverage/tmp/native_separators.c.html %s
// TEXT-INDEX: \tmp\native_separators.c
-// HTML-INDEX: >tmp\native_separators.c
+// HTML-INDEX: >native_separators.c
// HTML: \tmp\native_separators.c (Binary: native_separators.covmapping)
int main() {}
Index: test/tools/llvm-cov/showLineExecutionCounts.cpp
===================================================================
--- test/tools/llvm-cov/showLineExecutionCounts.cpp
+++ test/tools/llvm-cov/showLineExecutionCounts.cpp
@@ -89,4 +89,4 @@
// HTML-INDEX: 80.00% (16/20)
// HTML-INDEX:
// HTML-INDEX: 70.00% (7/10)
-// HTML-INDEX: TOTALS
+// HTML-INDEX: Totals
Index: tools/llvm-cov/SourceCoverageViewHTML.h
===================================================================
--- tools/llvm-cov/SourceCoverageViewHTML.h
+++ tools/llvm-cov/SourceCoverageViewHTML.h
@@ -37,7 +37,10 @@
private:
void emitFileSummary(raw_ostream &OS, StringRef SF,
const FileCoverageSummary &FCS,
- bool IsTotals = false) const;
+ const StringRef ProjectDir, bool IsTotals = false) const;
+
+ void emitProjectSummary(raw_ostream &OS, StringRef ProjectDir,
+ const FileCoverageSummary &PCS) const;
};
/// \brief A code coverage view which supports html-based rendering.
Index: tools/llvm-cov/SourceCoverageViewHTML.cpp
===================================================================
--- tools/llvm-cov/SourceCoverageViewHTML.cpp
+++ tools/llvm-cov/SourceCoverageViewHTML.cpp
@@ -287,63 +287,142 @@
static void emitColumnLabelsForIndex(raw_ostream &OS) {
SmallVector Columns;
Columns.emplace_back(tag("td", "Filename", "column-entry-left"));
+ Columns.emplace_back(tag("td", "File Directory", "column-entry-left"));
for (const char *Label :
{"Function Coverage", "Line Coverage", "Region Coverage"})
Columns.emplace_back(tag("td", Label, "column-entry"));
OS << tag("tr", join(Columns.begin(), Columns.end(), ""));
}
+// Return the relative path from the project directory (\p ProjectDir) to the
+// file (\p Filename).
+// \code
+// Filename: /output/dir1/dir2/dir3/test.cpp
+// Dir: /output/dir1
+// return string: /dir2/dir3/test.cpp
+// \endcode
+static std::string getFileDir(const StringRef Filename,
+ const StringRef ProjectDir) {
+ assert(Filename.substr(0, ProjectDir.size()) == ProjectDir &&
+ "Filename does not start with ProjectDir.");
+ SmallString<16> FileRelativeDir(Filename);
+ sys::path::remove_filename(FileRelativeDir);
+ if (FileRelativeDir.size() > ProjectDir.size())
+ return FileRelativeDir.str().substr(ProjectDir.size());
+ return sys::path::get_separator().data();
+}
+
+// Return the common path from the list of FilePaths (\p Files).
+// \code
+// FilePath: /output/dir1/test.cpp
+// /output/dir1/dir2/dir3/test1.cpp
+// return string: /output/dir1
+// \endcode
+static std::string commonPath(const std::vector &Files) {
+ assert(!Files.empty() && "Source file vector is empty");
+ std::string CompareString = Files[0];
+ int MaxCommonPath = CompareString.size();
+ for (auto FI = ++Files.begin(), End = Files.end(); FI != End; ++FI) {
+ auto MismatchPair =
+ std::mismatch(CompareString.begin(), CompareString.end(), FI->begin());
+ if ((MismatchPair.first - CompareString.begin()) < MaxCommonPath)
+ MaxCommonPath = MismatchPair.first - CompareString.begin() - 1;
+ }
+ // If there is no common path, return an empty string.
+ if (MaxCommonPath <= 0)
+ return std::string();
+ std::string::size_type found =
+ CompareString.rfind(sys::path::get_separator().data(), MaxCommonPath);
+ return CompareString.substr(0, found);
+}
+
+/// Get the HTML for a coverage triple.
+static std::string getCoverageTriple(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";
+ return tag("td", tag("pre", RSO.str()), CellClass);
+}
+
/// 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,
+ const StringRef ProjectDir,
bool IsTotals) const {
- SmallVector 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), CellClass));
- };
+ SmallVector Columns;
// 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);
+ std::string FileDir;
+ std::string LinkText = escape(sys::path::filename(SF), Opts);
if (IsTotals) {
+ FileDir = std::string();
Filename = LinkText;
} else {
+ FileDir = getFileDir(SF, ProjectDir);
std::string LinkTarget =
escape(getOutputPath(SF, "html", /*InToplevel=*/false), Opts);
Filename = a(LinkTarget, LinkText);
}
Columns.emplace_back(tag("td", tag("pre", Filename)));
- AddCoverageTripleToColumn(FCS.FunctionCoverage.Executed,
- FCS.FunctionCoverage.NumFunctions,
- FCS.FunctionCoverage.getPercentCovered());
- AddCoverageTripleToColumn(
+ Columns.emplace_back(tag("td", tag("pre", FileDir)));
+ Columns.emplace_back(getCoverageTriple(
+ FCS.FunctionCoverage.Executed, FCS.FunctionCoverage.NumFunctions,
+ FCS.FunctionCoverage.getPercentCovered()));
+ Columns.emplace_back(getCoverageTriple(
FCS.LineCoverage.NumLines - FCS.LineCoverage.NotCovered,
- FCS.LineCoverage.NumLines, FCS.LineCoverage.getPercentCovered());
- AddCoverageTripleToColumn(
+ FCS.LineCoverage.NumLines, FCS.LineCoverage.getPercentCovered()));
+ Columns.emplace_back(getCoverageTriple(
FCS.RegionCoverage.NumRegions - FCS.RegionCoverage.NotCovered,
- FCS.RegionCoverage.NumRegions, FCS.RegionCoverage.getPercentCovered());
-
+ FCS.RegionCoverage.NumRegions, FCS.RegionCoverage.getPercentCovered()));
OS << tag("tr", join(Columns.begin(), Columns.end(), ""), "light-row");
}
+/// Render the project coverage summary (\p PCS) and the project directory (\p
+/// ProjectDir) above the index table.
+void CoveragePrinterHTML::emitProjectSummary(
+ raw_ostream &OS, StringRef ProjectDir,
+ const FileCoverageSummary &PCS) const {
+
+ OS << ""
+ << "";
+
+ // Add a coverage row to the coverage summary table.
+ auto AddCoverageSummaryRow = [&OS](StringRef CoverageTitle,
+ StringRef CoverageValue) {
+ OS << tag("tr", tag("td", tag("pre", CoverageTitle)) + CoverageValue.str());
+ };
+
+ AddCoverageSummaryRow("Project directory:",
+ tag("td", tag("pre", ProjectDir)));
+ AddCoverageSummaryRow(
+ "Function coverage:",
+ getCoverageTriple(PCS.FunctionCoverage.Executed,
+ PCS.FunctionCoverage.NumFunctions,
+ PCS.FunctionCoverage.getPercentCovered()));
+ AddCoverageSummaryRow(
+ "Line coverage:",
+ getCoverageTriple(PCS.LineCoverage.NumLines - PCS.LineCoverage.NotCovered,
+ PCS.LineCoverage.NumLines,
+ PCS.LineCoverage.getPercentCovered()));
+ AddCoverageSummaryRow(
+ "Region coverage:",
+ getCoverageTriple(PCS.RegionCoverage.NumRegions -
+ PCS.RegionCoverage.NotCovered,
+ PCS.RegionCoverage.NumRegions,
+ PCS.RegionCoverage.getPercentCovered()));
+
+ OS << ""
+ << " " << " ";
+}
+
Error CoveragePrinterHTML::createIndexFile(
ArrayRef SourceFiles,
const coverage::CoverageMapping &Coverage) {
@@ -372,15 +451,26 @@
if (Opts.hasCreatedTime())
OSRef << tag(CreatedTimeTag, escape(Opts.CreatedTimeStr, Opts));
- // Emit a table containing links to reports for each file in the covmapping.
+ // Emit a table containing the project coverage summary.
CoverageReport Report(Opts, Coverage);
- OSRef << BeginCenteredDiv << BeginTable;
- emitColumnLabelsForIndex(OSRef);
FileCoverageSummary Totals("TOTALS");
auto FileReports = Report.prepareFileReports(Totals, SourceFiles);
+ std::vector NativeFiles;
+ for (const auto &SourceFile : SourceFiles) {
+ SmallString<256> NativeFile(SourceFile);
+ sys::path::remove_dots(NativeFile, /* remove_dot_dot */ true);
+ sys::path::native(NativeFile);
+ NativeFiles.push_back(NativeFile.c_str());
+ }
+ std::string ProjectDir = commonPath(NativeFiles);
+ emitProjectSummary(OSRef, ProjectDir, Totals);
+
+ // Emit a table containing links to reports for each file in the covmapping.
+ OSRef << BeginCenteredDiv << BeginTable;
+ emitColumnLabelsForIndex(OSRef);
for (unsigned I = 0, E = FileReports.size(); I < E; ++I)
- emitFileSummary(OSRef, SourceFiles[I], FileReports[I]);
- emitFileSummary(OSRef, "Totals", Totals, /*IsTotals=*/true);
+ emitFileSummary(OSRef, NativeFiles[I], FileReports[I], ProjectDir);
+ emitFileSummary(OSRef, "Totals", Totals, ProjectDir, /*IsTotals=*/true);
OSRef << EndTable << EndCenteredDiv
<< tag("h5", escape(Opts.getLLVMVersionString(), Opts));
emitEpilog(OSRef);
|