Expecting googlemock calls from another thread

淺唱寂寞╮ 提交于 2019-11-29 09:29:11

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.

Using lambdas, you could do something like (I have put boost equivalents in comments):

TEST_F(BarTest, DoSomethingWhenFunc2Gt0)
{
    std::mutex mutex;                  // boost::mutex mutex;
    std::condition_variable cond_var;  // boost::condition_variable cond_var;
    bool done(false);

    EXPECT_CALL(fooInterfaceMock, func1())
        .Times(1);
    EXPECT_CALL(fooInterfaceMock, func2())
        .Times(1)
        .WillOnce(testing::Invoke([&]()->int {
            std::lock_guard<std::mutex> lock(mutex);  // boost::mutex::scoped_lock lock(mutex);
            done = true;
            cond_var.notify_one();
            return 1; }));

    bar.start();
    bar.triggerDoSomething();
    {
      std::unique_lock<std::mutex> lock(mutex);               // boost::mutex::scoped_lock lock(mutex);
      EXPECT_TRUE(cond_var.wait_for(lock,                     // cond_var.timed_wait
                                    std::chrono::seconds(1),  // boost::posix_time::seconds(1),
                                    [&done] { return done; }));
    }
    bar.stop();
}

If you can't use lambdas, I imagine you could use boost::bind instead.

Fraser's answer inspired me also. I used his suggestion, and it worked, but then I found another way to accomplish the same without the condition variable. You'll need to add a method to check some condition, and you'll need an infinite loop. This is also assuming that you have a separate thread that will update the condition.

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

    bar.start();
    bar.triggerDoSomething();

    // How long of a wait is too long?
    auto now = chrono::system_clock::now();
    auto tooLong = now + std::chrono::milliseconds(50); 

    /* Expect your thread to update this condition, so execution will continue
     * as soon as the condition is updated and you won't have to sleep
     * for the remainder of the time
     */
    while (!bar.condition() && (now = chrono::system_clock::now()) < tooLong) 
    {
        /* Not necessary in all cases, but some compilers may optimize out
         * the while loop if there's no loop body.
         */
        this_thread::sleep_for(chrono::milliseconds(1));
    }

    // If the assertion fails, then time ran out.  
    ASSERT_LT(now, tooLong);

    bar.stop();
}
atomcraft

So I liked these solutions, but thought it might be easier with a promise, I had to wait for my test to startup:

std::promise<void> started;
EXPECT_CALL(mock, start_test())
    .Times(1)
    .WillOnce(testing::Invoke([&started]() {
        started.set_value();
    }));
system_->start();
EXPECT_EQ(std::future_status::ready, started.get_future().wait_for(std::chrono::seconds(3)));
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!