Index: test/tools/llvm-cov/showProjectSummary.cpp
===================================================================
--- test/tools/llvm-cov/showProjectSummary.cpp
+++ test/tools/llvm-cov/showProjectSummary.cpp
@@ -24,7 +24,7 @@
// Test html output.
// RUN: llvm-cov show %S/Inputs/showProjectSummary.covmapping -format=html -o %t.dir -instr-profile %t.profdata -filename-equivalence %s
-// RUN: FileCheck -check-prefixes=HTML,HTML-FILE,HTML-HEADER,HTML-UNCOVEREDLINE -input-file %t.dir/coverage/tmp/showProjectSummary.cpp.html %s
+// RUN: FileCheck -check-prefixes=HTML,HTML-FILE,HTML-HEADER,HTML-UNCOVEREDLINE,HTML_NAVIGATION -input-file %t.dir/coverage/tmp/showProjectSummary.cpp.html %s
// RUN: llvm-cov show %S/Inputs/showProjectSummary.covmapping -format=html -o %t.dir -instr-profile %t.profdata -project-title "Test Suite" -filename-equivalence %s
// RUN: FileCheck -check-prefixes=HTML-TITLE,HTML,HTML-FILE,HTML-HEADER,HTML-UNCOVEREDLINE -input-file %t.dir/coverage/tmp/showProjectSummary.cpp.html %s
// RUN: FileCheck -check-prefixes=HTML-TITLE,HTML -input-file %t.dir/index.html %s
@@ -36,6 +36,7 @@
// HTML: Code Coverage Report
// HTML:
// HTML:
Created:
+// HTML_NAVIGATION: Index | Prev | Next
// HTML-FILE: Source: {{.*}}showProjectSummary.cpp (Binary: showProjectSummary.covmapping)
// HTML-FUNCTION: main
// HTML-UNCOVEREDLINE: Go to first unexecuted line
Index: tools/llvm-cov/CodeCoverage.cpp
===================================================================
--- tools/llvm-cov/CodeCoverage.cpp
+++ tools/llvm-cov/CodeCoverage.cpp
@@ -707,7 +707,8 @@
auto OS = std::move(OSOrErr.get());
View->print(*OS.get(), /*Wholefile=*/true,
- /*ShowSourceName=*/ShowFilenames);
+ /*ShowSourceName=*/ShowFilenames, /*ViewDepth=*/0,
+ SourceFiles);
Printer->closeViewFile(std::move(OS));
});
}
Index: tools/llvm-cov/CoverageViewOptions.h
===================================================================
--- tools/llvm-cov/CoverageViewOptions.h
+++ tools/llvm-cov/CoverageViewOptions.h
@@ -56,6 +56,9 @@
/// \brief Check if the created time of the profile data file is available.
bool hasCreatedTime() const { return !CreatedTimeStr.empty(); }
+
+ /// \brief Return the sub-directory name for file coverage reports.
+ static StringRef getCoverageDir() { return "coverage"; }
};
}
Index: tools/llvm-cov/SourceCoverageView.h
===================================================================
--- tools/llvm-cov/SourceCoverageView.h
+++ tools/llvm-cov/SourceCoverageView.h
@@ -122,9 +122,6 @@
Expected createOutputStream(StringRef Path, StringRef Extension,
bool InToplevel);
- /// \brief Return the sub-directory name for file coverage reports.
- static StringRef getCoverageDir() { return "coverage"; }
-
public:
static std::unique_ptr
create(const CoverageViewOptions &Opts);
@@ -242,7 +239,8 @@
/// \brief Render the project title, the report title \p CellText and the
/// created time for the view.
- virtual void renderCellInTitle(raw_ostream &OS, StringRef CellText) = 0;
+ virtual void renderCellInTitle(raw_ostream &OS, StringRef CellText,
+ ArrayRef SourceFiles) = 0;
/// \brief Render the table header for a given source file
virtual void renderTableHeader(raw_ostream &OS, unsigned IndentLevel = 0) = 0;
@@ -293,7 +291,8 @@
/// \brief Print the code coverage information for a specific portion of a
/// source file to the output stream.
void print(raw_ostream &OS, bool WholeFile, bool ShowSourceName,
- unsigned ViewDepth = 0);
+ unsigned ViewDepth = 0,
+ ArrayRef SourceFiles = ArrayRef());
};
} // namespace llvm
Index: tools/llvm-cov/SourceCoverageView.cpp
===================================================================
--- tools/llvm-cov/SourceCoverageView.cpp
+++ tools/llvm-cov/SourceCoverageView.cpp
@@ -38,7 +38,7 @@
FullPath.append(Opts.ShowOutputDirectory);
if (!InToplevel)
- sys::path::append(FullPath, getCoverageDir());
+ sys::path::append(FullPath, Opts.getCoverageDir());
SmallString<256> ParentPath = sys::path::parent_path(Path);
sys::path::remove_dots(ParentPath, /*remove_dot_dots=*/true);
@@ -166,9 +166,10 @@
}
void SourceCoverageView::print(raw_ostream &OS, bool WholeFile,
- bool ShowSourceName, unsigned ViewDepth) {
+ bool ShowSourceName, unsigned ViewDepth,
+ ArrayRef SourceFiles) {
if (WholeFile)
- renderCellInTitle(OS, "Code Coverage Report");
+ renderCellInTitle(OS, "Code Coverage Report", SourceFiles);
renderViewHeader(OS);
Index: tools/llvm-cov/SourceCoverageViewHTML.h
===================================================================
--- tools/llvm-cov/SourceCoverageViewHTML.h
+++ tools/llvm-cov/SourceCoverageViewHTML.h
@@ -71,10 +71,40 @@
void renderRegionMarkers(raw_ostream &OS, CoverageSegmentArray Segments,
unsigned ViewDepth) override;
- void renderCellInTitle(raw_ostream &OS, StringRef CellText) override;
+ void renderCellInTitle(raw_ostream &OS, StringRef CellText,
+ ArrayRef SourceFiles) override;
void renderTableHeader(raw_ostream &OS, unsigned IndentLevel) override;
+ /// \brief Return a relative filename from \p FirstFilename to \p
+ /// SecondFileName.
+ ///
+ /// Typical usage:
+ /// \code
+ /// case 1:
+ /// first input file1: /coverage/dir1/test.cpp
+ /// seconds input file2: /coverage/dir1/dir2/test1.cpp
+ /// return string: ../../../coverage/dir1/dir2/test1.cpp.html
+ /// case 2:
+ /// first input file1: /output/dir1/test.cpp
+ /// seconds input file2: /output/dir1/test1.cpp
+ /// return string: ../../../output/dir1/test1.cpp.html
+ /// case 3:
+ /// first input file1: /output/dir1/dir2/test.cpp
+ /// seconds input file2: /output/dir1/test1.cpp
+ /// return string: ../../../../output/dir1/test1.cpp.html
+ /// case 4:
+ /// first input file1: /coverage/dir1/test.cpp
+ /// seconds input file2: index.html
+ /// return string: ../../../index.html
+ /// \endcode
+ std::string getRelativePath(const StringRef FirstFilename,
+ const StringRef SecondFileName);
+
+ /// \brief Render the page navigation in the project summary.
+ void renderPageNavigation(raw_ostream &OS, StringRef SourceFileName,
+ ArrayRef SourceFiles);
+
public:
SourceCoverageViewHTML(StringRef SourceName, const MemoryBuffer &File,
const CoverageViewOptions &Options,
Index: tools/llvm-cov/SourceCoverageViewHTML.cpp
===================================================================
--- tools/llvm-cov/SourceCoverageViewHTML.cpp
+++ tools/llvm-cov/SourceCoverageViewHTML.cpp
@@ -544,8 +544,8 @@
OS << EndExpansionDiv;
}
-void SourceCoverageViewHTML::renderCellInTitle(raw_ostream &OS,
- StringRef CellText) {
+void SourceCoverageViewHTML::renderCellInTitle(
+ raw_ostream &OS, StringRef CellText, ArrayRef SourceFiles) {
if (getOptions().hasProjectTitle())
OS << BeginProjectTitleDiv
<< tag("span", escape(getOptions().ProjectTitle, getOptions()))
@@ -558,6 +558,9 @@
OS << BeginCreatedTimeDiv
<< tag("span", escape(getOptions().CreatedTimeStr, getOptions()))
<< EndCreatedTimeDiv;
+
+ if (getOptions().hasOutputDirectory())
+ renderPageNavigation(OS, getSourceName(), SourceFiles);
}
void SourceCoverageViewHTML::renderTableHeader(raw_ostream &OS,
@@ -568,3 +571,62 @@
<< tag("td", tag("span", tag("pre", escape("Source", getOptions()))));
renderLineSuffix(OS, ViewDepth);
}
+
+std::string
+SourceCoverageViewHTML::getRelativePath(const StringRef FirstFilename,
+ const StringRef SecondFileName) {
+ // Resolve the dots in the file paths.
+ SmallString<256> FileOne(FirstFilename);
+ sys::path::remove_dots(FileOne, /* remove_dot_dot */ true);
+ SmallString<256> FileTwo(SecondFileName);
+ sys::path::remove_dots(FileTwo, /* remove_dot_dot */ true);
+ // HTML uses '/' as the separator
+#ifdef LLVM_ON_WIN32
+ std::replace(FileTwo.begin(), FileTwo.end(), '\\', '/');
+#endif
+ std::string RelativePath;
+ for (auto PathChar : FileOne.str())
+ if (sys::path::is_separator(PathChar))
+ RelativePath += "../";
+ if (SecondFileName == "index.html")
+ RelativePath += SecondFileName.str();
+ else {
+ RelativePath += getOptions().getCoverageDir().str() + '/';
+ RelativePath += sys::path::relative_path(FileTwo).str() + ".html";
+ }
+ return RelativePath;
+}
+
+void SourceCoverageViewHTML::renderPageNavigation(
+ raw_ostream &OS, StringRef SourceFileName,
+ ArrayRef SourceFiles) {
+ std::string RelativePathToIndex =
+ getRelativePath(SourceFileName, "index.html");
+ StringRef PrevSourceFile;
+ StringRef NextSourceFile;
+ for (ArrayRef::iterator I = SourceFiles.begin(),
+ E = SourceFiles.end();
+ I != E; ++I) {
+ if (SourceFileName == (*I)) {
+ if (I != SourceFiles.begin())
+ PrevSourceFile = (*(I - 1));
+ if ((I + 1) != E)
+ NextSourceFile = (*(I + 1));
+ break;
+ }
+ if ((I + 1) == E)
+ assert("Source file doesn't find");
+ }
+
+ std::string RelativePathToPrev;
+ if (!PrevSourceFile.empty())
+ RelativePathToPrev = getRelativePath(SourceFileName, PrevSourceFile);
+ std::string RelativePathToNext;
+ if (!NextSourceFile.empty())
+ RelativePathToNext = getRelativePath(SourceFileName, NextSourceFile);
+ OS << a(RelativePathToIndex, "Index") << " | ";
+ OS << (RelativePathToPrev.empty() ? "Prev" : a(RelativePathToPrev, "Prev"))
+ << " | ";
+ OS << (RelativePathToNext.empty() ? "Next" : a(RelativePathToNext, "Next"))
+ << LineBreak;
+}
Index: tools/llvm-cov/SourceCoverageViewText.h
===================================================================
--- tools/llvm-cov/SourceCoverageViewText.h
+++ tools/llvm-cov/SourceCoverageViewText.h
@@ -71,7 +71,8 @@
void renderRegionMarkers(raw_ostream &OS, CoverageSegmentArray Segments,
unsigned ViewDepth) override;
- void renderCellInTitle(raw_ostream &OS, StringRef CellText) override;
+ void renderCellInTitle(raw_ostream &OS, StringRef CellText,
+ ArrayRef SourceFiles) override;
void renderTableHeader(raw_ostream &OS, unsigned IndentLevel) override;
Index: tools/llvm-cov/SourceCoverageViewText.cpp
===================================================================
--- tools/llvm-cov/SourceCoverageViewText.cpp
+++ tools/llvm-cov/SourceCoverageViewText.cpp
@@ -213,8 +213,8 @@
ISV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/true, ViewDepth);
}
-void SourceCoverageViewText::renderCellInTitle(raw_ostream &OS,
- StringRef CellText) {
+void SourceCoverageViewText::renderCellInTitle(
+ raw_ostream &OS, StringRef CellText, ArrayRef SourceFiles) {
if (getOptions().hasProjectTitle())
getOptions().colored_ostream(OS, raw_ostream::CYAN)
<< getOptions().ProjectTitle << "\n";