File indexing completed on 2024-04-21 03:53:39

0001 /*
0002     This file is part of the KDE project
0003     SPDX-FileCopyrightText: 2019 David Hallas <david@davidhallas.dk>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-only
0006 */
0007 
0008 #include "klistopenfilesjobtest_unix.h"
0009 #include "klistopenfilesjob.h"
0010 #include <QCoreApplication>
0011 #include <QStandardPaths>
0012 #include <QStringLiteral>
0013 #include <QTemporaryDir>
0014 #include <QTest>
0015 #include <algorithm>
0016 
0017 #ifdef Q_OS_FREEBSD
0018 // See implementation note in testOpenFiles()
0019 #include <QProcess>
0020 #endif
0021 
0022 QTEST_MAIN(KListOpenFilesJobTest)
0023 void initLocale()
0024 {
0025     qputenv("LC_ALL", "en_US.utf-8");
0026 }
0027 
0028 Q_CONSTRUCTOR_FUNCTION(initLocale)
0029 
0030 namespace
0031 {
0032 bool hasLsofInstalled()
0033 {
0034     return !QStandardPaths::findExecutable(QStringLiteral("lsof")).isEmpty();
0035 }
0036 
0037 }
0038 
0039 void KListOpenFilesJobTest::testOpenFiles()
0040 {
0041     if (!hasLsofInstalled()) {
0042         QSKIP("lsof is not installed - skipping test");
0043     }
0044 
0045     // Create a file and hold it open, so that lsof must report us
0046     QTemporaryDir tempDir;
0047     QFile tempFile(tempDir.path() + QStringLiteral("/file"));
0048     QVERIFY(tempFile.open(QIODevice::WriteOnly));
0049     bool xfail_zfs = false; // Expected failure because of ZFS
0050 #ifdef Q_OS_FREEBSD
0051     // FIXME: On FreeBSD, lsof does not support zfs (as of 2022), see
0052     // https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=253553
0053     //
0054     // This affects regular files only. So for FreeBSD, check if
0055     // the tempDir seems to be on a ZFS filesystem, e.g.
0056     //
0057     // ```
0058     // [adridg@beastie .../invent/kcoreaddons]$ lsof +d /tmp > /dev/null
0059     // lsof: WARNING: no ZFS support has been defined.
0060     //   See 00FAQ for more information.
0061     // ```
0062     {
0063         QProcess lsof;
0064         lsof.start(QStringLiteral("lsof"), {QStringLiteral("+d"), tempDir.path()});
0065         lsof.waitForFinished();
0066 
0067         auto stderr = lsof.readAllStandardError();
0068         xfail_zfs = (lsof.exitCode() != 0) && stderr.contains("ZFS");
0069     }
0070 #endif
0071 
0072     auto job = new KListOpenFilesJob(tempDir.path());
0073     QVERIFY2(job->exec(), qPrintable(job->errorString()));
0074     QCOMPARE(job->error(), KJob::NoError);
0075     auto processInfoList = job->processInfoList();
0076     if (xfail_zfs) {
0077         // Ths list is empty, so the subsequent find and validity-checks
0078         // don't make sense.
0079         QEXPECT_FAIL("", "lsof(8) does not support regular files on ZFS", Abort);
0080     }
0081     QVERIFY(!processInfoList.empty());
0082     auto testProcessIterator = std::find_if(processInfoList.begin(), processInfoList.end(), [](const KProcessList::KProcessInfo &info) {
0083         return info.pid() == QCoreApplication::applicationPid();
0084     });
0085     QVERIFY(testProcessIterator != processInfoList.end());
0086     const auto &processInfo = *testProcessIterator;
0087     QVERIFY(processInfo.isValid());
0088     QCOMPARE(processInfo.pid(), QCoreApplication::applicationPid());
0089 }
0090 
0091 void KListOpenFilesJobTest::testNoOpenFiles()
0092 {
0093     if (!hasLsofInstalled()) {
0094         QSKIP("lsof is not installed - skipping test");
0095     }
0096     QTemporaryDir tempDir;
0097     auto job = new KListOpenFilesJob(tempDir.path());
0098     QVERIFY2(job->exec(), qPrintable(job->errorString()));
0099     QCOMPARE(job->error(), KJob::NoError);
0100     QVERIFY(job->processInfoList().empty());
0101 }
0102 
0103 void KListOpenFilesJobTest::testNonExistingDir()
0104 {
0105     if (!hasLsofInstalled()) {
0106         QSKIP("lsof is not installed - skipping test");
0107     }
0108     QString nonExistingDir(QStringLiteral("/does/not/exist"));
0109     auto job = new KListOpenFilesJob(nonExistingDir);
0110     QVERIFY(!job->exec());
0111     QCOMPARE(job->error(), static_cast<int>(KListOpenFilesJob::Error::DoesNotExist));
0112     QCOMPARE(job->errorText(), QStringLiteral("Path %1 doesn't exist").arg(nonExistingDir));
0113     QVERIFY(job->processInfoList().empty());
0114 }
0115 
0116 /**
0117  * @brief Helper class to temporarily set an environment variable and reset it on destruction
0118  */
0119 class ScopedEnvVariable
0120 {
0121 public:
0122     ScopedEnvVariable(const QLatin1String &Name, const QByteArray &NewValue)
0123         : name(Name)
0124         , originalValue(qgetenv(name.latin1()))
0125     {
0126         qputenv(name.latin1(), NewValue);
0127     }
0128     ~ScopedEnvVariable()
0129     {
0130         qputenv(name.latin1(), originalValue);
0131     }
0132 
0133 private:
0134     const QLatin1String name;
0135     const QByteArray originalValue;
0136 };
0137 
0138 void KListOpenFilesJobTest::testLsofNotFound()
0139 {
0140     // This test relies on clearing the PATH variable so that lsof is not found
0141     ScopedEnvVariable emptyPathEnvironment(QLatin1String("PATH"), QByteArray());
0142     QDir path(QCoreApplication::applicationDirPath());
0143     auto job = new KListOpenFilesJob(path.path());
0144     QVERIFY(!job->exec());
0145     QCOMPARE(job->error(), static_cast<int>(KListOpenFilesJob::Error::InternalError));
0146     QVERIFY(job->processInfoList().empty());
0147 }
0148 
0149 #include "moc_klistopenfilesjobtest_unix.cpp"