diff options
Diffstat (limited to 'tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp')
-rw-r--r-- | tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp | 417 |
1 files changed, 249 insertions, 168 deletions
diff --git a/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp b/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp index 09113cd40b..f7d531f61f 100644 --- a/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp +++ b/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp @@ -1,35 +1,10 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> +#include <QtTest/private/qcomparisontesthelper_p.h> #include <QStandardPaths> #include <QScopeGuard> -#include <QScopedValueRollback> #include <qfile.h> #include <qdir.h> @@ -56,23 +31,23 @@ #endif #include <qplatformdefs.h> #include <qdebug.h> -#if defined(Q_OS_WIN) -#include "../../../network-settings.h" -#endif #include <private/qfileinfo_p.h> #include "../../../../shared/filesystem.h" +#if defined(Q_OS_MACOS) +#include <Foundation/Foundation.h> +#endif + #if defined(Q_OS_VXWORKS) #define Q_NO_SYMLINKS #endif #if defined(Q_OS_WIN) -QT_BEGIN_NAMESPACE -extern Q_CORE_EXPORT int qt_ntfs_permission_lookup; -QT_END_NAMESPACE bool IsUserAdmin(); #endif +using namespace Qt::StringLiterals; + inline bool qIsLikelyToBeFat(const QString &path) { QByteArray name = QStorageInfo(path).fileSystemType().toLower(); @@ -96,31 +71,6 @@ inline bool qIsLikelyToBeNfs(const QString &path) } #if defined(Q_OS_WIN) -# ifndef SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE // MinGW -# define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE (0x2) -# endif - -static DWORD createSymbolicLink(const QString &symLinkName, const QString &target, - QString *errorMessage) -{ - DWORD result = ERROR_SUCCESS; - const QString nativeSymLinkName = QDir::toNativeSeparators(symLinkName); - const QString nativeTarget = QDir::toNativeSeparators(target); - DWORD flags = 0; - if (QOperatingSystemVersion::current() >= QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, 14972)) - flags |= SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE; - if (QFileInfo(target).isDir()) - flags |= SYMBOLIC_LINK_FLAG_DIRECTORY; - if (CreateSymbolicLink(reinterpret_cast<const wchar_t*>(nativeSymLinkName.utf16()), - reinterpret_cast<const wchar_t*>(nativeTarget.utf16()), flags) == FALSE) { - result = GetLastError(); - QTextStream(errorMessage) << "CreateSymbolicLink(" << nativeSymLinkName << ", " - << nativeTarget << ", 0x" << Qt::hex << flags << Qt::dec << ") failed with error " << result - << ": " << qt_error_string(int(result)); - } - return result; -} - static QByteArray msgInsufficientPrivileges(const QString &errorMessage) { return "Insufficient privileges (" + errorMessage.toLocal8Bit() + ')'; @@ -223,6 +173,7 @@ private slots: void systemFiles(); + void compareCompiles(); void compare_data(); void compare(); @@ -231,6 +182,7 @@ private slots: void fileTimes_data(); void fileTimes(); + void setFileTimes(); void fakeFileTimes_data(); void fakeFileTimes(); @@ -243,12 +195,15 @@ private slots: void isShortcut_data(); void isShortcut(); + void isAlias_data(); + void isAlias(); + void link_data(); void link(); void isHidden_data(); void isHidden(); -#if defined(Q_OS_MAC) +#if defined(Q_OS_DARWIN) void isHiddenFromFinder(); #endif @@ -285,6 +240,7 @@ private slots: void nonExistingFile(); void stdfilesystem(); + void readSymLink(); private: const QString m_currentDir; @@ -397,7 +353,7 @@ void tst_QFileInfo::isDir_data() QFile::remove("brokenlink.lnk"); QFile::remove("dummyfile"); QFile file3("dummyfile"); - file3.open(QIODevice::WriteOnly); + QVERIFY(file3.open(QIODevice::WriteOnly)); if (file3.link("brokenlink.lnk")) { file3.remove(); QFileInfo info3("brokenlink.lnk"); @@ -424,7 +380,7 @@ void tst_QFileInfo::isDir_data() //QTest::newRow("drive 2") << "t:s" << false; #endif #if defined(Q_OS_WIN) - const QString uncRoot = QStringLiteral("//") + QtNetworkSettings::winServerName(); + const QString uncRoot = QStringLiteral("//") + QTest::uncServerName(); QTest::newRow("unc 1") << uncRoot << true; QTest::newRow("unc 2") << uncRoot + QLatin1Char('/') << true; QTest::newRow("unc 3") << uncRoot + "/testshare" << true; @@ -467,7 +423,7 @@ void tst_QFileInfo::isRoot_data() #endif #if defined(Q_OS_WIN) - const QString uncRoot = QStringLiteral("//") + QtNetworkSettings::winServerName(); + const QString uncRoot = QStringLiteral("//") + QTest::uncServerName(); QTest::newRow("unc 1") << uncRoot << true; QTest::newRow("unc 2") << uncRoot + QLatin1Char('/') << true; QTest::newRow("unc 3") << uncRoot + "/testshare" << false; @@ -511,7 +467,7 @@ void tst_QFileInfo::exists_data() QTest::newRow("simple dir with slash") << (m_resourcesDir + QLatin1Char('/')) << true; #if defined(Q_OS_WIN) - const QString uncRoot = QStringLiteral("//") + QtNetworkSettings::winServerName(); + const QString uncRoot = QStringLiteral("//") + QTest::uncServerName(); QTest::newRow("unc 1") << uncRoot << true; QTest::newRow("unc 2") << uncRoot + QLatin1Char('/') << true; QTest::newRow("unc 3") << uncRoot + "/testshare" << true; @@ -702,7 +658,7 @@ void tst_QFileInfo::canonicalFilePath() QFile file(QDir::currentPath()); if (file.link(link)) { QFile tempfile("tempfile.txt"); - tempfile.open(QIODevice::ReadWrite); + QVERIFY(tempfile.open(QIODevice::ReadWrite)); tempfile.write("This file is generated by the QFileInfo autotest."); QVERIFY(tempfile.flush()); tempfile.close(); @@ -740,12 +696,11 @@ void tst_QFileInfo::canonicalFilePath() #if defined(Q_OS_WIN) { - QString errorMessage; const QString linkTarget = QStringLiteral("res"); - const DWORD dwErr = createSymbolicLink(linkTarget, m_resourcesDir, &errorMessage); - if (dwErr == ERROR_PRIVILEGE_NOT_HELD) - QSKIP(msgInsufficientPrivileges(errorMessage)); - QVERIFY2(dwErr == ERROR_SUCCESS, qPrintable(errorMessage)); + const auto result = FileSystem::createSymbolicLink(linkTarget, m_resourcesDir); + if (result.dwErr == ERROR_PRIVILEGE_NOT_HELD) + QSKIP(msgInsufficientPrivileges(result.errorMessage)); + QVERIFY2(result.dwErr == ERROR_SUCCESS, qPrintable(result.errorMessage)); QString currentPath = QDir::currentPath(); QVERIFY(QDir::setCurrent(linkTarget)); const QString actualCanonicalPath = QFileInfo("file1").canonicalFilePath(); @@ -809,7 +764,7 @@ void tst_QFileInfo::bundleName_data() QTest::newRow("root") << "/" << ""; QTest::newRow("etc") << "/etc" << ""; -#ifdef Q_OS_MAC +#ifdef Q_OS_DARWIN QTest::newRow("safari") << "/Applications/Safari.app" << "Safari"; #endif } @@ -1040,6 +995,11 @@ void tst_QFileInfo::systemFiles() QVERIFY(fi.birthTime() <= fi.lastModified()); } +void tst_QFileInfo::compareCompiles() +{ + QTestPrivate::testEqualityOperatorsCompile<QFileInfo>(); +} + void tst_QFileInfo::compare_data() { QTest::addColumn<QString>("file1"); @@ -1066,7 +1026,7 @@ void tst_QFileInfo::compare_data() << m_sourceFile #if defined(Q_OS_WIN) << true; -#elif defined(Q_OS_MAC) +#elif defined(Q_OS_DARWIN) << !pathconf(QDir::currentPath().toLatin1().constData(), _PC_CASE_SENSITIVE); #else << false; @@ -1075,7 +1035,7 @@ void tst_QFileInfo::compare_data() void tst_QFileInfo::compare() { -#if defined(Q_OS_MAC) +#if defined(Q_OS_DARWIN) if (qstrcmp(QTest::currentDataTag(), "casesense1") == 0) QSKIP("Qt thinks all UNIX filesystems are case sensitive, see QTBUG-28246"); #endif @@ -1084,7 +1044,7 @@ void tst_QFileInfo::compare() QFETCH(QString, file2); QFETCH(bool, same); QFileInfo fi1(file1), fi2(file2); - QCOMPARE(fi1 == fi2, same); + QT_TEST_EQUALITY_OPS(fi1, fi2, same); } void tst_QFileInfo::consistent_data() @@ -1152,8 +1112,8 @@ void tst_QFileInfo::fileTimes() { // try to guess if file times on this filesystem round to the second QFileInfo cwd("."); - if (cwd.lastModified().toMSecsSinceEpoch() % 1000 == 0 - && cwd.lastRead().toMSecsSinceEpoch() % 1000 == 0) { + if (cwd.lastModified(QTimeZone::UTC).toMSecsSinceEpoch() % 1000 == 0 + && cwd.lastRead(QTimeZone::UTC).toMSecsSinceEpoch() % 1000 == 0) { fsClockSkew = sleepTime = 1000; noAccessTime = qIsLikelyToBeFat(fileName); @@ -1173,46 +1133,46 @@ void tst_QFileInfo::fileTimes() QDateTime birthTime, writeTime, metadataChangeTime, readTime; // --- Create file and write to it - beforeBirth = QDateTime::currentDateTime().addMSecs(-fsClockSkew); + beforeBirth = QDateTime::currentDateTimeUtc().addMSecs(-fsClockSkew); { QFile file(fileName); QVERIFY(file.open(QFile::WriteOnly | QFile::Text)); QFileInfo fileInfo(fileName); - birthTime = fileInfo.birthTime(); + birthTime = fileInfo.birthTime(QTimeZone::UTC); QVERIFY2(!birthTime.isValid() || birthTime > beforeBirth, datePairString(birthTime, beforeBirth)); QTest::qSleep(sleepTime); - beforeWrite = QDateTime::currentDateTime().addMSecs(-fsClockSkew); + beforeWrite = QDateTime::currentDateTimeUtc().addMSecs(-fsClockSkew); QTextStream ts(&file); ts << fileName << Qt::endl; } { QFileInfo fileInfo(fileName); - writeTime = fileInfo.lastModified(); + writeTime = fileInfo.lastModified(QTimeZone::UTC); QVERIFY2(writeTime > beforeWrite, datePairString(writeTime, beforeWrite)); - QCOMPARE(fileInfo.birthTime(), birthTime); // mustn't have changed + QCOMPARE(fileInfo.birthTime(QTimeZone::UTC), birthTime); // mustn't have changed } // --- Change the file's metadata QTest::qSleep(sleepTime); - beforeMetadataChange = QDateTime::currentDateTime().addMSecs(-fsClockSkew); + beforeMetadataChange = QDateTime::currentDateTimeUtc().addMSecs(-fsClockSkew); { QFile file(fileName); file.setPermissions(file.permissions()); } { QFileInfo fileInfo(fileName); - metadataChangeTime = fileInfo.metadataChangeTime(); + metadataChangeTime = fileInfo.metadataChangeTime(QTimeZone::UTC); QVERIFY2(metadataChangeTime > beforeMetadataChange, datePairString(metadataChangeTime, beforeMetadataChange)); QVERIFY(metadataChangeTime >= writeTime); // not all filesystems can store both times - QCOMPARE(fileInfo.birthTime(), birthTime); // mustn't have changed + QCOMPARE(fileInfo.birthTime(QTimeZone::UTC), birthTime); // mustn't have changed } // --- Read the file QTest::qSleep(sleepTime); - beforeRead = QDateTime::currentDateTime().addMSecs(-fsClockSkew); + beforeRead = QDateTime::currentDateTimeUtc().addMSecs(-fsClockSkew); { QFile file(fileName); QVERIFY(file.open(QFile::ReadOnly | QFile::Text)); @@ -1222,12 +1182,12 @@ void tst_QFileInfo::fileTimes() } QFileInfo fileInfo(fileName); - readTime = fileInfo.lastRead(); - QCOMPARE(fileInfo.lastModified(), writeTime); // mustn't have changed - QCOMPARE(fileInfo.birthTime(), birthTime); // mustn't have changed + readTime = fileInfo.lastRead(QTimeZone::UTC); + QCOMPARE(fileInfo.lastModified(QTimeZone::UTC), writeTime); // mustn't have changed + QCOMPARE(fileInfo.birthTime(QTimeZone::UTC), birthTime); // mustn't have changed QVERIFY(readTime.isValid()); -#if defined(Q_OS_QNX) || (defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)) +#if defined(Q_OS_QNX) || defined(Q_OS_ANDROID) noAccessTime = true; #elif defined(Q_OS_WIN) //In Vista the last-access timestamp is not updated when the file is accessed/touched (by default). @@ -1248,6 +1208,21 @@ void tst_QFileInfo::fileTimes() QVERIFY(writeTime < beforeRead); } +void tst_QFileInfo::setFileTimes() +{ + QByteArray data("OLE\nOLE\nOLE"); + QTemporaryFile file; + + QVERIFY(file.open()); + QCOMPARE(file.write(data), data.size()); + QCOMPARE(file.size(), data.size()); + + const QDateTime before = QDateTime::currentDateTimeUtc().addMSecs(-5000); + QVERIFY(file.setFileTime(before, QFile::FileModificationTime)); + const QDateTime mtime = file.fileTime(QFile::FileModificationTime).toUTC(); + QCOMPARE(mtime, before); +} + void tst_QFileInfo::fakeFileTimes_data() { QTest::addColumn<QDateTime>("when"); @@ -1265,7 +1240,7 @@ void tst_QFileInfo::fakeFileTimes() QFETCH(QDateTime, when); QFile file("faketimefile.txt"); - file.open(QIODevice::WriteOnly); + QVERIFY(file.open(QIODevice::WriteOnly)); file.write("\n", 1); file.close(); @@ -1274,12 +1249,12 @@ void tst_QFileInfo::fakeFileTimes() the file is open at the time. Of course, when writing, close() changes modification time, so need to re-open for read in order to setFileTime(). */ - file.open(QIODevice::ReadOnly); + QVERIFY(file.open(QIODevice::ReadOnly)); bool ok = file.setFileTime(when, QFileDevice::FileModificationTime); file.close(); if (ok) - QCOMPARE(QFileInfo(file.fileName()).lastModified(), when); + QCOMPARE(QFileInfo(file.fileName()).lastModified(QTimeZone::UTC), when); else QSKIP("Unable to set file metadata to contrived values"); } @@ -1296,7 +1271,7 @@ void tst_QFileInfo::isSymLink_data() QVERIFY(file1.link("link.lnk")); QFile file2("dummyfile"); - file2.open(QIODevice::WriteOnly); + QVERIFY(file2.open(QIODevice::WriteOnly)); QVERIFY(file2.link("brokenlink.lnk")); file2.remove(); @@ -1371,6 +1346,57 @@ void tst_QFileInfo::isShortcut() QCOMPARE(fi.isShortcut(), isShortcut); } +void tst_QFileInfo::isAlias_data() +{ + QFile::remove("symlink"); + QFile::remove("file-alias"); + QFile::remove("directory-alias"); + + QTest::addColumn<QString>("path"); + QTest::addColumn<bool>("isAlias"); + + QFile regularFile(m_sourceFile); + QTest::newRow("regular-file") << regularFile.fileName() << false; + QTest::newRow("directory") << QDir::currentPath() << false; + +#if defined(Q_OS_MACOS) + auto createAlias = [](const QString &target, const QString &alias) { + NSURL *targetUrl = [NSURL fileURLWithPath:target.toNSString()]; + NSURL *aliasUrl = [NSURL fileURLWithPath:alias.toNSString()]; + NSData *bookmarkData = [targetUrl bookmarkDataWithOptions:NSURLBookmarkCreationSuitableForBookmarkFile + includingResourceValuesForKeys:nil relativeToURL:nil error:nullptr]; + Q_ASSERT(bookmarkData); + + bool success = [NSURL writeBookmarkData:bookmarkData toURL:aliasUrl + options:NSURLBookmarkCreationSuitableForBookmarkFile error:nullptr]; + Q_ASSERT(success); + }; + + regularFile.link("symlink"); + QTest::newRow("symlink") << "symlink" << false; + + createAlias(regularFile.fileName(), QDir::current().filePath("file-alias")); + QTest::newRow("file-alias") << "file-alias" << true; + + createAlias(QDir::currentPath(), QDir::current().filePath("directory-alias")); + QTest::newRow("directory-alias") << "directory-alias" << true; + + regularFile.copy("non-existing-file"); + createAlias("non-existing-file", QDir::current().filePath("non-existing-file-alias")); + QDir::current().remove("non-existing-file"); + QTest::newRow("non-existing-file-alias") << "non-existing-file-alias" << true; +#endif +} + +void tst_QFileInfo::isAlias() +{ + QFETCH(QString, path); + QFETCH(bool, isAlias); + + QFileInfo fi(path); + QCOMPARE(fi.isAlias(), isAlias); +} + void tst_QFileInfo::isSymbolicLink_data() { QTest::addColumn<QString>("path"); @@ -1384,12 +1410,11 @@ void tst_QFileInfo::isSymbolicLink_data() #ifndef Q_NO_SYMLINKS #if defined(Q_OS_WIN) - QString errorMessage; - const DWORD creationResult = createSymbolicLink("symlink", m_sourceFile, &errorMessage); - if (creationResult == ERROR_PRIVILEGE_NOT_HELD) { - QWARN(msgInsufficientPrivileges(errorMessage)); + const auto creationResult = FileSystem::createSymbolicLink("symlink", m_sourceFile); + if (creationResult.dwErr == ERROR_PRIVILEGE_NOT_HELD) { + qWarning() << qPrintable(msgInsufficientPrivileges(creationResult.errorMessage)); } else { - QVERIFY2(creationResult == ERROR_SUCCESS, qPrintable(errorMessage)); + QVERIFY2(creationResult.dwErr == ERROR_SUCCESS, qPrintable(creationResult.errorMessage)); QTest::newRow("NTFS-symlink") << "symlink" << true; } @@ -1432,7 +1457,7 @@ void tst_QFileInfo::link_data() QFile file1(m_sourceFile); QFile file2("dummyfile"); - file2.open(QIODevice::WriteOnly); + QVERIFY(file2.open(QIODevice::WriteOnly)); QTest::newRow("existent file") << m_sourceFile << false << false << ""; #if defined(Q_OS_WIN) @@ -1448,21 +1473,20 @@ void tst_QFileInfo::link_data() #ifndef Q_NO_SYMLINKS #if defined(Q_OS_WIN) - QString errorMessage; - DWORD creationResult = createSymbolicLink("link", m_sourceFile, &errorMessage); - if (creationResult == ERROR_PRIVILEGE_NOT_HELD) { - QWARN(msgInsufficientPrivileges(errorMessage)); + auto creationResult = FileSystem::createSymbolicLink("link", m_sourceFile); + if (creationResult.dwErr == ERROR_PRIVILEGE_NOT_HELD) { + qWarning() << qPrintable(msgInsufficientPrivileges(creationResult.errorMessage)); } else { - QVERIFY2(creationResult == ERROR_SUCCESS, qPrintable(errorMessage)); + QVERIFY2(creationResult.dwErr == ERROR_SUCCESS, qPrintable(creationResult.errorMessage)); QTest::newRow("link") << "link" << false << true << QFileInfo(m_sourceFile).absoluteFilePath(); } - creationResult = createSymbolicLink("brokenlink", "dummyfile", &errorMessage); - if (creationResult == ERROR_PRIVILEGE_NOT_HELD) { - QWARN(msgInsufficientPrivileges(errorMessage)); + creationResult = FileSystem::createSymbolicLink("brokenlink", "dummyfile"); + if (creationResult.dwErr == ERROR_PRIVILEGE_NOT_HELD) { + qWarning() << qPrintable(msgInsufficientPrivileges(creationResult.errorMessage)); } else { - QVERIFY2(creationResult == ERROR_SUCCESS, qPrintable(errorMessage)); + QVERIFY2(creationResult.dwErr == ERROR_SUCCESS, qPrintable(creationResult.errorMessage)); QTest::newRow("broken link") << "brokenlink" << false << true << QFileInfo("dummyfile").absoluteFilePath(); } @@ -1501,9 +1525,9 @@ void tst_QFileInfo::isHidden_data() { QTest::addColumn<QString>("path"); QTest::addColumn<bool>("isHidden"); - foreach (const QFileInfo& info, QDir::drives()) { + const auto drives = QDir::drives(); + for (const QFileInfo& info : drives) QTest::newRow(qPrintable("drive." + info.path())) << info.path() << false; - } #if defined(Q_OS_WIN) QVERIFY(QDir("./hidden-directory").exists() || QDir().mkdir("./hidden-directory")); @@ -1518,14 +1542,14 @@ void tst_QFileInfo::isHidden_data() QTest::newRow("/path/to/.hidden-directory/..") << QDir::currentPath() + QString("/.hidden-directory/..") << true; #endif -#if defined(Q_OS_MAC) +#if defined(Q_OS_DARWIN) // /bin has the hidden attribute on OS X QTest::newRow("/bin/") << QString::fromLatin1("/bin/") << true; #elif !defined(Q_OS_WIN) QTest::newRow("/bin/") << QString::fromLatin1("/bin/") << false; #endif -#ifdef Q_OS_MAC +#ifdef Q_OS_DARWIN QTest::newRow("mac_etc") << QString::fromLatin1("/etc") << true; QTest::newRow("mac_private_etc") << QString::fromLatin1("/private/etc") << false; QTest::newRow("mac_Applications") << QString::fromLatin1("/Applications") << false; @@ -1541,7 +1565,7 @@ void tst_QFileInfo::isHidden() QCOMPARE(fi.isHidden(), isHidden); } -#if defined(Q_OS_MAC) +#if defined(Q_OS_DARWIN) void tst_QFileInfo::isHiddenFromFinder() { const char *filename = "test_foobar.txt"; @@ -1567,7 +1591,7 @@ void tst_QFileInfo::isBundle_data() QTest::addColumn<QString>("path"); QTest::addColumn<bool>("isBundle"); QTest::newRow("root") << QString::fromLatin1("/") << false; -#ifdef Q_OS_MAC +#ifdef Q_OS_DARWIN QTest::newRow("mac_Applications") << QString::fromLatin1("/Applications") << false; QTest::newRow("mac_Applications") << QString::fromLatin1("/Applications/Safari.app") << true; #endif @@ -1621,14 +1645,14 @@ void tst_QFileInfo::refresh() file.flush(); QFileInfo info(file); - QDateTime lastModified = info.lastModified(); + QDateTime lastModified = info.lastModified(QTimeZone::UTC); QCOMPARE(info.size(), qint64(7)); QTest::qSleep(sleepTime); QCOMPARE(file.write("JOJOJO"), qint64(6)); file.flush(); - QCOMPARE(info.lastModified(), lastModified); + QCOMPARE(info.lastModified(QTimeZone::UTC), lastModified); QCOMPARE(info.size(), qint64(7)); #if defined(Q_OS_WIN) @@ -1636,7 +1660,7 @@ void tst_QFileInfo::refresh() #endif info.refresh(); QCOMPARE(info.size(), qint64(13)); - QVERIFY(info.lastModified() > lastModified); + QVERIFY(info.lastModified(QTimeZone::UTC) > lastModified); QFileInfo info2 = info; QCOMPARE(info2.size(), info.size()); @@ -1665,7 +1689,6 @@ void tst_QFileInfo::ntfsJunctionPointsAndSymlinks_data() { QTest::addColumn<NtfsTestResource>("resource"); QTest::addColumn<QString>("path"); - QTest::addColumn<bool>("isSymLink"); QTest::addColumn<QString>("linkTarget"); QTest::addColumn<QString>("canonicalFilePath"); @@ -1693,13 +1716,13 @@ void tst_QFileInfo::ntfsJunctionPointsAndSymlinks_data() QTest::newRow("absolute dir symlink") << NtfsTestResource(NtfsTestResource::SymLink, absSymlink, absTarget) - << absSymlink << true << QDir::fromNativeSeparators(absTarget) << target.canonicalPath(); + << absSymlink << QDir::fromNativeSeparators(absTarget) << target.canonicalPath(); QTest::newRow("relative dir symlink") << NtfsTestResource(NtfsTestResource::SymLink, relSymlink, relTarget) - << relSymlink << true << QDir::fromNativeSeparators(absTarget) << target.canonicalPath(); + << relSymlink << QDir::fromNativeSeparators(absTarget) << target.canonicalPath(); QTest::newRow("file in symlink dir") << NtfsTestResource() - << fileInSymlink << false << "" << target.canonicalPath().append("/file"); + << fileInSymlink << "" << target.canonicalPath().append("/file"); } { //File symlinks @@ -1715,23 +1738,22 @@ void tst_QFileInfo::ntfsJunctionPointsAndSymlinks_data() QTest::newRow("absolute file symlink") << NtfsTestResource(NtfsTestResource::SymLink, absSymlink, absTarget) - << absSymlink << true << QDir::fromNativeSeparators(absTarget) << target.canonicalFilePath(); + << absSymlink << QDir::fromNativeSeparators(absTarget) << target.canonicalFilePath(); QTest::newRow("relative file symlink") << NtfsTestResource(NtfsTestResource::SymLink, relSymlink, relTarget) - << relSymlink << true << QDir::fromNativeSeparators(absTarget) << target.canonicalFilePath(); + << relSymlink << QDir::fromNativeSeparators(absTarget) << target.canonicalFilePath(); QTest::newRow("relative to relative file symlink") << NtfsTestResource(NtfsTestResource::SymLink, relToRelSymlink, relToRelTarget) - << relToRelSymlink << true << QDir::fromNativeSeparators(absTarget) << target.canonicalFilePath(); + << relToRelSymlink << QDir::fromNativeSeparators(absTarget) << target.canonicalFilePath(); } { // Symlink to UNC share pwd.mkdir("unc"); - QString errorMessage; - QString uncTarget = QStringLiteral("//") + QtNetworkSettings::winServerName() + "/testshare"; + QString uncTarget = QStringLiteral("//") + QTest::uncServerName() + "/testshare"; QString uncSymlink = QDir::toNativeSeparators(pwd.absolutePath().append("\\unc\\link_to_unc")); QTest::newRow("UNC symlink") << NtfsTestResource(NtfsTestResource::SymLink, uncSymlink, uncTarget) - << QDir::fromNativeSeparators(uncSymlink) << true << QDir::fromNativeSeparators(uncTarget) << uncTarget; + << QDir::fromNativeSeparators(uncSymlink) << QDir::fromNativeSeparators(uncTarget) << uncTarget; } //Junctions @@ -1740,7 +1762,7 @@ void tst_QFileInfo::ntfsJunctionPointsAndSymlinks_data() QFileInfo targetInfo(target); QTest::newRow("junction_pwd") << NtfsTestResource(NtfsTestResource::Junction, junction, target) - << junction << false << QString() << QString(); + << junction << QString() << QString(); QFileInfo fileInJunction(targetInfo.absoluteFilePath().append("/file")); QFile file(fileInJunction.absoluteFilePath()); @@ -1749,14 +1771,14 @@ void tst_QFileInfo::ntfsJunctionPointsAndSymlinks_data() QVERIFY2(file.exists(), msgDoesNotExist(file.fileName()).constData()); QTest::newRow("file in junction") << NtfsTestResource() - << fileInJunction.absoluteFilePath() << false << QString() << fileInJunction.canonicalFilePath(); + << fileInJunction.absoluteFilePath() << QString() << fileInJunction.canonicalFilePath(); target = QDir::rootPath(); junction = "junction_root"; targetInfo.setFile(target); QTest::newRow("junction_root") << NtfsTestResource(NtfsTestResource::Junction, junction, target) - << junction << false << QString() << QString(); + << junction << QString() << QString(); //Mountpoint wchar_t buffer[MAX_PATH]; @@ -1767,35 +1789,37 @@ void tst_QFileInfo::ntfsJunctionPointsAndSymlinks_data() rootVolume.replace("\\\\?\\","\\??\\"); QTest::newRow("mountpoint") << NtfsTestResource(NtfsTestResource::Junction, junction, rootVolume) - << junction << false << QString() << QString(); + << junction << QString() << QString(); } void tst_QFileInfo::ntfsJunctionPointsAndSymlinks() { QFETCH(NtfsTestResource, resource); QFETCH(QString, path); - QFETCH(bool, isSymLink); QFETCH(QString, linkTarget); QFETCH(QString, canonicalFilePath); - QString errorMessage; - DWORD creationResult = ERROR_SUCCESS; + bool isSymLink = false; + bool isJunction = false; + FileSystem::Result creationResult; switch (resource.type) { case NtfsTestResource::None: break; case NtfsTestResource::SymLink: - creationResult = createSymbolicLink(resource.source, resource.target, &errorMessage); + isSymLink = true; + creationResult = FileSystem::createSymbolicLink(resource.source, resource.target); break; case NtfsTestResource::Junction: - creationResult = FileSystem::createNtfsJunction(resource.target, resource.source, &errorMessage); - if (creationResult == ERROR_NOT_SUPPORTED) // Special value indicating non-NTFS drive - QSKIP(qPrintable(errorMessage)); + isJunction = true; + creationResult = FileSystem::createNtfsJunction(resource.target, resource.source); + if (creationResult.dwErr == ERROR_NOT_SUPPORTED) // Special value indicating non-NTFS drive + QSKIP(qPrintable(creationResult.errorMessage)); break; } - if (creationResult == ERROR_PRIVILEGE_NOT_HELD) - QSKIP(msgInsufficientPrivileges(errorMessage)); - QVERIFY2(creationResult == ERROR_SUCCESS, qPrintable(errorMessage)); + if (creationResult.dwErr == ERROR_PRIVILEGE_NOT_HELD) + QSKIP(msgInsufficientPrivileges(creationResult.errorMessage)); + QVERIFY2(creationResult.dwErr == ERROR_SUCCESS, qPrintable(creationResult.errorMessage)); QFileInfo fi(path); auto guard = qScopeGuard([&fi, this]() { @@ -1810,14 +1834,36 @@ void tst_QFileInfo::ntfsJunctionPointsAndSymlinks() } } }); - const QString actualSymLinkTarget = isSymLink ? fi.symLinkTarget() : QString(); - const QString actualCanonicalFilePath = isSymLink ? fi.canonicalFilePath() : QString(); - QCOMPARE(fi.isJunction(), resource.type == NtfsTestResource::Junction); + const QString actualCanonicalFilePath = fi.canonicalFilePath(); + QCOMPARE(fi.isJunction(), isJunction); QCOMPARE(fi.isSymbolicLink(), isSymLink); if (isSymLink) { - QCOMPARE(actualSymLinkTarget, linkTarget); + QCOMPARE(fi.symLinkTarget(), linkTarget); QCOMPARE(actualCanonicalFilePath, canonicalFilePath); } + + if (isJunction) { + if (creationResult.target.startsWith(uR"(\??\)")) + creationResult.target = creationResult.target.sliced(4); + + // resolve volume to drive letter + static const QRegularExpression matchVolumeRe(uR"(^Volume\{([a-z]|[0-9]|-)+\}\\)"_s, + QRegularExpression::CaseInsensitiveOption); + auto matchVolume = matchVolumeRe.match(creationResult.target); + if (matchVolume.hasMatch()) { + Q_ASSERT(matchVolume.capturedStart() == 0); + DWORD len; + wchar_t buffer[MAX_PATH]; + const QString volumeName = uR"(\\?\)"_s + matchVolume.captured(); + if (GetVolumePathNamesForVolumeName(reinterpret_cast<LPCWSTR>(volumeName.utf16()), + buffer, MAX_PATH, &len) != 0) { + creationResult.target.replace(0, matchVolume.capturedLength(), + QString::fromWCharArray(buffer)); + } + } + QCOMPARE(fi.junctionTarget(), QDir::fromNativeSeparators(creationResult.target)); + QCOMPARE(actualCanonicalFilePath, QFileInfo(creationResult.link).canonicalFilePath()); + } } void tst_QFileInfo::brokenShortcut() @@ -1848,7 +1894,7 @@ void tst_QFileInfo::brokenShortcut() void tst_QFileInfo::isWritable() { QFile tempfile("tempfile.txt"); - tempfile.open(QIODevice::WriteOnly); + QVERIFY(tempfile.open(QIODevice::WriteOnly)); tempfile.write("This file is generated by the QFileInfo autotest."); tempfile.close(); @@ -1862,15 +1908,14 @@ void tst_QFileInfo::isWritable() #endif #if defined (Q_OS_WIN) - QScopedValueRollback<int> ntfsMode(qt_ntfs_permission_lookup); - qt_ntfs_permission_lookup = 1; + QNtfsPermissionCheckGuard permissionGuard; QFileInfo fi2(QFile::decodeName(qgetenv("SystemRoot") + "/system.ini")); QVERIFY(fi2.exists()); QCOMPARE(fi2.isWritable(), IsUserAdmin()); #endif #if defined (Q_OS_QNX) // On QNX /etc is usually on a read-only filesystem - QVERIFY(!QFileInfo("/etc/passwd").isWritable()); + QCOMPARE(QFileInfo("/etc/passwd").isWritable(), (geteuid() == 0)); #elif defined (Q_OS_UNIX) && !defined(Q_OS_VXWORKS) // VxWorks does not have users/groups for (const char *attempt : { "/etc/passwd", "/etc/machine-id", "/proc/version" }) { if (access(attempt, F_OK) == -1) @@ -1883,8 +1928,14 @@ void tst_QFileInfo::isWritable() void tst_QFileInfo::isExecutable() { QString appPath = QCoreApplication::applicationDirPath(); -#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED) - appPath += "/libtst_qfileinfo.so"; +#ifdef Q_OS_ANDROID + QDir dir(appPath); + QVERIFY(dir.exists()); + dir.setNameFilters({ "libtst_qfileinfo*.so" }); + QStringList entries = dir.entryList(); + QCOMPARE(entries.size(), 1); + + appPath += "/" + entries[0]; #else appPath += "/tst_qfileinfo"; # if defined(Q_OS_WIN) @@ -1892,6 +1943,7 @@ void tst_QFileInfo::isExecutable() # endif #endif QFileInfo fi(appPath); + QVERIFY(fi.exists()); QCOMPARE(fi.isExecutable(), true); QCOMPARE(QFileInfo(m_proFile).isExecutable(), false); @@ -1962,7 +2014,7 @@ private: void tst_QFileInfo::testDecomposedUnicodeNames() { -#ifndef Q_OS_MAC +#ifndef Q_OS_DARWIN QSKIP("This is a OS X only test (unless you know more about filesystems, then maybe you should try it ;)"); #else QFETCH(QString, filePath); @@ -2069,7 +2121,7 @@ bool IsUserAdmin() void tst_QFileInfo::owner() { QString userName; -#if defined(Q_OS_UNIX) && !defined(Q_OS_VXWORKS) +#if defined(Q_OS_UNIX) && !defined(Q_OS_VXWORKS) && !defined(Q_OS_INTEGRITY) { passwd *user = getpwuid(geteuid()); QVERIFY(user); @@ -2106,7 +2158,7 @@ void tst_QFileInfo::owner() NetApiBufferFree(pBuf); } } - qt_ntfs_permission_lookup = 1; + QNtfsPermissionCheckGuard permissionGuard; #endif if (userName.isEmpty()) QSKIP("Can't retrieve the user name"); @@ -2123,15 +2175,12 @@ void tst_QFileInfo::owner() QCOMPARE(fi.owner(), userName); QFile::remove(fileName); -#if defined(Q_OS_WIN) - qt_ntfs_permission_lookup = 0; -#endif } void tst_QFileInfo::group() { QString expected; -#if defined(Q_OS_UNIX) && !defined(Q_OS_VXWORKS) +#if defined(Q_OS_UNIX) && !defined(Q_OS_VXWORKS) && !defined(Q_OS_INTEGRITY) struct group *gr; gid_t gid = getegid(); @@ -2209,10 +2258,10 @@ static void stateCheck(const QFileInfo &info, const QString &dirname, const QStr QCOMPARE(info.permissions(), QFile::Permissions()); - QVERIFY(!info.birthTime().isValid()); - QVERIFY(!info.metadataChangeTime().isValid()); - QVERIFY(!info.lastRead().isValid()); - QVERIFY(!info.lastModified().isValid()); + QVERIFY(!info.birthTime(QTimeZone::UTC).isValid()); + QVERIFY(!info.metadataChangeTime(QTimeZone::UTC).isValid()); + QVERIFY(!info.lastRead(QTimeZone::UTC).isValid()); + QVERIFY(!info.lastModified(QTimeZone::UTC).isValid()); }; void tst_QFileInfo::invalidState_data() @@ -2261,13 +2310,15 @@ void tst_QFileInfo::stdfilesystem() // We compare using absoluteFilePath since QFileInfo::operator== ends up using // canonicalFilePath which evaluates to empty-string for non-existent paths causing // these tests to always succeed. -#define COMPARE_CONSTRUCTION(filepath) \ - QCOMPARE(QFileInfo(fs::path(filepath)).absoluteFilePath(), \ - QFileInfo(QString::fromLocal8Bit(filepath)).absoluteFilePath()); \ - QCOMPARE(QFileInfo(base, fs::path(filepath)).absoluteFilePath(), \ - QFileInfo(base, QString::fromLocal8Bit(filepath)).absoluteFilePath()) - QDir base{ "../" }; // Used for the QFileInfo(QDir, <path>) ctor + auto doCompare = [&base](const char *filepath) { + QCOMPARE(QFileInfo(fs::path(filepath)).absoluteFilePath(), + QFileInfo(QString::fromLocal8Bit(filepath)).absoluteFilePath()); + QCOMPARE(QFileInfo(base, fs::path(filepath)).absoluteFilePath(), + QFileInfo(base, QString::fromLocal8Bit(filepath)).absoluteFilePath()); + }; +#define COMPARE_CONSTRUCTION(filepath) \ + doCompare(filepath); if (QTest::currentTestFailed()) return COMPARE_CONSTRUCTION("./file"); @@ -2280,7 +2331,22 @@ void tst_QFileInfo::stdfilesystem() COMPARE_CONSTRUCTION("/path/TO/file.txt"); COMPARE_CONSTRUCTION("./path/TO/file.txt"); COMPARE_CONSTRUCTION("../file.txt"); +#if !(defined(__GLIBCXX__) && defined(Q_OS_WIN32)) + // libstdc++ bug on Windows - https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111244 COMPARE_CONSTRUCTION("./filæ.txt"); +#endif + + // Test unicode strings + QCOMPARE(QFileInfo(fs::path(u"./filæ.txt")).absoluteFilePath(), + QFileInfo(u"./filæ.txt"_s).absoluteFilePath()); + QCOMPARE(QFileInfo(base, fs::path(u"./filæ.txt")).absoluteFilePath(), + QFileInfo(base, u"./filæ.txt"_s).absoluteFilePath()); +#ifdef __cpp_char8_t + QCOMPARE(QFileInfo(fs::path(u8"./filæ.txt")).absoluteFilePath(), + QFileInfo(u8"./filæ.txt").absoluteFilePath()); + QCOMPARE(QFileInfo(base, fs::path(u8"./filæ.txt")).absoluteFilePath(), + QFileInfo(base, u8"./filæ.txt").absoluteFilePath()); +#endif #undef COMPARE_CONSTRUCTION { @@ -2342,5 +2408,20 @@ void tst_QFileInfo::stdfilesystem() #endif } +void tst_QFileInfo::readSymLink() +{ + QString symLinkName("./a.link"); + const auto tidier = qScopeGuard([symLinkName]() { QFile::remove(symLinkName); }); + +#ifdef Q_OS_WIN + QVERIFY2(CreateSymbolicLink(L"a.link", L"..\\..\\a", SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) + != 0, + "Failed to create symlink for test"); +#else + QVERIFY2(QFile::link("../../a", symLinkName), "Failed to create symlink for test"); +#endif + QFileInfo info(symLinkName); + QCOMPARE(info.readSymLink(), QString("../../a")); +} QTEST_MAIN(tst_QFileInfo) #include "tst_qfileinfo.moc" |