Merge branch 'mart/packageFallback'

REVIEW:120029
This commit is contained in:
Marco Martin 2014-09-01 20:59:22 +02:00
commit 8c8bd49130
10 changed files with 235 additions and 5 deletions

View File

@ -37,6 +37,7 @@ MACRO(PLASMA_UNIT_TESTS)
ENDMACRO(PLASMA_UNIT_TESTS)
PLASMA_UNIT_TESTS(
fallbackpackagetest
packagestructuretest
packageurlinterceptortest
pluginloadertest

View File

@ -0,0 +1,7 @@
import QtQuick 2.0
Rectangle {
id: root
color: "darkblue"
}

View File

@ -0,0 +1,15 @@
[Desktop Entry]
Encoding=UTF-8
Keywords=
Name=Test Fallback Package
Type=Service
X-KDE-ParentApp=
X-KDE-PluginInfo-Author=Marco Martin
X-KDE-PluginInfo-Category=
X-KDE-PluginInfo-Email=mart@kde.org
X-KDE-PluginInfo-License=GPLv2+
X-KDE-PluginInfo-Name=org.kde.testfallbackpackage
X-KDE-PluginInfo-Version=
X-KDE-PluginInfo-Website=
X-Plasma-MainScript=ui/main.qml

View File

@ -0,0 +1,7 @@
import QtQuick 2.0
Rectangle {
id: root
color: "darkblue"
}

View File

@ -0,0 +1,71 @@
/******************************************************************************
* Copyright 2007 by Aaron Seigo <aseigo@kde.org> *
* Copyright 2014 Marco Martin <mart@kde.org> *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public License *
* along with this library; see the file COPYING.LIB. If not, write to *
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, *
* Boston, MA 02110-1301, USA. *
*******************************************************************************/
#include "fallbackpackagetest.h"
#include <QDebug>
#include "packagestructure.h"
#include "pluginloader.h"
void FallbackPackageTest::initTestCase()
{
m_fallPackagePath = QFINDTESTDATA("data/testpackage");
m_fallbackPkg = Plasma::PluginLoader::self()->loadPackage("Plasma/Generic");
m_fallbackPkg.setPath(m_fallPackagePath);
m_packagePath = QFINDTESTDATA("data/testfallbackpackage");
m_pkg = Plasma::PluginLoader::self()->loadPackage("Plasma/Generic");
m_pkg.setPath(m_packagePath);
}
void FallbackPackageTest::beforeFallback()
{
QVERIFY(m_fallbackPkg.hasValidStructure());
QVERIFY(m_pkg.hasValidStructure());
//m_fallbackPkg should have otherfile.qml, m_pkg shouldn't
QVERIFY(!m_fallbackPkg.filePath("ui", "otherfile.qml").isEmpty());
QVERIFY(m_pkg.filePath("ui", "otherfile.qml").isEmpty());
}
void FallbackPackageTest::afterFallback()
{
m_pkg.setFallbackPackage(m_fallbackPkg);
//after setting the fallback, m_pkg should resolve the exact same file as m_fallbackPkg
// for otherfile.qml
QVERIFY(!m_pkg.filePath("ui", "otherfile.qml").isEmpty());
QCOMPARE(m_fallbackPkg.filePath("ui", "otherfile.qml"), m_pkg.filePath("ui", "otherfile.qml"));
QVERIFY(m_fallbackPkg.filePath("mainscript") != m_pkg.filePath("mainscript"));
}
void FallbackPackageTest::cycle()
{
m_fallbackPkg.setFallbackPackage(m_pkg);
m_pkg.setFallbackPackage(m_fallbackPkg);
//The cycle should have been detected and filePath should take a not infinite time
QTRY_COMPARE_WITH_TIMEOUT(m_fallbackPkg.filePath("ui", "otherfile.qml"), m_pkg.filePath("ui", "otherfile.qml"), 1000);
}
QTEST_MAIN(FallbackPackageTest)

View File

@ -0,0 +1,46 @@
/******************************************************************************
* Copyright 2007 by Aaron Seigo <aseigo@kde.org> *
* Copyright 2014 Marco Martin <mart@kde.org> *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public License *
* along with this library; see the file COPYING.LIB. If not, write to *
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, *
* Boston, MA 02110-1301, USA. *
*******************************************************************************/
#ifndef FALLBACKPACKAGETEST_H
#include <QtTest/QtTest>
#include "plasma/package.h"
class FallbackPackageTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void beforeFallback();
void afterFallback();
void cycle();
private:
Plasma::Package m_pkg;
Plasma::Package m_fallbackPkg;
QString m_packagePath;
QString m_fallPackagePath;
};
#endif

