Пример #1
0
        public override async Task <string> Authenticate(JsonClient client)
        {
            // step1 request: User -> Host: I, A = g^a (identifies self, a = random number)
            var clientEphemeral = SrpClient.GenerateEphemeral();
            var request1        = new AuthRequest();

            request1.Parameters[SrpProtocolConstants.UserNameKey] = UserName;
            request1.Parameters[SrpProtocolConstants.ClientPublicEphemeralKey] = clientEphemeral.Public;

            // step1 response: Host -> User: s, B = kv + g^b (sends salt, b = random number)
            var response1 = await client.Call(request1);

            var salt = response1.GetSalt();
            var serverPublicEphemeral = response1.GetServerPublicEphemeral();

            if (string.IsNullOrWhiteSpace(salt) || string.IsNullOrWhiteSpace(serverPublicEphemeral))
            {
                throw new AuthFailedException("Server doesn't support SRP authentication protocol");
            }

            // step2 request: User -> Host: M = H(H(N) xor H(g), H(I), s, A, B, K)
            var privateKey    = SrpClient.DerivePrivateKey(salt, UserName, Password);
            var clientSession = SrpClient.DeriveSession(clientEphemeral.Secret, serverPublicEphemeral, salt, UserName, privateKey);
            var request2      = new AuthRequest();

            request2.Parameters[SrpProtocolConstants.ClientSessionProofKey] = clientSession.Proof;

            // step2 response: Host -> User: H(A, M, K)
            var response2 = await client.Call(request2);

            var serverSessionProof = response2.GetServerSessionProof();

            SrpClient.VerifySession(clientEphemeral.Public, clientSession, serverSessionProof);
            return(response2.SessionId);
        }
Пример #2
0
        protected async Task CallDisconnectAndReconnectCore(JsonServer js, JsonClient jc, ICredentials credentials = null)
        {
            js.ProductName    = "My awesome server";
            js.ProductVersion = "1.2.3.4";
            js.Start();

            // connect, call, disconnect
            await Assert_NotTimedOut(jc.ConnectAsync(credentials), "connect");

            var result = await Assert_NotTimedOut(jc.Call(new VersionRequest()), "jc.Call(VersionRequest)");

            Assert.NotNull(result);
            Assert.AreEqual(js.ProductName, result.ProductName);
            Assert.AreEqual(js.ProductVersion, result.ProductVersion);
            await Assert_NotTimedOut(jc.DisconnectAsync(), "disconnect");

            // reconnect, call, disconnect
            await Assert_NotTimedOut(jc.ConnectAsync(credentials), "reconnect");

            result = await Assert_NotTimedOut(jc.Call(new VersionRequest()), "jc.Call(VersionRequest) after reconnect");

            Assert.NotNull(result);
            Assert.AreEqual(js.ProductName, result.ProductName);
            Assert.AreEqual(js.ProductVersion, result.ProductVersion);
            await Assert_NotTimedOut(jc.DisconnectAsync(), "disconnect completely");

            Assert.AreEqual(0, jc.PendingMessages.Count);
        }
Пример #3
0
        protected async Task CallGenericMessagesCore(JsonServer js, JsonClient jc, ICredentials credentials = null)
        {
            js.Start();
            await jc.ConnectAsync();

            var intMsg = new GenericRequest <int> {
                Value = 1
            };
            var intResult = await jc.Call(intMsg);

            Assert.AreEqual(2, intResult.Result);

            var dtMsg = new GenericRequest <DateTime> {
                Value = new DateTime(2018, 12, 18)
            };
            var dtResult = await jc.Call(dtMsg);

            Assert.AreEqual(new DateTime(2019, 12, 18), dtResult.Result);

            var strMsg = new GenericRequest <string> {
                Value = "World"
            };
            var strResult = await jc.Call(strMsg);

            Assert.AreEqual("Hello World!", strResult.Result);

            var boolMsg = new GenericRequest <bool> {
                Value = true
            };

            Assert.ThrowsAsync <MethodNotFoundException>(async() => await jc.Call(boolMsg));

            // make sure all incoming messages are processed
            Assert.AreEqual(0, jc.PendingMessages.Count);
        }
