Как повторно вызывать функцию до тех пор, пока макет не будет удовлетворен? - PullRequest
0 голосов
/ 22 сентября 2018

Я пишу библиотеку с интерфейсом 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, что обратный вызов действительно называется.

Iиспользовал MockFunction<void()> для перехвата фактического обратного вызова;это вызывается статической функцией в стиле C, которая преобразует void *userData в MockFunction<void()> * и вызывает его.Это работает нормально.

Проблема в том, что обратный вызов не обязательно происходит при первом вызове processEvents;все, что я знаю, это то, что это произойдет в конечном итоге , если мы будем продолжать звонить processEvents в цикле.

Так что я думаю, мне нужно что-то вроде этого:

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

Ноэтот вымышленный AllExpectationsSatisfied, кажется, не существует.Самое близкое, что я могу найти, это VerifyAndClearExpectations, но оно сразу же делает тест неудачным, если ожидания не оправдываются с первой попытки (и очищает их для загрузки).

OfКонечно, я заставляю этот цикл работать в течение полной секунды или около того, что сделало бы тест зеленым, но также сделало бы его слишком медленным.

Кто-нибудь знает лучшее решение?

Ответы [ 2 ]

0 голосов
/ 23 сентября 2018

После публикации вопроса я подумал об использовании счетчика, который уменьшается при каждом вызове фиктивной функции.Но ответ @ 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(), чтобы повысить эффективность использования процессора.Эти улучшения оставлены читателю в качестве упражнения;)

0 голосов
/ 22 сентября 2018

Если вы ищете эффективную синхронизацию между потоками, проверьте std::condition_variable.Пока не произойдет следующее событие, ваша реализация с циклом while будет продолжать работать , вращаясь - используя ресурсы ЦП, не делая ничего полезного.

Вместо этого было бы разумнее приостановитьвыполнение вашего кода, освобождая время обработки для других потоков, пока не наступит событие, а затем подайте сигнал приостановленному потоку для возобновления его работы.Условные переменные делают именно это.За дополнительной информацией обращайтесь к документам .

. Кроме того, вам может быть интересно изучить std::future и std::promise, которые в основном заключают в себе схему ожидания чего-то, что происходит асинхронно,Найти более подробную информацию здесь .

...