Expecting googlemock calls from another thread

后端 未结 5 565
挽巷
挽巷 2021-01-11 11:26

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

相关标签:
5条回答
  • 2021-01-11 12:11

    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();
    }
    
    0 讨论(0)
  • 2021-01-11 12:13

    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.

    0 讨论(0)
  • 2021-01-11 12:21

    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)));
    
    0 讨论(0)
  • 2021-01-11 12:25

    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.

    0 讨论(0)
  • 2021-01-11 12:31

    I've managed to solve the issue after proposed by πάντα ῥεῖ solution, but with std::condition_variable. The solution become a bit different than Fraser proposed and also may be improved with lambdas.

    ACTION_P(ReturnFromAsyncCall, cv)
    {
        cv->notify_all();
    }
    
    ...
    
    TEST_F(..,..)
    {
    
       std::condition_variable cv;
       ...
       EXPECT_CALL(...).WillRepeatedly(ReturnFromAsyncCall(&cv));
    
       
       std::mutex mx;
       std::unique_lock<std::mutex> lock(mx);
       cv.wait_for(lock, std::chrono::seconds(1));
       
     }
    

    Seems mutex here just to sutisfy condition variable.

    0 讨论(0)
提交回复
热议问题