Skip to content

Commit 99cb653

Browse files
author
Paulo Barbosa
committed
Add multiple threshold
1 parent 5de0ad7 commit 99cb653

File tree

5 files changed

+266
-54
lines changed

5 files changed

+266
-54
lines changed

Documentation/MSBuildIntegration.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,12 @@ The above command will automatically fail the build if the line, branch or metho
9898
dotnet test /p:CollectCoverage=true /p:Threshold=80 /p:ThresholdType=line
9999
```
100100

101-
You can specify multiple values for `ThresholdType` by separating them with commas. Valid values include `line`, `branch` and `method`.
101+
You can specify multiple values for `ThresholdType` by separating them with commas. Valid values include `line`, `branch` and `method`.
102+
You can do the same for `Threshold` as well.
103+
104+
```bash
105+
dotnet test /p:CollectCoverage=true /p:Threshold="80,100,70", /p:ThresholdType="line,branch,method"
106+
```
102107

103108
By default, Coverlet will validate the threshold value against the coverage result of each module. The `/p:ThresholdStat` option allows you to change this behaviour and can have any of the following values:
104109

src/coverlet.console/Program.cs

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.ComponentModel;
44
using System.Diagnostics;
55
using System.IO;
6+
using System.Linq;
67
using System.Text;
78

89
using ConsoleTables;
@@ -129,13 +130,13 @@ static int Main(string[] args)
129130
process.WaitForExit();
130131

131132
var dOutput = output.HasValue() ? output.Value() : Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar.ToString();
132-
var dThreshold = threshold.HasValue() ? double.Parse(threshold.Value()) : 0;
133133
var dThresholdTypes = thresholdTypes.HasValue() ? thresholdTypes.Values : new List<string>(new string[] { "line", "branch", "method" });
134134
var dThresholdStat = thresholdStat.HasValue() ? Enum.Parse<ThresholdStatistic>(thresholdStat.Value(), true) : Enum.Parse<ThresholdStatistic>("minimum", true);
135135

136136
logger.LogInformation("\nCalculating coverage result...");
137137

138138
var result = coverage.GetCoverageResult();
139+
139140
var directory = Path.GetDirectoryName(dOutput);
140141
if (directory == string.Empty)
141142
{
@@ -173,27 +174,57 @@ static int Main(string[] args)
173174
}
174175
}
175176

176-
var thresholdTypeFlags = ThresholdTypeFlags.None;
177-
177+
var thresholdTypeFlagQueue = new Queue<ThresholdTypeFlags>();
178+
178179
foreach (var thresholdType in dThresholdTypes)
179180
{
180181
if (thresholdType.Equals("line", StringComparison.OrdinalIgnoreCase))
181182
{
182-
thresholdTypeFlags |= ThresholdTypeFlags.Line;
183+
thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Line);
183184
}
184185
else if (thresholdType.Equals("branch", StringComparison.OrdinalIgnoreCase))
185186
{
186-
thresholdTypeFlags |= ThresholdTypeFlags.Branch;
187+
thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Branch);
187188
}
188189
else if (thresholdType.Equals("method", StringComparison.OrdinalIgnoreCase))
189190
{
190-
thresholdTypeFlags |= ThresholdTypeFlags.Method;
191+
thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Method);
192+
}
193+
}
194+
195+
Dictionary<ThresholdTypeFlags, double> thresholdTypeFlagValues = new Dictionary<ThresholdTypeFlags, double>();
196+
if (threshold.HasValue() && threshold.Value().Contains(','))
197+
{
198+
var thresholdValues = threshold.Value().Split(',', StringSplitOptions.RemoveEmptyEntries).Select(t => t.Trim());
199+
if (thresholdValues.Count() != thresholdTypeFlagQueue.Count())
200+
{
201+
throw new Exception($"Threshold type flag count ({thresholdTypeFlagQueue.Count()}) and values count ({thresholdValues.Count()}) doesn't match");
202+
}
203+
204+
foreach (var thresholdValue in thresholdValues)
205+
{
206+
if (double.TryParse(thresholdValue, out var value))
207+
{
208+
thresholdTypeFlagValues[thresholdTypeFlagQueue.Dequeue()] = value;
209+
}
210+
else
211+
{
212+
throw new Exception($"Invalid threshold value must be numeric");
213+
}
214+
}
215+
}
216+
else
217+
{
218+
double thresholdValue = threshold.HasValue() ? double.Parse(threshold.Value()) : 0;
219+
220+
while (thresholdTypeFlagQueue.Any())
221+
{
222+
thresholdTypeFlagValues[thresholdTypeFlagQueue.Dequeue()] = thresholdValue;
191223
}
192224
}
193225

194226
var coverageTable = new ConsoleTable("Module", "Line", "Branch", "Method");
195227
var summary = new CoverageSummary();
196-
int numModules = result.Modules.Count;
197228

198229
var linePercentCalculation = summary.CalculateLineCoverage(result.Modules);
199230
var branchPercentCalculation = summary.CalculateBranchCoverage(result.Modules);
@@ -230,26 +261,26 @@ static int Main(string[] args)
230261
{
231262
exitCode += (int)CommandExitCodes.TestFailed;
232263
}
233-
thresholdTypeFlags = result.GetThresholdTypesBelowThreshold(summary, dThreshold, thresholdTypeFlags, dThresholdStat);
264+
265+
var thresholdTypeFlags = result.GetThresholdTypesBelowThreshold(summary, thresholdTypeFlagValues, dThresholdStat);
234266
if (thresholdTypeFlags != ThresholdTypeFlags.None)
235267
{
236268
exitCode += (int)CommandExitCodes.CoverageBelowThreshold;
237269
var exceptionMessageBuilder = new StringBuilder();
238270
if ((thresholdTypeFlags & ThresholdTypeFlags.Line) != ThresholdTypeFlags.None)
239271
{
240-
exceptionMessageBuilder.AppendLine($"The {dThresholdStat.ToString().ToLower()} line coverage is below the specified {dThreshold}");
272+
exceptionMessageBuilder.AppendLine($"The {dThresholdStat.ToString().ToLower()} line coverage is below the specified {thresholdTypeFlagValues[ThresholdTypeFlags.Line]}");
241273
}
242274

243275
if ((thresholdTypeFlags & ThresholdTypeFlags.Branch) != ThresholdTypeFlags.None)
244276
{
245-
exceptionMessageBuilder.AppendLine($"The {dThresholdStat.ToString().ToLower()} branch coverage is below the specified {dThreshold}");
277+
exceptionMessageBuilder.AppendLine($"The {dThresholdStat.ToString().ToLower()} branch coverage is below the specified {thresholdTypeFlagValues[ThresholdTypeFlags.Branch]}");
246278
}
247279

248280
if ((thresholdTypeFlags & ThresholdTypeFlags.Method) != ThresholdTypeFlags.None)
249281
{
250-
exceptionMessageBuilder.AppendLine($"The {dThresholdStat.ToString().ToLower()} method coverage is below the specified {dThreshold}");
282+
exceptionMessageBuilder.AppendLine($"The {dThresholdStat.ToString().ToLower()} method coverage is below the specified {thresholdTypeFlagValues[ThresholdTypeFlags.Method]}");
251283
}
252-
253284
throw new Exception(exceptionMessageBuilder.ToString());
254285
}
255286

src/coverlet.core/CoverageResult.cs

Lines changed: 21 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ internal void Merge(Modules modules)
111111
}
112112
}
113113

114-
public ThresholdTypeFlags GetThresholdTypesBelowThreshold(CoverageSummary summary, double threshold, ThresholdTypeFlags thresholdTypes, ThresholdStatistic thresholdStat)
114+
public ThresholdTypeFlags GetThresholdTypesBelowThreshold(CoverageSummary summary, Dictionary<ThresholdTypeFlags, double> thresholdTypeFlagValues, ThresholdStatistic thresholdStat)
115115
{
116116
var thresholdTypeFlags = ThresholdTypeFlags.None;
117117
switch (thresholdStat)
@@ -123,23 +123,20 @@ public ThresholdTypeFlags GetThresholdTypesBelowThreshold(CoverageSummary summar
123123
double line = summary.CalculateLineCoverage(module.Value).Percent;
124124
double branch = summary.CalculateBranchCoverage(module.Value).Percent;
125125
double method = summary.CalculateMethodCoverage(module.Value).Percent;
126-
127-
if ((thresholdTypes & ThresholdTypeFlags.Line) != ThresholdTypeFlags.None)
126+
127+
if (thresholdTypeFlagValues.TryGetValue(ThresholdTypeFlags.Line, out var lineThresholdValue) && lineThresholdValue > line)
128128
{
129-
if (line < threshold)
130-
thresholdTypeFlags |= ThresholdTypeFlags.Line;
129+
thresholdTypeFlags |= ThresholdTypeFlags.Line;
131130
}
132131

133-
if ((thresholdTypes & ThresholdTypeFlags.Branch) != ThresholdTypeFlags.None)
132+
if (thresholdTypeFlagValues.TryGetValue(ThresholdTypeFlags.Branch, out var branchThresholdValue) && branchThresholdValue > branch)
134133
{
135-
if (branch < threshold)
136-
thresholdTypeFlags |= ThresholdTypeFlags.Branch;
134+
thresholdTypeFlags |= ThresholdTypeFlags.Branch;
137135
}
138136

139-
if ((thresholdTypes & ThresholdTypeFlags.Method) != ThresholdTypeFlags.None)
137+
if (thresholdTypeFlagValues.TryGetValue(ThresholdTypeFlags.Method, out var methodThresholdValue) && methodThresholdValue > method)
140138
{
141-
if (method < threshold)
142-
thresholdTypeFlags |= ThresholdTypeFlags.Method;
139+
thresholdTypeFlags |= ThresholdTypeFlags.Method;
143140
}
144141
}
145142
}
@@ -149,23 +146,20 @@ public ThresholdTypeFlags GetThresholdTypesBelowThreshold(CoverageSummary summar
149146
double line = summary.CalculateLineCoverage(Modules).AverageModulePercent;
150147
double branch = summary.CalculateBranchCoverage(Modules).AverageModulePercent;
151148
double method = summary.CalculateMethodCoverage(Modules).AverageModulePercent;
152-
153-
if ((thresholdTypes & ThresholdTypeFlags.Line) != ThresholdTypeFlags.None)
149+
150+
if (thresholdTypeFlagValues.TryGetValue(ThresholdTypeFlags.Line, out var lineThresholdValue) && lineThresholdValue > line)
154151
{
155-
if (line < threshold)
156-
thresholdTypeFlags |= ThresholdTypeFlags.Line;
152+
thresholdTypeFlags |= ThresholdTypeFlags.Line;
157153
}
158154

159-
if ((thresholdTypes & ThresholdTypeFlags.Branch) != ThresholdTypeFlags.None)
155+
if (thresholdTypeFlagValues.TryGetValue(ThresholdTypeFlags.Branch, out var branchThresholdValue) && branchThresholdValue > branch)
160156
{
161-
if (branch < threshold)
162-
thresholdTypeFlags |= ThresholdTypeFlags.Branch;
157+
thresholdTypeFlags |= ThresholdTypeFlags.Branch;
163158
}
164159

165-
if ((thresholdTypes & ThresholdTypeFlags.Method) != ThresholdTypeFlags.None)
160+
if (thresholdTypeFlagValues.TryGetValue(ThresholdTypeFlags.Method, out var methodThresholdValue) && methodThresholdValue > method)
166161
{
167-
if (method < threshold)
168-
thresholdTypeFlags |= ThresholdTypeFlags.Method;
162+
thresholdTypeFlags |= ThresholdTypeFlags.Method;
169163
}
170164
}
171165
break;
@@ -175,22 +169,19 @@ public ThresholdTypeFlags GetThresholdTypesBelowThreshold(CoverageSummary summar
175169
double branch = summary.CalculateBranchCoverage(Modules).Percent;
176170
double method = summary.CalculateMethodCoverage(Modules).Percent;
177171

178-
if ((thresholdTypes & ThresholdTypeFlags.Line) != ThresholdTypeFlags.None)
172+
if (thresholdTypeFlagValues.TryGetValue(ThresholdTypeFlags.Line, out var lineThresholdValue) && lineThresholdValue > line)
179173
{
180-
if (line < threshold)
181-
thresholdTypeFlags |= ThresholdTypeFlags.Line;
174+
thresholdTypeFlags |= ThresholdTypeFlags.Line;
182175
}
183176

184-
if ((thresholdTypes & ThresholdTypeFlags.Branch) != ThresholdTypeFlags.None)
177+
if (thresholdTypeFlagValues.TryGetValue(ThresholdTypeFlags.Branch, out var branchThresholdValue) && branchThresholdValue > branch)
185178
{
186-
if (branch < threshold)
187-
thresholdTypeFlags |= ThresholdTypeFlags.Branch;
179+
thresholdTypeFlags |= ThresholdTypeFlags.Branch;
188180
}
189181

190-
if ((thresholdTypes & ThresholdTypeFlags.Method) != ThresholdTypeFlags.None)
182+
if (thresholdTypeFlagValues.TryGetValue(ThresholdTypeFlags.Method, out var methodThresholdValue) && methodThresholdValue > method)
191183
{
192-
if (method < threshold)
193-
thresholdTypeFlags |= ThresholdTypeFlags.Method;
184+
thresholdTypeFlags |= ThresholdTypeFlags.Method;
194185
}
195186
}
196187
break;

src/coverlet.msbuild.tasks/CoverageResultTask.cs

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public class CoverageResultTask : BaseTask
2525
public string OutputFormat { get; set; }
2626

2727
[Required]
28-
public double Threshold { get; set; }
28+
public string Threshold { get; set; }
2929

3030
[Required]
3131
public string ThresholdType { get; set; }
@@ -124,25 +124,56 @@ public override bool Execute()
124124

125125
ReportItems = coverageReportPaths.ToArray();
126126

127-
var thresholdTypeFlags = ThresholdTypeFlags.None;
128-
var thresholdStat = ThresholdStatistic.Minimum;
127+
var thresholdTypeFlagQueue = new Queue<ThresholdTypeFlags>();
129128

130129
foreach (var thresholdType in ThresholdType.Split(',').Select(t => t.Trim()))
131130
{
132131
if (thresholdType.Equals("line", StringComparison.OrdinalIgnoreCase))
133132
{
134-
thresholdTypeFlags |= ThresholdTypeFlags.Line;
133+
thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Line);
135134
}
136135
else if (thresholdType.Equals("branch", StringComparison.OrdinalIgnoreCase))
137136
{
138-
thresholdTypeFlags |= ThresholdTypeFlags.Branch;
137+
thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Branch);
139138
}
140139
else if (thresholdType.Equals("method", StringComparison.OrdinalIgnoreCase))
141140
{
142-
thresholdTypeFlags |= ThresholdTypeFlags.Method;
141+
thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Method);
143142
}
144143
}
144+
145+
Dictionary<ThresholdTypeFlags, double> thresholdTypeFlagValues = new Dictionary<ThresholdTypeFlags, double>();
146+
if (Threshold.Contains(','))
147+
{
148+
var thresholdValues = Threshold.Split(new char[] {','}, StringSplitOptions.RemoveEmptyEntries).Select(t => t.Trim());
149+
if(thresholdValues.Count() != thresholdTypeFlagQueue.Count())
150+
{
151+
throw new Exception($"Threshold type flag count ({thresholdTypeFlagQueue.Count()}) and values count ({thresholdValues.Count()}) doesn't match");
152+
}
145153

154+
foreach (var threshold in thresholdValues)
155+
{
156+
if (double.TryParse(threshold, out var value))
157+
{
158+
thresholdTypeFlagValues[thresholdTypeFlagQueue.Dequeue()] = value;
159+
}
160+
else
161+
{
162+
throw new Exception($"Invalid threshold value must be numeric");
163+
}
164+
}
165+
}
166+
else
167+
{
168+
double thresholdValue = double.Parse(Threshold);
169+
170+
while (thresholdTypeFlagQueue.Any())
171+
{
172+
thresholdTypeFlagValues[thresholdTypeFlagQueue.Dequeue()] = thresholdValue;
173+
}
174+
}
175+
176+
var thresholdStat = ThresholdStatistic.Minimum;
146177
if (ThresholdStat.Equals("average", StringComparison.OrdinalIgnoreCase))
147178
{
148179
thresholdStat = ThresholdStatistic.Average;
@@ -154,7 +185,6 @@ public override bool Execute()
154185

155186
var coverageTable = new ConsoleTable("Module", "Line", "Branch", "Method");
156187
var summary = new CoverageSummary();
157-
int numModules = result.Modules.Count;
158188

159189
var linePercentCalculation = summary.CalculateLineCoverage(result.Modules);
160190
var branchPercentCalculation = summary.CalculateBranchCoverage(result.Modules);
@@ -189,23 +219,26 @@ public override bool Execute()
189219

190220
Console.WriteLine(coverageTable.ToStringAlternative());
191221

192-
thresholdTypeFlags = result.GetThresholdTypesBelowThreshold(summary, Threshold, thresholdTypeFlags, thresholdStat);
222+
var thresholdTypeFlags = result.GetThresholdTypesBelowThreshold(summary, thresholdTypeFlagValues, thresholdStat);
193223
if (thresholdTypeFlags != ThresholdTypeFlags.None)
194224
{
195225
var exceptionMessageBuilder = new StringBuilder();
196226
if ((thresholdTypeFlags & ThresholdTypeFlags.Line) != ThresholdTypeFlags.None)
197227
{
198-
exceptionMessageBuilder.AppendLine($"The {thresholdStat.ToString().ToLower()} line coverage is below the specified {Threshold}");
228+
exceptionMessageBuilder.AppendLine(
229+
$"The {thresholdStat.ToString().ToLower()} line coverage is below the specified {thresholdTypeFlagValues[ThresholdTypeFlags.Line]}");
199230
}
200231

201232
if ((thresholdTypeFlags & ThresholdTypeFlags.Branch) != ThresholdTypeFlags.None)
202233
{
203-
exceptionMessageBuilder.AppendLine($"The {thresholdStat.ToString().ToLower()} branch coverage is below the specified {Threshold}");
234+
exceptionMessageBuilder.AppendLine(
235+
$"The {thresholdStat.ToString().ToLower()} branch coverage is below the specified {thresholdTypeFlagValues[ThresholdTypeFlags.Branch]}");
204236
}
205237

206238
if ((thresholdTypeFlags & ThresholdTypeFlags.Method) != ThresholdTypeFlags.None)
207239
{
208-
exceptionMessageBuilder.AppendLine($"The {thresholdStat.ToString().ToLower()} method coverage is below the specified {Threshold}");
240+
exceptionMessageBuilder.AppendLine(
241+
$"The {thresholdStat.ToString().ToLower()} method coverage is below the specified {thresholdTypeFlagValues[ThresholdTypeFlags.Method]}");
209242
}
210243

211244
throw new Exception(exceptionMessageBuilder.ToString());

0 commit comments

Comments
 (0)