Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add NLS test scenario #6120

Merged
merged 6 commits into from
Jan 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
}
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();
}
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";
}
}
}
Loading