Skip to content

Commit 10f3c1f

Browse files
authored
Merge pull request #162 from tonyhallett/fix-missing-msg-no-hotspots
fix #161 #160
2 parents 4f1e0ac + 51c2ecf commit 10f3c1f

File tree

4 files changed

+73
-5
lines changed

4 files changed

+73
-5
lines changed

FineCodeCoverage/Core/ReportGenerator/ReportGeneratorUtil.cs

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Text;
77
using System.Threading.Tasks;
88
using FineCodeCoverage.Core.Utilities;
9+
using FineCodeCoverage.Options;
910
using Fizzler.Systems.HtmlAgilityPack;
1011
using HtmlAgilityPack;
1112
using Newtonsoft.Json;
@@ -38,7 +39,8 @@ internal partial class ReportGeneratorUtil: IReportGeneratorUtil
3839
private readonly IToolFolder toolFolder;
3940
private readonly IToolZipProvider toolZipProvider;
4041
private readonly IFileUtil fileUtil;
41-
private const string zipPrefix = "reportGenerator";
42+
private readonly IAppOptionsProvider appOptionsProvider;
43+
private const string zipPrefix = "reportGenerator";
4244
private const string zipDirectoryName = "reportGenerator";
4345

4446
public string ReportGeneratorExePath { get; private set; }
@@ -50,10 +52,12 @@ public ReportGeneratorUtil(
5052
ILogger logger,
5153
IToolFolder toolFolder,
5254
IToolZipProvider toolZipProvider,
53-
IFileUtil fileUtil
55+
IFileUtil fileUtil,
56+
IAppOptionsProvider appOptionsProvider
5457
)
5558
{
5659
this.fileUtil = fileUtil;
60+
this.appOptionsProvider = appOptionsProvider;
5761
this.assemblyUtil = assemblyUtil;
5862
this.processUtil = processUtil;
5963
this.logger = logger;
@@ -87,12 +91,22 @@ async Task<bool> run(string outputReportType, string inputReports)
8791
{
8892
reportTypeSettings.Add($@"""-reports:{inputReports}""");
8993
reportTypeSettings.Add($@"""-reporttypes:Cobertura""");
94+
9095
}
9196
else if (outputReportType.Equals("HtmlInline_AzurePipelines", StringComparison.OrdinalIgnoreCase))
9297
{
9398
reportTypeSettings.Add($@"""-reports:{inputReports}""");
9499
reportTypeSettings.Add($@"""-plugins:{typeof(FccLightReportBuilder).Assembly.Location}""");
95100
reportTypeSettings.Add($@"""-reporttypes:{(darkMode ? FccDarkReportBuilder.REPORT_TYPE : FccLightReportBuilder.REPORT_TYPE)}""");
101+
var options = appOptionsProvider.Get();
102+
var cyclomaticThreshold = options.ThresholdForCyclomaticComplexity;
103+
var crapScoreThreshold = options.ThresholdForCrapScore;
104+
var nPathThreshold = options.ThresholdForNPathComplexity;
105+
106+
reportTypeSettings.Add($@"""riskHotspotsAnalysisThresholds:metricThresholdForCyclomaticComplexity={cyclomaticThreshold}""");
107+
reportTypeSettings.Add($@"""riskHotspotsAnalysisThresholds:metricThresholdForCrapScore={crapScoreThreshold}""");
108+
reportTypeSettings.Add($@"""riskHotspotsAnalysisThresholds:metricThresholdForNPathComplexity={nPathThreshold}""");
109+
96110
}
97111
else
98112
{
@@ -407,14 +421,16 @@ public string ProcessUnifiedHtml(string htmlForProcessing, string reportOutputFo
407421
{ button: 'btnRiskHotspots', content: 'risk-hotspots' },
408422
];
409423
424+
var riskHotspotsTable;
425+
var riskHotspotsElement;
410426
var addedFileIndexToRiskHotspots = false;
411427
var addFileIndexToRiskHotspotsClassLink = function(){
412428
if(!addedFileIndexToRiskHotspots){
413429
addedFileIndexToRiskHotspots = true;
414430
var riskHotspotsElements = document.getElementsByTagName('risk-hotspots');
415431
if(riskHotspotsElements.length == 1){{
416-
var riskHotspotsElement = riskHotspotsElements[0];
417-
var riskHotspotsTable = riskHotspotsElement.querySelector('table');
432+
riskHotspotsElement = riskHotspotsElements[0];
433+
riskHotspotsTable = riskHotspotsElement.querySelector('table');
418434
if(riskHotspotsTable){
419435
var rhBody = riskHotspotsTable.querySelector('tbody');
420436
var rows = rhBody.rows;
@@ -439,10 +455,40 @@ public string ProcessUnifiedHtml(string htmlForProcessing, string reportOutputFo
439455
}}
440456
}
441457
}
458+
459+
// necessary for WebBrowser
460+
function removeElement(element){
461+
element.parentNode.removeChild(element);
462+
}
463+
464+
function insertAfter(newNode, existingNode) {
465+
existingNode.parentNode.insertBefore(newNode, existingNode.nextSibling);
466+
}
467+
468+
var noHotspotsMessage
469+
var addNoRiskHotspotsMessageIfRequired = function(){
470+
if(riskHotspotsTable == null){
471+
noHotspotsMessage = document.createElement(""p"");
472+
noHotspotsMessage.style.margin = ""0"";
473+
noHotspotsMessage.innerText = ""No risk hotspots found."";
474+
475+
insertAfter(noHotspotsMessage, riskHotspotsElement);
476+
}
477+
}
478+
479+
var removeNoRiskHotspotsMessage = function(){
480+
if(noHotspotsMessage){
481+
removeElement(noHotspotsMessage);
482+
noHotspotsMessage = null;
483+
}
484+
}
442485
443-
var openTab = function (tabIndex) {
486+
var openTab = function (tabIndex) {
444487
if(tabIndex==2){{
445488
addFileIndexToRiskHotspotsClassLink();
489+
addNoRiskHotspotsMessageIfRequired();
490+
}}else{{
491+
removeNoRiskHotspotsMessage();
446492
}}
447493
for (var i = 0; i < tabs.length; i++) {
448494

FineCodeCoverage/Options/AppOptions.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ internal class AppOptions : DialogPage, IAppOptions
1313
private const string coverletCategory = "Coverlet";
1414
private const string openCoverCategory = "OpenCover";
1515
private const string outputCategory = "Output";
16+
private const string reportCategory = "Report";
1617

1718
public AppOptions():this(false)
1819
{
@@ -137,6 +138,18 @@ You can also ignore additional attributes by adding to this list (short name or
137138
[Category(outputCategory)]
138139
public string FCCSolutionOutputDirectoryName { get; set; }
139140

141+
[Category(reportCategory)]
142+
[Description("When cyclomatic complexity exceeds this value for a method then the method will be present in the risk hotspots tab.")]
143+
public int ThresholdForCyclomaticComplexity { get; set; } = 30;
144+
145+
[Category(reportCategory)]
146+
[Description("When npath complexity exceeds this value for a method then the method will be present in the risk hotspots tab. OpenCover only")]
147+
public int ThresholdForNPathComplexity { get; set; } = 200;
148+
149+
[Category(reportCategory)]
150+
[Description("When crap score exceeds this value for a method then the method will be present in the risk hotspots tab. OpenCover only")]
151+
public int ThresholdForCrapScore { get; set; } = 15;
152+
140153
[SuppressMessage("Usage", "VSTHRD010:Invoke single-threaded types on Main thread")]
141154
public override void SaveSettingsToStorage()
142155
{

FineCodeCoverage/Options/IAppOptions.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,8 @@ public interface IAppOptions
1818
string CoverletCollectorDirectoryPath { get; }
1919
string OpenCoverCustomPath { get; }
2020
string FCCSolutionOutputDirectoryName { get; }
21+
int ThresholdForCyclomaticComplexity { get; }
22+
int ThresholdForNPathComplexity { get; }
23+
int ThresholdForCrapScore { get; }
2124
}
2225
}

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,10 @@ ExcludeByFile Glob patterns specifying source files to exclude e.g. **/Migra
121121
ExcludeByAttribute Attributes to exclude from code coverage (multiple values)
122122
IncludeTestAssembly Specifies whether to report code coverage of the test assembly
123123
124+
ThresholdForCyclomaticComplexity When [cyclomatic complexity](https://en.wikipedia.org/wiki/Cyclomatic_complexity) exceeds this value for a method then the method will be present in the risk hotspots tab.
125+
ThresholdForNPathComplexity When [npath complexity](https://en.wikipedia.org/wiki/Cyclomatic_complexity) exceeds this value for a method then the method will be present in the risk hotspots tab. OpenCover only.
126+
ThresholdForCrapScore When [crap score](https://testing.googleblog.com/2011/02/this-code-is-crap.html) exceeds this value for a method then the method will be present in the risk hotspots tab. OpenCover only.
127+
124128
RunSettingsOnly Specify false for global and project options to be used for coverlet data collector configuration elements when not specified in runsettings
125129
CoverletCollectorDirectoryPath Specify path to directory containing coverlet collector files if you need functionality that the FCC version does not provide.
126130
@@ -138,6 +142,8 @@ You can ignore a method or an entire class from code coverage by creating and ap
138142
You can also ignore additional attributes by adding to the 'ExcludeByAttributes' list (short name or full name supported) e.g. :
139143
[GeneratedCode] => Present in System.CodeDom.Compiler namespace
140144
[MyCustomExcludeFromCodeCoverage] => Any custom attribute that you may define
145+
146+
141147
```
142148

143149
#### Filter Expressions

0 commit comments

Comments
 (0)