1

The latest version of Hippo Mocks (in its Git repository) looks to have added support for COM interfaces. I've tried mocking an ADO connection object; which took some tweaking of Hippo Mocks to build properly (seems the COM version of the code wasn't updated for changes in the rest of Hippo Mocks). I have it building now, but the following test fails:

MockRepository mocks;
auto pConn = mocks.Mock<ADONS::_Connection>();
mocks.OnCall(pConn, ADONS::_Connection::AddRef).Return(1);

ADONS::_ConnectionPtr conn = pConn;

The very first thing the smart pointer does is AddRef the interface. My mock shouldn't care about reference counting, so I add a call expectation that simply returns 1. However, as soon as AddRef gets called, a HippoMocks::NotImplementedException gets thrown.

Has anyone had success with mocking a COM interface with Hippo Mocks?

Bret Kuhns
  • 4,034
  • 5
  • 31
  • 43
  • Can you let me know what you needed to patch in the com version (as I am prob. the one that broke it). If you provide a short compilable example I'll have a look. – Thomas Mar 12 '13 at 10:51
  • @Thomas Absolutely, here's a patch file: https://gist.github.com/bkuhns/5142428 Please let me know if you get anywhere. I don't know enough of the low-level memory model to get any further than making the code for COM build. – Bret Kuhns Mar 12 '13 at 12:14
  • A minimal single file test case for com would be nice, as I never use com myself. – Thomas Mar 12 '13 at 12:25
  • @Thomas Never use COM? You're a lucky man. This aught to be a sufficient example: https://gist.github.com/bkuhns/5142716 I'm using Visual Studio 2010, by the way. – Bret Kuhns Mar 12 '13 at 13:26
  • Sorry, can't help you. I didn't break it and don't know how to fix it. Your best bet is to ask the author. – Thomas Mar 12 '13 at 20:32
  • @Thomas Thanks for looking at it nonetheless. I emailed dascandy, but haven't heard back yet. – Bret Kuhns Mar 12 '13 at 21:35

1 Answers1

2

I had the same issue and solved it. The actual version of hippomocks is now published on github:

https://github.com/dascandy/hippomocks

Great appreciation for the provided links, which helped to find an idea for the fix.

UPDATE, Details about my implementation and the added COM support.

First I made it work, which the following test demonstrates

class ICom 
{
public:
    virtual ~ICom() {}
    virtual long __stdcall A(void) = 0;
    virtual long __stdcall B(int) = 0;
    virtual long __stdcall C(int, int) = 0;
    ...
};


TEST(checkStdCallBase)
{
    MockRepository mocks;

    ICom* ic = mocks.Mock<ICom>();
    mocks.ExpectCall(ic, ICom::A)
        .Return(1);

    long actual = ic->A();
    EQUALS(1, actual);
}

In order to make it work I had to patch several places in hippomocks.h, the most vital in virtual_function_index method. The correction ensures correct address calculation for the call on an interface.

Second, I added some common setup helpers for COM objects, providing standard behaviour for AddRef, Release and QueryInterface.

The tests show how to use it:

MIDL_INTERFACE("4745C05E-23E6-4c6d-B9F2-E483359A8B89")
COMInterface1 : public IUnknown
{
public:
    virtual HRESULT STDMETHODCALLTYPE getTObjectCount( 
        /* [out] */ unsigned long *pCount) = 0;
};

typedef GUID ESTypeID;

MIDL_INTERFACE("356D44D9-980A-4149-A586-C5CB8B191437")
COMInterface2 : public IUnknown
{
public:
    virtual HRESULT STDMETHODCALLTYPE getMappablePackages( 
        /* [out] */ long *pSize,
        /* [size_is][size_is][out] */ ESTypeID **pIdList) = 0;
};

        TEST(CheckThat_AddCommExpectations_Stubs_QueryInterface_AddRef_Release)
{
    MockRepository mocks; 
    COMInterface1* deviceMock = mocks.Mock<COMInterface1>();

    AddComExpectations(mocks, deviceMock);

    {
        CComPtr<IUnknown> pUnk = deviceMock;  
        CComQIPtr<COMInterface1> pDevice = pUnk;

        CHECK(pDevice == pUnk);

        IUnknown* p = NULL;
        pDevice->QueryInterface(__uuidof(IUnknown), (void**)&p);

        CHECK(p == deviceMock);
    }
}