View File

@ -56,4 +56,3 @@ Comment[sv]=Plasma skalkomponenter
Comment[uk]=Компоненти оболонки Плазми
Comment[x-test]=xxPlasma Shell Componentsxx
Comment[zh_TW]=Plasma Shell

View File

@ -49,6 +49,7 @@ Package::Package(PackageStructure *structure)
: d(new PackagePrivate())
{
d->structure = structure;
if (d->structure) {
d->structure.data()->initPackage(this);
}
@ -188,6 +189,27 @@ void Package::setDefaultPackageRoot(const QString &packageRoot)
}
}
void Package::setFallbackPackage(const Plasma::Package &package)
{
if ((d->fallbackPackage && d->fallbackPackage->path() == package.path() && d->fallbackPackage->metadata() == package.metadata()) ||
//can't be fallback of itself
(package.path() == path() && package.metadata() == metadata()) ||
d->hasCycle(package)) {
return;
}
d->fallbackPackage = new Package(package);
}
Plasma::Package Package::fallbackPackage() const
{
if (d->fallbackPackage) {
return (*d->fallbackPackage);
} else {
return Package();
}
}
QString Package::servicePrefix() const
{
return d->servicePrefix;
@ -290,7 +312,7 @@ QString Package::filePath(const char *fileType, const QString &filename) const
{
if (!d->valid) {
//qDebug() << "package is not valid";
return QString();
return d->fallbackFilePath(fileType, filename);
}
const QString discoveryKey(fileType + filename);
@ -305,7 +327,7 @@ QString Package::filePath(const char *fileType, const QString &filename) const
//qDebug()<<d->contents.keys();
if (!d->contents.contains(fileType)) {
//qDebug() << "package does not contain" << fileType << filename;
return QString();
return d->fallbackFilePath(fileType, filename);
}
paths = d->contents[fileType].paths;
@ -313,7 +335,7 @@ QString Package::filePath(const char *fileType, const QString &filename) const
if (paths.isEmpty()) {
//qDebug() << "no matching path came of it, while looking for" << fileType << filename;
d->discoveries.insert(discoveryKey, QString());
return QString();
return d->fallbackFilePath(fileType, filename);
}
} else {
//when filetype is empty paths is always empty, so try with an empty string
@ -356,7 +378,7 @@ QString Package::filePath(const char *fileType, const QString &filename) const
}
//qDebug() << fileType << filename << "does not exist in" << prefixes << "at root" << d->path;
return QString();
return d->fallbackFilePath(fileType, filename);
}
QStringList Package::entryList(const char *key) const
@ -500,6 +522,7 @@ void Package::setPath(const QString &path)
}
}
// if nothing did change, then we go back to the old dptr
if (d->path == previousPath) {
d = oldD;
@ -511,6 +534,8 @@ void Package::setPath(const QString &path)
delete d->metadata;
d->metadata = 0;
QString fallback;
// uh-oh, but we didn't end up with anything valid, so we sadly reset ourselves
// to futility.
if (!d->valid) {
@ -764,6 +789,7 @@ KJob *Package::uninstall(const QString &packageName, const QString &packageRoot)
PackagePrivate::PackagePrivate()
: QSharedData(),
servicePrefix("plasma-applet-"),
fallbackPackage(0),
metadata(0),
externalPaths(false),
valid(false),
@ -786,6 +812,7 @@ PackagePrivate::~PackagePrivate()
dir.removeRecursively();
}
delete metadata;
delete fallbackPackage;
}
PackagePrivate &PackagePrivate::operator=(const PackagePrivate &rhs)
@ -795,6 +822,11 @@ PackagePrivate &PackagePrivate::operator=(const PackagePrivate &rhs)
}
structure = rhs.structure;
if (rhs.fallbackPackage) {
fallbackPackage = new Package(*rhs.fallbackPackage);
} else {
fallbackPackage = 0;
}
path = rhs.path;
contentsPrefixPaths = rhs.contentsPrefixPaths;
servicePrefix = rhs.servicePrefix;
@ -874,4 +906,38 @@ void PackagePrivate::createPackageMetadata(const QString &path)
metadata = new KPluginInfo(metadataPath);
}
QString PackagePrivate::fallbackFilePath(const char *key, const QString &filename) const
{
//don't fallback if the package isn't valid and never fallback the metadata file
if (qstrcmp(key, "metadata") != 0 && fallbackPackage && fallbackPackage->isValid()) {
return fallbackPackage->filePath(key, filename);
} else {
return QString();
}
}
bool PackagePrivate::hasCycle(const Plasma::Package &package)
{
if (!package.d->fallbackPackage) {
return false;
}
//This is the Floyd cycle detection algorithm
//http://en.wikipedia.org/wiki/Cycle_detection#Tortoise_and_hare
Plasma::Package *slowPackage = const_cast<Plasma::Package *>(&package);
Plasma::Package *fastPackage = const_cast<Plasma::Package *>(&package);
while (fastPackage && fastPackage->d->fallbackPackage) {
//consider two packages the same if they have the same metadata
if ((fastPackage->d->fallbackPackage->metadata().isValid() && fastPackage->d->fallbackPackage->metadata() == slowPackage->metadata()) ||
(fastPackage->d->fallbackPackage->d->fallbackPackage && fastPackage->d->fallbackPackage->d->fallbackPackage->metadata().isValid() && fastPackage->d->fallbackPackage->d->fallbackPackage->metadata() == slowPackage->metadata())) {
qWarning() << "Warning: the fallback chain of " << package.metadata().pluginName() << "contains a cyclical dependency.";
return true;
}
fastPackage = fastPackage->d->fallbackPackage->d->fallbackPackage;
slowPackage = slowPackage->d->fallbackPackage;
}
return false;
}
} // Namespace

