From 1bb08bb518922fc0a292ce86f55800fbce474ab7 Mon Sep 17 00:00:00 2001 From: Marco Rossignoli Date: Tue, 10 Sep 2019 12:56:04 +0200 Subject: [PATCH] Inject InstrumentationHelper (#531) Inject InstrumentationHelper --- .../DataCollection/CoverageWrapper.cs | 4 +- src/coverlet.console/Program.cs | 4 +- .../Abstracts/InstrumentationHelper.cs | 18 +++++ src/coverlet.core/Coverage.cs | 30 ++++--- src/coverlet.core/DependencyInjection.cs | 6 +- .../Helpers/InstrumentationHelper.cs | 60 +++++++------- .../Instrumentation/Instrumenter.cs | 25 ++++-- .../CoverageResultTask.cs | 3 +- .../InstrumentationTask.cs | 4 +- .../CoverletCoverageDataCollectorTests.cs | 4 +- test/coverlet.core.tests/CoverageTests.cs | 10 ++- .../Helpers/InstrumentationHelperTests.cs | 79 ++++++++++--------- .../Instrumentation/InstrumenterTests.cs | 18 +++-- .../coverlet.core.tests/InstrumenterHelper.cs | 8 +- 14 files changed, 164 insertions(+), 109 deletions(-) create mode 100644 src/coverlet.core/Abstracts/InstrumentationHelper.cs diff --git a/src/coverlet.collector/DataCollection/CoverageWrapper.cs b/src/coverlet.collector/DataCollection/CoverageWrapper.cs index 76eece4..f974712 100644 --- a/src/coverlet.collector/DataCollection/CoverageWrapper.cs +++ b/src/coverlet.collector/DataCollection/CoverageWrapper.cs @@ -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))); } /// diff --git a/src/coverlet.console/Program.cs b/src/coverlet.console/Program.cs index c3be3b2..c5afa90 100644 --- a/src/coverlet.console/Program.cs +++ b/src/coverlet.console/Program.cs @@ -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(); diff --git a/src/coverlet.core/Abstracts/InstrumentationHelper.cs b/src/coverlet.core/Abstracts/InstrumentationHelper.cs new file mode 100644 index 0000000..5899855 --- /dev/null +++ b/src/coverlet.core/Abstracts/InstrumentationHelper.cs @@ -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); + } +} diff --git a/src/coverlet.core/Coverage.cs b/src/coverlet.core/Coverage.cs index 4778fa7..8ce16f0 100644 --- a/src/coverlet.core/Coverage.cs +++ b/src/coverlet.core/Coverage.cs @@ -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 _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(); } - 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(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(), filter => _logger.LogVerbose($"Excluded module filter '{filter}'")); Array.ForEach(_includeFilters ?? Array.Empty(), 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); } } diff --git a/src/coverlet.core/DependencyInjection.cs b/src/coverlet.core/DependencyInjection.cs index e50d00d..5f3b2e8 100644 --- a/src/coverlet.core/DependencyInjection.cs +++ b/src/coverlet.core/DependencyInjection.cs @@ -6,7 +6,7 @@ using Microsoft.Extensions.DependencyInjection; namespace Coverlet.Core { - internal static class DependencyInjection + public static class DependencyInjection { private static Lazy _serviceProvider = new Lazy(() => InitDefaultServices(), true); @@ -28,6 +28,10 @@ namespace Coverlet.Core IServiceCollection serviceCollection = new ServiceCollection(); serviceCollection.AddTransient(); serviceCollection.AddTransient(); + + // We need to keep singleton/static semantics + serviceCollection.AddSingleton(); + return serviceCollection.BuildServiceProvider(); } diff --git a/src/coverlet.core/Helpers/InstrumentationHelper.cs b/src/coverlet.core/Helpers/InstrumentationHelper.cs index 90b3070..3a71b89 100644 --- a/src/coverlet.core/Helpers/InstrumentationHelper.cs +++ b/src/coverlet.core/Helpers/InstrumentationHelper.cs @@ -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 _backupList = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _backupList = new ConcurrentDictionary(); + private readonly IRetryHelper _retryHelper; - static InstrumentationHelper() + public InstrumentationHelper(IProcessExitHandler processExitHandler, IRetryHelper retryHelper) { - DependencyInjection.Current.GetService().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().Retry(() => + _retryHelper.Retry(() => { File.Copy(backupPath, module, true); File.Delete(backupPath); _backupList.TryRemove(module, out string _); }, retryStrategy, 10); - DependencyInjection.Current.GetService().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().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().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 CreateRetryStrategy(int initialSleepSeconds = 6) + private Func 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 } } } -} - +} \ No newline at end of file diff --git a/src/coverlet.core/Instrumentation/Instrumenter.cs b/src/coverlet.core/Instrumentation/Instrumenter.cs index 0e1ccc9..87d3d9c 100644 --- a/src/coverlet.core/Instrumentation/Instrumenter.cs +++ b/src/coverlet.core/Instrumentation/Instrumenter.cs @@ -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 _asyncMachineStateMethod; private List _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 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) diff --git a/src/coverlet.msbuild.tasks/CoverageResultTask.cs b/src/coverlet.msbuild.tasks/CoverageResultTask.cs index a191a70..0e06b40 100644 --- a/src/coverlet.msbuild.tasks/CoverageResultTask.cs +++ b/src/coverlet.msbuild.tasks/CoverageResultTask.cs @@ -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); diff --git a/src/coverlet.msbuild.tasks/InstrumentationTask.cs b/src/coverlet.msbuild.tasks/InstrumentationTask.cs index 5242bdb..0a1f50b 100644 --- a/src/coverlet.msbuild.tasks/InstrumentationTask.cs +++ b/src/coverlet.msbuild.tasks/InstrumentationTask.cs @@ -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)) diff --git a/test/coverlet.collector.tests/CoverletCoverageDataCollectorTests.cs b/test/coverlet.collector.tests/CoverletCoverageDataCollectorTests.cs index 6e2dbbc..a8ada80 100644 --- a/test/coverlet.collector.tests/CoverletCoverageDataCollectorTests.cs +++ b/test/coverlet.collector.tests/CoverletCoverageDataCollectorTests.cs @@ -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 sessionStartProperties = new Dictionary(); - Coverage coverage = new Coverage("abc.dll", null, null, null, null, null, true, true, "abc.json", true, It.IsAny()); + Coverage coverage = new Coverage("abc.dll", null, null, null, null, null, true, true, "abc.json", true, It.IsAny(), (IInstrumentationHelper)DependencyInjection.Current.GetService(typeof(IInstrumentationHelper))); sessionStartProperties.Add("TestSources", new List { "abc.dll" }); _mockCoverageWrapper.Setup(x => x.CreateCoverage(It.IsAny(), It.IsAny())).Returns(coverage); diff --git a/test/coverlet.core.tests/CoverageTests.cs b/test/coverlet.core.tests/CoverageTests.cs index 73e1aec..018ca99 100644 --- a/test/coverlet.core.tests/CoverageTests.cs +++ b/test/coverlet.core.tests/CoverageTests.cs @@ -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 _mockLogger = new Mock(); + [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(), Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty(), false, false, string.Empty, false, new Mock().Object); + var coverage = new Coverage(Path.Combine(directory.FullName, Path.GetFileName(module)), Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty(), 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(), Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty(), true, false, string.Empty, false, new Mock().Object); + var coverage = new Coverage(Path.Combine(directory.FullName, Path.GetFileName(module)), Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty(), true, false, string.Empty, false, _mockLogger.Object, _instrumentationHelper); coverage.PrepareModules(); var result = coverage.GetCoverageResult(); diff --git a/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs b/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs index 202f412..7457242 100644 --- a/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs +++ b/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs @@ -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(), false); + var modules = _instrumentationHelper.GetCoverableModules(module, Array.Empty(), 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(), true); + var modules = _instrumentationHelper.GetCoverableModules(module, Array.Empty(), 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); diff --git a/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs b/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs index 1b31d50..4bcbaea 100644 --- a/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs +++ b/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs @@ -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 _mockLogger = new Mock(); + [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(), Array.Empty(), Array.Empty(), Array.Empty(), false, new Mock().Object); + Instrumenter instrumenter = new Instrumenter(Path.Combine(TestFilesDir, files[0]), "_coverlet_instrumented", Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty(), 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(), Array.Empty(), excludedFiles, attributesToIgnore, singleHit, new Mock().Object); - + Instrumenter instrumenter = new Instrumenter(module, identifier, Array.Empty(), Array.Empty(), Array.Empty(), 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(); - Instrumenter instrumenter = new Instrumenter(xunitDll, "_xunit_instrumented", Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty(), false, loggerMock.Object); - Assert.True(InstrumentationHelper.HasPdb(xunitDll, out bool embedded)); + Instrumenter instrumenter = new Instrumenter(xunitDll, "_xunit_instrumented", Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty(), 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())); // Default case string coverletCoreDll = Directory.GetFiles(Directory.GetCurrentDirectory(), "coverlet.core.dll").First(); - instrumenter = new Instrumenter(coverletCoreDll, "_coverlet_core_instrumented", Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty(), false, loggerMock.Object); - Assert.True(InstrumentationHelper.HasPdb(coverletCoreDll, out embedded)); + instrumenter = new Instrumenter(coverletCoreDll, "_coverlet_core_instrumented", Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty(), 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(); - var instrumenter = new Instrumenter("test", "_test_instrumented", Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty(), false, loggerMock.Object); + var instrumenter = new Instrumenter("test", "_test_instrumented", Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty(), false, loggerMock.Object, _instrumentationHelper); Assert.False(instrumenter.CanInstrument()); loggerMock.Verify(l => l.LogWarning(It.IsAny())); } diff --git a/test/coverlet.core.tests/InstrumenterHelper.cs b/test/coverlet.core.tests/InstrumenterHelper.cs index 0ea0da4..12a9ad7 100644 --- a/test/coverlet.core.tests/InstrumenterHelper.cs +++ b/test/coverlet.core.tests/InstrumenterHelper.cs @@ -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().Object); + Coverage coverage = new Coverage(coveragePrepareResultLoaded, new Mock().Object, new InstrumentationHelper(new ProcessExitHandler(), new RetryHelper())); return coverage.GetCoverageResult(); } } @@ -201,11 +202,12 @@ namespace Coverlet.Core.Tests DependencyInjection.Set(new ServiceCollection() .AddTransient() .AddTransient() + .AddSingleton() .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(), Array.Empty(), true, false, "", false, new Logger(logFile)); + }, Array.Empty(), Array.Empty(), true, false, "", false, new Logger(logFile), DependencyInjection.Current.GetService()); CoveragePrepareResult prepareResult = coverage.PrepareModules(); // Load new assembly