TEST(CheckThat_ConnectComInterfaces_Stubs_QueryInterface_ToEachOther)
{
    MockRepository mocks; 
    COMInterface1* deviceMock = mocks.Mock<COMInterface1>();
    COMInterface2* devMappingMock = mocks.Mock<COMInterface2>();

    ConnectComInterfaces(mocks, deviceMock, devMappingMock);

    {
        //Com objects can reach each other
        CComQIPtr<COMInterface2> pDevMapping = deviceMock;

        CHECK(pDevMapping != NULL);
        CHECK(pDevMapping == devMappingMock);

        CComQIPtr<COMInterface1> pDevNavigate = devMappingMock;

        CHECK(pDevNavigate != NULL);
        CHECK(pDevNavigate == deviceMock);
    }

}

The helper methods AddComExpectations and ConnectComInterfaces are provided in a separate header "comsupport.h". The header is an add-on for Hippomocks:

template <typename T>
void AddComExpectations(HM_NS MockRepository& mocks, T* m)
{
    mocks.OnCall(m, T::AddRef)
        .Return(1);
    mocks.OnCall(m, T::Release)
        .Return(1);
    mocks.OnCall(m, T::QueryInterface)
        .With(__uuidof(T), Out((void**)m))
        .Return(S_OK);

    mocks.OnCall(m, T::QueryInterface)
        .With(__uuidof(IUnknown), Out((void**)m))
        .Return(S_OK);

}

template <typename T1, typename T2>
void ConnectComInterfaces(HM_NS MockRepository& mocks, T1* m1, T2* m2)
{
    //from T1 to T2
    mocks.OnCall(m1, T1::QueryInterface)
        .With(__uuidof(T2), Out((void**)m2))
        .Return(S_OK);
    //from T2 to T1
    mocks.OnCall(m2, T2::QueryInterface)
        .With(__uuidof(T1), Out((void**)m1))
        .Return(S_OK);

    AddComExpectations(mocks, m1);
    AddComExpectations(mocks, m2);

    //no support for interface hierarchies
    //no Base IUnknown -> do it yourself if you really need that special case
}
mrAtari
  • 620
  • 5
  • 17
  • Please add summary or details of the solution, link only answers may not be useful if link dies in future – prasun Nov 10 '15 at 09:45
  • The work on the answer included patching multiple locations in hippoMocks Framework. – mrAtari Nov 11 '15 at 13:36
  • The key idea is in the virtual_function_index functon. Added a new case: `case 0x0424448b: if (func[7] == 0x20) return 0; return *(unsigned char *)(func + 8) / sizeof(void*);` There are also some new tests provided in test_com_support_stdcall.cpp and some helpers setting up basic COM Objects in ComSupport.h – mrAtari Nov 11 '15 at 13:46
  • @mrAtari Very nice, but I'm getting some errors while trying to compile a simple test app with VS 2015. The sample code is in https://pastebin.com/aAuvUVqg and the error log is in https://pastebin.com/074HXD0G . I've taken a deep look but I'm lost in the templates. Do you have any clues? Or should I open a github issue for hippomocks? – erelender Sep 08 '17 at 13:00
  • @erelender: Looks, that you've done everything correct. I checked the newest version of hippomocks and it has the same problem. The test suite containing the com support test do not run and produce the same compiler errors. Seems there was a checkin without running the test suite on windows. :(. I was working with an older version of hippomock for a long time and mocking for COM Interfaces was fine. – mrAtari Sep 12 '17 at 10:46
  • Phew, I'm glad I'm not the only one, thank you for checking. I'll open an issue on github and check different commits to see what went wrong where. – erelender Sep 12 '17 at 11:29
  • Well, I tried a lot of commits going way before the initial comsupport commit but all failed to compile... with c++11. After checking out cpp11 branch, it all works smoothly. – erelender Sep 12 '17 at 13:06