如何反复调用函数,直到满足模拟条件?

时间:2018-09-22 12:35:02

标签: c++ googletest googlemock

我正在编写一个带有C(不是C ++)接口的库,其中包含事件循环,将其称为processEvents。这应该在循环中调用,并在发生某些情况时调用用户定义的回调。在这种情况下,“某物”是由在另一个线程中接收到的RPC响应触发的,并添加到事件队列中,该事件队列由主线程上的processEvents使用。

因此,从我的图书馆用户的角度来看,用法如下:

function myCallback(void *userData) {
  // ...
}

int main() {
  setCallback(&myCallback, NULL);
  requestCallback();
  while (true) {
    processEvents(); /* Eventually calls myCallback, but not immediately. */
    doSomeOtherStuff();
  }
}

现在,我想使用Google Test和Google Mock测试回调确实已被调用。

我使用MockFunction<void()>截取了实际的回调;这由C样式的静态函数调用,该函数将void *userData转换为MockFunction<void()> *并对其进行调用。效果很好。

麻烦是:回调不一定发生在processEvents first 调用中;我所知道的是,如果我们继续循环调用processEvents,它会最终发生。

所以我想我需要这样的东西:

while (!testing::Mock::AllExpectationsSatisfied() && !timedOut()) {
  processEvents();
}

但是虚构的AllExpectationsSatisfied似乎不存在。我能找到的最接近的是VerifyAndClearExpectations,但是如果第一次尝试没有达到期望(并清除它们以进行引导),它会使测试立即失败。

当然,我使此循环运行了整整一秒左右,这会使测试变为绿色,但也不必要地使其变慢。

有人知道更好的解决方案吗?

2 个答案:

答案 0 :(得分:0)

如果您正在寻找线程之间的高效同步,请查看std::condition_variable。在发生下一个事件之前,具有while循环的实现将继续进行 spinning -耗尽CPU资源,无济于事。

相反,最好暂停执行代码,为其他线程释放处理时间,直到事件发生,然后向暂停的线程发出信号以恢复其工作。条件变量就是这样做的。有关更多信息,请查看the docs

此外,您可能会对研究std::futurestd::promise感兴趣,它们基本上封装了等待异步出现的模式。查找更多详细信息here

答案 1 :(得分:-1)

发布问题后,我想到了使用每个模拟函数调用都会递减的计数器。但是@PetrMánek的回答给了我一个更好的主意。我最终做了这样的事情:

MockFunction<void()> myMockFunction;

// Machinery to wire callback to invoke myMockFunction...

Semaphore semaphore; // Implementation from https://stackoverflow.com/a/4793662/14637
EXPECT_CALL(myMockFunction, Call())
    .WillRepeatedly(Invoke(&semaphore, &Semaphore::notify));
do {
  processEvents();
} while (semaphore.try_wait());

(我使用的是信号灯,而不是std::condition_variable,因为(1)虚假唤醒,以及(2)在我期望多次回调调用的情况下可以使用。)

当然,这仍然需要整体超时,因此失败的测试不会永远挂起。还可以向try_wait()添加可选超时,以提高CPU效率。这些改进留给读者练习;)