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(*Coverage)) { error("Could not create index file!", toString(std::move(E))); return 1; } Index: SourceCoverageView.h =================================================================== --- SourceCoverageView.h +++ SourceCoverageView.h @@ -142,7 +142,7 @@ 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(const coverage::CoverageMapping &Coverage) = 0; /// @} }; Index: SourceCoverageViewHTML.h =================================================================== --- SourceCoverageViewHTML.h +++ SourceCoverageViewHTML.h @@ -26,7 +26,7 @@ void closeViewFile(OwnedStream OS) override; - Error createIndexFile(ArrayRef SourceFiles) override; + Error createIndexFile(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 = - "" + "" "" ""; @@ -206,19 +208,41 @@ return PathToStyle + "style.css"; } -void emitPrelude(raw_ostream &OS, const CoverageViewOptions &Opts, - const std::string &PathToStyle = "") { +static std::string getCSSForCoverage(const CoverageViewOptions &Opts, + const std::string &PathToStyle = "", + bool AlternateRowColor = false) { + std::string result; + + // Link to a stylesheet if one is available. Otherwise, use the default style. + if (PathToStyle.empty()) { + result = ""; + } else { + result = ""; + } + if (AlternateRowColor) { + result += ""; + } + + return result; +} + +void emitPrelude(raw_ostream &OS, const std::string &CSS, + bool trAlternate = false) { OS << "" "" << BeginHeader; - // Link to a stylesheet if one is available. Otherwise, use the default style. - if (PathToStyle.empty()) - OS << ""; - else - OS << ""; - + OS << CSS; OS << EndHeader << "" << BeginCenteredDiv; } @@ -238,10 +262,10 @@ OwnedStream OS = std::move(OSOrErr.get()); if (!Opts.hasOutputDirectory()) { - emitPrelude(*OS.get(), Opts); + emitPrelude(*OS.get(), getCSSForCoverage(Opts)); } else { std::string ViewPath = getOutputPath(Path, "html", InToplevel); - emitPrelude(*OS.get(), Opts, getPathToStyle(ViewPath)); + emitPrelude(*OS.get(), getCSSForCoverage(Opts, getPathToStyle(ViewPath))); } return std::move(OS); @@ -251,7 +275,58 @@ 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"; +} + +void emitFormattedCount(raw_ostream &OSRef, const format_object_base &Value, + const char *Align, const char *Color = NULL) { + OSRef << ""; + OSRef << Value; + OSRef << ""; +} + +static void showSummary(raw_ostream &OSRef, StringRef 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( + const coverage::CoverageMapping &Coverage) { auto OSOrErr = createOutputStream("index", "html", /*InToplevel=*/true); if (Error E = OSOrErr.takeError()) return E; @@ -260,15 +335,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, getCSSForCoverage(Opts, getPathToStyle(""), true)); OSRef << BeginSourceNameDiv << "Index" << EndSourceNameDiv; OSRef << BeginTable; - for (StringRef SF : SourceFiles) { + 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 : Coverage.getUniqueSourceFiles()) { + 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,7 @@ void closeViewFile(OwnedStream OS) override; - Error createIndexFile(ArrayRef SourceFiles) override; + Error createIndexFile(const coverage::CoverageMapping &Coverage) override; CoveragePrinterText(const CoverageViewOptions &Opts) : CoveragePrinter(Opts) {} Index: SourceCoverageViewText.cpp =================================================================== --- SourceCoverageViewText.cpp +++ SourceCoverageViewText.cpp @@ -27,14 +27,15 @@ OS->operator<<('\n'); } -Error CoveragePrinterText::createIndexFile(ArrayRef SourceFiles) { +Error CoveragePrinterText::createIndexFile( + 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) + for (StringRef SF : Coverage.getUniqueSourceFiles()) OSRef << getOutputPath(SF, "txt", /*InToplevel=*/false) << '\n'; return Error::success(); @@ -88,8 +89,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 +160,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()));