public void Dispatcher_ClientConnectionThrows_BeginsShutdown()
        {
            // Arrange
            var listenCancellationToken = default(CancellationToken);
            var firstConnectionTask     = CreateConnectionWithEmptyServerRequest(c =>
            {
                c.WaitForDisconnectAsyncFunc = (ct) =>
                {
                    listenCancellationToken = ct;
                    return(Task.Delay(Timeout.Infinite, ct).ContinueWith <Connection>(_ => null));
                };
            });
            var secondConnectionTask = CreateConnectionWithEmptyServerRequest(c =>
            {
                c.WaitForDisconnectAsyncFunc = (ct) => throw new Exception();
            });

            var compilerHost   = CreateCompilerHost();
            var connectionHost = CreateConnectionHost(
                firstConnectionTask,
                secondConnectionTask,
                new TaskCompletionSource <Connection>().Task);
            var keepAlive  = TimeSpan.FromSeconds(10);
            var eventBus   = new TestableEventBus();
            var dispatcher = new DefaultRequestDispatcher(connectionHost, compilerHost, CancellationToken.None, eventBus, keepAlive);

            // Act
            dispatcher.Run();

            // Assert
            Assert.True(eventBus.HasDetectedBadConnection);
            Assert.True(listenCancellationToken.IsCancellationRequested);
        }
        public void Dispatcher_ProcessMultipleConnections_HitsKeepAliveTimeout()
        {
            // Arrange
            var count = 5;
            var list  = new List <Task <Connection> >();

            for (var i = 0; i < count; i++)
            {
                var connectionTask = CreateConnectionWithEmptyServerRequest();
                list.Add(connectionTask);
            }

            list.Add(new TaskCompletionSource <Connection>().Task);
            var connectionHost = CreateConnectionHost(list.ToArray());
            var compilerHost   = CreateCompilerHost(c =>
            {
                c.ExecuteFunc = (req, ct) =>
                {
                    return(EmptyServerResponse);
                };
            });

            var keepAlive  = TimeSpan.FromSeconds(1);
            var eventBus   = new TestableEventBus();
            var dispatcher = new DefaultRequestDispatcher(connectionHost, compilerHost, CancellationToken.None, eventBus, keepAlive);

            // Act
            dispatcher.Run();

            // Assert
            Assert.Equal(count, eventBus.CompletedCount);
            Assert.True(eventBus.LastProcessedTime.HasValue);
            Assert.True(eventBus.HitKeepAliveTimeout);
        }
        public async Task Dispatcher_ProcessSimultaneousConnections_HitsKeepAliveTimeout()
        {
            // Arrange
            var totalCount     = 2;
            var readySource    = new TaskCompletionSource <bool>();
            var list           = new List <TaskCompletionSource <bool> >();
            var connectionHost = new Mock <ConnectionHost>();

            connectionHost
            .Setup(x => x.WaitForConnectionAsync(It.IsAny <CancellationToken>()))
            .Returns((CancellationToken ct) =>
            {
                if (list.Count < totalCount)
                {
                    var source         = new TaskCompletionSource <bool>();
                    var connectionTask = CreateConnectionWithEmptyServerRequest(c =>
                    {
                        c.WaitForDisconnectAsyncFunc = _ => source.Task;
                    });
                    list.Add(source);
                    return(connectionTask);
                }

                readySource.SetResult(true);
                return(new TaskCompletionSource <Connection>().Task);
            });

            var compilerHost = CreateCompilerHost(c =>
            {
                c.ExecuteFunc = (req, ct) =>
                {
                    return(EmptyServerResponse);
                };
            });

            var keepAlive      = TimeSpan.FromSeconds(1);
            var eventBus       = new TestableEventBus();
            var dispatcherTask = Task.Run(() =>
            {
                var dispatcher = new DefaultRequestDispatcher(connectionHost.Object, compilerHost, CancellationToken.None, eventBus, keepAlive);
                dispatcher.Run();
            });

            await readySource.Task;

            foreach (var source in list)
            {
                source.SetResult(true);
            }

            // Act
            await dispatcherTask;

            // Assert
            Assert.Equal(totalCount, eventBus.CompletedCount);
            Assert.True(eventBus.LastProcessedTime.HasValue, "LastProcessedTime should have had a value.");
            Assert.True(eventBus.HitKeepAliveTimeout, "HitKeepAliveTimeout should have been hit.");
        }
