100% Code Coverage of switch statement not possible. Unreachable code block
I have a c# method, as shown below. Code coverage on the method states that I have one missing block.
public void RetrieveAllRegRelationshipInfo(OwnerKeeperSteve registerKeeper)
{
switch (registerKeeper)
{
case OwnerKeeperSteve.NA:
break;
}
}
Looking at the IL, and I am no expert, we get the following:.method public hidebysig instance void RetrieveAllRegRelationshipInfo(valuetype [Confused.MotorInsurance.DataContracts]Confused.MotorInsurance.DataContracts.Enumerations.OwnerKeeperSteve registerKeeper) cil managed
{
// Code size 12 (0xc)
.maxstack 2
.locals init ([0] valuetype [Confused.MotorInsurance.DataContracts]Confused.MotorInsurance.DataContracts.Enumerations.OwnerKeeperSteve CS$4$0000)
IL_0000: nop
IL_0001: ldarg.1
IL_0002: stloc.0
IL_0003: ldloc.0
IL_0004: ldc.i4.0
IL_0005: beq.s IL_0009
IL_0007: br.s IL_000b
IL_0009: br.s IL_000b
IL_000b: ret
} // end of method RetrieveRegRelationshipInfo::RetrieveAllRegRelationshipInfo
As I said, I am no expert, but doesnt the first br.s statement unconditionally jump to IL_000b, meaning that the highlighted block can not be reached?
Answers
I recreated your scenario with the following:
Code Snippetpublic class CoverageTest
{
public enum Bar
{
X,
Y,
Z
}
public static void Foo(Bar b)
{
switch (b)
{
case Bar.X:
break;
}
}
}
With the following test case I received partial coverage (1 block not covered) as you saw:
Code Snippet[TestMethod()]
public void FooTest()
{
CoverageTest.Foo(CoverageTest.Bar.X);
}
With this test case, I received full coverage:
Code Snippet[TestMethod()]
public void FooTest()
{
CoverageTest.Foo(CoverageTest.Bar.X);
CoverageTest.Foo(CoverageTest.Bar.Y);
}
Under debug builds, the coverage "issue" with the first test case is that it does not hit the following highlighted IL instruction:
Code Snippet.method public hidebysig static void Foo(valuetype ConsoleApplication33.CoverageTest/Bar b) cil managed
{
// Code size 12 (0xc)
.maxstack 2
.locals init ([0] valuetype ConsoleApplication33.CoverageTest/Bar CS$4$0000)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: stloc.0
IL_0003: ldloc.0
IL_0004: ldc.i4.0
IL_0005: beq.s IL_0009
IL_0007: br.s IL_000b
IL_0009: br.s IL_000b
IL_000b: ret
}
Switching to a release build (optimizations on), the IL generated is much more efficient and we can get full coverage (as expected) with the first test case alone.
I would recommend collecting coverage numbers against release builds.
Regards,
Chris
All Replies
Moved the thread here.
I'm not an MSIL expert too. But so far as I know, this is a known issue. Sometimes, not all the generated MSIL code can be covered. You can see it here. Your opinion is on the right track.
I'm not an MSIL expert too. But so far as I know, this is a known issue. Sometimes, not all the generated MSIL code can be covered. You can see it here. Your opinion is on the right track.
Please ignore this, it's a duplicated reply due to network delay. Sorry.
Thanks. I guess that the example I provided is a lot less code to highlight the issue, including much less MSIL too!
So we are saying this is a compiler issue? Does anyone know when we can expect a solution? I would imaging our Java friends would have a field day when they find out the compiler is spewing out unreachable code!
While I understand that 100% coverage is not a statement on code quality in itself, if we drop below 100%, I feel that will be the start of a trend and it will be difficult to measure code quality going forward.
Hi SonicDeathMonkey,
Yes, this class of issues boils down to the code being emitted by the compiler. We've met with the compiler team and are actively working towards a solution.
Do you get different coverage results depending on your solution configuration (Debug vs. Release)? Are you testing both the positive and negative cases here (i.e. hitting the case statement and not hitting it)? According to your IL listing, there are no unreachable blocks:
{
// Code size 12 (0xc)
.maxstack 2
.locals init ([0] valuetype [Confused.MotorInsurance.DataContracts]Confused.MotorInsurance.DataContracts.Enumerations.OwnerKeeperSteve CS$4$0000)
IL_0000: nop
IL_0001: ldarg.1
IL_0002: stloc.0
IL_0003: ldloc.0
IL_0004: ldc.i4.0
IL_0005: beq.s IL_0009
IL_0007: br.s IL_000b
IL_0009: br.s IL_000b
IL_000b: ret
}
Instruction IL_0005 here jumps to instruction IL_0009 if the two values on top of the stack are equal (when the first case statement is hit), so it is reachable.
In your code coverage, what are the statistics for this method? Do any source lines show up as Not Covered in the source view?
Regards,
Chris
- Moved this thread to the profiler forums.
I recreated your scenario with the following:
Code Snippetpublic class CoverageTest
{
public enum Bar
{
X,
Y,
Z
}
public static void Foo(Bar b)
{
switch (b)
{
case Bar.X:
break;
}
}
}
With the following test case I received partial coverage (1 block not covered) as you saw:
Code Snippet[TestMethod()]
public void FooTest()
{
CoverageTest.Foo(CoverageTest.Bar.X);
}
With this test case, I received full coverage:
Code Snippet[TestMethod()]
public void FooTest()
{
CoverageTest.Foo(CoverageTest.Bar.X);
CoverageTest.Foo(CoverageTest.Bar.Y);
}
Under debug builds, the coverage "issue" with the first test case is that it does not hit the following highlighted IL instruction:
Code Snippet.method public hidebysig static void Foo(valuetype ConsoleApplication33.CoverageTest/Bar b) cil managed
{
// Code size 12 (0xc)
.maxstack 2
.locals init ([0] valuetype ConsoleApplication33.CoverageTest/Bar CS$4$0000)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: stloc.0
IL_0003: ldloc.0
IL_0004: ldc.i4.0
IL_0005: beq.s IL_0009
IL_0007: br.s IL_000b
IL_0009: br.s IL_000b
IL_000b: ret
}
Switching to a release build (optimizations on), the IL generated is much more efficient and we can get full coverage (as expected) with the first test case alone.
I would recommend collecting coverage numbers against release builds.
Regards,
Chris
Excellent, thanks very much. I will only go by code coverage results from our continuous integration server from now on, or ensure I am using a release build locally.

