From 209e275840956e7005e7864ea12b9bac559303c2 Mon Sep 17 00:00:00 2001 From: Michael Render Date: Sat, 30 Nov 2024 16:52:12 -0500 Subject: [PATCH 1/4] [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 2/4] [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 3/4] [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 4/4] [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) {