qt 6.5.1 original

This commit is contained in:
kleuter
2023-10-29 23:33:08 +01:00
parent 71d22ab6b0
commit 85d238dfda
21202 changed files with 5499099 additions and 0 deletions

View File

@ -0,0 +1,15 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qsavefile Test:
#####################################################################
# Collect test data
list(APPEND test_data "tst_qsavefile.cpp")
qt_internal_add_test(tst_qsavefile
SOURCES
tst_qsavefile.cpp
TESTDATA ${test_data}
)

View File

@ -0,0 +1,556 @@
// Copyright (C) 2012 David Faure <faure@kde.org>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QTest>
#include <QSaveFile>
#include <qcoreapplication.h>
#include <qstring.h>
#include <qtemporaryfile.h>
#include <qfile.h>
#include <qdir.h>
#include <qset.h>
#if defined(Q_OS_UNIX) && !defined(Q_OS_VXWORKS)
#include <unistd.h> // for geteuid
#endif
#if defined(Q_OS_WIN)
# include <qt_windows.h>
#endif
#ifdef Q_OS_INTEGRITY
#include "qplatformdefs.h"
#endif
// Restore permissions so that the QTemporaryDir cleanup can happen
class PermissionRestorer
{
Q_DISABLE_COPY(PermissionRestorer)
public:
explicit PermissionRestorer(const QString& path) : m_path(path) {}
~PermissionRestorer() { restore(); }
inline void restore()
{
QFile file(m_path);
#ifdef Q_OS_UNIX
file.setPermissions(QFile::Permissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner));
#else
file.setPermissions(QFile::WriteOwner);
file.remove();
#endif
}
private:
const QString m_path;
};
class tst_QSaveFile : public QObject
{
Q_OBJECT
public slots:
private slots:
void transactionalWrite();
void retryTransactionalWrite();
void textStreamManualFlush();
void textStreamAutoFlush();
void saveTwice();
void transactionalWriteNoPermissionsOnDir_data();
void transactionalWriteNoPermissionsOnDir();
void transactionalWriteNoPermissionsOnFile();
void transactionalWriteCanceled();
void transactionalWriteErrorRenaming();
void symlink();
void directory();
#ifdef Q_OS_WIN
void alternateDataStream_data();
void alternateDataStream();
#endif
};
static inline QByteArray msgCannotOpen(const QFileDevice &f)
{
QString result = QStringLiteral("Cannot open ") + QDir::toNativeSeparators(f.fileName())
+ QStringLiteral(": ") + f.errorString();
return result.toLocal8Bit();
}
void tst_QSaveFile::transactionalWrite()
{
QTemporaryDir dir;
QVERIFY2(dir.isValid(), qPrintable(dir.errorString()));
const QString targetFile = dir.path() + QString::fromLatin1("/outfile");
QFile::remove(targetFile);
QSaveFile file(targetFile);
QVERIFY2(file.open(QIODevice::WriteOnly), msgCannotOpen(file).constData());
QVERIFY(file.isOpen());
QCOMPARE(file.fileName(), targetFile);
QVERIFY(!QFile::exists(targetFile));
QCOMPARE(file.write("Hello"), Q_INT64_C(5));
QCOMPARE(file.error(), QFile::NoError);
QVERIFY(!QFile::exists(targetFile));
QVERIFY(file.commit());
QVERIFY(QFile::exists(targetFile));
QCOMPARE(file.fileName(), targetFile);
QFile reader(targetFile);
QVERIFY(reader.open(QIODevice::ReadOnly));
QCOMPARE(QString::fromLatin1(reader.readAll()), QString::fromLatin1("Hello"));
// check that permissions are the same as for QFile
const QString otherFile = dir.path() + QString::fromLatin1("/otherfile");
QFile::remove(otherFile);
QFile other(otherFile);
other.open(QIODevice::WriteOnly);
other.close();
QCOMPARE(QFile::permissions(targetFile), QFile::permissions(otherFile));
}
// QTBUG-77007: Simulate the case of an application with a loop prompting
// to retry saving on failure. Create a read-only file first (Unix only)
void tst_QSaveFile::retryTransactionalWrite()
{
#ifndef Q_OS_UNIX
QSKIP("This test is Unix only");
#else
// root can open the read-only file for writing...
if (geteuid() == 0)
QSKIP("This test does not work as the root user");
#endif
QTemporaryDir dir;
QVERIFY2(dir.isValid(), qPrintable(dir.errorString()));
QString targetFile = dir.path() + QLatin1String("/outfile");
const QString readOnlyName = targetFile + QLatin1String(".ro");
{
QFile readOnlyFile(readOnlyName);
QVERIFY2(readOnlyFile.open(QIODevice::WriteOnly), msgCannotOpen(readOnlyFile).constData());
readOnlyFile.write("Hello");
readOnlyFile.close();
auto permissions = readOnlyFile.permissions();
permissions &= ~(QFileDevice::WriteOwner | QFileDevice::WriteGroup | QFileDevice::WriteUser);
QVERIFY(readOnlyFile.setPermissions(permissions));
}
QSaveFile file(readOnlyName);
QVERIFY(!file.open(QIODevice::WriteOnly));
file.setFileName(targetFile);
QVERIFY2(file.open(QIODevice::WriteOnly), msgCannotOpen(file).constData());
QVERIFY(file.isOpen());
QCOMPARE(file.write("Hello"), Q_INT64_C(5));
QCOMPARE(file.error(), QFile::NoError);
QVERIFY(file.commit());
}
void tst_QSaveFile::saveTwice()
{
// Check that we can reuse a QSaveFile object
// (and test the case of an existing target file)
QTemporaryDir dir;
QVERIFY2(dir.isValid(), qPrintable(dir.errorString()));
const QString targetFile = dir.path() + QString::fromLatin1("/outfile");
QSaveFile file(targetFile);
QVERIFY2(file.open(QIODevice::WriteOnly), msgCannotOpen(file).constData());
QCOMPARE(file.write("Hello"), Q_INT64_C(5));
QVERIFY2(file.commit(), qPrintable(file.errorString()));
QVERIFY2(file.open(QIODevice::WriteOnly), msgCannotOpen(file).constData());
QCOMPARE(file.write("World"), Q_INT64_C(5));
QVERIFY2(file.commit(), qPrintable(file.errorString()));
QFile reader(targetFile);
QVERIFY2(reader.open(QIODevice::ReadOnly), msgCannotOpen(reader).constData());
QCOMPARE(QString::fromLatin1(reader.readAll()), QString::fromLatin1("World"));
}
void tst_QSaveFile::textStreamManualFlush()
{
QTemporaryDir dir;
QVERIFY2(dir.isValid(), qPrintable(dir.errorString()));
const QString targetFile = dir.path() + QString::fromLatin1("/outfile");
QSaveFile file(targetFile);
QVERIFY2(file.open(QIODevice::WriteOnly), msgCannotOpen(file).constData());
QTextStream ts(&file);
ts << "Manual flush";
ts.flush();
QCOMPARE(file.error(), QFile::NoError);
QVERIFY(!QFile::exists(targetFile));
QVERIFY(file.commit());
QFile reader(targetFile);
QVERIFY(reader.open(QIODevice::ReadOnly));
QCOMPARE(QString::fromLatin1(reader.readAll().constData()), QString::fromLatin1("Manual flush"));
QFile::remove(targetFile);
}
void tst_QSaveFile::textStreamAutoFlush()
{
QTemporaryDir dir;
QVERIFY2(dir.isValid(), qPrintable(dir.errorString()));
const QString targetFile = dir.path() + QString::fromLatin1("/outfile");
QSaveFile file(targetFile);
QVERIFY2(file.open(QIODevice::WriteOnly), msgCannotOpen(file).constData());
QTextStream ts(&file);
ts << "Auto-flush.";
// no flush
QVERIFY(file.commit()); // QIODevice::close will emit aboutToClose, which will flush the stream
QFile reader(targetFile);
QVERIFY(reader.open(QIODevice::ReadOnly));
QCOMPARE(QString::fromLatin1(reader.readAll().constData()), QString::fromLatin1("Auto-flush."));
QFile::remove(targetFile);
}
void tst_QSaveFile::transactionalWriteNoPermissionsOnDir_data()
{
QTest::addColumn<bool>("directWriteFallback");
QTest::newRow("default") << false;
QTest::newRow("directWriteFallback") << true;
}
void tst_QSaveFile::transactionalWriteNoPermissionsOnDir()
{
#ifdef Q_OS_UNIX
#if !defined(Q_OS_VXWORKS)
if (::geteuid() == 0)
QSKIP("Test is not applicable with root privileges");
#endif
QFETCH(bool, directWriteFallback);
QTemporaryDir dir;
QVERIFY2(dir.isValid(), qPrintable(dir.errorString()));
QVERIFY(QFile(dir.path()).setPermissions(QFile::ReadOwner | QFile::ExeOwner));
PermissionRestorer permissionRestorer(dir.path());
const QString targetFile = dir.path() + QString::fromLatin1("/outfile");
QSaveFile firstTry(targetFile);
QVERIFY(!firstTry.open(QIODevice::WriteOnly));
QCOMPARE((int)firstTry.error(), (int)QFile::OpenError);
QVERIFY(!firstTry.commit());
// Now make an existing writable file
permissionRestorer.restore();
QFile f(targetFile);
QVERIFY(f.open(QIODevice::WriteOnly));
QCOMPARE(f.write("Hello"), Q_INT64_C(5));
f.close();
// Make the directory non-writable again
QVERIFY(QFile(dir.path()).setPermissions(QFile::ReadOwner | QFile::ExeOwner));
// And write to it again using QSaveFile; only works if directWriteFallback is enabled
QSaveFile file(targetFile);
file.setDirectWriteFallback(directWriteFallback);
QCOMPARE(file.directWriteFallback(), directWriteFallback);
if (directWriteFallback) {
QVERIFY2(file.open(QIODevice::WriteOnly), msgCannotOpen(file).constData());
QCOMPARE((int)file.error(), (int)QFile::NoError);
QCOMPARE(file.write("World"), Q_INT64_C(5));
QVERIFY(file.commit());
QFile reader(targetFile);
QVERIFY(reader.open(QIODevice::ReadOnly));
QCOMPARE(QString::fromLatin1(reader.readAll()), QString::fromLatin1("World"));
reader.close();
QVERIFY2(file.open(QIODevice::WriteOnly), msgCannotOpen(file).constData());
QCOMPARE((int)file.error(), (int)QFile::NoError);
QCOMPARE(file.write("W"), Q_INT64_C(1));
file.cancelWriting(); // no effect, as per the documentation
QVERIFY(file.commit());
QVERIFY(reader.open(QIODevice::ReadOnly));
QCOMPARE(QString::fromLatin1(reader.readAll()), QString::fromLatin1("W"));
} else {
QVERIFY(!file.open(QIODevice::WriteOnly));
QCOMPARE((int)file.error(), (int)QFile::OpenError);
}
#endif
}
void tst_QSaveFile::transactionalWriteNoPermissionsOnFile()
{
#if defined(Q_OS_UNIX) && !defined(Q_OS_VXWORKS)
if (::geteuid() == 0)
QSKIP("Test is not applicable with root privileges");
#endif
// Setup an existing but readonly file
QTemporaryDir dir;
QVERIFY2(dir.isValid(), qPrintable(dir.errorString()));
const QString targetFile = dir.path() + QString::fromLatin1("/outfile");
QFile file(targetFile);
PermissionRestorer permissionRestorer(targetFile);
QVERIFY2(file.open(QIODevice::WriteOnly), msgCannotOpen(file).constData());
QCOMPARE(file.write("Hello"), Q_INT64_C(5));
file.close();
file.setPermissions(QFile::ReadOwner);
QVERIFY(!file.open(QIODevice::WriteOnly));
// Try saving into it
{
QSaveFile saveFile(targetFile);
QVERIFY(!saveFile.open(QIODevice::WriteOnly)); // just like QFile
}
QVERIFY(file.exists());
}
void tst_QSaveFile::transactionalWriteCanceled()
{
QTemporaryDir dir;
QVERIFY2(dir.isValid(), qPrintable(dir.errorString()));
const QString targetFile = dir.path() + QString::fromLatin1("/outfile");
QFile::remove(targetFile);
QSaveFile file(targetFile);
QVERIFY2(file.open(QIODevice::WriteOnly), msgCannotOpen(file).constData());
QTextStream ts(&file);
ts << "This writing operation will soon be canceled.\n";
ts.flush();
QCOMPARE(file.error(), QFile::NoError);
QVERIFY(!QFile::exists(targetFile));
// We change our mind, let's abort writing
file.cancelWriting();
QVERIFY(!file.commit());
QVERIFY(!QFile::exists(targetFile)); // temp file was discarded
QCOMPARE(file.fileName(), targetFile);
}
void tst_QSaveFile::transactionalWriteErrorRenaming()
{
#if defined(Q_OS_UNIX) && !defined(Q_OS_VXWORKS)
if (::geteuid() == 0)
QSKIP("Test is not applicable with root privileges");
#endif
QTemporaryDir dir;
QVERIFY2(dir.isValid(), qPrintable(dir.errorString()));
const QString targetFile = dir.path() + QString::fromLatin1("/outfile");
QSaveFile file(targetFile);
QVERIFY2(file.open(QIODevice::WriteOnly), msgCannotOpen(file).constData());
QCOMPARE(file.write("Hello"), qint64(5));
QVERIFY(!QFile::exists(targetFile));
#ifdef Q_OS_UNIX
// Make rename() fail for lack of permissions in the directory
QFile dirAsFile(dir.path()); // yay, I have to use QFile to change a dir's permissions...
QVERIFY(dirAsFile.setPermissions(QFile::Permissions{})); // no permissions
PermissionRestorer permissionRestorer(dir.path());
#else
// Windows: Make rename() fail for lack of permissions on an existing target file
QFile existingTargetFile(targetFile);
QVERIFY2(existingTargetFile.open(QIODevice::WriteOnly), msgCannotOpen(existingTargetFile).constData());
QCOMPARE(file.write("Target"), qint64(6));
existingTargetFile.close();
QVERIFY(existingTargetFile.setPermissions(QFile::ReadOwner));
PermissionRestorer permissionRestorer(targetFile);
#endif
// The saving should fail.
QVERIFY(!file.commit());
#ifdef Q_OS_UNIX
QVERIFY(!QFile::exists(targetFile)); // renaming failed
#endif
QCOMPARE(file.error(), QFile::RenameError);
}
void tst_QSaveFile::symlink()
{
#ifdef Q_OS_UNIX
QByteArray someData = "some data";
QTemporaryDir dir;
QVERIFY2(dir.isValid(), qPrintable(dir.errorString()));
const QString targetFile = dir.path() + QLatin1String("/outfile");
const QString linkFile = dir.path() + QLatin1String("/linkfile");
{
QFile file(targetFile);
QVERIFY2(file.open(QIODevice::WriteOnly), msgCannotOpen(file).constData());
QCOMPARE(file.write("Hello"), Q_INT64_C(5));
file.close();
}
QVERIFY(QFile::link(targetFile, linkFile));
QString canonical = QFileInfo(linkFile).canonicalFilePath();
QCOMPARE(canonical, QFileInfo(targetFile).canonicalFilePath());
// Try saving into it
{
QSaveFile saveFile(linkFile);
QVERIFY(saveFile.open(QIODevice::WriteOnly));
QCOMPARE(saveFile.write(someData), someData.size());
saveFile.commit();
//Check that the linkFile is still a link and still has the same canonical path
QFileInfo info(linkFile);
QVERIFY(info.isSymLink());
QCOMPARE(QFileInfo(linkFile).canonicalFilePath(), canonical);
QFile file(targetFile);
QVERIFY2(file.open(QIODevice::ReadOnly), msgCannotOpen(file).constData());
QCOMPARE(file.readAll(), someData);
file.remove();
}
// Save into a symbolic link that point to a removed file
someData = "more stuff";
{
QSaveFile saveFile(linkFile);
QVERIFY(saveFile.open(QIODevice::WriteOnly));
QCOMPARE(saveFile.write(someData), someData.size());
saveFile.commit();
QFileInfo info(linkFile);
QVERIFY(info.isSymLink());
QCOMPARE(QFileInfo(linkFile).canonicalFilePath(), canonical);
QFile file(targetFile);
QVERIFY2(file.open(QIODevice::ReadOnly), msgCannotOpen(file).constData());
QCOMPARE(file.readAll(), someData);
}
// link to a link in another directory
QTemporaryDir dir2;
QVERIFY2(dir2.isValid(), qPrintable(dir2.errorString()));
const QString linkFile2 = dir2.path() + QLatin1String("/linkfile");
QVERIFY(QFile::link(linkFile, linkFile2));
QCOMPARE(QFileInfo(linkFile2).canonicalFilePath(), canonical);
someData = "hello everyone";
{
QSaveFile saveFile(linkFile2);
QVERIFY(saveFile.open(QIODevice::WriteOnly));
QCOMPARE(saveFile.write(someData), someData.size());
saveFile.commit();
QFile file(targetFile);
QVERIFY2(file.open(QIODevice::ReadOnly), msgCannotOpen(file).constData());
QCOMPARE(file.readAll(), someData);
}
//cyclic link
const QString cyclicLink = dir.path() + QLatin1String("/cyclic");
QVERIFY(QFile::link(cyclicLink, cyclicLink));
{
QSaveFile saveFile(cyclicLink);
QVERIFY(saveFile.open(QIODevice::WriteOnly));
QCOMPARE(saveFile.write(someData), someData.size());
saveFile.commit();
QFile file(cyclicLink);
QVERIFY2(file.open(QIODevice::ReadOnly), msgCannotOpen(file).constData());
QCOMPARE(file.readAll(), someData);
}
//cyclic link2
QVERIFY(QFile::link(cyclicLink + QLatin1Char('1'), cyclicLink + QLatin1Char('2')));
QVERIFY(QFile::link(cyclicLink + QLatin1Char('2'), cyclicLink + QLatin1Char('1')));
{
QSaveFile saveFile(cyclicLink + QLatin1Char('1'));
QVERIFY(saveFile.open(QIODevice::WriteOnly));
QCOMPARE(saveFile.write(someData), someData.size());
saveFile.commit();
// the explicit file becomes a file instead of a link
QVERIFY(!QFileInfo(cyclicLink + QLatin1Char('1')).isSymLink());
QVERIFY(QFileInfo(cyclicLink + QLatin1Char('2')).isSymLink());
QFile file(cyclicLink + QLatin1Char('1'));
QVERIFY2(file.open(QIODevice::ReadOnly), msgCannotOpen(file).constData());
QCOMPARE(file.readAll(), someData);
}
#endif
}
void tst_QSaveFile::directory()
{
QTemporaryDir dir;
QVERIFY2(dir.isValid(), qPrintable(dir.errorString()));
const QString subdir = dir.path() + QLatin1String("/subdir");
QVERIFY(QDir(dir.path()).mkdir(QStringLiteral("subdir")));
{
QFile sf(subdir);
QVERIFY(!sf.open(QIODevice::WriteOnly));
}
#ifdef Q_OS_UNIX
//link to a directory
const QString linkToDir = dir.path() + QLatin1String("/linkToDir");
QVERIFY(QFile::link(subdir, linkToDir));
{
QFile sf(linkToDir);
QVERIFY(!sf.open(QIODevice::WriteOnly));
}
#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"