From 3b832a0e15f380e39efc057b6f4a1eb9f2c6c087 Mon Sep 17 00:00:00 2001
From: Jeremy Herbison <jeremy.herbison@gmail.com>
Date: Fri, 12 Oct 2018 15:31:06 -0400
Subject: [PATCH 1/9] Added an "IncludeDirectories" param so that assemblies
 that are not project references can also be instrumented.

---
 src/coverlet.console/Program.cs               |  3 +-
 src/coverlet.core/Coverage.cs                 | 53 +++++++++---------
 src/coverlet.core/CoverageResult.cs           | 20 +++----
 .../Helpers/InstrumentationHelper.cs          | 55 +++++++++++++++++--
 .../InstrumentationTask.cs                    | 10 +++-
 src/coverlet.msbuild/coverlet.msbuild.props   |  1 +
 src/coverlet.msbuild/coverlet.msbuild.targets |  2 +
 test/coverlet.core.tests/CoverageTests.cs     |  2 +-
 .../Helpers/InstrumentationHelperTests.cs     | 38 ++++++++++---
 9 files changed, 133 insertions(+), 51 deletions(-)

diff --git a/src/coverlet.console/Program.cs b/src/coverlet.console/Program.cs
index 89bc94ce9..5d7e837bd 100644
--- a/src/coverlet.console/Program.cs
+++ b/src/coverlet.console/Program.cs
@@ -32,6 +32,7 @@ static int Main(string[] args)
             CommandOption thresholdTypes = app.Option("--threshold-type", "Coverage type to apply the threshold to.", CommandOptionType.MultipleValue);
             CommandOption excludeFilters = app.Option("--exclude", "Filter expressions to exclude specific modules and types.", CommandOptionType.MultipleValue);
             CommandOption includeFilters = app.Option("--include", "Filter expressions to include only specific modules and types.", CommandOptionType.MultipleValue);
+            CommandOption includeDirectories = app.Option("--include-directories", "Include directories containing additional assemblies to be instrumented.", CommandOptionType.MultipleValue);
             CommandOption excludedSourceFiles = app.Option("--exclude-by-file", "Glob patterns specifying source files to exclude.", CommandOptionType.MultipleValue);
             CommandOption mergeWith = app.Option("--merge-with", "Path to existing coverage result to merge.", CommandOptionType.SingleValue);
 
@@ -43,7 +44,7 @@ static int Main(string[] args)
                 if (!target.HasValue())
                     throw new CommandParsingException(app, "Target must be specified.");
 
-                Coverage coverage = new Coverage(module.Value, excludeFilters.Values.ToArray(), includeFilters.Values.ToArray(), excludedSourceFiles.Values.ToArray(), mergeWith.Value());
+                Coverage coverage = new Coverage(module.Value, excludeFilters.Values.ToArray(), includeFilters.Values.ToArray(), includeDirectories.Values.ToArray(), excludedSourceFiles.Values.ToArray(), mergeWith.Value());
                 coverage.PrepareModules();
 
                 Process process = new Process();
diff --git a/src/coverlet.core/Coverage.cs b/src/coverlet.core/Coverage.cs
index 00d00e1e3..b440a78f2 100644
--- a/src/coverlet.core/Coverage.cs
+++ b/src/coverlet.core/Coverage.cs
@@ -17,6 +17,7 @@ public class Coverage
         private string _identifier;
         private string[] _excludeFilters;
         private string[] _includeFilters;
+        private string[] _includeDirectories;
         private string[] _excludedSourceFiles;
         private string _mergeWith;
         private List<InstrumenterResult> _results;
@@ -26,11 +27,13 @@ public string Identifier
             get { return _identifier; }
         }
 
