diff --git a/Documentation/Examples/StandaloneUsage/Calculator/Calculator.csproj b/Documentation/Examples/StandaloneUsage/Calculator/Calculator.csproj
new file mode 100644
index 000000000..8cd52edce
--- /dev/null
+++ b/Documentation/Examples/StandaloneUsage/Calculator/Calculator.csproj
@@ -0,0 +1,13 @@
+
+
+
+ Exe
+ netcoreapp2.2
+
+
+
+
+ ..\..\..\..\src\coverlet.core\bin\Debug\netstandard2.0\coverlet.core.dll
+
+
+
diff --git a/Documentation/Examples/StandaloneUsage/Calculator/CalculatorRuntime.cs b/Documentation/Examples/StandaloneUsage/Calculator/CalculatorRuntime.cs
new file mode 100644
index 000000000..1fe0eb3d0
--- /dev/null
+++ b/Documentation/Examples/StandaloneUsage/Calculator/CalculatorRuntime.cs
@@ -0,0 +1,25 @@
+namespace Calculator
+{
+ public class CalculatorRuntime
+ {
+ public double Add(double a, double b)
+ {
+ return a + b;
+ }
+
+ public double Subtract(double a, double b)
+ {
+ return a - b;
+ }
+
+ public double Divide(double a, double b)
+ {
+ return a / b;
+ }
+
+ public double Multiply(double a, double b)
+ {
+ return a * b;
+ }
+ }
+}
diff --git a/Documentation/Examples/StandaloneUsage/Calculator/Program.cs b/Documentation/Examples/StandaloneUsage/Calculator/Program.cs
new file mode 100644
index 000000000..b5ae3db35
--- /dev/null
+++ b/Documentation/Examples/StandaloneUsage/Calculator/Program.cs
@@ -0,0 +1,101 @@
+#nullable enable
+
+using System;
+using System.IO;
+
+namespace Calculator
+{
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ if (args.Length == 0 || !File.Exists(args[0]))
+ {
+ Console.WriteLine("Instrumentation result file not found");
+ }
+
+
+ RealTimeCoverageAnalysis inProc = new RealTimeCoverageAnalysis(args[0]);
+ CalculatorRuntime runtime = new CalculatorRuntime();
+ double? operanda = null;
+ double? operandb = null;
+ for (; ; )
+ {
+ if (operanda is null)
+ {
+ Console.WriteLine("Insert operand a");
+ double operand;
+ while (!double.TryParse(Console.ReadLine(), out operand))
+ {
+ Console.WriteLine("Invalid value, insert operand a");
+ }
+ operanda = operand;
+ }
+ if (operandb is null)
+ {
+ Console.WriteLine("Insert operand b");
+ double operand;
+ while (!double.TryParse(Console.ReadLine(), out operand))
+ {
+ Console.WriteLine("Invalid value, insert operand b");
+ }
+ operandb = operand;
+ }
+
+
+ for (; ; )
+ {
+ Console.WriteLine("Insert operation");
+ ConsoleKeyInfo consoleKeyInfo = Console.ReadKey();
+ if (consoleKeyInfo.Key != ConsoleKey.Add && consoleKeyInfo.Key != ConsoleKey.Subtract &&
+ consoleKeyInfo.Key != ConsoleKey.Divide && consoleKeyInfo.Key != ConsoleKey.Multiply)
+ {
+ Console.WriteLine("Invalid operation, allowed operation, +-*/");
+ }
+ else
+ {
+ Console.WriteLine();
+ switch (consoleKeyInfo.Key)
+ {
+ case ConsoleKey.Add:
+ {
+ Console.WriteLine($"Result: {runtime.Add(operanda.Value, operandb.Value)}");
+ break;
+ }
+ case ConsoleKey.Subtract:
+ {
+ Console.WriteLine($"Result: {runtime.Subtract(operanda.Value, operandb.Value)}");
+ break;
+ }
+ case ConsoleKey.Multiply:
+ {
+ Console.WriteLine($"Result: {runtime.Multiply(operanda.Value, operandb.Value)}");
+ break;
+ }
+ case ConsoleKey.Divide:
+ {
+ Console.WriteLine($"Result: {runtime.Divide(operanda.Value, operandb.Value)}");
+ break;
+ }
+ default:
+ break;
+ }
+ operanda = operandb = null;
+
+ inProc.PrintCoverageCurrentState();
+
+ Console.WriteLine();
+ Console.WriteLine("Exit(press E)? Any other button to another loop");
+ if (Console.ReadKey().Key == ConsoleKey.E)
+ {
+ return;
+ }
+ Console.Clear();
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
diff --git a/Documentation/Examples/StandaloneUsage/Calculator/RealTimeCoverageAnalysis.cs b/Documentation/Examples/StandaloneUsage/Calculator/RealTimeCoverageAnalysis.cs
new file mode 100644
index 000000000..6de3abbf4
--- /dev/null
+++ b/Documentation/Examples/StandaloneUsage/Calculator/RealTimeCoverageAnalysis.cs
@@ -0,0 +1,56 @@
+using System;
+using System.IO;
+using System.Reflection;
+
+using Coverlet.Core.Abstracts;
+using Coverlet.Core.Instrumentation;
+using Coverlet.Core.ObjectModel;
+
+namespace Calculator
+{
+ public class RealTimeCoverageAnalysis
+ {
+ readonly IInProcessCoverageEngine inProcessEngine;
+
+ public RealTimeCoverageAnalysis(string instrumentationResult)
+ {
+ ICoverageEngineFactory coverageFactory = new BuildInCoverageEngineFactory();
+ inProcessEngine = coverageFactory.CreateInProcessEngine(File.OpenRead(instrumentationResult));
+ }
+
+ public void PrintCoverageCurrentState()
+ {
+ Console.WriteLine();
+ Console.WriteLine("***Start live coverage analysis***");
+ Console.WriteLine("---List of instrumented assemblies---");
+ foreach (Assembly asm in inProcessEngine.GetInstrumentedAssemblies())
+ {
+ Console.WriteLine(asm);
+ }
+ Console.WriteLine("---Method lines coverage---");
+ CoverageResult? coverageResult = inProcessEngine.ReadCurrentCoverage();
+ if (coverageResult != null)
+ {
+ CoverageSummary summary = new CoverageSummary();
+ foreach (var module in coverageResult.Modules)
+ {
+ foreach (var document in module.Value)
+ {
+ foreach (var @class in document.Value)
+ {
+ foreach (var method in @class.Value)
+ {
+ var methodLineDetails = summary.CalculateMethodCoverage(method.Value.Lines);
+ Console.WriteLine($"Method '{method.Key}' {methodLineDetails.Percent}%");
+ }
+ }
+ }
+ }
+ ConsoleColor tmp = Console.ForegroundColor;
+ Console.ForegroundColor = ConsoleColor.Green;
+ Console.WriteLine($"Total modules method lines covered '{summary.CalculateMethodCoverage(coverageResult.Modules).Percent}'");
+ Console.ForegroundColor = tmp;
+ }
+ }
+ }
+}
diff --git a/Documentation/Examples/StandaloneUsage/Directory.Build.props b/Documentation/Examples/StandaloneUsage/Directory.Build.props
new file mode 100644
index 000000000..b6216655d
--- /dev/null
+++ b/Documentation/Examples/StandaloneUsage/Directory.Build.props
@@ -0,0 +1,10 @@
+
+
+
+ enable
+ preview
+
+
+
+
+
diff --git a/Documentation/Examples/StandaloneUsage/Instrumentor/Instrumentor.csproj b/Documentation/Examples/StandaloneUsage/Instrumentor/Instrumentor.csproj
new file mode 100644
index 000000000..8cd52edce
--- /dev/null
+++ b/Documentation/Examples/StandaloneUsage/Instrumentor/Instrumentor.csproj
@@ -0,0 +1,13 @@
+
+
+
+ Exe
+ netcoreapp2.2
+
+
+
+
+ ..\..\..\..\src\coverlet.core\bin\Debug\netstandard2.0\coverlet.core.dll
+
+
+
diff --git a/Documentation/Examples/StandaloneUsage/Instrumentor/Program.cs b/Documentation/Examples/StandaloneUsage/Instrumentor/Program.cs
new file mode 100644
index 000000000..de5d75aeb
--- /dev/null
+++ b/Documentation/Examples/StandaloneUsage/Instrumentor/Program.cs
@@ -0,0 +1,34 @@
+using System;
+using System.IO;
+using Coverlet.Core.Abstracts;
+using Coverlet.Core.Instrumentation;
+
+namespace Instrumentor
+{
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ if (args.Length == 0 || !File.Exists(args[0]))
+ {
+ Console.WriteLine("Invalid lib to instrument");
+ return;
+ }
+
+ ICoverageEngineFactory coverageFactory = new BuildInCoverageEngineFactory();
+ InstrumentationOptions options = new InstrumentationOptions();
+ options.Module = args[0];
+ options.IncludeTestAssembly = true;
+ ICoverageEngine coverageEngine = coverageFactory.CreateEngine(options);
+ using (Stream stream = coverageEngine.PrepareModules(),
+ fs = File.OpenWrite("instrumentationResult"))
+ {
+ stream.CopyTo(fs);
+ }
+
+ Console.WriteLine($"Instrumentation result saved '{Path.GetFullPath("instrumentationResult")}'");
+ Console.WriteLine("Instrumentor active, click any button to restore instrumented libraries");
+ Console.ReadKey();
+ }
+ }
+}
diff --git a/Documentation/Examples/StandaloneUsage/Readme.md b/Documentation/Examples/StandaloneUsage/Readme.md
new file mode 100644
index 000000000..7e3c0e483
--- /dev/null
+++ b/Documentation/Examples/StandaloneUsage/Readme.md
@@ -0,0 +1,69 @@
+### Run sample
+
+1) Go to root coverlet folder and generate packages
+```
+dotnet pack
+...
+Successfully created package 'C:\git\coverlet\bin\Debug\Packages\coverlet.core.1.0.4-gac3e48b424.nupkg'
+```
+2) Update `run_instrumentor.cmd` with correct package version(in this case *1.0.4-gac3e48b424*)
+```
+...
+dotnet build /p:coverletCoreVersion=1.0.4-gac3e48b424
+...
+```
+3) Open fresh new command line and run `run_instrumentor.cmd`
+```
+run_instrumentor.cmd
+
+dotnet build /p:coverletCoreVersion=1.0.2-g1df3e82a5b
+Microsoft (R) Build Engine version 16.2.32702+c4012a063 for .NET Core
+Copyright (C) Microsoft Corporation. All rights reserved.
+
+ Restore completed in 54,96 ms for C:\git\coverlet\Documentation\Examples\StandaloneUsage\Calculator\Calculator.csproj.
+ Restore completed in 54,96 ms for C:\git\coverlet\Documentation\Examples\StandaloneUsage\Instrumentor\Instrumentor.csproj.
+ Instrumentor -> C:\git\coverlet\Documentation\Examples\StandaloneUsage\Instrumentor\bin\Debug\netcoreapp2.2\Instrumentor.dll
+ Calculator -> C:\git\coverlet\Documentation\Examples\StandaloneUsage\Calculator\bin\Debug\netcoreapp2.2\Calculator.dll
+
+Build succeeded.
+ 0 Warning(s)
+ 0 Error(s)
+
+Time Elapsed 00:00:01.25
+
+dotnet Instrumentor/bin/Debug/netcoreapp2.2/Instrumentor.dll Calculator/bin/Debug/netcoreapp2.2/Calculator.dll
+[LogVerbose] Instrumented module: 'Calculator\bin\Debug\netcoreapp2.2\Calculator.dll'
+Instrumentation result saved 'C:\git\coverlet\Documentation\Examples\StandaloneUsage\instrumentationResult'
+Instrumentor active, click any button to restore instrumented libraries
+```
+This process instruments "Calculator" app dll, you need to keep this process alive until end of app usage because when
+instrumentor process shutdown coverlet restores old "non instrumented" libraries.
+
+4) Open another fresh new command line and run `run_app.cmd` and play with it
+```
+run_app.cmd
+
+dotnet Calculator/bin/Debug/netcoreapp2.2/Calculator.dll instrumentationResult
+Insert operand a
+10
+Insert operand b
+20
+Insert operation
++
+Result: 30
+
+***Start live coverage analysis***
+---List of instrumented assemblies---
+Calculator, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
+---Method lines coverage---
+Method 'System.Double Calculator.CalculatorRuntime::Add(System.Double,System.Double)' 100%
+Method 'System.Double Calculator.CalculatorRuntime::Subtrac(System.Double,System.Double)' 0%
+Method 'System.Double Calculator.CalculatorRuntime::Divide(System.Double,System.Double)' 0%
+Method 'System.Double Calculator.CalculatorRuntime::Multiply(System.Double,System.Double)' 0%
+Method 'System.Void Calculator.Program::Main(System.String[])' 100%
+Method 'System.Void Calculator.RealTimeCoverageAnalysis::PrintCoverageCurrentState()' 100%
+Method 'System.Void Calculator.RealTimeCoverageAnalysis::.ctor(System.String)' 100%
+Total modules method lines covered '57,14'
+
+Exit(press E)? Any other button to another loop
+```
\ No newline at end of file
diff --git a/Documentation/Examples/StandaloneUsage/StandaloneUsage.sln b/Documentation/Examples/StandaloneUsage/StandaloneUsage.sln
new file mode 100644
index 000000000..1ea3b3c44
--- /dev/null
+++ b/Documentation/Examples/StandaloneUsage/StandaloneUsage.sln
@@ -0,0 +1,60 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29519.161
+MinimumVisualStudioVersion = 15.0.26124.0
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{280AB950-6C74-493D-B4BD-0BA355511561}"
+ ProjectSection(SolutionItems) = preProject
+ Directory.Build.props = Directory.Build.props
+ nuget.config = nuget.config
+ Readme.md = Readme.md
+ run_app.cmd = run_app.cmd
+ run_instrumentor.cmd = run_instrumentor.cmd
+ EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Calculator", "Calculator\Calculator.csproj", "{06FD3D7A-AD30-4FF1-8E39-CEA6384B33AD}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Instrumentor", "Instrumentor\Instrumentor.csproj", "{DCD64A1A-8BE9-4475-AE56-3BA44AB2ED2A}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {06FD3D7A-AD30-4FF1-8E39-CEA6384B33AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {06FD3D7A-AD30-4FF1-8E39-CEA6384B33AD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {06FD3D7A-AD30-4FF1-8E39-CEA6384B33AD}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {06FD3D7A-AD30-4FF1-8E39-CEA6384B33AD}.Debug|x64.Build.0 = Debug|Any CPU
+ {06FD3D7A-AD30-4FF1-8E39-CEA6384B33AD}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {06FD3D7A-AD30-4FF1-8E39-CEA6384B33AD}.Debug|x86.Build.0 = Debug|Any CPU
+ {06FD3D7A-AD30-4FF1-8E39-CEA6384B33AD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {06FD3D7A-AD30-4FF1-8E39-CEA6384B33AD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {06FD3D7A-AD30-4FF1-8E39-CEA6384B33AD}.Release|x64.ActiveCfg = Release|Any CPU
+ {06FD3D7A-AD30-4FF1-8E39-CEA6384B33AD}.Release|x64.Build.0 = Release|Any CPU
+ {06FD3D7A-AD30-4FF1-8E39-CEA6384B33AD}.Release|x86.ActiveCfg = Release|Any CPU
+ {06FD3D7A-AD30-4FF1-8E39-CEA6384B33AD}.Release|x86.Build.0 = Release|Any CPU
+ {DCD64A1A-8BE9-4475-AE56-3BA44AB2ED2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DCD64A1A-8BE9-4475-AE56-3BA44AB2ED2A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DCD64A1A-8BE9-4475-AE56-3BA44AB2ED2A}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {DCD64A1A-8BE9-4475-AE56-3BA44AB2ED2A}.Debug|x64.Build.0 = Debug|Any CPU
+ {DCD64A1A-8BE9-4475-AE56-3BA44AB2ED2A}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {DCD64A1A-8BE9-4475-AE56-3BA44AB2ED2A}.Debug|x86.Build.0 = Debug|Any CPU
+ {DCD64A1A-8BE9-4475-AE56-3BA44AB2ED2A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DCD64A1A-8BE9-4475-AE56-3BA44AB2ED2A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DCD64A1A-8BE9-4475-AE56-3BA44AB2ED2A}.Release|x64.ActiveCfg = Release|Any CPU
+ {DCD64A1A-8BE9-4475-AE56-3BA44AB2ED2A}.Release|x64.Build.0 = Release|Any CPU
+ {DCD64A1A-8BE9-4475-AE56-3BA44AB2ED2A}.Release|x86.ActiveCfg = Release|Any CPU
+ {DCD64A1A-8BE9-4475-AE56-3BA44AB2ED2A}.Release|x86.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {91E885C2-56D5-41E4-AA91-015650029ECA}
+ EndGlobalSection
+EndGlobal
diff --git a/Documentation/Examples/StandaloneUsage/nuget.config b/Documentation/Examples/StandaloneUsage/nuget.config
new file mode 100644
index 000000000..168cadce8
--- /dev/null
+++ b/Documentation/Examples/StandaloneUsage/nuget.config
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/Documentation/Examples/StandaloneUsage/run_app.cmd b/Documentation/Examples/StandaloneUsage/run_app.cmd
new file mode 100644
index 000000000..3207f5e71
--- /dev/null
+++ b/Documentation/Examples/StandaloneUsage/run_app.cmd
@@ -0,0 +1 @@
+dotnet Calculator/bin/Debug/netcoreapp2.2/Calculator.dll instrumentationResult
\ No newline at end of file
diff --git a/Documentation/Examples/StandaloneUsage/run_instrumentor.cmd b/Documentation/Examples/StandaloneUsage/run_instrumentor.cmd
new file mode 100644
index 000000000..f1ada3f9d
--- /dev/null
+++ b/Documentation/Examples/StandaloneUsage/run_instrumentor.cmd
@@ -0,0 +1,2 @@
+dotnet build /p:coverletCoreVersion=1.0.2-g1df3e82a5b
+dotnet Instrumentor/bin/Debug/netcoreapp2.2/Instrumentor.dll Calculator/bin/Debug/netcoreapp2.2/Calculator.dll
\ No newline at end of file
diff --git a/coverlet.sln b/coverlet.sln
index 0df80bdce..92a2aa516 100644
--- a/coverlet.sln
+++ b/coverlet.sln
@@ -46,6 +46,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.integration.tests"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.integration.template", "test\coverlet.integration.template\coverlet.integration.template.csproj", "{F6FE7678-C662-43D3-AC6A-64F6AC5A5935}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.core.standalone.tests", "test\coverlet.core.standalone.tests\coverlet.core.standalone.tests.csproj", "{D324E73F-69DB-41BA-BF54-A79FD292114C}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.core.standalone.sample", "test\coverlet.core.standalone.sample\coverlet.core.standalone.sample.csproj", "{F1617303-0F38-4D98-9BFA-370B57F3A154}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -104,6 +108,14 @@ Global
{F6FE7678-C662-43D3-AC6A-64F6AC5A5935}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F6FE7678-C662-43D3-AC6A-64F6AC5A5935}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F6FE7678-C662-43D3-AC6A-64F6AC5A5935}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D324E73F-69DB-41BA-BF54-A79FD292114C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D324E73F-69DB-41BA-BF54-A79FD292114C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D324E73F-69DB-41BA-BF54-A79FD292114C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D324E73F-69DB-41BA-BF54-A79FD292114C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F1617303-0F38-4D98-9BFA-370B57F3A154}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F1617303-0F38-4D98-9BFA-370B57F3A154}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F1617303-0F38-4D98-9BFA-370B57F3A154}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F1617303-0F38-4D98-9BFA-370B57F3A154}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -122,6 +134,8 @@ Global
{D6B14F2F-9E7D-4D2C-BAC8-48834F853ED6} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
{99B4059C-B25C-4B82-8117-A0E9DC9B0949} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
{F6FE7678-C662-43D3-AC6A-64F6AC5A5935} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
+ {D324E73F-69DB-41BA-BF54-A79FD292114C} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
+ {F1617303-0F38-4D98-9BFA-370B57F3A154} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9CA57C02-97B0-4C38-A027-EA61E8741F10}
diff --git a/src/coverlet.collector/DataCollection/CoverageManager.cs b/src/coverlet.collector/DataCollection/CoverageManager.cs
index 5dd59e2f6..85d79b14b 100644
--- a/src/coverlet.collector/DataCollection/CoverageManager.cs
+++ b/src/coverlet.collector/DataCollection/CoverageManager.cs
@@ -2,11 +2,13 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
+
using coverlet.collector.Resources;
using Coverlet.Collector.Utilities;
using Coverlet.Collector.Utilities.Interfaces;
using Coverlet.Core;
using Coverlet.Core.Abstracts;
+using Coverlet.Core.ObjectModel;
using Coverlet.Core.Reporters;
namespace Coverlet.Collector.DataCollection
diff --git a/src/coverlet.collector/DataCollection/CoverageWrapper.cs b/src/coverlet.collector/DataCollection/CoverageWrapper.cs
index 990274af3..e7dd800f8 100644
--- a/src/coverlet.collector/DataCollection/CoverageWrapper.cs
+++ b/src/coverlet.collector/DataCollection/CoverageWrapper.cs
@@ -2,6 +2,7 @@
using Coverlet.Core;
using Coverlet.Core.Abstracts;
using Coverlet.Core.Extensions;
+using Coverlet.Core.ObjectModel;
namespace Coverlet.Collector.DataCollection
{
diff --git a/src/coverlet.collector/Utilities/Interfaces/ICoverageWrapper.cs b/src/coverlet.collector/Utilities/Interfaces/ICoverageWrapper.cs
index 59cab7c04..d24f6dea8 100644
--- a/src/coverlet.collector/Utilities/Interfaces/ICoverageWrapper.cs
+++ b/src/coverlet.collector/Utilities/Interfaces/ICoverageWrapper.cs
@@ -1,6 +1,7 @@
using Coverlet.Collector.DataCollection;
using Coverlet.Core;
using Coverlet.Core.Abstracts;
+using Coverlet.Core.ObjectModel;
namespace Coverlet.Collector.Utilities.Interfaces
{
diff --git a/src/coverlet.console/Program.cs b/src/coverlet.console/Program.cs
index 608786fcb..5f0771a4d 100644
--- a/src/coverlet.console/Program.cs
+++ b/src/coverlet.console/Program.cs
@@ -11,6 +11,7 @@
using Coverlet.Core.Abstracts;
using Coverlet.Core.Enums;
using Coverlet.Core.Extensions;
+using Coverlet.Core.ObjectModel;
using Coverlet.Core.Reporters;
using McMaster.Extensions.CommandLineUtils;
diff --git a/src/coverlet.core/Abstracts/IInstrumentationHelper.cs b/src/coverlet.core/Abstracts/IInstrumentationHelper.cs
index 42bab81f3..e3b17e473 100644
--- a/src/coverlet.core/Abstracts/IInstrumentationHelper.cs
+++ b/src/coverlet.core/Abstracts/IInstrumentationHelper.cs
@@ -3,7 +3,7 @@
internal interface IInstrumentationHelper
{
void BackupOriginalModule(string module, string identifier);
- void DeleteHitsFile(string path);
+ bool DeleteHitsFile(string path);
string[] GetCoverableModules(string module, string[] directories, bool includeTestAssembly);
bool HasPdb(string module, out bool embedded);
bool IsModuleExcluded(string module, string[] excludeFilters);
diff --git a/src/coverlet.core/Abstracts/IInstrumenter.cs b/src/coverlet.core/Abstracts/IInstrumenter.cs
new file mode 100644
index 000000000..470d4aa43
--- /dev/null
+++ b/src/coverlet.core/Abstracts/IInstrumenter.cs
@@ -0,0 +1,42 @@
+#nullable enable
+
+using System.IO;
+using System.Reflection;
+
+using Coverlet.Core.ObjectModel;
+
+namespace Coverlet.Core.Abstracts
+{
+ public class InstrumentationOptions
+ {
+ public string? Module { get; set; }
+ public string[]? IncludeFilters { get; set; }
+ public string[]? ExcludeFilters { get; set; }
+ public string[]? ExcludeSourceFiles { get; set; }
+ public string[]? ExcludeAttributes { get; set; }
+ public string[]? IncludeDirectories { get; set; }
+ public bool IncludeTestAssembly { get; set; }
+ public bool SingleHit { get; set; }
+ public bool UseSourceLink { get; set; }
+ public string? MergeWith { get; set; }
+ }
+
+ public interface ICoverageEngineFactory
+ {
+ ICoverageEngine CreateEngine(InstrumentationOptions options);
+ IReporter CreateReporter(string format);
+ IInProcessCoverageEngine CreateInProcessEngine(Stream instrumentationResultStream);
+ }
+
+ public interface ICoverageEngine
+ {
+ Stream PrepareModules();
+ CoverageResult GetCoverageResult(Stream instrumentationResultStream);
+ }
+
+ public interface IInProcessCoverageEngine
+ {
+ Assembly[] GetInstrumentedAssemblies();
+ CoverageResult? ReadCurrentCoverage();
+ }
+}
diff --git a/src/coverlet.core/Abstracts/IReporter.cs b/src/coverlet.core/Abstracts/IReporter.cs
new file mode 100644
index 000000000..116c7d882
--- /dev/null
+++ b/src/coverlet.core/Abstracts/IReporter.cs
@@ -0,0 +1,23 @@
+using Coverlet.Core.ObjectModel;
+
+namespace Coverlet.Core.Abstracts
+{
+ internal interface IReporterFactory
+ {
+ IReporter Create(string format);
+ }
+
+ public interface IReporter
+ {
+ ReporterOutputType OutputType { get; }
+ string Format { get; }
+ string Extension { get; }
+ string Report(CoverageResult result);
+ }
+
+ public enum ReporterOutputType
+ {
+ File,
+ Console
+ }
+}
\ No newline at end of file
diff --git a/src/coverlet.core/Coverage.cs b/src/coverlet.core/Coverage.cs
index 1a2050824..990d52d79 100644
--- a/src/coverlet.core/Coverage.cs
+++ b/src/coverlet.core/Coverage.cs
@@ -2,9 +2,10 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
+
using Coverlet.Core.Abstracts;
using Coverlet.Core.Instrumentation;
-
+using Coverlet.Core.ObjectModel;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
@@ -132,10 +133,15 @@ public CoveragePrepareResult PrepareModules()
public CoverageResult GetCoverageResult()
{
- CalculateCoverage();
+ return GetCoverageResult(_results, _useSourceLink, _logger, _fileSystem, _instrumentationHelper, _identifier, _mergeWith);
+ }
+
+ public static CoverageResult GetCoverageResult(IEnumerable results, bool useSourceLink, ILogger logger, IFileSystem fileSystem, IInstrumentationHelper instrumentationHelper, string identifier, string mergeWith)
+ {
+ CalculateCoverage(results, useSourceLink, logger, fileSystem, instrumentationHelper);
Modules modules = new Modules();
- foreach (var result in _results)
+ foreach (var result in results)
{
Documents documents = new Documents();
foreach (var doc in result.Documents.Values)
@@ -216,7 +222,7 @@ public CoverageResult GetCoverageResult()
}
modules.Add(Path.GetFileName(result.ModulePath), documents);
- _instrumentationHelper.RestoreOriginalModule(result.ModulePath, _identifier);
+ instrumentationHelper.RestoreOriginalModule(result.ModulePath, identifier);
}
// In case of anonymous delegate compiler generate a custom class and passes it as type.method delegate.
@@ -234,7 +240,7 @@ public CoverageResult GetCoverageResult()
{
foreach (var branch in method.Value.Branches)
{
- if (BranchInCompilerGeneratedClass(method.Key))
+ if (BranchInCompilerGeneratedClass(method.Key, results))
{
Method actualMethod = GetMethodWithSameLineInSameDocument(document.Value, @class.Key, branch.Line);
@@ -276,20 +282,20 @@ public CoverageResult GetCoverageResult()
}
}
- var coverageResult = new CoverageResult { Identifier = _identifier, Modules = modules, InstrumentedResults = _results, UseSourceLink = _useSourceLink };
+ var coverageResult = new CoverageResult { Identifier = identifier, Modules = modules, InstrumentedResults = results.ToList(), UseSourceLink = useSourceLink };
- if (!string.IsNullOrEmpty(_mergeWith) && !string.IsNullOrWhiteSpace(_mergeWith) && _fileSystem.Exists(_mergeWith))
+ if (!string.IsNullOrEmpty(mergeWith) && !string.IsNullOrWhiteSpace(mergeWith) && fileSystem.Exists(mergeWith))
{
- string json = _fileSystem.ReadAllText(_mergeWith);
+ string json = fileSystem.ReadAllText(mergeWith);
coverageResult.Merge(JsonConvert.DeserializeObject(json));
}
return coverageResult;
}
- private bool BranchInCompilerGeneratedClass(string methodName)
+ private static bool BranchInCompilerGeneratedClass(string methodName, IEnumerable results)
{
- foreach (var instrumentedResult in _results)
+ foreach (var instrumentedResult in results)
{
if (instrumentedResult.BranchesInCompiledGeneratedClass.Contains(methodName))
{
@@ -299,7 +305,7 @@ private bool BranchInCompilerGeneratedClass(string methodName)
return false;
}
- private Method GetMethodWithSameLineInSameDocument(Classes documentClasses, string compilerGeneratedClassName, int branchLine)
+ private static Method GetMethodWithSameLineInSameDocument(Classes documentClasses, string compilerGeneratedClassName, int branchLine)
{
foreach (var @class in documentClasses)
{
@@ -322,22 +328,22 @@ private Method GetMethodWithSameLineInSameDocument(Classes documentClasses, stri
return null;
}
- private void CalculateCoverage()
+ private static void CalculateCoverage(IEnumerable results, bool useSourceLink, ILogger logger, IFileSystem fileSystem, IInstrumentationHelper instrumentationHelper)
{
- foreach (var result in _results)
+ foreach (var result in results)
{
- if (!_fileSystem.Exists(result.HitsFilePath))
+ if (!fileSystem.Exists(result.HitsFilePath))
{
// Hits file could be missed mainly for two reason
// 1) Issue during module Unload()
// 2) Instrumented module is never loaded or used so we don't have any hit to register and
// module tracker is never used
- _logger.LogVerbose($"Hits file:'{result.HitsFilePath}' not found for module: '{result.Module}'");
+ logger.LogVerbose($"Hits file:'{result.HitsFilePath}' not found for module: '{result.Module}'");
continue;
}
List documents = result.Documents.Values.ToList();
- if (_useSourceLink && result.SourceLink != null)
+ if (useSourceLink && result.SourceLink != null)
{
var jObject = JObject.Parse(result.SourceLink)["documents"];
var sourceLinkDocuments = JsonConvert.DeserializeObject>(jObject.ToString());
@@ -349,7 +355,7 @@ private void CalculateCoverage()
List<(int docIndex, int line)> zeroHitsLines = new List<(int docIndex, int line)>();
var documentsList = result.Documents.Values.ToList();
- using (var fs = _fileSystem.NewFileStream(result.HitsFilePath, FileMode.Open))
+ using (var fs = fileSystem.NewFileStream(result.HitsFilePath, FileMode.Open))
using (var br = new BinaryReader(fs))
{
int hitCandidatesCount = br.ReadInt32();
@@ -396,12 +402,14 @@ private void CalculateCoverage()
}
}
- _instrumentationHelper.DeleteHitsFile(result.HitsFilePath);
- _logger.LogVerbose($"Hit file '{result.HitsFilePath}' deleted");
+ if (instrumentationHelper.DeleteHitsFile(result.HitsFilePath))
+ {
+ logger.LogVerbose($"Hit file '{result.HitsFilePath}' deleted");
+ }
}
}
- private string GetSourceLinkUrl(Dictionary sourceLinkDocuments, string document)
+ private static string GetSourceLinkUrl(Dictionary sourceLinkDocuments, string document)
{
if (sourceLinkDocuments.TryGetValue(document, out string url))
{
diff --git a/src/coverlet.core/CoverageDetails.cs b/src/coverlet.core/CoverageDetails.cs
deleted file mode 100644
index 69d20f54c..000000000
--- a/src/coverlet.core/CoverageDetails.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using System;
-
-namespace Coverlet.Core
-{
- internal class CoverageDetails
- {
- private double _averageModulePercent;
- public double Covered { get; internal set; }
- public int Total { get; internal set; }
- public double AverageModulePercent
- {
- get { return Math.Floor(_averageModulePercent * 100) / 100; }
- internal set { _averageModulePercent = value; }
- }
-
- public double Percent => Total == 0 ? 100D : Math.Floor((Covered / Total) * 10000) / 100;
- }
-}
\ No newline at end of file
diff --git a/src/coverlet.core/CoverageResult.cs b/src/coverlet.core/CoverageResult.cs
deleted file mode 100644
index 470bb6c6a..000000000
--- a/src/coverlet.core/CoverageResult.cs
+++ /dev/null
@@ -1,202 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using Coverlet.Core.Enums;
-using Coverlet.Core.Instrumentation;
-using Coverlet.Core.Symbols;
-
-namespace Coverlet.Core
-{
- internal class BranchInfo
- {
- public int Line { get; set; }
- public int Offset { get; set; }
- public int EndOffset { get; set; }
- public int Path { get; set; }
- public uint Ordinal { get; set; }
- public int Hits { get; set; }
- }
-
- internal class Lines : SortedDictionary { }
-
- internal class Branches : List { }
-
- internal class Method
- {
- internal Method()
- {
- Lines = new Lines();
- Branches = new Branches();
- }
- public Lines Lines;
- public Branches Branches;
- }
- internal class Methods : Dictionary { }
- internal class Classes : Dictionary { }
- internal class Documents : Dictionary { }
- internal class Modules : Dictionary { }
-
- internal class CoverageResult
- {
- public string Identifier;
- public Modules Modules;
- public bool UseSourceLink;
- internal List InstrumentedResults;
-
- internal CoverageResult() { }
-
- internal void Merge(Modules modules)
- {
- foreach (var module in modules)
- {
- if (!this.Modules.Keys.Contains(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;
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
-
- public ThresholdTypeFlags GetThresholdTypesBelowThreshold(CoverageSummary summary, double threshold, ThresholdTypeFlags thresholdTypes, ThresholdStatistic thresholdStat)
- {
- var thresholdTypeFlags = ThresholdTypeFlags.None;
- switch (thresholdStat)
- {
- case ThresholdStatistic.Minimum:
- {
- foreach (var module in Modules)
- {
- double line = summary.CalculateLineCoverage(module.Value).Percent;
- double branch = summary.CalculateBranchCoverage(module.Value).Percent;
- double method = summary.CalculateMethodCoverage(module.Value).Percent;
-
- if ((thresholdTypes & ThresholdTypeFlags.Line) != ThresholdTypeFlags.None)
- {
- if (line < threshold)
- thresholdTypeFlags |= ThresholdTypeFlags.Line;
- }
-
- if ((thresholdTypes & ThresholdTypeFlags.Branch) != ThresholdTypeFlags.None)
- {
- if (branch < threshold)
- thresholdTypeFlags |= ThresholdTypeFlags.Branch;
- }
-
- if ((thresholdTypes & ThresholdTypeFlags.Method) != ThresholdTypeFlags.None)
- {
- if (method < threshold)
- thresholdTypeFlags |= ThresholdTypeFlags.Method;
- }
- }
- }
- break;
- case ThresholdStatistic.Average:
- {
- double line = summary.CalculateLineCoverage(Modules).AverageModulePercent;
- double branch = summary.CalculateBranchCoverage(Modules).AverageModulePercent;
- double method = summary.CalculateMethodCoverage(Modules).AverageModulePercent;
-
- if ((thresholdTypes & ThresholdTypeFlags.Line) != ThresholdTypeFlags.None)
- {
- if (line < threshold)
- thresholdTypeFlags |= ThresholdTypeFlags.Line;
- }
-
- if ((thresholdTypes & ThresholdTypeFlags.Branch) != ThresholdTypeFlags.None)
- {
- if (branch < threshold)
- thresholdTypeFlags |= ThresholdTypeFlags.Branch;
- }
-
- if ((thresholdTypes & ThresholdTypeFlags.Method) != ThresholdTypeFlags.None)
- {
- if (method < threshold)
- thresholdTypeFlags |= ThresholdTypeFlags.Method;
- }
- }
- break;
- case ThresholdStatistic.Total:
- {
- double line = summary.CalculateLineCoverage(Modules).Percent;
- double branch = summary.CalculateBranchCoverage(Modules).Percent;
- double method = summary.CalculateMethodCoverage(Modules).Percent;
-
- if ((thresholdTypes & ThresholdTypeFlags.Line) != ThresholdTypeFlags.None)
- {
- if (line < threshold)
- thresholdTypeFlags |= ThresholdTypeFlags.Line;
- }
-
- if ((thresholdTypes & ThresholdTypeFlags.Branch) != ThresholdTypeFlags.None)
- {
- if (branch < threshold)
- thresholdTypeFlags |= ThresholdTypeFlags.Branch;
- }
-
- if ((thresholdTypes & ThresholdTypeFlags.Method) != ThresholdTypeFlags.None)
- {
- if (method < threshold)
- thresholdTypeFlags |= ThresholdTypeFlags.Method;
- }
- }
- break;
- }
-
- return thresholdTypeFlags;
- }
- }
-}
\ No newline at end of file
diff --git a/src/coverlet.core/CoverageSummary.cs b/src/coverlet.core/CoverageSummary.cs
deleted file mode 100644
index 5a64e0b93..000000000
--- a/src/coverlet.core/CoverageSummary.cs
+++ /dev/null
@@ -1,227 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace Coverlet.Core
-{
- internal class CoverageSummary
- {
- public CoverageDetails CalculateLineCoverage(Lines lines)
- {
- var details = new CoverageDetails();
- details.Covered = lines.Where(l => l.Value > 0).Count();
- details.Total = lines.Count;
- return details;
- }
-
- public CoverageDetails CalculateLineCoverage(Methods methods)
- {
- var details = new CoverageDetails();
- foreach (var method in methods)
- {
- var methodCoverage = CalculateLineCoverage(method.Value.Lines);
- details.Covered += methodCoverage.Covered;
- details.Total += methodCoverage.Total;
- }
- return details;
- }
-
- public CoverageDetails CalculateLineCoverage(Classes classes)
- {
- var details = new CoverageDetails();
- foreach (var @class in classes)
- {
- var classCoverage = CalculateLineCoverage(@class.Value);
- details.Covered += classCoverage.Covered;
- details.Total += classCoverage.Total;
- }
- return details;
- }
-
- public CoverageDetails CalculateLineCoverage(Documents documents)
- {
- var details = new CoverageDetails();
- foreach (var document in documents)
- {
- var documentCoverage = CalculateLineCoverage(document.Value);
- details.Covered += documentCoverage.Covered;
- details.Total += documentCoverage.Total;
- }
- return details;
- }
-
- public CoverageDetails CalculateLineCoverage(Modules modules)
- {
- var details = new CoverageDetails();
- var accumPercent = 0.0D;
- foreach (var module in modules)
- {
- var moduleCoverage = CalculateLineCoverage(module.Value);
- details.Covered += moduleCoverage.Covered;
- details.Total += moduleCoverage.Total;
- accumPercent += moduleCoverage.Percent;
- }
- details.AverageModulePercent = accumPercent / modules.Count;
- return details;
- }
-
- public CoverageDetails CalculateBranchCoverage(IList branches)
- {
- var details = new CoverageDetails();
- details.Covered = branches.Count(bi => bi.Hits > 0);
- details.Total = branches.Count;
- return details;
- }
-
- public int CalculateCyclomaticComplexity(IList branches)
- {
- return Math.Max(1, branches.Count);
- }
-
- public int CalculateCyclomaticComplexity(Methods methods)
- {
- return methods.Values.Select(m => CalculateCyclomaticComplexity(m.Branches)).Sum();
- }
-
- public int CalculateMaxCyclomaticComplexity(Methods methods)
- {
- return methods.Values.Select(m => CalculateCyclomaticComplexity(m.Branches)).DefaultIfEmpty(1).Max();
- }
-
- public int CalculateMinCyclomaticComplexity(Methods methods)
- {
- return methods.Values.Select(m => CalculateCyclomaticComplexity(m.Branches)).DefaultIfEmpty(1).Min();
- }
-
- public int CalculateCyclomaticComplexity(Modules modules)
- {
- return modules.Values.Select(CalculateCyclomaticComplexity).Sum();
- }
-
- public int CalculateMaxCyclomaticComplexity(Modules modules)
- {
- return modules.Values.Select(CalculateCyclomaticComplexity).DefaultIfEmpty(1).Max();
- }
-
- public int CalculateMinCyclomaticComplexity(Modules modules)
- {
- return modules.Values.Select(CalculateCyclomaticComplexity).DefaultIfEmpty(1).Min();
- }
-
- public int CalculateCyclomaticComplexity(Documents documents)
- {
- return documents.Values.SelectMany(c => c.Values.Select(CalculateCyclomaticComplexity)).Sum();
- }
-
- public CoverageDetails CalculateBranchCoverage(Methods methods)
- {
- var details = new CoverageDetails();
- foreach (var method in methods)
- {
- var methodCoverage = CalculateBranchCoverage(method.Value.Branches);
- details.Covered += methodCoverage.Covered;
- details.Total += methodCoverage.Total;
- }
- return details;
- }
-
- public CoverageDetails CalculateBranchCoverage(Classes classes)
- {
- var details = new CoverageDetails();
- foreach (var @class in classes)
- {
- var classCoverage = CalculateBranchCoverage(@class.Value);
- details.Covered += classCoverage.Covered;
- details.Total += classCoverage.Total;
- }
- return details;
- }
-
- public CoverageDetails CalculateBranchCoverage(Documents documents)
- {
- var details = new CoverageDetails();
- foreach (var document in documents)
- {
- var documentCoverage = CalculateBranchCoverage(document.Value);
- details.Covered += documentCoverage.Covered;
- details.Total += documentCoverage.Total;
- }
- return details;
- }
-
- public CoverageDetails CalculateBranchCoverage(Modules modules)
- {
- var details = new CoverageDetails();
- var accumPercent = 0.0D;
- foreach (var module in modules)
- {
- var moduleCoverage = CalculateBranchCoverage(module.Value);
- details.Covered += moduleCoverage.Covered;
- details.Total += moduleCoverage.Total;
- accumPercent += moduleCoverage.Percent;
- }
- details.AverageModulePercent = accumPercent / modules.Count;
- return details;
- }
-
- public CoverageDetails CalculateMethodCoverage(Lines lines)
- {
- var details = new CoverageDetails();
- details.Covered = lines.Any(l => l.Value > 0) ? 1 : 0;
- details.Total = 1;
- return details;
- }
-
- public CoverageDetails CalculateMethodCoverage(Methods methods)
- {
- var details = new CoverageDetails();
- var methodsWithLines = methods.Where(m => m.Value.Lines.Count > 0);
- foreach (var method in methodsWithLines)
- {
- var methodCoverage = CalculateMethodCoverage(method.Value.Lines);
- details.Covered += methodCoverage.Covered;
- }
- details.Total = methodsWithLines.Count();
- return details;
- }
-
- public CoverageDetails CalculateMethodCoverage(Classes classes)
- {
- var details = new CoverageDetails();
- foreach (var @class in classes)
- {
- var classCoverage = CalculateMethodCoverage(@class.Value);
- details.Covered += classCoverage.Covered;
- details.Total += classCoverage.Total;
- }
- return details;
- }
-
- public CoverageDetails CalculateMethodCoverage(Documents documents)
- {
- var details = new CoverageDetails();
- foreach (var document in documents)
- {
- var documentCoverage = CalculateMethodCoverage(document.Value);
- details.Covered += documentCoverage.Covered;
- details.Total += documentCoverage.Total;
- }
- return details;
- }
-
- public CoverageDetails CalculateMethodCoverage(Modules modules)
- {
- var details = new CoverageDetails();
- var accumPercent = 0.0D;
- foreach (var module in modules)
- {
- var moduleCoverage = CalculateMethodCoverage(module.Value);
- details.Covered += moduleCoverage.Covered;
- details.Total += moduleCoverage.Total;
- accumPercent += moduleCoverage.Percent;
- }
- details.AverageModulePercent = accumPercent / modules.Count;
- return details;
- }
- }
-}
\ No newline at end of file
diff --git a/src/coverlet.core/Enums/ThresholdStatistic.cs b/src/coverlet.core/Enums/ThresholdStatistic.cs
index 9b7dd18ba..1bafe3dd8 100644
--- a/src/coverlet.core/Enums/ThresholdStatistic.cs
+++ b/src/coverlet.core/Enums/ThresholdStatistic.cs
@@ -1,6 +1,6 @@
namespace Coverlet.Core.Enums
{
- internal enum ThresholdStatistic
+ public enum ThresholdStatistic
{
Minimum,
Average,
diff --git a/src/coverlet.core/Enums/ThresholdTypeFlags.cs b/src/coverlet.core/Enums/ThresholdTypeFlags.cs
index 11a082178..fbc719186 100644
--- a/src/coverlet.core/Enums/ThresholdTypeFlags.cs
+++ b/src/coverlet.core/Enums/ThresholdTypeFlags.cs
@@ -3,7 +3,7 @@
namespace Coverlet.Core.Enums
{
[Flags]
- internal enum ThresholdTypeFlags
+ public enum ThresholdTypeFlags
{
None = 0,
Line = 2,
diff --git a/src/coverlet.core/Helpers/InstrumentationHelper.cs b/src/coverlet.core/Helpers/InstrumentationHelper.cs
index 09b6a31fd..77622c4a5 100644
--- a/src/coverlet.core/Helpers/InstrumentationHelper.cs
+++ b/src/coverlet.core/Helpers/InstrumentationHelper.cs
@@ -245,12 +245,13 @@ public virtual void RestoreOriginalModules()
}
}
- public void DeleteHitsFile(string path)
+ public bool DeleteHitsFile(string path)
{
// Retry hitting the hits file - retry up to 10 times, since the file could be locked
// See: https://github.com/tonerdo/coverlet/issues/25
var retryStrategy = CreateRetryStrategy();
_retryHelper.Retry(() => _fileSystem.Delete(path), retryStrategy, 10);
+ return true;
}
public bool IsValidFilterExpression(string filter)
diff --git a/src/coverlet.core/Instrumentation/CoverageEngine.cs b/src/coverlet.core/Instrumentation/CoverageEngine.cs
new file mode 100644
index 000000000..31593c149
--- /dev/null
+++ b/src/coverlet.core/Instrumentation/CoverageEngine.cs
@@ -0,0 +1,336 @@
+#nullable enable
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+
+using Coverlet.Core.Abstracts;
+using Coverlet.Core.Helpers;
+using Coverlet.Core.ObjectModel;
+using Coverlet.Core.Reporters;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Coverlet.Core.Instrumentation
+{
+ public class BuildInCoverageEngineFactory : ICoverageEngineFactory
+ {
+ private readonly IServiceProvider _serviceProvider;
+
+ public BuildInCoverageEngineFactory()
+ {
+ _serviceProvider = GetServiceProvider();
+ }
+
+ protected virtual IServiceProvider GetServiceProvider()
+ {
+ IServiceCollection serviceCollection = new ServiceCollection();
+ serviceCollection.AddTransient();
+ serviceCollection.AddTransient();
+ serviceCollection.AddTransient();
+ serviceCollection.AddTransient();
+ serviceCollection.AddTransient();
+ // We need to keep singleton/static semantics
+ serviceCollection.AddSingleton();
+
+ return serviceCollection.BuildServiceProvider();
+ }
+
+ public ICoverageEngine CreateEngine(InstrumentationOptions options)
+ {
+ return new CoverageEngine(options, _serviceProvider);
+ }
+
+ public IReporter CreateReporter(string format)
+ {
+ return _serviceProvider.GetService().Create(format);
+ }
+
+ public IInProcessCoverageEngine CreateInProcessEngine(Stream instrumentationResultStream)
+ {
+ return new InProcessCoverageEngine(instrumentationResultStream);
+ }
+ }
+
+ class CoverageEngine : ICoverageEngine
+ {
+ private readonly InstrumentationOptions _options;
+ private readonly IServiceProvider _serviceProvider;
+
+ public CoverageEngine(InstrumentationOptions? options, IServiceProvider? serviceProvider)
+ {
+ _options = options ?? throw new ArgumentNullException(nameof(options));
+ _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
+ }
+
+ public CoverageResult GetCoverageResult(Stream stream)
+ {
+ Coverage coverage = new Coverage(
+ CoveragePrepareResult.Deserialize(stream),
+ _serviceProvider.GetService(),
+ _serviceProvider.GetService(),
+ _serviceProvider.GetService());
+
+ return coverage.GetCoverageResult();
+ }
+
+ public Stream PrepareModules()
+ {
+ if (string.IsNullOrEmpty(_options.Module))
+ {
+ throw new ArgumentException("Module cannot be empty");
+ }
+
+ if (!File.Exists(_options.Module))
+ {
+ throw new FileNotFoundException($"Invalid module '{_options.Module}'");
+ }
+
+ Coverage coverage = new Coverage(
+ _options.Module,
+ _options.IncludeFilters,
+ _options.IncludeDirectories,
+ _options.ExcludeFilters,
+ _options.ExcludeSourceFiles,
+ _options.ExcludeAttributes,
+ _options.IncludeTestAssembly,
+ _options.SingleHit,
+ _options.MergeWith,
+ _options.UseSourceLink,
+ _serviceProvider.GetService(),
+ _serviceProvider.GetService(),
+ _serviceProvider.GetService());
+
+ CoveragePrepareResult result = coverage.PrepareModules();
+
+ if (result.Results.Length == 0)
+ {
+ throw new InvalidOperationException("No module instrumented");
+ }
+
+ return CoveragePrepareResult.Serialize(result);
+ }
+ }
+
+ class InProcessCoverageEngine : IInProcessCoverageEngine
+ {
+ private readonly Stream _instrumentationResultStream;
+
+ public InProcessCoverageEngine(Stream instrumentationResultStream) => _instrumentationResultStream = instrumentationResultStream;
+
+ public Assembly[] GetInstrumentedAssemblies()
+ {
+ HashSet instrumentedAssemblies = new HashSet();
+ foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
+ {
+ foreach (Type type in assembly.GetTypes())
+ {
+ if (type.Namespace == "Coverlet.Core.Instrumentation.Tracker"
+ && type.Name.StartsWith(assembly.GetName().Name + "_"))
+ {
+ instrumentedAssemblies.Add(assembly);
+ }
+ }
+ }
+ return instrumentedAssemblies.ToArray();
+ }
+
+ public CoverageResult? ReadCurrentCoverage()
+ {
+ CoveragePrepareResult prepareResult = CoveragePrepareResult.Deserialize(_instrumentationResultStream);
+ _instrumentationResultStream.Seek(0, SeekOrigin.Begin);
+
+ Dictionary asmList = new Dictionary();
+ foreach (Assembly asm in GetInstrumentedAssemblies())
+ {
+ asmList.Add(Path.GetFileNameWithoutExtension(asm.ManifestModule.ScopeName), asm);
+ }
+
+ return Coverage.GetCoverageResult(
+ prepareResult.Results,
+ prepareResult.UseSourceLink,
+ new ConsoleLogger(),
+ new InProcessFileSystem(prepareResult, asmList),
+ new InProcessInstrumentationHelper(),
+ prepareResult.Identifier,
+ prepareResult.MergeWith);
+ }
+
+ // InProcess custom services
+
+ class InProcessFileSystem : IFileSystem
+ {
+ private readonly Dictionary _asmList;
+ private readonly CoveragePrepareResult _coveragePrepareResult;
+
+
+ public InProcessFileSystem(CoveragePrepareResult coveragePrepareResult, Dictionary asmList) => (_asmList, _coveragePrepareResult) = (asmList, coveragePrepareResult);
+
+ public void Copy(string sourceFileName, string destFileName, bool overwrite)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Delete(string path)
+ {
+ throw new NotImplementedException();
+ }
+
+ public bool Exists(string path)
+ {
+ // File does't exists until end of process or until flush
+ return true;
+ }
+
+ public Stream NewFileStream(string path, FileMode mode)
+ {
+ foreach (InstrumenterResult result in _coveragePrepareResult.Results)
+ {
+ if (result.HitsFilePath == path)
+ {
+ Assembly asm = _asmList[result.Module];
+ foreach (Type type in asm.GetTypes())
+ {
+ if (type.Namespace == "Coverlet.Core.Instrumentation.Tracker"
+ && type.Name.StartsWith(asm.GetName().Name + "_"))
+ {
+ int[] hitsArray = (int[])type.GetField("HitsArray").GetValue(null);
+ MemoryStream ms = new MemoryStream();
+ using BinaryWriter bw = new BinaryWriter(ms, Encoding.UTF8, true);
+ bw.Write(hitsArray.Length);
+ for (int i = 0; i < hitsArray.Length; i++)
+ {
+ bw.Write(hitsArray[i]);
+ }
+ ms.Seek(0, SeekOrigin.Begin);
+ return ms;
+ }
+ }
+ }
+ }
+ throw new InvalidOperationException($"Hits file '{path}' not found in results list");
+ }
+
+ public Stream NewFileStream(string path, FileMode mode, FileAccess access)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Stream OpenRead(string path)
+ {
+ throw new NotImplementedException();
+ }
+
+ public string ReadAllText(string path)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void WriteAllText(string path, string contents)
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ class InProcessInstrumentationHelper : IInstrumentationHelper
+ {
+ public void BackupOriginalModule(string module, string identifier)
+ {
+ throw new NotImplementedException();
+ }
+
+ public bool DeleteHitsFile(string path)
+ {
+ // In process we don't remove files
+ return false;
+ }
+
+ public bool EmbeddedPortablePdbHasLocalSource(string module, out string firstNotFoundDocument)
+ {
+ throw new NotImplementedException();
+ }
+
+ public string[] GetCoverableModules(string module, string[] directories, bool includeTestAssembly)
+ {
+ throw new NotImplementedException();
+ }
+
+ public bool HasPdb(string module, out bool embedded)
+ {
+ throw new NotImplementedException();
+ }
+
+ public bool IsLocalMethod(string method)
+ {
+ throw new NotImplementedException();
+ }
+
+ public bool IsModuleExcluded(string module, string[] excludeFilters)
+ {
+ throw new NotImplementedException();
+ }
+
+ public bool IsModuleIncluded(string module, string[] includeFilters)
+ {
+ throw new NotImplementedException();
+ }
+
+ public bool IsTypeExcluded(string module, string type, string[] excludeFilters)
+ {
+ throw new NotImplementedException();
+ }
+
+ public bool IsTypeIncluded(string module, string type, string[] includeFilters)
+ {
+ throw new NotImplementedException();
+ }
+
+ public bool IsValidFilterExpression(string filter)
+ {
+ throw new NotImplementedException();
+ }
+
+ public bool PortablePdbHasLocalSource(string module, out string firstNotFoundDocument)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void RestoreOriginalModule(string module, string identifier)
+ {
+ // In process we don't restore nothing
+ }
+ }
+ }
+
+ // Plain vanilla console logger
+ class ConsoleLogger : ILogger
+ {
+ public void LogError(string message)
+ {
+ Console.WriteLine($"[Error] {message}");
+ }
+
+ public void LogError(Exception exception)
+ {
+ Console.WriteLine($"[Error] {exception.ToString()}");
+ }
+
+ public void LogInformation(string message, bool important = false)
+ {
+ Console.WriteLine($"[LogInformation] {message}");
+ }
+
+ public void LogVerbose(string message)
+ {
+ Console.WriteLine($"[LogVerbose] {message}");
+ }
+
+ public void LogWarning(string message)
+ {
+ Console.WriteLine($"[LogWarning] {message}");
+ }
+ }
+}
+
diff --git a/src/coverlet.core/ObjectModel.cs b/src/coverlet.core/ObjectModel.cs
new file mode 100644
index 000000000..aeed0eda8
--- /dev/null
+++ b/src/coverlet.core/ObjectModel.cs
@@ -0,0 +1,436 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+using Coverlet.Core.Enums;
+using Coverlet.Core.Instrumentation;
+
+namespace Coverlet.Core.ObjectModel
+{
+ public class BranchInfo
+ {
+ public int Line { get; set; }
+ public int Offset { get; set; }
+ public int EndOffset { get; set; }
+ public int Path { get; set; }
+ public uint Ordinal { get; set; }
+ public int Hits { get; set; }
+ }
+
+ public class Lines : SortedDictionary { }
+
+ public class Branches : List { }
+
+ public class Method
+ {
+ internal Method()
+ {
+ Lines = new Lines();
+ Branches = new Branches();
+ }
+ public Lines Lines;
+ public Branches Branches;
+ }
+ public class Methods : Dictionary { }
+ public class Classes : Dictionary { }
+ public class Documents : Dictionary { }
+ public class Modules : Dictionary { }
+
+ public class CoverageResult
+ {
+ public string Identifier;
+ public Modules Modules;
+ public bool UseSourceLink;
+ internal List InstrumentedResults;
+
+ internal CoverageResult() { }
+
+ internal void Merge(Modules modules)
+ {
+ foreach (var module in modules)
+ {
+ if (!this.Modules.Keys.Contains(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;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public ThresholdTypeFlags GetThresholdTypesBelowThreshold(CoverageSummary summary, double threshold, ThresholdTypeFlags thresholdTypes, ThresholdStatistic thresholdStat)
+ {
+ var thresholdTypeFlags = ThresholdTypeFlags.None;
+ switch (thresholdStat)
+ {
+ case ThresholdStatistic.Minimum:
+ {
+ foreach (var module in Modules)
+ {
+ double line = summary.CalculateLineCoverage(module.Value).Percent;
+ double branch = summary.CalculateBranchCoverage(module.Value).Percent;
+ double method = summary.CalculateMethodCoverage(module.Value).Percent;
+
+ if ((thresholdTypes & ThresholdTypeFlags.Line) != ThresholdTypeFlags.None)
+ {
+ if (line < threshold)
+ thresholdTypeFlags |= ThresholdTypeFlags.Line;
+ }
+
+ if ((thresholdTypes & ThresholdTypeFlags.Branch) != ThresholdTypeFlags.None)
+ {
+ if (branch < threshold)
+ thresholdTypeFlags |= ThresholdTypeFlags.Branch;
+ }
+
+ if ((thresholdTypes & ThresholdTypeFlags.Method) != ThresholdTypeFlags.None)
+ {
+ if (method < threshold)
+ thresholdTypeFlags |= ThresholdTypeFlags.Method;
+ }
+ }
+ }
+ break;
+ case ThresholdStatistic.Average:
+ {
+ double line = summary.CalculateLineCoverage(Modules).AverageModulePercent;
+ double branch = summary.CalculateBranchCoverage(Modules).AverageModulePercent;
+ double method = summary.CalculateMethodCoverage(Modules).AverageModulePercent;
+
+ if ((thresholdTypes & ThresholdTypeFlags.Line) != ThresholdTypeFlags.None)
+ {
+ if (line < threshold)
+ thresholdTypeFlags |= ThresholdTypeFlags.Line;
+ }
+
+ if ((thresholdTypes & ThresholdTypeFlags.Branch) != ThresholdTypeFlags.None)
+ {
+ if (branch < threshold)
+ thresholdTypeFlags |= ThresholdTypeFlags.Branch;
+ }
+
+ if ((thresholdTypes & ThresholdTypeFlags.Method) != ThresholdTypeFlags.None)
+ {
+ if (method < threshold)
+ thresholdTypeFlags |= ThresholdTypeFlags.Method;
+ }
+ }
+ break;
+ case ThresholdStatistic.Total:
+ {
+ double line = summary.CalculateLineCoverage(Modules).Percent;
+ double branch = summary.CalculateBranchCoverage(Modules).Percent;
+ double method = summary.CalculateMethodCoverage(Modules).Percent;
+
+ if ((thresholdTypes & ThresholdTypeFlags.Line) != ThresholdTypeFlags.None)
+ {
+ if (line < threshold)
+ thresholdTypeFlags |= ThresholdTypeFlags.Line;
+ }
+
+ if ((thresholdTypes & ThresholdTypeFlags.Branch) != ThresholdTypeFlags.None)
+ {
+ if (branch < threshold)
+ thresholdTypeFlags |= ThresholdTypeFlags.Branch;
+ }
+
+ if ((thresholdTypes & ThresholdTypeFlags.Method) != ThresholdTypeFlags.None)
+ {
+ if (method < threshold)
+ thresholdTypeFlags |= ThresholdTypeFlags.Method;
+ }
+ }
+ break;
+ }
+
+ return thresholdTypeFlags;
+ }
+ }
+
+ public class CoverageSummary
+ {
+ public CoverageDetails CalculateLineCoverage(Lines lines)
+ {
+ var details = new CoverageDetails();
+ details.Covered = lines.Where(l => l.Value > 0).Count();
+ details.Total = lines.Count;
+ return details;
+ }
+
+ public CoverageDetails CalculateLineCoverage(Methods methods)
+ {
+ var details = new CoverageDetails();
+ foreach (var method in methods)
+ {
+ var methodCoverage = CalculateLineCoverage(method.Value.Lines);
+ details.Covered += methodCoverage.Covered;
+ details.Total += methodCoverage.Total;
+ }
+ return details;
+ }
+
+ public CoverageDetails CalculateLineCoverage(Classes classes)
+ {
+ var details = new CoverageDetails();
+ foreach (var @class in classes)
+ {
+ var classCoverage = CalculateLineCoverage(@class.Value);
+ details.Covered += classCoverage.Covered;
+ details.Total += classCoverage.Total;
+ }
+ return details;
+ }
+
+ public CoverageDetails CalculateLineCoverage(Documents documents)
+ {
+ var details = new CoverageDetails();
+ foreach (var document in documents)
+ {
+ var documentCoverage = CalculateLineCoverage(document.Value);
+ details.Covered += documentCoverage.Covered;
+ details.Total += documentCoverage.Total;
+ }
+ return details;
+ }
+
+ public CoverageDetails CalculateLineCoverage(Modules modules)
+ {
+ var details = new CoverageDetails();
+ var accumPercent = 0.0D;
+ foreach (var module in modules)
+ {
+ var moduleCoverage = CalculateLineCoverage(module.Value);
+ details.Covered += moduleCoverage.Covered;
+ details.Total += moduleCoverage.Total;
+ accumPercent += moduleCoverage.Percent;
+ }
+ details.AverageModulePercent = accumPercent / modules.Count;
+ return details;
+ }
+
+ public CoverageDetails CalculateBranchCoverage(IList branches)
+ {
+ var details = new CoverageDetails();
+ details.Covered = branches.Count(bi => bi.Hits > 0);
+ details.Total = branches.Count;
+ return details;
+ }
+
+ public int CalculateCyclomaticComplexity(IList branches)
+ {
+ return Math.Max(1, branches.Count);
+ }
+
+ public int CalculateCyclomaticComplexity(Methods methods)
+ {
+ return methods.Values.Select(m => CalculateCyclomaticComplexity(m.Branches)).Sum();
+ }
+
+ public int CalculateMaxCyclomaticComplexity(Methods methods)
+ {
+ return methods.Values.Select(m => CalculateCyclomaticComplexity(m.Branches)).DefaultIfEmpty(1).Max();
+ }
+
+ public int CalculateMinCyclomaticComplexity(Methods methods)
+ {
+ return methods.Values.Select(m => CalculateCyclomaticComplexity(m.Branches)).DefaultIfEmpty(1).Min();
+ }
+
+ public int CalculateCyclomaticComplexity(Modules modules)
+ {
+ return modules.Values.Select(CalculateCyclomaticComplexity).Sum();
+ }
+
+ public int CalculateMaxCyclomaticComplexity(Modules modules)
+ {
+ return modules.Values.Select(CalculateCyclomaticComplexity).DefaultIfEmpty(1).Max();
+ }
+
+ public int CalculateMinCyclomaticComplexity(Modules modules)
+ {
+ return modules.Values.Select(CalculateCyclomaticComplexity).DefaultIfEmpty(1).Min();
+ }
+
+ public int CalculateCyclomaticComplexity(Documents documents)
+ {
+ return documents.Values.SelectMany(c => c.Values.Select(CalculateCyclomaticComplexity)).Sum();
+ }
+
+ public CoverageDetails CalculateBranchCoverage(Methods methods)
+ {
+ var details = new CoverageDetails();
+ foreach (var method in methods)
+ {
+ var methodCoverage = CalculateBranchCoverage(method.Value.Branches);
+ details.Covered += methodCoverage.Covered;
+ details.Total += methodCoverage.Total;
+ }
+ return details;
+ }
+
+ public CoverageDetails CalculateBranchCoverage(Classes classes)
+ {
+ var details = new CoverageDetails();
+ foreach (var @class in classes)
+ {
+ var classCoverage = CalculateBranchCoverage(@class.Value);
+ details.Covered += classCoverage.Covered;
+ details.Total += classCoverage.Total;
+ }
+ return details;
+ }
+
+ public CoverageDetails CalculateBranchCoverage(Documents documents)
+ {
+ var details = new CoverageDetails();
+ foreach (var document in documents)
+ {
+ var documentCoverage = CalculateBranchCoverage(document.Value);
+ details.Covered += documentCoverage.Covered;
+ details.Total += documentCoverage.Total;
+ }
+ return details;
+ }
+
+ public CoverageDetails CalculateBranchCoverage(Modules modules)
+ {
+ var details = new CoverageDetails();
+ var accumPercent = 0.0D;
+ foreach (var module in modules)
+ {
+ var moduleCoverage = CalculateBranchCoverage(module.Value);
+ details.Covered += moduleCoverage.Covered;
+ details.Total += moduleCoverage.Total;
+ accumPercent += moduleCoverage.Percent;
+ }
+ details.AverageModulePercent = accumPercent / modules.Count;
+ return details;
+ }
+
+ public CoverageDetails CalculateMethodCoverage(Lines lines)
+ {
+ var details = new CoverageDetails();
+ details.Covered = lines.Any(l => l.Value > 0) ? 1 : 0;
+ details.Total = 1;
+ return details;
+ }
+
+ public CoverageDetails CalculateMethodCoverage(Methods methods)
+ {
+ var details = new CoverageDetails();
+ var methodsWithLines = methods.Where(m => m.Value.Lines.Count > 0);
+ foreach (var method in methodsWithLines)
+ {
+ var methodCoverage = CalculateMethodCoverage(method.Value.Lines);
+ details.Covered += methodCoverage.Covered;
+ }
+ details.Total = methodsWithLines.Count();
+ return details;
+ }
+
+ public CoverageDetails CalculateMethodCoverage(Classes classes)
+ {
+ var details = new CoverageDetails();
+ foreach (var @class in classes)
+ {
+ var classCoverage = CalculateMethodCoverage(@class.Value);
+ details.Covered += classCoverage.Covered;
+ details.Total += classCoverage.Total;
+ }
+ return details;
+ }
+
+ public CoverageDetails CalculateMethodCoverage(Documents documents)
+ {
+ var details = new CoverageDetails();
+ foreach (var document in documents)
+ {
+ var documentCoverage = CalculateMethodCoverage(document.Value);
+ details.Covered += documentCoverage.Covered;
+ details.Total += documentCoverage.Total;
+ }
+ return details;
+ }
+
+ public CoverageDetails CalculateMethodCoverage(Modules modules)
+ {
+ var details = new CoverageDetails();
+ var accumPercent = 0.0D;
+ foreach (var module in modules)
+ {
+ var moduleCoverage = CalculateMethodCoverage(module.Value);
+ details.Covered += moduleCoverage.Covered;
+ details.Total += moduleCoverage.Total;
+ accumPercent += moduleCoverage.Percent;
+ }
+ details.AverageModulePercent = accumPercent / modules.Count;
+ return details;
+ }
+ }
+
+ public class CoverageDetails
+ {
+ private double _averageModulePercent;
+ public double Covered { get; internal set; }
+ public int Total { get; internal set; }
+ public double AverageModulePercent
+ {
+ get { return Math.Floor(_averageModulePercent * 100) / 100; }
+ internal set { _averageModulePercent = value; }
+ }
+
+ public double Percent => Total == 0 ? 100D : Math.Floor((Covered / Total) * 10000) / 100;
+ }
+}
\ No newline at end of file
diff --git a/src/coverlet.core/Reporters/CoberturaReporter.cs b/src/coverlet.core/Reporters/CoberturaReporter.cs
index d3c11f956..3528888e1 100644
--- a/src/coverlet.core/Reporters/CoberturaReporter.cs
+++ b/src/coverlet.core/Reporters/CoberturaReporter.cs
@@ -7,6 +7,9 @@
using System.Text;
using System.Xml.Linq;
+using Coverlet.Core.Abstracts;
+using Coverlet.Core.ObjectModel;
+
namespace Coverlet.Core.Reporters
{
internal class CoberturaReporter : IReporter
diff --git a/src/coverlet.core/Reporters/IReporter.cs b/src/coverlet.core/Reporters/IReporter.cs
deleted file mode 100644
index ab3e8f99d..000000000
--- a/src/coverlet.core/Reporters/IReporter.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-namespace Coverlet.Core.Reporters
-{
- internal interface IReporter
- {
- ReporterOutputType OutputType { get; }
- string Format { get; }
- string Extension { get; }
- string Report(CoverageResult result);
- }
-
- internal enum ReporterOutputType
- {
- File,
- Console,
- }
-}
\ No newline at end of file
diff --git a/src/coverlet.core/Reporters/JsonReporter.cs b/src/coverlet.core/Reporters/JsonReporter.cs
index 09e23e83f..dc27c2648 100644
--- a/src/coverlet.core/Reporters/JsonReporter.cs
+++ b/src/coverlet.core/Reporters/JsonReporter.cs
@@ -1,3 +1,5 @@
+using Coverlet.Core.Abstracts;
+using Coverlet.Core.ObjectModel;
using Newtonsoft.Json;
namespace Coverlet.Core.Reporters
diff --git a/src/coverlet.core/Reporters/LcovReporter.cs b/src/coverlet.core/Reporters/LcovReporter.cs
index e8e94b68d..576a7b15e 100644
--- a/src/coverlet.core/Reporters/LcovReporter.cs
+++ b/src/coverlet.core/Reporters/LcovReporter.cs
@@ -2,6 +2,9 @@
using System.Linq;
using System.Collections.Generic;
+using Coverlet.Core.Abstracts;
+using Coverlet.Core.ObjectModel;
+
namespace Coverlet.Core.Reporters
{
internal class LcovReporter : IReporter
diff --git a/src/coverlet.core/Reporters/OpenCoverReporter.cs b/src/coverlet.core/Reporters/OpenCoverReporter.cs
index 4a5c61005..ebde70dcc 100644
--- a/src/coverlet.core/Reporters/OpenCoverReporter.cs
+++ b/src/coverlet.core/Reporters/OpenCoverReporter.cs
@@ -1,11 +1,13 @@
using System;
-using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Linq;
+using Coverlet.Core.Abstracts;
+using Coverlet.Core.ObjectModel;
+
namespace Coverlet.Core.Reporters
{
internal class OpenCoverReporter : IReporter
diff --git a/src/coverlet.core/Reporters/ReporterFactory.cs b/src/coverlet.core/Reporters/ReporterFactory.cs
index 7ac72337b..4d2f97b3c 100644
--- a/src/coverlet.core/Reporters/ReporterFactory.cs
+++ b/src/coverlet.core/Reporters/ReporterFactory.cs
@@ -1,16 +1,26 @@
using System;
using System.Linq;
+using Coverlet.Core.Abstracts;
+
namespace Coverlet.Core.Reporters
{
- internal class ReporterFactory
+ public static class ReporterTypes
+ {
+ public readonly static string Cobertura = "cobertura";
+ public readonly static string Json = "json";
+ public readonly static string Lcov = "lcov";
+ public readonly static string OpenCover = "opencover";
+ public readonly static string TeamCity = "teamcity";
+ }
+
+ internal class ReporterFactory : IReporterFactory
{
private string _format;
private IReporter[] _reporters;
- public ReporterFactory(string format)
+ public ReporterFactory()
{
- _format = format;
_reporters = new IReporter[] {
new JsonReporter(), new LcovReporter(),
new OpenCoverReporter(), new CoberturaReporter(),
@@ -18,6 +28,11 @@ public ReporterFactory(string format)
};
}
+ public ReporterFactory(string format) : this()
+ {
+ _format = format;
+ }
+
public bool IsValidFormat()
{
return CreateReporter() != null;
@@ -25,5 +40,15 @@ public bool IsValidFormat()
public IReporter CreateReporter()
=> _reporters.FirstOrDefault(r => string.Equals(r.Format, _format, StringComparison.OrdinalIgnoreCase));
+
+ public IReporter Create(string format)
+ {
+ ReporterFactory reporterFactory = new ReporterFactory(format);
+ if (!reporterFactory.IsValidFormat())
+ {
+ throw new ArgumentException($"Format not supported '{format}'");
+ }
+ return reporterFactory.CreateReporter();
+ }
}
}
\ No newline at end of file
diff --git a/src/coverlet.core/Reporters/TeamCityReporter.cs b/src/coverlet.core/Reporters/TeamCityReporter.cs
index a12a8f96c..e62048f7d 100644
--- a/src/coverlet.core/Reporters/TeamCityReporter.cs
+++ b/src/coverlet.core/Reporters/TeamCityReporter.cs
@@ -1,8 +1,9 @@
using System.Globalization;
-using Coverlet.Core;
-using Coverlet.Core.Reporters;
using System.Text;
+using Coverlet.Core.Abstracts;
+using Coverlet.Core.ObjectModel;
+
namespace Coverlet.Core.Reporters
{
internal class TeamCityReporter : IReporter
diff --git a/src/coverlet.core/coverlet.core.csproj b/src/coverlet.core/coverlet.core.csproj
index 4add60886..ce8111607 100644
--- a/src/coverlet.core/coverlet.core.csproj
+++ b/src/coverlet.core/coverlet.core.csproj
@@ -3,9 +3,21 @@
Library
netstandard2.0
- 5.2.0
- false
+ coverlet.core
+ coverlet.core
+ coverlet.core
+ tonerdo
+ MIT
+ http://github.com/tonerdo/coverlet
+ https://raw.githubusercontent.com/tonerdo/coverlet/master/_assets/coverlet-icon.svg?sanitize=true
+ false
+ Coverlet is a cross platform code coverage library for .NET, with support for line, branch and method coverage.
+ coverage testing unit-test lcov opencover quality
+ git
+ true
+ snupkg
preview
+ 5.2.0
@@ -17,5 +29,4 @@
-
diff --git a/src/coverlet.core/version.json b/src/coverlet.core/version.json
new file mode 100644
index 000000000..80a4e476c
--- /dev/null
+++ b/src/coverlet.core/version.json
@@ -0,0 +1,7 @@
+{
+ "$schema": "https://raw.githubusercontent.com/AArnott/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json",
+ "version": "1.0",
+ "publicReleaseRefSpec": [
+ "^refs/heads/master$"
+ ]
+}
diff --git a/src/coverlet.msbuild.tasks/CoverageResultTask.cs b/src/coverlet.msbuild.tasks/CoverageResultTask.cs
index 2cf0f3691..2d1e15e97 100644
--- a/src/coverlet.msbuild.tasks/CoverageResultTask.cs
+++ b/src/coverlet.msbuild.tasks/CoverageResultTask.cs
@@ -2,11 +2,13 @@
using System.IO;
using System.Linq;
using System.Text;
+
using ConsoleTables;
using Coverlet.Core;
using Coverlet.Core.Abstracts;
using Coverlet.Core.Enums;
using Coverlet.Core.Extensions;
+using Coverlet.Core.ObjectModel;
using Coverlet.Core.Reporters;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
diff --git a/src/coverlet.msbuild.tasks/ReportWriter.cs b/src/coverlet.msbuild.tasks/ReportWriter.cs
index 346822a56..f22a3dc7a 100644
--- a/src/coverlet.msbuild.tasks/ReportWriter.cs
+++ b/src/coverlet.msbuild.tasks/ReportWriter.cs
@@ -1,7 +1,7 @@
using System.IO;
-using Coverlet.Core;
using Coverlet.Core.Abstracts;
+using Coverlet.Core.ObjectModel;
using Coverlet.Core.Reporters;
namespace Coverlet.MSbuild.Tasks
diff --git a/test/coverlet.collector.tests/CoverletCoverageDataCollectorTests.cs b/test/coverlet.collector.tests/CoverletCoverageDataCollectorTests.cs
index 8f19a95f2..87a4248ab 100644
--- a/test/coverlet.collector.tests/CoverletCoverageDataCollectorTests.cs
+++ b/test/coverlet.collector.tests/CoverletCoverageDataCollectorTests.cs
@@ -8,13 +8,13 @@
using Moq;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Coverlet.Core;
+using Coverlet.Core.Abstracts;
+using Coverlet.Core.Extensions;
+using Coverlet.Collector.DataCollection;
using Coverlet.Collector.Utilities.Interfaces;
using Coverlet.Collector.Utilities;
using Xunit;
-using Coverlet.Collector.DataCollection;
using Coverlet.Core.Reporters;
-using Coverlet.Core.Abstracts;
-using Coverlet.Core.Extensions;
namespace Coverlet.Collector.Tests
{
diff --git a/test/coverlet.core.standalone.sample/Sample.cs b/test/coverlet.core.standalone.sample/Sample.cs
new file mode 100644
index 000000000..312cbe2c7
--- /dev/null
+++ b/test/coverlet.core.standalone.sample/Sample.cs
@@ -0,0 +1,10 @@
+namespace Coverlet.Core.Standalone.Sample
+{
+ public class Calculator
+ {
+ public int Add(int a, int b)
+ {
+ return a + b;
+ }
+ }
+}
diff --git a/test/coverlet.core.standalone.sample/coverlet.core.standalone.sample.csproj b/test/coverlet.core.standalone.sample/coverlet.core.standalone.sample.csproj
new file mode 100644
index 000000000..939b54a16
--- /dev/null
+++ b/test/coverlet.core.standalone.sample/coverlet.core.standalone.sample.csproj
@@ -0,0 +1,9 @@
+
+
+
+ netcoreapp2.2
+ false
+ false
+
+
+
diff --git a/test/coverlet.core.standalone.tests/BaseTest.cs b/test/coverlet.core.standalone.tests/BaseTest.cs
new file mode 100644
index 000000000..cc5efca3a
--- /dev/null
+++ b/test/coverlet.core.standalone.tests/BaseTest.cs
@@ -0,0 +1,51 @@
+#nullable enable
+
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Reflection;
+
+namespace Coverlet.Core.Standalone.Tests
+{
+ public abstract class BaseTest
+ {
+ protected string GetLibToInstrument()
+ {
+ string moduleToInstrument = "coverlet.core.standalone.sample.dll";
+ string location = Path.GetFullPath(moduleToInstrument);
+ string newPath = Path.Combine(Path.GetDirectoryName(location), Guid.NewGuid().ToString("N"), moduleToInstrument);
+ Directory.CreateDirectory(Path.GetDirectoryName(newPath));
+ File.Copy(location, newPath);
+ File.Copy(Path.ChangeExtension(location, ".pdb"), Path.ChangeExtension(newPath, ".pdb"));
+ return newPath;
+ }
+ }
+
+ public class SampleWrapper
+ {
+ readonly dynamic? _object;
+ readonly Assembly _asm;
+
+ public SampleWrapper(string asmFile)
+ {
+ _asm = Assembly.Load(File.ReadAllBytes(asmFile));
+ foreach (Type type in _asm.GetTypes())
+ {
+ if (type.Name == "Calculator")
+ {
+ _object = Activator.CreateInstance(type);
+ }
+ }
+ }
+
+ public int Add(int a, int b)
+ {
+ if (_object is null)
+ {
+ throw new NullReferenceException("_object is null");
+ }
+
+ return _object.Add(a, b);
+ }
+ }
+}
diff --git a/test/coverlet.core.standalone.tests/StandaloneInstrumentation.cs b/test/coverlet.core.standalone.tests/StandaloneInstrumentation.cs
new file mode 100644
index 000000000..16222430f
--- /dev/null
+++ b/test/coverlet.core.standalone.tests/StandaloneInstrumentation.cs
@@ -0,0 +1,108 @@
+using System.IO;
+using System.Reflection;
+using System.Threading.Tasks;
+
+using Coverlet.Core.Abstracts;
+using Coverlet.Core.Instrumentation;
+using Coverlet.Core.ObjectModel;
+using Coverlet.Core.Reporters;
+using Coverlet.Tests.RemoteExecutor;
+using Xunit;
+
+namespace Coverlet.Core.Standalone.Tests
+{
+ public class StandaloneInstrumentation : BaseTest
+ {
+ readonly ICoverageEngineFactory _coverageFactory = new BuildInCoverageEngineFactory();
+
+ [Fact]
+ async public Task Instrument_Run_GetResult()
+ {
+ // Copy file to instrument to avoid lock
+ string fileToInstrument = GetLibToInstrument();
+
+ // Create engine
+ InstrumentationOptions options = new InstrumentationOptions();
+ options.Module = fileToInstrument;
+ options.IncludeTestAssembly = true;
+ options.IncludeFilters = new string[] { $"[{Path.GetFileNameWithoutExtension(options.Module)}*]*" };
+ ICoverageEngine coverageEngine = _coverageFactory.CreateEngine(options);
+
+ // Instrument modules
+ Stream instrumentationResult = coverageEngine.PrepareModules();
+ // Persist stream
+ string instrumentationResultPath = Path.GetTempFileName();
+ using (var fs = File.OpenWrite(instrumentationResultPath))
+ {
+ await instrumentationResult.CopyToAsync(fs);
+ }
+ instrumentationResult.Seek(0, SeekOrigin.Begin);
+
+ // Run in new process
+ RemoteExecutor.Invoke(assets =>
+ {
+ string[] assetsPath = assets.Split("|");
+ ICoverageEngineFactory coverageFactory = new BuildInCoverageEngineFactory();
+
+ // We need to pass instrumentation result to engine
+ IInProcessCoverageEngine inProcessEngine = coverageFactory.CreateInProcessEngine(File.OpenRead(assetsPath[1]));
+
+ // Use object
+ var sampleWrapper = new SampleWrapper(assetsPath[0]);
+
+ // Get app domain loaded instrumented assemblies
+ Assembly[] assemblies = inProcessEngine.GetInstrumentedAssemblies();
+ Assert.NotEmpty(assemblies);
+
+ Assert.Equal(3, sampleWrapper.Add(1, 2));
+
+ // Read current coverage state
+ CoverageResult result = inProcessEngine.ReadCurrentCoverage();
+ AssertCoverage(result, 1);
+
+ Assert.Equal(3, sampleWrapper.Add(1, 2));
+
+ // Read current coverage state
+ result = inProcessEngine.ReadCurrentCoverage();
+ AssertCoverage(result, 2);
+
+ return Task.FromResult(0);
+ }, $"{fileToInstrument}|{instrumentationResultPath}").Dispose();
+
+ // Get results
+ CoverageResult result = coverageEngine.GetCoverageResult(instrumentationResult);
+ AssertCoverage(result, 2);
+
+ // Create report
+ IReporter reporter = _coverageFactory.CreateReporter(ReporterTypes.Cobertura);
+ string reportResult = reporter.Report(result);
+ Assert.NotEmpty(reportResult);
+
+ return;
+
+ // Assertion helper
+ static void AssertCoverage(CoverageResult result, int coverageValue)
+ {
+ foreach (var module in result.Modules)
+ {
+ foreach (var document in module.Value)
+ {
+ foreach (var @class in document.Value)
+ {
+ foreach (var method in @class.Value)
+ {
+ if (method.Key == "System.Int32 Coverlet.Core.Standalone.Sample.Calculator::Add(System.Int32,System.Int32)")
+ {
+ foreach (var line in method.Value.Lines)
+ {
+ Assert.Equal(coverageValue, line.Value);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/coverlet.core.standalone.tests/coverlet.core.standalone.tests.csproj b/test/coverlet.core.standalone.tests/coverlet.core.standalone.tests.csproj
new file mode 100644
index 000000000..566e14081
--- /dev/null
+++ b/test/coverlet.core.standalone.tests/coverlet.core.standalone.tests.csproj
@@ -0,0 +1,29 @@
+
+
+
+
+ netcoreapp2.2
+ false
+ Coverlet.Core.Standalone.Tests
+ preview
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/coverlet.core.tests/Coverage/CoverageSummaryTests.cs b/test/coverlet.core.tests/Coverage/CoverageSummaryTests.cs
index 9dc9bf5a4..152b4595a 100644
--- a/test/coverlet.core.tests/Coverage/CoverageSummaryTests.cs
+++ b/test/coverlet.core.tests/Coverage/CoverageSummaryTests.cs
@@ -1,9 +1,6 @@
-using System;
-using System.Collections.Generic;
using System.Linq;
-using Coverlet.Core;
-using Moq;
+using Coverlet.Core.ObjectModel;
using Xunit;
namespace Coverlet.Core.Tests
diff --git a/test/coverlet.core.tests/Coverage/CoverageTests.cs b/test/coverlet.core.tests/Coverage/CoverageTests.cs
index 785a5c322..fb8e0d2c0 100644
--- a/test/coverlet.core.tests/Coverage/CoverageTests.cs
+++ b/test/coverlet.core.tests/Coverage/CoverageTests.cs
@@ -5,6 +5,7 @@
using Coverlet.Core.Abstracts;
using Coverlet.Core.Helpers;
+using Coverlet.Core.ObjectModel;
using Coverlet.Core.Samples.Tests;
using Coverlet.Tests.RemoteExecutor;
using Moq;
diff --git a/test/coverlet.core.tests/Coverage/InstrumenterHelper.cs b/test/coverlet.core.tests/Coverage/InstrumenterHelper.cs
index 35b4ff1d5..29ab62e8c 100644
--- a/test/coverlet.core.tests/Coverage/InstrumenterHelper.cs
+++ b/test/coverlet.core.tests/Coverage/InstrumenterHelper.cs
@@ -11,6 +11,7 @@
using Coverlet.Core.Abstracts;
using Coverlet.Core.Helpers;
using Coverlet.Core.Instrumentation;
+using Coverlet.Core.ObjectModel;
using Coverlet.Core.Reporters;
using Microsoft.Extensions.DependencyInjection;
using Moq;
diff --git a/test/coverlet.core.tests/Reporters/CoberturaReporterTests.cs b/test/coverlet.core.tests/Reporters/CoberturaReporterTests.cs
index 83e4ab057..5ae42d3b8 100644
--- a/test/coverlet.core.tests/Reporters/CoberturaReporterTests.cs
+++ b/test/coverlet.core.tests/Reporters/CoberturaReporterTests.cs
@@ -7,6 +7,8 @@
using System.Text;
using System.Threading;
using System.Xml.Linq;
+
+using Coverlet.Core.ObjectModel;
using Xunit;
namespace Coverlet.Core.Reporters.Tests
diff --git a/test/coverlet.core.tests/Reporters/JsonReporterTests.cs b/test/coverlet.core.tests/Reporters/JsonReporterTests.cs
index 2bfc2ca43..75c7e020b 100644
--- a/test/coverlet.core.tests/Reporters/JsonReporterTests.cs
+++ b/test/coverlet.core.tests/Reporters/JsonReporterTests.cs
@@ -1,4 +1,6 @@
using System;
+
+using Coverlet.Core.ObjectModel;
using Xunit;
namespace Coverlet.Core.Reporters.Tests
diff --git a/test/coverlet.core.tests/Reporters/LcovReporterTests.cs b/test/coverlet.core.tests/Reporters/LcovReporterTests.cs
index f5c888bf1..048ac7b4a 100644
--- a/test/coverlet.core.tests/Reporters/LcovReporterTests.cs
+++ b/test/coverlet.core.tests/Reporters/LcovReporterTests.cs
@@ -1,5 +1,6 @@
using System;
-using System.Collections.Generic;
+
+using Coverlet.Core.ObjectModel;
using Xunit;
namespace Coverlet.Core.Reporters.Tests
diff --git a/test/coverlet.core.tests/Reporters/OpenCoverReporterTests.cs b/test/coverlet.core.tests/Reporters/OpenCoverReporterTests.cs
index 7d3e520bd..2c0e754c8 100644
--- a/test/coverlet.core.tests/Reporters/OpenCoverReporterTests.cs
+++ b/test/coverlet.core.tests/Reporters/OpenCoverReporterTests.cs
@@ -1,9 +1,10 @@
using System;
-using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Linq;
+
+using Coverlet.Core.ObjectModel;
using Xunit;
namespace Coverlet.Core.Reporters.Tests
diff --git a/test/coverlet.core.tests/Reporters/ReporterFactoryTests.cs b/test/coverlet.core.tests/Reporters/ReporterFactoryTests.cs
index e148fa378..058ee4be3 100644
--- a/test/coverlet.core.tests/Reporters/ReporterFactoryTests.cs
+++ b/test/coverlet.core.tests/Reporters/ReporterFactoryTests.cs
@@ -1,4 +1,3 @@
-using Coverlet.Core.Reporters;
using Xunit;
namespace Coverlet.Core.Reporters.Tests
diff --git a/test/coverlet.core.tests/Reporters/Reporters.cs b/test/coverlet.core.tests/Reporters/Reporters.cs
index 6ddaf7cdd..a031eb7b0 100644
--- a/test/coverlet.core.tests/Reporters/Reporters.cs
+++ b/test/coverlet.core.tests/Reporters/Reporters.cs
@@ -1,6 +1,7 @@
using System.IO;
using Coverlet.Core.Abstracts;
+using Coverlet.Core.ObjectModel;
using Coverlet.MSbuild.Tasks;
using Moq;
using Xunit;
diff --git a/test/coverlet.core.tests/Reporters/TeamCityReporter.cs b/test/coverlet.core.tests/Reporters/TeamCityReporter.cs
index 8328dd90e..30ed99b94 100644
--- a/test/coverlet.core.tests/Reporters/TeamCityReporter.cs
+++ b/test/coverlet.core.tests/Reporters/TeamCityReporter.cs
@@ -1,5 +1,6 @@
using System;
-using Coverlet.Core.Reporters;
+using Coverlet.Core.Abstracts;
+using Coverlet.Core.ObjectModel;
using Xunit;
namespace Coverlet.Core.Reporters.Tests
diff --git a/test/coverlet.integration.tests/AssertHelper.cs b/test/coverlet.integration.tests/AssertHelper.cs
index 4fa6e0658..d3629db05 100644
--- a/test/coverlet.integration.tests/AssertHelper.cs
+++ b/test/coverlet.integration.tests/AssertHelper.cs
@@ -3,7 +3,7 @@
using System.IO;
using System.Linq;
-using Coverlet.Core;
+using Coverlet.Core.ObjectModel;
using Xunit.Sdk;
namespace Coverlet.Integration.Tests
diff --git a/test/coverlet.integration.tests/BaseTest.cs b/test/coverlet.integration.tests/BaseTest.cs
index 739ebbf42..932a233dc 100644
--- a/test/coverlet.integration.tests/BaseTest.cs
+++ b/test/coverlet.integration.tests/BaseTest.cs
@@ -5,7 +5,7 @@
using System.Reflection;
using System.Xml.Linq;
-using Coverlet.Core;
+using Coverlet.Core.ObjectModel;
using Newtonsoft.Json;
using NuGet.Packaging;
using Xunit;