Skip to content

Commit 6d32ae5

Browse files
author
Joseph Sun
committed
Revert "Revert (#3)"
This reverts commit d9621e2.
1 parent d9621e2 commit 6d32ae5

File tree

3 files changed

+132
-5
lines changed

3 files changed

+132
-5
lines changed

src/coverlet.console/Program.cs

+14-4
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ static int Main(string[] args)
4949
var doesNotReturnAttributes = new Option<string[]>("--does-not-return-attribute", "Attributes that mark methods that do not return") { Arity = ArgumentArity.ZeroOrMore, AllowMultipleArgumentsPerToken = true };
5050
var excludeAssembliesWithoutSources = new Option<string>("--exclude-assemblies-without-sources", "Specifies behaviour of heuristic to ignore assemblies with missing source documents.") { Arity = ArgumentArity.ZeroOrOne };
5151
var sourceMappingFile = new Option<string>("--source-mapping-file", "Specifies the path to a SourceRootsMappings file.") { Arity = ArgumentArity.ZeroOrOne };
52+
var unloadCoverletFromModulesOnly = new Option<bool>("---only-unload-modules", "Specifies Whether or not coverlet will only unload after unit tests are finished and skip coverage calculation"){ Arity = ArgumentArity.ZeroOrOne };
5253

5354
RootCommand rootCommand = new()
5455
{
@@ -73,7 +74,8 @@ static int Main(string[] args)
7374
useSourceLink,
7475
doesNotReturnAttributes,
7576
excludeAssembliesWithoutSources,
76-
sourceMappingFile
77+
sourceMappingFile,
78+
unloadCoverletFromModulesOnly
7779
};
7880

7981
rootCommand.Description = "Cross platform .NET Core code coverage tool";
@@ -102,6 +104,7 @@ static int Main(string[] args)
102104
string[] doesNotReturnAttributesValue = context.ParseResult.GetValueForOption(doesNotReturnAttributes);
103105
string excludeAssembliesWithoutSourcesValue = context.ParseResult.GetValueForOption(excludeAssembliesWithoutSources);
104106
string sourceMappingFileValue = context.ParseResult.GetValueForOption(sourceMappingFile);
107+
bool unloadCoverletFromModulesOnlyBool = context.ParseResult.GetValueForOption(unloadCoverletFromModulesOnly);
105108

106109
if (string.IsNullOrEmpty(moduleOrAppDirectoryValue) || string.IsNullOrWhiteSpace(moduleOrAppDirectoryValue))
107110
throw new ArgumentException("No test assembly or application directory specified.");
@@ -127,7 +130,8 @@ static int Main(string[] args)
127130
useSourceLinkValue,
128131
doesNotReturnAttributesValue,
129132
excludeAssembliesWithoutSourcesValue,
130-
sourceMappingFileValue);
133+
sourceMappingFileValue,
134+
unloadCoverletFromModulesOnlyBool);
131135
context.ExitCode = taskStatus;
132136

133137
});
@@ -154,7 +158,8 @@ private static Task<int> HandleCommand(string moduleOrAppDirectory,
154158
bool useSourceLink,
155159
string[] doesNotReturnAttributes,
156160
string excludeAssembliesWithoutSources,
157-
string sourceMappingFile
161+
string sourceMappingFile,
162+
bool unloadCoverletFromModulesOnly
158163
)
159164
{
160165

@@ -232,6 +237,12 @@ string sourceMappingFile
232237

233238
string dOutput = output != null ? output : Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar.ToString();
234239

240+
if (unloadCoverletFromModulesOnly)
241+
{
242+
int unloadModuleExitCode = coverage.UnloadModules();
243+
return Task.FromResult(unloadModuleExitCode);
244+
}
245+
235246
logger.LogInformation("\nCalculating coverage result...");
236247

237248
CoverageResult result = coverage.GetCoverageResult();
@@ -385,7 +396,6 @@ string sourceMappingFile
385396

386397
return Task.FromResult(exitCode);
387398

388-
389399
}
390400

391401
catch (Win32Exception we) when (we.Source == "System.Diagnostics.Process")

src/coverlet.core/Coverage.cs

+47-1
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ public CoverageResult GetCoverageResult()
241241
}
242242

243243
modules.Add(Path.GetFileName(result.ModulePath), documents);
244-
_instrumentationHelper.RestoreOriginalModule(result.ModulePath, Identifier);
244+
UnloadModule(result.ModulePath);
245245
}
246246

247247
// In case of anonymous delegate compiler generate a custom class and passes it as type.method delegate.
@@ -326,6 +326,52 @@ public CoverageResult GetCoverageResult()
326326
return coverageResult;
327327
}
328328

