From 2e8816febd17233d54c1773e09d427b88c859a24 Mon Sep 17 00:00:00 2001 From: Selenium CI Bot Date: Wed, 27 Nov 2024 12:08:30 +0000 Subject: [PATCH 01/17] Update mirror info (Wed Nov 27 12:08:30 UTC 2024) --- common/mirror/selenium | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/common/mirror/selenium b/common/mirror/selenium index 829332bd46f9c..3f7b7af0c7a31 100644 --- a/common/mirror/selenium +++ b/common/mirror/selenium @@ -1,35 +1,35 @@ [ { - "tag_name": "selenium-4.27.0", + "tag_name": "nightly", "assets": [ { - "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.27.0/selenium-dotnet-4.27.0.zip" - }, - { - "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.27.0/selenium-dotnet-strongnamed-4.27.0.zip" - }, - { - "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.27.0/selenium-java-4.27.0.zip" + "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/nightly/selenium-java-4.28.0-SNAPSHOT.zip" }, { - "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.27.0/selenium-server-4.27.0.jar" + "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/nightly/selenium-server-4.28.0-SNAPSHOT.jar" }, { - "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.27.0/selenium-server-4.27.0.zip" + "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/nightly/selenium-server-4.28.0-SNAPSHOT.zip" } ] }, { - "tag_name": "nightly", + "tag_name": "selenium-4.27.0", "assets": [ { - "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/nightly/selenium-java-4.28.0-SNAPSHOT.zip" + "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.27.0/selenium-dotnet-4.27.0.zip" }, { - "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/nightly/selenium-server-4.28.0-SNAPSHOT.jar" + "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.27.0/selenium-dotnet-strongnamed-4.27.0.zip" }, { - "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/nightly/selenium-server-4.28.0-SNAPSHOT.zip" + "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.27.0/selenium-java-4.27.0.zip" + }, + { + "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.27.0/selenium-server-4.27.0.jar" + }, + { + "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.27.0/selenium-server-4.27.0.zip" } ] }, From 59aa1a0b5128fcd5e12fe918fd109d443e9f5586 Mon Sep 17 00:00:00 2001 From: Navin Chandra <98466550+navin772@users.noreply.github.com> Date: Wed, 27 Nov 2024 18:05:19 +0530 Subject: [PATCH 02/17] [java]: encapsulate `additionalCommands` with getter method (#14816) * change scope of `additionalCommands` to `protected` * expose getter `getAdditionalCommands` * add unit test for `additionalCommands` --------- Co-authored-by: Diego Molina --- .../selenium/remote/HttpCommandExecutor.java | 27 ++++++++++++++++++- .../RemoteWebDriverInitializationTest.java | 19 +++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/java/src/org/openqa/selenium/remote/HttpCommandExecutor.java b/java/src/org/openqa/selenium/remote/HttpCommandExecutor.java index 03fc84bc45b19..1eb3283eae014 100644 --- a/java/src/org/openqa/selenium/remote/HttpCommandExecutor.java +++ b/java/src/org/openqa/selenium/remote/HttpCommandExecutor.java @@ -25,6 +25,8 @@ import java.io.IOException; import java.net.URL; +import java.util.Collections; +import java.util.HashMap; import java.util.Map; import org.openqa.selenium.NoSuchSessionException; import org.openqa.selenium.SessionNotCreatedException; @@ -109,11 +111,34 @@ public HttpCommandExecutor( ClientConfig config, HttpClient.Factory httpClientFactory) { remoteServer = Require.nonNull("HTTP client configuration", config).baseUrl(); - this.additionalCommands = Require.nonNull("Additional commands", additionalCommands); + this.additionalCommands = + new HashMap<>(Require.nonNull("Additional commands", additionalCommands)); this.httpClientFactory = Require.nonNull("HTTP client factory", httpClientFactory); this.client = this.httpClientFactory.createClient(config); } + /** + * Returns an immutable view of the additional commands. + * + * @return an unmodifiable map of additional commands. + */ + public Map getAdditionalCommands() { + return Collections.unmodifiableMap(additionalCommands); + } + + /** + * Adds or updates additional commands. This method is protected to allow subclasses to define + * their commands. + * + * @param commandName the name of the command to add or update. + * @param info the CommandInfo for the command. + */ + protected void addAdditionalCommand(String commandName, CommandInfo info) { + Require.nonNull("Command name", commandName); + Require.nonNull("Command info", info); + this.additionalCommands.put(commandName, info); + } + /** * It may be useful to extend the commands understood by this {@code HttpCommandExecutor} at run * time, and this can be achieved via this method. Note, this is protected, and expected usage is diff --git a/java/test/org/openqa/selenium/remote/RemoteWebDriverInitializationTest.java b/java/test/org/openqa/selenium/remote/RemoteWebDriverInitializationTest.java index 111cac68e5085..8c01983488ed3 100644 --- a/java/test/org/openqa/selenium/remote/RemoteWebDriverInitializationTest.java +++ b/java/test/org/openqa/selenium/remote/RemoteWebDriverInitializationTest.java @@ -51,6 +51,7 @@ import org.openqa.selenium.remote.http.ClientConfig; import org.openqa.selenium.remote.http.Contents; import org.openqa.selenium.remote.http.HttpClient; +import org.openqa.selenium.remote.http.HttpMethod; import org.openqa.selenium.remote.http.HttpResponse; @Tag("UnitTests") @@ -234,4 +235,22 @@ public void quit() { quitCalled = true; } } + + @Test + void additionalCommandsCanBeModified() throws MalformedURLException { + HttpClient client = mock(HttpClient.class); + HttpClient.Factory factory = mock(HttpClient.Factory.class); + + when(factory.createClient(any(ClientConfig.class))).thenReturn(client); + + URL url = new URL("http://localhost:4444/"); + HttpCommandExecutor executor = + new HttpCommandExecutor(emptyMap(), ClientConfig.defaultConfig().baseUrl(url), factory); + + String commandName = "customCommand"; + CommandInfo commandInfo = new CommandInfo("/session/:sessionId/custom", HttpMethod.GET); + executor.addAdditionalCommand(commandName, commandInfo); + + assertThat(executor.getAdditionalCommands()).containsEntry(commandName, commandInfo); + } } From 8594d5e61fa3e46661a62c7f4f88daf55567f429 Mon Sep 17 00:00:00 2001 From: Diego Molina Date: Wed, 27 Nov 2024 15:16:21 +0100 Subject: [PATCH 03/17] [java] Ignoring no shadow root test due to https://issues.chromium.org/issues/375892677 Related to #14631 --- java/test/org/openqa/selenium/NoSuchShadowRootTest.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/java/test/org/openqa/selenium/NoSuchShadowRootTest.java b/java/test/org/openqa/selenium/NoSuchShadowRootTest.java index 4fe1be1ecec08..f945aa6d4561e 100644 --- a/java/test/org/openqa/selenium/NoSuchShadowRootTest.java +++ b/java/test/org/openqa/selenium/NoSuchShadowRootTest.java @@ -18,13 +18,18 @@ package org.openqa.selenium; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.openqa.selenium.testing.drivers.Browser.CHROME; +import static org.openqa.selenium.testing.drivers.Browser.EDGE; import org.junit.jupiter.api.Test; +import org.openqa.selenium.testing.Ignore; import org.openqa.selenium.testing.JupiterTestBase; public class NoSuchShadowRootTest extends JupiterTestBase { @Test + @Ignore(value = CHROME, reason = "https://issues.chromium.org/issues/375892677") + @Ignore(value = EDGE, reason = "https://issues.chromium.org/issues/375892677") public void getNoSuchShadowRoot() { driver.get(pages.shadowRootPage); WebElement nonExistentShadowRootElement = driver.findElement(By.id("noShadowRoot")); From e71f0dbef35cb1efdea6057e3e7361277a1da56d Mon Sep 17 00:00:00 2001 From: Diego Molina Date: Wed, 27 Nov 2024 15:16:34 +0100 Subject: [PATCH 04/17] [dotnet] Ignoring no shadow root test due to https://issues.chromium.org/issues/375892677 Related to #14631 --- dotnet/test/common/ShadowRootHandlingTest.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dotnet/test/common/ShadowRootHandlingTest.cs b/dotnet/test/common/ShadowRootHandlingTest.cs index 2d014aa865e95..7fa25654dd10f 100644 --- a/dotnet/test/common/ShadowRootHandlingTest.cs +++ b/dotnet/test/common/ShadowRootHandlingTest.cs @@ -47,6 +47,8 @@ public void ShouldFindElementUnderShadowRoot() } [Test] + [IgnoreBrowser(Browser.Chrome, "https://issues.chromium.org/issues/375892677")] + [IgnoreBrowser(Browser.Edge, "https://issues.chromium.org/issues/375892677")] public void ShouldThrowGettingShadowRootWithElementNotHavingShadowRoot() { driver.Url = shadowRootPage; From 4fe2a56aab3a977dc2bbbc4ea8200bb0841ac99f Mon Sep 17 00:00:00 2001 From: Michael Render Date: Wed, 27 Nov 2024 14:00:07 -0500 Subject: [PATCH 05/17] [dotnet] Add nullability annotations to `ShadowRoot` (#14812) --- dotnet/src/webdriver/ISearchContext.cs | 2 + dotnet/src/webdriver/IWrapsDriver.cs | 2 + .../Internal/IWebDriverObjectReference.cs | 2 + dotnet/src/webdriver/ShadowRoot.cs | 57 ++++++++++--------- dotnet/src/webdriver/WebDriver.cs | 4 +- 5 files changed, 39 insertions(+), 28 deletions(-) diff --git a/dotnet/src/webdriver/ISearchContext.cs b/dotnet/src/webdriver/ISearchContext.cs index 53c5f33ae36da..a8e244ffdddee 100644 --- a/dotnet/src/webdriver/ISearchContext.cs +++ b/dotnet/src/webdriver/ISearchContext.cs @@ -20,6 +20,8 @@ using System; using System.Collections.ObjectModel; +#nullable enable + namespace OpenQA.Selenium { /// diff --git a/dotnet/src/webdriver/IWrapsDriver.cs b/dotnet/src/webdriver/IWrapsDriver.cs index 27af4c7e56faa..15fe5afa2bcf3 100644 --- a/dotnet/src/webdriver/IWrapsDriver.cs +++ b/dotnet/src/webdriver/IWrapsDriver.cs @@ -17,6 +17,8 @@ // under the License. // +#nullable enable + namespace OpenQA.Selenium { /// diff --git a/dotnet/src/webdriver/Internal/IWebDriverObjectReference.cs b/dotnet/src/webdriver/Internal/IWebDriverObjectReference.cs index bc67fdd7c6a6b..a62286ca57676 100644 --- a/dotnet/src/webdriver/Internal/IWebDriverObjectReference.cs +++ b/dotnet/src/webdriver/Internal/IWebDriverObjectReference.cs @@ -19,6 +19,8 @@ using System.Collections.Generic; +#nullable enable + namespace OpenQA.Selenium.Internal { /// diff --git a/dotnet/src/webdriver/ShadowRoot.cs b/dotnet/src/webdriver/ShadowRoot.cs index 459674b27c1d5..a872d4fbc7d74 100644 --- a/dotnet/src/webdriver/ShadowRoot.cs +++ b/dotnet/src/webdriver/ShadowRoot.cs @@ -21,6 +21,9 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; + +#nullable enable namespace OpenQA.Selenium { @@ -34,49 +37,46 @@ public class ShadowRoot : ISearchContext, IWrapsDriver, IWebDriverObjectReferenc /// public const string ShadowRootReferencePropertyName = "shadow-6066-11e4-a52e-4f735466cecf"; - private WebDriver driver; - private string shadowRootId; + private readonly WebDriver driver; + private readonly string shadowRootId; /// /// Initializes a new instance of the class. /// /// The instance that is driving this shadow root. /// The ID value provided to identify the shadow root. + /// If or are . public ShadowRoot(WebDriver parentDriver, string id) { - this.driver = parentDriver; - this.shadowRootId = id; + this.driver = parentDriver ?? throw new ArgumentNullException(nameof(parentDriver)); + this.shadowRootId = id ?? throw new ArgumentNullException(nameof(id)); } /// /// Gets the driving this shadow root. /// - public IWebDriver WrappedDriver - { - get { return this.driver; } - } + public IWebDriver WrappedDriver => this.driver; /// /// Gets the internal ID for this ShadowRoot. /// - string IWebDriverObjectReference.ObjectReferenceId - { - get { return this.shadowRootId; } - } + string IWebDriverObjectReference.ObjectReferenceId => this.shadowRootId; - internal static bool ContainsShadowRootReference(Dictionary shadowRootDictionary) + internal static bool TryCreate(WebDriver parentDriver, Dictionary shadowRootDictionary, [NotNullWhen(true)] out ShadowRoot? shadowRoot) { - if (shadowRootDictionary == null) + if (shadowRootDictionary is null) { throw new ArgumentNullException(nameof(shadowRootDictionary), "The dictionary containing the shadow root reference cannot be null"); } - return shadowRootDictionary.ContainsKey(ShadowRootReferencePropertyName); - } + if (shadowRootDictionary.TryGetValue(ShadowRootReferencePropertyName, out object? shadowRootValue)) + { + shadowRoot = new ShadowRoot(parentDriver, shadowRootValue?.ToString()!); + return true; + } - internal static ShadowRoot FromDictionary(WebDriver driver, Dictionary shadowRootDictionary) - { - return new ShadowRoot(driver, shadowRootDictionary[ShadowRoot.ShadowRootReferencePropertyName].ToString()); + shadowRoot = null; + return false; } /// @@ -84,18 +84,20 @@ internal static ShadowRoot FromDictionary(WebDriver driver, Dictionary /// The locating mechanism to use. /// The first matching on the current context. + /// If is . /// If no element matches the criteria. public IWebElement FindElement(By by) { - if (by == null) + if (by is null) { - throw new ArgumentNullException(nameof(@by), "by cannot be null"); + throw new ArgumentNullException(nameof(by), "by cannot be null"); } Dictionary parameters = new Dictionary(); parameters.Add("id", this.shadowRootId); parameters.Add("using", by.Mechanism); parameters.Add("value", by.Criteria); + Response commandResponse = this.driver.InternalExecute(DriverCommand.FindShadowChildElement, parameters); return this.driver.GetElementFromResponse(commandResponse); } @@ -107,26 +109,29 @@ public IWebElement FindElement(By by) /// The locating mechanism to use. /// A of all WebElements /// matching the current criteria, or an empty list if nothing matches. + /// If is . public ReadOnlyCollection FindElements(By by) { - if (by == null) + if (by is null) { - throw new ArgumentNullException(nameof(@by), "by cannot be null"); + throw new ArgumentNullException(nameof(by), "by cannot be null"); } Dictionary parameters = new Dictionary(); parameters.Add("id", this.shadowRootId); parameters.Add("using", by.Mechanism); parameters.Add("value", by.Criteria); + Response commandResponse = this.driver.InternalExecute(DriverCommand.FindShadowChildElements, parameters); return this.driver.GetElementsFromResponse(commandResponse); } Dictionary IWebDriverObjectReference.ToDictionary() { - Dictionary shadowRootDictionary = new Dictionary(); - shadowRootDictionary.Add(ShadowRootReferencePropertyName, this.shadowRootId); - return shadowRootDictionary; + return new Dictionary + { + [ShadowRootReferencePropertyName] = this.shadowRootId + }; } } } diff --git a/dotnet/src/webdriver/WebDriver.cs b/dotnet/src/webdriver/WebDriver.cs index 235b2e648058e..0720811caba33 100644 --- a/dotnet/src/webdriver/WebDriver.cs +++ b/dotnet/src/webdriver/WebDriver.cs @@ -980,9 +980,9 @@ private object ParseJavaScriptReturnValue(object responseValue) { returnValue = this.elementFactory.CreateElement(resultAsDictionary); } - else if (ShadowRoot.ContainsShadowRootReference(resultAsDictionary)) + else if (ShadowRoot.TryCreate(this, resultAsDictionary, out ShadowRoot shadowRoot)) { - returnValue = ShadowRoot.FromDictionary(this, resultAsDictionary); + returnValue = shadowRoot; } else { From cfaa8c45cf785e716c521bf6d14d9bad2bfa7d79 Mon Sep 17 00:00:00 2001 From: Michael Render Date: Wed, 27 Nov 2024 14:25:47 -0500 Subject: [PATCH 06/17] [dotnet] Fix `WebDriver.AuthenticatorId` to return proper state set by user (#14814) --- dotnet/src/webdriver/WebDriver.cs | 21 +++++++++---------- .../VirtualAuthn/VirtualAuthenticatorTest.cs | 5 ++++- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/dotnet/src/webdriver/WebDriver.cs b/dotnet/src/webdriver/WebDriver.cs index 0720811caba33..d9960ea97b235 100644 --- a/dotnet/src/webdriver/WebDriver.cs +++ b/dotnet/src/webdriver/WebDriver.cs @@ -45,7 +45,6 @@ public class WebDriver : IWebDriver, ISearchContext, IJavaScriptExecutor, IFinds private NetworkManager network; private WebElementFactory elementFactory; private SessionId sessionId; - private String authenticatorId; private List registeredCommands = new List(); /// @@ -1046,8 +1045,8 @@ public string AddVirtualAuthenticator(VirtualAuthenticatorOptions options) { Response commandResponse = this.Execute(DriverCommand.AddVirtualAuthenticator, options.ToDictionary()); string id = commandResponse.Value.ToString(); - this.authenticatorId = id; - return this.authenticatorId; + this.AuthenticatorId = id; + return this.AuthenticatorId; } /// @@ -1057,15 +1056,15 @@ public string AddVirtualAuthenticator(VirtualAuthenticatorOptions options) public void RemoveVirtualAuthenticator(string authenticatorId) { Dictionary parameters = new Dictionary(); - parameters.Add("authenticatorId", this.authenticatorId); + parameters.Add("authenticatorId", this.AuthenticatorId); this.Execute(DriverCommand.RemoveVirtualAuthenticator, parameters); - this.authenticatorId = null; + this.AuthenticatorId = null; } /// /// Gets the virtual authenticator ID for this WebDriver instance. /// - public string AuthenticatorId { get; } + public string AuthenticatorId { get; private set; } /// /// Add a credential to the Virtual Authenticator/ @@ -1074,7 +1073,7 @@ public void RemoveVirtualAuthenticator(string authenticatorId) public void AddCredential(Credential credential) { Dictionary parameters = new Dictionary(credential.ToDictionary()); - parameters.Add("authenticatorId", this.authenticatorId); + parameters.Add("authenticatorId", this.AuthenticatorId); this.Execute(driverCommandToExecute: DriverCommand.AddCredential, parameters); } @@ -1086,7 +1085,7 @@ public void AddCredential(Credential credential) public List GetCredentials() { Dictionary parameters = new Dictionary(); - parameters.Add("authenticatorId", this.authenticatorId); + parameters.Add("authenticatorId", this.AuthenticatorId); object[] commandResponse = (object[])this.Execute(driverCommandToExecute: DriverCommand.GetCredentials, parameters).Value; @@ -1117,7 +1116,7 @@ public void RemoveCredential(byte[] credentialId) public void RemoveCredential(string credentialId) { Dictionary parameters = new Dictionary(); - parameters.Add("authenticatorId", this.authenticatorId); + parameters.Add("authenticatorId", this.AuthenticatorId); parameters.Add("credentialId", credentialId); this.Execute(driverCommandToExecute: DriverCommand.RemoveCredential, parameters); @@ -1129,7 +1128,7 @@ public void RemoveCredential(string credentialId) public void RemoveAllCredentials() { Dictionary parameters = new Dictionary(); - parameters.Add("authenticatorId", this.authenticatorId); + parameters.Add("authenticatorId", this.AuthenticatorId); this.Execute(driverCommandToExecute: DriverCommand.RemoveAllCredentials, parameters); } @@ -1141,7 +1140,7 @@ public void RemoveAllCredentials() public void SetUserVerified(bool verified) { Dictionary parameters = new Dictionary(); - parameters.Add("authenticatorId", this.authenticatorId); + parameters.Add("authenticatorId", this.AuthenticatorId); parameters.Add("isUserVerified", verified); this.Execute(driverCommandToExecute: DriverCommand.SetUserVerified, parameters); diff --git a/dotnet/test/common/VirtualAuthn/VirtualAuthenticatorTest.cs b/dotnet/test/common/VirtualAuthn/VirtualAuthenticatorTest.cs index a5a8d53d73da4..e6c16e11b673a 100644 --- a/dotnet/test/common/VirtualAuthn/VirtualAuthenticatorTest.cs +++ b/dotnet/test/common/VirtualAuthn/VirtualAuthenticatorTest.cs @@ -71,7 +71,8 @@ public void Setup() [TearDown] public void Teardown() { - if (webDriver.AuthenticatorId != null) + if (webDriver.AuthenticatorId is not null && + webDriver.SessionId is not null) { webDriver.RemoveVirtualAuthenticator(webDriver.AuthenticatorId); } @@ -186,6 +187,8 @@ public void ShouldRemoveAuthenticator() { VirtualAuthenticatorOptions options = new VirtualAuthenticatorOptions(); string authenticatorId = webDriver.AddVirtualAuthenticator(options); + Assert.That(webDriver.AuthenticatorId, Is.EqualTo(authenticatorId)); + webDriver.RemoveVirtualAuthenticator(authenticatorId); Assert.IsNull(webDriver.AuthenticatorId); From 6dc99f5178482fb97e8df0e212d8013cc37f9b35 Mon Sep 17 00:00:00 2001 From: Michael Render Date: Wed, 27 Nov 2024 17:20:04 -0500 Subject: [PATCH 07/17] [dotnet] Add future-proofing note on `Base64UrlEncoder` (#14821) --- dotnet/src/webdriver/Internal/Base64UrlEncoder.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/dotnet/src/webdriver/Internal/Base64UrlEncoder.cs b/dotnet/src/webdriver/Internal/Base64UrlEncoder.cs index 23c2fc5fae5f9..87fb7085e86c5 100644 --- a/dotnet/src/webdriver/Internal/Base64UrlEncoder.cs +++ b/dotnet/src/webdriver/Internal/Base64UrlEncoder.cs @@ -19,8 +19,17 @@ using System; +#nullable enable + namespace OpenQA.Selenium.Internal { + /* + * Based on: https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/6.19.0/src/Microsoft.IdentityModel.Tokens/Base64UrlEncoder.cs + * + * Now it is a part of .NET 9+ as System.Buffers.Text.Base64Url + * https://github.com/SeleniumHQ/selenium/issues/14813 + */ + /// /// Encodes and Decodes strings as Base64Url encoding. /// @@ -45,8 +54,6 @@ public static class Base64UrlEncoder base64UrlCharacter63 }; - // https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/6.19.0/src/Microsoft.IdentityModel.Tokens/Base64UrlEncoder.cs#L85 - /// /// Converts a subset of an array of 8-bit unsigned integers to its equivalent string representation which is encoded with base-64-url digits. /// @@ -115,8 +122,6 @@ public static string Encode(byte[] inArray) return new string(output, 0, j); } - // https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/6.19.0/src/Microsoft.IdentityModel.Tokens/Base64UrlEncoder.cs#L179 - /// /// Converts the specified string, which encodes binary data as base-64-url digits, to an equivalent 8-bit unsigned integer array. /// base64Url encoded string. From a96f6aad1ad91008f5fae1d948cb9310a710e902 Mon Sep 17 00:00:00 2001 From: mk868 Date: Thu, 28 Nov 2024 12:03:37 +0100 Subject: [PATCH 08/17] [java] SpotBugs exclude `NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE` from the `firefox.AddHasExtensions$1` (#14766) Co-authored-by: Puja Jagani --- java/spotbugs-excludes.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/java/spotbugs-excludes.xml b/java/spotbugs-excludes.xml index d62598f910a60..2ae98dc7e6050 100644 --- a/java/spotbugs-excludes.xml +++ b/java/spotbugs-excludes.xml @@ -54,6 +54,11 @@ + + + + + From 17a2c339ea4bb8ab2d57f7e60633974ed07aaa94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Sautter?= Date: Thu, 28 Nov 2024 18:03:04 +0100 Subject: [PATCH 09/17] [java] read complete output of selenium manager #14820 --- .../selenium/manager/SeleniumManager.java | 5 ++- .../openqa/selenium/os/ExternalProcess.java | 38 +++++++++++++------ 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/java/src/org/openqa/selenium/manager/SeleniumManager.java b/java/src/org/openqa/selenium/manager/SeleniumManager.java index b907d8ab755ec..97e23c85f4cb6 100644 --- a/java/src/org/openqa/selenium/manager/SeleniumManager.java +++ b/java/src/org/openqa/selenium/manager/SeleniumManager.java @@ -123,7 +123,10 @@ private static Result runCommand(Path binary, List arguments) { String output; int code; try { - ExternalProcess.Builder processBuilder = ExternalProcess.builder(); + ExternalProcess.Builder processBuilder = + ExternalProcess.builder() + // keep all output of the process to avoid JSON syntax errors while parsing + .bufferSize(-1); Properties properties = System.getProperties(); for (String name : properties.stringPropertyNames()) { diff --git a/java/src/org/openqa/selenium/os/ExternalProcess.java b/java/src/org/openqa/selenium/os/ExternalProcess.java index 2bbb7ceb01b9c..e4ae0ec9c46fa 100644 --- a/java/src/org/openqa/selenium/os/ExternalProcess.java +++ b/java/src/org/openqa/selenium/os/ExternalProcess.java @@ -19,6 +19,7 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -159,8 +160,8 @@ public Builder directory(File directory) { } /** - * Where to copy the combined stdout and stderr output to, {@code OsProcess#getOutput} is still - * working when called. + * Where to copy the combined stdout and stderr output to, {@code ExternalProcess#getOutput} is + * still working when called. * * @param stream where to copy the combined output to * @return this instance to continue building @@ -172,9 +173,9 @@ public Builder copyOutputTo(OutputStream stream) { } /** - * The number of bytes to buffer for {@code OsProcess#getOutput} calls. + * The number of bytes to buffer for {@code ExternalProcess#getOutput} calls. * - * @param toKeep the number of bytes, default is 4096 + * @param toKeep the number of bytes, default is 32768 * @return this instance to continue building */ public Builder bufferSize(int toKeep) { @@ -195,13 +196,19 @@ public ExternalProcess start() throws UncheckedIOException { } try { - CircularOutputStream circular = new CircularOutputStream(bufferSize); + OutputStream buffer; + + if (bufferSize != -1) { + buffer = new CircularOutputStream(bufferSize); + } else { + buffer = new ByteArrayOutputStream(); + } Thread worker = new Thread( () -> { // copyOutputTo might be system.out or system.err, do not to close - OutputStream output = new MultiOutputStream(circular, copyOutputTo); + OutputStream output = new MultiOutputStream(buffer, copyOutputTo); // closing the InputStream does somehow disturb the process, do not to close InputStream input = process.getInputStream(); // use the CircularOutputStream as mandatory, we know it will never raise a @@ -221,7 +228,7 @@ public ExternalProcess start() throws UncheckedIOException { worker.setDaemon(true); worker.start(); - return new ExternalProcess(process, circular, worker); + return new ExternalProcess(process, buffer, worker); } catch (Throwable t) { // ensure we do not leak a process in case of failures try { @@ -239,10 +246,10 @@ public static Builder builder() { } private final Process process; - private final CircularOutputStream outputStream; + private final OutputStream outputStream; private final Thread worker; - public ExternalProcess(Process process, CircularOutputStream outputStream, Thread worker) { + public ExternalProcess(Process process, OutputStream outputStream, Thread worker) { this.process = process; this.outputStream = outputStream; this.worker = worker; @@ -250,7 +257,7 @@ public ExternalProcess(Process process, CircularOutputStream outputStream, Threa /** * The last N bytes of the combined stdout and stderr as String, the value of N is set while - * building the OsProcess. + * building the ExternalProcess. * * @return stdout and stderr as String in Charset.defaultCharset() encoding */ @@ -260,13 +267,20 @@ public String getOutput() { /** * The last N bytes of the combined stdout and stderr as String, the value of N is set while - * building the OsProcess. + * building the ExternalProcess. * * @param encoding the encoding to decode the stream * @return stdout and stderr as String in the given encoding */ public String getOutput(Charset encoding) { - return outputStream.toString(encoding); + if (outputStream instanceof CircularOutputStream) { + return ((CircularOutputStream) outputStream).toString(encoding); + } else if (outputStream instanceof ByteArrayOutputStream) { + return ((ByteArrayOutputStream) outputStream).toString(encoding); + } else { + throw new IllegalStateException( + "unexpected OutputStream implementation: " + outputStream.getClass().getSimpleName()); + } } public boolean isAlive() { From 5c49a66f048d625703491879f7cf0c237ab51f6b Mon Sep 17 00:00:00 2001 From: Benoit Pierre Date: Fri, 29 Nov 2024 11:11:04 +0100 Subject: [PATCH 10/17] [py] fix packaging (#14823) Ensure `selenium.webdriver.common.fedcm` gets packaged in. --- py/selenium/webdriver/common/fedcm/__init__.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 py/selenium/webdriver/common/fedcm/__init__.py diff --git a/py/selenium/webdriver/common/fedcm/__init__.py b/py/selenium/webdriver/common/fedcm/__init__.py new file mode 100644 index 0000000000000..a5b1e6f85a09e --- /dev/null +++ b/py/selenium/webdriver/common/fedcm/__init__.py @@ -0,0 +1,16 @@ +# 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. From 5e3d04f6ffcc025de8302a72a4c1532dc9194ad1 Mon Sep 17 00:00:00 2001 From: Simon Mavi Stewart Date: Fri, 29 Nov 2024 15:02:26 +0200 Subject: [PATCH 11/17] Run update_copyright script. No logical changes --- dotnet/src/webdriver/Internal/Base64UrlEncoder.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dotnet/src/webdriver/Internal/Base64UrlEncoder.cs b/dotnet/src/webdriver/Internal/Base64UrlEncoder.cs index 87fb7085e86c5..c8b22e7223e8b 100644 --- a/dotnet/src/webdriver/Internal/Base64UrlEncoder.cs +++ b/dotnet/src/webdriver/Internal/Base64UrlEncoder.cs @@ -25,9 +25,9 @@ namespace OpenQA.Selenium.Internal { /* * Based on: https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/6.19.0/src/Microsoft.IdentityModel.Tokens/Base64UrlEncoder.cs - * + * * Now it is a part of .NET 9+ as System.Buffers.Text.Base64Url - * https://github.com/SeleniumHQ/selenium/issues/14813 + * https://github.com/SeleniumHQ/selenium/issues/14813 */ /// From ff7fa5266013f5b2a53278390fc80f12487d7682 Mon Sep 17 00:00:00 2001 From: Simon Stewart Date: Fri, 29 Nov 2024 17:29:48 +0200 Subject: [PATCH 12/17] [bazel] Bump `rules_jvm_external` to 6.6 and use the maven resolver (#14829) By using the maven resolver, we can make use of BOMs, which mean less duplication of version numbers within the `install` tag. --- MODULE.bazel | 56 ++++++++++++++++------------- java/maven_install.json | 80 ++++++++++++++++++----------------------- 2 files changed, 65 insertions(+), 71 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index 9a98a3348bb0d..ffb74a8cfbaf4 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -19,7 +19,7 @@ bazel_dep(name = "rules_cc", version = "0.0.9", dev_dependency = True) bazel_dep(name = "rules_dotnet", version = "0.16.1") bazel_dep(name = "rules_java", version = "7.11.1") -bazel_dep(name = "rules_jvm_external", version = "6.3") +bazel_dep(name = "rules_jvm_external", version = "6.6") bazel_dep(name = "rules_nodejs", version = "6.3.0") bazel_dep(name = "rules_oci", version = "1.7.6") bazel_dep(name = "rules_pkg", version = "0.10.1") @@ -182,23 +182,23 @@ maven.install( "dev.failsafe:failsafe:3.3.2", "io.grpc:grpc-context:1.68.1", "io.lettuce:lettuce-core:6.5.0.RELEASE", - "io.netty:netty-buffer:4.1.115.Final", - "io.netty:netty-codec-http:4.1.115.Final", - "io.netty:netty-codec-http2:4.1.115.Final", - "io.netty:netty-common:4.1.115.Final", - "io.netty:netty-handler:4.1.115.Final", - "io.netty:netty-handler-proxy:4.1.115.Final", - "io.netty:netty-transport:4.1.115.Final", - "io.opentelemetry:opentelemetry-api:1.44.1", - "io.opentelemetry:opentelemetry-context:1.44.1", - "io.opentelemetry:opentelemetry-exporter-logging:1.44.1", - "io.opentelemetry:opentelemetry-sdk:1.44.1", - "io.opentelemetry:opentelemetry-sdk-common:1.44.1", - "io.opentelemetry:opentelemetry-sdk-extension-autoconfigure:1.44.1", - "io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:1.44.1", - "io.opentelemetry:opentelemetry-sdk-testing:1.44.1", - "io.opentelemetry:opentelemetry-sdk-trace:1.44.1", - "io.opentelemetry.semconv:opentelemetry-semconv:1.25.0-alpha", + "io.netty:netty-buffer", + "io.netty:netty-codec-http", + "io.netty:netty-codec-http2", + "io.netty:netty-common", + "io.netty:netty-handler", + "io.netty:netty-handler-proxy", + "io.netty:netty-transport", + "io.opentelemetry:opentelemetry-api", + "io.opentelemetry:opentelemetry-context", + "io.opentelemetry:opentelemetry-exporter-logging", + "io.opentelemetry:opentelemetry-sdk", + "io.opentelemetry:opentelemetry-sdk-common", + "io.opentelemetry:opentelemetry-sdk-extension-autoconfigure", + "io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi", + "io.opentelemetry:opentelemetry-sdk-testing", + "io.opentelemetry:opentelemetry-sdk-trace", + "io.opentelemetry.semconv:opentelemetry-semconv:1.28.0-alpha", "it.ozimov:embedded-redis:0.7.3", "net.bytebuddy:byte-buddy:1.15.10", "org.htmlunit:htmlunit-core-js:4.6.0", @@ -209,13 +209,13 @@ maven.install( "org.eclipse.mylyn.github:org.eclipse.egit.github.core:2.1.5", "org.hsqldb:hsqldb:2.7.4", "org.jspecify:jspecify:1.0.0", - "org.junit.jupiter:junit-jupiter-api:5.11.3", - "org.junit.jupiter:junit-jupiter-engine:5.11.3", - "org.junit.jupiter:junit-jupiter-params:5.11.3", - "org.junit.platform:junit-platform-launcher:1.11.3", - "org.junit.platform:junit-platform-reporting:1.11.3", - "org.junit.platform:junit-platform-commons:1.11.3", - "org.junit.platform:junit-platform-engine:1.11.3", + "org.junit.jupiter:junit-jupiter-api", + "org.junit.jupiter:junit-jupiter-engine", + "org.junit.jupiter:junit-jupiter-params", + "org.junit.platform:junit-platform-launcher", + "org.junit.platform:junit-platform-reporting", + "org.junit.platform:junit-platform-commons", + "org.junit.platform:junit-platform-engine", "org.mockito:mockito-core:5.14.2", "org.redisson:redisson:3.39.0", "org.slf4j:slf4j-api:2.0.16", @@ -223,6 +223,11 @@ maven.install( "org.tomlj:tomlj:1.1.1", "org.zeromq:jeromq:0.6.0", ], + boms = [ + "io.opentelemetry:opentelemetry-bom:1.44.1", + "io.netty:netty-bom:4.1.115.Final", + "org.junit:junit-bom:5.11.3", + ], excluded_artifacts = [ "org.hamcrest:hamcrest-all", # Replaced by hamcrest 2 "org.hamcrest:hamcrest-core", @@ -235,6 +240,7 @@ maven.install( repositories = [ "https://repo1.maven.org/maven2", ], + resolver = "maven", strict_visibility = True, ) use_repo(maven, "maven", "unpinned_maven") diff --git a/java/maven_install.json b/java/maven_install.json index 89ea3131ad63b..f7a2f0fe6bcde 100644 --- a/java/maven_install.json +++ b/java/maven_install.json @@ -1,14 +1,7 @@ { "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", - "__INPUT_ARTIFACTS_HASH": 329453186, - "__RESOLVED_ARTIFACTS_HASH": -9148858, - "conflict_resolution": { - "com.google.code.gson:gson:2.8.9": "com.google.code.gson:gson:2.11.0", - "com.google.errorprone:error_prone_annotations:2.3.2": "com.google.errorprone:error_prone_annotations:2.28.0", - "com.google.guava:guava:31.1-jre": "com.google.guava:guava:33.3.1-jre", - "com.google.j2objc:j2objc-annotations:1.3": "com.google.j2objc:j2objc-annotations:3.0.0", - "org.mockito:mockito-core:4.3.1": "org.mockito:mockito-core:5.14.2" - }, + "__INPUT_ARTIFACTS_HASH": -756051712, + "__RESOLVED_ARTIFACTS_HASH": -748733705, "artifacts": { "com.beust:jcommander": { "shasums": { @@ -138,10 +131,10 @@ }, "com.google.errorprone:error_prone_annotations": { "shasums": { - "jar": "f3fc8a3a0a4020706a373b00e7f57c2512dd26d1f83d28c7d38768f8682b231e", - "sources": "2936e9b315d790d8a6364f0574bcec9c8b2d78688b317e1765c4a16f9ef80632" + "jar": "357cd6cfb067c969226c442451502aee13800a24e950fdfde77bcdb4565a668d", + "sources": "7ce688ed1582a67097228c050192b7cfd00479a81d2b921f7cd5116994f1402d" }, - "version": "2.28.0" + "version": "2.3.2" }, "com.google.googlejavaformat:google-java-format": { "shasums": { @@ -173,17 +166,16 @@ }, "com.google.guava:listenablefuture": { "shasums": { - "jar": "b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99", - "sources": null + "jar": "b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99" }, "version": "9999.0-empty-to-avoid-conflict-with-guava" }, "com.google.j2objc:j2objc-annotations": { "shasums": { - "jar": "88241573467ddca44ffd4d74aa04c2bbfd11bf7c17e0c342c94c9de7a70a7c64", - "sources": "bd60019a0423c3a025ef6ab24fe0761f5f45ffb48a8cca74a01b678de1105d38" + "jar": "21af30c92267bd6122c0e0b4d20cccb6641a37eaf956c6540ec471d584e64a7b", + "sources": "ba4df669fec153fa4cd0ef8d02c6d3ef0702b7ac4cabe080facf3b6e490bb972" }, - "version": "3.0.0" + "version": "1.3" }, "com.google.truth:truth": { "shasums": { @@ -355,10 +347,10 @@ }, "io.opentelemetry.semconv:opentelemetry-semconv": { "shasums": { - "jar": "745a86a75ecb5e03f464f05ea2dc76e0f04d07273c5509fa74f393bff9b222b7", - "sources": "58a375cd34943d8dd4f64233b19fee6a5094e3ae533f77d527e75c276626d49e" + "jar": "e8ab86e93cef09e421a6213f4cf18421fcc6e1f9cf0ab94b9a31ed4460ddf553", + "sources": "b0588ae0617071c30451fe0f4916b2cde7aa8d24b542ee696a7bf59f7d7f46a8" }, - "version": "1.25.0-alpha" + "version": "1.28.0-alpha" }, "io.opentelemetry:opentelemetry-api": { "shasums": { @@ -446,10 +438,10 @@ }, "io.projectreactor:reactor-core": { "shasums": { - "jar": "44f055fbd033b6c976c53fb2e04b59027e79fb2312c37d2eaa54c77ea1ea80fe", - "sources": "fee913ed4e41d79ce1cf7db4526d23e848719083b65a4041dea590f91b1ef2f6" + "jar": "6e235f0be9732ebd6a42c585dfd53274065978bfbc28d721d7ecf487fde27b52", + "sources": "57e48b121636923ec362aa337556150edc8bc29ccba686c21d36016e05207b23" }, - "version": "3.6.6" + "version": "3.6.2" }, "io.reactivex.rxjava3:rxjava": { "shasums": { @@ -530,10 +522,10 @@ }, "org.apache.commons:commons-lang3": { "shasums": { - "jar": "7b96bf3ee68949abb5bc465559ac270e0551596fa34523fddf890ec418dde13c", - "sources": "ab3b86afb898f1026dbe43aaf71e9c1d719ec52d6e41887b362d86777c299b6f" + "jar": "d919d904486c037f8d193412da0c92e22a9fa24230b9d67a57855c5c31c7e94e", + "sources": "325a4551eee7d99f7616aa05b00ee3ca9d0cdc8face1b252a9864f2d945c58b3" }, - "version": "3.14.0" + "version": "3.12.0" }, "org.apache.commons:commons-text": { "shasums": { @@ -614,10 +606,10 @@ }, "org.checkerframework:checker-qual": { "shasums": { - "jar": "3fbc2e98f05854c3df16df9abaa955b91b15b3ecac33623208ed6424640ef0f6", - "sources": "d6bdee58964cd05aabfca4e44947d3cbdada6bf617ed618b62b3b0d5a21de339" + "jar": "ab0468b1ba35bb2ae45f61a60dc4960bd887660ab8f05113a662a7e675eae776", + "sources": "cbe362ef1dfacb927e0e4bf78a97b1b92ba50ca38b4406ae58b8c11e41ef2075" }, - "version": "3.43.0" + "version": "3.9.1" }, "org.dom4j:dom4j": { "shasums": { @@ -733,10 +725,10 @@ }, "org.ow2.asm:asm": { "shasums": { - "jar": "adf46d5e34940bdf148ecdd26a9ee8eea94496a72034ff7141066b3eea5c4e9d", - "sources": "11dfd88129204be18c0f592f8e066d0c07d8a6bc001f6c7b2cce5ff0588d5d71" + "jar": "0df97574914aee92fd349d0cb4e00f3345d45b2c239e0bb50f0a90ead47888e0", + "sources": "829bc5eb0ccd705a7c8afbf7cdc4b7e9a9f733d3a1a954b9afffd99c8e063366" }, - "version": "9.7" + "version": "9.0" }, "org.ow2.asm:asm-analysis": { "shasums": { @@ -1042,9 +1034,6 @@ "io.opentelemetry:opentelemetry-api-incubator", "io.opentelemetry:opentelemetry-sdk-common" ], - "io.projectreactor:reactor-core": [ - "org.reactivestreams:reactive-streams" - ], "io.reactivex.rxjava3:rxjava": [ "org.reactivestreams:reactive-streams" ], @@ -1182,9 +1171,6 @@ "eu.neilalexander:jnacl" ] }, - "skipped": [ - "com.google.guava:listenablefuture:jar:sources" - ], "packages": { "com.beust:jcommander": [ "com.beust.ah", @@ -1957,9 +1943,6 @@ "net.bytebuddy.agent", "net.bytebuddy.agent.utility.nullability" ], - "net.bytebuddy:byte-buddy:jar:sources": [ - "net.bytebuddy.build" - ], "net.sf.saxon:Saxon-HE": [ "net.sf.saxon", "net.sf.saxon.dom", @@ -2063,8 +2046,7 @@ "org.apache.commons.lang3.text", "org.apache.commons.lang3.text.translate", "org.apache.commons.lang3.time", - "org.apache.commons.lang3.tuple", - "org.apache.commons.lang3.util" + "org.apache.commons.lang3.tuple" ], "org.apache.commons:commons-text": [ "org.apache.commons.text", @@ -2551,32 +2533,38 @@ "org.checkerframework.checker.calledmethods.qual", "org.checkerframework.checker.compilermsgs.qual", "org.checkerframework.checker.fenum.qual", + "org.checkerframework.checker.formatter", "org.checkerframework.checker.formatter.qual", "org.checkerframework.checker.guieffect.qual", "org.checkerframework.checker.i18n.qual", + "org.checkerframework.checker.i18nformatter", "org.checkerframework.checker.i18nformatter.qual", "org.checkerframework.checker.index.qual", "org.checkerframework.checker.initialization.qual", "org.checkerframework.checker.interning.qual", "org.checkerframework.checker.lock.qual", - "org.checkerframework.checker.mustcall.qual", + "org.checkerframework.checker.nullness", "org.checkerframework.checker.nullness.qual", "org.checkerframework.checker.optional.qual", "org.checkerframework.checker.propkey.qual", + "org.checkerframework.checker.regex", "org.checkerframework.checker.regex.qual", "org.checkerframework.checker.signature.qual", + "org.checkerframework.checker.signedness", "org.checkerframework.checker.signedness.qual", "org.checkerframework.checker.tainting.qual", + "org.checkerframework.checker.units", "org.checkerframework.checker.units.qual", "org.checkerframework.common.aliasing.qual", "org.checkerframework.common.initializedfields.qual", "org.checkerframework.common.reflection.qual", "org.checkerframework.common.returnsreceiver.qual", "org.checkerframework.common.subtyping.qual", - "org.checkerframework.common.util.count.report.qual", + "org.checkerframework.common.util.report.qual", "org.checkerframework.common.value.qual", "org.checkerframework.dataflow.qual", - "org.checkerframework.framework.qual" + "org.checkerframework.framework.qual", + "org.checkerframework.framework.util" ], "org.dom4j:dom4j": [ "org.dom4j", From 209e275840956e7005e7864ea12b9bac559303c2 Mon Sep 17 00:00:00 2001 From: Michael Render Date: Sat, 30 Nov 2024 16:52:12 -0500 Subject: [PATCH 13/17] [dotnet] Add nullable reference annotations to `Platform` (#14834) --- .../Chromium/ChromiumDriverService.cs | 9 +- .../webdriver/Firefox/FirefoxDriverService.cs | 8 +- dotnet/src/webdriver/Platform.cs | 115 ++++++------------ 3 files changed, 39 insertions(+), 93 deletions(-) diff --git a/dotnet/src/webdriver/Chromium/ChromiumDriverService.cs b/dotnet/src/webdriver/Chromium/ChromiumDriverService.cs index 04a4e8994118f..95958e44812f4 100644 --- a/dotnet/src/webdriver/Chromium/ChromiumDriverService.cs +++ b/dotnet/src/webdriver/Chromium/ChromiumDriverService.cs @@ -208,7 +208,7 @@ protected static string ChromiumDriverServiceFileName(string fileName = DefaultC // straightforward as you might hope. // See: http://mono.wikia.com/wiki/Detecting_the_execution_platform // and https://msdn.microsoft.com/en-us/library/3a8hyw88(v=vs.110).aspx - const int PlatformMonoUnixValue = 128; + const PlatformID PlatformIDMonoUnix = (PlatformID)128; switch (Environment.OSVersion.Platform) { @@ -221,17 +221,14 @@ protected static string ChromiumDriverServiceFileName(string fileName = DefaultC case PlatformID.MacOSX: case PlatformID.Unix: + case PlatformIDMonoUnix: break; // Don't handle the Xbox case. Let default handle it. // case PlatformID.Xbox: // break; - default: - if ((int)Environment.OSVersion.Platform == PlatformMonoUnixValue) - { - break; - } + default: throw new WebDriverException("Unsupported platform: " + Environment.OSVersion.Platform); } diff --git a/dotnet/src/webdriver/Firefox/FirefoxDriverService.cs b/dotnet/src/webdriver/Firefox/FirefoxDriverService.cs index 6c58457461a22..3d35057e69e64 100644 --- a/dotnet/src/webdriver/Firefox/FirefoxDriverService.cs +++ b/dotnet/src/webdriver/Firefox/FirefoxDriverService.cs @@ -263,7 +263,7 @@ private static string FirefoxDriverServiceFileName() // straightforward as you might hope. // See: http://mono.wikia.com/wiki/Detecting_the_execution_platform // and https://msdn.microsoft.com/en-us/library/3a8hyw88(v=vs.110).aspx - const int PlatformMonoUnixValue = 128; + const PlatformID PlatformIDMonoUnix = (PlatformID)128; switch (Environment.OSVersion.Platform) { @@ -276,17 +276,13 @@ private static string FirefoxDriverServiceFileName() case PlatformID.MacOSX: case PlatformID.Unix: + case PlatformIDMonoUnix: break; // Don't handle the Xbox case. Let default handle it. // case PlatformID.Xbox: // break; default: - if ((int)Environment.OSVersion.Platform == PlatformMonoUnixValue) - { - break; - } - throw new WebDriverException("Unsupported platform: " + Environment.OSVersion.Platform); } diff --git a/dotnet/src/webdriver/Platform.cs b/dotnet/src/webdriver/Platform.cs index c143fd682d3b7..4ea2cc9be61c8 100644 --- a/dotnet/src/webdriver/Platform.cs +++ b/dotnet/src/webdriver/Platform.cs @@ -19,6 +19,8 @@ using System; +#nullable enable + namespace OpenQA.Selenium { /// @@ -84,10 +86,7 @@ public enum PlatformType /// public class Platform { - private static Platform current; - private PlatformType platformTypeValue; - private int major; - private int minor; + private static Platform? current; /// /// Initializes a new instance of the class for a specific platform type. @@ -95,39 +94,39 @@ public class Platform /// The platform type. public Platform(PlatformType typeValue) { - this.platformTypeValue = typeValue; + this.PlatformType = typeValue; } private Platform() { - this.major = Environment.OSVersion.Version.Major; - this.minor = Environment.OSVersion.Version.Minor; + this.MajorVersion = Environment.OSVersion.Version.Major; + this.MinorVersion = Environment.OSVersion.Version.Minor; switch (Environment.OSVersion.Platform) { case PlatformID.Win32NT: - if (this.major == 5) + if (this.MajorVersion == 5) { - this.platformTypeValue = PlatformType.XP; + this.PlatformType = PlatformType.XP; } - else if (this.major == 6) + else if (this.MajorVersion == 6) { - this.platformTypeValue = PlatformType.Vista; + this.PlatformType = PlatformType.Vista; } else { - this.platformTypeValue = PlatformType.Windows; + this.PlatformType = PlatformType.Windows; } break; // Thanks to a bug in Mono Mac and Linux will be treated the same https://bugzilla.novell.com/show_bug.cgi?id=515570 but adding this in case case PlatformID.MacOSX: - this.platformTypeValue = PlatformType.Mac; + this.PlatformType = PlatformType.Mac; break; case PlatformID.Unix: - this.platformTypeValue = PlatformType.Unix; + this.PlatformType = PlatformType.Unix; break; } } @@ -135,87 +134,44 @@ private Platform() /// /// Gets the current platform. /// - public static Platform CurrentPlatform - { - get - { - if (current == null) - { - current = new Platform(); - } - - return current; - } - } + public static Platform CurrentPlatform => current ??= new Platform(); /// /// Gets the major version of the platform operating system. /// - public int MajorVersion - { - get { return this.major; } - } + public int MajorVersion { get; } /// /// Gets the major version of the platform operating system. /// - public int MinorVersion - { - get { return this.minor; } - } + public int MinorVersion { get; } /// /// Gets the type of the platform. /// - public PlatformType PlatformType - { - get { return this.platformTypeValue; } - } + public PlatformType PlatformType { get; } /// /// Gets the value of the platform type for transmission using the JSON Wire Protocol. /// - public string ProtocolPlatformType - { - get { return this.platformTypeValue.ToString("G").ToUpperInvariant(); } - } + public string ProtocolPlatformType => this.PlatformType.ToString("G").ToUpperInvariant(); /// /// Compares the platform to the specified type. /// - /// A value to compare to. + /// A value to compare to. /// if the platforms match; otherwise . public bool IsPlatformType(PlatformType compareTo) { - bool platformIsType = false; - switch (compareTo) + return compareTo switch { - case PlatformType.Any: - platformIsType = true; - break; - - case PlatformType.Windows: - platformIsType = this.platformTypeValue == PlatformType.Windows || this.platformTypeValue == PlatformType.XP || this.platformTypeValue == PlatformType.Vista; - break; - - case PlatformType.Vista: - platformIsType = this.platformTypeValue == PlatformType.Windows || this.platformTypeValue == PlatformType.Vista; - break; - - case PlatformType.XP: - platformIsType = this.platformTypeValue == PlatformType.Windows || this.platformTypeValue == PlatformType.XP; - break; - - case PlatformType.Linux: - platformIsType = this.platformTypeValue == PlatformType.Linux || this.platformTypeValue == PlatformType.Unix; - break; - - default: - platformIsType = this.platformTypeValue == compareTo; - break; - } - - return platformIsType; + PlatformType.Any => true, + PlatformType.Windows => this.PlatformType is PlatformType.Windows or PlatformType.XP or PlatformType.Vista, + PlatformType.Vista => this.PlatformType is PlatformType.Windows or PlatformType.Vista, + PlatformType.XP => this.PlatformType is PlatformType.Windows or PlatformType.XP, + PlatformType.Linux => this.PlatformType is PlatformType.Linux or PlatformType.Unix, + _ => this.PlatformType == compareTo, + }; } /// @@ -224,7 +180,7 @@ public bool IsPlatformType(PlatformType compareTo) /// The string value for this platform type. public override string ToString() { - return this.platformTypeValue.ToString(); + return this.PlatformType.ToString(); } /// @@ -234,18 +190,15 @@ public override string ToString() /// The Platform object represented by the string name. internal static Platform FromString(string platformName) { - PlatformType platformTypeFromString = PlatformType.Any; - try + if (Enum.TryParse(platformName, ignoreCase: true, out PlatformType platformTypeFromString)) { - platformTypeFromString = (PlatformType)Enum.Parse(typeof(PlatformType), platformName, true); - } - catch (ArgumentException) - { - // If the requested platform string is not a valid platform type, - // ignore it and use PlatformType.Any. + return new Platform(platformTypeFromString); } - return new Platform(platformTypeFromString); + // If the requested platform string is not a valid platform type, + // ignore it and use PlatformType.Any. + + return new Platform(PlatformType.Any); } } } From b256c5f619d8c2686b0918bab148689e18eeaba4 Mon Sep 17 00:00:00 2001 From: Michael Render Date: Sun, 1 Dec 2024 04:41:51 -0500 Subject: [PATCH 14/17] [dotnet] Fix Virtual Authenticator removal, annotate NRT (#14822) --- .../src/webdriver/VirtualAuth/Credential.cs | 82 ++++++++----------- .../VirtualAuth/IHasVirtualAuthenticator.cs | 14 ++++ .../VirtualAuthenticatorOptions.cs | 55 +++++++------ dotnet/src/webdriver/WebDriver.cs | 79 ++++++++++++++---- .../VirtualAuthn/VirtualAuthenticatorTest.cs | 56 ++++++++++++- 5 files changed, 195 insertions(+), 91 deletions(-) diff --git a/dotnet/src/webdriver/VirtualAuth/Credential.cs b/dotnet/src/webdriver/VirtualAuth/Credential.cs index 170677b12e035..57dbcbe76ff45 100644 --- a/dotnet/src/webdriver/VirtualAuth/Credential.cs +++ b/dotnet/src/webdriver/VirtualAuth/Credential.cs @@ -18,31 +18,30 @@ // using OpenQA.Selenium.Internal; +using System; using System.Collections.Generic; +#nullable enable + namespace OpenQA.Selenium.VirtualAuth { /// /// A credential stored in a virtual authenticator. - /// Refer https://w3c.github.io/webauthn/#credential-parameters + /// Refer /// - public class Credential + public sealed class Credential { private readonly byte[] id; - private readonly bool isResidentCredential; - private readonly string rpId; - private readonly string privateKey; - private readonly byte[] userHandle; - private readonly int signCount; + private readonly byte[]? userHandle; - private Credential(byte[] id, bool isResidentCredential, string rpId, string privateKey, byte[] userHandle, int signCount) + private Credential(byte[] id, bool isResidentCredential, string rpId, string privateKey, byte[]? userHandle, int signCount) { - this.id = id; - this.isResidentCredential = isResidentCredential; - this.rpId = rpId; - this.privateKey = privateKey; + this.id = id ?? throw new ArgumentNullException(nameof(id)); + this.IsResidentCredential = isResidentCredential; + this.RpId = rpId ?? throw new ArgumentNullException(nameof(rpId)); + this.PrivateKey = privateKey ?? throw new ArgumentNullException(nameof(privateKey)); this.userHandle = userHandle; - this.signCount = signCount; + this.SignCount = signCount; } /// @@ -53,6 +52,7 @@ private Credential(byte[] id, bool isResidentCredential, string rpId, string pri /// The private Key for the credentials. /// The signature counter for the credentials. /// The created instance of the Credential class. + /// If , , or are . public static Credential CreateNonResidentCredential(byte[] id, string rpId, string privateKey, int signCount) { return new Credential(id, false, rpId, privateKey, null, signCount); @@ -67,6 +67,7 @@ public static Credential CreateNonResidentCredential(byte[] id, string rpId, str /// The user handle associated to the credential. /// The signature counter for the credentials. /// The created instance of the Credential class. + /// If , , or are . public static Credential CreateResidentCredential(byte[] id, string rpId, string privateKey, byte[] userHandle, int signCount) { return new Credential(id, true, rpId, privateKey, userHandle, signCount); @@ -75,50 +76,32 @@ public static Credential CreateResidentCredential(byte[] id, string rpId, string /// /// Gets the byte array of the ID of the credential. /// - public byte[] Id - { - get { return (byte[])id.Clone(); } - } + public byte[] Id => (byte[])id.Clone(); /// /// Gets a value indicating whether this Credential is a resident credential. /// - public bool IsResidentCredential - { - get { return this.isResidentCredential; } - } + public bool IsResidentCredential { get; } /// /// Gets the ID of the relying party of this credential. /// - public string RpId - { - get { return this.rpId; } - } + public string RpId { get; } /// /// Gets the private key of the credential. /// - public string PrivateKey - { - get { return this.privateKey; } - } + public string PrivateKey { get; } /// /// Gets the user handle of the credential. /// - public byte[] UserHandle - { - get { return userHandle == null ? null : (byte[])userHandle.Clone(); } - } + public byte[]? UserHandle => (byte[]?)userHandle?.Clone(); /// /// Gets the signature counter associated to the public key credential source. /// - public int SignCount - { - get { return this.signCount; } - } + public int SignCount { get; } /// /// Creates a Credential instance from a dictionary of values. @@ -127,13 +110,14 @@ public int SignCount /// The created instance of the Credential. public static Credential FromDictionary(Dictionary dictionary) { - return new Credential( - Base64UrlEncoder.DecodeBytes((string)dictionary["credentialId"]), - (bool)dictionary["isResidentCredential"], - dictionary.ContainsKey("rpId") ? (string)dictionary["rpId"] : null, - (string)dictionary["privateKey"], - dictionary.ContainsKey("userHandle") ? Base64UrlEncoder.DecodeBytes((string)dictionary["userHandle"]) : null, - (int)((long)dictionary["signCount"])); + byte[] id = Base64UrlEncoder.DecodeBytes((string)dictionary["credentialId"]); + bool isResidentCredential = (bool)dictionary["isResidentCredential"]; + string? rpId = dictionary.TryGetValue("rpId", out object? r) ? (string)r : null; + string privateKey = (string)dictionary["privateKey"]; + byte[]? userHandle = dictionary.TryGetValue("userHandle", out object? u) ? Base64UrlEncoder.DecodeBytes((string)u) : null; + int signCount = (int)(long)dictionary["signCount"]; + + return new Credential(id, isResidentCredential, rpId, privateKey, userHandle, signCount); } /// @@ -145,11 +129,11 @@ public Dictionary ToDictionary() Dictionary toReturn = new Dictionary(); toReturn["credentialId"] = Base64UrlEncoder.Encode(this.id); - toReturn["isResidentCredential"] = this.isResidentCredential; - toReturn["rpId"] = this.rpId; - toReturn["privateKey"] = this.privateKey; - toReturn["signCount"] = this.signCount; - if (this.userHandle != null) + toReturn["isResidentCredential"] = this.IsResidentCredential; + toReturn["rpId"] = this.RpId; + toReturn["privateKey"] = this.PrivateKey; + toReturn["signCount"] = this.SignCount; + if (this.userHandle is not null) { toReturn["userHandle"] = Base64UrlEncoder.Encode(this.userHandle); } diff --git a/dotnet/src/webdriver/VirtualAuth/IHasVirtualAuthenticator.cs b/dotnet/src/webdriver/VirtualAuth/IHasVirtualAuthenticator.cs index 92619ded68acb..2d8616416f90e 100644 --- a/dotnet/src/webdriver/VirtualAuth/IHasVirtualAuthenticator.cs +++ b/dotnet/src/webdriver/VirtualAuth/IHasVirtualAuthenticator.cs @@ -17,8 +17,11 @@ // under the License. // +using System; using System.Collections.Generic; +#nullable enable + namespace OpenQA.Selenium.VirtualAuth { /// @@ -31,18 +34,23 @@ public interface IHasVirtualAuthenticator /// /// The VirtualAuthenticatorOptions to use in creating the authenticator. /// The ID of the added virtual authenticator. + /// If is . string AddVirtualAuthenticator(VirtualAuthenticatorOptions options); /// /// Removes a virtual authenticator. /// /// The ID of the virtual authenticator to remove. + /// If is . + /// If the specified virtual authenticator does not exist. void RemoveVirtualAuthenticator(string id); /// /// Adds a credential to the virtual authenticator. /// /// The credential to add to the authenticator. + /// If is . + /// If a Virtual Authenticator has not been added yet. void AddCredential(Credential credential); /// @@ -55,23 +63,29 @@ public interface IHasVirtualAuthenticator /// Removes a credential from the virtual authenticator. /// /// A byte array representing the ID of the credential to remove. + /// If is . + /// If a Virtual Authenticator has not been added yet. void RemoveCredential(byte[] credentialId); /// /// Removes a credential from the virtual authenticator. /// /// A string representing the ID of the credential to remove. + /// If is . + /// If a Virtual Authenticator has not been added yet. void RemoveCredential(string credentialId); /// /// Removes all credentials registered to this virtual authenticator. /// + /// If a Virtual Authenticator has not been added yet. void RemoveAllCredentials(); /// /// Sets whether or not a user is verified in this virtual authenticator. /// /// if the user is verified; otherwise . + /// If a Virtual Authenticator has not been added yet. void SetUserVerified(bool verified); } } diff --git a/dotnet/src/webdriver/VirtualAuth/VirtualAuthenticatorOptions.cs b/dotnet/src/webdriver/VirtualAuth/VirtualAuthenticatorOptions.cs index 6e118df89e037..502d1368f9d0f 100644 --- a/dotnet/src/webdriver/VirtualAuth/VirtualAuthenticatorOptions.cs +++ b/dotnet/src/webdriver/VirtualAuth/VirtualAuthenticatorOptions.cs @@ -20,6 +20,8 @@ using System; using System.Collections.Generic; +#nullable enable + namespace OpenQA.Selenium.VirtualAuth { /// @@ -78,10 +80,13 @@ public static class Transport private bool isUserVerified = false; /// - /// Sets the protocol the Virtual Authenticator speaks + /// Sets the Client to Authenticator Protocol (CTAP) this Virtual Authenticator speaks. /// - /// Valid protocol value - /// VirtualAuthenticatorOptions + /// The CTAP protocol identifier. + /// This options instance for chaining. + /// Valid protocols are available on the type. + /// If is not a supported protocol value. + /// public VirtualAuthenticatorOptions SetProtocol(string protocol) { if (string.Equals(Protocol.CTAP2, protocol) || string.Equals(Protocol.U2F, protocol)) @@ -92,15 +97,19 @@ public VirtualAuthenticatorOptions SetProtocol(string protocol) else { throw new ArgumentException("Enter a valid protocol value." + - "Refer to https://www.w3.org/TR/webauthn-2/#sctn-automation-virtual-authenticators for supported protocols."); + "Refer to https://www.w3.org/TR/webauthn-2/#sctn-automation-virtual-authenticators for supported protocols."); } } /// - /// Sets the transport authenticator needs to implement to communicate with clients + /// Sets the Authenticator Transport this Virtual Authenticator needs to implement, to communicate with clients. /// - /// Valid transport value - /// VirtualAuthenticatorOptions + /// Valid transport value. + /// + /// This options instance for chaining. + /// Valid protocols are available on the type. + /// If is not a supported transport value. + /// public VirtualAuthenticatorOptions SetTransport(string transport) { if (Transport.BLE == transport || Transport.INTERNAL == transport || Transport.NFC == transport || Transport.USB == transport) @@ -111,16 +120,15 @@ public VirtualAuthenticatorOptions SetTransport(string transport) else { throw new ArgumentException("Enter a valid transport value." + - "Refer to https://www.w3.org/TR/webauthn-2/#enum-transport for supported transport values."); + "Refer to https://www.w3.org/TR/webauthn-2/#enum-transport for supported transport values."); } } /// - /// If set to true the authenticator will support client-side discoverable credentials. - /// Refer https://w3c.github.io/webauthn/#client-side-discoverable-credential + /// If set to , the authenticator will support Client-side discoverable Credentials. /// - /// boolean value to set - /// VirtualAuthenticatorOptions + /// Whether authenticator will support client-side discoverable credentials. + /// This options instance for chaining. public VirtualAuthenticatorOptions SetHasResidentKey(bool hasResidentKey) { this.hasResidentKey = hasResidentKey; @@ -128,11 +136,10 @@ public VirtualAuthenticatorOptions SetHasResidentKey(bool hasResidentKey) } /// - /// If set to true, the authenticator supports user verification. - /// Refer https://w3c.github.io/webauthn/#user-verification. + /// If set to , the authenticator will support User Verification. /// - /// boolean value to set - /// + /// Whether the authenticator supports user verification. + /// This options instance for chaining. public VirtualAuthenticatorOptions SetHasUserVerification(bool hasUserVerification) { this.hasUserVerification = hasUserVerification; @@ -140,11 +147,10 @@ public VirtualAuthenticatorOptions SetHasUserVerification(bool hasUserVerificati } /// - /// If set to true, a user consent will always be granted. - /// Refer https://w3c.github.io/webauthn/#user-consent + /// If set to , a User Consent will always be granted. /// - /// boolean value to set - /// VirtualAuthenticatorOptions + /// Whether a user consent will always be granted. + /// This options instance for chaining. public VirtualAuthenticatorOptions SetIsUserConsenting(bool isUserConsenting) { this.isUserConsenting = isUserConsenting; @@ -152,11 +158,10 @@ public VirtualAuthenticatorOptions SetIsUserConsenting(bool isUserConsenting) } /// - /// If set to true, User Verification will always succeed. - /// Refer https://w3c.github.io/webauthn/#user-verification + /// If set to , User Verification will always succeed. /// - /// boolean value to set - /// VirtualAuthenticatorOptions + /// Whether User Verification will always succeed. + /// This options instance for chaining. public VirtualAuthenticatorOptions SetIsUserVerified(bool isUserVerified) { this.isUserVerified = isUserVerified; @@ -164,7 +169,7 @@ public VirtualAuthenticatorOptions SetIsUserVerified(bool isUserVerified) } /// - /// Serializes this set of options to a dictionary of key-value pairs. + /// Serializes this set of options into a dictionary of key-value pairs. /// /// The dictionary containing the values of this set of options. public Dictionary ToDictionary() diff --git a/dotnet/src/webdriver/WebDriver.cs b/dotnet/src/webdriver/WebDriver.cs index d9960ea97b235..9bcca8b28f8b3 100644 --- a/dotnet/src/webdriver/WebDriver.cs +++ b/dotnet/src/webdriver/WebDriver.cs @@ -1036,44 +1036,68 @@ private object ParseJavaScriptReturnValue(object responseValue) return returnValue; } +#nullable enable + /// /// Creates a Virtual Authenticator. /// - /// VirtualAuthenticator Options (https://w3c.github.io/webauthn/#sctn-automation-virtual-authenticators) + /// Virtual Authenticator Options. /// Authenticator id as string + /// If is . public string AddVirtualAuthenticator(VirtualAuthenticatorOptions options) { + if (options is null) + { + throw new ArgumentNullException(nameof(options)); + } + Response commandResponse = this.Execute(DriverCommand.AddVirtualAuthenticator, options.ToDictionary()); - string id = commandResponse.Value.ToString(); + string id = (string)commandResponse.Value!; this.AuthenticatorId = id; - return this.AuthenticatorId; + return id; } /// /// Removes the Virtual Authenticator /// - /// Id as string that uniquely identifies a Virtual Authenticator + /// Id as string that uniquely identifies a Virtual Authenticator. + /// If is . public void RemoveVirtualAuthenticator(string authenticatorId) { + if (authenticatorId is null) + { + throw new ArgumentNullException(nameof(authenticatorId)); + } + Dictionary parameters = new Dictionary(); - parameters.Add("authenticatorId", this.AuthenticatorId); + parameters.Add("authenticatorId", authenticatorId); + this.Execute(DriverCommand.RemoveVirtualAuthenticator, parameters); this.AuthenticatorId = null; } /// - /// Gets the virtual authenticator ID for this WebDriver instance. + /// Gets the cached virtual authenticator ID, or if no authenticator ID is set. /// - public string AuthenticatorId { get; private set; } + public string? AuthenticatorId { get; private set; } /// /// Add a credential to the Virtual Authenticator/ /// /// The credential to be stored in the Virtual Authenticator + /// If is . + /// If a Virtual Authenticator has not been added yet. public void AddCredential(Credential credential) { + if (credential is null) + { + throw new ArgumentNullException(nameof(credential)); + } + + string authenticatorId = this.AuthenticatorId ?? throw new InvalidOperationException("Virtual Authenticator needs to be added before it can perform operations"); + Dictionary parameters = new Dictionary(credential.ToDictionary()); - parameters.Add("authenticatorId", this.AuthenticatorId); + parameters.Add("authenticatorId", authenticatorId); this.Execute(driverCommandToExecute: DriverCommand.AddCredential, parameters); } @@ -1082,18 +1106,25 @@ public void AddCredential(Credential credential) /// Retrieves all the credentials stored in the Virtual Authenticator /// /// List of credentials + /// If a Virtual Authenticator has not been added yet. public List GetCredentials() { + string authenticatorId = this.AuthenticatorId ?? throw new InvalidOperationException("Virtual Authenticator needs to be added before it can perform operations"); + Dictionary parameters = new Dictionary(); - parameters.Add("authenticatorId", this.AuthenticatorId); + parameters.Add("authenticatorId", authenticatorId); - object[] commandResponse = (object[])this.Execute(driverCommandToExecute: DriverCommand.GetCredentials, parameters).Value; + Response getCredentialsResponse = this.Execute(driverCommandToExecute: DriverCommand.GetCredentials, parameters); - List credentials = new List(); + if (getCredentialsResponse.Value is not object?[] credentialsList) + { + throw new WebDriverException($"Get credentials call succeeded, but the response was not a list of credentials: {getCredentialsResponse.Value}"); + } - foreach (object dictionary in commandResponse) + List credentials = new List(credentialsList.Length); + foreach (object? dictionary in credentialsList) { - Credential credential = Credential.FromDictionary((Dictionary)dictionary); + Credential credential = Credential.FromDictionary((Dictionary)dictionary!); credentials.Add(credential); } @@ -1104,6 +1135,8 @@ public List GetCredentials() /// Removes the credential identified by the credentialId from the Virtual Authenticator. /// /// The id as byte array that uniquely identifies a credential + /// If is . + /// If a Virtual Authenticator has not been added yet. public void RemoveCredential(byte[] credentialId) { RemoveCredential(Base64UrlEncoder.Encode(credentialId)); @@ -1113,10 +1146,19 @@ public void RemoveCredential(byte[] credentialId) /// Removes the credential identified by the credentialId from the Virtual Authenticator. /// /// The id as string that uniquely identifies a credential + /// If is . + /// If a Virtual Authenticator has not been added yet. public void RemoveCredential(string credentialId) { + if (credentialId is null) + { + throw new ArgumentNullException(nameof(credentialId)); + } + + string authenticatorId = this.AuthenticatorId ?? throw new InvalidOperationException("Virtual Authenticator needs to be added before it can perform operations"); + Dictionary parameters = new Dictionary(); - parameters.Add("authenticatorId", this.AuthenticatorId); + parameters.Add("authenticatorId", authenticatorId); parameters.Add("credentialId", credentialId); this.Execute(driverCommandToExecute: DriverCommand.RemoveCredential, parameters); @@ -1125,10 +1167,13 @@ public void RemoveCredential(string credentialId) /// /// Removes all the credentials stored in the Virtual Authenticator. /// + /// If a Virtual Authenticator has not been added yet. public void RemoveAllCredentials() { + string authenticatorId = this.AuthenticatorId ?? throw new InvalidOperationException("Virtual Authenticator needs to be added before it can perform operations"); + Dictionary parameters = new Dictionary(); - parameters.Add("authenticatorId", this.AuthenticatorId); + parameters.Add("authenticatorId", authenticatorId); this.Execute(driverCommandToExecute: DriverCommand.RemoveAllCredentials, parameters); } @@ -1139,8 +1184,10 @@ public void RemoveAllCredentials() /// The boolean value representing value to be set public void SetUserVerified(bool verified) { + string authenticatorId = this.AuthenticatorId ?? throw new InvalidOperationException("Virtual Authenticator needs to be added before it can perform operations"); + Dictionary parameters = new Dictionary(); - parameters.Add("authenticatorId", this.AuthenticatorId); + parameters.Add("authenticatorId", authenticatorId); parameters.Add("isUserVerified", verified); this.Execute(driverCommandToExecute: DriverCommand.SetUserVerified, parameters); diff --git a/dotnet/test/common/VirtualAuthn/VirtualAuthenticatorTest.cs b/dotnet/test/common/VirtualAuthn/VirtualAuthenticatorTest.cs index e6c16e11b673a..c46d541dcb548 100644 --- a/dotnet/test/common/VirtualAuthn/VirtualAuthenticatorTest.cs +++ b/dotnet/test/common/VirtualAuthn/VirtualAuthenticatorTest.cs @@ -194,6 +194,26 @@ public void ShouldRemoveAuthenticator() Assert.IsNull(webDriver.AuthenticatorId); } + [Test] + [NeedsFreshDriver(IsCreatedAfterTest = true)] + [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Virtual Authenticator")] + [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Virtual Authenticator")] + [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Virtual Authenticator")] + public void ShouldSupportMultipleVirtualAuthenticatorsAtOnce() + { + VirtualAuthenticatorOptions options = new VirtualAuthenticatorOptions(); + + string authenticatorId1 = webDriver.AddVirtualAuthenticator(options); + Assert.That(webDriver.AuthenticatorId, Is.EqualTo(authenticatorId1)); + + string authenticatorId2 = webDriver.AddVirtualAuthenticator(options); + + webDriver.RemoveVirtualAuthenticator(authenticatorId1); + webDriver.RemoveVirtualAuthenticator(authenticatorId2); + + Assert.IsNull(webDriver.AuthenticatorId); + } + [Test] [NeedsFreshDriver(IsCreatedAfterTest = true)] [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Virtual Authenticator")] @@ -302,7 +322,9 @@ public void AddResidentCredentialNotSupportedWhenAuthenticatorUsesU2FProtocol() byte[] userHandle = { 1 }; Credential credential = Credential.CreateResidentCredential( credentialId, "localhost", base64EncodedEC256PK, userHandle, /*signCount=*/0); - Assert.Throws(() => webDriver.AddCredential(credential)); + Assert.That( + () => webDriver.AddCredential(credential), + Throws.TypeOf().With.Message.Contains("The Authenticator does not support Resident Credentials.")); } [Test] @@ -492,5 +514,37 @@ public void testSetUserVerified() Assert.True(error.StartsWith("NotAllowedError")); } + + [Test] + [NeedsFreshDriver(IsCreatedAfterTest = true)] + [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Virtual Authenticator")] + [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Virtual Authenticator")] + [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Virtual Authenticator")] + public void ShouldThrowOnInvalidArguments() + { + Assert.That( + () => webDriver.AddVirtualAuthenticator(null), + Throws.ArgumentNullException); + + Assert.That( + () => webDriver.RemoveVirtualAuthenticator(null), + Throws.ArgumentNullException); + + Assert.That( + () => webDriver.AddCredential(null), + Throws.ArgumentNullException); + + Assert.That( + () => webDriver.RemoveCredential((byte[])null), + Throws.ArgumentNullException); + + Assert.That( + () => webDriver.RemoveCredential((string)null), + Throws.ArgumentNullException); + + Assert.That( + () => webDriver.RemoveVirtualAuthenticator("non-existant"), + Throws.TypeOf()); + } } } From 51e72677c73f11bacb1b7a81a306d57989efd136 Mon Sep 17 00:00:00 2001 From: Michael Render Date: Sun, 1 Dec 2024 15:49:50 -0500 Subject: [PATCH 15/17] [dotnet] Annotate Nullable Reference Types on `OpenQA.Selenium.Internal` (#14840) --- dotnet/src/webdriver/ICapabilities.cs | 4 +- .../src/webdriver/Internal/AndroidOptions.cs | 26 +++-------- .../src/webdriver/Internal/FileUtilities.cs | 12 ++--- .../src/webdriver/Internal/IFindsElement.cs | 2 + .../Internal/IHasCapabilitiesDictionary.cs | 2 + .../webdriver/Internal/ResourceUtilities.cs | 33 ++++++-------- .../Internal/ResponseValueJsonConverter.cs | 14 +++--- .../Internal/ReturnedCapabilities.cs | 45 +++++++------------ 8 files changed, 59 insertions(+), 79 deletions(-) diff --git a/dotnet/src/webdriver/ICapabilities.cs b/dotnet/src/webdriver/ICapabilities.cs index 1a66162ffb098..df0c8a776c77e 100644 --- a/dotnet/src/webdriver/ICapabilities.cs +++ b/dotnet/src/webdriver/ICapabilities.cs @@ -19,6 +19,8 @@ using System; +#nullable enable + namespace OpenQA.Selenium { /// @@ -49,6 +51,6 @@ public interface ICapabilities /// The capability to get. /// An object associated with the capability, or /// if the capability is not set on the browser. - object GetCapability(string capability); + object? GetCapability(string capability); } } diff --git a/dotnet/src/webdriver/Internal/AndroidOptions.cs b/dotnet/src/webdriver/Internal/AndroidOptions.cs index e2d9e5b94763c..d002d1e6acc81 100644 --- a/dotnet/src/webdriver/Internal/AndroidOptions.cs +++ b/dotnet/src/webdriver/Internal/AndroidOptions.cs @@ -19,6 +19,8 @@ using System; +#nullable enable + namespace OpenQA.Selenium.Internal { /// @@ -26,14 +28,11 @@ namespace OpenQA.Selenium.Internal /// public class AndroidOptions { - private string androidPackage; - private string androidDeviceSerial; - private string androidActivity; - /// /// Initializes a new instance of the class. /// /// + /// If is or . protected AndroidOptions(string androidPackage) { if (string.IsNullOrEmpty(androidPackage)) @@ -41,33 +40,22 @@ protected AndroidOptions(string androidPackage) throw new ArgumentException("The Android package cannot be null or the empty string", nameof(androidPackage)); } - this.androidPackage = androidPackage; + this.AndroidPackage = androidPackage; } /// /// The package name of the application to automate. /// - public string AndroidPackage - { - get { return this.androidPackage; } - } + public string AndroidPackage { get; } /// /// The serial number of the device on which to launch the application. /// - public string AndroidDeviceSerial - { - get { return this.androidDeviceSerial; } - set { this.androidDeviceSerial = value; } - } + public string? AndroidDeviceSerial { get; set; } /// /// Gets or sets the name of the Activity hosting the app. /// - public string AndroidActivity - { - get { return this.androidActivity; } - set { this.androidActivity = value; } - } + public string? AndroidActivity { get; set; } } } diff --git a/dotnet/src/webdriver/Internal/FileUtilities.cs b/dotnet/src/webdriver/Internal/FileUtilities.cs index 0d83a6927aeca..d288a58e5f296 100644 --- a/dotnet/src/webdriver/Internal/FileUtilities.cs +++ b/dotnet/src/webdriver/Internal/FileUtilities.cs @@ -23,6 +23,8 @@ using System.IO; using System.Reflection; +#nullable enable + namespace OpenQA.Selenium.Internal { /// @@ -40,7 +42,7 @@ internal static class FileUtilities /// if the copy is completed; otherwise . public static bool CopyDirectory(string sourceDirectory, string destinationDirectory) { - bool copyComplete = false; + bool copyComplete; DirectoryInfo sourceDirectoryInfo = new DirectoryInfo(sourceDirectory); DirectoryInfo destinationDirectoryInfo = new DirectoryInfo(destinationDirectory); @@ -132,7 +134,7 @@ public static string FindFile(string fileName) // If it's not in the same directory as the executing assembly, // try looking in the system path. - string systemPath = Environment.GetEnvironmentVariable("PATH"); + string? systemPath = Environment.GetEnvironmentVariable("PATH"); if (!string.IsNullOrEmpty(systemPath)) { string expandedPath = Environment.ExpandEnvironmentVariables(systemPath); @@ -165,7 +167,7 @@ public static string FindFile(string fileName) public static string GetCurrentDirectory() { Assembly executingAssembly = typeof(FileUtilities).Assembly; - string location = null; + string? location = null; // Make sure not to call Path.GetDirectoryName if assembly location is null or empty if (!string.IsNullOrEmpty(executingAssembly.Location)) @@ -184,13 +186,13 @@ public static string GetCurrentDirectory() location = Directory.GetCurrentDirectory(); } - string currentDirectory = location; + string currentDirectory = location!; // If we're shadow copying, get the directory from the codebase instead if (AppDomain.CurrentDomain.ShadowCopyFiles) { Uri uri = new Uri(executingAssembly.CodeBase); - currentDirectory = Path.GetDirectoryName(uri.LocalPath); + currentDirectory = Path.GetDirectoryName(uri.LocalPath)!; } return currentDirectory; diff --git a/dotnet/src/webdriver/Internal/IFindsElement.cs b/dotnet/src/webdriver/Internal/IFindsElement.cs index a43055f9c91bd..7bcd08751a8fb 100644 --- a/dotnet/src/webdriver/Internal/IFindsElement.cs +++ b/dotnet/src/webdriver/Internal/IFindsElement.cs @@ -19,6 +19,8 @@ using System.Collections.ObjectModel; +#nullable enable + namespace OpenQA.Selenium.Internal { /// diff --git a/dotnet/src/webdriver/Internal/IHasCapabilitiesDictionary.cs b/dotnet/src/webdriver/Internal/IHasCapabilitiesDictionary.cs index 8c281818c4e0d..8723b5b11e580 100644 --- a/dotnet/src/webdriver/Internal/IHasCapabilitiesDictionary.cs +++ b/dotnet/src/webdriver/Internal/IHasCapabilitiesDictionary.cs @@ -19,6 +19,8 @@ using System.Collections.Generic; +#nullable enable + namespace OpenQA.Selenium.Internal { /// diff --git a/dotnet/src/webdriver/Internal/ResourceUtilities.cs b/dotnet/src/webdriver/Internal/ResourceUtilities.cs index 2f854a70214d4..0f22a33681c83 100644 --- a/dotnet/src/webdriver/Internal/ResourceUtilities.cs +++ b/dotnet/src/webdriver/Internal/ResourceUtilities.cs @@ -22,6 +22,8 @@ using System.Reflection; using System.Runtime.InteropServices; +#nullable enable + namespace OpenQA.Selenium.Internal { /// @@ -29,8 +31,8 @@ namespace OpenQA.Selenium.Internal /// internal static class ResourceUtilities { - private static string productVersion; - private static string platformFamily; + private static string? productVersion; + private static string? platformFamily; /// /// Gets a string representing the informational version of the Selenium product. @@ -60,18 +62,7 @@ public static string ProductVersion /// /// Gets a string representing the platform family on which the Selenium assembly is executing. /// - public static string PlatformFamily - { - get - { - if (string.IsNullOrEmpty(platformFamily)) - { - platformFamily = GetPlatformString(); - } - - return platformFamily; - } - } + public static string PlatformFamily => platformFamily ??= GetPlatformString(); /// /// Gets a that contains the resource to use. @@ -94,7 +85,7 @@ public static string PlatformFamily /// public static Stream GetResourceStream(string fileName, string resourceId) { - Stream resourceStream = null; + Stream? resourceStream; string resourceFilePath = Path.Combine(FileUtilities.GetCurrentDirectory(), Path.GetFileName(fileName)); if (File.Exists(resourceFilePath)) { @@ -125,20 +116,22 @@ public static Stream GetResourceStream(string fileName, string resourceId) private static string GetPlatformString() { - string platformName = "unknown"; if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - platformName = "windows"; + return "windows"; } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { - platformName = "linux"; + return "linux"; } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { - platformName = "mac"; + return "mac"; + } + else + { + return "unknown"; } - return platformName; } } } diff --git a/dotnet/src/webdriver/Internal/ResponseValueJsonConverter.cs b/dotnet/src/webdriver/Internal/ResponseValueJsonConverter.cs index 2732cebbd13a2..66b962b2878a8 100644 --- a/dotnet/src/webdriver/Internal/ResponseValueJsonConverter.cs +++ b/dotnet/src/webdriver/Internal/ResponseValueJsonConverter.cs @@ -22,6 +22,8 @@ using System.Text.Json; using System.Text.Json.Serialization; +#nullable enable + namespace OpenQA.Selenium.Internal { /// @@ -29,7 +31,7 @@ namespace OpenQA.Selenium.Internal /// internal class ResponseValueJsonConverter : JsonConverter { - public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override object? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return ProcessReadToken(ref reader, options); } @@ -67,19 +69,19 @@ public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOp } } - private static object ProcessReadToken(ref Utf8JsonReader reader, JsonSerializerOptions options) + private static object? ProcessReadToken(ref Utf8JsonReader reader, JsonSerializerOptions options) { // Recursively processes a token. This is required for elements that next other elements. - object processedObject; + object? processedObject; switch (reader.TokenType) { case JsonTokenType.StartObject: { - Dictionary dictionaryValue = []; + Dictionary dictionaryValue = []; while (reader.Read() && reader.TokenType != JsonTokenType.EndObject) { - string elementKey = reader.GetString(); + string elementKey = reader.GetString()!; reader.Read(); dictionaryValue.Add(elementKey, ProcessReadToken(ref reader, options)); } @@ -90,7 +92,7 @@ private static object ProcessReadToken(ref Utf8JsonReader reader, JsonSerializer case JsonTokenType.StartArray: { - List arrayValue = []; + List arrayValue = []; while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) { arrayValue.Add(ProcessReadToken(ref reader, options)); diff --git a/dotnet/src/webdriver/Internal/ReturnedCapabilities.cs b/dotnet/src/webdriver/Internal/ReturnedCapabilities.cs index 3bac423108923..2932b75273e84 100644 --- a/dotnet/src/webdriver/Internal/ReturnedCapabilities.cs +++ b/dotnet/src/webdriver/Internal/ReturnedCapabilities.cs @@ -22,13 +22,15 @@ using System.Collections.ObjectModel; using System.Globalization; +#nullable enable + namespace OpenQA.Selenium.Internal { /// /// Class to Create the capabilities of the browser you require for . /// If you wish to use default values use the static methods /// - internal class ReturnedCapabilities : ICapabilities, IHasCapabilitiesDictionary + internal sealed class ReturnedCapabilities : ICapabilities, IHasCapabilitiesDictionary { private readonly Dictionary capabilities = new Dictionary(); @@ -43,32 +45,26 @@ public ReturnedCapabilities() /// Initializes a new instance of the class /// /// Dictionary of items for the remote driver - public ReturnedCapabilities(Dictionary rawMap) + public ReturnedCapabilities(Dictionary? rawMap) { if (rawMap != null) { - foreach (string key in rawMap.Keys) + foreach (KeyValuePair rawItem in rawMap) { - this.capabilities[key] = rawMap[key]; + this.capabilities[rawItem.Key] = rawItem.Value; } } } /// - /// Gets the browser name + /// Gets the browser name, or if not specified. /// public string BrowserName { get { - string name = string.Empty; - object capabilityValue = this.GetCapability(CapabilityType.BrowserName); - if (capabilityValue != null) - { - name = capabilityValue.ToString(); - } - - return name; + object? capabilityValue = this.GetCapability(CapabilityType.BrowserName); + return capabilityValue?.ToString() ?? string.Empty; } } @@ -84,30 +80,24 @@ public object this[string capabilityName] { get { - if (!this.capabilities.ContainsKey(capabilityName)) + if (!this.capabilities.TryGetValue(capabilityName, out object? capabilityValue)) { throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "The capability {0} is not present in this set of capabilities", capabilityName)); } - return this.capabilities[capabilityName]; + return capabilityValue; } } /// /// Gets the underlying Dictionary for a given set of capabilities. /// - IDictionary IHasCapabilitiesDictionary.CapabilitiesDictionary - { - get { return this.CapabilitiesDictionary; } - } + IDictionary IHasCapabilitiesDictionary.CapabilitiesDictionary => this.CapabilitiesDictionary; /// /// Gets the internal capabilities dictionary. /// - internal IDictionary CapabilitiesDictionary - { - get { return new ReadOnlyDictionary(this.capabilities); } - } + internal IDictionary CapabilitiesDictionary => new ReadOnlyDictionary(this.capabilities); /// /// Gets a value indicating whether the browser has a given capability. @@ -125,15 +115,14 @@ public bool HasCapability(string capability) /// The capability to get. /// An object associated with the capability, or /// if the capability is not set on the browser. - public object GetCapability(string capability) + public object? GetCapability(string capability) { - object capabilityValue = null; - if (this.capabilities.ContainsKey(capability)) + if (this.capabilities.TryGetValue(capability, out object? capabilityValue)) { - capabilityValue = this.capabilities[capability]; + return capabilityValue; } - return capabilityValue; + return null; } /// From 4f07e4a4b7e4e415ad0596a3c7fcbd776cd57f57 Mon Sep 17 00:00:00 2001 From: Michael Render Date: Sun, 1 Dec 2024 16:52:39 -0500 Subject: [PATCH 16/17] [dotnet] Annotate nullable reference types in internal logging (#14819) --- .../Internal/Logging/ConsoleLogHandler.cs | 2 ++ .../Internal/Logging/FileLogHandler.cs | 8 +++-- .../webdriver/Internal/Logging/ILogContext.cs | 2 ++ .../webdriver/Internal/Logging/ILogHandler.cs | 2 ++ .../Internal/Logging/ILogHandlerList.cs | 2 ++ .../src/webdriver/Internal/Logging/ILogger.cs | 2 ++ dotnet/src/webdriver/Internal/Logging/Log.cs | 14 ++++---- .../webdriver/Internal/Logging/LogContext.cs | 17 +++++----- .../Internal/Logging/LogContextManager.cs | 32 ++++++------------- .../webdriver/Internal/Logging/LogEvent.cs | 5 ++- .../Internal/Logging/LogEventLevel.cs | 2 ++ .../Internal/Logging/LogHandlerList.cs | 4 ++- .../src/webdriver/Internal/Logging/Logger.cs | 4 ++- 13 files changed, 51 insertions(+), 45 deletions(-) diff --git a/dotnet/src/webdriver/Internal/Logging/ConsoleLogHandler.cs b/dotnet/src/webdriver/Internal/Logging/ConsoleLogHandler.cs index 32a51421bf524..30a1a36efe128 100644 --- a/dotnet/src/webdriver/Internal/Logging/ConsoleLogHandler.cs +++ b/dotnet/src/webdriver/Internal/Logging/ConsoleLogHandler.cs @@ -19,6 +19,8 @@ using System; +#nullable enable + namespace OpenQA.Selenium.Internal.Logging { /// diff --git a/dotnet/src/webdriver/Internal/Logging/FileLogHandler.cs b/dotnet/src/webdriver/Internal/Logging/FileLogHandler.cs index 8c92cf46aa3b4..7bb673486f793 100644 --- a/dotnet/src/webdriver/Internal/Logging/FileLogHandler.cs +++ b/dotnet/src/webdriver/Internal/Logging/FileLogHandler.cs @@ -20,6 +20,8 @@ using System; using System.IO; +#nullable enable + namespace OpenQA.Selenium.Internal.Logging { /// @@ -40,6 +42,7 @@ public class FileLogHandler : ILogHandler, IDisposable /// Initializes a new instance of the class with the specified file path. /// /// The path of the log file. + /// If is or . public FileLogHandler(string filePath) : this(filePath, overwrite: true) { @@ -51,6 +54,7 @@ public FileLogHandler(string filePath) /// /// The path of the log file. /// Specifies whether the file should be overwritten if it exists on the disk. + /// If is or . public FileLogHandler(string filePath, bool overwrite) { if (string.IsNullOrEmpty(filePath)) throw new ArgumentException("File log path cannot be null or empty.", nameof(filePath)); @@ -112,9 +116,9 @@ protected virtual void Dispose(bool disposing) if (disposing) { _streamWriter?.Dispose(); - _streamWriter = null; + _streamWriter = null!; _fileStream?.Dispose(); - _fileStream = null; + _fileStream = null!; } _isDisposed = true; diff --git a/dotnet/src/webdriver/Internal/Logging/ILogContext.cs b/dotnet/src/webdriver/Internal/Logging/ILogContext.cs index d1cc52cae4509..321c2bc99bd07 100644 --- a/dotnet/src/webdriver/Internal/Logging/ILogContext.cs +++ b/dotnet/src/webdriver/Internal/Logging/ILogContext.cs @@ -19,6 +19,8 @@ using System; +#nullable enable + namespace OpenQA.Selenium.Internal.Logging { /// diff --git a/dotnet/src/webdriver/Internal/Logging/ILogHandler.cs b/dotnet/src/webdriver/Internal/Logging/ILogHandler.cs index 59c8133d007b3..c7ab4cc7bb0c1 100644 --- a/dotnet/src/webdriver/Internal/Logging/ILogHandler.cs +++ b/dotnet/src/webdriver/Internal/Logging/ILogHandler.cs @@ -17,6 +17,8 @@ // under the License. // +#nullable enable + namespace OpenQA.Selenium.Internal.Logging { /// diff --git a/dotnet/src/webdriver/Internal/Logging/ILogHandlerList.cs b/dotnet/src/webdriver/Internal/Logging/ILogHandlerList.cs index f27b10a53b416..4930613c86957 100644 --- a/dotnet/src/webdriver/Internal/Logging/ILogHandlerList.cs +++ b/dotnet/src/webdriver/Internal/Logging/ILogHandlerList.cs @@ -19,6 +19,8 @@ using System.Collections.Generic; +#nullable enable + namespace OpenQA.Selenium.Internal.Logging { /// diff --git a/dotnet/src/webdriver/Internal/Logging/ILogger.cs b/dotnet/src/webdriver/Internal/Logging/ILogger.cs index a92a43e0d8445..ab2713722894b 100644 --- a/dotnet/src/webdriver/Internal/Logging/ILogger.cs +++ b/dotnet/src/webdriver/Internal/Logging/ILogger.cs @@ -19,6 +19,8 @@ using System; +#nullable enable + namespace OpenQA.Selenium.Internal.Logging { /// diff --git a/dotnet/src/webdriver/Internal/Logging/Log.cs b/dotnet/src/webdriver/Internal/Logging/Log.cs index 884e147c8659a..01b40ac2bc9fd 100644 --- a/dotnet/src/webdriver/Internal/Logging/Log.cs +++ b/dotnet/src/webdriver/Internal/Logging/Log.cs @@ -18,6 +18,9 @@ // using System; +using System.Diagnostics.CodeAnalysis; + +#nullable enable namespace OpenQA.Selenium.Internal.Logging { @@ -65,16 +68,11 @@ public static ILogContext CreateContext(LogEventLevel minimumLevel) /// /// Gets or sets the current log context. /// + [AllowNull] internal static ILogContext CurrentContext { - get - { - return _logContextManager.CurrentContext; - } - set - { - _logContextManager.CurrentContext = value; - } + get => _logContextManager.CurrentContext; + set => _logContextManager.CurrentContext = value; } /// diff --git a/dotnet/src/webdriver/Internal/Logging/LogContext.cs b/dotnet/src/webdriver/Internal/Logging/LogContext.cs index ccf902fb9b35b..bb4d9feede2c5 100644 --- a/dotnet/src/webdriver/Internal/Logging/LogContext.cs +++ b/dotnet/src/webdriver/Internal/Logging/LogContext.cs @@ -22,6 +22,8 @@ using System.Collections.Generic; using System.Linq; +#nullable enable + namespace OpenQA.Selenium.Internal.Logging { /// @@ -30,15 +32,15 @@ namespace OpenQA.Selenium.Internal.Logging /// internal class LogContext : ILogContext { - private ConcurrentDictionary _loggers; + private ConcurrentDictionary? _loggers; private LogEventLevel _level; - private readonly ILogContext _parentLogContext; + private readonly ILogContext? _parentLogContext; private readonly Lazy _lazyLogHandlerList; - public LogContext(LogEventLevel level, ILogContext parentLogContext, ConcurrentDictionary loggers, IEnumerable handlers) + public LogContext(LogEventLevel level, ILogContext? parentLogContext, ConcurrentDictionary? loggers, IEnumerable? handlers) { _level = level; @@ -63,7 +65,7 @@ public ILogContext CreateContext() public ILogContext CreateContext(LogEventLevel minimumLevel) { - ConcurrentDictionary loggers = null; + ConcurrentDictionary? loggers = null; if (_loggers != null) { @@ -89,12 +91,9 @@ public ILogger GetLogger(Type type) throw new ArgumentNullException(nameof(type)); } - if (_loggers is null) - { - _loggers = new ConcurrentDictionary(); - } + _loggers ??= new ConcurrentDictionary(); - return _loggers.GetOrAdd(type, _ => new Logger(type, _level)); + return _loggers.GetOrAdd(type, type => new Logger(type, _level)); } public bool IsEnabled(ILogger logger, LogEventLevel level) diff --git a/dotnet/src/webdriver/Internal/Logging/LogContextManager.cs b/dotnet/src/webdriver/Internal/Logging/LogContextManager.cs index 41594f1125447..834e806d8b74a 100644 --- a/dotnet/src/webdriver/Internal/Logging/LogContextManager.cs +++ b/dotnet/src/webdriver/Internal/Logging/LogContextManager.cs @@ -17,45 +17,31 @@ // under the License. // +using System.Diagnostics.CodeAnalysis; using System.Threading; +#nullable enable + namespace OpenQA.Selenium.Internal.Logging { internal class LogContextManager { - private readonly ILogContext _globalLogContext; - - private readonly AsyncLocal _currentAmbientLogContext = new AsyncLocal(); + private readonly AsyncLocal _currentAmbientLogContext = new AsyncLocal(); public LogContextManager() { var defaulConsoleLogHandler = new ConsoleLogHandler(); - _globalLogContext = new LogContext(LogEventLevel.Info, null, null, new[] { defaulConsoleLogHandler }); + GlobalContext = new LogContext(LogEventLevel.Info, null, null, new[] { defaulConsoleLogHandler }); } - public ILogContext GlobalContext - { - get { return _globalLogContext; } - } + public ILogContext GlobalContext { get; } + [AllowNull] public ILogContext CurrentContext { - get - { - if (_currentAmbientLogContext.Value is null) - { - return _globalLogContext; - } - else - { - return _currentAmbientLogContext.Value; - } - } - set - { - _currentAmbientLogContext.Value = value; - } + get => _currentAmbientLogContext.Value ?? GlobalContext; + set => _currentAmbientLogContext.Value = value; } } } diff --git a/dotnet/src/webdriver/Internal/Logging/LogEvent.cs b/dotnet/src/webdriver/Internal/Logging/LogEvent.cs index 2a4f9daaf27f9..2fad19a93dc2b 100644 --- a/dotnet/src/webdriver/Internal/Logging/LogEvent.cs +++ b/dotnet/src/webdriver/Internal/Logging/LogEvent.cs @@ -19,6 +19,8 @@ using System; +#nullable enable + namespace OpenQA.Selenium.Internal.Logging { /// @@ -33,9 +35,10 @@ public sealed class LogEvent /// The timestamp of the log event. /// The level of the log event. /// The message of the log event. + /// If is . public LogEvent(Type issuedBy, DateTimeOffset timestamp, LogEventLevel level, string message) { - IssuedBy = issuedBy; + IssuedBy = issuedBy ?? throw new ArgumentNullException(nameof(issuedBy)); Timestamp = timestamp; Level = level; Message = message; diff --git a/dotnet/src/webdriver/Internal/Logging/LogEventLevel.cs b/dotnet/src/webdriver/Internal/Logging/LogEventLevel.cs index af8b728f0d326..57090b75de4e7 100644 --- a/dotnet/src/webdriver/Internal/Logging/LogEventLevel.cs +++ b/dotnet/src/webdriver/Internal/Logging/LogEventLevel.cs @@ -17,6 +17,8 @@ // under the License. // +#nullable enable + namespace OpenQA.Selenium.Internal.Logging { /// diff --git a/dotnet/src/webdriver/Internal/Logging/LogHandlerList.cs b/dotnet/src/webdriver/Internal/Logging/LogHandlerList.cs index 7c75c36f4b2cf..9f05d3d5c52fa 100644 --- a/dotnet/src/webdriver/Internal/Logging/LogHandlerList.cs +++ b/dotnet/src/webdriver/Internal/Logging/LogHandlerList.cs @@ -19,13 +19,15 @@ using System.Collections.Generic; +#nullable enable + namespace OpenQA.Selenium.Internal.Logging { /// /// Represents a list of log handlers. /// /// - internal class LogHandlerList : List, ILogHandlerList + internal sealed class LogHandlerList : List, ILogHandlerList { private readonly ILogContext _logContext; diff --git a/dotnet/src/webdriver/Internal/Logging/Logger.cs b/dotnet/src/webdriver/Internal/Logging/Logger.cs index 0c92d0c0f299f..058d7944af153 100644 --- a/dotnet/src/webdriver/Internal/Logging/Logger.cs +++ b/dotnet/src/webdriver/Internal/Logging/Logger.cs @@ -19,13 +19,15 @@ using System; +#nullable enable + namespace OpenQA.Selenium.Internal.Logging { /// /// The implementation of the interface through which log messages are emitted. /// /// - internal class Logger : ILogger + internal sealed class Logger : ILogger { public Logger(Type issuer, LogEventLevel level) { From dd8c914110a7162511b295c145d94870997301ad Mon Sep 17 00:00:00 2001 From: Viet Nguyen Duc Date: Mon, 2 Dec 2024 20:11:04 +0700 Subject: [PATCH 17/17] [py] Update README command install source distribution from PyPI Follow https://packaging.python.org/en/latest/discussions/setup-py-deprecated/ --- py/docs/source/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/docs/source/index.rst b/py/docs/source/index.rst index 54123b5d600ca..9f8c9ed892d56 100755 --- a/py/docs/source/index.rst +++ b/py/docs/source/index.rst @@ -35,7 +35,7 @@ If you have `pip `_ on your system, you can simply install Alternately, you can download the source distribution from `PyPI `, unarchive it, and run:: - python setup.py install + python -m pip install . Note: You may want to consider using `virtualenv `_ to create isolated Python environments.