3

In c#, async and await are turned into a state machine at compile time. My problem is hitting each piece of the state machine. I've decompiled the code and ran my exact same unit test against it to find out which branch I'm missing.

    49            void IAsyncStateMachine.MoveNext()
4   50            {
4   51              int result1 = 0;
    52              try
4   53              {
4   54                bool flag = true;
    55                TaskAwaiter<int> awaiter;
4   56                switch (this._state)
    57                {
    58                  case -3:
0   59                    goto label_6;
    60                  case 0:
1   61                    awaiter = this._awaiter2;
1   62                    this._awaiter2 = new TaskAwaiter<int>();
1   63                    this._state = -1;
1   64                    break;
    65                  default:
3   66                    awaiter = this._this._additionAsync.Add(this.value1, this.value2).GetAwaiter();
2   67                    if (!awaiter.IsCompleted)
1   68                    {
1   69                      this._state = 0;
1   70                      this._awaiter2 = awaiter;
1   71                      this._builder.AwaitUnsafeOnCompleted<TaskAwaiter<int>, CalculatorAsync.d__0>(ref awaiter, ref this);
1   72                      flag = false;
1   73                      return;
    74                    }
1   75                    break;
    76                }
2   77                int result2 = awaiter.GetResult();
2   78                awaiter = new TaskAwaiter<int>();
2   79                this._results1 = result2;
2   80                result1 = this._results1;
2   81              }
1   82              catch (Exception ex)
1   83              {
1   84                this._state = -2;
1   85                this._builder.SetException(ex);
1   86                return;
    87              }
    88            label_6:
2   89              this._state = -2;
2   90              this._builder.SetResult(result1);
4   91            }

Above you can see that line 59 is hit 0 times in terms of coverage. According to this article http://www.codeproject.com/Articles/535635/Async-Await-and-the-Generated-StateMachine it's hit when the machine is requested to stop. I haven't been able cancel the async call in a manner that hits the case.

This is a test I thought would hit it but isn't.

    [Test]
    [ExpectedException("System.InvalidOperationException")]
    public async void DecompiledCancellationTest()
    {
        var expectedResult = 4;

        var tcs = new TaskCompletionSource<int>();
        tcs.SetResult(4);
        var additionHandler = new Mock<IAdditionHandler>();

        additionHandler.Setup(x => x.Add(2, 2)).Returns(tcs.Task);
        tcs.SetCanceled();

        var calculator = new CalculatorAsync(additionHandler.Object);
        var aTaskResponse = calculator.AddDecompiled(2, 2);
        var aResponse = await aTaskResponse;
        Assert.AreEqual(expectedResult, aResponse);
    }

I've uploaded a sample to github

Using Asp.Net 4.5, Nunit 2.6.4, OpenCover 4.5.3522, and ReportGenerator 2.1.4.0

How do I get that line covered?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Daniel McMullen
  • 336
  • 3
  • 8
  • 2
    Why do you care? 100% coverage is not a realistic goal; trying to reach it is a waste of time for very little benefit (especially in this case, where there is no benefit at all). – Thomas Levesque Apr 16 '15 at 19:01
  • 2
    What is your question? You're trying to cover an implementation detail in your tests? Why would you need that? – Yuval Itzchakov Apr 16 '15 at 19:02
  • 2
    I think it is quite practical question if you read it as "what test need to be written to cover all possible ways async function can complete". I.e. it is quite easy to get success/fail for synchronous part of method, success/fail for first/subsequent async part. It is reasonable to try to find other code paths that need to be tested (or maybe not if it is not an interesting case and will just test framework's code- but that is useful info too). – Alexei Levenkov Apr 16 '15 at 19:09
  • 2
    I would like to be able to test this to know that my code responds in an expected way when it gets into the wild. I'm currently unaware of how this code gets hit and however this code gets hit would cause the await to complete without returning a result. This seems like a significant case to test for in my opinion. Plus curiosity . – Daniel McMullen Apr 16 '15 at 20:06

0 Answers0