Skip to content

Commit 34d6dc5

Browse files
authored
Flow ILogger to InstrumentationHelper (#727)
Flow ILogger to InstrumentationHelpe
1 parent 39e9b10 commit 34d6dc5

File tree

11 files changed

+107
-54
lines changed

11 files changed

+107
-54
lines changed

src/coverlet.collector/DataCollection/CoverletCoverageCollector.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
using System.Xml;
66
using Coverlet.Collector.Utilities;
77
using Coverlet.Collector.Utilities.Interfaces;
8+
using Coverlet.Core;
9+
using Coverlet.Core.Abstracts;
10+
using Coverlet.Core.Helpers;
11+
using Microsoft.Extensions.DependencyInjection;
812
using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection;
913

1014
namespace Coverlet.Collector.DataCollection
@@ -75,6 +79,7 @@ public override void Initialize(
7579
_dataSink = dataSink;
7680
_dataCollectionContext = environmentContext.SessionDataCollectionContext;
7781
_logger = new TestPlatformLogger(logger, _dataCollectionContext);
82+
DependencyInjection.Set(GetServiceProvider(_eqtTrace, _logger));
7883

7984
// Register events
8085
_events.SessionStart += OnSessionStart;
@@ -203,5 +208,19 @@ private static IEnumerable<string> GetPropertyValueWrapper(SessionStartEventArgs
203208
{
204209
return sessionStartEventArgs.GetPropertyValue<IEnumerable<string>>(CoverletConstants.TestSourcesPropertyName);
205210
}
211+
212+
private static IServiceProvider GetServiceProvider(TestPlatformEqtTrace eqtTrace, TestPlatformLogger logger)
213+
{
214+
IServiceCollection serviceCollection = new ServiceCollection();
215+
serviceCollection.AddTransient<IRetryHelper, RetryHelper>();
216+
serviceCollection.AddTransient<IProcessExitHandler, ProcessExitHandler>();
217+
serviceCollection.AddTransient<IFileSystem, FileSystem>();
218+
serviceCollection.AddTransient<ILogger, CoverletLogger>(_ => new CoverletLogger(eqtTrace, logger));
219+
220+
// We need to keep singleton/static semantics
221+
serviceCollection.AddSingleton<IInstrumentationHelper, InstrumentationHelper>();
222+
223+
return serviceCollection.BuildServiceProvider();
224+
}
206225
}
207226
}

src/coverlet.console/Program.cs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,31 @@
1010
using Coverlet.Core;
1111
using Coverlet.Core.Abstracts;
1212
using Coverlet.Core.Enums;
13-
using Coverlet.Core.Extensions;
13+
using Coverlet.Core.Helpers;
1414
using Coverlet.Core.Reporters;
1515
using McMaster.Extensions.CommandLineUtils;
16+
using Microsoft.Extensions.DependencyInjection;
1617

1718
namespace Coverlet.Console
1819
{
1920
class Program
2021
{
2122
static int Main(string[] args)
2223
{
23-
var logger = new ConsoleLogger();
24+
IServiceCollection serviceCollection = new ServiceCollection();
25+
serviceCollection.AddTransient<IRetryHelper, RetryHelper>();
26+
serviceCollection.AddTransient<IProcessExitHandler, ProcessExitHandler>();
27+
serviceCollection.AddTransient<IFileSystem, FileSystem>();
28+
serviceCollection.AddTransient<ILogger, ConsoleLogger>();
29+
30+
// We need to keep singleton/static semantics
31+
serviceCollection.AddSingleton<IInstrumentationHelper, InstrumentationHelper>();
32+
33+
DependencyInjection.Set(serviceCollection.BuildServiceProvider());
34+
35+
var logger = (ConsoleLogger) DependencyInjection.Current.GetService<ILogger>();
36+
var fileSystem = DependencyInjection.Current.GetService<IFileSystem>();
37+
2438
var app = new CommandLineApplication();
2539
app.Name = "coverlet";
2640
app.FullName = "Cross platform .NET Core code coverage tool";
@@ -60,7 +74,6 @@ static int Main(string[] args)
6074
// Adjust log level based on user input.
6175
logger.Level = verbosity.ParsedValue;
6276
}
63-
var fileSystem = DependencyInjection.Current.GetService<IFileSystem>();
6477
Coverage coverage = new Coverage(module.Value,
6578
includeFilters.Values.ToArray(),
6679
includeDirectories.Values.ToArray(),

src/coverlet.core/Abstracts/IInstrumentationHelper.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@ internal interface IInstrumentationHelper
1515
bool EmbeddedPortablePdbHasLocalSource(string module, out string firstNotFoundDocument);
1616
bool PortablePdbHasLocalSource(string module, out string firstNotFoundDocument);
1717
bool IsLocalMethod(string method);
18+
void SetLogger(ILogger logger);
1819
}
1920
}
Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,22 @@
11
using System;
22

3-
using Coverlet.Core.Abstracts;
4-
using Coverlet.Core.Helpers;
5-
using Microsoft.Extensions.DependencyInjection;
6-
73
namespace Coverlet.Core
84
{
95
internal static class DependencyInjection
106
{
11-
private static Lazy<IServiceProvider> _serviceProvider = new Lazy<IServiceProvider>(() => InitDefaultServices(), true);
7+
private static IServiceProvider _serviceProvider;
128

139
public static IServiceProvider Current
1410
{
1511
get
1612
{
17-
return _serviceProvider.Value;
13+
return _serviceProvider;
1814
}
1915
}
2016

2117
public static void Set(IServiceProvider serviceProvider)
2218
{
23-
_serviceProvider = new Lazy<IServiceProvider>(() => serviceProvider);
19+
_serviceProvider = serviceProvider;
2420
}
25-
26-
private static IServiceProvider InitDefaultServices()
27-
{
28-
IServiceCollection serviceCollection = new ServiceCollection();
29-
serviceCollection.AddTransient<IRetryHelper, RetryHelper>();
30-
serviceCollection.AddTransient<IProcessExitHandler, ProcessExitHandler>();
31-
serviceCollection.AddTransient<IFileSystem, FileSystem>();
32-
serviceCollection.AddTransient<IConsole, SystemConsole>();
33-
34-
// We need to keep singleton/static semantics
35-
serviceCollection.AddSingleton<IInstrumentationHelper, InstrumentationHelper>();
36-
37-
return serviceCollection.BuildServiceProvider();
38-
}
39-
4021
}
4122
}

src/coverlet.core/Helpers/InstrumentationHelper.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@ internal class InstrumentationHelper : IInstrumentationHelper
1818
private readonly ConcurrentDictionary<string, string> _backupList = new ConcurrentDictionary<string, string>();
1919
private readonly IRetryHelper _retryHelper;
2020
private readonly IFileSystem _fileSystem;
21+
private ILogger _logger;
2122

22-
public InstrumentationHelper(IProcessExitHandler processExitHandler, IRetryHelper retryHelper, IFileSystem fileSystem)
23+
public InstrumentationHelper(IProcessExitHandler processExitHandler, IRetryHelper retryHelper, IFileSystem fileSystem, ILogger logger)
2324
{
2425
processExitHandler.Add((s, e) => RestoreOriginalModules());
2526
_retryHelper = retryHelper;
2627
_fileSystem = fileSystem;
28+
_logger = logger;
2729
}
2830

2931
public string[] GetCoverableModules(string module, string[] directories, bool includeTestAssembly)
@@ -153,8 +155,7 @@ public bool PortablePdbHasLocalSource(string module, out string firstNotFoundDoc
153155
}
154156
catch (BadImageFormatException)
155157
{
156-
// TODO log this to warning
157-
// In case of non portable pdb we get exception so we skip file sources check
158+
_logger.LogWarning($"{nameof(BadImageFormatException)} during MetadataReaderProvider.FromPortablePdbStream in InstrumentationHelper.PortablePdbHasLocalSource, unable to check if module has got local source.");
158159
return true;
159160
}
160161
foreach (DocumentHandle docHandle in metadataReader.Documents)
@@ -367,6 +368,11 @@ public bool IsTypeIncluded(string module, string type, string[] includeFilters)
367368
public bool IsLocalMethod(string method)
368369
=> new Regex(WildcardToRegex("<*>*__*|*")).IsMatch(method);
369370

371+
public void SetLogger(ILogger logger)
372+
{
373+
_logger = logger;
374+
}
375+
370376
private bool IsTypeFilterMatch(string module, string type, string[] filters)
371377
{
372378
Debug.Assert(module != null);

src/coverlet.msbuild.tasks/CoverageResultTask.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ public override bool Execute()
9393
Coverage coverage = null;
9494
using (Stream instrumenterStateStream = fileSystem.NewFileStream(InstrumenterState.ItemSpec, FileMode.Open))
9595
{
96+
var instrumentationHelper = DependencyInjection.Current.GetService<IInstrumentationHelper>();
97+
// Task.Log is teared down after a task and thus the new MSBuildLogger must be passed to the InstrumentationHelper
98+
// https://github.com/microsoft/msbuild/issues/5153
99+
instrumentationHelper.SetLogger(_logger);
96100
coverage = new Coverage(CoveragePrepareResult.Deserialize(instrumenterStateStream), this._logger, DependencyInjection.Current.GetService<IInstrumentationHelper>(), fileSystem);
97101
}
98102

src/coverlet.msbuild.tasks/InstrumentationTask.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44

55
using Coverlet.Core;
66
using Coverlet.Core.Abstracts;
7-
using Coverlet.Core.Extensions;
7+
using Coverlet.Core.Helpers;
88
using Microsoft.Build.Framework;
99
using Microsoft.Build.Utilities;
10+
using Microsoft.Extensions.DependencyInjection;
11+
using ILogger = Coverlet.Core.Abstracts.ILogger;
1012

1113
namespace Coverlet.MSbuild.Tasks
1214
{
@@ -119,6 +121,18 @@ public override bool Execute()
119121
{
120122
WaitForDebuggerIfEnabled();
121123

124+
IServiceCollection serviceCollection = new ServiceCollection();
125+
serviceCollection.AddSingleton<IRetryHelper, RetryHelper>();
126+
serviceCollection.AddTransient<IProcessExitHandler, ProcessExitHandler>();
127+
serviceCollection.AddTransient<IFileSystem, FileSystem>();
128+
serviceCollection.AddTransient<IConsole, SystemConsole>();
129+
serviceCollection.AddTransient<ILogger, MSBuildLogger>(x => _logger);
130+
131+
// We need to keep singleton/static semantics
132+
serviceCollection.AddSingleton<IInstrumentationHelper, InstrumentationHelper>();
133+
134+
DependencyInjection.Set(serviceCollection.BuildServiceProvider());
135+
122136
try
123137
{
124138
var includeFilters = _include?.Split(',');

test/coverlet.core.tests/Coverage/CoverageTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace Coverlet.Core.Tests
1010
{
1111
public partial class CoverageTests
1212
{
13-
private readonly InstrumentationHelper _instrumentationHelper = new InstrumentationHelper(new ProcessExitHandler(), new RetryHelper(), new FileSystem());
13+
private readonly InstrumentationHelper _instrumentationHelper = new InstrumentationHelper(new ProcessExitHandler(), new RetryHelper(), new FileSystem(), new Mock<ILogger>().Object);
1414
private readonly Mock<ILogger> _mockLogger = new Mock<ILogger>();
1515

1616
[Fact]

test/coverlet.core.tests/Coverage/InstrumenterHelper.cs

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,8 @@ private static BuildConfiguration GetAssemblyBuildConfiguration()
303303

304304
static class TestInstrumentationHelper
305305
{
306+
private static IServiceProvider _processWideContainer;
307+
306308
/// <summary>
307309
/// caller sample: TestInstrumentationHelper.GenerateHtmlReport(result, sourceFileFilter: @"+**\Samples\Instrumentation.cs");
308310
/// TestInstrumentationHelper.GenerateHtmlReport(result);
@@ -335,14 +337,16 @@ public static void GenerateHtmlReport(CoverageResult coverageResult, IReporter r
335337

336338
public static CoverageResult GetCoverageResult(string filePath)
337339
{
340+
SetTestContainer();
338341
using var result = new FileStream(filePath, FileMode.Open);
339342
var logger = new Mock<ILogger>();
340343
logger.Setup(l => l.LogVerbose(It.IsAny<string>())).Callback((string message) =>
341344
{
342345
Assert.DoesNotContain("not found for module: ", message);
343346
});
347+
_processWideContainer.GetRequiredService<IInstrumentationHelper>().SetLogger(logger.Object);
344348
CoveragePrepareResult coveragePrepareResultLoaded = CoveragePrepareResult.Deserialize(result);
345-
Coverage coverage = new Coverage(coveragePrepareResultLoaded, logger.Object, DependencyInjection.Current.GetService<IInstrumentationHelper>(), new FileSystem());
349+
Coverage coverage = new Coverage(coveragePrepareResultLoaded, logger.Object, _processWideContainer.GetService<IInstrumentationHelper>(), new FileSystem());
346350
return coverage.GetCoverageResult();
347351
}
348352

@@ -353,22 +357,7 @@ async public static Task<CoveragePrepareResult> Run<T>(Func<dynamic, Task> callM
353357
throw new ArgumentNullException(nameof(persistPrepareResultToFile));
354358
}
355359

356-
var serviceCollection = new ServiceCollection();
357-
serviceCollection.AddTransient<IRetryHelper, CustomRetryHelper>();
358-
serviceCollection.AddTransient<IProcessExitHandler, CustomProcessExitHandler>();
359-
serviceCollection.AddTransient<IFileSystem, FileSystem>();
360-
if (disableRestoreModules)
361-
{
362-
serviceCollection.AddSingleton<IInstrumentationHelper, InstrumentationHelperForDebugging>();
363-
}
364-
else
365-
{
366-
serviceCollection.AddSingleton<IInstrumentationHelper, InstrumentationHelper>();
367-
}
368-
369-
// Setup correct retry helper to avoid exception in InstrumentationHelper.RestoreOriginalModules on remote process exit
370-
DependencyInjection.Set(serviceCollection.BuildServiceProvider());
371-
360+
SetTestContainer(disableRestoreModules);
372361

373362
// Rename test file to avoid locks
374363
string location = typeof(T).Assembly.Location;
@@ -392,7 +381,7 @@ async public static Task<CoveragePrepareResult> Run<T>(Func<dynamic, Task> callM
392381
{
393382
"[xunit.*]*",
394383
"[coverlet.*]*"
395-
}).ToArray(), Array.Empty<string>(), Array.Empty<string>(), true, false, "", false, new Logger(logFile), DependencyInjection.Current.GetService<IInstrumentationHelper>(), DependencyInjection.Current.GetService<IFileSystem>());
384+
}).ToArray(), Array.Empty<string>(), Array.Empty<string>(), true, false, "", false, new Logger(logFile), _processWideContainer.GetService<IInstrumentationHelper>(), _processWideContainer.GetService<IFileSystem>());
396385
CoveragePrepareResult prepareResult = coverage.PrepareModules();
397386

398387
Assert.Single(prepareResult.Results);
@@ -421,6 +410,30 @@ async public static Task<CoveragePrepareResult> Run<T>(Func<dynamic, Task> callM
421410

422411
return prepareResult;
423412
}
413+
414+
private static void SetTestContainer(bool disableRestoreModules = false)
415+
{
416+
LazyInitializer.EnsureInitialized(ref _processWideContainer, () =>
417+
{
418+
var serviceCollection = new ServiceCollection();
419+
serviceCollection.AddTransient<IRetryHelper, CustomRetryHelper>();
420+
serviceCollection.AddTransient<IProcessExitHandler, CustomProcessExitHandler>();
421+
serviceCollection.AddTransient<IFileSystem, FileSystem>();
422+
serviceCollection.AddTransient(_ => new Mock<ILogger>().Object);
423+
424+
// We need to keep singleton/static semantics
425+
if (disableRestoreModules)
426+
{
427+
serviceCollection.AddSingleton<IInstrumentationHelper, InstrumentationHelperForDebugging>();
428+
}
429+
else
430+
{
431+
serviceCollection.AddSingleton<IInstrumentationHelper, InstrumentationHelper>();
432+
}
433+
434+
return serviceCollection.BuildServiceProvider();
435+
});
436+
}
424437
}
425438

426439
class CustomProcessExitHandler : IProcessExitHandler
@@ -514,8 +527,8 @@ public void LogWarning(string message)
514527

515528
class InstrumentationHelperForDebugging : InstrumentationHelper
516529
{
517-
public InstrumentationHelperForDebugging(IProcessExitHandler processExitHandler, IRetryHelper retryHelper, IFileSystem fileSystem)
518-
: base(processExitHandler, retryHelper, fileSystem)
530+
public InstrumentationHelperForDebugging(IProcessExitHandler processExitHandler, IRetryHelper retryHelper, IFileSystem fileSystem, ILogger logger)
531+
: base(processExitHandler, retryHelper, fileSystem, logger)
519532
{
520533

521534
}

test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33
using Xunit;
44
using System.Collections.Generic;
55
using System.Linq;
6+
using Moq;
7+
using Coverlet.Core.Abstracts;
68

79
namespace Coverlet.Core.Helpers.Tests
810
{
911
public class InstrumentationHelperTests
1012
{
11-
private readonly InstrumentationHelper _instrumentationHelper = new InstrumentationHelper(new ProcessExitHandler(), new RetryHelper(), new FileSystem());
13+
private readonly InstrumentationHelper _instrumentationHelper = new InstrumentationHelper(new ProcessExitHandler(), new RetryHelper(), new FileSystem(), new Mock<ILogger>().Object);
1214

1315
[Fact]
1416
public void TestGetDependencies()

0 commit comments

Comments
 (0)