Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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 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