Пример #4
0
        public void JsonClientCallAndNotifyThrowOnNullArguments()
        {
            var server           = new StubServer();
            var client           = new StubClient(server);
            var clientProvider   = new StubMessageTypeProvider();
            var clientSerializer = new Serializer();
            var jc = new JsonClient(client, clientProvider, clientSerializer);

            Assert.Throws <ArgumentNullException>(() => jc.Notify(null));
            Assert.ThrowsAsync <ArgumentNullException>(() => jc.Call(null));
            Assert.ThrowsAsync <ArgumentNullException>(() => jc.Call <string>(null));
        }
Пример #5
0
        protected virtual async Task CallDelayServiceCore(JsonServer js, JsonClient jc, ICredentials credentials = null)
        {
            js.Start();
            await jc.ConnectAsync(credentials);

            await Assert_NotTimedOut(jc.Call(new DelayRequest {
                Milliseconds = 10
            }), "jc.Call(Delay 10)");
            await Assert_TimedOut(jc.Call(new DelayRequest {
                Milliseconds = 200
            }), "jc.Call(Delay 200)", Task.Delay(10));

            // make sure that await completes before the server is disposed (affects NetMQ server)
            await Task.Delay(300);
        }
Пример #6
0
        private async Task CallBuiltinVersionServiceCore(JsonServer js, JsonClient jc, ICredentials credentials = null)
        {
            // event handlers
            var connected    = 0;
            var disconnected = 0;

            js.ClientConnected    += (s, e) => connected++;
            js.ClientDisconnected += (s, e) => disconnected++;
            js.UnhandledException += (s, e) => Assert.Fail($"Unhandled server exception: {e.Exception}. Connected: {connected}, disconnected: {disconnected}.");
            jc.UnhandledException += (s, e) => Assert.Fail($"Unhandled client exception: {e.Exception}. Connected: {connected}, disconnected: {disconnected}.");

            // start json server and connect the client
            js.Start();

            Assert.IsNull(jc.SessionId);
            var sessionId = await jc.ConnectAsync(credentials);

            Assert.IsNotNull(jc.SessionId);
            Assert.AreEqual(sessionId, jc.SessionId);

            // call Version
            var msg    = new VersionRequest();
            var result = await Assert_NotTimedOut(jc.Call(msg), "jc.Call(VersionRequest)");

            Assert.NotNull(result);
            Assert.AreEqual(nameof(JsonServices), result.ProductName);
            Assert.NotNull(result.ProductVersion);
            Assert.NotNull(result.EngineVersion);

            // make sure all incoming messages are processed
            Assert.AreEqual(0, jc.PendingMessages.Count);
        }
Пример #7
0
        public virtual async Task <string> Authenticate(JsonClient client)
        {
            var response = await client.Call(new AuthRequest
            {
                Parameters = Parameters,
            });

            return(response.SessionId);
        }
Пример #8
0
        public async Task CallServiceBeforeConnectingShouldFail()
        {
            // fake transport and serializer
            var server     = new StubServer();
            var client     = new StubClient(server);
            var serializer = new Serializer();
            var executor   = new StubExecutor();
            var provider   = new StubMessageTypeProvider();

            var js = new JsonServer(server, provider, serializer, executor);
            var jc = new JsonClient(client, provider, serializer);

            js.Start();

            Assert.ThrowsAsync <AuthRequiredException>(async() =>
                                                       await Assert_NotTimedOut(jc.Call(new GetVersion()), "jc.Call(GetVersion) before Connect"));

            await Assert_NotTimedOut(jc.ConnectAsync(), "jc.ConnectAsync()");
            await Assert_NotTimedOut(jc.Call(new GetVersion()), "jc.Call(GetVersion) after connect");
        }
Пример #9
0
        private async Task ExecuteGetVersion(JsonClient client, bool isInternal)
        {
            var result = await Assert_NotTimedOut(client.Call(new GetVersion {
                IsInternal = isInternal
            }), "StressTest.GetVersion");

            var expected = isInternal ? "Version 0.01-alpha, build 12345, by yallie" : "0.01-alpha";

            Assert.NotNull(result);
            Assert.AreEqual(expected, result.Version);
        }
