From 89d63792f4984c9ede7635a42cdb8bf0720c12d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Fri, 15 Oct 2021 14:01:54 +0200 Subject: [PATCH] fix: Improve errors messages when configuration contains invalid config keys (#1707) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead, use System.Text.Json everywhere. ### JSON Configuration File Since there's [no equivalent][1] to `MissingMemberHandling` with `System.Text.Json`, the error messages in case of erroneous keys in the JSON configuration file have been improved. For example, if you have a typo in the `enabled` key you would get this error: > The allowed keys for the "stryker-config.since" object are { "enabled", "ignore-changes-in", "target" } but "enable" was found in the config file at "[…]/tests/stryker-config.json" Those better error messages also required to have a `JsonPropertyName` attribute on _all_ the properties. This is probably a good thing anyway since it makes the code more _greppable_. ### Newtonsoft.Json transitive dependency Unfortunately, `Newtonsoft.Json` is still there transitively (through Buildalyzer/3.2.2 → Microsoft.Extensions.DependencyModel/2.1.0 → Newtonsoft.Json/9.0.1) but there's nothing we can do about it for now, see https://github.com/microsoft/vstest/issues/2488#issuecomment-932036883 for a full explanation. [1]: https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to#missingmemberhandling Co-authored-by: Rouke Broersma --- .../Stryker.CLI.UnitTest/ConfigFileTests.cs | 4 +- .../Stryker.CLI.UnitTest/StrykerCLITests.cs | 12 +- .../Stryker.CLI.UnitTest/packages.lock.json | 58 +++++- src/Stryker.CLI/Stryker.CLI/FileBasedInput.cs | 170 ++++++++++++------ .../Stryker.CLI/JsonConfigHandler.cs | 55 +++++- .../Stryker.CLI/packages.lock.json | 116 +++++++++++- .../Utils/BaselineMutantHelperTests.cs | 67 +++---- .../Reporters/JsonReporter/MockJsonReport.cs | 6 +- .../MockJsonReportFileComponent.cs | 8 +- .../Reporters/JsonReporterTests.cs | 5 +- .../Stryker.Core.UnitTest/packages.lock.json | 58 +++++- .../AzureFileShareBaselineProvider.cs | 5 +- .../Providers/DiskBaselineProvider.cs | 23 +-- .../Stryker.Core/Clients/DashboardClient.cs | 5 +- .../JsonReporter/JsonMutantLocation.cs | 12 +- .../Reporters/JsonReporter/JsonReport.cs | 31 ---- .../JsonReporter/JsonReportFileComponent.cs | 13 +- .../JsonReporter/JsonReportSerialization.cs | 48 +++++ .../Reporters/JsonReporter/JsonReporter.cs | 8 +- .../Stryker.Core/Stryker.Core.csproj | 1 - .../Stryker.Core/packages.lock.json | 122 ++++++++++++- 21 files changed, 626 insertions(+), 201 deletions(-) create mode 100644 src/Stryker.Core/Stryker.Core/Reporters/JsonReporter/JsonReportSerialization.cs diff --git a/src/Stryker.CLI/Stryker.CLI.UnitTest/ConfigFileTests.cs b/src/Stryker.CLI/Stryker.CLI.UnitTest/ConfigFileTests.cs index 2e04932b7f..637cb0942f 100644 --- a/src/Stryker.CLI/Stryker.CLI.UnitTest/ConfigFileTests.cs +++ b/src/Stryker.CLI/Stryker.CLI.UnitTest/ConfigFileTests.cs @@ -18,7 +18,7 @@ public void WithNoArgumentsAndNoConfigFile_ShouldStartStrykerWithConfigOptions() var mock = new Mock(MockBehavior.Strict); var options = new StrykerOptions() { - Thresholds = new Thresholds() + Thresholds = new Stryker.Core.Options.Thresholds() { High = 80, Low = 60, @@ -45,7 +45,7 @@ public void WithConfigFile_ShouldStartStrykerWithConfigFileOptions(string argNam { IStrykerInputs actualInputs = null; var options = new StrykerOptions() { - Thresholds = new Thresholds() { + Thresholds = new Stryker.Core.Options.Thresholds() { High = 80, Low = 60, Break = 0 diff --git a/src/Stryker.CLI/Stryker.CLI.UnitTest/StrykerCLITests.cs b/src/Stryker.CLI/Stryker.CLI.UnitTest/StrykerCLITests.cs index ef5dbae56f..6b0e66c38d 100644 --- a/src/Stryker.CLI/Stryker.CLI.UnitTest/StrykerCLITests.cs +++ b/src/Stryker.CLI/Stryker.CLI.UnitTest/StrykerCLITests.cs @@ -31,7 +31,7 @@ public class StrykerCLITests public StrykerCLITests() { - _options = new StrykerOptions() { Thresholds = new Thresholds { Break = 0 } }; + _options = new StrykerOptions() { Thresholds = new Stryker.Core.Options.Thresholds { Break = 0 } }; _runResults = new StrykerRunResult(_options, 0.3); _strykerRunnerMock.Setup(x => x.RunMutationTest(It.IsAny(), It.IsAny(), It.IsAny())) .Callback((c, l, p) => _inputs = c) @@ -113,7 +113,7 @@ public void OnMutationScoreBelowThresholdBreak_ShouldReturn_ExitCodeBreakThresho var mock = new Mock(MockBehavior.Strict); var options = new StrykerOptions() { - Thresholds = new Thresholds + Thresholds = new Stryker.Core.Options.Thresholds { Break = 40 } @@ -124,7 +124,7 @@ public void OnMutationScoreBelowThresholdBreak_ShouldReturn_ExitCodeBreakThresho .Callback((c, l, p) => Core.Logging.ApplicationLogging.LoggerFactory = l) .Returns(strykerRunResult) .Verifiable(); - + var target = new StrykerCli(mock.Object); var result = target.Run(new string[] { }); @@ -139,7 +139,7 @@ public void OnMutationScoreEqualToNullAndThresholdBreakEqualTo0_ShouldReturnExit var mock = new Mock(MockBehavior.Strict); var options = new StrykerOptions() { - Thresholds = new Thresholds + Thresholds = new Stryker.Core.Options.Thresholds { Break = 0 } @@ -163,7 +163,7 @@ public void OnMutationScoreEqualToNullAndThresholdBreakAbove0_ShouldReturnExitCo var mock = new Mock(MockBehavior.Strict); var options = new StrykerOptions() { - Thresholds = new Thresholds + Thresholds = new Stryker.Core.Options.Thresholds { Break = 40 } @@ -187,7 +187,7 @@ public void OnMutationScoreAboveThresholdBreak_ShouldReturnExitCode0() var mock = new Mock(MockBehavior.Strict); var options = new StrykerOptions() { - Thresholds = new Thresholds + Thresholds = new Stryker.Core.Options.Thresholds { Break = 0 } diff --git a/src/Stryker.CLI/Stryker.CLI.UnitTest/packages.lock.json b/src/Stryker.CLI/Stryker.CLI.UnitTest/packages.lock.json index 55e6b80b22..89e2bd2726 100644 --- a/src/Stryker.CLI/Stryker.CLI.UnitTest/packages.lock.json +++ b/src/Stryker.CLI/Stryker.CLI.UnitTest/packages.lock.json @@ -621,8 +621,32 @@ }, "Newtonsoft.Json": { "type": "Transitive", - "resolved": "13.0.1", - "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" + "resolved": "9.0.1", + "contentHash": "U82mHQSKaIk+lpSVCbWYKNavmNH1i5xrExDEquU1i6I5pV6UMOqRnJRSlKO3cMPfcpp0RgDY+8jUXHdQ4IfXvw==", + "dependencies": { + "Microsoft.CSharp": "4.0.1", + "System.Collections": "4.0.11", + "System.Diagnostics.Debug": "4.0.11", + "System.Dynamic.Runtime": "4.0.11", + "System.Globalization": "4.0.11", + "System.IO": "4.1.0", + "System.Linq": "4.1.0", + "System.Linq.Expressions": "4.1.0", + "System.ObjectModel": "4.0.12", + "System.Reflection": "4.1.0", + "System.Reflection.Extensions": "4.0.1", + "System.Resources.ResourceManager": "4.0.1", + "System.Runtime": "4.1.0", + "System.Runtime.Extensions": "4.1.0", + "System.Runtime.Serialization.Primitives": "4.1.1", + "System.Text.Encoding": "4.0.11", + "System.Text.Encoding.Extensions": "4.0.11", + "System.Text.RegularExpressions": "4.1.0", + "System.Threading": "4.0.11", + "System.Threading.Tasks": "4.0.11", + "System.Xml.ReaderWriter": "4.0.11", + "System.Xml.XDocument": "4.0.11" + } }, "NuGet.Frameworks": { "type": "Transitive", @@ -1119,6 +1143,16 @@ "System.Security.Principal.Windows": "5.0.0" } }, + "System.Diagnostics.Tools": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "xBfJ8pnd4C17dWaC9FM6aShzbJcRNMChUMD42I6772KGGrqaFdumwhn9OdM68erj1ueNo3xdQ1EwiFjK5k8p0g==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0" + } + }, "System.Diagnostics.TraceSource": { "type": "Transitive", "resolved": "4.3.0", @@ -2041,6 +2075,25 @@ "System.Threading.Tasks.Extensions": "4.3.0" } }, + "System.Xml.XDocument": { + "type": "Transitive", + "resolved": "4.0.11", + "contentHash": "Mk2mKmPi0nWaoiYeotq1dgeNK1fqWh61+EK+w4Wu8SWuTYLzpUnschb59bJtGywaPq7SmTuPf44wrXRwbIrukg==", + "dependencies": { + "System.Collections": "4.0.11", + "System.Diagnostics.Debug": "4.0.11", + "System.Diagnostics.Tools": "4.0.1", + "System.Globalization": "4.0.11", + "System.IO": "4.1.0", + "System.Reflection": "4.1.0", + "System.Resources.ResourceManager": "4.0.1", + "System.Runtime": "4.1.0", + "System.Runtime.Extensions": "4.1.0", + "System.Text.Encoding": "4.0.11", + "System.Threading": "4.0.11", + "System.Xml.ReaderWriter": "4.0.11" + } + }, "System.Xml.XmlDocument": { "type": "Transitive", "resolved": "4.3.0", @@ -2130,7 +2183,6 @@ "Microsoft.TestPlatform.TranslationLayer": "16.10.0", "Microsoft.Web.LibraryManager.Build": "2.1.113", "Mono.Cecil": "0.11.4", - "Newtonsoft.Json": "13.0.1", "Serilog": "2.10.0", "Serilog.Extensions.Logging": "3.0.1", "Serilog.Extensions.Logging.File": "2.0.0", diff --git a/src/Stryker.CLI/Stryker.CLI/FileBasedInput.cs b/src/Stryker.CLI/Stryker.CLI/FileBasedInput.cs index 72caa8f9ad..b8da009122 100644 --- a/src/Stryker.CLI/Stryker.CLI/FileBasedInput.cs +++ b/src/Stryker.CLI/Stryker.CLI/FileBasedInput.cs @@ -1,103 +1,155 @@ -using Newtonsoft.Json; -using Stryker.Core.Options; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; namespace Stryker.CLI { - public class FileBasedInputOuter + public interface IExtraData { - [JsonProperty(PropertyName = "stryker-config")] - public FileBasedInput Input { get; set; } + Dictionary ExtraData { get; init; } } - public class FileBasedInput + public class FileBasedInputOuter : IExtraData { - [JsonProperty(PropertyName = "project-info")] - public ProjectInfo ProjectInfo { get; set; } + [JsonPropertyName("stryker-config")] + public FileBasedInput Input { get; init; } - public int? Concurrency { get; set; } + [JsonExtensionData] + public Dictionary ExtraData { get; init; } + } + + public class FileBasedInput : IExtraData + { + [JsonPropertyName("project-info")] + public ProjectInfo ProjectInfo { get; init; } + + [JsonPropertyName("concurrency")] + public int? Concurrency { get; init; } + + [JsonPropertyName("mutation-level")] + public string MutationLevel { get; init; } + + [JsonPropertyName("language-version")] + public string LanguageVersion { get; init; } + + [JsonPropertyName("additional-timeout")] + public int? AdditionalTimeout { get; init; } + + [JsonPropertyName("mutate")] + public string[] Mutate { get; init; } - [JsonProperty(PropertyName = "mutation-level")] - public string MutationLevel { get; set; } + [JsonPropertyName("solution")] + public string Solution { get; init; } - [JsonProperty(PropertyName = "language-version")] - public string LanguageVersion { get; set; } + [JsonPropertyName("target-framework")] + public string TargetFramework { get; init; } - [JsonProperty(PropertyName = "additional-timeout")] - public int AdditionalTimeout { get; set; } + [JsonPropertyName("project")] + public string Project { get; init; } - public string[] Mutate { get; set; } + [JsonPropertyName("coverage-analysis")] + public string CoverageAnalysis { get; init; } - public string Solution { get; set; } + [JsonPropertyName("disable-bail")] + public bool? DisableBail { get; init; } - [JsonProperty(PropertyName = "target-framework")] - public string TargetFramework { get; set; } + [JsonPropertyName("disable-mix-mutants")] + public bool? DisableMixMutants { get; init; } - public string Project { get; set; } + [JsonPropertyName("thresholds")] + public Thresholds Thresholds { get; init; } - [JsonProperty(PropertyName = "coverage-analysis")] - public string CoverageAnalysis { get; set; } + [JsonPropertyName("verbosity")] + public string Verbosity { get; init; } - [JsonProperty(PropertyName = "disable-bail")] - public bool DisableBail { get; set; } + [JsonPropertyName("reporters")] + public string[] Reporters { get; init; } - [JsonProperty(PropertyName = "disable-mix-mutants")] - public bool DisableMixMutants { get; set; } + [JsonPropertyName("since")] + public Since Since { get; init; } - public Thresholds Thresholds { get; set; } + [JsonPropertyName("baseline")] + public Baseline Baseline { get; init; } - public string Verbosity { get; set; } + [JsonPropertyName("dashboard-url")] + public string DashboardUrl { get; init; } - public string[] Reporters { get; set; } + [JsonPropertyName("test-projects")] + public string[] TestProjects { get; init; } - public Since Since { get; set; } + [JsonPropertyName("test-case-filter")] + public string TestCaseFilter { get; init; } - public Baseline Baseline { get; set; } + [JsonPropertyName("ignore-mutations")] + public string[] IgnoreMutations { get; init; } - [JsonProperty(PropertyName = "dashboard-url")] - public string DashboardUrl { get; set; } + [JsonPropertyName("ignore-methods")] + public string[] IgnoreMethods { get; init; } - [JsonProperty(PropertyName = "test-projects")] - public string[] TestProjects { get; set; } + [JsonExtensionData] + public Dictionary ExtraData { get; init; } + } + + public class Since : IExtraData + { + [JsonPropertyName("enabled")] + public bool? Enabled { get; init; } - [JsonProperty(PropertyName = "test-case-filter")] - public string TestCaseFilter { get; set; } + [JsonPropertyName("ignore-changes-in")] + public string[] IgnoreChangesIn { get; init; } - [JsonProperty(PropertyName = "ignore-mutations")] - public string[] IgnoreMutations { get; set; } + [JsonPropertyName("target")] + public string Target { get; init; } - [JsonProperty(PropertyName = "ignore-methods")] - public string[] IgnoreMethods { get; set; } + [JsonExtensionData] + public Dictionary ExtraData { get; init; } } - public class Since + public class Baseline : IExtraData { - public bool? Enabled { get; set; } + [JsonPropertyName("enabled")] + public bool? Enabled { get; init; } + + [JsonPropertyName("provider")] + public string Provider { get; init; } - [JsonProperty(PropertyName = "ignore-changes-in")] - public string[] IgnoreChangesIn { get; set; } + [JsonPropertyName("azure-fileshare-url")] + public string AzureFileShareUrl { get; init; } - [JsonProperty(PropertyName = "target")] - public string Target { get; set; } + [JsonPropertyName("fallback-version")] + public string FallbackVersion { get; init; } + + [JsonExtensionData] + public Dictionary ExtraData { get; init; } } - public class Baseline + public class ProjectInfo : IExtraData { - public bool? Enabled { get; set; } + [JsonPropertyName("name")] + public string Name { get; init; } - [JsonProperty(PropertyName = "provider")] - public string Provider { get; set; } + [JsonPropertyName("module")] + public string Module { get; init; } - [JsonProperty(PropertyName = "azure-fileshare-url")] - public string AzureFileShareUrl { get; set; } + [JsonPropertyName("version")] + public string Version { get; init; } - [JsonProperty(PropertyName = "fallback-version")] - public string FallbackVersion { get; set; } + [JsonExtensionData] + public Dictionary ExtraData { get; init; } } - public class ProjectInfo + public class Thresholds : IExtraData { - public string Name { get; set; } - public string Module { get; set; } - public string Version { get; set; } + [JsonPropertyName("high")] + public int? High { get; init; } + + [JsonPropertyName("low")] + public int? Low { get; init; } + + [JsonPropertyName("break")] + public int? Break { get; init; } + + [JsonExtensionData] + public Dictionary ExtraData { get; init; } } } diff --git a/src/Stryker.CLI/Stryker.CLI/JsonConfigHandler.cs b/src/Stryker.CLI/Stryker.CLI/JsonConfigHandler.cs index b69ada1764..b0fe104f9b 100644 --- a/src/Stryker.CLI/Stryker.CLI/JsonConfigHandler.cs +++ b/src/Stryker.CLI/Stryker.CLI/JsonConfigHandler.cs @@ -1,5 +1,10 @@ +using System; +using System.Collections.Generic; using System.IO; -using Newtonsoft.Json; +using System.Linq; +using System.Reflection; +using System.Text.Json; +using System.Text.Json.Serialization; using Stryker.Core.Exceptions; using Stryker.Core.Options; @@ -54,20 +59,52 @@ jsonConfig.Baseline is not null && private static FileBasedInput LoadJsonConfig(string configFilePath) { - var json = new StreamReader(configFilePath).ReadToEnd(); - + using var streamReader = new StreamReader(configFilePath); + var json = streamReader.ReadToEnd(); + FileBasedInput input; try { - var settings = new JsonSerializerSettings() + var root = JsonSerializer.Deserialize(json); + if (root == null) + { + throw new InputException($"The config file at \"{configFilePath}\" could not be parsed."); + } + IReadOnlyCollection extraKeys = root.ExtraData != null ? root.ExtraData.Keys : Array.Empty(); + if (extraKeys.Any()) { - MissingMemberHandling = MissingMemberHandling.Error - }; + var description = extraKeys.Count == 1 ? $"\"{extraKeys.First()}\" was found" : $"several were found: {{ \"{string.Join("\", \"", extraKeys)}\" }}"; + throw new InputException($"The config file at \"{configFilePath}\" must contain a single \"stryker-config\" root object but {description}."); + } + input = root.Input ?? throw new InputException($"The config file at \"{configFilePath}\" must contain a single \"stryker-config\" root object."); + } + catch (JsonException jsonException) + { + throw new InputException($"The config file at \"{configFilePath}\" could not be parsed.", jsonException.Message); + } + + EnsureCorrectKeys(configFilePath, input, "stryker-config"); - return JsonConvert.DeserializeObject(json, settings).Input; + return input; + } + + private static void EnsureCorrectKeys(string configFilePath, IExtraData @object, string namePath) + { + var properties = @object.GetType().GetProperties().Where(e => e.GetCustomAttribute() != null).ToList(); + foreach (var property in properties.Where(property => property.PropertyType.IsAssignableTo(typeof(IExtraData)))) + { + var child = (IExtraData)property.GetValue(@object); + if (child != null) + { + EnsureCorrectKeys(configFilePath, child, $"{namePath}.{property.GetCustomAttribute()!.Name}"); + } } - catch (JsonSerializationException ex) + var extraData = @object.ExtraData; + IReadOnlyCollection extraKeys = extraData != null ? extraData.Keys : Array.Empty(); + if (extraKeys.Any()) { - throw new InputException(@$"There was a problem with one of the json properties in your stryker config. Path ""{ex.Path}"", message: ""{ex.Message}"""); + var allowedKeys = properties.Select(e => e.GetCustomAttribute()!.Name).OrderBy(e => e); + var description = extraKeys.Count == 1 ? $"\"{extraKeys.First()}\" was found" : $"others were found (\"{string.Join("\", \"", extraKeys)}\")"; + throw new InputException($"The allowed keys for the \"{namePath}\" object are {{ \"{string.Join("\", \"", allowedKeys)}\" }} but {description} in the config file at \"{configFilePath}\""); } } } diff --git a/src/Stryker.CLI/Stryker.CLI/packages.lock.json b/src/Stryker.CLI/Stryker.CLI/packages.lock.json index b924a66990..4504cb6e8b 100644 --- a/src/Stryker.CLI/Stryker.CLI/packages.lock.json +++ b/src/Stryker.CLI/Stryker.CLI/packages.lock.json @@ -189,6 +189,29 @@ "Microsoft.CodeAnalysis.Common": "[3.11.0]" } }, + "Microsoft.CSharp": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "17h8b5mXa87XYKrrVqdgZ38JefSUqLChUQpXgSnpzsM0nDOhE40FTeNWOJ/YmySGV6tG6T8+hjz6vxbknHJr6A==", + "dependencies": { + "System.Collections": "4.0.11", + "System.Diagnostics.Debug": "4.0.11", + "System.Dynamic.Runtime": "4.0.11", + "System.Globalization": "4.0.11", + "System.Linq": "4.1.0", + "System.Linq.Expressions": "4.1.0", + "System.ObjectModel": "4.0.12", + "System.Reflection": "4.1.0", + "System.Reflection.Extensions": "4.0.1", + "System.Reflection.Primitives": "4.0.1", + "System.Reflection.TypeExtensions": "4.1.0", + "System.Resources.ResourceManager": "4.0.1", + "System.Runtime": "4.1.0", + "System.Runtime.Extensions": "4.1.0", + "System.Runtime.InteropServices": "4.1.0", + "System.Threading": "4.0.11" + } + }, "Microsoft.DotNet.PlatformAbstractions": { "type": "Transitive", "resolved": "2.1.0", @@ -459,8 +482,32 @@ }, "Newtonsoft.Json": { "type": "Transitive", - "resolved": "13.0.1", - "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" + "resolved": "9.0.1", + "contentHash": "U82mHQSKaIk+lpSVCbWYKNavmNH1i5xrExDEquU1i6I5pV6UMOqRnJRSlKO3cMPfcpp0RgDY+8jUXHdQ4IfXvw==", + "dependencies": { + "Microsoft.CSharp": "4.0.1", + "System.Collections": "4.0.11", + "System.Diagnostics.Debug": "4.0.11", + "System.Dynamic.Runtime": "4.0.11", + "System.Globalization": "4.0.11", + "System.IO": "4.1.0", + "System.Linq": "4.1.0", + "System.Linq.Expressions": "4.1.0", + "System.ObjectModel": "4.0.12", + "System.Reflection": "4.1.0", + "System.Reflection.Extensions": "4.0.1", + "System.Resources.ResourceManager": "4.0.1", + "System.Runtime": "4.1.0", + "System.Runtime.Extensions": "4.1.0", + "System.Runtime.Serialization.Primitives": "4.1.1", + "System.Text.Encoding": "4.0.11", + "System.Text.Encoding.Extensions": "4.0.11", + "System.Text.RegularExpressions": "4.1.0", + "System.Threading": "4.0.11", + "System.Threading.Tasks": "4.0.11", + "System.Xml.ReaderWriter": "4.0.11", + "System.Xml.XDocument": "4.0.11" + } }, "NuGet.Frameworks": { "type": "Transitive", @@ -751,6 +798,16 @@ "System.Threading": "4.3.0" } }, + "System.Diagnostics.Tools": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "xBfJ8pnd4C17dWaC9FM6aShzbJcRNMChUMD42I6772KGGrqaFdumwhn9OdM68erj1ueNo3xdQ1EwiFjK5k8p0g==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0" + } + }, "System.Diagnostics.TraceSource": { "type": "Transitive", "resolved": "4.0.0", @@ -1411,6 +1468,19 @@ "System.Text.Encoding": "4.0.11" } }, + "System.Text.RegularExpressions": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "i88YCXpRTjCnoSQZtdlHkAOx4KNNik4hMy83n0+Ftlb7jvV6ZiZWMpnEZHhjBp6hQVh8gWd/iKNPzlPF7iyA2g==", + "dependencies": { + "System.Collections": "4.0.11", + "System.Globalization": "4.0.11", + "System.Resources.ResourceManager": "4.0.1", + "System.Runtime": "4.1.0", + "System.Runtime.Extensions": "4.1.0", + "System.Threading": "4.0.11" + } + }, "System.Threading": { "type": "Transitive", "resolved": "4.3.0", @@ -1458,6 +1528,47 @@ "System.Runtime": "4.1.0" } }, + "System.Xml.ReaderWriter": { + "type": "Transitive", + "resolved": "4.0.11", + "contentHash": "ZIiLPsf67YZ9zgr31vzrFaYQqxRPX9cVHjtPSnmx4eN6lbS/yEyYNr2vs1doGDEscF0tjCZFsk9yUg1sC9e8tg==", + "dependencies": { + "System.Collections": "4.0.11", + "System.Diagnostics.Debug": "4.0.11", + "System.Globalization": "4.0.11", + "System.IO": "4.1.0", + "System.IO.FileSystem": "4.0.1", + "System.IO.FileSystem.Primitives": "4.0.1", + "System.Resources.ResourceManager": "4.0.1", + "System.Runtime": "4.1.0", + "System.Runtime.Extensions": "4.1.0", + "System.Runtime.InteropServices": "4.1.0", + "System.Text.Encoding": "4.0.11", + "System.Text.Encoding.Extensions": "4.0.11", + "System.Text.RegularExpressions": "4.1.0", + "System.Threading.Tasks": "4.0.11", + "System.Threading.Tasks.Extensions": "4.0.0" + } + }, + "System.Xml.XDocument": { + "type": "Transitive", + "resolved": "4.0.11", + "contentHash": "Mk2mKmPi0nWaoiYeotq1dgeNK1fqWh61+EK+w4Wu8SWuTYLzpUnschb59bJtGywaPq7SmTuPf44wrXRwbIrukg==", + "dependencies": { + "System.Collections": "4.0.11", + "System.Diagnostics.Debug": "4.0.11", + "System.Diagnostics.Tools": "4.0.1", + "System.Globalization": "4.0.11", + "System.IO": "4.1.0", + "System.Reflection": "4.1.0", + "System.Resources.ResourceManager": "4.0.1", + "System.Runtime": "4.1.0", + "System.Runtime.Extensions": "4.1.0", + "System.Text.Encoding": "4.0.11", + "System.Threading": "4.0.11", + "System.Xml.ReaderWriter": "4.0.11" + } + }, "stryker": { "type": "Project", "dependencies": { @@ -1476,7 +1587,6 @@ "Microsoft.TestPlatform.TranslationLayer": "16.10.0", "Microsoft.Web.LibraryManager.Build": "2.1.113", "Mono.Cecil": "0.11.4", - "Newtonsoft.Json": "13.0.1", "Serilog": "2.10.0", "Serilog.Extensions.Logging": "3.0.1", "Serilog.Extensions.Logging.File": "2.0.0", diff --git a/src/Stryker.Core/Stryker.Core.UnitTest/Baseline/Utils/BaselineMutantHelperTests.cs b/src/Stryker.Core/Stryker.Core.UnitTest/Baseline/Utils/BaselineMutantHelperTests.cs index eba052413c..46f3294b63 100644 --- a/src/Stryker.Core/Stryker.Core.UnitTest/Baseline/Utils/BaselineMutantHelperTests.cs +++ b/src/Stryker.Core/Stryker.Core.UnitTest/Baseline/Utils/BaselineMutantHelperTests.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; using System.IO; -using System.Text; -using Moq; using Shouldly; using Stryker.Core.Baseline.Utils; using Stryker.Core.Reporters.Json; @@ -24,16 +20,19 @@ public void GetMutantSourceShouldReturnMutantSource() var jsonMutant = new JsonMutant { - Location = new JsonMutantLocation(new JsonMutantPosition + Location = new JsonMutantLocation { - Column = 17, - Line = 17 - }, - new JsonMutantPosition - { - Column = 62, - Line = 17 - }), + Start = new JsonMutantPosition + { + Column = 17, + Line = 17 + }, + End = new JsonMutantPosition + { + Column = 62, + Line = 17 + } + } }; var target = new BaselineMutantHelper(); @@ -57,16 +56,19 @@ public void GetMutantSourceShouldReturnMutantSource_When_Multiple_Lines() var jsonMutant = new JsonMutant { - Location = new JsonMutantLocation(new JsonMutantPosition - { - Column = 13, - Line = 24 - }, - new JsonMutantPosition + Location = new JsonMutantLocation { - Column = 38, - Line = 26 - }), + Start = new JsonMutantPosition + { + Column = 13, + Line = 24 + }, + End = new JsonMutantPosition + { + Column = 38, + Line = 26 + } + } }; var target = new BaselineMutantHelper(); @@ -92,16 +94,19 @@ public void GetMutantSource_Gets_Partial_Line() var jsonMutant = new JsonMutant { - Location = new JsonMutantLocation(new JsonMutantPosition - { - Column = 30, - Line = 34 - }, - new JsonMutantPosition + Location = new JsonMutantLocation { - Column = 34, - Line = 34 - }), + Start = new JsonMutantPosition + { + Column = 30, + Line = 34 + }, + End = new JsonMutantPosition + { + Column = 34, + Line = 34 + } + } }; var target = new BaselineMutantHelper(); diff --git a/src/Stryker.Core/Stryker.Core.UnitTest/Reporters/JsonReporter/MockJsonReport.cs b/src/Stryker.Core/Stryker.Core.UnitTest/Reporters/JsonReporter/MockJsonReport.cs index 597cbf0b69..ece1a73d66 100644 --- a/src/Stryker.Core/Stryker.Core.UnitTest/Reporters/JsonReporter/MockJsonReport.cs +++ b/src/Stryker.Core/Stryker.Core.UnitTest/Reporters/JsonReporter/MockJsonReport.cs @@ -8,9 +8,11 @@ public class MockJsonReport : JsonReport public MockJsonReport( IDictionary thresholds, IDictionary files - ) : base("1.3", thresholds, files) + ) { - + SchemaVersion = "1.3"; + Thresholds = thresholds; + Files = files; } } } diff --git a/src/Stryker.Core/Stryker.Core.UnitTest/Reporters/JsonReporter/MockJsonReportFileComponent.cs b/src/Stryker.Core/Stryker.Core.UnitTest/Reporters/JsonReporter/MockJsonReportFileComponent.cs index 1861eff765..feea48c658 100644 --- a/src/Stryker.Core/Stryker.Core.UnitTest/Reporters/JsonReporter/MockJsonReportFileComponent.cs +++ b/src/Stryker.Core/Stryker.Core.UnitTest/Reporters/JsonReporter/MockJsonReportFileComponent.cs @@ -1,6 +1,4 @@ -using System; using System.Collections.Generic; -using System.Text; using Stryker.Core.Reporters.Json; namespace Stryker.Core.UnitTest.Reporters @@ -11,9 +9,11 @@ public MockJsonReportFileComponent( string language, string source, ISet mutants - ) : base(language, source, mutants) + ) { - + Language = language; + Source = source; + Mutants = mutants; } } } diff --git a/src/Stryker.Core/Stryker.Core.UnitTest/Reporters/JsonReporterTests.cs b/src/Stryker.Core/Stryker.Core.UnitTest/Reporters/JsonReporterTests.cs index 851bb7c6a1..110790a1fa 100644 --- a/src/Stryker.Core/Stryker.Core.UnitTest/Reporters/JsonReporterTests.cs +++ b/src/Stryker.Core/Stryker.Core.UnitTest/Reporters/JsonReporterTests.cs @@ -7,7 +7,6 @@ using Microsoft.Extensions.Logging; using Moq; using Shouldly; -using Stryker.Core.Logging; using Stryker.Core.Options; using Stryker.Core.ProjectComponents; using Stryker.Core.Reporters.Json; @@ -140,6 +139,10 @@ public void JsonReporter_OnAllMutantsTestedShouldWriteJsonToFile() reporter.OnAllMutantsTested(JsonReportTestHelper.CreateProjectWith().ToReadOnlyInputComponent()); var reportPath = Path.Combine(options.OutputPath, "reports", $"mutation-report.json"); mockFileSystem.FileExists(reportPath).ShouldBeTrue($"Path {reportPath} should exist but it does not."); + var fileContents = mockFileSystem.File.ReadAllText(reportPath); + fileContents.ShouldContain(@"""thresholds"": {"); + fileContents.ShouldContain(@"""high"": 80"); + fileContents.ShouldContain(@"""low"": 60"); } } } diff --git a/src/Stryker.Core/Stryker.Core.UnitTest/packages.lock.json b/src/Stryker.Core/Stryker.Core.UnitTest/packages.lock.json index 01b6f3770d..f0b9cc3cff 100644 --- a/src/Stryker.Core/Stryker.Core.UnitTest/packages.lock.json +++ b/src/Stryker.Core/Stryker.Core.UnitTest/packages.lock.json @@ -623,8 +623,32 @@ }, "Newtonsoft.Json": { "type": "Transitive", - "resolved": "13.0.1", - "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" + "resolved": "9.0.1", + "contentHash": "U82mHQSKaIk+lpSVCbWYKNavmNH1i5xrExDEquU1i6I5pV6UMOqRnJRSlKO3cMPfcpp0RgDY+8jUXHdQ4IfXvw==", + "dependencies": { + "Microsoft.CSharp": "4.0.1", + "System.Collections": "4.0.11", + "System.Diagnostics.Debug": "4.0.11", + "System.Dynamic.Runtime": "4.0.11", + "System.Globalization": "4.0.11", + "System.IO": "4.1.0", + "System.Linq": "4.1.0", + "System.Linq.Expressions": "4.1.0", + "System.ObjectModel": "4.0.12", + "System.Reflection": "4.1.0", + "System.Reflection.Extensions": "4.0.1", + "System.Resources.ResourceManager": "4.0.1", + "System.Runtime": "4.1.0", + "System.Runtime.Extensions": "4.1.0", + "System.Runtime.Serialization.Primitives": "4.1.1", + "System.Text.Encoding": "4.0.11", + "System.Text.Encoding.Extensions": "4.0.11", + "System.Text.RegularExpressions": "4.1.0", + "System.Threading": "4.0.11", + "System.Threading.Tasks": "4.0.11", + "System.Xml.ReaderWriter": "4.0.11", + "System.Xml.XDocument": "4.0.11" + } }, "NuGet.Frameworks": { "type": "Transitive", @@ -1111,6 +1135,16 @@ "System.Security.Principal.Windows": "5.0.0" } }, + "System.Diagnostics.Tools": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "xBfJ8pnd4C17dWaC9FM6aShzbJcRNMChUMD42I6772KGGrqaFdumwhn9OdM68erj1ueNo3xdQ1EwiFjK5k8p0g==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0" + } + }, "System.Diagnostics.TraceSource": { "type": "Transitive", "resolved": "4.3.0", @@ -2033,6 +2067,25 @@ "System.Threading.Tasks.Extensions": "4.3.0" } }, + "System.Xml.XDocument": { + "type": "Transitive", + "resolved": "4.0.11", + "contentHash": "Mk2mKmPi0nWaoiYeotq1dgeNK1fqWh61+EK+w4Wu8SWuTYLzpUnschb59bJtGywaPq7SmTuPf44wrXRwbIrukg==", + "dependencies": { + "System.Collections": "4.0.11", + "System.Diagnostics.Debug": "4.0.11", + "System.Diagnostics.Tools": "4.0.1", + "System.Globalization": "4.0.11", + "System.IO": "4.1.0", + "System.Reflection": "4.1.0", + "System.Resources.ResourceManager": "4.0.1", + "System.Runtime": "4.1.0", + "System.Runtime.Extensions": "4.1.0", + "System.Text.Encoding": "4.0.11", + "System.Threading": "4.0.11", + "System.Xml.ReaderWriter": "4.0.11" + } + }, "System.Xml.XmlDocument": { "type": "Transitive", "resolved": "4.3.0", @@ -2118,7 +2171,6 @@ "Microsoft.TestPlatform.TranslationLayer": "16.10.0", "Microsoft.Web.LibraryManager.Build": "2.1.113", "Mono.Cecil": "0.11.4", - "Newtonsoft.Json": "13.0.1", "Serilog": "2.10.0", "Serilog.Extensions.Logging": "3.0.1", "Serilog.Extensions.Logging.File": "2.0.0", diff --git a/src/Stryker.Core/Stryker.Core/Baseline/Providers/AzureFileShareBaselineProvider.cs b/src/Stryker.Core/Stryker.Core/Baseline/Providers/AzureFileShareBaselineProvider.cs index 8137397917..e3cb5616c0 100644 --- a/src/Stryker.Core/Stryker.Core/Baseline/Providers/AzureFileShareBaselineProvider.cs +++ b/src/Stryker.Core/Stryker.Core/Baseline/Providers/AzureFileShareBaselineProvider.cs @@ -1,5 +1,4 @@ using Microsoft.Extensions.Logging; -using Newtonsoft.Json; using Stryker.Core.Logging; using Stryker.Core.Options; using Stryker.Core.Reporters.Json; @@ -48,9 +47,9 @@ public async Task Load(string version) if (response.StatusCode == HttpStatusCode.OK) { - var content = await response.Content.ReadAsStringAsync(); + var stream = await response.Content.ReadAsStreamAsync(); - return JsonConvert.DeserializeObject(content); + return await stream.DeserializeJsonReportAsync(); } _logger.LogDebug("No baseline was found at {0}", fileUrl); diff --git a/src/Stryker.Core/Stryker.Core/Baseline/Providers/DiskBaselineProvider.cs b/src/Stryker.Core/Stryker.Core/Baseline/Providers/DiskBaselineProvider.cs index 16ce15575f..68132147d7 100644 --- a/src/Stryker.Core/Stryker.Core/Baseline/Providers/DiskBaselineProvider.cs +++ b/src/Stryker.Core/Stryker.Core/Baseline/Providers/DiskBaselineProvider.cs @@ -1,5 +1,4 @@ using Microsoft.Extensions.Logging; -using Newtonsoft.Json; using Stryker.Core.Logging; using Stryker.Core.Options; using Stryker.Core.Reporters.Json; @@ -31,31 +30,27 @@ public async Task Load(string version) if (_fileSystem.File.Exists(reportPath)) { - using StreamReader inputReader = _fileSystem.File.OpenText(reportPath); + await using var reportStream = _fileSystem.File.OpenRead(reportPath); - var reportJson = await inputReader.ReadToEndAsync(); - - return JsonConvert.DeserializeObject(reportJson); + return await reportStream.DeserializeJsonReportAsync(); } - _logger.LogDebug("No baseline was found at {0}", reportPath.ToString()); + _logger.LogDebug("No baseline was found at {ReportPath}", reportPath); return null; } public async Task Save(JsonReport report, string version) { - var reportPath = FilePathUtils.NormalizePathSeparators( + var reportDirectory = FilePathUtils.NormalizePathSeparators( Path.Combine(_options.BasePath, _outputPath, version)); - var reportJson = report.ToJson(); - - _fileSystem.Directory.CreateDirectory(reportPath); - - await using StreamWriter outputWriter = _fileSystem.File.CreateText(Path.Combine(reportPath, $"stryker-report.json")); + _fileSystem.Directory.CreateDirectory(reportDirectory); - await outputWriter.WriteAsync(reportJson); + var reportPath = Path.Combine(reportDirectory, "stryker-report.json"); + await using var reportStream = _fileSystem.File.Create(reportPath); + await report.SerializeAsync(reportStream); - _logger.LogDebug($"Baseline report has been saved to {Path.Combine(reportPath, $"stryker-report.json")}"); + _logger.LogDebug("Baseline report has been saved to {ReportPath}", reportPath); } } } diff --git a/src/Stryker.Core/Stryker.Core/Clients/DashboardClient.cs b/src/Stryker.Core/Stryker.Core/Clients/DashboardClient.cs index d7af6dd0b4..6136915414 100644 --- a/src/Stryker.Core/Stryker.Core/Clients/DashboardClient.cs +++ b/src/Stryker.Core/Stryker.Core/Clients/DashboardClient.cs @@ -44,8 +44,9 @@ public async Task PublishReport(JsonReport report, string version) try { - using var response = await _httpClient.PutAsJsonAsync(url, report); + using var response = await _httpClient.PutAsJsonAsync(url, report, JsonReportSerialization.Options); response.EnsureSuccessStatusCode(); + var result = await response.Content.ReadFromJsonAsync(); return result?.Href; } @@ -63,7 +64,7 @@ public async Task PullReport(string version) _logger.LogDebug("Sending GET to {DashboardUrl}", url); try { - var report = await _httpClient.GetFromJsonAsync(url); + var report = await _httpClient.GetFromJsonAsync(url, JsonReportSerialization.Options); return report; } catch (Exception exception) diff --git a/src/Stryker.Core/Stryker.Core/Reporters/JsonReporter/JsonMutantLocation.cs b/src/Stryker.Core/Stryker.Core/Reporters/JsonReporter/JsonMutantLocation.cs index c665aa5706..8dc7ad9ec6 100644 --- a/src/Stryker.Core/Stryker.Core/Reporters/JsonReporter/JsonMutantLocation.cs +++ b/src/Stryker.Core/Stryker.Core/Reporters/JsonReporter/JsonMutantLocation.cs @@ -1,18 +1,14 @@ using Microsoft.CodeAnalysis; -using Newtonsoft.Json; namespace Stryker.Core.Reporters.Json { public class JsonMutantLocation { - public JsonMutantPosition Start { get; } - public JsonMutantPosition End { get; } + public JsonMutantPosition Start { get; init; } + public JsonMutantPosition End { get; init; } - [JsonConstructor] - public JsonMutantLocation(JsonMutantPosition start, JsonMutantPosition end) + public JsonMutantLocation() { - Start = start; - End = end; } public JsonMutantLocation(FileLinePositionSpan location) @@ -29,4 +25,4 @@ public JsonMutantLocation(FileLinePositionSpan location) }; } } -} \ No newline at end of file +} diff --git a/src/Stryker.Core/Stryker.Core/Reporters/JsonReporter/JsonReport.cs b/src/Stryker.Core/Stryker.Core/Reporters/JsonReporter/JsonReport.cs index a23584dd88..4b59041f42 100644 --- a/src/Stryker.Core/Stryker.Core/Reporters/JsonReporter/JsonReport.cs +++ b/src/Stryker.Core/Stryker.Core/Reporters/JsonReporter/JsonReport.cs @@ -1,7 +1,5 @@ using System.Collections.Generic; using System.Linq; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; using Stryker.Core.Options; using Stryker.Core.ProjectComponents; @@ -14,10 +12,8 @@ public class JsonReport public string ProjectRoot { get; init; } public IDictionary Files { get; init; } = new Dictionary(); - [JsonConstructor] public JsonReport() { - } private JsonReport(StrykerOptions options, IReadOnlyProjectComponent mutationReport) @@ -30,38 +26,11 @@ private JsonReport(StrykerOptions options, IReadOnlyProjectComponent mutationRep Merge(Files, GenerateReportComponents(mutationReport)); } - protected JsonReport(string schemaVersion, IDictionary thresholds, IDictionary files) - { - SchemaVersion = schemaVersion ?? SchemaVersion; - Thresholds = thresholds ?? Thresholds; - Files = files ?? Files; - } - public static JsonReport Build(StrykerOptions options, IReadOnlyProjectComponent mutationReport) { return new JsonReport(options, mutationReport); } - public string ToJson() - { - var json = JsonConvert.SerializeObject(this, new JsonSerializerSettings - { - ContractResolver = new DefaultContractResolver - { - NamingStrategy = new CamelCaseNamingStrategy() - }, - Formatting = Formatting.Indented, - NullValueHandling = NullValueHandling.Ignore - }); - - return json; - } - - public string ToJsonHtmlSafe() - { - return ToJson().Replace("<", "<\" + \""); - } - private IDictionary GenerateReportComponents(IReadOnlyProjectComponent component) { var files = new Dictionary(); diff --git a/src/Stryker.Core/Stryker.Core/Reporters/JsonReporter/JsonReportFileComponent.cs b/src/Stryker.Core/Stryker.Core/Reporters/JsonReporter/JsonReportFileComponent.cs index a7eac50889..1e2a53fa47 100644 --- a/src/Stryker.Core/Stryker.Core/Reporters/JsonReporter/JsonReportFileComponent.cs +++ b/src/Stryker.Core/Stryker.Core/Reporters/JsonReporter/JsonReportFileComponent.cs @@ -1,5 +1,4 @@ using Microsoft.Extensions.Logging; -using Newtonsoft.Json; using Stryker.Core.Logging; using Stryker.Core.ProjectComponents; using System.Collections.Generic; @@ -8,16 +7,12 @@ namespace Stryker.Core.Reporters.Json { public class JsonReportFileComponent { - public string Language { get; } - public string Source { get; } - public ISet Mutants { get; } + public string Language { get; init; } + public string Source { get; init; } + public ISet Mutants { get; init; } - [JsonConstructor] - protected JsonReportFileComponent(string language, string source, ISet mutants) + public JsonReportFileComponent() { - Language = language; - Source = source; - Mutants = mutants; } public JsonReportFileComponent(ReadOnlyFileLeaf file, ILogger logger = null) diff --git a/src/Stryker.Core/Stryker.Core/Reporters/JsonReporter/JsonReportSerialization.cs b/src/Stryker.Core/Stryker.Core/Reporters/JsonReporter/JsonReportSerialization.cs new file mode 100644 index 0000000000..b116e50762 --- /dev/null +++ b/src/Stryker.Core/Stryker.Core/Reporters/JsonReporter/JsonReportSerialization.cs @@ -0,0 +1,48 @@ +using System.IO; +using System.Text.Json; +using System.Threading.Tasks; + +namespace Stryker.Core.Reporters.Json +{ + internal static class JsonReportSerialization + { + public static readonly JsonSerializerOptions Options = new() + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + WriteIndented = true, + }; + + public static async Task DeserializeJsonReportAsync(this Stream stream) + { + return await JsonSerializer.DeserializeAsync(stream, Options); + } + + public static async Task SerializeAsync(this JsonReport report, Stream stream) + { + await JsonSerializer.SerializeAsync(stream, report, Options); + } + + public static async Task SerializeAsync(this JsonReport report) + { + await using var stream = new MemoryStream(); + await report.SerializeAsync(stream); + return stream.ToArray(); + } + + public static void Serialize(this JsonReport report, Stream stream) + { + using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Indented = Options.WriteIndented }); + JsonSerializer.Serialize(writer, report, Options); + } + + public static string ToJson(this JsonReport report) + { + return JsonSerializer.Serialize(report, Options); + } + + public static string ToJsonHtmlSafe(this JsonReport report) + { + return report.ToJson().Replace("<", "<\" + \""); + } + } +} diff --git a/src/Stryker.Core/Stryker.Core/Reporters/JsonReporter/JsonReporter.cs b/src/Stryker.Core/Stryker.Core/Reporters/JsonReporter/JsonReporter.cs index e35e1d1127..b64e5351df 100644 --- a/src/Stryker.Core/Stryker.Core/Reporters/JsonReporter/JsonReporter.cs +++ b/src/Stryker.Core/Stryker.Core/Reporters/JsonReporter/JsonReporter.cs @@ -28,7 +28,7 @@ public void OnAllMutantsTested(IReadOnlyProjectComponent reportComponent) var reportPath = Path.Combine(_options.OutputPath, "reports", "mutation-report.json"); - WriteReportToJsonFile(reportPath, mutationReport.ToJson()); + WriteReportToJsonFile(reportPath, mutationReport); var clickablePath = reportPath.Replace("\\", "/"); clickablePath = clickablePath.StartsWith("/") ? clickablePath : $"/{clickablePath}"; @@ -36,12 +36,12 @@ public void OnAllMutantsTested(IReadOnlyProjectComponent reportComponent) _consoleWriter.Write(Output.Green($"\nYour json report has been generated at: \n file://{clickablePath} \n")); } - private void WriteReportToJsonFile(string filePath, string mutationReport) + private void WriteReportToJsonFile(string filePath, JsonReport mutationReport) { _fileSystem.Directory.CreateDirectory(Path.GetDirectoryName(filePath)); - using (var file = _fileSystem.File.CreateText(filePath)) + using (var file = _fileSystem.File.Create(filePath)) { - file.WriteLine(mutationReport); + mutationReport.Serialize(file); } } diff --git a/src/Stryker.Core/Stryker.Core/Stryker.Core.csproj b/src/Stryker.Core/Stryker.Core/Stryker.Core.csproj index 3eaa2d5d0b..a404325aee 100644 --- a/src/Stryker.Core/Stryker.Core/Stryker.Core.csproj +++ b/src/Stryker.Core/Stryker.Core/Stryker.Core.csproj @@ -50,7 +50,6 @@ - diff --git a/src/Stryker.Core/Stryker.Core/packages.lock.json b/src/Stryker.Core/Stryker.Core/packages.lock.json index c39dff6914..a464c9d82e 100644 --- a/src/Stryker.Core/Stryker.Core/packages.lock.json +++ b/src/Stryker.Core/Stryker.Core/packages.lock.json @@ -152,12 +152,6 @@ "resolved": "0.11.4", "contentHash": "IC1h5g0NeJGHIUgzM1P82ld57knhP0IcQfrYITDPXlNpMYGUrsG5TxuaWTjaeqDNQMBDNZkB8L0rBnwsY6JHuQ==" }, - "Newtonsoft.Json": { - "type": "Direct", - "requested": "[13.0.1, )", - "resolved": "13.0.1", - "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" - }, "Serilog": { "type": "Direct", "requested": "[2.10.0, )", @@ -322,6 +316,29 @@ "System.Threading.Tasks.Extensions": "4.5.4" } }, + "Microsoft.CSharp": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "17h8b5mXa87XYKrrVqdgZ38JefSUqLChUQpXgSnpzsM0nDOhE40FTeNWOJ/YmySGV6tG6T8+hjz6vxbknHJr6A==", + "dependencies": { + "System.Collections": "4.0.11", + "System.Diagnostics.Debug": "4.0.11", + "System.Dynamic.Runtime": "4.0.11", + "System.Globalization": "4.0.11", + "System.Linq": "4.1.0", + "System.Linq.Expressions": "4.1.0", + "System.ObjectModel": "4.0.12", + "System.Reflection": "4.1.0", + "System.Reflection.Extensions": "4.0.1", + "System.Reflection.Primitives": "4.0.1", + "System.Reflection.TypeExtensions": "4.1.0", + "System.Resources.ResourceManager": "4.0.1", + "System.Runtime": "4.1.0", + "System.Runtime.Extensions": "4.1.0", + "System.Runtime.InteropServices": "4.1.0", + "System.Threading": "4.0.11" + } + }, "Microsoft.DotNet.PlatformAbstractions": { "type": "Transitive", "resolved": "2.1.0", @@ -527,6 +544,35 @@ "Microsoft.NETCore.Platforms": "1.1.0" } }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "9.0.1", + "contentHash": "U82mHQSKaIk+lpSVCbWYKNavmNH1i5xrExDEquU1i6I5pV6UMOqRnJRSlKO3cMPfcpp0RgDY+8jUXHdQ4IfXvw==", + "dependencies": { + "Microsoft.CSharp": "4.0.1", + "System.Collections": "4.0.11", + "System.Diagnostics.Debug": "4.0.11", + "System.Dynamic.Runtime": "4.0.11", + "System.Globalization": "4.0.11", + "System.IO": "4.1.0", + "System.Linq": "4.1.0", + "System.Linq.Expressions": "4.1.0", + "System.ObjectModel": "4.0.12", + "System.Reflection": "4.1.0", + "System.Reflection.Extensions": "4.0.1", + "System.Resources.ResourceManager": "4.0.1", + "System.Runtime": "4.1.0", + "System.Runtime.Extensions": "4.1.0", + "System.Runtime.Serialization.Primitives": "4.1.1", + "System.Text.Encoding": "4.0.11", + "System.Text.Encoding.Extensions": "4.0.11", + "System.Text.RegularExpressions": "4.1.0", + "System.Threading": "4.0.11", + "System.Threading.Tasks": "4.0.11", + "System.Xml.ReaderWriter": "4.0.11", + "System.Xml.XDocument": "4.0.11" + } + }, "NuGet.Frameworks": { "type": "Transitive", "resolved": "5.0.0", @@ -766,6 +812,16 @@ "System.Threading": "4.3.0" } }, + "System.Diagnostics.Tools": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "xBfJ8pnd4C17dWaC9FM6aShzbJcRNMChUMD42I6772KGGrqaFdumwhn9OdM68erj1ueNo3xdQ1EwiFjK5k8p0g==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.NETCore.Targets": "1.0.1", + "System.Runtime": "4.1.0" + } + }, "System.Diagnostics.TraceSource": { "type": "Transitive", "resolved": "4.0.0", @@ -1413,6 +1469,19 @@ "System.Text.Encoding": "4.0.11" } }, + "System.Text.RegularExpressions": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "i88YCXpRTjCnoSQZtdlHkAOx4KNNik4hMy83n0+Ftlb7jvV6ZiZWMpnEZHhjBp6hQVh8gWd/iKNPzlPF7iyA2g==", + "dependencies": { + "System.Collections": "4.0.11", + "System.Globalization": "4.0.11", + "System.Resources.ResourceManager": "4.0.1", + "System.Runtime": "4.1.0", + "System.Runtime.Extensions": "4.1.0", + "System.Threading": "4.0.11" + } + }, "System.Threading": { "type": "Transitive", "resolved": "4.3.0", @@ -1460,6 +1529,47 @@ "System.Runtime": "4.1.0" } }, + "System.Xml.ReaderWriter": { + "type": "Transitive", + "resolved": "4.0.11", + "contentHash": "ZIiLPsf67YZ9zgr31vzrFaYQqxRPX9cVHjtPSnmx4eN6lbS/yEyYNr2vs1doGDEscF0tjCZFsk9yUg1sC9e8tg==", + "dependencies": { + "System.Collections": "4.0.11", + "System.Diagnostics.Debug": "4.0.11", + "System.Globalization": "4.0.11", + "System.IO": "4.1.0", + "System.IO.FileSystem": "4.0.1", + "System.IO.FileSystem.Primitives": "4.0.1", + "System.Resources.ResourceManager": "4.0.1", + "System.Runtime": "4.1.0", + "System.Runtime.Extensions": "4.1.0", + "System.Runtime.InteropServices": "4.1.0", + "System.Text.Encoding": "4.0.11", + "System.Text.Encoding.Extensions": "4.0.11", + "System.Text.RegularExpressions": "4.1.0", + "System.Threading.Tasks": "4.0.11", + "System.Threading.Tasks.Extensions": "4.0.0" + } + }, + "System.Xml.XDocument": { + "type": "Transitive", + "resolved": "4.0.11", + "contentHash": "Mk2mKmPi0nWaoiYeotq1dgeNK1fqWh61+EK+w4Wu8SWuTYLzpUnschb59bJtGywaPq7SmTuPf44wrXRwbIrukg==", + "dependencies": { + "System.Collections": "4.0.11", + "System.Diagnostics.Debug": "4.0.11", + "System.Diagnostics.Tools": "4.0.1", + "System.Globalization": "4.0.11", + "System.IO": "4.1.0", + "System.Reflection": "4.1.0", + "System.Resources.ResourceManager": "4.0.1", + "System.Runtime": "4.1.0", + "System.Runtime.Extensions": "4.1.0", + "System.Text.Encoding": "4.0.11", + "System.Threading": "4.0.11", + "System.Xml.ReaderWriter": "4.0.11" + } + }, "stryker.datacollector": { "type": "Project", "dependencies": {