diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 000000000..1227fcc9b
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,215 @@
+# To learn more about .editorconfig see https://aka.ms/editorconfigdocs
+
+# top-most EditorConfig file
+root = true
+
+###############################
+# Core EditorConfig Options #
+###############################
+# All files
+[*]
+insert_final_newline = true
+indent_style = space
+indent_size = 4
+trim_trailing_whitespace = true
+
+# XML project files
+[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
+indent_size = 2
+charset = utf-8
+
+# XML config files
+[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]
+indent_size = 2
+
+# Xml build files
+[*.builds]
+indent_size = 2
+
+# Xml files
+[*.{xml,stylecop,resx,ruleset}]
+indent_size = 2
+
+# YAML config files
+[*.{yml,yaml}]
+indent_size = 2
+
+# Shell scripts
+[*.sh]
+end_of_line = lf
+[*.{cmd,bat}]
+end_of_line = crlf
+
+# Code files
+[*.{cs,csx,vb,vbx}]
+indent_size = 4
+insert_final_newline = true
+charset = utf-8-bom
+###############################
+# .NET Coding Conventions #
+###############################
+[*.{cs,vb}]
+# Organize usings
+dotnet_sort_system_directives_first = true
+## IDE0005: Using directive is unnecessary.
+dotnet_diagnostic.IDE0005.severity = warning
+# 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
+dotnet_diagnostic.IDE0073.severity = warning
+# this. preferences
+dotnet_style_qualification_for_field = false:warning
+dotnet_style_qualification_for_property = false:warning
+dotnet_style_qualification_for_method = false:warning
+dotnet_style_qualification_for_event = false:warning
+# Language keywords vs BCL types preferences
+dotnet_style_predefined_type_for_locals_parameters_members = true:silent
+dotnet_style_predefined_type_for_member_access = true:silent
+# Parentheses preferences
+dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
+dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
+dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
+dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
+# Modifier preferences
+dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
+dotnet_style_readonly_field = true:warning
+## IDE0044: Add readonly modifier
+dotnet_diagnostic.IDE0044.severity = warning
+# Expression-level preferences
+dotnet_style_object_initializer = true:suggestion
+dotnet_style_collection_initializer = true:suggestion
+dotnet_style_explicit_tuple_names = true:suggestion
+dotnet_style_null_propagation = true:suggestion
+dotnet_style_coalesce_expression = true:suggestion
+dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning
+dotnet_style_prefer_inferred_tuple_names = true:suggestion
+dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
+dotnet_style_prefer_auto_properties = true:warning
+dotnet_style_prefer_conditional_expression_over_assignment = true:silent
+dotnet_style_prefer_conditional_expression_over_return = true:silent
+# CA1805: Do not initialize unnecessarily
+dotnet_diagnostic.CA1805.severity = warning
+# IDE0063: Use simple 'using' statement
+dotnet_diagnostic.IDE0063.severity = warning
+# IDE0057: Use range operator
+dotnet_diagnostic.IDE0057.severity = warning
+# IDE0075: Simplify conditional expression
+dotnet_diagnostic.IDE0075.severity = warning
+# IDE0071: Simplify interpolation
+dotnet_diagnostic.IDE0071.severity = warning
+# CA1829: Use Length/Count property instead of Count() when available
+dotnet_diagnostic.CA1829.severity = warning
+# CA1827: Do not use Count() or LongCount() when Any() can be used
+dotnet_diagnostic.CA1827.severity = warning
+###############################
+# Naming Conventions #
+###############################
+# Name all constant fields using PascalCase
+dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
+dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
+dotnet_naming_symbols.constant_fields.applicable_kinds = field
+dotnet_naming_symbols.constant_fields.required_modifiers = const
+dotnet_naming_style.pascal_case_style.capitalization = pascal_case
+# Static fields should have s_ prefix
+dotnet_naming_rule.static_fields_should_have_prefix.severity = suggestion
+dotnet_naming_rule.static_fields_should_have_prefix.symbols = static_fields
+dotnet_naming_rule.static_fields_should_have_prefix.style = static_prefix_style
+dotnet_naming_symbols.static_fields.applicable_kinds = field
+dotnet_naming_symbols.static_fields.required_modifiers = static
+dotnet_naming_symbols.static_fields.applicable_accessibilities = private, internal, private_protected
+dotnet_naming_style.static_prefix_style.required_prefix = s_
+dotnet_naming_style.static_prefix_style.capitalization = camel_case
+# Internal and private fields should be _camelCase
+dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion
+dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields
+dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style
+dotnet_naming_symbols.private_internal_fields.applicable_kinds = field
+dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal
+dotnet_naming_style.camel_case_underscore_style.required_prefix = _
+dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case
+# IDE1006: Naming Styles
+dotnet_diagnostic.IDE1006.severity = warning
+# IDE0090: Use 'new(...)'
+dotnet_diagnostic.IDE0090.severity = warning
+###############################
+# C# Coding Conventions #
+###############################
+[*.cs]
+# Organize usings
+csharp_using_directive_placement = outside_namespace:warning
+# 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
+csharp_style_var_elsewhere = false:warning
+# Expression-bodied members
+csharp_style_expression_bodied_methods = false:silent
+csharp_style_expression_bodied_constructors = false:silent
+csharp_style_expression_bodied_operators = false:silent
+csharp_style_expression_bodied_properties = true:silent
+csharp_style_expression_bodied_indexers = true:silent
+csharp_style_expression_bodied_accessors = true:silent
+# Pattern matching preferences
+csharp_style_pattern_matching_over_is_with_cast_check = true:warning
+csharp_style_pattern_matching_over_as_with_null_check = true:warning
+csharp_style_prefer_not_pattern = true
+## IDE0083: Use pattern matching
+dotnet_diagnostic.IDE0083.severity = warning
+# Null-checking preferences
+csharp_style_throw_expression = true:warning
+csharp_style_conditional_delegate_call = true:warning
+# Modifier preferences
+csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:warning
+# Expression-level preferences
+csharp_prefer_braces = true:silent
+csharp_style_deconstructed_variable_declaration = true:suggestion
+csharp_prefer_simple_default_expression = true:warning
+## IDE0034: Simplify 'default' expression
+dotnet_diagnostic.IDE0034.severity = warning
+csharp_style_pattern_local_over_anonymous_function = true:suggestion
+csharp_style_inlined_variable_declaration = true:warning
+###############################
+# C# Formatting Rules #
+###############################
+# New line preferences
+csharp_new_line_before_open_brace = all
+csharp_new_line_before_else = true
+csharp_new_line_before_catch = true
+csharp_new_line_before_finally = true
+csharp_new_line_before_members_in_object_initializers = true
+csharp_new_line_before_members_in_anonymous_types = true
+csharp_new_line_between_query_expression_clauses = true
+dotnet_style_allow_multiple_blank_lines_experimental=false:warning
+# Indentation preferences
+csharp_indent_case_contents = true
+csharp_indent_switch_labels = true
+csharp_indent_block_contents = true
+csharp_indent_braces = false
+csharp_indent_case_contents_when_block = true
+csharp_indent_labels = one_less_than_current
+# Space preferences
+csharp_space_after_cast = false
+csharp_space_after_colon_in_inheritance_clause = true
+csharp_space_after_comma = true
+csharp_space_after_dot = false
+csharp_space_after_keywords_in_control_flow_statements = true
+csharp_space_after_semicolon_in_for_statement = true
+csharp_space_around_binary_operators = before_and_after
+csharp_space_around_declaration_statements = do_not_ignore
+csharp_space_before_colon_in_inheritance_clause = true
+csharp_space_before_comma = false
+csharp_space_before_dot = false
+csharp_space_before_open_square_brackets = false
+csharp_space_before_semicolon_in_for_statement = false
+csharp_space_between_empty_square_brackets = false
+csharp_space_between_method_call_empty_parameter_list_parentheses = false
+csharp_space_between_method_call_name_and_opening_parenthesis = false
+csharp_space_between_method_call_parameter_list_parentheses = false
+csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
+csharp_space_between_method_declaration_name_and_open_parenthesis = false
+csharp_space_between_method_declaration_parameter_list_parentheses = false
+csharp_space_between_parentheses = false
+csharp_space_between_square_brackets = false
+# Wrapping preferences
+csharp_preserve_single_line_statements = true
+csharp_preserve_single_line_blocks = true
diff --git a/DeterministicBuild.targets b/DeterministicBuild.targets
index 78052937d..13208d103 100644
--- a/DeterministicBuild.targets
+++ b/DeterministicBuild.targets
@@ -9,7 +9,7 @@ https://github.com/dotnet/sourcelink/issues/572 -->
-
+
true
snupkg
true
+ true
+ preview
+ true
preview
- $(NoWarn);NU5105
+ $(NoWarn);NU5105
https://api.nuget.org/v3/index.json;
diff --git a/Directory.Build.targets b/Directory.Build.targets
index f6cd1b61d..d970b1531 100644
--- a/Directory.Build.targets
+++ b/Directory.Build.targets
@@ -4,13 +4,12 @@
-
-
+
-
+
-
+
@@ -22,8 +21,8 @@
We can check minimum supported package version here https://github.com/Microsoft/vstest/blob/master/src/Microsoft.TestPlatform.ObjectModel/Microsoft.TestPlatform.ObjectModel.csproj#L37
-->
-
-
+
+
diff --git a/Documentation/Changelog.md b/Documentation/Changelog.md
index 5c551ceec..3575567a4 100644
--- a/Documentation/Changelog.md
+++ b/Documentation/Changelog.md
@@ -4,6 +4,38 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## Unreleased
+
+### 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
+
+### 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)
+
+### 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
diff --git a/Documentation/DeterministicBuild.md b/Documentation/DeterministicBuild.md
index a48865ce9..aa984e2dd 100644
--- a/Documentation/DeterministicBuild.md
+++ b/Documentation/DeterministicBuild.md
@@ -47,7 +47,7 @@ https://github.com/dotnet/sourcelink/issues/572 -->
-
+
-
+
-
+
origin)
-λ dotnet test --collect:"XPlat Code Coverage" /p:DeterministicSourcePaths=true
+λ 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
Copyright (c) Microsoft Corporation. All rights reserved.
@@ -78,5 +78,5 @@ Total tests: 1
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.
```
-Documentation\Examples\VSTest\DeterministicBuild\XUnitTestProject1\bin\Debug\netcoreapp3.1\CoverletSourceRootsMapping
+Documentation\Examples\VSTest\DeterministicBuild\XUnitTestProject1\bin\Debug\netcoreapp3.1\CoverletSourceRootsMapping_XUnitTestProject1
```
\ No newline at end of file
diff --git a/Documentation/GlobalTool.md b/Documentation/GlobalTool.md
index 231da2ff5..455f34b39 100644
--- a/Documentation/GlobalTool.md
+++ b/Documentation/GlobalTool.md
@@ -14,30 +14,31 @@ Cross platform .NET Core code coverage tool 3.0.0.0
Usage: coverlet [arguments] [options]
Arguments:
- Path to the test assembly or application directory.
+ 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.
- -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.
- --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.
- --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.
- --include-directory Include directories containing additional assemblies to be instrumented.
- --exclude-by-attribute Attributes to exclude from code coverage.
- --include-test-assembly Specifies whether to report code coverage of the test assembly.
- --single-hit Specifies whether to limit code coverage hit reporting to a single hit for each location
- --skipautoprops Neither track nor record auto-implemented properties.
- --merge-with Path to existing coverage result to merge.
- --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.
+ -h|--help Show help information
+ -v|--version Show version information
+ -t|--target Path to the test runner application.
+ -a|--targetargs Arguments to be passed to the test runner.
+ -o|--output Output of the generated coverage report
+ -v|--verbosity Sets the verbosity level of the command. Allowed values are quiet, minimal, normal, detailed.
+ -f|--format Format of the generated coverage report.
+ --threshold Exits with error if the coverage % is below value.
+ --threshold-type Coverage type to apply the threshold to.
+ --threshold-stat Coverage statistic used to enforce the threshold value.
+ --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.
+ --include-directory Include directories containing additional assemblies to be instrumented.
+ --exclude-by-attribute Attributes to exclude from code coverage.
+ --include-test-assembly Specifies whether to report code coverage of the test assembly.
+ --single-hit Specifies whether to limit code coverage hit reporting to a single hit for each location
+ --skipautoprops Neither track nor record auto-implemented properties.
+ --merge-with Path to existing coverage result to merge.
+ --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.
```
NB. For a [multiple value] options you have to specify values multiple times i.e.
diff --git a/Documentation/KnownIssues.md b/Documentation/KnownIssues.md
index bf1a24d95..0b5b842ec 100644
--- a/Documentation/KnownIssues.md
+++ b/Documentation/KnownIssues.md
@@ -201,3 +201,33 @@ NB. Workaround doesn't work if test method itself explicitly creates an appdomai
SUT (System Under Test) assembly is also not listed in MSBuild logs - "Instrumented module" is missing for your dll.
*Solution*: Check whether deterministic build is turned on for your solution, if so, follow the [instructions](DeterministicBuild.md) on how to handle deterministic builds.
+
+## Failure to produce Code Coverage Report
+
+*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]
+
+....
+
+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]
+```
+
+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.
+
diff --git a/Documentation/MSBuildIntegration.md b/Documentation/MSBuildIntegration.md
index bde9f364e..a937665be 100644
--- a/Documentation/MSBuildIntegration.md
+++ b/Documentation/MSBuildIntegration.md
@@ -181,6 +181,10 @@ You can also include coverage of the test assembly itself by setting `/p:Include
Neither track nor record auto-implemented properties.
Syntax: `/p:SkipAutoProps=true`
+### Instrument module wihtout local sources file.
+
+Syntax: `/p:InstrumentModulesWithoutLocalSources=true`
+
### Methods that do not return
Methods that do not return can be marked with attributes to cause statements after them to be excluded from coverage.
@@ -224,3 +228,19 @@ To generate deterministc report the parameter is:
```
/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.
+
+| Parameter | Description |
+|-----------|-------------|
+| MissingAll | Includes the assembly if at least one document is matching. In case the `ExcludeAssembliesWithoutSources` parameter is not specified the default value is `MissingAll`. |
+| MissingAny | Includes the assembly only if all documents can be matched to corresponding source files. |
+| None | No assembly is excluded. |
+
+Here is an example of how to specifiy the parameter:
+```
+/p:ExcludeAssembliesWithoutSources="MissingAny"
+```
\ No newline at end of file
diff --git a/Documentation/ReleasePlan.md b/Documentation/ReleasePlan.md
index 630f2941c..3472cf8f6 100644
--- a/Documentation/ReleasePlan.md
+++ b/Documentation/ReleasePlan.md
@@ -23,13 +23,14 @@ We release 3 components as NuGet packages:
| Package | Version |
|:----------------------|:--------|
-|**coverlet.msbuild** | 3.1.2 |
-|**coverlet.console** | 3.1.2 |
-|**coverlet.collector** | 3.1.2 |
+|**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 |
@@ -81,8 +82,6 @@ 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)
-Update core lib project file version https://github.com/coverlet-coverage/coverlet/blob/master/src/coverlet.core/coverlet.core.csproj.
-
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.
diff --git a/Documentation/VSTestIntegration.md b/Documentation/VSTestIntegration.md
index 07c17a092..4cc67383d 100644
--- a/Documentation/VSTestIntegration.md
+++ b/Documentation/VSTestIntegration.md
@@ -77,9 +77,20 @@ We're working to fill the gaps.
### Default option (if you don't specify a runsettings file)
-| Option | Summary |
-|:-------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-|Format | Results format in which coverage output is generated. Default format is cobertura. Supported format lcov, opencover, cobertura, teamcity, json (default coverlet proprietary format) |
+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.
+```
+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).
+```
+dotnet test --collect:"XPlat Code Coverage;Format=json"
+```
+
+It is even possible to specify the coverage output in multiple formats.
+```
+dotnet test --collect:"XPlat Code Coverage;Format=json,lcov,cobertura"
+```
### Advanced Options (Supported via runsettings)
@@ -97,7 +108,8 @@ These are a list of options that are supported by coverlet. These can be specifi
| IncludeTestAssembly | Include coverage of the test assembly. |
| SkipAutoProps | Neither track nor record auto-implemented properties. |
| DoesNotReturnAttribute | Methods marked with these attributes are known not to return, statements following them will be excluded from coverage |
-| DeterministicReport | Generates deterministic report in context of deterministic build. Take a look at [documentation](DeterministicBuild.md) for further informations. |
+| DeterministicReport | Generates deterministic report in context of deterministic build. Take a look at [documentation](DeterministicBuild.md) for further informations.
+| ExcludeAssembliesWithoutSources | Specifies whether to exclude assemblies without source. Options are either MissingAll, MissingAny or None. Default is MissingAll.|
How to specify these options via runsettings?
@@ -119,6 +131,7 @@ How to specify these options via runsettings?
true
true
false
+ MissingAll,MissingAny,None
diff --git a/README.md b/README.md
index e008ec44a..c60b9d37c 100644
--- a/README.md
+++ b/README.md
@@ -37,7 +37,7 @@ Coverlet can be used through three different *drivers*
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) supports only .NET Core application)
+### 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
```bash
diff --git a/coverlet.sln b/coverlet.sln
index 34e7257db..efccfa31f 100644
--- a/coverlet.sln
+++ b/coverlet.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.28902.138
+# 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
@@ -27,6 +27,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.tests.projectsampl
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
@@ -53,9 +54,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{9A8B19D4
test\Directory.Build.targets = test\Directory.Build.targets
EndProjectSection
EndProject
-Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "coverlet.tests.projectsample.fsharp", "test\coverlet.tests.projectsample.fsharp\coverlet.tests.projectsample.fsharp.fsproj", "{1CBF6966-2A67-4D2C-8598-D174B83072F4}"
+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("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "coverlet.tests.projectsample.netframework", "test\coverlet.tests.projectsample.netframework\coverlet.tests.projectsample.netframework.csproj", "{E69D68C9-78ED-4076-A14B-D07295A4B2A5}"
+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
@@ -119,6 +122,10 @@ Global
{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
@@ -149,6 +156,7 @@ Global
{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}
diff --git a/eng/build.yml b/eng/build.yml
index fb3a5b9c9..e4db0b0a6 100644
--- a/eng/build.yml
+++ b/eng/build.yml
@@ -9,6 +9,16 @@ steps:
version: 5.0.401
displayName: Install .NET Core SDK 5.0.401
+- 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
+
- script: dotnet restore
displayName: Restore packages
diff --git a/global.json b/global.json
index c07142494..2dbcd442b 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,6 @@
{
"sdk": {
- "version": "5.0.401",
+ "version": "6.0.408",
"rollForward": "latestMajor"
}
}
diff --git a/src/coverlet.collector/DataCollection/AttachmentManager.cs b/src/coverlet.collector/DataCollection/AttachmentManager.cs
index a7b2d24a0..8259f71f3 100644
--- a/src/coverlet.collector/DataCollection/AttachmentManager.cs
+++ b/src/coverlet.collector/DataCollection/AttachmentManager.cs
@@ -1,7 +1,9 @@
-using System;
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
using System.ComponentModel;
using System.IO;
-
using coverlet.collector.Resources;
using Coverlet.Collector.Utilities;
using Coverlet.Collector.Utilities.Interfaces;
@@ -50,7 +52,7 @@ public AttachmentManager(DataCollectionSink dataSink, DataCollectionContext data
_reportDirectory = Path.Combine(Path.GetTempPath(), reportDirectoryName);
// Register events
- _dataSink.SendFileCompleted += this.OnSendFileCompleted;
+ _dataSink.SendFileCompleted += OnSendFileCompleted;
}
///
@@ -61,10 +63,10 @@ public AttachmentManager(DataCollectionSink dataSink, DataCollectionContext data
public void SendCoverageReport(string coverageReport, string coverageReportFileName)
{
// Save coverage report to file
- string coverageReportPath = this.SaveCoverageReport(coverageReport, coverageReportFileName);
+ string coverageReportPath = SaveCoverageReport(coverageReport, coverageReportFileName);
// Send coverage attachment to test platform.
- this.SendAttachment(coverageReportPath);
+ SendAttachment(coverageReportPath);
}
///
@@ -78,9 +80,9 @@ public void Dispose()
_countDownEvent.Wait();
if (_dataSink != null)
{
- _dataSink.SendFileCompleted -= this.OnSendFileCompleted;
+ _dataSink.SendFileCompleted -= OnSendFileCompleted;
}
- this.CleanupReportDirectory();
+ CleanupReportDirectory();
}
catch (Exception ex)
{
diff --git a/src/coverlet.collector/DataCollection/CoverageManager.cs b/src/coverlet.collector/DataCollection/CoverageManager.cs
index ce12489f7..89ec41eb1 100644
--- a/src/coverlet.collector/DataCollection/CoverageManager.cs
+++ b/src/coverlet.collector/DataCollection/CoverageManager.cs
@@ -1,4 +1,7 @@
-using System;
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -77,8 +80,8 @@ public void InstrumentModules()
public IEnumerable<(string report, string fileName)> GetCoverageReports()
{
// Get coverage result
- CoverageResult coverageResult = this.GetCoverageResult();
- return this.GetCoverageReports(coverageResult);
+ CoverageResult coverageResult = GetCoverageResult();
+ return GetCoverageReports(coverageResult);
}
///
diff --git a/src/coverlet.collector/DataCollection/CoverageWrapper.cs b/src/coverlet.collector/DataCollection/CoverageWrapper.cs
index 12f4005e7..0569ab277 100644
--- a/src/coverlet.collector/DataCollection/CoverageWrapper.cs
+++ b/src/coverlet.collector/DataCollection/CoverageWrapper.cs
@@ -1,4 +1,7 @@
-using Coverlet.Collector.Utilities.Interfaces;
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Coverlet.Collector.Utilities.Interfaces;
using Coverlet.Core;
using Coverlet.Core.Abstractions;
@@ -30,7 +33,8 @@ public Coverage CreateCoverage(CoverletSettings settings, ILogger coverletLogger
UseSourceLink = settings.UseSourceLink,
SkipAutoProps = settings.SkipAutoProps,
DoesNotReturnAttributes = settings.DoesNotReturnAttributes,
- DeterministicReport = settings.DeterministicReport
+ DeterministicReport = settings.DeterministicReport,
+ ExcludeAssembliesWithoutSources = settings.ExcludeAssembliesWithoutSources
};
return new Coverage(
diff --git a/src/coverlet.collector/DataCollection/CoverletCoverageCollector.cs b/src/coverlet.collector/DataCollection/CoverletCoverageCollector.cs
index d5094dc91..d40a02e86 100644
--- a/src/coverlet.collector/DataCollection/CoverletCoverageCollector.cs
+++ b/src/coverlet.collector/DataCollection/CoverletCoverageCollector.cs
@@ -1,4 +1,7 @@
-using System;
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
@@ -124,7 +127,7 @@ private void OnSessionStart(object sender, SessionStartEventArgs sessionStartEve
try
{
// Get coverlet settings
- IEnumerable testModules = this.GetTestModules(sessionStartEventArgs);
+ IEnumerable testModules = GetTestModules(sessionStartEventArgs);
var coverletSettingsParser = new CoverletSettingsParser(_eqtTrace);
CoverletSettings coverletSettings = coverletSettingsParser.Parse(_configurationElement, testModules);
@@ -142,7 +145,7 @@ private void OnSessionStart(object sender, SessionStartEventArgs sessionStartEve
catch (Exception ex)
{
_logger.LogWarning(ex.ToString());
- this.Dispose(true);
+ Dispose(true);
}
}
@@ -160,15 +163,13 @@ private void OnSessionEnd(object sender, SessionEndEventArgs e)
// Get coverage reports
IEnumerable<(string report, string fileName)> coverageReports = _coverageManager?.GetCoverageReports();
- if (coverageReports != null && coverageReports.Count() > 0)
+ 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))))
+ using var attachmentManager = new AttachmentManager(_dataSink, _dataCollectionContext, _logger, _eqtTrace, _countDownEventFactory.Create(coverageReports.Count(), TimeSpan.FromSeconds(30)));
+ foreach ((string report, string fileName) in coverageReports)
{
- foreach ((string report, string fileName) in coverageReports)
- {
- attachmentManager.SendCoverageReport(report, fileName);
- }
+ attachmentManager.SendCoverageReport(report, fileName);
}
}
else
@@ -179,7 +180,7 @@ private void OnSessionEnd(object sender, SessionEndEventArgs e)
catch (Exception ex)
{
_logger.LogWarning(ex.ToString());
- this.Dispose(true);
+ Dispose(true);
}
}
@@ -223,11 +224,13 @@ private static IServiceCollection GetDefaultServiceCollection(TestPlatformEqtTra
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()));
+ 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 144d1ccf1..42eb6a1b2 100644
--- a/src/coverlet.collector/DataCollection/CoverletLogger.cs
+++ b/src/coverlet.collector/DataCollection/CoverletLogger.cs
@@ -1,5 +1,7 @@
-using System;
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using System;
using Coverlet.Collector.Utilities;
using Coverlet.Core.Abstractions;
diff --git a/src/coverlet.collector/DataCollection/CoverletSettings.cs b/src/coverlet.collector/DataCollection/CoverletSettings.cs
index 347173c76..2ebde799f 100644
--- a/src/coverlet.collector/DataCollection/CoverletSettings.cs
+++ b/src/coverlet.collector/DataCollection/CoverletSettings.cs
@@ -1,4 +1,7 @@
-using System.Linq;
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Linq;
using System.Text;
namespace Coverlet.Collector.DataCollection
@@ -78,6 +81,11 @@ internal class CoverletSettings
///
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();
@@ -95,6 +103,7 @@ public override string ToString()
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 7fe778173..3776c98d4 100644
--- a/src/coverlet.collector/DataCollection/CoverletSettingsParser.cs
+++ b/src/coverlet.collector/DataCollection/CoverletSettingsParser.cs
@@ -1,4 +1,7 @@
-using System;
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
@@ -45,6 +48,7 @@ public CoverletSettings Parse(XmlElement configurationElement, IEnumerable
/// Test modules
/// Test module
- private string ParseTestModule(IEnumerable testModules)
+ private static string ParseTestModule(IEnumerable testModules)
{
// Validate if at least one source present.
if (testModules == null || !testModules.Any())
@@ -83,13 +87,13 @@ private string ParseTestModule(IEnumerable testModules)
///
/// Configuration element
/// Report formats
- private string[] ParseReportFormats(XmlElement configurationElement)
+ private static string[] ParseReportFormats(XmlElement configurationElement)
{
string[] formats = Array.Empty();
if (configurationElement != null)
{
XmlElement reportFormatElement = configurationElement[CoverletConstants.ReportFormatElementName];
- formats = this.SplitElement(reportFormatElement);
+ formats = SplitElement(reportFormatElement);
}
return formats is null || formats.Length == 0 ? new[] { CoverletConstants.DefaultReportFormat } : formats;
@@ -100,10 +104,10 @@ private string[] ParseReportFormats(XmlElement configurationElement)
///
/// Configuration element
/// Filters to include
- private string[] ParseIncludeFilters(XmlElement configurationElement)
+ private static string[] ParseIncludeFilters(XmlElement configurationElement)
{
XmlElement includeFiltersElement = configurationElement[CoverletConstants.IncludeFiltersElementName];
- return this.SplitElement(includeFiltersElement);
+ return SplitElement(includeFiltersElement);
}
///
@@ -111,10 +115,10 @@ private string[] ParseIncludeFilters(XmlElement configurationElement)
///
/// Configuration element
/// Directories to include
- private string[] ParseIncludeDirectories(XmlElement configurationElement)
+ private static string[] ParseIncludeDirectories(XmlElement configurationElement)
{
XmlElement includeDirectoriesElement = configurationElement[CoverletConstants.IncludeDirectoriesElementName];
- return this.SplitElement(includeDirectoriesElement);
+ return SplitElement(includeDirectoriesElement);
}
///
@@ -122,14 +126,14 @@ private string[] ParseIncludeDirectories(XmlElement configurationElement)
///
/// Configuration element
/// Filters to exclude
- private string[] ParseExcludeFilters(XmlElement configurationElement)
+ private static string[] ParseExcludeFilters(XmlElement configurationElement)
{
- List excludeFilters = new List { CoverletConstants.DefaultExcludeFilter };
+ var excludeFilters = new List { CoverletConstants.DefaultExcludeFilter };
if (configurationElement != null)
{
XmlElement excludeFiltersElement = configurationElement[CoverletConstants.ExcludeFiltersElementName];
- string[] filters = this.SplitElement(excludeFiltersElement);
+ string[] filters = SplitElement(excludeFiltersElement);
if (filters != null)
{
excludeFilters.AddRange(filters);
@@ -144,10 +148,10 @@ private string[] ParseExcludeFilters(XmlElement configurationElement)
///
/// Configuration element
/// Source files to exclude
- private string[] ParseExcludeSourceFiles(XmlElement configurationElement)
+ private static string[] ParseExcludeSourceFiles(XmlElement configurationElement)
{
XmlElement excludeSourceFilesElement = configurationElement[CoverletConstants.ExcludeSourceFilesElementName];
- return this.SplitElement(excludeSourceFilesElement);
+ return SplitElement(excludeSourceFilesElement);
}
///
@@ -155,10 +159,10 @@ private string[] ParseExcludeSourceFiles(XmlElement configurationElement)
///
/// Configuration element
/// Attributes to exclude
- private string[] ParseExcludeAttributes(XmlElement configurationElement)
+ private static string[] ParseExcludeAttributes(XmlElement configurationElement)
{
XmlElement excludeAttributesElement = configurationElement[CoverletConstants.ExcludeAttributesElementName];
- return this.SplitElement(excludeAttributesElement);
+ return SplitElement(excludeAttributesElement);
}
///
@@ -166,7 +170,7 @@ private string[] ParseExcludeAttributes(XmlElement configurationElement)
///
/// Configuration element
/// Merge with attribute
- private string ParseMergeWith(XmlElement configurationElement)
+ private static string ParseMergeWith(XmlElement configurationElement)
{
XmlElement mergeWithElement = configurationElement[CoverletConstants.MergeWithElementName];
return mergeWithElement?.InnerText;
@@ -177,7 +181,7 @@ private string ParseMergeWith(XmlElement configurationElement)
///
/// Configuration element
/// Use source link flag
- private bool ParseUseSourceLink(XmlElement configurationElement)
+ private static bool ParseUseSourceLink(XmlElement configurationElement)
{
XmlElement useSourceLinkElement = configurationElement[CoverletConstants.UseSourceLinkElementName];
bool.TryParse(useSourceLinkElement?.InnerText, out bool useSourceLink);
@@ -189,7 +193,7 @@ private bool ParseUseSourceLink(XmlElement configurationElement)
///
/// Configuration element
/// Single hit flag
- private bool ParseSingleHit(XmlElement configurationElement)
+ private static bool ParseSingleHit(XmlElement configurationElement)
{
XmlElement singleHitElement = configurationElement[CoverletConstants.SingleHitElementName];
bool.TryParse(singleHitElement?.InnerText, out bool singleHit);
@@ -201,19 +205,30 @@ private bool ParseSingleHit(XmlElement configurationElement)
///
/// Configuration element
/// ParseDeterministicReport flag
- private bool ParseDeterministicReport(XmlElement configurationElement)
+ 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 include test assembly flag
///
/// Configuration element
/// Include Test Assembly Flag
- private bool ParseIncludeTestAssembly(XmlElement configurationElement)
+ private static bool ParseIncludeTestAssembly(XmlElement configurationElement)
{
XmlElement includeTestAssemblyElement = configurationElement[CoverletConstants.IncludeTestAssemblyElementName];
bool.TryParse(includeTestAssemblyElement?.InnerText, out bool includeTestAssembly);
@@ -225,7 +240,7 @@ private bool ParseIncludeTestAssembly(XmlElement configurationElement)
///
/// Configuration element
/// Include Test Assembly Flag
- private bool ParseSkipAutoProps(XmlElement configurationElement)
+ private static bool ParseSkipAutoProps(XmlElement configurationElement)
{
XmlElement skipAutoPropsElement = configurationElement[CoverletConstants.SkipAutoProps];
bool.TryParse(skipAutoPropsElement?.InnerText, out bool skipAutoProps);
@@ -237,10 +252,10 @@ private bool ParseSkipAutoProps(XmlElement configurationElement)
///
/// Configuration element
/// DoesNotReturn attributes
- private string[] ParseDoesNotReturnAttributes(XmlElement configurationElement)
+ private static string[] ParseDoesNotReturnAttributes(XmlElement configurationElement)
{
XmlElement doesNotReturnAttributesElement = configurationElement[CoverletConstants.DoesNotReturnAttributesElementName];
- return this.SplitElement(doesNotReturnAttributesElement);
+ return SplitElement(doesNotReturnAttributesElement);
}
///
@@ -248,7 +263,7 @@ private string[] ParseDoesNotReturnAttributes(XmlElement configurationElement)
///
/// The element to split
/// An array of the values in the element
- private string[] SplitElement(XmlElement 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 5539d6b04..f682b2687 100644
--- a/src/coverlet.collector/InProcDataCollection/CoverletInProcDataCollector.cs
+++ b/src/coverlet.collector/InProcDataCollection/CoverletInProcDataCollector.cs
@@ -1,8 +1,10 @@
-using System;
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
using System.Diagnostics;
using System.Reflection;
using System.Text;
-
using coverlet.collector.Resources;
using Coverlet.Collector.Utilities;
using Coverlet.Core.Instrumentation;
@@ -15,7 +17,7 @@ namespace Coverlet.Collector.DataCollection
public class CoverletInProcDataCollector : InProcDataCollection
{
private TestPlatformEqtTrace _eqtTrace;
- private bool _enableExceptionLog = false;
+ private bool _enableExceptionLog;
private void AttachDebugger()
{
@@ -53,7 +55,7 @@ public void TestCaseStart(TestCaseStartArgs testCaseStartArgs)
public void TestSessionEnd(TestSessionEndArgs testSessionEndArgs)
{
- foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
+ foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
Type injectedInstrumentationClass = GetInstrumentationClass(assembly);
if (injectedInstrumentationClass is null)
@@ -64,7 +66,7 @@ public void TestSessionEnd(TestSessionEndArgs testSessionEndArgs)
try
{
_eqtTrace.Verbose($"Calling ModuleTrackerTemplate.UnloadModule for '{injectedInstrumentationClass.Assembly.FullName}'");
- var unloadModule = injectedInstrumentationClass.GetMethod(nameof(ModuleTrackerTemplate.UnloadModule), new[] { typeof(object), typeof(EventArgs) });
+ 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}'");
@@ -89,7 +91,7 @@ private Type GetInstrumentationClass(Assembly assembly)
{
try
{
- foreach (var type in assembly.GetTypes())
+ foreach (Type type in assembly.GetTypes())
{
if (type.Namespace == "Coverlet.Core.Instrumentation.Tracker"
&& type.Name.StartsWith(assembly.GetName().Name + "_"))
@@ -104,7 +106,7 @@ private Type GetInstrumentationClass(Assembly assembly)
{
if (_enableExceptionLog)
{
- StringBuilder exceptionString = new StringBuilder();
+ var exceptionString = new StringBuilder();
exceptionString.AppendFormat("{0}: Failed to get Instrumentation class for assembly '{1}' with error: {2}",
CoverletConstants.InProcDataCollectorName, assembly, ex);
exceptionString.AppendLine();
diff --git a/src/coverlet.collector/Properties/AssemblyInfo.cs b/src/coverlet.collector/Properties/AssemblyInfo.cs
index 35079a9c9..4d4a63712 100644
--- a/src/coverlet.collector/Properties/AssemblyInfo.cs
+++ b/src/coverlet.collector/Properties/AssemblyInfo.cs
@@ -1,3 +1,6 @@
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
using System.Reflection;
using System.Runtime.CompilerServices;
diff --git a/src/coverlet.collector/Utilities/CountDownEvent.cs b/src/coverlet.collector/Utilities/CountDownEvent.cs
index a5a19b6f7..265c54103 100644
--- a/src/coverlet.collector/Utilities/CountDownEvent.cs
+++ b/src/coverlet.collector/Utilities/CountDownEvent.cs
@@ -1,6 +1,8 @@
-using System;
-using System.Threading;
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using System;
+using System.Threading;
using Coverlet.Collector.Utilities.Interfaces;
namespace Coverlet.Collector.Utilities
diff --git a/src/coverlet.collector/Utilities/CoverletConstants.cs b/src/coverlet.collector/Utilities/CoverletConstants.cs
index 431beafc6..3a7bfa998 100644
--- a/src/coverlet.collector/Utilities/CoverletConstants.cs
+++ b/src/coverlet.collector/Utilities/CoverletConstants.cs
@@ -1,4 +1,7 @@
-namespace Coverlet.Collector.Utilities
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace Coverlet.Collector.Utilities
{
internal static class CoverletConstants
{
@@ -23,5 +26,6 @@ internal static class CoverletConstants
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 e0665673b..189b363f8 100644
--- a/src/coverlet.collector/Utilities/CoverletDataCollectorException.cs
+++ b/src/coverlet.collector/Utilities/CoverletDataCollectorException.cs
@@ -1,4 +1,7 @@
-using System;
+// 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.Collector.Utilities
{
diff --git a/src/coverlet.collector/Utilities/DirectoryHelper.cs b/src/coverlet.collector/Utilities/DirectoryHelper.cs
index d03d212cc..c9800a21b 100644
--- a/src/coverlet.collector/Utilities/DirectoryHelper.cs
+++ b/src/coverlet.collector/Utilities/DirectoryHelper.cs
@@ -1,4 +1,7 @@
-using System.IO;
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.IO;
using Coverlet.Collector.Utilities.Interfaces;
namespace Coverlet.Collector.Utilities
diff --git a/src/coverlet.collector/Utilities/FileHelper.cs b/src/coverlet.collector/Utilities/FileHelper.cs
index f6e578380..f8a85b13e 100644
--- a/src/coverlet.collector/Utilities/FileHelper.cs
+++ b/src/coverlet.collector/Utilities/FileHelper.cs
@@ -1,4 +1,7 @@
-using System.IO;
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.IO;
using Coverlet.Collector.Utilities.Interfaces;
namespace Coverlet.Collector.Utilities
diff --git a/src/coverlet.collector/Utilities/Interfaces/ICountDown.cs b/src/coverlet.collector/Utilities/Interfaces/ICountDown.cs
index 3c884b0bb..69eddbce4 100644
--- a/src/coverlet.collector/Utilities/Interfaces/ICountDown.cs
+++ b/src/coverlet.collector/Utilities/Interfaces/ICountDown.cs
@@ -1,4 +1,7 @@
-using System;
+// 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.Collector.Utilities.Interfaces
{
diff --git a/src/coverlet.collector/Utilities/Interfaces/ICoverageWrapper.cs b/src/coverlet.collector/Utilities/Interfaces/ICoverageWrapper.cs
index 1358fab1b..1a34612c0 100644
--- a/src/coverlet.collector/Utilities/Interfaces/ICoverageWrapper.cs
+++ b/src/coverlet.collector/Utilities/Interfaces/ICoverageWrapper.cs
@@ -1,4 +1,7 @@
-using Coverlet.Collector.DataCollection;
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Coverlet.Collector.DataCollection;
using Coverlet.Core;
using Coverlet.Core.Abstractions;
diff --git a/src/coverlet.collector/Utilities/Interfaces/IDirectoryHelper.cs b/src/coverlet.collector/Utilities/Interfaces/IDirectoryHelper.cs
index 8e26c0dcf..f148f21ea 100644
--- a/src/coverlet.collector/Utilities/Interfaces/IDirectoryHelper.cs
+++ b/src/coverlet.collector/Utilities/Interfaces/IDirectoryHelper.cs
@@ -1,4 +1,7 @@
-namespace Coverlet.Collector.Utilities.Interfaces
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace Coverlet.Collector.Utilities.Interfaces
{
interface IDirectoryHelper
{
diff --git a/src/coverlet.collector/Utilities/Interfaces/IFileHelper.cs b/src/coverlet.collector/Utilities/Interfaces/IFileHelper.cs
index 8fd0ab9ea..1ddcc8678 100644
--- a/src/coverlet.collector/Utilities/Interfaces/IFileHelper.cs
+++ b/src/coverlet.collector/Utilities/Interfaces/IFileHelper.cs
@@ -1,4 +1,7 @@
-namespace Coverlet.Collector.Utilities.Interfaces
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace Coverlet.Collector.Utilities.Interfaces
{
internal interface IFileHelper
{
diff --git a/src/coverlet.collector/Utilities/TestPlatformEqtTrace.cs b/src/coverlet.collector/Utilities/TestPlatformEqtTrace.cs
index 31f5d4409..7c02f6d03 100644
--- a/src/coverlet.collector/Utilities/TestPlatformEqtTrace.cs
+++ b/src/coverlet.collector/Utilities/TestPlatformEqtTrace.cs
@@ -1,4 +1,7 @@
-using Microsoft.VisualStudio.TestPlatform.ObjectModel;
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Microsoft.VisualStudio.TestPlatform.ObjectModel;
namespace Coverlet.Collector.Utilities
{
diff --git a/src/coverlet.collector/Utilities/TestPlatformLogger.cs b/src/coverlet.collector/Utilities/TestPlatformLogger.cs
index 47f55c897..105e4c9d9 100644
--- a/src/coverlet.collector/Utilities/TestPlatformLogger.cs
+++ b/src/coverlet.collector/Utilities/TestPlatformLogger.cs
@@ -1,4 +1,7 @@
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection;
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection;
namespace Coverlet.Collector.Utilities
{
diff --git a/src/coverlet.collector/build/netstandard1.0/coverlet.collector.targets b/src/coverlet.collector/build/netstandard1.0/coverlet.collector.targets
index 2e4adb4c5..7bd7b28e7 100644
--- a/src/coverlet.collector/build/netstandard1.0/coverlet.collector.targets
+++ b/src/coverlet.collector/build/netstandard1.0/coverlet.collector.targets
@@ -44,7 +44,7 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and
<_mapping Include="@(_byProject->'%(Identity)|%(OriginalPath)=%(MappedPath)')" />
- <_sourceRootMappingFilePath>$([MSBuild]::EnsureTrailingSlash('$(OutputPath)'))CoverletSourceRootsMapping
+ <_sourceRootMappingFilePath>$([MSBuild]::EnsureTrailingSlash('$(OutputPath)'))CoverletSourceRootsMapping_$(AssemblyName)
/// Exit Codes returned from Coverlet console process.
diff --git a/src/coverlet.console/Logging/ConsoleLogger.cs b/src/coverlet.console/Logging/ConsoleLogger.cs
index 98e620f16..1e41af504 100644
--- a/src/coverlet.console/Logging/ConsoleLogger.cs
+++ b/src/coverlet.console/Logging/ConsoleLogger.cs
@@ -1,3 +1,6 @@
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
using System;
using Coverlet.Core.Abstractions;
using static System.Console;
@@ -6,7 +9,8 @@ namespace Coverlet.Console.Logging
{
class ConsoleLogger : ILogger
{
- private static readonly object _sync = new object();
+ 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);
@@ -23,7 +27,7 @@ private void Log(LogLevel level, string message, ConsoleColor color)
{
if (level < Level) return;
- lock (_sync)
+ lock (s_sync)
{
ConsoleColor currentForegroundColor;
if (color != (currentForegroundColor = ForegroundColor))
@@ -39,4 +43,4 @@ private void Log(LogLevel level, string message, ConsoleColor color)
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/coverlet.console/Logging/LogLevel.cs b/src/coverlet.console/Logging/LogLevel.cs
index 5ec47ef90..2e0cf7320 100644
--- a/src/coverlet.console/Logging/LogLevel.cs
+++ b/src/coverlet.console/Logging/LogLevel.cs
@@ -1,3 +1,6 @@
+// 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
{
///
diff --git a/src/coverlet.console/Program.cs b/src/coverlet.console/Program.cs
index ae3b02d03..d967f90d6 100644
--- a/src/coverlet.console/Program.cs
+++ b/src/coverlet.console/Program.cs
@@ -1,4 +1,7 @@
-using System;
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
@@ -6,7 +9,6 @@
using System.IO;
using System.Linq;
using System.Text;
-
using ConsoleTables;
using Coverlet.Console.Logging;
using Coverlet.Core;
@@ -37,7 +39,7 @@ static int Main(string[] args)
ServiceProvider serviceProvider = serviceCollection.BuildServiceProvider();
var logger = (ConsoleLogger)serviceProvider.GetService();
- var fileSystem = serviceProvider.GetService();
+ IFileSystem fileSystem = serviceProvider.GetService();
var app = new CommandLineApplication
{
@@ -68,6 +70,7 @@ static int Main(string[] args)
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(() =>
{
@@ -95,7 +98,8 @@ static int Main(string[] args)
MergeWith = mergeWith.Value(),
UseSourceLink = useSourceLink.HasValue(),
SkipAutoProps = skipAutoProp.HasValue(),
- DoesNotReturnAttributes = doesNotReturnAttributes.Values.ToArray()
+ DoesNotReturnAttributes = doesNotReturnAttributes.Values.ToArray(),
+ ExcludeAssembliesWithoutSources = excludeAssembliesWithoutSources.Value()
};
ISourceRootTranslator sourceRootTranslator = serviceProvider.GetRequiredService();
@@ -134,15 +138,15 @@ static int Main(string[] args)
process.WaitForExit();
- var dOutput = output.HasValue() ? output.Value() : Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar.ToString();
- var dThresholdTypes = thresholdTypes.HasValue() ? thresholdTypes.Values : new List(new string[] { "line", "branch", "method" });
- var dThresholdStat = thresholdStat.HasValue() ? Enum.Parse(thresholdStat.Value(), true) : Enum.Parse("minimum", true);
+ 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...");
- var result = coverage.GetCoverageResult();
-
- var directory = Path.GetDirectoryName(dOutput);
+ CoverageResult result = coverage.GetCoverageResult();
+
+ string directory = Path.GetDirectoryName(dOutput);
if (directory == string.Empty)
{
directory = Directory.GetCurrentDirectory();
@@ -152,9 +156,9 @@ static int Main(string[] args)
Directory.CreateDirectory(directory);
}
- foreach (var format in formats.HasValue() ? formats.Values : new List(new string[] { "json" }))
+ foreach (string format in formats.HasValue() ? formats.Values : new List(new string[] { "json" }))
{
- var reporter = new ReporterFactory(format).CreateReporter();
+ Core.Abstractions.IReporter reporter = new ReporterFactory(format).CreateReporter();
if (reporter == null)
{
throw new Exception($"Specified output format '{format}' is not supported");
@@ -169,19 +173,19 @@ static int Main(string[] args)
else
{
// Output to file
- var filename = Path.GetFileName(dOutput);
+ string filename = Path.GetFileName(dOutput);
filename = (filename == string.Empty) ? $"coverage.{reporter.Extension}" : filename;
filename = Path.HasExtension(filename) ? filename : $"{filename}.{reporter.Extension}";
- var report = Path.Combine(directory, filename);
+ 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 (var thresholdType in dThresholdTypes)
+
+ foreach (string thresholdType in dThresholdTypes)
{
if (thresholdType.Equals("line", StringComparison.OrdinalIgnoreCase))
{
@@ -196,19 +200,19 @@ static int Main(string[] args)
thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Method);
}
}
-
- Dictionary thresholdTypeFlagValues = new Dictionary();
+
+ var thresholdTypeFlagValues = new Dictionary();
if (threshold.HasValue() && threshold.Value().Contains(','))
{
- var thresholdValues = threshold.Value().Split(',', StringSplitOptions.RemoveEmptyEntries).Select(t => t.Trim());
- if (thresholdValues.Count() != thresholdTypeFlagQueue.Count())
+ 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");
+ throw new Exception($"Threshold type flag count ({thresholdTypeFlagQueue.Count}) and values count ({thresholdValues.Count()}) doesn't match");
}
- foreach (var thresholdValue in thresholdValues)
+ foreach (string thresholdValue in thresholdValues)
{
- if (double.TryParse(thresholdValue, out var value))
+ if (double.TryParse(thresholdValue, out double value))
{
thresholdTypeFlagValues[thresholdTypeFlagQueue.Dequeue()] = value;
}
@@ -231,23 +235,23 @@ static int Main(string[] args)
var coverageTable = new ConsoleTable("Module", "Line", "Branch", "Method");
var summary = new CoverageSummary();
- var linePercentCalculation = summary.CalculateLineCoverage(result.Modules);
- var branchPercentCalculation = summary.CalculateBranchCoverage(result.Modules);
- var methodPercentCalculation = summary.CalculateMethodCoverage(result.Modules);
+ CoverageDetails linePercentCalculation = summary.CalculateLineCoverage(result.Modules);
+ CoverageDetails branchPercentCalculation = summary.CalculateBranchCoverage(result.Modules);
+ CoverageDetails methodPercentCalculation = summary.CalculateMethodCoverage(result.Modules);
- var totalLinePercent = linePercentCalculation.Percent;
- var totalBranchPercent = branchPercentCalculation.Percent;
- var totalMethodPercent = methodPercentCalculation.Percent;
+ double totalLinePercent = linePercentCalculation.Percent;
+ double totalBranchPercent = branchPercentCalculation.Percent;
+ double totalMethodPercent = methodPercentCalculation.Percent;
- var averageLinePercent = linePercentCalculation.AverageModulePercent;
- var averageBranchPercent = branchPercentCalculation.AverageModulePercent;
- var averageMethodPercent = methodPercentCalculation.AverageModulePercent;
+ double averageLinePercent = linePercentCalculation.AverageModulePercent;
+ double averageBranchPercent = branchPercentCalculation.AverageModulePercent;
+ double averageMethodPercent = methodPercentCalculation.AverageModulePercent;
- foreach (var _module in result.Modules)
+ foreach (KeyValuePair _module in result.Modules)
{
- var linePercent = summary.CalculateLineCoverage(_module.Value).Percent;
- var branchPercent = summary.CalculateBranchCoverage(_module.Value).Percent;
- var methodPercent = summary.CalculateMethodCoverage(_module.Value).Percent;
+ 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)}%");
}
@@ -266,15 +270,15 @@ static int Main(string[] args)
{
exitCode += (int)CommandExitCodes.TestFailed;
}
-
- var thresholdTypeFlags = result.GetThresholdTypesBelowThreshold(summary, thresholdTypeFlagValues, dThresholdStat);
+
+ 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]}");
+ exceptionMessageBuilder.AppendLine($"The {dThresholdStat.ToString().ToLower()} line coverage is below the specified {thresholdTypeFlagValues[ThresholdTypeFlags.Line]}");
}
if ((thresholdTypeFlags & ThresholdTypeFlags.Branch) != ThresholdTypeFlags.None)
@@ -284,7 +288,7 @@ static int Main(string[] args)
if ((thresholdTypeFlags & ThresholdTypeFlags.Method) != ThresholdTypeFlags.None)
{
- exceptionMessageBuilder.AppendLine($"The {dThresholdStat.ToString().ToLower()} method coverage is below the specified {thresholdTypeFlagValues[ThresholdTypeFlags.Method]}");
+ exceptionMessageBuilder.AppendLine($"The {dThresholdStat.ToString().ToLower()} method coverage is below the specified {thresholdTypeFlagValues[ThresholdTypeFlags.Method]}");
}
throw new Exception(exceptionMessageBuilder.ToString());
}
diff --git a/src/coverlet.console/Properties/AssemblyInfo.cs b/src/coverlet.console/Properties/AssemblyInfo.cs
index 27cbf8a13..59db2a82f 100644
--- a/src/coverlet.console/Properties/AssemblyInfo.cs
+++ b/src/coverlet.console/Properties/AssemblyInfo.cs
@@ -1 +1,4 @@
+// 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
diff --git a/src/coverlet.console/coverlet.console.csproj b/src/coverlet.console/coverlet.console.csproj
index 5d33ebec8..b44e05dff 100644
--- a/src/coverlet.console/coverlet.console.csproj
+++ b/src/coverlet.console/coverlet.console.csproj
@@ -1,8 +1,8 @@
-
+
Exe
- net5.0
+ net6.0
coverlet
true
coverlet.console
diff --git a/src/coverlet.core/Abstractions/IAssemblyAdapter.cs b/src/coverlet.core/Abstractions/IAssemblyAdapter.cs
new file mode 100644
index 000000000..48b23084b
--- /dev/null
+++ b/src/coverlet.core/Abstractions/IAssemblyAdapter.cs
@@ -0,0 +1,10 @@
+// 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 IAssemblyAdapter
+ {
+ string GetAssemblyName(string assemblyPath);
+ }
+}
diff --git a/src/coverlet.core/Abstractions/ICecilSymbolHelper.cs b/src/coverlet.core/Abstractions/ICecilSymbolHelper.cs
index 10bf1719a..06cdde3d1 100644
--- a/src/coverlet.core/Abstractions/ICecilSymbolHelper.cs
+++ b/src/coverlet.core/Abstractions/ICecilSymbolHelper.cs
@@ -1,4 +1,7 @@
-using System.Collections.Generic;
+// 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.Symbols;
using Mono.Cecil;
using Mono.Cecil.Cil;
diff --git a/src/coverlet.core/Abstractions/IConsole.cs b/src/coverlet.core/Abstractions/IConsole.cs
index 9bc7e4e0b..72991cac5 100644
--- a/src/coverlet.core/Abstractions/IConsole.cs
+++ b/src/coverlet.core/Abstractions/IConsole.cs
@@ -1,4 +1,7 @@
-namespace Coverlet.Core.Abstractions
+// 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 IConsole
{
diff --git a/src/coverlet.core/Abstractions/IFileSystem.cs b/src/coverlet.core/Abstractions/IFileSystem.cs
index 54185dbf5..cb710c758 100644
--- a/src/coverlet.core/Abstractions/IFileSystem.cs
+++ b/src/coverlet.core/Abstractions/IFileSystem.cs
@@ -1,4 +1,7 @@
-using System.IO;
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.IO;
namespace Coverlet.Core.Abstractions
{
diff --git a/src/coverlet.core/Abstractions/IInstrumentationHelper.cs b/src/coverlet.core/Abstractions/IInstrumentationHelper.cs
index 295a111d1..65af40011 100644
--- a/src/coverlet.core/Abstractions/IInstrumentationHelper.cs
+++ b/src/coverlet.core/Abstractions/IInstrumentationHelper.cs
@@ -1,4 +1,9 @@
-namespace Coverlet.Core.Abstractions
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Coverlet.Core.Enums;
+
+namespace Coverlet.Core.Abstractions
{
internal interface IInstrumentationHelper
{
@@ -12,8 +17,8 @@ internal interface IInstrumentationHelper
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, out string firstNotFoundDocument);
- bool PortablePdbHasLocalSource(string module, out string firstNotFoundDocument);
+ 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 bc7e78c01..c3e6ef15e 100644
--- a/src/coverlet.core/Abstractions/ILogger.cs
+++ b/src/coverlet.core/Abstractions/ILogger.cs
@@ -1,3 +1,6 @@
+// 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
diff --git a/src/coverlet.core/Abstractions/IProcessExitHandler.cs b/src/coverlet.core/Abstractions/IProcessExitHandler.cs
index fc5262d89..635015946 100644
--- a/src/coverlet.core/Abstractions/IProcessExitHandler.cs
+++ b/src/coverlet.core/Abstractions/IProcessExitHandler.cs
@@ -1,4 +1,7 @@
-using System;
+// 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
{
diff --git a/src/coverlet.core/Abstractions/IReporter.cs b/src/coverlet.core/Abstractions/IReporter.cs
index 5a76d1858..9e497d62a 100644
--- a/src/coverlet.core/Abstractions/IReporter.cs
+++ b/src/coverlet.core/Abstractions/IReporter.cs
@@ -1,3 +1,6 @@
+// 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
diff --git a/src/coverlet.core/Abstractions/IRetryHelper.cs b/src/coverlet.core/Abstractions/IRetryHelper.cs
index c0e6e14cb..88a1b29d5 100644
--- a/src/coverlet.core/Abstractions/IRetryHelper.cs
+++ b/src/coverlet.core/Abstractions/IRetryHelper.cs
@@ -1,4 +1,7 @@
-using System;
+// 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
{
diff --git a/src/coverlet.core/Abstractions/ISourceRootTranslator.cs b/src/coverlet.core/Abstractions/ISourceRootTranslator.cs
index c91077950..4af6cfddf 100644
--- a/src/coverlet.core/Abstractions/ISourceRootTranslator.cs
+++ b/src/coverlet.core/Abstractions/ISourceRootTranslator.cs
@@ -1,10 +1,14 @@
-using System.Collections.Generic;
+// 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.Helpers;
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);
diff --git a/src/coverlet.core/Attributes/DoesNotReturnAttribute.cs b/src/coverlet.core/Attributes/DoesNotReturnAttribute.cs
index a35be1336..cebe198f1 100644
--- a/src/coverlet.core/Attributes/DoesNotReturnAttribute.cs
+++ b/src/coverlet.core/Attributes/DoesNotReturnAttribute.cs
@@ -1,4 +1,7 @@
-using System;
+// 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
{
diff --git a/src/coverlet.core/Attributes/ExcludeFromCoverage.cs b/src/coverlet.core/Attributes/ExcludeFromCoverage.cs
index f7281ca04..71efcb7bc 100644
--- a/src/coverlet.core/Attributes/ExcludeFromCoverage.cs
+++ b/src/coverlet.core/Attributes/ExcludeFromCoverage.cs
@@ -1,3 +1,6 @@
+// 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
diff --git a/src/coverlet.core/Coverage.cs b/src/coverlet.core/Coverage.cs
index 30714c0dd..8a3def37b 100644
--- a/src/coverlet.core/Coverage.cs
+++ b/src/coverlet.core/Coverage.cs
@@ -1,3 +1,6 @@
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
using System;
using System.Collections.Generic;
using System.IO;
@@ -6,7 +9,6 @@
using Coverlet.Core.Abstractions;
using Coverlet.Core.Helpers;
using Coverlet.Core.Instrumentation;
-
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
@@ -41,24 +43,22 @@ internal class CoverageParameters
public bool SkipAutoProps { get; set; }
[DataMember]
public bool DeterministicReport { get; set; }
+ [DataMember]
+ public string ExcludeAssembliesWithoutSources { get; set; }
}
internal class Coverage
{
- private string _moduleOrAppDirectory;
- private string _identifier;
- private ILogger _logger;
- private IInstrumentationHelper _instrumentationHelper;
- private IFileSystem _fileSystem;
- private ISourceRootTranslator _sourceRootTranslator;
- private ICecilSymbolHelper _cecilSymbolHelper;
- private List _results;
- private CoverageParameters _parameters;
-
- public string Identifier
- {
- get { return _identifier; }
- }
+ 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,
@@ -76,7 +76,7 @@ public Coverage(string moduleOrDirectory,
_fileSystem = fileSystem;
_sourceRootTranslator = sourceRootTranslator;
_cecilSymbolHelper = cecilSymbolHelper;
- _identifier = Guid.NewGuid().ToString();
+ Identifier = Guid.NewGuid().ToString();
_results = new List();
}
@@ -86,7 +86,7 @@ public Coverage(CoveragePrepareResult prepareResult,
IFileSystem fileSystem,
ISourceRootTranslator sourceRootTranslator)
{
- _identifier = prepareResult.Identifier;
+ Identifier = prepareResult.Identifier;
_moduleOrAppDirectory = prepareResult.ModuleOrDirectory;
_parameters = prepareResult.Parameters;
_results = new List(prepareResult.Results);
@@ -107,7 +107,7 @@ public CoveragePrepareResult PrepareModules()
_parameters.ExcludeFilters = _parameters.ExcludeFilters?.Where(f => _instrumentationHelper.IsValidFilterExpression(f)).ToArray();
_parameters.IncludeFilters = _parameters.IncludeFilters?.Where(f => _instrumentationHelper.IsValidFilterExpression(f)).ToArray();
- foreach (var module in modules)
+ foreach (string module in modules)
{
if (_instrumentationHelper.IsModuleExcluded(module, _parameters.ExcludeFilters) ||
!_instrumentationHelper.IsModuleIncluded(module, _parameters.IncludeFilters))
@@ -117,7 +117,7 @@ public CoveragePrepareResult PrepareModules()
}
var instrumenter = new Instrumenter(module,
- _identifier,
+ Identifier,
_parameters,
_logger,
_instrumentationHelper,
@@ -127,7 +127,7 @@ public CoveragePrepareResult PrepareModules()
if (instrumenter.CanInstrument())
{
- _instrumentationHelper.BackupOriginalModule(module, _identifier);
+ _instrumentationHelper.BackupOriginalModule(module, Identifier);
// Guard code path and restore if instrumentation fails.
try
@@ -142,14 +142,14 @@ public CoveragePrepareResult PrepareModules()
catch (Exception ex)
{
_logger.LogWarning($"Unable to instrument module: {module}\n{ex}");
- _instrumentationHelper.RestoreOriginalModule(module, _identifier);
+ _instrumentationHelper.RestoreOriginalModule(module, Identifier);
}
}
}
return new CoveragePrepareResult()
{
- Identifier = _identifier,
+ Identifier = Identifier,
ModuleOrDirectory = _moduleOrAppDirectory,
Parameters = _parameters,
Results = _results.ToArray()
@@ -160,14 +160,14 @@ public CoverageResult GetCoverageResult()
{
CalculateCoverage();
- Modules modules = new Modules();
- foreach (var result in _results)
+ var modules = new Modules();
+ foreach (InstrumenterResult result in _results)
{
- Documents documents = new Documents();
- foreach (var doc in result.Documents.Values)
+ var documents = new Documents();
+ foreach (Document doc in result.Documents.Values)
{
// Construct Line Results
- foreach (var line in doc.Lines.Values)
+ foreach (Line line in doc.Lines.Values)
{
if (documents.TryGetValue(doc.Path, out Classes classes))
{
@@ -200,7 +200,7 @@ public CoverageResult GetCoverageResult()
}
// Construct Branch Results
- foreach (var branch in doc.Branches.Values)
+ foreach (Branch branch in doc.Branches.Values)
{
if (documents.TryGetValue(doc.Path, out Classes classes))
{
@@ -242,7 +242,7 @@ public CoverageResult GetCoverageResult()
}
modules.Add(Path.GetFileName(result.ModulePath), documents);
- _instrumentationHelper.RestoreOriginalModule(result.ModulePath, _identifier);
+ _instrumentationHelper.RestoreOriginalModule(result.ModulePath, Identifier);
}
// In case of anonymous delegate compiler generate a custom class and passes it as type.method delegate.
@@ -250,11 +250,11 @@ public CoverageResult GetCoverageResult()
// 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 (var module in modules)
+ foreach (KeyValuePair module in modules)
{
- foreach (var document in module.Value)
+ foreach (KeyValuePair document in module.Value)
{
- foreach (var @class in document.Value)
+ 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
@@ -263,9 +263,9 @@ public CoverageResult GetCoverageResult()
continue;
}
- foreach (var method in @class.Value)
+ foreach (KeyValuePair method in @class.Value)
{
- foreach (var branch in method.Value.Branches)
+ foreach (BranchInfo branch in method.Value.Branches)
{
if (BranchInCompilerGeneratedClass(method.Key))
{
@@ -295,13 +295,13 @@ public CoverageResult GetCoverageResult()
}
// After method/branches analysis of compiled generated class we can remove noise from reports
- if (!(compileGeneratedClassToRemove is null))
+ if (compileGeneratedClassToRemove is not null)
{
- foreach (var module in modules)
+ foreach (KeyValuePair module in modules)
{
- foreach (var document in module.Value)
+ foreach (KeyValuePair document in module.Value)
{
- foreach (var classToRemove in compileGeneratedClassToRemove)
+ foreach (string classToRemove in compileGeneratedClassToRemove)
{
document.Value.Remove(classToRemove);
}
@@ -309,7 +309,7 @@ public CoverageResult GetCoverageResult()
}
}
- var coverageResult = new CoverageResult { Identifier = _identifier, Modules = modules, InstrumentedResults = _results, Parameters = _parameters };
+ 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))
{
@@ -322,7 +322,7 @@ public CoverageResult GetCoverageResult()
private bool BranchInCompilerGeneratedClass(string methodName)
{
- foreach (var instrumentedResult in _results)
+ foreach (InstrumenterResult instrumentedResult in _results)
{
if (instrumentedResult.BranchesInCompiledGeneratedClass.Contains(methodName))
{
@@ -332,18 +332,18 @@ private bool BranchInCompilerGeneratedClass(string methodName)
return false;
}
- private Method GetMethodWithSameLineInSameDocument(Classes documentClasses, string compilerGeneratedClassName, int branchLine)
+ private static Method GetMethodWithSameLineInSameDocument(Classes documentClasses, string compilerGeneratedClassName, int branchLine)
{
- foreach (var @class in documentClasses)
+ foreach (KeyValuePair @class in documentClasses)
{
if (@class.Key == compilerGeneratedClassName)
{
continue;
}
- foreach (var method in @class.Value)
+ foreach (KeyValuePair method in @class.Value)
{
- foreach (var line in method.Value.Lines)
+ foreach (KeyValuePair line in method.Value.Lines)
{
if (line.Key == branchLine)
{
@@ -357,7 +357,7 @@ private Method GetMethodWithSameLineInSameDocument(Classes documentClasses, stri
private void CalculateCoverage()
{
- foreach (var result in _results)
+ foreach (InstrumenterResult result in _results)
{
if (!_fileSystem.Exists(result.HitsFilePath))
{
@@ -369,12 +369,12 @@ private void CalculateCoverage()
continue;
}
- List documents = result.Documents.Values.ToList();
+ var documents = result.Documents.Values.ToList();
if (_parameters.UseSourceLink && result.SourceLink != null)
{
- var jObject = JObject.Parse(result.SourceLink)["documents"];
- var sourceLinkDocuments = JsonConvert.DeserializeObject>(jObject.ToString());
- foreach (var document in documents)
+ JToken jObject = JObject.Parse(result.SourceLink)["documents"];
+ Dictionary sourceLinkDocuments = JsonConvert.DeserializeObject>(jObject.ToString());
+ foreach (Document document in documents)
{
document.Path = GetSourceLinkUrl(sourceLinkDocuments, document.Path);
}
@@ -408,7 +408,7 @@ private void CalculateCoverage()
}
var documentsList = result.Documents.Values.ToList();
- using (var fs = _fileSystem.NewFileStream(result.HitsFilePath, FileMode.Open, FileAccess.Read))
+ using (Stream fs = _fileSystem.NewFileStream(result.HitsFilePath, FileMode.Open, FileAccess.Read))
using (var br = new BinaryReader(fs))
{
int hitCandidatesCount = br.ReadInt32();
@@ -417,8 +417,8 @@ private void CalculateCoverage()
for (int i = 0; i < hitCandidatesCount; ++i)
{
- var hitLocation = result.HitCandidates[i];
- var document = documentsList[hitLocation.docIndex];
+ HitCandidate hitLocation = result.HitCandidates[i];
+ Document document = documentsList[hitLocation.docIndex];
int hits = br.ReadInt32();
if (hits == 0)
@@ -428,7 +428,7 @@ private void CalculateCoverage()
if (hitLocation.isBranch)
{
- var branch = document.Branches[new BranchKey(hitLocation.start, hitLocation.end)];
+ Branch branch = document.Branches[new BranchKey(hitLocation.start, hitLocation.end)];
branch.Hits += hits;
if (branch.Hits < 0)
@@ -443,7 +443,7 @@ private void CalculateCoverage()
continue;
}
- var line = document.Lines[j];
+ Line line = document.Lines[j];
line.Hits += hits;
if (line.Hits < 0)
@@ -472,10 +472,10 @@ private string GetSourceLinkUrl(Dictionary sourceLinkDocuments,
return url;
}
- var keyWithBestMatch = string.Empty;
- var relativePathOfBestMatch = string.Empty;
+ string keyWithBestMatch = string.Empty;
+ string relativePathOfBestMatch = string.Empty;
- foreach (var sourceLinkDocument in sourceLinkDocuments)
+ foreach (KeyValuePair sourceLinkDocument in sourceLinkDocuments)
{
string key = sourceLinkDocument.Key;
if (Path.GetFileName(key) != "*") continue;
@@ -515,8 +515,12 @@ private string GetSourceLinkUrl(Dictionary sourceLinkDocuments,
string replacement = Path.Combine(relativePathOfBestMatch, Path.GetFileName(document));
replacement = replacement.Replace('\\', '/');
- url = sourceLinkDocuments[keyWithBestMatch];
- return url.Replace("*", replacement);
+ 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 be2ccd4e2..59db863c2 100644
--- a/src/coverlet.core/CoverageDetails.cs
+++ b/src/coverlet.core/CoverageDetails.cs
@@ -1,3 +1,6 @@
+// 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
diff --git a/src/coverlet.core/CoveragePrepareResult.cs b/src/coverlet.core/CoveragePrepareResult.cs
index 9bf9037f8..7c15d76c3 100644
--- a/src/coverlet.core/CoveragePrepareResult.cs
+++ b/src/coverlet.core/CoveragePrepareResult.cs
@@ -1,4 +1,7 @@
-using System.IO;
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.IO;
using System.Runtime.Serialization;
using Coverlet.Core.Instrumentation;
@@ -30,7 +33,7 @@ public static CoveragePrepareResult Deserialize(Stream serializedInstrumentState
public static Stream Serialize(CoveragePrepareResult instrumentState)
{
- MemoryStream ms = new MemoryStream();
+ 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 e02e5e0f5..4e981346b 100644
--- a/src/coverlet.core/CoverageResult.cs
+++ b/src/coverlet.core/CoverageResult.cs
@@ -1,10 +1,10 @@
-using System;
+// 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 System.IO;
using System.Linq;
using Coverlet.Core.Enums;
using Coverlet.Core.Instrumentation;
-using Coverlet.Core.Symbols;
namespace Coverlet.Core
{
@@ -48,54 +48,54 @@ public CoverageResult() { }
public void Merge(Modules modules)
{
- foreach (var module in modules)
+ foreach (KeyValuePair module in modules)
{
- if (!this.Modules.Keys.Contains(module.Key))
+ if (!Modules.Keys.Contains(module.Key))
{
- this.Modules.Add(module.Key, module.Value);
+ Modules.Add(module.Key, module.Value);
}
else
{
- foreach (var document in module.Value)
+ foreach (KeyValuePair document in module.Value)
{
- if (!this.Modules[module.Key].ContainsKey(document.Key))
+ if (!Modules[module.Key].ContainsKey(document.Key))
{
- this.Modules[module.Key].Add(document.Key, document.Value);
+ Modules[module.Key].Add(document.Key, document.Value);
}
else
{
- foreach (var @class in document.Value)
+ foreach (KeyValuePair @class in document.Value)
{
- if (!this.Modules[module.Key][document.Key].ContainsKey(@class.Key))
+ if (!Modules[module.Key][document.Key].ContainsKey(@class.Key))
{
- this.Modules[module.Key][document.Key].Add(@class.Key, @class.Value);
+ Modules[module.Key][document.Key].Add(@class.Key, @class.Value);
}
else
{
- foreach (var method in @class.Value)
+ foreach (KeyValuePair method in @class.Value)
{
- if (!this.Modules[module.Key][document.Key][@class.Key].ContainsKey(method.Key))
+ if (!Modules[module.Key][document.Key][@class.Key].ContainsKey(method.Key))
{
- this.Modules[module.Key][document.Key][@class.Key].Add(method.Key, method.Value);
+ Modules[module.Key][document.Key][@class.Key].Add(method.Key, method.Value);
}
else
{
- foreach (var line in method.Value.Lines)
+ foreach (KeyValuePair line in method.Value.Lines)
{
- if (!this.Modules[module.Key][document.Key][@class.Key][method.Key].Lines.ContainsKey(line.Key))
+ if (!Modules[module.Key][document.Key][@class.Key][method.Key].Lines.ContainsKey(line.Key))
{
- this.Modules[module.Key][document.Key][@class.Key][method.Key].Lines.Add(line.Key, line.Value);
+ Modules[module.Key][document.Key][@class.Key][method.Key].Lines.Add(line.Key, line.Value);
}
else
{
- this.Modules[module.Key][document.Key][@class.Key][method.Key].Lines[line.Key] += line.Value;
+ Modules[module.Key][document.Key][@class.Key][method.Key].Lines[line.Key] += line.Value;
}
}
- foreach (var branch in method.Value.Branches)
+ foreach (BranchInfo branch in method.Value.Branches)
{
- var branches = this.Modules[module.Key][document.Key][@class.Key][method.Key].Branches;
- var branchInfo = branches.FirstOrDefault(b => b.EndOffset == branch.EndOffset && b.Line == branch.Line && b.Offset == branch.Offset && b.Ordinal == branch.Ordinal && b.Path == branch.Path);
+ 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
@@ -113,7 +113,7 @@ public void Merge(Modules modules)
public ThresholdTypeFlags GetThresholdTypesBelowThreshold(CoverageSummary summary, Dictionary thresholdTypeFlagValues, ThresholdStatistic thresholdStat)
{
- var thresholdTypeFlags = ThresholdTypeFlags.None;
+ ThresholdTypeFlags thresholdTypeFlags = ThresholdTypeFlags.None;
switch (thresholdStat)
{
case ThresholdStatistic.Minimum:
@@ -121,7 +121,7 @@ public ThresholdTypeFlags GetThresholdTypesBelowThreshold(CoverageSummary summar
if (!Modules.Any())
thresholdTypeFlags = CompareThresholdValues(thresholdTypeFlagValues, thresholdTypeFlags, 0, 0, 0);
- foreach (var module in Modules)
+ foreach (KeyValuePair module in Modules)
{
double line = summary.CalculateLineCoverage(module.Value).Percent;
double branch = summary.CalculateBranchCoverage(module.Value).Percent;
@@ -158,19 +158,19 @@ private static ThresholdTypeFlags CompareThresholdValues(
Dictionary thresholdTypeFlagValues, ThresholdTypeFlags thresholdTypeFlags,
double line, double branch, double method)
{
- if (thresholdTypeFlagValues.TryGetValue(ThresholdTypeFlags.Line, out var lineThresholdValue) &&
+ if (thresholdTypeFlagValues.TryGetValue(ThresholdTypeFlags.Line, out double lineThresholdValue) &&
lineThresholdValue > line)
{
thresholdTypeFlags |= ThresholdTypeFlags.Line;
}
- if (thresholdTypeFlagValues.TryGetValue(ThresholdTypeFlags.Branch, out var branchThresholdValue) &&
+ if (thresholdTypeFlagValues.TryGetValue(ThresholdTypeFlags.Branch, out double branchThresholdValue) &&
branchThresholdValue > branch)
{
thresholdTypeFlags |= ThresholdTypeFlags.Branch;
}
- if (thresholdTypeFlagValues.TryGetValue(ThresholdTypeFlags.Method, out var methodThresholdValue) &&
+ if (thresholdTypeFlagValues.TryGetValue(ThresholdTypeFlags.Method, out double methodThresholdValue) &&
methodThresholdValue > method)
{
thresholdTypeFlags |= ThresholdTypeFlags.Method;
@@ -179,4 +179,4 @@ private static ThresholdTypeFlags CompareThresholdValues(
return thresholdTypeFlags;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/coverlet.core/CoverageSummary.cs b/src/coverlet.core/CoverageSummary.cs
index 19700deac..a56cda454 100644
--- a/src/coverlet.core/CoverageSummary.cs
+++ b/src/coverlet.core/CoverageSummary.cs
@@ -1,3 +1,6 @@
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
using System;
using System.Collections.Generic;
using System.Linq;
@@ -17,9 +20,9 @@ public CoverageDetails CalculateLineCoverage(Lines lines)
public CoverageDetails CalculateLineCoverage(Methods methods)
{
var details = new CoverageDetails();
- foreach (var method in methods)
+ foreach (KeyValuePair method in methods)
{
- var methodCoverage = CalculateLineCoverage(method.Value.Lines);
+ CoverageDetails methodCoverage = CalculateLineCoverage(method.Value.Lines);
details.Covered += methodCoverage.Covered;
details.Total += methodCoverage.Total;
}
@@ -29,9 +32,9 @@ public CoverageDetails CalculateLineCoverage(Methods methods)
public CoverageDetails CalculateLineCoverage(Classes classes)
{
var details = new CoverageDetails();
- foreach (var @class in classes)
+ foreach (KeyValuePair @class in classes)
{
- var classCoverage = CalculateLineCoverage(@class.Value);
+ CoverageDetails classCoverage = CalculateLineCoverage(@class.Value);
details.Covered += classCoverage.Covered;
details.Total += classCoverage.Total;
}
@@ -41,9 +44,9 @@ public CoverageDetails CalculateLineCoverage(Classes classes)
public CoverageDetails CalculateLineCoverage(Documents documents)
{
var details = new CoverageDetails();
- foreach (var document in documents)
+ foreach (KeyValuePair document in documents)
{
- var documentCoverage = CalculateLineCoverage(document.Value);
+ CoverageDetails documentCoverage = CalculateLineCoverage(document.Value);
details.Covered += documentCoverage.Covered;
details.Total += documentCoverage.Total;
}
@@ -52,15 +55,15 @@ public CoverageDetails CalculateLineCoverage(Documents documents)
public CoverageDetails CalculateLineCoverage(Modules modules)
{
- var details = new CoverageDetails{Modules = modules};
- var accumPercent = 0.0D;
+ var details = new CoverageDetails { Modules = modules };
+ double accumPercent = 0.0D;
if (modules.Count == 0)
return details;
- foreach (var module in modules)
+ foreach (KeyValuePair module in modules)
{
- var moduleCoverage = CalculateLineCoverage(module.Value);
+ CoverageDetails moduleCoverage = CalculateLineCoverage(module.Value);
details.Covered += moduleCoverage.Covered;
details.Total += moduleCoverage.Total;
accumPercent += moduleCoverage.Percent;
@@ -86,7 +89,7 @@ public int CalculateNpathComplexity(IList branches)
}
var paths = new Dictionary();
- foreach (var branch in branches)
+ foreach (BranchInfo branch in branches)
{
if (!paths.TryGetValue(branch.Offset, out int count))
{
@@ -96,7 +99,7 @@ public int CalculateNpathComplexity(IList branches)
}
int npath = 1;
- foreach (var branchPoints in paths.Values)
+ foreach (int branchPoints in paths.Values)
{
try
{
@@ -154,9 +157,9 @@ public int CalculateCyclomaticComplexity(Documents documents)
public CoverageDetails CalculateBranchCoverage(Methods methods)
{
var details = new CoverageDetails();
- foreach (var method in methods)
+ foreach (KeyValuePair method in methods)
{
- var methodCoverage = CalculateBranchCoverage(method.Value.Branches);
+ CoverageDetails methodCoverage = CalculateBranchCoverage(method.Value.Branches);
details.Covered += methodCoverage.Covered;
details.Total += methodCoverage.Total;
}
@@ -166,9 +169,9 @@ public CoverageDetails CalculateBranchCoverage(Methods methods)
public CoverageDetails CalculateBranchCoverage(Classes classes)
{
var details = new CoverageDetails();
- foreach (var @class in classes)
+ foreach (KeyValuePair @class in classes)
{
- var classCoverage = CalculateBranchCoverage(@class.Value);
+ CoverageDetails classCoverage = CalculateBranchCoverage(@class.Value);
details.Covered += classCoverage.Covered;
details.Total += classCoverage.Total;
}
@@ -178,9 +181,9 @@ public CoverageDetails CalculateBranchCoverage(Classes classes)
public CoverageDetails CalculateBranchCoverage(Documents documents)
{
var details = new CoverageDetails();
- foreach (var document in documents)
+ foreach (KeyValuePair document in documents)
{
- var documentCoverage = CalculateBranchCoverage(document.Value);
+ CoverageDetails documentCoverage = CalculateBranchCoverage(document.Value);
details.Covered += documentCoverage.Covered;
details.Total += documentCoverage.Total;
}
@@ -189,15 +192,15 @@ public CoverageDetails CalculateBranchCoverage(Documents documents)
public CoverageDetails CalculateBranchCoverage(Modules modules)
{
- var details = new CoverageDetails{ Modules = modules };
- var accumPercent = 0.0D;
+ var details = new CoverageDetails { Modules = modules };
+ double accumPercent = 0.0D;
if (modules.Count == 0)
return details;
- foreach (var module in modules)
+ foreach (KeyValuePair module in modules)
{
- var moduleCoverage = CalculateBranchCoverage(module.Value);
+ CoverageDetails moduleCoverage = CalculateBranchCoverage(module.Value);
details.Covered += moduleCoverage.Covered;
details.Total += moduleCoverage.Total;
accumPercent += moduleCoverage.Percent;
@@ -217,10 +220,10 @@ public CoverageDetails CalculateMethodCoverage(Lines lines)
public CoverageDetails CalculateMethodCoverage(Methods methods)
{
var details = new CoverageDetails();
- var methodsWithLines = methods.Where(m => m.Value.Lines.Count > 0);
- foreach (var method in methodsWithLines)
+ IEnumerable> methodsWithLines = methods.Where(m => m.Value.Lines.Count > 0);
+ foreach (KeyValuePair method in methodsWithLines)
{
- var methodCoverage = CalculateMethodCoverage(method.Value.Lines);
+ CoverageDetails methodCoverage = CalculateMethodCoverage(method.Value.Lines);
details.Covered += methodCoverage.Covered;
}
details.Total = methodsWithLines.Count();
@@ -230,9 +233,9 @@ public CoverageDetails CalculateMethodCoverage(Methods methods)
public CoverageDetails CalculateMethodCoverage(Classes classes)
{
var details = new CoverageDetails();
- foreach (var @class in classes)
+ foreach (KeyValuePair @class in classes)
{
- var classCoverage = CalculateMethodCoverage(@class.Value);
+ CoverageDetails classCoverage = CalculateMethodCoverage(@class.Value);
details.Covered += classCoverage.Covered;
details.Total += classCoverage.Total;
}
@@ -242,9 +245,9 @@ public CoverageDetails CalculateMethodCoverage(Classes classes)
public CoverageDetails CalculateMethodCoverage(Documents documents)
{
var details = new CoverageDetails();
- foreach (var document in documents)
+ foreach (KeyValuePair document in documents)
{
- var documentCoverage = CalculateMethodCoverage(document.Value);
+ CoverageDetails documentCoverage = CalculateMethodCoverage(document.Value);
details.Covered += documentCoverage.Covered;
details.Total += documentCoverage.Total;
}
@@ -253,15 +256,15 @@ public CoverageDetails CalculateMethodCoverage(Documents documents)
public CoverageDetails CalculateMethodCoverage(Modules modules)
{
- var details = new CoverageDetails{ Modules = modules };
- var accumPercent = 0.0D;
+ var details = new CoverageDetails { Modules = modules };
+ double accumPercent = 0.0D;
if (modules.Count == 0)
return details;
- foreach (var module in modules)
+ foreach (KeyValuePair module in modules)
{
- var moduleCoverage = CalculateMethodCoverage(module.Value);
+ CoverageDetails moduleCoverage = CalculateMethodCoverage(module.Value);
details.Covered += moduleCoverage.Covered;
details.Total += moduleCoverage.Total;
accumPercent += moduleCoverage.Percent;
@@ -270,4 +273,4 @@ public CoverageDetails CalculateMethodCoverage(Modules modules)
return details;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/coverlet.core/Enums/AssemblySearchType.cs b/src/coverlet.core/Enums/AssemblySearchType.cs
new file mode 100644
index 000000000..099e54217
--- /dev/null
+++ b/src/coverlet.core/Enums/AssemblySearchType.cs
@@ -0,0 +1,12 @@
+// 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 AssemblySearchType
+ {
+ MissingAny,
+ MissingAll,
+ None
+ }
+}
diff --git a/src/coverlet.core/Enums/ThresholdStatistic.cs b/src/coverlet.core/Enums/ThresholdStatistic.cs
index 9b7dd18ba..1dbb55dc0 100644
--- a/src/coverlet.core/Enums/ThresholdStatistic.cs
+++ b/src/coverlet.core/Enums/ThresholdStatistic.cs
@@ -1,3 +1,6 @@
+// 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
diff --git a/src/coverlet.core/Enums/ThresholdTypeFlags.cs b/src/coverlet.core/Enums/ThresholdTypeFlags.cs
index 11a082178..9b24222a5 100644
--- a/src/coverlet.core/Enums/ThresholdTypeFlags.cs
+++ b/src/coverlet.core/Enums/ThresholdTypeFlags.cs
@@ -1,3 +1,6 @@
+// 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
diff --git a/src/coverlet.core/Exceptions.cs b/src/coverlet.core/Exceptions.cs
index 81a6809aa..4eefd76ca 100644
--- a/src/coverlet.core/Exceptions.cs
+++ b/src/coverlet.core/Exceptions.cs
@@ -1,4 +1,7 @@
-using System;
+// 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.Exceptions
{
diff --git a/src/coverlet.core/Extensions/HelperExtensions.cs b/src/coverlet.core/Extensions/HelperExtensions.cs
index fe8f45cae..30439c0c9 100644
--- a/src/coverlet.core/Extensions/HelperExtensions.cs
+++ b/src/coverlet.core/Extensions/HelperExtensions.cs
@@ -1,3 +1,5 @@
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using Coverlet.Core.Attributes;
@@ -7,10 +9,10 @@ namespace Coverlet.Core.Extensions
internal static class HelperExtensions
{
[ExcludeFromCoverage]
- public static TRet Maybe(this T value, Func action, TRet defValue = default(TRet))
+ public static TRet Maybe(this T value, Func action, TRet defValue = default)
where T : class
{
return (value != null) ? action(value) : defValue;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/coverlet.core/Helpers/AssemblyAdapter.cs b/src/coverlet.core/Helpers/AssemblyAdapter.cs
new file mode 100644
index 000000000..f4626d2ff
--- /dev/null
+++ b/src/coverlet.core/Helpers/AssemblyAdapter.cs
@@ -0,0 +1,16 @@
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Reflection;
+using Coverlet.Core.Abstractions;
+
+namespace Coverlet.Core.Helpers
+{
+ internal class AssemblyAdapter : IAssemblyAdapter
+ {
+ public string GetAssemblyName(string assemblyPath)
+ {
+ return AssemblyName.GetAssemblyName(assemblyPath).Name;
+ }
+ }
+}
diff --git a/src/coverlet.core/Helpers/Console.cs b/src/coverlet.core/Helpers/Console.cs
index eacddd323..8781b49de 100644
--- a/src/coverlet.core/Helpers/Console.cs
+++ b/src/coverlet.core/Helpers/Console.cs
@@ -1,5 +1,7 @@
-using System;
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using System;
using Coverlet.Core.Abstractions;
namespace Coverlet.Core.Helpers
diff --git a/src/coverlet.core/Helpers/FileSystem.cs b/src/coverlet.core/Helpers/FileSystem.cs
index 7213d3b4e..10dfc8f0a 100644
--- a/src/coverlet.core/Helpers/FileSystem.cs
+++ b/src/coverlet.core/Helpers/FileSystem.cs
@@ -1,5 +1,8 @@
-using Coverlet.Core.Abstractions;
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
using System.IO;
+using Coverlet.Core.Abstractions;
namespace Coverlet.Core.Helpers
{
diff --git a/src/coverlet.core/Helpers/InstrumentationHelper.cs b/src/coverlet.core/Helpers/InstrumentationHelper.cs
index 8a6827621..6723fc733 100644
--- a/src/coverlet.core/Helpers/InstrumentationHelper.cs
+++ b/src/coverlet.core/Helpers/InstrumentationHelper.cs
@@ -1,3 +1,6 @@
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@@ -8,15 +11,15 @@
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using System.Text.RegularExpressions;
-
using Coverlet.Core.Abstractions;
+using Coverlet.Core.Enums;
namespace Coverlet.Core.Helpers
{
internal class InstrumentationHelper : IInstrumentationHelper
{
private const int RetryAttempts = 12;
- private readonly ConcurrentDictionary _backupList = new ConcurrentDictionary();
+ private readonly ConcurrentDictionary _backupList = new();
private readonly IRetryHelper _retryHelper;
private readonly IFileSystem _fileSystem;
private readonly ISourceRootTranslator _sourceRootTranslator;
@@ -81,51 +84,59 @@ public string[] GetCoverableModules(string moduleOrAppDirectory, string[] direct
public bool HasPdb(string module, out bool embedded)
{
embedded = false;
- using (var moduleStream = _fileSystem.OpenRead(module))
- using (var peReader = new PEReader(moduleStream))
+ using Stream moduleStream = _fileSystem.OpenRead(module);
+ using var peReader = new PEReader(moduleStream);
+ foreach (DebugDirectoryEntry entry in peReader.ReadDebugDirectory())
{
- foreach (var entry in peReader.ReadDebugDirectory())
+ if (entry.Type == DebugDirectoryEntryType.CodeView)
{
- if (entry.Type == DebugDirectoryEntryType.CodeView)
+ CodeViewDebugDirectoryData codeViewData = peReader.ReadCodeViewDebugDirectoryData(entry);
+ string modulePdbFileName = $"{Path.GetFileNameWithoutExtension(module)}.pdb";
+ if (_sourceRootTranslator.ResolveFilePath(codeViewData.Path) == modulePdbFileName)
{
- var codeViewData = peReader.ReadCodeViewDebugDirectoryData(entry);
- if (_sourceRootTranslator.ResolveFilePath(codeViewData.Path) == $"{Path.GetFileNameWithoutExtension(module)}.pdb")
- {
- // PDB is embedded
- embedded = true;
- return true;
- }
-
- return _fileSystem.Exists(_sourceRootTranslator.ResolveFilePath(codeViewData.Path));
+ // PDB is embedded
+ embedded = true;
+ return true;
}
- }
- return false;
+ 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 false;
}
- public bool EmbeddedPortablePdbHasLocalSource(string module, out string firstNotFoundDocument)
+ public bool EmbeddedPortablePdbHasLocalSource(string module, AssemblySearchType excludeAssembliesWithoutSources)
{
- firstNotFoundDocument = "";
- using (Stream moduleStream = _fileSystem.OpenRead(module))
- using (var peReader = new PEReader(moduleStream))
+ using Stream moduleStream = _fileSystem.OpenRead(module);
+ using var peReader = new PEReader(moduleStream);
+ foreach (DebugDirectoryEntry entry in peReader.ReadDebugDirectory())
{
- foreach (DebugDirectoryEntry entry in peReader.ReadDebugDirectory())
+ if (entry.Type == DebugDirectoryEntryType.EmbeddedPortablePdb)
{
- if (entry.Type == DebugDirectoryEntryType.EmbeddedPortablePdb)
+ using MetadataReaderProvider embeddedMetadataProvider = peReader.ReadEmbeddedPortablePdbDebugDirectoryData(entry);
+ MetadataReader metadataReader = embeddedMetadataProvider.GetMetadataReader();
+
+ if (!MatchDocumentsWithSources(module, excludeAssembliesWithoutSources, metadataReader))
{
- using (MetadataReaderProvider embeddedMetadataProvider = peReader.ReadEmbeddedPortablePdbDebugDirectoryData(entry))
- {
- MetadataReader metadataReader = embeddedMetadataProvider.GetMetadataReader();
-
- var matchingResult = MatchDocumentsWithSources(metadataReader);
-
- if (!matchingResult.allDocumentsMatch)
- {
- firstNotFoundDocument = matchingResult.notFoundDocument;
- return false;
- }
- }
+ return false;
}
}
}
@@ -135,37 +146,31 @@ public bool EmbeddedPortablePdbHasLocalSource(string module, out string firstNot
return true;
}
- public bool PortablePdbHasLocalSource(string module, out string firstNotFoundDocument)
+ public bool PortablePdbHasLocalSource(string module, AssemblySearchType excludeAssembliesWithoutSources)
{
- firstNotFoundDocument = "";
- using (var moduleStream = _fileSystem.OpenRead(module))
- using (var peReader = new PEReader(moduleStream))
+ using Stream moduleStream = _fileSystem.OpenRead(module);
+ using var peReader = new PEReader(moduleStream);
+ foreach (DebugDirectoryEntry entry in peReader.ReadDebugDirectory())
{
- foreach (var entry in peReader.ReadDebugDirectory())
+ if (entry.Type == DebugDirectoryEntryType.CodeView)
{
- 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))
{
- var codeViewData = peReader.ReadCodeViewDebugDirectoryData(entry);
- using Stream pdbStream = _fileSystem.OpenRead(_sourceRootTranslator.ResolveFilePath(codeViewData.Path));
- using MetadataReaderProvider 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;
- }
-
- var matchingResult = MatchDocumentsWithSources(metadataReader);
-
- if (!matchingResult.allDocumentsMatch)
- {
- firstNotFoundDocument = matchingResult.notFoundDocument;
- return false;
- }
+ return false;
}
}
}
@@ -173,38 +178,71 @@ public bool PortablePdbHasLocalSource(string module, out string firstNotFoundDoc
return true;
}
- private (bool allDocumentsMatch, string notFoundDocument) MatchDocumentsWithSources(MetadataReader metadataReader)
+ private bool MatchDocumentsWithSources(string module, AssemblySearchType excludeAssembliesWithoutSources,
+ MetadataReader metadataReader)
{
- foreach (DocumentHandle docHandle in metadataReader.Documents)
+ if (excludeAssembliesWithoutSources.Equals(AssemblySearchType.MissingAll))
{
- Document document = metadataReader.GetDocument(docHandle);
- string docName = _sourceRootTranslator.ResolveFilePath(metadataReader.GetString(document.Name));
- Guid languageGuid = metadataReader.GetGuid(document.Language);
- // We verify all docs and return false if not all are present in local
- // We could have false negative if doc is not a source
- // Btw check for all possible extension could be weak approach
- // We exlude from the check the autogenerated source file(i.e. source generators)
- // We exclude special F# construct https://github.com/coverlet-coverage/coverlet/issues/1145
- if (!_fileSystem.Exists(docName) && !docName.EndsWith(".g.cs") &&
- !IsUnknownModuleInFSharpAssembly(languageGuid, docName))
+ bool anyDocumentMatches = MatchDocumentsWithSourcesMissingAll(metadataReader);
+ if (!anyDocumentMatches)
{
- return (false, docName);
+ _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;
+ }
+ }
+
+ 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));
+ });
+ }
+
+ private bool MatchDocumentsWithSourcesMissingAll(MetadataReader metadataReader)
+ {
+ return DocumentSourceMap(metadataReader).Any(x => x.documentExists);
+ }
+
+ private (bool allDocumentsMatch, string notFoundDocument) MatchDocumentsWithSourcesMissingAny(
+ MetadataReader metadataReader)
+ {
+ var documentSourceMap = DocumentSourceMap(metadataReader).ToList();
+
+ if (documentSourceMap.Any(x => !x.documentExists))
+ return (false, documentSourceMap.FirstOrDefault(x => !x.documentExists).documentName);
+
return (true, string.Empty);
}
public void BackupOriginalModule(string module, string identifier)
{
- var backupPath = GetBackupPath(module, identifier);
- var backupSymbolPath = Path.ChangeExtension(backupPath, ".pdb");
+ 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}'");
}
- var symbolFile = Path.ChangeExtension(module, ".pdb");
+ string symbolFile = Path.ChangeExtension(module, ".pdb");
if (_fileSystem.Exists(symbolFile))
{
_fileSystem.Copy(symbolFile, backupSymbolPath, true);
@@ -217,12 +255,12 @@ public void BackupOriginalModule(string module, string identifier)
public virtual void RestoreOriginalModule(string module, string identifier)
{
- var backupPath = GetBackupPath(module, identifier);
- var backupSymbolPath = Path.ChangeExtension(backupPath, ".pdb");
+ 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
- var retryStrategy = CreateRetryStrategy();
+ Func retryStrategy = CreateRetryStrategy();
_retryHelper.Retry(() =>
{
@@ -247,7 +285,7 @@ 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
- var retryStrategy = CreateRetryStrategy();
+ Func retryStrategy = CreateRetryStrategy();
foreach (string key in _backupList.Keys.ToList())
{
@@ -263,7 +301,7 @@ public virtual void RestoreOriginalModules()
public void DeleteHitsFile(string path)
{
- var retryStrategy = CreateRetryStrategy();
+ Func retryStrategy = CreateRetryStrategy();
_retryHelper.Retry(() => _fileSystem.Delete(path), retryStrategy, RetryAttempts);
}
@@ -308,7 +346,7 @@ public bool IsModuleExcluded(string module, string[] excludeFilters)
if (module == null)
return false;
- foreach (var filter in excludeFilters)
+ foreach (string filter in excludeFilters)
{
string typePattern = filter.Substring(filter.IndexOf(']') + 1);
@@ -336,7 +374,7 @@ public bool IsModuleIncluded(string module, string[] includeFilters)
if (module == null)
return false;
- foreach (var filter in includeFilters)
+ foreach (string filter in includeFilters)
{
string modulePattern = filter.Substring(1, filter.IndexOf(']') - 1);
@@ -386,12 +424,12 @@ public void SetLogger(ILogger logger)
_logger = logger;
}
- private bool IsTypeFilterMatch(string module, string type, string[] filters)
+ private static bool IsTypeFilterMatch(string module, string type, string[] filters)
{
Debug.Assert(module != null);
Debug.Assert(filters != null);
- foreach (var filter in filters)
+ foreach (string filter in filters)
{
string typePattern = filter.Substring(filter.IndexOf(']') + 1);
string modulePattern = filter.Substring(1, filter.IndexOf(']') - 1);
@@ -406,7 +444,7 @@ private bool IsTypeFilterMatch(string module, string type, string[] filters)
return false;
}
- private string GetBackupPath(string module, string identifier)
+ private static string GetBackupPath(string module, string identifier)
{
return Path.Combine(
Path.GetTempPath(),
@@ -426,14 +464,14 @@ TimeSpan retryStrategy()
return retryStrategy;
}
- private string WildcardToRegex(string pattern)
+ private static string WildcardToRegex(string pattern)
{
return "^" + Regex.Escape(pattern).
Replace("\\*", ".*").
Replace("\\?", "?") + "$";
}
- private bool IsAssembly(string filePath)
+ private static bool IsAssembly(string filePath)
{
Debug.Assert(filePath != null);
@@ -450,12 +488,5 @@ private bool IsAssembly(string filePath)
return false;
}
}
-
- private bool IsUnknownModuleInFSharpAssembly(Guid languageGuid, string docName)
- {
- // https://github.com/dotnet/runtime/blob/main/docs/design/specs/PortablePdb-Metadata.md#document-table-0x30
- return languageGuid.Equals(new Guid("ab4f38c9-b6e6-43ba-be3b-58080b2ccce3"))
- && docName.EndsWith("unknown");
- }
}
-}
\ No newline at end of file
+}
diff --git a/src/coverlet.core/Helpers/ProcessExitHandler.cs b/src/coverlet.core/Helpers/ProcessExitHandler.cs
index 7083e27ae..1e570f07f 100644
--- a/src/coverlet.core/Helpers/ProcessExitHandler.cs
+++ b/src/coverlet.core/Helpers/ProcessExitHandler.cs
@@ -1,4 +1,7 @@
-using System;
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
using Coverlet.Core.Abstractions;
namespace Coverlet.Core.Helpers
diff --git a/src/coverlet.core/Helpers/RetryHelper.cs b/src/coverlet.core/Helpers/RetryHelper.cs
index 652cb1cfb..dcf3fa9d0 100644
--- a/src/coverlet.core/Helpers/RetryHelper.cs
+++ b/src/coverlet.core/Helpers/RetryHelper.cs
@@ -1,7 +1,10 @@
-using Coverlet.Core.Abstractions;
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
using System;
using System.Collections.Generic;
using System.Threading;
+using Coverlet.Core.Abstractions;
namespace Coverlet.Core.Helpers
{
@@ -59,4 +62,4 @@ public T Do(
throw new AggregateException(exceptions);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/coverlet.core/Helpers/SourceRootTranslator.cs b/src/coverlet.core/Helpers/SourceRootTranslator.cs
index b75901ada..7fea89516 100644
--- a/src/coverlet.core/Helpers/SourceRootTranslator.cs
+++ b/src/coverlet.core/Helpers/SourceRootTranslator.cs
@@ -1,8 +1,11 @@
-using Coverlet.Core.Abstractions;
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
+using Coverlet.Core.Abstractions;
namespace Coverlet.Core.Helpers
{
@@ -19,7 +22,7 @@ internal class SourceRootTranslator : ISourceRootTranslator
private readonly IFileSystem _fileSystem;
private readonly Dictionary> _sourceRootMapping;
private readonly Dictionary> _sourceToDeterministicPathMapping;
- private const string MappingFileName = "CoverletSourceRootsMapping";
+ private readonly string _mappingFileName;
private Dictionary _resolutionCacheFiles;
public SourceRootTranslator(ILogger logger, IFileSystem fileSystem)
@@ -29,7 +32,7 @@ public SourceRootTranslator(ILogger logger, IFileSystem fileSystem)
_sourceRootMapping = new Dictionary>();
}
- public SourceRootTranslator(string moduleTestPath, ILogger logger, IFileSystem fileSystem)
+ public SourceRootTranslator(string moduleTestPath, ILogger logger, IFileSystem fileSystem, IAssemblyAdapter assemblyAdapter)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem));
@@ -41,18 +44,22 @@ public SourceRootTranslator(string moduleTestPath, ILogger logger, IFileSystem f
{
throw new FileNotFoundException($"Module test path '{moduleTestPath}' not found", moduleTestPath);
}
+
+ string assemblyName = assemblyAdapter.GetAssemblyName(moduleTestPath);
+ _mappingFileName = $"CoverletSourceRootsMapping_{assemblyName}";
+
_sourceRootMapping = LoadSourceRootMapping(Path.GetDirectoryName(moduleTestPath));
_sourceToDeterministicPathMapping = LoadSourceToDeterministicPathMapping(_sourceRootMapping);
}
- private Dictionary> LoadSourceToDeterministicPathMapping(Dictionary> sourceRootMapping)
+ private static Dictionary> LoadSourceToDeterministicPathMapping(Dictionary> sourceRootMapping)
{
if (sourceRootMapping is null)
{
throw new ArgumentNullException(nameof(sourceRootMapping));
}
- Dictionary> sourceToDeterministicPathMapping = new Dictionary>();
+ var sourceToDeterministicPathMapping = new Dictionary>();
foreach (KeyValuePair> sourceRootMappingEntry in sourceRootMapping)
{
foreach (SourceRootMapping originalPath in sourceRootMappingEntry.Value)
@@ -70,9 +77,9 @@ private Dictionary> LoadSourceToDeterministicPathMapping(Di
private Dictionary> LoadSourceRootMapping(string directory)
{
- Dictionary> mapping = new Dictionary>();
+ var mapping = new Dictionary>();
- string mappingFilePath = Path.Combine(directory, MappingFileName);
+ string mappingFilePath = Path.Combine(directory, _mappingFileName);
if (!_fileSystem.Exists(mappingFilePath))
{
return mapping;
@@ -105,6 +112,17 @@ private Dictionary> LoadSourceRootMapping(string
return mapping;
}
+ public bool AddMappingInCache(string originalFileName, string targetFileName)
+ {
+ if (_resolutionCacheFiles != null && _resolutionCacheFiles.ContainsKey(originalFileName))
+ {
+ return false;
+ }
+
+ (_resolutionCacheFiles ??= new Dictionary()).Add(originalFileName, targetFileName);
+ return true;
+ }
+
public IReadOnlyList ResolvePathRoot(string pathRoot)
{
return _sourceRootMapping.TryGetValue(pathRoot, out List sourceRootMapping) ? sourceRootMapping.AsReadOnly() : null;
@@ -138,7 +156,7 @@ public string ResolveFilePath(string originalFileName)
public string ResolveDeterministicPath(string originalFileName)
{
- foreach (var originalPath in _sourceToDeterministicPathMapping)
+ foreach (KeyValuePair> originalPath in _sourceToDeterministicPathMapping)
{
if (originalFileName.StartsWith(originalPath.Key))
{
diff --git a/src/coverlet.core/Instrumentation/CecilAssemblyResolver.cs b/src/coverlet.core/Instrumentation/CecilAssemblyResolver.cs
index 2624475f3..6a781485d 100644
--- a/src/coverlet.core/Instrumentation/CecilAssemblyResolver.cs
+++ b/src/coverlet.core/Instrumentation/CecilAssemblyResolver.cs
@@ -1,8 +1,10 @@
-using System;
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-
using Coverlet.Core.Abstractions;
using Coverlet.Core.Exceptions;
using Microsoft.Extensions.DependencyModel;
@@ -15,21 +17,21 @@ namespace Coverlet.Core.Instrumentation
/// In case of testing different runtime i.e. netfx we could find netstandard.dll in folder.
/// netstandard.dll is a forward only lib, there is no IL but only forwards to "runtime" implementation.
/// For some classes implementation are in different assembly for different runtime for instance:
- ///
+ ///
/// For NetFx 4.7
/// // Token: 0x2700072C RID: 1836
/// .class extern forwarder System.Security.Cryptography.X509Certificates.StoreName
/// {
/// .assembly extern System
- /// }
- ///
+ /// }
+ ///
/// For netcoreapp2.2
/// Token: 0x2700072C RID: 1836
/// .class extern forwarder System.Security.Cryptography.X509Certificates.StoreName
/// {
/// .assembly extern System.Security.Cryptography.X509Certificates
/// }
- ///
+ ///
/// There is a concrete possibility that Cecil cannot find implementation and throws StackOverflow exception https://github.com/jbevain/cecil/issues/575
/// This custom resolver check if requested lib is a "official" netstandard.dll and load once of "current runtime" with
/// correct forwards.
@@ -37,10 +39,10 @@ namespace Coverlet.Core.Instrumentation
///
internal class NetstandardAwareAssemblyResolver : DefaultAssemblyResolver
{
- private static readonly System.Reflection.Assembly _netStandardAssembly;
- private static readonly string _name;
- private static readonly byte[] _publicKeyToken;
- private static readonly AssemblyDefinition _assemblyDefinition;
+ private static readonly System.Reflection.Assembly s_netStandardAssembly;
+ private static readonly string s_name;
+ private static readonly byte[] s_publicKeyToken;
+ private static readonly AssemblyDefinition s_assemblyDefinition;
private readonly string _modulePath;
private readonly Lazy _compositeResolver;
@@ -51,11 +53,11 @@ static NetstandardAwareAssemblyResolver()
try
{
// To be sure to load information of "real" runtime netstandard implementation
- _netStandardAssembly = System.Reflection.Assembly.LoadFile(Path.Combine(Path.GetDirectoryName(typeof(object).Assembly.Location), "netstandard.dll"));
- System.Reflection.AssemblyName name = _netStandardAssembly.GetName();
- _name = name.Name;
- _publicKeyToken = name.GetPublicKeyToken();
- _assemblyDefinition = AssemblyDefinition.ReadAssembly(_netStandardAssembly.Location);
+ s_netStandardAssembly = System.Reflection.Assembly.LoadFile(Path.Combine(Path.GetDirectoryName(typeof(object).Assembly.Location), "netstandard.dll"));
+ System.Reflection.AssemblyName name = s_netStandardAssembly.GetName();
+ s_name = name.Name;
+ s_publicKeyToken = name.GetPublicKeyToken();
+ s_assemblyDefinition = AssemblyDefinition.ReadAssembly(s_netStandardAssembly.Location);
}
catch (FileNotFoundException)
{
@@ -68,7 +70,7 @@ public NetstandardAwareAssemblyResolver(string modulePath, ILogger logger)
_modulePath = modulePath;
_logger = logger;
- // this is lazy because we cannot create AspNetCoreSharedFrameworkResolver if not on .NET Core runtime,
+ // this is lazy because we cannot create AspNetCoreSharedFrameworkResolver if not on .NET Core runtime,
// runtime folders are different
_compositeResolver = new Lazy(() => new CompositeCompilationAssemblyResolver(new ICompilationAssemblyResolver[]
{
@@ -80,26 +82,26 @@ public NetstandardAwareAssemblyResolver(string modulePath, ILogger logger)
}
// Check name and public key but not version that could be different
- private bool CheckIfSearchingNetstandard(AssemblyNameReference name)
+ private static bool CheckIfSearchingNetstandard(AssemblyNameReference name)
{
- if (_netStandardAssembly is null)
+ if (s_netStandardAssembly is null)
{
return false;
}
- if (_name != name.Name)
+ if (s_name != name.Name)
{
return false;
}
- if (name.PublicKeyToken.Length != _publicKeyToken.Length)
+ if (name.PublicKeyToken.Length != s_publicKeyToken.Length)
{
return false;
}
for (int i = 0; i < name.PublicKeyToken.Length; i++)
{
- if (_publicKeyToken[i] != name.PublicKeyToken[i])
+ if (s_publicKeyToken[i] != name.PublicKeyToken[i])
{
return false;
}
@@ -112,7 +114,7 @@ public override AssemblyDefinition Resolve(AssemblyNameReference name)
{
if (CheckIfSearchingNetstandard(name))
{
- return _assemblyDefinition;
+ return s_assemblyDefinition;
}
else
{
@@ -134,21 +136,21 @@ public override AssemblyDefinition Resolve(AssemblyNameReference name)
}
}
- private bool IsDotNetCore()
+ private static bool IsDotNetCore()
{
// object for .NET Framework is inside mscorlib.dll
return Path.GetFileName(typeof(object).Assembly.Location) == "System.Private.CoreLib.dll";
}
///
- ///
+ ///
/// We try to manually load assembly.
/// To work test project needs to use
///
///
/// true
///
- ///
+ ///
/// Runtime configuration file doc https://github.com/dotnet/cli/blob/master/Documentation/specs/runtime-configuration-file.md
///
///
@@ -167,8 +169,8 @@ internal AssemblyDefinition TryWithCustomResolverOnDotNetCore(AssemblyNameRefere
throw new AssemblyResolutionException(name);
}
- using DependencyContextJsonReader contextJsonReader = new DependencyContextJsonReader();
- Dictionary> libraries = new Dictionary>();
+ using var contextJsonReader = new DependencyContextJsonReader();
+ var libraries = new Dictionary>();
foreach (string fileName in Directory.GetFiles(Path.GetDirectoryName(_modulePath), "*.deps.json"))
{
@@ -200,7 +202,7 @@ internal AssemblyDefinition TryWithCustomResolverOnDotNetCore(AssemblyNameRefere
catch (Exception ex)
{
// if we don't find a lib go on
- _logger.LogVerbose($"TryWithCustomResolverOnDotNetCore exception: {ex.ToString()}");
+ _logger.LogVerbose($"TryWithCustomResolverOnDotNetCore exception: {ex}");
}
}
}
@@ -216,8 +218,8 @@ internal AssemblyDefinition TryWithCustomResolverOnDotNetCore(AssemblyNameRefere
internal class AspNetCoreSharedFrameworkResolver : ICompilationAssemblyResolver
{
- private readonly string[] _aspNetSharedFrameworkDirs = null;
- private readonly ILogger _logger = null;
+ private readonly string[] _aspNetSharedFrameworkDirs;
+ private readonly ILogger _logger;
public AspNetCoreSharedFrameworkResolver(ILogger logger)
{
@@ -248,7 +250,7 @@ public bool TryResolveAssemblyPaths(CompilationLibrary library, List ass
continue;
}
- foreach (var file in Directory.GetFiles(sharedFrameworkPath))
+ foreach (string file in Directory.GetFiles(sharedFrameworkPath))
{
if (Path.GetFileName(file).Equals(dllName, StringComparison.OrdinalIgnoreCase))
{
diff --git a/src/coverlet.core/Instrumentation/Instrumenter.cs b/src/coverlet.core/Instrumentation/Instrumenter.cs
index cf80e90a3..df43496c9 100644
--- a/src/coverlet.core/Instrumentation/Instrumenter.cs
+++ b/src/coverlet.core/Instrumentation/Instrumenter.cs
@@ -1,3 +1,6 @@
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -5,11 +8,11 @@
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
-
-using Coverlet.Core.Instrumentation.Reachability;
using Coverlet.Core.Abstractions;
using Coverlet.Core.Attributes;
+using Coverlet.Core.Enums;
using Coverlet.Core.Helpers;
+using Coverlet.Core.Instrumentation.Reachability;
using Coverlet.Core.Symbols;
using Microsoft.Extensions.FileSystemGlobbing;
using Mono.Cecil;
@@ -31,6 +34,8 @@ internal class Instrumenter
private readonly IFileSystem _fileSystem;
private readonly ISourceRootTranslator _sourceRootTranslator;
private readonly ICecilSymbolHelper _cecilSymbolHelper;
+ private readonly string[] _doesNotReturnAttributes;
+ private readonly AssemblySearchType _excludeAssembliesWithoutSources;
private InstrumenterResult _result;
private FieldDefinition _customTrackerHitsArray;
private FieldDefinition _customTrackerHitsFilePath;
@@ -43,11 +48,11 @@ internal class Instrumenter
private List _excludedSourceFiles;
private List _branchesInCompiledGeneratedClass;
private List<(MethodDefinition, int)> _excludedMethods;
+ private List _excludedLambdaMethods;
private List _excludedCompilerGeneratedTypes;
- private readonly string[] _doesNotReturnAttributes;
private ReachabilityHelper _reachabilityHelper;
- public bool SkipModule { get; set; } = false;
+ public bool SkipModule { get; set; }
public Instrumenter(
string module,
@@ -71,6 +76,16 @@ public Instrumenter(
_sourceRootTranslator = sourceRootTranslator;
_cecilSymbolHelper = cecilSymbolHelper;
_doesNotReturnAttributes = PrepareAttributes(parameters.DoesNotReturnAttributes);
+ _excludeAssembliesWithoutSources = DetermineHeuristics(parameters.ExcludeAssembliesWithoutSources);
+ }
+
+ private AssemblySearchType DetermineHeuristics(string parametersExcludeAssembliesWithoutSources)
+ {
+ if (Enum.TryParse(parametersExcludeAssembliesWithoutSources, true, out AssemblySearchType option))
+ {
+ return option;
+ }
+ return AssemblySearchType.MissingAll;
}
private static string[] PrepareAttributes(IEnumerable providedAttrs, params string[] defaultAttrs)
@@ -91,29 +106,18 @@ public bool CanInstrument()
{
if (_instrumentationHelper.HasPdb(_module, out bool embeddedPdb))
{
+ if (_excludeAssembliesWithoutSources.Equals(AssemblySearchType.None))
+ {
+ return true;
+ }
+
if (embeddedPdb)
{
- if (_instrumentationHelper.EmbeddedPortablePdbHasLocalSource(_module, out string firstNotFoundDocument))
- {
- return true;
- }
- else
- {
- _logger.LogVerbose($"Unable to instrument module: {_module}, embedded pdb without local source files, [{FileSystem.EscapeFileName(firstNotFoundDocument)}]");
- return false;
- }
+ return _instrumentationHelper.EmbeddedPortablePdbHasLocalSource(_module, _excludeAssembliesWithoutSources);
}
else
{
- if (_instrumentationHelper.PortablePdbHasLocalSource(_module, out string firstNotFoundDocument))
- {
- return true;
- }
- else
- {
- _logger.LogVerbose($"Unable to instrument module: {_module}, pdb without local source files, [{FileSystem.EscapeFileName(firstNotFoundDocument)}]");
- return false;
- }
+ return _instrumentationHelper.PortablePdbHasLocalSource(_module, _excludeAssembliesWithoutSources);
}
}
else
@@ -183,163 +187,155 @@ private bool Is_System_Threading_Interlocked_CoreLib_Type(TypeDefinition type)
// locking issues if we do it while writing.
private void CreateReachabilityHelper()
{
- using (var stream = _fileSystem.NewFileStream(_module, FileMode.Open, FileAccess.Read))
- using (var resolver = new NetstandardAwareAssemblyResolver(_module, _logger))
+ using Stream stream = _fileSystem.NewFileStream(_module, FileMode.Open, FileAccess.Read);
+ using var resolver = new NetstandardAwareAssemblyResolver(_module, _logger);
+ resolver.AddSearchDirectory(Path.GetDirectoryName(_module));
+ var parameters = new ReaderParameters { ReadSymbols = true, AssemblyResolver = resolver };
+ if (_isCoreLibrary)
{
- resolver.AddSearchDirectory(Path.GetDirectoryName(_module));
- var parameters = new ReaderParameters { ReadSymbols = true, AssemblyResolver = resolver };
- if (_isCoreLibrary)
- {
- parameters.MetadataImporterProvider = new CoreLibMetadataImporterProvider();
- }
-
- using (var module = ModuleDefinition.ReadModule(stream, parameters))
- {
- _reachabilityHelper = ReachabilityHelper.CreateForModule(module, _doesNotReturnAttributes, _logger);
- }
+ parameters.MetadataImporterProvider = new CoreLibMetadataImporterProvider();
}
+
+ using var module = ModuleDefinition.ReadModule(stream, parameters);
+ _reachabilityHelper = ReachabilityHelper.CreateForModule(module, _doesNotReturnAttributes, _logger);
}
private void InstrumentModule()
{
CreateReachabilityHelper();
- using (var stream = _fileSystem.NewFileStream(_module, FileMode.Open, FileAccess.ReadWrite))
- using (var resolver = new NetstandardAwareAssemblyResolver(_module, _logger))
+ using Stream stream = _fileSystem.NewFileStream(_module, FileMode.Open, FileAccess.ReadWrite);
+ using var resolver = new NetstandardAwareAssemblyResolver(_module, _logger);
+ resolver.AddSearchDirectory(Path.GetDirectoryName(_module));
+ var parameters = new ReaderParameters { ReadSymbols = true, AssemblyResolver = resolver };
+ if (_isCoreLibrary)
{
- resolver.AddSearchDirectory(Path.GetDirectoryName(_module));
- var parameters = new ReaderParameters { ReadSymbols = true, AssemblyResolver = resolver };
- if (_isCoreLibrary)
+ parameters.MetadataImporterProvider = new CoreLibMetadataImporterProvider();
+ }
+
+ using var module = ModuleDefinition.ReadModule(stream, parameters);
+ foreach (CustomAttribute customAttribute in module.Assembly.CustomAttributes)
+ {
+ if (IsExcludeAttribute(customAttribute))
{
- parameters.MetadataImporterProvider = new CoreLibMetadataImporterProvider();
+ _logger.LogVerbose($"Excluded module: '{module}' for assembly level attribute {customAttribute.AttributeType.FullName}");
+ SkipModule = true;
+ return;
}
+ }
- using (var module = ModuleDefinition.ReadModule(stream, parameters))
- {
- foreach (CustomAttribute customAttribute in module.Assembly.CustomAttributes)
- {
- if (IsExcludeAttribute(customAttribute))
- {
- _logger.LogVerbose($"Excluded module: '{module}' for assembly level attribute {customAttribute.AttributeType.FullName}");
- SkipModule = true;
- return;
- }
- }
+ bool containsAppContext = module.GetType(nameof(System), nameof(AppContext)) != null;
+ IEnumerable types = module.GetTypes();
+ AddCustomModuleTrackerToModule(module);
- var containsAppContext = module.GetType(nameof(System), nameof(AppContext)) != null;
- var types = module.GetTypes();
- AddCustomModuleTrackerToModule(module);
+ CustomDebugInformation sourceLinkDebugInfo = module.CustomDebugInformations.FirstOrDefault(c => c.Kind == CustomDebugInformationKind.SourceLink);
+ if (sourceLinkDebugInfo != null)
+ {
+ _result.SourceLink = ((SourceLinkDebugInformation)sourceLinkDebugInfo).Content;
+ }
- var sourceLinkDebugInfo = module.CustomDebugInformations.FirstOrDefault(c => c.Kind == CustomDebugInformationKind.SourceLink);
- if (sourceLinkDebugInfo != null)
+ foreach (TypeDefinition type in types)
+ {
+ if (
+ !Is_System_Threading_Interlocked_CoreLib_Type(type) &&
+ !IsTypeExcluded(type) &&
+ _instrumentationHelper.IsTypeIncluded(_module, type.FullName, _parameters.IncludeFilters)
+ )
+ {
+ if (IsSynthesizedMemberToBeExcluded(type))
{
- _result.SourceLink = ((SourceLinkDebugInformation)sourceLinkDebugInfo).Content;
+ (_excludedCompilerGeneratedTypes ??= new List()).Add(type.FullName);
}
-
- foreach (TypeDefinition type in types)
+ else
{
- if (
- !Is_System_Threading_Interlocked_CoreLib_Type(type) &&
- !IsTypeExcluded(type) &&
- _instrumentationHelper.IsTypeIncluded(_module, type.FullName, _parameters.IncludeFilters)
- )
- {
- if (IsSynthesizedMemberToBeExcluded(type))
- {
- (_excludedCompilerGeneratedTypes ??= new List()).Add(type.FullName);
- }
- else
- {
- InstrumentType(type);
- }
- }
+ InstrumentType(type);
}
+ }
+ }
- // Fixup the custom tracker class constructor, according to all instrumented types
- if (_customTrackerRegisterUnloadEventsMethod == null)
- {
- _customTrackerRegisterUnloadEventsMethod = new MethodReference(
- nameof(ModuleTrackerTemplate.RegisterUnloadEvents), module.TypeSystem.Void, _customTrackerTypeDef);
- }
+ // Fixup the custom tracker class constructor, according to all instrumented types
+ if (_customTrackerRegisterUnloadEventsMethod == null)
+ {
+ _customTrackerRegisterUnloadEventsMethod = new MethodReference(
+ nameof(ModuleTrackerTemplate.RegisterUnloadEvents), module.TypeSystem.Void, _customTrackerTypeDef);
+ }
- Instruction lastInstr = _customTrackerClassConstructorIl.Body.Instructions.Last();
+ Instruction lastInstr = _customTrackerClassConstructorIl.Body.Instructions.Last();
- if (!containsAppContext)
- {
- // For "normal" cases, where the instrumented assembly is not the core library, we add a call to
- // RegisterUnloadEvents to the static constructor of the generated custom tracker. Due to static
- // initialization constraints, the core library is handled separately below.
- _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Call, _customTrackerRegisterUnloadEventsMethod));
- }
+ if (!containsAppContext)
+ {
+ // For "normal" cases, where the instrumented assembly is not the core library, we add a call to
+ // RegisterUnloadEvents to the static constructor of the generated custom tracker. Due to static
+ // initialization constraints, the core library is handled separately below.
+ _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Call, _customTrackerRegisterUnloadEventsMethod));
+ }
- _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Ldc_I4, _result.HitCandidates.Count));
- _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Newarr, module.TypeSystem.Int32));
- _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Stsfld, _customTrackerHitsArray));
- _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Ldstr, _result.HitsFilePath));
- _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Stsfld, _customTrackerHitsFilePath));
- _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(_parameters.SingleHit ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0));
- _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Stsfld, _customTrackerSingleHit));
- _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Ldc_I4_1));
- _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Stsfld, _customTrackerFlushHitFile));
-
- if (containsAppContext)
+ _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Ldc_I4, _result.HitCandidates.Count));
+ _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Newarr, module.TypeSystem.Int32));
+ _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Stsfld, _customTrackerHitsArray));
+ _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Ldstr, _result.HitsFilePath));
+ _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Stsfld, _customTrackerHitsFilePath));
+ _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(_parameters.SingleHit ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0));
+ _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Stsfld, _customTrackerSingleHit));
+ _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Ldc_I4_1));
+ _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Stsfld, _customTrackerFlushHitFile));
+
+ if (containsAppContext)
+ {
+ // Handle the core library by instrumenting System.AppContext.OnProcessExit to directly call
+ // the UnloadModule method of the custom tracker type. This avoids loops between the static
+ // initialization of the custom tracker and the static initialization of the hosting AppDomain
+ // (which for the core library case will be instrumented code).
+ var eventArgsType = new TypeReference(nameof(System), nameof(EventArgs), module, module.TypeSystem.CoreLibrary);
+ var customTrackerUnloadModule = new MethodReference(nameof(ModuleTrackerTemplate.UnloadModule), module.TypeSystem.Void, _customTrackerTypeDef);
+ customTrackerUnloadModule.Parameters.Add(new ParameterDefinition(module.TypeSystem.Object));
+ customTrackerUnloadModule.Parameters.Add(new ParameterDefinition(eventArgsType));
+
+ var appContextType = new TypeReference(nameof(System), nameof(AppContext), module, module.TypeSystem.CoreLibrary);
+ MethodDefinition onProcessExitMethod = new MethodReference("OnProcessExit", module.TypeSystem.Void, appContextType).Resolve();
+ ILProcessor onProcessExitIl = onProcessExitMethod.Body.GetILProcessor();
+
+ // Put the OnProcessExit body inside try/finally to ensure the call to the UnloadModule.
+ Instruction lastInst = onProcessExitMethod.Body.Instructions.Last();
+ var firstNullParam = Instruction.Create(OpCodes.Ldnull);
+ var secondNullParam = Instruction.Create(OpCodes.Ldnull);
+ var callUnload = Instruction.Create(OpCodes.Call, customTrackerUnloadModule);
+ onProcessExitIl.InsertAfter(lastInst, firstNullParam);
+ onProcessExitIl.InsertAfter(firstNullParam, secondNullParam);
+ onProcessExitIl.InsertAfter(secondNullParam, callUnload);
+ var endFinally = Instruction.Create(OpCodes.Endfinally);
+ onProcessExitIl.InsertAfter(callUnload, endFinally);
+ Instruction ret = onProcessExitIl.Create(OpCodes.Ret);
+ Instruction leaveAfterFinally = onProcessExitIl.Create(OpCodes.Leave, ret);
+ onProcessExitIl.InsertAfter(endFinally, ret);
+ foreach (Instruction inst in onProcessExitMethod.Body.Instructions.ToArray())
+ {
+ // Patch ret to leave after the finally
+ if (inst.OpCode == OpCodes.Ret && inst != ret)
{
- // Handle the core library by instrumenting System.AppContext.OnProcessExit to directly call
- // the UnloadModule method of the custom tracker type. This avoids loops between the static
- // initialization of the custom tracker and the static initialization of the hosting AppDomain
- // (which for the core library case will be instrumented code).
- var eventArgsType = new TypeReference(nameof(System), nameof(EventArgs), module, module.TypeSystem.CoreLibrary);
- var customTrackerUnloadModule = new MethodReference(nameof(ModuleTrackerTemplate.UnloadModule), module.TypeSystem.Void, _customTrackerTypeDef);
- customTrackerUnloadModule.Parameters.Add(new ParameterDefinition(module.TypeSystem.Object));
- customTrackerUnloadModule.Parameters.Add(new ParameterDefinition(eventArgsType));
-
- var appContextType = new TypeReference(nameof(System), nameof(AppContext), module, module.TypeSystem.CoreLibrary);
- var onProcessExitMethod = new MethodReference("OnProcessExit", module.TypeSystem.Void, appContextType).Resolve();
- var onProcessExitIl = onProcessExitMethod.Body.GetILProcessor();
-
- // Put the OnProcessExit body inside try/finally to ensure the call to the UnloadModule.
- var lastInst = onProcessExitMethod.Body.Instructions.Last();
- var firstNullParam = Instruction.Create(OpCodes.Ldnull);
- var secondNullParam = Instruction.Create(OpCodes.Ldnull);
- var callUnload = Instruction.Create(OpCodes.Call, customTrackerUnloadModule);
- onProcessExitIl.InsertAfter(lastInst, firstNullParam);
- onProcessExitIl.InsertAfter(firstNullParam, secondNullParam);
- onProcessExitIl.InsertAfter(secondNullParam, callUnload);
- var endFinally = Instruction.Create(OpCodes.Endfinally);
- onProcessExitIl.InsertAfter(callUnload, endFinally);
- var ret = onProcessExitIl.Create(OpCodes.Ret);
- var leaveAfterFinally = onProcessExitIl.Create(OpCodes.Leave, ret);
- onProcessExitIl.InsertAfter(endFinally, ret);
- foreach (var inst in onProcessExitMethod.Body.Instructions.ToArray())
- {
- // Patch ret to leave after the finally
- if (inst.OpCode == OpCodes.Ret && inst != ret)
- {
- var leaveBodyInstAfterFinally = onProcessExitIl.Create(OpCodes.Leave, ret);
- var prevInst = inst.Previous;
- onProcessExitMethod.Body.Instructions.Remove(inst);
- onProcessExitIl.InsertAfter(prevInst, leaveBodyInstAfterFinally);
- }
- }
- var handler = new ExceptionHandler(ExceptionHandlerType.Finally)
- {
- TryStart = onProcessExitIl.Body.Instructions.First(),
- TryEnd = firstNullParam,
- HandlerStart = firstNullParam,
- HandlerEnd = ret
- };
-
- onProcessExitMethod.Body.ExceptionHandlers.Add(handler);
+ Instruction leaveBodyInstAfterFinally = onProcessExitIl.Create(OpCodes.Leave, ret);
+ Instruction prevInst = inst.Previous;
+ onProcessExitMethod.Body.Instructions.Remove(inst);
+ onProcessExitIl.InsertAfter(prevInst, leaveBodyInstAfterFinally);
}
-
- module.Write(stream, new WriterParameters { WriteSymbols = true });
}
+ var handler = new ExceptionHandler(ExceptionHandlerType.Finally)
+ {
+ TryStart = onProcessExitIl.Body.Instructions.First(),
+ TryEnd = firstNullParam,
+ HandlerStart = firstNullParam,
+ HandlerEnd = ret
+ };
+
+ onProcessExitMethod.Body.ExceptionHandlers.Add(handler);
}
+
+ module.Write(stream, new WriterParameters { WriteSymbols = true });
}
private void AddCustomModuleTrackerToModule(ModuleDefinition module)
{
- using (AssemblyDefinition coverletInstrumentationAssembly = AssemblyDefinition.ReadAssembly(typeof(ModuleTrackerTemplate).Assembly.Location))
+ using (var coverletInstrumentationAssembly = AssemblyDefinition.ReadAssembly(typeof(ModuleTrackerTemplate).Assembly.Location))
{
TypeDefinition moduleTrackerTemplate = coverletInstrumentationAssembly.MainModule.GetType(
"Coverlet.Core.Instrumentation", nameof(ModuleTrackerTemplate));
@@ -367,14 +363,14 @@ private void AddCustomModuleTrackerToModule(ModuleDefinition module)
foreach (MethodDefinition methodDef in moduleTrackerTemplate.Methods)
{
- MethodDefinition methodOnCustomType = new MethodDefinition(methodDef.Name, methodDef.Attributes, methodDef.ReturnType);
+ var methodOnCustomType = new MethodDefinition(methodDef.Name, methodDef.Attributes, methodDef.ReturnType);
- foreach (var parameter in methodDef.Parameters)
+ foreach (ParameterDefinition parameter in methodDef.Parameters)
{
methodOnCustomType.Parameters.Add(new ParameterDefinition(module.ImportReference(parameter.ParameterType)));
}
- foreach (var variable in methodDef.Body.Variables)
+ foreach (VariableDefinition variable in methodDef.Body.Variables)
{
methodOnCustomType.Body.Variables.Add(new VariableDefinition(module.ImportReference(variable.VariableType)));
}
@@ -398,7 +394,7 @@ private void AddCustomModuleTrackerToModule(ModuleDefinition module)
{
// Move to the custom type
var updatedMethodReference = new MethodReference(methodReference.Name, methodReference.ReturnType, _customTrackerTypeDef);
- foreach (var parameter in methodReference.Parameters)
+ foreach (ParameterDefinition parameter in methodReference.Parameters)
updatedMethodReference.Parameters.Add(new ParameterDefinition(parameter.Name, parameter.Attributes, module.ImportReference(parameter.ParameterType)));
instr.Operand = updatedMethodReference;
@@ -416,7 +412,7 @@ private void AddCustomModuleTrackerToModule(ModuleDefinition module)
ilProcessor.Append(instr);
}
- foreach (var handler in methodDef.Body.ExceptionHandlers)
+ foreach (ExceptionHandler handler in methodDef.Body.ExceptionHandlers)
{
if (handler.CatchType != null)
{
@@ -469,12 +465,12 @@ private bool IsMethodOfCompilerGeneratedClassOfAsyncStateMachineToBeExcluded(Met
private void InstrumentType(TypeDefinition type)
{
- var methods = type.GetMethods();
+ IEnumerable methods = type.GetMethods();
// We keep ordinal index because it's the way used by compiler for generated types/methods to
// avoid ambiguity
int ordinal = -1;
- foreach (var method in methods)
+ foreach (MethodDefinition method in methods)
{
MethodDefinition actualMethod = method;
IEnumerable customAttributes = method.CustomAttributes;
@@ -506,18 +502,24 @@ private void InstrumentType(TypeDefinition type)
continue;
}
+ if (_excludedLambdaMethods != null && _excludedLambdaMethods.Contains(method.FullName))
+ {
+ continue;
+ }
+
if (!customAttributes.Any(IsExcludeAttribute))
{
InstrumentMethod(method);
}
else
{
+ (_excludedLambdaMethods ??= new List()).AddRange(CollectLambdaMethodsInsideLocalFunction(method));
(_excludedMethods ??= new List<(MethodDefinition, int)>()).Add((method, ordinal));
}
}
- var ctors = type.GetConstructors();
- foreach (var ctor in ctors)
+ IEnumerable ctors = type.GetConstructors();
+ foreach (MethodDefinition ctor in ctors)
{
if (!ctor.CustomAttributes.Any(IsExcludeAttribute))
{
@@ -528,7 +530,10 @@ private void InstrumentType(TypeDefinition type)
private void InstrumentMethod(MethodDefinition method)
{
- var sourceFile = method.DebugInformation.SequencePoints.Select(s => _sourceRootTranslator.ResolveFilePath(s.Document.Url)).FirstOrDefault();
+ string sourceFile = method.DebugInformation.SequencePoints.Select(s => _sourceRootTranslator.ResolveFilePath(s.Document.Url)).FirstOrDefault();
+
+ if (string.IsNullOrEmpty(sourceFile)) return;
+
if (!string.IsNullOrEmpty(sourceFile) && _excludedFilesHelper.Exclude(sourceFile))
{
if (!(_excludedSourceFiles ??= new List()).Contains(sourceFile))
@@ -538,7 +543,7 @@ private void InstrumentMethod(MethodDefinition method)
return;
}
- var methodBody = GetMethodBody(method);
+ MethodBody methodBody = GetMethodBody(method);
if (methodBody == null)
return;
@@ -563,29 +568,30 @@ private void InstrumentIL(MethodDefinition method)
{
method.Body.SimplifyMacros();
ILProcessor processor = method.Body.GetILProcessor();
- var index = 0;
- var count = processor.Body.Instructions.Count;
- var branchPoints = _cecilSymbolHelper.GetBranchPoints(method);
- var unreachableRanges = _reachabilityHelper.FindUnreachableIL(processor.Body.Instructions, processor.Body.ExceptionHandlers);
- var currentUnreachableRangeIx = 0;
+ int index = 0;
+ int count = processor.Body.Instructions.Count;
+ IReadOnlyList branchPoints = _cecilSymbolHelper.GetBranchPoints(method);
+ IDictionary targetsMap = new Dictionary();
+ System.Collections.Immutable.ImmutableArray unreachableRanges = _reachabilityHelper.FindUnreachableIL(processor.Body.Instructions, processor.Body.ExceptionHandlers);
+ int currentUnreachableRangeIx = 0;
for (int n = 0; n < count; n++)
{
- var currentInstruction = processor.Body.Instructions[index];
- var sequencePoint = method.DebugInformation.GetSequencePoint(currentInstruction);
- var targetedBranchPoints = branchPoints.Where(p => p.EndOffset == currentInstruction.Offset);
+ Instruction currentInstruction = processor.Body.Instructions[index];
+ SequencePoint sequencePoint = method.DebugInformation.GetSequencePoint(currentInstruction);
+ IEnumerable targetedBranchPoints = branchPoints.Where(p => p.EndOffset == currentInstruction.Offset);
// make sure we're looking at the correct unreachable range (if any)
- var instrOffset = currentInstruction.Offset;
+ int instrOffset = currentInstruction.Offset;
while (currentUnreachableRangeIx < unreachableRanges.Length && instrOffset > unreachableRanges[currentUnreachableRangeIx].EndOffset)
{
currentUnreachableRangeIx++;
}
// determine if the unreachable
- var isUnreachable = false;
+ bool isUnreachable = false;
if (currentUnreachableRangeIx < unreachableRanges.Length)
{
- var range = unreachableRanges[currentUnreachableRangeIx];
+ ReachabilityHelper.UnreachableRange range = unreachableRanges[currentUnreachableRangeIx];
isUnreachable = instrOffset >= range.StartOffset && instrOffset <= range.EndOffset;
}
@@ -604,17 +610,13 @@ private void InstrumentIL(MethodDefinition method)
continue;
}
- var firstInjectedInstrumentedOpCode = AddInstrumentationCode(method, processor, currentInstruction, sequencePoint);
- foreach (var bodyInstruction in processor.Body.Instructions)
- ReplaceInstructionTarget(bodyInstruction, currentInstruction, firstInjectedInstrumentedOpCode);
-
- foreach (ExceptionHandler handler in processor.Body.ExceptionHandlers)
- ReplaceExceptionHandlerBoundary(handler, currentInstruction, firstInjectedInstrumentedOpCode);
+ Instruction firstInjectedInstrumentedOpCode = AddInstrumentationCode(method, processor, currentInstruction, sequencePoint);
+ targetsMap.Add(currentInstruction.Offset, firstInjectedInstrumentedOpCode);
index += 2;
}
- foreach (var branchTarget in targetedBranchPoints)
+ foreach (BranchPoint branchTarget in targetedBranchPoints)
{
/*
* Skip branches with no sequence point reference for now.
@@ -625,12 +627,9 @@ private void InstrumentIL(MethodDefinition method)
if (branchTarget.StartLine == -1 || branchTarget.Document == null)
continue;
- var firstInjectedInstrumentedOpCode = AddInstrumentationCode(method, processor, currentInstruction, branchTarget);
- foreach (var bodyInstruction in processor.Body.Instructions)
- ReplaceInstructionTarget(bodyInstruction, currentInstruction, firstInjectedInstrumentedOpCode);
-
- foreach (ExceptionHandler handler in processor.Body.ExceptionHandlers)
- ReplaceExceptionHandlerBoundary(handler, currentInstruction, firstInjectedInstrumentedOpCode);
+ Instruction firstInjectedInstrumentedOpCode = AddInstrumentationCode(method, processor, currentInstruction, branchTarget);
+ if (!targetsMap.ContainsKey(currentInstruction.Offset))
+ targetsMap.Add(currentInstruction.Offset, firstInjectedInstrumentedOpCode);
index += 2;
}
@@ -638,12 +637,18 @@ private void InstrumentIL(MethodDefinition method)
index++;
}
+ foreach (Instruction bodyInstruction in processor.Body.Instructions)
+ ReplaceInstructionTarget(bodyInstruction, targetsMap);
+
+ foreach (ExceptionHandler handler in processor.Body.ExceptionHandlers)
+ ReplaceExceptionHandlerBoundary(handler, targetsMap);
+
method.Body.OptimizeMacros();
}
private Instruction AddInstrumentationCode(MethodDefinition method, ILProcessor processor, Instruction instruction, SequencePoint sequencePoint)
{
- if (!_result.Documents.TryGetValue(_sourceRootTranslator.ResolveFilePath(sequencePoint.Document.Url), out var document))
+ if (!_result.Documents.TryGetValue(_sourceRootTranslator.ResolveFilePath(sequencePoint.Document.Url), out Document document))
{
document = new Document { Path = _sourceRootTranslator.ResolveFilePath(sequencePoint.Document.Url) };
document.Index = _result.Documents.Count;
@@ -663,14 +668,14 @@ private Instruction AddInstrumentationCode(MethodDefinition method, ILProcessor
private Instruction AddInstrumentationCode(MethodDefinition method, ILProcessor processor, Instruction instruction, BranchPoint branchPoint)
{
- if (!_result.Documents.TryGetValue(_sourceRootTranslator.ResolveFilePath(branchPoint.Document), out var document))
+ if (!_result.Documents.TryGetValue(_sourceRootTranslator.ResolveFilePath(branchPoint.Document), out Document document))
{
document = new Document { Path = _sourceRootTranslator.ResolveFilePath(branchPoint.Document) };
document.Index = _result.Documents.Count;
_result.Documents.Add(document.Path, document);
}
- BranchKey key = new BranchKey(branchPoint.StartLine, (int)branchPoint.Ordinal);
+ var key = new BranchKey(branchPoint.StartLine, (int)branchPoint.Ordinal);
if (!document.Branches.ContainsKey(key))
{
document.Branches.Add(
@@ -738,42 +743,41 @@ private Instruction AddInstrumentationInstructions(MethodDefinition method, ILPr
return indxInstr;
}
- private static void ReplaceInstructionTarget(Instruction instruction, Instruction oldTarget, Instruction newTarget)
+ private static void ReplaceInstructionTarget(Instruction instruction, IDictionary targetsMap)
{
if (instruction.Operand is Instruction operandInstruction)
{
- if (operandInstruction == oldTarget)
+ if (targetsMap.TryGetValue(operandInstruction.Offset, out Instruction newTarget))
{
instruction.Operand = newTarget;
- return;
}
}
else if (instruction.Operand is Instruction[] operandInstructions)
{
for (int i = 0; i < operandInstructions.Length; i++)
{
- if (operandInstructions[i] == oldTarget)
+ if (targetsMap.TryGetValue(operandInstructions[i].Offset, out Instruction newTarget))
operandInstructions[i] = newTarget;
}
}
}
- private static void ReplaceExceptionHandlerBoundary(ExceptionHandler handler, Instruction oldTarget, Instruction newTarget)
+ private static void ReplaceExceptionHandlerBoundary(ExceptionHandler handler, IDictionary targetsMap)
{
- if (handler.FilterStart == oldTarget)
- handler.FilterStart = newTarget;
+ if (handler.FilterStart is not null && targetsMap.TryGetValue(handler.FilterStart.Offset, out Instruction newFilterStart))
+ handler.FilterStart = newFilterStart;
- if (handler.HandlerEnd == oldTarget)
- handler.HandlerEnd = newTarget;
+ if (handler.HandlerEnd is not null && targetsMap.TryGetValue(handler.HandlerEnd.Offset, out Instruction newHandlerEnd))
+ handler.HandlerEnd = newHandlerEnd;
- if (handler.HandlerStart == oldTarget)
- handler.HandlerStart = newTarget;
+ if (handler.HandlerStart is not null && targetsMap.TryGetValue(handler.HandlerStart.Offset, out Instruction newHandlerStart))
+ handler.HandlerStart = newHandlerStart;
- if (handler.TryEnd == oldTarget)
- handler.TryEnd = newTarget;
+ if (handler.TryEnd is not null && targetsMap.TryGetValue(handler.TryEnd.Offset, out Instruction newTryEnd))
+ handler.TryEnd = newTryEnd;
- if (handler.TryStart == oldTarget)
- handler.TryStart = newTarget;
+ if (handler.TryStart is not null && targetsMap.TryGetValue(handler.TryStart.Offset, out Instruction newTryStart))
+ handler.TryStart = newTryStart;
}
private bool IsExcludeAttribute(CustomAttribute customAttribute)
@@ -815,7 +819,7 @@ private bool IsSynthesizedMemberToBeExcluded(IMemberDefinition definition)
}
// Check methods members and compiler generated types
- foreach (var excludedMethods in _excludedMethods)
+ foreach ((MethodDefinition, int) excludedMethods in _excludedMethods)
{
// Exclude this member if declaring type is the same of the excluded method and
// the name is synthesized from the name of the excluded method.
@@ -848,6 +852,19 @@ internal bool IsSynthesizedNameOf(string name, string methodName, int methodOrdi
(name.IndexOf($"<{methodName}>g__") != -1 && name.IndexOf($"|{methodOrdinal}_") != -1);
}
+ private static IEnumerable CollectLambdaMethodsInsideLocalFunction(MethodDefinition methodDefinition)
+ {
+ if (!methodDefinition.Name.Contains(">g__")) yield break;
+
+ foreach (Instruction instruction in methodDefinition.Body.Instructions.ToList())
+ {
+ if (instruction.OpCode == OpCodes.Ldftn && instruction.Operand is MethodReference mr && mr.Name.Contains(">b__"))
+ {
+ yield return mr.FullName;
+ }
+ }
+ }
+
///
/// A custom importer created specifically to allow the instrumentation of System.Private.CoreLib by
/// removing the external references to netstandard that are generated when instrumenting a typical
@@ -862,52 +879,52 @@ public IMetadataImporter GetMetadataImporter(ModuleDefinition module)
private class CoreLibMetadataImporter : IMetadataImporter
{
- private readonly ModuleDefinition module;
- private readonly DefaultMetadataImporter defaultMetadataImporter;
+ private readonly ModuleDefinition _module;
+ private readonly DefaultMetadataImporter _defaultMetadataImporter;
public CoreLibMetadataImporter(ModuleDefinition module)
{
- this.module = module;
- this.defaultMetadataImporter = new DefaultMetadataImporter(module);
+ _module = module;
+ _defaultMetadataImporter = new DefaultMetadataImporter(module);
}
public AssemblyNameReference ImportReference(AssemblyNameReference reference)
{
- return this.defaultMetadataImporter.ImportReference(reference);
+ return _defaultMetadataImporter.ImportReference(reference);
}
public TypeReference ImportReference(TypeReference type, IGenericParameterProvider context)
{
- var importedRef = this.defaultMetadataImporter.ImportReference(type, context);
- importedRef.GetElementType().Scope = module.TypeSystem.CoreLibrary;
+ TypeReference importedRef = _defaultMetadataImporter.ImportReference(type, context);
+ importedRef.GetElementType().Scope = _module.TypeSystem.CoreLibrary;
return importedRef;
}
public FieldReference ImportReference(FieldReference field, IGenericParameterProvider context)
{
- var importedRef = this.defaultMetadataImporter.ImportReference(field, context);
- importedRef.FieldType.GetElementType().Scope = module.TypeSystem.CoreLibrary;
+ FieldReference importedRef = _defaultMetadataImporter.ImportReference(field, context);
+ importedRef.FieldType.GetElementType().Scope = _module.TypeSystem.CoreLibrary;
return importedRef;
}
public MethodReference ImportReference(MethodReference method, IGenericParameterProvider context)
{
- var importedRef = this.defaultMetadataImporter.ImportReference(method, context);
- importedRef.DeclaringType.GetElementType().Scope = module.TypeSystem.CoreLibrary;
+ MethodReference importedRef = _defaultMetadataImporter.ImportReference(method, context);
+ importedRef.DeclaringType.GetElementType().Scope = _module.TypeSystem.CoreLibrary;
- foreach (var parameter in importedRef.Parameters)
+ foreach (ParameterDefinition parameter in importedRef.Parameters)
{
- if (parameter.ParameterType.Scope == module.TypeSystem.CoreLibrary)
+ if (parameter.ParameterType.Scope == _module.TypeSystem.CoreLibrary)
{
continue;
}
- parameter.ParameterType.GetElementType().Scope = module.TypeSystem.CoreLibrary;
+ parameter.ParameterType.GetElementType().Scope = _module.TypeSystem.CoreLibrary;
}
- if (importedRef.ReturnType.Scope != module.TypeSystem.CoreLibrary)
+ if (importedRef.ReturnType.Scope != _module.TypeSystem.CoreLibrary)
{
- importedRef.ReturnType.GetElementType().Scope = module.TypeSystem.CoreLibrary;
+ importedRef.ReturnType.GetElementType().Scope = _module.TypeSystem.CoreLibrary;
}
return importedRef;
@@ -919,14 +936,14 @@ public MethodReference ImportReference(MethodReference method, IGenericParameter
// Exclude files helper https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.filesystemglobbing.matcher?view=aspnetcore-2.2
internal class ExcludedFilesHelper
{
- Matcher _matcher;
+ readonly Matcher _matcher;
public ExcludedFilesHelper(string[] excludes, ILogger logger)
{
if (excludes != null && excludes.Length > 0)
{
_matcher = new Matcher();
- foreach (var excludeRule in excludes)
+ foreach (string excludeRule in excludes)
{
if (excludeRule is null)
{
diff --git a/src/coverlet.core/Instrumentation/InstrumenterResult.cs b/src/coverlet.core/Instrumentation/InstrumenterResult.cs
index a575f7739..69a6ab24a 100644
--- a/src/coverlet.core/Instrumentation/InstrumenterResult.cs
+++ b/src/coverlet.core/Instrumentation/InstrumenterResult.cs
@@ -1,3 +1,6 @@
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -47,11 +50,11 @@ internal class BranchKey : IEquatable
public override bool Equals(object obj) => Equals(obj);
- public bool Equals(BranchKey other) => other is BranchKey branchKey && branchKey.Line == this.Line && branchKey.Ordinal == this.Ordinal;
+ public bool Equals(BranchKey other) => other is BranchKey branchKey && branchKey.Line == Line && branchKey.Ordinal == Ordinal;
public override int GetHashCode()
{
- return (this.Line, this.Ordinal).GetHashCode();
+ return (Line, Ordinal).GetHashCode();
}
}
diff --git a/src/coverlet.core/Instrumentation/ModuleTrackerTemplate.cs b/src/coverlet.core/Instrumentation/ModuleTrackerTemplate.cs
index 8321a704f..600fe91b1 100644
--- a/src/coverlet.core/Instrumentation/ModuleTrackerTemplate.cs
+++ b/src/coverlet.core/Instrumentation/ModuleTrackerTemplate.cs
@@ -1,5 +1,7 @@
-using System;
-using System.Diagnostics;
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Reflection;
@@ -24,8 +26,8 @@ internal static class ModuleTrackerTemplate
public static int[] HitsArray;
public static bool SingleHit;
public static bool FlushHitFile;
- private static readonly bool _enableLog = int.TryParse(Environment.GetEnvironmentVariable("COVERLET_ENABLETRACKERLOG"), out int result) ? result == 1 : false;
- private static string _sessionId = Guid.NewGuid().ToString();
+ private static readonly bool s_enableLog = int.TryParse(Environment.GetEnvironmentVariable("COVERLET_ENABLETRACKERLOG"), out int result) && result == 1;
+ private static readonly string s_sessionId = Guid.NewGuid().ToString();
static ModuleTrackerTemplate()
{
@@ -81,100 +83,94 @@ public static void UnloadModule(object sender, EventArgs e)
{
// The same module can be unloaded multiple times in the same process via different app domains.
// Use a global mutex to ensure no concurrent access.
- using (var mutex = new Mutex(true, Path.GetFileNameWithoutExtension(HitsFilePath) + "_Mutex", out bool createdNew))
+ using var mutex = new Mutex(true, Path.GetFileNameWithoutExtension(HitsFilePath) + "_Mutex", out bool createdNew);
+ if (!createdNew)
{
- if (!createdNew)
- {
- mutex.WaitOne();
- }
+ mutex.WaitOne();
+ }
- if (FlushHitFile)
+ if (FlushHitFile)
+ {
+ try
{
- try
- {
- // Claim the current hits array and reset it to prevent double-counting scenarios.
- int[] hitsArray = Interlocked.Exchange(ref HitsArray, new int[HitsArray.Length]);
+ // Claim the current hits array and reset it to prevent double-counting scenarios.
+ int[] hitsArray = Interlocked.Exchange(ref HitsArray, new int[HitsArray.Length]);
- WriteLog($"Unload called for '{Assembly.GetExecutingAssembly().Location}' by '{sender ?? "null"}'");
- WriteLog($"Flushing hit file '{HitsFilePath}'");
+ WriteLog($"Unload called for '{Assembly.GetExecutingAssembly().Location}' by '{sender ?? "null"}'");
+ WriteLog($"Flushing hit file '{HitsFilePath}'");
- bool failedToCreateNewHitsFile = false;
- try
+ bool failedToCreateNewHitsFile = false;
+ try
+ {
+ using var fs = new FileStream(HitsFilePath, FileMode.CreateNew);
+ using var bw = new BinaryWriter(fs);
+ bw.Write(hitsArray.Length);
+ foreach (int hitCount in hitsArray)
{
- using (var fs = new FileStream(HitsFilePath, FileMode.CreateNew))
- using (var bw = new BinaryWriter(fs))
- {
- bw.Write(hitsArray.Length);
- foreach (int hitCount in hitsArray)
- {
- bw.Write(hitCount);
- }
- }
+ bw.Write(hitCount);
}
- catch (Exception ex)
+ }
+ catch (Exception ex)
+ {
+ WriteLog($"Failed to create new hits file '{HitsFilePath}' -> '{ex.Message}'");
+ failedToCreateNewHitsFile = true;
+ }
+
+ if (failedToCreateNewHitsFile)
+ {
+ // Update the number of hits by adding value on disk with the ones on memory.
+ // This path should be triggered only in the case of multiple AppDomain unloads.
+ using var fs = new FileStream(HitsFilePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
+ using var br = new BinaryReader(fs);
+ using var bw = new BinaryWriter(fs);
+ int hitsLength = br.ReadInt32();
+ WriteLog($"Current hits found '{hitsLength}'");
+
+ if (hitsLength != hitsArray.Length)
{
- WriteLog($"Failed to create new hits file '{HitsFilePath}' -> '{ex.Message}'");
- failedToCreateNewHitsFile = true;
+ throw new InvalidOperationException($"{HitsFilePath} has {hitsLength} entries but on memory {nameof(HitsArray)} has {hitsArray.Length}");
}
- if (failedToCreateNewHitsFile)
+ for (int i = 0; i < hitsLength; ++i)
{
- // Update the number of hits by adding value on disk with the ones on memory.
- // This path should be triggered only in the case of multiple AppDomain unloads.
- using (var fs = new FileStream(HitsFilePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
- using (var br = new BinaryReader(fs))
- using (var bw = new BinaryWriter(fs))
+ int oldHitCount = br.ReadInt32();
+ bw.Seek(-sizeof(int), SeekOrigin.Current);
+ if (SingleHit)
{
- int hitsLength = br.ReadInt32();
- WriteLog($"Current hits found '{hitsLength}'");
-
- if (hitsLength != hitsArray.Length)
- {
- throw new InvalidOperationException($"{HitsFilePath} has {hitsLength} entries but on memory {nameof(HitsArray)} has {hitsArray.Length}");
- }
-
- for (int i = 0; i < hitsLength; ++i)
- {
- int oldHitCount = br.ReadInt32();
- bw.Seek(-sizeof(int), SeekOrigin.Current);
- if (SingleHit)
- {
- bw.Write(hitsArray[i] + oldHitCount > 0 ? 1 : 0);
- }
- else
- {
- bw.Write(hitsArray[i] + oldHitCount);
- }
- }
+ bw.Write(hitsArray[i] + oldHitCount > 0 ? 1 : 0);
+ }
+ else
+ {
+ bw.Write(hitsArray[i] + oldHitCount);
}
}
+ }
- WriteHits(sender);
+ WriteHits(sender);
- WriteLog($"Hit file '{HitsFilePath}' flushed, size {new FileInfo(HitsFilePath).Length}");
- WriteLog("--------------------------------");
- }
- catch (Exception ex)
- {
- WriteLog(ex.ToString());
- throw;
- }
+ WriteLog($"Hit file '{HitsFilePath}' flushed, size {new FileInfo(HitsFilePath).Length}");
+ WriteLog("--------------------------------");
+ }
+ catch (Exception ex)
+ {
+ WriteLog(ex.ToString());
+ throw;
}
-
- // On purpose this is not under a try-finally: it is better to have an exception if there was any error writing the hits file
- // this case is relevant when instrumenting corelib since multiple processes can be running against the same instrumented dll.
- mutex.ReleaseMutex();
}
+
+ // On purpose this is not under a try-finally: it is better to have an exception if there was any error writing the hits file
+ // this case is relevant when instrumenting corelib since multiple processes can be running against the same instrumented dll.
+ mutex.ReleaseMutex();
}
private static void WriteHits(object sender)
{
- if (_enableLog)
+ if (s_enableLog)
{
- Assembly currentAssembly = Assembly.GetExecutingAssembly();
- DirectoryInfo location = new DirectoryInfo(Path.Combine(Path.GetDirectoryName(currentAssembly.Location), "TrackersHitsLog"));
+ var currentAssembly = Assembly.GetExecutingAssembly();
+ var location = new DirectoryInfo(Path.Combine(Path.GetDirectoryName(currentAssembly.Location), "TrackersHitsLog"));
location.Create();
- string logFile = Path.Combine(location.FullName, $"{Path.GetFileName(currentAssembly.Location)}_{DateTime.UtcNow.Ticks}_{_sessionId}.txt");
+ string logFile = Path.Combine(location.FullName, $"{Path.GetFileName(currentAssembly.Location)}_{DateTime.UtcNow.Ticks}_{s_sessionId}.txt");
using (var fs = new FileStream(HitsFilePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
using (var log = new FileStream(logFile, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None))
using (var logWriter = new StreamWriter(log))
@@ -193,12 +189,12 @@ private static void WriteHits(object sender)
private static void WriteLog(string logText)
{
- if (_enableLog)
+ if (s_enableLog)
{
// We don't set path as global var to keep benign possible errors inside try/catch
// I'm not sure that location will be ok in every scenario
string location = Assembly.GetExecutingAssembly().Location;
- File.AppendAllText(Path.Combine(Path.GetDirectoryName(location), Path.GetFileName(location) + "_tracker.txt"), $"[{DateTime.UtcNow} S:{_sessionId} T:{Thread.CurrentThread.ManagedThreadId}]{logText}{Environment.NewLine}");
+ File.AppendAllText(Path.Combine(Path.GetDirectoryName(location), Path.GetFileName(location) + "_tracker.txt"), $"[{DateTime.UtcNow} S:{s_sessionId} T:{Thread.CurrentThread.ManagedThreadId}]{logText}{Environment.NewLine}");
}
}
}
diff --git a/src/coverlet.core/Instrumentation/ReachabilityHelper.cs b/src/coverlet.core/Instrumentation/ReachabilityHelper.cs
index 4d7474ea6..1b4840d85 100644
--- a/src/coverlet.core/Instrumentation/ReachabilityHelper.cs
+++ b/src/coverlet.core/Instrumentation/ReachabilityHelper.cs
@@ -1,10 +1,13 @@
-using Coverlet.Core.Abstractions;
-using Mono.Cecil;
-using Mono.Cecil.Cil;
-using Mono.Collections.Generic;
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
using System;
using System.Collections.Immutable;
using System.Linq;
+using Coverlet.Core.Abstractions;
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+using Mono.Collections.Generic;
namespace Coverlet.Core.Instrumentation.Reachability
{
@@ -97,9 +100,9 @@ private readonly struct BranchInstruction
///
/// Returns true if this branch has multiple targets.
///
- public bool HasMultiTargets => _TargetOffset == -1;
+ public bool HasMultiTargets => _targetOffset == -1;
- private readonly int _TargetOffset;
+ private readonly int _targetOffset;
///
/// Target of the branch, assuming it has a single target.
@@ -115,11 +118,11 @@ public int TargetOffset
throw new InvalidOperationException($"{HasMultiTargets} is true");
}
- return _TargetOffset;
+ return _targetOffset;
}
}
- private readonly ImmutableArray _TargetOffsets;
+ private readonly ImmutableArray _targetOffsets;
///
/// Targets of the branch, assuming it has multiple targets.
@@ -135,15 +138,15 @@ public ImmutableArray TargetOffsets
throw new InvalidOperationException($"{HasMultiTargets} is false");
}
- return _TargetOffsets;
+ return _targetOffsets;
}
}
public BranchInstruction(int offset, int targetOffset)
{
Offset = offset;
- _TargetOffset = targetOffset;
- _TargetOffsets = ImmutableArray.Empty;
+ _targetOffset = targetOffset;
+ _targetOffsets = ImmutableArray.Empty;
}
public BranchInstruction(int offset, ImmutableArray targetOffset)
@@ -154,8 +157,8 @@ public BranchInstruction(int offset, ImmutableArray targetOffset)
}
Offset = offset;
- _TargetOffset = -1;
- _TargetOffsets = targetOffset;
+ _targetOffset = -1;
+ _targetOffsets = targetOffset;
}
public override string ToString()
@@ -166,7 +169,7 @@ public override string ToString()
/// OpCodes that transfer control code, even if they do not
/// introduce branch points.
///
- private static readonly ImmutableHashSet BRANCH_OPCODES =
+ private static readonly ImmutableHashSet s_branchOpCodes =
ImmutableHashSet.CreateRange(
new[]
{
@@ -221,7 +224,7 @@ public override string ToString()
/// OpCodes that unconditionally transfer control, so there
/// is not "fall through" branch target.
///
- private static readonly ImmutableHashSet UNCONDITIONAL_BRANCH_OPCODES =
+ private static readonly ImmutableHashSet s_unconditionalBranchOpCodes =
ImmutableHashSet.CreateRange(
new[]
{
@@ -232,11 +235,11 @@ public override string ToString()
}
);
- private readonly ImmutableHashSet DoesNotReturnMethods;
+ private readonly ImmutableHashSet _doesNotReturnMethods;
private ReachabilityHelper(ImmutableHashSet doesNotReturnMethods)
{
- DoesNotReturnMethods = doesNotReturnMethods;
+ _doesNotReturnMethods = doesNotReturnMethods;
}
///
@@ -252,11 +255,11 @@ public static ReachabilityHelper CreateForModule(ModuleDefinition module, string
return new ReachabilityHelper(ImmutableHashSet.Empty);
}
- var processedMethods = ImmutableHashSet.Empty;
- var doNotReturn = ImmutableHashSet.CreateBuilder();
- foreach (var type in module.Types)
+ ImmutableHashSet processedMethods = ImmutableHashSet.Empty;
+ ImmutableHashSet.Builder doNotReturn = ImmutableHashSet.CreateBuilder();
+ foreach (TypeDefinition type in module.Types)
{
- foreach (var mtd in type.Methods)
+ foreach (MethodDefinition mtd in type.Methods)
{
if (mtd.IsNative)
{
@@ -278,14 +281,14 @@ public static ReachabilityHelper CreateForModule(ModuleDefinition module, string
continue;
}
- foreach (var instr in body.Instructions)
+ foreach (Instruction instr in body.Instructions)
{
- if (!IsCall(instr, out var calledMtd))
+ if (!IsCall(instr, out MethodReference calledMtd))
{
continue;
}
- var token = calledMtd.MetadataToken;
+ MetadataToken token = calledMtd.MetadataToken;
if (processedMethods.Contains(token))
{
continue;
@@ -314,8 +317,8 @@ public static ReachabilityHelper CreateForModule(ModuleDefinition module, string
continue;
}
- var hasDoesNotReturnAttribute = false;
- foreach (var attr in mtdDef.CustomAttributes)
+ bool hasDoesNotReturnAttribute = false;
+ foreach (CustomAttribute attr in mtdDef.CustomAttributes)
{
if (Array.IndexOf(doesNotReturnAttributes, attr.AttributeType.Name) != -1)
{
@@ -333,7 +336,7 @@ public static ReachabilityHelper CreateForModule(ModuleDefinition module, string
}
}
- var doNoReturnTokens = doNotReturn.ToImmutable();
+ ImmutableHashSet doNoReturnTokens = doNotReturn.ToImmutable();
return new ReachabilityHelper(doNoReturnTokens);
}
@@ -369,12 +372,12 @@ public ImmutableArray FindUnreachableIL(Collection.Empty;
}
- var (mayContainUnreachableCode, branches) = AnalyzeInstructions(instrs, exceptionHandlers);
+ (bool mayContainUnreachableCode, ImmutableArray branches) = AnalyzeInstructions(instrs, exceptionHandlers);
// no need to do any more work, nothing unreachable here
if (!mayContainUnreachableCode)
@@ -382,9 +385,9 @@ public ImmutableArray FindUnreachableIL(Collection.Empty;
}
- var lastInstr = instrs[instrs.Count - 1];
+ Instruction lastInstr = instrs[instrs.Count - 1];
- var blocks = CreateBasicBlocks(instrs, exceptionHandlers, branches);
+ ImmutableArray blocks = CreateBasicBlocks(instrs, exceptionHandlers, branches);
DetermineHeadReachability(blocks);
return DetermineUnreachableRanges(blocks, lastInstr.Offset);
@@ -396,16 +399,16 @@ public ImmutableArray FindUnreachableIL(Collection
private (bool MayContainUnreachableCode, ImmutableArray Branches) AnalyzeInstructions(Collection instrs, Collection exceptionHandlers)
{
- var containsDoesNotReturnCall = false;
+ bool containsDoesNotReturnCall = false;
- var ret = ImmutableArray.CreateBuilder();
- foreach (var i in instrs)
+ ImmutableArray.Builder ret = ImmutableArray.CreateBuilder();
+ foreach (Instruction i in instrs)
{
containsDoesNotReturnCall = containsDoesNotReturnCall || DoesNotReturn(i);
- if (BRANCH_OPCODES.Contains(i.OpCode))
+ if (s_branchOpCodes.Contains(i.OpCode))
{
- var (singleTargetOffset, multiTargetOffsets) = GetInstructionTargets(i, exceptionHandlers);
+ (int? singleTargetOffset, ImmutableArray multiTargetOffsets) = GetInstructionTargets(i, exceptionHandlers);
if (singleTargetOffset != null)
{
@@ -435,7 +438,7 @@ private static (int? SingleTargetOffset, ImmutableArray MultiTargetOffsets)
singleTargetOffset = null;
multiTargetOffsets = ImmutableArray.Create(i.Next.Offset);
- foreach (var instr in multiTarget)
+ foreach (Instruction instr in multiTarget)
{
// in practice these are small arrays, so a scan should be fine
if (multiTargetOffsets.Contains(instr.Offset))
@@ -450,7 +453,7 @@ private static (int? SingleTargetOffset, ImmutableArray MultiTargetOffsets)
{
// it's any of the B.*(_S)? or Leave(_S)? instructions
- if (UNCONDITIONAL_BRANCH_OPCODES.Contains(i.OpCode))
+ if (s_unconditionalBranchOpCodes.Contains(i.OpCode))
{
multiTargetOffsets = ImmutableArray.Empty;
singleTargetOffset = targetInstr.Offset;
@@ -467,15 +470,15 @@ private static (int? SingleTargetOffset, ImmutableArray MultiTargetOffsets)
// flow is allowed so we can scan backwards to see find the block
ExceptionHandler filterForHandler = null;
- foreach (var handler in exceptionHandlers)
+ foreach (ExceptionHandler handler in exceptionHandlers)
{
if (handler.FilterStart == null)
{
continue;
}
- var startsAt = handler.FilterStart;
- var cur = startsAt;
+ Instruction startsAt = handler.FilterStart;
+ Instruction cur = startsAt;
while (cur != null && cur.Offset < i.Offset)
{
cur = cur.Next;
@@ -524,15 +527,15 @@ private static (int? SingleTargetOffset, ImmutableArray MultiTargetOffsets)
///
/// Calculates which ranges of IL are unreachable, given blocks which have head and tail reachability calculated.
///
- private ImmutableArray DetermineUnreachableRanges(ImmutableArray blocks, int lastInstructionOffset)
+ private static ImmutableArray DetermineUnreachableRanges(ImmutableArray blocks, int lastInstructionOffset)
{
- var ret = ImmutableArray.CreateBuilder();
+ ImmutableArray.Builder ret = ImmutableArray.CreateBuilder();
- var endOfMethodOffset = lastInstructionOffset + 1; // add 1 so we point _past_ the end of the method
+ int endOfMethodOffset = lastInstructionOffset + 1; // add 1 so we point _past_ the end of the method
- for (var curBlockIx = 0; curBlockIx < blocks.Length; curBlockIx++)
+ for (int curBlockIx = 0; curBlockIx < blocks.Length; curBlockIx++)
{
- var curBlock = blocks[curBlockIx];
+ BasicBlock curBlock = blocks[curBlockIx];
int endOfCurBlockOffset;
if (curBlockIx == blocks.Length - 1)
@@ -553,11 +556,11 @@ private ImmutableArray DetermineUnreachableRanges(ImmutableArr
}
// tail isn't reachable, which means there's a call to something that doesn't return...
- var doesNotReturnInstr = curBlock.UnreachableAfter;
+ Instruction doesNotReturnInstr = curBlock.UnreachableAfter;
// and it's everything _after_ the following instruction that is unreachable
// so record the following instruction through the end of the block
- var followingInstr = doesNotReturnInstr.Next;
+ Instruction followingInstr = doesNotReturnInstr.Next;
ret.Add(new UnreachableRange(followingInstr.Offset, endOfCurBlockOffset));
}
@@ -577,17 +580,17 @@ private ImmutableArray DetermineUnreachableRanges(ImmutableArr
///
/// "Tail reachability" will have already been determined in CreateBlocks.
///
- private void DetermineHeadReachability(ImmutableArray blocks)
+ private static void DetermineHeadReachability(ImmutableArray blocks)
{
var blockLookup = blocks.ToImmutableDictionary(b => b.StartOffset);
- var headBlock = blockLookup[0];
+ BasicBlock headBlock = blockLookup[0];
var knownLive = ImmutableStack.Create(headBlock);
while (!knownLive.IsEmpty)
{
- knownLive = knownLive.Pop(out var block);
+ knownLive = knownLive.Pop(out BasicBlock block);
if (block.HeadReachable)
{
@@ -601,18 +604,18 @@ private void DetermineHeadReachability(ImmutableArray blocks)
if (block.TailReachable)
{
// we can reach all the blocks it might flow to
- foreach (var reachableOffset in block.BranchesTo)
+ foreach (int reachableOffset in block.BranchesTo)
{
- var reachableBlock = blockLookup[reachableOffset];
+ BasicBlock reachableBlock = blockLookup[reachableOffset];
knownLive = knownLive.Push(reachableBlock);
}
}
// if the block is covered by an exception handler, then executing _any_ instruction in it
// could conceivably cause those handlers to be visited
- foreach (var exceptionHandlerOffset in block.ExceptionBranchesTo)
+ foreach (int exceptionHandlerOffset in block.ExceptionBranchesTo)
{
- var reachableHandler = blockLookup[exceptionHandlerOffset];
+ BasicBlock reachableHandler = blockLookup[exceptionHandlerOffset];
knownLive = knownLive.Push(reachableHandler);
}
}
@@ -629,16 +632,16 @@ private void DetermineHeadReachability(ImmutableArray blocks)
private ImmutableArray CreateBasicBlocks(Collection instrs, Collection exceptionHandlers, ImmutableArray branches)
{
// every branch-like instruction starts or stops a block
- var branchInstrLocs = branches.ToLookup(i => i.Offset);
+ ILookup branchInstrLocs = branches.ToLookup(i => i.Offset);
var branchInstrOffsets = branchInstrLocs.Select(k => k.Key).ToImmutableHashSet();
// every target that might be branched to starts or stops a block
- var branchTargetOffsetsBuilder = ImmutableHashSet.CreateBuilder();
- foreach (var branch in branches)
+ ImmutableHashSet.Builder branchTargetOffsetsBuilder = ImmutableHashSet.CreateBuilder();
+ foreach (BranchInstruction branch in branches)
{
if (branch.HasMultiTargets)
{
- foreach (var target in branch.TargetOffsets)
+ foreach (int target in branch.TargetOffsets)
{
branchTargetOffsetsBuilder.Add(target);
}
@@ -651,7 +654,7 @@ private ImmutableArray CreateBasicBlocks(Collection ins
// every exception handler an entry point
// either it's handler, or it's filter (if present)
- foreach (var handler in exceptionHandlers)
+ foreach (ExceptionHandler handler in exceptionHandlers)
{
if (handler.FilterStart != null)
{
@@ -663,18 +666,18 @@ private ImmutableArray CreateBasicBlocks(Collection ins
}
}
- var branchTargetOffsets = branchTargetOffsetsBuilder.ToImmutable();
+ ImmutableHashSet branchTargetOffsets = branchTargetOffsetsBuilder.ToImmutable();
// ending the method is also important
- var endOfMethodOffset = instrs[instrs.Count - 1].Offset;
+ int endOfMethodOffset = instrs[instrs.Count - 1].Offset;
- var blocks = ImmutableArray.Empty;
+ ImmutableArray blocks = ImmutableArray.Empty;
int? blockStartedAt = null;
Instruction unreachableAfter = null;
- foreach (var i in instrs)
+ foreach (Instruction i in instrs)
{
- var offset = i.Offset;
- var branchesAtLoc = branchInstrLocs[offset];
+ int offset = i.Offset;
+ System.Collections.Generic.IEnumerable branchesAtLoc = branchInstrLocs[offset];
if (blockStartedAt == null)
{
@@ -682,19 +685,19 @@ private ImmutableArray CreateBasicBlocks(Collection ins
unreachableAfter = null;
}
- var isBranch = branchInstrOffsets.Contains(offset);
- var isFollowedByBranchTarget = i.Next != null && branchTargetOffsets.Contains(i.Next.Offset);
- var isEndOfMtd = endOfMethodOffset == offset;
+ bool isBranch = branchInstrOffsets.Contains(offset);
+ bool isFollowedByBranchTarget = i.Next != null && branchTargetOffsets.Contains(i.Next.Offset);
+ bool isEndOfMtd = endOfMethodOffset == offset;
if (unreachableAfter == null && DoesNotReturn(i))
{
unreachableAfter = i;
}
- var blockEnds = isBranch || isFollowedByBranchTarget || isEndOfMtd;
+ bool blockEnds = isBranch || isFollowedByBranchTarget || isEndOfMtd;
if (blockEnds)
{
- var nextInstr = i.Next;
+ Instruction nextInstr = i.Next;
// figure out all the different places the basic block could lead to
ImmutableArray goesTo;
@@ -702,7 +705,7 @@ private ImmutableArray CreateBasicBlocks(Collection ins
{
// it ends in a branch, where all does it branch?
goesTo = ImmutableArray.Empty;
- foreach (var branch in branchesAtLoc)
+ foreach (BranchInstruction branch in branchesAtLoc)
{
if (branch.HasMultiTargets)
{
@@ -725,30 +728,30 @@ private ImmutableArray CreateBasicBlocks(Collection ins
goesTo = ImmutableArray.Empty;
}
- var exceptionSwitchesTo = ImmutableArray.Empty;
+ ImmutableArray exceptionSwitchesTo = ImmutableArray.Empty;
// if the block is covered by any exception handlers then
// it is possible that it will branch to its handler block
- foreach (var handler in exceptionHandlers)
+ foreach (ExceptionHandler handler in exceptionHandlers)
{
- var tryStart = handler.TryStart.Offset;
- var tryEnd = handler.TryEnd.Offset;
+ int tryStart = handler.TryStart.Offset;
+ int tryEnd = handler.TryEnd.Offset;
- var containsStartOfTry =
+ bool containsStartOfTry =
tryStart >= blockStartedAt.Value &&
tryStart <= i.Offset;
- var containsEndOfTry =
+ bool containsEndOfTry =
tryEnd >= blockStartedAt.Value &&
tryEnd <= i.Offset;
- var blockInsideTry = blockStartedAt.Value >= tryStart && i.Offset <= tryEnd;
+ bool blockInsideTry = blockStartedAt.Value >= tryStart && i.Offset <= tryEnd;
// blocks do not necessarily align to the TRY part of exception handlers, so we need to handle three cases:
// - the try _starts_ in the block
// - the try _ends_ in the block
// - the try complete covers the block, but starts and ends before and after it (respectively)
- var tryOverlapsBlock = containsStartOfTry || containsEndOfTry || blockInsideTry;
+ bool tryOverlapsBlock = containsStartOfTry || containsEndOfTry || blockInsideTry;
if (!tryOverlapsBlock)
{
@@ -783,12 +786,12 @@ private ImmutableArray CreateBasicBlocks(Collection ins
///
private bool DoesNotReturn(Instruction instr)
{
- if (!IsCall(instr, out var mtd))
+ if (!IsCall(instr, out MethodReference mtd))
{
return false;
}
- return DoesNotReturnMethods.Contains(mtd.MetadataToken);
+ return _doesNotReturnMethods.Contains(mtd.MetadataToken);
}
///
@@ -798,7 +801,7 @@ private bool DoesNotReturn(Instruction instr)
///
private static bool IsCall(Instruction instr, out MethodReference mtd)
{
- var opcode = instr.OpCode;
+ OpCode opcode = instr.OpCode;
if (opcode != OpCodes.Call && opcode != OpCodes.Callvirt)
{
mtd = null;
diff --git a/src/coverlet.core/Properties/AssemblyInfo.cs b/src/coverlet.core/Properties/AssemblyInfo.cs
index 8eb19aee7..7e65be514 100644
--- a/src/coverlet.core/Properties/AssemblyInfo.cs
+++ b/src/coverlet.core/Properties/AssemblyInfo.cs
@@ -1,3 +1,6 @@
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
using System.Reflection;
using System.Runtime.CompilerServices;
diff --git a/src/coverlet.core/Reporters/CoberturaReporter.cs b/src/coverlet.core/Reporters/CoberturaReporter.cs
index e9e13c719..e3d299fda 100644
--- a/src/coverlet.core/Reporters/CoberturaReporter.cs
+++ b/src/coverlet.core/Reporters/CoberturaReporter.cs
@@ -1,3 +1,6 @@
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -21,42 +24,42 @@ internal class CoberturaReporter : IReporter
public string Report(CoverageResult result, ISourceRootTranslator sourceRootTranslator)
{
- CoverageSummary summary = new CoverageSummary();
+ var summary = new CoverageSummary();
- var lineCoverage = summary.CalculateLineCoverage(result.Modules);
- var branchCoverage = summary.CalculateBranchCoverage(result.Modules);
+ CoverageDetails lineCoverage = summary.CalculateLineCoverage(result.Modules);
+ CoverageDetails branchCoverage = summary.CalculateBranchCoverage(result.Modules);
- XDocument xml = new XDocument();
- XElement coverage = new XElement("coverage");
+ var xml = new XDocument();
+ var coverage = new XElement("coverage");
coverage.Add(new XAttribute("line-rate", (summary.CalculateLineCoverage(result.Modules).Percent / 100).ToString(CultureInfo.InvariantCulture)));
coverage.Add(new XAttribute("branch-rate", (summary.CalculateBranchCoverage(result.Modules).Percent / 100).ToString(CultureInfo.InvariantCulture)));
coverage.Add(new XAttribute("version", "1.9"));
coverage.Add(new XAttribute("timestamp", (int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds));
- XElement sources = new XElement("sources");
+ var sources = new XElement("sources");
- List absolutePaths = new List();
+ var absolutePaths = new List();
if (!result.Parameters.DeterministicReport)
{
absolutePaths = GetBasePaths(result.Modules, result.Parameters.UseSourceLink).ToList();
absolutePaths.ForEach(x => sources.Add(new XElement("source", x)));
}
- XElement packages = new XElement("packages");
- foreach (var module in result.Modules)
+ var packages = new XElement("packages");
+ foreach (KeyValuePair module in result.Modules)
{
- XElement package = new XElement("package");
+ var package = new XElement("package");
package.Add(new XAttribute("name", Path.GetFileNameWithoutExtension(module.Key)));
package.Add(new XAttribute("line-rate", (summary.CalculateLineCoverage(module.Value).Percent / 100).ToString(CultureInfo.InvariantCulture)));
package.Add(new XAttribute("branch-rate", (summary.CalculateBranchCoverage(module.Value).Percent / 100).ToString(CultureInfo.InvariantCulture)));
package.Add(new XAttribute("complexity", summary.CalculateCyclomaticComplexity(module.Value)));
- XElement classes = new XElement("classes");
- foreach (var document in module.Value)
+ var classes = new XElement("classes");
+ foreach (KeyValuePair document in module.Value)
{
- foreach (var cls in document.Value)
+ foreach (KeyValuePair cls in document.Value)
{
- XElement @class = new XElement("class");
+ var @class = new XElement("class");
@class.Add(new XAttribute("name", cls.Key));
string fileName;
if (!result.Parameters.DeterministicReport)
@@ -72,27 +75,27 @@ public string Report(CoverageResult result, ISourceRootTranslator sourceRootTran
@class.Add(new XAttribute("branch-rate", (summary.CalculateBranchCoverage(cls.Value).Percent / 100).ToString(CultureInfo.InvariantCulture)));
@class.Add(new XAttribute("complexity", summary.CalculateCyclomaticComplexity(cls.Value)));
- XElement classLines = new XElement("lines");
- XElement methods = new XElement("methods");
+ var classLines = new XElement("lines");
+ var methods = new XElement("methods");
- foreach (var meth in cls.Value)
+ foreach (KeyValuePair meth in cls.Value)
{
// Skip all methods with no lines
if (meth.Value.Lines.Count == 0)
continue;
- XElement method = new XElement("method");
+ var method = new XElement("method");
method.Add(new XAttribute("name", meth.Key.Split(':').Last().Split('(').First()));
method.Add(new XAttribute("signature", "(" + meth.Key.Split(':').Last().Split('(').Last()));
method.Add(new XAttribute("line-rate", (summary.CalculateLineCoverage(meth.Value.Lines).Percent / 100).ToString(CultureInfo.InvariantCulture)));
method.Add(new XAttribute("branch-rate", (summary.CalculateBranchCoverage(meth.Value.Branches).Percent / 100).ToString(CultureInfo.InvariantCulture)));
method.Add(new XAttribute("complexity", summary.CalculateCyclomaticComplexity(meth.Value.Branches)));
- XElement lines = new XElement("lines");
- foreach (var ln in meth.Value.Lines)
+ var lines = new XElement("lines");
+ foreach (KeyValuePair ln in meth.Value.Lines)
{
bool isBranchPoint = meth.Value.Branches.Any(b => b.Line == ln.Key);
- XElement line = new XElement("line");
+ var line = new XElement("line");
line.Add(new XAttribute("number", ln.Key.ToString()));
line.Add(new XAttribute("hits", ln.Value.ToString()));
line.Add(new XAttribute("branch", isBranchPoint.ToString()));
@@ -100,15 +103,15 @@ public string Report(CoverageResult result, ISourceRootTranslator sourceRootTran
if (isBranchPoint)
{
var branches = meth.Value.Branches.Where(b => b.Line == ln.Key).ToList();
- var branchInfoCoverage = summary.CalculateBranchCoverage(branches);
+ CoverageDetails branchInfoCoverage = summary.CalculateBranchCoverage(branches);
line.Add(new XAttribute("condition-coverage", $"{branchInfoCoverage.Percent.ToString(CultureInfo.InvariantCulture)}% ({branchInfoCoverage.Covered.ToString(CultureInfo.InvariantCulture)}/{branchInfoCoverage.Total.ToString(CultureInfo.InvariantCulture)})"));
- XElement conditions = new XElement("conditions");
+ var conditions = new XElement("conditions");
var byOffset = branches.GroupBy(b => b.Offset).ToDictionary(b => b.Key, b => b.ToList());
- foreach (var entry in byOffset)
+ foreach (KeyValuePair> entry in byOffset)
{
- XElement condition = new XElement("condition");
+ var condition = new XElement("condition");
condition.Add(new XAttribute("number", entry.Key));
- condition.Add(new XAttribute("type", entry.Value.Count() > 2 ? "switch" : "jump")); // Just guessing here
+ condition.Add(new XAttribute("type", entry.Value.Count > 2 ? "switch" : "jump")); // Just guessing here
condition.Add(new XAttribute("coverage", $"{summary.CalculateBranchCoverage(entry.Value).Percent.ToString(CultureInfo.InvariantCulture)}%"));
conditions.Add(condition);
}
@@ -116,7 +119,6 @@ public string Report(CoverageResult result, ISourceRootTranslator sourceRootTran
line.Add(conditions);
}
-
lines.Add(line);
classLines.Add(line);
}
@@ -218,17 +220,14 @@ private static string GetRelativePathFromBase(IEnumerable basePaths, str
return path;
}
- foreach (var basePath in basePaths)
+ foreach (string basePath in basePaths)
{
if (path.StartsWith(basePath))
{
return path.Substring(basePath.Length);
}
}
-
- Debug.Assert(false, "Unexpected, we should find at least one path starts with one pre-build roots list");
-
return path;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/coverlet.core/Reporters/JsonReporter.cs b/src/coverlet.core/Reporters/JsonReporter.cs
index bbcb4fa31..39ecc6e2f 100644
--- a/src/coverlet.core/Reporters/JsonReporter.cs
+++ b/src/coverlet.core/Reporters/JsonReporter.cs
@@ -1,7 +1,8 @@
-using Newtonsoft.Json;
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using Coverlet.Core.Abstractions;
-using System;
+using Newtonsoft.Json;
namespace Coverlet.Core.Reporters
{
@@ -18,4 +19,4 @@ public string Report(CoverageResult result, ISourceRootTranslator _)
return JsonConvert.SerializeObject(result.Modules, Formatting.Indented);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/coverlet.core/Reporters/LcovReporter.cs b/src/coverlet.core/Reporters/LcovReporter.cs
index f1ede2437..5b7471f42 100644
--- a/src/coverlet.core/Reporters/LcovReporter.cs
+++ b/src/coverlet.core/Reporters/LcovReporter.cs
@@ -1,7 +1,9 @@
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
using System;
-using System.Linq;
using System.Collections.Generic;
-
+using System.Linq;
using Coverlet.Core.Abstractions;
namespace Coverlet.Core.Reporters
@@ -21,21 +23,21 @@ public string Report(CoverageResult result, ISourceRootTranslator sourceRootTran
throw new NotSupportedException("Deterministic report not supported by lcov reporter");
}
- CoverageSummary summary = new CoverageSummary();
- List lcov = new List();
+ var summary = new CoverageSummary();
+ var lcov = new List();
- foreach (var module in result.Modules)
+ foreach (KeyValuePair module in result.Modules)
{
- foreach (var doc in module.Value)
+ foreach (KeyValuePair doc in module.Value)
{
- var docLineCoverage = summary.CalculateLineCoverage(doc.Value);
- var docBranchCoverage = summary.CalculateBranchCoverage(doc.Value);
- var docMethodCoverage = summary.CalculateMethodCoverage(doc.Value);
+ CoverageDetails docLineCoverage = summary.CalculateLineCoverage(doc.Value);
+ CoverageDetails docBranchCoverage = summary.CalculateBranchCoverage(doc.Value);
+ CoverageDetails docMethodCoverage = summary.CalculateMethodCoverage(doc.Value);
lcov.Add("SF:" + doc.Key);
- foreach (var @class in doc.Value)
+ foreach (KeyValuePair @class in doc.Value)
{
- foreach (var method in @class.Value)
+ foreach (KeyValuePair method in @class.Value)
{
// Skip all methods with no lines
if (method.Value.Lines.Count == 0)
@@ -44,10 +46,10 @@ public string Report(CoverageResult result, ISourceRootTranslator sourceRootTran
lcov.Add($"FN:{method.Value.Lines.First().Key - 1},{method.Key}");
lcov.Add($"FNDA:{method.Value.Lines.First().Value},{method.Key}");
- foreach (var line in method.Value.Lines)
+ foreach (KeyValuePair line in method.Value.Lines)
lcov.Add($"DA:{line.Key},{line.Value}");
- foreach (var branch in method.Value.Branches)
+ foreach (BranchInfo branch in method.Value.Branches)
{
lcov.Add($"BRDA:{branch.Line},{branch.Offset},{branch.Path},{branch.Hits}");
}
@@ -70,4 +72,4 @@ public string Report(CoverageResult result, ISourceRootTranslator sourceRootTran
return string.Join(Environment.NewLine, lcov);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/coverlet.core/Reporters/OpenCoverReporter.cs b/src/coverlet.core/Reporters/OpenCoverReporter.cs
index 6d4c7b6ba..2e42c2894 100644
--- a/src/coverlet.core/Reporters/OpenCoverReporter.cs
+++ b/src/coverlet.core/Reporters/OpenCoverReporter.cs
@@ -1,10 +1,12 @@
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
using System;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Linq;
-
using Coverlet.Core.Abstractions;
namespace Coverlet.Core.Reporters
@@ -24,63 +26,63 @@ public string Report(CoverageResult result, ISourceRootTranslator sourceRootTran
throw new NotSupportedException("Deterministic report not supported by openCover reporter");
}
- CoverageSummary summary = new CoverageSummary();
- XDocument xml = new XDocument();
- XElement coverage = new XElement("CoverageSession");
- XElement coverageSummary = new XElement("Summary");
- XElement modules = new XElement("Modules");
+ var summary = new CoverageSummary();
+ var xml = new XDocument();
+ var coverage = new XElement("CoverageSession");
+ var coverageSummary = new XElement("Summary");
+ var modules = new XElement("Modules");
int numClasses = 0, numMethods = 0;
int visitedClasses = 0, visitedMethods = 0;
int i = 1;
- foreach (var mod in result.Modules)
+ foreach (System.Collections.Generic.KeyValuePair mod in result.Modules)
{
- XElement module = new XElement("Module");
+ var module = new XElement("Module");
module.Add(new XAttribute("hash", Guid.NewGuid().ToString().ToUpper()));
- XElement path = new XElement("ModulePath", mod.Key);
- XElement time = new XElement("ModuleTime", DateTime.UtcNow.ToString("yyyy-MM-ddThh:mm:ss"));
- XElement name = new XElement("ModuleName", Path.GetFileNameWithoutExtension(mod.Key));
+ var path = new XElement("ModulePath", mod.Key);
+ var time = new XElement("ModuleTime", DateTime.UtcNow.ToString("yyyy-MM-ddThh:mm:ss"));
+ var name = new XElement("ModuleName", Path.GetFileNameWithoutExtension(mod.Key));
module.Add(path);
module.Add(time);
module.Add(name);
- XElement files = new XElement("Files");
- XElement classes = new XElement("Classes");
+ var files = new XElement("Files");
+ var classes = new XElement("Classes");
- foreach (var doc in mod.Value)
+ foreach (System.Collections.Generic.KeyValuePair doc in mod.Value)
{
- XElement file = new XElement("File");
+ var file = new XElement("File");
file.Add(new XAttribute("uid", i.ToString()));
file.Add(new XAttribute("fullPath", doc.Key));
files.Add(file);
- foreach (var cls in doc.Value)
+ foreach (System.Collections.Generic.KeyValuePair cls in doc.Value)
{
- XElement @class = new XElement("Class");
- XElement classSummary = new XElement("Summary");
+ var @class = new XElement("Class");
+ var classSummary = new XElement("Summary");
- XElement className = new XElement("FullName", cls.Key);
+ var className = new XElement("FullName", cls.Key);
- XElement methods = new XElement("Methods");
+ var methods = new XElement("Methods");
int j = 0;
- var classVisited = false;
+ bool classVisited = false;
- foreach (var meth in cls.Value)
+ foreach (System.Collections.Generic.KeyValuePair meth in cls.Value)
{
// Skip all methods with no lines
if (meth.Value.Lines.Count == 0)
continue;
- var methLineCoverage = summary.CalculateLineCoverage(meth.Value.Lines);
- var methBranchCoverage = summary.CalculateBranchCoverage(meth.Value.Branches);
- var methCyclomaticComplexity = summary.CalculateCyclomaticComplexity(meth.Value.Branches);
- var methNpathComplexity = summary.CalculateNpathComplexity(meth.Value.Branches);
+ CoverageDetails methLineCoverage = summary.CalculateLineCoverage(meth.Value.Lines);
+ CoverageDetails methBranchCoverage = summary.CalculateBranchCoverage(meth.Value.Branches);
+ int methCyclomaticComplexity = summary.CalculateCyclomaticComplexity(meth.Value.Branches);
+ int methNpathComplexity = summary.CalculateNpathComplexity(meth.Value.Branches);
- XElement method = new XElement("Method");
+ var method = new XElement("Method");
method.Add(new XAttribute("cyclomaticComplexity", methCyclomaticComplexity.ToString()));
method.Add(new XAttribute("nPathComplexity", methCyclomaticComplexity.ToString()));
@@ -91,12 +93,12 @@ public string Report(CoverageResult result, ISourceRootTranslator sourceRootTran
method.Add(new XAttribute("isSetter", meth.Key.Contains("set_").ToString()));
method.Add(new XAttribute("isStatic", (!meth.Key.Contains("get_") || !meth.Key.Contains("set_")).ToString()));
- XElement methodName = new XElement("Name", meth.Key);
+ var methodName = new XElement("Name", meth.Key);
- XElement fileRef = new XElement("FileRef");
+ var fileRef = new XElement("FileRef");
fileRef.Add(new XAttribute("uid", i.ToString()));
- XElement methodPoint = new XElement("MethodPoint");
+ var methodPoint = new XElement("MethodPoint");
methodPoint.Add(new XAttribute("vc", methLineCoverage.Covered.ToString()));
methodPoint.Add(new XAttribute("uspid", "0"));
methodPoint.Add(new XAttribute(XName.Get("type", "xsi"), "SequencePoint"));
@@ -111,19 +113,19 @@ public string Report(CoverageResult result, ISourceRootTranslator sourceRootTran
methodPoint.Add(new XAttribute("fileid", i.ToString()));
// They're really just lines
- XElement sequencePoints = new XElement("SequencePoints");
- XElement branchPoints = new XElement("BranchPoints");
- XElement methodSummary = new XElement("Summary");
+ var sequencePoints = new XElement("SequencePoints");
+ var branchPoints = new XElement("BranchPoints");
+ var methodSummary = new XElement("Summary");
int k = 0;
int kBr = 0;
- var methodVisited = false;
+ bool methodVisited = false;
- foreach (var lines in meth.Value.Lines)
+ foreach (System.Collections.Generic.KeyValuePair lines in meth.Value.Lines)
{
- var lineBranches = meth.Value.Branches.Where(branchInfo => branchInfo.Line == lines.Key).ToArray();
- var branchCoverage = summary.CalculateBranchCoverage(lineBranches);
-
- XElement sequencePoint = new XElement("SequencePoint");
+ BranchInfo[] lineBranches = meth.Value.Branches.Where(branchInfo => branchInfo.Line == lines.Key).ToArray();
+ CoverageDetails branchCoverage = summary.CalculateBranchCoverage(lineBranches);
+
+ var sequencePoint = new XElement("SequencePoint");
sequencePoint.Add(new XAttribute("vc", lines.Value.ToString()));
sequencePoint.Add(new XAttribute("uspid", lines.Key.ToString()));
sequencePoint.Add(new XAttribute("ordinal", k.ToString()));
@@ -145,9 +147,9 @@ public string Report(CoverageResult result, ISourceRootTranslator sourceRootTran
k++;
}
- foreach (var branche in meth.Value.Branches)
+ foreach (BranchInfo branche in meth.Value.Branches)
{
- XElement branchPoint = new XElement("BranchPoint");
+ var branchPoint = new XElement("BranchPoint");
branchPoint.Add(new XAttribute("vc", branche.Hits.ToString()));
branchPoint.Add(new XAttribute("uspid", branche.Line.ToString()));
branchPoint.Add(new XAttribute("ordinal", branche.Ordinal.ToString()));
@@ -192,11 +194,11 @@ public string Report(CoverageResult result, ISourceRootTranslator sourceRootTran
if (classVisited)
visitedClasses++;
- var classLineCoverage = summary.CalculateLineCoverage(cls.Value);
- var classBranchCoverage = summary.CalculateBranchCoverage(cls.Value);
- var classMethodCoverage = summary.CalculateMethodCoverage(cls.Value);
- var classMaxCyclomaticComplexity = summary.CalculateMaxCyclomaticComplexity(cls.Value);
- var classMinCyclomaticComplexity = summary.CalculateMinCyclomaticComplexity(cls.Value);
+ CoverageDetails classLineCoverage = summary.CalculateLineCoverage(cls.Value);
+ CoverageDetails classBranchCoverage = summary.CalculateBranchCoverage(cls.Value);
+ CoverageDetails classMethodCoverage = summary.CalculateMethodCoverage(cls.Value);
+ int classMaxCyclomaticComplexity = summary.CalculateMaxCyclomaticComplexity(cls.Value);
+ int classMinCyclomaticComplexity = summary.CalculateMinCyclomaticComplexity(cls.Value);
classSummary.Add(new XAttribute("numSequencePoints", classLineCoverage.Total.ToString()));
classSummary.Add(new XAttribute("visitedSequencePoints", classLineCoverage.Covered.ToString()));
@@ -224,10 +226,10 @@ public string Report(CoverageResult result, ISourceRootTranslator sourceRootTran
modules.Add(module);
}
- var moduleLineCoverage = summary.CalculateLineCoverage(result.Modules);
- var moduleBranchCoverage = summary.CalculateBranchCoverage(result.Modules);
- var moduleMaxCyclomaticComplexity = summary.CalculateMaxCyclomaticComplexity(result.Modules);
- var moduleMinCyclomaticComplexity = summary.CalculateMinCyclomaticComplexity(result.Modules);
+ CoverageDetails moduleLineCoverage = summary.CalculateLineCoverage(result.Modules);
+ CoverageDetails moduleBranchCoverage = summary.CalculateBranchCoverage(result.Modules);
+ int moduleMaxCyclomaticComplexity = summary.CalculateMaxCyclomaticComplexity(result.Modules);
+ int moduleMinCyclomaticComplexity = summary.CalculateMinCyclomaticComplexity(result.Modules);
coverageSummary.Add(new XAttribute("numSequencePoints", moduleLineCoverage.Total.ToString()));
coverageSummary.Add(new XAttribute("visitedSequencePoints", moduleLineCoverage.Covered.ToString()));
@@ -252,4 +254,4 @@ public string Report(CoverageResult result, ISourceRootTranslator sourceRootTran
return Encoding.UTF8.GetString(stream.ToArray());
}
}
-}
\ No newline at end of file
+}
diff --git a/src/coverlet.core/Reporters/ReporterFactory.cs b/src/coverlet.core/Reporters/ReporterFactory.cs
index 0d13520cb..2c9dc95ee 100644
--- a/src/coverlet.core/Reporters/ReporterFactory.cs
+++ b/src/coverlet.core/Reporters/ReporterFactory.cs
@@ -1,14 +1,16 @@
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
using System;
using System.Linq;
-
using Coverlet.Core.Abstractions;
namespace Coverlet.Core.Reporters
{
internal class ReporterFactory
{
- private string _format;
- private IReporter[] _reporters;
+ private readonly string _format;
+ private readonly IReporter[] _reporters;
public ReporterFactory(string format)
{
@@ -28,4 +30,4 @@ public bool IsValidFormat()
public IReporter CreateReporter()
=> _reporters.FirstOrDefault(r => string.Equals(r.Format, _format, StringComparison.OrdinalIgnoreCase));
}
-}
\ No newline at end of file
+}
diff --git a/src/coverlet.core/Reporters/TeamCityReporter.cs b/src/coverlet.core/Reporters/TeamCityReporter.cs
index acb5d4ff1..8ccb0d997 100644
--- a/src/coverlet.core/Reporters/TeamCityReporter.cs
+++ b/src/coverlet.core/Reporters/TeamCityReporter.cs
@@ -1,7 +1,9 @@
-using System;
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
using System.Globalization;
using System.Text;
-
using Coverlet.Core.Abstractions;
namespace Coverlet.Core.Reporters
@@ -23,9 +25,9 @@ public string Report(CoverageResult result, ISourceRootTranslator sourceRootTran
// Calculate coverage
var summary = new CoverageSummary();
- var overallLineCoverage = summary.CalculateLineCoverage(result.Modules);
- var overallBranchCoverage = summary.CalculateBranchCoverage(result.Modules);
- var overallMethodCoverage = summary.CalculateMethodCoverage(result.Modules);
+ CoverageDetails overallLineCoverage = summary.CalculateLineCoverage(result.Modules);
+ CoverageDetails overallBranchCoverage = summary.CalculateBranchCoverage(result.Modules);
+ CoverageDetails overallMethodCoverage = summary.CalculateMethodCoverage(result.Modules);
// Report coverage
var stringBuilder = new StringBuilder();
@@ -37,7 +39,7 @@ public string Report(CoverageResult result, ISourceRootTranslator sourceRootTran
return stringBuilder.ToString();
}
- private void OutputLineCoverage(CoverageDetails coverageDetails, StringBuilder builder)
+ private static void OutputLineCoverage(CoverageDetails coverageDetails, StringBuilder builder)
{
// The number of covered lines
OutputTeamCityServiceMessage("CodeCoverageAbsLCovered", coverageDetails.Covered, builder);
@@ -46,7 +48,7 @@ private void OutputLineCoverage(CoverageDetails coverageDetails, StringBuilder b
OutputTeamCityServiceMessage("CodeCoverageAbsLTotal", coverageDetails.Total, builder);
}
- private void OutputBranchCoverage(CoverageDetails coverageDetails, StringBuilder builder)
+ private static void OutputBranchCoverage(CoverageDetails coverageDetails, StringBuilder builder)
{
// The number of covered branches
OutputTeamCityServiceMessage("CodeCoverageAbsBCovered", coverageDetails.Covered, builder);
@@ -55,7 +57,7 @@ private void OutputBranchCoverage(CoverageDetails coverageDetails, StringBuilder
OutputTeamCityServiceMessage("CodeCoverageAbsBTotal", coverageDetails.Total, builder);
}
- private void OutputMethodCoverage(CoverageDetails coverageDetails, StringBuilder builder)
+ private static void OutputMethodCoverage(CoverageDetails coverageDetails, StringBuilder builder)
{
// The number of covered methods
OutputTeamCityServiceMessage("CodeCoverageAbsMCovered", coverageDetails.Covered, builder);
@@ -64,7 +66,7 @@ private void OutputMethodCoverage(CoverageDetails coverageDetails, StringBuilder
OutputTeamCityServiceMessage("CodeCoverageAbsMTotal", coverageDetails.Total, builder);
}
- private void OutputTeamCityServiceMessage(string key, double value, StringBuilder builder)
+ private static void OutputTeamCityServiceMessage(string key, double value, StringBuilder builder)
{
builder.AppendLine($"##teamcity[buildStatisticValue key='{key}' value='{value.ToString("0.##", new CultureInfo("en-US"))}']");
}
diff --git a/src/coverlet.core/Symbols/BranchPoint.cs b/src/coverlet.core/Symbols/BranchPoint.cs
index 077d569d2..77a675e82 100644
--- a/src/coverlet.core/Symbols/BranchPoint.cs
+++ b/src/coverlet.core/Symbols/BranchPoint.cs
@@ -1,6 +1,8 @@
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
using System;
using System.Diagnostics;
-using System.Text.RegularExpressions;
namespace Coverlet.Core.Symbols
{
diff --git a/src/coverlet.core/Symbols/CecilSymbolHelper.cs b/src/coverlet.core/Symbols/CecilSymbolHelper.cs
index b6727de46..930d19ede 100644
--- a/src/coverlet.core/Symbols/CecilSymbolHelper.cs
+++ b/src/coverlet.core/Symbols/CecilSymbolHelper.cs
@@ -1,12 +1,12 @@
-//
-// This class is based heavily on the work of the OpenCover
-// team in OpenCover.Framework.Symbols.CecilSymbolManager
-//
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
+
using Coverlet.Core.Abstractions;
using Coverlet.Core.Extensions;
@@ -20,8 +20,8 @@ internal class CecilSymbolHelper : ICecilSymbolHelper
{
private const int StepOverLineCode = 0xFEEFEE;
// Create single instance, we cannot collide because we use full method name as key
- private readonly ConcurrentDictionary _compilerGeneratedBranchesToExclude = new ConcurrentDictionary();
- private readonly ConcurrentDictionary> _sequencePointOffsetToSkip = new ConcurrentDictionary>();
+ private readonly ConcurrentDictionary _compilerGeneratedBranchesToExclude = new();
+ private readonly ConcurrentDictionary> _sequencePointOffsetToSkip = new();
// In case of nested compiler generated classes, only the root one presents the CompilerGenerated attribute.
// So let's search up to the outermost declaring type to find the attribute
@@ -83,7 +83,8 @@ private static bool IsMoveNextInsideEnumerator(MethodDefinition methodDefinition
{
return false;
}
- if (methodDefinition.DeclaringType.CustomAttributes.Count(ca => ca.AttributeType.FullName == typeof(CompilerGeneratedAttribute).FullName) > 0)
+
+ if (methodDefinition.DeclaringType.CustomAttributes.Any(ca => ca.AttributeType.FullName == typeof(CompilerGeneratedAttribute).FullName))
{
foreach (InterfaceImplementation implementedInterface in methodDefinition.DeclaringType.Interfaces)
{
@@ -198,6 +199,7 @@ instruction.Previous.Operand is MethodReference operand &&
operand.DeclaringType.Scope.Name == "System.Runtime" ||
operand.DeclaringType.Scope.Name == "netstandard" ||
operand.DeclaringType.Scope.Name == "mscorlib" ||
+ operand.DeclaringType.Scope.Name == "System.Threading.Tasks" ||
operand.DeclaringType.Scope.Name == "System.Threading.Tasks.Extensions"
)
)
@@ -272,10 +274,10 @@ private static bool SkipGeneratedBranchForExceptionRethrown(List in
int branchIndex = instructions.BinarySearch(instruction, new InstructionByOffsetComparer());
- return new[] {2,3}.Any(x => branchIndex >= x &&
- instructions[branchIndex - x].OpCode == OpCodes.Isinst &&
- instructions[branchIndex - x].Operand is TypeReference tr &&
- tr.FullName == "System.Exception")
+ return new[] { 2, 3 }.Any(x => branchIndex >= x &&
+ instructions[branchIndex - x].OpCode == OpCodes.Isinst &&
+ instructions[branchIndex - x].Operand is TypeReference tr &&
+ tr.FullName == "System.Exception")
&&
// check for throw opcode after branch
instructions.Count - branchIndex >= 3 &&
@@ -399,11 +401,11 @@ private bool SkipGeneratedBranchesForExceptionHandlers(MethodDefinition methodDe
*/
if (!_compilerGeneratedBranchesToExclude.ContainsKey(methodDefinition.FullName))
{
- List detectedBranches = new List();
+ var detectedBranches = new List();
Collection handlers = methodDefinition.Body.ExceptionHandlers;
int numberOfCatchBlocks = 1;
- foreach (var handler in handlers)
+ foreach (ExceptionHandler handler in handlers)
{
if (handlers.Any(h => h.HandlerStart == handler.HandlerEnd))
{
@@ -465,7 +467,6 @@ private static bool SkipGeneratedBranchesForAwaitForeach(List instr
CheckIfExceptionThrown(instructions, instruction, currentIndex) ||
CheckThrownExceptionType(instructions, instruction, currentIndex);
-
// The pattern for the "should we stay in the loop or not?", which we don't
// want to skip (so we have no method to try to find it), looks like this:
//
@@ -477,7 +478,6 @@ private static bool SkipGeneratedBranchesForAwaitForeach(List instr
// the "call" and branch, but it's the same idea either way: branch
// if GetResult() returned true.
-
static bool CheckForAsyncEnumerator(List instructions, Instruction instruction, int currentIndex)
{
// We're looking for the following pattern, which checks whether a
@@ -497,8 +497,11 @@ static bool CheckForAsyncEnumerator(List instructions, Instruction
(instructions[currentIndex - 2].OpCode == OpCodes.Ldarg ||
instructions[currentIndex - 2].OpCode == OpCodes.Ldarg_0) &&
instructions[currentIndex - 1].OpCode == OpCodes.Ldfld &&
- instructions[currentIndex - 1].Operand is FieldDefinition field &&
- IsCompilerGenerated(field) && field.FieldType.FullName.StartsWith("System.Collections.Generic.IAsyncEnumerator"))
+ (
+ (instructions[currentIndex - 1].Operand is FieldDefinition field && IsCompilerGenerated(field) && field.FieldType.FullName.StartsWith("System.Collections.Generic.IAsyncEnumerator")) ||
+ (instructions[currentIndex - 1].Operand is FieldReference fieldRef && IsCompilerGenerated(fieldRef.Resolve()) && fieldRef.FieldType.FullName.StartsWith("System.Collections.Generic.IAsyncEnumerator"))
+ )
+ )
{
return true;
}
@@ -506,7 +509,6 @@ static bool CheckForAsyncEnumerator(List instructions, Instruction
return false;
}
-
static bool CheckIfExceptionThrown(List instructions, Instruction instruction, int currentIndex)
{
// Here, we want to find a pattern where we're checking whether a
@@ -540,8 +542,10 @@ static bool CheckIfExceptionThrown(List instructions, Instruction i
for (int i = currentIndex - 1; i >= minFieldIndex; --i)
{
if (instructions[i].OpCode == OpCodes.Ldfld &&
- instructions[i].Operand is FieldDefinition field &&
- IsCompilerGenerated(field) && field.FieldType.FullName == "System.Object")
+ (
+ (instructions[i].Operand is FieldDefinition field && IsCompilerGenerated(field) && field.FieldType.FullName == "System.Object") ||
+ (instructions[i].Operand is FieldReference fieldRef && IsCompilerGenerated(fieldRef.Resolve()) && fieldRef.FieldType.FullName == "System.Object")
+ ))
{
// We expect the call to GetResult() to be no more than four
// instructions before the loading of the field's value.
@@ -564,7 +568,6 @@ instructions[j].Operand is MethodReference callRef &&
return false;
}
-
static bool CheckThrownExceptionType(List instructions, Instruction instruction, int currentIndex)
{
// In this case, we're looking for a branch generated by the compiler to
@@ -617,7 +620,6 @@ private static bool SkipGeneratedBranchesForAwaitUsing(List instruc
return CheckForSkipDisposal(instructions, instruction, currentIndex) ||
CheckForCleanup(instructions, instruction, currentIndex);
-
static bool CheckForSkipDisposal(List instructions, Instruction instruction, int currentIndex)
{
// The async state machine generated for an "await using" contains a branch
@@ -663,7 +665,7 @@ static bool CheckForSkipDisposal(List instructions, Instruction ins
{
return false;
}
-
+
bool isFollowedByDisposeAsync = false;
if (instructions[currentIndex - 1].OpCode == OpCodes.Ldfld &&
@@ -727,7 +729,6 @@ instructions[i].Operand is FieldDefinition reloadedField &&
return false;
}
-
static bool CheckForCleanup(List instructions, Instruction instruction, int currentIndex)
{
// The pattern we're looking for here is this:
@@ -812,7 +813,6 @@ private static bool SkipGeneratedBranchesForAsyncIterator(List inst
return CheckForStateSwitch(instructions, instruction, currentIndex) ||
DisposeCheck(instructions, instruction, currentIndex);
-
static bool CheckForStateSwitch(List instructions, Instruction instruction, int currentIndex)
{
// The pattern we're looking for here is this one:
@@ -878,8 +878,10 @@ static bool DisposeCheck(List instructions, Instruction instruction
if (currentIndex >= 2 &&
instructions[currentIndex - 1].OpCode == OpCodes.Ldfld &&
- instructions[currentIndex - 1].Operand is FieldDefinition field &&
- IsCompilerGenerated(field) && field.FullName.EndsWith("__disposeMode") &&
+ (
+ (instructions[currentIndex - 1].Operand is FieldDefinition field && IsCompilerGenerated(field) && field.FullName.EndsWith("__disposeMode")) ||
+ (instructions[currentIndex - 1].Operand is FieldReference fieldRef && IsCompilerGenerated(fieldRef.Resolve()) && fieldRef.FullName.EndsWith("__disposeMode"))
+ ) &&
(instructions[currentIndex - 2].OpCode == OpCodes.Ldarg ||
instructions[currentIndex - 2].OpCode == OpCodes.Ldarg_0))
{
@@ -890,7 +892,7 @@ static bool DisposeCheck(List instructions, Instruction instruction
}
}
- private bool SkipGeneratedBranchesForEnumeratorCancellationAttribute(List instructions, Instruction instruction)
+ private static bool SkipGeneratedBranchesForEnumeratorCancellationAttribute(List instructions, Instruction instruction)
{
// For async-enumerable methods an additional cancellation token despite the default one can be passed.
// The EnumeratorCancellation attribute marks the parameter whose value is received by GetAsyncEnumerator(CancellationToken).
@@ -1021,13 +1023,13 @@ public IReadOnlyList GetBranchPoints(MethodDefinition methodDefinit
continue;
}
- var pathCounter = 0;
+ int pathCounter = 0;
// store branch origin offset
- var branchOffset = instruction.Offset;
- var closestSeqPt = FindClosestInstructionWithSequencePoint(methodDefinition.Body, instruction).Maybe(i => methodDefinition.DebugInformation.GetSequencePoint(i));
- var branchingInstructionLine = closestSeqPt.Maybe(x => x.StartLine, -1);
- var document = closestSeqPt.Maybe(x => x.Document.Url);
+ int branchOffset = instruction.Offset;
+ SequencePoint closestSeqPt = FindClosestInstructionWithSequencePoint(methodDefinition.Body, instruction).Maybe(i => methodDefinition.DebugInformation.GetSequencePoint(i));
+ int branchingInstructionLine = closestSeqPt.Maybe(x => x.StartLine, -1);
+ string document = closestSeqPt.Maybe(x => x.Document.Url);
if (instruction.Next == null)
{
@@ -1054,9 +1056,9 @@ private static bool BuildPointsForConditionalBranch(List list, Inst
// Add Default branch (Path=0)
// Follow else/default instruction
- var @else = instruction.Next;
+ Instruction @else = instruction.Next;
- var pathOffsetList = GetBranchPath(@else);
+ List pathOffsetList = GetBranchPath(@else);
// add Path 0
var path0 = new BranchPoint
@@ -1077,8 +1079,7 @@ private static bool BuildPointsForConditionalBranch(List list, Inst
if (instruction.OpCode.Code != Code.Switch)
{
// Follow instruction at operand
- var @then = instruction.Operand as Instruction;
- if (@then == null)
+ if (instruction.Operand is not Instruction @then)
return false;
ordinal = BuildPointsForBranch(list, then, branchingInstructionLine, document, branchOffset,
@@ -1086,8 +1087,7 @@ private static bool BuildPointsForConditionalBranch(List list, Inst
}
else // instruction.OpCode.Code == Code.Switch
{
- var branchInstructions = instruction.Operand as Instruction[];
- if (branchInstructions == null || branchInstructions.Length == 0)
+ if (instruction.Operand is not Instruction[] branchInstructions || branchInstructions.Length == 0)
return false;
ordinal = BuildPointsForSwitchCases(list, path0, branchInstructions, branchingInstructionLine,
@@ -1099,7 +1099,7 @@ private static bool BuildPointsForConditionalBranch(List list, Inst
private static uint BuildPointsForBranch(List list, Instruction then, int branchingInstructionLine, string document,
int branchOffset, uint ordinal, int pathCounter, BranchPoint path0, List instructions, MethodDefinition methodDefinition)
{
- var pathOffsetList1 = GetBranchPath(@then);
+ List pathOffsetList1 = GetBranchPath(@then);
// Add path 1
var path1 = new BranchPoint
@@ -1119,7 +1119,7 @@ private static uint BuildPointsForBranch(List list, Instruction the
// only add branch if branch does not match a known sequence
// e.g. auto generated field assignment
// or encapsulates at least one sequence point
- var offsets = new[]
+ int[] offsets = new[]
{
path0.Offset,
path0.EndOffset,
@@ -1127,22 +1127,22 @@ private static uint BuildPointsForBranch(List list, Instruction the
path1.EndOffset
};
- var ignoreSequences = new[]
+ Code[][] ignoreSequences = new[]
{
// we may need other samples
new[] {Code.Brtrue_S, Code.Pop, Code.Ldsfld, Code.Ldftn, Code.Newobj, Code.Dup, Code.Stsfld, Code.Newobj}, // CachedAnonymousMethodDelegate field allocation
};
- var bs = offsets.Min();
- var be = offsets.Max();
+ int bs = offsets.Min();
+ int be = offsets.Max();
var range = instructions.Where(i => (i.Offset >= bs) && (i.Offset <= be)).ToList();
- var match = ignoreSequences
+ bool match = ignoreSequences
.Where(ignoreSequence => range.Count >= ignoreSequence.Length)
.Any(ignoreSequence => range.Zip(ignoreSequence, (instruction, code) => instruction.OpCode.Code == code).All(x => x));
- var count = range
+ int count = range
.Count(i => methodDefinition.DebugInformation.GetSequencePoint(i) != null);
if (!match || count > 0)
@@ -1156,7 +1156,7 @@ private static uint BuildPointsForBranch(List list, Instruction the
private static uint BuildPointsForSwitchCases(List list, BranchPoint path0, Instruction[] branchInstructions,
int branchingInstructionLine, string document, int branchOffset, uint ordinal, ref int pathCounter)
{
- var counter = pathCounter;
+ int counter = pathCounter;
list.Add(path0);
// Add Conditional Branches (Path>0)
list.AddRange(branchInstructions.Select(GetBranchPath)
@@ -1342,7 +1342,7 @@ instance void .ctor () cil managed
}
...
*/
- var autogeneratedBackingFields = methodDefinition.DeclaringType.Fields.Where(x =>
+ IEnumerable autogeneratedBackingFields = methodDefinition.DeclaringType.Fields.Where(x =>
x.CustomAttributes.Any(ca => ca.AttributeType.FullName.Equals(typeof(CompilerGeneratedAttribute).FullName)) &&
x.FullName.EndsWith("k__BackingField"));
@@ -1354,15 +1354,15 @@ instance void .ctor () cil managed
private static bool SkipDefaultInitializationSystemObject(Instruction instruction)
{
- /*
- A type always has a constructor with a default instantiation of System.Object. For record types these
- instructions can have a own sequence point. This means that even the default constructor would be instrumented.
- To skip this we search for call instructions with a method reference that declares System.Object.
+ /*
+ A type always has a constructor with a default instantiation of System.Object. For record types these
+ instructions can have a own sequence point. This means that even the default constructor would be instrumented.
+ To skip this we search for call instructions with a method reference that declares System.Object.
- IL_0000: ldarg.0
- IL_0001: call instance void [System.Runtime]System.Object::.ctor()
- IL_0006: ret
- */
+ IL_0000: ldarg.0
+ IL_0001: call instance void [System.Runtime]System.Object::.ctor()
+ IL_0006: ret
+ */
return instruction.OpCode == OpCodes.Ldarg &&
instruction.Next?.OpCode == OpCodes.Call &&
instruction.Next?.Operand is MethodReference mr && mr.DeclaringType.FullName.Equals(typeof(System.Object).FullName);
@@ -1378,7 +1378,7 @@ private static bool SkipBranchGeneratedExceptionFilter(Instruction branchInstruc
.Where(e => e.HandlerType == ExceptionHandlerType.Filter)
.ToList();
- foreach (var exceptionHandler in handlers)
+ foreach (ExceptionHandler exceptionHandler in handlers)
{
Instruction startFilter = exceptionHandler.FilterStart;
Instruction endFilter = startFilter;
@@ -1418,7 +1418,7 @@ private static bool SkipBranchGeneratedFinallyBlock(Instruction branchInstructio
private static int GetOffsetOfNextEndfinally(MethodBody body, int startOffset)
{
- var lastOffset = body.Instructions.LastOrDefault().Maybe(i => i.Offset, int.MaxValue);
+ int lastOffset = body.Instructions.LastOrDefault().Maybe(i => i.Offset, int.MaxValue);
return body.Instructions.FirstOrDefault(i => i.Offset >= startOffset && i.OpCode.Code == Code.Endfinally).Maybe(i => i.Offset, lastOffset);
}
@@ -1428,12 +1428,11 @@ private static List GetBranchPath(Instruction instruction)
if (instruction != null)
{
- var point = instruction;
+ Instruction point = instruction;
offsetList.Add(point.Offset);
while (point.OpCode == OpCodes.Br || point.OpCode == OpCodes.Br_S)
{
- var nextPoint = point.Operand as Instruction;
- if (nextPoint != null)
+ if (point.Operand is Instruction nextPoint)
{
point = nextPoint;
offsetList.Add(point.Offset);
@@ -1453,12 +1452,12 @@ private static Instruction FindClosestInstructionWithSequencePoint(MethodBody me
var sequencePointsInMethod = methodBody.Instructions.Where(i => HasValidSequencePoint(i, methodBody.Method)).ToList();
if (!sequencePointsInMethod.Any())
return null;
- var idx = sequencePointsInMethod.BinarySearch(instruction, new InstructionByOffsetComparer());
+ int idx = sequencePointsInMethod.BinarySearch(instruction, new InstructionByOffsetComparer());
Instruction prev;
if (idx < 0)
{
// no exact match, idx corresponds to the next, larger element
- var lower = Math.Max(~idx - 1, 0);
+ int lower = Math.Max(~idx - 1, 0);
prev = sequencePointsInMethod[lower];
}
else
@@ -1472,7 +1471,7 @@ private static Instruction FindClosestInstructionWithSequencePoint(MethodBody me
private static bool HasValidSequencePoint(Instruction instruction, MethodDefinition methodDefinition)
{
- var sp = methodDefinition.DebugInformation.GetSequencePoint(instruction);
+ SequencePoint sp = methodDefinition.DebugInformation.GetSequencePoint(instruction);
return sp != null && sp.StartLine != StepOverLineCode;
}
diff --git a/src/coverlet.core/coverlet.core.csproj b/src/coverlet.core/coverlet.core.csproj
index 7ef394311..93a6c972d 100644
--- a/src/coverlet.core/coverlet.core.csproj
+++ b/src/coverlet.core/coverlet.core.csproj
@@ -3,7 +3,6 @@
Library
netstandard2.0
- 5.7.2
false
@@ -14,7 +13,6 @@
-
diff --git a/src/coverlet.msbuild.tasks/BaseTask.cs b/src/coverlet.msbuild.tasks/BaseTask.cs
index 1cf3aa6ce..99606bda8 100644
--- a/src/coverlet.msbuild.tasks/BaseTask.cs
+++ b/src/coverlet.msbuild.tasks/BaseTask.cs
@@ -1,4 +1,7 @@
-using System;
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
using Microsoft.Build.Utilities;
namespace Coverlet.MSbuild.Tasks
diff --git a/src/coverlet.msbuild.tasks/CoverageResultTask.cs b/src/coverlet.msbuild.tasks/CoverageResultTask.cs
index ebc17a0e4..23a29f7a5 100644
--- a/src/coverlet.msbuild.tasks/CoverageResultTask.cs
+++ b/src/coverlet.msbuild.tasks/CoverageResultTask.cs
@@ -1,3 +1,6 @@
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -17,7 +20,7 @@ namespace Coverlet.MSbuild.Tasks
{
public class CoverageResultTask : BaseTask
{
- private MSBuildLogger _logger;
+ private readonly MSBuildLogger _logger;
[Required]
public string Output { get; set; }
@@ -63,11 +66,11 @@ public override bool Execute()
Coverage coverage = null;
using (Stream instrumenterStateStream = fileSystem.NewFileStream(InstrumenterState.ItemSpec, FileMode.Open))
{
- var instrumentationHelper = ServiceProvider.GetService();
+ IInstrumentationHelper instrumentationHelper = ServiceProvider.GetService();
// Task.Log is teared down after a task and thus the new MSBuildLogger must be passed to the InstrumentationHelper
// https://github.com/microsoft/msbuild/issues/5153
instrumentationHelper.SetLogger(_logger);
- coverage = new Coverage(CoveragePrepareResult.Deserialize(instrumenterStateStream), this._logger, ServiceProvider.GetService(), fileSystem, ServiceProvider.GetService());
+ coverage = new Coverage(CoveragePrepareResult.Deserialize(instrumenterStateStream), _logger, ServiceProvider.GetService(), fileSystem, ServiceProvider.GetService());
}
try
@@ -82,7 +85,7 @@ public override bool Execute()
CoverageResult result = coverage.GetCoverageResult();
- var directory = Path.GetDirectoryName(Output);
+ string directory = Path.GetDirectoryName(Output);
if (directory == string.Empty)
{
directory = Directory.GetCurrentDirectory();
@@ -92,12 +95,12 @@ public override bool Execute()
Directory.CreateDirectory(directory);
}
- var formats = OutputFormat.Split(',');
+ string[] formats = OutputFormat.Split(',');
var coverageReportPaths = new List(formats.Length);
ISourceRootTranslator sourceRootTranslator = ServiceProvider.GetService();
- foreach (var format in formats)
+ foreach (string format in formats)
{
- var reporter = new ReporterFactory(format).CreateReporter();
+ IReporter reporter = new ReporterFactory(format).CreateReporter();
if (reporter == null)
{
throw new Exception($"Specified output format '{format}' is not supported");
@@ -119,7 +122,7 @@ public override bool Execute()
ServiceProvider.GetService(),
result,
sourceRootTranslator);
- var path = writer.WriteReport();
+ string path = writer.WriteReport();
var metadata = new Dictionary { ["Format"] = format };
coverageReportPaths.Add(new TaskItem(path, metadata));
}
@@ -129,7 +132,7 @@ public override bool Execute()
var thresholdTypeFlagQueue = new Queue();
- foreach (var thresholdType in ThresholdType.Split(',').Select(t => t.Trim()))
+ foreach (string thresholdType in ThresholdType.Split(',').Select(t => t.Trim()))
{
if (thresholdType.Equals("line", StringComparison.OrdinalIgnoreCase))
{
@@ -144,19 +147,19 @@ public override bool Execute()
thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Method);
}
}
-
- Dictionary thresholdTypeFlagValues = new Dictionary();
+
+ var thresholdTypeFlagValues = new Dictionary();
if (Threshold.Contains(','))
{
- var thresholdValues = Threshold.Split(new char[] {','}, StringSplitOptions.RemoveEmptyEntries).Select(t => t.Trim());
- if(thresholdValues.Count() != thresholdTypeFlagQueue.Count())
+ IEnumerable thresholdValues = Threshold.Split(new char[] { ',' }, 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");
+ throw new Exception($"Threshold type flag count ({thresholdTypeFlagQueue.Count}) and values count ({thresholdValues.Count()}) doesn't match");
}
- foreach (var threshold in thresholdValues)
+ foreach (string threshold in thresholdValues)
{
- if (double.TryParse(threshold, out var value))
+ if (double.TryParse(threshold, out double value))
{
thresholdTypeFlagValues[thresholdTypeFlagQueue.Dequeue()] = value;
}
@@ -175,8 +178,8 @@ public override bool Execute()
thresholdTypeFlagValues[thresholdTypeFlagQueue.Dequeue()] = thresholdValue;
}
}
-
- var thresholdStat = ThresholdStatistic.Minimum;
+
+ ThresholdStatistic thresholdStat = ThresholdStatistic.Minimum;
if (ThresholdStat.Equals("average", StringComparison.OrdinalIgnoreCase))
{
thresholdStat = ThresholdStatistic.Average;
@@ -189,23 +192,23 @@ public override bool Execute()
var coverageTable = new ConsoleTable("Module", "Line", "Branch", "Method");
var summary = new CoverageSummary();
- var linePercentCalculation = summary.CalculateLineCoverage(result.Modules);
- var branchPercentCalculation = summary.CalculateBranchCoverage(result.Modules);
- var methodPercentCalculation = summary.CalculateMethodCoverage(result.Modules);
+ CoverageDetails linePercentCalculation = summary.CalculateLineCoverage(result.Modules);
+ CoverageDetails branchPercentCalculation = summary.CalculateBranchCoverage(result.Modules);
+ CoverageDetails methodPercentCalculation = summary.CalculateMethodCoverage(result.Modules);
- var totalLinePercent = linePercentCalculation.Percent;
- var totalBranchPercent = branchPercentCalculation.Percent;
- var totalMethodPercent = methodPercentCalculation.Percent;
+ double totalLinePercent = linePercentCalculation.Percent;
+ double totalBranchPercent = branchPercentCalculation.Percent;
+ double totalMethodPercent = methodPercentCalculation.Percent;
- var averageLinePercent = linePercentCalculation.AverageModulePercent;
- var averageBranchPercent = branchPercentCalculation.AverageModulePercent;
- var averageMethodPercent = methodPercentCalculation.AverageModulePercent;
+ double averageLinePercent = linePercentCalculation.AverageModulePercent;
+ double averageBranchPercent = branchPercentCalculation.AverageModulePercent;
+ double averageMethodPercent = methodPercentCalculation.AverageModulePercent;
- foreach (var module in result.Modules)
+ foreach (KeyValuePair module in result.Modules)
{
- var linePercent = summary.CalculateLineCoverage(module.Value).Percent;
- var branchPercent = summary.CalculateBranchCoverage(module.Value).Percent;
- var methodPercent = summary.CalculateMethodCoverage(module.Value).Percent;
+ 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)}%");
}
@@ -222,7 +225,7 @@ public override bool Execute()
Console.WriteLine(coverageTable.ToStringAlternative());
- var thresholdTypeFlags = result.GetThresholdTypesBelowThreshold(summary, thresholdTypeFlagValues, thresholdStat);
+ ThresholdTypeFlags thresholdTypeFlags = result.GetThresholdTypesBelowThreshold(summary, thresholdTypeFlagValues, thresholdStat);
if (thresholdTypeFlags != ThresholdTypeFlags.None)
{
var exceptionMessageBuilder = new StringBuilder();
diff --git a/src/coverlet.msbuild.tasks/InstrumentationTask.cs b/src/coverlet.msbuild.tasks/InstrumentationTask.cs
index 9be72b5c6..9a0506f51 100644
--- a/src/coverlet.msbuild.tasks/InstrumentationTask.cs
+++ b/src/coverlet.msbuild.tasks/InstrumentationTask.cs
@@ -1,7 +1,9 @@
-using System;
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
using System.Diagnostics;
using System.IO;
-
using Coverlet.Core;
using Coverlet.Core.Abstractions;
using Coverlet.Core.Helpers;
@@ -9,6 +11,7 @@
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using Microsoft.Extensions.DependencyInjection;
+
using ILogger = Coverlet.Core.Abstractions.ILogger;
namespace Coverlet.MSbuild.Tasks
@@ -44,6 +47,8 @@ public class InstrumentationTask : BaseTask
public bool DeterministicReport { get; set; }
+ public string ExcludeAssembliesWithoutSources { get; set; }
+
[Output]
public ITaskItem InstrumenterState { get; set; }
@@ -68,11 +73,13 @@ public override bool Execute()
IServiceCollection serviceCollection = new ServiceCollection();
serviceCollection.AddTransient();
serviceCollection.AddTransient();
+ serviceCollection.AddTransient();
serviceCollection.AddTransient();
serviceCollection.AddTransient(_ => _logger);
serviceCollection.AddTransient();
// We cache resolutions
- serviceCollection.AddSingleton(serviceProvider => new SourceRootTranslator(Path, serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService()));
+ serviceCollection.AddSingleton(serviceProvider =>
+ new SourceRootTranslator(Path, serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService()));
// We need to keep singleton/static semantics
serviceCollection.AddSingleton();
serviceCollection.AddSingleton();
@@ -81,9 +88,9 @@ public override bool Execute()
try
{
- var fileSystem = ServiceProvider.GetService();
+ IFileSystem fileSystem = ServiceProvider.GetService();
- CoverageParameters parameters = new CoverageParameters
+ var parameters = new CoverageParameters
{
IncludeFilters = Include?.Split(','),
IncludeDirectories = IncludeDirectory?.Split(','),
@@ -96,10 +103,11 @@ public override bool Execute()
UseSourceLink = UseSourceLink,
SkipAutoProps = SkipAutoProps,
DeterministicReport = DeterministicReport,
+ ExcludeAssembliesWithoutSources = ExcludeAssembliesWithoutSources,
DoesNotReturnAttributes = DoesNotReturnAttribute?.Split(',')
};
- Coverage coverage = new Coverage(Path,
+ var coverage = new Coverage(Path,
parameters,
_logger,
ServiceProvider.GetService(),
@@ -109,13 +117,9 @@ public override bool Execute()
CoveragePrepareResult prepareResult = coverage.PrepareModules();
InstrumenterState = new TaskItem(System.IO.Path.GetTempFileName());
- using (var instrumentedStateFile = fileSystem.NewFileStream(InstrumenterState.ItemSpec, FileMode.Open, FileAccess.Write))
- {
- using (Stream serializedState = CoveragePrepareResult.Serialize(prepareResult))
- {
- serializedState.CopyTo(instrumentedStateFile);
- }
- }
+ using Stream instrumentedStateFile = fileSystem.NewFileStream(InstrumenterState.ItemSpec, FileMode.Open, FileAccess.Write);
+ using Stream serializedState = CoveragePrepareResult.Serialize(prepareResult);
+ serializedState.CopyTo(instrumentedStateFile);
}
catch (Exception ex)
{
diff --git a/src/coverlet.msbuild.tasks/MSBuildLogger.cs b/src/coverlet.msbuild.tasks/MSBuildLogger.cs
index e35f0af82..33fe3afaf 100644
--- a/src/coverlet.msbuild.tasks/MSBuildLogger.cs
+++ b/src/coverlet.msbuild.tasks/MSBuildLogger.cs
@@ -1,5 +1,7 @@
-using System;
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using System;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using ILogger = Coverlet.Core.Abstractions.ILogger;
diff --git a/src/coverlet.msbuild.tasks/Properties/AssemblyInfo.cs b/src/coverlet.msbuild.tasks/Properties/AssemblyInfo.cs
index 944948378..4ff5754c3 100644
--- a/src/coverlet.msbuild.tasks/Properties/AssemblyInfo.cs
+++ b/src/coverlet.msbuild.tasks/Properties/AssemblyInfo.cs
@@ -1,3 +1,6 @@
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
using System.Reflection;
using System.Runtime.CompilerServices;
diff --git a/src/coverlet.msbuild.tasks/ReportWriter.cs b/src/coverlet.msbuild.tasks/ReportWriter.cs
index 1261ce76c..8b72b9767 100644
--- a/src/coverlet.msbuild.tasks/ReportWriter.cs
+++ b/src/coverlet.msbuild.tasks/ReportWriter.cs
@@ -1,8 +1,9 @@
-using System.IO;
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using System.IO;
using Coverlet.Core;
using Coverlet.Core.Abstractions;
-using Coverlet.Core.Reporters;
namespace Coverlet.MSbuild.Tasks
{
diff --git a/src/coverlet.msbuild.tasks/coverlet.msbuild.props b/src/coverlet.msbuild.tasks/coverlet.msbuild.props
index 3821845ec..9356dbdf7 100644
--- a/src/coverlet.msbuild.tasks/coverlet.msbuild.props
+++ b/src/coverlet.msbuild.tasks/coverlet.msbuild.props
@@ -16,6 +16,7 @@
0
line,branch,method
minimum
+
$(MSBuildThisFileDirectory)
diff --git a/src/coverlet.msbuild.tasks/coverlet.msbuild.targets b/src/coverlet.msbuild.tasks/coverlet.msbuild.targets
index c271ec2d1..ed288de59 100644
--- a/src/coverlet.msbuild.tasks/coverlet.msbuild.targets
+++ b/src/coverlet.msbuild.tasks/coverlet.msbuild.targets
@@ -24,7 +24,7 @@
<_mapping Include="@(_byProject->'%(Identity)|%(OriginalPath)=%(MappedPath)')" />
- <_sourceRootMappingFilePath>$([MSBuild]::EnsureTrailingSlash('$(OutputPath)'))CoverletSourceRootsMapping
+ <_sourceRootMappingFilePath>$([MSBuild]::EnsureTrailingSlash('$(OutputPath)'))CoverletSourceRootsMapping_$(AssemblyName)
+ DoesNotReturnAttribute="$(DoesNotReturnAttribute)"
+ ExcludeAssembliesWithoutSources="$(ExcludeAssembliesWithoutSources)">
diff --git a/test/coverlet.collector.tests/AttachmentManagerTests.cs b/test/coverlet.collector.tests/AttachmentManagerTests.cs
index 9adb4c731..9e021212d 100644
--- a/test/coverlet.collector.tests/AttachmentManagerTests.cs
+++ b/test/coverlet.collector.tests/AttachmentManagerTests.cs
@@ -1,4 +1,7 @@
-using System;
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
using System.ComponentModel;
using System.IO;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
@@ -14,14 +17,14 @@ namespace Coverlet.Collector.Tests
public class AttachmentManagerTests
{
private AttachmentManager _attachmentManager;
- private Mock _mockDataCollectionSink;
- private DataCollectionContext _dataCollectionContext;
- private TestPlatformLogger _testPlatformLogger;
- private TestPlatformEqtTrace _eqtTrace;
- private Mock _mockFileHelper;
- private Mock _mockDirectoryHelper;
- private Mock _mockCountDownEvent;
- private Mock _mockDataCollectionLogger;
+ private readonly Mock _mockDataCollectionSink;
+ private readonly DataCollectionContext _dataCollectionContext;
+ private readonly TestPlatformLogger _testPlatformLogger;
+ private readonly TestPlatformEqtTrace _eqtTrace;
+ private readonly Mock _mockFileHelper;
+ private readonly Mock _mockDirectoryHelper;
+ private readonly Mock _mockCountDownEvent;
+ private readonly Mock _mockDataCollectionLogger;
public AttachmentManagerTests()
{
@@ -71,7 +74,7 @@ public void SendCoverageReportShouldThrowExceptionWhenFailedToSaveReportToFile()
[Fact]
public void SendCoverageReportShouldSendAttachmentToTestPlatform()
{
- var directory = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()));
+ DirectoryInfo directory = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()));
_attachmentManager = new AttachmentManager(_mockDataCollectionSink.Object, _dataCollectionContext, _testPlatformLogger,
_eqtTrace, directory.ToString(), new FileHelper(), _mockDirectoryHelper.Object, _mockCountDownEvent.Object);
diff --git a/test/coverlet.collector.tests/CoverletCoverageDataCollectorTests.cs b/test/coverlet.collector.tests/CoverletCoverageDataCollectorTests.cs
index 3c0346d20..6bb1af73b 100644
--- a/test/coverlet.collector.tests/CoverletCoverageDataCollectorTests.cs
+++ b/test/coverlet.collector.tests/CoverletCoverageDataCollectorTests.cs
@@ -1,9 +1,11 @@
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;
-
using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection;
using Moq;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
@@ -22,29 +24,32 @@ namespace Coverlet.Collector.Tests
{
public class CoverletCoverageDataCollectorTests
{
- private DataCollectionEnvironmentContext _context;
+ private readonly DataCollectionEnvironmentContext _context;
private CoverletCoverageCollector _coverletCoverageDataCollector;
- private DataCollectionContext _dataCollectionContext;
- private Mock _mockDataColectionEvents;
- private Mock _mockDataCollectionSink;
- private Mock _mockCoverageWrapper;
- private Mock _mockCountDownEventFactory;
+ private readonly DataCollectionContext _dataCollectionContext;
+ private readonly Mock _mockDataCollectionEvents;
+ private readonly Mock _mockDataCollectionSink;
+ private readonly Mock _mockCoverageWrapper;
+ private readonly Mock _mockCountDownEventFactory;
private XmlElement _configurationElement;
- private Mock _mockLogger;
+ private readonly Mock _mockLogger;
+ private readonly Mock _mockAssemblyAdapter;
public CoverletCoverageDataCollectorTests()
{
- _mockDataColectionEvents = new Mock();
+ _mockDataCollectionEvents = new Mock();
_mockDataCollectionSink = new Mock();
_mockLogger = new Mock();
_configurationElement = null;
- TestCase testcase = new TestCase { Id = Guid.NewGuid() };
+ var testcase = new TestCase { Id = Guid.NewGuid() };
_dataCollectionContext = new DataCollectionContext(testcase);
_context = new DataCollectionEnvironmentContext(_dataCollectionContext);
_mockCoverageWrapper = new Mock();
_mockCountDownEventFactory = new Mock();
_mockCountDownEventFactory.Setup(def => def.Create(It.IsAny(), It.IsAny())).Returns(new Mock().Object);
+ _mockAssemblyAdapter = new Mock();
+ _mockAssemblyAdapter.Setup(x => x.GetAssemblyName(It.IsAny())).Returns("abc");
}
[Fact]
@@ -53,7 +58,7 @@ public void OnSessionStartShouldInitializeCoverageWithCorrectCoverletSettings()
Func serviceCollectionFactory = (TestPlatformEqtTrace eqtTrace, TestPlatformLogger logger, string testModule) =>
{
IServiceCollection serviceCollection = new ServiceCollection();
- Mock fileSystem = new Mock();
+ var fileSystem = new Mock();
fileSystem.Setup(f => f.Exists(It.IsAny())).Returns((string testLib) => testLib == "abc.dll");
serviceCollection.AddTransient(_ => fileSystem.Object);
@@ -61,14 +66,14 @@ public void OnSessionStartShouldInitializeCoverageWithCorrectCoverletSettings()
serviceCollection.AddTransient();
serviceCollection.AddTransient(_ => new CoverletLogger(eqtTrace, logger));
serviceCollection.AddSingleton();
- serviceCollection.AddSingleton(serviceProvider => new SourceRootTranslator(testModule, serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService()));
+ serviceCollection.AddSingleton(serviceProvider => new SourceRootTranslator(testModule, serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService(), _mockAssemblyAdapter.Object));
serviceCollection.AddSingleton();
return serviceCollection;
};
_coverletCoverageDataCollector = new CoverletCoverageCollector(new TestPlatformEqtTrace(), _mockCoverageWrapper.Object, _mockCountDownEventFactory.Object, serviceCollectionFactory);
_coverletCoverageDataCollector.Initialize(
_configurationElement,
- _mockDataColectionEvents.Object,
+ _mockDataCollectionEvents.Object,
_mockDataCollectionSink.Object,
_mockLogger.Object,
_context);
@@ -76,7 +81,7 @@ public void OnSessionStartShouldInitializeCoverageWithCorrectCoverletSettings()
sessionStartProperties.Add("TestSources", new List { "abc.dll" });
- _mockDataColectionEvents.Raise(x => x.SessionStart += null, new SessionStartEventArgs(sessionStartProperties));
+ _mockDataCollectionEvents.Raise(x => x.SessionStart += null, new SessionStartEventArgs(sessionStartProperties));
_mockCoverageWrapper.Verify(x => x.CreateCoverage(It.Is(y => string.Equals(y.TestModule, "abc.dll")), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once);
}
@@ -87,7 +92,7 @@ public void OnSessionStartShouldPrepareModulesForCoverage()
Func serviceCollectionFactory = (TestPlatformEqtTrace eqtTrace, TestPlatformLogger logger, string testModule) =>
{
IServiceCollection serviceCollection = new ServiceCollection();
- Mock fileSystem = new Mock();
+ var fileSystem = new Mock();
fileSystem.Setup(f => f.Exists(It.IsAny())).Returns((string testLib) => testLib == "abc.dll");
serviceCollection.AddTransient(_ => fileSystem.Object);
@@ -95,14 +100,14 @@ public void OnSessionStartShouldPrepareModulesForCoverage()
serviceCollection.AddTransient();
serviceCollection.AddTransient(_ => new CoverletLogger(eqtTrace, logger));
serviceCollection.AddSingleton();
- serviceCollection.AddSingleton(serviceProvider => new SourceRootTranslator(testModule, serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService()));
+ serviceCollection.AddSingleton(serviceProvider => new SourceRootTranslator(testModule, serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService(), _mockAssemblyAdapter.Object));
serviceCollection.AddSingleton();
return serviceCollection;
};
_coverletCoverageDataCollector = new CoverletCoverageCollector(new TestPlatformEqtTrace(), _mockCoverageWrapper.Object, _mockCountDownEventFactory.Object, serviceCollectionFactory);
_coverletCoverageDataCollector.Initialize(
_configurationElement,
- _mockDataColectionEvents.Object,
+ _mockDataCollectionEvents.Object,
_mockDataCollectionSink.Object,
null,
_context);
@@ -114,7 +119,7 @@ public void OnSessionStartShouldPrepareModulesForCoverage()
new Mock().Object,
new Mock().Object);
- CoverageParameters parameters = new CoverageParameters
+ var parameters = new CoverageParameters
{
IncludeFilters = null,
IncludeDirectories = null,
@@ -126,12 +131,12 @@ public void OnSessionStartShouldPrepareModulesForCoverage()
UseSourceLink = true
};
- Coverage coverage = new Coverage("abc.dll", parameters, It.IsAny(), instrumentationHelper, new Mock().Object, new Mock().Object, new Mock().Object);
+ var coverage = new Coverage("abc.dll", parameters, It.IsAny(), instrumentationHelper, new Mock().Object, new Mock().Object, new Mock().Object);
sessionStartProperties.Add("TestSources", new List { "abc.dll" });
_mockCoverageWrapper.Setup(x => x.CreateCoverage(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).Returns(coverage);
- _mockDataColectionEvents.Raise(x => x.SessionStart += null, new SessionStartEventArgs(sessionStartProperties));
+ _mockDataCollectionEvents.Raise(x => x.SessionStart += null, new SessionStartEventArgs(sessionStartProperties));
_mockCoverageWrapper.Verify(x => x.CreateCoverage(It.Is(y => y.TestModule.Contains("abc.dll")), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once);
_mockCoverageWrapper.Verify(x => x.PrepareModules(It.IsAny()), Times.Once);
@@ -148,14 +153,14 @@ public void OnSessionEndShouldSendGetCoverageReportToTestPlatform()
serviceCollection.AddTransient();
serviceCollection.AddTransient(_ => new CoverletLogger(eqtTrace, logger));
serviceCollection.AddSingleton();
- serviceCollection.AddSingleton(serviceProvider => new SourceRootTranslator(testModule, serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService()));
+ serviceCollection.AddSingleton(serviceProvider => new SourceRootTranslator(testModule, serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService(), _mockAssemblyAdapter.Object));
serviceCollection.AddSingleton();
return serviceCollection;
};
_coverletCoverageDataCollector = new CoverletCoverageCollector(new TestPlatformEqtTrace(), new CoverageWrapper(), _mockCountDownEventFactory.Object, serviceCollectionFactory);
_coverletCoverageDataCollector.Initialize(
_configurationElement,
- _mockDataColectionEvents.Object,
+ _mockDataCollectionEvents.Object,
_mockDataCollectionSink.Object,
_mockLogger.Object,
_context);
@@ -163,7 +168,7 @@ public void OnSessionEndShouldSendGetCoverageReportToTestPlatform()
string module = GetType().Assembly.Location;
string pdb = Path.Combine(Path.GetDirectoryName(module), Path.GetFileNameWithoutExtension(module) + ".pdb");
- var directory = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()));
+ DirectoryInfo directory = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()));
File.Copy(module, Path.Combine(directory.FullName, Path.GetFileName(module)), true);
File.Copy(pdb, Path.Combine(directory.FullName, Path.GetFileName(pdb)), true);
@@ -171,8 +176,8 @@ public void OnSessionEndShouldSendGetCoverageReportToTestPlatform()
IDictionary sessionStartProperties = new Dictionary();
sessionStartProperties.Add("TestSources", new List { Path.Combine(directory.FullName, Path.GetFileName(module)) });
- _mockDataColectionEvents.Raise(x => x.SessionStart += null, new SessionStartEventArgs(sessionStartProperties));
- _mockDataColectionEvents.Raise(x => x.SessionEnd += null, new SessionEndEventArgs());
+ _mockDataCollectionEvents.Raise(x => x.SessionStart += null, new SessionStartEventArgs(sessionStartProperties));
+ _mockDataCollectionEvents.Raise(x => x.SessionEnd += null, new SessionEndEventArgs());
_mockDataCollectionSink.Verify(x => x.SendFileAsync(It.IsAny()), Times.Once);
@@ -188,22 +193,23 @@ public void OnSessionEndShouldSendCoverageReportsForMultipleFormatsToTestPlatfor
Func serviceCollectionFactory = (TestPlatformEqtTrace eqtTrace, TestPlatformLogger logger, string testModule) =>
{
IServiceCollection serviceCollection = new ServiceCollection();
- Mock fileSystem = new Mock();
+ var fileSystem = new Mock();
fileSystem.Setup(f => f.Exists(It.IsAny())).Returns((string testLib) => testLib == "Test");
serviceCollection.AddTransient(_ => fileSystem.Object);
serviceCollection.AddTransient();
serviceCollection.AddTransient();
+ serviceCollection.AddTransient();
serviceCollection.AddTransient(_ => new CoverletLogger(eqtTrace, logger));
serviceCollection.AddSingleton();
- serviceCollection.AddSingleton(serviceProvider => new SourceRootTranslator(testModule, serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService()));
+ serviceCollection.AddSingleton(serviceProvider => new SourceRootTranslator(testModule, serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService(), _mockAssemblyAdapter.Object));
serviceCollection.AddSingleton();
return serviceCollection;
};
_coverletCoverageDataCollector = new CoverletCoverageCollector(new TestPlatformEqtTrace(), new CoverageWrapper(), _mockCountDownEventFactory.Object, serviceCollectionFactory);
IList reporters = formats.Split(',').Select(f => new ReporterFactory(f).CreateReporter()).Where(x => x != null).ToList();
- Mock mockDataCollectionSink = new Mock();
+ var mockDataCollectionSink = new Mock();
mockDataCollectionSink.Setup(m => m.SendFileAsync(It.IsAny())).Callback(fti =>
{
reporters.Remove(reporters.First(x =>
@@ -212,8 +218,8 @@ public void OnSessionEndShouldSendCoverageReportsForMultipleFormatsToTestPlatfor
});
var doc = new XmlDocument();
- var root = doc.CreateElement("Configuration");
- var element = doc.CreateElement("Format");
+ XmlElement root = doc.CreateElement("Configuration");
+ XmlElement element = doc.CreateElement("Format");
element.AppendChild(doc.CreateTextNode(formats));
root.AppendChild(element);
@@ -221,15 +227,15 @@ public void OnSessionEndShouldSendCoverageReportsForMultipleFormatsToTestPlatfor
_coverletCoverageDataCollector.Initialize(
_configurationElement,
- _mockDataColectionEvents.Object,
+ _mockDataCollectionEvents.Object,
mockDataCollectionSink.Object,
_mockLogger.Object,
_context);
var sessionStartProperties = new Dictionary { { "TestSources", new List { "Test" } } };
- _mockDataColectionEvents.Raise(x => x.SessionStart += null, new SessionStartEventArgs(sessionStartProperties));
- _mockDataColectionEvents.Raise(x => x.SessionEnd += null, new SessionEndEventArgs());
+ _mockDataCollectionEvents.Raise(x => x.SessionStart += null, new SessionStartEventArgs(sessionStartProperties));
+ _mockDataCollectionEvents.Raise(x => x.SessionEnd += null, new SessionEndEventArgs());
mockDataCollectionSink.Verify(x => x.SendFileAsync(It.IsAny()), Times.Exactly(sendReportsCount));
Assert.Empty(reporters);
@@ -241,22 +247,23 @@ public void OnSessionStartShouldLogWarningIfInstrumentationFailed()
Func serviceCollectionFactory = (TestPlatformEqtTrace eqtTrace, TestPlatformLogger logger, string testModule) =>
{
IServiceCollection serviceCollection = new ServiceCollection();
- Mock fileSystem = new Mock();
+ var fileSystem = new Mock();
fileSystem.Setup(f => f.Exists(It.IsAny())).Returns((string testLib) => testLib == "abc.dll");
serviceCollection.AddTransient(_ => fileSystem.Object);
serviceCollection.AddTransient();
serviceCollection.AddTransient();
+ serviceCollection.AddTransient();
serviceCollection.AddTransient(_ => new CoverletLogger(eqtTrace, logger));
serviceCollection.AddSingleton();
- serviceCollection.AddSingleton(serviceProvider => new SourceRootTranslator(testModule, serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService()));
+ serviceCollection.AddSingleton(serviceProvider => new SourceRootTranslator(testModule, serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService(), _mockAssemblyAdapter.Object));
serviceCollection.AddSingleton();
return serviceCollection;
};
_coverletCoverageDataCollector = new CoverletCoverageCollector(new TestPlatformEqtTrace(), _mockCoverageWrapper.Object, _mockCountDownEventFactory.Object, serviceCollectionFactory);
_coverletCoverageDataCollector.Initialize(
_configurationElement,
- _mockDataColectionEvents.Object,
+ _mockDataCollectionEvents.Object,
_mockDataCollectionSink.Object,
_mockLogger.Object,
_context);
@@ -266,7 +273,7 @@ public void OnSessionStartShouldLogWarningIfInstrumentationFailed()
_mockCoverageWrapper.Setup(x => x.PrepareModules(It.IsAny())).Throws(new FileNotFoundException());
- _mockDataColectionEvents.Raise(x => x.SessionStart += null, new SessionStartEventArgs(sessionStartProperties));
+ _mockDataCollectionEvents.Raise(x => x.SessionStart += null, new SessionStartEventArgs(sessionStartProperties));
_mockLogger.Verify(x => x.LogWarning(_dataCollectionContext,
It.Is(y => y.Contains("CoverletDataCollectorException"))));
diff --git a/test/coverlet.collector.tests/CoverletSettingsParserTests.cs b/test/coverlet.collector.tests/CoverletSettingsParserTests.cs
index ebcba0b3f..191bdb232 100644
--- a/test/coverlet.collector.tests/CoverletSettingsParserTests.cs
+++ b/test/coverlet.collector.tests/CoverletSettingsParserTests.cs
@@ -1,4 +1,7 @@
-using System.Collections.Generic;
+// 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 System.Linq;
using System.Xml;
using Coverlet.Collector.DataCollection;
@@ -9,7 +12,7 @@ namespace Coverlet.Collector.Tests
{
public class CoverletSettingsParserTests
{
- private CoverletSettingsParser _coverletSettingsParser;
+ private readonly CoverletSettingsParser _coverletSettingsParser;
public CoverletSettingsParserTests()
{
@@ -64,19 +67,19 @@ public void ParseShouldCorrectlyParseConfigurationElement(string includeFilters,
{
var testModules = new List { "abc.dll" };
var doc = new XmlDocument();
- var configElement = doc.CreateElement("Configuration");
- this.CreateCoverletNodes(doc, configElement, CoverletConstants.IncludeFiltersElementName, includeFilters);
- this.CreateCoverletNodes(doc, configElement, CoverletConstants.ExcludeFiltersElementName, excludeFilters);
- this.CreateCoverletNodes(doc, configElement, CoverletConstants.IncludeDirectoriesElementName, includeDirectories);
- this.CreateCoverletNodes(doc, configElement, CoverletConstants.ExcludeSourceFilesElementName, excludeSourceFiles);
- this.CreateCoverletNodes(doc, configElement, CoverletConstants.ExcludeAttributesElementName, excludeAttributes);
- this.CreateCoverletNodes(doc, configElement, CoverletConstants.MergeWithElementName, "/path/to/result.json");
- this.CreateCoverletNodes(doc, configElement, CoverletConstants.UseSourceLinkElementName, "false");
- this.CreateCoverletNodes(doc, configElement, CoverletConstants.SingleHitElementName, "true");
- this.CreateCoverletNodes(doc, configElement, CoverletConstants.IncludeTestAssemblyElementName, "true");
- this.CreateCoverletNodes(doc, configElement, CoverletConstants.SkipAutoProps, "true");
- this.CreateCoverletNodes(doc, configElement, CoverletConstants.DeterministicReport, "true");
- this.CreateCoverletNodes(doc, configElement, CoverletConstants.DoesNotReturnAttributesElementName, doesNotReturnAttributes);
+ XmlElement configElement = doc.CreateElement("Configuration");
+ CreateCoverletNodes(doc, configElement, CoverletConstants.IncludeFiltersElementName, includeFilters);
+ CreateCoverletNodes(doc, configElement, CoverletConstants.ExcludeFiltersElementName, excludeFilters);
+ CreateCoverletNodes(doc, configElement, CoverletConstants.IncludeDirectoriesElementName, includeDirectories);
+ CreateCoverletNodes(doc, configElement, CoverletConstants.ExcludeSourceFilesElementName, excludeSourceFiles);
+ CreateCoverletNodes(doc, configElement, CoverletConstants.ExcludeAttributesElementName, excludeAttributes);
+ CreateCoverletNodes(doc, configElement, CoverletConstants.MergeWithElementName, "/path/to/result.json");
+ CreateCoverletNodes(doc, configElement, CoverletConstants.UseSourceLinkElementName, "false");
+ CreateCoverletNodes(doc, configElement, CoverletConstants.SingleHitElementName, "true");
+ CreateCoverletNodes(doc, configElement, CoverletConstants.IncludeTestAssemblyElementName, "true");
+ CreateCoverletNodes(doc, configElement, CoverletConstants.SkipAutoProps, "true");
+ CreateCoverletNodes(doc, configElement, CoverletConstants.DeterministicReport, "true");
+ CreateCoverletNodes(doc, configElement, CoverletConstants.DoesNotReturnAttributesElementName, doesNotReturnAttributes);
CoverletSettings coverletSettings = _coverletSettingsParser.Parse(configElement, testModules);
@@ -109,12 +112,12 @@ public void ParseShouldCorrectlyParseConfigurationElementWithNullInnerText()
{
var testModules = new List { "abc.dll" };
var doc = new XmlDocument();
- var configElement = doc.CreateElement("Configuration");
- this.CreateCoverletNullInnerTextNodes(doc, configElement, CoverletConstants.IncludeFiltersElementName);
- this.CreateCoverletNullInnerTextNodes(doc, configElement, CoverletConstants.ExcludeFiltersElementName);
- this.CreateCoverletNullInnerTextNodes(doc, configElement, CoverletConstants.IncludeDirectoriesElementName);
- this.CreateCoverletNullInnerTextNodes(doc, configElement, CoverletConstants.ExcludeSourceFilesElementName);
- this.CreateCoverletNullInnerTextNodes(doc, configElement, CoverletConstants.ExcludeAttributesElementName);
+ XmlElement configElement = doc.CreateElement("Configuration");
+ CreateCoverletNullInnerTextNodes(doc, configElement, CoverletConstants.IncludeFiltersElementName);
+ CreateCoverletNullInnerTextNodes(doc, configElement, CoverletConstants.ExcludeFiltersElementName);
+ CreateCoverletNullInnerTextNodes(doc, configElement, CoverletConstants.IncludeDirectoriesElementName);
+ CreateCoverletNullInnerTextNodes(doc, configElement, CoverletConstants.ExcludeSourceFilesElementName);
+ CreateCoverletNullInnerTextNodes(doc, configElement, CoverletConstants.ExcludeAttributesElementName);
CoverletSettings coverletSettings = _coverletSettingsParser.Parse(configElement, testModules);
@@ -131,7 +134,7 @@ public void ParseShouldCorrectlyParseConfigurationElementWithNullElements()
{
var testModules = new List { "abc.dll" };
var doc = new XmlDocument();
- var configElement = doc.CreateElement("Configuration");
+ XmlElement configElement = doc.CreateElement("Configuration");
CoverletSettings coverletSettings = _coverletSettingsParser.Parse(configElement, testModules);
@@ -160,8 +163,8 @@ public void ParseShouldCorrectlyParseMultipleFormats(string formats, int formats
{
var testModules = new List { "abc.dll" };
var doc = new XmlDocument();
- var configElement = doc.CreateElement("Configuration");
- this.CreateCoverletNodes(doc, configElement, CoverletConstants.ReportFormatElementName, formats);
+ XmlElement configElement = doc.CreateElement("Configuration");
+ CreateCoverletNodes(doc, configElement, CoverletConstants.ReportFormatElementName, formats);
CoverletSettings coverletSettings = _coverletSettingsParser.Parse(configElement, testModules);
@@ -175,26 +178,26 @@ public void ParseShouldCorrectlyParseMultipleFormats(string formats, int formats
public void ParseShouldUseDefaultFormatWhenNoFormatSpecified(string formats)
{
var testModules = new List { "abc.dll" };
- var defaultFormat = CoverletConstants.DefaultReportFormat;
+ string defaultFormat = CoverletConstants.DefaultReportFormat;
var doc = new XmlDocument();
- var configElement = doc.CreateElement("Configuration");
- this.CreateCoverletNodes(doc, configElement, CoverletConstants.ReportFormatElementName, formats);
+ XmlElement configElement = doc.CreateElement("Configuration");
+ CreateCoverletNodes(doc, configElement, CoverletConstants.ReportFormatElementName, formats);
CoverletSettings coverletSettings = _coverletSettingsParser.Parse(configElement, testModules);
Assert.Equal(defaultFormat, coverletSettings.ReportFormats[0]);
}
- private void CreateCoverletNodes(XmlDocument doc, XmlElement configElement, string nodeSetting, string nodeValue)
+ private static void CreateCoverletNodes(XmlDocument doc, XmlElement configElement, string nodeSetting, string nodeValue)
{
- var node = doc.CreateNode("element", nodeSetting, string.Empty);
+ XmlNode node = doc.CreateNode("element", nodeSetting, string.Empty);
node.InnerText = nodeValue;
configElement.AppendChild(node);
}
- private void CreateCoverletNullInnerTextNodes(XmlDocument doc, XmlElement configElement, string nodeSetting)
+ private static void CreateCoverletNullInnerTextNodes(XmlDocument doc, XmlElement configElement, string nodeSetting)
{
- var node = doc.CreateNode("element", nodeSetting, string.Empty);
+ XmlNode node = doc.CreateNode("element", nodeSetting, string.Empty);
node.InnerText = null;
configElement.AppendChild(node);
}
diff --git a/test/coverlet.collector.tests/Properties/AssemblyInfo.cs b/test/coverlet.collector.tests/Properties/AssemblyInfo.cs
index b80d4b319..0d10a5c3c 100644
--- a/test/coverlet.collector.tests/Properties/AssemblyInfo.cs
+++ b/test/coverlet.collector.tests/Properties/AssemblyInfo.cs
@@ -1,3 +1,6 @@
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
using System.Reflection;
[assembly: AssemblyKeyFile("coverlet.collector.tests.snk")]
diff --git a/test/coverlet.collector.tests/coverlet.collector.tests.csproj b/test/coverlet.collector.tests/coverlet.collector.tests.csproj
index 822ddda6f..fa828394a 100644
--- a/test/coverlet.collector.tests/coverlet.collector.tests.csproj
+++ b/test/coverlet.collector.tests/coverlet.collector.tests.csproj
@@ -2,7 +2,7 @@
- net5.0
+ net6.0
false
diff --git a/test/coverlet.core.performancetest/.editorconfig b/test/coverlet.core.performancetest/.editorconfig
new file mode 100644
index 000000000..d66ee0772
--- /dev/null
+++ b/test/coverlet.core.performancetest/.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/test/coverlet.core.performancetest/coverlet.core.performancetest.csproj b/test/coverlet.core.performancetest/coverlet.core.performancetest.csproj
index be9cd0e08..41f8d5bd3 100644
--- a/test/coverlet.core.performancetest/coverlet.core.performancetest.csproj
+++ b/test/coverlet.core.performancetest/coverlet.core.performancetest.csproj
@@ -2,13 +2,14 @@
- net5.0
+ net6.0
false
+
diff --git a/test/coverlet.core.tests.samples.netstandard/.editorconfig b/test/coverlet.core.tests.samples.netstandard/.editorconfig
new file mode 100644
index 000000000..d66ee0772
--- /dev/null
+++ b/test/coverlet.core.tests.samples.netstandard/.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/test/coverlet.core.tests/Coverage/CoverageSummaryTests.cs b/test/coverlet.core.tests/Coverage/CoverageSummaryTests.cs
index d66b72573..1a04e2021 100644
--- a/test/coverlet.core.tests/Coverage/CoverageSummaryTests.cs
+++ b/test/coverlet.core.tests/Coverage/CoverageSummaryTests.cs
@@ -1,9 +1,7 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-using Coverlet.Core;
-using Moq;
+using System.Linq;
using Xunit;
namespace Coverlet.Core.Tests
@@ -23,29 +21,29 @@ public CoverageSummaryTests()
private void SetupDataForArithmeticPrecision()
{
- Lines lines = new Lines();
+ var lines = new Lines();
lines.Add(1, 1);
for (int i = 2; i <= 6; i++)
{
lines.Add(i, 0);
}
- Branches branches = new Branches();
+ var branches = new Branches();
branches.Add(new BranchInfo { Line = 1, Hits = 1, Offset = 1, Path = 0, Ordinal = 1 });
for (int i = 2; i <= 6; i++)
{
branches.Add(new BranchInfo { Line = 1, Hits = 0, Offset = 1, Path = 1, Ordinal = (uint)i });
}
- Methods methods = new Methods();
- var methodString = "System.Void Coverlet.Core.Tests.CoverageSummaryTests::TestCalculateSummary()";
+ var methods = new Methods();
+ string methodString = "System.Void Coverlet.Core.Tests.CoverageSummaryTests::TestCalculateSummary()";
methods.Add(methodString, new Method());
methods[methodString].Lines = lines;
methods[methodString].Branches = branches;
- Classes classes = new Classes();
+ var classes = new Classes();
classes.Add("Coverlet.Core.Tests.CoverageSummaryTests", methods);
- Documents documents = new Documents();
+ var documents = new Documents();
documents.Add("doc.cs", classes);
_moduleArithmeticPrecision = new Modules();
@@ -54,23 +52,23 @@ private void SetupDataForArithmeticPrecision()
private void SetupDataSingleModule()
{
- Lines lines = new Lines();
+ var lines = new Lines();
lines.Add(1, 1);
lines.Add(2, 0);
- Branches branches = new Branches();
+ var branches = new Branches();
branches.Add(new BranchInfo { Line = 1, Hits = 1, Offset = 1, Path = 0, Ordinal = 1 });
branches.Add(new BranchInfo { Line = 1, Hits = 1, Offset = 1, Path = 1, Ordinal = 2 });
- Methods methods = new Methods();
- var methodString = "System.Void Coverlet.Core.Tests.CoverageSummaryTests::TestCalculateSummary()";
+ var methods = new Methods();
+ string methodString = "System.Void Coverlet.Core.Tests.CoverageSummaryTests::TestCalculateSummary()";
methods.Add(methodString, new Method());
methods[methodString].Lines = lines;
methods[methodString].Branches = branches;
- Classes classes = new Classes();
+ var classes = new Classes();
classes.Add("Coverlet.Core.Tests.CoverageSummaryTests", methods);
- Documents documents = new Documents();
+ var documents = new Documents();
documents.Add("doc.cs", classes);
_averageCalculationSingleModule = new Modules();
@@ -79,21 +77,21 @@ private void SetupDataSingleModule()
private void SetupDataMultipleModule()
{
- Lines lines = new Lines
+ var lines = new Lines
{
{ 1, 1 }, // covered
{ 2, 0 }, // not covered
{ 3, 0 } // not covered
};
- Branches branches = new Branches
+ var branches = new Branches
{
new BranchInfo { Line = 1, Hits = 1, Offset = 1, Path = 0, Ordinal = 1 }, // covered
new BranchInfo { Line = 1, Hits = 1, Offset = 1, Path = 1, Ordinal = 2 }, // covered
new BranchInfo { Line = 1, Hits = 0, Offset = 1, Path = 1, Ordinal = 2 } // not covered
};
- Methods methods = new Methods();
+ var methods = new Methods();
string[] methodString = {
"System.Void Coverlet.Core.Tests.CoverageSummaryTests::TestCalculateSummary()", // covered
"System.Void Coverlet.Core.Tests.CoverageSummaryTests::TestAditionalCalculateSummary()" // not covered
@@ -108,12 +106,12 @@ private void SetupDataMultipleModule()
{ 1, 0 } // not covered
};
- Classes classes = new Classes
+ var classes = new Classes
{
{ "Coverlet.Core.Tests.CoverageSummaryTests", methods }
};
- Documents documents = new Documents
+ var documents = new Documents
{
{ "doc.cs", classes }
};
@@ -128,7 +126,7 @@ private void SetupDataMultipleModule()
[Fact]
public void TestCalculateLineCoverage_NoModules()
{
- CoverageSummary summary = new CoverageSummary();
+ var summary = new CoverageSummary();
var modules = new Modules();
Assert.Equal(0, summary.CalculateLineCoverage(modules).Percent);
@@ -142,12 +140,12 @@ public void TestCalculateLineCoverage_NoModules()
[Fact]
public void TestCalculateLineCoverage_SingleModule()
{
- CoverageSummary summary = new CoverageSummary();
+ var summary = new CoverageSummary();
- var module = _averageCalculationSingleModule.First();
- var document = module.Value.First();
- var @class = document.Value.First();
- var method = @class.Value.First();
+ System.Collections.Generic.KeyValuePair module = _averageCalculationSingleModule.First();
+ System.Collections.Generic.KeyValuePair document = module.Value.First();
+ System.Collections.Generic.KeyValuePair @class = document.Value.First();
+ System.Collections.Generic.KeyValuePair method = @class.Value.First();
Assert.Equal(50, summary.CalculateLineCoverage(_averageCalculationSingleModule).AverageModulePercent);
Assert.Equal(50, summary.CalculateLineCoverage(module.Value).Percent);
@@ -159,9 +157,9 @@ public void TestCalculateLineCoverage_SingleModule()
[Fact]
public void TestCalculateLineCoverage_MultiModule()
{
- CoverageSummary summary = new CoverageSummary();
- var documentsFirstModule = _averageCalculationMultiModule["module"];
- var documentsSecondModule = _averageCalculationMultiModule["aditionalModule"];
+ var summary = new CoverageSummary();
+ Documents documentsFirstModule = _averageCalculationMultiModule["module"];
+ Documents documentsSecondModule = _averageCalculationMultiModule["aditionalModule"];
Assert.Equal(37.5, summary.CalculateLineCoverage(_averageCalculationMultiModule).AverageModulePercent);
Assert.Equal(50, summary.CalculateLineCoverage(documentsFirstModule.First().Value).Percent);
@@ -174,12 +172,12 @@ public void TestCalculateLineCoverage_MultiModule()
[Fact]
public void TestCalculateBranchCoverage_SingleModule()
{
- CoverageSummary summary = new CoverageSummary();
+ var summary = new CoverageSummary();
- var module = _averageCalculationSingleModule.First();
- var document = module.Value.First();
- var @class = document.Value.First();
- var method = @class.Value.First();
+ System.Collections.Generic.KeyValuePair module = _averageCalculationSingleModule.First();
+ System.Collections.Generic.KeyValuePair document = module.Value.First();
+ System.Collections.Generic.KeyValuePair @class = document.Value.First();
+ System.Collections.Generic.KeyValuePair method = @class.Value.First();
Assert.Equal(100, summary.CalculateBranchCoverage(_averageCalculationSingleModule).AverageModulePercent);
Assert.Equal(100, summary.CalculateBranchCoverage(module.Value).Percent);
@@ -191,9 +189,9 @@ public void TestCalculateBranchCoverage_SingleModule()
[Fact]
public void TestCalculateBranchCoverage_MultiModule()
{
- CoverageSummary summary = new CoverageSummary();
- var documentsFirstModule = _averageCalculationMultiModule["module"];
- var documentsSecondModule = _averageCalculationMultiModule["aditionalModule"];
+ var summary = new CoverageSummary();
+ Documents documentsFirstModule = _averageCalculationMultiModule["module"];
+ Documents documentsSecondModule = _averageCalculationMultiModule["aditionalModule"];
Assert.Equal(83.33, summary.CalculateBranchCoverage(_averageCalculationMultiModule).AverageModulePercent);
Assert.Equal(100, summary.CalculateBranchCoverage(documentsFirstModule.First().Value).Percent);
@@ -203,12 +201,12 @@ public void TestCalculateBranchCoverage_MultiModule()
[Fact]
public void TestCalculateMethodCoverage_SingleModule()
{
- CoverageSummary summary = new CoverageSummary();
+ var summary = new CoverageSummary();
- var module = _averageCalculationSingleModule.First();
- var document = module.Value.First();
- var @class = document.Value.First();
- var method = @class.Value.First();
+ System.Collections.Generic.KeyValuePair module = _averageCalculationSingleModule.First();
+ System.Collections.Generic.KeyValuePair document = module.Value.First();
+ System.Collections.Generic.KeyValuePair @class = document.Value.First();
+ System.Collections.Generic.KeyValuePair method = @class.Value.First();
Assert.Equal(100, summary.CalculateMethodCoverage(_averageCalculationSingleModule).AverageModulePercent);
Assert.Equal(100, summary.CalculateMethodCoverage(module.Value).Percent);
@@ -220,9 +218,9 @@ public void TestCalculateMethodCoverage_SingleModule()
[Fact]
public void TestCalculateMethodCoverage_MultiModule()
{
- CoverageSummary summary = new CoverageSummary();
- var documentsFirstModule = _averageCalculationMultiModule["module"];
- var documentsSecondModule = _averageCalculationMultiModule["aditionalModule"];
+ var summary = new CoverageSummary();
+ Documents documentsFirstModule = _averageCalculationMultiModule["module"];
+ Documents documentsSecondModule = _averageCalculationMultiModule["aditionalModule"];
Assert.Equal(75, summary.CalculateMethodCoverage(_averageCalculationMultiModule).AverageModulePercent);
Assert.Equal(100, summary.CalculateMethodCoverage(documentsFirstModule.First().Value).Percent);
@@ -232,12 +230,12 @@ public void TestCalculateMethodCoverage_MultiModule()
[Fact]
public void TestCalculateLineCoveragePercentage_ArithmeticPrecisionCheck()
{
- CoverageSummary summary = new CoverageSummary();
+ var summary = new CoverageSummary();
- var module = _moduleArithmeticPrecision.First();
- var document = module.Value.First();
- var @class = document.Value.First();
- var method = @class.Value.First();
+ System.Collections.Generic.KeyValuePair module = _moduleArithmeticPrecision.First();
+ System.Collections.Generic.KeyValuePair document = module.Value.First();
+ System.Collections.Generic.KeyValuePair @class = document.Value.First();
+ System.Collections.Generic.KeyValuePair method = @class.Value.First();
Assert.Equal(16.66, summary.CalculateLineCoverage(_moduleArithmeticPrecision).AverageModulePercent);
Assert.Equal(16.66, summary.CalculateLineCoverage(module.Value).Percent);
@@ -249,12 +247,12 @@ public void TestCalculateLineCoveragePercentage_ArithmeticPrecisionCheck()
[Fact]
public void TestCalculateBranchCoveragePercentage_ArithmeticPrecisionCheck()
{
- CoverageSummary summary = new CoverageSummary();
+ var summary = new CoverageSummary();
- var module = _moduleArithmeticPrecision.First();
- var document = module.Value.First();
- var @class = document.Value.First();
- var method = @class.Value.First();
+ System.Collections.Generic.KeyValuePair module = _moduleArithmeticPrecision.First();
+ System.Collections.Generic.KeyValuePair document = module.Value.First();
+ System.Collections.Generic.KeyValuePair @class = document.Value.First();
+ System.Collections.Generic.KeyValuePair method = @class.Value.First();
Assert.Equal(16.66, summary.CalculateBranchCoverage(_moduleArithmeticPrecision).AverageModulePercent);
Assert.Equal(16.66, summary.CalculateBranchCoverage(module.Value).Percent);
@@ -263,4 +261,4 @@ public void TestCalculateBranchCoveragePercentage_ArithmeticPrecisionCheck()
Assert.Equal(16.66, summary.CalculateBranchCoverage(method.Value.Branches).Percent);
}
}
-}
\ No newline at end of file
+}
diff --git a/test/coverlet.core.tests/Coverage/CoverageTests.AsyncAwait.cs b/test/coverlet.core.tests/Coverage/CoverageTests.AsyncAwait.cs
index 40d733b7f..8d0b70ec2 100644
--- a/test/coverlet.core.tests/Coverage/CoverageTests.AsyncAwait.cs
+++ b/test/coverlet.core.tests/Coverage/CoverageTests.AsyncAwait.cs
@@ -1,10 +1,11 @@
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
using System.IO;
-using System.Linq;
+using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
-
using Coverlet.Core.Samples.Tests;
-using Coverlet.Tests.Xunit.Extensions;
using Xunit;
namespace Coverlet.Core.Tests
@@ -112,7 +113,8 @@ public void AsyncAwait_Issue_669_2()
((ValueTask)instance.SendRequest()).ConfigureAwait(false).GetAwaiter().GetResult();
return Task.CompletedTask;
},
- persistPrepareResultToFile: pathSerialize[0]);
+ persistPrepareResultToFile: pathSerialize[0],
+ assemblyLocation: Assembly.GetExecutingAssembly().Location);
return 0;
}, new string[] { path });
@@ -146,7 +148,7 @@ public void AsyncAwait_Issue_1177()
return 0;
}, new string[] { path });
- var document = TestInstrumentationHelper.GetCoverageResult(path).Document("Instrumentation.AsyncAwait.cs");
+ Core.Instrumentation.Document document = TestInstrumentationHelper.GetCoverageResult(path).Document("Instrumentation.AsyncAwait.cs");
document.AssertLinesCovered(BuildConfiguration.Debug, (133, 1), (134, 1), (135, 1), (136, 1), (137, 1));
Assert.DoesNotContain(document.Branches, x => x.Key.Line == 134);
}
@@ -174,7 +176,7 @@ public void AsyncAwait_Issue_1233()
return 0;
}, new string[] { path });
- var document = TestInstrumentationHelper.GetCoverageResult(path).Document("Instrumentation.AsyncAwait.cs");
+ Core.Instrumentation.Document document = TestInstrumentationHelper.GetCoverageResult(path).Document("Instrumentation.AsyncAwait.cs");
document.AssertLinesCovered(BuildConfiguration.Debug, (150, 1));
Assert.DoesNotContain(document.Branches, x => x.Key.Line == 150);
}
@@ -203,7 +205,7 @@ public void AsyncAwait_Issue_1275()
return 0;
}, new string[] { path });
- var document = TestInstrumentationHelper.GetCoverageResult(path).Document("Instrumentation.AsyncAwait.cs");
+ Core.Instrumentation.Document document = TestInstrumentationHelper.GetCoverageResult(path).Document("Instrumentation.AsyncAwait.cs");
document.AssertLinesCoveredFromTo(BuildConfiguration.Debug, 170, 176);
document.AssertBranchesCovered(BuildConfiguration.Debug, (171, 0, 1), (171, 1, 1));
Assert.DoesNotContain(document.Branches, x => x.Key.Line == 176);
@@ -214,4 +216,4 @@ public void AsyncAwait_Issue_1275()
}
}
}
-}
\ No newline at end of file
+}
diff --git a/test/coverlet.core.tests/Coverage/CoverageTests.AsyncAwaitValueTask.cs b/test/coverlet.core.tests/Coverage/CoverageTests.AsyncAwaitValueTask.cs
index 0438dc66d..dc7ebddfc 100644
--- a/test/coverlet.core.tests/Coverage/CoverageTests.AsyncAwaitValueTask.cs
+++ b/test/coverlet.core.tests/Coverage/CoverageTests.AsyncAwaitValueTask.cs
@@ -1,8 +1,9 @@
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
using System.IO;
using System.Threading.Tasks;
-
using Coverlet.Core.Samples.Tests;
-using Coverlet.Tests.Xunit.Extensions;
using Xunit;
namespace Coverlet.Core.Tests
diff --git a/test/coverlet.core.tests/Coverage/CoverageTests.AsyncForeach.cs b/test/coverlet.core.tests/Coverage/CoverageTests.AsyncForeach.cs
index abeee16cd..77450d19b 100644
--- a/test/coverlet.core.tests/Coverage/CoverageTests.AsyncForeach.cs
+++ b/test/coverlet.core.tests/Coverage/CoverageTests.AsyncForeach.cs
@@ -1,10 +1,10 @@
-using System.Collections.Generic;
+// Copyright (c) Toni Solarin-Sodara
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
using System.IO;
using System.Linq;
using System.Threading.Tasks;
-
using Coverlet.Core.Samples.Tests;
-using Coverlet.Tests.Xunit.Extensions;
using Xunit;
namespace Coverlet.Core.Tests
@@ -24,6 +24,7 @@ public void AsyncForeach()
int res = ((ValueTask)instance.SumWithATwist(AsyncEnumerable.Range(1, 5))).GetAwaiter().GetResult();
res += ((ValueTask)instance.Sum(AsyncEnumerable.Range(1, 3))).GetAwaiter().GetResult();
res += ((ValueTask)instance.SumEmpty()).GetAwaiter().GetResult();
+ ((ValueTask)instance.GenericAsyncForeach