Skip to content

Commit

Permalink
cake-buildGH-2897: Add tests for MyGetProvider
Browse files Browse the repository at this point in the history
  • Loading branch information
duracellko committed Oct 20, 2020
1 parent e32f10f commit e8b20ed
Show file tree
Hide file tree
Showing 3 changed files with 254 additions and 15 deletions.
34 changes: 34 additions & 0 deletions src/Cake.Common.Tests/Fixtures/Build/MyGetFixture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using Cake.Common.Build.MyGet;
using Cake.Common.Tests.Fakes;
using Cake.Core;
using NSubstitute;

namespace Cake.Common.Tests.Fixtures.Build
{
internal sealed class MyGetFixture
{
public ICakeEnvironment Environment { get; set; }

public FakeBuildSystemServiceMessageWriter Writer { get; set; }

public MyGetFixture()
{
Environment = Substitute.For<ICakeEnvironment>();
Environment.GetEnvironmentVariable("BuildRunner").Returns((string)null);
Writer = new FakeBuildSystemServiceMessageWriter();
}

public void IsRunningOnMyGet(bool? capitalCase = default)
{
var buildRunnerValue = "MyGet";
if (capitalCase.HasValue)
{
buildRunnerValue = capitalCase.Value ? "MYGET" : "myget";
}

Environment.GetEnvironmentVariable("BuildRunner").Returns(buildRunnerValue);
}

public MyGetProvider CreateMyGetProvider() => new MyGetProvider(Environment, Writer);
}
}
179 changes: 179 additions & 0 deletions src/Cake.Common.Tests/Unit/Build/MyGet/MyGetProviderTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
using Cake.Common.Build.MyGet;
using Cake.Common.Tests.Fakes;
using Cake.Common.Tests.Fixtures.Build;
using Cake.Core;
using Cake.Testing;
using Xunit;

namespace Cake.Common.Tests.Unit.Build.MyGet
{
public sealed class MyGetProviderTests
{
public sealed class TheConstructor
{
[Fact]
public void Should_Throw_If_Environment_Is_Null()
{
// Given, When
var writer = new FakeBuildSystemServiceMessageWriter();
var result = Record.Exception(() => new MyGetProvider(null, writer));

// Then
AssertEx.IsArgumentNullException(result, "environment");
}

[Fact]
public void Should_Throw_If_Writer_Is_Null()
{
// Given, When
var result = Record.Exception(() => new MyGetProvider(new FakeEnvironment(PlatformFamily.Unknown), null));

// Then
AssertEx.IsArgumentNullException(result, "writer");
}
}

public sealed class IsRunningOnMyGet
{
[Theory]
[InlineData(true)]
[InlineData(false)]
[InlineData(null)]
public void Should_Return_True_If_Running_On_MyGet(bool? capitalCase)
{
// Given
var fixture = new MyGetFixture();
fixture.IsRunningOnMyGet(capitalCase);
var provider = fixture.CreateMyGetProvider();

// When
var result = provider.IsRunningOnMyGet;

// Then
Assert.True(result);
}

[Fact]
public void Should_Return_False_If_Not_Running_On_MyGet()
{
// Given
var fixture = new MyGetFixture();
var provider = fixture.CreateMyGetProvider();

// When
var result = provider.IsRunningOnMyGet;

// Then
Assert.False(result);
}
}

public sealed class BuildProblem
{
[Theory]
[InlineData("Test build problem", "Test build problem")]
[InlineData("", "")]
[InlineData(null, "")]
[InlineData("[Special characters:\r\n\"\'test|split\'\"]", "|[Special characters:|r|n\"|\'test||split|\'\"|]")]
public void Should_Log_Description(string description, string expectedOutput)
{
// Given
var fixture = new MyGetFixture();
var provider = fixture.CreateMyGetProvider();

// When
provider.BuildProblem(description);

// Then
var entry = Assert.Single(fixture.Writer.Entries);
Assert.Equal($"##myget[buildProblem description='{expectedOutput}']", entry);
}
}

public sealed class SetParameter
{
[Theory]
[InlineData("Parameter", "Value", "Parameter", "Value")]
[InlineData("", "", "", "")]
[InlineData(null, null, "", "")]
[InlineData("Special [param] \'name\'", "test\n|\r||value||", "Special |[param|] |\'name|\'", "test|n|||r||||value||||")]
public void Should_Log_Parameter_Value(string name, string value, string expectedName, string expectedValue)
{
// Given
var fixture = new MyGetFixture();
var provider = fixture.CreateMyGetProvider();

// When
provider.SetParameter(name, value);

// Then
var entry = Assert.Single(fixture.Writer.Entries);
Assert.Equal($"##myget[setParameter name='{expectedName}' value='{expectedValue}']", entry);
}
}

public sealed class WriteStatus
{
[Theory]
[InlineData("M", MyGetBuildStatus.Normal, "M", "NORMAL")]
[InlineData(null, MyGetBuildStatus.Warning, "", "WARNING")]
[InlineData("Message \n text", MyGetBuildStatus.Error, "Message |n text", "ERROR")]
[InlineData("[Failure]|status", MyGetBuildStatus.Failure, "|[Failure|]||status", "FAILURE")]
public void Should_Log_Status(string message, MyGetBuildStatus status, string expectedMessage, string expectedStatus)
{
// Given
var fixture = new MyGetFixture();
var provider = fixture.CreateMyGetProvider();

// When
provider.WriteStatus(message, status);

// Then
var entry = Assert.Single(fixture.Writer.Entries);
Assert.Equal($"##myget[message text='{expectedMessage}' status='{expectedStatus}']", entry);
}

[Theory]
[InlineData("Hello, World!", MyGetBuildStatus.Normal, "", "Hello, World!", "NORMAL", "")]
[InlineData("My custom message", MyGetBuildStatus.Warning, "Hello, World!", "My custom message", "WARNING", "Hello, World!")]
[InlineData("Test", MyGetBuildStatus.Error, "r = (a - b) * c + (s1 & s2)", "Test", "ERROR", "r = (a - b) * c + (s1 & s2)")]
[InlineData("T", MyGetBuildStatus.Failure, "i = (b << 4) | c;\r\nr = a[i] / c;", "T", "FAILURE", "i = (b << 4) || c;|r|nr = a|[i|] / c;")]
public void Should_Log_Status_With_Error_Details(string message, MyGetBuildStatus status, string errorDetails, string expectedMessage, string expectedStatus, string expectedDetails)
{
// Given
var fixture = new MyGetFixture();
var provider = fixture.CreateMyGetProvider();

// When
provider.WriteStatus(message, status, errorDetails);

// Then
var entry = Assert.Single(fixture.Writer.Entries);
Assert.Equal($"##myget[message text='{expectedMessage}' status='{expectedStatus}' errorDetails='{expectedDetails}']", entry);
}
}

public sealed class SetBuildNumber
{
[Theory]
[InlineData("2.3.1", "2.3.1")]
[InlineData("", "")]
[InlineData(null, "")]
[InlineData("99.4-beta", "99.4-beta")]
[InlineData("[net5.0\r\n\"\'beta|preview\'\"]", "|[net5.0|r|n\"|\'beta||preview|\'\"|]")]
public void Should_Log_Build_Number(string buildNumber, string expectedOutput)
{
// Given
var fixture = new MyGetFixture();
var provider = fixture.CreateMyGetProvider();

// When
provider.SetBuildNumber(buildNumber);

// Then
var entry = Assert.Single(fixture.Writer.Entries);
Assert.Equal($"##myget[buildNumber '{expectedOutput}']", entry);
}
}
}
}
56 changes: 41 additions & 15 deletions src/Cake.Common/Build/MyGet/MyGetProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Text;
using Cake.Core;

