Skip to content
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
4 changes: 4 additions & 0 deletions dotnet/src/webdriver/ILogs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@
// under the License.
// </copyright>

using System;
using System.Collections.ObjectModel;

#nullable enable

namespace OpenQA.Selenium
{
/// <summary>
Expand All @@ -37,6 +40,7 @@ public interface ILogs
/// <param name="logKind">The log for which to retrieve the log entries.
/// Log types can be found in the <see cref="LogType"/> class.</param>
/// <returns>The list of <see cref="LogEntry"/> objects for the specified log.</returns>
/// <exception cref="ArgumentNullException">If <paramref name="logKind"/> is <see langword="null"/>.</exception>
ReadOnlyCollection<LogEntry> GetLog(string logKind);
}
}
47 changes: 19 additions & 28 deletions dotnet/src/webdriver/LogEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,15 @@
using System.Collections.Generic;
using System.Globalization;

#nullable enable

namespace OpenQA.Selenium
{
/// <summary>
/// Represents an entry in a log from a driver instance.
/// </summary>
public class LogEntry
{
private LogLevel level = LogLevel.All;
private DateTime timestamp = DateTime.MinValue;
private string message = string.Empty;

/// <summary>
/// Initializes a new instance of the <see cref="LogEntry"/> class.
Expand All @@ -42,34 +41,27 @@ private LogEntry()
/// <summary>
/// Gets the timestamp value of the log entry.
/// </summary>
public DateTime Timestamp
{
get { return this.timestamp; }
}
public DateTime Timestamp { get; private set; } = DateTime.MinValue;

/// <summary>
/// Gets the logging level of the log entry.
/// </summary>
public LogLevel Level
{
get { return this.level; }
}
public LogLevel Level { get; private set; } = LogLevel.All;

/// <summary>
/// Gets the message of the log entry.
/// </summary>
public string Message
{
get { return this.message; }
}
public string Message { get; private set; } = string.Empty;

private static readonly DateTime UnixEpoch = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

/// <summary>
/// Returns a string that represents the current <see cref="LogEntry"/>.
/// </summary>
/// <returns>A string that represents the current <see cref="LogEntry"/>.</returns>
public override string ToString()
{
return string.Format(CultureInfo.InvariantCulture, "[{0:yyyy-MM-ddTHH:mm:ssZ}] [{1}] {2}", this.timestamp, this.level, this.message);
return string.Format(CultureInfo.InvariantCulture, "[{0:yyyy-MM-ddTHH:mm:ssZ}] [{1}] {2}", this.Timestamp, this.Level, this.Message);
}

/// <summary>
Expand All @@ -78,32 +70,31 @@ public override string ToString()
/// <param name="entryDictionary">The <see cref="Dictionary{TKey, TValue}"/> from
/// which to create the <see cref="LogEntry"/>.</param>
/// <returns>A <see cref="LogEntry"/> with the values in the dictionary.</returns>
internal static LogEntry FromDictionary(Dictionary<string, object> entryDictionary)
internal static LogEntry FromDictionary(Dictionary<string, object?> entryDictionary)
{
LogEntry entry = new LogEntry();
if (entryDictionary.ContainsKey("message"))
if (entryDictionary.TryGetValue("message", out object? message))
{
entry.message = entryDictionary["message"].ToString();
entry.Message = message?.ToString() ?? string.Empty;
}

if (entryDictionary.ContainsKey("timestamp"))
if (entryDictionary.TryGetValue("timestamp", out object? timestamp))
{
DateTime zeroDate = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
double timestampValue = Convert.ToDouble(entryDictionary["timestamp"], CultureInfo.InvariantCulture);
entry.timestamp = zeroDate.AddMilliseconds(timestampValue);
double timestampValue = Convert.ToDouble(timestamp, CultureInfo.InvariantCulture);
entry.Timestamp = UnixEpoch.AddMilliseconds(timestampValue);
}

if (entryDictionary.ContainsKey("level"))
if (entryDictionary.TryGetValue("level", out object? level))
{
string levelValue = entryDictionary["level"].ToString();
try
if (Enum.TryParse(level?.ToString(), ignoreCase: true, out LogLevel result))
{
entry.level = (LogLevel)Enum.Parse(typeof(LogLevel), levelValue, true);
entry.Level = result;
}
catch (ArgumentException)
else
{
// If the requested log level string is not a valid log level,
// ignore it and use LogLevel.All.
entry.Level = LogLevel.All;
}
}

Expand Down
2 changes: 2 additions & 0 deletions dotnet/src/webdriver/LogLevel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
// under the License.
// </copyright>

#nullable enable

namespace OpenQA.Selenium
{
/// <summary>
Expand Down
26 changes: 16 additions & 10 deletions dotnet/src/webdriver/Logs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,25 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;

#nullable enable

namespace OpenQA.Selenium
{
/// <summary>
/// Provides a mechanism for examining logs for the driver during the test.
/// </summary>
public class Logs : ILogs
{
private WebDriver driver;
private readonly WebDriver driver;

/// <summary>
/// Initializes a new instance of the <see cref="Logs"/> class.
/// </summary>
/// <param name="driver">Instance of the driver currently in use</param>
/// <exception cref="ArgumentNullException">If <paramref name="driver"/> is <see langword="null"/>.</exception>
public Logs(WebDriver driver)
{
this.driver = driver;
this.driver = driver ?? throw new ArgumentNullException(nameof(driver));
}

/// <summary>
Expand All @@ -50,12 +53,11 @@ public ReadOnlyCollection<string> AvailableLogTypes
try
{
Response commandResponse = this.driver.InternalExecute(DriverCommand.GetAvailableLogTypes, null);
object[] responseValue = commandResponse.Value as object[];
if (responseValue != null)
if (commandResponse.Value is object[] responseValue)
{
foreach (object logKind in responseValue)
{
availableLogTypes.Add(logKind.ToString());
availableLogTypes.Add(logKind.ToString()!);
}
}
}
Expand All @@ -74,21 +76,25 @@ public ReadOnlyCollection<string> AvailableLogTypes
/// <param name="logKind">The log for which to retrieve the log entries.
/// Log types can be found in the <see cref="LogType"/> class.</param>
/// <returns>The list of <see cref="LogEntry"/> objects for the specified log.</returns>
/// <exception cref="ArgumentNullException">If <paramref name="logKind"/> is <see langword="null"/>.</exception>
public ReadOnlyCollection<LogEntry> GetLog(string logKind)
{
if (logKind is null)
{
throw new ArgumentNullException(nameof(logKind));
}

List<LogEntry> entries = new List<LogEntry>();

Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("type", logKind);
Response commandResponse = this.driver.InternalExecute(DriverCommand.GetLog, parameters);

object[] responseValue = commandResponse.Value as object[];
if (responseValue != null)
if (commandResponse.Value is object?[] responseValue)
{
foreach (object rawEntry in responseValue)
foreach (object? rawEntry in responseValue)
{
Dictionary<string, object> entryDictionary = rawEntry as Dictionary<string, object>;
if (entryDictionary != null)
if (rawEntry is Dictionary<string, object?> entryDictionary)
{
entries.Add(LogEntry.FromDictionary(entryDictionary));
}
Expand Down
Loading