Running Google Mock from an Erlang NIF

86 views Asked by At

I work on a complex Erlang NIF written in C++. I want to be able to run unit tests and mocks by calling an exported NIF function that will initialise the google mock/test framework, passing in the correct arguments, and then call RUN_ALL_TESTS.

bool
run_tests(std::string filter)
{

#ifdef BUILD_GTEST
    const int argc = 3;
    std::string flt = "--gtest_filter=" + filter;
    const char* argv[argc] = { "libalalgo", "--gtest_output=xml:report.xml", flt.c_str() };

    ::testing::InitGoogleTest((int*) &argc, (char**) argv);
    ::testing::InitGoogleMock((int*) &argc, (char**) argv);
    int r = RUN_ALL_TESTS();
    if (r == 0)
        return true;

#endif
    return false;
}

The exported function is marked as

{"nif_run_cpp_tests", 1, pn_run_cpp_tests, ERL_NIF_DIRTY_JOB_CPU_BOUND}

The function is called from an exported method in the main erlang module call_cpp_test , which is called from a test suite run in the Erlang Common Test Framework. This works for the Google Test framework but fails when mocks are used.

%%% cpp_SUITE: [==========] Running 2 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 2 tests from test_unit_test
[ RUN      ] test_unit_test.test_unit_tests_work
[       OK ] test_unit_test.test_unit_tests_work (0 ms)
[ RUN      ] test_unit_test.test_mock_framework
/Users/barry.robinson/.cache/rebar3/bin/rebar3: line 4: 57525 Segmentation fault: 11  erl -pz /Users/barry.robinson/.cache/rebar3/vsns/${VSN}/lib/*/ebin +sbtu +A1 -noshell -boot start_clean -s rebar3 main $REBAR3_ERL_ARGS -extra "$@"
make: *** [ct] Error 139

Running it in the debugger gives me the failure point, even though GTest and GMock are built with -Dgtest_disable_pthreads=ON

* thread #37, name = '12_dirty_cpu_sc', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
    frame #0: 0x000000005d9c98d1 libalalgo.so`testing::internal::ThreadLocal<testing::Sequence*>::ValueHolder* testing::internal::CheckedDowncastToActualType<testing::internal::ThreadLocal<testing::Sequence*>::ValueHolder, testing::internal::ThreadLocalValueHolderBase>(base=0x0000700009b70000) at gtest-port.h:1127:3
   1124 template <class Derived, class Base>
   1125 Derived* CheckedDowncastToActualType(Base* base) {
   1126 #if GTEST_HAS_RTTI
-> 1127   GTEST_CHECK_(typeid(*base) == typeid(Derived));
   1128 #endif
   1129
   1130 #if GTEST_HAS_DOWNCAST_

The worrying part of that beoing EXC_BAD_ACCESS (code=EXC_I386_GPFLT). Is this just a general incompatibility with the Erlang VM? (What's the meaning of exception code "EXC_I386_GPFLT"? doesn't seem good).

The code that causes this problem works when compiled as a stand-alone executable, and running the same Mock test, which is provided below (and taken from an example on StackOverflow).

TEST(examples,mocks)
{
    class FooChild
    {
    public:
        virtual void doThis() {}
        virtual bool doThat(int n, double x) { return false; }
    };

    class MockFooChild : public FooChild
    {
    public:
        MOCK_METHOD(void, doThis, (), (override));
        MOCK_METHOD(bool, doThat, (int n, double x), (override));
    };

    class FooFighter
    {
    public:
        void doSomething(FooChild &fooChild)
        {
            fooChild.doThis();
            fooChild.doThat(4, 5);
        }
    };

    MockFooChild mockFooChild;
    FooFighter fooFighter;

    // doThis() must be called exactly 1 time.
    EXPECT_CALL(mockFooChild, doThis).Times(::testing::Exactly(1));

    // doThat() must be called exactly 1 time with parameters 4,5
    EXPECT_CALL(mockFooChild, doThat(4,5)).Times(::testing::Exactly(1));

    fooFighter.doSomething(mockFooChild);
}

I have built and run the same code as a stand-alone project with -fsanitize=address without problems. Does anyone have any ideas about what could be happening here?

Is the Google Mock framework simply incompatible with a NIF?

0

There are 0 answers