Skip to content

Commit 661a557

Browse files
committed
#431: Added Markdown output format
1 parent c3ed2a4 commit 661a557

File tree

7 files changed

+220
-4
lines changed

7 files changed

+220
-4
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ Explanations:
6868
Globbing is supported.
6969
Target directory: The directory where the generated report should be saved.
7070
Report types: The output formats and scope (separated by semicolon).
71-
Values: Badges, Clover, Cobertura, CsvSummary, Html, HtmlChart, HtmlInline,
71+
Values: Badges, Clover, Cobertura, CsvSummary, MarkdownSummary, Html, HtmlChart, HtmlInline,
7272
HtmlInline_AzurePipelines, HtmlInline_AzurePipelines_Dark, HtmlSummary,
7373
JsonSummary, Latex, LatexSummary, lcov, MHtml, PngChart, SonarQube, TeamCitySummary,
7474
TextSummary, Xml, XmlSummary
@@ -202,7 +202,7 @@ If you need a custom format, you can create a [plugin](https://github.com/daniel
202202

203203
| **Input formats** | **Output formats** |
204204
|:------------------|:-------------------|
205-
| <ul><li>[OpenCover](https://github.com/OpenCover/opencover) ([Nuget](https://www.nuget.org/packages/OpenCover))<br/>OpenCover format is also generated by [coverlet](https://github.com/coverlet-coverage/coverlet/) and [altcover](https://github.com/SteveGilham/altcover)</li><li>[dotCover](https://www.jetbrains.com/dotcover/help/dotCover__Console_Runner_Commands.html) ([Nuget](https://www.nuget.org/packages/JetBrains.dotCover.CommandLineTools/), /ReportType=DetailedXML)</li><li>Visual Studio ([vstest.console.exe](https://github.com/danielpalme/ReportGenerator/wiki/Visual-Studio-Coverage-Tools#vstestconsoleexe), [CodeCoverage.exe](https://github.com/danielpalme/ReportGenerator/wiki/Visual-Studio-Coverage-Tools#codecoverageexe))</li><li>[NCover](https://www.ncover.com/info/download) (tested version 1.5.8, other versions may not work)</li><li>[Cobertura](https://github.com/cobertura/cobertura)</li><li>[JaCoCo](https://www.jacoco.org/jacoco/index.html)</li><li>[Clover](https://openclover.org/)</li><li>Mono ([mprof-report](https://www.mono-project.com/docs/debug+profile/profile/profiler/#analyzing-the-profile-data))</li><li>[gcov](https://gcc.gnu.org/onlinedocs/gcc/Gcov.html)</li><li>[lcov](https://github.com/linux-test-project/lcov)</li></ul><br/> | <ul><li>Html, HtmlSummary, HtmlInline, HtmlInline_AzurePipelines, HtmlInline_AzurePipelines_Dark, HtmlChart, [MHtml](https://en.wikipedia.org/wiki/MHTML)</li><li>Clover</li><li>Cobertura</li><li>[SonarQube](https://docs.sonarqube.org/latest/analysis/generic-test)</li><li>TeamCitySummary</li><li>[lcov](https://github.com/linux-test-project/lcov)</li><li>Xml, XmlSummary</li><li>JsonSummary</li><li>Latex, LatexSummary</li><li>TextSummary</li><li>CsvSummary</li><li>PngChart</li><li>Badges</li><li>[Custom reports](https://github.com/danielpalme/ReportGenerator/wiki/Custom-reports)</li></ul> |
205+
| <ul><li>[OpenCover](https://github.com/OpenCover/opencover) ([Nuget](https://www.nuget.org/packages/OpenCover))<br/>OpenCover format is also generated by [coverlet](https://github.com/coverlet-coverage/coverlet/) and [altcover](https://github.com/SteveGilham/altcover)</li><li>[dotCover](https://www.jetbrains.com/dotcover/help/dotCover__Console_Runner_Commands.html) ([Nuget](https://www.nuget.org/packages/JetBrains.dotCover.CommandLineTools/), /ReportType=DetailedXML)</li><li>Visual Studio ([vstest.console.exe](https://github.com/danielpalme/ReportGenerator/wiki/Visual-Studio-Coverage-Tools#vstestconsoleexe), [CodeCoverage.exe](https://github.com/danielpalme/ReportGenerator/wiki/Visual-Studio-Coverage-Tools#codecoverageexe))</li><li>[NCover](https://www.ncover.com/info/download) (tested version 1.5.8, other versions may not work)</li><li>[Cobertura](https://github.com/cobertura/cobertura)</li><li>[JaCoCo](https://www.jacoco.org/jacoco/index.html)</li><li>[Clover](https://openclover.org/)</li><li>Mono ([mprof-report](https://www.mono-project.com/docs/debug+profile/profile/profiler/#analyzing-the-profile-data))</li><li>[gcov](https://gcc.gnu.org/onlinedocs/gcc/Gcov.html)</li><li>[lcov](https://github.com/linux-test-project/lcov)</li></ul><br/> | <ul><li>Html, HtmlSummary, HtmlInline, HtmlInline_AzurePipelines, HtmlInline_AzurePipelines_Dark, HtmlChart, [MHtml](https://en.wikipedia.org/wiki/MHTML)</li><li>Clover</li><li>Cobertura</li><li>[SonarQube](https://docs.sonarqube.org/latest/analysis/generic-test)</li><li>TeamCitySummary</li><li>[lcov](https://github.com/linux-test-project/lcov)</li><li>Xml, XmlSummary</li><li>JsonSummary</li><li>Latex, LatexSummary</li><li>TextSummary</li><li>CsvSummary</li><li>MarkdownSummary</li><li>PngChart</li><li>Badges</li><li>[Custom reports](https://github.com/danielpalme/ReportGenerator/wiki/Custom-reports)</li></ul> |
206206

207207
### Screenshots
208208
The screenshots show two snippets of the generated reports:

docs/resources/SampleReports.zip

364 Bytes
Binary file not shown.

docs/usage.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ <h4>Usage</h4>
257257
{ name: 'TeamCitySummary', description: 'Command line output interpreted by TeamCity.', requiresHistoryDir: false, checked: false },
258258
{ name: 'TextSummary', description: 'Creates a single TXT file containing coverage information per class.', requiresHistoryDir: false, checked: false },
259259
{ name: 'CsvSummary', description: 'Creates a single CSV file containing coverage information per class.', requiresHistoryDir: false, checked: false },
260+
{ name: 'MarkdownSummary', description: 'Creates a single Markdown file containing coverage information per class.', requiresHistoryDir: false, checked: false },
260261
{ name: 'PngChart', description: 'Creates a single PNG file containing a chart with historic coverage information.', requiresHistoryDir: true, checked: false },
261262
{ name: 'Badges', description: 'Creates three SVG and two PNG files that show line and/or branch coverage information.', requiresHistoryDir: false, checked: false }
262263
];

src/AzureDevopsTask/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ To learn how to use *ReportGenerator* have a look at the:
1818
inputs:
1919
reports: 'coverage.xml' # REQUIRED # The coverage reports that should be parsed (separated by semicolon). Globbing is supported.
2020
targetdir: 'coveragereport' # REQUIRED # The directory where the generated report should be saved.
21-
reporttypes: 'HtmlInline_AzurePipelines;Cobertura' # The output formats and scope (separated by semicolon) Values: Badges, Clover, Cobertura, CsvSummary, Html, HtmlChart, HtmlInline, HtmlInline_AzurePipelines, HtmlInline_AzurePipelines_Dark, HtmlSummary, JsonSummary, Latex, LatexSummary, lcov, MHtml, PngChart, SonarQube, TeamCitySummary, TextSummary, Xml, XmlSummary
21+
reporttypes: 'HtmlInline_AzurePipelines;Cobertura' # The output formats and scope (separated by semicolon) Values: Badges, Clover, Cobertura, CsvSummary, Html, HtmlChart, HtmlInline, HtmlInline_AzurePipelines, HtmlInline_AzurePipelines_Dark, HtmlSummary, JsonSummary, Latex, LatexSummary, lcov, MarkdownSummary, MHtml, PngChart, SonarQube, TeamCitySummary, TextSummary, Xml, XmlSummary
2222
sourcedirs: '' # Optional directories which contain the corresponding source code (separated by semicolon). The source directories are used if coverage report contains classes without path information.
2323
historydir: '' # Optional directory for storing persistent coverage information. Can be used in future reports to show coverage evolution.
2424
plugins: '' # Optional plugin files for custom reports or custom history storage (separated by semicolon).

src/AzureDevopsTask/ReportGenerator/task.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
"label": "Report types",
4242
"defaultValue": "HtmlInline_AzurePipelines;Cobertura",
4343
"required": false,
44-
"helpMarkDown": "The output formats and scope (separated by semicolon) Values: Badges, Clover, Cobertura, CsvSummary, Html, HtmlChart, HtmlInline, HtmlInline_AzurePipelines, HtmlInline_AzurePipelines_Dark, HtmlSummary, JsonSummary, Latex, LatexSummary, lcov, MHtml, PngChart, SonarQube, TeamCitySummary, TextSummary, Xml, XmlSummary"
44+
"helpMarkDown": "The output formats and scope (separated by semicolon) Values: Badges, Clover, Cobertura, CsvSummary, Html, HtmlChart, HtmlInline, HtmlInline_AzurePipelines, HtmlInline_AzurePipelines_Dark, HtmlSummary, JsonSummary, Latex, LatexSummary, lcov, MarkdownSummary, MHtml, PngChart, SonarQube, TeamCitySummary, TextSummary, Xml, XmlSummary"
4545
},
4646
{
4747
"name": "sourcedirs",

src/Readme.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ The following output formats are supported by ReportGenerator:
3232
-TeamCitySummary
3333
-TextSummary
3434
-CsvSummary
35+
-MarkdownSummary
3536
-PngChart
3637
-Badges
3738
-Custom formats (https://github.com/danielpalme/ReportGenerator/wiki/Custom-reports)
@@ -63,6 +64,10 @@ For further details take a look at LICENSE.txt.
6364

6465
CHANGELOG
6566

67+
4.8.10.0
68+
69+
* New: #431: Added Markdown output format
70+
6671
4.8.9.0
6772

6873
* Fix: Updated Angular Components
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Globalization;
4+
using System.IO;
5+
using System.Linq;
6+
using System.Text;
7+
using Palmmedia.ReportGenerator.Core.Common;
8+
using Palmmedia.ReportGenerator.Core.Logging;
9+
using Palmmedia.ReportGenerator.Core.Parser.Analysis;
10+
using Palmmedia.ReportGenerator.Core.Properties;
11+
12+
namespace Palmmedia.ReportGenerator.Core.Reporting.Builders
13+
{
14+
/// <summary>
15+
/// Creates summary report in Markdown format (no reports for classes are generated).
16+
/// </summary>
17+
public class MarkdownSummaryReportBuilder : IReportBuilder
18+
{
19+
/// <summary>
20+
/// The Logger.
21+
/// </summary>
22+
private static readonly ILogger Logger = LoggerFactory.GetLogger(typeof(MarkdownSummaryReportBuilder));
23+
24+
/// <summary>
25+
/// Gets the report type.
26+
/// </summary>
27+
/// <value>
28+
/// The report type.
29+
/// </value>
30+
public string ReportType => "MarkdownSummary";
31+
32+
/// <summary>
33+
/// Gets or sets the report context.
34+
/// </summary>
35+
/// <value>
36+
/// The report context.
37+
/// </value>
38+
public IReportContext ReportContext { get; set; }
39+
40+
/// <summary>
41+
/// Creates a class report.
42+
/// </summary>
43+
/// <param name="class">The class.</param>
44+
/// <param name="fileAnalyses">The file analyses that correspond to the class.</param>
45+
public void CreateClassReport(Class @class, IEnumerable<FileAnalysis> fileAnalyses)
46+
{
47+
}
48+
49+
/// <summary>
50+
/// Creates the summary report.
51+
/// </summary>
52+
/// <param name="summaryResult">The summary result.</param>
53+
public void CreateSummaryReport(SummaryResult summaryResult)
54+
{
55+
if (summaryResult == null)
56+
{
57+
throw new ArgumentNullException(nameof(summaryResult));
58+
}
59+
60+
string targetDirectory = this.ReportContext.ReportConfiguration.TargetDirectory;
61+
62+
if (this.ReportContext.Settings.CreateSubdirectoryForAllReportTypes)
63+
{
64+
targetDirectory = Path.Combine(targetDirectory, this.ReportType);
65+
66+
if (!Directory.Exists(targetDirectory))
67+
{
68+
try
69+
{
70+
Directory.CreateDirectory(targetDirectory);
71+
}
72+
catch (Exception ex)
73+
{
74+
Logger.ErrorFormat(Resources.TargetDirectoryCouldNotBeCreated, targetDirectory, ex.GetExceptionMessageForDisplay());
75+
return;
76+
}
77+
}
78+
}
79+
80+
string targetPath = Path.Combine(targetDirectory, "Summary.md");
81+
82+
Logger.InfoFormat(Resources.WritingReportFile, targetPath);
83+
84+
using (var reportTextWriter = new StreamWriter(new FileStream(targetPath, FileMode.Create), Encoding.UTF8))
85+
{
86+
reportTextWriter.WriteLine("# {0}", ReportResources.Summary);
87+
reportTextWriter.WriteLine("|||");
88+
reportTextWriter.WriteLine("|:---|:---|");
89+
reportTextWriter.WriteLine("| {0} | {1} |", ReportResources.GeneratedOn, DateTime.Now.ToShortDateString() + " - " + DateTime.Now.ToLongTimeString());
90+
reportTextWriter.WriteLine("| {0} | {1} |", ReportResources.Parser, summaryResult.UsedParser);
91+
reportTextWriter.WriteLine("| {0} | {1} |", ReportResources.Assemblies2, summaryResult.Assemblies.Count().ToString(CultureInfo.InvariantCulture));
92+
reportTextWriter.WriteLine("| {0} | {1} |", ReportResources.Classes, summaryResult.Assemblies.SelectMany(a => a.Classes).Count().ToString(CultureInfo.InvariantCulture));
93+
reportTextWriter.WriteLine("| {0} | {1} |", ReportResources.Files2, summaryResult.Assemblies.SelectMany(a => a.Classes).SelectMany(a => a.Files).Distinct().Count().ToString(CultureInfo.InvariantCulture));
94+
reportTextWriter.WriteLine("| {0} | {1} |", ReportResources.CoveredLines, summaryResult.CoveredLines.ToString(CultureInfo.InvariantCulture));
95+
reportTextWriter.WriteLine("| {0} | {1} |", ReportResources.UncoveredLines, (summaryResult.CoverableLines - summaryResult.CoveredLines).ToString(CultureInfo.InvariantCulture));
96+
reportTextWriter.WriteLine("| {0} | {1} |", ReportResources.CoverableLines, summaryResult.CoverableLines.ToString(CultureInfo.InvariantCulture));
97+
reportTextWriter.WriteLine("| {0} | {1} |", ReportResources.TotalLines, summaryResult.TotalLines.GetValueOrDefault().ToString(CultureInfo.InvariantCulture));
98+
reportTextWriter.WriteLine("| {0} | {1} |", ReportResources.Coverage2, summaryResult.CoverageQuota.HasValue ? $"{summaryResult.CoverageQuota.Value.ToString(CultureInfo.InvariantCulture)}% ({summaryResult.CoveredLines.ToString(CultureInfo.InvariantCulture)} {ReportResources.Of} {summaryResult.CoverableLines.ToString(CultureInfo.InvariantCulture)})" : string.Empty);
99+
100+
if (summaryResult.SupportsBranchCoverage)
101+
{
102+
if (summaryResult.CoveredBranches.HasValue && summaryResult.TotalBranches.HasValue)
103+
{
104+
reportTextWriter.WriteLine("| {0} | {1} |", ReportResources.CoveredBranches2, summaryResult.CoveredBranches.GetValueOrDefault().ToString(CultureInfo.InvariantCulture));
105+
reportTextWriter.WriteLine("| {0} | {1} |", ReportResources.TotalBranches, summaryResult.TotalBranches.GetValueOrDefault().ToString(CultureInfo.InvariantCulture));
106+
107+
decimal? branchCoverage = summaryResult.BranchCoverageQuota;
108+
109+
if (branchCoverage.HasValue)
110+
{
111+
reportTextWriter.WriteLine("| {0} | {1} |", ReportResources.BranchCoverage2, $"{branchCoverage.Value.ToString(CultureInfo.InvariantCulture)}% ({summaryResult.CoveredBranches.GetValueOrDefault().ToString(CultureInfo.InvariantCulture)} {ReportResources.Of} {summaryResult.TotalBranches.GetValueOrDefault().ToString(CultureInfo.InvariantCulture)})");
112+
}
113+
}
114+
}
115+
116+
if (this.ReportContext.ReportConfiguration.Tag != null)
117+
{
118+
reportTextWriter.WriteLine("| {0} | {1} |", ReportResources.Tag, this.ReportContext.ReportConfiguration.Tag);
119+
}
120+
121+
reportTextWriter.WriteLine();
122+
123+
if (summaryResult.Assemblies.Any())
124+
{
125+
var maximumNameLength = summaryResult.Assemblies
126+
.SelectMany(a => a.Classes).Select(c => c.DisplayName)
127+
.Union(summaryResult.Assemblies.Select(a => a.Name))
128+
.Max(n => n.Length);
129+
130+
reportTextWriter.Write(
131+
"|**{0}**|**{1}**|**{2}**|**{3}**|**{4}**|**{5}**|",
132+
ReportResources.Name,
133+
ReportResources.Covered,
134+
ReportResources.Uncovered,
135+
ReportResources.Coverable,
136+
ReportResources.Total,
137+
ReportResources.Coverage);
138+
139+
if (summaryResult.SupportsBranchCoverage)
140+
{
141+
reportTextWriter.WriteLine(
142+
"**{0}**|**{1}**|**{2}**|",
143+
ReportResources.Covered,
144+
ReportResources.Total,
145+
ReportResources.BranchCoverage);
146+
}
147+
else
148+
{
149+
reportTextWriter.WriteLine(string.Empty);
150+
}
151+
152+
reportTextWriter.Write("|:---|---:|---:|---:|---:|---:|");
153+
154+
if (summaryResult.SupportsBranchCoverage)
155+
{
156+
reportTextWriter.WriteLine("---:|---:|---:|");
157+
}
158+
else
159+
{
160+
reportTextWriter.WriteLine(string.Empty);
161+
}
162+
163+
foreach (var assembly in summaryResult.Assemblies)
164+
{
165+
reportTextWriter.Write("|**{0}**", assembly.Name);
166+
reportTextWriter.Write("|**{0}**", assembly.CoveredLines);
167+
reportTextWriter.Write("|**{0}**", assembly.CoverableLines - assembly.CoveredLines);
168+
reportTextWriter.Write("|**{0}**", assembly.CoverableLines);
169+
reportTextWriter.Write("|**{0}**", assembly.TotalLines.GetValueOrDefault());
170+
reportTextWriter.Write("|**{0}**", assembly.CoverageQuota.HasValue ? assembly.CoverageQuota.Value.ToString(CultureInfo.InvariantCulture) + "%" : string.Empty);
171+
172+
if (summaryResult.SupportsBranchCoverage)
173+
{
174+
reportTextWriter.Write("|**{0}**", assembly.CoveredBranches);
175+
reportTextWriter.Write("|**{0}**", assembly.TotalBranches);
176+
reportTextWriter.Write("|**{0}**", assembly.BranchCoverageQuota.HasValue ? assembly.BranchCoverageQuota.Value.ToString(CultureInfo.InvariantCulture) + "%" : string.Empty);
177+
}
178+
179+
reportTextWriter.WriteLine("|");
180+
181+
foreach (var @class in assembly.Classes)
182+
{
183+
reportTextWriter.Write("|{0}", @class.Name);
184+
reportTextWriter.Write("|{0}", @class.CoveredLines);
185+
reportTextWriter.Write("|{0}", @class.CoverableLines - @class.CoveredLines);
186+
reportTextWriter.Write("|{0}", @class.CoverableLines);
187+
reportTextWriter.Write("|{0}", @class.TotalLines.GetValueOrDefault());
188+
reportTextWriter.Write("|{0}", @class.CoverageQuota.HasValue ? @class.CoverageQuota.Value.ToString(CultureInfo.InvariantCulture) + "%" : string.Empty);
189+
190+
if (summaryResult.SupportsBranchCoverage)
191+
{
192+
reportTextWriter.Write("|{0}", @class.CoveredBranches);
193+
reportTextWriter.Write("|{0}", @class.TotalBranches);
194+
reportTextWriter.Write("|{0}", @class.BranchCoverageQuota.HasValue ? @class.BranchCoverageQuota.Value.ToString(CultureInfo.InvariantCulture) + "%" : string.Empty);
195+
}
196+
197+
reportTextWriter.WriteLine("|");
198+
}
199+
}
200+
}
201+
else
202+
{
203+
reportTextWriter.WriteLine(ReportResources.NoCoveredAssemblies);
204+
}
205+
206+
reportTextWriter.Flush();
207+
}
208+
}
209+
}
210+
}

0 commit comments

Comments
 (0)