Example #4
0
        internal static ServerData CreateServer(
            string pipeName                        = null,
            CompilerHost compilerHost              = null,
            ConnectionHost connectionHost          = null,
            Action <object, EventArgs> onListening = null)
        {
            pipeName       = pipeName ?? Guid.NewGuid().ToString();
            compilerHost   = compilerHost ?? CompilerHost.Create();
            connectionHost = connectionHost ?? ConnectionHost.Create(pipeName);

            var serverStatsSource  = new TaskCompletionSource <ServerStats>();
            var serverListenSource = new TaskCompletionSource <bool>();
            var cts       = new CancellationTokenSource();
            var mutexName = MutexName.GetServerMutexName(pipeName);
            var thread    = new Thread(_ =>
            {
                var eventBus        = new TestableEventBus();
                eventBus.Listening += (sender, e) => { serverListenSource.TrySetResult(true); };
                if (onListening != null)
                {
                    eventBus.Listening += (sender, e) => onListening(sender, e);
                }
                try
                {
                    RunServer(
                        pipeName,
                        connectionHost,
                        compilerHost,
                        cts.Token,
                        eventBus,
                        Timeout.InfiniteTimeSpan);
                }
                finally
                {
                    var serverStats = new ServerStats(connections: eventBus.ConnectionCount, completedConnections: eventBus.CompletedCount);
                    serverStatsSource.SetResult(serverStats);
                }
            });

            thread.Start();

            // The contract of this function is that it will return once the server has started.  Spin here until
            // we can verify the server has started or simply failed to start.
            while (ServerConnection.WasServerMutexOpen(mutexName) != true && thread.IsAlive)
            {
                Thread.Yield();
            }

            return(new ServerData(cts, pipeName, serverStatsSource.Task, serverListenSource.Task));
        }
        public void Dispatcher_NoConnections_HitsKeepAliveTimeout()
        {
            // Arrange
            var keepAlive      = TimeSpan.FromSeconds(3);
            var compilerHost   = CreateCompilerHost();
            var connectionHost = new Mock <ConnectionHost>();

            connectionHost
            .Setup(x => x.WaitForConnectionAsync(It.IsAny <CancellationToken>()))
            .Returns(new TaskCompletionSource <Connection>().Task);

            var eventBus   = new TestableEventBus();
            var dispatcher = new DefaultRequestDispatcher(connectionHost.Object, compilerHost, CancellationToken.None, eventBus, keepAlive);
            var startTime  = DateTime.Now;

            // Act
            dispatcher.Run();

            // Assert
            Assert.True(eventBus.HitKeepAliveTimeout);
        }
        public async Task Dispatcher_ProcessSimultaneousConnections_HitsKeepAliveTimeout()
        {
            // Arrange
            var totalCount     = 2;
            var readySource    = new TaskCompletionSource <bool>();
            var list           = new List <TaskCompletionSource <bool> >();
            var connectionHost = new Mock <ConnectionHost>();

            connectionHost
            .Setup(x => x.WaitForConnectionAsync(It.IsAny <CancellationToken>()))
            .Returns((CancellationToken ct) =>
            {
                if (list.Count < totalCount)
                {
                    var source         = new TaskCompletionSource <bool>();
                    var connectionTask = CreateConnectionWithEmptyServerRequest(c =>
                    {
                        // Keep the connection active until we decide to end it.
                        c.WaitForDisconnectAsyncFunc = _ => source.Task;
                    });
                    list.Add(source);
                    return(connectionTask);
                }

                readySource.SetResult(true);
                return(new TaskCompletionSource <Connection>().Task);
            });

            var compilerHost = CreateCompilerHost(c =>
            {
                c.ExecuteFunc = (req, ct) =>
                {
                    return(EmptyServerResponse);
                };
            });

            var eventBus = new TestableEventBus();
            var completedCompilations   = 0;
            var allCompilationsComplete = new TaskCompletionSource <bool>();

            eventBus.CompilationComplete += (obj, args) =>
            {
                if (++completedCompilations == totalCount)
                {
                    // All compilations have completed.
                    allCompilationsComplete.SetResult(true);
                }
            };
            var keepAlive      = TimeSpan.FromSeconds(1);
            var dispatcherTask = Task.Run(() =>
            {
                var dispatcher = new DefaultRequestDispatcher(connectionHost.Object, compilerHost, CancellationToken.None, eventBus, keepAlive);
                dispatcher.Run();
            });

            // Wait for all connections to be created.
            await readySource.Task;

            // Wait for all compilations to complete.
            await allCompilationsComplete.Task;

            // Now allow all the connections to be disconnected.
            foreach (var source in list)
            {
                source.SetResult(true);
            }

            // Act
            // Now dispatcher should be in an idle state with no active connections.
            await dispatcherTask;

            // Assert
            Assert.False(eventBus.HasDetectedBadConnection);
            Assert.Equal(totalCount, eventBus.CompletedCount);
            Assert.True(eventBus.LastProcessedTime.HasValue, "LastProcessedTime should have had a value.");
            Assert.True(eventBus.HitKeepAliveTimeout, "HitKeepAliveTimeout should have been hit.");
        }