Skip to content
Merged

fix #162

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 51 additions & 5 deletions FineCodeCoverage/Core/ReportGenerator/ReportGeneratorUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Text;
using System.Threading.Tasks;
using FineCodeCoverage.Core.Utilities;
using FineCodeCoverage.Options;
using Fizzler.Systems.HtmlAgilityPack;
using HtmlAgilityPack;
using Newtonsoft.Json;
Expand Down Expand Up @@ -38,7 +39,8 @@ internal partial class ReportGeneratorUtil: IReportGeneratorUtil
private readonly IToolFolder toolFolder;
private readonly IToolZipProvider toolZipProvider;
private readonly IFileUtil fileUtil;
private const string zipPrefix = "reportGenerator";
private readonly IAppOptionsProvider appOptionsProvider;
private const string zipPrefix = "reportGenerator";
private const string zipDirectoryName = "reportGenerator";

public string ReportGeneratorExePath { get; private set; }
Expand All @@ -50,10 +52,12 @@ public ReportGeneratorUtil(
ILogger logger,
IToolFolder toolFolder,
IToolZipProvider toolZipProvider,
IFileUtil fileUtil
IFileUtil fileUtil,
IAppOptionsProvider appOptionsProvider
)
{
this.fileUtil = fileUtil;
this.appOptionsProvider = appOptionsProvider;
this.assemblyUtil = assemblyUtil;
this.processUtil = processUtil;
this.logger = logger;
Expand Down Expand Up @@ -87,12 +91,22 @@ async Task<bool> run(string outputReportType, string inputReports)
{
reportTypeSettings.Add($@"""-reports:{inputReports}""");
reportTypeSettings.Add($@"""-reporttypes:Cobertura""");

}
else if (outputReportType.Equals("HtmlInline_AzurePipelines", StringComparison.OrdinalIgnoreCase))
{
reportTypeSettings.Add($@"""-reports:{inputReports}""");
reportTypeSettings.Add($@"""-plugins:{typeof(FccLightReportBuilder).Assembly.Location}""");
reportTypeSettings.Add($@"""-reporttypes:{(darkMode ? FccDarkReportBuilder.REPORT_TYPE : FccLightReportBuilder.REPORT_TYPE)}""");
var options = appOptionsProvider.Get();
var cyclomaticThreshold = options.ThresholdForCyclomaticComplexity;
var crapScoreThreshold = options.ThresholdForCrapScore;
var nPathThreshold = options.ThresholdForNPathComplexity;

reportTypeSettings.Add($@"""riskHotspotsAnalysisThresholds:metricThresholdForCyclomaticComplexity={cyclomaticThreshold}""");
reportTypeSettings.Add($@"""riskHotspotsAnalysisThresholds:metricThresholdForCrapScore={crapScoreThreshold}""");
reportTypeSettings.Add($@"""riskHotspotsAnalysisThresholds:metricThresholdForNPathComplexity={nPathThreshold}""");

}
else
{
Expand Down Expand Up @@ -407,14 +421,16 @@ public string ProcessUnifiedHtml(string htmlForProcessing, string reportOutputFo
{ button: 'btnRiskHotspots', content: 'risk-hotspots' },
];

var riskHotspotsTable;
var riskHotspotsElement;
var addedFileIndexToRiskHotspots = false;
var addFileIndexToRiskHotspotsClassLink = function(){
if(!addedFileIndexToRiskHotspots){
addedFileIndexToRiskHotspots = true;
var riskHotspotsElements = document.getElementsByTagName('risk-hotspots');
if(riskHotspotsElements.length == 1){{
var riskHotspotsElement = riskHotspotsElements[0];
var riskHotspotsTable = riskHotspotsElement.querySelector('table');
riskHotspotsElement = riskHotspotsElements[0];
riskHotspotsTable = riskHotspotsElement.querySelector('table');
if(riskHotspotsTable){
var rhBody = riskHotspotsTable.querySelector('tbody');
var rows = rhBody.rows;
Expand All @@ -439,10 +455,40 @@ public string ProcessUnifiedHtml(string htmlForProcessing, string reportOutputFo
}}
}
}

// necessary for WebBrowser
function removeElement(element){
element.parentNode.removeChild(element);
}

function insertAfter(newNode, existingNode) {
existingNode.parentNode.insertBefore(newNode, existingNode.nextSibling);
}

var noHotspotsMessage
var addNoRiskHotspotsMessageIfRequired = function(){
if(riskHotspotsTable == null){
noHotspotsMessage = document.createElement(""p"");
noHotspotsMessage.style.margin = ""0"";
noHotspotsMessage.innerText = ""No risk hotspots found."";

insertAfter(noHotspotsMessage, riskHotspotsElement);
}
}

var removeNoRiskHotspotsMessage = function(){
if(noHotspotsMessage){
removeElement(noHotspotsMessage);
noHotspotsMessage = null;
}
}

var openTab = function (tabIndex) {
var openTab = function (tabIndex) {
if(tabIndex==2){{
addFileIndexToRiskHotspotsClassLink();
addNoRiskHotspotsMessageIfRequired();
}}else{{
removeNoRiskHotspotsMessage();
}}
for (var i = 0; i < tabs.length; i++) {

Expand Down
13 changes: 13 additions & 0 deletions FineCodeCoverage/Options/AppOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ internal class AppOptions : DialogPage, IAppOptions
private const string coverletCategory = "Coverlet";
private const string openCoverCategory = "OpenCover";
private const string outputCategory = "Output";
private const string reportCategory = "Report";

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

[Category(reportCategory)]
[Description("When cyclomatic complexity exceeds this value for a method then the method will be present in the risk hotspots tab.")]
public int ThresholdForCyclomaticComplexity { get; set; } = 30;

[Category(reportCategory)]
[Description("When npath complexity exceeds this value for a method then the method will be present in the risk hotspots tab. OpenCover only")]
public int ThresholdForNPathComplexity { get; set; } = 200;

[Category(reportCategory)]
[Description("When crap score exceeds this value for a method then the method will be present in the risk hotspots tab. OpenCover only")]
public int ThresholdForCrapScore { get; set; } = 15;

[SuppressMessage("Usage", "VSTHRD010:Invoke single-threaded types on Main thread")]
public override void SaveSettingsToStorage()
{
Expand Down
3 changes: 3 additions & 0 deletions FineCodeCoverage/Options/IAppOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,8 @@ public interface IAppOptions
string CoverletCollectorDirectoryPath { get; }
string OpenCoverCustomPath { get; }
string FCCSolutionOutputDirectoryName { get; }
int ThresholdForCyclomaticComplexity { get; }
int ThresholdForNPathComplexity { get; }
int ThresholdForCrapScore { get; }
}
}
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@ ExcludeByFile Glob patterns specifying source files to exclude e.g. **/Migra
ExcludeByAttribute Attributes to exclude from code coverage (multiple values)
IncludeTestAssembly Specifies whether to report code coverage of the test assembly

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.
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.
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.

RunSettingsOnly Specify false for global and project options to be used for coverlet data collector configuration elements when not specified in runsettings
CoverletCollectorDirectoryPath Specify path to directory containing coverlet collector files if you need functionality that the FCC version does not provide.

Expand All @@ -138,6 +142,8 @@ You can ignore a method or an entire class from code coverage by creating and ap
You can also ignore additional attributes by adding to the 'ExcludeByAttributes' list (short name or full name supported) e.g. :
[GeneratedCode] => Present in System.CodeDom.Compiler namespace
[MyCustomExcludeFromCodeCoverage] => Any custom attribute that you may define


```

#### Filter Expressions
Expand Down