Skip to content

Commit 1bb08bb

Browse files
Inject InstrumentationHelper (#531)
Inject InstrumentationHelper
1 parent b4701d2 commit 1bb08bb

File tree

14 files changed

+164
-109
lines changed

14 files changed

+164
-109
lines changed

src/coverlet.collector/DataCollection/CoverageWrapper.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using Coverlet.Collector.Utilities.Interfaces;
22
using Coverlet.Core;
3+
using Coverlet.Core.Abstracts;
34
using Coverlet.Core.Logging;
45

56
namespace Coverlet.Collector.DataCollection
@@ -28,7 +29,8 @@ public Coverage CreateCoverage(CoverletSettings settings, ILogger coverletLogger
2829
settings.SingleHit,
2930
settings.MergeWith,
3031
settings.UseSourceLink,
31-
coverletLogger);
32+
coverletLogger,
33+
(IInstrumentationHelper)DependencyInjection.Current.GetService(typeof(IInstrumentationHelper)));
3234
}
3335

3436
/// <summary>

src/coverlet.console/Program.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using ConsoleTables;
99
using Coverlet.Console.Logging;
1010
using Coverlet.Core;
11+
using Coverlet.Core.Abstracts;
1112
using Coverlet.Core.Enums;
1213
using Coverlet.Core.Reporters;
1314
using McMaster.Extensions.CommandLineUtils;
@@ -69,7 +70,8 @@ static int Main(string[] args)
6970
singleHit.HasValue(),
7071
mergeWith.Value(),
7172
useSourceLink.HasValue(),
72-
logger);
73+
logger,
74+
(IInstrumentationHelper)DependencyInjection.Current.GetService(typeof(IInstrumentationHelper)));
7375
coverage.PrepareModules();
7476

7577
Process process = new Process();
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
namespace Coverlet.Core.Abstracts
2+
{
3+
public interface IInstrumentationHelper
4+
{
5+
void BackupOriginalModule(string module, string identifier);
6+
void DeleteHitsFile(string path);
7+
string[] GetCoverableModules(string module, string[] directories, bool includeTestAssembly);
8+
bool HasPdb(string module, out bool embedded);
9+
bool IsModuleExcluded(string module, string[] excludeFilters);
10+
bool IsModuleIncluded(string module, string[] includeFilters);
11+
bool IsValidFilterExpression(string filter);
12+
bool IsTypeExcluded(string module, string type, string[] excludeFilters);
13+
bool IsTypeIncluded(string module, string type, string[] includeFilters);
14+
void RestoreOriginalModule(string module, string identifier);
15+
bool EmbeddedPortablePdbHasLocalSource(string module);
16+
bool IsLocalMethod(string method);
17+
}
18+
}

src/coverlet.core/Coverage.cs

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
using System.Collections.Generic;
33
using System.IO;
44
using System.Linq;
5-
5+
using Coverlet.Core.Abstracts;
66
using Coverlet.Core.Helpers;
77
using Coverlet.Core.Instrumentation;
88
using Coverlet.Core.Logging;
@@ -26,6 +26,7 @@ public class Coverage
2626
private string _mergeWith;
2727
private bool _useSourceLink;
2828
private ILogger _logger;
29+
private IInstrumentationHelper _instrumentationHelper;
2930
private List<InstrumenterResult> _results;
3031