namespace Cake.Common.Build.MyGet
Expand All @@ -19,22 +20,25 @@ public sealed class MyGetProvider : IMyGetProvider
private const string MessagePrefix = "##myget[";
private const string MessagePostfix = "]";

private static readonly Dictionary<string, string> _sanitizationTokens;
private static readonly Dictionary<char, string> _sanitizationTokens;
private static readonly char[] _specialCharacters;

private readonly ICakeEnvironment _environment;
private readonly IBuildSystemServiceMessageWriter _writer;

static MyGetProvider()
{
_sanitizationTokens = new Dictionary<string, string>
_sanitizationTokens = new Dictionary<char, string>
{
{ "|", "||" },
{ "\'", "|\'" },
{ "\n", "|n" },
{ "\r", "|r" },
{ "[", "|['" },
{ "]", "|]" }
{ '|', "||" },
{ '\'', "|\'" },
{ '\n', "|n" },
{ '\r', "|r" },
{ '[', "|[" },
{ ']', "|]" }
};

_specialCharacters = _sanitizationTokens.Keys.ToArray();
}

/// <summary>
Expand Down Expand Up @@ -137,7 +141,7 @@ public void SetBuildNumber(string buildNumber)

private void WriteServiceMessage(string messageName, string attributeValue)
{
WriteServiceMessage(messageName, new Dictionary<string, string> { { " ", attributeValue } });
WriteServiceMessage(messageName, new Dictionary<string, string> { { string.Empty, attributeValue } });
}

private void WriteServiceMessage(string messageName, string attributeName, string attributeValue)
Expand All @@ -153,11 +157,12 @@ private void WriteServiceMessage(string messageName, Dictionary<string, string>
values
.Select(keypair =>
{
if (string.IsNullOrWhiteSpace(keypair.Key))
var sanitizedValue = Sanitize(keypair.Value);
if (string.IsNullOrEmpty(keypair.Key))
{
return string.Format(CultureInfo.InvariantCulture, "'{0}'", Sanitize(keypair.Value));
return string.Format(CultureInfo.InvariantCulture, "'{0}'", sanitizedValue);
}
return string.Format(CultureInfo.InvariantCulture, "{0}='{1}'", keypair.Key, Sanitize(keypair.Value));
return string.Format(CultureInfo.InvariantCulture, "{0}='{1}'", keypair.Key, sanitizedValue);
})
.ToArray());

Expand All @@ -166,12 +171,33 @@ private void WriteServiceMessage(string messageName, Dictionary<string, string>

private static string Sanitize(string source)
{
foreach (var charPair in _sanitizationTokens)
if (string.IsNullOrEmpty(source))
{
return string.Empty;
}

// When source does not contain special characters, then it is possible to skip building new string.
// This approach can possibly iterate through string 2 times, but special characters are exceptional.
if (source.IndexOfAny(_specialCharacters) < 0)
{
return source;
}

var stringBuilder = new StringBuilder(source.Length * 2);
for (int index = 0; index < source.Length; index++)
{
source = source.Replace(charPair.Key, charPair.Value);
char sourceChar = source[index];
if (_sanitizationTokens.TryGetValue(sourceChar, out var replacement))
{
stringBuilder.Append(replacement);
}
else
{
stringBuilder.Append(sourceChar);
}
}

return source;
return stringBuilder.ToString();
}
}
}

0 comments on commit e8b20ed

Please sign in to comment.