Commit 0345de04 authored by jpetrell's avatar jpetrell

[ssu] Introduce SsuFeatureModel. Fixes MER#1367

parent 8a44464f
......@@ -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
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
/**
* @file declarativessufeaturemodel.cpp
* @copyright 2015 Jolla Ltd.
* @author Joona Petrell <joona.petrell@jolla.com>
* @date 2015
*/
#include "declarativessufeaturemodel.h"
#include <QHash>
#include <qqml.h>
#include <QQmlEngine>
#include <qqmlinfo.h>
QJSValue DeclarativeSsuFeatureModel::get(int index) const
{
if (index < 0 || index >= count()) {
qmlInfo(this) << "Index" << index << "out of bounds";
return QJSValue();
}
const QHash<QByteArray, QString> feature = featureAt(index);
QJSEngine *const engine = qmlEngine(this);
QJSValue value = engine->newObject();
QHash<QByteArray, QString>::const_iterator i = feature.constBegin();
while (i != feature.constEnd()) {
value.setProperty(QString::fromLatin1(i.key()), i.value());
++i;
}
return value;
}
/**
* @file declarativessufeaturemodel.h
* @copyright 2015 Jolla Ltd.
* @author Joona Petrell <joona.petrell@jolla.com>
* @date 2015
*/
#ifndef _DECLARATIVESSUFEATUREMODEL_H
#define _DECLARATIVESSUFEATUREMODEL_H
#include "../libssu/ssufeaturemodel.h"
#include <QJSValue>
class DeclarativeSsuFeatureModel: public SsuFeatureModel
{
Q_OBJECT
public:
Q_INVOKABLE QJSValue get(int index) const;
};
#endif
/**
* @file plugin.cpp
* @copyright 2015 Jolla Ltd.
* @author Joona Petrell <joona.petrell@jollamobile.com>
* @date 2015
*/
#include <QQmlExtensionPlugin>
#include <QQmlEngine>
#include <qqml.h>
#include "declarativessufeaturemodel.h"
class NemoSsuPlugin : public QQmlExtensionPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.nemomobile.ssu")
public:
virtual void registerTypes(const char *)
{
qmlRegisterType<DeclarativeSsuFeatureModel>("Nemo.Ssu", 1, 0, "FeatureModel");
}
};
#include "plugin.moc"
module Nemo.Ssu
plugin declarativessu
\ No newline at end of file
......@@ -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
......
......@@ -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);
}
......
/**
* @file ssufeaturemodel.cpp
* @copyright 2015 Jolla Ltd.
* @author Joona Petrell <joona.petrell@jolla.com>
* @date 2015
*/
#include "ssufeaturemodel.h"
#include <QList>
#include <QSettings>
#include <QHash>
#include <QDebug>
#include <QDirIterator>
#include "../constants.h"
bool featureLessThan(const QHash<QByteArray, QString> &feature1, const QHash<QByteArray, QString> &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<QByteArray, QString> 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<QHash<QByteArray, QString> > 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<QByteArray, QString> &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<int, QByteArray> SsuFeatureModel::roleNames() const
{
QHash<int,QByteArray> roles;
roles.insert(Name, "name");
roles.insert(Version, "version");
return roles;
}
/**
* @file ssufeaturemodel.h
* @copyright 2015 Jolla Ltd.
* @author Joona Petrell <joona.petrell@jolla.com>
* @date 2015
*/
#ifndef _SSUFEATUREMODEL_H
#define _SSUFEATUREMODEL_H
#include <QAbstractListModel>
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<int, QByteArray> roleNames() const;
const QHash<QByteArray, QString> &featureAt(int index) const;
signals:
void countChanged();
private:
SsuFeatureModelPrivate *d;
friend class FeatureModelTest;
};
#endif
......@@ -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
......
......@@ -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
......
......@@ -6,6 +6,7 @@ SUBDIRS = \
testutils/sandboxhook.pro \
ut_coreconfig \
ut_deviceinfo \
ut_featuremodel \
ut_repomanager \
ut_ssu \
ut_ssucli \
......
......@@ -38,6 +38,11 @@
<step expected_result="0">/opt/tests/ssu/runtest.sh ut_settings</step>
</case>
</set>
<set name="featuremodel" description="Test to determine feature model behaves correctly" feature="featuremodel">
<case name="ut_featuremodel" type="Functional" description="Feature model processing test" timeout="1000" subfeature="">
<step expected_result="0">/opt/tests/ssu/runtest.sh ut_featuremodel</step>
</case>
</set>
<set name="ssuurlresolver" description="Test to determine if the UrlResolverPlugin works well with installed version of libzypp" feature="ssuurlresolver">
<case name="ut_ssuurlresolver" type="Functional" description="URL resolver plugin test" timeout="1000" subfeature="">
<step expected_result="0">/opt/tests/ssu/runtest.sh ut_ssuurlresolver</step>
......
/**
* @file featuremodeltest.cpp
* @copyright 2015 Jolla Ltd.
* @author Joona Petrell <joona.petrell@jolla.com>
* @date 2015
*/
#include "featuremodeltest.h"
#include <QtTest/QtTest>
#include <QtTest/QSignalSpy>
#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<QByteArray, QString> 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<QByteArray, QString> 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<QByteArray, QString> featureWithoutVersion = featureModel.featureAt(2);
QCOMPARE(featureWithoutVersion.value("name"), QString("Feature without version"));
QCOMPARE(featureWithoutVersion.value("version"), QString(""));
}
/**
* @file featuremodeltest.h
* @copyright 2015 Jolla Ltd.
* @author Joona Petrell <joona.petrell@jolla.com>
* @date 2015
*/
#ifndef _FEATUREMODELTEST_H
#define _FEATUREMODELTEST_H
#include <QObject>
class FeatureModelTest: public QObject {
Q_OBJECT
private slots:
void initTestCase();
void cleanupTestCase();
void testFeatures();
};
#endif
/**
* @file main.cpp
* @copyright 2015 Jolla Ltd.
* @author Joona Petrell <joona.petrell@jolla.com>
* @date 2015
*/
#include <QtCore/QCoreApplication>
#include <QtTest/QtTest>
#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;
}
<!DOCTYPE RCC>
<RCC version="1.0">
<qresource>
<file>testdata/feature-a.ini</file>
<file>testdata/feature-b.ini</file>
<file>testdata/feature-noname.ini</file>
<file>testdata/feature-noversion.ini</file>
</qresource>
</RCC>
[feature-a]
repos = feature-a
packages = feature-a
description = Feature A
version = 0.1
name = Feature A
[feature-b]
repos = feature-b
packages = feature-b
description = Feature B
version = 0.2
name = Feature B
[feature-noname]
repos = feature-noname
packages = feature-noname
description = Feature without name
version = 0.0.3
[feature-noversion]
repos = feature-noversion
packages = feature-noversion
description = Feature without version
name = Feature without version
TARGET = ut_featuremodel
include(../testapplication.pri)
include(ut_featuremodel_dependencies.pri)
HEADERS = \
featuremodeltest.h
SOURCES = \
main.cpp \
featuremodeltest.cpp
RESOURCES = testdata.qrc
include(../../libssu/libssu.pri)
include(../testutils/testutils.pri)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment