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?