From fef879de9f820f414d0e694033bb2f7c5e3c927c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6llner?= Date: Sat, 4 Apr 2020 14:55:31 +0200 Subject: [PATCH] tests + fixes --- .../DeserializationTests.cs | 177 ++++++++++++++- Capnp.Net.Runtime.Tests/TcpRpc.cs | 212 +++++++++++++----- Capnp.Net.Runtime/DeserializerState.cs | 16 +- .../FrameTracing/RpcFrameTracer.cs | 16 +- 4 files changed, 356 insertions(+), 65 deletions(-) diff --git a/Capnp.Net.Runtime.Tests/DeserializationTests.cs b/Capnp.Net.Runtime.Tests/DeserializationTests.cs index d9b2861..1f286b7 100644 --- a/Capnp.Net.Runtime.Tests/DeserializationTests.cs +++ b/Capnp.Net.Runtime.Tests/DeserializationTests.cs @@ -1,4 +1,5 @@ -using Capnproto_test.Capnp.Test; +using Capnp.Net.Runtime.Tests.GenImpls; +using Capnproto_test.Capnp.Test; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Collections.Generic; @@ -791,5 +792,179 @@ namespace Capnp.Net.Runtime.Tests var kind = loed.Cast(_ => _.Kind).Take(1).Single(); Assert.AreEqual(ObjectKind.Nil, kind); } + + [TestMethod] + public void DeserializerStateBadConv() + { + SerializerState s = null; + Assert.ThrowsException(() => (DeserializerState)s); + s = new DynamicSerializerState(); + Assert.ThrowsException(() => (DeserializerState)s); + } + + [TestMethod] + public void BadPointers() + { + var data = new ulong[1]; + var wf = new WireFrame(new Memory[] { new Memory(data) }); + var d0 = DeserializerState.CreateRoot(wf); + Assert.AreEqual(ObjectKind.Nil, d0.Kind); + WirePointer p = default; + p.BeginStruct(1, 0); + p.Offset = 0; + data[0] = p; + Assert.ThrowsException(() => DeserializerState.CreateRoot(wf)); + p.BeginList(ListKind.ListOfBits, 64); + data[0] = p; + Assert.ThrowsException(() => DeserializerState.CreateRoot(wf)); + p.BeginList(ListKind.ListOfBytes, 8); + data[0] = p; + Assert.ThrowsException(() => DeserializerState.CreateRoot(wf)); + p.BeginList(ListKind.ListOfEmpty, 6400); + data[0] = p; + var d1 = DeserializerState.CreateRoot(wf); + p.BeginList(ListKind.ListOfInts, 2); + data[0] = p; + Assert.ThrowsException(() => DeserializerState.CreateRoot(wf)); + p.BeginList(ListKind.ListOfLongs, 1); + data[0] = p; + Assert.ThrowsException(() => DeserializerState.CreateRoot(wf)); + p.BeginList(ListKind.ListOfPointers, 1); + data[0] = p; + Assert.ThrowsException(() => DeserializerState.CreateRoot(wf)); + p.BeginList(ListKind.ListOfShorts, 4); + data[0] = p; + Assert.ThrowsException(() => DeserializerState.CreateRoot(wf)); + p.BeginList(ListKind.ListOfStructs, 1); + data[0] = p; + Assert.ThrowsException(() => DeserializerState.CreateRoot(wf)); + p.SetFarPointer(0, 0, false); + Assert.ThrowsException(() => DeserializerState.CreateRoot(wf)); + p.SetFarPointer(1, 0, false); + Assert.ThrowsException(() => DeserializerState.CreateRoot(wf)); + p.SetFarPointer(0, 1, false); + Assert.ThrowsException(() => DeserializerState.CreateRoot(wf)); + p.SetFarPointer(0, 0, true); + Assert.ThrowsException(() => DeserializerState.CreateRoot(wf)); + + var data2 = new ulong[3]; + var wf2 = new WireFrame(new Memory[] { new Memory(data2) }); + p.BeginList(ListKind.ListOfStructs, 1); + data2[0] = p; + data2[1] = p; + Assert.ThrowsException(() => DeserializerState.CreateRoot(wf2)); + + p.SetFarPointer(0, 1, true); + data2[0] = p; + data2[1] = 0; + Assert.ThrowsException(() => DeserializerState.CreateRoot(wf2)); + + p.SetFarPointer(0, 1, false); + data2[1] = p; + data2[2] = p; + Assert.ThrowsException(() => DeserializerState.CreateRoot(wf2)); + + p.SetFarPointer(0, 2, true); + data2[0] = p; + Assert.ThrowsException(() => DeserializerState.CreateRoot(wf2)); + } + + [TestMethod] + public void ReadCap1() + { + var mb = MessageBuilder.Create(); + var dss = mb.CreateObject(); + dss.SetStruct(0, 1); + DeserializerState ds = dss; + Assert.ThrowsException(() => ds.ReadCap(-1)); + Assert.ThrowsException(() => ds.ReadCap(0)); + } + + [TestMethod] + public void ReadCap2() + { + var mb = MessageBuilder.Create(); + mb.InitCapTable(); + var dss1 = mb.CreateObject(); + var dss2 = mb.CreateObject(); + dss2.SetStruct(1, 1); + dss1.SetStruct(0, 2); + dss1.Link(0, dss2); + dss1.LinkToCapability(1, 7); + var d = (DeserializerState)dss1; + Assert.ThrowsException(() => d.ReadCap(0)); + Assert.ThrowsException(() => d.ReadCap(1)); + } + + [TestMethod] + public void Read1() + { + var mb = MessageBuilder.Create(); + var dss = mb.CreateObject(); + dss.SetStruct(1, 0); + dss.WriteData(0, ulong.MaxValue); + var d = (DeserializerState)dss; + Assert.AreEqual(ushort.MaxValue, d.ReadDataUShort(48)); + Assert.ThrowsException(() => d.ReadDataUShort(49)); + Assert.AreEqual((ushort)0, d.ReadDataUShort(64)); + Assert.IsNotNull(d.StructReadPointer(7)); + Assert.ThrowsException(() => d.RequireCapList()); + } + + [TestMethod] + public void Read2() + { + var mb = MessageBuilder.Create(); + var dss = mb.CreateObject(); + dss.Init(50); + var d = (DeserializerState)dss; + Assert.ThrowsException(() => d.ReadDataUInt(0)); + Assert.ThrowsException(() => d.StructReadPointer(0)); + } + + [TestMethod] + public void ReadCapList() + { + var mb = MessageBuilder.Create(); + mb.InitCapTable(); + var dss = mb.CreateObject(); + dss.SetStruct(0, 1); + var loc = mb.CreateObject>(); + loc.Init(1); + loc[0] = new TestInterfaceImpl2(); + dss.LinkObject(0, loc); + var d = (DeserializerState)dss; + var cl = d.ReadCapList(0); + Assert.AreEqual(1, cl.Count); + Assert.IsNotNull(cl[0]); + } + + [TestMethod] + public void ReadCap() + { + var dss = DynamicSerializerState.CreateForRpc(); + dss.SetStruct(0, 1); + dss.LinkObject(0, new TestInterfaceImpl2()); + var d = (DeserializerState)dss; + Assert.IsNotNull(d.ReadCap(0)); + Assert.IsNotNull(d.ReadCap(0)); + } + + [TestMethod] + public void RequireCap1() + { + var dss = DynamicSerializerState.CreateForRpc(); + dss.SetStruct(1, 1); + var d = (DeserializerState)dss; + Assert.ThrowsException(() => d.RequireCap()); + } + + [TestMethod] + public void RequireCap2() + { + DeserializerState d = default; + d.Kind = ObjectKind.Capability; + Assert.ThrowsException(() => d.RequireCap()); + } } } diff --git a/Capnp.Net.Runtime.Tests/TcpRpc.cs b/Capnp.Net.Runtime.Tests/TcpRpc.cs index b122da4..7836f96 100644 --- a/Capnp.Net.Runtime.Tests/TcpRpc.cs +++ b/Capnp.Net.Runtime.Tests/TcpRpc.cs @@ -7,6 +7,9 @@ using Capnp.Rpc; using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.Extensions.Logging; using System.Diagnostics; +using System.Threading.Tasks.Dataflow; +using Capnp.Net.Runtime.Tests.GenImpls; +using Capnproto_test.Capnp.Test; namespace Capnp.Net.Runtime.Tests { @@ -42,7 +45,7 @@ namespace Capnp.Net.Runtime.Tests }); } - int MediumTimeout => Debugger.IsAttached ? Timeout.Infinite : 2000; + int MediumNonDbgTimeout => Debugger.IsAttached ? Timeout.Infinite : 2000; [TestMethod] public void CreateAndDispose() @@ -66,7 +69,7 @@ namespace Capnp.Net.Runtime.Tests try { client.WhenConnected.Wait(); - SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout); + SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumNonDbgTimeout); Assert.AreEqual(1, server.ConnectionCount); } catch (System.Exception e) @@ -95,13 +98,13 @@ namespace Capnp.Net.Runtime.Tests using (client) { client.WhenConnected.Wait(); - SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout); + SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumNonDbgTimeout); Assert.AreEqual(1, server.ConnectionCount); server.Main = new ProvidedCapabilityMock(); var main = client.GetMain(); var resolving = main as IResolvingCapability; - Assert.IsTrue(resolving.WhenResolved.Wait(MediumTimeout)); + Assert.IsTrue(resolving.WhenResolved.Wait(MediumNonDbgTimeout)); } } @@ -114,12 +117,12 @@ namespace Capnp.Net.Runtime.Tests using (client) { client.WhenConnected.Wait(); - SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout); + SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumNonDbgTimeout); Assert.AreEqual(1, server.ConnectionCount); var main = client.GetMain(); var resolving = main as IResolvingCapability; - Assert.IsTrue(Assert.ThrowsExceptionAsync(() => resolving.WhenResolved).Wait(MediumTimeout)); + Assert.IsTrue(Assert.ThrowsExceptionAsync(() => resolving.WhenResolved).Wait(MediumNonDbgTimeout)); } } @@ -132,19 +135,19 @@ namespace Capnp.Net.Runtime.Tests using (client) { client.WhenConnected.Wait(); - SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout); + SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumNonDbgTimeout); Assert.AreEqual(1, server.ConnectionCount); var mock = new ProvidedCapabilityMock(); server.Main = mock; var main = client.GetMain(); - Assert.IsTrue(main.WhenResolved.Wait(MediumTimeout)); + Assert.IsTrue(main.WhenResolved.Wait(MediumNonDbgTimeout)); var args = DynamicSerializerState.CreateForRpc(); args.SetStruct(1, 0); args.WriteData(0, 123456); using (var answer = main.Call(0x1234567812345678, 0x3333, args)) { - Assert.IsTrue(mock.WhenCalled.Wait(MediumTimeout)); + Assert.IsTrue(mock.WhenCalled.Wait(MediumNonDbgTimeout)); (var interfaceId, var methodId, var inargs, var ct) = mock.WhenCalled.Result; Assert.AreEqual(0x1234567812345678, interfaceId); Assert.AreEqual(0x3333, methodId); @@ -156,7 +159,7 @@ namespace Capnp.Net.Runtime.Tests result.WriteData(0, 654321); mock.Return.SetResult(result); - Assert.IsTrue(answer.WhenReturned.Wait(MediumTimeout)); + Assert.IsTrue(answer.WhenReturned.Wait(MediumNonDbgTimeout)); var outresult = answer.WhenReturned.Result; Assert.AreEqual(ObjectKind.Struct, outresult.Kind); Assert.AreEqual(654321, outresult.ReadDataInt(0)); @@ -173,19 +176,19 @@ namespace Capnp.Net.Runtime.Tests using (client) { client.WhenConnected.Wait(); - SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout); + SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumNonDbgTimeout); Assert.AreEqual(1, server.ConnectionCount); var mock = new ProvidedCapabilityMock(); server.Main = mock; var main = client.GetMain(); - Assert.IsTrue(main.WhenResolved.Wait(MediumTimeout)); + Assert.IsTrue(main.WhenResolved.Wait(MediumNonDbgTimeout)); var args = DynamicSerializerState.CreateForRpc(); args.SetStruct(1, 0); args.WriteData(0, 123456); using (var answer = main.Call(0x1234567812345678, 0x3333, args)) { - Assert.IsTrue(mock.WhenCalled.Wait(MediumTimeout)); + Assert.IsTrue(mock.WhenCalled.Wait(MediumNonDbgTimeout)); (var interfaceId, var methodId, var inargs, var ct) = mock.WhenCalled.Result; Assert.AreEqual(0x1234567812345678, interfaceId); Assert.AreEqual(0x3333, methodId); @@ -194,7 +197,7 @@ namespace Capnp.Net.Runtime.Tests mock.Return.SetCanceled(); - Assert.IsTrue(Assert.ThrowsExceptionAsync(() => answer.WhenReturned).Wait(MediumTimeout)); + Assert.IsTrue(Assert.ThrowsExceptionAsync(() => answer.WhenReturned).Wait(MediumNonDbgTimeout)); } } } @@ -212,21 +215,21 @@ namespace Capnp.Net.Runtime.Tests try { client.WhenConnected.Wait(); - SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout); + SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumNonDbgTimeout); Assert.AreEqual(1, server.ConnectionCount); var mock = new ProvidedCapabilityMock(); server.Main = mock; var main = client.GetMain(); var resolving = main as IResolvingCapability; - Assert.IsTrue(resolving.WhenResolved.Wait(MediumTimeout)); + Assert.IsTrue(resolving.WhenResolved.Wait(MediumNonDbgTimeout)); var args = DynamicSerializerState.CreateForRpc(); args.SetStruct(1, 0); args.WriteData(0, 123456); CancellationToken ctx; using (var answer = main.Call(0x1234567812345678, 0x3333, args)) { - Assert.IsTrue(mock.WhenCalled.Wait(MediumTimeout)); + Assert.IsTrue(mock.WhenCalled.Wait(MediumNonDbgTimeout)); (var interfaceId, var methodId, var inargs, var ct) = mock.WhenCalled.Result; Assert.AreEqual(0x1234567812345678, interfaceId); Assert.AreEqual(0x3333, methodId); @@ -235,7 +238,7 @@ namespace Capnp.Net.Runtime.Tests ctx = ct; } - Assert.IsTrue(SpinWait.SpinUntil(() => ctx.IsCancellationRequested, MediumTimeout)); + Assert.IsTrue(SpinWait.SpinUntil(() => ctx.IsCancellationRequested, MediumNonDbgTimeout)); } finally { @@ -256,13 +259,13 @@ namespace Capnp.Net.Runtime.Tests try { client.WhenConnected.Wait(); - SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout); + SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumNonDbgTimeout); Assert.AreEqual(1, server.ConnectionCount); var mock = new ProvidedCapabilityMock(); server.Main = mock; var main = client.GetMain(); - Assert.IsTrue(main.WhenResolved.Wait(MediumTimeout)); + Assert.IsTrue(main.WhenResolved.Wait(MediumNonDbgTimeout)); var args = DynamicSerializerState.CreateForRpc(); args.SetStruct(1, 0); args.WriteData(0, 123456); @@ -270,7 +273,7 @@ namespace Capnp.Net.Runtime.Tests IPromisedAnswer answer; using (answer = main.Call(0x1234567812345678, 0x3333, args)) { - Assert.IsTrue(mock.WhenCalled.Wait(MediumTimeout)); + Assert.IsTrue(mock.WhenCalled.Wait(MediumNonDbgTimeout)); (var interfaceId, var methodId, var inargs, var ct) = mock.WhenCalled.Result; Assert.AreEqual(0x1234567812345678, interfaceId); Assert.AreEqual(0x3333, methodId); @@ -279,7 +282,7 @@ namespace Capnp.Net.Runtime.Tests ctx = ct; } - Assert.IsTrue(SpinWait.SpinUntil(() => ctx.IsCancellationRequested, MediumTimeout)); + Assert.IsTrue(SpinWait.SpinUntil(() => ctx.IsCancellationRequested, MediumNonDbgTimeout)); var mbr = MessageBuilder.Create(); mbr.InitCapTable(); @@ -290,7 +293,7 @@ namespace Capnp.Net.Runtime.Tests // Even after the client cancelled the call, the server must still send // a response. - Assert.IsTrue(answer.WhenReturned.ContinueWith(t => { }).Wait(MediumTimeout)); + Assert.IsTrue(answer.WhenReturned.ContinueWith(t => { }).Wait(MediumNonDbgTimeout)); } finally { @@ -315,19 +318,19 @@ namespace Capnp.Net.Runtime.Tests using (client) { client.WhenConnected.Wait(); - SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout); + SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumNonDbgTimeout); Assert.AreEqual(1, server.ConnectionCount); var mock = new ProvidedCapabilityMock(); server.Main = mock; var main = client.GetMain(); - Assert.IsTrue(main.WhenResolved.Wait(MediumTimeout)); + Assert.IsTrue(main.WhenResolved.Wait(MediumNonDbgTimeout)); var args = DynamicSerializerState.CreateForRpc(); args.SetStruct(1, 0); args.WriteData(0, 123456); using (var answer = main.Call(0x1234567812345678, 0x3333, args)) { - Assert.IsTrue(mock.WhenCalled.Wait(MediumTimeout)); + Assert.IsTrue(mock.WhenCalled.Wait(MediumNonDbgTimeout)); (var interfaceId, var methodId, var inargs, var ct) = mock.WhenCalled.Result; Assert.AreEqual(0x1234567812345678, interfaceId); Assert.AreEqual(0x3333, methodId); @@ -337,7 +340,7 @@ namespace Capnp.Net.Runtime.Tests mock.Return.SetException(new MyTestException()); var exTask = Assert.ThrowsExceptionAsync(() => answer.WhenReturned); - Assert.IsTrue(exTask.Wait(MediumTimeout)); + Assert.IsTrue(exTask.Wait(MediumNonDbgTimeout)); Assert.IsTrue(exTask.Result.Message.Contains(new MyTestException().Message)); } } @@ -352,19 +355,19 @@ namespace Capnp.Net.Runtime.Tests using (client) { client.WhenConnected.Wait(); - SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout); + SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumNonDbgTimeout); Assert.AreEqual(1, server.ConnectionCount); var mock = new ProvidedCapabilityMock(); server.Main = mock; var main = client.GetMain(); - Assert.IsTrue(main.WhenResolved.Wait(MediumTimeout)); + Assert.IsTrue(main.WhenResolved.Wait(MediumNonDbgTimeout)); var args = DynamicSerializerState.CreateForRpc(); args.SetStruct(1, 0); args.WriteData(0, 123456); using (var answer = main.Call(0x1234567812345678, 0x3333, args)) { - Assert.IsTrue(mock.WhenCalled.Wait(MediumTimeout)); + Assert.IsTrue(mock.WhenCalled.Wait(MediumNonDbgTimeout)); var pipelined = (BareProxy)CapabilityReflection.CreateProxy(answer.Access( new MemberAccessPath(new MemberAccessPath.MemberAccess[] { new MemberAccessPath.StructMemberAccess(1) }))); @@ -391,10 +394,10 @@ namespace Capnp.Net.Runtime.Tests mock.Return.SetResult(result); - Assert.IsTrue(answer.WhenReturned.Wait(MediumTimeout)); + Assert.IsTrue(answer.WhenReturned.Wait(MediumNonDbgTimeout)); Assert.IsFalse(ct.IsCancellationRequested); - Assert.IsTrue(mock2.WhenCalled.Wait(MediumTimeout)); + Assert.IsTrue(mock2.WhenCalled.Wait(MediumNonDbgTimeout)); (var interfaceId2, var methodId2, var inargs2, var ct2) = mock2.WhenCalled.Result; Assert.AreEqual(0x8765432187654321, interfaceId2); @@ -407,7 +410,7 @@ namespace Capnp.Net.Runtime.Tests result2.WriteData(0, 222222); mock2.Return.SetResult(result2); - Assert.IsTrue(answer2.WhenReturned.Wait(MediumTimeout)); + Assert.IsTrue(answer2.WhenReturned.Wait(MediumNonDbgTimeout)); var outresult2 = answer2.WhenReturned.Result; Assert.AreEqual(ObjectKind.Struct, outresult2.Kind); Assert.AreEqual(222222, outresult2.ReadDataInt(0)); @@ -425,19 +428,19 @@ namespace Capnp.Net.Runtime.Tests using (client) { client.WhenConnected.Wait(); - SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout); + SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumNonDbgTimeout); Assert.AreEqual(1, server.ConnectionCount); var mock = new ProvidedCapabilityMock(); server.Main = mock; var main = client.GetMain(); - Assert.IsTrue(main.WhenResolved.Wait(MediumTimeout)); + Assert.IsTrue(main.WhenResolved.Wait(MediumNonDbgTimeout)); var args = DynamicSerializerState.CreateForRpc(); args.SetStruct(1, 0); args.WriteData(0, 123456); using (var answer = main.Call(0x1234567812345678, 0x3333, args)) { - Assert.IsTrue(mock.WhenCalled.Wait(MediumTimeout)); + Assert.IsTrue(mock.WhenCalled.Wait(MediumNonDbgTimeout)); (var interfaceId, var methodId, var inargs, var ct) = mock.WhenCalled.Result; Assert.AreEqual(0x1234567812345678, interfaceId); @@ -467,8 +470,8 @@ namespace Capnp.Net.Runtime.Tests using (var answer2 = pipelined.Call(0x8765432187654321, 0x4444, args2)) { - Assert.IsTrue(answer.WhenReturned.Wait(MediumTimeout)); - Assert.IsTrue(mock2.WhenCalled.Wait(MediumTimeout)); + Assert.IsTrue(answer.WhenReturned.Wait(MediumNonDbgTimeout)); + Assert.IsTrue(mock2.WhenCalled.Wait(MediumNonDbgTimeout)); (var interfaceId2, var methodId2, var inargs2, var ct2) = mock2.WhenCalled.Result; Assert.AreEqual(0x8765432187654321, interfaceId2); @@ -481,7 +484,7 @@ namespace Capnp.Net.Runtime.Tests result2.WriteData(0, 222222); mock2.Return.SetResult(result2); - Assert.IsTrue(answer2.WhenReturned.Wait(MediumTimeout)); + Assert.IsTrue(answer2.WhenReturned.Wait(MediumNonDbgTimeout)); var outresult2 = answer2.WhenReturned.Result; Assert.AreEqual(ObjectKind.Struct, outresult2.Kind); Assert.AreEqual(222222, outresult2.ReadDataInt(0)); @@ -501,19 +504,19 @@ namespace Capnp.Net.Runtime.Tests using (client) { client.WhenConnected.Wait(); - SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout); + SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumNonDbgTimeout); Assert.AreEqual(1, server.ConnectionCount); var mock = new ProvidedCapabilityMock(); server.Main = mock; var main = client.GetMain(); - Assert.IsTrue(main.WhenResolved.Wait(MediumTimeout)); + Assert.IsTrue(main.WhenResolved.Wait(MediumNonDbgTimeout)); var args = DynamicSerializerState.CreateForRpc(); args.SetStruct(1, 0); args.WriteData(0, 123456); using (var answer = main.Call(0x1234567812345678, 0x3333, args)) { - Assert.IsTrue(mock.WhenCalled.Wait(MediumTimeout)); + Assert.IsTrue(mock.WhenCalled.Wait(MediumNonDbgTimeout)); var pipelined = (BareProxy)CapabilityReflection.CreateProxy(answer.Access(new MemberAccessPath(new MemberAccessPath.MemberAccess[] { new MemberAccessPath.StructMemberAccess(1) }))); @@ -545,7 +548,7 @@ namespace Capnp.Net.Runtime.Tests mock.Return.SetResult(result); - Assert.IsTrue(answer.WhenReturned.Wait(MediumTimeout)); + Assert.IsTrue(answer.WhenReturned.Wait(MediumNonDbgTimeout)); Assert.IsFalse(ct.IsCancellationRequested); var args4 = DynamicSerializerState.CreateForRpc(); @@ -564,10 +567,10 @@ namespace Capnp.Net.Runtime.Tests var call4 = mock2.WhenCalled; var call5 = mock2.WhenCalled; - Assert.IsTrue(call2.Wait(MediumTimeout)); - Assert.IsTrue(call3.Wait(MediumTimeout)); - Assert.IsTrue(call4.Wait(MediumTimeout)); - Assert.IsTrue(call5.Wait(MediumTimeout)); + Assert.IsTrue(call2.Wait(MediumNonDbgTimeout)); + Assert.IsTrue(call3.Wait(MediumNonDbgTimeout)); + Assert.IsTrue(call4.Wait(MediumNonDbgTimeout)); + Assert.IsTrue(call5.Wait(MediumNonDbgTimeout)); Assert.AreEqual(0x1111111111111111, call2.Result.InterfaceId); Assert.AreEqual(0x2222222222222222, call3.Result.InterfaceId); @@ -594,10 +597,10 @@ namespace Capnp.Net.Runtime.Tests ret5.WriteData(0, -4); call5.Result.Result.SetResult(ret5); - Assert.IsTrue(answer2.WhenReturned.Wait(MediumTimeout)); - Assert.IsTrue(answer3.WhenReturned.Wait(MediumTimeout)); - Assert.IsTrue(answer4.WhenReturned.Wait(MediumTimeout)); - Assert.IsTrue(answer5.WhenReturned.Wait(MediumTimeout)); + Assert.IsTrue(answer2.WhenReturned.Wait(MediumNonDbgTimeout)); + Assert.IsTrue(answer3.WhenReturned.Wait(MediumNonDbgTimeout)); + Assert.IsTrue(answer4.WhenReturned.Wait(MediumNonDbgTimeout)); + Assert.IsTrue(answer5.WhenReturned.Wait(MediumNonDbgTimeout)); Assert.AreEqual(-1, answer2.WhenReturned.Result.ReadDataInt(0)); Assert.AreEqual(-2, answer3.WhenReturned.Result.ReadDataInt(0)); @@ -618,20 +621,20 @@ namespace Capnp.Net.Runtime.Tests using (client) { client.WhenConnected.Wait(); - SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout); + SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumNonDbgTimeout); Assert.AreEqual(1, server.ConnectionCount); var mock = new ProvidedCapabilityMock(); server.Main = mock; var main = client.GetMain(); - Assert.IsTrue(main.WhenResolved.Wait(MediumTimeout)); + Assert.IsTrue(main.WhenResolved.Wait(MediumNonDbgTimeout)); var args = DynamicSerializerState.CreateForRpc(); args.SetStruct(1, 0); args.WriteData(0, 123456); BareProxy pipelined; using (var answer = main.Call(0x1234567812345678, 0x3333, args)) { - Assert.IsTrue(mock.WhenCalled.Wait(MediumTimeout)); + Assert.IsTrue(mock.WhenCalled.Wait(MediumNonDbgTimeout)); pipelined = (BareProxy)CapabilityReflection.CreateProxy( answer.Access(new MemberAccessPath(new MemberAccessPath.MemberAccess[] { new MemberAccessPath.StructMemberAccess(1) }))); @@ -665,20 +668,20 @@ namespace Capnp.Net.Runtime.Tests using (client) { client.WhenConnected.Wait(); - SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout); + SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumNonDbgTimeout); Assert.AreEqual(1, server.ConnectionCount); var mock = new ProvidedCapabilityMock(); server.Main = mock; var main = client.GetMain(); - Assert.IsTrue(main.WhenResolved.Wait(MediumTimeout)); + Assert.IsTrue(main.WhenResolved.Wait(MediumNonDbgTimeout)); var args = DynamicSerializerState.CreateForRpc(); args.SetStruct(1, 0); args.WriteData(0, 123456); IPromisedAnswer answer2; using (var answer = main.Call(0x1234567812345678, 0x3333, args)) { - Assert.IsTrue(mock.WhenCalled.Wait(MediumTimeout)); + Assert.IsTrue(mock.WhenCalled.Wait(MediumNonDbgTimeout)); var pipelined = (BareProxy)CapabilityReflection.CreateProxy(answer.Access(new MemberAccessPath(new MemberAccessPath.MemberAccess[] { new MemberAccessPath.StructMemberAccess(1) }))); @@ -696,7 +699,7 @@ namespace Capnp.Net.Runtime.Tests var tcs = new TaskCompletionSource(); using (ct.Register(() => tcs.SetResult(0))) { - Assert.IsTrue(tcs.Task.Wait(MediumTimeout)); + Assert.IsTrue(tcs.Task.Wait(MediumNonDbgTimeout)); } var mock2 = new ProvidedCapabilityMock(); @@ -710,9 +713,100 @@ namespace Capnp.Net.Runtime.Tests mock.Return.SetResult(result); Assert.IsTrue(Assert.ThrowsExceptionAsync( - () => answer2.WhenReturned).Wait(MediumTimeout)); + () => answer2.WhenReturned).Wait(MediumNonDbgTimeout)); } } } + + [TestMethod] + public void Server() + { + var cbb = new BufferBlock(); + var server = new TcpRpcServer(); + server.Main = new TestInterfaceImpl2(); + bool init = true; + var tracer = new FrameTracing.RpcFrameTracer(Console.Out); + server.OnConnectionChanged += (s, a) => + { + var c = a.Connection; + if (init) + { + Assert.ThrowsException(() => c.AttachTracer(null)); + c.AttachTracer(tracer); + Assert.ThrowsException(() => c.InjectMidlayer(null)); + c.InjectMidlayer(_ => _); + Assert.IsFalse(c.IsComputing); + Assert.IsFalse(c.IsWaitingForData); + Assert.AreEqual(ConnectionState.Initializing, c.State); + Assert.IsNotNull(c.RemotePort); + Assert.AreEqual(TcpPort, c.LocalPort); + Assert.AreEqual(0L, c.RecvCount); + Assert.AreEqual(0L, c.SendCount); + } + else + { + Assert.ThrowsException(() => c.AttachTracer(tracer)); + Assert.ThrowsException(() => c.InjectMidlayer(_ => _)); + Assert.AreEqual(ConnectionState.Down, c.State); + } + + cbb.Post(c); + }; + + Assert.ThrowsException(() => server.StopListening()); + + server.StartAccepting(IPAddress.Any, TcpPort); + Assert.IsTrue(server.IsAlive); + Assert.ThrowsException(() => server.StartAccepting(IPAddress.Any, TcpPort)); + + var server2 = new TcpRpcServer(); + Assert.ThrowsException(() => server2.StartAccepting(IPAddress.Any, TcpPort)); + + var client1 = new TcpRpcClient("localhost", TcpPort); + var c1 = cbb.Receive(TimeSpan.FromMilliseconds(MediumNonDbgTimeout)); + Assert.IsNotNull(c1); + Assert.AreEqual(1, server.ConnectionCount); + Assert.AreEqual(c1, server.Connections[0]); + Assert.AreEqual(ConnectionState.Active, c1.State); + var proxy = client1.GetMain(); + Assert.IsTrue(proxy is IResolvingCapability r && r.WhenResolved.Wait(MediumNonDbgTimeout)); + Assert.IsTrue(c1.RecvCount > 0); + Assert.IsTrue(c1.SendCount > 0); + + var client2 = new TcpRpcClient("localhost", TcpPort); + var c2 = cbb.Receive(TimeSpan.FromMilliseconds(MediumNonDbgTimeout)); + Assert.IsNotNull(c2); + Assert.AreEqual(2, server.ConnectionCount); + Assert.AreEqual(c2, server.Connections[1]); + + init = false; + + client1.Dispose(); + var c1d = cbb.Receive(TimeSpan.FromMilliseconds(MediumNonDbgTimeout)); + Assert.IsNotNull(c1d); + Assert.AreEqual(1, server.ConnectionCount); + Assert.AreEqual(c2, server.Connections[0]); + Assert.IsTrue(SpinWait.SpinUntil(() => c1d.State == ConnectionState.Down, MediumNonDbgTimeout)); + + client2.Dispose(); + var c2d = cbb.Receive(TimeSpan.FromMilliseconds(MediumNonDbgTimeout)); + Assert.IsNotNull(c2d); + Assert.AreEqual(0, server.ConnectionCount); + Assert.IsTrue(SpinWait.SpinUntil(() => c2d.State == ConnectionState.Down, MediumNonDbgTimeout)); + + server.StopListening(); + Assert.IsFalse(server.IsAlive); + Assert.ThrowsException(() => server.StopListening()); + + for (int i = 0; i < 100; i++) + { + server.StartAccepting(IPAddress.Any, TcpPort); + Assert.IsTrue(server.IsAlive); + server.StopListening(); + Assert.IsFalse(server.IsAlive); + } + + server.Dispose(); + } } } diff --git a/Capnp.Net.Runtime/DeserializerState.cs b/Capnp.Net.Runtime/DeserializerState.cs index 6fbb996..5773466 100644 --- a/Capnp.Net.Runtime/DeserializerState.cs +++ b/Capnp.Net.Runtime/DeserializerState.cs @@ -203,6 +203,10 @@ namespace Capnp GetRawBytes(); break; + case ObjectKind.ListOfShorts: + GetRawShorts(); + break; + case ObjectKind.ListOfInts: GetRawInts(); break; @@ -303,6 +307,8 @@ namespace Capnp case ListKind.ListOfStructs: { + if (Offset >= CurrentSegment.Length) + throw new DeserializationException("List of composites pointer exceeds segment bounds"); WirePointer tag = CurrentSegment[Offset]; if (tag.Kind != PointerKind.Struct) throw new DeserializationException("Unexpected: List of composites with non-struct type tag"); @@ -326,6 +332,10 @@ namespace Capnp if (pointer.IsDoubleFar) { CurrentSegmentIndex = pointer.TargetSegmentIndex; + + if (pointer.LandingPadOffset >= CurrentSegment.Length - 1) + throw new DeserializationException("Error decoding double-far pointer: exceeds segment bounds"); + Offset = 0; WirePointer pointer1 = CurrentSegment[pointer.LandingPadOffset]; @@ -373,14 +383,14 @@ namespace Capnp /// /// Offset relative to this.Offset within current segment /// the low-level capability object, or null if it is a null pointer - /// offset negative or out of range + /// offset negative or out of range /// capability table not set /// not a capability pointer or invalid capability index internal Rpc.ConsumedCapability? DecodeCapPointer(int offset) { if (offset < 0) { - throw new IndexOutOfRangeException(nameof(offset)); + throw new ArgumentOutOfRangeException(nameof(offset)); } if (Caps == null) @@ -677,7 +687,7 @@ namespace Capnp throw new DeserializationException("Expected a capability"); if (Caps == null) - throw new InvalidOperationException("Capability table not set. This is a bug."); + throw new InvalidOperationException("Capability table not set"); return (Rpc.CapabilityReflection.CreateProxy(Caps[(int)CapabilityIndex]) as T)!; } diff --git a/Capnp.Net.Runtime/FrameTracing/RpcFrameTracer.cs b/Capnp.Net.Runtime/FrameTracing/RpcFrameTracer.cs index fdd4ac2..62d7be9 100644 --- a/Capnp.Net.Runtime/FrameTracing/RpcFrameTracer.cs +++ b/Capnp.Net.Runtime/FrameTracing/RpcFrameTracer.cs @@ -18,15 +18,26 @@ namespace Capnp.FrameTracing readonly Stopwatch _timer = new Stopwatch(); readonly TextWriter _traceWriter; + readonly bool _disposeWriter; /// /// Constructs an instance /// /// textual logging target - public RpcFrameTracer(TextWriter traceWriter) + public RpcFrameTracer(TextWriter traceWriter): this(traceWriter, true) + { + } + + /// + /// Constructs an instance + /// + /// textual logging target + /// whether to dispose the writer when tracing is finished + public RpcFrameTracer(TextWriter traceWriter, bool dispose) { _traceWriter = traceWriter ?? throw new ArgumentNullException(nameof(traceWriter)); _traceWriter.WriteLine(Header); + _disposeWriter = dispose; } /// @@ -35,7 +46,8 @@ namespace Capnp.FrameTracing public void Dispose() { _traceWriter.WriteLine(""); - _traceWriter.Dispose(); + if (_disposeWriter) + _traceWriter.Dispose(); } void RenderMessageTarget(MessageTarget.READER target, FrameDirection dir)