diff --git a/UaClient.UnitTests/UnitTests/ServiceExtensionsTests.cs b/UaClient.UnitTests/UnitTests/ServiceExtensionsTests.cs new file mode 100644 index 0000000..797b9fc --- /dev/null +++ b/UaClient.UnitTests/UnitTests/ServiceExtensionsTests.cs @@ -0,0 +1,187 @@ +using FluentAssertions; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Workstation.ServiceModel.Ua; +using Xunit; + +namespace Workstation.UaClient.UnitTests +{ + public class ServiceExtensionsTests + { + private static Task Never() => Task.Delay(-1); + + private static async Task Never(T value) + { + await Never(); + return value; + } + + [Fact] + public void ToVariantArray() + { + var input = new object[] { 1, 2, 3, 70 }; + var expected = new Variant[] { 1, 2, 3, 70 }; + + input.ToVariantArray() + .Should().BeEquivalentTo(expected); + } + + [Fact] + public void ToVariantArrayEmpty() + { + var input = new object[] { }; + var expected = new Variant[] { }; + + input.ToVariantArray() + .Should().BeEquivalentTo(expected); + } + + [Fact] + public void ToVariantArrayNull() + { + var input = default(object[]); + + input.Invoking(i => i.ToVariantArray()) + .Should().Throw(); + } + + [Fact] + public void ToObjectArray() + { + var input = new Variant[] { 1, 2, 3, 70 }; + var expected = new object[] { 1, 2, 3, 70 }; + + input.ToObjectArray() + .Should().BeEquivalentTo(expected); + } + + [Fact] + public void ToObjectArrayEmpty() + { + var input = new Variant[] { }; + var expected = new object[] { }; + + input.ToObjectArray() + .Should().BeEquivalentTo(expected); + } + + [Fact] + public void ToObjectArrayNull() + { + var input = default(Variant[]); + + input.Invoking(i => i.ToObjectArray()) + .Should().Throw(); + } + + [Fact] + public void WithCancellationCompleted() + { + var task = Task.CompletedTask; + + task.WithCancellation(default).IsCompleted + .Should().BeTrue(); + } + + [Fact] + public async Task WithCancellationFastTask() + { + using (var tcs = new CancellationTokenSource()) + { + var fast = Task.Delay(1); + var task = fast.WithCancellation(tcs.Token); + + await task.Invoking(t => t) + .Should().NotThrowAsync(); + } + } + + [Fact] + public async Task WithCancellation() + { + using (var tcs = new CancellationTokenSource()) + { + var never = Never(); + var task = never.WithCancellation(tcs.Token); + tcs.Cancel(); + + await task.Invoking(t => t) + .Should().ThrowAsync(); + } + } + + [Fact] + public void ValueWithCancellationCompleted() + { + var task = Task.FromResult(10); + + task.WithCancellation(default).IsCompleted + .Should().BeTrue(); + } + + [Fact] + public async Task ValueWithCancellation() + { + using (var tcs = new CancellationTokenSource()) + { + var never = Never(10); + var task = never.WithCancellation(tcs.Token); + tcs.Cancel(); + + await task.Invoking(t => t) + .Should().ThrowAsync(); + } + } + + [Fact] + public void WithTimeoutAfterCompleted() + { + var task = Task.CompletedTask; + + task.WithTimeoutAfter(-1).IsCompleted + .Should().BeTrue(); + } + + [Fact] + public async Task WithTimeoutAfterFastTask() + { + var fast = Task.Delay(1); + var task = fast.WithTimeoutAfter(-1); + + await task.Invoking(t => t) + .Should().NotThrowAsync(); + } + + [Fact] + public async Task WithTimeoutAfter() + { + var never = Never(); + var task = never.WithTimeoutAfter(0); + + await task.Invoking(t => t) + .Should().ThrowAsync(); + } + + [Fact] + public void ValueWithTimeoutAfterCompleted() + { + var task = Task.FromResult(10); + + task.WithTimeoutAfter(-1).IsCompleted + .Should().BeTrue(); + } + + [Fact] + public async Task ValueWithTimeoutAfter() + { + var never = Never(10); + var task = never.WithTimeoutAfter(0); + + await task.Invoking(t => t) + .Should().ThrowAsync(); + } + } +} diff --git a/UaClient.UnitTests/UnitTests/ServiceSetTests.cs b/UaClient.UnitTests/UnitTests/ServiceSetTests.cs index e7dd509..bd8fc04 100644 --- a/UaClient.UnitTests/UnitTests/ServiceSetTests.cs +++ b/UaClient.UnitTests/UnitTests/ServiceSetTests.cs @@ -730,5 +730,138 @@ public async Task UnregisterNodesAsync() channel.Request .Should().BeSameAs(request); } + + /* + * ServiceExtensions + * Tests for: ServiceExtensions.css + */ + [Fact] + public async Task ConditionRefreshAsync() + { + var response = new CallResponse + { + Results = new CallMethodResult[] + { + new CallMethodResult + { + StatusCode = StatusCodes.BadAttributeIdInvalid + } + } + }; + var channel = new TestRequestChannel(response); + + var ret = await channel.ConditionRefreshAsync(subscriptionId: 100); + + ret + .Should().Be((StatusCode)StatusCodes.BadAttributeIdInvalid); + + channel.Request + .Should().BeEquivalentTo(new + { + MethodsToCall = new[] + { + new + { + ObjectId = NodeId.Parse(ObjectTypeIds.ConditionType), + MethodId = NodeId.Parse(MethodIds.ConditionType_ConditionRefresh), + InputArguments = new Variant[] { 100u } + } + } + }); + } + + [Fact] + public async Task AcknowledgeAsync() + { + var condition = new AcknowledgeableCondition(); + var comment = new LocalizedText("Comment"); + var response = new CallResponse + { + Results = new CallMethodResult[] + { + new CallMethodResult + { + StatusCode = StatusCodes.BadAttributeIdInvalid + } + } + }; + var channel = new TestRequestChannel(response); + + var ret = await channel.AcknowledgeAsync(condition, comment); + + ret + .Should().Be((StatusCode)StatusCodes.BadAttributeIdInvalid); + + channel.Request + .Should().BeEquivalentTo(new + { + MethodsToCall = new[] + { + new + { + ObjectId = condition.ConditionId, + MethodId = NodeId.Parse(MethodIds.AcknowledgeableConditionType_Acknowledge), + InputArguments = new Variant[] { condition.EventId, comment } + } + } + }); + } + + [Fact] + public void AcknowledgeAsyncNull() + { + var response = new CallResponse(); + var channel = new TestRequestChannel(response); + + channel.Invoking(c => c.AcknowledgeAsync(null, "Comment")) + .Should().Throw(); + } + + [Fact] + public async Task ConfirmAsync() + { + var condition = new AcknowledgeableCondition(); + var comment = new LocalizedText("Comment"); + var response = new CallResponse + { + Results = new CallMethodResult[] + { + new CallMethodResult + { + StatusCode = StatusCodes.BadAttributeIdInvalid + } + } + }; + var channel = new TestRequestChannel(response); + + var ret = await channel.ConfirmAsync(condition, comment); + + ret + .Should().Be((StatusCode)StatusCodes.BadAttributeIdInvalid); + + channel.Request + .Should().BeEquivalentTo(new + { + MethodsToCall = new[] + { + new + { + ObjectId = condition.ConditionId, + MethodId = NodeId.Parse(MethodIds.AcknowledgeableConditionType_Confirm), + InputArguments = new Variant[] { condition.EventId, comment } + } + } + }); + } + + [Fact] + public void ConfirmAsyncNull() + { + var response = new CallResponse(); + var channel = new TestRequestChannel(response); + + channel.Invoking(c => c.ConfirmAsync(null, "Comment")) + .Should().Throw(); + } } } diff --git a/UaClient/ServiceModel/Ua/StatusCode.cs b/UaClient/ServiceModel/Ua/StatusCode.cs index 8951449..d139d74 100644 --- a/UaClient/ServiceModel/Ua/StatusCode.cs +++ b/UaClient/ServiceModel/Ua/StatusCode.cs @@ -3,7 +3,7 @@ namespace Workstation.ServiceModel.Ua { - public struct StatusCode + public readonly struct StatusCode { private const uint SeverityMask = 0xC0000000u; private const uint SeverityGood = 0x00000000u; diff --git a/UaClient/ServiceModel/Ua/Variant.cs b/UaClient/ServiceModel/Ua/Variant.cs index f84b88f..ab1483e 100644 --- a/UaClient/ServiceModel/Ua/Variant.cs +++ b/UaClient/ServiceModel/Ua/Variant.cs @@ -40,7 +40,7 @@ public enum VariantType DiagnosticInfo = 25, } - public struct Variant + public readonly struct Variant { public static readonly Variant Null = default(Variant);