diff --git a/Directory.Packages.props b/Directory.Packages.props index 015e0ee98..6c94f40a1 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -44,7 +44,6 @@ - diff --git a/Documentation/Changelog.md b/Documentation/Changelog.md index 22edce12a..9cd1203ba 100644 --- a/Documentation/Changelog.md +++ b/Documentation/Changelog.md @@ -6,6 +6,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Fixed +- Threshold-stat triggers error [#1634](https://github.com/coverlet-coverage/coverlet/issues/1634) +- Fixed coverlet collector 6.0.1 requires dotnet sdk 8 [#1625](https://github.com/coverlet-coverage/coverlet/issues/1625) +- Type initializer errors after updating from 6.0.0 to 6.0.1 [#1629](https://github.com/coverlet-coverage/coverlet/issues/1629) +- Exception when multiple exclude-by-attribute filters specified [#1624](https://github.com/coverlet-coverage/coverlet/issues/1624) + +### Improvements +- More concise options to specify multiple parameters in coverlet.console [#1624](https://github.com/coverlet-coverage/coverlet/issues/1624) + +## Release date 2024-02-19 +### Packages +coverlet.msbuild 6.0.1 +coverlet.console 6.0.1 +coverlet.collector 6.0.1 + ### Fixed - Uncovered lines in .NET 8 for inheriting records [#1555](https://github.com/coverlet-coverage/coverlet/issues/1555) - Fix record constructors not covered when SkipAutoProps is true [#1561](https://github.com/coverlet-coverage/coverlet/issues/1561) diff --git a/Documentation/GlobalTool.md b/Documentation/GlobalTool.md index 4b3d2ab33..602a4aea8 100644 --- a/Documentation/GlobalTool.md +++ b/Documentation/GlobalTool.md @@ -37,17 +37,23 @@ Options: --use-source-link Specifies whether to use SourceLink URIs in place of file system paths. --does-not-return-attribute Attributes that mark methods that do not return. --exclude-assemblies-without-sources Specifies behaviour of heuristic to ignore assemblies with missing source documents. - --use-mapping-file Specifies the path to a SourceRootsMappings file. + --source-mapping-file Specifies the path to a SourceRootsMappings file. --version Show version information -?, -h, --help Show help and usage information ``` -NB. For a [multiple value] options you have to specify values multiple times i.e. +NB. For [multiple value] options you can either specify values multiple times i.e. ```shell --exclude-by-attribute 'Obsolete' --exclude-by-attribute 'GeneratedCode' --exclude-by-attribute 'CompilerGenerated' ``` +or pass the multiple values as space separated sequence, i.e. + +```shell +--exclude-by-attribute "Obsolete" "GeneratedCode" "CompilerGenerated" +``` + For `--merge-with` [check the sample](Examples.md). ## Code Coverage diff --git a/Documentation/ReleasePlan.md b/Documentation/ReleasePlan.md index 742b7d34b..22ff83dcd 100644 --- a/Documentation/ReleasePlan.md +++ b/Documentation/ReleasePlan.md @@ -19,38 +19,6 @@ We release 3 components as NuGet packages: **coverlet.console.nupkg** **coverlet.collector.nupkg** -### Current versions - -| Package | Version | -|:----------------------|:--------| -|**coverlet.msbuild** | 6.0.0 | -|**coverlet.console** | 6.0.0 | -|**coverlet.collector** | 6.0.0 | - -| Release Date | coverlet.msbuild | coverlet.console | coverlet.collector| commit hash | notes | -| :-----------------|:-----------------|:------------------|:------------------|:-----------------------------------------|:-------------------------------| -| 05 May 2023 | 6.0.0 | 6.0.0 | 6.0.0 | 3ad4fa1d5cd7ffe206c0cb9dc805ee6ca5a7b550 | Version aligned with github one| -| 29 Oct 2022 | 3.2.0 | 3.2.0 | 3.2.0 | e2c9d84a84a9d2d240ac15feb70f9198c6f8e173 | | -| 06 Feb 2022 | 3.1.2 | 3.1.2 | 3.1.2 | e335b1a8025e49e2f2de6b40ef12ec9d3ed11ceb | Fix CoreLib coverage issues | -| 30 Jan 2022 | 3.1.1 | 3.1.1 | 3.1.1 | e4278c06faba63122a870df15a1a1b934f6bc81d | | -| 19 July 2021 | 3.1.0 | 3.1.0 | 3.1.0 | 5a0ecc1e92fd754e2439dc3e4c828ff7386aa1a7 | Support for determistic build | -| 21 February 2021 | 3.0.3 | 3.0.3 | 3.0.3 | adfabfd58de0aabe263e7d2080324e0b8541071e | Fix regressions | -| 24 January 2021 | 3.0.2 | 3.0.2 | 3.0.2 | ed918515492193fd154b60270d440c40fa30fee9 | Fix regressions | -| 16 January 2021 | 3.0.1 | 3.0.1 | 3.0.1 | 1b45fd89245369ae94407e7a77bdfee112042486 | Fix severe coverage regression | -| 09 January 2021 | 3.0.0 | 3.0.0 | 3.0.0 | 1e77f9d2183a320e8991bfc296460e793301931f | Align versions numbers | -| 30 May 2020 | 2.9.0 | 1.7.2 | 1.3.0 | 83a38d45b3f9c231d705bfed849efbf41b3aaa86 | deterministic build support | -| 04 April 2020 | 2.8.1 | 1.7.1 | 1.2.1 | 3f81828821d07d756e02a4105b2533cedf0b543c | | -| 03 January 2019 | 2.8.0 | 1.7.0 | 1.2.0 | 72a688f1c47fa92059540d5fbb1c4b0b4bf0dc8c | | -| 23 September 2019 | 2.7.0 | 1.6.0 | 1.1.0 | 4ca01eb239038808739699470a61fad675af6c79 | | -| 01 July 2019 | 2.6.3 | 1.5.3 | 1.0.1 | e1593359497fdfe6befbb86304b8f4e09a656d14 | | -| 06 June 2019 | 2.6.2 | 1.5.2 | 1.0.0 | 3e7eac9df094c22335711a298d359890aed582e8 | first collector release | - -To get the list of commits between two version use git command - -```bash -git log --oneline hashbefore currenthash -``` - ## How to manually compare latest release with nightly build Before creating a new release it makes sense to test the new release against a benchmark repository. This can help to determine bugs that haven't been found diff --git a/src/coverlet.collector/coverlet.collector.csproj b/src/coverlet.collector/coverlet.collector.csproj index fc6145d80..ddb8522ba 100644 --- a/src/coverlet.collector/coverlet.collector.csproj +++ b/src/coverlet.collector/coverlet.collector.csproj @@ -1,6 +1,6 @@ - netstandard2.0;net6.0 + netstandard2.0 coverlet.collector true true diff --git a/src/coverlet.console/Program.cs b/src/coverlet.console/Program.cs index 7bf4dad77..864df117f 100644 --- a/src/coverlet.console/Program.cs +++ b/src/coverlet.console/Program.cs @@ -32,21 +32,21 @@ static int Main(string[] args) var targs = new Option(new[] { "--targetargs", "-a" }, "Arguments to be passed to the test runner.") { Arity = ArgumentArity.ZeroOrOne }; var output = new Option(new[] { "--output", "-o" }, "Output of the generated coverage report") { Arity = ArgumentArity.ZeroOrOne }; var verbosity = new Option(new[] { "--verbosity", "-v" }, () => LogLevel.Normal, "Sets the verbosity level of the command. Allowed values are quiet, minimal, normal, detailed.") { Arity = ArgumentArity.ZeroOrOne }; - var formats = new Option(new[] { "--format", "-f" }, () => new[] { "json" }, "Format of the generated coverage report.") { Arity = ArgumentArity.ZeroOrMore }; + var formats = new Option(new[] { "--format", "-f" }, () => new[] { "json" }, "Format of the generated coverage report.") { Arity = ArgumentArity.ZeroOrMore, AllowMultipleArgumentsPerToken = true }; var threshold = new Option("--threshold", "Exits with error if the coverage % is below value.") { Arity = ArgumentArity.ZeroOrOne }; var thresholdTypes = new Option>("--threshold-type", () => new List(new string[] { "line", "branch", "method" }), "Coverage type to apply the threshold to.").FromAmong("line", "branch", "method"); - var thresholdStat = new Option("--threshold-stat", () => ThresholdStatistic.Minimum, "Coverage statistic used to enforce the threshold value.") { Arity = ArgumentArity.ZeroOrMore }; - var excludeFilters = new Option("--exclude", "Filter expressions to exclude specific modules and types.") { Arity = ArgumentArity.ZeroOrMore }; - var includeFilters = new Option("--include", "Filter expressions to include only specific modules and types.") { Arity = ArgumentArity.ZeroOrMore }; - var excludedSourceFiles = new Option("--exclude-by-file", "Glob patterns specifying source files to exclude.") { Arity = ArgumentArity.ZeroOrMore }; - var includeDirectories = new Option("--include-directory", "Include directories containing additional assemblies to be instrumented.") { Arity = ArgumentArity.ZeroOrMore }; - var excludeAttributes = new Option("--exclude-by-attribute", "Attributes to exclude from code coverage.") { Arity = ArgumentArity.ZeroOrOne }; + var thresholdStat = new Option("--threshold-stat", () => ThresholdStatistic.Minimum, "Coverage statistic used to enforce the threshold value.") { Arity = ArgumentArity.ZeroOrOne }; + var excludeFilters = new Option("--exclude", "Filter expressions to exclude specific modules and types.") { Arity = ArgumentArity.ZeroOrMore, AllowMultipleArgumentsPerToken = true }; + var includeFilters = new Option("--include", "Filter expressions to include only specific modules and types.") { Arity = ArgumentArity.ZeroOrMore, AllowMultipleArgumentsPerToken = true }; + var excludedSourceFiles = new Option("--exclude-by-file", "Glob patterns specifying source files to exclude.") { Arity = ArgumentArity.ZeroOrMore, AllowMultipleArgumentsPerToken = true }; + var includeDirectories = new Option("--include-directory", "Include directories containing additional assemblies to be instrumented.") { Arity = ArgumentArity.ZeroOrMore, AllowMultipleArgumentsPerToken = true }; + var excludeAttributes = new Option("--exclude-by-attribute", "Attributes to exclude from code coverage.") { Arity = ArgumentArity.ZeroOrMore, AllowMultipleArgumentsPerToken = true }; var includeTestAssembly = new Option("--include-test-assembly", "Specifies whether to report code coverage of the test assembly.") { Arity = ArgumentArity.Zero }; var singleHit = new Option("--single-hit", "Specifies whether to limit code coverage hit reporting to a single hit for each location") { Arity = ArgumentArity.Zero }; var skipAutoProp = new Option("--skipautoprops", "Neither track nor record auto-implemented properties.") { Arity = ArgumentArity.Zero }; var mergeWith = new Option("--merge-with", "Path to existing coverage result to merge.") { Arity = ArgumentArity.ZeroOrOne }; var useSourceLink = new Option("--use-source-link", "Specifies whether to use SourceLink URIs in place of file system paths.") { Arity = ArgumentArity.Zero }; - var doesNotReturnAttributes = new Option("--does-not-return-attribute", "Attributes that mark methods that do not return") { Arity = ArgumentArity.ZeroOrMore }; + var doesNotReturnAttributes = new Option("--does-not-return-attribute", "Attributes that mark methods that do not return") { Arity = ArgumentArity.ZeroOrMore, AllowMultipleArgumentsPerToken = true }; var excludeAssembliesWithoutSources = new Option("--exclude-assemblies-without-sources", "Specifies behaviour of heuristic to ignore assemblies with missing source documents.") { Arity = ArgumentArity.ZeroOrOne }; var sourceMappingFile = new Option("--source-mapping-file", "Specifies the path to a SourceRootsMappings file.") { Arity = ArgumentArity.ZeroOrOne }; diff --git a/src/coverlet.core/Coverage.cs b/src/coverlet.core/Coverage.cs index 3e0f46216..e057a09bb 100644 --- a/src/coverlet.core/Coverage.cs +++ b/src/coverlet.core/Coverage.cs @@ -6,8 +6,8 @@ using System.IO; using System.Linq; using System.Runtime.Serialization; -using System.Text.Json; -using System.Text.Json.Nodes; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using Coverlet.Core.Abstractions; using Coverlet.Core.Helpers; using Coverlet.Core.Instrumentation; @@ -60,14 +60,6 @@ internal class Coverage public string Identifier { get; } - readonly JsonSerializerOptions _options = new() - { - PropertyNameCaseInsensitive = true, - DictionaryKeyPolicy = JsonNamingPolicy.CamelCase, - IncludeFields = true, - WriteIndented = true - }; - public Coverage(string moduleOrDirectory, CoverageParameters parameters, ILogger logger, @@ -325,7 +317,7 @@ public CoverageResult GetCoverageResult() { _logger.LogInformation($"MergeWith: '{_parameters.MergeWith}'."); string json = _fileSystem.ReadAllText(_parameters.MergeWith); - coverageResult.Merge(JsonSerializer.Deserialize(json, _options)); + coverageResult.Merge(JsonConvert.DeserializeObject(json)); } else { _logger.LogInformation($"MergeWith: file '{_parameters.MergeWith}' does not exist."); @@ -387,8 +379,8 @@ private void CalculateCoverage() var documents = result.Documents.Values.ToList(); if (_parameters.UseSourceLink && result.SourceLink != null) { - JsonNode jObject = JsonNode.Parse(result.SourceLink)["documents"]; - Dictionary sourceLinkDocuments = JsonSerializer.Deserialize>(jObject.ToString()); + JToken jObject = JObject.Parse(result.SourceLink)["documents"]; + Dictionary sourceLinkDocuments = JsonConvert.DeserializeObject>(jObject.ToString()); foreach (Document document in documents) { document.Path = GetSourceLinkUrl(sourceLinkDocuments, document.Path); diff --git a/src/coverlet.core/CoverageResult.cs b/src/coverlet.core/CoverageResult.cs index 3cc9ce084..00a64568c 100644 --- a/src/coverlet.core/CoverageResult.cs +++ b/src/coverlet.core/CoverageResult.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; -using System.Text.Json.Serialization; using Coverlet.Core.Enums; using Coverlet.Core.Instrumentation; @@ -23,11 +22,10 @@ internal class Branches : List { } internal class Method { - [JsonConstructor] internal Method() { - Lines = []; - Branches = []; + Lines = new Lines(); + Branches = new Branches(); } public Lines Lines; diff --git a/src/coverlet.core/Helpers/InstrumentationHelper.cs b/src/coverlet.core/Helpers/InstrumentationHelper.cs index f0af9b31c..f6a35f73a 100644 --- a/src/coverlet.core/Helpers/InstrumentationHelper.cs +++ b/src/coverlet.core/Helpers/InstrumentationHelper.cs @@ -24,6 +24,8 @@ internal class InstrumentationHelper : IInstrumentationHelper private readonly IFileSystem _fileSystem; private readonly ISourceRootTranslator _sourceRootTranslator; private ILogger _logger; + private static readonly RegexOptions s_regexOptions = + RegexOptions.Multiline | RegexOptions.Compiled | RegexOptions.IgnoreCase; public InstrumentationHelper(IProcessExitHandler processExitHandler, IRetryHelper retryHelper, IFileSystem fileSystem, ILogger logger, ISourceRootTranslator sourceRootTranslator) { @@ -331,7 +333,7 @@ public bool IsValidFilterExpression(string filter) if (filter.EndsWith("]")) return false; - if (new Regex(@"[^\w*]").IsMatch(filter.Replace(".", "").Replace("?", "").Replace("[", "").Replace("]", ""))) + if (new Regex(@"[^\w*]", s_regexOptions, TimeSpan.FromSeconds(10)).IsMatch(filter.Replace(".", "").Replace("?", "").Replace("[", "").Replace("]", ""))) return false; return true; @@ -358,7 +360,7 @@ public bool IsModuleExcluded(string module, string[] excludeFilters) #pragma warning restore IDE0057 // Use range operator modulePattern = WildcardToRegex(modulePattern); - var regex = new Regex(modulePattern); + var regex = new Regex(modulePattern, s_regexOptions, TimeSpan.FromSeconds(10)); if (regex.IsMatch(module)) return true; @@ -387,7 +389,7 @@ public bool IsModuleIncluded(string module, string[] includeFilters) modulePattern = WildcardToRegex(modulePattern); - var regex = new Regex(modulePattern); + var regex = new Regex(modulePattern, s_regexOptions, TimeSpan.FromSeconds(10)); if (regex.IsMatch(module)) return true; @@ -421,7 +423,7 @@ public bool IsTypeIncluded(string module, string type, string[] includeFilters) } public bool IsLocalMethod(string method) - => new Regex(WildcardToRegex("<*>*__*|*")).IsMatch(method); + => new Regex(WildcardToRegex("<*>*__*|*"), s_regexOptions, TimeSpan.FromSeconds(10)).IsMatch(method); public void SetLogger(ILogger logger) { @@ -443,7 +445,7 @@ private static bool IsTypeFilterMatch(string module, string type, string[] filte typePattern = WildcardToRegex(typePattern); modulePattern = WildcardToRegex(modulePattern); - if (new Regex(typePattern).IsMatch(type) && new Regex(modulePattern).IsMatch(module)) + if (new Regex(typePattern, s_regexOptions, TimeSpan.FromSeconds(10)).IsMatch(type) && new Regex(modulePattern, s_regexOptions, TimeSpan.FromSeconds(10)).IsMatch(module)) return true; } diff --git a/src/coverlet.core/Instrumentation/CecilAssemblyResolver.cs b/src/coverlet.core/Instrumentation/CecilAssemblyResolver.cs index c8e0a4109..49790660e 100644 --- a/src/coverlet.core/Instrumentation/CecilAssemblyResolver.cs +++ b/src/coverlet.core/Instrumentation/CecilAssemblyResolver.cs @@ -5,12 +5,12 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text.Json; using Coverlet.Core.Abstractions; using Coverlet.Core.Exceptions; using Microsoft.Extensions.DependencyModel; using Microsoft.Extensions.DependencyModel.Resolution; using Mono.Cecil; +using Newtonsoft.Json.Linq; using NuGet.Versioning; namespace Coverlet.Core.Instrumentation @@ -298,25 +298,24 @@ public RuntimeConfigurationReader(string runtimeConfigFile) { string jsonString = File.ReadAllText(_runtimeConfigFile); - var documentOptions = new JsonDocumentOptions + var jsonLoadSettings = new JsonLoadSettings() { - CommentHandling = JsonCommentHandling.Skip + CommentHandling = CommentHandling.Ignore }; - using var configuration = JsonDocument.Parse(jsonString, documentOptions); + var configuration = JObject.Parse(jsonString, jsonLoadSettings); - JsonElement rootElement = configuration.RootElement; + JToken rootElement = configuration.Root; + JToken runtimeOptionsElement = rootElement["runtimeOptions"]; - JsonElement runtimeOptionsElement = rootElement.GetProperty("runtimeOptions"); - - if (runtimeOptionsElement.TryGetProperty("framework", out JsonElement frameworkElement)) + if (runtimeOptionsElement?["framework"] != null) { - return new[] { (frameworkElement.GetProperty("name").GetString(), frameworkElement.GetProperty("version").GetString()) }; + return new[] { (runtimeOptionsElement["framework"]["name"]?.Value(), runtimeOptionsElement["framework"]["version"]?.Value()) }; } - if (runtimeOptionsElement.TryGetProperty("frameworks", out JsonElement frameworksElement)) + if (runtimeOptionsElement?["frameworks"] != null) { - return frameworksElement.EnumerateArray().Select(x => (x.GetProperty("name").GetString(), x.GetProperty("version").GetString())).ToList(); + return runtimeOptionsElement["frameworks"].Select(x => (x["name"]?.Value(), x["version"]?.Value())).ToList(); } throw new InvalidOperationException($"Unable to read runtime configuration from {_runtimeConfigFile}."); diff --git a/src/coverlet.core/Reporters/JsonReporter.cs b/src/coverlet.core/Reporters/JsonReporter.cs index 62be3373a..e684e8c8a 100644 --- a/src/coverlet.core/Reporters/JsonReporter.cs +++ b/src/coverlet.core/Reporters/JsonReporter.cs @@ -1,9 +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.Text.Encodings.Web; -using System.Text.Json; using Coverlet.Core.Abstractions; +using Newtonsoft.Json; namespace Coverlet.Core.Reporters { @@ -17,13 +16,7 @@ internal class JsonReporter : IReporter public string Report(CoverageResult result, ISourceRootTranslator _) { - var options = new JsonSerializerOptions - { - Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - IncludeFields = true, - WriteIndented = true, - }; - return JsonSerializer.Serialize(result.Modules, options); + return JsonConvert.SerializeObject(result.Modules, Formatting.Indented); } } } diff --git a/src/coverlet.core/coverlet.core.csproj b/src/coverlet.core/coverlet.core.csproj index 0492a403b..5e78d106a 100644 --- a/src/coverlet.core/coverlet.core.csproj +++ b/src/coverlet.core/coverlet.core.csproj @@ -2,23 +2,23 @@ Library - netstandard2.0;net6.0 + netstandard2.0 false - - - - + + + + - + - - + + diff --git a/src/coverlet.msbuild.tasks/buildMultiTargeting/coverlet.msbuild.props b/src/coverlet.msbuild.tasks/buildMultiTargeting/coverlet.msbuild.props deleted file mode 100644 index ba0c4ed38..000000000 --- a/src/coverlet.msbuild.tasks/buildMultiTargeting/coverlet.msbuild.props +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/src/coverlet.msbuild.tasks/coverlet.msbuild.props b/src/coverlet.msbuild.tasks/coverlet.msbuild.props index 6642a929d..9403e7702 100644 --- a/src/coverlet.msbuild.tasks/coverlet.msbuild.props +++ b/src/coverlet.msbuild.tasks/coverlet.msbuild.props @@ -19,7 +19,6 @@ - $(MSBuildThisFileDirectory)..\tasks\net6.0\ - $(MSBuildThisFileDirectory)..\tasks\netstandard2.0\ + $(MSBuildThisFileDirectory)..\tasks\netstandard2.0\ diff --git a/src/coverlet.msbuild.tasks/coverlet.msbuild.tasks.csproj b/src/coverlet.msbuild.tasks/coverlet.msbuild.tasks.csproj index 07c991a4d..17332caa6 100644 --- a/src/coverlet.msbuild.tasks/coverlet.msbuild.tasks.csproj +++ b/src/coverlet.msbuild.tasks/coverlet.msbuild.tasks.csproj @@ -2,7 +2,7 @@ Library - netstandard2.0;net6.0 + netstandard2.0 coverlet.msbuild.tasks true $(TargetsForTfmSpecificContentInPackage);PackBuildOutputs @@ -55,9 +55,6 @@ Always - - Always - Always diff --git a/test/Directory.Build.targets b/test/Directory.Build.targets index 30e3bcaad..b181fcf29 100644 --- a/test/Directory.Build.targets +++ b/test/Directory.Build.targets @@ -14,8 +14,7 @@ This is required when the coverlet.msbuild imports are made in their src directory (so that msbuild eval works even before they are built) so that they can still find the tooling that will be built by the build. --> - $(RepoRoot)artifacts\bin\coverlet.msbuild.tasks\$(Configuration.ToLowerInvariant())_net6.0\ - $(RepoRoot)artifacts\bin\coverlet.msbuild.tasks\$(Configuration.ToLowerInvariant())_netstandard2.0\ + $(RepoRoot)artifacts\bin\coverlet.msbuild.tasks\$(Configuration.ToLowerInvariant())_netstandard2.0\ diff --git a/test/coverlet.core.tests/Coverage/CoverageTests.cs b/test/coverlet.core.tests/Coverage/CoverageTests.cs index 25761f83d..701df8f30 100644 --- a/test/coverlet.core.tests/Coverage/CoverageTests.cs +++ b/test/coverlet.core.tests/Coverage/CoverageTests.cs @@ -2,17 +2,16 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; +using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text.Encodings.Web; -using System.Text.Json; -using System.Text.Json.Serialization; using Coverlet.Core.Abstractions; using Coverlet.Core.Helpers; using Coverlet.Core.Instrumentation; using Coverlet.Core.Symbols; using Moq; +using Newtonsoft.Json; using Xunit; namespace Coverlet.Core.Tests @@ -20,16 +19,6 @@ namespace Coverlet.Core.Tests public partial class CoverageTests { private readonly Mock _mockLogger = new(); - readonly JsonSerializerOptions _options = new() - { - Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - IncludeFields = true, - WriteIndented = true, - Converters = - { - new BranchDictionaryConverterFactory() - } - }; [Fact] public void TestCoverage() @@ -102,7 +91,7 @@ public void TestCoverageWithTestAssembly() new SourceRootTranslator(module, _mockLogger.Object, new FileSystem(), new AssemblyAdapter()), new CecilSymbolHelper()); coverage.PrepareModules(); - string result = JsonSerializer.Serialize(coverage.GetCoverageResult(), _options); + string result = JsonConvert.SerializeObject(coverage.GetCoverageResult(), Formatting.Indented, new BranchDictionaryConverter()); Assert.Contains("coverlet.core.tests.dll", result); @@ -141,7 +130,7 @@ public void TestCoverageMergeWithParameter() var coverage = new Coverage(Path.Combine(directory.FullName, Path.GetFileName(module)), parameters, _mockLogger.Object, instrumentationHelper, new FileSystem(), new SourceRootTranslator(_mockLogger.Object, new FileSystem()), new CecilSymbolHelper()); coverage.PrepareModules(); - string result = JsonSerializer.Serialize(coverage.GetCoverageResult(), _options); + string result = JsonConvert.SerializeObject(coverage.GetCoverageResult(), Formatting.Indented, new BranchDictionaryConverter()); Assert.Contains("DeepThought.cs", result); @@ -182,7 +171,7 @@ public void TestCoverageMergeWithWrongParameter() var coverage = new Coverage(Path.Combine(directory.FullName, Path.GetFileName(module)), parameters, _mockLogger.Object, instrumentationHelper, new FileSystem(), new SourceRootTranslator(_mockLogger.Object, new FileSystem()), new CecilSymbolHelper()); coverage.PrepareModules(); - string result = JsonSerializer.Serialize(coverage.GetCoverageResult(), _options); + JsonConvert.SerializeObject(coverage.GetCoverageResult()); _mockLogger.Verify(l => l.LogInformation(It.Is(v => v.Equals("MergeWith: file 'FileDoesNotExist.json' does not exist.")), It.IsAny()), Times.Once); @@ -190,43 +179,36 @@ public void TestCoverageMergeWithWrongParameter() } } } -public class BranchDictionaryConverterFactory : JsonConverterFactory -{ - public override bool CanConvert(Type typeToConvert) - { - return typeof(Dictionary).IsAssignableFrom(typeToConvert); - } - public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) +public class BranchDictionaryConverter: JsonConverter +{ + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { - Type[] genericArgs = typeToConvert.GetGenericArguments(); - Type keyType = genericArgs[0]; - Type valueType = genericArgs[1]; + Type type = value.GetType(); + var keys = (IEnumerable)type.GetProperty("Keys")?.GetValue(value, null); + var values = (IEnumerable)type.GetProperty("Values")?.GetValue(value, null); + IEnumerator valueEnumerator = values.GetEnumerator(); - JsonConverter converter = (JsonConverter)Activator.CreateInstance( - typeof(BranchDictionaryConverter<,>).MakeGenericType(new Type[] { keyType, valueType })); + writer.WriteStartArray(); + foreach (object key in keys) + { + valueEnumerator.MoveNext(); - return converter; + writer.WriteStartArray(); + serializer.Serialize(writer, key); + serializer.Serialize(writer, valueEnumerator.Current); + writer.WriteEndArray(); + } + writer.WriteEndArray(); } -} -public class BranchDictionaryConverter : JsonConverter> -{ - public override Dictionary Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer) { throw new NotImplementedException(); } - public override void Write(Utf8JsonWriter writer, Dictionary value, JsonSerializerOptions options) + public override bool CanConvert(Type objectType) { - writer.WriteStartObject(); - - foreach (KeyValuePair pair in value) - { - writer.WritePropertyName(pair.Key.ToString()); - JsonSerializer.Serialize(writer, pair.Value, options); - } - - writer.WriteEndObject(); + return typeof(Dictionary).IsAssignableFrom(objectType); } } diff --git a/test/coverlet.integration.determisticbuild/coverlet.integration.determisticbuild.csproj b/test/coverlet.integration.determisticbuild/coverlet.integration.determisticbuild.csproj index 584a6fef6..af3dd09ac 100644 --- a/test/coverlet.integration.determisticbuild/coverlet.integration.determisticbuild.csproj +++ b/test/coverlet.integration.determisticbuild/coverlet.integration.determisticbuild.csproj @@ -12,7 +12,6 @@ https://api.nuget.org/v3/index.json; $(RepoRoot)artifacts/package/$(Configuration.ToLowerInvariant()) - true diff --git a/version.json b/version.json index 55b26a30b..934880ede 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/AArnott/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", - "version": "6.0.2-preview.{height}", + "version": "6.0.2", "publicReleaseRefSpec": [ "^refs/heads/master$" ],