-        public Coverage(string module, string[] excludeFilters, string[] includeFilters, string[] excludedSourceFiles, string mergeWith)
+        public Coverage(string module, string[] excludeFilters, string[] includeFilters, string[] includeDirectories, string[] excludedSourceFiles, string mergeWith)
         {
+            Console.WriteLine(module);
             _module = module;
             _excludeFilters = excludeFilters;
             _includeFilters = includeFilters;
+            _includeDirectories = includeDirectories;
             _excludedSourceFiles = excludedSourceFiles;
             _mergeWith = mergeWith;
 
@@ -40,7 +43,7 @@ public Coverage(string module, string[] excludeFilters, string[] includeFilters,
 
         public void PrepareModules()
         {
-            string[] modules = InstrumentationHelper.GetCoverableModules(_module);
+            string[] modules = InstrumentationHelper.GetCoverableModules(_module, _includeDirectories);
             string[] excludes =  InstrumentationHelper.GetExcludedFiles(_excludedSourceFiles);
             _excludeFilters = _excludeFilters?.Where(f => InstrumentationHelper.IsValidFilterExpression(f)).ToArray();
             _includeFilters = _includeFilters?.Where(f => InstrumentationHelper.IsValidFilterExpression(f)).ToArray();
@@ -205,29 +208,29 @@ private void CalculateCoverage()
                     }
                 }
 
-                // for MoveNext() compiler autogenerated method we need to patch false positive (IAsyncStateMachine for instance) 
-                // we'll remove all MoveNext() not covered branch              
-                foreach (var document in result.Documents)
-                {
-                    List<KeyValuePair<(int, int), Branch>> branchesToRemove = new List<KeyValuePair<(int, int), Branch>>();
-                    foreach (var branch in document.Value.Branches)
-                    {
-                        //if one branch is covered we search the other one only if it's not covered
-                        if (CecilSymbolHelper.IsMoveNext(branch.Value.Method) && branch.Value.Hits > 0)
-                        {
-                            foreach (var moveNextBranch in document.Value.Branches)
-                            {
-                                if (moveNextBranch.Value.Method == branch.Value.Method && moveNextBranch.Value != branch.Value && moveNextBranch.Value.Hits == 0)
-                                {
-                                    branchesToRemove.Add(moveNextBranch);
-                                }
-                            }
-                        }
-                    }
-                    foreach (var branchToRemove in branchesToRemove)
-                    {
-                        document.Value.Branches.Remove(branchToRemove.Key);
-                    }
+                // for MoveNext() compiler autogenerated method we need to patch false positive (IAsyncStateMachine for instance) 
+                // we'll remove all MoveNext() not covered branch              
+                foreach (var document in result.Documents)
+                {
+                    List<KeyValuePair<(int, int), Branch>> branchesToRemove = new List<KeyValuePair<(int, int), Branch>>();
+                    foreach (var branch in document.Value.Branches)
+                    {
+                        //if one branch is covered we search the other one only if it's not covered
+                        if (CecilSymbolHelper.IsMoveNext(branch.Value.Method) && branch.Value.Hits > 0)
+                        {
+                            foreach (var moveNextBranch in document.Value.Branches)
+                            {
+                                if (moveNextBranch.Value.Method == branch.Value.Method && moveNextBranch.Value != branch.Value && moveNextBranch.Value.Hits == 0)
+                                {
+                                    branchesToRemove.Add(moveNextBranch);
+                                }
+                            }
+                        }
+                    }
+                    foreach (var branchToRemove in branchesToRemove)
+                    {
+                        document.Value.Branches.Remove(branchToRemove.Key);
+                    }
                 }
 
                 InstrumentationHelper.DeleteHitsFile(result.HitsFilePath);
diff --git a/src/coverlet.core/CoverageResult.cs b/src/coverlet.core/CoverageResult.cs
index ed17451a7..25ca1605c 100644
--- a/src/coverlet.core/CoverageResult.cs
+++ b/src/coverlet.core/CoverageResult.cs
@@ -53,43 +53,43 @@ internal void Merge(Modules modules)
                 {
                     foreach (var document in module.Value)
                     {
-                        if (!this.Modules[module.Key].ContainsKey(document.Key))
+                        if (!this.Modules[existingKey].ContainsKey(document.Key))
                         {
-                            this.Modules[module.Key].Add(document.Key, document.Value);
+                            this.Modules[existingKey].Add(document.Key, document.Value);
                         }
                         else
                         {
                             foreach (var @class in document.Value)
                             {
-                                if (!this.Modules[module.Key][document.Key].ContainsKey(@class.Key))
+                                if (!this.Modules[existingKey][document.Key].ContainsKey(@class.Key))
                                 {
-                                    this.Modules[module.Key][document.Key].Add(@class.Key, @class.Value);
+                                    this.Modules[existingKey][document.Key].Add(@class.Key, @class.Value);
                                 }
                                 else
                                 {
                                     foreach (var method in @class.Value)
                                     {
-                                        if (!this.Modules[module.Key][document.Key][@class.Key].ContainsKey(method.Key))
+                                        if (!this.Modules[existingKey][document.Key][@class.Key].ContainsKey(method.Key))
                                         {
-                                            this.Modules[module.Key][document.Key][@class.Key].Add(method.Key, method.Value);
+                                            this.Modules[existingKey][document.Key][@class.Key].Add(method.Key, method.Value);
                                         }
                                         else
                                         {
                                             foreach (var line in method.Value.Lines)
                                             {
-                                                if (!this.Modules[module.Key][document.Key][@class.Key][method.Key].Lines.ContainsKey(line.Key))
+                                                if (!this.Modules[existingKey][document.Key][@class.Key][method.Key].Lines.ContainsKey(line.Key))
                                                 {
-                                                    this.Modules[module.Key][document.Key][@class.Key][method.Key].Lines.Add(line.Key, line.Value);
+                                                    this.Modules[existingKey][document.Key][@class.Key][method.Key].Lines.Add(line.Key, line.Value);
                                                 }
                                                 else
                                                 {
-                                                    this.Modules[module.Key][document.Key][@class.Key][method.Key].Lines[line.Key] += line.Value;
+                                                    this.Modules[existingKey][document.Key][@class.Key][method.Key].Lines[line.Key] += line.Value;
                                                 }
                                             }
 
                                             foreach (var branch in method.Value.Branches)
                                             {
-                                                var branches = this.Modules[module.Key][document.Key][@class.Key][method.Key].Branches;
+                                                var branches = this.Modules[existingKey][document.Key][@class.Key][method.Key].Branches;
                                                 var branchInfo = branches.FirstOrDefault(b => b.EndOffset == branch.EndOffset && b.Line == branch.Line && b.Offset == branch.Offset && b.Ordinal == branch.Ordinal && b.Path == branch.Path);
                                                 if (branchInfo == null)
                                                     branches.Add(branch);
diff --git a/src/coverlet.core/Helpers/InstrumentationHelper.cs b/src/coverlet.core/Helpers/InstrumentationHelper.cs
index ea1fc19e1..3a25968f6 100644
--- a/src/coverlet.core/Helpers/InstrumentationHelper.cs
+++ b/src/coverlet.core/Helpers/InstrumentationHelper.cs
@@ -1,10 +1,14 @@
 using System;
+using System.Collections;
 using System.Collections.Generic;
 using System.Diagnostics;
+using System.Globalization;
 using System.IO;
 using System.Linq;
 using System.Reflection;
 using System.Reflection.PortableExecutable;
+using System.Security.Cryptography;
+using System.Text;
 using System.Text.RegularExpressions;
 
 using Microsoft.Extensions.FileSystemGlobbing;
@@ -14,11 +18,17 @@ namespace Coverlet.Core.Helpers
 {
     internal static class InstrumentationHelper
     {
-        public static string[] GetCoverableModules(string module)
+        public static string[] GetCoverableModules(string module, string[] includeDirectories)
         {
-            IEnumerable<string> modules = Directory.EnumerateFiles(Path.GetDirectoryName(module)).Where(f => f.EndsWith(".exe") || f.EndsWith(".dll"));
-            modules = modules.Where(m => IsAssembly(m) && Path.GetFileName(m) != Path.GetFileName(module));
-            return modules.ToArray();
+            var moduleDirectory = Path.GetDirectoryName(module);
+            var files = new List<string>(Directory.GetFiles(moduleDirectory));
+
+            if (includeDirectories != null)
+                foreach (var includeDirectory in ExpandIncludeDirectories(includeDirectories, moduleDirectory))
+                    files.AddRange(Directory.GetFiles(includeDirectory));
+
+            return files.Where(m => IsAssembly(m) && Path.GetFileName(m) != Path.GetFileName(module))
+                .Distinct().ToArray();
         }
 
         public static bool HasPdb(string module)
@@ -40,10 +50,11 @@ public static bool HasPdb(string module)
             }
         }
 
-        public static void BackupOriginalModule(string module, string identifier)
+        public static string BackupOriginalModule(string module, string identifier)
         {
             var backupPath = GetBackupPath(module, identifier);
             File.Copy(module, backupPath);
+            return backupPath;
         }
 
         public static void RestoreOriginalModule(string module, string identifier)
@@ -243,11 +254,33 @@ private static bool IsTypeFilterMatch(string module, string type, string[] filte
             return false;
         }
 
+        private static IEnumerable<string> ExpandIncludeDirectories(string[] includeDirectories, string moduleDirectory)
+        {
+            var result = new List<string>(includeDirectories.Length);
+
+            foreach (var includeDirectory in includeDirectories.Where(d => d != null))
+            {
+                var fullPath = (!Path.IsPathRooted(includeDirectory)
+                    ? Path.GetFullPath(Path.Combine(moduleDirectory, includeDirectory))
+                    : includeDirectory).TrimEnd('*');
+
+                if (!Directory.Exists(fullPath))
+                    throw new DirectoryNotFoundException($"The included directory '{fullPath}' does not exist.");
+
+                if (includeDirectory.EndsWith("*", StringComparison.Ordinal))
+                    result.AddRange(Directory.GetDirectories(fullPath));
+                else
+                    result.Add(fullPath);
+            }
+
+            return result;
+        }
+
         private static string GetBackupPath(string module, string identifier)
         {
             return Path.Combine(
                 Path.GetTempPath(),
-                Path.GetFileNameWithoutExtension(module) + "_" + identifier + ".dll"
+                Path.GetFileNameWithoutExtension(module) + "_" + GetPathHash(Path.GetDirectoryName(module)) + "_" + identifier + ".dll"
             );
         }
 
@@ -274,6 +307,9 @@ private static bool IsAssembly(string filePath)
         {
             try
             {
+                if (!(filePath.EndsWith(".exe") || filePath.EndsWith(".dll")))
+                    return false;
+
                 AssemblyName.GetAssemblyName(filePath);
                 return true;
             }
@@ -282,6 +318,13 @@ private static bool IsAssembly(string filePath)
                 return false;
             }
         }
+
+        private static string GetPathHash(string path)
+        {
+            using (var md5 = MD5.Create())
+                return BitConverter.ToString(md5.ComputeHash(Encoding.Unicode.GetBytes(path)))
+                    .Replace("-", string.Empty);
+        }
     }
 }
 
diff --git a/src/coverlet.msbuild.tasks/InstrumentationTask.cs b/src/coverlet.msbuild.tasks/InstrumentationTask.cs
index e53c87666..400f7f1d6 100644
--- a/src/coverlet.msbuild.tasks/InstrumentationTask.cs
+++ b/src/coverlet.msbuild.tasks/InstrumentationTask.cs
@@ -11,6 +11,7 @@ public class InstrumentationTask : Task
         private string _path;
         private string _exclude;
         private string _include;
+        private string _includeDirectories;
         private string _excludeByFile;
         private string _mergeWith;
 
@@ -38,6 +39,12 @@ public string Include
             set { _include = value; }
         }
 
+        public string IncludeDirectories
+        {
+            get { return _includeDirectories; }
+            set { _includeDirectories = value; }
+        }
+
         public string ExcludeByFile
         {
             get { return _excludeByFile; }
@@ -57,8 +64,9 @@ public override bool Execute()
                 var excludedSourceFiles = _excludeByFile?.Split(',');
                 var excludeFilters = _exclude?.Split(',');
                 var includeFilters = _include?.Split(',');
+                var includeDirectories = _includeDirectories?.Split(',');
 
-                _coverage = new Coverage(_path, excludeFilters, includeFilters, excludedSourceFiles, _mergeWith);
+                _coverage = new Coverage(_path, excludeFilters, includeFilters, includeDirectories, excludedSourceFiles, _mergeWith);
                 _coverage.PrepareModules();
             }
             catch (Exception ex)
diff --git a/src/coverlet.msbuild/coverlet.msbuild.props b/src/coverlet.msbuild/coverlet.msbuild.props
index d776044e9..d4beab8e3 100644
--- a/src/coverlet.msbuild/coverlet.msbuild.props
+++ b/src/coverlet.msbuild/coverlet.msbuild.props
@@ -9,5 +9,6 @@
     <MergeWith Condition="$(MergeWith) == ''"></MergeWith>
     <Threshold Condition="$(Threshold) == ''">0</Threshold>
     <ThresholdType Condition="$(ThresholdType) == ''">line,branch,method</ThresholdType>
+    <IncludeDirectories Condition="$(IncludeDirectories) == ''"></IncludeDirectories>
   </PropertyGroup>
 </Project>
diff --git a/src/coverlet.msbuild/coverlet.msbuild.targets b/src/coverlet.msbuild/coverlet.msbuild.targets
index b8f40a549..ec8807f9f 100644
--- a/src/coverlet.msbuild/coverlet.msbuild.targets
+++ b/src/coverlet.msbuild/coverlet.msbuild.targets
@@ -7,6 +7,7 @@
     <Coverlet.MSbuild.Tasks.InstrumentationTask
       Condition="'$(VSTestNoBuild)' == 'true' and $(CollectCoverage) == 'true'"
       Include="$(Include)"
+      IncludeDirectories="$(IncludeDirectories)"
       Exclude="$(Exclude)"
       ExcludeByFile="$(ExcludeByFile)"
       MergeWith="$(MergeWith)"
@@ -17,6 +18,7 @@
     <Coverlet.MSbuild.Tasks.InstrumentationTask
       Condition="'$(VSTestNoBuild)' != 'true' and $(CollectCoverage) == 'true'"
       Include="$(Include)"
+      IncludeDirectories="$(IncludeDirectories)"
       Exclude="$(Exclude)"
       ExcludeByFile="$(ExcludeByFile)"
       MergeWith="$(MergeWith)"
diff --git a/test/coverlet.core.tests/CoverageTests.cs b/test/coverlet.core.tests/CoverageTests.cs
index fc9d26f60..2732e656e 100644
--- a/test/coverlet.core.tests/CoverageTests.cs
+++ b/test/coverlet.core.tests/CoverageTests.cs
@@ -27,7 +27,7 @@ public void TestCoverage()
             // Since Coverage only instruments dependancies, we need a fake module here
             var testModule = Path.Combine(directory.FullName, "test.module.dll");
 
-            var coverage = new Coverage(testModule, Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), string.Empty);
+            var coverage = new Coverage(testModule, Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), string.Empty);
             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 44e1021c8..51ec77362 100644
--- a/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs
+++ b/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs
@@ -12,7 +12,7 @@ public class InstrumentationHelperTests
         public void TestGetDependencies()
         {
             string module = typeof(InstrumentationHelperTests).Assembly.Location;
-            var modules = InstrumentationHelper.GetCoverableModules(module);
+            var modules = InstrumentationHelper.GetCoverableModules(module, null);
             Assert.False(Array.Exists(modules, m => m == module));
         }
 
@@ -28,12 +28,7 @@ public void TestBackupOriginalModule()
             string module = typeof(InstrumentationHelperTests).Assembly.Location;
             string identifier = Guid.NewGuid().ToString();
 
-            InstrumentationHelper.BackupOriginalModule(module, identifier);
-
-            var backupPath = Path.Combine(
-                Path.GetTempPath(),
-                Path.GetFileNameWithoutExtension(module) + "_" + identifier + ".dll"
-            );
+            var backupPath = InstrumentationHelper.BackupOriginalModule(module, identifier);
 
             Assert.True(File.Exists(backupPath));
         }
@@ -230,6 +225,35 @@ public void TestIsTypeExcludedAndIncludedWithMatchingAndMismatchingFilter(string
             Assert.True(result);
         }
 
+        [Fact]
+        public void TestIncludeDirectoryInvalidThrowsDirectoryNotFoundException()
+        {
+            string module = typeof(InstrumentationHelperTests).Assembly.Location;
+            Assert.Throws<DirectoryNotFoundException>(() =>
+                InstrumentationHelper.GetCoverableModules(module, new[] {"Foobar"}));
+        }
+
+        [Fact]
+        public void TestIncludeDirectories()
+        {
+            string module = typeof(InstrumentationHelperTests).Assembly.Location;
+
+            var currentDirModules = InstrumentationHelper.GetCoverableModules(module,
+                new[] {Environment.CurrentDirectory});
+            
+            var parentDirWildcardModules = InstrumentationHelper.GetCoverableModules(module,
+                new[] {Path.Combine(Directory.GetParent(Environment.CurrentDirectory).FullName, "*")});
+
+            // 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[] {"."});
+
+            // Same number of modules found when using a relative path
+            Assert.Equal(currentDirModules.Length, relativePathModules.Length);
+        }
+
         public static IEnumerable<object[]> ValidModuleFilterData =>
             new List<object[]>
                 {

From 473cee95d2a48548823470308833f1d72bc3067c Mon Sep 17 00:00:00 2001
From: Jeremy Herbison <jherby2k@users.noreply.github.com>
Date: Fri, 12 Oct 2018 16:27:26 -0400
Subject: [PATCH 2/9] Remove WriteLine I accidentally left behind.

---
 src/coverlet.core/Coverage.cs | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/src/coverlet.core/Coverage.cs b/src/coverlet.core/Coverage.cs
index b440a78f2..24ad0a7f3 100644
--- a/src/coverlet.core/Coverage.cs
+++ b/src/coverlet.core/Coverage.cs
@@ -29,7 +29,6 @@ public string Identifier
 
         public Coverage(string module, string[] excludeFilters, string[] includeFilters, string[] includeDirectories, string[] excludedSourceFiles, string mergeWith)
         {
-            Console.WriteLine(module);
             _module = module;
             _excludeFilters = excludeFilters;
             _includeFilters = includeFilters;
@@ -153,8 +152,10 @@ public CoverageResult GetCoverageResult()
                 InstrumentationHelper.RestoreOriginalModule(result.ModulePath, _identifier);
             }
 
-            var coverageResult = new CoverageResult { Identifier = _identifier, Modules = modules };
-            if (!string.IsNullOrEmpty(_mergeWith) && !string.IsNullOrWhiteSpace(_mergeWith) && File.Exists(_mergeWith))
+            var coverageResult = new CoverageResult { Identifier = _identifier, Modules = new Modules() };
+            coverageResult.Merge(modules);
+
+            if (!string.IsNullOrEmpty(_mergeWith) && !string.IsNullOrWhiteSpace(_mergeWith))
             {
                 string json = File.ReadAllText(_mergeWith);
                 coverageResult.Merge(JsonConvert.DeserializeObject<Modules>(json));

From 6ecac9db3635fb7ba5c12f2d384710f2171c30e6 Mon Sep 17 00:00:00 2001
From: Jeremy Herbison <jeremy.herbison@gmail.com>
Date: Sun, 14 Oct 2018 15:55:21 -0400
Subject: [PATCH 3/9] Revert changes to CoverageResult, as merging the same
 file from multiple locations is already supported in the master branch.

---
 src/coverlet.core/CoverageResult.cs | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/src/coverlet.core/CoverageResult.cs b/src/coverlet.core/CoverageResult.cs
index 25ca1605c..ed17451a7 100644
--- a/src/coverlet.core/CoverageResult.cs
+++ b/src/coverlet.core/CoverageResult.cs
@@ -53,43 +53,43 @@ internal void Merge(Modules modules)
                 {
                     foreach (var document in module.Value)
                     {
-                        if (!this.Modules[existingKey].ContainsKey(document.Key))
+                        if (!this.Modules[module.Key].ContainsKey(document.Key))
                         {
-                            this.Modules[existingKey].Add(document.Key, document.Value);
+                            this.Modules[module.Key].Add(document.Key, document.Value);
                         }
                         else
                         {
                             foreach (var @class in document.Value)
                             {
-                                if (!this.Modules[existingKey][document.Key].ContainsKey(@class.Key))
+                                if (!this.Modules[module.Key][document.Key].ContainsKey(@class.Key))
                                 {
-                                    this.Modules[existingKey][document.Key].Add(@class.Key, @class.Value);
+                                    this.Modules[module.Key][document.Key].Add(@class.Key, @class.Value);
                                 }
                                 else
                                 {
                                     foreach (var method in @class.Value)
                                     {
-                                        if (!this.Modules[existingKey][document.Key][@class.Key].ContainsKey(method.Key))
+                                        if (!this.Modules[module.Key][document.Key][@class.Key].ContainsKey(method.Key))
                                         {
-                                            this.Modules[existingKey][document.Key][@class.Key].Add(method.Key, method.Value);
+                                            this.Modules[module.Key][document.Key][@class.Key].Add(method.Key, method.Value);
                                         }
                                         else
                                         {
                                             foreach (var line in method.Value.Lines)
                                             {
-                                                if (!this.Modules[existingKey][document.Key][@class.Key][method.Key].Lines.ContainsKey(line.Key))
+                                                if (!this.Modules[module.Key][document.Key][@class.Key][method.Key].Lines.ContainsKey(line.Key))
                                                 {
-                                                    this.Modules[existingKey][document.Key][@class.Key][method.Key].Lines.Add(line.Key, line.Value);
+                                                    this.Modules[module.Key][document.Key][@class.Key][method.Key].Lines.Add(line.Key, line.Value);
                                                 }
                                                 else
                                                 {
-                                                    this.Modules[existingKey][document.Key][@class.Key][method.Key].Lines[line.Key] += line.Value;
+                                                    this.Modules[module.Key][document.Key][@class.Key][method.Key].Lines[line.Key] += line.Value;
                                                 }
                                             }
 
                                             foreach (var branch in method.Value.Branches)
                                             {
-                                                var branches = this.Modules[existingKey][document.Key][@class.Key][method.Key].Branches;
+                                                var branches = this.Modules[module.Key][document.Key][@class.Key][method.Key].Branches;
                                                 var branchInfo = branches.FirstOrDefault(b => b.EndOffset == branch.EndOffset && b.Line == branch.Line && b.Offset == branch.Offset && b.Ordinal == branch.Ordinal && b.Path == branch.Path);
                                                 if (branchInfo == null)
                                                     branches.Add(branch);

From 017d277e3a9b527ed2f4b3d4c052286de8435b7e Mon Sep 17 00:00:00 2001
From: Jeremy Herbison <jeremy.herbison@gmail.com>
Date: Mon, 15 Oct 2018 22:39:50 -0400
Subject: [PATCH 4/9] Go back to full module paths as keys - it wasn't working
 in all scenarios.

---
 src/coverlet.core/Coverage.cs       |  2 +-
 src/coverlet.core/CoverageResult.cs | 25 +++++++++++++------------
 2 files changed, 14 insertions(+), 13 deletions(-)

diff --git a/src/coverlet.core/Coverage.cs b/src/coverlet.core/Coverage.cs
index 24ad0a7f3..e54cd2a98 100644
--- a/src/coverlet.core/Coverage.cs
+++ b/src/coverlet.core/Coverage.cs
@@ -148,7 +148,7 @@ public CoverageResult GetCoverageResult()
                     }
                 }
 
-                modules.Add(Path.GetFileName(result.ModulePath), documents);
+                modules.Add(result.ModulePath, documents);
                 InstrumentationHelper.RestoreOriginalModule(result.ModulePath, _identifier);
             }
 
diff --git a/src/coverlet.core/CoverageResult.cs b/src/coverlet.core/CoverageResult.cs
index ed17451a7..c78aa7dcd 100644
--- a/src/coverlet.core/CoverageResult.cs
+++ b/src/coverlet.core/CoverageResult.cs
@@ -1,4 +1,3 @@
-using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
@@ -45,7 +44,9 @@ internal void Merge(Modules modules)
         {
             foreach (var module in modules)
             {
-                if (!this.Modules.Keys.Contains(module.Key))
+                var existingKey = Modules.Keys.SingleOrDefault(key => Path.GetFileName(key) == Path.GetFileName(module.Key));
+
+                if (existingKey == null)
                 {
                     this.Modules.Add(module.Key, module.Value);
                 }
@@ -53,43 +54,43 @@ internal void Merge(Modules modules)
                 {
                     foreach (var document in module.Value)
                     {
-                        if (!this.Modules[module.Key].ContainsKey(document.Key))
+                        if (!this.Modules[existingKey].ContainsKey(document.Key))
                         {
-                            this.Modules[module.Key].Add(document.Key, document.Value);
+                            this.Modules[existingKey].Add(document.Key, document.Value);
                         }
                         else
                         {
                             foreach (var @class in document.Value)
                             {
-                                if (!this.Modules[module.Key][document.Key].ContainsKey(@class.Key))
+                                if (!this.Modules[existingKey][document.Key].ContainsKey(@class.Key))
                                 {
-                                    this.Modules[module.Key][document.Key].Add(@class.Key, @class.Value);
+                                    this.Modules[existingKey][document.Key].Add(@class.Key, @class.Value);
                                 }
                                 else
                                 {
                                     foreach (var method in @class.Value)
                                     {
-                                        if (!this.Modules[module.Key][document.Key][@class.Key].ContainsKey(method.Key))
+                                        if (!this.Modules[existingKey][document.Key][@class.Key].ContainsKey(method.Key))
                                         {
-                                            this.Modules[module.Key][document.Key][@class.Key].Add(method.Key, method.Value);
+                                            this.Modules[existingKey][document.Key][@class.Key].Add(method.Key, method.Value);
                                         }
                                         else
                                         {
                                             foreach (var line in method.Value.Lines)
                                             {
-                                                if (!this.Modules[module.Key][document.Key][@class.Key][method.Key].Lines.ContainsKey(line.Key))
+                                                if (!this.Modules[existingKey][document.Key][@class.Key][method.Key].Lines.ContainsKey(line.Key))
                                                 {
-                                                    this.Modules[module.Key][document.Key][@class.Key][method.Key].Lines.Add(line.Key, line.Value);
+                                                    this.Modules[existingKey][document.Key][@class.Key][method.Key].Lines.Add(line.Key, line.Value);
                                                 }
                                                 else
                                                 {
-                                                    this.Modules[module.Key][document.Key][@class.Key][method.Key].Lines[line.Key] += line.Value;
+                                                    this.Modules[existingKey][document.Key][@class.Key][method.Key].Lines[line.Key] += line.Value;
                                                 }
                                             }
 
                                             foreach (var branch in method.Value.Branches)
                                             {
-                                                var branches = this.Modules[module.Key][document.Key][@class.Key][method.Key].Branches;
+                                                var branches = this.Modules[existingKey][document.Key][@class.Key][method.Key].Branches;
                                                 var branchInfo = branches.FirstOrDefault(b => b.EndOffset == branch.EndOffset && b.Line == branch.Line && b.Offset == branch.Offset && b.Ordinal == branch.Ordinal && b.Path == branch.Path);
                                                 if (branchInfo == null)
                                                     branches.Add(branch);

From 0bf5227444c4c2957b3bec383bdd139ca6d9e81f Mon Sep 17 00:00:00 2001
From: Jeremy Herbison <jeremy.herbison@gmail.com>
Date: Mon, 15 Oct 2018 23:04:02 -0400
Subject: [PATCH 5/9] Skip included directories that don't exist quietly,
 rather than throwing.

---
 src/coverlet.core/Helpers/InstrumentationHelper.cs        | 3 +--
 .../Helpers/InstrumentationHelperTests.cs                 | 8 --------
 2 files changed, 1 insertion(+), 10 deletions(-)

diff --git a/src/coverlet.core/Helpers/InstrumentationHelper.cs b/src/coverlet.core/Helpers/InstrumentationHelper.cs
index 3a25968f6..5321247ea 100644
--- a/src/coverlet.core/Helpers/InstrumentationHelper.cs
+++ b/src/coverlet.core/Helpers/InstrumentationHelper.cs
@@ -264,8 +264,7 @@ private static IEnumerable<string> ExpandIncludeDirectories(string[] includeDire
                     ? Path.GetFullPath(Path.Combine(moduleDirectory, includeDirectory))
                     : includeDirectory).TrimEnd('*');
 
-                if (!Directory.Exists(fullPath))
-                    throw new DirectoryNotFoundException($"The included directory '{fullPath}' does not exist.");
+                if (!Directory.Exists(fullPath)) continue;
 
                 if (includeDirectory.EndsWith("*", StringComparison.Ordinal))
                     result.AddRange(Directory.GetDirectories(fullPath));
diff --git a/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs b/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs
index 51ec77362..c7043cf86 100644
--- a/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs
+++ b/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs
@@ -225,14 +225,6 @@ public void TestIsTypeExcludedAndIncludedWithMatchingAndMismatchingFilter(string
             Assert.True(result);
         }
 
-        [Fact]
-        public void TestIncludeDirectoryInvalidThrowsDirectoryNotFoundException()
-        {
-            string module = typeof(InstrumentationHelperTests).Assembly.Location;
-            Assert.Throws<DirectoryNotFoundException>(() =>
-                InstrumentationHelper.GetCoverableModules(module, new[] {"Foobar"}));
-        }
-
         [Fact]
         public void TestIncludeDirectories()
         {

From e6a9524b2b4683f1ec9ad322ca5e3767c506ba84 Mon Sep 17 00:00:00 2001
From: Jeremy Herbison <jeremy.herbison@gmail.com>
Date: Sun, 4 Nov 2018 00:12:21 -0400
Subject: [PATCH 6/9] Restore accidentally-removed #220.

---
 src/coverlet.core/Coverage.cs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/coverlet.core/Coverage.cs b/src/coverlet.core/Coverage.cs
index e54cd2a98..067bebc78 100644
--- a/src/coverlet.core/Coverage.cs
+++ b/src/coverlet.core/Coverage.cs
@@ -155,7 +155,7 @@ public CoverageResult GetCoverageResult()
             var coverageResult = new CoverageResult { Identifier = _identifier, Modules = new Modules() };
             coverageResult.Merge(modules);
 
-            if (!string.IsNullOrEmpty(_mergeWith) && !string.IsNullOrWhiteSpace(_mergeWith))
+            if (!string.IsNullOrEmpty(_mergeWith) && !string.IsNullOrWhiteSpace(_mergeWith) && File.Exists(_mergeWith))
             {
                 string json = File.ReadAllText(_mergeWith);
                 coverageResult.Merge(JsonConvert.DeserializeObject<Modules>(json));

From a7a27e7abe02d586b729bcb84a43d677d799d6af Mon Sep 17 00:00:00 2001
From: Viktor Hofer <viktor.hofer@microsoft.com>
Date: Mon, 26 Nov 2018 19:00:08 +0100
Subject: [PATCH 7/9] Apply PR from viktorhofer

---
 README.md                                     | 25 +++---
 src/coverlet.core/Coverage.cs                 | 25 ++++--
 src/coverlet.core/CoverageResult.cs           | 25 +++---
 .../Helpers/InstrumentationHelper.cs          | 87 ++++++++++---------
 .../Helpers/InstrumentationHelperTests.cs     |  2 +-
 5 files changed, 87 insertions(+), 77 deletions(-)

diff --git a/README.md b/README.md
index d79bb70d2..c25c0e694 100644
--- a/README.md
+++ b/README.md
@@ -56,18 +56,19 @@ Arguments:
   <ASSEMBLY>  Path to the test assembly.
 
 Options:
-  -h|--help          Show help information
-  -v|--version       Show version information
-  -t|--target        Path to the test runner application.
-  -a|--targetargs    Arguments to be passed to the test runner.
-  -o|--output        Output of the generated coverage report
-  -f|--format        Format of the generated coverage report.
-  --threshold        Exits with error if the coverage % is below value.
-  --threshold-type   Coverage type to apply the threshold to.
-  --exclude          Filter expressions to exclude specific modules and types.
-  --include          Filter expressions to include specific modules and types.
-  --exclude-by-file  Glob patterns specifying source files to exclude.
-  --merge-with       Path to existing coverage result to merge.
+  -h|--help              Show help information
+  -v|--version           Show version information
+  -t|--target            Path to the test runner application.
+  -a|--targetargs        Arguments to be passed to the test runner.
+  -o|--output            Output of the generated coverage report
+  -f|--format            Format of the generated coverage report.
+  --threshold            Exits with error if the coverage % is below value.
+  --threshold-type       Coverage type to apply the threshold to.
+  --exclude              Filter expressions to exclude specific modules and types.
+  --include              Filter expressions to include specific modules and types.
+  --include-directories  Include directories containing additional assemblies to be instrumented.
+  --exclude-by-file      Glob patterns specifying source files to exclude.
+  --merge-with           Path to existing coverage result to merge.
 ```
 
 #### Code Coverage
diff --git a/src/coverlet.core/Coverage.cs b/src/coverlet.core/Coverage.cs
index 067bebc78..c9a4a906c 100644
--- a/src/coverlet.core/Coverage.cs
+++ b/src/coverlet.core/Coverage.cs
@@ -32,7 +32,7 @@ public Coverage(string module, string[] excludeFilters, string[] includeFilters,
             _module = module;
             _excludeFilters = excludeFilters;
             _includeFilters = includeFilters;
-            _includeDirectories = includeDirectories;
+            _includeDirectories = includeDirectories ?? Array.Empty<string>();
             _excludedSourceFiles = excludedSourceFiles;
             _mergeWith = mergeWith;
 
@@ -49,16 +49,26 @@ public void PrepareModules()
 
             foreach (var module in modules)
             {
-                if (InstrumentationHelper.IsModuleExcluded(module, _excludeFilters)
-                    || !InstrumentationHelper.IsModuleIncluded(module, _includeFilters))
+                if (InstrumentationHelper.IsModuleExcluded(module, _excludeFilters) ||
+                    !InstrumentationHelper.IsModuleIncluded(module, _includeFilters))
                     continue;
 
                 var instrumenter = new Instrumenter(module, _identifier, _excludeFilters, _includeFilters, excludes);
                 if (instrumenter.CanInstrument())
                 {
                     InstrumentationHelper.BackupOriginalModule(module, _identifier);
-                    var result = instrumenter.Instrument();
-                    _results.Add(result);
+
+                    // Guard code path and restore if instrumentation fails.
+                    try
+                    {
+                        var result = instrumenter.Instrument();
+                        _results.Add(result);
+                    }
+                    catch (Exception)
+                    {
+                        InstrumentationHelper.RestoreOriginalModule(module, _identifier);
+                        throw;
+                    }
                 }
             }
         }
@@ -148,12 +158,11 @@ public CoverageResult GetCoverageResult()
                     }
                 }
 
-                modules.Add(result.ModulePath, documents);
+                modules.Add(Path.GetFileName(result.ModulePath), documents);
                 InstrumentationHelper.RestoreOriginalModule(result.ModulePath, _identifier);
             }
 
-            var coverageResult = new CoverageResult { Identifier = _identifier, Modules = new Modules() };
-            coverageResult.Merge(modules);
+            var coverageResult = new CoverageResult { Identifier = _identifier, Modules = modules };
 
             if (!string.IsNullOrEmpty(_mergeWith) && !string.IsNullOrWhiteSpace(_mergeWith) && File.Exists(_mergeWith))
             {
diff --git a/src/coverlet.core/CoverageResult.cs b/src/coverlet.core/CoverageResult.cs
index c78aa7dcd..ed17451a7 100644
--- a/src/coverlet.core/CoverageResult.cs
+++ b/src/coverlet.core/CoverageResult.cs
@@ -1,3 +1,4 @@
+using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
@@ -44,9 +45,7 @@ internal void Merge(Modules modules)
         {
             foreach (var module in modules)
             {
-                var existingKey = Modules.Keys.SingleOrDefault(key => Path.GetFileName(key) == Path.GetFileName(module.Key));
-
-                if (existingKey == null)
+                if (!this.Modules.Keys.Contains(module.Key))
                 {
                     this.Modules.Add(module.Key, module.Value);
                 }
@@ -54,43 +53,43 @@ internal void Merge(Modules modules)
                 {
                     foreach (var document in module.Value)
                     {
-                        if (!this.Modules[existingKey].ContainsKey(document.Key))
+                        if (!this.Modules[module.Key].ContainsKey(document.Key))
                         {
-                            this.Modules[existingKey].Add(document.Key, document.Value);
+                            this.Modules[module.Key].Add(document.Key, document.Value);
                         }
                         else
                         {
                             foreach (var @class in document.Value)
                             {
-                                if (!this.Modules[existingKey][document.Key].ContainsKey(@class.Key))
+                                if (!this.Modules[module.Key][document.Key].ContainsKey(@class.Key))
                                 {
-                                    this.Modules[existingKey][document.Key].Add(@class.Key, @class.Value);
+                                    this.Modules[module.Key][document.Key].Add(@class.Key, @class.Value);
                                 }
                                 else
                                 {
                                     foreach (var method in @class.Value)
                                     {
-                                        if (!this.Modules[existingKey][document.Key][@class.Key].ContainsKey(method.Key))
+                                        if (!this.Modules[module.Key][document.Key][@class.Key].ContainsKey(method.Key))
                                         {
-                                            this.Modules[existingKey][document.Key][@class.Key].Add(method.Key, method.Value);
+                                            this.Modules[module.Key][document.Key][@class.Key].Add(method.Key, method.Value);
                                         }
                                         else
                                         {
                                             foreach (var line in method.Value.Lines)
                                             {
-                                                if (!this.Modules[existingKey][document.Key][@class.Key][method.Key].Lines.ContainsKey(line.Key))
+                                                if (!this.Modules[module.Key][document.Key][@class.Key][method.Key].Lines.ContainsKey(line.Key))
                                                 {
-                                                    this.Modules[existingKey][document.Key][@class.Key][method.Key].Lines.Add(line.Key, line.Value);
+                                                    this.Modules[module.Key][document.Key][@class.Key][method.Key].Lines.Add(line.Key, line.Value);
                                                 }
                                                 else
                                                 {
-                                                    this.Modules[existingKey][document.Key][@class.Key][method.Key].Lines[line.Key] += line.Value;
+                                                    this.Modules[module.Key][document.Key][@class.Key][method.Key].Lines[line.Key] += line.Value;
                                                 }
                                             }
 
                                             foreach (var branch in method.Value.Branches)
                                             {
-                                                var branches = this.Modules[existingKey][document.Key][@class.Key][method.Key].Branches;
+                                                var branches = this.Modules[module.Key][document.Key][@class.Key][method.Key].Branches;
                                                 var branchInfo = branches.FirstOrDefault(b => b.EndOffset == branch.EndOffset && b.Line == branch.Line && b.Offset == branch.Offset && b.Ordinal == branch.Ordinal && b.Path == branch.Path);
                                                 if (branchInfo == null)
                                                     branches.Add(branch);
diff --git a/src/coverlet.core/Helpers/InstrumentationHelper.cs b/src/coverlet.core/Helpers/InstrumentationHelper.cs
index 5321247ea..5b3d7c0c0 100644
--- a/src/coverlet.core/Helpers/InstrumentationHelper.cs
+++ b/src/coverlet.core/Helpers/InstrumentationHelper.cs
@@ -1,14 +1,10 @@
 using System;
-using System.Collections;
 using System.Collections.Generic;
 using System.Diagnostics;
-using System.Globalization;
 using System.IO;
 using System.Linq;
 using System.Reflection;
 using System.Reflection.PortableExecutable;
-using System.Security.Cryptography;
-using System.Text;
 using System.Text.RegularExpressions;
 
 using Microsoft.Extensions.FileSystemGlobbing;
@@ -20,15 +16,45 @@ internal static class InstrumentationHelper
     {
         public static string[] GetCoverableModules(string module, string[] includeDirectories)
         {
-            var moduleDirectory = Path.GetDirectoryName(module);
-            var files = new List<string>(Directory.GetFiles(moduleDirectory));
+            Debug.Assert(includeDirectories != null, "Parameter " + nameof(includeDirectories) + " in method " + 
+                nameof(InstrumentationHelper) + "." + nameof(GetCoverableModules) + " must not be null");
 
-            if (includeDirectories != null)
-                foreach (var includeDirectory in ExpandIncludeDirectories(includeDirectories, moduleDirectory))
-                    files.AddRange(Directory.GetFiles(includeDirectory));
+            string moduleDirectory = Path.GetDirectoryName(module);
+            if (moduleDirectory == string.Empty)
+            {
+                moduleDirectory = Directory.GetCurrentDirectory();
+            }
+
+            var dirs = new List<string>(1 + includeDirectories.Length)
+            {
+                // Add the test assembly's directory.
+                moduleDirectory
+            };
 
-            return files.Where(m => IsAssembly(m) && Path.GetFileName(m) != Path.GetFileName(module))
-                .Distinct().ToArray();
+            // Prepare all the directories in which we probe for modules.
+            foreach (var includeDirectory in includeDirectories.Where(d => d != null))
+            {
+                var fullPath = (!Path.IsPathRooted(includeDirectory)
+                    ? Path.GetFullPath(Path.Combine(moduleDirectory, includeDirectory))
+                    : includeDirectory).TrimEnd('*');
+
+                if (!Directory.Exists(fullPath)) continue;
+
+                if (includeDirectory.EndsWith("*", StringComparison.Ordinal))
+                    dirs.AddRange(Directory.GetDirectories(fullPath));
+                else
+                    dirs.Add(fullPath);
+            }
+
+            // The test module's name must be unique.
+            var uniqueModules = new HashSet<string>
+            {
+                Path.GetFileName(module)
+            };
+
+            return dirs.SelectMany(d => Directory.EnumerateFiles(d))
+                .Where(m => IsAssembly(m) && uniqueModules.Add(Path.GetFileName(m)))
+                .ToArray();
         }
 
         public static bool HasPdb(string module)
@@ -254,32 +280,11 @@ private static bool IsTypeFilterMatch(string module, string type, string[] filte
             return false;
         }
 
-        private static IEnumerable<string> ExpandIncludeDirectories(string[] includeDirectories, string moduleDirectory)
-        {
-            var result = new List<string>(includeDirectories.Length);
-
-            foreach (var includeDirectory in includeDirectories.Where(d => d != null))
-            {
-                var fullPath = (!Path.IsPathRooted(includeDirectory)
-                    ? Path.GetFullPath(Path.Combine(moduleDirectory, includeDirectory))
-                    : includeDirectory).TrimEnd('*');
-
-                if (!Directory.Exists(fullPath)) continue;
-
-                if (includeDirectory.EndsWith("*", StringComparison.Ordinal))
-                    result.AddRange(Directory.GetDirectories(fullPath));
-                else
-                    result.Add(fullPath);
-            }
-
-            return result;
-        }
-
         private static string GetBackupPath(string module, string identifier)
         {
             return Path.Combine(
                 Path.GetTempPath(),
-                Path.GetFileNameWithoutExtension(module) + "_" + GetPathHash(Path.GetDirectoryName(module)) + "_" + identifier + ".dll"
+                Path.GetFileNameWithoutExtension(module) + "_" + identifier + ".dll"
             );
         }
 
@@ -304,11 +309,14 @@ private static string WildcardToRegex(string pattern)
 
         private static bool IsAssembly(string filePath)
         {
+            Debug.Assert(filePath != null, "Parameter " + nameof(filePath) + " in " + nameof(InstrumentationHelper) + 
+                "." + nameof(IsAssembly) + " must not be null.");
+
+            if (!(filePath.EndsWith(".exe") || filePath.EndsWith(".dll")))
+                return false;
+
             try
             {
-                if (!(filePath.EndsWith(".exe") || filePath.EndsWith(".dll")))
-                    return false;
-
                 AssemblyName.GetAssemblyName(filePath);
                 return true;
             }
@@ -317,13 +325,6 @@ private static bool IsAssembly(string filePath)
                 return false;
             }
         }
-
-        private static string GetPathHash(string path)
-        {
-            using (var md5 = MD5.Create())
-                return BitConverter.ToString(md5.ComputeHash(Encoding.Unicode.GetBytes(path)))
-                    .Replace("-", string.Empty);
-        }
     }
 }
 
diff --git a/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs b/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs
index c7043cf86..28530599d 100644
--- a/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs
+++ b/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs
@@ -12,7 +12,7 @@ public class InstrumentationHelperTests
         public void TestGetDependencies()
         {
             string module = typeof(InstrumentationHelperTests).Assembly.Location;
-            var modules = InstrumentationHelper.GetCoverableModules(module, null);
+            var modules = InstrumentationHelper.GetCoverableModules(module, Array.Empty<string>());
             Assert.False(Array.Exists(modules, m => m == module));
         }
 

From d6e008fba9afc2bde3d45d64bf6d66eac912e48d Mon Sep 17 00:00:00 2001
From: Viktor Hofer <viktor.hofer@microsoft.com>
Date: Wed, 28 Nov 2018 16:36:52 +0100
Subject: [PATCH 8/9] Singular argument name, code cleanup, cwd change

---
 README.md                                     | 26 +++++++++---------
 src/coverlet.console/Program.cs               |  2 +-
 src/coverlet.core/Coverage.cs                 |  2 +-
 .../Helpers/InstrumentationHelper.cs          | 27 ++++++++++---------
 .../InstrumentationTask.cs                    | 10 +++----
 src/coverlet.msbuild/coverlet.msbuild.props   |  2 +-
 src/coverlet.msbuild/coverlet.msbuild.targets |  4 +--
 7 files changed, 37 insertions(+), 36 deletions(-)

diff --git a/README.md b/README.md
index c25c0e694..00aa7e0f0 100644
--- a/README.md
+++ b/README.md
@@ -56,19 +56,19 @@ Arguments:
   <ASSEMBLY>  Path to the test assembly.
 
 Options:
-  -h|--help              Show help information
-  -v|--version           Show version information
-  -t|--target            Path to the test runner application.
-  -a|--targetargs        Arguments to be passed to the test runner.
-  -o|--output            Output of the generated coverage report
-  -f|--format            Format of the generated coverage report.
-  --threshold            Exits with error if the coverage % is below value.
-  --threshold-type       Coverage type to apply the threshold to.
-  --exclude              Filter expressions to exclude specific modules and types.
-  --include              Filter expressions to include specific modules and types.
-  --include-directories  Include directories containing additional assemblies to be instrumented.
-  --exclude-by-file      Glob patterns specifying source files to exclude.
-  --merge-with           Path to existing coverage result to merge.
+  -h|--help            Show help information
+  -v|--version         Show version information
+  -t|--target          Path to the test runner application.
+  -a|--targetargs      Arguments to be passed to the test runner.
+  -o|--output          Output of the generated coverage report
+  -f|--format          Format of the generated coverage report.
+  --threshold          Exits with error if the coverage % is below value.
+  --threshold-type     Coverage type to apply the threshold to.
+  --exclude            Filter expressions to exclude specific modules and types.
+  --include            Filter expressions to include specific modules and types.
+  --include-directory  Include directories containing additional assemblies to be instrumented.
+  --exclude-by-file    Glob patterns specifying source files to exclude.
+  --merge-with         Path to existing coverage result to merge.
 ```
 
 #### Code Coverage
diff --git a/src/coverlet.console/Program.cs b/src/coverlet.console/Program.cs
index 5d7e837bd..f5156336c 100644
--- a/src/coverlet.console/Program.cs
+++ b/src/coverlet.console/Program.cs
@@ -32,7 +32,7 @@ static int Main(string[] args)
             CommandOption thresholdTypes = app.Option("--threshold-type", "Coverage type to apply the threshold to.", CommandOptionType.MultipleValue);
             CommandOption excludeFilters = app.Option("--exclude", "Filter expressions to exclude specific modules and types.", CommandOptionType.MultipleValue);
             CommandOption includeFilters = app.Option("--include", "Filter expressions to include only specific modules and types.", CommandOptionType.MultipleValue);
-            CommandOption includeDirectories = app.Option("--include-directories", "Include directories containing additional assemblies to be instrumented.", CommandOptionType.MultipleValue);
+            CommandOption includeDirectories = app.Option("--include-directory", "Include directories containing additional assemblies to be instrumented.", CommandOptionType.MultipleValue);
             CommandOption excludedSourceFiles = app.Option("--exclude-by-file", "Glob patterns specifying source files to exclude.", CommandOptionType.MultipleValue);
             CommandOption mergeWith = app.Option("--merge-with", "Path to existing coverage result to merge.", CommandOptionType.SingleValue);
 
diff --git a/src/coverlet.core/Coverage.cs b/src/coverlet.core/Coverage.cs
index c9a4a906c..c6669257e 100644
--- a/src/coverlet.core/Coverage.cs
+++ b/src/coverlet.core/Coverage.cs
@@ -66,8 +66,8 @@ public void PrepareModules()
                     }
                     catch (Exception)
                     {
+                        // TODO: With verbose logging we should note that instrumentation failed.
                         InstrumentationHelper.RestoreOriginalModule(module, _identifier);
-                        throw;
                     }
                 }
             }
diff --git a/src/coverlet.core/Helpers/InstrumentationHelper.cs b/src/coverlet.core/Helpers/InstrumentationHelper.cs
index 5b3d7c0c0..6982ea1b6 100644
--- a/src/coverlet.core/Helpers/InstrumentationHelper.cs
+++ b/src/coverlet.core/Helpers/InstrumentationHelper.cs
@@ -14,10 +14,9 @@ namespace Coverlet.Core.Helpers
 {
     internal static class InstrumentationHelper
     {
-        public static string[] GetCoverableModules(string module, string[] includeDirectories)
+        public static string[] GetCoverableModules(string module, string[] directories)
         {
-            Debug.Assert(includeDirectories != null, "Parameter " + nameof(includeDirectories) + " in method " + 
-                nameof(InstrumentationHelper) + "." + nameof(GetCoverableModules) + " must not be null");
+            Debug.Assert(directories != null);
 
             string moduleDirectory = Path.GetDirectoryName(module);
             if (moduleDirectory == string.Empty)
@@ -25,28 +24,31 @@ public static string[] GetCoverableModules(string module, string[] includeDirect
                 moduleDirectory = Directory.GetCurrentDirectory();
             }
 
-            var dirs = new List<string>(1 + includeDirectories.Length)
+            var dirs = new List<string>()
             {
                 // Add the test assembly's directory.
                 moduleDirectory
             };
 
-            // Prepare all the directories in which we probe for modules.
-            foreach (var includeDirectory in includeDirectories.Where(d => d != null))
+            // Prepare all the directories we probe for modules.
+            foreach (string directory in directories)
             {
-                var fullPath = (!Path.IsPathRooted(includeDirectory)
-                    ? Path.GetFullPath(Path.Combine(moduleDirectory, includeDirectory))
-                    : includeDirectory).TrimEnd('*');
+                if (string.IsNullOrWhiteSpace(directory)) continue;
+
+                string fullPath = (!Path.IsPathRooted(directory)
+                    ? Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), directory))
+                    : directory).TrimEnd('*');
 
                 if (!Directory.Exists(fullPath)) continue;
 
-                if (includeDirectory.EndsWith("*", StringComparison.Ordinal))
+                if (directory.EndsWith("*", StringComparison.Ordinal))
                     dirs.AddRange(Directory.GetDirectories(fullPath));
                 else
                     dirs.Add(fullPath);
             }
 
-            // The test module's name must be unique.
+            // The module's name must be unique.
+            // Add the test module itself to exclude it from the files enumeration.
             var uniqueModules = new HashSet<string>
             {
                 Path.GetFileName(module)
@@ -309,8 +311,7 @@ private static string WildcardToRegex(string pattern)
 
         private static bool IsAssembly(string filePath)
         {
-            Debug.Assert(filePath != null, "Parameter " + nameof(filePath) + " in " + nameof(InstrumentationHelper) + 
-                "." + nameof(IsAssembly) + " must not be null.");
+            Debug.Assert(filePath != null);
 
             if (!(filePath.EndsWith(".exe") || filePath.EndsWith(".dll")))
                 return false;
diff --git a/src/coverlet.msbuild.tasks/InstrumentationTask.cs b/src/coverlet.msbuild.tasks/InstrumentationTask.cs
index 400f7f1d6..40410e024 100644
--- a/src/coverlet.msbuild.tasks/InstrumentationTask.cs
+++ b/src/coverlet.msbuild.tasks/InstrumentationTask.cs
@@ -11,7 +11,7 @@ public class InstrumentationTask : Task
         private string _path;
         private string _exclude;
         private string _include;
-        private string _includeDirectories;
+        private string _includeDirectory;
         private string _excludeByFile;
         private string _mergeWith;
 
@@ -39,10 +39,10 @@ public string Include
             set { _include = value; }
         }
 
-        public string IncludeDirectories
+        public string IncludeDirectory
         {
-            get { return _includeDirectories; }
-            set { _includeDirectories = value; }
+            get { return _includeDirectory; }
+            set { _includeDirectory = value; }
         }
 
         public string ExcludeByFile
@@ -64,7 +64,7 @@ public override bool Execute()
                 var excludedSourceFiles = _excludeByFile?.Split(',');
                 var excludeFilters = _exclude?.Split(',');
                 var includeFilters = _include?.Split(',');
-                var includeDirectories = _includeDirectories?.Split(',');
+                var includeDirectories = _includeDirectory?.Split(',');
 
                 _coverage = new Coverage(_path, excludeFilters, includeFilters, includeDirectories, excludedSourceFiles, _mergeWith);
                 _coverage.PrepareModules();
diff --git a/src/coverlet.msbuild/coverlet.msbuild.props b/src/coverlet.msbuild/coverlet.msbuild.props
index d4beab8e3..729a6e2fa 100644
--- a/src/coverlet.msbuild/coverlet.msbuild.props
+++ b/src/coverlet.msbuild/coverlet.msbuild.props
@@ -9,6 +9,6 @@
     <MergeWith Condition="$(MergeWith) == ''"></MergeWith>
     <Threshold Condition="$(Threshold) == ''">0</Threshold>
     <ThresholdType Condition="$(ThresholdType) == ''">line,branch,method</ThresholdType>
-    <IncludeDirectories Condition="$(IncludeDirectories) == ''"></IncludeDirectories>
+    <IncludeDirectory Condition="$(IncludeDirectory) == ''"></IncludeDirectory>
   </PropertyGroup>
 </Project>
diff --git a/src/coverlet.msbuild/coverlet.msbuild.targets b/src/coverlet.msbuild/coverlet.msbuild.targets
index ec8807f9f..acbb8801a 100644
--- a/src/coverlet.msbuild/coverlet.msbuild.targets
+++ b/src/coverlet.msbuild/coverlet.msbuild.targets
@@ -7,7 +7,7 @@
     <Coverlet.MSbuild.Tasks.InstrumentationTask
       Condition="'$(VSTestNoBuild)' == 'true' and $(CollectCoverage) == 'true'"
       Include="$(Include)"
-      IncludeDirectories="$(IncludeDirectories)"
+      IncludeDirectory="$(IncludeDirectory)"
       Exclude="$(Exclude)"
       ExcludeByFile="$(ExcludeByFile)"
       MergeWith="$(MergeWith)"
@@ -18,7 +18,7 @@
     <Coverlet.MSbuild.Tasks.InstrumentationTask
       Condition="'$(VSTestNoBuild)' != 'true' and $(CollectCoverage) == 'true'"
       Include="$(Include)"
-      IncludeDirectories="$(IncludeDirectories)"
+      IncludeDirectory="$(IncludeDirectory)"
       Exclude="$(Exclude)"
       ExcludeByFile="$(ExcludeByFile)"
       MergeWith="$(MergeWith)"

From 89f5be5081bf7019de2039246f52e451126a23fc Mon Sep 17 00:00:00 2001
From: Viktor Hofer <viktor.hofer@microsoft.com>
Date: Wed, 28 Nov 2018 17:50:40 +0100
Subject: [PATCH 9/9] BackupOriginalModule no return value and overwrite

---
 src/coverlet.core/Helpers/InstrumentationHelper.cs         | 5 ++---
 .../Helpers/InstrumentationHelperTests.cs                  | 7 ++++++-
 2 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/src/coverlet.core/Helpers/InstrumentationHelper.cs b/src/coverlet.core/Helpers/InstrumentationHelper.cs
index 6982ea1b6..426b76c7e 100644
--- a/src/coverlet.core/Helpers/InstrumentationHelper.cs
+++ b/src/coverlet.core/Helpers/InstrumentationHelper.cs
@@ -78,11 +78,10 @@ public static bool HasPdb(string module)
             }
         }
 
-        public static string BackupOriginalModule(string module, string identifier)
+        public static void BackupOriginalModule(string module, string identifier)
         {
             var backupPath = GetBackupPath(module, identifier);
-            File.Copy(module, backupPath);
-            return backupPath;
+            File.Copy(module, backupPath, true);
         }
 
         public static void RestoreOriginalModule(string module, string identifier)
diff --git a/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs b/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs
index 28530599d..c2147f50b 100644
--- a/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs
+++ b/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs
@@ -28,7 +28,12 @@ public void TestBackupOriginalModule()
             string module = typeof(InstrumentationHelperTests).Assembly.Location;
             string identifier = Guid.NewGuid().ToString();
 
-            var backupPath = InstrumentationHelper.BackupOriginalModule(module, identifier);
+            InstrumentationHelper.BackupOriginalModule(module, identifier);
+
+            var backupPath = Path.Combine(
+                Path.GetTempPath(),
+                Path.GetFileNameWithoutExtension(module) + "_" + identifier + ".dll"
+            );
 
             Assert.True(File.Exists(backupPath));
         }