Skip to content

Commit b8111fc

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

File tree

5 files changed

+265
-46
lines changed

5 files changed

+265
-46
lines changed

Diff for: Documentation/MSBuildIntegration.md

+6-1
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

Diff for: src/coverlet.console/Program.cs

+42-8
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
{
@@ -174,26 +175,60 @@ static int Main(string[] args)
174175
}
175176

176177
var thresholdTypeFlags = ThresholdTypeFlags.None;
177-
178+
var thresholdTypeFlagQueue = new Queue<ThresholdTypeFlags>();
179+
178180
foreach (var thresholdType in dThresholdTypes)
179181
{
180182
if (thresholdType.Equals("line", StringComparison.OrdinalIgnoreCase))
181183
{
182184
thresholdTypeFlags |= ThresholdTypeFlags.Line;
185+
thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Line);
183186
}
184187
else if (thresholdType.Equals("branch", StringComparison.OrdinalIgnoreCase))
185188
{
186189
thresholdTypeFlags |= ThresholdTypeFlags.Branch;
190+
thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Branch);
187191
}
188192
else if (thresholdType.Equals("method", StringComparison.OrdinalIgnoreCase))
189193
{
190194
thresholdTypeFlags |= ThresholdTypeFlags.Method;
195+
thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Method);
196+
}
197+
}
198+
199+
Dictionary<ThresholdTypeFlags, double> thresholdTypeFlagValues = new Dictionary<ThresholdTypeFlags, double>();
200+
if (threshold.HasValue() && threshold.Value().Contains(','))
201+
{
202+
var thresholdValues = threshold.Value().Split(',', StringSplitOptions.RemoveEmptyEntries).Select(t => t.Trim());
203+
if (thresholdValues.Count() != thresholdTypeFlagQueue.Count())
204+
{
205+
throw new Exception($"Threshold type flag count ({thresholdTypeFlagQueue.Count()}) and values count ({thresholdValues.Count()}) doesn't match");
206+
}
207+
208+
foreach (var thresholdValue in thresholdValues)
209+
{
210+
if (double.TryParse(thresholdValue, out var value))
211+
{
212+
thresholdTypeFlagValues[thresholdTypeFlagQueue.Dequeue()] = value;
213+
}
214+
else
215+
{
216+
throw new Exception($"Invalid threshold value must be numeric");
217+
}
218+
}
219+
}
220+
else
221+
{
222+
double thresholdValue = threshold.HasValue() ? double.Parse(threshold.Value()) : 0;
223+
224+
while (thresholdTypeFlagQueue.Any())
225+
{
226+
thresholdTypeFlagValues[thresholdTypeFlagQueue.Dequeue()] = thresholdValue;
191227
}
192228
}
193229

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

198233
var linePercentCalculation = summary.CalculateLineCoverage(result.Modules);
199234
var branchPercentCalculation = summary.CalculateBranchCoverage(result.Modules);
@@ -230,26 +265,25 @@ static int Main(string[] args)
230265
{
231266
exitCode += (int)CommandExitCodes.TestFailed;
232267
}
233-
thresholdTypeFlags = result.GetThresholdTypesBelowThreshold(summary, dThreshold, thresholdTypeFlags, dThresholdStat);
268+
thresholdTypeFlags = result.GetThresholdTypesBelowThreshold(summary, thresholdTypeFlagValues, dThresholdStat);
234269
if (thresholdTypeFlags != ThresholdTypeFlags.None)
235270
{
236271
exitCode += (int)CommandExitCodes.CoverageBelowThreshold;
237272
var exceptionMessageBuilder = new StringBuilder();
238273
if ((thresholdTypeFlags & ThresholdTypeFlags.Line) != ThresholdTypeFlags.None)
239274
{
240-
exceptionMessageBuilder.AppendLine($"The {dThresholdStat.ToString().ToLower()} line coverage is below the specified {dThreshold}");
275+
exceptionMessageBuilder.AppendLine($"The {dThresholdStat.ToString().ToLower()} line coverage is below the specified {thresholdTypeFlagValues[ThresholdTypeFlags.Line]}");
241276
}
242277

243278
if ((thresholdTypeFlags & ThresholdTypeFlags.Branch) != ThresholdTypeFlags.None)
244279
{
245-
exceptionMessageBuilder.AppendLine($"The {dThresholdStat.ToString().ToLower()} branch coverage is below the specified {dThreshold}");
280+
exceptionMessageBuilder.AppendLine($"The {dThresholdStat.ToString().ToLower()} branch coverage is below the specified {thresholdTypeFlagValues[ThresholdTypeFlags.Branch]}");
246281
}
247282

248283
if ((thresholdTypeFlags & ThresholdTypeFlags.Method) != ThresholdTypeFlags.None)
249284
{
250-
exceptionMessageBuilder.AppendLine($"The {dThresholdStat.ToString().ToLower()} method coverage is below the specified {dThreshold}");
285+
exceptionMessageBuilder.AppendLine($"The {dThresholdStat.ToString().ToLower()} method coverage is below the specified {thresholdTypeFlagValues[ThresholdTypeFlags.Method]}");
251286
}
252-
253287
throw new Exception(exceptionMessageBuilder.ToString());
254288
}
255289

