Skip to content

Commit a40d6b5

Browse files
committed
#731 Added option to break build when maximum risk hotspots metrics are exceeded
1 parent 15b7cb4 commit a40d6b5

9 files changed

+173
-2
lines changed

src/Readme.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ For further details take a look at LICENSE.txt.
6767

6868
CHANGELOG
6969

70+
5.4.7.0
71+
72+
* New: #731 Added option to break build when maximum risk hotspots metrics are exceeded
73+
7074
5.4.6.0
7175

7276
* Fix: #730 Added support for the REPORTGENERATOR_LICENSE environment variable in the MSBuild task (contributed by @0xced)

src/ReportGenerator.Core/CodeAnalysis/RiskHotspotsAnalysisThresholds.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,20 @@ public class RiskHotspotsAnalysisThresholds
1919
/// Gets or sets the threshold for NPath complexity.
2020
/// </summary>
2121
public decimal MetricThresholdForNPathComplexity { get; set; } = 200;
22+
23+
/// <summary>
24+
/// Gets or sets the maximum threshold for cylomatic complexity. If any risk hotspot is greater this this treshold, ReportGenerator will exit unsuccessfully.
25+
/// </summary>
26+
public decimal? MaximumThresholdForCyclomaticComplexity { get; set; }
27+
28+
/// <summary>
29+
/// Gets or sets the maximum threshold for crap score. If any risk hotspot is greater this this treshold, ReportGenerator will exit unsuccessfully.
30+
/// </summary>
31+
public decimal? MaximumThresholdForCrapScore { get; set; }
32+
33+
/// <summary>
34+
/// Gets or sets the maximum threshold for NPath complexity. If any risk hotspot is greater this this treshold, ReportGenerator will exit unsuccessfully.
35+
/// </summary>
36+
public decimal? MaximumThresholdForNPathComplexity { get; set; }
2237
}
2338
}

