/// <summary> /// Creates the summary report. /// </summary> /// <param name="reportRenderer">The report renderer.</param> /// <param name="summaryResult">The summary result.</param> public virtual void CreateSummaryReport(IReportRenderer reportRenderer, SummaryResult summaryResult) { if (reportRenderer == null) { throw new ArgumentNullException(nameof(reportRenderer)); } if (summaryResult == null) { throw new ArgumentNullException(nameof(summaryResult)); } string title = this.ReportContext.ReportConfiguration.Title != null ? $"{ReportResources.Summary} - {this.ReportContext.ReportConfiguration.Title}" : ReportResources.Summary; reportRenderer.BeginSummaryReport(this.ReportContext.ReportConfiguration.TargetDirectory, null, title); // reportRenderer.HeaderWithGithubLinks(title); reportRenderer.BeginKeyValueTable(); reportRenderer.KeyValueRow(ReportResources.GeneratedOn, DateTime.Now.ToShortDateString() + " - " + DateTime.Now.ToLongTimeString()); reportRenderer.KeyValueRow(ReportResources.Parser, summaryResult.UsedParser); reportRenderer.KeyValueRow(ReportResources.Assemblies2, summaryResult.Assemblies.Count().ToString(CultureInfo.InvariantCulture)); reportRenderer.KeyValueRow(ReportResources.Classes, summaryResult.Assemblies.SelectMany(a => a.Classes).Count().ToString(CultureInfo.InvariantCulture)); reportRenderer.KeyValueRow(ReportResources.Files2, summaryResult.Assemblies.SelectMany(a => a.Classes).SelectMany(a => a.Files).Distinct().Count().ToString(CultureInfo.InvariantCulture)); reportRenderer.KeyValueRow(ReportResources.CoveredLines, summaryResult.CoveredLines.ToString(CultureInfo.InvariantCulture)); reportRenderer.KeyValueRow(ReportResources.UncoveredLines, (summaryResult.CoverableLines - summaryResult.CoveredLines).ToString(CultureInfo.InvariantCulture)); reportRenderer.KeyValueRow(ReportResources.CoverableLines, summaryResult.CoverableLines.ToString(CultureInfo.InvariantCulture)); reportRenderer.KeyValueRow(ReportResources.TotalLines, summaryResult.TotalLines.GetValueOrDefault().ToString(CultureInfo.InvariantCulture)); reportRenderer.KeyValueRow(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); if (summaryResult.CoveredBranches.HasValue && summaryResult.TotalBranches.HasValue) { reportRenderer.KeyValueRow(ReportResources.CoveredBranches2, summaryResult.CoveredBranches.GetValueOrDefault().ToString(CultureInfo.InvariantCulture)); reportRenderer.KeyValueRow(ReportResources.TotalBranches, summaryResult.TotalBranches.GetValueOrDefault().ToString(CultureInfo.InvariantCulture)); decimal?branchCoverage = summaryResult.BranchCoverageQuota; if (branchCoverage.HasValue) { reportRenderer.KeyValueRow(ReportResources.BranchCoverage2, $"{branchCoverage.Value.ToString(CultureInfo.InvariantCulture)}% ({summaryResult.CoveredBranches.GetValueOrDefault().ToString(CultureInfo.InvariantCulture)} {ReportResources.Of} {summaryResult.TotalBranches.GetValueOrDefault().ToString(CultureInfo.InvariantCulture)})"); } } if (this.ReportContext.ReportConfiguration.Tag != null) { reportRenderer.KeyValueRow(ReportResources.Tag, this.ReportContext.ReportConfiguration.Tag); } reportRenderer.FinishTable(); if (reportRenderer.SupportsCharts) { var historicCoverages = this.GetOverallHistoricCoverages(this.ReportContext.OverallHistoricCoverages); if (historicCoverages.Any(h => h.CoverageQuota.HasValue || h.BranchCoverageQuota.HasValue)) { reportRenderer.Header(ReportResources.History); reportRenderer.Chart(historicCoverages, this.ReportContext.Settings.RenderPngFallBackImagesForHistoryCharts); } } var sumableMetrics = summaryResult.SumableMetrics; if (sumableMetrics.Count > 0) { reportRenderer.Header(ReportResources.Metrics); var methodMetric = new MethodMetric(ReportResources.Total, ReportResources.Total, sumableMetrics); reportRenderer.MetricsTable(new[] { methodMetric }); } if (this.ReportContext.RiskHotspotAnalysisResult != null && this.ReportContext.RiskHotspotAnalysisResult.CodeCodeQualityMetricsAvailable) { reportRenderer.Header(ReportResources.RiskHotspots); if (this.ReportContext.RiskHotspotAnalysisResult.RiskHotspots.Count > 0) { reportRenderer.BeginRiskHotspots(); reportRenderer.RiskHotspots(this.ReportContext.RiskHotspotAnalysisResult.RiskHotspots); reportRenderer.FinishRiskHotspots(); } else { // Angular element has to be present reportRenderer.BeginRiskHotspots(); reportRenderer.FinishRiskHotspots(); reportRenderer.Paragraph(ReportResources.NoRiskHotspots); } } else { // Angular element has to be present reportRenderer.BeginRiskHotspots(); reportRenderer.FinishRiskHotspots(); } reportRenderer.Header(ReportResources.Coverage3); if (summaryResult.Assemblies.Any()) { reportRenderer.BeginSummaryTable(); reportRenderer.BeginSummaryTable(summaryResult.SupportsBranchCoverage); foreach (var assembly in summaryResult.Assemblies) { reportRenderer.SummaryAssembly(assembly, summaryResult.SupportsBranchCoverage); foreach (var @class in assembly.Classes) { reportRenderer.SummaryClass(@class, summaryResult.SupportsBranchCoverage); } } reportRenderer.FinishTable(); reportRenderer.FinishSummaryTable(); } else { // Angular element has to be present reportRenderer.BeginSummaryTable(); reportRenderer.FinishSummaryTable(); reportRenderer.Paragraph(ReportResources.NoCoveredAssemblies); } reportRenderer.CustomSummary(summaryResult.Assemblies, this.ReportContext.RiskHotspotAnalysisResult.RiskHotspots, summaryResult.SupportsBranchCoverage); reportRenderer.AddFooter(); reportRenderer.SaveSummaryReport(this.ReportContext.ReportConfiguration.TargetDirectory); }
/// <summary> /// Extracts the metrics from the given <see cref="XElement">XElements</see>. /// </summary> /// <param name="codeFile">The code file.</param> /// <param name="methodsOfFile">The methods of the file.</param> private static void SetMethodMetrics(CodeFile codeFile, IEnumerable <XElement> methodsOfFile) { foreach (var method in methodsOfFile) { string fullName = method.Attribute("name").Value + method.Attribute("signature").Value; fullName = ExtractMethodName(fullName, method.Parent.Parent.Attribute("name").Value); if (lambdaMethodNameRegex.IsMatch(fullName)) { continue; } string shortName = methodRegex.Replace(fullName, m => string.Format(CultureInfo.InvariantCulture, "{0}({1})", m.Groups["MethodName"].Value, m.Groups["Arguments"].Value.Length > 0 ? "..." : string.Empty)); var metrics = new List <Metric>(); var lineRate = method.Attribute("line-rate"); if (lineRate != null) { decimal?value = null; if (!"NaN".Equals(lineRate.Value, StringComparison.OrdinalIgnoreCase)) { value = Math.Round(100 * decimal.Parse(lineRate.Value, CultureInfo.InvariantCulture), 2, MidpointRounding.AwayFromZero); } metrics.Add(new Metric( ReportResources.Coverage, ParserBase.CodeCoverageUri, MetricType.CoveragePercentual, value)); } var branchRate = method.Attribute("branch-rate"); if (branchRate != null) { decimal?value = null; if (!"NaN".Equals(branchRate.Value, StringComparison.OrdinalIgnoreCase)) { value = Math.Round(100 * decimal.Parse(branchRate.Value, CultureInfo.InvariantCulture), 2, MidpointRounding.AwayFromZero); } metrics.Add(new Metric( ReportResources.BranchCoverage, ParserBase.CodeCoverageUri, MetricType.CoveragePercentual, value)); } var cyclomaticComplexityAttribute = method.Attribute("complexity"); if (cyclomaticComplexityAttribute != null) { decimal?value = null; if (!"NaN".Equals(cyclomaticComplexityAttribute.Value, StringComparison.OrdinalIgnoreCase)) { value = Math.Round(decimal.Parse(cyclomaticComplexityAttribute.Value, CultureInfo.InvariantCulture), 2, MidpointRounding.AwayFromZero); } metrics.Insert( 0, new Metric( ReportResources.CyclomaticComplexity, ParserBase.CyclomaticComplexityUri, MetricType.CodeQuality, value, MetricMergeOrder.LowerIsBetter)); } var methodMetric = new MethodMetric(fullName, shortName, metrics); var line = method .Elements("lines") .Elements("line") .FirstOrDefault(); if (line != null) { methodMetric.Line = int.Parse(line.Attribute("number").Value, CultureInfo.InvariantCulture); } /* If cobertura file is merged from different files, a class and therefore method can exist several times. * The first result is used. */ if (codeFile.MethodMetrics.Any(m => m.Equals(methodMetric))) { continue; } codeFile.AddMethodMetric(methodMetric); } }
/// <summary> /// Extracts the metrics from the given <see cref="XElement">XElements</see>. /// </summary> /// <param name="codeFile">The code file.</param> /// <param name="methodsOfFile">The methods of the file.</param> private static void SetMethodMetrics(CodeFile codeFile, IEnumerable <XElement> methodsOfFile) { foreach (var methodGroup in methodsOfFile.GroupBy(m => m.Element("Name").Value)) { var method = methodGroup.First(); // Exclude properties and lambda expressions if (method.Attribute("skippedDueTo") != null || method.HasAttributeWithValue("isGetter", "true") || method.HasAttributeWithValue("isSetter", "true") || lambdaMethodNameRegex.IsMatch(methodGroup.Key)) { continue; } var metrics = new List <Metric>() { new Metric( ReportResources.CyclomaticComplexity, ParserBase.CyclomaticComplexityUri, MetricType.CodeQuality, methodGroup.Max(m => int.Parse(m.Attribute("cyclomaticComplexity").Value, CultureInfo.InvariantCulture)), MetricMergeOrder.LowerIsBetter), new Metric( ReportResources.SequenceCoverage, ParserBase.CodeCoverageUri, MetricType.CoveragePercentual, methodGroup.Max(m => decimal.Parse(m.Attribute("sequenceCoverage").Value, CultureInfo.InvariantCulture))), new Metric( ReportResources.BranchCoverage, ParserBase.CodeCoverageUri, MetricType.CoveragePercentual, methodGroup.Max(m => decimal.Parse(m.Attribute("branchCoverage").Value, CultureInfo.InvariantCulture))) }; var npathComplexityAttributes = methodGroup.Select(m => m.Attribute("nPathComplexity")).Where(a => a != null).ToArray(); if (npathComplexityAttributes.Length > 0) { metrics.Insert( 1, new Metric( ReportResources.NPathComplexity, ParserBase.NPathComplexityUri, MetricType.CodeQuality, npathComplexityAttributes .Select(a => int.Parse(a.Value, CultureInfo.InvariantCulture)) .Max(a => a < 0 ? int.MaxValue : a), MetricMergeOrder.LowerIsBetter)); } var crapScoreAttributes = methodGroup.Select(m => m.Attribute("crapScore")).Where(a => a != null).ToArray(); if (crapScoreAttributes.Length > 0) { metrics.Add(new Metric( ReportResources.CrapScore, ParserBase.CrapScoreUri, MetricType.CodeQuality, crapScoreAttributes.Max(a => decimal.Parse(a.Value, CultureInfo.InvariantCulture)), MetricMergeOrder.LowerIsBetter)); } string fullName = ExtractMethodName(methodGroup.Key); string shortName = methodRegex.Replace(fullName, m => string.Format(CultureInfo.InvariantCulture, "{0}({1})", m.Groups["MethodName"].Value, m.Groups["Arguments"].Value.Length > 0 ? "..." : string.Empty)); var methodMetric = new MethodMetric(fullName, shortName, metrics); var seqpnt = method .Elements("SequencePoints") .Elements("SequencePoint") .FirstOrDefault(); if (seqpnt != null) { methodMetric.Line = int.Parse(seqpnt.Attribute("sl").Value, CultureInfo.InvariantCulture); } codeFile.AddMethodMetric(methodMetric); } }
/// <summary> /// Adds a metrics table to the report. /// </summary> /// <param name="metric">The metric.</param> public void BeginMetricsTable(MethodMetric metric) { }
/// <summary> /// Extracts the metrics from the given <see cref="XElement">XElements</see>. /// </summary> /// <param name="codeFile">The code file.</param> /// <param name="methodsOfFile">The methods of the file.</param> private static void SetMethodMetrics(CodeFile codeFile, IEnumerable <XElement> methodsOfFile) { foreach (var method in methodsOfFile) { string fullName = method.Attribute("name").Value + method.Attribute("desc").Value; if (fullName.StartsWith("lambda$")) { continue; } string shortName = methodRegex.Replace(fullName, m => string.Format(CultureInfo.InvariantCulture, "{0}({1})", m.Groups["MethodName"].Value, m.Groups["Arguments"].Value.Length > 0 ? "..." : string.Empty)); var metrics = new List <Metric>(); var lineRate = method.Elements("counter") .Where(e => e.Attribute("type") != null && e.Attribute("type").Value == "LINE") .FirstOrDefault(); if (lineRate != null) { decimal missed = decimal.Parse(lineRate.Attribute("missed").Value, CultureInfo.InvariantCulture); decimal covered = decimal.Parse(lineRate.Attribute("covered").Value, CultureInfo.InvariantCulture); decimal total = missed + covered; metrics.Add(new Metric( ReportResources.Coverage, ParserBase.CodeCoverageUri, MetricType.CoveragePercentual, total == 0 ? (decimal?)null : Math.Round((100 * covered) / total, 2, MidpointRounding.AwayFromZero))); } else { // If no line rate available, do not add branch coverage too continue; } var branchRate = method.Elements("counter") .Where(e => e.Attribute("type") != null && e.Attribute("type").Value == "BRANCH") .FirstOrDefault(); if (branchRate != null) { decimal missed = decimal.Parse(branchRate.Attribute("missed").Value, CultureInfo.InvariantCulture); decimal covered = decimal.Parse(branchRate.Attribute("covered").Value, CultureInfo.InvariantCulture); decimal total = missed + covered; metrics.Add(new Metric( ReportResources.BranchCoverage, ParserBase.CodeCoverageUri, MetricType.CoveragePercentual, total == 0 ? (decimal?)null : Math.Round((100 * covered) / total, 2, MidpointRounding.AwayFromZero))); } else { // If no branch coverage is available, add default to avoid empty columns metrics.Add(new Metric( ReportResources.BranchCoverage, ParserBase.CodeCoverageUri, MetricType.CoveragePercentual, null)); } var methodMetric = new MethodMetric(fullName, shortName, metrics); methodMetric.Line = method.Attribute("line") != null?int.Parse(method.Attribute("line").Value, CultureInfo.InvariantCulture) : default(int?); codeFile.AddMethodMetric(methodMetric); } }