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:
parent
752a02143b
commit
0f0d8a5a8f
@ -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
|
|
||||||
|
@ -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));
|
||||||
|
Loading…
Reference in New Issue
Block a user