diff options
Diffstat (limited to 'tests/auto/corelib/io/qfileinfo')
-rw-r--r-- | tests/auto/corelib/io/qfileinfo/CMakeLists.txt | 60 | ||||
-rw-r--r-- | tests/auto/corelib/io/qfileinfo/qfileinfo.pro | 8 | ||||
-rw-r--r-- | tests/auto/corelib/io/qfileinfo/qfileinfo.qrc | 5 | ||||
-rw-r--r-- | tests/auto/corelib/io/qfileinfo/testdata.qrc | 8 | ||||
-rw-r--r-- | tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp | 586 |
5 files changed, 433 insertions, 234 deletions
diff --git a/tests/auto/corelib/io/qfileinfo/CMakeLists.txt b/tests/auto/corelib/io/qfileinfo/CMakeLists.txt new file mode 100644 index 0000000000..3b997e1bca --- /dev/null +++ b/tests/auto/corelib/io/qfileinfo/CMakeLists.txt @@ -0,0 +1,60 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qfileinfo Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qfileinfo LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qfileinfo + SOURCES + tst_qfileinfo.cpp + LIBRARIES + Qt::CorePrivate + Qt::TestPrivate +) + +# Resources: +set(qfileinfo_resource_files + "resources/" +) + +qt_internal_add_resource(tst_qfileinfo "qfileinfo" + PREFIX + "/tst_qfileinfo/" + FILES + ${qfileinfo_resource_files} +) +set(testdata_resource_files + "resources/file1" + "resources/file1.ext1" + "resources/file1.ext1.ext2" + "tst_qfileinfo.cpp" +) + +qt_internal_add_resource(tst_qfileinfo "testdata" + PREFIX + "/testdata" + FILES + ${testdata_resource_files} +) + + +## Scopes: +##################################################################### + +qt_internal_extend_target(tst_qfileinfo CONDITION WIN32 + LIBRARIES + advapi32 + netapi32 +) + +if (APPLE) + enable_language(OBJCXX) + set_source_files_properties(tst_qfileinfo.cpp PROPERTIES LANGUAGE OBJCXX) +endif() diff --git a/tests/auto/corelib/io/qfileinfo/qfileinfo.pro b/tests/auto/corelib/io/qfileinfo/qfileinfo.pro deleted file mode 100644 index d181d16a3e..0000000000 --- a/tests/auto/corelib/io/qfileinfo/qfileinfo.pro +++ /dev/null @@ -1,8 +0,0 @@ -CONFIG += testcase -TARGET = tst_qfileinfo -QT = core-private testlib -SOURCES = tst_qfileinfo.cpp -RESOURCES += qfileinfo.qrc \ - testdata.qrc - -win32:!winrt: QMAKE_USE += advapi32 netapi32 diff --git a/tests/auto/corelib/io/qfileinfo/qfileinfo.qrc b/tests/auto/corelib/io/qfileinfo/qfileinfo.qrc deleted file mode 100644 index 2c63d8afeb..0000000000 --- a/tests/auto/corelib/io/qfileinfo/qfileinfo.qrc +++ /dev/null @@ -1,5 +0,0 @@ -<!DOCTYPE RCC><RCC version="1.0"> -<qresource prefix="/tst_qfileinfo/"> - <file>resources/</file> -</qresource> -</RCC> diff --git a/tests/auto/corelib/io/qfileinfo/testdata.qrc b/tests/auto/corelib/io/qfileinfo/testdata.qrc deleted file mode 100644 index d2974bae77..0000000000 --- a/tests/auto/corelib/io/qfileinfo/testdata.qrc +++ /dev/null @@ -1,8 +0,0 @@ -<RCC> - <qresource prefix="/testdata"> - <file>resources/file1</file> - <file>resources/file1.ext1</file> - <file>resources/file1.ext1.ext2</file> - <file>tst_qfileinfo.cpp</file> - </qresource> -</RCC> diff --git a/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp b/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp index ebb9a0a44c..563e4c2a83 100644 --- a/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp +++ b/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp @@ -1,32 +1,10 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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$ -** -****************************************************************************/ - -#include <QtTest/QtTest> +// 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 <qfile.h> #include <qdir.h> @@ -48,32 +26,28 @@ #endif #ifdef Q_OS_WIN #include <qt_windows.h> -#if !defined(Q_OS_WINRT) #include <private/qwinregistry_p.h> #include <lm.h> #endif -#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_VXWORKS) || defined(Q_OS_WINRT) +#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 -# ifndef Q_OS_WINRT bool IsUserAdmin(); -# endif #endif +using namespace Qt::StringLiterals; + inline bool qIsLikelyToBeFat(const QString &path) { QByteArray name = QStorageInfo(path).fileSystemType().toLower(); @@ -96,37 +70,12 @@ inline bool qIsLikelyToBeNfs(const QString &path) #endif } -#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) -# 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; -} - +#if defined(Q_OS_WIN) static QByteArray msgInsufficientPrivileges(const QString &errorMessage) { return "Insufficient privileges (" + errorMessage.toLocal8Bit() + ')'; } -#endif // Q_OS_WIN && !Q_OS_WINRT +#endif // Q_OS_WIN static QString seedAndTemplate() { @@ -224,6 +173,7 @@ private slots: void systemFiles(); + void compareCompiles(); void compare_data(); void compare(); @@ -232,6 +182,7 @@ private slots: void fileTimes_data(); void fileTimes(); + void setFileTimes(); void fakeFileTimes_data(); void fakeFileTimes(); @@ -244,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 @@ -261,7 +215,7 @@ private slots: void refresh(); -#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) +#if defined(Q_OS_WIN) void ntfsJunctionPointsAndSymlinks_data(); void ntfsJunctionPointsAndSymlinks(); void brokenShortcut(); @@ -278,15 +232,16 @@ private slots: void detachingOperations(); -#if !defined(Q_OS_WINRT) void owner(); -#endif void group(); void invalidState_data(); void invalidState(); void nonExistingFile(); + void stdfilesystem(); + void readSymLink(); + private: const QString m_currentDir; QString m_sourceFile; @@ -398,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"); @@ -419,13 +374,13 @@ void tst_QFileInfo::isDir_data() QTest::newRow("broken link") << "brokenlink.lnk" << false; -#if (defined(Q_OS_WIN) && !defined(Q_OS_WINRT)) +#if defined(Q_OS_WIN) QTest::newRow("drive 1") << "c:" << true; QTest::newRow("drive 2") << "c:/" << true; //QTest::newRow("drive 2") << "t:s" << false; #endif -#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) - const QString uncRoot = QStringLiteral("//") + QtNetworkSettings::winServerName(); +#if defined(Q_OS_WIN) + 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; @@ -461,14 +416,14 @@ void tst_QFileInfo::isRoot_data() QTest::newRow("simple dir") << m_resourcesDir << false; QTest::newRow("simple dir with slash") << (m_resourcesDir + QLatin1Char('/')) << false; -#if (defined(Q_OS_WIN) && !defined(Q_OS_WINRT)) +#if defined(Q_OS_WIN) QTest::newRow("drive 1") << "c:" << false; QTest::newRow("drive 2") << "c:/" << true; QTest::newRow("drive 3") << "p:/" << false; #endif -#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) - const QString uncRoot = QStringLiteral("//") + QtNetworkSettings::winServerName(); +#if defined(Q_OS_WIN) + 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; @@ -505,19 +460,14 @@ void tst_QFileInfo::exists_data() QTest::newRow("data8") << (m_resourcesDir + "/*.ext1") << false; QTest::newRow("data9") << (m_resourcesDir + "/file?.ext1") << false; QTest::newRow("data10") << "." << true; - - // Skip for the WinRT case, as GetFileAttributesEx removes _any_ - // trailing whitespace and "." is a valid entry as seen in data10 -#ifndef Q_OS_WINRT QTest::newRow("data11") << ". " << false; -#endif QTest::newRow("empty") << "" << false; QTest::newRow("simple dir") << m_resourcesDir << true; QTest::newRow("simple dir with slash") << (m_resourcesDir + QLatin1Char('/')) << true; -#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) - const QString uncRoot = QStringLiteral("//") + QtNetworkSettings::winServerName(); +#if defined(Q_OS_WIN) + 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; @@ -551,7 +501,7 @@ void tst_QFileInfo::absolutePath_data() QTest::addColumn<QString>("filename"); QString drivePrefix; -#if (defined(Q_OS_WIN) && !defined(Q_OS_WINRT)) +#if defined(Q_OS_WIN) drivePrefix = QDir::currentPath().left(2); QString nonCurrentDrivePrefix = drivePrefix.left(1).compare("X", Qt::CaseInsensitive) == 0 ? QString("Y:") : QString("X:"); @@ -561,8 +511,6 @@ void tst_QFileInfo::absolutePath_data() QTest::newRow("<not current drive>:my.dll") << nonCurrentDrivePrefix + "my.dll" << nonCurrentDrivePrefix + "/" << "my.dll"; -#elif defined(Q_OS_WINRT) - drivePrefix = QDir::currentPath().left(2); #endif QTest::newRow("0") << "/machine/share/dir1/" << drivePrefix + "/machine/share/dir1" << ""; QTest::newRow("1") << "/machine/share/dir1" << drivePrefix + "/machine/share" << "dir1"; @@ -570,7 +518,7 @@ void tst_QFileInfo::absolutePath_data() QTest::newRow("3") << "/usr/local/bin/" << drivePrefix + "/usr/local/bin" << ""; QTest::newRow("/test") << "/test" << drivePrefix + "/" << "test"; -#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) +#if defined(Q_OS_WIN) QTest::newRow("c:\\autoexec.bat") << "c:\\autoexec.bat" << "C:/" << "autoexec.bat"; QTest::newRow("c:autoexec.bat") << QDir::currentPath().left(2) + "autoexec.bat" << QDir::currentPath() @@ -710,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(); @@ -746,14 +694,13 @@ void tst_QFileInfo::canonicalFilePath() } #endif -#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) +#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(); @@ -817,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 } @@ -844,7 +791,7 @@ void tst_QFileInfo::dir_data() QTest::newRow("absFilePath") << QDir::currentPath() + "/tmp.txt" << false << QDir::currentPath(); QTest::newRow("absFilePathAbsPath") << QDir::currentPath() + "/tmp.txt" << true << QDir::currentPath(); QTest::newRow("resource1") << ":/tst_qfileinfo/resources/file1.ext1" << true << ":/tst_qfileinfo/resources"; -#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) +#if defined(Q_OS_WIN) QTest::newRow("driveWithSlash") << "C:/file1.ext1.ext2" << true << "C:/"; QTest::newRow("driveWithoutSlash") << QDir::currentPath().left(2) + "file1.ext1.ext2" << false << QDir::currentPath().left(2); #endif @@ -1035,7 +982,7 @@ void tst_QFileInfo::size() void tst_QFileInfo::systemFiles() { -#if !defined(Q_OS_WIN) || defined(Q_OS_WINRT) +#if !defined(Q_OS_WIN) QSKIP("This is a Windows only test"); #endif QFileInfo fi("c:\\pagefile.sys"); @@ -1046,9 +993,11 @@ void tst_QFileInfo::systemFiles() QCOMPARE(fi.metadataChangeTime(), fi.lastModified()); // On Windows, they're the same QVERIFY(fi.birthTime().isValid()); QVERIFY(fi.birthTime() <= fi.lastModified()); -#if QT_DEPRECATED_SINCE(5, 10) - QCOMPARE(fi.created(), fi.birthTime()); // On Windows, they're the same -#endif +} + +void tst_QFileInfo::compareCompiles() +{ + QTestPrivate::testEqualityOperatorsCompile<QFileInfo>(); } void tst_QFileInfo::compare_data() @@ -1077,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; @@ -1086,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 @@ -1095,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() @@ -1163,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); @@ -1184,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)); @@ -1233,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_WINRT) || 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). @@ -1259,6 +1208,28 @@ 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()); + + QDateTime before = QDateTime::currentDateTimeUtc().addMSecs(-5000); + + QVERIFY(file.setFileTime(before, QFile::FileModificationTime)); + const QDateTime mtime = file.fileTime(QFile::FileModificationTime).toUTC(); + if (mtime.time().msec() == 0) + { + const QTime beforeTime = before.time(); + const QTime beforeTimeWithMSCutOff{beforeTime.hour(), beforeTime.minute(), beforeTime.second(), 0}; + before.setTime(beforeTimeWithMSCutOff); + } + QCOMPARE(mtime, before); +} + void tst_QFileInfo::fakeFileTimes_data() { QTest::addColumn<QDateTime>("when"); @@ -1276,7 +1247,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(); @@ -1285,12 +1256,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"); } @@ -1307,7 +1278,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(); @@ -1359,7 +1330,7 @@ void tst_QFileInfo::isShortcut_data() << regularFile.fileName() << false; QTest::newRow("directory") << QDir::currentPath() << false; -#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) +#if defined(Q_OS_WIN) // windows shortcuts QVERIFY(regularFile.link("link.lnk")); QTest::newRow("shortcut") @@ -1382,6 +1353,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"); @@ -1395,17 +1417,14 @@ void tst_QFileInfo::isSymbolicLink_data() #ifndef Q_NO_SYMLINKS #if defined(Q_OS_WIN) -#if !defined(Q_OS_WINRT) - 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; } -#endif // !Q_OS_WINRT #else // Unix: QVERIFY(regularFile.link("symlink.lnk")); QTest::newRow("symlink.lnk") @@ -1445,10 +1464,10 @@ 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) && !defined(Q_OS_WINRT) +#if defined(Q_OS_WIN) // windows shortcuts QVERIFY(file1.link("link.lnk")); QTest::newRow("link.lnk") @@ -1461,26 +1480,23 @@ void tst_QFileInfo::link_data() #ifndef Q_NO_SYMLINKS #if defined(Q_OS_WIN) -#if !defined(Q_OS_WINRT) - 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(); } -#endif // !Q_OS_WINRT #else // Unix: QVERIFY(file1.link("link")); QTest::newRow("link") @@ -1516,9 +1532,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")); @@ -1533,14 +1549,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; @@ -1556,7 +1572,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"; @@ -1582,7 +1598,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 @@ -1636,14 +1652,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) @@ -1651,7 +1667,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()); @@ -1660,7 +1676,7 @@ void tst_QFileInfo::refresh() QCOMPARE(info2.size(), info.size()); } -#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) +#if defined(Q_OS_WIN) struct NtfsTestResource { @@ -1680,7 +1696,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"); @@ -1708,13 +1723,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 @@ -1730,23 +1745,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 @@ -1755,7 +1769,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()); @@ -1764,14 +1778,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]; @@ -1782,35 +1796,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]() { @@ -1825,14 +1841,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() @@ -1863,42 +1901,48 @@ 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(); QVERIFY(QFileInfo("tempfile.txt").isWritable()); tempfile.remove(); -#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) +#if defined(Q_OS_WIN) QFileInfo fi("c:\\pagefile.sys"); QVERIFY2(fi.exists(), msgDoesNotExist(fi.absoluteFilePath()).constData()); QVERIFY(!fi.isWritable()); #endif -#if defined (Q_OS_WIN) && !defined(Q_OS_WINRT) - QScopedValueRollback<int> ntfsMode(qt_ntfs_permission_lookup); - qt_ntfs_permission_lookup = 1; +#if defined (Q_OS_WIN) + 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 - if (::getuid() == 0) - QVERIFY(QFileInfo("/etc/passwd").isWritable()); - else - QVERIFY(!QFileInfo("/etc/passwd").isWritable()); + for (const char *attempt : { "/etc/passwd", "/etc/machine-id", "/proc/version" }) { + if (access(attempt, F_OK) == -1) + continue; + QCOMPARE(QFileInfo(attempt).isWritable(), ::access(attempt, W_OK) == 0); + } #endif } 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) @@ -1906,6 +1950,7 @@ void tst_QFileInfo::isExecutable() # endif #endif QFileInfo fi(appPath); + QVERIFY(fi.exists()); QCOMPARE(fi.isExecutable(), true); QCOMPARE(QFileInfo(m_proFile).isExecutable(), false); @@ -1976,7 +2021,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); @@ -2056,7 +2101,7 @@ void tst_QFileInfo::detachingOperations() QVERIFY(!info1.caching()); } -#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) +#if defined(Q_OS_WIN) bool IsUserAdmin() { BOOL b; @@ -2078,13 +2123,12 @@ bool IsUserAdmin() return b != FALSE; } -#endif // Q_OS_WIN && !Q_OS_WINRT +#endif // Q_OS_WIN -#ifndef Q_OS_WINRT 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); @@ -2121,7 +2165,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"); @@ -2138,16 +2182,12 @@ void tst_QFileInfo::owner() QCOMPARE(fi.owner(), userName); QFile::remove(fileName); -#if defined(Q_OS_WIN) - qt_ntfs_permission_lookup = 0; -#endif } -#endif // !Q_OS_WINRT 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(); @@ -2225,13 +2265,10 @@ static void stateCheck(const QFileInfo &info, const QString &dirname, const QStr QCOMPARE(info.permissions(), QFile::Permissions()); -#if QT_DEPRECATED_SINCE(5, 10) - QVERIFY(!info.created().isValid()); -#endif - 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() @@ -2269,6 +2306,129 @@ void tst_QFileInfo::nonExistingFile() stateCheck(info, dirname, filename); } +void tst_QFileInfo::stdfilesystem() +{ +#if QT_CONFIG(cxx17_filesystem) + + namespace fs = std::filesystem; + // Verify constructing with fs::path leads to valid objects + { + // 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. + 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"); + +#ifdef Q_OS_WIN32 + COMPARE_CONSTRUCTION("C:\\path\\to\\file.txt"); + COMPARE_CONSTRUCTION("x:\\path/to\\file.txt"); + COMPARE_CONSTRUCTION("D:/path/TO/file.txt"); + COMPARE_CONSTRUCTION("//sharename/folder/file.txt"); +#endif + 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 + { + // One proper comparison with operator== for each ctor + QFile file(QStringLiteral("./filesystem_test_file.txt")); + if (!file.open(QFile::NewOnly)) + QVERIFY(file.exists()); + file.close(); + + QFileInfo pinfo{ fs::path{ "./filesystem_test_file.txt" } }; + QFileInfo info{ QStringLiteral("./filesystem_test_file.txt") }; + QCOMPARE(pinfo, info); + } + + { + // And once more for QFileInfo(QDir, <path>) + const QString &subdir = QStringLiteral("./filesystem_test_dir/"); + base = QDir(QStringLiteral(".")); + if (!base.exists(subdir)) + QVERIFY(base.mkdir(subdir)); + base.cd(subdir); + QFile file{ base.filePath(QStringLiteral("./filesystem_test_file.txt")) }; + if (!file.open(QFile::NewOnly)) + QVERIFY(file.exists()); + file.close(); + QFileInfo pinfo{ base, fs::path{ "filesystem_test_file.txt" } }; + QFileInfo info{ base, QStringLiteral("filesystem_test_file.txt") }; + QCOMPARE(pinfo, info); + } + } + + // Verify that functions returning path all point to the same place + { +#define COMPARE_PATHS(actual, expected) \ + QCOMPARE(QString::fromStdU16String(actual.u16string()), expected) + + QFile file(QStringLiteral("./orig")); + if (!file.open(QFile::NewOnly)) + QVERIFY(file.exists()); + file.close(); + + QFileInfo info{ QStringLiteral("./orig") }; + COMPARE_PATHS(info.filesystemPath(), info.path()); + COMPARE_PATHS(info.filesystemAbsolutePath(), info.absolutePath()); + COMPARE_PATHS(info.filesystemCanonicalPath(), info.canonicalPath()); + COMPARE_PATHS(info.filesystemFilePath(), info.filePath()); + COMPARE_PATHS(info.filesystemAbsoluteFilePath(), info.absoluteFilePath()); + COMPARE_PATHS(info.filesystemCanonicalFilePath(), info.canonicalFilePath()); + + QVERIFY(file.link(QStringLiteral("./filesystem_test_symlink.lnk"))); + info = QFileInfo{ "./filesystem_test_symlink.lnk" }; + + COMPARE_PATHS(info.filesystemSymLinkTarget(), info.symLinkTarget()); +#undef COMPARE_PATHS + } + +#else + QSKIP("Not supported"); +#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" |