Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
310 views
in Technique[技术] by (71.8m points)

c++ - Expecting googlemock calls from another thread

What will be the best way to write (google) test cases using a google mock object and expect the EXPECT_CALL() definitions being called from another thread controlled by the class in test? Simply calling sleep() or alike after triggering the call sequences doesn't feel appropriate since it may slow down testing unnecessary and may not really hit the timing conditions. But finishing the test case somehow has to wait until the mock methods have been called. Ideas anyone?

Here's some code to illustrate the situation:

Bar.hpp (the class under test)

class Bar
{
public:

Bar(IFooInterface* argFooInterface);
virtual ~Bar();

void triggerDoSomething();
void start();
void stop();

private:
void* barThreadMethod(void* userArgs);
void endThread();
void doSomething();

ClassMethodThread<Bar> thread; // A simple class method thread implementation using boost::thread
IFooInterface* fooInterface;
boost::interprocess::interprocess_semaphore semActionTrigger;
boost::interprocess::interprocess_semaphore semEndThread;
bool stopped;
bool endThreadRequested;
};

Bar.cpp (excerpt):

void Bar::triggerDoSomething()
{
    semActionTrigger.post();
}

void* Bar::barThreadMethod(void* userArgs)
{
    (void)userArgs;
    stopped = false;
    do
    {
        semActionTrigger.wait();
        if(!endThreadRequested && !semActionTrigger.try_wait())
        {
            doSomething();
        }
    } while(!endThreadRequested && !semEndThread.try_wait());
    stopped = true;
    return NULL;
}

void Bar::doSomething()
{
    if(fooInterface)
    {
        fooInterface->func1();
        if(fooInterface->func2() > 0)
        {
            return;
        }
        fooInterface->func3(5);
    }
}

The testing code (excerpt, nothing special in the definition of FooInterfaceMock so far):

class BarTest : public ::testing::Test
{
public:

    BarTest()
    : fooInterfaceMock()
    , bar(&fooInterfaceMock)
    {
    }

protected:
    FooInterfaceMock fooInterfaceMock;
    Bar bar;
};

TEST_F(BarTest, DoSomethingWhenFunc2Gt0)
{
    EXPECT_CALL(fooInterfaceMock,func1())
        .Times(1);
    EXPECT_CALL(fooInterfaceMock,func2())
        .Times(1)
        .WillOnce(Return(1));

    bar.start();
    bar.triggerDoSomething();
    //sleep(1);
    bar.stop();
}

Test results without sleep():

[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from BarTest
[ RUN      ] BarTest.DoSomethingWhenFunc2Gt0
../test/BarTest.cpp:39: Failure
Actual function call count doesn't match EXPECT_CALL(fooInterfaceMock, func2())...
         Expected: to be called once
           Actual: never called - unsatisfied and active
../test/BarTest.cpp:37: Failure
Actual function call count doesn't match EXPECT_CALL(fooInterfaceMock, func1())...
         Expected: to be called once
           Actual: never called - unsatisfied and active
[  FAILED  ] BarTest.DoSomethingWhenFunc2Gt0 (1 ms)
[----------] 1 test from BarTest (1 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (1 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] BarTest.DoSomethingWhenFunc2Gt0

 1 FAILED TEST
terminate called after throwing an instance of         'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::lock_error> >'
Aborted

Test results with sleep() enabled:

[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from BarTest
[ RUN      ] BarTest.DoSomethingWhenFunc2Gt0
[       OK ] BarTest.DoSomethingWhenFunc2Gt0 (1000 ms)
[----------] 1 test from BarTest (1000 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (1000 ms total)
[  PASSED  ] 1 test.

I want to avoid the sleep(), in best case without need to change the Bar class at all.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Fraser's answer inspired me for a simple solution using a GMock specialized Action. GMock makes it very easy to quickly write such Actions.

Here's the code (excerpt from BarTest.cpp):

// Specialize an action that synchronizes with the calling thread
ACTION_P2(ReturnFromAsyncCall,RetVal,SemDone)
{
    SemDone->post();
    return RetVal;
}

TEST_F(BarTest, DoSomethingWhenFunc2Gt0)
{
    boost::interprocess::interprocess_semaphore semDone(0);
    EXPECT_CALL(fooInterfaceMock,func1())
        .Times(1);
    EXPECT_CALL(fooInterfaceMock,func2())
        .Times(1)
        // Note that the return type doesn't need to be explicitly specialized
        .WillOnce(ReturnFromAsyncCall(1,&semDone));

    bar.start();
    bar.triggerDoSomething();
    boost::posix_time::ptime until = boost::posix_time::second_clock::universal_time() +
            boost::posix_time::seconds(1);
    EXPECT_TRUE(semDone.timed_wait(until));
    bar.stop();
}

TEST_F(BarTest, DoSomethingWhenFunc2Eq0)
{
    boost::interprocess::interprocess_semaphore semDone(0);
    EXPECT_CALL(fooInterfaceMock,func1())
        .Times(1);
    EXPECT_CALL(fooInterfaceMock,func2())
        .Times(1)
        .WillOnce(Return(0));
    EXPECT_CALL(fooInterfaceMock,func3(Eq(5)))
        .Times(1)
        // Note that the return type doesn't need to be explicitly specialized
        .WillOnce(ReturnFromAsyncCall(true,&semDone));

    bar.start();
    bar.triggerDoSomething();
    boost::posix_time::ptime until = boost::posix_time::second_clock::universal_time() +
            boost::posix_time::seconds(1);
    EXPECT_TRUE(semDone.timed_wait(until));
    bar.stop();
}

Note the same principle will work well for any other kind of semaphore implementation as boost::interprocess::interprocess_semaphore. I'm using it for testing with our production code that uses it's own OS abstraction layer and semaphore implementation.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...