Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[dotnet] Added function for sending multiple DevTools command #13539

Draft
wants to merge 5 commits into
base: trunk
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
38 changes: 38 additions & 0 deletions dotnet/src/webdriver/DevTools/DevToolsCommandSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// <copyright file="DevToolsCommandSettings.cs" company="WebDriver 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 Newtonsoft.Json.Linq;

namespace OpenQA.Selenium.DevTools
{
public class DevToolsCommandSettings
{
public DevToolsCommandSettings(string commandName)
{
if (string.IsNullOrWhiteSpace(commandName))
{
throw new ArgumentNullException(nameof(commandName));
}

CommandName = commandName;
}
public string SessionId { get; set; }
public string CommandName { get; set; }
public JToken CommandParameters { get; set; }
}
}
84 changes: 84 additions & 0 deletions dotnet/src/webdriver/DevTools/DevToolsSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,90 @@ public async Task<JToken> SendCommand(string commandName, string sessionId, JTok
return null;
}

/// <summary>
/// Send a collection of <see cref="DevToolsCommandSettings"/> and wait on all of their results.
/// </summary>
/// <param name="commands"></param>
/// <param name="cancellationToken"></param>
/// <param name="millisecondsTimeout"></param>
/// <param name="throwExceptionIfResponseNotReceived"></param>
/// <returns>A list of command response object implementeing the <see cref="ICommandResponse{T}"/> interface.</returns>
public async Task<List<DevToolsCommandResponse>> SendCommands(List<DevToolsCommandSettings> commands, CancellationToken cancellationToken = default(CancellationToken), int? millisecondsTimeout = null, bool throwExceptionIfResponseNotReceived = true)
{
if (millisecondsTimeout.HasValue == false)
{
millisecondsTimeout = Convert.ToInt32(CommandTimeout.TotalMilliseconds);
}

if (this.attachedTargetId == null)
{
LogTrace("Session not currently attached to a target; reattaching");
await this.InitializeSession();
}

var messages = new List<DevToolsCommandData>();
foreach (var item in commands)
{
messages.Add(new DevToolsCommandData(Interlocked.Increment(ref this.currentCommandId), item.SessionId, item.CommandName, item.CommandParameters));
}

if (this.connection != null && this.connection.IsActive)
{
foreach (var message in messages)
{
var contents = JsonConvert.SerializeObject(message);

this.pendingCommands.TryAdd(message.CommandId, message);
await this.connection.SendData(contents).ConfigureAwait(false);
}

WaitHandle.WaitAll(messages.Select(x => x.SyncEvent.WaitHandle).ToArray(), millisecondsTimeout.Value);

var noResponsesReceived = messages.Where(x => !x.SyncEvent.IsSet);
if (noResponsesReceived.Any() && throwExceptionIfResponseNotReceived)
{
throw new InvalidOperationException($"A command response was not received: {string.Join(", ", noResponsesReceived.Select(x => x.CommandName))}");
}

foreach (var message in messages)
{
DevToolsCommandData modified;
if (this.pendingCommands.TryRemove(message.CommandId, out modified))
{
if (modified.IsError)
{
var errorMessage = modified.Result.Value<string>("message");
var errorData = modified.Result.Value<string>("data");

var exceptionMessage = $"{message.CommandName}: {errorMessage}";
if (!string.IsNullOrWhiteSpace(errorData))
{
exceptionMessage = $"{exceptionMessage} - {errorData}";
}

LogTrace("Received Error Response {0}: {1} {2}", modified.CommandId, message, errorData);
throw new CommandResponseException(exceptionMessage)
{
Code = modified.Result.Value<long>("code")
};
}
}
}

return messages.Select(x => new DevToolsCommandResponse
{
Result = x.Result,
SessionId = x.SessionId
}).ToList();
}
else
{
LogTrace("WebSocket is not connected; not sending {0}", string.Join(", ", commands.Select(itm => itm.CommandName)));
}

return null;
}

/// <summary>
/// Releases all resources associated with this <see cref="DevToolsSession"/>.
/// </summary>
Expand Down
10 changes: 10 additions & 0 deletions dotnet/src/webdriver/DevTools/IDevToolsSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,5 +85,15 @@ Task<ICommandResponse<TCommand>> SendCommand<TCommand>(TCommand command, Cancell
/// <param name="throwExceptionIfResponseNotReceived"><see langword="true"/> to throw an exception if a response is not received; otherwise, <see langword="false"/>.</param>
/// <returns>The command response object implementing the <see cref="ICommandResponse{T}"/> interface.</returns>
Task<JToken> SendCommand(string commandName, JToken @params, CancellationToken cancellationToken, int? millisecondsTimeout, bool throwExceptionIfResponseNotReceived);

/// <summary>
/// Returns a collection of <see cref="DevToolsCommandResponse"/> based on a collection of commands.
/// </summary>
/// <param name="commands"></param>
/// <param name="cancellationToken"></param>
/// <param name="millisecondsTimeout"></param>
/// <param name="throwExceptionIfResponseNotReceived"></param>
/// <returns>A list of command response object implementeing the <see cref="ICommandResponse{T}"/> interface.</returns>
Task<List<DevToolsCommandResponse>> SendCommands(List<DevToolsCommandSettings> commands, CancellationToken cancellationToken = default(CancellationToken), int? millisecondsTimeout = null, bool throwExceptionIfResponseNotReceived = true);
}
}