From d93613826b1a33f894b9c80f6ae893643a8b7254 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20M=C3=BCller?= Date: Tue, 21 Mar 2023 08:21:09 +0100 Subject: [PATCH] Fixes CoverletSourceRootsMapping issue (#1456) --- Documentation/Changelog.md | 1 + .../VSTest/DeterministicBuild/HowTo.md | 4 +- .../CoverletCoverageCollector.cs | 4 +- .../netstandard1.0/coverlet.collector.targets | 2 +- .../Abstractions/IAssemblyAdapter.cs | 10 +++++ src/coverlet.core/Helpers/AssemblyAdapter.cs | 16 +++++++ .../Helpers/SourceRootTranslator.cs | 10 +++-- .../InstrumentationTask.cs | 4 +- .../coverlet.msbuild.targets | 2 +- .../CoverletCoverageDataCollectorTests.cs | 43 +++++++++++-------- .../Coverage/CoverageTests.AsyncAwait.cs | 4 +- ...erageTests.ExcludeFromCoverageAttribute.cs | 2 +- .../Coverage/CoverageTests.cs | 6 +-- .../Coverage/InstrumenterHelper.cs | 11 +++-- .../Helpers/InstrumentationHelperTests.cs | 6 +-- .../Helpers/SourceRootTranslatorTests.cs | 24 +++++++---- .../Instrumentation/InstrumenterTests.cs | 16 +++---- .../DeterministicBuild.cs | 8 ++-- 18 files changed, 113 insertions(+), 60 deletions(-) create mode 100644 src/coverlet.core/Abstractions/IAssemblyAdapter.cs create mode 100644 src/coverlet.core/Helpers/AssemblyAdapter.cs diff --git a/Documentation/Changelog.md b/Documentation/Changelog.md index 3cf3a8d9c..3cffc7cd3 100644 --- a/Documentation/Changelog.md +++ b/Documentation/Changelog.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased ### Fixed +-Could not write lines to file CoverletSourceRootsMapping - in use by another process [#1155](https://github.com/coverlet-coverage/coverlet/issues/1155) -Incorrect coverage for methods returning IAsyncEnumerable in generic classes [#1383](https://github.com/coverlet-coverage/coverlet/issues/1383) -Wrong branch coverage for async methods .NET Standard 1.x [#1376](https://github.com/coverlet-coverage/coverlet/issues/1376) -Empty path exception in visual basic projects [#775](https://github.com/coverlet-coverage/coverlet/issues/775) diff --git a/Documentation/Examples/VSTest/DeterministicBuild/HowTo.md b/Documentation/Examples/VSTest/DeterministicBuild/HowTo.md index b1002a65c..96d94ef1b 100644 --- a/Documentation/Examples/VSTest/DeterministicBuild/HowTo.md +++ b/Documentation/Examples/VSTest/DeterministicBuild/HowTo.md @@ -59,7 +59,7 @@ Add collectors package version generated to `"..\Documentation\Examples\VSTest\D Go to test project folder and run ``` C:\git\coverlet\Documentation\Examples\VSTest\DeterministicBuild (detbuilddocs -> origin) -λ dotnet test --collect:"XPlat Code Coverage" /p:DeterministicSourcePaths=true +λ dotnet test --collect:"XPlat Code Coverage" /p:DeterministicSourcePaths=true -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.DeterministicReport=true Test run for C:\git\coverlet\Documentation\Examples\VSTest\DeterministicBuild\XUnitTestProject1\bin\Debug\netcoreapp3.1\XUnitTestProject1.dll(.NETCoreApp,Version=v3.1) Microsoft (R) Test Execution Command Line Tool Version 16.5.0 Copyright (c) Microsoft Corporation. All rights reserved. @@ -78,5 +78,5 @@ Total tests: 1 You should see on output folder the coverlet source root mapping file generated. This is the confirmation that you're running coverage on deterministic build. ``` -Documentation\Examples\VSTest\DeterministicBuild\XUnitTestProject1\bin\Debug\netcoreapp3.1\CoverletSourceRootsMapping +Documentation\Examples\VSTest\DeterministicBuild\XUnitTestProject1\bin\Debug\netcoreapp3.1\CoverletSourceRootsMapping_XUnitTestProject1 ``` \ No newline at end of file diff --git a/src/coverlet.collector/DataCollection/CoverletCoverageCollector.cs b/src/coverlet.collector/DataCollection/CoverletCoverageCollector.cs index e1b27a219..d40a02e86 100644 --- a/src/coverlet.collector/DataCollection/CoverletCoverageCollector.cs +++ b/src/coverlet.collector/DataCollection/CoverletCoverageCollector.cs @@ -224,11 +224,13 @@ private static IServiceCollection GetDefaultServiceCollection(TestPlatformEqtTra serviceCollection.AddTransient(); serviceCollection.AddTransient(); serviceCollection.AddTransient(); + serviceCollection.AddTransient(); serviceCollection.AddTransient(_ => new CoverletLogger(eqtTrace, logger)); // We need to keep singleton/static semantics serviceCollection.AddSingleton(); // We cache resolutions - serviceCollection.AddSingleton(serviceProvider => new SourceRootTranslator(testModule, serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService())); + serviceCollection.AddSingleton(serviceProvider => + new SourceRootTranslator(testModule, serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService())); serviceCollection.AddSingleton(); return serviceCollection; } diff --git a/src/coverlet.collector/build/netstandard1.0/coverlet.collector.targets b/src/coverlet.collector/build/netstandard1.0/coverlet.collector.targets index 2e4adb4c5..7bd7b28e7 100644 --- a/src/coverlet.collector/build/netstandard1.0/coverlet.collector.targets +++ b/src/coverlet.collector/build/netstandard1.0/coverlet.collector.targets @@ -44,7 +44,7 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and <_mapping Include="@(_byProject->'%(Identity)|%(OriginalPath)=%(MappedPath)')" /> - <_sourceRootMappingFilePath>$([MSBuild]::EnsureTrailingSlash('$(OutputPath)'))CoverletSourceRootsMapping + <_sourceRootMappingFilePath>$([MSBuild]::EnsureTrailingSlash('$(OutputPath)'))CoverletSourceRootsMapping_$(AssemblyName) > _sourceRootMapping; private readonly Dictionary> _sourceToDeterministicPathMapping; - private const string MappingFileName = "CoverletSourceRootsMapping"; + private readonly string _mappingFileName; private Dictionary _resolutionCacheFiles; public SourceRootTranslator(ILogger logger, IFileSystem fileSystem) @@ -32,7 +32,7 @@ public SourceRootTranslator(ILogger logger, IFileSystem fileSystem) _sourceRootMapping = new Dictionary>(); } - public SourceRootTranslator(string moduleTestPath, ILogger logger, IFileSystem fileSystem) + public SourceRootTranslator(string moduleTestPath, ILogger logger, IFileSystem fileSystem, IAssemblyAdapter assemblyAdapter) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem)); @@ -44,6 +44,10 @@ public SourceRootTranslator(string moduleTestPath, ILogger logger, IFileSystem f { throw new FileNotFoundException($"Module test path '{moduleTestPath}' not found", moduleTestPath); } + + string assemblyName = assemblyAdapter.GetAssemblyName(moduleTestPath); + _mappingFileName = $"CoverletSourceRootsMapping_{assemblyName}"; + _sourceRootMapping = LoadSourceRootMapping(Path.GetDirectoryName(moduleTestPath)); _sourceToDeterministicPathMapping = LoadSourceToDeterministicPathMapping(_sourceRootMapping); } @@ -75,7 +79,7 @@ private Dictionary> LoadSourceRootMapping(string { var mapping = new Dictionary>(); - string mappingFilePath = Path.Combine(directory, MappingFileName); + string mappingFilePath = Path.Combine(directory, _mappingFileName); if (!_fileSystem.Exists(mappingFilePath)) { return mapping; diff --git a/src/coverlet.msbuild.tasks/InstrumentationTask.cs b/src/coverlet.msbuild.tasks/InstrumentationTask.cs index fa0868e11..9a0506f51 100644 --- a/src/coverlet.msbuild.tasks/InstrumentationTask.cs +++ b/src/coverlet.msbuild.tasks/InstrumentationTask.cs @@ -73,11 +73,13 @@ public override bool Execute() IServiceCollection serviceCollection = new ServiceCollection(); serviceCollection.AddTransient(); serviceCollection.AddTransient(); + serviceCollection.AddTransient(); serviceCollection.AddTransient(); serviceCollection.AddTransient(_ => _logger); serviceCollection.AddTransient(); // We cache resolutions - serviceCollection.AddSingleton(serviceProvider => new SourceRootTranslator(Path, serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService())); + serviceCollection.AddSingleton(serviceProvider => + new SourceRootTranslator(Path, serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService())); // We need to keep singleton/static semantics serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); diff --git a/src/coverlet.msbuild.tasks/coverlet.msbuild.targets b/src/coverlet.msbuild.tasks/coverlet.msbuild.targets index 6e7137385..ed288de59 100644 --- a/src/coverlet.msbuild.tasks/coverlet.msbuild.targets +++ b/src/coverlet.msbuild.tasks/coverlet.msbuild.targets @@ -24,7 +24,7 @@ <_mapping Include="@(_byProject->'%(Identity)|%(OriginalPath)=%(MappedPath)')" /> - <_sourceRootMappingFilePath>$([MSBuild]::EnsureTrailingSlash('$(OutputPath)'))CoverletSourceRootsMapping + <_sourceRootMappingFilePath>$([MSBuild]::EnsureTrailingSlash('$(OutputPath)'))CoverletSourceRootsMapping_$(AssemblyName) _mockDataColectionEvents; + private readonly Mock _mockDataCollectionEvents; private readonly Mock _mockDataCollectionSink; private readonly Mock _mockCoverageWrapper; private readonly Mock _mockCountDownEventFactory; private XmlElement _configurationElement; private readonly Mock _mockLogger; + private readonly Mock _mockAssemblyAdapter; public CoverletCoverageDataCollectorTests() { - _mockDataColectionEvents = new Mock(); + _mockDataCollectionEvents = new Mock(); _mockDataCollectionSink = new Mock(); _mockLogger = new Mock(); _configurationElement = null; @@ -47,6 +48,8 @@ public CoverletCoverageDataCollectorTests() _mockCoverageWrapper = new Mock(); _mockCountDownEventFactory = new Mock(); _mockCountDownEventFactory.Setup(def => def.Create(It.IsAny(), It.IsAny())).Returns(new Mock().Object); + _mockAssemblyAdapter = new Mock(); + _mockAssemblyAdapter.Setup(x => x.GetAssemblyName(It.IsAny())).Returns("abc"); } [Fact] @@ -63,14 +66,14 @@ public void OnSessionStartShouldInitializeCoverageWithCorrectCoverletSettings() serviceCollection.AddTransient(); serviceCollection.AddTransient(_ => new CoverletLogger(eqtTrace, logger)); serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(serviceProvider => new SourceRootTranslator(testModule, serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService())); + serviceCollection.AddSingleton(serviceProvider => new SourceRootTranslator(testModule, serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService(), _mockAssemblyAdapter.Object)); serviceCollection.AddSingleton(); return serviceCollection; }; _coverletCoverageDataCollector = new CoverletCoverageCollector(new TestPlatformEqtTrace(), _mockCoverageWrapper.Object, _mockCountDownEventFactory.Object, serviceCollectionFactory); _coverletCoverageDataCollector.Initialize( _configurationElement, - _mockDataColectionEvents.Object, + _mockDataCollectionEvents.Object, _mockDataCollectionSink.Object, _mockLogger.Object, _context); @@ -78,7 +81,7 @@ public void OnSessionStartShouldInitializeCoverageWithCorrectCoverletSettings() sessionStartProperties.Add("TestSources", new List { "abc.dll" }); - _mockDataColectionEvents.Raise(x => x.SessionStart += null, new SessionStartEventArgs(sessionStartProperties)); + _mockDataCollectionEvents.Raise(x => x.SessionStart += null, new SessionStartEventArgs(sessionStartProperties)); _mockCoverageWrapper.Verify(x => x.CreateCoverage(It.Is(y => string.Equals(y.TestModule, "abc.dll")), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); } @@ -97,14 +100,14 @@ public void OnSessionStartShouldPrepareModulesForCoverage() serviceCollection.AddTransient(); serviceCollection.AddTransient(_ => new CoverletLogger(eqtTrace, logger)); serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(serviceProvider => new SourceRootTranslator(testModule, serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService())); + serviceCollection.AddSingleton(serviceProvider => new SourceRootTranslator(testModule, serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService(), _mockAssemblyAdapter.Object)); serviceCollection.AddSingleton(); return serviceCollection; }; _coverletCoverageDataCollector = new CoverletCoverageCollector(new TestPlatformEqtTrace(), _mockCoverageWrapper.Object, _mockCountDownEventFactory.Object, serviceCollectionFactory); _coverletCoverageDataCollector.Initialize( _configurationElement, - _mockDataColectionEvents.Object, + _mockDataCollectionEvents.Object, _mockDataCollectionSink.Object, null, _context); @@ -133,7 +136,7 @@ public void OnSessionStartShouldPrepareModulesForCoverage() sessionStartProperties.Add("TestSources", new List { "abc.dll" }); _mockCoverageWrapper.Setup(x => x.CreateCoverage(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).Returns(coverage); - _mockDataColectionEvents.Raise(x => x.SessionStart += null, new SessionStartEventArgs(sessionStartProperties)); + _mockDataCollectionEvents.Raise(x => x.SessionStart += null, new SessionStartEventArgs(sessionStartProperties)); _mockCoverageWrapper.Verify(x => x.CreateCoverage(It.Is(y => y.TestModule.Contains("abc.dll")), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); _mockCoverageWrapper.Verify(x => x.PrepareModules(It.IsAny()), Times.Once); @@ -150,14 +153,14 @@ public void OnSessionEndShouldSendGetCoverageReportToTestPlatform() serviceCollection.AddTransient(); serviceCollection.AddTransient(_ => new CoverletLogger(eqtTrace, logger)); serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(serviceProvider => new SourceRootTranslator(testModule, serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService())); + serviceCollection.AddSingleton(serviceProvider => new SourceRootTranslator(testModule, serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService(), _mockAssemblyAdapter.Object)); serviceCollection.AddSingleton(); return serviceCollection; }; _coverletCoverageDataCollector = new CoverletCoverageCollector(new TestPlatformEqtTrace(), new CoverageWrapper(), _mockCountDownEventFactory.Object, serviceCollectionFactory); _coverletCoverageDataCollector.Initialize( _configurationElement, - _mockDataColectionEvents.Object, + _mockDataCollectionEvents.Object, _mockDataCollectionSink.Object, _mockLogger.Object, _context); @@ -173,8 +176,8 @@ public void OnSessionEndShouldSendGetCoverageReportToTestPlatform() IDictionary sessionStartProperties = new Dictionary(); sessionStartProperties.Add("TestSources", new List { Path.Combine(directory.FullName, Path.GetFileName(module)) }); - _mockDataColectionEvents.Raise(x => x.SessionStart += null, new SessionStartEventArgs(sessionStartProperties)); - _mockDataColectionEvents.Raise(x => x.SessionEnd += null, new SessionEndEventArgs()); + _mockDataCollectionEvents.Raise(x => x.SessionStart += null, new SessionStartEventArgs(sessionStartProperties)); + _mockDataCollectionEvents.Raise(x => x.SessionEnd += null, new SessionEndEventArgs()); _mockDataCollectionSink.Verify(x => x.SendFileAsync(It.IsAny()), Times.Once); @@ -196,9 +199,10 @@ public void OnSessionEndShouldSendCoverageReportsForMultipleFormatsToTestPlatfor serviceCollection.AddTransient(); serviceCollection.AddTransient(); + serviceCollection.AddTransient(); serviceCollection.AddTransient(_ => new CoverletLogger(eqtTrace, logger)); serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(serviceProvider => new SourceRootTranslator(testModule, serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService())); + serviceCollection.AddSingleton(serviceProvider => new SourceRootTranslator(testModule, serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService(), _mockAssemblyAdapter.Object)); serviceCollection.AddSingleton(); return serviceCollection; }; @@ -223,15 +227,15 @@ public void OnSessionEndShouldSendCoverageReportsForMultipleFormatsToTestPlatfor _coverletCoverageDataCollector.Initialize( _configurationElement, - _mockDataColectionEvents.Object, + _mockDataCollectionEvents.Object, mockDataCollectionSink.Object, _mockLogger.Object, _context); var sessionStartProperties = new Dictionary { { "TestSources", new List { "Test" } } }; - _mockDataColectionEvents.Raise(x => x.SessionStart += null, new SessionStartEventArgs(sessionStartProperties)); - _mockDataColectionEvents.Raise(x => x.SessionEnd += null, new SessionEndEventArgs()); + _mockDataCollectionEvents.Raise(x => x.SessionStart += null, new SessionStartEventArgs(sessionStartProperties)); + _mockDataCollectionEvents.Raise(x => x.SessionEnd += null, new SessionEndEventArgs()); mockDataCollectionSink.Verify(x => x.SendFileAsync(It.IsAny()), Times.Exactly(sendReportsCount)); Assert.Empty(reporters); @@ -249,16 +253,17 @@ public void OnSessionStartShouldLogWarningIfInstrumentationFailed() serviceCollection.AddTransient(); serviceCollection.AddTransient(); + serviceCollection.AddTransient(); serviceCollection.AddTransient(_ => new CoverletLogger(eqtTrace, logger)); serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(serviceProvider => new SourceRootTranslator(testModule, serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService())); + serviceCollection.AddSingleton(serviceProvider => new SourceRootTranslator(testModule, serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService(), _mockAssemblyAdapter.Object)); serviceCollection.AddSingleton(); return serviceCollection; }; _coverletCoverageDataCollector = new CoverletCoverageCollector(new TestPlatformEqtTrace(), _mockCoverageWrapper.Object, _mockCountDownEventFactory.Object, serviceCollectionFactory); _coverletCoverageDataCollector.Initialize( _configurationElement, - _mockDataColectionEvents.Object, + _mockDataCollectionEvents.Object, _mockDataCollectionSink.Object, _mockLogger.Object, _context); @@ -268,7 +273,7 @@ public void OnSessionStartShouldLogWarningIfInstrumentationFailed() _mockCoverageWrapper.Setup(x => x.PrepareModules(It.IsAny())).Throws(new FileNotFoundException()); - _mockDataColectionEvents.Raise(x => x.SessionStart += null, new SessionStartEventArgs(sessionStartProperties)); + _mockDataCollectionEvents.Raise(x => x.SessionStart += null, new SessionStartEventArgs(sessionStartProperties)); _mockLogger.Verify(x => x.LogWarning(_dataCollectionContext, It.Is(y => y.Contains("CoverletDataCollectorException")))); diff --git a/test/coverlet.core.tests/Coverage/CoverageTests.AsyncAwait.cs b/test/coverlet.core.tests/Coverage/CoverageTests.AsyncAwait.cs index e127c1660..8d0b70ec2 100644 --- a/test/coverlet.core.tests/Coverage/CoverageTests.AsyncAwait.cs +++ b/test/coverlet.core.tests/Coverage/CoverageTests.AsyncAwait.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.IO; +using System.Reflection; using System.Threading; using System.Threading.Tasks; using Coverlet.Core.Samples.Tests; @@ -112,7 +113,8 @@ public void AsyncAwait_Issue_669_2() ((ValueTask)instance.SendRequest()).ConfigureAwait(false).GetAwaiter().GetResult(); return Task.CompletedTask; }, - persistPrepareResultToFile: pathSerialize[0]); + persistPrepareResultToFile: pathSerialize[0], + assemblyLocation: Assembly.GetExecutingAssembly().Location); return 0; }, new string[] { path }); diff --git a/test/coverlet.core.tests/Coverage/CoverageTests.ExcludeFromCoverageAttribute.cs b/test/coverlet.core.tests/Coverage/CoverageTests.ExcludeFromCoverageAttribute.cs index bead5b913..ed8906a5f 100644 --- a/test/coverlet.core.tests/Coverage/CoverageTests.ExcludeFromCoverageAttribute.cs +++ b/test/coverlet.core.tests/Coverage/CoverageTests.ExcludeFromCoverageAttribute.cs @@ -31,7 +31,7 @@ public void TestCoverageSkipModule__AssemblyMarkedAsExcludeFromCodeCoverage() var instrumentationHelper = new InstrumentationHelper(new ProcessExitHandler(), new RetryHelper(), new FileSystem(), new Mock().Object, - new SourceRootTranslator(excludedbyattributeDll, new Mock().Object, new FileSystem())); + new SourceRootTranslator(excludedbyattributeDll, new Mock().Object, new FileSystem(), new AssemblyAdapter())); var parameters = new CoverageParameters { diff --git a/test/coverlet.core.tests/Coverage/CoverageTests.cs b/test/coverlet.core.tests/Coverage/CoverageTests.cs index 06be97c72..4f0465fe2 100644 --- a/test/coverlet.core.tests/Coverage/CoverageTests.cs +++ b/test/coverlet.core.tests/Coverage/CoverageTests.cs @@ -29,7 +29,7 @@ public void TestCoverage() // TODO: Find a way to mimick hits var instrumentationHelper = new InstrumentationHelper(new ProcessExitHandler(), new RetryHelper(), new FileSystem(), new Mock().Object, - new SourceRootTranslator(module, new Mock().Object, new FileSystem())); + new SourceRootTranslator(module, new Mock().Object, new FileSystem(), new AssemblyAdapter())); var parameters = new CoverageParameters { @@ -67,7 +67,7 @@ public void TestCoverageWithTestAssembly() var instrumentationHelper = new InstrumentationHelper(new ProcessExitHandler(), new RetryHelper(), new FileSystem(), new Mock().Object, - new SourceRootTranslator(module, new Mock().Object, new FileSystem())); + new SourceRootTranslator(module, new Mock().Object, new FileSystem(), new AssemblyAdapter())); var parameters = new CoverageParameters { @@ -83,7 +83,7 @@ public void TestCoverageWithTestAssembly() }; var coverage = new Coverage(Path.Combine(directory.FullName, Path.GetFileName(module)), parameters, _mockLogger.Object, instrumentationHelper, new FileSystem(), - new SourceRootTranslator(module, _mockLogger.Object, new FileSystem()), new CecilSymbolHelper()); + new SourceRootTranslator(module, _mockLogger.Object, new FileSystem(), new AssemblyAdapter()), new CecilSymbolHelper()); coverage.PrepareModules(); CoverageResult result = coverage.GetCoverageResult(); diff --git a/test/coverlet.core.tests/Coverage/InstrumenterHelper.cs b/test/coverlet.core.tests/Coverage/InstrumenterHelper.cs index fd39d8289..c2a218592 100644 --- a/test/coverlet.core.tests/Coverage/InstrumenterHelper.cs +++ b/test/coverlet.core.tests/Coverage/InstrumenterHelper.cs @@ -76,7 +76,8 @@ public static async Task Run(Func callM Func doesNotReturnAttributes = null, string persistPrepareResultToFile = null, bool disableRestoreModules = false, - bool skipAutoProps = false) + bool skipAutoProps = false, + string assemblyLocation = null) { if (persistPrepareResultToFile is null) { @@ -92,8 +93,9 @@ public static async Task Run(Func callM File.Copy(location, newPath); File.Copy(Path.ChangeExtension(location, ".pdb"), Path.ChangeExtension(newPath, ".pdb")); - SetTestContainer(newPath, disableRestoreModules); - + string sourceRootTranslatorModulePath = assemblyLocation ?? newPath; + SetTestContainer(sourceRootTranslatorModulePath, disableRestoreModules); + static string[] defaultFilters(string _) => Array.Empty(); var parameters = new CoverageParameters @@ -159,6 +161,7 @@ private static void SetTestContainer(string testModule = null, bool disableResto serviceCollection.AddTransient(); serviceCollection.AddTransient(); serviceCollection.AddTransient(); + serviceCollection.AddTransient(); serviceCollection.AddTransient(_ => new Mock().Object); // We need to keep singleton/static semantics @@ -173,7 +176,7 @@ private static void SetTestContainer(string testModule = null, bool disableResto serviceCollection.AddSingleton(serviceProvider => string.IsNullOrEmpty(testModule) ? new SourceRootTranslator(serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService()) : - new SourceRootTranslator(testModule, serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService())); + new SourceRootTranslator(testModule, serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService())); serviceCollection.AddSingleton(); diff --git a/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs b/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs index 8f5a05523..8c6917042 100644 --- a/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs +++ b/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs @@ -16,7 +16,7 @@ namespace Coverlet.Core.Helpers.Tests public class InstrumentationHelperTests { private readonly InstrumentationHelper _instrumentationHelper = - new(new ProcessExitHandler(), new RetryHelper(), new FileSystem(), new Mock().Object, new SourceRootTranslator(typeof(InstrumentationHelperTests).Assembly.Location, new Mock().Object, new FileSystem())); + new(new ProcessExitHandler(), new RetryHelper(), new FileSystem(), new Mock().Object, new SourceRootTranslator(typeof(InstrumentationHelperTests).Assembly.Location, new Mock().Object, new FileSystem(), new AssemblyAdapter())); [Fact] public void TestGetDependencies() @@ -41,7 +41,7 @@ public void EmbeddedPortablePDPHasLocalSource_NoDocumentsExist_ReturnsFalse() fileSystem.Setup(x => x.Exists(It.IsAny())).Returns(false); var instrumentationHelper = - new InstrumentationHelper(new ProcessExitHandler(), new RetryHelper(), fileSystem.Object, new Mock().Object, new SourceRootTranslator(typeof(InstrumentationHelperTests).Assembly.Location, new Mock().Object, new FileSystem())); + new InstrumentationHelper(new ProcessExitHandler(), new RetryHelper(), fileSystem.Object, new Mock().Object, new SourceRootTranslator(typeof(InstrumentationHelperTests).Assembly.Location, new Mock().Object, new FileSystem(), new AssemblyAdapter())); Assert.False(instrumentationHelper.PortablePdbHasLocalSource(typeof(InstrumentationHelperTests).Assembly.Location, AssemblySearchType.MissingAny)); Assert.False(instrumentationHelper.PortablePdbHasLocalSource(typeof(InstrumentationHelperTests).Assembly.Location, AssemblySearchType.MissingAll)); @@ -69,7 +69,7 @@ public void EmbeddedPortablePDPHasLocalSource_FirstDocumentDoesNotExist_ReturnsE }); var instrumentationHelper = - new InstrumentationHelper(new ProcessExitHandler(), new RetryHelper(), fileSystem.Object, new Mock().Object, new SourceRootTranslator(typeof(InstrumentationHelperTests).Assembly.Location, new Mock().Object, new FileSystem())); + new InstrumentationHelper(new ProcessExitHandler(), new RetryHelper(), fileSystem.Object, new Mock().Object, new SourceRootTranslator(typeof(InstrumentationHelperTests).Assembly.Location, new Mock().Object, new FileSystem(), new AssemblyAdapter())); Assert.Equal(result, instrumentationHelper.PortablePdbHasLocalSource(typeof(InstrumentationHelperTests).Assembly.Location, (AssemblySearchType) assemblySearchType)); } diff --git a/test/coverlet.core.tests/Helpers/SourceRootTranslatorTests.cs b/test/coverlet.core.tests/Helpers/SourceRootTranslatorTests.cs index 0768b9a70..e464635a3 100644 --- a/test/coverlet.core.tests/Helpers/SourceRootTranslatorTests.cs +++ b/test/coverlet.core.tests/Helpers/SourceRootTranslatorTests.cs @@ -18,14 +18,16 @@ public void Translate_Success() { string fileToTranslate = "/_/src/coverlet.core/obj/Debug/netstandard2.0/coverlet.core.pdb"; var logger = new Mock(); + var assemblyAdapter = new Mock(); + assemblyAdapter.Setup(x => x.GetAssemblyName(It.IsAny())).Returns("testLib"); var fileSystem = new Mock(); fileSystem.Setup(f => f.Exists(It.IsAny())).Returns((string p) => { - if (p == "testLib.dll" || p == @"C:\git\coverlet\src\coverlet.core\obj\Debug\netstandard2.0\coverlet.core.pdb" || p == "CoverletSourceRootsMapping") return true; + if (p == "testLib.dll" || p == @"C:\git\coverlet\src\coverlet.core\obj\Debug\netstandard2.0\coverlet.core.pdb" || p == "CoverletSourceRootsMapping_testLib") return true; return false; }); fileSystem.Setup(f => f.ReadAllLines(It.IsAny())).Returns(File.ReadAllLines(@"TestAssets/CoverletSourceRootsMappingTest")); - var translator = new SourceRootTranslator("testLib.dll", logger.Object, fileSystem.Object); + var translator = new SourceRootTranslator("testLib.dll", logger.Object, fileSystem.Object, assemblyAdapter.Object); Assert.Equal(@"C:\git\coverlet\src\coverlet.core\obj\Debug\netstandard2.0\coverlet.core.pdb", translator.ResolveFilePath(fileToTranslate)); Assert.Equal(@"C:\git\coverlet\src\coverlet.core\obj\Debug\netstandard2.0\coverlet.core.pdb", translator.ResolveFilePath(fileToTranslate)); } @@ -36,14 +38,16 @@ public void Translate_Success() public void TranslatePathRoot_Success() { var logger = new Mock(); + var assemblyAdapter = new Mock(); + assemblyAdapter.Setup(x => x.GetAssemblyName(It.IsAny())).Returns("testLib"); var fileSystem = new Mock(); fileSystem.Setup(f => f.Exists(It.IsAny())).Returns((string p) => { - if (p == "testLib.dll" || p == @"C:\git\coverlet\src\coverlet.core\obj\Debug\netstandard2.0\coverlet.core.pdb" || p == "CoverletSourceRootsMapping") return true; + if (p == "testLib.dll" || p == @"C:\git\coverlet\src\coverlet.core\obj\Debug\netstandard2.0\coverlet.core.pdb" || p == "CoverletSourceRootsMapping_testLib") return true; return false; }); fileSystem.Setup(f => f.ReadAllLines(It.IsAny())).Returns(File.ReadAllLines(@"TestAssets/CoverletSourceRootsMappingTest")); - var translator = new SourceRootTranslator("testLib.dll", logger.Object, fileSystem.Object); + var translator = new SourceRootTranslator("testLib.dll", logger.Object, fileSystem.Object, assemblyAdapter.Object); Assert.Equal(@"C:\git\coverlet\", translator.ResolvePathRoot("/_/")[0].OriginalPath); } @@ -52,14 +56,16 @@ public void Translate_EmptyFile() { string fileToTranslate = "/_/src/coverlet.core/obj/Debug/netstandard2.0/coverlet.core.pdb"; var logger = new Mock(); + var assemblyAdapter = new Mock(); + assemblyAdapter.Setup(x => x.GetAssemblyName(It.IsAny())).Returns("testLib"); var fileSystem = new Mock(); fileSystem.Setup(f => f.Exists(It.IsAny())).Returns((string p) => { - if (p == "testLib.dll" || p == "CoverletSourceRootsMapping") return true; + if (p == "testLib.dll" || p == "CoverletSourceRootsMapping_testLib") return true; return false; }); fileSystem.Setup(f => f.ReadAllLines(It.IsAny())).Returns(new string[0]); - var translator = new SourceRootTranslator("testLib.dll", logger.Object, fileSystem.Object); + var translator = new SourceRootTranslator("testLib.dll", logger.Object, fileSystem.Object, assemblyAdapter.Object); Assert.Equal(fileToTranslate, translator.ResolveFilePath(fileToTranslate)); } @@ -68,14 +74,16 @@ public void Translate_MalformedFile() { string fileToTranslate = "/_/src/coverlet.core/obj/Debug/netstandard2.0/coverlet.core.pdb"; var logger = new Mock(); + var assemblyAdapter = new Mock(); + assemblyAdapter.Setup(x => x.GetAssemblyName(It.IsAny())).Returns("testLib"); var fileSystem = new Mock(); fileSystem.Setup(f => f.Exists(It.IsAny())).Returns((string p) => { - if (p == "testLib.dll" || p == "CoverletSourceRootsMapping") return true; + if (p == "testLib.dll" || p == "CoverletSourceRootsMapping_testLib") return true; return false; }); fileSystem.Setup(f => f.ReadAllLines(It.IsAny())).Returns(new string[1] { "malformedRow" }); - var translator = new SourceRootTranslator("testLib.dll", logger.Object, fileSystem.Object); + var translator = new SourceRootTranslator("testLib.dll", logger.Object, fileSystem.Object, assemblyAdapter.Object); Assert.Equal(fileToTranslate, translator.ResolveFilePath(fileToTranslate)); logger.Verify(l => l.LogWarning(It.IsAny()), Times.Once); } diff --git a/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs b/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs index 39d5d06d3..1918a6d72 100644 --- a/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs +++ b/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs @@ -424,9 +424,9 @@ public void SkipEmbeddedPpdbWithoutLocalSource() var instrumentationHelper = new InstrumentationHelper(new ProcessExitHandler(), new RetryHelper(), new FileSystem(), loggerMock.Object, - new SourceRootTranslator(xunitDll, new Mock().Object, new FileSystem())); + new SourceRootTranslator(xunitDll, new Mock().Object, new FileSystem(), new AssemblyAdapter())); - var instrumenter = new Instrumenter(xunitDll, "_xunit_instrumented", new CoverageParameters(), loggerMock.Object, instrumentationHelper, new FileSystem(), new SourceRootTranslator(xunitDll, loggerMock.Object, new FileSystem()), new CecilSymbolHelper()); + var instrumenter = new Instrumenter(xunitDll, "_xunit_instrumented", new CoverageParameters(), loggerMock.Object, instrumentationHelper, new FileSystem(), new SourceRootTranslator(xunitDll, loggerMock.Object, new FileSystem(), new AssemblyAdapter()), new CecilSymbolHelper()); Assert.True(instrumentationHelper.HasPdb(xunitDll, out bool embedded)); Assert.True(embedded); Assert.False(instrumenter.CanInstrument()); @@ -436,9 +436,9 @@ public void SkipEmbeddedPpdbWithoutLocalSource() string sample = Directory.GetFiles(Directory.GetCurrentDirectory(), "coverlet.tests.projectsample.empty.dll").First(); instrumentationHelper = new InstrumentationHelper(new ProcessExitHandler(), new RetryHelper(), new FileSystem(), new Mock().Object, - new SourceRootTranslator(sample, new Mock().Object, new FileSystem())); + new SourceRootTranslator(Assembly.GetExecutingAssembly().Location, new Mock().Object, new FileSystem(), new AssemblyAdapter())); - instrumenter = new Instrumenter(sample, "_coverlet_tests_projectsample_empty", new CoverageParameters(), loggerMock.Object, instrumentationHelper, new FileSystem(), new SourceRootTranslator(sample, loggerMock.Object, new FileSystem()), new CecilSymbolHelper()); + instrumenter = new Instrumenter(sample, "_coverlet_tests_projectsample_empty", new CoverageParameters(), loggerMock.Object, instrumentationHelper, new FileSystem(), new SourceRootTranslator(Assembly.GetExecutingAssembly().Location, loggerMock.Object, new FileSystem(), new AssemblyAdapter()), new CecilSymbolHelper()); Assert.True(instrumentationHelper.HasPdb(sample, out embedded)); Assert.False(embedded); @@ -514,10 +514,10 @@ public void CanInstrumentFSharpAssemblyWithAnonymousRecord() string sample = Directory.GetFiles(Directory.GetCurrentDirectory(), "coverlet.tests.projectsample.fsharp.dll").First(); var instrumentationHelper = new InstrumentationHelper(new ProcessExitHandler(), new RetryHelper(), new FileSystem(), new Mock().Object, - new SourceRootTranslator(sample, new Mock().Object, new FileSystem())); + new SourceRootTranslator(sample, new Mock().Object, new FileSystem(), new AssemblyAdapter())); var instrumenter = new Instrumenter(sample, "_coverlet_tests_projectsample_fsharp", new CoverageParameters(), loggerMock.Object, instrumentationHelper, - new FileSystem(), new SourceRootTranslator(sample, loggerMock.Object, new FileSystem()), new CecilSymbolHelper()); + new FileSystem(), new SourceRootTranslator(sample, loggerMock.Object, new FileSystem(), new AssemblyAdapter()), new CecilSymbolHelper()); Assert.True(instrumentationHelper.HasPdb(sample, out bool embedded)); Assert.False(embedded); @@ -816,12 +816,12 @@ public void Instrumenter_MethodsWithoutReferenceToSource_AreSkipped() var instrumentationHelper = new InstrumentationHelper(new ProcessExitHandler(), new RetryHelper(), new FileSystem(), new Mock().Object, - new SourceRootTranslator(module, new Mock().Object, new FileSystem())); + new SourceRootTranslator(module, new Mock().Object, new FileSystem(), new AssemblyAdapter())); CoverageParameters parameters = new(); var instrumenter = new Instrumenter(Path.Combine(directory.FullName, Path.GetFileName(module)), "_coverlet_tests_projectsample_vbmynamespace", parameters, - loggerMock.Object, instrumentationHelper, new FileSystem(), new SourceRootTranslator(Path.Combine(directory.FullName, Path.GetFileName(module)), loggerMock.Object, new FileSystem()), new CecilSymbolHelper()); + loggerMock.Object, instrumentationHelper, new FileSystem(), new SourceRootTranslator(Path.Combine(directory.FullName, Path.GetFileName(module)), loggerMock.Object, new FileSystem(), new AssemblyAdapter()), new CecilSymbolHelper()); instrumentationHelper.BackupOriginalModule(Path.Combine(directory.FullName, Path.GetFileName(module)), "_coverlet_tests_projectsample_vbmynamespace"); diff --git a/test/coverlet.integration.tests/DeterministicBuild.cs b/test/coverlet.integration.tests/DeterministicBuild.cs index b9f11a047..5af1f3339 100644 --- a/test/coverlet.integration.tests/DeterministicBuild.cs +++ b/test/coverlet.integration.tests/DeterministicBuild.cs @@ -75,7 +75,7 @@ public void Msbuild() CreateDeterministicTestPropsFile(); DotnetCli($"build -c {_buildConfiguration} /p:DeterministicSourcePaths=true", out string standardOutput, out string _, _testProjectPath); Assert.Contains("Build succeeded.", standardOutput); - string sourceRootMappingFilePath = Path.Combine(_testProjectPath, "bin", _buildConfiguration, _testProjectTfm!, "CoverletSourceRootsMapping"); + string sourceRootMappingFilePath = Path.Combine(_testProjectPath, "bin", _buildConfiguration, _testProjectTfm!, "CoverletSourceRootsMapping_coverletsample.integration.determisticbuild"); Assert.True(File.Exists(sourceRootMappingFilePath), sourceRootMappingFilePath); Assert.True(!string.IsNullOrEmpty(File.ReadAllText(sourceRootMappingFilePath)), "Empty CoverletSourceRootsMapping file"); Assert.Contains("=/_/", File.ReadAllText(sourceRootMappingFilePath)); @@ -98,7 +98,7 @@ public void Msbuild_SourceLink() CreateDeterministicTestPropsFile(); DotnetCli($"build -c {_buildConfiguration} /p:DeterministicSourcePaths=true", out string standardOutput, out string _, _testProjectPath); Assert.Contains("Build succeeded.", standardOutput); - string sourceRootMappingFilePath = Path.Combine(_testProjectPath, "bin", _buildConfiguration, _testProjectTfm!, "CoverletSourceRootsMapping"); + string sourceRootMappingFilePath = Path.Combine(_testProjectPath, "bin", _buildConfiguration, _testProjectTfm!, "CoverletSourceRootsMapping_coverletsample.integration.determisticbuild"); Assert.True(File.Exists(sourceRootMappingFilePath), sourceRootMappingFilePath); Assert.True(!string.IsNullOrEmpty(File.ReadAllText(sourceRootMappingFilePath)), "Empty CoverletSourceRootsMapping file"); Assert.Contains("=/_/", File.ReadAllText(sourceRootMappingFilePath)); @@ -122,7 +122,7 @@ public void Collectors() CreateDeterministicTestPropsFile(); DotnetCli($"build -c {_buildConfiguration} /p:DeterministicSourcePaths=true", out string standardOutput, out string _, _testProjectPath); Assert.Contains("Build succeeded.", standardOutput); - string sourceRootMappingFilePath = Path.Combine(_testProjectPath, "bin", GetAssemblyBuildConfiguration().ToString(), _testProjectTfm!, "CoverletSourceRootsMapping"); + string sourceRootMappingFilePath = Path.Combine(_testProjectPath, "bin", GetAssemblyBuildConfiguration().ToString(), _testProjectTfm!, "CoverletSourceRootsMapping_coverletsample.integration.determisticbuild"); Assert.True(File.Exists(sourceRootMappingFilePath), sourceRootMappingFilePath); Assert.NotEmpty(File.ReadAllText(sourceRootMappingFilePath)); Assert.Contains("=/_/", File.ReadAllText(sourceRootMappingFilePath)); @@ -150,7 +150,7 @@ public void Collectors_SourceLink() CreateDeterministicTestPropsFile(); DotnetCli($"build -c {_buildConfiguration} /p:DeterministicSourcePaths=true", out string standardOutput, out string _, _testProjectPath); Assert.Contains("Build succeeded.", standardOutput); - string sourceRootMappingFilePath = Path.Combine(_testProjectPath, "bin", GetAssemblyBuildConfiguration().ToString(), _testProjectTfm!, "CoverletSourceRootsMapping"); + string sourceRootMappingFilePath = Path.Combine(_testProjectPath, "bin", GetAssemblyBuildConfiguration().ToString(), _testProjectTfm!, "CoverletSourceRootsMapping_coverletsample.integration.determisticbuild"); Assert.True(File.Exists(sourceRootMappingFilePath), sourceRootMappingFilePath); Assert.NotEmpty(File.ReadAllText(sourceRootMappingFilePath)); Assert.Contains("=/_/", File.ReadAllText(sourceRootMappingFilePath));