Skip to content

Commit

Permalink
Add NLS test scenario (#6120)
Browse files Browse the repository at this point in the history
  • Loading branch information
lbussell authored Jan 6, 2025
1 parent 950a600 commit 715cde0
Show file tree
Hide file tree
Showing 8 changed files with 218 additions and 2 deletions.
5 changes: 5 additions & 0 deletions tests/Microsoft.DotNet.Docker.Tests/AspnetImageTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ public async Task VerifyFxDependentAppScenario(ProductImageData imageData)
public async Task VerifyGlobalizationScenario(ProductImageData imageData) =>
await VerifyGlobalizationScenarioBase(imageData);

[WindowsImageTheory]
[MemberData(nameof(GetImageData))]
public async Task VerifyNLSScenario(ProductImageData imageData) =>
await VerifyNlsScenarioBase(imageData);

[DotNetTheory]
[MemberData(nameof(GetImageData))]
public void VerifyEnvironmentVariables(ProductImageData imageData)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,5 +82,11 @@ protected async Task VerifyGlobalizationScenarioBase(ProductImageData imageData)
using var testScenario = new GlobalizationScenario(imageData, ImageRepo, DockerHelper);
await testScenario.ExecuteAsync();
}

protected async Task VerifyNlsScenarioBase(ProductImageData imageData)
{
using var testScenario = new NlsScenario(imageData, ImageRepo, DockerHelper);
await testScenario.ExecuteAsync();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@
</ItemGroup>

<ItemGroup>
<Compile Remove="TestAppArtifacts\GlobalizationTest.cs" />
<Compile Remove="TestAppArtifacts\UnitTests.cs" />
<Compile Remove="TestAppArtifacts/*.cs" />
</ItemGroup>

</Project>
5 changes: 5 additions & 0 deletions tests/Microsoft.DotNet.Docker.Tests/RuntimeImageTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ public async Task VerifyTestProjectScenario(ProductImageData imageData)
public async Task VerifyGlobalizationScenario(ProductImageData imageData) =>
await VerifyGlobalizationScenarioBase(imageData);

[WindowsImageTheory]
[MemberData(nameof(GetImageData))]
public async Task VerifyNLSScenario(ProductImageData imageData) =>
await VerifyNlsScenarioBase(imageData);

[DotNetTheory]
[MemberData(nameof(GetImageData))]
public void VerifyEnvironmentVariables(ProductImageData imageData)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
ARG sdk_image
ARG runtime_image

FROM ${sdk_image} AS sdk
RUN dotnet new console -n App -o /src --no-restore
WORKDIR /src
COPY NLSTest.cs /src/Program.cs
RUN dotnet restore
RUN dotnet publish --no-restore -o /app

FROM ${runtime_image} AS runtime
ARG icu_expected
ENV ICU_EXPECTED=${icu_expected}
COPY --from=sdk /app /app/
ENTRYPOINT ["/app/App"]
87 changes: 87 additions & 0 deletions tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/NLSTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
using System.Globalization;
using static System.Console;

const string nlsVar = "DOTNET_SYSTEM_GLOBALIZATION_USENLS";
const string icuVar = "ICU_EXPECTED";

GetEnvironmentVariableValue(nlsVar);
bool icuExpected = GetEnvironmentVariableValue(icuVar);

bool icuMode = IsIcuMode();
WriteLine($"Detected ICU mode: {icuMode}");

if (icuMode != icuExpected)
{
throw new Exception($"ICU mode detected as {icuMode}, expected {icuExpected}");
}

// https://learn.microsoft.com/en-us/dotnet/core/extensions/globalization-icu#stringendswith
Assert(
""" "abc".EndsWith("\0")""",
"abc".EndsWith("\0").ToString(),
icuResult: true.ToString(),
nlsResult: false.ToString(),
icuMode);
Assert(
""" "abc".EndsWith("\0", StringComparison.CurrentCulture)""",
"abc".EndsWith("\0", StringComparison.CurrentCulture).ToString(),
icuResult: true.ToString(),
nlsResult: false.ToString(),
icuMode);

// https://learn.microsoft.com/en-us/dotnet/core/extensions/globalization-icu#stringstartswith
Assert(
""" "foo".StartsWith("\0")""",
"foo".StartsWith("\0").ToString(),
icuResult: true.ToString(),
nlsResult: false.ToString(),
icuMode);
Assert(
""" "foo".StartsWith("\0", StringComparison.CurrentCulture)""",
"foo".StartsWith("\0", StringComparison.CurrentCulture).ToString(),
icuResult: true.ToString(),
nlsResult: false.ToString(),
icuMode);

// https://learn.microsoft.com/en-us/dotnet/core/extensions/globalization-icu#stringindexof
Assert(
""" "Hel\0lo".IndexOf("\0")""",
"Hel\0lo".IndexOf("\0").ToString(),
icuResult: "0",
nlsResult: "3",
icuMode);
Assert(
""" "Hel\0lo".IndexOf("\0", StringComparison.CurrentCulture)""",
"Hel\0lo".IndexOf("\0", StringComparison.CurrentCulture).ToString(),
icuResult: "0",
nlsResult: "3",
icuMode);

WriteLine("All assertions passed!");

// https://learn.microsoft.com/en-us/dotnet/core/extensions/globalization-icu#determine-if-your-app-is-using-icu
bool IsIcuMode()
{
SortVersion sortVersion = CultureInfo.InvariantCulture.CompareInfo.Version;
byte[] bytes = sortVersion.SortId.ToByteArray();
int version = bytes[3] << 24 | bytes[2] << 16 | bytes[1] << 8 | bytes[0];
return version != 0 && version == sortVersion.FullVersion;
}

void Assert(string functionText, string actualResult, string icuResult, string nlsResult, bool icuExpected)
{
string expectedResult = icuExpected ? icuResult : nlsResult;
Console.WriteLine($"{functionText} returned {actualResult}");
if (actualResult != expectedResult)
{
throw new Exception($"Assertion failed: {functionText} returned {actualResult}, expected {expectedResult}");
}
}

bool GetEnvironmentVariableValue(string variable)
{
string value = Environment.GetEnvironmentVariable(variable);
bool parsedValue = !string.IsNullOrWhiteSpace(value) && bool.Parse(value);
WriteLine($"{variable} evaluated to {parsedValue}");
return parsedValue;
}
83 changes: 83 additions & 0 deletions tests/Microsoft.DotNet.Docker.Tests/TestScenarios/NlsScenario.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.IO;
using System.Threading.Tasks;
using FluentAssertions;

namespace Microsoft.DotNet.Docker.Tests.TestScenarios;

#nullable enable
public sealed class NlsScenario : ITestScenario, IDisposable
{
private const string DockerfileName = "NLSTest.Dockerfile";

private const string TestSourceFileName = "NLSTest.cs";

private readonly TempFolderContext _tempFolderContext = FileHelper.UseTempFolder();

private readonly ProductImageData _imageData;

private readonly DotNetImageRepo _repo;

private readonly DockerHelper _dockerHelper;

public NlsScenario(
ProductImageData imageData,
DotNetImageRepo repo,
DockerHelper dockerHelper)
{
_imageData = imageData;
_repo = repo;
_dockerHelper = dockerHelper;
}

// ICU is not supported on Nano Server
private bool IsIcuSupported => _imageData.OS.Contains(OS.ServerCore);

public Task ExecuteAsync()
{
// Setup project in temp dir
string dockerfilePath = Path.Combine(_tempFolderContext.Path, "Dockerfile");
File.Copy(
sourceFileName: Path.Combine(DockerHelper.TestArtifactsDir, DockerfileName),
destFileName: dockerfilePath);
File.Copy(
sourceFileName: Path.Combine(DockerHelper.TestArtifactsDir, TestSourceFileName),
destFileName: Path.Combine(_tempFolderContext.Path, TestSourceFileName));

string sdkImage = _imageData.GetImage(DotNetImageRepo.SDK, _dockerHelper);
string runtimeImage = _imageData.GetImage(_repo, _dockerHelper);
string[] buildArgs =
[
$"sdk_image={sdkImage}",
$"runtime_image={runtimeImage}",
];

if (IsIcuSupported)
{
buildArgs = [..buildArgs, $"icu_expected={IsIcuSupported}"];
}

string tag = nameof(NlsScenario).ToLowerInvariant();
_dockerHelper.Build(
tag: tag,
dockerfile: dockerfilePath,
contextDir: _tempFolderContext.Path,
pull: Config.PullImages,
buildArgs: buildArgs);

string containerName = ImageData.GenerateContainerName(nameof(NlsScenario));
Func<string> runImage = () => _dockerHelper.Run(tag, containerName);

string justification = $"image {runtimeImage} should{(IsIcuSupported ? "" : " not")} support ICU";
runImage.Should().NotThrow(because: justification)
.Which.Should().ContainEquivalentOf("All assertions passed", Exactly.Once());

return Task.CompletedTask;
}

public void Dispose() => _tempFolderContext.Dispose();
}
16 changes: 16 additions & 0 deletions tests/Microsoft.DotNet.Docker.Tests/WindowsImageTheoryAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

namespace Microsoft.DotNet.Docker.Tests;

public class WindowsImageTheoryAttribute : DotNetTheoryAttribute
{
public WindowsImageTheoryAttribute()
{
if (DockerHelper.IsLinuxContainerModeEnabled)
{
Skip = "Windows image test not applicable when running in Linux Container mode";
}
}
}

0 comments on commit 715cde0

Please sign in to comment.