Files
2022-03-30 11:30:34 +02:00

707 lines
28 KiB
C#

/* ========================================================================
* Copyright (c) 2005-2020 The OPC Foundation, Inc. All rights reserved.
*
* OPC Foundation MIT License 1.00
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* The complete license agreement can be found here:
* http://opcfoundation.org/License/MIT/1.00/
* ======================================================================*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using NUnit.Framework;
using Opc.Ua.Configuration;
using Opc.Ua.Server.Tests;
using Quickstarts.ReferenceServer;
namespace Opc.Ua.Client.Tests
{
/// <summary>
/// Client tests.
/// </summary>
[TestFixture, Category("Client")]
[SetCulture("en-us"), SetUICulture("en-us")]
[TestFixtureSource(nameof(FixtureArgs))]
[NonParallelizable]
[MemoryDiagnoser]
[DisassemblyDiagnoser]
public class ClientTest : ClientTestFramework
{
public ClientTest(string uriScheme = Utils.UriSchemeOpcTcp) :
base(uriScheme)
{
}
#region DataPointSources
public static readonly NodeId[] TypeSystems = {
ObjectIds.OPCBinarySchema_TypeSystem,
ObjectIds.XmlSchema_TypeSystem
};
#endregion
#region Test Setup
/// <summary>
/// Set up a Server and a Client instance.
/// </summary>
[OneTimeSetUp]
public new Task OneTimeSetUp()
{
SupportsExternalServerUrl = true;
return base.OneTimeSetUp();
}
/// <summary>
/// Tear down the Server and the Client.
/// </summary>
[OneTimeTearDown]
public new Task OneTimeTearDownAsync()
{
return base.OneTimeTearDownAsync();
}
/// <summary>
/// Test setup.
/// </summary>
[SetUp]
public new Task SetUp()
{
return base.SetUp();
}
/// <summary>
/// Test teardown.
/// </summary>
[TearDown]
public new Task TearDown()
{
return base.TearDown();
}
#endregion
#region Benchmark Setup
/// <summary>
/// Global Setup for benchmarks.
/// </summary>
[GlobalSetup]
public new void GlobalSetup()
{
base.GlobalSetup();
}
/// <summary>
/// Global cleanup for benchmarks.
/// </summary>
[GlobalCleanup]
public new void GlobalCleanup()
{
base.GlobalCleanup();
}
#endregion
#region Test Methods
[Test, Order(100)]
public async Task GetEndpointsAsync()
{
var endpointConfiguration = EndpointConfiguration.Create();
endpointConfiguration.OperationTimeout = 10000;
using (var client = DiscoveryClient.Create(ServerUrl, endpointConfiguration))
{
Endpoints = await client.GetEndpointsAsync(null).ConfigureAwait(false);
TestContext.Out.WriteLine("Endpoints:");
foreach (var endpoint in Endpoints)
{
TestContext.Out.WriteLine("{0}", endpoint.Server.ApplicationName);
TestContext.Out.WriteLine(" {0}", endpoint.Server.ApplicationUri);
TestContext.Out.WriteLine(" {0}", endpoint.EndpointUrl);
TestContext.Out.WriteLine(" {0}", endpoint.EncodingSupport);
TestContext.Out.WriteLine(" {0}/{1}/{2}", endpoint.SecurityLevel, endpoint.SecurityMode, endpoint.SecurityPolicyUri);
if (endpoint.ServerCertificate != null)
{
using (var cert = new X509Certificate2(endpoint.ServerCertificate))
{
TestContext.Out.WriteLine(" [{0}]", cert.Thumbprint);
}
}
else
{
TestContext.Out.WriteLine(" [no certificate]");
}
foreach (var userIdentity in endpoint.UserIdentityTokens)
{
TestContext.Out.WriteLine(" {0}", userIdentity.TokenType);
TestContext.Out.WriteLine(" {0}", userIdentity.PolicyId);
TestContext.Out.WriteLine(" {0}", userIdentity.SecurityPolicyUri);
}
}
}
}
[Test, Order(100)]
public async Task FindServersAsync()
{
var endpointConfiguration = EndpointConfiguration.Create();
endpointConfiguration.OperationTimeout = 10000;
using (var client = DiscoveryClient.Create(ServerUrl, endpointConfiguration))
{
var servers = await client.FindServersAsync(null).ConfigureAwait(false);
foreach (var server in servers)
{
TestContext.Out.WriteLine("{0}", server.ApplicationName);
TestContext.Out.WriteLine(" {0}", server.ApplicationUri);
TestContext.Out.WriteLine(" {0}", server.ApplicationType);
TestContext.Out.WriteLine(" {0}", server.ProductUri);
foreach (var discoveryUrl in server.DiscoveryUrls)
{
TestContext.Out.WriteLine(" {0}", discoveryUrl);
}
}
}
}
[Test, Order(110)]
public async Task InvalidConfiguration()
{
var applicationInstance = new ApplicationInstance() {
ApplicationName = ClientFixture.Config.ApplicationName
};
Assert.NotNull(applicationInstance);
ApplicationConfiguration config = await applicationInstance.Build(ClientFixture.Config.ApplicationUri, ClientFixture.Config.ProductUri)
.AsClient()
.AddSecurityConfiguration(ClientFixture.Config.SecurityConfiguration.ApplicationCertificate.SubjectName)
.Create().ConfigureAwait(false);
}
[Theory, Order(200)]
public async Task Connect(string securityPolicy)
{
var session = await ClientFixture.ConnectAsync(ServerUrl, securityPolicy, Endpoints).ConfigureAwait(false);
Assert.NotNull(session);
var result = session.Close();
Assert.NotNull(result);
session.Dispose();
}
[Test, Order(210)]
public async Task ConnectAndReconnectAsync()
{
const int connectTimeout = MaxTimeout;
var session = await ClientFixture.ConnectAsync(ServerUrl, SecurityPolicies.Basic256Sha256, Endpoints).ConfigureAwait(false);
Assert.NotNull(session);
ManualResetEvent quitEvent = new ManualResetEvent(false);
var reconnectHandler = new SessionReconnectHandler();
reconnectHandler.BeginReconnect(session, connectTimeout / 5,
(object sender, EventArgs e) => {
// ignore callbacks from discarded objects.
if (!Object.ReferenceEquals(sender, reconnectHandler))
{
return;
}
session = reconnectHandler.Session;
reconnectHandler.Dispose();
quitEvent.Set();
});
var timeout = quitEvent.WaitOne(connectTimeout);
Assert.True(timeout);
var result = session.Close();
Assert.NotNull(result);
session.Dispose();
}
[Test, Order(300)]
public new void GetOperationLimits()
{
base.GetOperationLimits();
}
[Test]
public void ReadPublicProperties()
{
TestContext.Out.WriteLine("Identity : {0}", Session.Identity);
TestContext.Out.WriteLine("IdentityHistory : {0}", Session.IdentityHistory);
TestContext.Out.WriteLine("NamespaceUris : {0}", Session.NamespaceUris);
TestContext.Out.WriteLine("ServerUris : {0}", Session.ServerUris);
TestContext.Out.WriteLine("SystemContext : {0}", Session.SystemContext);
TestContext.Out.WriteLine("Factory : {0}", Session.Factory);
TestContext.Out.WriteLine("TypeTree : {0}", Session.TypeTree);
TestContext.Out.WriteLine("FilterContext : {0}", Session.FilterContext);
TestContext.Out.WriteLine("PreferredLocales : {0}", Session.PreferredLocales);
TestContext.Out.WriteLine("DataTypeSystem : {0}", Session.DataTypeSystem);
TestContext.Out.WriteLine("Subscriptions : {0}", Session.Subscriptions);
TestContext.Out.WriteLine("SubscriptionCount: {0}", Session.SubscriptionCount);
TestContext.Out.WriteLine("DefaultSubscription: {0}", Session.DefaultSubscription);
TestContext.Out.WriteLine("LastKeepAliveTime: {0}", Session.LastKeepAliveTime);
TestContext.Out.WriteLine("KeepAliveInterval: {0}", Session.KeepAliveInterval);
Session.KeepAliveInterval += 1000;
TestContext.Out.WriteLine("KeepAliveInterval: {0}", Session.KeepAliveInterval);
Session.KeepAliveInterval -= 1000;
TestContext.Out.WriteLine("KeepAliveInterval: {0}", Session.KeepAliveInterval);
TestContext.Out.WriteLine("KeepAliveStopped : {0}", Session.KeepAliveStopped);
TestContext.Out.WriteLine("OutstandingRequestCount : {0}", Session.OutstandingRequestCount);
TestContext.Out.WriteLine("DefunctRequestCount : {0}", Session.DefunctRequestCount);
TestContext.Out.WriteLine("GoodPublishRequestCount : {0}", Session.GoodPublishRequestCount);
}
[Test]
public void ChangePreferredLocales()
{
// change locale
var localeCollection = new StringCollection() { "de-de", "en-us" };
Session.ChangePreferredLocales(localeCollection);
}
[Test]
public void ReadValue()
{
// Test ReadValue
_ = Session.ReadValue(VariableIds.Server_ServerRedundancy_RedundancySupport, typeof(Int32));
_ = Session.ReadValue(VariableIds.Server_ServerStatus, typeof(ServerStatusDataType));
var sre = Assert.Throws<ServiceResultException>(() => Session.ReadValue(VariableIds.Server_ServerStatus, typeof(ServiceHost)));
Assert.AreEqual(StatusCodes.BadTypeMismatch, sre.StatusCode);
}
[Test]
public void ReadValues()
{
var namespaceUris = Session.NamespaceUris;
var testSet = GetTestSetStatic(namespaceUris).ToList();
testSet.AddRange(GetTestSetSimulation(namespaceUris));
foreach (var nodeId in testSet)
{
var dataValue = Session.ReadValue(nodeId);
Assert.NotNull(dataValue);
Assert.NotNull(dataValue.Value);
}
}
[Test]
public void ReadDataTypeDefinition()
{
// Test Read a DataType Node
var node = Session.ReadNode(DataTypeIds.ProgramDiagnosticDataType);
Assert.NotNull(node);
var dataTypeNode = (DataTypeNode)node;
Assert.NotNull(dataTypeNode);
var dataTypeDefinition = dataTypeNode.DataTypeDefinition;
Assert.NotNull(dataTypeDefinition);
Assert.True(dataTypeDefinition is ExtensionObject);
Assert.NotNull(dataTypeDefinition.Body);
Assert.True(dataTypeDefinition.Body is StructureDefinition);
StructureDefinition structureDefinition = dataTypeDefinition.Body as StructureDefinition;
Assert.AreEqual(ObjectIds.ProgramDiagnosticDataType_Encoding_DefaultBinary, structureDefinition.DefaultEncodingId);
}
[Theory, Order(400)]
public async Task BrowseFullAddressSpace(string securityPolicy)
{
if (OperationLimits == null) { GetOperationLimits(); }
var requestHeader = new RequestHeader();
requestHeader.Timestamp = DateTime.UtcNow;
requestHeader.TimeoutHint = MaxTimeout;
// Session
Session session;
if (securityPolicy != null)
{
session = await ClientFixture.ConnectAsync(ServerUrl, securityPolicy, Endpoints).ConfigureAwait(false);
}
else
{
session = Session;
}
var clientTestServices = new ClientTestServices(session);
ReferenceDescriptions = CommonTestWorkers.BrowseFullAddressSpaceWorker(clientTestServices, requestHeader, OperationLimits);
if (securityPolicy != null)
{
session.Close();
session.Dispose();
}
}
[Test, Order(410)]
public async Task ReadDisplayNames()
{
if (ReferenceDescriptions == null) { await BrowseFullAddressSpace(null).ConfigureAwait(false); }
var nodeIds = ReferenceDescriptions.Select(n => ExpandedNodeId.ToNodeId(n.NodeId, Session.NamespaceUris)).ToList();
if (OperationLimits.MaxNodesPerRead > 0 &&
nodeIds.Count > OperationLimits.MaxNodesPerRead)
{
var sre = Assert.Throws<ServiceResultException>(() => Session.ReadDisplayName(nodeIds, out var displayNames, out var errors));
Assert.AreEqual(StatusCodes.BadTooManyOperations, sre.StatusCode);
while (nodeIds.Count > 0)
{
Session.ReadDisplayName(nodeIds.Take((int)OperationLimits.MaxNodesPerRead).ToArray(), out var displayNames, out var errors);
foreach (var name in displayNames)
{
TestContext.Out.WriteLine("{0}", name);
}
nodeIds = nodeIds.Skip((int)OperationLimits.MaxNodesPerRead).ToList();
}
}
else
{
Session.ReadDisplayName(nodeIds, out var displayNames, out var errors);
foreach (var name in displayNames)
{
TestContext.Out.WriteLine("{0}", name);
}
}
}
[Test, Order(480)]
public void Subscription()
{
var requestHeader = new RequestHeader();
requestHeader.Timestamp = DateTime.UtcNow;
requestHeader.TimeoutHint = MaxTimeout;
var clientTestServices = new ClientTestServices(Session);
CommonTestWorkers.SubscriptionTest(clientTestServices, requestHeader);
}
/// <summary>
/// Load Ua types in node cache.
/// </summary>
[Test, Order(500)]
public void NodeCache_LoadUaDefinedTypes()
{
INodeCache nodeCache = Session.NodeCache;
Assert.IsNotNull(nodeCache);
// clear node cache
nodeCache.Clear();
// load the predefined types
nodeCache.LoadUaDefinedTypes(Session.SystemContext);
// reload the predefined types
nodeCache.LoadUaDefinedTypes(Session.SystemContext);
}
/// <summary>
/// Browse all variables in the objects folder.
/// </summary>
[Test, Order(510)]
public void NodeCache_BrowseAllVariables()
{
var result = new List<INode>();
var nodesToBrowse = new ExpandedNodeIdCollection {
ObjectIds.ObjectsFolder
};
Session.NodeCache.Clear();
Session.NodeCache.LoadUaDefinedTypes(Session.SystemContext);
while (nodesToBrowse.Count > 0)
{
var nextNodesToBrowse = new ExpandedNodeIdCollection();
foreach (var node in nodesToBrowse)
{
try
{
var organizers = Session.NodeCache.FindReferences(
node,
ReferenceTypeIds.HierarchicalReferences,
false,
true);
var objectNodes = organizers.Where(n => n is ObjectNode);
nextNodesToBrowse.AddRange(objectNodes.Select(n => n.NodeId));
var variableNodes = organizers.Where(n => n is VariableNode);
nextNodesToBrowse.AddRange(variableNodes.Select(n => n.NodeId).ToList());
result.AddRange(variableNodes);
}
catch (ServiceResultException sre)
{
if (sre.StatusCode == StatusCodes.BadUserAccessDenied)
{
TestContext.Out.WriteLine($"Access denied: Skip node {node}.");
}
}
}
nodesToBrowse = nextNodesToBrowse;
if (result.Count > MaxReferences)
{
break;
}
}
TestContext.Out.WriteLine("Browsed {0} variables", result.Count);
}
/// <summary>
/// Load Ua types in node cache.
/// </summary>
[Test, Order(520)]
public void NodeCache_References()
{
INodeCache nodeCache = Session.NodeCache;
Assert.IsNotNull(nodeCache);
// ensure the predefined types are loaded
nodeCache.LoadUaDefinedTypes(Session.SystemContext);
// check on all reference type ids
var refTypeDictionary = typeof(ReferenceTypeIds).GetFields(BindingFlags.Public | BindingFlags.Static)
.Where(f => f.FieldType == typeof(NodeId))
.ToDictionary(f => f.Name, f => (NodeId)f.GetValue(null));
TestContext.Out.WriteLine("Testing {0} references", refTypeDictionary.Count);
foreach (var property in refTypeDictionary)
{
TestContext.Out.WriteLine("FindReferenceTypeName({0})={1}", property.Value, property.Key);
// find the Qualified Name
var qn = nodeCache.FindReferenceTypeName(property.Value);
Assert.NotNull(qn);
Assert.AreEqual(property.Key, qn.Name);
// find the node by name
var refId = nodeCache.FindReferenceType(new QualifiedName(property.Key));
Assert.NotNull(refId);
Assert.AreEqual(property.Value, refId);
// is the node id known?
var isKnown = nodeCache.IsKnown(property.Value);
Assert.IsTrue(isKnown);
// is it a reference?
var isTypeOf = nodeCache.IsTypeOf(
NodeId.ToExpandedNodeId(refId, Session.NamespaceUris),
NodeId.ToExpandedNodeId(ReferenceTypeIds.References, Session.NamespaceUris));
Assert.IsTrue(isTypeOf);
// negative test
isTypeOf = nodeCache.IsTypeOf(
NodeId.ToExpandedNodeId(refId, Session.NamespaceUris),
NodeId.ToExpandedNodeId(DataTypeIds.Byte, Session.NamespaceUris));
Assert.IsFalse(isTypeOf);
var subTypes = nodeCache.FindSubTypes(NodeId.ToExpandedNodeId(refId, Session.NamespaceUris));
Assert.NotNull(subTypes);
}
}
[Test, Order(550)]
public async Task Read()
{
if (ReferenceDescriptions == null)
{
await BrowseFullAddressSpace(null).ConfigureAwait(false);
}
foreach (var reference in ReferenceDescriptions.Take(MaxReferences))
{
var nodeId = ExpandedNodeId.ToNodeId(reference.NodeId, Session.NamespaceUris);
var node = Session.ReadNode(nodeId);
Assert.NotNull(node);
TestContext.Out.WriteLine("NodeId: {0} Node: {1}", nodeId, node);
if (node is VariableNode)
{
try
{
var value = Session.ReadValue(nodeId);
Assert.NotNull(value);
TestContext.Out.WriteLine("-- Value {0} ", value);
}
catch (ServiceResultException sre)
{
TestContext.Out.WriteLine("-- Read Value {0} ", sre.Message);
}
}
}
}
[Test, Order(600)]
public async Task NodeCache_Read()
{
if (ReferenceDescriptions == null)
{
await BrowseFullAddressSpace(null).ConfigureAwait(false);
}
foreach (var reference in ReferenceDescriptions.Take(MaxReferences))
{
var nodeId = ExpandedNodeId.ToNodeId(reference.NodeId, Session.NamespaceUris);
var node = Session.NodeCache.Find(reference.NodeId);
TestContext.Out.WriteLine("NodeId: {0} Node: {1}", nodeId, node);
}
}
[Test, Order(610)]
public void FetchTypeTree()
{
Session.FetchTypeTree(NodeId.ToExpandedNodeId(DataTypeIds.BaseDataType, Session.NamespaceUris));
}
[Test, Order(620)]
public void ReadAvailableEncodings()
{
var sre = Assert.Throws<ServiceResultException>(() => Session.ReadAvailableEncodings(DataTypeIds.BaseDataType));
Assert.AreEqual(StatusCodes.BadNodeIdInvalid, sre.StatusCode);
var encoding = Session.ReadAvailableEncodings(VariableIds.Server_ServerStatus_CurrentTime);
Assert.NotNull(encoding);
Assert.AreEqual(0, encoding.Count);
}
[Test, Order(700)]
public async Task LoadStandardDataTypeSystem()
{
var sre = Assert.ThrowsAsync<ServiceResultException>(async () => {
var t = await Session.LoadDataTypeSystem(ObjectIds.ObjectAttributes_Encoding_DefaultJson).ConfigureAwait(false);
});
Assert.AreEqual(StatusCodes.BadNodeIdInvalid, sre.StatusCode);
var typeSystem = await Session.LoadDataTypeSystem().ConfigureAwait(false);
Assert.NotNull(typeSystem);
typeSystem = await Session.LoadDataTypeSystem(ObjectIds.OPCBinarySchema_TypeSystem).ConfigureAwait(false);
Assert.NotNull(typeSystem);
typeSystem = await Session.LoadDataTypeSystem(ObjectIds.XmlSchema_TypeSystem).ConfigureAwait(false);
Assert.NotNull(typeSystem);
}
[Test, Order(710)]
[TestCaseSource(nameof(TypeSystems))]
public async Task LoadAllServerDataTypeSystems(NodeId dataTypeSystem)
{
// find the dictionary for the description.
Browser browser = new Browser(Session) {
BrowseDirection = BrowseDirection.Forward,
ReferenceTypeId = ReferenceTypeIds.HasComponent,
IncludeSubtypes = false,
NodeClassMask = 0
};
ReferenceDescriptionCollection references = browser.Browse(dataTypeSystem);
Assert.NotNull(references);
TestContext.Out.WriteLine(" Found {0} references", references.Count);
// read all type dictionaries in the type system
foreach (var r in references)
{
NodeId dictionaryId = ExpandedNodeId.ToNodeId(r.NodeId, Session.NamespaceUris);
TestContext.Out.WriteLine(" ReadDictionary {0} {1}", r.BrowseName.Name, dictionaryId);
var dictionaryToLoad = new DataDictionary(Session);
await dictionaryToLoad.Load(dictionaryId, r.BrowseName.Name).ConfigureAwait(false);
// internal API for testing only
var dictionary = dictionaryToLoad.ReadDictionary(dictionaryId);
// TODO: workaround known issues in the Xml type system.
// https://mantis.opcfoundation.org/view.php?id=7393
if (dataTypeSystem.Equals(ObjectIds.XmlSchema_TypeSystem))
{
try
{
await dictionaryToLoad.Validate(dictionary, true).ConfigureAwait(false);
}
catch (Exception ex)
{
Assert.Inconclusive(ex.Message);
}
}
else
{
await dictionaryToLoad.Validate(dictionary, true).ConfigureAwait(false);
}
}
}
/// <summary>
/// Transfer the subscription using the native service calls, not the client SDK layer.
/// </summary>
/// <remarks>
/// Create a subscription with a monitored item using the native service calls.
/// Create a secondary Session.
/// </remarks>
[Theory, Order(800)]
[NonParallelizable]
public async Task TransferSubscriptionNative(bool sendInitialData)
{
Session transferSession = null;
try
{
var requestHeader = new RequestHeader {
Timestamp = DateTime.UtcNow,
TimeoutHint = MaxTimeout
};
// to validate the behavior of the sendInitialValue flag,
// use a static variable to avoid sampled notifications in publish requests
var namespaceUris = Session.NamespaceUris;
NodeId[] testSet = CommonTestWorkers.NodeIdTestSetStatic.Select(n => ExpandedNodeId.ToNodeId(n, namespaceUris)).ToArray();
var clientTestServices = new ClientTestServices(Session);
CommonTestWorkers.CreateSubscriptionForTransfer(clientTestServices, requestHeader, testSet, out var subscriptionIds);
TestContext.Out.WriteLine("Transfer SubscriptionIds: {0}", subscriptionIds[0]);
transferSession = await ClientFixture.ConnectAsync(ServerUrl, SecurityPolicies.Basic256Sha256, Endpoints).ConfigureAwait(false);
Assert.AreNotEqual(Session.SessionId, transferSession.SessionId);
requestHeader = new RequestHeader {
Timestamp = DateTime.UtcNow,
TimeoutHint = MaxTimeout
};
var transferTestServices = new ClientTestServices(transferSession);
CommonTestWorkers.TransferSubscriptionTest(transferTestServices, requestHeader, subscriptionIds, sendInitialData, false);
// verify the notification of message transfer
requestHeader = new RequestHeader {
Timestamp = DateTime.UtcNow,
TimeoutHint = MaxTimeout
};
CommonTestWorkers.VerifySubscriptionTransferred(clientTestServices, requestHeader, subscriptionIds, true);
transferSession.Close();
}
finally
{
transferSession?.Dispose();
}
}
#endregion
#region Benchmarks
/// <summary>
/// Benchmark wrapper for browse tests.
/// </summary>
[Benchmark]
public async Task BrowseFullAddressSpaceBenchmark()
{
await BrowseFullAddressSpace(null).ConfigureAwait(false);
}
#endregion
#region Private Methods
#endregion
}
}