diff options
author | Riccardo Ferrazzo <rferrazz@develer.com> | 2013-08-29 12:36:34 +0200 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2013-09-19 09:13:57 +0200 |
commit | 31db8728cf038f0a38c42d64c375403fde8598c9 (patch) | |
tree | d90706ae06b54e08f18313962f2ac6f2803e65f6 | |
parent | 73b88fccedfe3912649d119bedf8490ab6c41812 (diff) |
Fix QFileSystemWatcher inotify
On linux using QFileSystemWatcher with inotify backend when a watched
file is moved and added again to the watched files its path is not
replaced with the new one. This behavior prevents the emission of
the fileChanged signal with the wrong file path.
Task-number: QTBUG-33211
Change-Id: Ib45d8efdf5afbf8b8f6b4b26e43f3d6ee740aca6
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
-rw-r--r-- | src/corelib/io/qfilesystemwatcher_inotify.cpp | 21 | ||||
-rw-r--r-- | src/corelib/io/qfilesystemwatcher_inotify_p.h | 5 | ||||
-rw-r--r-- | tests/auto/corelib/io/qfilesystemwatcher/tst_qfilesystemwatcher.cpp | 81 |
3 files changed, 102 insertions, 5 deletions
diff --git a/src/corelib/io/qfilesystemwatcher_inotify.cpp b/src/corelib/io/qfilesystemwatcher_inotify.cpp index 024af79c33..c731f3d417 100644 --- a/src/corelib/io/qfilesystemwatcher_inotify.cpp +++ b/src/corelib/io/qfilesystemwatcher_inotify.cpp @@ -365,11 +365,11 @@ void QInotifyFileSystemWatcherEngine::readFromInotify() // qDebug() << "inotify event, wd" << event.wd << "mask" << hex << event.mask; int id = event.wd; - QString path = idToPath.value(id); + QString path = getPathFromID(id); if (path.isEmpty()) { // perhaps a directory? id = -id; - path = idToPath.value(id); + path = getPathFromID(id); if (path.isEmpty()) continue; } @@ -378,8 +378,9 @@ void QInotifyFileSystemWatcherEngine::readFromInotify() if ((event.mask & (IN_DELETE_SELF | IN_MOVE_SELF | IN_UNMOUNT)) != 0) { pathToID.remove(path); - idToPath.remove(id); - inotify_rm_watch(inotifyFd, event.wd); + idToPath.remove(id, getPathFromID(id)); + if (!idToPath.contains(id)) + inotify_rm_watch(inotifyFd, event.wd); if (id < 0) emit directoryChanged(path, true); @@ -394,6 +395,18 @@ void QInotifyFileSystemWatcherEngine::readFromInotify() } } +QString QInotifyFileSystemWatcherEngine::getPathFromID(int id) const +{ + QHash<int, QString>::const_iterator i = idToPath.find(id); + while (i != idToPath.constEnd() && i.key() == id) { + if ((i + 1) == idToPath.constEnd() || (i + 1).key() != id) { + return i.value(); + } + ++i; + } + return QString(); +} + QT_END_NAMESPACE #endif // QT_NO_FILESYSTEMWATCHER diff --git a/src/corelib/io/qfilesystemwatcher_inotify_p.h b/src/corelib/io/qfilesystemwatcher_inotify_p.h index 959d9edc12..d02f04eed6 100644 --- a/src/corelib/io/qfilesystemwatcher_inotify_p.h +++ b/src/corelib/io/qfilesystemwatcher_inotify_p.h @@ -79,10 +79,13 @@ private Q_SLOTS: void readFromInotify(); private: + QString getPathFromID(int id) const; + +private: QInotifyFileSystemWatcherEngine(int fd, QObject *parent); int inotifyFd; QHash<QString, int> pathToID; - QHash<int, QString> idToPath; + QMultiHash<int, QString> idToPath; QSocketNotifier notifier; }; diff --git a/tests/auto/corelib/io/qfilesystemwatcher/tst_qfilesystemwatcher.cpp b/tests/auto/corelib/io/qfilesystemwatcher/tst_qfilesystemwatcher.cpp index 4105a43735..20ef7b5a76 100644 --- a/tests/auto/corelib/io/qfilesystemwatcher/tst_qfilesystemwatcher.cpp +++ b/tests/auto/corelib/io/qfilesystemwatcher/tst_qfilesystemwatcher.cpp @@ -79,6 +79,8 @@ private slots: void QTBUG2331(); void QTBUG2331_data() { basicTest_data(); } + void signalsEmittedAfterFileMoved(); + private: QString m_tempDirPattern; }; @@ -596,5 +598,84 @@ void tst_QFileSystemWatcher::QTBUG2331() QCOMPARE(watcher.directories(), QStringList()); } +class SignalReceiver : public QObject +{ + Q_OBJECT +public: + SignalReceiver(const QDir &moveSrcDir, + const QString &moveDestination, + QFileSystemWatcher *watcher, + QObject *parent = 0) + : QObject(parent), + added(false), + moveSrcDir(moveSrcDir), + moveDestination(QDir(moveDestination)), + watcher(watcher) + {} + +public slots: + void fileChanged(const QString &path) + { + QFileInfo finfo(path); + + QCOMPARE(finfo.absolutePath(), moveSrcDir.absolutePath()); + + if (!added) { + foreach (const QFileInfo &fi, moveDestination.entryInfoList(QDir::Files | QDir::NoSymLinks)) + watcher->addPath(fi.absoluteFilePath()); + added = true; + } + } + +private: + bool added; + QDir moveSrcDir; + QDir moveDestination; + QFileSystemWatcher *watcher; +}; + +// regression test for QTBUG-33211. +// using inotify backend if a file is moved and then added to the watcher +// before all the fileChanged signals are emitted the remaining signals are +// emitted with the destination path instead of the starting path +void tst_QFileSystemWatcher::signalsEmittedAfterFileMoved() +{ + QTemporaryDir temporaryDirectory(m_tempDirPattern); + QVERIFY(temporaryDirectory.isValid()); + QDir testDir(temporaryDirectory.path()); + QVERIFY(testDir.mkdir("movehere")); + QString movePath = testDir.filePath("movehere"); + + for (int i = 0; i < 10; i++) { + QFile f(testDir.filePath(QString("test%1.txt").arg(i))); + QVERIFY(f.open(QIODevice::WriteOnly)); + f.write(QByteArray("i am " + i)); + f.close(); + } + + QFileSystemWatcher watcher; + QVERIFY(watcher.addPath(testDir.path())); + QVERIFY(watcher.addPath(movePath)); + + // add files to watcher + QFileInfoList files = testDir.entryInfoList(QDir::Files | QDir::NoSymLinks); + foreach (const QFileInfo &finfo, files) + QVERIFY(watcher.addPath(finfo.absoluteFilePath())); + + // create the signal receiver + SignalReceiver signalReceiver(testDir, movePath, &watcher); + connect(&watcher, SIGNAL(fileChanged(QString)), &signalReceiver, SLOT(fileChanged(QString))); + + // watch signals + QSignalSpy changedSpy(&watcher, SIGNAL(fileChanged(QString))); + QVERIFY(changedSpy.isValid()); + + // move files to second directory + foreach (const QFileInfo &finfo, files) + QVERIFY(testDir.rename(finfo.fileName(), QString("movehere/%2").arg(finfo.fileName()))); + + QTRY_COMPARE(changedSpy.count(), 10); +} + QTEST_MAIN(tst_QFileSystemWatcher) #include "tst_qfilesystemwatcher.moc" |