3132
public string Identifier
@@ -43,7 +44,8 @@ public Coverage(string module,
4344
bool singleHit,
4445
string mergeWith,
4546
bool useSourceLink,
46-
ILogger logger)
47+
ILogger logger,
48+
IInstrumentationHelper instrumentationHelper)
4749
{
4850
_module = module;
4951
_includeFilters = includeFilters;
@@ -56,44 +58,46 @@ public Coverage(string module,
5658
_mergeWith = mergeWith;
5759
_useSourceLink = useSourceLink;
5860
_logger = logger;
61+
_instrumentationHelper = instrumentationHelper;
5962

6063
_identifier = Guid.NewGuid().ToString();
6164
_results = new List<InstrumenterResult>();
6265
}
6366

64-
public Coverage(CoveragePrepareResult prepareResult, ILogger logger)
67+
public Coverage(CoveragePrepareResult prepareResult, ILogger logger, IInstrumentationHelper instrumentationHelper)
6568
{
6669
_identifier = prepareResult.Identifier;
6770
_module = prepareResult.Module;
6871
_mergeWith = prepareResult.MergeWith;
6972
_useSourceLink = prepareResult.UseSourceLink;
7073
_results = new List<InstrumenterResult>(prepareResult.Results);
7174
_logger = logger;
75+
_instrumentationHelper = instrumentationHelper;
7276
}
7377

7478
public CoveragePrepareResult PrepareModules()
7579
{
76-
string[] modules = InstrumentationHelper.GetCoverableModules(_module, _includeDirectories, _includeTestAssembly);
80+
string[] modules = _instrumentationHelper.GetCoverableModules(_module, _includeDirectories, _includeTestAssembly);
7781

7882
Array.ForEach(_excludeFilters ?? Array.Empty<string>(), filter => _logger.LogVerbose($"Excluded module filter '{filter}'"));
7983
Array.ForEach(_includeFilters ?? Array.Empty<string>(), filter => _logger.LogVerbose($"Included module filter '{filter}'"));
8084

81-
_excludeFilters = _excludeFilters?.Where(f => InstrumentationHelper.IsValidFilterExpression(f)).ToArray();
82-
_includeFilters = _includeFilters?.Where(f => InstrumentationHelper.IsValidFilterExpression(f)).ToArray();
85+
_excludeFilters = _excludeFilters?.Where(f => _instrumentationHelper.IsValidFilterExpression(f)).ToArray();
86+
_includeFilters = _includeFilters?.Where(f => _instrumentationHelper.IsValidFilterExpression(f)).ToArray();
8387

8488
foreach (var module in modules)
8589
{
86-
if (InstrumentationHelper.IsModuleExcluded(module, _excludeFilters) ||
87-
!InstrumentationHelper.IsModuleIncluded(module, _includeFilters))
90+
if (_instrumentationHelper.IsModuleExcluded(module, _excludeFilters) ||
91+
!_instrumentationHelper.IsModuleIncluded(module, _includeFilters))
8892
{
8993
_logger.LogVerbose($"Excluded module: '{module}'");
9094
continue;
9195
}
9296

93-
var instrumenter = new Instrumenter(module, _identifier, _excludeFilters, _includeFilters, _excludedSourceFiles, _excludeAttributes, _singleHit, _logger);
97+
var instrumenter = new Instrumenter(module, _identifier, _excludeFilters, _includeFilters, _excludedSourceFiles, _excludeAttributes, _singleHit, _logger, _instrumentationHelper);
9498
if (instrumenter.CanInstrument())
9599
{
96-
InstrumentationHelper.BackupOriginalModule(module, _identifier);
100+
_instrumentationHelper.BackupOriginalModule(module, _identifier);
97101

98102
// Guard code path and restore if instrumentation fails.
99103
try
@@ -105,7 +109,7 @@ public CoveragePrepareResult PrepareModules()
105109
catch (Exception ex)
106110
{
107111
_logger.LogWarning($"Unable to instrument module: {module} because : {ex.Message}");
108-
InstrumentationHelper.RestoreOriginalModule(module, _identifier);
112+
_instrumentationHelper.RestoreOriginalModule(module, _identifier);
109113
}
110114
}
111115
}
@@ -206,7 +210,7 @@ public CoverageResult GetCoverageResult()
206210
}
207211

208212
modules.Add(Path.GetFileName(result.ModulePath), documents);
209-
InstrumentationHelper.RestoreOriginalModule(result.ModulePath, _identifier);
213+
_instrumentationHelper.RestoreOriginalModule(result.ModulePath, _identifier);
210214
}
211215

212216
var coverageResult = new CoverageResult { Identifier = _identifier, Modules = modules, InstrumentedResults = _results };
@@ -301,7 +305,7 @@ private void CalculateCoverage()
301305
}
302306
}
303307

304-
InstrumentationHelper.DeleteHitsFile(result.HitsFilePath);
308+
_instrumentationHelper.DeleteHitsFile(result.HitsFilePath);
305309
}
306310
}
307311