Diff for: src/coverlet.core/CoverageResult.cs

+21-30
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;

Diff for: src/coverlet.msbuild.tasks/CoverageResultTask.cs

+44-7
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; }
@@ -125,24 +125,59 @@ public override bool Execute()
125125
ReportItems = coverageReportPaths.ToArray();
126126

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

130130
foreach (var thresholdType in ThresholdType.Split(',').Select(t => t.Trim()))
131131
{
132132
if (thresholdType.Equals("line", StringComparison.OrdinalIgnoreCase))
133133
{
134134
thresholdTypeFlags |= ThresholdTypeFlags.Line;
135+
thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Line);
135136
}
136137
else if (thresholdType.Equals("branch", StringComparison.OrdinalIgnoreCase))
137138
{
138139
thresholdTypeFlags |= ThresholdTypeFlags.Branch;
140+
thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Branch);
139141
}
140142
else if (thresholdType.Equals("method", StringComparison.OrdinalIgnoreCase))
141143
{
142144
thresholdTypeFlags |= ThresholdTypeFlags.Method;
145+
thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Method);
143146
}
144147
}
148+
149+
Dictionary<ThresholdTypeFlags, double> thresholdTypeFlagValues = new Dictionary<ThresholdTypeFlags, double>();
150+
if (Threshold.Contains(','))
151+
{
152+
var thresholdValues = Threshold.Split(new char[] {','}, StringSplitOptions.RemoveEmptyEntries).Select(t => t.Trim());
153+
if(thresholdValues.Count() != thresholdTypeFlagQueue.Count())
154+
{
155+
throw new Exception($"Threshold type flag count ({thresholdTypeFlagQueue.Count()}) and values count ({thresholdValues.Count()}) doesn't match");
156+
}
145157

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

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

159193
var linePercentCalculation = summary.CalculateLineCoverage(result.Modules);
160194
var branchPercentCalculation = summary.CalculateBranchCoverage(result.Modules);
@@ -189,23 +223,26 @@ public override bool Execute()
189223

190224
Console.WriteLine(coverageTable.ToStringAlternative());
191225

192-
thresholdTypeFlags = result.GetThresholdTypesBelowThreshold(summary, Threshold, thresholdTypeFlags, thresholdStat);
226+
thresholdTypeFlags = result.GetThresholdTypesBelowThreshold(summary, thresholdTypeFlagValues, thresholdStat);
193227
if (thresholdTypeFlags != ThresholdTypeFlags.None)
194228
{
195229
var exceptionMessageBuilder = new StringBuilder();
196230
if ((thresholdTypeFlags & ThresholdTypeFlags.Line) != ThresholdTypeFlags.None)
197231
{
198-
exceptionMessageBuilder.AppendLine($"The {thresholdStat.ToString().ToLower()} line coverage is below the specified {Threshold}");
232+
exceptionMessageBuilder.AppendLine(
233+
$"The {thresholdStat.ToString().ToLower()} line coverage is below the specified {thresholdTypeFlagValues[ThresholdTypeFlags.Line]}");
199234
}
200235

201236
if ((thresholdTypeFlags & ThresholdTypeFlags.Branch) != ThresholdTypeFlags.None)
202237
{
203-
exceptionMessageBuilder.AppendLine($"The {thresholdStat.ToString().ToLower()} branch coverage is below the specified {Threshold}");
238+
exceptionMessageBuilder.AppendLine(
239+
$"The {thresholdStat.ToString().ToLower()} branch coverage is below the specified {thresholdTypeFlagValues[ThresholdTypeFlags.Branch]}");
204240
}
205241

206242
if ((thresholdTypeFlags & ThresholdTypeFlags.Method) != ThresholdTypeFlags.None)
207243
{
208-
exceptionMessageBuilder.AppendLine($"The {thresholdStat.ToString().ToLower()} method coverage is below the specified {Threshold}");
244+
exceptionMessageBuilder.AppendLine(
245+
$"The {thresholdStat.ToString().ToLower()} method coverage is below the specified {thresholdTypeFlagValues[ThresholdTypeFlags.Method]}");
209246
}
210247

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

0 commit comments

Comments
 (0)