Пример #10
0
        protected async Task CallGetVersionServiceCore(JsonServer js, JsonClient jc, ICredentials credentials = null)
        {
            // event handlers
            var connected    = 0;
            var disconnected = 0;

            js.ClientConnected    += (s, e) => connected++;
            js.ClientDisconnected += (s, e) => disconnected++;
            js.UnhandledException += (s, e) => Assert.Fail($"Unhandled server exception: {e.Exception}. Connected: {connected}, disconnected: {disconnected}.");
            jc.UnhandledException += (s, e) => Assert.Fail($"Unhandled client exception: {e.Exception}. Connected: {connected}, disconnected: {disconnected}.");

            // start json server and connect the client
            js.Start();

            Assert.IsNull(jc.SessionId);
            var sessionId = await jc.ConnectAsync(credentials);

            Assert.IsNotNull(jc.SessionId);
            Assert.AreEqual(sessionId, jc.SessionId);

            // call GetVersion
            var msg    = new GetVersion();
            var result = await Assert_NotTimedOut(jc.Call(msg), "jc.Call(msg)");

            Assert.NotNull(result);
            Assert.AreEqual("0.01-alpha", result.Version);

            // call GetVersion
            msg = new GetVersion {
                IsInternal = true
            };
            result = await Assert_NotTimedOut(jc.Call(msg), "jc.Call(msg...IsInternal)");

            Assert.NotNull(result);
            Assert.AreEqual("Version 0.01-alpha, build 12345, by yallie", result.Version);

            // make sure all incoming messages are processed
            Assert.AreEqual(0, jc.PendingMessages.Count);
        }
Пример #11
0
        protected async Task CallDelayServiceAndAbortConnectionCore(JsonServer js, JsonClient jc, ICredentials credentials = null)
        {
            js.Start();
            await jc.ConnectAsync(credentials);

            var call = jc.Call(new DelayRequest {
                Milliseconds = 1000
            });
            await Task.Delay(100);             // make sure that the call was actually sent

            await jc.Client.DisconnectAsync(); // should fire Disconnected event

            Assert.ThrowsAsync <ClientDisconnectedException>(async() =>
                                                             await Assert_NotTimedOut(call, "jc.Call(Delay 1000)", Task.Delay(2000)));
        }
Пример #12
0
        protected async Task CallUnregisteredServiceCore(JsonServer js, JsonClient jc, ICredentials credentials = null)
        {
            // event handlers
            var connected    = 0;
            var disconnected = 0;

            js.ClientConnected    += (s, e) => connected++;
            js.ClientDisconnected += (s, e) => disconnected++;
            js.UnhandledException += (s, e) => Assert.Fail($"Unhandled server exception: {e.Exception}. Connected: {connected}, disconnected: {disconnected}.");
            jc.UnhandledException += (s, e) => Assert.Fail($"Unhandled client exception: {e.Exception}. Connected: {connected}, disconnected: {disconnected}.");

            // start json server and connect the client
            js.Start();
            await jc.ConnectAsync(credentials);

            // call UnregisteredService
            var msg = new UnregisteredService();
            var ex  = Assert.ThrowsAsync <MethodNotFoundException>(async() =>
                                                                   await Assert_NotTimedOut(jc.Call(msg), "jc.Call(UnregisteredService)"));

            Assert.AreEqual(MethodNotFoundException.ErrorCode, ex.Code);

            // make sure all incoming messages are processed
            Assert.AreEqual(0, jc.PendingMessages.Count);
        }
Пример #13
0
        protected async Task CallCalculateServiceCore(JsonServer js, JsonClient jc, ICredentials credentials = null)
        {
            // unhandled exception handlers
            var connected    = 0;
            var disconnected = 0;

            js.ClientConnected    += (s, e) => connected++;
            js.ClientDisconnected += (s, e) => disconnected++;
            js.UnhandledException += (s, e) => Assert.Fail($"Unhandled server exception: {e.Exception}. Connected: {connected}, disconnected: {disconnected}.");
            jc.UnhandledException += (s, e) => Assert.Fail($"Unhandled client exception: {e.Exception}. Connected: {connected}, disconnected: {disconnected}.");

            // start json server and connect the client
            js.Start();
            await jc.ConnectAsync(credentials);

            // normal call
            var msg = new Calculate
            {
                FirstOperand  = 353,
                SecondOperand = 181,
                Operation     = "+",
            };

            var result = await Assert_NotTimedOut(jc.Call(msg), "353 + 181");

            Assert.NotNull(result);
            Assert.AreEqual(534, result.Result);

            msg.SecondOperand = 333;
            result            = await Assert_NotTimedOut(jc.Call(msg), "353 + 333");

            Assert.NotNull(result);
            Assert.AreEqual(686, result.Result);

            msg.Operation = "-";
            result        = await Assert_NotTimedOut(jc.Call(msg), "353 - 333");

            Assert.NotNull(result);
            Assert.AreEqual(20, result.Result);

            // call with error
            msg.Operation = "#";
            var ex = Assert.ThrowsAsync <InternalErrorException>(async() =>
                                                                 await Assert_NotTimedOut(jc.Call(msg), "353 # 333"));

            // internal server error
            Assert.AreEqual(-32603, ex.Code);
            Assert.AreEqual("Internal server error: Bad operation: #", ex.Message);

            // call with another error
            msg.Operation     = "%";
            msg.SecondOperand = 0;
            ex = Assert.ThrowsAsync <InternalErrorException>(async() =>
                                                             await Assert_NotTimedOut(jc.Call(msg), "353 % 0"));

            // internal server error
            Assert.AreEqual(-32603, ex.Code);
            Assert.AreEqual("Internal server error: Attempted to divide by zero.", ex.Message);

            // normal call again
            msg.Operation = "*";
            result        = await Assert_NotTimedOut(jc.Call(msg), "353 * 0");

            Assert.NotNull(result);
            Assert.AreEqual(0, result.Result);

            msg.Operation     = "+";
            msg.SecondOperand = 181;
            result            = await Assert_NotTimedOut(jc.Call(msg), "353 + 181 again");

            Assert.NotNull(result);
            Assert.AreEqual(534, result.Result);

            // make sure all incoming messages are processed
            Assert.AreEqual(0, jc.PendingMessages.Count);
        }
