tst_QStorageInfo: try harder to make the free space change
This merges the tempFile and caching tests, which had been updated in commit ae03ffaffdcc8b2a0589b846e16ad016691dec29 to attempt other filesystems than APFS and btrfs. Instead of adding yet another item to the list (xfs), let's insist by writing more and flushing the OS buffers. Local testing says that btrfs usually updates after the first write(), no later than the second, and that APFS and XFS "just works" now (without the fsync() even). Fixes: QTBUG-123151 Change-Id: I6818d78a57394e37857bfffd17bbe7427307efc4 Reviewed-by: Ahmad Samir <a.samirh78@gmail.com> (cherry picked from commit 025c45d628ae42989f50df10ca6c75ad1007614f) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
parent
532e562b91
commit
571c71407d
@ -10,6 +10,13 @@
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
# include <io.h> // _get_osfhandle
|
||||
# include <windows.h>
|
||||
#else
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "../../../../manual/qstorageinfo/printvolumes.cpp"
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
@ -28,8 +35,7 @@ private slots:
|
||||
void currentStorage();
|
||||
void storageList_data();
|
||||
void storageList();
|
||||
void tempFile();
|
||||
void caching();
|
||||
void freeSpaceUpdate();
|
||||
|
||||
#if defined(Q_OS_LINUX) && defined(QT_BUILD_INTERNAL)
|
||||
void testParseMountInfo_data();
|
||||
@ -207,80 +213,88 @@ void tst_QStorageInfo::storageList()
|
||||
QCOMPARE(other.isReady(), storage.isReady());
|
||||
}
|
||||
|
||||
static bool checkFilesystemGoodForWriting(QTemporaryFile &file, QStorageInfo &storage)
|
||||
static QString suitableDirectoryForWriting()
|
||||
{
|
||||
std::initializer_list<const char *> inadvisableFs = {
|
||||
#ifdef Q_OS_LINUX
|
||||
auto reconstructAt = [](auto *where, auto &&... how) {
|
||||
// it's very difficult to convince QTemporaryFile to change the path...
|
||||
std::destroy_at(where);
|
||||
q20::construct_at(where, std::forward<decltype(how)>(how)...);
|
||||
};
|
||||
if (storage.fileSystemType() == "btrfs") {
|
||||
// let's see if we can find another, writable FS
|
||||
QString runtimeDir = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
|
||||
if (!runtimeDir.isEmpty()) {
|
||||
reconstructAt(&file, runtimeDir + "/XXXXXX");
|
||||
if (file.open()) {
|
||||
storage.setPath(file.fileName());
|
||||
if (storage.fileSystemType() != "btrfs")
|
||||
return true;
|
||||
}
|
||||
}
|
||||
QTest::qSkip("btrfs does not synchronously update free space; this test would fail",
|
||||
__FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
#elif defined(Q_OS_DARWIN)
|
||||
Q_UNUSED(file);
|
||||
if (storage.fileSystemType() == "apfs") {
|
||||
QTest::qSkip("APFS does not synchronously update free space; this test would fail",
|
||||
__FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
Q_UNUSED(file);
|
||||
Q_UNUSED(storage);
|
||||
// See comment below. If we can get a tmpfs, let's try it.
|
||||
"btrfs",
|
||||
"xfs",
|
||||
#endif
|
||||
return true;
|
||||
};
|
||||
|
||||
QString tempDir = QDir::tempPath();
|
||||
QString fsType = QStorageInfo(tempDir).fileSystemType();
|
||||
if (std::find(std::begin(inadvisableFs), std::end(inadvisableFs), fsType)
|
||||
!= std::end(inadvisableFs)) {
|
||||
// the RuntimeLocation on Linux is almost always a tmpfs
|
||||
QString runtimeDir = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
|
||||
if (!runtimeDir.isEmpty())
|
||||
return runtimeDir;
|
||||
}
|
||||
|
||||
return tempDir;
|
||||
}
|
||||
|
||||
void tst_QStorageInfo::tempFile()
|
||||
void tst_QStorageInfo::freeSpaceUpdate()
|
||||
{
|
||||
QTemporaryFile file;
|
||||
QVERIFY2(file.open(), qPrintable(file.errorString()));
|
||||
// Some filesystems don't update the free space unless we ask that the OS
|
||||
// flush its buffers to disk and even then the update may not be entirely
|
||||
// synchronous. So we always ask the OS to flush them and we may keep
|
||||
// trying to write until the free space changes (with a maximum so we don't
|
||||
// exhaust and cause other problems).
|
||||
//
|
||||
// In the past, we had this issue with APFS (Apple systems), BTRFS and XFS
|
||||
// (Linux). Current testing is that APFS and XFS always succeed after the
|
||||
// first block is written and BTRFS almost always by the second block.
|
||||
|
||||
QStorageInfo storage1(file.fileName());
|
||||
if (!checkFilesystemGoodForWriting(file, storage1))
|
||||
return;
|
||||
|
||||
qint64 free = storage1.bytesFree();
|
||||
QCOMPARE_NE(free, -1);
|
||||
|
||||
file.write(QByteArray(1024*1024, '1'));
|
||||
auto flushAndSync = [](QFile &file) {
|
||||
file.flush();
|
||||
file.close();
|
||||
|
||||
QStorageInfo storage2(file.fileName());
|
||||
QCOMPARE_NE(free, storage2.bytesFree());
|
||||
}
|
||||
#ifdef Q_OS_WIN
|
||||
FlushFileBuffers(HANDLE(_get_osfhandle(file.handle())));
|
||||
#elif _POSIX_VERSION >= 200112L
|
||||
fsync(file.handle());
|
||||
sync();
|
||||
#endif
|
||||
};
|
||||
|
||||
void tst_QStorageInfo::caching()
|
||||
{
|
||||
QTemporaryFile file;
|
||||
QTemporaryFile file(suitableDirectoryForWriting() + "/tst_qstorageinfo.XXXXXX");
|
||||
QVERIFY2(file.open(), qPrintable(file.errorString()));
|
||||
|
||||
QStorageInfo storage1(file.fileName());
|
||||
if (!checkFilesystemGoodForWriting(file, storage1))
|
||||
return;
|
||||
qInfo() << "Testing on" << storage1;
|
||||
|
||||
qint64 free = storage1.bytesFree();
|
||||
QStorageInfo storage2(storage1);
|
||||
QCOMPARE(free, storage2.bytesFree());
|
||||
QCOMPARE_NE(free, -1);
|
||||
|
||||
file.write(QByteArray(1024*1024, '\0'));
|
||||
file.flush();
|
||||
// let's see if we can make it change
|
||||
QByteArray block(1024 * 1024 / 2, '\0');
|
||||
|
||||
// let's try and keep to less than ~10% of the free disk space
|
||||
int maxIterations = 25;
|
||||
if (free < 256 * block.size())
|
||||
maxIterations = free / 10 / block.size();
|
||||
if (maxIterations == 0)
|
||||
QSKIP("Not enough free disk space to continue");
|
||||
|
||||
file.write(block);
|
||||
flushAndSync(file);
|
||||
for (int i = 0; i < maxIterations; ++i) {
|
||||
QStorageInfo storage3(file.fileName());
|
||||
qint64 nowFree = storage3.bytesFree();
|
||||
if (nowFree != free)
|
||||
break;
|
||||
|
||||
// grow some more
|
||||
file.write(block);
|
||||
flushAndSync(file);
|
||||
}
|
||||
// qDebug() << "Needed to grow" << file.fileName() << "to" << file.size();
|
||||
|
||||
QCOMPARE(storage1, storage2);
|
||||
QCOMPARE(free, storage1.bytesFree());
|
||||
QCOMPARE(free, storage2.bytesFree());
|
||||
storage2.refresh();
|
||||
|
Loading…
x
Reference in New Issue
Block a user