Index: CodeCoverage.cpp =================================================================== --- CodeCoverage.cpp +++ CodeCoverage.cpp @@ -652,7 +652,7 @@ // 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; } Index: SourceCoverageView.h =================================================================== --- SourceCoverageView.h +++ SourceCoverageView.h @@ -142,7 +142,8 @@ virtual void closeViewFile(OwnedStream OS) = 0; /// \brief Create an index which lists reports for the given source files. - virtual Error createIndexFile(ArrayRef SourceFiles) = 0; + virtual Error createIndexFile(ArrayRef SourceFiles, + const coverage::CoverageMapping &Coverage) = 0; /// @} }; Index: SourceCoverageViewHTML.h =================================================================== --- SourceCoverageViewHTML.h +++ SourceCoverageViewHTML.h @@ -26,7 +26,8 @@ void closeViewFile(OwnedStream OS) override; - Error createIndexFile(ArrayRef SourceFiles) override; + Error createIndexFile(ArrayRef SourceFiles, + const coverage::CoverageMapping &Coverage) override; CoveragePrinterHTML(const CoverageViewOptions &Opts) : CoveragePrinter(Opts) {} Index: SourceCoverageViewHTML.cpp =================================================================== --- SourceCoverageViewHTML.cpp +++ SourceCoverageViewHTML.cpp @@ -12,9 +12,11 @@ //===----------------------------------------------------------------------===// #include "SourceCoverageViewHTML.h" +#include "CoverageSummaryInfo.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Format.h" #include "llvm/Support/Path.h" using namespace llvm; @@ -66,7 +68,7 @@ } const char *BeginHeader = - "" + "" "" ""; @@ -207,7 +209,8 @@ } void emitPrelude(raw_ostream &OS, const CoverageViewOptions &Opts, - const std::string &PathToStyle = "") { + const std::string &PathToStyle = "", + bool trAlternate = false) { OS << "" "" << BeginHeader; @@ -219,6 +222,16 @@ OS << ""; + if (trAlternate) { + OS << ""; + } OS << EndHeader << "" << BeginCenteredDiv; } @@ -251,7 +264,60 @@ emitEpilog(*OS.get()); } -Error CoveragePrinterHTML::createIndexFile(ArrayRef SourceFiles) { +/// \brief Return the color which correponds to the coverage +/// percentage of a certain metric. +template +static const char *determineCoveragePercentageColor(const T &Info) { + if (Info.isFullyCovered()) + return "green"; + return Info.getPercentCovered() >= 80.0 ? "yellow" : "red"; +} + +template +void emitFormattedCount(raw_ostream &OSRef, format_object value, + const char *align, const char *color = NULL) { + OSRef << ""; + OSRef << value; + OSRef << ""; +} + +static void showSummary(raw_ostream &OSRef, std::string Name, + FileCoverageSummary &File) { + OSRef << ""; + OSRef << tag("td", Name); + + emitFormattedCount(OSRef, format("%u", File.RegionCoverage.NumRegions), + "right"); + emitFormattedCount(OSRef, format("%u", File.RegionCoverage.NotCovered), + "right", + File.RegionCoverage.isFullyCovered() ? "green" : "red"); + emitFormattedCount( + OSRef, format("%.2f%%", File.RegionCoverage.getPercentCovered()), "right", + determineCoveragePercentageColor(File.RegionCoverage)); + emitFormattedCount(OSRef, format("%u", File.FunctionCoverage.NumFunctions), + "right"); + emitFormattedCount(OSRef, format("%u", File.FunctionCoverage.NumFunctions - + File.FunctionCoverage.Executed), + "right"); + emitFormattedCount( + OSRef, format("%.2f%%", File.FunctionCoverage.getPercentCovered()), + "right", determineCoveragePercentageColor(File.FunctionCoverage)); + emitFormattedCount(OSRef, format("%u", File.LineCoverage.NumLines), "right"); + emitFormattedCount(OSRef, format("%u", File.LineCoverage.NotCovered), "right", + File.LineCoverage.isFullyCovered() ? "green" : "red"); + emitFormattedCount( + OSRef, format("%.2f%%", File.LineCoverage.getPercentCovered()), "right", + determineCoveragePercentageColor(File.LineCoverage)); + OSRef << ""; +} + +Error CoveragePrinterHTML::createIndexFile( + ArrayRef SourceFiles, + const coverage::CoverageMapping &Coverage) { auto OSOrErr = createOutputStream("index", "html", /*InToplevel=*/true); if (Error E = OSOrErr.takeError()) return E; @@ -260,15 +326,33 @@ // 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("")); + emitPrelude(OSRef, Opts, getPathToStyle(""), true); OSRef << BeginSourceNameDiv << "Index" << EndSourceNameDiv; OSRef << BeginTable; + OSRef << ""; + OSRef << tag("th", "Filename") << tag("th", "Regions"); + OSRef << tag("th", "Missed Regions") << tag("th", "Cover"); + OSRef << tag("th", "Functions") << tag("th", "Missed Functions"); + OSRef << tag("th", "Executed") + tag("th", "Lines"); + OSRef << tag("th", "Missed Lines") + tag("th", "Cover"); + OSRef << ""; + + FileCoverageSummary Totals("TOTAL"); for (StringRef SF : SourceFiles) { + FileCoverageSummary Summary(SF); + for (const auto &F : Coverage.getCoveredFunctions(SF)) { + FunctionCoverageSummary Function = FunctionCoverageSummary::get(F); + Summary.addFunction(Function); + Totals.addFunction(Function); + } + std::string LinkText = escape(sys::path::relative_path(SF), Opts); std::string LinkTarget = escape(getOutputPath(SF, "html", /*InToplevel=*/false), Opts); - OSRef << tag("tr", tag("td", tag("pre", a(LinkTarget, LinkText), "code"))); + showSummary(OSRef, tag("pre", a(LinkTarget, LinkText), "code"), Summary); } + + showSummary(OSRef, "TOTAL", Totals); OSRef << EndTable; emitEpilog(OSRef); Index: SourceCoverageViewText.h =================================================================== --- SourceCoverageViewText.h +++ SourceCoverageViewText.h @@ -26,7 +26,8 @@ void closeViewFile(OwnedStream OS) override; - Error createIndexFile(ArrayRef SourceFiles) override; + Error createIndexFile(ArrayRef SourceFiles, + const coverage::CoverageMapping &Coverage) override; CoveragePrinterText(const CoverageViewOptions &Opts) : CoveragePrinter(Opts) {} Index: SourceCoverageViewText.cpp =================================================================== --- SourceCoverageViewText.cpp +++ SourceCoverageViewText.cpp @@ -27,7 +27,9 @@ OS->operator<<('\n'); } -Error CoveragePrinterText::createIndexFile(ArrayRef SourceFiles) { +Error CoveragePrinterText::createIndexFile( + ArrayRef SourceFiles, + const coverage::CoverageMapping &) { auto OSOrErr = createOutputStream("index", "txt", /*InToplevel=*/true); if (Error E = OSOrErr.takeError()) return E; @@ -88,8 +90,7 @@ } void SourceCoverageViewText::renderLine( - raw_ostream &OS, LineRef L, - const coverage::CoverageSegment *WrappedSegment, + raw_ostream &OS, LineRef L, const coverage::CoverageSegment *WrappedSegment, CoverageSegmentArray Segments, unsigned ExpansionCol, unsigned ViewDepth) { StringRef Line = L.Line; unsigned LineNumber = L.LineNo; @@ -160,8 +161,9 @@ OS.indent(LineNumberColumnWidth - Str.size()) << Str << '|'; } -void SourceCoverageViewText::renderRegionMarkers( - raw_ostream &OS, CoverageSegmentArray Segments, unsigned ViewDepth) { +void SourceCoverageViewText::renderRegionMarkers(raw_ostream &OS, + CoverageSegmentArray Segments, + unsigned ViewDepth) { renderLinePrefix(OS, ViewDepth); OS.indent(getCombinedColumnWidth(getOptions()));