I'm having a problem with QEventLoop. I want to create a "TimeBoundExerciser" for my unit test so that my SUT, which blocks on a QEventLoop, won't block the rest of the test cases. Specifically, my test case is to make sure the SUT terminates after a timeout.
The TimeBoundExerciser basically spawns a thread, executes the SUT on that thread, waits for the thread to terminate, and if it doesn't terminate after a specific amount of time, invokes the quit() method on the thread through QMetaObject::invokeMethod() and a QueuedConnection. I would expect that executing quit() will cause my nested QEventLoop to exit, terminating my thread. However, what I've found is that the quit() method is never invoked, and the thread never terminates. The code for my TimeBoundExerciser is below:
class IExerciseTheSystem
{
void operator()() = 0;
};
class TimeBoundExerciser : private QThread
{
Q_OBJECT
public:
enum CompletionType
{
TERMINATED,
FORCE_QUIT,
QUIT
};
TimeBoundExerciser(const IExerciseTheSystem& exerciser);
CompletionType exercise(unsigned long timeoutMillis);
protected:
void run();
protected slots:
void exerciseTheSystem();
private:
const IExerciseTheSystem& exerciser;
};
TimeBoundExerciser::TimeBoundExerciser(const IExerciseTheSystem& exerciser) : exerciser(exerciser)
{
}
TimeBoundExerciser::CompletionType TimeBoundExerciser::exercise(unsigned long timeoutMillis)
{
start();
while (!isRunning())
{
msleep(10);
}
moveToThread(this);
wait(timeoutMillis);
if (!isFinished())
{
bool quitResult;
QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection, Q_RETURN_ARG(bool, quitResult));
wait();
return FORCE_QUIT;
}
return QUIT;
}
void TimeBoundExerciser::run()
{
setTerminationEnabled(true);
QMetaObject::invokeMethod(this, "exerciseTheSystem", Qt::QueuedConnection);
exec();
}
void TimeBoundExerciser::exerciseTheSystem()
{
cout << "Starting exerciser" << endl;
exerciser();
cout << "Exerciser ended" << endl;
}
The exercise() method is executed on the main thread to kick off the whole process.