blob: 4652e5ebc32288a97dbe532a6e6924c951a4e15d [file]
// <copyright file="EnvironmentManager.cs" company="Selenium Committers">
// 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.
// </copyright>
using System.IO;
using System.Runtime.InteropServices;
using Bazel;
using Microsoft.Extensions.Configuration;
namespace OpenQA.Selenium.Testing.NUnit.Environment;
public class EnvironmentManager
{
private static readonly Lazy<EnvironmentManager> instance = new(() => new EnvironmentManager());
private static readonly Dictionary<Browser, string> driverPathEnvironmentVariables = new()
{
[Browser.Chrome] = "SE_CHROMEDRIVER",
[Browser.Edge] = "SE_EDGEDRIVER",
[Browser.Firefox] = "SE_GECKODRIVER",
[Browser.IE] = "SE_IEDRIVER",
[Browser.Safari] = "SE_SAFARIDRIVER",
};
private EnvironmentManager()
{
string appConfigFilePath;
Runfiles runfiles = null;
try
{
runfiles = Runfiles.Create();
appConfigFilePath = runfiles.Rlocation("_main/dotnet/test/testing.nunit/appconfig.json");
}
catch (FileNotFoundException)
{
appConfigFilePath = "appconfig.json";
}
string currentDirectory = this.CurrentDirectory;
string configurationPath = Path.GetFullPath(appConfigFilePath);
IConfigurationRoot configuration = new ConfigurationBuilder()
.SetBasePath(Path.GetDirectoryName(configurationPath))
.AddJsonFile(Path.GetFileName(configurationPath), optional: false, reloadOnChange: false)
.AddEnvironmentVariables()
.AddNUnitParameters()
.Build();
TestEnvironment env = new();
configuration.Bind(env);
// Bazel's $(location ...) expands to a runfiles-relative path (e.g.
// "../+pin_browsers_extension+mac_chrome/Chrome.app/Contents/MacOS/Chrome")
// which is relative to <runfiles>/_main. The driver process runs from a
// different cwd and cannot resolve that, so absolutize through TEST_SRCDIR.
string driverServiceLocation = ResolveRunfilesPath(env.DriverServiceLocation);
string browserLocation = ResolveRunfilesPath(env.BrowserLocation);
DriverConfig driverConfig = env.DriverConfigs[env.ActiveDriverConfig];
Configuration = env;
env.DriverServiceLocation = driverServiceLocation;
BrowserLocation = browserLocation;
Browser = driverConfig.BrowserValue;
RemoteCapabilities = driverConfig.RemoteCapabilities;
// Services the tests build themselves then resolve the pinned driver instead of invoking Selenium Manager.
if (!string.IsNullOrEmpty(driverServiceLocation)
&& driverPathEnvironmentVariables.TryGetValue(Browser, out string driverVariable))
{
System.Environment.SetEnvironmentVariable(driverVariable, driverServiceLocation);
}
WebServer = new AppServer();
WebServer.StartAsync().GetAwaiter().GetResult();
// Find selenium-manager binary.
try
{
string managerFilePath = "";
runfiles ??= Runfiles.Create();
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
managerFilePath = runfiles.Rlocation("_main/dotnet/src/webdriver/manager/windows/selenium-manager.exe");
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
var linuxArch = RuntimeInformation.ProcessArchitecture == Architecture.Arm64 ? "arm64" : "x86_64";
managerFilePath = runfiles.Rlocation($"_main/dotnet/src/webdriver/manager/linux-{linuxArch}/selenium-manager");
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
managerFilePath = runfiles.Rlocation("_main/dotnet/src/webdriver/manager/macos/selenium-manager");
}
System.Environment.SetEnvironmentVariable("SE_MANAGER_PATH", managerFilePath);
}
catch (FileNotFoundException)
{
// Use the default one.
}
string projectRoot = System.Environment.GetEnvironmentVariable("TEST_SRCDIR");
if (!string.IsNullOrEmpty(projectRoot))
{
projectRoot += "/_main";
}
else
{
projectRoot = FindProjectRoot(currentDirectory);
}
bool autoStartRemoteServer = false;
if (Browser == Browser.Remote)
{
autoStartRemoteServer = driverConfig.AutoStartRemoteServer;
}
RemoteServer = new RemoteSeleniumServer(projectRoot, autoStartRemoteServer);
}
~EnvironmentManager()
{
RemoteServer?.StopAsync().Wait();
WebServer?.StopAsync().Wait();
}
public static EnvironmentManager Instance => instance.Value;
public Browser Browser { get; }
internal TestEnvironment Configuration { get; }
public string BrowserLocation { get; }
public string CurrentDirectory
{
get
{
string assemblyLocation = Path.GetDirectoryName(typeof(EnvironmentManager).Assembly.Location);
string testDirectory = TestContext.CurrentContext.TestDirectory;
if (assemblyLocation != testDirectory)
{
return assemblyLocation;
}
return testDirectory;
}
}
public AppServer WebServer { get; }
public RemoteSeleniumServer RemoteServer { get; }
public string RemoteCapabilities { get; }
private static string FindProjectRoot(string startDirectory)
{
// Walk up until we find a directory containing common/ and dotnet/
DirectoryInfo info = new DirectoryInfo(startDirectory);
while (info is not null && info != info.Root)
{
if (Directory.Exists(Path.Combine(info.FullName, "common"))
&& Directory.Exists(Path.Combine(info.FullName, "dotnet")))
{
return info.FullName;
}
info = info.Parent;
}
return startDirectory;
}
private static string ResolveRunfilesPath(string path)
{
if (string.IsNullOrEmpty(path) || Path.IsPathRooted(path))
{
return path;
}
string srcdir = System.Environment.GetEnvironmentVariable("TEST_SRCDIR");
if (string.IsNullOrEmpty(srcdir))
{
return path;
}
// Bazel $(location ...) emits paths relative to <runfiles>/_main.
return Path.GetFullPath(Path.Combine(srcdir, "_main", path));
}
}