Skip to content

Commit

Permalink
#612 Increase percentage accuracy in reports
Browse files Browse the repository at this point in the history
  • Loading branch information
danielpalme committed Aug 12, 2023
1 parent baf099b commit 0a8ce85
Show file tree
Hide file tree
Showing 18 changed files with 117 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ export class CoverageHistoryChartComponent {

for (let i: number = 0; i < value.length; i++) {
path += `${i === 0 ? "M" : "L"}`;
path += `${Helper.roundNumber(30 * i / (value.length - 1), 1)}`;
path += `,${Helper.roundNumber(18 - (18 * value[i] / 100), 1)}`;
path += `${Helper.roundNumber(30 * i / (value.length - 1))}`;
path += `,${Helper.roundNumber(18 - (18 * value[i] / 100))}`;
}

this.path = path;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { CoverageInfoSettings } from "./data/coverageinfo-settings.class";
import { Metric } from "./data/metric.class";
import { ClassViewModel } from "./viewmodels/class-viewmodel.class";
import { CodeElementViewModel } from "./viewmodels/codelement-viewmodel.class";
import { Helper } from "./viewmodels/helper.class";

@Component({
selector: "coverage-info",
Expand Down Expand Up @@ -251,6 +252,7 @@ export class CoverageInfoComponent {
this.metrics = (<any>this.window).metrics;

this.translations = (<any>this.window).translations;
Helper.maximumDecimalPlacesForCoverageQuotas = (<any>this.window).maximumDecimalPlacesForCoverageQuotas;

let restoredFromHistory: boolean = false;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export class ClassViewModel extends ElementBase {
return NaN;
}

return Helper.roundNumber(100 * this.coveredLines / this.coverableLines, 1);
return Helper.roundNumber(100 * this.coveredLines / this.coverableLines);
}

visible(filter: string, historicCoverageFilter: string): boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export abstract class ElementBase {
return NaN;
}

return Helper.roundNumber(100 * this.coveredLines / this.coverableLines, 1);
return Helper.roundNumber(100 * this.coveredLines / this.coverableLines);
}

get coveragePercentage(): string {
Expand All @@ -43,7 +43,7 @@ export abstract class ElementBase {
return NaN;
}

return Helper.roundNumber(100 * this.coveredBranches / this.totalBranches, 1);
return Helper.roundNumber(100 * this.coveredBranches / this.totalBranches);
}

get branchCoveragePercentage(): string {
Expand All @@ -67,7 +67,7 @@ export abstract class ElementBase {
return NaN;
}

return Helper.roundNumber(100 * this.coveredMethods / this.totalMethods, 1);
return Helper.roundNumber(100 * this.coveredMethods / this.totalMethods);
}

get methodCoveragePercentage(): string {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
export class Helper {
static roundNumber(number: number, precision: number): number {
return Math.floor(number * Math.pow(10, precision)) / Math.pow(10, precision);
static maximumDecimalPlacesForCoverageQuotas: number;

static roundNumber(number: number): number {
return Math.floor(number * Math.pow(10, Helper.maximumDecimalPlacesForCoverageQuotas)) / Math.pow(10, Helper.maximumDecimalPlacesForCoverageQuotas);
}

static getNthOrLastIndexOf(text: string, substring: string, n: number): number {
Expand Down
1 change: 1 addition & 0 deletions src/Readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ CHANGELOG

5.1.24.0

* Fix: #612 Increase percentage accuracy in reports
* Fix: #619 Improved report parsing to avoid integer overflow

5.1.23.0
Expand Down
27 changes: 27 additions & 0 deletions src/ReportGenerator.Core.Test/Common/MathExtensionsTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using Palmmedia.ReportGenerator.Core.Common;
using Xunit;

namespace Palmmedia.ReportGenerator.Core.Test.Common
{
public class MathExtensionsTest
{
[Theory]
[InlineData(1, 1000, 1, 0.1)]
[InlineData(1, 10000, 1, 0)]
[InlineData(1, 100000, 1, 0)]

[InlineData(1, 1000, 2, 0.1)]
[InlineData(1, 10000, 2, 0.01)]
[InlineData(1, 100000, 2, 0)]

[InlineData(1, 1000, 3, 0.1)]
[InlineData(1, 10000, 3, 0.01)]
[InlineData(1, 100000, 3, 0.001)]
public void CalculatePercentage(int number1, int number2, int maximumDecimalPlaces, decimal expected)
{
MathExtensions.MaximumDecimalPlaces = maximumDecimalPlaces;
decimal result = MathExtensions.CalculatePercentage(number1, number2);
Assert.Equal(expected, result);
}
}
}
51 changes: 51 additions & 0 deletions src/ReportGenerator.Core/Common/MathExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;

namespace Palmmedia.ReportGenerator.Core.Common
{
/// <summary>
/// Math extensions.
/// </summary>
internal static class MathExtensions
{
private static int maximumDecimalPlaces = 1;

private static double factor = 1000;

private static int divisor = 10;

/// <summary>
/// Gets or sets the maximum decimal places for coverage quotas / percentages.
/// </summary>
public static int MaximumDecimalPlaces
{
get
{
return maximumDecimalPlaces;
}

set
{
maximumDecimalPlaces = Math.Min(8, Math.Max(0, value));
factor = 100 * Math.Pow(10, maximumDecimalPlaces);
divisor = (int)Math.Pow(10, maximumDecimalPlaces);
}
}

/// <summary>
/// Creates a <see cref="HashSet&lt;T&gt;"/> from an <see cref="IEnumerable&lt;T&gt;"/>.
/// </summary>
/// <param name="number1">The first number.</param>
/// <param name="number2">The second number.</param>
/// <returns>The percentage.</returns>
internal static decimal CalculatePercentage(int number1, int number2)
{
if (number2 == 0)
{
throw new ArgumentException("Number must not be 0", nameof(number2));
}

return (decimal)Math.Truncate(factor * (double)number1 / (double)number2) / divisor;
}
}
}
2 changes: 2 additions & 0 deletions src/ReportGenerator.Core/Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,8 @@ public void GenerateReport(
throw new ArgumentNullException(nameof(parserResult));
}

MathExtensions.MaximumDecimalPlaces = settings.MaximumDecimalPlacesForCoverageQuotas;

var reportContext = new ReportContext(reportConfiguration, settings);

var pluginLoader = new ReflectionPluginLoader(reportConfiguration.Plugins);
Expand Down
6 changes: 3 additions & 3 deletions src/ReportGenerator.Core/Parser/Analysis/Assembly.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public string ShortName
/// Gets the coverage quota of the class.
/// </summary>
/// <value>The coverage quota.</value>
public decimal? CoverageQuota => (this.CoverableLines == 0) ? (decimal?)null : (decimal)Math.Truncate(1000 * (double)this.CoveredLines / (double)this.CoverableLines) / 10;
public decimal? CoverageQuota => (this.CoverableLines == 0) ? (decimal?)null : MathExtensions.CalculatePercentage(this.CoveredLines, this.CoverableLines);

/// <summary>
/// Gets the number of covered branches.
Expand All @@ -92,7 +92,7 @@ public string ShortName
/// Gets the branch coverage quota of the class.
/// </summary>
/// <value>The branch coverage quota.</value>
public decimal? BranchCoverageQuota => (this.TotalBranches == 0) ? (decimal?)null : (decimal)Math.Truncate(1000 * (double)this.CoveredBranches / (double)this.TotalBranches) / 10;
public decimal? BranchCoverageQuota => (this.TotalBranches == 0) ? (decimal?)null : MathExtensions.CalculatePercentage(this.CoveredBranches.GetValueOrDefault(), this.TotalBranches.GetValueOrDefault());

/// <summary>
/// Gets the number of covered code elements.
Expand All @@ -114,7 +114,7 @@ public string ShortName
/// Gets the code elements coverage quota.
/// </summary>
/// <value>The code elements coverage quota.</value>
public decimal? CodeElementCoverageQuota => (this.TotalCodeElements == 0) ? (decimal?)null : (decimal)Math.Truncate(1000 * (double)this.CoveredCodeElements / (double)this.TotalCodeElements) / 10;
public decimal? CodeElementCoverageQuota => (this.TotalCodeElements == 0) ? (decimal?)null : MathExtensions.CalculatePercentage(this.CoveredCodeElements, this.TotalCodeElements);

/// <summary>
/// Returns a <see cref="string" /> that represents this instance.
Expand Down
6 changes: 3 additions & 3 deletions src/ReportGenerator.Core/Parser/Analysis/Class.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ public decimal? CoverageQuota
{
get
{
return (this.CoverableLines == 0) ? (decimal?)null : (decimal)Math.Truncate(1000 * (double)this.CoveredLines / (double)this.CoverableLines) / 10;
return (this.CoverableLines == 0) ? (decimal?)null : MathExtensions.CalculatePercentage(this.CoveredLines, this.CoverableLines);
}
}

Expand All @@ -163,7 +163,7 @@ public decimal? CoverageQuota
/// Gets the branch coverage quota of the class.
/// </summary>
/// <value>The branch coverage quota.</value>
public decimal? BranchCoverageQuota => (this.TotalBranches == 0) ? (decimal?)null : (decimal)Math.Truncate(1000 * (double)this.CoveredBranches / (double)this.TotalBranches) / 10;
public decimal? BranchCoverageQuota => (this.TotalBranches == 0) ? (decimal?)null : MathExtensions.CalculatePercentage(this.CoveredBranches.GetValueOrDefault(), this.TotalBranches.GetValueOrDefault());

/// <summary>
/// Gets the number of covered code elements.
Expand All @@ -185,7 +185,7 @@ public decimal? CoverageQuota
/// Gets the code elements coverage quota.
/// </summary>
/// <value>The code elements coverage quota.</value>
public decimal? CodeElementCoverageQuota => (this.TotalCodeElements == 0) ? (decimal?)null : (decimal)Math.Truncate(1000 * (double)this.CoveredCodeElements / (double)this.TotalCodeElements) / 10;
public decimal? CodeElementCoverageQuota => (this.TotalCodeElements == 0) ? (decimal?)null : MathExtensions.CalculatePercentage(this.CoveredCodeElements, this.TotalCodeElements);

/// <summary>
/// Returns a <see cref="string" /> that represents this instance.
Expand Down
2 changes: 1 addition & 1 deletion src/ReportGenerator.Core/Parser/Analysis/CodeFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ public override bool Equals(object obj)
}
}

return (coverableLines == 0) ? (decimal?)null : (decimal)Math.Truncate(1000 * (double)coveredLines / (double)coverableLines) / 10;
return (coverableLines == 0) ? (decimal?)null : MathExtensions.CalculatePercentage(coveredLines, coverableLines);
}

/// <summary>
Expand Down
7 changes: 4 additions & 3 deletions src/ReportGenerator.Core/Parser/Analysis/HistoricCoverage.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using Palmmedia.ReportGenerator.Core.Common;

namespace Palmmedia.ReportGenerator.Core.Parser.Analysis
{
Expand Down Expand Up @@ -73,7 +74,7 @@ public HistoricCoverage(Class @class, DateTime executionTime, string tag)
/// Gets the coverage quota of the class.
/// </summary>
/// <value>The coverage quota.</value>
public decimal? CoverageQuota => (this.CoverableLines == 0) ? (decimal?)null : (decimal)Math.Truncate(1000 * (double)this.CoveredLines / (double)this.CoverableLines) / 10;
public decimal? CoverageQuota => (this.CoverableLines == 0) ? (decimal?)null : MathExtensions.CalculatePercentage(this.CoveredLines, this.CoverableLines);

/// <summary>
/// Gets or sets the number of total lines.
Expand Down Expand Up @@ -101,7 +102,7 @@ public HistoricCoverage(Class @class, DateTime executionTime, string tag)
/// Gets the branch coverage quota of the class.
/// </summary>
/// <value>The branch coverage quota.</value>
public decimal? BranchCoverageQuota => (this.TotalBranches == 0) ? (decimal?)null : (decimal)Math.Truncate(1000 * (double)this.CoveredBranches / (double)this.TotalBranches) / 10;
public decimal? BranchCoverageQuota => (this.TotalBranches == 0) ? (decimal?)null : MathExtensions.CalculatePercentage(this.CoveredBranches, this.TotalBranches);

/// <summary>
/// Gets or sets the number of covered code elements.
Expand All @@ -123,7 +124,7 @@ public HistoricCoverage(Class @class, DateTime executionTime, string tag)
/// Gets the code elements coverage quota.
/// </summary>
/// <value>The code elements coverage quota.</value>
public decimal? CodeElementCoverageQuota => (this.TotalCodeElements.GetValueOrDefault() == 0) ? (decimal?)null : (decimal)Math.Truncate(1000 * (double)this.CoveredCodeElements.GetValueOrDefault() / (double)this.TotalCodeElements) / 10;
public decimal? CodeElementCoverageQuota => (this.TotalCodeElements.GetValueOrDefault() == 0) ? (decimal?)null : MathExtensions.CalculatePercentage(this.CoveredCodeElements.GetValueOrDefault(), this.TotalCodeElements.GetValueOrDefault());

/// <summary>
/// Determines whether the specified <see cref="object" />, is equal to this instance.
Expand Down
6 changes: 3 additions & 3 deletions src/ReportGenerator.Core/Parser/Analysis/SummaryResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ public int? TotalLines
/// Gets the coverage quota.
/// </summary>
/// <value>The coverage quota.</value>
public decimal? CoverageQuota => (this.CoverableLines == 0) ? (decimal?)null : (decimal)Math.Truncate(1000 * (double)this.CoveredLines / (double)this.CoverableLines) / 10;
public decimal? CoverageQuota => (this.CoverableLines == 0) ? (decimal?)null : MathExtensions.CalculatePercentage(this.CoveredLines, this.CoverableLines);

/// <summary>
/// Gets the number of covered branches.
Expand All @@ -146,7 +146,7 @@ public int? TotalLines
/// Gets the branch coverage quota.
/// </summary>
/// <value>The branch coverage quota.</value>
public decimal? BranchCoverageQuota => (this.TotalBranches == 0) ? (decimal?)null : (decimal)Math.Truncate(1000 * (double)this.CoveredBranches / (double)this.TotalBranches) / 10;
public decimal? BranchCoverageQuota => (this.TotalBranches == 0) ? (decimal?)null : MathExtensions.CalculatePercentage(this.CoveredBranches.GetValueOrDefault(), this.TotalBranches.GetValueOrDefault());

/// <summary>
/// Gets the number of covered code elements.
Expand All @@ -168,7 +168,7 @@ public int? TotalLines
/// Gets the code elements coverage quota.
/// </summary>
/// <value>The code elements coverage quota.</value>
public decimal? CodeElementCoverageQuota => (this.TotalCodeElements == 0) ? (decimal?)null : (decimal)Math.Truncate(1000 * (double)this.CoveredCodeElements / (double)this.TotalCodeElements) / 10;
public decimal? CodeElementCoverageQuota => (this.TotalCodeElements == 0) ? (decimal?)null : MathExtensions.CalculatePercentage(this.CoveredCodeElements, this.TotalCodeElements);

/// <summary>
/// Gets all sumable metrics.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,7 @@ private void AddSequenceAndBranchPoints(

private decimal CalculateQuota(int covered, int total)
{
return (total == 0) ? 0m : (decimal)Math.Truncate(1000 * (double)covered / (double)total) / 10;
return (total == 0) ? 0m : MathExtensions.CalculatePercentage(covered, total);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ public void TestMethods(IEnumerable<TestMethod> testMethods, IEnumerable<FileAna

int coverableLines = fileAnalyses.SafeSum(f => f.Lines.Count(l => l.LineVisitStatus != LineVisitStatus.NotCoverable));
int coveredLines = fileAnalyses.SafeSum(f => f.Lines.Count(l => l.LineVisitStatus > LineVisitStatus.NotCovered));
decimal? coverage = (coverableLines == 0) ? (decimal?)null : (decimal)Math.Truncate(1000 * (double)coveredLines / (double)coverableLines) / 10;
decimal? coverage = (coverableLines == 0) ? (decimal?)null : MathExtensions.CalculatePercentage(coveredLines, coverableLines);

int? coverageRounded = null;

Expand All @@ -336,7 +336,7 @@ public void TestMethods(IEnumerable<TestMethod> testMethods, IEnumerable<FileAna
foreach (var testMethod in testMethods)
{
coveredLines = fileAnalyses.SafeSum(f => f.Lines.Count(l => l.LineCoverageByTestMethod.ContainsKey(testMethod) && l.LineCoverageByTestMethod[testMethod].LineVisitStatus > LineVisitStatus.NotCovered));
coverage = (coverableLines == 0) ? (decimal?)null : (decimal)Math.Truncate(1000 * (double)coveredLines / (double)coverableLines) / 10;
coverage = (coverableLines == 0) ? (decimal?)null : MathExtensions.CalculatePercentage(coveredLines, coverableLines);

coverageRounded = null;

Expand Down Expand Up @@ -751,6 +751,7 @@ public void CustomSummary(IEnumerable<Assembly> assemblies, IEnumerable<RiskHots

this.javaScriptContent.AppendLine("var branchCoverageAvailable = " + branchCoverageAvailable.ToString().ToLowerInvariant() + ";");
this.javaScriptContent.AppendLine("var methodCoverageAvailable = " + methodCoverageAvailable.ToString().ToLowerInvariant() + ";");
this.javaScriptContent.AppendLine("var maximumDecimalPlacesForCoverageQuotas = " + MathExtensions.MaximumDecimalPlaces + ";");
this.javaScriptContent.AppendLine();
}

Expand Down

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions src/ReportGenerator.Core/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,5 +77,10 @@ public int CachingDuringOfRemoteFilesInMinutes
/// Gets or sets the default assembly name for gcov and lcov.
/// </summary>
public string DefaultAssemblyName { get; set; } = "Default";

/// <summary>
/// Gets or sets the maximum decimal places for coverage quotas / percentages.
/// </summary>
public int MaximumDecimalPlacesForCoverageQuotas { get; set; } = 1;
}
}

0 comments on commit 0a8ce85

Please sign in to comment.