public async Task TestSendAsync_ConsumeCanceled() { var cts = new CancellationTokenSource(); DelegatePropagator<int, int> target = null; target = new DelegatePropagator<int, int> { OfferMessageDelegate = delegate(DataflowMessageHeader header, int value, ISourceBlock<int> source, bool consumeToAccept) { if (source == null) { return DataflowMessageStatus.Declined; } Assert.True(consumeToAccept); cts.Cancel(); bool consumed; int consumedMessage = source.ConsumeMessage(header, target, out consumed); Assert.False(consumed); Assert.Equal(expected: 0, actual: consumedMessage); return DataflowMessageStatus.Postponed; // should really be NotAvailable, but doing so causes an (expected) assert in the product code } }; Task<bool> send = target.SendAsync(42, cts.Token); await Assert.ThrowsAnyAsync<OperationCanceledException>(() => send); }
public async Task TestSendAsync_FaultyTarget() { var target = new DelegatePropagator<int, int> { OfferMessageDelegate = (header, value, source, consumeToAccept) => { if (source == null) return DataflowMessageStatus.Declined; throw new FormatException(); } }; await Assert.ThrowsAsync<FormatException>(() => target.SendAsync(1)); }
public void TestSendAsync_BehavesAsGoodSource() { ISourceBlock<int> sendSource = null; ITargetBlock<int> capturingTarget = new DelegatePropagator<int, int> { OfferMessageDelegate = delegate(DataflowMessageHeader header, int value, ISourceBlock<int> source, bool consumeToAccept) { if (source == null) { return DataflowMessageStatus.Declined; } sendSource = source; return DataflowMessageStatus.Postponed; } }; Task<bool> sendTask = capturingTarget.SendAsync(42); Assert.False(sendTask.IsCompleted); Assert.NotNull(sendSource); DataflowTestHelpers.TestConsumeReserveReleaseArgumentsExceptions(sendSource); Assert.Throws<NotSupportedException>(() => sendSource.LinkTo(DataflowBlock.NullTarget<int>())); Assert.Throws<NotSupportedException>(() => sendSource.Fault(new Exception())); Assert.Throws<NotSupportedException>(() => sendSource.Complete()); }
public async Task TestSendAsync_Canceled() { CancellationTokenSource cts; var bb = new BufferBlock<int>(new DataflowBlockOptions { BoundedCapacity = 1 }); Task<bool> t = bb.SendAsync(1, new CancellationToken(canceled: true)); Assert.True(t.IsCanceled); t = bb.SendAsync(2, new CancellationToken(canceled: false)); Assert.True(t.IsCompleted); Assert.True(t.Result); cts = new CancellationTokenSource(); t = bb.SendAsync(3, cts.Token); Assert.False(t.IsCompleted); cts.Cancel(); await Assert.ThrowsAnyAsync<OperationCanceledException>(() => t); Assert.Equal(expected: 2, actual: await bb.ReceiveAsync()); bb.Complete(); await bb.Completion; foreach (bool withCancellation in DataflowTestHelpers.BooleanValues) { var target = new DelegatePropagator<int, int>(); target.OfferMessageDelegate = (messageHeader, messageValue, source, consumeToAccept) => { if (source == null) return DataflowMessageStatus.Declined; Assert.Equal(expected: withCancellation, actual: consumeToAccept); if (consumeToAccept) { Assert.NotNull(source); bool consumed; source.ConsumeMessage(messageHeader, target, out consumed); Assert.True(consumed); } return DataflowMessageStatus.Accepted; }; t = withCancellation ? target.SendAsync(1, new CancellationTokenSource().Token) : target.SendAsync(2); Assert.True(await t); } }
public async Task TestSendAsync_ReserveRelease() { bool alreadyReservedReleased = false; foreach (bool withCancellation in DataflowTestHelpers.BooleanValues) { var cts = new CancellationTokenSource(); DelegatePropagator<int, int> target = new DelegatePropagator<int, int>(); target.OfferMessageDelegate = (messageHeader, messageValue, source, consumeToAccept) => { Assert.True(messageHeader.IsValid); Assert.Equal(expected: 42, actual: messageValue); if (source == null) { return DataflowMessageStatus.Declined; } Assert.Equal(expected: withCancellation, actual: consumeToAccept); if (!alreadyReservedReleased) { alreadyReservedReleased = true; Task.Run(() => { Assert.True(source.ReserveMessage(messageHeader, target)); if (withCancellation) cts.Cancel(); source.ReleaseReservation(messageHeader, target); }); return DataflowMessageStatus.Postponed; } else { return DataflowMessageStatus.Accepted; } }; if (withCancellation) { await Assert.ThrowsAnyAsync<OperationCanceledException>(() => target.SendAsync(42, cts.Token)); } else { Assert.True(await target.SendAsync(42)); } } }
public async Task TestNullTarget_OfferMessage() { DataflowTestHelpers.TestOfferMessage_ArgumentValidation(DataflowBlock.NullTarget<int>()); DataflowTestHelpers.TestOfferMessage_AcceptsDataDirectly(DataflowBlock.NullTarget<string>()); await DataflowTestHelpers.TestOfferMessage_AcceptsViaLinking(DataflowBlock.NullTarget<double>()); // Test OfferMessage(consumeToAccept: false) Assert.Equal( expected: DataflowMessageStatus.Accepted, actual: DataflowBlock.NullTarget<double>().OfferMessage(new DataflowMessageHeader(1), 3.14, null, consumeToAccept: false)); // Test OfferMessage(consumeToAccept: true) long consumedId = -1; DataflowBlock.NullTarget<int>().OfferMessage(new DataflowMessageHeader(42), 84, new DelegatePropagator<int, int>() { ConsumeMessageDelegate = delegate(DataflowMessageHeader messageHeader, ITargetBlock<int> target, out bool messageConsumed) { consumedId = messageHeader.Id; messageConsumed = true; return 0; } }, consumeToAccept: true); Assert.Equal(expected: 42, actual: consumedId); // Test bad source Assert.Throws<InvalidOperationException>(() => { var target = DataflowBlock.NullTarget<int>(); DataflowBlock.NullTarget<int>().OfferMessage(new DataflowMessageHeader(42), 84, new DelegatePropagator<int, int>() { ConsumeMessageDelegate = delegate(DataflowMessageHeader _, ITargetBlock<int> __, out bool ___) { throw new InvalidOperationException(); } }, consumeToAccept: true); Assert.True(target.Post(42)); }); // Test message no longer available var stingySource = new DelegatePropagator<int, int> { ConsumeMessageDelegate = (DataflowMessageHeader messageHeader, ITargetBlock<int> target, out bool messageConsumed) => { messageConsumed = false; return 0; } }; Assert.Equal( expected: DataflowMessageStatus.NotAvailable, actual: DataflowBlock.NullTarget<int>().OfferMessage( new DataflowMessageHeader(1), 1, stingySource, consumeToAccept: true)); }
public async Task TestLinkTo_DoubleLinking_ValidPropagator() { bool tested = false; var tcs = new TaskCompletionSource<bool>(); // Link a source to a target var source = new BufferBlock<int>(); DelegatePropagator<int, int> target = null; target = new DelegatePropagator<int, int> { OfferMessageDelegate = (header, value, nopPropagator, consumeToAccept) => { if (!tested) // just run once; the release below could trigger an addition offering { // Make sure the nop propagator's Completion object is the same as that of the source Assert.Same(expected: source.Completion, actual: nopPropagator.Completion); // Make sure we can reserve and release through the propagator Assert.True(nopPropagator.ReserveMessage(header, target)); nopPropagator.ReleaseReservation(header, target); // Make sure its LinkTo doesn't work; that wouldn't make sense Assert.Throws<NotSupportedException>(() => nopPropagator.LinkTo(DataflowBlock.NullTarget<int>(), new DataflowLinkOptions())); } return DataflowMessageStatus.Accepted; }, CompleteDelegate = () => tcs.SetResult(true) }; // Link from the source to the target, ensuring that we do so via a nop propagator using (source.LinkTo(target)) { source.LinkTo(target, new DataflowLinkOptions { PropagateCompletion = true }); } // Now put data in the source that it can propagator through the nop link source.Post(42); source.Complete(); // Wait for everything to shut down. await source.Completion; await tcs.Task; }
public async Task TestOutputAvailableAsync_FaultySource() { var source = new DelegatePropagator<int, int> { LinkToDelegate = delegate { throw new InvalidCastException(); } }; var t = source.OutputAvailableAsync(); await Assert.ThrowsAsync<InvalidCastException>(() => t); }
public async Task TestReleaseOnReserveException() { foreach (bool linkBadFirst in DataflowTestHelpers.BooleanValues) { var goodSource = new BufferBlock<int>(); goodSource.Post(1); DelegatePropagator<int, int> badSource = null; badSource = new DelegatePropagator<int, int> { LinkToDelegate = (target, options) => { target.OfferMessage(new DataflowMessageHeader(1), 2, badSource, consumeToAccept: true); return new DelegateDisposable(); }, ReserveMessageDelegate = delegate { throw new InvalidCastException(); } }; var batch = new BatchBlock<int>(2, new GroupingDataflowBlockOptions { Greedy = false }); if (linkBadFirst) // Each linking will offer a message { badSource.LinkTo(batch); goodSource.LinkTo(batch); } else { goodSource.LinkTo(batch); badSource.LinkTo(batch); } await Assert.ThrowsAnyAsync<InvalidCastException>(() => batch.Completion); int item; Assert.True(goodSource.TryReceive(out item)); // The good message must not be Reserved } }
public async Task TestNonGreedyFailedConsume() { foreach (bool exceptionalConsume in DataflowTestHelpers.BooleanValues) foreach (bool linkGoodFirst in DataflowTestHelpers.BooleanValues) { const int BatchSize = 2; var bb = new BatchBlock<int>(BatchSize, new GroupingDataflowBlockOptions { Greedy = false }); var goodSource = new BufferBlock<int>(); Assert.True(goodSource.Post(1)); if (linkGoodFirst) { goodSource.LinkTo(bb); } var badSource = new DelegatePropagator<int, int> { ReserveMessageDelegate = delegate { return true; }, ConsumeMessageDelegate = delegate(DataflowMessageHeader header, ITargetBlock<int> target, out bool messageConsumed) { if (exceptionalConsume) { throw new FormatException(); // throw when attempting to consume reserved message } else { messageConsumed = false; // fail when attempting to consume reserved message return 0; } } }; Assert.Equal( expected: DataflowMessageStatus.Postponed, actual: ((ITargetBlock<int>)bb).OfferMessage(new DataflowMessageHeader(2), 2, badSource, consumeToAccept: true)); if (!linkGoodFirst) { goodSource.LinkTo(bb); } await (exceptionalConsume ? (Task)Assert.ThrowsAsync<FormatException>(() => bb.Completion) : (Task)Assert.ThrowsAsync<InvalidOperationException>(() => bb.Completion)); } }
public async Task TestReleasingFailsAtCompletion() { // Create a bounded block that's filled var bb = new BufferBlock<int>(new DataflowBlockOptions { BoundedCapacity = 1 }); bb.Post(1); // Create a source that will throw an exception when a message is released, // which should happen when the buffer block drops any postponed messages var source = new DelegatePropagator<int, int> { ReserveMessageDelegate = (header, target) => true, ReleaseMessageDelegate = delegate { throw new FormatException(); } }; // Offer a message from the source. It'll be postponed. ((ITargetBlock<int>)bb).OfferMessage(new DataflowMessageHeader(1), 1, source, consumeToAccept: false); // Mark the block as complete. This should cause the block to reserve/release any postponed messages, // which will cause the block to fault. bb.Complete(); await Assert.ThrowsAsync<FormatException>(() => bb.Completion); }
public async Task TestFaultySource() { var bb = new BufferBlock<int>(new DataflowBlockOptions { BoundedCapacity = 1 }); bb.Post(1); var source = new DelegatePropagator<int, int> { ConsumeMessageDelegate = delegate(DataflowMessageHeader messageHeader, ITargetBlock<int> target, out bool messageConsumed) { throw new FormatException(); } }; Assert.Equal( expected: DataflowMessageStatus.Postponed, actual: ((ITargetBlock<int>)bb).OfferMessage(new DataflowMessageHeader(1), 2, source, true)); Assert.Equal(expected: 1, actual: bb.Receive()); await Assert.ThrowsAsync<FormatException>(() => bb.Completion); }