diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json
new file mode 100644
index 000000000..924174dc1
--- /dev/null
+++ b/.config/dotnet-tools.json
@@ -0,0 +1,12 @@
+{
+ "version": 1,
+ "isRoot": true,
+ "tools": {
+ "dotnet-reportgenerator-globaltool": {
+ "version": "5.2.1",
+ "commands": [
+ "reportgenerator"
+ ]
+ }
+ }
+}
diff --git a/.editorconfig b/.editorconfig
index 1227fcc9b..8e51a965d 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -13,6 +13,8 @@ indent_style = space
indent_size = 4
trim_trailing_whitespace = true
+file_header_template = Copyright (c) Toni Solarin-Sodara\nLicensed under the MIT license. See LICENSE file in the project root for full license information.
+
# XML project files
[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
indent_size = 2
@@ -42,7 +44,7 @@ end_of_line = crlf
# Code files
[*.{cs,csx,vb,vbx}]
-indent_size = 4
+indent_size = 2
insert_final_newline = true
charset = utf-8-bom
###############################
@@ -53,6 +55,8 @@ charset = utf-8-bom
dotnet_sort_system_directives_first = true
## IDE0005: Using directive is unnecessary.
dotnet_diagnostic.IDE0005.severity = warning
+## IDE0007 use 'var' instead of explicit type
+dotnet_diagnostic.IDE0007.severity = none
# License header
file_header_template = Copyright (c) Toni Solarin-Sodara\nLicensed under the MIT license. See LICENSE file in the project root for full license information.
## IDE0073: The file header is missing or not located at the top of the file
@@ -132,12 +136,15 @@ dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case
dotnet_diagnostic.IDE1006.severity = warning
# IDE0090: Use 'new(...)'
dotnet_diagnostic.IDE0090.severity = warning
+dotnet_style_operator_placement_when_wrapping = beginning_of_line
+tab_width = 2
+end_of_line = crlf
###############################
# C# Coding Conventions #
###############################
[*.cs]
# Organize usings
-csharp_using_directive_placement = outside_namespace:warning
+csharp_using_directive_placement = outside_namespace:silent
# var preferences - use keywords instead of BCL types, and permit var only when the type is clear
csharp_style_var_for_built_in_types = false:warning
csharp_style_var_when_type_is_apparent = true:warning
@@ -213,3 +220,10 @@ csharp_space_between_square_brackets = false
# Wrapping preferences
csharp_preserve_single_line_statements = true
csharp_preserve_single_line_blocks = true
+csharp_prefer_simple_using_statement = true:suggestion
+csharp_style_namespace_declarations = block_scoped:silent
+csharp_style_prefer_method_group_conversion = true:silent
+csharp_style_prefer_top_level_statements = true:silent
+csharp_style_prefer_primary_constructors = true:suggestion
+csharp_style_expression_bodied_lambdas = true:silent
+csharp_style_expression_bodied_local_functions = false:silent
diff --git a/.github/ISSUE_TEMPLATE/01-bug_report.md b/.github/ISSUE_TEMPLATE/01-bug_report.md
new file mode 100644
index 000000000..cf23db478
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/01-bug_report.md
@@ -0,0 +1,33 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: "[BUG]"
+labels: ''
+assignees: ''
+
+---
+
+**Describe the bug**
+Please share a clear and concise description of the problem.
+
+**To Reproduce**
+Please include minimal steps to reproduce the problem if possible. E.g.: the smallest possible code snippet; or a small project, with steps to run it. If possible include text as text rather than screenshots (so it shows up in searches).
+
+**Expected behavior**
+Provide a description of the expected behavior.
+
+**Actual behavior**
+Provide a description of the actual behavior observed. If applicable please include any error messages, exception stacktraces or memory dumps.
+
+**Configuration (please complete the following information):**
+Please provide more information on your .NET configuration:
+ * Which coverlet package and version was used?
+ * Which version of .NET is the code running on?
+ * What OS and version, and what distro if applicable?
+ * What is the architecture (x64, x86, ARM, ARM64)?
+ * Do you know whether it is specific to that configuration?
+
+**Additional context**
+Add any other context about the problem here.
+
+> :exclamation: Please also read [Known Issues](https://github.com/coverlet-coverage/coverlet/blob/master/Documentation/KnownIssues.md)
diff --git a/.github/ISSUE_TEMPLATE/02-blank-issue-template.md b/.github/ISSUE_TEMPLATE/02-blank-issue-template.md
new file mode 100644
index 000000000..19921adfb
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/02-blank-issue-template.md
@@ -0,0 +1,10 @@
+---
+name: Blank issue template
+about: Create a report for questions or feature requests
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 000000000..3ba13e0ce
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1 @@
+blank_issues_enabled: false
diff --git a/.github/workflows/issue-close.yml b/.github/workflows/issue-close.yml
new file mode 100644
index 000000000..c4a17a05f
--- /dev/null
+++ b/.github/workflows/issue-close.yml
@@ -0,0 +1,25 @@
+name: Close inactive issues
+on:
+ schedule:
+ - cron: "0 1 * * 0"
+ workflow_dispatch:
+
+env:
+ DAYS_BEFORE_ISSUE_CLOSE: 275
+
+jobs:
+ close-issues:
+ runs-on: ubuntu-latest
+ permissions:
+ issues: write
+ pull-requests: write
+ steps:
+ - uses: actions/stale@v9
+ with:
+ exempt-issue-labels: "bug,tracking-external-issue,Known Issue"
+ days-before-issue-close: ${{ env.DAYS_BEFORE_ISSUE_CLOSE }}
+ close-issue-message: "This issue was closed because it has been inactive for ${{ env.DAYS_BEFORE_ISSUE_CLOSE }} days since being marked as stale."
+ days-before-pr-stale: -1
+ days-before-pr-close: -1
+ operations-per-run: 100
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/issue-inactive.yml b/.github/workflows/issue-inactive.yml
new file mode 100644
index 000000000..b2bcfaef2
--- /dev/null
+++ b/.github/workflows/issue-inactive.yml
@@ -0,0 +1,26 @@
+name: Label inactive issues
+on:
+ schedule:
+ - cron: "0 1 * * 0"
+ workflow_dispatch:
+
+env:
+ DAYS_BEFORE_ISSUE_STALE: 90
+
+jobs:
+ close-issues:
+ runs-on: ubuntu-latest
+ permissions:
+ issues: write
+ pull-requests: write
+ steps:
+ - uses: actions/stale@v9
+ with:
+ exempt-issue-labels: "bug,tracking-external-issue,Known Issue,stale"
+ days-before-issue-stale: ${{ env.DAYS_BEFORE_ISSUE_STALE }}
+ stale-issue-label: "stale"
+ stale-issue-message: "This issue is stale because it has been open for ${{ env.DAYS_BEFORE_ISSUE_STALE }} days with no activity."
+ days-before-pr-stale: -1
+ days-before-pr-close: -1
+ operations-per-run: 100
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/issue-untriaged.yml b/.github/workflows/issue-untriaged.yml
new file mode 100644
index 000000000..2115d7c30
--- /dev/null
+++ b/.github/workflows/issue-untriaged.yml
@@ -0,0 +1,25 @@
+# current list of labels: untriaged|duplicate|invalid|bug|enhancement|feature-request|documentation|question|wontfix|Known issue|up-for-grabs|in progress|with repo|needs repo|tracking-external-issue
+# https://github.com/coverlet-coverage/coverlet/blob/master/Documentation/Roadmap.md
+
+name: Label issues
+on:
+ issues:
+ types:
+ - reopened
+ - opened
+jobs:
+ label_issues:
+ runs-on: ubuntu-latest
+ permissions:
+ issues: write
+ steps:
+ - uses: actions/github-script@v6
+ with:
+ script: |
+ github.rest.issues.addLabels({
+ issue_number: context.issue.number,
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ labels: ["untriaged"]
+ })
+
diff --git a/.gitignore b/.gitignore
index 177132965..478b5fc8b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,7 +24,7 @@ bld/
[Bb]in/
[Oo]bj/
[Ll]og/
-msbuild.binlog
+*.binlog
# Visual Studio 2015 cache/options directory
.vs/
@@ -303,4 +303,16 @@ __pycache__/
.AssemblyAttributes
DeterministicTest.props
test/coverlet.integration.determisticbuild/*.txt
-test/coverlet.integration.determisticbuild/runsettings
\ No newline at end of file
+test/coverlet.integration.determisticbuild/runsettings
+
+coverage.json
+coverage.xml
+coverage.*.json
+coverage.*.xml
+coverage.cobertura.xml
+coverage.opencover.xml
+coverage.*.cobertura.xml
+coverage.*.opencover.xml
+
+FolderProfile.pubxml
+/NuGet.config
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 5d5c71993..ffe337455 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -4,8 +4,9 @@ Contributions are highly welcome, however, except for very small changes, kindly
## Requirements
-.NET SDK 2.2 https://dotnet.microsoft.com/download/dotnet-core/2.2
-.NET SDK 3.1 https://dotnet.microsoft.com/download/dotnet-core/3.1
+[.NET SDK 6.0](https://dotnet.microsoft.com/en-us/download/dotnet/6.0)
+[.NET SDK 7.0](https://dotnet.microsoft.com/en-us/download/dotnet/7.0)
+[.NET SDK 8.0](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)
## Building the Project
@@ -18,7 +19,7 @@ Building, testing, and packing use all the standard dotnet commands:
dotnet restore
dotnet build --no-restore
- dotnet pack
+ dotnet pack -c Debug
dotnet test --no-build /p:CollectCoverage=true /p:Include=\"[coverlet.collector]*,[coverlet.core]*,[coverlet.msbuild.tasks]*\" /p:Exclude=\"[coverlet.core.tests.samples.netstandard]*,[coverlet.tests.xunit.extensions]*\"
NB. You need to `pack` before testing because we have some integration testing that consume packages
diff --git a/DeterministicBuild.targets b/DeterministicBuild.targets
deleted file mode 100644
index 13208d103..000000000
--- a/DeterministicBuild.targets
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
- $([System.IO.Path]::Combine('$(IntermediateOutputPath)','$(TargetFrameworkMoniker).AssemblyAttributes$(DefaultLanguageSourceExtension)'))
-
-
-
-
-
-
-
-
-
-
- <_LocalTopLevelSourceRoot Include="@(SourceRoot)" Condition="'%(SourceRoot.NestedRoot)' == ''"/>
-
-
-
diff --git a/Directory.Build.props b/Directory.Build.props
index a075f6f8b..74a9fdaea 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -2,21 +2,30 @@
$(MSBuildThisFileDirectory)
+
Debug
- $(MSBuildThisFileDirectory)bin\$(Configuration)\Packages\
+
true
true
true
snupkg
+
true
- true
preview
+ false
true
preview
- $(NoWarn);NU5105
+ $(NoWarn);NU1507;NU5105;CS1591
+ true
https://api.nuget.org/v3/index.json;
+
+
+
+ true
+ $(MSBuildThisFileDirectory)artifacts
+ 6.0.0
@@ -25,7 +34,49 @@
-
-
+
+
+
+
+ $(RepoRoot)artifacts/testresults/$(Configuration.ToLowerInvariant())
+ @(VSTestLogger)
+
+
+
+ $(RepoRoot)artifacts\testresults\$(Configuration.ToLowerInvariant())
+ @(VSTestLogger)
+
+
+
+
+
+
+ 15.9.20
+ 1.6.0
+ 1.5.0
+
+
+
+
diff --git a/Directory.Build.targets b/Directory.Build.targets
index d970b1531..8c119d541 100644
--- a/Directory.Build.targets
+++ b/Directory.Build.targets
@@ -1,32 +1,2 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Directory.Packages.props b/Directory.Packages.props
new file mode 100644
index 000000000..08e359ac2
--- /dev/null
+++ b/Directory.Packages.props
@@ -0,0 +1,60 @@
+
+
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Documentation/Changelog.md b/Documentation/Changelog.md
index 3575567a4..5dfebc247 100644
--- a/Documentation/Changelog.md
+++ b/Documentation/Changelog.md
@@ -6,233 +6,307 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased
+## Release date 2024-01-20
+### Packages
+coverlet.msbuild 6.0.4
+coverlet.console 6.0.4
+coverlet.collector 6.0.4
+
+### Fixed
+- Fix empty coverage report when using include and exclude filters [#1726](https://github.com/coverlet-coverage/coverlet/issues/1726)
+
+## Release date 2024-12-31
+### Packages
+coverlet.msbuild 6.0.3
+coverlet.console 6.0.3
+coverlet.collector 6.0.3
+
+### Fixed
+- Fix RuntimeConfigurationReader to support self-contained builds [#1705](https://github.com/coverlet-coverage/coverlet/pull/1705) by https://github.com/pfeigl
+- Fix inconsistent filenames with UseSourceLink after .NET 8 [#1679](https://github.com/coverlet-coverage/coverlet/issues/1679)
+- Fix hanging tests [#989](https://github.com/coverlet-coverage/coverlet/issues/989)
+- Fix coverlet instrumentation becomes slow after installing dotnet sdk 8.0.200 [#1620](https://github.com/coverlet-coverage/coverlet/issues/1620)
+- Fix upgrading v6.0.1 to v6.0.2 increases instrumentation time [#1649](https://github.com/coverlet-coverage/coverlet/issues/1649)
+- Fix Unable to instrument module - NET 8 [#1631](https://github.com/coverlet-coverage/coverlet/issues/1631)
+- Fix slow modules filtering process [#1646](https://github.com/coverlet-coverage/coverlet/issues/1646) by https://github.com/BlackGad
+- Fix incorrect coverage await using in generic method [#1490](https://github.com/coverlet-coverage/coverlet/issues/1490)
+
+### Improvements
+- Cache the regex used in InstrumentationHelper [#1693](https://github.com/coverlet-coverage/coverlet/issues/1693)
+- Enable dotnetTool integration tests for linux [#660](https://github.com/coverlet-coverage/coverlet/issues/660)
+
+## Release date 2024-03-13
+### Packages
+coverlet.msbuild 6.0.2
+coverlet.console 6.0.2
+coverlet.collector 6.0.2
+
+### Fixed
+- Threshold-stat triggers error [#1634](https://github.com/coverlet-coverage/coverlet/issues/1634)
+- Fixed coverlet collector 6.0.1 requires dotnet sdk 8 [#1625](https://github.com/coverlet-coverage/coverlet/issues/1625)
+- Type initializer errors after updating from 6.0.0 to 6.0.1 [#1629](https://github.com/coverlet-coverage/coverlet/issues/1629)
+- Exception when multiple exclude-by-attribute filters specified [#1624](https://github.com/coverlet-coverage/coverlet/issues/1624)
+
+### Improvements
+- More concise options to specify multiple parameters in coverlet.console [#1624](https://github.com/coverlet-coverage/coverlet/issues/1624)
+
+## Release date 2024-02-19
+### Packages
+coverlet.msbuild 6.0.1
+coverlet.console 6.0.1
+coverlet.collector 6.0.1
+
### Fixed
--Could not write lines to file CoverletSourceRootsMapping - in use by another process [#1155](https://github.com/coverlet-coverage/coverlet/issues/1155)
--Incorrect coverage for methods returning IAsyncEnumerable in generic classes [#1383](https://github.com/coverlet-coverage/coverlet/issues/1383)
--Wrong branch coverage for async methods .NET Standard 1.x [#1376](https://github.com/coverlet-coverage/coverlet/issues/1376)
--Empty path exception in visual basic projects [#775](https://github.com/coverlet-coverage/coverlet/issues/775)
--Allign published nuget package version to github release version [#1413](https://github.com/coverlet-coverage/coverlet/issues/1413)
--Sync nuget and github release versions [#1122](https://github.com/coverlet-coverage/coverlet/issues/1122)
+- Uncovered lines in .NET 8 for inheriting records [#1555](https://github.com/coverlet-coverage/coverlet/issues/1555)
+- Fix record constructors not covered when SkipAutoProps is true [#1561](https://github.com/coverlet-coverage/coverlet/issues/1561)
+- Fix .NET 7 Method Group branch coverage issue [#1447](https://github.com/coverlet-coverage/coverlet/issues/1447)
+- Fix ExcludeFromCodeCoverage does not exclude method in a partial class [#1548](https://github.com/coverlet-coverage/coverlet/issues/1548)
+- Fix ExcludeFromCodeCoverage does not exclude F# task [#1547](https://github.com/coverlet-coverage/coverlet/issues/1547)
+- Fix issues where ExcludeFromCodeCoverage ignored [#1431](https://github.com/coverlet-coverage/coverlet/issues/1431)
+- Fix issues with ExcludeFromCodeCoverage attribute [#1484](https://github.com/coverlet-coverage/coverlet/issues/1484)
+- Fix broken links in documentation [#1514](https://github.com/coverlet-coverage/coverlet/issues/1514)
+- Fix problem with coverage for .net5 WPF application [#1221](https://github.com/coverlet-coverage/coverlet/issues/1221) by https://github.com/lg2de
+- Fix unable to instrument module for Microsoft.AspNetCore.Mvc.Razor [#1459](https://github.com/coverlet-coverage/coverlet/issues/1459) by https://github.com/lg2de
+
+### Improvements
+- Extended exclude by attribute feature to work with fully qualified name [#1589](https://github.com/coverlet-coverage/coverlet/issues/1589)
+- Use System.CommandLine instead of McMaster.Extensions.CommandLineUtils [#1474](https://github.com/coverlet-coverage/coverlet/issues/1474) by https://github.com/Bertk
+- Fix deadlog in Coverlet.Integration.Tests.BaseTest [#1541](https://github.com/coverlet-coverage/coverlet/pull/1541) by https://github.com/Bertk
+- Add coverlet.msbuild.tasks unit tests [#1534](https://github.com/coverlet-coverage/coverlet/pull/1534) by https://github.com/Bertk
+
+## Release date 2023-05-21
+### Packages
+coverlet.msbuild 6.0.0
+coverlet.console 6.0.0
+coverlet.collector 6.0.0
-### Improvements
--Migration of the project to .NET 6.0 [#1473](https://github.com/coverlet-coverage/coverlet/pull/1473)
+### Fixed
+- Could not write lines to file CoverletSourceRootsMapping - in use by another process [#1155](https://github.com/coverlet-coverage/coverlet/issues/1155)
+- Incorrect coverage for methods returning IAsyncEnumerable in generic classes [#1383](https://github.com/coverlet-coverage/coverlet/issues/1383)
+- Wrong branch coverage for async methods .NET Standard 1.x [#1376](https://github.com/coverlet-coverage/coverlet/issues/1376)
+- Empty path exception in visual basic projects [#775](https://github.com/coverlet-coverage/coverlet/issues/775)
+- Allign published nuget package version to github release version [#1413](https://github.com/coverlet-coverage/coverlet/issues/1413)
+- Sync nuget and github release versions [#1122](https://github.com/coverlet-coverage/coverlet/issues/1122)
+
+### Improvements
+- Migration of the project to .NET 6.0 [#1473](https://github.com/coverlet-coverage/coverlet/pull/1473)
### Breaking changes
- New parameter `ExcludeAssembliesWithoutSources` to control automatic assembly exclusion [1164](https://github.com/coverlet-coverage/coverlet/issues/1164). The parameter `InstrumentModulesWithoutLocalSources` has been removed. since it can be handled by setting `ExcludeAssembliesWithoutSources` to `None`.
- The default heuristics for determining whether to instrument an assembly has been changed. In previous versions any missing source file was taken as a signal that it was a third-party project that shouldn't be instrumented, with exceptions for some common file name patterns for source generators. Now only assemblies where no source files at all can be found are excluded from instrumentation, and the code for detecting source generator files have been removed. To get back to the behaviour that at least one missing file is sufficient to exclude an assembly, set `ExcludeAssembliesWithoutSources` to `MissingAny`, or use assembly exclusion filters for more fine-grained control.
## Release date 2022-10-29
-### Packages
-coverlet.msbuild 3.2.0
-coverlet.console 3.2.0
-coverlet.collector 3.2.0
+### Packages
+coverlet.msbuild 3.2.0
+coverlet.console 3.2.0
+coverlet.collector 3.2.0
### Fixed
--Fix TypeLoadException when referencing Microsoft.Extensions.DependencyInjection v6.0.1 [#1390](https://github.com/coverlet-coverage/coverlet/issues/1390)
--Source Link for code generators fails [#1322](https://github.com/coverlet-coverage/coverlet/issues/1322)
--Await foreach has wrong branch coverage when method is generic [#1210](https://github.com/coverlet-coverage/coverlet/issues/1210)
--ExcludeFromCodeCoverage attribute on local functions ignores lambda expression [#1302](https://github.com/coverlet-coverage/coverlet/issues/1302)
+- Fix TypeLoadException when referencing Microsoft.Extensions.DependencyInjection v6.0.1 [#1390](https://github.com/coverlet-coverage/coverlet/issues/1390)
+- Source Link for code generators fails [#1322](https://github.com/coverlet-coverage/coverlet/issues/1322)
+- Await foreach has wrong branch coverage when method is generic [#1210](https://github.com/coverlet-coverage/coverlet/issues/1210)
+- ExcludeFromCodeCoverage attribute on local functions ignores lambda expression [#1302](https://github.com/coverlet-coverage/coverlet/issues/1302)
-### Added
--Added InstrumentModulesWithoutLocalSources setting [#1360](https://github.com/coverlet-coverage/coverlet/pull/1360) by @TFTomSun
+### Added
+- Added InstrumentModulesWithoutLocalSources setting [#1360](https://github.com/coverlet-coverage/coverlet/pull/1360) by @TFTomSun
## Release date 2022-02-06
-### Packages
-coverlet.msbuild 3.1.2
-coverlet.console 3.1.2
-coverlet.collector 3.1.2
+### Packages
+coverlet.msbuild 3.1.2
+coverlet.console 3.1.2
+coverlet.collector 3.1.2
### Fixed
--Fix CoreLib's coverage measurement is broken [#1286](https://github.com/coverlet-coverage/coverlet/pull/1286)
--Fix UnloadModule injection [1291](https://github.com/coverlet-coverage/coverlet/pull/1291)
+- Fix CoreLib's coverage measurement is broken [#1286](https://github.com/coverlet-coverage/coverlet/pull/1286)
+- Fix UnloadModule injection [1291](https://github.com/coverlet-coverage/coverlet/pull/1291)
## Release date 2022-01-30
-### Packages
-coverlet.msbuild 3.1.1
-coverlet.console 3.1.1
-coverlet.collector 3.1.1
-
-### Fixed
--Fix wrong branch coverage with EnumeratorCancellation attribute [#1275](https://github.com/coverlet-coverage/coverlet/issues/1275)
--Fix negative coverage exceeding int.MaxValue [#1266](https://github.com/coverlet-coverage/coverlet/issues/1266)
--Fix summary output format for culture de-DE [#1263](https://github.com/coverlet-coverage/coverlet/issues/1263)
--Fix branch coverage issue for finally block with await [#1233](https://github.com/coverlet-coverage/coverlet/issues/1233)
--Fix threshold doesn't work when coverage empty [#1205](https://github.com/coverlet-coverage/coverlet/issues/1205)
--Fix branch coverage issue for il switch [#1177](https://github.com/coverlet-coverage/coverlet/issues/1177)
--Fix branch coverage with using statement and several awaits[#1176](https://github.com/coverlet-coverage/coverlet/issues/1176)
--Fix `CopyCoverletDataCollectorFiles` to avoid to override user dlls for `dotnet publish` scenario [#1243](https://github.com/coverlet-coverage/coverlet/pull/1243)
-
-### Improvements
--Improve logging in case of exception inside static ctor of NetstandardAwareAssemblyResolver [#1230](https://github.com/coverlet-coverage/coverlet/pull/1230)
--When collecting open the hitfile with read access [#1214](https://github.com/coverlet-coverage/coverlet/pull/1214) by https://github.com/JamesWTruher
--Add CompilerGenerated attribute to the tracker [#1229](https://github.com/coverlet-coverage/coverlet/pull/1229)
+### Packages
+coverlet.msbuild 3.1.1
+coverlet.console 3.1.1
+coverlet.collector 3.1.1
+
+### Fixed
+- Fix wrong branch coverage with EnumeratorCancellation attribute [#1275](https://github.com/coverlet-coverage/coverlet/issues/1275)
+- Fix negative coverage exceeding int.MaxValue [#1266](https://github.com/coverlet-coverage/coverlet/issues/1266)
+- Fix summary output format for culture de-DE [#1263](https://github.com/coverlet-coverage/coverlet/issues/1263)
+- Fix branch coverage issue for finally block with await [#1233](https://github.com/coverlet-coverage/coverlet/issues/1233)
+- Fix threshold doesn't work when coverage empty [#1205](https://github.com/coverlet-coverage/coverlet/issues/1205)
+- Fix branch coverage issue for il switch [#1177](https://github.com/coverlet-coverage/coverlet/issues/1177)
+- Fix branch coverage with using statement and several awaits[#1176](https://github.com/coverlet-coverage/coverlet/issues/1176)
+- Fix `CopyCoverletDataCollectorFiles` to avoid to override user dlls for `dotnet publish` scenario [#1243](https://github.com/coverlet-coverage/coverlet/pull/1243)
+
+### Improvements
+- Improve logging in case of exception inside static ctor of NetstandardAwareAssemblyResolver [#1230](https://github.com/coverlet-coverage/coverlet/pull/1230)
+- When collecting open the hitfile with read access [#1214](https://github.com/coverlet-coverage/coverlet/pull/1214) by https://github.com/JamesWTruher
+- Add CompilerGenerated attribute to the tracker [#1229](https://github.com/coverlet-coverage/coverlet/pull/1229)
## Release date 2021-07-19
-### Packages
-coverlet.msbuild 3.1.0
-coverlet.console 3.1.0
+### Packages
+coverlet.msbuild 3.1.0
+coverlet.console 3.1.0
coverlet.collector 3.1.0
-### Fixed
--Fix branch coverage for targetframework net472 [#1167](https://github.com/coverlet-coverage/coverlet/issues/1167)
--Fix F# projects with `unkown` source [#1145](https://github.com/coverlet-coverage/coverlet/issues/1145)
--Fix SkipAutoProps for inline assigned properties [#1139](https://github.com/coverlet-coverage/coverlet/issues/1139)
--Fix partially covered throw statement [#1144](https://github.com/coverlet-coverage/coverlet/pull/1144)
--Fix coverage threshold not failing when no coverage [#1115](https://github.com/coverlet-coverage/coverlet/pull/1115)
--Fix partially covered `await foreach` statement [#1107](https://github.com/coverlet-coverage/coverlet/pull/1107) by https://github.com/alexthornton1
--Fix `System.MissingMethodException`(TryGetIntArgFromDict) [#1101](https://github.com/coverlet-coverage/coverlet/pull/1101)
--Fix ExcludeFromCodeCoverage on props [#1114](https://github.com/coverlet-coverage/coverlet/pull/1114)
--Fix incorrect branch coverage with await using [#1111](https://github.com/coverlet-coverage/coverlet/pull/1111) by https://github.com/alexthornton1
+### Fixed
+- Fix branch coverage for targetframework net472 [#1167](https://github.com/coverlet-coverage/coverlet/issues/1167)
+- Fix F# projects with `unkown` source [#1145](https://github.com/coverlet-coverage/coverlet/issues/1145)
+- Fix SkipAutoProps for inline assigned properties [#1139](https://github.com/coverlet-coverage/coverlet/issues/1139)
+- Fix partially covered throw statement [#1144](https://github.com/coverlet-coverage/coverlet/pull/1144)
+- Fix coverage threshold not failing when no coverage [#1115](https://github.com/coverlet-coverage/coverlet/pull/1115)
+- Fix partially covered `await foreach` statement [#1107](https://github.com/coverlet-coverage/coverlet/pull/1107) by https://github.com/alexthornton1
+- Fix `System.MissingMethodException`(TryGetIntArgFromDict) [#1101](https://github.com/coverlet-coverage/coverlet/pull/1101)
+- Fix ExcludeFromCodeCoverage on props [#1114](https://github.com/coverlet-coverage/coverlet/pull/1114)
+- Fix incorrect branch coverage with await using [#1111](https://github.com/coverlet-coverage/coverlet/pull/1111) by https://github.com/alexthornton1
### Added
--Support deterministic reports [#1113](https://github.com/coverlet-coverage/coverlet/pull/1113)
--Specifying threshold level for each threshold type [#1123](https://github.com/coverlet-coverage/coverlet/pull/1123) by https://github.com/pbmiguel
+- Support deterministic reports [#1113](https://github.com/coverlet-coverage/coverlet/pull/1113)
+- Specifying threshold level for each threshold type [#1123](https://github.com/coverlet-coverage/coverlet/pull/1123) by https://github.com/pbmiguel
### Improvements
--Implementation of Npath complexity for the OpenCover reports [#1058](https://github.com/coverlet-coverage/coverlet/pull/1058) by https://github.com/benjaminZale
+- Implementation of Npath complexity for the OpenCover reports [#1058](https://github.com/coverlet-coverage/coverlet/pull/1058) by https://github.com/benjaminZale
## Release date 2021-02-21
-### Packages
-coverlet.msbuild 3.0.3
-coverlet.console 3.0.3
-coverlet.collector 3.0.3
+### Packages
+coverlet.msbuild 3.0.3
+coverlet.console 3.0.3
+coverlet.collector 3.0.3
### Fixed
--Fix code coverage stops working if assembly contains source generators generated file [#1091](https://github.com/coverlet-coverage/coverlet/pull/1091)
+- Fix code coverage stops working if assembly contains source generators generated file [#1091](https://github.com/coverlet-coverage/coverlet/pull/1091)
## Release date 2021-01-24
-### Packages
-coverlet.msbuild 3.0.2
-coverlet.console 3.0.2
-coverlet.collector 3.0.2
+### Packages
+coverlet.msbuild 3.0.2
+coverlet.console 3.0.2
+coverlet.collector 3.0.2
### Fixed
--Fix multi-line lambda coverage regression [#1060](https://github.com/coverlet-coverage/coverlet/pull/1060)
--Opt-in reachability helper to mitigate resolution issue [#1061](https://github.com/coverlet-coverage/coverlet/pull/1061)
+- Fix multi-line lambda coverage regression [#1060](https://github.com/coverlet-coverage/coverlet/pull/1060)
+- Opt-in reachability helper to mitigate resolution issue [#1061](https://github.com/coverlet-coverage/coverlet/pull/1061)
## Release date 2021-01-16
-### Packages
-coverlet.msbuild 3.0.1
-coverlet.console 3.0.1
-coverlet.collector 3.0.1
+### Packages
+coverlet.msbuild 3.0.1
+coverlet.console 3.0.1
+coverlet.collector 3.0.1
### Fixed
--Fix severe loss in coverage [#1043](https://github.com/coverlet-coverage/coverlet/pull/1043) by https://github.com/daveMueller
+- Fix severe loss in coverage [#1043](https://github.com/coverlet-coverage/coverlet/pull/1043) by https://github.com/daveMueller
## Release date 2021-01-09
-### Packages
-coverlet.msbuild 3.0.0
-coverlet.console 3.0.0
-coverlet.collector 3.0.0
+### Packages
+coverlet.msbuild 3.0.0
+coverlet.console 3.0.0
+coverlet.collector 3.0.0
### Fixed
--Attribute exclusion does not work if attribute name does not end with "Attribute" [#884](https://github.com/coverlet-coverage/coverlet/pull/884) by https://github.com/bddckr
--Fix deterministic build+source link bug [#895](https://github.com/coverlet-coverage/coverlet/pull/895)
--Fix anonymous delegate compiler generate bug [#896](https://github.com/coverlet-coverage/coverlet/pull/896)
--Fix incorrect branch coverage with await ValueTask [#949](https://github.com/coverlet-coverage/coverlet/pull/949) by https://github.com/alexthornton1
--Fix switch pattern coverage [#1006](https://github.com/coverlet-coverage/coverlet/pull/1006)
+- Attribute exclusion does not work if attribute name does not end with "Attribute" [#884](https://github.com/coverlet-coverage/coverlet/pull/884) by https://github.com/bddckr
+- Fix deterministic build+source link bug [#895](https://github.com/coverlet-coverage/coverlet/pull/895)
+- Fix anonymous delegate compiler generate bug [#896](https://github.com/coverlet-coverage/coverlet/pull/896)
+- Fix incorrect branch coverage with await ValueTask [#949](https://github.com/coverlet-coverage/coverlet/pull/949) by https://github.com/alexthornton1
+- Fix switch pattern coverage [#1006](https://github.com/coverlet-coverage/coverlet/pull/1006)
### Added
--Skip autoprops feature [#912](https://github.com/coverlet-coverage/coverlet/pull/912)
--Exclude code that follows [DoesNotReturn] from code coverage [#904](https://github.com/coverlet-coverage/coverlet/pull/904) by https://github.com/kevin-montrose
--`CoverletReport` MSBuild variable containing coverage filenames [#932](https://github.com/coverlet-coverage/coverlet/pull/932) by https://github.com/0xced
--Add Visual Studio Add-In [#954](https://github.com/coverlet-coverage/coverlet/pull/954) by https://github.com/FortuneN
--Remove workaround for deterministic build for sdk >= 3.1.100 [#965](https://github.com/coverlet-coverage/coverlet/pull/965)
--Allow standalone coverlet usage for integration/end-to-end tests using .NET tool driver [#991](https://github.com/coverlet-coverage/coverlet/pull/991)
--Support .NET Framework(>= net461) for in-process data collectors [#970](https://github.com/coverlet-coverage/coverlet/pull/970)
+- Skip autoprops feature [#912](https://github.com/coverlet-coverage/coverlet/pull/912)
+- Exclude code that follows [DoesNotReturn] from code coverage [#904](https://github.com/coverlet-coverage/coverlet/pull/904) by https://github.com/kevin-montrose
+- `CoverletReport` MSBuild variable containing coverage filenames [#932](https://github.com/coverlet-coverage/coverlet/pull/932) by https://github.com/0xced
+- Add Visual Studio Add-In [#954](https://github.com/coverlet-coverage/coverlet/pull/954) by https://github.com/FortuneN
+- Remove workaround for deterministic build for sdk >= 3.1.100 [#965](https://github.com/coverlet-coverage/coverlet/pull/965)
+- Allow standalone coverlet usage for integration/end-to-end tests using .NET tool driver [#991](https://github.com/coverlet-coverage/coverlet/pull/991)
+- Support .NET Framework(>= net461) for in-process data collectors [#970](https://github.com/coverlet-coverage/coverlet/pull/970)
## Release date 2020-05-30
-### Packages
-coverlet.msbuild 2.9.0
-coverlet.console 1.7.2
-coverlet.collector 1.3.0
+### Packages
+coverlet.msbuild 2.9.0
+coverlet.console 1.7.2
+coverlet.collector 1.3.0
### Fixed
--Fix for code complexity not being generated for methods for cobertura reporter [#738](https://github.com/tonerdo/coverlet/pull/798) by https://github.com/dannyBies
--Fix coverage, skip branches in generated `MoveNext()` for singleton iterators [#813](https://github.com/coverlet-coverage/coverlet/pull/813) by https://github.com/bert2
--Fix 'The process cannot access the file...because it is being used by another process' due to double flush for collectors driver [#https://github.com/coverlet-coverage/coverlet/pull/835](https://github.com/coverlet-coverage/coverlet/pull/835)
--Fix skip [ExcludefromCoverage] for generated async state machine [#849](https://github.com/coverlet-coverage/coverlet/pull/849)
+- Fix for code complexity not being generated for methods for cobertura reporter [#738](https://github.com/tonerdo/coverlet/pull/798) by https://github.com/dannyBies
+- Fix coverage, skip branches in generated `MoveNext()` for singleton iterators [#813](https://github.com/coverlet-coverage/coverlet/pull/813) by https://github.com/bert2
+- Fix 'The process cannot access the file...because it is being used by another process' due to double flush for collectors driver [#https://github.com/coverlet-coverage/coverlet/pull/835](https://github.com/coverlet-coverage/coverlet/pull/835)
+- Fix skip [ExcludefromCoverage] for generated async state machine [#849](https://github.com/coverlet-coverage/coverlet/pull/849)
### Added
--Added support for deterministic build for msbuild/collectors driver [#802](https://github.com/tonerdo/coverlet/pull/802) [#796](https://github.com/tonerdo/coverlet/pull/796) with the help of https://github.com/clairernovotny and https://github.com/tmat
+- Added support for deterministic build for msbuild/collectors driver [#802](https://github.com/tonerdo/coverlet/pull/802) [#796](https://github.com/tonerdo/coverlet/pull/796) with the help of https://github.com/clairernovotny and https://github.com/tmat
### Improvements
--Refactore DependencyInjection [#728](https://github.com/coverlet-coverage/coverlet/pull/768) by https://github.com/daveMueller
+- Refactore DependencyInjection [#728](https://github.com/coverlet-coverage/coverlet/pull/768) by https://github.com/daveMueller
## Release date 2020-04-02
-### Packages
-coverlet.msbuild 2.8.1
-coverlet.console 1.7.1
-coverlet.collector 1.2.1
+### Packages
+coverlet.msbuild 2.8.1
+coverlet.console 1.7.1
+coverlet.collector 1.2.1
### Fixed
--Fix ExcludeFromCodeCoverage attribute bugs [#129](https://github.com/tonerdo/coverlet/issues/129) and [#670](https://github.com/tonerdo/coverlet/issues/670) with [#671](https://github.com/tonerdo/coverlet/pull/671) by https://github.com/matteoerigozzi
--Fix bug with nested types filtering [#689](https://github.com/tonerdo/coverlet/issues/689)
--Fix Coverage Issue - New Using + Async/Await + ConfigureAwait [#669](https://github.com/tonerdo/coverlet/issues/669)
--Improve branch detection for lambda functions and async/await statements [#702](https://github.com/tonerdo/coverlet/pull/702) by https://github.com/matteoerigozzi
--Improve coverage, hide compiler generated branches for try/catch blocks inside async state machine [#716](https://github.com/tonerdo/coverlet/pull/716) by https://github.com/matteoerigozzi
--Improve coverage, skip lambda cached field [#753](https://github.com/tonerdo/coverlet/pull/753)
+- Fix ExcludeFromCodeCoverage attribute bugs [#129](https://github.com/tonerdo/coverlet/issues/129) and [#670](https://github.com/tonerdo/coverlet/issues/670) with [#671](https://github.com/tonerdo/coverlet/pull/671) by https://github.com/matteoerigozzi
+- Fix bug with nested types filtering [#689](https://github.com/tonerdo/coverlet/issues/689)
+- Fix Coverage Issue - New Using + Async/Await + ConfigureAwait [#669](https://github.com/tonerdo/coverlet/issues/669)
+- Improve branch detection for lambda functions and async/await statements [#702](https://github.com/tonerdo/coverlet/pull/702) by https://github.com/matteoerigozzi
+- Improve coverage, hide compiler generated branches for try/catch blocks inside async state machine [#716](https://github.com/tonerdo/coverlet/pull/716) by https://github.com/matteoerigozzi
+- Improve coverage, skip lambda cached field [#753](https://github.com/tonerdo/coverlet/pull/753)
### Improvements
--Trim whitespace between values when reading from configuration from runsettings [#679](https://github.com/tonerdo/coverlet/pull/679) by https://github.com/EricStG
--Code improvement, flow ILogger to InstrumentationHelper [#727](https://github.com/tonerdo/coverlet/pull/727) by https://github.com/daveMueller
--Add support for line branch coverage in OpenCover format [#772](https://github.com/tonerdo/coverlet/pull/772) by https://github.com/costin-zaharia
+- Trim whitespace between values when reading from configuration from runsettings [#679](https://github.com/tonerdo/coverlet/pull/679) by https://github.com/EricStG
+- Code improvement, flow ILogger to InstrumentationHelper [#727](https://github.com/tonerdo/coverlet/pull/727) by https://github.com/daveMueller
+- Add support for line branch coverage in OpenCover format [#772](https://github.com/tonerdo/coverlet/pull/772) by https://github.com/costin-zaharia
## Release date 2020-01-03
-### Packages
-coverlet.msbuild 2.8.0
-coverlet.console 1.7.0
+### Packages
+coverlet.msbuild 2.8.0
+coverlet.console 1.7.0
coverlet.collector 1.2.0
### Added
--Add log to tracker [#553](https://github.com/tonerdo/coverlet/pull/553)
--Exclude by assembly level System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage [#589](https://github.com/tonerdo/coverlet/pull/589)
--Allow coverlet integration with other MSBuild test strategies[#615](https://github.com/tonerdo/coverlet/pull/615) by https://github.com/sharwell
+- Add log to tracker [#553](https://github.com/tonerdo/coverlet/pull/553)
+- Exclude by assembly level System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage [#589](https://github.com/tonerdo/coverlet/pull/589)
+- Allow coverlet integration with other MSBuild test strategies[#615](https://github.com/tonerdo/coverlet/pull/615) by https://github.com/sharwell
### Fixed
--Fix and simplify async coverage [#549](https://github.com/tonerdo/coverlet/pull/549)
--Improve lambda scenario coverage [#583](https://github.com/tonerdo/coverlet/pull/583)
--Mitigate issue in case of failure in assembly loading by cecil [#625](https://github.com/tonerdo/coverlet/pull/625)
--Fix ConfigureAwait state machine generated branches [#634](https://github.com/tonerdo/coverlet/pull/634)
--Fix coverage overwritten if the project has multiple target frameworks [#636](https://github.com/tonerdo/coverlet/issues/177)
--Fix cobertura Jenkins reporter + source link support [#614](https://github.com/tonerdo/coverlet/pull/614) by https://github.com/daveMueller
--Fix pdb file locking during instrumentation [#656](https://github.com/tonerdo/coverlet/pull/656)
+- Fix and simplify async coverage [#549](https://github.com/tonerdo/coverlet/pull/549)
+- Improve lambda scenario coverage [#583](https://github.com/tonerdo/coverlet/pull/583)
+- Mitigate issue in case of failure in assembly loading by cecil [#625](https://github.com/tonerdo/coverlet/pull/625)
+- Fix ConfigureAwait state machine generated branches [#634](https://github.com/tonerdo/coverlet/pull/634)
+- Fix coverage overwritten if the project has multiple target frameworks [#636](https://github.com/tonerdo/coverlet/issues/177)
+- Fix cobertura Jenkins reporter + source link support [#614](https://github.com/tonerdo/coverlet/pull/614) by https://github.com/daveMueller
+- Fix pdb file locking during instrumentation [#656](https://github.com/tonerdo/coverlet/pull/656)
### Improvements
--Improve exception message for unsupported runtime [#569](https://github.com/tonerdo/
-coverlet/pull/569) by https://github.com/daveMueller
--Improve cobertura absolute/relative path report generation [#661](https://github.com/tonerdo/coverlet/pull/661) by https://github.com/daveMueller
+- Improve exception message for unsupported runtime [#569](https://github.com/tonerdo/
+coverlet/pull/569) by https://github.com/daveMueller
+- Improve cobertura absolute/relative path report generation [#661](https://github.com/tonerdo/coverlet/pull/661) by https://github.com/daveMueller
## Release date 2019-09-23
-### Packages
-coverlet.msbuild 2.7.0
-coverlet.console 1.6.0
+### Packages
+coverlet.msbuild 2.7.0
+coverlet.console 1.6.0
coverlet.collector 1.1.0
### Added
--Output multiple formats for vstest integration [#533](https://github.com/tonerdo/coverlet/pull/533) by https://github.com/daveMueller
--Different exit codes to indicate particular failures [#412](https://github.com/tonerdo/coverlet/pull/412) by https://github.com/sasivishnu
+- Output multiple formats for vstest integration [#533](https://github.com/tonerdo/coverlet/pull/533) by https://github.com/daveMueller
+- Different exit codes to indicate particular failures [#412](https://github.com/tonerdo/coverlet/pull/412) by https://github.com/sasivishnu
### Changed
--Skip instrumentation of module with embedded ppbd without local sources [#510](https://github.com/tonerdo/coverlet/pull/510), with this today xunit will be skipped in automatic way.
+- Skip instrumentation of module with embedded ppbd without local sources [#510](https://github.com/tonerdo/coverlet/pull/510), with this today xunit will be skipped in automatic way.
### Fixed
--Fix exclude by files [#524](https://github.com/tonerdo/coverlet/pull/524)
--Changed to calculate based on the average coverage of the module [#479](https://github.com/tonerdo/coverlet/pull/479) by https://github.com/dlplenin
--Fix property attribute detection [#477](https://github.com/tonerdo/coverlet/pull/477) by https://github.com/amweiss
--Fix instrumentation serialization bug [#458](https://github.com/tonerdo/coverlet/pull/458)
--Fix culture for cobertura xml report [#464](https://github.com/tonerdo/coverlet/pull/464)
+- Fix exclude by files [#524](https://github.com/tonerdo/coverlet/pull/524)
+- Changed to calculate based on the average coverage of the module [#479](https://github.com/tonerdo/coverlet/pull/479) by https://github.com/dlplenin
+- Fix property attribute detection [#477](https://github.com/tonerdo/coverlet/pull/477) by https://github.com/amweiss
+- Fix instrumentation serialization bug [#458](https://github.com/tonerdo/coverlet/pull/458)
+- Fix culture for cobertura xml report [#464](https://github.com/tonerdo/coverlet/pull/464)
diff --git a/Documentation/ConsumeNightlyBuild.md b/Documentation/ConsumeNightlyBuild.md
index 8f9309335..3901f70a5 100644
--- a/Documentation/ConsumeNightlyBuild.md
+++ b/Documentation/ConsumeNightlyBuild.md
@@ -8,9 +8,9 @@ To consume nightly builds, create a `NuGet.Config` in your root solution directo
-
+
-
+
@@ -18,36 +18,44 @@ To consume nightly builds, create a `NuGet.Config` in your root solution directo
## Install packages
-### Visual Studio:
+### Visual Studio
\
Example:\

-### NuGet (Package Manager console):
+### NuGet (Package Manager console)
+
```powershell
PM> Install-Package coverlet.msbuild -Version X.X.X-preview.X.XXX -Source https://pkgs.dev.azure.com/tonerdo/coverlet/_packaging/coverlet-nightly/nuget/v3/index.json
```
+
Example:
+
```powershell
PM> Install-Package coverlet.msbuild -Version 3.0.4-preview.4.g5de0ad7d60 -Source https://pkgs.dev.azure.com/tonerdo/coverlet/_packaging/coverlet-nightly/nuget/v3/index.json
```
-### .NET CLI:
+### .NET CLI
+
```bash
dotnet add package coverlet.msbuild --version X.X.X-preview.X.XXX --source https://pkgs.dev.azure.com/tonerdo/coverlet/_packaging/coverlet-nightly/nuget/v3/index.json
```
+
Example:
+
```bash
dotnet add package coverlet.msbuild --version 3.0.4-preview.4.g5de0ad7d60 --source https://pkgs.dev.azure.com/tonerdo/coverlet/_packaging/coverlet-nightly/nuget/v3/index.json
```
-### MSBuild project file:
+### MSBuild project file
```xml
```
+
Example:
+
```xml
-```
\ No newline at end of file
+```
diff --git a/Documentation/DeterministicBuild.md b/Documentation/DeterministicBuild.md
index aa984e2dd..f4f12d142 100644
--- a/Documentation/DeterministicBuild.md
+++ b/Documentation/DeterministicBuild.md
@@ -4,16 +4,17 @@ Support for deterministic builds is available **only** in the `msbuild` (`/p:Col
From a coverage perspective, deterministic builds create some challenges because coverage tools usually need access to complete source file metadata (ie. local path) during instrumentation and report generation. These files are reported inside the `.pdb` files, where debugging information is stored.
-In local (non-CI) builds, metadata emitted to pdbs are not "deterministic", which means that source files are reported with their full paths. For example, when we build the same project on different machines we'll have different paths emitted inside pdbs, hence, builds are "non deterministic".
+In local (non-CI) builds, metadata emitted to pdbs are not "deterministic", which means that source files are reported with their full paths. For example, when we build the same project on different machines we'll have different paths emitted inside pdbs, hence, builds are "non deterministic".
As explained above, to improve the level of security of generated artifacts (for instance, DLLs inside the NuGet package), we need to apply some signature (signing with certificate) and validate before usage to avoid possible security issues like tampering.
Finally, thanks to deterministic CI builds (with the `ContinuousIntegrationBuild` property set to `true`) plus signature we can validate artifacts and be sure that the binary was built from specific sources (because there is no hard-coded variable metadata, like paths from different build machines).
-# Deterministic report
+## Deterministic report
-Coverlet supports also deterministic reports(for now only for cobertura coverage format).
+Coverlet supports also deterministic reports(for now only for __Cobertura__ coverage format).
If you include `DeterministicReport` parameters for `msbuild` and `collectors` integrations resulting report will be like:
+
```xml
@@ -24,45 +25,9 @@ If you include `DeterministicReport` parameters for `msbuild` and `collectors` i
...
-```
-As you can see we have empty `` element and the `filename` start with well known deterministic fragment `/_/...`
-
-**Deterministic build is supported without any workaround since version 3.1.100 of .NET Core SDK**
-
-## Workaround only for .NET Core SDK < 3.1.100
-
-At the moment, deterministic build works thanks to the Roslyn compiler emitting deterministic metadata if `DeterministicSourcePaths` is enabled. Take a look [here](https://github.com/dotnet/sourcelink/tree/master/docs#deterministicsourcepaths) for more information.
-
-To allow Coverlet to correctly do its work, we need to provide information to translate deterministic paths to real local paths for every project referenced by the test project. The current workaround is to add at the root of your repo a `Directory.Build.targets` with a custom `target` that supports Coverlet resolution algorithm.
-
-```xml
-
-
-
-
- $([System.IO.Path]::Combine('$(IntermediateOutputPath)','$(TargetFrameworkMoniker).AssemblyAttributes$(DefaultLanguageSourceExtension)'))
-
-
-
-
-
-
-
-
-
-
- <_LocalTopLevelSourceRoot Include="@(SourceRoot)" Condition="'%(SourceRoot.NestedRoot)' == ''"/>
-
-
-
-
```
-If you already have a `Directory.Build.targets` file on your repo root you can simply copy `DeterministicBuild.targets` (which can be found at the root of this repo) next to yours and import it in your targets file. This target will be used by Coverlet to generate, at build time, a file that contains mapping translation information, the file is named `CoverletSourceRootsMapping` and will be in the output folder of your project.
+As you can see we have empty `` element and the `filename` start with well known deterministic fragment `/_/...`
You can follow our [step-by-step sample](Examples.md)
diff --git a/Documentation/Examples.md b/Documentation/Examples.md
index 55f8169f0..2a6e5cc0b 100644
--- a/Documentation/Examples.md
+++ b/Documentation/Examples.md
@@ -1,4 +1,5 @@
# Examples
+
## MSBuild Integration
* Using `/p:MergeWith` feature `Documentation/Examples/MSBuild/MergeWith/MergeWith.sln`
diff --git a/Documentation/Examples/.editorconfig b/Documentation/Examples/.editorconfig
new file mode 100644
index 000000000..d66ee0772
--- /dev/null
+++ b/Documentation/Examples/.editorconfig
@@ -0,0 +1,8 @@
+# top-most EditorConfig file
+# We don't want to import other EditorConfig files and we want
+# to ensure no rules are enabled for these asset source files.
+root = true
+
+[*.cs]
+# Default severity for all analyzer diagnostics
+dotnet_analyzer_diagnostic.severity = none
diff --git a/Documentation/Examples/MSBuild/DeterministicBuild/ClassLibrary1/Class1.cs b/Documentation/Examples/MSBuild/DeterministicBuild/ClassLibrary1/Class1.cs
index 289865e7b..dcb976188 100644
--- a/Documentation/Examples/MSBuild/DeterministicBuild/ClassLibrary1/Class1.cs
+++ b/Documentation/Examples/MSBuild/DeterministicBuild/ClassLibrary1/Class1.cs
@@ -1,8 +1,6 @@
-using System;
-
-namespace ClassLibrary1
+namespace ClassLibrary1
{
- public class Class1
+ public class Class1
{
public int Method()
{
diff --git a/Documentation/Examples/MSBuild/DeterministicBuild/ClassLibrary1/ClassLibrary1.csproj b/Documentation/Examples/MSBuild/DeterministicBuild/ClassLibrary1/ClassLibrary1.csproj
index 9f5c4f4ab..8271e5471 100644
--- a/Documentation/Examples/MSBuild/DeterministicBuild/ClassLibrary1/ClassLibrary1.csproj
+++ b/Documentation/Examples/MSBuild/DeterministicBuild/ClassLibrary1/ClassLibrary1.csproj
@@ -2,6 +2,7 @@
netstandard2.0
+ false
diff --git a/Documentation/Examples/MSBuild/DeterministicBuild/DeterministicBuild.sln b/Documentation/Examples/MSBuild/DeterministicBuild/DeterministicBuild.sln
index e5ea12eb9..958518178 100644
--- a/Documentation/Examples/MSBuild/DeterministicBuild/DeterministicBuild.sln
+++ b/Documentation/Examples/MSBuild/DeterministicBuild/DeterministicBuild.sln
@@ -9,7 +9,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClassLibrary1", "ClassLibra
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8AE3B75E-33BA-4E07-AD78-2DBCC3392262}"
ProjectSection(SolutionItems) = preProject
- DeterministicBuild.targets = DeterministicBuild.targets
Directory.Build.props = Directory.Build.props
Directory.Build.targets = Directory.Build.targets
HowTo.md = HowTo.md
diff --git a/Documentation/Examples/MSBuild/DeterministicBuild/DeterministicBuild.targets b/Documentation/Examples/MSBuild/DeterministicBuild/DeterministicBuild.targets
deleted file mode 100644
index 13208d103..000000000
--- a/Documentation/Examples/MSBuild/DeterministicBuild/DeterministicBuild.targets
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
- $([System.IO.Path]::Combine('$(IntermediateOutputPath)','$(TargetFrameworkMoniker).AssemblyAttributes$(DefaultLanguageSourceExtension)'))
-
-
-
-
-
-
-
-
-
-
- <_LocalTopLevelSourceRoot Include="@(SourceRoot)" Condition="'%(SourceRoot.NestedRoot)' == ''"/>
-
-
-
diff --git a/Documentation/Examples/MSBuild/DeterministicBuild/Directory.Build.props b/Documentation/Examples/MSBuild/DeterministicBuild/Directory.Build.props
index 5c27e11f6..34a4ddf95 100644
--- a/Documentation/Examples/MSBuild/DeterministicBuild/Directory.Build.props
+++ b/Documentation/Examples/MSBuild/DeterministicBuild/Directory.Build.props
@@ -8,6 +8,9 @@
-
+
+ all
+ runtime; build; native; contentfiles; analyzers
+
diff --git a/Documentation/Examples/MSBuild/DeterministicBuild/Directory.Build.targets b/Documentation/Examples/MSBuild/DeterministicBuild/Directory.Build.targets
index 95978e1e1..faf2349ba 100644
--- a/Documentation/Examples/MSBuild/DeterministicBuild/Directory.Build.targets
+++ b/Documentation/Examples/MSBuild/DeterministicBuild/Directory.Build.targets
@@ -1,4 +1,3 @@
-
-
+
diff --git a/Documentation/Examples/MSBuild/DeterministicBuild/HowTo.md b/Documentation/Examples/MSBuild/DeterministicBuild/HowTo.md
index 44b9f2b1f..60ddebbef 100644
--- a/Documentation/Examples/MSBuild/DeterministicBuild/HowTo.md
+++ b/Documentation/Examples/MSBuild/DeterministicBuild/HowTo.md
@@ -1,9 +1,10 @@
-To run test we need to generates packages to reference in on test project.
+To run test we need to generates packages to reference in on test project.
Run from repo root
-```
+
+```shell
C:\git\coverlet
λ dotnet pack
-Microsoft (R) Build Engine version 16.5.0+d4cbfca49 for .NET Core
+Microsoft (R) Build Engine version 17.7.4+3ebbd7c49 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.
Restore completed in 73,36 ms for C:\git\coverlet\src\coverlet.core\coverlet.core.csproj.
@@ -23,31 +24,34 @@ Copyright (C) Microsoft Corporation. All rights reserved.
coverlet.core -> C:\git\coverlet\src\coverlet.core\bin\Debug\netstandard2.0\coverlet.core.dll
coverlet.collector -> C:\git\coverlet\src\coverlet.collector\bin\Debug\netcoreapp2.0\coverlet.collector.dll
coverlet.msbuild.tasks -> C:\git\coverlet\src\coverlet.msbuild.tasks\bin\Debug\netstandard2.0\coverlet.msbuild.tasks.dll
- coverlet.console -> C:\git\coverlet\src\coverlet.console\bin\Debug\netcoreapp2.2\coverlet.console.dll
- coverlet.console -> C:\git\coverlet\src\coverlet.console\bin\Debug\netcoreapp2.2\coverlet.console.dll
- Successfully created package 'C:\git\coverlet\bin\Debug\Packages\coverlet.collector.1.3.0-preview.6.ga0e22ec622.nupkg'.
- Successfully created package 'C:\git\coverlet\bin\Debug\Packages\coverlet.console.1.7.2-preview.6.ga0e22ec622.nupkg'.
- Successfully created package 'C:\git\coverlet\bin\Debug\Packages\coverlet.console.1.7.2-preview.6.ga0e22ec622.snupkg'.
- Successfully created package 'C:\git\coverlet\bin\Debug\Packages\coverlet.collector.1.3.0-preview.6.ga0e22ec622.snupkg'.
- Successfully created package 'C:\git\coverlet\bin\Debug\Packages\coverlet.msbuild.2.9.0-preview.6.ga0e22ec622.nupkg'.
- Successfully created package 'C:\git\coverlet\bin\Debug\Packages\coverlet.msbuild.2.9.0-preview.6.ga0e22ec622.snupkg'.
+ coverlet.console -> C:\git\coverlet\src\coverlet.console\bin\Debug\net6.0\coverlet.console.dll
+ Successfully created package 'C:\git\coverlet\bin\Debug\Packages\coverlet.collector.6.0.1-preview.6.g918cd179e0.nupkg'.
+ Successfully created package 'C:\git\coverlet\bin\Debug\Packages\coverlet.collector.6.0.1-preview.6.g918cd179e0.snupkg'.
+ Successfully created package 'C:\git\coverlet\bin\Debug\Packages\coverlet.msbuild.6.0.1-preview.6.g918cd179e0.nupkg'.
+ Successfully created package 'C:\git\coverlet\bin\Debug\Packages\coverlet.msbuild.6.0.1-preview.6.g918cd179e0.snupkg'.
+ Successfully created package 'C:\git\coverlet\bin\Debug\Packages\coverlet.console.6.0.1-preview.6.g918cd179e0.nupkg'.
+ Successfully created package 'C:\git\coverlet\bin\Debug\Packages\coverlet.console.6.0.1-preview.6.g918cd179e0.snupkg'.
```
+
Add msbuild package version generated to `"..\Documentation\Examples\MSBuild\DeterministicBuild\XUnitTestProject1\XUnitTestProject1.csproj"`
+
```xml
- netcoreapp3.1
+ net6.0
false
-
-
-
-
-
-
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
@@ -58,14 +62,15 @@ Add msbuild package version generated to `"..\Documentation\Examples\MSBuild\Det
-
```
+
Go to test project folder and run
-```
+
+```shell
C:\git\coverlet\Documentation\Examples\MSBuild\DeterministicBuild (detbuilddocs -> origin)
λ dotnet test /p:CollectCoverage=true /p:DeterministicSourcePaths=true
-Test run for C:\git\coverlet\Documentation\Examples\MSBuild\DeterministicBuild\XUnitTestProject1\bin\Debug\netcoreapp3.1\XUnitTestProject1.dll(.NETCoreApp,Version=v3.1)
-Microsoft (R) Test Execution Command Line Tool Version 16.5.0
+Test run for C:\git\coverlet\Documentation\Examples\MSBuild\DeterministicBuild\XUnitTestProject1\bin\Debug\net6.0\XUnitTestProject1.dll(.NETCoreApp,Version=v6.0)
+Microsoft (R) Test Execution Command Line Tool Version 17.5.0
Copyright (c) Microsoft Corporation. All rights reserved.
Starting test execution, please wait...
@@ -94,8 +99,11 @@ Calculating coverage result...
| Average | 100% | 100% | 100% |
+---------+------+--------+--------+
```
-You should see on output folder the coverlet source root mapping file generated.
-This is the confirmation that you're running coverage on deterministic build.
+
+You should see on output folder the coverlet source root mapping file generated. The filename starts with 'CoverletSourceRootsMapping_'. Do not use `--no-build` option
+This is the confirmation that you're running coverage on deterministic build e.g. `Documentation\Examples\MSBuild\DeterministicBuild\XUnitTestProject1\bin\Debug\net6.0\CoverletSourceRootsMapping_XUnitTestProject1`
+
+
+```text
+Documentation\Examples\MSBuild\DeterministicBuild\XUnitTestProject1\bin\Debug\net6.0\CoverletSourceRootsMapping_XUnitTestProject1
```
-Documentation\Examples\MSBuild\DeterministicBuild\XUnitTestProject1\bin\Debug\netcoreapp3.1\CoverletSourceRootsMapping
-```
\ No newline at end of file
diff --git a/Documentation/Examples/MSBuild/DeterministicBuild/XUnitTestProject1/XUnitTestProject1.csproj b/Documentation/Examples/MSBuild/DeterministicBuild/XUnitTestProject1/XUnitTestProject1.csproj
index de3e88f32..19586970c 100644
--- a/Documentation/Examples/MSBuild/DeterministicBuild/XUnitTestProject1/XUnitTestProject1.csproj
+++ b/Documentation/Examples/MSBuild/DeterministicBuild/XUnitTestProject1/XUnitTestProject1.csproj
@@ -1,15 +1,18 @@
- netcoreapp3.1
+ net6.0
false
+ false
-
-
-
-
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/Documentation/Examples/MSBuild/MergeWith/ClassLibrary1/Class1.cs b/Documentation/Examples/MSBuild/MergeWith/ClassLibrary1/Class1.cs
index 289865e7b..dcb976188 100644
--- a/Documentation/Examples/MSBuild/MergeWith/ClassLibrary1/Class1.cs
+++ b/Documentation/Examples/MSBuild/MergeWith/ClassLibrary1/Class1.cs
@@ -1,8 +1,6 @@
-using System;
-
-namespace ClassLibrary1
+namespace ClassLibrary1
{
- public class Class1
+ public class Class1
{
public int Method()
{
diff --git a/Documentation/Examples/MSBuild/MergeWith/ClassLibrary2/Class2.cs b/Documentation/Examples/MSBuild/MergeWith/ClassLibrary2/Class2.cs
index aa93f5552..a2b5a7acc 100644
--- a/Documentation/Examples/MSBuild/MergeWith/ClassLibrary2/Class2.cs
+++ b/Documentation/Examples/MSBuild/MergeWith/ClassLibrary2/Class2.cs
@@ -1,8 +1,6 @@
-using System;
-
-namespace ClassLibrary2
+namespace ClassLibrary2
{
- public class Class2
+ public class Class2
{
public int Method()
{
diff --git a/Documentation/Examples/MSBuild/MergeWith/ClassLibrary3/Class3.cs b/Documentation/Examples/MSBuild/MergeWith/ClassLibrary3/Class3.cs
index 872291eb2..cfecc19cf 100644
--- a/Documentation/Examples/MSBuild/MergeWith/ClassLibrary3/Class3.cs
+++ b/Documentation/Examples/MSBuild/MergeWith/ClassLibrary3/Class3.cs
@@ -1,8 +1,6 @@
-using System;
-
-namespace ClassLibrary3
+namespace ClassLibrary3
{
- public class Class3
+ public class Class3
{
public int Method()
{
diff --git a/Documentation/Examples/MSBuild/MergeWith/HowTo.md b/Documentation/Examples/MSBuild/MergeWith/HowTo.md
index c157b7b80..4135a1379 100644
--- a/Documentation/Examples/MSBuild/MergeWith/HowTo.md
+++ b/Documentation/Examples/MSBuild/MergeWith/HowTo.md
@@ -1,9 +1,9 @@
**Run from solution root sln**
-To merge report togheter you need to run separate test and merge in one `json` format file.
+To merge report together you need to run separate test and merge in one `json` format file.
Last command will join and create final needed format file.
-```
+```shell
dotnet test XUnitTestProject1\XUnitTestProject1.csproj /p:CollectCoverage=true /p:CoverletOutput=../CoverageResults/
dotnet test XUnitTestProject2\XUnitTestProject2.csproj /p:CollectCoverage=true /p:CoverletOutput=../CoverageResults/ /p:MergeWith="../CoverageResults/coverage.json"
dotnet test XUnitTestProject3\XUnitTestProject3.csproj /p:CollectCoverage=true /p:CoverletOutput=../CoverageResults/ /p:MergeWith="../CoverageResults/coverage.json" /p:CoverletOutputFormat="opencover"
@@ -11,13 +11,14 @@ dotnet test XUnitTestProject3\XUnitTestProject3.csproj /p:CollectCoverage=true
You can merge also running `dotnet test` and merge with single command from a solution file, but you need to ensure that tests will run sequentially(`-m:1`). This slow down testing but avoid invalid coverage result.
-```
+```shell
dotnet test /p:CollectCoverage=true /p:CoverletOutput=../CoverageResults/ /p:MergeWith="../CoverageResults/coverage.json" /p:CoverletOutputFormat=\"opencover,json\" -m:1
```
+
N.B. You need to specify `json` format plus another format(the final one), because Coverlet can only merge proprietary format. At the end you can delete temporary `coverage.json` file.
You can also merge the coverage result and generate another valid format to export the content than opencover, like cobertura.
-```
+```shell
dotnet test /p:CollectCoverage=true /p:CoverletOutput=../CoverageResults/ /p:MergeWith="../CoverageResults/coverage.json" /p:CoverletOutputFormat=\"cobertura,json\" -m:1
```
diff --git a/Documentation/Examples/MSBuild/MergeWith/XUnitTestProject1/UnitTest1.cs b/Documentation/Examples/MSBuild/MergeWith/XUnitTestProject1/UnitTest1.cs
index 5eeb5df3b..47655c8e7 100644
--- a/Documentation/Examples/MSBuild/MergeWith/XUnitTestProject1/UnitTest1.cs
+++ b/Documentation/Examples/MSBuild/MergeWith/XUnitTestProject1/UnitTest1.cs
@@ -1,9 +1,8 @@
-using System;
-using Xunit;
+using Xunit;
namespace XUnitTestProject1
{
- public class UnitTest1
+ public class UnitTest1
{
[Fact]
public void Test1()
diff --git a/Documentation/Examples/MSBuild/MergeWith/XUnitTestProject1/XUnitTestProject1.csproj b/Documentation/Examples/MSBuild/MergeWith/XUnitTestProject1/XUnitTestProject1.csproj
index 76ecf3d44..e78fcdd22 100644
--- a/Documentation/Examples/MSBuild/MergeWith/XUnitTestProject1/XUnitTestProject1.csproj
+++ b/Documentation/Examples/MSBuild/MergeWith/XUnitTestProject1/XUnitTestProject1.csproj
@@ -1,18 +1,22 @@
-
+
- netcoreapp3.1
+ net6.0
false
+ false
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers
+
diff --git a/Documentation/Examples/MSBuild/MergeWith/XUnitTestProject2/UnitTest2.cs b/Documentation/Examples/MSBuild/MergeWith/XUnitTestProject2/UnitTest2.cs
index c6ce4b13d..48a8448a2 100644
--- a/Documentation/Examples/MSBuild/MergeWith/XUnitTestProject2/UnitTest2.cs
+++ b/Documentation/Examples/MSBuild/MergeWith/XUnitTestProject2/UnitTest2.cs
@@ -1,9 +1,8 @@
-using System;
-using Xunit;
+using Xunit;
namespace XUnitTestProject2
{
- public class UnitTest2
+ public class UnitTest2
{
[Fact]
public void Test2()
diff --git a/Documentation/Examples/MSBuild/MergeWith/XUnitTestProject2/XUnitTestProject2.csproj b/Documentation/Examples/MSBuild/MergeWith/XUnitTestProject2/XUnitTestProject2.csproj
index 6c938c759..4346c22c3 100644
--- a/Documentation/Examples/MSBuild/MergeWith/XUnitTestProject2/XUnitTestProject2.csproj
+++ b/Documentation/Examples/MSBuild/MergeWith/XUnitTestProject2/XUnitTestProject2.csproj
@@ -1,18 +1,22 @@
-
+
- netcoreapp3.1
+ net6.0
false
+ false
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers
+
diff --git a/Documentation/Examples/MSBuild/MergeWith/XUnitTestProject3/UnitTest3.cs b/Documentation/Examples/MSBuild/MergeWith/XUnitTestProject3/UnitTest3.cs
index 482d9ed81..34cb51c16 100644
--- a/Documentation/Examples/MSBuild/MergeWith/XUnitTestProject3/UnitTest3.cs
+++ b/Documentation/Examples/MSBuild/MergeWith/XUnitTestProject3/UnitTest3.cs
@@ -1,9 +1,8 @@
-using System;
-using Xunit;
+using Xunit;
namespace XUnitTestProject3
{
- public class UnitTest3
+ public class UnitTest3
{
[Fact]
public void Test3()
diff --git a/Documentation/Examples/MSBuild/MergeWith/XUnitTestProject3/XUnitTestProject3.csproj b/Documentation/Examples/MSBuild/MergeWith/XUnitTestProject3/XUnitTestProject3.csproj
index d5267e6a2..4cdea2dbd 100644
--- a/Documentation/Examples/MSBuild/MergeWith/XUnitTestProject3/XUnitTestProject3.csproj
+++ b/Documentation/Examples/MSBuild/MergeWith/XUnitTestProject3/XUnitTestProject3.csproj
@@ -1,18 +1,22 @@
-
+
- netcoreapp3.1
+ net6.0
false
+ false
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers
+
diff --git a/Documentation/Examples/VSTest/DeterministicBuild/ClassLibrary1/ClassLibrary1.csproj b/Documentation/Examples/VSTest/DeterministicBuild/ClassLibrary1/ClassLibrary1.csproj
index 9f5c4f4ab..8271e5471 100644
--- a/Documentation/Examples/VSTest/DeterministicBuild/ClassLibrary1/ClassLibrary1.csproj
+++ b/Documentation/Examples/VSTest/DeterministicBuild/ClassLibrary1/ClassLibrary1.csproj
@@ -2,6 +2,7 @@
netstandard2.0
+ false
diff --git a/Documentation/Examples/VSTest/DeterministicBuild/DeterministicBuild.sln b/Documentation/Examples/VSTest/DeterministicBuild/DeterministicBuild.sln
index e5ea12eb9..958518178 100644
--- a/Documentation/Examples/VSTest/DeterministicBuild/DeterministicBuild.sln
+++ b/Documentation/Examples/VSTest/DeterministicBuild/DeterministicBuild.sln
@@ -9,7 +9,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClassLibrary1", "ClassLibra
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8AE3B75E-33BA-4E07-AD78-2DBCC3392262}"
ProjectSection(SolutionItems) = preProject
- DeterministicBuild.targets = DeterministicBuild.targets
Directory.Build.props = Directory.Build.props
Directory.Build.targets = Directory.Build.targets
HowTo.md = HowTo.md
diff --git a/Documentation/Examples/VSTest/DeterministicBuild/DeterministicBuild.targets b/Documentation/Examples/VSTest/DeterministicBuild/DeterministicBuild.targets
deleted file mode 100644
index 13208d103..000000000
--- a/Documentation/Examples/VSTest/DeterministicBuild/DeterministicBuild.targets
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
- $([System.IO.Path]::Combine('$(IntermediateOutputPath)','$(TargetFrameworkMoniker).AssemblyAttributes$(DefaultLanguageSourceExtension)'))
-
-
-
-
-
-
-
-
-
-
- <_LocalTopLevelSourceRoot Include="@(SourceRoot)" Condition="'%(SourceRoot.NestedRoot)' == ''"/>
-
-
-
diff --git a/Documentation/Examples/VSTest/DeterministicBuild/Directory.Build.targets b/Documentation/Examples/VSTest/DeterministicBuild/Directory.Build.targets
index 95978e1e1..8c119d541 100644
--- a/Documentation/Examples/VSTest/DeterministicBuild/Directory.Build.targets
+++ b/Documentation/Examples/VSTest/DeterministicBuild/Directory.Build.targets
@@ -1,4 +1,2 @@
-
-
diff --git a/Documentation/Examples/VSTest/DeterministicBuild/HowTo.md b/Documentation/Examples/VSTest/DeterministicBuild/HowTo.md
index 96d94ef1b..c25b9cd50 100644
--- a/Documentation/Examples/VSTest/DeterministicBuild/HowTo.md
+++ b/Documentation/Examples/VSTest/DeterministicBuild/HowTo.md
@@ -1,6 +1,7 @@
-To run test we need to generates packages to reference in on test project.
+To run test we need to generates packages to reference in on test project.
Run from repo root
-```
+
+```shell
C:\git\coverlet
λ dotnet pack
Microsoft (R) Build Engine version 16.5.0+d4cbfca49 for .NET Core
@@ -23,30 +24,34 @@ Copyright (C) Microsoft Corporation. All rights reserved.
coverlet.core -> C:\git\coverlet\src\coverlet.core\bin\Debug\netstandard2.0\coverlet.core.dll
coverlet.collector -> C:\git\coverlet\src\coverlet.collector\bin\Debug\netcoreapp2.0\coverlet.collector.dll
coverlet.msbuild.tasks -> C:\git\coverlet\src\coverlet.msbuild.tasks\bin\Debug\netstandard2.0\coverlet.msbuild.tasks.dll
- coverlet.console -> C:\git\coverlet\src\coverlet.console\bin\Debug\netcoreapp2.2\coverlet.console.dll
- coverlet.console -> C:\git\coverlet\src\coverlet.console\bin\Debug\netcoreapp2.2\coverlet.console.dll
- Successfully created package 'C:\git\coverlet\bin\Debug\Packages\coverlet.collector.1.3.0-preview.6.ga0e22ec622.nupkg'.
- Successfully created package 'C:\git\coverlet\bin\Debug\Packages\coverlet.console.1.7.2-preview.6.ga0e22ec622.nupkg'.
- Successfully created package 'C:\git\coverlet\bin\Debug\Packages\coverlet.console.1.7.2-preview.6.ga0e22ec622.snupkg'.
- Successfully created package 'C:\git\coverlet\bin\Debug\Packages\coverlet.collector.1.3.0-preview.6.ga0e22ec622.snupkg'.
- Successfully created package 'C:\git\coverlet\bin\Debug\Packages\coverlet.msbuild.2.9.0-preview.6.ga0e22ec622.nupkg'.
- Successfully created package 'C:\git\coverlet\bin\Debug\Packages\coverlet.msbuild.2.9.0-preview.6.ga0e22ec622.snupkg'.
+ coverlet.console -> C:\git\coverlet\src\coverlet.console\bin\Debug\net6.0\coverlet.console.dll
+ Successfully created package 'C:\git\coverlet\bin\Debug\Packages\coverlet.collector.6.0.1-preview.6.g918cd179e0.nupkg'.
+ Successfully created package 'C:\git\coverlet\bin\Debug\Packages\coverlet.collector.6.0.1-preview.6.g918cd179e0.snupkg'.
+ Successfully created package 'C:\git\coverlet\bin\Debug\Packages\coverlet.msbuild.6.0.1-preview.6.g918cd179e0.nupkg'.
+ Successfully created package 'C:\git\coverlet\bin\Debug\Packages\coverlet.msbuild.6.0.1-preview.6.g918cd179e0.snupkg'.
+ Successfully created package 'C:\git\coverlet\bin\Debug\Packages\coverlet.console.6.0.1-preview.6.g918cd179e0.nupkg'.
+ Successfully created package 'C:\git\coverlet\bin\Debug\Packages\coverlet.console.6.0.1-preview.6.g918cd179e0.snupkg'.
```
+
Add collectors package version generated to `"..\Documentation\Examples\VSTest\DeterministicBuild\XUnitTestProject1\XUnitTestProject1.csproj"`
+
```xml
- netcoreapp3.1
+ net6.0
false
-
-
-
-
-
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers
+
+
+
@@ -56,12 +61,14 @@ Add collectors package version generated to `"..\Documentation\Examples\VSTest\D
```
+
Go to test project folder and run
-```
+
+```shell
C:\git\coverlet\Documentation\Examples\VSTest\DeterministicBuild (detbuilddocs -> origin)
λ dotnet test --collect:"XPlat Code Coverage" /p:DeterministicSourcePaths=true -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.DeterministicReport=true
-Test run for C:\git\coverlet\Documentation\Examples\VSTest\DeterministicBuild\XUnitTestProject1\bin\Debug\netcoreapp3.1\XUnitTestProject1.dll(.NETCoreApp,Version=v3.1)
-Microsoft (R) Test Execution Command Line Tool Version 16.5.0
+Test run for C:\git\coverlet\Documentation\Examples\VSTest\DeterministicBuild\XUnitTestProject1\bin\Debug\netcoreapp3.1\XUnitTestProject1.dll(.NETCoreApp,Version=v6.0)
+Microsoft (R) Test Execution Command Line Tool Version 17.5.0
Copyright (c) Microsoft Corporation. All rights reserved.
Starting test execution, please wait...
@@ -75,8 +82,10 @@ Total tests: 1
Passed: 1
Total time: 1,3472 Seconds
```
-You should see on output folder the coverlet source root mapping file generated.
+
+You should see on output folder the coverlet source root mapping file generated.
This is the confirmation that you're running coverage on deterministic build.
+
+```text
+Documentation\Examples\VSTest\DeterministicBuild\XUnitTestProject1\bin\Debug\net6.0\CoverletSourceRootsMapping_XUnitTestProject1
```
-Documentation\Examples\VSTest\DeterministicBuild\XUnitTestProject1\bin\Debug\netcoreapp3.1\CoverletSourceRootsMapping_XUnitTestProject1
-```
\ No newline at end of file
diff --git a/Documentation/Examples/VSTest/DeterministicBuild/XUnitTestProject1/XUnitTestProject1.csproj b/Documentation/Examples/VSTest/DeterministicBuild/XUnitTestProject1/XUnitTestProject1.csproj
index ea3c58201..d63628b1e 100644
--- a/Documentation/Examples/VSTest/DeterministicBuild/XUnitTestProject1/XUnitTestProject1.csproj
+++ b/Documentation/Examples/VSTest/DeterministicBuild/XUnitTestProject1/XUnitTestProject1.csproj
@@ -1,14 +1,18 @@
-
+
- netcoreapp3.1
+ net6.0
false
+ false
-
-
-
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers
+
diff --git a/Documentation/Examples/VSTest/HelloWorld/ClassLibrary1/Class1.cs b/Documentation/Examples/VSTest/HelloWorld/ClassLibrary1/Class1.cs
index 289865e7b..dcb976188 100644
--- a/Documentation/Examples/VSTest/HelloWorld/ClassLibrary1/Class1.cs
+++ b/Documentation/Examples/VSTest/HelloWorld/ClassLibrary1/Class1.cs
@@ -1,8 +1,6 @@
-using System;
-
-namespace ClassLibrary1
+namespace ClassLibrary1
{
- public class Class1
+ public class Class1
{
public int Method()
{
diff --git a/Documentation/Examples/VSTest/HelloWorld/ClassLibrary1/ClassLibrary1.csproj b/Documentation/Examples/VSTest/HelloWorld/ClassLibrary1/ClassLibrary1.csproj
index 9f5c4f4ab..8271e5471 100644
--- a/Documentation/Examples/VSTest/HelloWorld/ClassLibrary1/ClassLibrary1.csproj
+++ b/Documentation/Examples/VSTest/HelloWorld/ClassLibrary1/ClassLibrary1.csproj
@@ -2,6 +2,7 @@
netstandard2.0
+ false
diff --git a/Documentation/Examples/VSTest/HelloWorld/HowTo.md b/Documentation/Examples/VSTest/HelloWorld/HowTo.md
index 548105f9f..d965d05e1 100644
--- a/Documentation/Examples/VSTest/HelloWorld/HowTo.md
+++ b/Documentation/Examples/VSTest/HelloWorld/HowTo.md
@@ -1,11 +1,11 @@
**Run from XUnitTestProject1 folder**
-```
+```shell
dotnet test --collect:"XPlat Code Coverage"
```
With custom runsettings file
-```
+```shell
dotnet test --collect:"XPlat Code Coverage" --settings runsettings.xml
-```
\ No newline at end of file
+```
diff --git a/Documentation/Examples/VSTest/HelloWorld/XUnitTestProject1/runsettings.xml b/Documentation/Examples/VSTest/HelloWorld/XUnitTestProject1/.runsettings
similarity index 100%
rename from Documentation/Examples/VSTest/HelloWorld/XUnitTestProject1/runsettings.xml
rename to Documentation/Examples/VSTest/HelloWorld/XUnitTestProject1/.runsettings
diff --git a/Documentation/Examples/VSTest/HelloWorld/XUnitTestProject1/UnitTest1.cs b/Documentation/Examples/VSTest/HelloWorld/XUnitTestProject1/UnitTest1.cs
index 5eeb5df3b..47655c8e7 100644
--- a/Documentation/Examples/VSTest/HelloWorld/XUnitTestProject1/UnitTest1.cs
+++ b/Documentation/Examples/VSTest/HelloWorld/XUnitTestProject1/UnitTest1.cs
@@ -1,9 +1,8 @@
-using System;
-using Xunit;
+using Xunit;
namespace XUnitTestProject1
{
- public class UnitTest1
+ public class UnitTest1
{
[Fact]
public void Test1()
diff --git a/Documentation/Examples/VSTest/HelloWorld/XUnitTestProject1/XUnitTestProject1.csproj b/Documentation/Examples/VSTest/HelloWorld/XUnitTestProject1/XUnitTestProject1.csproj
index 3000c34e8..3ab4b931e 100644
--- a/Documentation/Examples/VSTest/HelloWorld/XUnitTestProject1/XUnitTestProject1.csproj
+++ b/Documentation/Examples/VSTest/HelloWorld/XUnitTestProject1/XUnitTestProject1.csproj
@@ -1,15 +1,20 @@
- netcoreapp3.1
+ net6.0
false
+ false
+ $(MSBuildThisFileDirectory).runsettings
-
-
-
-
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/Documentation/GlobalTool.md b/Documentation/GlobalTool.md
index 455f34b39..99a9f9b4c 100644
--- a/Documentation/GlobalTool.md
+++ b/Documentation/GlobalTool.md
@@ -8,8 +8,8 @@ coverlet --help
The current options are (output of `coverlet --help`):
-```bash
-Cross platform .NET Core code coverage tool 3.0.0.0
+```text
+Cross platform .NET Core code coverage tool 6.0.0.0
Usage: coverlet [arguments] [options]
@@ -17,16 +17,14 @@ Arguments:
Path to the test assembly or application directory.
Options:
- -h|--help Show help information
- -v|--version Show version information
- -t|--target Path to the test runner application.
+ -t|--target (REQUIRED) Path to the test runner application.
-a|--targetargs Arguments to be passed to the test runner.
-o|--output Output of the generated coverage report
-v|--verbosity Sets the verbosity level of the command. Allowed values are quiet, minimal, normal, detailed.
- -f|--format Format of the generated coverage report.
+ -f|--format Format of the generated coverage report. [default: json]
--threshold Exits with error if the coverage % is below value.
--threshold-type Coverage type to apply the threshold to.
- --threshold-stat Coverage statistic used to enforce the threshold value.
+ --threshold-stat Coverage statistic used to enforce the threshold value. [default: Minimum]
--exclude Filter expressions to exclude specific modules and types.
--include Filter expressions to include only specific modules and types.
--exclude-by-file Glob patterns specifying source files to exclude.
@@ -39,12 +37,23 @@ Options:
--use-source-link Specifies whether to use SourceLink URIs in place of file system paths.
--does-not-return-attribute Attributes that mark methods that do not return.
--exclude-assemblies-without-sources Specifies behaviour of heuristic to ignore assemblies with missing source documents.
+ --source-mapping-file Specifies the path to a SourceRootsMappings file.
+ --version Show version information
+ -?, -h, --help Show help and usage information
```
-NB. For a [multiple value] options you have to specify values multiple times i.e.
+NB. For [multiple value] options you can either specify values multiple times i.e.
+
+```shell
+--exclude-by-attribute 'Obsolete' --exclude-by-attribute 'GeneratedCode' --exclude-by-attribute 'CompilerGenerated'
```
---exclude-by-attribute 'Obsolete' --exclude-by-attribute'GeneratedCode' --exclude-by-attribute 'CompilerGenerated'
+
+or pass the multiple values as space separated sequence, i.e.
+
+```shell
+--exclude-by-attribute "Obsolete" "GeneratedCode" "CompilerGenerated"
```
+
For `--merge-with` [check the sample](Examples.md).
## Code Coverage
@@ -61,7 +70,7 @@ After the above command is run, a `coverage.json` file containing the results wi
_Note: The `--no-build` flag is specified so that the `/path/to/test-assembly.dll` isn't rebuilt_
-## Code Coverage for integration tests and end-to-end tests.
+## Code Coverage for integration tests and end-to-end tests
Sometimes, there are tests that doesn't use regular unit test frameworks like xunit. You may find yourself in a situation where your tests are driven by a custom executable/script, which when run, could do anything from making API calls to driving Selenium.
@@ -105,6 +114,9 @@ coverlet --target --targetargs --output "/custo
The above command will write the results to the supplied path, if no file extension is specified it'll use the standard extension of the selected output format. To specify a directory instead, simply append a `/` to the end of the value.
+>[!TIP]
+>Use only folder name whenever multiple coverage output formats are used.
+
```bash
coverlet --target --targetargs --output "/custom/directory/" -f json -f lcov
```
@@ -179,7 +191,10 @@ coverlet --target --targetargs --threshold 80 -
You can ignore a method or an entire class from code coverage by creating and applying the `ExcludeFromCodeCoverage` attribute present in the `System.Diagnostics.CodeAnalysis` namespace.
-You can also ignore additional attributes by using the `ExcludeByAttribute` property (short name or full name supported):
+You can also ignore additional attributes by using the `ExcludeByAttribute` property
+
+* Can be specified multiple times
+* Use attribute name, attribute full name or fully qualified name of the attribute type (`Obsolete`, `ObsoleteAttribute`, `System.ObsoleteAttribute`)
```bash
coverlet --target --targetargs --exclude-by-attribute 'Obsolete' --exclude-by-attribute 'GeneratedCode' --exclude-by-attribute 'CompilerGenerated'
@@ -188,8 +203,9 @@ coverlet --target --targetargs --exclude-by-att
### Source Files
You can also ignore specific source files from code coverage using the `--exclude-by-file` option
- - Can be specified multiple times
- - Use file path or directory path with globbing (e.g `dir1/*.cs`)
+
+* Can be specified multiple times
+* Use file path or directory path with globbing (e.g `dir1/*.cs`)
```bash
coverlet --target --targetargs --exclude-by-file "**/dir1/class1.cs"
@@ -202,15 +218,17 @@ Coverlet gives the ability to have fine grained control over what gets excluded
Syntax: `--exclude '[Assembly-Filter]Type-Filter'`
Wildcards
-- `*` => matches zero or more characters
-- `?` => the prefixed character is optional
+
+* `*` => matches zero or more characters
+* `?` => the prefixed character is optional
Examples
- - `--exclude "[*]*"` => Excludes all types in all assemblies (nothing is instrumented)
- - `--exclude "[coverlet.*]Coverlet.Core.Coverage"` => Excludes the Coverage class in the `Coverlet.Core` namespace belonging to any assembly that matches `coverlet.*` (e.g `coverlet.core`)
- - `--exclude "[*]Coverlet.Core.Instrumentation.*"` => Excludes all types belonging to `Coverlet.Core.Instrumentation` namespace in any assembly
- - `--exclude "[coverlet.*.tests?]*"` => Excludes all types in any assembly starting with `coverlet.` and ending with `.test` or `.tests` (the `?` makes the `s` optional)
- - `--exclude "[coverlet.*]*" --exclude "[*]Coverlet.Core*"` => Excludes assemblies matching `coverlet.*` and excludes all types belonging to the `Coverlet.Core` namespace in any assembly
+
+* `--exclude "[*]*"` => Excludes all types in all assemblies (nothing is instrumented)
+* `--exclude "[coverlet.*]Coverlet.Core.Coverage"` => Excludes the Coverage class in the `Coverlet.Core` namespace belonging to any assembly that matches `coverlet.*` (e.g `coverlet.core`)
+* `--exclude "[*]Coverlet.Core.Instrumentation.*"` => Excludes all types belonging to `Coverlet.Core.Instrumentation` namespace in any assembly
+* `--exclude "[coverlet.*.tests?]*"` => Excludes all types in any assembly starting with `coverlet.` and ending with `.test` or `.tests` (the `?` makes the `s` optional)
+* `--exclude "[coverlet.*]*" --exclude "[*]Coverlet.Core*"` => Excludes assemblies matching `coverlet.*` and excludes all types belonging to the `Coverlet.Core` namespace in any assembly
```bash
coverlet --target --targetargs --exclude "[coverlet.*]Coverlet.Core.Coverage"
@@ -219,9 +237,10 @@ coverlet --target --targetargs --exclude "[cove
Coverlet goes a step in the other direction by also letting you explicitly set what can be included using the `--include` option.
Examples
- - `--include "[*]*"` => Includes all types in all assemblies (everything is instrumented)
- - `--include "[coverlet.*]Coverlet.Core.Coverage"` => Includes the Coverage class in the `Coverlet.Core` namespace belonging to any assembly that matches `coverlet.*` (e.g `coverlet.core`)
- - `--include "[coverlet.*.tests?]*"` => Includes all types in any assembly starting with `coverlet.` and ending with `.test` or `.tests` (the `?` makes the `s` optional)
+
+* `--include "[*]*"` => Includes all types in all assemblies (everything is instrumented)
+* `--include "[coverlet.*]Coverlet.Core.Coverage"` => Includes the Coverage class in the `Coverlet.Core` namespace belonging to any assembly that matches `coverlet.*` (e.g `coverlet.core`)
+* `--include "[coverlet.*.tests?]*"` => Includes all types in any assembly starting with `coverlet.` and ending with `.test` or `.tests` (the `?` makes the `s` optional)
Both `--exclude` and `--include` options can be used together but `--exclude` takes precedence. You can specify the `--exclude` and `--include` options multiple times to allow for multiple filter expressions.
@@ -231,6 +250,18 @@ You can also include coverage of the test assembly itself by specifying the `--i
Coverlet supports [SourceLink](https://github.com/dotnet/sourcelink) custom debug information contained in PDBs. When you specify the `--use-source-link` flag, Coverlet will generate results that contain the URL to the source files in your source control instead of local file paths.
+## Path Mappings
+
+Coverlet has the ability to map the paths contained inside the debug sources into a local path where the source is currently located using the option `--source-mapping-file`. This is useful if the source was built using a deterministic build which sets the path to `/_/` or if it was built on a different host where the source is located in a different path.
+
+The value for `--source-mapping-file` should be a file with each line being in the format `|path to map to=path in debug symbol`. For example to map the local checkout of a project `C:\git\coverlet` to project that was built with `true` which sets the sources to `/_/*` the following line must be in the mapping file.
+
+```
+|C:\git\coverlet\=/_/
+```
+
+During coverage collection, Coverlet will translate any path that starts with `/_/` to `C:\git\coverlet\` allowing the collector to find the source file.
+
## Exit Codes
Coverlet outputs specific exit codes to better support build automation systems for determining the kind of failure so the appropriate action can be taken.
diff --git a/Documentation/KnownIssues.md b/Documentation/KnownIssues.md
index 0b5b842ec..b4c53e15e 100644
--- a/Documentation/KnownIssues.md
+++ b/Documentation/KnownIssues.md
@@ -2,7 +2,7 @@
## VSTest stops process execution early
-*Affected drivers*: msbuild (`dotnet test`) , dotnet tool(if you're using ` --targetargs "test ... --no-build"`)
+*Affected drivers*: msbuild (`dotnet test`) , dotnet tool(if you're using `--targetargs "test ... --no-build"`)
*Symptoms:*
@@ -12,12 +12,12 @@
`warning : [coverlet] Hits file:'C:\Users\REDACTED\AppData\Local\Temp\testApp_ac32258b-fd4a-4bb4-824c-a79061e97c31' not found for module: 'testApp'`
-* zero coverage result (often only on CI but not on local)
+* zero coverage result (often only on CI but not on local)
```bash
Calculating coverage result...
- C:\Users\REDACTED\.nuget\packages\coverlet.msbuild\2.6.0\build\netstandard2.0\coverlet.msbuild.targets(21,5): warning : [coverlet] Hits file:'C:\Users\REDACTED\AppData\Local\Temp\testApp_ac32258b-fd4a-4bb4-824c-a79061e97c31' not found for module: 'testApp' [C:\Users\REDACTED\Documents\repo\testapp\testapp.Tests\testapp.Tests.csproj]
- C:\Users\REDACTED\.nuget\packages\coverlet.msbuild\2.6.0\build\netstandard2.0\coverlet.msbuild.targets(21,5): warning : [coverlet] Hits file:'C:\Users\REDACTED\AppData\Local\Temp\testApp.Tests_ac32258b-fd4a-4bb4-824c-a79061e97c31' not found for module: 'testApp.Tests' [C:\Users\REDACTED\Documents\repo\testapp\testapp.Tests\testapp.Tests.csproj]
+ C:\Users\REDACTED\.nuget\packages\coverlet.msbuild\6.0.0\build\netstandard2.0\coverlet.msbuild.targets(21,5): warning : [coverlet] Hits file:'C:\Users\REDACTED\AppData\Local\Temp\testApp_ac32258b-fd4a-4bb4-824c-a79061e97c31' not found for module: 'testApp' [C:\Users\REDACTED\Documents\repo\testapp\testapp.Tests\testapp.Tests.csproj]
+ C:\Users\REDACTED\.nuget\packages\coverlet.msbuild\6.0.0\build\netstandard2.0\coverlet.msbuild.targets(21,5): warning : [coverlet] Hits file:'C:\Users\REDACTED\AppData\Local\Temp\testApp.Tests_ac32258b-fd4a-4bb4-824c-a79061e97c31' not found for module: 'testApp.Tests' [C:\Users\REDACTED\Documents\repo\testapp\testapp.Tests\testapp.Tests.csproj]
Generating report 'C:\Users\REDACTED\Documents\repo\testapp\lcov.info'
+---------------+------+--------+--------+
@@ -37,7 +37,7 @@
+---------+------+--------+--------+
```
-The issue is related to VSTest platform https://github.com/microsoft/vstest/issues/1900#issuecomment-457488472
+The issue is related to VSTest platform
> However if testhost doesn't shut down within 100ms (as the execution is completed, we expect it to shutdown fast). vstest.console forcefully kills the process.
@@ -46,7 +46,7 @@ This happen also if there are other "piece of code" during testing that slow dow
*Solution:*
-The only way to get around this issue is to use collectors integration https://github.com/coverlet-coverage/coverlet#vstest-integration-preferred-due-to-known-issue-supports-only-net-core-application. With the collector, we're injected in test host through a in-proc collector that communicates with the VSTest platform so we can signal when we end our work.
+The only way to get around this issue is to use collectors integration . With the collector, we're injected in test host through a in-proc collector that communicates with the VSTest platform so we can signal when we end our work.
## Upgrade `coverlet.collector` to version > 1.0.0
@@ -54,44 +54,46 @@ The only way to get around this issue is to use collectors integration https://g
*Symptoms:* The same as "known issue 1".
-There is a bug inside the VSTest platform: https://github.com/microsoft/vstest/issues/2205.
+There is a bug inside the VSTest platform: .
If you upgrade the collector package with a version greater than 1.0.0, in-proc collector won't be loaded so you could incur "known issue number 1" and get zero coverage result
*Solutions:*
-1) Reference `Microsoft.NET.Test.Sdk` with version *greater than* 16.4.0
-
-```xml
-
- ...
-
- ...
-
-```
-2) You can pass custom *coverage.runsettings* file like this
-```xml
-
-
-
-
-
-
- cobertura
-
-
-
-
-
-
-
-
-
-
-```
+1) Reference `Microsoft.NET.Test.Sdk` with version *greater than* 17.7.0
+
+ ```xml
+
+ ...
+
+ ...
+
+ ```
+
+1) You can pass custom *coverage.runsettings* file like this
+
+ ```xml
+
+
+
+
+
+
+ cobertura
+
+
+
+
+
+
+
+
+
+
+ ```
And pass it to command line
@@ -103,11 +105,11 @@ dotnet test --settings coverage.runsettings
*Affected drivers*: all drivers that support `/p:UseSourceLink=true`
-*Symptoms:* some tool like SonarSource doesn't work well see https://github.com/coverlet-coverage/coverlet/issues/482
+*Symptoms:* some tool like SonarSource doesn't work well see
-`Nerdbank.GitVersioning` generates a version file on the fly but this file is not part of user solution and it's not commited to repo so the generated remote source file reference does not exit, i.e.
+`Nerdbank.GitVersioning` generates a version file on the fly but this file is not part of user solution and it's not committed to repo so the generated remote source file reference does not exit, i.e.
-```
+```text
...
...
@@ -116,7 +118,10 @@ dotnet test --settings coverage.runsettings
*Solution:* we can exclude `Nerdbank.GitVersioning` autogenerated file from instrumentation using filters
```bash
+# example for coverlet.msbuild
/p:ExcludeByFile=\"**/*Json.Version.cs\"
+# example for coverlet.collector
+dotnet test test-assembly.dll /collect:"XPlat Code Coverage" -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.ExcludeByFile="**/*.Version.cs"
```
## Failed to resolve assembly during instrumentation
@@ -125,8 +130,8 @@ dotnet test --settings coverage.runsettings
*Symptoms:* during build/instrumentation you may get an exception like:
-```
-[coverlet] Unable to instrument module: ..\UnitTests\bin\Debug\netcoreapp2.1\Core.Messaging.dll because : Failed to resolve assembly: 'Microsoft.Azure.ServiceBus, Version=3.4.0.0, Culture=neutral, PublicKeyToken=7e34167dcc6d6d8c' [..\UnitTests.csproj]
+```text
+[coverlet] Unable to instrument module: ..\UnitTests\bin\Debug\net6.0\Core.Messaging.dll because : Failed to resolve assembly: 'Microsoft.Azure.ServiceBus, Version=7.16.1.0, Culture=neutral, PublicKeyToken=7e34167dcc6d6d8c' [..\UnitTests.csproj]
```
In the instrumentation phase, Coverlet needs to load all references used by your instrumented module. Sometimes the build phase (out of Coverlet's control) does not copy those dlls to the output folder because they are not resolved till runtime or at publish phase from the NuGet packages folders.
@@ -148,13 +153,13 @@ or by adding the property `` to the project file
```
-NB. This **DOESN'T ALWAYS WORK**, for example in case of the shared framework https://github.com/dotnet/cli/issues/12705#issuecomment-536686785
+NB. This **DOESN'T ALWAYS WORK**, for example in case of the shared framework
We can do nothing at the moment as this is a build behaviour out of our control.
-For .NET runtime version >= 3.0 the new default behavior is to copy all assets to the build output (CopyLocalLockFileAssemblies=true) https://github.com/dotnet/cli/issues/12705#issuecomment-535150372, unfortunately the issue could still arise.
+For .NET runtime version >= 3.0 the new default behavior is to copy all assets to the build output (CopyLocalLockFileAssemblies=true) , unfortunately the issue could still arise.
-In this case the only workaround at the moment is to *manually copy* missing dlls to the output folder: https://github.com/coverlet-coverage/coverlet/issues/560#issue-496440052
+In this case the only workaround at the moment is to *manually copy* missing dlls to the output folder:
> The only reliable way to work around this problem is to drop the DLL in the unit tests project's bin\Release\netcoreapp2.2 directory.
@@ -164,11 +169,11 @@ In this case the only workaround at the moment is to *manually copy* missing dll
*Symptoms:* Running coverage on .NET Framework runtime(i.e. .NET 4.6.1) and get error like:
-```
+```text
Failed Tests.MinMax.Min_AsyncSelector_Int32_4
Error Message:
System.TypeInitializationException : The type initializer for 'Tests.AsyncEnumerableTests' threw an exception.
----- System.IO.FileLoadException : Could not load file or assembly 'System.Linq.Async, Version=4.0.0.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263' or one of its dependencies. Strong name signature could not be verified. The assembly may have been tampered with, or it was delay signed but not fully signed with the correct private key. (Exception from HRESULT: 0x80131045)
+---- System.IO.FileLoadException : Could not load file or assembly 'System.Linq.Async, Version=6.0.0.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263' or one of its dependencies. Strong name signature could not be verified. The assembly may have been tampered with, or it was delay signed but not fully signed with the correct private key. (Exception from HRESULT: 0x80131045)
Stack Trace:
at Tests.AsyncEnumerableTests..ctor()
at Tests.MinMax..ctor()
@@ -178,13 +183,11 @@ Stack Trace:
*Solution:* Looks like this is caused by xUnit's app domains. For `dotnet test`, it can be disabled with the following argument: `-- RunConfiguration.DisableAppDomain=true`
-NB. Workaround doesn't work if test method itself explicitly creates an appdomain and uses shadow copying in order to test that the assembly behaves properly in those conditions.
-
-## Code coverage returns NaN%
+## Code coverage returns NaN
*Symptoms:* You are getting following result when running Coverlet within CI/CD pipeline:
-```
+```text
+--------+------+--------+--------+
| Module | Line | Branch | Method |
+--------+------+--------+--------+
@@ -207,27 +210,74 @@ SUT (System Under Test) assembly is also not listed in MSBuild logs - "Instrumen
*Symptoms:*
```log
-C:\Users\REDACTED\.nuget\packages\coverlet.msbuild\3.2.0\build\coverlet.msbuild.targets(39,5): error : Stream was too long. [REDACTED.csproj]
-C:\Users\REDACTED\.nuget\packages\coverlet.msbuild\3.2.0\build\coverlet.msbuild.targets(39,5): error : at System.IO.MemoryStream.Write(Byte[] buffer, Int32 offset, Int32 count) [REDACTED.csproj]
-C:\Users\REDACTED\.nuget\packages\coverlet.msbuild\3.2.0\build\coverlet.msbuild.targets(39,5): error : at System.Xml.XmlStreamNodeWriter.FlushBuffer() [REDACTED.csproj]
-C:\Users\REDACTED\.nuget\packages\coverlet.msbuild\3.2.0\build\coverlet.msbuild.targets(39,5): error : at System.Xml.XmlStreamNodeWriter.GetBuffer(Int32 count, Int32& offset) [REDACTED.csproj]
-C:\Users\REDACTED\.nuget\packages\coverlet.msbuild\3.2.0\build\coverlet.msbuild.targets(39,5): error : at System.Xml.XmlStreamNodeWriter.UnsafeWriteUTF8Chars(Char* chars, Int32 charCount) [REDACTED.csproj]
-C:\Users\REDACTED\.nuget\packages\coverlet.msbuild\3.2.0\build\coverlet.msbuild.targets(39,5): error : at System.Xml.XmlUTF8NodeWriter.WriteEscapedText(String s) [REDACTED.csproj]
-C:\Users\REDACTED\.nuget\packages\coverlet.msbuild\3.2.0\build\coverlet.msbuild.targets(39,5): error : at System.Xml.XmlBaseWriter.WriteString(String value) [REDACTED.csproj]
-C:\Users\REDACTED\.nuget\packages\coverlet.msbuild\3.2.0\build\coverlet.msbuild.targets(39,5): error : at System.Runtime.Serialization.XmlObjectSerializerWriteContext.WriteString(XmlWriterDelegator xmlWriter, String value, XmlDictionaryString name, XmlDictionaryString ns) [REDACTED.csproj]
-C:\Users\REDACTED\.nuget\packages\coverlet.msbuild\3.2.0\build\coverlet.msbuild.targets(39,5): error : at WriteLineToXml(XmlWriterDelegator , Object , XmlObjectSerializerWriteContext , ClassDataContract ) [REDACTED.csproj]
+C:\Users\REDACTED\.nuget\packages\coverlet.msbuild\6.0.0\build\coverlet.msbuild.targets(39,5): error : Stream was too long. [REDACTED.csproj]
+C:\Users\REDACTED\.nuget\packages\coverlet.msbuild\6.0.0\build\coverlet.msbuild.targets(39,5): error : at System.IO.MemoryStream.Write(Byte[] buffer, Int32 offset, Int32 count) [REDACTED.csproj]
+C:\Users\REDACTED\.nuget\packages\coverlet.msbuild\6.0.0\build\coverlet.msbuild.targets(39,5): error : at System.Xml.XmlStreamNodeWriter.FlushBuffer() [REDACTED.csproj]
+C:\Users\REDACTED\.nuget\packages\coverlet.msbuild\6.0.0\build\coverlet.msbuild.targets(39,5): error : at System.Xml.XmlStreamNodeWriter.GetBuffer(Int32 count, Int32& offset) [REDACTED.csproj]
+C:\Users\REDACTED\.nuget\packages\coverlet.msbuild\6.0.0\build\coverlet.msbuild.targets(39,5): error : at System.Xml.XmlStreamNodeWriter.UnsafeWriteUTF8Chars(Char* chars, Int32 charCount) [REDACTED.csproj]
+C:\Users\REDACTED\.nuget\packages\coverlet.msbuild\6.0.0\build\coverlet.msbuild.targets(39,5): error : at System.Xml.XmlUTF8NodeWriter.WriteEscapedText(String s) [REDACTED.csproj]
+C:\Users\REDACTED\.nuget\packages\coverlet.msbuild\6.0.0\build\coverlet.msbuild.targets(39,5): error : at System.Xml.XmlBaseWriter.WriteString(String value) [REDACTED.csproj]
+C:\Users\REDACTED\.nuget\packages\coverlet.msbuild\6.0.0\build\coverlet.msbuild.targets(39,5): error : at System.Runtime.Serialization.XmlObjectSerializerWriteContext.WriteString(XmlWriterDelegator xmlWriter, String value, XmlDictionaryString name, XmlDictionaryString ns) [REDACTED.csproj]
+C:\Users\REDACTED\.nuget\packages\coverlet.msbuild\6.0.0\build\coverlet.msbuild.targets(39,5): error : at WriteLineToXml(XmlWriterDelegator , Object , XmlObjectSerializerWriteContext , ClassDataContract ) [REDACTED.csproj]
....
Calculating coverage result...
-C:\Users\REDACTED\.nuget\packages\coverlet.msbuild\3.2.0\build\coverlet.msbuild.targets(71,5): error : Unexpected end of file. [REDACTED.csproj]
-C:\Users\REDACTED\.nuget\packages\coverlet.msbuild\3.2.0\build\coverlet.msbuild.targets(71,5): error : at System.Xml.EncodingStreamWrapper.ReadBOMEncoding(Boolean notOutOfBand) [REDACTED.csproj]
-C:\Users\REDACTED\.nuget\packages\coverlet.msbuild\3.2.0\build\coverlet.msbuild.targets(71,5): error : at System.Xml.EncodingStreamWrapper..ctor(Stream stream, Encoding encoding) [REDACTED.csproj]
-C:\Users\REDACTED\.nuget\packages\coverlet.msbuild\3.2.0\build\coverlet.msbuild.targets(71,5): error : at System.Xml.XmlUTF8TextReader.SetInput(Stream stream, Encoding encoding, XmlDictionaryReaderQuotas quotas, OnXmlDictionaryReaderClose onClose) [REDACTED.csproj]
+C:\Users\REDACTED\.nuget\packages\coverlet.msbuild\6.0.0\build\coverlet.msbuild.targets(71,5): error : Unexpected end of file. [REDACTED.csproj]
+C:\Users\REDACTED\.nuget\packages\coverlet.msbuild\6.0.0\build\coverlet.msbuild.targets(71,5): error : at System.Xml.EncodingStreamWrapper.ReadBOMEncoding(Boolean notOutOfBand) [REDACTED.csproj]
+C:\Users\REDACTED\.nuget\packages\coverlet.msbuild\6.0.0\build\coverlet.msbuild.targets(71,5): error : at System.Xml.EncodingStreamWrapper..ctor(Stream stream, Encoding encoding) [REDACTED.csproj]
+C:\Users\REDACTED\.nuget\packages\coverlet.msbuild\6.0.0\build\coverlet.msbuild.targets(71,5): error : at System.Xml.XmlUTF8TextReader.SetInput(Stream stream, Encoding encoding, XmlDictionaryReaderQuotas quotas, OnXmlDictionaryReaderClose onClose) [REDACTED.csproj]
```
The XML code coverage report is too large for the coverlet to parse.
*Potential Solutions:*
-* Reduce noise from auto generated code, for example excluding your EntityFrameworkCore Migrations namespace or automatically generated typed Http Clients. See [Excluding From Coverage](./MSBuildIntegration.md#excluding-from-coverage) for more information on ignoring namespaces from code coverage.
+* Reduce noise from auto generated code, for example excluding your EntityFrameworkCore Migrations namespace or automatically generated typed Http Clients. See [Excluding From Coverage](./MSBuildIntegration.md#excluding-from-coverage) for more information on ignoring namespaces from code coverage.
+
+## BadImageFormatException .NET Framework 4.7.x, 4.8.x
+
+*Symptoms:*
+
+```text
+BadImageFormatException during MetadataReaderProvider.FromPortablePdbStream in InstrumentationHelper.PortablePdbHasLocalSource, unable to check if the module has got local source.
+```
+
+*Solutions:*
+
+Change [DebugType](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-options/code-generation#debugtype) from `full` to `portable`.
+
+>[!IMPORTANT]
+>NET Core introduces a new symbol file (PDB) format - portable PDBs. Unlike traditional PDBs which are Windows-only, portable PDBs can be created and read on all platforms.
+
+## CoverletCoverageDataCollector: Failed to instrument modules
+
+*Affected drivers*: VSTest integration `dotnet test --collect:"XPlat Code Coverage"`
+
+*Symptoms:*
+
+```text
+Data collector 'XPlat code coverage' message: [coverlet]Coverlet.Collector.Utilities.CoverletDataCollectorException: CoverletCoverageDataCollector: Failed to instrument modules
+ ---> System.AggregateException: One or more errors occurred. (The process cannot access the file XXX\ABC.pdb
+ ---> System.IO.IOException: The process cannot access the file 'XXX\ABC.pdb' because it is being used by another process.
+ at System.IO.FileSystem.CopyFile(String sourceFullPath, String destFullPath, Boolean overwrite)
+ at Coverlet.Core.Helpers.FileSystem.Copy(String sourceFileName, String destFileName, Boolean overwrite) in /_/src/coverlet.core/Helpers/FileSystem.cs:line 35
+ at Coverlet.Core.Helpers.InstrumentationHelper.<>c__DisplayClass16_0.b__1() in /_/src/coverlet.core/Helpers/InstrumentationHelper.cs:line 277
+ at Coverlet.Core.Helpers.RetryHelper.<>c__DisplayClass0_0.b__0() in /_/src/coverlet.core/Helpers/RetryHelper.cs:line 28
+ at Coverlet.Core.Helpers.RetryHelper.Do[T](Func`1 action, Func`1 backoffStrategy, Int32 maxAttemptCount) in /_/src/coverlet.core/Helpers/RetryHelper.cs:line 55
+ --- End of inner exception stack trace ---
+ ...
+ ```
+
+ >[!Note]
+ >This is not an coverlet issue but running tests in parallel without proper separation of test case resources
+ >>
+ >>**dotnet vstest cli option**
+ >>
+ >>--Parallel
+ >>
+ >> Run tests in parallel. By default, all available cores on the machine are available for use. Specify an explicit number of cores by setting the MaxCpuCount property under the RunConfiguration node in the runsettings file.
+
+ *Solutions:*
+
+ use VSTest setting [-maxcpucount:1](https://learn.microsoft.com/en-us/visualstudio/msbuild/building-multiple-projects-in-parallel-with-msbuild?view=vs-2022#-maxcpucount-switch) which limits "worker processes".
diff --git a/Documentation/MSBuildIntegration.md b/Documentation/MSBuildIntegration.md
index a937665be..6cee59fa4 100644
--- a/Documentation/MSBuildIntegration.md
+++ b/Documentation/MSBuildIntegration.md
@@ -1,10 +1,11 @@
# Coverlet integration with MSBuild
-In this mode, Coverlet doesn't require any additional setup other than including the NuGet package in the unit test project. It integrates with the `dotnet test` infrastructure built into the .NET Core CLI and when enabled, will automatically generate coverage results after tests are run.
+In this mode, Coverlet doesn't require any additional setup other than including the `coverlet.msbuild` NuGet package in the unit test project. It integrates with the `dotnet test` infrastructure built into the .NET Core CLI and when enabled, will automatically generate coverage results after tests are run.
If a property takes multiple comma-separated values please note that [you will have to add escaped quotes around the string](https://github.com/Microsoft/msbuild/issues/2999#issuecomment-366078677) like this: `/p:Exclude=\"[coverlet.*]*,[*]Coverlet.Core*\"`, `/p:Include=\"[coverlet.*]*,[*]Coverlet.Core*\"`, or `/p:CoverletOutputFormat=\"json,opencover\"`.
-To achieve same behavior above using **powershell** you need to use the verbatim argument marker `--%` like this:
+To achieve same behavior above using **powershell** you need to use the verbatim argument marker `--%` like this:
+
```powershell
dotnet test /p:CollectCoverage=true --% /p:CoverletOutputFormat=\"opencover,lcov\"
```
@@ -49,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
@@ -103,7 +113,7 @@ The above command will automatically fail the build if the line, branch or metho
dotnet test /p:CollectCoverage=true /p:Threshold=80 /p:ThresholdType=line
```
-You can specify multiple values for `ThresholdType` by separating them with commas. Valid values include `line`, `branch` and `method`.
+You can specify multiple values for `ThresholdType` by separating them with commas. Valid values include `line`, `branch` and `method`.
You can do the same for `Threshold` as well.
```bash
@@ -128,7 +138,10 @@ dotnet test /p:CollectCoverage=true /p:Threshold=80 /p:ThresholdType=line /p:Thr
You can ignore a method, an entire class or assembly from code coverage by creating and applying the `ExcludeFromCodeCoverage` attribute present in the `System.Diagnostics.CodeAnalysis` namespace.
-You can also ignore additional attributes by using the `ExcludeByAttribute` property (short name, i.e. the type name without the namespace, supported only):
+You can also ignore additional attributes by using the `ExcludeByAttribute` property
+
+* Use single or multiple attributes (separate by comma)
+* Use attribute name, attribute full name or fully qualified name of the attribute type (`Obsolete`, `ObsoleteAttribute`, `System.ObsoleteAttribute`)
```bash
dotnet test /p:CollectCoverage=true /p:ExcludeByAttribute="Obsolete,GeneratedCodeAttribute,CompilerGeneratedAttribute"
@@ -137,8 +150,9 @@ dotnet test /p:CollectCoverage=true /p:ExcludeByAttribute="Obsolete,GeneratedCod
### Source Files
You can also ignore specific source files from code coverage using the `ExcludeByFile` property
- - Use single or multiple paths (separate by comma)
- - Use file path or directory path with globbing (e.g `dir1/*.cs`)
+
+* Use single or multiple paths (separate by comma)
+* Use file path or directory path with globbing (e.g `dir1/*.cs`)
```bash
dotnet test /p:CollectCoverage=true /p:ExcludeByFile=\"**/dir1/class1.cs,**/dir2/*.cs,**/dir3/**/*.cs\"
@@ -151,15 +165,17 @@ Coverlet gives the ability to have fine grained control over what gets excluded
Syntax: `/p:Exclude=[Assembly-Filter]Type-Filter`
Wildcards
-- `*` => matches zero or more characters
-- `?` => the prefixed character is optional
+
+* `*` => matches zero or more characters
+* `?` => the prefixed character is optional
Examples
- - `/p:Exclude="[*]*"` => Excludes all types in all assemblies (nothing is instrumented)
- - `/p:Exclude="[coverlet.*]Coverlet.Core.Coverage"` => Excludes the Coverage class in the `Coverlet.Core` namespace belonging to any assembly that matches `coverlet.*` (e.g `coverlet.core`)
- - `/p:Exclude="[*]Coverlet.Core.Instrumentation.*"` => Excludes all types belonging to `Coverlet.Core.Instrumentation` namespace in any assembly
- - `/p:Exclude="[coverlet.*.tests?]*"` => Excludes all types in any assembly starting with `coverlet.` and ending with `.test` or `.tests` (the `?` makes the `s` optional)
- - `/p:Exclude=\"[coverlet.*]*,[*]Coverlet.Core*\"` => Excludes assemblies matching `coverlet.*` and excludes all types belonging to the `Coverlet.Core` namespace in any assembly
+
+* `/p:Exclude="[*]*"` => Excludes all types in all assemblies (nothing is instrumented)
+* `/p:Exclude="[coverlet.*]Coverlet.Core.Coverage"` => Excludes the Coverage class in the `Coverlet.Core` namespace belonging to any assembly that matches `coverlet.*` (e.g `coverlet.core`)
+* `/p:Exclude="[*]Coverlet.Core.Instrumentation.*"` => Excludes all types belonging to `Coverlet.Core.Instrumentation` namespace in any assembly
+* `/p:Exclude="[coverlet.*.tests?]*"` => Excludes all types in any assembly starting with `coverlet.` and ending with `.test` or `.tests` (the `?` makes the `s` optional)
+* `/p:Exclude=\"[coverlet.*]*,[*]Coverlet.Core*\"` => Excludes assemblies matching `coverlet.*` and excludes all types belonging to the `Coverlet.Core` namespace in any assembly
```bash
dotnet test /p:CollectCoverage=true /p:Exclude="[coverlet.*]Coverlet.Core.Coverage"
@@ -168,20 +184,21 @@ dotnet test /p:CollectCoverage=true /p:Exclude="[coverlet.*]Coverlet.Core.Covera
Coverlet goes a step in the other direction by also letting you explicitly set what can be included using the `Include` property.
Examples
- - `/p:Include="[*]*"` => Includes all types in all assemblies (everything is instrumented)
- - `/p:Include="[coverlet.*]Coverlet.Core.Coverage"` => Includes the Coverage class in the `Coverlet.Core` namespace belonging to any assembly that matches `coverlet.*` (e.g `coverlet.core`)
- - `/p:Include="[coverlet.*.tests?]*"` => Includes all types in any assembly starting with `coverlet.` and ending with `.test` or `.tests` (the `?` makes the `s` optional)
+
+* `/p:Include="[*]*"` => Includes all types in all assemblies (everything is instrumented)
+* `/p:Include="[coverlet.*]Coverlet.Core.Coverage"` => Includes the Coverage class in the `Coverlet.Core` namespace belonging to any assembly that matches `coverlet.*` (e.g `coverlet.core`)
+* `/p:Include="[coverlet.*.tests?]*"` => Includes all types in any assembly starting with `coverlet.` and ending with `.test` or `.tests` (the `?` makes the `s` optional)
Both `Exclude` and `Include` properties can be used together but `Exclude` takes precedence. You can specify multiple filter expressions by separting them with a comma (`,`).
You can also include coverage of the test assembly itself by setting `/p:IncludeTestAssembly` to `true`.
-### Skip auto-implemented properties
+### Skip auto-implemented properties
-Neither track nor record auto-implemented properties.
+Neither track nor record auto-implemented properties.
Syntax: `/p:SkipAutoProps=true`
-### Instrument module wihtout local sources file.
+### Instrument module wihtout local sources file
Syntax: `/p:InstrumentModulesWithoutLocalSources=true`
@@ -200,7 +217,7 @@ To exclude or include multiple assemblies when using Powershell scripts or creat
Azure DevOps builds do not require double quotes to be unescaped:
-```
+```shell
dotnet test --configuration $(buildConfiguration) --no-build /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura /p:CoverletOutput=$(Build.SourcesDirectory)/TestResults/Coverage/ /p:Exclude="[MyAppName.DebugHost]*%2c[MyAppNamet.WebHost]*%2c[MyAppName.App]*"
```
@@ -223,16 +240,17 @@ Coverlet supports [SourceLink](https://github.com/dotnet/sourcelink) custom debu
## Deterministic build
-Take a look at [documentation](DeterministicBuild.md) for further informations.
-To generate deterministc report the parameter is:
-```
+Take a look at [documentation](DeterministicBuild.md) for further information.
+To generate deterministic report the parameter is:
+
+```shell
/p:DeterministicReport=true
```
## Exclude assemblies without sources from coverage
The heuristic coverlet uses to determine if an assembly is a third-party dependency is based on the matching of the assembly`s source documents and the corresponding source files.
-This parameter has three different values to control the automatic assembly exclusion.
+This parameter has three different values to control the automatic assembly exclusion.
| Parameter | Description |
|-----------|-------------|
@@ -241,6 +259,7 @@ This parameter has three different values to control the automatic assembly excl
| None | No assembly is excluded. |
Here is an example of how to specifiy the parameter:
-```
+
+```shell
/p:ExcludeAssembliesWithoutSources="MissingAny"
-```
\ No newline at end of file
+```
diff --git a/Documentation/ReleasePlan.md b/Documentation/ReleasePlan.md
index 3472cf8f6..42a87f13a 100644
--- a/Documentation/ReleasePlan.md
+++ b/Documentation/ReleasePlan.md
@@ -4,7 +4,7 @@
Coverlet is versioned with Semantic Versioning [2.0.0](https://semver.org/#semantic-versioning-200) that states:
-```
+```text
Given a version number MAJOR.MINOR.PATCH, increment the:
MAJOR version when you make incompatible API changes,
@@ -13,113 +13,87 @@ PATCH version when you make backwards-compatible bug fixes.
Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format.
```
-We release 3 components as NuGet packages:
-
-**coverlet.msbuild.nupkg**
-**coverlet.console.nupkg**
-**coverlet.collector.nupkg**
-
-### Current versions
-
-| Package | Version |
-|:----------------------|:--------|
-|**coverlet.msbuild** | 3.2.0 |
-|**coverlet.console** | 3.2.0 |
-|**coverlet.collector** | 3.2.0 |
-
-
-| Release Date | coverlet.msbuild | coverlet.console | coverlet.collector| commit hash | notes |
-| :-----------------|:-----------------|:------------------|:------------------|:-----------------------------------------|:-------------------------------|
-| 29 Oct 2022 | 3.2.0 | 3.2.0 | 3.2.0 | e2c9d84a84a9d2d240ac15feb70f9198c6f8e173 | |
-| 06 Feb 2022 | 3.1.2 | 3.1.2 | 3.1.2 | e335b1a8025e49e2f2de6b40ef12ec9d3ed11ceb | Fix CoreLib coverage issues |
-| 30 Jan 2022 | 3.1.1 | 3.1.1 | 3.1.1 | e4278c06faba63122a870df15a1a1b934f6bc81d | |
-| 19 July 2021 | 3.1.0 | 3.1.0 | 3.1.0 | 5a0ecc1e92fd754e2439dc3e4c828ff7386aa1a7 | Support for determistic build |
-| 21 February 2021 | 3.0.3 | 3.0.3 | 3.0.3 | adfabfd58de0aabe263e7d2080324e0b8541071e | Fix regressions |
-| 24 January 2021 | 3.0.2 | 3.0.2 | 3.0.2 | ed918515492193fd154b60270d440c40fa30fee9 | Fix regressions |
-| 16 January 2021 | 3.0.1 | 3.0.1 | 3.0.1 | 1b45fd89245369ae94407e7a77bdfee112042486 | Fix severe coverage regression |
-| 09 January 2021 | 3.0.0 | 3.0.0 | 3.0.0 | 1e77f9d2183a320e8991bfc296460e793301931f | Align versions numbers |
-| 30 May 2020 | 2.9.0 | 1.7.2 | 1.3.0 | 83a38d45b3f9c231d705bfed849efbf41b3aaa86 | deterministic build support |
-| 04 April 2020 | 2.8.1 | 1.7.1 | 1.2.1 | 3f81828821d07d756e02a4105b2533cedf0b543c | |
-| 03 January 2019 | 2.8.0 | 1.7.0 | 1.2.0 | 72a688f1c47fa92059540d5fbb1c4b0b4bf0dc8c | |
-| 23 September 2019 | 2.7.0 | 1.6.0 | 1.1.0 | 4ca01eb239038808739699470a61fad675af6c79 | |
-| 01 July 2019 | 2.6.3 | 1.5.3 | 1.0.1 | e1593359497fdfe6befbb86304b8f4e09a656d14 | |
-| 06 June 2019 | 2.6.2 | 1.5.2 | 1.0.0 | 3e7eac9df094c22335711a298d359890aed582e8 | first collector release |
-
-To get the list of commits between two version use git command
-```bash
-git log --oneline hashbefore currenthash
-```
-# How to manually compare latest release with nightly build
+We release 3 components as NuGet packages:
-Before creating a new release it makes sense to test the new release against a benchmark repository. This can help to determine bugs that haven't been found
-by the unit/integration tests. Therefore, coverage of the latest release is compared with our nightly build.
+**coverlet.msbuild.nupkg**
+**coverlet.console.nupkg**
+**coverlet.collector.nupkg**
-In the following example the benchmark repository refit (https://github.com/reactiveui/refit) is used which already uses coverlet for coverage.
+## How to manually compare latest release with nightly build
-1. Clone the benchmark repository (https://github.com/reactiveui/refit)
+Before creating a new release it makes sense to test the new release against a benchmark repository. This can help to determine bugs that haven't been found
+by the unit/integration tests. Therefore, coverage of the latest release is compared with our nightly build.
-2. Check if latest coverlet version is used by the project, otherwise add coverlet to the project (https://github.com/coverlet-coverage/coverlet#installation).
+In the following example the benchmark repository refit () is used which already uses coverlet for coverage.
+1. Clone the benchmark repository ()
+2. Check if latest coverlet version is used by the project, otherwise add coverlet to the project ().
3. Create coverage report for latest coverlet version:
-```
-dotnet test --collect:"XPlat Code Coverage"
-```
+
+ ```shell
+ dotnet test --collect:"XPlat Code Coverage"
+ ```
4. Update the test projects with the latest nightly build version of coverlet
-(https://github.com/coverlet-coverage/coverlet/blob/master/Documentation/ConsumeNightlyBuild.md).
+().
5. Create coverage report for nightly build version by rerunning the tests:
-```
-dotnet test --collect:"XPlat Code Coverage"
-```
-6. Check for differences in the coverage reports.
+ ```shell
+ dotnet test --collect:"XPlat Code Coverage"
+ ```
+6. Check for differences in the coverage reports.
-# How to manually release packages to nuget.org
+## How to manually release packages to nuget.org
This is the steps to release new packages to nuget.org
1. Update projects version in file `version.json` in root of repo (remove `-preview.{height}` and adjust version)
-Do a PR and merge to master.
+ Do a PR and merge to master.
-2. Clone repo, **remember to build packages from master and not from your fork or metadata links will point to your forked repo.** . Run `git log -5` from repo root to verify last commit.
+1. Clone repo, **remember to build packages from master and not from your fork or metadata links will point to your forked repo.** . Run `git log -5` from repo root to verify last commit.
-3. From new cloned, aligned and versions updated repo root run pack command
+1. From new cloned, aligned and versions updated repo root run build command
- ```
+ ```shell
dotnet pack -c release /p:TF_BUILD=true /p:PublicRelease=true
...
- coverlet.console -> D:\git\coverlet\src\coverlet.console\bin\Release\netcoreapp2.2\coverlet.console.dll
- coverlet.console -> D:\git\coverlet\src\coverlet.console\bin\Release\netcoreapp2.2\publish\
- Successfully created package 'D:\git\coverlet\bin\Release\Packages\coverlet.msbuild.2.8.1.nupkg'.
- Successfully created package 'D:\git\coverlet\bin\Release\Packages\coverlet.msbuild.2.8.1.snupkg'.
- Successfully created package 'D:\git\coverlet\bin\Release\Packages\coverlet.console.1.7.1.nupkg'.
- Successfully created package 'D:\git\coverlet\bin\Release\Packages\coverlet.console.1.7.1.snupkg'.
- Successfully created package 'D:\git\coverlet\bin\Release\Packages\coverlet.collector.1.2.1.nupkg'.
- Successfully created package 'D:\git\coverlet\bin\Release\Packages\coverlet.collector.1.2.1.snupkg'.
+ coverlet.core -> C:\GitHub\coverlet\artifacts\bin\coverlet.core\release_netstandard2.0\coverlet.core.dll
+ coverlet.core -> C:\GitHub\coverlet\artifacts\bin\coverlet.core\release_net6.0\coverlet.core.dll
+ coverlet.collector -> C:\GitHub\coverlet\artifacts\bin\coverlet.collector\release_netstandard2.0\coverlet.collector.dll
+ coverlet.collector -> C:\GitHub\coverlet\artifacts\bin\coverlet.collector\release_net6.0\coverlet.collector.dll
+ coverlet.msbuild.tasks -> C:\GitHub\coverlet\artifacts\bin\coverlet.msbuild.tasks\release_netstandard2.0\coverlet.msbuild.tasks.dll
+ coverlet.msbuild.tasks -> C:\GitHub\coverlet\artifacts\bin\coverlet.msbuild.tasks\release_net6.0\coverlet.msbuild.tasks.dll
+ coverlet.console -> C:\GitHub\coverlet\artifacts\bin\coverlet.console\release\coverlet.console.dll
+ coverlet.console -> C:\GitHub\coverlet\artifacts\bin\coverlet.console\release\coverlet.console.exe
+ ...
+ Successfully created package 'C:\GitHub\coverlet\artifacts\package\release\coverlet.msbuild.6.0.1.nupkg'.
+ Successfully created package 'C:\GitHub\coverlet\artifacts\package\release\coverlet.msbuild.6.0.1.snupkg'.
+ Successfully created package 'C:\GitHub\coverlet\artifacts\package\release\coverlet.collector.6.0.1.nupkg'.
+ Successfully created package 'C:\GitHub\coverlet\artifacts\package\release\coverlet.collector.6.0.1.snupkg'.
+ Successfully created package 'C:\GitHub\coverlet\artifacts\package\release\coverlet.console.6.0.1.nupkg'.
+ Successfully created package 'C:\GitHub\coverlet\artifacts\package\release\coverlet.console.6.0.1.snupkg'.
+ ...
```
-4. Sign the packages using SignClient tool https://www.nuget.org/packages/SignClient
-
- ```powershell
- ❯ SignClient "Sign" `
- >> --baseDirectory "REPO ROOT DIRECTORY\bin" `
- >> --input "**/*.nupkg" `
- >> --config "ROOT REPO DIRECTORY\eng\signclient.json" `
- >> --user "USER" `
- >> --secret "SECRET" `
- >> --name "Coverlet" `
- >> --description "Coverlet" `
- >> --descriptionUrl "https://github.com/coverlet-coverage/coverlet"
- ```
+1. Sign nuget packages using sign
+
+```powershell
+sign code azure-key-vault **/*.nupkg --base-directory [ROOT-DIRECTORY]\artifacts\package\release\ --file-digest sha256 --description Coverlet --description-url https://github.com/coverlet-coverage/coverlet `
+ --azure-key-vault-url [KEYVAULT-URL] `
+ --azure-key-vault-client-id [CLIENT-ID] `
+ --azure-key-vault-tenant-id [TENANT-ID] `
+ --azure-key-vault-client-secret [KEYVAULT-SECRET] `
+ --azure-key-vault-certificate [CERT-FRIENDLY-NAME]
+```
-5. Upload *.nupkg files to Nuget.org site. **Check all metadata(url links, deterministic build etc...) before "Submit"**
+1. Upload *.nupkg files to Nuget.org site. **Check all metadata(url links, deterministic build etc...) before "Submit"**
-6. **On your fork**:
- * Align to master
- * Bump version by one (fix part) and re-add `-preview.{height}`
- * Create release on repo https://github.com/coverlet-coverage/coverlet/releases
- * Update the [Release Plan](https://github.com/coverlet-coverage/coverlet/blob/master/Documentation/ReleasePlan.md)(this document) and [ChangeLog](https://github.com/coverlet-coverage/coverlet/blob/master/Documentation/Changelog.md)
- * Do PR and merge
+1. **On your fork**:
+ * Align to master
+ * Bump version by one (fix part) and re-add `-preview.{height}`
+ * Create release on repo
+ * Update the [Release Plan](https://github.com/coverlet-coverage/coverlet/blob/master/Documentation/ReleasePlan.md)(this document) and [ChangeLog](https://github.com/coverlet-coverage/coverlet/blob/master/Documentation/Changelog.md)
+ * Do PR and merge
diff --git a/Documentation/Roadmap.md b/Documentation/Roadmap.md
index a7ef5117b..6415247af 100644
--- a/Documentation/Roadmap.md
+++ b/Documentation/Roadmap.md
@@ -1,30 +1,30 @@
# Roadmap
-This document describes the roadmap for coverlet showing priorities.
-Maintain coverlet means like any other project two things, answer/resolve soon as possible new issues that are blocking our users an second improve product with new features and enhancements in different areas.
+This document describes the roadmap for coverlet showing priorities.
+Maintain coverlet means like any other project two things, answer/resolve soon as possible new issues that are blocking our users an second improve product with new features and enhancements in different areas.
All coverlet issues are labeled and categorized to better support this activites.
-As soon as an issue is open is labeled with `untriaged` if not immediately solvable(we need to do some debugging/PoC to understand where is the issue).
-After triage a final correct label is applied and will be taken into account depending on priority order.
+As soon as an issue is open is labeled with `untriaged` if not immediately solvable(we need to do some debugging/PoC to understand where is the issue).
+After triage a final correct label is applied and will be taken into account depending on priority order.
We use `needs more info` if we're waiting for answers.
Default priority order "should" be:
1) Bugs: we should fix bugs as soon as possible and for first bugs related to coverage because this is the goal of coverlet.
-Coverage bugs: https://github.com/coverlet-coverage/coverlet/issues?q=is%3Aissue+is%3Aopen+label%3Abug+label%3Atenet-coverage
-Other bugs: https://github.com/coverlet-coverage/coverlet/issues?q=is%3Aissue+is%3Aopen+label%3Abug
+ Coverage bugs:
+ Other bugs:
2) New features: analyze and add new features, we have three drivers so features could be related to one of these.
-Feature requests: https://github.com/coverlet-coverage/coverlet/issues?q=is%3Aissue+is%3Aopen+label%3Afeature-request
+ Feature requests:
3) Performance: we never worked on performance aspect of coverlet, it makes sense for a "new project with some hope", but today coverlet is the facto the dotnet coverage tool, so we HAVE TO approach this aspect.
-Performance issues: https://github.com/coverlet-coverage/coverlet/issues?q=is%3Aissue+is%3Aopen+label%3Atenet-performance
+Performance issues:
-Some new features have got a `Discussion` label if we don't have and agreement yet on semantics.
-Discussions: https://github.com/coverlet-coverage/coverlet/issues?q=is%3Aissue+is%3Aopen+label%3Adiscussion
+Some new features have got a `Discussion` label if we don't have and agreement yet on semantics.
+Discussions:
## New features roadmap
@@ -32,16 +32,14 @@ This is the list of features we should develop soon as possible:
### High priority
-- Allow merge reports solution wide on all flavours https://github.com/coverlet-coverage/coverlet/issues/662 https://github.com/coverlet-coverage/coverlet/issues/357
+- Allow merge reports solution wide on all flavours
-- Some perf improvements https://github.com/coverlet-coverage/coverlet/issues/836
+- Some perf improvements
### Low priority
-- Rethink hits reports strategy https://github.com/coverlet-coverage/coverlet/issues/808
+- Rethink hits reports strategy
## Maintainers discussion channel
[As maintainers we should try to find a way to keep in synch, we could use a chat where we can "take note" of progress and if possible answer to questions/doubt, I know this is OSS, but it's hard keep project at high level without "more ideas".]
-
-
diff --git a/Documentation/Troubleshooting.md b/Documentation/Troubleshooting.md
index f5df25ac8..5a3e682cc 100644
--- a/Documentation/Troubleshooting.md
+++ b/Documentation/Troubleshooting.md
@@ -4,45 +4,46 @@
1) Generate verbose log
-```
-dotnet test test\coverlet.core.tests\coverlet.core.tests.csproj -c debug /p:CollectCoverage=true /p:CoverletOutputFormat=opencover /p:Include=[coverlet.*]* -verbosity:diagnostic -bl:msbuild.binlog -noconsolelogger
-```
+ ```shell
+ dotnet test test\coverlet.core.tests\coverlet.core.tests.csproj -c debug /p:CollectCoverage=true /p:CoverletOutputFormat=opencover /p:Include=[coverlet.*]* -verbosity:diagnostic -bl:msbuild.binlog -noconsolelogger
+ ```
-2) Download http://msbuildlog.com/
+2) Download
3) Open `msbuild.binlog` generated and search for '[coverlet]' logs
-
-
+
+ 
## Collectors integration
-```
+```shell
dotnet test --collect:"XPlat Code Coverage" --settings runsettings --diag:log.txt
```
You'll get logs file in same folder similar to
-```
+```text
log.datacollector.19-09-12_14-55-17_64755_5.txt
log.host.19-09-12_14-55-18_82700_6.txt
log.txt
```
+
Search inside with filter '[coverlet]'
## Coverlet Global Tool
-```
-coverlet "C:\git\coverlet\test\coverlet.core.tests\bin\Debug\netcoreapp2.0\coverlet.core.tests.dll" --target "dotnet" --targetargs "test C:\git\coverlet\test\coverlet.core.tests --no-build" --verbosity detailed
+```shell
+coverlet "C:\git\coverlet\test\coverlet.core.tests\bin\Debug\net6.0\coverlet.core.tests.dll" --target "dotnet" --targetargs "test C:\git\coverlet\test\coverlet.core.tests --no-build" --verbosity detailed
```
Sample output
-```
+```text
...
-Instrumented module: 'C:\git\coverlet\test\coverlet.core.tests\bin\Debug\netcoreapp2.0\coverlet.core.dll'
-Instrumented module: 'C:\git\coverlet\test\coverlet.core.tests\bin\Debug\netcoreapp2.0\xunit.runner.reporters.netcoreapp10.dll'
-Instrumented module: 'C:\git\coverlet\test\coverlet.core.tests\bin\Debug\netcoreapp2.0\xunit.runner.utility.netcoreapp10.dll'
-Instrumented module: 'C:\git\coverlet\test\coverlet.core.tests\bin\Debug\netcoreapp2.0\xunit.runner.visualstudio.dotnetcore.testadapter.dll'
-Test run for C:\git\coverlet\test\coverlet.core.tests\bin\Debug\netcoreapp2.0\coverlet.core.tests.dll(.NETCoreApp,Version=v2.0)
+Instrumented module: 'C:\git\coverlet\test\coverlet.core.tests\bin\Debug\net6.0\coverlet.core.dll'
+Instrumented module: 'C:\git\coverlet\test\coverlet.core.tests\bin\Debug\net6.0\xunit.runner.reporters.netcoreapp10.dll'
+Instrumented module: 'C:\git\coverlet\test\coverlet.core.tests\bin\Debug\net6.0\xunit.runner.utility.netcoreapp10.dll'
+Instrumented module: 'C:\git\coverlet\test\coverlet.core.tests\bin\Debug\net6.0\xunit.runner.visualstudio.dotnetcore.testadapter.dll'
+Test run for C:\git\coverlet\test\coverlet.core.tests\bin\Debug\net6.0\coverlet.core.tests.dll(.NETCoreApp,Version=v2.0)
Microsoft (R) Test Execution Command Line Tool Version 16.0.1
Copyright (c) Microsoft Corporation. All rights reserved.
Starting test execution, please wait...
@@ -56,7 +57,7 @@ Test execution time: 4,6411 Seconds
Calculating coverage result...
Hits file:'C:\Users\Marco\AppData\Local\Temp\coverlet.core_703263e9-21f0-4d1c-9ce3-98ddeacecc01' not found for module: 'coverlet.core'
- Generating report 'C:\git\coverlet\src\coverlet.console\bin\Debug\netcoreapp2.2\coverage.json'
+ Generating report 'C:\git\coverlet\src\coverlet.console\bin\Debug\net6.0\coverage.json'
+--------------------------------------------------+--------+--------+--------+
| Module | Line | Branch | Method |
+--------------------------------------------------+--------+--------+--------+
@@ -80,116 +81,120 @@ Hits file:'C:\Users\Marco\AppData\Local\Temp\coverlet.core_703263e9-21f0-4d1c-9c
## Use local build (no collectors)
-Sometimes is useful test local updated source to fix issue.
+Sometimes is useful test local updated source to fix issue.
You can "load" your local build using simple switch:
* build repo
-```
-D:\git\coverlet (fixjsonserializerbug -> origin)
-λ dotnet build
-Microsoft (R) Build Engine version 16.1.76+g14b0a930a7 for .NET Core
-Copyright (C) Microsoft Corporation. All rights reserved.
-
- Restore completed in 52.23 ms for D:\git\coverlet\test\coverlet.testsubject\coverlet.testsubject.csproj.
- Restore completed in 58.97 ms for D:\git\coverlet\src\coverlet.console\coverlet.console.csproj.
- Restore completed in 59 ms for D:\git\coverlet\src\coverlet.core\coverlet.core.csproj.
- Restore completed in 59.17 ms for D:\git\coverlet\src\coverlet.msbuild.tasks\coverlet.msbuild.tasks.csproj.
- Restore completed in 59.26 ms for D:\git\coverlet\src\coverlet.collector\coverlet.collector.csproj.
- Restore completed in 60.1 ms for D:\git\coverlet\test\coverlet.collector.tests\coverlet.collector.tests.csproj.
- Restore completed in 60.42 ms for D:\git\coverlet\test\coverlet.core.performancetest\coverlet.core.performancetest.csproj.
- Restore completed in 60.47 ms for D:\git\coverlet\test\coverlet.core.tests\coverlet.core.tests.csproj.
- Restore completed in 22.85 ms for D:\git\coverlet\test\coverlet.core.tests\coverlet.core.tests.csproj.
- coverlet.testsubject -> D:\git\coverlet\test\coverlet.testsubject\bin\Debug\netcoreapp2.0\coverlet.testsubject.dll
- coverlet.core -> D:\git\coverlet\src\coverlet.core\bin\Debug\netstandard2.0\coverlet.core.dll
- coverlet.msbuild.tasks -> D:\git\coverlet\src\coverlet.msbuild.tasks\bin\Debug\netstandard2.0\coverlet.msbuild.tasks.dll
- coverlet.collector -> D:\git\coverlet\src\coverlet.collector\bin\Debug\netcoreapp2.0\coverlet.collector.dll
- coverlet.console -> D:\git\coverlet\src\coverlet.console\bin\Debug\netcoreapp2.2\coverlet.console.dll
- coverlet.core.performancetest -> D:\git\coverlet\test\coverlet.core.performancetest\bin\Debug\netcoreapp2.0\coverlet.core.performancetest.dll
- coverlet.core.tests -> D:\git\coverlet\test\coverlet.core.tests\bin\Debug\netcoreapp2.0\coverlet.core.tests.dll
- coverlet.collector.tests -> D:\git\coverlet\test\coverlet.collector.tests\bin\Debug\netcoreapp2.2\coverlet.collector.tests.dll
-
-Build succeeded.
- 0 Warning(s)
- 0 Error(s)
-
-Time Elapsed 00:00:07.42
-
-D:\git\coverlet (fixjsonserializerbug -> origin)
-```
+ ```text
+ D:\git\coverlet (fixjsonserializerbug -> origin)
+ λ dotnet build
+ Microsoft (R) Build Engine version 16.1.76+g14b0a930a7 for .NET Core
+ Copyright (C) Microsoft Corporation. All rights reserved.
+
+ Restore completed in 52.23 ms for D:\git\coverlet\test\coverlet.testsubject\coverlet.testsubject.csproj.
+ Restore completed in 58.97 ms for D:\git\coverlet\src\coverlet.console\coverlet.console.csproj.
+ Restore completed in 59 ms for D:\git\coverlet\src\coverlet.core\coverlet.core.csproj.
+ Restore completed in 59.17 ms for D:\git\coverlet\src\coverlet.msbuild.tasks\coverlet.msbuild.tasks.csproj.
+ Restore completed in 59.26 ms for D:\git\coverlet\src\coverlet.collector\coverlet.collector.csproj.
+ Restore completed in 60.1 ms for D:\git\coverlet\test\coverlet.collector.tests\coverlet.collector.tests.csproj.
+ Restore completed in 60.42 ms for D:\git\coverlet\test\coverlet.core.performancetest\coverlet.core.performancetest.csproj.
+ Restore completed in 60.47 ms for D:\git\coverlet\test\coverlet.core.tests\coverlet.core.tests.csproj.
+ Restore completed in 22.85 ms for D:\git\coverlet\test\coverlet.core.tests\coverlet.core.tests.csproj.
+ coverlet.testsubject -> D:\git\coverlet\test\coverlet.testsubject\bin\Debug\net6.0\coverlet.testsubject.dll
+ coverlet.core -> D:\git\coverlet\src\coverlet.core\bin\Debug\netstandard2.0\coverlet.core.dll
+ coverlet.msbuild.tasks -> D:\git\coverlet\src\coverlet.msbuild.tasks\bin\Debug\netstandard2.0\coverlet.msbuild.tasks.dll
+ coverlet.collector -> D:\git\coverlet\src\coverlet.collector\bin\Debug\net6.0\coverlet.collector.dll
+ coverlet.console -> D:\git\coverlet\src\coverlet.console\bin\Debug\net6.0\coverlet.console.dll
+ coverlet.core.performancetest -> D:\git\coverlet\test\coverlet.core.performancetest\bin\Debug\net6.0\coverlet.core.performancetest.dll
+ coverlet.core.tests -> D:\git\coverlet\test\coverlet.core.tests\bin\Debug\net6.0\coverlet.core.tests.dll
+ coverlet.collector.tests -> D:\git\coverlet\test\coverlet.collector.tests\bin\Debug\net6.0\coverlet.collector.tests.dll
+
+ Build succeeded.
+ 0 Warning(s)
+ 0 Error(s)
+
+ Time Elapsed 00:00:07.42
+
+ D:\git\coverlet (fixjsonserializerbug -> origin)
+ ```
* Go to repro project and run
-```
-D:\git\Cake.Codecov\Source\Cake.Codecov.Tests (develop -> origin)
-λ dotnet test /p:CollectCoverage=true /p:Exclude="[xunit.*]*" /p:CoverletToolsPath=D:\git\coverlet\src\coverlet.msbuild.tasks\bin\Debug\netstandard2.0\
-Test run for D:\git\Cake.Codecov\Source\Cake.Codecov.Tests\bin\Debug\netcoreapp2.0\Cake.Codecov.Tests.dll(.NETCoreApp,Version=v2.0)
-...
-```
+ ```text
+ D:\git\Cake.Codecov\Source\Cake.Codecov.Tests (develop -> origin)
+ λ dotnet test /p:CollectCoverage=true /p:Exclude="[xunit.*]*" /p:CoverletToolsPath=D:\git\coverlet\src\coverlet.msbuild.tasks\bin\Debug\netstandard2.0\
+ Test run for D:\git\Cake.Codecov\Source\Cake.Codecov.Tests\bin\Debug\netcoreapp2.0\Cake.Codecov.Tests.dll(.NETCoreApp,Version=v2.0)
+ ...
+ ```
In this way you can add `Debug.Launch()` inside coverlet source and debug.
-## Use local collectors build
+## Use local collectors build
To use/debug local collectors build we need to tell to our project to restore and use our local build nuget package.
1) Build local package
-```
-C:\git\coverlet\src\coverlet.collector (master -> origin)
-λ dotnet pack
-Microsoft (R) Build Engine version 16.2.32702+c4012a063 for .NET Core
-Copyright (C) Microsoft Corporation. All rights reserved.
-
- Restore completed in 50,28 ms for C:\git\coverlet\src\coverlet.collector\coverlet.collector.csproj.
- Restore completed in 50,28 ms for C:\git\coverlet\src\coverlet.core\coverlet.core.csproj.
- coverlet.core -> C:\git\coverlet\src\coverlet.core\bin\Debug\netstandard2.0\coverlet.core.dll
- coverlet.collector -> C:\git\coverlet\src\coverlet.collector\bin\Debug\netcoreapp2.0\coverlet.collector.dll
- Successfully created package 'C:\git\coverlet\bin\Debug\Packages\coverlet.collector.1.0.67.nupkg'.
- Successfully created package 'C:\git\coverlet\bin\Debug\Packages\coverlet.collector.1.0.67.snupkg'.
-```
+ ```text
+ C:\git\coverlet\src\coverlet.collector (master -> origin)
+ λ dotnet pack
+ Microsoft (R) Build Engine version 16.2.32702+c4012a063 for .NET Core
+ Copyright (C) Microsoft Corporation. All rights reserved.
+
+ Restore completed in 50,28 ms for C:\git\coverlet\src\coverlet.collector\coverlet.collector.csproj.
+ Restore completed in 50,28 ms for C:\git\coverlet\src\coverlet.core\coverlet.core.csproj.
+ coverlet.core -> C:\git\coverlet\src\coverlet.core\bin\Debug\netstandard2.0\coverlet.core.dll
+ coverlet.collector -> C:\git\coverlet\src\coverlet.collector\bin\Debug\netcoreapp2.0\coverlet.collector.dll
+ Successfully created package 'C:\git\coverlet\bin\Debug\Packages\coverlet.collector.1.0.67.nupkg'.
+ Successfully created package 'C:\git\coverlet\bin\Debug\Packages\coverlet.collector.1.0.67.snupkg'.
+ ```
+
2) Add new `NuGet.Config` file on your test project/solution
-```
-
-
-
-
-
-
-
-
-
-
-
-```
+
+ ```xml
+
+
+
+
+
+
+
+
+
+
+
+ ```
+
3) Update nuget package in our test project
-```xml
-
-
-
- netcoreapp2.2
- false
-
-
-
-
-
-
- <-- My local package version
-
-
-
-
-
-
-
-```
+
+ ```xml
+
+
+
+ net6.0
+ false
+
+
+
+
+
+
+ <-- My local package version -->
+
+
+
+
+
+
+
+ ```
4) Run test command
-```
- dotnet test XUnitTestProject1\ --collect:"XPlat Code Coverage"
-```
+ ```shell
+ dotnet test XUnitTestProject1\ --collect:"XPlat Code Coverage"
+ ```
You can also attach/debug your code adding some line of code on collectors i.e.
@@ -208,15 +213,15 @@ Fire attach
System.Diagnostics.Debugger.Launch();
```
-If you want debug in-process collector, you need to set VSTEST_HOST_DEBUG(https://github.com/microsoft/vstest/issues/2158) environment variable
+If you want debug in-process collector, you need to set [VSTEST_HOST_DEBUG](https://github.com/microsoft/vstest/issues/2158) environment variable
-```
+```shell
set VSTEST_HOST_DEBUG=1
```
Test host will wait for debugger
-```
+```text
Starting test execution, please wait...
Logging Vstest Diagnostics in file: C:\git\coverletissue\collectorlog\XUnitTestProject1\log.txt
Host debugging is enabled. Please attach debugger to testhost process to continue.
@@ -231,7 +236,7 @@ Coverlet works thanks to ModuleTracker that is injected during instrumentation f
We can collect logs from trackers through an enviroment variable
-```
+```shell
set COVERLET_ENABLETRACKERLOG=1
```
@@ -241,7 +246,7 @@ When enabled, tracking event will be collected in a log file near to module loca
You can live attach and debug msbuild tasks with `COVERLET_MSBUILD_INSTRUMENTATIONTASK_DEBUG` env variable
-```
+```shell
set COVERLET_MSBUILD_INSTRUMENTATIONTASK_DEBUG=1
```
@@ -249,14 +254,14 @@ You can live attach and debug msbuild tasks with `COVERLET_MSBUILD_INSTRUMENTATI
You can live attach and debug collectors with `COVERLET_DATACOLLECTOR_OUTOFPROC_DEBUG` and `COVERLET_DATACOLLECTOR_INPROC_DEBUG` env variable
-```
+```shell
set COVERLET_DATACOLLECTOR_OUTOFPROC_DEBUG=1
set COVERLET_DATACOLLECTOR_INPROC_DEBUG=1
```
-You will be asked to attach a debugger through UI popup.
+You will be asked to attach a debugger through UI popup.
To enable exceptions log for in-process data collectors
-```
+```shell
set COVERLET_DATACOLLECTOR_INPROC_EXCEPTIONLOG_ENABLED=1
```
diff --git a/Documentation/VSTestIntegration.md b/Documentation/VSTestIntegration.md
index 4cc67383d..3c8be75e1 100644
--- a/Documentation/VSTestIntegration.md
+++ b/Documentation/VSTestIntegration.md
@@ -2,28 +2,25 @@
**Supported runtime versions**:
-Before version `3.0.0`
-- .NET Core >= 2.0
-- .NET Framework not fully supported(only out of process collector, could suffer of [known issue](KnownIssues.md#1-vstest-stops-process-execution-earlydotnet-test))
+Since version `6.0.0`
-Since version `3.0.0`
-- .NET Core >= 2.0
-- .NET Framework >= 4.6.1
+* .NET Core >= 6.0
+* .NET Framework >= 4.6.2
-As explained in quick start section, to use collectors you need to run *SDK v2.2.401* or newer and your project file must reference `coverlet.collector.dll` and a minimum version of `Microsoft.NET.Test.Sdk`.
+As explained in quick start section, to use collectors you need to run *SDK v6.0.100* (LTS) or newer and your project file must reference `coverlet.collector` and a minimum version of `Microsoft.NET.Test.Sdk`.
A sample project file looks like:
```xml
- netcoreapp3.0;netcoreapp2.1;net46
+ net6.0;net48
-
-
+
+
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
@@ -37,13 +34,13 @@ The reference to `coverlet.collector` package is included by default with xunit
With correct reference in place you can run coverage through default dotnet test CLI verbs:
-```
+```shell
dotnet test --collect:"XPlat Code Coverage"
```
or
-```
+```text
dotnet publish
...
... -> C:\project\bin\Debug\netcoreapp3.0\testdll.dll
@@ -56,7 +53,7 @@ As you can see in case of `vstest` verb you **must** publish project before.
At the end of tests you'll find the coverage file data under default VSTest platform directory `TestResults`
-```
+```text
Attachments:
C:\git\coverlet\Documentation\Examples\VSTest\HelloWorld\XUnitTestProject1\TestResults\bc5e983b-d7a8-4f17-8c0a-8a8831a4a891\coverage.cobertura.xml
Test Run Successful.
@@ -65,30 +62,43 @@ Total tests: 1
Total time: 2,5451 Seconds
```
-You can change the position of files using standard `dotnet test` switch `[-r|--results-directory]`
+You can change the output directory using the standard `dotnet test` switch `--results-directory`
-*NB: By design VSTest platform will create your file under a random named folder(guid string) so if you need stable path to load file to some gui report system(i.e. coveralls, codecov, reportgenerator etc..) that doesn't support glob patterns or hierarchical search, you'll need to manually move resulting file to a predictable folder*
+>*NB: By design VSTest platform will create your file under a random named folder(guid string) so if you need stable path to load file to some gui report system(i.e. coveralls, codecov, reportgenerator etc..) that doesn't support glob patterns or hierarchical search, you'll need to manually move resulting file to a predictable folder*
## Coverlet options supported by VSTest integration
-At the moment VSTest integration doesn't support all features of msbuild and .NET tool, for instance show result on console, report merging and threshold validation.
-We're working to fill the gaps.
-*PS: if you don't have any other way to merge reports(for instance your report generator doesn't support multi coverage file) you can for the moment exploit a trick reported by one of our contributor Daniel Paz(@p4p3) https://github.com/tonerdo/coverlet/pull/225#issuecomment-573896446*
+:warning:At the moment VSTest integration **doesn't support all features** of msbuild and .NET tool, for instance show result on console, report merging and threshold validation.
+We're working to fill the gaps.
+
+> [!TIP]
+> *Some alternative solutions to merge coverage files*
+>
+> * use _dotnet-coverage_ tool and merge multiple coverage files
+>
+> `dotnet-coverage merge artifacts/coverage/**/coverage.cobertura.xml -f cobertura -o artifacts/coverage/coverage.xml`*
+>
+> * use _dotnet-reportgenerator-globaltool_ to create a HTML report and a merged coverage file
+>
+> `reportgenerator -reports:"**/*.cobertura.xml" -targetdir:"artifacts\reports.cobertura" -reporttypes:"HtmlInline_AzurePipelines_Dark;Cobertura"`
### Default option (if you don't specify a runsettings file)
Without specifying a runsettings file and calling coverlet by just the name of the collector, the result of the generated coverage output is by default in cobertura format.
-```
+
+```shell
dotnet test --collect:"XPlat Code Coverage"
```
The output format of the coverage report can also be changed without a runsettings file by specifying it in a parameter. The supported formats are lcov, opencover, cobertura, teamcity, json (default coverlet proprietary format).
-```
+
+```shell
dotnet test --collect:"XPlat Code Coverage;Format=json"
```
It is even possible to specify the coverage output in multiple formats.
-```
+
+```shell
dotnet test --collect:"XPlat Code Coverage;Format=json,lcov,cobertura"
```
@@ -98,12 +108,13 @@ These are a list of options that are supported by coverlet. These can be specifi
| Option | Summary |
|:-------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| Format | Coverage output format. These are either cobertura, json, lcov, opencover or teamcity as well as combinations of these formats. |
-| Exclude | Exclude from code coverage analysing using filter expressions. |
-| ExcludeByFile | Ignore specific source files from code coverage. |
-| Include | Explicitly set what to include in code coverage analysis using filter expressions. |
+| Format | Coverage output format. These are either cobertura, json, lcov, opencover or teamcity as well as combinations of these formats. |
+| Exclude | Exclude from code coverage analysing using filter expressions. |
+| ExcludeByAttribute | Exclude a method, an entire class or assembly from code coverage decorated by an attribute. |
+| ExcludeByFile | Ignore specific source files from code coverage. |
+| Include | Explicitly set what to include in code coverage analysis using filter expressions. |
| IncludeDirectory | Explicitly set which directories to include in code coverage analysis. |
-| SingleHit | Specifies whether to limit code coverage hit reporting to a single hit for each location. |
+| SingleHit | Specifies whether to limit code coverage hit reporting to a single hit for each location. |
| UseSourceLink | Specifies whether to use SourceLink URIs in place of file system paths. |
| IncludeTestAssembly | Include coverage of the test assembly. |
| SkipAutoProps | Neither track nor record auto-implemented properties. |
@@ -113,14 +124,14 @@ These are a list of options that are supported by coverlet. These can be specifi
How to specify these options via runsettings?
-```
+```xml
- json,cobertura,lcov,teamcity,opencover
+ json,cobertura,lcov,teamcity,opencover
[coverlet.*.tests?]*,[*]Coverlet.Core*
[coverlet.*]*,[*]Coverlet.Core*
Obsolete,GeneratedCodeAttribute,CompilerGeneratedAttribute
@@ -138,13 +149,13 @@ How to specify these options via runsettings?
```
+
Filtering details are present on [msbuild guide](MSBuildIntegration.md#excluding-from-coverage).
This runsettings file can easily be provided using command line option as given :
-1. `dotnet test --collect:"XPlat Code Coverage" --settings coverlet.runsettings`
-
-2. `dotnet vstest C:\project\bin\Debug\netcoreapp3.0\publish\testdll.dll --collect:"XPlat Code Coverage" --settings coverlet.runsettings`
+* `dotnet test --collect:"XPlat Code Coverage" --settings coverlet.runsettings`
+* `dotnet vstest C:\project\bin\Debug\netcoreapp3.0\publish\testdll.dll --collect:"XPlat Code Coverage" --settings coverlet.runsettings`
Take a look at our [`HelloWorld`](Examples/VSTest/HelloWorld/HowTo.md) sample.
@@ -154,20 +165,20 @@ You can avoid passing a `runsettings` file to `dotnet test` driver by using the
For instance if you want to set the `Format` element as a runsettings option you can use this syntax:
-```
+```shell
dotnet test --collect:"XPlat Code Coverage" -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=json,cobertura,lcov,teamcity,opencover
```
-Take a look here for further information: https://github.com/microsoft/vstest-docs/blob/master/docs/RunSettingsArguments.md
+Take a look here for further information:
## How it works
-Coverlet integration is implemented with the help of [datacollectors](https://github.com/Microsoft/vstest-docs/blob/master/docs/extensions/datacollector.md).
+Coverlet integration is implemented with the help of [datacollectors](https://github.com/microsoft/vstest/blob/main/docs/extensions/datacollector.md).
When we specify `--collect:"XPlat Code Coverage"` VSTest platform tries to load coverlet collectors inside `coverlet.collector.dll`
1. Out-of-proc Datacollector: The outproc collector run in a separate process(datacollector.exe/datacollector.dll) than the process in which tests are being executed(testhost*.exe/testhost.dll). This datacollector is responsible for calling into Coverlet APIs for instrumenting dlls, collecting coverage results and sending the coverage output file back to test platform.
-2. In-proc Datacollector: The in-proc collector is loaded in the testhost process executing the tests. This collector will be needed to remove the dependency on the process exit handler to flush the hit files and avoid to hit this [serious known issue](KnownIssues.md#1-vstest-stops-process-execution-earlydotnet-test)
+1. In-proc Datacollector: The in-proc collector is loaded in the testhost process executing the tests. This collector will be needed to remove the dependency on the process exit handler to flush the hit files and avoid to hit this [serious known issue](KnownIssues.md#1-vstest-stops-process-execution-earlydotnet-test)
## Known Issues
diff --git a/README.md b/README.md
index c60b9d37c..cea37fcfc 100644
--- a/README.md
+++ b/README.md
@@ -1,51 +1,54 @@
# Coverlet
-[](https://dev.azure.com/tonerdo/coverlet/_build/latest?definitionId=5&branchName=master) [](https://github.com/coverlet-coverage/coverlet/blob/master/LICENSE)
+[](https://dev.azure.com/tonerdo/coverlet/_build/latest?definitionId=5&branchName=master)  [](https://github.com/coverlet-coverage/coverlet/blob/master/LICENSE)
| Driver | Current version | Downloads |
|---|---|---|
-| coverlet.collector | [](https://www.nuget.org/packages/coverlet.collector/) | [](https://www.nuget.org/packages/coverlet.collector/)
+| coverlet.collector | [](https://www.nuget.org/packages/coverlet.collector/) | [](https://www.nuget.org/packages/coverlet.collector/)
| coverlet.msbuild | [](https://www.nuget.org/packages/coverlet.msbuild/) | [](https://www.nuget.org/packages/coverlet.msbuild/) |
| coverlet.console | [](https://www.nuget.org/packages/coverlet.console/) | [](https://www.nuget.org/packages/coverlet.console/) |
-Coverlet is a cross platform code coverage framework for .NET, with support for line, branch and method coverage. It works with .NET Framework on Windows and .NET Core on all supported platforms.
+Coverlet is a cross platform code coverage framework for .NET, with support for line, branch and method coverage. It works with [.NET Framework](Documentation/KnownIssues.md#badimageformatexception-net-framework-47x-48x) on Windows and .NET Core on all supported platforms.
-**Coverlet documentation reflect the current repository state of the features, not the released ones.**
+**Coverlet documentation reflect the current repository state of the features, not the released ones.**
**Check the [changelog](Documentation/Changelog.md) to understand if the documented feature you want to use has been officially released.**
-# Main contents
-* [QuickStart](#Quick-Start)
-* [How It Works](#How-It-Works)
+## Main contents
+
+* [QuickStart](#quick-start)
+* [How It Works](#how-it-works)
* [Drivers features differences](Documentation/DriversFeatures.md)
-* [Deterministic build support](#Deterministic-build-support)
-* [Known Issues](#Known-Issues)
-* [Consume nightly build](#Consume-nightly-build)
+* [Deterministic build support](#deterministic-build-support)
+* [Known Issues](#known-issues)
+* [Consume nightly build](#consume-nightly-build)
* [Feature samples](Documentation/Examples.md)
-* [Cake Add-In](#Cake-Add-In)
-* [Visual Studio Add-In](#Visual-Studio-Add-In)
+* [Cake Add-In](#cake-add-in)
+* [Visual Studio Add-In](#visual-studio-add-in)
* [Changelog](Documentation/Changelog.md)
* [Roadmap](Documentation/Roadmap.md)
## Quick Start
-Coverlet can be used through three different *drivers*
+Coverlet can be used through three different *drivers*
* VSTest engine integration
* MSBuild task integration
* As a .NET Global tool (supports standalone integration tests)
-Coverlet supports only SDK-style projects https://docs.microsoft.com/en-us/visualstudio/msbuild/how-to-use-project-sdk?view=vs-2019
-
+Coverlet supports only SDK-style projects https://docs.microsoft.com/en-us/visualstudio/msbuild/how-to-use-project-sdk?view=vs-2019
### VSTest Integration (preferred due to [known issue](https://github.com/coverlet-coverage/coverlet/blob/master/Documentation/KnownIssues.md#1-vstest-stops-process-execution-earlydotnet-test))
-### Installation
+### Installation (coverlet.collector)
+
```bash
dotnet add package coverlet.collector
```
-N.B. You **MUST** add package only to test projects and if you create xunit test projects (`dotnet new xunit`) you'll find the reference already present in `csproj` file because Coverlet is the default coverage tool for every .NET Core and >= .NET 5 applications, you've only to update to last version if needed.
-### Usage
+N.B. You **MUST** add package only to test projects and if you create xunit test projects (`dotnet new xunit`) you'll find the reference already present in `csproj` file because Coverlet is the default coverage tool for every .NET Core and >= .NET 6 applications, you've only to update to last version if needed. Do not add `coverlet.collector` and `coverlet.msbuild` package in a test project.
+
+### Usage (coverlet.collector)
+
Coverlet is integrated into the Visual Studio Test Platform as a [data collector](https://github.com/Microsoft/vstest-docs/blob/master/docs/extensions/datacollector.md). To get coverage simply run the following command:
```bash
@@ -56,22 +59,26 @@ After the above command is run, a `coverage.cobertura.xml` file containing the r
See [documentation](Documentation/VSTestIntegration.md) for advanced usage.
-#### Requirements
-* _You need to be running .NET Core SDK v2.2.401 or newer_
-* _You need to reference version 16.5.0 and above of Microsoft.NET.Test.Sdk_
-```
-
+#### Requirements (coverlet.collector)
+
+* _You need to be running .NET 6.0 SDK v6.0.316 or newer_
+* _You need to reference version 17.5.0 and above of Microsoft.NET.Test.Sdk_
+
+```xml
+
```
### MSBuild Integration (suffers of possible [known issue](https://github.com/coverlet-coverage/coverlet/blob/master/Documentation/KnownIssues.md#1-vstest-stops-process-execution-earlydotnet-test))
-### Installation
+### Installation (coverlet.msbuild)
+
```bash
dotnet add package coverlet.msbuild
```
-N.B. You **MUST** add package only to test projects
-### Usage
+N.B. Typically you **MUST** add package only to test projects. Do not add `coverlet.msbuild` and `coverlet.collector` package in a test project.
+
+### Usage (coverlet.msbuild)
Coverlet also integrates with the build system to run code coverage after tests. Enabling code coverage is as simple as setting the `CollectCoverage` property to `true`
@@ -83,18 +90,19 @@ After the above command is run, a `coverage.json` file containing the results wi
See [documentation](Documentation/MSBuildIntegration.md) for advanced usage.
-#### Requirements
+#### Requirements (coverlet.msbuild)
+
Requires a runtime that support _.NET Standard 2.0 and above_
### .NET Global Tool ([guide](https://docs.microsoft.com/en-us/dotnet/core/tools/global-tools), suffers from possible [known issue](https://github.com/coverlet-coverage/coverlet/blob/master/Documentation/KnownIssues.md#1-vstest-stops-process-execution-earlydotnet-test))
-### Installation
+### Installation (coverlet.console)
```bash
dotnet tool install --global coverlet.console
```
-### Usage
+### Usage (coverlet.console)
The `coverlet` tool is invoked by specifying the path to the assembly that contains the unit tests. You also need to specify the test runner and the arguments to pass to the test runner using the `--target` and `--targetargs` options respectively. The invocation of the test runner with the supplied arguments **must not** involve a recompilation of the unit test assembly or no coverage result will be generated.
@@ -108,11 +116,11 @@ _Note: The `--no-build` flag is specified so that the `/path/to/test-assembly.dl
See [documentation](Documentation/GlobalTool.md) for advanced usage.
-#### Requirements
-.NET global tools rely on a .NET Core runtime installed on your machine https://docs.microsoft.com/en-us/dotnet/core/tools/global-tools#what-could-go-wrong
+#### Requirements (coverlet.console)
-.NET Coverlet global tool requires _.NET Core 2.2 and above_
+.NET global tools rely on a .NET Core runtime installed on your machine https://docs.microsoft.com/en-us/dotnet/core/tools/global-tools#what-could-go-wrong
+.NET Coverlet global tool requires _.NET Core 2.2 and above_
## How It Works
@@ -131,14 +139,14 @@ Coverlet generates code coverage information by going through the following proc
## Deterministic build support
-Coverlet supports coverage for deterministic builds. The solution at the moment is not optimal and need a workaround.
+Coverlet supports coverage for deterministic builds. The solution at the moment is not optimal and need a workaround.
Take a look at [documentation](Documentation/DeterministicBuild.md).
## Are you in trouble with some feature? Check on [examples](Documentation/Examples.md)!
## Known Issues
-Unfortunately we have some known issues, check it [here](Documentation/KnownIssues.md)
+Unfortunately we have some known issues, check it [here](Documentation/KnownIssues.md)
## Cake Add-In
@@ -149,10 +157,12 @@ If you're using [Cake Build](https://cakebuild.net) for your build script you ca
If you want to visualize coverlet output inside Visual Studio while you code, you can use the following addins depending on your platform.
### Windows
+
If you're using Visual Studio on Windows, you can use the [Fine Code Coverage](https://marketplace.visualstudio.com/items?itemName=FortuneNgwenya.FineCodeCoverage) extension.
Visualization is updated when you run unit tests inside Visual Studio.
### Mac OS
+
If you're using Visual Studio for Mac, you can use the [VSMac-CodeCoverage](https://github.com/ademanuele/VSMac-CodeCoverage) extension.
## Consume nightly build
@@ -166,13 +176,14 @@ If you find a bug or have a feature request, please report them at this reposito
## Coverlet Team
-Author and owner
-* [Toni Solarin-Sodara](https://github.com/tonerdo)
+Author and owner
+* [Toni Solarin-Sodara](https://github.com/tonerdo)
Co-maintainers
-* [Peter Liljenberg](https://github.com/petli)
* [David Müller](https://github.com/daveMueller)
+* [Bert](https://github.com/Bertk)
+* [Peter Liljenberg](https://github.com/petli)
* [Marco Rossignoli](https://github.com/MarcoRossignoli)
## Code of Conduct
@@ -188,6 +199,6 @@ Part of the code is based on work done by OpenCover team https://github.com/Open
## License
-This project is licensed under the MIT license. See the [LICENSE](LICENSE) file for more info.
-
+This project is licensed under the MIT license. See the [LICENSE](LICENSE) file for more info.
+
## Supported by the [.NET Foundation](https://dotnetfoundation.org/)
diff --git a/coverlet.sln b/coverlet.sln
index efccfa31f..85241f5e4 100644
--- a/coverlet.sln
+++ b/coverlet.sln
@@ -1,164 +1,234 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.2.32208.508
-MinimumVisualStudioVersion = 15.0.26124.0
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E877EBA4-E78B-4F7D-A2D3-1E070FED04CD}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.core", "src\coverlet.core\coverlet.core.csproj", "{31084026-D563-4B91-BE71-174C4270CCF4}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.msbuild.tasks", "src\coverlet.msbuild.tasks\coverlet.msbuild.tasks.csproj", "{FA73E423-9790-4F35-B018-3C4E3CA338BA}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.core.tests", "test\coverlet.core.tests\coverlet.core.tests.csproj", "{E7637CC6-43F7-461A-A0BF-3C14562419BD}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.console", "src\coverlet.console\coverlet.console.csproj", "{F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.testsubject", "test\coverlet.testsubject\coverlet.testsubject.csproj", "{AE117FAA-C21D-4F23-917E-0C8050614750}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.core.performancetest", "test\coverlet.core.performancetest\coverlet.core.performancetest.csproj", "{C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.collector", "src\coverlet.collector\coverlet.collector.csproj", "{F5B2C45B-274B-43D6-9565-8B50659CFE56}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.collector.tests", "test\coverlet.collector.tests\coverlet.collector.tests.csproj", "{5ED4FA81-8F8C-4211-BA88-7573BD63262E}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.tests.projectsample.empty", "test\coverlet.tests.projectsample.empty\coverlet.tests.projectsample.empty.csproj", "{085A3AFB-C086-4E98-86F1-1B481446EC5E}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{77A15177-8262-488F-AF2B-91B9055715DA}"
- ProjectSection(SolutionItems) = preProject
- .editorconfig = .editorconfig
- .gitignore = .gitignore
- eng\azure-pipelines-nightly.yml = eng\azure-pipelines-nightly.yml
- eng\azure-pipelines.yml = eng\azure-pipelines.yml
- eng\build.yml = eng\build.yml
- DeterministicBuild.targets = DeterministicBuild.targets
- Directory.Build.props = Directory.Build.props
- Directory.Build.targets = Directory.Build.targets
- global.json = global.json
- EndProjectSection
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.tests.projectsample.excludedbyattribute", "test\coverlet.tests.projectsample.excludedbyattribute\coverlet.tests.projectsample.excludedbyattribute.csproj", "{D6B14F2F-9E7D-4D2C-BAC8-48834F853ED6}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.integration.tests", "test\coverlet.integration.tests\coverlet.integration.tests.csproj", "{99B4059C-B25C-4B82-8117-A0E9DC9B0949}"
-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.tests.samples.netstandard", "test\coverlet.core.tests.samples.netstandard\coverlet.core.tests.samples.netstandard.csproj", "{5FF404AD-7C0B-465A-A1E9-558CDC642B0C}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.tests.xunit.extensions", "test\coverlet.tests.xunit.extensions\coverlet.tests.xunit.extensions.csproj", "{F8199E19-FA9A-4559-9101-CAD7028121B4}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{9A8B19D4-4A24-4217-AEFE-159B68F029A1}"
- ProjectSection(SolutionItems) = preProject
- test\Directory.Build.props = test\Directory.Build.props
- test\Directory.Build.targets = test\Directory.Build.targets
- EndProjectSection
-EndProject
-Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "coverlet.tests.projectsample.vbmynamespace", "test\coverlet.tests.projectsample.vbmynamespace\coverlet.tests.projectsample.vbmynamespace.vbproj", "{C9B7DC34-3E04-4F20-AED4-73791AF8020D}"
-EndProject
-Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "coverlet.tests.projectsample.fsharp", "test\coverlet.tests.projectsample.fsharp\coverlet.tests.projectsample.fsharp.fsproj", "{1CBF6966-2A67-4D2C-8598-D174B83072F4}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.tests.projectsample.netframework", "test\coverlet.tests.projectsample.netframework\coverlet.tests.projectsample.netframework.csproj", "{E69D68C9-78ED-4076-A14B-D07295A4B2A5}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {31084026-D563-4B91-BE71-174C4270CCF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {31084026-D563-4B91-BE71-174C4270CCF4}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {31084026-D563-4B91-BE71-174C4270CCF4}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {31084026-D563-4B91-BE71-174C4270CCF4}.Release|Any CPU.Build.0 = Release|Any CPU
- {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|Any CPU.Build.0 = Release|Any CPU
- {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|Any CPU.Build.0 = Release|Any CPU
- {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|Any CPU.Build.0 = Release|Any CPU
- {AE117FAA-C21D-4F23-917E-0C8050614750}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {AE117FAA-C21D-4F23-917E-0C8050614750}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {AE117FAA-C21D-4F23-917E-0C8050614750}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {AE117FAA-C21D-4F23-917E-0C8050614750}.Release|Any CPU.Build.0 = Release|Any CPU
- {C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Release|Any CPU.Build.0 = Release|Any CPU
- {F5B2C45B-274B-43D6-9565-8B50659CFE56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {F5B2C45B-274B-43D6-9565-8B50659CFE56}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {F5B2C45B-274B-43D6-9565-8B50659CFE56}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {F5B2C45B-274B-43D6-9565-8B50659CFE56}.Release|Any CPU.Build.0 = Release|Any CPU
- {5ED4FA81-8F8C-4211-BA88-7573BD63262E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {5ED4FA81-8F8C-4211-BA88-7573BD63262E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {5ED4FA81-8F8C-4211-BA88-7573BD63262E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {5ED4FA81-8F8C-4211-BA88-7573BD63262E}.Release|Any CPU.Build.0 = Release|Any CPU
- {085A3AFB-C086-4E98-86F1-1B481446EC5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {085A3AFB-C086-4E98-86F1-1B481446EC5E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {085A3AFB-C086-4E98-86F1-1B481446EC5E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {085A3AFB-C086-4E98-86F1-1B481446EC5E}.Release|Any CPU.Build.0 = Release|Any CPU
- {D6B14F2F-9E7D-4D2C-BAC8-48834F853ED6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D6B14F2F-9E7D-4D2C-BAC8-48834F853ED6}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {D6B14F2F-9E7D-4D2C-BAC8-48834F853ED6}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D6B14F2F-9E7D-4D2C-BAC8-48834F853ED6}.Release|Any CPU.Build.0 = Release|Any CPU
- {99B4059C-B25C-4B82-8117-A0E9DC9B0949}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {99B4059C-B25C-4B82-8117-A0E9DC9B0949}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {99B4059C-B25C-4B82-8117-A0E9DC9B0949}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {99B4059C-B25C-4B82-8117-A0E9DC9B0949}.Release|Any CPU.Build.0 = Release|Any CPU
- {F6FE7678-C662-43D3-AC6A-64F6AC5A5935}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {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
- {5FF404AD-7C0B-465A-A1E9-558CDC642B0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {5FF404AD-7C0B-465A-A1E9-558CDC642B0C}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {5FF404AD-7C0B-465A-A1E9-558CDC642B0C}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {5FF404AD-7C0B-465A-A1E9-558CDC642B0C}.Release|Any CPU.Build.0 = Release|Any CPU
- {F8199E19-FA9A-4559-9101-CAD7028121B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {F8199E19-FA9A-4559-9101-CAD7028121B4}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {F8199E19-FA9A-4559-9101-CAD7028121B4}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {F8199E19-FA9A-4559-9101-CAD7028121B4}.Release|Any CPU.Build.0 = Release|Any CPU
- {C9B7DC34-3E04-4F20-AED4-73791AF8020D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C9B7DC34-3E04-4F20-AED4-73791AF8020D}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C9B7DC34-3E04-4F20-AED4-73791AF8020D}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C9B7DC34-3E04-4F20-AED4-73791AF8020D}.Release|Any CPU.Build.0 = Release|Any CPU
- {1CBF6966-2A67-4D2C-8598-D174B83072F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {1CBF6966-2A67-4D2C-8598-D174B83072F4}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {1CBF6966-2A67-4D2C-8598-D174B83072F4}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {1CBF6966-2A67-4D2C-8598-D174B83072F4}.Release|Any CPU.Build.0 = Release|Any CPU
- {E69D68C9-78ED-4076-A14B-D07295A4B2A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {E69D68C9-78ED-4076-A14B-D07295A4B2A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {E69D68C9-78ED-4076-A14B-D07295A4B2A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {E69D68C9-78ED-4076-A14B-D07295A4B2A5}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(NestedProjects) = preSolution
- {31084026-D563-4B91-BE71-174C4270CCF4} = {E877EBA4-E78B-4F7D-A2D3-1E070FED04CD}
- {FA73E423-9790-4F35-B018-3C4E3CA338BA} = {E877EBA4-E78B-4F7D-A2D3-1E070FED04CD}
- {E7637CC6-43F7-461A-A0BF-3C14562419BD} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
- {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E} = {E877EBA4-E78B-4F7D-A2D3-1E070FED04CD}
- {AE117FAA-C21D-4F23-917E-0C8050614750} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
- {C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
- {F5B2C45B-274B-43D6-9565-8B50659CFE56} = {E877EBA4-E78B-4F7D-A2D3-1E070FED04CD}
- {5ED4FA81-8F8C-4211-BA88-7573BD63262E} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
- {085A3AFB-C086-4E98-86F1-1B481446EC5E} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
- {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}
- {5FF404AD-7C0B-465A-A1E9-558CDC642B0C} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
- {F8199E19-FA9A-4559-9101-CAD7028121B4} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
- {9A8B19D4-4A24-4217-AEFE-159B68F029A1} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
- {1CBF6966-2A67-4D2C-8598-D174B83072F4} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
- {E69D68C9-78ED-4076-A14B-D07295A4B2A5} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
- {C9B7DC34-3E04-4F20-AED4-73791AF8020D} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
- EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {9CA57C02-97B0-4C38-A027-EA61E8741F10}
- EndGlobalSection
-EndGlobal
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.2.32208.508
+MinimumVisualStudioVersion = 15.0.26124.0
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E877EBA4-E78B-4F7D-A2D3-1E070FED04CD}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.core", "src\coverlet.core\coverlet.core.csproj", "{31084026-D563-4B91-BE71-174C4270CCF4}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.msbuild.tasks", "src\coverlet.msbuild.tasks\coverlet.msbuild.tasks.csproj", "{FA73E423-9790-4F35-B018-3C4E3CA338BA}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.core.tests", "test\coverlet.core.tests\coverlet.core.tests.csproj", "{E7637CC6-43F7-461A-A0BF-3C14562419BD}"
+ ProjectSection(ProjectDependencies) = postProject
+ {F5B2C45B-274B-43D6-9565-8B50659CFE56} = {F5B2C45B-274B-43D6-9565-8B50659CFE56}
+ {FA73E423-9790-4F35-B018-3C4E3CA338BA} = {FA73E423-9790-4F35-B018-3C4E3CA338BA}
+ EndProjectSection
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.console", "src\coverlet.console\coverlet.console.csproj", "{F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.testsubject", "test\coverlet.testsubject\coverlet.testsubject.csproj", "{AE117FAA-C21D-4F23-917E-0C8050614750}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.core.performancetest", "test\coverlet.core.performancetest\coverlet.core.performancetest.csproj", "{C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.collector", "src\coverlet.collector\coverlet.collector.csproj", "{F5B2C45B-274B-43D6-9565-8B50659CFE56}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.collector.tests", "test\coverlet.collector.tests\coverlet.collector.tests.csproj", "{5ED4FA81-8F8C-4211-BA88-7573BD63262E}"
+ ProjectSection(ProjectDependencies) = postProject
+ {FA73E423-9790-4F35-B018-3C4E3CA338BA} = {FA73E423-9790-4F35-B018-3C4E3CA338BA}
+ EndProjectSection
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.tests.projectsample.empty", "test\coverlet.tests.projectsample.empty\coverlet.tests.projectsample.empty.csproj", "{085A3AFB-C086-4E98-86F1-1B481446EC5E}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{77A15177-8262-488F-AF2B-91B9055715DA}"
+ ProjectSection(SolutionItems) = preProject
+ .editorconfig = .editorconfig
+ .gitignore = .gitignore
+ eng\azure-pipelines-nightly.yml = eng\azure-pipelines-nightly.yml
+ eng\azure-pipelines.yml = eng\azure-pipelines.yml
+ eng\build.yml = eng\build.yml
+ Directory.Build.props = Directory.Build.props
+ Directory.Build.targets = Directory.Build.targets
+ Directory.Packages.props = Directory.Packages.props
+ global.json = global.json
+ EndProjectSection
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.tests.projectsample.excludedbyattribute", "test\coverlet.tests.projectsample.excludedbyattribute\coverlet.tests.projectsample.excludedbyattribute.csproj", "{D6B14F2F-9E7D-4D2C-BAC8-48834F853ED6}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.integration.tests", "test\coverlet.integration.tests\coverlet.integration.tests.csproj", "{99B4059C-B25C-4B82-8117-A0E9DC9B0949}"
+ ProjectSection(ProjectDependencies) = postProject
+ {F5B2C45B-274B-43D6-9565-8B50659CFE56} = {F5B2C45B-274B-43D6-9565-8B50659CFE56}
+ {FA73E423-9790-4F35-B018-3C4E3CA338BA} = {FA73E423-9790-4F35-B018-3C4E3CA338BA}
+ EndProjectSection
+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.tests.samples.netstandard", "test\coverlet.core.tests.samples.netstandard\coverlet.core.tests.samples.netstandard.csproj", "{5FF404AD-7C0B-465A-A1E9-558CDC642B0C}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.tests.xunit.extensions", "test\coverlet.tests.xunit.extensions\coverlet.tests.xunit.extensions.csproj", "{F8199E19-FA9A-4559-9101-CAD7028121B4}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{9A8B19D4-4A24-4217-AEFE-159B68F029A1}"
+ ProjectSection(SolutionItems) = preProject
+ test\Directory.Build.props = test\Directory.Build.props
+ test\Directory.Build.targets = test\Directory.Build.targets
+ EndProjectSection
+EndProject
+Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "coverlet.tests.projectsample.fsharp", "test\coverlet.tests.projectsample.fsharp\coverlet.tests.projectsample.fsharp.fsproj", "{1CBF6966-2A67-4D2C-8598-D174B83072F4}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.tests.projectsample.netframework", "test\coverlet.tests.projectsample.netframework\coverlet.tests.projectsample.netframework.csproj", "{E69D68C9-78ED-4076-A14B-D07295A4B2A5}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.tests.projectsample.aspnet8", "test\coverlet.tests.projectsample.aspnet8\coverlet.tests.projectsample.aspnet8.csproj", "{1C3CA3F8-DF8C-433F-8A56-69102D2BBDF6}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.tests.projectsample.aspnet8.tests", "test\coverlet.tests.projectsample.aspnet8.tests\coverlet.tests.projectsample.aspnet8.tests.csproj", "{8EC065A4-7700-45E6-8B90-0182E3649DEA}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.tests.projectsample.wpf8", "test\coverlet.tests.projectsample.wpf8\coverlet.tests.projectsample.wpf8.csproj", "{988A5FF0-4326-4F5B-9F05-CB165543A555}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.tests.projectsample.aspmvcrazor", "test\coverlet.tests.projectsample.aspmvcrazor\coverlet.tests.projectsample.aspmvcrazor.csproj", "{6ACF69B1-C01F-44A4-8F8E-2501884238D4}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.tests.projectsample.aspmvcrazor.tests", "test\coverlet.tests.projectsample.aspmvcrazor.tests\coverlet.tests.projectsample.aspmvcrazor.tests.csproj", "{F508CCDD-5BC8-4AB6-97B3-D37498813C41}"
+ ProjectSection(ProjectDependencies) = postProject
+ {31084026-D563-4B91-BE71-174C4270CCF4} = {31084026-D563-4B91-BE71-174C4270CCF4}
+ {6ACF69B1-C01F-44A4-8F8E-2501884238D4} = {6ACF69B1-C01F-44A4-8F8E-2501884238D4}
+ EndProjectSection
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.msbuild.tasks.tests", "test\coverlet.msbuild.tasks.tests\coverlet.msbuild.tasks.tests.csproj", "{351A034E-E642-4DB9-A21D-F71C8151C243}"
+EndProject
+Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "coverlet.tests.projectsample.vbmynamespace", "test\coverlet.tests.projectsample.vbmynamespace\coverlet.tests.projectsample.vbmynamespace.vbproj", "{03400776-1F9A-4326-B927-1CA9B64B42A1}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.tests.utils", "test\coverlet.tests.utils\coverlet.tests.utils.csproj", "{0B109210-03CB-413F-888C-3023994AA384}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.tests.projectsample.wpf8.selfcontained", "test\coverlet.tests.projectsample.wpf8.selfcontained\coverlet.tests.projectsample.wpf8.selfcontained.csproj", "{71004336-9896-4AE5-8367-B29BB1680542}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {31084026-D563-4B91-BE71-174C4270CCF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {31084026-D563-4B91-BE71-174C4270CCF4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {31084026-D563-4B91-BE71-174C4270CCF4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {31084026-D563-4B91-BE71-174C4270CCF4}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AE117FAA-C21D-4F23-917E-0C8050614750}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AE117FAA-C21D-4F23-917E-0C8050614750}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AE117FAA-C21D-4F23-917E-0C8050614750}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AE117FAA-C21D-4F23-917E-0C8050614750}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F5B2C45B-274B-43D6-9565-8B50659CFE56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F5B2C45B-274B-43D6-9565-8B50659CFE56}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F5B2C45B-274B-43D6-9565-8B50659CFE56}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F5B2C45B-274B-43D6-9565-8B50659CFE56}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5ED4FA81-8F8C-4211-BA88-7573BD63262E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5ED4FA81-8F8C-4211-BA88-7573BD63262E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5ED4FA81-8F8C-4211-BA88-7573BD63262E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5ED4FA81-8F8C-4211-BA88-7573BD63262E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {085A3AFB-C086-4E98-86F1-1B481446EC5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {085A3AFB-C086-4E98-86F1-1B481446EC5E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {085A3AFB-C086-4E98-86F1-1B481446EC5E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {085A3AFB-C086-4E98-86F1-1B481446EC5E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D6B14F2F-9E7D-4D2C-BAC8-48834F853ED6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D6B14F2F-9E7D-4D2C-BAC8-48834F853ED6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D6B14F2F-9E7D-4D2C-BAC8-48834F853ED6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D6B14F2F-9E7D-4D2C-BAC8-48834F853ED6}.Release|Any CPU.Build.0 = Release|Any CPU
+ {99B4059C-B25C-4B82-8117-A0E9DC9B0949}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {99B4059C-B25C-4B82-8117-A0E9DC9B0949}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {99B4059C-B25C-4B82-8117-A0E9DC9B0949}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {99B4059C-B25C-4B82-8117-A0E9DC9B0949}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F6FE7678-C662-43D3-AC6A-64F6AC5A5935}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {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
+ {5FF404AD-7C0B-465A-A1E9-558CDC642B0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5FF404AD-7C0B-465A-A1E9-558CDC642B0C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5FF404AD-7C0B-465A-A1E9-558CDC642B0C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5FF404AD-7C0B-465A-A1E9-558CDC642B0C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F8199E19-FA9A-4559-9101-CAD7028121B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F8199E19-FA9A-4559-9101-CAD7028121B4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F8199E19-FA9A-4559-9101-CAD7028121B4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F8199E19-FA9A-4559-9101-CAD7028121B4}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1CBF6966-2A67-4D2C-8598-D174B83072F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1CBF6966-2A67-4D2C-8598-D174B83072F4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1CBF6966-2A67-4D2C-8598-D174B83072F4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1CBF6966-2A67-4D2C-8598-D174B83072F4}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E69D68C9-78ED-4076-A14B-D07295A4B2A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E69D68C9-78ED-4076-A14B-D07295A4B2A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E69D68C9-78ED-4076-A14B-D07295A4B2A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E69D68C9-78ED-4076-A14B-D07295A4B2A5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1C3CA3F8-DF8C-433F-8A56-69102D2BBDF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1C3CA3F8-DF8C-433F-8A56-69102D2BBDF6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1C3CA3F8-DF8C-433F-8A56-69102D2BBDF6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1C3CA3F8-DF8C-433F-8A56-69102D2BBDF6}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8EC065A4-7700-45E6-8B90-0182E3649DEA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8EC065A4-7700-45E6-8B90-0182E3649DEA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8EC065A4-7700-45E6-8B90-0182E3649DEA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8EC065A4-7700-45E6-8B90-0182E3649DEA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {988A5FF0-4326-4F5B-9F05-CB165543A555}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {988A5FF0-4326-4F5B-9F05-CB165543A555}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {988A5FF0-4326-4F5B-9F05-CB165543A555}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {988A5FF0-4326-4F5B-9F05-CB165543A555}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6ACF69B1-C01F-44A4-8F8E-2501884238D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6ACF69B1-C01F-44A4-8F8E-2501884238D4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6ACF69B1-C01F-44A4-8F8E-2501884238D4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6ACF69B1-C01F-44A4-8F8E-2501884238D4}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F508CCDD-5BC8-4AB6-97B3-D37498813C41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F508CCDD-5BC8-4AB6-97B3-D37498813C41}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F508CCDD-5BC8-4AB6-97B3-D37498813C41}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F508CCDD-5BC8-4AB6-97B3-D37498813C41}.Release|Any CPU.Build.0 = Release|Any CPU
+ {351A034E-E642-4DB9-A21D-F71C8151C243}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {351A034E-E642-4DB9-A21D-F71C8151C243}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {351A034E-E642-4DB9-A21D-F71C8151C243}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {351A034E-E642-4DB9-A21D-F71C8151C243}.Release|Any CPU.Build.0 = Release|Any CPU
+ {03400776-1F9A-4326-B927-1CA9B64B42A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {03400776-1F9A-4326-B927-1CA9B64B42A1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {03400776-1F9A-4326-B927-1CA9B64B42A1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {03400776-1F9A-4326-B927-1CA9B64B42A1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0B109210-03CB-413F-888C-3023994AA384}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0B109210-03CB-413F-888C-3023994AA384}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0B109210-03CB-413F-888C-3023994AA384}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0B109210-03CB-413F-888C-3023994AA384}.Release|Any CPU.Build.0 = Release|Any CPU
+ {71004336-9896-4AE5-8367-B29BB1680542}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {71004336-9896-4AE5-8367-B29BB1680542}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {71004336-9896-4AE5-8367-B29BB1680542}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {71004336-9896-4AE5-8367-B29BB1680542}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {31084026-D563-4B91-BE71-174C4270CCF4} = {E877EBA4-E78B-4F7D-A2D3-1E070FED04CD}
+ {FA73E423-9790-4F35-B018-3C4E3CA338BA} = {E877EBA4-E78B-4F7D-A2D3-1E070FED04CD}
+ {E7637CC6-43F7-461A-A0BF-3C14562419BD} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
+ {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E} = {E877EBA4-E78B-4F7D-A2D3-1E070FED04CD}
+ {AE117FAA-C21D-4F23-917E-0C8050614750} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
+ {C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
+ {F5B2C45B-274B-43D6-9565-8B50659CFE56} = {E877EBA4-E78B-4F7D-A2D3-1E070FED04CD}
+ {5ED4FA81-8F8C-4211-BA88-7573BD63262E} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
+ {085A3AFB-C086-4E98-86F1-1B481446EC5E} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
+ {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}
+ {5FF404AD-7C0B-465A-A1E9-558CDC642B0C} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
+ {F8199E19-FA9A-4559-9101-CAD7028121B4} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
+ {9A8B19D4-4A24-4217-AEFE-159B68F029A1} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
+ {1CBF6966-2A67-4D2C-8598-D174B83072F4} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
+ {E69D68C9-78ED-4076-A14B-D07295A4B2A5} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
+ {1C3CA3F8-DF8C-433F-8A56-69102D2BBDF6} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
+ {8EC065A4-7700-45E6-8B90-0182E3649DEA} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
+ {988A5FF0-4326-4F5B-9F05-CB165543A555} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
+ {6ACF69B1-C01F-44A4-8F8E-2501884238D4} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
+ {F508CCDD-5BC8-4AB6-97B3-D37498813C41} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
+ {351A034E-E642-4DB9-A21D-F71C8151C243} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
+ {03400776-1F9A-4326-B927-1CA9B64B42A1} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
+ {0B109210-03CB-413F-888C-3023994AA384} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
+ {71004336-9896-4AE5-8367-B29BB1680542} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {9CA57C02-97B0-4C38-A027-EA61E8741F10}
+ EndGlobalSection
+EndGlobal
diff --git a/eng/CheckNugetStatus.yml b/eng/CheckNugetStatus.yml
new file mode 100644
index 000000000..8eaf4797b
--- /dev/null
+++ b/eng/CheckNugetStatus.yml
@@ -0,0 +1,69 @@
+# File: CheckNugetStatus.yml
+# the template will write lists of outdated, deprecated or vulnerable nuget packages to build log for every C# project. If a deprecated or vulnerable package is detected, an error will be written to the build log.
+# Precondition: restore and build was executed before this template is used
+# Limitation: dotnet SDK does not provide .NET framework MSBuild targets like "Microsoft.WebApplication.targets". This c# projects will be ignored and "An error occurred for " message is added in build log.
+
+parameters:
+ condition: 'succeeded()'
+ enableQualitySteps: true
+ sourcePath: ''
+ nugetConfig: ''
+ breakBuild: false
+
+steps:
+- task: NuGetAuthenticate@1
+ condition: ${{parameters.condition}}
+ enabled: ${{parameters.enableQualitySteps}}
+
+- task: PowerShell@2
+ displayName: 'Check nuget package status'
+ inputs:
+ targetType: 'inline'
+ pwsh: true
+ script: |
+ Write-Information -MessageData "sourcePath='${{parameters.sourcePath}}'" -InformationAction Continue
+ Write-Information -MessageData "nugetConfig='${{parameters.nugetConfig}}'" -InformationAction Continue
+ Write-Information -MessageData "#########################################" -InformationAction Continue
+ if (!(Test-Path "${{parameters.sourcePath}}" -PathType Container)) {
+ Write-Host "##vso[task.LogIssue type=error;]sourcePath does not exist."
+ }
+ $existsDeprecatedPackage = $false
+ $existsVulnerablePackage = $false
+
+ $projectFiles = Get-ChildItem -Path ${{parameters.sourcePath}} -Filter *.csproj -Recurse
+ foreach ($project in $projectFiles) {
+ try {
+ $outdatedList = dotnet list $project package --outdated --include-transitive --source https://api.nuget.org/v3/index.json
+ if ($LASTEXITCODE -gt 0) {
+ Throw "The command exited with error code: $lastexitcode"
+ }
+ $outdatedList
+ $deprecatedList = dotnet list $project package --deprecated --include-transitive --source https://api.nuget.org/v3/index.json
+ if ($deprecatedList.Length -gt 5) {
+ $deprecatedList
+ $existsDeprecatedPackage = $true
+ } else {
+ $deprecatedList[4]
+ }
+ $vulnerableList = dotnet list $project package --vulnerable --source https://api.nuget.org/v3/index.json
+ if ($vulnerableList.Length -gt 5) {
+ $vulnerableList
+ $existsVulnerablePackage = $true
+ } else {
+ $vulnerableList[4]
+ }
+ } catch { "An error occurred for $($project.PSChildName)" }
+ }
+ if ( $existsDeprecatedPackage -or $existsVulnerablePackage) {
+ Write-Host "##vso[task.LogIssue type=error;]Detected nuget package: Deprecated = $existsDeprecatedPackage, Vulnerable = $existsVulnerablePackage"
+ if ("${{parameters.breakBuild}}" -eq "true") {
+ exit 42
+ }
+ } else {
+ Write-Information -MessageData "Did not detected deprecated or vulnerable nuget package." -InformationAction Continue
+ }
+ exit 0
+ env:
+ SYSTEM_ACCESSTOKEN: $(System.AccessToken)
+ condition: ${{parameters.condition}}
+ enabled: ${{parameters.enableQualitySteps}}
diff --git a/eng/azure-pipelines-nightly.yml b/eng/azure-pipelines-nightly.yml
index 3535bd2bc..6cf27c1e0 100644
--- a/eng/azure-pipelines-nightly.yml
+++ b/eng/azure-pipelines-nightly.yml
@@ -1,18 +1,18 @@
pool:
- vmImage: 'windows-2019'
+ vmImage: 'windows-latest'
steps:
- task: UseDotNet@2
inputs:
- version: 3.1.404
- displayName: Install .NET Core SDK 3.1.404
+ version: 6.0.428
+ displayName: Install .NET Core SDK 6.0.428
- task: UseDotNet@2
inputs:
- version: 5.0.401
- displayName: Install .NET Core SDK 5.0.401
+ useGlobalJson: true
+ displayName: Install .NET Core SDK 8.0.111
-- task: NuGetAuthenticate@0
+- task: NuGetAuthenticate@1
displayName: Authenticate with NuGet feeds
- script: dotnet pack -c Release /p:PublicRelease=false
@@ -21,7 +21,7 @@ steps:
- task: NuGetCommand@2
inputs:
command: push
- packagesToPush: $(Build.SourcesDirectory)/bin/Release/Packages/*.nupkg
+ packagesToPush: $(Build.SourcesDirectory)\artifacts\package\release\*.nupkg
nuGetFeedType: internal
publishVstsFeed: coverlet/coverlet-nightly
displayName: Publish NuGet packages
@@ -29,7 +29,7 @@ steps:
- task: NuGetCommand@2
inputs:
command: push
- packagesToPush: $(Build.SourcesDirectory)/bin/Release/Packages/*.snupkg
+ packagesToPush: $(Build.SourcesDirectory)\artifacts\package\release\*.snupkg
nuGetFeedType: internal
publishVstsFeed: coverlet/coverlet-nightly
displayName: Publish NuGet symbol packages
diff --git a/eng/azure-pipelines.yml b/eng/azure-pipelines.yml
index 67d6bc41e..f3f008cdb 100644
--- a/eng/azure-pipelines.yml
+++ b/eng/azure-pipelines.yml
@@ -2,12 +2,17 @@ trigger:
branches:
include: ["master", "*_validate"]
paths:
- exclude: [".github", "doc", "*.md"]
+ exclude: [".github", "Documentation", "*.md"]
+
+variables:
+ system.debug: false
jobs:
- job: Windows
displayName: Windows
continueOnError: 'true'
+ timeoutInMinutes: 30
+ cancelTimeoutInMinutes: 5
strategy:
matrix:
Debug:
@@ -21,7 +26,7 @@ jobs:
- task: CopyFiles@2
displayName: Collect packages
inputs:
- SourceFolder: bin\$(BuildConfiguration)\Packages
+ SourceFolder: artifacts\package\$(BuildConfiguration)
Contents: |
*.nupkg
*.snupkg
@@ -34,10 +39,24 @@ jobs:
ArtifactName: Packages
publishLocation: Container
condition: eq(variables['BuildConfiguration'], 'Release')
+ - task: PublishBuildArtifacts@1
+ displayName: Publish tests artifacts
+ inputs:
+ PathtoPublish: $(Build.SourcesDirectory)\artifacts\publish
+ ArtifactName: PublishedTests
+ publishLocation: Container
+ condition: eq(variables['BuildConfiguration'], 'Debug')
+ - template: CheckNugetStatus.yml
+ parameters:
+ sourcePath: '$(Build.SourcesDirectory)/src'
+ breakBuild: false
+ # nugetConfig: '$(Build.SourcesDirectory)/nuget.config'
- job: macOS
displayName: macOS
continueOnError: 'true'
+ timeoutInMinutes: 30
+ cancelTimeoutInMinutes: 5
strategy:
matrix:
Debug:
@@ -52,6 +71,8 @@ jobs:
- job: Linux
displayName: Linux
continueOnError: 'true'
+ timeoutInMinutes: 30
+ cancelTimeoutInMinutes: 5
strategy:
matrix:
Debug:
diff --git a/eng/build.yml b/eng/build.yml
index e4db0b0a6..bd3c56e48 100644
--- a/eng/build.yml
+++ b/eng/build.yml
@@ -1,36 +1,52 @@
steps:
- task: UseDotNet@2
inputs:
- version: 3.1.404
- displayName: Install .NET Core SDK 3.1.404
+ version: 6.0.428
+ displayName: Install .NET Core SDK 6.0.428
- task: UseDotNet@2
inputs:
- version: 5.0.401
- displayName: Install .NET Core SDK 5.0.401
+ useGlobalJson: true
+ displayName: Install .NET Core SDK 8.0.111
-- task: UseDotNet@2
- inputs:
- version: 6.0.408
- displayName: Install .NET Core SDK 6.0.408
-
-- task: UseDotNet@2
- inputs:
- version: 7.0.203
- displayName: Install .NET Core SDK 7.0.203
+# create artifact/package folder
+- pwsh: |
+ New-Item -ItemType Directory -Path artifacts/package/debug -Force
+ New-Item -ItemType Directory -Path artifacts/package/release -Force
+ displayName: create folder artifacts/package/$(BuildConfiguration)
- script: dotnet restore
displayName: Restore packages
-- script: dotnet build -c $(BuildConfiguration) --no-restore
+- script: dotnet build -c $(BuildConfiguration) --no-restore -bl:build.msbuild.binlog
displayName: Build
- script: dotnet pack -c $(BuildConfiguration) --no-restore
displayName: Pack
-- task: DotNetCoreCLI@2
- displayName: Run tests
+- script: |
+ dotnet test test/coverlet.collector.tests/coverlet.collector.tests.csproj -c $(BuildConfiguration) --no-build -bl:test.collector.binlog --results-directory:"$(Build.SourcesDirectory)\artifacts\Reports" /p:CollectCoverage=true /p:CoverletOutputFormat=opencover /p:Exclude="[coverlet.core.tests.samples.netstandard]*%2c[coverlet.tests.xunit.extensions]*%2c[coverlet.tests.projectsample]*%2c[testgen_]*" /p:ExcludeByAttribute="GeneratedCodeAttribute" --diag:"$(Build.SourcesDirectory)\artifacts\log\$(buildConfiguration)\coverlet.collector.test.diag.log;tracelevel=verbose"
+ dotnet test test/coverlet.core.tests/coverlet.core.tests.csproj -c $(BuildConfiguration) --no-build -bl:test.core.binlog --results-directory:"$(Build.SourcesDirectory)\artifacts\Reports" /p:CollectCoverage=true /p:CoverletOutputFormat=opencover /p:Exclude="[coverlet.core.tests.samples.netstandard]*%2c[coverlet.tests.xunit.extensions]*%2c[coverlet.tests.projectsample]*%2c[testgen_]*" /p:ExcludeByAttribute="GeneratedCodeAttribute" --diag:"$(Build.SourcesDirectory)\artifacts\log\$(buildConfiguration)\coverlet.core.test.diag.log;tracelevel=verbose"
+ dotnet test test/coverlet.msbuild.tasks.tests/coverlet.msbuild.tasks.tests.csproj -c $(BuildConfiguration) --no-build -bl:test.msbuild.binlog --results-directory:"$(Build.SourcesDirectory)\artifacts\Reports" /p:CollectCoverage=true /p:CoverletOutputFormat=opencover /p:Exclude="[coverlet.core.tests.samples.netstandard]*%2c[coverlet.tests.xunit.extensions]*%2c[coverlet.tests.projectsample]*%2c[testgen_]*" /p:ExcludeByAttribute="GeneratedCodeAttribute" --diag:"$(Build.SourcesDirectory)\artifacts\log\$(buildConfiguration)\coverlet.msbuild.test.diag.log;tracelevel=verbose"
+ dotnet test test/coverlet.integration.tests/coverlet.integration.tests.csproj -c $(BuildConfiguration) --no-build -bl:test.integration.binlog --results-directory:"$(Build.SourcesDirectory)\artifacts\Reports" --diag:"$(Build.SourcesDirectory)\artifacts\log\$(buildConfiguration)\coverlet.integration.test.diag.log;tracelevel=verbose"
+ displayName: Run unit tests with coverage
+ env:
+ MSBUILDDISABLENODEREUSE: 1
+
+- task: PublishTestResults@2
inputs:
- command: test
- arguments: -c $(BuildConfiguration) --no-build /p:CollectCoverage=true /p:CoverletOutputFormat=opencover /p:Include="[coverlet.collector]*%2c[coverlet.core]*%2c[coverlet.msbuild.tasks]*" /p:Exclude="[coverlet.core.tests.samples.netstandard]*%2c[coverlet.tests.xunit.extensions]*"
- testRunTitle: $(Agent.JobName)
+ testResultsFormat: 'VSTest'
+ testResultsFiles: '**/*.trx'
+ mergeTestResults: false
+ publishRunAttachments: true
+ failTaskOnFailedTests: true
+ condition: succeededOrFailed()
+
+- template: publish-coverlet-result-files.yml
+
+- template: publish-coverage-results.yml
+ parameters:
+ reports: $(Build.SourcesDirectory)\**\*.opencover.xml
+ condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Debug'), eq(variables['agent.os'], 'Windows_NT'))
+ assemblyfilters: '-xunit;-coverlet.testsubject;-Coverlet.Tests.ProjectSample.*;-coverlet.core.tests.samples.netstandard;-coverlet.tests.xunit.extensions;-coverletsamplelib.integration.template;-coverlet.tests.utils'
+
diff --git a/eng/publish-coverage-results.yml b/eng/publish-coverage-results.yml
new file mode 100644
index 000000000..796d8f577
--- /dev/null
+++ b/eng/publish-coverage-results.yml
@@ -0,0 +1,35 @@
+# File: publish-coverage-results.yml
+# uses reportgenerator task to create a code coverage report and aggregates available cobertura XML files. The results are publishes as a build artifact.
+
+parameters:
+ condition: 'succeeded()'
+ reports: ''
+ assemblyfilters: '-xunit*'
+ classfilters: ''
+ breakBuild: false
+ minimumLineCoverage: 75
+
+steps:
+- task: Powershell@2
+ displayName: ReportGenerator
+ condition: ${{parameters.condition}}
+ inputs:
+ targetType: inline
+ pwsh: true
+ 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:"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'
+ artifact: CoverageResults_$(Agent.Os)_$(BuildConfiguration)
+ condition: ${{parameters.condition}}
+
+- task: PublishCodeCoverageResults@2
+ displayName: 'Publish code coverage'
+ condition: ${{parameters.condition}}
+ inputs:
+ codeCoverageTool: Cobertura
+ summaryFileLocation: '$(Build.SourcesDirectory)/artifacts/CoverageReport/Cobertura.xml'
+ failIfCoverageEmpty: ${{parameters.breakBuild}}
diff --git a/eng/publish-coverlet-result-files.yml b/eng/publish-coverlet-result-files.yml
new file mode 100644
index 000000000..c8addb84c
--- /dev/null
+++ b/eng/publish-coverlet-result-files.yml
@@ -0,0 +1,46 @@
+steps:
+- task: CopyFiles@2
+ displayName: Copy test results
+ continueOnError: true
+ condition: always()
+ inputs:
+ SourceFolder: '$(Build.SourcesDirectory)/artifacts'
+ Contents: |
+ **/*.trx
+ **/*.html
+ **/*.opencover.xml
+ **/*.cobertura.xml
+ **/*.coverage.json
+ **/*.diag.log
+ **/log.txt
+ **/log.datacollector.*.txt
+ **/log.host.*.txt
+ TargetFolder: '$(Build.SourcesDirectory)/artifacts/TestLogs'
+
+- task: CopyFiles@2
+ displayName: Copy trx files
+ condition: always()
+ inputs:
+ SourceFolder: '$(Agent.TempDirectory)'
+ Contents: |
+ **/*.trx
+ **/coverage.opencover.xml
+ **/coverage.cobertura.xml
+ **/coverage.json
+ TargetFolder: '$(Build.SourcesDirectory)/artifacts/TestLogs'
+
+- task: CopyFiles@2
+ displayName: Copy binlog files
+ condition: always()
+ inputs:
+ SourceFolder: '$(Build.SourcesDirectory)'
+ Contents: '**/*.binlog'
+ TargetFolder: '$(Build.SourcesDirectory)/artifacts/TestLogs'
+
+- task: PublishPipelineArtifact@1
+ displayName: Publish Coverlet logs
+ continueOnError: true
+ condition: always()
+ inputs:
+ path: artifacts/TestLogs
+ artifactName: TestLogs_$(Agent.Os)_$(BuildConfiguration)
diff --git a/global.json b/global.json
index 2dbcd442b..19f8c0399 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,5 @@
{
- "sdk": {
- "version": "6.0.408",
- "rollForward": "latestMajor"
- }
+ "sdk": {
+ "version": "8.0.111"
+ }
}
diff --git a/src/coverlet.collector/DataCollection/AttachmentManager.cs b/src/coverlet.collector/DataCollection/AttachmentManager.cs
index 8259f71f3..a062a40bc 100644
--- a/src/coverlet.collector/DataCollection/AttachmentManager.cs
+++ b/src/coverlet.collector/DataCollection/AttachmentManager.cs
@@ -4,173 +4,170 @@
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;
namespace Coverlet.Collector.DataCollection
{
- ///
- /// Manages coverage report attachments
- ///
- internal class AttachmentManager : IDisposable
- {
- private readonly DataCollectionSink _dataSink;
- private readonly TestPlatformEqtTrace _eqtTrace;
- private readonly TestPlatformLogger _logger;
- private readonly DataCollectionContext _dataCollectionContext;
- private readonly IFileHelper _fileHelper;
- private readonly IDirectoryHelper _directoryHelper;
- private readonly ICountDownEvent _countDownEvent;
- private readonly string _reportDirectory;
+ ///
+ /// Manages coverage report attachments
+ ///
+ internal class AttachmentManager : IDisposable
+ {
+ private readonly DataCollectionSink _dataSink;
+ private readonly TestPlatformEqtTrace _eqtTrace;
+ private readonly TestPlatformLogger _logger;
+ private readonly DataCollectionContext _dataCollectionContext;
+ private readonly IFileHelper _fileHelper;
+ private readonly IDirectoryHelper _directoryHelper;
+ private readonly ICountDownEvent _countDownEvent;
+ private readonly string _reportDirectory;
- public AttachmentManager(DataCollectionSink dataSink, DataCollectionContext dataCollectionContext, TestPlatformLogger logger, TestPlatformEqtTrace eqtTrace, ICountDownEvent countDownEvent)
- : this(dataSink,
- dataCollectionContext,
- logger,
- eqtTrace,
- Guid.NewGuid().ToString(),
- new FileHelper(),
- new DirectoryHelper(),
- countDownEvent)
- {
- }
+ public AttachmentManager(DataCollectionSink dataSink, DataCollectionContext dataCollectionContext, TestPlatformLogger logger, TestPlatformEqtTrace eqtTrace, ICountDownEvent countDownEvent)
+ : this(dataSink,
+ dataCollectionContext,
+ logger,
+ eqtTrace,
+ Guid.NewGuid().ToString(),
+ new FileHelper(),
+ new DirectoryHelper(),
+ countDownEvent)
+ {
+ }
- public AttachmentManager(DataCollectionSink dataSink, DataCollectionContext dataCollectionContext, TestPlatformLogger logger, TestPlatformEqtTrace eqtTrace, string reportDirectoryName, IFileHelper fileHelper, IDirectoryHelper directoryHelper, ICountDownEvent countDownEvent)
- {
- // Store input variabless
- _dataSink = dataSink;
- _dataCollectionContext = dataCollectionContext;
- _logger = logger;
- _eqtTrace = eqtTrace;
- _fileHelper = fileHelper;
- _directoryHelper = directoryHelper;
- _countDownEvent = countDownEvent;
+ public AttachmentManager(DataCollectionSink dataSink, DataCollectionContext dataCollectionContext, TestPlatformLogger logger, TestPlatformEqtTrace eqtTrace, string reportDirectoryName, IFileHelper fileHelper, IDirectoryHelper directoryHelper, ICountDownEvent countDownEvent)
+ {
+ // Store input variables
+ _dataSink = dataSink;
+ _dataCollectionContext = dataCollectionContext;
+ _logger = logger;
+ _eqtTrace = eqtTrace;
+ _fileHelper = fileHelper;
+ _directoryHelper = directoryHelper;
+ _countDownEvent = countDownEvent;
- // Report directory to store the coverage reports.
- _reportDirectory = Path.Combine(Path.GetTempPath(), reportDirectoryName);
+ // Report directory to store the coverage reports.
+ _reportDirectory = Path.Combine(Path.GetTempPath(), reportDirectoryName);
- // Register events
- _dataSink.SendFileCompleted += OnSendFileCompleted;
- }
+ // Register events
+ _dataSink.SendFileCompleted += OnSendFileCompleted;
+ }
- ///
- /// Sends coverage report to test platform
- ///
- /// Coverage report
- /// Coverage report file name
- public void SendCoverageReport(string coverageReport, string coverageReportFileName)
- {
- // Save coverage report to file
- string coverageReportPath = SaveCoverageReport(coverageReport, coverageReportFileName);
+ ///
+ /// Sends coverage report to test platform
+ ///
+ /// Coverage report
+ /// Coverage report file name
+ public void SendCoverageReport(string coverageReport, string coverageReportFileName)
+ {
+ // Save coverage report to file
+ string coverageReportPath = SaveCoverageReport(coverageReport, coverageReportFileName);
- // Send coverage attachment to test platform.
- SendAttachment(coverageReportPath);
- }
+ // Send coverage attachment to test platform.
+ SendAttachment(coverageReportPath);
+ }
- ///
- /// Disposes attachment manager
- ///
- public void Dispose()
+ ///
+ /// Disposes attachment manager
+ ///
+ public void Dispose()
+ {
+ // Unregister events
+ try
+ {
+ _countDownEvent.Wait();
+ if (_dataSink != null)
{
- // Unregister events
- try
- {
- _countDownEvent.Wait();
- if (_dataSink != null)
- {
- _dataSink.SendFileCompleted -= OnSendFileCompleted;
- }
- CleanupReportDirectory();
- }
- catch (Exception ex)
- {
- _logger.LogWarning(ex.ToString());
- }
+ _dataSink.SendFileCompleted -= OnSendFileCompleted;
}
+ CleanupReportDirectory();
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning(ex.ToString());
+ }
+ }
- ///
- /// Saves coverage report to file system
- ///
- /// Coverage report
- /// Coverage report file name
- /// Coverage report file path
- private string SaveCoverageReport(string report, string reportFileName)
- {
- try
- {
- _directoryHelper.CreateDirectory(_reportDirectory);
- string filePath = Path.Combine(_reportDirectory, reportFileName);
- _fileHelper.WriteAllText(filePath, report);
- _eqtTrace.Info("{0}: Saved coverage report to path: '{1}'", CoverletConstants.DataCollectorName, filePath);
+ ///
+ /// Saves coverage report to file system
+ ///
+ /// Coverage report
+ /// Coverage report file name
+ /// Coverage report file path
+ private string SaveCoverageReport(string report, string reportFileName)
+ {
+ try
+ {
+ _directoryHelper.CreateDirectory(_reportDirectory);
+ string filePath = Path.Combine(_reportDirectory, reportFileName);
+ _fileHelper.WriteAllText(filePath, report);
+ _eqtTrace.Info("{0}: Saved coverage report to path: '{1}'", CoverletConstants.DataCollectorName, filePath);
- return filePath;
- }
- catch (Exception ex)
- {
- string errorMessage = string.Format(Resources.FailedToSaveCoverageReport, CoverletConstants.DataCollectorName, reportFileName, _reportDirectory);
- throw new CoverletDataCollectorException(errorMessage, ex);
- }
- }
+ return filePath;
+ }
+ catch (Exception ex)
+ {
+ throw new CoverletDataCollectorException($"{CoverletConstants.DataCollectorName}: Failed to save coverage report '{reportFileName}' in directory '{_reportDirectory}'", ex);
+ }
+ }
- ///
- /// SendFileCompleted event handler
- ///
- /// Sender
- /// Event args
- public void OnSendFileCompleted(object sender, AsyncCompletedEventArgs e)
- {
- try
- {
- _eqtTrace.Verbose("{0}: SendFileCompleted received", CoverletConstants.DataCollectorName);
- }
- catch (Exception ex)
- {
- _logger.LogWarning(ex.ToString());
- }
- finally
- {
- _countDownEvent.Signal();
- }
- }
+ ///
+ /// SendFileCompleted event handler
+ ///
+ /// Sender
+ /// Event args
+ public void OnSendFileCompleted(object sender, AsyncCompletedEventArgs e)
+ {
+ try
+ {
+ _eqtTrace.Verbose("{0}: SendFileCompleted received", CoverletConstants.DataCollectorName);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning(ex.ToString());
+ }
+ finally
+ {
+ _countDownEvent.Signal();
+ }
+ }
- ///
- /// Sends attachment file to test platform
- ///
- /// Attachment file path
- private void SendAttachment(string attachmentPath)
- {
- if (_fileHelper.Exists(attachmentPath))
- {
- // Send coverage attachment to test platform.
- _eqtTrace.Verbose("{0}: Sending attachment to test platform", CoverletConstants.DataCollectorName);
- _dataSink.SendFileAsync(_dataCollectionContext, attachmentPath, false);
- }
- else
- {
- _eqtTrace.Warning("{0}: Attachment file does not exist", CoverletConstants.DataCollectorName);
- }
- }
+ ///
+ /// Sends attachment file to test platform
+ ///
+ /// Attachment file path
+ private void SendAttachment(string attachmentPath)
+ {
+ if (_fileHelper.Exists(attachmentPath))
+ {
+ // Send coverage attachment to test platform.
+ _eqtTrace.Verbose("{0}: Sending attachment to test platform", CoverletConstants.DataCollectorName);
+ _dataSink.SendFileAsync(_dataCollectionContext, attachmentPath, false);
+ }
+ else
+ {
+ _eqtTrace.Warning("{0}: Attachment file does not exist", CoverletConstants.DataCollectorName);
+ }
+ }
- ///
- /// Cleans up coverage report directory
- ///
- private void CleanupReportDirectory()
+ ///
+ /// Cleans up coverage report directory
+ ///
+ private void CleanupReportDirectory()
+ {
+ try
+ {
+ if (_directoryHelper.Exists(_reportDirectory))
{
- try
- {
- if (_directoryHelper.Exists(_reportDirectory))
- {
- _directoryHelper.Delete(_reportDirectory, true);
- _eqtTrace.Verbose("{0}: Deleted report directory: '{1}'", CoverletConstants.DataCollectorName, _reportDirectory);
- }
- }
- catch (Exception ex)
- {
- string errorMessage = string.Format(Resources.FailedToCleanupReportDirectory, CoverletConstants.DataCollectorName, _reportDirectory);
- throw new CoverletDataCollectorException(errorMessage, ex);
- }
+ _directoryHelper.Delete(_reportDirectory, true);
+ _eqtTrace.Verbose("{0}: Deleted report directory: '{1}'", CoverletConstants.DataCollectorName, _reportDirectory);
}
+ }
+ catch (Exception 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 89ec41eb1..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;
@@ -14,109 +13,106 @@
namespace Coverlet.Collector.DataCollection
{
- ///
- /// Manages coverlet coverage
- ///
- internal class CoverageManager
- {
- private readonly Coverage _coverage;
- private readonly ICoverageWrapper _coverageWrapper;
- private readonly ISourceRootTranslator _sourceRootTranslator;
- public IReporter[] Reporters { get; }
+ ///
+ /// Manages coverlet coverage
+ ///
+ internal class CoverageManager
+ {
+ private readonly Coverage _coverage;
+ private readonly ICoverageWrapper _coverageWrapper;
+ private readonly ISourceRootTranslator _sourceRootTranslator;
+ public IReporter[] Reporters { get; }
- public CoverageManager(CoverletSettings settings, TestPlatformEqtTrace eqtTrace, TestPlatformLogger logger, ICoverageWrapper coverageWrapper,
- IInstrumentationHelper instrumentationHelper, IFileSystem fileSystem, ISourceRootTranslator sourceRootTranslator, ICecilSymbolHelper cecilSymbolHelper)
- : this(settings,
- settings.ReportFormats.Select(format =>
- {
- var reporterFactory = new ReporterFactory(format);
- if (!reporterFactory.IsValidFormat())
- {
- eqtTrace.Warning($"Invalid report format '{format}'");
- return null;
- }
- else
- {
- return reporterFactory.CreateReporter();
- }
- }).Where(r => r != null).ToArray(),
- new CoverletLogger(eqtTrace, logger),
- coverageWrapper, instrumentationHelper, fileSystem, sourceRootTranslator, cecilSymbolHelper)
+ public CoverageManager(CoverletSettings settings, TestPlatformEqtTrace eqtTrace, TestPlatformLogger logger, ICoverageWrapper coverageWrapper,
+ IInstrumentationHelper instrumentationHelper, IFileSystem fileSystem, ISourceRootTranslator sourceRootTranslator, ICecilSymbolHelper cecilSymbolHelper)
+ : this(settings,
+ settings.ReportFormats.Select(format =>
{
- }
+ var reporterFactory = new ReporterFactory(format);
+ if (!reporterFactory.IsValidFormat())
+ {
+ eqtTrace.Warning($"Invalid report format '{format}'");
+ return null;
+ }
+ else
+ {
+ return reporterFactory.CreateReporter();
+ }
+ }).Where(r => r != null).ToArray(),
+ new CoverletLogger(eqtTrace, logger),
+ coverageWrapper, instrumentationHelper, fileSystem, sourceRootTranslator, cecilSymbolHelper)
+ {
+ }
- public CoverageManager(CoverletSettings settings, IReporter[] reporters, ILogger logger, ICoverageWrapper coverageWrapper,
- IInstrumentationHelper instrumentationHelper, IFileSystem fileSystem, ISourceRootTranslator sourceRootTranslator, ICecilSymbolHelper cecilSymbolHelper)
- {
- // Store input vars
- Reporters = reporters;
- _coverageWrapper = coverageWrapper;
- _sourceRootTranslator = sourceRootTranslator;
- // Coverage object
- _coverage = _coverageWrapper.CreateCoverage(settings, logger, instrumentationHelper, fileSystem, sourceRootTranslator, cecilSymbolHelper);
- }
+ public CoverageManager(CoverletSettings settings, IReporter[] reporters, ILogger logger, ICoverageWrapper coverageWrapper,
+ IInstrumentationHelper instrumentationHelper, IFileSystem fileSystem, ISourceRootTranslator sourceRootTranslator, ICecilSymbolHelper cecilSymbolHelper)
+ {
+ // Store input vars
+ Reporters = reporters;
+ _coverageWrapper = coverageWrapper;
+ _sourceRootTranslator = sourceRootTranslator;
+ // Coverage object
+ _coverage = _coverageWrapper.CreateCoverage(settings, logger, instrumentationHelper, fileSystem, sourceRootTranslator, cecilSymbolHelper);
+ }
- ///
- /// Instrument modules
- ///
- public void InstrumentModules()
- {
- try
- {
- // Instrument modules
- _coverageWrapper.PrepareModules(_coverage);
- }
- catch (Exception ex)
- {
- string errorMessage = string.Format(Resources.InstrumentationException, CoverletConstants.DataCollectorName);
- throw new CoverletDataCollectorException(errorMessage, ex);
- }
- }
+ ///
+ /// Instrument modules
+ ///
+ public void InstrumentModules()
+ {
+ try
+ {
+ // Instrument modules
+ _coverageWrapper.PrepareModules(_coverage);
+ }
+ catch (Exception ex)
+ {
+ throw new CoverletDataCollectorException($"{CoverletConstants.DataCollectorName}: Failed to instrument modules", ex);
+ }
+ }
- ///
- /// Gets coverlet coverage reports
- ///
- /// Coverage reports
- public IEnumerable<(string report, string fileName)> GetCoverageReports()
- {
- // Get coverage result
- CoverageResult coverageResult = GetCoverageResult();
- return GetCoverageReports(coverageResult);
- }
+ ///
+ /// Gets coverlet coverage reports
+ ///
+ /// Coverage reports
+ public IEnumerable<(string report, string fileName)> GetCoverageReports()
+ {
+ // Get coverage result
+ CoverageResult coverageResult = GetCoverageResult();
+ return GetCoverageReports(coverageResult);
+ }
- ///
- /// Gets coverlet coverage result
- ///
- /// Coverage result
- private CoverageResult GetCoverageResult()
- {
- try
- {
- return _coverageWrapper.GetCoverageResult(_coverage);
- }
- catch (Exception ex)
- {
- string errorMessage = string.Format(Resources.CoverageResultException, CoverletConstants.DataCollectorName);
- throw new CoverletDataCollectorException(errorMessage, ex);
- }
- }
+ ///
+ /// Gets coverlet coverage result
+ ///
+ /// Coverage result
+ private CoverageResult GetCoverageResult()
+ {
+ try
+ {
+ return _coverageWrapper.GetCoverageResult(_coverage);
+ }
+ catch (Exception ex)
+ {
+ throw new CoverletDataCollectorException($"{CoverletConstants.DataCollectorName}: Failed to get coverage result", ex);
+ }
+ }
- ///
- /// Gets coverage reports from coverage result
- ///
- /// Coverage result
- /// Coverage reports
- private IEnumerable<(string report, string fileName)> GetCoverageReports(CoverageResult coverageResult)
- {
- try
- {
- return Reporters.Select(reporter => (reporter.Report(coverageResult, _sourceRootTranslator), Path.ChangeExtension(CoverletConstants.DefaultFileName, reporter.Extension)));
- }
- catch (Exception ex)
- {
- string errorMessage = string.Format(Resources.CoverageReportException, CoverletConstants.DataCollectorName);
- throw new CoverletDataCollectorException(errorMessage, ex);
- }
- }
+ ///
+ /// Gets coverage reports from coverage result
+ ///
+ /// Coverage result
+ /// Coverage reports
+ private IEnumerable<(string report, string fileName)> GetCoverageReports(CoverageResult coverageResult)
+ {
+ try
+ {
+ return Reporters.Select(reporter => (reporter.Report(coverageResult, _sourceRootTranslator), Path.ChangeExtension(CoverletConstants.DefaultFileName, reporter.Extension)));
+ }
+ catch (Exception ex)
+ {
+ throw new CoverletDataCollectorException($"{CoverletConstants.DataCollectorName}: Failed to get coverage report", ex);
+ }
}
+ }
}
diff --git a/src/coverlet.collector/DataCollection/CoverageWrapper.cs b/src/coverlet.collector/DataCollection/CoverageWrapper.cs
index 0569ab277..4e3f5a729 100644
--- a/src/coverlet.collector/DataCollection/CoverageWrapper.cs
+++ b/src/coverlet.collector/DataCollection/CoverageWrapper.cs
@@ -7,64 +7,68 @@
namespace Coverlet.Collector.DataCollection
{
+ ///
+ /// Implementation for wrapping over Coverage class in coverlet.core
+ ///
+ internal class CoverageWrapper : ICoverageWrapper
+ {
///
- /// Implementation for wrapping over Coverage class in coverlet.core
+ /// Creates a coverage object from given coverlet settings
///
- internal class CoverageWrapper : ICoverageWrapper
+ /// Coverlet settings
+ /// Coverlet logger
+ ///
+ ///
+ ///
+ ///
+ /// Coverage object
+ public Coverage CreateCoverage(CoverletSettings settings, ILogger coverletLogger, IInstrumentationHelper instrumentationHelper, IFileSystem fileSystem, ISourceRootTranslator sourceRootTranslator, ICecilSymbolHelper cecilSymbolHelper)
{
- ///
- /// Creates a coverage object from given coverlet settings
- ///
- /// Coverlet settings
- /// Coverlet logger
- /// Coverage object
- public Coverage CreateCoverage(CoverletSettings settings, ILogger coverletLogger, IInstrumentationHelper instrumentationHelper, IFileSystem fileSystem, ISourceRootTranslator sourceRootTranslator, ICecilSymbolHelper cecilSymbolHelper)
- {
- CoverageParameters parameters = new()
- {
- IncludeFilters = settings.IncludeFilters,
- IncludeDirectories = settings.IncludeDirectories,
- ExcludeFilters = settings.ExcludeFilters,
- ExcludedSourceFiles = settings.ExcludeSourceFiles,
- ExcludeAttributes = settings.ExcludeAttributes,
- IncludeTestAssembly = settings.IncludeTestAssembly,
- SingleHit = settings.SingleHit,
- MergeWith = settings.MergeWith,
- UseSourceLink = settings.UseSourceLink,
- SkipAutoProps = settings.SkipAutoProps,
- DoesNotReturnAttributes = settings.DoesNotReturnAttributes,
- DeterministicReport = settings.DeterministicReport,
- ExcludeAssembliesWithoutSources = settings.ExcludeAssembliesWithoutSources
- };
+ CoverageParameters parameters = new()
+ {
+ IncludeFilters = settings.IncludeFilters,
+ IncludeDirectories = settings.IncludeDirectories,
+ ExcludeFilters = settings.ExcludeFilters,
+ ExcludedSourceFiles = settings.ExcludeSourceFiles,
+ ExcludeAttributes = settings.ExcludeAttributes,
+ IncludeTestAssembly = settings.IncludeTestAssembly,
+ SingleHit = settings.SingleHit,
+ MergeWith = settings.MergeWith,
+ UseSourceLink = settings.UseSourceLink,
+ SkipAutoProps = settings.SkipAutoProps,
+ DoesNotReturnAttributes = settings.DoesNotReturnAttributes,
+ DeterministicReport = settings.DeterministicReport,
+ ExcludeAssembliesWithoutSources = settings.ExcludeAssembliesWithoutSources
+ };
- return new Coverage(
- settings.TestModule,
- parameters,
- coverletLogger,
- instrumentationHelper,
- fileSystem,
- sourceRootTranslator,
- cecilSymbolHelper);
- }
+ return new Coverage(
+ settings.TestModule,
+ parameters,
+ coverletLogger,
+ instrumentationHelper,
+ fileSystem,
+ sourceRootTranslator,
+ cecilSymbolHelper);
+ }
- ///
- /// Gets the coverage result from provided coverage object
- ///
- /// Coverage
- /// The coverage result
- public CoverageResult GetCoverageResult(Coverage coverage)
- {
- return coverage.GetCoverageResult();
- }
+ ///
+ /// Gets the coverage result from provided coverage object
+ ///
+ /// Coverage
+ /// The coverage result
+ public CoverageResult GetCoverageResult(Coverage coverage)
+ {
+ return coverage.GetCoverageResult();
+ }
- ///
- /// Prepares modules for getting coverage.
- /// Wrapper over coverage.PrepareModules
- ///
- ///
- public void PrepareModules(Coverage coverage)
- {
- coverage.PrepareModules();
- }
+ ///
+ /// Prepares modules for getting coverage.
+ /// Wrapper over coverage.PrepareModules
+ ///
+ ///
+ public void PrepareModules(Coverage coverage)
+ {
+ coverage.PrepareModules();
}
+ }
}
diff --git a/src/coverlet.collector/DataCollection/CoverletCoverageCollector.cs b/src/coverlet.collector/DataCollection/CoverletCoverageCollector.cs
index d40a02e86..c95ba217b 100644
--- a/src/coverlet.collector/DataCollection/CoverletCoverageCollector.cs
+++ b/src/coverlet.collector/DataCollection/CoverletCoverageCollector.cs
@@ -16,223 +16,223 @@
namespace Coverlet.Collector.DataCollection
{
+ ///
+ /// Coverlet coverage out-proc data collector.
+ ///
+ [DataCollectorTypeUri(CoverletConstants.DefaultUri)]
+ [DataCollectorFriendlyName(CoverletConstants.FriendlyName)]
+ public class CoverletCoverageCollector : DataCollector
+ {
+ private readonly TestPlatformEqtTrace _eqtTrace;
+ private readonly ICoverageWrapper _coverageWrapper;
+ private readonly ICountDownEventFactory _countDownEventFactory;
+ private readonly Func _serviceCollectionFactory;
+
+ private DataCollectionEvents _events;
+ private TestPlatformLogger _logger;
+ private XmlElement _configurationElement;
+ private DataCollectionSink _dataSink;
+ private DataCollectionContext _dataCollectionContext;
+ private CoverageManager _coverageManager;
+ private IServiceProvider _serviceProvider;
+
+ public CoverletCoverageCollector() : this(new TestPlatformEqtTrace(), new CoverageWrapper(), new CollectorCountdownEventFactory(), GetDefaultServiceCollection)
+ {
+ }
+
+ internal CoverletCoverageCollector(TestPlatformEqtTrace eqtTrace, ICoverageWrapper coverageWrapper, ICountDownEventFactory countDownEventFactory, Func serviceCollectionFactory) : base()
+ {
+ _eqtTrace = eqtTrace;
+ _coverageWrapper = coverageWrapper;
+ _countDownEventFactory = countDownEventFactory;
+ _serviceCollectionFactory = serviceCollectionFactory;
+ }
+
+ private void AttachDebugger()
+ {
+ if (int.TryParse(Environment.GetEnvironmentVariable("COVERLET_DATACOLLECTOR_OUTOFPROC_DEBUG"), out int result) && result == 1)
+ {
+ Debugger.Launch();
+ Debugger.Break();
+ }
+ }
+
///
- /// Coverlet coverage out-proc data collector.
+ /// Initializes data collector
///
- [DataCollectorTypeUri(CoverletConstants.DefaultUri)]
- [DataCollectorFriendlyName(CoverletConstants.FriendlyName)]
- public class CoverletCoverageCollector : DataCollector
+ /// Configuration element
+ /// Events to register on
+ /// Data sink to send attachments to test platform
+ /// Test platform logger
+ /// Environment context
+ public override void Initialize(
+ XmlElement configurationElement,
+ DataCollectionEvents events,
+ DataCollectionSink dataSink,
+ DataCollectionLogger logger,
+ DataCollectionEnvironmentContext environmentContext)
{
- private readonly TestPlatformEqtTrace _eqtTrace;
- private readonly ICoverageWrapper _coverageWrapper;
- private readonly ICountDownEventFactory _countDownEventFactory;
- private readonly Func _serviceCollectionFactory;
-
- private DataCollectionEvents _events;
- private TestPlatformLogger _logger;
- private XmlElement _configurationElement;
- private DataCollectionSink _dataSink;
- private DataCollectionContext _dataCollectionContext;
- private CoverageManager _coverageManager;
- private IServiceProvider _serviceProvider;
-
- public CoverletCoverageCollector() : this(new TestPlatformEqtTrace(), new CoverageWrapper(), new CollectorCountdownEventFactory(), GetDefaultServiceCollection)
- {
- }
- internal CoverletCoverageCollector(TestPlatformEqtTrace eqtTrace, ICoverageWrapper coverageWrapper, ICountDownEventFactory countDownEventFactory, Func serviceCollectionFactory) : base()
- {
- _eqtTrace = eqtTrace;
- _coverageWrapper = coverageWrapper;
- _countDownEventFactory = countDownEventFactory;
- _serviceCollectionFactory = serviceCollectionFactory;
- }
+ AttachDebugger();
- private void AttachDebugger()
- {
- if (int.TryParse(Environment.GetEnvironmentVariable("COVERLET_DATACOLLECTOR_OUTOFPROC_DEBUG"), out int result) && result == 1)
- {
- Debugger.Launch();
- Debugger.Break();
- }
- }
+ if (_eqtTrace.IsInfoEnabled)
+ {
+ _eqtTrace.Info("Initializing {0} with configuration: '{1}'", CoverletConstants.DataCollectorName, configurationElement?.OuterXml);
+ }
- ///
- /// Initializes data collector
- ///
- /// Configuration element
- /// Events to register on
- /// Data sink to send attachments to test platform
- /// Test platform logger
- /// Environment context
- public override void Initialize(
- XmlElement configurationElement,
- DataCollectionEvents events,
- DataCollectionSink dataSink,
- DataCollectionLogger logger,
- DataCollectionEnvironmentContext environmentContext)
- {
+ // Store input variables
+ _events = events;
+ _configurationElement = configurationElement;
+ _dataSink = dataSink;
+ _dataCollectionContext = environmentContext.SessionDataCollectionContext;
+ _logger = new TestPlatformLogger(logger, _dataCollectionContext);
- AttachDebugger();
+ // Register events
+ _events.SessionStart += OnSessionStart;
+ _events.SessionEnd += OnSessionEnd;
+ }
- if (_eqtTrace.IsInfoEnabled)
- {
- _eqtTrace.Info("Initializing {0} with configuration: '{1}'", CoverletConstants.DataCollectorName, configurationElement?.OuterXml);
- }
+ ///
+ /// Disposes the data collector
+ ///
+ /// Disposing flag
+ protected override void Dispose(bool disposing)
+ {
+ _eqtTrace.Verbose("{0}: Disposing", CoverletConstants.DataCollectorName);
- // Store input variables
- _events = events;
- _configurationElement = configurationElement;
- _dataSink = dataSink;
- _dataCollectionContext = environmentContext.SessionDataCollectionContext;
- _logger = new TestPlatformLogger(logger, _dataCollectionContext);
+ // Unregister events
+ if (_events != null)
+ {
+ _events.SessionStart -= OnSessionStart;
+ _events.SessionEnd -= OnSessionEnd;
+ }
- // Register events
- _events.SessionStart += OnSessionStart;
- _events.SessionEnd += OnSessionEnd;
- }
+ // Remove vars
+ _events = null;
+ _dataSink = null;
+ _coverageManager = null;
- ///
- /// Disposes the data collector
- ///
- /// Disposing flag
- protected override void Dispose(bool disposing)
- {
- _eqtTrace.Verbose("{0}: Disposing", CoverletConstants.DataCollectorName);
+ base.Dispose(disposing);
+ }
- // Unregister events
- if (_events != null)
- {
- _events.SessionStart -= OnSessionStart;
- _events.SessionEnd -= OnSessionEnd;
- }
+ ///
+ /// SessionStart event handler
+ ///
+ /// Sender
+ /// Event args
+ private void OnSessionStart(object sender, SessionStartEventArgs sessionStartEventArgs)
+ {
+ _eqtTrace.Verbose("{0}: SessionStart received", CoverletConstants.DataCollectorName);
+
+ try
+ {
+ // Get coverlet settings
+ IEnumerable testModules = GetTestModules(sessionStartEventArgs);
+ var coverletSettingsParser = new CoverletSettingsParser(_eqtTrace);
+ CoverletSettings coverletSettings = coverletSettingsParser.Parse(_configurationElement, testModules);
+
+ // Build services container
+ _serviceProvider = _serviceCollectionFactory(_eqtTrace, _logger, coverletSettings.TestModule).BuildServiceProvider();
+
+ // Get coverage and attachment managers
+ _coverageManager = new CoverageManager(coverletSettings, _eqtTrace, _logger, _coverageWrapper,
+ _serviceProvider.GetRequiredService(), _serviceProvider.GetRequiredService(),
+ _serviceProvider.GetRequiredService(), _serviceProvider.GetRequiredService());
+
+ // Instrument modules
+ _coverageManager.InstrumentModules();
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning(ex.ToString());
+ Dispose(true);
+ }
+ }
- // Remove vars
- _events = null;
- _dataSink = null;
- _coverageManager = null;
+ ///
+ /// SessionEnd event handler
+ ///
+ /// Sender
+ /// Event args
+ private void OnSessionEnd(object sender, SessionEndEventArgs e)
+ {
+ try
+ {
+ _eqtTrace.Verbose("{0}: SessionEnd received", CoverletConstants.DataCollectorName);
- base.Dispose(disposing);
- }
+ // Get coverage reports
+ IEnumerable<(string report, string fileName)> coverageReports = _coverageManager?.GetCoverageReports();
- ///
- /// SessionStart event handler
- ///
- /// Sender
- /// Event args
- private void OnSessionStart(object sender, SessionStartEventArgs sessionStartEventArgs)
+ if (coverageReports != null && coverageReports.Any())
{
- _eqtTrace.Verbose("{0}: SessionStart received", CoverletConstants.DataCollectorName);
-
- try
- {
- // Get coverlet settings
- IEnumerable testModules = GetTestModules(sessionStartEventArgs);
- var coverletSettingsParser = new CoverletSettingsParser(_eqtTrace);
- CoverletSettings coverletSettings = coverletSettingsParser.Parse(_configurationElement, testModules);
-
- // Build services container
- _serviceProvider = _serviceCollectionFactory(_eqtTrace, _logger, coverletSettings.TestModule).BuildServiceProvider();
-
- // Get coverage and attachment managers
- _coverageManager = new CoverageManager(coverletSettings, _eqtTrace, _logger, _coverageWrapper,
- _serviceProvider.GetRequiredService(), _serviceProvider.GetRequiredService(),
- _serviceProvider.GetRequiredService(), _serviceProvider.GetRequiredService());
-
- // Instrument modules
- _coverageManager.InstrumentModules();
- }
- catch (Exception ex)
- {
- _logger.LogWarning(ex.ToString());
- Dispose(true);
- }
+ // Send result attachments to test platform.
+ using var attachmentManager = new AttachmentManager(_dataSink, _dataCollectionContext, _logger, _eqtTrace, _countDownEventFactory.Create(coverageReports.Count(), TimeSpan.FromSeconds(30)));
+ foreach ((string report, string fileName) in coverageReports)
+ {
+ attachmentManager.SendCoverageReport(report, fileName);
+ }
}
-
- ///
- /// SessionEnd event handler
- ///
- /// Sender
- /// Event args
- private void OnSessionEnd(object sender, SessionEndEventArgs e)
+ else
{
- try
- {
- _eqtTrace.Verbose("{0}: SessionEnd received", CoverletConstants.DataCollectorName);
-
- // Get coverage reports
- IEnumerable<(string report, string fileName)> coverageReports = _coverageManager?.GetCoverageReports();
-
- if (coverageReports != null && coverageReports.Any())
- {
- // Send result attachments to test platform.
- using var attachmentManager = new AttachmentManager(_dataSink, _dataCollectionContext, _logger, _eqtTrace, _countDownEventFactory.Create(coverageReports.Count(), TimeSpan.FromSeconds(30)));
- foreach ((string report, string fileName) in coverageReports)
- {
- attachmentManager.SendCoverageReport(report, fileName);
- }
- }
- else
- {
- _eqtTrace.Verbose("{0}: No coverage reports specified", CoverletConstants.DataCollectorName);
- }
- }
- catch (Exception ex)
- {
- _logger.LogWarning(ex.ToString());
- Dispose(true);
- }
+ _eqtTrace.Verbose("{0}: No coverage reports specified", CoverletConstants.DataCollectorName);
}
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning(ex.ToString());
+ Dispose(true);
+ }
+ }
- ///
- /// Gets test modules
- ///
- /// Event args
- /// Test modules list
- private IEnumerable GetTestModules(SessionStartEventArgs sessionStartEventArgs)
+ ///
+ /// Gets test modules
+ ///
+ /// Event args
+ /// Test modules list
+ private IEnumerable GetTestModules(SessionStartEventArgs sessionStartEventArgs)
+ {
+ try
+ {
+ IEnumerable testModules = GetPropertyValueWrapper(sessionStartEventArgs);
+ if (_eqtTrace.IsInfoEnabled)
{
- try
- {
- IEnumerable testModules = GetPropertyValueWrapper(sessionStartEventArgs);
- if (_eqtTrace.IsInfoEnabled)
- {
- _eqtTrace.Info("{0}: TestModules: '{1}'",
- CoverletConstants.DataCollectorName,
- string.Join(",", testModules ?? Enumerable.Empty()));
- }
- return testModules;
- }
- catch (MissingMethodException ex)
- {
- throw new MissingMethodException("Make sure to use .NET core SDK Version >= 2.2.300", ex);
- }
+ _eqtTrace.Info("{0}: TestModules: '{1}'",
+ CoverletConstants.DataCollectorName,
+ string.Join(",", testModules ?? Enumerable.Empty()));
}
+ return testModules;
+ }
+ catch (MissingMethodException ex)
+ {
+ throw new MissingMethodException("Make sure to use .NET core SDK Version >= 2.2.300", ex);
+ }
+ }
- ///
- /// Wraps GetPropertyValue to catch possible MissingMethodException on unsupported runtime
- ///
- ///
- ///
- private static IEnumerable GetPropertyValueWrapper(SessionStartEventArgs sessionStartEventArgs)
- {
- return sessionStartEventArgs.GetPropertyValue>(CoverletConstants.TestSourcesPropertyName);
- }
+ ///
+ /// Wraps GetPropertyValue to catch possible MissingMethodException on unsupported runtime
+ ///
+ ///
+ ///
+ private static IEnumerable GetPropertyValueWrapper(SessionStartEventArgs sessionStartEventArgs)
+ {
+ return sessionStartEventArgs.GetPropertyValue>(CoverletConstants.TestSourcesPropertyName);
+ }
- private static IServiceCollection GetDefaultServiceCollection(TestPlatformEqtTrace eqtTrace, TestPlatformLogger logger, string testModule)
- {
- IServiceCollection serviceCollection = new ServiceCollection();
- serviceCollection.AddTransient();
- serviceCollection.AddTransient();
- serviceCollection.AddTransient();
- serviceCollection.AddTransient();
- serviceCollection.AddTransient(_ => new CoverletLogger(eqtTrace, logger));
- // We need to keep singleton/static semantics
- serviceCollection.AddSingleton();
- // We cache resolutions
- serviceCollection.AddSingleton(serviceProvider =>
- new SourceRootTranslator(testModule, serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService()));
- serviceCollection.AddSingleton();
- return serviceCollection;
- }
+ private static IServiceCollection GetDefaultServiceCollection(TestPlatformEqtTrace eqtTrace, TestPlatformLogger logger, string testModule)
+ {
+ IServiceCollection serviceCollection = new ServiceCollection();
+ serviceCollection.AddTransient();
+ serviceCollection.AddTransient();
+ serviceCollection.AddTransient();
+ serviceCollection.AddTransient();
+ serviceCollection.AddTransient(_ => new CoverletLogger(eqtTrace, logger));
+ // We need to keep singleton/static semantics
+ serviceCollection.AddSingleton();
+ // We cache resolutions
+ serviceCollection.AddSingleton(serviceProvider =>
+ new SourceRootTranslator(testModule, serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService()));
+ serviceCollection.AddSingleton();
+ return serviceCollection;
}
+ }
}
diff --git a/src/coverlet.collector/DataCollection/CoverletLogger.cs b/src/coverlet.collector/DataCollection/CoverletLogger.cs
index 42eb6a1b2..543f5417d 100644
--- a/src/coverlet.collector/DataCollection/CoverletLogger.cs
+++ b/src/coverlet.collector/DataCollection/CoverletLogger.cs
@@ -7,64 +7,64 @@
namespace Coverlet.Collector.DataCollection
{
+ ///
+ /// Coverlet logger
+ ///
+ internal class CoverletLogger : ILogger
+ {
+ private readonly TestPlatformEqtTrace _eqtTrace;
+ private readonly TestPlatformLogger _logger;
+
+ public CoverletLogger(TestPlatformEqtTrace eqtTrace, TestPlatformLogger logger)
+ {
+ _eqtTrace = eqtTrace;
+ _logger = logger;
+ }
+
///
- /// Coverlet logger
+ /// Logs error
///
- internal class CoverletLogger : ILogger
+ /// Error message
+ public void LogError(string message)
{
- private readonly TestPlatformEqtTrace _eqtTrace;
- private readonly TestPlatformLogger _logger;
-
- public CoverletLogger(TestPlatformEqtTrace eqtTrace, TestPlatformLogger logger)
- {
- _eqtTrace = eqtTrace;
- _logger = logger;
- }
-
- ///
- /// Logs error
- ///
- /// Error message
- public void LogError(string message)
- {
- _logger.LogWarning(message);
- }
+ _logger.LogWarning(message);
+ }
- ///
- /// Logs error
- ///
- /// Exception to log
- public void LogError(Exception exception)
- {
- _logger.LogWarning(exception.ToString());
- }
+ ///
+ /// Logs error
+ ///
+ /// Exception to log
+ public void LogError(Exception exception)
+ {
+ _logger.LogWarning(exception.ToString());
+ }
- ///
- /// Logs information
- ///
- /// Information message
- /// importance
- public void LogInformation(string message, bool important = false)
- {
- _eqtTrace.Info(message);
- }
+ ///
+ /// Logs information
+ ///
+ /// Information message
+ /// importance
+ public void LogInformation(string message, bool important = false)
+ {
+ _eqtTrace.Info(message);
+ }
- ///
- /// Logs verbose
- ///
- /// Verbose message
- public void LogVerbose(string message)
- {
- _eqtTrace.Verbose(message);
- }
+ ///
+ /// Logs verbose
+ ///
+ /// Verbose message
+ public void LogVerbose(string message)
+ {
+ _eqtTrace.Verbose(message);
+ }
- ///
- /// Logs warning
- ///
- /// Warning message
- public void LogWarning(string message)
- {
- _eqtTrace.Warning(message);
- }
+ ///
+ /// Logs warning
+ ///
+ /// Warning message
+ public void LogWarning(string message)
+ {
+ _eqtTrace.Warning(message);
}
+ }
}
diff --git a/src/coverlet.collector/DataCollection/CoverletSettings.cs b/src/coverlet.collector/DataCollection/CoverletSettings.cs
index 2ebde799f..0c80687f9 100644
--- a/src/coverlet.collector/DataCollection/CoverletSettings.cs
+++ b/src/coverlet.collector/DataCollection/CoverletSettings.cs
@@ -6,106 +6,106 @@
namespace Coverlet.Collector.DataCollection
{
+ ///
+ /// Coverlet settings
+ ///
+ internal class CoverletSettings
+ {
///
- /// Coverlet settings
+ /// Test module
///
- internal class CoverletSettings
+ public string TestModule { get; set; }
+
+ ///
+ /// Report formats
+ ///
+ public string[] ReportFormats { get; set; }
+
+ ///
+ /// Filters to include
+ ///
+ public string[] IncludeFilters { get; set; }
+
+ ///
+ /// Directories to include
+ ///
+ public string[] IncludeDirectories { get; set; }
+
+ ///
+ /// Filters to exclude
+ ///
+ public string[] ExcludeFilters { get; set; }
+
+ ///
+ /// Source files to exclude
+ ///
+ public string[] ExcludeSourceFiles { get; set; }
+
+ ///
+ /// Attributes to exclude
+ ///
+ public string[] ExcludeAttributes { get; set; }
+
+ ///
+ /// Coverate report path to merge with
+ ///
+ public string MergeWith { get; set; }
+
+ ///
+ /// Use source link flag
+ ///
+ public bool UseSourceLink { get; set; }
+
+ ///
+ /// Single hit flag
+ ///
+ public bool SingleHit { get; set; }
+
+ ///
+ /// Includes test assembly
+ ///
+ public bool IncludeTestAssembly { get; set; }
+
+ ///
+ /// Neither track nor record auto-implemented properties.
+ ///
+ public bool SkipAutoProps { get; set; }
+
+ ///
+ /// Attributes that mark methods that never return.
+ ///
+ public string[] DoesNotReturnAttributes { get; set; }
+
+ ///
+ /// DeterministicReport flag
+ ///
+ public bool DeterministicReport { get; set; }
+
+ ///
+ /// Switch for heuristic to automatically exclude assemblies without source.
+ ///
+ public string ExcludeAssembliesWithoutSources { get; set; }
+
+ public override string ToString()
{
- ///
- /// Test module
- ///
- public string TestModule { get; set; }
-
- ///
- /// Report formats
- ///
- public string[] ReportFormats { get; set; }
-
- ///
- /// Filters to include
- ///
- public string[] IncludeFilters { get; set; }
-
- ///
- /// Directories to include
- ///
- public string[] IncludeDirectories { get; set; }
-
- ///
- /// Filters to exclude
- ///
- public string[] ExcludeFilters { get; set; }
-
- ///
- /// Source files to exclude
- ///
- public string[] ExcludeSourceFiles { get; set; }
-
- ///
- /// Attributes to exclude
- ///
- public string[] ExcludeAttributes { get; set; }
-
- ///
- /// Coverate report path to merge with
- ///
- public string MergeWith { get; set; }
-
- ///
- /// Use source link flag
- ///
- public bool UseSourceLink { get; set; }
-
- ///
- /// Single hit flag
- ///
- public bool SingleHit { get; set; }
-
- ///
- /// Includes test assembly
- ///
- public bool IncludeTestAssembly { get; set; }
-
- ///
- /// Neither track nor record auto-implemented properties.
- ///
- public bool SkipAutoProps { get; set; }
-
- ///
- /// Attributes that mark methods that never return.
- ///
- public string[] DoesNotReturnAttributes { get; set; }
-
- ///
- /// DeterministicReport flag
- ///
- public bool DeterministicReport { get; set; }
-
- ///
- /// Switch for heuristic to automatically exclude assemblies without source.
- ///
- public string ExcludeAssembliesWithoutSources { get; set; }
-
- public override string ToString()
- {
- var builder = new StringBuilder();
-
- builder.AppendFormat("TestModule: '{0}', ", TestModule);
- builder.AppendFormat("IncludeFilters: '{0}', ", string.Join(",", IncludeFilters ?? Enumerable.Empty()));
- builder.AppendFormat("IncludeDirectories: '{0}', ", string.Join(",", IncludeDirectories ?? Enumerable.Empty()));
- builder.AppendFormat("ExcludeFilters: '{0}', ", string.Join(",", ExcludeFilters ?? Enumerable.Empty()));
- builder.AppendFormat("ExcludeSourceFiles: '{0}', ", string.Join(",", ExcludeSourceFiles ?? Enumerable.Empty()));
- builder.AppendFormat("ExcludeAttributes: '{0}', ", string.Join(",", ExcludeAttributes ?? Enumerable.Empty()));
- builder.AppendFormat("MergeWith: '{0}', ", MergeWith);
- builder.AppendFormat("UseSourceLink: '{0}'", UseSourceLink);
- builder.AppendFormat("SingleHit: '{0}'", SingleHit);
- builder.AppendFormat("IncludeTestAssembly: '{0}'", IncludeTestAssembly);
- builder.AppendFormat("SkipAutoProps: '{0}'", SkipAutoProps);
- builder.AppendFormat("DoesNotReturnAttributes: '{0}'", string.Join(",", DoesNotReturnAttributes ?? Enumerable.Empty()));
- builder.AppendFormat("DeterministicReport: '{0}'", DeterministicReport);
- builder.AppendFormat("ExcludeAssembliesWithoutSources: '{0}'", ExcludeAssembliesWithoutSources);
-
- return builder.ToString();
- }
+ var builder = new StringBuilder();
+
+ builder.AppendFormat("TestModule: '{0}', ", TestModule);
+ builder.AppendFormat("IncludeFilters: '{0}', ", string.Join(",", IncludeFilters ?? Enumerable.Empty()));
+ builder.AppendFormat("IncludeDirectories: '{0}', ", string.Join(",", IncludeDirectories ?? Enumerable.Empty()));
+ builder.AppendFormat("ExcludeFilters: '{0}', ", string.Join(",", ExcludeFilters ?? Enumerable.Empty()));
+ builder.AppendFormat("ExcludeSourceFiles: '{0}', ", string.Join(",", ExcludeSourceFiles ?? Enumerable.Empty()));
+ builder.AppendFormat("ExcludeAttributes: '{0}', ", string.Join(",", ExcludeAttributes ?? Enumerable.Empty()));
+ builder.AppendFormat("MergeWith: '{0}', ", MergeWith);
+ builder.AppendFormat("UseSourceLink: '{0}'", UseSourceLink);
+ builder.AppendFormat("SingleHit: '{0}'", SingleHit);
+ builder.AppendFormat("IncludeTestAssembly: '{0}'", IncludeTestAssembly);
+ builder.AppendFormat("SkipAutoProps: '{0}'", SkipAutoProps);
+ builder.AppendFormat("DoesNotReturnAttributes: '{0}'", string.Join(",", DoesNotReturnAttributes ?? Enumerable.Empty()));
+ builder.AppendFormat("DeterministicReport: '{0}'", DeterministicReport);
+ builder.AppendFormat("ExcludeAssembliesWithoutSources: '{0}'", ExcludeAssembliesWithoutSources);
+
+ return builder.ToString();
}
+ }
}
diff --git a/src/coverlet.collector/DataCollection/CoverletSettingsParser.cs b/src/coverlet.collector/DataCollection/CoverletSettingsParser.cs
index 3776c98d4..733dacfcc 100644
--- a/src/coverlet.collector/DataCollection/CoverletSettingsParser.cs
+++ b/src/coverlet.collector/DataCollection/CoverletSettingsParser.cs
@@ -5,267 +5,265 @@
using System.Collections.Generic;
using System.Linq;
using System.Xml;
-using coverlet.collector.Resources;
using Coverlet.Collector.Utilities;
namespace Coverlet.Collector.DataCollection
{
+ ///
+ /// Coverlet settings parser
+ ///
+ internal class CoverletSettingsParser
+ {
+ private readonly TestPlatformEqtTrace _eqtTrace;
+
+ public CoverletSettingsParser(TestPlatformEqtTrace eqtTrace)
+ {
+ _eqtTrace = eqtTrace;
+ }
+
///
- /// Coverlet settings parser
+ /// Parser coverlet settings
///
- internal class CoverletSettingsParser
+ /// Configuration element
+ /// Test modules
+ /// Coverlet settings
+ public CoverletSettings Parse(XmlElement configurationElement, IEnumerable testModules)
{
- private readonly TestPlatformEqtTrace _eqtTrace;
+ var coverletSettings = new CoverletSettings
+ {
+ TestModule = ParseTestModule(testModules)
+ };
- public CoverletSettingsParser(TestPlatformEqtTrace eqtTrace)
- {
- _eqtTrace = eqtTrace;
- }
+ if (configurationElement != null)
+ {
+ coverletSettings.IncludeFilters = ParseIncludeFilters(configurationElement);
+ coverletSettings.IncludeDirectories = ParseIncludeDirectories(configurationElement);
+ coverletSettings.ExcludeAttributes = ParseExcludeAttributes(configurationElement);
+ coverletSettings.ExcludeSourceFiles = ParseExcludeSourceFiles(configurationElement);
+ coverletSettings.MergeWith = ParseMergeWith(configurationElement);
+ coverletSettings.UseSourceLink = ParseUseSourceLink(configurationElement);
+ coverletSettings.SingleHit = ParseSingleHit(configurationElement);
+ coverletSettings.IncludeTestAssembly = ParseIncludeTestAssembly(configurationElement);
+ coverletSettings.SkipAutoProps = ParseSkipAutoProps(configurationElement);
+ coverletSettings.DoesNotReturnAttributes = ParseDoesNotReturnAttributes(configurationElement);
+ coverletSettings.DeterministicReport = ParseDeterministicReport(configurationElement);
+ coverletSettings.ExcludeAssembliesWithoutSources = ParseExcludeAssembliesWithoutSources(configurationElement);
+ }
- ///
- /// Parser coverlet settings
- ///
- /// Configuration element
- /// Test modules
- /// Coverlet settings
- public CoverletSettings Parse(XmlElement configurationElement, IEnumerable testModules)
- {
- var coverletSettings = new CoverletSettings
- {
- TestModule = ParseTestModule(testModules)
- };
+ coverletSettings.ReportFormats = ParseReportFormats(configurationElement);
+ coverletSettings.ExcludeFilters = ParseExcludeFilters(configurationElement);
- if (configurationElement != null)
- {
- coverletSettings.IncludeFilters = ParseIncludeFilters(configurationElement);
- coverletSettings.IncludeDirectories = ParseIncludeDirectories(configurationElement);
- coverletSettings.ExcludeAttributes = ParseExcludeAttributes(configurationElement);
- coverletSettings.ExcludeSourceFiles = ParseExcludeSourceFiles(configurationElement);
- coverletSettings.MergeWith = ParseMergeWith(configurationElement);
- coverletSettings.UseSourceLink = ParseUseSourceLink(configurationElement);
- coverletSettings.SingleHit = ParseSingleHit(configurationElement);
- coverletSettings.IncludeTestAssembly = ParseIncludeTestAssembly(configurationElement);
- coverletSettings.SkipAutoProps = ParseSkipAutoProps(configurationElement);
- coverletSettings.DoesNotReturnAttributes = ParseDoesNotReturnAttributes(configurationElement);
- coverletSettings.DeterministicReport = ParseDeterministicReport(configurationElement);
- coverletSettings.ExcludeAssembliesWithoutSources = ParseExcludeAssembliesWithoutSources(configurationElement);
- }
+ if (_eqtTrace.IsVerboseEnabled)
+ {
+ _eqtTrace.Verbose("{0}: Initializing coverlet process with settings: \"{1}\"", CoverletConstants.DataCollectorName, coverletSettings.ToString());
+ }
- coverletSettings.ReportFormats = ParseReportFormats(configurationElement);
- coverletSettings.ExcludeFilters = ParseExcludeFilters(configurationElement);
+ return coverletSettings;
+ }
- if (_eqtTrace.IsVerboseEnabled)
- {
- _eqtTrace.Verbose("{0}: Initializing coverlet process with settings: \"{1}\"", CoverletConstants.DataCollectorName, coverletSettings.ToString());
- }
+ ///
+ /// Parses test module
+ ///
+ /// Test modules
+ /// Test module
+ private static string ParseTestModule(IEnumerable testModules)
+ {
+ // Validate if at least one source present.
+ if (testModules == null || !testModules.Any())
+ {
+ throw new CoverletDataCollectorException($"{CoverletConstants.DataCollectorName}: No test modules found");
+ }
- return coverletSettings;
- }
+ // Note:
+ // 1) .NET core test run supports one testModule per run. Coverlet also supports one testModule per run. So, we are using first testSource only and ignoring others.
+ // 2) If and when .NET full is supported with coverlet OR .NET core starts supporting multiple testModules, revisit this code to use other testModules as well.
+ return testModules.FirstOrDefault();
+ }
- ///
- /// Parses test module
- ///
- /// Test modules
- /// Test module
- 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);
- }
+ ///
+ /// Parse report formats
+ ///
+ /// Configuration element
+ /// Report formats
+ private static string[] ParseReportFormats(XmlElement configurationElement)
+ {
+ string[] formats = Array.Empty();
+ if (configurationElement != null)
+ {
+ XmlElement reportFormatElement = configurationElement[CoverletConstants.ReportFormatElementName];
+ formats = SplitElement(reportFormatElement);
+ }
- // Note:
- // 1) .NET core test run supports one testModule per run. Coverlet also supports one testModule per run. So, we are using first testSource only and ignoring others.
- // 2) If and when .NET full is supported with coverlet OR .NET core starts supporting multiple testModules, revisit this code to use other testModules as well.
- return testModules.FirstOrDefault();
- }
+ return formats is null || formats.Length == 0 ? new[] { CoverletConstants.DefaultReportFormat } : formats;
+ }
- ///
- /// Parse report formats
- ///
- /// Configuration element
- /// Report formats
- private static string[] ParseReportFormats(XmlElement configurationElement)
- {
- string[] formats = Array.Empty();
- if (configurationElement != null)
- {
- XmlElement reportFormatElement = configurationElement[CoverletConstants.ReportFormatElementName];
- formats = SplitElement(reportFormatElement);
- }
+ ///
+ /// Parse filters to include
+ ///
+ /// Configuration element
+ /// Filters to include
+ private static string[] ParseIncludeFilters(XmlElement configurationElement)
+ {
+ XmlElement includeFiltersElement = configurationElement[CoverletConstants.IncludeFiltersElementName];
+ return SplitElement(includeFiltersElement);
+ }
- return formats is null || formats.Length == 0 ? new[] { CoverletConstants.DefaultReportFormat } : formats;
- }
+ ///
+ /// Parse directories to include
+ ///
+ /// Configuration element
+ /// Directories to include
+ private static string[] ParseIncludeDirectories(XmlElement configurationElement)
+ {
+ XmlElement includeDirectoriesElement = configurationElement[CoverletConstants.IncludeDirectoriesElementName];
+ return SplitElement(includeDirectoriesElement);
+ }
- ///
- /// Parse filters to include
- ///
- /// Configuration element
- /// Filters to include
- private static string[] ParseIncludeFilters(XmlElement configurationElement)
- {
- XmlElement includeFiltersElement = configurationElement[CoverletConstants.IncludeFiltersElementName];
- return SplitElement(includeFiltersElement);
- }
+ ///
+ /// Parse filters to exclude
+ ///
+ /// Configuration element
+ /// Filters to exclude
+ private static string[] ParseExcludeFilters(XmlElement configurationElement)
+ {
+ var excludeFilters = new List { CoverletConstants.DefaultExcludeFilter };
- ///
- /// Parse directories to include
- ///
- /// Configuration element
- /// Directories to include
- private static string[] ParseIncludeDirectories(XmlElement configurationElement)
+ if (configurationElement != null)
+ {
+ XmlElement excludeFiltersElement = configurationElement[CoverletConstants.ExcludeFiltersElementName];
+ string[] filters = SplitElement(excludeFiltersElement);
+ if (filters != null)
{
- XmlElement includeDirectoriesElement = configurationElement[CoverletConstants.IncludeDirectoriesElementName];
- return SplitElement(includeDirectoriesElement);
+ excludeFilters.AddRange(filters);
}
+ }
- ///
- /// Parse filters to exclude
- ///
- /// Configuration element
- /// Filters to exclude
- private static string[] ParseExcludeFilters(XmlElement configurationElement)
- {
- var excludeFilters = new List { CoverletConstants.DefaultExcludeFilter };
-
- if (configurationElement != null)
- {
- XmlElement excludeFiltersElement = configurationElement[CoverletConstants.ExcludeFiltersElementName];
- string[] filters = SplitElement(excludeFiltersElement);
- if (filters != null)
- {
- excludeFilters.AddRange(filters);
- }
- }
-
- return excludeFilters.ToArray();
- }
+ return excludeFilters.ToArray();
+ }
- ///
- /// Parse source files to exclude
- ///
- /// Configuration element
- /// Source files to exclude
- private static string[] ParseExcludeSourceFiles(XmlElement configurationElement)
- {
- XmlElement excludeSourceFilesElement = configurationElement[CoverletConstants.ExcludeSourceFilesElementName];
- return SplitElement(excludeSourceFilesElement);
- }
+ ///
+ /// Parse source files to exclude
+ ///
+ /// Configuration element
+ /// Source files to exclude
+ private static string[] ParseExcludeSourceFiles(XmlElement configurationElement)
+ {
+ XmlElement excludeSourceFilesElement = configurationElement[CoverletConstants.ExcludeSourceFilesElementName];
+ return SplitElement(excludeSourceFilesElement);
+ }
- ///
- /// Parse attributes to exclude
- ///
- /// Configuration element
- /// Attributes to exclude
- private static string[] ParseExcludeAttributes(XmlElement configurationElement)
- {
- XmlElement excludeAttributesElement = configurationElement[CoverletConstants.ExcludeAttributesElementName];
- return SplitElement(excludeAttributesElement);
- }
+ ///
+ /// Parse attributes to exclude
+ ///
+ /// Configuration element
+ /// Attributes to exclude
+ private static string[] ParseExcludeAttributes(XmlElement configurationElement)
+ {
+ XmlElement excludeAttributesElement = configurationElement[CoverletConstants.ExcludeAttributesElementName];
+ return SplitElement(excludeAttributesElement);
+ }
- ///
- /// Parse merge with attribute
- ///
- /// Configuration element
- /// Merge with attribute
- private static string ParseMergeWith(XmlElement configurationElement)
- {
- XmlElement mergeWithElement = configurationElement[CoverletConstants.MergeWithElementName];
- return mergeWithElement?.InnerText;
- }
+ ///
+ /// Parse merge with attribute
+ ///
+ /// Configuration element
+ /// Merge with attribute
+ private static string ParseMergeWith(XmlElement configurationElement)
+ {
+ XmlElement mergeWithElement = configurationElement[CoverletConstants.MergeWithElementName];
+ return mergeWithElement?.InnerText;
+ }
- ///
- /// Parse use source link flag
- ///
- /// Configuration element
- /// Use source link flag
- private static bool ParseUseSourceLink(XmlElement configurationElement)
- {
- XmlElement useSourceLinkElement = configurationElement[CoverletConstants.UseSourceLinkElementName];
- bool.TryParse(useSourceLinkElement?.InnerText, out bool useSourceLink);
- return useSourceLink;
- }
+ ///
+ /// Parse use source link flag
+ ///
+ /// Configuration element
+ /// Use source link flag
+ private static bool ParseUseSourceLink(XmlElement configurationElement)
+ {
+ XmlElement useSourceLinkElement = configurationElement[CoverletConstants.UseSourceLinkElementName];
+ bool.TryParse(useSourceLinkElement?.InnerText, out bool useSourceLink);
+ return useSourceLink;
+ }
- ///
- /// Parse single hit flag
- ///
- /// Configuration element
- /// Single hit flag
- private static bool ParseSingleHit(XmlElement configurationElement)
- {
- XmlElement singleHitElement = configurationElement[CoverletConstants.SingleHitElementName];
- bool.TryParse(singleHitElement?.InnerText, out bool singleHit);
- return singleHit;
- }
+ ///
+ /// Parse single hit flag
+ ///
+ /// Configuration element
+ /// Single hit flag
+ private static bool ParseSingleHit(XmlElement configurationElement)
+ {
+ XmlElement singleHitElement = configurationElement[CoverletConstants.SingleHitElementName];
+ bool.TryParse(singleHitElement?.InnerText, out bool singleHit);
+ return singleHit;
+ }
- ///
- /// Parse ParseDeterministicReport flag
- ///
- /// Configuration element
- /// ParseDeterministicReport flag
- private static bool ParseDeterministicReport(XmlElement configurationElement)
- {
- XmlElement deterministicReportElement = configurationElement[CoverletConstants.DeterministicReport];
- bool.TryParse(deterministicReportElement?.InnerText, out bool deterministicReport);
- return deterministicReport;
- }
+ ///
+ /// Parse ParseDeterministicReport flag
+ ///
+ /// Configuration element
+ /// ParseDeterministicReport flag
+ private static bool ParseDeterministicReport(XmlElement configurationElement)
+ {
+ XmlElement deterministicReportElement = configurationElement[CoverletConstants.DeterministicReport];
+ bool.TryParse(deterministicReportElement?.InnerText, out bool deterministicReport);
+ return deterministicReport;
+ }
- ///
- /// Parse ExcludeAssembliesWithoutSources flag
- ///
- /// Configuration element
- /// ExcludeAssembliesWithoutSources flag
- private static string ParseExcludeAssembliesWithoutSources(XmlElement configurationElement)
- {
- XmlElement instrumentModulesWithoutLocalSourcesElement = configurationElement[CoverletConstants.ExcludeAssembliesWithoutSources];
- return instrumentModulesWithoutLocalSourcesElement?.InnerText;
- }
+ ///
+ /// Parse ExcludeAssembliesWithoutSources flag
+ ///
+ /// Configuration element
+ /// ExcludeAssembliesWithoutSources flag
+ private static string ParseExcludeAssembliesWithoutSources(XmlElement configurationElement)
+ {
+ XmlElement instrumentModulesWithoutLocalSourcesElement = configurationElement[CoverletConstants.ExcludeAssembliesWithoutSources];
+ return instrumentModulesWithoutLocalSourcesElement?.InnerText;
+ }
- ///
- /// Parse include test assembly flag
- ///
- /// Configuration element
- /// Include Test Assembly Flag
- private static bool ParseIncludeTestAssembly(XmlElement configurationElement)
- {
- XmlElement includeTestAssemblyElement = configurationElement[CoverletConstants.IncludeTestAssemblyElementName];
- bool.TryParse(includeTestAssemblyElement?.InnerText, out bool includeTestAssembly);
- return includeTestAssembly;
- }
+ ///
+ /// Parse include test assembly flag
+ ///
+ /// Configuration element
+ /// Include Test Assembly Flag
+ private static bool ParseIncludeTestAssembly(XmlElement configurationElement)
+ {
+ XmlElement includeTestAssemblyElement = configurationElement[CoverletConstants.IncludeTestAssemblyElementName];
+ bool.TryParse(includeTestAssemblyElement?.InnerText, out bool includeTestAssembly);
+ return includeTestAssembly;
+ }
- ///
- /// Parse skipautoprops flag
- ///
- /// Configuration element
- /// Include Test Assembly Flag
- private static bool ParseSkipAutoProps(XmlElement configurationElement)
- {
- XmlElement skipAutoPropsElement = configurationElement[CoverletConstants.SkipAutoProps];
- bool.TryParse(skipAutoPropsElement?.InnerText, out bool skipAutoProps);
- return skipAutoProps;
- }
+ ///
+ /// Parse skipautoprops flag
+ ///
+ /// Configuration element
+ /// Include Test Assembly Flag
+ private static bool ParseSkipAutoProps(XmlElement configurationElement)
+ {
+ XmlElement skipAutoPropsElement = configurationElement[CoverletConstants.SkipAutoProps];
+ bool.TryParse(skipAutoPropsElement?.InnerText, out bool skipAutoProps);
+ return skipAutoProps;
+ }
- ///
- /// Parse attributes that mark methods that do not return.
- ///
- /// Configuration element
- /// DoesNotReturn attributes
- private static string[] ParseDoesNotReturnAttributes(XmlElement configurationElement)
- {
- XmlElement doesNotReturnAttributesElement = configurationElement[CoverletConstants.DoesNotReturnAttributesElementName];
- return SplitElement(doesNotReturnAttributesElement);
- }
+ ///
+ /// Parse attributes that mark methods that do not return.
+ ///
+ /// Configuration element
+ /// DoesNotReturn attributes
+ private static string[] ParseDoesNotReturnAttributes(XmlElement configurationElement)
+ {
+ XmlElement doesNotReturnAttributesElement = configurationElement[CoverletConstants.DoesNotReturnAttributesElementName];
+ return SplitElement(doesNotReturnAttributesElement);
+ }
- ///
- /// Splits a comma separated elements into an array
- ///
- /// The element to split
- /// An array of the values in the element
- private static string[] SplitElement(XmlElement element)
- {
- return element?.InnerText?.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Where(value => !string.IsNullOrWhiteSpace(value)).Select(value => value.Trim()).ToArray();
- }
+ ///
+ /// Splits a comma separated elements into an array
+ ///
+ /// The element to split
+ /// An array of the values in the element
+ private static string[] SplitElement(XmlElement element)
+ {
+ return element?.InnerText?.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Where(value => !string.IsNullOrWhiteSpace(value)).Select(value => value.Trim()).ToArray();
}
+ }
}
diff --git a/src/coverlet.collector/InProcDataCollection/CoverletInProcDataCollector.cs b/src/coverlet.collector/InProcDataCollection/CoverletInProcDataCollector.cs
index f682b2687..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;
@@ -14,117 +13,116 @@
namespace Coverlet.Collector.DataCollection
{
- public class CoverletInProcDataCollector : InProcDataCollection
+ public class CoverletInProcDataCollector : InProcDataCollection
+ {
+ private TestPlatformEqtTrace _eqtTrace;
+ private bool _enableExceptionLog;
+
+ private void AttachDebugger()
{
- private TestPlatformEqtTrace _eqtTrace;
- private bool _enableExceptionLog;
+ if (int.TryParse(Environment.GetEnvironmentVariable("COVERLET_DATACOLLECTOR_INPROC_DEBUG"), out int result) && result == 1)
+ {
+ Debugger.Launch();
+ Debugger.Break();
+ }
+ }
- private void AttachDebugger()
- {
- if (int.TryParse(Environment.GetEnvironmentVariable("COVERLET_DATACOLLECTOR_INPROC_DEBUG"), out int result) && result == 1)
- {
- Debugger.Launch();
- Debugger.Break();
- }
- }
+ private void EnableExceptionLog()
+ {
+ if (int.TryParse(Environment.GetEnvironmentVariable("COVERLET_DATACOLLECTOR_INPROC_EXCEPTIONLOG_ENABLED"), out int result) && result == 1)
+ {
+ _enableExceptionLog = true;
+ }
+ }
- private void EnableExceptionLog()
- {
- if (int.TryParse(Environment.GetEnvironmentVariable("COVERLET_DATACOLLECTOR_INPROC_EXCEPTIONLOG_ENABLED"), out int result) && result == 1)
- {
- _enableExceptionLog = true;
- }
- }
+ public void Initialize(IDataCollectionSink dataCollectionSink)
+ {
+ AttachDebugger();
+ EnableExceptionLog();
- public void Initialize(IDataCollectionSink dataCollectionSink)
- {
- AttachDebugger();
- EnableExceptionLog();
+ _eqtTrace = new TestPlatformEqtTrace();
+ _eqtTrace.Verbose("Initialize CoverletInProcDataCollector");
+ }
- _eqtTrace = new TestPlatformEqtTrace();
- _eqtTrace.Verbose("Initialize CoverletInProcDataCollector");
- }
+ public void TestCaseEnd(TestCaseEndArgs testCaseEndArgs)
+ {
+ }
- public void TestCaseEnd(TestCaseEndArgs testCaseEndArgs)
+ public void TestCaseStart(TestCaseStartArgs testCaseStartArgs)
+ {
+ }
+
+ public void TestSessionEnd(TestSessionEndArgs testSessionEndArgs)
+ {
+ foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
+ {
+ Type injectedInstrumentationClass = GetInstrumentationClass(assembly);
+ if (injectedInstrumentationClass is null)
{
+ continue;
}
- public void TestCaseStart(TestCaseStartArgs testCaseStartArgs)
+ try
{
+ _eqtTrace.Verbose($"Calling ModuleTrackerTemplate.UnloadModule for '{injectedInstrumentationClass.Assembly.FullName}'");
+ MethodInfo unloadModule = injectedInstrumentationClass.GetMethod(nameof(ModuleTrackerTemplate.UnloadModule), new[] { typeof(object), typeof(EventArgs) });
+ unloadModule.Invoke(null, new[] { (object)this, EventArgs.Empty });
+ injectedInstrumentationClass.GetField("FlushHitFile", BindingFlags.Static | BindingFlags.Public).SetValue(null, false);
+ _eqtTrace.Verbose($"Called ModuleTrackerTemplate.UnloadModule for '{injectedInstrumentationClass.Assembly.FullName}'");
}
-
- public void TestSessionEnd(TestSessionEndArgs testSessionEndArgs)
+ catch (Exception ex)
{
- foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
- {
- Type injectedInstrumentationClass = GetInstrumentationClass(assembly);
- if (injectedInstrumentationClass is null)
- {
- continue;
- }
-
- try
- {
- _eqtTrace.Verbose($"Calling ModuleTrackerTemplate.UnloadModule for '{injectedInstrumentationClass.Assembly.FullName}'");
- MethodInfo unloadModule = injectedInstrumentationClass.GetMethod(nameof(ModuleTrackerTemplate.UnloadModule), new[] { typeof(object), typeof(EventArgs) });
- unloadModule.Invoke(null, new[] { (object)this, EventArgs.Empty });
- injectedInstrumentationClass.GetField("FlushHitFile", BindingFlags.Static | BindingFlags.Public).SetValue(null, false);
- _eqtTrace.Verbose($"Called ModuleTrackerTemplate.UnloadModule for '{injectedInstrumentationClass.Assembly.FullName}'");
- }
- catch (Exception ex)
- {
- 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);
- }
- }
- }
+ if (_enableExceptionLog)
+ {
+ _eqtTrace.Error("{0}: Failed to unload module with error: {1}", CoverletConstants.InProcDataCollectorName, ex);
+ throw new CoverletDataCollectorException($"{CoverletConstants.InProcDataCollectorName}: Failed to unload module", ex);
+ }
}
+ }
+ }
- public void TestSessionStart(TestSessionStartArgs testSessionStartArgs)
+ public void TestSessionStart(TestSessionStartArgs testSessionStartArgs)
+ {
+ }
+
+ private Type GetInstrumentationClass(Assembly assembly)
+ {
+ try
+ {
+ foreach (Type type in assembly.GetTypes())
{
+ if (type.Namespace == "Coverlet.Core.Instrumentation.Tracker"
+ && type.Name.StartsWith(assembly.GetName().Name + "_"))
+ {
+ return type;
+ }
}
- private Type GetInstrumentationClass(Assembly assembly)
+ return null;
+ }
+ catch (Exception ex)
+ {
+ if (_enableExceptionLog)
{
- try
+ var exceptionString = new StringBuilder();
+ exceptionString.AppendFormat("{0}: Failed to get Instrumentation class for assembly '{1}' with error: {2}",
+ CoverletConstants.InProcDataCollectorName, assembly, ex);
+ exceptionString.AppendLine();
+
+ if (ex is ReflectionTypeLoadException rtle)
+ {
+ exceptionString.AppendLine("ReflectionTypeLoadException list:");
+ foreach (Exception loaderEx in rtle.LoaderExceptions)
{
- foreach (Type type in assembly.GetTypes())
- {
- if (type.Namespace == "Coverlet.Core.Instrumentation.Tracker"
- && type.Name.StartsWith(assembly.GetName().Name + "_"))
- {
- return type;
- }
- }
-
- return null;
- }
- catch (Exception ex)
- {
- if (_enableExceptionLog)
- {
- var exceptionString = new StringBuilder();
- exceptionString.AppendFormat("{0}: Failed to get Instrumentation class for assembly '{1}' with error: {2}",
- CoverletConstants.InProcDataCollectorName, assembly, ex);
- exceptionString.AppendLine();
-
- if (ex is ReflectionTypeLoadException rtle)
- {
- exceptionString.AppendLine("ReflectionTypeLoadException list:");
- foreach (Exception loaderEx in rtle.LoaderExceptions)
- {
- exceptionString.AppendLine(loaderEx.ToString());
- }
- }
-
- _eqtTrace.Warning(exceptionString.ToString());
- }
-
- return null;
+ exceptionString.AppendLine(loaderEx.ToString());
}
+ }
+
+ _eqtTrace.Warning(exceptionString.ToString());
}
+
+ return null;
+ }
}
+ }
}
diff --git a/src/coverlet.collector/Properties/AssemblyInfo.cs b/src/coverlet.collector/Properties/AssemblyInfo.cs
index 4d4a63712..9d5c4a633 100644
--- a/src/coverlet.collector/Properties/AssemblyInfo.cs
+++ b/src/coverlet.collector/Properties/AssemblyInfo.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Toni Solarin-Sodara
+// Copyright (c) Toni Solarin-Sodara
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Reflection;
@@ -8,4 +8,4 @@
[assembly: InternalsVisibleTo("coverlet.core.tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100757cf9291d78a82e5bb58a827a3c46c2f959318327ad30d1b52e918321ffbd847fb21565b8576d2a3a24562a93e86c77a298b564a0f1b98f63d7a1441a3a8bcc206da3ed09d5dacc76e122a109a9d3ac608e21a054d667a2bae98510a1f0f653c0e6f58f42b4b3934f6012f5ec4a09b3dfd3e14d437ede1424bdb722aead64ad")]
[assembly: InternalsVisibleTo("coverlet.collector.tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100ed0ed6af9693182615b8dcadc83c918b8d36312f86cefc69539d67d4189cd1b89420e7c3871802ffef7f5ca7816c68ad856c77bf7c230cc07824d96aa5d1237eebd30e246b9a14e22695fb26b40c800f74ea96619092cbd3a5d430d6c003fc7a82e8ccd1e315b935105d9232fe9e99e8d7ff54bba6f191959338d4a3169df9b3")]
// Needed to mock internal type https://github.com/Moq/moq4/wiki/Quickstart#advanced-features
-[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
\ No newline at end of file
+[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
diff --git a/src/coverlet.collector/Resources/Resources.Designer.cs b/src/coverlet.collector/Resources/Resources.Designer.cs
deleted file mode 100644
index a1af3afe4..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", "15.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/Utilities/CountDownEvent.cs b/src/coverlet.collector/Utilities/CountDownEvent.cs
index 265c54103..9a82a76b3 100644
--- a/src/coverlet.collector/Utilities/CountDownEvent.cs
+++ b/src/coverlet.collector/Utilities/CountDownEvent.cs
@@ -7,36 +7,36 @@
namespace Coverlet.Collector.Utilities
{
- internal class CollectorCountdownEventFactory : ICountDownEventFactory
+ internal class CollectorCountdownEventFactory : ICountDownEventFactory
+ {
+ public ICountDownEvent Create(int count, TimeSpan waitTimeout)
{
- public ICountDownEvent Create(int count, TimeSpan waitTimeout)
- {
- return new CollectorCountdownEvent(count, waitTimeout);
- }
+ return new CollectorCountdownEvent(count, waitTimeout);
}
+ }
- internal class CollectorCountdownEvent : ICountDownEvent
- {
- private readonly CountdownEvent _countDownEvent;
- private readonly TimeSpan _waitTimeout;
+ internal class CollectorCountdownEvent : ICountDownEvent
+ {
+ private readonly CountdownEvent _countDownEvent;
+ private readonly TimeSpan _waitTimeout;
- public CollectorCountdownEvent(int count, TimeSpan waitTimeout)
- {
- _countDownEvent = new CountdownEvent(count);
- _waitTimeout = waitTimeout;
- }
+ public CollectorCountdownEvent(int count, TimeSpan waitTimeout)
+ {
+ _countDownEvent = new CountdownEvent(count);
+ _waitTimeout = waitTimeout;
+ }
- public void Signal()
- {
- _countDownEvent.Signal();
- }
+ public void Signal()
+ {
+ _countDownEvent.Signal();
+ }
- public void Wait()
- {
- if (!_countDownEvent.Wait(_waitTimeout))
- {
- throw new TimeoutException($"CollectorCountdownEvent timeout after {_waitTimeout}");
- }
- }
+ public void Wait()
+ {
+ if (!_countDownEvent.Wait(_waitTimeout))
+ {
+ throw new TimeoutException($"CollectorCountdownEvent timeout after {_waitTimeout}");
+ }
}
+ }
}
diff --git a/src/coverlet.collector/Utilities/CoverletConstants.cs b/src/coverlet.collector/Utilities/CoverletConstants.cs
index 3a7bfa998..5ce4a79ef 100644
--- a/src/coverlet.collector/Utilities/CoverletConstants.cs
+++ b/src/coverlet.collector/Utilities/CoverletConstants.cs
@@ -3,29 +3,29 @@
namespace Coverlet.Collector.Utilities
{
- internal static class CoverletConstants
- {
- public const string FriendlyName = "XPlat code coverage";
- public const string DefaultUri = @"datacollector://Microsoft/CoverletCodeCoverage/1.0";
- public const string DataCollectorName = "CoverletCoverageDataCollector";
- public const string DefaultReportFormat = "cobertura";
- public const string DefaultFileName = "coverage";
- public const string IncludeFiltersElementName = "Include";
- public const string IncludeDirectoriesElementName = "IncludeDirectory";
- public const string ExcludeFiltersElementName = "Exclude";
- public const string ExcludeSourceFilesElementName = "ExcludeByFile";
- public const string ExcludeAttributesElementName = "ExcludeByAttribute";
- public const string MergeWithElementName = "MergeWith";
- public const string UseSourceLinkElementName = "UseSourceLink";
- public const string SingleHitElementName = "SingleHit";
- public const string IncludeTestAssemblyElementName = "IncludeTestAssembly";
- public const string TestSourcesPropertyName = "TestSources";
- public const string ReportFormatElementName = "Format";
- public const string DefaultExcludeFilter = "[coverlet.*]*";
- public const string InProcDataCollectorName = "CoverletInProcDataCollector";
- public const string SkipAutoProps = "SkipAutoProps";
- public const string DoesNotReturnAttributesElementName = "DoesNotReturnAttribute";
- public const string DeterministicReport = "DeterministicReport";
- public const string ExcludeAssembliesWithoutSources = "ExcludeAssembliesWithoutSources";
- }
+ internal static class CoverletConstants
+ {
+ public const string FriendlyName = "XPlat code coverage";
+ public const string DefaultUri = @"datacollector://Microsoft/CoverletCodeCoverage/1.0";
+ public const string DataCollectorName = "CoverletCoverageDataCollector";
+ public const string DefaultReportFormat = "cobertura";
+ public const string DefaultFileName = "coverage";
+ public const string IncludeFiltersElementName = "Include";
+ public const string IncludeDirectoriesElementName = "IncludeDirectory";
+ public const string ExcludeFiltersElementName = "Exclude";
+ public const string ExcludeSourceFilesElementName = "ExcludeByFile";
+ public const string ExcludeAttributesElementName = "ExcludeByAttribute";
+ public const string MergeWithElementName = "MergeWith";
+ public const string UseSourceLinkElementName = "UseSourceLink";
+ public const string SingleHitElementName = "SingleHit";
+ public const string IncludeTestAssemblyElementName = "IncludeTestAssembly";
+ public const string TestSourcesPropertyName = "TestSources";
+ public const string ReportFormatElementName = "Format";
+ public const string DefaultExcludeFilter = "[coverlet.*]*";
+ public const string InProcDataCollectorName = "CoverletInProcDataCollector";
+ public const string SkipAutoProps = "SkipAutoProps";
+ public const string DoesNotReturnAttributesElementName = "DoesNotReturnAttribute";
+ public const string DeterministicReport = "DeterministicReport";
+ public const string ExcludeAssembliesWithoutSources = "ExcludeAssembliesWithoutSources";
+ }
}
diff --git a/src/coverlet.collector/Utilities/CoverletDataCollectorException.cs b/src/coverlet.collector/Utilities/CoverletDataCollectorException.cs
index 189b363f8..2b015d24e 100644
--- a/src/coverlet.collector/Utilities/CoverletDataCollectorException.cs
+++ b/src/coverlet.collector/Utilities/CoverletDataCollectorException.cs
@@ -5,14 +5,14 @@
namespace Coverlet.Collector.Utilities
{
- internal class CoverletDataCollectorException : Exception
+ internal class CoverletDataCollectorException : Exception
+ {
+ public CoverletDataCollectorException(string message) : base(message)
{
- public CoverletDataCollectorException(string message) : base(message)
- {
- }
+ }
- public CoverletDataCollectorException(string message, Exception innerException) : base(message, innerException)
- {
- }
+ public CoverletDataCollectorException(string message, Exception innerException) : base(message, innerException)
+ {
}
+ }
}
diff --git a/src/coverlet.collector/Utilities/DirectoryHelper.cs b/src/coverlet.collector/Utilities/DirectoryHelper.cs
index c9800a21b..35ef0ccf5 100644
--- a/src/coverlet.collector/Utilities/DirectoryHelper.cs
+++ b/src/coverlet.collector/Utilities/DirectoryHelper.cs
@@ -6,25 +6,25 @@
namespace Coverlet.Collector.Utilities
{
+ ///
+ internal class DirectoryHelper : IDirectoryHelper
+ {
///
- internal class DirectoryHelper : IDirectoryHelper
+ public bool Exists(string path)
{
- ///
- public bool Exists(string path)
- {
- return Directory.Exists(path);
- }
+ return Directory.Exists(path);
+ }
- ///
- public void CreateDirectory(string path)
- {
- Directory.CreateDirectory(path);
- }
+ ///
+ public void CreateDirectory(string path)
+ {
+ Directory.CreateDirectory(path);
+ }
- ///
- public void Delete(string path, bool recursive)
- {
- Directory.Delete(path, recursive);
- }
+ ///
+ public void Delete(string path, bool recursive)
+ {
+ Directory.Delete(path, recursive);
}
+ }
}
diff --git a/src/coverlet.collector/Utilities/FileHelper.cs b/src/coverlet.collector/Utilities/FileHelper.cs
index f8a85b13e..7f6ecaaf4 100644
--- a/src/coverlet.collector/Utilities/FileHelper.cs
+++ b/src/coverlet.collector/Utilities/FileHelper.cs
@@ -6,19 +6,19 @@
namespace Coverlet.Collector.Utilities
{
+ ///
+ internal class FileHelper : IFileHelper
+ {
///
- internal class FileHelper : IFileHelper
+ public bool Exists(string path)
{
- ///
- public bool Exists(string path)
- {
- return File.Exists(path);
- }
+ return File.Exists(path);
+ }
- ///
- public void WriteAllText(string path, string contents)
- {
- File.WriteAllText(path, contents);
- }
+ ///
+ public void WriteAllText(string path, string contents)
+ {
+ File.WriteAllText(path, contents);
}
+ }
}
diff --git a/src/coverlet.collector/Utilities/Interfaces/ICountDown.cs b/src/coverlet.collector/Utilities/Interfaces/ICountDown.cs
index 69eddbce4..a9b7b3354 100644
--- a/src/coverlet.collector/Utilities/Interfaces/ICountDown.cs
+++ b/src/coverlet.collector/Utilities/Interfaces/ICountDown.cs
@@ -6,33 +6,33 @@
namespace Coverlet.Collector.Utilities.Interfaces
{
+ ///
+ /// Factory for ICountDownEvent
+ ///
+ internal interface ICountDownEventFactory
+ {
///
- /// Factory for ICountDownEvent
+ /// Create ICountDownEvent instance
///
- internal interface ICountDownEventFactory
- {
- ///
- /// Create ICountDownEvent instance
- ///
- /// count of CountDownEvent
- /// max wait
- ///
- ICountDownEvent Create(int count, TimeSpan waitTimeout);
- }
+ /// count of CountDownEvent
+ /// max wait
+ ///
+ ICountDownEvent Create(int count, TimeSpan waitTimeout);
+ }
+ ///
+ /// Wrapper interface for CountDownEvent
+ ///
+ internal interface ICountDownEvent
+ {
///
- /// Wrapper interface for CountDownEvent
+ /// Signal event
///
- internal interface ICountDownEvent
- {
- ///
- /// Signal event
- ///
- void Signal();
+ void Signal();
- ///
- /// Wait for event
- ///
- void Wait();
- }
+ ///
+ /// Wait for event
+ ///
+ void Wait();
+ }
}
diff --git a/src/coverlet.collector/Utilities/Interfaces/ICoverageWrapper.cs b/src/coverlet.collector/Utilities/Interfaces/ICoverageWrapper.cs
index 1a34612c0..48410be09 100644
--- a/src/coverlet.collector/Utilities/Interfaces/ICoverageWrapper.cs
+++ b/src/coverlet.collector/Utilities/Interfaces/ICoverageWrapper.cs
@@ -7,37 +7,37 @@
namespace Coverlet.Collector.Utilities.Interfaces
{
+ ///
+ /// Wrapper interface for Coverage class in coverlet.core
+ /// Since the class is not testable, this interface is used to abstract methods for mocking in unit tests.
+ ///
+ internal interface ICoverageWrapper
+ {
///
- /// Wrapper interface for Coverage class in coverlet.core
- /// Since the class is not testable, this interface is used to abstract methods for mocking in unit tests.
+ /// Creates a coverage object from given coverlet settings
///
- internal interface ICoverageWrapper
- {
- ///
- /// Creates a coverage object from given coverlet settings
- ///
- /// Coverlet settings
- /// Coverlet logger
- /// Coverlet instrumentationHelper
- /// Coverlet fileSystem
- /// Coverlet sourceRootTranslator
- ///
- /// Coverage object
- Coverage CreateCoverage(CoverletSettings settings, ILogger logger, IInstrumentationHelper instrumentationHelper, IFileSystem fileSystem, ISourceRootTranslator sourceRootTranslator, ICecilSymbolHelper cecilSymbolHelper);
+ /// Coverlet settings
+ /// Coverlet logger
+ /// Coverlet instrumentationHelper
+ /// Coverlet fileSystem
+ /// Coverlet sourceRootTranslator
+ ///
+ /// Coverage object
+ Coverage CreateCoverage(CoverletSettings settings, ILogger logger, IInstrumentationHelper instrumentationHelper, IFileSystem fileSystem, ISourceRootTranslator sourceRootTranslator, ICecilSymbolHelper cecilSymbolHelper);
- ///
- /// Gets the coverage result from provided coverage object
- ///
- /// Coverage
- /// The coverage result
- CoverageResult GetCoverageResult(Coverage coverage);
+ ///
+ /// Gets the coverage result from provided coverage object
+ ///
+ /// Coverage
+ /// The coverage result
+ CoverageResult GetCoverageResult(Coverage coverage);
- ///
- /// Prepares modules for getting coverage.
- /// Wrapper over coverage.PrepareModules
- ///
- ///
- void PrepareModules(Coverage coverage);
+ ///
+ /// Prepares modules for getting coverage.
+ /// Wrapper over coverage.PrepareModules
+ ///
+ ///
+ void PrepareModules(Coverage coverage);
- }
+ }
}
diff --git a/src/coverlet.collector/Utilities/Interfaces/IDirectoryHelper.cs b/src/coverlet.collector/Utilities/Interfaces/IDirectoryHelper.cs
index f148f21ea..07ee98c21 100644
--- a/src/coverlet.collector/Utilities/Interfaces/IDirectoryHelper.cs
+++ b/src/coverlet.collector/Utilities/Interfaces/IDirectoryHelper.cs
@@ -3,29 +3,29 @@
namespace Coverlet.Collector.Utilities.Interfaces
{
- interface IDirectoryHelper
- {
- ///
- /// Determines whether the specified directory exists.
- ///
- /// The directory to check.
- /// true if the caller has the required permissions and path contains the name of an existing directory; otherwise, false.
- /// This method also returns false if path is null, an invalid path, or a zero-length string.
- /// If the caller does not have sufficient permissions to read the specified file,
- /// no exception is thrown and the method returns false regardless of the existence of path.
- bool Exists(string path);
+ interface IDirectoryHelper
+ {
+ ///
+ /// Determines whether the specified directory exists.
+ ///
+ /// The directory to check.
+ /// true if the caller has the required permissions and path contains the name of an existing directory; otherwise, false.
+ /// This method also returns false if path is null, an invalid path, or a zero-length string.
+ /// If the caller does not have sufficient permissions to read the specified file,
+ /// no exception is thrown and the method returns false regardless of the existence of path.
+ bool Exists(string path);
- ///
- /// Creates all directories and subdirectories in the specified path unless they already exist.
- ///
- /// The directory to create.
- void CreateDirectory(string directory);
+ ///
+ /// Creates all directories and subdirectories in the specified path unless they already exist.
+ ///
+ /// The directory to create.
+ void CreateDirectory(string directory);
- ///
- /// Deletes the specified directory and, if indicated, any subdirectories and files in the directory.
- ///
- /// The name of the directory to remove.
- /// true to remove directories, subdirectories, and files in path; otherwise, false.
- void Delete(string path, bool recursive);
- }
+ ///
+ /// Deletes the specified directory and, if indicated, any subdirectories and files in the directory.
+ ///
+ /// The name of the directory to remove.
+ /// true to remove directories, subdirectories, and files in path; otherwise, false.
+ void Delete(string path, bool recursive);
+ }
}
diff --git a/src/coverlet.collector/Utilities/Interfaces/IFileHelper.cs b/src/coverlet.collector/Utilities/Interfaces/IFileHelper.cs
index 1ddcc8678..c9c58bc21 100644
--- a/src/coverlet.collector/Utilities/Interfaces/IFileHelper.cs
+++ b/src/coverlet.collector/Utilities/Interfaces/IFileHelper.cs
@@ -3,24 +3,24 @@
namespace Coverlet.Collector.Utilities.Interfaces
{
- internal interface IFileHelper
- {
- ///
- /// Determines whether the specified file exists.
- ///
- /// The file to check.
- /// true if the caller has the required permissions and path contains the name of an existing file; otherwise, false.
- /// This method also returns false if path is null, an invalid path, or a zero-length string.
- /// If the caller does not have sufficient permissions to read the specified file,
- /// no exception is thrown and the method returns false regardless of the existence of path.
- bool Exists(string path);
+ internal interface IFileHelper
+ {
+ ///
+ /// Determines whether the specified file exists.
+ ///
+ /// The file to check.
+ /// true if the caller has the required permissions and path contains the name of an existing file; otherwise, false.
+ /// This method also returns false if path is null, an invalid path, or a zero-length string.
+ /// If the caller does not have sufficient permissions to read the specified file,
+ /// no exception is thrown and the method returns false regardless of the existence of path.
+ bool Exists(string path);
- ///
- /// Creates a new file, writes the specified string to the file, and then closes the file.
- /// If the target file already exists, it is overwritten.
- ///
- /// The file to write to.
- /// The string to write to the file.
- void WriteAllText(string path, string contents);
- }
+ ///
+ /// Creates a new file, writes the specified string to the file, and then closes the file.
+ /// If the target file already exists, it is overwritten.
+ ///
+ /// The file to write to.
+ /// The string to write to the file.
+ void WriteAllText(string path, string contents);
+ }
}
diff --git a/src/coverlet.collector/Utilities/TestPlatformEqtTrace.cs b/src/coverlet.collector/Utilities/TestPlatformEqtTrace.cs
index 7c02f6d03..a5cbdf16a 100644
--- a/src/coverlet.collector/Utilities/TestPlatformEqtTrace.cs
+++ b/src/coverlet.collector/Utilities/TestPlatformEqtTrace.cs
@@ -5,52 +5,52 @@
namespace Coverlet.Collector.Utilities
{
+ ///
+ /// Test platform eqttrace
+ ///
+ internal class TestPlatformEqtTrace
+ {
+ public bool IsInfoEnabled => EqtTrace.IsInfoEnabled;
+ public bool IsVerboseEnabled => EqtTrace.IsVerboseEnabled;
+
///
- /// Test platform eqttrace
+ /// Verbose logger
///
- internal class TestPlatformEqtTrace
+ /// Format
+ /// Args
+ public void Verbose(string format, params object[] args)
{
- public bool IsInfoEnabled => EqtTrace.IsInfoEnabled;
- public bool IsVerboseEnabled => EqtTrace.IsVerboseEnabled;
-
- ///
- /// Verbose logger
- ///
- /// Format
- /// Args
- public void Verbose(string format, params object[] args)
- {
- EqtTrace.Verbose($"[coverlet]{format}", args);
- }
+ EqtTrace.Verbose($"[coverlet]{format}", args);
+ }
- ///
- /// Warning logger
- ///
- /// Format
- /// Args
- public void Warning(string format, params object[] args)
- {
- EqtTrace.Warning($"[coverlet]{format}", args);
- }
+ ///
+ /// Warning logger
+ ///
+ /// Format
+ /// Args
+ public void Warning(string format, params object[] args)
+ {
+ EqtTrace.Warning($"[coverlet]{format}", args);
+ }
- ///
- /// Info logger
- ///
- /// Format
- /// Args
- public void Info(string format, params object[] args)
- {
- EqtTrace.Info($"[coverlet]{format}", args);
- }
+ ///
+ /// Info logger
+ ///
+ /// Format
+ /// Args
+ public void Info(string format, params object[] args)
+ {
+ EqtTrace.Info($"[coverlet]{format}", args);
+ }
- ///
- /// Error logger
- ///
- /// Format
- /// Args
- public void Error(string format, params object[] args)
- {
- EqtTrace.Error($"[coverlet]{format}", args);
- }
+ ///
+ /// Error logger
+ ///
+ /// Format
+ /// Args
+ public void Error(string format, params object[] args)
+ {
+ EqtTrace.Error($"[coverlet]{format}", args);
}
+ }
}
diff --git a/src/coverlet.collector/Utilities/TestPlatformLogger.cs b/src/coverlet.collector/Utilities/TestPlatformLogger.cs
index 105e4c9d9..dd3560220 100644
--- a/src/coverlet.collector/Utilities/TestPlatformLogger.cs
+++ b/src/coverlet.collector/Utilities/TestPlatformLogger.cs
@@ -5,27 +5,27 @@
namespace Coverlet.Collector.Utilities
{
+ ///
+ /// Test platform logger
+ ///
+ internal class TestPlatformLogger
+ {
+ private readonly DataCollectionLogger _logger;
+ private readonly DataCollectionContext _dataCollectionContext;
+
+ public TestPlatformLogger(DataCollectionLogger logger, DataCollectionContext dataCollectionContext)
+ {
+ _logger = logger;
+ _dataCollectionContext = dataCollectionContext;
+ }
+
///
- /// Test platform logger
+ /// Log warning
///
- internal class TestPlatformLogger
+ /// Warning message
+ public void LogWarning(string warning)
{
- private readonly DataCollectionLogger _logger;
- private readonly DataCollectionContext _dataCollectionContext;
-
- public TestPlatformLogger(DataCollectionLogger logger, DataCollectionContext dataCollectionContext)
- {
- _logger = logger;
- _dataCollectionContext = dataCollectionContext;
- }
-
- ///
- /// Log warning
- ///
- /// Warning message
- public void LogWarning(string warning)
- {
- _logger.LogWarning(_dataCollectionContext, $"[coverlet]{warning}");
- }
+ _logger.LogWarning(_dataCollectionContext, $"[coverlet]{warning}");
}
+ }
}
diff --git a/src/coverlet.collector/build/netstandard1.0/coverlet.collector.targets b/src/coverlet.collector/build/coverlet.collector.targets
similarity index 97%
rename from src/coverlet.collector/build/netstandard1.0/coverlet.collector.targets
rename to src/coverlet.collector/build/coverlet.collector.targets
index 7bd7b28e7..12cf946c8 100644
--- a/src/coverlet.collector/build/netstandard1.0/coverlet.collector.targets
+++ b/src/coverlet.collector/build/coverlet.collector.targets
@@ -9,14 +9,15 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and
-->
+
-
+
-
+
$(VSTestTestAdapterPath);$(MSBuildThisFileDirectory)
@@ -26,7 +27,7 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and
<_CoverletSdkNETCoreSdkVersion>$(NETCoreSdkVersion)
<_CoverletSdkNETCoreSdkVersion Condition="$(_CoverletSdkNETCoreSdkVersion.Contains('-'))">$(_CoverletSdkNETCoreSdkVersion.Split('-')[0])
- <_CoverletSdkMinVersionWithDependencyTarget>3.1.300
+ <_CoverletSdkMinVersionWithDependencyTarget>6.0.100
<_CoverletSourceRootTargetName>CoverletGetPathMap
<_CoverletSourceRootTargetName Condition="'$([System.Version]::Parse($(_CoverletSdkNETCoreSdkVersion)).CompareTo($([System.Version]::Parse($(_CoverletSdkMinVersionWithDependencyTarget)))))' >= '0' ">InitializeSourceRootMappedPaths
diff --git a/src/coverlet.collector/coverlet.collector.csproj b/src/coverlet.collector/coverlet.collector.csproj
index ba206705c..ddb8522ba 100644
--- a/src/coverlet.collector/coverlet.collector.csproj
+++ b/src/coverlet.collector/coverlet.collector.csproj
@@ -1,20 +1,23 @@
-
+
- netstandard2.0
+ netstandard2.0
coverlet.collector
true
true
false
-
true
$(TargetsForTfmSpecificContentInPackage);PackBuildOutputs
- $(NoWarn);NU5127
+ $(NoWarn);NU5127;NU5100
+ true
+
+ false
@@ -29,59 +32,41 @@
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
+ VSTestIntegration.md
+ https://github.com/coverlet-coverage/coverlet/blob/master/Documentation/Changelog.md
git
+
-
+
+ true
+
-
- True
- True
- Resources.resx
-
-
- True
- True
- Resources.resx
-
-
- True
- True
- Resources.resx
-
-
-
-
- ResXFileCodeGenerator
- Resources.Designer.cs
-
-
- ResXFileCodeGenerator
- Resource.Designer.cs
-
-
- ResXFileCodeGenerator
- Resources.Designer.cs
-
+
-
+
+ Always
+
-
-
-
+
+
+
+
+
+
diff --git a/src/coverlet.console/ExitCodes.cs b/src/coverlet.console/ExitCodes.cs
index 670eda89a..93a7d395f 100644
--- a/src/coverlet.console/ExitCodes.cs
+++ b/src/coverlet.console/ExitCodes.cs
@@ -9,29 +9,29 @@
[Flags]
internal enum CommandExitCodes
{
- ///
- /// Indicates successful run of dotnet test without any test failure and coverage percentage above threshold if provided.
- ///
- Success = 0,
+ ///
+ /// Indicates successful run of dotnet test without any test failure and coverage percentage above threshold if provided.
+ ///
+ Success = 0,
- ///
- /// Indicates test failure by dotnet test.
- ///
- TestFailed = 1,
+ ///
+ /// Indicates test failure by dotnet test.
+ ///
+ TestFailed = 1,
- ///
- /// Indicates coverage percentage is below given threshold for one or more threshold type.
- ///
- CoverageBelowThreshold = 2,
+ ///
+ /// Indicates coverage percentage is below given threshold for one or more threshold type.
+ ///
+ CoverageBelowThreshold = 2,
- ///
- /// Indicates exception occurred during Coverlet process.
- ///
- Exception = 101,
+ ///
+ /// Indicates exception occurred during Coverlet process.
+ ///
+ Exception = 101,
- ///
- /// Indicates missing options or empty arguments for Coverlet process.
- ///
- CommandParsingException = 102
+ ///
+ /// Indicates missing options or empty arguments for Coverlet process.
+ ///
+ CommandParsingException = 102
}
diff --git a/src/coverlet.console/Logging/ConsoleLogger.cs b/src/coverlet.console/Logging/ConsoleLogger.cs
index 1e41af504..30aca3a20 100644
--- a/src/coverlet.console/Logging/ConsoleLogger.cs
+++ b/src/coverlet.console/Logging/ConsoleLogger.cs
@@ -7,40 +7,40 @@
namespace Coverlet.Console.Logging
{
- class ConsoleLogger : ILogger
- {
- private static readonly object s_sync = new();
-
- public LogLevel Level { get; set; } = LogLevel.Normal;
+ class ConsoleLogger : ILogger
+ {
+ private static readonly object s_sync = new();
+
+ public LogLevel Level { get; set; } = LogLevel.Normal;
+
+ public void LogError(string message) => Log(LogLevel.Quiet, message, ConsoleColor.Red);
- public void LogError(string message) => Log(LogLevel.Quiet, message, ConsoleColor.Red);
+ public void LogError(Exception exception) => LogError(exception.ToString());
- public void LogError(Exception exception) => LogError(exception.ToString());
+ public void LogInformation(string message, bool important = false) => Log(important ? LogLevel.Minimal : LogLevel.Normal, message, ForegroundColor);
- public void LogInformation(string message, bool important = false) => Log(important ? LogLevel.Minimal : LogLevel.Normal, message, ForegroundColor);
+ public void LogVerbose(string message) => Log(LogLevel.Detailed, message, ForegroundColor);
- public void LogVerbose(string message) => Log(LogLevel.Detailed, message, ForegroundColor);
+ public void LogWarning(string message) => Log(LogLevel.Quiet, message, ConsoleColor.Yellow);
- public void LogWarning(string message) => Log(LogLevel.Quiet, message, ConsoleColor.Yellow);
+ private void Log(LogLevel level, string message, ConsoleColor color)
+ {
+ if (level < Level) return;
- private void Log(LogLevel level, string message, ConsoleColor color)
+ lock (s_sync)
+ {
+ ConsoleColor currentForegroundColor;
+ if (color != (currentForegroundColor = ForegroundColor))
+ {
+ ForegroundColor = color;
+ WriteLine(message);
+ ForegroundColor = currentForegroundColor;
+ }
+ else
{
- if (level < Level) return;
-
- lock (s_sync)
- {
- ConsoleColor currentForegroundColor;
- if (color != (currentForegroundColor = ForegroundColor))
- {
- ForegroundColor = color;
- WriteLine(message);
- ForegroundColor = currentForegroundColor;
- }
- else
- {
- WriteLine(message);
- }
- }
+ WriteLine(message);
}
+ }
}
+ }
}
diff --git a/src/coverlet.console/Logging/LogLevel.cs b/src/coverlet.console/Logging/LogLevel.cs
index 2e0cf7320..4035d07bd 100644
--- a/src/coverlet.console/Logging/LogLevel.cs
+++ b/src/coverlet.console/Logging/LogLevel.cs
@@ -1,33 +1,33 @@
-// Copyright (c) Toni Solarin-Sodara
+// Copyright (c) Toni Solarin-Sodara
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Coverlet.Console.Logging
{
+ ///
+ /// Defines logging severity levels.
+ ///
+ enum LogLevel
+ {
///
- /// Defines logging severity levels.
+ /// Logs that track the general flow of the application. These logs should have long-term value.
///
- enum LogLevel
- {
- ///
- /// Logs that track the general flow of the application. These logs should have long-term value.
- ///
- Detailed = 0,
+ Detailed = 0,
- ///
- /// Logs that highlight an abnormal or unexpected event in the application flow, but do not otherwise cause the
- /// application execution to stop.
- ///
- Normal = 1,
+ ///
+ /// Logs that highlight an abnormal or unexpected event in the application flow, but do not otherwise cause the
+ /// application execution to stop.
+ ///
+ Normal = 1,
- ///
- /// Logs that highlight when the current flow of execution is stopped due to a failure. These should indicate a
- /// failure in the current activity, not an application-wide failure.
- ///
- Minimal = 2,
+ ///
+ /// Logs that highlight when the current flow of execution is stopped due to a failure. These should indicate a
+ /// failure in the current activity, not an application-wide failure.
+ ///
+ Minimal = 2,
- ///
- /// Not used for writing log messages. Specifies that a logging category should not write any messages except warning and errors.
- ///
- Quiet = 3
- }
+ ///
+ /// Not used for writing log messages. Specifies that a logging category should not write any messages except warning and errors.
+ ///
+ Quiet = 3
+ }
}
diff --git a/src/coverlet.console/Program.cs b/src/coverlet.console/Program.cs
index d967f90d6..2c6035ee5 100644
--- a/src/coverlet.console/Program.cs
+++ b/src/coverlet.console/Program.cs
@@ -3,12 +3,14 @@
using System;
using System.Collections.Generic;
+using System.CommandLine;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
+using System.Threading.Tasks;
using ConsoleTables;
using Coverlet.Console.Logging;
using Coverlet.Core;
@@ -17,309 +19,387 @@
using Coverlet.Core.Helpers;
using Coverlet.Core.Reporters;
using Coverlet.Core.Symbols;
-using McMaster.Extensions.CommandLineUtils;
using Microsoft.Extensions.DependencyInjection;
namespace Coverlet.Console
{
- class Program
+ public static class Program
+ {
+ static int Main(string[] args)
{
- static int Main(string[] args)
+ var moduleOrAppDirectory = new Argument("path", "Path to the test assembly or application directory.");
+ var target = new Option(new[] { "--target", "-t" }, "Path to the test runner application.") { Arity = ArgumentArity.ZeroOrOne, IsRequired = true };
+ var targs = new Option(new[] { "--targetargs", "-a" }, "Arguments to be passed to the test runner.") { Arity = ArgumentArity.ZeroOrOne };
+ var output = new Option(new[] { "--output", "-o" }, "Output of the generated coverage report") { Arity = ArgumentArity.ZeroOrOne };
+ var verbosity = new Option(new[] { "--verbosity", "-v" }, () => LogLevel.Normal, "Sets the verbosity level of the command. Allowed values are quiet, minimal, normal, detailed.") { Arity = ArgumentArity.ZeroOrOne };
+ var formats = new Option(new[] { "--format", "-f" }, () => new[] { "json" }, "Format of the generated coverage report.") { Arity = ArgumentArity.ZeroOrMore, AllowMultipleArgumentsPerToken = true };
+ var threshold = new Option("--threshold", "Exits with error if the coverage % is below value.") { Arity = ArgumentArity.ZeroOrOne };
+ Option> thresholdTypes = new Option>("--threshold-type", () => new List(new string[] { "line", "branch", "method" }), "Coverage type to apply the threshold to.").FromAmong("line", "branch", "method");
+ var thresholdStat = new Option("--threshold-stat", () => ThresholdStatistic.Minimum, "Coverage statistic used to enforce the threshold value.") { Arity = ArgumentArity.ZeroOrOne };
+ var excludeFilters = new Option("--exclude", "Filter expressions to exclude specific modules and types.") { Arity = ArgumentArity.ZeroOrMore, AllowMultipleArgumentsPerToken = true };
+ var includeFilters = new Option("--include", "Filter expressions to include only specific modules and types.") { Arity = ArgumentArity.ZeroOrMore, AllowMultipleArgumentsPerToken = true };
+ var excludedSourceFiles = new Option("--exclude-by-file", "Glob patterns specifying source files to exclude.") { Arity = ArgumentArity.ZeroOrMore, AllowMultipleArgumentsPerToken = true };
+ var includeDirectories = new Option("--include-directory", "Include directories containing additional assemblies to be instrumented.") { Arity = ArgumentArity.ZeroOrMore, AllowMultipleArgumentsPerToken = true };
+ var excludeAttributes = new Option("--exclude-by-attribute", "Attributes to exclude from code coverage.") { Arity = ArgumentArity.ZeroOrMore, AllowMultipleArgumentsPerToken = true };
+ var includeTestAssembly = new Option("--include-test-assembly", "Specifies whether to report code coverage of the test assembly.") { Arity = ArgumentArity.Zero };
+ var singleHit = new Option("--single-hit", "Specifies whether to limit code coverage hit reporting to a single hit for each location") { Arity = ArgumentArity.Zero };
+ var skipAutoProp = new Option("--skipautoprops", "Neither track nor record auto-implemented properties.") { Arity = ArgumentArity.Zero };
+ var mergeWith = new Option("--merge-with", "Path to existing coverage result to merge.") { Arity = ArgumentArity.ZeroOrOne };
+ var useSourceLink = new Option("--use-source-link", "Specifies whether to use SourceLink URIs in place of file system paths.") { Arity = ArgumentArity.Zero };
+ var doesNotReturnAttributes = new Option("--does-not-return-attribute", "Attributes that mark methods that do not return") { Arity = ArgumentArity.ZeroOrMore, AllowMultipleArgumentsPerToken = true };
+ var excludeAssembliesWithoutSources = new Option("--exclude-assemblies-without-sources", "Specifies behaviour of heuristic to ignore assemblies with missing source documents.") { Arity = ArgumentArity.ZeroOrOne };
+ var sourceMappingFile = new Option("--source-mapping-file", "Specifies the path to a SourceRootsMappings file.") { Arity = ArgumentArity.ZeroOrOne };
+
+ RootCommand rootCommand = new()
+ {
+ moduleOrAppDirectory,
+ target,
+ targs,
+ output,
+ verbosity,
+ formats,
+ threshold,
+ thresholdTypes,
+ thresholdStat,
+ excludeFilters,
+ includeFilters,
+ excludedSourceFiles,
+ includeDirectories,
+ excludeAttributes,
+ includeTestAssembly,
+ singleHit,
+ skipAutoProp,
+ mergeWith,
+ useSourceLink,
+ doesNotReturnAttributes,
+ excludeAssembliesWithoutSources,
+ sourceMappingFile
+ };
+
+ rootCommand.Description = "Cross platform .NET Core code coverage tool";
+
+ rootCommand.SetHandler(async (context) =>
+ {
+ string moduleOrAppDirectoryValue = context.ParseResult.GetValueForArgument(moduleOrAppDirectory);
+ string targetValue = context.ParseResult.GetValueForOption(target);
+ string targsValue = context.ParseResult.GetValueForOption(targs);
+ string outputValue = context.ParseResult.GetValueForOption(output);
+ LogLevel verbosityValue = context.ParseResult.GetValueForOption(verbosity);
+ string[] formatsValue = context.ParseResult.GetValueForOption(formats);
+ string thresholdValue = context.ParseResult.GetValueForOption(threshold);
+ List thresholdTypesValue = context.ParseResult.GetValueForOption(thresholdTypes);
+ ThresholdStatistic thresholdStatValue = context.ParseResult.GetValueForOption(thresholdStat);
+ string[] excludeFiltersValue = context.ParseResult.GetValueForOption(excludeFilters);
+ string[] includeFiltersValue = context.ParseResult.GetValueForOption(includeFilters);
+ string[] excludedSourceFilesValue = context.ParseResult.GetValueForOption(excludedSourceFiles);
+ string[] includeDirectoriesValue = context.ParseResult.GetValueForOption(includeDirectories);
+ string[] excludeAttributesValue = context.ParseResult.GetValueForOption(excludeAttributes);
+ bool includeTestAssemblyValue = context.ParseResult.GetValueForOption(includeTestAssembly);
+ bool singleHitValue = context.ParseResult.GetValueForOption(singleHit);
+ bool skipAutoPropValue = context.ParseResult.GetValueForOption(skipAutoProp);
+ string mergeWithValue = context.ParseResult.GetValueForOption(mergeWith);
+ bool useSourceLinkValue = context.ParseResult.GetValueForOption(useSourceLink);
+ string[] doesNotReturnAttributesValue = context.ParseResult.GetValueForOption(doesNotReturnAttributes);
+ string excludeAssembliesWithoutSourcesValue = context.ParseResult.GetValueForOption(excludeAssembliesWithoutSources);
+ string sourceMappingFileValue = context.ParseResult.GetValueForOption(sourceMappingFile);
+
+ if (string.IsNullOrEmpty(moduleOrAppDirectoryValue) || string.IsNullOrWhiteSpace(moduleOrAppDirectoryValue))
+ throw new ArgumentException("No test assembly or application directory specified.");
+
+ int taskStatus = await HandleCommand(moduleOrAppDirectoryValue,
+ targetValue,
+ targsValue,
+ outputValue,
+ verbosityValue,
+ formatsValue,
+ thresholdValue,
+ thresholdTypesValue,
+ thresholdStatValue,
+ excludeFiltersValue,
+ includeFiltersValue,
+ excludedSourceFilesValue,
+ includeDirectoriesValue,
+ excludeAttributesValue,
+ includeTestAssemblyValue,
+ singleHitValue,
+ skipAutoPropValue,
+ mergeWithValue,
+ useSourceLinkValue,
+ doesNotReturnAttributesValue,
+ excludeAssembliesWithoutSourcesValue,
+ sourceMappingFileValue);
+ context.ExitCode = taskStatus;
+
+ });
+ return rootCommand.Invoke(args);
+ }
+ private static Task HandleCommand(string moduleOrAppDirectory,
+ string target,
+ string targs,
+ string output,
+ LogLevel verbosity,
+ string[] formats,
+ string threshold,
+ List thresholdTypes,
+ ThresholdStatistic thresholdStat,
+ string[] excludeFilters,
+ string[] includeFilters,
+ string[] excludedSourceFiles,
+ string[] includeDirectories,
+ string[] excludeAttributes,
+ bool includeTestAssembly,
+ bool singleHit,
+ bool skipAutoProp,
+ string mergeWith,
+ bool useSourceLink,
+ string[] doesNotReturnAttributes,
+ string excludeAssembliesWithoutSources,
+ string sourceMappingFile
+ )
+ {
+
+ IServiceCollection serviceCollection = new ServiceCollection();
+ serviceCollection.AddTransient();
+ serviceCollection.AddTransient();
+ serviceCollection.AddTransient();
+ serviceCollection.AddTransient();
+ // We need to keep singleton/static semantics
+ serviceCollection.AddSingleton();
+ serviceCollection.AddSingleton(provider => new SourceRootTranslator(sourceMappingFile, provider.GetRequiredService(), provider.GetRequiredService()));
+ serviceCollection.AddSingleton();
+
+ ServiceProvider serviceProvider = serviceCollection.BuildServiceProvider();
+
+ var logger = (ConsoleLogger)serviceProvider.GetService();
+ IFileSystem fileSystem = serviceProvider.GetService();
+
+ // Adjust log level based on user input.
+ logger.Level = verbosity;
+ int exitCode = (int)CommandExitCodes.Success;
+
+ try
+ {
+ CoverageParameters parameters = new()
{
- IServiceCollection serviceCollection = new ServiceCollection();
- serviceCollection.AddTransient();
- serviceCollection.AddTransient();
- serviceCollection.AddTransient();
- serviceCollection.AddTransient();
- // We need to keep singleton/static semantics
- serviceCollection.AddSingleton();
- serviceCollection.AddSingleton(provider => new SourceRootTranslator(provider.GetRequiredService(), provider.GetRequiredService()));
- serviceCollection.AddSingleton();
-
- ServiceProvider serviceProvider = serviceCollection.BuildServiceProvider();
-
- var logger = (ConsoleLogger)serviceProvider.GetService();
- IFileSystem fileSystem = serviceProvider.GetService();
-
- var app = new CommandLineApplication
- {
- Name = "coverlet",
- FullName = "Cross platform .NET Core code coverage tool"
- };
- app.HelpOption("-h|--help");
- app.VersionOption("-v|--version", GetAssemblyVersion());
- int exitCode = (int)CommandExitCodes.Success;
-
- CommandArgument moduleOrAppDirectory = app.Argument("", "Path to the test assembly or application directory.");
- CommandOption target = app.Option("-t|--target", "Path to the test runner application.", CommandOptionType.SingleValue);
- CommandOption targs = app.Option("-a|--targetargs", "Arguments to be passed to the test runner.", CommandOptionType.SingleValue);
- CommandOption output = app.Option("-o|--output", "Output of the generated coverage report", CommandOptionType.SingleValue);
- CommandOption verbosity = app.Option("-v|--verbosity", "Sets the verbosity level of the command. Allowed values are quiet, minimal, normal, detailed.", CommandOptionType.SingleValue);
- CommandOption formats = app.Option("-f|--format", "Format of the generated coverage report.", CommandOptionType.MultipleValue);
- CommandOption threshold = app.Option("--threshold", "Exits with error if the coverage % is below value.", CommandOptionType.SingleValue);
- CommandOption thresholdTypes = app.Option("--threshold-type", "Coverage type to apply the threshold to.", CommandOptionType.MultipleValue);
- CommandOption thresholdStat = app.Option("--threshold-stat", "Coverage statistic used to enforce the threshold value.", CommandOptionType.SingleValue);
- CommandOption excludeFilters = app.Option("--exclude", "Filter expressions to exclude specific modules and types.", CommandOptionType.MultipleValue);
- CommandOption includeFilters = app.Option("--include", "Filter expressions to include only specific modules and types.", CommandOptionType.MultipleValue);
- CommandOption excludedSourceFiles = app.Option("--exclude-by-file", "Glob patterns specifying source files to exclude.", CommandOptionType.MultipleValue);
- CommandOption includeDirectories = app.Option("--include-directory", "Include directories containing additional assemblies to be instrumented.", CommandOptionType.MultipleValue);
- CommandOption excludeAttributes = app.Option("--exclude-by-attribute", "Attributes to exclude from code coverage.", CommandOptionType.MultipleValue);
- CommandOption includeTestAssembly = app.Option("--include-test-assembly", "Specifies whether to report code coverage of the test assembly.", CommandOptionType.NoValue);
- CommandOption singleHit = app.Option("--single-hit", "Specifies whether to limit code coverage hit reporting to a single hit for each location", CommandOptionType.NoValue);
- CommandOption skipAutoProp = app.Option("--skipautoprops", "Neither track nor record auto-implemented properties.", CommandOptionType.NoValue);
- CommandOption mergeWith = app.Option("--merge-with", "Path to existing coverage result to merge.", CommandOptionType.SingleValue);
- CommandOption useSourceLink = app.Option("--use-source-link", "Specifies whether to use SourceLink URIs in place of file system paths.", CommandOptionType.NoValue);
- CommandOption doesNotReturnAttributes = app.Option("--does-not-return-attribute", "Attributes that mark methods that do not return.", CommandOptionType.MultipleValue);
- CommandOption excludeAssembliesWithoutSources = app.Option("--exclude-assemblies-without-sources", "Specifies behaviour of heuristic to ignore assemblies with missing source documents.", CommandOptionType.SingleValue);
-
- app.OnExecute(() =>
- {
- if (string.IsNullOrEmpty(moduleOrAppDirectory.Value) || string.IsNullOrWhiteSpace(moduleOrAppDirectory.Value))
- throw new CommandParsingException(app, "No test assembly or application directory specified.");
-
- if (!target.HasValue())
- throw new CommandParsingException(app, "Target must be specified.");
-
- if (verbosity.HasValue())
- {
- // Adjust log level based on user input.
- logger.Level = verbosity.ParsedValue;
- }
-
- CoverageParameters parameters = new()
- {
- IncludeFilters = includeFilters.Values.ToArray(),
- IncludeDirectories = includeDirectories.Values.ToArray(),
- ExcludeFilters = excludeFilters.Values.ToArray(),
- ExcludedSourceFiles = excludedSourceFiles.Values.ToArray(),
- ExcludeAttributes = excludeAttributes.Values.ToArray(),
- IncludeTestAssembly = includeTestAssembly.HasValue(),
- SingleHit = singleHit.HasValue(),
- MergeWith = mergeWith.Value(),
- UseSourceLink = useSourceLink.HasValue(),
- SkipAutoProps = skipAutoProp.HasValue(),
- DoesNotReturnAttributes = doesNotReturnAttributes.Values.ToArray(),
- ExcludeAssembliesWithoutSources = excludeAssembliesWithoutSources.Value()
- };
-
- ISourceRootTranslator sourceRootTranslator = serviceProvider.GetRequiredService();
-
- Coverage coverage = new(moduleOrAppDirectory.Value,
- parameters,
- logger,
- serviceProvider.GetRequiredService(),
- fileSystem,
- sourceRootTranslator,
- serviceProvider.GetRequiredService());
- coverage.PrepareModules();
-
- Process process = new();
- process.StartInfo.FileName = target.Value();
- process.StartInfo.Arguments = targs.HasValue() ? targs.Value() : string.Empty;
- process.StartInfo.CreateNoWindow = true;
- process.StartInfo.RedirectStandardOutput = true;
- process.StartInfo.RedirectStandardError = true;
- process.OutputDataReceived += (sender, eventArgs) =>
- {
- if (!string.IsNullOrEmpty(eventArgs.Data))
- logger.LogInformation(eventArgs.Data, important: true);
- };
-
- process.ErrorDataReceived += (sender, eventArgs) =>
- {
- if (!string.IsNullOrEmpty(eventArgs.Data))
- logger.LogError(eventArgs.Data);
- };
-
- process.Start();
-
- process.BeginErrorReadLine();
- process.BeginOutputReadLine();
-
- process.WaitForExit();
-
- string dOutput = output.HasValue() ? output.Value() : Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar.ToString();
- List dThresholdTypes = thresholdTypes.HasValue() ? thresholdTypes.Values : new List(new string[] { "line", "branch", "method" });
- ThresholdStatistic dThresholdStat = thresholdStat.HasValue() ? Enum.Parse(thresholdStat.Value(), true) : Enum.Parse("minimum", true);
-
- logger.LogInformation("\nCalculating coverage result...");
-
- CoverageResult result = coverage.GetCoverageResult();
-
- string directory = Path.GetDirectoryName(dOutput);
- if (directory == string.Empty)
- {
- directory = Directory.GetCurrentDirectory();
- }
- else if (!Directory.Exists(directory))
- {
- Directory.CreateDirectory(directory);
- }
-
- foreach (string format in formats.HasValue() ? formats.Values : new List(new string[] { "json" }))
- {
- Core.Abstractions.IReporter reporter = new ReporterFactory(format).CreateReporter();
- if (reporter == null)
- {
- throw new Exception($"Specified output format '{format}' is not supported");
- }
-
- if (reporter.OutputType == ReporterOutputType.Console)
- {
- // Output to console
- logger.LogInformation(" Outputting results to console", important: true);
- logger.LogInformation(reporter.Report(result, sourceRootTranslator), important: true);
- }
- else
- {
- // Output to file
- string filename = Path.GetFileName(dOutput);
- filename = (filename == string.Empty) ? $"coverage.{reporter.Extension}" : filename;
- filename = Path.HasExtension(filename) ? filename : $"{filename}.{reporter.Extension}";
-
- string report = Path.Combine(directory, filename);
- logger.LogInformation($" Generating report '{report}'", important: true);
- fileSystem.WriteAllText(report, reporter.Report(result, sourceRootTranslator));
- }
- }
-
- var thresholdTypeFlagQueue = new Queue();
-
- foreach (string thresholdType in dThresholdTypes)
- {
- if (thresholdType.Equals("line", StringComparison.OrdinalIgnoreCase))
- {
- thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Line);
- }
- else if (thresholdType.Equals("branch", StringComparison.OrdinalIgnoreCase))
- {
- thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Branch);
- }
- else if (thresholdType.Equals("method", StringComparison.OrdinalIgnoreCase))
- {
- thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Method);
- }
- }
-
- var thresholdTypeFlagValues = new Dictionary();
- if (threshold.HasValue() && threshold.Value().Contains(','))
- {
- IEnumerable thresholdValues = threshold.Value().Split(',', StringSplitOptions.RemoveEmptyEntries).Select(t => t.Trim());
- if (thresholdValues.Count() != thresholdTypeFlagQueue.Count)
- {
- throw new Exception($"Threshold type flag count ({thresholdTypeFlagQueue.Count}) and values count ({thresholdValues.Count()}) doesn't match");
- }
-
- foreach (string thresholdValue in thresholdValues)
- {
- if (double.TryParse(thresholdValue, out double value))
- {
- thresholdTypeFlagValues[thresholdTypeFlagQueue.Dequeue()] = value;
- }
- else
- {
- throw new Exception($"Invalid threshold value must be numeric");
- }
- }
- }
- else
- {
- double thresholdValue = threshold.HasValue() ? double.Parse(threshold.Value()) : 0;
-
- while (thresholdTypeFlagQueue.Any())
- {
- thresholdTypeFlagValues[thresholdTypeFlagQueue.Dequeue()] = thresholdValue;
- }
- }
-
- var coverageTable = new ConsoleTable("Module", "Line", "Branch", "Method");
- var summary = new CoverageSummary();
-
- CoverageDetails linePercentCalculation = summary.CalculateLineCoverage(result.Modules);
- CoverageDetails branchPercentCalculation = summary.CalculateBranchCoverage(result.Modules);
- CoverageDetails methodPercentCalculation = summary.CalculateMethodCoverage(result.Modules);
-
- double totalLinePercent = linePercentCalculation.Percent;
- double totalBranchPercent = branchPercentCalculation.Percent;
- double totalMethodPercent = methodPercentCalculation.Percent;
-
- double averageLinePercent = linePercentCalculation.AverageModulePercent;
- double averageBranchPercent = branchPercentCalculation.AverageModulePercent;
- double averageMethodPercent = methodPercentCalculation.AverageModulePercent;
-
- foreach (KeyValuePair _module in result.Modules)
- {
- double linePercent = summary.CalculateLineCoverage(_module.Value).Percent;
- double branchPercent = summary.CalculateBranchCoverage(_module.Value).Percent;
- double methodPercent = summary.CalculateMethodCoverage(_module.Value).Percent;
-
- coverageTable.AddRow(Path.GetFileNameWithoutExtension(_module.Key), $"{InvariantFormat(linePercent)}%", $"{InvariantFormat(branchPercent)}%", $"{InvariantFormat(methodPercent)}%");
- }
-
- logger.LogInformation(coverageTable.ToStringAlternative());
-
- coverageTable.Columns.Clear();
- coverageTable.Rows.Clear();
-
- coverageTable.AddColumn(new[] { "", "Line", "Branch", "Method" });
- coverageTable.AddRow("Total", $"{InvariantFormat(totalLinePercent)}%", $"{InvariantFormat(totalBranchPercent)}%", $"{InvariantFormat(totalMethodPercent)}%");
- coverageTable.AddRow("Average", $"{InvariantFormat(averageLinePercent)}%", $"{InvariantFormat(averageBranchPercent)}%", $"{InvariantFormat(averageMethodPercent)}%");
-
- logger.LogInformation(coverageTable.ToStringAlternative());
- if (process.ExitCode > 0)
- {
- exitCode += (int)CommandExitCodes.TestFailed;
- }
-
- ThresholdTypeFlags thresholdTypeFlags = result.GetThresholdTypesBelowThreshold(summary, thresholdTypeFlagValues, dThresholdStat);
- if (thresholdTypeFlags != ThresholdTypeFlags.None)
- {
- exitCode += (int)CommandExitCodes.CoverageBelowThreshold;
- var exceptionMessageBuilder = new StringBuilder();
- if ((thresholdTypeFlags & ThresholdTypeFlags.Line) != ThresholdTypeFlags.None)
- {
- exceptionMessageBuilder.AppendLine($"The {dThresholdStat.ToString().ToLower()} line coverage is below the specified {thresholdTypeFlagValues[ThresholdTypeFlags.Line]}");
- }
-
- if ((thresholdTypeFlags & ThresholdTypeFlags.Branch) != ThresholdTypeFlags.None)
- {
- exceptionMessageBuilder.AppendLine($"The {dThresholdStat.ToString().ToLower()} branch coverage is below the specified {thresholdTypeFlagValues[ThresholdTypeFlags.Branch]}");
- }
-
- if ((thresholdTypeFlags & ThresholdTypeFlags.Method) != ThresholdTypeFlags.None)
- {
- exceptionMessageBuilder.AppendLine($"The {dThresholdStat.ToString().ToLower()} method coverage is below the specified {thresholdTypeFlagValues[ThresholdTypeFlags.Method]}");
- }
- throw new Exception(exceptionMessageBuilder.ToString());
- }
-
- return exitCode;
- });
-
- try
- {
- return app.Execute(args);
- }
- catch (CommandParsingException ex)
- {
- logger.LogError(ex.Message);
- app.ShowHelp();
- return (int)CommandExitCodes.CommandParsingException;
- }
- catch (Win32Exception we) when (we.Source == "System.Diagnostics.Process")
+ IncludeFilters = includeFilters,
+ IncludeDirectories = includeDirectories,
+ ExcludeFilters = excludeFilters,
+ ExcludedSourceFiles = excludedSourceFiles,
+ ExcludeAttributes = excludeAttributes,
+ IncludeTestAssembly = includeTestAssembly,
+ SingleHit = singleHit,
+ MergeWith = mergeWith,
+ UseSourceLink = useSourceLink,
+ SkipAutoProps = skipAutoProp,
+ DoesNotReturnAttributes = doesNotReturnAttributes,
+ ExcludeAssembliesWithoutSources = excludeAssembliesWithoutSources
+ };
+ ISourceRootTranslator sourceRootTranslator = serviceProvider.GetRequiredService();
+
+ Coverage coverage = new(moduleOrAppDirectory,
+ parameters,
+ logger,
+ serviceProvider.GetRequiredService(),
+ fileSystem,
+ sourceRootTranslator,
+ serviceProvider.GetRequiredService());
+ coverage.PrepareModules();
+
+ Process process = new();
+ process.StartInfo.FileName = target;
+ process.StartInfo.Arguments = targs ?? string.Empty;
+ process.StartInfo.CreateNoWindow = true;
+ process.StartInfo.RedirectStandardOutput = true;
+ process.StartInfo.RedirectStandardError = true;
+ process.OutputDataReceived += (sender, eventArgs) =>
+ {
+ if (!string.IsNullOrEmpty(eventArgs.Data))
+ logger.LogInformation(eventArgs.Data, important: true);
+ };
+
+ process.ErrorDataReceived += (sender, eventArgs) =>
+ {
+ if (!string.IsNullOrEmpty(eventArgs.Data))
+ logger.LogError(eventArgs.Data);
+ };
+
+ process.Start();
+
+ process.BeginErrorReadLine();
+ process.BeginOutputReadLine();
+
+ process.WaitForExit();
+
+ string dOutput = output != null ? output : Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar.ToString();
+
+ logger.LogInformation("\nCalculating coverage result...");
+
+ CoverageResult result = coverage.GetCoverageResult();
+
+ string directory = Path.GetDirectoryName(dOutput);
+ if (directory == string.Empty)
+ {
+ directory = Directory.GetCurrentDirectory();
+ }
+ else if (!Directory.Exists(directory))
+ {
+ Directory.CreateDirectory(directory);
+ }
+
+ foreach (string format in formats)
+ {
+ IReporter reporter = new ReporterFactory(format).CreateReporter();
+ if (reporter == null)
+ {
+ throw new Exception($"Specified output format '{format}' is not supported");
+ }
+
+ if (reporter.OutputType == ReporterOutputType.Console)
+ {
+ // Output to console
+ logger.LogInformation(" Outputting results to console", important: true);
+ logger.LogInformation(reporter.Report(result, sourceRootTranslator), important: true);
+ }
+ else
+ {
+ // Output to file
+ string filename = Path.GetFileName(dOutput);
+ filename = (filename == string.Empty) ? $"coverage.{reporter.Extension}" : filename;
+ filename = Path.HasExtension(filename) ? filename : $"{filename}.{reporter.Extension}";
+
+ string report = Path.Combine(directory, filename);
+ logger.LogInformation($" Generating report '{report}'", important: true);
+ fileSystem.WriteAllText(report, reporter.Report(result, sourceRootTranslator));
+ }
+ }
+
+ var thresholdTypeFlagQueue = new Queue();
+
+ foreach (string thresholdType in thresholdTypes)
+ {
+ if (thresholdType.Equals("line", StringComparison.OrdinalIgnoreCase))
+ {
+ thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Line);
+ }
+ else if (thresholdType.Equals("branch", StringComparison.OrdinalIgnoreCase))
+ {
+ thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Branch);
+ }
+ else if (thresholdType.Equals("method", StringComparison.OrdinalIgnoreCase))
+ {
+ thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Method);
+ }
+ }
+
+ var thresholdTypeFlagValues = new Dictionary();
+ if (!string.IsNullOrEmpty(threshold) && threshold.Contains(','))
+ {
+ IEnumerable thresholdValues = threshold.Split(',', StringSplitOptions.RemoveEmptyEntries).Select(t => t.Trim());
+ if (thresholdValues.Count() != thresholdTypeFlagQueue.Count)
+ {
+ throw new Exception($"Threshold type flag count ({thresholdTypeFlagQueue.Count}) and values count ({thresholdValues.Count()}) doesn't match");
+ }
+
+ foreach (string thresholdValue in thresholdValues)
+ {
+ if (double.TryParse(thresholdValue, out double value))
{
- logger.LogError($"Start process '{target.Value()}' failed with '{we.Message}'");
- return exitCode > 0 ? exitCode : (int)CommandExitCodes.Exception;
+ thresholdTypeFlagValues[thresholdTypeFlagQueue.Dequeue()] = value;
}
- catch (Exception ex)
+ else
{
- logger.LogError(ex.Message);
- return exitCode > 0 ? exitCode : (int)CommandExitCodes.Exception;
+ throw new Exception($"Invalid threshold value must be numeric");
}
+ }
}
+ else
+ {
+ double thresholdValue = threshold != null ? double.Parse(threshold) : 0;
- static string GetAssemblyVersion() => typeof(Program).Assembly.GetName().Version.ToString();
+ while (thresholdTypeFlagQueue.Any())
+ {
+ thresholdTypeFlagValues[thresholdTypeFlagQueue.Dequeue()] = thresholdValue;
+ }
+ }
+
+ var coverageTable = new ConsoleTable("Module", "Line", "Branch", "Method");
+ var summary = new CoverageSummary();
+
+ CoverageDetails linePercentCalculation = summary.CalculateLineCoverage(result.Modules);
+ CoverageDetails branchPercentCalculation = summary.CalculateBranchCoverage(result.Modules);
+ CoverageDetails methodPercentCalculation = summary.CalculateMethodCoverage(result.Modules);
+
+ double totalLinePercent = linePercentCalculation.Percent;
+ double totalBranchPercent = branchPercentCalculation.Percent;
+ double totalMethodPercent = methodPercentCalculation.Percent;
+
+ double averageLinePercent = linePercentCalculation.AverageModulePercent;
+ double averageBranchPercent = branchPercentCalculation.AverageModulePercent;
+ double averageMethodPercent = methodPercentCalculation.AverageModulePercent;
+
+ foreach (KeyValuePair _module in result.Modules)
+ {
+ double linePercent = summary.CalculateLineCoverage(_module.Value).Percent;
+ double branchPercent = summary.CalculateBranchCoverage(_module.Value).Percent;
+ double methodPercent = summary.CalculateMethodCoverage(_module.Value).Percent;
+
+ coverageTable.AddRow(Path.GetFileNameWithoutExtension(_module.Key), $"{InvariantFormat(linePercent)}%", $"{InvariantFormat(branchPercent)}%", $"{InvariantFormat(methodPercent)}%");
+ }
+
+ logger.LogInformation(coverageTable.ToStringAlternative());
+
+ coverageTable.Columns.Clear();
+ coverageTable.Rows.Clear();
+
+ coverageTable.AddColumn(new[] { "", "Line", "Branch", "Method" });
+ coverageTable.AddRow("Total", $"{InvariantFormat(totalLinePercent)}%", $"{InvariantFormat(totalBranchPercent)}%", $"{InvariantFormat(totalMethodPercent)}%");
+ coverageTable.AddRow("Average", $"{InvariantFormat(averageLinePercent)}%", $"{InvariantFormat(averageBranchPercent)}%", $"{InvariantFormat(averageMethodPercent)}%");
+
+ logger.LogInformation(coverageTable.ToStringAlternative());
+ if (process.ExitCode > 0)
+ {
+ exitCode += (int)CommandExitCodes.TestFailed;
+ }
+
+ ThresholdTypeFlags thresholdTypeFlags = result.GetThresholdTypesBelowThreshold(summary, thresholdTypeFlagValues, thresholdStat);
+ if (thresholdTypeFlags != ThresholdTypeFlags.None)
+ {
+ exitCode += (int)CommandExitCodes.CoverageBelowThreshold;
+ var exceptionMessageBuilder = new StringBuilder();
+ if ((thresholdTypeFlags & ThresholdTypeFlags.Line) != ThresholdTypeFlags.None)
+ {
+ exceptionMessageBuilder.AppendLine($"The {thresholdStat.ToString().ToLower()} line coverage is below the specified {thresholdTypeFlagValues[ThresholdTypeFlags.Line]}");
+ }
+
+ if ((thresholdTypeFlags & ThresholdTypeFlags.Branch) != ThresholdTypeFlags.None)
+ {
+ exceptionMessageBuilder.AppendLine($"The {thresholdStat.ToString().ToLower()} branch coverage is below the specified {thresholdTypeFlagValues[ThresholdTypeFlags.Branch]}");
+ }
+
+ if ((thresholdTypeFlags & ThresholdTypeFlags.Method) != ThresholdTypeFlags.None)
+ {
+ exceptionMessageBuilder.AppendLine($"The {thresholdStat.ToString().ToLower()} method coverage is below the specified {thresholdTypeFlagValues[ThresholdTypeFlags.Method]}");
+ }
+ throw new Exception(exceptionMessageBuilder.ToString());
+ }
+
+ return Task.FromResult(exitCode);
+
+ }
+
+ catch (Win32Exception we) when (we.Source == "System.Diagnostics.Process")
+ {
+ logger.LogError($"Start process '{target}' failed with '{we.Message}'");
+ return Task.FromResult(exitCode > 0 ? exitCode : (int)CommandExitCodes.Exception);
+ }
+ catch (Exception ex)
+ {
+ logger.LogError(ex.Message);
+ return Task.FromResult(exitCode > 0 ? exitCode : (int)CommandExitCodes.Exception);
+ }
- static string InvariantFormat(double value) => value.ToString(CultureInfo.InvariantCulture);
}
+
+ static string InvariantFormat(double value) => value.ToString(CultureInfo.InvariantCulture);
+ }
}
diff --git a/src/coverlet.console/Properties/AssemblyInfo.cs b/src/coverlet.console/Properties/AssemblyInfo.cs
index 59db2a82f..aff16fa56 100644
--- a/src/coverlet.console/Properties/AssemblyInfo.cs
+++ b/src/coverlet.console/Properties/AssemblyInfo.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Toni Solarin-Sodara
+// Copyright (c) Toni Solarin-Sodara
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-[assembly: System.Reflection.AssemblyKeyFileAttribute("coverlet.console.snk")]
\ No newline at end of file
+[assembly: System.Reflection.AssemblyKeyFileAttribute("coverlet.console.snk")]
diff --git a/src/coverlet.console/coverlet.console.csproj b/src/coverlet.console/coverlet.console.csproj
index b44e05dff..d4d110fa8 100644
--- a/src/coverlet.console/coverlet.console.csproj
+++ b/src/coverlet.console/coverlet.console.csproj
@@ -14,15 +14,17 @@
tonerdo
Coverlet is a cross platform code coverage tool for .NET, with support for line, branch and method coverage.
coverage;testing;unit-test;lcov;opencover;quality
+ GlobalTool.md
+ https://github.com/coverlet-coverage/coverlet/blob/master/Documentation/Changelog.md
https://raw.githubusercontent.com/tonerdo/coverlet/master/_assets/coverlet-icon.svg?sanitize=true
coverlet-icon.png
https://github.com/coverlet-coverage/coverlet
MIT
git
-
+
-
+
@@ -31,6 +33,9 @@
+
+ true
+
diff --git a/src/coverlet.core/Abstractions/IAssemblyAdapter.cs b/src/coverlet.core/Abstractions/IAssemblyAdapter.cs
index 48b23084b..a020bced5 100644
--- a/src/coverlet.core/Abstractions/IAssemblyAdapter.cs
+++ b/src/coverlet.core/Abstractions/IAssemblyAdapter.cs
@@ -3,8 +3,8 @@
namespace Coverlet.Core.Abstractions
{
- internal interface IAssemblyAdapter
- {
- string GetAssemblyName(string assemblyPath);
- }
+ internal interface IAssemblyAdapter
+ {
+ string GetAssemblyName(string assemblyPath);
+ }
}
diff --git a/src/coverlet.core/Abstractions/ICecilSymbolHelper.cs b/src/coverlet.core/Abstractions/ICecilSymbolHelper.cs
index 06cdde3d1..b22463b5d 100644
--- a/src/coverlet.core/Abstractions/ICecilSymbolHelper.cs
+++ b/src/coverlet.core/Abstractions/ICecilSymbolHelper.cs
@@ -8,10 +8,10 @@
namespace Coverlet.Core.Abstractions
{
- internal interface ICecilSymbolHelper
- {
- IReadOnlyList GetBranchPoints(MethodDefinition methodDefinition);
- bool SkipNotCoverableInstruction(MethodDefinition methodDefinition, Instruction instruction);
- bool SkipInlineAssignedAutoProperty(bool skipAutoProps, MethodDefinition methodDefinition, Instruction instruction);
- }
+ internal interface ICecilSymbolHelper
+ {
+ IReadOnlyList GetBranchPoints(MethodDefinition methodDefinition);
+ bool SkipNotCoverableInstruction(MethodDefinition methodDefinition, Instruction instruction);
+ bool SkipInlineAssignedAutoProperty(bool skipAutoProps, MethodDefinition methodDefinition, Instruction instruction);
+ }
}
diff --git a/src/coverlet.core/Abstractions/IConsole.cs b/src/coverlet.core/Abstractions/IConsole.cs
index 72991cac5..2724ccf8b 100644
--- a/src/coverlet.core/Abstractions/IConsole.cs
+++ b/src/coverlet.core/Abstractions/IConsole.cs
@@ -3,8 +3,8 @@
namespace Coverlet.Core.Abstractions
{
- internal interface IConsole
- {
- public void WriteLine(string value);
- }
+ internal interface IConsole
+ {
+ public void WriteLine(string value);
+ }
}
diff --git a/src/coverlet.core/Abstractions/IFileSystem.cs b/src/coverlet.core/Abstractions/IFileSystem.cs
index cb710c758..6f0978467 100644
--- a/src/coverlet.core/Abstractions/IFileSystem.cs
+++ b/src/coverlet.core/Abstractions/IFileSystem.cs
@@ -5,24 +5,24 @@
namespace Coverlet.Core.Abstractions
{
- internal interface IFileSystem
- {
- bool Exists(string path);
+ internal interface IFileSystem
+ {
+ bool Exists(string path);
- void WriteAllText(string path, string contents);
+ void WriteAllText(string path, string contents);
- string ReadAllText(string path);
+ string ReadAllText(string path);
- Stream OpenRead(string path);
+ Stream OpenRead(string path);
- void Copy(string sourceFileName, string destFileName, bool overwrite);
+ void Copy(string sourceFileName, string destFileName, bool overwrite);
- void Delete(string path);
+ void Delete(string path);
- Stream NewFileStream(string path, FileMode mode);
+ Stream NewFileStream(string path, FileMode mode);
- Stream NewFileStream(string path, FileMode mode, FileAccess access);
+ Stream NewFileStream(string path, FileMode mode, FileAccess access);
- string[] ReadAllLines(string path);
- }
+ string[] ReadAllLines(string path);
+ }
}
diff --git a/src/coverlet.core/Abstractions/IInstrumentationHelper.cs b/src/coverlet.core/Abstractions/IInstrumentationHelper.cs
index 65af40011..d363fab63 100644
--- a/src/coverlet.core/Abstractions/IInstrumentationHelper.cs
+++ b/src/coverlet.core/Abstractions/IInstrumentationHelper.cs
@@ -1,25 +1,25 @@
// Copyright (c) Toni Solarin-Sodara
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using System.Collections.Generic;
using Coverlet.Core.Enums;
namespace Coverlet.Core.Abstractions
{
- internal interface IInstrumentationHelper
- {
- void BackupOriginalModule(string module, string identifier);
- void DeleteHitsFile(string path);
- string[] GetCoverableModules(string module, string[] directories, bool includeTestAssembly);
- bool HasPdb(string module, out bool embedded);
- bool IsModuleExcluded(string module, string[] excludeFilters);
- bool IsModuleIncluded(string module, string[] includeFilters);
- bool IsValidFilterExpression(string filter);
- bool IsTypeExcluded(string module, string type, string[] excludeFilters);
- bool IsTypeIncluded(string module, string type, string[] includeFilters);
- void RestoreOriginalModule(string module, string identifier);
- bool EmbeddedPortablePdbHasLocalSource(string module, AssemblySearchType excludeAssembliesWithoutSources);
- bool PortablePdbHasLocalSource(string module, AssemblySearchType excludeAssembliesWithoutSources);
- bool IsLocalMethod(string method);
- void SetLogger(ILogger logger);
- }
+ internal interface IInstrumentationHelper
+ {
+ void BackupOriginalModule(string module, string identifier);
+ void DeleteHitsFile(string path);
+ string[] GetCoverableModules(string module, string[] directories, bool includeTestAssembly);
+ bool HasPdb(string module, out bool embedded);
+ IEnumerable SelectModules(IEnumerable modules, string[] includeFilters, string[] excludeFilters);
+ bool IsValidFilterExpression(string filter);
+ bool IsTypeExcluded(string module, string type, string[] excludeFilters);
+ bool IsTypeIncluded(string module, string type, string[] includeFilters);
+ void RestoreOriginalModule(string module, string identifier);
+ bool EmbeddedPortablePdbHasLocalSource(string module, AssemblySearchType excludeAssembliesWithoutSources);
+ bool PortablePdbHasLocalSource(string module, AssemblySearchType excludeAssembliesWithoutSources);
+ bool IsLocalMethod(string method);
+ void SetLogger(ILogger logger);
+ }
}
diff --git a/src/coverlet.core/Abstractions/ILogger.cs b/src/coverlet.core/Abstractions/ILogger.cs
index c3e6ef15e..c03831531 100644
--- a/src/coverlet.core/Abstractions/ILogger.cs
+++ b/src/coverlet.core/Abstractions/ILogger.cs
@@ -1,16 +1,16 @@
-// Copyright (c) Toni Solarin-Sodara
+// Copyright (c) Toni Solarin-Sodara
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
namespace Coverlet.Core.Abstractions
{
- internal interface ILogger
- {
- void LogVerbose(string message);
- void LogInformation(string message, bool important = false);
- void LogWarning(string message);
- void LogError(string message);
- void LogError(Exception exception);
- }
-}
\ No newline at end of file
+ internal interface ILogger
+ {
+ void LogVerbose(string message);
+ void LogInformation(string message, bool important = false);
+ void LogWarning(string message);
+ void LogError(string message);
+ void LogError(Exception exception);
+ }
+}
diff --git a/src/coverlet.core/Abstractions/IProcessExitHandler.cs b/src/coverlet.core/Abstractions/IProcessExitHandler.cs
index 635015946..c0f9d524c 100644
--- a/src/coverlet.core/Abstractions/IProcessExitHandler.cs
+++ b/src/coverlet.core/Abstractions/IProcessExitHandler.cs
@@ -5,8 +5,8 @@
namespace Coverlet.Core.Abstractions
{
- internal interface IProcessExitHandler
- {
- void Add(EventHandler handler);
- }
+ internal interface IProcessExitHandler
+ {
+ void Add(EventHandler handler);
+ }
}
diff --git a/src/coverlet.core/Abstractions/IReporter.cs b/src/coverlet.core/Abstractions/IReporter.cs
index 9e497d62a..82441aa37 100644
--- a/src/coverlet.core/Abstractions/IReporter.cs
+++ b/src/coverlet.core/Abstractions/IReporter.cs
@@ -1,19 +1,19 @@
-// Copyright (c) Toni Solarin-Sodara
+// Copyright (c) Toni Solarin-Sodara
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Coverlet.Core.Abstractions
{
- internal interface IReporter
- {
- ReporterOutputType OutputType { get; }
- string Format { get; }
- string Extension { get; }
- string Report(CoverageResult result, ISourceRootTranslator sourceRootTranslator);
- }
+ internal interface IReporter
+ {
+ ReporterOutputType OutputType { get; }
+ string Format { get; }
+ string Extension { get; }
+ string Report(CoverageResult result, ISourceRootTranslator sourceRootTranslator);
+ }
- internal enum ReporterOutputType
- {
- File,
- Console,
- }
-}
\ No newline at end of file
+ internal enum ReporterOutputType
+ {
+ File,
+ Console,
+ }
+}
diff --git a/src/coverlet.core/Abstractions/IRetryHelper.cs b/src/coverlet.core/Abstractions/IRetryHelper.cs
index 88a1b29d5..85f8b53bb 100644
--- a/src/coverlet.core/Abstractions/IRetryHelper.cs
+++ b/src/coverlet.core/Abstractions/IRetryHelper.cs
@@ -5,9 +5,9 @@
namespace Coverlet.Core.Abstractions
{
- internal interface IRetryHelper
- {
- void Retry(Action action, Func backoffStrategy, int maxAttemptCount = 3);
- T Do(Func action, Func backoffStrategy, int maxAttemptCount = 3);
- }
+ internal interface IRetryHelper
+ {
+ void Retry(Action action, Func backoffStrategy, int maxAttemptCount = 3);
+ T Do(Func action, Func backoffStrategy, int maxAttemptCount = 3);
+ }
}
diff --git a/src/coverlet.core/Abstractions/ISourceRootTranslator.cs b/src/coverlet.core/Abstractions/ISourceRootTranslator.cs
index 4af6cfddf..2bbef7a87 100644
--- a/src/coverlet.core/Abstractions/ISourceRootTranslator.cs
+++ b/src/coverlet.core/Abstractions/ISourceRootTranslator.cs
@@ -6,11 +6,11 @@
namespace Coverlet.Core.Abstractions
{
- internal interface ISourceRootTranslator
- {
- bool AddMappingInCache(string originalFileName, string targetFileName);
- string ResolveFilePath(string originalFileName);
- string ResolveDeterministicPath(string originalFileName);
- IReadOnlyList ResolvePathRoot(string pathRoot);
- }
+ internal interface ISourceRootTranslator
+ {
+ bool AddMappingInCache(string originalFileName, string targetFileName);
+ string ResolveFilePath(string originalFileName);
+ string ResolveDeterministicPath(string originalFileName);
+ IReadOnlyList ResolvePathRoot(string pathRoot);
+ }
}
diff --git a/src/coverlet.core/Attributes/DoesNotReturnAttribute.cs b/src/coverlet.core/Attributes/DoesNotReturnAttribute.cs
index cebe198f1..074d0a59f 100644
--- a/src/coverlet.core/Attributes/DoesNotReturnAttribute.cs
+++ b/src/coverlet.core/Attributes/DoesNotReturnAttribute.cs
@@ -5,6 +5,6 @@
namespace Coverlet.Core.Attributes
{
- [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Class)]
- internal class DoesNotReturnAttribute : Attribute { }
+ [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Class)]
+ internal class DoesNotReturnAttribute : Attribute { }
}
diff --git a/src/coverlet.core/Attributes/ExcludeFromCoverage.cs b/src/coverlet.core/Attributes/ExcludeFromCoverage.cs
index 71efcb7bc..1436c2175 100644
--- a/src/coverlet.core/Attributes/ExcludeFromCoverage.cs
+++ b/src/coverlet.core/Attributes/ExcludeFromCoverage.cs
@@ -1,10 +1,10 @@
-// Copyright (c) Toni Solarin-Sodara
+// Copyright (c) Toni Solarin-Sodara
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
namespace Coverlet.Core.Attributes
{
- [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Class)]
- internal sealed class ExcludeFromCoverageAttribute : Attribute { }
-}
\ No newline at end of file
+ [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Class)]
+ internal sealed class ExcludeFromCoverageAttribute : Attribute { }
+}
diff --git a/src/coverlet.core/Coverage.cs b/src/coverlet.core/Coverage.cs
index 8a3def37b..e3957cb72 100644
--- a/src/coverlet.core/Coverage.cs
+++ b/src/coverlet.core/Coverage.cs
@@ -6,521 +6,531 @@
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
using Coverlet.Core.Abstractions;
using Coverlet.Core.Helpers;
using Coverlet.Core.Instrumentation;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
namespace Coverlet.Core
{
- [DataContract]
- internal class CoverageParameters
+ [DataContract]
+ internal class CoverageParameters
+ {
+ [DataMember]
+ public string Module { get; set; }
+ [DataMember]
+ public string[] IncludeFilters { get; set; }
+ [DataMember]
+ public string[] IncludeDirectories { get; set; }
+ [DataMember]
+ public string[] ExcludeFilters { get; set; }
+ [DataMember]
+ public string[] ExcludedSourceFiles { get; set; }
+ [DataMember]
+ public string[] ExcludeAttributes { get; set; }
+ [DataMember]
+ public bool IncludeTestAssembly { get; set; }
+ [DataMember]
+ public bool SingleHit { get; set; }
+ [DataMember]
+ public string MergeWith { get; set; }
+ [DataMember]
+ public bool UseSourceLink { get; set; }
+ [DataMember]
+ public string[] DoesNotReturnAttributes { get; set; }
+ [DataMember]
+ public bool SkipAutoProps { get; set; }
+ [DataMember]
+ public bool DeterministicReport { get; set; }
+ [DataMember]
+ public string ExcludeAssembliesWithoutSources { get; set; }
+ }
+
+ internal class Coverage
+ {
+ private readonly string _moduleOrAppDirectory;
+ private readonly ILogger _logger;
+ private readonly IInstrumentationHelper _instrumentationHelper;
+ private readonly IFileSystem _fileSystem;
+ private readonly ISourceRootTranslator _sourceRootTranslator;
+ private readonly ICecilSymbolHelper _cecilSymbolHelper;
+ private readonly List _results;
+ private readonly CoverageParameters _parameters;
+
+ public string Identifier { get; }
+
+ public Coverage(string moduleOrDirectory,
+ CoverageParameters parameters,
+ ILogger logger,
+ IInstrumentationHelper instrumentationHelper,
+ IFileSystem fileSystem,
+ ISourceRootTranslator sourceRootTranslator,
+ ICecilSymbolHelper cecilSymbolHelper)
{
- [DataMember]
- public string Module { get; set; }
- [DataMember]
- public string[] IncludeFilters { get; set; }
- [DataMember]
- public string[] IncludeDirectories { get; set; }
- [DataMember]
- public string[] ExcludeFilters { get; set; }
- [DataMember]
- public string[] ExcludedSourceFiles { get; set; }
- [DataMember]
- public string[] ExcludeAttributes { get; set; }
- [DataMember]
- public bool IncludeTestAssembly { get; set; }
- [DataMember]
- public bool SingleHit { get; set; }
- [DataMember]
- public string MergeWith { get; set; }
- [DataMember]
- public bool UseSourceLink { get; set; }
- [DataMember]
- public string[] DoesNotReturnAttributes { get; set; }
- [DataMember]
- public bool SkipAutoProps { get; set; }
- [DataMember]
- public bool DeterministicReport { get; set; }
- [DataMember]
- public string ExcludeAssembliesWithoutSources { get; set; }
+ _moduleOrAppDirectory = moduleOrDirectory;
+ parameters.IncludeDirectories ??= Array.Empty();
+ _logger = logger;
+ _instrumentationHelper = instrumentationHelper;
+ _parameters = parameters;
+ _fileSystem = fileSystem;
+ _sourceRootTranslator = sourceRootTranslator;
+ _cecilSymbolHelper = cecilSymbolHelper;
+ Identifier = Guid.NewGuid().ToString();
+ _results = new List();
}
- internal class Coverage
+ public Coverage(CoveragePrepareResult prepareResult,
+ ILogger logger,
+ IInstrumentationHelper instrumentationHelper,
+ IFileSystem fileSystem,
+ ISourceRootTranslator sourceRootTranslator)
{
- private readonly string _moduleOrAppDirectory;
- private readonly ILogger _logger;
- private readonly IInstrumentationHelper _instrumentationHelper;
- private readonly IFileSystem _fileSystem;
- private readonly ISourceRootTranslator _sourceRootTranslator;
- private readonly ICecilSymbolHelper _cecilSymbolHelper;
- private readonly List _results;
- private readonly CoverageParameters _parameters;
-
- public string Identifier { get; }
-
- public Coverage(string moduleOrDirectory,
- CoverageParameters parameters,
- ILogger logger,
- IInstrumentationHelper instrumentationHelper,
- IFileSystem fileSystem,
- ISourceRootTranslator sourceRootTranslator,
- ICecilSymbolHelper cecilSymbolHelper)
- {
- _moduleOrAppDirectory = moduleOrDirectory;
- parameters.IncludeDirectories ??= Array.Empty();
- _logger = logger;
- _instrumentationHelper = instrumentationHelper;
- _parameters = parameters;
- _fileSystem = fileSystem;
- _sourceRootTranslator = sourceRootTranslator;
- _cecilSymbolHelper = cecilSymbolHelper;
- Identifier = Guid.NewGuid().ToString();
- _results = new List();
- }
-
- public Coverage(CoveragePrepareResult prepareResult,
- ILogger logger,
- IInstrumentationHelper instrumentationHelper,
- IFileSystem fileSystem,
- ISourceRootTranslator sourceRootTranslator)
- {
- Identifier = prepareResult.Identifier;
- _moduleOrAppDirectory = prepareResult.ModuleOrDirectory;
- _parameters = prepareResult.Parameters;
- _results = new List(prepareResult.Results);
- _logger = logger;
- _instrumentationHelper = instrumentationHelper;
- _fileSystem = fileSystem;
- _sourceRootTranslator = sourceRootTranslator;
- }
+ Identifier = prepareResult.Identifier;
+ _moduleOrAppDirectory = prepareResult.ModuleOrDirectory;
+ _parameters = prepareResult.Parameters;
+ _results = new List(prepareResult.Results);
+ _logger = logger;
+ _instrumentationHelper = instrumentationHelper;
+ _fileSystem = fileSystem;
+ _sourceRootTranslator = sourceRootTranslator;
+ }
- public CoveragePrepareResult PrepareModules()
+ public CoveragePrepareResult PrepareModules()
+ {
+ string[] modules = _instrumentationHelper.GetCoverableModules(_moduleOrAppDirectory, _parameters.IncludeDirectories, _parameters.IncludeTestAssembly);
+
+ Array.ForEach(_parameters.ExcludeFilters ?? Array.Empty(), filter => _logger.LogVerbose($"Excluded module filter '{filter}'"));
+ Array.ForEach(_parameters.IncludeFilters ?? Array.Empty(), filter => _logger.LogVerbose($"Included module filter '{filter}'"));
+ Array.ForEach(_parameters.ExcludedSourceFiles ?? Array.Empty(), filter => _logger.LogVerbose($"Excluded source files filter '{FileSystem.EscapeFileName(filter)}'"));
+
+ _parameters.ExcludeFilters = _parameters.ExcludeFilters?.Where(f => _instrumentationHelper.IsValidFilterExpression(f)).ToArray();
+ _parameters.IncludeFilters = _parameters.IncludeFilters?.Where(f => _instrumentationHelper.IsValidFilterExpression(f)).ToArray();
+
+ IReadOnlyList validModules = _instrumentationHelper.SelectModules(modules, _parameters.IncludeFilters, _parameters.ExcludeFilters).ToList();
+ foreach (string excludedModule in modules.Except(validModules))
+ {
+ _logger.LogVerbose($"Excluded module: '{excludedModule}'");
+ }
+
+ foreach (string module in validModules)
+ {
+ var instrumenter = new Instrumenter(module,
+ Identifier,
+ _parameters,
+ _logger,
+ _instrumentationHelper,
+ _fileSystem,
+ _sourceRootTranslator,
+ _cecilSymbolHelper);
+
+ if (instrumenter.CanInstrument())
{
- string[] modules = _instrumentationHelper.GetCoverableModules(_moduleOrAppDirectory, _parameters.IncludeDirectories, _parameters.IncludeTestAssembly);
+ _instrumentationHelper.BackupOriginalModule(module, Identifier);
- Array.ForEach(_parameters.ExcludeFilters ?? Array.Empty(), filter => _logger.LogVerbose($"Excluded module filter '{filter}'"));
- Array.ForEach(_parameters.IncludeFilters ?? Array.Empty(), filter => _logger.LogVerbose($"Included module filter '{filter}'"));
- Array.ForEach(_parameters.ExcludedSourceFiles ?? Array.Empty(), filter => _logger.LogVerbose($"Excluded source files filter '{FileSystem.EscapeFileName(filter)}'"));
+ // Guard code path and restore if instrumentation fails.
+ try
+ {
+ InstrumenterResult result = instrumenter.Instrument();
+ if (!instrumenter.SkipModule)
+ {
+ _results.Add(result);
+ _logger.LogVerbose($"Instrumented module: '{module}'");
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning($"Unable to instrument module: {module}\n{ex}");
+ _instrumentationHelper.RestoreOriginalModule(module, Identifier);
+ }
+ }
+ }
+
+ return new CoveragePrepareResult()
+ {
+ Identifier = Identifier,
+ ModuleOrDirectory = _moduleOrAppDirectory,
+ Parameters = _parameters,
+ Results = _results.ToArray()
+ };
+ }
- _parameters.ExcludeFilters = _parameters.ExcludeFilters?.Where(f => _instrumentationHelper.IsValidFilterExpression(f)).ToArray();
- _parameters.IncludeFilters = _parameters.IncludeFilters?.Where(f => _instrumentationHelper.IsValidFilterExpression(f)).ToArray();
+ public CoverageResult GetCoverageResult()
+ {
+ CalculateCoverage();
- foreach (string module in modules)
+ var modules = new Modules();
+ foreach (InstrumenterResult result in _results)
+ {
+ var documents = new Documents();
+ foreach (Document doc in result.Documents.Values)
+ {
+ // Construct Line Results
+ foreach (Line line in doc.Lines.Values)
+ {
+ if (documents.TryGetValue(doc.Path, out Classes classes))
{
- if (_instrumentationHelper.IsModuleExcluded(module, _parameters.ExcludeFilters) ||
- !_instrumentationHelper.IsModuleIncluded(module, _parameters.IncludeFilters))
+ if (classes.TryGetValue(line.Class, out Methods methods))
+ {
+ if (methods.TryGetValue(line.Method, out Method method))
{
- _logger.LogVerbose($"Excluded module: '{module}'");
- continue;
+ documents[doc.Path][line.Class][line.Method].Lines.Add(line.Number, line.Hits);
}
-
- var instrumenter = new Instrumenter(module,
- Identifier,
- _parameters,
- _logger,
- _instrumentationHelper,
- _fileSystem,
- _sourceRootTranslator,
- _cecilSymbolHelper);
-
- if (instrumenter.CanInstrument())
+ else
{
- _instrumentationHelper.BackupOriginalModule(module, Identifier);
-
- // Guard code path and restore if instrumentation fails.
- try
- {
- InstrumenterResult result = instrumenter.Instrument();
- if (!instrumenter.SkipModule)
- {
- _results.Add(result);
- _logger.LogVerbose($"Instrumented module: '{module}'");
- }
- }
- catch (Exception ex)
- {
- _logger.LogWarning($"Unable to instrument module: {module}\n{ex}");
- _instrumentationHelper.RestoreOriginalModule(module, Identifier);
- }
+ documents[doc.Path][line.Class].Add(line.Method, new Method());
+ documents[doc.Path][line.Class][line.Method].Lines.Add(line.Number, line.Hits);
}
+ }
+ else
+ {
+ documents[doc.Path].Add(line.Class, new Methods());
+ documents[doc.Path][line.Class].Add(line.Method, new Method());
+ documents[doc.Path][line.Class][line.Method].Lines.Add(line.Number, line.Hits);
+ }
}
-
- return new CoveragePrepareResult()
+ else
{
- Identifier = Identifier,
- ModuleOrDirectory = _moduleOrAppDirectory,
- Parameters = _parameters,
- Results = _results.ToArray()
- };
- }
-
- public CoverageResult GetCoverageResult()
- {
- CalculateCoverage();
-
- var modules = new Modules();
- foreach (InstrumenterResult result in _results)
- {
- var documents = new Documents();
- foreach (Document doc in result.Documents.Values)
- {
- // Construct Line Results
- foreach (Line line in doc.Lines.Values)
- {
- if (documents.TryGetValue(doc.Path, out Classes classes))
- {
- if (classes.TryGetValue(line.Class, out Methods methods))
- {
- if (methods.TryGetValue(line.Method, out Method method))
- {
- documents[doc.Path][line.Class][line.Method].Lines.Add(line.Number, line.Hits);
- }
- else
- {
- documents[doc.Path][line.Class].Add(line.Method, new Method());
- documents[doc.Path][line.Class][line.Method].Lines.Add(line.Number, line.Hits);
- }
- }
- else
- {
- documents[doc.Path].Add(line.Class, new Methods());
- documents[doc.Path][line.Class].Add(line.Method, new Method());
- documents[doc.Path][line.Class][line.Method].Lines.Add(line.Number, line.Hits);
- }
- }
- else
- {
- documents.Add(doc.Path, new Classes());
- documents[doc.Path].Add(line.Class, new Methods());
- documents[doc.Path][line.Class].Add(line.Method, new Method());
- documents[doc.Path][line.Class][line.Method].Lines.Add(line.Number, line.Hits);
- }
- }
-
- // Construct Branch Results
- foreach (Branch branch in doc.Branches.Values)
- {
- if (documents.TryGetValue(doc.Path, out Classes classes))
- {
- if (classes.TryGetValue(branch.Class, out Methods methods))
- {
- if (methods.TryGetValue(branch.Method, out Method method))
- {
- method.Branches.Add(new BranchInfo
- { Line = branch.Number, Hits = branch.Hits, Offset = branch.Offset, EndOffset = branch.EndOffset, Path = branch.Path, Ordinal = branch.Ordinal }
- );
- }
- else
- {
- documents[doc.Path][branch.Class].Add(branch.Method, new Method());
- documents[doc.Path][branch.Class][branch.Method].Branches.Add(new BranchInfo
- { Line = branch.Number, Hits = branch.Hits, Offset = branch.Offset, EndOffset = branch.EndOffset, Path = branch.Path, Ordinal = branch.Ordinal }
- );
- }
- }
- else
- {
- documents[doc.Path].Add(branch.Class, new Methods());
- documents[doc.Path][branch.Class].Add(branch.Method, new Method());
- documents[doc.Path][branch.Class][branch.Method].Branches.Add(new BranchInfo
- { Line = branch.Number, Hits = branch.Hits, Offset = branch.Offset, EndOffset = branch.EndOffset, Path = branch.Path, Ordinal = branch.Ordinal }
- );
- }
- }
- else
- {
- documents.Add(doc.Path, new Classes());
- documents[doc.Path].Add(branch.Class, new Methods());
- documents[doc.Path][branch.Class].Add(branch.Method, new Method());
- documents[doc.Path][branch.Class][branch.Method].Branches.Add(new BranchInfo
- { Line = branch.Number, Hits = branch.Hits, Offset = branch.Offset, EndOffset = branch.EndOffset, Path = branch.Path, Ordinal = branch.Ordinal }
- );
- }
- }
- }
-
- modules.Add(Path.GetFileName(result.ModulePath), documents);
- _instrumentationHelper.RestoreOriginalModule(result.ModulePath, Identifier);
+ documents.Add(doc.Path, new Classes());
+ documents[doc.Path].Add(line.Class, new Methods());
+ documents[doc.Path][line.Class].Add(line.Method, new Method());
+ documents[doc.Path][line.Class][line.Method].Lines.Add(line.Number, line.Hits);
}
+ }
- // In case of anonymous delegate compiler generate a custom class and passes it as type.method delegate.
- // If in delegate method we've a branches we need to move these to "actual" class/method that use it.
- // We search "method" with same "Line" of closure class method and add missing branches to it,
- // in this way we correctly report missing branch inside compiled generated anonymous delegate.
- List compileGeneratedClassToRemove = null;
- foreach (KeyValuePair module in modules)
+ // Construct Branch Results
+ foreach (Branch branch in doc.Branches.Values)
+ {
+ if (documents.TryGetValue(doc.Path, out Classes classes))
{
- foreach (KeyValuePair document in module.Value)
+ if (classes.TryGetValue(branch.Class, out Methods methods))
+ {
+ if (methods.TryGetValue(branch.Method, out Method method))
{
- foreach (KeyValuePair @class in document.Value)
- {
- // We fix only lamda generated class
- // https://github.com/dotnet/roslyn/blob/master/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNameKind.cs#L18
- if (!@class.Key.Contains("<>c"))
- {
- continue;
- }
-
- foreach (KeyValuePair method in @class.Value)
- {
- foreach (BranchInfo branch in method.Value.Branches)
- {
- if (BranchInCompilerGeneratedClass(method.Key))
- {
- Method actualMethod = GetMethodWithSameLineInSameDocument(document.Value, @class.Key, branch.Line);
-
- if (actualMethod is null)
- {
- continue;
- }
-
- actualMethod.Branches.Add(branch);
-
- if (compileGeneratedClassToRemove is null)
- {
- compileGeneratedClassToRemove = new List();
- }
-
- if (!compileGeneratedClassToRemove.Contains(@class.Key))
- {
- compileGeneratedClassToRemove.Add(@class.Key);
- }
- }
- }
- }
- }
+ method.Branches.Add(new BranchInfo
+ { Line = branch.Number, Hits = branch.Hits, Offset = branch.Offset, EndOffset = branch.EndOffset, Path = branch.Path, Ordinal = branch.Ordinal }
+ );
}
- }
-
- // After method/branches analysis of compiled generated class we can remove noise from reports
- if (compileGeneratedClassToRemove is not null)
- {
- foreach (KeyValuePair module in modules)
+ else
{
- foreach (KeyValuePair document in module.Value)
- {
- foreach (string classToRemove in compileGeneratedClassToRemove)
- {
- document.Value.Remove(classToRemove);
- }
- }
+ documents[doc.Path][branch.Class].Add(branch.Method, new Method());
+ documents[doc.Path][branch.Class][branch.Method].Branches.Add(new BranchInfo
+ { Line = branch.Number, Hits = branch.Hits, Offset = branch.Offset, EndOffset = branch.EndOffset, Path = branch.Path, Ordinal = branch.Ordinal }
+ );
}
+ }
+ else
+ {
+ documents[doc.Path].Add(branch.Class, new Methods());
+ documents[doc.Path][branch.Class].Add(branch.Method, new Method());
+ documents[doc.Path][branch.Class][branch.Method].Branches.Add(new BranchInfo
+ { Line = branch.Number, Hits = branch.Hits, Offset = branch.Offset, EndOffset = branch.EndOffset, Path = branch.Path, Ordinal = branch.Ordinal }
+ );
+ }
}
-
- 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))
+ else
{
- string json = _fileSystem.ReadAllText(_parameters.MergeWith);
- coverageResult.Merge(JsonConvert.DeserializeObject(json));
+ documents.Add(doc.Path, new Classes());
+ documents[doc.Path].Add(branch.Class, new Methods());
+ documents[doc.Path][branch.Class].Add(branch.Method, new Method());
+ documents[doc.Path][branch.Class][branch.Method].Branches.Add(new BranchInfo
+ { Line = branch.Number, Hits = branch.Hits, Offset = branch.Offset, EndOffset = branch.EndOffset, Path = branch.Path, Ordinal = branch.Ordinal }
+ );
}
-
- return coverageResult;
+ }
}
- private bool BranchInCompilerGeneratedClass(string methodName)
+ modules.Add(Path.GetFileName(result.ModulePath), documents);
+ _instrumentationHelper.RestoreOriginalModule(result.ModulePath, Identifier);
+ }
+
+ // In case of anonymous delegate compiler generate a custom class and passes it as type.method delegate.
+ // If in delegate method we've a branches we need to move these to "actual" class/method that use it.
+ // We search "method" with same "Line" of closure class method and add missing branches to it,
+ // in this way we correctly report missing branch inside compiled generated anonymous delegate.
+ List compileGeneratedClassToRemove = null;
+ foreach (KeyValuePair module in modules)
+ {
+ foreach (KeyValuePair document in module.Value)
{
- foreach (InstrumenterResult instrumentedResult in _results)
+ foreach (KeyValuePair @class in document.Value)
+ {
+ // We fix only lamda generated class
+ // https://github.com/dotnet/roslyn/blob/master/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNameKind.cs#L18
+ if (!@class.Key.Contains("<>c"))
{
- if (instrumentedResult.BranchesInCompiledGeneratedClass.Contains(methodName))
- {
- return true;
- }
+ continue;
}
- return false;
- }
- private static Method GetMethodWithSameLineInSameDocument(Classes documentClasses, string compilerGeneratedClassName, int branchLine)
- {
- foreach (KeyValuePair @class in documentClasses)
+ foreach (KeyValuePair method in @class.Value)
{
- if (@class.Key == compilerGeneratedClassName)
+ foreach (BranchInfo branch in method.Value.Branches)
+ {
+ if (BranchInCompilerGeneratedClass(method.Key))
{
+ Method actualMethod = GetMethodWithSameLineInSameDocument(document.Value, @class.Key, branch.Line);
+
+ if (actualMethod is null)
+ {
continue;
- }
+ }
- foreach (KeyValuePair method in @class.Value)
- {
- foreach (KeyValuePair line in method.Value.Lines)
- {
- if (line.Key == branchLine)
- {
- return method.Value;
- }
- }
+ actualMethod.Branches.Add(branch);
+
+ if (compileGeneratedClassToRemove is null)
+ {
+ compileGeneratedClassToRemove = new List();
+ }
+
+ if (!compileGeneratedClassToRemove.Contains(@class.Key))
+ {
+ compileGeneratedClassToRemove.Add(@class.Key);
+ }
}
+ }
}
- return null;
+ }
}
+ }
- private void CalculateCoverage()
+ // After method/branches analysis of compiled generated class we can remove noise from reports
+ if (compileGeneratedClassToRemove is not null)
+ {
+ foreach (KeyValuePair module in modules)
{
- foreach (InstrumenterResult result in _results)
+ foreach (KeyValuePair document in module.Value)
+ {
+ foreach (string classToRemove in compileGeneratedClassToRemove)
{
- 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}'");
- continue;
- }
+ document.Value.Remove(classToRemove);
+ }
+ }
+ }
+ }
- var documents = result.Documents.Values.ToList();
- if (_parameters.UseSourceLink && result.SourceLink != null)
- {
- JToken jObject = JObject.Parse(result.SourceLink)["documents"];
- Dictionary sourceLinkDocuments = JsonConvert.DeserializeObject>(jObject.ToString());
- foreach (Document document in documents)
- {
- document.Path = GetSourceLinkUrl(sourceLinkDocuments, document.Path);
- }
- }
+ var coverageResult = new CoverageResult { Identifier = Identifier, Modules = modules, InstrumentedResults = _results, Parameters = _parameters };
- // Calculate lines to skip for every hits start/end candidate
- // Nested ranges win on outermost one
- foreach (HitCandidate hitCandidate in result.HitCandidates)
- {
- if (hitCandidate.isBranch || hitCandidate.end == hitCandidate.start)
- {
- continue;
- }
-
- foreach (HitCandidate hitCandidateToCompare in result.HitCandidates.Where(x => x.docIndex.Equals(hitCandidate.docIndex)))
- {
- if (hitCandidate != hitCandidateToCompare && !hitCandidateToCompare.isBranch)
- {
- if (hitCandidateToCompare.start > hitCandidate.start &&
- hitCandidateToCompare.end < hitCandidate.end)
- {
- for (int i = hitCandidateToCompare.start;
- i <= (hitCandidateToCompare.end == 0 ? hitCandidateToCompare.start : hitCandidateToCompare.end);
- i++)
- {
- (hitCandidate.AccountedByNestedInstrumentation ??= new HashSet()).Add(i);
- }
- }
- }
- }
- }
+ if (!string.IsNullOrEmpty(_parameters.MergeWith) && !string.IsNullOrWhiteSpace(_parameters.MergeWith))
+ {
+ if (_fileSystem.Exists(_parameters.MergeWith))
+ {
+ _logger.LogInformation($"MergeWith: '{_parameters.MergeWith}'.");
+ string json = _fileSystem.ReadAllText(_parameters.MergeWith);
+ coverageResult.Merge(JsonConvert.DeserializeObject(json));
+ } else
+ {
+ _logger.LogInformation($"MergeWith: file '{_parameters.MergeWith}' does not exist.");
+ }
+ }
- var documentsList = result.Documents.Values.ToList();
- using (Stream fs = _fileSystem.NewFileStream(result.HitsFilePath, FileMode.Open, FileAccess.Read))
- using (var br = new BinaryReader(fs))
- {
- int hitCandidatesCount = br.ReadInt32();
-
- // TODO: hitCandidatesCount should be verified against result.HitCandidates.Count
-
- for (int i = 0; i < hitCandidatesCount; ++i)
- {
- HitCandidate hitLocation = result.HitCandidates[i];
- Document document = documentsList[hitLocation.docIndex];
- int hits = br.ReadInt32();
-
- if (hits == 0)
- continue;
-
- hits = hits < 0 ? int.MaxValue : hits;
-
- if (hitLocation.isBranch)
- {
- Branch branch = document.Branches[new BranchKey(hitLocation.start, hitLocation.end)];
- branch.Hits += hits;
-
- if (branch.Hits < 0)
- branch.Hits = int.MaxValue;
- }
- else
- {
- for (int j = hitLocation.start; j <= hitLocation.end; j++)
- {
- if (hitLocation.AccountedByNestedInstrumentation?.Contains(j) == true)
- {
- continue;
- }
-
- Line line = document.Lines[j];
- line.Hits += hits;
-
- if (line.Hits < 0)
- line.Hits = int.MaxValue;
- }
- }
- }
- }
+ return coverageResult;
+ }
- try
- {
- _instrumentationHelper.DeleteHitsFile(result.HitsFilePath);
- _logger.LogVerbose($"Hit file '{result.HitsFilePath}' deleted");
- }
- catch (Exception ex)
- {
- _logger.LogWarning($"Unable to remove hit file: {result.HitsFilePath} because : {ex.Message}");
- }
- }
+ private bool BranchInCompilerGeneratedClass(string methodName)
+ {
+ foreach (InstrumenterResult instrumentedResult in _results)
+ {
+ if (instrumentedResult.BranchesInCompiledGeneratedClass.Contains(methodName))
+ {
+ return true;
}
+ }
+ return false;
+ }
- private string GetSourceLinkUrl(Dictionary sourceLinkDocuments, string document)
+ private static Method GetMethodWithSameLineInSameDocument(Classes documentClasses, string compilerGeneratedClassName, int branchLine)
+ {
+ foreach (KeyValuePair @class in documentClasses)
+ {
+ if (@class.Key == compilerGeneratedClassName)
+ {
+ continue;
+ }
+
+ foreach (KeyValuePair method in @class.Value)
{
- if (sourceLinkDocuments.TryGetValue(document, out string url))
+ foreach (KeyValuePair line in method.Value.Lines)
+ {
+ if (line.Key == branchLine)
{
- return url;
+ return method.Value;
}
+ }
+ }
+ }
+ return null;
+ }
- string keyWithBestMatch = string.Empty;
- string relativePathOfBestMatch = string.Empty;
+ private void CalculateCoverage()
+ {
+ foreach (InstrumenterResult result in _results)
+ {
+ var documents = result.Documents.Values.ToList();
+ if (_parameters.UseSourceLink && result.SourceLink != null)
+ {
+ JToken jObject = JObject.Parse(result.SourceLink)["documents"];
+ Dictionary sourceLinkDocuments = JsonConvert.DeserializeObject>(jObject.ToString());
+ foreach (Document document in documents)
+ {
+ document.Path = GetSourceLinkUrl(sourceLinkDocuments, document.Path);
+ }
+ }
- foreach (KeyValuePair sourceLinkDocument in sourceLinkDocuments)
- {
- string key = sourceLinkDocument.Key;
- if (Path.GetFileName(key) != "*") continue;
+ 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}'");
+ continue;
+ }
- IReadOnlyList rootMapping = _sourceRootTranslator.ResolvePathRoot(key.Substring(0, key.Length - 1));
- foreach (string keyMapping in rootMapping is null ? new List() { key } : new List(rootMapping.Select(m => m.OriginalPath)))
+ // Calculate lines to skip for every hits start/end candidate
+ // Nested ranges win on outermost one
+ foreach (HitCandidate hitCandidate in result.HitCandidates)
+ {
+ if (hitCandidate.isBranch || hitCandidate.end == hitCandidate.start)
+ {
+ continue;
+ }
+
+ foreach (HitCandidate hitCandidateToCompare in result.HitCandidates.Where(x => x.docIndex.Equals(hitCandidate.docIndex)))
+ {
+ if (hitCandidate != hitCandidateToCompare && !hitCandidateToCompare.isBranch)
+ {
+ if (hitCandidateToCompare.start > hitCandidate.start &&
+ hitCandidateToCompare.end < hitCandidate.end)
+ {
+ for (int i = hitCandidateToCompare.start;
+ i <= (hitCandidateToCompare.end == 0 ? hitCandidateToCompare.start : hitCandidateToCompare.end);
+ i++)
{
- string directoryDocument = Path.GetDirectoryName(document);
- string sourceLinkRoot = Path.GetDirectoryName(keyMapping);
- string relativePath = "";
-
- // if document is on repo root we skip relative path calculation
- if (directoryDocument != sourceLinkRoot)
- {
- if (!directoryDocument.StartsWith(sourceLinkRoot + Path.DirectorySeparatorChar))
- continue;
-
- relativePath = directoryDocument.Substring(sourceLinkRoot.Length + 1);
- }
-
- if (relativePathOfBestMatch.Length == 0)
- {
- keyWithBestMatch = sourceLinkDocument.Key;
- relativePathOfBestMatch = relativePath;
- }
-
- if (relativePath.Length < relativePathOfBestMatch.Length)
- {
- keyWithBestMatch = sourceLinkDocument.Key;
- relativePathOfBestMatch = relativePath;
- }
+ (hitCandidate.AccountedByNestedInstrumentation ??= new HashSet()).Add(i);
}
+ }
}
+ }
+ }
+
+ var documentsList = result.Documents.Values.ToList();
+ using (Stream fs = _fileSystem.NewFileStream(result.HitsFilePath, FileMode.Open, FileAccess.Read))
+ using (var br = new BinaryReader(fs))
+ {
+ int hitCandidatesCount = br.ReadInt32();
- relativePathOfBestMatch = relativePathOfBestMatch == "." ? string.Empty : relativePathOfBestMatch;
+ // TODO: hitCandidatesCount should be verified against result.HitCandidates.Count
- string replacement = Path.Combine(relativePathOfBestMatch, Path.GetFileName(document));
- replacement = replacement.Replace('\\', '/');
+ for (int i = 0; i < hitCandidatesCount; ++i)
+ {
+ HitCandidate hitLocation = result.HitCandidates[i];
+ Document document = documentsList[hitLocation.docIndex];
+ int hits = br.ReadInt32();
- if (sourceLinkDocuments.TryGetValue(keyWithBestMatch, out url))
+ if (hits == 0)
+ continue;
+
+ hits = hits < 0 ? int.MaxValue : hits;
+
+ if (hitLocation.isBranch)
{
- return url.Replace("*", replacement);
+ Branch branch = document.Branches[new BranchKey(hitLocation.start, hitLocation.end)];
+ branch.Hits += hits;
+
+ if (branch.Hits < 0)
+ branch.Hits = int.MaxValue;
+ }
+ else
+ {
+ for (int j = hitLocation.start; j <= hitLocation.end; j++)
+ {
+ if (hitLocation.AccountedByNestedInstrumentation?.Contains(j) == true)
+ {
+ continue;
+ }
+
+ Line line = document.Lines[j];
+ line.Hits += hits;
+
+ if (line.Hits < 0)
+ line.Hits = int.MaxValue;
+ }
}
+ }
+ }
- return document;
+ try
+ {
+ _instrumentationHelper.DeleteHitsFile(result.HitsFilePath);
+ _logger.LogVerbose($"Hit file '{result.HitsFilePath}' deleted");
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning($"Unable to remove hit file: {result.HitsFilePath} because : {ex.Message}");
}
+ }
+ }
+
+ private string GetSourceLinkUrl(Dictionary sourceLinkDocuments, string document)
+ {
+ if (sourceLinkDocuments.TryGetValue(document, out string url))
+ {
+ return url;
+ }
+
+ string keyWithBestMatch = string.Empty;
+ string relativePathOfBestMatch = string.Empty;
+
+ foreach (KeyValuePair sourceLinkDocument in sourceLinkDocuments)
+ {
+ string key = sourceLinkDocument.Key;
+ if (Path.GetFileName(key) != "*") continue;
+
+#pragma warning disable IDE0057 // Use range operator
+ IReadOnlyList rootMapping = _sourceRootTranslator.ResolvePathRoot(key.Substring(0, key.Length - 1));
+#pragma warning restore IDE0057 // Use range operator
+ foreach (string keyMapping in rootMapping is null ? new List() { key } : new List(rootMapping.Select(m => m.OriginalPath)))
+ {
+ string directoryDocument = Path.GetDirectoryName(document);
+ string sourceLinkRoot = Path.GetDirectoryName(keyMapping);
+ string relativePath = "";
+
+ // if document is on repo root we skip relative path calculation
+ if (directoryDocument != sourceLinkRoot)
+ {
+ if (!directoryDocument.StartsWith(sourceLinkRoot + Path.DirectorySeparatorChar))
+ continue;
+
+#pragma warning disable IDE0057 // Use range operator
+ relativePath = directoryDocument.Substring(sourceLinkRoot.Length + 1);
+#pragma warning restore IDE0057 // Use range operator
+ }
+
+ if (relativePathOfBestMatch.Length == 0)
+ {
+ keyWithBestMatch = sourceLinkDocument.Key;
+ relativePathOfBestMatch = relativePath;
+ }
+
+ if (relativePath.Length < relativePathOfBestMatch.Length)
+ {
+ keyWithBestMatch = sourceLinkDocument.Key;
+ relativePathOfBestMatch = relativePath;
+ }
+ }
+ }
+
+ relativePathOfBestMatch = relativePathOfBestMatch == "." ? string.Empty : relativePathOfBestMatch;
+
+ string replacement = Path.Combine(relativePathOfBestMatch, Path.GetFileName(document));
+ replacement = replacement.Replace('\\', '/');
+
+ if (sourceLinkDocuments.TryGetValue(keyWithBestMatch, out url))
+ {
+ return url.Replace("*", replacement);
+ }
+
+ return document;
}
+ }
}
diff --git a/src/coverlet.core/CoverageDetails.cs b/src/coverlet.core/CoverageDetails.cs
index 59db863c2..0359fa11a 100644
--- a/src/coverlet.core/CoverageDetails.cs
+++ b/src/coverlet.core/CoverageDetails.cs
@@ -1,30 +1,30 @@
-// Copyright (c) Toni Solarin-Sodara
+// Copyright (c) Toni Solarin-Sodara
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
namespace Coverlet.Core
{
- internal class CoverageDetails
- {
- private double _averageModulePercent;
+ internal class CoverageDetails
+ {
+ private double _averageModulePercent;
- public Modules Modules { get; internal set; }
- public double Covered { get; internal set; }
- public int Total { get; internal set; }
- public double Percent
- {
- get
- {
- if (Modules?.Count == 0) return 0;
- return Total == 0 ? 100D : Math.Floor((Covered / Total) * 10000) / 100;
- }
- }
+ public Modules Modules { get; internal set; }
+ public double Covered { get; internal set; }
+ public int Total { get; internal set; }
+ public double Percent
+ {
+ get
+ {
+ if (Modules?.Count == 0) return 0;
+ return Total == 0 ? 100D : Math.Floor((Covered / Total) * 10000) / 100;
+ }
+ }
- public double AverageModulePercent
- {
- get { return Math.Floor(_averageModulePercent * 100) / 100; }
- internal set { _averageModulePercent = value; }
- }
+ public double AverageModulePercent
+ {
+ get { return Math.Floor(_averageModulePercent * 100) / 100; }
+ internal set { _averageModulePercent = value; }
}
-}
\ No newline at end of file
+ }
+}
diff --git a/src/coverlet.core/CoveragePrepareResult.cs b/src/coverlet.core/CoveragePrepareResult.cs
index 7c15d76c3..ecfd234cf 100644
--- a/src/coverlet.core/CoveragePrepareResult.cs
+++ b/src/coverlet.core/CoveragePrepareResult.cs
@@ -7,36 +7,36 @@
namespace Coverlet.Core
{
- // Followed safe serializer guide, will emit xml format
- // https://docs.microsoft.com/en-us/visualstudio/code-quality/ca2300-do-not-use-insecure-deserializer-binaryformatter?view=vs-2019
- // https://docs.microsoft.com/en-us/visualstudio/code-quality/ca2301-do-not-call-binaryformatter-deserialize-without-first-setting-binaryformatter-binder?view=vs-2019
- [DataContract]
- internal class CoveragePrepareResult
- {
- [DataMember]
- public string Identifier { get; set; }
- [DataMember]
- public string ModuleOrDirectory { get; set; }
- [DataMember]
- public string MergeWith { get; set; }
- [DataMember]
- public bool UseSourceLink { get; set; }
- [DataMember]
- public InstrumenterResult[] Results { get; set; }
- [DataMember]
- public CoverageParameters Parameters { get; set; }
+ // Followed safe serializer guide, will emit xml format
+ // https://docs.microsoft.com/en-us/visualstudio/code-quality/ca2300-do-not-use-insecure-deserializer-binaryformatter?view=vs-2019
+ // https://docs.microsoft.com/en-us/visualstudio/code-quality/ca2301-do-not-call-binaryformatter-deserialize-without-first-setting-binaryformatter-binder?view=vs-2019
+ [DataContract]
+ internal class CoveragePrepareResult
+ {
+ [DataMember]
+ public string Identifier { get; set; }
+ [DataMember]
+ public string ModuleOrDirectory { get; set; }
+ [DataMember]
+ public string MergeWith { get; set; }
+ [DataMember]
+ public bool UseSourceLink { get; set; }
+ [DataMember]
+ public InstrumenterResult[] Results { get; set; }
+ [DataMember]
+ public CoverageParameters Parameters { get; set; }
- public static CoveragePrepareResult Deserialize(Stream serializedInstrumentState)
- {
- return (CoveragePrepareResult)new DataContractSerializer(typeof(CoveragePrepareResult)).ReadObject(serializedInstrumentState);
- }
+ public static CoveragePrepareResult Deserialize(Stream serializedInstrumentState)
+ {
+ return (CoveragePrepareResult)new DataContractSerializer(typeof(CoveragePrepareResult)).ReadObject(serializedInstrumentState);
+ }
- public static Stream Serialize(CoveragePrepareResult instrumentState)
- {
- var ms = new MemoryStream();
- new DataContractSerializer(typeof(CoveragePrepareResult)).WriteObject(ms, instrumentState);
- ms.Position = 0;
- return ms;
- }
+ public static Stream Serialize(CoveragePrepareResult instrumentState)
+ {
+ var ms = new MemoryStream();
+ new DataContractSerializer(typeof(CoveragePrepareResult)).WriteObject(ms, instrumentState);
+ ms.Position = 0;
+ return ms;
}
+ }
}
diff --git a/src/coverlet.core/CoverageResult.cs b/src/coverlet.core/CoverageResult.cs
index 4e981346b..00a64568c 100644
--- a/src/coverlet.core/CoverageResult.cs
+++ b/src/coverlet.core/CoverageResult.cs
@@ -8,175 +8,174 @@
namespace Coverlet.Core
{
- internal class BranchInfo
+ 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()
{
- 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; }
+ Lines = new Lines();
+ Branches = new Branches();
}
- internal class Lines : SortedDictionary { }
+ public Lines Lines;
+ public Branches Branches;
+ }
+ internal class Methods : Dictionary { }
+ internal class Classes : Dictionary { }
+ internal class Documents : Dictionary { }
+ internal class Modules : Dictionary { }
- internal class Branches : List { }
+ internal class CoverageResult
+ {
+ public string Identifier { get; set; }
+ public Modules Modules { get; set; }
+ public List InstrumentedResults { get; set; }
+ public CoverageParameters Parameters { get; set; }
- internal class Method
+ public CoverageResult() { }
+
+ public void Merge(Modules modules)
{
- internal Method()
+ foreach (KeyValuePair module in modules)
+ {
+ if (!Modules.Keys.Contains(module.Key))
{
- Lines = new Lines();
- Branches = new Branches();
+ Modules.Add(module.Key, module.Value);
}
- 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 { get; set; }
- public Modules Modules { get; set; }
- public List InstrumentedResults { get; set; }
- public CoverageParameters Parameters { get; set; }
-
- public CoverageResult() { }
-
- public void Merge(Modules modules)
+ else
{
- foreach (KeyValuePair module in modules)
+ foreach (KeyValuePair document in module.Value)
+ {
+ if (!Modules[module.Key].ContainsKey(document.Key))
+ {
+ Modules[module.Key].Add(document.Key, document.Value);
+ }
+ else
{
- if (!Modules.Keys.Contains(module.Key))
+ foreach (KeyValuePair @class in document.Value)
+ {
+ if (!Modules[module.Key][document.Key].ContainsKey(@class.Key))
{
- Modules.Add(module.Key, module.Value);
+ Modules[module.Key][document.Key].Add(@class.Key, @class.Value);
}
else
{
- foreach (KeyValuePair document in module.Value)
+ foreach (KeyValuePair method in @class.Value)
+ {
+ if (!Modules[module.Key][document.Key][@class.Key].ContainsKey(method.Key))
+ {
+ Modules[module.Key][document.Key][@class.Key].Add(method.Key, method.Value);
+ }
+ else
{
- if (!Modules[module.Key].ContainsKey(document.Key))
+ foreach (KeyValuePair line in method.Value.Lines)
+ {
+ if (!Modules[module.Key][document.Key][@class.Key][method.Key].Lines.ContainsKey(line.Key))
{
- Modules[module.Key].Add(document.Key, document.Value);
+ Modules[module.Key][document.Key][@class.Key][method.Key].Lines.Add(line.Key, line.Value);
}
else
{
- foreach (KeyValuePair @class in document.Value)
- {
- if (!Modules[module.Key][document.Key].ContainsKey(@class.Key))
- {
- Modules[module.Key][document.Key].Add(@class.Key, @class.Value);
- }
- else
- {
- foreach (KeyValuePair method in @class.Value)
- {
- if (!Modules[module.Key][document.Key][@class.Key].ContainsKey(method.Key))
- {
- Modules[module.Key][document.Key][@class.Key].Add(method.Key, method.Value);
- }
- else
- {
- foreach (KeyValuePair line in method.Value.Lines)
- {
- if (!Modules[module.Key][document.Key][@class.Key][method.Key].Lines.ContainsKey(line.Key))
- {
- Modules[module.Key][document.Key][@class.Key][method.Key].Lines.Add(line.Key, line.Value);
- }
- else
- {
- Modules[module.Key][document.Key][@class.Key][method.Key].Lines[line.Key] += line.Value;
- }
- }
-
- foreach (BranchInfo branch in method.Value.Branches)
- {
- Branches branches = Modules[module.Key][document.Key][@class.Key][method.Key].Branches;
- BranchInfo 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;
- }
- }
- }
- }
- }
+ Modules[module.Key][document.Key][@class.Key][method.Key].Lines[line.Key] += line.Value;
}
+ }
+
+ foreach (BranchInfo branch in method.Value.Branches)
+ {
+ Branches branches = Modules[module.Key][document.Key][@class.Key][method.Key].Branches;
+ BranchInfo 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, Dictionary thresholdTypeFlagValues, ThresholdStatistic thresholdStat)
- {
- ThresholdTypeFlags thresholdTypeFlags = ThresholdTypeFlags.None;
- switch (thresholdStat)
- {
- case ThresholdStatistic.Minimum:
- {
- if (!Modules.Any())
- thresholdTypeFlags = CompareThresholdValues(thresholdTypeFlagValues, thresholdTypeFlags, 0, 0, 0);
-
- foreach (KeyValuePair module in Modules)
- {
- double line = summary.CalculateLineCoverage(module.Value).Percent;
- double branch = summary.CalculateBranchCoverage(module.Value).Percent;
- double method = summary.CalculateMethodCoverage(module.Value).Percent;
-
- thresholdTypeFlags = CompareThresholdValues(thresholdTypeFlagValues, thresholdTypeFlags, line, branch, method);
- }
- }
- break;
- case ThresholdStatistic.Average:
- {
- double line = summary.CalculateLineCoverage(Modules).AverageModulePercent;
- double branch = summary.CalculateBranchCoverage(Modules).AverageModulePercent;
- double method = summary.CalculateMethodCoverage(Modules).AverageModulePercent;
-
- thresholdTypeFlags = CompareThresholdValues(thresholdTypeFlagValues, thresholdTypeFlags, line, branch, method);
- }
- break;
- case ThresholdStatistic.Total:
- {
- double line = summary.CalculateLineCoverage(Modules).Percent;
- double branch = summary.CalculateBranchCoverage(Modules).Percent;
- double method = summary.CalculateMethodCoverage(Modules).Percent;
-
- thresholdTypeFlags = CompareThresholdValues(thresholdTypeFlagValues, thresholdTypeFlags, line, branch, method);
- }
- break;
- }
-
- return thresholdTypeFlags;
- }
-
- private static ThresholdTypeFlags CompareThresholdValues(
- Dictionary thresholdTypeFlagValues, ThresholdTypeFlags thresholdTypeFlags,
- double line, double branch, double method)
- {
- if (thresholdTypeFlagValues.TryGetValue(ThresholdTypeFlags.Line, out double lineThresholdValue) &&
- lineThresholdValue > line)
- {
- thresholdTypeFlags |= ThresholdTypeFlags.Line;
- }
-
- if (thresholdTypeFlagValues.TryGetValue(ThresholdTypeFlags.Branch, out double branchThresholdValue) &&
- branchThresholdValue > branch)
+ public ThresholdTypeFlags GetThresholdTypesBelowThreshold(CoverageSummary summary, Dictionary thresholdTypeFlagValues, ThresholdStatistic thresholdStat)
+ {
+ ThresholdTypeFlags thresholdTypeFlags = ThresholdTypeFlags.None;
+ switch (thresholdStat)
+ {
+ case ThresholdStatistic.Minimum:
+ {
+ if (!Modules.Any())
+ thresholdTypeFlags = CompareThresholdValues(thresholdTypeFlagValues, thresholdTypeFlags, 0, 0, 0);
+
+ foreach (KeyValuePair module in Modules)
{
- thresholdTypeFlags |= ThresholdTypeFlags.Branch;
- }
+ double line = summary.CalculateLineCoverage(module.Value).Percent;
+ double branch = summary.CalculateBranchCoverage(module.Value).Percent;
+ double method = summary.CalculateMethodCoverage(module.Value).Percent;
- if (thresholdTypeFlagValues.TryGetValue(ThresholdTypeFlags.Method, out double methodThresholdValue) &&
- methodThresholdValue > method)
- {
- thresholdTypeFlags |= ThresholdTypeFlags.Method;
+ thresholdTypeFlags = CompareThresholdValues(thresholdTypeFlagValues, thresholdTypeFlags, line, branch, method);
}
+ }
+ break;
+ case ThresholdStatistic.Average:
+ {
+ double line = summary.CalculateLineCoverage(Modules).AverageModulePercent;
+ double branch = summary.CalculateBranchCoverage(Modules).AverageModulePercent;
+ double method = summary.CalculateMethodCoverage(Modules).AverageModulePercent;
+
+ thresholdTypeFlags = CompareThresholdValues(thresholdTypeFlagValues, thresholdTypeFlags, line, branch, method);
+ }
+ break;
+ case ThresholdStatistic.Total:
+ {
+ double line = summary.CalculateLineCoverage(Modules).Percent;
+ double branch = summary.CalculateBranchCoverage(Modules).Percent;
+ double method = summary.CalculateMethodCoverage(Modules).Percent;
+
+ thresholdTypeFlags = CompareThresholdValues(thresholdTypeFlagValues, thresholdTypeFlags, line, branch, method);
+ }
+ break;
+ }
+
+ return thresholdTypeFlags;
+ }
- return thresholdTypeFlags;
- }
+ private static ThresholdTypeFlags CompareThresholdValues(
+ Dictionary thresholdTypeFlagValues, ThresholdTypeFlags thresholdTypeFlags,
+ double line, double branch, double method)
+ {
+ if (thresholdTypeFlagValues.TryGetValue(ThresholdTypeFlags.Line, out double lineThresholdValue) &&
+ lineThresholdValue > line)
+ {
+ thresholdTypeFlags |= ThresholdTypeFlags.Line;
+ }
+
+ if (thresholdTypeFlagValues.TryGetValue(ThresholdTypeFlags.Branch, out double branchThresholdValue) &&
+ branchThresholdValue > branch)
+ {
+ thresholdTypeFlags |= ThresholdTypeFlags.Branch;
+ }
+
+ if (thresholdTypeFlagValues.TryGetValue(ThresholdTypeFlags.Method, out double methodThresholdValue) &&
+ methodThresholdValue > method)
+ {
+ thresholdTypeFlags |= ThresholdTypeFlags.Method;
+ }
+
+ return thresholdTypeFlags;
}
+ }
}
diff --git a/src/coverlet.core/CoverageSummary.cs b/src/coverlet.core/CoverageSummary.cs
index a56cda454..34370074b 100644
--- a/src/coverlet.core/CoverageSummary.cs
+++ b/src/coverlet.core/CoverageSummary.cs
@@ -7,270 +7,270 @@
namespace Coverlet.Core
{
- internal class CoverageSummary
+ internal class CoverageSummary
+ {
+ public CoverageDetails CalculateLineCoverage(Lines lines)
{
- public CoverageDetails CalculateLineCoverage(Lines lines)
- {
- var details = new CoverageDetails();
- details.Covered = lines.Where(l => l.Value > 0).Count();
- details.Total = lines.Count;
- return details;
- }
+ 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 (KeyValuePair method in methods)
- {
- CoverageDetails methodCoverage = CalculateLineCoverage(method.Value.Lines);
- details.Covered += methodCoverage.Covered;
- details.Total += methodCoverage.Total;
- }
- return details;
- }
+ public CoverageDetails CalculateLineCoverage(Methods methods)
+ {
+ var details = new CoverageDetails();
+ foreach (KeyValuePair method in methods)
+ {
+ CoverageDetails 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 (KeyValuePair @class in classes)
- {
- CoverageDetails classCoverage = CalculateLineCoverage(@class.Value);
- details.Covered += classCoverage.Covered;
- details.Total += classCoverage.Total;
- }
- return details;
- }
+ public CoverageDetails CalculateLineCoverage(Classes classes)
+ {
+ var details = new CoverageDetails();
+ foreach (KeyValuePair @class in classes)
+ {
+ CoverageDetails classCoverage = CalculateLineCoverage(@class.Value);
+ details.Covered += classCoverage.Covered;
+ details.Total += classCoverage.Total;
+ }
+ return details;
+ }
- public CoverageDetails CalculateLineCoverage(Documents documents)
- {
- var details = new CoverageDetails();
- foreach (KeyValuePair document in documents)
- {
- CoverageDetails documentCoverage = CalculateLineCoverage(document.Value);
- details.Covered += documentCoverage.Covered;
- details.Total += documentCoverage.Total;
- }
- return details;
- }
+ public CoverageDetails CalculateLineCoverage(Documents documents)
+ {
+ var details = new CoverageDetails();
+ foreach (KeyValuePair document in documents)
+ {
+ CoverageDetails documentCoverage = CalculateLineCoverage(document.Value);
+ details.Covered += documentCoverage.Covered;
+ details.Total += documentCoverage.Total;
+ }
+ return details;
+ }
- public CoverageDetails CalculateLineCoverage(Modules modules)
- {
- var details = new CoverageDetails { Modules = modules };
- double accumPercent = 0.0D;
-
- if (modules.Count == 0)
- return details;
-
- foreach (KeyValuePair module in modules)
- {
- CoverageDetails moduleCoverage = CalculateLineCoverage(module.Value);
- details.Covered += moduleCoverage.Covered;
- details.Total += moduleCoverage.Total;
- accumPercent += moduleCoverage.Percent;
- }
- details.AverageModulePercent = accumPercent / modules.Count;
- return details;
- }
+ public CoverageDetails CalculateLineCoverage(Modules modules)
+ {
+ var details = new CoverageDetails { Modules = modules };
+ double accumPercent = 0.0D;
+
+ if (modules.Count == 0)
+ return details;
+
+ foreach (KeyValuePair module in modules)
+ {
+ CoverageDetails 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 CoverageDetails CalculateBranchCoverage(IList branches)
+ {
+ var details = new CoverageDetails();
+ details.Covered = branches.Count(bi => bi.Hits > 0);
+ details.Total = branches.Count;
+ return details;
+ }
- public int CalculateNpathComplexity(IList branches)
+ public int CalculateNpathComplexity(IList branches)
+ {
+ // Adapted from OpenCover see https://github.com/OpenCover/opencover/blob/master/main/OpenCover.Framework/Persistance/BasePersistance.cs#L419
+ if (!branches.Any())
+ {
+ return 0;
+ }
+
+ var paths = new Dictionary();
+ foreach (BranchInfo branch in branches)
+ {
+ if (!paths.TryGetValue(branch.Offset, out int count))
{
- // Adapted from OpenCover see https://github.com/OpenCover/opencover/blob/master/main/OpenCover.Framework/Persistance/BasePersistance.cs#L419
- if (!branches.Any())
- {
- return 0;
- }
-
- var paths = new Dictionary();
- foreach (BranchInfo branch in branches)
- {
- if (!paths.TryGetValue(branch.Offset, out int count))
- {
- count = 0;
- }
- paths[branch.Offset] = ++count;
- }
-
- int npath = 1;
- foreach (int branchPoints in paths.Values)
- {
- try
- {
- npath = checked(npath * branchPoints);
- }
- catch (OverflowException)
- {
- npath = int.MaxValue;
- break;
- }
- }
- return npath;
+ count = 0;
}
+ paths[branch.Offset] = ++count;
+ }
- public int CalculateCyclomaticComplexity(IList branches)
+ int npath = 1;
+ foreach (int branchPoints in paths.Values)
+ {
+ try
{
- return Math.Max(1, branches.Count);
+ npath = checked(npath * branchPoints);
}
-
- public int CalculateCyclomaticComplexity(Methods methods)
+ catch (OverflowException)
{
- return methods.Values.Select(m => CalculateCyclomaticComplexity(m.Branches)).Sum();
+ npath = int.MaxValue;
+ break;
}
+ }
+ return npath;
+ }
- public int CalculateMaxCyclomaticComplexity(Methods methods)
- {
- return methods.Values.Select(m => CalculateCyclomaticComplexity(m.Branches)).DefaultIfEmpty(1).Max();
- }
+ public int CalculateCyclomaticComplexity(IList branches)
+ {
+ return Math.Max(1, branches.Count);
+ }
- public int CalculateMinCyclomaticComplexity(Methods methods)
- {
- return methods.Values.Select(m => CalculateCyclomaticComplexity(m.Branches)).DefaultIfEmpty(1).Min();
- }
+ public int CalculateCyclomaticComplexity(Methods methods)
+ {
+ return methods.Values.Select(m => CalculateCyclomaticComplexity(m.Branches)).Sum();
+ }
- public int CalculateCyclomaticComplexity(Modules modules)
- {
- return modules.Values.Select(CalculateCyclomaticComplexity).Sum();
- }
+ public int CalculateMaxCyclomaticComplexity(Methods methods)
+ {
+ return methods.Values.Select(m => CalculateCyclomaticComplexity(m.Branches)).DefaultIfEmpty(1).Max();
+ }
- public int CalculateMaxCyclomaticComplexity(Modules modules)
- {
- return modules.Values.Select(CalculateCyclomaticComplexity).DefaultIfEmpty(1).Max();
- }
+ public int CalculateMinCyclomaticComplexity(Methods methods)
+ {
+ return methods.Values.Select(m => CalculateCyclomaticComplexity(m.Branches)).DefaultIfEmpty(1).Min();
+ }
- public int CalculateMinCyclomaticComplexity(Modules modules)
- {
- return modules.Values.Select(CalculateCyclomaticComplexity).DefaultIfEmpty(1).Min();
- }
+ public int CalculateCyclomaticComplexity(Modules modules)
+ {
+ return modules.Values.Select(CalculateCyclomaticComplexity).Sum();
+ }
- public int CalculateCyclomaticComplexity(Documents documents)
- {
- return documents.Values.SelectMany(c => c.Values.Select(CalculateCyclomaticComplexity)).Sum();
- }
+ public int CalculateMaxCyclomaticComplexity(Modules modules)
+ {
+ return modules.Values.Select(CalculateCyclomaticComplexity).DefaultIfEmpty(1).Max();
+ }
- public CoverageDetails CalculateBranchCoverage(Methods methods)
- {
- var details = new CoverageDetails();
- foreach (KeyValuePair method in methods)
- {
- CoverageDetails methodCoverage = CalculateBranchCoverage(method.Value.Branches);
- details.Covered += methodCoverage.Covered;
- details.Total += methodCoverage.Total;
- }
- return details;
- }
+ public int CalculateMinCyclomaticComplexity(Modules modules)
+ {
+ return modules.Values.Select(CalculateCyclomaticComplexity).DefaultIfEmpty(1).Min();
+ }
- public CoverageDetails CalculateBranchCoverage(Classes classes)
- {
- var details = new CoverageDetails();
- foreach (KeyValuePair @class in classes)
- {
- CoverageDetails classCoverage = CalculateBranchCoverage(@class.Value);
- details.Covered += classCoverage.Covered;
- details.Total += classCoverage.Total;
- }
- return details;
- }
+ public int CalculateCyclomaticComplexity(Documents documents)
+ {
+ return documents.Values.SelectMany(c => c.Values.Select(CalculateCyclomaticComplexity)).Sum();
+ }
- public CoverageDetails CalculateBranchCoverage(Documents documents)
- {
- var details = new CoverageDetails();
- foreach (KeyValuePair document in documents)
- {
- CoverageDetails documentCoverage = CalculateBranchCoverage(document.Value);
- details.Covered += documentCoverage.Covered;
- details.Total += documentCoverage.Total;
- }
- return details;
- }
+ public CoverageDetails CalculateBranchCoverage(Methods methods)
+ {
+ var details = new CoverageDetails();
+ foreach (KeyValuePair method in methods)
+ {
+ CoverageDetails methodCoverage = CalculateBranchCoverage(method.Value.Branches);
+ details.Covered += methodCoverage.Covered;
+ details.Total += methodCoverage.Total;
+ }
+ return details;
+ }
- public CoverageDetails CalculateBranchCoverage(Modules modules)
- {
- var details = new CoverageDetails { Modules = modules };
- double accumPercent = 0.0D;
-
- if (modules.Count == 0)
- return details;
-
- foreach (KeyValuePair module in modules)
- {
- CoverageDetails moduleCoverage = CalculateBranchCoverage(module.Value);
- details.Covered += moduleCoverage.Covered;
- details.Total += moduleCoverage.Total;
- accumPercent += moduleCoverage.Percent;
- }
- details.AverageModulePercent = modules.Count == 0 ? 0 : accumPercent / modules.Count;
- return details;
- }
+ public CoverageDetails CalculateBranchCoverage(Classes classes)
+ {
+ var details = new CoverageDetails();
+ foreach (KeyValuePair @class in classes)
+ {
+ CoverageDetails classCoverage = CalculateBranchCoverage(@class.Value);
+ details.Covered += classCoverage.Covered;
+ details.Total += classCoverage.Total;
+ }
+ 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 CalculateBranchCoverage(Documents documents)
+ {
+ var details = new CoverageDetails();
+ foreach (KeyValuePair document in documents)
+ {
+ CoverageDetails documentCoverage = CalculateBranchCoverage(document.Value);
+ details.Covered += documentCoverage.Covered;
+ details.Total += documentCoverage.Total;
+ }
+ return details;
+ }
- public CoverageDetails CalculateMethodCoverage(Methods methods)
- {
- var details = new CoverageDetails();
- IEnumerable> methodsWithLines = methods.Where(m => m.Value.Lines.Count > 0);
- foreach (KeyValuePair method in methodsWithLines)
- {
- CoverageDetails methodCoverage = CalculateMethodCoverage(method.Value.Lines);
- details.Covered += methodCoverage.Covered;
- }
- details.Total = methodsWithLines.Count();
- return details;
- }
+ public CoverageDetails CalculateBranchCoverage(Modules modules)
+ {
+ var details = new CoverageDetails { Modules = modules };
+ double accumPercent = 0.0D;
+
+ if (modules.Count == 0)
+ return details;
+
+ foreach (KeyValuePair module in modules)
+ {
+ CoverageDetails moduleCoverage = CalculateBranchCoverage(module.Value);
+ details.Covered += moduleCoverage.Covered;
+ details.Total += moduleCoverage.Total;
+ accumPercent += moduleCoverage.Percent;
+ }
+ details.AverageModulePercent = modules.Count == 0 ? 0 : accumPercent / modules.Count;
+ return details;
+ }
- public CoverageDetails CalculateMethodCoverage(Classes classes)
- {
- var details = new CoverageDetails();
- foreach (KeyValuePair @class in classes)
- {
- CoverageDetails classCoverage = CalculateMethodCoverage(@class.Value);
- details.Covered += classCoverage.Covered;
- details.Total += classCoverage.Total;
- }
- 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(Documents documents)
- {
- var details = new CoverageDetails();
- foreach (KeyValuePair document in documents)
- {
- CoverageDetails documentCoverage = CalculateMethodCoverage(document.Value);
- details.Covered += documentCoverage.Covered;
- details.Total += documentCoverage.Total;
- }
- return details;
- }
+ public CoverageDetails CalculateMethodCoverage(Methods methods)
+ {
+ var details = new CoverageDetails();
+ IEnumerable> methodsWithLines = methods.Where(m => m.Value.Lines.Count > 0);
+ foreach (KeyValuePair method in methodsWithLines)
+ {
+ CoverageDetails methodCoverage = CalculateMethodCoverage(method.Value.Lines);
+ details.Covered += methodCoverage.Covered;
+ }
+ details.Total = methodsWithLines.Count();
+ return details;
+ }
- public CoverageDetails CalculateMethodCoverage(Modules modules)
- {
- var details = new CoverageDetails { Modules = modules };
- double accumPercent = 0.0D;
-
- if (modules.Count == 0)
- return details;
-
- foreach (KeyValuePair module in modules)
- {
- CoverageDetails moduleCoverage = CalculateMethodCoverage(module.Value);
- details.Covered += moduleCoverage.Covered;
- details.Total += moduleCoverage.Total;
- accumPercent += moduleCoverage.Percent;
- }
- details.AverageModulePercent = modules.Count == 0 ? 0 : accumPercent / modules.Count;
- return details;
- }
+ public CoverageDetails CalculateMethodCoverage(Classes classes)
+ {
+ var details = new CoverageDetails();
+ foreach (KeyValuePair @class in classes)
+ {
+ CoverageDetails classCoverage = CalculateMethodCoverage(@class.Value);
+ details.Covered += classCoverage.Covered;
+ details.Total += classCoverage.Total;
+ }
+ return details;
+ }
+
+ public CoverageDetails CalculateMethodCoverage(Documents documents)
+ {
+ var details = new CoverageDetails();
+ foreach (KeyValuePair document in documents)
+ {
+ CoverageDetails documentCoverage = CalculateMethodCoverage(document.Value);
+ details.Covered += documentCoverage.Covered;
+ details.Total += documentCoverage.Total;
+ }
+ return details;
+ }
+
+ public CoverageDetails CalculateMethodCoverage(Modules modules)
+ {
+ var details = new CoverageDetails { Modules = modules };
+ double accumPercent = 0.0D;
+
+ if (modules.Count == 0)
+ return details;
+
+ foreach (KeyValuePair module in modules)
+ {
+ CoverageDetails moduleCoverage = CalculateMethodCoverage(module.Value);
+ details.Covered += moduleCoverage.Covered;
+ details.Total += moduleCoverage.Total;
+ accumPercent += moduleCoverage.Percent;
+ }
+ details.AverageModulePercent = modules.Count == 0 ? 0 : accumPercent / modules.Count;
+ return details;
}
+ }
}
diff --git a/src/coverlet.core/Enums/AssemblySearchType.cs b/src/coverlet.core/Enums/AssemblySearchType.cs
index 099e54217..4eef8b96f 100644
--- a/src/coverlet.core/Enums/AssemblySearchType.cs
+++ b/src/coverlet.core/Enums/AssemblySearchType.cs
@@ -3,10 +3,10 @@
namespace Coverlet.Core.Enums
{
- internal enum AssemblySearchType
- {
- MissingAny,
- MissingAll,
- None
- }
+ internal enum AssemblySearchType
+ {
+ MissingAny,
+ MissingAll,
+ None
+ }
}
diff --git a/src/coverlet.core/Enums/ThresholdStatistic.cs b/src/coverlet.core/Enums/ThresholdStatistic.cs
index 1dbb55dc0..31aa72cd9 100644
--- a/src/coverlet.core/Enums/ThresholdStatistic.cs
+++ b/src/coverlet.core/Enums/ThresholdStatistic.cs
@@ -1,12 +1,12 @@
-// Copyright (c) Toni Solarin-Sodara
+// Copyright (c) Toni Solarin-Sodara
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Coverlet.Core.Enums
{
- internal enum ThresholdStatistic
- {
- Minimum,
- Average,
- Total
- }
-}
\ No newline at end of file
+ internal enum ThresholdStatistic
+ {
+ Minimum,
+ Average,
+ Total
+ }
+}
diff --git a/src/coverlet.core/Enums/ThresholdTypeFlags.cs b/src/coverlet.core/Enums/ThresholdTypeFlags.cs
index 9b24222a5..2463fab9a 100644
--- a/src/coverlet.core/Enums/ThresholdTypeFlags.cs
+++ b/src/coverlet.core/Enums/ThresholdTypeFlags.cs
@@ -1,16 +1,16 @@
-// Copyright (c) Toni Solarin-Sodara
+// Copyright (c) Toni Solarin-Sodara
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
namespace Coverlet.Core.Enums
{
- [Flags]
- internal enum ThresholdTypeFlags
- {
- None = 0,
- Line = 2,
- Branch = 4,
- Method = 8
- }
-}
\ No newline at end of file
+ [Flags]
+ internal enum ThresholdTypeFlags
+ {
+ None = 0,
+ Line = 2,
+ Branch = 4,
+ Method = 8
+ }
+}
diff --git a/src/coverlet.core/Exceptions.cs b/src/coverlet.core/Exceptions.cs
index 4eefd76ca..d65b22096 100644
--- a/src/coverlet.core/Exceptions.cs
+++ b/src/coverlet.core/Exceptions.cs
@@ -5,25 +5,25 @@
namespace Coverlet.Core.Exceptions
{
- [Serializable]
- internal class CoverletException : Exception
- {
- public CoverletException() { }
- public CoverletException(string message) : base(message) { }
- public CoverletException(string message, System.Exception inner) : base(message, inner) { }
- protected CoverletException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
- }
+ [Serializable]
+ public class CoverletException : Exception
+ {
+ public CoverletException() { }
+ public CoverletException(string message) : base(message) { }
+ public CoverletException(string message, System.Exception inner) : base(message, inner) { }
+ protected CoverletException(
+ System.Runtime.Serialization.SerializationInfo info,
+ System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
+ }
- [Serializable]
- internal class CecilAssemblyResolutionException : CoverletException
- {
- public CecilAssemblyResolutionException() { }
- public CecilAssemblyResolutionException(string message) : base(message) { }
- public CecilAssemblyResolutionException(string message, System.Exception inner) : base(message, inner) { }
- protected CecilAssemblyResolutionException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
- }
+ [Serializable]
+ internal class CecilAssemblyResolutionException : CoverletException
+ {
+ public CecilAssemblyResolutionException() { }
+ public CecilAssemblyResolutionException(string message) : base(message) { }
+ public CecilAssemblyResolutionException(string message, System.Exception inner) : base(message, inner) { }
+ protected CecilAssemblyResolutionException(
+ System.Runtime.Serialization.SerializationInfo info,
+ System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
+ }
}
diff --git a/src/coverlet.core/Extensions/HelperExtensions.cs b/src/coverlet.core/Extensions/HelperExtensions.cs
index 30439c0c9..191e9cc38 100644
--- a/src/coverlet.core/Extensions/HelperExtensions.cs
+++ b/src/coverlet.core/Extensions/HelperExtensions.cs
@@ -6,13 +6,13 @@
namespace Coverlet.Core.Extensions
{
- internal static class HelperExtensions
+ internal static class HelperExtensions
+ {
+ [ExcludeFromCoverage]
+ public static TRet Maybe(this T value, Func action, TRet defValue = default)
+ where T : class
{
- [ExcludeFromCoverage]
- public static TRet Maybe(this T value, Func action, TRet defValue = default)
- where T : class
- {
- return (value != null) ? action(value) : defValue;
- }
+ return (value != null) ? action(value) : defValue;
}
+ }
}
diff --git a/src/coverlet.core/Helpers/AssemblyAdapter.cs b/src/coverlet.core/Helpers/AssemblyAdapter.cs
index f4626d2ff..124c11a9f 100644
--- a/src/coverlet.core/Helpers/AssemblyAdapter.cs
+++ b/src/coverlet.core/Helpers/AssemblyAdapter.cs
@@ -6,11 +6,11 @@
namespace Coverlet.Core.Helpers
{
- internal class AssemblyAdapter : IAssemblyAdapter
+ internal class AssemblyAdapter : IAssemblyAdapter
+ {
+ public string GetAssemblyName(string assemblyPath)
{
- public string GetAssemblyName(string assemblyPath)
- {
- return AssemblyName.GetAssemblyName(assemblyPath).Name;
- }
+ return AssemblyName.GetAssemblyName(assemblyPath).Name;
}
+ }
}
diff --git a/src/coverlet.core/Helpers/Console.cs b/src/coverlet.core/Helpers/Console.cs
index 8781b49de..4652a194a 100644
--- a/src/coverlet.core/Helpers/Console.cs
+++ b/src/coverlet.core/Helpers/Console.cs
@@ -6,11 +6,11 @@
namespace Coverlet.Core.Helpers
{
- public class SystemConsole : IConsole
+ public class SystemConsole : IConsole
+ {
+ public void WriteLine(string value)
{
- public void WriteLine(string value)
- {
- Console.WriteLine(value);
- }
+ Console.WriteLine(value);
}
+ }
}
diff --git a/src/coverlet.core/Helpers/FileSystem.cs b/src/coverlet.core/Helpers/FileSystem.cs
index 10dfc8f0a..e8af5b947 100644
--- a/src/coverlet.core/Helpers/FileSystem.cs
+++ b/src/coverlet.core/Helpers/FileSystem.cs
@@ -6,61 +6,61 @@
namespace Coverlet.Core.Helpers
{
- internal class FileSystem : IFileSystem
+ internal class FileSystem : IFileSystem
+ {
+ // We need to partial mock this method on tests
+ public virtual bool Exists(string path)
{
- // We need to partial mock this method on tests
- public virtual bool Exists(string path)
- {
- return File.Exists(path);
- }
+ return File.Exists(path);
+ }
- public void WriteAllText(string path, string contents)
- {
- File.WriteAllText(path, contents);
- }
+ public void WriteAllText(string path, string contents)
+ {
+ File.WriteAllText(path, contents);
+ }
- public string ReadAllText(string path)
- {
- return File.ReadAllText(path);
- }
+ public string ReadAllText(string path)
+ {
+ return File.ReadAllText(path);
+ }
- // We need to partial mock this method on tests
- public virtual Stream OpenRead(string path)
- {
- return File.OpenRead(path);
- }
+ // We need to partial mock this method on tests
+ public virtual Stream OpenRead(string path)
+ {
+ return File.OpenRead(path);
+ }
- public void Copy(string sourceFileName, string destFileName, bool overwrite)
- {
- File.Copy(sourceFileName, destFileName, overwrite);
- }
+ public void Copy(string sourceFileName, string destFileName, bool overwrite)
+ {
+ File.Copy(sourceFileName, destFileName, overwrite);
+ }
- public void Delete(string path)
- {
- File.Delete(path);
- }
+ public void Delete(string path)
+ {
+ File.Delete(path);
+ }
- // We need to partial mock this method on tests
- public virtual Stream NewFileStream(string path, FileMode mode)
- {
- return new FileStream(path, mode);
- }
+ // We need to partial mock this method on tests
+ public virtual Stream NewFileStream(string path, FileMode mode)
+ {
+ return new FileStream(path, mode);
+ }
- // We need to partial mock this method on tests
- public virtual Stream NewFileStream(string path, FileMode mode, FileAccess access)
- {
- return new FileStream(path, mode, access);
- }
+ // We need to partial mock this method on tests
+ public virtual Stream NewFileStream(string path, FileMode mode, FileAccess access)
+ {
+ return new FileStream(path, mode, access);
+ }
- public string[] ReadAllLines(string path)
- {
- return File.ReadAllLines(path);
- }
+ public string[] ReadAllLines(string path)
+ {
+ return File.ReadAllLines(path);
+ }
- // Escape format characters in file names
- internal static string EscapeFileName(string fileName)
- {
- return fileName?.Replace("{", "{{").Replace("}", "}}");
- }
+ // Escape format characters in file names
+ internal static string EscapeFileName(string fileName)
+ {
+ return fileName?.Replace("{", "{{").Replace("}", "}}");
}
+ }
}
diff --git a/src/coverlet.core/Helpers/InstrumentationHelper.cs b/src/coverlet.core/Helpers/InstrumentationHelper.cs
index 6723fc733..7ae12972e 100644
--- a/src/coverlet.core/Helpers/InstrumentationHelper.cs
+++ b/src/coverlet.core/Helpers/InstrumentationHelper.cs
@@ -16,477 +16,500 @@
namespace Coverlet.Core.Helpers
{
- internal class InstrumentationHelper : IInstrumentationHelper
+ internal class InstrumentationHelper : IInstrumentationHelper
+ {
+ private const int RetryAttempts = 12;
+ private readonly ConcurrentDictionary _backupList = new();
+ private readonly IRetryHelper _retryHelper;
+ private readonly IFileSystem _fileSystem;
+ private readonly ISourceRootTranslator _sourceRootTranslator;
+ private ILogger _logger;
+ private static readonly RegexOptions s_regexOptions =
+ RegexOptions.Multiline | RegexOptions.Compiled;
+
+ public InstrumentationHelper(IProcessExitHandler processExitHandler, IRetryHelper retryHelper, IFileSystem fileSystem, ILogger logger, ISourceRootTranslator sourceRootTranslator)
{
- private const int RetryAttempts = 12;
- private readonly ConcurrentDictionary _backupList = new();
- private readonly IRetryHelper _retryHelper;
- private readonly IFileSystem _fileSystem;
- private readonly ISourceRootTranslator _sourceRootTranslator;
- private ILogger _logger;
-
- public InstrumentationHelper(IProcessExitHandler processExitHandler, IRetryHelper retryHelper, IFileSystem fileSystem, ILogger logger, ISourceRootTranslator sourceRootTranslator)
- {
- processExitHandler.Add((s, e) => RestoreOriginalModules());
- _retryHelper = retryHelper;
- _fileSystem = fileSystem;
- _logger = logger;
- _sourceRootTranslator = sourceRootTranslator;
- }
+ processExitHandler.Add((s, e) => RestoreOriginalModules());
+ _retryHelper = retryHelper;
+ _fileSystem = fileSystem;
+ _logger = logger;
+ _sourceRootTranslator = sourceRootTranslator;
+ }
- public string[] GetCoverableModules(string moduleOrAppDirectory, string[] directories, bool includeTestAssembly)
- {
- Debug.Assert(directories != null);
- Debug.Assert(moduleOrAppDirectory != null);
+ public string[] GetCoverableModules(string moduleOrAppDirectory, string[] directories, bool includeTestAssembly)
+ {
+ Debug.Assert(directories != null);
+ Debug.Assert(moduleOrAppDirectory != null);
- bool isAppDirectory = !File.Exists(moduleOrAppDirectory) && Directory.Exists(moduleOrAppDirectory);
- string moduleDirectory = isAppDirectory ? moduleOrAppDirectory : Path.GetDirectoryName(moduleOrAppDirectory);
+ bool isAppDirectory = !File.Exists(moduleOrAppDirectory) && Directory.Exists(moduleOrAppDirectory);
+ string moduleDirectory = isAppDirectory ? moduleOrAppDirectory : Path.GetDirectoryName(moduleOrAppDirectory);
- if (moduleDirectory == string.Empty)
- {
- moduleDirectory = Directory.GetCurrentDirectory();
- }
+ if (moduleDirectory == string.Empty)
+ {
+ moduleDirectory = Directory.GetCurrentDirectory();
+ }
- var dirs = new List()
+ var dirs = new List()
{
// Add the test assembly's directory.
moduleDirectory
};
- // Prepare all the directories we probe for modules.
- foreach (string directory in directories)
- {
- if (string.IsNullOrWhiteSpace(directory)) continue;
+ // Prepare all the directories we probe for modules.
+ foreach (string directory in directories)
+ {
+ if (string.IsNullOrWhiteSpace(directory)) continue;
- string fullPath = (!Path.IsPathRooted(directory)
- ? Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), directory))
- : directory).TrimEnd('*');
+ string fullPath = (!Path.IsPathRooted(directory)
+ ? Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), directory))
+ : directory).TrimEnd('*');
- if (!Directory.Exists(fullPath)) continue;
+ if (!Directory.Exists(fullPath)) continue;
- if (directory.EndsWith("*", StringComparison.Ordinal))
- dirs.AddRange(Directory.GetDirectories(fullPath));
- else
- dirs.Add(fullPath);
- }
+ if (directory.EndsWith("*", StringComparison.Ordinal))
+ dirs.AddRange(Directory.GetDirectories(fullPath));
+ else
+ dirs.Add(fullPath);
+ }
- // The module's name must be unique.
- var uniqueModules = new HashSet();
+ // The module's name must be unique.
+ var uniqueModules = new HashSet();
- if (!includeTestAssembly && !isAppDirectory)
- uniqueModules.Add(Path.GetFileName(moduleOrAppDirectory));
+ if (!includeTestAssembly && !isAppDirectory)
+ uniqueModules.Add(Path.GetFileName(moduleOrAppDirectory));
- return dirs.SelectMany(d => Directory.EnumerateFiles(d))
- .Where(m => IsAssembly(m) && uniqueModules.Add(Path.GetFileName(m)))
- .ToArray();
- }
+ return dirs.SelectMany(d => Directory.EnumerateFiles(d))
+ .Where(m => IsAssembly(m) && uniqueModules.Add(Path.GetFileName(m)))
+ .ToArray();
+ }
- public bool HasPdb(string module, out bool embedded)
+ public bool HasPdb(string module, out bool embedded)
+ {
+ embedded = false;
+ using Stream moduleStream = _fileSystem.OpenRead(module);
+ using var peReader = new PEReader(moduleStream);
+ foreach (DebugDirectoryEntry entry in peReader.ReadDebugDirectory())
+ {
+ if (entry.Type == DebugDirectoryEntryType.CodeView)
{
+ CodeViewDebugDirectoryData codeViewData = peReader.ReadCodeViewDebugDirectoryData(entry);
+ string modulePdbFileName = $"{Path.GetFileNameWithoutExtension(module)}.pdb";
+ if (_sourceRootTranslator.ResolveFilePath(codeViewData.Path) == modulePdbFileName)
+ {
+ // PDB is embedded
+ embedded = true;
+ return true;
+ }
+
+ if (_fileSystem.Exists(_sourceRootTranslator.ResolveFilePath(codeViewData.Path)))
+ {
+ // local PDB is located within original build location
embedded = false;
- using Stream moduleStream = _fileSystem.OpenRead(module);
- using var peReader = new PEReader(moduleStream);
- foreach (DebugDirectoryEntry entry in peReader.ReadDebugDirectory())
- {
- if (entry.Type == DebugDirectoryEntryType.CodeView)
- {
- CodeViewDebugDirectoryData codeViewData = peReader.ReadCodeViewDebugDirectoryData(entry);
- string modulePdbFileName = $"{Path.GetFileNameWithoutExtension(module)}.pdb";
- if (_sourceRootTranslator.ResolveFilePath(codeViewData.Path) == modulePdbFileName)
- {
- // PDB is embedded
- embedded = true;
- return true;
- }
-
- if (_fileSystem.Exists(_sourceRootTranslator.ResolveFilePath(codeViewData.Path)))
- {
- // local PDB is located within original build location
- embedded = false;
- return true;
- }
-
- string localPdbFileName = Path.Combine(Path.GetDirectoryName(module), modulePdbFileName);
- if (_fileSystem.Exists(localPdbFileName))
- {
- // local PDB is located within same folder as module
- embedded = false;
-
- // mapping need to be registered in _sourceRootTranslator to use that discovery
- _sourceRootTranslator.AddMappingInCache(codeViewData.Path, localPdbFileName);
-
- return true;
- }
- }
- }
+ return true;
+ }
- return false;
- }
+ string localPdbFileName = Path.Combine(Path.GetDirectoryName(module), modulePdbFileName);
+ if (_fileSystem.Exists(localPdbFileName))
+ {
+ // local PDB is located within same folder as module
+ embedded = false;
+
+ // mapping need to be registered in _sourceRootTranslator to use that discovery
+ _sourceRootTranslator.AddMappingInCache(codeViewData.Path, localPdbFileName);
- public bool EmbeddedPortablePdbHasLocalSource(string module, AssemblySearchType excludeAssembliesWithoutSources)
- {
- using Stream moduleStream = _fileSystem.OpenRead(module);
- using var peReader = new PEReader(moduleStream);
- foreach (DebugDirectoryEntry entry in peReader.ReadDebugDirectory())
- {
- if (entry.Type == DebugDirectoryEntryType.EmbeddedPortablePdb)
- {
- using MetadataReaderProvider embeddedMetadataProvider = peReader.ReadEmbeddedPortablePdbDebugDirectoryData(entry);
- MetadataReader metadataReader = embeddedMetadataProvider.GetMetadataReader();
-
- if (!MatchDocumentsWithSources(module, excludeAssembliesWithoutSources, metadataReader))
- {
- return false;
- }
- }
- }
-
- // If we don't have EmbeddedPortablePdb entry return true, for instance empty dll
- // We should call this method only on embedded pdb module
return true;
+ }
}
+ }
+
+ return false;
+ }
- public bool PortablePdbHasLocalSource(string module, AssemblySearchType excludeAssembliesWithoutSources)
+ public bool EmbeddedPortablePdbHasLocalSource(string module, AssemblySearchType excludeAssembliesWithoutSources)
+ {
+ using Stream moduleStream = _fileSystem.OpenRead(module);
+ using var peReader = new PEReader(moduleStream);
+ foreach (DebugDirectoryEntry entry in peReader.ReadDebugDirectory())
+ {
+ if (entry.Type == DebugDirectoryEntryType.EmbeddedPortablePdb)
{
- using Stream moduleStream = _fileSystem.OpenRead(module);
- using var peReader = new PEReader(moduleStream);
- foreach (DebugDirectoryEntry entry in peReader.ReadDebugDirectory())
- {
- if (entry.Type == DebugDirectoryEntryType.CodeView)
- {
- CodeViewDebugDirectoryData codeViewData = peReader.ReadCodeViewDebugDirectoryData(entry);
- using Stream pdbStream = _fileSystem.OpenRead(_sourceRootTranslator.ResolveFilePath(codeViewData.Path));
- using var metadataReaderProvider = MetadataReaderProvider.FromPortablePdbStream(pdbStream);
- MetadataReader metadataReader = null;
- try
- {
- metadataReader = metadataReaderProvider.GetMetadataReader();
- }
- catch (BadImageFormatException)
- {
- _logger.LogWarning($"{nameof(BadImageFormatException)} during MetadataReaderProvider.FromPortablePdbStream in InstrumentationHelper.PortablePdbHasLocalSource, unable to check if module has got local source.");
- return true;
- }
-
- if (!MatchDocumentsWithSources(module, excludeAssembliesWithoutSources, metadataReader))
- {
- return false;
- }
- }
- }
+ using MetadataReaderProvider embeddedMetadataProvider = peReader.ReadEmbeddedPortablePdbDebugDirectoryData(entry);
+ MetadataReader metadataReader = embeddedMetadataProvider.GetMetadataReader();
- return true;
+ if (!MatchDocumentsWithSources(module, excludeAssembliesWithoutSources, metadataReader))
+ {
+ return false;
+ }
}
+ }
- private bool MatchDocumentsWithSources(string module, AssemblySearchType excludeAssembliesWithoutSources,
- MetadataReader metadataReader)
- {
- if (excludeAssembliesWithoutSources.Equals(AssemblySearchType.MissingAll))
- {
- bool anyDocumentMatches = MatchDocumentsWithSourcesMissingAll(metadataReader);
- if (!anyDocumentMatches)
- {
- _logger.LogVerbose($"Excluding module from instrumentation: {module}, pdb without any local source files");
- return false;
- }
- }
-
- if (excludeAssembliesWithoutSources.Equals(AssemblySearchType.MissingAny))
- {
- (bool allDocumentsMatch, string notFoundDocument) = MatchDocumentsWithSourcesMissingAny(metadataReader);
-
- if (!allDocumentsMatch)
- {
- _logger.LogVerbose(
- $"Excluding module from instrumentation: {module}, pdb without local source files, [{FileSystem.EscapeFileName(notFoundDocument)}]");
- return false;
- }
- }
+ // If we don't have EmbeddedPortablePdb entry return true, for instance empty dll
+ // We should call this method only on embedded pdb module
+ return true;
+ }
+ public bool PortablePdbHasLocalSource(string module, AssemblySearchType excludeAssembliesWithoutSources)
+ {
+ using Stream moduleStream = _fileSystem.OpenRead(module);
+ using var peReader = new PEReader(moduleStream);
+ foreach (DebugDirectoryEntry entry in peReader.ReadDebugDirectory())
+ {
+ if (entry.Type == DebugDirectoryEntryType.CodeView)
+ {
+ CodeViewDebugDirectoryData codeViewData = peReader.ReadCodeViewDebugDirectoryData(entry);
+ using Stream pdbStream = _fileSystem.OpenRead(_sourceRootTranslator.ResolveFilePath(codeViewData.Path));
+ using var metadataReaderProvider = MetadataReaderProvider.FromPortablePdbStream(pdbStream);
+ MetadataReader metadataReader = null;
+ try
+ {
+ metadataReader = metadataReaderProvider.GetMetadataReader();
+ }
+ catch (BadImageFormatException)
+ {
+ _logger.LogWarning($"{nameof(BadImageFormatException)} during MetadataReaderProvider.FromPortablePdbStream in InstrumentationHelper.PortablePdbHasLocalSource, unable to check if module has got local source.");
return true;
- }
+ }
- private IEnumerable<(string documentName, bool documentExists)> DocumentSourceMap(MetadataReader metadataReader)
- {
- return metadataReader.Documents.Select(docHandle =>
- {
- Document document = metadataReader.GetDocument(docHandle);
- string docName = _sourceRootTranslator.ResolveFilePath(metadataReader.GetString(document.Name));
- return (docName, _fileSystem.Exists(docName));
- });
+ if (!MatchDocumentsWithSources(module, excludeAssembliesWithoutSources, metadataReader))
+ {
+ return false;
+ }
}
+ }
- private bool MatchDocumentsWithSourcesMissingAll(MetadataReader metadataReader)
- {
- return DocumentSourceMap(metadataReader).Any(x => x.documentExists);
- }
+ return true;
+ }
- private (bool allDocumentsMatch, string notFoundDocument) MatchDocumentsWithSourcesMissingAny(
- MetadataReader metadataReader)
+ private bool MatchDocumentsWithSources(string module, AssemblySearchType excludeAssembliesWithoutSources,
+ MetadataReader metadataReader)
+ {
+ if (excludeAssembliesWithoutSources.Equals(AssemblySearchType.MissingAll))
+ {
+ bool anyDocumentMatches = MatchDocumentsWithSourcesMissingAll(metadataReader);
+ if (!anyDocumentMatches)
{
- var documentSourceMap = DocumentSourceMap(metadataReader).ToList();
+ _logger.LogVerbose($"Excluding module from instrumentation: {module}, pdb without any local source files");
+ return false;
+ }
+ }
- if (documentSourceMap.Any(x => !x.documentExists))
- return (false, documentSourceMap.FirstOrDefault(x => !x.documentExists).documentName);
+ if (excludeAssembliesWithoutSources.Equals(AssemblySearchType.MissingAny))
+ {
+ (bool allDocumentsMatch, string notFoundDocument) = MatchDocumentsWithSourcesMissingAny(metadataReader);
- return (true, string.Empty);
+ if (!allDocumentsMatch)
+ {
+ _logger.LogVerbose(
+ $"Excluding module from instrumentation: {module}, pdb without local source files, [{FileSystem.EscapeFileName(notFoundDocument)}]");
+ return false;
}
+ }
- public void BackupOriginalModule(string module, string identifier)
- {
- string backupPath = GetBackupPath(module, identifier);
- string backupSymbolPath = Path.ChangeExtension(backupPath, ".pdb");
- _fileSystem.Copy(module, backupPath, true);
- if (!_backupList.TryAdd(module, backupPath))
- {
- throw new ArgumentException($"Key already added '{module}'");
- }
+ return true;
+ }
- string symbolFile = Path.ChangeExtension(module, ".pdb");
- if (_fileSystem.Exists(symbolFile))
- {
- _fileSystem.Copy(symbolFile, backupSymbolPath, true);
- if (!_backupList.TryAdd(symbolFile, backupSymbolPath))
- {
- throw new ArgumentException($"Key already added '{module}'");
- }
- }
- }
+ private IEnumerable<(string documentName, bool documentExists)> DocumentSourceMap(MetadataReader metadataReader)
+ {
+ return metadataReader.Documents.Select(docHandle =>
+ {
+ Document document = metadataReader.GetDocument(docHandle);
+ string docName = _sourceRootTranslator.ResolveFilePath(metadataReader.GetString(document.Name));
+ return (docName, _fileSystem.Exists(docName));
+ });
+ }
- public virtual void RestoreOriginalModule(string module, string identifier)
- {
- string backupPath = GetBackupPath(module, identifier);
- string backupSymbolPath = Path.ChangeExtension(backupPath, ".pdb");
+ private bool MatchDocumentsWithSourcesMissingAll(MetadataReader metadataReader)
+ {
+ return DocumentSourceMap(metadataReader).Any(x => x.documentExists);
+ }
- // Restore the original module - retry up to 10 times, since the destination file could be locked
- // See: https://github.com/tonerdo/coverlet/issues/25
- Func retryStrategy = CreateRetryStrategy();
+ private (bool allDocumentsMatch, string notFoundDocument) MatchDocumentsWithSourcesMissingAny(
+ MetadataReader metadataReader)
+ {
+ var documentSourceMap = DocumentSourceMap(metadataReader).ToList();
- _retryHelper.Retry(() =>
- {
- _fileSystem.Copy(backupPath, module, true);
- _fileSystem.Delete(backupPath);
- _backupList.TryRemove(module, out string _);
- }, retryStrategy, RetryAttempts);
+ if (documentSourceMap.Any(x => !x.documentExists))
+ return (false, documentSourceMap.FirstOrDefault(x => !x.documentExists).documentName);
- _retryHelper.Retry(() =>
- {
- if (_fileSystem.Exists(backupSymbolPath))
- {
- string symbolFile = Path.ChangeExtension(module, ".pdb");
- _fileSystem.Copy(backupSymbolPath, symbolFile, true);
- _fileSystem.Delete(backupSymbolPath);
- _backupList.TryRemove(symbolFile, out string _);
- }
- }, retryStrategy, RetryAttempts);
- }
+ return (true, string.Empty);
+ }
- public virtual void RestoreOriginalModules()
+ public void BackupOriginalModule(string module, string identifier)
+ {
+ string backupPath = GetBackupPath(module, identifier);
+ string backupSymbolPath = Path.ChangeExtension(backupPath, ".pdb");
+ _fileSystem.Copy(module, backupPath, true);
+ if (!_backupList.TryAdd(module, backupPath))
+ {
+ throw new ArgumentException($"Key already added '{module}'");
+ }
+
+ string symbolFile = Path.ChangeExtension(module, ".pdb");
+ if (_fileSystem.Exists(symbolFile))
+ {
+ _fileSystem.Copy(symbolFile, backupSymbolPath, true);
+ if (!_backupList.TryAdd(symbolFile, backupSymbolPath))
{
- // Restore the original module - retry up to 10 times, since the destination file could be locked
- // See: https://github.com/tonerdo/coverlet/issues/25
- Func retryStrategy = CreateRetryStrategy();
-
- foreach (string key in _backupList.Keys.ToList())
- {
- string backupPath = _backupList[key];
- _retryHelper.Retry(() =>
- {
- _fileSystem.Copy(backupPath, key, true);
- _fileSystem.Delete(backupPath);
- _backupList.TryRemove(key, out string _);
- }, retryStrategy, RetryAttempts);
- }
+ throw new ArgumentException($"Key already added '{module}'");
}
+ }
+ }
- public void DeleteHitsFile(string path)
+ public virtual void RestoreOriginalModule(string module, string identifier)
+ {
+ string backupPath = GetBackupPath(module, identifier);
+ string backupSymbolPath = Path.ChangeExtension(backupPath, ".pdb");
+
+ // Restore the original module - retry up to 10 times, since the destination file could be locked
+ // See: https://github.com/tonerdo/coverlet/issues/25
+ Func retryStrategy = CreateRetryStrategy();
+
+ _retryHelper.Retry(() =>
+ {
+ _fileSystem.Copy(backupPath, module, true);
+ _fileSystem.Delete(backupPath);
+ _backupList.TryRemove(module, out string _);
+ }, retryStrategy, RetryAttempts);
+
+ _retryHelper.Retry(() =>
+ {
+ if (_fileSystem.Exists(backupSymbolPath))
{
- Func retryStrategy = CreateRetryStrategy();
- _retryHelper.Retry(() => _fileSystem.Delete(path), retryStrategy, RetryAttempts);
+ string symbolFile = Path.ChangeExtension(module, ".pdb");
+ _fileSystem.Copy(backupSymbolPath, symbolFile, true);
+ _fileSystem.Delete(backupSymbolPath);
+ _backupList.TryRemove(symbolFile, out string _);
}
+ }, retryStrategy, RetryAttempts);
+ }
- public bool IsValidFilterExpression(string filter)
+ public virtual void RestoreOriginalModules()
+ {
+ // Restore the original module - retry up to 10 times, since the destination file could be locked
+ // See: https://github.com/tonerdo/coverlet/issues/25
+ Func retryStrategy = CreateRetryStrategy();
+
+ foreach (string key in _backupList.Keys.ToList())
+ {
+ string backupPath = _backupList[key];
+ _retryHelper.Retry(() =>
{
- if (filter == null)
- return false;
-
- if (!filter.StartsWith("["))
- return false;
+ _fileSystem.Copy(backupPath, key, true);
+ _fileSystem.Delete(backupPath);
+ _backupList.TryRemove(key, out string _);
+ }, retryStrategy, RetryAttempts);
+ }
+ }
- if (!filter.Contains("]"))
- return false;
+ public void DeleteHitsFile(string path)
+ {
+ Func retryStrategy = CreateRetryStrategy();
+ _retryHelper.Retry(() => _fileSystem.Delete(path), retryStrategy, RetryAttempts);
+ }
- if (filter.Count(f => f == '[') > 1)
- return false;
+ public bool IsValidFilterExpression(string filter)
+ {
+ if (filter == null)
+ return false;
- if (filter.Count(f => f == ']') > 1)
- return false;
+ if (!filter.StartsWith("["))
+ return false;
- if (filter.IndexOf(']') < filter.IndexOf('['))
- return false;
+ if (!filter.Contains("]"))
+ return false;
- if (filter.IndexOf(']') - filter.IndexOf('[') == 1)
- return false;
+ if (filter.Count(f => f == '[') > 1)
+ return false;
- if (filter.EndsWith("]"))
- return false;
+ if (filter.Count(f => f == ']') > 1)
+ return false;
- if (new Regex(@"[^\w*]").IsMatch(filter.Replace(".", "").Replace("?", "").Replace("[", "").Replace("]", "")))
- return false;
+ if (filter.IndexOf(']') < filter.IndexOf('['))
+ return false;
- return true;
- }
+ if (filter.IndexOf(']') - filter.IndexOf('[') == 1)
+ return false;
- public bool IsModuleExcluded(string module, string[] excludeFilters)
- {
- if (excludeFilters == null || excludeFilters.Length == 0)
- return false;
+ if (filter.EndsWith("]"))
+ return false;
- module = Path.GetFileNameWithoutExtension(module);
- if (module == null)
- return false;
+ if (new Regex(@"[^\w*]", s_regexOptions, TimeSpan.FromSeconds(10)).IsMatch(filter.Replace(".", "").Replace("?", "").Replace("[", "").Replace("]", "")))
+ return false;
- foreach (string filter in excludeFilters)
- {
- string typePattern = filter.Substring(filter.IndexOf(']') + 1);
+ return true;
+ }
- if (typePattern != "*")
- continue;
+ public IEnumerable SelectModules(IEnumerable modules, string[] includeFilters, string[] excludeFilters)
+ {
+ const char escapeSymbol = '!';
+ ILookup modulesLookup = modules.Where(x => x != null)
+ .ToLookup(x => $"{escapeSymbol}{Path.GetFileNameWithoutExtension(x)}{escapeSymbol}");
- string modulePattern = filter.Substring(1, filter.IndexOf(']') - 1);
- modulePattern = WildcardToRegex(modulePattern);
+ string moduleKeys = string.Join(Environment.NewLine, modulesLookup.Select(x => x.Key));
+ string includedModuleKeys = GetModuleKeysForIncludeFilters(includeFilters, escapeSymbol, moduleKeys);
+ string excludedModuleKeys = GetModuleKeysForExcludeFilters(excludeFilters, escapeSymbol, includedModuleKeys);
- var regex = new Regex(modulePattern);
+ IEnumerable moduleKeysToInclude = includedModuleKeys
+ .Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)
+ .Except(excludedModuleKeys.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries));
- if (regex.IsMatch(module))
- return true;
- }
+ return moduleKeysToInclude.SelectMany(x => modulesLookup[x]);
+ }
- return false;
- }
+ private string GetModuleKeysForIncludeFilters(IEnumerable filters, char escapeSymbol, string moduleKeys)
+ {
+ string[] validFilters = GetValidFilters(filters);
- public bool IsModuleIncluded(string module, string[] includeFilters)
- {
- if (includeFilters == null || includeFilters.Length == 0)
- return true;
+ return !validFilters.Any() ? moduleKeys : GetIncludeModuleKeysForValidFilters(escapeSymbol, moduleKeys, validFilters);
+ }
- module = Path.GetFileNameWithoutExtension(module);
- if (module == null)
- return false;
+ private string GetModuleKeysForExcludeFilters(IEnumerable filters, char escapeSymbol, string moduleKeys)
+ {
+ string[] validFilters = GetValidFilters(filters);
- foreach (string filter in includeFilters)
- {
- string modulePattern = filter.Substring(1, filter.IndexOf(']') - 1);
+ return !validFilters.Any() ? string.Empty : GetExcludeModuleKeysForValidFilters(escapeSymbol, moduleKeys, validFilters);
+ }
- if (modulePattern == "*")
- return true;
+ private string[] GetValidFilters(IEnumerable filters)
+ {
+ return (filters ?? Array.Empty())
+ .Where(IsValidFilterExpression)
+ .Where(x => x.EndsWith("*"))
+ .ToArray();
+ }
- modulePattern = WildcardToRegex(modulePattern);
+ private static string GetExcludeModuleKeysForValidFilters(char escapeSymbol, string moduleKeys, string[] validFilters)
+ {
+ string pattern = CreateRegexExcludePattern(validFilters, escapeSymbol);
+ IEnumerable matches = Regex.Matches(moduleKeys, pattern, RegexOptions.IgnoreCase).Cast();
- var regex = new Regex(modulePattern);
+ return string.Join(
+ Environment.NewLine,
+ matches.Where(x => x.Success).Select(x => x.Groups[0].Value));
+ }
- if (regex.IsMatch(module))
- return true;
- }
+ private static string GetIncludeModuleKeysForValidFilters(char escapeSymbol, string moduleKeys, string[] validFilters)
+ {
+ string pattern = CreateRegexIncludePattern(validFilters, escapeSymbol);
+ IEnumerable matches = Regex.Matches(moduleKeys, pattern, RegexOptions.IgnoreCase).Cast();
- return false;
- }
+ return string.Join(
+ Environment.NewLine,
+ matches.Where(x => x.Success).Select(x => x.Groups[0].Value));
+ }
- public bool IsTypeExcluded(string module, string type, string[] excludeFilters)
- {
- if (excludeFilters == null || excludeFilters.Length == 0)
- return false;
+ private static string CreateRegexExcludePattern(IEnumerable filters, char escapeSymbol)
+ //only look for module filters here, types will be filtered out when instrumenting
+ => CreateRegexPattern(filters, escapeSymbol, filter => filter.Substring(filter.IndexOf(']') + 1) == "*");
- module = Path.GetFileNameWithoutExtension(module);
- if (module == null)
- return false;
+ private static string CreateRegexIncludePattern(IEnumerable filters, char escapeSymbol) =>
+ CreateRegexPattern(filters, escapeSymbol);
- return IsTypeFilterMatch(module, type, excludeFilters);
- }
+ private static string CreateRegexPattern(IEnumerable filters, char escapeSymbol, Func filterPredicate = null)
+ {
+ IEnumerable filteredFilters = filterPredicate != null ? filters.Where(filterPredicate) : filters;
+ IEnumerable regexPatterns = filteredFilters.Select(x =>
+ $"{escapeSymbol}{WildcardToRegex(x.Substring(1, x.IndexOf(']') - 1)).Trim('^', '$')}{escapeSymbol}");
+ return string.Join("|", regexPatterns);
+ }
- public bool IsTypeIncluded(string module, string type, string[] includeFilters)
- {
- if (includeFilters == null || includeFilters.Length == 0)
- return true;
+ public bool IsTypeExcluded(string module, string type, string[] excludeFilters)
+ {
+ if (excludeFilters == null || excludeFilters.Length == 0)
+ return false;
- module = Path.GetFileNameWithoutExtension(module);
- if (module == null)
- return true;
+ module = Path.GetFileNameWithoutExtension(module);
+ if (module == null)
+ return false;
- return IsTypeFilterMatch(module, type, includeFilters);
- }
+ return IsTypeFilterMatch(module, type, excludeFilters);
+ }
- public bool IsLocalMethod(string method)
- => new Regex(WildcardToRegex("<*>*__*|*")).IsMatch(method);
+ public bool IsTypeIncluded(string module, string type, string[] includeFilters)
+ {
+ if (includeFilters == null || includeFilters.Length == 0)
+ return true;
- public void SetLogger(ILogger logger)
- {
- _logger = logger;
- }
+ module = Path.GetFileNameWithoutExtension(module);
+ if (module == null)
+ return true;
- private static bool IsTypeFilterMatch(string module, string type, string[] filters)
- {
- Debug.Assert(module != null);
- Debug.Assert(filters != null);
+ return IsTypeFilterMatch(module, type, includeFilters);
+ }
- foreach (string filter in filters)
- {
- string typePattern = filter.Substring(filter.IndexOf(']') + 1);
- string modulePattern = filter.Substring(1, filter.IndexOf(']') - 1);
+ public bool IsLocalMethod(string method)
+ => Regex.IsMatch(method, WildcardToRegex("<*>*__*|*"));
- typePattern = WildcardToRegex(typePattern);
- modulePattern = WildcardToRegex(modulePattern);
+ public void SetLogger(ILogger logger)
+ {
+ _logger = logger;
+ }
- if (new Regex(typePattern).IsMatch(type) && new Regex(modulePattern).IsMatch(module))
- return true;
- }
+ private static bool IsTypeFilterMatch(string module, string type, string[] filters)
+ {
+ Debug.Assert(module != null);
+ Debug.Assert(filters != null);
- return false;
- }
+ foreach (string filter in filters)
+ {
+#pragma warning disable IDE0057 // Use range operator
+ string typePattern = filter.Substring(filter.IndexOf(']') + 1);
+ string modulePattern = filter.Substring(1, filter.IndexOf(']') - 1);
+#pragma warning restore IDE0057 // Use range operator
- private static string GetBackupPath(string module, string identifier)
- {
- return Path.Combine(
- Path.GetTempPath(),
- Path.GetFileNameWithoutExtension(module) + "_" + identifier + ".dll"
- );
- }
+ typePattern = WildcardToRegex(typePattern);
+ modulePattern = WildcardToRegex(modulePattern);
- private Func CreateRetryStrategy(int initialSleepSeconds = 6)
- {
- TimeSpan retryStrategy()
- {
- var sleep = TimeSpan.FromMilliseconds(initialSleepSeconds);
- initialSleepSeconds *= 2;
- return sleep;
- }
+ if (Regex.IsMatch(type, typePattern) && Regex.IsMatch(module, modulePattern))
+ return true;
+ }
- return retryStrategy;
- }
+ return false;
+ }
- private static string WildcardToRegex(string pattern)
- {
- return "^" + Regex.Escape(pattern).
- Replace("\\*", ".*").
- Replace("\\?", "?") + "$";
- }
+ private static string GetBackupPath(string module, string identifier)
+ {
+ return Path.Combine(
+ Path.GetTempPath(),
+ Path.GetFileNameWithoutExtension(module) + "_" + identifier + ".dll"
+ );
+ }
- private static bool IsAssembly(string filePath)
- {
- Debug.Assert(filePath != null);
+ private Func CreateRetryStrategy(int initialSleepSeconds = 6)
+ {
+ TimeSpan retryStrategy()
+ {
+ var sleep = TimeSpan.FromMilliseconds(initialSleepSeconds);
+ initialSleepSeconds *= 2;
+ return sleep;
+ }
+
+ return retryStrategy;
+ }
- if (!(filePath.EndsWith(".exe") || filePath.EndsWith(".dll")))
- return false;
+ private static string WildcardToRegex(string pattern)
+ {
+ return "^" + Regex.Escape(pattern).
+ Replace("\\*", ".*").
+ Replace("\\?", "?") + "$";
+ }
- try
- {
- AssemblyName.GetAssemblyName(filePath);
- return true;
- }
- catch
- {
- return false;
- }
- }
+ private static bool IsAssembly(string filePath)
+ {
+ Debug.Assert(filePath != null);
+
+ if (!(filePath.EndsWith(".exe") || filePath.EndsWith(".dll")))
+ return false;
+
+ try
+ {
+ AssemblyName.GetAssemblyName(filePath);
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
}
+ }
}
diff --git a/src/coverlet.core/Helpers/ProcessExitHandler.cs b/src/coverlet.core/Helpers/ProcessExitHandler.cs
index 1e570f07f..e941405d3 100644
--- a/src/coverlet.core/Helpers/ProcessExitHandler.cs
+++ b/src/coverlet.core/Helpers/ProcessExitHandler.cs
@@ -6,11 +6,11 @@
namespace Coverlet.Core.Helpers
{
- internal class ProcessExitHandler : IProcessExitHandler
+ internal class ProcessExitHandler : IProcessExitHandler
+ {
+ public void Add(EventHandler handler)
{
- public void Add(EventHandler handler)
- {
- AppDomain.CurrentDomain.ProcessExit += handler;
- }
+ AppDomain.CurrentDomain.ProcessExit += handler;
}
+ }
}
diff --git a/src/coverlet.core/Helpers/RetryHelper.cs b/src/coverlet.core/Helpers/RetryHelper.cs
index dcf3fa9d0..f28361dac 100644
--- a/src/coverlet.core/Helpers/RetryHelper.cs
+++ b/src/coverlet.core/Helpers/RetryHelper.cs
@@ -8,58 +8,58 @@
namespace Coverlet.Core.Helpers
{
- // A slightly amended version of the code found here: https://stackoverflow.com/a/1563234/186184
- // This code allows for varying backoff strategies through the use of Func.
- internal class RetryHelper : IRetryHelper
+ // A slightly amended version of the code found here: https://stackoverflow.com/a/1563234/186184
+ // This code allows for varying backoff strategies through the use of Func.
+ internal class RetryHelper : IRetryHelper
+ {
+ ///
+ /// Retry a void method.
+ ///
+ /// The action to perform
+ /// A function returning a Timespan defining the backoff strategy to use.
+ /// The maximum number of retries before bailing out. Defaults to 3.
+ public void Retry(
+ Action action,
+ Func backoffStrategy,
+ int maxAttemptCount = 3)
{
- ///
- /// Retry a void method.
- ///
- /// The action to perform
- /// A function returning a Timespan defining the backoff strategy to use.
- /// The maximum number of retries before bailing out. Defaults to 3.
- public void Retry(
- Action action,
- Func backoffStrategy,
- int maxAttemptCount = 3)
+ Do