From 7ca82250e01cea6a270796beb2fa21de20b9b952 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Wed, 2 Aug 2023 13:58:46 -0700 Subject: [PATCH] tst_QFile: update virtualFile() to check if readLine() reads all We know that all lines from /proc//maps end in a newline, so we trim exactly that one byte, then we put it all back together with .join('\n') to check if we've read the entire file. Linux virtual files are usually served in 4 kB increments; tst_qfile's maps file is about 16000 bytes for me, just short of the QIODevice buffer: [pid 414315] read(5, "55c6afe04000-55c6afe11000 r--p 0"..., 16384) = 4049 [pid 414315] read(5, "7f215fd25000-7f215fd26000 r--p 0"..., 12335) = 4038 [pid 414315] read(5, "7f2160800000-7f21608c7000 r--p 0"..., 8297) = 4072 [pid 414315] read(5, "7f216119f000-7f21611a0000 rw-p 0"..., 4225) = 3994 It is not a coincidence that the reads are at line boundaries, though it's not a guarantee from the kernel. We appear to have accidentally fixed the QEMU emulation bug by reading another process' /proc//maps (hypothesis: QMU emulates the target system in /proc/self, hiding itself in maps, but makes no translation for other process map files. Change-Id: Ifbf974a4d10745b099b1fffd1777ac919b12dc90 Reviewed-by: Qt CI Bot Reviewed-by: Fabian Kosmale --- tests/auto/corelib/io/qfile/tst_qfile.cpp | 50 ++++++++++++++++++----- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/tests/auto/corelib/io/qfile/tst_qfile.cpp b/tests/auto/corelib/io/qfile/tst_qfile.cpp index bd83aff141..1c53a2fa1b 100644 --- a/tests/auto/corelib/io/qfile/tst_qfile.cpp +++ b/tests/auto/corelib/io/qfile/tst_qfile.cpp @@ -52,7 +52,9 @@ QT_END_NAMESPACE #ifdef Q_OS_DARWIN # include #elif defined(Q_OS_LINUX) +# include # include +# include #elif defined(Q_OS_FREEBSD) # include # include @@ -2613,8 +2615,29 @@ void tst_QFile::writeToReadOnlyFile() // This platform have 0-sized virtual files void tst_QFile::virtualFile() { - // test if QFile works with virtual files - QString fname = "/proc/self/maps"; + // We need to test a large-ish /proc file on Linux, one that is usually + // over 4 kB (because the kernel writes in chunks of that), has a + // cross-platform file format, and is definitely readable. The best + // candidate and the one we can verify anything in is /proc//maps. + // However, our act of reading may change the map because we allocate + // memory, so we fork() here so we have a frozen snapshot of the file. + + int efd = eventfd(0, EFD_CLOEXEC); + pid_t pid = fork(); + if (pid == 0) { + // child + uint64_t val; + eventfd_read(efd, &val); + _exit(0); + } + QVERIFY2(pid > 0, "fork failed: " + qt_error_string().toLocal8Bit()); + auto waitForChild = qScopeGuard([=] { + eventfd_write(efd, 1); + close(efd); + waitpid(pid, nullptr, 0); + }); + + QString fname = u"/proc/%1/maps"_s.arg(pid); // consistency check QFileInfo fi(fname); @@ -2625,11 +2648,7 @@ void tst_QFile::virtualFile() // open the file QFile f(fname); QVERIFY2(f.open(QIODevice::ReadOnly), msgOpenFailed(f).constData()); - if (QTestPrivate::isRunningArmOnX86()) - QEXPECT_FAIL("","QEMU does not read /proc/self/maps size correctly", Continue); QCOMPARE(f.size(), Q_INT64_C(0)); - if (QTestPrivate::isRunningArmOnX86()) - QEXPECT_FAIL("","QEMU does not read /proc/self/maps size correctly", Continue); QVERIFY(f.atEnd()); // read data @@ -2637,18 +2656,27 @@ void tst_QFile::virtualFile() QCOMPARE(data.size(), 16); QCOMPARE(f.pos(), Q_INT64_C(16)); + // seeking + QVERIFY(f.seek(1)); + QCOMPARE(f.pos(), Q_INT64_C(1)); + QVERIFY(f.seek(0)); + QCOMPARE(f.pos(), Q_INT64_C(0)); + // line-reading - data = f.readLine(); - QVERIFY(!data.isEmpty()); + QList lines; + for (data = f.readLine(); !data.isEmpty(); data = f.readLine()) { + // chop the newline -- not using .trimmed() so cut exactly one byte + data.chop(1); + lines += std::move(data); + } // read all: + QVERIFY(f.seek(0)); data = f.readAll(); QVERIFY(f.pos() != 0); QVERIFY(!data.isEmpty()); - // seeking - QVERIFY(f.seek(1)); - QCOMPARE(f.pos(), Q_INT64_C(1)); + QCOMPARE(data, lines.join('\n') + '\n'); } #endif // defined(Q_OS_LINUX) || defined(Q_OS_AIX) || defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)