From e8315ec9ea759b80fb3adce6b5bf65384c41e4a4 Mon Sep 17 00:00:00 2001 From: Gary Ewan Park Date: Tue, 10 Sep 2019 21:29:02 +0100 Subject: [PATCH 01/18] (GH-130) Switch to recipe.cake file This provides some immediate information that a repository is using Cake.Recipe, and it is a convention now followed by a number of other repositories. --- .appveyor.yml | 2 +- build.ps1 | 4 ++-- build.sh | 2 +- setup.cake => recipe.cake | 0 src/Cake.Issues.sln | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) mode change 100644 => 100755 build.sh rename setup.cake => recipe.cake (100%) diff --git a/.appveyor.yml b/.appveyor.yml index 1dbceb116..0d21fc40b 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -27,4 +27,4 @@ branches: # Build Cache # #---------------------------------# cache: -- tools -> setup.cake +- tools -> recipe.cake diff --git a/build.ps1 b/build.ps1 index bdfb32b4f..a6d30c4e8 100644 --- a/build.ps1 +++ b/build.ps1 @@ -35,7 +35,7 @@ http://cakebuild.net [CmdletBinding()] Param( - [string]$Script = "setup.cake", + [string]$Script = "recipe.cake", [string]$Target = "Default", [ValidateSet("Release", "Debug")] [string]$Configuration = "Release", @@ -181,4 +181,4 @@ if (!(Test-Path $CAKE_EXE)) { # Start Cake Write-Host "Running build script..." Invoke-Expression "& `"$CAKE_EXE`" `"$Script`" -target=`"$Target`" -configuration=`"$Configuration`" -verbosity=`"$Verbosity`" $UseMono $UseDryRun $UseExperimental $ScriptArgs" -exit $LASTEXITCODE \ No newline at end of file +exit $LASTEXITCODE diff --git a/build.sh b/build.sh old mode 100644 new mode 100755 index a541ec165..d35bcf4ef --- a/build.sh +++ b/build.sh @@ -11,7 +11,7 @@ NUGET_EXE=$TOOLS_DIR/nuget.exe CAKE_EXE=$TOOLS_DIR/Cake/Cake.exe # Define default arguments. -SCRIPT="setup.cake" +SCRIPT="recipe.cake" TARGET="Default" CONFIGURATION="Release" VERBOSITY="verbose" diff --git a/setup.cake b/recipe.cake similarity index 100% rename from setup.cake rename to recipe.cake diff --git a/src/Cake.Issues.sln b/src/Cake.Issues.sln index 8b6a67117..12fdc0efa 100644 --- a/src/Cake.Issues.sln +++ b/src/Cake.Issues.sln @@ -9,7 +9,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cake.Issues.Tests", "Cake.I EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{12963AB7-D907-4D27-B1F0-8C77A55BD2F2}" ProjectSection(SolutionItems) = preProject - ..\setup.cake = ..\setup.cake + ..\recipe.cake = ..\recipe.cake EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "nuspec", "nuspec", "{C9E29268-658F-4887-95C3-99C48F9A2DAD}" From 2eca4d1e314427302de02226f3f416e4425497f9 Mon Sep 17 00:00:00 2001 From: Gary Ewan Park Date: Tue, 10 Sep 2019 21:29:12 +0100 Subject: [PATCH 02/18] (maint) Remove whitespace --- recipe.cake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/recipe.cake b/recipe.cake index c22d5dd85..fb4418517 100644 --- a/recipe.cake +++ b/recipe.cake @@ -3,7 +3,7 @@ Environment.SetVariableNames(); BuildParameters.SetParameters( - context: Context, + context: Context, buildSystem: BuildSystem, sourceDirectoryPath: "./src", title: "Cake.Issues", From 412885baca3206a457353b6cd21b7d0bb64c6688 Mon Sep 17 00:00:00 2001 From: Gary Ewan Park Date: Tue, 10 Sep 2019 21:29:42 +0100 Subject: [PATCH 03/18] (GH-130) Always run GitVersion So build runs on other operating systems --- recipe.cake | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/recipe.cake b/recipe.cake index fb4418517..e772d580a 100644 --- a/recipe.cake +++ b/recipe.cake @@ -10,7 +10,8 @@ BuildParameters.SetParameters( repositoryOwner: "cake-contrib", repositoryName: "Cake.Issues", appVeyorAccountName: "cakecontrib", - shouldRunCodecov: false); + shouldRunCodecov: false, + shouldRunGitVersion: true); BuildParameters.PrintParameters(Context); From b981d902621c0813b362a8c286dcb493aae1c8e3 Mon Sep 17 00:00:00 2001 From: "D. Domig" Date: Tue, 1 Oct 2019 21:22:33 +0200 Subject: [PATCH 04/18] Fix typo --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8417d5d26..a8574d888 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ This addin for the Cake build automation system allows you to read issues from any code analyzer or linter. For more information about this addin see the [Cake.Issues website](https://cakeissues.net) -and for general information about the Cake build automation system see the [Cake website](http://cakebuild.net) +and for general information about the Cake build automation system see the [Cake website](http://cakebuild.net). [![License](http://img.shields.io/:license-mit-blue.svg)](https://github.com/cake-contrib/Cake.Issues/blob/develop/LICENSE) @@ -31,7 +31,7 @@ and for general information about the Cake build automation system see the [Cake ## Chat Room -Come join in the conversation about this addin in our Gitter Chat Room +Come join in the conversation about this addin in our Gitter Chat Room. [![Join the chat at https://gitter.im/cake-contrib/Lobby](https://badges.gitter.im/cake-contrib/Lobby.svg)](https://gitter.im/cake-contrib/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) From a21bcbc55f5b2937e069a7775135197cd049f692 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 3 Oct 2019 00:15:28 +0000 Subject: [PATCH 05/18] Bump Microsoft.CodeAnalysis.FxCopAnalyzers from 2.9.4 to 2.9.5 in /src Bumps [Microsoft.CodeAnalysis.FxCopAnalyzers](https://github.com/dotnet/roslyn-analyzers) from 2.9.4 to 2.9.5. - [Release notes](https://github.com/dotnet/roslyn-analyzers/releases) - [Changelog](https://github.com/dotnet/roslyn-analyzers/blob/master/PostReleaseActivities.md) - [Commits](https://github.com/dotnet/roslyn-analyzers/compare/v2.9.4...v2.9.5) Signed-off-by: dependabot-preview[bot] --- src/Cake.Issues.Testing/Cake.Issues.Testing.csproj | 2 +- src/Cake.Issues/Cake.Issues.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Cake.Issues.Testing/Cake.Issues.Testing.csproj b/src/Cake.Issues.Testing/Cake.Issues.Testing.csproj index d628f20db..9c9e2519d 100644 --- a/src/Cake.Issues.Testing/Cake.Issues.Testing.csproj +++ b/src/Cake.Issues.Testing/Cake.Issues.Testing.csproj @@ -26,7 +26,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Cake.Issues/Cake.Issues.csproj b/src/Cake.Issues/Cake.Issues.csproj index d4003de5f..b969cd720 100644 --- a/src/Cake.Issues/Cake.Issues.csproj +++ b/src/Cake.Issues/Cake.Issues.csproj @@ -24,7 +24,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive From d04e0f7959fbdc62ba406f68ef5481ec467f80b4 Mon Sep 17 00:00:00 2001 From: Pascal Berger Date: Mon, 30 Sep 2019 21:52:22 +0200 Subject: [PATCH 06/18] (GH-133) Add versioning to serialization format of issues --- 3rd-Party-License.md | 10 + recipe.cake | 7 +- .../IssueSerializationExtensionsTests.cs | 827 ----------- .../IssueDeserializationExtensionsTests.cs | 170 +++ .../IssueSerializationExtensionsTests.cs | 1319 +++++++++++++++++ .../SerializableIssueExtensionsTests.cs | 25 + src/Cake.Issues/Aliases.IssueSerialization.cs | 1 + src/Cake.Issues/Issue.cs | 2 +- .../IssueSerializationExtensions.cs | 226 --- .../IssueDeserializationExtensions.cs | 139 ++ .../IssueSerializationExtensions.cs | 97 ++ .../Serialization/LitJson/IJsonWrapper.cs | 66 + .../Serialization/LitJson/JsonData.cs | 1065 +++++++++++++ .../Serialization/LitJson/JsonException.cs | 71 + .../Serialization/LitJson/JsonMapper.cs | 967 ++++++++++++ .../Serialization/LitJson/JsonMockWrapper.cs | 111 ++ .../Serialization/LitJson/JsonReader.cs | 484 ++++++ .../Serialization/LitJson/JsonWriter.cs | 479 ++++++ .../Serialization/LitJson/Lexer.cs | 918 ++++++++++++ .../Serialization/LitJson/ParserToken.cs | 49 + .../{ => Serialization}/SerializableIssue.cs | 16 +- .../SerializableIssueExtensions.cs | 39 + 22 files changed, 6018 insertions(+), 1070 deletions(-) create mode 100644 3rd-Party-License.md delete mode 100644 src/Cake.Issues.Tests/IssueSerializationExtensionsTests.cs create mode 100644 src/Cake.Issues.Tests/Serialization/IssueDeserializationExtensionsTests.cs create mode 100644 src/Cake.Issues.Tests/Serialization/IssueSerializationExtensionsTests.cs create mode 100644 src/Cake.Issues.Tests/Serialization/SerializableIssueExtensionsTests.cs delete mode 100644 src/Cake.Issues/IssueSerializationExtensions.cs create mode 100644 src/Cake.Issues/Serialization/IssueDeserializationExtensions.cs create mode 100644 src/Cake.Issues/Serialization/IssueSerializationExtensions.cs create mode 100644 src/Cake.Issues/Serialization/LitJson/IJsonWrapper.cs create mode 100644 src/Cake.Issues/Serialization/LitJson/JsonData.cs create mode 100644 src/Cake.Issues/Serialization/LitJson/JsonException.cs create mode 100644 src/Cake.Issues/Serialization/LitJson/JsonMapper.cs create mode 100644 src/Cake.Issues/Serialization/LitJson/JsonMockWrapper.cs create mode 100644 src/Cake.Issues/Serialization/LitJson/JsonReader.cs create mode 100644 src/Cake.Issues/Serialization/LitJson/JsonWriter.cs create mode 100644 src/Cake.Issues/Serialization/LitJson/Lexer.cs create mode 100644 src/Cake.Issues/Serialization/LitJson/ParserToken.cs rename src/Cake.Issues/{ => Serialization}/SerializableIssue.cs (80%) create mode 100644 src/Cake.Issues/Serialization/SerializableIssueExtensions.cs diff --git a/3rd-Party-License.md b/3rd-Party-License.md new file mode 100644 index 000000000..9487ddc3d --- /dev/null +++ b/3rd-Party-License.md @@ -0,0 +1,10 @@ +# License overview of included 3rd party code + +Cake.Issues is licensed under the terms of the [MIT License](LICENSE). + +Cake.Issues includes third-party code which is licensed under its own respective license. + +## LitJSON + +License: Unlicense +https://github.com/LitJSON/litjson/blob/develop/COPYING diff --git a/recipe.cake b/recipe.cake index e772d580a..f2f01cac9 100644 --- a/recipe.cake +++ b/recipe.cake @@ -17,7 +17,12 @@ BuildParameters.PrintParameters(Context); ToolSettings.SetToolSettings( context: Context, - dupFinderExcludePattern: new string[] { BuildParameters.RootDirectoryPath + "/src/Cake.Issues.Tests/*.cs", BuildParameters.RootDirectoryPath + "/src/Cake.Issues*/**/*.AssemblyInfo.cs" }, + dupFinderExcludePattern: new string[] + { + BuildParameters.RootDirectoryPath + "/src/Cake.Issues*/**/*.AssemblyInfo.cs", + BuildParameters.RootDirectoryPath + "/src/Cake.Issues*/Serialization/LitJson/*.cs", + BuildParameters.RootDirectoryPath + "/src/Cake.Issues.Tests/**/*.cs" + }, testCoverageFilter: "+[*]* -[xunit.*]* -[Cake.Core]* -[Cake.Testing]* -[*.Tests]* -[Shouldly]*", testCoverageExcludeByAttribute: "*.ExcludeFromCodeCoverage*", testCoverageExcludeByFile: "*/*Designer.cs;*/*.g.cs;*/*.g.i.cs"); diff --git a/src/Cake.Issues.Tests/IssueSerializationExtensionsTests.cs b/src/Cake.Issues.Tests/IssueSerializationExtensionsTests.cs deleted file mode 100644 index dd18d166d..000000000 --- a/src/Cake.Issues.Tests/IssueSerializationExtensionsTests.cs +++ /dev/null @@ -1,827 +0,0 @@ -namespace Cake.Issues.Tests -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Cake.Core.IO; - using Cake.Issues.Testing; - using Shouldly; - using Xunit; - - public sealed class IssueSerializationExtensionsTests - { - public sealed class TheSerializeToJsonStringExtensionForASingleIssue - { - [Fact] - public void Should_Throw_If_Issue_Is_Null() - { - // Given - IIssue issue = null; - - // When - var result = Record.Exception(() => issue.SerializeToJsonString()); - - // Then - result.IsArgumentNullException("issue"); - } - - [Fact] - public void Should_Give_Correct_Result_For_Message_After_Roundtrip() - { - // Given - var message = "message"; - var issue = - IssueBuilder - .NewIssue(message, "providerType", "providerName") - .Create(); - - // When - var result = issue.SerializeToJsonString().DeserializeToIssue(); - - // Then - result.Message.ShouldBe(message); - } - - [Fact] - public void Should_Give_Correct_Result_For_ProviderType_After_Roundtrip() - { - // Given - var providerType = "providerType"; - var issue = - IssueBuilder - .NewIssue("message", providerType, "providerName") - .Create(); - - // When - var result = issue.SerializeToJsonString().DeserializeToIssue(); - - // Then - result.ProviderType.ShouldBe(providerType); - } - - [Fact] - public void Should_Give_Correct_Result_For_ProviderName_After_Roundtrip() - { - // Given - var providerName = "providerName"; - var issue = - IssueBuilder - .NewIssue("message", "providerType", providerName) - .Create(); - - // When - var result = issue.SerializeToJsonString().DeserializeToIssue(); - - // Then - result.ProviderName.ShouldBe(providerName); - } - - [Fact] - public void Should_Give_Correct_Result_For_ProjectFileRelativePath_After_Roundtrip() - { - // Given - var projectFileRelativePath = @"src/myproj.file"; - var issue = - IssueBuilder - .NewIssue("message", "providerType", "providerName") - .InProjectFile(projectFileRelativePath) - .Create(); - - // When - var result = issue.SerializeToJsonString().DeserializeToIssue(); - - // Then - result.ProjectFileRelativePath.FullPath.ShouldBe(projectFileRelativePath); - } - - [Fact] - public void Should_Give_Correct_Result_For_ProjectName_After_Roundtrip() - { - // Given - var projectName = "projectName"; - var issue = - IssueBuilder - .NewIssue("message", "providerType", "providerName") - .InProjectOfName(projectName) - .Create(); - - // When - var result = issue.SerializeToJsonString().DeserializeToIssue(); - - // Then - result.ProjectName.ShouldBe(projectName); - } - - [Fact] - public void Should_Give_Correct_Result_For_AffectedFileRelativePath_After_Roundtrip() - { - // Given - var affectedFileRelativePath = @"src/foo.bar"; - var issue = - IssueBuilder - .NewIssue("message", "providerType", "providerName") - .InFile(affectedFileRelativePath) - .Create(); - - // When - var result = issue.SerializeToJsonString().DeserializeToIssue(); - - // Then - result.AffectedFileRelativePath.FullPath.ShouldBe(affectedFileRelativePath); - } - - [Fact] - public void Should_Give_Correct_Result_For_Line_After_Roundtrip() - { - // Given - var line = 42; - var issue = - IssueBuilder - .NewIssue("message", "providerType", "providerName") - .InFile(@"src/foo.bar", line) - .Create(); - - // When - var result = issue.SerializeToJsonString().DeserializeToIssue(); - - // Then - result.Line.ShouldBe(line); - } - - [Fact] - public void Should_Give_Correct_Result_For_Priority_After_Roundtrip() - { - // Given - var priority = 42; - var issue = - IssueBuilder - .NewIssue("message", "providerType", "providerName") - .WithPriority(priority, "priorityName") - .Create(); - - // When - var result = issue.SerializeToJsonString().DeserializeToIssue(); - - // Then - result.Priority.ShouldBe(priority); - } - - [Fact] - public void Should_Give_Correct_Result_For_PriorityName_After_Roundtrip() - { - // Given - var priorityName = "priorityName"; - var issue = - IssueBuilder - .NewIssue("message", "providerType", "providerName") - .WithPriority(42, priorityName) - .Create(); - - // When - var result = issue.SerializeToJsonString().DeserializeToIssue(); - - // Then - result.PriorityName.ShouldBe(priorityName); - } - - [Fact] - public void Should_Give_Correct_Result_For_Rule_After_Roundtrip() - { - // Given - var rule = "rule"; - var issue = - IssueBuilder - .NewIssue("message", "providerType", "providerName") - .OfRule(rule) - .Create(); - - // When - var result = issue.SerializeToJsonString().DeserializeToIssue(); - - // Then - result.Rule.ShouldBe(rule); - } - - [Fact] - public void Should_Give_Correct_Result_For_RuleUrl_After_Roundtrip() - { - // Given - var ruleUrl = new Uri("https://rule.url"); - var issue = - IssueBuilder - .NewIssue("message", "providerType", "providerName") - .OfRule("rule", ruleUrl) - .Create(); - - // When - var result = issue.SerializeToJsonString().DeserializeToIssue(); - - // Then - result.RuleUrl.ToString().ShouldBe(ruleUrl.ToString()); - } - } - - public sealed class TheSerializeToJsonStringExtensionForAnEnumerableOfIssues - { - [Fact] - public void Should_Throw_If_Issue_Is_Null() - { - // Given - IEnumerable issues = null; - - // When - var result = Record.Exception(() => issues.SerializeToJsonString()); - - // Then - result.IsArgumentNullException("issues"); - } - - [Fact] - public void Should_Serialize_Issues() - { - // Given - var issues = - new List - { - IssueBuilder - .NewIssue("message1", "providerType1", "providerName1") - .Create(), - IssueBuilder - .NewIssue("message2", "providerType2", "providerName2") - .Create(), - }; - - // When - var result = issues.SerializeToJsonString(); - - // Then - result.ShouldNotBeNull(); - } - } - - public sealed class TheSerializeToJsonFileExtensionForASingleIssue - { - [Fact] - public void Should_Throw_If_Issue_Is_Null() - { - // Given - IIssue issue = null; - var filePath = @"c:\issue.json"; - - // When - var result = Record.Exception(() => issue.SerializeToJsonFile(filePath)); - - // Then - result.IsArgumentNullException("issue"); - } - - [Fact] - public void Should_Throw_If_FilePath_Is_Null() - { - // Given - var issue = - IssueBuilder - .NewIssue("message", "providerType", "providerName") - .Create(); - FilePath filePath = null; - - // When - var result = Record.Exception(() => issue.SerializeToJsonFile(filePath)); - - // Then - result.IsArgumentNullException("filePath"); - } - - [Fact] - public void Should_Give_Correct_Result_For_Message_After_Roundtrip() - { - // Given - var message = "message"; - var issue = - IssueBuilder - .NewIssue(message, "providerType", "providerName") - .Create(); - var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); - - try - { - // When - issue.SerializeToJsonFile(filePath); - var result = filePath.DeserializeToIssue(); - - // Then - result.Message.ShouldBe(message); - } - finally - { - System.IO.File.Delete(filePath.FullPath); - } - } - - [Fact] - public void Should_Give_Correct_Result_For_ProviderType_After_Roundtrip() - { - // Given - var providerType = "providerType"; - var issue = - IssueBuilder - .NewIssue("message", providerType, "providerName") - .Create(); - var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); - - try - { - // When - issue.SerializeToJsonFile(filePath); - var result = filePath.DeserializeToIssue(); - - // Then - result.ProviderType.ShouldBe(providerType); - } - finally - { - System.IO.File.Delete(filePath.FullPath); - } - } - - [Fact] - public void Should_Give_Correct_Result_For_ProviderName_After_Roundtrip() - { - // Given - var providerName = "providerName"; - var issue = - IssueBuilder - .NewIssue("message", "providerType", providerName) - .Create(); - var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); - - try - { - // When - issue.SerializeToJsonFile(filePath); - var result = filePath.DeserializeToIssue(); - - // Then - result.ProviderName.ShouldBe(providerName); - } - finally - { - System.IO.File.Delete(filePath.FullPath); - } - } - - [Fact] - public void Should_Give_Correct_Result_For_ProjectFileRelativePath_After_Roundtrip() - { - // Given - var projectFileRelativePath = @"src/myproj.file"; - var issue = - IssueBuilder - .NewIssue("message", "providerType", "providerName") - .InProjectFile(projectFileRelativePath) - .Create(); - var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); - - try - { - // When - issue.SerializeToJsonFile(filePath); - var result = filePath.DeserializeToIssue(); - - // Then - result.ProjectFileRelativePath.FullPath.ShouldBe(projectFileRelativePath); - } - finally - { - System.IO.File.Delete(filePath.FullPath); - } - } - - [Fact] - public void Should_Give_Correct_Result_For_ProjectName_After_Roundtrip() - { - // Given - var projectName = "projectName"; - var issue = - IssueBuilder - .NewIssue("message", "providerType", "providerName") - .InProjectOfName(projectName) - .Create(); - var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); - - try - { - // When - issue.SerializeToJsonFile(filePath); - var result = filePath.DeserializeToIssue(); - - // Then - result.ProjectName.ShouldBe(projectName); - } - finally - { - System.IO.File.Delete(filePath.FullPath); - } - } - - [Fact] - public void Should_Give_Correct_Result_For_AffectedFileRelativePath_After_Roundtrip() - { - // Given - var affectedFileRelativePath = @"src/foo.bar"; - var issue = - IssueBuilder - .NewIssue("message", "providerType", "providerName") - .InFile(affectedFileRelativePath) - .Create(); - var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); - - try - { - // When - issue.SerializeToJsonFile(filePath); - var result = filePath.DeserializeToIssue(); - - // Then - result.AffectedFileRelativePath.FullPath.ShouldBe(affectedFileRelativePath); - } - finally - { - System.IO.File.Delete(filePath.FullPath); - } - } - - [Fact] - public void Should_Give_Correct_Result_For_Line_After_Roundtrip() - { - // Given - var line = 42; - var issue = - IssueBuilder - .NewIssue("message", "providerType", "providerName") - .InFile(@"src/foo.bar", line) - .Create(); - var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); - - try - { - // When - issue.SerializeToJsonFile(filePath); - var result = filePath.DeserializeToIssue(); - - // Then - result.Line.ShouldBe(line); - } - finally - { - System.IO.File.Delete(filePath.FullPath); - } - } - - [Fact] - public void Should_Give_Correct_Result_For_Priority_After_Roundtrip() - { - // Given - var priority = 42; - var issue = - IssueBuilder - .NewIssue("message", "providerType", "providerName") - .WithPriority(priority, "priorityName") - .Create(); - var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); - - try - { - // When - issue.SerializeToJsonFile(filePath); - var result = filePath.DeserializeToIssue(); - - // Then - result.Priority.ShouldBe(priority); - } - finally - { - System.IO.File.Delete(filePath.FullPath); - } - } - - [Fact] - public void Should_Give_Correct_Result_For_PriorityName_After_Roundtrip() - { - // Given - var priorityName = "priorityName"; - var issue = - IssueBuilder - .NewIssue("message", "providerType", "providerName") - .WithPriority(42, priorityName) - .Create(); - var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); - - try - { - // When - issue.SerializeToJsonFile(filePath); - var result = filePath.DeserializeToIssue(); - - // Then - result.PriorityName.ShouldBe(priorityName); - } - finally - { - System.IO.File.Delete(filePath.FullPath); - } - } - - [Fact] - public void Should_Give_Correct_Result_For_Rule_After_Roundtrip() - { - // Given - var rule = "rule"; - var issue = - IssueBuilder - .NewIssue("message", "providerType", "providerName") - .OfRule(rule) - .Create(); - var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); - - try - { - // When - issue.SerializeToJsonFile(filePath); - var result = filePath.DeserializeToIssue(); - - // Then - result.Rule.ShouldBe(rule); - } - finally - { - System.IO.File.Delete(filePath.FullPath); - } - } - - [Fact] - public void Should_Give_Correct_Result_For_RuleUrl_After_Roundtrip() - { - // Given - var ruleUrl = new Uri("https://rule.url"); - var issue = - IssueBuilder - .NewIssue("message", "providerType", "providerName") - .OfRule("rule", ruleUrl) - .Create(); - var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); - - try - { - // When - issue.SerializeToJsonFile(filePath); - var result = filePath.DeserializeToIssue(); - - // Then - result.RuleUrl.ToString().ShouldBe(ruleUrl.ToString()); - } - finally - { - System.IO.File.Delete(filePath.FullPath); - } - } - } - - public sealed class TheSerializeToJsonFileExtensionForAnEnumerableOfIssues - { - [Fact] - public void Should_Throw_If_Issue_Is_Null() - { - // Given - IEnumerable issues = null; - var filePath = @"c:\issues.json"; - - // When - var result = Record.Exception(() => issues.SerializeToJsonFile(filePath)); - - // Then - result.IsArgumentNullException("issues"); - } - - [Fact] - public void Should_Throw_If_FilePath_Is_Null() - { - // Given - var issues = new List(); - FilePath filePath = null; - - // When - var result = Record.Exception(() => issues.SerializeToJsonFile(filePath)); - - // Then - result.IsArgumentNullException("filePath"); - } - - [Fact] - public void Should_Serialize_Issues() - { - // Given - var issues = - new List - { - IssueBuilder - .NewIssue("message1", "providerType1", "providerName1") - .Create(), - IssueBuilder - .NewIssue("message2", "providerType2", "providerName2") - .Create(), - }; - var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); - - try - { - // When - issues.SerializeToJsonFile(filePath); - - // Then - System.IO.File.Exists(filePath.FullPath).ShouldBeTrue(); - } - finally - { - System.IO.File.Delete(filePath.FullPath); - } - } - } - - public sealed class TheDeserializeToIssueExtensionForAJsonString - { - [Fact] - public void Should_Throw_If_JsonString_Is_Null() - { - // Given - string jsonString = null; - - // When - var result = Record.Exception(() => jsonString.DeserializeToIssue()); - - // Then - result.IsArgumentNullException("jsonString"); - } - } - - public sealed class TheDeserializeToIssuesExtensionForAJsonString - { - [Fact] - public void Should_Throw_If_JsonString_Is_Null() - { - // Given - string jsonString = null; - - // When - var result = Record.Exception(() => jsonString.DeserializeToIssues()); - - // Then - result.IsArgumentNullException("jsonString"); - } - - [Fact] - public void Should_Return_An_Empty_List_For_An_Empty_Array() - { - // Given - string jsonString = "[]"; - - // When - var result = jsonString.DeserializeToIssues(); - - // Then - result.ShouldBeEmpty(); - } - } - - public sealed class TheDeserializeToIssueExtensionForAJsonFile - { - [Fact] - public void Should_Throw_If_FilePath_Is_Null() - { - // Given - FilePath filePath = null; - - // When - var result = Record.Exception(() => filePath.DeserializeToIssue()); - - // Then - result.IsArgumentNullException("filePath"); - } - - [Fact] - public void Should_Return_Issue() - { - // Given - var filePath = new FilePath("Testfiles/issue.json"); - - // When - var result = filePath.DeserializeToIssue(); - - // Then - IssueChecker.Check( - result, - IssueBuilder.NewIssue( - "Something went wrong.", - "TestProvider", - "Test Provider") - .InProject(@"src\Foo\Bar.csproj", "Bar") - .InFile(@"src\Foo\Bar.cs", 42) - .OfRule("Rule", new Uri("https://google.com")) - .WithPriority(IssuePriority.Warning)); - } - } - - public sealed class TheDeserializeToIssuesExtensionForAJsonFile - { - [Fact] - public void Should_Throw_If_FilePath_Is_Null() - { - // Given - FilePath filePath = null; - - // When - var result = Record.Exception(() => filePath.DeserializeToIssues()); - - // Then - result.IsArgumentNullException("filePath"); - } - - [Fact] - public void Should_Return_An_Empty_List_For_An_Empty_Array() - { - // Given - var filePath = new FilePath("Testfiles/empty-array.json"); - - // When - var result = filePath.DeserializeToIssues(); - - // Then - result.ShouldBeEmpty(); - } - - [Fact] - public void Should_Return_List_Of_Issues() - { - // Given - var filePath = new FilePath("Testfiles/issues.json"); - - // When - var result = filePath.DeserializeToIssues().ToList(); - - // Then - result.Count.ShouldBe(2); - IssueChecker.Check( - result[0], - IssueBuilder.NewIssue( - "Something went wrong.", - "TestProvider", - "Test Provider") - .InProject(@"src\Foo\Bar.csproj", "Bar") - .InFile(@"src\Foo\Bar.cs", 42) - .OfRule("Rule", new Uri("https://google.com")) - .WithPriority(IssuePriority.Warning)); - IssueChecker.Check( - result[1], - IssueBuilder.NewIssue( - "Something went wrong again.", - "TestProvider", - "Test Provider") - .InProject(@"src\Foo\Bar.csproj", "Bar") - .InFile(@"src\Foo\Bar2.cs") - .WithPriority(IssuePriority.Warning)); - } - } - - public sealed class TheToSerializableIssueExtension - { - [Fact] - public void Should_Throw_If_Issue_Is_Null() - { - // Given - IIssue issue = null; - - // When - var result = Record.Exception(() => issue.ToSerializableIssue()); - - // Then - result.IsArgumentNullException("issue"); - } - } - - public sealed class TheToIssueExtension - { - [Fact] - public void Should_Throw_If_SerializableIssue_Is_Null() - { - // Given - SerializableIssue serializableIssue = null; - - // When - var result = Record.Exception(() => serializableIssue.ToIssue()); - - // Then - result.IsArgumentNullException("serializableIssue"); - } - } - } -} \ No newline at end of file diff --git a/src/Cake.Issues.Tests/Serialization/IssueDeserializationExtensionsTests.cs b/src/Cake.Issues.Tests/Serialization/IssueDeserializationExtensionsTests.cs new file mode 100644 index 000000000..2d8d5086b --- /dev/null +++ b/src/Cake.Issues.Tests/Serialization/IssueDeserializationExtensionsTests.cs @@ -0,0 +1,170 @@ +namespace Cake.Issues.Tests.Serialization +{ + using System; + using System.Linq; + using Cake.Core.IO; + using Cake.Issues.Serialization; + using Cake.Issues.Testing; + using Shouldly; + using Xunit; + + public sealed class IssueDeserializationExtensionsTests + { + public sealed class TheDeserializeToIssueExtensionForAJsonString + { + [Fact] + public void Should_Throw_If_JsonString_Is_Null() + { + // Given + string jsonString = null; + + // When + var result = Record.Exception(() => jsonString.DeserializeToIssue()); + + // Then + result.IsArgumentNullException("jsonString"); + } + + [Fact] + public void Should_Throw_If_JsonString_Has_Unknown_Version() + { + // Given + var jsonString = "{\"Version\": -1}"; + + // When + var result = Record.Exception(() => jsonString.DeserializeToIssue()); + + // Then + result.Message.ShouldBe("Not supported issue serialization format -1"); + } + } + + public sealed class TheDeserializeToIssuesExtensionForAJsonString + { + [Fact] + public void Should_Throw_If_JsonString_Is_Null() + { + // Given + string jsonString = null; + + // When + var result = Record.Exception(() => jsonString.DeserializeToIssues()); + + // Then + result.IsArgumentNullException("jsonString"); + } + + [Fact] + public void Should_Return_An_Empty_List_For_An_Empty_Array() + { + // Given + string jsonString = "[]"; + + // When + var result = jsonString.DeserializeToIssues(); + + // Then + result.ShouldBeEmpty(); + } + } + + public sealed class TheDeserializeToIssueExtensionForAJsonFile + { + [Fact] + public void Should_Throw_If_FilePath_Is_Null() + { + // Given + FilePath filePath = null; + + // When + var result = Record.Exception(() => filePath.DeserializeToIssue()); + + // Then + result.IsArgumentNullException("filePath"); + } + + [Fact] + public void Should_Return_Issue() + { + // Given + var filePath = new FilePath("Testfiles/issue.json"); + + // When + var result = filePath.DeserializeToIssue(); + + // Then + IssueChecker.Check( + result, + IssueBuilder.NewIssue( + "Something went wrong.", + "TestProvider", + "Test Provider") + .InProject(@"src\Foo\Bar.csproj", "Bar") + .InFile(@"src\Foo\Bar.cs", 42) + .OfRule("Rule", new Uri("https://google.com")) + .WithPriority(IssuePriority.Warning)); + } + } + + public sealed class TheDeserializeToIssuesExtensionForAJsonFile + { + [Fact] + public void Should_Throw_If_FilePath_Is_Null() + { + // Given + FilePath filePath = null; + + // When + var result = Record.Exception(() => filePath.DeserializeToIssues()); + + // Then + result.IsArgumentNullException("filePath"); + } + + [Fact] + public void Should_Return_An_Empty_List_For_An_Empty_Array() + { + // Given + var filePath = new FilePath("Testfiles/empty-array.json"); + + // When + var result = filePath.DeserializeToIssues(); + + // Then + result.ShouldBeEmpty(); + } + + [Fact] + public void Should_Return_List_Of_Issues() + { + // Given + var filePath = new FilePath("Testfiles/issues.json"); + + // When + var result = filePath.DeserializeToIssues().ToList(); + + // Then + result.Count.ShouldBe(2); + IssueChecker.Check( + result[0], + IssueBuilder.NewIssue( + "Something went wrong.", + "TestProvider", + "Test Provider") + .InProject(@"src\Foo\Bar.csproj", "Bar") + .InFile(@"src\Foo\Bar.cs", 42) + .OfRule("Rule", new Uri("https://google.com")) + .WithPriority(IssuePriority.Warning)); + IssueChecker.Check( + result[1], + IssueBuilder.NewIssue( + "Something went wrong again.", + "TestProvider", + "Test Provider") + .InProject(@"src\Foo\Bar.csproj", "Bar") + .InFile(@"src\Foo\Bar2.cs") + .WithPriority(IssuePriority.Warning)); + } + } + } +} \ No newline at end of file diff --git a/src/Cake.Issues.Tests/Serialization/IssueSerializationExtensionsTests.cs b/src/Cake.Issues.Tests/Serialization/IssueSerializationExtensionsTests.cs new file mode 100644 index 000000000..5dc8a4f3a --- /dev/null +++ b/src/Cake.Issues.Tests/Serialization/IssueSerializationExtensionsTests.cs @@ -0,0 +1,1319 @@ +namespace Cake.Issues.Tests.Serialization +{ + using System; + using System.Collections.Generic; + using System.Linq; + using Cake.Core.IO; + using Cake.Issues.Serialization; + using Cake.Issues.Testing; + using Shouldly; + using Xunit; + + public sealed class IssueSerializationExtensionsTests + { + public sealed class TheSerializeToJsonStringExtensionForASingleIssue + { + [Fact] + public void Should_Throw_If_Issue_Is_Null() + { + // Given + IIssue issue = null; + + // When + var result = Record.Exception(() => issue.SerializeToJsonString()); + + // Then + result.IsArgumentNullException("issue"); + } + + [Fact] + public void Should_Give_Correct_Result_For_Message_After_Roundtrip() + { + // Given + var message = "message"; + var issue = + IssueBuilder + .NewIssue(message, "providerType", "providerName") + .Create(); + + // When + var result = issue.SerializeToJsonString().DeserializeToIssue(); + + // Then + result.Message.ShouldBe(message); + } + + [Fact] + public void Should_Give_Correct_Result_For_ProviderType_After_Roundtrip() + { + // Given + var providerType = "providerType"; + var issue = + IssueBuilder + .NewIssue("message", providerType, "providerName") + .Create(); + + // When + var result = issue.SerializeToJsonString().DeserializeToIssue(); + + // Then + result.ProviderType.ShouldBe(providerType); + } + + [Fact] + public void Should_Give_Correct_Result_For_ProviderName_After_Roundtrip() + { + // Given + var providerName = "providerName"; + var issue = + IssueBuilder + .NewIssue("message", "providerType", providerName) + .Create(); + + // When + var result = issue.SerializeToJsonString().DeserializeToIssue(); + + // Then + result.ProviderName.ShouldBe(providerName); + } + + [Fact] + public void Should_Give_Correct_Result_For_ProjectFileRelativePath_After_Roundtrip() + { + // Given + var projectFileRelativePath = @"src/myproj.file"; + var issue = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InProjectFile(projectFileRelativePath) + .Create(); + + // When + var result = issue.SerializeToJsonString().DeserializeToIssue(); + + // Then + result.ProjectFileRelativePath.FullPath.ShouldBe(projectFileRelativePath); + } + + [Fact] + public void Should_Give_Correct_Result_For_ProjectName_After_Roundtrip() + { + // Given + var projectName = "projectName"; + var issue = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InProjectOfName(projectName) + .Create(); + + // When + var result = issue.SerializeToJsonString().DeserializeToIssue(); + + // Then + result.ProjectName.ShouldBe(projectName); + } + + [Fact] + public void Should_Give_Correct_Result_For_AffectedFileRelativePath_After_Roundtrip() + { + // Given + var affectedFileRelativePath = @"src/foo.bar"; + var issue = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InFile(affectedFileRelativePath) + .Create(); + + // When + var result = issue.SerializeToJsonString().DeserializeToIssue(); + + // Then + result.AffectedFileRelativePath.FullPath.ShouldBe(affectedFileRelativePath); + } + + [Fact] + public void Should_Give_Correct_Result_For_Line_After_Roundtrip() + { + // Given + var line = 42; + var issue = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InFile(@"src/foo.bar", line) + .Create(); + + // When + var result = issue.SerializeToJsonString().DeserializeToIssue(); + + // Then + result.Line.ShouldBe(line); + } + + [Fact] + public void Should_Give_Correct_Result_For_Priority_After_Roundtrip() + { + // Given + var priority = 42; + var issue = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .WithPriority(priority, "priorityName") + .Create(); + + // When + var result = issue.SerializeToJsonString().DeserializeToIssue(); + + // Then + result.Priority.ShouldBe(priority); + } + + [Fact] + public void Should_Give_Correct_Result_For_PriorityName_After_Roundtrip() + { + // Given + var priorityName = "priorityName"; + var issue = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .WithPriority(42, priorityName) + .Create(); + + // When + var result = issue.SerializeToJsonString().DeserializeToIssue(); + + // Then + result.PriorityName.ShouldBe(priorityName); + } + + [Fact] + public void Should_Give_Correct_Result_For_Rule_After_Roundtrip() + { + // Given + var rule = "rule"; + var issue = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .OfRule(rule) + .Create(); + + // When + var result = issue.SerializeToJsonString().DeserializeToIssue(); + + // Then + result.Rule.ShouldBe(rule); + } + + [Fact] + public void Should_Give_Correct_Result_For_RuleUrl_After_Roundtrip() + { + // Given + var ruleUrl = new Uri("https://rule.url"); + var issue = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .OfRule("rule", ruleUrl) + .Create(); + + // When + var result = issue.SerializeToJsonString().DeserializeToIssue(); + + // Then + result.RuleUrl.ToString().ShouldBe(ruleUrl.ToString()); + } + } + + public sealed class TheSerializeToJsonStringExtensionForAnEnumerableOfIssues + { + [Fact] + public void Should_Throw_If_Issue_Is_Null() + { + // Given + IEnumerable issues = null; + + // When + var result = Record.Exception(() => issues.SerializeToJsonString()); + + // Then + result.IsArgumentNullException("issues"); + } + + [Fact] + public void Should_Give_Correct_Result_For_Message_After_Roundtrip() + { + // Given + var message1 = "message1"; + var message2 = "message2"; + var issues = + new List + { + IssueBuilder + .NewIssue(message1, "providerType1", "providerName1") + .Create(), + IssueBuilder + .NewIssue(message2, "providerType2", "providerName2") + .Create(), + }; + + // When + var result = issues.SerializeToJsonString().DeserializeToIssues(); + + // Then + result.Count().ShouldBe(2); + result.First().Message.ShouldBe(message1); + result.Last().Message.ShouldBe(message2); + } + + [Fact] + public void Should_Give_Correct_Result_For_ProviderType_After_Roundtrip() + { + // Given + var providerType1 = "providerType1"; + var providerType2 = "providerType2"; + var issues = + new List + { + IssueBuilder + .NewIssue("message1", providerType1, "providerName1") + .Create(), + IssueBuilder + .NewIssue("message2", providerType2, "providerName2") + .Create(), + }; + + // When + var result = issues.SerializeToJsonString().DeserializeToIssues(); + + // Then + result.Count().ShouldBe(2); + result.First().ProviderType.ShouldBe(providerType1); + result.Last().ProviderType.ShouldBe(providerType2); + } + + [Fact] + public void Should_Give_Correct_Result_For_ProviderName_After_Roundtrip() + { + // Given + var providerName1 = "providerName1"; + var providerName2 = "providerName2"; + var issues = + new List + { + IssueBuilder + .NewIssue("message1", "providerType1", providerName1) + .Create(), + IssueBuilder + .NewIssue("message2", "providerType2", providerName2) + .Create(), + }; + + // When + var result = issues.SerializeToJsonString().DeserializeToIssues(); + + // Then + result.Count().ShouldBe(2); + result.First().ProviderName.ShouldBe(providerName1); + result.Last().ProviderName.ShouldBe(providerName2); + } + + [Fact] + public void Should_Give_Correct_Result_For_ProjectFileRelativePath_After_Roundtrip() + { + // Given + var projectFileRelativePath1 = @"src/myproj1.file"; + var projectFileRelativePath2 = @"src/myproj2.file"; + var issues = + new List + { + IssueBuilder + .NewIssue("message1", "providerType1", "providerName1") + .InProjectFile(projectFileRelativePath1) + .Create(), + IssueBuilder + .NewIssue("message2", "providerType2", "providerName2") + .InProjectFile(projectFileRelativePath2) + .Create(), + }; + + // When + var result = issues.SerializeToJsonString().DeserializeToIssues(); + + // Then + result.Count().ShouldBe(2); + result.First().ProjectFileRelativePath.FullPath.ShouldBe(projectFileRelativePath1); + result.Last().ProjectFileRelativePath.FullPath.ShouldBe(projectFileRelativePath2); + } + + [Fact] + public void Should_Give_Correct_Result_For_ProjectName_After_Roundtrip() + { + // Given + var projectName1 = "projectName1"; + var projectName2 = "projectName2"; + var issues = + new List + { + IssueBuilder + .NewIssue("message1", "providerType1", "providerName1") + .InProjectOfName(projectName1) + .Create(), + IssueBuilder + .NewIssue("message2", "providerType2", "providerName2") + .InProjectOfName(projectName2) + .Create(), + }; + + // When + var result = issues.SerializeToJsonString().DeserializeToIssues(); + + // Then + result.Count().ShouldBe(2); + result.First().ProjectName.ShouldBe(projectName1); + result.Last().ProjectName.ShouldBe(projectName2); + } + + [Fact] + public void Should_Give_Correct_Result_For_AffectedFileRelativePath_After_Roundtrip() + { + // Given + var affectedFileRelativePath1 = @"src/foo1.bar"; + var affectedFileRelativePath2 = @"src/foo2.bar"; + var issues = + new List + { + IssueBuilder + .NewIssue("message1", "providerType1", "providerName1") + .InFile(affectedFileRelativePath1) + .Create(), + IssueBuilder + .NewIssue("message2", "providerType2", "providerName2") + .InFile(affectedFileRelativePath2) + .Create(), + }; + + // When + var result = issues.SerializeToJsonString().DeserializeToIssues(); + + // Then + result.Count().ShouldBe(2); + result.First().AffectedFileRelativePath.FullPath.ShouldBe(affectedFileRelativePath1); + result.Last().AffectedFileRelativePath.FullPath.ShouldBe(affectedFileRelativePath2); + } + + [Fact] + public void Should_Give_Correct_Result_For_Line_After_Roundtrip() + { + // Given + var line1 = 23; + var line2 = 42; + var issues = + new List + { + IssueBuilder + .NewIssue("message1", "providerType1", "providerName1") + .InFile(@"src/foo.bar", line1) + .Create(), + IssueBuilder + .NewIssue("message2", "providerType2", "providerName2") + .InFile(@"src/foo.bar", line2) + .Create(), + }; + + // When + var result = issues.SerializeToJsonString().DeserializeToIssues(); + + // Then + result.Count().ShouldBe(2); + result.First().Line.ShouldBe(line1); + result.Last().Line.ShouldBe(line2); + } + + [Fact] + public void Should_Give_Correct_Result_For_Priority_After_Roundtrip() + { + // Given + var priority1 = 23; + var priority2 = 42; + var issues = + new List + { + IssueBuilder + .NewIssue("message1", "providerType1", "providerName1") + .WithPriority(priority1, "priorityName") + .Create(), + IssueBuilder + .NewIssue("message2", "providerType2", "providerName2") + .WithPriority(priority2, "priorityName") + .Create(), + }; + + // When + var result = issues.SerializeToJsonString().DeserializeToIssues(); + + // Then + result.Count().ShouldBe(2); + result.First().Priority.ShouldBe(priority1); + result.Last().Priority.ShouldBe(priority2); + } + + [Fact] + public void Should_Give_Correct_Result_For_PriorityName_After_Roundtrip() + { + // Given + var priorityName1 = "priorityName1"; + var priorityName2 = "priorityName2"; + var issues = + new List + { + IssueBuilder + .NewIssue("message1", "providerType1", "providerName1") + .WithPriority(42, priorityName1) + .Create(), + IssueBuilder + .NewIssue("message2", "providerType2", "providerName2") + .WithPriority(42, priorityName2) + .Create(), + }; + + // When + var result = issues.SerializeToJsonString().DeserializeToIssues(); + + // Then + result.Count().ShouldBe(2); + result.First().PriorityName.ShouldBe(priorityName1); + result.Last().PriorityName.ShouldBe(priorityName2); + } + + [Fact] + public void Should_Give_Correct_Result_For_Rule_After_Roundtrip() + { + // Given + var rule1 = "rule1"; + var rule2 = "rule2"; + var issues = + new List + { + IssueBuilder + .NewIssue("message1", "providerType1", "providerName1") + .OfRule(rule1) + .Create(), + IssueBuilder + .NewIssue("message2", "providerType2", "providerName2") + .OfRule(rule2) + .Create(), + }; + + // When + var result = issues.SerializeToJsonString().DeserializeToIssues(); + + // Then + result.Count().ShouldBe(2); + result.First().Rule.ShouldBe(rule1); + result.Last().Rule.ShouldBe(rule2); + } + + [Fact] + public void Should_Give_Correct_Result_For_RuleUrl_After_Roundtrip() + { + // Given + var ruleUrl1 = new Uri("https://rule1.url"); + var ruleUrl2 = new Uri("https://rule2.url"); + var issues = + new List + { + IssueBuilder + .NewIssue("message1", "providerType1", "providerName1") + .OfRule("rule", ruleUrl1) + .Create(), + IssueBuilder + .NewIssue("message2", "providerType2", "providerName2") + .OfRule("rule", ruleUrl2) + .Create(), + }; + + // When + var result = issues.SerializeToJsonString().DeserializeToIssues(); + + // Then + result.Count().ShouldBe(2); + result.First().RuleUrl.ToString().ShouldBe(ruleUrl1.ToString()); + result.Last().RuleUrl.ToString().ShouldBe(ruleUrl2.ToString()); + } + } + + public sealed class TheSerializeToJsonFileExtensionForASingleIssue + { + [Fact] + public void Should_Throw_If_Issue_Is_Null() + { + // Given + IIssue issue = null; + var filePath = @"c:\issue.json"; + + // When + var result = Record.Exception(() => issue.SerializeToJsonFile(filePath)); + + // Then + result.IsArgumentNullException("issue"); + } + + [Fact] + public void Should_Throw_If_FilePath_Is_Null() + { + // Given + var issue = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .Create(); + FilePath filePath = null; + + // When + var result = Record.Exception(() => issue.SerializeToJsonFile(filePath)); + + // Then + result.IsArgumentNullException("filePath"); + } + + [Fact] + public void Should_Give_Correct_Result_For_Message_After_Roundtrip() + { + // Given + var message = "message"; + var issue = + IssueBuilder + .NewIssue(message, "providerType", "providerName") + .Create(); + var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); + + try + { + // When + issue.SerializeToJsonFile(filePath); + var result = filePath.DeserializeToIssue(); + + // Then + result.Message.ShouldBe(message); + } + finally + { + System.IO.File.Delete(filePath.FullPath); + } + } + + [Fact] + public void Should_Give_Correct_Result_For_ProviderType_After_Roundtrip() + { + // Given + var providerType = "providerType"; + var issue = + IssueBuilder + .NewIssue("message", providerType, "providerName") + .Create(); + var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); + + try + { + // When + issue.SerializeToJsonFile(filePath); + var result = filePath.DeserializeToIssue(); + + // Then + result.ProviderType.ShouldBe(providerType); + } + finally + { + System.IO.File.Delete(filePath.FullPath); + } + } + + [Fact] + public void Should_Give_Correct_Result_For_ProviderName_After_Roundtrip() + { + // Given + var providerName = "providerName"; + var issue = + IssueBuilder + .NewIssue("message", "providerType", providerName) + .Create(); + var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); + + try + { + // When + issue.SerializeToJsonFile(filePath); + var result = filePath.DeserializeToIssue(); + + // Then + result.ProviderName.ShouldBe(providerName); + } + finally + { + System.IO.File.Delete(filePath.FullPath); + } + } + + [Fact] + public void Should_Give_Correct_Result_For_ProjectFileRelativePath_After_Roundtrip() + { + // Given + var projectFileRelativePath = @"src/myproj.file"; + var issue = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InProjectFile(projectFileRelativePath) + .Create(); + var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); + + try + { + // When + issue.SerializeToJsonFile(filePath); + var result = filePath.DeserializeToIssue(); + + // Then + result.ProjectFileRelativePath.FullPath.ShouldBe(projectFileRelativePath); + } + finally + { + System.IO.File.Delete(filePath.FullPath); + } + } + + [Fact] + public void Should_Give_Correct_Result_For_ProjectName_After_Roundtrip() + { + // Given + var projectName = "projectName"; + var issue = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InProjectOfName(projectName) + .Create(); + var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); + + try + { + // When + issue.SerializeToJsonFile(filePath); + var result = filePath.DeserializeToIssue(); + + // Then + result.ProjectName.ShouldBe(projectName); + } + finally + { + System.IO.File.Delete(filePath.FullPath); + } + } + + [Fact] + public void Should_Give_Correct_Result_For_AffectedFileRelativePath_After_Roundtrip() + { + // Given + var affectedFileRelativePath = @"src/foo.bar"; + var issue = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InFile(affectedFileRelativePath) + .Create(); + var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); + + try + { + // When + issue.SerializeToJsonFile(filePath); + var result = filePath.DeserializeToIssue(); + + // Then + result.AffectedFileRelativePath.FullPath.ShouldBe(affectedFileRelativePath); + } + finally + { + System.IO.File.Delete(filePath.FullPath); + } + } + + [Fact] + public void Should_Give_Correct_Result_For_Line_After_Roundtrip() + { + // Given + var line = 42; + var issue = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InFile(@"src/foo.bar", line) + .Create(); + var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); + + try + { + // When + issue.SerializeToJsonFile(filePath); + var result = filePath.DeserializeToIssue(); + + // Then + result.Line.ShouldBe(line); + } + finally + { + System.IO.File.Delete(filePath.FullPath); + } + } + + [Fact] + public void Should_Give_Correct_Result_For_Priority_After_Roundtrip() + { + // Given + var priority = 42; + var issue = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .WithPriority(priority, "priorityName") + .Create(); + var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); + + try + { + // When + issue.SerializeToJsonFile(filePath); + var result = filePath.DeserializeToIssue(); + + // Then + result.Priority.ShouldBe(priority); + } + finally + { + System.IO.File.Delete(filePath.FullPath); + } + } + + [Fact] + public void Should_Give_Correct_Result_For_PriorityName_After_Roundtrip() + { + // Given + var priorityName = "priorityName"; + var issue = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .WithPriority(42, priorityName) + .Create(); + var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); + + try + { + // When + issue.SerializeToJsonFile(filePath); + var result = filePath.DeserializeToIssue(); + + // Then + result.PriorityName.ShouldBe(priorityName); + } + finally + { + System.IO.File.Delete(filePath.FullPath); + } + } + + [Fact] + public void Should_Give_Correct_Result_For_Rule_After_Roundtrip() + { + // Given + var rule = "rule"; + var issue = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .OfRule(rule) + .Create(); + var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); + + try + { + // When + issue.SerializeToJsonFile(filePath); + var result = filePath.DeserializeToIssue(); + + // Then + result.Rule.ShouldBe(rule); + } + finally + { + System.IO.File.Delete(filePath.FullPath); + } + } + + [Fact] + public void Should_Give_Correct_Result_For_RuleUrl_After_Roundtrip() + { + // Given + var ruleUrl = new Uri("https://rule.url"); + var issue = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .OfRule("rule", ruleUrl) + .Create(); + var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); + + try + { + // When + issue.SerializeToJsonFile(filePath); + var result = filePath.DeserializeToIssue(); + + // Then + result.RuleUrl.ToString().ShouldBe(ruleUrl.ToString()); + } + finally + { + System.IO.File.Delete(filePath.FullPath); + } + } + } + + public sealed class TheSerializeToJsonFileExtensionForAnEnumerableOfIssues + { + [Fact] + public void Should_Throw_If_Issue_Is_Null() + { + // Given + IEnumerable issues = null; + var filePath = @"c:\issues.json"; + + // When + var result = Record.Exception(() => issues.SerializeToJsonFile(filePath)); + + // Then + result.IsArgumentNullException("issues"); + } + + [Fact] + public void Should_Throw_If_FilePath_Is_Null() + { + // Given + var issues = new List(); + FilePath filePath = null; + + // When + var result = Record.Exception(() => issues.SerializeToJsonFile(filePath)); + + // Then + result.IsArgumentNullException("filePath"); + } + + [Fact] + public void Should_Give_Correct_Result_For_Message_After_Roundtrip() + { + // Given + var message1 = "message1"; + var message2 = "message2"; + var issues = + new List + { + IssueBuilder + .NewIssue(message1, "providerType1", "providerName1") + .Create(), + IssueBuilder + .NewIssue(message2, "providerType2", "providerName2") + .Create(), + }; + var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); + + try + { + // When + issues.SerializeToJsonFile(filePath); + var result = filePath.DeserializeToIssues(); + + // Then + result.Count().ShouldBe(2); + result.First().Message.ShouldBe(message1); + result.Last().Message.ShouldBe(message2); + } + finally + { + System.IO.File.Delete(filePath.FullPath); + } + } + + [Fact] + public void Should_Give_Correct_Result_For_ProviderType_After_Roundtrip() + { + // Given + var providerType1 = "providerType1"; + var providerType2 = "providerType2"; + var issues = + new List + { + IssueBuilder + .NewIssue("message1", providerType1, "providerName1") + .Create(), + IssueBuilder + .NewIssue("message2", providerType2, "providerName2") + .Create(), + }; + var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); + + try + { + // When + issues.SerializeToJsonFile(filePath); + var result = filePath.DeserializeToIssues(); + + // Then + result.Count().ShouldBe(2); + result.First().ProviderType.ShouldBe(providerType1); + result.Last().ProviderType.ShouldBe(providerType2); + } + finally + { + System.IO.File.Delete(filePath.FullPath); + } + } + + [Fact] + public void Should_Give_Correct_Result_For_ProviderName_After_Roundtrip() + { + // Given + var providerName1 = "providerName1"; + var providerName2 = "providerName2"; + var issues = + new List + { + IssueBuilder + .NewIssue("message1", "providerType1", providerName1) + .Create(), + IssueBuilder + .NewIssue("message2", "providerType2", providerName2) + .Create(), + }; + var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); + + try + { + // When + issues.SerializeToJsonFile(filePath); + var result = filePath.DeserializeToIssues(); + + // Then + result.Count().ShouldBe(2); + result.First().ProviderName.ShouldBe(providerName1); + result.Last().ProviderName.ShouldBe(providerName2); + } + finally + { + System.IO.File.Delete(filePath.FullPath); + } + } + + [Fact] + public void Should_Give_Correct_Result_For_ProjectFileRelativePath_After_Roundtrip() + { + // Given + var projectFileRelativePath1 = @"src/myproj1.file"; + var projectFileRelativePath2 = @"src/myproj2.file"; + var issues = + new List + { + IssueBuilder + .NewIssue("message1", "providerType1", "providerName1") + .InProjectFile(projectFileRelativePath1) + .Create(), + IssueBuilder + .NewIssue("message2", "providerType2", "providerName2") + .InProjectFile(projectFileRelativePath2) + .Create(), + }; + var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); + + try + { + // When + issues.SerializeToJsonFile(filePath); + var result = filePath.DeserializeToIssues(); + + // Then + result.Count().ShouldBe(2); + result.First().ProjectFileRelativePath.FullPath.ShouldBe(projectFileRelativePath1); + result.Last().ProjectFileRelativePath.FullPath.ShouldBe(projectFileRelativePath2); + } + finally + { + System.IO.File.Delete(filePath.FullPath); + } + } + + [Fact] + public void Should_Give_Correct_Result_For_ProjectName_After_Roundtrip() + { + // Given + var projectName1 = "projectName1"; + var projectName2 = "projectName2"; + var issues = + new List + { + IssueBuilder + .NewIssue("message1", "providerType1", "providerName1") + .InProjectOfName(projectName1) + .Create(), + IssueBuilder + .NewIssue("message2", "providerType2", "providerName2") + .InProjectOfName(projectName2) + .Create(), + }; + var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); + + try + { + // When + issues.SerializeToJsonFile(filePath); + var result = filePath.DeserializeToIssues(); + + // Then + result.Count().ShouldBe(2); + result.First().ProjectName.ShouldBe(projectName1); + result.Last().ProjectName.ShouldBe(projectName2); + } + finally + { + System.IO.File.Delete(filePath.FullPath); + } + } + + [Fact] + public void Should_Give_Correct_Result_For_AffectedFileRelativePath_After_Roundtrip() + { + // Given + var affectedFileRelativePath1 = @"src/foo1.bar"; + var affectedFileRelativePath2 = @"src/foo2.bar"; + var issues = + new List + { + IssueBuilder + .NewIssue("message1", "providerType1", "providerName1") + .InFile(affectedFileRelativePath1) + .Create(), + IssueBuilder + .NewIssue("message2", "providerType2", "providerName2") + .InFile(affectedFileRelativePath2) + .Create(), + }; + var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); + + try + { + // When + issues.SerializeToJsonFile(filePath); + var result = filePath.DeserializeToIssues(); + + // Then + result.Count().ShouldBe(2); + result.First().AffectedFileRelativePath.FullPath.ShouldBe(affectedFileRelativePath1); + result.Last().AffectedFileRelativePath.FullPath.ShouldBe(affectedFileRelativePath2); + } + finally + { + System.IO.File.Delete(filePath.FullPath); + } + } + + [Fact] + public void Should_Give_Correct_Result_For_Line_After_Roundtrip() + { + // Given + var line1 = 23; + var line2 = 42; + var issues = + new List + { + IssueBuilder + .NewIssue("message1", "providerType1", "providerName1") + .InFile(@"src/foo.bar", line1) + .Create(), + IssueBuilder + .NewIssue("message2", "providerType2", "providerName2") + .InFile(@"src/foo.bar", line2) + .Create(), + }; + var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); + + try + { + // When + issues.SerializeToJsonFile(filePath); + var result = filePath.DeserializeToIssues(); + + // Then + result.Count().ShouldBe(2); + result.First().Line.ShouldBe(line1); + result.Last().Line.ShouldBe(line2); + } + finally + { + System.IO.File.Delete(filePath.FullPath); + } + } + + [Fact] + public void Should_Give_Correct_Result_For_Priority_After_Roundtrip() + { + // Given + var priority1 = 23; + var priority2 = 42; + var issues = + new List + { + IssueBuilder + .NewIssue("message1", "providerType1", "providerName1") + .WithPriority(priority1, "priorityName") + .Create(), + IssueBuilder + .NewIssue("message2", "providerType2", "providerName2") + .WithPriority(priority2, "priorityName") + .Create(), + }; + var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); + + try + { + // When + issues.SerializeToJsonFile(filePath); + var result = filePath.DeserializeToIssues(); + + // Then + result.Count().ShouldBe(2); + result.First().Priority.ShouldBe(priority1); + result.Last().Priority.ShouldBe(priority2); + } + finally + { + System.IO.File.Delete(filePath.FullPath); + } + } + + [Fact] + public void Should_Give_Correct_Result_For_PriorityName_After_Roundtrip() + { + // Given + var priorityName1 = "priorityName1"; + var priorityName2 = "priorityName2"; + var issues = + new List + { + IssueBuilder + .NewIssue("message1", "providerType1", "providerName1") + .WithPriority(42, priorityName1) + .Create(), + IssueBuilder + .NewIssue("message2", "providerType2", "providerName2") + .WithPriority(42, priorityName2) + .Create(), + }; + var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); + + try + { + // When + issues.SerializeToJsonFile(filePath); + var result = filePath.DeserializeToIssues(); + + // Then + result.Count().ShouldBe(2); + result.First().PriorityName.ShouldBe(priorityName1); + result.Last().PriorityName.ShouldBe(priorityName2); + } + finally + { + System.IO.File.Delete(filePath.FullPath); + } + } + + [Fact] + public void Should_Give_Correct_Result_For_Rule_After_Roundtrip() + { + // Given + var rule1 = "rule1"; + var rule2 = "rule2"; + var issues = + new List + { + IssueBuilder + .NewIssue("message1", "providerType1", "providerName1") + .OfRule(rule1) + .Create(), + IssueBuilder + .NewIssue("message2", "providerType2", "providerName2") + .OfRule(rule2) + .Create(), + }; + var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); + + try + { + // When + issues.SerializeToJsonFile(filePath); + var result = filePath.DeserializeToIssues(); + + // Then + result.Count().ShouldBe(2); + result.First().Rule.ShouldBe(rule1); + result.Last().Rule.ShouldBe(rule2); + } + finally + { + System.IO.File.Delete(filePath.FullPath); + } + } + + [Fact] + public void Should_Give_Correct_Result_For_RuleUrl_After_Roundtrip() + { + // Given + var ruleUrl1 = new Uri("https://rule1.url"); + var ruleUrl2 = new Uri("https://rule2.url"); + var issues = + new List + { + IssueBuilder + .NewIssue("message1", "providerType1", "providerName1") + .OfRule("rule", ruleUrl1) + .Create(), + IssueBuilder + .NewIssue("message2", "providerType2", "providerName2") + .OfRule("rule", ruleUrl2) + .Create(), + }; + var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); + + try + { + // When + issues.SerializeToJsonFile(filePath); + var result = filePath.DeserializeToIssues(); + + // Then + result.Count().ShouldBe(2); + result.First().RuleUrl.ToString().ShouldBe(ruleUrl1.ToString()); + result.Last().RuleUrl.ToString().ShouldBe(ruleUrl2.ToString()); + } + finally + { + System.IO.File.Delete(filePath.FullPath); + } + } + } + + public sealed class TheToSerializableIssueExtension + { + [Fact] + public void Should_Throw_If_Issue_Is_Null() + { + // Given + IIssue issue = null; + + // When + var result = Record.Exception(() => issue.ToSerializableIssue()); + + // Then + result.IsArgumentNullException("issue"); + } + } + } +} \ No newline at end of file diff --git a/src/Cake.Issues.Tests/Serialization/SerializableIssueExtensionsTests.cs b/src/Cake.Issues.Tests/Serialization/SerializableIssueExtensionsTests.cs new file mode 100644 index 000000000..79e174bae --- /dev/null +++ b/src/Cake.Issues.Tests/Serialization/SerializableIssueExtensionsTests.cs @@ -0,0 +1,25 @@ +namespace Cake.Issues.Tests.Serialization +{ + using Cake.Issues.Serialization; + using Cake.Issues.Testing; + using Xunit; + + public sealed class SerializableIssueExtensionsTests + { + public sealed class TheToIssueExtension + { + [Fact] + public void Should_Throw_If_SerializableIssue_Is_Null() + { + // Given + SerializableIssue serializableIssue = null; + + // When + var result = Record.Exception(() => serializableIssue.ToIssue()); + + // Then + result.IsArgumentNullException("serializableIssue"); + } + } + } +} \ No newline at end of file diff --git a/src/Cake.Issues/Aliases.IssueSerialization.cs b/src/Cake.Issues/Aliases.IssueSerialization.cs index 56959cff3..c1422b39d 100644 --- a/src/Cake.Issues/Aliases.IssueSerialization.cs +++ b/src/Cake.Issues/Aliases.IssueSerialization.cs @@ -4,6 +4,7 @@ using Cake.Core; using Cake.Core.Annotations; using Cake.Core.IO; + using Cake.Issues.Serialization; /// /// Contains functionality related to serializing and deserializing issues. diff --git a/src/Cake.Issues/Issue.cs b/src/Cake.Issues/Issue.cs index c33cc31c2..d774dde8e 100644 --- a/src/Cake.Issues/Issue.cs +++ b/src/Cake.Issues/Issue.cs @@ -81,7 +81,7 @@ public Issue( if (!this.AffectedFileRelativePath.IsRelative) { throw new ArgumentOutOfRangeException( - nameof(affectedFileRelativePath), + nameof(affectedFileRelativePath), $"File path '{this.AffectedFileRelativePath}' needs to be relative to the repository root."); } } diff --git a/src/Cake.Issues/IssueSerializationExtensions.cs b/src/Cake.Issues/IssueSerializationExtensions.cs deleted file mode 100644 index 3a6a9e6d5..000000000 --- a/src/Cake.Issues/IssueSerializationExtensions.cs +++ /dev/null @@ -1,226 +0,0 @@ -namespace Cake.Issues -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Runtime.Serialization.Json; - using System.Text; - using Cake.Core.IO; - - /// - /// Extensions for serializing and deserializing an . - /// - internal static class IssueSerializationExtensions - { - /// - /// Serializes an to a JSON string. - /// - /// Issue which should be serialized. - /// Serialized issue. - public static string SerializeToJsonString(this IIssue issue) - { - issue.NotNull(nameof(issue)); - - var serializer = new DataContractJsonSerializer(typeof(SerializableIssue)); - - using (var stream = new MemoryStream()) - { - serializer.WriteObject(stream, issue.ToSerializableIssue()); - return Encoding.UTF8.GetString(stream.ToArray()); - } - } - - /// - /// Serializes an to a JSON string. - /// - /// Issues which should be serialized. - /// Serialized issues. - public static string SerializeToJsonString(this IEnumerable issues) - { - issues.NotNull(nameof(issues)); - - var serializer = new DataContractJsonSerializer(typeof(IEnumerable)); - - using (var stream = new MemoryStream()) - { - serializer.WriteObject(stream, issues.Select(x => x.ToSerializableIssue())); - return Encoding.UTF8.GetString(stream.ToArray()); - } - } - - /// - /// Serializes an to a JSON file. - /// - /// Issue which should be serialized. - /// Path to the file. - public static void SerializeToJsonFile(this IIssue issue, FilePath filePath) - { - issue.NotNull(nameof(issue)); - filePath.NotNull(nameof(filePath)); - - var serializer = new DataContractJsonSerializer(typeof(SerializableIssue)); - - using (var stream = File.Open(filePath.FullPath, FileMode.Create)) - { - serializer.WriteObject(stream, issue.ToSerializableIssue()); - } - } - - /// - /// Serializes an to a JSON file. - /// - /// Issues which should be serialized. - /// Path to the file. - public static void SerializeToJsonFile(this IEnumerable issues, FilePath filePath) - { - issues.NotNull(nameof(issues)); - filePath.NotNull(nameof(filePath)); - - var serializer = new DataContractJsonSerializer(typeof(IEnumerable)); - - using (var stream = File.Open(filePath.FullPath, FileMode.Create)) - { - serializer.WriteObject(stream, issues.Select(x => x.ToSerializableIssue())); - } - } - - /// - /// Deserializes an from a JSON string. - /// - /// JSON representation of the issue. - /// Instance of the issue. - public static Issue DeserializeToIssue(this string jsonString) - { - jsonString.NotNullOrWhiteSpace(nameof(jsonString)); - - using (var stream = new MemoryStream(Encoding.Default.GetBytes(jsonString))) - { - return DeserializeStreamToIssue(stream); - } - } - - /// - /// Deserializes an from a JSON string. - /// - /// JSON representation of the issues. - /// List of issues. - public static IEnumerable DeserializeToIssues(this string jsonString) - { - jsonString.NotNullOrWhiteSpace(nameof(jsonString)); - - using (var stream = new MemoryStream(Encoding.Default.GetBytes(jsonString))) - { - return DeserializeStreamToIssues(stream); - } - } - - /// - /// Deserializes an from a JSON file. - /// - /// Path to the JSON file. - /// Instance of the issue. - public static Issue DeserializeToIssue(this FilePath filePath) - { - filePath.NotNull(nameof(filePath)); - - using (var stream = File.Open(filePath.FullPath, FileMode.Open)) - { - return DeserializeStreamToIssue(stream); - } - } - - /// - /// Deserializes an from a JSON file. - /// - /// Path to the JSON file. - /// List of issues. - public static IEnumerable DeserializeToIssues(this FilePath filePath) - { - filePath.NotNull(nameof(filePath)); - - using (var stream = File.Open(filePath.FullPath, FileMode.Open)) - { - return DeserializeStreamToIssues(stream); - } - } - - /// - /// Converts an to a . - /// - /// Issue which should be converted. - /// Converted issue. - internal static SerializableIssue ToSerializableIssue(this IIssue issue) - { - issue.NotNull(nameof(issue)); - - return new SerializableIssue - { - ProjectFileRelativePath = issue.ProjectFileRelativePath?.FullPath, - ProjectName = issue.ProjectName, - AffectedFileRelativePath = issue.AffectedFileRelativePath?.FullPath, - Line = issue.Line, - Message = issue.Message, - Priority = issue.Priority, - PriorityName = issue.PriorityName, - Rule = issue.Rule, - RuleUrl = issue.RuleUrl?.ToString(), - ProviderType = issue.ProviderType, - ProviderName = issue.ProviderName, - }; - } - - /// - /// Converts a to an . - /// - /// Issue which should be converted. - /// Converted issue. - internal static Issue ToIssue(this SerializableIssue serializableIssue) - { - serializableIssue.NotNull(nameof(serializableIssue)); - - Uri ruleUrl = null; - if (!string.IsNullOrWhiteSpace(serializableIssue.RuleUrl)) - { - ruleUrl = new Uri(serializableIssue.RuleUrl); - } - - return new Issue( - serializableIssue.ProjectFileRelativePath, - serializableIssue.ProjectName, - serializableIssue.AffectedFileRelativePath, - serializableIssue.Line, - serializableIssue.Message, - serializableIssue.Priority, - serializableIssue.PriorityName, - serializableIssue.Rule, - ruleUrl, - serializableIssue.ProviderType, - serializableIssue.ProviderName); - } - - private static Issue DeserializeStreamToIssue(Stream stream) - { - var serializer = new DataContractJsonSerializer(typeof(SerializableIssue)); - - if (!(serializer.ReadObject(stream) is SerializableIssue deserializedIssue)) - { - return null; - } - - return deserializedIssue.ToIssue(); - } - - private static IEnumerable DeserializeStreamToIssues(Stream stream) - { - var serializer = new DataContractJsonSerializer(typeof(IEnumerable)); - - if (!(serializer.ReadObject(stream) is IEnumerable deserializedIssues)) - { - return new List(); - } - - return deserializedIssues.Select(x => x.ToIssue()); - } - } -} diff --git a/src/Cake.Issues/Serialization/IssueDeserializationExtensions.cs b/src/Cake.Issues/Serialization/IssueDeserializationExtensions.cs new file mode 100644 index 000000000..28e7f0105 --- /dev/null +++ b/src/Cake.Issues/Serialization/IssueDeserializationExtensions.cs @@ -0,0 +1,139 @@ +namespace Cake.Issues.Serialization +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Text; + using Cake.Core.IO; + using LitJson; + + /// + /// Extensions for deserializing an . + /// + internal static class IssueDeserializationExtensions + { + /// + /// Deserializes an from a JSON string. + /// + /// JSON representation of the issue. + /// Instance of the issue. + public static Issue DeserializeToIssue(this string jsonString) + { + jsonString.NotNullOrWhiteSpace(nameof(jsonString)); + + using (var stream = new MemoryStream(Encoding.Default.GetBytes(jsonString))) + { + return DeserializeStreamToIssue(stream); + } + } + + /// + /// Deserializes an from a JSON string. + /// + /// JSON representation of the issues. + /// List of issues. + public static IEnumerable DeserializeToIssues(this string jsonString) + { + jsonString.NotNullOrWhiteSpace(nameof(jsonString)); + + using (var stream = new MemoryStream(Encoding.Default.GetBytes(jsonString))) + { + return DeserializeStreamToIssues(stream); + } + } + + /// + /// Deserializes an from a JSON file. + /// + /// Path to the JSON file. + /// Instance of the issue. + public static Issue DeserializeToIssue(this FilePath filePath) + { + filePath.NotNull(nameof(filePath)); + + using (var stream = File.Open(filePath.FullPath, FileMode.Open)) + { + return DeserializeStreamToIssue(stream); + } + } + + /// + /// Deserializes an from a JSON file. + /// + /// Path to the JSON file. + /// List of issues. + public static IEnumerable DeserializeToIssues(this FilePath filePath) + { + filePath.NotNull(nameof(filePath)); + + using (var stream = File.Open(filePath.FullPath, FileMode.Open)) + { + return DeserializeStreamToIssues(stream); + } + } + + /// + /// Deserializes a stream containing the JSON representation of an issue to an . + /// + /// Stream whose content should be deserialized. + /// Issue instance. + private static Issue DeserializeStreamToIssue(Stream stream) + { + using (var reader = new StreamReader(stream)) + { + var jsonContent = reader.ReadToEnd(); + + var data = JsonMapper.ToObject(jsonContent); + + return DeserializeJsonDataToIssue(data); + } + } + + /// + /// Deserializes a stream containing the JSON representation of an array of issues to an . + /// + /// Stream whose content should be deserialized. + /// List of issues. + private static IEnumerable DeserializeStreamToIssues(Stream stream) + { + using (var reader = new StreamReader(stream)) + { + var jsonContent = reader.ReadToEnd(); + + var data = JsonMapper.ToObject(jsonContent); + var issues = new List(); + foreach (JsonData element in data) + { + issues.Add(DeserializeJsonDataToIssue(element)); + } + + return issues; + } + } + + /// + /// Deserializes JSON repesentation of an issue to an . + /// Supports serialization format of the current version of Cake.Issues as versions + /// written with previous versions of Cake.Issues. + /// + /// JSON representation of the issue. + /// Issue instance. + private static Issue DeserializeJsonDataToIssue(JsonData data) + { + if (data.ContainsKey("Version")) + { + var version = (int)data["Version"]; + switch (version) + { + default: + throw new Exception($"Not supported issue serialization format {version}"); + } + } + else + { + // If no version is available deserialize to original format. + return JsonMapper.ToObject(data.ToJson()).ToIssue(); + } + } + } +} diff --git a/src/Cake.Issues/Serialization/IssueSerializationExtensions.cs b/src/Cake.Issues/Serialization/IssueSerializationExtensions.cs new file mode 100644 index 000000000..436ba99b7 --- /dev/null +++ b/src/Cake.Issues/Serialization/IssueSerializationExtensions.cs @@ -0,0 +1,97 @@ +namespace Cake.Issues.Serialization +{ + using System.Collections.Generic; + using System.IO; + using System.Linq; + using Cake.Core.IO; + using LitJson; + + /// + /// Extensions for serializing an to the latest serialization format. + /// + internal static class IssueSerializationExtensions + { + /// + /// Serializes an to a JSON string. + /// + /// Issue which should be serialized. + /// Serialized issue. + public static string SerializeToJsonString(this IIssue issue) + { + issue.NotNull(nameof(issue)); + + return JsonMapper.ToJson(issue.ToSerializableIssue()); + } + + /// + /// Serializes an to a JSON string. + /// + /// Issues which should be serialized. + /// Serialized issues. + public static string SerializeToJsonString(this IEnumerable issues) + { + issues.NotNull(nameof(issues)); + + return JsonMapper.ToJson(issues.Select(x => x.ToSerializableIssue()).ToArray()); + } + + /// + /// Serializes an to a JSON file. + /// + /// Issue which should be serialized. + /// Path to the file. + public static void SerializeToJsonFile(this IIssue issue, FilePath filePath) + { + issue.NotNull(nameof(issue)); + filePath.NotNull(nameof(filePath)); + + using (var stream = File.Open(filePath.FullPath, FileMode.Create)) + using (var writer = new StreamWriter(stream)) + { + JsonMapper.ToJson(issue.ToSerializableIssue(), new JsonWriter(writer)); + } + } + + /// + /// Serializes an to a JSON file. + /// + /// Issues which should be serialized. + /// Path to the file. + public static void SerializeToJsonFile(this IEnumerable issues, FilePath filePath) + { + issues.NotNull(nameof(issues)); + filePath.NotNull(nameof(filePath)); + + using (var stream = File.Open(filePath.FullPath, FileMode.Create)) + using (var writer = new StreamWriter(stream)) + { + JsonMapper.ToJson(issues.Select(x => x.ToSerializableIssue()).ToArray(), new JsonWriter(writer)); + } + } + + /// + /// Converts an to a . + /// + /// Issue which should be converted. + /// Converted issue. + internal static SerializableIssue ToSerializableIssue(this IIssue issue) + { + issue.NotNull(nameof(issue)); + + return new SerializableIssue + { + ProjectFileRelativePath = issue.ProjectFileRelativePath?.FullPath, + ProjectName = issue.ProjectName, + AffectedFileRelativePath = issue.AffectedFileRelativePath?.FullPath, + Line = issue.Line, + Message = issue.Message, + Priority = issue.Priority, + PriorityName = issue.PriorityName, + Rule = issue.Rule, + RuleUrl = issue.RuleUrl?.ToString(), + ProviderType = issue.ProviderType, + ProviderName = issue.ProviderName, + }; + } + } +} diff --git a/src/Cake.Issues/Serialization/LitJson/IJsonWrapper.cs b/src/Cake.Issues/Serialization/LitJson/IJsonWrapper.cs new file mode 100644 index 000000000..423708172 --- /dev/null +++ b/src/Cake.Issues/Serialization/LitJson/IJsonWrapper.cs @@ -0,0 +1,66 @@ +#pragma warning disable 1587 +#pragma warning disable 1591 + +#region Header +/** + * IJsonWrapper.cs + * Interface that represents a type capable of handling all kinds of JSON + * data. This is mainly used when mapping objects through JsonMapper, and + * it's implemented by JsonData. + * + * The authors disclaim copyright to this source code. For more details, see + * the 3rd-Party-License.md file included with this distribution. + **/ + +// This file isn't generated, but this comment is necessary to exclude it from code analysis. +// +#endregion + + +using System.Collections; +using System.Collections.Specialized; + + +namespace LitJson +{ + public enum JsonType + { + None, + + Object, + Array, + String, + Int, + Long, + Double, + Boolean + } + + public interface IJsonWrapper : IList, IOrderedDictionary + { + bool IsArray { get; } + bool IsBoolean { get; } + bool IsDouble { get; } + bool IsInt { get; } + bool IsLong { get; } + bool IsObject { get; } + bool IsString { get; } + + bool GetBoolean (); + double GetDouble (); + int GetInt (); + JsonType GetJsonType (); + long GetLong (); + string GetString (); + + void SetBoolean (bool val); + void SetDouble (double val); + void SetInt (int val); + void SetJsonType (JsonType type); + void SetLong (long val); + void SetString (string val); + + string ToJson (); + void ToJson (JsonWriter writer); + } +} diff --git a/src/Cake.Issues/Serialization/LitJson/JsonData.cs b/src/Cake.Issues/Serialization/LitJson/JsonData.cs new file mode 100644 index 000000000..3198eeb45 --- /dev/null +++ b/src/Cake.Issues/Serialization/LitJson/JsonData.cs @@ -0,0 +1,1065 @@ +#pragma warning disable 1587 +#pragma warning disable 1591 + +#region Header +/** + * JsonData.cs + * Generic type to hold JSON data (objects, arrays, and so on). This is + * the default type returned by JsonMapper.ToObject(). + * + * The authors disclaim copyright to this source code. For more details, see + * the 3rd-Party-License.md file included with this distribution. + **/ + +// This file isn't generated, but this comment is necessary to exclude it from code analysis. +// +#endregion + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.IO; + + +namespace LitJson +{ + public class JsonData : IJsonWrapper, IEquatable + { + #region Fields + private IList inst_array; + private bool inst_boolean; + private double inst_double; + private int inst_int; + private long inst_long; + private IDictionary inst_object; + private string inst_string; + private string json; + private JsonType type; + + // Used to implement the IOrderedDictionary interface + private IList> object_list; + #endregion + + + #region Properties + public int Count { + get { return EnsureCollection ().Count; } + } + + public bool IsArray { + get { return type == JsonType.Array; } + } + + public bool IsBoolean { + get { return type == JsonType.Boolean; } + } + + public bool IsDouble { + get { return type == JsonType.Double; } + } + + public bool IsInt { + get { return type == JsonType.Int; } + } + + public bool IsLong { + get { return type == JsonType.Long; } + } + + public bool IsObject { + get { return type == JsonType.Object; } + } + + public bool IsString { + get { return type == JsonType.String; } + } + + public ICollection Keys { + get { EnsureDictionary (); return inst_object.Keys; } + } + + /// + /// Determines whether the json contains an element that has the specified key. + /// + /// The key to locate in the json. + /// true if the json contains an element that has the specified key; otherwise, false. + public Boolean ContainsKey(String key) { + EnsureDictionary(); + return this.inst_object.Keys.Contains(key); + } + #endregion + + + #region ICollection Properties + int ICollection.Count { + get { + return Count; + } + } + + bool ICollection.IsSynchronized { + get { + return EnsureCollection ().IsSynchronized; + } + } + + object ICollection.SyncRoot { + get { + return EnsureCollection ().SyncRoot; + } + } + #endregion + + + #region IDictionary Properties + bool IDictionary.IsFixedSize { + get { + return EnsureDictionary ().IsFixedSize; + } + } + + bool IDictionary.IsReadOnly { + get { + return EnsureDictionary ().IsReadOnly; + } + } + + ICollection IDictionary.Keys { + get { + EnsureDictionary (); + IList keys = new List (); + + foreach (KeyValuePair entry in + object_list) { + keys.Add (entry.Key); + } + + return (ICollection) keys; + } + } + + ICollection IDictionary.Values { + get { + EnsureDictionary (); + IList values = new List (); + + foreach (KeyValuePair entry in + object_list) { + values.Add (entry.Value); + } + + return (ICollection) values; + } + } + #endregion + + + + #region IJsonWrapper Properties + bool IJsonWrapper.IsArray { + get { return IsArray; } + } + + bool IJsonWrapper.IsBoolean { + get { return IsBoolean; } + } + + bool IJsonWrapper.IsDouble { + get { return IsDouble; } + } + + bool IJsonWrapper.IsInt { + get { return IsInt; } + } + + bool IJsonWrapper.IsLong { + get { return IsLong; } + } + + bool IJsonWrapper.IsObject { + get { return IsObject; } + } + + bool IJsonWrapper.IsString { + get { return IsString; } + } + #endregion + + + #region IList Properties + bool IList.IsFixedSize { + get { + return EnsureList ().IsFixedSize; + } + } + + bool IList.IsReadOnly { + get { + return EnsureList ().IsReadOnly; + } + } + #endregion + + + #region IDictionary Indexer + object IDictionary.this[object key] { + get { + return EnsureDictionary ()[key]; + } + + set { + if (! (key is String)) + throw new ArgumentException ( + "The key has to be a string"); + + JsonData data = ToJsonData (value); + + this[(string) key] = data; + } + } + #endregion + + + #region IOrderedDictionary Indexer + object IOrderedDictionary.this[int idx] { + get { + EnsureDictionary (); + return object_list[idx].Value; + } + + set { + EnsureDictionary (); + JsonData data = ToJsonData (value); + + KeyValuePair old_entry = object_list[idx]; + + inst_object[old_entry.Key] = data; + + KeyValuePair entry = + new KeyValuePair (old_entry.Key, data); + + object_list[idx] = entry; + } + } + #endregion + + + #region IList Indexer + object IList.this[int index] { + get { + return EnsureList ()[index]; + } + + set { + EnsureList (); + JsonData data = ToJsonData (value); + + this[index] = data; + } + } + #endregion + + + #region Public Indexers + public JsonData this[string prop_name] { + get { + EnsureDictionary (); + return inst_object[prop_name]; + } + + set { + EnsureDictionary (); + + KeyValuePair entry = + new KeyValuePair (prop_name, value); + + if (inst_object.ContainsKey (prop_name)) { + for (int i = 0; i < object_list.Count; i++) { + if (object_list[i].Key == prop_name) { + object_list[i] = entry; + break; + } + } + } else + object_list.Add (entry); + + inst_object[prop_name] = value; + + json = null; + } + } + + public JsonData this[int index] { + get { + EnsureCollection (); + + if (type == JsonType.Array) + return inst_array[index]; + + return object_list[index].Value; + } + + set { + EnsureCollection (); + + if (type == JsonType.Array) + inst_array[index] = value; + else { + KeyValuePair entry = object_list[index]; + KeyValuePair new_entry = + new KeyValuePair (entry.Key, value); + + object_list[index] = new_entry; + inst_object[entry.Key] = value; + } + + json = null; + } + } + #endregion + + + #region Constructors + public JsonData () + { + } + + public JsonData (bool boolean) + { + type = JsonType.Boolean; + inst_boolean = boolean; + } + + public JsonData (double number) + { + type = JsonType.Double; + inst_double = number; + } + + public JsonData (int number) + { + type = JsonType.Int; + inst_int = number; + } + + public JsonData (long number) + { + type = JsonType.Long; + inst_long = number; + } + + public JsonData (object obj) + { + if (obj is Boolean) { + type = JsonType.Boolean; + inst_boolean = (bool) obj; + return; + } + + if (obj is Double) { + type = JsonType.Double; + inst_double = (double) obj; + return; + } + + if (obj is Int32) { + type = JsonType.Int; + inst_int = (int) obj; + return; + } + + if (obj is Int64) { + type = JsonType.Long; + inst_long = (long) obj; + return; + } + + if (obj is String) { + type = JsonType.String; + inst_string = (string) obj; + return; + } + + throw new ArgumentException ( + "Unable to wrap the given object with JsonData"); + } + + public JsonData (string str) + { + type = JsonType.String; + inst_string = str; + } + #endregion + + + #region Implicit Conversions + public static implicit operator JsonData (Boolean data) + { + return new JsonData (data); + } + + public static implicit operator JsonData (Double data) + { + return new JsonData (data); + } + + public static implicit operator JsonData (Int32 data) + { + return new JsonData (data); + } + + public static implicit operator JsonData (Int64 data) + { + return new JsonData (data); + } + + public static implicit operator JsonData (String data) + { + return new JsonData (data); + } + #endregion + + + #region Explicit Conversions + public static explicit operator Boolean (JsonData data) + { + if (data.type != JsonType.Boolean) + throw new InvalidCastException ( + "Instance of JsonData doesn't hold a double"); + + return data.inst_boolean; + } + + public static explicit operator Double (JsonData data) + { + if (data.type != JsonType.Double) + throw new InvalidCastException ( + "Instance of JsonData doesn't hold a double"); + + return data.inst_double; + } + + public static explicit operator Int32(JsonData data) + { + if (data.type != JsonType.Int && data.type != JsonType.Long) + { + throw new InvalidCastException( + "Instance of JsonData doesn't hold an int"); + } + + // cast may truncate data... but that's up to the user to consider + return data.type == JsonType.Int ? data.inst_int : (int)data.inst_long; + } + + public static explicit operator Int64(JsonData data) + { + if (data.type != JsonType.Long && data.type != JsonType.Int) + { + throw new InvalidCastException( + "Instance of JsonData doesn't hold a long"); + } + + return data.type == JsonType.Long ? data.inst_long : data.inst_int; + } + + public static explicit operator String (JsonData data) + { + if (data.type != JsonType.String) + throw new InvalidCastException ( + "Instance of JsonData doesn't hold a string"); + + return data.inst_string; + } + #endregion + + + #region ICollection Methods + void ICollection.CopyTo (Array array, int index) + { + EnsureCollection ().CopyTo (array, index); + } + #endregion + + + #region IDictionary Methods + void IDictionary.Add (object key, object value) + { + JsonData data = ToJsonData (value); + + EnsureDictionary ().Add (key, data); + + KeyValuePair entry = + new KeyValuePair ((string) key, data); + object_list.Add (entry); + + json = null; + } + + void IDictionary.Clear () + { + EnsureDictionary ().Clear (); + object_list.Clear (); + json = null; + } + + bool IDictionary.Contains (object key) + { + return EnsureDictionary ().Contains (key); + } + + IDictionaryEnumerator IDictionary.GetEnumerator () + { + return ((IOrderedDictionary) this).GetEnumerator (); + } + + void IDictionary.Remove (object key) + { + EnsureDictionary ().Remove (key); + + for (int i = 0; i < object_list.Count; i++) { + if (object_list[i].Key == (string) key) { + object_list.RemoveAt (i); + break; + } + } + + json = null; + } + #endregion + + + #region IEnumerable Methods + IEnumerator IEnumerable.GetEnumerator () + { + return EnsureCollection ().GetEnumerator (); + } + #endregion + + + #region IJsonWrapper Methods + bool IJsonWrapper.GetBoolean () + { + if (type != JsonType.Boolean) + throw new InvalidOperationException ( + "JsonData instance doesn't hold a boolean"); + + return inst_boolean; + } + + double IJsonWrapper.GetDouble () + { + if (type != JsonType.Double) + throw new InvalidOperationException ( + "JsonData instance doesn't hold a double"); + + return inst_double; + } + + int IJsonWrapper.GetInt () + { + if (type != JsonType.Int) + throw new InvalidOperationException ( + "JsonData instance doesn't hold an int"); + + return inst_int; + } + + long IJsonWrapper.GetLong () + { + if (type != JsonType.Long) + throw new InvalidOperationException ( + "JsonData instance doesn't hold a long"); + + return inst_long; + } + + string IJsonWrapper.GetString () + { + if (type != JsonType.String) + throw new InvalidOperationException ( + "JsonData instance doesn't hold a string"); + + return inst_string; + } + + void IJsonWrapper.SetBoolean (bool val) + { + type = JsonType.Boolean; + inst_boolean = val; + json = null; + } + + void IJsonWrapper.SetDouble (double val) + { + type = JsonType.Double; + inst_double = val; + json = null; + } + + void IJsonWrapper.SetInt (int val) + { + type = JsonType.Int; + inst_int = val; + json = null; + } + + void IJsonWrapper.SetLong (long val) + { + type = JsonType.Long; + inst_long = val; + json = null; + } + + void IJsonWrapper.SetString (string val) + { + type = JsonType.String; + inst_string = val; + json = null; + } + + string IJsonWrapper.ToJson () + { + return ToJson (); + } + + void IJsonWrapper.ToJson (JsonWriter writer) + { + ToJson (writer); + } + #endregion + + + #region IList Methods + int IList.Add (object value) + { + return Add (value); + } + + void IList.Clear () + { + EnsureList ().Clear (); + json = null; + } + + bool IList.Contains (object value) + { + return EnsureList ().Contains (value); + } + + int IList.IndexOf (object value) + { + return EnsureList ().IndexOf (value); + } + + void IList.Insert (int index, object value) + { + EnsureList ().Insert (index, value); + json = null; + } + + void IList.Remove (object value) + { + EnsureList ().Remove (value); + json = null; + } + + void IList.RemoveAt (int index) + { + EnsureList ().RemoveAt (index); + json = null; + } + #endregion + + + #region IOrderedDictionary Methods + IDictionaryEnumerator IOrderedDictionary.GetEnumerator () + { + EnsureDictionary (); + + return new OrderedDictionaryEnumerator ( + object_list.GetEnumerator ()); + } + + void IOrderedDictionary.Insert (int idx, object key, object value) + { + string property = (string) key; + JsonData data = ToJsonData (value); + + this[property] = data; + + KeyValuePair entry = + new KeyValuePair (property, data); + + object_list.Insert (idx, entry); + } + + void IOrderedDictionary.RemoveAt (int idx) + { + EnsureDictionary (); + + inst_object.Remove (object_list[idx].Key); + object_list.RemoveAt (idx); + } + #endregion + + + #region Private Methods + private ICollection EnsureCollection () + { + if (type == JsonType.Array) + return (ICollection) inst_array; + + if (type == JsonType.Object) + return (ICollection) inst_object; + + throw new InvalidOperationException ( + "The JsonData instance has to be initialized first"); + } + + private IDictionary EnsureDictionary () + { + if (type == JsonType.Object) + return (IDictionary) inst_object; + + if (type != JsonType.None) + throw new InvalidOperationException ( + "Instance of JsonData is not a dictionary"); + + type = JsonType.Object; + inst_object = new Dictionary (); + object_list = new List> (); + + return (IDictionary) inst_object; + } + + private IList EnsureList () + { + if (type == JsonType.Array) + return (IList) inst_array; + + if (type != JsonType.None) + throw new InvalidOperationException ( + "Instance of JsonData is not a list"); + + type = JsonType.Array; + inst_array = new List (); + + return (IList) inst_array; + } + + private JsonData ToJsonData (object obj) + { + if (obj == null) + return null; + + if (obj is JsonData) + return (JsonData) obj; + + return new JsonData (obj); + } + + private static void WriteJson (IJsonWrapper obj, JsonWriter writer) + { + if (obj == null) { + writer.Write (null); + return; + } + + if (obj.IsString) { + writer.Write (obj.GetString ()); + return; + } + + if (obj.IsBoolean) { + writer.Write (obj.GetBoolean ()); + return; + } + + if (obj.IsDouble) { + writer.Write (obj.GetDouble ()); + return; + } + + if (obj.IsInt) { + writer.Write (obj.GetInt ()); + return; + } + + if (obj.IsLong) { + writer.Write (obj.GetLong ()); + return; + } + + if (obj.IsArray) { + writer.WriteArrayStart (); + foreach (object elem in (IList) obj) + WriteJson ((JsonData) elem, writer); + writer.WriteArrayEnd (); + + return; + } + + if (obj.IsObject) { + writer.WriteObjectStart (); + + foreach (DictionaryEntry entry in ((IDictionary) obj)) { + writer.WritePropertyName ((string) entry.Key); + WriteJson ((JsonData) entry.Value, writer); + } + writer.WriteObjectEnd (); + + return; + } + } + #endregion + + + public int Add (object value) + { + JsonData data = ToJsonData (value); + + json = null; + + return EnsureList ().Add (data); + } + + public bool Remove(object obj) + { + json = null; + if(IsObject) + { + JsonData value = null; + if (inst_object.TryGetValue((string)obj, out value)) + return inst_object.Remove((string)obj) && object_list.Remove(new KeyValuePair((string)obj, value)); + else + throw new KeyNotFoundException("The specified key was not found in the JsonData object."); + } + if(IsArray) + { + return inst_array.Remove(ToJsonData(obj)); + } + throw new InvalidOperationException ( + "Instance of JsonData is not an object or a list."); + } + + public void Clear () + { + if (IsObject) { + ((IDictionary) this).Clear (); + return; + } + + if (IsArray) { + ((IList) this).Clear (); + return; + } + } + + public bool Equals (JsonData x) + { + if (x == null) + return false; + + if (x.type != this.type) + { + // further check to see if this is a long to int comparison + if ((x.type != JsonType.Int && x.type != JsonType.Long) + || (this.type != JsonType.Int && this.type != JsonType.Long)) + { + return false; + } + } + + switch (this.type) { + case JsonType.None: + return true; + + case JsonType.Object: + return this.inst_object.Equals (x.inst_object); + + case JsonType.Array: + return this.inst_array.Equals (x.inst_array); + + case JsonType.String: + return this.inst_string.Equals (x.inst_string); + + case JsonType.Int: + { + if (x.IsLong) + { + if (x.inst_long < Int32.MinValue || x.inst_long > Int32.MaxValue) + return false; + return this.inst_int.Equals((int)x.inst_long); + } + return this.inst_int.Equals(x.inst_int); + } + + case JsonType.Long: + { + if (x.IsInt) + { + if (this.inst_long < Int32.MinValue || this.inst_long > Int32.MaxValue) + return false; + return x.inst_int.Equals((int)this.inst_long); + } + return this.inst_long.Equals(x.inst_long); + } + + case JsonType.Double: + return this.inst_double.Equals (x.inst_double); + + case JsonType.Boolean: + return this.inst_boolean.Equals (x.inst_boolean); + } + + return false; + } + + public JsonType GetJsonType () + { + return type; + } + + public void SetJsonType (JsonType type) + { + if (this.type == type) + return; + + switch (type) { + case JsonType.None: + break; + + case JsonType.Object: + inst_object = new Dictionary (); + object_list = new List> (); + break; + + case JsonType.Array: + inst_array = new List (); + break; + + case JsonType.String: + inst_string = default (String); + break; + + case JsonType.Int: + inst_int = default (Int32); + break; + + case JsonType.Long: + inst_long = default (Int64); + break; + + case JsonType.Double: + inst_double = default (Double); + break; + + case JsonType.Boolean: + inst_boolean = default (Boolean); + break; + } + + this.type = type; + } + + public string ToJson () + { + if (json != null) + return json; + + StringWriter sw = new StringWriter (); + JsonWriter writer = new JsonWriter (sw); + writer.Validate = false; + + WriteJson (this, writer); + json = sw.ToString (); + + return json; + } + + public void ToJson (JsonWriter writer) + { + bool old_validate = writer.Validate; + + writer.Validate = false; + + WriteJson (this, writer); + + writer.Validate = old_validate; + } + + public override string ToString () + { + switch (type) { + case JsonType.Array: + return "JsonData array"; + + case JsonType.Boolean: + return inst_boolean.ToString (); + + case JsonType.Double: + return inst_double.ToString (); + + case JsonType.Int: + return inst_int.ToString (); + + case JsonType.Long: + return inst_long.ToString (); + + case JsonType.Object: + return "JsonData object"; + + case JsonType.String: + return inst_string; + } + + return "Uninitialized JsonData"; + } + } + + + internal class OrderedDictionaryEnumerator : IDictionaryEnumerator + { + IEnumerator> list_enumerator; + + + public object Current { + get { return Entry; } + } + + public DictionaryEntry Entry { + get { + KeyValuePair curr = list_enumerator.Current; + return new DictionaryEntry (curr.Key, curr.Value); + } + } + + public object Key { + get { return list_enumerator.Current.Key; } + } + + public object Value { + get { return list_enumerator.Current.Value; } + } + + + public OrderedDictionaryEnumerator ( + IEnumerator> enumerator) + { + list_enumerator = enumerator; + } + + + public bool MoveNext () + { + return list_enumerator.MoveNext (); + } + + public void Reset () + { + list_enumerator.Reset (); + } + } +} diff --git a/src/Cake.Issues/Serialization/LitJson/JsonException.cs b/src/Cake.Issues/Serialization/LitJson/JsonException.cs new file mode 100644 index 000000000..721d400bb --- /dev/null +++ b/src/Cake.Issues/Serialization/LitJson/JsonException.cs @@ -0,0 +1,71 @@ +#pragma warning disable 1587 +#pragma warning disable 1591 + +#region Header +/** + * JsonException.cs + * Base class throwed by LitJSON when a parsing error occurs. + * + * The authors disclaim copyright to this source code. For more details, see + * the 3rd-Party-License.md file included with this distribution. + **/ + +// This file isn't generated, but this comment is necessary to exclude it from code analysis. +// +#endregion + + +using System; + + +namespace LitJson +{ + public class JsonException : +#if NETSTANDARD1_5 + Exception +#else + ApplicationException +#endif + { + public JsonException () : base () + { + } + + internal JsonException (ParserToken token) : + base (String.Format ( + "Invalid token '{0}' in input string", token)) + { + } + + internal JsonException (ParserToken token, + Exception inner_exception) : + base (String.Format ( + "Invalid token '{0}' in input string", token), + inner_exception) + { + } + + internal JsonException (int c) : + base (String.Format ( + "Invalid character '{0}' in input string", (char) c)) + { + } + + internal JsonException (int c, Exception inner_exception) : + base (String.Format ( + "Invalid character '{0}' in input string", (char) c), + inner_exception) + { + } + + + public JsonException (string message) : base (message) + { + } + + public JsonException (string message, Exception inner_exception) : + base (message, inner_exception) + { + } + } +} diff --git a/src/Cake.Issues/Serialization/LitJson/JsonMapper.cs b/src/Cake.Issues/Serialization/LitJson/JsonMapper.cs new file mode 100644 index 000000000..36690405e --- /dev/null +++ b/src/Cake.Issues/Serialization/LitJson/JsonMapper.cs @@ -0,0 +1,967 @@ +#pragma warning disable 1587 +#pragma warning disable 1591 + +#region Header +/** + * JsonMapper.cs + * JSON to .Net object and object to JSON conversions. + * + * The authors disclaim copyright to this source code. For more details, see + * the 3rd-Party-License.md file included with this distribution. + **/ + +// This file isn't generated, but this comment is necessary to exclude it from code analysis. +// +#endregion + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Reflection; + + +namespace LitJson +{ + internal struct PropertyMetadata + { + public MemberInfo Info; + public bool IsField; + public Type Type; + } + + + internal struct ArrayMetadata + { + private Type element_type; + private bool is_array; + private bool is_list; + + + public Type ElementType { + get { + if (element_type == null) + return typeof (JsonData); + + return element_type; + } + + set { element_type = value; } + } + + public bool IsArray { + get { return is_array; } + set { is_array = value; } + } + + public bool IsList { + get { return is_list; } + set { is_list = value; } + } + } + + + internal struct ObjectMetadata + { + private Type element_type; + private bool is_dictionary; + + private IDictionary properties; + + + public Type ElementType { + get { + if (element_type == null) + return typeof (JsonData); + + return element_type; + } + + set { element_type = value; } + } + + public bool IsDictionary { + get { return is_dictionary; } + set { is_dictionary = value; } + } + + public IDictionary Properties { + get { return properties; } + set { properties = value; } + } + } + + + internal delegate void ExporterFunc (object obj, JsonWriter writer); + public delegate void ExporterFunc (T obj, JsonWriter writer); + + internal delegate object ImporterFunc (object input); + public delegate TValue ImporterFunc (TJson input); + + public delegate IJsonWrapper WrapperFactory (); + + + public class JsonMapper + { + #region Fields + private static readonly int max_nesting_depth; + + private static readonly IFormatProvider datetime_format; + + private static readonly IDictionary base_exporters_table; + private static readonly IDictionary custom_exporters_table; + + private static readonly IDictionary> base_importers_table; + private static readonly IDictionary> custom_importers_table; + + private static readonly IDictionary array_metadata; + private static readonly object array_metadata_lock = new Object (); + + private static readonly IDictionary> conv_ops; + private static readonly object conv_ops_lock = new Object (); + + private static readonly IDictionary object_metadata; + private static readonly object object_metadata_lock = new Object (); + + private static readonly IDictionary> type_properties; + private static readonly object type_properties_lock = new Object (); + + private static readonly JsonWriter static_writer; + private static readonly object static_writer_lock = new Object (); + #endregion + + + #region Constructors + static JsonMapper () + { + max_nesting_depth = 100; + + array_metadata = new Dictionary (); + conv_ops = new Dictionary> (); + object_metadata = new Dictionary (); + type_properties = new Dictionary> (); + + static_writer = new JsonWriter (); + + datetime_format = DateTimeFormatInfo.InvariantInfo; + + base_exporters_table = new Dictionary (); + custom_exporters_table = new Dictionary (); + + base_importers_table = new Dictionary> (); + custom_importers_table = new Dictionary> (); + + RegisterBaseExporters (); + RegisterBaseImporters (); + } + #endregion + + + #region Private Methods + private static void AddArrayMetadata (Type type) + { + if (array_metadata.ContainsKey (type)) + return; + + ArrayMetadata data = new ArrayMetadata (); + + data.IsArray = type.IsArray; + + if (type.GetInterface ("System.Collections.IList") != null) + data.IsList = true; + + foreach (PropertyInfo p_info in type.GetProperties ()) { + if (p_info.Name != "Item") + continue; + + ParameterInfo[] parameters = p_info.GetIndexParameters (); + + if (parameters.Length != 1) + continue; + + if (parameters[0].ParameterType == typeof (int)) + data.ElementType = p_info.PropertyType; + } + + lock (array_metadata_lock) { + try { + array_metadata.Add (type, data); + } catch (ArgumentException) { + return; + } + } + } + + private static void AddObjectMetadata (Type type) + { + if (object_metadata.ContainsKey (type)) + return; + + ObjectMetadata data = new ObjectMetadata (); + + if (type.GetInterface ("System.Collections.IDictionary") != null) + data.IsDictionary = true; + + data.Properties = new Dictionary (); + + foreach (PropertyInfo p_info in type.GetProperties ()) { + if (p_info.Name == "Item") { + ParameterInfo[] parameters = p_info.GetIndexParameters (); + + if (parameters.Length != 1) + continue; + + if (parameters[0].ParameterType == typeof (string)) + data.ElementType = p_info.PropertyType; + + continue; + } + + PropertyMetadata p_data = new PropertyMetadata (); + p_data.Info = p_info; + p_data.Type = p_info.PropertyType; + + data.Properties.Add (p_info.Name, p_data); + } + + foreach (FieldInfo f_info in type.GetFields ()) { + PropertyMetadata p_data = new PropertyMetadata (); + p_data.Info = f_info; + p_data.IsField = true; + p_data.Type = f_info.FieldType; + + data.Properties.Add (f_info.Name, p_data); + } + + lock (object_metadata_lock) { + try { + object_metadata.Add (type, data); + } catch (ArgumentException) { + return; + } + } + } + + private static void AddTypeProperties (Type type) + { + if (type_properties.ContainsKey (type)) + return; + + IList props = new List (); + + foreach (PropertyInfo p_info in type.GetProperties ()) { + if (p_info.Name == "Item") + continue; + + PropertyMetadata p_data = new PropertyMetadata (); + p_data.Info = p_info; + p_data.IsField = false; + props.Add (p_data); + } + + foreach (FieldInfo f_info in type.GetFields ()) { + PropertyMetadata p_data = new PropertyMetadata (); + p_data.Info = f_info; + p_data.IsField = true; + + props.Add (p_data); + } + + lock (type_properties_lock) { + try { + type_properties.Add (type, props); + } catch (ArgumentException) { + return; + } + } + } + + private static MethodInfo GetConvOp (Type t1, Type t2) + { + lock (conv_ops_lock) { + if (! conv_ops.ContainsKey (t1)) + conv_ops.Add (t1, new Dictionary ()); + } + + if (conv_ops[t1].ContainsKey (t2)) + return conv_ops[t1][t2]; + + MethodInfo op = t1.GetMethod ( + "op_Implicit", new Type[] { t2 }); + + lock (conv_ops_lock) { + try { + conv_ops[t1].Add (t2, op); + } catch (ArgumentException) { + return conv_ops[t1][t2]; + } + } + + return op; + } + + private static object ReadValue (Type inst_type, JsonReader reader) + { + reader.Read (); + + if (reader.Token == JsonToken.ArrayEnd) + return null; + + Type underlying_type = Nullable.GetUnderlyingType(inst_type); + Type value_type = underlying_type ?? inst_type; + + if (reader.Token == JsonToken.Null) { + #if NETSTANDARD1_5 + if (inst_type.IsClass() || underlying_type != null) { + return null; + } + #else + if (inst_type.IsClass || underlying_type != null) { + return null; + } + #endif + + throw new JsonException (String.Format ( + "Can't assign null to an instance of type {0}", + inst_type)); + } + + if (reader.Token == JsonToken.Double || + reader.Token == JsonToken.Int || + reader.Token == JsonToken.Long || + reader.Token == JsonToken.String || + reader.Token == JsonToken.Boolean) { + + Type json_type = reader.Value.GetType (); + + if (value_type.IsAssignableFrom (json_type)) + return reader.Value; + + // If there's a custom importer that fits, use it + if (custom_importers_table.ContainsKey (json_type) && + custom_importers_table[json_type].ContainsKey ( + value_type)) { + + ImporterFunc importer = + custom_importers_table[json_type][value_type]; + + return importer (reader.Value); + } + + // Maybe there's a base importer that works + if (base_importers_table.ContainsKey (json_type) && + base_importers_table[json_type].ContainsKey ( + value_type)) { + + ImporterFunc importer = + base_importers_table[json_type][value_type]; + + return importer (reader.Value); + } + + // Maybe it's an enum + #if NETSTANDARD1_5 + if (value_type.IsEnum()) + return Enum.ToObject (value_type, reader.Value); + #else + if (value_type.IsEnum) + return Enum.ToObject (value_type, reader.Value); + #endif + // Try using an implicit conversion operator + MethodInfo conv_op = GetConvOp (value_type, json_type); + + if (conv_op != null) + return conv_op.Invoke (null, + new object[] { reader.Value }); + + // No luck + throw new JsonException (String.Format ( + "Can't assign value '{0}' (type {1}) to type {2}", + reader.Value, json_type, inst_type)); + } + + object instance = null; + + if (reader.Token == JsonToken.ArrayStart) { + + AddArrayMetadata (inst_type); + ArrayMetadata t_data = array_metadata[inst_type]; + + if (! t_data.IsArray && ! t_data.IsList) + throw new JsonException (String.Format ( + "Type {0} can't act as an array", + inst_type)); + + IList list; + Type elem_type; + + if (! t_data.IsArray) { + list = (IList) Activator.CreateInstance (inst_type); + elem_type = t_data.ElementType; + } else { + list = new ArrayList (); + elem_type = inst_type.GetElementType (); + } + + while (true) { + object item = ReadValue (elem_type, reader); + if (item == null && reader.Token == JsonToken.ArrayEnd) + break; + + list.Add (item); + } + + if (t_data.IsArray) { + int n = list.Count; + instance = Array.CreateInstance (elem_type, n); + + for (int i = 0; i < n; i++) + ((Array) instance).SetValue (list[i], i); + } else + instance = list; + + } else if (reader.Token == JsonToken.ObjectStart) { + AddObjectMetadata (value_type); + ObjectMetadata t_data = object_metadata[value_type]; + + instance = Activator.CreateInstance (value_type); + + while (true) { + reader.Read (); + + if (reader.Token == JsonToken.ObjectEnd) + break; + + string property = (string) reader.Value; + + if (t_data.Properties.ContainsKey (property)) { + PropertyMetadata prop_data = + t_data.Properties[property]; + + if (prop_data.IsField) { + ((FieldInfo) prop_data.Info).SetValue ( + instance, ReadValue (prop_data.Type, reader)); + } else { + PropertyInfo p_info = + (PropertyInfo) prop_data.Info; + + if (p_info.CanWrite) + p_info.SetValue ( + instance, + ReadValue (prop_data.Type, reader), + null); + else + ReadValue (prop_data.Type, reader); + } + + } else { + if (! t_data.IsDictionary) { + + if (! reader.SkipNonMembers) { + throw new JsonException (String.Format ( + "The type {0} doesn't have the " + + "property '{1}'", + inst_type, property)); + } else { + ReadSkip (reader); + continue; + } + } + + ((IDictionary) instance).Add ( + property, ReadValue ( + t_data.ElementType, reader)); + } + + } + + } + + return instance; + } + + private static IJsonWrapper ReadValue (WrapperFactory factory, + JsonReader reader) + { + reader.Read (); + + if (reader.Token == JsonToken.ArrayEnd || + reader.Token == JsonToken.Null) + return null; + + IJsonWrapper instance = factory (); + + if (reader.Token == JsonToken.String) { + instance.SetString ((string) reader.Value); + return instance; + } + + if (reader.Token == JsonToken.Double) { + instance.SetDouble ((double) reader.Value); + return instance; + } + + if (reader.Token == JsonToken.Int) { + instance.SetInt ((int) reader.Value); + return instance; + } + + if (reader.Token == JsonToken.Long) { + instance.SetLong ((long) reader.Value); + return instance; + } + + if (reader.Token == JsonToken.Boolean) { + instance.SetBoolean ((bool) reader.Value); + return instance; + } + + if (reader.Token == JsonToken.ArrayStart) { + instance.SetJsonType (JsonType.Array); + + while (true) { + IJsonWrapper item = ReadValue (factory, reader); + if (item == null && reader.Token == JsonToken.ArrayEnd) + break; + + ((IList) instance).Add (item); + } + } + else if (reader.Token == JsonToken.ObjectStart) { + instance.SetJsonType (JsonType.Object); + + while (true) { + reader.Read (); + + if (reader.Token == JsonToken.ObjectEnd) + break; + + string property = (string) reader.Value; + + ((IDictionary) instance)[property] = ReadValue ( + factory, reader); + } + + } + + return instance; + } + + private static void ReadSkip (JsonReader reader) + { + ToWrapper ( + delegate { return new JsonMockWrapper (); }, reader); + } + + private static void RegisterBaseExporters () + { + base_exporters_table[typeof (byte)] = + delegate (object obj, JsonWriter writer) { + writer.Write (Convert.ToInt32 ((byte) obj)); + }; + + base_exporters_table[typeof (char)] = + delegate (object obj, JsonWriter writer) { + writer.Write (Convert.ToString ((char) obj)); + }; + + base_exporters_table[typeof (DateTime)] = + delegate (object obj, JsonWriter writer) { + writer.Write (Convert.ToString ((DateTime) obj, + datetime_format)); + }; + + base_exporters_table[typeof (decimal)] = + delegate (object obj, JsonWriter writer) { + writer.Write ((decimal) obj); + }; + + base_exporters_table[typeof (sbyte)] = + delegate (object obj, JsonWriter writer) { + writer.Write (Convert.ToInt32 ((sbyte) obj)); + }; + + base_exporters_table[typeof (short)] = + delegate (object obj, JsonWriter writer) { + writer.Write (Convert.ToInt32 ((short) obj)); + }; + + base_exporters_table[typeof (ushort)] = + delegate (object obj, JsonWriter writer) { + writer.Write (Convert.ToInt32 ((ushort) obj)); + }; + + base_exporters_table[typeof (uint)] = + delegate (object obj, JsonWriter writer) { + writer.Write (Convert.ToUInt64 ((uint) obj)); + }; + + base_exporters_table[typeof (ulong)] = + delegate (object obj, JsonWriter writer) { + writer.Write ((ulong) obj); + }; + + base_exporters_table[typeof(DateTimeOffset)] = + delegate (object obj, JsonWriter writer) { + writer.Write(((DateTimeOffset)obj).ToString("yyyy-MM-ddTHH:mm:ss.fffffffzzz", datetime_format)); + }; + } + + private static void RegisterBaseImporters () + { + ImporterFunc importer; + + importer = delegate (object input) { + return Convert.ToByte ((int) input); + }; + RegisterImporter (base_importers_table, typeof (int), + typeof (byte), importer); + + importer = delegate (object input) { + return Convert.ToUInt64 ((int) input); + }; + RegisterImporter (base_importers_table, typeof (int), + typeof (ulong), importer); + + importer = delegate (object input) { + return Convert.ToInt64((int)input); + }; + RegisterImporter(base_importers_table, typeof(int), + typeof(long), importer); + + importer = delegate (object input) { + return Convert.ToSByte ((int) input); + }; + RegisterImporter (base_importers_table, typeof (int), + typeof (sbyte), importer); + + importer = delegate (object input) { + return Convert.ToInt16 ((int) input); + }; + RegisterImporter (base_importers_table, typeof (int), + typeof (short), importer); + + importer = delegate (object input) { + return Convert.ToUInt16 ((int) input); + }; + RegisterImporter (base_importers_table, typeof (int), + typeof (ushort), importer); + + importer = delegate (object input) { + return Convert.ToUInt32 ((int) input); + }; + RegisterImporter (base_importers_table, typeof (int), + typeof (uint), importer); + + importer = delegate (object input) { + return Convert.ToSingle ((int) input); + }; + RegisterImporter (base_importers_table, typeof (int), + typeof (float), importer); + + importer = delegate (object input) { + return Convert.ToDouble ((int) input); + }; + RegisterImporter (base_importers_table, typeof (int), + typeof (double), importer); + + importer = delegate (object input) { + return Convert.ToDecimal ((double) input); + }; + RegisterImporter (base_importers_table, typeof (double), + typeof (decimal), importer); + + + importer = delegate (object input) { + return Convert.ToUInt32 ((long) input); + }; + RegisterImporter (base_importers_table, typeof (long), + typeof (uint), importer); + + importer = delegate (object input) { + return Convert.ToChar ((string) input); + }; + RegisterImporter (base_importers_table, typeof (string), + typeof (char), importer); + + importer = delegate (object input) { + return Convert.ToDateTime ((string) input, datetime_format); + }; + RegisterImporter (base_importers_table, typeof (string), + typeof (DateTime), importer); + + importer = delegate (object input) { + return DateTimeOffset.Parse((string)input, datetime_format); + }; + RegisterImporter(base_importers_table, typeof(string), + typeof(DateTimeOffset), importer); + } + + private static void RegisterImporter ( + IDictionary> table, + Type json_type, Type value_type, ImporterFunc importer) + { + if (! table.ContainsKey (json_type)) + table.Add (json_type, new Dictionary ()); + + table[json_type][value_type] = importer; + } + + private static void WriteValue (object obj, JsonWriter writer, + bool writer_is_private, + int depth) + { + if (depth > max_nesting_depth) + throw new JsonException ( + String.Format ("Max allowed object depth reached while " + + "trying to export from type {0}", + obj.GetType ())); + + if (obj == null) { + writer.Write (null); + return; + } + + if (obj is IJsonWrapper) { + if (writer_is_private) + writer.TextWriter.Write (((IJsonWrapper) obj).ToJson ()); + else + ((IJsonWrapper) obj).ToJson (writer); + + return; + } + + if (obj is String) { + writer.Write ((string) obj); + return; + } + + if (obj is Double) { + writer.Write ((double) obj); + return; + } + + if (obj is Int32) { + writer.Write ((int) obj); + return; + } + + if (obj is Boolean) { + writer.Write ((bool) obj); + return; + } + + if (obj is Int64) { + writer.Write ((long) obj); + return; + } + + if (obj is Array) { + writer.WriteArrayStart (); + + foreach (object elem in (Array) obj) + WriteValue (elem, writer, writer_is_private, depth + 1); + + writer.WriteArrayEnd (); + + return; + } + + if (obj is IList) { + writer.WriteArrayStart (); + foreach (object elem in (IList) obj) + WriteValue (elem, writer, writer_is_private, depth + 1); + writer.WriteArrayEnd (); + + return; + } + + if (obj is IDictionary) { + writer.WriteObjectStart (); + foreach (DictionaryEntry entry in (IDictionary) obj) { + writer.WritePropertyName ((string) entry.Key); + WriteValue (entry.Value, writer, writer_is_private, + depth + 1); + } + writer.WriteObjectEnd (); + + return; + } + + Type obj_type = obj.GetType (); + + // See if there's a custom exporter for the object + if (custom_exporters_table.ContainsKey (obj_type)) { + ExporterFunc exporter = custom_exporters_table[obj_type]; + exporter (obj, writer); + + return; + } + + // If not, maybe there's a base exporter + if (base_exporters_table.ContainsKey (obj_type)) { + ExporterFunc exporter = base_exporters_table[obj_type]; + exporter (obj, writer); + + return; + } + + // Last option, let's see if it's an enum + if (obj is Enum) { + Type e_type = Enum.GetUnderlyingType (obj_type); + + if (e_type == typeof (long) + || e_type == typeof (uint) + || e_type == typeof (ulong)) + writer.Write ((ulong) obj); + else + writer.Write ((int) obj); + + return; + } + + // Okay, so it looks like the input should be exported as an + // object + AddTypeProperties (obj_type); + IList props = type_properties[obj_type]; + + writer.WriteObjectStart (); + foreach (PropertyMetadata p_data in props) { + if (p_data.IsField) { + writer.WritePropertyName (p_data.Info.Name); + WriteValue (((FieldInfo) p_data.Info).GetValue (obj), + writer, writer_is_private, depth + 1); + } + else { + PropertyInfo p_info = (PropertyInfo) p_data.Info; + + if (p_info.CanRead) { + writer.WritePropertyName (p_data.Info.Name); + WriteValue (p_info.GetValue (obj, null), + writer, writer_is_private, depth + 1); + } + } + } + writer.WriteObjectEnd (); + } + #endregion + + + public static string ToJson (object obj) + { + lock (static_writer_lock) { + static_writer.Reset (); + + WriteValue (obj, static_writer, true, 0); + + return static_writer.ToString (); + } + } + + public static void ToJson (object obj, JsonWriter writer) + { + WriteValue (obj, writer, false, 0); + } + + public static JsonData ToObject (JsonReader reader) + { + return (JsonData) ToWrapper ( + delegate { return new JsonData (); }, reader); + } + + public static JsonData ToObject (TextReader reader) + { + JsonReader json_reader = new JsonReader (reader); + + return (JsonData) ToWrapper ( + delegate { return new JsonData (); }, json_reader); + } + + public static JsonData ToObject (string json) + { + return (JsonData) ToWrapper ( + delegate { return new JsonData (); }, json); + } + + public static T ToObject (JsonReader reader) + { + return (T) ReadValue (typeof (T), reader); + } + + public static T ToObject (TextReader reader) + { + JsonReader json_reader = new JsonReader (reader); + + return (T) ReadValue (typeof (T), json_reader); + } + + public static T ToObject (string json) + { + JsonReader reader = new JsonReader (json); + + return (T) ReadValue (typeof (T), reader); + } + + public static object ToObject(string json, Type ConvertType ) + { + JsonReader reader = new JsonReader(json); + + return ReadValue(ConvertType, reader); + } + + public static IJsonWrapper ToWrapper (WrapperFactory factory, + JsonReader reader) + { + return ReadValue (factory, reader); + } + + public static IJsonWrapper ToWrapper (WrapperFactory factory, + string json) + { + JsonReader reader = new JsonReader (json); + + return ReadValue (factory, reader); + } + + public static void RegisterExporter (ExporterFunc exporter) + { + ExporterFunc exporter_wrapper = + delegate (object obj, JsonWriter writer) { + exporter ((T) obj, writer); + }; + + custom_exporters_table[typeof (T)] = exporter_wrapper; + } + + public static void RegisterImporter ( + ImporterFunc importer) + { + ImporterFunc importer_wrapper = + delegate (object input) { + return importer ((TJson) input); + }; + + RegisterImporter (custom_importers_table, typeof (TJson), + typeof (TValue), importer_wrapper); + } + + public static void UnregisterExporters () + { + custom_exporters_table.Clear (); + } + + public static void UnregisterImporters () + { + custom_importers_table.Clear (); + } + } +} diff --git a/src/Cake.Issues/Serialization/LitJson/JsonMockWrapper.cs b/src/Cake.Issues/Serialization/LitJson/JsonMockWrapper.cs new file mode 100644 index 000000000..5dce9086d --- /dev/null +++ b/src/Cake.Issues/Serialization/LitJson/JsonMockWrapper.cs @@ -0,0 +1,111 @@ +#pragma warning disable 1587 +#pragma warning disable 1591 + +#region Header +/** + * JsonMockWrapper.cs + * Mock object implementing IJsonWrapper, to facilitate actions like + * skipping data more efficiently. + * + * The authors disclaim copyright to this source code. For more details, see + * the 3rd-Party-License.md file included with this distribution. + **/ + +// This file isn't generated, but this comment is necessary to exclude it from code analysis. +// +#endregion + + +using System; +using System.Collections; +using System.Collections.Specialized; + + +namespace LitJson +{ + public class JsonMockWrapper : IJsonWrapper + { + public bool IsArray { get { return false; } } + public bool IsBoolean { get { return false; } } + public bool IsDouble { get { return false; } } + public bool IsInt { get { return false; } } + public bool IsLong { get { return false; } } + public bool IsObject { get { return false; } } + public bool IsString { get { return false; } } + + public bool GetBoolean () { return false; } + public double GetDouble () { return 0.0; } + public int GetInt () { return 0; } + public JsonType GetJsonType () { return JsonType.None; } + public long GetLong () { return 0L; } + public string GetString () { return ""; } + + public void SetBoolean (bool val) {} + public void SetDouble (double val) {} + public void SetInt (int val) {} + public void SetJsonType (JsonType type) {} + public void SetLong (long val) {} + public void SetString (string val) {} + + public string ToJson () { return ""; } + public void ToJson (JsonWriter writer) {} + + + bool IList.IsFixedSize { get { return true; } } + bool IList.IsReadOnly { get { return true; } } + + object IList.this[int index] { + get { return null; } + set {} + } + + int IList.Add (object value) { return 0; } + void IList.Clear () {} + bool IList.Contains (object value) { return false; } + int IList.IndexOf (object value) { return -1; } + void IList.Insert (int i, object v) {} + void IList.Remove (object value) {} + void IList.RemoveAt (int index) {} + + + int ICollection.Count { get { return 0; } } + bool ICollection.IsSynchronized { get { return false; } } + object ICollection.SyncRoot { get { return null; } } + + void ICollection.CopyTo (Array array, int index) {} + + + IEnumerator IEnumerable.GetEnumerator () { return null; } + + + bool IDictionary.IsFixedSize { get { return true; } } + bool IDictionary.IsReadOnly { get { return true; } } + + ICollection IDictionary.Keys { get { return null; } } + ICollection IDictionary.Values { get { return null; } } + + object IDictionary.this[object key] { + get { return null; } + set {} + } + + void IDictionary.Add (object k, object v) {} + void IDictionary.Clear () {} + bool IDictionary.Contains (object key) { return false; } + void IDictionary.Remove (object key) {} + + IDictionaryEnumerator IDictionary.GetEnumerator () { return null; } + + + object IOrderedDictionary.this[int idx] { + get { return null; } + set {} + } + + IDictionaryEnumerator IOrderedDictionary.GetEnumerator () { + return null; + } + void IOrderedDictionary.Insert (int i, object k, object v) {} + void IOrderedDictionary.RemoveAt (int i) {} + } +} diff --git a/src/Cake.Issues/Serialization/LitJson/JsonReader.cs b/src/Cake.Issues/Serialization/LitJson/JsonReader.cs new file mode 100644 index 000000000..8d6496507 --- /dev/null +++ b/src/Cake.Issues/Serialization/LitJson/JsonReader.cs @@ -0,0 +1,484 @@ +#pragma warning disable 1587 +#pragma warning disable 1591 + +#region Header +/** + * JsonReader.cs + * Stream-like access to JSON text. + * + * The authors disclaim copyright to this source code. For more details, see + * the 3rd-Party-License.md file included with this distribution. + **/ + +// This file isn't generated, but this comment is necessary to exclude it from code analysis. +// +#endregion + + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Text; + + +namespace LitJson +{ + public enum JsonToken + { + None, + + ObjectStart, + PropertyName, + ObjectEnd, + + ArrayStart, + ArrayEnd, + + Int, + Long, + Double, + + String, + + Boolean, + Null + } + + + public class JsonReader + { + #region Fields + private static readonly IDictionary> parse_table; + + private Stack automaton_stack; + private int current_input; + private int current_symbol; + private bool end_of_json; + private bool end_of_input; + private Lexer lexer; + private bool parser_in_string; + private bool parser_return; + private bool read_started; + private TextReader reader; + private bool reader_is_owned; + private bool skip_non_members; + private object token_value; + private JsonToken token; + #endregion + + + #region Public Properties + public bool AllowComments { + get { return lexer.AllowComments; } + set { lexer.AllowComments = value; } + } + + public bool AllowSingleQuotedStrings { + get { return lexer.AllowSingleQuotedStrings; } + set { lexer.AllowSingleQuotedStrings = value; } + } + + public bool SkipNonMembers { + get { return skip_non_members; } + set { skip_non_members = value; } + } + + public bool EndOfInput { + get { return end_of_input; } + } + + public bool EndOfJson { + get { return end_of_json; } + } + + public JsonToken Token { + get { return token; } + } + + public object Value { + get { return token_value; } + } + #endregion + + + #region Constructors + static JsonReader () + { + parse_table = PopulateParseTable (); + } + + public JsonReader (string json_text) : + this (new StringReader (json_text), true) + { + } + + public JsonReader (TextReader reader) : + this (reader, false) + { + } + + private JsonReader (TextReader reader, bool owned) + { + if (reader == null) + throw new ArgumentNullException ("reader"); + + parser_in_string = false; + parser_return = false; + + read_started = false; + automaton_stack = new Stack (); + automaton_stack.Push ((int) ParserToken.End); + automaton_stack.Push ((int) ParserToken.Text); + + lexer = new Lexer (reader); + + end_of_input = false; + end_of_json = false; + + skip_non_members = true; + + this.reader = reader; + reader_is_owned = owned; + } + #endregion + + + #region Static Methods + private static IDictionary> PopulateParseTable () + { + // See section A.2. of the manual for details + IDictionary> parse_table = new Dictionary> (); + + TableAddRow (parse_table, ParserToken.Array); + TableAddCol (parse_table, ParserToken.Array, '[', + '[', + (int) ParserToken.ArrayPrime); + + TableAddRow (parse_table, ParserToken.ArrayPrime); + TableAddCol (parse_table, ParserToken.ArrayPrime, '"', + (int) ParserToken.Value, + + (int) ParserToken.ValueRest, + ']'); + TableAddCol (parse_table, ParserToken.ArrayPrime, '[', + (int) ParserToken.Value, + (int) ParserToken.ValueRest, + ']'); + TableAddCol (parse_table, ParserToken.ArrayPrime, ']', + ']'); + TableAddCol (parse_table, ParserToken.ArrayPrime, '{', + (int) ParserToken.Value, + (int) ParserToken.ValueRest, + ']'); + TableAddCol (parse_table, ParserToken.ArrayPrime, (int) ParserToken.Number, + (int) ParserToken.Value, + (int) ParserToken.ValueRest, + ']'); + TableAddCol (parse_table, ParserToken.ArrayPrime, (int) ParserToken.True, + (int) ParserToken.Value, + (int) ParserToken.ValueRest, + ']'); + TableAddCol (parse_table, ParserToken.ArrayPrime, (int) ParserToken.False, + (int) ParserToken.Value, + (int) ParserToken.ValueRest, + ']'); + TableAddCol (parse_table, ParserToken.ArrayPrime, (int) ParserToken.Null, + (int) ParserToken.Value, + (int) ParserToken.ValueRest, + ']'); + + TableAddRow (parse_table, ParserToken.Object); + TableAddCol (parse_table, ParserToken.Object, '{', + '{', + (int) ParserToken.ObjectPrime); + + TableAddRow (parse_table, ParserToken.ObjectPrime); + TableAddCol (parse_table, ParserToken.ObjectPrime, '"', + (int) ParserToken.Pair, + (int) ParserToken.PairRest, + '}'); + TableAddCol (parse_table, ParserToken.ObjectPrime, '}', + '}'); + + TableAddRow (parse_table, ParserToken.Pair); + TableAddCol (parse_table, ParserToken.Pair, '"', + (int) ParserToken.String, + ':', + (int) ParserToken.Value); + + TableAddRow (parse_table, ParserToken.PairRest); + TableAddCol (parse_table, ParserToken.PairRest, ',', + ',', + (int) ParserToken.Pair, + (int) ParserToken.PairRest); + TableAddCol (parse_table, ParserToken.PairRest, '}', + (int) ParserToken.Epsilon); + + TableAddRow (parse_table, ParserToken.String); + TableAddCol (parse_table, ParserToken.String, '"', + '"', + (int) ParserToken.CharSeq, + '"'); + + TableAddRow (parse_table, ParserToken.Text); + TableAddCol (parse_table, ParserToken.Text, '[', + (int) ParserToken.Array); + TableAddCol (parse_table, ParserToken.Text, '{', + (int) ParserToken.Object); + + TableAddRow (parse_table, ParserToken.Value); + TableAddCol (parse_table, ParserToken.Value, '"', + (int) ParserToken.String); + TableAddCol (parse_table, ParserToken.Value, '[', + (int) ParserToken.Array); + TableAddCol (parse_table, ParserToken.Value, '{', + (int) ParserToken.Object); + TableAddCol (parse_table, ParserToken.Value, (int) ParserToken.Number, + (int) ParserToken.Number); + TableAddCol (parse_table, ParserToken.Value, (int) ParserToken.True, + (int) ParserToken.True); + TableAddCol (parse_table, ParserToken.Value, (int) ParserToken.False, + (int) ParserToken.False); + TableAddCol (parse_table, ParserToken.Value, (int) ParserToken.Null, + (int) ParserToken.Null); + + TableAddRow (parse_table, ParserToken.ValueRest); + TableAddCol (parse_table, ParserToken.ValueRest, ',', + ',', + (int) ParserToken.Value, + (int) ParserToken.ValueRest); + TableAddCol (parse_table, ParserToken.ValueRest, ']', + (int) ParserToken.Epsilon); + + return parse_table; + } + + private static void TableAddCol (IDictionary> parse_table, ParserToken row, int col, + params int[] symbols) + { + parse_table[(int) row].Add (col, symbols); + } + + private static void TableAddRow (IDictionary> parse_table, ParserToken rule) + { + parse_table.Add ((int) rule, new Dictionary ()); + } + #endregion + + + #region Private Methods + private void ProcessNumber (string number) + { + if (number.IndexOf ('.') != -1 || + number.IndexOf ('e') != -1 || + number.IndexOf ('E') != -1) { + + double n_double; + if (double.TryParse (number, NumberStyles.Any, CultureInfo.InvariantCulture, out n_double)) { + token = JsonToken.Double; + token_value = n_double; + + return; + } + } + + int n_int32; + if (int.TryParse (number, NumberStyles.Integer, CultureInfo.InvariantCulture, out n_int32)) { + token = JsonToken.Int; + token_value = n_int32; + + return; + } + + long n_int64; + if (long.TryParse (number, NumberStyles.Integer, CultureInfo.InvariantCulture, out n_int64)) { + token = JsonToken.Long; + token_value = n_int64; + + return; + } + + ulong n_uint64; + if (ulong.TryParse(number, NumberStyles.Integer, CultureInfo.InvariantCulture, out n_uint64)) + { + token = JsonToken.Long; + token_value = n_uint64; + + return; + } + + // Shouldn't happen, but just in case, return something + token = JsonToken.Int; + token_value = 0; + } + + private void ProcessSymbol () + { + if (current_symbol == '[') { + token = JsonToken.ArrayStart; + parser_return = true; + + } else if (current_symbol == ']') { + token = JsonToken.ArrayEnd; + parser_return = true; + + } else if (current_symbol == '{') { + token = JsonToken.ObjectStart; + parser_return = true; + + } else if (current_symbol == '}') { + token = JsonToken.ObjectEnd; + parser_return = true; + + } else if (current_symbol == '"') { + if (parser_in_string) { + parser_in_string = false; + + parser_return = true; + + } else { + if (token == JsonToken.None) + token = JsonToken.String; + + parser_in_string = true; + } + + } else if (current_symbol == (int) ParserToken.CharSeq) { + token_value = lexer.StringValue; + + } else if (current_symbol == (int) ParserToken.False) { + token = JsonToken.Boolean; + token_value = false; + parser_return = true; + + } else if (current_symbol == (int) ParserToken.Null) { + token = JsonToken.Null; + parser_return = true; + + } else if (current_symbol == (int) ParserToken.Number) { + ProcessNumber (lexer.StringValue); + + parser_return = true; + + } else if (current_symbol == (int) ParserToken.Pair) { + token = JsonToken.PropertyName; + + } else if (current_symbol == (int) ParserToken.True) { + token = JsonToken.Boolean; + token_value = true; + parser_return = true; + + } + } + + private bool ReadToken () + { + if (end_of_input) + return false; + + lexer.NextToken (); + + if (lexer.EndOfInput) { + Close (); + + return false; + } + + current_input = lexer.Token; + + return true; + } + #endregion + + + public void Close () + { + if (end_of_input) + return; + + end_of_input = true; + end_of_json = true; + + if (reader_is_owned) + { + using(reader){} + } + + reader = null; + } + + public bool Read () + { + if (end_of_input) + return false; + + if (end_of_json) { + end_of_json = false; + automaton_stack.Clear (); + automaton_stack.Push ((int) ParserToken.End); + automaton_stack.Push ((int) ParserToken.Text); + } + + parser_in_string = false; + parser_return = false; + + token = JsonToken.None; + token_value = null; + + if (! read_started) { + read_started = true; + + if (! ReadToken ()) + return false; + } + + + int[] entry_symbols; + + while (true) { + if (parser_return) { + if (automaton_stack.Peek () == (int) ParserToken.End) + end_of_json = true; + + return true; + } + + current_symbol = automaton_stack.Pop (); + + ProcessSymbol (); + + if (current_symbol == current_input) { + if (! ReadToken ()) { + if (automaton_stack.Peek () != (int) ParserToken.End) + throw new JsonException ( + "Input doesn't evaluate to proper JSON text"); + + if (parser_return) + return true; + + return false; + } + + continue; + } + + try { + + entry_symbols = + parse_table[current_symbol][current_input]; + + } catch (KeyNotFoundException e) { + throw new JsonException ((ParserToken) current_input, e); + } + + if (entry_symbols[0] == (int) ParserToken.Epsilon) + continue; + + for (int i = entry_symbols.Length - 1; i >= 0; i--) + automaton_stack.Push (entry_symbols[i]); + } + } + + } +} diff --git a/src/Cake.Issues/Serialization/LitJson/JsonWriter.cs b/src/Cake.Issues/Serialization/LitJson/JsonWriter.cs new file mode 100644 index 000000000..575e86f8d --- /dev/null +++ b/src/Cake.Issues/Serialization/LitJson/JsonWriter.cs @@ -0,0 +1,479 @@ +#pragma warning disable 1587 +#pragma warning disable 1591 + +#region Header +/** + * JsonWriter.cs + * Stream-like facility to output JSON text. + * + * The authors disclaim copyright to this source code. For more details, see + * the 3rd-Party-License.md file included with this distribution. + **/ + +// This file isn't generated, but this comment is necessary to exclude it from code analysis. +// +#endregion + + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Text; + + +namespace LitJson +{ + internal enum Condition + { + InArray, + InObject, + NotAProperty, + Property, + Value + } + + internal class WriterContext + { + public int Count; + public bool InArray; + public bool InObject; + public bool ExpectingValue; + public int Padding; + } + + public class JsonWriter + { + #region Fields + private static readonly NumberFormatInfo number_format; + + private WriterContext context; + private Stack ctx_stack; + private bool has_reached_end; + private char[] hex_seq; + private int indentation; + private int indent_value; + private StringBuilder inst_string_builder; + private bool pretty_print; + private bool validate; + private bool lower_case_properties; + private TextWriter writer; + #endregion + + + #region Properties + public int IndentValue { + get { return indent_value; } + set { + indentation = (indentation / indent_value) * value; + indent_value = value; + } + } + + public bool PrettyPrint { + get { return pretty_print; } + set { pretty_print = value; } + } + + public TextWriter TextWriter { + get { return writer; } + } + + public bool Validate { + get { return validate; } + set { validate = value; } + } + + public bool LowerCaseProperties { + get { return lower_case_properties; } + set { lower_case_properties = value; } + } + #endregion + + + #region Constructors + static JsonWriter () + { + number_format = NumberFormatInfo.InvariantInfo; + } + + public JsonWriter () + { + inst_string_builder = new StringBuilder (); + writer = new StringWriter (inst_string_builder); + + Init (); + } + + public JsonWriter (StringBuilder sb) : + this (new StringWriter (sb)) + { + } + + public JsonWriter (TextWriter writer) + { + if (writer == null) + throw new ArgumentNullException ("writer"); + + this.writer = writer; + + Init (); + } + #endregion + + + #region Private Methods + private void DoValidation (Condition cond) + { + if (! context.ExpectingValue) + context.Count++; + + if (! validate) + return; + + if (has_reached_end) + throw new JsonException ( + "A complete JSON symbol has already been written"); + + switch (cond) { + case Condition.InArray: + if (! context.InArray) + throw new JsonException ( + "Can't close an array here"); + break; + + case Condition.InObject: + if (! context.InObject || context.ExpectingValue) + throw new JsonException ( + "Can't close an object here"); + break; + + case Condition.NotAProperty: + if (context.InObject && ! context.ExpectingValue) + throw new JsonException ( + "Expected a property"); + break; + + case Condition.Property: + if (! context.InObject || context.ExpectingValue) + throw new JsonException ( + "Can't add a property here"); + break; + + case Condition.Value: + if (! context.InArray && + (! context.InObject || ! context.ExpectingValue)) + throw new JsonException ( + "Can't add a value here"); + + break; + } + } + + private void Init () + { + has_reached_end = false; + hex_seq = new char[4]; + indentation = 0; + indent_value = 4; + pretty_print = false; + validate = true; + lower_case_properties = false; + + ctx_stack = new Stack (); + context = new WriterContext (); + ctx_stack.Push (context); + } + + private static void IntToHex (int n, char[] hex) + { + int num; + + for (int i = 0; i < 4; i++) { + num = n % 16; + + if (num < 10) + hex[3 - i] = (char) ('0' + num); + else + hex[3 - i] = (char) ('A' + (num - 10)); + + n >>= 4; + } + } + + private void Indent () + { + if (pretty_print) + indentation += indent_value; + } + + + private void Put (string str) + { + if (pretty_print && ! context.ExpectingValue) + for (int i = 0; i < indentation; i++) + writer.Write (' '); + + writer.Write (str); + } + + private void PutNewline () + { + PutNewline (true); + } + + private void PutNewline (bool add_comma) + { + if (add_comma && ! context.ExpectingValue && + context.Count > 1) + writer.Write (','); + + if (pretty_print && ! context.ExpectingValue) + writer.Write (Environment.NewLine); + } + + private void PutString (string str) + { + Put (String.Empty); + + writer.Write ('"'); + + int n = str.Length; + for (int i = 0; i < n; i++) { + switch (str[i]) { + case '\n': + writer.Write ("\\n"); + continue; + + case '\r': + writer.Write ("\\r"); + continue; + + case '\t': + writer.Write ("\\t"); + continue; + + case '"': + case '\\': + writer.Write ('\\'); + writer.Write (str[i]); + continue; + + case '\f': + writer.Write ("\\f"); + continue; + + case '\b': + writer.Write ("\\b"); + continue; + } + + if ((int) str[i] >= 32 && (int) str[i] <= 126) { + writer.Write (str[i]); + continue; + } + + // Default, turn into a \uXXXX sequence + IntToHex ((int) str[i], hex_seq); + writer.Write ("\\u"); + writer.Write (hex_seq); + } + + writer.Write ('"'); + } + + private void Unindent () + { + if (pretty_print) + indentation -= indent_value; + } + #endregion + + + public override string ToString () + { + if (inst_string_builder == null) + return String.Empty; + + return inst_string_builder.ToString (); + } + + public void Reset () + { + has_reached_end = false; + + ctx_stack.Clear (); + context = new WriterContext (); + ctx_stack.Push (context); + + if (inst_string_builder != null) + inst_string_builder.Remove (0, inst_string_builder.Length); + } + + public void Write (bool boolean) + { + DoValidation (Condition.Value); + PutNewline (); + + Put (boolean ? "true" : "false"); + + context.ExpectingValue = false; + } + + public void Write (decimal number) + { + DoValidation (Condition.Value); + PutNewline (); + + Put (Convert.ToString (number, number_format)); + + context.ExpectingValue = false; + } + + public void Write (double number) + { + DoValidation (Condition.Value); + PutNewline (); + + string str = Convert.ToString (number, number_format); + Put (str); + + if (str.IndexOf ('.') == -1 && + str.IndexOf ('E') == -1) + writer.Write (".0"); + + context.ExpectingValue = false; + } + + public void Write (int number) + { + DoValidation (Condition.Value); + PutNewline (); + + Put (Convert.ToString (number, number_format)); + + context.ExpectingValue = false; + } + + public void Write (long number) + { + DoValidation (Condition.Value); + PutNewline (); + + Put (Convert.ToString (number, number_format)); + + context.ExpectingValue = false; + } + + public void Write (string str) + { + DoValidation (Condition.Value); + PutNewline (); + + if (str == null) + Put ("null"); + else + PutString (str); + + context.ExpectingValue = false; + } + + [CLSCompliant(false)] + public void Write (ulong number) + { + DoValidation (Condition.Value); + PutNewline (); + + Put (Convert.ToString (number, number_format)); + + context.ExpectingValue = false; + } + + public void WriteArrayEnd () + { + DoValidation (Condition.InArray); + PutNewline (false); + + ctx_stack.Pop (); + if (ctx_stack.Count == 1) + has_reached_end = true; + else { + context = ctx_stack.Peek (); + context.ExpectingValue = false; + } + + Unindent (); + Put ("]"); + } + + public void WriteArrayStart () + { + DoValidation (Condition.NotAProperty); + PutNewline (); + + Put ("["); + + context = new WriterContext (); + context.InArray = true; + ctx_stack.Push (context); + + Indent (); + } + + public void WriteObjectEnd () + { + DoValidation (Condition.InObject); + PutNewline (false); + + ctx_stack.Pop (); + if (ctx_stack.Count == 1) + has_reached_end = true; + else { + context = ctx_stack.Peek (); + context.ExpectingValue = false; + } + + Unindent (); + Put ("}"); + } + + public void WriteObjectStart () + { + DoValidation (Condition.NotAProperty); + PutNewline (); + + Put ("{"); + + context = new WriterContext (); + context.InObject = true; + ctx_stack.Push (context); + + Indent (); + } + + public void WritePropertyName (string property_name) + { + DoValidation (Condition.Property); + PutNewline (); + string propertyName = (property_name == null || !lower_case_properties) + ? property_name + : property_name.ToLowerInvariant(); + + PutString (propertyName); + + if (pretty_print) { + if (propertyName.Length > context.Padding) + context.Padding = propertyName.Length; + + for (int i = context.Padding - propertyName.Length; + i >= 0; i--) + writer.Write (' '); + + writer.Write (": "); + } else + writer.Write (':'); + + context.ExpectingValue = true; + } + } +} diff --git a/src/Cake.Issues/Serialization/LitJson/Lexer.cs b/src/Cake.Issues/Serialization/LitJson/Lexer.cs new file mode 100644 index 000000000..9f604509d --- /dev/null +++ b/src/Cake.Issues/Serialization/LitJson/Lexer.cs @@ -0,0 +1,918 @@ +#pragma warning disable 1587 +#pragma warning disable 1591 + +#region Header +/** + * Lexer.cs + * JSON lexer implementation based on a finite state machine. + * + * The authors disclaim copyright to this source code. For more details, see + * the 3rd-Party-License.md file included with this distribution. + **/ + +// This file isn't generated, but this comment is necessary to exclude it from code analysis. +// +#endregion + + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + + +namespace LitJson +{ + internal class FsmContext + { + public bool Return; + public int NextState; + public Lexer L; + public int StateStack; + } + + + internal class Lexer + { + #region Fields + private delegate bool StateHandler (FsmContext ctx); + + private static readonly int[] fsm_return_table; + private static readonly StateHandler[] fsm_handler_table; + + private bool allow_comments; + private bool allow_single_quoted_strings; + private bool end_of_input; + private FsmContext fsm_context; + private int input_buffer; + private int input_char; + private TextReader reader; + private int state; + private StringBuilder string_buffer; + private string string_value; + private int token; + private int unichar; + #endregion + + + #region Properties + public bool AllowComments { + get { return allow_comments; } + set { allow_comments = value; } + } + + public bool AllowSingleQuotedStrings { + get { return allow_single_quoted_strings; } + set { allow_single_quoted_strings = value; } + } + + public bool EndOfInput { + get { return end_of_input; } + } + + public int Token { + get { return token; } + } + + public string StringValue { + get { return string_value; } + } + #endregion + + + #region Constructors + static Lexer () + { + PopulateFsmTables (out fsm_handler_table, out fsm_return_table); + } + + public Lexer (TextReader reader) + { + allow_comments = true; + allow_single_quoted_strings = true; + + input_buffer = 0; + string_buffer = new StringBuilder (128); + state = 1; + end_of_input = false; + this.reader = reader; + + fsm_context = new FsmContext (); + fsm_context.L = this; + } + #endregion + + + #region Static Methods + private static int HexValue (int digit) + { + switch (digit) { + case 'a': + case 'A': + return 10; + + case 'b': + case 'B': + return 11; + + case 'c': + case 'C': + return 12; + + case 'd': + case 'D': + return 13; + + case 'e': + case 'E': + return 14; + + case 'f': + case 'F': + return 15; + + default: + return digit - '0'; + } + } + + private static void PopulateFsmTables (out StateHandler[] fsm_handler_table, out int[] fsm_return_table) + { + // See section A.1. of the manual for details of the finite + // state machine. + fsm_handler_table = new StateHandler[28] { + State1, + State2, + State3, + State4, + State5, + State6, + State7, + State8, + State9, + State10, + State11, + State12, + State13, + State14, + State15, + State16, + State17, + State18, + State19, + State20, + State21, + State22, + State23, + State24, + State25, + State26, + State27, + State28 + }; + + fsm_return_table = new int[28] { + (int) ParserToken.Char, + 0, + (int) ParserToken.Number, + (int) ParserToken.Number, + 0, + (int) ParserToken.Number, + 0, + (int) ParserToken.Number, + 0, + 0, + (int) ParserToken.True, + 0, + 0, + 0, + (int) ParserToken.False, + 0, + 0, + (int) ParserToken.Null, + (int) ParserToken.CharSeq, + (int) ParserToken.Char, + 0, + 0, + (int) ParserToken.CharSeq, + (int) ParserToken.Char, + 0, + 0, + 0, + 0 + }; + } + + private static char ProcessEscChar (int esc_char) + { + switch (esc_char) { + case '"': + case '\'': + case '\\': + case '/': + return Convert.ToChar (esc_char); + + case 'n': + return '\n'; + + case 't': + return '\t'; + + case 'r': + return '\r'; + + case 'b': + return '\b'; + + case 'f': + return '\f'; + + default: + // Unreachable + return '?'; + } + } + + private static bool State1 (FsmContext ctx) + { + while (ctx.L.GetChar ()) { + if (ctx.L.input_char == ' ' || + ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r') + continue; + + if (ctx.L.input_char >= '1' && ctx.L.input_char <= '9') { + ctx.L.string_buffer.Append ((char) ctx.L.input_char); + ctx.NextState = 3; + return true; + } + + switch (ctx.L.input_char) { + case '"': + ctx.NextState = 19; + ctx.Return = true; + return true; + + case ',': + case ':': + case '[': + case ']': + case '{': + case '}': + ctx.NextState = 1; + ctx.Return = true; + return true; + + case '-': + ctx.L.string_buffer.Append ((char) ctx.L.input_char); + ctx.NextState = 2; + return true; + + case '0': + ctx.L.string_buffer.Append ((char) ctx.L.input_char); + ctx.NextState = 4; + return true; + + case 'f': + ctx.NextState = 12; + return true; + + case 'n': + ctx.NextState = 16; + return true; + + case 't': + ctx.NextState = 9; + return true; + + case '\'': + if (! ctx.L.allow_single_quoted_strings) + return false; + + ctx.L.input_char = '"'; + ctx.NextState = 23; + ctx.Return = true; + return true; + + case '/': + if (! ctx.L.allow_comments) + return false; + + ctx.NextState = 25; + return true; + + default: + return false; + } + } + + return true; + } + + private static bool State2 (FsmContext ctx) + { + ctx.L.GetChar (); + + if (ctx.L.input_char >= '1' && ctx.L.input_char<= '9') { + ctx.L.string_buffer.Append ((char) ctx.L.input_char); + ctx.NextState = 3; + return true; + } + + switch (ctx.L.input_char) { + case '0': + ctx.L.string_buffer.Append ((char) ctx.L.input_char); + ctx.NextState = 4; + return true; + + default: + return false; + } + } + + private static bool State3 (FsmContext ctx) + { + while (ctx.L.GetChar ()) { + if (ctx.L.input_char >= '0' && ctx.L.input_char <= '9') { + ctx.L.string_buffer.Append ((char) ctx.L.input_char); + continue; + } + + if (ctx.L.input_char == ' ' || + ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r') { + ctx.Return = true; + ctx.NextState = 1; + return true; + } + + switch (ctx.L.input_char) { + case ',': + case ']': + case '}': + ctx.L.UngetChar (); + ctx.Return = true; + ctx.NextState = 1; + return true; + + case '.': + ctx.L.string_buffer.Append ((char) ctx.L.input_char); + ctx.NextState = 5; + return true; + + case 'e': + case 'E': + ctx.L.string_buffer.Append ((char) ctx.L.input_char); + ctx.NextState = 7; + return true; + + default: + return false; + } + } + return true; + } + + private static bool State4 (FsmContext ctx) + { + ctx.L.GetChar (); + + if (ctx.L.input_char == ' ' || + ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r') { + ctx.Return = true; + ctx.NextState = 1; + return true; + } + + switch (ctx.L.input_char) { + case ',': + case ']': + case '}': + ctx.L.UngetChar (); + ctx.Return = true; + ctx.NextState = 1; + return true; + + case '.': + ctx.L.string_buffer.Append ((char) ctx.L.input_char); + ctx.NextState = 5; + return true; + + case 'e': + case 'E': + ctx.L.string_buffer.Append ((char) ctx.L.input_char); + ctx.NextState = 7; + return true; + + default: + return false; + } + } + + private static bool State5 (FsmContext ctx) + { + ctx.L.GetChar (); + + if (ctx.L.input_char >= '0' && ctx.L.input_char <= '9') { + ctx.L.string_buffer.Append ((char) ctx.L.input_char); + ctx.NextState = 6; + return true; + } + + return false; + } + + private static bool State6 (FsmContext ctx) + { + while (ctx.L.GetChar ()) { + if (ctx.L.input_char >= '0' && ctx.L.input_char <= '9') { + ctx.L.string_buffer.Append ((char) ctx.L.input_char); + continue; + } + + if (ctx.L.input_char == ' ' || + ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r') { + ctx.Return = true; + ctx.NextState = 1; + return true; + } + + switch (ctx.L.input_char) { + case ',': + case ']': + case '}': + ctx.L.UngetChar (); + ctx.Return = true; + ctx.NextState = 1; + return true; + + case 'e': + case 'E': + ctx.L.string_buffer.Append ((char) ctx.L.input_char); + ctx.NextState = 7; + return true; + + default: + return false; + } + } + + return true; + } + + private static bool State7 (FsmContext ctx) + { + ctx.L.GetChar (); + + if (ctx.L.input_char >= '0' && ctx.L.input_char<= '9') { + ctx.L.string_buffer.Append ((char) ctx.L.input_char); + ctx.NextState = 8; + return true; + } + + switch (ctx.L.input_char) { + case '+': + case '-': + ctx.L.string_buffer.Append ((char) ctx.L.input_char); + ctx.NextState = 8; + return true; + + default: + return false; + } + } + + private static bool State8 (FsmContext ctx) + { + while (ctx.L.GetChar ()) { + if (ctx.L.input_char >= '0' && ctx.L.input_char<= '9') { + ctx.L.string_buffer.Append ((char) ctx.L.input_char); + continue; + } + + if (ctx.L.input_char == ' ' || + ctx.L.input_char >= '\t' && ctx.L.input_char<= '\r') { + ctx.Return = true; + ctx.NextState = 1; + return true; + } + + switch (ctx.L.input_char) { + case ',': + case ']': + case '}': + ctx.L.UngetChar (); + ctx.Return = true; + ctx.NextState = 1; + return true; + + default: + return false; + } + } + + return true; + } + + private static bool State9 (FsmContext ctx) + { + ctx.L.GetChar (); + + switch (ctx.L.input_char) { + case 'r': + ctx.NextState = 10; + return true; + + default: + return false; + } + } + + private static bool State10 (FsmContext ctx) + { + ctx.L.GetChar (); + + switch (ctx.L.input_char) { + case 'u': + ctx.NextState = 11; + return true; + + default: + return false; + } + } + + private static bool State11 (FsmContext ctx) + { + ctx.L.GetChar (); + + switch (ctx.L.input_char) { + case 'e': + ctx.Return = true; + ctx.NextState = 1; + return true; + + default: + return false; + } + } + + private static bool State12 (FsmContext ctx) + { + ctx.L.GetChar (); + + switch (ctx.L.input_char) { + case 'a': + ctx.NextState = 13; + return true; + + default: + return false; + } + } + + private static bool State13 (FsmContext ctx) + { + ctx.L.GetChar (); + + switch (ctx.L.input_char) { + case 'l': + ctx.NextState = 14; + return true; + + default: + return false; + } + } + + private static bool State14 (FsmContext ctx) + { + ctx.L.GetChar (); + + switch (ctx.L.input_char) { + case 's': + ctx.NextState = 15; + return true; + + default: + return false; + } + } + + private static bool State15 (FsmContext ctx) + { + ctx.L.GetChar (); + + switch (ctx.L.input_char) { + case 'e': + ctx.Return = true; + ctx.NextState = 1; + return true; + + default: + return false; + } + } + + private static bool State16 (FsmContext ctx) + { + ctx.L.GetChar (); + + switch (ctx.L.input_char) { + case 'u': + ctx.NextState = 17; + return true; + + default: + return false; + } + } + + private static bool State17 (FsmContext ctx) + { + ctx.L.GetChar (); + + switch (ctx.L.input_char) { + case 'l': + ctx.NextState = 18; + return true; + + default: + return false; + } + } + + private static bool State18 (FsmContext ctx) + { + ctx.L.GetChar (); + + switch (ctx.L.input_char) { + case 'l': + ctx.Return = true; + ctx.NextState = 1; + return true; + + default: + return false; + } + } + + private static bool State19 (FsmContext ctx) + { + while (ctx.L.GetChar ()) { + switch (ctx.L.input_char) { + case '"': + ctx.L.UngetChar (); + ctx.Return = true; + ctx.NextState = 20; + return true; + + case '\\': + ctx.StateStack = 19; + ctx.NextState = 21; + return true; + + default: + ctx.L.string_buffer.Append ((char) ctx.L.input_char); + continue; + } + } + + return true; + } + + private static bool State20 (FsmContext ctx) + { + ctx.L.GetChar (); + + switch (ctx.L.input_char) { + case '"': + ctx.Return = true; + ctx.NextState = 1; + return true; + + default: + return false; + } + } + + private static bool State21 (FsmContext ctx) + { + ctx.L.GetChar (); + + switch (ctx.L.input_char) { + case 'u': + ctx.NextState = 22; + return true; + + case '"': + case '\'': + case '/': + case '\\': + case 'b': + case 'f': + case 'n': + case 'r': + case 't': + ctx.L.string_buffer.Append ( + ProcessEscChar (ctx.L.input_char)); + ctx.NextState = ctx.StateStack; + return true; + + default: + return false; + } + } + + private static bool State22 (FsmContext ctx) + { + int counter = 0; + int mult = 4096; + + ctx.L.unichar = 0; + + while (ctx.L.GetChar ()) { + + if (ctx.L.input_char >= '0' && ctx.L.input_char <= '9' || + ctx.L.input_char >= 'A' && ctx.L.input_char <= 'F' || + ctx.L.input_char >= 'a' && ctx.L.input_char <= 'f') { + + ctx.L.unichar += HexValue (ctx.L.input_char) * mult; + + counter++; + mult /= 16; + + if (counter == 4) { + ctx.L.string_buffer.Append ( + Convert.ToChar (ctx.L.unichar)); + ctx.NextState = ctx.StateStack; + return true; + } + + continue; + } + + return false; + } + + return true; + } + + private static bool State23 (FsmContext ctx) + { + while (ctx.L.GetChar ()) { + switch (ctx.L.input_char) { + case '\'': + ctx.L.UngetChar (); + ctx.Return = true; + ctx.NextState = 24; + return true; + + case '\\': + ctx.StateStack = 23; + ctx.NextState = 21; + return true; + + default: + ctx.L.string_buffer.Append ((char) ctx.L.input_char); + continue; + } + } + + return true; + } + + private static bool State24 (FsmContext ctx) + { + ctx.L.GetChar (); + + switch (ctx.L.input_char) { + case '\'': + ctx.L.input_char = '"'; + ctx.Return = true; + ctx.NextState = 1; + return true; + + default: + return false; + } + } + + private static bool State25 (FsmContext ctx) + { + ctx.L.GetChar (); + + switch (ctx.L.input_char) { + case '*': + ctx.NextState = 27; + return true; + + case '/': + ctx.NextState = 26; + return true; + + default: + return false; + } + } + + private static bool State26 (FsmContext ctx) + { + while (ctx.L.GetChar ()) { + if (ctx.L.input_char == '\n') { + ctx.NextState = 1; + return true; + } + } + + return true; + } + + private static bool State27 (FsmContext ctx) + { + while (ctx.L.GetChar ()) { + if (ctx.L.input_char == '*') { + ctx.NextState = 28; + return true; + } + } + + return true; + } + + private static bool State28 (FsmContext ctx) + { + while (ctx.L.GetChar ()) { + if (ctx.L.input_char == '*') + continue; + + if (ctx.L.input_char == '/') { + ctx.NextState = 1; + return true; + } + + ctx.NextState = 27; + return true; + } + + return true; + } + #endregion + + + private bool GetChar () + { + if ((input_char = NextChar ()) != -1) + return true; + + end_of_input = true; + return false; + } + + private int NextChar () + { + if (input_buffer != 0) { + int tmp = input_buffer; + input_buffer = 0; + + return tmp; + } + + return reader.Read (); + } + + public bool NextToken () + { + StateHandler handler; + fsm_context.Return = false; + + while (true) { + handler = fsm_handler_table[state - 1]; + + if (! handler (fsm_context)) + throw new JsonException (input_char); + + if (end_of_input) + return false; + + if (fsm_context.Return) { + string_value = string_buffer.ToString (); + string_buffer.Remove (0, string_buffer.Length); + token = fsm_return_table[state - 1]; + + if (token == (int) ParserToken.Char) + token = input_char; + + state = fsm_context.NextState; + + return true; + } + + state = fsm_context.NextState; + } + } + + private void UngetChar () + { + input_buffer = input_char; + } + } +} diff --git a/src/Cake.Issues/Serialization/LitJson/ParserToken.cs b/src/Cake.Issues/Serialization/LitJson/ParserToken.cs new file mode 100644 index 000000000..85dd10da3 --- /dev/null +++ b/src/Cake.Issues/Serialization/LitJson/ParserToken.cs @@ -0,0 +1,49 @@ +#pragma warning disable 1587 +#pragma warning disable 1591 + +#region Header +/** + * ParserToken.cs + * Internal representation of the tokens used by the lexer and the parser. + * + * The authors disclaim copyright to this source code. For more details, see + * the 3rd-Party-License.md file included with this distribution. + **/ + +// This file isn't generated, but this comment is necessary to exclude it from code analysis. +// +#endregion + +namespace LitJson +{ + internal enum ParserToken + { + // Lexer tokens (see section A.1.1. of the manual) + None = System.Char.MaxValue + 1, + Number, + True, + False, + Null, + CharSeq, + // Single char + Char, + + // Parser Rules (see section A.2.1 of the manual) + Text, + Object, + ObjectPrime, + Pair, + PairRest, + Array, + ArrayPrime, + Value, + ValueRest, + String, + + // End of input + End, + + // The empty rule + Epsilon + } +} diff --git a/src/Cake.Issues/SerializableIssue.cs b/src/Cake.Issues/Serialization/SerializableIssue.cs similarity index 80% rename from src/Cake.Issues/SerializableIssue.cs rename to src/Cake.Issues/Serialization/SerializableIssue.cs index 86c80e743..0cbfa7b9d 100644 --- a/src/Cake.Issues/SerializableIssue.cs +++ b/src/Cake.Issues/Serialization/SerializableIssue.cs @@ -1,55 +1,41 @@ -namespace Cake.Issues +namespace Cake.Issues.Serialization { - using System.Runtime.Serialization; - /// /// Class for serializing and deserializing an instance. /// - [DataContract] internal class SerializableIssue { /// - [DataMember] public string ProjectFileRelativePath { get; set; } /// - [DataMember] public string ProjectName { get; set; } /// - [DataMember] public string AffectedFileRelativePath { get; set; } /// - [DataMember] public int? Line { get; set; } /// - [DataMember] public string Message { get; set; } /// - [DataMember] public int? Priority { get; set; } /// - [DataMember] public string PriorityName { get; set; } /// - [DataMember] public string Rule { get; set; } /// - [DataMember] public string RuleUrl { get; set; } /// - [DataMember] public string ProviderType { get; set; } /// - [DataMember] public string ProviderName { get; set; } } } diff --git a/src/Cake.Issues/Serialization/SerializableIssueExtensions.cs b/src/Cake.Issues/Serialization/SerializableIssueExtensions.cs new file mode 100644 index 000000000..7a85751c5 --- /dev/null +++ b/src/Cake.Issues/Serialization/SerializableIssueExtensions.cs @@ -0,0 +1,39 @@ +namespace Cake.Issues.Serialization +{ + using System; + + /// + /// Extensions for . + /// + internal static class SerializableIssueExtensions + { + /// + /// Converts a to an . + /// + /// Issue which should be converted. + /// Converted issue. + internal static Issue ToIssue(this SerializableIssue serializableIssue) + { + serializableIssue.NotNull(nameof(serializableIssue)); + + Uri ruleUrl = null; + if (!string.IsNullOrWhiteSpace(serializableIssue.RuleUrl)) + { + ruleUrl = new Uri(serializableIssue.RuleUrl); + } + + return new Issue( + serializableIssue.ProjectFileRelativePath, + serializableIssue.ProjectName, + serializableIssue.AffectedFileRelativePath, + serializableIssue.Line, + serializableIssue.Message, + serializableIssue.Priority, + serializableIssue.PriorityName, + serializableIssue.Rule, + ruleUrl, + serializableIssue.ProviderType, + serializableIssue.ProviderName); + } + } +} From a544daf2f19ec5df52d97d3b004ffbc7f1eaf121 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 4 Oct 2019 00:00:23 +0000 Subject: [PATCH 07/18] Bump Microsoft.CodeAnalysis.FxCopAnalyzers from 2.9.5 to 2.9.6 in /src Bumps [Microsoft.CodeAnalysis.FxCopAnalyzers](https://github.com/dotnet/roslyn-analyzers) from 2.9.5 to 2.9.6. - [Release notes](https://github.com/dotnet/roslyn-analyzers/releases) - [Changelog](https://github.com/dotnet/roslyn-analyzers/blob/master/PostReleaseActivities.md) - [Commits](https://github.com/dotnet/roslyn-analyzers/compare/v2.9.5...v2.9.6) Signed-off-by: dependabot-preview[bot] --- src/Cake.Issues.Testing/Cake.Issues.Testing.csproj | 2 +- src/Cake.Issues/Cake.Issues.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Cake.Issues.Testing/Cake.Issues.Testing.csproj b/src/Cake.Issues.Testing/Cake.Issues.Testing.csproj index 9c9e2519d..e91a7e15a 100644 --- a/src/Cake.Issues.Testing/Cake.Issues.Testing.csproj +++ b/src/Cake.Issues.Testing/Cake.Issues.Testing.csproj @@ -26,7 +26,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Cake.Issues/Cake.Issues.csproj b/src/Cake.Issues/Cake.Issues.csproj index b969cd720..294cf68fc 100644 --- a/src/Cake.Issues/Cake.Issues.csproj +++ b/src/Cake.Issues/Cake.Issues.csproj @@ -24,7 +24,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive From 5ffe4acccc50b3c04e989a948fb09e0f1c6aae73 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 4 Oct 2019 07:18:57 +0000 Subject: [PATCH 08/18] Bump Microsoft.NET.Test.Sdk from 16.2.0 to 16.3.0 in /src Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 16.2.0 to 16.3.0. - [Release notes](https://github.com/microsoft/vstest/releases) - [Commits](https://github.com/microsoft/vstest/compare/v16.2.0...v16.3) Signed-off-by: dependabot-preview[bot] --- src/Cake.Issues.Tests/Cake.Issues.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cake.Issues.Tests/Cake.Issues.Tests.csproj b/src/Cake.Issues.Tests/Cake.Issues.Tests.csproj index e373cbaa5..622d7d8d5 100644 --- a/src/Cake.Issues.Tests/Cake.Issues.Tests.csproj +++ b/src/Cake.Issues.Tests/Cake.Issues.Tests.csproj @@ -25,7 +25,7 @@ - + From 292df30104473316cb13f5a6e53841cac74dd7ed Mon Sep 17 00:00:00 2001 From: Pascal Berger Date: Fri, 4 Oct 2019 22:58:08 +0200 Subject: [PATCH 09/18] Change target framework for test assembly to .NET Core 2.1 --- src/Cake.Issues.Tests/Cake.Issues.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cake.Issues.Tests/Cake.Issues.Tests.csproj b/src/Cake.Issues.Tests/Cake.Issues.Tests.csproj index 622d7d8d5..445ec7958 100644 --- a/src/Cake.Issues.Tests/Cake.Issues.Tests.csproj +++ b/src/Cake.Issues.Tests/Cake.Issues.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp2.0 + netcoreapp2.1 false Cake.Issues Copyright © BBT Software AG and contributors From 76ecb04a60ebd39d0bef9d65e5a64b6d1e172607 Mon Sep 17 00:00:00 2001 From: Pascal Berger Date: Tue, 20 Aug 2019 22:13:04 +0200 Subject: [PATCH 10/18] (GH-121) Support message in multiple format on issues --- .../BaseIssueProviderFixture.cs | 4 +- .../FakeConfigurableIssueProvider.cs | 8 +- src/Cake.Issues.Testing/FakeIssueProvider.cs | 8 +- src/Cake.Issues.Testing/FakeLogFileFormat.cs | 1 - src/Cake.Issues.Testing/IssueChecker.cs | 28 +- .../BaseIssueProviderTests.cs | 2 +- .../BaseMultiFormatIssueProviderTests.cs | 2 +- .../Cake.Issues.Tests.csproj | 6 + .../IIssueExtensionsTests.cs | 8 +- src/Cake.Issues.Tests/IssueBuilderFixture.cs | 4 +- src/Cake.Issues.Tests/IssueReaderTests.cs | 20 +- src/Cake.Issues.Tests/IssueTests.cs | 428 ++++++++++++++---- src/Cake.Issues.Tests/IssuesFixture.cs | 4 +- .../IssueDeserializationExtensionsTests.cs | 60 +++ .../IssueSerializationExtensionsTests.cs | 260 ++++++++++- .../SerializableIssueV2ExtensionsTests.cs | 25 + src/Cake.Issues.Tests/Testfiles/issueV2.json | 16 + src/Cake.Issues.Tests/Testfiles/issuesV2.json | 34 ++ .../Testing/IssueCheckerFixture.cs | 16 +- .../Testing/IssueCheckerTests.cs | 128 +++++- src/Cake.Issues/Aliases.ReadIssues.cs | 2 +- src/Cake.Issues/BaseIssueProvider.cs | 7 +- src/Cake.Issues/BaseLogFileFormat.cs | 1 - .../BaseMultiFormatIssueProvider.cs | 3 +- src/Cake.Issues/IIssue.cs | 14 +- src/Cake.Issues/IIssueExtensions.cs | 46 +- src/Cake.Issues/IIssueProvider.cs | 3 +- src/Cake.Issues/ILogFileFormat.cs | 2 - src/Cake.Issues/Issue.cs | 22 +- src/Cake.Issues/IssueBuilder.cs | 42 +- src/Cake.Issues/IssuesReader.cs | 5 +- src/Cake.Issues/ReadIssuesSettings.cs | 5 - .../IssueDeserializationExtensions.cs | 2 + .../IssueSerializationExtensions.cs | 10 +- .../Serialization/SerializableIssue.cs | 2 +- .../SerializableIssueExtensions.cs | 8 + .../Serialization/SerializableIssueV2.cs | 75 +++ .../SerializableIssueV2Extensions.cs | 47 ++ 38 files changed, 1150 insertions(+), 208 deletions(-) create mode 100644 src/Cake.Issues.Tests/Serialization/SerializableIssueV2ExtensionsTests.cs create mode 100644 src/Cake.Issues.Tests/Testfiles/issueV2.json create mode 100644 src/Cake.Issues.Tests/Testfiles/issuesV2.json create mode 100644 src/Cake.Issues/Serialization/SerializableIssueV2.cs create mode 100644 src/Cake.Issues/Serialization/SerializableIssueV2Extensions.cs diff --git a/src/Cake.Issues.Testing/BaseIssueProviderFixture.cs b/src/Cake.Issues.Testing/BaseIssueProviderFixture.cs index 8016a45d2..c71f7f198 100644 --- a/src/Cake.Issues.Testing/BaseIssueProviderFixture.cs +++ b/src/Cake.Issues.Testing/BaseIssueProviderFixture.cs @@ -33,13 +33,13 @@ protected BaseIssueProviderFixture() public RepositorySettings RepositorySettings { get; set; } /// - /// Calls . + /// Calls . /// /// Issues returned from issue provider. public IEnumerable ReadIssues() { var issueProvider = this.CreateIssueProvider(); - return issueProvider.ReadIssues(IssueCommentFormat.PlainText); + return issueProvider.ReadIssues(); } /// diff --git a/src/Cake.Issues.Testing/FakeConfigurableIssueProvider.cs b/src/Cake.Issues.Testing/FakeConfigurableIssueProvider.cs index 3bff27240..5a7c9f061 100644 --- a/src/Cake.Issues.Testing/FakeConfigurableIssueProvider.cs +++ b/src/Cake.Issues.Testing/FakeConfigurableIssueProvider.cs @@ -55,18 +55,12 @@ public FakeConfigurableIssueProvider( /// public new IssueProviderSettings IssueProviderSettings => base.IssueProviderSettings; - /// - /// Gets the format in which issues should be returned. - /// - public IssueCommentFormat Format { get; private set; } - /// public override string ProviderName => "Fake Issue Provider"; /// - protected override IEnumerable InternalReadIssues(IssueCommentFormat format) + protected override IEnumerable InternalReadIssues() { - this.Format = format; return this.issues; } } diff --git a/src/Cake.Issues.Testing/FakeIssueProvider.cs b/src/Cake.Issues.Testing/FakeIssueProvider.cs index 7d84a558f..8dcef5169 100644 --- a/src/Cake.Issues.Testing/FakeIssueProvider.cs +++ b/src/Cake.Issues.Testing/FakeIssueProvider.cs @@ -44,18 +44,12 @@ public FakeIssueProvider(ICakeLog log, IEnumerable issues) /// public new RepositorySettings Settings => base.Settings; - /// - /// Gets the format in which issues should be returned. - /// - public IssueCommentFormat Format { get; private set; } - /// public override string ProviderName => "Fake Issue Provider"; /// - protected override IEnumerable InternalReadIssues(IssueCommentFormat format) + protected override IEnumerable InternalReadIssues() { - this.Format = format; return this.issues; } } diff --git a/src/Cake.Issues.Testing/FakeLogFileFormat.cs b/src/Cake.Issues.Testing/FakeLogFileFormat.cs index 4245b5c1e..8e39bfd73 100644 --- a/src/Cake.Issues.Testing/FakeLogFileFormat.cs +++ b/src/Cake.Issues.Testing/FakeLogFileFormat.cs @@ -42,7 +42,6 @@ public FakeLogFileFormat(ICakeLog log, IEnumerable issues) /// public override IEnumerable ReadIssues( FakeMultiFormatIssueProvider issueProvider, - IssueCommentFormat format, RepositorySettings repositorySettings, FakeMultiFormatIssueProviderSettings issueProviderSettings) { diff --git a/src/Cake.Issues.Testing/IssueChecker.cs b/src/Cake.Issues.Testing/IssueChecker.cs index a784d8970..d68a51799 100644 --- a/src/Cake.Issues.Testing/IssueChecker.cs +++ b/src/Cake.Issues.Testing/IssueChecker.cs @@ -45,7 +45,9 @@ public static void Check( expectedIssue.ProjectName, expectedIssue.AffectedFileRelativePath?.ToString(), expectedIssue.Line, - expectedIssue.Message, + expectedIssue.MessageText, + expectedIssue.MessageHtml, + expectedIssue.MessageMarkdown, expectedIssue.Priority, expectedIssue.PriorityName, expectedIssue.Rule, @@ -66,7 +68,9 @@ public static void Check( /// null if the issue is not expected to be related to a change in a file. /// Expected line number. /// null if the issue is not expected to be related to a file or specific line. - /// Expected message. + /// Expected message in plain text format. + /// Expected message in HTML format. + /// Expected message in Markdown format. /// Expected priority. /// null if no priority is expected. /// Expected priority name. @@ -83,7 +87,9 @@ public static void Check( string projectName, string affectedFileRelativePath, int? line, - string message, + string messageText, + string messageHtml, + string messageMarkdown, int? priority, string priorityName, string rule, @@ -161,10 +167,22 @@ public static void Check( $"Expected issue.Line to be '{line}' but was '{issue.Line}'."); } - if (issue.Message != message) + if (issue.MessageText != messageText) { throw new Exception( - $"Expected issue.Message to be '{message}' but was '{issue.Message}'."); + $"Expected issue.MessageText to be '{messageText}' but was '{issue.MessageText}'."); + } + + if (issue.MessageHtml != messageHtml) + { + throw new Exception( + $"Expected issue.MessageHtml to be '{messageHtml}' but was '{issue.MessageHtml}'."); + } + + if (issue.MessageMarkdown != messageMarkdown) + { + throw new Exception( + $"Expected issue.MessageMarkdown to be '{messageMarkdown}' but was '{issue.MessageMarkdown}'."); } if (issue.Priority != priority) diff --git a/src/Cake.Issues.Tests/BaseIssueProviderTests.cs b/src/Cake.Issues.Tests/BaseIssueProviderTests.cs index 05e9d0735..f943a5aa0 100644 --- a/src/Cake.Issues.Tests/BaseIssueProviderTests.cs +++ b/src/Cake.Issues.Tests/BaseIssueProviderTests.cs @@ -42,7 +42,7 @@ public void Should_Throw_If_Settings_Is_Null() var provider = new FakeIssueProvider(new FakeLog()); // When - var result = Record.Exception(() => provider.ReadIssues(IssueCommentFormat.PlainText)); + var result = Record.Exception(() => provider.ReadIssues()); // Then result.IsInvalidOperationException("Initialize needs to be called first."); diff --git a/src/Cake.Issues.Tests/BaseMultiFormatIssueProviderTests.cs b/src/Cake.Issues.Tests/BaseMultiFormatIssueProviderTests.cs index 114c6fec0..f446e83d8 100644 --- a/src/Cake.Issues.Tests/BaseMultiFormatIssueProviderTests.cs +++ b/src/Cake.Issues.Tests/BaseMultiFormatIssueProviderTests.cs @@ -113,7 +113,7 @@ public void Should_Read_Issues_From_Format() provider.Initialize(new RepositorySettings(@"c:\repo")); // When - var result = provider.ReadIssues(IssueCommentFormat.PlainText); + var result = provider.ReadIssues(); // Then result.Count().ShouldBe(2); diff --git a/src/Cake.Issues.Tests/Cake.Issues.Tests.csproj b/src/Cake.Issues.Tests/Cake.Issues.Tests.csproj index 445ec7958..63f8064b5 100644 --- a/src/Cake.Issues.Tests/Cake.Issues.Tests.csproj +++ b/src/Cake.Issues.Tests/Cake.Issues.Tests.csproj @@ -43,6 +43,12 @@ + + Always + + + Always + Always diff --git a/src/Cake.Issues.Tests/IIssueExtensionsTests.cs b/src/Cake.Issues.Tests/IIssueExtensionsTests.cs index 500e9bb95..c444ac375 100644 --- a/src/Cake.Issues.Tests/IIssueExtensionsTests.cs +++ b/src/Cake.Issues.Tests/IIssueExtensionsTests.cs @@ -308,13 +308,17 @@ public void Should_Throw_If_Issue_Is_Null() [InlineData("foo {Line} bar", "foo 42 bar")] [InlineData("foo {Rule} bar", "foo Rule Foo bar")] [InlineData("foo {RuleUrl} bar", "foo https://google.com/ bar")] - [InlineData("foo {Message} bar", "foo Message Foo bar")] + [InlineData("foo {MessageText} bar", "foo MessageText Foo bar")] + [InlineData("foo {MessageHtml} bar", "foo MessageHtml Foo bar")] + [InlineData("foo {MessageMarkdown} bar", "foo MessageMarkdown Foo bar")] public void Should_Replace_Tokens(string pattern, string expectedResult) { // Given var issue = IssueBuilder - .NewIssue("Message Foo", "ProviderType Foo", "ProviderName Foo") + .NewIssue("MessageText Foo", "ProviderType Foo", "ProviderName Foo") + .WithMessageInHtmlFormat("MessageHtml Foo") + .WithMessageInMarkdownFormat("MessageMarkdown Foo") .InFile(@"src/Cake.Issues/foo.cs", 42) .InProject(@"src/Cake.Issues/Cake.Issues.csproj", "Cake.Issues") .OfRule("Rule Foo", new Uri("https://google.com")) diff --git a/src/Cake.Issues.Tests/IssueBuilderFixture.cs b/src/Cake.Issues.Tests/IssueBuilderFixture.cs index d734d2da9..7b5e1cbc0 100644 --- a/src/Cake.Issues.Tests/IssueBuilderFixture.cs +++ b/src/Cake.Issues.Tests/IssueBuilderFixture.cs @@ -7,10 +7,10 @@ public IssueBuilderFixture() { } - public IssueBuilderFixture(string message, string providerType, string providerName) + public IssueBuilderFixture(string messageText, string providerType, string providerName) { this.IssueBuilder = - IssueBuilder.NewIssue(message, providerType, providerName); + IssueBuilder.NewIssue(messageText, providerType, providerName); } public IssueBuilder IssueBuilder { get; private set; } diff --git a/src/Cake.Issues.Tests/IssueReaderTests.cs b/src/Cake.Issues.Tests/IssueReaderTests.cs index 61efe99b5..6a6696d62 100644 --- a/src/Cake.Issues.Tests/IssueReaderTests.cs +++ b/src/Cake.Issues.Tests/IssueReaderTests.cs @@ -20,7 +20,7 @@ public void Should_Throw_If_Log_Is_Null() }; // When - var result = Record.Exception(() => fixture.ReadIssues(IssueCommentFormat.Undefined)); + var result = Record.Exception(() => fixture.ReadIssues()); // Then result.IsArgumentNullException("log"); @@ -36,7 +36,7 @@ public void Should_Throw_If_Issue_Provider_List_Is_Null() }; // When - var result = Record.Exception(() => fixture.ReadIssues(IssueCommentFormat.Undefined)); + var result = Record.Exception(() => fixture.ReadIssues()); // Then result.IsArgumentNullException("issueProviders"); @@ -50,7 +50,7 @@ public void Should_Throw_If_Issue_Provider_List_Is_Empty() fixture.IssueProviders.Clear(); // When - var result = Record.Exception(() => fixture.ReadIssues(IssueCommentFormat.Undefined)); + var result = Record.Exception(() => fixture.ReadIssues()); // Then result.IsArgumentException("issueProviders"); @@ -65,7 +65,7 @@ public void Should_Throw_If_Issue_Provider_Is_Null() fixture.IssueProviders.Add(null); // When - var result = Record.Exception(() => fixture.ReadIssues(IssueCommentFormat.Undefined)); + var result = Record.Exception(() => fixture.ReadIssues()); // Then result.IsArgumentOutOfRangeException("issueProviders"); @@ -81,7 +81,7 @@ public void Should_Throw_If_Settings_Are_Null() }; // When - var result = Record.Exception(() => fixture.ReadIssues(IssueCommentFormat.Undefined)); + var result = Record.Exception(() => fixture.ReadIssues()); // Then result.IsArgumentNullException("settings"); @@ -97,7 +97,7 @@ public void Should_Initialize_Issue_Provider() var fixture = new IssuesFixture(); // When - fixture.ReadIssues(IssueCommentFormat.Undefined); + fixture.ReadIssues(); // Then fixture.IssueProviders.ShouldAllBe(x => x.Settings == fixture.Settings); @@ -147,7 +147,7 @@ public void Should_Initialize_All_Issue_Provider() })); // When - fixture.ReadIssues(IssueCommentFormat.Undefined); + fixture.ReadIssues(); // Then fixture.IssueProviders.ShouldAllBe(x => x.Settings == fixture.Settings); @@ -183,7 +183,7 @@ public void Should_Read_Correct_Number_Of_Issues() })); // When - var issues = fixture.ReadIssues(IssueCommentFormat.Undefined).ToList(); + var issues = fixture.ReadIssues().ToList(); // Then issues.Count.ShouldBe(2); @@ -219,7 +219,7 @@ public void Should_Read_Correct_Number_Of_Issues_Not_Related_To_A_File() })); // When - var issues = fixture.ReadIssues(IssueCommentFormat.Undefined).ToList(); + var issues = fixture.ReadIssues().ToList(); // Then issues.Count.ShouldBe(2); @@ -279,7 +279,7 @@ public void Should_Read_Correct_Number_Of_Issues_From_Multiple_Providers() })); // When - var issues = fixture.ReadIssues(IssueCommentFormat.Undefined).ToList(); + var issues = fixture.ReadIssues().ToList(); // Then issues.Count.ShouldBe(4); diff --git a/src/Cake.Issues.Tests/IssueTests.cs b/src/Cake.Issues.Tests/IssueTests.cs index 3df1205d5..5a5d9b7ea 100644 --- a/src/Cake.Issues.Tests/IssueTests.cs +++ b/src/Cake.Issues.Tests/IssueTests.cs @@ -19,7 +19,9 @@ public void Should_Throw_If_Project_Path_Is_Invalid(string projectPath) var projectName = "foo"; var filePath = @"src\foo.cs"; var line = 100; - var message = "Message"; + var messageText = "MessageText"; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priority = 1; var priorityName = "Warning"; var rule = "Rule"; @@ -34,7 +36,9 @@ public void Should_Throw_If_Project_Path_Is_Invalid(string projectPath) projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, @@ -56,7 +60,9 @@ public void Should_Throw_If_File_Path_Is_Absolute(string projectPath) var projectName = "foo"; var filePath = @"src\foo.cs"; var line = 100; - var message = "Message"; + var messageText = "MessageText"; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priority = 1; var priorityName = "Warning"; var rule = "Rule"; @@ -71,7 +77,9 @@ public void Should_Throw_If_File_Path_Is_Absolute(string projectPath) projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, @@ -91,7 +99,9 @@ public void Should_Handle_Project_Paths_Which_Are_Null() var projectName = "foo"; var filePath = @"src\foo.cs"; var line = 10; - var message = "Message"; + var messageText = "MessageText"; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priority = 1; var priorityName = "Warning"; var rule = "Rule"; @@ -106,7 +116,9 @@ public void Should_Handle_Project_Paths_Which_Are_Null() projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, @@ -126,7 +138,9 @@ public void Should_Handle_Project_Paths_Which_Are_Empty() var projectName = "foo"; var filePath = @"src\foo.cs"; var line = 10; - var message = "Message"; + var messageText = "MessageText"; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priority = 1; var priorityName = "Warning"; var rule = "Rule"; @@ -141,7 +155,9 @@ public void Should_Handle_Project_Paths_Which_Are_Empty() projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, @@ -161,7 +177,9 @@ public void Should_Handle_Project_Paths_Which_Are_WhiteSpace() var projectName = "foo"; var filePath = @"src\foo.cs"; var line = 10; - var message = "Message"; + var messageText = "MessageText"; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priority = 1; var priorityName = "Warning"; var rule = "Rule"; @@ -176,7 +194,9 @@ public void Should_Handle_Project_Paths_Which_Are_WhiteSpace() projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, @@ -196,7 +216,9 @@ public void Should_Set_ProjectFileRelativePath(string projectPath) var projectName = "foo"; var filePath = @"src\foo.cs"; var line = 10; - var message = "Message"; + var messageText = "MessageText"; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priority = 1; var priorityName = "Warning"; var rule = "Rule"; @@ -211,7 +233,9 @@ public void Should_Set_ProjectFileRelativePath(string projectPath) projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, @@ -234,7 +258,9 @@ public void Should_Handle_Projects_Which_Are_Null() string projectName = null; var filePath = @"src\foo.cs"; var line = 10; - var message = "Message"; + var messageText = "MessageText"; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priority = 1; var priorityName = "Warning"; var rule = "Rule"; @@ -249,7 +275,9 @@ public void Should_Handle_Projects_Which_Are_Null() projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, @@ -269,7 +297,9 @@ public void Should_Handle_Projects_Which_Are_Empty() var projectName = string.Empty; var filePath = @"src\foo.cs"; var line = 10; - var message = "Message"; + var messageText = "MessageText"; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priority = 1; var priorityName = "Warning"; var rule = "Rule"; @@ -284,7 +314,9 @@ public void Should_Handle_Projects_Which_Are_Empty() projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, @@ -304,7 +336,9 @@ public void Should_Handle_Projects_Which_Are_WhiteSpace() var projectName = " "; var filePath = @"src\foo.cs"; var line = 10; - var message = "Message"; + var messageText = "MessageText"; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priority = 1; var priorityName = "Warning"; var rule = "Rule"; @@ -319,7 +353,9 @@ public void Should_Handle_Projects_Which_Are_WhiteSpace() projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, @@ -339,7 +375,9 @@ public void Should_Set_ProjectName(string projectName) var projectPath = @"src\foo.csproj"; var filePath = @"src\foo.cs"; var line = 10; - var message = "Message"; + var messageText = "MessageText"; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priority = 1; var priorityName = "Warning"; var rule = "Rule"; @@ -354,7 +392,9 @@ public void Should_Set_ProjectName(string projectName) projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, @@ -377,7 +417,9 @@ public void Should_Throw_If_File_Path_Is_Invalid(string filePath) var projectPath = @"src\foo.csproj"; var projectName = "foo"; var line = 100; - var message = "Message"; + var messageText = "MessageText"; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priority = 1; var priorityName = "Warning"; var rule = "Rule"; @@ -392,7 +434,9 @@ public void Should_Throw_If_File_Path_Is_Invalid(string filePath) projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, @@ -414,7 +458,9 @@ public void Should_Throw_If_File_Path_Is_Absolute(string filePath) var projectPath = @"src\foo.csproj"; var projectName = "foo"; var line = 100; - var message = "Message"; + var messageText = "MessageText"; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priority = 1; var priorityName = "Warning"; var rule = "Rule"; @@ -429,7 +475,9 @@ public void Should_Throw_If_File_Path_Is_Absolute(string filePath) projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, @@ -449,7 +497,9 @@ public void Should_Handle_File_Paths_Which_Are_Null() var projectName = "foo"; string filePath = null; int? line = null; - var message = "Message"; + var messageText = "MessageText"; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priority = 1; var priorityName = "Warning"; var rule = "Rule"; @@ -464,7 +514,9 @@ public void Should_Handle_File_Paths_Which_Are_Null() projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, @@ -484,7 +536,9 @@ public void Should_Handle_File_Paths_Which_Are_Empty() var projectName = "foo"; var filePath = string.Empty; int? line = null; - var message = "Message"; + var messageText = "MessageText"; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priority = 1; var priorityName = "Warning"; var rule = "Rule"; @@ -499,7 +553,9 @@ public void Should_Handle_File_Paths_Which_Are_Empty() projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, @@ -519,7 +575,9 @@ public void Should_Handle_File_Paths_Which_Are_WhiteSpace() var projectName = "foo"; var filePath = " "; int? line = null; - var message = "Message"; + var messageText = "MessageText"; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priority = 1; var priorityName = "Warning"; var rule = "Rule"; @@ -534,7 +592,9 @@ public void Should_Handle_File_Paths_Which_Are_WhiteSpace() projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, @@ -562,7 +622,9 @@ public void Should_Set_File_Path(string filePath, string expectedFilePath) var projectPath = @"src\foo.csproj"; var projectName = "foo"; var line = 10; - var message = "Message"; + var messageText = "MessageText"; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priority = 1; var priorityName = "Warning"; var rule = "Rule"; @@ -577,7 +639,9 @@ public void Should_Set_File_Path(string filePath, string expectedFilePath) projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, @@ -601,7 +665,9 @@ public void Should_Throw_If_Line_Is_Negative() var projectName = "foo"; var filePath = @"src\foo.cs"; var line = -1; - var message = "Message"; + var messageText = "MessageText"; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priority = 1; var priorityName = "Warning"; var rule = "Rule"; @@ -616,7 +682,9 @@ public void Should_Throw_If_Line_Is_Negative() projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, @@ -636,7 +704,9 @@ public void Should_Throw_If_Line_Is_Zero() var projectName = "foo"; var filePath = @"src\foo.cs"; var line = 0; - var message = "Message"; + var messageText = "MessageText"; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priority = 1; var priorityName = "Warning"; var rule = "Rule"; @@ -651,7 +721,9 @@ public void Should_Throw_If_Line_Is_Zero() projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, @@ -671,7 +743,9 @@ public void Should_Throw_If_Line_Is_Set_But_No_File() var projectName = "foo"; string filePath = null; var line = 10; - var message = "Message"; + var messageText = "MessageText"; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priority = 1; var priorityName = "Warning"; var rule = "Rule"; @@ -686,7 +760,9 @@ public void Should_Throw_If_Line_Is_Set_But_No_File() projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, @@ -708,7 +784,9 @@ public void Should_Set_Line(int? line) var projectPath = @"src\foo.csproj"; var projectName = "foo"; var filePath = @"src\foo.cs"; - var message = "Message"; + var messageText = "MessageText"; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priority = 1; var priorityName = "Warning"; var rule = "Rule"; @@ -723,7 +801,9 @@ public void Should_Set_Line(int? line) projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, @@ -736,17 +816,19 @@ public void Should_Set_Line(int? line) } } - public sealed class TheMessageArgument + public sealed class TheMessageTextArgument { [Fact] - public void Should_Throw_If_Message_Is_Null() + public void Should_Throw_If_MessageText_Is_Null() { // Given var projectPath = @"src\foo.csproj"; var projectName = "foo"; var filePath = @"src\foo.cs"; var line = 10; - string message = null; + string messageText = null; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priority = 1; var priorityName = "Warning"; var rule = "Rule"; @@ -761,7 +843,9 @@ public void Should_Throw_If_Message_Is_Null() projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, @@ -770,18 +854,20 @@ public void Should_Throw_If_Message_Is_Null() providerName)); // Then - result.IsArgumentNullException("message"); + result.IsArgumentNullException("messageText"); } [Fact] - public void Should_Throw_If_Message_Is_Empty() + public void Should_Throw_If_MessageText_Is_Empty() { // Given var projectPath = @"src\foo.csproj"; var projectName = "foo"; var filePath = @"src\foo.cs"; var line = 10; - var message = string.Empty; + var messageText = string.Empty; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priority = 1; var priorityName = "Warning"; var rule = "Rule"; @@ -796,7 +882,9 @@ public void Should_Throw_If_Message_Is_Empty() projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, @@ -805,18 +893,20 @@ public void Should_Throw_If_Message_Is_Empty() providerName)); // Then - result.IsArgumentOutOfRangeException("message"); + result.IsArgumentOutOfRangeException("messageText"); } [Fact] - public void Should_Throw_If_Message_Is_WhiteSpace() + public void Should_Throw_If_MessageText_Is_WhiteSpace() { // Given var projectPath = @"src\foo.csproj"; var projectName = "foo"; var filePath = @"src\foo.cs"; var line = 10; - var message = " "; + var messageText = " "; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priority = 1; var priorityName = "Warning"; var rule = "Rule"; @@ -831,7 +921,9 @@ public void Should_Throw_If_Message_Is_WhiteSpace() projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, @@ -840,18 +932,20 @@ public void Should_Throw_If_Message_Is_WhiteSpace() providerName)); // Then - result.IsArgumentOutOfRangeException("message"); + result.IsArgumentOutOfRangeException("messageText"); } [Theory] - [InlineData("message")] - public void Should_Set_Message(string message) + [InlineData("messageText")] + public void Should_Set_MessageText(string messageText) { // Given var projectPath = @"src\foo.csproj"; var projectName = "foo"; var filePath = @"src\foo.cs"; var line = 10; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priority = 1; var priorityName = "Warning"; var rule = "Rule"; @@ -866,7 +960,9 @@ public void Should_Set_Message(string message) projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, @@ -875,7 +971,97 @@ public void Should_Set_Message(string message) providerName); // Then - issue.Message.ShouldBe(message); + issue.MessageText.ShouldBe(messageText); + } + } + + public sealed class TheMessageHtmlArgument + { + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData(" ")] + [InlineData("messageHtml")] + public void Should_Set_MessageHtml(string messageHtml) + { + // Given + var projectPath = @"src\foo.csproj"; + var projectName = "foo"; + var filePath = @"src\foo.cs"; + var line = 10; + var messageText = "MessageText"; + var messageMarkdown = "MessageMarkdown"; + var priority = 1; + var priorityName = "Warning"; + var rule = "Rule"; + var ruleUri = new Uri("https://google.com"); + var providerType = "ProviderType"; + var providerName = "ProviderName"; + + // When + var issue = + new Issue( + projectPath, + projectName, + filePath, + line, + messageText, + messageHtml, + messageMarkdown, + priority, + priorityName, + rule, + ruleUri, + providerType, + providerName); + + // Then + issue.MessageHtml.ShouldBe(messageHtml); + } + } + + public sealed class TheMessageMarkdownArgument + { + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData(" ")] + [InlineData("messageMarkdown")] + public void Should_Set_MessageHtml(string messageMarkdown) + { + // Given + var projectPath = @"src\foo.csproj"; + var projectName = "foo"; + var filePath = @"src\foo.cs"; + var line = 10; + var messageText = "MessageText"; + var messageHtml = "MessageHtml"; + var priority = 1; + var priorityName = "Warning"; + var rule = "Rule"; + var ruleUri = new Uri("https://google.com"); + var providerType = "ProviderType"; + var providerName = "ProviderName"; + + // When + var issue = + new Issue( + projectPath, + projectName, + filePath, + line, + messageText, + messageHtml, + messageMarkdown, + priority, + priorityName, + rule, + ruleUri, + providerType, + providerName); + + // Then + issue.MessageMarkdown.ShouldBe(messageMarkdown); } } @@ -895,7 +1081,9 @@ public void Should_Set_Priority(int? priority) var projectName = "foo"; var filePath = @"src\foo.cs"; var line = 10; - var message = "Message"; + var messageText = "MessageText"; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priorityName = "Warning"; var rule = "Rule"; var ruleUri = new Uri("https://google.com"); @@ -909,7 +1097,9 @@ public void Should_Set_Priority(int? priority) projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, @@ -932,7 +1122,9 @@ public void Should_Handle_PriorityNames_Which_Are_Null() var projectName = "foo"; var filePath = @"src\foo.cs"; var line = 10; - var message = "Message"; + var messageText = "MessageText"; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priority = 1; string priorityName = null; var rule = "Rule"; @@ -947,7 +1139,9 @@ public void Should_Handle_PriorityNames_Which_Are_Null() projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, @@ -967,7 +1161,9 @@ public void Should_Handle_PriorityNames_Which_Are_Empty() var projectName = "foo"; var filePath = @"src\foo.cs"; var line = 10; - var message = "Message"; + var messageText = "MessageText"; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priority = 1; var priorityName = string.Empty; var rule = "Rule"; @@ -982,7 +1178,9 @@ public void Should_Handle_PriorityNames_Which_Are_Empty() projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, @@ -1002,7 +1200,9 @@ public void Should_Handle_PriorityNames_Which_Are_WhiteSpace() var projectName = "foo"; var filePath = @"src\foo.cs"; var line = 10; - var message = "Message"; + var messageText = "MessageText"; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priority = 1; var priorityName = " "; var rule = "Rule"; @@ -1017,7 +1217,9 @@ public void Should_Handle_PriorityNames_Which_Are_WhiteSpace() projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, @@ -1038,7 +1240,9 @@ public void Should_Set_Priority_Name(string priorityName) var projectName = "foo"; var filePath = @"src\foo.cs"; var line = 10; - var message = "Message"; + var messageText = "MessageText"; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priority = 1; var rule = "Rule"; var ruleUri = new Uri("https://google.com"); @@ -1052,7 +1256,9 @@ public void Should_Set_Priority_Name(string priorityName) projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, @@ -1078,7 +1284,9 @@ public void Should_Set_Rule(string rule) var projectName = "foo"; var filePath = @"src\foo.cs"; var line = 10; - var message = "Message"; + var messageText = "MessageText"; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priority = 1; var priorityName = "Warning"; var ruleUri = new Uri("https://google.com"); @@ -1092,7 +1300,9 @@ public void Should_Set_Rule(string rule) projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, @@ -1115,7 +1325,9 @@ public void Should_Set_Rule_Url() var projectName = "foo"; var filePath = @"src\foo.cs"; var line = 10; - var message = "Message"; + var messageText = "MessageText"; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priority = 1; var priorityName = "Warning"; var rule = "Rule"; @@ -1130,7 +1342,9 @@ public void Should_Set_Rule_Url() projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, @@ -1150,7 +1364,9 @@ public void Should_Set_Rule_Url_If_Null() var projectName = "foo"; var filePath = @"src\foo.cs"; var line = 10; - var message = "Message"; + var messageText = "MessageText"; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priority = 1; var priorityName = "Warning"; var rule = "Rule"; @@ -1165,7 +1381,9 @@ public void Should_Set_Rule_Url_If_Null() projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, @@ -1188,7 +1406,9 @@ public void Should_Throw_If_Provider_Type_Is_Null() var projectName = "foo"; var filePath = @"src\foo.cs"; var line = 10; - var message = "Message"; + var messageText = "MessageText"; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priority = 1; var priorityName = "Warning"; var rule = "Rule"; @@ -1203,7 +1423,9 @@ public void Should_Throw_If_Provider_Type_Is_Null() projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, @@ -1223,7 +1445,9 @@ public void Should_Throw_If_Provider_Type_Is_Empty() var projectName = "foo"; var filePath = @"src\foo.cs"; var line = 10; - var message = "Message"; + var messageText = "MessageText"; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priority = 1; var priorityName = "Warning"; var rule = "Rule"; @@ -1238,7 +1462,9 @@ public void Should_Throw_If_Provider_Type_Is_Empty() projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, @@ -1258,7 +1484,9 @@ public void Should_Throw_If_Provider_Type_Is_WhiteSpace() var projectName = "foo"; var filePath = @"src\foo.cs"; var line = 10; - var message = "Message"; + var messageText = "MessageText"; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priority = 1; var priorityName = "Warning"; var rule = "Rule"; @@ -1273,7 +1501,9 @@ public void Should_Throw_If_Provider_Type_Is_WhiteSpace() projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, @@ -1294,7 +1524,9 @@ public void Should_Set_ProviderType(string providerType) var projectName = "foo"; var filePath = @"src\foo.cs"; var line = 10; - var message = "Message"; + var messageText = "MessageText"; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priority = 1; var priorityName = "Warning"; var rule = "Rule"; @@ -1308,7 +1540,9 @@ public void Should_Set_ProviderType(string providerType) projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, @@ -1331,7 +1565,9 @@ public void Should_Throw_If_Provider_Name_Is_Null() var projectName = "foo"; var filePath = @"src\foo.cs"; var line = 10; - var message = "Message"; + var messageText = "MessageText"; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priority = 1; var priorityName = "Warning"; var rule = "Rule"; @@ -1346,7 +1582,9 @@ public void Should_Throw_If_Provider_Name_Is_Null() projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, @@ -1366,7 +1604,9 @@ public void Should_Throw_If_Provider_Name_Is_Empty() var projectName = "foo"; var filePath = @"src\foo.cs"; var line = 10; - var message = "Message"; + var messageText = "MessageText"; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priority = 1; var priorityName = "Warning"; var rule = "Rule"; @@ -1381,7 +1621,9 @@ public void Should_Throw_If_Provider_Name_Is_Empty() projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, @@ -1401,7 +1643,9 @@ public void Should_Throw_If_Provider_Name_Is_WhiteSpace() var projectName = "foo"; var filePath = @"src\foo.cs"; var line = 10; - var message = "Message"; + var messageText = "MessageText"; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priority = 1; var priorityName = "Warning"; var rule = "Rule"; @@ -1416,7 +1660,9 @@ public void Should_Throw_If_Provider_Name_Is_WhiteSpace() projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, @@ -1437,7 +1683,9 @@ public void Should_Set_ProviderName(string providerName) var projectName = "foo"; var filePath = @"src\foo.cs"; var line = 10; - var message = "Message"; + var messageText = "MessageText"; + var messageHtml = "MessageHtml"; + var messageMarkdown = "MessageMarkdown"; var priority = 1; var priorityName = "Warning"; var rule = "Rule"; @@ -1451,7 +1699,9 @@ public void Should_Set_ProviderName(string providerName) projectName, filePath, line, - message, + messageText, + messageHtml, + messageMarkdown, priority, priorityName, rule, diff --git a/src/Cake.Issues.Tests/IssuesFixture.cs b/src/Cake.Issues.Tests/IssuesFixture.cs index 9c6baf706..eecba2799 100644 --- a/src/Cake.Issues.Tests/IssuesFixture.cs +++ b/src/Cake.Issues.Tests/IssuesFixture.cs @@ -22,10 +22,10 @@ public IssuesFixture() public RepositorySettings Settings { get; set; } - public IEnumerable ReadIssues(IssueCommentFormat format) + public IEnumerable ReadIssues() { var issueReader = new IssuesReader(this.Log, this.IssueProviders, this.Settings); - return issueReader.ReadIssues(format); + return issueReader.ReadIssues(); } } } diff --git a/src/Cake.Issues.Tests/Serialization/IssueDeserializationExtensionsTests.cs b/src/Cake.Issues.Tests/Serialization/IssueDeserializationExtensionsTests.cs index 2d8d5086b..f2ad8facb 100644 --- a/src/Cake.Issues.Tests/Serialization/IssueDeserializationExtensionsTests.cs +++ b/src/Cake.Issues.Tests/Serialization/IssueDeserializationExtensionsTests.cs @@ -104,6 +104,30 @@ public void Should_Return_Issue() .OfRule("Rule", new Uri("https://google.com")) .WithPriority(IssuePriority.Warning)); } + + [Fact] + public void Should_Return_IssueV2() + { + // Given + var filePath = new FilePath("Testfiles/issueV2.json"); + + // When + var result = filePath.DeserializeToIssue(); + + // Then + IssueChecker.Check( + result, + IssueBuilder.NewIssue( + "Something went wrong.", + "TestProvider", + "Test Provider") + .WithMessageInHtmlFormat("Something went wrong.") + .WithMessageInMarkdownFormat("Something went **wrong**.") + .InProject(@"src\Foo\Bar.csproj", "Bar") + .InFile(@"src\Foo\Bar.cs", 42) + .OfRule("Rule", new Uri("https://google.com")) + .WithPriority(IssuePriority.Warning)); + } } public sealed class TheDeserializeToIssuesExtensionForAJsonFile @@ -165,6 +189,42 @@ public void Should_Return_List_Of_Issues() .InFile(@"src\Foo\Bar2.cs") .WithPriority(IssuePriority.Warning)); } + + [Fact] + public void Should_Return_List_Of_IssuesV2() + { + // Given + var filePath = new FilePath("Testfiles/issuesV2.json"); + + // When + var result = filePath.DeserializeToIssues().ToList(); + + // Then + result.Count.ShouldBe(2); + IssueChecker.Check( + result[0], + IssueBuilder.NewIssue( + "Something went wrong.", + "TestProvider", + "Test Provider") + .WithMessageInHtmlFormat("Something went wrong.") + .WithMessageInMarkdownFormat("Something went **wrong**.") + .InProject(@"src\Foo\Bar.csproj", "Bar") + .InFile(@"src\Foo\Bar.cs", 42) + .OfRule("Rule", new Uri("https://google.com")) + .WithPriority(IssuePriority.Warning)); + IssueChecker.Check( + result[1], + IssueBuilder.NewIssue( + "Something went wrong again.", + "TestProvider", + "Test Provider") + .WithMessageInHtmlFormat("Something went wrong again.") + .WithMessageInMarkdownFormat("Something went **wrong** again.") + .InProject(@"src\Foo\Bar.csproj", "Bar") + .InFile(@"src\Foo\Bar2.cs") + .WithPriority(IssuePriority.Warning)); + } } } } \ No newline at end of file diff --git a/src/Cake.Issues.Tests/Serialization/IssueSerializationExtensionsTests.cs b/src/Cake.Issues.Tests/Serialization/IssueSerializationExtensionsTests.cs index 5dc8a4f3a..343ab1f3d 100644 --- a/src/Cake.Issues.Tests/Serialization/IssueSerializationExtensionsTests.cs +++ b/src/Cake.Issues.Tests/Serialization/IssueSerializationExtensionsTests.cs @@ -27,7 +27,7 @@ public void Should_Throw_If_Issue_Is_Null() } [Fact] - public void Should_Give_Correct_Result_For_Message_After_Roundtrip() + public void Should_Give_Correct_Result_For_MessageText_After_Roundtrip() { // Given var message = "message"; @@ -40,7 +40,43 @@ public void Should_Give_Correct_Result_For_Message_After_Roundtrip() var result = issue.SerializeToJsonString().DeserializeToIssue(); // Then - result.Message.ShouldBe(message); + result.MessageText.ShouldBe(message); + } + + [Fact] + public void Should_Give_Correct_Result_For_MessageMarkdown_After_Roundtrip() + { + // Given + var messageMarkdown = "messageMarkdown"; + var issue = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .WithMessageInMarkdownFormat(messageMarkdown) + .Create(); + + // When + var result = issue.SerializeToJsonString().DeserializeToIssue(); + + // Then + result.MessageMarkdown.ShouldBe(messageMarkdown); + } + + [Fact] + public void Should_Give_Correct_Result_For_MessageHtml_After_Roundtrip() + { + // Given + var messageHtml = "messageHtml"; + var issue = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .WithMessageInHtmlFormat(messageHtml) + .Create(); + + // When + var result = issue.SerializeToJsonString().DeserializeToIssue(); + + // Then + result.MessageHtml.ShouldBe(messageHtml); } [Fact] @@ -238,19 +274,75 @@ public void Should_Throw_If_Issue_Is_Null() } [Fact] - public void Should_Give_Correct_Result_For_Message_After_Roundtrip() + public void Should_Give_Correct_Result_For_MessageText_After_Roundtrip() + { + // Given + var messageText1 = "messageText1"; + var messageText2 = "messageText2"; + var issues = + new List + { + IssueBuilder + .NewIssue(messageText1, "providerType1", "providerName1") + .Create(), + IssueBuilder + .NewIssue(messageText2, "providerType2", "providerName2") + .Create(), + }; + + // When + var result = issues.SerializeToJsonString().DeserializeToIssues(); + + // Then + result.Count().ShouldBe(2); + result.First().MessageText.ShouldBe(messageText1); + result.Last().MessageText.ShouldBe(messageText2); + } + + [Fact] + public void Should_Give_Correct_Result_For_MessageMarkdown_After_Roundtrip() + { + // Given + var messageMarkdown1 = "messageMarkdown1"; + var messageMarkdown2 = "messageMarkdown2"; + var issues = + new List + { + IssueBuilder + .NewIssue("messageText1", "providerType1", "providerName1") + .WithMessageInMarkdownFormat(messageMarkdown1) + .Create(), + IssueBuilder + .NewIssue("messageText2", "providerType2", "providerName2") + .WithMessageInMarkdownFormat(messageMarkdown2) + .Create(), + }; + + // When + var result = issues.SerializeToJsonString().DeserializeToIssues(); + + // Then + result.Count().ShouldBe(2); + result.First().MessageMarkdown.ShouldBe(messageMarkdown1); + result.Last().MessageMarkdown.ShouldBe(messageMarkdown2); + } + + [Fact] + public void Should_Give_Correct_Result_For_MessageHtml_After_Roundtrip() { // Given - var message1 = "message1"; - var message2 = "message2"; + var messageHtml1 = "messageHtml1"; + var messageHtml2 = "messageHtml2"; var issues = new List { IssueBuilder - .NewIssue(message1, "providerType1", "providerName1") + .NewIssue("messageText1", "providerType1", "providerName1") + .WithMessageInHtmlFormat(messageHtml1) .Create(), IssueBuilder - .NewIssue(message2, "providerType2", "providerName2") + .NewIssue("messageText2", "providerType2", "providerName2") + .WithMessageInHtmlFormat(messageHtml2) .Create(), }; @@ -259,8 +351,8 @@ public void Should_Give_Correct_Result_For_Message_After_Roundtrip() // Then result.Count().ShouldBe(2); - result.First().Message.ShouldBe(message1); - result.Last().Message.ShouldBe(message2); + result.First().MessageHtml.ShouldBe(messageHtml1); + result.Last().MessageHtml.ShouldBe(messageHtml2); } [Fact] @@ -574,13 +666,67 @@ public void Should_Throw_If_FilePath_Is_Null() } [Fact] - public void Should_Give_Correct_Result_For_Message_After_Roundtrip() + public void Should_Give_Correct_Result_For_MessageText_After_Roundtrip() { // Given - var message = "message"; + var messageText = "messageText"; var issue = IssueBuilder - .NewIssue(message, "providerType", "providerName") + .NewIssue(messageText, "providerType", "providerName") + .Create(); + var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); + + try + { + // When + issue.SerializeToJsonFile(filePath); + var result = filePath.DeserializeToIssue(); + + // Then + result.MessageText.ShouldBe(messageText); + } + finally + { + System.IO.File.Delete(filePath.FullPath); + } + } + + [Fact] + public void Should_Give_Correct_Result_For_MessageMarkdown_After_Roundtrip() + { + // Given + var messageMarkdown = "messageMarkdown"; + var issue = + IssueBuilder + .NewIssue("messageText", "providerType", "providerName") + .WithMessageInMarkdownFormat(messageMarkdown) + .Create(); + var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); + + try + { + // When + issue.SerializeToJsonFile(filePath); + var result = filePath.DeserializeToIssue(); + + // Then + result.MessageMarkdown.ShouldBe(messageMarkdown); + } + finally + { + System.IO.File.Delete(filePath.FullPath); + } + } + + [Fact] + public void Should_Give_Correct_Result_For_MessageHtml_After_Roundtrip() + { + // Given + var messageHtml = "messageHtml"; + var issue = + IssueBuilder + .NewIssue("messageText", "providerType", "providerName") + .WithMessageInHtmlFormat(messageHtml) .Create(); var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); @@ -591,7 +737,7 @@ public void Should_Give_Correct_Result_For_Message_After_Roundtrip() var result = filePath.DeserializeToIssue(); // Then - result.Message.ShouldBe(message); + result.MessageHtml.ShouldBe(messageHtml); } finally { @@ -899,19 +1045,93 @@ public void Should_Throw_If_FilePath_Is_Null() } [Fact] - public void Should_Give_Correct_Result_For_Message_After_Roundtrip() + public void Should_Give_Correct_Result_For_MessageText_After_Roundtrip() + { + // Given + var messageText1 = "messageText1"; + var messageText2 = "messageText2"; + var issues = + new List + { + IssueBuilder + .NewIssue(messageText1, "providerType1", "providerName1") + .Create(), + IssueBuilder + .NewIssue(messageText2, "providerType2", "providerName2") + .Create(), + }; + var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); + + try + { + // When + issues.SerializeToJsonFile(filePath); + var result = filePath.DeserializeToIssues(); + + // Then + result.Count().ShouldBe(2); + result.First().MessageText.ShouldBe(messageText1); + result.Last().MessageText.ShouldBe(messageText2); + } + finally + { + System.IO.File.Delete(filePath.FullPath); + } + } + + [Fact] + public void Should_Give_Correct_Result_For_MessageMarkdown_After_Roundtrip() + { + // Given + var messageMarkdown1 = "messageMarkdown1"; + var messageMarkdown2 = "messageMarkdown2"; + var issues = + new List + { + IssueBuilder + .NewIssue("messageText1", "providerType1", "providerName1") + .WithMessageInMarkdownFormat(messageMarkdown1) + .Create(), + IssueBuilder + .NewIssue("messageText2", "providerType2", "providerName2") + .WithMessageInMarkdownFormat(messageMarkdown2) + .Create(), + }; + var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); + + try + { + // When + issues.SerializeToJsonFile(filePath); + var result = filePath.DeserializeToIssues(); + + // Then + result.Count().ShouldBe(2); + result.First().MessageMarkdown.ShouldBe(messageMarkdown1); + result.Last().MessageMarkdown.ShouldBe(messageMarkdown2); + } + finally + { + System.IO.File.Delete(filePath.FullPath); + } + } + + [Fact] + public void Should_Give_Correct_Result_For_MessageHtml_After_Roundtrip() { // Given - var message1 = "message1"; - var message2 = "message2"; + var messageHtml1 = "messageHtml1"; + var messageHtml2 = "messageHtml2"; var issues = new List { IssueBuilder - .NewIssue(message1, "providerType1", "providerName1") + .NewIssue("messageText1", "providerType1", "providerName1") + .WithMessageInHtmlFormat(messageHtml1) .Create(), IssueBuilder - .NewIssue(message2, "providerType2", "providerName2") + .NewIssue("messageText2", "providerType2", "providerName2") + .WithMessageInHtmlFormat(messageHtml2) .Create(), }; var filePath = new FilePath(System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".json"); @@ -924,8 +1144,8 @@ public void Should_Give_Correct_Result_For_Message_After_Roundtrip() // Then result.Count().ShouldBe(2); - result.First().Message.ShouldBe(message1); - result.Last().Message.ShouldBe(message2); + result.First().MessageHtml.ShouldBe(messageHtml1); + result.Last().MessageHtml.ShouldBe(messageHtml2); } finally { diff --git a/src/Cake.Issues.Tests/Serialization/SerializableIssueV2ExtensionsTests.cs b/src/Cake.Issues.Tests/Serialization/SerializableIssueV2ExtensionsTests.cs new file mode 100644 index 000000000..4ef0f43c0 --- /dev/null +++ b/src/Cake.Issues.Tests/Serialization/SerializableIssueV2ExtensionsTests.cs @@ -0,0 +1,25 @@ +namespace Cake.Issues.Tests.Serialization +{ + using Cake.Issues.Serialization; + using Cake.Issues.Testing; + using Xunit; + + public sealed class SerializableIssueV2ExtensionsTests + { + public sealed class TheToIssueExtension + { + [Fact] + public void Should_Throw_If_SerializableIssue_Is_Null() + { + // Given + SerializableIssueV2 serializableIssue = null; + + // When + var result = Record.Exception(() => serializableIssue.ToIssue()); + + // Then + result.IsArgumentNullException("serializableIssue"); + } + } + } +} \ No newline at end of file diff --git a/src/Cake.Issues.Tests/Testfiles/issueV2.json b/src/Cake.Issues.Tests/Testfiles/issueV2.json new file mode 100644 index 000000000..88edfa346 --- /dev/null +++ b/src/Cake.Issues.Tests/Testfiles/issueV2.json @@ -0,0 +1,16 @@ +{ + "Version": 2, + "AffectedFileRelativePath": "src\/Foo\/Bar.cs", + "Line": 42, + "MessageText": "Something went wrong.", + "MessageHtml": "Something went wrong.", + "MessageMarkdown": "Something went **wrong**.", + "Priority": 300, + "PriorityName": "Warning", + "ProjectFileRelativePath": "src\/Foo\/Bar.csproj", + "ProjectName": "Bar", + "ProviderName": "Test Provider", + "ProviderType": "TestProvider", + "Rule": "Rule", + "RuleUrl": "https://google.com" +} \ No newline at end of file diff --git a/src/Cake.Issues.Tests/Testfiles/issuesV2.json b/src/Cake.Issues.Tests/Testfiles/issuesV2.json new file mode 100644 index 000000000..9b42ecc13 --- /dev/null +++ b/src/Cake.Issues.Tests/Testfiles/issuesV2.json @@ -0,0 +1,34 @@ +[ + { + "Version": 2, + "AffectedFileRelativePath": "src\/Foo\/Bar.cs", + "Line": 42, + "MessageText": "Something went wrong.", + "MessageHtml": "Something went wrong.", + "MessageMarkdown": "Something went **wrong**.", + "Priority": 300, + "PriorityName": "Warning", + "ProjectFileRelativePath": "src\/Foo\/Bar.csproj", + "ProjectName": "Bar", + "ProviderName": "Test Provider", + "ProviderType": "TestProvider", + "Rule": "Rule", + "RuleUrl": "https://google.com" + }, + { + "Version": 2, + "AffectedFileRelativePath": "src\/Foo\/Bar2.cs", + "Line": null, + "MessageText": "Something went wrong again.", + "MessageHtml": "Something went wrong again.", + "MessageMarkdown": "Something went **wrong** again.", + "Priority": 300, + "PriorityName": "Warning", + "ProjectFileRelativePath": "src\/Foo\/Bar.csproj", + "ProjectName": "Bar", + "ProviderName": "Test Provider", + "ProviderType": "TestProvider", + "Rule": null, + "RuleUrl": null + } +] \ No newline at end of file diff --git a/src/Cake.Issues.Tests/Testing/IssueCheckerFixture.cs b/src/Cake.Issues.Tests/Testing/IssueCheckerFixture.cs index e6102eeb4..918571a1d 100644 --- a/src/Cake.Issues.Tests/Testing/IssueCheckerFixture.cs +++ b/src/Cake.Issues.Tests/Testing/IssueCheckerFixture.cs @@ -9,8 +9,8 @@ public IssueCheckerFixture() { } - public IssueCheckerFixture(string message, string providerType, string providerName) - : base(message, providerType, providerName) + public IssueCheckerFixture(string messageText, string providerType, string providerName) + : base(messageText, providerType, providerName) { this.ProviderType = providerType; this.ProviderName = providerName; @@ -18,13 +18,17 @@ public IssueCheckerFixture(string message, string providerType, string providerN this.ProjectName = "ProjectName"; this.AffectedFileRelativePath = @"src\source.file"; this.Line = 42; - this.Message = message; + this.MessageText = messageText; + this.MessageHtml = "messageHtml"; + this.MessageMarkdown = "messageMarkdown"; this.Priority = 100; this.PriorityName = "PriorityName"; this.Rule = "Rule"; this.RuleUrl = new Uri("https://google.com"); this.IssueBuilder + .WithMessageInHtmlFormat(this.MessageHtml) + .WithMessageInMarkdownFormat(this.MessageMarkdown) .InProject(this.ProjectFileRelativePath, this.ProjectName) .InFile(this.AffectedFileRelativePath, this.Line) .OfRule(this.Rule, this.RuleUrl) @@ -48,7 +52,11 @@ public IssueCheckerFixture(string message, string providerType, string providerN public int Line { get; private set; } - public string Message { get; private set; } + public string MessageText { get; private set; } + + public string MessageHtml { get; private set; } + + public string MessageMarkdown { get; private set; } public int Priority { get; private set; } diff --git a/src/Cake.Issues.Tests/Testing/IssueCheckerTests.cs b/src/Cake.Issues.Tests/Testing/IssueCheckerTests.cs index 310fdd7ca..2b8c8fddf 100644 --- a/src/Cake.Issues.Tests/Testing/IssueCheckerTests.cs +++ b/src/Cake.Issues.Tests/Testing/IssueCheckerTests.cs @@ -148,7 +148,9 @@ public void Should_Throw_If_Issue_Is_Null() fixture.ProjectName, fixture.AffectedFileRelativePath, fixture.Line, - fixture.Message, + fixture.MessageText, + fixture.MessageHtml, + fixture.MessageMarkdown, fixture.Priority, fixture.PriorityName, fixture.Rule, @@ -173,7 +175,9 @@ public void Should_Not_Throw_If_All_Values_Are_The_Same() fixture.ProjectName, fixture.AffectedFileRelativePath, fixture.Line, - fixture.Message, + fixture.MessageText, + fixture.MessageHtml, + fixture.MessageMarkdown, fixture.Priority, fixture.PriorityName, fixture.Rule, @@ -202,7 +206,9 @@ public void Should_Throw_If_ProviderType_Is_Different(string expectedValue, stri fixture.ProjectName, fixture.AffectedFileRelativePath, fixture.Line, - fixture.Message, + fixture.MessageText, + fixture.MessageHtml, + fixture.MessageMarkdown, fixture.Priority, fixture.PriorityName, fixture.Rule, @@ -233,7 +239,9 @@ public void Should_Throw_If_ProviderName_Is_Different(string expectedValue, stri fixture.ProjectName, fixture.AffectedFileRelativePath, fixture.Line, - fixture.Message, + fixture.MessageText, + fixture.MessageHtml, + fixture.MessageMarkdown, fixture.Priority, fixture.PriorityName, fixture.Rule, @@ -265,7 +273,9 @@ public void Should_Throw_If_ProjectFileRelativePath_Is_Different(string expected fixture.ProjectName, fixture.AffectedFileRelativePath, fixture.Line, - fixture.Message, + fixture.MessageText, + fixture.MessageHtml, + fixture.MessageMarkdown, fixture.Priority, fixture.PriorityName, fixture.Rule, @@ -300,7 +310,9 @@ public void Should_Throw_If_ProjectName_Is_Different(string expectedValue, strin expectedValue, fixture.AffectedFileRelativePath, fixture.Line, - fixture.Message, + fixture.MessageText, + fixture.MessageHtml, + fixture.MessageMarkdown, fixture.Priority, fixture.PriorityName, fixture.Rule, @@ -332,7 +344,9 @@ public void Should_Throw_If_AffectedFileRelativePath_Is_Different(string expecte fixture.ProjectName, expectedValue, fixture.Line, - fixture.Message, + fixture.MessageText, + fixture.MessageHtml, + fixture.MessageMarkdown, fixture.Priority, fixture.PriorityName, fixture.Rule, @@ -366,7 +380,9 @@ public void Should_Throw_If_Line_Is_Different(int? expectedValue, int? actualVal fixture.ProjectName, fixture.AffectedFileRelativePath, expectedValue, - fixture.Message, + fixture.MessageText, + fixture.MessageHtml, + fixture.MessageMarkdown, fixture.Priority, fixture.PriorityName, fixture.Rule, @@ -382,7 +398,7 @@ public void Should_Throw_If_Line_Is_Different(int? expectedValue, int? actualVal [InlineData(null, "Foo")] [InlineData("", "Foo")] [InlineData(" ", "Foo")] - public void Should_Throw_If_Message_Is_Different(string expectedValue, string actualValue) + public void Should_Throw_If_MessageText_Is_Different(string expectedValue, string actualValue) { // Given var fixture = new IssueCheckerFixture(actualValue, "ProviderType", "ProviderName"); @@ -398,6 +414,8 @@ public void Should_Throw_If_Message_Is_Different(string expectedValue, string ac fixture.AffectedFileRelativePath, fixture.Line, expectedValue, + fixture.MessageHtml, + fixture.MessageMarkdown, fixture.Priority, fixture.PriorityName, fixture.Rule, @@ -405,7 +423,81 @@ public void Should_Throw_If_Message_Is_Different(string expectedValue, string ac // Then result.ShouldBeOfType(); - result.Message.ShouldStartWith("Expected issue.Message"); + result.Message.ShouldStartWith("Expected issue.MessageText"); + } + + [Theory] + [InlineData("Message", "Foo")] + [InlineData(null, "Foo")] + [InlineData("", "Foo")] + [InlineData(" ", "Foo")] + public void Should_Throw_If_MessageHtml_Is_Different(string expectedValue, string actualValue) + { + // Given + var fixture = new IssueCheckerFixture(); + var issue = + fixture.IssueBuilder + .WithMessageInHtmlFormat(actualValue) + .Create(); + + // When + var result = Record.Exception(() => + IssueChecker.Check( + fixture.Issue, + fixture.ProviderType, + fixture.ProviderName, + fixture.ProjectFileRelativePath, + fixture.ProjectName, + fixture.AffectedFileRelativePath, + fixture.Line, + fixture.MessageText, + expectedValue, + fixture.MessageMarkdown, + fixture.Priority, + fixture.PriorityName, + fixture.Rule, + fixture.RuleUrl)); + + // Then + result.ShouldBeOfType(); + result.Message.ShouldStartWith("Expected issue.MessageHtml"); + } + + [Theory] + [InlineData("Message", "Foo")] + [InlineData(null, "Foo")] + [InlineData("", "Foo")] + [InlineData(" ", "Foo")] + public void Should_Throw_If_MessageMarkdown_Is_Different(string expectedValue, string actualValue) + { + // Given + var fixture = new IssueCheckerFixture(); + var issue = + fixture.IssueBuilder + .WithMessageInMarkdownFormat(actualValue) + .Create(); + + // When + var result = Record.Exception(() => + IssueChecker.Check( + fixture.Issue, + fixture.ProviderType, + fixture.ProviderName, + fixture.ProjectFileRelativePath, + fixture.ProjectName, + fixture.AffectedFileRelativePath, + fixture.Line, + fixture.MessageText, + fixture.MessageHtml, + expectedValue, + fixture.Priority, + fixture.PriorityName, + fixture.Rule, + fixture.RuleUrl)); + + // Then + result.ShouldBeOfType(); + result.Message.ShouldStartWith("Expected issue.MessageMarkdown"); } [Theory] @@ -429,7 +521,9 @@ public void Should_Throw_If_Priority_Is_Different(IssuePriority expectedValue, I fixture.ProjectName, fixture.AffectedFileRelativePath, fixture.Line, - fixture.Message, + fixture.MessageText, + fixture.MessageHtml, + fixture.MessageMarkdown, (int)expectedValue, fixture.PriorityName, fixture.Rule, @@ -464,7 +558,9 @@ public void Should_Throw_If_PriorityName_Is_Different(string expectedValue, stri fixture.ProjectName, fixture.AffectedFileRelativePath, fixture.Line, - fixture.Message, + fixture.MessageText, + fixture.MessageHtml, + fixture.MessageMarkdown, fixture.Priority, expectedValue, fixture.Rule, @@ -499,7 +595,9 @@ public void Should_Throw_If_Rule_Is_Different(string expectedValue, string actua fixture.ProjectName, fixture.AffectedFileRelativePath, fixture.Line, - fixture.Message, + fixture.MessageText, + fixture.MessageHtml, + fixture.MessageMarkdown, fixture.Priority, fixture.PriorityName, expectedValue, @@ -531,7 +629,9 @@ public void Should_Throw_If_RuleUrl_Is_Different(string expectedValue, string ac fixture.ProjectName, fixture.AffectedFileRelativePath, fixture.Line, - fixture.Message, + fixture.MessageText, + fixture.MessageHtml, + fixture.MessageMarkdown, fixture.Priority, fixture.PriorityName, fixture.Rule, diff --git a/src/Cake.Issues/Aliases.ReadIssues.cs b/src/Cake.Issues/Aliases.ReadIssues.cs index cb302213c..1abb5a8d6 100644 --- a/src/Cake.Issues/Aliases.ReadIssues.cs +++ b/src/Cake.Issues/Aliases.ReadIssues.cs @@ -182,7 +182,7 @@ public static IEnumerable ReadIssues( var issuesReader = new IssuesReader(context.Log, issueProviders, settings); - return issuesReader.ReadIssues(settings.Format); + return issuesReader.ReadIssues(); } } } diff --git a/src/Cake.Issues/BaseIssueProvider.cs b/src/Cake.Issues/BaseIssueProvider.cs index 198821f40..e3eeb928b 100644 --- a/src/Cake.Issues/BaseIssueProvider.cs +++ b/src/Cake.Issues/BaseIssueProvider.cs @@ -21,19 +21,18 @@ protected BaseIssueProvider(ICakeLog log) public abstract string ProviderName { get; } /// - public IEnumerable ReadIssues(IssueCommentFormat format) + public IEnumerable ReadIssues() { this.AssertInitialized(); - return this.InternalReadIssues(format); + return this.InternalReadIssues(); } /// /// Gets all issues. /// Compared to it is safe to access Settings from this method. /// - /// Preferred format of the comments. /// List of issues. - protected abstract IEnumerable InternalReadIssues(IssueCommentFormat format); + protected abstract IEnumerable InternalReadIssues(); } } diff --git a/src/Cake.Issues/BaseLogFileFormat.cs b/src/Cake.Issues/BaseLogFileFormat.cs index 0e51dbc6a..facf44245 100644 --- a/src/Cake.Issues/BaseLogFileFormat.cs +++ b/src/Cake.Issues/BaseLogFileFormat.cs @@ -31,7 +31,6 @@ protected BaseLogFileFormat(ICakeLog log) /// public abstract IEnumerable ReadIssues( TIssueProvider issueProvider, - IssueCommentFormat format, RepositorySettings repositorySettings, TSettings issueProviderSettings); } diff --git a/src/Cake.Issues/BaseMultiFormatIssueProvider.cs b/src/Cake.Issues/BaseMultiFormatIssueProvider.cs index 4b40c25d8..92ebb7042 100644 --- a/src/Cake.Issues/BaseMultiFormatIssueProvider.cs +++ b/src/Cake.Issues/BaseMultiFormatIssueProvider.cs @@ -23,12 +23,11 @@ protected BaseMultiFormatIssueProvider(ICakeLog log, TSettings settings) } /// - protected override IEnumerable InternalReadIssues(IssueCommentFormat format) + protected override IEnumerable InternalReadIssues() { return this.IssueProviderSettings.Format.ReadIssues( (TIssueProvider)this, - format, this.Settings, this.IssueProviderSettings); } diff --git a/src/Cake.Issues/IIssue.cs b/src/Cake.Issues/IIssue.cs index 2394f725b..0a90ff793 100644 --- a/src/Cake.Issues/IIssue.cs +++ b/src/Cake.Issues/IIssue.cs @@ -35,9 +35,19 @@ public interface IIssue int? Line { get; } /// - /// Gets the message of the issue. + /// Gets the message of the issue in text format. /// - string Message { get; } + string MessageText { get; } + + /// + /// Gets the message of the issue in HTML format. + /// + string MessageHtml { get; } + + /// + /// Gets the message of the issue in Markdown format. + /// + string MessageMarkdown { get; } /// /// Gets the priority of the message. A higher value indicates a higher priority. diff --git a/src/Cake.Issues/IIssueExtensions.cs b/src/Cake.Issues/IIssueExtensions.cs index eccddde38..4c3c604bc 100644 --- a/src/Cake.Issues/IIssueExtensions.cs +++ b/src/Cake.Issues/IIssueExtensions.cs @@ -1,10 +1,38 @@ namespace Cake.Issues { + using System; + /// /// Extensions for . /// public static class IIssueExtensions { + /// + /// Gets the message of the issue in a specific format. + /// If the message is not available in the specific format, the message in + /// text format will be returned. + /// + /// Issue for which the message should be returned. + /// Format in which the message should be returned. + /// Message in the format specified by or message in text + /// format if it is not available in the desired format. + public static string Message(this IIssue issue, IssueCommentFormat format) + { + issue.NotNull(nameof(issue)); + + switch (format) + { + case IssueCommentFormat.PlainText: + return issue.MessageText; + case IssueCommentFormat.Html: + return !string.IsNullOrEmpty(issue.MessageHtml) ? issue.MessageHtml : issue.MessageText; + case IssueCommentFormat.Markdown: + return !string.IsNullOrEmpty(issue.MessageMarkdown) ? issue.MessageMarkdown : issue.MessageText; + default: + throw new ArgumentOutOfRangeException(nameof(format)); + } + } + /// /// Returns the full path of or null. /// @@ -128,8 +156,18 @@ public static string FileName(this IIssue issue) /// The value of . /// /// - /// {Message} - /// The value of . + /// {MessageText} + /// The value of . + /// + /// + /// {MessageHtml} + /// The value of or + /// if message in HTML format is not available. + /// + /// + /// {MessageMarkdown} + /// The value of or + /// if message in Markdown format is not available. /// /// /// @@ -155,7 +193,9 @@ public static string ReplaceIssuePattern(this string pattern, IIssue issue) .Replace("{Line}", issue.Line?.ToString()) .Replace("{Rule}", issue.Rule) .Replace("{RuleUrl}", issue.RuleUrl?.ToString()) - .Replace("{Message}", issue.Message); + .Replace("{MessageText}", issue.Message(IssueCommentFormat.PlainText)) + .Replace("{MessageHtml}", issue.Message(IssueCommentFormat.Html)) + .Replace("{MessageMarkdown}", issue.Message(IssueCommentFormat.Markdown)); } } } \ No newline at end of file diff --git a/src/Cake.Issues/IIssueProvider.cs b/src/Cake.Issues/IIssueProvider.cs index 832a1e66b..dd3231b46 100644 --- a/src/Cake.Issues/IIssueProvider.cs +++ b/src/Cake.Issues/IIssueProvider.cs @@ -15,8 +15,7 @@ public interface IIssueProvider : IBaseIssueComponent /// /// Gets all issues. /// - /// Preferred format of the comments. /// List of issues. - IEnumerable ReadIssues(IssueCommentFormat format); + IEnumerable ReadIssues(); } } diff --git a/src/Cake.Issues/ILogFileFormat.cs b/src/Cake.Issues/ILogFileFormat.cs index ca62fdcdd..457bc8f12 100644 --- a/src/Cake.Issues/ILogFileFormat.cs +++ b/src/Cake.Issues/ILogFileFormat.cs @@ -15,13 +15,11 @@ public interface ILogFileFormat /// Gets all issues. /// /// Issue provider instance. - /// Preferred format for comments. /// Repository settings to use. /// Settings for issue provider to use. /// List of issues. IEnumerable ReadIssues( TIssueProvider issueProvider, - IssueCommentFormat format, RepositorySettings repositorySettings, TSettings issueProviderSettings); } diff --git a/src/Cake.Issues/Issue.cs b/src/Cake.Issues/Issue.cs index d774dde8e..e699c512b 100644 --- a/src/Cake.Issues/Issue.cs +++ b/src/Cake.Issues/Issue.cs @@ -21,7 +21,9 @@ public class Issue : IIssue /// null or if issue is not related to a change in a file. /// The line in the file where the issues has occurred. /// null if the issue affects the whole file or an asssembly. - /// The message of the issue. + /// The message of the issue in plain text format. + /// The message of the issue in Html format. + /// The message of the issue in Markdown format. /// The priority of the message. /// null if no priority was assigned. /// The human friendly name of the priority. @@ -37,7 +39,9 @@ public Issue( string projectName, string affectedFileRelativePath, int? line, - string message, + string messageText, + string messageHtml, + string messageMarkdown, int? priority, string priorityName, string rule, @@ -46,7 +50,7 @@ public Issue( string providerName) { line?.NotNegativeOrZero(nameof(line)); - message.NotNullOrWhiteSpace(nameof(message)); + messageText.NotNullOrWhiteSpace(nameof(messageText)); providerType.NotNullOrWhiteSpace(nameof(providerType)); providerName.NotNullOrWhiteSpace(nameof(providerName)); @@ -93,7 +97,9 @@ public Issue( this.ProjectName = projectName; this.Line = line; - this.Message = message; + this.MessageText = messageText; + this.MessageHtml = messageHtml; + this.MessageMarkdown = messageMarkdown; this.Priority = priority; this.PriorityName = priorityName; this.Rule = rule; @@ -115,7 +121,13 @@ public Issue( public int? Line { get; } /// - public string Message { get; } + public string MessageText { get; } + + /// + public string MessageHtml { get; } + + /// + public string MessageMarkdown { get; } /// public int? Priority { get; } diff --git a/src/Cake.Issues/IssueBuilder.cs b/src/Cake.Issues/IssueBuilder.cs index 0be1bf428..aee2041d6 100644 --- a/src/Cake.Issues/IssueBuilder.cs +++ b/src/Cake.Issues/IssueBuilder.cs @@ -7,9 +7,11 @@ /// public class IssueBuilder { - private readonly string message; private readonly string providerType; private readonly string providerName; + private readonly string messageText; + private string messageHtml; + private string messageMarkdown; private string projectFileRelativePath; private string projectName; private string filePath; @@ -22,7 +24,7 @@ public class IssueBuilder /// /// Initializes a new instance of the class. /// - /// The message of the issue. + /// The message of the issue in plain text format. /// The type of the issue provider. /// The human friendly name of the issue provider. private IssueBuilder( @@ -34,7 +36,7 @@ private IssueBuilder( providerType.NotNullOrWhiteSpace(nameof(providerType)); providerName.NotNullOrWhiteSpace(nameof(providerName)); - this.message = message; + this.messageText = message; this.providerType = providerType; this.providerName = providerName; } @@ -42,7 +44,7 @@ private IssueBuilder( /// /// Initiates the creation of a new . /// - /// The message of the issue. + /// The message of the issue in plain text format. /// The type of the issue provider. /// The human friendly name of the issue provider. /// Builder class for creating a new issue. @@ -62,7 +64,7 @@ public static IssueBuilder NewIssue( /// Initiates the creation of a new . /// /// Type of the issue provider which has the issue created. - /// The message of the issue. + /// The message of the issue in plain text format. /// Issue provider which has the issue created. /// Builder class for creating a new issue. public static IssueBuilder NewIssue( @@ -80,6 +82,32 @@ public static IssueBuilder NewIssue( return new IssueBuilder(message, typeof(T).FullName, issueProvider.ProviderName); } + /// + /// Sets the message in HTML format. + /// + /// Message in HTML format. + /// Can be null or if issue doesn't have a message in HTML format. + /// Issue Builder instance. + public IssueBuilder WithMessageInHtmlFormat(string message) + { + this.messageHtml = message; + + return this; + } + + /// + /// Sets the message in Markdown format. + /// + /// Message in Markdown format. + /// Can be null or if issue doesn't have a message in Markdown format. + /// Issue Builder instance. + public IssueBuilder WithMessageInMarkdownFormat(string message) + { + this.messageMarkdown = message; + + return this; + } + /// /// Sets the path of the project to which the file affected by the issue belongs. /// @@ -224,7 +252,9 @@ public IIssue Create() this.projectName, this.filePath, this.line, - this.message, + this.messageText, + this.messageHtml, + this.messageMarkdown, this.priority, this.priorityName, this.rule, diff --git a/src/Cake.Issues/IssuesReader.cs b/src/Cake.Issues/IssuesReader.cs index 2eacfe2e2..7751cbc34 100644 --- a/src/Cake.Issues/IssuesReader.cs +++ b/src/Cake.Issues/IssuesReader.cs @@ -40,9 +40,8 @@ public IssuesReader( /// /// Read issues from issue providers. /// - /// Preferred format for comments. /// List of issues. - public IEnumerable ReadIssues(IssueCommentFormat format) + public IEnumerable ReadIssues() { // Initialize issue providers and read issues. var issues = new List(); @@ -53,7 +52,7 @@ public IEnumerable ReadIssues(IssueCommentFormat format) if (issueProvider.Initialize(this.settings)) { this.log.Verbose("Reading issues from {0}...", providerName); - var currentIssues = issueProvider.ReadIssues(format).ToList(); + var currentIssues = issueProvider.ReadIssues().ToList(); this.log.Verbose( "Found {0} issues using issue provider {1}...", diff --git a/src/Cake.Issues/ReadIssuesSettings.cs b/src/Cake.Issues/ReadIssuesSettings.cs index d12003994..d3c52dcec 100644 --- a/src/Cake.Issues/ReadIssuesSettings.cs +++ b/src/Cake.Issues/ReadIssuesSettings.cs @@ -15,10 +15,5 @@ public ReadIssuesSettings(DirectoryPath repositoryRoot) : base(repositoryRoot) { } - - /// - /// Gets or sets the preferred format in which issue comments should be returned. - /// - public IssueCommentFormat Format { get; set; } = IssueCommentFormat.Undefined; } } diff --git a/src/Cake.Issues/Serialization/IssueDeserializationExtensions.cs b/src/Cake.Issues/Serialization/IssueDeserializationExtensions.cs index 28e7f0105..49b3ef740 100644 --- a/src/Cake.Issues/Serialization/IssueDeserializationExtensions.cs +++ b/src/Cake.Issues/Serialization/IssueDeserializationExtensions.cs @@ -125,6 +125,8 @@ private static Issue DeserializeJsonDataToIssue(JsonData data) var version = (int)data["Version"]; switch (version) { + case 2: + return JsonMapper.ToObject(data.ToJson()).ToIssue(); default: throw new Exception($"Not supported issue serialization format {version}"); } diff --git a/src/Cake.Issues/Serialization/IssueSerializationExtensions.cs b/src/Cake.Issues/Serialization/IssueSerializationExtensions.cs index 436ba99b7..c107779ec 100644 --- a/src/Cake.Issues/Serialization/IssueSerializationExtensions.cs +++ b/src/Cake.Issues/Serialization/IssueSerializationExtensions.cs @@ -70,21 +70,23 @@ public static void SerializeToJsonFile(this IEnumerable issues, FilePath } /// - /// Converts an to a . + /// Converts an to a . /// /// Issue which should be converted. /// Converted issue. - internal static SerializableIssue ToSerializableIssue(this IIssue issue) + internal static SerializableIssueV2 ToSerializableIssue(this IIssue issue) { issue.NotNull(nameof(issue)); - return new SerializableIssue + return new SerializableIssueV2 { ProjectFileRelativePath = issue.ProjectFileRelativePath?.FullPath, ProjectName = issue.ProjectName, AffectedFileRelativePath = issue.AffectedFileRelativePath?.FullPath, Line = issue.Line, - Message = issue.Message, + MessageText = issue.MessageText, + MessageMarkdown = issue.MessageMarkdown, + MessageHtml = issue.MessageHtml, Priority = issue.Priority, PriorityName = issue.PriorityName, Rule = issue.Rule, diff --git a/src/Cake.Issues/Serialization/SerializableIssue.cs b/src/Cake.Issues/Serialization/SerializableIssue.cs index 0cbfa7b9d..86579bd1d 100644 --- a/src/Cake.Issues/Serialization/SerializableIssue.cs +++ b/src/Cake.Issues/Serialization/SerializableIssue.cs @@ -17,7 +17,7 @@ internal class SerializableIssue /// public int? Line { get; set; } - /// + /// public string Message { get; set; } /// diff --git a/src/Cake.Issues/Serialization/SerializableIssueExtensions.cs b/src/Cake.Issues/Serialization/SerializableIssueExtensions.cs index 7a85751c5..9b722e83c 100644 --- a/src/Cake.Issues/Serialization/SerializableIssueExtensions.cs +++ b/src/Cake.Issues/Serialization/SerializableIssueExtensions.cs @@ -14,6 +14,10 @@ internal static class SerializableIssueExtensions /// Converted issue. internal static Issue ToIssue(this SerializableIssue serializableIssue) { +#pragma warning disable SA1123 // Do not place regions within elements + #region DupFinder Exclusion +#pragma warning restore SA1123 // Do not place regions within elements + serializableIssue.NotNull(nameof(serializableIssue)); Uri ruleUrl = null; @@ -28,12 +32,16 @@ internal static Issue ToIssue(this SerializableIssue serializableIssue) serializableIssue.AffectedFileRelativePath, serializableIssue.Line, serializableIssue.Message, + null, + null, serializableIssue.Priority, serializableIssue.PriorityName, serializableIssue.Rule, ruleUrl, serializableIssue.ProviderType, serializableIssue.ProviderName); + + #endregion } } } diff --git a/src/Cake.Issues/Serialization/SerializableIssueV2.cs b/src/Cake.Issues/Serialization/SerializableIssueV2.cs new file mode 100644 index 000000000..9afe5e54c --- /dev/null +++ b/src/Cake.Issues/Serialization/SerializableIssueV2.cs @@ -0,0 +1,75 @@ +namespace Cake.Issues.Serialization +{ + using System.Runtime.Serialization; + + /// + /// Class for serializing and deserializing an instance. + /// + [DataContract] + internal class SerializableIssueV2 + { + /// + /// Gets the version of the serialization format. + /// + [DataMember] + public int Version + { + get + { + return 2; + } + } + + /// + [DataMember] + public string ProjectFileRelativePath { get; set; } + + /// + [DataMember] + public string ProjectName { get; set; } + + /// + [DataMember] + public string AffectedFileRelativePath { get; set; } + + /// + [DataMember] + public int? Line { get; set; } + + /// + [DataMember] + public string MessageText { get; set; } + + /// + [DataMember] + public string MessageMarkdown { get; set; } + + /// + [DataMember] + public string MessageHtml { get; set; } + + /// + [DataMember] + public int? Priority { get; set; } + + /// + [DataMember] + public string PriorityName { get; set; } + + /// + [DataMember] + public string Rule { get; set; } + + /// + [DataMember] + public string RuleUrl { get; set; } + + /// + [DataMember] + public string ProviderType { get; set; } + + /// + [DataMember] + public string ProviderName { get; set; } + } +} diff --git a/src/Cake.Issues/Serialization/SerializableIssueV2Extensions.cs b/src/Cake.Issues/Serialization/SerializableIssueV2Extensions.cs new file mode 100644 index 000000000..4ab0491f8 --- /dev/null +++ b/src/Cake.Issues/Serialization/SerializableIssueV2Extensions.cs @@ -0,0 +1,47 @@ +namespace Cake.Issues.Serialization +{ + using System; + + /// + /// Extensions for . + /// + internal static class SerializableIssueV2Extensions + { + /// + /// Converts a to an . + /// + /// Issue which should be converted. + /// Converted issue. + internal static Issue ToIssue(this SerializableIssueV2 serializableIssue) + { +#pragma warning disable SA1123 // Do not place regions within elements + #region DupFinder Exclusion +#pragma warning restore SA1123 // Do not place regions within elements + + serializableIssue.NotNull(nameof(serializableIssue)); + + Uri ruleUrl = null; + if (!string.IsNullOrWhiteSpace(serializableIssue.RuleUrl)) + { + ruleUrl = new Uri(serializableIssue.RuleUrl); + } + + return new Issue( + serializableIssue.ProjectFileRelativePath, + serializableIssue.ProjectName, + serializableIssue.AffectedFileRelativePath, + serializableIssue.Line, + serializableIssue.MessageText, + serializableIssue.MessageHtml, + serializableIssue.MessageMarkdown, + serializableIssue.Priority, + serializableIssue.PriorityName, + serializableIssue.Rule, + ruleUrl, + serializableIssue.ProviderType, + serializableIssue.ProviderName); + + #endregion + } + } +} From 5f81a59ef5c22d13de8705e795db08b1c5d05412 Mon Sep 17 00:00:00 2001 From: Pascal Berger Date: Thu, 3 Oct 2019 00:05:01 +0200 Subject: [PATCH 11/18] (GH-128) Add issue comparer --- src/Cake.Issues.Tests/IIssueComparerTests.cs | 1425 ++++++++++++++++++ src/Cake.Issues/IIssueComparer.cs | 135 ++ 2 files changed, 1560 insertions(+) create mode 100644 src/Cake.Issues.Tests/IIssueComparerTests.cs create mode 100644 src/Cake.Issues/IIssueComparer.cs diff --git a/src/Cake.Issues.Tests/IIssueComparerTests.cs b/src/Cake.Issues.Tests/IIssueComparerTests.cs new file mode 100644 index 000000000..169003a5a --- /dev/null +++ b/src/Cake.Issues.Tests/IIssueComparerTests.cs @@ -0,0 +1,1425 @@ +namespace Cake.Issues.Tests +{ + using System; + using System.Collections.Generic; + using System.Linq; + using Shouldly; + using Xunit; + + public sealed class IIssueComparerTests + { + public sealed class TheCtorWithCompareOnlyPersistentPropertiesSetToFalse + { + [Fact] + public void Should_Return_False_If_First_Issue_Is_Null() + { + // Given + IIssue issue1 = null; + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .Create(); + + // When / Then + CompareIssues(issue1, issue2, false); + } + + [Fact] + public void Should_Return_False_If_Second_Issue_Is_Null() + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .Create(); + IIssue issue2 = null; + + // When / Then + CompareIssues(issue1, issue2, false); + } + + [Theory] + [InlineData("foo", "bar")] + [InlineData("foo", "Foo")] + [InlineData("foo", null)] + [InlineData(null, "foo")] + public void Should_Return_False_If_ProjectFileRelativePath_Is_Different(string path1, string path2) + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InProjectFile(path1) + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InProjectFile(path2) + .Create(); + + // When / Then + CompareIssues(issue1, issue2, false); + } + + [Theory] + [InlineData("foo", "bar")] + [InlineData("foo", "Foo")] + [InlineData("foo", null)] + [InlineData(null, "foo")] + public void Should_Return_False_If_ProjectName_Is_Different(string projectName1, string projectName2) + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InProjectOfName(projectName1) + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InProjectOfName(projectName2) + .Create(); + + // When / Then + CompareIssues(issue1, issue2, false); + } + + [Theory] + [InlineData("foo", "bar")] + [InlineData("foo", "Foo")] + [InlineData("foo", null)] + [InlineData(null, "foo")] + public void Should_Return_False_If_AffectedFileRelativePath_Is_Different(string path1, string path2) + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InFile(path1) + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InFile(path2) + .Create(); + + // When / Then + CompareIssues(issue1, issue2, false); + } + + [Theory] + [InlineData(1, 2)] + [InlineData(1, null)] + [InlineData(null, 1)] + [InlineData(int.MaxValue, 1)] + [InlineData(1, int.MaxValue)] + public void Should_Return_False_If_Line_Is_Different(int? line1, int? line2) + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InFile("foo", line1) + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InFile("foo", line2) + .Create(); + + // When / Then + CompareIssues(issue1, issue2, false); + } + + [Fact] + public void Should_Return_False_If_MessageText_Is_Different() + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message1", "providerType", "providerName") + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message2", "providerType", "providerName") + .Create(); + + // When / Then + CompareIssues(issue1, issue2, false); + } + + [Theory] + [InlineData("foo", "bar")] + [InlineData("foo", "Foo")] + [InlineData("foo", null)] + [InlineData(null, "foo")] + public void Should_Return_False_If_MessageHtml_Is_Different(string message1, string message2) + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .WithMessageInHtmlFormat(message1) + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .WithMessageInHtmlFormat(message2) + .Create(); + + // When / Then + CompareIssues(issue1, issue2, false); + } + + [Theory] + [InlineData("foo", "bar")] + [InlineData("foo", "Foo")] + [InlineData("foo", null)] + [InlineData(null, "foo")] + public void Should_Return_False_If_MessageMarkdown_Is_Different(string message1, string message2) + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .WithMessageInMarkdownFormat(message1) + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .WithMessageInMarkdownFormat(message2) + .Create(); + + // When / Then + CompareIssues(issue1, issue2, false); + } + + [Theory] + [InlineData(1, 2)] + [InlineData(1, null)] + [InlineData(null, 1)] + [InlineData(int.MinValue, 0)] + [InlineData(int.MaxValue, 0)] + public void Should_Return_False_If_Priority_Is_Different(int? priority1, int? priority2) + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .WithPriority(priority1, "Foo") + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .WithPriority(priority2, "Foo") + .Create(); + + // When / Then + CompareIssues(issue1, issue2, false); + } + + [Theory] + [InlineData("foo", "bar")] + [InlineData("foo", "Foo")] + [InlineData("foo", null)] + [InlineData(null, "foo")] + public void Should_Return_False_If_PriorityName_Is_Different(string priorityName1, string priorityName2) + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .WithPriority(42, priorityName1) + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .WithPriority(42, priorityName2) + .Create(); + + // When / Then + CompareIssues(issue1, issue2, false); + } + + [Theory] + [InlineData("foo", "bar")] + [InlineData("foo", "Foo")] + [InlineData("foo", null)] + [InlineData(null, "foo")] + public void Should_Return_False_If_Rule_Is_Different(string rule1, string rule2) + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .OfRule(rule1) + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .OfRule(rule2) + .Create(); + + // When / Then + CompareIssues(issue1, issue2, false); + } + + [Theory] + [InlineData("http://foo", "http://bar")] + [InlineData("http://foo", null)] + [InlineData(null, "http://foo")] + public void Should_Return_False_If_RuleUlr_Is_Different(string ruleUrl1, string ruleUrl2) + { + // Given + var issueBuilder = + IssueBuilder + .NewIssue("message", "providerType", "providerName"); + if (!string.IsNullOrEmpty(ruleUrl1)) + { + issueBuilder = + issueBuilder + .OfRule("foo", new Uri(ruleUrl1)); + } + + var issue1 = issueBuilder.Create(); + + issueBuilder = + IssueBuilder + .NewIssue("message", "providerType", "providerName"); + if (!string.IsNullOrEmpty(ruleUrl2)) + { + issueBuilder = + issueBuilder + .OfRule("foo", new Uri(ruleUrl2)); + } + + var issue2 = issueBuilder.Create(); + + // When / Then + CompareIssues(issue1, issue2, false); + } + + [Fact] + public void Should_Return_False_If_ProviderType_Is_Different() + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType1", "providerName") + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType2", "providerName") + .Create(); + + // When / Then + CompareIssues(issue1, issue2, false); + } + + [Fact] + public void Should_Return_False_If_ProviderName_Is_Different() + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName1") + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName2") + .Create(); + + // When / Then + CompareIssues(issue1, issue2, false); + } + + [Fact] + public void Should_Return_True_If_Same_Reference() + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .Create(); + var issue2 = issue1; + + // When / Then + CompareIssues(issue1, issue2, true); + } + + [Fact] + public void Should_Return_True_If_Both_Are_Null() + { + // Given + IIssue issue1 = null; + IIssue issue2 = null; + + // When / Then + CompareIssues(issue1, issue2, true); + } + + [Fact] + public void Should_Return_True_If_Properties_Are_The_Same() + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .Create(); + + // When / Then + CompareIssues(issue1, issue2, true); + } + + [Theory] + [InlineData("foo", "foo")] + [InlineData("foo", "foo/")] + [InlineData("foo/", "foo")] + [InlineData("", "")] + [InlineData(null, null)] + public void Should_Return_True_If_ProjectFileRelativePath_Is_Same(string path1, string path2) + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InProjectFile(path1) + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InProjectFile(path2) + .Create(); + + // When / Then + CompareIssues(issue1, issue2, true); + } + + [Theory] + [InlineData("foo", "foo")] + [InlineData("", "")] + [InlineData(null, null)] + public void Should_Return_True_If_ProjectName_Is_Same(string projectName1, string projectName2) + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InProjectOfName(projectName1) + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InProjectOfName(projectName2) + .Create(); + + // When / Then + CompareIssues(issue1, issue2, true); + } + + [Theory] + [InlineData("foo", "foo")] + [InlineData("foo", "foo/")] + [InlineData("foo/", "foo")] + [InlineData("", "")] + [InlineData(null, null)] + public void Should_Return_True_If_AffectedFileRelativePath_Is_Same(string path1, string path2) + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InFile(path1) + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InFile(path2) + .Create(); + + // When / Then + CompareIssues(issue1, issue2, true); + } + + [Theory] + [InlineData(1, 1)] + [InlineData(null, null)] + [InlineData(int.MaxValue, int.MaxValue)] + public void Should_Return_True_If_Line_Is_Same(int? line1, int? line2) + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InFile("foo", line1) + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InFile("foo", line2) + .Create(); + + // When / Then + CompareIssues(issue1, issue2, true); + } + + [Fact] + public void Should_Return_True_If_MessageText_Is_Same() + { + // Given + var issue1 = + IssueBuilder + .NewIssue("messageText", "providerType", "providerName") + .Create(); + var issue2 = + IssueBuilder + .NewIssue("messageText", "providerType", "providerName") + .Create(); + + // When / Then + CompareIssues(issue1, issue2, true); + } + + [Theory] + [InlineData("foo", "foo")] + [InlineData("", "")] + [InlineData(null, null)] + public void Should_Return_True_If_MessageHtml_Is_Same(string message1, string message2) + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .WithMessageInHtmlFormat(message1) + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .WithMessageInHtmlFormat(message2) + .Create(); + + // When / Then + CompareIssues(issue1, issue2, true); + } + + [Theory] + [InlineData("foo", "foo")] + [InlineData("", "")] + [InlineData(null, null)] + public void Should_Return_True_If_MessageMarkdown_Is_Same(string message1, string message2) + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .WithMessageInMarkdownFormat(message1) + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .WithMessageInMarkdownFormat(message2) + .Create(); + + // When / Then + CompareIssues(issue1, issue2, true); + } + + [Theory] + [InlineData(1, 1)] + [InlineData(null, null)] + [InlineData(0, 0)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + public void Should_Return_True_If_Priority_Is_Same(int? priority1, int? priority2) + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .WithPriority(priority1, "Foo") + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .WithPriority(priority2, "Foo") + .Create(); + + // When / Then + CompareIssues(issue1, issue2, true); + } + + [Theory] + [InlineData("foo", "foo")] + [InlineData("", "")] + [InlineData(null, null)] + public void Should_Return_True_If_PriorityName_Is_Same(string priorityName1, string priorityName2) + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .WithPriority(42, priorityName1) + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .WithPriority(42, priorityName2) + .Create(); + + // When / Then + CompareIssues(issue1, issue2, true); + } + + [Theory] + [InlineData("foo", "foo")] + [InlineData("", "")] + [InlineData(null, null)] + public void Should_Return_True_If_Rule_Is_Same(string rule1, string rule2) + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .OfRule(rule1) + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .OfRule(rule2) + .Create(); + + // When / Then + CompareIssues(issue1, issue2, true); + } + + [Theory] + [InlineData("http://foo", "http://foo")] + [InlineData("http://foo", "http://Foo")] + [InlineData(null, null)] + public void Should_Return_True_If_RuleUlr_Is_Same(string ruleUrl1, string ruleUrl2) + { + // Given + var issueBuilder = + IssueBuilder + .NewIssue("message", "providerType", "providerName"); + if (!string.IsNullOrEmpty(ruleUrl1)) + { + issueBuilder = + issueBuilder + .OfRule("foo", new Uri(ruleUrl1)); + } + + var issue1 = issueBuilder.Create(); + + issueBuilder = + IssueBuilder + .NewIssue("message", "providerType", "providerName"); + if (!string.IsNullOrEmpty(ruleUrl2)) + { + issueBuilder = + issueBuilder + .OfRule("foo", new Uri(ruleUrl2)); + } + + var issue2 = issueBuilder.Create(); + + // When / Then + CompareIssues(issue1, issue2, true); + } + + [Fact] + public void Should_Return_True_If_ProviderType_Is_Same() + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .Create(); + + // When / Then + CompareIssues(issue1, issue2, true); + } + + [Fact] + public void Should_Return_True_If_ProviderName_Is_Same() + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .Create(); + + // When / Then + CompareIssues(issue1, issue2, true); + } + + [Fact] + public void Should_Remove_Identical_Issues_From_List_Of_Issues() + { + // Given + var issue1_1 = + IssueBuilder + .NewIssue("message1", "providerType1", "providerName1") + .Create(); + var issue1_2 = + IssueBuilder + .NewIssue("message1", "providerType1", "providerName1") + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message2", "providerType2", "providerName2") + .Create(); + var issue3 = + IssueBuilder + .NewIssue("message3", "providerType3", "providerName3") + .Create(); + var issues1 = new List { issue1_1, issue2 }; + var issues2 = new List { issue1_2, issue3 }; + var comparer = new IIssueComparer(); + + // When + var result = issues1.Except(issues2, comparer); + + // Then + result.Count().ShouldBe(1); + result.ShouldContain(issue2); + } + + private static void CompareIssues(IIssue issue1, IIssue issue2, bool expectedToBeEqual) + { + var comparer = new IIssueComparer(false); + + comparer.Equals(issue1, issue2).ShouldBe(expectedToBeEqual); + + if (expectedToBeEqual) + { + comparer.GetHashCode(issue1).ShouldBe(comparer.GetHashCode(issue2)); + } + else + { + comparer.GetHashCode(issue1).ShouldNotBe(comparer.GetHashCode(issue2)); + } + } + } + + public sealed class TheCtorWithCompareOnlyPersistentPropertiesSetToTrue + { + [Fact] + public void Should_Return_False_If_First_Issue_Is_Null() + { + // Given + IIssue issue1 = null; + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .Create(); + + // When / Then + CompareIssues(issue1, issue2, false); + } + + [Fact] + public void Should_Return_False_If_Second_Issue_Is_Null() + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .Create(); + IIssue issue2 = null; + + // When / Then + CompareIssues(issue1, issue2, false); + } + + [Theory] + [InlineData("foo", "bar")] + [InlineData("foo", "Foo")] + [InlineData("foo", null)] + [InlineData(null, "foo")] + public void Should_Return_False_If_ProjectName_Is_Different(string projectName1, string projectName2) + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InProjectOfName(projectName1) + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InProjectOfName(projectName2) + .Create(); + + // When / Then + CompareIssues(issue1, issue2, false); + } + + [Fact] + public void Should_Return_False_If_MessageText_Is_Different() + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message1", "providerType", "providerName") + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message2", "providerType", "providerName") + .Create(); + + // When / Then + CompareIssues(issue1, issue2, false); + } + + [Theory] + [InlineData("foo", "bar")] + [InlineData("foo", "Foo")] + [InlineData("foo", null)] + [InlineData(null, "foo")] + public void Should_Return_False_If_MessageHtml_Is_Different(string message1, string message2) + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .WithMessageInHtmlFormat(message1) + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .WithMessageInHtmlFormat(message2) + .Create(); + + // When / Then + CompareIssues(issue1, issue2, false); + } + + [Theory] + [InlineData("foo", "bar")] + [InlineData("foo", "Foo")] + [InlineData("foo", null)] + [InlineData(null, "foo")] + public void Should_Return_False_If_MessageMarkdown_Is_Different(string message1, string message2) + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .WithMessageInMarkdownFormat(message1) + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .WithMessageInMarkdownFormat(message2) + .Create(); + + // When / Then + CompareIssues(issue1, issue2, false); + } + + [Theory] + [InlineData(1, 2)] + [InlineData(1, null)] + [InlineData(null, 1)] + [InlineData(int.MinValue, 0)] + [InlineData(int.MaxValue, 0)] + public void Should_Return_False_If_Priority_Is_Different(int? priority1, int? priority2) + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .WithPriority(priority1, "Foo") + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .WithPriority(priority2, "Foo") + .Create(); + + // When / Then + CompareIssues(issue1, issue2, false); + } + + [Theory] + [InlineData("foo", "bar")] + [InlineData("foo", "Foo")] + [InlineData("foo", null)] + [InlineData(null, "foo")] + public void Should_Return_False_If_PriorityName_Is_Different(string priorityName1, string priorityName2) + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .WithPriority(42, priorityName1) + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .WithPriority(42, priorityName2) + .Create(); + + // When / Then + CompareIssues(issue1, issue2, false); + } + + [Theory] + [InlineData("foo", "bar")] + [InlineData("foo", "Foo")] + [InlineData("foo", null)] + [InlineData(null, "foo")] + public void Should_Return_False_If_Rule_Is_Different(string rule1, string rule2) + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .OfRule(rule1) + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .OfRule(rule2) + .Create(); + + // When / Then + CompareIssues(issue1, issue2, false); + } + + [Theory] + [InlineData("http://foo", "http://bar")] + [InlineData("http://foo", null)] + [InlineData(null, "http://foo")] + public void Should_Return_False_If_RuleUlr_Is_Different(string ruleUrl1, string ruleUrl2) + { + // Given + var issueBuilder = + IssueBuilder + .NewIssue("message", "providerType", "providerName"); + if (!string.IsNullOrEmpty(ruleUrl1)) + { + issueBuilder = + issueBuilder + .OfRule("foo", new Uri(ruleUrl1)); + } + + var issue1 = issueBuilder.Create(); + + issueBuilder = + IssueBuilder + .NewIssue("message", "providerType", "providerName"); + if (!string.IsNullOrEmpty(ruleUrl2)) + { + issueBuilder = + issueBuilder + .OfRule("foo", new Uri(ruleUrl2)); + } + + var issue2 = issueBuilder.Create(); + + // When / Then + CompareIssues(issue1, issue2, false); + } + + [Fact] + public void Should_Return_False_If_ProviderType_Is_Different() + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType1", "providerName") + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType2", "providerName") + .Create(); + + // When / Then + CompareIssues(issue1, issue2, false); + } + + [Fact] + public void Should_Return_False_If_ProviderName_Is_Different() + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName1") + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName2") + .Create(); + + // When / Then + CompareIssues(issue1, issue2, false); + } + + [Fact] + public void Should_Return_True_If_Same_Reference() + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .Create(); + var issue2 = issue1; + + // When / Then + CompareIssues(issue1, issue2, true); + } + + [Fact] + public void Should_Return_True_If_Both_Are_Null() + { + // Given + IIssue issue1 = null; + IIssue issue2 = null; + + // When / Then + CompareIssues(issue1, issue2, true); + } + + [Fact] + public void Should_Return_True_If_Properties_Are_The_Same() + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .Create(); + + // When / Then + CompareIssues(issue1, issue2, true); + } + + [Theory] + [InlineData("foo", "bar")] + [InlineData("foo", "Foo")] + [InlineData("foo", null)] + [InlineData(null, "foo")] + public void Should_Return_True_If_ProjectFileRelativePath_Is_Different(string path1, string path2) + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InProjectFile(path1) + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InProjectFile(path2) + .Create(); + + // When / Then + CompareIssues(issue1, issue2, true); + } + + [Theory] + [InlineData("foo", "foo")] + [InlineData("foo", "foo/")] + [InlineData("foo/", "foo")] + [InlineData("", "")] + [InlineData(null, null)] + public void Should_Return_True_If_ProjectFileRelativePath_Is_Same(string path1, string path2) + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InProjectFile(path1) + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InProjectFile(path2) + .Create(); + + // When / Then + CompareIssues(issue1, issue2, true); + } + + [Theory] + [InlineData("foo", "foo")] + [InlineData("", "")] + [InlineData(null, null)] + public void Should_Return_True_If_ProjectName_Is_Same(string projectName1, string projectName2) + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InProjectOfName(projectName1) + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InProjectOfName(projectName2) + .Create(); + + // When / Then + CompareIssues(issue1, issue2, true); + } + + [Theory] + [InlineData("foo", "bar")] + [InlineData("foo", "Foo")] + [InlineData("foo", null)] + [InlineData(null, "foo")] + public void Should_Return_True_If_AffectedFileRelativePath_Is_Different(string path1, string path2) + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InFile(path1) + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InFile(path2) + .Create(); + + // When / Then + CompareIssues(issue1, issue2, true); + } + + [Theory] + [InlineData("foo", "foo")] + [InlineData("foo", "foo/")] + [InlineData("foo/", "foo")] + [InlineData("", "")] + [InlineData(null, null)] + public void Should_Return_True_If_AffectedFileRelativePath_Is_Same(string path1, string path2) + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InFile(path1) + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InFile(path2) + .Create(); + + // When / Then + CompareIssues(issue1, issue2, true); + } + + [Theory] + [InlineData(1, 2)] + [InlineData(1, null)] + [InlineData(null, 1)] + [InlineData(int.MaxValue, 1)] + [InlineData(1, int.MaxValue)] + public void Should_Return_True_If_Line_Is_Different(int? line1, int? line2) + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InFile("foo", line1) + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InFile("foo", line2) + .Create(); + + // When / Then + CompareIssues(issue1, issue2, true); + } + + [Theory] + [InlineData(1, 1)] + [InlineData(null, null)] + [InlineData(int.MaxValue, int.MaxValue)] + public void Should_Return_True_If_Line_Is_Same(int? line1, int? line2) + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InFile("foo", line1) + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .InFile("foo", line2) + .Create(); + + // When / Then + CompareIssues(issue1, issue2, true); + } + + [Fact] + public void Should_Return_True_If_MessageText_Is_Same() + { + // Given + var issue1 = + IssueBuilder + .NewIssue("messageText", "providerType", "providerName") + .Create(); + var issue2 = + IssueBuilder + .NewIssue("messageText", "providerType", "providerName") + .Create(); + + // When / Then + CompareIssues(issue1, issue2, true); + } + + [Theory] + [InlineData("foo", "foo")] + [InlineData("", "")] + [InlineData(null, null)] + public void Should_Return_True_If_MessageHtml_Is_Same(string message1, string message2) + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .WithMessageInHtmlFormat(message1) + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .WithMessageInHtmlFormat(message2) + .Create(); + + // When / Then + CompareIssues(issue1, issue2, true); + } + + [Theory] + [InlineData("foo", "foo")] + [InlineData("", "")] + [InlineData(null, null)] + public void Should_Return_True_If_MessageMarkdown_Is_Same(string message1, string message2) + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .WithMessageInMarkdownFormat(message1) + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .WithMessageInMarkdownFormat(message2) + .Create(); + + // When / Then + CompareIssues(issue1, issue2, true); + } + + [Theory] + [InlineData(1, 1)] + [InlineData(null, null)] + [InlineData(0, 0)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + public void Should_Return_True_If_Priority_Is_Same(int? priority1, int? priority2) + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .WithPriority(priority1, "Foo") + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .WithPriority(priority2, "Foo") + .Create(); + + // When / Then + CompareIssues(issue1, issue2, true); + } + + [Theory] + [InlineData("foo", "foo")] + [InlineData("", "")] + [InlineData(null, null)] + public void Should_Return_True_If_PriorityName_Is_Same(string priorityName1, string priorityName2) + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .WithPriority(42, priorityName1) + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .WithPriority(42, priorityName2) + .Create(); + + // When / Then + CompareIssues(issue1, issue2, true); + } + + [Theory] + [InlineData("foo", "foo")] + [InlineData("", "")] + [InlineData(null, null)] + public void Should_Return_True_If_Rule_Is_Same(string rule1, string rule2) + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .OfRule(rule1) + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .OfRule(rule2) + .Create(); + + // When / Then + CompareIssues(issue1, issue2, true); + } + + [Theory] + [InlineData("http://foo", "http://foo")] + [InlineData("http://foo", "http://Foo")] + [InlineData(null, null)] + public void Should_Return_True_If_RuleUlr_Is_Same(string ruleUrl1, string ruleUrl2) + { + // Given + var issueBuilder = + IssueBuilder + .NewIssue("message", "providerType", "providerName"); + if (!string.IsNullOrEmpty(ruleUrl1)) + { + issueBuilder = + issueBuilder + .OfRule("foo", new Uri(ruleUrl1)); + } + + var issue1 = issueBuilder.Create(); + + issueBuilder = + IssueBuilder + .NewIssue("message", "providerType", "providerName"); + if (!string.IsNullOrEmpty(ruleUrl2)) + { + issueBuilder = + issueBuilder + .OfRule("foo", new Uri(ruleUrl2)); + } + + var issue2 = issueBuilder.Create(); + + // When / Then + CompareIssues(issue1, issue2, true); + } + + [Fact] + public void Should_Return_True_If_ProviderType_Is_Same() + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .Create(); + + // When / Then + CompareIssues(issue1, issue2, true); + } + + [Fact] + public void Should_Return_True_If_ProviderName_Is_Same() + { + // Given + var issue1 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message", "providerType", "providerName") + .Create(); + + // When / Then + CompareIssues(issue1, issue2, true); + } + + [Fact] + public void Should_Remove_Identical_Issues_From_List_Of_Issues() + { + // Given + var issue1_1 = + IssueBuilder + .NewIssue("message1", "providerType1", "providerName1") + .InFile("foo.cs", 10) + .Create(); + var issue1_2 = + IssueBuilder + .NewIssue("message1", "providerType1", "providerName1") + .InFile("foo.cs", 20) + .Create(); + var issue2 = + IssueBuilder + .NewIssue("message2", "providerType2", "providerName2") + .Create(); + var issue3 = + IssueBuilder + .NewIssue("message3", "providerType3", "providerName3") + .Create(); + var issues1 = new List { issue1_1, issue2 }; + var issues2 = new List { issue1_2, issue3 }; + var comparer = new IIssueComparer(true); + + // When + var result = issues1.Except(issues2, comparer); + + // Then + result.Count().ShouldBe(1); + result.ShouldContain(issue2); + } + + private static void CompareIssues(IIssue issue1, IIssue issue2, bool expectedToBeEqual) + { + var comparer = new IIssueComparer(true); + + comparer.Equals(issue1, issue2).ShouldBe(expectedToBeEqual); + + if (expectedToBeEqual) + { + comparer.GetHashCode(issue1).ShouldBe(comparer.GetHashCode(issue2)); + } + else + { + comparer.GetHashCode(issue1).ShouldNotBe(comparer.GetHashCode(issue2)); + } + } + } + } +} diff --git a/src/Cake.Issues/IIssueComparer.cs b/src/Cake.Issues/IIssueComparer.cs new file mode 100644 index 000000000..d7367fba3 --- /dev/null +++ b/src/Cake.Issues/IIssueComparer.cs @@ -0,0 +1,135 @@ +namespace Cake.Issues +{ + using System.Collections.Generic; + + /// + /// Comparer to compare if two issues are identical. + /// + public class IIssueComparer : IEqualityComparer + { + private readonly bool compareOnlyPersistentProperties; + + /// + /// Initializes a new instance of the class. + /// Two issues are seen as identical if all properties have identical values. + /// + public IIssueComparer() + : this(false) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Flag indicating whether properties which + /// are affected by changes in files should be considered while comparing issues. + /// If set to true, the comparer can be used to compare issues from different + /// build runs, where files might have been changed or renamed. + /// + /// If is set to true the following + /// properties will be ignored while comparing the issue: + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public IIssueComparer(bool compareOnlyPersistentProperties) + { + this.compareOnlyPersistentProperties = compareOnlyPersistentProperties; + } + + /// + public bool Equals(IIssue x, IIssue y) + { + if (object.ReferenceEquals(x, y)) + { + return true; + } + + if (x is null || y is null) + { + return false; + } + + return + (this.compareOnlyPersistentProperties || x.ProjectFileRelativePath?.FullPath == y.ProjectFileRelativePath?.FullPath) && + (x.ProjectName == y.ProjectName) && + (this.compareOnlyPersistentProperties || x.AffectedFileRelativePath?.FullPath == y.AffectedFileRelativePath?.FullPath) && + (this.compareOnlyPersistentProperties || x.Line == y.Line) && + (x.MessageText == y.MessageText) && + (x.MessageHtml == y.MessageHtml) && + (x.MessageMarkdown == y.MessageMarkdown) && + (x.Priority == y.Priority) && + (x.PriorityName == y.PriorityName) && + (x.Rule == y.Rule) && + (x.RuleUrl?.ToString() == y.RuleUrl?.ToString()) && + (x.ProviderType == y.ProviderType) && + (x.ProviderName == y.ProviderName); + } + + /// + public int GetHashCode(IIssue obj) + { + if (obj is null) + { + return 0; + } + + if (this.compareOnlyPersistentProperties) + { + return + GetHashCode( + obj.ProjectName, + obj.MessageText, + obj.MessageHtml, + obj.MessageMarkdown, + obj.Priority, + obj.PriorityName, + obj.Rule, + obj.RuleUrl, + obj.ProviderType, + obj.ProviderName); + } + else + { + return + GetHashCode( + obj.ProjectFileRelativePath?.ToString(), + obj.ProjectName, + obj.AffectedFileRelativePath?.ToString(), + obj.Line, + obj.MessageText, + obj.MessageHtml, + obj.MessageMarkdown, + obj.Priority, + obj.PriorityName, + obj.Rule, + obj.RuleUrl, + obj.ProviderType, + obj.ProviderName); + } + } + + private static int GetHashCode(params object[] objects) + { + unchecked + { + int hash = 17; + + foreach (var obj in objects) + { + hash = (23 * hash) + (obj is object ? obj.GetHashCode() : 0); + } + + return hash; + } + } + } +} From cfc420639e1b4826af81a336f55f2d7c52d545f3 Mon Sep 17 00:00:00 2001 From: Pascal Berger Date: Thu, 10 Oct 2019 21:17:35 +0200 Subject: [PATCH 12/18] Update release notes link --- nuspec/nuget/Cake.Issues.Testing.nuspec | 2 +- nuspec/nuget/Cake.Issues.nuspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nuspec/nuget/Cake.Issues.Testing.nuspec b/nuspec/nuget/Cake.Issues.Testing.nuspec index 2b702c31f..68773e8b7 100644 --- a/nuspec/nuget/Cake.Issues.Testing.nuspec +++ b/nuspec/nuget/Cake.Issues.Testing.nuspec @@ -17,7 +17,7 @@ Common helpers for testing add-ins based on Cake.Issues Copyright © BBT Software AG and contributors Cake Script Cake-Issues Issues Testing - https://github.com/cake-contrib/Cake.Issues/releases/tag/0.7.1 + https://github.com/cake-contrib/Cake.Issues/releases/tag/0.8.0 diff --git a/nuspec/nuget/Cake.Issues.nuspec b/nuspec/nuget/Cake.Issues.nuspec index 1a698374e..cbc594d3c 100644 --- a/nuspec/nuget/Cake.Issues.nuspec +++ b/nuspec/nuget/Cake.Issues.nuspec @@ -24,7 +24,7 @@ See the Project Site for an overview of the whole ecosystem of addins for workin Copyright © BBT Software AG and contributors Cake Script Cake-Issues CodeAnalysis Linting Issues - https://github.com/cake-contrib/Cake.Issues/releases/tag/0.7.1 + https://github.com/cake-contrib/Cake.Issues/releases/tag/0.8.0 From 832e5c71d095d5a479b5f05bbf724b3103b7f246 Mon Sep 17 00:00:00 2001 From: Pascal Berger Date: Thu, 10 Oct 2019 21:23:16 +0200 Subject: [PATCH 13/18] Update test robustness --- .../IssueSerializationExtensionsTests.cs | 130 ++++++++++++++---- 1 file changed, 104 insertions(+), 26 deletions(-) diff --git a/src/Cake.Issues.Tests/Serialization/IssueSerializationExtensionsTests.cs b/src/Cake.Issues.Tests/Serialization/IssueSerializationExtensionsTests.cs index 343ab1f3d..a280ff2af 100644 --- a/src/Cake.Issues.Tests/Serialization/IssueSerializationExtensionsTests.cs +++ b/src/Cake.Issues.Tests/Serialization/IssueSerializationExtensionsTests.cs @@ -687,7 +687,10 @@ public void Should_Give_Correct_Result_For_MessageText_After_Roundtrip() } finally { - System.IO.File.Delete(filePath.FullPath); + if (System.IO.File.Exists(filePath.FullPath)) + { + System.IO.File.Delete(filePath.FullPath); + } } } @@ -714,7 +717,10 @@ public void Should_Give_Correct_Result_For_MessageMarkdown_After_Roundtrip() } finally { - System.IO.File.Delete(filePath.FullPath); + if (System.IO.File.Exists(filePath.FullPath)) + { + System.IO.File.Delete(filePath.FullPath); + } } } @@ -741,7 +747,10 @@ public void Should_Give_Correct_Result_For_MessageHtml_After_Roundtrip() } finally { - System.IO.File.Delete(filePath.FullPath); + if (System.IO.File.Exists(filePath.FullPath)) + { + System.IO.File.Delete(filePath.FullPath); + } } } @@ -767,7 +776,10 @@ public void Should_Give_Correct_Result_For_ProviderType_After_Roundtrip() } finally { - System.IO.File.Delete(filePath.FullPath); + if (System.IO.File.Exists(filePath.FullPath)) + { + System.IO.File.Delete(filePath.FullPath); + } } } @@ -793,7 +805,10 @@ public void Should_Give_Correct_Result_For_ProviderName_After_Roundtrip() } finally { - System.IO.File.Delete(filePath.FullPath); + if (System.IO.File.Exists(filePath.FullPath)) + { + System.IO.File.Delete(filePath.FullPath); + } } } @@ -820,7 +835,10 @@ public void Should_Give_Correct_Result_For_ProjectFileRelativePath_After_Roundtr } finally { - System.IO.File.Delete(filePath.FullPath); + if (System.IO.File.Exists(filePath.FullPath)) + { + System.IO.File.Delete(filePath.FullPath); + } } } @@ -847,7 +865,10 @@ public void Should_Give_Correct_Result_For_ProjectName_After_Roundtrip() } finally { - System.IO.File.Delete(filePath.FullPath); + if (System.IO.File.Exists(filePath.FullPath)) + { + System.IO.File.Delete(filePath.FullPath); + } } } @@ -874,7 +895,10 @@ public void Should_Give_Correct_Result_For_AffectedFileRelativePath_After_Roundt } finally { - System.IO.File.Delete(filePath.FullPath); + if (System.IO.File.Exists(filePath.FullPath)) + { + System.IO.File.Delete(filePath.FullPath); + } } } @@ -901,7 +925,10 @@ public void Should_Give_Correct_Result_For_Line_After_Roundtrip() } finally { - System.IO.File.Delete(filePath.FullPath); + if (System.IO.File.Exists(filePath.FullPath)) + { + System.IO.File.Delete(filePath.FullPath); + } } } @@ -928,7 +955,10 @@ public void Should_Give_Correct_Result_For_Priority_After_Roundtrip() } finally { - System.IO.File.Delete(filePath.FullPath); + if (System.IO.File.Exists(filePath.FullPath)) + { + System.IO.File.Delete(filePath.FullPath); + } } } @@ -955,7 +985,10 @@ public void Should_Give_Correct_Result_For_PriorityName_After_Roundtrip() } finally { - System.IO.File.Delete(filePath.FullPath); + if (System.IO.File.Exists(filePath.FullPath)) + { + System.IO.File.Delete(filePath.FullPath); + } } } @@ -982,7 +1015,10 @@ public void Should_Give_Correct_Result_For_Rule_After_Roundtrip() } finally { - System.IO.File.Delete(filePath.FullPath); + if (System.IO.File.Exists(filePath.FullPath)) + { + System.IO.File.Delete(filePath.FullPath); + } } } @@ -1009,7 +1045,10 @@ public void Should_Give_Correct_Result_For_RuleUrl_After_Roundtrip() } finally { - System.IO.File.Delete(filePath.FullPath); + if (System.IO.File.Exists(filePath.FullPath)) + { + System.IO.File.Delete(filePath.FullPath); + } } } } @@ -1075,7 +1114,10 @@ public void Should_Give_Correct_Result_For_MessageText_After_Roundtrip() } finally { - System.IO.File.Delete(filePath.FullPath); + if (System.IO.File.Exists(filePath.FullPath)) + { + System.IO.File.Delete(filePath.FullPath); + } } } @@ -1112,7 +1154,10 @@ public void Should_Give_Correct_Result_For_MessageMarkdown_After_Roundtrip() } finally { - System.IO.File.Delete(filePath.FullPath); + if (System.IO.File.Exists(filePath.FullPath)) + { + System.IO.File.Delete(filePath.FullPath); + } } } @@ -1149,7 +1194,10 @@ public void Should_Give_Correct_Result_For_MessageHtml_After_Roundtrip() } finally { - System.IO.File.Delete(filePath.FullPath); + if (System.IO.File.Exists(filePath.FullPath)) + { + System.IO.File.Delete(filePath.FullPath); + } } } @@ -1184,7 +1232,10 @@ public void Should_Give_Correct_Result_For_ProviderType_After_Roundtrip() } finally { - System.IO.File.Delete(filePath.FullPath); + if (System.IO.File.Exists(filePath.FullPath)) + { + System.IO.File.Delete(filePath.FullPath); + } } } @@ -1219,7 +1270,10 @@ public void Should_Give_Correct_Result_For_ProviderName_After_Roundtrip() } finally { - System.IO.File.Delete(filePath.FullPath); + if (System.IO.File.Exists(filePath.FullPath)) + { + System.IO.File.Delete(filePath.FullPath); + } } } @@ -1256,7 +1310,10 @@ public void Should_Give_Correct_Result_For_ProjectFileRelativePath_After_Roundtr } finally { - System.IO.File.Delete(filePath.FullPath); + if (System.IO.File.Exists(filePath.FullPath)) + { + System.IO.File.Delete(filePath.FullPath); + } } } @@ -1293,7 +1350,10 @@ public void Should_Give_Correct_Result_For_ProjectName_After_Roundtrip() } finally { - System.IO.File.Delete(filePath.FullPath); + if (System.IO.File.Exists(filePath.FullPath)) + { + System.IO.File.Delete(filePath.FullPath); + } } } @@ -1330,7 +1390,10 @@ public void Should_Give_Correct_Result_For_AffectedFileRelativePath_After_Roundt } finally { - System.IO.File.Delete(filePath.FullPath); + if (System.IO.File.Exists(filePath.FullPath)) + { + System.IO.File.Delete(filePath.FullPath); + } } } @@ -1367,7 +1430,10 @@ public void Should_Give_Correct_Result_For_Line_After_Roundtrip() } finally { - System.IO.File.Delete(filePath.FullPath); + if (System.IO.File.Exists(filePath.FullPath)) + { + System.IO.File.Delete(filePath.FullPath); + } } } @@ -1404,7 +1470,10 @@ public void Should_Give_Correct_Result_For_Priority_After_Roundtrip() } finally { - System.IO.File.Delete(filePath.FullPath); + if (System.IO.File.Exists(filePath.FullPath)) + { + System.IO.File.Delete(filePath.FullPath); + } } } @@ -1441,7 +1510,10 @@ public void Should_Give_Correct_Result_For_PriorityName_After_Roundtrip() } finally { - System.IO.File.Delete(filePath.FullPath); + if (System.IO.File.Exists(filePath.FullPath)) + { + System.IO.File.Delete(filePath.FullPath); + } } } @@ -1478,7 +1550,10 @@ public void Should_Give_Correct_Result_For_Rule_After_Roundtrip() } finally { - System.IO.File.Delete(filePath.FullPath); + if (System.IO.File.Exists(filePath.FullPath)) + { + System.IO.File.Delete(filePath.FullPath); + } } } @@ -1515,7 +1590,10 @@ public void Should_Give_Correct_Result_For_RuleUrl_After_Roundtrip() } finally { - System.IO.File.Delete(filePath.FullPath); + if (System.IO.File.Exists(filePath.FullPath)) + { + System.IO.File.Delete(filePath.FullPath); + } } } } From 3674b4bb54f0532d47b180d969116db9b1435798 Mon Sep 17 00:00:00 2001 From: Pascal Berger Date: Sun, 13 Oct 2019 10:08:33 +0200 Subject: [PATCH 14/18] Trigger pull request build for PRs targetting release or hotfix branches (#143) --- azure-pipelines.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 8f395df29..d7198be65 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -6,6 +6,8 @@ trigger: pr: - develop +- release/* +- hotfix/* jobs: - job: Windows From 354894ed3de1fe72b201ee94e6faa4946c25bc9c Mon Sep 17 00:00:00 2001 From: Pascal Berger Date: Sun, 13 Oct 2019 10:27:07 +0200 Subject: [PATCH 15/18] Exclude LitJson from code coverage (#144) --- recipe.cake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/recipe.cake b/recipe.cake index f2f01cac9..5836671e3 100644 --- a/recipe.cake +++ b/recipe.cake @@ -23,7 +23,7 @@ ToolSettings.SetToolSettings( BuildParameters.RootDirectoryPath + "/src/Cake.Issues*/Serialization/LitJson/*.cs", BuildParameters.RootDirectoryPath + "/src/Cake.Issues.Tests/**/*.cs" }, - testCoverageFilter: "+[*]* -[xunit.*]* -[Cake.Core]* -[Cake.Testing]* -[*.Tests]* -[Shouldly]*", + testCoverageFilter: "+[*]* -[xunit.*]* -[Cake.Core]* -[Cake.Testing]* -[*.Tests]* -[Cake.Issues]LitJson.* -[Shouldly]*", testCoverageExcludeByAttribute: "*.ExcludeFromCodeCoverage*", testCoverageExcludeByFile: "*/*Designer.cs;*/*.g.cs;*/*.g.i.cs"); From ab79d19baa56e383ed499ff526022f2801e0519a Mon Sep 17 00:00:00 2001 From: Pascal Berger Date: Sun, 13 Oct 2019 13:57:15 +0200 Subject: [PATCH 16/18] Mark LitJson as internal (#145) --- src/Cake.Issues/Serialization/LitJson/IJsonWrapper.cs | 4 ++-- src/Cake.Issues/Serialization/LitJson/JsonData.cs | 2 +- src/Cake.Issues/Serialization/LitJson/JsonException.cs | 2 +- src/Cake.Issues/Serialization/LitJson/JsonMapper.cs | 8 ++++---- src/Cake.Issues/Serialization/LitJson/JsonMockWrapper.cs | 2 +- src/Cake.Issues/Serialization/LitJson/JsonReader.cs | 4 ++-- src/Cake.Issues/Serialization/LitJson/JsonWriter.cs | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Cake.Issues/Serialization/LitJson/IJsonWrapper.cs b/src/Cake.Issues/Serialization/LitJson/IJsonWrapper.cs index 423708172..34b2b820d 100644 --- a/src/Cake.Issues/Serialization/LitJson/IJsonWrapper.cs +++ b/src/Cake.Issues/Serialization/LitJson/IJsonWrapper.cs @@ -23,7 +23,7 @@ namespace LitJson { - public enum JsonType + internal enum JsonType { None, @@ -36,7 +36,7 @@ public enum JsonType Boolean } - public interface IJsonWrapper : IList, IOrderedDictionary + internal interface IJsonWrapper : IList, IOrderedDictionary { bool IsArray { get; } bool IsBoolean { get; } diff --git a/src/Cake.Issues/Serialization/LitJson/JsonData.cs b/src/Cake.Issues/Serialization/LitJson/JsonData.cs index 3198eeb45..d595bfc2e 100644 --- a/src/Cake.Issues/Serialization/LitJson/JsonData.cs +++ b/src/Cake.Issues/Serialization/LitJson/JsonData.cs @@ -25,7 +25,7 @@ namespace LitJson { - public class JsonData : IJsonWrapper, IEquatable + internal class JsonData : IJsonWrapper, IEquatable { #region Fields private IList inst_array; diff --git a/src/Cake.Issues/Serialization/LitJson/JsonException.cs b/src/Cake.Issues/Serialization/LitJson/JsonException.cs index 721d400bb..6353b6995 100644 --- a/src/Cake.Issues/Serialization/LitJson/JsonException.cs +++ b/src/Cake.Issues/Serialization/LitJson/JsonException.cs @@ -20,7 +20,7 @@ namespace LitJson { - public class JsonException : + internal class JsonException : #if NETSTANDARD1_5 Exception #else diff --git a/src/Cake.Issues/Serialization/LitJson/JsonMapper.cs b/src/Cake.Issues/Serialization/LitJson/JsonMapper.cs index 36690405e..d9dd42f3f 100644 --- a/src/Cake.Issues/Serialization/LitJson/JsonMapper.cs +++ b/src/Cake.Issues/Serialization/LitJson/JsonMapper.cs @@ -95,15 +95,15 @@ public IDictionary Properties { internal delegate void ExporterFunc (object obj, JsonWriter writer); - public delegate void ExporterFunc (T obj, JsonWriter writer); + internal delegate void ExporterFunc (T obj, JsonWriter writer); internal delegate object ImporterFunc (object input); - public delegate TValue ImporterFunc (TJson input); + internal delegate TValue ImporterFunc (TJson input); - public delegate IJsonWrapper WrapperFactory (); + internal delegate IJsonWrapper WrapperFactory (); - public class JsonMapper + internal class JsonMapper { #region Fields private static readonly int max_nesting_depth; diff --git a/src/Cake.Issues/Serialization/LitJson/JsonMockWrapper.cs b/src/Cake.Issues/Serialization/LitJson/JsonMockWrapper.cs index 5dce9086d..2b02d338d 100644 --- a/src/Cake.Issues/Serialization/LitJson/JsonMockWrapper.cs +++ b/src/Cake.Issues/Serialization/LitJson/JsonMockWrapper.cs @@ -23,7 +23,7 @@ namespace LitJson { - public class JsonMockWrapper : IJsonWrapper + internal class JsonMockWrapper : IJsonWrapper { public bool IsArray { get { return false; } } public bool IsBoolean { get { return false; } } diff --git a/src/Cake.Issues/Serialization/LitJson/JsonReader.cs b/src/Cake.Issues/Serialization/LitJson/JsonReader.cs index 8d6496507..b298d6aad 100644 --- a/src/Cake.Issues/Serialization/LitJson/JsonReader.cs +++ b/src/Cake.Issues/Serialization/LitJson/JsonReader.cs @@ -24,7 +24,7 @@ namespace LitJson { - public enum JsonToken + internal enum JsonToken { None, @@ -46,7 +46,7 @@ public enum JsonToken } - public class JsonReader + internal class JsonReader { #region Fields private static readonly IDictionary> parse_table; diff --git a/src/Cake.Issues/Serialization/LitJson/JsonWriter.cs b/src/Cake.Issues/Serialization/LitJson/JsonWriter.cs index 575e86f8d..63af4dde8 100644 --- a/src/Cake.Issues/Serialization/LitJson/JsonWriter.cs +++ b/src/Cake.Issues/Serialization/LitJson/JsonWriter.cs @@ -42,7 +42,7 @@ internal class WriterContext public int Padding; } - public class JsonWriter + internal class JsonWriter { #region Fields private static readonly NumberFormatInfo number_format; From 60ccec9e6104b0a1e118acee4763180382d9c8d4 Mon Sep 17 00:00:00 2001 From: Pascal Berger Date: Thu, 17 Oct 2019 20:59:14 +0200 Subject: [PATCH 17/18] (GH-148) Embed icon in NuGet package (#149) --- .appveyor.yml | 4 ++++ nuspec/nuget/Cake.Issues.nuspec | 3 ++- nuspec/nuget/icon.png | Bin 0 -> 15580 bytes 3 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 nuspec/nuget/icon.png diff --git a/.appveyor.yml b/.appveyor.yml index 0d21fc40b..c180aecce 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -6,6 +6,10 @@ image: Visual Studio 2017 #---------------------------------# # Build Script # #---------------------------------# +install: + # Update to latest NuGet version since we require 5.3.0 for embedded icon + - ps: nuget update -self + build_script: - ps: .\build.ps1 -Target AppVeyor diff --git a/nuspec/nuget/Cake.Issues.nuspec b/nuspec/nuget/Cake.Issues.nuspec index cbc594d3c..a8e8c08e6 100644 --- a/nuspec/nuget/Cake.Issues.nuspec +++ b/nuspec/nuget/Cake.Issues.nuspec @@ -19,7 +19,7 @@ See the Project Site for an overview of the whole ecosystem of addins for workin MIT https://cakeissues.net - https://cdn.jsdelivr.net/gh/cake-contrib/graphics@a5cf0f881c390650144b2243ae551d5b9f836196/png/cake-contrib-medium.png + icon.png false Copyright © BBT Software AG and contributors @@ -27,6 +27,7 @@ See the Project Site for an overview of the whole ecosystem of addins for workin https://github.com/cake-contrib/Cake.Issues/releases/tag/0.8.0 + diff --git a/nuspec/nuget/icon.png b/nuspec/nuget/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..9881edc4e937ff34999ecf375c550249d82acc32 GIT binary patch literal 15580 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 literal 0 HcmV?d00001 From 655645e2efe6d213bf280759cbe8d7e353c97459 Mon Sep 17 00:00:00 2001 From: Pascal Berger Date: Thu, 17 Oct 2019 21:25:55 +0200 Subject: [PATCH 18/18] (GH-148) Embed icon in Cake.Issues.Testing NuGet package (#150) --- nuspec/nuget/Cake.Issues.Testing.nuspec | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nuspec/nuget/Cake.Issues.Testing.nuspec b/nuspec/nuget/Cake.Issues.Testing.nuspec index 68773e8b7..c536efe0e 100644 --- a/nuspec/nuget/Cake.Issues.Testing.nuspec +++ b/nuspec/nuget/Cake.Issues.Testing.nuspec @@ -12,7 +12,7 @@ Common helpers for testing add-ins based on Cake.Issues MIT https://cakeissues.net - https://cdn.jsdelivr.net/gh/cake-contrib/graphics@a5cf0f881c390650144b2243ae551d5b9f836196/png/cake-contrib-medium.png + icon.png false Copyright © BBT Software AG and contributors @@ -20,6 +20,7 @@ Common helpers for testing add-ins based on Cake.Issues https://github.com/cake-contrib/Cake.Issues/releases/tag/0.8.0 +