From 0345de04d19bf4ab203caeb92f52f3660f92e7d3 Mon Sep 17 00:00:00 2001 From: Joona Petrell Date: Wed, 7 Oct 2015 18:12:56 +0300 Subject: [PATCH] [ssu] Introduce SsuFeatureModel. Fixes MER#1367 --- constants.h | 7 + declarative/declarative.pro | 24 ++++ declarative/declarativessufeaturemodel.cpp | 32 +++++ declarative/declarativessufeaturemodel.h | 23 ++++ declarative/plugin.cpp | 26 ++++ declarative/qmldir | 2 + libssu/libssu.pro | 2 + libssu/ssufeaturemanager.cpp | 10 -- libssu/ssufeaturemodel.cpp | 127 ++++++++++++++++++ libssu/ssufeaturemodel.h | 49 +++++++ rpm/ssu.spec | 12 +- ssu.pro | 3 +- tests/tests.pro | 1 + tests/tests.xml | 5 + tests/ut_featuremodel/featuremodeltest.cpp | 48 +++++++ tests/ut_featuremodel/featuremodeltest.h | 22 +++ tests/ut_featuremodel/main.cpp | 22 +++ tests/ut_featuremodel/testdata.qrc | 9 ++ tests/ut_featuremodel/testdata/feature-a.ini | 6 + tests/ut_featuremodel/testdata/feature-b.ini | 6 + .../testdata/feature-noname.ini | 5 + .../testdata/feature-noversion.ini | 5 + tests/ut_featuremodel/ut_featuremodel.pro | 12 ++ .../ut_featuremodel_dependencies.pri | 2 + 24 files changed, 448 insertions(+), 12 deletions(-) create mode 100644 declarative/declarative.pro create mode 100644 declarative/declarativessufeaturemodel.cpp create mode 100644 declarative/declarativessufeaturemodel.h create mode 100644 declarative/plugin.cpp create mode 100644 declarative/qmldir create mode 100644 libssu/ssufeaturemodel.cpp create mode 100644 libssu/ssufeaturemodel.h create mode 100644 tests/ut_featuremodel/featuremodeltest.cpp create mode 100644 tests/ut_featuremodel/featuremodeltest.h create mode 100644 tests/ut_featuremodel/main.cpp create mode 100644 tests/ut_featuremodel/testdata.qrc create mode 100644 tests/ut_featuremodel/testdata/feature-a.ini create mode 100644 tests/ut_featuremodel/testdata/feature-b.ini create mode 100644 tests/ut_featuremodel/testdata/feature-noname.ini create mode 100644 tests/ut_featuremodel/testdata/feature-noversion.ini create mode 100644 tests/ut_featuremodel/ut_featuremodel.pro create mode 100644 tests/ut_featuremodel/ut_featuremodel_dependencies.pri diff --git a/constants.h b/constants.h index 20243a1..f75ce85 100644 --- a/constants.h +++ b/constants.h @@ -24,4 +24,11 @@ #define SSU_MAX_RECURSION 1024 /// Path to zypper repo configuration #define ZYPP_REPO_PATH "/etc/zypp/repos.d" + +/// Path to the main ssu configuration file +#define SSU_FEATURE_CONFIGURATION "/var/cache/ssu/features.ini" + +/// Path to the main ssu configuration file +#define SSU_FEATURE_CONFIGURATION_DIR "/usr/share/ssu/features.d" + #endif diff --git a/declarative/declarative.pro b/declarative/declarative.pro new file mode 100644 index 0000000..7db2d8e --- /dev/null +++ b/declarative/declarative.pro @@ -0,0 +1,24 @@ +TEMPLATE = lib +TARGET = declarativessu +TARGET = $$qtLibraryTarget($$TARGET) + +include(../libssu/libssu.pri) + +MODULENAME = Nemo/Ssu +TARGETPATH = $$[QT_INSTALL_QML]/$$MODULENAME + +QT += qml +QT -= gui + +CONFIG += plugin + +target.path = $$TARGETPATH + +HEADERS = declarativessufeaturemodel.h +SOURCES += plugin.cpp \ + declarativessufeaturemodel.cpp + +qmldir.files = qmldir *.qml *.js +qmldir.path = $$target.path + +INSTALLS += target qmldir diff --git a/declarative/declarativessufeaturemodel.cpp b/declarative/declarativessufeaturemodel.cpp new file mode 100644 index 0000000..bc4f17e --- /dev/null +++ b/declarative/declarativessufeaturemodel.cpp @@ -0,0 +1,32 @@ +/** + * @file declarativessufeaturemodel.cpp + * @copyright 2015 Jolla Ltd. + * @author Joona Petrell + * @date 2015 + */ + +#include "declarativessufeaturemodel.h" +#include +#include +#include +#include + +QJSValue DeclarativeSsuFeatureModel::get(int index) const +{ + if (index < 0 || index >= count()) { + qmlInfo(this) << "Index" << index << "out of bounds"; + return QJSValue(); + } + const QHash feature = featureAt(index); + + QJSEngine *const engine = qmlEngine(this); + QJSValue value = engine->newObject(); + + QHash::const_iterator i = feature.constBegin(); + while (i != feature.constEnd()) { + value.setProperty(QString::fromLatin1(i.key()), i.value()); + ++i; + } + + return value; +} diff --git a/declarative/declarativessufeaturemodel.h b/declarative/declarativessufeaturemodel.h new file mode 100644 index 0000000..f59482b --- /dev/null +++ b/declarative/declarativessufeaturemodel.h @@ -0,0 +1,23 @@ +/** + * @file declarativessufeaturemodel.h + * @copyright 2015 Jolla Ltd. + * @author Joona Petrell + * @date 2015 + */ + +#ifndef _DECLARATIVESSUFEATUREMODEL_H +#define _DECLARATIVESSUFEATUREMODEL_H + +#include "../libssu/ssufeaturemodel.h" +#include + +class DeclarativeSsuFeatureModel: public SsuFeatureModel +{ + Q_OBJECT +public: + Q_INVOKABLE QJSValue get(int index) const; +}; + +#endif + + diff --git a/declarative/plugin.cpp b/declarative/plugin.cpp new file mode 100644 index 0000000..392ec63 --- /dev/null +++ b/declarative/plugin.cpp @@ -0,0 +1,26 @@ +/** + * @file plugin.cpp + * @copyright 2015 Jolla Ltd. + * @author Joona Petrell + * @date 2015 + */ + +#include +#include + +#include +#include "declarativessufeaturemodel.h" + +class NemoSsuPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.nemomobile.ssu") + +public: + virtual void registerTypes(const char *) + { + qmlRegisterType("Nemo.Ssu", 1, 0, "FeatureModel"); + } +}; + +#include "plugin.moc" diff --git a/declarative/qmldir b/declarative/qmldir new file mode 100644 index 0000000..1d870c6 --- /dev/null +++ b/declarative/qmldir @@ -0,0 +1,2 @@ +module Nemo.Ssu +plugin declarativessu \ No newline at end of file diff --git a/libssu/libssu.pro b/libssu/libssu.pro index ad5df7e..71121b5 100644 --- a/libssu/libssu.pro +++ b/libssu/libssu.pro @@ -6,6 +6,7 @@ public_headers = \ ssu.h \ ssudeviceinfo.h \ ssurepomanager.h \ + ssufeaturemodel.h HEADERS = \ $${public_headers} \ @@ -23,6 +24,7 @@ SOURCES = \ ssudeviceinfo.cpp \ ssulog.cpp \ ssufeaturemanager.cpp \ + ssufeaturemodel.cpp \ ssuvariables.cpp \ ssurepomanager.cpp \ ssusettings.cpp diff --git a/libssu/ssufeaturemanager.cpp b/libssu/ssufeaturemanager.cpp index 4fa28ba..453e879 100644 --- a/libssu/ssufeaturemanager.cpp +++ b/libssu/ssufeaturemanager.cpp @@ -18,16 +18,6 @@ #include "../constants.h" -#ifndef SSU_FEATURE_CONFIGURATION -/// Path to the main ssu configuration file -#define SSU_FEATURE_CONFIGURATION "/var/cache/ssu/features.ini" -#endif - -#ifndef SSU_FEATURE_CONFIGURATION_DIR -/// Path to the main ssu configuration file -#define SSU_FEATURE_CONFIGURATION_DIR "/usr/share/ssu/features.d" -#endif - SsuFeatureManager::SsuFeatureManager(): QObject() { featureSettings = new SsuSettings(SSU_FEATURE_CONFIGURATION, SSU_FEATURE_CONFIGURATION_DIR); } diff --git a/libssu/ssufeaturemodel.cpp b/libssu/ssufeaturemodel.cpp new file mode 100644 index 0000000..b5bce6a --- /dev/null +++ b/libssu/ssufeaturemodel.cpp @@ -0,0 +1,127 @@ +/** + * @file ssufeaturemodel.cpp + * @copyright 2015 Jolla Ltd. + * @author Joona Petrell + * @date 2015 + */ + +#include "ssufeaturemodel.h" +#include +#include +#include +#include +#include +#include "../constants.h" + +bool featureLessThan(const QHash &feature1, const QHash &feature2) +{ + return feature1.value("name") < feature2.value("name"); +} + +class SsuFeatureModelPrivate { +public: + SsuFeatureModelPrivate(const QString &p) : path(p) + { + load(); + } + + ~SsuFeatureModelPrivate() + {} + + void load() { + QDirIterator it(path, QDir::AllEntries|QDir::NoDot|QDir::NoDotDot, QDirIterator::FollowSymlinks); + QStringList settingsFiles; + + while (it.hasNext()) { + it.next(); + settingsFiles.append(it.filePath()); + } + foreach (const QString &settingsFile, settingsFiles) { + QSettings settings(settingsFile, QSettings::IniFormat); + + foreach (const QString &childGroup, settings.childGroups()) { + settings.beginGroup(childGroup); + QStringList keys = settings.childKeys(); + + if (keys.contains("name")) { + QHash feature; + feature.insert("name", settings.value("name").toString()); + if (keys.contains("version")) { + feature.insert("version", settings.value("version").toString()); + } else { + feature.insert("version", QString("")); + } + features.append(feature); + } + settings.endGroup(); + } + } + qSort(features.begin(), features.end(), featureLessThan); + } + + QString path; + QList > features; +}; + +SsuFeatureModel::SsuFeatureModel(QObject *parent, const QString &path) + : QAbstractListModel(parent), + d(new SsuFeatureModelPrivate(path)) +{ +} + +SsuFeatureModel::SsuFeatureModel(QObject *parent) + : QAbstractListModel(parent), + d(new SsuFeatureModelPrivate(SSU_FEATURE_CONFIGURATION_DIR)) +{ +} + +SsuFeatureModel::~SsuFeatureModel() +{ + delete d; +} + +int SsuFeatureModel::count() const +{ + return rowCount(); +} + +const QHash &SsuFeatureModel::featureAt(int index) const +{ + return d->features.at(index); +} + +int SsuFeatureModel::rowCount(const QModelIndex &parent) const +{ + return parent.isValid() ? 0 : d->features.count(); +} + +void SsuFeatureModel::reload() +{ + beginResetModel(); + d->features.clear(); + d->load(); + endResetModel(); +} + +QVariant SsuFeatureModel::data(const QModelIndex &index, int role) const +{ + int row = index.row(); + if (!index.isValid() || row < 0 || row >= d->features.count()) + return QVariant(); + + switch (role) { + case Name: + return d->features.at(row).value("name"); + case Version: + return d->features.at(row).value("version"); + } + return QVariant(); +} + +QHash SsuFeatureModel::roleNames() const +{ + QHash roles; + roles.insert(Name, "name"); + roles.insert(Version, "version"); + return roles; +} diff --git a/libssu/ssufeaturemodel.h b/libssu/ssufeaturemodel.h new file mode 100644 index 0000000..b8af278 --- /dev/null +++ b/libssu/ssufeaturemodel.h @@ -0,0 +1,49 @@ +/** + * @file ssufeaturemodel.h + * @copyright 2015 Jolla Ltd. + * @author Joona Petrell + * @date 2015 + */ + +#ifndef _SSUFEATUREMODEL_H +#define _SSUFEATUREMODEL_H + +#include + +class SsuFeatureModelPrivate; +class FeatureModelTest; + +class SsuFeatureModel: public QAbstractListModel +{ + Q_OBJECT + Q_PROPERTY(int count READ count NOTIFY countChanged) +public: + enum Roles { + Name = Qt::UserRole, + Version + }; + + SsuFeatureModel(QObject *parent = 0); + SsuFeatureModel(QObject *parent, const QString &path); + ~SsuFeatureModel(); + + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + virtual QVariant data(const QModelIndex &index, int role) const; + +public slots: + void reload(); + int count() const; + +protected: + QHash roleNames() const; + const QHash &featureAt(int index) const; + +signals: + void countChanged(); + +private: + SsuFeatureModelPrivate *d; + friend class FeatureModelTest; +}; + +#endif diff --git a/rpm/ssu.spec b/rpm/ssu.spec index 6349bfc..85bf744 100644 --- a/rpm/ssu.spec +++ b/rpm/ssu.spec @@ -84,7 +84,6 @@ Provides: rpm-macros %config %{_sysconfdir}/rpm/macros.ssuks %{_bindir}/ssuks - %package slipstream Summary: %{name} OS factory snapshot download provider Group: System/Base @@ -96,6 +95,17 @@ Helper utility to authenticate downloads of factory snapshot manifests. %defattr(-,root,root,-) %{_bindir}/ssuslipstream +%package declarative +Summary: QML plugin for libssu +Group: System/Base +BuildRequires: pkgconfig(Qt5Qml) + +%description declarative +%{summary} + +%files declarative +%{_libdir}/qt5/qml/Nemo/Ssu/* + %package devel Summary: Development files for %{name} Group: Development/Libraries diff --git a/ssu.pro b/ssu.pro index b8fe1a4..a5473ba 100644 --- a/ssu.pro +++ b/ssu.pro @@ -4,7 +4,8 @@ contains(QT_VERSION, ^4\\.[0-7]\\..*) { TEMPLATE = subdirs SUBDIRS = libssu libssunetworkproxy ssud -SUBDIRS += ssucli ssuurlresolver ssuks ssuslipstream +SUBDIRS += ssucli ssuurlresolver ssuks +SUBDIRS += ssuslipstream declarative ssuconfhack { SUBDIRS += ssuconfperm diff --git a/tests/tests.pro b/tests/tests.pro index 5f27299..64a2121 100644 --- a/tests/tests.pro +++ b/tests/tests.pro @@ -6,6 +6,7 @@ SUBDIRS = \ testutils/sandboxhook.pro \ ut_coreconfig \ ut_deviceinfo \ + ut_featuremodel \ ut_repomanager \ ut_ssu \ ut_ssucli \ diff --git a/tests/tests.xml b/tests/tests.xml index ea0402b..afe4d9b 100644 --- a/tests/tests.xml +++ b/tests/tests.xml @@ -38,6 +38,11 @@ /opt/tests/ssu/runtest.sh ut_settings + + + /opt/tests/ssu/runtest.sh ut_featuremodel + + /opt/tests/ssu/runtest.sh ut_ssuurlresolver diff --git a/tests/ut_featuremodel/featuremodeltest.cpp b/tests/ut_featuremodel/featuremodeltest.cpp new file mode 100644 index 0000000..2b8a5a4 --- /dev/null +++ b/tests/ut_featuremodel/featuremodeltest.cpp @@ -0,0 +1,48 @@ +/** + * @file featuremodeltest.cpp + * @copyright 2015 Jolla Ltd. + * @author Joona Petrell + * @date 2015 + */ + +#include "featuremodeltest.h" + +#include +#include +#include "libssu/ssufeaturemodel.h" + +void FeatureModelTest::initTestCase(){ + +} + +void FeatureModelTest::cleanupTestCase(){ + +} + +void FeatureModelTest::testFeatures(){ + SsuFeatureModel featureModel(0, ":/testdata"); + + // Features with no name are skipped (feature-noname.ini) + QCOMPARE(featureModel.count(), 3); + + // First feature is "Feature A" in version 0.1 + + // Explicit getter + const QHash featureA = featureModel.featureAt(0); + QCOMPARE(featureA.value("name"), QString("Feature A")); + QCOMPARE(featureA.value("version"), QString("0.1")); + + // Through QAbstractListModel API + QCOMPARE(featureModel.data(featureModel.index(0), SsuFeatureModel::Name).toString(), QString("Feature A")); + QCOMPARE(featureModel.data(featureModel.index(0), SsuFeatureModel::Version).toString(), QString("0.1")); + + // Second feature is "Feature B" in version 0.2 + const QHash featureB = featureModel.featureAt(1); + QCOMPARE(featureB.value("name"), QString("Feature B")); + QCOMPARE(featureB.value("version"), QString("0.2")); + + // Third feature lacks version number + const QHash featureWithoutVersion = featureModel.featureAt(2); + QCOMPARE(featureWithoutVersion.value("name"), QString("Feature without version")); + QCOMPARE(featureWithoutVersion.value("version"), QString("")); +} diff --git a/tests/ut_featuremodel/featuremodeltest.h b/tests/ut_featuremodel/featuremodeltest.h new file mode 100644 index 0000000..bd21a4a --- /dev/null +++ b/tests/ut_featuremodel/featuremodeltest.h @@ -0,0 +1,22 @@ +/** + * @file featuremodeltest.h + * @copyright 2015 Jolla Ltd. + * @author Joona Petrell + * @date 2015 + */ + +#ifndef _FEATUREMODELTEST_H +#define _FEATUREMODELTEST_H + +#include + +class FeatureModelTest: public QObject { + Q_OBJECT + + private slots: + void initTestCase(); + void cleanupTestCase(); + void testFeatures(); +}; + +#endif diff --git a/tests/ut_featuremodel/main.cpp b/tests/ut_featuremodel/main.cpp new file mode 100644 index 0000000..cd2a5a0 --- /dev/null +++ b/tests/ut_featuremodel/main.cpp @@ -0,0 +1,22 @@ +/** + * @file main.cpp + * @copyright 2015 Jolla Ltd. + * @author Joona Petrell + * @date 2015 + */ + +#include +#include + +#include "featuremodeltest.h" + +int main(int argc, char **argv){ + QCoreApplication app(argc, argv); + + FeatureModelTest featureModelTest; + + if (QTest::qExec(&featureModelTest, argc, argv)) + return 1; + + return 0; +} diff --git a/tests/ut_featuremodel/testdata.qrc b/tests/ut_featuremodel/testdata.qrc new file mode 100644 index 0000000..c68c003 --- /dev/null +++ b/tests/ut_featuremodel/testdata.qrc @@ -0,0 +1,9 @@ + + + + testdata/feature-a.ini + testdata/feature-b.ini + testdata/feature-noname.ini + testdata/feature-noversion.ini + + diff --git a/tests/ut_featuremodel/testdata/feature-a.ini b/tests/ut_featuremodel/testdata/feature-a.ini new file mode 100644 index 0000000..2eea57c --- /dev/null +++ b/tests/ut_featuremodel/testdata/feature-a.ini @@ -0,0 +1,6 @@ +[feature-a] +repos = feature-a +packages = feature-a +description = Feature A +version = 0.1 +name = Feature A diff --git a/tests/ut_featuremodel/testdata/feature-b.ini b/tests/ut_featuremodel/testdata/feature-b.ini new file mode 100644 index 0000000..ebdbac5 --- /dev/null +++ b/tests/ut_featuremodel/testdata/feature-b.ini @@ -0,0 +1,6 @@ +[feature-b] +repos = feature-b +packages = feature-b +description = Feature B +version = 0.2 +name = Feature B diff --git a/tests/ut_featuremodel/testdata/feature-noname.ini b/tests/ut_featuremodel/testdata/feature-noname.ini new file mode 100644 index 0000000..b618b2d --- /dev/null +++ b/tests/ut_featuremodel/testdata/feature-noname.ini @@ -0,0 +1,5 @@ +[feature-noname] +repos = feature-noname +packages = feature-noname +description = Feature without name +version = 0.0.3 diff --git a/tests/ut_featuremodel/testdata/feature-noversion.ini b/tests/ut_featuremodel/testdata/feature-noversion.ini new file mode 100644 index 0000000..7e220d9 --- /dev/null +++ b/tests/ut_featuremodel/testdata/feature-noversion.ini @@ -0,0 +1,5 @@ +[feature-noversion] +repos = feature-noversion +packages = feature-noversion +description = Feature without version +name = Feature without version diff --git a/tests/ut_featuremodel/ut_featuremodel.pro b/tests/ut_featuremodel/ut_featuremodel.pro new file mode 100644 index 0000000..6d57104 --- /dev/null +++ b/tests/ut_featuremodel/ut_featuremodel.pro @@ -0,0 +1,12 @@ +TARGET = ut_featuremodel +include(../testapplication.pri) +include(ut_featuremodel_dependencies.pri) + +HEADERS = \ + featuremodeltest.h + +SOURCES = \ + main.cpp \ + featuremodeltest.cpp + +RESOURCES = testdata.qrc diff --git a/tests/ut_featuremodel/ut_featuremodel_dependencies.pri b/tests/ut_featuremodel/ut_featuremodel_dependencies.pri new file mode 100644 index 0000000..5e56454 --- /dev/null +++ b/tests/ut_featuremodel/ut_featuremodel_dependencies.pri @@ -0,0 +1,2 @@ +include(../../libssu/libssu.pri) +include(../testutils/testutils.pri)