Search code examples
c++googletestgooglemock

How to call a function repeatedly until a mock has been satisfied?


I'm writing a library with a C (not C++) interface that contains an event loop, call it processEvents. This should be called in a loop, and invokes user-defined callbacks when something has happened. The "something" in this case is triggered by an RPC response that is received in a different thread, and added to an event queue which is consumed by processEvents on the main thread.

So from the point of view of the user of my library, the usage looks like this:

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

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

Now I want to test, using Google Test and Google Mock, that the callback is indeed called.

I've used MockFunction<void()> to intercept the actual callback; this is called by a C-style static function that casts the void *userData to a MockFunction<void()> * and calls it. This works fine.

The trouble is: the callback isn't necessarily happen on the first call of processEvents; all I know is that it happens eventually if we keep calling processEvents in a loop.

So I guess I need something like this:

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

But this fictional AllExpectationsSatisfied doesn't seem to exist. The closest I can find is VerifyAndClearExpectations, but it makes the test fail immediately if the expectations aren't met on the first try (and clears them, to boot).

Of course I make this loop run for a full second or so, which would make the test green, but also make it needlessly slow.

Does anyone know a better solution?


Solution

  • After posting the question, I thought of using a counter that is decremented by each mock function invocation. But @PetrMánek's answer gave me a better idea. I ended up doing something like this:

    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());
    

    (I'm using a semaphore rather than std::condition_variable because (1) spurious wakeups and (2) it can be used in case I expect multiple callback invocations.)

    Of course this still needs an overall timeout so a failing test won't hang forever. An optional timeout could also be added to try_wait() to make this more CPU-efficient. These improvements are left as an exercise to the reader ;)