Merge remote-tracking branch 'tonerdo/master' into cleaner-build
This commit is contained in:
@@ -136,12 +136,12 @@ The currently supported [TeamCity statistics](https://confluence.jetbrains.com/d
|
||||
| TeamCity Statistic Key | Description |
|
||||
| :--- | :--- |
|
||||
| CodeCoverageL | Line-level code coverage |
|
||||
| CodeCoverageC | Class-level code coverage |
|
||||
| CodeCoverageR | Branch-level code coverage |
|
||||
| CodeCoverageM | Method-level code coverage |
|
||||
| CodeCoverageAbsLTotal | The total number of lines |
|
||||
| CodeCoverageAbsLCovered | The number of covered lines |
|
||||
| CodeCoverageAbsCTotal | The total number of classes |
|
||||
| CodeCoverageAbsCCovered | The number of covered classes |
|
||||
| CodeCoverageAbsRTotal | The total number of branches |
|
||||
| CodeCoverageAbsRCovered | The number of covered branches |
|
||||
| CodeCoverageAbsMTotal | The total number of methods |
|
||||
| CodeCoverageAbsMCovered | The number of covered methods |
|
||||
|
||||
|
||||
@@ -24,97 +24,37 @@ EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{31084026-D563-4B91-BE71-174C4270CCF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{31084026-D563-4B91-BE71-174C4270CCF4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{31084026-D563-4B91-BE71-174C4270CCF4}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{31084026-D563-4B91-BE71-174C4270CCF4}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{31084026-D563-4B91-BE71-174C4270CCF4}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{31084026-D563-4B91-BE71-174C4270CCF4}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{31084026-D563-4B91-BE71-174C4270CCF4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{31084026-D563-4B91-BE71-174C4270CCF4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{31084026-D563-4B91-BE71-174C4270CCF4}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{31084026-D563-4B91-BE71-174C4270CCF4}.Release|x64.Build.0 = Release|Any CPU
|
||||
{31084026-D563-4B91-BE71-174C4270CCF4}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{31084026-D563-4B91-BE71-174C4270CCF4}.Release|x86.Build.0 = Release|Any CPU
|
||||
{FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|x64.Build.0 = Release|Any CPU
|
||||
{FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|x86.Build.0 = Release|Any CPU
|
||||
{E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|x64.Build.0 = Release|Any CPU
|
||||
{E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|x86.Build.0 = Release|Any CPU
|
||||
{F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|x64.Build.0 = Release|Any CPU
|
||||
{F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|x86.Build.0 = Release|Any CPU
|
||||
{AE117FAA-C21D-4F23-917E-0C8050614750}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{AE117FAA-C21D-4F23-917E-0C8050614750}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{AE117FAA-C21D-4F23-917E-0C8050614750}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{AE117FAA-C21D-4F23-917E-0C8050614750}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{AE117FAA-C21D-4F23-917E-0C8050614750}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{AE117FAA-C21D-4F23-917E-0C8050614750}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{AE117FAA-C21D-4F23-917E-0C8050614750}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{AE117FAA-C21D-4F23-917E-0C8050614750}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{AE117FAA-C21D-4F23-917E-0C8050614750}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{AE117FAA-C21D-4F23-917E-0C8050614750}.Release|x64.Build.0 = Release|Any CPU
|
||||
{AE117FAA-C21D-4F23-917E-0C8050614750}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{AE117FAA-C21D-4F23-917E-0C8050614750}.Release|x86.Build.0 = Release|Any CPU
|
||||
{C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Release|x64.Build.0 = Release|Any CPU
|
||||
{C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Release|x86.Build.0 = Release|Any CPU
|
||||
{FF16BD00-4BE7-41F3-95AE-F69B56E5E254}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FF16BD00-4BE7-41F3-95AE-F69B56E5E254}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FF16BD00-4BE7-41F3-95AE-F69B56E5E254}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{FF16BD00-4BE7-41F3-95AE-F69B56E5E254}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{FF16BD00-4BE7-41F3-95AE-F69B56E5E254}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{FF16BD00-4BE7-41F3-95AE-F69B56E5E254}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{FF16BD00-4BE7-41F3-95AE-F69B56E5E254}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FF16BD00-4BE7-41F3-95AE-F69B56E5E254}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{FF16BD00-4BE7-41F3-95AE-F69B56E5E254}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{FF16BD00-4BE7-41F3-95AE-F69B56E5E254}.Release|x64.Build.0 = Release|Any CPU
|
||||
{FF16BD00-4BE7-41F3-95AE-F69B56E5E254}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{FF16BD00-4BE7-41F3-95AE-F69B56E5E254}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -37,6 +37,7 @@ namespace Coverlet.Console
|
||||
CommandOption excludedSourceFiles = app.Option("--exclude-by-file", "Glob patterns specifying source files to exclude.", CommandOptionType.MultipleValue);
|
||||
CommandOption includeDirectories = app.Option("--include-directory", "Include directories containing additional assemblies to be instrumented.", CommandOptionType.MultipleValue);
|
||||
CommandOption excludeAttributes = app.Option("--exclude-by-attribute", "Attributes to exclude from code coverage.", CommandOptionType.MultipleValue);
|
||||
CommandOption singleHit = app.Option("--single-hit", "Specifies whether to limit code coverage hit reporting to a single hit for each location", CommandOptionType.NoValue);
|
||||
CommandOption mergeWith = app.Option("--merge-with", "Path to existing coverage result to merge.", CommandOptionType.SingleValue);
|
||||
CommandOption useSourceLink = app.Option("--use-source-link", "Specifies whether to use SourceLink URIs in place of file system paths.", CommandOptionType.NoValue);
|
||||
|
||||
@@ -48,7 +49,7 @@ namespace Coverlet.Console
|
||||
if (!target.HasValue())
|
||||
throw new CommandParsingException(app, "Target must be specified.");
|
||||
|
||||
Coverage coverage = new Coverage(module.Value, includeFilters.Values.ToArray(), includeDirectories.Values.ToArray(), excludeFilters.Values.ToArray(), excludedSourceFiles.Values.ToArray(), excludeAttributes.Values.ToArray(), mergeWith.Value(), useSourceLink.HasValue());
|
||||
Coverage coverage = new Coverage(module.Value, includeFilters.Values.ToArray(), includeDirectories.Values.ToArray(), excludeFilters.Values.ToArray(), excludedSourceFiles.Values.ToArray(), excludeAttributes.Values.ToArray(), singleHit.HasValue(), mergeWith.Value(), useSourceLink.HasValue());
|
||||
coverage.PrepareModules();
|
||||
|
||||
Process process = new Process();
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace Coverlet.Core
|
||||
private string[] _excludeFilters;
|
||||
private string[] _excludedSourceFiles;
|
||||
private string[] _excludeAttributes;
|
||||
private bool _singleHit;
|
||||
private string _mergeWith;
|
||||
private bool _useSourceLink;
|
||||
private List<InstrumenterResult> _results;
|
||||
@@ -31,7 +32,7 @@ namespace Coverlet.Core
|
||||
get { return _identifier; }
|
||||
}
|
||||
|
||||
public Coverage(string module, string[] includeFilters, string[] includeDirectories, string[] excludeFilters, string[] excludedSourceFiles, string[] excludeAttributes, string mergeWith, bool useSourceLink)
|
||||
public Coverage(string module, string[] includeFilters, string[] includeDirectories, string[] excludeFilters, string[] excludedSourceFiles, string[] excludeAttributes, bool singleHit, string mergeWith, bool useSourceLink)
|
||||
{
|
||||
_module = module;
|
||||
_includeFilters = includeFilters;
|
||||
@@ -39,6 +40,7 @@ namespace Coverlet.Core
|
||||
_excludeFilters = excludeFilters;
|
||||
_excludedSourceFiles = excludedSourceFiles;
|
||||
_excludeAttributes = excludeAttributes;
|
||||
_singleHit = singleHit;
|
||||
_mergeWith = mergeWith;
|
||||
_useSourceLink = useSourceLink;
|
||||
|
||||
@@ -59,7 +61,7 @@ namespace Coverlet.Core
|
||||
!InstrumentationHelper.IsModuleIncluded(module, _includeFilters))
|
||||
continue;
|
||||
|
||||
var instrumenter = new Instrumenter(module, _identifier, _excludeFilters, _includeFilters, excludes, _excludeAttributes);
|
||||
var instrumenter = new Instrumenter(module, _identifier, _excludeFilters, _includeFilters, excludes, _excludeAttributes, _singleHit);
|
||||
if (instrumenter.CanInstrument())
|
||||
{
|
||||
InstrumentationHelper.BackupOriginalModule(module, _identifier);
|
||||
|
||||
@@ -48,11 +48,7 @@ namespace Coverlet.Core.Helpers
|
||||
}
|
||||
|
||||
// The module's name must be unique.
|
||||
// Add the test module itself to exclude it from the files enumeration.
|
||||
var uniqueModules = new HashSet<string>
|
||||
{
|
||||
Path.GetFileName(module)
|
||||
};
|
||||
var uniqueModules = new HashSet<string>();
|
||||
|
||||
return dirs.SelectMany(d => Directory.EnumerateFiles(d))
|
||||
.Where(m => IsAssembly(m) && uniqueModules.Add(Path.GetFileName(m)))
|
||||
@@ -86,12 +82,20 @@ namespace Coverlet.Core.Helpers
|
||||
public static void BackupOriginalModule(string module, string identifier)
|
||||
{
|
||||
var backupPath = GetBackupPath(module, identifier);
|
||||
var backupSymbolPath = Path.ChangeExtension(backupPath, ".pdb");
|
||||
File.Copy(module, backupPath, true);
|
||||
|
||||
var symbolFile = Path.ChangeExtension(module, ".pdb");
|
||||
if (File.Exists(symbolFile))
|
||||
{
|
||||
File.Copy(symbolFile, backupSymbolPath, true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void RestoreOriginalModule(string module, string identifier)
|
||||
{
|
||||
var backupPath = GetBackupPath(module, identifier);
|
||||
var backupSymbolPath = Path.ChangeExtension(backupPath, ".pdb");
|
||||
|
||||
// Restore the original module - retry up to 10 times, since the destination file could be locked
|
||||
// See: https://github.com/tonerdo/coverlet/issues/25
|
||||
@@ -102,6 +106,12 @@ namespace Coverlet.Core.Helpers
|
||||
File.Copy(backupPath, module, true);
|
||||
File.Delete(backupPath);
|
||||
}, retryStrategy, 10);
|
||||
|
||||
RetryHelper.Retry(() =>
|
||||
{
|
||||
File.Copy(backupSymbolPath, Path.ChangeExtension(module, ".pdb"), true);
|
||||
File.Delete(backupSymbolPath);
|
||||
}, retryStrategy, 10);
|
||||
}
|
||||
|
||||
public static void DeleteHitsFile(string path)
|
||||
|
||||
@@ -23,17 +23,19 @@ namespace Coverlet.Core.Instrumentation
|
||||
private readonly string[] _includeFilters;
|
||||
private readonly string[] _excludedFiles;
|
||||
private readonly string[] _excludedAttributes;
|
||||
private readonly bool _singleHit;
|
||||
private readonly bool _isCoreLibrary;
|
||||
private InstrumenterResult _result;
|
||||
private FieldDefinition _customTrackerHitsArray;
|
||||
private FieldDefinition _customTrackerHitsFilePath;
|
||||
private FieldDefinition _customTrackerSingleHit;
|
||||
private ILProcessor _customTrackerClassConstructorIl;
|
||||
private TypeDefinition _customTrackerTypeDef;
|
||||
private MethodReference _customTrackerRegisterUnloadEventsMethod;
|
||||
private MethodReference _customTrackerRecordHitMethod;
|
||||
private List<string> _asyncMachineStateMethod;
|
||||
|
||||
public Instrumenter(string module, string identifier, string[] excludeFilters, string[] includeFilters, string[] excludedFiles, string[] excludedAttributes)
|
||||
public Instrumenter(string module, string identifier, string[] excludeFilters, string[] includeFilters, string[] excludedFiles, string[] excludedAttributes, bool singleHit)
|
||||
{
|
||||
_module = module;
|
||||
_identifier = identifier;
|
||||
@@ -41,6 +43,7 @@ namespace Coverlet.Core.Instrumentation
|
||||
_includeFilters = includeFilters;
|
||||
_excludedFiles = excludedFiles ?? Array.Empty<string>();
|
||||
_excludedAttributes = excludedAttributes;
|
||||
_singleHit = singleHit;
|
||||
|
||||
_isCoreLibrary = Path.GetFileNameWithoutExtension(_module) == "System.Private.CoreLib";
|
||||
}
|
||||
@@ -125,6 +128,8 @@ namespace Coverlet.Core.Instrumentation
|
||||
_customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Stsfld, _customTrackerHitsArray));
|
||||
_customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Ldstr, _result.HitsFilePath));
|
||||
_customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Stsfld, _customTrackerHitsFilePath));
|
||||
_customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(_singleHit ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0));
|
||||
_customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Stsfld, _customTrackerSingleHit));
|
||||
|
||||
if (containsAppContext)
|
||||
{
|
||||
@@ -147,7 +152,7 @@ namespace Coverlet.Core.Instrumentation
|
||||
onProcessExitIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Call, customTrackerUnloadModule));
|
||||
}
|
||||
|
||||
module.Write(stream);
|
||||
module.Write(stream, new WriterParameters { WriteSymbols = true });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -170,10 +175,12 @@ namespace Coverlet.Core.Instrumentation
|
||||
|
||||
_customTrackerTypeDef.Fields.Add(fieldClone);
|
||||
|
||||
if (fieldClone.Name == "HitsArray")
|
||||
if (fieldClone.Name == nameof(ModuleTrackerTemplate.HitsArray))
|
||||
_customTrackerHitsArray = fieldClone;
|
||||
else if (fieldClone.Name == "HitsFilePath")
|
||||
else if (fieldClone.Name == nameof(ModuleTrackerTemplate.HitsFilePath))
|
||||
_customTrackerHitsFilePath = fieldClone;
|
||||
else if (fieldClone.Name == nameof(ModuleTrackerTemplate.SingleHit))
|
||||
_customTrackerSingleHit = fieldClone;
|
||||
}
|
||||
|
||||
foreach (MethodDefinition methodDef in moduleTrackerTemplate.Methods)
|
||||
@@ -426,9 +433,20 @@ namespace Coverlet.Core.Instrumentation
|
||||
{
|
||||
if (_customTrackerRecordHitMethod == null)
|
||||
{
|
||||
var recordHitMethodName = _isCoreLibrary
|
||||
? nameof(ModuleTrackerTemplate.RecordHitInCoreLibrary)
|
||||
: nameof(ModuleTrackerTemplate.RecordHit);
|
||||
string recordHitMethodName;
|
||||
if (_singleHit)
|
||||
{
|
||||
recordHitMethodName = _isCoreLibrary
|
||||
? nameof(ModuleTrackerTemplate.RecordSingleHitInCoreLibrary)
|
||||
: nameof(ModuleTrackerTemplate.RecordSingleHit);
|
||||
}
|
||||
else
|
||||
{
|
||||
recordHitMethodName = _isCoreLibrary
|
||||
? nameof(ModuleTrackerTemplate.RecordHitInCoreLibrary)
|
||||
: nameof(ModuleTrackerTemplate.RecordHit);
|
||||
}
|
||||
|
||||
_customTrackerRecordHitMethod = new MethodReference(
|
||||
recordHitMethodName, method.Module.TypeSystem.Void, _customTrackerTypeDef);
|
||||
_customTrackerRecordHitMethod.Parameters.Add(new ParameterDefinition("hitLocationIndex", ParameterAttributes.None, method.Module.TypeSystem.Int32));
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@@ -76,8 +77,8 @@ namespace Coverlet.Core.Reporters
|
||||
|
||||
method.Add(new XAttribute("cyclomaticComplexity", methCyclomaticComplexity.ToString()));
|
||||
method.Add(new XAttribute("nPathComplexity", "0"));
|
||||
method.Add(new XAttribute("sequenceCoverage", methLineCoverage.Percent.ToString()));
|
||||
method.Add(new XAttribute("branchCoverage", methBranchCoverage.Percent.ToString()));
|
||||
method.Add(new XAttribute("sequenceCoverage", Math.Round(methLineCoverage.Percent * 100, 2).ToString("G", CultureInfo.InvariantCulture)));
|
||||
method.Add(new XAttribute("branchCoverage", Math.Round(methBranchCoverage.Percent * 100, 2).ToString("G", CultureInfo.InvariantCulture)));
|
||||
method.Add(new XAttribute("isConstructor", meth.Key.Contains("ctor").ToString()));
|
||||
method.Add(new XAttribute("isGetter", meth.Key.Contains("get_").ToString()));
|
||||
method.Add(new XAttribute("isSetter", meth.Key.Contains("set_").ToString()));
|
||||
@@ -157,8 +158,8 @@ namespace Coverlet.Core.Reporters
|
||||
methodSummary.Add(new XAttribute("visitedSequencePoints", methLineCoverage.Covered.ToString()));
|
||||
methodSummary.Add(new XAttribute("numBranchPoints", methBranchCoverage.Total.ToString()));
|
||||
methodSummary.Add(new XAttribute("visitedBranchPoints", methBranchCoverage.Covered.ToString()));
|
||||
methodSummary.Add(new XAttribute("sequenceCoverage", methLineCoverage.Percent.ToString()));
|
||||
methodSummary.Add(new XAttribute("branchCoverage", methBranchCoverage.Percent.ToString()));
|
||||
methodSummary.Add(new XAttribute("sequenceCoverage", Math.Round(methLineCoverage.Percent * 100, 2).ToString("G", CultureInfo.InvariantCulture)));
|
||||
methodSummary.Add(new XAttribute("branchCoverage", Math.Round(methBranchCoverage.Percent * 100, 2).ToString("G", CultureInfo.InvariantCulture)));
|
||||
methodSummary.Add(new XAttribute("maxCyclomaticComplexity", methCyclomaticComplexity.ToString()));
|
||||
methodSummary.Add(new XAttribute("minCyclomaticComplexity", methCyclomaticComplexity.ToString()));
|
||||
methodSummary.Add(new XAttribute("visitedClasses", "0"));
|
||||
@@ -191,8 +192,8 @@ namespace Coverlet.Core.Reporters
|
||||
classSummary.Add(new XAttribute("visitedSequencePoints", classLineCoverage.Covered.ToString()));
|
||||
classSummary.Add(new XAttribute("numBranchPoints", classBranchCoverage.Total.ToString()));
|
||||
classSummary.Add(new XAttribute("visitedBranchPoints", classBranchCoverage.Covered.ToString()));
|
||||
classSummary.Add(new XAttribute("sequenceCoverage", classLineCoverage.Percent.ToString()));
|
||||
classSummary.Add(new XAttribute("branchCoverage", classBranchCoverage.Percent.ToString()));
|
||||
classSummary.Add(new XAttribute("sequenceCoverage", Math.Round(classLineCoverage.Percent * 100, 2).ToString("G", CultureInfo.InvariantCulture)));
|
||||
classSummary.Add(new XAttribute("branchCoverage", Math.Round(classBranchCoverage.Percent * 100, 2).ToString("G", CultureInfo.InvariantCulture)));
|
||||
classSummary.Add(new XAttribute("maxCyclomaticComplexity", classMaxCyclomaticComplexity.ToString()));
|
||||
classSummary.Add(new XAttribute("minCyclomaticComplexity", classMinCyclomaticComplexity.ToString()));
|
||||
classSummary.Add(new XAttribute("visitedClasses", classVisited ? "1" : "0"));
|
||||
@@ -214,7 +215,7 @@ namespace Coverlet.Core.Reporters
|
||||
}
|
||||
|
||||
var moduleLineCoverage = summary.CalculateLineCoverage(result.Modules);
|
||||
var moduleBranchCoverage = summary.CalculateLineCoverage(result.Modules);
|
||||
var moduleBranchCoverage = summary.CalculateBranchCoverage(result.Modules);
|
||||
var moduleMaxCyclomaticComplexity = summary.CalculateMaxCyclomaticComplexity(result.Modules);
|
||||
var moduleMinCyclomaticComplexity = summary.CalculateMinCyclomaticComplexity(result.Modules);
|
||||
|
||||
@@ -222,8 +223,8 @@ namespace Coverlet.Core.Reporters
|
||||
coverageSummary.Add(new XAttribute("visitedSequencePoints", moduleLineCoverage.Covered.ToString()));
|
||||
coverageSummary.Add(new XAttribute("numBranchPoints", moduleBranchCoverage.Total.ToString()));
|
||||
coverageSummary.Add(new XAttribute("visitedBranchPoints", moduleBranchCoverage.Covered.ToString()));
|
||||
coverageSummary.Add(new XAttribute("sequenceCoverage", moduleLineCoverage.Percent.ToString()));
|
||||
coverageSummary.Add(new XAttribute("branchCoverage", moduleBranchCoverage.Percent.ToString()));
|
||||
coverageSummary.Add(new XAttribute("sequenceCoverage", Math.Round(moduleLineCoverage.Percent * 100, 2).ToString("G", CultureInfo.InvariantCulture)));
|
||||
coverageSummary.Add(new XAttribute("branchCoverage", Math.Round(moduleBranchCoverage.Percent * 100, 2).ToString("G", CultureInfo.InvariantCulture)));
|
||||
coverageSummary.Add(new XAttribute("maxCyclomaticComplexity", moduleMaxCyclomaticComplexity.ToString()));
|
||||
coverageSummary.Add(new XAttribute("minCyclomaticComplexity", moduleMinCyclomaticComplexity.ToString()));
|
||||
coverageSummary.Add(new XAttribute("visitedClasses", visitedClasses.ToString()));
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace Coverlet.MSbuild.Tasks
|
||||
private string _exclude;
|
||||
private string _excludeByFile;
|
||||
private string _excludeByAttribute;
|
||||
private bool _singleHit;
|
||||
private string _mergeWith;
|
||||
private bool _useSourceLink;
|
||||
|
||||
@@ -59,6 +60,12 @@ namespace Coverlet.MSbuild.Tasks
|
||||
set { _excludeByAttribute = value; }
|
||||
}
|
||||
|
||||
public bool SingleHit
|
||||
{
|
||||
get { return _singleHit; }
|
||||
set { _singleHit = value; }
|
||||
}
|
||||
|
||||
public string MergeWith
|
||||
{
|
||||
get { return _mergeWith; }
|
||||
@@ -81,7 +88,7 @@ namespace Coverlet.MSbuild.Tasks
|
||||
var excludedSourceFiles = _excludeByFile?.Split(',');
|
||||
var excludeAttributes = _excludeByAttribute?.Split(',');
|
||||
|
||||
_coverage = new Coverage(_path, includeFilters, includeDirectories, excludeFilters, excludedSourceFiles, excludeAttributes, _mergeWith, _useSourceLink);
|
||||
_coverage = new Coverage(_path, includeFilters, includeDirectories, excludeFilters, excludedSourceFiles, excludeAttributes, _singleHit, _mergeWith, _useSourceLink);
|
||||
_coverage.PrepareModules();
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
<Exclude Condition="$(Exclude) == ''"></Exclude>
|
||||
<ExcludeByFile Condition="$(ExcludeByFile) == ''"></ExcludeByFile>
|
||||
<ExcludeByAttribute Condition="$(ExcludeByAttribute) == ''"></ExcludeByAttribute>
|
||||
<CoverletSingleHit Condition="'$(CoverletSingleHit)' == ''">false</CoverletSingleHit>
|
||||
<MergeWith Condition="$(MergeWith) == ''"></MergeWith>
|
||||
<UseSourceLink Condition="$(UseSourceLink) == ''">false</UseSourceLink>
|
||||
<CoverletOutputFormat Condition="$(CoverletOutputFormat) == ''">json</CoverletOutputFormat>
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
Exclude="$(Exclude)"
|
||||
ExcludeByFile="$(ExcludeByFile)"
|
||||
ExcludeByAttribute="$(ExcludeByAttribute)"
|
||||
SingleHit="$(CoverletSingleHit)"
|
||||
MergeWith="$(MergeWith)"
|
||||
UseSourceLink="$(UseSourceLink)" />
|
||||
</Target>
|
||||
@@ -25,6 +26,7 @@
|
||||
Exclude="$(Exclude)"
|
||||
ExcludeByFile="$(ExcludeByFile)"
|
||||
ExcludeByAttribute="$(ExcludeByAttribute)"
|
||||
SingleHit="$(CoverletSingleHit)"
|
||||
MergeWith="$(MergeWith)"
|
||||
UseSourceLink="$(UseSourceLink)" />
|
||||
</Target>
|
||||
|
||||
@@ -18,6 +18,7 @@ namespace Coverlet.Core.Instrumentation
|
||||
{
|
||||
public static string HitsFilePath;
|
||||
public static int[] HitsArray;
|
||||
public static bool SingleHit;
|
||||
|
||||
static ModuleTrackerTemplate()
|
||||
{
|
||||
@@ -50,6 +51,25 @@ namespace Coverlet.Core.Instrumentation
|
||||
Interlocked.Increment(ref HitsArray[hitLocationIndex]);
|
||||
}
|
||||
|
||||
public static void RecordSingleHitInCoreLibrary(int hitLocationIndex)
|
||||
{
|
||||
// Make sure to avoid recording if this is a call to RecordHit within the AppDomain setup code in an
|
||||
// instrumented build of System.Private.CoreLib.
|
||||
if (HitsArray is null)
|
||||
return;
|
||||
|
||||
ref int location = ref HitsArray[hitLocationIndex];
|
||||
if (location == 0)
|
||||
location = 1;
|
||||
}
|
||||
|
||||
public static void RecordSingleHit(int hitLocationIndex)
|
||||
{
|
||||
ref int location = ref HitsArray[hitLocationIndex];
|
||||
if (location == 0)
|
||||
location = 1;
|
||||
}
|
||||
|
||||
public static void UnloadModule(object sender, EventArgs e)
|
||||
{
|
||||
// Claim the current hits array and reset it to prevent double-counting scenarios.
|
||||
@@ -99,7 +119,10 @@ namespace Coverlet.Core.Instrumentation
|
||||
{
|
||||
int oldHitCount = br.ReadInt32();
|
||||
bw.Seek(-sizeof(int), SeekOrigin.Current);
|
||||
bw.Write(hitsArray[i] + oldHitCount);
|
||||
if (SingleHit)
|
||||
bw.Write(hitsArray[i] + oldHitCount > 0 ? 1 : 0);
|
||||
else
|
||||
bw.Write(hitsArray[i] + oldHitCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,10 +24,7 @@ namespace Coverlet.Core.Tests
|
||||
|
||||
// TODO: Find a way to mimick hits
|
||||
|
||||
// Since Coverage only instruments dependancies, we need a fake module here
|
||||
var testModule = Path.Combine(directory.FullName, "test.module.dll");
|
||||
|
||||
var coverage = new Coverage(testModule, Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), string.Empty, false);
|
||||
var coverage = new Coverage(Path.Combine(directory.FullName, Path.GetFileName(module)), Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), false, string.Empty, false);
|
||||
coverage.PrepareModules();
|
||||
|
||||
var result = coverage.GetCoverageResult();
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Coverlet.Core.Helpers.Tests
|
||||
{
|
||||
string module = typeof(InstrumentationHelperTests).Assembly.Location;
|
||||
var modules = InstrumentationHelper.GetCoverableModules(module, Array.Empty<string>());
|
||||
Assert.False(Array.Exists(modules, m => m == module));
|
||||
Assert.True(Array.Exists(modules, m => m == module));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Coverlet.Core.Instrumentation.Tests
|
||||
foreach (var file in files)
|
||||
File.Copy(Path.Combine(OriginalFilesDir, file), Path.Combine(TestFilesDir, file), overwrite: true);
|
||||
|
||||
Instrumenter instrumenter = new Instrumenter(Path.Combine(TestFilesDir, files[0]), "_coverlet_instrumented", Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>());
|
||||
Instrumenter instrumenter = new Instrumenter(Path.Combine(TestFilesDir, files[0]), "_coverlet_instrumented", Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), false);
|
||||
Assert.True(instrumenter.CanInstrument());
|
||||
var result = instrumenter.Instrument();
|
||||
Assert.NotNull(result);
|
||||
@@ -119,7 +119,7 @@ namespace Coverlet.Core.Instrumentation.Tests
|
||||
File.Copy(pdb, Path.Combine(directory.FullName, destPdb), true);
|
||||
|
||||
module = Path.Combine(directory.FullName, destModule);
|
||||
Instrumenter instrumenter = new Instrumenter(module, identifier, Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), attributesToIgnore);
|
||||
Instrumenter instrumenter = new Instrumenter(module, identifier, Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), attributesToIgnore, false);
|
||||
return new InstrumenterTest
|
||||
{
|
||||
Instrumenter = instrumenter,
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace Coverlet.Core.Reporters.Tests
|
||||
@@ -16,7 +20,11 @@ namespace Coverlet.Core.Reporters.Tests
|
||||
result.Modules.Add("Coverlet.Core.Reporters.Tests", CreateFirstDocuments());
|
||||
|
||||
OpenCoverReporter reporter = new OpenCoverReporter();
|
||||
Assert.NotEqual(string.Empty, reporter.Report(result));
|
||||
string report = reporter.Report(result);
|
||||
Assert.NotEmpty(report);
|
||||
XDocument doc = XDocument.Load(new MemoryStream(Encoding.UTF8.GetBytes(report)));
|
||||
Assert.Empty(doc.Descendants().Attributes("sequenceCoverage").Where(v => v.Value != "33.3"));
|
||||
Assert.Empty(doc.Descendants().Attributes("branchCoverage").Where(v => v.Value != "25"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -42,10 +50,13 @@ namespace Coverlet.Core.Reporters.Tests
|
||||
Lines lines = new Lines();
|
||||
lines.Add(1, 1);
|
||||
lines.Add(2, 0);
|
||||
lines.Add(3, 0);
|
||||
|
||||
Branches branches = new Branches();
|
||||
branches.Add(new BranchInfo { Line = 1, Hits = 1, Offset = 23, EndOffset = 24, Path = 0, Ordinal = 1 });
|
||||
branches.Add(new BranchInfo { Line = 1, Hits = 0, Offset = 23, EndOffset = 27, Path = 1, Ordinal = 2 });
|
||||
branches.Add(new BranchInfo { Line = 1, Hits = 0, Offset = 40, EndOffset = 41, Path = 0, Ordinal = 3 });
|
||||
branches.Add(new BranchInfo { Line = 1, Hits = 0, Offset = 40, EndOffset = 44, Path = 1, Ordinal = 4 });
|
||||
|
||||
Methods methods = new Methods();
|
||||
var methodString = "System.Void Coverlet.Core.Reporters.Tests.OpenCoverReporterTests.TestReport()";
|
||||
|
||||
@@ -122,6 +122,14 @@ namespace Coverlet.Core.Samples.Tests
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method is used by a unit test that verifies the behavior of the instrumentation process on an assembly
|
||||
/// which is not instrumented. Excluding this method from code coverage prevents the bytecode for this reference
|
||||
/// method from getting modified prior to test execution so it retains its original form for the test. This is
|
||||
/// not a problem for the test because the instrumentation process only runs on assemblies which have not
|
||||
/// already been instrumented.
|
||||
/// </summary>
|
||||
[ExcludeFromCodeCoverage]
|
||||
public void HasSimpleTaskWithLambda()
|
||||
{
|
||||
var t = new Task(() => { });
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Coverlet.Core.Symbols.Tests
|
||||
{
|
||||
// arrange
|
||||
var type = _module.Types.First(x => x.FullName == typeof(DeclaredConstructorClass).FullName);
|
||||
var method = type.Methods.First(x => x.FullName.Contains("::HasSingleDecision"));
|
||||
var method = type.Methods.First(x => x.FullName.Contains($"::{nameof(DeclaredConstructorClass.HasSingleDecision)}"));
|
||||
|
||||
// act
|
||||
var points = CecilSymbolHelper.GetBranchPoints(method);
|
||||
@@ -50,7 +50,7 @@ namespace Coverlet.Core.Symbols.Tests
|
||||
{
|
||||
// arrange
|
||||
var type = _module.Types.First(x => x.FullName == typeof(DeclaredConstructorClass).FullName);
|
||||
var method = type.Methods.First(x => x.FullName.Contains("::HasSimpleUsingStatement"));
|
||||
var method = type.Methods.First(x => x.FullName.Contains($"::{nameof(DeclaredConstructorClass.HasSimpleUsingStatement)}"));
|
||||
|
||||
// act
|
||||
var points = CecilSymbolHelper.GetBranchPoints(method);
|
||||
@@ -63,7 +63,7 @@ namespace Coverlet.Core.Symbols.Tests
|
||||
{
|
||||
// arrange
|
||||
var type = _module.Types.First(x => x.FullName == typeof(DeclaredConstructorClass).FullName);
|
||||
var method = type.Methods.First(x => x.FullName.Contains("::HasSimpleTaskWithLambda"));
|
||||
var method = type.Methods.First(x => x.FullName.Contains($"::{nameof(DeclaredConstructorClass.HasSimpleTaskWithLambda)}"));
|
||||
|
||||
// act
|
||||
var points = CecilSymbolHelper.GetBranchPoints(method);
|
||||
@@ -76,7 +76,7 @@ namespace Coverlet.Core.Symbols.Tests
|
||||
{
|
||||
// arrange
|
||||
var type = _module.Types.First(x => x.FullName == typeof(DeclaredConstructorClass).FullName);
|
||||
var method = type.Methods.First(x => x.FullName.Contains("::HasTwoDecisions"));
|
||||
var method = type.Methods.First(x => x.FullName.Contains($"::{nameof(DeclaredConstructorClass.HasTwoDecisions)}"));
|
||||
|
||||
// act
|
||||
var points = CecilSymbolHelper.GetBranchPoints(method);
|
||||
@@ -95,7 +95,7 @@ namespace Coverlet.Core.Symbols.Tests
|
||||
{
|
||||
// arrange
|
||||
var type = _module.Types.First(x => x.FullName == typeof(DeclaredConstructorClass).FullName);
|
||||
var method = type.Methods.First(x => x.FullName.Contains("::HasCompleteIf"));
|
||||
var method = type.Methods.First(x => x.FullName.Contains($"::{nameof(DeclaredConstructorClass.HasCompleteIf)}"));
|
||||
|
||||
// act
|
||||
var points = CecilSymbolHelper.GetBranchPoints(method);
|
||||
@@ -113,7 +113,7 @@ namespace Coverlet.Core.Symbols.Tests
|
||||
{
|
||||
// arrange
|
||||
var type = _module.Types.First(x => x.FullName == typeof(DeclaredConstructorClass).FullName);
|
||||
var method = type.Methods.First(x => x.FullName.Contains("::HasSwitch"));
|
||||
var method = type.Methods.First(x => x.FullName.Contains($"::{nameof(DeclaredConstructorClass.HasSwitch)}"));
|
||||
|
||||
// act
|
||||
var points = CecilSymbolHelper.GetBranchPoints(method);
|
||||
@@ -136,7 +136,7 @@ namespace Coverlet.Core.Symbols.Tests
|
||||
{
|
||||
// arrange
|
||||
var type = _module.Types.First(x => x.FullName == typeof(DeclaredConstructorClass).FullName);
|
||||
var method = type.Methods.First(x => x.FullName.Contains("::HasSwitchWithDefault"));
|
||||
var method = type.Methods.First(x => x.FullName.Contains($"::{nameof(DeclaredConstructorClass.HasSwitchWithDefault)}"));
|
||||
|
||||
// act
|
||||
var points = CecilSymbolHelper.GetBranchPoints(method);
|
||||
@@ -159,7 +159,7 @@ namespace Coverlet.Core.Symbols.Tests
|
||||
{
|
||||
// arrange
|
||||
var type = _module.Types.First(x => x.FullName == typeof(DeclaredConstructorClass).FullName);
|
||||
var method = type.Methods.First(x => x.FullName.Contains("::HasSwitchWithBreaks"));
|
||||
var method = type.Methods.First(x => x.FullName.Contains($"::{nameof(DeclaredConstructorClass.HasSwitchWithBreaks)}"));
|
||||
|
||||
// act
|
||||
var points = CecilSymbolHelper.GetBranchPoints(method);
|
||||
@@ -182,7 +182,7 @@ namespace Coverlet.Core.Symbols.Tests
|
||||
{
|
||||
// arrange
|
||||
var type = _module.Types.First(x => x.FullName == typeof(DeclaredConstructorClass).FullName);
|
||||
var method = type.Methods.First(x => x.FullName.Contains("::HasSwitchWithMultipleCases"));
|
||||
var method = type.Methods.First(x => x.FullName.Contains($"::{nameof(DeclaredConstructorClass.HasSwitchWithMultipleCases)}"));
|
||||
|
||||
// act
|
||||
var points = CecilSymbolHelper.GetBranchPoints(method);
|
||||
@@ -226,7 +226,7 @@ namespace Coverlet.Core.Symbols.Tests
|
||||
{
|
||||
// arrange
|
||||
var type = _module.Types.First(x => x.FullName == typeof(DeclaredConstructorClass).FullName);
|
||||
var method = type.Methods.First(x => x.FullName.Contains("::UsingWithException_Issue243"));
|
||||
var method = type.Methods.First(x => x.FullName.Contains($"::{nameof(DeclaredConstructorClass.UsingWithException_Issue243)}"));
|
||||
|
||||
// check that the method is laid out the way we discovered it to be during the defect
|
||||
// @see https://github.com/OpenCover/opencover/issues/243
|
||||
|
||||
Reference in New Issue
Block a user