diff --git a/dotnet/test/common/BiDi/Script/CallFunctionLocalValueTest.cs b/dotnet/test/common/BiDi/Script/CallFunctionLocalValueTest.cs index 2fad98b20f9e4..3b17476ed2209 100644 --- a/dotnet/test/common/BiDi/Script/CallFunctionLocalValueTest.cs +++ b/dotnet/test/common/BiDi/Script/CallFunctionLocalValueTest.cs @@ -223,7 +223,7 @@ public async Task CanCallFunctionWithArgumentNumberZero() [Test] public async Task CanCallFunctionWithArgumentNumberNegativeZero() { - var arg = new NumberLocalValue(double.NegativeZero); + var arg = new NumberLocalValue(-0.0d); var result = await context.Script.CallFunctionAsync($$""" (arg) => { diff --git a/dotnet/test/common/BiDi/Script/CallFunctionParameterTest.cs b/dotnet/test/common/BiDi/Script/CallFunctionParameterTest.cs index 85f4f452dc2e4..4465bf870b0ed 100644 --- a/dotnet/test/common/BiDi/Script/CallFunctionParameterTest.cs +++ b/dotnet/test/common/BiDi/Script/CallFunctionParameterTest.cs @@ -18,7 +18,7 @@ // using NUnit.Framework; -using System.Runtime.CompilerServices; +using System.Reflection; using System.Threading.Tasks; namespace OpenQA.Selenium.BiDi.Script; @@ -229,7 +229,9 @@ private string GetElementId(By selector) var element = (WebElement)driver.FindElement(selector); return ReflectElementId(element); - [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_Id")] - static extern string ReflectElementId(WebElement element); + static string ReflectElementId(WebElement element) + { + return (string)typeof(WebElement).GetProperty("Id", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(element, null); + } } } diff --git a/dotnet/test/common/BiDi/Script/CallFunctionRemoteValueTest.cs b/dotnet/test/common/BiDi/Script/CallFunctionRemoteValueTest.cs index bbfb14ac1d155..fa591019bd423 100644 --- a/dotnet/test/common/BiDi/Script/CallFunctionRemoteValueTest.cs +++ b/dotnet/test/common/BiDi/Script/CallFunctionRemoteValueTest.cs @@ -18,6 +18,7 @@ // using NUnit.Framework; +using OpenQA.Selenium.Extensions; using System.Threading.Tasks; namespace OpenQA.Selenium.BiDi.Script; @@ -135,7 +136,7 @@ public async Task CanCallFunctionAndReturnNumberNegativeZero() var actualNumberValue = ((NumberRemoteValue)response.AsSuccessResult()).Value; Assert.That(actualNumberValue, Is.Zero); - Assert.That(double.IsNegative(actualNumberValue), Is.True); + Assert.That(DoubleUtility.IsNegative(actualNumberValue), Is.True); } [Test] diff --git a/dotnet/test/common/BiDi/Script/LocalValueConversionTests.cs b/dotnet/test/common/BiDi/Script/LocalValueConversionTests.cs index 492ff44684eba..9a77e3b6ac401 100644 --- a/dotnet/test/common/BiDi/Script/LocalValueConversionTests.cs +++ b/dotnet/test/common/BiDi/Script/LocalValueConversionTests.cs @@ -163,7 +163,7 @@ static void AssertValue(LocalValue value) [Test] public void CanConvertDateTimeOffsetToLocalValue() { - var date = new DateTimeOffset(2025, 4, 13, 5, 40, 20, 123, 456, TimeSpan.FromHours(+3)); + var date = new DateTimeOffset(2025, 4, 13, 5, 40, 20, 123, TimeSpan.FromHours(+3)); AssertValue(date); @@ -172,7 +172,7 @@ public void CanConvertDateTimeOffsetToLocalValue() static void AssertValue(LocalValue value) { Assert.That(value, Is.TypeOf()); - Assert.That((value as DateLocalValue).Value, Is.EqualTo("2025-04-13T05:40:20.1234560+03:00")); + Assert.That((value as DateLocalValue).Value, Is.EqualTo("2025-04-13T05:40:20.1230000+03:00")); } } diff --git a/dotnet/test/common/Environment/TestWebServer.cs b/dotnet/test/common/Environment/TestWebServer.cs index 14c47996340d9..f01176b48d72b 100644 --- a/dotnet/test/common/Environment/TestWebServer.cs +++ b/dotnet/test/common/Environment/TestWebServer.cs @@ -18,6 +18,7 @@ // using Bazel; +using OpenQA.Selenium.Utilities; using System; using System.Diagnostics; using System.IO; @@ -46,7 +47,7 @@ public async Task StartAsync() var standaloneAppserverProbingPath = @"_main/java/test/org/openqa/selenium/environment/appserver"; - if (OperatingSystem.IsWindows()) + if (PlatformUtilities.IsWindows()) { standaloneAppserverProbingPath += ".exe"; } diff --git a/dotnet/test/common/Environment/UrlBuilder.cs b/dotnet/test/common/Environment/UrlBuilder.cs index d6082a0cadcb2..a13e868303a58 100644 --- a/dotnet/test/common/Environment/UrlBuilder.cs +++ b/dotnet/test/common/Environment/UrlBuilder.cs @@ -130,9 +130,10 @@ public string CreateInlinePage(InlinePage page) // The response string from the Java remote server has trailing null // characters. This is due to the fix for issue 288. - if (responseString.Contains('\0')) + int terminator = responseString.IndexOf('\0'); + if (terminator >= 0) { - responseString = responseString[..responseString.IndexOf('\0')]; + responseString = responseString.Substring(0, terminator); } if (responseString.Contains("localhost")) diff --git a/dotnet/test/common/Utilities/ArrayExtensions.cs b/dotnet/test/common/Utilities/ArrayExtensions.cs new file mode 100644 index 0000000000000..c5e3faa55220a --- /dev/null +++ b/dotnet/test/common/Utilities/ArrayExtensions.cs @@ -0,0 +1,31 @@ +// +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +using System.Collections.Generic; +using System.Linq; + +namespace System; + +internal static class ArrayExtensions +{ + public static IEnumerable Reverse(this T[] array) + { + return Enumerable.Reverse(array); + } +} diff --git a/dotnet/test/common/Utilities/DoubleUtility.cs b/dotnet/test/common/Utilities/DoubleUtility.cs new file mode 100644 index 0000000000000..247fcca983bf7 --- /dev/null +++ b/dotnet/test/common/Utilities/DoubleUtility.cs @@ -0,0 +1,34 @@ +// +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +using System; + +namespace OpenQA.Selenium.Extensions; + +internal static class DoubleUtility +{ + public static bool IsNegative(double d) + { + var isNegative = BitConverter.DoubleToInt64Bits(d) < 0; +#if NET8_0_OR_GREATER + System.Diagnostics.Debug.Assert(isNegative == double.IsNegative(d)); +#endif + return isNegative; + } +} diff --git a/dotnet/test/common/Utilities/PlatformUtilities.cs b/dotnet/test/common/Utilities/PlatformUtilities.cs new file mode 100644 index 0000000000000..d38752c558763 --- /dev/null +++ b/dotnet/test/common/Utilities/PlatformUtilities.cs @@ -0,0 +1,32 @@ +// +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +namespace OpenQA.Selenium.Utilities; + +internal static class PlatformUtilities +{ + public static bool IsWindows() + { +#if NET8_0_OR_GREATER + return System.OperatingSystem.IsWindows(); +#else + return System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows); +#endif + } +} diff --git a/dotnet/test/common/Utilities/ProcessExtensions.cs b/dotnet/test/common/Utilities/ProcessExtensions.cs new file mode 100644 index 0000000000000..85298d6220e1a --- /dev/null +++ b/dotnet/test/common/Utilities/ProcessExtensions.cs @@ -0,0 +1,37 @@ +// +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +using System.Diagnostics; + +namespace System; + +internal static class ProcessExtensions +{ + public static void Kill(this Process process, bool entireProcessTree) + { + if (!entireProcessTree) + { + process.Kill(); + return; + } + + // TODO kill all child processes + process.Kill(); + } +} diff --git a/dotnet/test/common/Utilities/TaskExtensions.cs b/dotnet/test/common/Utilities/TaskExtensions.cs new file mode 100644 index 0000000000000..ce7b3c6cde683 --- /dev/null +++ b/dotnet/test/common/Utilities/TaskExtensions.cs @@ -0,0 +1,40 @@ +// +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +#if !NET8_0_OR_GREATER + +using System.Threading.Tasks; + +namespace System; + +internal static class TaskExtensions +{ + public static async Task WaitAsync(this Task task, TimeSpan timeout) + { + var timeoutTask = Task.Delay(timeout); + var completedTask = await Task.WhenAny(task, timeoutTask); + if (completedTask == timeoutTask) + { + throw new TimeoutException(); + } + return await task; + } +} + +#endif