SEBWIN-219: Extended unit test coverage of runtime operations.

This commit is contained in:
dbuechel 2018-03-21 15:28:59 +01:00
parent 6efa7bed81
commit eb5fc50fea
10 changed files with 470 additions and 32 deletions

View file

@ -7,30 +7,30 @@
*/
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Client.Behaviour;
using SafeExamBrowser.Contracts.Behaviour.OperationModel;
using SafeExamBrowser.Contracts.Communication.Proxies;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.Monitoring;
using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Contracts.UserInterface.Taskbar;
//using Moq;
//using SafeExamBrowser.Client.Behaviour;
//using SafeExamBrowser.Contracts.Behaviour.OperationModel;
//using SafeExamBrowser.Contracts.Communication.Proxies;
//using SafeExamBrowser.Contracts.Logging;
//using SafeExamBrowser.Contracts.Monitoring;
//using SafeExamBrowser.Contracts.UserInterface;
//using SafeExamBrowser.Contracts.UserInterface.Taskbar;
namespace SafeExamBrowser.Client.UnitTests
{
[TestClass]
public class ClientControllerTests
{
private Mock<IDisplayMonitor> displayMonitorMock;
private Mock<ILogger> loggerMock;
private Mock<IProcessMonitor> processMonitorMock;
private Mock<IOperationSequence> operationSequenceMock;
private Mock<IRuntimeProxy> runtimeProxyMock;
private Mock<ITaskbar> taskbarMock;
private Mock<IUserInterfaceFactory> uiFactoryMock;
private Mock<IWindowMonitor> windowMonitorMock;
//private Mock<IDisplayMonitor> displayMonitorMock;
//private Mock<ILogger> loggerMock;
//private Mock<IProcessMonitor> processMonitorMock;
//private Mock<IOperationSequence> operationSequenceMock;
//private Mock<IRuntimeProxy> runtimeProxyMock;
//private Mock<ITaskbar> taskbarMock;
//private Mock<IUserInterfaceFactory> uiFactoryMock;
//private Mock<IWindowMonitor> windowMonitorMock;
private ClientController sut;
//private ClientController sut;
[TestMethod]
public void TODO()

View file

@ -16,7 +16,7 @@ namespace SafeExamBrowser.Contracts.Behaviour.OperationModel
/// <summary>
/// Indicates that the operation has been aborted due to an expected condition, e.g. as result of a user decision.
/// </summary>
Aborted,
Aborted = 1,
/// <summary>
/// Indicates that the operation has failed due to an invalid or unexpected condition.

View file

@ -0,0 +1,216 @@
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Contracts.Behaviour.OperationModel;
using SafeExamBrowser.Contracts.Communication.Data;
using SafeExamBrowser.Contracts.Communication.Hosts;
using SafeExamBrowser.Contracts.Communication.Proxies;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.WindowsApi;
using SafeExamBrowser.Runtime.Behaviour.Operations;
namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
{
[TestClass]
public class ClientOperationTests
{
private Action clientReady;
private Action terminated;
private Mock<IConfigurationRepository> configuration;
private Mock<IClientProxy> proxy;
private Mock<ILogger> logger;
private Mock<IProcess> process;
private Mock<IProcessFactory> processFactory;
private Mock<IProxyFactory> proxyFactory;
private Mock<IRuntimeHost> runtimeHost;
private RuntimeInfo runtimeInfo;
private Mock<ISessionData> session;
private ClientOperation sut;
[TestInitialize]
public void Initialize()
{
configuration = new Mock<IConfigurationRepository>();
clientReady = new Action(() => runtimeHost.Raise(h => h.ClientReady += null));
logger = new Mock<ILogger>();
process = new Mock<IProcess>();
processFactory = new Mock<IProcessFactory>();
proxy = new Mock<IClientProxy>();
proxyFactory = new Mock<IProxyFactory>();
runtimeHost = new Mock<IRuntimeHost>();
runtimeInfo = new RuntimeInfo();
session = new Mock<ISessionData>();
terminated = new Action(() =>
{
runtimeHost.Raise(h => h.ClientDisconnected += null);
process.Raise(p => p.Terminated += null, 0);
});
configuration.SetupGet(c => c.CurrentSession).Returns(session.Object);
configuration.SetupGet(c => c.RuntimeInfo).Returns(runtimeInfo);
proxyFactory.Setup(f => f.CreateClientProxy(It.IsAny<string>())).Returns(proxy.Object);
sut = new ClientOperation(configuration.Object, logger.Object, processFactory.Object, proxyFactory.Object, runtimeHost.Object, 0);
}
[TestMethod]
public void MustStartClientWhenPerforming()
{
var result = default(OperationResult);
var response = new AuthenticationResponse { ProcessId = 1234 };
process.SetupGet(p => p.Id).Returns(response.ProcessId);
processFactory.Setup(f => f.StartNew(It.IsAny<string>(), It.IsAny<string[]>())).Returns(process.Object).Callback(clientReady);
proxy.Setup(p => p.RequestAuthentication()).Returns(response);
proxy.Setup(p => p.Connect(It.IsAny<Guid>(), true)).Returns(true);
result = sut.Perform();
session.VerifySet(s => s.ClientProcess = process.Object, Times.Once);
session.VerifySet(s => s.ClientProxy = proxy.Object, Times.Once);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void MustStartClientWhenRepeating()
{
var result = default(OperationResult);
var response = new AuthenticationResponse { ProcessId = 1234 };
process.SetupGet(p => p.Id).Returns(response.ProcessId);
processFactory.Setup(f => f.StartNew(It.IsAny<string>(), It.IsAny<string[]>())).Returns(process.Object).Callback(clientReady);
proxy.Setup(p => p.RequestAuthentication()).Returns(response);
proxy.Setup(p => p.Connect(It.IsAny<Guid>(), true)).Returns(true);
result = sut.Repeat();
session.VerifySet(s => s.ClientProcess = process.Object, Times.Once);
session.VerifySet(s => s.ClientProxy = proxy.Object, Times.Once);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void MustFailStartupIfClientNotStartedWithinTimeout()
{
var result = default(OperationResult);
result = sut.Perform();
session.VerifySet(s => s.ClientProcess = process.Object, Times.Never);
session.VerifySet(s => s.ClientProxy = proxy.Object, Times.Never);
Assert.AreEqual(OperationResult.Failed, result);
}
[TestMethod]
public void MustFailStartupIfConnectionToClientNotEstablished()
{
var result = default(OperationResult);
processFactory.Setup(f => f.StartNew(It.IsAny<string>(), It.IsAny<string[]>())).Returns(process.Object).Callback(clientReady);
proxy.Setup(p => p.Connect(It.IsAny<Guid>(), true)).Returns(false);
result = sut.Perform();
session.VerifySet(s => s.ClientProcess = process.Object, Times.Never);
session.VerifySet(s => s.ClientProxy = proxy.Object, Times.Never);
Assert.AreEqual(OperationResult.Failed, result);
}
[TestMethod]
public void MustFailStartupIfAuthenticationNotSuccessful()
{
var result = default(OperationResult);
var response = new AuthenticationResponse { ProcessId = -1 };
process.SetupGet(p => p.Id).Returns(1234);
processFactory.Setup(f => f.StartNew(It.IsAny<string>(), It.IsAny<string[]>())).Returns(process.Object).Callback(clientReady);
proxy.Setup(p => p.RequestAuthentication()).Returns(response);
proxy.Setup(p => p.Connect(It.IsAny<Guid>(), true)).Returns(true);
result = sut.Perform();
session.VerifySet(s => s.ClientProcess = process.Object, Times.Never);
session.VerifySet(s => s.ClientProxy = proxy.Object, Times.Never);
Assert.AreEqual(OperationResult.Failed, result);
}
[TestMethod]
public void MustStopClientWhenReverting()
{
proxy.Setup(p => p.Disconnect()).Callback(terminated);
PerformNormally();
sut.Revert();
proxy.Verify(p => p.InitiateShutdown(), Times.Once);
proxy.Verify(p => p.Disconnect(), Times.Once);
process.Verify(p => p.Kill(), Times.Never);
session.VerifySet(s => s.ClientProcess = null, Times.Once);
session.VerifySet(s => s.ClientProxy = null, Times.Once);
}
[TestMethod]
public void MustKillClientIfStoppingFailed()
{
process.Setup(p => p.Kill()).Callback(() => process.SetupGet(p => p.HasTerminated).Returns(true));
PerformNormally();
sut.Revert();
process.Verify(p => p.Kill(), Times.AtLeastOnce);
session.VerifySet(s => s.ClientProcess = null, Times.Once);
session.VerifySet(s => s.ClientProxy = null, Times.Once);
}
[TestMethod]
public void MustAttemptToKillFiveTimesThenAbort()
{
PerformNormally();
sut.Revert();
process.Verify(p => p.Kill(), Times.Exactly(5));
session.VerifySet(s => s.ClientProcess = null, Times.Never);
session.VerifySet(s => s.ClientProxy = null, Times.Never);
}
[TestMethod]
public void MustNotStopClientIfAlreadyTerminated()
{
process.SetupGet(p => p.HasTerminated).Returns(true);
sut.Revert();
proxy.Verify(p => p.InitiateShutdown(), Times.Never);
proxy.Verify(p => p.Disconnect(), Times.Never);
process.Verify(p => p.Kill(), Times.Never);
session.VerifySet(s => s.ClientProcess = null, Times.Never);
session.VerifySet(s => s.ClientProxy = null, Times.Never);
}
private void PerformNormally()
{
var response = new AuthenticationResponse { ProcessId = 1234 };
process.SetupGet(p => p.Id).Returns(response.ProcessId);
processFactory.Setup(f => f.StartNew(It.IsAny<string>(), It.IsAny<string[]>())).Returns(process.Object).Callback(clientReady);
proxy.Setup(p => p.RequestAuthentication()).Returns(response);
proxy.Setup(p => p.Connect(It.IsAny<Guid>(), true)).Returns(true);
sut.Perform();
}
}
}

View file

@ -0,0 +1,84 @@
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Contracts.Communication.Hosts;
using SafeExamBrowser.Contracts.Communication.Proxies;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.WindowsApi;
using SafeExamBrowser.Runtime.Behaviour.Operations;
namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
{
[TestClass]
public class ClientTerminationOperationTests
{
private Action clientReady;
private Mock<IConfigurationRepository> configuration;
private Mock<IClientProxy> proxy;
private Mock<ILogger> logger;
private Mock<IProcess> process;
private Mock<IProcessFactory> processFactory;
private Mock<IProxyFactory> proxyFactory;
private Mock<IRuntimeHost> runtimeHost;
private RuntimeInfo runtimeInfo;
private Mock<ISessionData> session;
private ClientTerminationOperation sut;
[TestInitialize]
public void Initialize()
{
configuration = new Mock<IConfigurationRepository>();
clientReady = new Action(() => runtimeHost.Raise(h => h.ClientReady += null));
logger = new Mock<ILogger>();
process = new Mock<IProcess>();
processFactory = new Mock<IProcessFactory>();
proxy = new Mock<IClientProxy>();
proxyFactory = new Mock<IProxyFactory>();
runtimeHost = new Mock<IRuntimeHost>();
runtimeInfo = new RuntimeInfo();
session = new Mock<ISessionData>();
configuration.SetupGet(c => c.CurrentSession).Returns(session.Object);
configuration.SetupGet(c => c.RuntimeInfo).Returns(runtimeInfo);
proxyFactory.Setup(f => f.CreateClientProxy(It.IsAny<string>())).Returns(proxy.Object);
sut = new ClientTerminationOperation(configuration.Object, logger.Object, processFactory.Object, proxyFactory.Object, runtimeHost.Object, 0);
}
[TestMethod]
public void MustDoNothingOnPerform()
{
sut.Perform();
processFactory.VerifyNoOtherCalls();
proxy.VerifyNoOtherCalls();
proxyFactory.VerifyNoOtherCalls();
}
[TestMethod]
public void MustDoNothingOnRevert()
{
sut.Revert();
process.VerifyNoOtherCalls();
proxy.VerifyNoOtherCalls();
runtimeHost.VerifyNoOtherCalls();
}
[TestMethod]
public void MustStartClientOnRepeat()
{
// TODO: Extract static fields from operation -> allows unit testing of this requirement etc.!
Assert.Fail();
}
}
}

View file

@ -21,11 +21,13 @@ using SafeExamBrowser.Runtime.Behaviour.Operations;
namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
{
[TestClass]
public class ServiceConnectionOperationTests
public class ServiceOperationTests
{
private Mock<ILogger> logger;
private Mock<IServiceProxy> service;
private Mock<IConfigurationRepository> configuration;
private Mock<ISessionData> session;
private Settings settings;
private Mock<IProgressIndicator> progressIndicator;
private Mock<IText> text;
private ServiceOperation sut;
@ -36,9 +38,14 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
logger = new Mock<ILogger>();
service = new Mock<IServiceProxy>();
configuration = new Mock<IConfigurationRepository>();
session = new Mock<ISessionData>();
settings = new Settings();
progressIndicator = new Mock<IProgressIndicator>();
text = new Mock<IText>();
configuration.SetupGet(c => c.CurrentSession).Returns(session.Object);
configuration.SetupGet(c => c.CurrentSettings).Returns(settings);
sut = new ServiceOperation(configuration.Object, logger.Object, service.Object, text.Object);
}
@ -58,6 +65,26 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
service.Verify(s => s.Connect(null, true), Times.Exactly(2));
}
[TestMethod]
public void MustStartSessionIfConnected()
{
service.Setup(s => s.Connect(null, true)).Returns(true);
sut.Perform();
service.Verify(s => s.StartSession(It.IsAny<Guid>(), It.IsAny<Settings>()), Times.Once);
}
[TestMethod]
public void MustNotStartSessionIfNotConnected()
{
service.Setup(s => s.Connect(null, true)).Returns(false);
sut.Perform();
service.Verify(s => s.StartSession(It.IsAny<Guid>(), It.IsAny<Settings>()), Times.Never);
}
[TestMethod]
public void MustNotFailIfServiceNotAvailable()
{
@ -124,6 +151,28 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
service.Verify(s => s.Disconnect(), Times.Exactly(2));
}
[TestMethod]
public void MustStopSessionWhenReverting()
{
service.Setup(s => s.Connect(null, true)).Returns(true);
sut.Perform();
sut.Revert();
service.Verify(s => s.StopSession(It.IsAny<Guid>()), Times.Once);
}
[TestMethod]
public void MustNotStopSessionWhenRevertingAndNotConnected()
{
service.Setup(s => s.Connect(null, true)).Returns(false);
sut.Perform();
sut.Revert();
service.Verify(s => s.StopSession(It.IsAny<Guid>()), Times.Never);
}
[TestMethod]
public void MustNotFailWhenDisconnecting()
{

View file

@ -0,0 +1,80 @@
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Contracts.Communication.Hosts;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Runtime.Behaviour.Operations;
namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
{
[TestClass]
public class SessionInitializationOperationTests
{
private SessionInitializationOperation sut;
private Mock<IConfigurationRepository> configuration;
private Mock<ILogger> logger;
private Mock<IRuntimeHost> runtimeHost;
private RuntimeInfo runtimeInfo;
private Mock<ISessionData> session;
[TestInitialize]
public void Initialize()
{
configuration = new Mock<IConfigurationRepository>();
logger = new Mock<ILogger>();
runtimeHost = new Mock<IRuntimeHost>();
runtimeInfo = new RuntimeInfo();
session = new Mock<ISessionData>();
configuration.SetupGet(c => c.CurrentSession).Returns(session.Object);
configuration.SetupGet(c => c.RuntimeInfo).Returns(runtimeInfo);
sut = new SessionInitializationOperation(configuration.Object, logger.Object, runtimeHost.Object);
}
[TestMethod]
public void MustInitializeConfigurationOnPerform()
{
var token = Guid.NewGuid();
session.SetupGet(s => s.StartupToken).Returns(token);
sut.Perform();
configuration.Verify(c => c.InitializeSessionConfiguration(), Times.Once);
runtimeHost.VerifySet(r => r.StartupToken = token, Times.Once);
}
[TestMethod]
public void MustInitializeConfigurationOnRepeat()
{
var token = Guid.NewGuid();
session.SetupGet(s => s.StartupToken).Returns(token);
sut.Repeat();
configuration.Verify(c => c.InitializeSessionConfiguration(), Times.Once);
runtimeHost.VerifySet(r => r.StartupToken = token, Times.Once);
}
[TestMethod]
public void MustDoNothingOnRevert()
{
sut.Revert();
configuration.VerifyNoOtherCalls();
logger.VerifyNoOtherCalls();
runtimeHost.VerifyNoOtherCalls();
}
}
}

View file

@ -82,7 +82,10 @@
<ItemGroup>
<Compile Include="Behaviour\Operations\ConfigurationOperationTests.cs" />
<Compile Include="Behaviour\Operations\KioskModeOperationTests.cs" />
<Compile Include="Behaviour\Operations\ServiceConnectionOperationTests.cs" />
<Compile Include="Behaviour\Operations\ServiceOperationTests.cs" />
<Compile Include="Behaviour\Operations\ClientOperationTests.cs" />
<Compile Include="Behaviour\Operations\ClientTerminationOperationTests.cs" />
<Compile Include="Behaviour\Operations\SessionInitializationOperationTests.cs" />
<Compile Include="Behaviour\RuntimeControllerTests.cs" />
<Compile Include="Communication\RuntimeHostTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />

View file

@ -21,7 +21,7 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
{
internal class ClientOperation : IOperation
{
private const int TEN_SECONDS = 10000;
private readonly int timeout_ms;
protected IConfigurationRepository configuration;
protected ILogger logger;
@ -38,13 +38,15 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
ILogger logger,
IProcessFactory processFactory,
IProxyFactory proxyFactory,
IRuntimeHost runtimeHost)
IRuntimeHost runtimeHost,
int timeout_ms)
{
this.configuration = configuration;
this.logger = logger;
this.processFactory = processFactory;
this.proxyFactory = proxyFactory;
this.runtimeHost = runtimeHost;
this.timeout_ms = timeout_ms;
}
public virtual OperationResult Perform()
@ -84,6 +86,7 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
var clientReady = false;
var clientReadyEvent = new AutoResetEvent(false);
var clientReadyEventHandler = new CommunicationEventHandler(() => clientReadyEvent.Set());
var clientExecutable = configuration.RuntimeInfo.ClientExecutablePath;
var clientLogFile = $"{'"' + configuration.RuntimeInfo.ClientLogFile + '"'}";
var hostUri = configuration.RuntimeInfo.RuntimeAddress;
@ -94,12 +97,12 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
ClientProcess = processFactory.StartNew(clientExecutable, clientLogFile, hostUri, token);
logger.Info("Waiting for client to complete initialization...");
clientReady = clientReadyEvent.WaitOne(TEN_SECONDS);
clientReady = clientReadyEvent.WaitOne(timeout_ms);
runtimeHost.ClientReady -= clientReadyEventHandler;
if (!clientReady)
{
logger.Error($"Failed to start client within {TEN_SECONDS / 1000} seconds!");
logger.Error($"Failed to start client within {timeout_ms / 1000} seconds!");
return false;
}
@ -155,19 +158,19 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
ClientProxy.Disconnect();
logger.Info("Waiting for client to disconnect from runtime communication host...");
disconnected = disconnectedEvent.WaitOne(TEN_SECONDS);
disconnected = disconnectedEvent.WaitOne(timeout_ms);
if (!disconnected)
{
logger.Error($"Client failed to disconnect within {TEN_SECONDS / 1000} seconds!");
logger.Error($"Client failed to disconnect within {timeout_ms / 1000} seconds!");
}
logger.Info("Waiting for client process to terminate...");
terminated = terminatedEvent.WaitOne(TEN_SECONDS);
terminated = terminatedEvent.WaitOne(timeout_ms);
if (!terminated)
{
logger.Error($"Client failed to terminate within {TEN_SECONDS / 1000} seconds!");
logger.Error($"Client failed to terminate within {timeout_ms / 1000} seconds!");
}
runtimeHost.ClientDisconnected -= disconnectedEventHandler;
@ -217,7 +220,7 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
{
logger.Warn("Failed to kill client process. Trying again...");
return TryKillClient(attempt++);
return TryKillClient(++attempt);
}
}
}

View file

@ -23,7 +23,8 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
ILogger logger,
IProcessFactory processFactory,
IProxyFactory proxyFactory,
IRuntimeHost runtimeHost) : base(configuration, logger, processFactory, proxyFactory, runtimeHost)
IRuntimeHost runtimeHost,
int timeout_ms) : base(configuration, logger, processFactory, proxyFactory, runtimeHost, timeout_ms)
{
}

View file

@ -36,6 +36,8 @@ namespace SafeExamBrowser.Runtime
internal void BuildObjectGraph(Action shutdown)
{
const int TEN_SECONDS = 10000;
var args = Environment.GetCommandLineArgs();
var configuration = new ConfigurationRepository();
var nativeMethods = new NativeMethods();
@ -64,9 +66,9 @@ namespace SafeExamBrowser.Runtime
sessionOperations.Enqueue(new ConfigurationOperation(configuration, logger, messageBox, runtimeInfo, text, args));
sessionOperations.Enqueue(new SessionInitializationOperation(configuration, logger, runtimeHost));
sessionOperations.Enqueue(new ServiceOperation(configuration, logger, serviceProxy, text));
sessionOperations.Enqueue(new ClientTerminationOperation(configuration, logger, processFactory, proxyFactory, runtimeHost));
sessionOperations.Enqueue(new ClientTerminationOperation(configuration, logger, processFactory, proxyFactory, runtimeHost, TEN_SECONDS));
sessionOperations.Enqueue(new KioskModeOperation(logger, configuration));
sessionOperations.Enqueue(new ClientOperation(configuration, logger, processFactory, proxyFactory, runtimeHost));
sessionOperations.Enqueue(new ClientOperation(configuration, logger, processFactory, proxyFactory, runtimeHost, TEN_SECONDS));
var bootstrapSequence = new OperationSequence(logger, bootstrapOperations);
var sessionSequence = new OperationSequence(logger, sessionOperations);