diff options
Diffstat (limited to 'tests/auto/corelib/io')
18 files changed, 749 insertions, 617 deletions
diff --git a/tests/auto/corelib/io/io.pro b/tests/auto/corelib/io/io.pro index 01ed84fda9..bd2a9f4c8e 100644 --- a/tests/auto/corelib/io/io.pro +++ b/tests/auto/corelib/io/io.pro @@ -34,12 +34,6 @@ SUBDIRS=\ qurl \ qurlinternal \ qurlquery \ - qwinoverlappedionotifier \ - -!win32 { - SUBDIRS -=\ - qwinoverlappedionotifier -} !qtHaveModule(gui): SUBDIRS -= \ qdatastream \ @@ -73,5 +67,4 @@ win32:!qtConfig(private_tests): SUBDIRS -= \ qprocess-noapplication winrt: SUBDIRS -= \ - qstorageinfo \ - qwinoverlappedionotifier + qstorageinfo diff --git a/tests/auto/corelib/io/qabstractfileengine/tst_qabstractfileengine.cpp b/tests/auto/corelib/io/qabstractfileengine/tst_qabstractfileengine.cpp index ddfd14550b..1072cb44f9 100644 --- a/tests/auto/corelib/io/qabstractfileengine/tst_qabstractfileengine.cpp +++ b/tests/auto/corelib/io/qabstractfileengine/tst_qabstractfileengine.cpp @@ -341,8 +341,10 @@ public: if (file) { QMutexLocker lock(&file->mutex); switch (time) { - case CreationTime: - return file->creation; + case BirthTime: + return file->birth; + case MetadataChangeTime: + return file->change; case ModificationTime: return file->modification; case AccessTime: @@ -353,6 +355,13 @@ public: return QDateTime(); } + bool setFileTime(const QDateTime &newDate, FileTime time) + { + Q_UNUSED(newDate); + Q_UNUSED(time); + return false; + } + void setFileName(const QString &file) { if (openForRead_ || openForWrite_) @@ -449,7 +458,7 @@ protected: uint userId, groupId; QAbstractFileEngine::FileFlags fileFlags; - QDateTime creation, modification, access; + QDateTime birth, change, modification, access; QByteArray content; }; diff --git a/tests/auto/corelib/io/qdebug/tst_qdebug.cpp b/tests/auto/corelib/io/qdebug/tst_qdebug.cpp index 7147405f3b..b43ea7cfa5 100644 --- a/tests/auto/corelib/io/qdebug/tst_qdebug.cpp +++ b/tests/auto/corelib/io/qdebug/tst_qdebug.cpp @@ -51,6 +51,7 @@ private slots: void qDebugQChar() const; void qDebugQString() const; void qDebugQStringRef() const; + void qDebugQStringView() const; void qDebugQLatin1String() const; void qDebugQByteArray() const; void qDebugQFlags() const; @@ -492,6 +493,46 @@ void tst_QDebug::qDebugQStringRef() const } } +void tst_QDebug::qDebugQStringView() const +{ + /* Use a basic string. */ + { + QLatin1String file, function; + int line = 0; + const QStringView inView = QStringViewLiteral("input"); + + MessageHandlerSetter mhs(myMessageHandler); + { qDebug() << inView; } +#ifndef QT_NO_MESSAGELOGCONTEXT + file = QLatin1String(__FILE__); line = __LINE__ - 2; function = QLatin1String(Q_FUNC_INFO); +#endif + QCOMPARE(s_msgType, QtDebugMsg); + QCOMPARE(s_msg, QLatin1String("\"input\"")); + QCOMPARE(QLatin1String(s_file), file); + QCOMPARE(s_line, line); + QCOMPARE(QLatin1String(s_function), function); + } + + /* Use a null QStringView. */ + { + QString file, function; + int line = 0; + + const QStringView inView; + + MessageHandlerSetter mhs(myMessageHandler); + { qDebug() << inView; } +#ifndef QT_NO_MESSAGELOGCONTEXT + file = __FILE__; line = __LINE__ - 2; function = Q_FUNC_INFO; +#endif + QCOMPARE(s_msgType, QtDebugMsg); + QCOMPARE(s_msg, QLatin1String("\"\"")); + QCOMPARE(QLatin1String(s_file), file); + QCOMPARE(s_line, line); + QCOMPARE(QLatin1String(s_function), function); + } +} + void tst_QDebug::qDebugQLatin1String() const { QString file, function; diff --git a/tests/auto/corelib/io/qfile/BLACKLIST b/tests/auto/corelib/io/qfile/BLACKLIST index 8d636d40b8..8366667166 100644 --- a/tests/auto/corelib/io/qfile/BLACKLIST +++ b/tests/auto/corelib/io/qfile/BLACKLIST @@ -5,7 +5,3 @@ msvc-2017 ci [readLineStdin_lineByLine] msvc-2015 ci msvc-2017 ci -[openStandardStreamsFileDescriptors] -osx -[openStandardStreamsBufferedStreams] -osx diff --git a/tests/auto/corelib/io/qfile/tst_qfile.cpp b/tests/auto/corelib/io/qfile/tst_qfile.cpp index 32165ad2b8..7bf45be58c 100644 --- a/tests/auto/corelib/io/qfile/tst_qfile.cpp +++ b/tests/auto/corelib/io/qfile/tst_qfile.cpp @@ -2086,6 +2086,7 @@ public: uint ownerId(FileOwner) const { return 0; } QString owner(FileOwner) const { return QString(); } QDateTime fileTime(FileTime) const { return QDateTime(); } + bool setFileTime(const QDateTime &newDate, FileTime time) { return false; } private: int number; @@ -3184,22 +3185,39 @@ static qint64 streamExpectedSize(int fd) QT_STATBUF sb; if (QT_FSTAT(fd, &sb) != -1) return sb.st_size; + qErrnoWarning("Could not fstat fd %d", fd); return 0; } static qint64 streamCurrentPosition(int fd) { - QT_OFF_T pos = QT_LSEEK(fd, 0, SEEK_CUR); - if (pos != -1) - return pos; + QT_STATBUF sb; + if (QT_FSTAT(fd, &sb) != -1) { + QT_OFF_T pos = -1; + if ((sb.st_mode & QT_STAT_MASK) == QT_STAT_REG) + pos = QT_LSEEK(fd, 0, SEEK_CUR); + if (pos != -1) + return pos; + // failure to lseek() is not a problem + } else { + qErrnoWarning("Could not fstat fd %d", fd); + } return 0; } static qint64 streamCurrentPosition(FILE *f) { - QT_OFF_T pos = QT_FTELL(f); - if (pos != -1) - return pos; + QT_STATBUF sb; + if (QT_FSTAT(QT_FILENO(f), &sb) != -1) { + QT_OFF_T pos = -1; + if ((sb.st_mode & QT_STAT_MASK) == QT_STAT_REG) + pos = QT_FTELL(f); + if (pos != -1) + return pos; + // failure to ftell() is not a problem + } else { + qErrnoWarning("Could not fstat fd %d", QT_FILENO(f)); + } return 0; } diff --git a/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp b/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp index f35dab2cad..8945daff4a 100644 --- a/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp +++ b/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp @@ -35,6 +35,7 @@ #include <qtemporarydir.h> #include <qdir.h> #include <qfileinfo.h> +#include <qstorageinfo.h> #ifdef Q_OS_UNIX #include <errno.h> #include <fcntl.h> @@ -64,84 +65,38 @@ #define Q_NO_SYMLINKS #endif - -#if defined(Q_OS_UNIX) && !defined(Q_OS_VXWORKS) -inline bool qt_isEvilFsTypeName(const char *name) -{ - return (qstrncmp(name, "nfs", 3) == 0 - || qstrncmp(name, "autofs", 6) == 0 - || qstrncmp(name, "cachefs", 7) == 0); -} - -#if defined(Q_OS_BSD4) && !defined(Q_OS_NETBSD) -# include <sys/param.h> -# include <sys/mount.h> - -bool qIsLikelyToBeNfs(int handle) -{ - struct statfs buf; - if (fstatfs(handle, &buf) != 0) - return false; - return qt_isEvilFsTypeName(buf.f_fstypename); -} - -#elif defined(Q_OS_LINUX) || defined(Q_OS_HURD) - -# include <sys/vfs.h> -# ifdef QT_LINUXBASE - // LSB 3.2 has fstatfs in sys/statfs.h, sys/vfs.h is just an empty dummy header -# include <sys/statfs.h> -# endif - -# ifndef NFS_SUPER_MAGIC -# define NFS_SUPER_MAGIC 0x00006969 -# endif -# ifndef AUTOFS_SUPER_MAGIC -# define AUTOFS_SUPER_MAGIC 0x00000187 -# endif -# ifndef AUTOFSNG_SUPER_MAGIC -# define AUTOFSNG_SUPER_MAGIC 0x7d92b1a0 -# endif - -bool qIsLikelyToBeNfs(int handle) +inline bool qIsLikelyToBeFat(const QString &path) { - struct statfs buf; - if (fstatfs(handle, &buf) != 0) - return false; - return buf.f_type == NFS_SUPER_MAGIC - || buf.f_type == AUTOFS_SUPER_MAGIC - || buf.f_type == AUTOFSNG_SUPER_MAGIC; + QByteArray name = QStorageInfo(path).fileSystemType().toLower(); + return name.contains("fat") || name.contains("msdos"); } -#elif defined(Q_OS_SOLARIS) || defined(Q_OS_IRIX) || defined(Q_OS_AIX) || defined(Q_OS_HPUX) \ - || defined(Q_OS_OSF) || defined(Q_OS_QNX) || defined(Q_OS_SCO) \ - || defined(Q_OS_UNIXWARE) || defined(Q_OS_RELIANT) || defined(Q_OS_NETBSD) - -# include <sys/statvfs.h> - -bool qIsLikelyToBeNfs(int handle) +inline bool qIsLikelyToBeNfs(const QString &path) { - struct statvfs buf; - if (fstatvfs(handle, &buf) != 0) - return false; -#if defined(Q_OS_NETBSD) - return qt_isEvilFsTypeName(buf.f_fstypename); +#ifdef Q_OS_WIN + Q_UNUSED(path); + return false; #else - return qt_isEvilFsTypeName(buf.f_basetype); + QByteArray type = QStorageInfo(path).fileSystemType(); + const char *name = type.constData(); + + return (qstrncmp(name, "nfs", 3) == 0 + || qstrncmp(name, "autofs", 6) == 0 + || qstrncmp(name, "autofsng", 8) == 0 + || qstrncmp(name, "cachefs", 7) == 0); #endif } -#else -inline bool qIsLikelyToBeNfs(int /* handle */) -{ - return false; -} -#endif -#endif static QString seedAndTemplate() { - qsrand(QDateTime::currentSecsSinceEpoch()); - return QDir::tempPath() + "/tst_qfileinfo-XXXXXX"; + QString base; +#if defined(Q_OS_UNIX) && !defined(Q_OS_ANDROID) + // use XDG_RUNTIME_DIR as it's a fully-capable FS + base = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation); +#endif + if (base.isEmpty()) + base = QDir::tempPath(); + return base + "/tst_qfileinfo-XXXXXX"; } static QByteArray msgDoesNotExist(const QString &name) @@ -277,8 +232,9 @@ private slots: #endif void group(); + void invalidState_data(); void invalidState(); - void nonExistingFileDates(); + void nonExistingFile(); private: const QString m_currentDir; @@ -1034,6 +990,11 @@ void tst_QFileInfo::systemFiles() QVERIFY2(fi.exists(), msgDoesNotExist(fi.absoluteFilePath()).constData()); QVERIFY(fi.size() > 0); QVERIFY(fi.lastModified().isValid()); + QVERIFY(fi.metadataChangeTime().isValid()); + QCOMPARE(fi.metadataChangeTime(), fi.lastModified()); // On Windows, they're the same + QVERIFY(fi.birthTime().isValid()); + QVERIFY(fi.birthTime() <= fi.lastModified()); + QCOMPARE(fi.created(), fi.birthTime()); // On Windows, they're the same } void tst_QFileInfo::compare_data() @@ -1124,43 +1085,92 @@ void tst_QFileInfo::fileTimes_data() void tst_QFileInfo::fileTimes() { - int sleepTime = 2000; + auto datePairString = [](const QDateTime &actual, const QDateTime &before) { + return (actual.toString(Qt::ISODateWithMs) + " (should be >) " + before.toString(Qt::ISODateWithMs)) + .toLatin1(); + }; + QFETCH(QString, fileName); + int sleepTime = 100; + + // on Linux and Windows, the filesystem timestamps may be slightly out of + // sync with the system clock (maybe they're using CLOCK_REALTIME_COARSE), + // so add a margin of error to our comparisons + int fsClockSkew = 10; +#ifdef Q_OS_WIN + fsClockSkew = 500; +#endif + + // NFS clocks may be WAY out of sync + if (qIsLikelyToBeNfs(fileName)) + QSKIP("This test doesn't work on NFS"); + + bool noAccessTime = false; + { + // 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) { + fsClockSkew = sleepTime = 1000; + + noAccessTime = qIsLikelyToBeFat(fileName); + if (noAccessTime) { + // FAT filesystems (but maybe not exFAT) store timestamps with 2-second + // granularity and access time with 1-day granularity + fsClockSkew = sleepTime = 2000; + } + } + } + if (QFile::exists(fileName)) { QVERIFY(QFile::remove(fileName)); } - QTest::qSleep(sleepTime); + + QDateTime beforeBirth, beforeWrite, beforeMetadataChange, beforeRead; + QDateTime birthTime, writeTime, metadataChangeTime, readTime; + + // --- Create file and write to it + beforeBirth = QDateTime::currentDateTime().addMSecs(-fsClockSkew); { QFile file(fileName); QVERIFY(file.open(QFile::WriteOnly | QFile::Text)); -#if defined(Q_OS_UNIX) && !defined(Q_OS_VXWORKS) - if (qIsLikelyToBeNfs(file.handle())) - QSKIP("This Test doesn't work on NFS"); -#endif + QFileInfo fileInfo(fileName); + birthTime = fileInfo.birthTime(); + QVERIFY2(!birthTime.isValid() || birthTime > beforeBirth, + datePairString(birthTime, beforeBirth)); + + QTest::qSleep(sleepTime); + beforeWrite = QDateTime::currentDateTime().addMSecs(-fsClockSkew); QTextStream ts(&file); ts << fileName << endl; } - QTest::qSleep(sleepTime); - QDateTime beforeWrite = QDateTime::currentDateTime(); - QTest::qSleep(sleepTime); { QFileInfo fileInfo(fileName); - QVERIFY(fileInfo.created() < beforeWrite); - QFile file(fileName); - QVERIFY(file.open(QFile::ReadWrite | QFile::Text)); - QTextStream ts(&file); - ts << fileName << endl; + writeTime = fileInfo.lastModified(); + QVERIFY2(writeTime > beforeWrite, datePairString(writeTime, beforeWrite)); + QCOMPARE(fileInfo.birthTime(), birthTime); // mustn't have changed } + + // --- Change the file's metadata QTest::qSleep(sleepTime); - QDateTime beforeRead = QDateTime::currentDateTime(); - QTest::qSleep(sleepTime); + beforeMetadataChange = QDateTime::currentDateTime().addMSecs(-fsClockSkew); + { + QFile file(fileName); + file.setPermissions(file.permissions()); + } { QFileInfo fileInfo(fileName); -// On unix created() returns the same as lastModified(). -#if !defined(Q_OS_UNIX) - QVERIFY(fileInfo.created() < beforeWrite); -#endif - QVERIFY(fileInfo.lastModified() > beforeWrite); + metadataChangeTime = fileInfo.metadataChangeTime(); + QVERIFY2(metadataChangeTime > beforeMetadataChange, + datePairString(metadataChangeTime, beforeMetadataChange)); + QVERIFY(metadataChangeTime >= writeTime); // not all filesystems can store both times + QCOMPARE(fileInfo.birthTime(), birthTime); // mustn't have changed + } + + // --- Read the file + QTest::qSleep(sleepTime); + beforeRead = QDateTime::currentDateTime().addMSecs(-fsClockSkew); + { QFile file(fileName); QVERIFY(file.open(QFile::ReadOnly | QFile::Text)); QTextStream ts(&file); @@ -1169,13 +1179,17 @@ void tst_QFileInfo::fileTimes() } QFileInfo fileInfo(fileName); -#if !defined(Q_OS_UNIX) - QVERIFY(fileInfo.created() < beforeWrite); -#endif + readTime = fileInfo.lastRead(); + QCOMPARE(fileInfo.lastModified(), writeTime); // mustn't have changed + QCOMPARE(fileInfo.birthTime(), birthTime); // mustn't have changed + QVERIFY(readTime.isValid()); + +#if defined(Q_OS_WINRT) || 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). //To enable this the HKLM\SYSTEM\CurrentControlSet\Control\FileSystem\NtfsDisableLastAccessUpdate //is set to 0, in the test machine. -#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) HKEY key; if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\FileSystem", 0, KEY_READ, &key)) { @@ -1184,75 +1198,43 @@ void tst_QFileInfo::fileTimes() LONG error = RegQueryValueEx(key, L"NtfsDisableLastAccessUpdate" , NULL, NULL, (LPBYTE)&disabledAccessTimes, &size); if (ERROR_SUCCESS == error && disabledAccessTimes) - QEXPECT_FAIL("", "File access times are disabled in windows registry (this is the default setting)", Continue); + noAccessTime = true; RegCloseKey(key); } #endif -#if defined(Q_OS_WINRT) - QEXPECT_FAIL("", "WinRT does not allow timestamp handling change in the filesystem due to sandboxing", Continue); -#elif defined(Q_OS_QNX) - QEXPECT_FAIL("", "QNX uses the noatime filesystem option", Continue); -#elif defined(Q_OS_ANDROID) - if (fileInfo.lastRead() <= beforeRead) - QEXPECT_FAIL("", "Android may use relatime or noatime on mounts", Continue); -#endif - QVERIFY(fileInfo.lastRead() > beforeRead); - QVERIFY(fileInfo.lastModified() > beforeWrite); - QVERIFY(fileInfo.lastModified() < beforeRead); + if (noAccessTime) + return; + + QVERIFY2(readTime > beforeRead, datePairString(readTime, beforeRead)); + QVERIFY(writeTime < beforeRead); } void tst_QFileInfo::fileTimes_oldFile() { - // This is not supported on WinRT -#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) - // All files are opened in share mode (both read and write). - DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; - - // All files on Windows can be read; there's no such thing as an - // unreadable file. Add GENERIC_WRITE if WriteOnly is passed. - int accessRights = GENERIC_READ | GENERIC_WRITE; - - SECURITY_ATTRIBUTES securityAtts = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; - - // Regular file mode. In Unbuffered mode, pass the no-buffering flag. - DWORD flagsAndAtts = FILE_ATTRIBUTE_NORMAL; - - // WriteOnly can create files, ReadOnly cannot. - DWORD creationDisp = OPEN_ALWAYS; - - // Create the file handle. - HANDLE fileHandle = CreateFile(L"oldfile.txt", - accessRights, - shareMode, - &securityAtts, - creationDisp, - flagsAndAtts, - NULL); - - // Set file times back to 1601. - SYSTEMTIME stime; - stime.wYear = 1601; - stime.wMonth = 1; - stime.wDayOfWeek = 1; - stime.wDay = 1; - stime.wHour = 1; - stime.wMinute = 0; - stime.wSecond = 0; - stime.wMilliseconds = 0; - - FILETIME ctime; - QVERIFY(SystemTimeToFileTime(&stime, &ctime)); - FILETIME atime = ctime; - FILETIME mtime = atime; - QVERIFY(fileHandle); - QVERIFY(SetFileTime(fileHandle, &ctime, &atime, &mtime) != 0); - - CloseHandle(fileHandle); - - QFileInfo info("oldfile.txt"); - QCOMPARE(info.lastModified(), QDateTime(QDate(1601, 1, 1), QTime(1, 0), Qt::UTC).toLocalTime()); -#endif + // This is 2^{31} seconds before 1970-01-01 15:14:8, + // i.e. shortly after the start of time_t, in any time-zone: + const QDateTime early(QDate(1901, 12, 14), QTime(12, 0)); + QFile file("ancientfile.txt"); + file.open(QIODevice::WriteOnly); + file.write("\n", 1); + file.close(); + + /* + QFile's setFileTime calls QFSFileEngine::setFileTime() which fails unless + 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); + bool ok = file.setFileTime(early, QFileDevice::FileModificationTime); + file.close(); + + if (ok) { + QFileInfo info(file.fileName()); + QCOMPARE(info.lastModified(), early); + } else { + QSKIP("Unable to set file metadata to ancient values"); + } } void tst_QFileInfo::isSymLink_data() @@ -1392,7 +1374,7 @@ void tst_QFileInfo::isNativePath_data() QTest::addColumn<bool>("isNativePath"); QTest::newRow("default-constructed") << QString() << false; - QTest::newRow("empty") << QString("") << true; + QTest::newRow("empty") << QString("") << false; QTest::newRow("local root") << QString::fromLatin1("/") << true; QTest::newRow("local non-existent file") << QString::fromLatin1("/abrakadabra.boo") << true; @@ -1928,56 +1910,96 @@ void tst_QFileInfo::group() QCOMPARE(fi.group(), expected); } -void tst_QFileInfo::invalidState() +static void stateCheck(const QFileInfo &info, const QString &dirname, const QString &filename) { - // Shouldn't crash; - - { - QFileInfo info; - QCOMPARE(info.size(), qint64(0)); - QVERIFY(!info.exists()); - - info.setCaching(false); + QCOMPARE(info.size(), qint64(0)); + QVERIFY(!info.exists()); - info.created(); - info.lastRead(); - info.lastModified(); + QString path; + QString abspath; + if (!dirname.isEmpty()) { + path = "."; + abspath = dirname + '/' + filename; } - { - QFileInfo info(""); - QCOMPARE(info.size(), qint64(0)); - QVERIFY(!info.exists()); - - info.setCaching(false); + QCOMPARE(info.filePath(), filename); + QCOMPARE(info.absoluteFilePath(), abspath); + QCOMPARE(info.canonicalFilePath(), QString()); + QCOMPARE(info.fileName(), filename); + QCOMPARE(info.baseName(), filename); + QCOMPARE(info.completeBaseName(), filename); + QCOMPARE(info.suffix(), QString()); + QCOMPARE(info.bundleName(), QString()); + QCOMPARE(info.completeSuffix(), QString()); + + QVERIFY(info.isRelative()); + QCOMPARE(info.path(), path); + QCOMPARE(info.absolutePath(), dirname); + QCOMPARE(info.dir().path(), "."); + + // these don't look right + QCOMPARE(info.canonicalPath(), path); + QCOMPARE(info.absoluteDir().path(), dirname.isEmpty() ? "." : dirname); + + QVERIFY(!info.isReadable()); + QVERIFY(!info.isWritable()); + QVERIFY(!info.isExecutable()); + QVERIFY(!info.isHidden()); + QVERIFY(!info.isFile()); + QVERIFY(!info.isDir()); + QVERIFY(!info.isSymLink()); + QVERIFY(!info.isBundle()); + QVERIFY(!info.isRoot()); + QCOMPARE(info.isNativePath(), !filename.isEmpty()); + + QCOMPARE(info.readLink(), QString()); + QCOMPARE(info.ownerId(), uint(-2)); + QCOMPARE(info.groupId(), uint(-2)); + QCOMPARE(info.owner(), QString()); + QCOMPARE(info.group(), QString()); + + QCOMPARE(info.permissions(), QFile::Permissions()); - info.created(); - info.lastRead(); - info.lastModified(); - } + QVERIFY(!info.created().isValid()); + QVERIFY(!info.birthTime().isValid()); + QVERIFY(!info.metadataChangeTime().isValid()); + QVERIFY(!info.lastRead().isValid()); + QVERIFY(!info.lastModified().isValid()); +}; - { - QFileInfo info("file-doesn't-really-exist.txt"); - QCOMPARE(info.size(), qint64(0)); - QVERIFY(!info.exists()); +void tst_QFileInfo::invalidState_data() +{ + QTest::addColumn<int>("mode"); + QTest::newRow("default") << 0; + QTest::newRow("empty") << 1; + QTest::newRow("copy-of-default") << 2; + QTest::newRow("copy-of-empty") << 3; +} - info.setCaching(false); +void tst_QFileInfo::invalidState() +{ + // Shouldn't crash or produce warnings + QFETCH(int, mode); + const QFileInfo &info = (mode & 1 ? QFileInfo("") : QFileInfo()); - info.created(); - info.lastRead(); - info.lastModified(); + if (mode & 2) { + QFileInfo copy(info); + stateCheck(copy, QString(), QString()); + } else { + stateCheck(info, QString(), QString()); } - - QVERIFY(true); } -void tst_QFileInfo::nonExistingFileDates() +void tst_QFileInfo::nonExistingFile() { - QFileInfo info("non-existing-file.foobar"); - QVERIFY(!info.exists()); - QVERIFY(!info.created().isValid()); - QVERIFY(!info.lastRead().isValid()); - QVERIFY(!info.lastModified().isValid()); + QString dirname = QDir::currentPath(); + QString cdirname = QFileInfo(dirname).canonicalFilePath(); + if (dirname != cdirname) + QDir::setCurrent(cdirname); // chdir() to our canonical path + + QString filename = "non-existing-file-foobar"; + QFileInfo info(filename); + stateCheck(info, dirname, filename); } QTEST_MAIN(tst_QFileInfo) diff --git a/tests/auto/corelib/io/qfilesystemmetadata/tst_qfilesystemmetadata.cpp b/tests/auto/corelib/io/qfilesystemmetadata/tst_qfilesystemmetadata.cpp index 1c104688a5..ea4da39cd5 100644 --- a/tests/auto/corelib/io/qfilesystemmetadata/tst_qfilesystemmetadata.cpp +++ b/tests/auto/corelib/io/qfilesystemmetadata/tst_qfilesystemmetadata.cpp @@ -66,14 +66,16 @@ void tst_QFileSystemMetaData::timeSinceEpoch() /* data.ftLastAccessTime = data.ftLastWriteTime = */ data.ftCreationTime = epochToFileTime(afterEpochUtc); meta.fillFromFindData(data); + QCOMPARE(meta.birthTime().toUTC(), + QDateTime::fromMSecsSinceEpoch(afterEpochUtc * qint64(1000), Qt::UTC)); #else QT_STATBUF data; memset(&data, 0, sizeof(data)); data.st_ctime = afterEpochUtc; meta.fillFromStatBuf(data); -#endif - QCOMPARE(meta.creationTime().toUTC(), + QCOMPARE(meta.metadataChangeTime().toUTC(), QDateTime::fromMSecsSinceEpoch(afterEpochUtc * qint64(1000), Qt::UTC)); +#endif } #else // i.e. no Q_AUTOTEST_EXPORT void tst_QFileSystemMetaData::timeSinceEpoch() diff --git a/tests/auto/corelib/io/qiodevice/tst_qiodevice.cpp b/tests/auto/corelib/io/qiodevice/tst_qiodevice.cpp index a76fd4703e..a0188f8ba9 100644 --- a/tests/auto/corelib/io/qiodevice/tst_qiodevice.cpp +++ b/tests/auto/corelib/io/qiodevice/tst_qiodevice.cpp @@ -55,6 +55,10 @@ private slots: void readAllKeepPosition(); void writeInTextMode(); + void skip_data(); + void skip(); + void skipAfterPeek_data(); + void skipAfterPeek(); void transaction_data(); void transaction(); @@ -628,6 +632,93 @@ void tst_QIODevice::writeInTextMode() #endif } +void tst_QIODevice::skip_data() +{ + QTest::addColumn<bool>("sequential"); + QTest::addColumn<QByteArray>("data"); + QTest::addColumn<int>("read"); + QTest::addColumn<int>("skip"); + QTest::addColumn<int>("skipped"); + QTest::addColumn<char>("expect"); + + QByteArray bigData; + bigData.fill('a', 20000); + bigData[10001] = 'x'; + + bool sequential = true; + do { + QByteArray devName(sequential ? "sequential" : "random-access"); + + QTest::newRow(qPrintable(devName + "-small_data")) << true << QByteArray("abcdefghij") + << 3 << 6 << 6 << 'j'; + QTest::newRow(qPrintable(devName + "-big_data")) << true << bigData + << 1 << 10000 << 10000 << 'x'; + QTest::newRow(qPrintable(devName + "-beyond_the_end")) << true << bigData + << 1 << 20000 << 19999 << '\0'; + + sequential = !sequential; + } while (!sequential); +} + +void tst_QIODevice::skip() +{ + QFETCH(bool, sequential); + QFETCH(QByteArray, data); + QFETCH(int, read); + QFETCH(int, skip); + QFETCH(int, skipped); + QFETCH(char, expect); + char lastChar = 0; + + QScopedPointer<QIODevice> dev(sequential ? (QIODevice *) new SequentialReadBuffer(&data) + : (QIODevice *) new QBuffer(&data)); + dev->open(QIODevice::ReadOnly); + + for (int i = 0; i < read; ++i) + dev->getChar(nullptr); + + QCOMPARE(dev->skip(skip), skipped); + dev->getChar(&lastChar); + QCOMPARE(lastChar, expect); +} + +void tst_QIODevice::skipAfterPeek_data() +{ + QTest::addColumn<bool>("sequential"); + QTest::addColumn<QByteArray>("data"); + + QByteArray bigData; + for (int i = 0; i < 1000; ++i) + bigData += "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + QTest::newRow("sequential") << true << bigData; + QTest::newRow("random-access") << false << bigData; +} + +void tst_QIODevice::skipAfterPeek() +{ + QFETCH(bool, sequential); + QFETCH(QByteArray, data); + + QScopedPointer<QIODevice> dev(sequential ? (QIODevice *) new SequentialReadBuffer(&data) + : (QIODevice *) new QBuffer(&data)); + int readSoFar = 0; + qint64 bytesToSkip = 1; + + dev->open(QIODevice::ReadOnly); + forever { + QByteArray chunk = dev->peek(bytesToSkip); + if (chunk.isEmpty()) + break; + + QCOMPARE(dev->skip(bytesToSkip), qint64(chunk.size())); + QCOMPARE(chunk, data.mid(readSoFar, chunk.size())); + readSoFar += chunk.size(); + bytesToSkip <<= 1; + } + QCOMPARE(readSoFar, data.size()); +} + void tst_QIODevice::transaction_data() { QTest::addColumn<bool>("sequential"); diff --git a/tests/auto/corelib/io/qnodebug/tst_qnodebug.cpp b/tests/auto/corelib/io/qnodebug/tst_qnodebug.cpp index b0acb1c58d..b78fa29fb6 100644 --- a/tests/auto/corelib/io/qnodebug/tst_qnodebug.cpp +++ b/tests/auto/corelib/io/qnodebug/tst_qnodebug.cpp @@ -64,7 +64,7 @@ void tst_QNoDebug::noDebugOutput() const void tst_QNoDebug::streaming() const { QDateTime dt(QDate(1,2,3),QTime(4,5,6)); - const QByteArray debugString = dt.toString(QStringLiteral("yyyy-MM-dd HH:mm:ss.zzz t")).toLatin1(); + const QByteArray debugString = dt.toString(QStringViewLiteral("yyyy-MM-dd HH:mm:ss.zzz t")).toLatin1(); const QByteArray message = "QDateTime(" + debugString + " Qt::TimeSpec(LocalTime))"; QTest::ignoreMessage(QtWarningMsg, message.constData()); qWarning() << dt; diff --git a/tests/auto/corelib/io/qprocess/testDetached/main.cpp b/tests/auto/corelib/io/qprocess/testDetached/main.cpp index 702cabe873..c10e32d584 100644 --- a/tests/auto/corelib/io/qprocess/testDetached/main.cpp +++ b/tests/auto/corelib/io/qprocess/testDetached/main.cpp @@ -40,32 +40,89 @@ #include <windows.h> #endif +static void writeStuff(QFile &f) +{ + f.write(QDir::currentPath().toUtf8()); + f.putChar('\n'); +#if defined(Q_OS_UNIX) + f.write(QByteArray::number(quint64(getpid()))); +#elif defined(Q_OS_WIN) + f.write(QByteArray::number(quint64(GetCurrentProcessId()))); +#endif + f.putChar('\n'); + f.write(qgetenv("tst_QProcess")); + f.putChar('\n'); +} + +struct Args +{ + int exitCode = 0; + QByteArray errorMessage; + QString fileName; + FILE *channel = nullptr; + QByteArray channelName; +}; + +static Args parseArguments(const QStringList &args) +{ + Args result; + if (args.count() < 2) { + result.exitCode = 128; + result.errorMessage = "Usage: testDetached [--out-channel={stdout|stderr}] filename.txt\n"; + return result; + } + for (const QString &arg : args) { + if (arg.startsWith("--")) { + if (!arg.startsWith("--out-channel=")) { + result.exitCode = 2; + result.errorMessage = "Unknown argument " + arg.toLocal8Bit(); + return result; + } + result.channelName = arg.mid(14).toLocal8Bit(); + if (result.channelName == "stdout") { + result.channel = stdout; + } else if (result.channelName == "stderr") { + result.channel = stderr; + } else { + result.exitCode = 3; + result.errorMessage = "Unknown channel " + result.channelName; + return result; + } + } else { + result.fileName = arg; + } + } + return result; +} + int main(int argc, char **argv) { QCoreApplication app(argc, argv); - QStringList args = app.arguments(); - if (args.count() != 2) { - fprintf(stderr, "Usage: testDetached filename.txt\n"); - return 128; + const Args args = parseArguments(app.arguments()); + if (args.exitCode) { + fprintf(stderr, "testDetached: %s\n", args.errorMessage.constData()); + return args.exitCode; } - QFile f(args.at(1)); + if (args.channel) { + QFile channel; + if (!channel.open(args.channel, QIODevice::WriteOnly | QIODevice::Text)) { + fprintf(stderr, "Cannot open channel %s for writing: %s\n", + qPrintable(args.channelName), qPrintable(channel.errorString())); + return 4; + } + writeStuff(channel); + } + + QFile f(args.fileName); if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) { fprintf(stderr, "Cannot open %s for writing: %s\n", qPrintable(f.fileName()), qPrintable(f.errorString())); return 1; } - f.write(QDir::currentPath().toUtf8()); - f.putChar('\n'); -#if defined(Q_OS_UNIX) - f.write(QByteArray::number(quint64(getpid()))); -#elif defined(Q_OS_WIN) - f.write(QByteArray::number(quint64(GetCurrentProcessId()))); -#endif - f.putChar('\n'); - + writeStuff(f); f.close(); return 0; diff --git a/tests/auto/corelib/io/qprocess/tst_qprocess.cpp b/tests/auto/corelib/io/qprocess/tst_qprocess.cpp index f4d6d5cb40..de6eb28503 100644 --- a/tests/auto/corelib/io/qprocess/tst_qprocess.cpp +++ b/tests/auto/corelib/io/qprocess/tst_qprocess.cpp @@ -127,7 +127,8 @@ private slots: void systemEnvironment(); void lockupsInStartDetached(); void waitForReadyReadForNonexistantProcess(); - void detachedWorkingDirectoryAndPid(); + void detachedProcessParameters_data(); + void detachedProcessParameters(); void startFinishStartFinish(); void invalidProgramString_data(); void invalidProgramString(); @@ -2072,21 +2073,54 @@ void tst_QProcess::fileWriterProcess() } while (stopWatch.elapsed() < 3000); } -void tst_QProcess::detachedWorkingDirectoryAndPid() +void tst_QProcess::detachedProcessParameters_data() { + QTest::addColumn<QString>("outChannel"); + QTest::newRow("none") << QString(); + QTest::newRow("stdout") << QString("stdout"); + QTest::newRow("stderr") << QString("stderr"); +} + +void tst_QProcess::detachedProcessParameters() +{ + QFETCH(QString, outChannel); qint64 pid; QFile infoFile(m_temporaryDir.path() + QLatin1String("/detachedinfo.txt")); if (infoFile.exists()) QVERIFY(infoFile.remove()); + QFile channelFile(m_temporaryDir.path() + QLatin1String("detachedinfo2.txt")); + if (channelFile.exists()) + QVERIFY(channelFile.remove()); QString workingDir = QDir::currentPath() + "/testDetached"; QVERIFY(QFile::exists(workingDir)); - QStringList args; - args << infoFile.fileName(); - QVERIFY(QProcess::startDetached(QDir::currentPath() + QLatin1String("/testDetached/testDetached"), args, workingDir, &pid)); + QVERIFY(qgetenv("tst_QProcess").isEmpty()); + QByteArray envVarValue("foobarbaz"); + QProcessEnvironment environment = QProcessEnvironment::systemEnvironment(); + environment.insert(QStringLiteral("tst_QProcess"), QString::fromUtf8(envVarValue)); + + QProcess process; + process.setProgram(QDir::currentPath() + QLatin1String("/testDetached/testDetached")); +#ifdef Q_OS_WIN + int modifierCalls = 0; + process.setCreateProcessArgumentsModifier( + [&modifierCalls] (QProcess::CreateProcessArguments *) { modifierCalls++; }); +#endif + QStringList args(infoFile.fileName()); + if (!outChannel.isEmpty()) { + args << QStringLiteral("--out-channel=") + outChannel; + if (outChannel == "stdout") + process.setStandardOutputFile(channelFile.fileName()); + else if (outChannel == "stderr") + process.setStandardErrorFile(channelFile.fileName()); + } + process.setArguments(args); + process.setWorkingDirectory(workingDir); + process.setProcessEnvironment(environment); + QVERIFY(process.startDetached(&pid)); QFileInfo fi(infoFile); fi.setCaching(false); @@ -2097,19 +2131,35 @@ void tst_QProcess::detachedWorkingDirectoryAndPid() } QVERIFY(infoFile.open(QIODevice::ReadOnly | QIODevice::Text)); - QString actualWorkingDir = QString::fromUtf8(infoFile.readLine()); - actualWorkingDir.chop(1); // strip off newline - QByteArray processIdString = infoFile.readLine(); - processIdString.chop(1); + QString actualWorkingDir = QString::fromUtf8(infoFile.readLine()).trimmed(); + QByteArray processIdString = infoFile.readLine().trimmed(); + QByteArray actualEnvVarValue = infoFile.readLine().trimmed(); + QByteArray infoFileContent; + if (!outChannel.isEmpty()) { + infoFile.seek(0); + infoFileContent = infoFile.readAll(); + } infoFile.close(); infoFile.remove(); + if (!outChannel.isEmpty()) { + QVERIFY(channelFile.open(QIODevice::ReadOnly | QIODevice::Text)); + QByteArray channelContent = channelFile.readAll(); + channelFile.close(); + channelFile.remove(); + QCOMPARE(channelContent, infoFileContent); + } + bool ok = false; qint64 actualPid = processIdString.toLongLong(&ok); QVERIFY(ok); QCOMPARE(actualWorkingDir, workingDir); QCOMPARE(actualPid, pid); + QCOMPARE(actualEnvVarValue, envVarValue); +#ifdef Q_OS_WIN + QCOMPARE(modifierCalls, 1); +#endif } void tst_QProcess::switchReadChannels() diff --git a/tests/auto/corelib/io/qsavefile/tst_qsavefile.cpp b/tests/auto/corelib/io/qsavefile/tst_qsavefile.cpp index 9573f3078b..96970421d3 100644 --- a/tests/auto/corelib/io/qsavefile/tst_qsavefile.cpp +++ b/tests/auto/corelib/io/qsavefile/tst_qsavefile.cpp @@ -82,6 +82,11 @@ private slots: void transactionalWriteErrorRenaming(); void symlink(); void directory(); + +#ifdef Q_OS_WIN + void alternateDataStream_data(); + void alternateDataStream(); +#endif }; static inline QByteArray msgCannotOpen(const QFileDevice &f) @@ -474,5 +479,60 @@ void tst_QSaveFile::directory() #endif } +#ifdef Q_OS_WIN +void tst_QSaveFile::alternateDataStream_data() +{ + QTest::addColumn<bool>("directWriteFallback"); + QTest::addColumn<bool>("success"); + + QTest::newRow("default") << false << false; + QTest::newRow("directWriteFallback") << true << true; +} + +void tst_QSaveFile::alternateDataStream() +{ + QFETCH(bool, directWriteFallback); + QFETCH(bool, success); + static const char newContent[] = "New content\r\n"; + + QTemporaryDir dir; + QVERIFY2(dir.isValid(), qPrintable(dir.errorString())); + QString baseName = dir.path() + QLatin1String("/base"); + { + QFile baseFile(baseName); + QVERIFY2(baseFile.open(QIODevice::ReadWrite), qPrintable(baseFile.errorString())); + } + + // First, create a file with old content + QString adsName = baseName + QLatin1String(":outfile"); + { + QFile targetFile(adsName); + if (!targetFile.open(QIODevice::ReadWrite)) + QSKIP("Failed to ceate ADS file (" + targetFile.errorString().toUtf8() + + "). Temp dir is FAT?"); + targetFile.write("Old content\r\n"); + } + + // And write to it again using QSaveFile; only works if directWriteFallback is enabled + QSaveFile file(adsName); + file.setDirectWriteFallback(directWriteFallback); + QCOMPARE(file.directWriteFallback(), directWriteFallback); + + if (success) { + QVERIFY2(file.open(QIODevice::WriteOnly), qPrintable(file.errorString())); + file.write(newContent); + QVERIFY2(file.commit(), qPrintable(file.errorString())); + + // check the contents + QFile targetFile(adsName); + QVERIFY2(targetFile.open(QIODevice::ReadOnly), qPrintable(targetFile.errorString())); + QByteArray contents = targetFile.readAll(); + QCOMPARE(contents, QByteArray(newContent)); + } else { + QVERIFY(!file.open(QIODevice::WriteOnly)); + } +} +#endif + QTEST_MAIN(tst_QSaveFile) #include "tst_qsavefile.moc" diff --git a/tests/auto/corelib/io/qsettings/tst_qsettings.cpp b/tests/auto/corelib/io/qsettings/tst_qsettings.cpp index 332c2dcc01..012ce5f2f5 100644 --- a/tests/auto/corelib/io/qsettings/tst_qsettings.cpp +++ b/tests/auto/corelib/io/qsettings/tst_qsettings.cpp @@ -118,6 +118,10 @@ private slots: void remove(); void contains(); void sync(); + void syncNonWriteableDir(); +#ifdef Q_OS_WIN + void syncAlternateDataStream(); +#endif void setFallbacksEnabled(); void setFallbacksEnabled_data(); void fromFile_data(); @@ -1750,6 +1754,78 @@ void tst_QSettings::sync() QCOMPARE(settings1.allKeys().count(), 11); } +void tst_QSettings::syncNonWriteableDir() +{ + QTemporaryDir tempDir; + QVERIFY2(tempDir.isValid(), qUtf8Printable(tempDir.errorString())); + + // first, create a file + QString filename = tempDir.path() + "/config.ini"; + { + QFile f(filename); + QVERIFY2(f.open(QIODevice::WriteOnly), qUtf8Printable(f.errorString())); + } + + // second, make the dir unwriteable + QVERIFY(QFile::setPermissions(tempDir.path(), QFile::ReadUser | QFile::ExeUser)); + struct UndoSetPermissions { + QString name; + UndoSetPermissions(const QString &name) : name(name) {} + ~UndoSetPermissions() + { QFile::setPermissions(name, QFile::ReadUser | QFile::WriteUser | QFile::ExeUser); } + }; + UndoSetPermissions undo(tempDir.path()); // otherwise, QTemporaryDir will fail + + { + QSettings settings(filename, QSettings::IniFormat); + QVERIFY(settings.isAtomicSyncRequired()); + settings.setAtomicSyncRequired(false); + settings.setValue("alpha/beta", 1); + settings.sync(); + QCOMPARE(settings.status(), QSettings::NoError); + } + + QVERIFY(QFileInfo(filename).size() != 0); + QSettings settings(filename, QSettings::IniFormat); + QCOMPARE(settings.value("alpha/beta"), QVariant(1)); +} + +#ifdef Q_OS_WIN +void tst_QSettings::syncAlternateDataStream() +{ + QTemporaryDir tempDir; + QVERIFY2(tempDir.isValid(), qUtf8Printable(tempDir.errorString())); + + // first, create a file + QString filename = tempDir.path() + "/file"; + { + QFile f(filename); + QVERIFY2(f.open(QIODevice::WriteOnly), qUtf8Printable(f.errorString())); + } + + // then create an ADS + filename += ":config.ini"; + { + QFile f(filename); + if (!f.open(QIODevice::WriteOnly)) + QSKIP("Could not create ADS file (" + f.errorString().toUtf8() + ") - FAT drive?"); + } + + { + QSettings settings(filename, QSettings::IniFormat); + QVERIFY(settings.isAtomicSyncRequired()); + settings.setAtomicSyncRequired(false); + settings.setValue("alpha/beta", 1); + settings.sync(); + QCOMPARE(settings.status(), QSettings::NoError); + } + + QVERIFY(QFileInfo(filename).size() != 0); + QSettings settings(filename, QSettings::IniFormat); + QCOMPARE(settings.value("alpha/beta"), QVariant(1)); +} +#endif + void tst_QSettings::setFallbacksEnabled_data() { populateWithFormats(); diff --git a/tests/auto/corelib/io/qtemporarydir/tst_qtemporarydir.cpp b/tests/auto/corelib/io/qtemporarydir/tst_qtemporarydir.cpp index 4bed8d0fd6..fcd9133099 100644 --- a/tests/auto/corelib/io/qtemporarydir/tst_qtemporarydir.cpp +++ b/tests/auto/corelib/io/qtemporarydir/tst_qtemporarydir.cpp @@ -148,25 +148,24 @@ void tst_QTemporaryDir::fileTemplate_data() { QTest::addColumn<QString>("constructorTemplate"); QTest::addColumn<QString>("prefix"); + QTest::addColumn<QString>("suffix"); + + QTest::newRow("default") << "" << "tst_qtemporarydir-" << ""; - QTest::newRow("constructor default") << "" << "tst_qtemporarydir-"; - - QTest::newRow("constructor with xxx sufix") << "qt_XXXXXXxxx" << "qt_XXXXXXxxx"; - QTest::newRow("constructor with xXx sufix") << "qt_XXXXXXxXx" << "qt_XXXXXXxXx"; - QTest::newRow("constructor with no suffix") << "qt_XXXXXX" << "qt_"; - QTest::newRow("constructor with >6 X's, no suffix") << "qt_XXXXXXXXXX" << "qt_"; - // When more than 6 X are present at the end, linux and windows will only replace the last 6, - // while Mac OS will actually replace all of them so we can only expect "qt_" (and check isValid). - QTest::newRow("constructor with XXXX suffix") << "qt_XXXXXX_XXXX" << "qt_"; - QTest::newRow("constructor with XXXX prefix") << "qt_XXXX" << "qt_"; - QTest::newRow("constructor with XXXXX prefix") << "qt_XXXXX" << "qt_"; + QTest::newRow("xxx-suffix") << "qt_XXXXXXxxx" << "qt_" << "xxx"; + QTest::newRow("xXx-suffix") << "qt_XXXXXXxXx" << "qt_" << "xXx"; + QTest::newRow("no-suffix") << "qt_XXXXXX" << "qt_" << ""; + QTest::newRow("10X") << "qt_XXXXXXXXXX" << "qt_" << ""; + QTest::newRow("4Xsuffix") << "qt_XXXXXX_XXXX" << "qt_" << "_XXXX"; + QTest::newRow("4Xprefix") << "qt_XXXX" << "qt_XXXX" << ""; + QTest::newRow("5Xprefix") << "qt_XXXXX" << "qt_XXXXX" << ""; if (canHandleUnicodeFileNames()) { // Test Umlauts (contained in Latin1) QString prefix = "qt_" + umlautTestText(); - QTest::newRow("Umlauts") << (prefix + "XXXXXX") << prefix; - // Test Chinese + QTest::newRow("Umlauts") << (prefix + "XXXXXX") << prefix << ""; + // test non-Latin1 prefix = "qt_" + hanTestText(); - QTest::newRow("Chinese characters") << (prefix + "XXXXXX") << prefix; + QTest::newRow("Chinese") << (prefix + "XXXXXX" + umlautTestText()) << prefix << umlautTestText(); } } @@ -174,14 +173,17 @@ void tst_QTemporaryDir::fileTemplate() { QFETCH(QString, constructorTemplate); QFETCH(QString, prefix); + QFETCH(QString, suffix); QTemporaryDir tempDir(constructorTemplate); QVERIFY(tempDir.isValid()); QString dirName = QDir(tempDir.path()).dirName(); - if (prefix.length()) + if (prefix.length()) { QCOMPARE(dirName.left(prefix.length()), prefix); + QCOMPARE(dirName.right(suffix.length()), suffix); + } } diff --git a/tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp b/tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp index ab6b435125..11c24ca86f 100644 --- a/tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp +++ b/tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2017 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the test suite of the Qt Toolkit. @@ -32,6 +33,7 @@ #include <qtemporarydir.h> #include <qtemporaryfile.h> #include <qfile.h> +#include <qdatetime.h> #include <qdir.h> #include <qset.h> #include <qtextcodec.h> @@ -63,7 +65,7 @@ private slots: void fileNameIsEmpty(); void autoRemove(); void nonWritableCurrentDir(); - void write(); + void io(); void openCloseOpenClose(); void removeAndReOpen(); void size(); @@ -286,6 +288,18 @@ void tst_QTemporaryFile::autoRemove() fileName = file.fileName(); file.close(); } + QVERIFY(!fileName.isEmpty()); + QVERIFY(!QFile::exists(fileName)); + + // same, but gets the file name after closing + { + QTemporaryFile file("tempXXXXXX"); + file.setAutoRemove(true); + QVERIFY(file.open()); + file.close(); + fileName = file.fileName(); + } + QVERIFY(!fileName.isEmpty()); QVERIFY(!QFile::exists(fileName)); // Test if disabling auto remove works. @@ -296,6 +310,19 @@ void tst_QTemporaryFile::autoRemove() fileName = file.fileName(); file.close(); } + QVERIFY(!fileName.isEmpty()); + QVERIFY(QFile::exists(fileName)); + QVERIFY(QFile::remove(fileName)); + + // same, but gets the file name after closing + { + QTemporaryFile file("tempXXXXXX"); + file.setAutoRemove(false); + QVERIFY(file.open()); + file.close(); + fileName = file.fileName(); + } + QVERIFY(!fileName.isEmpty()); QVERIFY(QFile::exists(fileName)); QVERIFY(QFile::remove(fileName)); @@ -346,17 +373,51 @@ void tst_QTemporaryFile::nonWritableCurrentDir() #endif } -void tst_QTemporaryFile::write() +void tst_QTemporaryFile::io() { QByteArray data("OLE\nOLE\nOLE"); QTemporaryFile file; + QDateTime before = QDateTime::currentDateTimeUtc().addMSecs(-250); + + // discard msec component (round down) - not all FSs and OSs support them + before.setSecsSinceEpoch(before.toSecsSinceEpoch()); + QVERIFY(file.open()); + QVERIFY(file.readLink().isEmpty()); // it's not a link! + QFile::Permissions perm = file.permissions(); + QVERIFY(perm & QFile::ReadOwner); + QVERIFY(file.setPermissions(perm)); + + QCOMPARE(int(file.size()), 0); + QVERIFY(file.resize(data.size())); + QCOMPARE(int(file.size()), data.size()); QCOMPARE((int)file.write(data), data.size()); + QCOMPARE(int(file.size()), data.size()); + + QDateTime mtime = file.fileTime(QFile::FileModificationTime).toUTC(); + QDateTime btime = file.fileTime(QFile::FileBirthTime).toUTC(); + QDateTime ctime = file.fileTime(QFile::FileMetadataChangeTime).toUTC(); + QDateTime atime = file.fileTime(QFile::FileAccessTime).toUTC(); + + QDateTime after = QDateTime::currentDateTimeUtc().toUTC().addMSecs(250); + // round msecs up + after.setSecsSinceEpoch(after.toSecsSinceEpoch() + 1); + + // mtime must be valid, the rest could fail + QVERIFY(mtime <= after && mtime >= before); + QVERIFY(!btime.isValid() || (btime <= after && btime >= before)); + QVERIFY(!ctime.isValid() || (ctime <= after && ctime >= before)); + QVERIFY(!btime.isValid() || (btime <= after && btime >= before)); + + QVERIFY(file.setFileTime(before.addSecs(-10), QFile::FileModificationTime)); + mtime = file.fileTime(QFile::FileModificationTime).toUTC(); + QCOMPARE(mtime, before.addSecs(-10)); + file.reset(); QFile compare(file.fileName()); compare.open(QIODevice::ReadOnly); QCOMPARE(compare.readAll() , data); - file.close(); + QCOMPARE(compare.fileTime(QFile::FileModificationTime), mtime); } void tst_QTemporaryFile::openCloseOpenClose() @@ -407,17 +468,19 @@ void tst_QTemporaryFile::size() { QTemporaryFile file; QVERIFY(file.open()); - QVERIFY(file.exists()); QVERIFY(!file.isSequential()); QByteArray str("foobar"); file.write(str); - QVERIFY(QFile::exists(file.fileName())); + // On CE it takes more time for the filesystem to update // the information. Usually you have to close it or seek // to get latest information. flush() does not help either. QCOMPARE(file.size(), qint64(6)); file.seek(0); QCOMPARE(file.size(), qint64(6)); + + QVERIFY(QFile::exists(file.fileName())); + QVERIFY(file.exists()); } void tst_QTemporaryFile::resize() @@ -814,6 +877,14 @@ void tst_QTemporaryFile::QTBUG_4796() QCOMPARE(file5.open(), openResult); QCOMPARE(file6.open(), openResult); + // force the files to exist, if they are supposed to + QCOMPARE(!file1.fileName().isEmpty(), openResult); + QCOMPARE(!file2.fileName().isEmpty(), openResult); + QCOMPARE(!file3.fileName().isEmpty(), openResult); + QCOMPARE(!file4.fileName().isEmpty(), openResult); + QCOMPARE(!file5.fileName().isEmpty(), openResult); + QCOMPARE(!file6.fileName().isEmpty(), openResult); + QCOMPARE(file1.exists(), openResult); QCOMPARE(file2.exists(), openResult); QCOMPARE(file3.exists(), openResult); @@ -869,8 +940,6 @@ void tst_QTemporaryFile::guaranteeUnique() // First pass. See which filename QTemporaryFile will try first. { - // Fix the random seed. - qsrand(1135); QTemporaryFile tmpFile("testFile1.XXXXXX"); tmpFile.open(); takenFileName = tmpFile.fileName(); @@ -884,8 +953,6 @@ void tst_QTemporaryFile::guaranteeUnique() // Second pass, now we have blocked its first attempt with a directory. { - // Fix the random seed. - qsrand(1135); QTemporaryFile tmpFile("testFile1.XXXXXX"); QVERIFY(tmpFile.open()); QString uniqueFileName = tmpFile.fileName(); diff --git a/tests/auto/corelib/io/qurl/tst_qurl.cpp b/tests/auto/corelib/io/qurl/tst_qurl.cpp index c5647752fd..7eeaea564f 100644 --- a/tests/auto/corelib/io/qurl/tst_qurl.cpp +++ b/tests/auto/corelib/io/qurl/tst_qurl.cpp @@ -1384,18 +1384,6 @@ void tst_QUrl::compat_constructor_01_data() void tst_QUrl::compat_constructor_01() { - /* The following should work as expected: - * - * QUrlOperator op; - * op.copy( QString( "Makefile" ), - * QString("ftp://rms:grmpf12@nibbler/home/rms/tmp"), - * false ); - * - * as well as the following: - * - * QUrlOperator op; - * op.copy(QString("ftp://ftp.qt-project.org/qt/INSTALL"), "."); - */ QFETCH( QString, urlStr ); { @@ -1425,11 +1413,6 @@ void tst_QUrl::compat_constructor_02_data() void tst_QUrl::compat_constructor_02() { - /* The following should work as expected: - * - * QUrlOperator op( "ftp://ftp.qt-project.org/qt" ); - * op.copy(QString("INSTALL"), "."); - */ QFETCH( QString, urlStr ); QFETCH( QString, fileName ); diff --git a/tests/auto/corelib/io/qwinoverlappedionotifier/qwinoverlappedionotifier.pro b/tests/auto/corelib/io/qwinoverlappedionotifier/qwinoverlappedionotifier.pro deleted file mode 100644 index 4f0e9da3c2..0000000000 --- a/tests/auto/corelib/io/qwinoverlappedionotifier/qwinoverlappedionotifier.pro +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG += testcase -TARGET = tst_qwinoverlappedionotifier -QT = core-private testlib -SOURCES = tst_qwinoverlappedionotifier.cpp diff --git a/tests/auto/corelib/io/qwinoverlappedionotifier/tst_qwinoverlappedionotifier.cpp b/tests/auto/corelib/io/qwinoverlappedionotifier/tst_qwinoverlappedionotifier.cpp deleted file mode 100644 index 7034c2c9fd..0000000000 --- a/tests/auto/corelib/io/qwinoverlappedionotifier/tst_qwinoverlappedionotifier.cpp +++ /dev/null @@ -1,331 +0,0 @@ -/**************************************************************************** -** -** 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> -#include <private/qwinoverlappedionotifier_p.h> -#include <qbytearray.h> -#include <qt_windows.h> - -#ifndef PIPE_REJECT_REMOTE_CLIENTS -#define PIPE_REJECT_REMOTE_CLIENTS 0x08 -#endif - -class tst_QWinOverlappedIoNotifier : public QObject -{ - Q_OBJECT - -private slots: - void initTestCase(); - void readFile_data(); - void readFile(); - void waitForNotified_data(); - void waitForNotified(); - void waitForAnyNotified(); - void brokenPipe(); - void multipleOperations(); - -private: - QFileInfo sourceFileInfo; - DWORD notifiedBytesRead; - DWORD notifiedErrorCode; -}; - -class NotifierSink : public QObject -{ - Q_OBJECT -public: - NotifierSink(QWinOverlappedIoNotifier *notifier) - : QObject(notifier), - threshold(1) - { - connect(notifier, &QWinOverlappedIoNotifier::notified, this, &NotifierSink::notified); - } - -protected slots: - void notified(DWORD bytesRead, DWORD errorCode, OVERLAPPED *overlapped) - { - IOResult ioResult; - ioResult.bytes = bytesRead; - ioResult.errorCode = errorCode; - ioResult.overlapped = overlapped; - notifications.append(ioResult); - if (notifications.count() >= threshold) - emit notificationReceived(); - } - -signals: - void notificationReceived(); - -public: - int threshold; - - struct IOResult - { - IOResult() - : bytes(0), errorCode(ERROR_SUCCESS), overlapped(0) - {} - DWORD bytes; - DWORD errorCode; - OVERLAPPED *overlapped; - }; - - QList<IOResult> notifications; -}; - -void tst_QWinOverlappedIoNotifier::initTestCase() -{ - sourceFileInfo.setFile(QFINDTESTDATA("tst_qwinoverlappedionotifier.cpp")); - QVERIFY2(sourceFileInfo.exists(), "File tst_qwinoverlappedionotifier.cpp not found."); -} - -void tst_QWinOverlappedIoNotifier::readFile_data() -{ - QTest::addColumn<QString>("fileName"); - QTest::addColumn<int>("readBufferSize"); - QTest::addColumn<DWORD>("expectedBytesRead"); - - QString sourceFileName = QDir::toNativeSeparators(sourceFileInfo.absoluteFilePath()); - int sourceFileSize = sourceFileInfo.size(); - - QTest::newRow("read file, less than available") - << sourceFileName << sourceFileSize / 2 << DWORD(sourceFileSize / 2); - QTest::newRow("read file, more than available") - << sourceFileName << sourceFileSize * 2 << DWORD(sourceFileSize); -} - -void tst_QWinOverlappedIoNotifier::readFile() -{ - QFETCH(QString, fileName); - QFETCH(int, readBufferSize); - QFETCH(DWORD, expectedBytesRead); - - QWinOverlappedIoNotifier notifier; - NotifierSink sink(¬ifier); - connect(&sink, &NotifierSink::notificationReceived, &QTestEventLoop::instance(), &QTestEventLoop::exitLoop); - - HANDLE hFile = CreateFile(reinterpret_cast<const wchar_t*>(fileName.utf16()), - GENERIC_READ, FILE_SHARE_READ, - NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); - notifier.setHandle(hFile); - notifier.setEnabled(true); - - OVERLAPPED overlapped; - ZeroMemory(&overlapped, sizeof(OVERLAPPED)); - QByteArray buffer(readBufferSize, 0); - BOOL readSuccess = ReadFile(hFile, buffer.data(), buffer.size(), NULL, &overlapped); - QVERIFY(readSuccess || GetLastError() == ERROR_IO_PENDING); - - QTestEventLoop::instance().enterLoop(3); - CloseHandle(hFile); - QCOMPARE(sink.notifications.count(), 1); - QCOMPARE(sink.notifications.last().bytes, expectedBytesRead); - QCOMPARE(sink.notifications.last().errorCode, DWORD(ERROR_SUCCESS)); - QCOMPARE(sink.notifications.last().overlapped, &overlapped); -} - -void tst_QWinOverlappedIoNotifier::waitForNotified_data() -{ - readFile_data(); -} - -void tst_QWinOverlappedIoNotifier::waitForNotified() -{ - QFETCH(QString, fileName); - QFETCH(int, readBufferSize); - QFETCH(DWORD, expectedBytesRead); - - QWinOverlappedIoNotifier notifier; - NotifierSink sink(¬ifier); - HANDLE hFile = CreateFile(reinterpret_cast<const wchar_t*>(fileName.utf16()), - GENERIC_READ, FILE_SHARE_READ, - NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); - notifier.setHandle(hFile); - notifier.setEnabled(true); - QCOMPARE(notifier.waitForNotified(100, 0), false); - - OVERLAPPED overlapped; - ZeroMemory(&overlapped, sizeof(OVERLAPPED)); - QByteArray buffer(readBufferSize, 0); - BOOL readSuccess = ReadFile(hFile, buffer.data(), buffer.size(), NULL, &overlapped); - QVERIFY(readSuccess || GetLastError() == ERROR_IO_PENDING); - - QCOMPARE(notifier.waitForNotified(3000, &overlapped), true); - CloseHandle(hFile); - QCOMPARE(sink.notifications.count(), 1); - QCOMPARE(sink.notifications.last().bytes, expectedBytesRead); - QCOMPARE(sink.notifications.last().errorCode, DWORD(ERROR_SUCCESS)); - QCOMPARE(sink.notifications.last().overlapped, &overlapped); - QCOMPARE(notifier.waitForNotified(100, &overlapped), false); -} - -void tst_QWinOverlappedIoNotifier::waitForAnyNotified() -{ - const QString fileName = QDir::toNativeSeparators(sourceFileInfo.absoluteFilePath()); - const int readBufferSize = sourceFileInfo.size(); - - QWinOverlappedIoNotifier notifier; - HANDLE hFile = CreateFile(reinterpret_cast<const wchar_t*>(fileName.utf16()), - GENERIC_READ, FILE_SHARE_READ, - NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); - notifier.setHandle(hFile); - notifier.setEnabled(true); - QVERIFY(!notifier.waitForAnyNotified(100)); - - OVERLAPPED overlapped1; - ZeroMemory(&overlapped1, sizeof(OVERLAPPED)); - QByteArray buffer1(readBufferSize, 0); - BOOL readSuccess = ReadFile(hFile, buffer1.data(), buffer1.size(), NULL, &overlapped1); - QVERIFY(readSuccess || GetLastError() == ERROR_IO_PENDING); - - OVERLAPPED overlapped2; - ZeroMemory(&overlapped2, sizeof(OVERLAPPED)); - QByteArray buffer2(readBufferSize, 0); - readSuccess = ReadFile(hFile, buffer2.data(), buffer2.size(), NULL, &overlapped2); - QVERIFY(readSuccess || GetLastError() == ERROR_IO_PENDING); - - QSet<OVERLAPPED *> overlappedObjects; - overlappedObjects << &overlapped1 << &overlapped2; - - for (int i = 1; i <= 2; ++i) { - OVERLAPPED *notifiedOverlapped = notifier.waitForAnyNotified(3000); - QVERIFY(overlappedObjects.contains(notifiedOverlapped)); - overlappedObjects.remove(notifiedOverlapped); - } - - CloseHandle(hFile); - QVERIFY(overlappedObjects.isEmpty()); - QVERIFY(!notifier.waitForAnyNotified(100)); -} - -void tst_QWinOverlappedIoNotifier::brokenPipe() -{ - QWinOverlappedIoNotifier notifier; - NotifierSink sink(¬ifier); - connect(&sink, &NotifierSink::notificationReceived, &QTestEventLoop::instance(), &QTestEventLoop::exitLoop); - - wchar_t pipeName[] = L"\\\\.\\pipe\\tst_QWinOverlappedIoNotifier_brokenPipe"; - HANDLE hPipe = CreateNamedPipe(pipeName, - PIPE_ACCESS_DUPLEX, - PIPE_TYPE_BYTE | PIPE_NOWAIT | PIPE_REJECT_REMOTE_CLIENTS, - 1, 0, 0, 0, NULL); - QVERIFY(hPipe != INVALID_HANDLE_VALUE); - HANDLE hReadEnd = CreateFile(pipeName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); - QVERIFY(hReadEnd != INVALID_HANDLE_VALUE); - notifier.setHandle(hReadEnd); - notifier.setEnabled(true); - - OVERLAPPED overlapped; - ZeroMemory(&overlapped, sizeof(OVERLAPPED)); - QByteArray buffer(1024, 0); - BOOL readSuccess = ReadFile(hReadEnd, buffer.data(), buffer.size(), NULL, &overlapped); - QVERIFY(readSuccess || GetLastError() == ERROR_IO_PENDING); - - // close the write end of the pipe - CloseHandle(hPipe); - - QTestEventLoop::instance().enterLoop(3); - CloseHandle(hReadEnd); - QCOMPARE(sink.notifications.count(), 1); - QCOMPARE(sink.notifications.last().bytes, DWORD(0)); - QCOMPARE(sink.notifications.last().errorCode, DWORD(ERROR_BROKEN_PIPE)); - QCOMPARE(sink.notifications.last().overlapped, &overlapped); -} - -void tst_QWinOverlappedIoNotifier::multipleOperations() -{ - QWinOverlappedIoNotifier clientNotifier; - NotifierSink sink(&clientNotifier); - sink.threshold = 2; - connect(&sink, &NotifierSink::notificationReceived, - &QTestEventLoop::instance(), &QTestEventLoop::exitLoop); - - wchar_t pipeName[] = L"\\\\.\\pipe\\tst_QWinOverlappedIoNotifier_multipleOperations"; - HANDLE hServer = CreateNamedPipe(pipeName, - PIPE_ACCESS_DUPLEX, - PIPE_TYPE_BYTE | PIPE_NOWAIT | PIPE_REJECT_REMOTE_CLIENTS, - 1, 0, 0, 0, NULL); - QVERIFY(hServer != INVALID_HANDLE_VALUE); - HANDLE hClient = CreateFile(pipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, - OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); - QVERIFY(hClient != INVALID_HANDLE_VALUE); - clientNotifier.setHandle(hClient); - clientNotifier.setEnabled(true); - - // start async read on client - QByteArray clientReadBuffer(377, Qt::Uninitialized); - OVERLAPPED clientReadOverlapped; - ZeroMemory(&clientReadOverlapped, sizeof(clientReadOverlapped)); - BOOL readSuccess = ReadFile(hClient, clientReadBuffer.data(), clientReadBuffer.size(), - NULL, &clientReadOverlapped); - QVERIFY(readSuccess || GetLastError() == ERROR_IO_PENDING); - - // start async write client -> server - QByteArray clientDataToWrite(233, 'B'); - OVERLAPPED clientWriteOverlapped; - ZeroMemory(&clientWriteOverlapped, sizeof(clientWriteOverlapped)); - BOOL writeSuccess = WriteFile(hClient, clientDataToWrite.data(), clientDataToWrite.size(), - NULL, &clientWriteOverlapped); - QVERIFY(writeSuccess || GetLastError() == ERROR_IO_PENDING); - - // start async write server -> client - QByteArray serverDataToWrite(144, 'A'); - OVERLAPPED serverOverlapped; - ZeroMemory(&serverOverlapped, sizeof(serverOverlapped)); - writeSuccess = WriteFile(hServer, serverDataToWrite.data(), serverDataToWrite.size(), - NULL, &serverOverlapped); - QVERIFY(writeSuccess || GetLastError() == ERROR_IO_PENDING); - - // read synchronously on server to complete the client -> server write - QByteArray serverReadBuffer(610, Qt::Uninitialized); - DWORD dwBytesRead = 0; - readSuccess = ReadFile(hServer, serverReadBuffer.data(), serverReadBuffer.size(), - &dwBytesRead, NULL); - QVERIFY(readSuccess); - QCOMPARE(int(dwBytesRead), clientDataToWrite.size()); - serverReadBuffer.resize(dwBytesRead); - QCOMPARE(serverReadBuffer, clientDataToWrite); - - QTestEventLoop::instance().enterLoop(3); - QTRY_COMPARE(sink.notifications.count(), 2); - foreach (const NotifierSink::IOResult &r, sink.notifications) { - QCOMPARE(r.errorCode, DWORD(ERROR_SUCCESS)); - if (r.bytes == DWORD(serverDataToWrite.count())) - QCOMPARE(r.overlapped, &clientReadOverlapped); - else if (r.bytes == DWORD(clientDataToWrite.count())) - QCOMPARE(r.overlapped, &clientWriteOverlapped); - else - QVERIFY2(false, "Unexpected number of bytes received."); - } - - CloseHandle(hClient); - CloseHandle(hServer); -} - -QTEST_MAIN(tst_QWinOverlappedIoNotifier) - -#include "tst_qwinoverlappedionotifier.moc" |