Beispiel #1
0
        /// <summary>
        /// Tests that handler does not have to remove an item from the collection
        /// and that the handled item is the item on which the handler completed.
        /// </summary>
        private async Task HandleableCollectionHandleNoRemovalTestCore(bool useAsync)
        {
            using var collection = new HandleableCollection <int>();
            Assert.Empty(collection);

            var shim = new HandleableCollectionApiShim <int>(collection, useAsync);

            AddRangeAndVerifyItems(collection, endInclusive: 4);

            const int expectedItem = 2;

            HandleableCollection <int> .Handler handler = (int item, out bool removeItem) =>
            {
                // Do not remove any item (the purpose of this test is to handle an
                // item without removing it).
                removeItem = false;

                // Terminate handler on some item in the middle of the collection
                return(expectedItem == item);
            };

            // Wait for handled item to be returned
            int handledItem = await shim.Handle(handler, DefaultPositiveVerificationTimeout);

            Assert.Equal(expectedItem, handledItem);
            Assert.Equal(new int[] { 0, 1, 2, 3, 4 }, collection);
        }
Beispiel #2
0
        /// <summary>
        /// Tests that a non-default handler can remove items independently from
        /// handling an item and that the handled item is the item on which
        /// the handler completed.
        /// </summary>
        private async Task HandleableCollectionComplexHandlerTestCore(bool useAsync)
        {
            using var collection = new HandleableCollection <int>();
            Assert.Empty(collection);

            var shim = new HandleableCollectionApiShim <int>(collection, useAsync);

            const int expectedItem = 7;

            AddRangeAndVerifyItems(collection, endInclusive: expectedItem);

            HandleableCollection <int> .Handler handler = (int item, out bool removeItem) =>
            {
                removeItem = false;

                // Remove every third item
                if (item % 3 == 0)
                {
                    removeItem = true;
                }

                // Terminate handler on last item
                return(expectedItem == item);
            };

            int handledItem = await shim.Handle(handler, DefaultPositiveVerificationTimeout);

            Assert.Equal(expectedItem, handledItem);
            Assert.Equal(new int[] { 1, 2, 4, 5, 7 }, collection);
        }
Beispiel #3
0
        /// <summary>
        /// Tests that the default handler handles one item at a time and
        /// removes each item after each successful handling.
        /// </summary>
        private async Task HandleableCollectionDefaultHandlerTestCore(bool useAsync)
        {
            using var collection = new HandleableCollection <int>();
            Assert.Empty(collection);

            var shim = new HandleableCollectionApiShim <int>(collection, useAsync);

            AddRangeAndVerifyItems(collection, endInclusive: 14);

            int expectedCollectionCount = collection.Count();

            for (int item = 0; item < 15; item++)
            {
                int handledItem = await shim.Handle(DefaultPositiveVerificationTimeout);

                expectedCollectionCount--;

                Assert.Equal(item, handledItem);
                Assert.Equal(expectedCollectionCount, collection.Count());
            }

            Assert.Empty(collection);

            await shim.Handle(DefaultNegativeVerificationTimeout, expectTimeout : true);
        }
Beispiel #4
0
        public async Task HandleableCollectionThrowsWhenDisposedTest()
        {
            var collection = new HandleableCollection <int>();

            AddRangeAndVerifyItems(collection, endInclusive: 9);

            HandleableCollection <int> .Handler handler = (int item, out bool removeItem) =>
            {
                removeItem = false;
                return(20 == item);
            };

            using var cancellation = new CancellationTokenSource(DefaultPositiveVerificationTimeout);

            Task handleTask      = Task.Run(() => collection.Handle(handler, DefaultPositiveVerificationTimeout));
            Task handleAsyncTask = collection.HandleAsync(handler, cancellation.Token);

            // Task.Delay intentionally shorter than default timeout to check that Handle*
            // calls did not complete quickly.
            Task delayTask     = Task.Delay(TimeSpan.FromSeconds(1));
            Task completedTask = await Task.WhenAny(delayTask, handleTask, handleAsyncTask);

            // Check that the handle tasks didn't complete
            Assert.Equal(delayTask, completedTask);

            collection.Dispose();

            // Incomplete calls from prior to disposal should throw ObjectDisposedException
            await Assert.ThrowsAsync <ObjectDisposedException>(() => handleTask);

            await Assert.ThrowsAsync <ObjectDisposedException>(() => handleAsyncTask);

            // New calls should throw ObjectDisposedException
            Assert.Throws <ObjectDisposedException>(
                () => collection.Add(10));

            Assert.Throws <ObjectDisposedException>(
                () => collection.ClearItems());

            Assert.Throws <ObjectDisposedException>(
                () => collection.Handle(DefaultPositiveVerificationTimeout));

            Assert.Throws <ObjectDisposedException>(
                () => collection.Handle(handler, DefaultPositiveVerificationTimeout));

            await Assert.ThrowsAsync <ObjectDisposedException>(
                () => collection.HandleAsync(cancellation.Token));

            await Assert.ThrowsAsync <ObjectDisposedException>(
                () => collection.HandleAsync(handler, cancellation.Token));

            Assert.Throws <ObjectDisposedException>(
                () => ((IEnumerable)collection).GetEnumerator());

            Assert.Throws <ObjectDisposedException>(
                () => ((IEnumerable <int>)collection).GetEnumerator());
        }