Пример #14
0
        protected async Task TestSubscriptionsAndUnsubscriptionsCore(JsonServer js, JsonClient jc, JsonClient sc, ICredentials credentials = null)
        {
            // unhandled exception handlers
            var connected    = 0;
            var disconnected = 0;

            js.ClientConnected    += (s, e) => connected++;
            js.ClientDisconnected += (s, e) => disconnected++;
            js.UnhandledException += (s, e) => Assert.Fail($"Unhandled server exception: {e.Exception}. Connected: {connected}, disconnected: {disconnected}.");
            jc.UnhandledException += (s, e) => Assert.Fail($"Unhandled client exception in jc (first client): {e.Exception}. Connected: {connected}, disconnected: {disconnected}.");
            sc.UnhandledException += (s, e) => Assert.Fail($"Unhandled client exception in sc (second client): {e.Exception}. Connected: {connected}, disconnected: {disconnected}.");

            // start json server and connect both clients
            js.Start();
            await Assert_NotTimedOut(jc.ConnectAsync(credentials), "jc.ConnectAsync()");
            await Assert_NotTimedOut(sc.ConnectAsync(credentials), "sc.ConnectAsync()");

            // subscribe to jc events
            var jcounter     = 0;
            var jcancel      = default(bool?);
            var jtcs         = new TaskCompletionSource <bool>();
            var junsubscribe = await Assert_NotTimedOut(jc.Subscribe <CancelEventArgs>(
                                                            EventBroadcaster.BeforeShutdownEventName, (s, e) =>
            {
                jcounter++;
                jcancel = e.Cancel;
                jtcs.TrySetResult(true);
            }), "jc.Subscribe<CancelEventArgs>(...)");

            // subscribe to sc events
            var scounter     = 0;
            var spropName    = default(string);
            var stcs         = new TaskCompletionSource <bool>();
            var sunsubscribe = await Assert_NotTimedOut(sc.Subscribe <MyCoolEventArgs>(
                                                            EventBroadcaster.AfterStartupEventName, (s, e) =>
            {
                scounter++;
                spropName = e.PropertyName;
                stcs.TrySetResult(true);
            }), "sc.Subscribe<MyCoolEventArgs>(...)");

            // one-way call EventBroadcaster.AfterStartup
            jc.Notify(new EventBroadcaster
            {
                EventName = EventBroadcaster.AfterStartupEventName,
            });

            // sc is subscribed to AfterStartup event, jc is not
            await Assert_NotTimedOut(stcs.Task, "stcs.Task");

            Assert.AreEqual(1, scounter);
            Assert.AreEqual(0, jcounter);
            Assert.AreEqual(nameof(EventBroadcaster), spropName);

            // call EventBroadcaster.BeforeShutdown
            await Assert_NotTimedOut(jc.Call(new EventBroadcaster
            {
                EventName = EventBroadcaster.BeforeShutdownEventName,
            }), "jc.Call(new EventBroadcaster(...BeforeShutdown))");

            // js is subscribed to BeforeShutdown event, sc is not
            await Assert_NotTimedOut(jtcs.Task, "jtcs.Task");

            Assert.AreEqual(1, scounter);
            Assert.AreEqual(1, jcounter);
            Assert.IsTrue(jcancel);

            // restart both task completion sources
            jtcs = new TaskCompletionSource <bool>();
            stcs = new TaskCompletionSource <bool>();

            // call EventBroadcaster.BeforeShutdown
            await Assert_NotTimedOut(jc.Call(new EventBroadcaster
            {
                EventName = EventBroadcaster.BeforeShutdownEventName,
            }), "jc.Call(new EventBroadcaster(...BeforeShutdown)) #2");

            // js is subscribed to BeforeShutdown event, sc is not
            await Assert_NotTimedOut(jtcs.Task, "jtcs.Task #2");

            Assert.AreEqual(1, scounter);
            Assert.AreEqual(2, jcounter);
            Assert.IsTrue(jcancel);

            // unsubscribe sc from AfterStartup event
            await Assert_NotTimedOut(sunsubscribe(), "sunsubscribe()");

            // call EventBroadcaster.AfterStartup
            await Assert_NotTimedOut(jc.Call(new EventBroadcaster
            {
                EventName = EventBroadcaster.AfterStartupEventName,
            }), "jc.Call(new EventBroadcaster(...AfterStartup)) #2");

            // make sure that event is not handled anymore
            await Assert_TimedOut(stcs.Task, "stcs.Task #2", Task.Delay(200));

            Assert.AreEqual(1, scounter);

            // unsubscribe jc from BeforeShutdown event
            await Assert_NotTimedOut(junsubscribe(), "junsubscribe()");

            jtcs     = new TaskCompletionSource <bool>();
            scounter = 0;
            jcounter = 0;

            // call EventBroadcaster.BeforeShutdown
            await Assert_NotTimedOut(jc.Call(new EventBroadcaster
            {
                EventName = EventBroadcaster.BeforeShutdownEventName,
            }), "jc.Call(new EventBroadcaster(...BeforeShutdown)) #3");

            // nobody is subscribed to BeforeShutdown event
            await Assert_TimedOut(jtcs.Task, "jtcs.Task #3", Task.Delay(200));

            Assert.AreEqual(0, scounter);
            Assert.AreEqual(0, jcounter);

            // make sure all incoming messages are processed
            Assert.AreEqual(0, jc.PendingMessages.Count);
            Assert.AreEqual(0, sc.PendingMessages.Count);
        }
