Skip to content

Commit ca9fed5

Browse files
Joseph SunJosephSun2003
authored andcommitted
Revert "Revert (#3)"
This reverts commit d9621e2.
1 parent 3a353cf commit ca9fed5

File tree

3 files changed

+133
-5
lines changed

3 files changed

+133
-5
lines changed

src/coverlet.console/Program.cs

Lines changed: 14 additions & 3 deletions
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 behavior 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();

src/coverlet.core/Coverage.cs

Lines changed: 47 additions & 1 deletion
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.
@@ -324,6 +324,52 @@ public CoverageResult GetCoverageResult()
324324
return coverageResult;
325325
}
326326

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

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

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,10 +219,81 @@ public void GetSourceLinkUrl_ReturnsOriginalDocument_WhenNoMatch()
219219
// Assert
220220
Assert.Equal("other/coverlet.core/Coverage.cs", result);
221221
}
222+
223+
[Fact]
224+
public void TestCoverageUnloadWithParameters()
225+
{
226+
string module = GetType().Assembly.Location;
227+
string pdb = Path.Combine(Path.GetDirectoryName(module), Path.GetFileNameWithoutExtension(module) + ".pdb");
228+
229+
DirectoryInfo directory = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()));
230+
231+
File.Copy(module, Path.Combine(directory.FullName, Path.GetFileName(module)), true);
232+
File.Copy(pdb, Path.Combine(directory.FullName, Path.GetFileName(pdb)), true);
233+
234+
var mockInstrumentationHelper = new Mock<IInstrumentationHelper>();
235+
236+
var parameters = new CoverageParameters
237+
{
238+
IncludeFilters = new string[] { "[coverlet.tests.projectsample.excludedbyattribute*]*" },
239+
IncludeDirectories = Array.Empty<string>(),
240+
ExcludeFilters = Array.Empty<string>(),
241+
ExcludedSourceFiles = Array.Empty<string>(),
242+
ExcludeAttributes = Array.Empty<string>(),
243+
IncludeTestAssembly = false,
244+
SingleHit = false,
245+
MergeWith = string.Empty,
246+
UseSourceLink = false
247+
};
248+
249+
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());
250+
coverage.PrepareModules();
251+
coverage.UnloadModule(Path.Combine(directory.FullName, Path.GetFileName(module)));
252+
253+
mockInstrumentationHelper.Verify(i => i.RestoreOriginalModule(It.Is<string>(v => v.Equals(Path.Combine(directory.FullName, Path.GetFileName(module)))), It.IsAny<string>()), Times.Once);
254+
_mockLogger.Verify(l => l.LogVerbose(It.Is<string>(v => v.Equals($"Module at {Path.Combine(directory.FullName, Path.GetFileName(module))} is unloaded."))), Times.Once);
255+
}
256+
257+
[Fact]
258+
public void TestCoverageUnloadWithNoParameters()
259+
{
260+
string module = GetType().Assembly.Location;
261+
string pdb = Path.Combine(Path.GetDirectoryName(module), Path.GetFileNameWithoutExtension(module) + ".pdb");
262+
263+
DirectoryInfo directory = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()));
264+
265+
File.Copy(module, Path.Combine(directory.FullName, Path.GetFileName(module)), true);
266+
File.Copy(pdb, Path.Combine(directory.FullName, Path.GetFileName(pdb)), true);
267+
268+
var mockInstrumentationHelper = new Mock<IInstrumentationHelper>();
269+
mockInstrumentationHelper
270+
.Setup(x => x.SelectModules(It.IsAny<IEnumerable<string>>(), It.IsAny<string[]>(), It.IsAny<string[]>()))
271+
.Returns(new List<string>(){"ModuleX"});
272+
273+
var parameters = new CoverageParameters
274+
{
275+
IncludeFilters = new string[] { "[coverlet.tests.projectsample.excludedbyattribute*]*" },
276+
IncludeDirectories = Array.Empty<string>(),
277+
ExcludeFilters = Array.Empty<string>(),
278+
ExcludedSourceFiles = Array.Empty<string>(),
279+
ExcludeAttributes = Array.Empty<string>(),
280+
IncludeTestAssembly = false,
281+
SingleHit = false,
282+
MergeWith = string.Empty,
283+
UseSourceLink = false
284+
};
285+
286+
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());
287+
coverage.PrepareModules();
288+
coverage.UnloadModules();
289+
290+
mockInstrumentationHelper.Verify(i => i.RestoreOriginalModule(It.Is<string>(v => v.Equals("ModuleX")), It.IsAny<string>()), Times.Once);
291+
_mockLogger.Verify(l => l.LogVerbose(It.Is<string>(v => v.Equals("All Modules unloaded."))), Times.Once);
292+
}
222293
}
223294
}
224295

225-
public class BranchDictionaryConverter : JsonConverter
296+
public class BranchDictionaryConverter: JsonConverter
226297
{
227298
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
228299
{

0 commit comments

Comments
 (0)