Skip to content

Commit

Permalink
[dotnet] Making SeleniumManager a thin wrapper (#13833)
Browse files Browse the repository at this point in the history
* [dotnet] Making SeleniumManager a thin wrapper

Also moving things to the driver classes and
DriverFinder.

Logging warnings from Selenium Manager as well,
and debug messages when that level is enabled.

* [dotnet] Addressing PR comments
  • Loading branch information
diemol authored Apr 18, 2024
1 parent aa62f10 commit f36b334
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 67 deletions.
8 changes: 7 additions & 1 deletion dotnet/src/webdriver/Chromium/ChromiumDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,15 @@ private static ICommandExecutor GenerateDriverServiceCommandExecutor(DriverServi
{
if (service.DriverServicePath == null)
{
string fullServicePath = DriverFinder.FullPath(options);
DriverFinder finder = new DriverFinder(options);
string fullServicePath = finder.DriverPath();
service.DriverServicePath = Path.GetDirectoryName(fullServicePath);
service.DriverServiceExecutableName = Path.GetFileName(fullServicePath);
if (finder.HasBrowserPath())
{
options.BinaryLocation = finder.BrowserPath();
options.BrowserVersion = null;
}
}
return new DriverServiceCommandExecutor(service, commandTimeout);
}
Expand Down
120 changes: 100 additions & 20 deletions dotnet/src/webdriver/DriverFinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,51 +17,131 @@
// </copyright>

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;

namespace OpenQA.Selenium
{
/// <summary>
/// Finds a driver, checks if the provided path exists, if not, Selenium Manager is used.
/// This implementation is still in beta, and may change.
/// This implementation is still in beta and may change.
/// </summary>
public static class DriverFinder
public class DriverFinder
{
private DriverOptions options;
private Dictionary<string, string> paths = new Dictionary<string, string>();
private const string BrowserPathKey = "browser_path";
private const string DriverPathKey = "driver_path";

/// <summary>
/// Initializes a new instance of the <see cref="DriverFinder"/> class.
/// </summary>
public DriverFinder(DriverOptions options)
{
this.options = options;
}

/// <summary>
/// Use Selenium Manager to locate the driver
/// Gets the browser path retrieved by Selenium Manager
/// </summary>
/// <param name="options">DriverOptions with the current browser options.</param>
/// <returns>
/// The full path and name of the driver
/// The full browser path
/// </returns>
/// <exception cref="NoSuchDriverException"></exception>
public static string FullPath(DriverOptions options)
public string BrowserPath()
{
return BinaryPaths()[BrowserPathKey];
}

/// <summary>
/// Gets the driver path retrieved by Selenium Manager
/// </summary>
/// <returns>
/// The full driver path
/// </returns>
public string DriverPath()
{
return BinaryPaths()[DriverPathKey];
}

public bool HasBrowserPath()
{
return !string.IsNullOrWhiteSpace(BrowserPath());
}

/// <summary>
/// Invokes Selenium Manager to get the binaries paths and validates if they exist.
/// </summary>
/// <returns>
/// A Dictionary with the validated browser and driver path.
/// </returns>
/// <exception cref="NoSuchDriverException">If one of the paths does not exist.</exception>
private Dictionary<string, string> BinaryPaths()
{
string executablePath;
try
if (paths.ContainsKey(DriverPathKey) && !string.IsNullOrWhiteSpace(paths[DriverPathKey]))
{
executablePath = SeleniumManager.DriverPath(options);
return paths;
}
catch (Exception e)
Dictionary<string, string> binaryPaths = SeleniumManager.BinaryPaths(CreateArguments());
string driverPath = binaryPaths[DriverPathKey];
string browserPath = binaryPaths[BrowserPathKey];
if (File.Exists(driverPath))
{
throw new NoSuchDriverException($"Unable to obtain {options.BrowserName} using Selenium Manager", e);
paths.Add(DriverPathKey, driverPath);
}

string message;
if (executablePath == null)
else
{
message = $"Unable to locate or obtain {options.BrowserName} driver";
throw new NoSuchDriverException($"The driver path is not a valid file: {driverPath}");
}
else if (!File.Exists(executablePath))
if (File.Exists(browserPath))
{
message = $"{options.BrowserName} driver located at {executablePath}, but invalid";
paths.Add(BrowserPathKey, browserPath);
}
else
{
return executablePath;
throw new NoSuchDriverException($"The browser path is not a valid file: {browserPath}");
}
return paths;
}

/// <summary>
/// Create arguments to invoke Selenium Manager
/// </summary>
/// <returns>
/// A string with all arguments to invoke Selenium Manager
/// </returns>
/// <exception cref="NoSuchDriverException"></exception>
private string CreateArguments()
{
StringBuilder argsBuilder = new StringBuilder();
argsBuilder.AppendFormat(CultureInfo.InvariantCulture, " --browser \"{0}\"", options.BrowserName);

if (!string.IsNullOrEmpty(options.BrowserVersion))
{
argsBuilder.AppendFormat(CultureInfo.InvariantCulture, " --browser-version {0}", options.BrowserVersion);
}

throw new NoSuchDriverException(message);
string browserBinary = options.BinaryLocation;
if (!string.IsNullOrEmpty(browserBinary))
{
argsBuilder.AppendFormat(CultureInfo.InvariantCulture, " --browser-path \"{0}\"", browserBinary);
}

if (options.Proxy != null)
{
if (options.Proxy.SslProxy != null)
{
argsBuilder.AppendFormat(CultureInfo.InvariantCulture, " --proxy \"{0}\"", options.Proxy.SslProxy);
}
else if (options.Proxy.HttpProxy != null)
{
argsBuilder.AppendFormat(CultureInfo.InvariantCulture, " --proxy \"{0}\"", options.Proxy.HttpProxy);
}
}

return argsBuilder.ToString();
}

}
}
2 changes: 1 addition & 1 deletion dotnet/src/webdriver/DriverService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ public void Start()
}
else
{
this.driverServiceProcess.StartInfo.FileName = DriverFinder.FullPath(this.GetDefaultDriverOptions());
this.driverServiceProcess.StartInfo.FileName = new DriverFinder(this.GetDefaultDriverOptions()).DriverPath();
}

this.driverServiceProcess.StartInfo.Arguments = this.CommandLineArguments;
Expand Down
8 changes: 7 additions & 1 deletion dotnet/src/webdriver/Firefox/FirefoxDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,15 @@ private static ICommandExecutor GenerateDriverServiceCommandExecutor(DriverServi
{
if (service.DriverServicePath == null)
{
string fullServicePath = DriverFinder.FullPath(options);
DriverFinder finder = new DriverFinder(options);
string fullServicePath = finder.DriverPath();
service.DriverServicePath = Path.GetDirectoryName(fullServicePath);
service.DriverServiceExecutableName = Path.GetFileName(fullServicePath);
if (finder.HasBrowserPath())
{
options.BinaryLocation = finder.BrowserPath();
options.BrowserVersion = null;
}
}
return new DriverServiceCommandExecutor(service, commandTimeout);
}
Expand Down
3 changes: 2 additions & 1 deletion dotnet/src/webdriver/IE/InternetExplorerDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,8 @@ private static ICommandExecutor GenerateDriverServiceCommandExecutor(DriverServi
{
if (service.DriverServicePath == null)
{
string fullServicePath = DriverFinder.FullPath(options);
DriverFinder finder = new DriverFinder(options);
string fullServicePath = finder.DriverPath();
service.DriverServicePath = Path.GetDirectoryName(fullServicePath);
service.DriverServiceExecutableName = Path.GetFileName(fullServicePath);
}
Expand Down
3 changes: 2 additions & 1 deletion dotnet/src/webdriver/Safari/SafariDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,8 @@ private static ICommandExecutor GenerateDriverServiceCommandExecutor(DriverServi
{
if (service.DriverServicePath == null)
{
string fullServicePath = DriverFinder.FullPath(options);
DriverFinder finder = new DriverFinder(options);
string fullServicePath = finder.DriverPath();
service.DriverServicePath = Path.GetDirectoryName(fullServicePath);
service.DriverServiceExecutableName = Path.GetFileName(fullServicePath);
}
Expand Down
77 changes: 35 additions & 42 deletions dotnet/src/webdriver/SeleniumManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,62 +71,34 @@ static SeleniumManager()
}

/// <summary>
/// Determines the location of the correct driver.
/// Determines the location of the browser and driver binaries.
/// </summary>
/// <param name="options">The correct path depends on which options are being used.</param>
/// <param name="arguments">List of arguments to use when invoking Selenium Manager.</param>
/// <returns>
/// The location of the driver.
/// An array with two entries, one for the driver path, and another one for the browser path.
/// </returns>
public static string DriverPath(DriverOptions options)
public static Dictionary<string, string> BinaryPaths(string arguments)
{
StringBuilder argsBuilder = new StringBuilder();
argsBuilder.AppendFormat(CultureInfo.InvariantCulture, " --browser \"{0}\"", options.BrowserName);
StringBuilder argsBuilder = new StringBuilder(arguments);
argsBuilder.Append(" --language-binding csharp");
argsBuilder.Append(" --output json");

if (!string.IsNullOrEmpty(options.BrowserVersion))
{
argsBuilder.AppendFormat(CultureInfo.InvariantCulture, " --browser-version {0}", options.BrowserVersion);
}

string browserBinary = options.BinaryLocation;
if (!string.IsNullOrEmpty(browserBinary))
if (_logger.IsEnabled(LogEventLevel.Debug))
{
argsBuilder.AppendFormat(CultureInfo.InvariantCulture, " --browser-path \"{0}\"", browserBinary);
}

if (options.Proxy != null)
{
if (options.Proxy.SslProxy != null)
{
argsBuilder.AppendFormat(CultureInfo.InvariantCulture, " --proxy \"{0}\"", options.Proxy.SslProxy);
}
else if (options.Proxy.HttpProxy != null)
{
argsBuilder.AppendFormat(CultureInfo.InvariantCulture, " --proxy \"{0}\"", options.Proxy.HttpProxy);
}
argsBuilder.Append(" --debug");
}

Dictionary<string, object> output = RunCommand(BinaryFullPath, argsBuilder.ToString());

try
{
options.BinaryLocation = (string)output["browser_path"] == "" ? null : (string)output["browser_path"];
options.BrowserVersion = null;
}
catch (NotImplementedException)
{
// Cannot set Browser Location for this driver and that is ok
}

var driverPath = (string)output["driver_path"];
Dictionary<string, string> binaryPaths = new Dictionary<string, string>();
binaryPaths.Add("browser_path", (string)output["browser_path"]);
binaryPaths.Add("driver_path", (string)output["driver_path"]);

if (_logger.IsEnabled(LogEventLevel.Trace))
{
_logger.Trace($"Driver path: {driverPath}");
_logger.Trace($"Driver path: {binaryPaths["driver_path"]}");
_logger.Trace($"Browser path: {binaryPaths["browser_path"]}");
}

return driverPath;
return binaryPaths;
}

/// <summary>
Expand Down Expand Up @@ -169,7 +141,7 @@ private static Dictionary<string, object> RunCommand(string fileName, string arg

if (process.ExitCode != 0)
{
// We do not log any warnings coming from Selenium Manager like the other bindings as we don't have any logging in the .NET bindings
// We do not log any warnings coming from Selenium Manager like the other bindings, as we don't have any logging in the .NET bindings

var exceptionMessageBuilder = new StringBuilder($"Selenium Manager process exited abnormally with {process.ExitCode} code: {fileName} {arguments}");

Expand Down Expand Up @@ -214,6 +186,27 @@ private static Dictionary<string, object> RunCommand(string fileName, string arg
throw new WebDriverException($"Error deserializing Selenium Manager's response: {output}", ex);
}

if (result.ContainsKey("logs"))
{
Dictionary<string, string> logs = result["logs"] as Dictionary<string, string>;
foreach (KeyValuePair<string, string> entry in logs)
{
switch (entry.Key)
{
case "WARN":
_logger.Warn(entry.Value);
break;
case "DEBUG":
case "INFO":
if (_logger.IsEnabled(LogEventLevel.Debug) && (entry.Key == "DEBUG" || entry.Key == "INFO"))
{
_logger.Debug(entry.Value);
}
break;
}
}
}

return result;
}
}
Expand Down

0 comments on commit f36b334

Please sign in to comment.