Merge remote-tracking branch 'tonerdo/master' into cleaner-build

This commit is contained in:
Sam Harwell
2019-02-09 12:43:58 -06:00
17 changed files with 128 additions and 107 deletions
+3 -3
View File
@@ -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 |
-60
View File
@@ -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
+2 -1
View File
@@ -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();
+4 -2
View File
@@ -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>
+24 -1
View File
@@ -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);
}
}
}
+1 -4
View File
@@ -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