Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[dotnet] Annotate nullability on platform-specific WebDrivers #15236

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion dotnet/src/webdriver/Chrome/ChromeDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;

#nullable enable

namespace OpenQA.Selenium.Chrome
{
/// <summary>
Expand Down Expand Up @@ -60,7 +62,7 @@ namespace OpenQA.Selenium.Chrome
/// </example>
public class ChromeDriver : ChromiumDriver
{
private static Dictionary<string, CommandInfo> chromeCustomCommands = new Dictionary<string, CommandInfo>()
private static readonly Dictionary<string, CommandInfo> chromeCustomCommands = new Dictionary<string, CommandInfo>()
{
{ ExecuteCdp, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/goog/cdp/execute") },
{ GetCastSinksCommand, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/goog/cast/get_sinks") },
Expand All @@ -83,6 +85,7 @@ public ChromeDriver()
/// Initializes a new instance of the <see cref="ChromeDriver"/> class using the specified options.
/// </summary>
/// <param name="options">The <see cref="ChromeOptions"/> to be used with the Chrome driver.</param>
/// <exception cref="ArgumentNullException">If <paramref name="options"/> is <see langword="null"/>.</exception>
public ChromeDriver(ChromeOptions options)
: this(ChromeDriverService.CreateDefaultService(), options, RemoteWebDriver.DefaultCommandTimeout)
{
Expand All @@ -92,6 +95,7 @@ public ChromeDriver(ChromeOptions options)
/// Initializes a new instance of the <see cref="ChromeDriver"/> class using the specified driver service.
/// </summary>
/// <param name="service">The <see cref="ChromeDriverService"/> used to initialize the driver.</param>
/// <exception cref="ArgumentNullException">If <paramref name="service"/> is <see langword="null"/>.</exception>
public ChromeDriver(ChromeDriverService service)
: this(service, new ChromeOptions())
{
Expand All @@ -113,6 +117,7 @@ public ChromeDriver(string chromeDriverDirectory)
/// </summary>
/// <param name="chromeDriverDirectory">The full path to the directory containing ChromeDriver.exe.</param>
/// <param name="options">The <see cref="ChromeOptions"/> to be used with the Chrome driver.</param>
/// <exception cref="ArgumentNullException">If <paramref name="options"/> is <see langword="null"/>.</exception>
public ChromeDriver(string chromeDriverDirectory, ChromeOptions options)
: this(chromeDriverDirectory, options, RemoteWebDriver.DefaultCommandTimeout)
{
Expand All @@ -125,6 +130,7 @@ public ChromeDriver(string chromeDriverDirectory, ChromeOptions options)
/// <param name="chromeDriverDirectory">The full path to the directory containing ChromeDriver.exe.</param>
/// <param name="options">The <see cref="ChromeOptions"/> to be used with the Chrome driver.</param>
/// <param name="commandTimeout">The maximum amount of time to wait for each command.</param>
/// <exception cref="ArgumentNullException">If <paramref name="options"/> is <see langword="null"/>.</exception>
public ChromeDriver(string chromeDriverDirectory, ChromeOptions options, TimeSpan commandTimeout)
: this(ChromeDriverService.CreateDefaultService(chromeDriverDirectory), options, commandTimeout)
{
Expand All @@ -136,6 +142,7 @@ public ChromeDriver(string chromeDriverDirectory, ChromeOptions options, TimeSpa
/// </summary>
/// <param name="service">The <see cref="ChromeDriverService"/> to use.</param>
/// <param name="options">The <see cref="ChromeOptions"/> used to initialize the driver.</param>
/// <exception cref="ArgumentNullException">If <paramref name="service"/> or <paramref name="options"/> are <see langword="null"/>.</exception>
public ChromeDriver(ChromeDriverService service, ChromeOptions options)
: this(service, options, RemoteWebDriver.DefaultCommandTimeout)
{
Expand All @@ -147,6 +154,7 @@ public ChromeDriver(ChromeDriverService service, ChromeOptions options)
/// <param name="service">The <see cref="ChromeDriverService"/> to use.</param>
/// <param name="options">The <see cref="ChromeOptions"/> to be used with the Chrome driver.</param>
/// <param name="commandTimeout">The maximum amount of time to wait for each command.</param>
/// <exception cref="ArgumentNullException">If <paramref name="service"/> or <paramref name="options"/> are <see langword="null"/>.</exception>
public ChromeDriver(ChromeDriverService service, ChromeOptions options, TimeSpan commandTimeout)
: base(service, options, commandTimeout)
{
Expand Down
78 changes: 48 additions & 30 deletions dotnet/src/webdriver/Chromium/ChromiumDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Threading.Tasks;

#nullable enable

namespace OpenQA.Selenium.Chromium
{
/// <summary>
Expand Down Expand Up @@ -108,9 +111,9 @@ public class ChromiumDriver : WebDriver, ISupportsLogs, IDevTools
public static readonly string SetPermissionCommand = "setPermission";

private readonly string optionsCapabilityName;
private DevToolsSession devToolsSession;
private DevToolsSession? devToolsSession;

private static Dictionary<string, CommandInfo> chromiumCustomCommands = new Dictionary<string, CommandInfo>()
private static readonly Dictionary<string, CommandInfo> chromiumCustomCommands = new Dictionary<string, CommandInfo>()
{
{ GetNetworkConditionsCommand, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/chromium/network_conditions") },
{ SetNetworkConditionsCommand, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/chromium/network_conditions") },
Expand All @@ -127,19 +130,18 @@ public class ChromiumDriver : WebDriver, ISupportsLogs, IDevTools
/// <param name="service">The <see cref="ChromiumDriverService"/> to use.</param>
/// <param name="options">The <see cref="ChromiumOptions"/> to be used with the ChromiumDriver.</param>
/// <param name="commandTimeout">The maximum amount of time to wait for each command.</param>
/// <exception cref="ArgumentNullException">If <paramref name="service"/> or <paramref name="options"/> are <see langword="null"/>.</exception>
/// <exception cref="ArgumentException">If the Chromium options capability name is <see langword="null"/>.</exception>
protected ChromiumDriver(ChromiumDriverService service, ChromiumOptions options, TimeSpan commandTimeout)
: base(GenerateDriverServiceCommandExecutor(service, options, commandTimeout), ConvertOptionsToCapabilities(options))
{
this.optionsCapabilityName = options.CapabilityName;
this.optionsCapabilityName = options.CapabilityName ?? throw new ArgumentException("No chromium options capability name specified", nameof(options));
}

/// <summary>
/// Gets the dictionary of custom Chromium commands registered with the driver.
/// </summary>
protected static IReadOnlyDictionary<string, CommandInfo> ChromiumCustomCommands
{
get { return new ReadOnlyDictionary<string, CommandInfo>(chromiumCustomCommands); }
}
protected static IReadOnlyDictionary<string, CommandInfo> ChromiumCustomCommands => new ReadOnlyDictionary<string, CommandInfo>(chromiumCustomCommands);

/// <summary>
/// Uses DriverFinder to set Service attributes if necessary when creating the command executor
Expand All @@ -150,6 +152,16 @@ protected static IReadOnlyDictionary<string, CommandInfo> ChromiumCustomCommands
/// <returns></returns>
private static ICommandExecutor GenerateDriverServiceCommandExecutor(DriverService service, DriverOptions options, TimeSpan commandTimeout)
{
if (service is null)
{
throw new ArgumentNullException(nameof(service));
}

if (options is null)
{
throw new ArgumentNullException(nameof(options));
}

if (service.DriverServicePath == null)
{
DriverFinder finder = new DriverFinder(options);
Expand Down Expand Up @@ -177,27 +189,31 @@ private static ICommandExecutor GenerateDriverServiceCommandExecutor(DriverServi
/// in conjunction with a standalone WebDriver server.</remarks>
public override IFileDetector FileDetector
{
get { return base.FileDetector; }
get => base.FileDetector;
set { }
}

/// <summary>
/// Gets a value indicating whether a DevTools session is active.
/// </summary>
public bool HasActiveDevToolsSession
{
get { return this.devToolsSession != null; }
}
[MemberNotNullWhen(true, nameof(devToolsSession))]
public bool HasActiveDevToolsSession => this.devToolsSession != null;

/// <summary>
/// Gets or sets the network condition emulation for Chromium.
/// </summary>
/// <exception cref="ArgumentNullException">If the value is set to <see langword="null"/>.</exception>
public ChromiumNetworkConditions NetworkConditions
{
get
{
Response response = this.Execute(GetNetworkConditionsCommand, null);
return ChromiumNetworkConditions.FromDictionary(response.Value as Dictionary<string, object>);
if (response.Value is not Dictionary<string, object?> responseDictionary)
{
throw new WebDriverException($"GetNetworkConditions command returned successfully, but data was not an object: {response.Value}");
}

return ChromiumNetworkConditions.FromDictionary(responseDictionary);
}

set
Expand All @@ -209,6 +225,7 @@ public ChromiumNetworkConditions NetworkConditions

Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters["network_conditions"] = value;

this.Execute(SetNetworkConditionsCommand, parameters);
}
}
Expand All @@ -217,6 +234,7 @@ public ChromiumNetworkConditions NetworkConditions
/// Launches a Chromium based application.
/// </summary>
/// <param name="id">ID of the chromium app to launch.</param>
/// <exception cref="ArgumentNullException">If <paramref name="id"/> is <see langword="null"/>.</exception>
public void LaunchApp(string id)
{
if (id == null)
Expand All @@ -226,6 +244,7 @@ public void LaunchApp(string id)

Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters["id"] = id;

this.Execute(LaunchAppCommand, parameters);
}

Expand All @@ -234,6 +253,7 @@ public void LaunchApp(string id)
/// </summary>
/// <param name="permissionName">Name of item to set the permission on.</param>
/// <param name="permissionValue">Value to set the permission to.</param>
/// <exception cref="ArgumentNullException">If <paramref name="permissionName"/> or <paramref name="permissionValue"/> are <see langword="null"/>.</exception>
public void SetPermission(string permissionName, string permissionValue)
{
if (permissionName == null)
Expand All @@ -260,7 +280,8 @@ public void SetPermission(string permissionName, string permissionValue)
/// <param name="commandName">Name of the command to execute.</param>
/// <param name="commandParameters">Parameters of the command to execute.</param>
/// <returns>An object representing the result of the command, if applicable.</returns>
public object ExecuteCdpCommand(string commandName, Dictionary<string, object> commandParameters)
/// <exception cref="ArgumentNullException">If <paramref name="commandName"/> is <see langword="null"/>.</exception>
public object? ExecuteCdpCommand(string commandName, Dictionary<string, object> commandParameters)
{
if (commandName == null)
{
Expand Down Expand Up @@ -296,21 +317,20 @@ public DevToolsSession GetDevToolsSession(DevToolsOptions options)
throw new WebDriverException("Cannot find " + this.optionsCapabilityName + " capability for driver");
}

Dictionary<string, object> optionsCapability = this.Capabilities.GetCapability(this.optionsCapabilityName) as Dictionary<string, object>;
if (optionsCapability == null)
object? optionsCapabilityObject = this.Capabilities.GetCapability(this.optionsCapabilityName);
if (optionsCapabilityObject is not Dictionary<string, object?> optionsCapability)
{
throw new WebDriverException("Found " + this.optionsCapabilityName + " capability, but is not an object");
throw new WebDriverException($"Found {this.optionsCapabilityName} capability, but is not an object: {optionsCapabilityObject}");
}

if (!optionsCapability.ContainsKey("debuggerAddress"))
if (!optionsCapability.TryGetValue("debuggerAddress", out object? debuggerAddress))
{
throw new WebDriverException("Did not find debuggerAddress capability in " + this.optionsCapabilityName);
}

string debuggerAddress = optionsCapability["debuggerAddress"].ToString();
try
{
DevToolsSession session = new DevToolsSession(debuggerAddress, options);
DevToolsSession session = new DevToolsSession(debuggerAddress?.ToString(), options);
Task.Run(async () => await session.StartSession()).GetAwaiter().GetResult();
this.devToolsSession = session;
}
Expand Down Expand Up @@ -341,7 +361,7 @@ public void CloseDevToolsSession()
{
if (this.devToolsSession != null)
{
Task.Run(async () => await this.devToolsSession.StopSession(true)).GetAwaiter().GetResult();
Task.Run(async () => await this.devToolsSession.StopSession(manualDetach: true)).GetAwaiter().GetResult();
}
}

Expand All @@ -361,18 +381,16 @@ public List<Dictionary<string, string>> GetCastSinks()
{
List<Dictionary<string, string>> returnValue = new List<Dictionary<string, string>>();
Response response = this.Execute(GetCastSinksCommand, null);
object[] responseValue = response.Value as object[];
if (responseValue != null)
if (response.Value is object?[] responseValue)
{
foreach (object entry in responseValue)
foreach (object? entry in responseValue)
{
Dictionary<string, object> entryValue = entry as Dictionary<string, object>;
if (entryValue != null)
if (entry is Dictionary<string, object?> entryValue)
{
Dictionary<string, string> sink = new Dictionary<string, string>();
foreach (KeyValuePair<string, object> pair in entryValue)
foreach (KeyValuePair<string, object?> pair in entryValue)
{
sink[pair.Key] = pair.Value.ToString();
sink[pair.Key] = pair.Value!.ToString()!;
}

returnValue.Add(sink);
Expand Down Expand Up @@ -434,10 +452,10 @@ public void StartDesktopMirroring(string deviceName)
/// Returns the error message if there is any issue in a Cast session.
/// </summary>
/// <returns>An error message.</returns>
public String GetCastIssueMessage()
public string? GetCastIssueMessage()
{
Response response = this.Execute(GetCastIssueMessageCommand, null);
return (string)response.Value;
return (string?)response.Value;
}

/// <summary>
Expand Down
8 changes: 4 additions & 4 deletions dotnet/src/webdriver/Chromium/ChromiumNetworkConditions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,12 @@ public long UploadThroughput
/// </summary>
/// <param name="dictionary">The dictionary to use to create the object.</param>
/// <returns>The ChromiumNetworkConditions object created from the dictionary.</returns>
public static ChromiumNetworkConditions FromDictionary(Dictionary<string, object> dictionary)
public static ChromiumNetworkConditions FromDictionary(Dictionary<string, object?> dictionary)
{
ChromiumNetworkConditions conditions = new ChromiumNetworkConditions();
if (dictionary.TryGetValue("offline", out object? offline))
{
conditions.IsOffline = (bool)offline;
conditions.IsOffline = (bool)offline!;
}

if (dictionary.TryGetValue("latency", out object? latency))
Expand All @@ -106,12 +106,12 @@ public static ChromiumNetworkConditions FromDictionary(Dictionary<string, object

if (dictionary.TryGetValue("upload_throughput", out object? uploadThroughput))
{
conditions.UploadThroughput = (long)uploadThroughput;
conditions.UploadThroughput = (long)uploadThroughput!;
}

if (dictionary.TryGetValue("download_throughput", out object? downloadThroughput))
{
conditions.DownloadThroughput = (long)downloadThroughput;
conditions.DownloadThroughput = (long)downloadThroughput!;
}

return conditions;
Expand Down
Loading
Loading