From 6b2463665b02ed58b7d36be51584d65937ef5b94 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Sun, 3 Dec 2023 23:36:09 +0300 Subject: [PATCH] [dotnet] Internal logging (#13140) * Update HttpCommandExecutor.cs * Nullable handlers * Don't capture logger * Log message issuer * Update nunit adapter to work with dotnet 7 and be more friendly with windows * Introduce LogContextManager * Make ILogger as static field * Support hierarchical contexts * Do not emit message to parent context * Deep copy of loggers and handlers per context * Static works with current log context * Create context with minimum level * Set minimum level for context * Set minimum level per issuer * Use DateTimeOffset * Docs for everything * Info minimum log level by default * Adjust log levels in console output * Make webdriver assembly internals visible to tests * Make ILogger hidden from user * Console handler by default * Output logs to stderr * New api to mange log handlers * Verbose driver creation in tests * Search driver type in loaded assemblies * Declare internals visible to in csproj to not conflict with bazel * Clean specific assembly name for driver type * Fix targeting packs for test targets --- dotnet/src/webdriver/BUILD.bazel | 5 + dotnet/src/webdriver/Chrome/ChromeDriver.cs | 1 + .../Internal/Logging/ConsoleLogHandler.cs | 49 ++++ .../webdriver/Internal/Logging/ILogContext.cs | 83 +++++++ .../webdriver/Internal/Logging/ILogHandler.cs | 38 ++++ .../Internal/Logging/ILogHandlerList.cs | 48 ++++ .../src/webdriver/Internal/Logging/ILogger.cs | 68 ++++++ dotnet/src/webdriver/Internal/Logging/Log.cs | 125 ++++++++++ .../webdriver/Internal/Logging/LogContext.cs | 143 ++++++++++++ .../Internal/Logging/LogContextManager.cs | 60 +++++ .../webdriver/Internal/Logging/LogEvent.cs | 63 +++++ .../Internal/Logging/LogEventLevel.cs | 56 +++++ .../Internal/Logging/LogHandlerList.cs | 57 +++++ .../src/webdriver/Internal/Logging/Logger.cs | 69 ++++++ .../webdriver/Remote/HttpCommandExecutor.cs | 13 ++ dotnet/src/webdriver/SeleniumManager.cs | 9 +- dotnet/src/webdriver/WebDriver.csproj | 3 + .../test/chrome/WebDriver.Chrome.Tests.csproj | 4 +- dotnet/test/common/BUILD.bazel | 1 + .../test/common/Environment/DriverConfig.cs | 8 - .../test/common/Environment/DriverFactory.cs | 2 + .../common/Environment/EnvironmentManager.cs | 18 +- .../test/common/Environment/TestWebServer.cs | 15 +- .../test/common/Internal/Logging/LogTest.cs | 215 ++++++++++++++++++ .../test/common/WebDriver.Common.Tests.csproj | 4 +- dotnet/test/common/appconfig.json | 10 - dotnet/test/edge/WebDriver.Edge.Tests.csproj | 4 +- .../firefox/WebDriver.Firefox.Tests.csproj | 4 +- dotnet/test/ie/WebDriver.IE.Tests.csproj | 4 +- .../test/remote/WebDriver.Remote.Tests.csproj | 4 +- .../test/safari/WebDriver.Safari.Tests.csproj | 4 +- .../support/WebDriver.Support.Tests.csproj | 4 +- 32 files changed, 1146 insertions(+), 45 deletions(-) create mode 100644 dotnet/src/webdriver/Internal/Logging/ConsoleLogHandler.cs create mode 100644 dotnet/src/webdriver/Internal/Logging/ILogContext.cs create mode 100644 dotnet/src/webdriver/Internal/Logging/ILogHandler.cs create mode 100644 dotnet/src/webdriver/Internal/Logging/ILogHandlerList.cs create mode 100644 dotnet/src/webdriver/Internal/Logging/ILogger.cs create mode 100644 dotnet/src/webdriver/Internal/Logging/Log.cs create mode 100644 dotnet/src/webdriver/Internal/Logging/LogContext.cs create mode 100644 dotnet/src/webdriver/Internal/Logging/LogContextManager.cs create mode 100644 dotnet/src/webdriver/Internal/Logging/LogEvent.cs create mode 100644 dotnet/src/webdriver/Internal/Logging/LogEventLevel.cs create mode 100644 dotnet/src/webdriver/Internal/Logging/LogHandlerList.cs create mode 100644 dotnet/src/webdriver/Internal/Logging/Logger.cs create mode 100644 dotnet/test/common/Internal/Logging/LogTest.cs diff --git a/dotnet/src/webdriver/BUILD.bazel b/dotnet/src/webdriver/BUILD.bazel index 317a07cea39d6..edf6087329f71 100644 --- a/dotnet/src/webdriver/BUILD.bazel +++ b/dotnet/src/webdriver/BUILD.bazel @@ -33,6 +33,10 @@ csharp_library( "**/*.cs", ]) + devtools_version_targets(), out = "WebDriver", + internals_visible_to = [ + "WebDriver.Common.Tests", + ], + langversion = "10.0", resources = [ "//javascript/atoms/fragments:find-elements.js", "//javascript/atoms/fragments:is-displayed.js", @@ -64,6 +68,7 @@ csharp_library( ]) + devtools_version_targets(), out = "WebDriver.StrongNamed", keyfile = "//dotnet:WebDriver.snk", + langversion = "10.0", resources = [ "//javascript/atoms/fragments:find-elements.js", "//javascript/atoms/fragments:is-displayed.js", diff --git a/dotnet/src/webdriver/Chrome/ChromeDriver.cs b/dotnet/src/webdriver/Chrome/ChromeDriver.cs index a22ae7dc75ac7..1fc39a4dce0e9 100644 --- a/dotnet/src/webdriver/Chrome/ChromeDriver.cs +++ b/dotnet/src/webdriver/Chrome/ChromeDriver.cs @@ -21,6 +21,7 @@ using System.Collections.ObjectModel; using OpenQA.Selenium.Remote; using OpenQA.Selenium.Chromium; +using OpenQA.Selenium.Internal.Logging; namespace OpenQA.Selenium.Chrome { diff --git a/dotnet/src/webdriver/Internal/Logging/ConsoleLogHandler.cs b/dotnet/src/webdriver/Internal/Logging/ConsoleLogHandler.cs new file mode 100644 index 0000000000000..83759ac09dee6 --- /dev/null +++ b/dotnet/src/webdriver/Internal/Logging/ConsoleLogHandler.cs @@ -0,0 +1,49 @@ +// +// 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. +// + +using System; + +namespace OpenQA.Selenium.Internal.Logging +{ + /// + /// Represents a log handler that writes log events to the console. + /// + public class ConsoleLogHandler : ILogHandler + { + // performance trick to avoid expensive Enum.ToString() with fixed length + private static readonly string[] _levels = { "TRACE", "DEBUG", " INFO", " WARN", "ERROR" }; + + /// + /// Handles a log event by writing it to the console. + /// + /// The log event to handle. + public void Handle(LogEvent logEvent) + { + Console.Error.WriteLine($"{logEvent.Timestamp:HH:mm:ss.fff} {_levels[(int)logEvent.Level]} {logEvent.IssuedBy.Name}: {logEvent.Message}"); + } + + /// + /// Creates a new instance of the class. + /// + /// A new instance of the class. + public ILogHandler Clone() + { + return this; + } + } +} diff --git a/dotnet/src/webdriver/Internal/Logging/ILogContext.cs b/dotnet/src/webdriver/Internal/Logging/ILogContext.cs new file mode 100644 index 0000000000000..e5c31328c982a --- /dev/null +++ b/dotnet/src/webdriver/Internal/Logging/ILogContext.cs @@ -0,0 +1,83 @@ +// +// 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. +// + +using System; + +namespace OpenQA.Selenium.Internal.Logging +{ + /// + /// Represents a logging context that provides methods for creating sub-contexts, retrieving loggers, emitting log messages, and configuring minimum log levels. + /// + public interface ILogContext : IDisposable + { + /// + /// Creates a new logging context. + /// + /// A new instance of . + ILogContext CreateContext(); + + /// + /// Creates a new logging context with the specified minimum log level. + /// + /// The minimum log level for the new context. + /// A new instance of with the specified minimum log level. + ILogContext CreateContext(LogEventLevel minimumLevel); + + /// + /// Gets a logger for the specified type. + /// + /// The type for which to retrieve the logger. + /// An instance of for the specified type. + internal ILogger GetLogger(); + + /// + /// Gets a logger for the specified type. + /// + /// The type for which to retrieve the logger. + /// An instance of for the specified type. + internal ILogger GetLogger(Type type); + + /// + /// Emits a log message using the specified logger, log level, and message. + /// + /// The logger to emit the log message. + /// The log level of the message. + /// The log message. + internal void EmitMessage(ILogger logger, LogEventLevel level, string message); + + /// + /// Sets the minimum log level for the current context. + /// + /// The minimum log level. + /// The current instance of with the minimum log level set. + ILogContext SetLevel(LogEventLevel level); + + /// + /// Sets the minimum log level for the specified type in the current context. + /// + /// The type for which to set the minimum log level. + /// The minimum log level. + /// The current instance of with the minimum log level set for the specified type. + ILogContext SetLevel(Type issuer, LogEventLevel level); + + /// + /// Gets a list of log handlers. + /// + ILogHandlerList Handlers { get; } + } +} diff --git a/dotnet/src/webdriver/Internal/Logging/ILogHandler.cs b/dotnet/src/webdriver/Internal/Logging/ILogHandler.cs new file mode 100644 index 0000000000000..44c3ae67f6377 --- /dev/null +++ b/dotnet/src/webdriver/Internal/Logging/ILogHandler.cs @@ -0,0 +1,38 @@ +// +// 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. +// + +namespace OpenQA.Selenium.Internal.Logging +{ + /// + /// Represents a log handler that handles log events. + /// + public interface ILogHandler + { + /// + /// Handles a log event. + /// + /// The log event to handle. + void Handle(LogEvent logEvent); + + /// + /// Creates a clone of the log handler. + /// + /// A clone of the log handler. + ILogHandler Clone(); + } +} diff --git a/dotnet/src/webdriver/Internal/Logging/ILogHandlerList.cs b/dotnet/src/webdriver/Internal/Logging/ILogHandlerList.cs new file mode 100644 index 0000000000000..60d63ca5251f9 --- /dev/null +++ b/dotnet/src/webdriver/Internal/Logging/ILogHandlerList.cs @@ -0,0 +1,48 @@ +// +// 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. +// + +using System.Collections.Generic; + +namespace OpenQA.Selenium.Internal.Logging +{ + /// + /// Represents a list of log handlers. + /// + public interface ILogHandlerList : IEnumerable + { + /// + /// Adds a log handler to the list. + /// + /// The log handler to add. + /// The log context. + ILogContext Add(ILogHandler handler); + + /// + /// Removes a log handler from the list. + /// + /// The log handler to remove. + /// The log context. + ILogContext Remove(ILogHandler handler); + + /// + /// Clears all log handlers from the list. + /// + /// The log context. + ILogContext Clear(); + } +} diff --git a/dotnet/src/webdriver/Internal/Logging/ILogger.cs b/dotnet/src/webdriver/Internal/Logging/ILogger.cs new file mode 100644 index 0000000000000..66e5af8f240c7 --- /dev/null +++ b/dotnet/src/webdriver/Internal/Logging/ILogger.cs @@ -0,0 +1,68 @@ +// +// 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. +// + +using System; + +namespace OpenQA.Selenium.Internal.Logging +{ + /// + /// Defines the interface through which log messages are emitted. + /// + internal interface ILogger + { + /// + /// Writes a trace-level log message. + /// + /// The log message. + void Trace(string message); + + /// + /// Writes a debug-level log message. + /// + /// The log message. + void Debug(string message); + + /// + /// Writes an info-level log message. + /// + /// The log message. + void Info(string message); + + /// + /// Writes a warning-level log message. + /// + /// The log message. + void Warn(string message); + + /// + /// Writes an error-level log message. + /// + /// The log message. + void Error(string message); + + /// + /// Gets or sets the log event level. + /// + LogEventLevel Level { get; set; } + + /// + /// Gets the type of the logger issuer. + /// + Type Issuer { get; } + } +} diff --git a/dotnet/src/webdriver/Internal/Logging/Log.cs b/dotnet/src/webdriver/Internal/Logging/Log.cs new file mode 100644 index 0000000000000..a44054e13ba78 --- /dev/null +++ b/dotnet/src/webdriver/Internal/Logging/Log.cs @@ -0,0 +1,125 @@ +// +// 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. +// + +using System; + +namespace OpenQA.Selenium.Internal.Logging +{ + /// + /// Provides context aware logging functionality for the Selenium WebDriver. + /// + /// + /// + /// Use the following code to enable logging to console: + /// + /// Log.SetMinimumLevel(LogEventLevel.Debug)).WithHandler(new ConsoleLogHandler()); + /// + /// + /// Or enable it per limited execution scope: + /// + /// using (var ctx = Log.CreateContext(LogEventLevel.Trace)) + /// { + /// // do something + /// } + /// + /// + public static class Log + { + private static readonly LogContextManager _logContextManager = new LogContextManager(); + + /// + /// Creates a new log context with the current context properties and the specified minimum log event level. + /// + /// The created log context. + public static ILogContext CreateContext() + { + return _logContextManager.CurrentContext.CreateContext(); + } + + /// + /// Creates a new log context with with the current context properties and the specified minimum log event level. + /// + /// The minimum log event level. + /// The created log context. + public static ILogContext CreateContext(LogEventLevel minimumLevel) + { + return _logContextManager.CurrentContext.CreateContext(minimumLevel); + } + + /// + /// Gets or sets the current log context. + /// + internal static ILogContext CurrentContext + { + get + { + return _logContextManager.CurrentContext; + } + set + { + _logContextManager.CurrentContext = value; + } + } + + /// + /// Gets a logger for the specified type. + /// + /// The type to get the logger for. + /// The logger. + internal static ILogger GetLogger() + { + return _logContextManager.CurrentContext.GetLogger(); + } + + /// + /// Gets a logger for the specified type. + /// + /// The type to get the logger for. + /// The logger. + internal static ILogger GetLogger(Type type) + { + return _logContextManager.CurrentContext.GetLogger(type); + } + + /// + /// Sets the minimum log event level for the current log context. + /// + /// The minimum log event level. + /// The current log context. + public static ILogContext SetLevel(LogEventLevel level) + { + return _logContextManager.CurrentContext.SetLevel(level); + } + + /// + /// Sets the minimum log event level for the specified issuer in the current log context. + /// + /// The issuer type. + /// The minimum log event level. + /// The current log context. + public static ILogContext SetLevel(Type issuer, LogEventLevel level) + { + return _logContextManager.CurrentContext.SetLevel(issuer, level); + } + + /// + /// Gets a list of log handlers for the current log context. + /// + public static ILogHandlerList Handlers => _logContextManager.CurrentContext.Handlers; + } +} diff --git a/dotnet/src/webdriver/Internal/Logging/LogContext.cs b/dotnet/src/webdriver/Internal/Logging/LogContext.cs new file mode 100644 index 0000000000000..6b19059b5cd5a --- /dev/null +++ b/dotnet/src/webdriver/Internal/Logging/LogContext.cs @@ -0,0 +1,143 @@ +// +// 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. +// + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; + +namespace OpenQA.Selenium.Internal.Logging +{ + /// + /// Represents a logging context that provides methods for creating sub-contexts, retrieving loggers, emitting log messages, and configuring minimum log levels. + /// + /// + internal class LogContext : ILogContext + { + private ConcurrentDictionary _loggers; + + private LogEventLevel _level; + + private readonly ILogContext _parentLogContext; + + public LogContext(LogEventLevel level, ILogContext parentLogContext, ConcurrentDictionary loggers, ILogHandlerList handlers) + { + _level = level; + + _parentLogContext = parentLogContext; + + _loggers = loggers; + + Handlers = handlers ?? new LogHandlerList(this); + } + + public ILogContext CreateContext() + { + return CreateContext(_level); + } + + public ILogContext CreateContext(LogEventLevel minimumLevel) + { + ConcurrentDictionary loggers = null; + + if (_loggers != null) + { + loggers = new ConcurrentDictionary(_loggers.Select(l => new KeyValuePair(l.Key, new Logger(l.Value.Issuer, minimumLevel)))); + } + + IList handlers = null; + + if (Handlers != null) + { + handlers = new List(Handlers.Select(h => h.Clone())); + } + else + { + handlers = new List(); + } + + var context = new LogContext(minimumLevel, this, loggers, Handlers); + + Log.CurrentContext = context; + + return context; + } + + public ILogger GetLogger() + { + return GetLogger(typeof(T)); + } + + public ILogger GetLogger(Type type) + { + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + + if (_loggers is null) + { + _loggers = new ConcurrentDictionary(); + } + + return _loggers.GetOrAdd(type, _ => new Logger(type, _level)); + } + + public void EmitMessage(ILogger logger, LogEventLevel level, string message) + { + if (Handlers != null && level >= _level && level >= GetLogger(logger.Issuer).Level) + { + var logEvent = new LogEvent(logger.Issuer, DateTimeOffset.Now, level, message); + + foreach (var handler in Handlers) + { + handler.Handle(logEvent); + } + } + } + + public ILogContext SetLevel(LogEventLevel level) + { + _level = level; + + if (_loggers != null) + { + foreach (var logger in _loggers.Values) + { + logger.Level = _level; + } + } + + return this; + } + + public ILogContext SetLevel(Type issuer, LogEventLevel level) + { + GetLogger(issuer).Level = level; + + return this; + } + + public ILogHandlerList Handlers { get; } + + public void Dispose() + { + Log.CurrentContext = _parentLogContext; + } + } +} diff --git a/dotnet/src/webdriver/Internal/Logging/LogContextManager.cs b/dotnet/src/webdriver/Internal/Logging/LogContextManager.cs new file mode 100644 index 0000000000000..b1d64a0179d74 --- /dev/null +++ b/dotnet/src/webdriver/Internal/Logging/LogContextManager.cs @@ -0,0 +1,60 @@ +// +// 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. +// + +using System.Threading; + +namespace OpenQA.Selenium.Internal.Logging +{ + internal class LogContextManager + { + private readonly ILogContext _globalLogContext; + + private readonly AsyncLocal _currentAmbientLogContext = new AsyncLocal(); + + public LogContextManager() + { + _globalLogContext = new LogContext(LogEventLevel.Info, null, null, null); + + _globalLogContext.Handlers.Add(new ConsoleLogHandler()); + } + + public ILogContext GlobalContext + { + get { return _globalLogContext; } + } + + public ILogContext CurrentContext + { + get + { + if (_currentAmbientLogContext.Value is null) + { + return _globalLogContext; + } + else + { + return _currentAmbientLogContext.Value; + } + } + set + { + _currentAmbientLogContext.Value = value; + } + } + } +} diff --git a/dotnet/src/webdriver/Internal/Logging/LogEvent.cs b/dotnet/src/webdriver/Internal/Logging/LogEvent.cs new file mode 100644 index 0000000000000..103194070ee81 --- /dev/null +++ b/dotnet/src/webdriver/Internal/Logging/LogEvent.cs @@ -0,0 +1,63 @@ +// +// 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. +// + +using System; + +namespace OpenQA.Selenium.Internal.Logging +{ + /// + /// Represents a log event in the Selenium WebDriver internal logging system. + /// + public sealed class LogEvent + { + /// + /// Initializes a new instance of the class. + /// + /// The type that issued the log event. + /// The timestamp of the log event. + /// The level of the log event. + /// The message of the log event. + public LogEvent(Type issuedBy, DateTimeOffset timestamp, LogEventLevel level, string message) + { + IssuedBy = issuedBy; + Timestamp = timestamp; + Level = level; + Message = message; + } + + /// + /// Gets the type that issued the log event. + /// + public Type IssuedBy { get; } + + /// + /// Gets the timestamp of the log event. + /// + public DateTimeOffset Timestamp { get; } + + /// + /// Gets the level of the log event. + /// + public LogEventLevel Level { get; } + + /// + /// Gets the message of the log event. + /// + public string Message { get; } + } +} diff --git a/dotnet/src/webdriver/Internal/Logging/LogEventLevel.cs b/dotnet/src/webdriver/Internal/Logging/LogEventLevel.cs new file mode 100644 index 0000000000000..61e60f910de92 --- /dev/null +++ b/dotnet/src/webdriver/Internal/Logging/LogEventLevel.cs @@ -0,0 +1,56 @@ +// +// 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. +// + +namespace OpenQA.Selenium.Internal.Logging +{ + /// + /// Defines the levels of logging events. + /// + public enum LogEventLevel + { + /// + /// The most detailed log events. + /// + Trace = 0, + + /// + /// Log events that are useful for debugging purposes. + /// + Debug = 1, + + /// + /// Log events that provide general information about the application's operation. + /// + Info = 2, + + /// + /// Log events that indicate a potential problem or a non-critical error. + /// + Warn = 3, + + /// + /// Log events that indicate a serious error or a failure that requires immediate attention. + /// + Error = 4, + + /// + /// No log events. + /// + None = 5 + } +} diff --git a/dotnet/src/webdriver/Internal/Logging/LogHandlerList.cs b/dotnet/src/webdriver/Internal/Logging/LogHandlerList.cs new file mode 100644 index 0000000000000..ad826cc539861 --- /dev/null +++ b/dotnet/src/webdriver/Internal/Logging/LogHandlerList.cs @@ -0,0 +1,57 @@ +// +// 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. +// + +using System.Collections.Generic; + +namespace OpenQA.Selenium.Internal.Logging +{ + /// + /// Represents a list of log handlers. + /// + /// + internal class LogHandlerList : List, ILogHandlerList + { + private readonly ILogContext _logContext; + + public LogHandlerList(ILogContext logContext) + { + _logContext = logContext; + } + + public new ILogContext Add(ILogHandler handler) + { + base.Add(handler); + + return _logContext; + } + + public new ILogContext Remove(ILogHandler handler) + { + base.Remove(handler); + + return _logContext; + } + + public new ILogContext Clear() + { + base.Clear(); + + return _logContext; + } + } +} diff --git a/dotnet/src/webdriver/Internal/Logging/Logger.cs b/dotnet/src/webdriver/Internal/Logging/Logger.cs new file mode 100644 index 0000000000000..9bb2cece9ee6c --- /dev/null +++ b/dotnet/src/webdriver/Internal/Logging/Logger.cs @@ -0,0 +1,69 @@ +// +// 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. +// + +using System; + +namespace OpenQA.Selenium.Internal.Logging +{ + /// + /// The implementation of the interface through which log messages are emitted. + /// + /// + internal class Logger : ILogger + { + public Logger(Type issuer, LogEventLevel level) + { + Issuer = issuer; + Level = level; + } + + public LogEventLevel Level { get; set; } + + public Type Issuer { get; internal set; } + + public void Trace(string message) + { + LogMessage(LogEventLevel.Trace, message); + } + + public void Debug(string message) + { + LogMessage(LogEventLevel.Debug, message); + } + + public void Info(string message) + { + LogMessage(LogEventLevel.Info, message); + } + + public void Warn(string message) + { + LogMessage(LogEventLevel.Warn, message); + } + + public void Error(string message) + { + LogMessage(LogEventLevel.Error, message); + } + + private void LogMessage(LogEventLevel level, string message) + { + Log.CurrentContext.EmitMessage(this, level, message); + } + } +} diff --git a/dotnet/src/webdriver/Remote/HttpCommandExecutor.cs b/dotnet/src/webdriver/Remote/HttpCommandExecutor.cs index 0b69c9dccf05b..5599241182b9c 100644 --- a/dotnet/src/webdriver/Remote/HttpCommandExecutor.cs +++ b/dotnet/src/webdriver/Remote/HttpCommandExecutor.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; using System.Linq; using System.Net; @@ -27,6 +28,7 @@ using System.Threading; using System.Threading.Tasks; using OpenQA.Selenium.Internal; +using OpenQA.Selenium.Internal.Logging; namespace OpenQA.Selenium.Remote { @@ -50,6 +52,8 @@ public class HttpCommandExecutor : ICommandExecutor private CommandInfoRepository commandInfoRepository = new W3CWireProtocolCommandInfoRepository(); private HttpClient client; + private static readonly ILogger _logger = Log.GetLogger(); + /// /// Initializes a new instance of the class /// @@ -162,6 +166,8 @@ public virtual Response Execute(Command commandToExecute) throw new ArgumentNullException(nameof(commandToExecute), "commandToExecute cannot be null"); } + _logger.Debug($"Executing command: {commandToExecute}"); + HttpCommandInfo info = this.commandInfoRepository.GetCommandInfo(commandToExecute.Name); if (info == null) { @@ -191,6 +197,9 @@ public virtual Response Execute(Command commandToExecute) } Response toReturn = this.CreateResponse(responseInfo); + + _logger.Debug($"Response: {toReturn}"); + return toReturn; } @@ -270,8 +279,12 @@ private async Task MakeHttpRequest(HttpRequestInfo requestInfo requestMessage.Content.Headers.ContentType = contentTypeHeader; } + _logger.Trace($">> {requestMessage}"); + using (HttpResponseMessage responseMessage = await this.client.SendAsync(requestMessage).ConfigureAwait(false)) { + _logger.Trace($"<< {responseMessage}"); + HttpResponseInfo httpResponseInfo = new HttpResponseInfo(); httpResponseInfo.Body = await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false); httpResponseInfo.ContentType = responseMessage.Content.Headers.ContentType?.ToString(); diff --git a/dotnet/src/webdriver/SeleniumManager.cs b/dotnet/src/webdriver/SeleniumManager.cs index 96785b3d06f06..eb25d522a906e 100644 --- a/dotnet/src/webdriver/SeleniumManager.cs +++ b/dotnet/src/webdriver/SeleniumManager.cs @@ -18,6 +18,7 @@ using Newtonsoft.Json; using OpenQA.Selenium.Internal; +using OpenQA.Selenium.Internal.Logging; using System; using System.Collections.Generic; using System.Diagnostics; @@ -34,6 +35,8 @@ namespace OpenQA.Selenium /// public static class SeleniumManager { + private static readonly ILogger _logger = Log.GetLogger(typeof(SeleniumManager)); + private static readonly string BinaryFullPath = Environment.GetEnvironmentVariable("SE_MANAGER_PATH"); static SeleniumManager() @@ -115,7 +118,11 @@ public static string DriverPath(DriverOptions options) // Cannot set Browser Location for this driver and that is ok } - return (string)output["driver_path"]; + var driverPath = (string)output["driver_path"]; + + _logger.Trace($"Driver path: {driverPath}"); + + return driverPath; } /// diff --git a/dotnet/src/webdriver/WebDriver.csproj b/dotnet/src/webdriver/WebDriver.csproj index 85abd8152ee1d..24df12ee0da74 100644 --- a/dotnet/src/webdriver/WebDriver.csproj +++ b/dotnet/src/webdriver/WebDriver.csproj @@ -4,6 +4,7 @@ netstandard2.0 WebDriver OpenQA.Selenium + 10.0 @@ -42,6 +43,8 @@ + + diff --git a/dotnet/test/chrome/WebDriver.Chrome.Tests.csproj b/dotnet/test/chrome/WebDriver.Chrome.Tests.csproj index d63c0e61a6e86..0d168ea50c237 100644 --- a/dotnet/test/chrome/WebDriver.Chrome.Tests.csproj +++ b/dotnet/test/chrome/WebDriver.Chrome.Tests.csproj @@ -24,8 +24,8 @@ - - + + diff --git a/dotnet/test/common/BUILD.bazel b/dotnet/test/common/BUILD.bazel index 6f0742a0c6540..98b925cd30a6c 100644 --- a/dotnet/test/common/BUILD.bazel +++ b/dotnet/test/common/BUILD.bazel @@ -63,6 +63,7 @@ dotnet_nunit_test_suite( ]) + [ ":assembly-fixtures", ], + out = "WebDriver.Common.Tests", browsers = [ # The first browser in this list is assumed to be the one that should # be used by default. diff --git a/dotnet/test/common/Environment/DriverConfig.cs b/dotnet/test/common/Environment/DriverConfig.cs index 17932b9c950d5..4f81e97086cab 100644 --- a/dotnet/test/common/Environment/DriverConfig.cs +++ b/dotnet/test/common/Environment/DriverConfig.cs @@ -1,10 +1,5 @@ using Newtonsoft.Json; using Newtonsoft.Json.Converters; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace OpenQA.Selenium.Environment { @@ -14,9 +9,6 @@ public class DriverConfig [JsonProperty] public string DriverTypeName { get; set; } - [JsonProperty] - public string AssemblyName { get; set; } - [JsonProperty] [JsonConverter(typeof(StringEnumConverter))] public Browser BrowserValue { get; set; } diff --git a/dotnet/test/common/Environment/DriverFactory.cs b/dotnet/test/common/Environment/DriverFactory.cs index e0b5d705a2d77..11cf7d00f9390 100644 --- a/dotnet/test/common/Environment/DriverFactory.cs +++ b/dotnet/test/common/Environment/DriverFactory.cs @@ -55,6 +55,8 @@ public IWebDriver CreateDriver(Type driverType, bool logging = false) public IWebDriver CreateDriverWithOptions(Type driverType, DriverOptions driverOptions, bool logging = false) { + Console.WriteLine($"Creating new driver of {driverType} type..."); + Browser browser = Browser.All; DriverService service = null; DriverOptions options = null; diff --git a/dotnet/test/common/Environment/EnvironmentManager.cs b/dotnet/test/common/Environment/EnvironmentManager.cs index 7aa60cb577601..ac606787d5fa2 100644 --- a/dotnet/test/common/Environment/EnvironmentManager.cs +++ b/dotnet/test/common/Environment/EnvironmentManager.cs @@ -5,6 +5,7 @@ using Newtonsoft.Json; using NUnit.Framework; using OpenQA.Selenium.Internal; +using System.Linq; namespace OpenQA.Selenium.Environment { @@ -58,17 +59,18 @@ private EnvironmentManager() this.driverFactory = new DriverFactory(driverServiceLocation, browserLocation); this.driverFactory.DriverStarting += OnDriverStarting; - Assembly driverAssembly = null; - try - { - driverAssembly = Assembly.Load(driverConfig.AssemblyName); - } - catch (FileNotFoundException) + // Search for the driver type in the all assemblies, + // bazel uses unpredictable assembly names to execute tests + driverType = AppDomain.CurrentDomain.GetAssemblies() + .Reverse() + .Select(assembly => assembly.GetType(driverConfig.DriverTypeName)) + .FirstOrDefault(t => t != null); + + if (driverType == null) { - driverAssembly = Assembly.GetExecutingAssembly(); + throw new ArgumentOutOfRangeException($"Unable to find driver type {driverConfig.DriverTypeName}"); } - driverType = driverAssembly.GetType(driverConfig.DriverTypeName); browser = driverConfig.BrowserValue; remoteCapabilities = driverConfig.RemoteCapabilities; diff --git a/dotnet/test/common/Environment/TestWebServer.cs b/dotnet/test/common/Environment/TestWebServer.cs index 5cb8645a68353..b5381a1c0b3a2 100644 --- a/dotnet/test/common/Environment/TestWebServer.cs +++ b/dotnet/test/common/Environment/TestWebServer.cs @@ -4,6 +4,7 @@ using System.Net; using System.Diagnostics; using System.Text; +using System.Runtime.InteropServices; using System.Net.Http; namespace OpenQA.Selenium.Environment @@ -45,6 +46,11 @@ public void Start() standaloneTestJar = Path.Combine(baseDirectory, "../../../../../../bazel-bin/java/test/org/openqa/selenium/environment/appserver"); } + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + standaloneTestJar += ".exe"; + } + Console.Write("Standalone jar is " + standaloneTestJar); if (!File.Exists(standaloneTestJar)) @@ -148,8 +154,13 @@ public void Start() public void Stop() { - using var httpClient = new HttpClient(); - using var quitResponse = httpClient.GetAsync(EnvironmentManager.Instance.UrlBuilder.LocalWhereIs("quitquitquit")).GetAwaiter().GetResult(); + using (var httpClient = new HttpClient()) + { + using (var quitResponse = httpClient.GetAsync(EnvironmentManager.Instance.UrlBuilder.LocalWhereIs("quitquitquit")).GetAwaiter().GetResult()) + { + + } + } if (webserverProcess != null) { diff --git a/dotnet/test/common/Internal/Logging/LogTest.cs b/dotnet/test/common/Internal/Logging/LogTest.cs new file mode 100644 index 0000000000000..037e94faefec6 --- /dev/null +++ b/dotnet/test/common/Internal/Logging/LogTest.cs @@ -0,0 +1,215 @@ +using NUnit.Framework; +using System; +using System.Collections.Generic; + +namespace OpenQA.Selenium.Internal.Logging +{ + public class LogTest + { + private TestLogHandler testLogHandler; + private ILogger logger; + + [SetUp] + public void SetUp() + { + testLogHandler = new TestLogHandler(); + logger = Log.GetLogger(); + } + + [Test] + public void LoggerShouldEmitEvent() + { + Log.SetLevel(LogEventLevel.Info).Handlers.Add(testLogHandler); + + logger.Info("test message"); + + Assert.That(testLogHandler.Events, Has.Count.EqualTo(1)); + + var logEvent = testLogHandler.Events[0]; + Assert.That(logEvent.Level, Is.EqualTo(LogEventLevel.Info)); + Assert.That(logEvent.Message, Is.EqualTo("test message")); + Assert.That(logEvent.IssuedBy, Is.EqualTo(typeof(LogTest))); + Assert.That(logEvent.Timestamp, Is.EqualTo(DateTimeOffset.Now).Within(100).Milliseconds); + } + + [Test] + [TestCase(LogEventLevel.Trace)] + [TestCase(LogEventLevel.Debug)] + [TestCase(LogEventLevel.Info)] + [TestCase(LogEventLevel.Warn)] + [TestCase(LogEventLevel.Error)] + public void LoggerShouldEmitEventWithProperLevel(LogEventLevel level) + { + Log.SetLevel(level).Handlers.Add(testLogHandler); + + switch (level) + { + case LogEventLevel.Trace: + logger.Trace("test message"); + break; + case LogEventLevel.Debug: + logger.Debug("test message"); + break; + case LogEventLevel.Info: + logger.Info("test message"); + break; + case LogEventLevel.Warn: + logger.Warn("test message"); + break; + case LogEventLevel.Error: + logger.Error("test message"); + break; + } + + Assert.That(testLogHandler.Events, Has.Count.EqualTo(1)); + + Assert.That(testLogHandler.Events[0].Level, Is.EqualTo(level)); + } + + [Test] + public void LoggerShouldNotEmitEventWhenLevelIsLess() + { + Log.SetLevel(LogEventLevel.Info).Handlers.Add(testLogHandler); + + logger.Trace("test message"); + + Assert.That(testLogHandler.Events, Has.Count.EqualTo(0)); + } + + [Test] + public void ShouldGetProperLogger() + { + Log.SetLevel(LogEventLevel.Info); + + var logger = Log.GetLogger(); + + Assert.That(logger.Issuer, Is.EqualTo(typeof(LogTest))); + Assert.That(logger.Level, Is.EqualTo(LogEventLevel.Info)); + } + + [Test] + public void ShouldCacheLogger() + { + var logger1 = Log.GetLogger(); + var logger2 = Log.GetLogger(); + + Assert.That(logger1, Is.SameAs(logger2)); + } + + [Test] + public void ShouldCreateContext() + { + Log.SetLevel(LogEventLevel.Info); + + using var context = Log.CreateContext(); + + var logger = context.GetLogger(); + + Assert.That(logger.Level, Is.EqualTo(LogEventLevel.Info)); + } + + [Test] + public void ShouldCreateContextWithCustomLevel() + { + Log.SetLevel(LogEventLevel.Info); + + using var context = Log.CreateContext(LogEventLevel.Warn); + + var logger = context.GetLogger(); + + Assert.That(logger.Level, Is.EqualTo(LogEventLevel.Warn)); + } + + [Test] + public void ContextShouldChangeLevel() + { + Log.SetLevel(LogEventLevel.Info); + + using var context = Log.CreateContext(); + + context.SetLevel(LogEventLevel.Warn); + + var logger = context.GetLogger(); + + Assert.That(logger.Level, Is.EqualTo(LogEventLevel.Warn)); + } + + [Test] + public void ShouldCreateContextWithCustomHandler() + { + Log.SetLevel(LogEventLevel.Info); + + using var context = Log.CreateContext().Handlers.Add(testLogHandler); + + var logger = context.GetLogger(); + + logger.Info("test message"); + + Assert.That(testLogHandler.Events, Has.Count.EqualTo(1)); + } + + [Test] + public void ShouldCreateSubContext() + { + Log.SetLevel(LogEventLevel.Info); + + using var context = Log.CreateContext(); + + Assert.That(Log.CurrentContext, Is.SameAs(context)); + + using var subContext = context.CreateContext(); + + Assert.That(Log.CurrentContext, Is.SameAs(subContext)); + + var logger = subContext.GetLogger(); + + Assert.That(logger.Level, Is.EqualTo(LogEventLevel.Info)); + } + + [Test] + public void ShouldCreateSubContextFromCurrentContext() + { + Log.SetLevel(LogEventLevel.Info); + + using var context = Log.CreateContext(); + + Assert.That(Log.CurrentContext, Is.SameAs(context)); + + using var subContext = Log.CreateContext(); + + Assert.That(Log.CurrentContext, Is.SameAs(subContext)); + + var logger = subContext.GetLogger(); + + Assert.That(logger.Level, Is.EqualTo(LogEventLevel.Info)); + } + + [Test] + public void ShouldCapturePreviousContextWhenCurrentFinishes() + { + using var globalContext = Log.CurrentContext; + + using (var subContext = Log.CreateContext()) + { + Assert.That(Log.CurrentContext, Is.SameAs(subContext)); + } + + Assert.That(Log.CurrentContext, Is.SameAs(globalContext)); + } + } + + class TestLogHandler : ILogHandler + { + public ILogHandler Clone() + { + return this; + } + + public void Handle(LogEvent logEvent) + { + Events.Add(logEvent); + } + + public IList Events { get; internal set; } = new List(); + } +} diff --git a/dotnet/test/common/WebDriver.Common.Tests.csproj b/dotnet/test/common/WebDriver.Common.Tests.csproj index ed9b250727217..c0f4075b1d439 100644 --- a/dotnet/test/common/WebDriver.Common.Tests.csproj +++ b/dotnet/test/common/WebDriver.Common.Tests.csproj @@ -25,9 +25,9 @@ - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/dotnet/test/common/appconfig.json b/dotnet/test/common/appconfig.json index 9ed48075d594c..0871ec6ed9a9f 100644 --- a/dotnet/test/common/appconfig.json +++ b/dotnet/test/common/appconfig.json @@ -26,7 +26,6 @@ "DriverConfigs": { "Chrome": { "DriverTypeName": "OpenQA.Selenium.Chrome.StableChannelChromeDriver", - "AssemblyName": "WebDriver.Common.Tests", "BrowserValue": "Chrome", "RemoteCapabilities": "chrome", "Logging": false @@ -34,7 +33,6 @@ "ChromeDev": { "DriverTypeName": "OpenQA.Selenium.Chrome.DevChannelChromeDriver", - "AssemblyName": "WebDriver.Common.Tests", "BrowserValue": "Chrome", "RemoteCapabilities": "chrome", "Logging": false @@ -42,7 +40,6 @@ "EdgeIEMode": { "DriverTypeName": "OpenQA.Selenium.IE.EdgeInternetExplorerModeDriver", - "AssemblyName": "WebDriver.Common.Tests", "BrowserValue": "IE", "RemoteCapabilities": "internet explorer", "Logging": false @@ -50,7 +47,6 @@ "Edge": { "DriverTypeName": "OpenQA.Selenium.Edge.StableChannelEdgeDriver", - "AssemblyName": "WebDriver.Common.Tests", "BrowserValue": "Edge", "RemoteCapabilities": "MicrosoftEdge", "Logging": false @@ -58,7 +54,6 @@ "EdgeDev": { "DriverTypeName": "OpenQA.Selenium.Edge.DevChannelEdgeDriver", - "AssemblyName": "WebDriver.Common.Tests", "BrowserValue": "Edge", "RemoteCapabilities": "MicrosoftEdge", "Logging": false @@ -66,7 +61,6 @@ "Firefox": { "DriverTypeName": "OpenQA.Selenium.Firefox.StableChannelFirefoxDriver", - "AssemblyName": "WebDriver.Common.Tests", "BrowserValue": "Firefox", "RemoteCapabilities": "firefox", "Logging": false @@ -74,7 +68,6 @@ "FirefoxNightly": { "DriverTypeName": "OpenQA.Selenium.Firefox.NightlyChannelFirefoxDriver", - "AssemblyName": "WebDriver.Common.Tests", "BrowserValue": "Firefox", "RemoteCapabilities": "firefox", "Logging": false @@ -82,7 +75,6 @@ "Safari": { "DriverTypeName": "OpenQA.Selenium.Safari.DefaultSafariDriver", - "AssemblyName": "WebDriver.Common.Tests", "BrowserValue": "Safari", "RemoteCapabilities": "safari", "Logging": false @@ -90,7 +82,6 @@ "SafariTechPreview": { "DriverTypeName": "OpenQA.Selenium.Safari.SafariTechnologyPreviewDriver", - "AssemblyName": "WebDriver.Common.Tests", "BrowserValue": "Safari", "RemoteCapabilities": "safari", "Logging": false @@ -98,7 +89,6 @@ "Remote": { "DriverTypeName": "OpenQA.Selenium.Remote.StableChannelRemoteChromeDriver", - "AssemblyName": "WebDriver.Common.Tests", "BrowserValue": "Remote", "RemoteCapabilities": "chrome", "AutoStartRemoteServer": true, diff --git a/dotnet/test/edge/WebDriver.Edge.Tests.csproj b/dotnet/test/edge/WebDriver.Edge.Tests.csproj index 23c4e4f4fadd1..411dd22b73e55 100644 --- a/dotnet/test/edge/WebDriver.Edge.Tests.csproj +++ b/dotnet/test/edge/WebDriver.Edge.Tests.csproj @@ -26,8 +26,8 @@ - - + + diff --git a/dotnet/test/firefox/WebDriver.Firefox.Tests.csproj b/dotnet/test/firefox/WebDriver.Firefox.Tests.csproj index e3dda77d41329..e75039f3f4bb6 100644 --- a/dotnet/test/firefox/WebDriver.Firefox.Tests.csproj +++ b/dotnet/test/firefox/WebDriver.Firefox.Tests.csproj @@ -37,8 +37,8 @@ - - + + diff --git a/dotnet/test/ie/WebDriver.IE.Tests.csproj b/dotnet/test/ie/WebDriver.IE.Tests.csproj index 85eec3747b968..262410e1b705e 100644 --- a/dotnet/test/ie/WebDriver.IE.Tests.csproj +++ b/dotnet/test/ie/WebDriver.IE.Tests.csproj @@ -25,8 +25,8 @@ - - + + diff --git a/dotnet/test/remote/WebDriver.Remote.Tests.csproj b/dotnet/test/remote/WebDriver.Remote.Tests.csproj index c925d28af20ca..56edf029a66a3 100644 --- a/dotnet/test/remote/WebDriver.Remote.Tests.csproj +++ b/dotnet/test/remote/WebDriver.Remote.Tests.csproj @@ -22,8 +22,8 @@ - - + + diff --git a/dotnet/test/safari/WebDriver.Safari.Tests.csproj b/dotnet/test/safari/WebDriver.Safari.Tests.csproj index eb3e5e4205a59..1700c54bcf225 100644 --- a/dotnet/test/safari/WebDriver.Safari.Tests.csproj +++ b/dotnet/test/safari/WebDriver.Safari.Tests.csproj @@ -23,8 +23,8 @@ - - + + diff --git a/dotnet/test/support/WebDriver.Support.Tests.csproj b/dotnet/test/support/WebDriver.Support.Tests.csproj index 103fb1359569b..7fb4107b4b938 100644 --- a/dotnet/test/support/WebDriver.Support.Tests.csproj +++ b/dotnet/test/support/WebDriver.Support.Tests.csproj @@ -23,8 +23,8 @@ - - + +