diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json
index acf83a0be..924174dc1 100644
--- a/.config/dotnet-tools.json
+++ b/.config/dotnet-tools.json
@@ -3,7 +3,7 @@
"isRoot": true,
"tools": {
"dotnet-reportgenerator-globaltool": {
- "version": "5.2.0",
+ "version": "5.2.1",
"commands": [
"reportgenerator"
]
diff --git a/Directory.Build.props b/Directory.Build.props
index 36526253d..91ee686a1 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -53,6 +53,25 @@
15.9.20
1.6.0
1.5.0
+
+
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 6976c130a..015e0ee98 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -29,13 +29,13 @@
vstest 17.8 version
NuGetFrameworksVersion is defined here https://github.com/microsoft/vstest/blob/9a0c41811637edf4afe0e265e08fdd1cb18109ed/eng/Versions.props#L94C1-L94C1
-->
+
-
-
-
+
+
@@ -44,11 +44,11 @@
-
+
-
+
-
+
diff --git a/Documentation/Examples/MSBuild/DeterministicBuild/XUnitTestProject1/XUnitTestProject1.csproj b/Documentation/Examples/MSBuild/DeterministicBuild/XUnitTestProject1/XUnitTestProject1.csproj
index a5048b133..19586970c 100644
--- a/Documentation/Examples/MSBuild/DeterministicBuild/XUnitTestProject1/XUnitTestProject1.csproj
+++ b/Documentation/Examples/MSBuild/DeterministicBuild/XUnitTestProject1/XUnitTestProject1.csproj
@@ -8,7 +8,7 @@
-
+
all
runtime; build; native; contentfiles; analyzers
diff --git a/Documentation/Examples/MSBuild/MergeWith/XUnitTestProject1/XUnitTestProject1.csproj b/Documentation/Examples/MSBuild/MergeWith/XUnitTestProject1/XUnitTestProject1.csproj
index e59657fc7..e78fcdd22 100644
--- a/Documentation/Examples/MSBuild/MergeWith/XUnitTestProject1/XUnitTestProject1.csproj
+++ b/Documentation/Examples/MSBuild/MergeWith/XUnitTestProject1/XUnitTestProject1.csproj
@@ -12,8 +12,8 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
+
+
all
runtime; build; native; contentfiles; analyzers
diff --git a/Documentation/Examples/MSBuild/MergeWith/XUnitTestProject2/XUnitTestProject2.csproj b/Documentation/Examples/MSBuild/MergeWith/XUnitTestProject2/XUnitTestProject2.csproj
index 6c791bc5d..4346c22c3 100644
--- a/Documentation/Examples/MSBuild/MergeWith/XUnitTestProject2/XUnitTestProject2.csproj
+++ b/Documentation/Examples/MSBuild/MergeWith/XUnitTestProject2/XUnitTestProject2.csproj
@@ -12,7 +12,7 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
all
runtime; build; native; contentfiles; analyzers
diff --git a/Documentation/Examples/MSBuild/MergeWith/XUnitTestProject3/XUnitTestProject3.csproj b/Documentation/Examples/MSBuild/MergeWith/XUnitTestProject3/XUnitTestProject3.csproj
index e85527020..4cdea2dbd 100644
--- a/Documentation/Examples/MSBuild/MergeWith/XUnitTestProject3/XUnitTestProject3.csproj
+++ b/Documentation/Examples/MSBuild/MergeWith/XUnitTestProject3/XUnitTestProject3.csproj
@@ -12,7 +12,7 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
all
runtime; build; native; contentfiles; analyzers
diff --git a/Documentation/Examples/VSTest/DeterministicBuild/XUnitTestProject1/XUnitTestProject1.csproj b/Documentation/Examples/VSTest/DeterministicBuild/XUnitTestProject1/XUnitTestProject1.csproj
index 2f200b8cd..d63628b1e 100644
--- a/Documentation/Examples/VSTest/DeterministicBuild/XUnitTestProject1/XUnitTestProject1.csproj
+++ b/Documentation/Examples/VSTest/DeterministicBuild/XUnitTestProject1/XUnitTestProject1.csproj
@@ -8,7 +8,7 @@
-
+
all
runtime; build; native; contentfiles; analyzers
diff --git a/Documentation/Examples/VSTest/HelloWorld/XUnitTestProject1/XUnitTestProject1.csproj b/Documentation/Examples/VSTest/HelloWorld/XUnitTestProject1/XUnitTestProject1.csproj
index e77db159d..3ab4b931e 100644
--- a/Documentation/Examples/VSTest/HelloWorld/XUnitTestProject1/XUnitTestProject1.csproj
+++ b/Documentation/Examples/VSTest/HelloWorld/XUnitTestProject1/XUnitTestProject1.csproj
@@ -9,7 +9,7 @@
-
+
all
runtime; build; native; contentfiles; analyzers
diff --git a/Documentation/MSBuildIntegration.md b/Documentation/MSBuildIntegration.md
index a097462e9..f3bd95251 100644
--- a/Documentation/MSBuildIntegration.md
+++ b/Documentation/MSBuildIntegration.md
@@ -50,6 +50,15 @@ To specify a directory where all results will be written to (especially if using
dotnet test /p:CollectCoverage=true /p:CoverletOutput='./results/'
```
+_Note: escape characters might produce some unexpected results._
+
+|status| msbuild parameter |
+|---|---|
+|:heavy_check_mark:|`/p:CoverletOutput="C:/GitHub/coverlet/artifacts/Reports/coverlet.core/"`|
+|:heavy_check_mark:|`/p:CoverletOutput="C:\\GitHub\\coverlet\\artifacts\\Reports\\coverlet.core\\"`|
+|:heavy_check_mark:|`/p:CoverletOutput="C:\GitHub\coverlet\artifacts\Reports\coverlet.core\\"`|
+|:x:|`/p:CoverletOutput="C:\GitHub\coverlet\artifacts\Reports\coverlet.core\"`|
+
The Coverlet MSBuild task sets the `CoverletReport` MSBuild item so that you can easily use the produced coverage reports. For example, using [ReportGenerator](https://github.com/danielpalme/ReportGenerator#usage--command-line-parameters) to generate an html coverage report.
```xml
diff --git a/eng/publish-coverage-results.yml b/eng/publish-coverage-results.yml
index a74bac9ec..58dda6870 100644
--- a/eng/publish-coverage-results.yml
+++ b/eng/publish-coverage-results.yml
@@ -19,7 +19,7 @@ steps:
script: |
dotnet tool restore --add-source https://api.nuget.org/v3/index.json
dotnet tool list
- dotnet reportgenerator -reports:"${{parameters.reports}}" -targetdir:"$(Build.SourcesDirectory)\artifacts\CoverageReport" -reporttypes:"Html;HtmlInline_AzurePipelines_Dark;Cobertura" -assemblyfilters:"${{parameters.assemblyfilters}}" -classfilters:"${{parameters.classfilters}}" -verbosity:Verbose --minimumCoverageThresholds:lineCoverage=${{parameters.minimumLineCoverage}}
+ dotnet reportgenerator -reports:"${{parameters.reports}}" -targetdir:"$(Build.SourcesDirectory)\artifacts\CoverageReport" -reporttypes:"HtmlInline_AzurePipelines_Dark;Cobertura" -assemblyfilters:"${{parameters.assemblyfilters}}" -classfilters:"${{parameters.classfilters}}" -verbosity:Verbose --minimumCoverageThresholds:lineCoverage=${{parameters.minimumLineCoverage}}
- publish: '$(Build.SourcesDirectory)/artifacts/CoverageReport'
displayName: 'Publish CoverageReport Artifact'
diff --git a/src/coverlet.collector/DataCollection/AttachmentManager.cs b/src/coverlet.collector/DataCollection/AttachmentManager.cs
index 9a4a68786..a062a40bc 100644
--- a/src/coverlet.collector/DataCollection/AttachmentManager.cs
+++ b/src/coverlet.collector/DataCollection/AttachmentManager.cs
@@ -4,7 +4,6 @@
using System;
using System.ComponentModel;
using System.IO;
-using coverlet.collector.Resources;
using Coverlet.Collector.Utilities;
using Coverlet.Collector.Utilities.Interfaces;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection;
@@ -109,8 +108,7 @@ private string SaveCoverageReport(string report, string reportFileName)
}
catch (Exception ex)
{
- string errorMessage = string.Format(Resources.FailedToSaveCoverageReport, CoverletConstants.DataCollectorName, reportFileName, _reportDirectory);
- throw new CoverletDataCollectorException(errorMessage, ex);
+ throw new CoverletDataCollectorException($"{CoverletConstants.DataCollectorName}: Failed to save coverage report '{reportFileName}' in directory '{_reportDirectory}'", ex);
}
}
@@ -168,8 +166,7 @@ private void CleanupReportDirectory()
}
catch (Exception ex)
{
- string errorMessage = string.Format(Resources.FailedToCleanupReportDirectory, CoverletConstants.DataCollectorName, _reportDirectory);
- throw new CoverletDataCollectorException(errorMessage, ex);
+ throw new CoverletDataCollectorException($"{CoverletConstants.DataCollectorName}: Failed to cleanup report directory: '{_reportDirectory}'", ex);
}
}
}
diff --git a/src/coverlet.collector/DataCollection/CoverageManager.cs b/src/coverlet.collector/DataCollection/CoverageManager.cs
index d6f6052c8..f0453501c 100644
--- a/src/coverlet.collector/DataCollection/CoverageManager.cs
+++ b/src/coverlet.collector/DataCollection/CoverageManager.cs
@@ -5,7 +5,6 @@
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;
@@ -68,8 +67,7 @@ public void InstrumentModules()
}
catch (Exception ex)
{
- string errorMessage = string.Format(Resources.InstrumentationException, CoverletConstants.DataCollectorName);
- throw new CoverletDataCollectorException(errorMessage, ex);
+ throw new CoverletDataCollectorException($"{CoverletConstants.DataCollectorName}: Failed to instrument modules", ex);
}
}
@@ -96,8 +94,7 @@ private CoverageResult GetCoverageResult()
}
catch (Exception ex)
{
- string errorMessage = string.Format(Resources.CoverageResultException, CoverletConstants.DataCollectorName);
- throw new CoverletDataCollectorException(errorMessage, ex);
+ throw new CoverletDataCollectorException($"{CoverletConstants.DataCollectorName}: Failed to get coverage result", ex);
}
}
@@ -114,8 +111,7 @@ private CoverageResult GetCoverageResult()
}
catch (Exception ex)
{
- string errorMessage = string.Format(Resources.CoverageReportException, CoverletConstants.DataCollectorName);
- throw new CoverletDataCollectorException(errorMessage, ex);
+ throw new CoverletDataCollectorException($"{CoverletConstants.DataCollectorName}: Failed to get coverage report", ex);
}
}
}
diff --git a/src/coverlet.collector/DataCollection/CoverletSettingsParser.cs b/src/coverlet.collector/DataCollection/CoverletSettingsParser.cs
index 29584281e..733dacfcc 100644
--- a/src/coverlet.collector/DataCollection/CoverletSettingsParser.cs
+++ b/src/coverlet.collector/DataCollection/CoverletSettingsParser.cs
@@ -5,7 +5,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Xml;
-using coverlet.collector.Resources;
using Coverlet.Collector.Utilities;
namespace Coverlet.Collector.DataCollection
@@ -72,8 +71,7 @@ private static string ParseTestModule(IEnumerable testModules)
// Validate if at least one source present.
if (testModules == null || !testModules.Any())
{
- string errorMessage = string.Format(Resources.NoTestModulesFound, CoverletConstants.DataCollectorName);
- throw new CoverletDataCollectorException(errorMessage);
+ throw new CoverletDataCollectorException($"{CoverletConstants.DataCollectorName}: No test modules found");
}
// Note:
diff --git a/src/coverlet.collector/InProcDataCollection/CoverletInProcDataCollector.cs b/src/coverlet.collector/InProcDataCollection/CoverletInProcDataCollector.cs
index b61d75f55..b1e6c5be2 100644
--- a/src/coverlet.collector/InProcDataCollection/CoverletInProcDataCollector.cs
+++ b/src/coverlet.collector/InProcDataCollection/CoverletInProcDataCollector.cs
@@ -5,7 +5,6 @@
using System.Diagnostics;
using System.Reflection;
using System.Text;
-using coverlet.collector.Resources;
using Coverlet.Collector.Utilities;
using Coverlet.Core.Instrumentation;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection;
@@ -76,8 +75,7 @@ public void TestSessionEnd(TestSessionEndArgs testSessionEndArgs)
if (_enableExceptionLog)
{
_eqtTrace.Error("{0}: Failed to unload module with error: {1}", CoverletConstants.InProcDataCollectorName, ex);
- string errorMessage = string.Format(Resources.FailedToUnloadModule, CoverletConstants.InProcDataCollectorName);
- throw new CoverletDataCollectorException(errorMessage, ex);
+ throw new CoverletDataCollectorException($"{CoverletConstants.InProcDataCollectorName}: Failed to unload module", ex);
}
}
}
diff --git a/src/coverlet.collector/Resources/Resources.Designer.cs b/src/coverlet.collector/Resources/Resources.Designer.cs
deleted file mode 100644
index b8dab181e..000000000
--- a/src/coverlet.collector/Resources/Resources.Designer.cs
+++ /dev/null
@@ -1,126 +0,0 @@
-//------------------------------------------------------------------------------
-//
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-//
-//------------------------------------------------------------------------------
-
-namespace coverlet.collector.Resources {
- using System;
-
-
- ///
- /// A strongly-typed resource class, for looking up localized strings, etc.
- ///
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class Resources {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal Resources() {
- }
-
- ///
- /// Returns the cached ResourceManager instance used by this class.
- ///
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("coverlet.collector.Resources.Resources", typeof(Resources).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- ///
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- ///
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- ///
- /// Looks up a localized string similar to {0}: Failed to get coverage report.
- ///
- internal static string CoverageReportException {
- get {
- return ResourceManager.GetString("CoverageReportException", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to {0}: Failed to get coverage result.
- ///
- internal static string CoverageResultException {
- get {
- return ResourceManager.GetString("CoverageResultException", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to {0}: Failed to cleanup report directory: '{1}'.
- ///
- internal static string FailedToCleanupReportDirectory {
- get {
- return ResourceManager.GetString("FailedToCleanupReportDirectory", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to {0}: Failed to save coverage report '{1}' in directory '{2}'.
- ///
- internal static string FailedToSaveCoverageReport {
- get {
- return ResourceManager.GetString("FailedToSaveCoverageReport", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to {0}: Failed to unload module.
- ///
- internal static string FailedToUnloadModule {
- get {
- return ResourceManager.GetString("FailedToUnloadModule", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to {0}: Failed to instrument modules.
- ///
- internal static string InstrumentationException {
- get {
- return ResourceManager.GetString("InstrumentationException", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to {0}: No test modules found.
- ///
- internal static string NoTestModulesFound {
- get {
- return ResourceManager.GetString("NoTestModulesFound", resourceCulture);
- }
- }
- }
-}
diff --git a/src/coverlet.collector/Resources/Resources.resx b/src/coverlet.collector/Resources/Resources.resx
deleted file mode 100644
index af111547e..000000000
--- a/src/coverlet.collector/Resources/Resources.resx
+++ /dev/null
@@ -1,141 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- text/microsoft-resx
-
-
- 2.0
-
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- {0}: Failed to get coverage report
-
-
- {0}: Failed to get coverage result
-
-
- {0}: Failed to cleanup report directory: '{1}'
-
-
- {0}: Failed to save coverage report '{1}' in directory '{2}'
-
-
- {0}: Failed to unload module
-
-
- {0}: Failed to instrument modules
-
-
- {0}: No test modules found
-
-
\ No newline at end of file
diff --git a/src/coverlet.collector/coverlet.collector.csproj b/src/coverlet.collector/coverlet.collector.csproj
index 7b48b9a37..fc6145d80 100644
--- a/src/coverlet.collector/coverlet.collector.csproj
+++ b/src/coverlet.collector/coverlet.collector.csproj
@@ -49,33 +49,6 @@
-
-
- True
- True
- Resources.resx
-
-
- True
- True
- Resources.resx
-
-
-
-
- ResXFileCodeGenerator
- Resources.Designer.cs
-
-
- ResXFileCodeGenerator
- Resource.Designer.cs
-
-
- ResXFileCodeGenerator
- Resources.Designer.cs
-
-
-
diff --git a/src/coverlet.core/Coverage.cs b/src/coverlet.core/Coverage.cs
index 3c7778db3..3e0f46216 100644
--- a/src/coverlet.core/Coverage.cs
+++ b/src/coverlet.core/Coverage.cs
@@ -60,6 +60,14 @@ internal class Coverage
public string Identifier { get; }
+ readonly JsonSerializerOptions _options = new()
+ {
+ PropertyNameCaseInsensitive = true,
+ DictionaryKeyPolicy = JsonNamingPolicy.CamelCase,
+ IncludeFields = true,
+ WriteIndented = true
+ };
+
public Coverage(string moduleOrDirectory,
CoverageParameters parameters,
ILogger logger,
@@ -311,10 +319,17 @@ public CoverageResult GetCoverageResult()
var coverageResult = new CoverageResult { Identifier = Identifier, Modules = modules, InstrumentedResults = _results, Parameters = _parameters };
- if (!string.IsNullOrEmpty(_parameters.MergeWith) && !string.IsNullOrWhiteSpace(_parameters.MergeWith) && _fileSystem.Exists(_parameters.MergeWith))
+ if (!string.IsNullOrEmpty(_parameters.MergeWith) && !string.IsNullOrWhiteSpace(_parameters.MergeWith))
{
- string json = _fileSystem.ReadAllText(_parameters.MergeWith);
- coverageResult.Merge(JsonSerializer.Deserialize(json));
+ if (_fileSystem.Exists(_parameters.MergeWith))
+ {
+ _logger.LogInformation($"MergeWith: '{_parameters.MergeWith}'.");
+ string json = _fileSystem.ReadAllText(_parameters.MergeWith);
+ coverageResult.Merge(JsonSerializer.Deserialize(json, _options));
+ } else
+ {
+ _logger.LogInformation($"MergeWith: file '{_parameters.MergeWith}' does not exist.");
+ }
}
return coverageResult;
diff --git a/src/coverlet.core/CoverageResult.cs b/src/coverlet.core/CoverageResult.cs
index c547f6a40..3cc9ce084 100644
--- a/src/coverlet.core/CoverageResult.cs
+++ b/src/coverlet.core/CoverageResult.cs
@@ -24,7 +24,7 @@ internal class Branches : List { }
internal class Method
{
[JsonConstructor]
- public Method()
+ internal Method()
{
Lines = [];
Branches = [];
diff --git a/src/coverlet.core/coverlet.core.csproj b/src/coverlet.core/coverlet.core.csproj
index 77ef61484..0492a403b 100644
--- a/src/coverlet.core/coverlet.core.csproj
+++ b/src/coverlet.core/coverlet.core.csproj
@@ -7,18 +7,18 @@
-
-
-
-
+
+
+
+
-
+
-
-
+
+
diff --git a/src/coverlet.msbuild.tasks/ReportWriter.cs b/src/coverlet.msbuild.tasks/ReportWriter.cs
index 65225f098..2f5404295 100644
--- a/src/coverlet.msbuild.tasks/ReportWriter.cs
+++ b/src/coverlet.msbuild.tasks/ReportWriter.cs
@@ -30,8 +30,8 @@ public string WriteReport()
if (filename == string.Empty)
{
- // empty filename for instance only directory is passed to CoverletOutput c:\reportpath
- // c:\reportpath\coverage.reportedextension
+ // empty filename for instance only directory is passed to CoverletOutput 'c:/reportpath/'
+ // note: use always '/' and not backslash => 'c:\reportpath\coverage.cobertura.xml'
filename = $"coverage.{_coverletMultiTargetFrameworksCurrentTFM}{separatorPoint}{_reporter.Extension}";
}
else if (Path.HasExtension(filename))
@@ -47,8 +47,9 @@ public string WriteReport()
filename = $"{filename}{separatorPoint}{_coverletMultiTargetFrameworksCurrentTFM}.{_reporter.Extension}";
}
- string report = Path.Combine(_directory, filename);
+ string report = Path.Combine(Path.GetFullPath(_directory), filename);
_fileSystem.WriteAllText(report, _reporter.Report(_result, _sourceRootTranslator));
+
return report;
}
}
diff --git a/test/coverlet.core.tests/Coverage/CoverageTests.cs b/test/coverlet.core.tests/Coverage/CoverageTests.cs
index ccd293760..25761f83d 100644
--- a/test/coverlet.core.tests/Coverage/CoverageTests.cs
+++ b/test/coverlet.core.tests/Coverage/CoverageTests.cs
@@ -2,9 +2,15 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
+using System.Collections.Generic;
using System.IO;
+using System.Linq;
+using System.Text.Encodings.Web;
+using System.Text.Json;
+using System.Text.Json.Serialization;
using Coverlet.Core.Abstractions;
using Coverlet.Core.Helpers;
+using Coverlet.Core.Instrumentation;
using Coverlet.Core.Symbols;
using Moq;
using Xunit;
@@ -14,6 +20,16 @@ namespace Coverlet.Core.Tests
public partial class CoverageTests
{
private readonly Mock _mockLogger = new();
+ readonly JsonSerializerOptions _options = new()
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ IncludeFields = true,
+ WriteIndented = true,
+ Converters =
+ {
+ new BranchDictionaryConverterFactory()
+ }
+ };
[Fact]
public void TestCoverage()
@@ -86,11 +102,131 @@ public void TestCoverageWithTestAssembly()
new SourceRootTranslator(module, _mockLogger.Object, new FileSystem(), new AssemblyAdapter()), new CecilSymbolHelper());
coverage.PrepareModules();
- CoverageResult result = coverage.GetCoverageResult();
+ string result = JsonSerializer.Serialize(coverage.GetCoverageResult(), _options);
+
+ Assert.Contains("coverlet.core.tests.dll", result);
+
+ directory.Delete(true);
+ }
+
+ [Fact]
+ public void TestCoverageMergeWithParameter()
+ {
+ string module = GetType().Assembly.Location;
+ string pdb = Path.Combine(Path.GetDirectoryName(module), Path.GetFileNameWithoutExtension(module) + ".pdb");
+
+ DirectoryInfo directory = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()));
+
+ File.Copy(module, Path.Combine(directory.FullName, Path.GetFileName(module)), true);
+ File.Copy(pdb, Path.Combine(directory.FullName, Path.GetFileName(pdb)), true);
+
+ // TODO: Find a way to mimick hits
+ var instrumentationHelper =
+ new InstrumentationHelper(new ProcessExitHandler(), new RetryHelper(), new FileSystem(), new Mock().Object,
+ new SourceRootTranslator(module, new Mock().Object, new FileSystem(), new AssemblyAdapter()));
+
+ var parameters = new CoverageParameters
+ {
+ IncludeFilters = new string[] { "[coverlet.tests.projectsample.excludedbyattribute*]*" },
+ IncludeDirectories = Array.Empty(),
+ ExcludeFilters = Array.Empty(),
+ ExcludedSourceFiles = Array.Empty(),
+ ExcludeAttributes = Array.Empty(),
+ IncludeTestAssembly = false,
+ SingleHit = false,
+ MergeWith = Directory.GetFiles(Path.Combine(Directory.GetCurrentDirectory(), "TestAssets"), "MergeWith.coverage.json").First(),
+ UseSourceLink = false
+ };
+
+ var coverage = new Coverage(Path.Combine(directory.FullName, Path.GetFileName(module)), parameters, _mockLogger.Object, instrumentationHelper, new FileSystem(), new SourceRootTranslator(_mockLogger.Object, new FileSystem()), new CecilSymbolHelper());
+ coverage.PrepareModules();
+
+ string result = JsonSerializer.Serialize(coverage.GetCoverageResult(), _options);
+
+ Assert.Contains("DeepThought.cs", result);
+
+ _mockLogger.Verify(l => l.LogInformation(It.Is(v => v.StartsWith("MergeWith: '") && v.EndsWith("MergeWith.coverage.json'.")), It.IsAny()), Times.Once);
+
+ directory.Delete(true);
+ }
+
+ [Fact]
+ public void TestCoverageMergeWithWrongParameter()
+ {
+ string module = GetType().Assembly.Location;
+ string pdb = Path.Combine(Path.GetDirectoryName(module), Path.GetFileNameWithoutExtension(module) + ".pdb");
- Assert.NotEmpty(result.Modules);
+ DirectoryInfo directory = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()));
+
+ File.Copy(module, Path.Combine(directory.FullName, Path.GetFileName(module)), true);
+ File.Copy(pdb, Path.Combine(directory.FullName, Path.GetFileName(pdb)), true);
+
+ // TODO: Find a way to mimick hits
+ var instrumentationHelper =
+ new InstrumentationHelper(new ProcessExitHandler(), new RetryHelper(), new FileSystem(), new Mock().Object,
+ new SourceRootTranslator(module, new Mock().Object, new FileSystem(), new AssemblyAdapter()));
+
+ var parameters = new CoverageParameters
+ {
+ IncludeFilters = new string[] { "[coverlet.tests.projectsample.excludedbyattribute*]*" },
+ IncludeDirectories = Array.Empty(),
+ ExcludeFilters = Array.Empty(),
+ ExcludedSourceFiles = Array.Empty(),
+ ExcludeAttributes = Array.Empty(),
+ IncludeTestAssembly = false,
+ SingleHit = false,
+ MergeWith = "FileDoesNotExist.json",
+ UseSourceLink = false
+ };
+
+ var coverage = new Coverage(Path.Combine(directory.FullName, Path.GetFileName(module)), parameters, _mockLogger.Object, instrumentationHelper, new FileSystem(), new SourceRootTranslator(_mockLogger.Object, new FileSystem()), new CecilSymbolHelper());
+ coverage.PrepareModules();
+
+ string result = JsonSerializer.Serialize(coverage.GetCoverageResult(), _options);
+
+ _mockLogger.Verify(l => l.LogInformation(It.Is(v => v.Equals("MergeWith: file 'FileDoesNotExist.json' does not exist.")), It.IsAny()), Times.Once);
directory.Delete(true);
}
}
}
+public class BranchDictionaryConverterFactory : JsonConverterFactory
+{
+ public override bool CanConvert(Type typeToConvert)
+ {
+ return typeof(Dictionary).IsAssignableFrom(typeToConvert);
+ }
+
+ public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
+ {
+ Type[] genericArgs = typeToConvert.GetGenericArguments();
+ Type keyType = genericArgs[0];
+ Type valueType = genericArgs[1];
+
+ JsonConverter converter = (JsonConverter)Activator.CreateInstance(
+ typeof(BranchDictionaryConverter<,>).MakeGenericType(new Type[] { keyType, valueType }));
+
+ return converter;
+ }
+}
+
+public class BranchDictionaryConverter : JsonConverter>
+{
+ public override Dictionary Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override void Write(Utf8JsonWriter writer, Dictionary value, JsonSerializerOptions options)
+ {
+ writer.WriteStartObject();
+
+ foreach (KeyValuePair pair in value)
+ {
+ writer.WritePropertyName(pair.Key.ToString());
+ JsonSerializer.Serialize(writer, pair.Value, options);
+ }
+
+ writer.WriteEndObject();
+ }
+}
diff --git a/test/coverlet.core.tests/TestAssets/MergeWith.coverage.json b/test/coverlet.core.tests/TestAssets/MergeWith.coverage.json
new file mode 100644
index 000000000..ab9608617
--- /dev/null
+++ b/test/coverlet.core.tests/TestAssets/MergeWith.coverage.json
@@ -0,0 +1,43 @@
+{
+ "coverletsamplelib.integration.template.dll": {
+ "C:\\GitHub\\coverlet\\artifacts\\bin\\coverlet.integration.tests\\debug_net7.0\\01021711\\Program.cs": {
+ "HelloWorld.Program": {
+ "System.Void HelloWorld.Program::Main(System.String[])": {
+ "Lines": {
+ "10": 1,
+ "11": 1,
+ "12": 1,
+ "13": 1,
+ "14": 1
+ },
+ "Branches": []
+ }
+ }
+ },
+ "C:\\GitHub\\coverlet\\artifacts\\bin\\coverlet.integration.tests\\debug_net7.0\\01021711\\DeepThought.cs": {
+ "Coverlet.Integration.Template.DeepThought": {
+ "System.Int32 Coverlet.Integration.Template.DeepThought::AnswerToTheUltimateQuestionOfLifeTheUniverseAndEverything()": {
+ "Lines": {
+ "6": 1,
+ "7": 1,
+ "8": 1
+ },
+ "Branches": []
+ }
+ }
+ },
+ "C:\\GitHub\\coverlet\\artifacts\\bin\\coverlet.integration.tests\\debug_net7.0\\01021711\\TemplateTest.cs": {
+ "Coverlet.Integration.Template.TemplateTest": {
+ "System.Void Coverlet.Integration.Template.TemplateTest::Answer()": {
+ "Lines": {
+ "9": 0,
+ "10": 0,
+ "11": 0,
+ "12": 0
+ },
+ "Branches": []
+ }
+ }
+ }
+ }
+}
diff --git a/test/coverlet.core.tests/coverlet.core.tests.csproj b/test/coverlet.core.tests/coverlet.core.tests.csproj
index bf8b35f17..314575a09 100644
--- a/test/coverlet.core.tests/coverlet.core.tests.csproj
+++ b/test/coverlet.core.tests/coverlet.core.tests.csproj
@@ -55,6 +55,9 @@
Always
+
+ Always
+
Always
diff --git a/test/coverlet.integration.determisticbuild/coverlet.integration.determisticbuild.csproj b/test/coverlet.integration.determisticbuild/coverlet.integration.determisticbuild.csproj
index 0f1e39197..584a6fef6 100644
--- a/test/coverlet.integration.determisticbuild/coverlet.integration.determisticbuild.csproj
+++ b/test/coverlet.integration.determisticbuild/coverlet.integration.determisticbuild.csproj
@@ -25,7 +25,7 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/test/coverlet.integration.template/coverlet.integration.template.csproj b/test/coverlet.integration.template/coverlet.integration.template.csproj
index 759a71f66..0ac2c4764 100644
--- a/test/coverlet.integration.template/coverlet.integration.template.csproj
+++ b/test/coverlet.integration.template/coverlet.integration.template.csproj
@@ -11,7 +11,7 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/test/coverlet.integration.tests/coverlet.integration.tests.csproj b/test/coverlet.integration.tests/coverlet.integration.tests.csproj
index ae5e1804d..e685ce525 100644
--- a/test/coverlet.integration.tests/coverlet.integration.tests.csproj
+++ b/test/coverlet.integration.tests/coverlet.integration.tests.csproj
@@ -10,9 +10,8 @@
-
-
+
diff --git a/test/coverlet.msbuild.tasks.tests/Reporters.cs b/test/coverlet.msbuild.tasks.tests/Reporters.cs
index a38563a41..16450f1f7 100644
--- a/test/coverlet.msbuild.tasks.tests/Reporters.cs
+++ b/test/coverlet.msbuild.tasks.tests/Reporters.cs
@@ -21,14 +21,12 @@ public class Reporters
[InlineData(null, "/folder/reportFolder/file.ext", "cobertura", "/folder/reportFolder/file.ext")]
[InlineData(null, "/folder/reportFolder/file.ext1.ext2", "cobertura", "/folder/reportFolder/file.ext1.ext2")]
[InlineData(null, "/folder/reportFolder/file", "cobertura", "/folder/reportFolder/file.cobertura.xml")]
- [InlineData(null, "file", "cobertura", "file.cobertura.xml")]
// multiple tfm
[InlineData("netcoreapp2.2", "/folder/reportFolder/", "lcov", "/folder/reportFolder/coverage.netcoreapp2.2.info")]
[InlineData("netcoreapp2.2", "/folder/reportFolder/", "cobertura", "/folder/reportFolder/coverage.netcoreapp2.2.cobertura.xml")]
[InlineData("net472", "/folder/reportFolder/file.ext", "cobertura", "/folder/reportFolder/file.net472.ext")]
[InlineData("net472", "/folder/reportFolder/file.ext1.ext2", "cobertura", "/folder/reportFolder/file.ext1.net472.ext2")]
[InlineData("netcoreapp2.2", "/folder/reportFolder/file", "cobertura", "/folder/reportFolder/file.netcoreapp2.2.cobertura.xml")]
- [InlineData("netcoreapp2.2", "file", "cobertura", "file.netcoreapp2.2.cobertura.xml")]
public void Msbuild_ReportWriter(string? coverletMultiTargetFrameworksCurrentTFM, string coverletOutput, string reportFormat, string expectedFileName)
{
var fileSystem = new Mock();
@@ -43,6 +41,11 @@ public void Msbuild_ReportWriter(string? coverletMultiTargetFrameworksCurrentTFM
new CoverageResult() { Modules = new Modules(), Parameters = new CoverageParameters() }, null);
string path = reportWriter.WriteReport();
+ //remove drive for windows paths
+ if (path.Contains(':'))
+ {
+ path = path[2..];
+ }
// Path.Combine depends on OS so we can change only win side to avoid duplication
Assert.Equal(path.Replace('/', Path.DirectorySeparatorChar), expectedFileName.Replace('/', Path.DirectorySeparatorChar));
}
diff --git a/test/coverlet.tests.utils/Properties/AssemblyInfo.cs b/test/coverlet.tests.utils/Properties/AssemblyInfo.cs
index 081699970..44bd6fece 100644
--- a/test/coverlet.tests.utils/Properties/AssemblyInfo.cs
+++ b/test/coverlet.tests.utils/Properties/AssemblyInfo.cs
@@ -8,4 +8,3 @@
[assembly: InternalsVisibleTo("coverlet.core.tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100757cf9291d78a82e5bb58a827a3c46c2f959318327ad30d1b52e918321ffbd847fb21565b8576d2a3a24562a93e86c77a298b564a0f1b98f63d7a1441a3a8bcc206da3ed09d5dacc76e122a109a9d3ac608e21a054d667a2bae98510a1f0f653c0e6f58f42b4b3934f6012f5ec4a09b3dfd3e14d437ede1424bdb722aead64ad")]
[assembly: InternalsVisibleTo("coverlet.integration.tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010001d24efbe9cbc2dc49b7a3d2ae34ca37cfb69b4f450acd768a22ce5cd021c8a38ae7dc68b2809a1ac606ad531b578f192a5690b2986990cbda4dd84ec65a3a4c1c36f6d7bb18f08592b93091535eaee2f0c8e48763ed7f190db2008e1f9e0facd5c0df5aaab74febd3430e09a428a72e5e6b88357f92d78e47512d46ebdc3cbb")]
-
diff --git a/test/coverlet.tests.xunit.extensions/coverlet.tests.xunit.extensions.csproj b/test/coverlet.tests.xunit.extensions/coverlet.tests.xunit.extensions.csproj
index 6a67a0006..f3b1a3aac 100644
--- a/test/coverlet.tests.xunit.extensions/coverlet.tests.xunit.extensions.csproj
+++ b/test/coverlet.tests.xunit.extensions/coverlet.tests.xunit.extensions.csproj
@@ -8,6 +8,6 @@
-
+