Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. Visual Studio
  4. Code coverage: partially covered, but WHY?

Code coverage: partially covered, but WHY?

Scheduled Pinned Locked Moved Visual Studio
c++csharptestingquestion
5 Posts 3 Posters 0 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • H Offline
    H Offline
    Hao from MSFT
    wrote on last edited by
    #1

    Dear all, I have been working on unit test in C# and found out the following line is always indicated as partially covered. ClassA *objectA = new ClassA(0); ClassA has two constructors: ClassA::ClassA() ClassA::ClassA(int param) Several notes: 1. ClassA is defined in native C++ 2. Test code is written in managed C++ 3. Code coverage analysis shows the inside of constructor ClassA::ClassA(int param) is fully covered. What do you guys think of this? Is it because that the new operation can throw some exception and we have not tested the case yet? Any ideas/comments will be appreciated. Thanks

    Hao

    D 1 Reply Last reply
    0
    • H Hao from MSFT

      Dear all, I have been working on unit test in C# and found out the following line is always indicated as partially covered. ClassA *objectA = new ClassA(0); ClassA has two constructors: ClassA::ClassA() ClassA::ClassA(int param) Several notes: 1. ClassA is defined in native C++ 2. Test code is written in managed C++ 3. Code coverage analysis shows the inside of constructor ClassA::ClassA(int param) is fully covered. What do you guys think of this? Is it because that the new operation can throw some exception and we have not tested the case yet? Any ideas/comments will be appreciated. Thanks

      Hao

      D Offline
      D Offline
      Dennis C Dietrich
      wrote on last edited by
      #2

      If you put that line in a static method, compile it and then disassemble it you'll see that there is branching to account for the possibility that new fails. Also, note that the constructor call is wrapped in a try-fault in order to free the memory allocated for the unmanaged object in case the constructor fails:

      .method public hidebysig static void Test() cil managed
      {
      // Code size 30 (0x1e)
      .maxstack 2
      .locals ([0] valuetype ClassA* V_0)
      IL_0000: ldc.i4.1
      IL_0001: call void* modopt([mscorlib]System.Runtime.CompilerServices.CallConvCdecl) new(uint32)
      IL_0006: stloc.0
      .try
      {
      IL_0007: ldloc.0
      IL_0008: brtrue.s IL_000c
      IL_000a: leave.s IL_001d
      IL_000c: ldloc.0
      IL_000d: ldc.i4.0
      IL_000e: call valuetype ClassA* modopt([mscorlib]System.Runtime.CompilerServices.CallConvThiscall) 'ClassA.{ctor}'(valuetype ClassA* modopt([mscorlib]System.Runtime.CompilerServices.IsConst) modopt([mscorlib]System.Runtime.CompilerServices.IsConst),
      int32)
      IL_0013: pop
      IL_0014: leave.s IL_001d
      } // end .try
      fault
      {
      IL_0016: ldloc.0
      IL_0017: call void modopt([mscorlib]System.Runtime.CompilerServices.CallConvCdecl) delete(void*)
      IL_001c: endfinally
      } // end handler
      IL_001d: ret
      } // end of method Class1::Test

      If ClassA were managed the compiled code would be a single (and much simpler) block:

      .method public hidebysig static void Test2() cil managed
      {
      // Code size 8 (0x8)
      .maxstack 1
      IL_0000: ldc.i4.0
      IL_0001: newobj instance void ClassLibrary1.Class1::.ctor(int32)
      IL_0006: pop
      IL_0007: ret
      } // end of method Class1::Test2

      Best regards Dennis

      H 1 Reply Last reply
      0
      • D Dennis C Dietrich

        If you put that line in a static method, compile it and then disassemble it you'll see that there is branching to account for the possibility that new fails. Also, note that the constructor call is wrapped in a try-fault in order to free the memory allocated for the unmanaged object in case the constructor fails:

        .method public hidebysig static void Test() cil managed
        {
        // Code size 30 (0x1e)
        .maxstack 2
        .locals ([0] valuetype ClassA* V_0)
        IL_0000: ldc.i4.1
        IL_0001: call void* modopt([mscorlib]System.Runtime.CompilerServices.CallConvCdecl) new(uint32)
        IL_0006: stloc.0
        .try
        {
        IL_0007: ldloc.0
        IL_0008: brtrue.s IL_000c
        IL_000a: leave.s IL_001d
        IL_000c: ldloc.0
        IL_000d: ldc.i4.0
        IL_000e: call valuetype ClassA* modopt([mscorlib]System.Runtime.CompilerServices.CallConvThiscall) 'ClassA.{ctor}'(valuetype ClassA* modopt([mscorlib]System.Runtime.CompilerServices.IsConst) modopt([mscorlib]System.Runtime.CompilerServices.IsConst),
        int32)
        IL_0013: pop
        IL_0014: leave.s IL_001d
        } // end .try
        fault
        {
        IL_0016: ldloc.0
        IL_0017: call void modopt([mscorlib]System.Runtime.CompilerServices.CallConvCdecl) delete(void*)
        IL_001c: endfinally
        } // end handler
        IL_001d: ret
        } // end of method Class1::Test

        If ClassA were managed the compiled code would be a single (and much simpler) block:

        .method public hidebysig static void Test2() cil managed
        {
        // Code size 8 (0x8)
        .maxstack 1
        IL_0000: ldc.i4.0
        IL_0001: newobj instance void ClassLibrary1.Class1::.ctor(int32)
        IL_0006: pop
        IL_0007: ret
        } // end of method Class1::Test2

        Best regards Dennis

        H Offline
        H Offline
        Hao from MSFT
        wrote on last edited by
        #3

        Thanks Dennis, That's very thorough. Then, I guess there is no way we can get it fully covered without making it managed. Hao

        D L 2 Replies Last reply
        0
        • H Hao from MSFT

          Thanks Dennis, That's very thorough. Then, I guess there is no way we can get it fully covered without making it managed. Hao

          D Offline
          D Offline
          Dennis C Dietrich
          wrote on last edited by
          #4

          Actually, there is. There are many fault injection[^] techniques and tools to cover error handling code paths that are difficult to execute as part of normal control flow. The real question is whether or not it's worth it. Regarding block coverage in particular, it's okay not to reach 100%. In fact, in my experience real-world code bases virtually always have code paths that are difficult to hit with automation and the benefits of automating may not outweigh the cost, e.g. when automating a particular scenario is very difficult to automate and the code path rarely changes or when making the automated test reliable proves difficult. There are alternatives though. In simple cases a code review or inspection may be enough. Or you may be able to get coverage through specialized testing such as stress or fuzz testing (which should be part of the overall test plan anyway). I suggest you consider this before making a final call. I would certainly not recommend porting unmanaged to managed code if the only reason is to increase block coverage a bit (assuming there was a good reason to mix managed and unmanaged code in the first place).

          Best regards D.C.

          1 Reply Last reply
          0
          • H Hao from MSFT

            Thanks Dennis, That's very thorough. Then, I guess there is no way we can get it fully covered without making it managed. Hao

            L Offline
            L Offline
            Lost User
            wrote on last edited by
            #5

            Hao from MSFT wrote:

            Then, I guess there is no way we can get it fully covered without making it managed.

            Since when is reaching 100% coverage more important than functionality? If unmanaged code is dropped that easily, then I'm wondering about the argumentation on using unmanaged code in the first place.

            Bastard Programmer from Hell :suss: If you can't read my code, try converting it here[^]

            1 Reply Last reply
            0
            Reply
            • Reply as topic
            Log in to reply
            • Oldest to Newest
            • Newest to Oldest
            • Most Votes


            • Login

            • Don't have an account? Register

            • Login or register to search.
            • First post
              Last post
            0
            • Categories
            • Recent
            • Tags
            • Popular
            • World
            • Users
            • Groups