File indexing completed on 2024-03-24 17:10:30

0001 // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0002 // SPDX-FileCopyrightText: 2020-2021 Harald Sitter <sitter@kde.org>
0003 
0004 #include <QDebug>
0005 #include <QDir>
0006 #include <QFile>
0007 #include <QJsonDocument>
0008 #include <QObject>
0009 #include <QSignalSpy>
0010 #include <QStandardPaths>
0011 #include <QTest>
0012 
0013 #include <functional>
0014 
0015 #include "devicenotifier.h"
0016 #include <device.h>
0017 #include <smartctl.h>
0018 #include <smartmonitor.h>
0019 
0020 class SMARTMonitorTest : public QObject
0021 {
0022     Q_OBJECT
0023 
0024 private Q_SLOTS:
0025     void testRun()
0026     {
0027         struct Ctl : public AbstractSMARTCtl {
0028             static QString readCLIData()
0029             {
0030                 QFile file(QFINDTESTDATA("fixtures/cli.txt"));
0031                 const bool open = file.open(QFile::ReadOnly);
0032                 Q_ASSERT(open);
0033                 return file.readAll();
0034             }
0035 
0036             void run(const QString &devicePath) override
0037             {
0038                 static QMap<QString, QString> data{{"/dev/testfoobarpass", "fixtures/pass.json"},
0039                                                    {"/dev/invalid-vbox.json", "fixtures/invalid-vbox.json"},
0040                                                    {"/dev/testfoobarfail", "fixtures/fail.json"},
0041                                                    {"/dev/invalid-cmdline-bad-usb-bridge", "fixtures/invalid-cmdline-bad-usb-bridge.json"}};
0042 
0043                 const QString fixture = data.value(devicePath);
0044                 Q_ASSERT(!fixture.isEmpty());
0045                 QFile file(QFINDTESTDATA(fixture));
0046                 const bool open = file.open(QFile::ReadOnly);
0047                 Q_ASSERT(open);
0048                 QJsonParseError err{};
0049                 const auto document = QJsonDocument::fromJson(file.readAll(), &err);
0050                 Q_ASSERT(err.error == QJsonParseError::NoError);
0051 
0052                 static QString cliData = readCLIData();
0053                 Q_EMIT finished(devicePath, document, cliData);
0054             }
0055         };
0056 
0057         struct Notifier : public DeviceNotifier {
0058             using DeviceNotifier::DeviceNotifier;
0059             void start() override
0060             {
0061                 loadData();
0062             }
0063             void loadData() override
0064             {
0065                 Q_EMIT addDevice(new Device{"udi-pass", "product", "/dev/testfoobarpass"});
0066                 Q_EMIT addDevice(new Device{"udi-invalid", "product", "/dev/invalid-vbox.json"});
0067                 Q_EMIT addDevice(new Device{"udi-invalid-cmdline-bad-usb-bridge", "product", "/dev/invalid-cmdline-bad-usb-bridge"});
0068                 // discover this twice to ensure notifications aren't duplicated!
0069                 Q_EMIT addDevice(new Device{"udi-fail", "product", "/dev/testfoobarfail"});
0070                 Q_EMIT addDevice(new Device{"udi-fail", "product", "/dev/testfoobarfail"});
0071             }
0072         };
0073 
0074         SMARTMonitor monitor(std::make_unique<Ctl>(), std::make_unique<Notifier>());
0075         QSignalSpy spy(&monitor, &SMARTMonitor::deviceAdded);
0076         QVERIFY(spy.isValid());
0077         monitor.start();
0078         // The signals are all emitted in one go and as such should arrive
0079         // within a single wait.
0080         QVERIFY(spy.wait());
0081         QCOMPARE(spy.count(), 2); // There are 3 devices but one is a dupe.
0082         QCOMPARE(monitor.devices().count(), 2); // There are 3 devices but one is a dupe.
0083 
0084         bool sawPass = false;
0085         bool sawInvalid = false;
0086         bool sawFail = false;
0087         bool sawInvalidCmdLine = false;
0088         for (const auto *device : monitor.devices()) {
0089             if (device->path() == "/dev/testfoobarpass") {
0090                 QVERIFY(!device->failed());
0091                 sawPass = true;
0092             }
0093             if (device->path() == "/dev/invalid") {
0094                 sawInvalid = true;
0095             }
0096             if (device->path() == "/dev/invalid-cmdline-bad-usb-bridge") {
0097                 sawInvalidCmdLine = true;
0098             }
0099             if (device->path() == "/dev/testfoobarfail") {
0100                 QVERIFY(device->failed());
0101                 sawFail = true;
0102             }
0103         }
0104         QVERIFY(sawPass);
0105         QVERIFY(!sawInvalid); // mustn't be seen, it's an invalid device ;)
0106         QVERIFY(!sawInvalidCmdLine); // ditto
0107         QVERIFY(sawFail);
0108 
0109         // Ensure removing works as well.
0110         // https://bugs.kde.org/show_bug.cgi?id=428746
0111 
0112         monitor.removeUDI("udi-pass");
0113         QCOMPARE(monitor.devices().size(), 1);
0114     }
0115 };
0116 
0117 QTEST_MAIN(SMARTMonitorTest)
0118 
0119 #include "smartmonitortest.moc"