Weird test coverage results for iterator block, why are these statements not executed?

Viewed 2256

I'm using dotCover to analyze code coverage of my unit tests, and I'm getting some strange results... I have an iterator method for which the coverage is not complete, but the statements that are not covered are just the closing braces at the end of the method.

Here's the method I'm testing:

    public static IEnumerable<T> CommonPrefix<T>(
        this IEnumerable<T> source,
        IEnumerable<T> other,
        IEqualityComparer<T> comparer)
    {
        source.CheckArgumentNull("source");
        other.CheckArgumentNull("other");

        return source.CommonPrefixImpl(other, comparer);
    }

    private static IEnumerable<T> CommonPrefixImpl<T>(
        this IEnumerable<T> source,
        IEnumerable<T> other,
        IEqualityComparer<T> comparer)
    {
        comparer = comparer ?? EqualityComparer<T>.Default;

        using (IEnumerator<T> en1 = source.GetEnumerator(),
                              en2 = other.GetEnumerator())
        {
            while (en1.MoveNext() && en2.MoveNext())
            {
                if (comparer.Equals(en1.Current, en2.Current))
                    yield return en1.Current;
                else
                    yield break;
            }
        } // not covered
    } // not covered

The unit test:

    [Test]
    public void Test_CommonPrefix_SpecificComparer()
    {
        var first = new[] { "Foo", "Bar", "Baz", "Titi", "Tata", "Toto" };
        var second = new[] { "FOO", "bAR", "baz", "tata", "Toto" };

        var expected = new[] { "Foo", "Bar", "Baz" };
        var actual = first.CommonPrefix(second, StringComparer.CurrentCultureIgnoreCase);
        Assert.That(actual, Is.EquivalentTo(expected));
    }

And the coverage results:

coverage results

I assume the closing brace of the using block is actually the calls to Dispose on the enumerators; but then, why is it not executed? I first suspected that NUnit wasn't disposing the enumerators, but I get the same result if I do a foreach on actual.

As for the second uncovered closing brace, I have no idea what it stands for... I guess it's related to how the compiler transforms the iterator block.

Can anyone shed some light on what these two "statements" are, and why they are not executed ?


EDIT: Peter raised a very good question: the results shown above were obtained when running the tests on a debug build. If I run the tests on a release build, coverage of the CommonPrefixImpl method is 100%, so it's probably related to compiler optimizations.

2 Answers

In addition to your question and the in detail answer, I had the following behaviour.

    // less than 100% coverage
    public static IEnumerable<T> ForEachYieldDo<T>(this IEnumerable<T> source, Action<T> action)
    {
        foreach (var x in source)
        {
            action(x);
            yield return x;
        }
    }

    // 100% code coverage
    public static IEnumerable<T> ForEachSelectDo<T>(this IEnumerable<T> source, Action<T> action)
    {
        return source.Select(x =>
        {
            action(x);
            return x;
        });
    }

Both functions have the same behaviour. The action is only executed, if the item is processed. If the retrieval of the items is stopped, the action is not executed.

Related