Inject InstrumentationHelper (#531)

Inject InstrumentationHelper
This commit is contained in:
Marco Rossignoli
2019-09-10 12:56:04 +02:00
committed by GitHub
parent b4701d260b
commit 1bb08bb518
14 changed files with 164 additions and 109 deletions
@@ -1,5 +1,6 @@
using Coverlet.Collector.Utilities.Interfaces;
using Coverlet.Core;
using Coverlet.Core.Abstracts;
using Coverlet.Core.Logging;
namespace Coverlet.Collector.DataCollection
@@ -28,7 +29,8 @@ namespace Coverlet.Collector.DataCollection
settings.SingleHit,
settings.MergeWith,
settings.UseSourceLink,
coverletLogger);
coverletLogger,
(IInstrumentationHelper)DependencyInjection.Current.GetService(typeof(IInstrumentationHelper)));
}
/// <summary>
+3 -1
View File
@@ -8,6 +8,7 @@ using System.Text;
using ConsoleTables;
using Coverlet.Console.Logging;
using Coverlet.Core;
using Coverlet.Core.Abstracts;
using Coverlet.Core.Enums;
using Coverlet.Core.Reporters;
using McMaster.Extensions.CommandLineUtils;
@@ -69,7 +70,8 @@ namespace Coverlet.Console
singleHit.HasValue(),
mergeWith.Value(),
useSourceLink.HasValue(),
logger);
logger,
(IInstrumentationHelper)DependencyInjection.Current.GetService(typeof(IInstrumentationHelper)));
coverage.PrepareModules();
Process process = new Process();
@@ -0,0 +1,18 @@
namespace Coverlet.Core.Abstracts
{
public interface IInstrumentationHelper
{
void BackupOriginalModule(string module, string identifier);
void DeleteHitsFile(string path);
string[] GetCoverableModules(string module, string[] directories, bool includeTestAssembly);
bool HasPdb(string module, out bool embedded);
bool IsModuleExcluded(string module, string[] excludeFilters);
bool IsModuleIncluded(string module, string[] includeFilters);
bool IsValidFilterExpression(string filter);
bool IsTypeExcluded(string module, string type, string[] excludeFilters);
bool IsTypeIncluded(string module, string type, string[] includeFilters);
void RestoreOriginalModule(string module, string identifier);
bool EmbeddedPortablePdbHasLocalSource(string module);
bool IsLocalMethod(string method);
}
}
+17 -13
View File
@@ -2,7 +2,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Coverlet.Core.Abstracts;
using Coverlet.Core.Helpers;
using Coverlet.Core.Instrumentation;
using Coverlet.Core.Logging;
@@ -26,6 +26,7 @@ namespace Coverlet.Core
private string _mergeWith;
private bool _useSourceLink;
private ILogger _logger;
private IInstrumentationHelper _instrumentationHelper;
private List<InstrumenterResult> _results;
public string Identifier
@@ -43,7 +44,8 @@ namespace Coverlet.Core
bool singleHit,
string mergeWith,
bool useSourceLink,
ILogger logger)
ILogger logger,
IInstrumentationHelper instrumentationHelper)
{
_module = module;
_includeFilters = includeFilters;
@@ -56,12 +58,13 @@ namespace Coverlet.Core
_mergeWith = mergeWith;
_useSourceLink = useSourceLink;
_logger = logger;
_instrumentationHelper = instrumentationHelper;
_identifier = Guid.NewGuid().ToString();
_results = new List<InstrumenterResult>();
}
public Coverage(CoveragePrepareResult prepareResult, ILogger logger)
public Coverage(CoveragePrepareResult prepareResult, ILogger logger, IInstrumentationHelper instrumentationHelper)
{
_identifier = prepareResult.Identifier;
_module = prepareResult.Module;
@@ -69,31 +72,32 @@ namespace Coverlet.Core
_useSourceLink = prepareResult.UseSourceLink;
_results = new List<InstrumenterResult>(prepareResult.Results);
_logger = logger;
_instrumentationHelper = instrumentationHelper;
}
public CoveragePrepareResult PrepareModules()
{
string[] modules = InstrumentationHelper.GetCoverableModules(_module, _includeDirectories, _includeTestAssembly);
string[] modules = _instrumentationHelper.GetCoverableModules(_module, _includeDirectories, _includeTestAssembly);
Array.ForEach(_excludeFilters ?? Array.Empty<string>(), filter => _logger.LogVerbose($"Excluded module filter '{filter}'"));
Array.ForEach(_includeFilters ?? Array.Empty<string>(), filter => _logger.LogVerbose($"Included module filter '{filter}'"));
_excludeFilters = _excludeFilters?.Where(f => InstrumentationHelper.IsValidFilterExpression(f)).ToArray();
_includeFilters = _includeFilters?.Where(f => InstrumentationHelper.IsValidFilterExpression(f)).ToArray();
_excludeFilters = _excludeFilters?.Where(f => _instrumentationHelper.IsValidFilterExpression(f)).ToArray();
_includeFilters = _includeFilters?.Where(f => _instrumentationHelper.IsValidFilterExpression(f)).ToArray();
foreach (var module in modules)
{
if (InstrumentationHelper.IsModuleExcluded(module, _excludeFilters) ||
!InstrumentationHelper.IsModuleIncluded(module, _includeFilters))
if (_instrumentationHelper.IsModuleExcluded(module, _excludeFilters) ||
!_instrumentationHelper.IsModuleIncluded(module, _includeFilters))
{
_logger.LogVerbose($"Excluded module: '{module}'");
continue;
}
var instrumenter = new Instrumenter(module, _identifier, _excludeFilters, _includeFilters, _excludedSourceFiles, _excludeAttributes, _singleHit, _logger);
var instrumenter = new Instrumenter(module, _identifier, _excludeFilters, _includeFilters, _excludedSourceFiles, _excludeAttributes, _singleHit, _logger, _instrumentationHelper);
if (instrumenter.CanInstrument())
{
InstrumentationHelper.BackupOriginalModule(module, _identifier);
_instrumentationHelper.BackupOriginalModule(module, _identifier);
// Guard code path and restore if instrumentation fails.
try
@@ -105,7 +109,7 @@ namespace Coverlet.Core
catch (Exception ex)
{
_logger.LogWarning($"Unable to instrument module: {module} because : {ex.Message}");
InstrumentationHelper.RestoreOriginalModule(module, _identifier);
_instrumentationHelper.RestoreOriginalModule(module, _identifier);
}
}
}
@@ -206,7 +210,7 @@ namespace Coverlet.Core
}
modules.Add(Path.GetFileName(result.ModulePath), documents);
InstrumentationHelper.RestoreOriginalModule(result.ModulePath, _identifier);
_instrumentationHelper.RestoreOriginalModule(result.ModulePath, _identifier);
}
var coverageResult = new CoverageResult { Identifier = _identifier, Modules = modules, InstrumentedResults = _results };
@@ -301,7 +305,7 @@ namespace Coverlet.Core
}
}
InstrumentationHelper.DeleteHitsFile(result.HitsFilePath);
_instrumentationHelper.DeleteHitsFile(result.HitsFilePath);
}
}
+5 -1
View File
@@ -6,7 +6,7 @@ using Microsoft.Extensions.DependencyInjection;
namespace Coverlet.Core
{
internal static class DependencyInjection
public static class DependencyInjection
{
private static Lazy<IServiceProvider> _serviceProvider = new Lazy<IServiceProvider>(() => InitDefaultServices(), true);
@@ -28,6 +28,10 @@ namespace Coverlet.Core
IServiceCollection serviceCollection = new ServiceCollection();
serviceCollection.AddTransient<IRetryHelper, RetryHelper>();
serviceCollection.AddTransient<IProcessExitHandler, ProcessExitHandler>();
// We need to keep singleton/static semantics
serviceCollection.AddSingleton<IInstrumentationHelper, InstrumentationHelper>();
return serviceCollection.BuildServiceProvider();
}
@@ -9,22 +9,21 @@ using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using System.Text.RegularExpressions;
using Coverlet.Core.Abstracts;
using Microsoft.Extensions.FileSystemGlobbing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileSystemGlobbing.Abstractions;
namespace Coverlet.Core.Helpers
{
internal static class InstrumentationHelper
internal class InstrumentationHelper : IInstrumentationHelper
{
private static readonly ConcurrentDictionary<string, string> _backupList = new ConcurrentDictionary<string, string>();
private readonly ConcurrentDictionary<string, string> _backupList = new ConcurrentDictionary<string, string>();
private readonly IRetryHelper _retryHelper;
static InstrumentationHelper()
public InstrumentationHelper(IProcessExitHandler processExitHandler, IRetryHelper retryHelper)
{
DependencyInjection.Current.GetService<IProcessExitHandler>().Add((s, e) => RestoreOriginalModules());
processExitHandler.Add((s, e) => RestoreOriginalModules());
_retryHelper = retryHelper;
}
public static string[] GetCoverableModules(string module, string[] directories, bool includeTestAssembly)
public string[] GetCoverableModules(string module, string[] directories, bool includeTestAssembly)
{
Debug.Assert(directories != null);
@@ -68,7 +67,7 @@ namespace Coverlet.Core.Helpers
.ToArray();
}
public static bool HasPdb(string module, out bool embedded)
public bool HasPdb(string module, out bool embedded)
{
embedded = false;
using (var moduleStream = File.OpenRead(module))
@@ -94,7 +93,7 @@ namespace Coverlet.Core.Helpers
}
}
public static bool EmbeddedPortablePdbHasLocalSource(string module)
public bool EmbeddedPortablePdbHasLocalSource(string module)
{
using (FileStream moduleStream = File.OpenRead(module))
using (var peReader = new PEReader(moduleStream))
@@ -129,7 +128,7 @@ namespace Coverlet.Core.Helpers
return true;
}
public static void BackupOriginalModule(string module, string identifier)
public void BackupOriginalModule(string module, string identifier)
{
var backupPath = GetBackupPath(module, identifier);
var backupSymbolPath = Path.ChangeExtension(backupPath, ".pdb");
@@ -150,7 +149,7 @@ namespace Coverlet.Core.Helpers
}
}
public static void RestoreOriginalModule(string module, string identifier)
public void RestoreOriginalModule(string module, string identifier)
{
var backupPath = GetBackupPath(module, identifier);
var backupSymbolPath = Path.ChangeExtension(backupPath, ".pdb");
@@ -159,14 +158,14 @@ namespace Coverlet.Core.Helpers
// See: https://github.com/tonerdo/coverlet/issues/25
var retryStrategy = CreateRetryStrategy();
DependencyInjection.Current.GetService<IRetryHelper>().Retry(() =>
_retryHelper.Retry(() =>
{
File.Copy(backupPath, module, true);
File.Delete(backupPath);
_backupList.TryRemove(module, out string _);
}, retryStrategy, 10);
DependencyInjection.Current.GetService<IRetryHelper>().Retry(() =>
_retryHelper.Retry(() =>
{
if (File.Exists(backupSymbolPath))
{
@@ -178,7 +177,7 @@ namespace Coverlet.Core.Helpers
}, retryStrategy, 10);
}
public static void RestoreOriginalModules()
public void RestoreOriginalModules()
{
// Restore the original module - retry up to 10 times, since the destination file could be locked
// See: https://github.com/tonerdo/coverlet/issues/25
@@ -187,7 +186,7 @@ namespace Coverlet.Core.Helpers
foreach (string key in _backupList.Keys.ToList())
{
string backupPath = _backupList[key];
DependencyInjection.Current.GetService<IRetryHelper>().Retry(() =>
_retryHelper.Retry(() =>
{
File.Copy(backupPath, key, true);
File.Delete(backupPath);
@@ -196,15 +195,15 @@ namespace Coverlet.Core.Helpers
}
}
public static void DeleteHitsFile(string path)
public void DeleteHitsFile(string path)
{
// Retry hitting the hits file - retry up to 10 times, since the file could be locked
// See: https://github.com/tonerdo/coverlet/issues/25
var retryStrategy = CreateRetryStrategy();
DependencyInjection.Current.GetService<IRetryHelper>().Retry(() => File.Delete(path), retryStrategy, 10);
_retryHelper.Retry(() => File.Delete(path), retryStrategy, 10);
}
public static bool IsValidFilterExpression(string filter)
public bool IsValidFilterExpression(string filter)
{
if (filter == null)
return false;
@@ -236,7 +235,7 @@ namespace Coverlet.Core.Helpers
return true;
}
public static bool IsModuleExcluded(string module, string[] excludeFilters)
public bool IsModuleExcluded(string module, string[] excludeFilters)
{
if (excludeFilters == null || excludeFilters.Length == 0)
return false;
@@ -264,7 +263,7 @@ namespace Coverlet.Core.Helpers
return false;
}
public static bool IsModuleIncluded(string module, string[] includeFilters)
public bool IsModuleIncluded(string module, string[] includeFilters)
{
if (includeFilters == null || includeFilters.Length == 0)
return true;
@@ -291,7 +290,7 @@ namespace Coverlet.Core.Helpers
return false;
}
public static bool IsTypeExcluded(string module, string type, string[] excludeFilters)
public bool IsTypeExcluded(string module, string type, string[] excludeFilters)
{
if (excludeFilters == null || excludeFilters.Length == 0)
return false;
@@ -303,7 +302,7 @@ namespace Coverlet.Core.Helpers
return IsTypeFilterMatch(module, type, excludeFilters);
}
public static bool IsTypeIncluded(string module, string type, string[] includeFilters)
public bool IsTypeIncluded(string module, string type, string[] includeFilters)
{
if (includeFilters == null || includeFilters.Length == 0)
return true;
@@ -315,10 +314,10 @@ namespace Coverlet.Core.Helpers
return IsTypeFilterMatch(module, type, includeFilters);
}
public static bool IsLocalMethod(string method)
public bool IsLocalMethod(string method)
=> new Regex(WildcardToRegex("<*>*__*|*")).IsMatch(method);
private static bool IsTypeFilterMatch(string module, string type, string[] filters)
private bool IsTypeFilterMatch(string module, string type, string[] filters)
{
Debug.Assert(module != null);
Debug.Assert(filters != null);
@@ -338,7 +337,7 @@ namespace Coverlet.Core.Helpers
return false;
}
private static string GetBackupPath(string module, string identifier)
private string GetBackupPath(string module, string identifier)
{
return Path.Combine(
Path.GetTempPath(),
@@ -346,7 +345,7 @@ namespace Coverlet.Core.Helpers
);
}
private static Func<TimeSpan> CreateRetryStrategy(int initialSleepSeconds = 6)
private Func<TimeSpan> CreateRetryStrategy(int initialSleepSeconds = 6)
{
TimeSpan retryStrategy()
{
@@ -358,14 +357,14 @@ namespace Coverlet.Core.Helpers
return retryStrategy;
}
private static string WildcardToRegex(string pattern)
private string WildcardToRegex(string pattern)
{
return "^" + Regex.Escape(pattern).
Replace("\\*", ".*").
Replace("\\?", "?") + "$";
}
private static bool IsAssembly(string filePath)
private bool IsAssembly(string filePath)
{
Debug.Assert(filePath != null);
@@ -383,5 +382,4 @@ namespace Coverlet.Core.Helpers
}
}
}
}
}
@@ -4,7 +4,7 @@ using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using Coverlet.Core.Abstracts;
using Coverlet.Core.Attributes;
using Coverlet.Core.Helpers;
using Coverlet.Core.Logging;
@@ -27,6 +27,7 @@ namespace Coverlet.Core.Instrumentation
private readonly bool _singleHit;
private readonly bool _isCoreLibrary;
private readonly ILogger _logger;
private readonly IInstrumentationHelper _instrumentationHelper;
private InstrumenterResult _result;
private FieldDefinition _customTrackerHitsArray;
private FieldDefinition _customTrackerHitsFilePath;
@@ -38,7 +39,16 @@ namespace Coverlet.Core.Instrumentation
private List<string> _asyncMachineStateMethod;
private List<string> _excludedSourceFiles;
public Instrumenter(string module, string identifier, string[] excludeFilters, string[] includeFilters, string[] excludedFiles, string[] excludedAttributes, bool singleHit, ILogger logger)
public Instrumenter(
string module,
string identifier,
string[] excludeFilters,
string[] includeFilters,
string[] excludedFiles,
string[] excludedAttributes,
bool singleHit,
ILogger logger,
IInstrumentationHelper instrumentationHelper)
{
_module = module;
_identifier = identifier;
@@ -49,17 +59,18 @@ namespace Coverlet.Core.Instrumentation
_singleHit = singleHit;
_isCoreLibrary = Path.GetFileNameWithoutExtension(_module) == "System.Private.CoreLib";
_logger = logger;
_instrumentationHelper = instrumentationHelper;
}
public bool CanInstrument()
{
try
{
if (InstrumentationHelper.HasPdb(_module, out bool embeddedPdb))
if (_instrumentationHelper.HasPdb(_module, out bool embeddedPdb))
{
if (embeddedPdb)
{
if (InstrumentationHelper.EmbeddedPortablePdbHasLocalSource(_module))
if (_instrumentationHelper.EmbeddedPortablePdbHasLocalSource(_module))
{
return true;
}
@@ -145,8 +156,8 @@ namespace Coverlet.Core.Instrumentation
if (!actualType.CustomAttributes.Any(IsExcludeAttribute)
// Instrumenting Interlocked which is used for recording hits would cause an infinite loop.
&& (!_isCoreLibrary || actualType.FullName != "System.Threading.Interlocked")
&& !InstrumentationHelper.IsTypeExcluded(_module, actualType.FullName, _excludeFilters)
&& InstrumentationHelper.IsTypeIncluded(_module, actualType.FullName, _includeFilters))
&& !_instrumentationHelper.IsTypeExcluded(_module, actualType.FullName, _excludeFilters)
&& _instrumentationHelper.IsTypeIncluded(_module, actualType.FullName, _includeFilters))
InstrumentType(type);
}
@@ -305,7 +316,7 @@ namespace Coverlet.Core.Instrumentation
{
MethodDefinition actualMethod = method;
IEnumerable<CustomAttribute> customAttributes = method.CustomAttributes;
if (InstrumentationHelper.IsLocalMethod(method.Name))
if (_instrumentationHelper.IsLocalMethod(method.Name))
actualMethod = methods.FirstOrDefault(m => m.Name == method.Name.Split('>')[0].Substring(1)) ?? method;
if (actualMethod.IsGetter || actualMethod.IsSetter)
@@ -4,6 +4,7 @@ using System.Linq;
using System.Text;
using ConsoleTables;
using Coverlet.Core;
using Coverlet.Core.Abstracts;
using Coverlet.Core.Enums;
using Coverlet.Core.Reporters;
using Microsoft.Build.Framework;
@@ -80,7 +81,7 @@ namespace Coverlet.MSbuild.Tasks
return false;
}
var coverage = new Coverage(CoveragePrepareResult.Deserialize(new FileStream(InstrumenterState.ItemSpec, FileMode.Open)), this._logger);
var coverage = new Coverage(CoveragePrepareResult.Deserialize(new FileStream(InstrumenterState.ItemSpec, FileMode.Open)), this._logger, (IInstrumentationHelper)DependencyInjection.Current.GetService(typeof(IInstrumentationHelper)));
var result = coverage.GetCoverageResult();
var directory = Path.GetDirectoryName(_output);
@@ -1,6 +1,8 @@
using System;
using System.IO;
using Coverlet.Core;
using Coverlet.Core.Abstracts;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
@@ -104,7 +106,7 @@ namespace Coverlet.MSbuild.Tasks
var excludedSourceFiles = _excludeByFile?.Split(',');
var excludeAttributes = _excludeByAttribute?.Split(',');
Coverage coverage = new Coverage(_path, includeFilters, includeDirectories, excludeFilters, excludedSourceFiles, excludeAttributes, _includeTestAssembly, _singleHit, _mergeWith, _useSourceLink, _logger);
Coverage coverage = new Coverage(_path, includeFilters, includeDirectories, excludeFilters, excludedSourceFiles, excludeAttributes, _includeTestAssembly, _singleHit, _mergeWith, _useSourceLink, _logger, (IInstrumentationHelper)DependencyInjection.Current.GetService(typeof(IInstrumentationHelper)));
CoveragePrepareResult prepareResult = coverage.PrepareModules();
InstrumenterState = new TaskItem(System.IO.Path.GetTempFileName());
using (var instrumentedStateFile = new FileStream(InstrumenterState.ItemSpec, FileMode.Open, FileAccess.Write))
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection;
using Moq;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
@@ -11,6 +12,7 @@ using Coverlet.Collector.Utilities.Interfaces;
using Coverlet.Collector.Utilities;
using Xunit;
using Coverlet.Collector.DataCollection;
using Coverlet.Core.Abstracts;
namespace Coverlet.Collector.Tests
{
@@ -68,7 +70,7 @@ namespace Coverlet.Collector.Tests
null,
_context);
IDictionary<string, object> sessionStartProperties = new Dictionary<string, object>();
Coverage coverage = new Coverage("abc.dll", null, null, null, null, null, true, true, "abc.json", true, It.IsAny<ILogger>());
Coverage coverage = new Coverage("abc.dll", null, null, null, null, null, true, true, "abc.json", true, It.IsAny<ILogger>(), (IInstrumentationHelper)DependencyInjection.Current.GetService(typeof(IInstrumentationHelper)));
sessionStartProperties.Add("TestSources", new List<string> { "abc.dll" });
_mockCoverageWrapper.Setup(x => x.CreateCoverage(It.IsAny<CoverletSettings>(), It.IsAny<ILogger>())).Returns(coverage);
+6 -4
View File
@@ -1,9 +1,8 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Coverlet.Core.Helpers;
using Coverlet.Core.Logging;
using Coverlet.Core.Samples.Tests;
using Coverlet.Tests.RemoteExecutor;
@@ -15,6 +14,9 @@ namespace Coverlet.Core.Tests
{
public class CoverageTests
{
private readonly InstrumentationHelper _instrumentationHelper = new InstrumentationHelper(new ProcessExitHandler(), new RetryHelper());
private readonly Mock<ILogger> _mockLogger = new Mock<ILogger>();
[Fact]
public void TestCoverage()
{
@@ -28,7 +30,7 @@ namespace Coverlet.Core.Tests
// TODO: Find a way to mimick hits
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, false, string.Empty, false, new Mock<ILogger>().Object);
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, false, string.Empty, false, _mockLogger.Object, _instrumentationHelper);
coverage.PrepareModules();
var result = coverage.GetCoverageResult();
@@ -51,7 +53,7 @@ namespace Coverlet.Core.Tests
// TODO: Find a way to mimick hits
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>(), true, false, string.Empty, false, new Mock<ILogger>().Object);
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>(), true, false, string.Empty, false, _mockLogger.Object, _instrumentationHelper);
coverage.PrepareModules();
var result = coverage.GetCoverageResult();
@@ -8,11 +8,13 @@ namespace Coverlet.Core.Helpers.Tests
{
public class InstrumentationHelperTests
{
private readonly InstrumentationHelper _instrumentationHelper = new InstrumentationHelper(new ProcessExitHandler(), new RetryHelper());
[Fact]
public void TestGetDependencies()
{
string module = typeof(InstrumentationHelperTests).Assembly.Location;
var modules = InstrumentationHelper.GetCoverableModules(module, Array.Empty<string>(), false);
var modules = _instrumentationHelper.GetCoverableModules(module, Array.Empty<string>(), false);
Assert.False(Array.Exists(modules, m => m == module));
}
@@ -20,14 +22,14 @@ namespace Coverlet.Core.Helpers.Tests
public void TestGetDependenciesWithTestAssembly()
{
string module = typeof(InstrumentationHelperTests).Assembly.Location;
var modules = InstrumentationHelper.GetCoverableModules(module, Array.Empty<string>(), true);
var modules = _instrumentationHelper.GetCoverableModules(module, Array.Empty<string>(), true);
Assert.True(Array.Exists(modules, m => m == module));
}
[Fact]
public void TestHasPdb()
{
Assert.True(InstrumentationHelper.HasPdb(typeof(InstrumentationHelperTests).Assembly.Location, out bool embeddedPdb));
Assert.True(_instrumentationHelper.HasPdb(typeof(InstrumentationHelperTests).Assembly.Location, out bool embeddedPdb));
Assert.False(embeddedPdb);
}
@@ -37,7 +39,7 @@ namespace Coverlet.Core.Helpers.Tests
string module = typeof(InstrumentationHelperTests).Assembly.Location;
string identifier = Guid.NewGuid().ToString();
InstrumentationHelper.BackupOriginalModule(module, identifier);
_instrumentationHelper.BackupOriginalModule(module, identifier);
var backupPath = Path.Combine(
Path.GetTempPath(),
@@ -50,16 +52,16 @@ namespace Coverlet.Core.Helpers.Tests
[Fact]
public void TestIsValidFilterExpression()
{
Assert.True(InstrumentationHelper.IsValidFilterExpression("[*]*"));
Assert.True(InstrumentationHelper.IsValidFilterExpression("[*]*core"));
Assert.True(InstrumentationHelper.IsValidFilterExpression("[assembly]*"));
Assert.True(InstrumentationHelper.IsValidFilterExpression("[*]type"));
Assert.True(InstrumentationHelper.IsValidFilterExpression("[assembly]type"));
Assert.False(InstrumentationHelper.IsValidFilterExpression("[*]"));
Assert.False(InstrumentationHelper.IsValidFilterExpression("[-]*"));
Assert.False(InstrumentationHelper.IsValidFilterExpression("*"));
Assert.False(InstrumentationHelper.IsValidFilterExpression("]["));
Assert.False(InstrumentationHelper.IsValidFilterExpression(null));
Assert.True(_instrumentationHelper.IsValidFilterExpression("[*]*"));
Assert.True(_instrumentationHelper.IsValidFilterExpression("[*]*core"));
Assert.True(_instrumentationHelper.IsValidFilterExpression("[assembly]*"));
Assert.True(_instrumentationHelper.IsValidFilterExpression("[*]type"));
Assert.True(_instrumentationHelper.IsValidFilterExpression("[assembly]type"));
Assert.False(_instrumentationHelper.IsValidFilterExpression("[*]"));
Assert.False(_instrumentationHelper.IsValidFilterExpression("[-]*"));
Assert.False(_instrumentationHelper.IsValidFilterExpression("*"));
Assert.False(_instrumentationHelper.IsValidFilterExpression("]["));
Assert.False(_instrumentationHelper.IsValidFilterExpression(null));
}
[Fact]
@@ -68,14 +70,14 @@ namespace Coverlet.Core.Helpers.Tests
var tempFile = Path.GetTempFileName();
Assert.True(File.Exists(tempFile));
InstrumentationHelper.DeleteHitsFile(tempFile);
_instrumentationHelper.DeleteHitsFile(tempFile);
Assert.False(File.Exists(tempFile));
}
[Fact]
public void TestIsModuleExcludedWithoutFilter()
{
var result = InstrumentationHelper.IsModuleExcluded("Module.dll", new string[0]);
var result = _instrumentationHelper.IsModuleExcluded("Module.dll", new string[0]);
Assert.False(result);
}
@@ -83,7 +85,7 @@ namespace Coverlet.Core.Helpers.Tests
[Fact]
public void TestIsModuleIncludedWithoutFilter()
{
var result = InstrumentationHelper.IsModuleIncluded("Module.dll", new string[0]);
var result = _instrumentationHelper.IsModuleIncluded("Module.dll", new string[0]);
Assert.True(result);
}
@@ -93,7 +95,7 @@ namespace Coverlet.Core.Helpers.Tests
[InlineData("[Mismatch]*")]
public void TestIsModuleExcludedWithSingleMismatchFilter(string filter)
{
var result = InstrumentationHelper.IsModuleExcluded("Module.dll", new[] { filter });
var result = _instrumentationHelper.IsModuleExcluded("Module.dll", new[] { filter });
Assert.False(result);
}
@@ -101,7 +103,7 @@ namespace Coverlet.Core.Helpers.Tests
[Fact]
public void TestIsModuleIncludedWithSingleMismatchFilter()
{
var result = InstrumentationHelper.IsModuleIncluded("Module.dll", new[] { "[Mismatch]*" });
var result = _instrumentationHelper.IsModuleIncluded("Module.dll", new[] { "[Mismatch]*" });
Assert.False(result);
}
@@ -110,10 +112,10 @@ namespace Coverlet.Core.Helpers.Tests
[MemberData(nameof(ValidModuleFilterData))]
public void TestIsModuleExcludedAndIncludedWithFilter(string filter)
{
var result = InstrumentationHelper.IsModuleExcluded("Module.dll", new[] { filter });
var result = _instrumentationHelper.IsModuleExcluded("Module.dll", new[] { filter });
Assert.True(result);
result = InstrumentationHelper.IsModuleIncluded("Module.dll", new[] { filter });
result = _instrumentationHelper.IsModuleIncluded("Module.dll", new[] { filter });
Assert.True(result);
}
@@ -123,17 +125,17 @@ namespace Coverlet.Core.Helpers.Tests
{
var filters = new[] { "[Mismatch]*", filter, "[Mismatch]*" };
var result = InstrumentationHelper.IsModuleExcluded("Module.dll", filters);
var result = _instrumentationHelper.IsModuleExcluded("Module.dll", filters);
Assert.True(result);
result = InstrumentationHelper.IsModuleIncluded("Module.dll", filters);
result = _instrumentationHelper.IsModuleIncluded("Module.dll", filters);
Assert.True(result);
}
[Fact]
public void TestIsTypeExcludedWithoutFilter()
{
var result = InstrumentationHelper.IsTypeExcluded("Module.dll", "a.b.Dto", new string[0]);
var result = _instrumentationHelper.IsTypeExcluded("Module.dll", "a.b.Dto", new string[0]);
Assert.False(result);
}
@@ -141,7 +143,7 @@ namespace Coverlet.Core.Helpers.Tests
[Fact]
public void TestIsTypeIncludedWithoutFilter()
{
var result = InstrumentationHelper.IsTypeIncluded("Module.dll", "a.b.Dto", new string[0]);
var result = _instrumentationHelper.IsTypeIncluded("Module.dll", "a.b.Dto", new string[0]);
Assert.True(result);
}
@@ -152,10 +154,10 @@ namespace Coverlet.Core.Helpers.Tests
[InlineData("[Mismatch]a.b.Dto")]
public void TestIsTypeExcludedAndIncludedWithSingleMismatchFilter(string filter)
{
var result = InstrumentationHelper.IsTypeExcluded("Module.dll", "a.b.Dto", new[] { filter });
var result = _instrumentationHelper.IsTypeExcluded("Module.dll", "a.b.Dto", new[] { filter });
Assert.False(result);
result = InstrumentationHelper.IsTypeIncluded("Module.dll", "a.b.Dto", new[] { filter });
result = _instrumentationHelper.IsTypeIncluded("Module.dll", "a.b.Dto", new[] { filter });
Assert.False(result);
}
@@ -163,10 +165,10 @@ namespace Coverlet.Core.Helpers.Tests
[MemberData(nameof(ValidModuleAndNamespaceFilterData))]
public void TestIsTypeExcludedAndIncludedWithFilter(string filter)
{
var result = InstrumentationHelper.IsTypeExcluded("Module.dll", "a.b.Dto", new[] { filter });
var result = _instrumentationHelper.IsTypeExcluded("Module.dll", "a.b.Dto", new[] { filter });
Assert.True(result);
result = InstrumentationHelper.IsTypeIncluded("Module.dll", "a.b.Dto", new[] { filter });
result = _instrumentationHelper.IsTypeIncluded("Module.dll", "a.b.Dto", new[] { filter });
Assert.True(result);
}
@@ -176,10 +178,10 @@ namespace Coverlet.Core.Helpers.Tests
{
var filters = new[] { "[Mismatch]*", filter, "[Mismatch]*" };
var result = InstrumentationHelper.IsTypeExcluded("Module.dll", "a.b.Dto", filters);
var result = _instrumentationHelper.IsTypeExcluded("Module.dll", "a.b.Dto", filters);
Assert.True(result);
result = InstrumentationHelper.IsTypeIncluded("Module.dll", "a.b.Dto", filters);
result = _instrumentationHelper.IsTypeIncluded("Module.dll", "a.b.Dto", filters);
Assert.True(result);
}
@@ -188,17 +190,20 @@ namespace Coverlet.Core.Helpers.Tests
{
string module = typeof(InstrumentationHelperTests).Assembly.Location;
var currentDirModules = InstrumentationHelper.GetCoverableModules(module,
new[] { Environment.CurrentDirectory }, false);
var currentDirModules = _instrumentationHelper.GetCoverableModules(module,
new[] { Environment.CurrentDirectory }, false)
.Where(m => !m.StartsWith("testgen_")).ToArray();
var parentDirWildcardModules = InstrumentationHelper.GetCoverableModules(module,
new[] { Path.Combine(Directory.GetParent(Environment.CurrentDirectory).FullName, "*") }, false);
var parentDirWildcardModules = _instrumentationHelper.GetCoverableModules(module,
new[] { Path.Combine(Directory.GetParent(Environment.CurrentDirectory).FullName, "*") }, false)
.Where(m => !m.StartsWith("testgen_")).ToArray();
// There are at least as many modules found when searching the parent directory's subdirectories
Assert.True(parentDirWildcardModules.Length >= currentDirModules.Length);
var relativePathModules = InstrumentationHelper.GetCoverableModules(module,
new[] { "." }, false);
var relativePathModules = _instrumentationHelper.GetCoverableModules(module,
new[] { "." }, false)
.Where(m => !m.StartsWith("testgen_")).ToArray();
// Same number of modules found when using a relative path
Assert.Equal(currentDirModules.Length, relativePathModules.Length);
@@ -20,6 +20,9 @@ namespace Coverlet.Core.Instrumentation.Tests
{
public class InstrumenterTests
{
private readonly InstrumentationHelper _instrumentationHelper = new InstrumentationHelper(new ProcessExitHandler(), new RetryHelper());
private readonly Mock<ILogger> _mockLogger = new Mock<ILogger>();
[Fact(Skip = "To be used only validating System.Private.CoreLib instrumentation")]
public void TestCoreLibInstrumentation()
{
@@ -38,7 +41,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>(), false, new Mock<ILogger>().Object);
Instrumenter instrumenter = new Instrumenter(Path.Combine(TestFilesDir, files[0]), "_coverlet_instrumented", Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), false, _mockLogger.Object, _instrumentationHelper);
Assert.True(instrumenter.CanInstrument());
var result = instrumenter.Instrument();
Assert.NotNull(result);
@@ -174,8 +177,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>(), excludedFiles, attributesToIgnore, singleHit, new Mock<ILogger>().Object);
Instrumenter instrumenter = new Instrumenter(module, identifier, Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), attributesToIgnore, false, _mockLogger.Object, _instrumentationHelper);
return new InstrumenterTest
{
Instrumenter = instrumenter,
@@ -346,16 +348,16 @@ namespace Coverlet.Core.Instrumentation.Tests
{
string xunitDll = Directory.GetFiles(Directory.GetCurrentDirectory(), "xunit.*.dll").First();
var loggerMock = new Mock<ILogger>();
Instrumenter instrumenter = new Instrumenter(xunitDll, "_xunit_instrumented", Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), false, loggerMock.Object);
Assert.True(InstrumentationHelper.HasPdb(xunitDll, out bool embedded));
Instrumenter instrumenter = new Instrumenter(xunitDll, "_xunit_instrumented", Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), false, loggerMock.Object, _instrumentationHelper);
Assert.True(_instrumentationHelper.HasPdb(xunitDll, out bool embedded));
Assert.True(embedded);
Assert.False(instrumenter.CanInstrument());
loggerMock.Verify(l => l.LogWarning(It.IsAny<string>()));
// Default case
string coverletCoreDll = Directory.GetFiles(Directory.GetCurrentDirectory(), "coverlet.core.dll").First();
instrumenter = new Instrumenter(coverletCoreDll, "_coverlet_core_instrumented", Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), false, loggerMock.Object);
Assert.True(InstrumentationHelper.HasPdb(coverletCoreDll, out embedded));
instrumenter = new Instrumenter(coverletCoreDll, "_coverlet_core_instrumented", Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), false, loggerMock.Object, _instrumentationHelper);
Assert.True(_instrumentationHelper.HasPdb(coverletCoreDll, out embedded));
Assert.False(embedded);
Assert.True(instrumenter.CanInstrument());
loggerMock.VerifyNoOtherCalls();
@@ -365,7 +367,7 @@ namespace Coverlet.Core.Instrumentation.Tests
public void TestInstrument_MissingModule()
{
var loggerMock = new Mock<ILogger>();
var instrumenter = new Instrumenter("test", "_test_instrumented", Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), false, loggerMock.Object);
var instrumenter = new Instrumenter("test", "_test_instrumented", Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), false, loggerMock.Object, _instrumentationHelper);
Assert.False(instrumenter.CanInstrument());
loggerMock.Verify(l => l.LogWarning(It.IsAny<string>()));
}
@@ -8,6 +8,7 @@ using System.Threading;
using System.Threading.Tasks;
using Coverlet.Core.Abstracts;
using Coverlet.Core.Helpers;
using Coverlet.Core.Instrumentation;
using Coverlet.Core.Logging;
using Coverlet.Core.Reporters;
@@ -190,7 +191,7 @@ namespace Coverlet.Core.Tests
using (var result = new FileStream(filePath, FileMode.Open))
{
CoveragePrepareResult coveragePrepareResultLoaded = CoveragePrepareResult.Deserialize(result);
Coverage coverage = new Coverage(coveragePrepareResultLoaded, new Mock<ILogger>().Object);
Coverage coverage = new Coverage(coveragePrepareResultLoaded, new Mock<ILogger>().Object, new InstrumentationHelper(new ProcessExitHandler(), new RetryHelper()));
return coverage.GetCoverageResult();
}
}
@@ -201,11 +202,12 @@ namespace Coverlet.Core.Tests
DependencyInjection.Set(new ServiceCollection()
.AddTransient<IRetryHelper, CustomRetryHelper>()
.AddTransient<IProcessExitHandler, CustomProcessExitHandler>()
.AddSingleton<IInstrumentationHelper, InstrumentationHelper>()
.BuildServiceProvider());
// Rename test file to avoid locks
string location = typeof(T).Assembly.Location;
string fileName = Path.ChangeExtension(Path.GetRandomFileName(), ".dll");
string fileName = Path.ChangeExtension($"testgen_{Path.GetFileNameWithoutExtension(Path.GetRandomFileName())}", ".dll");
string logFile = Path.ChangeExtension(fileName, ".log");
string newPath = Path.Combine(Path.GetDirectoryName(location), fileName);
@@ -223,7 +225,7 @@ namespace Coverlet.Core.Tests
{
"[xunit.*]*",
"[coverlet.*]*"
}, Array.Empty<string>(), Array.Empty<string>(), true, false, "", false, new Logger(logFile));
}, Array.Empty<string>(), Array.Empty<string>(), true, false, "", false, new Logger(logFile), DependencyInjection.Current.GetService<IInstrumentationHelper>());
CoveragePrepareResult prepareResult = coverage.PrepareModules();
// Load new assembly