View File

@ -290,6 +290,20 @@ public:
*/
void setDefaultPackageRoot(const QString &packageRoot);
/**
* Sets the fallback package root path
* If a file won't be found in this package, it will search it in the package
* with the same structure identified by path
* It is intended to be used by the packageStructure
* @param path package root path @see setPath
*/
void setFallbackPackage(const Plasma::Package &package);
/**
* @return The fallback package root path
*/
Plasma::Package fallbackPackage() const;
// Content structure description methods
/**
* @return all directories registered as part of this Package's structure
@ -328,6 +342,7 @@ public:
private:
QExplicitlySharedDataPointer<PackagePrivate> d;
friend class PackagePrivate;
};
}

View File

@ -73,6 +73,8 @@ public:
void createPackageMetadata(const QString &path);
QString unpack(const QString &filePath);
void updateHash(const QString &basePath, const QString &subPath, const QDir &dir, QCryptographicHash &hash);
QString fallbackFilePath(const char *key, const QString &filename = QString()) const;
bool hasCycle(const Plasma::Package &package);
QWeakPointer<PackageStructure> structure;
QString path;
@ -82,6 +84,7 @@ public:
QString servicePrefix;
QHash<QString, QString> discoveries;
QHash<QByteArray, ContentStructure> contents;
Package *fallbackPackage;
#ifndef PLASMA_NO_PACKAGE_EXTRADATA
QStringList mimeTypes;
#endif