cf12defd28
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
269 lines
10 KiB
C++
269 lines
10 KiB
C++
/*
|
|
SPDX-FileCopyrightText: 2008 Kevin Ottens <ervin@kde.org>
|
|
|
|
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
|
*/
|
|
|
|
#include <QObject>
|
|
#include <QThread>
|
|
#include <QThreadPool>
|
|
#include <qtconcurrentrun.h>
|
|
|
|
#include <QTest>
|
|
|
|
#include <solid/device.h>
|
|
#include <solid/genericinterface.h>
|
|
#include <solid/predicate.h>
|
|
#include <solid/storagedrive.h>
|
|
#include <solid/storagevolume.h>
|
|
|
|
class SolidMtTest : public QObject
|
|
{
|
|
Q_OBJECT
|
|
private Q_SLOTS:
|
|
void testWorkerThread();
|
|
void testThreadedPredicate();
|
|
void testTextPredicates_data();
|
|
void testTextPredicates();
|
|
void testTextPredicatesExtended();
|
|
void testDeviceMatching();
|
|
};
|
|
|
|
class WorkerThread : public QThread
|
|
{
|
|
Q_OBJECT
|
|
protected:
|
|
void run() override
|
|
{
|
|
Solid::Device dev(QStringLiteral("/org/freedesktop/Hal/devices/computer"));
|
|
|
|
const QList<Solid::Device> driveList = Solid::Device::listFromType(Solid::DeviceInterface::StorageDrive);
|
|
for (const Solid::Device &solidDevice : driveList) {
|
|
const Solid::StorageDrive *solidDrive = solidDevice.as<Solid::StorageDrive>();
|
|
Q_ASSERT(solidDrive);
|
|
Q_UNUSED(solidDrive);
|
|
}
|
|
}
|
|
};
|
|
|
|
static void doPredicates()
|
|
{
|
|
Solid::Predicate p5 = Solid::Predicate::fromString(
|
|
QStringLiteral("[[Processor.maxSpeed == 3201 AND Processor.canChangeFrequency == false] OR StorageVolume.mountPoint == '/media/blup']"));
|
|
|
|
Solid::Predicate p6 = Solid::Predicate::fromString(QStringLiteral("StorageVolume.usage == 'Other'"));
|
|
Solid::Predicate p7 = Solid::Predicate::fromString(QStringLiteral("StorageVolume.usage == %1").arg((int)Solid::StorageVolume::Other));
|
|
Solid::Predicate p8 = Solid::Predicate::fromString(QStringLiteral("StorageVolume.ignored == false"));
|
|
}
|
|
|
|
QTEST_MAIN(SolidMtTest)
|
|
|
|
void SolidMtTest::testWorkerThread()
|
|
{
|
|
Solid::Device dev(QStringLiteral("/org/freedesktop/Hal/devices/acpi_ADP1"));
|
|
|
|
WorkerThread *wt = new WorkerThread;
|
|
wt->start();
|
|
wt->wait();
|
|
|
|
const QList<Solid::Device> driveList = Solid::Device::listFromType(Solid::DeviceInterface::StorageDrive);
|
|
for (const Solid::Device &solidDevice : driveList) {
|
|
const Solid::GenericInterface *solidDrive = solidDevice.as<Solid::GenericInterface>();
|
|
Q_ASSERT(solidDrive);
|
|
Q_UNUSED(solidDrive);
|
|
}
|
|
|
|
delete wt;
|
|
}
|
|
|
|
void SolidMtTest::testThreadedPredicate()
|
|
{
|
|
QThreadPool::globalInstance()->setMaxThreadCount(10);
|
|
const QList<QFuture<void>> futures = {
|
|
QtConcurrent::run(&doPredicates),
|
|
QtConcurrent::run(&doPredicates),
|
|
QtConcurrent::run(&doPredicates),
|
|
QtConcurrent::run(&doPredicates),
|
|
QtConcurrent::run(&doPredicates),
|
|
QtConcurrent::run(&doPredicates),
|
|
QtConcurrent::run(&doPredicates),
|
|
QtConcurrent::run(&doPredicates),
|
|
};
|
|
for (QFuture<void> f : futures) {
|
|
f.waitForFinished();
|
|
}
|
|
QThreadPool::globalInstance()->setMaxThreadCount(1); // delete those threads
|
|
}
|
|
|
|
|
|
void SolidMtTest::testTextPredicates_data()
|
|
{
|
|
QTest::addColumn<QString>("pstring");
|
|
QTest::addColumn<bool>("isValidPredicate");
|
|
|
|
QTest::newRow("empty") << QString() << false;
|
|
QTest::newRow("simple") << QStringLiteral("@") << true;
|
|
QTest::newRow("and") << QStringLiteral("@ AND @") << false;
|
|
QTest::newRow("[and]") << QStringLiteral("[@ AND @]") << true;
|
|
QTest::newRow("[3and]") << QStringLiteral("[@ AND @ AND @]") << false;
|
|
QTest::newRow("[[and]and]") << QStringLiteral("[[@ AND @] AND @]") << true;
|
|
QTest::newRow("[and[and]]") << QStringLiteral("[@ AND [@ AND @]]") << true;
|
|
QTest::newRow("[[and][and] (unbalance)") << QStringLiteral("[ [@ AND @] AND [@ AND @]") << false;
|
|
QTest::newRow("[[and][and]]") << QStringLiteral("[ [@ AND @] AND [@ AND @] ]") << true;
|
|
QTest::newRow("[[[and]and]and]") << QStringLiteral("[ [ [ @ AND @] AND @ ] AND @]") << true;
|
|
}
|
|
|
|
/** @brief Check validity (and hence, parser-code) for a bunch of text predicates
|
|
*
|
|
*/
|
|
void SolidMtTest::testTextPredicates()
|
|
{
|
|
QFETCH(QString, pstring);
|
|
QFETCH(bool, isValidPredicate);
|
|
|
|
// The expressions contain @ as a placeholder, so they are
|
|
// short enough to easily see the structure of the expression
|
|
// in _data(); replace all those @s with an actual atom.
|
|
const QString atom(QStringLiteral("StorageVolume.ignored == false"));
|
|
while (pstring.contains(QLatin1Char('@'))) {
|
|
pstring = pstring.replace(QLatin1Char('@'), atom);
|
|
}
|
|
|
|
Solid::Predicate p = Solid::Predicate::fromString(pstring);
|
|
QCOMPARE(p.isValid(), isValidPredicate);
|
|
}
|
|
|
|
void SolidMtTest::testTextPredicatesExtended()
|
|
{
|
|
Solid::Predicate p = Solid::Predicate::fromString(QStringLiteral(
|
|
"[[StorageVolume.ignored == false AND StorageVolume.usage == 'FileSystem'] AND [StorageVolume.ignored == false AND StorageDrive.removable == true]]"));
|
|
QVERIFY(p.isValid());
|
|
QCOMPARE(p.type(), Solid::Predicate::Conjunction);
|
|
QCOMPARE(p.firstOperand().type(), Solid::Predicate::Conjunction);
|
|
QCOMPARE(p.firstOperand().firstOperand().type(), Solid::Predicate::PropertyCheck);
|
|
QCOMPARE(p.secondOperand().secondOperand().type(), Solid::Predicate::PropertyCheck);
|
|
|
|
QCOMPARE(p.firstOperand().firstOperand().propertyName(), QStringLiteral("ignored"));
|
|
QCOMPARE(p.firstOperand().firstOperand().interfaceType(), Solid::DeviceInterface::StorageVolume);
|
|
// Let's not call firstOperand() quite so much, and copy into a local atom
|
|
{
|
|
auto atom = p.firstOperand().firstOperand();
|
|
QVERIFY(atom.isValid());
|
|
QCOMPARE(atom.propertyName(), QStringLiteral("ignored"));
|
|
QCOMPARE(atom.matchingValue().userType(), QMetaType::Bool);
|
|
QCOMPARE(atom.matchingValue().toBool(), false);
|
|
}
|
|
{
|
|
auto atom = p.firstOperand().secondOperand();
|
|
QVERIFY(atom.isValid());
|
|
QCOMPARE(atom.interfaceType(), Solid::DeviceInterface::StorageVolume);
|
|
QCOMPARE(atom.propertyName(), QStringLiteral("usage"));
|
|
QCOMPARE(atom.matchingValue().userType(), QMetaType::QString);
|
|
QCOMPARE(atom.matchingValue().toString(), QStringLiteral("FileSystem"));
|
|
}
|
|
{
|
|
auto atom = p.secondOperand().firstOperand();
|
|
QVERIFY(atom.isValid());
|
|
QCOMPARE(atom.interfaceType(), Solid::DeviceInterface::StorageVolume);
|
|
QCOMPARE(atom.propertyName(), QStringLiteral("ignored"));
|
|
QCOMPARE(atom.matchingValue().userType(), QMetaType::Bool);
|
|
QCOMPARE(atom.matchingValue().toBool(), false);
|
|
}
|
|
{
|
|
auto atom = p.secondOperand().secondOperand();
|
|
QVERIFY(atom.isValid());
|
|
QCOMPARE(atom.interfaceType(), Solid::DeviceInterface::StorageDrive);
|
|
QCOMPARE(atom.propertyName(), QStringLiteral("removable"));
|
|
QCOMPARE(atom.matchingValue().userType(), QMetaType::Bool);
|
|
QCOMPARE(atom.matchingValue().toBool(), true);
|
|
}
|
|
}
|
|
|
|
void SolidMtTest::testDeviceMatching()
|
|
{
|
|
auto deviceList = Solid::Device::allDevices();
|
|
QVERIFY(!deviceList.isEmpty());
|
|
|
|
Solid::Predicate volumesPredicate = Solid::Predicate::fromString(QStringLiteral("StorageVolume.ignored == false"));
|
|
QVERIFY(volumesPredicate.isValid());
|
|
|
|
int volumes = 0;
|
|
for (const auto &device : std::as_const(deviceList)) {
|
|
if(volumesPredicate.matches(device))
|
|
{
|
|
volumes++;
|
|
}
|
|
}
|
|
// If there are no removable volumes, that's fine; below we will be matching empty lists.
|
|
// QVERIFY(volumes > 0);
|
|
|
|
// From the pre-parsed query
|
|
{
|
|
auto matchedDeviceList = Solid::Device::listFromQuery(volumesPredicate);
|
|
QCOMPARE(matchedDeviceList.size(), volumes);
|
|
}
|
|
// Same from text again
|
|
{
|
|
auto matchedDeviceList = Solid::Device::listFromQuery(QStringLiteral("StorageVolume.ignored == false"));
|
|
QCOMPARE(matchedDeviceList.size(), volumes);
|
|
}
|
|
|
|
// Are there removables? Hope so.
|
|
//
|
|
// The removableFSVolumesPredicate is checking two incompatible things:
|
|
// a given Solid::Device isn't a Volume and a Drive at the same time,
|
|
// so it cannot match.
|
|
{
|
|
Solid::Predicate fsVolumesPredicate =
|
|
Solid::Predicate::fromString(QStringLiteral("[StorageVolume.ignored == false AND StorageVolume.usage == 'FileSystem']"));
|
|
Solid::Predicate removablePredicate = Solid::Predicate::fromString(QStringLiteral("StorageDrive.removable == true"));
|
|
Solid::Predicate removableFSVolumesPredicate = Solid::Predicate::fromString(
|
|
QStringLiteral("[[StorageVolume.ignored == false AND StorageVolume.usage == 'FileSystem'] AND StorageDrive.removable == true]"));
|
|
volumes = 0;
|
|
for (const auto &device : std::as_const(deviceList)) {
|
|
qDebug() << device.displayName() << "fs?" << fsVolumesPredicate.matches(device) << "removable?" << removablePredicate.matches(device) << "removableFS?" << removableFSVolumesPredicate.matches(device);
|
|
if (removableFSVolumesPredicate.matches(device))
|
|
{
|
|
volumes++;
|
|
}
|
|
}
|
|
QCOMPARE(volumes, 0);
|
|
|
|
auto matchedDeviceList = Solid::Device::listFromQuery(removableFSVolumesPredicate);
|
|
QCOMPARE(matchedDeviceList.size(), volumes);
|
|
}
|
|
|
|
// Internally, the predicate is going to check interfaces,
|
|
// so let's do that here as well. This will show no device is
|
|
// both a Volume and a Drive.
|
|
{
|
|
const Solid::DeviceInterface::Type storageVolume = Solid::DeviceInterface::Type::StorageVolume;
|
|
const Solid::DeviceInterface::Type storageDrive = Solid::DeviceInterface::Type::StorageDrive;
|
|
|
|
int volumeCount = 0;
|
|
int driveCount = 0;
|
|
int bothCount = 0;
|
|
|
|
for (const auto &device : std::as_const(deviceList)) {
|
|
// Copied from internals in predicate.cpp:
|
|
// const DeviceInterface *iface = device.asDeviceInterface(d->ifaceType);
|
|
const Solid::DeviceInterface *volumeIface = device.asDeviceInterface(storageVolume);
|
|
const Solid::DeviceInterface *driveIface = device.asDeviceInterface(storageDrive);
|
|
volumeCount += volumeIface ? 1 : 0;
|
|
driveCount += driveIface ? 1 : 0;
|
|
bothCount += (volumeIface && driveIface) ? 1 : 0;
|
|
}
|
|
QCOMPARE(bothCount, 0);
|
|
if (volumeCount + driveCount > 0)
|
|
{
|
|
// On Linux CI, there is no DBus connection, so no drives or volumes at all.
|
|
// On a local machine, or on FreeBSD CI, there are volumes and/or drives,
|
|
// so verify that there's at least one of each.
|
|
QVERIFY(volumeCount > 0);
|
|
QVERIFY(driveCount > 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
#include "solidmttest.moc"
|