Stabilize QFileSystemWatcher test.

- Run each test in a temporary directory, avoid writing test
  files in source/build tree and prevents tests being influenced
  by left-overs from previous runs and locks of the application
  on the current directory.
- Modify test to be able to use absolute paths to the temporary
  directory.
- Skip parts of test removeFileAndUnWatch if a race condition
  occurs.

Task-number: QTBUG-24029
Change-Id: I215cc2e0fe6f92d2ffe597b01cdc9c9a39e3c5b4
Reviewed-by: Robin Burchell <robin+qt@viroteck.net>
This commit is contained in:
Friedemann Kleint 2012-02-14 15:52:49 +01:00 committed by Qt by Nokia
parent 752a02143b
commit 0f0d8a5a8f
2 changed files with 69 additions and 35 deletions

View File

@ -4,4 +4,3 @@ QT = core testlib
SOURCES = tst_qfilesystemwatcher.cpp SOURCES = tst_qfilesystemwatcher.cpp
mac: CONFIG += insignificant_test # QTBUG-22744 mac: CONFIG += insignificant_test # QTBUG-22744
win32:CONFIG += insignificant_test # QTBUG-24029

View File

@ -42,6 +42,7 @@
#include <QCoreApplication> #include <QCoreApplication>
#include <QTemporaryDir>
#include <QFileSystemWatcher> #include <QFileSystemWatcher>
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
@ -52,9 +53,15 @@
# endif # endif
#endif #endif
/* All tests need to run in temporary directories not used
* by the application to avoid non-deterministic failures on Windows
* due to locked directories and left-overs from previous tests. */
class tst_QFileSystemWatcher : public QObject class tst_QFileSystemWatcher : public QObject
{ {
Q_OBJECT Q_OBJECT
public:
tst_QFileSystemWatcher();
private slots: private slots:
void basicTest_data(); void basicTest_data();
@ -75,11 +82,20 @@ private slots:
void removeFileAndUnWatch(); void removeFileAndUnWatch();
void cleanup();
void destroyAfterQCoreApplication(); void destroyAfterQCoreApplication();
private:
QString m_tempDirPattern;
}; };
tst_QFileSystemWatcher::tst_QFileSystemWatcher()
{
m_tempDirPattern = QDir::tempPath();
if (!m_tempDirPattern.endsWith(QLatin1Char('/')))
m_tempDirPattern += QLatin1Char('/');
m_tempDirPattern += QStringLiteral("tst_qfilesystemwatcherXXXXXX");
}
void tst_QFileSystemWatcher::basicTest_data() void tst_QFileSystemWatcher::basicTest_data()
{ {
QTest::addColumn<QString>("backend"); QTest::addColumn<QString>("backend");
@ -92,7 +108,9 @@ void tst_QFileSystemWatcher::basicTest()
QFETCH(QString, backend); QFETCH(QString, backend);
// create test file // create test file
QFile testFile("testfile.txt"); QTemporaryDir temporaryDirectory(m_tempDirPattern);
QVERIFY(temporaryDirectory.isValid());
QFile testFile(temporaryDirectory.path() + QStringLiteral("/testfile.txt"));
testFile.setPermissions(QFile::ReadOwner | QFile::WriteOwner); testFile.setPermissions(QFile::ReadOwner | QFile::WriteOwner);
testFile.remove(); testFile.remove();
QVERIFY(testFile.open(QIODevice::WriteOnly | QIODevice::Truncate)); QVERIFY(testFile.open(QIODevice::WriteOnly | QIODevice::Truncate));
@ -151,14 +169,16 @@ void tst_QFileSystemWatcher::basicTest()
QCOMPARE(changedSpy.count(), 0); QCOMPARE(changedSpy.count(), 0);
// readd the file watch with a relative path // readd the file watch with a relative path
QVERIFY(watcher.addPath(testFile.fileName().prepend("./"))); const QString relativeTestFileName = QDir::current().relativeFilePath(testFile.fileName());
QVERIFY(!relativeTestFileName.isEmpty());
QVERIFY(watcher.addPath(relativeTestFileName));
testFile.open(QIODevice::WriteOnly | QIODevice::Truncate); testFile.open(QIODevice::WriteOnly | QIODevice::Truncate);
testFile.write(QByteArray("hello multiverse!")); testFile.write(QByteArray("hello multiverse!"));
testFile.close(); testFile.close();
QTRY_VERIFY(changedSpy.count() > 0); QTRY_VERIFY(changedSpy.count() > 0);
QVERIFY(watcher.removePath(testFile.fileName().prepend("./"))); QVERIFY(watcher.removePath(relativeTestFileName));
changedSpy.clear(); changedSpy.clear();
@ -222,15 +242,21 @@ void tst_QFileSystemWatcher::watchDirectory()
{ {
QFETCH(QString, backend); QFETCH(QString, backend);
QDir().mkdir("testDir"); QTemporaryDir temporaryDirectory(m_tempDirPattern);
QDir testDir("testDir"); QVERIFY(temporaryDirectory.isValid());
QDir temporaryDir(temporaryDirectory.path());
const QString testDirName = QStringLiteral("testDir");
QVERIFY(temporaryDir.mkdir(testDirName));
QDir testDir = temporaryDir;
QVERIFY(testDir.cd(testDirName));
QString testFileName = testDir.filePath("testFile.txt"); QString testFileName = testDir.filePath("testFile.txt");
QFile::remove(testFileName); QFile::remove(testFileName);
QFileSystemWatcher watcher; QFileSystemWatcher watcher;
watcher.setObjectName(QLatin1String("_qt_autotest_force_engine_") + backend); watcher.setObjectName(QLatin1String("_qt_autotest_force_engine_") + backend);
QVERIFY(watcher.addPath(testDir.dirName())); QVERIFY(watcher.addPath(testDir.absolutePath()));
QSignalSpy changedSpy(&watcher, SIGNAL(directoryChanged(const QString &))); QSignalSpy changedSpy(&watcher, SIGNAL(directoryChanged(const QString &)));
QVERIFY(changedSpy.isValid()); QVERIFY(changedSpy.isValid());
@ -246,7 +272,7 @@ void tst_QFileSystemWatcher::watchDirectory()
QString fileName; QString fileName;
// remove the watch, should not get notification of a new file // remove the watch, should not get notification of a new file
QVERIFY(watcher.removePath(testDir.dirName())); QVERIFY(watcher.removePath(testDir.absolutePath()));
QVERIFY(testFile.open(QIODevice::WriteOnly | QIODevice::Truncate)); QVERIFY(testFile.open(QIODevice::WriteOnly | QIODevice::Truncate));
testFile.close(); testFile.close();
@ -256,7 +282,7 @@ void tst_QFileSystemWatcher::watchDirectory()
QCOMPARE(changedSpy.count(), 0); QCOMPARE(changedSpy.count(), 0);
QVERIFY(watcher.addPath(testDir.dirName())); QVERIFY(watcher.addPath(testDir.absolutePath()));
// remove the file again, should get a signal from the watcher // remove the file again, should get a signal from the watcher
QVERIFY(testFile.remove()); QVERIFY(testFile.remove());
@ -265,7 +291,7 @@ void tst_QFileSystemWatcher::watchDirectory()
eventLoop.exec(); eventLoop.exec();
// remove the directory, should get a signal from the watcher // remove the directory, should get a signal from the watcher
QVERIFY(QDir().rmdir("testDir")); QVERIFY(temporaryDir.rmdir(testDirName));
// waiting max 5 seconds for notification for directory removal to trigger // waiting max 5 seconds for notification for directory removal to trigger
#ifdef Q_OS_WINCE #ifdef Q_OS_WINCE
@ -276,15 +302,16 @@ void tst_QFileSystemWatcher::watchDirectory()
QCOMPARE(changedSpy.at(1).count(), 1); QCOMPARE(changedSpy.at(1).count(), 1);
fileName = changedSpy.at(0).at(0).toString(); fileName = changedSpy.at(0).at(0).toString();
QCOMPARE(fileName, testDir.dirName()); QCOMPARE(fileName, testDir.absolutePath());
fileName = changedSpy.at(1).at(0).toString(); fileName = changedSpy.at(1).at(0).toString();
QCOMPARE(fileName, testDir.dirName()); QCOMPARE(fileName, testDir.absolutePath());
changedSpy.clear(); changedSpy.clear();
// recreate the file, we should not get any notification // recreate the file, we should not get any notification
if (!QDir().mkdir("testDir")) if (!temporaryDir.mkdir(testDirName))
QSKIP("Failed to recreate directory, skipping final test."); QSKIP(qPrintable(QString::fromLatin1("Failed to recreate directory '%1' under '%2', skipping final test.").
arg(testDirName, temporaryDir.absolutePath())));
// waiting max 5 seconds for notification for dir recreation to trigger // waiting max 5 seconds for notification for dir recreation to trigger
timer.start(5000); timer.start(5000);
@ -292,7 +319,7 @@ void tst_QFileSystemWatcher::watchDirectory()
QCOMPARE(changedSpy.count(), 0); QCOMPARE(changedSpy.count(), 0);
QVERIFY(QDir().rmdir("testDir")); QVERIFY(temporaryDir.rmdir(testDirName));
} }
void tst_QFileSystemWatcher::addPath() void tst_QFileSystemWatcher::addPath()
@ -360,8 +387,15 @@ void tst_QFileSystemWatcher::removePaths()
void tst_QFileSystemWatcher::watchFileAndItsDirectory() void tst_QFileSystemWatcher::watchFileAndItsDirectory()
{ {
QFETCH(QString, backend); QFETCH(QString, backend);
QDir().mkdir("testDir");
QDir testDir("testDir"); QTemporaryDir temporaryDirectory(m_tempDirPattern);
QVERIFY(temporaryDirectory.isValid());
QDir temporaryDir(temporaryDirectory.path());
const QString testDirName = QStringLiteral("testDir");
QVERIFY(temporaryDir.mkdir(testDirName));
QDir testDir = temporaryDir;
QVERIFY(testDir.cd(testDirName));
QString testFileName = testDir.filePath("testFile.txt"); QString testFileName = testDir.filePath("testFile.txt");
QString secondFileName = testDir.filePath("testFile2.txt"); QString secondFileName = testDir.filePath("testFile2.txt");
@ -378,7 +412,7 @@ void tst_QFileSystemWatcher::watchFileAndItsDirectory()
QFileSystemWatcher watcher; QFileSystemWatcher watcher;
watcher.setObjectName(QLatin1String("_qt_autotest_force_engine_") + backend); watcher.setObjectName(QLatin1String("_qt_autotest_force_engine_") + backend);
QVERIFY(watcher.addPath(testDir.dirName())); QVERIFY(watcher.addPath(testDir.absolutePath()));
QVERIFY(watcher.addPath(testFileName)); QVERIFY(watcher.addPath(testFileName));
QSignalSpy fileChangedSpy(&watcher, SIGNAL(fileChanged(const QString &))); QSignalSpy fileChangedSpy(&watcher, SIGNAL(fileChanged(const QString &)));
@ -443,17 +477,7 @@ void tst_QFileSystemWatcher::watchFileAndItsDirectory()
QCOMPARE(fileChangedSpy.count(), 0); QCOMPARE(fileChangedSpy.count(), 0);
QCOMPARE(dirChangedSpy.count(), 1); QCOMPARE(dirChangedSpy.count(), 1);
QVERIFY(QDir().rmdir("testDir")); QVERIFY(temporaryDir.rmdir(testDirName));
}
void tst_QFileSystemWatcher::cleanup()
{
QDir testDir("testDir");
QString testFileName = testDir.filePath("testFile.txt");
QString secondFileName = testDir.filePath("testFile2.txt");
QFile::remove(testFileName);
QFile::remove(secondFileName);
QDir().rmdir("testDir");
} }
void tst_QFileSystemWatcher::nonExistingFile() void tst_QFileSystemWatcher::nonExistingFile()
@ -475,22 +499,33 @@ void tst_QFileSystemWatcher::nonExistingFile()
void tst_QFileSystemWatcher::removeFileAndUnWatch() void tst_QFileSystemWatcher::removeFileAndUnWatch()
{ {
static const char * const filename = "foo.txt"; QTemporaryDir temporaryDirectory(m_tempDirPattern);
QVERIFY(temporaryDirectory.isValid());
const QString filename = temporaryDirectory.path() + QStringLiteral("/foo.txt");
QFileSystemWatcher watcher; QFileSystemWatcher watcher;
{ {
QFile testFile(filename); QFile testFile(filename);
testFile.open(QIODevice::WriteOnly); QVERIFY2(testFile.open(QIODevice::WriteOnly),
qPrintable(QString::fromLatin1("Cannot open %1 for writing: %2").arg(filename, testFile.errorString())));
testFile.close(); testFile.close();
} }
QVERIFY(watcher.addPath(filename)); QVERIFY(watcher.addPath(filename));
QFile::remove(filename); QFile::remove(filename);
QVERIFY(watcher.removePath(filename)); /* There are potential race conditions here; the watcher thread might remove the file from its list
* before the call to watcher.removePath(), which then fails. When that happens, the auto-signal
* notification to remove the file from the watcher's main list will not be delivered before the next
* event loop such that the call to watcher.addPath() fails since the file is still in the main list. */
if (!watcher.removePath(filename))
QSKIP("Skipping remaining test due to race condition.");
{ {
QFile testFile(filename); QFile testFile(filename);
testFile.open(QIODevice::WriteOnly); QVERIFY2(testFile.open(QIODevice::WriteOnly),
qPrintable(QString::fromLatin1("Cannot open %1 for writing: %2").arg(filename, testFile.errorString())));
testFile.close(); testFile.close();
} }
QVERIFY(watcher.addPath(filename)); QVERIFY(watcher.addPath(filename));