Пример #15
0
        protected async Task TestFilteredSubscriptionsAndUnsubscriptionsCore(JsonServer js, JsonClient jc, JsonClient sc, ICredentials credentials = null)
        {
            // unhandled exception handlers
            var connected    = 0;
            var disconnected = 0;

            js.ClientConnected    += (s, e) => connected++;
            js.ClientDisconnected += (s, e) => disconnected++;
            js.UnhandledException += (s, e) => Assert.Fail($"Unhandled server exception: {e.Exception}. Connected: {connected}, disconnected: {disconnected}.");
            jc.UnhandledException += (s, e) => Assert.Fail($"Unhandled client exception in jc (first client): {e.Exception}. Connected: {connected}, disconnected: {disconnected}.");
            sc.UnhandledException += (s, e) => Assert.Fail($"Unhandled client exception in sc (second client): {e.Exception}. Connected: {connected}, disconnected: {disconnected}.");

            // start json server and connect both clients
            js.Start();
            await Assert_NotTimedOut(jc.ConnectAsync(credentials), "jc.ConnectAsync()");
            await Assert_NotTimedOut(sc.ConnectAsync(credentials), "sc.ConnectAsync()");

            // subscribe to jc events
            var jcounter     = 0;
            var jeventArgs   = default(FilteredEventArgs);
            var jtcs         = new TaskCompletionSource <bool>();
            var junsubscribe = await Assert_NotTimedOut(jc.Subscribe <FilteredEventArgs>(
                                                            EventBroadcaster.FilteredEventName, (s, e) =>
            {
                jcounter++;
                jeventArgs = e;
                jtcs.TrySetResult(true);
            },
                                                            new Dictionary <string, string>
            {
                { nameof(FilteredEventArgs.StringProperty), "Twain" }
            }), "jc.Subscribe<FilteredEventArgs>(...)");

            // subscribe to sc events
            var scounter     = 0;
            var seventArgs   = default(FilteredEventArgs);
            var stcs         = new TaskCompletionSource <bool>();
            var sunsubscribe = await Assert_NotTimedOut(sc.Subscribe <FilteredEventArgs>(
                                                            EventBroadcaster.FilteredEventName, (s, e) =>
            {
                scounter++;
                seventArgs = e;
                stcs.TrySetResult(true);
            },
                                                            new Dictionary <string, string>
            {
                { nameof(FilteredEventArgs.StringProperty), "Mark" }
            }), "sc.Subscribe<FilteredEventArgs>(...)");

            // call EventBroadcaster.FilteredEvent
            await Assert_NotTimedOut(jc.Call(new EventBroadcaster
            {
                EventName      = EventBroadcaster.FilteredEventName,
                StringArgument = "Mark Hamill",
            }), "jc.Call(new EventBroadcaster...FilteredEvent...Mark Hamill))");

            // sc is subscribed to Mark filtered event, jc is not
            await Assert_NotTimedOut(stcs.Task, "stcs.Task");

            Assert.AreEqual(1, scounter);
            Assert.AreEqual(0, jcounter);
            Assert.AreEqual("Mark Hamill", seventArgs.StringProperty);

            // restart both task completion sources
            jtcs = new TaskCompletionSource <bool>();
            stcs = new TaskCompletionSource <bool>();

            // call EventBroadcaster.FilteredEvent
            await Assert_NotTimedOut(jc.Call(new EventBroadcaster
            {
                EventName      = EventBroadcaster.FilteredEventName,
                StringArgument = "Mark Twain",
            }), "jc.Call(new EventBroadcaster(...FilteredEvent...Mark Twain))");

            // js and sc are both subscribed to this filtered event
            await Assert_NotTimedOut(jtcs.Task, "jtcs.Task");
            await Assert_NotTimedOut(stcs.Task, "stcs.Task");

            Assert.AreEqual(2, scounter);
            Assert.AreEqual(1, jcounter);
            Assert.AreEqual("Mark Twain", jeventArgs.StringProperty);
            Assert.AreEqual("Mark Twain", seventArgs.StringProperty);

            // restart both task completion sources
            jtcs = new TaskCompletionSource <bool>();
            stcs = new TaskCompletionSource <bool>();

            // call EventBroadcaster.FilteredEvent
            await Assert_NotTimedOut(sc.Call(new EventBroadcaster
            {
                EventName      = EventBroadcaster.FilteredEventName,
                StringArgument = "TWAIN driver"
            }), "sc.Call(new EventBroadcaster(...FilteredEvent...TWAIN driver))");

            // jc is subscribed to TWAIN filtered event, sc is not
            await Assert_NotTimedOut(jtcs.Task, "jtcs.Task #2");

            Assert.AreEqual(2, scounter);
            Assert.AreEqual(2, jcounter);
            Assert.AreEqual("TWAIN driver", jeventArgs.StringProperty);

            // unsubscribe sc from the filtered event
            await Assert_NotTimedOut(sunsubscribe(), "sunsubscribe()");

            // one-way call EventBroadcaster.FilteredEvent
            jc.Notify(new EventBroadcaster
            {
                EventName      = EventBroadcaster.FilteredEventName,
                StringArgument = "Mark Knopfler"
            });

            // make sure that event is not handled anymore
            await Assert_TimedOut(stcs.Task, "stcs.Task #2", Task.Delay(200));

            Assert.AreEqual(2, scounter);
            Assert.AreEqual(2, jcounter);

            // unsubscribe jc from the filtered event
            await Assert_NotTimedOut(junsubscribe(), "junsubscribe()");

            jtcs     = new TaskCompletionSource <bool>();
            scounter = 0;
            jcounter = 0;

            // call EventBroadcaster.FilteredEvent
            await Assert_NotTimedOut(sc.Call(new EventBroadcaster
            {
                EventName      = EventBroadcaster.FilteredEventName,
                StringArgument = "Twain, Mark",
            }), "sc.Call(new EventBroadcaster(...FilteredEvent...Twain, Mark))");

            // nobody is subscribed to BeforeShutdown event
            await Assert_TimedOut(jtcs.Task, "jtcs.Task #3", Task.Delay(200));

            Assert.AreEqual(0, scounter);
            Assert.AreEqual(0, jcounter);

            // make sure all incoming messages are processed
            Assert.AreEqual(0, jc.PendingMessages.Count);
            Assert.AreEqual(0, sc.PendingMessages.Count);
        }