summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAhmad Samir <a.samirh78@gmail.com>2024-02-04 14:10:15 +0200
committerAhmad Samir <a.samirh78@gmail.com>2024-02-29 16:35:57 +0200
commit78c33a77414585b39c2c3fa14d1c88d6af7c03f1 (patch)
tree33415f89d9bd3f5212b2ccd1ed116164fbc753e2
parent6c424dbcb0385a4b36835fc5103ca74c1044eccc (diff)
Copy QDirIterator.{cpp,h} to QDirListing.{cpp,h}
To make it easier to follow the history in git. Change-Id: I094056c1ec130aeef77aa2d20289ab766bc25083 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
-rw-r--r--src/corelib/doc/snippets/code/src_corelib_io_qdirlisting.cpp28
-rw-r--r--src/corelib/io/qdirlisting.cpp575
-rw-r--r--src/corelib/io/qdirlisting.h55
-rw-r--r--tests/auto/corelib/io/qdirlisting/CMakeLists.txt45
-rw-r--r--tests/auto/corelib/io/qdirlisting/entrylist/directory/dummy0
-rw-r--r--tests/auto/corelib/io/qdirlisting/entrylist/file0
-rw-r--r--tests/auto/corelib/io/qdirlisting/tst_qdirlisting.cpp633
7 files changed, 1336 insertions, 0 deletions
diff --git a/src/corelib/doc/snippets/code/src_corelib_io_qdirlisting.cpp b/src/corelib/doc/snippets/code/src_corelib_io_qdirlisting.cpp
new file mode 100644
index 0000000000..ec3f3adc50
--- /dev/null
+++ b/src/corelib/doc/snippets/code/src_corelib_io_qdirlisting.cpp
@@ -0,0 +1,28 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+//! [0]
+QDirIterator it("/etc", QDirIterator::Subdirectories);
+while (it.hasNext()) {
+ QString dir = it.next();
+ qDebug() << dir;
+ // /etc/.
+ // /etc/..
+ // /etc/X11
+ // /etc/X11/fs
+ // ...
+}
+//! [0]
+
+//! [1]
+QDirIterator it("/sys", QStringList() << "scaling_cur_freq", QDir::NoFilter, QDirIterator::Subdirectories);
+while (it.hasNext()) {
+ QFile f(it.next());
+ f.open(QIODevice::ReadOnly);
+ qDebug() << f.fileName() << f.readAll().trimmed().toDouble() / 1000 << "MHz";
+}
+//! [1]
+
+//! [2]
+QDirIterator audioFileIt(audioPath, {"*.mp3", "*.wav"}, QDir::Files);
+//! [2]
diff --git a/src/corelib/io/qdirlisting.cpp b/src/corelib/io/qdirlisting.cpp
new file mode 100644
index 0000000000..aa725d03dd
--- /dev/null
+++ b/src/corelib/io/qdirlisting.cpp
@@ -0,0 +1,575 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+/*!
+ \since 4.3
+ \class QDirIterator
+ \inmodule QtCore
+ \brief The QDirIterator class provides an iterator for directory entrylists.
+
+ You can use QDirIterator to navigate entries of a directory one at a time.
+ It is similar to QDir::entryList() and QDir::entryInfoList(), but because
+ it lists entries one at a time instead of all at once, it scales better
+ and is more suitable for large directories. It also supports listing
+ directory contents recursively, and following symbolic links. Unlike
+ QDir::entryList(), QDirIterator does not support sorting.
+
+ The QDirIterator constructor takes a QDir or a directory as
+ argument. After construction, the iterator is located before the first
+ directory entry. Here's how to iterate over all the entries sequentially:
+
+ \snippet code/src_corelib_io_qdiriterator.cpp 0
+
+ Here's how to find and read all files filtered by name, recursively:
+
+ \snippet code/src_corelib_io_qdiriterator.cpp 1
+
+ The next() and nextFileInfo() functions advance the iterator and return
+ the path or the QFileInfo of the next directory entry. You can also call
+ filePath() or fileInfo() to get the current file path or QFileInfo without
+ first advancing the iterator. The fileName() function returns only the
+ name of the file, similar to how QDir::entryList() works.
+
+ Unlike Qt's container iterators, QDirIterator is uni-directional (i.e.,
+ you cannot iterate directories in reverse order) and does not allow random
+ access.
+
+ \sa QDir, QDir::entryList()
+*/
+
+/*! \enum QDirIterator::IteratorFlag
+
+ This enum describes flags that you can combine to configure the behavior
+ of QDirIterator.
+
+ \value NoIteratorFlags The default value, representing no flags. The
+ iterator will return entries for the assigned path.
+
+ \value Subdirectories List entries inside all subdirectories as well.
+
+ \value FollowSymlinks When combined with Subdirectories, this flag
+ enables iterating through all subdirectories of the assigned path,
+ following all symbolic links. Symbolic link loops (e.g., "link" => "." or
+ "link" => "..") are automatically detected and ignored.
+*/
+
+#include "qdiriterator.h"
+#include "qdir_p.h"
+#include "qabstractfileengine_p.h"
+
+#include <QtCore/qset.h>
+#include <QtCore/qstack.h>
+#include <QtCore/qvariant.h>
+#if QT_CONFIG(regularexpression)
+#include <QtCore/qregularexpression.h>
+#endif
+
+#include <QtCore/private/qfilesystemiterator_p.h>
+#include <QtCore/private/qfilesystementry_p.h>
+#include <QtCore/private/qfilesystemmetadata_p.h>
+#include <QtCore/private/qfilesystemengine_p.h>
+#include <QtCore/private/qfileinfo_p.h>
+#include <QtCore/private/qduplicatetracker_p.h>
+
+#include <memory>
+#include <stack>
+#include <vector>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+class QDirIteratorPrivate
+{
+public:
+ void init(bool resolveEngine);
+ void advance();
+
+ bool entryMatches(const QString & fileName, const QFileInfo &fileInfo);
+ void pushDirectory(const QFileInfo &fileInfo);
+ void checkAndPushDirectory(const QFileInfo &);
+ bool matchesFilters(const QString &fileName, const QFileInfo &fi) const;
+
+ std::unique_ptr<QAbstractFileEngine> engine;
+
+ QFileSystemEntry dirEntry;
+ QStringList nameFilters;
+ QDir::Filters filters;
+ QDirIterator::IteratorFlags iteratorFlags;
+
+#if QT_CONFIG(regularexpression)
+ QList<QRegularExpression> nameRegExps;
+#endif
+
+ using FEngineIteratorPtr = std::unique_ptr<QAbstractFileEngineIterator>;
+ std::stack<FEngineIteratorPtr, std::vector<FEngineIteratorPtr>> fileEngineIterators;
+#ifndef QT_NO_FILESYSTEMITERATOR
+ using FsIteratorPtr = std::unique_ptr<QFileSystemIterator>;
+ std::stack<FsIteratorPtr, std::vector<FsIteratorPtr>> nativeIterators;
+#endif
+
+ QFileInfo currentFileInfo;
+ QFileInfo nextFileInfo;
+
+ // Loop protection
+ QDuplicateTracker<QString> visitedLinks;
+};
+
+void QDirIteratorPrivate::init(bool resolveEngine = true)
+{
+ if (nameFilters.contains("*"_L1))
+ nameFilters.clear();
+
+ if (filters == QDir::NoFilter)
+ filters = QDir::AllEntries;
+
+#if QT_CONFIG(regularexpression)
+ nameRegExps.reserve(nameFilters.size());
+ for (const auto &filter : nameFilters) {
+ auto re = QRegularExpression::fromWildcard(filter, (filters & QDir::CaseSensitive ?
+ Qt::CaseSensitive : Qt::CaseInsensitive));
+ nameRegExps.append(re);
+ }
+#endif
+ QFileSystemMetaData metaData;
+ if (resolveEngine)
+ engine.reset(QFileSystemEngine::resolveEntryAndCreateLegacyEngine(dirEntry, metaData));
+ QFileInfo fileInfo(new QFileInfoPrivate(dirEntry, metaData));
+
+ // Populate fields for hasNext() and next()
+ pushDirectory(fileInfo);
+ advance();
+}
+
+/*!
+ \internal
+*/
+void QDirIteratorPrivate::pushDirectory(const QFileInfo &fileInfo)
+{
+ QString path = fileInfo.filePath();
+
+#ifdef Q_OS_WIN
+ if (fileInfo.isSymLink())
+ path = fileInfo.canonicalFilePath();
+#endif
+
+ if ((iteratorFlags & QDirIterator::FollowSymlinks)) {
+ // Stop link loops
+ if (visitedLinks.hasSeen(fileInfo.canonicalFilePath()))
+ return;
+ }
+
+ if (engine) {
+ engine->setFileName(path);
+ QAbstractFileEngineIterator *it = engine->beginEntryList(filters, nameFilters);
+ if (it) {
+ it->setPath(path);
+ fileEngineIterators.emplace(FEngineIteratorPtr(it));
+ } else {
+ // No iterator; no entry list.
+ }
+ } else {
+#ifndef QT_NO_FILESYSTEMITERATOR
+ nativeIterators.emplace(std::make_unique<QFileSystemIterator>(
+ fileInfo.d_ptr->fileEntry, filters));
+#else
+ qWarning("Qt was built with -no-feature-filesystemiterator: no files/plugins will be found!");
+#endif
+ }
+}
+
+inline bool QDirIteratorPrivate::entryMatches(const QString & fileName, const QFileInfo &fileInfo)
+{
+ checkAndPushDirectory(fileInfo);
+
+ if (matchesFilters(fileName, fileInfo)) {
+ currentFileInfo = nextFileInfo;
+ nextFileInfo = fileInfo;
+
+ //We found a matching entry.
+ return true;
+ }
+
+ return false;
+}
+
+/*!
+ \internal
+
+ Advances the internal iterator, either a QAbstractFileEngineIterator (e.g.
+ QResourceFileEngineIterator) or a QFileSystemIterator (which uses low-level
+ system methods, e.g. readdir() on Unix).
+
+ An iterator stack is used for holding the iterators.
+
+ A typical example of doing recursive iteration:
+ - while iterating directory A we find a sub-dir B
+ - an iterator for B is added to the iterator stack
+ - B's iterator is processed (the top() of the stack) first; then loop
+ goes back to processing A's iterator
+*/
+void QDirIteratorPrivate::advance()
+{
+ // Use get() in both code paths below because the iterator returned by top()
+ // may be invalidated due to reallocation when appending new iterators in
+ // pushDirectory().
+
+ if (engine) {
+ while (!fileEngineIterators.empty()) {
+ // Find the next valid iterator that matches the filters.
+ QAbstractFileEngineIterator *it;
+ while (it = fileEngineIterators.top().get(), it->hasNext()) {
+ it->next();
+ if (entryMatches(it->currentFileName(), it->currentFileInfo()))
+ return;
+ }
+
+ fileEngineIterators.pop();
+ }
+ } else {
+#ifndef QT_NO_FILESYSTEMITERATOR
+ QFileSystemEntry nextEntry;
+ QFileSystemMetaData nextMetaData;
+
+ while (!nativeIterators.empty()) {
+ // Find the next valid iterator that matches the filters.
+ QFileSystemIterator *it;
+ while (it = nativeIterators.top().get(), it->advance(nextEntry, nextMetaData)) {
+ QFileInfo info(new QFileInfoPrivate(nextEntry, nextMetaData));
+
+ if (entryMatches(nextEntry.fileName(), info))
+ return;
+ nextMetaData = QFileSystemMetaData();
+ }
+
+ nativeIterators.pop();
+ }
+#endif
+ }
+
+ currentFileInfo = nextFileInfo;
+ nextFileInfo = QFileInfo();
+}
+
+/*!
+ \internal
+ */
+void QDirIteratorPrivate::checkAndPushDirectory(const QFileInfo &fileInfo)
+{
+ // If we're doing flat iteration, we're done.
+ if (!(iteratorFlags & QDirIterator::Subdirectories))
+ return;
+
+ // Never follow non-directory entries
+ if (!fileInfo.isDir())
+ return;
+
+ // Follow symlinks only when asked
+ if (!(iteratorFlags & QDirIterator::FollowSymlinks) && fileInfo.isSymLink())
+ return;
+
+ // Never follow . and ..
+ QString fileName = fileInfo.fileName();
+ if ("."_L1 == fileName || ".."_L1 == fileName)
+ return;
+
+ // No hidden directories unless requested
+ if (!(filters & QDir::AllDirs) && !(filters & QDir::Hidden) && fileInfo.isHidden())
+ return;
+
+ pushDirectory(fileInfo);
+}
+
+/*!
+ \internal
+
+ This convenience function implements the iterator's filtering logics and
+ applies then to the current directory entry.
+
+ It returns \c true if the current entry matches the filters (i.e., the
+ current entry will be returned as part of the directory iteration);
+ otherwise, false is returned.
+*/
+
+bool QDirIteratorPrivate::matchesFilters(const QString &fileName, const QFileInfo &fi) const
+{
+ if (fileName.isEmpty())
+ return false;
+
+ // filter . and ..?
+ const qsizetype fileNameSize = fileName.size();
+ const bool dotOrDotDot = fileName[0] == u'.'
+ && ((fileNameSize == 1)
+ ||(fileNameSize == 2 && fileName[1] == u'.'));
+ if ((filters & QDir::NoDot) && dotOrDotDot && fileNameSize == 1)
+ return false;
+ if ((filters & QDir::NoDotDot) && dotOrDotDot && fileNameSize == 2)
+ return false;
+
+ // name filter
+#if QT_CONFIG(regularexpression)
+ // Pass all entries through name filters, except dirs if the AllDirs
+ if (!nameFilters.isEmpty() && !((filters & QDir::AllDirs) && fi.isDir())) {
+ bool matched = false;
+ for (const auto &re : nameRegExps) {
+ if (re.match(fileName).hasMatch()) {
+ matched = true;
+ break;
+ }
+ }
+ if (!matched)
+ return false;
+ }
+#endif
+ // skip symlinks
+ const bool skipSymlinks = filters.testAnyFlag(QDir::NoSymLinks);
+ const bool includeSystem = filters.testAnyFlag(QDir::System);
+ if (skipSymlinks && fi.isSymLink()) {
+ // The only reason to save this file is if it is a broken link and we are requesting system files.
+ if (!includeSystem || fi.exists())
+ return false;
+ }
+
+ // filter hidden
+ const bool includeHidden = filters.testAnyFlag(QDir::Hidden);
+ if (!includeHidden && !dotOrDotDot && fi.isHidden())
+ return false;
+
+ // filter system files
+ if (!includeSystem && (!(fi.isFile() || fi.isDir() || fi.isSymLink())
+ || (!fi.exists() && fi.isSymLink())))
+ return false;
+
+ // skip directories
+ const bool skipDirs = !(filters & (QDir::Dirs | QDir::AllDirs));
+ if (skipDirs && fi.isDir())
+ return false;
+
+ // skip files
+ const bool skipFiles = !(filters & QDir::Files);
+ if (skipFiles && fi.isFile())
+ // Basically we need a reason not to exclude this file otherwise we just eliminate it.
+ return false;
+
+ // filter permissions
+ const bool filterPermissions = ((filters & QDir::PermissionMask)
+ && (filters & QDir::PermissionMask) != QDir::PermissionMask);
+ const bool doWritable = !filterPermissions || (filters & QDir::Writable);
+ const bool doExecutable = !filterPermissions || (filters & QDir::Executable);
+ const bool doReadable = !filterPermissions || (filters & QDir::Readable);
+ if (filterPermissions
+ && ((doReadable && !fi.isReadable())
+ || (doWritable && !fi.isWritable())
+ || (doExecutable && !fi.isExecutable()))) {
+ return false;
+ }
+
+ return true;
+}
+
+/*!
+ Constructs a QDirIterator that can iterate over \a dir's entrylist, using
+ \a dir's name filters and regular filters. You can pass options via \a
+ flags to decide how the directory should be iterated.
+
+ By default, \a flags is NoIteratorFlags, which provides the same behavior
+ as in QDir::entryList().
+
+ The sorting in \a dir is ignored.
+
+ \note To list symlinks that point to non existing files, QDir::System must be
+ passed to the flags.
+
+ \sa hasNext(), next(), IteratorFlags
+*/
+QDirIterator::QDirIterator(const QDir &dir, IteratorFlags flags)
+ : d(new QDirIteratorPrivate)
+{
+ const QDirPrivate *other = dir.d_ptr.constData();
+ d->dirEntry = other->dirEntry;
+ d->nameFilters = other->nameFilters;
+ d->filters = other->filters;
+ d->iteratorFlags = flags;
+ const bool resolveEngine = other->fileEngine ? true : false;
+ d->init(resolveEngine);
+}
+
+/*!
+ Constructs a QDirIterator that can iterate over \a path, with no name
+ filtering and \a filters for entry filtering. You can pass options via \a
+ flags to decide how the directory should be iterated.
+
+ By default, \a filters is QDir::NoFilter, and \a flags is NoIteratorFlags,
+ which provides the same behavior as in QDir::entryList().
+
+ \note To list symlinks that point to non existing files, QDir::System must be
+ passed to the flags.
+
+ \sa hasNext(), next(), IteratorFlags
+*/
+QDirIterator::QDirIterator(const QString &path, QDir::Filters filters, IteratorFlags flags)
+ : d(new QDirIteratorPrivate)
+{
+ d->dirEntry = QFileSystemEntry(path);
+ d->filters = filters;
+ d->iteratorFlags = flags;
+ d->init();
+}
+
+/*!
+ Constructs a QDirIterator that can iterate over \a path. You can pass
+ options via \a flags to decide how the directory should be iterated.
+
+ By default, \a flags is NoIteratorFlags, which provides the same behavior
+ as in QDir::entryList().
+
+ \note To list symlinks that point to non existing files, QDir::System must be
+ passed to the flags.
+
+ \sa hasNext(), next(), IteratorFlags
+*/
+QDirIterator::QDirIterator(const QString &path, IteratorFlags flags)
+ : d(new QDirIteratorPrivate)
+{
+ d->dirEntry = QFileSystemEntry(path);
+ d->filters = QDir::NoFilter;
+ d->iteratorFlags = flags;
+ d->init();
+}
+
+/*!
+ Constructs a QDirIterator that can iterate over \a path, using \a
+ nameFilters and \a filters. You can pass options via \a flags to decide
+ how the directory should be iterated.
+
+ By default, \a flags is NoIteratorFlags, which provides the same behavior
+ as QDir::entryList().
+
+ For example, the following iterator could be used to iterate over audio
+ files:
+
+ \snippet code/src_corelib_io_qdiriterator.cpp 2
+
+ \note To list symlinks that point to non existing files, QDir::System must be
+ passed to the flags.
+
+ \sa hasNext(), next(), IteratorFlags, QDir::setNameFilters()
+*/
+QDirIterator::QDirIterator(const QString &path, const QStringList &nameFilters,
+ QDir::Filters filters, IteratorFlags flags)
+ : d(new QDirIteratorPrivate)
+{
+ d->dirEntry = QFileSystemEntry(path);
+ d->nameFilters = nameFilters;
+ d->filters = filters;
+ d->iteratorFlags = flags;
+ d->init();
+}
+
+/*!
+ Destroys the QDirIterator.
+*/
+QDirIterator::~QDirIterator()
+{
+}
+
+/*!
+ Advances the iterator to the next entry, and returns the file path of this
+ new entry. If hasNext() returns \c false, this function does nothing, and
+ returns an empty QString.
+
+ You can call fileName() or filePath() to get the current entry's file name
+ or path, or fileInfo() to get a QFileInfo for the current entry.
+
+ Call nextFileInfo() instead of next() if you're interested in the QFileInfo.
+
+ \sa hasNext(), nextFileInfo(), fileName(), filePath(), fileInfo()
+*/
+QString QDirIterator::next()
+{
+ d->advance();
+ return filePath();
+}
+
+/*!
+ \since 6.3
+
+ Advances the iterator to the next entry, and returns the file info of this
+ new entry. If hasNext() returns \c false, this function does nothing, and
+ returns an empty QFileInfo.
+
+ You can call fileName() or filePath() to get the current entry's file name
+ or path, or fileInfo() to get a QFileInfo for the current entry.
+
+ Call next() instead of nextFileInfo() when all you need is the filePath().
+
+ \sa hasNext(), fileName(), filePath(), fileInfo()
+*/
+QFileInfo QDirIterator::nextFileInfo()
+{
+ d->advance();
+ return fileInfo();
+}
+
+/*!
+ Returns \c true if there is at least one more entry in the directory;
+ otherwise, false is returned.
+
+ \sa next(), nextFileInfo(), fileName(), filePath(), fileInfo()
+*/
+bool QDirIterator::hasNext() const
+{
+ if (d->engine)
+ return !d->fileEngineIterators.empty();
+ else
+#ifndef QT_NO_FILESYSTEMITERATOR
+ return !d->nativeIterators.empty();
+#else
+ return false;
+#endif
+}
+
+/*!
+ Returns the file name for the current directory entry, without the path
+ prepended.
+
+ This function is convenient when iterating a single directory. When using
+ the QDirIterator::Subdirectories flag, you can use filePath() to get the
+ full path.
+
+ \sa filePath(), fileInfo()
+*/
+QString QDirIterator::fileName() const
+{
+ return d->currentFileInfo.fileName();
+}
+
+/*!
+ Returns the full file path for the current directory entry.
+
+ \sa fileInfo(), fileName()
+*/
+QString QDirIterator::filePath() const
+{
+ return d->currentFileInfo.filePath();
+}
+
+/*!
+ Returns a QFileInfo for the current directory entry.
+
+ \sa filePath(), fileName()
+*/
+QFileInfo QDirIterator::fileInfo() const
+{
+ return d->currentFileInfo;
+}
+
+/*!
+ Returns the base directory of the iterator.
+*/
+QString QDirIterator::path() const
+{
+ return d->dirEntry.filePath();
+}
+
+QT_END_NAMESPACE
diff --git a/src/corelib/io/qdirlisting.h b/src/corelib/io/qdirlisting.h
new file mode 100644
index 0000000000..7fa612ebe0
--- /dev/null
+++ b/src/corelib/io/qdirlisting.h
@@ -0,0 +1,55 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QDIRITERATOR_H
+#define QDIRITERATOR_H
+
+#include <QtCore/qdir.h>
+
+QT_BEGIN_NAMESPACE
+
+class QDirIteratorPrivate;
+class Q_CORE_EXPORT QDirIterator
+{
+public:
+ enum IteratorFlag {
+ NoIteratorFlags = 0x0,
+ FollowSymlinks = 0x1,
+ Subdirectories = 0x2
+ };
+ Q_DECLARE_FLAGS(IteratorFlags, IteratorFlag)
+
+ QDirIterator(const QDir &dir, IteratorFlags flags = NoIteratorFlags);
+ QDirIterator(const QString &path,
+ IteratorFlags flags = NoIteratorFlags);
+ QDirIterator(const QString &path,
+ QDir::Filters filter,
+ IteratorFlags flags = NoIteratorFlags);
+ QDirIterator(const QString &path,
+ const QStringList &nameFilters,
+ QDir::Filters filters = QDir::NoFilter,
+ IteratorFlags flags = NoIteratorFlags);
+
+ ~QDirIterator();
+
+ QString next();
+ QFileInfo nextFileInfo();
+ bool hasNext() const;
+
+ QString fileName() const;
+ QString filePath() const;
+ QFileInfo fileInfo() const;
+ QString path() const;
+
+private:
+ Q_DISABLE_COPY(QDirIterator)
+
+ QScopedPointer<QDirIteratorPrivate> d;
+ friend class QDir;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QDirIterator::IteratorFlags)
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tests/auto/corelib/io/qdirlisting/CMakeLists.txt b/tests/auto/corelib/io/qdirlisting/CMakeLists.txt
new file mode 100644
index 0000000000..41784546aa
--- /dev/null
+++ b/tests/auto/corelib/io/qdirlisting/CMakeLists.txt
@@ -0,0 +1,45 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## tst_qdiriterator Test:
+#####################################################################
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qdiriterator LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+# Collect test data
+list(APPEND test_data "entrylist")
+
+qt_internal_add_test(tst_qdiriterator
+ SOURCES
+ tst_qdiriterator.cpp
+ LIBRARIES
+ Qt::CorePrivate
+ TESTDATA ${test_data}
+)
+
+# Resources:
+set(qdiriterator_resource_files
+ "entrylist/directory/dummy"
+ "entrylist/file"
+)
+
+qt_internal_add_resource(tst_qdiriterator "qdiriterator"
+ PREFIX
+ "/testdata/"
+ FILES
+ ${qdiriterator_resource_files}
+)
+
+
+## Scopes:
+#####################################################################
+
+qt_internal_extend_target(tst_qdiriterator CONDITION CONFIG___contains___builtin_testdata
+ DEFINES
+ BUILTIN_TESTDATA
+)
diff --git a/tests/auto/corelib/io/qdirlisting/entrylist/directory/dummy b/tests/auto/corelib/io/qdirlisting/entrylist/directory/dummy
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/auto/corelib/io/qdirlisting/entrylist/directory/dummy
diff --git a/tests/auto/corelib/io/qdirlisting/entrylist/file b/tests/auto/corelib/io/qdirlisting/entrylist/file
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/auto/corelib/io/qdirlisting/entrylist/file
diff --git a/tests/auto/corelib/io/qdirlisting/tst_qdirlisting.cpp b/tests/auto/corelib/io/qdirlisting/tst_qdirlisting.cpp
new file mode 100644
index 0000000000..ac92b9b692
--- /dev/null
+++ b/tests/auto/corelib/io/qdirlisting/tst_qdirlisting.cpp
@@ -0,0 +1,633 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <QTest>
+
+#include <qcoreapplication.h>
+#include <qdebug.h>
+#include <qdiriterator.h>
+#include <qfileinfo.h>
+#include <qstringlist.h>
+#include <QSet>
+#include <QString>
+
+#include <QtCore/private/qfsfileengine_p.h>
+
+#if defined(Q_OS_VXWORKS)
+#define Q_NO_SYMLINKS
+#endif
+
+#include "../../../../shared/filesystem.h"
+
+#ifdef Q_OS_ANDROID
+#include <QStandardPaths>
+#endif
+
+using namespace Qt::StringLiterals;
+
+Q_DECLARE_METATYPE(QDirIterator::IteratorFlags)
+Q_DECLARE_METATYPE(QDir::Filters)
+
+class tst_QDirIterator : public QObject
+{
+ Q_OBJECT
+
+private: // convenience functions
+ QStringList createdDirectories;
+ QStringList createdFiles;
+
+ QDir currentDir;
+ bool createDirectory(const QString &dirName)
+ {
+ if (currentDir.mkdir(dirName)) {
+ createdDirectories.prepend(dirName);
+ return true;
+ }
+ return false;
+ }
+
+ bool createFile(const QString &fileName)
+ {
+ QFile file(fileName);
+ return file.open(QIODevice::WriteOnly);
+ }
+
+ bool createLink(const QString &destination, const QString &linkName)
+ {
+ if (QFile::link(destination, linkName)) {
+ createdFiles << linkName;
+ return true;
+ }
+ return false;
+ }
+
+private slots:
+ void initTestCase();
+ void iterateRelativeDirectory_data();
+ void iterateRelativeDirectory();
+ void iterateResource_data();
+ void iterateResource();
+ void stopLinkLoop();
+#ifdef QT_BUILD_INTERNAL
+ void engineWithNoIterator();
+ void testQFsFileEngineIterator_data() { iterateRelativeDirectory_data(); }
+ void testQFsFileEngineIterator();
+#endif
+ void absoluteFilePathsFromRelativeIteratorPath();
+ void recurseWithFilters() const;
+ void longPath();
+ void dirorder();
+ void relativePaths();
+#if defined(Q_OS_WIN)
+ void uncPaths_data();
+ void uncPaths();
+#endif
+#ifndef Q_OS_WIN
+ void hiddenDirs_hiddenFiles();
+#endif
+
+private:
+ QTemporaryDir m_dataDir;
+};
+
+void tst_QDirIterator::initTestCase()
+{
+#ifdef Q_OS_ANDROID
+ QString testdata_dir = QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
+ QString resourceSourcePath = QStringLiteral(":/testdata");
+ QDirIterator it(resourceSourcePath, QDirIterator::Subdirectories);
+ while (it.hasNext()) {
+ QFileInfo fileInfo = it.nextFileInfo();
+
+ if (!fileInfo.isDir()) {
+ QString destination = testdata_dir + QLatin1Char('/')
+ + fileInfo.filePath().mid(resourceSourcePath.length());
+ QFileInfo destinationFileInfo(destination);
+ if (!destinationFileInfo.exists()) {
+ QDir().mkpath(destinationFileInfo.path());
+ if (!QFile::copy(fileInfo.filePath(), destination))
+ qWarning("Failed to copy %s", qPrintable(fileInfo.filePath()));
+ }
+ }
+
+ }
+
+ testdata_dir += QStringLiteral("/entrylist");
+#elif defined(BUILTIN_TESTDATA)
+ m_dataDir = QEXTRACTTESTDATA("/testdata");
+ QVERIFY2(!m_dataDir.isNull(), qPrintable("Could not extract test data"));
+ QString testdata_dir = m_dataDir->path();
+#else
+
+ // chdir into testdata directory, then find testdata by relative paths.
+ QString testdata_dir = m_dataDir.path();
+#endif
+
+ QVERIFY(!testdata_dir.isEmpty());
+ // Must call QDir::setCurrent() here because all the tests that use relative
+ // paths depend on that.
+ QVERIFY2(QDir::setCurrent(testdata_dir), qPrintable("Could not chdir to " + testdata_dir));
+
+ createDirectory("entrylist");
+ createDirectory("entrylist/directory");
+ createFile("entrylist/file");
+ createFile("entrylist/writable");
+ createFile("entrylist/directory/dummy");
+
+ createDirectory("recursiveDirs");
+ createDirectory("recursiveDirs/dir1");
+ createFile("recursiveDirs/textFileA.txt");
+ createFile("recursiveDirs/dir1/aPage.html");
+ createFile("recursiveDirs/dir1/textFileB.txt");
+
+ createDirectory("foo");
+ createDirectory("foo/bar");
+ createFile("foo/bar/readme.txt");
+
+ createDirectory("empty");
+
+#ifndef Q_NO_SYMLINKS
+# if defined(Q_OS_WIN)
+ // ### Sadly, this is a platform difference right now.
+ createLink("entrylist/file", "entrylist/linktofile.lnk");
+# ifndef Q_NO_SYMLINKS_TO_DIRS
+ createLink("entrylist/directory", "entrylist/linktodirectory.lnk");
+# endif
+ createLink("entrylist/nothing", "entrylist/brokenlink.lnk");
+# else
+ createLink("file", "entrylist/linktofile.lnk");
+# ifndef Q_NO_SYMLINKS_TO_DIRS
+ createLink("directory", "entrylist/linktodirectory.lnk");
+# endif
+ createLink("nothing", "entrylist/brokenlink.lnk");
+# endif
+#endif
+
+#if !defined(Q_OS_WIN)
+ createDirectory("hiddenDirs_hiddenFiles");
+ createFile("hiddenDirs_hiddenFiles/normalFile");
+ createFile("hiddenDirs_hiddenFiles/.hiddenFile");
+ createDirectory("hiddenDirs_hiddenFiles/normalDirectory");
+ createDirectory("hiddenDirs_hiddenFiles/.hiddenDirectory");
+ createFile("hiddenDirs_hiddenFiles/normalDirectory/normalFile");
+ createFile("hiddenDirs_hiddenFiles/normalDirectory/.hiddenFile");
+ createFile("hiddenDirs_hiddenFiles/.hiddenDirectory/normalFile");
+ createFile("hiddenDirs_hiddenFiles/.hiddenDirectory/.hiddenFile");
+ createDirectory("hiddenDirs_hiddenFiles/normalDirectory/normalDirectory");
+ createDirectory("hiddenDirs_hiddenFiles/normalDirectory/.hiddenDirectory");
+ createDirectory("hiddenDirs_hiddenFiles/.hiddenDirectory/normalDirectory");
+ createDirectory("hiddenDirs_hiddenFiles/.hiddenDirectory/.hiddenDirectory");
+#endif
+}
+
+void tst_QDirIterator::iterateRelativeDirectory_data()
+{
+ QTest::addColumn<QString>("dirName"); // relative from current path or abs
+ QTest::addColumn<QDirIterator::IteratorFlags>("flags");
+ QTest::addColumn<QDir::Filters>("filters");
+ QTest::addColumn<QStringList>("nameFilters");
+ QTest::addColumn<QStringList>("entries");
+
+ QTest::newRow("no flags")
+ << QString("entrylist") << QDirIterator::IteratorFlags{}
+ << QDir::Filters(QDir::NoFilter) << QStringList("*")
+ << QString(
+ "entrylist/.,"
+ "entrylist/..,"
+ "entrylist/file,"
+#ifndef Q_NO_SYMLINKS
+ "entrylist/linktofile.lnk,"
+#endif
+ "entrylist/directory,"
+#if !defined(Q_NO_SYMLINKS) && !defined(Q_NO_SYMLINKS_TO_DIRS)
+ "entrylist/linktodirectory.lnk,"
+#endif
+ "entrylist/writable").split(',');
+
+ QTest::newRow("NoDot")
+ << QString("entrylist") << QDirIterator::IteratorFlags{}
+ << QDir::Filters(QDir::AllEntries | QDir::NoDot) << QStringList("*")
+ << QString(
+ "entrylist/..,"
+ "entrylist/file,"
+#ifndef Q_NO_SYMLINKS
+ "entrylist/linktofile.lnk,"
+#endif
+ "entrylist/directory,"
+#if !defined(Q_NO_SYMLINKS) && !defined(Q_NO_SYMLINKS_TO_DIRS)
+ "entrylist/linktodirectory.lnk,"
+#endif
+ "entrylist/writable").split(',');
+
+ QTest::newRow("NoDotDot")
+ << QString("entrylist") << QDirIterator::IteratorFlags{}
+ << QDir::Filters(QDir::AllEntries | QDir::NoDotDot) << QStringList("*")
+ << QString(
+ "entrylist/.,"
+ "entrylist/file,"
+#ifndef Q_NO_SYMLINKS
+ "entrylist/linktofile.lnk,"
+#endif
+ "entrylist/directory,"
+#if !defined(Q_NO_SYMLINKS) && !defined(Q_NO_SYMLINKS_TO_DIRS)
+ "entrylist/linktodirectory.lnk,"
+#endif
+ "entrylist/writable").split(',');
+
+ QTest::newRow("NoDotAndDotDot")
+ << QString("entrylist") << QDirIterator::IteratorFlags{}
+ << QDir::Filters(QDir::AllEntries | QDir::NoDotAndDotDot) << QStringList("*")
+ << QString(
+ "entrylist/file,"
+#ifndef Q_NO_SYMLINKS
+ "entrylist/linktofile.lnk,"
+#endif
+ "entrylist/directory,"
+#if !defined(Q_NO_SYMLINKS) && !defined(Q_NO_SYMLINKS_TO_DIRS)
+ "entrylist/linktodirectory.lnk,"
+#endif
+ "entrylist/writable").split(',');
+
+ QTest::newRow("QDir::Subdirectories | QDir::FollowSymlinks")
+ << QString("entrylist") << QDirIterator::IteratorFlags(QDirIterator::Subdirectories | QDirIterator::FollowSymlinks)
+ << QDir::Filters(QDir::NoFilter) << QStringList("*")
+ << QString(
+ "entrylist/.,"
+ "entrylist/..,"
+ "entrylist/directory/.,"
+ "entrylist/directory/..,"
+ "entrylist/file,"
+#ifndef Q_NO_SYMLINKS
+ "entrylist/linktofile.lnk,"
+#endif
+ "entrylist/directory,"
+ "entrylist/directory/dummy,"
+#if !defined(Q_NO_SYMLINKS) && !defined(Q_NO_SYMLINKS_TO_DIRS)
+ "entrylist/linktodirectory.lnk,"
+#endif
+ "entrylist/writable").split(',');
+
+ QTest::newRow("QDir::Subdirectories / QDir::Files")
+ << QString("entrylist") << QDirIterator::IteratorFlags(QDirIterator::Subdirectories)
+ << QDir::Filters(QDir::Files) << QStringList("*")
+ << QString("entrylist/directory/dummy,"
+ "entrylist/file,"
+#ifndef Q_NO_SYMLINKS
+ "entrylist/linktofile.lnk,"
+#endif
+ "entrylist/writable").split(',');
+
+ QTest::newRow("QDir::Subdirectories | QDir::FollowSymlinks / QDir::Files")
+ << QString("entrylist") << QDirIterator::IteratorFlags(QDirIterator::Subdirectories | QDirIterator::FollowSymlinks)
+ << QDir::Filters(QDir::Files) << QStringList("*")
+ << QString("entrylist/file,"
+#ifndef Q_NO_SYMLINKS
+ "entrylist/linktofile.lnk,"
+#endif
+ "entrylist/directory/dummy,"
+ "entrylist/writable").split(',');
+
+ QTest::newRow("empty, default")
+ << QString("empty") << QDirIterator::IteratorFlags{}
+ << QDir::Filters(QDir::NoFilter) << QStringList("*")
+ << QString("empty/.,empty/..").split(',');
+
+ QTest::newRow("empty, QDir::NoDotAndDotDot")
+ << QString("empty") << QDirIterator::IteratorFlags{}
+ << QDir::Filters(QDir::NoDotAndDotDot) << QStringList("*")
+ << QStringList();
+}
+
+void tst_QDirIterator::iterateRelativeDirectory()
+{
+ QFETCH(QString, dirName);
+ QFETCH(QDirIterator::IteratorFlags, flags);
+ QFETCH(QDir::Filters, filters);
+ QFETCH(QStringList, nameFilters);
+ QFETCH(const QStringList, entries);
+
+ QDirIterator it(dirName, nameFilters, filters, flags);
+ QStringList list;
+ while (it.hasNext()) {
+ QString next = it.next();
+
+ QString fileName = it.fileName();
+ QString filePath = it.filePath();
+ QString path = it.path();
+
+ QFileInfo info = it.fileInfo();
+
+ QCOMPARE(path, dirName);
+ QCOMPARE(next, filePath);
+
+ QCOMPARE(info, QFileInfo(next));
+ QCOMPARE(fileName, info.fileName());
+ QCOMPARE(filePath, info.filePath());
+
+ // Using canonical file paths for final comparison
+ list << info.canonicalFilePath();
+ }
+
+ // The order of items returned by QDirIterator is not guaranteed.
+ list.sort();
+
+ QStringList sortedEntries;
+ for (const QString &item : entries)
+ sortedEntries.append(QFileInfo(item).canonicalFilePath());
+ sortedEntries.sort();
+
+ if (sortedEntries != list) {
+ qDebug() << "ACTUAL: " << list;
+ qDebug() << "EXPECTED:" << sortedEntries;
+ }
+
+ QCOMPARE(list, sortedEntries);
+}
+
+void tst_QDirIterator::iterateResource_data()
+{
+ QTest::addColumn<QString>("dirName"); // relative from current path or abs
+ QTest::addColumn<QDirIterator::IteratorFlags>("flags");
+ QTest::addColumn<QDir::Filters>("filters");
+ QTest::addColumn<QStringList>("nameFilters");
+ QTest::addColumn<QStringList>("entries");
+
+ QTest::newRow("invalid") << QString::fromLatin1(":/testdata/burpaburpa") << QDirIterator::IteratorFlags{}
+ << QDir::Filters(QDir::NoFilter) << QStringList(QLatin1String("*"))
+ << QStringList();
+ QTest::newRow("qrc:/testdata") << u":/testdata/"_s << QDirIterator::IteratorFlags{}
+ << QDir::Filters(QDir::NoFilter) << QStringList(QLatin1String("*"))
+ << QString::fromLatin1(":/testdata/entrylist").split(QLatin1String(","));
+ QTest::newRow("qrc:/testdata/entrylist") << u":/testdata/entrylist"_s << QDirIterator::IteratorFlags{}
+ << QDir::Filters(QDir::NoFilter) << QStringList(QLatin1String("*"))
+ << QString::fromLatin1(":/testdata/entrylist/directory,:/testdata/entrylist/file").split(QLatin1String(","));
+ QTest::newRow("qrc:/testdata recursive") << u":/testdata"_s
+ << QDirIterator::IteratorFlags(QDirIterator::Subdirectories)
+ << QDir::Filters(QDir::NoFilter) << QStringList(QLatin1String("*"))
+ << QString::fromLatin1(":/testdata/entrylist,:/testdata/entrylist/directory,:/testdata/entrylist/directory/dummy,:/testdata/entrylist/file").split(QLatin1String(","));
+}
+
+void tst_QDirIterator::iterateResource()
+{
+ QFETCH(QString, dirName);
+ QFETCH(QDirIterator::IteratorFlags, flags);
+ QFETCH(QDir::Filters, filters);
+ QFETCH(QStringList, nameFilters);
+ QFETCH(QStringList, entries);
+
+ QDirIterator it(dirName, nameFilters, filters, flags);
+ QStringList list;
+ while (it.hasNext()) {
+ const QString dir = it.next();
+ if (!dir.startsWith(":/qt-project.org"))
+ list << dir;
+ }
+
+ list.sort();
+ QStringList sortedEntries = entries;
+ sortedEntries.sort();
+
+ if (sortedEntries != list) {
+ qDebug() << "ACTUAL:" << list;
+ qDebug() << "EXPECTED:" << sortedEntries;
+ }
+
+ QCOMPARE(list, sortedEntries);
+}
+
+void tst_QDirIterator::stopLinkLoop()
+{
+#ifdef Q_OS_WIN
+ // ### Sadly, this is a platform difference right now.
+ createLink(QDir::currentPath() + QLatin1String("/entrylist"), "entrylist/entrylist1.lnk");
+ createLink("entrylist/.", "entrylist/entrylist2.lnk");
+ createLink("entrylist/../entrylist/.", "entrylist/entrylist3.lnk");
+ createLink("entrylist/..", "entrylist/entrylist4.lnk");
+ createLink(QDir::currentPath() + QLatin1String("/entrylist"), "entrylist/directory/entrylist1.lnk");
+ createLink("entrylist/.", "entrylist/directory/entrylist2.lnk");
+ createLink("entrylist/../directory/.", "entrylist/directory/entrylist3.lnk");
+ createLink("entrylist/..", "entrylist/directory/entrylist4.lnk");
+#else
+ createLink(QDir::currentPath() + QLatin1String("/entrylist"), "entrylist/entrylist1.lnk");
+ createLink(".", "entrylist/entrylist2.lnk");
+ createLink("../entrylist/.", "entrylist/entrylist3.lnk");
+ createLink("..", "entrylist/entrylist4.lnk");
+ createLink(QDir::currentPath() + QLatin1String("/entrylist"), "entrylist/directory/entrylist1.lnk");
+ createLink(".", "entrylist/directory/entrylist2.lnk");
+ createLink("../directory/.", "entrylist/directory/entrylist3.lnk");
+ createLink("..", "entrylist/directory/entrylist4.lnk");
+#endif
+
+ QDirIterator it(QLatin1String("entrylist"), QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
+ QStringList list;
+ int max = 200;
+ while (--max && it.hasNext())
+ it.nextFileInfo();
+ QVERIFY(max);
+
+ // The goal of this test is only to ensure that the test above don't malfunction
+}
+
+#ifdef QT_BUILD_INTERNAL
+class EngineWithNoIterator : public QFSFileEngine
+{
+public:
+ EngineWithNoIterator(const QString &fileName)
+ : QFSFileEngine(fileName)
+ { }
+
+ QAbstractFileEngineIterator *beginEntryList(QDir::Filters, const QStringList &) override
+ { return 0; }
+};
+
+class EngineWithNoIteratorHandler : public QAbstractFileEngineHandler
+{
+public:
+ QAbstractFileEngine *create(const QString &fileName) const override
+ {
+ return new EngineWithNoIterator(fileName);
+ }
+};
+#endif
+
+#ifdef QT_BUILD_INTERNAL
+void tst_QDirIterator::engineWithNoIterator()
+{
+ EngineWithNoIteratorHandler handler;
+
+ QDir("entrylist").entryList();
+ QVERIFY(true); // test that the above line doesn't crash
+}
+
+class CustomEngineHandler : public QAbstractFileEngineHandler
+{
+public:
+ QAbstractFileEngine *create(const QString &fileName) const override
+ {
+ // We want to test QFSFileEngine specifically, so force QDirIterator to use it
+ // over the default QFileSystemEngine
+ return new QFSFileEngine(fileName);
+ }
+};
+
+void tst_QDirIterator::testQFsFileEngineIterator()
+{
+ QFETCH(QString, dirName);
+ QFETCH(QStringList, nameFilters);
+ QFETCH(QDir::Filters, filters);
+ QFETCH(QDirIterator::IteratorFlags, flags);
+
+ if (dirName == u"empty")
+ return; // This row isn't useful in this test
+
+ CustomEngineHandler handler;
+ bool isEmpty = true;
+ QDirIterator iter(dirName, nameFilters, filters, flags);
+ while (iter.hasNext()) {
+ const QFileInfo &fi = iter.nextFileInfo();
+ if (fi.filePath().contains(u"entrylist"))
+ isEmpty = false; // At least one entry in `entrylist` dir
+ }
+ QVERIFY(!isEmpty);
+}
+#endif
+
+void tst_QDirIterator::absoluteFilePathsFromRelativeIteratorPath()
+{
+ QDirIterator it("entrylist/", QDir::NoDotAndDotDot);
+ while (it.hasNext())
+ QVERIFY(it.nextFileInfo().absoluteFilePath().contains("entrylist"));
+}
+
+void tst_QDirIterator::recurseWithFilters() const
+{
+ QStringList nameFilters;
+ nameFilters.append("*.txt");
+
+ QDirIterator it("recursiveDirs/", nameFilters, QDir::Files,
+ QDirIterator::Subdirectories);
+
+ QSet<QString> actualEntries;
+ QSet<QString> expectedEntries;
+ expectedEntries.insert(QString::fromLatin1("recursiveDirs/dir1/textFileB.txt"));
+ expectedEntries.insert(QString::fromLatin1("recursiveDirs/textFileA.txt"));
+
+ QVERIFY(it.hasNext());
+ actualEntries.insert(it.next());
+ QVERIFY(it.hasNext());
+ actualEntries.insert(it.next());
+ QCOMPARE(actualEntries, expectedEntries);
+
+ QVERIFY(!it.hasNext());
+}
+
+void tst_QDirIterator::longPath()
+{
+ QDir dir;
+ dir.mkdir("longpaths");
+ dir.cd("longpaths");
+
+ QString dirName = "x";
+ int n = 0;
+ while (dir.exists(dirName) || dir.mkdir(dirName)) {
+ ++n;
+ dirName.append('x');
+ }
+
+ QDirIterator it(dir.absolutePath(), QDir::NoDotAndDotDot|QDir::Dirs, QDirIterator::Subdirectories);
+ int m = 0;
+ while (it.hasNext()) {
+ ++m;
+ it.nextFileInfo();
+ }
+
+ QCOMPARE(n, m);
+
+ dirName.chop(1);
+ while (dirName.size() > 0 && dir.exists(dirName) && dir.rmdir(dirName)) {
+ dirName.chop(1);
+ }
+ dir.cdUp();
+ dir.rmdir("longpaths");
+}
+
+void tst_QDirIterator::dirorder()
+{
+ QDirIterator iterator("foo", QDirIterator::Subdirectories);
+ while (iterator.hasNext() && iterator.next() != "foo/bar")
+ { }
+
+ QCOMPARE(iterator.filePath(), QString("foo/bar"));
+ QCOMPARE(iterator.fileInfo().filePath(), QString("foo/bar"));
+}
+
+void tst_QDirIterator::relativePaths()
+{
+ QDirIterator iterator("*", QDirIterator::Subdirectories);
+ while(iterator.hasNext()) {
+ QCOMPARE(iterator.filePath(), QDir::cleanPath(iterator.filePath()));
+ }
+}
+
+#if defined(Q_OS_WIN)
+void tst_QDirIterator::uncPaths_data()
+{
+ QTest::addColumn<QString>("dirName");
+ QTest::newRow("uncserver")
+ <<QString("//" + QTest::uncServerName());
+ QTest::newRow("uncserver/testshare")
+ <<QString("//" + QTest::uncServerName() + "/testshare");
+ QTest::newRow("uncserver/testshare/tmp")
+ <<QString("//" + QTest::uncServerName() + "/testshare/tmp");
+}
+void tst_QDirIterator::uncPaths()
+{
+ QFETCH(QString, dirName);
+ QDirIterator iterator(dirName, QDir::AllEntries|QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
+ while(iterator.hasNext()) {
+ iterator.next();
+ QCOMPARE(iterator.filePath(), QDir::cleanPath(iterator.filePath()));
+ }
+}
+#endif
+
+#ifndef Q_OS_WIN
+// In Unix it is easy to create hidden files, but in Windows it requires
+// a special call since hidden files need to be "marked" while in Unix
+// anything starting by a '.' is a hidden file.
+// For that reason this test is not run in Windows.
+void tst_QDirIterator::hiddenDirs_hiddenFiles()
+{
+ // Only files
+ {
+ int matches = 0;
+ int failures = 0;
+ QDirIterator di("hiddenDirs_hiddenFiles", QDir::Files | QDir::Hidden | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
+ while (di.hasNext()) {
+ ++matches;
+ if (di.nextFileInfo().isDir())
+ ++failures; // search was only supposed to find files
+ }
+ QCOMPARE(matches, 6);
+ QCOMPARE(failures, 0);
+ }
+ // Only directories
+ {
+ int matches = 0;
+ int failures = 0;
+ QDirIterator di("hiddenDirs_hiddenFiles", QDir::Dirs | QDir::Hidden | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
+ while (di.hasNext()) {
+ ++matches;
+ if (!di.nextFileInfo().isDir())
+ ++failures; // search was only supposed to find files
+ }
+ QCOMPARE(matches, 6);
+ QCOMPARE(failures, 0);
+ }
+}
+#endif // Q_OS_WIN
+
+QTEST_MAIN(tst_QDirIterator)
+
+#include "tst_qdiriterator.moc"
+