Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the most concise way to create a Task that never returns?

For testing purposes, I need to mock a Task-returning method on an interface handing back a task that never runs the continuation. Here's the code I have so far:

// FooTests.cs

[Test]
public void Foo()
{
    var yielder = Substitute.For<IYielder>();
    yielder.YieldAsync().Returns(ThreadingUtilities.NeverReturningTask);

    ...
}

// ThreadingUtilities.cs

using System.Diagnostics;
using System.Threading.Tasks;

namespace Repository.Editor.Android.UnitTests.TestInternal.Threading
{
    internal static class ThreadingUtilities
    {
        public static Task NeverReturningTask { get; } = CreateNeverReturningTask();

        private async static Task CreateNeverReturningTask()
        {
            await new StopAwaitable();
            Debug.Fail("Execution shouldn't reach here.");
        }
    }
}

// StopAwaitable.cs

using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace Repository.Editor.Android.UnitTests.TestInternal.Threading
{
    internal struct StopAwaitable : INotifyCompletion
    {
        public bool IsCompleted => false;

        public StopAwaitable GetAwaiter() => this;

        public void GetResult()
        {
            Debug.Fail("The continuation shouldn't have been posted!");
        }

        public void OnCompleted(Action continuation)
        {
            // Ignore the continuation.
        }
    }
}

Here, I've created a custom awaitable type that simply ignores the continuation handed to it-- await new StopAwaitable() has essentially the same effect as a return from a sync method. Then, I await it in an async Task method, creating a Task object that will never run the continuation.

My question is, is this the most concise/clear way to do this? Is there any other way to create such a task that doesn't involve a custom awaitable, which will confuse people unfamiliar w/ how async works?

like image 459
James Ko Avatar asked Aug 30 '17 07:08

James Ko


1 Answers

You can use:

var taskThatNeverReturns = Task.Delay(Timeout.Infinite);

The docs states that the parameter represents:

The number of milliseconds to wait before completing the returned task, or -1 to wait indefinitely.

Timeout.Infinite is a constant field with a value of -1.

like image 188
Kirk Larkin Avatar answered Oct 13 '22 22:10

Kirk Larkin