Skip to content

Commit 9f028f3

Browse files
CopilotYoussef1313
andauthored
Fix MTP timeout parsing to use invariant culture instead of current culture (#5705)
Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: Youssef1313 <[email protected]> Co-authored-by: Youssef Victor <[email protected]>
1 parent 2497a40 commit 9f028f3

File tree

3 files changed

+75
-2
lines changed

3 files changed

+75
-2
lines changed

src/Platform/Microsoft.Testing.Platform/CommandLine/PlatformCommandLineProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ public Task<ValidationResult> ValidateOptionArgumentsAsync(CommandLineOption com
111111
{
112112
string arg = arguments[0];
113113
int size = arg.Length;
114-
if ((char.ToLowerInvariant(arg[size - 1]) != 'h' && char.ToLowerInvariant(arg[size - 1]) != 'm' && char.ToLowerInvariant(arg[size - 1]) != 's') || !float.TryParse(arg[..(size - 1)], out float _))
114+
if ((char.ToLowerInvariant(arg[size - 1]) != 'h' && char.ToLowerInvariant(arg[size - 1]) != 'm' && char.ToLowerInvariant(arg[size - 1]) != 's') || !float.TryParse(arg[..(size - 1)], NumberStyles.Float, CultureInfo.InvariantCulture, out float _))
115115
{
116116
return ValidationResult.InvalidTask(PlatformResources.PlatformCommandLineTimeoutArgumentErrorMessage);
117117
}

src/Platform/Microsoft.Testing.Platform/Hosts/TestHostBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ public async Task<ITestHost> BuildAsync(
255255
{
256256
string arg = args[0];
257257
int size = arg.Length;
258-
if (!float.TryParse(arg[..(size - 1)], out float value))
258+
if (!float.TryParse(arg[..(size - 1)], NumberStyles.Float, CultureInfo.InvariantCulture, out float value))
259259
{
260260
throw ApplicationStateGuard.Unreachable();
261261
}

test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/PlatformCommandLineProviderTests.cs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,4 +181,77 @@ public async Task IsNotValid_If_ExitOnProcess_Not_Running()
181181
Assert.IsFalse(validateOptionsResult.IsValid);
182182
Assert.IsTrue(validateOptionsResult.ErrorMessage.StartsWith($"Invalid PID '{pid}'", StringComparison.OrdinalIgnoreCase));
183183
}
184+
185+
[TestMethod]
186+
[DataRow("1.5s")]
187+
[DataRow("2.0m")]
188+
[DataRow("0.5h")]
189+
[DataRow("10s")]
190+
[DataRow("30m")]
191+
[DataRow("1h")]
192+
public async Task IsValid_If_Timeout_Has_CorrectFormat_InvariantCulture(string timeout)
193+
{
194+
var provider = new PlatformCommandLineProvider();
195+
CommandLineOption option = provider.GetCommandLineOptions().First(x => x.Name == PlatformCommandLineProvider.TimeoutOptionKey);
196+
197+
// Save current culture
198+
CultureInfo originalCulture = CultureInfo.CurrentCulture;
199+
try
200+
{
201+
// Test with various cultures to ensure invariant parsing works
202+
foreach (string cultureName in new[] { "en-US", "de-DE", "fr-FR" })
203+
{
204+
CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo(cultureName);
205+
ValidationResult validateOptionsResult = await provider.ValidateOptionArgumentsAsync(option, [timeout]);
206+
Assert.IsTrue(validateOptionsResult.IsValid, $"Failed with culture {cultureName} and timeout {timeout}");
207+
Assert.IsTrue(string.IsNullOrEmpty(validateOptionsResult.ErrorMessage));
208+
}
209+
}
210+
finally
211+
{
212+
// Restore original culture
213+
CultureInfo.CurrentCulture = originalCulture;
214+
}
215+
}
216+
217+
[TestMethod]
218+
[DataRow("1,5s")] // German decimal separator
219+
[DataRow("invalid")]
220+
[DataRow("1.5")] // Missing unit
221+
[DataRow("abc.5s")]
222+
public async Task IsInvalid_If_Timeout_Has_IncorrectFormat(string timeout)
223+
{
224+
var provider = new PlatformCommandLineProvider();
225+
CommandLineOption option = provider.GetCommandLineOptions().First(x => x.Name == PlatformCommandLineProvider.TimeoutOptionKey);
226+
227+
ValidationResult validateOptionsResult = await provider.ValidateOptionArgumentsAsync(option, [timeout]);
228+
Assert.IsFalse(validateOptionsResult.IsValid);
229+
Assert.AreEqual(PlatformResources.PlatformCommandLineTimeoutArgumentErrorMessage, validateOptionsResult.ErrorMessage);
230+
}
231+
232+
[TestMethod]
233+
public async Task Timeout_Parsing_Uses_InvariantCulture_NotCurrentCulture()
234+
{
235+
var provider = new PlatformCommandLineProvider();
236+
CommandLineOption option = provider.GetCommandLineOptions().First(x => x.Name == PlatformCommandLineProvider.TimeoutOptionKey);
237+
238+
// Save current culture
239+
CultureInfo originalCulture = CultureInfo.CurrentCulture;
240+
try
241+
{
242+
// Set culture to German where decimal separator is comma
243+
CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("de-DE");
244+
// This should work because we use invariant culture (period as decimal separator)
245+
ValidationResult validResult = await provider.ValidateOptionArgumentsAsync(option, ["1.5s"]);
246+
Assert.IsTrue(validResult.IsValid, "1.5s should be valid when using invariant culture");
247+
// This should fail because comma is not valid in invariant culture
248+
ValidationResult invalidResult = await provider.ValidateOptionArgumentsAsync(option, ["1,5s"]);
249+
Assert.IsFalse(invalidResult.IsValid, "1,5s should be invalid when using invariant culture");
250+
}
251+
finally
252+
{
253+
// Restore original culture
254+
CultureInfo.CurrentCulture = originalCulture;
255+
}
256+
}
184257
}

0 commit comments

Comments
 (0)