File indexing completed on 2024-10-27 13:03:22
0001 /* 0002 SPDX-FileCopyrightText: 2008 Kevin Ottens <ervin@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0005 */ 0006 0007 #include <QObject> 0008 #include <QThread> 0009 #include <QThreadPool> 0010 #include <qtconcurrentrun.h> 0011 0012 #include <QTest> 0013 0014 #include <solid/device.h> 0015 #include <solid/genericinterface.h> 0016 #include <solid/predicate.h> 0017 #include <solid/storagedrive.h> 0018 #include <solid/storagevolume.h> 0019 0020 class SolidMtTest : public QObject 0021 { 0022 Q_OBJECT 0023 private Q_SLOTS: 0024 void testWorkerThread(); 0025 void testThreadedPredicate(); 0026 void testTextPredicates_data(); 0027 void testTextPredicates(); 0028 void testTextPredicatesExtended(); 0029 void testDeviceMatching(); 0030 }; 0031 0032 class WorkerThread : public QThread 0033 { 0034 Q_OBJECT 0035 protected: 0036 void run() override 0037 { 0038 Solid::Device dev("/org/freedesktop/Hal/devices/computer"); 0039 0040 const QList<Solid::Device> driveList = Solid::Device::listFromType(Solid::DeviceInterface::StorageDrive); 0041 for (const Solid::Device &solidDevice : driveList) { 0042 const Solid::StorageDrive *solidDrive = solidDevice.as<Solid::StorageDrive>(); 0043 Q_ASSERT(solidDrive); 0044 Q_UNUSED(solidDrive); 0045 } 0046 } 0047 }; 0048 0049 static void doPredicates() 0050 { 0051 Solid::Predicate p5 = 0052 Solid::Predicate::fromString("[[Processor.maxSpeed == 3201 AND Processor.canChangeFrequency == false] OR StorageVolume.mountPoint == '/media/blup']"); 0053 0054 Solid::Predicate p6 = Solid::Predicate::fromString("StorageVolume.usage == 'Other'"); 0055 Solid::Predicate p7 = Solid::Predicate::fromString(QString("StorageVolume.usage == %1").arg((int)Solid::StorageVolume::Other)); 0056 Solid::Predicate p8 = Solid::Predicate::fromString("StorageVolume.ignored == false"); 0057 } 0058 0059 QTEST_MAIN(SolidMtTest) 0060 0061 void SolidMtTest::testWorkerThread() 0062 { 0063 Solid::Device dev("/org/freedesktop/Hal/devices/acpi_ADP1"); 0064 0065 WorkerThread *wt = new WorkerThread; 0066 wt->start(); 0067 wt->wait(); 0068 0069 const QList<Solid::Device> driveList = Solid::Device::listFromType(Solid::DeviceInterface::StorageDrive); 0070 for (const Solid::Device &solidDevice : driveList) { 0071 const Solid::GenericInterface *solidDrive = solidDevice.as<Solid::GenericInterface>(); 0072 Q_ASSERT(solidDrive); 0073 Q_UNUSED(solidDrive); 0074 } 0075 0076 delete wt; 0077 } 0078 0079 void SolidMtTest::testThreadedPredicate() 0080 { 0081 QThreadPool::globalInstance()->setMaxThreadCount(10); 0082 const QList<QFuture<void>> futures = { 0083 QtConcurrent::run(&doPredicates), 0084 QtConcurrent::run(&doPredicates), 0085 QtConcurrent::run(&doPredicates), 0086 QtConcurrent::run(&doPredicates), 0087 QtConcurrent::run(&doPredicates), 0088 QtConcurrent::run(&doPredicates), 0089 QtConcurrent::run(&doPredicates), 0090 QtConcurrent::run(&doPredicates), 0091 }; 0092 for (QFuture<void> f : futures) { 0093 f.waitForFinished(); 0094 } 0095 QThreadPool::globalInstance()->setMaxThreadCount(1); // delete those threads 0096 } 0097 0098 0099 void SolidMtTest::testTextPredicates_data() 0100 { 0101 QTest::addColumn<QString>("pstring"); 0102 QTest::addColumn<bool>("isValidPredicate"); 0103 0104 QTest::newRow("empty") << QString() << false; 0105 QTest::newRow("simple") << QStringLiteral("@") << true; 0106 QTest::newRow("and") << QStringLiteral("@ AND @") << false; 0107 QTest::newRow("[and]") << QStringLiteral("[@ AND @]") << true; 0108 QTest::newRow("[3and]") << QStringLiteral("[@ AND @ AND @]") << false; 0109 QTest::newRow("[[and]and]") << QStringLiteral("[[@ AND @] AND @]") << true; 0110 QTest::newRow("[and[and]]") << QStringLiteral("[@ AND [@ AND @]]") << true; 0111 QTest::newRow("[[and][and] (unbalance)") << QStringLiteral("[ [@ AND @] AND [@ AND @]") << false; 0112 QTest::newRow("[[and][and]]") << QStringLiteral("[ [@ AND @] AND [@ AND @] ]") << true; 0113 QTest::newRow("[[[and]and]and]") << QStringLiteral("[ [ [ @ AND @] AND @ ] AND @]") << true; 0114 } 0115 0116 /** @brief Check validity (and hence, parser-code) for a bunch of text predicates 0117 * 0118 */ 0119 void SolidMtTest::testTextPredicates() 0120 { 0121 QFETCH(QString, pstring); 0122 QFETCH(bool, isValidPredicate); 0123 0124 // The expressions contain @ as a placeholder, so they are 0125 // short enough to easily see the structure of the expression 0126 // in _data(); replace all those @s with an actual atom. 0127 const QString atom("StorageVolume.ignored == false"); 0128 while(pstring.contains('@')) 0129 { 0130 pstring = pstring.replace('@', atom); 0131 } 0132 0133 Solid::Predicate p = Solid::Predicate::fromString(pstring); 0134 QCOMPARE(p.isValid(), isValidPredicate); 0135 } 0136 0137 void SolidMtTest::testTextPredicatesExtended() 0138 { 0139 Solid::Predicate p = Solid::Predicate::fromString("[[StorageVolume.ignored == false AND StorageVolume.usage == 'FileSystem'] AND [StorageVolume.ignored == false AND StorageDrive.removable == true]]"); 0140 QVERIFY(p.isValid()); 0141 QCOMPARE(p.type(), Solid::Predicate::Conjunction); 0142 QCOMPARE(p.firstOperand().type(), Solid::Predicate::Conjunction); 0143 QCOMPARE(p.firstOperand().firstOperand().type(), Solid::Predicate::PropertyCheck); 0144 QCOMPARE(p.secondOperand().secondOperand().type(), Solid::Predicate::PropertyCheck); 0145 0146 QCOMPARE(p.firstOperand().firstOperand().propertyName(), QStringLiteral("ignored")); 0147 QCOMPARE(p.firstOperand().firstOperand().interfaceType(), Solid::DeviceInterface::StorageVolume); 0148 // Let's not call firstOperand() quite so much, and copy into a local atom 0149 { 0150 auto atom = p.firstOperand().firstOperand(); 0151 QVERIFY(atom.isValid()); 0152 QCOMPARE(atom.propertyName(), QStringLiteral("ignored")); 0153 QCOMPARE(atom.matchingValue().type(), QVariant::Bool); 0154 QCOMPARE(atom.matchingValue().toBool(), false); 0155 } 0156 { 0157 auto atom = p.firstOperand().secondOperand(); 0158 QVERIFY(atom.isValid()); 0159 QCOMPARE(atom.interfaceType(), Solid::DeviceInterface::StorageVolume); 0160 QCOMPARE(atom.propertyName(), QStringLiteral("usage")); 0161 QCOMPARE(atom.matchingValue().type(), QVariant::String); 0162 QCOMPARE(atom.matchingValue().toString(), QStringLiteral("FileSystem")); 0163 } 0164 { 0165 auto atom = p.secondOperand().firstOperand(); 0166 QVERIFY(atom.isValid()); 0167 QCOMPARE(atom.interfaceType(), Solid::DeviceInterface::StorageVolume); 0168 QCOMPARE(atom.propertyName(), QStringLiteral("ignored")); 0169 QCOMPARE(atom.matchingValue().type(), QVariant::Bool); 0170 QCOMPARE(atom.matchingValue().toBool(), false); 0171 } 0172 { 0173 auto atom = p.secondOperand().secondOperand(); 0174 QVERIFY(atom.isValid()); 0175 QCOMPARE(atom.interfaceType(), Solid::DeviceInterface::StorageDrive); 0176 QCOMPARE(atom.propertyName(), QStringLiteral("removable")); 0177 QCOMPARE(atom.matchingValue().type(), QVariant::Bool); 0178 QCOMPARE(atom.matchingValue().toBool(), true); 0179 } 0180 } 0181 0182 void SolidMtTest::testDeviceMatching() 0183 { 0184 auto deviceList = Solid::Device::allDevices(); 0185 QVERIFY(!deviceList.isEmpty()); 0186 0187 Solid::Predicate volumesPredicate = Solid::Predicate::fromString("StorageVolume.ignored == false"); 0188 QVERIFY(volumesPredicate.isValid()); 0189 0190 int volumes = 0; 0191 for (const auto &device : std::as_const(deviceList)) { 0192 if(volumesPredicate.matches(device)) 0193 { 0194 volumes++; 0195 } 0196 } 0197 // If there are no removable volumes, that's fine; below we will be matching empty lists. 0198 // QVERIFY(volumes > 0); 0199 0200 // From the pre-parsed query 0201 { 0202 auto matchedDeviceList = Solid::Device::listFromQuery(volumesPredicate); 0203 QCOMPARE(matchedDeviceList.size(), volumes); 0204 } 0205 // Same from text again 0206 { 0207 auto matchedDeviceList = Solid::Device::listFromQuery("StorageVolume.ignored == false"); 0208 QCOMPARE(matchedDeviceList.size(), volumes); 0209 } 0210 0211 // Are there removables? Hope so. 0212 // 0213 // The removableFSVolumesPredicate is checking two incompatible things: 0214 // a given Solid::Device isn't a Volume and a Drive at the same time, 0215 // so it cannot match. 0216 { 0217 Solid::Predicate fsVolumesPredicate = Solid::Predicate::fromString("[StorageVolume.ignored == false AND StorageVolume.usage == 'FileSystem']"); 0218 Solid::Predicate removablePredicate = Solid::Predicate::fromString("StorageDrive.removable == true"); 0219 Solid::Predicate removableFSVolumesPredicate = Solid::Predicate::fromString("[[StorageVolume.ignored == false AND StorageVolume.usage == 'FileSystem'] AND StorageDrive.removable == true]"); 0220 volumes = 0; 0221 for (const auto &device : std::as_const(deviceList)) { 0222 qDebug() << device.displayName() << "fs?" << fsVolumesPredicate.matches(device) << "removable?" << removablePredicate.matches(device) << "removableFS?" << removableFSVolumesPredicate.matches(device); 0223 if (removableFSVolumesPredicate.matches(device)) 0224 { 0225 volumes++; 0226 } 0227 } 0228 QCOMPARE(volumes, 0); 0229 0230 auto matchedDeviceList = Solid::Device::listFromQuery(removableFSVolumesPredicate); 0231 QCOMPARE(matchedDeviceList.size(), volumes); 0232 } 0233 0234 // Internally, the predicate is going to check interfaces, 0235 // so let's do that here as well. This will show no device is 0236 // both a Volume and a Drive. 0237 { 0238 const Solid::DeviceInterface::Type storageVolume = Solid::DeviceInterface::Type::StorageVolume; 0239 const Solid::DeviceInterface::Type storageDrive = Solid::DeviceInterface::Type::StorageDrive; 0240 0241 int volumeCount = 0; 0242 int driveCount = 0; 0243 int bothCount = 0; 0244 0245 for (const auto &device : std::as_const(deviceList)) { 0246 // Copied from internals in predicate.cpp: 0247 // const DeviceInterface *iface = device.asDeviceInterface(d->ifaceType); 0248 const Solid::DeviceInterface *volumeIface = device.asDeviceInterface(storageVolume); 0249 const Solid::DeviceInterface *driveIface = device.asDeviceInterface(storageDrive); 0250 volumeCount += volumeIface ? 1 : 0; 0251 driveCount += driveIface ? 1 : 0; 0252 bothCount += (volumeIface && driveIface) ? 1 : 0; 0253 } 0254 QCOMPARE(bothCount, 0); 0255 if (volumeCount + driveCount > 0) 0256 { 0257 // On Linux CI, there is no DBus connection, so no drives or volumes at all. 0258 // On a local machine, or on FreeBSD CI, there are volumes and/or drives, 0259 // so verify that there's at least one of each. 0260 QVERIFY(volumeCount > 0); 0261 QVERIFY(driveCount > 0); 0262 } 0263 } 0264 } 0265 0266 #include "solidmttest.moc"