diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Helpers/AcceptanceAssert.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Helpers/AcceptanceAssert.cs
index eead86e9ad..76b2c29d8d 100644
--- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Helpers/AcceptanceAssert.cs
+++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Helpers/AcceptanceAssert.cs
@@ -133,6 +133,34 @@ public static void AssertOutputMatchesRegex(this DotnetMuxerResult dotnetMuxerRe
public static void AssertOutputDoesNotContain(this TestHostResult testHostResult, string value, [CallerMemberName] string? callerMemberName = null, [CallerFilePath] string? callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0)
=> Assert.IsFalse(testHostResult.StandardOutput.Contains(value, StringComparison.Ordinal), GenerateFailedAssertionMessage(testHostResult, callerMemberName: callerMemberName, callerFilePath: callerFilePath, callerLineNumber: callerLineNumber));
+ ///
+ /// Asserts that the test host output contains the given value after Unicode normalization (FormC) and
+ /// non-breaking space (U+00A0) replacement. Use this instead of when
+ /// comparing localized strings that may use different normalization forms or typographic spacing conventions.
+ ///
+ public static void AssertOutputContainsNormalized(this TestHostResult testHostResult, string value, [CallerMemberName] string? callerMemberName = null, [CallerFilePath] string? callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0)
+ {
+ string normalizedOutput = NormalizeForLocalization(testHostResult.StandardOutput);
+ string normalizedValue = NormalizeForLocalization(value);
+ Assert.IsTrue(
+ normalizedOutput.Contains(normalizedValue, StringComparison.Ordinal),
+ GenerateFailedAssertionMessage(testHostResult, callerMemberName, callerFilePath, callerLineNumber));
+ }
+
+ ///
+ /// Asserts that the test host output does not contain the given value after Unicode normalization (FormC) and
+ /// non-breaking space (U+00A0) replacement. Use this instead of when
+ /// comparing localized strings that may use different normalization forms or typographic spacing conventions.
+ ///
+ public static void AssertOutputDoesNotContainNormalized(this TestHostResult testHostResult, string value, [CallerMemberName] string? callerMemberName = null, [CallerFilePath] string? callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0)
+ {
+ string normalizedOutput = NormalizeForLocalization(testHostResult.StandardOutput);
+ string normalizedValue = NormalizeForLocalization(value);
+ Assert.IsFalse(
+ normalizedOutput.Contains(normalizedValue, StringComparison.Ordinal),
+ GenerateFailedAssertionMessage(testHostResult, callerMemberName, callerFilePath, callerLineNumber));
+ }
+
public static void AssertStandardErrorContains(this TestHostResult testHostResult, string value, [CallerMemberName] string? callerMemberName = null, [CallerFilePath] string? callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0)
=> Assert.Contains(value, testHostResult.StandardError, StringComparison.Ordinal, GenerateFailedAssertionMessage(testHostResult, callerMemberName: callerMemberName, callerFilePath: callerFilePath, callerLineNumber: callerLineNumber));
@@ -165,4 +193,9 @@ private static string GenerateFailedAssertionMessage(TestHostResult testHostResu
private static string GenerateFailedAssertionMessage(DotnetMuxerResult dotnetMuxerResult, string? callerMemberName, string? callerFilePath, int callerLineNumber, [CallerMemberName] string? assertCallerMemberName = null)
=> $"Expression '{assertCallerMemberName}' failed for member '{callerMemberName}' at line {callerLineNumber} of file '{callerFilePath}'. Output of the dotnet muxer is:{Environment.NewLine}{dotnetMuxerResult}";
+
+ // Localized resource strings may use different Unicode normalization forms (NFC vs NFD) than C# string literals.
+ // French locale also uses non-breaking space (U+00A0) before colons per typographic convention.
+ private static string NormalizeForLocalization(string text)
+ => text.Normalize(NormalizationForm.FormC).Replace('\u00A0', ' ');
}
diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/LocalizationFailingTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/LocalizationFailingTests.cs
index a18df5479d..ec7421bdbd 100644
--- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/LocalizationFailingTests.cs
+++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/LocalizationFailingTests.cs
@@ -1,8 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-using System.Text;
-
namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests;
// Temporarily disabled: OneLocBuild keeps reverting the TerminalResources.*.xlf targets to English,
@@ -13,23 +11,6 @@ public sealed class LocalizationFailingTests : AcceptanceTestBase text.Normalize(NormalizationForm.FormC).Replace('\u00A0', ' ');
-
- private static void AssertOutputContainsNormalized(TestHostResult testHostResult, string value)
- {
- string normalizedOutput = NormalizeForComparison(testHostResult.StandardOutput);
- string normalizedValue = NormalizeForComparison(value);
- Assert.IsTrue(
- normalizedOutput.Contains(normalizedValue, StringComparison.Ordinal),
- $"Output does not contain '{value}'.{Environment.NewLine}Output:{Environment.NewLine}{testHostResult.StandardOutput}");
- }
-
[DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))]
[TestMethod]
public async Task Execution_WithFailingTest_OutputContainsTranslatedFailureSummary(string tfm)
@@ -42,8 +23,8 @@ public async Task Execution_WithFailingTest_OutputContainsTranslatedFailureSumma
testHostResult.AssertExitCodeIs(ExitCode.AtLeastOneTestFailed);
// Verify failure summary is in French ("Résumé de série de tests : Échec!")
- AssertOutputContainsNormalized(testHostResult, "Résumé de série de tests : Échec!");
- AssertOutputContainsNormalized(testHostResult, "échec: 1");
+ testHostResult.AssertOutputContainsNormalized("Résumé de série de tests : Échec!");
+ testHostResult.AssertOutputContainsNormalized("échec: 1");
}
public sealed class TestAssetFixture() : TestAssetFixtureBase()
diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/LocalizationTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/LocalizationTests.cs
index 3a1243b0c8..de8362093b 100644
--- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/LocalizationTests.cs
+++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/LocalizationTests.cs
@@ -1,8 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-using System.Text;
-
namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests;
// Temporarily disabled: OneLocBuild keeps reverting the TerminalResources.*.xlf targets to English,
@@ -13,32 +11,6 @@ public class LocalizationTests : AcceptanceTestBase text.Normalize(NormalizationForm.FormC).Replace('\u00A0', ' ');
-
- private static void AssertOutputContainsNormalized(TestHostResult testHostResult, string value)
- {
- string normalizedOutput = NormalizeForComparison(testHostResult.StandardOutput);
- string normalizedValue = NormalizeForComparison(value);
- Assert.IsTrue(
- normalizedOutput.Contains(normalizedValue, StringComparison.Ordinal),
- $"Output does not contain '{value}'.{Environment.NewLine}Output:{Environment.NewLine}{testHostResult.StandardOutput}");
- }
-
- private static void AssertOutputDoesNotContainNormalized(TestHostResult testHostResult, string value)
- {
- string normalizedOutput = NormalizeForComparison(testHostResult.StandardOutput);
- string normalizedValue = NormalizeForComparison(value);
- Assert.IsFalse(
- normalizedOutput.Contains(normalizedValue, StringComparison.Ordinal),
- $"Output should not contain '{value}'.{Environment.NewLine}Output:{Environment.NewLine}{testHostResult.StandardOutput}");
- }
-
[DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))]
[TestMethod]
public async Task Execution_WithFrenchLocale_OutputContainsTranslatedSummary(string tfm)
@@ -51,17 +23,17 @@ public async Task Execution_WithFrenchLocale_OutputContainsTranslatedSummary(str
testHostResult.AssertExitCodeIs(ExitCode.Success);
// Verify the summary line is in French ("Résumé de série de tests : Réussite!")
- AssertOutputContainsNormalized(testHostResult, "Résumé de série de tests : Réussite!");
+ testHostResult.AssertOutputContainsNormalized("Résumé de série de tests : Réussite!");
// Verify the count labels are in French
- AssertOutputContainsNormalized(testHostResult, "total: 2");
- AssertOutputContainsNormalized(testHostResult, "échec: 0");
- AssertOutputContainsNormalized(testHostResult, "opération réussie: 2");
- AssertOutputContainsNormalized(testHostResult, "ignoré: 0");
+ testHostResult.AssertOutputContainsNormalized("total: 2");
+ testHostResult.AssertOutputContainsNormalized("échec: 0");
+ testHostResult.AssertOutputContainsNormalized("opération réussie: 2");
+ testHostResult.AssertOutputContainsNormalized("ignoré: 0");
// Verify English strings are NOT in the output
- AssertOutputDoesNotContainNormalized(testHostResult, "Test run summary:");
- AssertOutputDoesNotContainNormalized(testHostResult, "succeeded:");
+ testHostResult.AssertOutputDoesNotContainNormalized("Test run summary:");
+ testHostResult.AssertOutputDoesNotContainNormalized("succeeded:");
}
[DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))]
@@ -105,8 +77,8 @@ public async Task Execution_WithTestingPlatformUILanguage_TakesPrecedenceOverDot
testHostResult.AssertExitCodeIs(ExitCode.Success);
// French should win because TESTINGPLATFORM_UI_LANGUAGE has higher precedence
- AssertOutputContainsNormalized(testHostResult, "Résumé de série de tests : Réussite!");
- AssertOutputDoesNotContainNormalized(testHostResult, "Resumen de la serie de pruebas:");
+ testHostResult.AssertOutputContainsNormalized("Résumé de série de tests : Réussite!");
+ testHostResult.AssertOutputDoesNotContainNormalized("Resumen de la serie de pruebas:");
}
public sealed class TestAssetFixture() : TestAssetFixtureBase()