2020-04-10 19:23:16 +02:00

1202 lines
47 KiB
C#

using Capnp.FrameTracing;
using Capnp.Net.Runtime.Tests.GenImpls;
using Capnp.Rpc;
using Capnproto_test.Capnp.Test;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Capnp.Net.Runtime.Tests
{
[TestClass]
public class TcpRpcInterop: TestBase
{
Process StartProcess(ProcessStartInfo processStartInfo)
{
try
{
return Process.Start(processStartInfo);
}
catch (Win32Exception exception) when (exception.ErrorCode == 2 || exception.ErrorCode == 3)
{
Assert.Fail($"Did not find test executable {processStartInfo.FileName}. Did you build CapnpCompatTest.sln in Release configuration?");
}
catch (System.Exception exception)
{
Assert.Fail($"Could not execute {processStartInfo.FileName}: {exception.Message}");
}
return null;
}
Process _currentProcess;
bool TryLaunchCompatTestProcess(string whichTest, Action<StreamReader> test)
{
string myPath = Path.GetDirectoryName(typeof(TcpRpcInterop).Assembly.Location);
string config;
#if DEBUG
config = "Debug";
#else
config = "Release";
#endif
string path = Path.Combine(myPath, $@"..\..\..\..\{config}\CapnpCompatTest.exe");
path = Path.GetFullPath(path);
string arguments = $"{whichTest} 127.0.0.1:{TcpPort}";
var startInfo = new ProcessStartInfo(path, arguments)
{
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardOutput = true,
RedirectStandardInput = true
};
using (_currentProcess = StartProcess(startInfo))
using (var job = new Job())
{
job.AddProcess(_currentProcess.Handle);
try
{
try
{
_currentProcess.StandardError.ReadToEndAsync().ContinueWith(t => Console.Error.WriteLine(t.Result));
var firstLine = _currentProcess.StandardOutput.ReadLineAsync();
Assert.IsTrue(firstLine.Wait(MediumNonDbgTimeout), "Problem after launching test process");
Assert.IsNotNull(firstLine.Result, "Problem after launching test process");
Assert.IsTrue(firstLine.Result.StartsWith("Listening") || firstLine.Result.StartsWith("Connecting"),
"Problem after launching test process");
}
catch (AssertFailedException exception)
{
Logger.LogError(exception.Message);
return false;
}
test(_currentProcess.StandardOutput);
return true;
}
finally
{
try
{
_currentProcess.Kill();
}
catch
{
}
}
}
}
void LaunchCompatTestProcess(string whichTest, Action<StreamReader> test)
{
for (int retry = 0; retry < 5; retry++)
{
if (TryLaunchCompatTestProcess(whichTest, test))
return;
if (whichTest.StartsWith("server:"))
PrepareNextTest();
}
Assert.Fail("Problem after launching test process");
}
void SendInput(string line)
{
_currentProcess.StandardInput.WriteLine(line);
}
void AssertOutput(StreamReader stdout, string expected)
{
var line = stdout.ReadLineAsync();
Assert.IsTrue(line.Wait(MediumNonDbgTimeout));
Assert.AreEqual(expected, line.Result);
}
[TestInitialize]
public void PrepareNextTest()
{
IncrementTcpPort();
}
[TestMethod]
public void BasicClient()
{
LaunchCompatTestProcess("server:Interface", stdout =>
{
using (var client = new TcpRpcClient("localhost", TcpPort))
{
client.WhenConnected.Wait();
using (var main = client.GetMain<ITestInterface>())
{
var request1 = main.Foo(123, true, default);
var request3 = Assert.ThrowsExceptionAsync<RpcException>(() => main.Bar(default));
var s = new TestAllTypes();
Common.InitTestMessage(s);
var request2 = main.Baz(s, default);
AssertOutput(stdout, "foo 123 1");
Assert.IsTrue(request1.Wait(MediumNonDbgTimeout));
Assert.AreEqual("foo", request1.Result);
Assert.IsTrue(request2.Wait(MediumNonDbgTimeout));
AssertOutput(stdout, "baz");
AssertOutput(stdout, "baz fin");
Assert.IsTrue(request3.Wait(MediumNonDbgTimeout));
}
}
});
}
[TestMethod]
public void BasicServer()
{
using (var server = SetupServer())
{
var counters = new Counters();
server.Main = new TestInterfaceImpl(counters);
LaunchCompatTestProcess("client:Basic", stdout =>
{
AssertOutput(stdout, "Basic test start");
AssertOutput(stdout, "Basic test end");
Assert.AreEqual(2, counters.CallCount);
});
}
}
[TestMethod]
public void PipelineClient()
{
LaunchCompatTestProcess("server:Pipeline", stdout =>
{
stdout.ReadToEndAsync().ContinueWith(t => Console.WriteLine(t.Result));
using (var client = new TcpRpcClient("localhost", TcpPort))
{
client.WhenConnected.Wait();
using (var main = client.GetMain<ITestPipeline>())
{
var chainedCallCount = new Counters();
var request = main.GetCap(234, new TestInterfaceImpl(chainedCallCount), default);
using (var box = request.OutBox_Cap())
{
var pipelineRequest = box.Foo(321, false, default);
using (var box2 = ((Proxy)box).Cast<ITestExtends>(false))
{
var pipelineRequest2 = box2.Grault(default);
Assert.IsTrue(pipelineRequest.Wait(MediumNonDbgTimeout));
Assert.IsTrue(pipelineRequest2.Wait(MediumNonDbgTimeout));
Assert.AreEqual("bar", pipelineRequest.Result);
Common.CheckTestMessage(pipelineRequest2.Result);
Assert.AreEqual(1, chainedCallCount.CallCount);
}
}
request.ContinueWith(t => t.Result.Item2.Cap.Dispose());
}
}
});
}
[TestMethod]
public void PipelineServer()
{
using (var server = SetupServer())
{
var counters = new Counters();
server.Main = new TestPipelineImpl(counters);
LaunchCompatTestProcess("client:Pipelining", stdout =>
{
AssertOutput(stdout, "Pipelining test start");
AssertOutput(stdout, "foo 123 1");
AssertOutput(stdout, "~");
AssertOutput(stdout, "Pipelining test end");
Assert.AreEqual(3, counters.CallCount);
});
}
}
[TestMethod]
public void ReleaseClient()
{
LaunchCompatTestProcess("server:MoreStuff", stdout =>
{
using (var client = new TcpRpcClient("localhost", TcpPort))
{
client.WhenConnected.Wait();
using (var main = client.GetMain<ITestMoreStuff>())
{
var task1 = main.GetHandle(default);
var task2 = main.GetHandle(default);
Assert.IsTrue(task1.Wait(MediumNonDbgTimeout));
Assert.IsTrue(task2.Wait(MediumNonDbgTimeout));
AssertOutput(stdout, "getHandle");
AssertOutput(stdout, "++");
AssertOutput(stdout, "getHandle");
AssertOutput(stdout, "++");
task1.Result.Dispose();
AssertOutput(stdout, "--");
task2.Result.Dispose();
AssertOutput(stdout, "--");
}
}
});
}
[TestMethod]
public void ReleaseServer()
{
using (var server = SetupServer())
{
var counters = new Counters();
server.Main = new TestMoreStuffImpl(counters);
LaunchCompatTestProcess("client:Release", stdout =>
{
AssertOutput(stdout, "Release test start");
AssertOutput(stdout, "sync");
Assert.AreEqual(2, counters.HandleCount);
SendInput("x");
AssertOutput(stdout, "handle1 null");
Assert.IsTrue(SpinWait.SpinUntil(() => counters.HandleCount == 1, MediumNonDbgTimeout));
SendInput("x");
AssertOutput(stdout, "handle2 null");
Assert.IsTrue(SpinWait.SpinUntil(() => counters.HandleCount == 1, MediumNonDbgTimeout));
SendInput("x");
AssertOutput(stdout, "promise null");
Assert.IsTrue(SpinWait.SpinUntil(() => counters.HandleCount == 0, MediumNonDbgTimeout));
SendInput("x");
AssertOutput(stdout, "Release test end");
});
}
}
[TestMethod]
public void ReleaseOnCancelClient()
{
// Since we have a threaded model, there is no way to deterministically provoke the situation
// where Cancel and Finish message cross paths. Instead, we'll do a lot of such requests and
// later on verify that the handle count is 0.
int iterationCount = 5000;
LaunchCompatTestProcess("server:MoreStuff", stdout =>
{
using (var client = new TcpRpcClient("localhost", TcpPort))
{
client.WhenConnected.Wait();
using (var main = client.GetMain<ITestMoreStuff>())
{
((Proxy)main).WhenResolved.Wait(MediumNonDbgTimeout);
async Task VerifyOutput()
{
int handleCount = 0;
for (int i = 0; i < 2 * iterationCount; i++)
{
string line = await stdout.ReadLineAsync();
switch (line)
{
case "getHandle":
line = await stdout.ReadLineAsync();
Assert.AreEqual("++", line);
++handleCount;
Assert.IsTrue(handleCount <= iterationCount);
break;
case "--":
Assert.IsTrue(handleCount > 0);
--handleCount;
break;
default:
Assert.Fail("Unexpected output");
break;
}
}
Assert.AreEqual(0, handleCount);
}
var verifyOutputTask = VerifyOutput();
var taskList = new List<Task>();
for (int i = 0; i < iterationCount; i++)
{
var task = main.GetHandle(default);
async Task TerminateAnswer()
{
try
{
using (var proxy = await task)
{
}
}
catch (TaskCanceledException)
{
}
}
taskList.Add(TerminateAnswer());
}
// Ensure that all answers return (probably in canceled state)
Assert.IsTrue(Task.WhenAll(taskList).Wait(LargeNonDbgTimeout));
Assert.IsTrue(verifyOutputTask.Wait(LargeNonDbgTimeout));
// Not part of original test. Ensure that there is no unwanted extra output
// arising from the test sequence above.
var sync = main.GetCallSequence(0, default);
AssertOutput(stdout, "getCallSequence");
}
}
});
}
[TestMethod]
public void ReleaseOnCancelServer()
{
using (var server = SetupServer())
{
var counters = new Counters();
server.Main = new TestMoreStuffImpl(counters);
LaunchCompatTestProcess("client:ReleaseOnCancel", stdout =>
{
AssertOutput(stdout, "ReleaseOnCancel test start");
AssertOutput(stdout, "ReleaseOnCancel test end");
Assert.IsTrue(SpinWait.SpinUntil(() => counters.HandleCount == 0, MediumNonDbgTimeout), $"Handle count stuck at {counters.HandleCount}");
});
}
}
[TestMethod]
[TestCategory("Coverage")]
public void TestTailCallClient()
{
LaunchCompatTestProcess("server:TailCaller", stdout =>
{
using (var client = new TcpRpcClient())
{
var tracer = new RpcFrameTracer(Console.Out);
client.AttachTracer(tracer);
client.Connect("localhost", TcpPort);
client.WhenConnected.Wait();
using (var main = client.GetMain<ITestTailCaller>())
{
var calleeCallCount = new Counters();
var callee = new TestTailCalleeImpl(calleeCallCount);
var promise = main.Foo(456, callee, default);
using (var c = promise.C())
{
var dependentCall0 = c.GetCallSequence(0, default);
Assert.IsTrue(promise.Wait(MediumNonDbgTimeout));
Assert.AreEqual(456u, promise.Result.I);
Assert.AreEqual("from TestTailCaller", promise.Result.T);
var dependentCall1 = c.GetCallSequence(0, default);
var dependentCall2 = c.GetCallSequence(0, default);
AssertOutput(stdout, "foo");
Assert.IsTrue(dependentCall0.Wait(MediumNonDbgTimeout));
Assert.IsTrue(dependentCall1.Wait(MediumNonDbgTimeout));
Assert.IsTrue(dependentCall2.Wait(MediumNonDbgTimeout));
}
Assert.AreEqual(1, calleeCallCount.CallCount);
}
}
});
}
[TestMethod, Ignore]
// For details on why this test is ignored, see https://github.com/capnproto/capnproto/issues/876
public void TestTailCallServer()
{
using (var server = SetupServer())
{
var tracer = new RpcFrameTracer(Console.Out);
server.OnConnectionChanged += (s, a) =>
{
if (a.Connection.State == ConnectionState.Initializing)
a.Connection.AttachTracer(tracer);
};
var counters = new Counters();
server.Main = new TestTailCallerImpl(counters);
LaunchCompatTestProcess("client:TailCall", stdout =>
{
AssertOutput(stdout, "TailCall test start");
AssertOutput(stdout, "foo");
AssertOutput(stdout, "TailCall test end");
Assert.AreEqual(1, counters.CallCount);
});
}
}
[TestMethod]
public void CancelationServer()
{
LaunchCompatTestProcess("server:MoreStuff", stdout =>
{
stdout.ReadToEndAsync().ContinueWith(t => Console.WriteLine(t.Result));
using (var client = new TcpRpcClient("localhost", TcpPort))
{
client.WhenConnected.Wait();
using (var main = client.GetMain<ITestMoreStuff>())
{
var counters = new Counters();
var destroyed = new TaskCompletionSource<int>();
var impl = new TestInterfaceImpl(counters, destroyed);
var cts = new CancellationTokenSource();
var cancelTask = main.ExpectCancel(impl, cts.Token);
Assert.IsFalse(SpinWait.SpinUntil(() => destroyed.Task.IsCompleted || cancelTask.IsCompleted, ShortTimeout));
cts.Cancel();
Assert.IsTrue(destroyed.Task.Wait(MediumNonDbgTimeout));
Assert.IsFalse(cancelTask.IsCompleted && !cancelTask.IsCanceled);
}
}
});
}
[TestMethod]
public void CancelationClient()
{
using (var server = SetupServer())
{
var counters = new Counters();
server.Main = new TestMoreStuffImpl(counters);
LaunchCompatTestProcess("client:Cancelation", stdout =>
{
AssertOutput(stdout, "Cancelation test start");
AssertOutput(stdout, "~");
AssertOutput(stdout, "Cancelation test end");
});
}
}
[TestMethod]
public void PromiseResolveServer()
{
LaunchCompatTestProcess("server:MoreStuff", stdout =>
{
using (var client = new TcpRpcClient("localhost", TcpPort))
{
client.WhenConnected.Wait();
using (var main = client.GetMain<ITestMoreStuff>())
{
var tcs = new TaskCompletionSource<ITestInterface>();
using (var eager = tcs.Task.Eager(true))
{
var request = main.CallFoo(Proxy.Share(eager), default);
AssertOutput(stdout, "callFoo");
var request2 = main.CallFooWhenResolved(Proxy.Share(eager), default);
AssertOutput(stdout, "callFooWhenResolved");
var gcs = main.GetCallSequence(0, default);
AssertOutput(stdout, "getCallSequence");
Assert.IsTrue(gcs.Wait(MediumNonDbgTimeout));
Assert.AreEqual(2u, gcs.Result);
var chainedCallCount = new Counters();
var tiimpl = new TestInterfaceImpl(chainedCallCount);
tcs.SetResult(tiimpl);
Assert.IsTrue(request.Wait(MediumNonDbgTimeout));
Assert.IsTrue(request2.Wait(MediumNonDbgTimeout));
Assert.AreEqual("bar", request.Result);
Assert.AreEqual("bar", request2.Result);
Assert.AreEqual(2, chainedCallCount.CallCount);
}
AssertOutput(stdout, "fin");
AssertOutput(stdout, "fin");
}
}
});
}
[TestMethod]
public void PromiseResolveClient()
{
using (var server = SetupServer())
{
var counters = new Counters();
server.Main = new TestMoreStuffImpl(counters);
LaunchCompatTestProcess("client:PromiseResolve", stdout =>
{
AssertOutput(stdout, "PromiseResolve test start");
AssertOutput(stdout, "foo 123 1");
AssertOutput(stdout, "foo 123 1");
AssertOutput(stdout, "~");
AssertOutput(stdout, "PromiseResolve test end");
Assert.AreEqual(3, counters.CallCount);
});
}
}
[TestMethod]
public void RetainAndReleaseServer()
{
var destructionPromise = new TaskCompletionSource<int>();
var destructionTask = destructionPromise.Task;
LaunchCompatTestProcess("server:MoreStuff", stdout =>
{
stdout.ReadToEndAsync().ContinueWith(t => Console.WriteLine(t.Result));
using (var client = new TcpRpcClient("localhost", TcpPort))
{
client.WhenConnected.Wait();
using (var main = client.GetMain<ITestMoreStuff>())
{
var holdTask = main.Hold(new TestInterfaceImpl(new Counters(), destructionPromise), default);
Assert.IsTrue(holdTask.Wait(MediumNonDbgTimeout));
var cstask = main.GetCallSequence(0, default);
Assert.IsTrue(cstask.Wait(MediumNonDbgTimeout));
Assert.AreEqual(1u, cstask.Result);
Assert.IsFalse(destructionTask.IsCompleted);
var htask = main.CallHeld(default);
Assert.IsTrue(htask.Wait(MediumNonDbgTimeout));
Assert.AreEqual("bar", htask.Result);
var gtask = main.GetHeld(default);
Assert.IsTrue(gtask.Wait(MediumNonDbgTimeout));
// We can get the cap back from it.
using (var cap = gtask.Result)
{
// And call it, without any network communications.
long oldSendCount = client.SendCount;
var ftask = cap.Foo(123, true, default);
Assert.IsTrue(ftask.Wait(MediumNonDbgTimeout));
Assert.AreEqual("foo", ftask.Result);
Assert.AreEqual(oldSendCount, client.SendCount);
// We can send another copy of the same cap to another method, and it works.
var ctask = main.CallFoo(cap, default);
Assert.IsTrue(ctask.Wait(MediumNonDbgTimeout));
Assert.AreEqual("bar", ctask.Result);
// Give some time to settle.
cstask = main.GetCallSequence(0, default);
Assert.IsTrue(cstask.Wait(MediumNonDbgTimeout));
Assert.AreEqual(5u, cstask.Result);
cstask = main.GetCallSequence(0, default);
Assert.IsTrue(cstask.Wait(MediumNonDbgTimeout));
Assert.AreEqual(6u, cstask.Result);
cstask = main.GetCallSequence(0, default);
Assert.IsTrue(cstask.Wait(MediumNonDbgTimeout));
Assert.AreEqual(7u, cstask.Result);
// Can't be destroyed, we haven't released it.
Assert.IsFalse(destructionTask.IsCompleted);
}
// At this point, we have a major difference to Cap'n Proto original test suite.
// In the original suite, you will find the following comment:
// "We released our client, which should cause the server to be released, which in turn will
// release the cap pointing back to us."
// For the situation here, this assumption would be wrong: Releasing the client does NOT release
// the server, because it is the bootstrap capability, and there might be other clients in the future.
// The bootstrap capbility is held as long as the TCP server is running.
// Instead, the test requests the server to hold the "null" capability, which will replace the
// existing one, which in turn will release the cap pointing back to us.
holdTask = main.Hold(null, default);
Assert.IsTrue(holdTask.Wait(MediumNonDbgTimeout));
Assert.IsTrue(destructionTask.Wait(MediumNonDbgTimeout));
}
}
});
}
[TestMethod]
public void RetainAndReleaseClient()
{
using (var server = SetupServer())
{
var counters = new Counters();
server.Main = new TestMoreStuffImpl(counters);
LaunchCompatTestProcess("client:RetainAndRelease", stdout =>
{
AssertOutput(stdout, "RetainAndRelease test start");
AssertOutput(stdout, "foo 123 1");
AssertOutput(stdout, "foo 123 1");
AssertOutput(stdout, "foo 123 1");
AssertOutput(stdout, "~");
AssertOutput(stdout, "RetainAndRelease test end");
});
}
}
[TestMethod]
public void CancelServer()
{
LaunchCompatTestProcess("server:MoreStuff", stdout =>
{
using (var client = new TcpRpcClient("localhost", TcpPort))
{
client.WhenConnected.Wait();
var destructionPromise = new TaskCompletionSource<int>();
var destructionTask = destructionPromise.Task;
using (var main = client.GetMain<ITestMoreStuff>())
using (var cts = new CancellationTokenSource())
{
var counters = new Counters();
var ntask = main.NeverReturn(new TestInterfaceImpl(counters, destructionPromise), cts.Token);
// Allow some time to settle.
var cstask = main.GetCallSequence(0, default);
Assert.IsTrue(cstask.Wait(MediumNonDbgTimeout));
Assert.AreEqual(1u, cstask.Result);
cstask = main.GetCallSequence(0, default);
Assert.IsTrue(cstask.Wait(MediumNonDbgTimeout));
Assert.AreEqual(2u, cstask.Result);
// The cap shouldn't have been destroyed yet because the call never returned.
Assert.IsFalse(destructionTask.IsCompleted);
// There will be no automatic cancellation just because "ntask" goes of of scope or
// because the Proxy is disposed. Even ntask.Dispose() would not cancel the request.
// In .NET this needs to be done explicitly.
cts.Cancel();
// Now the cap should be released.
Assert.IsTrue(destructionTask.Wait(MediumNonDbgTimeout));
}
}
});
}
[TestMethod]
public void CancelClient()
{
using (var server = SetupServer())
{
var counters = new Counters();
server.Main = new TestMoreStuffImpl(counters);
LaunchCompatTestProcess("client:Cancel", stdout =>
{
AssertOutput(stdout, "Cancel test start");
AssertOutput(stdout, "~");
AssertOutput(stdout, "Cancel test end");
});
}
}
[TestMethod]
public void SendTwiceServer()
{
LaunchCompatTestProcess("server:MoreStuff", stdout =>
{
using (var client = new TcpRpcClient("localhost", TcpPort))
{
client.WhenConnected.Wait();
using (var main = client.GetMain<ITestMoreStuff>())
{
var destructionPromise = new TaskCompletionSource<int>();
var destructionTask = destructionPromise.Task;
var cap = new TestInterfaceImpl(new Counters(), destructionPromise);
Task<string> ftask1, ftask2;
using (Skeleton.Claim(cap))
{
var ftask = main.CallFoo(cap, default);
Assert.IsTrue(ftask.Wait(MediumNonDbgTimeout));
Assert.AreEqual("bar", ftask.Result);
var ctask = main.GetCallSequence(0, default);
Assert.IsTrue(ctask.Wait(MediumNonDbgTimeout));
Assert.AreEqual(1u, ctask.Result);
ftask1 = main.CallFoo(cap, default);
ftask2 = main.CallFoo(cap, default);
}
Assert.IsTrue(ftask1.Wait(MediumNonDbgTimeout));
Assert.AreEqual("bar", ftask1.Result);
Assert.IsTrue(ftask2.Wait(MediumNonDbgTimeout));
Assert.AreEqual("bar", ftask2.Result);
// Now the cap should be released.
Assert.IsTrue(destructionTask.Wait(MediumNonDbgTimeout));
}
}
});
}
[TestMethod]
public void SendTwiceClient()
{
using (var server = SetupServer())
{
var counters = new Counters();
server.Main = new TestMoreStuffImpl(counters);
LaunchCompatTestProcess("client:SendTwice", stdout =>
{
AssertOutput(stdout, "SendTwice test start");
AssertOutput(stdout, "foo 123 1");
AssertOutput(stdout, "foo 123 1");
AssertOutput(stdout, "foo 123 1");
AssertOutput(stdout, "~");
AssertOutput(stdout, "SendTwice test end");
});
}
}
[TestMethod]
public void EmbargoServer()
{
LaunchCompatTestProcess("server:MoreStuff", stdout =>
{
int retry = 0;
label:
using (var client = new TcpRpcClient("localhost", TcpPort))
{
Assert.IsTrue(client.WhenConnected.Wait(MediumNonDbgTimeout), "client connect");
using (var main = client.GetMain<ITestMoreStuff>())
{
var resolving = main as IResolvingCapability;
bool success;
try
{
success = resolving.WhenResolved.Wait(MediumNonDbgTimeout);
}
catch
{
success = false;
}
if (!success)
{
if (++retry == 5)
{
Assert.Fail("Attempting to obtain bootstrap interface failed. Bailing out.");
}
goto label;
}
var cap = new TestCallOrderImpl();
cap.CountToDispose = 6;
#if DEBUG_DISPOSE
Skeleton.BeginAssertNotDisposed(cap);
#endif
var earlyCall = main.GetCallSequence(0, default);
var echo = main.Echo(cap, default);
using (var pipeline = echo.Eager())
{
var call0 = pipeline.GetCallSequence(0, default);
var call1 = pipeline.GetCallSequence(1, default);
Assert.IsTrue(earlyCall.Wait(MediumNonDbgTimeout), "early call returns");
var call2 = pipeline.GetCallSequence(2, default);
Assert.IsTrue(echo.Wait(MediumNonDbgTimeout));
using (var resolved = echo.Result)
{
var call3 = pipeline.GetCallSequence(3, default);
var call4 = pipeline.GetCallSequence(4, default);
var call5 = pipeline.GetCallSequence(5, default);
Assert.IsTrue(call0.Wait(MediumNonDbgTimeout), "call 0 returns");
Assert.IsTrue(call1.Wait(MediumNonDbgTimeout), "call 1 returns");
Assert.IsTrue(call2.Wait(MediumNonDbgTimeout), "call 2 returns");
Assert.IsTrue(call3.Wait(MediumNonDbgTimeout), "call 3 returns");
Assert.IsTrue(call4.Wait(MediumNonDbgTimeout), "call 4 returns");
Assert.IsTrue(call5.Wait(MediumNonDbgTimeout), "call 5 returns");
Assert.AreEqual(0u, call0.Result);
Assert.AreEqual(1u, call1.Result);
Assert.AreEqual(2u, call2.Result);
Assert.AreEqual(3u, call3.Result);
Assert.AreEqual(4u, call4.Result);
Assert.AreEqual(5u, call5.Result);
#if DEBUG_DISPOSE
Skeleton.EndAssertNotDisposed(cap);
#endif
}
}
}
}
});
}
[TestMethod]
public void EmbargoServer2()
{
LaunchCompatTestProcess("server:MoreStuff", stdout =>
{
int retry = 0;
label:
using (var client = new TcpRpcClient("localhost", TcpPort))
{
Assert.IsTrue(client.WhenConnected.Wait(MediumNonDbgTimeout), "client connect");
using (var main = client.GetMain<ITestMoreStuff>())
{
var resolving = main as IResolvingCapability;
bool success;
try
{
success = resolving.WhenResolved.Wait(MediumNonDbgTimeout);
}
catch
{
success = false;
}
if (!success)
{
if (++retry == 5)
{
Assert.Fail("Attempting to obtain bootstrap interface failed. Bailing out.");
}
goto label;
}
var cap = new TestCallOrderImpl();
cap.CountToDispose = 6;
#if DEBUG_DISPOSE
Skeleton.BeginAssertNotDisposed(cap);
#endif
var earlyCall = main.GetCallSequence(0, default);
var echo = main.Echo(cap, default);
using (var pipeline = echo.Result)
{
var call0 = pipeline.GetCallSequence(0, default);
var call1 = pipeline.GetCallSequence(1, default);
Assert.IsTrue(earlyCall.Wait(MediumNonDbgTimeout), "early call returns");
var call2 = pipeline.GetCallSequence(2, default);
Assert.IsTrue(echo.Wait(MediumNonDbgTimeout));
var call3 = pipeline.GetCallSequence(3, default);
var call4 = pipeline.GetCallSequence(4, default);
var call5 = pipeline.GetCallSequence(5, default);
Assert.IsTrue(call0.Wait(MediumNonDbgTimeout), "call 0 returns");
Assert.IsTrue(call1.Wait(MediumNonDbgTimeout), "call 1 returns");
Assert.IsTrue(call2.Wait(MediumNonDbgTimeout), "call 2 returns");
Assert.IsTrue(call3.Wait(MediumNonDbgTimeout), "call 3 returns");
Assert.IsTrue(call4.Wait(MediumNonDbgTimeout), "call 4 returns");
Assert.IsTrue(call5.Wait(MediumNonDbgTimeout), "call 5 returns");
Assert.AreEqual(0u, call0.Result);
Assert.AreEqual(1u, call1.Result);
Assert.AreEqual(2u, call2.Result);
Assert.AreEqual(3u, call3.Result);
Assert.AreEqual(4u, call4.Result);
Assert.AreEqual(5u, call5.Result);
#if DEBUG_DISPOSE
Skeleton.EndAssertNotDisposed(cap);
#endif
}
}
}
});
}
[TestMethod]
public void EmbargoClient()
{
using (var server = SetupServer())
{
var counters = new Counters();
server.Main = new TestMoreStuffImpl(counters);
LaunchCompatTestProcess("client:Embargo", stdout =>
{
AssertOutput(stdout, "Embargo test start");
AssertOutput(stdout, "Embargo test end");
});
}
}
public void EmbargoErrorImpl(StreamReader stdout)
{
using (var client = new TcpRpcClient("localhost", TcpPort))
{
Assert.IsTrue(client.WhenConnected.Wait(MediumNonDbgTimeout));
using (var main = client.GetMain<ITestMoreStuff>())
{
var resolving = main as IResolvingCapability;
Assert.IsTrue(resolving.WhenResolved.Wait(MediumNonDbgTimeout));
var cap = new TaskCompletionSource<ITestCallOrder>();
var earlyCall = main.GetCallSequence(0, default);
using (var eager = cap.Task.Eager(true))
{
var echo = main.Echo(eager, default);
using (var pipeline = echo.Eager())
{
var call0 = pipeline.GetCallSequence(0, default);
var call1 = pipeline.GetCallSequence(1, default);
Assert.IsTrue(earlyCall.Wait(MediumNonDbgTimeout));
var call2 = pipeline.GetCallSequence(2, default);
Assert.IsTrue(echo.Wait(MediumNonDbgTimeout));
using (var resolved = echo.Result)
{
var call3 = pipeline.GetCallSequence(3, default);
var call4 = pipeline.GetCallSequence(4, default);
var call5 = pipeline.GetCallSequence(5, default);
cap.SetException(new InvalidOperationException("I'm annoying"));
ExpectPromiseThrows(call0);
ExpectPromiseThrows(call1);
ExpectPromiseThrows(call2);
ExpectPromiseThrows(call3);
ExpectPromiseThrows(call4);
ExpectPromiseThrows(call5);
}
}
}
// Verify that we're still connected (there were no protocol errors).
Assert.IsTrue(main.GetCallSequence(1, default).Wait(MediumNonDbgTimeout));
}
}
}
[TestMethod]
public void EmbargoErrorServer()
{
LaunchCompatTestProcess("server:MoreStuff", EmbargoErrorImpl);
}
[TestMethod, Timeout(240000)]
public void RepeatedEmbargoError()
{
LaunchCompatTestProcess("server:MoreStuff", stdout =>
{
for (int i = 0; i < 20; i++)
{
EmbargoErrorImpl(stdout);
}
});
}
[TestMethod]
public void EmbargoErrorClient()
{
using (var server = SetupServer())
{
var counters = new Counters();
server.Main = new TestMoreStuffImpl(counters);
LaunchCompatTestProcess("client:EmbargoError", stdout =>
{
AssertOutput(stdout, "EmbargoError test start");
AssertOutput(stdout, "EmbargoError test end");
});
}
}
[TestMethod]
public void EmbargoNullServer()
{
LaunchCompatTestProcess("server:MoreStuff", stdout =>
{
int retry = 0;
label:
using (var client = new TcpRpcClient("localhost", TcpPort))
{
client.WhenConnected.Wait();
using (var main = client.GetMain<ITestMoreStuff>())
{
var resolving = main as IResolvingCapability;
bool success;
try
{
success = resolving.WhenResolved.Wait(MediumNonDbgTimeout);
}
catch
{
success = false;
}
if (!success)
{
if (++retry == 5)
{
Assert.Fail("Attempting to obtain bootstrap interface failed. Bailing out.");
}
goto label;
}
var promise = main.GetNull(default);
using (var cap = promise.Eager())
{
var call0 = cap.GetCallSequence(0, default);
Assert.IsTrue(promise.Wait(MediumNonDbgTimeout));
using (promise.Result)
{
var call1 = cap.GetCallSequence(1, default);
ExpectPromiseThrows(call0);
ExpectPromiseThrows(call1);
// Verify that we're still connected (there were no protocol errors).
Assert.IsTrue(main.GetCallSequence(1, default).Wait(MediumNonDbgTimeout));
}
}
}
}
});
}
[TestMethod]
public void EmbargoNullClient()
{
using (var server = SetupServer())
{
var counters = new Counters();
server.Main = new TestMoreStuffImpl(counters);
LaunchCompatTestProcess("client:EmbargoNull", stdout =>
{
AssertOutput(stdout, "EmbargoNull test start");
AssertOutput(stdout, "EmbargoNull test end");
});
}
}
[TestMethod]
public void CallBrokenPromiseServer()
{
LaunchCompatTestProcess("server:MoreStuff", stdout =>
{
using (var client = new TcpRpcClient("localhost", TcpPort))
{
client.WhenConnected.Wait();
using (var main = client.GetMain<ITestMoreStuff>())
{
var resolving = main as IResolvingCapability;
Assert.IsTrue(resolving.WhenResolved.Wait(MediumNonDbgTimeout));
var tcs = new TaskCompletionSource<ITestInterface>();
using (var eager = tcs.Task.Eager(true))
{
var req = main.Hold(eager, default);
Assert.IsTrue(req.Wait(MediumNonDbgTimeout));
}
var req2 = main.CallHeld(default);
Assert.IsFalse(req2.Wait(ShortTimeout));
tcs.SetException(new InvalidOperationException("I'm a promise-breaker!"));
ExpectPromiseThrows(req2);
// Verify that we're still connected (there were no protocol errors).
Assert.IsTrue(main.GetCallSequence(1, default).Wait(MediumNonDbgTimeout));
}
}
});
}
[TestMethod]
public void CallBrokenPromiseClient()
{
using (var server = SetupServer())
{
var counters = new Counters();
server.Main = new TestMoreStuffImpl(counters);
LaunchCompatTestProcess("client:CallBrokenPromise", stdout =>
{
AssertOutput(stdout, "CallBrokenPromise test start");
AssertOutput(stdout, "CallBrokenPromise test end");
});
}
}
}
}