dascandy / hippomocks

GNU Lesser General Public License v2.1
196 stars 67 forks source link

Unable to mock calls of functions called through dlopen only when valgrind is used #87

Open Marcus10110 opened 6 years ago

Marcus10110 commented 6 years ago

First, thanks for the awesome mocking library, I'm new to it, but I'm a big fan. I'm constantly impressed by how well it works despite the challenging limitations of the language.

Environment & Libraries:

Ubuntu 16.04, gcc 7, catch, latest hippomocks master, valgrind 3.11.0 (stock valgrind from package manager)

basic repro case:

https://github.com/Marcus10110/ci_helloworld/blob/hippomocks-valgrind-repro/test/test_consumer.cpp#L87

Problem description:

I am unit testing a plugin loader. The plugin loader uses the classic pattern of exporting a create function and a destroy function. In testing, I mock both of these functions, and it works great using hippomocks' ExpectCallFunc feature to mock the function getting called. The test uses dlopen( nullptr, RTLD_LAZY ); to load the target functions out of the running unit test executable.

All tests pass, and the mocked functions are successfully called using the function pointer returned from dlsym.

However, when running tests with valgrind, via ctest -T memcheck, the mocked functions are not called. Instead the non-mocked functions are called and the test fails.

I created a repro case with a few minor edits to a C++ hello world application that's already setup with valgrind and hippomocks. My fork is here: https://github.com/Marcus10110/ci_helloworld/tree/hippomocks-valgrind-repro

The link at the top goes directly to the test that fails when run with valgrind and passes when run without valgrind. Note that valgrind does not detect memory leaks, the unit test just fails, because the REQUIRE expects the result of the mocked function, but instead gets the result of the original function.

I haven't tested this with any compilers other than gcc-7 yet. Instructions:

git clone https://github.com/Marcus10110/ci_helloworld.git
cd ci_helloworld
git checkout hippomocks-valgrind-repro

mkdir build
cd build

cmake ..
make
ctest
ctest -T memcheck

ctest should pass and ctest -T memcheck should fail.

Below is an excerpt of the code for easy reading:

#include <hippomocks.h>
#include <catch/catch.hpp>
#include <dlfcn.h>

extern "C" __attribute__((visibility("default"))) int* CreatePlugin();
using create_t = int * ( * )();

TEST_CASE("c func plugin mocked")
{
    MockRepository mocks;
    int i = 5;
    mocks.ExpectCallFunc( CreatePlugin ).Return( &i );

    void* handle = dlopen( nullptr, RTLD_LAZY );
    create_t create_func = reinterpret_cast<create_t>(dlsym( handle, "CreatePlugin" ));

    REQUIRE(create_func() == &i); //create_func() should return the address if i when mocked, and nullptr when not mocked.
}

int* CreatePlugin()
{
    return nullptr;
}
Nihlus commented 9 months ago

I'm also having this problem with hippmocks 5.0 and valgrind 3.18 on Ubuntu 22.04. Same general issue, but showing up when mocking libsodium functions and running with valgrind - non-valgrind usage works fine.