Beispiel #5
0
        public async Task HandleableCollectionClearItemsTest()
        {
            using var collection = new HandleableCollection <int>();
            Assert.Empty(collection);

            AddRangeAndVerifyItems(collection, endInclusive: 4);

            HandleableCollection <int> .Handler handler = (int value, out bool removeItem) =>
            {
                if (value == 7)
                {
                    removeItem = true;
                    return(true);
                }

                removeItem = false;
                return(false);
            };

            using var cancellation = new CancellationTokenSource(DefaultPositiveVerificationTimeout);
            Task handleAsyncTask = Task.Run(() => collection.HandleAsync(handler, cancellation.Token));

            // Task.Delay intentionally shorter than default timeout to check that HandleAsync
            // calls did not complete quickly.
            Task delayTask     = Task.Delay(TimeSpan.FromSeconds(1));
            Task completedTask = await Task.WhenAny(delayTask, handleAsyncTask);

            // Check that the handle task didn't complete
            Assert.Equal(delayTask, completedTask);

            collection.ClearItems();
            Assert.Empty(collection);

            // The remainder of the test checks that the previously registered handler is still
            // registered with the collection and was not removed by calling ClearItems.
            IList <(int, int)> itemsAndCounts = new List <(int, int)>();

            itemsAndCounts.Add((6, 1));
            itemsAndCounts.Add((7, 1)); // Item is consumed immediately, thus collection count does not change
            itemsAndCounts.Add((8, 2));
            AddAndVerifyItems(collection, itemsAndCounts);

            // Task.Delay intentionally longer than default timeout to check that HandleAsync
            // does complete by handling a value. The delay Task is used in case the handler doesn't
            // handle a value and doesn't respect cancellation so as to not stall the test indefinitely.
            delayTask     = Task.Delay(2 * DefaultPositiveVerificationTimeout);
            completedTask = await Task.WhenAny(delayTask, handleAsyncTask);

            // Check that the handle task did complete
            Assert.Equal(handleAsyncTask, completedTask);

            // Check that the value was removed
            Assert.Equal(new int[] { 6, 8 }, collection);
        }
Beispiel #6
0
        /// <summary>
        /// Tests that handler can be added before an item is provided to the collection.
        /// </summary>
        private async Task HandleableCollectionHandleBeforeAddTestCore(bool useAsync)
        {
            using var collection = new TestHandleableCollection <int>();
            Assert.Empty(collection);

            var shim = new HandleableCollectionApiShim <int>(collection, useAsync);

            // Register to be notified when handler is beginning to be processed
            Task handlerBeginTask = collection.WaitForHandlerBeginAsync(DefaultPositiveVerificationTimeout);

            const int expectedItem = 3;

            HandleableCollection <int> .Handler handler = (int item, out bool removeItem) =>
            {
                // Terminate handler on some item in the middle of the collection
                if (expectedItem == item)
                {
                    removeItem = true;
                    return(true);
                }

                removeItem = false;
                return(false);
            };

            // Create task that will start handling BEFORE an item is added
            Task <int> handleItemTask = Task.Run(() => shim.Handle(handler, DefaultPositiveVerificationTimeout));

            // Wait for handler to begin processing
            Task delayTask     = Task.Delay(5 * DefaultPositiveVerificationTimeout);
            Task completedTask = await Task.WhenAny(delayTask, handlerBeginTask);

            Assert.Equal(handlerBeginTask, completedTask);

            IList <(int, int)> itemsAndCounts = new List <(int, int)>()
            {
                (0, 1),
                (1, 2),
                (2, 3),
                (3, 3), // Item is consumed immediately, thus collection count does not change
                (4, 4),
                (5, 5)
            };

            AddAndVerifyItems(collection, itemsAndCounts);

            // Wait for handled item to be returned
            int handledItem = await handleItemTask;

            Assert.Equal(expectedItem, handledItem);
            Assert.Equal(new int[] { 0, 1, 2, 4, 5 }, collection);
        }
