Я пытаюсь написать модульный тест для моего FileWatcher
класса.
FileWatcher
происходит от класса Thread и использует WaitForMultipleObjects
для ожидания двух дескрипторов в своей процедуре потока:
- Ручка вернулась с
FindFirstChangeNotification
- Дескриптор события, который позволяет мне отменить вышеуказанное ожидание.
Так что в основном FileWatcher
ждет того, что произойдет раньше: изменение файла или я говорю, чтобы оно прекратило смотреть.
Теперь, когда я пытаюсь написать код, который тестирует этот класс, мне нужно ждать, пока он начнет ждать.
Код Пеусдо:
FileWatcher.Wait(INFINITE)
ChangeFile()
// Verify that FileWatcher works (with some other event - unimportant...)
Проблема в том, что есть состояние гонки. Сначала мне нужно убедиться, что FileWatcher начал ждать (т. Е. Его поток теперь заблокирован на WaitForMultipleObjects
), прежде чем я смогу инициировать изменение файла в строке # 2. Я не хочу использовать Sleeps, потому что, ну, это кажется хакерским и обязательно вызовет проблемы при отладке.
Я знаком с SignalObjectAndWait
, но это на самом деле не решает мою проблему, потому что мне нужно это для "SignalObjectAndWaitOnMultipleObjects" ...
Есть идеи?
Редактировать
Для пояснения, вот упрощенная версия класса FileWatcher
:
// Inherit from this class, override OnChange, and call Start() to turn on monitoring.
class FileChangeWatcher : public Utils::Thread
{
public:
// File must exist before constructing this instance
FileChangeWatcher(const std::string& filename);
virtual int Run();
virtual void OnChange() = 0;
};
Он наследуется от Thread
и реализует функцию потока, которая выглядит примерно так (очень упрощенно):
_changeEvent = ::FindFirstChangeNotificationW(wfn.c_str(), FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE);
HANDLE events[2] = { _changeEvent, m_hStopEvent };
DWORD hWaitDone = WAIT_OBJECT_0;
while (hWaitDone == WAIT_OBJECT_0)
{
hWaitDone = ::WaitForMultipleObjects(2, events, FALSE, INFINITE);
if (hWaitDone == WAIT_OBJECT_0)
OnChange();
else
return Thread::THREAD_ABORTED;
}
return THREAD_FINISHED;
Обратите внимание, что функция потока ожидает два дескриптора, один - уведомление об изменении, а другой - событие "остановка потока" (унаследованное от Thread).
Теперь код, который тестирует этот класс, выглядит следующим образом:
class TestFileWatcher : public FileChangeWatcher
{
public:
bool Changed;
Event evtDone;
TestFileWatcher(const std::string& fname) : FileChangeWatcher(fname) { Changed = false; }
virtual void OnChange()
{
Changed = true;
evtDone.Set();
}
};
И вызывается из теста CPPUnit:
std::string tempFile = TempFilePath();
StringToFile("Hello, file", tempFile);
TestFileWatcher tfw(tempFile);
tfw.Start();
::Sleep(100); // Ugly, but we have to wait for monitor to kick in in worker thread
StringToFile("Modify me", tempFile);
tfw.evtDone.Wait(INFINITE);
CPPUNIT_ASSERT(tfw.Changed);
Идея состоит в том, чтобы избавиться от этого сна в середине.