diff options
author | Gunnar Sletta <gunnar.sletta@nokia.com> | 2011-09-12 08:12:58 +0200 |
---|---|---|
committer | Gunnar Sletta <gunnar.sletta@nokia.com> | 2011-09-12 08:12:58 +0200 |
commit | d77218522eb480c8d528de18049cd7b604cdeb2a (patch) | |
tree | a6433a8e9205a95006eae10b53285a0e3487423d /tests | |
parent | 589c8445e2623ef8e0b8294d7c558a2948b2a5e3 (diff) | |
parent | d5686fa2ac2248d5a31237573fa08697f18f035f (diff) |
Merge branch 'master' into refactor
Conflicts:
examples/declarative/cppextensions/qwidgets/qwidgets.pro
examples/declarative/minehunt/main.cpp
examples/declarative/minehunt/minehunt.pro
src/declarative/items/context2d/qsgcontext2d.cpp
src/declarative/items/qsgflickable.cpp
src/declarative/items/qsgtextedit.cpp
src/declarative/items/qsgtextinput.cpp
src/declarative/particles/qsgangleddirection.cpp
src/declarative/particles/qsgcumulativedirection.cpp
src/declarative/particles/qsgcumulativedirection_p.h
src/declarative/particles/qsgfollowemitter.cpp
src/declarative/particles/qsgmodelparticle.cpp
src/declarative/particles/qsgparticlesystem.cpp
src/qtquick1/util/qdeclarativeview.h
tests/auto/declarative/examples/examples.pro
tests/auto/declarative/qsgfocusscope/tst_qsgfocusscope.cpp
Change-Id: Ib4be2a5e742dee1a399d73da97161736f77448e5
Diffstat (limited to 'tests')
59 files changed, 4306 insertions, 317 deletions
diff --git a/tests/auto/declarative/declarative.pro b/tests/auto/declarative/declarative.pro index 8563dc6fe6..fa075459b5 100644 --- a/tests/auto/declarative/declarative.pro +++ b/tests/auto/declarative/declarative.pro @@ -31,9 +31,10 @@ PRIVATETESTS += \ qdeclarativebehaviors \ qdeclarativebinding \ qdeclarativeconnection \ - qdeclarativedebug \ + qdeclarativeenginedebug \ qdeclarativedebugclient \ qdeclarativedebugservice \ + qdeclarativedebugjs \ qdeclarativeecmascript \ qdeclarativeimageprovider \ qdeclarativeinstruction \ diff --git a/tests/auto/declarative/examples/examples.pro b/tests/auto/declarative/examples/examples.pro index 75adbb53ba..b1e120094d 100644 --- a/tests/auto/declarative/examples/examples.pro +++ b/tests/auto/declarative/examples/examples.pro @@ -1,20 +1,9 @@ load(qttest_p4) -contains(QT_CONFIG,declarative): QT += declarative qtquick1 +contains(QT_CONFIG,declarative): QT += declarative macx:CONFIG -= app_bundle -SOURCES += tst_examples.cpp - -include(../../../../tools/qmlviewer/qml.pri) - -include(../symbianlibs.pri) - -symbian: { - importFiles.files = data - importFiles.path = . - DEPLOYMENT += importFiles -} else { - DEFINES += SRCDIR=\\\"$$PWD\\\" -} +SOURCES += tst_examples.cpp +DEFINES += SRCDIR=\\\"$$PWD\\\" CONFIG += parallel_test diff --git a/tests/auto/declarative/examples/tst_examples.cpp b/tests/auto/declarative/examples/tst_examples.cpp index e2edc3e93c..563aa3ce84 100644 --- a/tests/auto/declarative/examples/tst_examples.cpp +++ b/tests/auto/declarative/examples/tst_examples.cpp @@ -38,21 +38,15 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ + #include <qtest.h> #include <QLibraryInfo> #include <QDir> #include <QProcess> #include <QDebug> -#include "qmlruntime.h" -#include <QDeclarativeView> #include <QSGView> #include <QDeclarativeError> -#ifdef Q_OS_SYMBIAN -// In Symbian OS test data is located in applications private dir -#define SRCDIR "." -#endif - class tst_examples : public QObject { Q_OBJECT @@ -60,8 +54,6 @@ public: tst_examples(); private slots: - void examples_data(); - void examples(); void sgexamples_data(); void sgexamples(); @@ -77,6 +69,9 @@ tst_examples::tst_examples() { // Add directories you want excluded here + // Not run in QSGView + excludedDirs << "examples/declarative/qtquick1"; + // These snippets are not expected to run on their own. excludedDirs << "doc/src/snippets/declarative/visualdatamodel_rootindex"; excludedDirs << "doc/src/snippets/declarative/qtbinding"; @@ -185,7 +180,12 @@ that they start and exit cleanly. Examples are any .qml files under the examples/ directory that start with a lower case letter. */ -void tst_examples::examples_data() +static void silentErrorsMsgHandler(QtMsgType, const char *) +{ +} + + +void tst_examples::sgexamples_data() { QTest::addColumn<QString>("file"); @@ -200,37 +200,8 @@ void tst_examples::examples_data() QTest::newRow(qPrintable(file)) << file; } -static void silentErrorsMsgHandler(QtMsgType, const char *) -{ -} - -void tst_examples::examples() -{ - QFETCH(QString, file); - - QDeclarativeViewer viewer; - - QtMsgHandler old = qInstallMsgHandler(silentErrorsMsgHandler); - QVERIFY(viewer.open(file)); - qInstallMsgHandler(old); - - if (viewer.view()->status() == QDeclarativeView::Error) - qWarning() << viewer.view()->errors(); - - QCOMPARE(viewer.view()->status(), QDeclarativeView::Ready); - viewer.show(); - - QTest::qWaitForWindowShown(&viewer); -} - -void tst_examples::sgexamples_data() -{ - examples_data(); -} - void tst_examples::sgexamples() { - qputenv("QMLSCENE_IMPORT_NAME", "quick1"); QFETCH(QString, file); QSGView view; diff --git a/tests/auto/declarative/qdeclarativeanimations/data/pathAnimation.qml b/tests/auto/declarative/qdeclarativeanimations/data/pathAnimation.qml new file mode 100644 index 0000000000..d2006a1c6a --- /dev/null +++ b/tests/auto/declarative/qdeclarativeanimations/data/pathAnimation.qml @@ -0,0 +1,27 @@ +import QtQuick 2.0 + +Rectangle { + width: 400 + height: 400 + + Rectangle { + id: redRect + color: "red" + width: 100; height: 100 + x: 50; y: 50 + } + + PathAnimation { + target: redRect + duration: 100; + path: Path { + startX: 50; startY: 50 + PathCubic { + x: 300; y: 300 + + control1X: 300; control1Y: 50 + control2X: 50; control2Y: 300 + } + } + } +} diff --git a/tests/auto/declarative/qdeclarativeanimations/data/pathInterpolator.qml b/tests/auto/declarative/qdeclarativeanimations/data/pathInterpolator.qml new file mode 100644 index 0000000000..0104412d7c --- /dev/null +++ b/tests/auto/declarative/qdeclarativeanimations/data/pathInterpolator.qml @@ -0,0 +1,13 @@ +import QtQuick 2.0 + +PathInterpolator { + path: Path { + startX: 50; startY: 50 + PathCubic { + x: 300; y: 300 + + control1X: 300; control1Y: 50 + control2X: 50; control2Y: 300 + } + } +} diff --git a/tests/auto/declarative/qdeclarativeanimations/data/pathTransition.qml b/tests/auto/declarative/qdeclarativeanimations/data/pathTransition.qml new file mode 100644 index 0000000000..55ffc33f95 --- /dev/null +++ b/tests/auto/declarative/qdeclarativeanimations/data/pathTransition.qml @@ -0,0 +1,41 @@ +import QtQuick 2.0 + +Rectangle { + width: 800 + height: 800 + + Rectangle { + id: redRect; objectName: "redRect" + color: "red" + width: 50; height: 50 + x: 500; y: 50 + } + + states: State { + name: "moved" + PropertyChanges { + target: redRect + x: 100; y: 700 + } + } + + transitions: Transition { + to: "moved"; reversible: true + PathAnimation { + id: pathAnim + target: redRect + duration: 300 + path: Path { + PathCurve { x: 100; y: 100 } + PathCurve { x: 200; y: 350 } + PathCurve { x: 600; y: 500 } + PathCurve {} + } + } + } + + MouseArea { + anchors.fill: parent + onClicked: parent.state = parent.state == "moved" ? "" : "moved" + } +} diff --git a/tests/auto/declarative/qdeclarativeanimations/tst_qdeclarativeanimations.cpp b/tests/auto/declarative/qdeclarativeanimations/tst_qdeclarativeanimations.cpp index 367de014b4..df840e9d5c 100644 --- a/tests/auto/declarative/qdeclarativeanimations/tst_qdeclarativeanimations.cpp +++ b/tests/auto/declarative/qdeclarativeanimations/tst_qdeclarativeanimations.cpp @@ -44,6 +44,8 @@ #include <QtDeclarative/qsgview.h> #include <QtDeclarative/private/qsgrectangle_p.h> #include <QtDeclarative/private/qdeclarativeanimation_p.h> +#include <QtDeclarative/private/qsganimation_p.h> +#include <QtDeclarative/private/qdeclarativepathinterpolator_p.h> #include <QtDeclarative/private/qsgitem_p.h> #include <QVariantAnimation> #include <QEasingCurve> @@ -68,6 +70,8 @@ private slots: void simpleNumber(); void simpleColor(); void simpleRotation(); + void simplePath(); + void pathInterpolator(); void alwaysRunToEnd(); void complete(); void resume(); @@ -77,6 +81,7 @@ private slots: void mixedTypes(); void properties(); void propertiesTransition(); + void pathTransition(); void disabledTransition(); void invalidDuration(); void attached(); @@ -213,6 +218,62 @@ void tst_qdeclarativeanimations::simpleRotation() QCOMPARE(rect.rotation(), qreal(135)); } +void tst_qdeclarativeanimations::simplePath() +{ + QDeclarativeEngine engine; + QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/pathAnimation.qml")); + QSGRectangle *rect = qobject_cast<QSGRectangle*>(c.create()); + QVERIFY(rect); + + QSGRectangle *redRect = rect->findChild<QSGRectangle*>(); + QVERIFY(redRect); + QSGPathAnimation *pathAnim = rect->findChild<QSGPathAnimation*>(); + QVERIFY(pathAnim); + + pathAnim->start(); + pathAnim->pause(); + + pathAnim->setCurrentTime(50); + QCOMPARE(redRect->x(), qreal(175)); + QCOMPARE(redRect->y(), qreal(175)); + + pathAnim->setCurrentTime(100); + QCOMPARE(redRect->x(), qreal(300)); + QCOMPARE(redRect->y(), qreal(300)); + + //verify animation runs to end + pathAnim->start(); + QCOMPARE(redRect->x(), qreal(50)); + QCOMPARE(redRect->y(), qreal(50)); + QTRY_COMPARE(redRect->x(), qreal(300)); + QCOMPARE(redRect->y(), qreal(300)); +} + +void tst_qdeclarativeanimations::pathInterpolator() +{ + QDeclarativeEngine engine; + QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/pathInterpolator.qml")); + QDeclarativePathInterpolator *interpolator = qobject_cast<QDeclarativePathInterpolator*>(c.create()); + QVERIFY(interpolator); + + QCOMPARE(interpolator->progress(), qreal(0)); + QCOMPARE(interpolator->x(), qreal(50)); + QCOMPARE(interpolator->y(), qreal(50)); + QCOMPARE(interpolator->angle(), qreal(0)); + + interpolator->setProgress(.5); + QCOMPARE(interpolator->progress(), qreal(.5)); + QCOMPARE(interpolator->x(), qreal(175)); + QCOMPARE(interpolator->y(), qreal(175)); + QCOMPARE(interpolator->angle(), qreal(270)); + + interpolator->setProgress(1); + QCOMPARE(interpolator->progress(), qreal(1)); + QCOMPARE(interpolator->x(), qreal(300)); + QCOMPARE(interpolator->y(), qreal(300)); + QCOMPARE(interpolator->angle(), qreal(0)); +} + void tst_qdeclarativeanimations::alwaysRunToEnd() { QSGRectangle rect; @@ -577,6 +638,26 @@ void tst_qdeclarativeanimations::propertiesTransition() } +void tst_qdeclarativeanimations::pathTransition() +{ + QDeclarativeEngine engine; + QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/pathTransition.qml")); + QSGRectangle *rect = qobject_cast<QSGRectangle*>(c.create()); + QVERIFY(rect); + + QSGRectangle *myRect = rect->findChild<QSGRectangle*>("redRect"); + QVERIFY(myRect); + + QSGItemPrivate::get(rect)->setState("moved"); + QTRY_VERIFY(myRect->x() < 500 && myRect->x() > 100 && myRect->y() > 50 && myRect->y() < 700 ); //animation started + QTRY_VERIFY(qFuzzyCompare(myRect->x(), qreal(100)) && qFuzzyCompare(myRect->y(), qreal(700))); + QTest::qWait(100); + + QSGItemPrivate::get(rect)->setState(""); + QTRY_VERIFY(myRect->x() < 500 && myRect->x() > 100 && myRect->y() > 50 && myRect->y() < 700 ); //animation started + QTRY_VERIFY(qFuzzyCompare(myRect->x(), qreal(500)) && qFuzzyCompare(myRect->y(), qreal(50))); +} + void tst_qdeclarativeanimations::disabledTransition() { QDeclarativeEngine engine; diff --git a/tests/auto/declarative/qdeclarativeconnection/data/moduleapi-target.qml b/tests/auto/declarative/qdeclarativeconnection/data/moduleapi-target.qml new file mode 100644 index 0000000000..8803f24542 --- /dev/null +++ b/tests/auto/declarative/qdeclarativeconnection/data/moduleapi-target.qml @@ -0,0 +1,22 @@ +import QtQuick 2.0 +import MyTestModuleApi 1.0 as MyTestModuleApi + +Item { + id: rootObject + objectName: "rootObject" + property int newIntPropValue: 12 + + property int moduleIntPropChangedCount: 0 + property int moduleOtherSignalCount: 0 + + function setModuleIntProp() { + MyTestModuleApi.intProp = newIntPropValue; + newIntPropValue = newIntPropValue + 1; + } + + Connections { + target: MyTestModuleApi + onIntPropChanged: moduleIntPropChangedCount = moduleIntPropChangedCount + 1; + onOtherSignal: moduleOtherSignalCount = moduleOtherSignalCount + 1; + } +} diff --git a/tests/auto/declarative/qdeclarativeconnection/tst_qdeclarativeconnection.cpp b/tests/auto/declarative/qdeclarativeconnection/tst_qdeclarativeconnection.cpp index 37cce5c578..c726fde0e8 100644 --- a/tests/auto/declarative/qdeclarativeconnection/tst_qdeclarativeconnection.cpp +++ b/tests/auto/declarative/qdeclarativeconnection/tst_qdeclarativeconnection.cpp @@ -68,6 +68,7 @@ private slots: void unknownSignals(); void errors_data(); void errors(); + void moduleApiTarget(); private: QDeclarativeEngine engine; @@ -229,6 +230,69 @@ void tst_qdeclarativeconnection::errors() QCOMPARE(errors.at(0).description(), error); } + +class MyTestModuleApi : public QObject +{ +Q_OBJECT +Q_PROPERTY(int intProp READ intProp WRITE setIntProp NOTIFY intPropChanged) + +public: + MyTestModuleApi(QObject *parent = 0) : QObject(parent), m_intProp(0), m_changeCount(0) {} + ~MyTestModuleApi() {} + + Q_INVOKABLE int otherMethod(int val) { return val + 4; } + + int intProp() const { return m_intProp; } + void setIntProp(int val) + { + if (++m_changeCount % 3 == 0) emit otherSignal(); + m_intProp = val; emit intPropChanged(); + } + +signals: + void intPropChanged(); + void otherSignal(); + +private: + int m_intProp; + int m_changeCount; +}; + +static QObject *module_api_factory(QDeclarativeEngine *engine, QJSEngine *scriptEngine) +{ + Q_UNUSED(engine) + Q_UNUSED(scriptEngine) + MyTestModuleApi *api = new MyTestModuleApi(); + return api; +} + +// QTBUG-20937 +void tst_qdeclarativeconnection::moduleApiTarget() +{ + qmlRegisterModuleApi("MyTestModuleApi", 1, 0, module_api_factory); + QDeclarativeComponent component(&engine, QUrl(SRCDIR "/data/moduleapi-target.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("moduleIntPropChangedCount").toInt(), 0); + QCOMPARE(object->property("moduleOtherSignalCount").toInt(), 0); + + QMetaObject::invokeMethod(object, "setModuleIntProp"); + QCOMPARE(object->property("moduleIntPropChangedCount").toInt(), 1); + QCOMPARE(object->property("moduleOtherSignalCount").toInt(), 0); + + QMetaObject::invokeMethod(object, "setModuleIntProp"); + QCOMPARE(object->property("moduleIntPropChangedCount").toInt(), 2); + QCOMPARE(object->property("moduleOtherSignalCount").toInt(), 0); + + // the module API emits otherSignal every 3 times the int property changes. + QMetaObject::invokeMethod(object, "setModuleIntProp"); + QCOMPARE(object->property("moduleIntPropChangedCount").toInt(), 3); + QCOMPARE(object->property("moduleOtherSignalCount").toInt(), 1); + + delete object; +} + QTEST_MAIN(tst_qdeclarativeconnection) #include "tst_qdeclarativeconnection.moc" diff --git a/tests/auto/declarative/qdeclarativedebugclient/tst_qdeclarativedebugclient.cpp b/tests/auto/declarative/qdeclarativedebugclient/tst_qdeclarativedebugclient.cpp index d7f53c9620..41486877d6 100644 --- a/tests/auto/declarative/qdeclarativedebugclient/tst_qdeclarativedebugclient.cpp +++ b/tests/auto/declarative/qdeclarativedebugclient/tst_qdeclarativedebugclient.cpp @@ -47,10 +47,6 @@ #include <QtDeclarative/qdeclarativeengine.h> -#include <private/qdeclarativedebug_p.h> -#include <private/qdeclarativeenginedebug_p.h> -#include <private/qdeclarativedebugservice_p.h> - #include "../../../shared/util.h" #include "../shared/debugutil_p.h" @@ -144,7 +140,8 @@ int main(int argc, char *argv[]) char **_argv = new char*[_argc]; for (int i = 0; i < argc; ++i) _argv[i] = argv[i]; - _argv[_argc - 1] = "-qmljsdebugger=port:13770"; + char arg[] = "-qmljsdebugger=port:13770"; + _argv[_argc - 1] = arg; QApplication app(_argc, _argv); tst_QDeclarativeDebugClient tc; diff --git a/tests/auto/declarative/qdeclarativedebugjs/data/test.js b/tests/auto/declarative/qdeclarativedebugjs/data/test.js new file mode 100644 index 0000000000..230a4ea7de --- /dev/null +++ b/tests/auto/declarative/qdeclarativedebugjs/data/test.js @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +function printMessage(msg) +{ + print(msg); +} + +function add(a,b) +{ + //This is a comment and below is an empty line + + var out = a + b; + return out; +} diff --git a/tests/auto/declarative/qdeclarativedebugjs/data/test.qml b/tests/auto/declarative/qdeclarativedebugjs/data/test.qml new file mode 100644 index 0000000000..386b366cab --- /dev/null +++ b/tests/auto/declarative/qdeclarativedebugjs/data/test.qml @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import "test.js" as Script + +//DO NOT CHANGE + +Rectangle { + id: root + width: 10; height: 10; + Component.onCompleted: print("onCompleted") + + property int result:0 + + property int someValue: 10 + + function doSomething() { + var a = root.result; + var b = commonFunction(); + var c = [1,2,3]; + var d = Script.add(a,c[2]); + result += d; + doSomethingElse(); + } + + Timer { + interval: 4000; running: true; repeat: true + onTriggered: { + doSomething(); + Script.printMessage("onTriggered"); + } + } + + function commonFunction() { + console.log("commonFunction"); + return 5; + } + + function doSomethingElse() { + result = Script.add(result,8); + eval("print(root.result)"); + if (root.result > 15) + dummy(); + } + +} + diff --git a/tests/auto/declarative/qdeclarativedebugjs/qdeclarativedebugjs.pro b/tests/auto/declarative/qdeclarativedebugjs/qdeclarativedebugjs.pro new file mode 100644 index 0000000000..883f94111a --- /dev/null +++ b/tests/auto/declarative/qdeclarativedebugjs/qdeclarativedebugjs.pro @@ -0,0 +1,25 @@ +load(qttest_p4) +QT += declarative network script declarative-private +macx:CONFIG -= app_bundle + +HEADERS += ../shared/debugutil_p.h + +SOURCES += tst_qdeclarativedebugjs.cpp \ + ../shared/debugutil.cpp + +INCLUDEPATH += ../shared + +# QMAKE_CXXFLAGS = -fprofile-arcs -ftest-coverage +# LIBS += -lgcov + +symbian { + importFiles.files = data + importFiles.path = . + DEPLOYMENT += importFiles +} + +OTHER_FILES = data/test.qml \ + data/test.js + + +CONFIG += parallel_test diff --git a/tests/auto/declarative/qdeclarativedebugjs/tst_qdeclarativedebugjs.cpp b/tests/auto/declarative/qdeclarativedebugjs/tst_qdeclarativedebugjs.cpp new file mode 100644 index 0000000000..d76fa749a2 --- /dev/null +++ b/tests/auto/declarative/qdeclarativedebugjs/tst_qdeclarativedebugjs.cpp @@ -0,0 +1,1712 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qtest.h> +#include <QtCore/QProcess> +#include <QtCore/QTimer> +#include <QtCore/QFileInfo> +#include <QtCore/QDir> +#include <QtCore/QMutex> +#include <QtCore/QLibraryInfo> +#include <QtDeclarative/private/qdeclarativedebugclient_p.h> +#include <QtDeclarative/QJSEngine> + +//QDeclarativeDebugTest +#include "../../../shared/util.h" +#include "../shared/debugutil_p.h" + +const char *SEQ = "seq"; +const char *TYPE = "type"; +const char *COMMAND = "command"; +const char *ARGUMENTS = "arguments"; +const char *STEPACTION = "stepaction"; +const char *STEPCOUNT = "stepcount"; +const char *EXPRESSION = "expression"; +const char *FRAME = "frame"; +const char *GLOBAL = "global"; +const char *DISABLEBREAK = "disable_break"; +const char *HANDLES = "handles"; +const char *INCLUDESOURCE = "includeSource"; +const char *FROMFRAME = "fromFrame"; +const char *TOFRAME = "toFrame"; +const char *BOTTOM = "bottom"; +const char *NUMBER = "number"; +const char *FRAMENUMBER = "frameNumber"; +const char *TYPES = "types"; +const char *IDS = "ids"; +const char *FILTER = "filter"; +const char *FROMLINE = "fromLine"; +const char *TOLINE = "toLine"; +const char *TARGET = "target"; +const char *LINE = "line"; +const char *COLUMN = "column"; +const char *ENABLED = "enabled"; +const char *CONDITION = "condition"; +const char *IGNORECOUNT = "ignoreCount"; +const char *BREAKPOINT = "breakpoint"; +const char *FLAGS = "flags"; + +const char *CONTINEDEBUGGING = "continue"; +const char *EVALUATE = "evaluate"; +const char *LOOKUP = "lookup"; +const char *BACKTRACE = "backtrace"; +const char *SCOPE = "scope"; +const char *SCOPES = "scopes"; +const char *SCRIPTS = "scripts"; +const char *SOURCE = "source"; +const char *SETBREAKPOINT = "setbreakpoint"; +const char *CHANGEBREAKPOINT = "changebreakpoint"; +const char *CLEARBREAKPOINT = "clearbreakpoint"; +const char *SETEXCEPTIONBREAK = "setexceptionbreak"; +const char *V8FLAGS = "v8flags"; +const char *VERSION = "version"; +const char *DISCONNECT = "disconnect"; +const char *LISTBREAKPOINTS = "listbreakpoints"; +const char *GARBAGECOLLECTOR = "gc"; +//const char *PROFILE = "profile"; + +const char *CONNECT = "connect"; +const char *INTERRUPT = "interrupt"; + +const char *REQUEST = "request"; +const char *IN = "in"; +const char *NEXT = "next"; +const char *OUT = "out"; + +const char *FUNCTION = "function"; +const char *SCRIPT = "script"; + +const char *ALL = "all"; +const char *UNCAUGHT = "uncaught"; + +//const char *PAUSE = "pause"; +//const char *RESUME = "resume"; + +const char *BLOCKMODE = "-qmljsdebugger=port:3771,block"; +const char *NORMALMODE = "-qmljsdebugger=port:3771"; +const char *QMLFILE = "test.qml"; +const char *JSFILE = "test.js"; + +#define VARIANTMAPINIT \ + QString obj("{}"); \ + QJSValue jsonVal = parser.call(QJSValue(), QJSValueList() << obj); \ + jsonVal.setProperty(SEQ,QJSValue(seq++)); \ + jsonVal.setProperty(TYPE,REQUEST); + +inline QString TEST_FILE(const QString &filename) +{ + QFileInfo fileInfo(__FILE__); + return fileInfo.absoluteDir().filePath("data/" + filename); +} + +class QJSDebugProcess; +class QJSDebugClient; + +class tst_QDeclarativeDebugJS : public QObject +{ + Q_OBJECT + +private slots: + + void initTestCase(); + void cleanupTestCase(); + + void init(); + void cleanup(); + + void getVersion(); + + void applyV8Flags(); + + void disconnect(); + + void gc(); + + void listBreakpoints(); + + void setBreakpointInScriptOnCompleted(); + void setBreakpointInScriptOnTimerCallback(); + void setBreakpointInScriptInDifferentFile(); + void setBreakpointInScriptOnComment(); + void setBreakpointInScriptOnEmptyLine(); + void setBreakpointInScriptWithCondition(); + //void setBreakpointInFunction(); //NOT SUPPORTED + + void changeBreakpoint(); + void changeBreakpointOnCondition(); + + void clearBreakpoint(); + + void setExceptionBreak(); + + void stepNext(); + void stepNextWithCount(); + void stepIn(); + void stepOut(); + void continueDebugging(); + + void backtrace(); + + void getFrameDetails(); + + void getScopeDetails(); + + void evaluateInGlobalScope(); + void evaluateInLocalScope(); + + void getScopes(); + + void getScripts(); + + void getSource(); + + // void profile(); //NOT SUPPORTED + + // void verifyQMLOptimizerDisabled(); + +private: + QJSDebugProcess *process; + QJSDebugClient *client; + QDeclarativeDebugConnection *connection; +}; + +class QJSDebugProcess : public QObject +{ + Q_OBJECT +public: + QJSDebugProcess(); + ~QJSDebugProcess(); + + void start(const QStringList &arguments); + bool waitForSessionStart(); + +private slots: + void processAppOutput(); + +private: + void stop(); + +private: + QProcess m_process; + QTimer m_timer; + QEventLoop m_eventLoop; + QMutex m_mutex; + bool m_started; +}; + +QJSDebugProcess::QJSDebugProcess() + : m_started(false) +{ + m_process.setProcessChannelMode(QProcess::MergedChannels); + m_timer.setSingleShot(true); + m_timer.setInterval(5000); + connect(&m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(processAppOutput())); + connect(&m_timer, SIGNAL(timeout()), &m_eventLoop, SLOT(quit())); + +// QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); +// env.insert("QML_DISABLE_OPTIMIZER", "1"); // Add an environment variable +// m_process.setProcessEnvironment(env); + +} + +QJSDebugProcess::~QJSDebugProcess() +{ + stop(); +} + +void QJSDebugProcess::start(const QStringList &arguments) +{ + m_mutex.lock(); + m_process.start(QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlscene", arguments); + m_process.waitForStarted(); + m_timer.start(); + m_mutex.unlock(); +} + +void QJSDebugProcess::stop() +{ + if (m_process.state() != QProcess::NotRunning) { + m_process.terminate(); + m_process.waitForFinished(5000); + } +} + +bool QJSDebugProcess::waitForSessionStart() +{ + m_eventLoop.exec(QEventLoop::ExcludeUserInputEvents); + + return m_started; +} + +void QJSDebugProcess::processAppOutput() +{ + m_mutex.lock(); + const QString appOutput = m_process.readAll(); + static QRegExp newline("[\n\r]{1,2}"); + QStringList lines = appOutput.split(newline); + foreach (const QString &line, lines) { + if (line.isEmpty()) + continue; + if (line.startsWith("Qml debugging is enabled")) // ignore + continue; + if (line.startsWith("QDeclarativeDebugServer:")) { + if (line.contains("Waiting for connection ")) { + m_started = true; + m_eventLoop.quit(); + continue; + } + if (line.contains("Connection established")) { + continue; + } + } + } + m_mutex.unlock(); +} + +class QJSDebugClient : public QDeclarativeDebugClient +{ + Q_OBJECT +public: + enum StepAction + { + Continue, + In, + Out, + Next + }; + + enum Exception + { + All, + Uncaught + }; + +// enum ProfileCommand +// { +// Pause, +// Resume +// }; + + QJSDebugClient(QDeclarativeDebugConnection *connection) + : QDeclarativeDebugClient(QLatin1String("V8Debugger"), connection), + seq(0) + { + parser = jsEngine.evaluate(QLatin1String("JSON.parse")); + stringify = jsEngine.evaluate(QLatin1String("JSON.stringify")); + } + + void startDebugging(); + void interrupt(); + + void continueDebugging(StepAction stepAction, int stepCount = 1); + void evaluate(QString expr, bool global = false, bool disableBreak = false, int frame = -1, const QVariantMap &addContext = QVariantMap()); + void lookup(QList<int> handles, bool includeSource = false); + void backtrace(int fromFrame = -1, int toFrame = -1, bool bottom = false); + void frame(int number = -1); + void scope(int number = -1, int frameNumber = -1); + void scopes(int frameNumber = -1); + void scripts(int types = 4, QList<int> ids = QList<int>(), bool includeSource = false, QVariant filter = QVariant()); + void source(int frame = -1, int fromLine = -1, int toLine = -1); + void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = true, QString condition = QString(), int ignoreCount = -1); + void changeBreakpoint(int breakpoint, bool enabled = true, QString condition = QString(), int ignoreCount = -1); + void clearBreakpoint(int breakpoint); + void setExceptionBreak(Exception type, bool enabled = false); + void v8flags(QString flags); + void version(); + //void profile(ProfileCommand command); //NOT SUPPORTED + void disconnect(); + void gc(); + void listBreakpoints(); + +protected: + //inherited from QDeclarativeDebugClient + void statusChanged(Status status); + void messageReceived(const QByteArray &data); + +signals: + void enabled(); + void breakpointSet(); + void result(); + void stopped(); + +private: + void sendMessage(const QByteArray &); + void flushSendBuffer(); + QByteArray packMessage(QByteArray message); + +private: + QJSEngine jsEngine; + int seq; + + QList<QByteArray> sendBuffer; + +public: + QJSValue parser; + QJSValue stringify; + QByteArray response; + +}; + +void QJSDebugClient::startDebugging() +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "connect", + // } + VARIANTMAPINIT; + jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(CONNECT))); + + QJSValue json = stringify.call(QJSValue(), QJSValueList() << jsonVal); + sendMessage(packMessage(json.toString().toUtf8())); +} + +void QJSDebugClient::interrupt() +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "interrupt", + // } + VARIANTMAPINIT; + jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(INTERRUPT))); + + QJSValue json = stringify.call(QJSValue(), QJSValueList() << jsonVal); + sendMessage(packMessage(json.toString().toUtf8())); +} + +void QJSDebugClient::continueDebugging(StepAction action, int count) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "continue", + // "arguments" : { "stepaction" : <"in", "next" or "out">, + // "stepcount" : <number of steps (default 1)> + // } + // } + VARIANTMAPINIT; + jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(CONTINEDEBUGGING))); + + if (action != Continue) { + QJSValue args = parser.call(QJSValue(), QJSValueList() << obj); + switch (action) { + case In: args.setProperty(QLatin1String(STEPACTION),QJSValue(QLatin1String(IN))); + break; + case Out: args.setProperty(QLatin1String(STEPACTION),QJSValue(QLatin1String(OUT))); + break; + case Next: args.setProperty(QLatin1String(STEPACTION),QJSValue(QLatin1String(NEXT))); + break; + default:break; + } + if (args.isValid()) { + if (count != 1) + args.setProperty(QLatin1String(STEPCOUNT),QJSValue(count)); + jsonVal.setProperty(QLatin1String(ARGUMENTS),args); + } + } + QJSValue json = stringify.call(QJSValue(), QJSValueList() << jsonVal); + sendMessage(packMessage(json.toString().toUtf8())); +} + +void QJSDebugClient::evaluate(QString expr, bool global, bool disableBreak, int frame, const QVariantMap &/*addContext*/) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "evaluate", + // "arguments" : { "expression" : <expression to evaluate>, + // "frame" : <number>, + // "global" : <boolean>, + // "disable_break" : <boolean>, + // "additional_context" : [ + // { "name" : <name1>, "handle" : <handle1> }, + // { "name" : <name2>, "handle" : <handle2> }, + // ... + // ] + // } + // } + VARIANTMAPINIT; + jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(EVALUATE))); + + QJSValue args = parser.call(QJSValue(), QJSValueList() << obj); + args.setProperty(QLatin1String(EXPRESSION),QJSValue(expr)); + + if (frame != -1) + args.setProperty(QLatin1String(FRAME),QJSValue(frame)); + + if (global) + args.setProperty(QLatin1String(GLOBAL),QJSValue(global)); + + if (disableBreak) + args.setProperty(QLatin1String(DISABLEBREAK),QJSValue(disableBreak)); + + if (args.isValid()) { + jsonVal.setProperty(QLatin1String(ARGUMENTS),args); + } + + QJSValue json = stringify.call(QJSValue(), QJSValueList() << jsonVal); + sendMessage(packMessage(json.toString().toUtf8())); +} + +void QJSDebugClient::lookup(QList<int> handles, bool includeSource) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "lookup", + // "arguments" : { "handles" : <array of handles>, + // "includeSource" : <boolean indicating whether the source will be included when script objects are returned>, + // } + // } + VARIANTMAPINIT; + jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(LOOKUP))); + + QJSValue args = parser.call(QJSValue(), QJSValueList() << obj); + + QString arr("[]"); + QJSValue array = parser.call(QJSValue(), QJSValueList() << arr); + int index = 0; + foreach (int handle, handles) { + array.setProperty(index++,QJSValue(handle)); + } + args.setProperty(QLatin1String(HANDLES),array); + + if (includeSource) + args.setProperty(QLatin1String(INCLUDESOURCE),QJSValue(includeSource)); + + if (args.isValid()) { + jsonVal.setProperty(QLatin1String(ARGUMENTS),args); + } + + QJSValue json = stringify.call(QJSValue(), QJSValueList() << jsonVal); + sendMessage(packMessage(json.toString().toUtf8())); +} + +void QJSDebugClient::backtrace(int fromFrame, int toFrame, bool bottom) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "backtrace", + // "arguments" : { "fromFrame" : <number> + // "toFrame" : <number> + // "bottom" : <boolean, set to true if the bottom of the stack is requested> + // } + // } + VARIANTMAPINIT; + jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(BACKTRACE))); + + QJSValue args = parser.call(QJSValue(), QJSValueList() << obj); + + if (fromFrame != -1) + args.setProperty(QLatin1String(FROMFRAME),QJSValue(fromFrame)); + + if (toFrame != -1) + args.setProperty(QLatin1String(TOFRAME),QJSValue(toFrame)); + + if (bottom) + args.setProperty(QLatin1String(BOTTOM),QJSValue(bottom)); + + if (args.isValid()) { + jsonVal.setProperty(QLatin1String(ARGUMENTS),args); + } + + QJSValue json = stringify.call(QJSValue(), QJSValueList() << jsonVal); + sendMessage(packMessage(json.toString().toUtf8())); +} + +void QJSDebugClient::frame(int number) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "frame", + // "arguments" : { "number" : <frame number> + // } + // } + VARIANTMAPINIT; + jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(FRAME))); + + if (number != -1) { + QJSValue args = parser.call(QJSValue(), QJSValueList() << obj); + args.setProperty(QLatin1String(NUMBER),QJSValue(number)); + + if (args.isValid()) { + jsonVal.setProperty(QLatin1String(ARGUMENTS),args); + } + } + + QJSValue json = stringify.call(QJSValue(), QJSValueList() << jsonVal); + sendMessage(packMessage(json.toString().toUtf8())); +} + +void QJSDebugClient::scope(int number, int frameNumber) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "scope", + // "arguments" : { "number" : <scope number> + // "frameNumber" : <frame number, optional uses selected frame if missing> + // } + // } + VARIANTMAPINIT; + jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(SCOPE))); + + if (number != -1) { + QJSValue args = parser.call(QJSValue(), QJSValueList() << obj); + args.setProperty(QLatin1String(NUMBER),QJSValue(number)); + + if (frameNumber != -1) + args.setProperty(QLatin1String(FRAMENUMBER),QJSValue(frameNumber)); + + if (args.isValid()) { + jsonVal.setProperty(QLatin1String(ARGUMENTS),args); + } + } + + QJSValue json = stringify.call(QJSValue(), QJSValueList() << jsonVal); + sendMessage(packMessage(json.toString().toUtf8())); +} + +void QJSDebugClient::scopes(int frameNumber) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "scopes", + // "arguments" : { "frameNumber" : <frame number, optional uses selected frame if missing> + // } + // } + VARIANTMAPINIT; + jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(SCOPES))); + + if (frameNumber != -1) { + QJSValue args = parser.call(QJSValue(), QJSValueList() << obj); + args.setProperty(QLatin1String(FRAMENUMBER),QJSValue(frameNumber)); + + if (args.isValid()) { + jsonVal.setProperty(QLatin1String(ARGUMENTS),args); + } + } + + QJSValue json = stringify.call(QJSValue(), QJSValueList() << jsonVal); + sendMessage(packMessage(json.toString().toUtf8())); +} + +void QJSDebugClient::scripts(int types, QList<int> ids, bool includeSource, QVariant /*filter*/) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "scripts", + // "arguments" : { "types" : <types of scripts to retrieve + // set bit 0 for native scripts + // set bit 1 for extension scripts + // set bit 2 for normal scripts + // (default is 4 for normal scripts)> + // "ids" : <array of id's of scripts to return. If this is not specified all scripts are requrned> + // "includeSource" : <boolean indicating whether the source code should be included for the scripts returned> + // "filter" : <string or number: filter string or script id. + // If a number is specified, then only the script with the same number as its script id will be retrieved. + // If a string is specified, then only scripts whose names contain the filter string will be retrieved.> + // } + // } + VARIANTMAPINIT; + jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(SCRIPTS))); + + QJSValue args = parser.call(QJSValue(), QJSValueList() << obj); + args.setProperty(QLatin1String(TYPES),QJSValue(types)); + + if (ids.count()) { + QString arr("[]"); + QJSValue array = parser.call(QJSValue(), QJSValueList() << arr); + int index = 0; + foreach (int id, ids) { + array.setProperty(index++,QJSValue(id)); + } + args.setProperty(QLatin1String(IDS),array); + } + + if (includeSource) + args.setProperty(QLatin1String(INCLUDESOURCE),QJSValue(includeSource)); + + if (args.isValid()) { + jsonVal.setProperty(QLatin1String(ARGUMENTS),args); + } + + QJSValue json = stringify.call(QJSValue(), QJSValueList() << jsonVal); + sendMessage(packMessage(json.toString().toUtf8())); +} + +void QJSDebugClient::source(int frame, int fromLine, int toLine) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "source", + // "arguments" : { "frame" : <frame number (default selected frame)> + // "fromLine" : <from line within the source default is line 0> + // "toLine" : <to line within the source this line is not included in + // the result default is the number of lines in the script> + // } + // } + VARIANTMAPINIT; + jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(SOURCE))); + + QJSValue args = parser.call(QJSValue(), QJSValueList() << obj); + + if (frame != -1) + args.setProperty(QLatin1String(FRAME),QJSValue(frame)); + + if (fromLine != -1) + args.setProperty(QLatin1String(FROMLINE),QJSValue(fromLine)); + + if (toLine != -1) + args.setProperty(QLatin1String(TOLINE),QJSValue(toLine)); + + if (args.isValid()) { + jsonVal.setProperty(QLatin1String(ARGUMENTS),args); + } + + QJSValue json = stringify.call(QJSValue(), QJSValueList() << jsonVal); + sendMessage(packMessage(json.toString().toUtf8())); +} + +void QJSDebugClient::setBreakpoint(QString type, QString target, int line, int column, bool enabled, QString condition, int ignoreCount) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "setbreakpoint", + // "arguments" : { "type" : <"function" or "script" or "scriptId" or "scriptRegExp"> + // "target" : <function expression or script identification> + // "line" : <line in script or function> + // "column" : <character position within the line> + // "enabled" : <initial enabled state. True or false, default is true> + // "condition" : <string with break point condition> + // "ignoreCount" : <number specifying the number of break point hits to ignore, default value is 0> + // } + // } + VARIANTMAPINIT; + jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(SETBREAKPOINT))); + + QJSValue args = parser.call(QJSValue(), QJSValueList() << obj); + + args.setProperty(QLatin1String(TYPE),QJSValue(type)); + args.setProperty(QLatin1String(TARGET),QJSValue(target)); + + if (line != -1) + args.setProperty(QLatin1String(LINE),QJSValue(line)); + + if (column != -1) + args.setProperty(QLatin1String(COLUMN),QJSValue(column)); + + args.setProperty(QLatin1String(ENABLED),QJSValue(enabled)); + + if (!condition.isEmpty()) + args.setProperty(QLatin1String(CONDITION),QJSValue(condition)); + + if (ignoreCount != -1) + args.setProperty(QLatin1String(IGNORECOUNT),QJSValue(ignoreCount)); + + if (args.isValid()) { + jsonVal.setProperty(QLatin1String(ARGUMENTS),args); + } + + QJSValue json = stringify.call(QJSValue(), QJSValueList() << jsonVal); + sendMessage(packMessage(json.toString().toUtf8())); +} + +void QJSDebugClient::changeBreakpoint(int breakpoint, bool enabled, QString condition, int ignoreCount) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "changebreakpoint", + // "arguments" : { "breakpoint" : <number of the break point to clear> + // "enabled" : <initial enabled state. True or false, default is true> + // "condition" : <string with break point condition> + // "ignoreCount" : <number specifying the number of break point hits } + // } + VARIANTMAPINIT; + jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(CHANGEBREAKPOINT))); + + QJSValue args = parser.call(QJSValue(), QJSValueList() << obj); + + args.setProperty(QLatin1String(BREAKPOINT),QJSValue(breakpoint)); + + args.setProperty(QLatin1String(ENABLED),QJSValue(enabled)); + + if (!condition.isEmpty()) + args.setProperty(QLatin1String(CONDITION),QJSValue(condition)); + + if (ignoreCount != -1) + args.setProperty(QLatin1String(IGNORECOUNT),QJSValue(ignoreCount)); + + if (args.isValid()) { + jsonVal.setProperty(QLatin1String(ARGUMENTS),args); + } + + QJSValue json = stringify.call(QJSValue(), QJSValueList() << jsonVal); + sendMessage(packMessage(json.toString().toUtf8())); +} + +void QJSDebugClient::clearBreakpoint(int breakpoint) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "clearbreakpoint", + // "arguments" : { "breakpoint" : <number of the break point to clear> + // } + // } + VARIANTMAPINIT; + jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(CLEARBREAKPOINT))); + + QJSValue args = parser.call(QJSValue(), QJSValueList() << obj); + + args.setProperty(QLatin1String(BREAKPOINT),QJSValue(breakpoint)); + + if (args.isValid()) { + jsonVal.setProperty(QLatin1String(ARGUMENTS),args); + } + + QJSValue json = stringify.call(QJSValue(), QJSValueList() << jsonVal); + sendMessage(packMessage(json.toString().toUtf8())); +} + +void QJSDebugClient::setExceptionBreak(Exception type, bool enabled) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "setexceptionbreak", + // "arguments" : { "type" : <string: "all", or "uncaught">, + // "enabled" : <optional bool: enables the break type if true> + // } + // } + VARIANTMAPINIT; + jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(SETEXCEPTIONBREAK))); + + QJSValue args = parser.call(QJSValue(), QJSValueList() << obj); + + if (type == All) + args.setProperty(QLatin1String(TYPE),QJSValue(QLatin1String(ALL))); + else if (type == Uncaught) + args.setProperty(QLatin1String(TYPE),QJSValue(QLatin1String(UNCAUGHT))); + + if (enabled) + args.setProperty(QLatin1String(ENABLED),QJSValue(enabled)); + + if (args.isValid()) { + jsonVal.setProperty(QLatin1String(ARGUMENTS),args); + } + + QJSValue json = stringify.call(QJSValue(), QJSValueList() << jsonVal); + sendMessage(packMessage(json.toString().toUtf8())); +} + +void QJSDebugClient::v8flags(QString flags) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "v8flags", + // "arguments" : { "flags" : <string: a sequence of v8 flags just like those used on the command line> + // } + // } + VARIANTMAPINIT; + jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(V8FLAGS))); + + QJSValue args = parser.call(QJSValue(), QJSValueList() << obj); + + args.setProperty(QLatin1String(FLAGS),QJSValue(flags)); + + if (args.isValid()) { + jsonVal.setProperty(QLatin1String(ARGUMENTS),args); + } + + QJSValue json = stringify.call(QJSValue(), QJSValueList() << jsonVal); + sendMessage(packMessage(json.toString().toUtf8())); +} + +void QJSDebugClient::version() +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "version", + // } + VARIANTMAPINIT; + jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(VERSION))); + + QJSValue json = stringify.call(QJSValue(), QJSValueList() << jsonVal); + sendMessage(packMessage(json.toString().toUtf8())); +} + +//void QJSDebugClient::profile(ProfileCommand command) +//{ +//// { "seq" : <number>, +//// "type" : "request", +//// "command" : "profile", +//// "arguments" : { "command" : "resume" or "pause" } +//// } +// VARIANTMAPINIT; +// jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(PROFILE))); + +// QJSValue args = parser.call(QJSValue(), QJSValueList() << obj); + +// if (command == Resume) +// args.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(RESUME))); +// else +// args.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(PAUSE))); + +// args.setProperty(QLatin1String("modules"),QJSValue(1)); +// if (args.isValid()) { +// jsonVal.setProperty(QLatin1String(ARGUMENTS),args); +// } + +// QJSValue json = stringify.call(QJSValue(), QJSValueList() << jsonVal); +// sendMessage(packMessage(json.toString().toUtf8())); +//} + +void QJSDebugClient::disconnect() +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "disconnect", + // } + VARIANTMAPINIT; + jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(DISCONNECT))); + + QJSValue json = stringify.call(QJSValue(), QJSValueList() << jsonVal); + sendMessage(packMessage(json.toString().toUtf8())); +} + +void QJSDebugClient::gc() +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "gc", + // "arguments" : { "type" : <string: "all">, + // } + // } + VARIANTMAPINIT; + jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(GARBAGECOLLECTOR))); + + QJSValue args = parser.call(QJSValue(), QJSValueList() << obj); + + args.setProperty(QLatin1String(FLAGS),QJSValue(QLatin1String(ALL))); + + if (args.isValid()) { + jsonVal.setProperty(QLatin1String(ARGUMENTS),args); + } + + QJSValue json = stringify.call(QJSValue(), QJSValueList() << jsonVal); + sendMessage(packMessage(json.toString().toUtf8())); +} + +void QJSDebugClient::listBreakpoints() +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "listbreakpoints", + // } + VARIANTMAPINIT; + jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(LISTBREAKPOINTS))); + + QJSValue json = stringify.call(QJSValue(), QJSValueList() << jsonVal); + sendMessage(packMessage(json.toString().toUtf8())); +} + +void QJSDebugClient::statusChanged(Status status) +{ + if (status == Enabled) { + flushSendBuffer(); + emit enabled(); + } +} + +void QJSDebugClient::messageReceived(const QByteArray &data) +{ + QDataStream ds(data); + QByteArray command; + ds >> command; + + if (command == "V8DEBUG") { + ds >> response; + QString jsonString(response); + QVariantMap value = parser.call(QJSValue(), QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + QString type = value.value("type").toString(); + + if (type == "response") { + + if (!value.value("success").toBool()) { + qDebug() << "Error: The test case will fail since no signal is emitted"; + return; + } + + QString debugCommand(value.value("command").toString()); + if (debugCommand == "backtrace" || + debugCommand == "lookup" || + debugCommand == "setbreakpoint" || + debugCommand == "evaluate" || + debugCommand == "listbreakpoints" || + debugCommand == "version" || + debugCommand == "v8flags" || + debugCommand == "disconnect" || + debugCommand == "gc" || + debugCommand == "changebreakpoint" || + debugCommand == "clearbreakpoint" || + debugCommand == "frame" || + debugCommand == "scope" || + debugCommand == "scopes" || + debugCommand == "scripts" || + debugCommand == "source" || + debugCommand == "setexceptionbreak" /*|| + debugCommand == "profile"*/) { + emit result(); + + } else { + // DO NOTHING + } + + } else if (type == "event") { + QString event(value.value("event").toString()); + + if (event == "break" || + event == "exception") { + emit stopped(); + } + } + } +} + +void QJSDebugClient::sendMessage(const QByteArray &msg) +{ + if (status() == Enabled) { + QDeclarativeDebugClient::sendMessage(msg); + } else { + sendBuffer.append(msg); + } +} + +void QJSDebugClient::flushSendBuffer() +{ + foreach (const QByteArray &msg, sendBuffer) + QDeclarativeDebugClient::sendMessage(msg); + sendBuffer.clear(); +} + +QByteArray QJSDebugClient::packMessage(QByteArray message) +{ + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + QByteArray cmd = "V8DEBUG"; + rs << cmd << message; + return reply; +} + +void tst_QDeclarativeDebugJS::initTestCase() +{ + process = 0; + client = 0; + connection = 0; +} + +void tst_QDeclarativeDebugJS::cleanupTestCase() +{ + if (process) + delete process; + + if (client) + delete client; + + if (connection) + delete connection; +} + +void tst_QDeclarativeDebugJS::init() +{ + connection = new QDeclarativeDebugConnection(); + process = new QJSDebugProcess(); + client = new QJSDebugClient(connection); + + process->start(QStringList() << QLatin1String(BLOCKMODE) << TEST_FILE(QLatin1String(QMLFILE))); + QVERIFY(process->waitForSessionStart()); + + connection->connectToHost("127.0.0.1", 3771); + QVERIFY(connection->waitForConnected()); + + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(enabled()))); +} + +void tst_QDeclarativeDebugJS::cleanup() +{ + if (process) + delete process; + + if (client) + delete client; + + if (connection) + delete connection; + + process = 0; + client = 0; + connection = 0; +} + +void tst_QDeclarativeDebugJS::getVersion() +{ + //void version() + + client->interrupt(); + client->startDebugging(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + client->version(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(result()))); +} + +void tst_QDeclarativeDebugJS::applyV8Flags() +{ + //void v8flags(QString flags) + + client->interrupt(); + client->startDebugging(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + client->v8flags(QString()); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(result()))); +} + +void tst_QDeclarativeDebugJS::disconnect() +{ + //void disconnect() + + client->interrupt(); + client->startDebugging(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + client->disconnect(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(result()))); +} + +void tst_QDeclarativeDebugJS::gc() +{ + //void gc() + + client->setBreakpoint(QLatin1String(SCRIPT), QLatin1String(JSFILE), 2, -1, true); + client->startDebugging(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + client->gc(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(result()))); +} + +void tst_QDeclarativeDebugJS::listBreakpoints() +{ + //void listBreakpoints() + + int sourceLine1 = 57; + int sourceLine2 = 60; + int sourceLine3 = 67; + + client->setBreakpoint(QLatin1String(SCRIPT), QLatin1String(QMLFILE), sourceLine1, -1, true); + client->setBreakpoint(QLatin1String(SCRIPT), QLatin1String(QMLFILE), sourceLine2, -1, true); + client->setBreakpoint(QLatin1String(SCRIPT), QLatin1String(JSFILE), sourceLine3, -1, true); + client->startDebugging(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + client->listBreakpoints(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(result()))); + + QString jsonString(client->response); + QVariantMap value = client->parser.call(QJSValue(), QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + QList<QVariant> breakpoints = value.value("body").toMap().value("breakpoints").toList(); + + QCOMPARE(breakpoints.count(), 3); +} + +void tst_QDeclarativeDebugJS::setBreakpointInScriptOnCompleted() +{ + //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) + + int sourceLine = 49; + + client->setBreakpoint(QLatin1String(SCRIPT), QLatin1String(QMLFILE), sourceLine, -1, true); + client->startDebugging(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + QString jsonString(client->response); + QVariantMap value = client->parser.call(QJSValue(), QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + QVariantMap body = value.value("body").toMap(); + + QCOMPARE(body.value("sourceLine").toInt(), sourceLine); + QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(QMLFILE)); +} + +void tst_QDeclarativeDebugJS::setBreakpointInScriptOnTimerCallback() +{ + //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) + + int sourceLine = 67; + + client->setBreakpoint(QLatin1String(SCRIPT), QLatin1String(QMLFILE), sourceLine, -1, true); + client->startDebugging(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + QString jsonString(client->response); + QVariantMap value = client->parser.call(QJSValue(), QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + QVariantMap body = value.value("body").toMap(); + + QCOMPARE(body.value("sourceLine").toInt(), sourceLine); + QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(QMLFILE)); +} + +void tst_QDeclarativeDebugJS::setBreakpointInScriptInDifferentFile() +{ + //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) + + int sourceLine = 43; + + client->setBreakpoint(QLatin1String(SCRIPT), QLatin1String(JSFILE), sourceLine, -1, true); + client->startDebugging(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + QString jsonString(client->response); + QVariantMap value = client->parser.call(QJSValue(), QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + QVariantMap body = value.value("body").toMap(); + + QCOMPARE(body.value("sourceLine").toInt(), sourceLine); + QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(JSFILE)); +} + +void tst_QDeclarativeDebugJS::setBreakpointInScriptOnComment() +{ + //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) + + int sourceLine = 48; + int actualLine = 50; + + client->setBreakpoint(QLatin1String(SCRIPT), QLatin1String(JSFILE), sourceLine, -1, true); + client->startDebugging(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + QString jsonString(client->response); + QVariantMap value = client->parser.call(QJSValue(), QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + QVariantMap body = value.value("body").toMap(); + + QCOMPARE(body.value("sourceLine").toInt(), actualLine); + QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(JSFILE)); +} + +void tst_QDeclarativeDebugJS::setBreakpointInScriptOnEmptyLine() +{ + //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) + + int sourceLine = 49; + int actualLine = 50; + + client->setBreakpoint(QLatin1String(SCRIPT), QLatin1String(JSFILE), sourceLine, -1, true); + client->startDebugging(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + QString jsonString(client->response); + QVariantMap value = client->parser.call(QJSValue(), QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + QVariantMap body = value.value("body").toMap(); + + QCOMPARE(body.value("sourceLine").toInt(), actualLine); + QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(JSFILE)); +} + +void tst_QDeclarativeDebugJS::setBreakpointInScriptWithCondition() +{ + //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) + + int out = 10; + int sourceLine = 51; + + client->setBreakpoint(QLatin1String(SCRIPT), QLatin1String(JSFILE), sourceLine, -1, true, QLatin1String("out > 10")); + client->startDebugging(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + //Get the frame index + QString jsonString = client->response; + QVariantMap value = client->parser.call(QJSValue(), QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + QVariantMap body = value.value("body").toMap(); + + int frameIndex = body.value("index").toInt(); + + //Verify the value of 'result' + client->evaluate(QLatin1String("out"),frameIndex); + + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(result()))); + + jsonString = client->response; + value = client->parser.call(QJSValue(), QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + body = value.value("body").toMap(); + + QVERIFY(body.value("value").toInt() > out); +} + +//void tst_QDeclarativeDebugJS::setBreakpointInFunction() +//{ +// //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) + +// int actualLine = 31; + +// client->startDebugging(); +// client->setBreakpoint(QLatin1String(FUNCTION), QLatin1String("doSomethingElse"), -1, -1, true); + +// QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()))); + +// QString jsonString(client->response); +// QVariantMap value = client->parser.call(QJSValue(), QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + +// QVariantMap body = value.value("body").toMap(); + +// QCOMPARE(body.value("sourceLine").toInt(), actualLine); +// QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(QMLFILE)); +//} + +void tst_QDeclarativeDebugJS::changeBreakpoint() +{ + //void changeBreakpoint(int breakpoint, bool enabled = false, QString condition = QString(), int ignoreCount = -1) + + int sourceLine1 = 77; + int sourceLine2 = 78; + + client->setBreakpoint(QLatin1String(SCRIPT), QLatin1String(QMLFILE), sourceLine1, -1, true); + client->setBreakpoint(QLatin1String(SCRIPT), QLatin1String(QMLFILE), sourceLine2, -1, true); + client->startDebugging(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + //Will hit 1st brakpoint, change this breakpoint enable = false + QString jsonString(client->response); + QVariantMap value = client->parser.call(QJSValue(), QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + QVariantMap body = value.value("body").toMap(); + QList<QVariant> breakpointsHit = body.value("breakpoints").toList(); + + int breakpoint = breakpointsHit.at(0).toInt(); + client->changeBreakpoint(breakpoint,false); + + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(result()))); + + //Continue with debugging + client->continueDebugging(QJSDebugClient::Continue); + //Hit 2nd breakpoint + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()))); + //Continue with debugging + client->continueDebugging(QJSDebugClient::Continue); + //Should stop at 2nd breakpoint + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + jsonString = client->response; + value = client->parser.call(QJSValue(), QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + body = value.value("body").toMap(); + + QCOMPARE(body.value("sourceLine").toInt(), sourceLine2); +} + +void tst_QDeclarativeDebugJS::changeBreakpointOnCondition() +{ + //void changeBreakpoint(int breakpoint, bool enabled = false, QString condition = QString(), int ignoreCount = -1) + + int sourceLine1 = 56; + int sourceLine2 = 60; + int result = 0; + + client->setBreakpoint(QLatin1String(SCRIPT), QLatin1String(QMLFILE), sourceLine1, -1, true); + client->setBreakpoint(QLatin1String(SCRIPT), QLatin1String(QMLFILE), sourceLine2, -1, true); + client->startDebugging(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + //Will hit 1st brakpoint, change this breakpoint enable = false + QString jsonString(client->response); + QVariantMap value = client->parser.call(QJSValue(), QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + QVariantMap body = value.value("body").toMap(); + QList<QVariant> breakpointsHit = body.value("breakpoints").toList(); + + int breakpoint = breakpointsHit.at(0).toInt(); + client->changeBreakpoint(breakpoint,false,QLatin1String("a = 0")); + + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(result()))); + + //Continue with debugging + client->continueDebugging(QJSDebugClient::Continue); + //Hit 2nd breakpoint + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()))); + //Continue with debugging + client->continueDebugging(QJSDebugClient::Continue); + //Should stop at 2nd breakpoint + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + jsonString = client->response; + value = client->parser.call(QJSValue(), QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + body = value.value("body").toMap(); + + QCOMPARE(body.value("sourceLine").toInt(), sourceLine2); + + client->frame(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(result()))); + + //Get the frame index + jsonString = client->response; + value = client->parser.call(QJSValue(), QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + body = value.value("body").toMap(); + + int frameIndex = body.value("index").toInt(); + + //Verify the value of 'result' + client->evaluate(QLatin1String("root.result"),frameIndex); + + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(result()))); + + jsonString = client->response; + value = client->parser.call(QJSValue(), QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + body = value.value("body").toMap(); + + QVERIFY(body.value("value").toInt() > result); +} + +void tst_QDeclarativeDebugJS::clearBreakpoint() +{ + //void clearBreakpoint(int breakpoint); + + int sourceLine1 = 77; + int sourceLine2 = 78; + + client->setBreakpoint(QLatin1String(SCRIPT), QLatin1String(QMLFILE), sourceLine1, -1, true); + client->setBreakpoint(QLatin1String(SCRIPT), QLatin1String(QMLFILE), sourceLine2, -1, true); + client->startDebugging(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + //Will hit 1st brakpoint, change this breakpoint enable = false + QString jsonString(client->response); + QVariantMap value = client->parser.call(QJSValue(), QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + QVariantMap body = value.value("body").toMap(); + QList<QVariant> breakpointsHit = body.value("breakpoints").toList(); + + int breakpoint = breakpointsHit.at(0).toInt(); + client->changeBreakpoint(breakpoint,false,QLatin1String("result > 5")); + + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(result()))); + + //Continue with debugging + client->continueDebugging(QJSDebugClient::Continue); + //Hit 2nd breakpoint + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()))); + //Continue with debugging + client->continueDebugging(QJSDebugClient::Continue); + //Should stop at 2nd breakpoint + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + jsonString = client->response; + value = client->parser.call(QJSValue(), QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + body = value.value("body").toMap(); + + QCOMPARE(body.value("sourceLine").toInt(), sourceLine2); +} + +void tst_QDeclarativeDebugJS::setExceptionBreak() +{ + //void setExceptionBreak(QString type, bool enabled = false); + + client->interrupt(); + client->startDebugging(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + client->setExceptionBreak(QJSDebugClient::All,true); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(result()))); + + client->continueDebugging(QJSDebugClient::Continue); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()), 10000)); +} + +void tst_QDeclarativeDebugJS::stepNext() +{ + //void continueDebugging(StepAction stepAction, int stepCount = 1); + + int sourceLine = 57; + + client->setBreakpoint(QLatin1String(SCRIPT), QLatin1String(QMLFILE), sourceLine, -1, true); + client->startDebugging(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + client->continueDebugging(QJSDebugClient::Next); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()), 10000)); + + QString jsonString(client->response); + QVariantMap value = client->parser.call(QJSValue(), QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + QVariantMap body = value.value("body").toMap(); + + QCOMPARE(body.value("sourceLine").toInt(), sourceLine + 1); + QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(QMLFILE)); +} + +void tst_QDeclarativeDebugJS::stepNextWithCount() +{ + //void continueDebugging(StepAction stepAction, int stepCount = 1); + + int sourceLine = 59; + + client->setBreakpoint(QLatin1String(SCRIPT), QLatin1String(QMLFILE), sourceLine, -1, true); + client->startDebugging(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + client->continueDebugging(QJSDebugClient::Next,2); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()), 10000)); + + QString jsonString(client->response); + QVariantMap value = client->parser.call(QJSValue(), QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + QVariantMap body = value.value("body").toMap(); + + QCOMPARE(body.value("sourceLine").toInt(), sourceLine + 2); + QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(QMLFILE)); +} + +void tst_QDeclarativeDebugJS::stepIn() +{ + //void continueDebugging(StepAction stepAction, int stepCount = 1); + + int sourceLine = 67; + int actualLine = 56; + + client->setBreakpoint(QLatin1String(SCRIPT), QLatin1String(QMLFILE), sourceLine, -1, true); + client->startDebugging(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + client->continueDebugging(QJSDebugClient::In); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()), 10000)); + + QString jsonString(client->response); + QVariantMap value = client->parser.call(QJSValue(), QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + QVariantMap body = value.value("body").toMap(); + + QCOMPARE(body.value("sourceLine").toInt(), actualLine); + QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(QMLFILE)); +} + +void tst_QDeclarativeDebugJS::stepOut() +{ + //void continueDebugging(StepAction stepAction, int stepCount = 1); + + int sourceLine = 56; + int actualLine = 68; + + client->setBreakpoint(QLatin1String(SCRIPT), QLatin1String(QMLFILE), sourceLine, -1, true); + client->startDebugging(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + client->continueDebugging(QJSDebugClient::Out); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()), 10000)); + + QString jsonString(client->response); + QVariantMap value = client->parser.call(QJSValue(), QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + QVariantMap body = value.value("body").toMap(); + + QCOMPARE(body.value("sourceLine").toInt(), actualLine); + QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(QMLFILE)); +} + +void tst_QDeclarativeDebugJS::continueDebugging() +{ + //void continueDebugging(StepAction stepAction, int stepCount = 1); + + int sourceLine1 = 56; + int sourceLine2 = 60; + + client->setBreakpoint(QLatin1String(SCRIPT), QLatin1String(QMLFILE), sourceLine1, -1, true); + client->setBreakpoint(QLatin1String(SCRIPT), QLatin1String(QMLFILE), sourceLine2, -1, true); + client->startDebugging(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + client->continueDebugging(QJSDebugClient::Continue); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()), 10000)); + + QString jsonString(client->response); + QVariantMap value = client->parser.call(QJSValue(), QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + QVariantMap body = value.value("body").toMap(); + + QCOMPARE(body.value("sourceLine").toInt(), sourceLine2); + QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(QMLFILE)); +} + +void tst_QDeclarativeDebugJS::backtrace() +{ + //void backtrace(int fromFrame = -1, int toFrame = -1, bool bottom = false); + + int sourceLine = 60; + client->setBreakpoint(QLatin1String(SCRIPT), QLatin1String(QMLFILE), sourceLine, -1, true); + client->startDebugging(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + client->backtrace(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(result()))); +} + +void tst_QDeclarativeDebugJS::getFrameDetails() +{ + //void frame(int number = -1); + + int sourceLine = 60; + client->setBreakpoint(QLatin1String(SCRIPT), QLatin1String(QMLFILE), sourceLine, -1, true); + client->startDebugging(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + client->frame(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(result()))); +} + +void tst_QDeclarativeDebugJS::getScopeDetails() +{ + //void scope(int number = -1, int frameNumber = -1); + + int sourceLine = 60; + client->setBreakpoint(QLatin1String(SCRIPT), QLatin1String(QMLFILE), sourceLine, -1, true); + client->startDebugging(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + client->scope(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(result()))); +} + +void tst_QDeclarativeDebugJS::evaluateInGlobalScope() +{ + //void evaluate(QString expr, bool global = false, bool disableBreak = false, int frame = -1, const QVariantMap &addContext = QVariantMap()); + + int sourceLine = 49; + client->setBreakpoint(QLatin1String(SCRIPT), QLatin1String(QMLFILE), sourceLine, -1, true); + client->startDebugging(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + client->evaluate(QLatin1String("print('Hello World')"),true); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(result()))); + + //Verify the value of 'print' + QString jsonString(client->response); + QVariantMap value = client->parser.call(QJSValue(), QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + QVariantMap body = value.value("body").toMap(); + + QCOMPARE(body.value("text").toString(),QLatin1String("undefined")); +} + +void tst_QDeclarativeDebugJS::evaluateInLocalScope() +{ + //void evaluate(QString expr, bool global = false, bool disableBreak = false, int frame = -1, const QVariantMap &addContext = QVariantMap()); + + int sourceLine = 60; + client->setBreakpoint(QLatin1String(SCRIPT), QLatin1String(QMLFILE), sourceLine, -1, true); + client->startDebugging(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + client->frame(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(result()))); + + //Get the frame index + QString jsonString(client->response); + QVariantMap value = client->parser.call(QJSValue(), QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + QVariantMap body = value.value("body").toMap(); + + int frameIndex = body.value("index").toInt(); + + client->evaluate(QLatin1String("root.someValue"),frameIndex); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(result()))); + + //Verify the value of 'root.someValue' + jsonString = client->response; + value = client->parser.call(QJSValue(), QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + body = value.value("body").toMap(); + + QCOMPARE(body.value("value").toInt(),10); +} + +void tst_QDeclarativeDebugJS::getScopes() +{ + //void scopes(int frameNumber = -1); + + client->interrupt(); + client->startDebugging(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + client->scopes(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(result()))); +} + +void tst_QDeclarativeDebugJS::getScripts() +{ + //void scripts(int types = -1, QList<int> ids = QList<int>(), bool includeSource = false, QVariant filter = QVariant()); + + client->interrupt(); + client->startDebugging(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + client->scripts(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(result()))); +} + +void tst_QDeclarativeDebugJS::getSource() +{ + //void source(int frame = -1, int fromLine = -1, int toLine = -1); + + client->interrupt(); + client->startDebugging(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + client->source(); + QVERIFY(QDeclarativeDebugTest::waitForSignal(client, SIGNAL(result()))); +} + +QTEST_MAIN(tst_QDeclarativeDebugJS) + +#include "tst_qdeclarativedebugjs.moc" + diff --git a/tests/auto/declarative/qdeclarativedebugservice/tst_qdeclarativedebugservice.cpp b/tests/auto/declarative/qdeclarativedebugservice/tst_qdeclarativedebugservice.cpp index 74f549c076..d849ad2a79 100644 --- a/tests/auto/declarative/qdeclarativedebugservice/tst_qdeclarativedebugservice.cpp +++ b/tests/auto/declarative/qdeclarativedebugservice/tst_qdeclarativedebugservice.cpp @@ -47,8 +47,6 @@ #include <QtDeclarative/qdeclarativeengine.h> -#include <private/qdeclarativedebug_p.h> -#include <private/qdeclarativeenginedebug_p.h> #include <private/qdeclarativedebugclient_p.h> #include <private/qdeclarativedebugservice_p.h> @@ -189,7 +187,8 @@ int main(int argc, char *argv[]) char **_argv = new char*[_argc]; for (int i = 0; i < argc; ++i) _argv[i] = argv[i]; - _argv[_argc - 1] = "-qmljsdebugger=port:13769"; + char arg[] = "-qmljsdebugger=port:13769"; + _argv[_argc - 1] = arg; QApplication app(_argc, _argv); tst_QDeclarativeDebugService tc; diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/handleReferenceManagement.handle.1.qml b/tests/auto/declarative/qdeclarativeecmascript/data/handleReferenceManagement.handle.1.qml new file mode 100644 index 0000000000..9c27653b34 --- /dev/null +++ b/tests/auto/declarative/qdeclarativeecmascript/data/handleReferenceManagement.handle.1.qml @@ -0,0 +1,25 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +Item { + id: obj + objectName: "obj" + property CircularReferenceHandle first + property CircularReferenceHandle second + + CircularReferenceHandle { + id: crh + objectName: "crh" + } + + function createReference() { + first = crh.generate(crh); + second = crh.generate(crh); + // NOTE: manually add reference from first to second + // in unit test prior reparenting and gc. + } + + function performGc() { + gc(); + } +} diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/handleReferenceManagement.handle.2.qml b/tests/auto/declarative/qdeclarativeecmascript/data/handleReferenceManagement.handle.2.qml new file mode 100644 index 0000000000..dc196263b4 --- /dev/null +++ b/tests/auto/declarative/qdeclarativeecmascript/data/handleReferenceManagement.handle.2.qml @@ -0,0 +1,26 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +Item { + id: obj + objectName: "obj" + property CircularReferenceHandle first + property CircularReferenceHandle second + + CircularReferenceHandle { + id: crh + objectName: "crh" + } + + function circularReference() { + // generate the circularly referential pair + first = crh.generate(crh); + second = crh.generate(crh); + // note: must manually reparent in unit test + // after setting the handle references. + } + + function performGc() { + gc(); + } +} diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/handleReferenceManagement.object.1.qml b/tests/auto/declarative/qdeclarativeecmascript/data/handleReferenceManagement.object.1.qml new file mode 100644 index 0000000000..4fd1311c29 --- /dev/null +++ b/tests/auto/declarative/qdeclarativeecmascript/data/handleReferenceManagement.object.1.qml @@ -0,0 +1,31 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +Item { + id: obj + objectName: "obj" + + property CircularReferenceObject first + property CircularReferenceObject second + + + CircularReferenceObject { + id: cro + objectName: "cro" + } + + function createReference() { + // generate the objects + first = cro.generate(cro); // has parent, so won't be collected + second = cro.generate(); // no parent, but will be kept alive by first's reference + first.addReference(second); + + // remove top level references + first = cro; + second = cro; + } + + function performGc() { + gc(); + } +} diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/handleReferenceManagement.object.2.qml b/tests/auto/declarative/qdeclarativeecmascript/data/handleReferenceManagement.object.2.qml new file mode 100644 index 0000000000..3f8415ce0f --- /dev/null +++ b/tests/auto/declarative/qdeclarativeecmascript/data/handleReferenceManagement.object.2.qml @@ -0,0 +1,32 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +Item { + id: obj + objectName: "obj" + + property CircularReferenceObject first + property CircularReferenceObject second + + + CircularReferenceObject { + id: cro + objectName: "cro" + } + + function circularReference() { + // generate the circularly referential pair - they should still be collected + first = cro.generate(); // no parent, so should be collected + second = cro.generate(); // no parent, so should be collected + first.addReference(second); + second.addReference(first); + + // remove top level references + first = cro; + second = cro; + } + + function performGc() { + gc(); + } +} diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/signalHandlers.qml b/tests/auto/declarative/qdeclarativeecmascript/data/signalHandlers.qml new file mode 100644 index 0000000000..975be1b2ad --- /dev/null +++ b/tests/auto/declarative/qdeclarativeecmascript/data/signalHandlers.qml @@ -0,0 +1,60 @@ +import Qt.test 1.0 +import QtQuick 2.0 + +QtObject { + id: root + + property int count: 0 + signal testSignal + onTestSignal: count++ + + property int funcCount: 0 + function testFunction() { + funcCount++; + } + + //should increment count + function testSignalCall() { + testSignal() + } + + //should NOT increment count, and should throw an exception + property string errorString + function testSignalHandlerCall() { + try { + onTestSignal() + } catch (error) { + errorString = error.toString(); + } + } + + //should increment funcCount once + function testSignalConnection() { + testSignal.connect(testFunction) + testSignal(); + testSignal.disconnect(testFunction) + testSignal(); + } + + //should increment funcCount once + function testSignalHandlerConnection() { + onTestSignal.connect(testFunction) + testSignal(); + onTestSignal.disconnect(testFunction) + testSignal(); + } + + //should be defined + property bool definedResult: false + function testSignalDefined() { + if (testSignal !== undefined) + definedResult = true; + } + + //should be defined + property bool definedHandlerResult: false + function testSignalHandlerDefined() { + if (onTestSignal !== undefined) + definedHandlerResult = true; + } +} diff --git a/tests/auto/declarative/qdeclarativeecmascript/testtypes.cpp b/tests/auto/declarative/qdeclarativeecmascript/testtypes.cpp index 05d5510033..0057c117fa 100644 --- a/tests/auto/declarative/qdeclarativeecmascript/testtypes.cpp +++ b/tests/auto/declarative/qdeclarativeecmascript/testtypes.cpp @@ -189,6 +189,9 @@ void registerTypes() qRegisterMetaType<MyQmlObject::MyType>("MyEnum2"); qRegisterMetaType<Qt::MouseButtons>("Qt::MouseButtons"); + + qmlRegisterType<CircularReferenceObject>("Qt.test", 1, 0, "CircularReferenceObject"); + qmlRegisterType<CircularReferenceHandle>("Qt.test", 1, 0, "CircularReferenceHandle"); } #include "testtypes.moc" diff --git a/tests/auto/declarative/qdeclarativeecmascript/testtypes.h b/tests/auto/declarative/qdeclarativeecmascript/testtypes.h index 2738ee3d60..52b74affa8 100644 --- a/tests/auto/declarative/qdeclarativeecmascript/testtypes.h +++ b/tests/auto/declarative/qdeclarativeecmascript/testtypes.h @@ -57,6 +57,9 @@ #include <QtDeclarative/qdeclarativescriptstring.h> #include <QtDeclarative/qdeclarativecomponent.h> +#include <private/qv8gccallback_p.h> +#include <private/qdeclarativeengine_p.h> + class MyQmlAttachedObject : public QObject { Q_OBJECT @@ -966,6 +969,108 @@ private: int m_methodCallCount; }; +class CircularReferenceObject : public QObject, + public QV8GCCallback::Node +{ + Q_OBJECT + +public: + CircularReferenceObject(QObject *parent = 0) + : QObject(parent), QV8GCCallback::Node(callback), m_referenced(0), m_dtorCount(0) + { + QV8GCCallback::addGcCallbackNode(this); + } + + ~CircularReferenceObject() + { + if (m_dtorCount) *m_dtorCount = *m_dtorCount + 1; + } + + Q_INVOKABLE void setDtorCount(int *dtorCount) + { + m_dtorCount = dtorCount; + } + + Q_INVOKABLE CircularReferenceObject *generate(QObject *parent = 0) + { + CircularReferenceObject *retn = new CircularReferenceObject(parent); + retn->m_dtorCount = m_dtorCount; + return retn; + } + + Q_INVOKABLE void addReference(QObject *other) + { + m_referenced = other; + } + + static void callback(QV8GCCallback::Referencer *r, QV8GCCallback::Node *n) + { + CircularReferenceObject *cro = static_cast<CircularReferenceObject*>(n); + if (cro->m_referenced) { + r->addRelationship(cro, cro->m_referenced); + } + } + +private: + QObject *m_referenced; + int *m_dtorCount; +}; +Q_DECLARE_METATYPE(CircularReferenceObject*) + +class CircularReferenceHandle : public QObject, + public QV8GCCallback::Node +{ + Q_OBJECT + +public: + CircularReferenceHandle(QObject *parent = 0) + : QObject(parent), QV8GCCallback::Node(gccallback), m_dtorCount(0) + { + QV8GCCallback::addGcCallbackNode(this); + } + + ~CircularReferenceHandle() + { + if (m_dtorCount) *m_dtorCount = *m_dtorCount + 1; + } + + Q_INVOKABLE void setDtorCount(int *dtorCount) + { + m_dtorCount = dtorCount; + } + + Q_INVOKABLE CircularReferenceHandle *generate(QObject *parent = 0) + { + CircularReferenceHandle *retn = new CircularReferenceHandle(parent); + retn->m_dtorCount = m_dtorCount; + return retn; + } + + Q_INVOKABLE void addReference(v8::Persistent<v8::Value> handle) + { + m_referenced = qPersistentNew(handle); + m_referenced.MakeWeak(static_cast<void*>(this), wrcallback); + } + + static void wrcallback(v8::Persistent<v8::Value> handle, void *params) + { + CircularReferenceHandle *crh = static_cast<CircularReferenceHandle*>(params); + qPersistentDispose(handle); + crh->m_referenced.Clear(); + } + + static void gccallback(QV8GCCallback::Referencer *r, QV8GCCallback::Node *n) + { + CircularReferenceHandle *crh = static_cast<CircularReferenceHandle*>(n); + r->addRelationship(crh, crh->m_referenced); + } + +private: + v8::Persistent<v8::Value> m_referenced; + int *m_dtorCount; +}; +Q_DECLARE_METATYPE(CircularReferenceHandle*) + void registerTypes(); #endif // TESTTYPES_H diff --git a/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp b/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp index f14db0a330..b732cd8193 100644 --- a/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp +++ b/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp @@ -153,6 +153,7 @@ private slots: void elementAssign(); void objectPassThroughSignals(); void booleanConversion(); + void handleReferenceManagement(); void bug1(); void bug2(); @@ -188,6 +189,7 @@ private slots: void realToInt(); void dynamicString(); void include(); + void signalHandlers(); void callQtInvokables(); void invokableObjectArg(); @@ -687,6 +689,8 @@ void tst_qdeclarativeecmascript::attachedProperties() QCOMPARE(object->property("b").toInt(), 26); QCOMPARE(object->property("c").toInt(), 26); QCOMPARE(object->property("d").toInt(), 26); + + delete object; } { @@ -3207,6 +3211,256 @@ void tst_qdeclarativeecmascript::booleanConversion() delete object; } +void tst_qdeclarativeecmascript::handleReferenceManagement() +{ + + int dtorCount = 0; + { + // Linear QObject reference + QDeclarativeEngine hrmEngine; + QDeclarativeComponent component(&hrmEngine, TEST_FILE("handleReferenceManagement.object.1.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro"); + cro->setDtorCount(&dtorCount); + QMetaObject::invokeMethod(object, "createReference"); + QMetaObject::invokeMethod(object, "performGc"); + QCoreApplication::processEvents(QEventLoop::DeferredDeletion); + QCOMPARE(dtorCount, 0); // second has JS ownership, kept alive by first's reference + delete object; + hrmEngine.collectGarbage(); + QCoreApplication::processEvents(QEventLoop::DeferredDeletion); + QCOMPARE(dtorCount, 3); + } + + dtorCount = 0; + { + // Circular QObject reference + QDeclarativeEngine hrmEngine; + QDeclarativeComponent component(&hrmEngine, TEST_FILE("handleReferenceManagement.object.2.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro"); + cro->setDtorCount(&dtorCount); + QMetaObject::invokeMethod(object, "circularReference"); + QMetaObject::invokeMethod(object, "performGc"); + QCoreApplication::processEvents(QEventLoop::DeferredDeletion); + QCOMPARE(dtorCount, 2); // both should be cleaned up, since circular references shouldn't keep alive. + delete object; + hrmEngine.collectGarbage(); + QCoreApplication::processEvents(QEventLoop::DeferredDeletion); + QCOMPARE(dtorCount, 3); + } + + dtorCount = 0; + { + // Linear handle reference + QDeclarativeEngine hrmEngine; + QDeclarativeComponent component(&hrmEngine, TEST_FILE("handleReferenceManagement.handle.1.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + CircularReferenceHandle *crh = object->findChild<CircularReferenceHandle*>("crh"); + QVERIFY(crh != 0); + crh->setDtorCount(&dtorCount); + QMetaObject::invokeMethod(object, "createReference"); + CircularReferenceHandle *first = object->property("first").value<CircularReferenceHandle*>(); + CircularReferenceHandle *second = object->property("second").value<CircularReferenceHandle*>(); + QVERIFY(first != 0); + QVERIFY(second != 0); + first->addReference(QDeclarativeData::get(second)->v8object); // create reference + // now we have to reparent second and make second owned by JS. + second->setParent(0); + QDeclarativeEngine::setObjectOwnership(second, QDeclarativeEngine::JavaScriptOwnership); + QMetaObject::invokeMethod(object, "performGc"); + QCoreApplication::processEvents(QEventLoop::DeferredDeletion); + QCOMPARE(dtorCount, 0); // due to reference from first to second, second shouldn't be collected. + delete object; + hrmEngine.collectGarbage(); + QCoreApplication::processEvents(QEventLoop::DeferredDeletion); + QCOMPARE(dtorCount, 3); + } + + dtorCount = 0; + { + // Circular handle reference + QDeclarativeEngine hrmEngine; + QDeclarativeComponent component(&hrmEngine, TEST_FILE("handleReferenceManagement.handle.2.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + CircularReferenceHandle *crh = object->findChild<CircularReferenceHandle*>("crh"); + QVERIFY(crh != 0); + crh->setDtorCount(&dtorCount); + QMetaObject::invokeMethod(object, "circularReference"); + CircularReferenceHandle *first = object->property("first").value<CircularReferenceHandle*>(); + CircularReferenceHandle *second = object->property("second").value<CircularReferenceHandle*>(); + QVERIFY(first != 0); + QVERIFY(second != 0); + first->addReference(QDeclarativeData::get(second)->v8object); // create circular reference + second->addReference(QDeclarativeData::get(first)->v8object); // note: must be weak. + // now we have to reparent and change ownership. + first->setParent(0); + second->setParent(0); + QDeclarativeEngine::setObjectOwnership(first, QDeclarativeEngine::JavaScriptOwnership); + QDeclarativeEngine::setObjectOwnership(second, QDeclarativeEngine::JavaScriptOwnership); + QMetaObject::invokeMethod(object, "performGc"); + QCoreApplication::processEvents(QEventLoop::DeferredDeletion); + QCOMPARE(dtorCount, 2); // despite circular references, both will be collected. + delete object; + hrmEngine.collectGarbage(); + QCoreApplication::processEvents(QEventLoop::DeferredDeletion); + QCOMPARE(dtorCount, 3); + } + + dtorCount = 0; + { + // multiple engine interaction - linear reference + QDeclarativeEngine hrmEngine1; + QDeclarativeEngine hrmEngine2; + QDeclarativeComponent component1(&hrmEngine1, TEST_FILE("handleReferenceManagement.handle.1.qml")); + QDeclarativeComponent component2(&hrmEngine2, TEST_FILE("handleReferenceManagement.handle.1.qml")); + QObject *object1 = component1.create(); + QObject *object2 = component2.create(); + QVERIFY(object1 != 0); + QVERIFY(object2 != 0); + CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh"); + CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh"); + QVERIFY(crh1 != 0); + QVERIFY(crh2 != 0); + crh1->setDtorCount(&dtorCount); + crh2->setDtorCount(&dtorCount); + QMetaObject::invokeMethod(object1, "createReference"); + QMetaObject::invokeMethod(object2, "createReference"); + CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>(); + CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>(); + CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>(); + CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>(); + QVERIFY(first1 != 0); + QVERIFY(second1 != 0); + QVERIFY(first2 != 0); + QVERIFY(second2 != 0); + first1->addReference(QDeclarativeData::get(second2)->v8object); // create reference across engines + // now we have to reparent second2 and make second2 owned by JS. + second2->setParent(0); + QDeclarativeEngine::setObjectOwnership(second2, QDeclarativeEngine::JavaScriptOwnership); + QMetaObject::invokeMethod(object1, "performGc"); + QMetaObject::invokeMethod(object2, "performGc"); + QCoreApplication::processEvents(QEventLoop::DeferredDeletion); + QCOMPARE(dtorCount, 0); // due to reference from first1 to second2, second2 shouldn't be collected. + delete object1; + delete object2; + hrmEngine1.collectGarbage(); + hrmEngine2.collectGarbage(); + QCoreApplication::processEvents(QEventLoop::DeferredDeletion); + QCOMPARE(dtorCount, 6); + } + + dtorCount = 0; + { + // multiple engine interaction - circular reference + QDeclarativeEngine hrmEngine1; + QDeclarativeEngine hrmEngine2; + QDeclarativeComponent component1(&hrmEngine1, TEST_FILE("handleReferenceManagement.handle.1.qml")); + QDeclarativeComponent component2(&hrmEngine2, TEST_FILE("handleReferenceManagement.handle.1.qml")); + QObject *object1 = component1.create(); + QObject *object2 = component2.create(); + QVERIFY(object1 != 0); + QVERIFY(object2 != 0); + CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh"); + CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh"); + QVERIFY(crh1 != 0); + QVERIFY(crh2 != 0); + crh1->setDtorCount(&dtorCount); + crh2->setDtorCount(&dtorCount); + QMetaObject::invokeMethod(object1, "createReference"); + QMetaObject::invokeMethod(object2, "createReference"); + CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>(); + CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>(); + CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>(); + CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>(); + QVERIFY(first1 != 0); + QVERIFY(second1 != 0); + QVERIFY(first2 != 0); + QVERIFY(second2 != 0); + first1->addReference(QDeclarativeData::get(second1)->v8object); // create linear reference within engine1 + second1->addReference(QDeclarativeData::get(second2)->v8object); // create linear reference across engines + second2->addReference(QDeclarativeData::get(first2)->v8object); // create linear reference within engine2 + first2->addReference(QDeclarativeData::get(first1)->v8object); // close the loop - circular ref across engines + // now we have to reparent and change ownership to JS. + first1->setParent(0); + second1->setParent(0); + first2->setParent(0); + second2->setParent(0); + QDeclarativeEngine::setObjectOwnership(first1, QDeclarativeEngine::JavaScriptOwnership); + QDeclarativeEngine::setObjectOwnership(second1, QDeclarativeEngine::JavaScriptOwnership); + QDeclarativeEngine::setObjectOwnership(first2, QDeclarativeEngine::JavaScriptOwnership); + QDeclarativeEngine::setObjectOwnership(second2, QDeclarativeEngine::JavaScriptOwnership); + QMetaObject::invokeMethod(object1, "performGc"); + QMetaObject::invokeMethod(object2, "performGc"); + QCoreApplication::processEvents(QEventLoop::DeferredDeletion); + QCOMPARE(dtorCount, 4); // circular references shouldn't keep them alive. + delete object1; + delete object2; + hrmEngine1.collectGarbage(); + hrmEngine2.collectGarbage(); + QCoreApplication::processEvents(QEventLoop::DeferredDeletion); + QCOMPARE(dtorCount, 6); + } + + dtorCount = 0; + { + // multiple engine interaction - linear reference with engine deletion + QDeclarativeEngine *hrmEngine1 = new QDeclarativeEngine; + QDeclarativeEngine *hrmEngine2 = new QDeclarativeEngine; + QDeclarativeComponent component1(hrmEngine1, TEST_FILE("handleReferenceManagement.handle.1.qml")); + QDeclarativeComponent component2(hrmEngine2, TEST_FILE("handleReferenceManagement.handle.1.qml")); + QObject *object1 = component1.create(); + QObject *object2 = component2.create(); + QVERIFY(object1 != 0); + QVERIFY(object2 != 0); + CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh"); + CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh"); + QVERIFY(crh1 != 0); + QVERIFY(crh2 != 0); + crh1->setDtorCount(&dtorCount); + crh2->setDtorCount(&dtorCount); + QMetaObject::invokeMethod(object1, "createReference"); + QMetaObject::invokeMethod(object2, "createReference"); + CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>(); + CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>(); + CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>(); + CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>(); + QVERIFY(first1 != 0); + QVERIFY(second1 != 0); + QVERIFY(first2 != 0); + QVERIFY(second2 != 0); + first1->addReference(QDeclarativeData::get(second1)->v8object); // create linear reference within engine1 + second1->addReference(QDeclarativeData::get(second2)->v8object); // create linear reference across engines + second2->addReference(QDeclarativeData::get(first2)->v8object); // create linear reference within engine2 + // now we have to reparent and change ownership to JS. + first1->setParent(crh1); + second1->setParent(0); + first2->setParent(0); + second2->setParent(0); + QDeclarativeEngine::setObjectOwnership(second1, QDeclarativeEngine::JavaScriptOwnership); + QDeclarativeEngine::setObjectOwnership(first2, QDeclarativeEngine::JavaScriptOwnership); + QDeclarativeEngine::setObjectOwnership(second2, QDeclarativeEngine::JavaScriptOwnership); + QMetaObject::invokeMethod(object1, "performGc"); + QMetaObject::invokeMethod(object2, "performGc"); + QCoreApplication::processEvents(QEventLoop::DeferredDeletion); + QCOMPARE(dtorCount, 0); + delete hrmEngine2; + QMetaObject::invokeMethod(object1, "performGc"); + QCoreApplication::processEvents(QEventLoop::DeferredDeletion); + QCOMPARE(dtorCount, 0); + delete object1; + delete object2; + hrmEngine1->collectGarbage(); + QCoreApplication::processEvents(QEventLoop::DeferredDeletion); + QCOMPARE(dtorCount, 6); + delete hrmEngine1; + } +} + // Test that assigning a null object works // Regressed with: df1788b4dbbb2826ae63f26bdf166342595343f4 void tst_qdeclarativeecmascript::nullObjectBinding() @@ -3597,6 +3851,36 @@ void tst_qdeclarativeecmascript::include() } } +void tst_qdeclarativeecmascript::signalHandlers() +{ + QDeclarativeComponent component(&engine, TEST_FILE("signalHandlers.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QVERIFY(o->property("count").toInt() == 0); + QMetaObject::invokeMethod(o, "testSignalCall"); + QCOMPARE(o->property("count").toInt(), 1); + + QMetaObject::invokeMethod(o, "testSignalHandlerCall"); + QCOMPARE(o->property("count").toInt(), 1); + QCOMPARE(o->property("errorString").toString(), QLatin1String("TypeError: Property 'onTestSignal' of object [object Object] is not a function")); + + QVERIFY(o->property("funcCount").toInt() == 0); + QMetaObject::invokeMethod(o, "testSignalConnection"); + QCOMPARE(o->property("funcCount").toInt(), 1); + + QMetaObject::invokeMethod(o, "testSignalHandlerConnection"); + QCOMPARE(o->property("funcCount").toInt(), 2); + + QMetaObject::invokeMethod(o, "testSignalDefined"); + QCOMPARE(o->property("definedResult").toBool(), true); + + QMetaObject::invokeMethod(o, "testSignalHandlerDefined"); + QCOMPARE(o->property("definedHandlerResult").toBool(), true); + + delete o; +} + void tst_qdeclarativeecmascript::qtbug_10696() { QDeclarativeComponent component(&engine, TEST_FILE("qtbug_10696.qml")); diff --git a/tests/auto/declarative/qdeclarativedebug/qdeclarativedebug.pro b/tests/auto/declarative/qdeclarativeenginedebug/qdeclarativeenginedebug.pro index 6187020a38..acf62acaf9 100644 --- a/tests/auto/declarative/qdeclarativedebug/qdeclarativedebug.pro +++ b/tests/auto/declarative/qdeclarativeenginedebug/qdeclarativeenginedebug.pro @@ -3,7 +3,7 @@ contains(QT_CONFIG,declarative): QT += network declarative macx:CONFIG -= app_bundle HEADERS += ../shared/debugutil_p.h -SOURCES += tst_qdeclarativedebug.cpp \ +SOURCES += tst_qdeclarativeenginedebug.cpp \ ../shared/debugutil.cpp CONFIG += parallel_test declarative_debug diff --git a/tests/auto/declarative/qdeclarativedebug/tst_qdeclarativedebug.cpp b/tests/auto/declarative/qdeclarativeenginedebug/tst_qdeclarativeenginedebug.cpp index b47579ea6a..d202a79eb9 100644 --- a/tests/auto/declarative/qdeclarativedebug/tst_qdeclarativedebug.cpp +++ b/tests/auto/declarative/qdeclarativeenginedebug/tst_qdeclarativeenginedebug.cpp @@ -53,7 +53,6 @@ #include <QtDeclarative/qsgitem.h> #include <private/qdeclarativebinding_p.h> -#include <private/qdeclarativedebug_p.h> #include <private/qdeclarativeenginedebug_p.h> #include <private/qdeclarativedebugservice_p.h> #include <private/qdeclarativemetatype_p.h> @@ -64,7 +63,7 @@ Q_DECLARE_METATYPE(QDeclarativeDebugWatch::State) -class tst_QDeclarativeDebug : public QObject +class tst_QDeclarativeEngineDebug : public QObject { Q_OBJECT @@ -128,7 +127,7 @@ signals: QML_DECLARE_TYPE(NonScriptProperty) -QDeclarativeDebugObjectReference tst_QDeclarativeDebug::findRootObject(int context, bool recursive) +QDeclarativeDebugObjectReference tst_QDeclarativeEngineDebug::findRootObject(int context, bool recursive) { QDeclarativeDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this); waitForQuery(q_engines); @@ -154,7 +153,7 @@ QDeclarativeDebugObjectReference tst_QDeclarativeDebug::findRootObject(int conte return result; } -QDeclarativeDebugPropertyReference tst_QDeclarativeDebug::findProperty(const QList<QDeclarativeDebugPropertyReference> &props, const QString &name) const +QDeclarativeDebugPropertyReference tst_QDeclarativeEngineDebug::findProperty(const QList<QDeclarativeDebugPropertyReference> &props, const QString &name) const { foreach(const QDeclarativeDebugPropertyReference &p, props) { if (p.name() == name) @@ -163,7 +162,7 @@ QDeclarativeDebugPropertyReference tst_QDeclarativeDebug::findProperty(const QLi return QDeclarativeDebugPropertyReference(); } -void tst_QDeclarativeDebug::waitForQuery(QDeclarativeDebugQuery *query) +void tst_QDeclarativeEngineDebug::waitForQuery(QDeclarativeDebugQuery *query) { QVERIFY(query); QCOMPARE(query->parent(), qobject_cast<QObject*>(this)); @@ -172,7 +171,7 @@ void tst_QDeclarativeDebug::waitForQuery(QDeclarativeDebugQuery *query) QFAIL("query timed out"); } -void tst_QDeclarativeDebug::recursiveObjectTest(QObject *o, const QDeclarativeDebugObjectReference &oref, bool recursive) const +void tst_QDeclarativeEngineDebug::recursiveObjectTest(QObject *o, const QDeclarativeDebugObjectReference &oref, bool recursive) const { const QMetaObject *meta = o->metaObject(); @@ -241,7 +240,7 @@ void tst_QDeclarativeDebug::recursiveObjectTest(QObject *o, const QDeclarativeDe } } -void tst_QDeclarativeDebug::recursiveCompareObjects(const QDeclarativeDebugObjectReference &a, const QDeclarativeDebugObjectReference &b) const +void tst_QDeclarativeEngineDebug::recursiveCompareObjects(const QDeclarativeDebugObjectReference &a, const QDeclarativeDebugObjectReference &b) const { QCOMPARE(a.debugId(), b.debugId()); QCOMPARE(a.className(), b.className()); @@ -265,7 +264,7 @@ void tst_QDeclarativeDebug::recursiveCompareObjects(const QDeclarativeDebugObjec recursiveCompareObjects(a.children()[i], b.children()[i]); } -void tst_QDeclarativeDebug::recursiveCompareContexts(const QDeclarativeDebugContextReference &a, const QDeclarativeDebugContextReference &b) const +void tst_QDeclarativeEngineDebug::recursiveCompareContexts(const QDeclarativeDebugContextReference &a, const QDeclarativeDebugContextReference &b) const { QCOMPARE(a.debugId(), b.debugId()); QCOMPARE(a.name(), b.name()); @@ -279,7 +278,7 @@ void tst_QDeclarativeDebug::recursiveCompareContexts(const QDeclarativeDebugCont recursiveCompareContexts(a.contexts()[i], b.contexts()[i]); } -void tst_QDeclarativeDebug::compareProperties(const QDeclarativeDebugPropertyReference &a, const QDeclarativeDebugPropertyReference &b) const +void tst_QDeclarativeEngineDebug::compareProperties(const QDeclarativeDebugPropertyReference &a, const QDeclarativeDebugPropertyReference &b) const { QCOMPARE(a.objectDebugId(), b.objectDebugId()); QCOMPARE(a.name(), b.name()); @@ -289,7 +288,7 @@ void tst_QDeclarativeDebug::compareProperties(const QDeclarativeDebugPropertyRef QCOMPARE(a.hasNotifySignal(), b.hasNotifySignal()); } -void tst_QDeclarativeDebug::initTestCase() +void tst_QDeclarativeEngineDebug::initTestCase() { qRegisterMetaType<QDeclarativeDebugWatch::State>(); qmlRegisterType<NonScriptProperty>("Test", 1, 0, "NonScriptPropertyElement"); @@ -383,7 +382,7 @@ void tst_QDeclarativeDebug::initTestCase() QTRY_VERIFY(m_dbg->status() == QDeclarativeEngineDebug::Enabled); } -void tst_QDeclarativeDebug::cleanupTestCase() +void tst_QDeclarativeEngineDebug::cleanupTestCase() { delete m_dbg; delete m_conn; @@ -391,7 +390,7 @@ void tst_QDeclarativeDebug::cleanupTestCase() delete m_engine; } -void tst_QDeclarativeDebug::setMethodBody() +void tst_QDeclarativeEngineDebug::setMethodBody() { QDeclarativeDebugObjectReference obj = findRootObject(2); @@ -428,7 +427,7 @@ void tst_QDeclarativeDebug::setMethodBody() } } -void tst_QDeclarativeDebug::watch_property() +void tst_QDeclarativeEngineDebug::watch_property() { QDeclarativeDebugObjectReference obj = findRootObject(); QDeclarativeDebugPropertyReference prop = findProperty(obj.properties(), "width"); @@ -473,7 +472,7 @@ void tst_QDeclarativeDebug::watch_property() QCOMPARE(spy.at(0).at(1).value<QVariant>(), qVariantFromValue(origWidth*2)); } -void tst_QDeclarativeDebug::watch_object() +void tst_QDeclarativeEngineDebug::watch_object() { QDeclarativeDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this); waitForQuery(q_engines); @@ -546,7 +545,7 @@ void tst_QDeclarativeDebug::watch_object() QCOMPARE(newHeight, origHeight * 2); } -void tst_QDeclarativeDebug::watch_expression() +void tst_QDeclarativeEngineDebug::watch_expression() { QFETCH(QString, expr); QFETCH(int, increment); @@ -609,7 +608,7 @@ void tst_QDeclarativeDebug::watch_expression() } } -void tst_QDeclarativeDebug::watch_expression_data() +void tst_QDeclarativeEngineDebug::watch_expression_data() { QTest::addColumn<QString>("expr"); QTest::addColumn<int>("increment"); @@ -619,21 +618,21 @@ void tst_QDeclarativeDebug::watch_expression_data() QTest::newRow("width+10") << "width + 10" << 10 << 5; } -void tst_QDeclarativeDebug::watch_context() +void tst_QDeclarativeEngineDebug::watch_context() { QDeclarativeDebugContextReference c; QTest::ignoreMessage(QtWarningMsg, "QDeclarativeEngineDebug::addWatch(): Not implemented"); QVERIFY(!m_dbg->addWatch(c, QString(), this)); } -void tst_QDeclarativeDebug::watch_file() +void tst_QDeclarativeEngineDebug::watch_file() { QDeclarativeDebugFileReference f; QTest::ignoreMessage(QtWarningMsg, "QDeclarativeEngineDebug::addWatch(): Not implemented"); QVERIFY(!m_dbg->addWatch(f, this)); } -void tst_QDeclarativeDebug::queryAvailableEngines() +void tst_QDeclarativeEngineDebug::queryAvailableEngines() { QDeclarativeDebugEnginesQuery *q_engines; @@ -668,7 +667,7 @@ void tst_QDeclarativeDebug::queryAvailableEngines() m_dbg = new QDeclarativeEngineDebug(m_conn, this); } -void tst_QDeclarativeDebug::queryRootContexts() +void tst_QDeclarativeEngineDebug::queryRootContexts() { QDeclarativeDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this); waitForQuery(q_engines); @@ -714,7 +713,7 @@ void tst_QDeclarativeDebug::queryRootContexts() m_dbg = new QDeclarativeEngineDebug(m_conn, this); } -void tst_QDeclarativeDebug::queryObject() +void tst_QDeclarativeEngineDebug::queryObject() { QFETCH(bool, recursive); @@ -786,7 +785,7 @@ void tst_QDeclarativeDebug::queryObject() } } -void tst_QDeclarativeDebug::queryObject_data() +void tst_QDeclarativeEngineDebug::queryObject_data() { QTest::addColumn<bool>("recursive"); @@ -794,7 +793,7 @@ void tst_QDeclarativeDebug::queryObject_data() QTest::newRow("recursive") << true; } -void tst_QDeclarativeDebug::queryExpressionResult() +void tst_QDeclarativeEngineDebug::queryExpressionResult() { QFETCH(QString, expr); QFETCH(QVariant, result); @@ -835,7 +834,7 @@ void tst_QDeclarativeDebug::queryExpressionResult() m_dbg = new QDeclarativeEngineDebug(m_conn, this); } -void tst_QDeclarativeDebug::queryExpressionResult_data() +void tst_QDeclarativeEngineDebug::queryExpressionResult_data() { QTest::addColumn<QString>("expr"); QTest::addColumn<QVariant>("result"); @@ -847,7 +846,7 @@ void tst_QDeclarativeDebug::queryExpressionResult_data() QTest::newRow("list of QObject*") << "varObjList" << qVariantFromValue(QString("<unknown value>")); } -void tst_QDeclarativeDebug::tst_QDeclarativeDebugFileReference() +void tst_QDeclarativeEngineDebug::tst_QDeclarativeDebugFileReference() { QDeclarativeDebugFileReference ref; QVERIFY(ref.url().isEmpty()); @@ -871,7 +870,7 @@ void tst_QDeclarativeDebug::tst_QDeclarativeDebugFileReference() } } -void tst_QDeclarativeDebug::tst_QDeclarativeDebugEngineReference() +void tst_QDeclarativeEngineDebug::tst_QDeclarativeDebugEngineReference() { QDeclarativeDebugEngineReference ref; QCOMPARE(ref.debugId(), -1); @@ -895,7 +894,7 @@ void tst_QDeclarativeDebug::tst_QDeclarativeDebugEngineReference() } } -void tst_QDeclarativeDebug::tst_QDeclarativeDebugObjectReference() +void tst_QDeclarativeEngineDebug::tst_QDeclarativeDebugObjectReference() { QDeclarativeDebugObjectReference ref; QCOMPARE(ref.debugId(), -1); @@ -928,7 +927,7 @@ void tst_QDeclarativeDebug::tst_QDeclarativeDebugObjectReference() recursiveCompareObjects(r, ref); } -void tst_QDeclarativeDebug::tst_QDeclarativeDebugContextReference() +void tst_QDeclarativeEngineDebug::tst_QDeclarativeDebugContextReference() { QDeclarativeDebugContextReference ref; QCOMPARE(ref.debugId(), -1); @@ -953,7 +952,7 @@ void tst_QDeclarativeDebug::tst_QDeclarativeDebugContextReference() recursiveCompareContexts(r, ref); } -void tst_QDeclarativeDebug::tst_QDeclarativeDebugPropertyReference() +void tst_QDeclarativeEngineDebug::tst_QDeclarativeDebugPropertyReference() { QDeclarativeDebugObjectReference rootObject = findRootObject(); QDeclarativeDebugObjectQuery *query = m_dbg->queryObject(rootObject, this); @@ -976,7 +975,7 @@ void tst_QDeclarativeDebug::tst_QDeclarativeDebugPropertyReference() compareProperties(r, ref); } -void tst_QDeclarativeDebug::setBindingForObject() +void tst_QDeclarativeEngineDebug::setBindingForObject() { QDeclarativeDebugObjectReference rootObject = findRootObject(); QVERIFY(rootObject.debugId() != -1); @@ -1047,7 +1046,7 @@ void tst_QDeclarativeDebug::setBindingForObject() QCOMPARE(onEnteredRef.value(), QVariant("{console.log('hello, world') }")); } -void tst_QDeclarativeDebug::setBindingInStates() +void tst_QDeclarativeEngineDebug::setBindingInStates() { // Check if changing bindings of propertychanges works @@ -1139,7 +1138,7 @@ void tst_QDeclarativeDebug::setBindingInStates() QCOMPARE(findProperty(obj.properties(),"width").value().toInt(), 300); } -void tst_QDeclarativeDebug::queryObjectTree() +void tst_QDeclarativeEngineDebug::queryObjectTree() { const int sourceIndex = 3; @@ -1209,12 +1208,13 @@ int main(int argc, char *argv[]) char **_argv = new char*[_argc]; for (int i = 0; i < argc; ++i) _argv[i] = argv[i]; - _argv[_argc - 1] = "-qmljsdebugger=port:3768"; + char arg[] = "-qmljsdebugger=port:3768"; + _argv[_argc - 1] = arg; QApplication app(_argc, _argv); - tst_QDeclarativeDebug tc; + tst_QDeclarativeEngineDebug tc; return QTest::qExec(&tc, _argc, _argv); delete _argv; } -#include "tst_qdeclarativedebug.moc" +#include "tst_qdeclarativeenginedebug.moc" diff --git a/tests/auto/declarative/qdeclarativepath/data/arc.qml b/tests/auto/declarative/qdeclarativepath/data/arc.qml new file mode 100644 index 0000000000..000221c784 --- /dev/null +++ b/tests/auto/declarative/qdeclarativepath/data/arc.qml @@ -0,0 +1,11 @@ +import QtQuick 2.0 + +Path { + startX: 0; startY: 0 + + PathArc { + x: 100; y: 100 + radiusX: 100; radiusY: 100 + direction: PathArc.Clockwise + } +} diff --git a/tests/auto/declarative/qdeclarativepath/data/curve.qml b/tests/auto/declarative/qdeclarativepath/data/curve.qml new file mode 100644 index 0000000000..c571186496 --- /dev/null +++ b/tests/auto/declarative/qdeclarativepath/data/curve.qml @@ -0,0 +1,9 @@ +import QtQuick 2.0 + +Path { + startX: 0; startY: 0 + + PathCurve { x: 100; y: 50 } + PathCurve { x: 50; y: 100 } + PathCurve { x: 100; y: 150 } +} diff --git a/tests/auto/declarative/qdeclarativepath/data/svg.qml b/tests/auto/declarative/qdeclarativepath/data/svg.qml new file mode 100644 index 0000000000..cec0f75061 --- /dev/null +++ b/tests/auto/declarative/qdeclarativepath/data/svg.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 + +Path { + PathSvg { path: "M200,300 Q400,50 600,300 T1000,300" } +} diff --git a/tests/auto/declarative/qdeclarativepath/qdeclarativepath.pro b/tests/auto/declarative/qdeclarativepath/qdeclarativepath.pro new file mode 100644 index 0000000000..d6fe1cf789 --- /dev/null +++ b/tests/auto/declarative/qdeclarativepath/qdeclarativepath.pro @@ -0,0 +1,17 @@ +load(qttest_p4) +contains(QT_CONFIG,declarative): QT += declarative +macx:CONFIG -= app_bundle + +SOURCES += tst_qdeclarativepath.cpp + +symbian: { + importFiles.files = data + importFiles.path = . + DEPLOYMENT += importFiles +} else { + DEFINES += SRCDIR=\\\"$$PWD\\\" +} + +CONFIG += parallel_test + +QT += core-private gui-private v8-private declarative-private diff --git a/tests/auto/declarative/qdeclarativepath/tst_qdeclarativepath.cpp b/tests/auto/declarative/qdeclarativepath/tst_qdeclarativepath.cpp new file mode 100644 index 0000000000..d8a539ea2a --- /dev/null +++ b/tests/auto/declarative/qdeclarativepath/tst_qdeclarativepath.cpp @@ -0,0 +1,169 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> +#include <QtDeclarative/qdeclarativeengine.h> +#include <QtDeclarative/qdeclarativecomponent.h> +#include <QtDeclarative/private/qdeclarativepath_p.h> + +#include "../../../shared/util.h" + +#ifdef Q_OS_SYMBIAN +// In Symbian OS test data is located in applications private dir +#define SRCDIR "." +#endif + +class tst_QDeclarativePath : public QObject +{ + Q_OBJECT +public: + tst_QDeclarativePath() {} + +private slots: + void arc(); + void catmullromCurve(); + void svg(); +}; + +void tst_QDeclarativePath::arc() +{ + QDeclarativeEngine engine; + QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/arc.qml")); + QDeclarativePath *obj = qobject_cast<QDeclarativePath*>(c.create()); + QVERIFY(obj != 0); + + QCOMPARE(obj->startX(), 0.); + QCOMPARE(obj->startY(), 0.); + + QDeclarativeListReference list(obj, "pathElements"); + QCOMPARE(list.count(), 1); + + QDeclarativePathArc* arc = qobject_cast<QDeclarativePathArc*>(list.at(0)); + QVERIFY(arc != 0); + QCOMPARE(arc->x(), 100.); + QCOMPARE(arc->y(), 100.); + QCOMPARE(arc->radiusX(), 100.); + QCOMPARE(arc->radiusY(), 100.); + QCOMPARE(arc->useLargeArc(), false); + QCOMPARE(arc->direction(), QDeclarativePathArc::Clockwise); + + QPainterPath path = obj->path(); + QVERIFY(path != QPainterPath()); + + QPointF pos = obj->pointAt(0); + QCOMPARE(pos, QPointF(0,0)); + pos = obj->pointAt(.25); + QCOMPARE(pos.toPoint(), QPoint(39,8)); //fuzzy compare + pos = obj->pointAt(.75); + QCOMPARE(pos.toPoint(), QPoint(92,61)); //fuzzy compare + pos = obj->pointAt(1); + QCOMPARE(pos, QPointF(100,100)); +} + +void tst_QDeclarativePath::catmullromCurve() +{ + QDeclarativeEngine engine; + QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/curve.qml")); + QDeclarativePath *obj = qobject_cast<QDeclarativePath*>(c.create()); + QVERIFY(obj != 0); + + QCOMPARE(obj->startX(), 0.); + QCOMPARE(obj->startY(), 0.); + + QDeclarativeListReference list(obj, "pathElements"); + QCOMPARE(list.count(), 3); + + QDeclarativePathCatmullRomCurve* arc = qobject_cast<QDeclarativePathCatmullRomCurve*>(list.at(0)); +// QVERIFY(arc != 0); +// QCOMPARE(arc->x(), 100.); +// QCOMPARE(arc->y(), 100.); +// QCOMPARE(arc->radiusX(), 100.); +// QCOMPARE(arc->radiusY(), 100.); +// QCOMPARE(arc->useLargeArc(), false); +// QCOMPARE(arc->direction(), QDeclarativePathArc::Clockwise); + + QPainterPath path = obj->path(); + QVERIFY(path != QPainterPath()); + + QPointF pos = obj->pointAt(0); + QCOMPARE(pos, QPointF(0,0)); + pos = obj->pointAt(.25); + QCOMPARE(pos.toPoint(), QPoint(63,26)); //fuzzy compare + pos = obj->pointAt(.75); + QCOMPARE(pos.toPoint(), QPoint(51,105)); //fuzzy compare + pos = obj->pointAt(1); + QCOMPARE(pos, QPointF(100,150)); +} + +void tst_QDeclarativePath::svg() +{ + QDeclarativeEngine engine; + QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/svg.qml")); + QDeclarativePath *obj = qobject_cast<QDeclarativePath*>(c.create()); + QVERIFY(obj != 0); + + QCOMPARE(obj->startX(), 0.); + QCOMPARE(obj->startY(), 0.); + + QDeclarativeListReference list(obj, "pathElements"); + QCOMPARE(list.count(), 1); + + QDeclarativePathSvg* svg = qobject_cast<QDeclarativePathSvg*>(list.at(0)); + QVERIFY(svg != 0); + QCOMPARE(svg->path(), QLatin1String("M200,300 Q400,50 600,300 T1000,300")); + + QPainterPath path = obj->path(); + QVERIFY(path != QPainterPath()); + + QPointF pos = obj->pointAt(0); + QCOMPARE(pos, QPointF(200,300)); + pos = obj->pointAt(.25); + QCOMPARE(pos.toPoint(), QPoint(400,175)); //fuzzy compare + pos = obj->pointAt(.75); + QCOMPARE(pos.toPoint(), QPoint(800,425)); //fuzzy compare + pos = obj->pointAt(1); + QCOMPARE(pos, QPointF(1000,300)); +} + + +QTEST_MAIN(tst_QDeclarativePath) + +#include "tst_qdeclarativepath.moc" diff --git a/tests/auto/declarative/qdeclarativepropertymap/tst_qdeclarativepropertymap.cpp b/tests/auto/declarative/qdeclarativepropertymap/tst_qdeclarativepropertymap.cpp index 5583701b12..966ac1950c 100644 --- a/tests/auto/declarative/qdeclarativepropertymap/tst_qdeclarativepropertymap.cpp +++ b/tests/auto/declarative/qdeclarativepropertymap/tst_qdeclarativepropertymap.cpp @@ -61,6 +61,7 @@ private slots: void count(); void crashBug(); + void QTBUG_17868(); }; void tst_QDeclarativePropertyMap::insert() @@ -218,6 +219,23 @@ void tst_QDeclarativePropertyMap::crashBug() delete obj; } +void tst_QDeclarativePropertyMap::QTBUG_17868() +{ + QDeclarativePropertyMap map; + + QDeclarativeEngine engine; + QDeclarativeContext context(&engine); + context.setContextProperty("map", &map); + map.insert("key", 1); + QDeclarativeComponent c(&engine); + c.setData("import QtQuick 2.0\nItem {property bool error:false; Component.onCompleted: {try{console.log(map.keys());}catch(e) {error=true;}}}",QUrl()); + QObject *obj = c.create(&context); + QVERIFY(obj); + QVERIFY(!obj->property("error").toBool()); + delete obj; + +} + QTEST_MAIN(tst_QDeclarativePropertyMap) #include "tst_qdeclarativepropertymap.moc" diff --git a/tests/auto/declarative/qdeclarativestates/data/avoidFastForward.qml b/tests/auto/declarative/qdeclarativestates/data/avoidFastForward.qml new file mode 100644 index 0000000000..519befc31e --- /dev/null +++ b/tests/auto/declarative/qdeclarativestates/data/avoidFastForward.qml @@ -0,0 +1,17 @@ +import QtQuick 2.0 + +Rectangle { + id: rect + width: 200 + height: 200 + + property int updateCount: 0 + onColorChanged: updateCount++ + + property color aColor: "green" + + states: State { + name: "a" + PropertyChanges { target: rect; color: aColor } + } +} diff --git a/tests/auto/declarative/qdeclarativestates/tst_qdeclarativestates.cpp b/tests/auto/declarative/qdeclarativestates/tst_qdeclarativestates.cpp index e13a6c4305..165179e35d 100644 --- a/tests/auto/declarative/qdeclarativestates/tst_qdeclarativestates.cpp +++ b/tests/auto/declarative/qdeclarativestates/tst_qdeclarativestates.cpp @@ -149,6 +149,7 @@ private slots: void extendsBug(); void editProperties(); void QTBUG_14830(); + void avoidFastForward(); }; void tst_qdeclarativestates::initTestCase() @@ -1513,6 +1514,20 @@ void tst_qdeclarativestates::QTBUG_14830() QCOMPARE(item->width(), qreal(171)); } +void tst_qdeclarativestates::avoidFastForward() +{ + QDeclarativeEngine engine; + + //shouldn't fast forward if there isn't a transition + QDeclarativeComponent c(&engine, SRCDIR "/data/avoidFastForward.qml"); + QSGRectangle *rect = qobject_cast<QSGRectangle*>(c.create()); + QVERIFY(rect != 0); + + QSGItemPrivate *rectPrivate = QSGItemPrivate::get(rect); + rectPrivate->setState("a"); + QCOMPARE(rect->property("updateCount").toInt(), 1); +} + QTEST_MAIN(tst_qdeclarativestates) #include "tst_qdeclarativestates.moc" diff --git a/tests/auto/declarative/qsgfocusscope/tst_qsgfocusscope.cpp b/tests/auto/declarative/qsgfocusscope/tst_qsgfocusscope.cpp index 83da0bae27..a5e998d1fa 100644 --- a/tests/auto/declarative/qsgfocusscope/tst_qsgfocusscope.cpp +++ b/tests/auto/declarative/qsgfocusscope/tst_qsgfocusscope.cpp @@ -127,10 +127,7 @@ void tst_qsgfocusscope::basic() view->requestActivateWindow(); qApp->processEvents(); -#ifdef Q_WS_X11 - // to be safe and avoid failing setFocus with window managers - qt_x11_wait_for_window_manager(view); -#endif + QTest::qWaitForWindowShown(view); QVERIFY(view->isTopLevel()); QVERIFY(item0->hasActiveFocus() == true); @@ -173,10 +170,7 @@ void tst_qsgfocusscope::nested() view->requestActivateWindow(); qApp->processEvents(); -#ifdef Q_WS_X11 - // to be safe and avoid failing setFocus with window managers - qt_x11_wait_for_window_manager(view); -#endif + QTest::qWaitForWindowShown(view); QVERIFY(view->windowState() == Qt::WindowActive); @@ -206,10 +200,7 @@ void tst_qsgfocusscope::noFocus() view->requestActivateWindow(); qApp->processEvents(); -#ifdef Q_WS_X11 - // to be safe and avoid failing setFocus with window managers - qt_x11_wait_for_window_manager(view); -#endif + QTest::qWaitForWindowShown(view); QVERIFY(view->windowState() == Qt::WindowActive); QVERIFY(item0->hasActiveFocus() == false); @@ -250,10 +241,7 @@ void tst_qsgfocusscope::textEdit() view->requestActivateWindow(); qApp->processEvents(); -#ifdef Q_WS_X11 - // to be safe and avoid failing setFocus with window managers - qt_x11_wait_for_window_manager(view); -#endif + QTest::qWaitForWindowShown(view); QVERIFY(view->windowState() == Qt::WindowActive); QVERIFY(item0->hasActiveFocus() == true); @@ -308,10 +296,7 @@ void tst_qsgfocusscope::forceFocus() view->requestActivateWindow(); qApp->processEvents(); -#ifdef Q_WS_X11 - // to be safe and avoid failing setFocus with window managers - qt_x11_wait_for_window_manager(view); -#endif + QTest::qWaitForWindowShown(view); QVERIFY(view->windowState() == Qt::WindowActive); QVERIFY(item0->hasActiveFocus() == true); @@ -382,10 +367,7 @@ void tst_qsgfocusscope::signalEmission() view->requestActivateWindow(); qApp->processEvents(); -#ifdef Q_WS_X11 - // to be safe and avoid failing setFocus with window managers - qt_x11_wait_for_window_manager(view); -#endif + QTest::qWaitForWindowShown(view); QVariant blue(QColor("blue")); QVariant red(QColor("red")); @@ -434,10 +416,7 @@ void tst_qsgfocusscope::qtBug13380() view->requestActivateWindow(); qApp->processEvents(); -#ifdef Q_WS_X11 - // to be safe and avoid failing setFocus with window managers - qt_x11_wait_for_window_manager(view); -#endif + QTest::qWaitForWindowShown(view); QVERIFY(view->windowState() == Qt::WindowActive); QVERIFY(view->rootObject()->property("noFocus").toBool()); @@ -613,10 +592,7 @@ void tst_qsgfocusscope::canvasFocus() view->requestActivateWindow(); qApp->processEvents(); -#ifdef Q_WS_X11 - // to be safe and avoid failing setFocus with window managers - qt_x11_wait_for_window_manager(view); -#endif + QTest::qWaitForWindowShown(view); // Now the canvas has focus, active focus given to item1 QTRY_COMPARE((view->windowState() == Qt::WindowActive), true); diff --git a/tests/auto/declarative/qsggridview/data/header.qml b/tests/auto/declarative/qsggridview/data/header.qml index 5978317a76..648e2a2298 100644 --- a/tests/auto/declarative/qsggridview/data/header.qml +++ b/tests/auto/declarative/qsggridview/data/header.qml @@ -24,8 +24,8 @@ Rectangle { GridView { id: grid objectName: "grid" - width: 240 - height: 320 + width: initialViewWidth + height: initialViewHeight cellWidth: 80 cellHeight: 60 model: testModel diff --git a/tests/auto/declarative/qsggridview/tst_qsggridview.cpp b/tests/auto/declarative/qsggridview/tst_qsggridview.cpp index 5933ee1103..98953aef40 100644 --- a/tests/auto/declarative/qsggridview/tst_qsggridview.cpp +++ b/tests/auto/declarative/qsggridview/tst_qsggridview.cpp @@ -78,6 +78,8 @@ private slots: void clear(); void moved(); void moved_data(); + void multipleChanges(); + void multipleChanges_data(); void swapWithFirstItem(); void changeFlow(); void currentIndex(); @@ -147,6 +149,8 @@ void tst_QSGGridView::cleanupTestCase() { } + + class TestModel : public QAbstractListModel { public: @@ -193,6 +197,13 @@ public: emit endInsertRows(); } + void insertItems(int index, const QList<QPair<QString, QString> > &items) { + emit beginInsertRows(QModelIndex(), index, index + items.count() - 1); + for (int i=0; i<items.count(); i++) + list.insert(index + i, QPair<QString,QString>(items[i].first, items[i].second)); + emit endInsertRows(); + } + void removeItem(int index) { emit beginRemoveRows(QModelIndex(), index, index); list.removeAt(index); @@ -352,8 +363,8 @@ void tst_QSGGridView::inserted() QTRY_VERIFY(contentItem != 0); model.insertItem(1, "Will", "9876"); - QCOMPARE(canvas->rootObject()->property("count").toInt(), model.count()); + QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count()); QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item QSGText *name = findItem<QSGText>(contentItem, "textName", 1); @@ -431,7 +442,7 @@ void tst_QSGGridView::removed() QTRY_VERIFY(contentItem != 0); model.removeItem(1); - QCOMPARE(canvas->rootObject()->property("count").toInt(), model.count()); + QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count()); QSGText *name = findItem<QSGText>(contentItem, "textName", 1); QTRY_VERIFY(name != 0); @@ -440,6 +451,7 @@ void tst_QSGGridView::removed() QTRY_VERIFY(number != 0); QTRY_COMPARE(number->text(), model.number(1)); + // Checks that onRemove is called QString removed = canvas->rootObject()->property("removed").toString(); QTRY_COMPARE(removed, QString("Item1")); @@ -449,13 +461,13 @@ void tst_QSGGridView::removed() for (int i = 0; i < model.count() && i < itemCount; ++i) { QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i); if (!item) qWarning() << "Item" << i << "not found"; - QTRY_VERIFY(item); QTRY_VERIFY(item->x() == (i%3)*80); QTRY_VERIFY(item->y() == (i/3)*60); } // Remove first item (which is the current item); model.removeItem(0); + QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count()); name = findItem<QSGText>(contentItem, "textName", 0); QTRY_VERIFY(name != 0); @@ -464,25 +476,25 @@ void tst_QSGGridView::removed() QTRY_VERIFY(number != 0); QTRY_COMPARE(number->text(), model.number(0)); + // Confirm items positioned correctly itemCount = findItems<QSGItem>(contentItem, "wrapper").count(); for (int i = 0; i < model.count() && i < itemCount; ++i) { QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i); if (!item) qWarning() << "Item" << i << "not found"; - QTRY_VERIFY(item); QTRY_VERIFY(item->x() == (i%3)*80); QTRY_VERIFY(item->y() == (i/3)*60); } // Remove items not visible model.removeItem(25); + QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count()); // Confirm items positioned correctly itemCount = findItems<QSGItem>(contentItem, "wrapper").count(); for (int i = 0; i < model.count() && i < itemCount; ++i) { QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i); if (!item) qWarning() << "Item" << i << "not found"; - QTRY_VERIFY(item); QTRY_VERIFY(item->x() == (i%3)*80); QTRY_VERIFY(item->y() == (i/3)*60); } @@ -495,12 +507,12 @@ void tst_QSGGridView::removed() QTRY_COMPARE(gridview->contentY(), 120.0); model.removeItem(1); + QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count()); // Confirm items positioned correctly for (int i = 6; i < 18; ++i) { QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i); if (!item) qWarning() << "Item" << i << "not found"; - QTRY_VERIFY(item); QTRY_VERIFY(item->x() == (i%3)*80); QTRY_VERIFY(item->y() == (i/3)*60); } @@ -508,20 +520,19 @@ void tst_QSGGridView::removed() // Remove currentIndex QSGItem *oldCurrent = gridview->currentItem(); model.removeItem(9); + QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count()); QTRY_COMPARE(gridview->currentIndex(), 9); QTRY_VERIFY(gridview->currentItem() != oldCurrent); gridview->setContentY(0); // let transitions settle. - QTest::qWait(100); + QTest::qWait(300); // Confirm items positioned correctly itemCount = findItems<QSGItem>(contentItem, "wrapper").count(); for (int i = 0; i < model.count() && i < itemCount; ++i) { QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i); - if (!item) qWarning() << "Item" << i << "not found"; - QTRY_VERIFY(item); QTRY_VERIFY(item->x() == (i%3)*80); QTRY_VERIFY(item->y() == (i/3)*60); } @@ -584,7 +595,7 @@ void tst_QSGGridView::clear() // confirm sanity when adding an item to cleared list model.addItem("New", "1"); - QVERIFY(gridview->count() == 1); + QTRY_COMPARE(gridview->count(), 1); QVERIFY(gridview->currentItem() != 0); QVERIFY(gridview->currentIndex() == 0); @@ -628,10 +639,12 @@ void tst_QSGGridView::moved() gridview->setContentY(contentY); model.moveItems(from, to, count); + // wait for items to move + QTest::qWait(300); + // Confirm items positioned correctly and indexes correct int firstVisibleIndex = qCeil(contentY / 60.0) * 3; int itemCount = findItems<QSGItem>(contentItem, "wrapper").count(); - for (int i = firstVisibleIndex; i < model.count() && i < itemCount; ++i) { if (i >= firstVisibleIndex + 18) // index has moved out of view continue; @@ -675,12 +688,12 @@ void tst_QSGGridView::moved_data() QTest::newRow("move 1 forwards, from non-visible -> visible") << 120.0 // show 6-23 << 1 << 23 << 1 - << 0.0; + << 0.0; // only 1 item was removed from the 1st row, so it doesn't move down QTest::newRow("move 1 forwards, from non-visible -> visible (move first item)") << 120.0 // // show 6-23 << 0 << 6 << 1 - << 0.0; + << 0.0; // only 1 item was removed from the 1st row, so it doesn't move down QTest::newRow("move 1 forwards, from visible -> non-visible") << 0.0 @@ -698,6 +711,11 @@ void tst_QSGGridView::moved_data() << 10 << 5 << 1 << 0.0; + QTest::newRow("move 1 backwards, within visible items (to first index)") + << 0.0 + << 10 << 0 << 1 + << 0.0; + QTest::newRow("move 1 backwards, from non-visible -> visible") << 0.0 << 28 << 8 << 1 @@ -711,12 +729,12 @@ void tst_QSGGridView::moved_data() QTest::newRow("move 1 backwards, from visible -> non-visible") << 120.0 // show 6-23 << 7 << 1 << 1 - << 60.0 * 2; // this results in a forward movement that removes 6 items (2 rows) + << 0.0; // only 1 item moved back, so items shift accordingly and first row doesn't move QTest::newRow("move 1 backwards, from visible -> non-visible (move first item)") << 120.0 // show 6-23 << 7 << 0 << 1 - << 60.0 * 2; // first visible item moved, so all items shift 2 rows down with it + << 0.0; // only 1 item moved back, so items shift accordingly and first row doesn't move QTest::newRow("move multiple forwards, within visible items") @@ -727,12 +745,12 @@ void tst_QSGGridView::moved_data() QTest::newRow("move multiple forwards, from non-visible -> visible") << 120.0 // show 6-23 << 1 << 6 << 3 - << 60.0; // top row moved, all items should shift down by 1 row + << 60.0; // 1st row (it's above visible area) disappears, 0 drops down 1 row, first visible item (6) stays where it is QTest::newRow("move multiple forwards, from non-visible -> visible (move first item)") << 120.0 // show 6-23 << 0 << 6 << 3 - << 60.0; // top row moved, all items should shift down by 1 row + << 60.0; // top row moved and shifted to below 3rd row, all items should shift down by 1 row QTest::newRow("move multiple forwards, from visible -> non-visible") << 0.0 @@ -763,14 +781,248 @@ void tst_QSGGridView::moved_data() QTest::newRow("move multiple backwards, from visible -> non-visible") << 120.0 // show 6-23 << 16 << 1 << 3 - << 60.0 * 5; // this results in a forward movement that removes 15 items (5 rows) + << -60.0; // to minimize movement, items are added above visible area, all items move up by 1 row QTest::newRow("move multiple backwards, from visible -> non-visible (move first item)") << 120.0 // show 6-23 << 16 << 0 << 3 - << 60.0 * 5; // this results in a forward movement that removes 16 items (5 rows) + << -60.0; // 16,17,18 move to above item 0, all items move up by 1 row } +struct ListChange { + enum { Inserted, Removed, Moved, SetCurrent } type; + int index; + int count; + int to; // Move + + static ListChange insert(int index, int count = 1) { ListChange c = { Inserted, index, count, -1 }; return c; } + static ListChange remove(int index, int count = 1) { ListChange c = { Removed, index, count, -1 }; return c; } + static ListChange move(int index, int to, int count) { ListChange c = { Moved, index, count, to }; return c; } + static ListChange setCurrent(int index) { ListChange c = { SetCurrent, index, -1, -1 }; return c; } +}; +Q_DECLARE_METATYPE(QList<ListChange>) + +void tst_QSGGridView::multipleChanges() +{ + QFETCH(int, startCount); + QFETCH(QList<ListChange>, changes); + QFETCH(int, newCount); + QFETCH(int, newCurrentIndex); + + QSGView *canvas = createView(); + canvas->show(); + + TestModel model; + for (int i = 0; i < startCount; i++) + model.addItem("Item" + QString::number(i), ""); + + QDeclarativeContext *ctxt = canvas->rootContext(); + ctxt->setContextProperty("testModel", &model); + ctxt->setContextProperty("testRightToLeft", QVariant(false)); + ctxt->setContextProperty("testTopToBottom", QVariant(false)); + + canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview1.qml")); + qApp->processEvents(); + + QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid"); + QTRY_VERIFY(gridview != 0); + + for (int i=0; i<changes.count(); i++) { + switch (changes[i].type) { + case ListChange::Inserted: + { + QList<QPair<QString, QString> > items; + for (int j=changes[i].index; j<changes[i].index + changes[i].count; ++j) + items << qMakePair(QString("new item " + j), QString::number(j)); + model.insertItems(changes[i].index, items); + break; + } + case ListChange::Removed: + model.removeItems(changes[i].index, changes[i].count); + break; + case ListChange::Moved: + model.moveItems(changes[i].index, changes[i].to, changes[i].count); + break; + case ListChange::SetCurrent: + gridview->setCurrentIndex(changes[i].index); + break; + } + } + + QTRY_COMPARE(gridview->count(), newCount); + QCOMPARE(gridview->count(), model.count()); + QTRY_COMPARE(gridview->currentIndex(), newCurrentIndex); + + QSGText *name; + QSGText *number; + QSGItem *contentItem = gridview->contentItem(); + QTRY_VERIFY(contentItem != 0); + int itemCount = findItems<QSGItem>(contentItem, "wrapper").count(); + for (int i=0; i < model.count() && i < itemCount; ++i) { + QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i); + QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i))); + name = findItem<QSGText>(contentItem, "textName", i); + QVERIFY(name != 0); + QTRY_COMPARE(name->text(), model.name(i)); + number = findItem<QSGText>(contentItem, "textNumber", i); + QVERIFY(number != 0); + QTRY_COMPARE(number->text(), model.number(i)); + } + + delete canvas; +} + +void tst_QSGGridView::multipleChanges_data() +{ + QTest::addColumn<int>("startCount"); + QTest::addColumn<QList<ListChange> >("changes"); + QTest::addColumn<int>("newCount"); + QTest::addColumn<int>("newCurrentIndex"); + + QList<ListChange> changes; + + for (int i=1; i<30; i++) + changes << ListChange::remove(0); + QTest::newRow("remove all but 1, first->last") << 30 << changes << 1 << 0; + + changes << ListChange::remove(0); + QTest::newRow("remove all") << 30 << changes << 0 << -1; + + changes.clear(); + changes << ListChange::setCurrent(29); + for (int i=29; i>0; i--) + changes << ListChange::remove(i); + QTest::newRow("remove last (current) -> first") << 30 << changes << 1 << 0; + + QTest::newRow("remove then insert at 0") << 10 << (QList<ListChange>() + << ListChange::remove(0, 1) + << ListChange::insert(0, 1) + ) << 10 << 1; + + QTest::newRow("remove then insert at non-zero index") << 10 << (QList<ListChange>() + << ListChange::setCurrent(2) + << ListChange::remove(2, 1) + << ListChange::insert(2, 1) + ) << 10 << 3; + + QTest::newRow("remove current then insert below it") << 10 << (QList<ListChange>() + << ListChange::setCurrent(1) + << ListChange::remove(1, 3) + << ListChange::insert(2, 2) + ) << 9 << 1; + + QTest::newRow("remove current index then move it down") << 10 << (QList<ListChange>() + << ListChange::setCurrent(2) + << ListChange::remove(1, 3) + << ListChange::move(1, 5, 1) + ) << 7 << 5; + + QTest::newRow("remove current index then move it up") << 10 << (QList<ListChange>() + << ListChange::setCurrent(5) + << ListChange::remove(4, 3) + << ListChange::move(4, 1, 1) + ) << 7 << 1; + + + QTest::newRow("insert multiple times") << 0 << (QList<ListChange>() + << ListChange::insert(0, 2) + << ListChange::insert(0, 4) + << ListChange::insert(0, 6) + ) << 12 << 10; + + QTest::newRow("insert multiple times with current index changes") << 0 << (QList<ListChange>() + << ListChange::insert(0, 2) + << ListChange::insert(0, 4) + << ListChange::insert(0, 6) + << ListChange::setCurrent(3) + << ListChange::insert(3, 2) + ) << 14 << 5; + + QTest::newRow("insert and remove all") << 0 << (QList<ListChange>() + << ListChange::insert(0, 30) + << ListChange::remove(0, 30) + ) << 0 << -1; + + QTest::newRow("insert and remove current") << 30 << (QList<ListChange>() + << ListChange::insert(1) + << ListChange::setCurrent(1) + << ListChange::remove(1) + ) << 30 << 1; + + QTest::newRow("insert before 0, then remove cross section of new and old items") << 10 << (QList<ListChange>() + << ListChange::insert(0, 10) + << ListChange::remove(5, 10) + ) << 10 << 5; + + QTest::newRow("insert multiple, then move new items to end") << 10 << (QList<ListChange>() + << ListChange::insert(0, 3) + << ListChange::move(0, 10, 3) + ) << 13 << 0; + + QTest::newRow("insert multiple, then move new and some old items to end") << 10 << (QList<ListChange>() + << ListChange::insert(0, 3) + << ListChange::move(0, 8, 5) + ) << 13 << 11; + + QTest::newRow("insert multiple at end, then move new and some old items to start") << 10 << (QList<ListChange>() + << ListChange::setCurrent(9) + << ListChange::insert(10, 3) + << ListChange::move(8, 0, 5) + ) << 13 << 1; + + + QTest::newRow("move back and forth to same index") << 10 << (QList<ListChange>() + << ListChange::setCurrent(1) + << ListChange::move(1, 2, 2) + << ListChange::move(2, 1, 2) + ) << 10 << 1; + + QTest::newRow("move forwards then back") << 10 << (QList<ListChange>() + << ListChange::setCurrent(2) + << ListChange::move(1, 2, 3) + << ListChange::move(3, 0, 5) + ) << 10 << 0; + + QTest::newRow("move current, then remove it") << 10 << (QList<ListChange>() + << ListChange::setCurrent(5) + << ListChange::move(5, 0, 1) + << ListChange::remove(0) + ) << 9 << 0; + + QTest::newRow("move current, then insert before it") << 10 << (QList<ListChange>() + << ListChange::setCurrent(5) + << ListChange::move(5, 0, 1) + << ListChange::insert(0) + ) << 11 << 1; + + QTest::newRow("move multiple, then remove them") << 10 << (QList<ListChange>() + << ListChange::setCurrent(1) + << ListChange::move(5, 1, 3) + << ListChange::remove(1, 3) + ) << 7 << 1; + + QTest::newRow("move multiple, then insert before them") << 10 << (QList<ListChange>() + << ListChange::setCurrent(5) + << ListChange::move(5, 1, 3) + << ListChange::insert(1, 5) + ) << 15 << 6; + + QTest::newRow("move multiple, then insert after them") << 10 << (QList<ListChange>() + << ListChange::setCurrent(3) + << ListChange::move(0, 1, 2) + << ListChange::insert(3, 5) + ) << 15 << 8; + + + QTest::newRow("clear current") << 0 << (QList<ListChange>() + << ListChange::insert(0, 5) + << ListChange::setCurrent(-1) + << ListChange::remove(0, 5) + << ListChange::insert(0, 5) + ) << 5 << -1; +} + + void tst_QSGGridView::swapWithFirstItem() { // QTBUG_9697 @@ -2041,6 +2293,7 @@ void tst_QSGGridView::footer() QFETCH(QPointF, initialContentPos); QFETCH(QPointF, changedContentPos); QFETCH(QPointF, firstDelegatePos); + QFETCH(QPointF, resizeContentPos); QSGView *canvas = createView(); canvas->show(); @@ -2130,6 +2383,11 @@ void tst_QSGGridView::footer() QVERIFY(item); QCOMPARE(item->pos(), firstDelegatePos); + gridview->positionViewAtEnd(); + footer->setHeight(10); + footer->setWidth(40); + QTRY_COMPARE(QPointF(gridview->contentX(), gridview->contentY()), resizeContentPos); + delete canvas; } @@ -2142,11 +2400,13 @@ void tst_QSGGridView::footer_data() QTest::addColumn<QPointF>("initialContentPos"); QTest::addColumn<QPointF>("changedContentPos"); QTest::addColumn<QPointF>("firstDelegatePos"); + QTest::addColumn<QPointF>("resizeContentPos"); // footer1 = 100 x 30 - // footer2 = 100 x 20 + // footer2 = 50 x 20 // cells = 80 * 60 // view width = 240 + // view height = 320 // footer below items, bottom left QTest::newRow("flow left to right") << QSGGridView::LeftToRight << Qt::LeftToRight @@ -2154,7 +2414,8 @@ void tst_QSGGridView::footer_data() << QPointF(0, 10 * 60) // 30 items = 10 rows << QPointF(0, 0) << QPointF(0, 0) - << QPointF(0, 0); + << QPointF(0, 0) + << QPointF(0, 10 * 60 - 320 + 10); // footer below items, bottom right QTest::newRow("flow left to right, layout right to left") << QSGGridView::LeftToRight << Qt::RightToLeft @@ -2162,7 +2423,8 @@ void tst_QSGGridView::footer_data() << QPointF((240 - 100) + 50, 10 * 60) // 50 = width diff between old and new footers << QPointF(0, 0) << QPointF(0, 0) - << QPointF(240 - 80, 0); + << QPointF(240 - 80, 0) + << QPointF(0, 10 * 60 - 320 + 10); // footer to right of items QTest::newRow("flow top to bottom, layout left to right") << QSGGridView::TopToBottom << Qt::LeftToRight @@ -2170,7 +2432,8 @@ void tst_QSGGridView::footer_data() << QPointF(6 * 80, 0) // 30 items = 6 columns << QPointF(0, 0) << QPointF(0, 0) - << QPointF(0, 0); + << QPointF(0, 0) + << QPointF(6 * 80 - 240 + 40, 0); // footer to left of items QTest::newRow("flow top to bottom, layout right to left") << QSGGridView::TopToBottom << Qt::RightToLeft @@ -2178,7 +2441,8 @@ void tst_QSGGridView::footer_data() << QPointF(-(6 * 80) - 50, 0) // 50 = new footer width << QPointF(-240, 0) << QPointF(-240, 0) // unchanged, footer change doesn't change content pos - << QPointF(-80, 0); + << QPointF(-80, 0) + << QPointF(-(6 * 80) - 40, 0); } void tst_QSGGridView::header() @@ -2190,18 +2454,17 @@ void tst_QSGGridView::header() QFETCH(QPointF, initialContentPos); QFETCH(QPointF, changedContentPos); QFETCH(QPointF, firstDelegatePos); - - QSGView *canvas = createView(); + QFETCH(QPointF, resizeContentPos); TestModel model; for (int i = 0; i < 30; i++) model.addItem("Item" + QString::number(i), ""); - QDeclarativeContext *ctxt = canvas->rootContext(); - ctxt->setContextProperty("testModel", &model); - + QSGView *canvas = createView(); + canvas->rootContext()->setContextProperty("testModel", &model); + canvas->rootContext()->setContextProperty("initialViewWidth", 240); + canvas->rootContext()->setContextProperty("initialViewHeight", 320); canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/header.qml")); - qApp->processEvents(); QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid"); QTRY_VERIFY(gridview != 0); @@ -2252,6 +2515,31 @@ void tst_QSGGridView::header() QVERIFY(item); QCOMPARE(item->pos(), firstDelegatePos); + header->setHeight(10); + header->setWidth(40); + QTRY_COMPARE(QPointF(gridview->contentX(), gridview->contentY()), resizeContentPos); + + delete canvas; + + + // QTBUG-21207 header should become visible if view resizes from initial empty size + + canvas = createView(); + canvas->rootContext()->setContextProperty("testModel", &model); + canvas->rootContext()->setContextProperty("initialViewWidth", 240); + canvas->rootContext()->setContextProperty("initialViewHeight", 320); + canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/header.qml")); + + gridview = findItem<QSGGridView>(canvas->rootObject(), "grid"); + QTRY_VERIFY(gridview != 0); + gridview->setFlow(flow); + gridview->setLayoutDirection(layoutDirection); + + gridview->setWidth(240); + gridview->setHeight(320); + QTRY_COMPARE(gridview->headerItem()->pos(), initialHeaderPos); + QCOMPARE(QPointF(gridview->contentX(), gridview->contentY()), initialContentPos); + delete canvas; } @@ -2264,9 +2552,10 @@ void tst_QSGGridView::header_data() QTest::addColumn<QPointF>("initialContentPos"); QTest::addColumn<QPointF>("changedContentPos"); QTest::addColumn<QPointF>("firstDelegatePos"); + QTest::addColumn<QPointF>("resizeContentPos"); // header1 = 100 x 30 - // header2 = 100 x 20 + // header2 = 50 x 20 // cells = 80 x 60 // view width = 240 @@ -2276,7 +2565,8 @@ void tst_QSGGridView::header_data() << QPointF(0, -20) << QPointF(0, -30) << QPointF(0, -20) - << QPointF(0, 0); + << QPointF(0, 0) + << QPointF(0, -10); // header above items, top right QTest::newRow("flow left to right, layout right to left") << QSGGridView::LeftToRight << Qt::RightToLeft @@ -2284,7 +2574,8 @@ void tst_QSGGridView::header_data() << QPointF((240 - 100) + 50, -20) // 50 = width diff between old and new headers << QPointF(0, -30) << QPointF(0, -20) - << QPointF(160, 0); + << QPointF(160, 0) + << QPointF(0, -10); // header to left of items QTest::newRow("flow top to bottom, layout left to right") << QSGGridView::TopToBottom << Qt::LeftToRight @@ -2292,7 +2583,8 @@ void tst_QSGGridView::header_data() << QPointF(-50, 0) << QPointF(-100, 0) << QPointF(-50, 0) - << QPointF(0, 0); + << QPointF(0, 0) + << QPointF(-40, 0); // header to right of items QTest::newRow("flow top to bottom, layout right to left") << QSGGridView::TopToBottom << Qt::RightToLeft @@ -2300,7 +2592,8 @@ void tst_QSGGridView::header_data() << QPointF(0, 0) << QPointF(-(240 - 100), 0) << QPointF(-(240 - 50), 0) - << QPointF(-80, 0); + << QPointF(-80, 0) + << QPointF(-(240 - 40), 0); } void tst_QSGGridView::indexAt() @@ -2372,10 +2665,11 @@ void tst_QSGGridView::onAdd() items << qMakePair(QString("value %1").arg(i), QString::number(i)); model.addItems(items); + QTRY_COMPARE(model.count(), qobject_cast<QSGGridView*>(canvas->rootObject())->count()); qApp->processEvents(); QVariantList result = object->property("addedDelegates").toList(); - QCOMPARE(result.count(), items.count()); + QTRY_COMPARE(result.count(), items.count()); for (int i=0; i<items.count(); i++) QCOMPARE(result[i].toString(), items[i].first); @@ -2420,10 +2714,8 @@ void tst_QSGGridView::onRemove() canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/attachedSignals.qml")); QObject *object = canvas->rootObject(); - qApp->processEvents(); - model.removeItems(indexToRemove, removeCount); - qApp->processEvents(); + QTRY_COMPARE(model.count(), qobject_cast<QSGGridView*>(canvas->rootObject())->count()); QCOMPARE(object->property("removedDelegateCount"), QVariant(removeCount)); delete canvas; diff --git a/tests/auto/declarative/qsglistview/data/header.qml b/tests/auto/declarative/qsglistview/data/header.qml index b073146582..bf70310630 100644 --- a/tests/auto/declarative/qsglistview/data/header.qml +++ b/tests/auto/declarative/qsglistview/data/header.qml @@ -24,8 +24,8 @@ Rectangle { id: list objectName: "list" focus: true - width: 240 - height: 320 + width: initialViewWidth + height: initialViewHeight snapMode: ListView.SnapToItem model: testModel delegate: myDelegate diff --git a/tests/auto/declarative/qsglistview/tst_qsglistview.cpp b/tests/auto/declarative/qsglistview/tst_qsglistview.cpp index 2b81f7cfd9..5a6fb35ab9 100644 --- a/tests/auto/declarative/qsglistview/tst_qsglistview.cpp +++ b/tests/auto/declarative/qsglistview/tst_qsglistview.cpp @@ -51,6 +51,7 @@ #include <QtDeclarative/private/qsgvisualitemmodel_p.h> #include <QtDeclarative/private/qdeclarativelistmodel_p.h> #include <QtDeclarative/private/qlistmodelinterface_p.h> +#include <QtDeclarative/private/qdeclarativechangeset_p.h> #include "../../../shared/util.h" #include "incrementalmodel.h" #include <QtOpenGL/QGLShaderProgram> @@ -90,6 +91,9 @@ private slots: void qAbstractItemModel_moved(); void qAbstractItemModel_moved_data(); + void multipleChanges(); + void multipleChanges_data(); + void qListModelInterface_clear(); void qAbstractItemModel_clear(); @@ -291,6 +295,12 @@ public: emit itemsInserted(index, 1); } + void insertItems(int index, const QList<QPair<QString, QString> > &items) { + for (int i=0; i<items.count(); i++) + list.insert(index + i, QPair<QString,QString>(items[i].first, items[i].second)); + emit itemsInserted(index, items.count()); + } + void removeItem(int index) { list.removeAt(index); emit itemsRemoved(index, 1); @@ -375,6 +385,13 @@ public: emit endInsertRows(); } + void insertItems(int index, const QList<QPair<QString, QString> > &items) { + emit beginInsertRows(QModelIndex(), index, index+items.count()-1); + for (int i=0; i<items.count(); i++) + list.insert(index + i, QPair<QString,QString>(items[i].first, items[i].second)); + emit endInsertRows(); + } + void removeItem(int index) { emit beginRemoveRows(QModelIndex(), index, index); list.removeAt(index); @@ -541,6 +558,7 @@ template <class T> void tst_QSGListView::inserted() { QSGView *canvas = createView(); + canvas->show(); T model; model.addItem("Fred", "12345"); @@ -564,6 +582,7 @@ void tst_QSGListView::inserted() model.insertItem(1, "Will", "9876"); + QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count()); QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item QSGText *name = findItem<QSGText>(contentItem, "textName", 1); @@ -581,7 +600,7 @@ void tst_QSGListView::inserted() model.insertItem(0, "Foo", "1111"); // zero index, and current item - QCOMPARE(canvas->rootObject()->property("count").toInt(), model.count()); + QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count()); QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item name = findItem<QSGText>(contentItem, "textName", 0); @@ -622,6 +641,8 @@ void tst_QSGListView::inserted() // QTBUG-19675 model.clear(); model.insertItem(0, "Hello", "1234"); + QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count()); + QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", 0); QVERIFY(item); QCOMPARE(item->y(), 0.); @@ -657,7 +678,7 @@ void tst_QSGListView::removed(bool animated) QTRY_VERIFY(contentItem != 0); model.removeItem(1); - QCOMPARE(canvas->rootObject()->property("count").toInt(), model.count()); + QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count()); QSGText *name = findItem<QSGText>(contentItem, "textName", 1); QTRY_VERIFY(name != 0); @@ -677,8 +698,7 @@ void tst_QSGListView::removed(bool animated) // Remove first item (which is the current item); model.removeItem(0); // post: top item starts at 20 - - QTest::qWait(300); + QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count()); name = findItem<QSGText>(contentItem, "textName", 0); QTRY_VERIFY(name != 0); @@ -698,7 +718,7 @@ void tst_QSGListView::removed(bool animated) // Remove items not visible model.removeItem(18); - qApp->processEvents(); + QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count()); // Confirm items positioned correctly itemCount = findItems<QSGItem>(contentItem, "wrapper").count(); @@ -714,7 +734,7 @@ void tst_QSGListView::removed(bool animated) listview->setCurrentIndex(10); model.removeItem(1); // post: top item will be at 40 - qApp->processEvents(); + QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count()); // Confirm items positioned correctly for (int i = 2; i < 18; ++i) { @@ -766,7 +786,7 @@ void tst_QSGListView::removed(bool animated) // remove all visible items model.removeItems(1, 18); - QTest::qWait(300); + QTRY_COMPARE(listview->count() , model.count()); // Confirm items positioned correctly itemCount = findItems<QSGItem>(contentItem, "wrapper").count(); @@ -778,10 +798,13 @@ void tst_QSGListView::removed(bool animated) } model.removeItems(1, 17); -// QTest::qWait(300); + QTRY_COMPARE(listview->count() , model.count()); model.removeItems(2, 1); + QTRY_COMPARE(listview->count() , model.count()); + model.addItem("New", "1"); + QTRY_COMPARE(listview->count() , model.count()); QTRY_VERIFY(name = findItem<QSGText>(contentItem, "textName", model.count()-1)); QCOMPARE(name->text(), QString("New")); @@ -886,12 +909,23 @@ void tst_QSGListView::moved() listview->setContentY(contentY); model.moveItems(from, to, count); - qApp->processEvents(); + + // wait for items to move + QTest::qWait(300); + + QList<QSGItem*> items = findItems<QSGItem>(contentItem, "wrapper"); + int firstVisibleIndex = -1; + for (int i=0; i<items.count(); i++) { + if (items[i]->y() >= contentY) { + QDeclarativeExpression e(qmlContext(items[i]), items[i], "index"); + firstVisibleIndex = e.evaluate().toInt(); + break; + } + } + QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex)); // Confirm items positioned correctly and indexes correct - int firstVisibleIndex = qCeil(contentY / 20.0); int itemCount = findItems<QSGItem>(contentItem, "wrapper").count(); - for (int i = firstVisibleIndex; i < model.count() && i < itemCount; ++i) { if (i >= firstVisibleIndex + 16) // index has moved out of view continue; @@ -934,12 +968,12 @@ void tst_QSGListView::moved_data() QTest::newRow("move 1 forwards, from non-visible -> visible") << 80.0 // show 4-19 << 1 << 18 << 1 - << 20.0; + << 20.0; // removed 1 item above the first visible, so item 0 should drop down by 1 to minimize movement QTest::newRow("move 1 forwards, from non-visible -> visible (move first item)") << 80.0 // show 4-19 << 0 << 4 << 1 - << 20.0; + << 20.0; // first item has moved to below item4, everything drops down by size of 1 item QTest::newRow("move 1 forwards, from visible -> non-visible") << 0.0 @@ -957,6 +991,11 @@ void tst_QSGListView::moved_data() << 4 << 1 << 1 << 0.0; + QTest::newRow("move 1 backwards, within visible items (to first index)") + << 0.0 + << 4 << 0 << 1 + << 0.0; + QTest::newRow("move 1 backwards, from non-visible -> visible") << 0.0 << 20 << 4 << 1 @@ -970,12 +1009,12 @@ void tst_QSGListView::moved_data() QTest::newRow("move 1 backwards, from visible -> non-visible") << 80.0 // show 4-19 << 16 << 1 << 1 - << 20.0 * 15; // this results in a forward movement that removes 15 items + << -20.0; // to minimize movement, item 0 moves to -20, and other items do not move QTest::newRow("move 1 backwards, from visible -> non-visible (move first item)") << 80.0 // show 4-19 << 16 << 0 << 1 - << 20.0 * 16; // everything should move to after item 16 + << -20.0; // to minimize movement, item 16 (now at 0) moves to -20, and other items do not move QTest::newRow("move multiple forwards, within visible items") @@ -1022,12 +1061,248 @@ void tst_QSGListView::moved_data() QTest::newRow("move multiple backwards, from visible -> non-visible") << 80.0 // show 4-19 << 16 << 1 << 3 - << 20.0 * 15; // this results in a forward movement that removes 15 items + << -20.0 * 3; // to minimize movement, 0 moves by -60, and other items do not move QTest::newRow("move multiple backwards, from visible -> non-visible (move first item)") << 80.0 // show 4-19 << 16 << 0 << 3 - << 20.0 * 16; + << -20.0 * 3; // to minimize movement, 16,17,18 move to above item 0, and other items do not move +} + + +struct ListChange { + enum { Inserted, Removed, Moved, SetCurrent } type; + int index; + int count; + int to; // Move + + static ListChange insert(int index, int count = 1) { ListChange c = { Inserted, index, count, -1 }; return c; } + static ListChange remove(int index, int count = 1) { ListChange c = { Removed, index, count, -1 }; return c; } + static ListChange move(int index, int to, int count) { ListChange c = { Moved, index, count, to }; return c; } + static ListChange setCurrent(int index) { ListChange c = { SetCurrent, index, -1, -1 }; return c; } +}; +Q_DECLARE_METATYPE(QList<ListChange>) + +void tst_QSGListView::multipleChanges() +{ + QFETCH(int, startCount); + QFETCH(QList<ListChange>, changes); + QFETCH(int, newCount); + QFETCH(int, newCurrentIndex); + + QSGView *canvas = createView(); + canvas->show(); + + TestModel model; + for (int i = 0; i < startCount; i++) + model.addItem("Item" + QString::number(i), ""); + + QDeclarativeContext *ctxt = canvas->rootContext(); + ctxt->setContextProperty("testModel", &model); + + TestObject *testObject = new TestObject; + ctxt->setContextProperty("testObject", testObject); + + canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/listviewtest.qml")); + qApp->processEvents(); + + QSGListView *listview = findItem<QSGListView>(canvas->rootObject(), "list"); + QTRY_VERIFY(listview != 0); + + for (int i=0; i<changes.count(); i++) { + switch (changes[i].type) { + case ListChange::Inserted: + { + QList<QPair<QString, QString> > items; + for (int j=changes[i].index; j<changes[i].index + changes[i].count; ++j) + items << qMakePair(QString("new item " + j), QString::number(j)); + model.insertItems(changes[i].index, items); + break; + } + case ListChange::Removed: + model.removeItems(changes[i].index, changes[i].count); + break; + case ListChange::Moved: + model.moveItems(changes[i].index, changes[i].to, changes[i].count); + break; + case ListChange::SetCurrent: + listview->setCurrentIndex(changes[i].index); + break; + } + } + + QTRY_COMPARE(listview->count(), newCount); + QCOMPARE(listview->count(), model.count()); + QTRY_COMPARE(listview->currentIndex(), newCurrentIndex); + + QSGText *name; + QSGText *number; + QSGItem *contentItem = listview->contentItem(); + QTRY_VERIFY(contentItem != 0); + int itemCount = findItems<QSGItem>(contentItem, "wrapper").count(); + for (int i=0; i < model.count() && i < itemCount; ++i) { + QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i); + QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i))); + name = findItem<QSGText>(contentItem, "textName", i); + QVERIFY(name != 0); + QTRY_COMPARE(name->text(), model.name(i)); + number = findItem<QSGText>(contentItem, "textNumber", i); + QVERIFY(number != 0); + QTRY_COMPARE(number->text(), model.number(i)); + } + + delete testObject; + delete canvas; +} + +void tst_QSGListView::multipleChanges_data() +{ + QTest::addColumn<int>("startCount"); + QTest::addColumn<QList<ListChange> >("changes"); + QTest::addColumn<int>("newCount"); + QTest::addColumn<int>("newCurrentIndex"); + + QList<ListChange> changes; + + for (int i=1; i<30; i++) + changes << ListChange::remove(0); + QTest::newRow("remove all but 1, first->last") << 30 << changes << 1 << 0; + + changes << ListChange::remove(0); + QTest::newRow("remove all") << 30 << changes << 0 << -1; + + changes.clear(); + changes << ListChange::setCurrent(29); + for (int i=29; i>0; i--) + changes << ListChange::remove(i); + QTest::newRow("remove last (current) -> first") << 30 << changes << 1 << 0; + + QTest::newRow("remove then insert at 0") << 10 << (QList<ListChange>() + << ListChange::remove(0, 1) + << ListChange::insert(0, 1) + ) << 10 << 1; + + QTest::newRow("remove then insert at non-zero index") << 10 << (QList<ListChange>() + << ListChange::setCurrent(2) + << ListChange::remove(2, 1) + << ListChange::insert(2, 1) + ) << 10 << 3; + + QTest::newRow("remove current then insert below it") << 10 << (QList<ListChange>() + << ListChange::setCurrent(1) + << ListChange::remove(1, 3) + << ListChange::insert(2, 2) + ) << 9 << 1; + + QTest::newRow("remove current index then move it down") << 10 << (QList<ListChange>() + << ListChange::setCurrent(2) + << ListChange::remove(1, 3) + << ListChange::move(1, 5, 1) + ) << 7 << 5; + + QTest::newRow("remove current index then move it up") << 10 << (QList<ListChange>() + << ListChange::setCurrent(5) + << ListChange::remove(4, 3) + << ListChange::move(4, 1, 1) + ) << 7 << 1; + + + QTest::newRow("insert multiple times") << 0 << (QList<ListChange>() + << ListChange::insert(0, 2) + << ListChange::insert(0, 4) + << ListChange::insert(0, 6) + ) << 12 << 10; + + QTest::newRow("insert multiple times with current index changes") << 0 << (QList<ListChange>() + << ListChange::insert(0, 2) + << ListChange::insert(0, 4) + << ListChange::insert(0, 6) + << ListChange::setCurrent(3) + << ListChange::insert(3, 2) + ) << 14 << 5; + + QTest::newRow("insert and remove all") << 0 << (QList<ListChange>() + << ListChange::insert(0, 30) + << ListChange::remove(0, 30) + ) << 0 << -1; + + QTest::newRow("insert and remove current") << 30 << (QList<ListChange>() + << ListChange::insert(1) + << ListChange::setCurrent(1) + << ListChange::remove(1) + ) << 30 << 1; + + QTest::newRow("insert before 0, then remove cross section of new and old items") << 10 << (QList<ListChange>() + << ListChange::insert(0, 10) + << ListChange::remove(5, 10) + ) << 10 << 5; + + QTest::newRow("insert multiple, then move new items to end") << 10 << (QList<ListChange>() + << ListChange::insert(0, 3) + << ListChange::move(0, 10, 3) + ) << 13 << 0; + + QTest::newRow("insert multiple, then move new and some old items to end") << 10 << (QList<ListChange>() + << ListChange::insert(0, 3) + << ListChange::move(0, 8, 5) + ) << 13 << 11; + + QTest::newRow("insert multiple at end, then move new and some old items to start") << 10 << (QList<ListChange>() + << ListChange::setCurrent(9) + << ListChange::insert(10, 3) + << ListChange::move(8, 0, 5) + ) << 13 << 1; + + + QTest::newRow("move back and forth to same index") << 10 << (QList<ListChange>() + << ListChange::setCurrent(1) + << ListChange::move(1, 2, 2) + << ListChange::move(2, 1, 2) + ) << 10 << 1; + + QTest::newRow("move forwards then back") << 10 << (QList<ListChange>() + << ListChange::setCurrent(2) + << ListChange::move(1, 2, 3) + << ListChange::move(3, 0, 5) + ) << 10 << 0; + + QTest::newRow("move current, then remove it") << 10 << (QList<ListChange>() + << ListChange::setCurrent(5) + << ListChange::move(5, 0, 1) + << ListChange::remove(0) + ) << 9 << 0; + + QTest::newRow("move current, then insert before it") << 10 << (QList<ListChange>() + << ListChange::setCurrent(5) + << ListChange::move(5, 0, 1) + << ListChange::insert(0) + ) << 11 << 1; + + QTest::newRow("move multiple, then remove them") << 10 << (QList<ListChange>() + << ListChange::setCurrent(1) + << ListChange::move(5, 1, 3) + << ListChange::remove(1, 3) + ) << 7 << 1; + + QTest::newRow("move multiple, then insert before them") << 10 << (QList<ListChange>() + << ListChange::setCurrent(5) + << ListChange::move(5, 1, 3) + << ListChange::insert(1, 5) + ) << 15 << 6; + + QTest::newRow("move multiple, then insert after them") << 10 << (QList<ListChange>() + << ListChange::setCurrent(3) + << ListChange::move(0, 1, 2) + << ListChange::insert(3, 5) + ) << 15 << 8; + + + QTest::newRow("clear current") << 0 << (QList<ListChange>() + << ListChange::insert(0, 5) + << ListChange::setCurrent(-1) + << ListChange::remove(0, 5) + << ListChange::insert(0, 5) + ) << 5 << -1; } void tst_QSGListView::swapWithFirstItem() @@ -1164,6 +1439,7 @@ void tst_QSGListView::enforceRange_withoutHighlight() void tst_QSGListView::spacing() { QSGView *canvas = createView(); + canvas->show(); TestModel model; for (int i = 0; i < 30; i++) @@ -1223,6 +1499,7 @@ void tst_QSGListView::spacing() void tst_QSGListView::sections() { QSGView *canvas = createView(); + canvas->show(); TestModel model; for (int i = 0; i < 30; i++) @@ -1254,6 +1531,7 @@ void tst_QSGListView::sections() // Remove section boundary model.removeItem(5); + QTRY_COMPARE(listview->count(), model.count()); // New section header created QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", 5); @@ -1261,6 +1539,7 @@ void tst_QSGListView::sections() QTRY_COMPARE(item->height(), 40.0); model.insertItem(3, "New Item", "0"); + QTRY_COMPARE(listview->count(), model.count()); // Section header moved item = findItem<QSGItem>(contentItem, "wrapper", 5); @@ -1273,6 +1552,7 @@ void tst_QSGListView::sections() // insert item which will become a section header model.insertItem(6, "Replace header", "1"); + QTRY_COMPARE(listview->count(), model.count()); item = findItem<QSGItem>(contentItem, "wrapper", 6); QTRY_VERIFY(item); @@ -1301,6 +1581,7 @@ void tst_QSGListView::sections() // check that headers change when item changes listview->setContentY(0); model.modifyItem(0, "changed", "2"); + QTest::qWait(300); item = findItem<QSGItem>(contentItem, "wrapper", 1); QTRY_VERIFY(item); @@ -1312,6 +1593,7 @@ void tst_QSGListView::sections() void tst_QSGListView::sectionsDelegate() { QSGView *canvas = createView(); + canvas->show(); TestModel model; for (int i = 0; i < 30; i++) @@ -1350,6 +1632,7 @@ void tst_QSGListView::sectionsDelegate() model.modifyItem(2, "Three", "aaa"); model.modifyItem(3, "Four", "aaa"); model.modifyItem(4, "Five", "aaa"); + QTest::qWait(300); for (int i = 0; i < 3; ++i) { QSGItem *item = findItem<QSGItem>(contentItem, @@ -1360,7 +1643,7 @@ void tst_QSGListView::sectionsDelegate() // remove section boundary model.removeItem(5); - qApp->processEvents(); + QTRY_COMPARE(listview->count(), model.count()); for (int i = 0; i < 3; ++i) { QSGItem *item = findItem<QSGItem>(contentItem, "sect_" + (i == 0 ? QString("aaa") : QString::number(i))); @@ -2175,18 +2458,17 @@ void tst_QSGListView::header() QFETCH(QPointF, initialContentPos); QFETCH(QPointF, changedHeaderPos); QFETCH(QPointF, changedContentPos); - - QSGView *canvas = createView(); + QFETCH(QPointF, resizeContentPos); TestModel model; for (int i = 0; i < 30; i++) model.addItem("Item" + QString::number(i), ""); - QDeclarativeContext *ctxt = canvas->rootContext(); - ctxt->setContextProperty("testModel", &model); - + QSGView *canvas = createView(); + canvas->rootContext()->setContextProperty("testModel", &model); + canvas->rootContext()->setContextProperty("initialViewWidth", 240); + canvas->rootContext()->setContextProperty("initialViewHeight", 320); canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/header.qml")); - qApp->processEvents(); QSGListView *listview = findItem<QSGListView>(canvas->rootObject(), "list"); QTRY_VERIFY(listview != 0); @@ -2235,6 +2517,31 @@ void tst_QSGListView::header() QCOMPARE(item->pos(), firstDelegatePos); delete canvas; + + + // QTBUG-21207 header should become visible if view resizes from initial empty size + + canvas = createView(); + canvas->rootContext()->setContextProperty("testModel", &model); + canvas->rootContext()->setContextProperty("initialViewWidth", 0.0); + canvas->rootContext()->setContextProperty("initialViewHeight", 0.0); + canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/header.qml")); + + listview = findItem<QSGListView>(canvas->rootObject(), "list"); + QTRY_VERIFY(listview != 0); + listview->setOrientation(orientation); + listview->setLayoutDirection(layoutDirection); + + listview->setWidth(240); + listview->setHeight(320); + QTRY_COMPARE(listview->headerItem()->pos(), initialHeaderPos); + QCOMPARE(QPointF(listview->contentX(), listview->contentY()), initialContentPos); + + header->setHeight(10); + header->setWidth(40); + QTRY_COMPARE(QPointF(listview->contentX(), listview->contentY()), resizeContentPos); + + delete canvas; } void tst_QSGListView::header_data() @@ -2246,6 +2553,7 @@ void tst_QSGListView::header_data() QTest::addColumn<QPointF>("initialContentPos"); QTest::addColumn<QPointF>("changedContentPos"); QTest::addColumn<QPointF>("firstDelegatePos"); + QTest::addColumn<QPointF>("resizeContentPos"); // header1 = 100 x 30 // header2 = 50 x 20 @@ -2258,7 +2566,8 @@ void tst_QSGListView::header_data() << QPointF(0, -20) << QPointF(0, -30) << QPointF(0, -20) - << QPointF(0, 0); + << QPointF(0, 0) + << QPointF(0, -10); // header above items, top right QTest::newRow("vertical, layout right to left") << QSGListView::Vertical << Qt::RightToLeft @@ -2266,7 +2575,8 @@ void tst_QSGListView::header_data() << QPointF(0, -20) << QPointF(0, -30) << QPointF(0, -20) - << QPointF(0, 0); + << QPointF(0, 0) + << QPointF(0, -10); // header to left of items QTest::newRow("horizontal, layout left to right") << QSGListView::Horizontal << Qt::LeftToRight @@ -2274,7 +2584,8 @@ void tst_QSGListView::header_data() << QPointF(-50, 0) << QPointF(-100, 0) << QPointF(-50, 0) - << QPointF(0, 0); + << QPointF(0, 0) + << QPointF(-40, 0); // header to right of items QTest::newRow("horizontal, layout right to left") << QSGListView::Horizontal << Qt::RightToLeft @@ -2282,7 +2593,8 @@ void tst_QSGListView::header_data() << QPointF(0, 0) << QPointF(-240 + 100, 0) << QPointF(-240 + 50, 0) - << QPointF(-240, 0); + << QPointF(-240, 0) + << QPointF(-240 + 40, 0); } void tst_QSGListView::header_delayItemCreation() @@ -2321,6 +2633,7 @@ void tst_QSGListView::footer() QFETCH(QPointF, initialContentPos); QFETCH(QPointF, changedFooterPos); QFETCH(QPointF, changedContentPos); + QFETCH(QPointF, resizeContentPos); QSGView *canvas = createView(); @@ -2405,6 +2718,11 @@ void tst_QSGListView::footer() QVERIFY(item); QCOMPARE(item->pos(), firstDelegatePos); + listview->positionViewAtEnd(); + footer->setHeight(10); + footer->setWidth(40); + QTRY_COMPARE(QPointF(listview->contentX(), listview->contentY()), resizeContentPos); + delete canvas; } @@ -2417,11 +2735,13 @@ void tst_QSGListView::footer_data() QTest::addColumn<QPointF>("initialContentPos"); QTest::addColumn<QPointF>("changedContentPos"); QTest::addColumn<QPointF>("firstDelegatePos"); + QTest::addColumn<QPointF>("resizeContentPos"); // footer1 = 100 x 30 - // footer2 = 100 x 20 + // footer2 = 50 x 20 // delegates = 40 x 20 // view width = 240 + // view height = 320 // footer below items, bottom left QTest::newRow("vertical, layout left to right") << QSGListView::Vertical << Qt::LeftToRight @@ -2429,7 +2749,8 @@ void tst_QSGListView::footer_data() << QPointF(0, 30 * 20) // added 30 items << QPointF(0, 0) << QPointF(0, 0) - << QPointF(0, 0); + << QPointF(0, 0) + << QPointF(0, 30 * 20 - 320 + 10); // footer below items, bottom right QTest::newRow("vertical, layout right to left") << QSGListView::Vertical << Qt::RightToLeft @@ -2437,7 +2758,8 @@ void tst_QSGListView::footer_data() << QPointF(0, 30 * 20) << QPointF(0, 0) << QPointF(0, 0) - << QPointF(0, 0); + << QPointF(0, 0) + << QPointF(0, 30 * 20 - 320 + 10); // footer to right of items QTest::newRow("horizontal, layout left to right") << QSGListView::Horizontal << Qt::LeftToRight @@ -2445,7 +2767,8 @@ void tst_QSGListView::footer_data() << QPointF(40 * 30, 0) << QPointF(0, 0) << QPointF(0, 0) - << QPointF(0, 0); + << QPointF(0, 0) + << QPointF(40 * 30 - 240 + 40, 0); // footer to left of items QTest::newRow("horizontal, layout right to left") << QSGListView::Horizontal << Qt::RightToLeft @@ -2453,7 +2776,8 @@ void tst_QSGListView::footer_data() << QPointF(-(40 * 30) - 50, 0) // 50 = new footer width << QPointF(-240, 0) << QPointF(-240, 0) - << QPointF(-40, 0); + << QPointF(-40, 0) + << QPointF(-(40 * 30) - 40, 0); } class LVAccessor : public QSGListView @@ -2492,8 +2816,8 @@ void tst_QSGListView::headerFooter() QVERIFY(footer); QCOMPARE(footer->y(), 0.); - QVERIFY(static_cast<LVAccessor*>(listview)->minY() == 0); - QVERIFY(static_cast<LVAccessor*>(listview)->maxY() == 0); + QCOMPARE(static_cast<LVAccessor*>(listview)->minY(), header->height()); + QCOMPARE(static_cast<LVAccessor*>(listview)->maxY(), header->height()); delete canvas; } @@ -2523,8 +2847,8 @@ void tst_QSGListView::headerFooter() QVERIFY(footer); QCOMPARE(footer->x(), 0.); - QVERIFY(static_cast<LVAccessor*>(listview)->minX() == 0); - QVERIFY(static_cast<LVAccessor*>(listview)->maxX() == 0); + QCOMPARE(static_cast<LVAccessor*>(listview)->minX(), header->width()); + QCOMPARE(static_cast<LVAccessor*>(listview)->maxX(), header->width()); delete canvas; } @@ -2555,8 +2879,8 @@ void tst_QSGListView::headerFooter() QVERIFY(footer); QCOMPARE(footer->x(), -footer->width()); - QCOMPARE(static_cast<LVAccessor*>(listview)->minX(), 240.); - QCOMPARE(static_cast<LVAccessor*>(listview)->maxX(), 240.); + QCOMPARE(static_cast<LVAccessor*>(listview)->minX(), 240. - header->width()); + QCOMPARE(static_cast<LVAccessor*>(listview)->maxX(), 240. - header->width()); delete canvas; } @@ -2691,6 +3015,7 @@ void tst_QSGListView::resizeDelegate() listview->setCurrentIndex(25); listview->setContentY(0); + QTest::qWait(300); for (int i = 0; i < 16; ++i) { QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i); @@ -2902,8 +3227,7 @@ void tst_QSGListView::onAdd() for (int i=0; i<itemsToAdd; i++) items << qMakePair(QString("value %1").arg(i), QString::number(i)); model.addItems(items); - - qApp->processEvents(); + QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count()); QVariantList result = object->property("addedDelegates").toList(); QCOMPARE(result.count(), items.count()); @@ -2949,10 +3273,9 @@ void tst_QSGListView::onRemove() canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/attachedSignals.qml")); QObject *object = canvas->rootObject(); - qApp->processEvents(); - model.removeItems(indexToRemove, removeCount); - qApp->processEvents(); + QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count()); + QCOMPARE(object->property("removedDelegateCount"), QVariant(removeCount)); delete canvas; diff --git a/tests/auto/declarative/qsgtextedit/data/horizontalAlignment_RightToLeft.qml b/tests/auto/declarative/qsgtextedit/data/horizontalAlignment_RightToLeft.qml index 74592fed7f..4cd92367ec 100644 --- a/tests/auto/declarative/qsgtextedit/data/horizontalAlignment_RightToLeft.qml +++ b/tests/auto/declarative/qsgtextedit/data/horizontalAlignment_RightToLeft.qml @@ -18,6 +18,7 @@ Rectangle { objectName: "text" anchors.fill: parent text: top.text + focus: true } } } diff --git a/tests/auto/declarative/qsgtextedit/data/mouseselection_false_words.qml b/tests/auto/declarative/qsgtextedit/data/mouseselection_false_words.qml index ac32f4ced7..86aea46a85 100644 --- a/tests/auto/declarative/qsgtextedit/data/mouseselection_false_words.qml +++ b/tests/auto/declarative/qsgtextedit/data/mouseselection_false_words.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 TextEdit { focus: true - text: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + text: "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" selectByMouse: false + mouseSelectionMode: TextEdit.SelectWords } diff --git a/tests/auto/declarative/qsgtextedit/data/mouseselection_true_words.qml b/tests/auto/declarative/qsgtextedit/data/mouseselection_true_words.qml index 7c7cb0b6fc..c356999220 100644 --- a/tests/auto/declarative/qsgtextedit/data/mouseselection_true_words.qml +++ b/tests/auto/declarative/qsgtextedit/data/mouseselection_true_words.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 TextEdit { focus: true - text: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + text: "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" selectByMouse: true + mouseSelectionMode: TextEdit.SelectWords } diff --git a/tests/auto/declarative/qsgtextedit/tst_qsgtextedit.cpp b/tests/auto/declarative/qsgtextedit/tst_qsgtextedit.cpp index 79ff8ea3ec..7c0f097358 100644 --- a/tests/auto/declarative/qsgtextedit/tst_qsgtextedit.cpp +++ b/tests/auto/declarative/qsgtextedit/tst_qsgtextedit.cpp @@ -489,6 +489,8 @@ void tst_qsgtextedit::hAlign_RightToLeft() QVERIFY(textEdit != 0); canvas.show(); + const QString rtlText = textEdit->text(); + // implicit alignment should follow the reading direction of text QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignRight); QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2); @@ -565,6 +567,16 @@ void tst_qsgtextedit::hAlign_RightToLeft() QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignLeft); QVERIFY(textEdit->positionToRectangle(0).x() < canvas.width()/2); + QApplication::setActiveWindow(&canvas); + QTest::qWaitForWindowShown(&canvas); + QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&canvas)); + + textEdit->setText(QString()); + { QInputMethodEvent ev(rtlText, QList<QInputMethodEvent::Attribute>()); QApplication::sendEvent(&canvas, &ev); } + QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignRight); + { QInputMethodEvent ev("Hello world!", QList<QInputMethodEvent::Attribute>()); QApplication::sendEvent(&canvas, &ev); } + QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignLeft); + #ifndef Q_OS_MAC // QTBUG-18040 // empty text with implicit alignment follows the system locale-based // keyboard input direction from QApplication::keyboardInputDirection @@ -1336,20 +1348,32 @@ void tst_qsgtextedit::moveCursorSelectionSequence() void tst_qsgtextedit::mouseSelection_data() { QTest::addColumn<QString>("qmlfile"); - QTest::addColumn<bool>("expectSelection"); + QTest::addColumn<int>("from"); + QTest::addColumn<int>("to"); + QTest::addColumn<QString>("selectedText"); // import installed - QTest::newRow("on") << SRCDIR "/data/mouseselection_true.qml" << true; - QTest::newRow("off") << SRCDIR "/data/mouseselection_false.qml" << false; - QTest::newRow("default") << SRCDIR "/data/mouseselection_default.qml" << false; - QTest::newRow("on word selection") << SRCDIR "/data/mouseselection_true_words.qml" << true; - QTest::newRow("off word selection") << SRCDIR "/data/mouseselection_false_words.qml" << false; + QTest::newRow("on") << SRCDIR "/data/mouseselection_true.qml" << 4 << 9 << "45678"; + QTest::newRow("off") << SRCDIR "/data/mouseselection_false.qml" << 4 << 9 << QString(); + QTest::newRow("default") << SRCDIR "/data/mouseselection_default.qml" << 4 << 9 << QString(); + QTest::newRow("off word selection") << SRCDIR "/data/mouseselection_false_words.qml" << 4 << 9 << QString(); + QTest::newRow("on word selection (4,9)") << SRCDIR "/data/mouseselection_true_words.qml" << 4 << 9 << "0123456789"; + QTest::newRow("on word selection (2,13)") << SRCDIR "/data/mouseselection_true_words.qml" << 2 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + QTest::newRow("on word selection (2,30)") << SRCDIR "/data/mouseselection_true_words.qml" << 2 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + QTest::newRow("on word selection (9,13)") << SRCDIR "/data/mouseselection_true_words.qml" << 9 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + QTest::newRow("on word selection (9,30)") << SRCDIR "/data/mouseselection_true_words.qml" << 9 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + QTest::newRow("on word selection (13,2)") << SRCDIR "/data/mouseselection_true_words.qml" << 13 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + QTest::newRow("on word selection (20,2)") << SRCDIR "/data/mouseselection_true_words.qml" << 20 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + QTest::newRow("on word selection (12,9)") << SRCDIR "/data/mouseselection_true_words.qml" << 12 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + QTest::newRow("on word selection (30,9)") << SRCDIR "/data/mouseselection_true_words.qml" << 30 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ"; } void tst_qsgtextedit::mouseSelection() { QFETCH(QString, qmlfile); - QFETCH(bool, expectSelection); + QFETCH(int, from); + QFETCH(int, to); + QFETCH(QString, selectedText); QSGView canvas(QUrl::fromLocalFile(qmlfile)); @@ -1363,19 +1387,20 @@ void tst_qsgtextedit::mouseSelection() QVERIFY(textEditObject != 0); // press-and-drag-and-release from x1 to x2 - int x1 = 10; - int x2 = 70; - int y = textEditObject->height()/2; - QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y)); - //QTest::mouseMove(canvas, QPoint(x2,y)); // doesn't work - QMouseEvent mv(QEvent::MouseMove, QPoint(x2,y), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier); + QPoint p1 = textEditObject->positionToRectangle(from).center().toPoint(); + QPoint p2 = textEditObject->positionToRectangle(to).center().toPoint(); + QTest::mousePress(&canvas, Qt::LeftButton, 0, p1); + //QTest::mouseMove(canvas->viewport(), canvas->mapFromScene(QPoint(x2,y))); // doesn't work + QMouseEvent mv(QEvent::MouseMove, p2, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier); QApplication::sendEvent(&canvas, &mv); - QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y)); - QString str = textEditObject->selectedText(); - if (expectSelection) - QVERIFY(str.length() > 3); // don't reallly care *what* was selected (and it's too sensitive to platform) - else - QVERIFY(str.isEmpty()); + QTest::mouseRelease(&canvas, Qt::LeftButton, 0, p2); + QCOMPARE(textEditObject->selectedText(), selectedText); + + // Clicking and shift to clicking between the same points should select the same text. + textEditObject->setCursorPosition(0); + QTest::mouseClick(&canvas, Qt::LeftButton, Qt::NoModifier, p1); + QTest::mouseClick(&canvas, Qt::LeftButton, Qt::ShiftModifier, p2); + QCOMPARE(textEditObject->selectedText(), selectedText); } void tst_qsgtextedit::dragMouseSelection() @@ -1569,6 +1594,43 @@ void tst_qsgtextedit::cursorDelegate() QCOMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x())); QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y())); } + // Clear preedit text; + QInputMethodEvent event; + QApplication::sendEvent(&view, &event); + + + // Test delegate gets moved on mouse press. + textEditObject->setSelectByMouse(true); + textEditObject->setCursorPosition(0); + const QPoint point1 = textEditObject->positionToRectangle(5).center().toPoint(); + QTest::mouseClick(&view, Qt::LeftButton, 0, point1); + QVERIFY(textEditObject->cursorPosition() != 0); + QCOMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x())); + QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y())); + + // Test delegate gets moved on mouse drag + textEditObject->setCursorPosition(0); + const QPoint point2 = textEditObject->positionToRectangle(10).center().toPoint(); + QTest::mousePress(&view, Qt::LeftButton, 0, point1); + QMouseEvent mv(QEvent::MouseMove, point2, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier); + QApplication::sendEvent(&view, &mv); + QTest::mouseRelease(&view, Qt::LeftButton, 0, point2); + QCOMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x())); + QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y())); + + textEditObject->setReadOnly(true); + textEditObject->setCursorPosition(0); + QTest::mouseClick(&view, Qt::LeftButton, 0, textEditObject->positionToRectangle(5).center().toPoint()); + QVERIFY(textEditObject->cursorPosition() != 0); + QCOMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x())); + QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y())); + + textEditObject->setCursorPosition(0); + QTest::mouseClick(&view, Qt::LeftButton, 0, textEditObject->positionToRectangle(5).center().toPoint()); + QVERIFY(textEditObject->cursorPosition() != 0); + QCOMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x())); + QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y())); + textEditObject->setCursorPosition(0); QCOMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x())); QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y())); diff --git a/tests/auto/declarative/qsgtextinput/data/horizontalAlignment_RightToLeft.qml b/tests/auto/declarative/qsgtextinput/data/horizontalAlignment_RightToLeft.qml index 15fbabe28c..5f88025536 100644 --- a/tests/auto/declarative/qsgtextinput/data/horizontalAlignment_RightToLeft.qml +++ b/tests/auto/declarative/qsgtextinput/data/horizontalAlignment_RightToLeft.qml @@ -18,6 +18,7 @@ Rectangle { objectName: "text" anchors.fill: parent text: top.text + focus: true } } } diff --git a/tests/auto/declarative/qsgtextinput/tst_qsgtextinput.cpp b/tests/auto/declarative/qsgtextinput/tst_qsgtextinput.cpp index 7da6ee23e8..483fa74c06 100644 --- a/tests/auto/declarative/qsgtextinput/tst_qsgtextinput.cpp +++ b/tests/auto/declarative/qsgtextinput/tst_qsgtextinput.cpp @@ -55,6 +55,8 @@ #include <QtOpenGL/QGLShaderProgram> #include <math.h> +#include "qplatformdefs.h" + #ifdef Q_OS_SYMBIAN // In Symbian OS test data is located in applications private dir #define SRCDIR "." @@ -132,6 +134,9 @@ private slots: void focusOutClearSelection(); void echoMode(); +#ifdef QT_GUI_PASSWORD_ECHO_DELAY + void passwordEchoDelay(); +#endif void geometrySignals(); void testQtQuick11Attributes(); void testQtQuick11Attributes_data(); @@ -1081,6 +1086,8 @@ void tst_qsgtextinput::horizontalAlignment_RightToLeft() QVERIFY(textInput != 0); canvas.show(); + const QString rtlText = textInput->text(); + QSGTextInputPrivate *textInputPrivate = QSGTextInputPrivate::get(textInput); QVERIFY(textInputPrivate != 0); QVERIFY(-textInputPrivate->hscroll > canvas.width()/2); @@ -1145,6 +1152,17 @@ void tst_qsgtextinput::horizontalAlignment_RightToLeft() QCOMPARE(textInput->hAlign(), QSGTextInput::AlignLeft); QVERIFY(-textInputPrivate->hscroll < canvas.width()/2); + QApplication::setActiveWindow(&canvas); + QTest::qWaitForWindowShown(&canvas); + QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&canvas)); + + // If there is no commited text, the preedit text should determine the alignment. + textInput->setText(QString()); + { QInputMethodEvent ev(rtlText, QList<QInputMethodEvent::Attribute>()); QApplication::sendEvent(&canvas, &ev); } + QCOMPARE(textInput->hAlign(), QSGTextInput::AlignRight); + { QInputMethodEvent ev("Hello world!", QList<QInputMethodEvent::Attribute>()); QApplication::sendEvent(&canvas, &ev); } + QCOMPARE(textInput->hAlign(), QSGTextInput::AlignLeft); + #ifndef Q_OS_MAC // QTBUG-18040 // empty text with implicit alignment follows the system locale-based // keyboard input direction from QApplication::keyboardInputDirection @@ -1937,6 +1955,59 @@ void tst_qsgtextinput::echoMode() QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), initial); } +#ifdef QT_GUI_PASSWORD_ECHO_DELAY +void tst_qdeclarativetextinput::passwordEchoDelay() +{ + QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/echoMode.qml")); + canvas.show(); + canvas.setFocus(); + QApplication::setActiveWindow(&canvas); + QTest::qWaitForWindowShown(&canvas); + QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&canvas)); + + QVERIFY(canvas.rootObject() != 0); + + QSGTextInput *input = qobject_cast<QSGTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput"))); + + QChar fillChar = QLatin1Char('*'); + + input->setEchoMode(QDeclarativeTextInput::Password); + QCOMPARE(input->displayText(), QString(8, fillChar)); + input->setText(QString()); + QCOMPARE(input->displayText(), QString()); + + QTest::keyPress(&canvas, '0'); + QTest::keyPress(&canvas, '1'); + QTest::keyPress(&canvas, '2'); + QCOMPARE(input->displayText(), QString(2, fillChar) + QLatin1Char('2')); + QTest::keyPress(&canvas, '3'); + QTest::keyPress(&canvas, '4'); + QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4')); + QTest::keyPress(&canvas, Qt::Key_Backspace); + QCOMPARE(input->displayText(), QString(4, fillChar)); + QTest::keyPress(&canvas, '4'); + QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4')); + QTest::qWait(QT_GUI_PASSWORD_ECHO_DELAY); + QTRY_COMPARE(input->displayText(), QString(5, fillChar)); + QTest::keyPress(&canvas, '5'); + QCOMPARE(input->displayText(), QString(5, fillChar) + QLatin1Char('5')); + input->setFocus(false); + QVERIFY(!input->hasFocus()); + QCOMPARE(input->displayText(), QString(6, fillChar)); + input->setFocus(true); + QTRY_VERIFY(input->hasFocus()); + QCOMPARE(input->displayText(), QString(6, fillChar)); + QTest::keyPress(&canvas, '6'); + QCOMPARE(input->displayText(), QString(6, fillChar) + QLatin1Char('6')); + + QInputMethodEvent ev; + ev.setCommitString(QLatin1String("7")); + QApplication::sendEvent(&canvas, &ev); + QCOMPARE(input->displayText(), QString(7, fillChar) + QLatin1Char('7')); +} +#endif + + void tst_qsgtextinput::simulateKey(QSGView *view, int key) { QKeyEvent press(QKeyEvent::KeyPress, key, 0); diff --git a/tests/auto/qmltest/tst_qmltest.cpp b/tests/auto/qmltest/tst_qmltest.cpp index e1bad75b82..a1e8c039e4 100644 --- a/tests/auto/qmltest/tst_qmltest.cpp +++ b/tests/auto/qmltest/tst_qmltest.cpp @@ -40,4 +40,4 @@ ****************************************************************************/ #include <QtQuickTest/quicktest.h> -QUICK_TEST_MAIN(qmlauto) +QUICK_TEST_MAIN(qmltest)
\ No newline at end of file diff --git a/tests/auto/qtquick1/examples/data/dummytest.qml b/tests/auto/qtquick1/examples/data/dummytest.qml new file mode 100644 index 0000000000..b20e907f27 --- /dev/null +++ b/tests/auto/qtquick1/examples/data/dummytest.qml @@ -0,0 +1,6 @@ +import Qt.VisualTest 4.6 + +VisualTest { + Frame { msec: 0 } + Frame { msec: 10 } +} diff --git a/tests/auto/qtquick1/examples/data/webbrowser/webbrowser.qml b/tests/auto/qtquick1/examples/data/webbrowser/webbrowser.qml new file mode 100644 index 0000000000..d31787b939 --- /dev/null +++ b/tests/auto/qtquick1/examples/data/webbrowser/webbrowser.qml @@ -0,0 +1,6 @@ +import Qt.VisualTest 4.6 + +VisualTest { + Frame { msec: 0 } + Frame { msec: 2000 } +} diff --git a/tests/auto/qtquick1/examples/examples.pro b/tests/auto/qtquick1/examples/examples.pro new file mode 100644 index 0000000000..14756b9df2 --- /dev/null +++ b/tests/auto/qtquick1/examples/examples.pro @@ -0,0 +1,14 @@ +load(qttest_p4) +contains(QT_CONFIG,declarative): QT += declarative qtquick1 +macx:CONFIG -= app_bundle + +include(../../../../tools/qmlviewer/qml.pri) + +SOURCES += tst_examples.cpp +DEFINES += SRCDIR=\\\"$$PWD\\\" + +CONFIG += parallel_test + +QT += core-private gui-private declarative-private qtquick1-private + +qpa:CONFIG+=insignificant_test # QTBUG-20990, aborts diff --git a/tests/auto/qtquick1/examples/tst_examples.cpp b/tests/auto/qtquick1/examples/tst_examples.cpp new file mode 100644 index 0000000000..c2859dc5ef --- /dev/null +++ b/tests/auto/qtquick1/examples/tst_examples.cpp @@ -0,0 +1,212 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qtest.h> +#include <QLibraryInfo> +#include <QDir> +#include <QProcess> +#include <QDebug> +#include "qmlruntime.h" +#include <QDeclarativeView> +#include <QDeclarativeError> + +class tst_examples : public QObject +{ + Q_OBJECT +public: + tst_examples(); + +private slots: + void examples_data(); + void examples(); + + void namingConvention(); +private: + QStringList excludedDirs; + + void namingConvention(const QDir &); + QStringList findQmlFiles(const QDir &); +}; + +tst_examples::tst_examples() +{ + // Add directories you want excluded here + +#ifdef QT_NO_WEBKIT + excludedDirs << "examples/declarative/qtquick1/modelviews/webview"; + excludedDirs << "examples/declarative/qtquick1/webbrowser"; + excludedDirs << "doc/src/snippets/declarative/qtquick1/webview"; + excludedDirs << "doc/src/snippets/qtquick1/qtquick1/webview"; +#endif + +#ifdef QT_NO_XMLPATTERNS + excludedDirs << "examples/declarative/qtquick1/xml/xmldata"; + excludedDirs << "examples/declarative/qtquick1/twitter"; + excludedDirs << "examples/declarative/qtquick1/flickr"; + excludedDirs << "examples/declarative/qtquick1/photoviewer"; +#endif +} + +/* +This tests that the examples follow the naming convention required +to have them tested by the examples() test. +*/ +void tst_examples::namingConvention(const QDir &d) +{ + for (int ii = 0; ii < excludedDirs.count(); ++ii) { + QString s = excludedDirs.at(ii); + if (d.absolutePath().endsWith(s)) + return; + } + + QStringList files = d.entryList(QStringList() << QLatin1String("*.qml"), + QDir::Files); + + bool seenQml = !files.isEmpty(); + bool seenLowercase = false; + + foreach (const QString &file, files) { + if (file.at(0).isLower()) + seenLowercase = true; + } + + if (!seenQml) { + QStringList dirs = d.entryList(QDir::Dirs | QDir::NoDotAndDotDot | + QDir::NoSymLinks); + foreach (const QString &dir, dirs) { + QDir sub = d; + sub.cd(dir); + namingConvention(sub); + } + } else if (!seenLowercase) { + QFAIL(qPrintable(QString( + "Directory %1 violates naming convention; expected at least one qml file " + "starting with lower case, got: %2" + ).arg(d.absolutePath()).arg(files.join(",")))); + } +} + +void tst_examples::namingConvention() +{ + QString examples = QLibraryInfo::location(QLibraryInfo::ExamplesPath); + + namingConvention(QDir(examples)); +} + +QStringList tst_examples::findQmlFiles(const QDir &d) +{ + for (int ii = 0; ii < excludedDirs.count(); ++ii) { + QString s = excludedDirs.at(ii); + if (d.absolutePath().endsWith(s)) + return QStringList(); + } + + QStringList rv; + + QStringList cppfiles = d.entryList(QStringList() << QLatin1String("*.cpp"), QDir::Files); + if (cppfiles.isEmpty()) { + QStringList files = d.entryList(QStringList() << QLatin1String("*.qml"), + QDir::Files); + foreach (const QString &file, files) { + if (file.at(0).isLower()) { + rv << d.absoluteFilePath(file); + } + } + } + + QStringList dirs = d.entryList(QDir::Dirs | QDir::NoDotAndDotDot | + QDir::NoSymLinks); + foreach (const QString &dir, dirs) { + QDir sub = d; + sub.cd(dir); + rv << findQmlFiles(sub); + } + + return rv; +} + +/* +This test runs all the examples in the declarative UI source tree and ensures +that they start and exit cleanly. + +Examples are any .qml files under the examples/ directory that start +with a lower case letter. +*/ +static void silentErrorsMsgHandler(QtMsgType, const char *) +{ +} + + +void tst_examples::examples_data() +{ + QTest::addColumn<QString>("file"); + + QString examples = QLatin1String(SRCDIR) + "/../../../../examples/declarative/qtquick1"; + + QStringList files; + files << findQmlFiles(QDir(examples)); + + foreach (const QString &file, files) + QTest::newRow(qPrintable(file)) << file; +} + +void tst_examples::examples() +{ + QFETCH(QString, file); + + QDeclarativeViewer viewer; + + QtMsgHandler old = qInstallMsgHandler(silentErrorsMsgHandler); + QVERIFY(viewer.open(file)); + qInstallMsgHandler(old); + + if (viewer.view()->status() == QDeclarativeView::Error) + qWarning() << viewer.view()->errors(); + + QCOMPARE(viewer.view()->status(), QDeclarativeView::Ready); + viewer.show(); + + QTest::qWaitForWindowShown(&viewer); +} + +QTEST_MAIN(tst_examples) + +#include "tst_examples.moc" diff --git a/tests/auto/qtquick1/qdeclarativetextedit/data/horizontalAlignment_RightToLeft.qml b/tests/auto/qtquick1/qdeclarativetextedit/data/horizontalAlignment_RightToLeft.qml index 43ea8d8a12..6e739bf2bb 100644 --- a/tests/auto/qtquick1/qdeclarativetextedit/data/horizontalAlignment_RightToLeft.qml +++ b/tests/auto/qtquick1/qdeclarativetextedit/data/horizontalAlignment_RightToLeft.qml @@ -18,6 +18,7 @@ Rectangle { objectName: "text" anchors.fill: parent text: top.text + focus: true } } } diff --git a/tests/auto/qtquick1/qdeclarativetextedit/data/mouseselection_false_words.qml b/tests/auto/qtquick1/qdeclarativetextedit/data/mouseselection_false_words.qml index 22a9871306..f8d2e4e2f2 100644 --- a/tests/auto/qtquick1/qdeclarativetextedit/data/mouseselection_false_words.qml +++ b/tests/auto/qtquick1/qdeclarativetextedit/data/mouseselection_false_words.qml @@ -1,7 +1,8 @@ -import QtQuick 1.0 +import QtQuick 1.1 TextEdit { focus: true - text: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + text: "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" selectByMouse: false + mouseSelectionMode: TextEdit.SelectWords } diff --git a/tests/auto/qtquick1/qdeclarativetextedit/data/mouseselection_true_words.qml b/tests/auto/qtquick1/qdeclarativetextedit/data/mouseselection_true_words.qml index d61da46f48..f58fd45837 100644 --- a/tests/auto/qtquick1/qdeclarativetextedit/data/mouseselection_true_words.qml +++ b/tests/auto/qtquick1/qdeclarativetextedit/data/mouseselection_true_words.qml @@ -1,7 +1,8 @@ -import QtQuick 1.0 +import QtQuick 1.1 TextEdit { focus: true - text: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + text: "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" selectByMouse: true + mouseSelectionMode: TextEdit.SelectWords } diff --git a/tests/auto/qtquick1/qdeclarativetextedit/tst_qdeclarativetextedit.cpp b/tests/auto/qtquick1/qdeclarativetextedit/tst_qdeclarativetextedit.cpp index 3af539f5eb..bcad6377f3 100644 --- a/tests/auto/qtquick1/qdeclarativetextedit/tst_qdeclarativetextedit.cpp +++ b/tests/auto/qtquick1/qdeclarativetextedit/tst_qdeclarativetextedit.cpp @@ -454,6 +454,8 @@ void tst_qdeclarativetextedit::hAlign_RightToLeft() QVERIFY(textEdit != 0); canvas->show(); + const QString rtlText = textEdit->text(); + // implicit alignment should follow the reading direction of text QCOMPARE(textEdit->hAlign(), QDeclarative1TextEdit::AlignRight); QVERIFY(textEdit->positionToRectangle(0).x() > canvas->width()/2); @@ -530,6 +532,16 @@ void tst_qdeclarativetextedit::hAlign_RightToLeft() QCOMPARE(textEdit->hAlign(), QDeclarative1TextEdit::AlignLeft); QVERIFY(textEdit->positionToRectangle(0).x() < canvas->width()/2); + QApplication::setActiveWindow(canvas); + QTest::qWaitForWindowShown(canvas); + QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(canvas)); + + textEdit->setText(QString()); + { QInputMethodEvent ev(rtlText, QList<QInputMethodEvent::Attribute>()); QApplication::sendEvent(canvas, &ev); } + QCOMPARE(textEdit->hAlign(), QDeclarative1TextEdit::AlignRight); + { QInputMethodEvent ev("Hello world!", QList<QInputMethodEvent::Attribute>()); QApplication::sendEvent(canvas, &ev); } + QCOMPARE(textEdit->hAlign(), QDeclarative1TextEdit::AlignLeft); + #ifndef Q_OS_MAC // QTBUG-18040 // empty text with implicit alignment follows the system locale-based // keyboard input direction from QApplication::keyboardInputDirection @@ -1315,20 +1327,32 @@ void tst_qdeclarativetextedit::moveCursorSelectionSequence() void tst_qdeclarativetextedit::mouseSelection_data() { QTest::addColumn<QString>("qmlfile"); - QTest::addColumn<bool>("expectSelection"); + QTest::addColumn<int>("from"); + QTest::addColumn<int>("to"); + QTest::addColumn<QString>("selectedText"); // import installed - QTest::newRow("on") << SRCDIR "/data/mouseselection_true.qml" << true; - QTest::newRow("off") << SRCDIR "/data/mouseselection_false.qml" << false; - QTest::newRow("default") << SRCDIR "/data/mouseselection_default.qml" << false; - QTest::newRow("on word selection") << SRCDIR "/data/mouseselection_true_words.qml" << true; - QTest::newRow("off word selection") << SRCDIR "/data/mouseselection_false_words.qml" << false; + QTest::newRow("on") << SRCDIR "/data/mouseselection_true.qml" << 4 << 9 << "45678"; + QTest::newRow("off") << SRCDIR "/data/mouseselection_false.qml" << 4 << 9 << QString(); + QTest::newRow("default") << SRCDIR "/data/mouseselection_default.qml" << 4 << 9 << QString(); + QTest::newRow("off word selection") << SRCDIR "/data/mouseselection_false_words.qml" << 4 << 9 << QString(); + QTest::newRow("on word selection (4,9)") << SRCDIR "/data/mouseselection_true_words.qml" << 4 << 9 << "0123456789"; + QTest::newRow("on word selection (2,13)") << SRCDIR "/data/mouseselection_true_words.qml" << 2 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + QTest::newRow("on word selection (2,30)") << SRCDIR "/data/mouseselection_true_words.qml" << 2 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + QTest::newRow("on word selection (9,13)") << SRCDIR "/data/mouseselection_true_words.qml" << 9 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + QTest::newRow("on word selection (9,30)") << SRCDIR "/data/mouseselection_true_words.qml" << 9 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + QTest::newRow("on word selection (13,2)") << SRCDIR "/data/mouseselection_true_words.qml" << 13 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + QTest::newRow("on word selection (20,2)") << SRCDIR "/data/mouseselection_true_words.qml" << 20 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + QTest::newRow("on word selection (12,9)") << SRCDIR "/data/mouseselection_true_words.qml" << 12 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + QTest::newRow("on word selection (30,9)") << SRCDIR "/data/mouseselection_true_words.qml" << 30 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ"; } void tst_qdeclarativetextedit::mouseSelection() { QFETCH(QString, qmlfile); - QFETCH(bool, expectSelection); + QFETCH(int, from); + QFETCH(int, to); + QFETCH(QString, selectedText); QDeclarativeView *canvas = createView(qmlfile); @@ -1342,25 +1366,20 @@ void tst_qdeclarativetextedit::mouseSelection() QVERIFY(textEditObject != 0); // press-and-drag-and-release from x1 to x2 - int x1 = 10; - int x2 = 70; - int y = textEditObject->height()/2; - QTest::mousePress(canvas->viewport(), Qt::LeftButton, 0, canvas->mapFromScene(QPoint(x1,y))); + QPoint p1 = canvas->mapFromScene(textEditObject->positionToRectangle(from).center()); + QPoint p2 = canvas->mapFromScene(textEditObject->positionToRectangle(to).center()); + QTest::mousePress(canvas->viewport(), Qt::LeftButton, 0, p1); //QTest::mouseMove(canvas->viewport(), canvas->mapFromScene(QPoint(x2,y))); // doesn't work - QMouseEvent mv(QEvent::MouseMove, canvas->mapFromScene(QPoint(x2,y)), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier); + QMouseEvent mv(QEvent::MouseMove, canvas->mapFromScene(p2), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier); QApplication::sendEvent(canvas->viewport(), &mv); - QTest::mouseRelease(canvas->viewport(), Qt::LeftButton, 0, canvas->mapFromScene(QPoint(x2,y))); - QString str = textEditObject->selectedText(); - if (expectSelection) - QVERIFY(str.length() > 3); // don't reallly care *what* was selected (and it's too sensitive to platform) - else - QVERIFY(str.isEmpty()); + QTest::mouseRelease(canvas->viewport(), Qt::LeftButton, 0, p2); + QCOMPARE(textEditObject->selectedText(), selectedText); // Clicking and shift to clicking between the same points should select the same text. textEditObject->setCursorPosition(0); - QTest::mouseClick(canvas->viewport(), Qt::LeftButton, Qt::NoModifier, canvas->mapFromScene(QPoint(x1,y))); - QTest::mouseClick(canvas->viewport(), Qt::LeftButton, Qt::ShiftModifier, canvas->mapFromScene(QPoint(x2,y))); - QCOMPARE(textEditObject->selectedText(), str); + QTest::mouseClick(canvas->viewport(), Qt::LeftButton, Qt::NoModifier, p1); + QTest::mouseClick(canvas->viewport(), Qt::LeftButton, Qt::ShiftModifier, p2); + QCOMPARE(textEditObject->selectedText(), selectedText); delete canvas; } @@ -1682,15 +1701,26 @@ void tst_qdeclarativetextedit::cursorDelegate() QInputMethodEvent event; QApplication::sendEvent(view, &event); + // Test delegate gets moved on mouse press. textEditObject->setSelectByMouse(true); textEditObject->setCursorPosition(0); - qDebug() << textEditObject->boundingRect() << textEditObject->positionToRectangle(5).center() << view->mapFromScene(textEditObject->positionToRectangle(5).center()); - QTest::mouseClick(view->viewport(), Qt::LeftButton, 0, view->mapFromScene(textEditObject->positionToRectangle(5).center())); + const QPoint point1 = view->mapFromScene(textEditObject->positionToRectangle(5).center()); + QTest::mouseClick(view->viewport(), Qt::LeftButton, 0, point1); QVERIFY(textEditObject->cursorPosition() != 0); QCOMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x())); QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y())); + // Test delegate gets moved on mouse drag + textEditObject->setCursorPosition(0); + const QPoint point2 = view->mapFromScene(textEditObject->positionToRectangle(10).center()); + QTest::mousePress(view->viewport(), Qt::LeftButton, 0, point1); + QMouseEvent mv(QEvent::MouseMove, point2, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier); + QApplication::sendEvent(view->viewport(), &mv); + QTest::mouseRelease(view->viewport(), Qt::LeftButton, 0, point2); + QCOMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x())); + QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y())); + textEditObject->setReadOnly(true); textEditObject->setCursorPosition(0); QTest::mouseClick(view->viewport(), Qt::LeftButton, 0, view->mapFromScene(textEditObject->positionToRectangle(5).center())); @@ -1699,6 +1729,12 @@ void tst_qdeclarativetextedit::cursorDelegate() QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y())); textEditObject->setCursorPosition(0); + QTest::mouseClick(view->viewport(), Qt::LeftButton, 0, view->mapFromScene(textEditObject->positionToRectangle(5).center())); + QVERIFY(textEditObject->cursorPosition() != 0); + QCOMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x())); + QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y())); + + textEditObject->setCursorPosition(0); QCOMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x())); QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y())); QVERIFY(textEditObject->cursorRectangle().y() >= 0); diff --git a/tests/auto/qtquick1/qdeclarativetextinput/data/horizontalAlignment_RightToLeft.qml b/tests/auto/qtquick1/qdeclarativetextinput/data/horizontalAlignment_RightToLeft.qml index b11535e50b..7f27bbe5cb 100644 --- a/tests/auto/qtquick1/qdeclarativetextinput/data/horizontalAlignment_RightToLeft.qml +++ b/tests/auto/qtquick1/qdeclarativetextinput/data/horizontalAlignment_RightToLeft.qml @@ -18,6 +18,7 @@ Rectangle { objectName: "text" anchors.fill: parent text: top.text + focus: true } } } diff --git a/tests/auto/qtquick1/qdeclarativetextinput/tst_qdeclarativetextinput.cpp b/tests/auto/qtquick1/qdeclarativetextinput/tst_qdeclarativetextinput.cpp index 413f04d496..633ab17a27 100644 --- a/tests/auto/qtquick1/qdeclarativetextinput/tst_qdeclarativetextinput.cpp +++ b/tests/auto/qtquick1/qdeclarativetextinput/tst_qdeclarativetextinput.cpp @@ -52,6 +52,8 @@ #include <QInputContext> #include <QtWidgets/5.0.0/QtWidgets/private/qapplication_p.h> +#include "qplatformdefs.h" + #ifdef Q_OS_SYMBIAN // In Symbian OS test data is located in applications private dir #define SRCDIR "." @@ -133,6 +135,9 @@ private slots: void focusOutClearSelection(); void echoMode(); +#ifdef QT_GUI_PASSWORD_ECHO_DELAY + void passwordEchoDelay(); +#endif void geometrySignals(); void testQtQuick11Attributes(); void testQtQuick11Attributes_data(); @@ -1197,6 +1202,8 @@ void tst_qdeclarativetextinput::horizontalAlignment_RightToLeft() QVERIFY(textInput != 0); canvas->show(); + const QString rtlText = textInput->text(); + QDeclarative1TextInputPrivate *textInputPrivate = QDeclarative1TextInputPrivate::get(textInput); QVERIFY(textInputPrivate != 0); QVERIFY(-textInputPrivate->hscroll > canvas->width()/2); @@ -1261,6 +1268,17 @@ void tst_qdeclarativetextinput::horizontalAlignment_RightToLeft() QCOMPARE(textInput->hAlign(), QDeclarative1TextInput::AlignLeft); QVERIFY(-textInputPrivate->hscroll < canvas->width()/2); + QApplication::setActiveWindow(canvas); + QTest::qWaitForWindowShown(canvas); + QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(canvas)); + + // If there is no commited text, the preedit text should determine the alignment. + textInput->setText(QString()); + { QInputMethodEvent ev(rtlText, QList<QInputMethodEvent::Attribute>()); QApplication::sendEvent(canvas, &ev); } + QCOMPARE(textInput->hAlign(), QDeclarative1TextInput::AlignRight); + { QInputMethodEvent ev("Hello world!", QList<QInputMethodEvent::Attribute>()); QApplication::sendEvent(canvas, &ev); } + QCOMPARE(textInput->hAlign(), QDeclarative1TextInput::AlignLeft); + #ifndef Q_OS_MAC // QTBUG-18040 // empty text with implicit alignment follows the system locale-based // keyboard input direction from QApplication::keyboardInputDirection @@ -2062,6 +2080,62 @@ void tst_qdeclarativetextinput::echoMode() delete canvas; } + +#ifdef QT_GUI_PASSWORD_ECHO_DELAY +void tst_qdeclarativetextinput::passwordEchoDelay() +{ + QDeclarativeView *canvas = createView(SRCDIR "/data/echoMode.qml"); + canvas->show(); + canvas->setFocus(); + QApplication::setActiveWindow(canvas); + QTest::qWaitForWindowShown(canvas); + QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(canvas)); + + QVERIFY(canvas->rootObject() != 0); + + QDeclarative1TextInput *input = qobject_cast<QDeclarative1TextInput *>(qvariant_cast<QObject *>(canvas->rootObject()->property("myInput"))); + + QChar fillChar = QLatin1Char('*'); + + input->setEchoMode(QDeclarativeTextInput::Password); + QCOMPARE(input->displayText(), QString(8, fillChar)); + input->setText(QString()); + QCOMPARE(input->displayText(), QString()); + + QTest::keyPress(canvas, '0'); + QTest::keyPress(canvas, '1'); + QTest::keyPress(canvas, '2'); + QCOMPARE(input->displayText(), QString(2, fillChar) + QLatin1Char('2')); + QTest::keyPress(canvas, '3'); + QTest::keyPress(canvas, '4'); + QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4')); + QTest::keyPress(canvas, Qt::Key_Backspace); + QCOMPARE(input->displayText(), QString(4, fillChar)); + QTest::keyPress(canvas, '4'); + QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4')); + QTest::qWait(QT_GUI_PASSWORD_ECHO_DELAY); + QTRY_COMPARE(input->displayText(), QString(5, fillChar)); + QTest::keyPress(canvas, '5'); + QCOMPARE(input->displayText(), QString(5, fillChar) + QLatin1Char('5')); + input->setFocus(false); + QVERIFY(!input->hasFocus()); + QCOMPARE(input->displayText(), QString(6, fillChar)); + input->setFocus(true); + QTRY_VERIFY(input->hasFocus()); + QCOMPARE(input->displayText(), QString(6, fillChar)); + QTest::keyPress(canvas, '6'); + QCOMPARE(input->displayText(), QString(6, fillChar) + QLatin1Char('6')); + + QInputMethodEvent ev; + ev.setCommitString(QLatin1String("7")); + QApplication::sendEvent(canvas, &ev); + QCOMPARE(input->displayText(), QString(7, fillChar) + QLatin1Char('7')); + + delete canvas; +} +#endif + + void tst_qdeclarativetextinput::simulateKey(QDeclarativeView *view, int key) { QKeyEvent press(QKeyEvent::KeyPress, key, 0); diff --git a/tests/auto/qtquick1/qtquick1.pro b/tests/auto/qtquick1/qtquick1.pro index 0c3e2498ff..489accb839 100644 --- a/tests/auto/qtquick1/qtquick1.pro +++ b/tests/auto/qtquick1/qtquick1.pro @@ -43,6 +43,7 @@ contains(QT_CONFIG, private_tests) { qdeclarativetimer \ qdeclarativevisualdatamodel \ qdeclarativexmllistmodel \ + examples } diff --git a/tests/benchmarks/declarative/js/qjsengine/tst_qjsengine.cpp b/tests/benchmarks/declarative/js/qjsengine/tst_qjsengine.cpp index ba486df2b1..f8bade3f9d 100644 --- a/tests/benchmarks/declarative/js/qjsengine/tst_qjsengine.cpp +++ b/tests/benchmarks/declarative/js/qjsengine/tst_qjsengine.cpp @@ -7,29 +7,29 @@ ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** ** 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 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception +** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. ** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. ** ** ** diff --git a/tests/benchmarks/declarative/js/qjsvalue/tst_qjsvalue.cpp b/tests/benchmarks/declarative/js/qjsvalue/tst_qjsvalue.cpp index afe6d1c4db..5ebcbd86ac 100644 --- a/tests/benchmarks/declarative/js/qjsvalue/tst_qjsvalue.cpp +++ b/tests/benchmarks/declarative/js/qjsvalue/tst_qjsvalue.cpp @@ -7,29 +7,29 @@ ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** ** 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 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception +** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. ** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. ** ** ** diff --git a/tests/benchmarks/declarative/js/qjsvalueiterator/tst_qjsvalueiterator.cpp b/tests/benchmarks/declarative/js/qjsvalueiterator/tst_qjsvalueiterator.cpp index 8b4921cf8e..14dc75d204 100644 --- a/tests/benchmarks/declarative/js/qjsvalueiterator/tst_qjsvalueiterator.cpp +++ b/tests/benchmarks/declarative/js/qjsvalueiterator/tst_qjsvalueiterator.cpp @@ -7,29 +7,29 @@ ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** ** 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 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception +** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. ** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. ** ** ** |