Beispiel #7
0
        /// <summary>
        /// Tests that handler does not have to remove an item from the collection
        /// and that the handled item is the item on which the handler completed.
        /// </summary>
        private async Task HandleableCollectionHandlerThrowsTestCore(bool useAsync)
        {
            using var collection = new HandleableCollection <int>();
            Assert.Empty(collection);

            var shim = new HandleableCollectionApiShim <int>(collection, useAsync);

            AddRangeAndVerifyItems(collection, endInclusive: 4);

            HandleableCollection <int> .Handler handler = (int item, out bool removeItem) =>
            {
                if (6 == item)
                {
                    throw new InvalidOperationException();
                }

                removeItem = false;
                return(false);
            };

            Task <int> handleTask = Task.Run(() => shim.Handle(handler, DefaultPositiveVerificationTimeout));

            // Task.Delay intentionally shorter than default timeout to check that Handle*
            // calls did not complete quickly.
            Task delayTask     = Task.Delay(TimeSpan.FromSeconds(1));
            Task completedTask = await Task.WhenAny(delayTask, handleTask);

            // Check that the handle task didn't complete
            Assert.Equal(delayTask, completedTask);

            IList <(int, int)> itemsAndCounts = new List <(int, int)>();

            itemsAndCounts.Add((5, 6));
            itemsAndCounts.Add((6, 7)); // Handler should fault on this task
            itemsAndCounts.Add((7, 8));
            AddAndVerifyItems(collection, itemsAndCounts);

            // Task.Delay intentionally longer than default timeout to check that Handle*
            // does complete by handling a value. The delay Task is used in case the handler doesn't
            // handle a value and doesn't respect cancellation so as to not stall the test indefinitely.
            delayTask     = Task.Delay(2 * DefaultPositiveVerificationTimeout);
            completedTask = await Task.WhenAny(delayTask, handleTask);

            // Check that the handle task faulted and the collection did not change
            Assert.Equal(handleTask, completedTask);
            await Assert.ThrowsAsync <InvalidOperationException>(() => handleTask);

            Assert.Equal(new int[] { 0, 1, 2, 3, 4, 5, 6, 7 }, collection);
        }
        /// <summary>
        /// Listens at the address for new connections.
        /// </summary>
        /// <param name="maxConnections">The maximum number of connections the server will support.</param>
        /// <param name="token">The token to monitor for cancellation requests.</param>
        /// <returns>A task that completes when the server is no longer listening at the address.</returns>
        private async Task ListenAsync(int maxConnections, CancellationToken token)
        {
            // This disposal shuts down the transport in case an exception is thrown.
            using var transport = IpcServerTransport.Create(_address, maxConnections, _enableTcpIpProtocol, TransportCallback);
            // This disposal shuts down the transport in case of cancellation; causes the transport
            // to not recreate the server stream before the AcceptAsync call observes the cancellation.
            using var _ = token.Register(() => transport.Dispose());

            while (!token.IsCancellationRequested)
            {
                Stream       stream    = null;
                IpcAdvertise advertise = null;
                try
                {
                    stream = await transport.AcceptAsync(token).ConfigureAwait(false);
                }
                catch (OperationCanceledException)
                {
                }
                catch (Exception)
                {
                    // The advertise data could be incomplete if the runtime shuts down before completely writing
                    // the information. Catch the exception and continue waiting for a new connection.
                }

                if (null != stream)
                {
                    // Cancel parsing of advertise data after timeout period to
                    // mitigate runtimes that write partial data and do not close the stream (avoid waiting forever).
                    using var parseCancellationSource = new CancellationTokenSource();
                    using var linkedSource            = CancellationTokenSource.CreateLinkedTokenSource(token, parseCancellationSource.Token);
                    try
                    {
                        parseCancellationSource.CancelAfter(ParseAdvertiseTimeout);

                        advertise = await IpcAdvertise.ParseAsync(stream, linkedSource.Token).ConfigureAwait(false);
                    }
                    catch (Exception)
                    {
                        stream.Dispose();
                    }
                }

                if (null != advertise)
                {
                    Guid runtimeCookie = advertise.RuntimeInstanceCookie;
                    int  pid           = unchecked ((int)advertise.ProcessId);

                    // The valueFactory parameter of the GetOrAdd overload that uses Func<TKey, TValue> valueFactory
                    // does not execute the factory under a lock thus it is not thread-safe. Create the collection and
                    // use a thread-safe version of GetOrAdd; use equality comparison on the result to determine if
                    // the new collection was added to the dictionary or if an existing one was returned.
                    var newStreamCollection = new HandleableCollection <Stream>();
                    var streamCollection    = _streamCollections.GetOrAdd(runtimeCookie, newStreamCollection);

                    try
                    {
                        streamCollection.ClearItems();
                        streamCollection.Add(stream);

                        if (newStreamCollection == streamCollection)
                        {
                            ServerIpcEndpoint endpoint = new ServerIpcEndpoint(this, runtimeCookie);
                            _endpointInfos.Add(new IpcEndpointInfo(endpoint, pid, runtimeCookie));
                        }
                        else
                        {
                            newStreamCollection.Dispose();
                        }
                    }
                    catch (ObjectDisposedException)
                    {
                        // The stream collection could be disposed by RemoveConnection which would cause an
                        // ObjectDisposedException to be thrown if trying to clear/add the stream.
                        stream.Dispose();
                    }
                }
            }
        }
Beispiel #9
0
 private static void AddAndVerifyItems <T>(HandleableCollection <T> collection, IEnumerable <(T, int)> itemsAndCounts)