@@ -31,24 +31,24 @@ internal class ReportGeneratorResult
31
31
}
32
32
33
33
[ Export ( typeof ( IReportGeneratorUtil ) ) ]
34
- internal partial class ReportGeneratorUtil : IReportGeneratorUtil
34
+ internal partial class ReportGeneratorUtil : IReportGeneratorUtil
35
35
{
36
- private readonly IAssemblyUtil assemblyUtil ;
37
- private readonly IProcessUtil processUtil ;
38
- private readonly ILogger logger ;
39
- private readonly IToolFolder toolFolder ;
40
- private readonly IToolZipProvider toolZipProvider ;
36
+ private readonly IAssemblyUtil assemblyUtil ;
37
+ private readonly IProcessUtil processUtil ;
38
+ private readonly ILogger logger ;
39
+ private readonly IToolFolder toolFolder ;
40
+ private readonly IToolZipProvider toolZipProvider ;
41
41
private readonly IFileUtil fileUtil ;
42
- private readonly IAppOptionsProvider appOptionsProvider ;
43
- private const string zipPrefix = "reportGenerator" ;
42
+ private readonly IAppOptionsProvider appOptionsProvider ;
43
+ private const string zipPrefix = "reportGenerator" ;
44
44
private const string zipDirectoryName = "reportGenerator" ;
45
45
46
- public string ReportGeneratorExePath { get ; private set ; }
46
+ public string ReportGeneratorExePath { get ; private set ; }
47
47
48
48
[ ImportingConstructor ]
49
49
public ReportGeneratorUtil (
50
50
IAssemblyUtil assemblyUtil ,
51
- IProcessUtil processUtil ,
51
+ IProcessUtil processUtil ,
52
52
ILogger logger ,
53
53
IToolFolder toolFolder ,
54
54
IToolZipProvider toolZipProvider ,
@@ -57,13 +57,13 @@ IAppOptionsProvider appOptionsProvider
57
57
)
58
58
{
59
59
this . fileUtil = fileUtil ;
60
- this . appOptionsProvider = appOptionsProvider ;
61
- this . assemblyUtil = assemblyUtil ;
62
- this . processUtil = processUtil ;
63
- this . logger = logger ;
64
- this . toolFolder = toolFolder ;
65
- this . toolZipProvider = toolZipProvider ;
66
- }
60
+ this . appOptionsProvider = appOptionsProvider ;
61
+ this . assemblyUtil = assemblyUtil ;
62
+ this . processUtil = processUtil ;
63
+ this . logger = logger ;
64
+ this . toolFolder = toolFolder ;
65
+ this . toolZipProvider = toolZipProvider ;
66
+ }
67
67
68
68
public void Initialize ( string appDataFolder )
69
69
{
@@ -72,7 +72,7 @@ public void Initialize(string appDataFolder)
72
72
?? Directory . GetFiles ( zipDestination , "*reportGenerator*.exe" , SearchOption . AllDirectories ) . FirstOrDefault ( ) ;
73
73
}
74
74
75
- public async Task < ReportGeneratorResult > GenerateAsync ( IEnumerable < string > coverOutputFiles , string reportOutputFolder , bool darkMode , bool throwError = false )
75
+ public async Task < ReportGeneratorResult > GenerateAsync ( IEnumerable < string > coverOutputFiles , string reportOutputFolder , bool darkMode , bool throwError = false )
76
76
{
77
77
var title = "ReportGenerator Run" ;
78
78
@@ -82,7 +82,7 @@ public async Task<ReportGeneratorResult> GenerateAsync(IEnumerable<string> cover
82
82
var reportGeneratorSettings = new List < string > ( ) ;
83
83
84
84
reportGeneratorSettings . Add ( $@ """-targetdir:{ reportOutputFolder } """);
85
-
85
+
86
86
async Task<bool> run(string outputReportType, string inputReports)
87
87
{
88
88
var reportTypeSettings = reportGeneratorSettings. ToArray( ) . ToList( ) ;
@@ -91,17 +91,14 @@ async Task<bool> run(string outputReportType, string inputReports)
91
91
{
92
92
reportTypeSettings . Add ( $@ """-reports:{ inputReports } """);
93
93
reportTypeSettings.Add($@""" - reporttypes : Cobertura""");
94
-
94
+
95
95
}
96
96
else if (outputReportType.Equals("HtmlInline_AzurePipelines", StringComparison.OrdinalIgnoreCase))
97
97
{
98
98
reportTypeSettings.Add($@""" - reports : { inputReports } """);
99
99
reportTypeSettings.Add($@""" - plugins : { typeof ( FccLightReportBuilder ) . Assembly . Location } """);
100
100
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;
101
+ var (cyclomaticThreshold, crapScoreThreshold, nPathThreshold) = HotspotThresholds();
105
102
106
103
reportTypeSettings.Add($@""" riskHotspotsAnalysisThresholds: metricThresholdForCyclomaticComplexity = { cyclomaticThreshold } """);
107
104
reportTypeSettings.Add($@""" riskHotspotsAnalysisThresholds: metricThresholdForCrapScore = { crapScoreThreshold } """);
@@ -122,10 +119,10 @@ async Task<bool> run(string outputReportType, string inputReports)
122
119
Arguments = string.Join(" ", reportTypeSettings),
123
120
WorkingDirectory = reportOutputFolder
124
121
});
125
-
126
122
127
- if(result != null)
128
- {
123
+
124
+ if (result != null)
125
+ {
129
126
if (result.ExitCode != 0)
130
127
{
131
128
logger.Log($"{title} [reporttype:{outputReportType}] Error", result.Output);
@@ -143,11 +140,11 @@ async Task<bool> run(string outputReportType, string inputReports)
143
140
return true;
144
141
}
145
142
return false;
146
-
143
+
147
144
}
148
-
145
+
149
146
var reportGeneratorResult = new ReportGeneratorResult { Success = false, UnifiedHtml = null, UnifiedXmlFile = unifiedXmlFile };
150
-
147
+
151
148
var coberturaResult = await run("Cobertura", string.Join(";", coverOutputFiles));
152
149
153
150
if (coberturaResult)
@@ -157,18 +154,23 @@ async Task<bool> run(string outputReportType, string inputReports)
157
154
{
158
155
reportGeneratorResult.UnifiedHtml = fileUtil.ReadAllText(unifiedHtmlFile);
159
156
reportGeneratorResult.Success = true;
160
- }
161
-
157
+ }
158
+
162
159
}
163
160
164
161
return reportGeneratorResult;
165
-
162
+
166
163
}
167
164
168
165
public string ProcessUnifiedHtml(string htmlForProcessing, string reportOutputFolder, bool darkMode)
169
166
{
170
167
return assemblyUtil.RunInAssemblyResolvingContext(() =>
171
168
{
169
+ var (cyclomaticThreshold, crapScoreThreshold, nPathThreshold) = HotspotThresholds();
170
+ var noRiskHotspotsHeader = "No risk hotspots that exceed options :";
171
+ var noRiskHotspotsCyclomaticMsg = $"Cyclomatic complexity : {cyclomaticThreshold}";
172
+ var noRiskHotspotsNpathMsg =$"NPath complexity : {nPathThreshold}";
173
+ var noRiskHotspotsCrapMessage = $"Crap score : {crapScoreThreshold}";
172
174
var doc = new HtmlDocument();
173
175
174
176
doc.OptionFixNestedTags = true;
@@ -181,7 +183,7 @@ public string ProcessUnifiedHtml(string htmlForProcessing, string reportOutputFo
181
183
doc.DocumentNode.QuerySelectorAll(".container").ToList().ForEach(x => x.SetAttributeValue("style", "margin:0;padding:0;border:0"));
182
184
doc.DocumentNode.QuerySelectorAll(".containerleft").ToList().ForEach(x => x.SetAttributeValue("style", "margin:0;padding:0;border:0"));
183
185
doc.DocumentNode.QuerySelectorAll(".containerleft > h1 , .containerleft > p").ToList().ForEach(x => x.SetAttributeValue("style", "display:none"));
184
-
186
+
185
187
// DOM changes
186
188
187
189
var table = doc.DocumentNode.QuerySelectorAll("table.overview").First();
@@ -242,29 +244,29 @@ public string ProcessUnifiedHtml(string htmlForProcessing, string reportOutputFo
242
244
var assembliesReplaced = assemblies.ToString();
243
245
htmlSb.Replace(assembliesToReplace, assembliesReplaced);
244
246
245
- //is this even present if there are no riskhotspots
246
- var riskHotspotsSearch = "var riskHotspots = [";
247
- var rhStartIndex = outerHtml.IndexOf(riskHotspotsSearch) + riskHotspotsSearch.Length - 1;
248
- var rhEndIndex = outerHtml.IndexOf("var branchCoverageAvailable");
249
- var rhToReplace = outerHtml.Substring(rhStartIndex, rhEndIndex - rhStartIndex);
250
- rhEndIndex = rhToReplace.LastIndexOf(']');
251
- rhToReplace = rhToReplace.Substring(0, rhEndIndex + 1);
252
-
253
- var riskHotspots = JArray.Parse(rhToReplace);
254
- foreach (JObject riskHotspot in riskHotspots)
255
- {
256
- var assembly = riskHotspot["assembly"].ToString();
257
- var qualifiedClassName = riskHotspot["class"].ToString();
247
+ //is this even present if there are no riskhotspots
248
+ var riskHotspotsSearch = "var riskHotspots = [";
249
+ var rhStartIndex = outerHtml.IndexOf(riskHotspotsSearch) + riskHotspotsSearch.Length - 1;
250
+ var rhEndIndex = outerHtml.IndexOf("var branchCoverageAvailable");
251
+ var rhToReplace = outerHtml.Substring(rhStartIndex, rhEndIndex - rhStartIndex);
252
+ rhEndIndex = rhToReplace.LastIndexOf(']');
253
+ rhToReplace = rhToReplace.Substring(0, rhEndIndex + 1);
254
+
255
+ var riskHotspots = JArray.Parse(rhToReplace);
256
+ foreach (JObject riskHotspot in riskHotspots)
257
+ {
258
+ var assembly = riskHotspot["assembly"].ToString();
259
+ var qualifiedClassName = riskHotspot["class"].ToString();
258
260
// simplify name
259
261
var lastIndexOfDotInName = qualifiedClassName.LastIndexOf('.');
260
262
if (lastIndexOfDotInName != -1) riskHotspot["class"] = qualifiedClassName.Substring(lastIndexOfDotInName).Trim('.');
261
263
var newReportPath = $"#{assembly}{assemblyClassDelimiter}{qualifiedClassName}.html";
262
- riskHotspot["reportPath"] = newReportPath;
263
- }
264
- var riskHotspotsReplaced = riskHotspots.ToString();
265
- htmlSb.Replace(rhToReplace, riskHotspotsReplaced);
264
+ riskHotspot["reportPath"] = newReportPath;
265
+ }
266
+ var riskHotspotsReplaced = riskHotspots.ToString();
267
+ htmlSb.Replace(rhToReplace, riskHotspotsReplaced);
266
268
267
- htmlSb.Replace(".table-fixed", ".table-fixed-ignore-me");
269
+ htmlSb.Replace(".table-fixed", ".table-fixed-ignore-me");
268
270
269
271
htmlSb.Replace("</head>", $@"
270
272
<style type=""text/css"">
@@ -391,23 +393,23 @@ public string ProcessUnifiedHtml(string htmlForProcessing, string reportOutputFo
391
393
");
392
394
}
393
395
394
- htmlSb.Replace("<body>", @"
396
+ htmlSb.Replace("<body>", $ @"
395
397
<body oncontextmenu='return false;'>
396
398
<style>
397
399
398
- table#headerTabs td {
400
+ table#headerTabs td {{
399
401
border-width:3px;
400
402
padding: 3px;
401
403
padding-left: 7px;
402
404
padding-right: 7px;
403
- }
404
- table#headerTabs td.tab {
405
+ }}
406
+ table#headerTabs td.tab {{
405
407
cursor: pointer;
406
- }
407
- table#headerTabs td.active {
408
+ }}
409
+ table#headerTabs td.active {{
408
410
border-bottom: 3px solid transparent;
409
411
font-weight: bolder;
410
- }
412
+ }}
411
413
412
414
</style>
413
415
<script>
@@ -416,25 +418,25 @@ public string ProcessUnifiedHtml(string htmlForProcessing, string reportOutputFo
416
418
body.style['padding-top'] = '50px';
417
419
418
420
var tabs = [
419
- { button: 'btnCoverage', content: 'coverage-info' },
420
- { button: 'btnSummary', content: 'table-fixed' },
421
- { button: 'btnRiskHotspots', content: 'risk-hotspots' },
421
+ {{ button: 'btnCoverage', content: 'coverage-info' } },
422
+ {{ button: 'btnSummary', content: 'table-fixed' } },
423
+ {{ button: 'btnRiskHotspots', content: 'risk-hotspots' } },
422
424
];
423
425
424
426
var riskHotspotsTable;
425
427
var riskHotspotsElement;
426
428
var addedFileIndexToRiskHotspots = false;
427
- var addFileIndexToRiskHotspotsClassLink = function(){
428
- if(!addedFileIndexToRiskHotspots){
429
+ var addFileIndexToRiskHotspotsClassLink = function(){{
430
+ if(!addedFileIndexToRiskHotspots){{
429
431
addedFileIndexToRiskHotspots = true;
430
432
var riskHotspotsElements = document.getElementsByTagName('risk-hotspots');
431
433
if(riskHotspotsElements.length == 1){{
432
434
riskHotspotsElement = riskHotspotsElements[0];
433
435
riskHotspotsTable = riskHotspotsElement.querySelector('table');
434
- if(riskHotspotsTable){
436
+ if(riskHotspotsTable){{
435
437
var rhBody = riskHotspotsTable.querySelector('tbody');
436
438
var rows = rhBody.rows;
437
- for(var i=0;i<rows.length;i++){
439
+ for(var i=0;i<rows.length;i++){{
438
440
var row = rows[i];
439
441
var cells = row.cells;
440
442
var classCell = cells[1];
@@ -449,48 +451,52 @@ public string ProcessUnifiedHtml(string htmlForProcessing, string reportOutputFo
449
451
var file = fileAndLine[0];
450
452
var line = fileAndLine[1];
451
453
classLink.href = classLink.hash + '#file' + file + '_line0';
452
- }
453
- }
454
+ }}
455
+ }}
454
456
455
457
}}
456
- }
457
- }
458
+ }}
459
+ }}
458
460
459
461
// necessary for WebBrowser
460
- function removeElement(element){
462
+ function removeElement(element){{
461
463
element.parentNode.removeChild(element);
462
- }
464
+ }}
463
465
464
- function insertAfter(newNode, existingNode) {
466
+ function insertAfter(newNode, existingNode) {{
465
467
existingNode.parentNode.insertBefore(newNode, existingNode.nextSibling);
466
- }
468
+ }}
467
469
468
470
var noHotspotsMessage
469
- var addNoRiskHotspotsMessageIfRequired = function(){
470
- if(riskHotspotsTable == null){
471
+ var addNoRiskHotspotsMessageIfRequired = function(){{
472
+ if(riskHotspotsTable == null){{
471
473
noHotspotsMessage = document.createElement(""p"");
472
474
noHotspotsMessage.style.margin = ""0"";
473
- noHotspotsMessage.innerText = ""No risk hotspots found."";
475
+ var header = ""{noRiskHotspotsHeader}"";
476
+ var cyclomaticMessage = ""{noRiskHotspotsCyclomaticMsg}"";
477
+ var crapMessage =""{noRiskHotspotsCrapMessage}"";
478
+ var nPathMessage = ""{noRiskHotspotsNpathMsg}"";
479
+ noHotspotsMessage.innerText = header + ""\n"" + cyclomaticMessage + ""\n"" + crapMessage + ""\n"" + nPathMessage;
474
480
475
481
insertAfter(noHotspotsMessage, riskHotspotsElement);
476
- }
477
- }
482
+ }}
483
+ }}
478
484
479
- var removeNoRiskHotspotsMessage = function(){
480
- if(noHotspotsMessage){
485
+ var removeNoRiskHotspotsMessage = function(){{
486
+ if(noHotspotsMessage){{
481
487
removeElement(noHotspotsMessage);
482
488
noHotspotsMessage = null;
483
- }
484
- }
489
+ }}
490
+ }}
485
491
486
- var openTab = function (tabIndex) {
492
+ var openTab = function (tabIndex) {{
487
493
if(tabIndex==2){{
488
494
addFileIndexToRiskHotspotsClassLink();
489
495
addNoRiskHotspotsMessageIfRequired();
490
496
}}else{{
491
497
removeNoRiskHotspotsMessage();
492
498
}}
493
- for (var i = 0; i < tabs.length; i++) {
499
+ for (var i = 0; i < tabs.length; i++) {{
494
500
495
501
var tab = tabs[i];
496
502
if (!tab) continue;
@@ -502,19 +508,19 @@ var noHotspotsMessage
502
508
if (!content) content = document.getElementsByClassName(tab.content)[0];
503
509
if (!content) continue;
504
510
505
- if (i == tabIndex) {
511
+ if (i == tabIndex) {{
506
512
if (button.className.indexOf('active') == -1) button.className += ' active';
507
513
content.style.display = 'block';
508
- } else {
514
+ }} else { {
509
515
button.className = button.className.replace('active', '');
510
516
content.style.display = 'none';
511
- }
512
- }
513
- };
517
+ }}
518
+ }}
519
+ }} ;
514
520
515
- window.addEventListener('load', function() {
521
+ window.addEventListener('load', function() {{
516
522
openTab(0);
517
- });
523
+ }} );
518
524
519
525
</script>
520
526
<div id='divHeader' style='border-collapse:collapse;padding:0;padding-top:3px;margin:0;border:0;position:fixed;top:0;left:0;width:100%;z-index:100' cellpadding='0' cellspacing='0'>
@@ -618,5 +624,16 @@ Risk Hotspots
618
624
619
625
} ) ;
620
626
}
627
+
628
+ private ( int cyclomaticThreshold , int crapScoreThreshold , int nPathThreshold ) HotspotThresholds ( )
629
+ {
630
+ var options = appOptionsProvider. Get( ) ;
631
+ return (
632
+ options . ThresholdForCyclomaticComplexity ,
633
+ options . ThresholdForCrapScore ,
634
+ options . ThresholdForNPathComplexity
635
+ ) ;
636
+
637
+ }
621
638
}
622
639
}
0 commit comments