src/ReportGenerator.Core/Generator.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ public bool GenerateReport(IReportConfiguration reportConfiguration)
5959
Logger.Error(ex.GetExceptionMessageForDisplay());
6060
return false;
6161
}
62+
catch (RiskhotspotThresholdException ex)
63+
{
64+
Logger.Error(ex.GetExceptionMessageForDisplay());
65+
return false;
66+
}
6267
catch (Exception ex)
6368
{
6469
Logger.Error(ex.GetExceptionMessageForDisplay());
@@ -340,6 +345,9 @@ public void GenerateReport(
340345

341346
new MinimumCoverageThresholdsValidator(minimumCoverageThresholds)
342347
.Validate(parserResult);
348+
349+
new MaxiumRiskhotspotsThresholdsValidator(riskHotspotsAnalysisThresholds)
350+
.Validate(reportContext.RiskHotspotAnalysisResult);
343351
}
344352

345353
/// <summary>
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Palmmedia.ReportGenerator.Core.CodeAnalysis;
4+
using Palmmedia.ReportGenerator.Core.Properties;
5+
6+
namespace Palmmedia.ReportGenerator.Core
7+
{
8+
/// <summary>
9+
/// Validates the risk hotspots thresholds.
10+
/// </summary>
11+
public class MaxiumRiskhotspotsThresholdsValidator
12+
{
13+
/// <summary>
14+
/// The minimum coverage thresholds.
15+
/// </summary>
16+
private readonly RiskHotspotsAnalysisThresholds riskHotspotsAnalysisThresholds;
17+
18+
/// <summary>
19+
/// Initializes a new instance of the <see cref="MaxiumRiskhotspotsThresholdsValidator" /> class.
20+
/// </summary>
21+
/// <param name="riskHotspotsAnalysisThresholds">The maximum risk hotspots thresholds.</param>
22+
public MaxiumRiskhotspotsThresholdsValidator(RiskHotspotsAnalysisThresholds riskHotspotsAnalysisThresholds)
23+
{
24+
if (riskHotspotsAnalysisThresholds == null)
25+
{
26+
throw new ArgumentNullException(nameof(riskHotspotsAnalysisThresholds));
27+
}
28+
29+
this.riskHotspotsAnalysisThresholds = riskHotspotsAnalysisThresholds;
30+
}
31+
32+
/// <summary>
33+
/// Validates the risk hotspots thresholds.
34+
/// </summary>
35+
/// <param name="riskHotspotAnalysisResult">The risk hotspots analysis.</param>
36+
public void Validate(RiskHotspotAnalysisResult riskHotspotAnalysisResult)
37+
{
38+
var thresholdsByMetricName = new Dictionary<string, decimal>();
39+
40+
if (this.riskHotspotsAnalysisThresholds.MaximumThresholdForCyclomaticComplexity.HasValue)
41+
{
42+
thresholdsByMetricName.Add(ReportResources.CyclomaticComplexity, this.riskHotspotsAnalysisThresholds.MaximumThresholdForCyclomaticComplexity.Value);
43+
}
44+
45+
if (this.riskHotspotsAnalysisThresholds.MaximumThresholdForCrapScore.HasValue)
46+
{
47+
thresholdsByMetricName.Add(ReportResources.CrapScore, this.riskHotspotsAnalysisThresholds.MaximumThresholdForCrapScore.Value);
48+
}
49+
50+
if (this.riskHotspotsAnalysisThresholds.MaximumThresholdForNPathComplexity.HasValue)
51+
{
52+
thresholdsByMetricName.Add(ReportResources.NPathComplexity, this.riskHotspotsAnalysisThresholds.MaximumThresholdForNPathComplexity.Value);
53+
}
54+
55+
if (thresholdsByMetricName.Count == 0)
56+
{
57+
return;
58+
}
59+
60+
var errors = new List<string>();
61+
62+
foreach (var riskHotspot in riskHotspotAnalysisResult.RiskHotspots)
63+
{
64+
foreach (var statusMetric in riskHotspot.StatusMetrics)
65+
{
66+
if (!thresholdsByMetricName.TryGetValue(statusMetric.Metric.Name, out var threshold))
67+
{
68+
continue;
69+
}
70+
71+
if (statusMetric.Metric.Value > threshold)
72+
{
73+
errors.Add(string.Format(
74+
Resources.ErrorRiskHotspot,
75+
statusMetric.Metric.Value,
76+
riskHotspot.Assembly.Name,
77+
riskHotspot.Class.Name,
78+
riskHotspot.MethodMetric.FullName,
79+
statusMetric.Metric.Name,
80+
threshold));
81+
}
82+
}
83+
}
84+
85+
if (errors.Count > 0)
86+
{
87+
throw new RiskhotspotThresholdException(string.Join("\r\n", errors));
88+
}
89+
}
90+
}
91+
}

src/ReportGenerator.Core/MinimumCoverageThresholdsValidator.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ public void Validate(ParserResult parserResult)
3838
{
3939
if (!this.minimumCoverageThresholds.LineCoverage.HasValue
4040
&& !this.minimumCoverageThresholds.BranchCoverage.HasValue
41-
&& !this.minimumCoverageThresholds.MethodCoverage.HasValue)
41+
&& !this.minimumCoverageThresholds.MethodCoverage.HasValue
42+
&& !this.minimumCoverageThresholds.FullMethodCoverage.HasValue)
4243
{
4344
return;
4445
}

src/ReportGenerator.Core/Properties/Resources.Designer.cs

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/ReportGenerator.Core/Properties/Resources.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,4 +346,7 @@
346346
<data name="ErrorLowFullMethodCoverage" xml:space="preserve">
347347
<value>The full method coverage of {0}% is below the minimum threshold of {1}%.</value>
348348
</data>
349+
<data name="ErrorRiskHotspot" xml:space="preserve">
350+
<value>The risk hotspot value {0} for assembly '{1}' class '{2}' method '{3}' metric '{4}' is greater than the maximum threshold of {5}.</value>
351+
</data>
349352
</root>
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using System;
2+
3+
namespace Palmmedia.ReportGenerator.Core
4+
{
5+
/// <summary>
6+
/// Exception indicating that mimimum coverage goals are not satisfied.
7+
/// </summary>
8+
[Serializable]
9+
internal class RiskhotspotThresholdException : Exception
10+
{
11+
/// <summary>
12+
/// Initializes a new instance of the <see cref="RiskhotspotThresholdException"/> class.
13+
/// </summary>
14+
public RiskhotspotThresholdException()
15+
{
16+
}
17+
18+
/// <summary>
19+
/// Initializes a new instance of the <see cref="RiskhotspotThresholdException"/> class.
20+
/// </summary>
21+
/// <param name="message">The message.</param>
22+
public RiskhotspotThresholdException(string message)
23+
: base(message)
24+
{
25+
}
26+
27+
/// <summary>
28+
/// Initializes a new instance of the <see cref="RiskhotspotThresholdException"/> class.
29+
/// </summary>
30+
/// <param name="message">The message.</param>
31+
/// <param name="inner">The inner exception.</param>
32+
public RiskhotspotThresholdException(string message, Exception inner)
33+
: base(message, inner)
34+
{
35+
}
36+
}
37+
}

src/ReportGenerator.Core/appsettings.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22
"riskHotspotsAnalysisThresholds": {
33
"metricThresholdForCyclomaticComplexity": 15,
44
"metricThresholdForCrapScore": 30,
5-
"metricThresholdForNPathComplexity": 200
5+
"metricThresholdForNPathComplexity": 200,
6+
"maximumThresholdForCyclomaticComplexity": null,
7+
"maximumThresholdForCrapScore": null,
8+
"maximumThresholdForNPathComplexity": null
69
},
710
"minimumCoverageThresholds": {
811
"lineCoverage": null,

0 commit comments

Comments
 (0)