Search code examples
visual-studio-2008c++-clicode-coverage

Partial coverage of a return statement in C++/CLI


I have C++/CLI code and I'm using Visual Studio 2008 Team Suite Code Coverage.

The code header:

// Library.h

#pragma once

#include <string>

using namespace System;

namespace Library
{
    public ref class MyClass
    {
    public:
  static void MyFoo();
  static std::string Foo();
    };
}

The code implementation:

#include "Library.h"

using namespace Library;
using namespace System;

void MyClass::MyFoo()
{
 Foo();
}

std::string MyClass::Foo()
{
 return std::string();
}

I have a C# unit test, that calls MyClass.MyFoo():

[TestMethod]
public void TestMethod1()
{
    Library.MyClass.MyFoo();
}

For some reason, I don't get a full code coverage for MyClass. The Foo() method has 3 uncovered blocks and 5 covered blocks. The closing curly brackets (}) are marked in orange - partially covered. I have no idea why is it partially covered instead of fully covered, and this is my question.

MyClass Code Coverage Print Screen http://img217.imageshack.us/img217/7664/myclasscoverage.png

UPDATE

Another Example:

Header:

// Library.h

#pragma once

using namespace System;

namespace Library
{
    struct MyStruct
    {
        int _number;
    };

    public ref class MyClass
    {
    public:
        static void MyFoo();
        static MyStruct* Foo();
    };
}

Implementation:

#include "Library.h"

using namespace Library;
using namespace System;

void MyClass::MyFoo()
{
    delete Foo();
}

MyStruct* MyClass::Foo()
{
    return new MyStruct();
}

I'm still getting the same missing coverage in Foo's return statement.


Solution

  • You aren't covering the case where the function exits via exception instead of normally. Of course if you can't even construct a zero-length std::string then your program is probably too far gone for recovery, but determining that is beyond the scope of code coverage analysis.

    EDIT: In order to improve coverage you can mock up a global operator new which fails based on some global flag (or more flexibly, fails on the Nth allocation) which you can set in your test case.

    e.g.

    int allocation_failure = 0;
    void* operator new(size_t requestedbytes)
    {
        if (allocation_failure) {
            if (!--allocation_failure) {
                throw std::bad_alloc();
            }
        }
        void* retval = malloc(requestedBytes);
        if (!retval) {
            throw std::bad_alloc();
        }
        return retval;
    }
    
    void operator delete(void* p)
    {
        if (p) free(p);
    }
    

    Or you could conditionally fail allocations of a particular size, or the Nth allocation of a particular size, etc, to exercise all the possible paths through your code.