I have a googletest that is meant to unittest a function that calls an external fortran function by mocking the external call:
#include <iostream>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
class InterfaceTest : public ::testing::Test {};
class MockFortranFuncInterfacePointerArgs {
public:
MOCK_METHOD(void, fortran_interface_test_func_pointerargs, (int*));
};
void testfunc_pointer(MockFortranFuncInterfacePointerArgs* func_interface) {
int testscalar = 123;
func_interface->fortran_interface_test_func_pointerargs(&testscalar);
}
TEST_F(InterfaceTest, TestFuncInterfacePointerArgs) {
MockFortranFuncInterfacePointerArgs mock_func_interface;
int* passed_scalar_pointer = nullptr;
EXPECT_CALL(mock_func_interface, fortran_interface_test_func_pointerargs(testing::_))
.WillOnce(testing::DoAll(
testing::SaveArg<0>(&passed_scalar_pointer)
));
testfunc_pointer(&mock_func_interface);
std::cout << *passed_scalar_pointer << std::endl; //prints 123
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
I want to verify what value the fortran function is being called with, which works fine. However, simply adding another cout call before the std::cout << *passed_scalar_pointer << std::endl;
statement suddenly causes the program to deallocate the value stored in the original pointer:
#include <iostream>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
class InterfaceTest : public ::testing::Test {};
class MockFortranFuncInterfacePointerArgs {
public:
MOCK_METHOD(void, fortran_interface_test_func_pointerargs, (int*));
};
void testfunc_pointer(MockFortranFuncInterfacePointerArgs* func_interface) {
int testscalar = 123;
func_interface->fortran_interface_test_func_pointerargs(&testscalar);
}
TEST_F(InterfaceTest, TestFuncInterfacePointerArgs) {
MockFortranFuncInterfacePointerArgs mock_func_interface;
int* passed_scalar_pointer = nullptr;
EXPECT_CALL(mock_func_interface, fortran_interface_test_func_pointerargs(testing::_))
.WillOnce(testing::DoAll(
testing::SaveArg<0>(&passed_scalar_pointer)
));
testfunc_pointer(&mock_func_interface);
std::cout << "testprint" << std::endl;
std::cout << *passed_scalar_pointer << std::endl; //prints random integer
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
How can a simple print statement cause such behaviour? What exactly does std::cout << "testprint" << std::endl;
do that could modify a value that is stored inside of a variable?
I want to verify what value the fortran function is being called with, which works fine. However, simply adding another cout call before the
std::cout << *passed_scalar_pointer << std::endl;
statement suddenly causes the program to deallocate the value stored in the original pointer:
Here is your code run with address sanitizer.
Basically problem is that you are passing a pointer to local variable which lifetime ends when testfunc_pointer
execution ends. So this is source of UB in your test.
IMO your test is invalid since tries to verify implementation detail (value of testscalar
inside a testfunc_pointer
). It is impossible to fix your test since testfunc_pointer
is oversimplified and can't tel what actually test should do.
By filing gaps (I ca be wrong), this should have following form:
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <iostream>
class InterfaceTest : public ::testing::Test { };
class MockFortranFuncInterfacePointerArgs {
public:
MOCK_METHOD(void, fortran_interface_test_func_pointerargs, (int*));
};
int testfunc_pointer(MockFortranFuncInterfacePointerArgs* func_interface)
{
int testscalar;
func_interface->fortran_interface_test_func_pointerargs(&testscalar);
return testscalar + 55;
}
TEST_F(InterfaceTest, WhenFortranFunctionProvidesValueResultIsGraterBy55)
{
MockFortranFuncInterfacePointerArgs mock_func_interface;
const int fortranValue = 11;
EXPECT_CALL(mock_func_interface, fortran_interface_test_func_pointerargs(testing::NotNull()))
.WillOnce(testing::Invoke([fortranValue](auto* p) { *p = fortranValue; }));
EXPECT_THAT(testfunc_pointer(&mock_func_interface), fortranValue + 55);
}
https://godbolt.org/z/5qTscK47G
If you expecting that Fortran function should get value (not provide a value) by pointer, the proper solution is:
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <iostream>
class InterfaceTest : public ::testing::Test { };
class MockFortranFuncInterfacePointerArgs {
public:
MOCK_METHOD(void, fortran_interface_test_func_pointerargs, (int*));
};
void testfunc_pointer(MockFortranFuncInterfacePointerArgs* func_interface)
{
int testscalar = 123;
func_interface->fortran_interface_test_func_pointerargs(&testscalar);
}
TEST_F(InterfaceTest, FortranFunctionProvidesValue123)
{
MockFortranFuncInterfacePointerArgs mock_func_interface;
EXPECT_CALL(mock_func_interface, fortran_interface_test_func_pointerargs(testing::Pointee(123)));
testfunc_pointer(&mock_func_interface);
}
https://godbolt.org/z/G5jaW3c91 failure example
Do not use std::cout
in tests! Use assertion to express what is expected. If it fails gtest will print actual value.