diff --git a/src/coverlet.console/Program.cs b/src/coverlet.console/Program.cs index e33746c01..580838a56 100644 --- a/src/coverlet.console/Program.cs +++ b/src/coverlet.console/Program.cs @@ -38,6 +38,7 @@ static int Main(string[] args) CommandOption excludedSourceFiles = app.Option("--exclude-by-file", "Glob patterns specifying source files to exclude.", CommandOptionType.MultipleValue); CommandOption includeDirectories = app.Option("--include-directory", "Include directories containing additional assemblies to be instrumented.", CommandOptionType.MultipleValue); CommandOption excludeAttributes = app.Option("--exclude-by-attribute", "Attributes to exclude from code coverage.", CommandOptionType.MultipleValue); + CommandOption includeTestAssembly = app.Option("--include-test-assembly", "Specifies whether to report code coverage of the test assembly", CommandOptionType.NoValue); CommandOption singleHit = app.Option("--single-hit", "Specifies whether to limit code coverage hit reporting to a single hit for each location", CommandOptionType.NoValue); CommandOption mergeWith = app.Option("--merge-with", "Path to existing coverage result to merge.", CommandOptionType.SingleValue); CommandOption useSourceLink = app.Option("--use-source-link", "Specifies whether to use SourceLink URIs in place of file system paths.", CommandOptionType.NoValue); @@ -50,7 +51,17 @@ static int Main(string[] args) if (!target.HasValue()) throw new CommandParsingException(app, "Target must be specified."); - Coverage coverage = new Coverage(module.Value, includeFilters.Values.ToArray(), includeDirectories.Values.ToArray(), excludeFilters.Values.ToArray(), excludedSourceFiles.Values.ToArray(), excludeAttributes.Values.ToArray(), singleHit.HasValue(), mergeWith.Value(), useSourceLink.HasValue(), logger); + Coverage coverage = new Coverage(module.Value, + includeFilters.Values.ToArray(), + includeDirectories.Values.ToArray(), + excludeFilters.Values.ToArray(), + excludedSourceFiles.Values.ToArray(), + excludeAttributes.Values.ToArray(), + includeTestAssembly.HasValue(), + singleHit.HasValue(), + mergeWith.Value(), + useSourceLink.HasValue(), + logger); coverage.PrepareModules(); Process process = new Process(); diff --git a/src/coverlet.core/Coverage.cs b/src/coverlet.core/Coverage.cs index 2c701f14f..2b1e52ec7 100644 --- a/src/coverlet.core/Coverage.cs +++ b/src/coverlet.core/Coverage.cs @@ -21,6 +21,7 @@ public class Coverage private string[] _excludeFilters; private string[] _excludedSourceFiles; private string[] _excludeAttributes; + private bool _includeTestAssembly; private bool _singleHit; private string _mergeWith; private bool _useSourceLink; @@ -38,6 +39,7 @@ public Coverage(string module, string[] excludeFilters, string[] excludedSourceFiles, string[] excludeAttributes, + bool includeTestAssembly, bool singleHit, string mergeWith, bool useSourceLink, @@ -49,6 +51,7 @@ public Coverage(string module, _excludeFilters = excludeFilters; _excludedSourceFiles = excludedSourceFiles; _excludeAttributes = excludeAttributes; + _includeTestAssembly = includeTestAssembly; _singleHit = singleHit; _mergeWith = mergeWith; _useSourceLink = useSourceLink; @@ -60,7 +63,7 @@ public Coverage(string module, public void PrepareModules() { - string[] modules = InstrumentationHelper.GetCoverableModules(_module, _includeDirectories); + string[] modules = InstrumentationHelper.GetCoverableModules(_module, _includeDirectories, _includeTestAssembly); string[] excludes = InstrumentationHelper.GetExcludedFiles(_excludedSourceFiles); Array.ForEach(_excludeFilters ?? Array.Empty(), filter => _logger.LogInformation($"Excluded module filter '{filter}'")); diff --git a/src/coverlet.core/Helpers/InstrumentationHelper.cs b/src/coverlet.core/Helpers/InstrumentationHelper.cs index f06bbf756..79d89d4c0 100644 --- a/src/coverlet.core/Helpers/InstrumentationHelper.cs +++ b/src/coverlet.core/Helpers/InstrumentationHelper.cs @@ -14,7 +14,7 @@ namespace Coverlet.Core.Helpers { internal static class InstrumentationHelper { - public static string[] GetCoverableModules(string module, string[] directories) + public static string[] GetCoverableModules(string module, string[] directories, bool includeTestAssembly) { Debug.Assert(directories != null); @@ -50,6 +50,9 @@ public static string[] GetCoverableModules(string module, string[] directories) // The module's name must be unique. var uniqueModules = new HashSet(); + if (!includeTestAssembly) + uniqueModules.Add(Path.GetFileName(module)); + return dirs.SelectMany(d => Directory.EnumerateFiles(d)) .Where(m => IsAssembly(m) && uniqueModules.Add(Path.GetFileName(m))) .ToArray(); diff --git a/src/coverlet.msbuild.tasks/InstrumentationTask.cs b/src/coverlet.msbuild.tasks/InstrumentationTask.cs index 7392eed24..ebe7903d8 100644 --- a/src/coverlet.msbuild.tasks/InstrumentationTask.cs +++ b/src/coverlet.msbuild.tasks/InstrumentationTask.cs @@ -14,6 +14,7 @@ public class InstrumentationTask : Task private string _exclude; private string _excludeByFile; private string _excludeByAttribute; + private bool _includeTestAssembly; private bool _singleHit; private string _mergeWith; private bool _useSourceLink; @@ -61,6 +62,12 @@ public string ExcludeByAttribute set { _excludeByAttribute = value; } } + public bool IncludeTestAssembly + { + get { return _includeTestAssembly; } + set { _includeTestAssembly = value; } + } + public bool SingleHit { get { return _singleHit; } @@ -94,7 +101,7 @@ public override bool Execute() var excludedSourceFiles = _excludeByFile?.Split(','); var excludeAttributes = _excludeByAttribute?.Split(','); - _coverage = new Coverage(_path, includeFilters, includeDirectories, excludeFilters, excludedSourceFiles, excludeAttributes, _singleHit, _mergeWith, _useSourceLink, _logger); + _coverage = new Coverage(_path, includeFilters, includeDirectories, excludeFilters, excludedSourceFiles, excludeAttributes, _includeTestAssembly, _singleHit, _mergeWith, _useSourceLink, _logger); _coverage.PrepareModules(); } catch (Exception ex) diff --git a/src/coverlet.msbuild.tasks/coverlet.msbuild.props b/src/coverlet.msbuild.tasks/coverlet.msbuild.props index 75ad624f3..c9d21109b 100644 --- a/src/coverlet.msbuild.tasks/coverlet.msbuild.props +++ b/src/coverlet.msbuild.tasks/coverlet.msbuild.props @@ -6,6 +6,7 @@ + false false false diff --git a/src/coverlet.msbuild.tasks/coverlet.msbuild.targets b/src/coverlet.msbuild.tasks/coverlet.msbuild.targets index ce241c331..1b1f4f73c 100644 --- a/src/coverlet.msbuild.tasks/coverlet.msbuild.targets +++ b/src/coverlet.msbuild.tasks/coverlet.msbuild.targets @@ -12,6 +12,7 @@ Exclude="$(Exclude)" ExcludeByFile="$(ExcludeByFile)" ExcludeByAttribute="$(ExcludeByAttribute)" + IncludeTestAssembly="$(IncludeTestAssembly)" SingleHit="$(SingleHit)" MergeWith="$(MergeWith)" UseSourceLink="$(UseSourceLink)" /> @@ -26,6 +27,7 @@ Exclude="$(Exclude)" ExcludeByFile="$(ExcludeByFile)" ExcludeByAttribute="$(ExcludeByAttribute)" + IncludeTestAssembly="$(IncludeTestAssembly)" SingleHit="$(SingleHit)" MergeWith="$(MergeWith)" UseSourceLink="$(UseSourceLink)" /> diff --git a/test/coverlet.core.tests/CoverageTests.cs b/test/coverlet.core.tests/CoverageTests.cs index 1af091193..004d8af4c 100644 --- a/test/coverlet.core.tests/CoverageTests.cs +++ b/test/coverlet.core.tests/CoverageTests.cs @@ -22,7 +22,30 @@ public void TestCoverage() // TODO: Find a way to mimick hits - var coverage = new Coverage(Path.Combine(directory.FullName, Path.GetFileName(module)), Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty(), false, string.Empty, false, new Mock().Object); + var coverage = new Coverage(Path.Combine(directory.FullName, Path.GetFileName(module)), Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty(), false, false, string.Empty, false, new Mock().Object); + coverage.PrepareModules(); + + var result = coverage.GetCoverageResult(); + + Assert.Empty(result.Modules); + + directory.Delete(true); + } + + [Fact] + public void TestCoverageWithTestAssembly() + { + string module = GetType().Assembly.Location; + string pdb = Path.Combine(Path.GetDirectoryName(module), Path.GetFileNameWithoutExtension(module) + ".pdb"); + + var directory = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString())); + + File.Copy(module, Path.Combine(directory.FullName, Path.GetFileName(module)), true); + File.Copy(pdb, Path.Combine(directory.FullName, Path.GetFileName(pdb)), true); + + // TODO: Find a way to mimick hits + + var coverage = new Coverage(Path.Combine(directory.FullName, Path.GetFileName(module)), Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty(), true, false, string.Empty, false, new Mock().Object); coverage.PrepareModules(); var result = coverage.GetCoverageResult(); diff --git a/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs b/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs index 3384839f4..b590e42de 100644 --- a/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs +++ b/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs @@ -12,7 +12,15 @@ public class InstrumentationHelperTests public void TestGetDependencies() { string module = typeof(InstrumentationHelperTests).Assembly.Location; - var modules = InstrumentationHelper.GetCoverableModules(module, Array.Empty()); + var modules = InstrumentationHelper.GetCoverableModules(module, Array.Empty(), false); + Assert.False(Array.Exists(modules, m => m == module)); + } + + [Fact] + public void TestGetDependenciesWithTestAssembly() + { + string module = typeof(InstrumentationHelperTests).Assembly.Location; + var modules = InstrumentationHelper.GetCoverableModules(module, Array.Empty(), true); Assert.True(Array.Exists(modules, m => m == module)); } @@ -235,16 +243,16 @@ public void TestIncludeDirectories() string module = typeof(InstrumentationHelperTests).Assembly.Location; var currentDirModules = InstrumentationHelper.GetCoverableModules(module, - new[] { Environment.CurrentDirectory }); + new[] { Environment.CurrentDirectory }, false); var parentDirWildcardModules = InstrumentationHelper.GetCoverableModules(module, - new[] { Path.Combine(Directory.GetParent(Environment.CurrentDirectory).FullName, "*") }); + new[] { Path.Combine(Directory.GetParent(Environment.CurrentDirectory).FullName, "*") }, false); // There are at least as many modules found when searching the parent directory's subdirectories Assert.True(parentDirWildcardModules.Length >= currentDirModules.Length); var relativePathModules = InstrumentationHelper.GetCoverableModules(module, - new[] { "." }); + new[] { "." }, false); // Same number of modules found when using a relative path Assert.Equal(currentDirModules.Length, relativePathModules.Length);