Iikka Eklund 2af57eb8c8 Merge remote-tracking branch 'origin/2.0'
Conflicts:
	installerfw.pri
	src/libs/kdtools/kdgenericfactory.cpp
	src/libs/kdtools/kdgenericfactory.h
	src/libs/kdtools/kdupdaterapplication.cpp
	src/libs/kdtools/kdupdaterapplication.h
	src/libs/kdtools/kdupdaterupdatesourcesinfo.cpp
	src/libs/kdtools/kdupdaterupdatesourcesinfo.h
	sync.profile

Change-Id: Ifdc8b065f89b7e241bd3788ed79768e21888161f
2016-01-20 14:18:30 +02:00

546 lines
19 KiB
C++

/**************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
**
** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** As a special exception, The Qt Company gives you certain additional
** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
**
**************************************************************************/
#include <component.h>
#include <errors.h>
#include <kdupdaterupdateoperationfactory.h>
#include <packagemanagercore.h>
#include <packagemanagergui.h>
#include <scriptengine.h>
#include <QTest>
#include <QSet>
#include <QFile>
#include <QString>
using namespace QInstaller;
class TestGui : public QInstaller::PackageManagerGui
{
Q_OBJECT
public:
explicit TestGui(QInstaller::PackageManagerCore *core)
: PackageManagerGui(core, 0)
{
setPage(PackageManagerCore::Introduction, new IntroductionPage(core));
setPage(PackageManagerCore::ComponentSelection, new ComponentSelectionPage(core));
setPage(PackageManagerCore::InstallationFinished, new FinishedPage(core));
foreach (const int id, pageIds()) {
packageManagerCore()->controlScriptEngine()->addToGlobalObject(page(id));
packageManagerCore()->componentScriptEngine()->addToGlobalObject(page(id));
}
}
virtual void init() {}
void callProtectedDelayedExecuteControlScript(int id)
{
executeControlScript(id);
}
};
class DynamicPageGui : public PackageManagerGui
{
Q_OBJECT
public:
explicit DynamicPageGui(PackageManagerCore *core)
: PackageManagerGui(core)
{}
void init() {
m_widget = new QWidget;
m_widget->setObjectName("Widget");
QWidget *button = new QWidget(m_widget);
button->setObjectName("Button");
packageManagerCore()->wizardPageInsertionRequested(m_widget,
PackageManagerCore::Introduction);
}
QWidget *widget() const { return m_widget; }
private:
QWidget *m_widget;
};
class EnteringPage : public PackageManagerPage
{
Q_OBJECT
Q_PROPERTY(QStringList invocationOrder READ invocationOrder)
public:
explicit EnteringPage(PackageManagerCore *core)
: PackageManagerPage(core)
{
setObjectName(QLatin1String("EnteringPage"));
}
QStringList invocationOrder() const {
return m_invocationOrder;
}
public slots:
Q_INVOKABLE void enteringInvoked() {
m_invocationOrder << QLatin1String("Entering");
}
Q_INVOKABLE void callbackInvoked() {
m_invocationOrder << QLatin1String("Callback");
}
protected:
void entering() {
enteringInvoked();
}
private:
QStringList m_invocationOrder;
};
class EnteringGui : public PackageManagerGui
{
Q_OBJECT
public:
explicit EnteringGui(PackageManagerCore *core)
: PackageManagerGui(core)
{}
EnteringPage *enteringPage() const {
return m_enteringPage;
}
void init() {
m_enteringPage = new EnteringPage(packageManagerCore());
setPage(0, m_enteringPage);
}
private:
EnteringPage *m_enteringPage;
};
class EmitSignalObject : public QObject
{
Q_OBJECT
public:
EmitSignalObject() {}
~EmitSignalObject() {}
void produceSignal() { emit emitted(); }
signals:
void emitted();
};
class EmptyArgOperation : public KDUpdater::UpdateOperation
{
public:
EmptyArgOperation() {
setName("EmptyArg");
}
void backup() {}
bool performOperation() {
return true;
}
bool undoOperation() {
return true;
}
bool testOperation() {
return true;
}
UpdateOperation *clone() const {
return 0;
}
};
// -- tst_ScriptEngine
class tst_ScriptEngine : public QObject
{
Q_OBJECT
private slots:
void initTestCase()
{
m_component = new Component(&m_core);
// append the component to the package manager which deletes it at destructor
// (it calls clearAllComponentLists which calls qDeleteAll(m_rootComponents);)
m_core.appendRootComponent(m_component);
m_component->setValue("Default", "Script");
m_component->setValue(scName, "component.test.name");
Component *component = new Component(&m_core);
component->setValue(scName, "component.test.addOperation");
m_core.appendRootComponent(component);
m_scriptEngine = m_core.componentScriptEngine();
}
void testDefaultScriptEngineValues()
{
const QJSValue global = m_scriptEngine->globalObject();
QCOMPARE(global.hasProperty(QLatin1String("print")), true);
QCOMPARE(global.hasProperty(QLatin1String("installer")), true);
QCOMPARE(global.property(QLatin1String("installer"))
.hasProperty(QLatin1String("componentByName")), true);
QCOMPARE(global.property(QLatin1String("installer"))
.hasProperty(QLatin1String("components")), true);
QCOMPARE(global.hasProperty(QLatin1String("console")), true);
QCOMPARE(global.property(QLatin1String("console"))
.hasProperty(QLatin1String("log")), true);
QCOMPARE(global.hasProperty(QLatin1String("QFileDialog")), true);
QCOMPARE(global.property(QLatin1String("QFileDialog"))
.hasProperty(QLatin1String("getExistingDirectory")), true);
QCOMPARE(global.property(QLatin1String("QFileDialog"))
.hasProperty(QLatin1String("getOpenFileName")), true);
QCOMPARE(global.hasProperty(QLatin1String("InstallerProxy")), true);
QCOMPARE(global.property(QLatin1String("InstallerProxy"))
.hasProperty(QLatin1String("componentByName")), true);
QCOMPARE(global.property(QLatin1String("InstallerProxy"))
.hasProperty(QLatin1String("components")), true);
QCOMPARE(global.hasProperty(QLatin1String("QDesktopServices")), true);
QCOMPARE(global.property(QLatin1String("QDesktopServices"))
.hasProperty(QLatin1String("openUrl")), true);
QCOMPARE(global.property(QLatin1String("QDesktopServices"))
.hasProperty(QLatin1String("displayName")), true);
QCOMPARE(global.property(QLatin1String("QDesktopServices"))
.hasProperty(QLatin1String("storageLocation")), true);
QCOMPARE(global.hasProperty(QLatin1String("buttons")), true);
QCOMPARE(global.hasProperty(QLatin1String("QInstaller")), true);
QCOMPARE(global.hasProperty(QLatin1String("QMessageBox")), true);
QCOMPARE(global.hasProperty(QLatin1String("gui")), true);
QCOMPARE(global.hasProperty(QLatin1String("qsTr")), true);
QCOMPARE(global.hasProperty(QLatin1String("systemInfo")), true);
QJSValue sinfo = global.property(QLatin1String("systemInfo"));
QCOMPARE(sinfo.property(QLatin1String("currentCpuArchitecture")).toString(),
QSysInfo::currentCpuArchitecture());
QCOMPARE(sinfo.property(QLatin1String("kernelType")).toString(), QSysInfo::kernelType());
QCOMPARE(sinfo.property(QLatin1String("kernelVersion")).toString(),
QSysInfo::kernelVersion());
QCOMPARE(sinfo.property(QLatin1String("productType")).toString(), QSysInfo::productType());
QCOMPARE(sinfo.property(QLatin1String("productVersion")).toString(),
QSysInfo::productVersion());
QCOMPARE(sinfo.property(QLatin1String("prettyProductName")).toString(),
QSysInfo::prettyProductName());
}
void testBrokenJSMethodConnect()
{
#if QT_VERSION <= 0x50400
QSKIP("Behavior changed from 5.4.1 onwards");
#endif
EmitSignalObject emiter;
m_scriptEngine->globalObject().setProperty(QLatin1String("emiter"),
m_scriptEngine->newQObject(&emiter));
QJSValue context = m_scriptEngine->loadInContext(QLatin1String("BrokenConnect"),
":///data/broken_connect.qs");
if (context.isError()) {
QFAIL(qPrintable(QString::fromLatin1("ScriptEngine error:\n %1").arg(
context.toString())));
}
QCOMPARE(context.isError(), false);
// ignore Output from script
setExpectedScriptOutput("function receive()");
QTest::ignoreMessage(QtWarningMsg, ":10: ReferenceError: foo is not defined");
emiter.produceSignal();
const QJSValue value = m_scriptEngine->evaluate("");
QCOMPARE(value.isError(), false);
}
void testScriptPrint()
{
setExpectedScriptOutput("test");
const QJSValue value = m_scriptEngine->evaluate("print(\"test\");");
if (value.isError()) {
QFAIL(qPrintable(QString::fromLatin1("ScriptEngine error:\n %1").arg(
value.toString())));
}
}
void testExistingInstallerObject()
{
setExpectedScriptOutput("object");
const QJSValue value = m_scriptEngine->evaluate("print(typeof installer)");
if (value.isError()) {
QFAIL(qPrintable(QString::fromLatin1("ScriptEngine error:\n %1").arg(
value.toString())));
}
}
void testComponentByName()
{
const QString script = QString::fromLatin1("var component = installer.componentByName('%1');"
"\n"
"print(component.name);").arg(m_component->name());
setExpectedScriptOutput("component.test.name");
const QJSValue value = m_scriptEngine->evaluate(script);
if (value.isError()) {
QFAIL(qPrintable(QString::fromLatin1("ScriptEngine error:\n %1").arg(
value.toString())));
}
}
void testComponentByWrongName()
{
const QString script = QString::fromLatin1( "var component = installer.componentByName('%1');"
"\n"
"print(brokenComponent.name);").arg("NotExistingComponentName");
const QJSValue value = m_scriptEngine->evaluate(script);
QCOMPARE(value.isError(), true);
}
void testComponents()
{
const QString script = QString::fromLatin1("var components = installer.components();"
"\n"
"print(components[0].name);");
setExpectedScriptOutput("component.test.name");
const QJSValue value = m_scriptEngine->evaluate(script);
if (value.isError()) {
QFAIL(qPrintable(QString::fromLatin1("ScriptEngine error:\n %1").arg(
value.toString())));
}
}
void loadSimpleComponentScript()
{
try {
// ignore retranslateUi which is called by loadComponentScript
setExpectedScriptOutput("Component constructor - OK");
setExpectedScriptOutput("retranslateUi - OK");
m_component->loadComponentScript(":///data/component1.qs");
setExpectedScriptOutput("retranslateUi - OK");
m_component->languageChanged();
setExpectedScriptOutput("createOperationsForPath - OK");
m_component->createOperationsForPath(":///data/");
setExpectedScriptOutput("createOperationsForArchive - OK");
// ignore createOperationsForPath which is called by createOperationsForArchive
setExpectedScriptOutput("createOperationsForPath - OK");
m_component->createOperationsForArchive("test.7z");
setExpectedScriptOutput("beginInstallation - OK");
m_component->beginInstallation();
setExpectedScriptOutput("createOperations - OK");
m_component->createOperations();
setExpectedScriptOutput("isDefault - OK");
bool returnIsDefault = m_component->isDefault();
QCOMPARE(returnIsDefault, false);
} catch (const Error &error) {
QFAIL(qPrintable(error.message()));
}
}
void loadBrokenComponentScript()
{
Component *testComponent = new Component(&m_core);
testComponent->setValue(scName, "broken.component");
// m_core becomes the owner of testComponent, it will delete it in the destructor
m_core.appendRootComponent(testComponent);
try {
// ignore Output from script
setExpectedScriptOutput("script function: Component");
testComponent->loadComponentScript(":///data/component2.qs");
} catch (const Error &error) {
const QString debugMessage(
QString("create Error-Exception: \"Exception while loading the component script \"%1\": "
"ReferenceError: broken is not defined\"").arg(QDir::toNativeSeparators(":///data/component2.qs")));
QVERIFY2(debugMessage.contains(error.message()), "(ReferenceError: broken is not defined)");
}
}
void loadComponentUserInterfaces()
{
try {
setExpectedScriptOutput("checked: false");
m_component->loadUserInterfaces(QDir(":///data"), QStringList() << QLatin1String("form.ui"));
m_component->loadComponentScript(":///data/userinterface.qs");
} catch (const Error &error) {
QFAIL(qPrintable(error.message()));
}
}
void loadSimpleAutoRunScript()
{
try {
TestGui testGui(&m_core);
setExpectedScriptOutput("Loaded control script \":///data/auto-install.qs\" ");
testGui.loadControlScript(":///data/auto-install.qs");
QCOMPARE(m_core.value("GuiTestValue"), QString("hello"));
testGui.show();
// inside the auto-install script we are clicking the next button, with a not existing button
QTest::ignoreMessage(QtWarningMsg, "Button with type: \"unknown button\" not found! ");
testGui.callProtectedDelayedExecuteControlScript(PackageManagerCore::ComponentSelection);
setExpectedScriptOutput("FinishedPageCallback - OK");
testGui.callProtectedDelayedExecuteControlScript(PackageManagerCore::InstallationFinished);
} catch (const Error &error) {
QFAIL(qPrintable(error.message()));
}
}
void testDynamicPage()
{
DynamicPageGui gui(&m_core);
gui.init();
setExpectedScriptOutput("Loaded control script \":///data/dynamicpage.qs\" ");
gui.loadControlScript(":///data/dynamicpage.qs");
gui.callControlScriptMethod("ReadAndSetValues");
QCOMPARE(m_core.value("DynamicWidget.final"), QString("false"));
QCOMPARE(gui.widget()->property("final").toString(), QString("false"));
QCOMPARE(m_core.value("DynamicWidget.commit"), QString("false"));
QCOMPARE(gui.widget()->property("commit").toString(), QString("false"));
QCOMPARE(m_core.value("DynamicWidget.complete"), QString("true"));
QCOMPARE(gui.widget()->property("complete").toString(), QString("true"));
gui.widget()->setProperty("final", true);
gui.widget()->setProperty("commit", true);
gui.widget()->setProperty("complete", false);
gui.callControlScriptMethod("ReadAndSetValues");
QCOMPARE(m_core.value("DynamicWidget.final"), QString("true"));
QCOMPARE(gui.widget()->property("final").toString(), QString("true"));
QCOMPARE(m_core.value("DynamicWidget.commit"), QString("true"));
QCOMPARE(gui.widget()->property("commit").toString(), QString("true"));
QCOMPARE(m_core.value("DynamicWidget.complete"), QString("false"));
QCOMPARE(gui.widget()->property("complete").toString(), QString("false"));
gui.callControlScriptMethod("UpdateAndSetValues");
QCOMPARE(m_core.value("DynamicWidget.final"), QString("false"));
QCOMPARE(gui.widget()->property("final").toString(), QString("false"));
QCOMPARE(m_core.value("DynamicWidget.commit"), QString("false"));
QCOMPARE(gui.widget()->property("commit").toString(), QString("false"));
QCOMPARE(m_core.value("DynamicWidget.complete"), QString("true"));
QCOMPARE(gui.widget()->property("complete").toString(), QString("true"));
}
void checkEnteringCalledBeforePageCallback()
{
EnteringGui gui(&m_core);
gui.init();
setExpectedScriptOutput("Loaded control script \":///data/enteringpage.qs\" ");
gui.loadControlScript(":///data/enteringpage.qs");
gui.show();
EnteringPage *enteringPage = gui.enteringPage();
QStringList expectedOrder;
expectedOrder << QLatin1String("Entering") << QLatin1String("Callback");
QCOMPARE(enteringPage->invocationOrder(), expectedOrder);
}
void testAddOperation_AddElevatedOperation()
{
using namespace KDUpdater;
UpdateOperationFactory &factory = UpdateOperationFactory::instance();
factory.registerUpdateOperation<EmptyArgOperation>(QLatin1String("EmptyArg"));
try {
m_core.setPackageManager();
Component *component = m_core.componentByName("component.test.addOperation");
component->loadComponentScript(":///data/addOperation.qs");
setExpectedScriptOutput("\"Component::createOperations()\"");
component->createOperations();
const OperationList operations = component->operations();
QCOMPARE(operations.count(), 8);
struct {
const char* args[3];
const char* operator[](int i) const {
return args[i];
}
} expectedArgs[] = {
{ "Arg", "Arg2", "" }, { "Arg", "", "Arg3" }, { "", "Arg2", "Arg3" }, { "Arg", "Arg2", "" },
{ "eArg", "eArg2", "" }, { "eArg", "", "eArg3" }, { "", "eArg2", "eArg3" }, { "eArg", "eArg2", "" }
};
for (int i = 0; i < operations.count(); ++i) {
const QStringList arguments = operations[i]->arguments();
QCOMPARE(arguments.count(), 3);
for (int j = 0; j < 3; ++j)
QCOMPARE(arguments[j], QString(expectedArgs[i][j]));
}
} catch (const QInstaller::Error &error) {
QFAIL(qPrintable(error.message()));
}
}
private:
void setExpectedScriptOutput(const char *message)
{
// Using setExpectedScriptOutput(...); inside the test method
// as a simple test that the scripts are called.
QTest::ignoreMessage(QtDebugMsg, message);
}
PackageManagerCore m_core;
Component *m_component;
ScriptEngine *m_scriptEngine;
};
QTEST_MAIN(tst_ScriptEngine)
#include "tst_scriptengine.moc"