329+
/// <summary>
330+
/// unloads all modules that were instrumented
331+
/// </summary>
332+
/// <returns> exit code of module unloading </returns>
333+
public int UnloadModules()
334+
{
335+
string[] modules = _instrumentationHelper.GetCoverableModules(_moduleOrAppDirectory,
336+
_parameters.IncludeDirectories, _parameters.IncludeTestAssembly);
337+
338+
var validModules = _instrumentationHelper
339+
.SelectModules(modules, _parameters.IncludeFilters, _parameters.ExcludeFilters);
340+
var validModulesAsList = validModules.ToList();
341+
foreach (string modulePath in validModulesAsList) {
342+
try
343+
{
344+
_instrumentationHelper.RestoreOriginalModule(modulePath, Identifier);
345+
_logger.LogVerbose("All Modules unloaded.");
346+
}
347+
catch (Exception e)
348+
{
349+
_logger.LogVerbose($"{e.InnerException} occured, module unloading aborted.");
350+
return -1;
351+
}
352+
}
353+
354+
return 0;
355+
}
356+
357+
/// <summary>
358+
/// Invoke the unloading of modules and restoration of the original assembly files
359+
/// </summary>
360+
/// <param name="modulePath"></param>
361+
/// <returns> exist code of unloading modules </returns>
362+
public void UnloadModule(string modulePath)
363+
{
364+
try
365+
{
366+
_instrumentationHelper.RestoreOriginalModule(modulePath, Identifier);
367+
_logger.LogVerbose($"Module at {modulePath} is unloaded.");
368+
}
369+
catch (Exception e)
370+
{
371+
_logger.LogVerbose($"{e.InnerException} occured, module unloading aborted.");
372+
}
373+
}
374+
329375
private bool BranchInCompilerGeneratedClass(string methodName)
330376
{
331377
foreach (InstrumenterResult instrumentedResult in _results)

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

+71
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,77 @@ public void TestCoverageMergeWithWrongParameter()
177177

178178
directory.Delete(true);
179179
}
180+
181+
[Fact]
182+
public void TestCoverageUnloadWithParameters()
183+
{
184+
string module = GetType().Assembly.Location;
185+
string pdb = Path.Combine(Path.GetDirectoryName(module), Path.GetFileNameWithoutExtension(module) + ".pdb");
186+
187+
DirectoryInfo directory = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()));
188+
189+
File.Copy(module, Path.Combine(directory.FullName, Path.GetFileName(module)), true);
190+
File.Copy(pdb, Path.Combine(directory.FullName, Path.GetFileName(pdb)), true);
191+
192+
var mockInstrumentationHelper = new Mock<IInstrumentationHelper>();
193+
194+
var parameters = new CoverageParameters
195+
{
196+
IncludeFilters = new string[] { "[coverlet.tests.projectsample.excludedbyattribute*]*" },
197+
IncludeDirectories = Array.Empty<string>(),
198+
ExcludeFilters = Array.Empty<string>(),
199+
ExcludedSourceFiles = Array.Empty<string>(),
200+
ExcludeAttributes = Array.Empty<string>(),
201+
IncludeTestAssembly = false,
202+
SingleHit = false,
203+
MergeWith = string.Empty,
204+
UseSourceLink = false
205+
};
206+
207+
var coverage = new Coverage(Path.Combine(directory.FullName, Path.GetFileName(module)), parameters, _mockLogger.Object, mockInstrumentationHelper.Object, new FileSystem(), new SourceRootTranslator(_mockLogger.Object, new FileSystem()), new CecilSymbolHelper());
208+
coverage.PrepareModules();
209+
coverage.UnloadModule(Path.Combine(directory.FullName, Path.GetFileName(module)));
210+
211+
mockInstrumentationHelper.Verify(i => i.RestoreOriginalModule(It.Is<string>(v => v.Equals(Path.Combine(directory.FullName, Path.GetFileName(module)))), It.IsAny<string>()), Times.Once);
212+
_mockLogger.Verify(l => l.LogVerbose(It.Is<string>(v => v.Equals($"Module at {Path.Combine(directory.FullName, Path.GetFileName(module))} is unloaded."))), Times.Once);
213+
}
214+
215+
[Fact]
216+
public void TestCoverageUnloadWithNoParameters()
217+
{
218+
string module = GetType().Assembly.Location;
219+
string pdb = Path.Combine(Path.GetDirectoryName(module), Path.GetFileNameWithoutExtension(module) + ".pdb");
220+
221+
DirectoryInfo directory = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()));
222+
223+
File.Copy(module, Path.Combine(directory.FullName, Path.GetFileName(module)), true);
224+
File.Copy(pdb, Path.Combine(directory.FullName, Path.GetFileName(pdb)), true);
225+
226+
var mockInstrumentationHelper = new Mock<IInstrumentationHelper>();
227+
mockInstrumentationHelper
228+
.Setup(x => x.SelectModules(It.IsAny<IEnumerable<string>>(), It.IsAny<string[]>(), It.IsAny<string[]>()))
229+
.Returns(new List<string>(){"ModuleX"});
230+
231+
var parameters = new CoverageParameters
232+
{
233+
IncludeFilters = new string[] { "[coverlet.tests.projectsample.excludedbyattribute*]*" },
234+
IncludeDirectories = Array.Empty<string>(),
235+
ExcludeFilters = Array.Empty<string>(),
236+
ExcludedSourceFiles = Array.Empty<string>(),
237+
ExcludeAttributes = Array.Empty<string>(),
238+
IncludeTestAssembly = false,
239+
SingleHit = false,
240+
MergeWith = string.Empty,
241+
UseSourceLink = false
242+
};
243+
244+
var coverage = new Coverage(Path.Combine(directory.FullName, Path.GetFileName(module)), parameters, _mockLogger.Object, mockInstrumentationHelper.Object, new FileSystem(), new SourceRootTranslator(_mockLogger.Object, new FileSystem()), new CecilSymbolHelper());
245+
coverage.PrepareModules();
246+
coverage.UnloadModules();
247+
248+
mockInstrumentationHelper.Verify(i => i.RestoreOriginalModule(It.Is<string>(v => v.Equals("ModuleX")), It.IsAny<string>()), Times.Once);
249+
_mockLogger.Verify(l => l.LogVerbose(It.Is<string>(v => v.Equals("All Modules unloaded."))), Times.Once);
250+
}
180251
}
181252
}
182253

0 commit comments

Comments
 (0)