diff --git a/build.proj b/build.proj
index f9c9390de..72e630f05 100644
--- a/build.proj
+++ b/build.proj
@@ -9,6 +9,7 @@
+
diff --git a/src/coverlet.console/Program.cs b/src/coverlet.console/Program.cs
index 867bd131d..3f2ac498a 100644
--- a/src/coverlet.console/Program.cs
+++ b/src/coverlet.console/Program.cs
@@ -34,6 +34,7 @@ static int Main(string[] args)
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 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);
app.OnExecute(() =>
{
@@ -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());
+ Coverage coverage = new Coverage(module.Value, excludeFilters.Values.ToArray(), includeFilters.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 459d82914..5904a96de 100644
--- a/src/coverlet.core/Coverage.cs
+++ b/src/coverlet.core/Coverage.cs
@@ -6,6 +6,8 @@
using Coverlet.Core.Helpers;
using Coverlet.Core.Instrumentation;
+using Newtonsoft.Json;
+
namespace Coverlet.Core
{
public class Coverage
@@ -15,6 +17,7 @@ public class Coverage
private string[] _excludeFilters;
private string[] _includeFilters;
private string[] _excludedSourceFiles;
+ private string _mergeWith;
private List _results;
public string Identifier
@@ -22,12 +25,14 @@ public string Identifier
get { return _identifier; }
}
- public Coverage(string module, string[] excludeFilters, string[] includeFilters, string[] excludedSourceFiles)
+ public Coverage(string module, string[] excludeFilters, string[] includeFilters, string[] excludedSourceFiles, string mergeWith)
{
_module = module;
_excludeFilters = excludeFilters;
_includeFilters = includeFilters;
_excludedSourceFiles = excludedSourceFiles;
+ _mergeWith = mergeWith;
+
_identifier = Guid.NewGuid().ToString();
_results = new List();
}
@@ -144,11 +149,14 @@ public CoverageResult GetCoverageResult()
InstrumentationHelper.RestoreOriginalModule(result.ModulePath, _identifier);
}
- return new CoverageResult
+ var coverageResult = new CoverageResult { Identifier = _identifier, Modules = modules };
+ if (!string.IsNullOrEmpty(_mergeWith) && !string.IsNullOrWhiteSpace(_mergeWith))
{
- Identifier = _identifier,
- Modules = modules
- };
+ string json = File.ReadAllText(_mergeWith);
+ coverageResult.Merge(JsonConvert.DeserializeObject(json));
+ }
+
+ return coverageResult;
}
private void CalculateCoverage()
diff --git a/src/coverlet.core/CoverageResult.cs b/src/coverlet.core/CoverageResult.cs
index 9f176ae61..b7caf6f5e 100644
--- a/src/coverlet.core/CoverageResult.cs
+++ b/src/coverlet.core/CoverageResult.cs
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.IO;
+using System.Linq;
namespace Coverlet.Core
{
@@ -38,5 +39,70 @@ public class CoverageResult
public Modules Modules;
internal CoverageResult() { }
+
+ internal void Merge(Modules modules)
+ {
+ foreach (var module in modules)
+ {
+ if (!this.Modules.ContainsKey(module.Key))
+ {
+ this.Modules.Add(module.Key, module.Value);
+ }
+ else
+ {
+ foreach (var document in module.Value)
+ {
+ if (!this.Modules[module.Key].ContainsKey(document.Key))
+ {
+ this.Modules[module.Key].Add(document.Key, document.Value);
+ }
+ else
+ {
+ foreach (var @class in document.Value)
+ {
+ if (!this.Modules[module.Key][document.Key].ContainsKey(@class.Key))
+ {
+ this.Modules[module.Key][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))
+ {
+ this.Modules[module.Key][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))
+ {
+ this.Modules[module.Key][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;
+ }
+ }
+
+ foreach (var branch in method.Value.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);
+ else
+ branchInfo.Hits += branch.Hits;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/coverlet.msbuild.tasks/InstrumentationTask.cs b/src/coverlet.msbuild.tasks/InstrumentationTask.cs
index 60514c7c7..e53c87666 100644
--- a/src/coverlet.msbuild.tasks/InstrumentationTask.cs
+++ b/src/coverlet.msbuild.tasks/InstrumentationTask.cs
@@ -12,6 +12,7 @@ public class InstrumentationTask : Task
private string _exclude;
private string _include;
private string _excludeByFile;
+ private string _mergeWith;
internal static Coverage Coverage
{
@@ -43,6 +44,12 @@ public string ExcludeByFile
set { _excludeByFile = value; }
}
+ public string MergeWith
+ {
+ get { return _mergeWith; }
+ set { _mergeWith = value; }
+ }
+
public override bool Execute()
{
try
@@ -51,7 +58,7 @@ public override bool Execute()
var excludeFilters = _exclude?.Split(',');
var includeFilters = _include?.Split(',');
- _coverage = new Coverage(_path, excludeFilters, includeFilters, excludedSourceFiles);
+ _coverage = new Coverage(_path, excludeFilters, includeFilters, excludedSourceFiles, _mergeWith);
_coverage.PrepareModules();
}
catch (Exception ex)
diff --git a/src/coverlet.msbuild/coverlet.msbuild.props b/src/coverlet.msbuild/coverlet.msbuild.props
index 3dcb69948..e3a616a66 100644
--- a/src/coverlet.msbuild/coverlet.msbuild.props
+++ b/src/coverlet.msbuild/coverlet.msbuild.props
@@ -5,6 +5,7 @@
$([MSBuild]::EnsureTrailingSlash('$(MSBuildProjectDirectory)'))
+
0
line,branch,method
diff --git a/src/coverlet.msbuild/coverlet.msbuild.targets b/src/coverlet.msbuild/coverlet.msbuild.targets
index e77b7a0c8..8c4930822 100644
--- a/src/coverlet.msbuild/coverlet.msbuild.targets
+++ b/src/coverlet.msbuild/coverlet.msbuild.targets
@@ -8,6 +8,7 @@
Condition="'$(VSTestNoBuild)' == 'true' and $(CollectCoverage) == 'true'"
Exclude="$(Exclude)"
ExcludeByFile="$(ExcludeByFile)"
+ MergeWith="$(MergeWith)"
Path="$(TargetPath)" />
@@ -16,6 +17,7 @@
Condition="'$(VSTestNoBuild)' != 'true' and $(CollectCoverage) == 'true'"
Exclude="$(Exclude)"
ExcludeByFile="$(ExcludeByFile)"
+ MergeWith="$(MergeWith)"
Path="$(TargetPath)" />
diff --git a/test/coverlet.core.tests/CoverageTests.cs b/test/coverlet.core.tests/CoverageTests.cs
index 45d8fdbd6..fc9d26f60 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(), Array.Empty(), Array.Empty());
+ var coverage = new Coverage(testModule, Array.Empty(), Array.Empty(), Array.Empty(), string.Empty);
coverage.PrepareModules();
var result = coverage.GetCoverageResult();