src/coverlet.core/DependencyInjection.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
namespace Coverlet.Core
88
{
9-
internal static class DependencyInjection
9+
public static class DependencyInjection
1010
{
1111
private static Lazy<IServiceProvider> _serviceProvider = new Lazy<IServiceProvider>(() => InitDefaultServices(), true);
1212

@@ -28,6 +28,10 @@ private static IServiceProvider InitDefaultServices()
2828
IServiceCollection serviceCollection = new ServiceCollection();
2929
serviceCollection.AddTransient<IRetryHelper, RetryHelper>();
3030
serviceCollection.AddTransient<IProcessExitHandler, ProcessExitHandler>();
31+
32+
// We need to keep singleton/static semantics
33+
serviceCollection.AddSingleton<IInstrumentationHelper, InstrumentationHelper>();
34+
3135
return serviceCollection.BuildServiceProvider();
3236
}
3337

src/coverlet.core/Helpers/InstrumentationHelper.cs

Lines changed: 29 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,21 @@
99
using System.Reflection.PortableExecutable;
1010
using System.Text.RegularExpressions;
1111
using Coverlet.Core.Abstracts;
12-
using Microsoft.Extensions.FileSystemGlobbing;
13-
using Microsoft.Extensions.DependencyInjection;
14-
using Microsoft.Extensions.FileSystemGlobbing.Abstractions;
1512

1613
namespace Coverlet.Core.Helpers
1714
{
18-
internal static class InstrumentationHelper
15+
internal class InstrumentationHelper : IInstrumentationHelper
1916
{
20-
private static readonly ConcurrentDictionary<string, string> _backupList = new ConcurrentDictionary<string, string>();
17+
private readonly ConcurrentDictionary<string, string> _backupList = new ConcurrentDictionary<string, string>();
18+
private readonly IRetryHelper _retryHelper;
2119

22-
static InstrumentationHelper()
20+
public InstrumentationHelper(IProcessExitHandler processExitHandler, IRetryHelper retryHelper)
2321
{
24-
DependencyInjection.Current.GetService<IProcessExitHandler>().Add((s, e) => RestoreOriginalModules());
22+
processExitHandler.Add((s, e) => RestoreOriginalModules());
23+
_retryHelper = retryHelper;
2524
}
2625

27-
public static string[] GetCoverableModules(string module, string[] directories, bool includeTestAssembly)
26+
public string[] GetCoverableModules(string module, string[] directories, bool includeTestAssembly)
2827
{
2928
Debug.Assert(directories != null);
3029

@@ -68,7 +67,7 @@ public static string[] GetCoverableModules(string module, string[] directories,
6867
.ToArray();
6968
}
7069

71-
public static bool HasPdb(string module, out bool embedded)
70+
public bool HasPdb(string module, out bool embedded)
7271
{
7372
embedded = false;
7473
using (var moduleStream = File.OpenRead(module))
@@ -94,7 +93,7 @@ public static bool HasPdb(string module, out bool embedded)
9493
}
9594
}
9695

97-
public static bool EmbeddedPortablePdbHasLocalSource(string module)
96+
public bool EmbeddedPortablePdbHasLocalSource(string module)
9897
{
9998
using (FileStream moduleStream = File.OpenRead(module))
10099
using (var peReader = new PEReader(moduleStream))
@@ -129,7 +128,7 @@ public static bool EmbeddedPortablePdbHasLocalSource(string module)
129128
return true;
130129
}
131130

132-
public static void BackupOriginalModule(string module, string identifier)
131+
public void BackupOriginalModule(string module, string identifier)
133132
{
134133
var backupPath = GetBackupPath(module, identifier);
135134
var backupSymbolPath = Path.ChangeExtension(backupPath, ".pdb");
@@ -150,7 +149,7 @@ public static void BackupOriginalModule(string module, string identifier)
150149
}
151150
}
152151

153-
public static void RestoreOriginalModule(string module, string identifier)
152+
public void RestoreOriginalModule(string module, string identifier)
154153
{
155154
var backupPath = GetBackupPath(module, identifier);
156155
var backupSymbolPath = Path.ChangeExtension(backupPath, ".pdb");
@@ -159,14 +158,14 @@ public static void RestoreOriginalModule(string module, string identifier)
159158
// See: https://github.com/tonerdo/coverlet/issues/25
160159
var retryStrategy = CreateRetryStrategy();
161160

162-
DependencyInjection.Current.GetService<IRetryHelper>().Retry(() =>
161+
_retryHelper.Retry(() =>
163162
{
164163
File.Copy(backupPath, module, true);
165164
File.Delete(backupPath);
166165
_backupList.TryRemove(module, out string _);
167166
}, retryStrategy, 10);
168167

169-
DependencyInjection.Current.GetService<IRetryHelper>().Retry(() =>
168+
_retryHelper.Retry(() =>
170169
{
171170
if (File.Exists(backupSymbolPath))
172171
{
@@ -178,7 +177,7 @@ public static void RestoreOriginalModule(string module, string identifier)
178177
}, retryStrategy, 10);
179178
}
180179

181-
public static void RestoreOriginalModules()
180+
public void RestoreOriginalModules()
182181
{
183182
// Restore the original module - retry up to 10 times, since the destination file could be locked
184183
// See: https://github.com/tonerdo/coverlet/issues/25
@@ -187,7 +186,7 @@ public static void RestoreOriginalModules()
187186
foreach (string key in _backupList.Keys.ToList())
188187
{
189188
string backupPath = _backupList[key];
190-
DependencyInjection.Current.GetService<IRetryHelper>().Retry(() =>
189+
_retryHelper.Retry(() =>
191190
{
192191
File.Copy(backupPath, key, true);
193192
File.Delete(backupPath);
@@ -196,15 +195,15 @@ public static void RestoreOriginalModules()
196195
}
197196
}
198197

199-
public static void DeleteHitsFile(string path)
198+
public void DeleteHitsFile(string path)
200199
{
201200
// Retry hitting the hits file - retry up to 10 times, since the file could be locked
202201
// See: https://github.com/tonerdo/coverlet/issues/25
203202
var retryStrategy = CreateRetryStrategy();
204-
DependencyInjection.Current.GetService<IRetryHelper>().Retry(() => File.Delete(path), retryStrategy, 10);
203+
_retryHelper.Retry(() => File.Delete(path), retryStrategy, 10);
205204
}
206205

207-
public static bool IsValidFilterExpression(string filter)
206+
public bool IsValidFilterExpression(string filter)
208207
{
209208
if (filter == null)
210209
return false;
@@ -236,7 +235,7 @@ public static bool IsValidFilterExpression(string filter)
236235
return true;
237236
}
238237

239-
public static bool IsModuleExcluded(string module, string[] excludeFilters)
238+
public bool IsModuleExcluded(string module, string[] excludeFilters)
240239
{
241240
if (excludeFilters == null || excludeFilters.Length == 0)
242241
return false;
@@ -264,7 +263,7 @@ public static bool IsModuleExcluded(string module, string[] excludeFilters)
264263
return false;
265264
}
266265

267-
public static bool IsModuleIncluded(string module, string[] includeFilters)
266+
public bool IsModuleIncluded(string module, string[] includeFilters)
268267
{
269268
if (includeFilters == null || includeFilters.Length == 0)
270269
return true;
@@ -291,7 +290,7 @@ public static bool IsModuleIncluded(string module, string[] includeFilters)
291290
return false;
292291
}
293292

294-
public static bool IsTypeExcluded(string module, string type, string[] excludeFilters)
293+
public bool IsTypeExcluded(string module, string type, string[] excludeFilters)
295294
{
296295
if (excludeFilters == null || excludeFilters.Length == 0)
297296
return false;
@@ -303,7 +302,7 @@ public static bool IsTypeExcluded(string module, string type, string[] excludeFi
303302
return IsTypeFilterMatch(module, type, excludeFilters);
304303
}
305304

306-
public static bool IsTypeIncluded(string module, string type, string[] includeFilters)
305+
public bool IsTypeIncluded(string module, string type, string[] includeFilters)
307306
{
308307
if (includeFilters == null || includeFilters.Length == 0)
309308
return true;
@@ -315,10 +314,10 @@ public static bool IsTypeIncluded(string module, string type, string[] includeFi
315314
return IsTypeFilterMatch(module, type, includeFilters);
316315
}
317316

318-
public static bool IsLocalMethod(string method)
317+
public bool IsLocalMethod(string method)
319318
=> new Regex(WildcardToRegex("<*>*__*|*")).IsMatch(method);
320319

321-
private static bool IsTypeFilterMatch(string module, string type, string[] filters)
320+
private bool IsTypeFilterMatch(string module, string type, string[] filters)
322321
{
323322
Debug.Assert(module != null);
324323
Debug.Assert(filters != null);
@@ -338,15 +337,15 @@ private static bool IsTypeFilterMatch(string module, string type, string[] filte
338337
return false;
339338
}
340339

341-
private static string GetBackupPath(string module, string identifier)
340+
private string GetBackupPath(string module, string identifier)
342341
{
343342
return Path.Combine(
344343
Path.GetTempPath(),
345344
Path.GetFileNameWithoutExtension(module) + "_" + identifier + ".dll"
346345
);
347346
}
348347

349-
private static Func<TimeSpan> CreateRetryStrategy(int initialSleepSeconds = 6)
348+
private Func<TimeSpan> CreateRetryStrategy(int initialSleepSeconds = 6)
350349
{
351350
TimeSpan retryStrategy()
352351
{
@@ -358,14 +357,14 @@ TimeSpan retryStrategy()
358357
return retryStrategy;
359358
}
360359

361-
private static string WildcardToRegex(string pattern)
360+
private string WildcardToRegex(string pattern)
362361
{
363362
return "^" + Regex.Escape(pattern).
364363
Replace("\\*", ".*").
365364
Replace("\\?", "?") + "$";
366365
}
367366

368-
private static bool IsAssembly(string filePath)
367+
private bool IsAssembly(string filePath)
369368
{
370369
Debug.Assert(filePath != null);
371370

@@ -383,5 +382,4 @@ private static bool IsAssembly(string filePath)
383382
}
384383
}
385384
}
386-
}
387-
385+
}

0 commit comments

Comments
 (0)