diff options
Diffstat (limited to 'tests/auto/integrationtests')
174 files changed, 35110 insertions, 0 deletions
diff --git a/tests/auto/integrationtests/collections/.gitignore b/tests/auto/integrationtests/collections/.gitignore new file mode 100644 index 0000000000..dcd9d49ec0 --- /dev/null +++ b/tests/auto/integrationtests/collections/.gitignore @@ -0,0 +1 @@ +tst_collections diff --git a/tests/auto/integrationtests/collections/collections.pro b/tests/auto/integrationtests/collections/collections.pro new file mode 100644 index 0000000000..8601ff877d --- /dev/null +++ b/tests/auto/integrationtests/collections/collections.pro @@ -0,0 +1,4 @@ +load(qttest_p4) +SOURCES += tst_collections.cpp +QT = core +CONFIG += parallel_test diff --git a/tests/auto/integrationtests/collections/tst_collections.cpp b/tests/auto/integrationtests/collections/tst_collections.cpp new file mode 100644 index 0000000000..f09ff06234 --- /dev/null +++ b/tests/auto/integrationtests/collections/tst_collections.cpp @@ -0,0 +1,3846 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + + +// test the container forwards +#include <QtContainerFwd> + +static QCache<int, int> *cacheX; +static QHash<int, int> *hashX; +static QLinkedList<int> *linkedListX; +static QList<int> *listX; +static QMap<int, int> *mapX; +static QMultiHash<int, int> *multiHashX; +static QMultiMap<int, int> *multiMapX; +static QPair<int, int> *pairX; +static QQueue<int> *queueX; +static QSet<int> *setX; +static QStack<int> *stackX; +static QVarLengthArray<int> *varLengthArrayX; +static QVarLengthArray<int, 512> *varLengthArrayY; +static QVector<int> *vectorX; + +void foo() +{ + cacheX = 0; + hashX = 0; + linkedListX = 0; + listX = 0; + mapX = 0; + multiHashX = 0; + multiMapX = 0; + pairX = 0; + queueX = 0; + setX = 0; + stackX = 0; + varLengthArrayX = 0; + varLengthArrayY = 0; + vectorX = 0; +} + +#include <QtTest/QtTest> + +#ifndef QT_NO_STL +# include <algorithm> +#endif + +#include "qalgorithms.h" +#include "qbitarray.h" +#include "qbytearray.h" +#include "qcache.h" +#include "qhash.h" +#include "qlinkedlist.h" +#include "qlist.h" +#include "qmap.h" +#include "qpair.h" +#include "qregexp.h" +#include "qset.h" +#include "qstack.h" +#include "qstring.h" +#include "qstringlist.h" +#include "qvarlengtharray.h" +#include "qvector.h" +#include "qqueue.h" + +template class QList<int>; + +//TESTED_FILES= + +class tst_Collections : public QObject +{ + Q_OBJECT + +public: + tst_Collections(); + ~tst_Collections(); + +public slots: + void init(); + void cleanup(); +private slots: + void typeinfo(); + void qstring(); + void list(); + void linkedList(); + void vector(); + void byteArray(); + void stack(); + void hash(); + void map(); + void bitArray(); + void cache(); + void regexp(); + void pair(); + void sharableQList(); + void sharableQLinkedList(); + void sharableQVector(); + void sharableQMap(); + void sharableQHash(); + void q_foreach(); + void conversions(); + void javaStyleIterators(); + void constAndNonConstStlIterators(); +#ifndef QT_NO_STL + void vector_stl_data(); + void vector_stl(); + void list_stl_data(); + void list_stl(); + void linkedlist_stl_data(); + void linkedlist_stl(); +#endif + void q_init(); + void pointersize(); + void containerInstantiation(); + void qtimerList(); + void containerTypedefs(); + void forwardDeclared(); +#if defined(Q_ALIGNOF) && defined(Q_DECL_ALIGN) + void alignment(); +#endif + void QTBUG13079_collectionInsideCollection(); + + void foreach_2(); + void insert_remove_loop(); +}; + +struct LargeStatic { + static int count; + LargeStatic():c(count) { ++count; } + LargeStatic(const LargeStatic& o):c(o.c) { ++count; } + ~LargeStatic() { --count; } + int c; + int data[8]; +}; + +int LargeStatic::count = 0; + +struct Movable { + static int count; + Movable():c(count) { ++count; } + Movable(const Movable& o):c(o.c) { ++count; } + ~Movable() { --count; } + int c; +}; + +int Movable::count = 0; +QT_BEGIN_NAMESPACE +Q_DECLARE_TYPEINFO(Movable, Q_MOVABLE_TYPE); +QT_END_NAMESPACE + + +struct Pod { + int i1, i2; +}; + +tst_Collections::tst_Collections() +{ +} + +tst_Collections::~tst_Collections() +{ + +} + +void tst_Collections::init() +{ +} + +void tst_Collections::cleanup() +{ +} + +void tst_Collections::typeinfo() +{ + QVERIFY(QTypeInfo<int*>::isPointer); + QVERIFY(!QTypeInfo<int>::isPointer); + QVERIFY(QTypeInfo<QString>::isComplex); + QVERIFY(!QTypeInfo<int>::isComplex); +} + +void tst_Collections::list() +{ + { + QList<int> list; + QVERIFY(list.isEmpty()); + list.append(1); + QVERIFY(list.size() == 1); + + QVERIFY(*list.begin() == 1); + + list.push_back(2); + list += (3); + list << 4 << 5 << 6; + QVERIFY(!list.isEmpty()); + QVERIFY(list.size() == 6); + QVERIFY(list.end() - list.begin() == list.size()); + +#if !defined(QT_NO_STL) && !defined(Q_CC_MSVC) && !defined(Q_CC_SUN) + QVERIFY(std::binary_search(list.begin(), list.end(), 2) == true); + QVERIFY(std::binary_search(list.begin(), list.end(), 9) == false); +#endif + QVERIFY(qBinaryFind(list.begin(), list.end(), 2) == list.begin() + 1); + QVERIFY(qLowerBound(list.begin(), list.end(), 2) == list.begin() + 1); + QVERIFY(qUpperBound(list.begin(), list.end(), 2) == list.begin() + 2); + QVERIFY(qBinaryFind(list.begin(), list.end(), 9) == list.end()); + QVERIFY(qLowerBound(list.begin(), list.end(), 9) == list.end()); + QVERIFY(qUpperBound(list.begin(), list.end(), 9) == list.end()); + { + int sum = 0; + QListIterator<int> i(list); + while (i.hasNext()) + sum += i.next(); + QVERIFY(sum == 21); + } + + { + QList<int> list1; + list1 << 1 << 2 << 3 << 5 << 7 << 8 << 9; + QList<int> list2 = list1; + + QMutableListIterator<int> i1(list1); + while (i1.hasNext()) { + if (i1.next() % 2 != 0) + i1.remove(); + } + + QMutableListIterator<int> i2(list2); + i2.toBack(); + while (i2.hasPrevious()) { + if (i2.previous() % 2 != 0) + i2.remove(); + } + QVERIFY(list1.size() == 2); + QVERIFY(list2.size() == 2); + QVERIFY(list1 == list2); + } + + { + int sum = 0; + for (int i = 0; i < list.size(); ++i) + sum += list[i]; + QVERIFY(sum == 21); + } + { + int sum = 0; + QList<int>::const_iterator i = list.begin(); + while (i != list.end()) + sum += *i++; + QVERIFY(sum == 21); + } + { + int sum = 0; + QList<int>::ConstIterator i = list.begin(); + while (i != list.end()) + sum += *i++; + QVERIFY(sum == 21); + } + { + QList<int>::Iterator i = list.begin(); + i += 2; + QCOMPARE(*i, 3); + i -= 1; + QCOMPARE(*i, 2); + } + { + QList<int>::ConstIterator i = list.begin(); + i += 2; + QCOMPARE(*i, 3); + i -= 1; + QCOMPARE(*i, 2); + } + { + int sum = 0; + int i; + for (i = 0; i < list.size(); ++i) + list[i] = list[i] +1; + for (i = 0; i < list.size(); ++i) + sum += list[i]; + QVERIFY(sum == 21 + list.size()); + } + { + int sum = 0; + int i; + for (i = 0; i < list.size(); ++i) + --list[i]; + for (i = 0; i < list.size(); ++i) + sum += list[i]; + QVERIFY(sum == 21); + } + { + QMutableListIterator<int> i(list); + while (i.hasNext()) + i.setValue(2*i.next()); + } + { + int sum = 0; + QListIterator<int> i(list); + i.toBack(); + while (i.hasPrevious()) + sum += i.previous(); + QVERIFY(sum == 2*21); + } + { + QMutableListIterator<int> i(list); + i.toBack(); + while (i.hasPrevious()) + i.setValue(2*i.previous()); + } + { + int sum = 0; + QListIterator<int> i(list); + i.toBack(); + while (i.hasPrevious()) + sum += i.previous(); + QVERIFY(sum == 2*2*21); + } + { + QMutableListIterator<int> i(list); + while (i.hasNext()) { + int a = i.next(); + i.insert(a); + } + } + { + int sum = 0; + QList<int>::iterator i = list.begin(); + while (i != list.end()) + sum += *i++; + QVERIFY(sum == 2*2*2*21); + } + { + int duplicates = 0; + QListIterator<int> i(list); + while (i.hasNext()) { + int a = i.next(); + if (i.hasNext() && a == i.peekNext()) + duplicates++; + } + QVERIFY(duplicates == 6); + } + { + int duplicates = 0; + QListIterator<int> i(list); + i.toBack(); + while (i.hasPrevious()) { + int a = i.previous(); + if (i.hasPrevious() && a == i.peekPrevious()) + duplicates++; + } + QVERIFY(duplicates == 6); + } + { + QMutableListIterator<int> i(list); + while (i.hasNext()) { + int a = i.next(); + if (i.hasNext() && + i.peekNext() == a) + i.remove(); + } + } + { + int duplicates = 0; + QMutableListIterator<int> i = list; + i.toBack(); + while (i.hasPrevious()) { + int a = i.previous(); + if (i.hasPrevious() && a == i.peekPrevious()) + duplicates++; + } + QVERIFY(duplicates == 0); + } + { + QVERIFY(list.size() == 6); + QMutableListIterator<int> i = list; + while (i.hasNext()) { + int a = i.peekNext(); + i.insert(42); + QVERIFY(i.peekPrevious() == 42 && i.peekNext() == a); + i.next(); + } + QVERIFY(list.size() == 12); + i.toFront(); + while (i.findNext(42)) + i.remove(); + } + { + QList<int> l; + l << 4 << 8 << 12 << 16 << 20 << 24; + QVERIFY(l == list); + QList<int> copy = list; + list += list; + QVERIFY(l != list && l.size() == list.size()/2 && l == copy); + l += copy; + QVERIFY(l == list); + list = copy; + } + { + QList<int> copy = list; + list << 8; + QVERIFY(list.indexOf(8) == 1); + QVERIFY(list.indexOf(8, list.indexOf(8)+1) == 6); + int a = list.indexOf(8); + QVERIFY(list.count(8) == 2); + int r = list.removeAll(8); + QVERIFY(r == 2); + list.insert(a, 8); + QVERIFY(list == copy); + } + { + QList<QString> list; + list << "one" << "two" << "three" << "four" << "five" << "six"; + while (!list.isEmpty()) + list.removeAll(list.first()); + } + { + QList<QString> list; + list << "one" << "two" << "one" << "two"; + QVERIFY(!list.removeOne("three")); + QVERIFY(list.removeOne("two")); + QCOMPARE(list, QList<QString>() << "one" << "one" << "two");; + QVERIFY(list.removeOne("two")); + QCOMPARE(list, QList<QString>() << "one" << "one"); + QVERIFY(!list.removeOne("two")); + QCOMPARE(list, QList<QString>() << "one" << "one"); + QVERIFY(list.removeOne("one")); + QCOMPARE(list, QList<QString>() << "one"); + QVERIFY(list.removeOne("one")); + QVERIFY(list.isEmpty()); + QVERIFY(!list.removeOne("one")); + QVERIFY(list.isEmpty()); + } + { + QList<int> copy = list; + list << 8; + QVERIFY(list.lastIndexOf(8) == 6); + QVERIFY(list.lastIndexOf(8, list.lastIndexOf(8)-1) == 1); + list = copy; + } + { + QList<int> copy = list; + list.insert(3, 999); + QVERIFY(list[3] == 999); + list.replace(3, 222); + QVERIFY(list[3] == 222); + QVERIFY(list.contains(222) && ! list.contains(999)); + list.removeAt(3); + list = copy; + QVERIFY(list == copy); + } + { + list.clear(); + QVERIFY(list.isEmpty()); + QVERIFY(list.begin() == list.end()); + QListIterator<int> i(list); + QVERIFY(!i.hasNext() && !i.hasPrevious()); + } + { + QList<int> l1; + QList<int> l2; + l1 << 1 << 2 << 3; + l2 << 4 << 5 << 6; + QList<int> l3 = l1 + l2; + l1 += l2; + QVERIFY(l3 == l1); + } + { + QList<int> list; + QVERIFY(list.isEmpty()); + list.append(1); + QList<int> list2; + list2 = list; + list2.clear(); + QVERIFY(list2.size() == 0); + QVERIFY(list.size() == 1); + } + { + QList<int> list; + list.append(1); + list = list; + QVERIFY(list.size() == 1); + } + } + { + QList<void*> list; + list.append(0); + list.append((void*)42); + QCOMPARE(list.size(), 2); + QCOMPARE(list.at(0), (void*)0); + QCOMPARE(list.at(1), (void*)42); + } + + { + QVector<QString> vector(5); + vector[0] = "99"; + vector[4] ="100"; + QList<QString> list = vector.toList(); + + QVERIFY(list.size() == 5); + QVERIFY(list.at(0) == "99"); + QVERIFY(list.at(4) == "100"); + list[0] = "10"; + QVERIFY(list.at(0) == "10"); + QVERIFY(vector.at(0) == "99"); + + } + + { + QList<QString> list; + list.append("Hello"); + + QList<QString>::iterator it = list.begin(); + QVERIFY((*it)[0] == QChar('H')); + QVERIFY(it->constData()[0] == QChar('H')); + it->replace(QChar('H'), QChar('X')); + QVERIFY(list.first() == "Xello"); + + QList<QString>::const_iterator cit = list.constBegin(); + QVERIFY((*cit).toLower() == "xello"); + QVERIFY(cit->toUpper() == "XELLO"); + } + + { + QList<int *> list; + QVERIFY(list.value(0) == 0); + int i; + list.append(&i); + QVERIFY(list.value(0) == &i); + } + { + QList<const int *> list; + QVERIFY(list.value(0) == 0); + int i; + list.append(&i); + QVERIFY(list.value(0) == &i); + } + { + QList<int> list; + QVERIFY(list.value(0) == 0); + list.append(10); + QVERIFY(list.value(0) == 10); + } + { + QList<Pod> list; + QCOMPARE(list.value(0).i1, 0); + QCOMPARE(list.value(0).i2, 0); + } + + { + QList<QString> list; + list << "alpha" << "beta"; + list += list; + QVERIFY(list.size() == 4); + QVERIFY(list.at(0) == "alpha"); + QVERIFY(list.at(1) == "beta"); + QVERIFY(list.at(2) == "alpha"); + QVERIFY(list.at(3) == "beta"); + } + + // test endcases for inserting into a qlist + { + QList<QString> list; + list << "foo" << "bar"; + QVERIFY(!list.isEmpty()); + + list.insert(-1, "lessthanzero"); + QCOMPARE(list.at(0), QString("lessthanzero")); + + list.insert(0, "atzero"); + QCOMPARE(list.at(0), QString("atzero")); + + int listCount = list.count(); + list.insert(listCount, "atcount"); + QCOMPARE(list.at(listCount), QString("atcount")); + + listCount = list.count(); + list.insert(listCount + 1, "beyondcount"); + QCOMPARE(list.at(listCount), QString("beyondcount")); + } + + { + QList<int> list1; + list1 << 0 << 1 << 2 << 3; + list1.removeFirst(); + + list1.swap(0, 0); + QVERIFY(list1 == QList<int>() << 1 << 2 << 3); + + list1.swap(1, 1); + QVERIFY(list1 == QList<int>() << 1 << 2 << 3); + + list1.swap(2, 2); + QVERIFY(list1 == QList<int>() << 1 << 2 << 3); + + list1.swap(0, 1); + QVERIFY(list1 == QList<int>() << 2 << 1 << 3); + + list1.swap(0, 2); + QVERIFY(list1 == QList<int>() << 3 << 1 << 2); + + list1.swap(1, 2); + QVERIFY(list1 == QList<int>() << 3 << 2 << 1); + + list1.swap(1, 2); + QVERIFY(list1 == QList<int>() << 3 << 1 << 2); + + QList<QString> list2; + list2 << "1" << "2" << "3"; + + list2.swap(0, 0); + QVERIFY(list2 == QList<QString>() << "1" << "2" << "3"); + + list2.swap(1, 1); + QVERIFY(list2 == QList<QString>() << "1" << "2" << "3"); + + list2.swap(2, 2); + QVERIFY(list2 == QList<QString>() << "1" << "2" << "3"); + + list2.swap(0, 1); + QVERIFY(list2 == QList<QString>() << "2" << "1" << "3"); + + list2.swap(0, 2); + QVERIFY(list2 == QList<QString>() << "3" << "1" << "2"); + + list2.swap(1, 2); + QVERIFY(list2 == QList<QString>() << "3" << "2" << "1"); + + list2.swap(1, 2); + QVERIFY(list2 == QList<QString>() << "3" << "1" << "2"); + + QList<double> list3; + list3 << 1.0 << 2.0 << 3.0; + + list3.swap(0, 0); + QVERIFY(list3 == QList<double>() << 1.0 << 2.0 << 3.0); + + list3.swap(1, 1); + QVERIFY(list3 == QList<double>() << 1.0 << 2.0 << 3.0); + + list3.swap(2, 2); + QVERIFY(list3 == QList<double>() << 1.0 << 2.0 << 3.0); + + list3.swap(0, 1); + QVERIFY(list3 == QList<double>() << 2.0 << 1.0 << 3.0); + + list3.swap(0, 2); + QVERIFY(list3 == QList<double>() << 3.0 << 1.0 << 2.0); + + list3.swap(1, 2); + QVERIFY(list3 == QList<double>() << 3.0 << 2.0 << 1.0); + + list3.swap(1, 2); + QVERIFY(list3 == QList<double>() << 3.0 << 1.0 << 2.0); + } + + // Check what happens when using references to own items. + // Ideally we should run valgrind on this. + { + int i; + + QList<void *> list1; + list1.append(reinterpret_cast<void *>(50)); + + for (i = 1; i < 100; ++i) { + list1.append(list1.at(i - 1)); + list1.prepend(list1.at(i)); + list1.insert(i, list1.at(i - 1)); + list1.insert(i, list1.at(i)); + list1.insert(i, list1.at(i + 1)); + list1.replace(i, list1.at(i - 1)); + list1.replace(i, list1.at(i)); + list1.replace(i, list1.at(i + 1)); + } + QCOMPARE(list1.size(), 496); + for (i = 0; i < list1.size(); ++i) { + QCOMPARE(list1.at(i), reinterpret_cast<void *>(50)); + } + + QList<QString> list2; + list2.append("50"); + + for (i = 1; i < 100; ++i) { + list2.append(list2.at(i - 1)); + list2.prepend(list2.at(i)); + list2.insert(i, list2.at(i - 1)); + list2.insert(i, list2.at(i)); + list2.insert(i, list2.at(i + 1)); + list2.replace(i, list2.at(i - 1)); + list2.replace(i, list2.at(i)); + list2.replace(i, list2.at(i + 1)); + } + QCOMPARE(list2.size(), 496); + for (i = 0; i < list2.size(); ++i) { + QCOMPARE(list2.at(i), QString::fromLatin1("50")); + } + + QList<double> list3; + list3.append(50.0); + + for (i = 1; i < 100; ++i) { + list3.append(list3.at(i - 1)); + list3.prepend(list3.at(i)); + list3.insert(i, list3.at(i - 1)); + list3.insert(i, list3.at(i)); + list3.insert(i, list3.at(i + 1)); + list3.replace(i, list3.at(i - 1)); + list3.replace(i, list3.at(i)); + list3.replace(i, list3.at(i + 1)); + } + QCOMPARE(list3.size(), 496); + for (i = 0; i < list3.size(); ++i) { + QCOMPARE(list3.at(i), 50.0); + } + + QList<QTime> list4; + list4.append(QTime(12, 34, 56)); + + for (i = 1; i < 100; ++i) { + list4.append(list4.at(i - 1)); + list4.prepend(list4.at(i)); + list4.insert(i, list4.at(i - 1)); + list4.insert(i, list4.at(i)); + list4.insert(i, list4.at(i + 1)); + list4.replace(i, list4.at(i - 1)); + list4.replace(i, list4.at(i)); + list4.replace(i, list4.at(i + 1)); + } + QCOMPARE(list4.size(), 496); + for (i = 0; i < list4.size(); ++i) { + QVERIFY(list4.at(i) == QTime(12, 34, 56)); + } + + } + { + QList<int> a; + QCOMPARE(a.startsWith(1), false); + QCOMPARE(a.endsWith(1), false); + a.append(1); + QCOMPARE(a.startsWith(1), true); + QCOMPARE(a.startsWith(2), false); + QCOMPARE(a.endsWith(1), true); + QCOMPARE(a.endsWith(2), false); + a.append(2); + QCOMPARE(a.startsWith(1), true); + QCOMPARE(a.startsWith(2), false); + QCOMPARE(a.endsWith(1), false); + QCOMPARE(a.endsWith(2), true); + } +} + +void tst_Collections::linkedList() +{ + { + QLinkedList<int> list; + QVERIFY(list.isEmpty()); + list.append(1); + list.push_back(2); + list += (3); + list << 4 << 5 << 6; + QVERIFY(!list.isEmpty()); + QVERIFY(list.size() == 6); + { + int sum = 0; + QLinkedListIterator<int> i = list; + while (i.hasNext()) { + sum += i.next(); + } + QVERIFY(sum == 21); + } + { + int sum = 0; + QLinkedList<int>::const_iterator i = list.begin(); + while (i != list.end()) + sum += *i++; + QVERIFY(sum == 21); + } + { + QMutableLinkedListIterator<int> i = list; + while (i.hasNext()) + i.setValue(2*i.next()); + } + { + int sum = 0; + QLinkedListIterator<int> i = list; + i.toBack(); + while (i.hasPrevious()) + sum += i.previous(); + QVERIFY(sum == 2*21); + } + { + QMutableLinkedListIterator<int> i = list; + i.toBack(); + while (i.hasPrevious()) + i.setValue(2*i.previous()); + } + { + int sum = 0; + QLinkedListIterator<int> i = list; + i.toBack(); + while (i.hasPrevious()) + sum += i.previous(); + QVERIFY(sum == 2*2*21); + } + { + QMutableLinkedListIterator<int> i = list; + while (i.hasNext()) { + int a = i.next(); + i.insert(a); + } + } + { + int sum = 0; + QLinkedList<int>::iterator i = list.begin(); + while (i != list.end()) + sum += *i++; + QVERIFY(sum == 2*2*2*21); + } + { + int duplicates = 0; + QLinkedListIterator<int> i = list; + while (i.hasNext()) { + int a = i.next(); + if (i.hasNext() && a == i.peekNext()) + duplicates++; + } + QVERIFY(duplicates == 6); + } + { + int duplicates = 0; + QLinkedListIterator<int> i = list; + i.toBack(); + while (i.hasPrevious()) { + int a = i.previous(); + if (i.hasPrevious() && a == i.peekPrevious()) + duplicates++; + } + QVERIFY(duplicates == 6); + } + { + QMutableLinkedListIterator<int> i = list; + while (i.hasNext()) { + int a = i.next(); + if (i.hasNext() && + i.peekNext() == a) + i.remove(); + } + } + { + int duplicates = 0; + QMutableLinkedListIterator<int> i = list; + i.toBack(); + while (i.hasPrevious()) { + int a = i.previous(); + if (i.hasPrevious() && a == i.peekPrevious()) + duplicates++; + } + QVERIFY(duplicates == 0); + } + { + QVERIFY(list.size() == 6); + QMutableLinkedListIterator<int> i = list; + while (i.hasNext()) { + int a = i.peekNext(); + i.insert(42); + QVERIFY(i.peekPrevious() == 42 && i.peekNext() == a); + i.next(); + } + QVERIFY(list.size() == 12); + i.toFront(); + while (i.findNext(42)) + i.remove(); + } + { + QLinkedList<int> l; + l << 4 << 8 << 12 << 16 << 20 << 24; + QVERIFY(l == list); + QLinkedList<int> copy = list; + list += list; + QVERIFY(l != list && l.size() == list.size()/2 && l == copy); + l += copy; + QVERIFY(l == list); + list = copy; + } + { + QLinkedList<int> copy = list; + list.prepend(999); + list.append(999); + QVERIFY(list.contains(999)); + QVERIFY(list.count(999) == 2); + list.removeAll(999); + QVERIFY(list == copy); + } + { + QLinkedList<QString> list; + list << "one" << "two" << "three" << "four" << "five" << "six"; + while (!list.isEmpty()) + list.removeAll(list.first()); + } + { + QLinkedList<QString> list; + list << "one" << "two" << "one" << "two"; + QVERIFY(!list.removeOne("three")); + QVERIFY(list.removeOne("two")); + QCOMPARE(list, QLinkedList<QString>() << "one" << "one" << "two");; + QVERIFY(list.removeOne("two")); + QCOMPARE(list, QLinkedList<QString>() << "one" << "one"); + QVERIFY(!list.removeOne("two")); + QCOMPARE(list, QLinkedList<QString>() << "one" << "one"); + QVERIFY(list.removeOne("one")); + QCOMPARE(list, QLinkedList<QString>() << "one"); + QVERIFY(list.removeOne("one")); + QVERIFY(list.isEmpty()); + QVERIFY(!list.removeOne("one")); + QVERIFY(list.isEmpty()); + } + { + list.clear(); + QVERIFY(list.isEmpty()); + QVERIFY(list.begin() == list.end()); + QLinkedListIterator<int> i(list); + QVERIFY(!i.hasNext() && !i.hasPrevious()); + } + } + + { + QLinkedList<QString> list; + list.append("Hello"); + + QLinkedList<QString>::iterator it = list.begin(); + QVERIFY((*it)[0] == QChar('H')); + QVERIFY(it->constData()[0] == QChar('H')); + it->replace(QChar('H'), QChar('X')); + QVERIFY(list.first() == "Xello"); + + QLinkedList<QString>::const_iterator cit = list.constBegin(); + QVERIFY((*cit).toLower() == "xello"); + QVERIFY(cit->toUpper() == "XELLO"); + } + + { + QLinkedList<QString> list; + list << "alpha" << "beta"; + list += list; + QVERIFY(list.size() == 4); + QVERIFY(*list.begin() == "alpha"); + QVERIFY(*(list.begin() + 1) == "beta"); + QVERIFY(*(list.begin() + 2) == "alpha"); + QVERIFY(*(list.begin() + 3) == "beta"); + } + + { + QLinkedList<int> a; + QCOMPARE(a.startsWith(1), false); + QCOMPARE(a.endsWith(1), false); + a.append(1); + QCOMPARE(a.startsWith(1), true); + QCOMPARE(a.startsWith(2), false); + QCOMPARE(a.endsWith(1), true); + QCOMPARE(a.endsWith(2), false); + a.append(2); + QCOMPARE(a.startsWith(1), true); + QCOMPARE(a.startsWith(2), false); + QCOMPARE(a.endsWith(1), false); + QCOMPARE(a.endsWith(2), true); + } +}; + + +void tst_Collections::vector() +{ + QVector<int> v1; + v1 << 1 << 2 << 3; + QVector<int> v2; + v2 << 4 << 5; + QVector<int> v3; + v3 << 1 << 2 << 3 << 4 << 5; + QVERIFY(v1 + v2 == v3); + + QVector<int> emptyVector; + QCOMPARE(emptyVector.size(), 0); +#if 0 + // this should trigger an assert + emptyVector.remove(3, -3); + QCOMPARE(emptyVector.size(), 0); +#endif + emptyVector.remove(0, 0); + QCOMPARE(emptyVector.size(), 0); + + QVector<int> v4; + v4 << 1 << 2 << 3; + QCOMPARE(v4.size(), 3); + v4.remove(1, 0); + QCOMPARE(v4.size(), 3); + + QVector<int> v; + v.append(2); + QVERIFY(*v.begin() == 2); + v.prepend(1); + + v << 3 << 4 << 5 << 6; +#if !defined(QT_NO_STL) + QVERIFY(std::binary_search(v.begin(), v.end(), 2) == true); + QVERIFY(std::binary_search(v.begin(), v.end(), 9) == false); +#endif + QVERIFY(qBinaryFind(v.begin(), v.end(), 2) == v.begin() + 1); + QVERIFY(qLowerBound(v.begin(), v.end(), 2) == v.begin() + 1); + QVERIFY(qUpperBound(v.begin(), v.end(), 2) == v.begin() + 2); + QVERIFY(qBinaryFind(v.begin(), v.end(), 9) == v.end()); + QVERIFY(qLowerBound(v.begin(), v.end(), 9) == v.end()); + QVERIFY(qUpperBound(v.begin(), v.end(), 9) == v.end()); + + v.clear(); + v << 1 << 2 << 3; + v.insert(v.begin(), 0); + v.insert(v.end(), 4); + v.insert(v.begin()+2, 9); + + QVector<int> result; + result << 0 << 1 << 9 << 2 << 3 << 4; + + QVERIFY( v == result ); + + v.clear(); + v << 1 << 2 << 3; + v.insert(0, 0); + v.insert(4, 4); + v.insert(2, 9); + + QVERIFY( v == result ); + + QVector<QString> vec; + vec << "foo" << "bar"; + vec.reserve( 512 ); + QVERIFY(vec[0] == "foo"); + QVERIFY(vec[1] == "bar"); + + int initialLargeStaticCount = LargeStatic::count; + { + QVector<LargeStatic> vector; + vector.append(LargeStatic()); + vector.resize(0); + } + QCOMPARE(LargeStatic::count, initialLargeStaticCount); + + { + QVector<QString> vector; + vector << "alpha" << "beta"; + vector += vector; + QVERIFY(vector.size() == 4); + QVERIFY(vector.at(0) == "alpha"); + QVERIFY(vector.at(1) == "beta"); + QVERIFY(vector.at(2) == "alpha"); + QVERIFY(vector.at(3) == "beta"); + } + + int originalLargeStaticCount = LargeStatic::count; + { + QVector<LargeStatic> vector(5); + } + QVERIFY(LargeStatic::count == originalLargeStaticCount); + { + QVector<LargeStatic> vector(5); + QList<LargeStatic> list = vector.toList(); + } + QVERIFY(LargeStatic::count == originalLargeStaticCount); + { + QVector<LargeStatic> vector; + LargeStatic *dummy = 0; + for (int i = 0; i < 10000; ++i) { + delete dummy; + dummy = new LargeStatic; + vector.append(LargeStatic()); + } + delete dummy; + } + QVERIFY(LargeStatic::count == originalLargeStaticCount); + + int originalMovableCount = Movable::count; + { + QVector<Movable> vector(5); + } + QVERIFY(Movable::count == originalMovableCount); + { + QVector<Movable> vector(5); + QList<Movable> list = vector.toList(); + } + QVERIFY(Movable::count == originalMovableCount); + { + QVector<Movable> vector; + Movable *dummy = 0; + for (int i = 0; i < 10000; ++i) { + delete dummy; + dummy = new Movable; + vector.append(Movable()); + } + delete dummy; + } + QVERIFY(Movable::count == originalMovableCount); + + // Check what happens when using references to own items. + // Ideally we should run valgrind on this. + { + int i; + + QVector<void *> vect1; + vect1.append(reinterpret_cast<void *>(50)); + + for (i = 1; i < 100; ++i) { + vect1.append(vect1.at(i - 1)); + vect1.prepend(vect1.at(i)); + vect1.insert(i, vect1.at(i - 1)); + vect1.insert(i, vect1.at(i)); + vect1.insert(i, vect1.at(i + 1)); + vect1.replace(i, vect1.at(i - 1)); + vect1.replace(i, vect1.at(i)); + vect1.replace(i, vect1.at(i + 1)); + } + QCOMPARE(vect1.size(), 496); + for (i = 0; i < vect1.size(); ++i) { + QCOMPARE(vect1.at(i), reinterpret_cast<void *>(50)); + } + + QVector<QString> vect2; + vect2.append("50"); + + for (i = 1; i < 100; ++i) { + vect2.append(vect2.at(i - 1)); + vect2.prepend(vect2.at(i)); + vect2.insert(i, vect2.at(i - 1)); + vect2.insert(i, vect2.at(i)); + vect2.insert(i, vect2.at(i + 1)); + vect2.replace(i, vect2.at(i - 1)); + vect2.replace(i, vect2.at(i)); + vect2.replace(i, vect2.at(i + 1)); + } + QCOMPARE(vect2.size(), 496); + for (i = 0; i < vect2.size(); ++i) { + QCOMPARE(vect2.at(i), QString::fromLatin1("50")); + } + + QVector<double> vect3; + vect3.append(50.0); + + for (i = 1; i < 100; ++i) { + vect3.append(vect3.at(i - 1)); + vect3.prepend(vect3.at(i)); + vect3.insert(i, vect3.at(i - 1)); + vect3.insert(i, vect3.at(i)); + vect3.insert(i, vect3.at(i + 1)); + vect3.replace(i, vect3.at(i - 1)); + vect3.replace(i, vect3.at(i)); + vect3.replace(i, vect3.at(i + 1)); + } + QCOMPARE(vect3.size(), 496); + for (i = 0; i < vect3.size(); ++i) { + QCOMPARE(vect3.at(i), 50.0); + } + + QVector<QTime> vect4; + vect4.append(QTime(12, 34, 56)); + + for (i = 1; i < 100; ++i) { + vect4.append(vect4.at(i - 1)); + vect4.prepend(vect4.at(i)); + vect4.insert(i, vect4.at(i - 1)); + vect4.insert(i, vect4.at(i)); + vect4.insert(i, vect4.at(i + 1)); + vect4.replace(i, vect4.at(i - 1)); + vect4.replace(i, vect4.at(i)); + vect4.replace(i, vect4.at(i + 1)); + } + QCOMPARE(vect4.size(), 496); + for (i = 0; i < vect4.size(); ++i) { + QVERIFY(vect4.at(i) == QTime(12, 34, 56)); + } + } + + // this used to trigger an uninitialized read in valgrind + QVector<char> foo; + foo.resize(144); + + { + QVector<int> a; + QCOMPARE(a.startsWith(1), false); + QCOMPARE(a.endsWith(1), false); + a.append(1); + QCOMPARE(a.startsWith(1), true); + QCOMPARE(a.startsWith(2), false); + QCOMPARE(a.endsWith(1), true); + QCOMPARE(a.endsWith(2), false); + a.append(2); + QCOMPARE(a.startsWith(1), true); + QCOMPARE(a.startsWith(2), false); + QCOMPARE(a.endsWith(1), false); + QCOMPARE(a.endsWith(2), true); + } +} + +void tst_Collections::byteArray() +{ + QByteArray hello = "hello"; + QByteArray ello = "ello"; + QByteArray World = "World"; + QByteArray Wor = "Wor"; + QByteArray helloWorld = "helloWorld"; + QVERIFY(hello + World == helloWorld); + QVERIFY(hello + "World" == helloWorld); + QVERIFY("hello" + World == helloWorld); + + + QByteArray l; + QVERIFY('h' + ello == hello); + QVERIFY(Wor + 'l' + 'd' == "World"); + QVERIFY(hello + World == "helloWorld"); + QVERIFY(hello + "World" == "helloWorld"); + QVERIFY("hello" + World == "helloWorld"); + QVERIFY('h' + ello == "hello"); + QVERIFY(Wor + 'l' + 'd' == "World"); + QVERIFY("helloWorld" == hello + World); + QVERIFY("helloWorld" == hello + "World"); + QVERIFY("helloWorld" == "hello" + World); + QVERIFY("hello" == 'h' + ello); + QVERIFY("World" == Wor + 'l' + 'd'); + + QVERIFY(hello.contains('e')); + QVERIFY (true == hello.contains('e')); + QVERIFY (hello.contains('e') != false); + + QVERIFY(hello.indexOf('e') == 1); + QVERIFY(hello.indexOf('e', -10) == 1); + QVERIFY(hello.indexOf('l') == 2); + QVERIFY(hello.indexOf('l',2) == 2); + QVERIFY(hello.indexOf('l',3) == 3); + + QByteArray large = "000 100 200 300 400 500 600 700 800 900"; + + QVERIFY(large.indexOf("700") == 28); + QVERIFY(large.indexOf("700", 28) == 28); + QVERIFY(large.indexOf("700", 29) == -1); + QVERIFY(large.lastIndexOf("700") == 28); + QVERIFY(large.lastIndexOf("700", 28) == 28); + QVERIFY(large.lastIndexOf("700", 27) == -1); + + QVERIFY(large.contains("200")); + QVERIFY(!large.contains("201")); + QVERIFY(large.contains('3')); + QVERIFY(!large.contains('a')); + + QVERIFY(large.count("00") == 11); + QVERIFY(large.count('3') == 1); + QVERIFY(large.count('0') == 21); + QVERIFY(large.count("0") == 21); + QVERIFY(large.count("200") == 1); + QVERIFY(large.count("201") == 0); + + QVERIFY(hello.left(0) == ""); + QVERIFY(!hello.left(0).isNull()); + QVERIFY(hello.left(1) == "h"); + QVERIFY(hello.left(2) == "he"); + QVERIFY(hello.left(200) == "hello"); + QVERIFY(hello.left(hello.size()) == hello); + QVERIFY(hello.left(hello.size()+1) == hello); + + QVERIFY(hello.right(0) == ""); + QVERIFY(!hello.right(0).isNull()); + QVERIFY(hello.right(1) == "o"); + QVERIFY(hello.right(2) == "lo"); + QVERIFY(hello.right(200) == "hello"); + QVERIFY(hello.right(hello.size()) == hello); + QVERIFY(hello.right(hello.size()+1) == hello); + + QVERIFY(!hello.mid(0, 0).isNull()); + QVERIFY(hello.mid(0, 1) == "h"); + QVERIFY(hello.mid(0, 2) == "he"); + QVERIFY(hello.mid(0, 200) == "hello"); + QVERIFY(hello.mid(0) == "hello"); + QVERIFY(hello.mid(0, hello.size()) == hello); + QVERIFY(hello.mid(0, hello.size()+1) == hello); + + QVERIFY(hello.mid(hello.size()-0) == ""); + + QVERIFY(hello.mid(hello.size()-0).isNull()); // weird but valid 3.x semantics + QVERIFY(hello.mid(hello.size()-1) == "o"); + QVERIFY(hello.mid(hello.size()-2) == "lo"); + QVERIFY(hello.mid(hello.size()-200) == "hello"); + + QByteArray null; + QByteArray nonNull = ""; + QVERIFY(null.left(10).isNull()); + QVERIFY(null.mid(0).isNull()); + +#if 0 + QVERIFY(null == QByteArray::null); + QVERIFY(QByteArray::null == null); + QVERIFY(nonNull != QByteArray::null); + QVERIFY(QByteArray::null != nonNull); + QVERIFY(null == nonNull); + QVERIFY(QByteArray::null == QByteArray::null); +#endif + + QByteArray str = "Hello"; + QByteArray cstr(str.data(), str.size()); + QVERIFY(str == "Hello"); + QVERIFY(cstr == "Hello"); + cstr.clear(); + QVERIFY(str == "Hello"); + QVERIFY(cstr.isEmpty()); + + { + QByteArray ba1("Foo"); + ba1.prepend(ba1); + QCOMPARE(ba1, QByteArray("FooFoo")); + ba1.append(ba1); + QCOMPARE(ba1, QByteArray("FooFooFooFoo")); + ba1.insert(2, ba1); + QCOMPARE(ba1, QByteArray("FoFooFooFooFoooFooFooFoo")); + ba1.replace(3, 3, ba1); + QCOMPARE(ba1, QByteArray("FoFFoFooFooFooFoooFooFooFooooFooFoooFooFooFoo")); + ba1 = "FooFoo"; + ba1.replace(char('F'), ba1); + QCOMPARE(ba1, QByteArray("FooFooooFooFoooo")); + ba1 = "FooFoo"; + ba1.replace(char('o'), ba1); + QCOMPARE(ba1, QByteArray("FFooFooFooFooFFooFooFooFoo")); + + ba1.replace(ba1, "xxx"); + QCOMPARE(ba1, QByteArray("xxx")); + ba1.replace(ba1, QByteArray("yyy")); + QCOMPARE(ba1, QByteArray("yyy")); + ba1 += ba1; + QCOMPARE(ba1, QByteArray("yyyyyy")); + + ba1.remove(1, -1); // do nothing + QCOMPARE(ba1, QByteArray("yyyyyy")); + + ba1.replace(0, -1, "ZZZ"); + QCOMPARE(ba1, QByteArray("ZZZyyyyyy")); + } +}; + +void tst_Collections::stack() +{ + QStack<int> stack; + stack.push(1); + stack.push(2); + stack.push(3); + QVectorIterator<int> i = stack; + i.toBack(); + int sum = 0; + while (i.hasPrevious()) + sum += i.previous(); + QVERIFY(sum == 6); + + sum = 0; + for (QStack<int>::iterator i = stack.begin(); i != stack.end(); ++i) + sum += *i; + QVERIFY(sum == 6); + + while (!stack.isEmpty()) + sum -= stack.pop(); + QVERIFY(sum == 0); +} + +void tst_Collections::hash() +{ + const char *hello = "hello"; + const char *world = "world"; + const char *allo = "allo"; + const char *monde = "monde"; + + { + typedef QHash<QString, QString> Hash; + Hash hash; + QString key; + for (int i = 0; i < 10; ++i) { + key[0] = i + '0'; + for (int j = 0; j < 10; ++j) { + key[1] = j + '0'; + hash.insert(key, "V" + key); + } + } + + for (int i = 0; i < 10; ++i) { + key[0] = i + '0'; + for (int j = 0; j < 10; ++j) { + key[1] = j + '0'; + hash.remove(key); + } + } + } + + { + typedef QHash<int, const char *> Hash; + Hash hash; + hash.insert(1, hello); + hash.insert(2, world); + + QVERIFY(hash.size() == 2); + QVERIFY(!hash.isEmpty()); + + { + Hash hash2 = hash; + hash2 = hash; + hash = hash2; + hash2 = hash2; + hash = hash; + hash2.clear(); + hash2 = hash2; + QVERIFY(hash2.size() == 0); + QVERIFY(hash2.isEmpty()); + } + QVERIFY(hash.size() == 2); + + { + Hash hash2 = hash; + hash2[1] = allo; + hash2[2] = monde; + + QVERIFY(hash2[1] == allo); + QVERIFY(hash2[2] == monde); + QVERIFY(hash[1] == hello); + QVERIFY(hash[2] == world); + + hash2[1] = hash[1]; + hash2[2] = hash[2]; + + QVERIFY(hash2[1] == hello); + QVERIFY(hash2[2] == world); + + hash[1] = hash[1]; + QVERIFY(hash[1] == hello); + } + + { + Hash hash2 = hash; + hash2.detach(); + hash2.remove(1); + QVERIFY(hash2.size() == 1); + hash2.remove(1); + QVERIFY(hash2.size() == 1); + hash2.remove(0); + QVERIFY(hash2.size() == 1); + hash2.remove(2); + QVERIFY(hash2.size() == 0); + QVERIFY(hash.size() == 2); + } + + hash.detach(); + + { + Hash::iterator it1 = hash.find(1); + QVERIFY(it1 != hash.end()); + + Hash::iterator it2 = hash.find(0); + QVERIFY(it2 != hash.begin()); + QVERIFY(it2 == hash.end()); + + *it1 = monde; + QVERIFY(*it1 == monde); + QVERIFY(hash[1] == monde); + + *it1 = hello; + QVERIFY(*it1 == hello); + QVERIFY(hash[1] == hello); + + hash[1] = monde; + QVERIFY(it1.key() == 1); + QVERIFY(it1.value() == monde); + QVERIFY(*it1 == monde); + QVERIFY(hash[1] == monde); + + hash[1] = hello; + QVERIFY(*it1 == hello); + QVERIFY(hash[1] == hello); + } + + { + const Hash hash2 = hash; + + Hash::const_iterator it1 = hash2.find(1); + QVERIFY(it1 != hash2.end()); + QVERIFY(it1.key() == 1); + QVERIFY(it1.value() == hello); + QVERIFY(*it1 == hello); + + Hash::const_iterator it2 = hash2.find(2); + QVERIFY(it1 != it2); + QVERIFY(it1 != hash2.end()); + QVERIFY(it2 != hash2.end()); + + int count = 0; + it1 = hash2.begin(); + while (it1 != hash2.end()) { + count++; + ++it1; + } + QVERIFY(count == 2); + + count = 0; + it1 = hash.begin(); + while (it1 != hash.end()) { + count++; + ++it1; + } + QVERIFY(count == 2); + } + + { + QVERIFY(hash.contains(1)); + QVERIFY(hash.contains(2)); + QVERIFY(!hash.contains(0)); + QVERIFY(!hash.contains(3)); + } + + { + QVERIFY(hash.value(1) == hello); + QVERIFY(hash.value(2) == world); + QVERIFY(hash.value(3) == 0); + QVERIFY(hash.value(1, allo) == hello); + QVERIFY(hash.value(2, allo) == world); + QVERIFY(hash.value(3, allo) == allo); + QVERIFY(hash.value(0, monde) == monde); + } + + { + QHash<int,LargeStatic> hash; + for (int i = 0; i < 10; i++) + hash.insert(i, LargeStatic()); + QVERIFY(LargeStatic::count == 10); + hash.remove(7); + QVERIFY(LargeStatic::count == 9); + + } + QVERIFY(LargeStatic::count == 0); + { + QHash<int, int*> hash; + QVERIFY(((const QHash<int,int*>*) &hash)->operator[](7) == 0); + } + + { + /* + This test relies on a certain implementation of + QHash. If you change the way QHash works internally, + change this test as well. + */ + QHash<int, int> hash; + for (int i = 0; i < 1000; ++i) + hash.insert(i, i); + QVERIFY(hash.capacity() == 1031); + hash.squeeze(); + QVERIFY(hash.capacity() == 521); + + hash.insert(12345, 12345); + QVERIFY(hash.capacity() == 1031); + + for (int j = 0; j < 900; ++j) + hash.remove(j); + QVERIFY(hash.capacity() == 257); + hash.squeeze(); + QVERIFY(hash.capacity() == 67); + hash.reserve(0); + } + } + + { + QHash<int, QString> hash; + hash.insert(0, "Hello"); + + QHash<int, QString>::iterator it = hash.begin(); + QVERIFY((*it)[0] == QChar('H')); + QVERIFY(it->constData()[0] == QChar('H')); + it->replace(QChar('H'), QChar('X')); + QVERIFY(*hash.begin() == "Xello"); + + QHash<int, QString>::const_iterator cit = hash.constBegin(); + QVERIFY((*cit).toLower() == "xello"); + QVERIFY(cit->toUpper() == "XELLO"); + } + + { + QHash<int, QString> hash1, hash2; + hash1.insertMulti(1, "Alpha"); + hash1.insertMulti(1, "Gamma"); + hash2.insertMulti(1, "Beta"); + hash2.insertMulti(1, "Gamma"); + hash2.insertMulti(1, "Gamma"); + + hash1.unite(hash2); + QCOMPARE(hash1.size(), 5); + QCOMPARE(hash1.values(), + (QList<QString>() << "Gamma" << "Gamma" << "Beta" << "Gamma" << "Alpha")); + + hash2 = hash1; + hash2.unite(hash2); + QCOMPARE(hash2.size(), 10); + QCOMPARE(hash2.values(), hash1.values() + hash1.values()); + } + +#if 0 + { + QMultiHash<QString, int> hash1; + hash1.insert("1", 2); + hash1.insert("1", 1); + hash1.insert("a", 3); + hash1.insert("a", 2); + hash1.insert("a", 1); + hash1.insert("b", 2); + hash1.insert("b", 1); + + // hash1: [ "1" -> 1, "1" -> 2, "a" -> 1, "a" -> 2, "a" -> 3, "b" -> 1, "b" -> 2 ] + + QHashIterator<QString, int> i1(hash1); + i1.toBack(); + + bool found; + + found = i1.findPreviousKey("1"); + QVERIFY(found && i1.value() == 2); + + found = i1.findPreviousKey("1"); + QVERIFY(found && i1.value() == 1); + + found = i1.findPreviousKey("1"); + QVERIFY(!found); + + i1.toFront(); + found = i1.findNextKey("1"); + QVERIFY(found && i1.value() == 1); + + found = i1.findNextKey("1"); + QVERIFY(found && i1.value() == 2); + + found = i1.findNextKey("1"); + QVERIFY(!found); + + i1.toBack(); + found = i1.findPreviousKey("b"); + QVERIFY(found && i1.value() == 2); + + found = i1.findPreviousKey("b"); + QVERIFY(found && i1.value() == 1); + + found = i1.findPreviousKey("b"); + QVERIFY(!found); + + i1.toFront(); + found = i1.findNextKey("b"); + QVERIFY(found && i1.value() == 1); + + found = i1.findNextKey("b"); + QVERIFY(found && i1.value() == 2); + + found = i1.findNextKey("b"); + QVERIFY(!found); + } +#endif +} + +void tst_Collections::map() +{ + const char *hello = "hello"; + const char *world = "world"; + const char *allo = "allo"; + const char *monde = "monde"; + + { + typedef QMap<int, const char *> Map; + Map map; + map.insert(1, hello); + map.insert(2, world); + + QVERIFY(*map.begin() == hello); + + QVERIFY(map.size() == 2); + QVERIFY(!map.isEmpty()); + + { + Map map2 = map; + map2 = map; + map = map2; + map2 = map2; + map = map; + map2.clear(); + map2 = map2; + QVERIFY(map2.size() == 0); + QVERIFY(map2.isEmpty()); + } + QVERIFY(map.size() == 2); + + { + Map map2 = map; + map2[1] = allo; + map2[2] = monde; + + QVERIFY(map2[1] == allo); + QVERIFY(map2[2] == monde); + QVERIFY(map[1] == hello); + QVERIFY(map[2] == world); + + map2[1] = map[1]; + map2[2] = map[2]; + + QVERIFY(map2[1] == hello); + QVERIFY(map2[2] == world); + + map[1] = map[1]; + QVERIFY(map[1] == hello); + } + + { + Map map2 = map; + map2.detach(); + map2.remove(1); + QVERIFY(map2.size() == 1); + map2.remove(1); + QVERIFY(map2.size() == 1); + map2.remove(0); + QVERIFY(map2.size() == 1); + map2.remove(2); + QVERIFY(map2.size() == 0); + QVERIFY(map.size() == 2); + } + + map.detach(); + + { + Map::iterator it1 = map.find(1); + QVERIFY(it1 == map.begin()); + QVERIFY(it1 != map.end()); + + Map::iterator it2 = map.find(0); + QVERIFY(it2 != map.begin()); + QVERIFY(it2 == map.end()); + + *it1 = monde; + QVERIFY(*it1 == monde); + QVERIFY(map[1] == monde); + + *it1 = hello; + QVERIFY(*it1 == hello); + QVERIFY(map[1] == hello); + + map[1] = monde; + QVERIFY(it1.key() == 1); + QVERIFY(it1.value() == monde); + QVERIFY(*it1 == monde); + QVERIFY(map[1] == monde); + + map[1] = hello; + QVERIFY(*it1 == hello); + QVERIFY(map[1] == hello); + + *++it1 = allo; + QVERIFY(*it1 == allo); + QVERIFY(map[2] == allo); + *it1 = world; + + ++it1; + QVERIFY(it1 == map.end()); + + int count = 0; + it1 = map.begin(); + while (it1 != map.end()) { + count++; + ++it1; + } + QVERIFY(count == 2); + } + + { + const Map map2 = map; + + Map::const_iterator it1 = map2.find(1); + QVERIFY(it1 != map2.end()); + QVERIFY(it1.key() == 1); + QVERIFY(it1.value() == hello); + QVERIFY(*it1 == hello); + ++it1; + + Map::const_iterator it2 = map2.find(2); + QVERIFY(it1 == it2); + ++it1; + QVERIFY(it1 == map2.end()); + QVERIFY(it2 != map2.end()); + QVERIFY(it1 != it2); + + int count = 0; + it1 = map2.begin(); + while (it1 != map2.end()) { + count++; + ++it1; + } + QVERIFY(count == 2); + + count = 0; + it1 = map.begin(); + while (it1 != map.end()) { + count++; + ++it1; + } + QVERIFY(count == 2); + } + + { + QVERIFY(map.contains(1)); + QVERIFY(map.contains(2)); + QVERIFY(!map.contains(0)); + QVERIFY(!map.contains(3)); + } + + { + QVERIFY(map.value(1) == hello); + QVERIFY(map.value(2) == world); + QVERIFY(map.value(3) == 0); + QVERIFY(map.value(1, allo) == hello); + QVERIFY(map.value(2, allo) == world); + QVERIFY(map.value(3, allo) == allo); + QVERIFY(map.value(0, monde) == monde); + } + int originalLargeStaticCount = LargeStatic::count; + { + QMap<int,LargeStatic> map; + for (int i = 0; i < 10; i++) + map.insert(i, LargeStatic()); + QVERIFY(LargeStatic::count == (originalLargeStaticCount + 10)); + map.remove(7); + QVERIFY(LargeStatic::count == (originalLargeStaticCount + 9)); + + } + QVERIFY(LargeStatic::count == originalLargeStaticCount); + { + QMap<int, int*> map; + QVERIFY(((const QMap<int,int*>*) &map)->operator[](7) == 0); + } + + { + QMap<int, int> map; + map[0] = 1; + map[1] = 2; + map[2] = 4; + map[3] = 8; + int sum = 0; + int sumkey = 0; + QMapIterator<int,int> i = map; + while (i.hasNext()) { + sum += i.next().value(); + sumkey += i.key(); + } + QVERIFY(sum == 15); + QVERIFY(sumkey == 6); + } + { + QMap<int, int> map; + map[0] = 1; + map[1] = 2; + map[2] = 4; + map[3] = 8; + int sum = 0; + QMutableMapIterator<int,int> i = map; + while(i.hasNext()) + if (i.next().key() == 2) + i.remove(); + i.toFront(); + while(i.hasNext()) { + sum += i.next().value(); + i.setValue(10); + i.value() += 22; + QVERIFY(i.value() == 32); + } + QVERIFY(sum == 11); + } + { + QMap<int, int> map; + map[0] = 1; + QMutableMapIterator<int,int> i(map); + i.toBack(); + while (i.hasPrevious()) { + i.previous(); + QCOMPARE(i.key(), 0); + QCOMPARE(i.value(), 1); + } + } + } + + { + QMultiMap<QString, int> map1; + map1.insert("1", 2); + map1.insert("1", 1); + map1.insert("a", 3); + map1.insert("a", 2); + map1.insert("a", 1); + map1.insert("b", 2); + map1.insert("b", 1); + + // map1: [ "1" -> 1, "1" -> 2, "a" -> 1, "a" -> 2, "a" -> 3, "b" -> 1, "b" -> 2 ] + +#if 0 + QMapIterator<QString, int> i1(map1); + i1.toBack(); + + bool found; + found = i1.findPreviousKey("1"); + QVERIFY(found && i1.value() == 2); + + found = i1.findPreviousKey("1"); + QVERIFY(found && i1.value() == 1); + + found = i1.findPreviousKey("1"); + QVERIFY(!found); + + i1.toFront(); + found = i1.findNextKey("1"); + QVERIFY(found && i1.value() == 1); + + found = i1.findNextKey("1"); + QVERIFY(found && i1.value() == 2); + + found = i1.findNextKey("1"); + QVERIFY(!found); + + i1.toBack(); + found = i1.findPreviousKey("b"); + QVERIFY(found && i1.value() == 2); + + found = i1.findPreviousKey("b"); + QVERIFY(found && i1.value() == 1); + + found = i1.findPreviousKey("b"); + QVERIFY(!found); + + i1.toFront(); + found = i1.findNextKey("b"); + QVERIFY(found && i1.value() == 1); + + found = i1.findNextKey("b"); + QVERIFY(found && i1.value() == 2); + + found = i1.findNextKey("b"); + QVERIFY(!found); +#endif + + QMultiMap<QString, int>::iterator j1, k1; + + j1 = map1.lowerBound("0"); k1 = map1.upperBound("0"); + QVERIFY(j1 == map1.begin() && k1 == j1); + j1 = map1.lowerBound("00"); k1 = map1.upperBound("00"); + QVERIFY(j1 == map1.find("1") && k1 == j1); + j1 = map1.lowerBound("1"); k1 = map1.upperBound("1"); + QVERIFY(j1 == map1.find("1") && --(--k1) == j1); + j1 = map1.lowerBound("11"); k1 = map1.upperBound("11"); + QVERIFY(j1 == map1.find("a") && k1 == j1); + j1 = map1.lowerBound("a"); k1 = map1.upperBound("a"); + QVERIFY(j1 == map1.find("a") && k1 == map1.find("b")); + QVERIFY(j1.value() == 1 && j1.value() == 1); + j1 = map1.lowerBound("aa"); k1 = map1.upperBound("aa"); + QVERIFY(j1 == map1.find("b") && k1 == j1); + QVERIFY(j1.value() == 1); + j1 = map1.lowerBound("b"); k1 = map1.upperBound("b"); + QVERIFY(j1 == map1.find("b") && k1 == map1.end()); + QVERIFY(j1.value() == 1); + j1 = map1.lowerBound("bb"); k1 = map1.upperBound("bb"); + QVERIFY(j1 == map1.end() && k1 == j1); + + const QMultiMap<QString, int> map2 = map1; + QMultiMap<QString, int>::const_iterator j2, k2; + + j2 = map2.lowerBound("0"); k2 = map2.upperBound("0"); + QVERIFY(j2 == map2.begin() && k2 == j2); + j2 = map2.lowerBound("00"); k2 = map2.upperBound("00"); + QVERIFY(j2 == map2.find("1") && k2 == j2); + j2 = map2.lowerBound("1"); k2 = map2.upperBound("1"); + QVERIFY(j2 == map2.find("1") && --(--k2) == j2); + j2 = map2.lowerBound("11"); k2 = map2.upperBound("11"); + QVERIFY(j2 == map2.find("a") && k2 == j2); + j2 = map2.lowerBound("a"); k2 = map2.upperBound("a"); + QVERIFY(j2 == map2.find("a") && k2 == map2.find("b")); + QVERIFY(j2.value() == 1 && j2.value() == 1); + j2 = map2.lowerBound("aa"); k2 = map2.upperBound("aa"); + QVERIFY(j2 == map2.find("b") && k2 == j2); + QVERIFY(j2.value() == 1); + j2 = map2.lowerBound("b"); k2 = map2.upperBound("b"); + QVERIFY(j2 == map2.find("b") && k2 == map2.end()); + QVERIFY(j2.value() == 1); + j2 = map2.lowerBound("bb"); k2 = map2.upperBound("bb"); + QVERIFY(j2 == map2.end() && k2 == j2); + } + + { + QMap<int, QString> map; + map.insert(0, "Hello"); + + QMap<int, QString>::iterator it = map.begin(); + QVERIFY((*it)[0] == QChar('H')); + QVERIFY(it->constData()[0] == QChar('H')); + it->replace(QChar('H'), QChar('X')); + QVERIFY(*map.begin() == "Xello"); + + QMap<int, QString>::const_iterator cit = map.constBegin(); + QVERIFY((*cit).toLower() == "xello"); + QVERIFY(cit->toUpper() == "XELLO"); + } + + { + QMap<int, QString> map1, map2; + map1.insertMulti(1, "Alpha"); + map1.insertMulti(1, "Gamma"); + map2.insertMulti(1, "Beta"); + map2.insertMulti(1, "Gamma"); + map2.insertMulti(1, "Gamma"); + + map1.unite(map2); + QCOMPARE(map1.size(), 5); + QCOMPARE(static_cast<QStringList>(map1.values()), + (QStringList() << "Gamma" << "Gamma" << "Beta" << "Gamma" << "Alpha")); + + map2 = map1; + map2.unite(map2); + QCOMPARE(map2.size(), 10); + QCOMPARE(map2.values(), map1.values() + map1.values()); + } +} + +void tst_Collections::qstring() +{ + QString hello = "hello"; + QString ello = "ello"; + QString World = "World"; + QString Wor = "Wor"; + QString helloWorld = "helloWorld"; + + QString s = hello + "World"; + QVERIFY(hello + World == helloWorld); + QVERIFY(hello + "World" == helloWorld); + QVERIFY("hello" + World == helloWorld); + + + QString l; + QVERIFY('h' + ello == hello); + QVERIFY(Wor + 'l' + 'd' == "World"); + QVERIFY(hello + World == "helloWorld"); + QVERIFY(hello + "World" == "helloWorld"); + QVERIFY("hello" + World == "helloWorld"); + QVERIFY('h' + ello == "hello"); + QVERIFY(Wor + 'l' + 'd' == "World"); + QVERIFY("helloWorld" == hello + World); + QVERIFY("helloWorld" == hello + "World"); + QVERIFY("helloWorld" == "hello" + World); + QVERIFY("hello" == 'h' + ello); + QVERIFY("World" == Wor + 'l' + 'd'); + + QVERIFY(hello.contains('e')); + QVERIFY (true == hello.contains('e')); + QVERIFY (hello.contains('e') != false); + + QVERIFY(hello.indexOf('e') == 1); + QVERIFY(hello.indexOf('e', -10) == 1); + QVERIFY(hello.indexOf('l') == 2); + QVERIFY(hello.indexOf('l',2) == 2); + QVERIFY(hello.indexOf('l',3) == 3); + + QString large = "000 100 200 300 400 500 600 700 800 900"; + + QVERIFY(large.indexOf("700") == 28); + QVERIFY(large.indexOf("700", 28) == 28); + QVERIFY(large.indexOf("700", 29) == -1); + QVERIFY(large.lastIndexOf("700") == 28); + QVERIFY(large.lastIndexOf("700", 28) == 28); + QVERIFY(large.lastIndexOf("700", 27) == -1); + + QVERIFY(large.contains("200")); + QVERIFY(!large.contains("201")); + QVERIFY(large.contains('3')); + QVERIFY(!large.contains('a')); + + QVERIFY(large.count("00") == 11); + QVERIFY(large.count('3') == 1); + QVERIFY(large.count('0') == 21); + QVERIFY(large.count("0") == 21); + QVERIFY(large.count("200") == 1); + QVERIFY(large.count("201") == 0); + + QVERIFY(hello.left(0) == ""); + QVERIFY(!hello.left(0).isNull()); + QVERIFY(hello.left(1) == "h"); + QVERIFY(hello.left(2) == "he"); + QVERIFY(hello.left(200) == "hello"); + QVERIFY(hello.left(hello.size()) == hello); + QVERIFY(hello.left(hello.size()+1) == hello); + + QVERIFY(hello.right(0) == ""); + QVERIFY(!hello.right(0).isNull()); + QVERIFY(hello.right(1) == "o"); + QVERIFY(hello.right(2) == "lo"); + QVERIFY(hello.right(200) == "hello"); + QVERIFY(hello.right(hello.size()) == hello); + QVERIFY(hello.right(hello.size()+1) == hello); + + QVERIFY(!hello.mid(0, 0).isNull()); + QVERIFY(hello.mid(0, 1) == "h"); + QVERIFY(hello.mid(0, 2) == "he"); + QVERIFY(hello.mid(0, 200) == "hello"); + QVERIFY(hello.mid(0) == "hello"); + QVERIFY(hello.mid(0, hello.size()) == hello); + QVERIFY(hello.mid(0, hello.size()+1) == hello); + + QVERIFY(hello.mid(hello.size()-0) == ""); + + QVERIFY(hello.mid(hello.size()-0).isNull()); + QVERIFY(hello.mid(hello.size()-1) == "o"); + QVERIFY(hello.mid(hello.size()-2) == "lo"); + QVERIFY(hello.mid(hello.size()-200) == "hello"); + + QString null; + QString nonNull = ""; + QVERIFY(null.left(10).isNull()); + QVERIFY(null.mid(0).isNull()); + + QVERIFY(null == QString::null); + QVERIFY(QString::null == null); + QVERIFY(nonNull != QString::null); + QVERIFY(QString::null != nonNull); + QVERIFY(null == nonNull); + QVERIFY(QString::null == QString::null); + + QString fill = "123"; + fill.fill('a'); + QVERIFY(fill == "aaa"); + + s.clear(); + s = hello; + s.append(World); + QVERIFY(s == helloWorld); + s.clear(); + s = World; + s.insert(0,hello); + QVERIFY(s == helloWorld); + s = "012345"; + s.insert(3, 'E'); + QVERIFY(s == "012E345"); + s.insert(3, "INSID"); + QVERIFY(s == "012INSIDE345"); + s = "short"; + s.insert(7, 'E'); + QVERIFY(s == "short E"); + s = "short"; + s.insert(7, "END"); + QVERIFY(s == "short END"); + + QVERIFY(QString::fromLatin1("hello") == "hello"); + + s = "first"; + QVERIFY(s.toLatin1() == "first"); + s = "second"; + QVERIFY(s.toLatin1() == "second"); + s.clear(); + QVERIFY(s.isNull()); + QVERIFY(s.toLatin1().size() == 0); + QVERIFY(s.toLatin1().isEmpty()); + QVERIFY(s.toLatin1().isNull()); + + s = "first-utf8"; + QVERIFY(s.toUtf8() == "first-utf8"); + s = "second-utf8"; + QVERIFY(s.toUtf8() == "second-utf8"); + s.clear(); + QVERIFY(s.isNull()); + QVERIFY(s.toUtf8().size() == 0); + QVERIFY(s.toUtf8().isEmpty()); + QVERIFY(s.toUtf8().isNull()); + + s = "first-utf8"; + QVERIFY(s.toUtf8() == "first-utf8"); + s = "second-utf8"; + QVERIFY(s.toUtf8() == "second-utf8"); + s.clear(); + QVERIFY(s.isNull()); + QVERIFY(s.toUtf8().size() == 0); + QVERIFY(s.toUtf8().isEmpty()); + QVERIFY(s.toUtf8().isNull()); + + s = "first-local8Bit"; + QVERIFY(s.toLocal8Bit() == "first-local8Bit"); + s = "second-local8Bit"; + QVERIFY(s.toLocal8Bit() == "second-local8Bit"); + s.clear(); + QVERIFY(s.isNull()); + QVERIFY(s.toLocal8Bit().size() == 0); + QVERIFY(s.toLocal8Bit().isEmpty()); + + s = "first-ascii"; + QVERIFY(s.toAscii() == "first-ascii"); + s = "second-ascii"; + QVERIFY(s.toAscii() == "second-ascii"); + s.clear(); + QVERIFY(s.isNull()); + QVERIFY(s.toAscii().size() == 0); + QVERIFY(s.toAscii().isEmpty()); + QVERIFY(s.toAscii().isNull()); + + s = "ascii"; + s += (uchar) 0xb0; + QVERIFY(s.toUtf8() != s.toLatin1()); + QString sa = s.toLatin1().constData(); + QVERIFY(sa[sa.length()-1] == (ushort) 0xb0); + QVERIFY(sa.left(sa.length()-1) == "ascii"); + + QVERIFY(s == QString::fromUtf8(s.toUtf8().constData())); + + s = "12"; + s.append('3'); + s += '4'; + QVERIFY(s == "1234"); + + s = "repend"; + s.prepend('p'); + QVERIFY(s == "prepend"); + s.prepend("abc "); + QVERIFY(s == "abc prepend"); + + s = " whitespace "; + QVERIFY(s.trimmed() == "whitespace"); + s = " lots of stuff "; + QVERIFY(s.simplified() == "lots of stuff"); + + s = "a hat, a stick, a ski"; + QVERIFY(s[2] == 'h'); + QVERIFY(s[1] < 'b'); + + + s = "12223"; + s.remove(1, 2); + QVERIFY(s == "123"); + + s = "(%1)(%2)"; + s = s.arg("foo").arg(7); + QVERIFY(s == "(foo)(7)"); + + +#if 0 + s = "stl rocks"; + std::string stl_string = s; + s = stl_string; + QVERIFY(s == "stl rocks"); +#endif + + { + QString str("Bananas"); + QVERIFY(str.startsWith("Ban")); + QVERIFY(false == str.startsWith("Car")); + } + { + QString str("Bananas"); + QVERIFY(str.endsWith("anas")); + QVERIFY(false == str.endsWith("pple")); + } + + + QString str = "Hello"; + QString cstr = QString::fromRawData(str.unicode(), str.length()); + QVERIFY(str == "Hello"); + QVERIFY(cstr == "Hello"); + cstr.clear(); + QVERIFY(str == "Hello"); + QVERIFY(cstr.isEmpty()); + + { + QString str1("Foo"); + str1.prepend(str1); + QCOMPARE(str1, QString("FooFoo")); + str1.append(str1); + QCOMPARE(str1, QString("FooFooFooFoo")); + str1.insert(2, str1); + QCOMPARE(str1, QString("FoFooFooFooFoooFooFooFoo")); + str1.replace(3, 3, str1); + QCOMPARE(str1, QString("FoFFoFooFooFooFoooFooFooFooooFooFoooFooFooFoo")); + str1 = "FooFoo"; + str1.replace(char('F'), str1); + QCOMPARE(str1, QString("FooFooooFooFoooo")); + str1 = "FooFoo"; + str1.replace(char('o'), str1); + QCOMPARE(str1, QString("FFooFooFooFooFFooFooFooFoo")); + + str1 = "Foo"; + str1.replace("Foo", str1); + QCOMPARE(str1, QString("Foo")); + str1.replace(str1, str1); + QCOMPARE(str1, QString("Foo")); + + str1 = "Foo"; + str1.replace("Foo", str1, Qt::CaseInsensitive); + QCOMPARE(str1, QString("Foo")); + str1.replace(str1, str1); + QCOMPARE(str1, QString("Foo")); + + str1 = "FooFoo"; + str1.reserve(100); + str1.replace("oo", str1); + QCOMPARE(str1, QString("FFooFooFFooFoo")); + + str1 = "Bar"; + str1.replace("FooFoo", str1); + QCOMPARE(str1, QString("Bar")); + + str1.replace(str1, "xxx"); + QCOMPARE(str1, QString("xxx")); + str1.replace(str1, QString("yyy")); + QCOMPARE(str1, QString("yyy")); + str1 += str1; + QCOMPARE(str1, QString("yyyyyy")); + } +} + + +void tst_Collections::bitArray() +{ + QBitArray ba(20); + QVERIFY(ba.testBit(17) == false); + ba.setBit(17); + QVERIFY(ba.size() == 20); + QVERIFY(ba.testBit(17)==true); + QVERIFY(!ba.testBit(16)); + ba[4] = true; + QVERIFY(ba.testBit(4)); + QVERIFY(ba[4]); + int sum = 0; + for(int i = 0; i < 20; i++) + sum += ba.testBit(i) ? 1 : 0; + QVERIFY(sum == 2); + + ba = QBitArray(7, true); + QVERIFY(ba.size() == 7); + QVERIFY(ba[5]); + + ba = QBitArray(3); + ba[0] = ba[2] = true; + + QBitArray nba(3); + nba[1] = true; + + QVERIFY(~ba == nba); + +}; + +struct CacheFoo +{ + CacheFoo(int v):val(v) { counter++; } + ~CacheFoo() { counter--; } + int val; + static int counter; + bool isDetached() const { return val != 2; } +}; + +int CacheFoo::counter = 0; + +void tst_Collections::cache() +{ + { + CacheFoo* cf; + QCache<int, CacheFoo> cache(120); + int i; + for (i = 0; i < 30; i++) { + cf = cache.object(10); + cache.insert(i, new CacheFoo(i), i); + } + + QVERIFY(cache.contains(10)); + QVERIFY(!cache.contains(1)); + QVERIFY(!cache.contains(2)); + delete cache.take(10); + } + { + QCache<int, QString> cache(120); + int i; + QString two; + for (i = 0; i < 30; i++) { + QString s = QString::number(i); + cache.insert(i, new QString(s), i); + if (i == 2) + two = s; + } + QVERIFY(!cache.contains(3)); + QVERIFY(!cache.contains(2)); + } + { + QCache<int, int> cache(100); + cache.insert(2, new int(2)); + *cache[2] = 3; + QVERIFY(*cache.object(2) == 3); + } + + QVERIFY(CacheFoo::counter == 0); + +} + +void tst_Collections::regexp() +{ + QRegExp rx("^\\d\\d?$"); + QVERIFY(rx.indexIn("123") == -1); + QVERIFY(rx.indexIn("-6") == -1); + QVERIFY(rx.indexIn("6") == 0) ; +} + +void tst_Collections::pair() +{ + QPair<double, int> p; + QVERIFY(p.first == 0.0); + QVERIFY(p.second == 0); + + QPair<int, QString> a(1, "Zebra"), b(2, "Ying"), c(3, "Yang"), d(3, "Ying"), e(5, "Alabama"); + QVERIFY(a.first == 1); + QVERIFY(a.second == "Zebra"); + QVERIFY(a == qMakePair(1, QString("Zebra"))); + + QVERIFY(a == a && b == b && c == c && d == d && e == e); + QVERIFY(a != b && a != c && a != d && a != e && b != c && b != d && b != e && c != d && c != e + && d != e); + + QVERIFY(a < b && b < c && c < d && d < e); + QVERIFY(a <= b && b <= c && c <= d && d <= e); + + QVERIFY(e > d && d > c && c > b && b > a); + QVERIFY(e >= d && d >= c && c >= b && b >= a); + + QVERIFY(!(a > b || b > c || c > d || d > e)); + QVERIFY(!(a >= b || b >= c || c >= d || d >= e)); + + QVERIFY(!(e < d || d < c || c < b || b < a)); + QVERIFY(!(e <= d || d <= c || c <= b || b <= a)); + + QVERIFY(a <= a && b <= b && c <= c && d <= d && e <= e); + QVERIFY(!(a < a || b < b || c < c || d < d || e < e)); + + QVERIFY(a >= a && b >= b && c >= c && d >= d && e >= e); + QVERIFY(!(a > a || b > b || c > c || d > d || e > e)); +} + +/* + These test that Java-style mutable iterators don't trash shared + copy (the setSharable() mechanism). +*/ + +template <class Container> +void populate(Container &); + +template <> +void populate(QList<int> &container) +{ + container << 1 << 2 << 4 << 8; +} + +template <> +void populate(QLinkedList<int> &container) +{ + container << 1 << 2 << 4 << 8; +} + +template <> +void populate(QVector<int> &container) +{ + container << 1 << 2 << 4 << 8; +} + +template <> +void populate(QMap<int, int> &container) +{ + container.insert(1, 1); + container.insert(2, 2); + container.insert(4, 4); + container.insert(8, 8); +} + +template <> +void populate(QHash<int, int> &container) +{ + container.insert(1, 1); + container.insert(2, 2); + container.insert(4, 4); + container.insert(8, 8); +} + +template <class Container> +bool isSharable(const Container &container) +{ + Container copy = container; + return !container.isDetached(); +} + +template <class Container, class ContainerMutableIterator> +void testContainer() +{ + /* + Verify that shared_null's 'sharable' flag is set to true. + */ + { + Container c1; + QVERIFY(!c1.isDetached()); + + Container c2 = c1; + QVERIFY(!c1.isDetached()); + QVERIFY(!c2.isDetached()); + } + + /* + Verify that the 'sharable' flag is true while no mutable + iterator is active. + */ + { + Container c1; + populate(c1); + QVERIFY(c1.size() == 4); + QVERIFY(c1.isDetached()); + + Container c2 = c1; + QVERIFY(c1.size() == 4); + QVERIFY(c2.size() == 4); + QVERIFY(!c1.isDetached()); + QVERIFY(!c2.isDetached()); + } + + /* + Verify that the 'sharable' flag is set to false by the + mutable iterator. + */ + { + Container c1; + populate(c1); + QVERIFY(c1.size() == 4); + QVERIFY(c1.isDetached()); + + ContainerMutableIterator i(c1); + i.next(); + + Container c2 = c1; + QVERIFY(c1.size() == 4); + QVERIFY(c2.size() == 4); + QVERIFY(c1.isDetached()); + QVERIFY(c2.isDetached()); + + i.remove(); + QVERIFY(c1.size() == 3); + QVERIFY(c2.size() == 4); + } + + /* + Verify that the 'sharable' flag is reset to true by the + mutable iterator's destructor. + */ + { + Container c1; + populate(c1); + QVERIFY(c1.size() == 4); + QVERIFY(c1.isDetached()); + + { + ContainerMutableIterator i(c1); + i.next(); + } + + Container c2 = c1; + QVERIFY(c1.size() == 4); + QVERIFY(c2.size() == 4); + QVERIFY(!c1.isDetached()); + QVERIFY(!c2.isDetached()); + } + + /* + Verify that the 'sharable' flag only affects the original + object, not the copies. + */ + { + Container c1; + populate(c1); + QVERIFY(c1.size() == 4); + QVERIFY(c1.isDetached()); + + Container c2 = c1; + QVERIFY(isSharable(c2)); + + ContainerMutableIterator i(c1); + QVERIFY(!isSharable(c1)); + QVERIFY(isSharable(c2)); + + Container c3 = c1; + QVERIFY(!isSharable(c1)); + QVERIFY(isSharable(c2)); + QVERIFY(isSharable(c3)); + QVERIFY(c1.isDetached()); + QVERIFY(c2.isDetached()); + QVERIFY(c3.isDetached()); + + Container c4; + c4 = c1; + QVERIFY(!isSharable(c1)); + QVERIFY(isSharable(c2)); + QVERIFY(isSharable(c4)); + QVERIFY(c1.isDetached()); + QVERIFY(c2.isDetached()); + QVERIFY(c4.isDetached()); + + c3 = c2; + QVERIFY(!isSharable(c1)); + QVERIFY(isSharable(c2)); + QVERIFY(isSharable(c3)); + QVERIFY(c1.isDetached()); + QVERIFY(!c2.isDetached()); + QVERIFY(!c3.isDetached()); + } +} + +#define TEST_SEQUENTIAL_CONTAINER(Container) \ + testContainer<Q##Container<int>, QMutable##Container##Iterator<int> >() + +#define TEST_ASSOCIATIVE_CONTAINER(Container) \ + testContainer<Q##Container<int, int>, QMutable##Container##Iterator<int, int> >() + +void tst_Collections::sharableQList() +{ + TEST_SEQUENTIAL_CONTAINER(List); +} + +void tst_Collections::sharableQLinkedList() +{ + TEST_SEQUENTIAL_CONTAINER(LinkedList); +} + +void tst_Collections::sharableQVector() +{ + TEST_SEQUENTIAL_CONTAINER(Vector); +} + +void tst_Collections::sharableQMap() +{ + TEST_ASSOCIATIVE_CONTAINER(Map); +} + +void tst_Collections::sharableQHash() +{ + TEST_ASSOCIATIVE_CONTAINER(Hash); +} + +static int getList_calls = 0; +QList<int> getList() +{ + ++getList_calls; + QList<int> list; + list << 1 << 2 << 3 << 4 << 5 << 6; + return list; +} + + +void tst_Collections::q_foreach() +{ + QList<int> list; + list << -2 << -1 << 0 << 1 << 2; + + int sum = 0; + int j = 0; + foreach(int i, list) { + QCOMPARE(i, list.at(j)); + sum += i; + ++j; + } + QCOMPARE(sum, 0); + + // again, but without scope + foreach(int i, list) + sum += i; + QCOMPARE(sum, 0); + + foreach(int i, list) { + sum += i; + if (i == 0) + break; + } + QCOMPARE(sum, -3); + + sum = 0; + foreach(int i, list) { + if (i < 0) + continue; + sum += i; + } + QCOMPARE(sum, 3); + + sum = 0; + getList_calls = 0; + foreach(int i, getList()) + sum += i; + QCOMPARE(sum, 21); + QCOMPARE(getList_calls, 1); +} + + +void tst_Collections::conversions() +{ +#define STUFF "A" << "C" << "B" << "A" + + { + QList<QString> list1; + list1 << STUFF; + + QVector<QString> vect1 = list1.toVector(); + QCOMPARE(list1.size(), 4); + QVERIFY(vect1 == (QVector<QString>() << STUFF)); + + QList<QString> list2 = vect1.toList(); + QCOMPARE(list2.size(), 4); + QVERIFY(list2 == (QList<QString>() << STUFF)); + + QSet<QString> set1 = list1.toSet(); + QCOMPARE(set1.size(), 3); + QVERIFY(set1.contains("A")); + QVERIFY(set1.contains("B")); + QVERIFY(set1.contains("C")); + QVERIFY(!set1.contains("D")); + + QList<QString> list3 = set1.toList(); + QCOMPARE(list3.size(), 3); + QVERIFY(list3.contains("A")); + QVERIFY(list3.contains("B")); + QVERIFY(list3.contains("C")); + QVERIFY(!list3.contains("D")); + + QVERIFY(QList<int>().toVector().isEmpty()); + QVERIFY(QList<int>().toSet().isEmpty()); + QVERIFY(QVector<int>().toList().isEmpty()); + QVERIFY(QSet<int>().toList().isEmpty()); + } + + { + QList<QString> list1; + list1 << STUFF; + + QVector<QString> vect1 = QVector<QString>::fromList(list1); + QCOMPARE(list1.size(), 4); + QVERIFY(vect1 == (QVector<QString>() << STUFF)); + + QList<QString> list2 = QList<QString>::fromVector(vect1); + QCOMPARE(list2.size(), 4); + QVERIFY(list2 == (QList<QString>() << STUFF)); + + QSet<QString> set1 = QSet<QString>::fromList(list1); + QCOMPARE(set1.size(), 3); + QVERIFY(set1.contains("A")); + QVERIFY(set1.contains("B")); + QVERIFY(set1.contains("C")); + QVERIFY(!set1.contains("D")); + + QList<QString> list3 = QList<QString>::fromSet(set1); + QCOMPARE(list3.size(), 3); + QVERIFY(list3.contains("A")); + QVERIFY(list3.contains("B")); + QVERIFY(list3.contains("C")); + QVERIFY(!list3.contains("D")); + + QVERIFY(QVector<int>::fromList(QList<int>()).isEmpty()); + QVERIFY(QSet<int>::fromList(QList<int>()).isEmpty()); + QVERIFY(QList<int>::fromVector(QVector<int>()).isEmpty()); + QVERIFY(QList<int>::fromSet(QSet<int>()).isEmpty()); + } +#undef STUFF +} + +void tst_Collections::javaStyleIterators() +{ + QStringList list; + list << "a" << "b" << "c"; + QMutableStringListIterator i(list); + while (i.hasNext()) { + i.next(); + i.setValue(""); + } + while (i.hasPrevious()) { + i.previous(); + QVERIFY(i.value().isEmpty()); + i.value() = "x"; + QCOMPARE(i.value(), QString("x")); + } +} + +template <class Container> +void testLinkedListLikeStlIterators() +{ + Container fake; + typename Container::value_type t; + fake << t; + + typename Container::iterator i1 = fake.begin(), i2 = i1 + 1; + typename Container::const_iterator c1 = i1, c2 = c1 + 1; + + QVERIFY(i1 == i1); + QVERIFY(i1 == c1); + QVERIFY(c1 == i1); + QVERIFY(c1 == c1); + QVERIFY(i2 == i2); + QVERIFY(i2 == c2); + QVERIFY(c2 == i2); + QVERIFY(c2 == c2); + + QVERIFY(i1 != i2); + QVERIFY(i1 != c2); + QVERIFY(c1 != i2); + QVERIFY(c1 != c2); + QVERIFY(i2 != i1); + QVERIFY(i2 != c1); + QVERIFY(c2 != i1); + QVERIFY(c2 != c1); +} + +template <class Container> +void testListLikeStlIterators() +{ + testLinkedListLikeStlIterators<Container>(); + + Container fake; + typename Container::value_type t; + fake << t; + + typename Container::iterator i1 = fake.begin(), i2 = i1 + 1; + typename Container::const_iterator c1 = i1, c2 = c1 + 1; + + QVERIFY(i1 < i2); + QVERIFY(i1 < c2); + QVERIFY(c1 < i2); + QVERIFY(c1 < c2); + QVERIFY(!(i2 < i1)); + QVERIFY(!(i2 < c1)); + QVERIFY(!(c2 < i1)); + QVERIFY(!(c2 < c1)); + QVERIFY(!(i1 < i1)); + QVERIFY(!(i1 < c1)); + QVERIFY(!(c1 < i1)); + QVERIFY(!(c1 < c1)); + QVERIFY(!(i2 < i2)); + QVERIFY(!(i2 < c2)); + QVERIFY(!(c2 < i2)); + QVERIFY(!(c2 < c2)); + + QVERIFY(i2 > i1); + QVERIFY(i2 > c1); + QVERIFY(c2 > i1); + QVERIFY(c2 > c1); + QVERIFY(!(i1 > i2)); + QVERIFY(!(i1 > c2)); + QVERIFY(!(c1 > i2)); + QVERIFY(!(c1 > c2)); + QVERIFY(!(i1 > i1)); + QVERIFY(!(i1 > c1)); + QVERIFY(!(c1 > i1)); + QVERIFY(!(c1 > c1)); + QVERIFY(!(i2 > i2)); + QVERIFY(!(i2 > c2)); + QVERIFY(!(c2 > i2)); + QVERIFY(!(c2 > c2)); + + QVERIFY(!(i1 >= i2)); + QVERIFY(!(i1 >= c2)); + QVERIFY(!(c1 >= i2)); + QVERIFY(!(c1 >= c2)); + QVERIFY(i2 >= i1); + QVERIFY(i2 >= c1); + QVERIFY(c2 >= i1); + QVERIFY(c2 >= c1); + QVERIFY(i1 >= i1); + QVERIFY(i1 >= c1); + QVERIFY(c1 >= i1); + QVERIFY(c1 >= c1); + QVERIFY(i2 >= i2); + QVERIFY(i2 >= c2); + QVERIFY(c2 >= i2); + QVERIFY(c2 >= c2); + + QVERIFY(!(i2 <= i1)); + QVERIFY(!(i2 <= c1)); + QVERIFY(!(c2 <= i1)); + QVERIFY(!(c2 <= c1)); + QVERIFY(i1 <= i2); + QVERIFY(i1 <= c2); + QVERIFY(c1 <= i2); + QVERIFY(c1 <= c2); + QVERIFY(i1 <= i1); + QVERIFY(i1 <= c1); + QVERIFY(c1 <= i1); + QVERIFY(c1 <= c1); + QVERIFY(i2 <= i2); + QVERIFY(i2 <= c2); + QVERIFY(c2 <= i2); + QVERIFY(c2 <= c2); +} + +template <class Container> +void testMapLikeStlIterators() +{ + Container fake; + QString k; + QString t; + fake.insert(k, t); + + typename Container::iterator i1 = fake.begin(), i2 = i1 + 1; + typename Container::const_iterator c1 = i1, c2 = c1 + 1; + + QVERIFY(i1 == i1); + QVERIFY(i1 == c1); + QVERIFY(c1 == i1); + QVERIFY(c1 == c1); + QVERIFY(i2 == i2); + QVERIFY(i2 == c2); + QVERIFY(c2 == i2); + QVERIFY(c2 == c2); + + QVERIFY(i1 != i2); + QVERIFY(i1 != c2); + QVERIFY(c1 != i2); + QVERIFY(c1 != c2); + QVERIFY(i2 != i1); + QVERIFY(i2 != c1); + QVERIFY(c2 != i1); + QVERIFY(c2 != c1); +} + +void tst_Collections::constAndNonConstStlIterators() +{ + testListLikeStlIterators<QList<int> >(); + testListLikeStlIterators<QStringList >(); + testLinkedListLikeStlIterators<QLinkedList<int> >(); + testListLikeStlIterators<QVector<int> >(); + testMapLikeStlIterators<QMap<QString, QString> >(); + testMapLikeStlIterators<QMultiMap<QString, QString> >(); + testMapLikeStlIterators<QHash<QString, QString> >(); + testMapLikeStlIterators<QMultiHash<QString, QString> >(); +} + +#ifndef QT_NO_STL +void tst_Collections::vector_stl_data() +{ + QTest::addColumn<QStringList>("elements"); + + QTest::newRow("empty") << QStringList(); + QTest::newRow("one") << (QStringList() << "Hei"); + QTest::newRow("two") << (QStringList() << "Hei" << "Hopp"); + QTest::newRow("three") << (QStringList() << "Hei" << "Hopp" << "Sann"); +} + +void tst_Collections::vector_stl() +{ + QFETCH(QStringList, elements); + + QVector<QString> vector; + for (int i = 0; i < elements.count(); ++i) + vector << elements.at(i); + + std::vector<QString> stdVector = vector.toStdVector(); + + QCOMPARE(int(stdVector.size()), elements.size()); + + std::vector<QString>::const_iterator it = stdVector.begin(); + for (uint j = 0; j < stdVector.size() && it != stdVector.end(); ++j, ++it) + QCOMPARE(*it, vector[j]); + + QCOMPARE(QVector<QString>::fromStdVector(stdVector), vector); +} + +void tst_Collections::linkedlist_stl_data() +{ + list_stl_data(); +} + +void tst_Collections::linkedlist_stl() +{ + QFETCH(QStringList, elements); + + QLinkedList<QString> list; + for (int i = 0; i < elements.count(); ++i) + list << elements.at(i); + + std::list<QString> stdList = list.toStdList(); + + QCOMPARE(int(stdList.size()), elements.size()); + + std::list<QString>::const_iterator it = stdList.begin(); + QLinkedList<QString>::const_iterator it2 = list.constBegin(); + for (uint j = 0; j < stdList.size(); ++j, ++it, ++it2) + QCOMPARE(*it, *it2); + + QCOMPARE(QLinkedList<QString>::fromStdList(stdList), list); +} + +void tst_Collections::list_stl_data() +{ + QTest::addColumn<QStringList>("elements"); + + QTest::newRow("empty") << QStringList(); + QTest::newRow("one") << (QStringList() << "Hei"); + QTest::newRow("two") << (QStringList() << "Hei" << "Hopp"); + QTest::newRow("three") << (QStringList() << "Hei" << "Hopp" << "Sann"); +} + +void tst_Collections::list_stl() +{ + QFETCH(QStringList, elements); + + QList<QString> list; + for (int i = 0; i < elements.count(); ++i) + list << elements.at(i); + + std::list<QString> stdList = list.toStdList(); + + QCOMPARE(int(stdList.size()), elements.size()); + + std::list<QString>::const_iterator it = stdList.begin(); + for (uint j = 0; j < stdList.size() && it != stdList.end(); ++j, ++it) + QCOMPARE(*it, list[j]); + + QCOMPARE(QList<QString>::fromStdList(stdList), list); +} +#endif + +template <typename T> +T qtInit(T * = 0) +{ + return T(); +} + +void tst_Collections::q_init() +{ + QCOMPARE(qtInit<int>(), 0); + QCOMPARE(qtInit<double>(), 0.0); + QCOMPARE(qtInit<QString>(), QString()); + QCOMPARE(qtInit<int *>(), static_cast<int *>(0)); + QCOMPARE(qtInit<double *>(), static_cast<double *>(0)); + QCOMPARE(qtInit<QString *>(), static_cast<QString *>(0)); + QCOMPARE(qtInit<Pod>().i1, 0); + QCOMPARE(qtInit<Pod>().i2, 0); +} + +void tst_Collections::pointersize() +{ + QCOMPARE(int(sizeof(void *)), QT_POINTER_SIZE); +} + +class LessThanComparable +{ +public: + bool operator<(const LessThanComparable &) const { return true; } +}; + +class EqualsComparable +{ +public: + bool operator==(const EqualsComparable &) const { return true; } +}; + +uint qHash(const EqualsComparable &) +{ + return 0; +} + +/* + The following functions instatiates every member functions in the + Qt containers that requires either operator== or operator<. + They are ordered in a concept inheritance tree: + + Container + MutableIterationContainer + Sequence (QLinkedList) + Random Access (QVector, QList, QQueue, QStack) + Pair Associative (QHash, QMap) + Associative (QSet) +*/ +template <typename ContainerType, typename ValueType> +void instantiateContainer() +{ + const ValueType value = ValueType(); + ContainerType container; + const ContainerType constContainer(container); + +#ifndef QT_NO_STL + typename ContainerType::const_iterator constIt; + constIt = constContainer.begin(); + container.constBegin(); + + constIt = constContainer.end(); + container.constEnd(); +#endif + container.clear(); + container.contains(value); + container.count(); + container.empty(); + container.isEmpty(); + container.size(); + + container != constContainer; + container == constContainer; + container = constContainer; +} + +template <typename ContainerType, typename ValueType> +void instantiateMutableIterationContainer() +{ + instantiateContainer<ContainerType, ValueType>(); + ContainerType container; + +#ifndef QT_NO_STL + typename ContainerType::iterator it; + it = container.begin(); + it = container.end(); +#endif + + // QSet lacks count(T). + const ValueType value = ValueType(); + container.count(value); +} + +template <typename ContainerType, typename ValueType> +void instantiateSequence() +{ + instantiateMutableIterationContainer<ContainerType, ValueType>(); + +// QVector lacks removeAll(T) +// ValueType value = ValueType(); +// ContainerType container; +// container.removeAll(value); +} + +template <typename ContainerType, typename ValueType> +void instantiateRandomAccess() +{ + instantiateSequence<ContainerType, ValueType>(); + + ValueType value = ValueType(); + ContainerType container; + container.indexOf(value); + container.lastIndexOf(value); +} + +template <typename ContainerType, typename ValueType> +void instantiateAssociative() +{ + instantiateContainer<ContainerType, ValueType>(); + + const ValueType value = ValueType(); + ContainerType container; + const ContainerType constContainer(container); + + container.reserve(1); + container.capacity(); + container.squeeze(); + + container.remove(value); + container.values(); + + container.unite(constContainer); + container.intersect(constContainer); + container.subtract(constContainer); + + container != constContainer; + container == constContainer; + container & constContainer; + container &= constContainer; + container &= value; + container + constContainer; + container += constContainer; + container += value; + container - constContainer; + container -= constContainer; + container -= value; + container | constContainer; + container |= constContainer; + container |= value; +} + +template <typename ContainerType, typename KeyType, typename ValueType> +void instantiatePairAssociative() +{ + instantiateMutableIterationContainer<ContainerType, KeyType>(); + + typename ContainerType::iterator it; + typename ContainerType::const_iterator constIt; + const KeyType key = KeyType(); + const ValueType value = ValueType(); + ContainerType container; + const ContainerType constContainer(container); + + it = container.insert(key, value); + container.erase(it); + container.find(key); + container.constFind(key); + constContainer.find(key); + + container.key(value); + container.keys(); + constContainer.keys(); + container.remove(key); + container.take(key); + container.unite(constContainer); + container.value(key); + container.value(key, value); + container.values(); + container.values(key); + container[key]; + const int foo = constContainer[key]; + Q_UNUSED(foo); +} + +/* + Instantiate all Qt containers using a datatype that + defines the minimum amount of operators. +*/ +void tst_Collections::containerInstantiation() +{ + // Instantiate QHash member functions. + typedef QHash<EqualsComparable, int> Hash; + instantiatePairAssociative<Hash, EqualsComparable, int>(); + + Hash hash; + hash.reserve(1); + hash.capacity(); + hash.squeeze(); + + // Instantiate QMap member functions. + typedef QMap<LessThanComparable, int> Map; + instantiatePairAssociative<Map, LessThanComparable, int>(); + + // Instantiate QSet member functions. + typedef QSet<EqualsComparable> Set; + instantiateAssociative<Set, EqualsComparable>(); + + //Instantiate QLinkedList member functions. + typedef QLinkedList<EqualsComparable> LinkedList; + instantiateSequence<LinkedList, EqualsComparable> (); + { + EqualsComparable value; + LinkedList list; + list.removeAll(value); + } + + //Instantiate QList member functions. + typedef QList<EqualsComparable> List; + instantiateRandomAccess<List, EqualsComparable>(); + { + EqualsComparable value; + List list; + list.removeAll(value); + } + + //Instantiate QVector member functions. + typedef QVector<EqualsComparable> Vector; + instantiateRandomAccess<Vector, EqualsComparable>(); + + //Instantiate QQueue member functions. + typedef QQueue<EqualsComparable> Queue; + instantiateRandomAccess<Queue, EqualsComparable>(); + + //Instantiate QStack member functions. + typedef QStack<EqualsComparable> Stack; + instantiateRandomAccess<Stack, EqualsComparable>(); +} + +void tst_Collections::qtimerList() +{ + QList<double> foo; + const int N = 10000; + + foo.append(99.9); + foo.append(99.9); + foo.append(99.9); + + for(int i = 0; i < N; i++) { + foo.removeFirst(); + foo.insert(1, 99.9); + } + + QList<double>::Iterator end = foo.end(); + for (int i = 0; i < (N / 2) - 10; ++i) { + foo.prepend(99.9); + if (foo.end() != end) + return; + } + QFAIL("QList preallocates too much memory"); +} + +template <typename Container> +void testContainerTypedefs(Container container) +{ + Q_UNUSED(container) + { typedef typename Container::value_type Foo; } + { typedef typename Container::iterator Foo; } + { typedef typename Container::const_iterator Foo; } + { typedef typename Container::reference Foo; } + { typedef typename Container::const_reference Foo; } + { typedef typename Container::pointer Foo; } + { typedef typename Container::difference_type Foo; } + { typedef typename Container::size_type Foo; } +} + +template <typename Container> +void testPairAssociativeContainerTypedefs(Container container) +{ + Q_UNUSED(container) + +// TODO: Not sure how to define value_type for our associative containers +// { typedef typename Container::value_type Foo; } +// { typedef typename Container::const_iterator Foo; } +// { typedef typename Container::reference Foo; } +// { typedef typename Container::const_reference Foo; } +// { typedef typename Container::pointer Foo; } + + { typedef typename Container::difference_type Foo; } + { typedef typename Container::size_type Foo; } + { typedef typename Container::iterator Foo; } + { typedef typename Container::key_type Foo; } + { typedef typename Container::mapped_type Foo; } +// TODO +// { typedef typename Container::key_compare Foo; } +// { typedef typename Container::value_comare Foo; } +} + +template <typename Container> +void testSetContainerTypedefs(Container container) +{ + Q_UNUSED(container) + { typedef typename Container::iterator Foo; } + { typedef typename Container::const_iterator Foo; } + { typedef typename Container::reference Foo; } + { typedef typename Container::const_reference Foo; } + { typedef typename Container::pointer Foo; } + { typedef typename Container::difference_type Foo; } + { typedef typename Container::size_type Foo; } + { typedef typename Container::key_type Foo; } +} + +/* + Compile-time test that verifies that the Qt containers + have STL-compatable typedefs. +*/ +void tst_Collections::containerTypedefs() +{ + testContainerTypedefs(QVector<int>()); + testContainerTypedefs(QStack<int>()); + testContainerTypedefs(QList<int>()); + testContainerTypedefs(QLinkedList<int>()); + testContainerTypedefs(QQueue<int>()); + + testPairAssociativeContainerTypedefs(QMap<int, int>()); + testPairAssociativeContainerTypedefs(QMultiMap<int, int>()); + testPairAssociativeContainerTypedefs(QHash<int, int>()); + + testSetContainerTypedefs(QSet<int>()); +} + +class Key1; +class T1; +class T2; + +void tst_Collections::forwardDeclared() +{ + { typedef QHash<Key1, T1> C; C *x = 0; C::iterator i; C::const_iterator j; Q_UNUSED(x) } + { typedef QMultiHash<Key1, T1> C; C *x = 0; C::iterator i; C::const_iterator j; Q_UNUSED(x) } + { typedef QMap<Key1, T1> C; C *x = 0; C::iterator i; C::const_iterator j; Q_UNUSED(x) } + { typedef QMultiMap<Key1, T1> C; C *x = 0; C::iterator i; C::const_iterator j; Q_UNUSED(x) } + { typedef QPair<T1, T2> C; C *x = 0; Q_UNUSED(x) } + { typedef QList<T1> C; C *x = 0; C::iterator i; C::const_iterator j; Q_UNUSED(x) } + { typedef QLinkedList<T1> C; C *x = 0; C::iterator i; C::const_iterator j; Q_UNUSED(x) } + { typedef QVector<T1> C; C *x = 0; C::iterator i; C::const_iterator j; Q_UNUSED(x) Q_UNUSED(i) Q_UNUSED(j) } + { typedef QStack<T1> C; C *x = 0; C::iterator i; C::const_iterator j; Q_UNUSED(x) Q_UNUSED(i) Q_UNUSED(j) } + { typedef QQueue<T1> C; C *x = 0; C::iterator i; C::const_iterator j; Q_UNUSED(x) } + { typedef QSet<T1> C; C *x = 0; /* C::iterator i; */ C::const_iterator j; Q_UNUSED(x) } +} + +#if defined(Q_ALIGNOF) && defined(Q_DECL_ALIGN) + +class Q_DECL_ALIGN(4) Aligned4 +{ + char i; +public: + Aligned4(int i = 0) : i(i) {} + bool checkAligned() const + { + return (quintptr(this) & 3) == 0; + } + + inline bool operator==(const Aligned4 &other) const { return i == other.i; } + inline bool operator<(const Aligned4 &other) const { return i < other.i; } + friend inline int qHash(const Aligned4 &a) { return qHash(a.i); } +}; + +class Q_DECL_ALIGN(128) Aligned128 +{ + char i; +public: + Aligned128(int i = 0) : i(i) {} + bool checkAligned() const + { + return (quintptr(this) & 127) == 0; + } + + inline bool operator==(const Aligned128 &other) const { return i == other.i; } + inline bool operator<(const Aligned128 &other) const { return i < other.i; } + friend inline int qHash(const Aligned128 &a) { return qHash(a.i); } +}; + +template<typename C> +void testVectorAlignment() +{ + typedef typename C::value_type Aligned; + C container; + container.append(Aligned()); + QVERIFY(container[0].checkAligned()); + + for (int i = 0; i < 200; ++i) + container.append(Aligned()); + + for (int i = 0; i < container.size(); ++i) + QVERIFY(container.at(i).checkAligned()); +} + +template<typename C> +void testContiguousCacheAlignment() +{ + typedef typename C::value_type Aligned; + C container(150); + container.append(Aligned()); + QVERIFY(container[container.firstIndex()].checkAligned()); + + for (int i = 0; i < 200; ++i) + container.append(Aligned()); + + for (int i = container.firstIndex(); i < container.lastIndex(); ++i) + QVERIFY(container.at(i).checkAligned()); +} + +template<typename C> +void testAssociativeContainerAlignment() +{ + typedef typename C::key_type Key; + typedef typename C::mapped_type Value; + C container; + container.insert(Key(), Value()); + + typename C::const_iterator it = container.constBegin(); + QVERIFY(it.key().checkAligned()); + QVERIFY(it.value().checkAligned()); + + // add some more elements + for (int i = 0; i < 200; ++i) + container.insert(Key(i), Value(i)); + + it = container.constBegin(); + for ( ; it != container.constEnd(); ++it) { + QVERIFY(it.key().checkAligned()); + QVERIFY(it.value().checkAligned()); + } +} + +void tst_Collections::alignment() +{ + testVectorAlignment<QVector<Aligned4> >(); + testVectorAlignment<QVector<Aligned128> >(); + testContiguousCacheAlignment<QContiguousCache<Aligned4> >(); + testContiguousCacheAlignment<QContiguousCache<Aligned128> >(); + testAssociativeContainerAlignment<QMap<Aligned4, Aligned4> >(); + testAssociativeContainerAlignment<QMap<Aligned4, Aligned128> >(); + testAssociativeContainerAlignment<QMap<Aligned128, Aligned4> >(); + testAssociativeContainerAlignment<QMap<Aligned128, Aligned128> >(); + testAssociativeContainerAlignment<QHash<Aligned4, Aligned4> >(); + testAssociativeContainerAlignment<QHash<Aligned4, Aligned128> >(); + testAssociativeContainerAlignment<QHash<Aligned128, Aligned4> >(); + testAssociativeContainerAlignment<QHash<Aligned128, Aligned128> >(); +} +#endif + +#ifndef QT_NO_TEMPLATE_TEMPLATE_PARAMETERS + +template<template<class> class C> +struct QTBUG13079_Node { + C<QTBUG13079_Node> children; + QString s; + + ~QTBUG13079_Node() { + children.begin(); //play with memory + } +}; +template<template<class> class C> void QTBUG13079_collectionInsideCollectionImpl() +{ + C<QTBUG13079_Node<C> > nodeList; + nodeList << QTBUG13079_Node<C>(); + nodeList.first().s = "parent"; + nodeList.first().children << QTBUG13079_Node<C>(); + nodeList.first().children.first().s = "child"; + + nodeList = nodeList.first().children; + QCOMPARE(nodeList.first().s, QString::fromLatin1("child")); + + nodeList = nodeList.first().children; + QCOMPARE(nodeList.count(), 0); + nodeList << QTBUG13079_Node<C>(); +} + +template<template<class, class> class C> +struct QTBUG13079_NodeAssoc { + C<int, QTBUG13079_NodeAssoc> children; + QString s; + + ~QTBUG13079_NodeAssoc() { + children.begin(); //play with memory + } +}; +template<template<class, class> class C> void QTBUG13079_collectionInsideCollectionAssocImpl() +{ + C<int, QTBUG13079_NodeAssoc<C> > nodeMap; + nodeMap[18] = QTBUG13079_NodeAssoc<C>(); + nodeMap[18].s = "parent"; + nodeMap[18].children[12] = QTBUG13079_NodeAssoc<C>(); + nodeMap[18].children[12].s = "child"; + + nodeMap = nodeMap[18].children; + QCOMPARE(nodeMap[12].s, QString::fromLatin1("child")); + + nodeMap = nodeMap[12].children; + QCOMPARE(nodeMap.count(), 0); + nodeMap[42] = QTBUG13079_NodeAssoc<C>(); +} + + +quint32 qHash(const QTBUG13079_Node<QSet> &) +{ + return 0; +} + +bool operator==(const QTBUG13079_Node<QSet> &a, const QTBUG13079_Node<QSet> &b) +{ + return a.s == b.s && a.children == b.children; +} + +template<template<class> class C> +struct QTBUG13079_NodePtr : QSharedData { + C<QTBUG13079_NodePtr> child; + QTBUG13079_NodePtr *next; + QString s; + + QTBUG13079_NodePtr() : next(0) {} + ~QTBUG13079_NodePtr() { + next = child.data(); //play with memory + } +}; +template<template<class> class C> void QTBUG13079_collectionInsidePtrImpl() +{ + typedef C<QTBUG13079_NodePtr<C> > Ptr; + { + Ptr nodePtr; + nodePtr = Ptr(new QTBUG13079_NodePtr<C>()); + nodePtr->s = "parent"; + nodePtr->child = Ptr(new QTBUG13079_NodePtr<C>()); + nodePtr->child->s = "child"; + nodePtr = nodePtr->child; + QCOMPARE(nodePtr->s, QString::fromLatin1("child")); + nodePtr = nodePtr->child; + QVERIFY(!nodePtr); + } + { + Ptr nodePtr; + nodePtr = Ptr(new QTBUG13079_NodePtr<C>()); + nodePtr->s = "parent"; + nodePtr->next = new QTBUG13079_NodePtr<C>(); + nodePtr->next->s = "next"; + nodePtr = Ptr(nodePtr->next); + QCOMPARE(nodePtr->s, QString::fromLatin1("next")); + nodePtr = Ptr(nodePtr->next); + QVERIFY(!nodePtr); + } +} + +#endif + +void tst_Collections::QTBUG13079_collectionInsideCollection() +{ +#ifndef QT_NO_TEMPLATE_TEMPLATE_PARAMETERS + QTBUG13079_collectionInsideCollectionImpl<QVector>(); + QTBUG13079_collectionInsideCollectionImpl<QStack>(); + QTBUG13079_collectionInsideCollectionImpl<QList>(); + QTBUG13079_collectionInsideCollectionImpl<QLinkedList>(); + QTBUG13079_collectionInsideCollectionImpl<QQueue>(); + + { + QSet<QTBUG13079_Node<QSet> > nodeSet; + nodeSet << QTBUG13079_Node<QSet>(); + nodeSet = nodeSet.begin()->children; + QCOMPARE(nodeSet.count(), 0); + } + + QTBUG13079_collectionInsideCollectionAssocImpl<QMap>(); + QTBUG13079_collectionInsideCollectionAssocImpl<QHash>(); + + QTBUG13079_collectionInsidePtrImpl<QSharedPointer>(); + QTBUG13079_collectionInsidePtrImpl<QExplicitlySharedDataPointer>(); + QTBUG13079_collectionInsidePtrImpl<QSharedDataPointer>(); +#endif +} + +template<class Container> void foreach_test_arrays(const Container &container) +{ + typedef typename Container::value_type T; + int i = 0; + QSet <T> set; + foreach(const T & val, container) { + QVERIFY( val == container[i] ); + set << val; + i++; + } + QCOMPARE(set.count(), container.count()); + + //modify the container while iterating. + Container c2 = container; + Container c3; + i = 0; + foreach (T val, c2) { + c3 << val; + c2.insert((i * 89) % c2.size(), T() ); + QVERIFY( val == container.at(i) ); + val = T(); + i++; + } + QVERIFY(c3 == container); +} + + +void tst_Collections::foreach_2() +{ + QStringList strlist = QString::fromLatin1("a,b,c,d,e,f,g,h,ih,kl,mn,op,qr,st,uvw,xyz").split(","); + foreach_test_arrays(strlist); + foreach_test_arrays(QList<QString>(strlist)); + foreach_test_arrays(strlist.toVector()); + + QList<int> intlist; + intlist << 1 << 2 << 3 << 4 <<5 << 6 << 7 << 8 << 9; + foreach_test_arrays(intlist); + foreach_test_arrays(intlist.toVector()); + + QVarLengthArray<int> varl1; + QVarLengthArray<int, 3> varl2; + QVarLengthArray<int, 10> varl3; + foreach(int i, intlist) { + varl1 << i; + varl2 << i; + varl3 << i; + } + QCOMPARE(varl1.count(), intlist.count()); + QCOMPARE(varl2.count(), intlist.count()); + QCOMPARE(varl3.count(), intlist.count()); + foreach_test_arrays(varl1); + foreach_test_arrays(varl2); + foreach_test_arrays(varl3); + + QVarLengthArray<QString> varl4; + QVarLengthArray<QString, 3> varl5; + QVarLengthArray<QString, 18> varl6; + foreach(const QString &str, strlist) { + varl4 << str; + varl5 << str; + varl6 << str; + } + QCOMPARE(varl4.count(), strlist.count()); + QCOMPARE(varl5.count(), strlist.count()); + QCOMPARE(varl6.count(), strlist.count()); + foreach_test_arrays(varl4); + foreach_test_arrays(varl5); + foreach_test_arrays(varl6); +} + +struct IntOrString +{ + int val; + IntOrString(int v) : val(v) { } + IntOrString(const QString &v) : val(v.toInt()) { } + operator int() { return val; } + operator QString() { return QString::number(val); } +#ifndef QT_NO_STL + operator std::string() { return QString::number(val).toStdString(); } + IntOrString(const std::string &v) : val(QString::fromStdString(v).toInt()) { } +#endif +}; + +template<class Container> void insert_remove_loop_impl() +{ + typedef typename Container::value_type T; + Container t; + t.append(T(IntOrString(1))); + t << (T(IntOrString(2))); + t += (T(IntOrString(3))); + t.prepend(T(IntOrString(4))); + t.insert(2, 3 , T(IntOrString(5))); + t.insert(4, T(IntOrString(6))); + t.insert(t.begin() + 2, T(IntOrString(7))); + t.insert(t.begin() + 5, 3, T(IntOrString(8))); + int expect1[] = { 4 , 1 , 7, 5 , 5 , 8, 8, 8, 6, 5, 2 , 3 }; + QCOMPARE(size_t(t.count()), sizeof(expect1)/sizeof(int)); + for (int i = 0; i < t.count(); i++) { + QCOMPARE(t[i], T(IntOrString(expect1[i]))); + } + + Container compare_test1 = t; + t.replace(5, T(IntOrString(9))); + Container compare_test2 = t; + QVERIFY(!(compare_test1 == t)); + QVERIFY( (compare_test1 != t)); + QVERIFY( (compare_test2 == t)); + QVERIFY(!(compare_test2 != t)); + t.remove(7); + t.remove(2, 3); + int expect2[] = { 4 , 1 , 9, 8, 6, 5, 2 , 3 }; + QCOMPARE(size_t(t.count()), sizeof(expect2)/sizeof(int)); + for (int i = 0; i < t.count(); i++) { + QCOMPARE(t[i], T(IntOrString(expect2[i]))); + } + + for (typename Container::iterator it = t.begin(); it != t.end(); ) { + if ( int(IntOrString(*it)) % 2 ) + ++it; + else + it = t.erase(it); + } + + int expect3[] = { 1 , 9, 5, 3 }; + QCOMPARE(size_t(t.count()), sizeof(expect3)/sizeof(int)); + for (int i = 0; i < t.count(); i++) { + QCOMPARE(t[i], T(IntOrString(expect3[i]))); + } + + t.erase(t.begin() + 1, t.end() - 1); + + int expect4[] = { 1 , 3 }; + QCOMPARE(size_t(t.count()), sizeof(expect4)/sizeof(int)); + for (int i = 0; i < t.count(); i++) { + QCOMPARE(t[i], T(IntOrString(expect4[i]))); + } + + t << T(IntOrString(10)) << T(IntOrString(11)) << T(IntOrString(12)) << T(IntOrString(13)); + t << T(IntOrString(14)) << T(IntOrString(15)) << T(IntOrString(16)) << T(IntOrString(17)); + t << T(IntOrString(18)) << T(IntOrString(19)) << T(IntOrString(20)) << T(IntOrString(21)); + for (typename Container::iterator it = t.begin(); it != t.end(); ++it) { + int iv = int(IntOrString(*it)); + if ( iv % 2 ) { + it = t.insert(it, T(IntOrString(iv * iv))); + it = t.insert(it + 2, T(IntOrString(iv * iv + 1))); + } + } + + int expect5[] = { 1, 1, 2, 3*3, 3, 3*3+1, 10, 11*11, 11, 11*11+1, 12 , 13*13, 13, 13*13+1, 14, + 15*15, 15, 15*15+1, 16 , 17*17, 17, 17*17+1 ,18 , 19*19, 19, 19*19+1, 20, 21*21, 21, 21*21+1 }; + QCOMPARE(size_t(t.count()), sizeof(expect5)/sizeof(int)); + for (int i = 0; i < t.count(); i++) { + QCOMPARE(t[i], T(IntOrString(expect5[i]))); + } +} + + +//Add insert(int, int, T) so it has the same interface as QVector and QVarLengthArray for the test. +template<typename T> +struct ExtList : QList<T> { + using QList<T>::insert; + void insert(int before, int n, const T&x) { + while (n--) { + this->insert(before, x ); + } + } + void insert(typename QList<T>::iterator before, int n, const T&x) { + while (n--) { + before = this->insert(before, x); + } + } + + void remove(int i) { + this->removeAt(i); + } + void remove(int i, int n) { + while (n--) { + this->removeAt(i); + } + } +}; + +void tst_Collections::insert_remove_loop() +{ + insert_remove_loop_impl<ExtList<int> >(); + insert_remove_loop_impl<ExtList<QString> >(); + insert_remove_loop_impl<QVector<int> >(); + insert_remove_loop_impl<QVector<QString> >(); + insert_remove_loop_impl<QVarLengthArray<int> >(); + insert_remove_loop_impl<QVarLengthArray<QString> >(); + insert_remove_loop_impl<QVarLengthArray<int, 10> >(); + insert_remove_loop_impl<QVarLengthArray<QString, 10> >(); + insert_remove_loop_impl<QVarLengthArray<int, 3> >(); + insert_remove_loop_impl<QVarLengthArray<QString, 3> >(); + insert_remove_loop_impl<QVarLengthArray<int, 15> >(); + insert_remove_loop_impl<QVarLengthArray<QString, 15> >(); + +#ifndef QT_NO_STL + insert_remove_loop_impl<ExtList<std::string> >(); + insert_remove_loop_impl<QVector<std::string> >(); + insert_remove_loop_impl<QVarLengthArray<std::string> >(); + insert_remove_loop_impl<QVarLengthArray<std::string, 10> >(); + insert_remove_loop_impl<QVarLengthArray<std::string, 3> >(); + insert_remove_loop_impl<QVarLengthArray<std::string, 15> >(); +#endif +} + + + +QTEST_APPLESS_MAIN(tst_Collections) +#include "tst_collections.moc" diff --git a/tests/auto/integrationtests/exceptionsafety/.gitignore b/tests/auto/integrationtests/exceptionsafety/.gitignore new file mode 100644 index 0000000000..9a854c4c26 --- /dev/null +++ b/tests/auto/integrationtests/exceptionsafety/.gitignore @@ -0,0 +1 @@ +tst_exceptionsafety diff --git a/tests/auto/integrationtests/exceptionsafety/exceptionsafety.pro b/tests/auto/integrationtests/exceptionsafety/exceptionsafety.pro new file mode 100644 index 0000000000..52ba9e27bd --- /dev/null +++ b/tests/auto/integrationtests/exceptionsafety/exceptionsafety.pro @@ -0,0 +1,4 @@ +load(qttest_p4) +SOURCES += tst_exceptionsafety.cpp +QT = core +CONFIG += parallel_test diff --git a/tests/auto/integrationtests/exceptionsafety/tst_exceptionsafety.cpp b/tests/auto/integrationtests/exceptionsafety/tst_exceptionsafety.cpp new file mode 100644 index 0000000000..27db9ea7f7 --- /dev/null +++ b/tests/auto/integrationtests/exceptionsafety/tst_exceptionsafety.cpp @@ -0,0 +1,734 @@ +/**************************************************************************** +** +** 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> + +QT_USE_NAMESPACE + +class tst_ExceptionSafety: public QObject +{ + Q_OBJECT +private slots: +#ifdef QT_NO_EXCEPTIONS + void initTestCase(); +#else + void exceptionInSlot(); + void exceptionVector(); + void exceptionHash(); + void exceptionMap(); + void exceptionList(); + void exceptionLinkedList(); +// void exceptionEventLoop(); +// void exceptionSignalSlot(); +#endif +}; + +#ifdef QT_NO_EXCEPTIONS +void tst_ExceptionSafety::initTestCase() +{ + QSKIP("This test requires exception support", SkipAll); +} + +#else + +class Emitter : public QObject +{ + Q_OBJECT +public: + inline void emitTestSignal() { emit testSignal(); } +signals: + void testSignal(); +}; + +class ExceptionThrower : public QObject +{ + Q_OBJECT +public slots: + void thrower() { throw 5; } +}; + +class Receiver : public QObject +{ + Q_OBJECT +public: + Receiver() + : received(0) {} + int received; + +public slots: + void receiver() { ++received; } +}; + +enum ThrowType { ThrowNot = 0, ThrowAtCreate = 1, ThrowAtCopy = 2, ThrowLater = 3, ThrowAtComparison = 4 }; + +ThrowType throwType = ThrowNot; // global flag to indicate when an exception should be throw. Will be reset when the exception has been generated. + +int objCounter = 0; + +/*! Class that does not throw any exceptions. Used as baseclass for all the other ones. + */ +template <int T> +class FlexibleThrower +{ + public: + FlexibleThrower() : _value(-1) { + if( throwType == ThrowAtCreate ) { + throwType = ThrowNot; + throw ThrowAtCreate; + } + objCounter++; + } + + FlexibleThrower( short value ) : _value(value) { + if( throwType == ThrowAtCreate ) { + throwType = ThrowNot; + throw ThrowAtCreate; + } + objCounter++; + } + + FlexibleThrower(FlexibleThrower const& other ) { + // qDebug("cc"); + + if( throwType == ThrowAtCopy ) { + throwType = ThrowNot; + throw ThrowAtCopy; + + } else if( throwType == ThrowLater ) { + throwType = ThrowAtCopy; + } + + objCounter++; + _value = other.value(); + } + + ~FlexibleThrower() { objCounter--; } + + bool operator==(const FlexibleThrower<T> &t) const + { + // qDebug("vv == %d %d", value(), t.value()); + if( throwType == ThrowAtComparison ) { + throwType = ThrowNot; + throw ThrowAtComparison; + } + return value()==t.value(); + } + + bool operator<(const FlexibleThrower<T> &t) const + { + // qDebug("vv < %d %d", value(), t.value()); + if( throwType == ThrowAtComparison ) { + throwType = ThrowNot; + throw ThrowAtComparison; + } + return value()<t.value(); + } + + int value() const + { return (int)_value; } + + short _value; + char dummy[T]; +}; + +uint qHash(const FlexibleThrower<2>& t) +{ + // qDebug("ha"); + if( throwType == ThrowAtComparison ) { + throwType = ThrowNot; + throw ThrowAtComparison; + } + return (uint)t.value(); +} + +typedef FlexibleThrower<2> FlexibleThrowerSmall; +typedef QMap<FlexibleThrowerSmall,FlexibleThrowerSmall> MyMap; +typedef QHash<FlexibleThrowerSmall,FlexibleThrowerSmall> MyHash; + +// connect a signal to a slot that throws an exception +// run this through valgrind to make sure it doesn't corrupt +void tst_ExceptionSafety::exceptionInSlot() +{ + Emitter emitter; + ExceptionThrower thrower; + + connect(&emitter, SIGNAL(testSignal()), &thrower, SLOT(thrower())); + + try { + emitter.emitTestSignal(); + } catch (int i) { + QCOMPARE(i, 5); + } +} + +void tst_ExceptionSafety::exceptionList() { + + { + QList<FlexibleThrowerSmall> list; + QList<FlexibleThrowerSmall> list2; + QList<FlexibleThrowerSmall> list3; + + for( int i = 0; i<10; i++ ) + list.append( FlexibleThrowerSmall(i) ); + + try { + throwType = ThrowAtCopy; + list.append( FlexibleThrowerSmall(10)); + } catch (...) { + } + QCOMPARE( list.size(), 10 ); + + try { + throwType = ThrowAtCopy; + list.prepend( FlexibleThrowerSmall(10)); + } catch (...) { + } + QCOMPARE( list.at(0).value(), 0 ); + QCOMPARE( list.size(), 10 ); + + try { + throwType = ThrowAtCopy; + list.insert( 8, FlexibleThrowerSmall(10)); + } catch (...) { + } + QCOMPARE( list.at(7).value(), 7 ); + QCOMPARE( list.at(8).value(), 8 ); + QCOMPARE( list.size(), 10 ); + + try { + throwType = ThrowAtCopy; + FlexibleThrowerSmall t = list.takeAt( 6 ); + } catch (...) { + } + QCOMPARE( list.at(6).value(), 6 ); + QCOMPARE( list.at(7).value(), 7 ); + QCOMPARE( list.size(), 10 ); + + try { + throwType = ThrowAtCopy; + list3 = list; + } catch (...) { + } + QCOMPARE( list.at(0).value(), 0 ); + QCOMPARE( list.at(7).value(), 7 ); + QCOMPARE( list.size(), 10 ); + QCOMPARE( list3.at(0).value(), 0 ); + QCOMPARE( list3.at(7).value(), 7 ); + QCOMPARE( list3.size(), 10 ); + + try { + throwType = ThrowAtCopy; + list3.append( FlexibleThrowerSmall(11) ); + } catch (...) { + } + QCOMPARE( list.at(0).value(), 0 ); + QCOMPARE( list.at(7).value(), 7 ); + QCOMPARE( list.size(), 10 ); + QCOMPARE( list3.at(0).value(), 0 ); + QCOMPARE( list3.at(7).value(), 7 ); + QCOMPARE( list3.size(), 10 ); + + try { + list2.clear(); + list2.append( FlexibleThrowerSmall(11)); + throwType = ThrowAtCopy; + list3 = list+list2; + } catch (...) { + } + QCOMPARE( list.at(0).value(), 0 ); + QCOMPARE( list.at(7).value(), 7 ); + QCOMPARE( list.size(), 10 ); + + // check that copy on write works atomar + list2.clear(); + list2.append( FlexibleThrowerSmall(11)); + list3 = list+list2; + try { + throwType = ThrowAtCreate; + list3[7]=FlexibleThrowerSmall(12); + } catch (...) { + } + QCOMPARE( list.at(7).value(), 7 ); + QCOMPARE( list.size(), 10 ); + QCOMPARE( list3.at(7).value(), 7 ); + QCOMPARE( list3.size(), 11 ); + + } + QCOMPARE(objCounter, 0 ); // check that every object has been freed +} + +void tst_ExceptionSafety::exceptionLinkedList() { + + { + QLinkedList<FlexibleThrowerSmall> list; + QLinkedList<FlexibleThrowerSmall> list2; + QLinkedList<FlexibleThrowerSmall> list3; + + for( int i = 0; i<10; i++ ) + list.append( FlexibleThrowerSmall(i) ); + + try { + throwType = ThrowAtCopy; + list.append( FlexibleThrowerSmall(10)); + } catch (...) { + } + QCOMPARE( list.size(), 10 ); + + try { + throwType = ThrowAtCopy; + list.prepend( FlexibleThrowerSmall(10)); + } catch (...) { + } + QCOMPARE( list.first().value(), 0 ); + QCOMPARE( list.size(), 10 ); + + try { + throwType = ThrowAtCopy; + list3 = list; + list3.append( FlexibleThrowerSmall(11) ); + } catch (...) { + } + QCOMPARE( list.first().value(), 0 ); + QCOMPARE( list.size(), 10 ); + QCOMPARE( list3.size(), 10 ); + } + QCOMPARE(objCounter, 0 ); // check that every object has been freed +} + +void tst_ExceptionSafety::exceptionVector() { + + { + QVector<FlexibleThrowerSmall> vector; + QVector<FlexibleThrowerSmall> vector2; + QVector<FlexibleThrowerSmall> vector3; + + for (int i = 0; i<10; i++) + vector.append( FlexibleThrowerSmall(i) ); + + try { + throwType = ThrowAtCopy; + vector.append( FlexibleThrowerSmall(10)); + } catch (...) { + } + QCOMPARE( vector.size(), 10 ); + + try { + throwType = ThrowAtCopy; + vector.prepend( FlexibleThrowerSmall(10)); + } catch (...) { + } + QCOMPARE( vector.at(0).value(), 0 ); + QCOMPARE( vector.size(), 10 ); + + try { + throwType = ThrowAtCopy; + vector.insert( 8, FlexibleThrowerSmall(10)); + } catch (...) { + } + QCOMPARE( vector.at(7).value(), 7 ); + QCOMPARE( vector.at(8).value(), 8 ); + QCOMPARE( vector.size(), 10 ); + + try { + throwType = ThrowAtCopy; + vector3 = vector; + } catch (...) { + } + QCOMPARE( vector.at(0).value(), 0 ); + QCOMPARE( vector.at(7).value(), 7 ); + QCOMPARE( vector.size(), 10 ); + QCOMPARE( vector3.at(0).value(), 0 ); + QCOMPARE( vector3.at(7).value(), 7 ); + QCOMPARE( vector3.size(), 10 ); + + try { + throwType = ThrowAtCopy; + vector3.append( FlexibleThrowerSmall(11) ); + } catch (...) { + } + QCOMPARE( vector.at(0).value(), 0 ); + QCOMPARE( vector.at(7).value(), 7 ); + QCOMPARE( vector.size(), 10 ); + QCOMPARE( vector3.at(0).value(), 0 ); + QCOMPARE( vector3.at(7).value(), 7 ); + + try { + vector2.clear(); + vector2.append( FlexibleThrowerSmall(11)); + throwType = ThrowAtCopy; + vector3 = vector+vector2; + } catch (...) { + } + QCOMPARE( vector.at(0).value(), 0 ); + QCOMPARE( vector.at(7).value(), 7 ); + QCOMPARE( vector.size(), 10 ); + + // check that copy on write works atomar + vector2.clear(); + vector2.append( FlexibleThrowerSmall(11)); + vector3 = vector+vector2; + try { + throwType = ThrowAtCreate; + vector3[7]=FlexibleThrowerSmall(12); + } catch (...) { + } + QCOMPARE( vector.at(7).value(), 7 ); + QCOMPARE( vector.size(), 10 ); + QCOMPARE( vector3.at(7).value(), 7 ); + QCOMPARE( vector3.size(), 11 ); + + try { + throwType = ThrowAtCreate; + vector.resize(15); + } catch (...) { + } + QCOMPARE( vector.at(7).value(), 7 ); + QCOMPARE( vector.size(), 10 ); + + try { + throwType = ThrowAtCreate; + vector.resize(15); + } catch (...) { + } + QCOMPARE( vector.at(7).value(), 7 ); + QCOMPARE( vector.size(), 10 ); + + try { + throwType = ThrowLater; + vector.fill(FlexibleThrowerSmall(1), 15); + } catch (...) { + } + QCOMPARE( vector.at(0).value(), 0 ); + QCOMPARE( vector.size(), 10 ); + + + } + QCOMPARE(objCounter, 0 ); // check that every object has been freed +} + + +void tst_ExceptionSafety::exceptionMap() { + + { + MyMap map; + MyMap map2; + MyMap map3; + + throwType = ThrowNot; + for (int i = 0; i<10; i++) + map[ FlexibleThrowerSmall(i) ] = FlexibleThrowerSmall(i); + + return; // further test are deactivated until Map is fixed. + + for( int i = ThrowAtCopy; i<=ThrowAtComparison; i++ ) { + try { + throwType = (ThrowType)i; + map[ FlexibleThrowerSmall(10) ] = FlexibleThrowerSmall(10); + } catch(...) { + } + QCOMPARE( map.size(), 10 ); + QCOMPARE( map[ FlexibleThrowerSmall(1) ], FlexibleThrowerSmall(1) ); + } + + map2 = map; + try { + throwType = ThrowLater; + map2[ FlexibleThrowerSmall(10) ] = FlexibleThrowerSmall(10); + } catch(...) { + } + /* qDebug("%d %d", map.size(), map2.size() ); + for( int i=0; i<map.size(); i++ ) + qDebug( "Value at %d: %d",i, map.value(FlexibleThrowerSmall(i), FlexibleThrowerSmall()).value() ); + QCOMPARE( map.value(FlexibleThrowerSmall(1), FlexibleThrowerSmall()), FlexibleThrowerSmall(1) ); + qDebug( "Value at %d: %d",1, map[FlexibleThrowerSmall(1)].value() ); + qDebug("%d %d", map.size(), map2.size() ); + */ + QCOMPARE( map[ FlexibleThrowerSmall(1) ], FlexibleThrowerSmall(1) ); + QCOMPARE( map.size(), 10 ); + QCOMPARE( map2[ FlexibleThrowerSmall(1) ], FlexibleThrowerSmall(1) ); + QCOMPARE( map2.size(), 10 ); + + } + QCOMPARE(objCounter, 0 ); // check that every object has been freed +} + +void tst_ExceptionSafety::exceptionHash() { + + { + MyHash hash; + MyHash hash2; + MyHash hash3; + + for( int i = 0; i<10; i++ ) + hash[ FlexibleThrowerSmall(i) ] = FlexibleThrowerSmall(i); + + for( int i = ThrowAtCopy; i<=ThrowAtComparison; i++ ) { + try { + throwType = (ThrowType)i; + hash[ FlexibleThrowerSmall(10) ] = FlexibleThrowerSmall(10); + } catch(...) { + } + QCOMPARE( hash.size(), 10 ); + } + + hash2 = hash; + try { + throwType = ThrowLater; + hash2[ FlexibleThrowerSmall(10) ] = FlexibleThrowerSmall(10); + } catch(...) { + } + QCOMPARE( hash[ FlexibleThrowerSmall(1) ], FlexibleThrowerSmall(1) ); + QCOMPARE( hash.size(), 10 ); + QCOMPARE( hash2[ FlexibleThrowerSmall(1) ], FlexibleThrowerSmall(1) ); + QCOMPARE( hash2.size(), 10 ); + + hash2.clear(); + try { + throwType = ThrowLater; + hash2.reserve(30); + } catch(...) { + } + QCOMPARE( hash2.size(), 0 ); + + /* + try { + throwType = ThrowAtCopy; + hash.prepend( FlexibleThrowerSmall(10)); + } catch (...) { + } + QCOMPARE( hash.at(0).value(), 0 ); + QCOMPARE( hash.size(), 10 ); + + try { + throwType = ThrowAtCopy; + hash.insert( 8, FlexibleThrowerSmall(10)); + } catch (...) { + } + QCOMPARE( hash.at(7).value(), 7 ); + QCOMPARE( hash.at(8).value(), 8 ); + QCOMPARE( hash.size(), 10 ); + + qDebug("val"); + try { + throwType = ThrowAtCopy; + hash3 = hash; + } catch (...) { + } + QCOMPARE( hash.at(0).value(), 0 ); + QCOMPARE( hash.at(7).value(), 7 ); + QCOMPARE( hash.size(), 10 ); + QCOMPARE( hash3.at(0).value(), 0 ); + QCOMPARE( hash3.at(7).value(), 7 ); + QCOMPARE( hash3.size(), 10 ); + + try { + throwType = ThrowAtCopy; + hash3.append( FlexibleThrowerSmall(11) ); + } catch (...) { + } + QCOMPARE( hash.at(0).value(), 0 ); + QCOMPARE( hash.at(7).value(), 7 ); + QCOMPARE( hash.size(), 10 ); + QCOMPARE( hash3.at(0).value(), 0 ); + QCOMPARE( hash3.at(7).value(), 7 ); + QCOMPARE( hash3.at(11).value(), 11 ); + + try { + hash2.clear(); + hash2.append( FlexibleThrowerSmall(11)); + throwType = ThrowAtCopy; + hash3 = hash+hash2; + } catch (...) { + } + QCOMPARE( hash.at(0).value(), 0 ); + QCOMPARE( hash.at(7).value(), 7 ); + QCOMPARE( hash.size(), 10 ); + + // check that copy on write works atomar + hash2.clear(); + hash2.append( FlexibleThrowerSmall(11)); + hash3 = hash+hash2; + try { + throwType = ThrowAtCopy; + hash3[7]=FlexibleThrowerSmall(12); + } catch (...) { + } + QCOMPARE( hash.at(7).value(), 7 ); + QCOMPARE( hash.size(), 10 ); + QCOMPARE( hash3.at(7).value(), 7 ); + QCOMPARE( hash3.size(), 11 ); + */ + + + } + QCOMPARE(objCounter, 0 ); // check that every object has been freed +} + +// Disable these tests until the level of exception safety in event loops is clear +#if 0 +enum +{ + ThrowEventId = QEvent::User + 42, + NoThrowEventId = QEvent::User + 43 +}; + +class ThrowEvent : public QEvent +{ +public: + ThrowEvent() + : QEvent(static_cast<QEvent::Type>(ThrowEventId)) + { + } +}; + +class NoThrowEvent : public QEvent +{ +public: + NoThrowEvent() + : QEvent(static_cast<QEvent::Type>(NoThrowEventId)) + {} +}; + +struct IntEx : public std::exception +{ + IntEx(int aEx) : ex(aEx) {} + int ex; +}; + +class TestObject : public QObject +{ +public: + TestObject() + : throwEventCount(0), noThrowEventCount(0) {} + + int throwEventCount; + int noThrowEventCount; + +protected: + bool event(QEvent *event) + { + if (int(event->type()) == ThrowEventId) { + throw IntEx(++throwEventCount); + } else if (int(event->type()) == NoThrowEventId) { + ++noThrowEventCount; + } + return QObject::event(event); + } +}; + +void tst_ExceptionSafety::exceptionEventLoop() +{ + // send an event that throws + TestObject obj; + ThrowEvent throwEvent; + try { + qApp->sendEvent(&obj, &throwEvent); + } catch (IntEx code) { + QCOMPARE(code.ex, 1); + } + QCOMPARE(obj.throwEventCount, 1); + + // post an event that throws + qApp->postEvent(&obj, new ThrowEvent); + + try { + qApp->processEvents(); + } catch (IntEx code) { + QCOMPARE(code.ex, 2); + } + QCOMPARE(obj.throwEventCount, 2); + + // post a normal event, then a throwing event, then a normal event + // run this in valgrind to ensure that it doesn't leak. + + qApp->postEvent(&obj, new NoThrowEvent); + qApp->postEvent(&obj, new ThrowEvent); + qApp->postEvent(&obj, new NoThrowEvent); + + try { + qApp->processEvents(); + } catch (IntEx code) { + QCOMPARE(code.ex, 3); + } + // here, we should have received on non-throwing event and one throwing one + QCOMPARE(obj.throwEventCount, 3); + QCOMPARE(obj.noThrowEventCount, 1); + + // spin the event loop again + qApp->processEvents(); + + // now, we should have received the second non-throwing event + QCOMPARE(obj.noThrowEventCount, 2); +} + +void tst_ExceptionSafety::exceptionSignalSlot() +{ + Emitter e; + ExceptionThrower thrower; + Receiver r1; + Receiver r2; + + // connect a signal to a normal object, a thrower and a normal object again + connect(&e, SIGNAL(testSignal()), &r1, SLOT(receiver())); + connect(&e, SIGNAL(testSignal()), &thrower, SLOT(thrower())); + connect(&e, SIGNAL(testSignal()), &r2, SLOT(receiver())); + + int code = 0; + try { + e.emitTestSignal(); + } catch (int c) { + code = c; + } + + // 5 is the magic number that's thrown by thrower + QCOMPARE(code, 5); + + // assumption: slots are called in the connection order + QCOMPARE(r1.received, 1); + QCOMPARE(r2.received, 0); +} +#endif + +#endif + +QTEST_MAIN(tst_ExceptionSafety) +#include "tst_exceptionsafety.moc" diff --git a/tests/auto/integrationtests/exceptionsafety_objects/3rdparty/memcheck.h b/tests/auto/integrationtests/exceptionsafety_objects/3rdparty/memcheck.h new file mode 100644 index 0000000000..72a02ca9ba --- /dev/null +++ b/tests/auto/integrationtests/exceptionsafety_objects/3rdparty/memcheck.h @@ -0,0 +1,319 @@ + +/* + ---------------------------------------------------------------- + + Notice that the following BSD-style license applies to this one + file (memcheck.h) only. The rest of Valgrind is licensed under the + terms of the GNU General Public License, version 2, unless + otherwise indicated. See the COPYING file in the source + distribution for details. + + ---------------------------------------------------------------- + + This file is part of MemCheck, a heavyweight Valgrind tool for + detecting memory errors. + + Copyright (C) 2000-2008 Julian Seward. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + ---------------------------------------------------------------- + + Notice that the above BSD-style license applies to this one file + (memcheck.h) only. The entire rest of Valgrind is licensed under + the terms of the GNU General Public License, version 2. See the + COPYING file in the source distribution for details. + + ---------------------------------------------------------------- +*/ + + +#ifndef __MEMCHECK_H +#define __MEMCHECK_H + + +/* This file is for inclusion into client (your!) code. + + You can use these macros to manipulate and query memory permissions + inside your own programs. + + See comment near the top of valgrind.h on how to use them. +*/ + +#include "valgrind.h" + +/* !! ABIWARNING !! ABIWARNING !! ABIWARNING !! ABIWARNING !! + This enum comprises an ABI exported by Valgrind to programs + which use client requests. DO NOT CHANGE THE ORDER OF THESE + ENTRIES, NOR DELETE ANY -- add new ones at the end. */ +typedef + enum { + VG_USERREQ__MAKE_MEM_NOACCESS = VG_USERREQ_TOOL_BASE('M','C'), + VG_USERREQ__MAKE_MEM_UNDEFINED, + VG_USERREQ__MAKE_MEM_DEFINED, + VG_USERREQ__DISCARD, + VG_USERREQ__CHECK_MEM_IS_ADDRESSABLE, + VG_USERREQ__CHECK_MEM_IS_DEFINED, + VG_USERREQ__DO_LEAK_CHECK, + VG_USERREQ__COUNT_LEAKS, + + VG_USERREQ__GET_VBITS, + VG_USERREQ__SET_VBITS, + + VG_USERREQ__CREATE_BLOCK, + + VG_USERREQ__MAKE_MEM_DEFINED_IF_ADDRESSABLE, + + VG_USERREQ__ENABLE_OOM, + VG_USERREQ__GET_ALLOC_INDEX, + + /* This is just for memcheck's internal use - don't use it */ + _VG_USERREQ__MEMCHECK_RECORD_OVERLAP_ERROR + = VG_USERREQ_TOOL_BASE('M','C') + 256, + + /* This is just for memcheck's internal use - don't use it */ + _VG_USERREQ__EXCEPTION + = VG_USERREQ_TOOL_BASE('M','C') + 512, + } Vg_MemCheckClientRequest; + + + +/* Client-code macros to manipulate the state of memory. */ + +/* Mark memory at _qzz_addr as unaddressable for _qzz_len bytes. */ +#define VALGRIND_MAKE_MEM_NOACCESS(_qzz_addr,_qzz_len) \ + (__extension__({unsigned long _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0 /* default return */, \ + VG_USERREQ__MAKE_MEM_NOACCESS, \ + _qzz_addr, _qzz_len, 0, 0, 0); \ + _qzz_res; \ + })) + +/* Similarly, mark memory at _qzz_addr as addressable but undefined + for _qzz_len bytes. */ +#define VALGRIND_MAKE_MEM_UNDEFINED(_qzz_addr,_qzz_len) \ + (__extension__({unsigned long _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0 /* default return */, \ + VG_USERREQ__MAKE_MEM_UNDEFINED, \ + _qzz_addr, _qzz_len, 0, 0, 0); \ + _qzz_res; \ + })) + +/* Similarly, mark memory at _qzz_addr as addressable and defined + for _qzz_len bytes. */ +#define VALGRIND_MAKE_MEM_DEFINED(_qzz_addr,_qzz_len) \ + (__extension__({unsigned long _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0 /* default return */, \ + VG_USERREQ__MAKE_MEM_DEFINED, \ + _qzz_addr, _qzz_len, 0, 0, 0); \ + _qzz_res; \ + })) + +/* Similar to VALGRIND_MAKE_MEM_DEFINED except that addressability is + not altered: bytes which are addressable are marked as defined, + but those which are not addressable are left unchanged. */ +#define VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE(_qzz_addr,_qzz_len) \ + (__extension__({unsigned long _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0 /* default return */, \ + VG_USERREQ__MAKE_MEM_DEFINED_IF_ADDRESSABLE, \ + _qzz_addr, _qzz_len, 0, 0, 0); \ + _qzz_res; \ + })) + +/* Create a block-description handle. The description is an ascii + string which is included in any messages pertaining to addresses + within the specified memory range. Has no other effect on the + properties of the memory range. */ +#define VALGRIND_CREATE_BLOCK(_qzz_addr,_qzz_len, _qzz_desc) \ + (__extension__({unsigned long _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0 /* default return */, \ + VG_USERREQ__CREATE_BLOCK, \ + _qzz_addr, _qzz_len, _qzz_desc, \ + 0, 0); \ + _qzz_res; \ + })) + +/* Discard a block-description-handle. Returns 1 for an + invalid handle, 0 for a valid handle. */ +#define VALGRIND_DISCARD(_qzz_blkindex) \ + (__extension__ ({unsigned long _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0 /* default return */, \ + VG_USERREQ__DISCARD, \ + 0, _qzz_blkindex, 0, 0, 0); \ + _qzz_res; \ + })) + + +/* Client-code macros to check the state of memory. */ + +/* Check that memory at _qzz_addr is addressable for _qzz_len bytes. + If suitable addressibility is not established, Valgrind prints an + error message and returns the address of the first offending byte. + Otherwise it returns zero. */ +#define VALGRIND_CHECK_MEM_IS_ADDRESSABLE(_qzz_addr,_qzz_len) \ + (__extension__({unsigned long _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__CHECK_MEM_IS_ADDRESSABLE,\ + _qzz_addr, _qzz_len, 0, 0, 0); \ + _qzz_res; \ + })) + +/* Check that memory at _qzz_addr is addressable and defined for + _qzz_len bytes. If suitable addressibility and definedness are not + established, Valgrind prints an error message and returns the + address of the first offending byte. Otherwise it returns zero. */ +#define VALGRIND_CHECK_MEM_IS_DEFINED(_qzz_addr,_qzz_len) \ + (__extension__({unsigned long _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__CHECK_MEM_IS_DEFINED, \ + _qzz_addr, _qzz_len, 0, 0, 0); \ + _qzz_res; \ + })) + +/* Use this macro to force the definedness and addressibility of an + lvalue to be checked. If suitable addressibility and definedness + are not established, Valgrind prints an error message and returns + the address of the first offending byte. Otherwise it returns + zero. */ +#define VALGRIND_CHECK_VALUE_IS_DEFINED(__lvalue) \ + VALGRIND_CHECK_MEM_IS_DEFINED( \ + (volatile unsigned char *)&(__lvalue), \ + (unsigned long)(sizeof (__lvalue))) + + +/* Do a memory leak check mid-execution. */ +#define VALGRIND_DO_LEAK_CHECK \ + {unsigned long _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__DO_LEAK_CHECK, \ + 0, 0, 0, 0, 0); \ + } + +/* Just display summaries of leaked memory, rather than all the + details */ +#define VALGRIND_DO_QUICK_LEAK_CHECK \ + {unsigned long _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__DO_LEAK_CHECK, \ + 1, 0, 0, 0, 0); \ + } + +/* Return number of leaked, dubious, reachable and suppressed bytes found by + all previous leak checks. They must be lvalues. */ +#define VALGRIND_COUNT_LEAKS(leaked, dubious, reachable, suppressed) \ + /* For safety on 64-bit platforms we assign the results to private + unsigned long variables, then assign these to the lvalues the user + specified, which works no matter what type 'leaked', 'dubious', etc + are. We also initialise '_qzz_leaked', etc because + VG_USERREQ__COUNT_LEAKS doesn't mark the values returned as + initialised. */ \ + {unsigned long _qzz_res; \ + unsigned long _qzz_leaked = 0, _qzz_dubious = 0; \ + unsigned long _qzz_reachable = 0, _qzz_suppressed = 0; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__COUNT_LEAKS, \ + &_qzz_leaked, &_qzz_dubious, \ + &_qzz_reachable, &_qzz_suppressed, 0); \ + leaked = _qzz_leaked; \ + dubious = _qzz_dubious; \ + reachable = _qzz_reachable; \ + suppressed = _qzz_suppressed; \ + } + + +/* Get the validity data for addresses [zza..zza+zznbytes-1] and copy it + into the provided zzvbits array. Return values: + 0 if not running on valgrind + 1 success + 2 [previously indicated unaligned arrays; these are now allowed] + 3 if any parts of zzsrc/zzvbits are not addressable. + The metadata is not copied in cases 0, 2 or 3 so it should be + impossible to segfault your system by using this call. +*/ +#define VALGRIND_GET_VBITS(zza,zzvbits,zznbytes) \ + (__extension__({unsigned long _qzz_res; \ + char* czza = (char*)zza; \ + char* czzvbits = (char*)zzvbits; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__GET_VBITS, \ + czza, czzvbits, zznbytes, 0, 0 ); \ + _qzz_res; \ + })) + +/* Set the validity data for addresses [zza..zza+zznbytes-1], copying it + from the provided zzvbits array. Return values: + 0 if not running on valgrind + 1 success + 2 [previously indicated unaligned arrays; these are now allowed] + 3 if any parts of zza/zzvbits are not addressable. + The metadata is not copied in cases 0, 2 or 3 so it should be + impossible to segfault your system by using this call. +*/ +#define VALGRIND_SET_VBITS(zza,zzvbits,zznbytes) \ + (__extension__({unsigned int _qzz_res; \ + char* czza = (char*)zza; \ + char* czzvbits = (char*)zzvbits; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__SET_VBITS, \ + czza, czzvbits, zznbytes, 0, 0 ); \ + _qzz_res; \ + })) + +/* Enable or disable OOM simulation. */ +#define VALGRIND_ENABLE_OOM_AT_ALLOC_INDEX(index) \ + (__extension__ ({unsigned long _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0 /* default return */, \ + VG_USERREQ__ENABLE_OOM, \ + 1, index, 0, 0, 0); \ + _qzz_res; \ + })) + +#define VALGRIND_DISABLE_OOM_AT_ALLOC_INDEX(index) \ + (__extension__ ({unsigned long _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0 /* default return */, \ + VG_USERREQ__ENABLE_OOM, \ + 0, index, 0, 0, 0); \ + _qzz_res; \ + })) + +/* Get the current allocation index. */ +#define VALGRIND_GET_ALLOC_INDEX \ + (__extension__ ({unsigned long _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, -1 /* default return */, \ + VG_USERREQ__GET_ALLOC_INDEX, \ + 0, 0, 0, 0, 0); \ + _qzz_res; \ + })) + + +#endif + diff --git a/tests/auto/integrationtests/exceptionsafety_objects/3rdparty/valgrind.h b/tests/auto/integrationtests/exceptionsafety_objects/3rdparty/valgrind.h new file mode 100644 index 0000000000..577c59ab0c --- /dev/null +++ b/tests/auto/integrationtests/exceptionsafety_objects/3rdparty/valgrind.h @@ -0,0 +1,3924 @@ +/* -*- c -*- + ---------------------------------------------------------------- + + Notice that the following BSD-style license applies to this one + file (valgrind.h) only. The rest of Valgrind is licensed under the + terms of the GNU General Public License, version 2, unless + otherwise indicated. See the COPYING file in the source + distribution for details. + + ---------------------------------------------------------------- + + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2000-2008 Julian Seward. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + ---------------------------------------------------------------- + + Notice that the above BSD-style license applies to this one file + (valgrind.h) only. The entire rest of Valgrind is licensed under + the terms of the GNU General Public License, version 2. See the + COPYING file in the source distribution for details. + + ---------------------------------------------------------------- +*/ + + +/* This file is for inclusion into client (your!) code. + + You can use these macros to manipulate and query Valgrind's + execution inside your own programs. + + The resulting executables will still run without Valgrind, just a + little bit more slowly than they otherwise would, but otherwise + unchanged. When not running on valgrind, each client request + consumes very few (eg. 7) instructions, so the resulting performance + loss is negligible unless you plan to execute client requests + millions of times per second. Nevertheless, if that is still a + problem, you can compile with the NVALGRIND symbol defined (gcc + -DNVALGRIND) so that client requests are not even compiled in. */ + +#ifndef __VALGRIND_H +#define __VALGRIND_H + +#include <stdarg.h> + +/* Nb: this file might be included in a file compiled with -ansi. So + we can't use C++ style "//" comments nor the "asm" keyword (instead + use "__asm__"). */ + +/* Derive some tags indicating what the target platform is. Note + that in this file we're using the compiler's CPP symbols for + identifying architectures, which are different to the ones we use + within the rest of Valgrind. Note, __powerpc__ is active for both + 32 and 64-bit PPC, whereas __powerpc64__ is only active for the + latter (on Linux, that is). */ +#undef PLAT_x86_linux +#undef PLAT_amd64_linux +#undef PLAT_ppc32_linux +#undef PLAT_ppc64_linux +#undef PLAT_ppc32_aix5 +#undef PLAT_ppc64_aix5 + +#if !defined(_AIX) && defined(__i386__) +# define PLAT_x86_linux 1 +#elif !defined(_AIX) && defined(__x86_64__) +# define PLAT_amd64_linux 1 +#elif !defined(_AIX) && defined(__powerpc__) && !defined(__powerpc64__) +# define PLAT_ppc32_linux 1 +#elif !defined(_AIX) && defined(__powerpc__) && defined(__powerpc64__) +# define PLAT_ppc64_linux 1 +#elif defined(_AIX) && defined(__64BIT__) +# define PLAT_ppc64_aix5 1 +#elif defined(_AIX) && !defined(__64BIT__) +# define PLAT_ppc32_aix5 1 +#endif + + +/* If we're not compiling for our target platform, don't generate + any inline asms. */ +#if !defined(PLAT_x86_linux) && !defined(PLAT_amd64_linux) \ + && !defined(PLAT_ppc32_linux) && !defined(PLAT_ppc64_linux) \ + && !defined(PLAT_ppc32_aix5) && !defined(PLAT_ppc64_aix5) +# if !defined(NVALGRIND) +# define NVALGRIND 1 +# endif +#endif + + +/* ------------------------------------------------------------------ */ +/* ARCHITECTURE SPECIFICS for SPECIAL INSTRUCTIONS. There is nothing */ +/* in here of use to end-users -- skip to the next section. */ +/* ------------------------------------------------------------------ */ + +#if defined(NVALGRIND) + +/* Define NVALGRIND to completely remove the Valgrind magic sequence + from the compiled code (analogous to NDEBUG's effects on + assert()) */ +#define VALGRIND_DO_CLIENT_REQUEST( \ + _zzq_rlval, _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + { \ + (_zzq_rlval) = (_zzq_default); \ + } + +#else /* ! NVALGRIND */ + +/* The following defines the magic code sequences which the JITter + spots and handles magically. Don't look too closely at them as + they will rot your brain. + + The assembly code sequences for all architectures is in this one + file. This is because this file must be stand-alone, and we don't + want to have multiple files. + + For VALGRIND_DO_CLIENT_REQUEST, we must ensure that the default + value gets put in the return slot, so that everything works when + this is executed not under Valgrind. Args are passed in a memory + block, and so there's no intrinsic limit to the number that could + be passed, but it's currently five. + + The macro args are: + _zzq_rlval result lvalue + _zzq_default default value (result returned when running on real CPU) + _zzq_request request code + _zzq_arg1..5 request params + + The other two macros are used to support function wrapping, and are + a lot simpler. VALGRIND_GET_NR_CONTEXT returns the value of the + guest's NRADDR pseudo-register and whatever other information is + needed to safely run the call original from the wrapper: on + ppc64-linux, the R2 value at the divert point is also needed. This + information is abstracted into a user-visible type, OrigFn. + + VALGRIND_CALL_NOREDIR_* behaves the same as the following on the + guest, but guarantees that the branch instruction will not be + redirected: x86: call *%eax, amd64: call *%rax, ppc32/ppc64: + branch-and-link-to-r11. VALGRIND_CALL_NOREDIR is just text, not a + complete inline asm, since it needs to be combined with more magic + inline asm stuff to be useful. +*/ + +/* ------------------------- x86-linux ------------------------- */ + +#if defined(PLAT_x86_linux) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "roll $3, %%edi ; roll $13, %%edi\n\t" \ + "roll $29, %%edi ; roll $19, %%edi\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST( \ + _zzq_rlval, _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + { volatile unsigned int _zzq_args[6]; \ + volatile unsigned int _zzq_result; \ + _zzq_args[0] = (unsigned int)(_zzq_request); \ + _zzq_args[1] = (unsigned int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned int)(_zzq_arg5); \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %EDX = client_request ( %EAX ) */ \ + "xchgl %%ebx,%%ebx" \ + : "=d" (_zzq_result) \ + : "a" (&_zzq_args[0]), "0" (_zzq_default) \ + : "cc", "memory" \ + ); \ + _zzq_rlval = _zzq_result; \ + } + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %EAX = guest_NRADDR */ \ + "xchgl %%ecx,%%ecx" \ + : "=a" (__addr) \ + : \ + : "cc", "memory" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_EAX \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* call-noredir *%EAX */ \ + "xchgl %%edx,%%edx\n\t" +#endif /* PLAT_x86_linux */ + +/* ------------------------ amd64-linux ------------------------ */ + +#if defined(PLAT_amd64_linux) + +typedef + struct { + unsigned long long int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rolq $3, %%rdi ; rolq $13, %%rdi\n\t" \ + "rolq $61, %%rdi ; rolq $51, %%rdi\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST( \ + _zzq_rlval, _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + { volatile unsigned long long int _zzq_args[6]; \ + volatile unsigned long long int _zzq_result; \ + _zzq_args[0] = (unsigned long long int)(_zzq_request); \ + _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %RDX = client_request ( %RAX ) */ \ + "xchgq %%rbx,%%rbx" \ + : "=d" (_zzq_result) \ + : "a" (&_zzq_args[0]), "0" (_zzq_default) \ + : "cc", "memory" \ + ); \ + _zzq_rlval = _zzq_result; \ + } + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned long long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %RAX = guest_NRADDR */ \ + "xchgq %%rcx,%%rcx" \ + : "=a" (__addr) \ + : \ + : "cc", "memory" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_RAX \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* call-noredir *%RAX */ \ + "xchgq %%rdx,%%rdx\n\t" +#endif /* PLAT_amd64_linux */ + +/* ------------------------ ppc32-linux ------------------------ */ + +#if defined(PLAT_ppc32_linux) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rlwinm 0,0,3,0,0 ; rlwinm 0,0,13,0,0\n\t" \ + "rlwinm 0,0,29,0,0 ; rlwinm 0,0,19,0,0\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST( \ + _zzq_rlval, _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + { unsigned int _zzq_args[6]; \ + unsigned int _zzq_result; \ + unsigned int* _zzq_ptr; \ + _zzq_args[0] = (unsigned int)(_zzq_request); \ + _zzq_args[1] = (unsigned int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned int)(_zzq_arg5); \ + _zzq_ptr = _zzq_args; \ + __asm__ volatile("mr 3,%1\n\t" /*default*/ \ + "mr 4,%2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = client_request ( %R4 ) */ \ + "or 1,1,1\n\t" \ + "mr %0,3" /*result*/ \ + : "=b" (_zzq_result) \ + : "b" (_zzq_default), "b" (_zzq_ptr) \ + : "cc", "memory", "r3", "r4"); \ + _zzq_rlval = _zzq_result; \ + } + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + unsigned int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR */ \ + "or 2,2,2\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "cc", "memory", "r3" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir *%R11 */ \ + "or 3,3,3\n\t" +#endif /* PLAT_ppc32_linux */ + +/* ------------------------ ppc64-linux ------------------------ */ + +#if defined(PLAT_ppc64_linux) + +typedef + struct { + unsigned long long int nraddr; /* where's the code? */ + unsigned long long int r2; /* what tocptr do we need? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rotldi 0,0,3 ; rotldi 0,0,13\n\t" \ + "rotldi 0,0,61 ; rotldi 0,0,51\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST( \ + _zzq_rlval, _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + { unsigned long long int _zzq_args[6]; \ + register unsigned long long int _zzq_result __asm__("r3"); \ + register unsigned long long int* _zzq_ptr __asm__("r4"); \ + _zzq_args[0] = (unsigned long long int)(_zzq_request); \ + _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \ + _zzq_ptr = _zzq_args; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = client_request ( %R4 ) */ \ + "or 1,1,1" \ + : "=r" (_zzq_result) \ + : "0" (_zzq_default), "r" (_zzq_ptr) \ + : "cc", "memory"); \ + _zzq_rlval = _zzq_result; \ + } + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + register unsigned long long int __addr __asm__("r3"); \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR */ \ + "or 2,2,2" \ + : "=r" (__addr) \ + : \ + : "cc", "memory" \ + ); \ + _zzq_orig->nraddr = __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR_GPR2 */ \ + "or 4,4,4" \ + : "=r" (__addr) \ + : \ + : "cc", "memory" \ + ); \ + _zzq_orig->r2 = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir *%R11 */ \ + "or 3,3,3\n\t" + +#endif /* PLAT_ppc64_linux */ + +/* ------------------------ ppc32-aix5 ------------------------- */ + +#if defined(PLAT_ppc32_aix5) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + unsigned int r2; /* what tocptr do we need? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rlwinm 0,0,3,0,0 ; rlwinm 0,0,13,0,0\n\t" \ + "rlwinm 0,0,29,0,0 ; rlwinm 0,0,19,0,0\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST( \ + _zzq_rlval, _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + { unsigned int _zzq_args[7]; \ + register unsigned int _zzq_result; \ + register unsigned int* _zzq_ptr; \ + _zzq_args[0] = (unsigned int)(_zzq_request); \ + _zzq_args[1] = (unsigned int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned int)(_zzq_arg5); \ + _zzq_args[6] = (unsigned int)(_zzq_default); \ + _zzq_ptr = _zzq_args; \ + __asm__ volatile("mr 4,%1\n\t" \ + "lwz 3, 24(4)\n\t" \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = client_request ( %R4 ) */ \ + "or 1,1,1\n\t" \ + "mr %0,3" \ + : "=b" (_zzq_result) \ + : "b" (_zzq_ptr) \ + : "r3", "r4", "cc", "memory"); \ + _zzq_rlval = _zzq_result; \ + } + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + register unsigned int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR */ \ + "or 2,2,2\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "r3", "cc", "memory" \ + ); \ + _zzq_orig->nraddr = __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR_GPR2 */ \ + "or 4,4,4\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "r3", "cc", "memory" \ + ); \ + _zzq_orig->r2 = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir *%R11 */ \ + "or 3,3,3\n\t" + +#endif /* PLAT_ppc32_aix5 */ + +/* ------------------------ ppc64-aix5 ------------------------- */ + +#if defined(PLAT_ppc64_aix5) + +typedef + struct { + unsigned long long int nraddr; /* where's the code? */ + unsigned long long int r2; /* what tocptr do we need? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rotldi 0,0,3 ; rotldi 0,0,13\n\t" \ + "rotldi 0,0,61 ; rotldi 0,0,51\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST( \ + _zzq_rlval, _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + { unsigned long long int _zzq_args[7]; \ + register unsigned long long int _zzq_result; \ + register unsigned long long int* _zzq_ptr; \ + _zzq_args[0] = (unsigned int long long)(_zzq_request); \ + _zzq_args[1] = (unsigned int long long)(_zzq_arg1); \ + _zzq_args[2] = (unsigned int long long)(_zzq_arg2); \ + _zzq_args[3] = (unsigned int long long)(_zzq_arg3); \ + _zzq_args[4] = (unsigned int long long)(_zzq_arg4); \ + _zzq_args[5] = (unsigned int long long)(_zzq_arg5); \ + _zzq_args[6] = (unsigned int long long)(_zzq_default); \ + _zzq_ptr = _zzq_args; \ + __asm__ volatile("mr 4,%1\n\t" \ + "ld 3, 48(4)\n\t" \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = client_request ( %R4 ) */ \ + "or 1,1,1\n\t" \ + "mr %0,3" \ + : "=b" (_zzq_result) \ + : "b" (_zzq_ptr) \ + : "r3", "r4", "cc", "memory"); \ + _zzq_rlval = _zzq_result; \ + } + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + register unsigned long long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR */ \ + "or 2,2,2\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "r3", "cc", "memory" \ + ); \ + _zzq_orig->nraddr = __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR_GPR2 */ \ + "or 4,4,4\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "r3", "cc", "memory" \ + ); \ + _zzq_orig->r2 = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir *%R11 */ \ + "or 3,3,3\n\t" + +#endif /* PLAT_ppc64_aix5 */ + +/* Insert assembly code for other platforms here... */ + +#endif /* NVALGRIND */ + + +/* ------------------------------------------------------------------ */ +/* PLATFORM SPECIFICS for FUNCTION WRAPPING. This is all very */ +/* ugly. It's the least-worst tradeoff I can think of. */ +/* ------------------------------------------------------------------ */ + +/* This section defines magic (a.k.a appalling-hack) macros for doing + guaranteed-no-redirection macros, so as to get from function + wrappers to the functions they are wrapping. The whole point is to + construct standard call sequences, but to do the call itself with a + special no-redirect call pseudo-instruction that the JIT + understands and handles specially. This section is long and + repetitious, and I can't see a way to make it shorter. + + The naming scheme is as follows: + + CALL_FN_{W,v}_{v,W,WW,WWW,WWWW,5W,6W,7W,etc} + + 'W' stands for "word" and 'v' for "void". Hence there are + different macros for calling arity 0, 1, 2, 3, 4, etc, functions, + and for each, the possibility of returning a word-typed result, or + no result. +*/ + +/* Use these to write the name of your wrapper. NOTE: duplicates + VG_WRAP_FUNCTION_Z{U,Z} in pub_tool_redir.h. */ + +#define I_WRAP_SONAME_FNNAME_ZU(soname,fnname) \ + _vgwZU_##soname##_##fnname + +#define I_WRAP_SONAME_FNNAME_ZZ(soname,fnname) \ + _vgwZZ_##soname##_##fnname + +/* Use this macro from within a wrapper function to collect the + context (address and possibly other info) of the original function. + Once you have that you can then use it in one of the CALL_FN_ + macros. The type of the argument _lval is OrigFn. */ +#define VALGRIND_GET_ORIG_FN(_lval) VALGRIND_GET_NR_CONTEXT(_lval) + +/* Derivatives of the main macros below, for calling functions + returning void. */ + +#define CALL_FN_v_v(fnptr) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_v(_junk,fnptr); } while (0) + +#define CALL_FN_v_W(fnptr, arg1) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_W(_junk,fnptr,arg1); } while (0) + +#define CALL_FN_v_WW(fnptr, arg1,arg2) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_WW(_junk,fnptr,arg1,arg2); } while (0) + +#define CALL_FN_v_WWW(fnptr, arg1,arg2,arg3) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_WWW(_junk,fnptr,arg1,arg2,arg3); } while (0) + +/* ------------------------- x86-linux ------------------------- */ + +#if defined(PLAT_x86_linux) + +/* These regs are trashed by the hidden call. No need to mention eax + as gcc can already see that, plus causes gcc to bomb. */ +#define __CALLER_SAVED_REGS /*"eax"*/ "ecx", "edx" + +/* These CALL_FN_ macros assume that on x86-linux, sizeof(unsigned + long) == 4. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $4, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $8, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $12, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $16, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $20, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $24, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $28, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $32, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + "pushl 36(%%eax)\n\t" \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $36, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + "pushl 40(%%eax)\n\t" \ + "pushl 36(%%eax)\n\t" \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $40, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + "pushl 44(%%eax)\n\t" \ + "pushl 40(%%eax)\n\t" \ + "pushl 36(%%eax)\n\t" \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $44, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + "pushl 48(%%eax)\n\t" \ + "pushl 44(%%eax)\n\t" \ + "pushl 40(%%eax)\n\t" \ + "pushl 36(%%eax)\n\t" \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $48, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_x86_linux */ + +/* ------------------------ amd64-linux ------------------------ */ + +#if defined(PLAT_amd64_linux) + +/* ARGREGS: rdi rsi rdx rcx r8 r9 (the rest on stack in R-to-L order) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS /*"rax",*/ "rcx", "rdx", "rsi", \ + "rdi", "r8", "r9", "r10", "r11" + +/* These CALL_FN_ macros assume that on amd64-linux, sizeof(unsigned + long) == 8. */ + +/* NB 9 Sept 07. There is a nasty kludge here in all these CALL_FN_ + macros. In order not to trash the stack redzone, we need to drop + %rsp by 128 before the hidden call, and restore afterwards. The + nastyness is that it is only by luck that the stack still appears + to be unwindable during the hidden call - since then the behaviour + of any routine using this macro does not match what the CFI data + says. Sigh. + + Why is this important? Imagine that a wrapper has a stack + allocated local, and passes to the hidden call, a pointer to it. + Because gcc does not know about the hidden call, it may allocate + that local in the redzone. Unfortunately the hidden call may then + trash it before it comes to use it. So we must step clear of the + redzone, for the duration of the hidden call, to make it safe. + + Probably the same problem afflicts the other redzone-style ABIs too + (ppc64-linux, ppc32-aix5, ppc64-aix5); but for those, the stack is + self describing (none of this CFI nonsense) so at least messing + with the stack pointer doesn't give a danger of non-unwindable + stack. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + "subq $128,%%rsp\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $128,%%rsp\n\t" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + "subq $128,%%rsp\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $128,%%rsp\n\t" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + "subq $128,%%rsp\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $128,%%rsp\n\t" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + "subq $128,%%rsp\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $128,%%rsp\n\t" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + "subq $128,%%rsp\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $128,%%rsp\n\t" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + "subq $128,%%rsp\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $128,%%rsp\n\t" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + "subq $128,%%rsp\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + "addq $128,%%rsp\n\t" \ + VALGRIND_CALL_NOREDIR_RAX \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + "subq $128,%%rsp\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $8, %%rsp\n" \ + "addq $128,%%rsp\n\t" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + "subq $128,%%rsp\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $16, %%rsp\n" \ + "addq $128,%%rsp\n\t" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + "subq $128,%%rsp\n\t" \ + "pushq 72(%%rax)\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $24, %%rsp\n" \ + "addq $128,%%rsp\n\t" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + "subq $128,%%rsp\n\t" \ + "pushq 80(%%rax)\n\t" \ + "pushq 72(%%rax)\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $32, %%rsp\n" \ + "addq $128,%%rsp\n\t" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + "subq $128,%%rsp\n\t" \ + "pushq 88(%%rax)\n\t" \ + "pushq 80(%%rax)\n\t" \ + "pushq 72(%%rax)\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $40, %%rsp\n" \ + "addq $128,%%rsp\n\t" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + "subq $128,%%rsp\n\t" \ + "pushq 96(%%rax)\n\t" \ + "pushq 88(%%rax)\n\t" \ + "pushq 80(%%rax)\n\t" \ + "pushq 72(%%rax)\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $48, %%rsp\n" \ + "addq $128,%%rsp\n\t" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_amd64_linux */ + +/* ------------------------ ppc32-linux ------------------------ */ + +#if defined(PLAT_ppc32_linux) + +/* This is useful for finding out about the on-stack stuff: + + extern int f9 ( int,int,int,int,int,int,int,int,int ); + extern int f10 ( int,int,int,int,int,int,int,int,int,int ); + extern int f11 ( int,int,int,int,int,int,int,int,int,int,int ); + extern int f12 ( int,int,int,int,int,int,int,int,int,int,int,int ); + + int g9 ( void ) { + return f9(11,22,33,44,55,66,77,88,99); + } + int g10 ( void ) { + return f10(11,22,33,44,55,66,77,88,99,110); + } + int g11 ( void ) { + return f11(11,22,33,44,55,66,77,88,99,110,121); + } + int g12 ( void ) { + return f12(11,22,33,44,55,66,77,88,99,110,121,132); + } +*/ + +/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS \ + "lr", "ctr", "xer", \ + "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ + "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ + "r11", "r12", "r13" + +/* These CALL_FN_ macros assume that on ppc32-linux, + sizeof(unsigned long) == 4. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "addi 1,1,-16\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,8(1)\n\t" \ + /* args1-8 */ \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "addi 1,1,16\n\t" \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "addi 1,1,-16\n\t" \ + /* arg10 */ \ + "lwz 3,40(11)\n\t" \ + "stw 3,12(1)\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,8(1)\n\t" \ + /* args1-8 */ \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "addi 1,1,16\n\t" \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + _argvec[11] = (unsigned long)arg11; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "addi 1,1,-32\n\t" \ + /* arg11 */ \ + "lwz 3,44(11)\n\t" \ + "stw 3,16(1)\n\t" \ + /* arg10 */ \ + "lwz 3,40(11)\n\t" \ + "stw 3,12(1)\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,8(1)\n\t" \ + /* args1-8 */ \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "addi 1,1,32\n\t" \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + _argvec[11] = (unsigned long)arg11; \ + _argvec[12] = (unsigned long)arg12; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "addi 1,1,-32\n\t" \ + /* arg12 */ \ + "lwz 3,48(11)\n\t" \ + "stw 3,20(1)\n\t" \ + /* arg11 */ \ + "lwz 3,44(11)\n\t" \ + "stw 3,16(1)\n\t" \ + /* arg10 */ \ + "lwz 3,40(11)\n\t" \ + "stw 3,12(1)\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,8(1)\n\t" \ + /* args1-8 */ \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "addi 1,1,32\n\t" \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_ppc32_linux */ + +/* ------------------------ ppc64-linux ------------------------ */ + +#if defined(PLAT_ppc64_linux) + +/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS \ + "lr", "ctr", "xer", \ + "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ + "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ + "r11", "r12", "r13" + +/* These CALL_FN_ macros assume that on ppc64-linux, sizeof(unsigned + long) == 8. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+0]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)" /* restore tocptr */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+1]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)" /* restore tocptr */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+2]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)" /* restore tocptr */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+3]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)" /* restore tocptr */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+4]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)" /* restore tocptr */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+5]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)" /* restore tocptr */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+6]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)" /* restore tocptr */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+7]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)" /* restore tocptr */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+8]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)" /* restore tocptr */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+9]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-128\n\t" /* expand stack frame */ \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + "addi 1,1,128" /* restore frame */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+10]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-128\n\t" /* expand stack frame */ \ + /* arg10 */ \ + "ld 3,80(11)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + "addi 1,1,128" /* restore frame */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+11]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-144\n\t" /* expand stack frame */ \ + /* arg11 */ \ + "ld 3,88(11)\n\t" \ + "std 3,128(1)\n\t" \ + /* arg10 */ \ + "ld 3,80(11)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + "addi 1,1,144" /* restore frame */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+12]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + _argvec[2+12] = (unsigned long)arg12; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-144\n\t" /* expand stack frame */ \ + /* arg12 */ \ + "ld 3,96(11)\n\t" \ + "std 3,136(1)\n\t" \ + /* arg11 */ \ + "ld 3,88(11)\n\t" \ + "std 3,128(1)\n\t" \ + /* arg10 */ \ + "ld 3,80(11)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + "addi 1,1,144" /* restore frame */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_ppc64_linux */ + +/* ------------------------ ppc32-aix5 ------------------------- */ + +#if defined(PLAT_ppc32_aix5) + +/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS \ + "lr", "ctr", "xer", \ + "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ + "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ + "r11", "r12", "r13" + +/* Expand the stack frame, copying enough info that unwinding + still works. Trashes r3. */ + +#define VG_EXPAND_FRAME_BY_trashes_r3(_n_fr) \ + "addi 1,1,-" #_n_fr "\n\t" \ + "lwz 3," #_n_fr "(1)\n\t" \ + "stw 3,0(1)\n\t" + +#define VG_CONTRACT_FRAME_BY(_n_fr) \ + "addi 1,1," #_n_fr "\n\t" + +/* These CALL_FN_ macros assume that on ppc32-aix5, sizeof(unsigned + long) == 4. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+0]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+1]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+2]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+3]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+4]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+5]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ + "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+6]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ + "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ + "lwz 8, 24(11)\n\t" /* arg6->r8 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+7]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ + "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ + "lwz 8, 24(11)\n\t" /* arg6->r8 */ \ + "lwz 9, 28(11)\n\t" /* arg7->r9 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+8]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ + "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ + "lwz 8, 24(11)\n\t" /* arg6->r8 */ \ + "lwz 9, 28(11)\n\t" /* arg7->r9 */ \ + "lwz 10, 32(11)\n\t" /* arg8->r10 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+9]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + VG_EXPAND_FRAME_BY_trashes_r3(64) \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,56(1)\n\t" \ + /* args1-8 */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ + "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ + "lwz 8, 24(11)\n\t" /* arg6->r8 */ \ + "lwz 9, 28(11)\n\t" /* arg7->r9 */ \ + "lwz 10, 32(11)\n\t" /* arg8->r10 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(64) \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+10]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + VG_EXPAND_FRAME_BY_trashes_r3(64) \ + /* arg10 */ \ + "lwz 3,40(11)\n\t" \ + "stw 3,60(1)\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,56(1)\n\t" \ + /* args1-8 */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ + "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ + "lwz 8, 24(11)\n\t" /* arg6->r8 */ \ + "lwz 9, 28(11)\n\t" /* arg7->r9 */ \ + "lwz 10, 32(11)\n\t" /* arg8->r10 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(64) \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+11]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + VG_EXPAND_FRAME_BY_trashes_r3(72) \ + /* arg11 */ \ + "lwz 3,44(11)\n\t" \ + "stw 3,64(1)\n\t" \ + /* arg10 */ \ + "lwz 3,40(11)\n\t" \ + "stw 3,60(1)\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,56(1)\n\t" \ + /* args1-8 */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ + "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ + "lwz 8, 24(11)\n\t" /* arg6->r8 */ \ + "lwz 9, 28(11)\n\t" /* arg7->r9 */ \ + "lwz 10, 32(11)\n\t" /* arg8->r10 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(72) \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+12]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + _argvec[2+12] = (unsigned long)arg12; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + VG_EXPAND_FRAME_BY_trashes_r3(72) \ + /* arg12 */ \ + "lwz 3,48(11)\n\t" \ + "stw 3,68(1)\n\t" \ + /* arg11 */ \ + "lwz 3,44(11)\n\t" \ + "stw 3,64(1)\n\t" \ + /* arg10 */ \ + "lwz 3,40(11)\n\t" \ + "stw 3,60(1)\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,56(1)\n\t" \ + /* args1-8 */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ + "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ + "lwz 8, 24(11)\n\t" /* arg6->r8 */ \ + "lwz 9, 28(11)\n\t" /* arg7->r9 */ \ + "lwz 10, 32(11)\n\t" /* arg8->r10 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(72) \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_ppc32_aix5 */ + +/* ------------------------ ppc64-aix5 ------------------------- */ + +#if defined(PLAT_ppc64_aix5) + +/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS \ + "lr", "ctr", "xer", \ + "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ + "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ + "r11", "r12", "r13" + +/* Expand the stack frame, copying enough info that unwinding + still works. Trashes r3. */ + +#define VG_EXPAND_FRAME_BY_trashes_r3(_n_fr) \ + "addi 1,1,-" #_n_fr "\n\t" \ + "ld 3," #_n_fr "(1)\n\t" \ + "std 3,0(1)\n\t" + +#define VG_CONTRACT_FRAME_BY(_n_fr) \ + "addi 1,1," #_n_fr "\n\t" + +/* These CALL_FN_ macros assume that on ppc64-aix5, sizeof(unsigned + long) == 8. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+0]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+1]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+2]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+3]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+4]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+5]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+6]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+7]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+8]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+9]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + VG_EXPAND_FRAME_BY_trashes_r3(128) \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(128) \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+10]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + VG_EXPAND_FRAME_BY_trashes_r3(128) \ + /* arg10 */ \ + "ld 3,80(11)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(128) \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+11]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + VG_EXPAND_FRAME_BY_trashes_r3(144) \ + /* arg11 */ \ + "ld 3,88(11)\n\t" \ + "std 3,128(1)\n\t" \ + /* arg10 */ \ + "ld 3,80(11)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(144) \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+12]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + _argvec[2+12] = (unsigned long)arg12; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + VG_EXPAND_FRAME_BY_trashes_r3(144) \ + /* arg12 */ \ + "ld 3,96(11)\n\t" \ + "std 3,136(1)\n\t" \ + /* arg11 */ \ + "ld 3,88(11)\n\t" \ + "std 3,128(1)\n\t" \ + /* arg10 */ \ + "ld 3,80(11)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(144) \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_ppc64_aix5 */ + + +/* ------------------------------------------------------------------ */ +/* ARCHITECTURE INDEPENDENT MACROS for CLIENT REQUESTS. */ +/* */ +/* ------------------------------------------------------------------ */ + +/* Some request codes. There are many more of these, but most are not + exposed to end-user view. These are the public ones, all of the + form 0x1000 + small_number. + + Core ones are in the range 0x00000000--0x0000ffff. The non-public + ones start at 0x2000. +*/ + +/* These macros are used by tools -- they must be public, but don't + embed them into other programs. */ +#define VG_USERREQ_TOOL_BASE(a,b) \ + ((unsigned int)(((a)&0xff) << 24 | ((b)&0xff) << 16)) +#define VG_IS_TOOL_USERREQ(a, b, v) \ + (VG_USERREQ_TOOL_BASE(a,b) == ((v) & 0xffff0000)) + +/* !! ABIWARNING !! ABIWARNING !! ABIWARNING !! ABIWARNING !! + This enum comprises an ABI exported by Valgrind to programs + which use client requests. DO NOT CHANGE THE ORDER OF THESE + ENTRIES, NOR DELETE ANY -- add new ones at the end. */ +typedef + enum { VG_USERREQ__RUNNING_ON_VALGRIND = 0x1001, + VG_USERREQ__DISCARD_TRANSLATIONS = 0x1002, + + /* These allow any function to be called from the simulated + CPU but run on the real CPU. Nb: the first arg passed to + the function is always the ThreadId of the running + thread! So CLIENT_CALL0 actually requires a 1 arg + function, etc. */ + VG_USERREQ__CLIENT_CALL0 = 0x1101, + VG_USERREQ__CLIENT_CALL1 = 0x1102, + VG_USERREQ__CLIENT_CALL2 = 0x1103, + VG_USERREQ__CLIENT_CALL3 = 0x1104, + + /* Can be useful in regression testing suites -- eg. can + send Valgrind's output to /dev/null and still count + errors. */ + VG_USERREQ__COUNT_ERRORS = 0x1201, + + /* These are useful and can be interpreted by any tool that + tracks malloc() et al, by using vg_replace_malloc.c. */ + VG_USERREQ__MALLOCLIKE_BLOCK = 0x1301, + VG_USERREQ__FREELIKE_BLOCK = 0x1302, + /* Memory pool support. */ + VG_USERREQ__CREATE_MEMPOOL = 0x1303, + VG_USERREQ__DESTROY_MEMPOOL = 0x1304, + VG_USERREQ__MEMPOOL_ALLOC = 0x1305, + VG_USERREQ__MEMPOOL_FREE = 0x1306, + VG_USERREQ__MEMPOOL_TRIM = 0x1307, + VG_USERREQ__MOVE_MEMPOOL = 0x1308, + VG_USERREQ__MEMPOOL_CHANGE = 0x1309, + VG_USERREQ__MEMPOOL_EXISTS = 0x130a, + + /* Allow printfs to valgrind log. */ + VG_USERREQ__PRINTF = 0x1401, + VG_USERREQ__PRINTF_BACKTRACE = 0x1402, + + /* Stack support. */ + VG_USERREQ__STACK_REGISTER = 0x1501, + VG_USERREQ__STACK_DEREGISTER = 0x1502, + VG_USERREQ__STACK_CHANGE = 0x1503 + } Vg_ClientRequest; + +#if !defined(__GNUC__) +# define __extension__ /* */ +#endif + +/* Returns the number of Valgrinds this code is running under. That + is, 0 if running natively, 1 if running under Valgrind, 2 if + running under Valgrind which is running under another Valgrind, + etc. */ +#define RUNNING_ON_VALGRIND __extension__ \ + ({unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0 /* if not */, \ + VG_USERREQ__RUNNING_ON_VALGRIND, \ + 0, 0, 0, 0, 0); \ + _qzz_res; \ + }) + + +/* Discard translation of code in the range [_qzz_addr .. _qzz_addr + + _qzz_len - 1]. Useful if you are debugging a JITter or some such, + since it provides a way to make sure valgrind will retranslate the + invalidated area. Returns no value. */ +#define VALGRIND_DISCARD_TRANSLATIONS(_qzz_addr,_qzz_len) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__DISCARD_TRANSLATIONS, \ + _qzz_addr, _qzz_len, 0, 0, 0); \ + } + + +/* These requests are for getting Valgrind itself to print something. + Possibly with a backtrace. This is a really ugly hack. */ + +#if defined(NVALGRIND) + +# define VALGRIND_PRINTF(...) +# define VALGRIND_PRINTF_BACKTRACE(...) + +#else /* NVALGRIND */ + +/* Modern GCC will optimize the static routine out if unused, + and unused attribute will shut down warnings about it. */ +static int VALGRIND_PRINTF(const char *format, ...) + __attribute__((format(__printf__, 1, 2), __unused__)); +static int +VALGRIND_PRINTF(const char *format, ...) +{ + unsigned long _qzz_res; + va_list vargs; + va_start(vargs, format); + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, VG_USERREQ__PRINTF, + (unsigned long)format, (unsigned long)vargs, + 0, 0, 0); + va_end(vargs); + return (int)_qzz_res; +} + +static int VALGRIND_PRINTF_BACKTRACE(const char *format, ...) + __attribute__((format(__printf__, 1, 2), __unused__)); +static int +VALGRIND_PRINTF_BACKTRACE(const char *format, ...) +{ + unsigned long _qzz_res; + va_list vargs; + va_start(vargs, format); + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, VG_USERREQ__PRINTF_BACKTRACE, + (unsigned long)format, (unsigned long)vargs, + 0, 0, 0); + va_end(vargs); + return (int)_qzz_res; +} + +#endif /* NVALGRIND */ + + +/* These requests allow control to move from the simulated CPU to the + real CPU, calling an arbitary function. + + Note that the current ThreadId is inserted as the first argument. + So this call: + + VALGRIND_NON_SIMD_CALL2(f, arg1, arg2) + + requires f to have this signature: + + Word f(Word tid, Word arg1, Word arg2) + + where "Word" is a word-sized type. + + Note that these client requests are not entirely reliable. For example, + if you call a function with them that subsequently calls printf(), + there's a high chance Valgrind will crash. Generally, your prospects of + these working are made higher if the called function does not refer to + any global variables, and does not refer to any libc or other functions + (printf et al). Any kind of entanglement with libc or dynamic linking is + likely to have a bad outcome, for tricky reasons which we've grappled + with a lot in the past. +*/ +#define VALGRIND_NON_SIMD_CALL0(_qyy_fn) \ + __extension__ \ + ({unsigned long _qyy_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \ + VG_USERREQ__CLIENT_CALL0, \ + _qyy_fn, \ + 0, 0, 0, 0); \ + _qyy_res; \ + }) + +#define VALGRIND_NON_SIMD_CALL1(_qyy_fn, _qyy_arg1) \ + __extension__ \ + ({unsigned long _qyy_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \ + VG_USERREQ__CLIENT_CALL1, \ + _qyy_fn, \ + _qyy_arg1, 0, 0, 0); \ + _qyy_res; \ + }) + +#define VALGRIND_NON_SIMD_CALL2(_qyy_fn, _qyy_arg1, _qyy_arg2) \ + __extension__ \ + ({unsigned long _qyy_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \ + VG_USERREQ__CLIENT_CALL2, \ + _qyy_fn, \ + _qyy_arg1, _qyy_arg2, 0, 0); \ + _qyy_res; \ + }) + +#define VALGRIND_NON_SIMD_CALL3(_qyy_fn, _qyy_arg1, _qyy_arg2, _qyy_arg3) \ + __extension__ \ + ({unsigned long _qyy_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \ + VG_USERREQ__CLIENT_CALL3, \ + _qyy_fn, \ + _qyy_arg1, _qyy_arg2, \ + _qyy_arg3, 0); \ + _qyy_res; \ + }) + + +/* Counts the number of errors that have been recorded by a tool. Nb: + the tool must record the errors with VG_(maybe_record_error)() or + VG_(unique_error)() for them to be counted. */ +#define VALGRIND_COUNT_ERRORS \ + __extension__ \ + ({unsigned int _qyy_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \ + VG_USERREQ__COUNT_ERRORS, \ + 0, 0, 0, 0, 0); \ + _qyy_res; \ + }) + +/* Mark a block of memory as having been allocated by a malloc()-like + function. `addr' is the start of the usable block (ie. after any + redzone) `rzB' is redzone size if the allocator can apply redzones; + use '0' if not. Adding redzones makes it more likely Valgrind will spot + block overruns. `is_zeroed' indicates if the memory is zeroed, as it is + for calloc(). Put it immediately after the point where a block is + allocated. + + If you're using Memcheck: If you're allocating memory via superblocks, + and then handing out small chunks of each superblock, if you don't have + redzones on your small blocks, it's worth marking the superblock with + VALGRIND_MAKE_MEM_NOACCESS when it's created, so that block overruns are + detected. But if you can put redzones on, it's probably better to not do + this, so that messages for small overruns are described in terms of the + small block rather than the superblock (but if you have a big overrun + that skips over a redzone, you could miss an error this way). See + memcheck/tests/custom_alloc.c for an example. + + WARNING: if your allocator uses malloc() or 'new' to allocate + superblocks, rather than mmap() or brk(), this will not work properly -- + you'll likely get assertion failures during leak detection. This is + because Valgrind doesn't like seeing overlapping heap blocks. Sorry. + + Nb: block must be freed via a free()-like function specified + with VALGRIND_FREELIKE_BLOCK or mismatch errors will occur. */ +#define VALGRIND_MALLOCLIKE_BLOCK(addr, sizeB, rzB, is_zeroed) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__MALLOCLIKE_BLOCK, \ + addr, sizeB, rzB, is_zeroed, 0); \ + } + +/* Mark a block of memory as having been freed by a free()-like function. + `rzB' is redzone size; it must match that given to + VALGRIND_MALLOCLIKE_BLOCK. Memory not freed will be detected by the leak + checker. Put it immediately after the point where the block is freed. */ +#define VALGRIND_FREELIKE_BLOCK(addr, rzB) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__FREELIKE_BLOCK, \ + addr, rzB, 0, 0, 0); \ + } + +/* Create a memory pool. */ +#define VALGRIND_CREATE_MEMPOOL(pool, rzB, is_zeroed) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__CREATE_MEMPOOL, \ + pool, rzB, is_zeroed, 0, 0); \ + } + +/* Destroy a memory pool. */ +#define VALGRIND_DESTROY_MEMPOOL(pool) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__DESTROY_MEMPOOL, \ + pool, 0, 0, 0, 0); \ + } + +/* Associate a piece of memory with a memory pool. */ +#define VALGRIND_MEMPOOL_ALLOC(pool, addr, size) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__MEMPOOL_ALLOC, \ + pool, addr, size, 0, 0); \ + } + +/* Disassociate a piece of memory from a memory pool. */ +#define VALGRIND_MEMPOOL_FREE(pool, addr) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__MEMPOOL_FREE, \ + pool, addr, 0, 0, 0); \ + } + +/* Disassociate any pieces outside a particular range. */ +#define VALGRIND_MEMPOOL_TRIM(pool, addr, size) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__MEMPOOL_TRIM, \ + pool, addr, size, 0, 0); \ + } + +/* Resize and/or move a piece associated with a memory pool. */ +#define VALGRIND_MOVE_MEMPOOL(poolA, poolB) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__MOVE_MEMPOOL, \ + poolA, poolB, 0, 0, 0); \ + } + +/* Resize and/or move a piece associated with a memory pool. */ +#define VALGRIND_MEMPOOL_CHANGE(pool, addrA, addrB, size) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__MEMPOOL_CHANGE, \ + pool, addrA, addrB, size, 0); \ + } + +/* Return 1 if a mempool exists, else 0. */ +#define VALGRIND_MEMPOOL_EXISTS(pool) \ + ({unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__MEMPOOL_EXISTS, \ + pool, 0, 0, 0, 0); \ + _qzz_res; \ + }) + +/* Mark a piece of memory as being a stack. Returns a stack id. */ +#define VALGRIND_STACK_REGISTER(start, end) \ + ({unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__STACK_REGISTER, \ + start, end, 0, 0, 0); \ + _qzz_res; \ + }) + +/* Unmark the piece of memory associated with a stack id as being a + stack. */ +#define VALGRIND_STACK_DEREGISTER(id) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__STACK_DEREGISTER, \ + id, 0, 0, 0, 0); \ + } + +/* Change the start and end address of the stack id. */ +#define VALGRIND_STACK_CHANGE(id, start, end) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__STACK_CHANGE, \ + id, start, end, 0, 0); \ + } + + +#undef PLAT_x86_linux +#undef PLAT_amd64_linux +#undef PLAT_ppc32_linux +#undef PLAT_ppc64_linux +#undef PLAT_ppc32_aix5 +#undef PLAT_ppc64_aix5 + +#endif /* __VALGRIND_H */ diff --git a/tests/auto/integrationtests/exceptionsafety_objects/exceptionsafety_objects.pro b/tests/auto/integrationtests/exceptionsafety_objects/exceptionsafety_objects.pro new file mode 100644 index 0000000000..d09c349215 --- /dev/null +++ b/tests/auto/integrationtests/exceptionsafety_objects/exceptionsafety_objects.pro @@ -0,0 +1,6 @@ +load(qttest_p4) +QT += widgets +HEADERS += oomsimulator.h 3rdparty/valgrind.h 3rdparty/memcheck.h +SOURCES += tst_exceptionsafety_objects.cpp + +CONFIG+=insignificant_test diff --git a/tests/auto/integrationtests/exceptionsafety_objects/oomsimulator.h b/tests/auto/integrationtests/exceptionsafety_objects/oomsimulator.h new file mode 100644 index 0000000000..ed4bb53922 --- /dev/null +++ b/tests/auto/integrationtests/exceptionsafety_objects/oomsimulator.h @@ -0,0 +1,283 @@ +/**************************************************************************** +** +** 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 <malloc.h> +#include <limits.h> +#include <stdio.h> +#include <exception> + +#if !defined(Q_OS_WIN) +# include "3rdparty/memcheck.h" +#endif + +static bool mallocFailActive = false; +static int mallocFailIndex = 0; +static int mallocCount = 0; + +static void my_terminate_handler() +{ + // set a breakpoint here to get a backtrace for your uncaught exceptions + fprintf(stderr, "Uncaught Exception Detected. Set a breakpoint in my_terminate_handler()\n"); + exit(1); +} + +#ifdef __GLIBC__ +/* Use glibc's memory allocation hooks */ + +// From glibc 2.14, the malloc hook variables are declared volatile. +// Note: The malloc hook implementation is marked as deprecated. + +#if !defined(__MALLOC_HOOK_VOLATILE) +# define __MALLOC_HOOK_VOLATILE +#endif + +/* our hooks */ +static void *my_malloc_hook(size_t, const void *); +static void *my_realloc_hook(void *, size_t, const void *); +static void *my_memalign_hook(size_t, size_t, const void *); +static void my_free_hook(void *, const void *); + +/* original hooks. */ +static void *(*__MALLOC_HOOK_VOLATILE old_malloc_hook)(size_t, const void *); +static void *(*__MALLOC_HOOK_VOLATILE old_realloc_hook)(void *, size_t, const void *); +static void *(*__MALLOC_HOOK_VOLATILE old_memalign_hook)(size_t, size_t, const void *); +static void (*__MALLOC_HOOK_VOLATILE old_free_hook)(void *, const void *); + +/* initializer function */ +static void my_init_hook(); + +/* Override initialising hook from the C library. */ + +void (*__MALLOC_HOOK_VOLATILE __malloc_initialize_hook) (void) = my_init_hook; + +static void disableHooks() +{ + __malloc_hook = old_malloc_hook; + __realloc_hook = old_realloc_hook; + __memalign_hook = old_memalign_hook; + __free_hook = old_free_hook; +} + +static void enableHooks() +{ + __malloc_hook = my_malloc_hook; + __realloc_hook = my_realloc_hook; + __memalign_hook = my_memalign_hook; + __free_hook = my_free_hook; +} + +void my_init_hook() +{ + old_malloc_hook = __malloc_hook; + old_realloc_hook = __realloc_hook; + old_memalign_hook = __memalign_hook; + old_free_hook = __free_hook; + enableHooks(); +} + +void *my_malloc_hook(size_t size, const void *) +{ + ++mallocCount; + + if (mallocFailActive && --mallocFailIndex < 0) + return 0; // simulate OOM + + __malloc_hook = old_malloc_hook; + void *result = ::malloc (size); + __malloc_hook = my_malloc_hook; + + return result; +} + +void *my_memalign_hook(size_t alignment, size_t size, const void *) +{ + ++mallocCount; + + if (mallocFailActive && --mallocFailIndex < 0) + return 0; // simulate OOM + + __memalign_hook = old_memalign_hook; + void *result = ::memalign(alignment, size); + __memalign_hook = my_memalign_hook; + + return result; +} + +void *my_realloc_hook(void *ptr, size_t size, const void *) +{ + ++mallocCount; + + if (mallocFailActive && --mallocFailIndex < 0) + return 0; // simulate OOM + + __realloc_hook = old_realloc_hook; + __malloc_hook = old_malloc_hook; + void *result = ::realloc(ptr, size); + __malloc_hook = my_malloc_hook; + __realloc_hook = my_realloc_hook; + + return result; +} + +void my_free_hook(void *ptr, const void *) +{ + __free_hook = old_free_hook; + ::free(ptr); + __free_hook = my_free_hook; +} + +#elif defined(Q_CC_MSVC) + +#include "crtdbg.h" + +static int qCrtAllocHook(int allocType, void * /*userData*/, size_t /*size*/, + int blockType, long /*requestNumber*/, + const unsigned char * /*filename*/, int /*lineNumber*/) +{ + if (blockType == _CRT_BLOCK) + return TRUE; // ignore allocations from the C library + + switch (allocType) { + case _HOOK_ALLOC: + case _HOOK_REALLOC: + ++mallocCount; + if (mallocFailActive && --mallocFailIndex < 0) + return FALSE; // simulate OOM + } + + return TRUE; +} + +static struct QCrtDebugRegistrator +{ + QCrtDebugRegistrator() + { + _CrtSetAllocHook(qCrtAllocHook); + } + +} crtDebugRegistrator; + +#endif + +struct AllocFailer +{ + inline AllocFailer(int index) { reactivateAt(index); } + inline ~AllocFailer() { deactivate(); } + + inline void reactivateAt(int index) + { +#ifdef RUNNING_ON_VALGRIND + if (RUNNING_ON_VALGRIND) + VALGRIND_ENABLE_OOM_AT_ALLOC_INDEX(VALGRIND_GET_ALLOC_INDEX + index + 1); +#endif + mallocFailIndex = index; + mallocFailActive = true; + } + + inline void deactivate() + { + mallocFailActive = false; +#ifdef RUNNING_ON_VALGRIND + VALGRIND_ENABLE_OOM_AT_ALLOC_INDEX(0); +#endif + } + + inline int currentAllocIndex() const + { +#ifdef RUNNING_ON_VALGRIND + if (RUNNING_ON_VALGRIND) + return VALGRIND_GET_ALLOC_INDEX; +#endif + return mallocCount; + } + + static bool initialize() + { + std::set_terminate(my_terminate_handler); +#ifdef RUNNING_ON_VALGRIND + if (RUNNING_ON_VALGRIND) { + if (VALGRIND_GET_ALLOC_INDEX == -1u) { + qWarning("You must use a valgrind with oom simulation support"); + return false; + } + // running in valgrind - don't use glibc hooks + disableHooks(); + + // never stop simulating OOM + VALGRIND_DISABLE_OOM_AT_ALLOC_INDEX(-1u); + } +#endif + return true; + } +}; + +static void *new_helper(std::size_t size) +{ + void *ptr = malloc(size); + if (!ptr) + throw std::bad_alloc(); + return ptr; +} + +#ifdef Q_CC_MSVC +# pragma warning(push) +# pragma warning(disable: 4290) +#endif + +// overload operator new +void* operator new(size_t size) throw (std::bad_alloc) { return new_helper(size); } +void* operator new[](size_t size) throw (std::bad_alloc) { return new_helper(size); } +void* operator new(size_t size, const std::nothrow_t&) throw() { return malloc(size); } +void* operator new[](std::size_t size, const std::nothrow_t&) throw() { return malloc(size); } + +// overload operator delete +void operator delete(void *ptr) throw() { if (ptr) free(ptr); } +void operator delete[](void *ptr) throw() { if (ptr) free(ptr); } +void operator delete(void *ptr, const std::nothrow_t&) throw() { if (ptr) free(ptr); } +void operator delete[](void *ptr, const std::nothrow_t&) throw() { if (ptr) free (ptr); } + +#ifdef Q_CC_MSVC +# pragma warning(pop) +#endif + +// ignore placement new and placement delete - those don't allocate. + + diff --git a/tests/auto/integrationtests/exceptionsafety_objects/tst_exceptionsafety_objects.cpp b/tests/auto/integrationtests/exceptionsafety_objects/tst_exceptionsafety_objects.cpp new file mode 100644 index 0000000000..0d410a79a2 --- /dev/null +++ b/tests/auto/integrationtests/exceptionsafety_objects/tst_exceptionsafety_objects.cpp @@ -0,0 +1,801 @@ +/**************************************************************************** +** +** 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 <QtGui/QtGui> +#include <QtWidgets/QtWidgets> +#include <QtTest/QtTest> + +#include <stddef.h> +#include <exception> + +QT_USE_NAMESPACE + +// this test only works with GLIBC + +#include "oomsimulator.h" +#include "3rdparty/memcheck.h" + +class tst_ExceptionSafety_Objects: public QObject +{ + Q_OBJECT + +public slots: + void initTestCase(); +#ifndef QT_NO_EXCEPTIONS + void cleanupTestCase(); + +private slots: + void objects_data(); + void objects(); + + void widgets_data(); + void widgets(); + + void vector_data(); + void vector(); + + void list_data(); + void list(); + + void linkedList_data(); + void linkedList(); + +private: + static QtMsgHandler testMessageHandler; + static void safeMessageHandler(QtMsgType, const char *); +#endif +}; + +#ifdef QT_NO_EXCEPTIONS +void tst_ExceptionSafety_Objects::initTestCase() +{ + QSKIP("This test requires exception support", SkipAll); +} + +#else +// helper structs to create an arbitrary widget +struct AbstractTester +{ + virtual void operator()(QObject *parent) = 0; +}; +Q_DECLARE_METATYPE(AbstractTester *) + +typedef void (*TestFunction)(QObject*); +Q_DECLARE_METATYPE(TestFunction) + +template <typename T> +struct ObjectCreator : public AbstractTester +{ + void operator()(QObject *) + { + QScopedPointer<T> ptr(new T); + } +}; + +struct BitArrayCreator : public AbstractTester +{ + void operator()(QObject *) + { QScopedPointer<QBitArray> bitArray(new QBitArray(100, true)); } +}; + +struct ByteArrayMatcherCreator : public AbstractTester +{ + void operator()(QObject *) + { QScopedPointer<QByteArrayMatcher> ptr(new QByteArrayMatcher("ralf test",8)); } +}; + +struct CryptographicHashCreator : public AbstractTester +{ + void operator()(QObject *) + { + QScopedPointer<QCryptographicHash> ptr(new QCryptographicHash(QCryptographicHash::Sha1)); + ptr->addData("ralf test",8); + } +}; + +struct DataStreamCreator : public AbstractTester +{ + void operator()(QObject *) + { + QScopedPointer<QByteArray> arr(new QByteArray("hallo, test")); + QScopedPointer<QDataStream> ptr(new QDataStream(arr.data(), QIODevice::ReadWrite)); + ptr->writeBytes("ralf test",8); + } +}; + +struct DirCreator : public AbstractTester +{ + void operator()(QObject *) + { + QDir::cleanPath("../////././"); + QScopedPointer<QDir> ptr(new QDir(".")); + while( ptr->cdUp() ) + ; // just going up + ptr->count(); + ptr->exists(ptr->path()); + + QStringList filters; + filters << "*.cpp" << "*.cxx" << "*.cc"; + ptr->setNameFilters(filters); + } +}; + +void tst_ExceptionSafety_Objects::objects_data() +{ + QTest::addColumn<AbstractTester *>("objectCreator"); + +#define NEWROW(T) QTest::newRow(#T) << static_cast<AbstractTester *>(new ObjectCreator<T >) + NEWROW(QObject); + NEWROW(QBuffer); + NEWROW(QFile); + NEWROW(QFSFileEngine); + NEWROW(QProcess); + NEWROW(QSettings); + NEWROW(QThread); + NEWROW(QThreadPool); + NEWROW(QTranslator); + +#define NEWROW2(T, CREATOR) QTest::newRow(#T) << static_cast<AbstractTester *>(new CREATOR) + NEWROW2(QBitArray, BitArrayCreator); + NEWROW2(QByteArrayMatcher, ByteArrayMatcherCreator); + NEWROW2(QCryptographicHash, CryptographicHashCreator); + NEWROW2(QDataStream, DataStreamCreator); + NEWROW2(QDir, DirCreator); +} + +// create and destructs an object, and lets each and every allocation +// during construction and destruction fail. +template <typename T> +static void doOOMTest(T &testFunc, QObject *parent, int start=0) +{ + int currentOOMIndex = start; + bool caught = false; + bool done = false; + + AllocFailer allocFailer(0); + int allocCountBefore = allocFailer.currentAllocIndex(); + + do { + allocFailer.reactivateAt(++currentOOMIndex); + + caught = false; + + try { + testFunc(parent); + } catch (const std::bad_alloc &) { + caught = true; + } catch (const std::exception &ex) { + if (strcmp(ex.what(), "autotest swallow") != 0) + throw; + caught = true; + } + + if (!caught) { + void *buf = malloc(42); + if (buf) { + // we got memory here - oom test is over. + free(buf); + done = true; + } + } + + // if we get a FAIL, stop executing now + if (QTest::currentTestFailed()) + done = true; + +//#define REALLY_VERBOSE +#ifdef REALLY_VERBOSE + fprintf(stderr, " OOM Index: %d\n", currentOOMIndex); +#endif + + + } while (caught || !done); + + allocFailer.deactivate(); + +//#define VERBOSE +#ifdef VERBOSE + fprintf(stderr, "OOM Test done, checked allocs: %d (range %d - %d)\n", currentOOMIndex, + allocCountBefore, allocFailer.currentAllocIndex()); +#else + Q_UNUSED(allocCountBefore); +#endif +} + +static int alloc1Failed = 0; +static int alloc2Failed = 0; +static int alloc3Failed = 0; +static int alloc4Failed = 0; +static int malloc1Failed = 0; +static int malloc2Failed = 0; + +// Tests that new, new[] and malloc() fail at least once during OOM testing. +class SelfTestObject : public QObject +{ +public: + SelfTestObject(QObject *parent = 0) + : QObject(parent) + { + try { delete new int; } catch (const std::bad_alloc &) { ++alloc1Failed; throw; } + try { delete [] new double[5]; } catch (const std::bad_alloc &) { ++alloc2Failed; throw ;} + void *buf = malloc(42); + if (buf) + free(buf); + else + ++malloc1Failed; + } + + ~SelfTestObject() + { + try { delete new int; } catch (const std::bad_alloc &) { ++alloc3Failed; } + try { delete [] new double[5]; } catch (const std::bad_alloc &) { ++alloc4Failed; } + void *buf = malloc(42); + if (buf) + free(buf); + else + ++malloc2Failed = true; + } +}; + +QtMsgHandler tst_ExceptionSafety_Objects::testMessageHandler; + +void tst_ExceptionSafety_Objects::safeMessageHandler(QtMsgType type, const char *msg) +{ + // this temporarily suspends OOM testing while handling a message + int currentIndex = mallocFailIndex; + AllocFailer allocFailer(0); + allocFailer.deactivate(); + (*testMessageHandler)(type, msg); + allocFailer.reactivateAt(currentIndex); +} + +typedef void (*PVF)(); +PVF defaultTerminate; +void debugTerminate() +{ + // you can detect uncaught exceptions with a breakpoint in here + (*defaultTerminate)(); +} + +PVF defaultUnexpected; +void debugUnexpected() +{ + // you can detect unexpected exceptions with a breakpoint in here + (*defaultUnexpected)(); +} + +void tst_ExceptionSafety_Objects::initTestCase() +{ + // set handlers for bad exception cases, you might want to step in and breakpoint the default handlers too + defaultTerminate = std::set_terminate(&debugTerminate); + defaultUnexpected = std::set_unexpected(&debugUnexpected); + testMessageHandler = qInstallMsgHandler(safeMessageHandler); + + QVERIFY(AllocFailer::initialize()); + + // sanity check whether OOM simulation works + AllocFailer allocFailer(0); + + // malloc fail index is 0 -> this malloc should fail. + void *buf = malloc(42); + allocFailer.deactivate(); + QVERIFY(!buf); + + // malloc fail index is 1 - second malloc should fail. + allocFailer.reactivateAt(1); + buf = malloc(42); + void *buf2 = malloc(42); + allocFailer.deactivate(); + + QVERIFY(buf); + free(buf); + QVERIFY(!buf2); + + ObjectCreator<SelfTestObject> *selfTest = new ObjectCreator<SelfTestObject>; + doOOMTest(*selfTest, 0); + delete selfTest; + QCOMPARE(alloc1Failed, 1); + QCOMPARE(alloc2Failed, 1); + QCOMPARE(alloc3Failed, 2); + QCOMPARE(alloc4Failed, 3); + QCOMPARE(malloc1Failed, 1); + QCOMPARE(malloc2Failed, 1); +} + +void tst_ExceptionSafety_Objects::cleanupTestCase() +{ + qInstallMsgHandler(testMessageHandler); +} + +void tst_ExceptionSafety_Objects::objects() +{ + QLatin1String tag = QLatin1String(QTest::currentDataTag()); + if (tag == QLatin1String("QFile") + || tag == QLatin1String("QProcess") + || tag == QLatin1String("QSettings") + || tag == QLatin1String("QThread") + || tag == QLatin1String("QThreadPool")) + QSKIP("This type of object is not currently strongly exception safe", SkipSingle); + + QFETCH(AbstractTester *, objectCreator); + + doOOMTest(*objectCreator, 0); + + delete objectCreator; +} + +template <typename T> +struct WidgetCreator : public AbstractTester +{ + void operator()(QObject *parent) + { + if (parent && !parent->isWidgetType()) + qFatal("%s: parent must be either null or a widget type", Q_FUNC_INFO); + QScopedPointer<T> ptr(parent ? new T(static_cast<QWidget *>(parent)) : new T); + } +}; + +// QSizeGrip doesn't have a default constructor - always pass parent (even though it might be 0) +template <> struct WidgetCreator<QSizeGrip> : public AbstractTester +{ + void operator()(QObject *parent) + { + if (parent && !parent->isWidgetType()) + qFatal("%s: parent must be either null or a widget type", Q_FUNC_INFO); + QScopedPointer<QSizeGrip> ptr(new QSizeGrip(static_cast<QWidget *>(parent))); + } +}; + +// QDesktopWidget doesn't need a parent. +template <> struct WidgetCreator<QDesktopWidget> : public AbstractTester +{ + void operator()(QObject *parent) + { + if (parent && !parent->isWidgetType()) + qFatal("%s: parent must be either null or a widget type", Q_FUNC_INFO); + QScopedPointer<QDesktopWidget> ptr(new QDesktopWidget()); + } +}; +void tst_ExceptionSafety_Objects::widgets_data() +{ + QTest::addColumn<AbstractTester *>("widgetCreator"); + +#undef NEWROW +#define NEWROW(T) QTest::newRow(#T) << static_cast<AbstractTester *>(new WidgetCreator<T >) + + NEWROW(QWidget); + + NEWROW(QButtonGroup); + NEWROW(QCheckBox); + NEWROW(QColumnView); + NEWROW(QComboBox); + NEWROW(QCommandLinkButton); + NEWROW(QDateEdit); + NEWROW(QDateTimeEdit); + NEWROW(QDesktopWidget); + NEWROW(QDial); + NEWROW(QDoubleSpinBox); + NEWROW(QFocusFrame); + NEWROW(QFontComboBox); + NEWROW(QFrame); + NEWROW(QGroupBox); + NEWROW(QLabel); + NEWROW(QLCDNumber); + NEWROW(QLineEdit); + NEWROW(QListView); + NEWROW(QListWidget); + NEWROW(QMainWindow); + NEWROW(QMenu); + NEWROW(QMenuBar); + NEWROW(QPlainTextEdit); + NEWROW(QProgressBar); + NEWROW(QPushButton); + NEWROW(QRadioButton); + NEWROW(QScrollArea); + NEWROW(QScrollBar); + NEWROW(QSizeGrip); + NEWROW(QSlider); + NEWROW(QSpinBox); + NEWROW(QSplitter); + NEWROW(QStackedWidget); + NEWROW(QStatusBar); + NEWROW(QTabBar); + NEWROW(QTableView); + NEWROW(QTableWidget); + NEWROW(QTabWidget); + NEWROW(QTextBrowser); + NEWROW(QTextEdit); + NEWROW(QTimeEdit); + NEWROW(QToolBar); + NEWROW(QToolBox); + NEWROW(QToolButton); + NEWROW(QTreeView); + NEWROW(QTreeWidget); + NEWROW(QWorkspace); +} + +void tst_ExceptionSafety_Objects::widgets() +{ + QLatin1String tag = QLatin1String(QTest::currentDataTag()); + if (tag == QLatin1String("QColumnView") + || tag == QLatin1String("QComboBox") + || tag == QLatin1String("QCommandLinkButton") + || tag == QLatin1String("QDateEdit") + || tag == QLatin1String("QDateTimeEdit") + || tag == QLatin1String("QDesktopWidget") + || tag == QLatin1String("QDoubleSpinBox") + || tag == QLatin1String("QFontComboBox") + || tag == QLatin1String("QGroupBox") + || tag == QLatin1String("QLineEdit") + || tag == QLatin1String("QListView") + || tag == QLatin1String("QListWidget") + || tag == QLatin1String("QMainWindow") + || tag == QLatin1String("QMenu") + || tag == QLatin1String("QMenuBar") + || tag == QLatin1String("QPlainTextEdit") + || tag == QLatin1String("QProgressBar") + || tag == QLatin1String("QPushButton") + || tag == QLatin1String("QScrollArea") + || tag == QLatin1String("QSpinBox") + || tag == QLatin1String("QStackedWidget") + || tag == QLatin1String("QStatusBar") + || tag == QLatin1String("QTableView") + || tag == QLatin1String("QTableWidget") + || tag == QLatin1String("QTabWidget") + || tag == QLatin1String("QTextBrowser") + || tag == QLatin1String("QTextEdit") + || tag == QLatin1String("QTimeEdit") + || tag == QLatin1String("QToolBar") + || tag == QLatin1String("QToolBox") + || tag == QLatin1String("QTreeView") + || tag == QLatin1String("QTreeWidget") + || tag == QLatin1String("QWorkspace")) + QSKIP("This type of widget is not currently strongly exception safe", SkipSingle); + + QFETCH(AbstractTester *, widgetCreator); + + doOOMTest(*widgetCreator, 0, 00000); + + QWidget parent; + doOOMTest(*widgetCreator, &parent, 00000); + + delete widgetCreator; + + // if the test reaches here without crashing, we passed :) + QVERIFY(true); +} + +struct Integer +{ + Integer(int value = 42) + : ptr(new int(value)) + { + ++instanceCount; + } + + Integer(const Integer &other) + : ptr(new int(*other.ptr)) + { + ++instanceCount; + } + + Integer &operator=(const Integer &other) + { + int *newPtr = new int(*other.ptr); + delete ptr; + ptr = newPtr; + return *this; + } + + ~Integer() + { + --instanceCount; + delete ptr; + } + + int value() const + { + return *ptr; + } + + int *ptr; + static int instanceCount; +}; + +int Integer::instanceCount = 0; + +struct IntegerMoveable + { + IntegerMoveable(int value = 42) + : val(value) + { + delete new int; + ++instanceCount; + } + + IntegerMoveable(const IntegerMoveable &other) + : val(other.val) + { + delete new int; + ++instanceCount; + } + + IntegerMoveable &operator=(const IntegerMoveable &other) + { + delete new int; + val = other.val; + return *this; + } + + ~IntegerMoveable() + { + --instanceCount; + } + + int value() const + { + return val; + } + + int val; + static int instanceCount; + }; + +int IntegerMoveable::instanceCount = 0; +QT_BEGIN_NAMESPACE +Q_DECLARE_TYPEINFO(IntegerMoveable, Q_MOVABLE_TYPE); +QT_END_NAMESPACE + +template <typename T, template<typename> class Container> +void containerInsertTest(QObject*) +{ + Container<T> container; + + // insert an item in an empty container + try { + container.insert(container.begin(), 41); + } catch (...) { + QVERIFY(container.isEmpty()); + QCOMPARE(T::instanceCount, 0); + return; + } + + QCOMPARE(container.size(), 1); + QCOMPARE(T::instanceCount, 1); + + // insert an item before another item + try { + container.insert(container.begin(), 42); + } catch (...) { + QCOMPARE(container.size(), 1); + QCOMPARE(container.first().value(), 41); + QCOMPARE(T::instanceCount, 1); + return; + } + + QCOMPARE(T::instanceCount, 2); + + // insert an item in between + try { + container.insert(container.begin() + 1, 43); + } catch (...) { + QCOMPARE(container.size(), 2); + QCOMPARE(container.first().value(), 41); + QCOMPARE((container.begin() + 1)->value(), 42); + QCOMPARE(T::instanceCount, 2); + return; + } + + QCOMPARE(T::instanceCount, 3); +} + +template <typename T, template<typename> class Container> +void containerAppendTest(QObject*) +{ + Container<T> container; + + // append to an empty container + try { + container.append(42); + } catch (...) { + QCOMPARE(container.size(), 0); + QCOMPARE(T::instanceCount, 0); + return; + } + + // append to a container with one item + try { + container.append(43); + } catch (...) { + QCOMPARE(container.size(), 1); + QCOMPARE(container.first().value(), 42); + QCOMPARE(T::instanceCount, 1); + return; + } + + Container<T> container2; + + try { + container2.append(44); + } catch (...) { + // don't care + return; + } + QCOMPARE(T::instanceCount, 3); + + // append another container with one item + try { + container += container2; + } catch (...) { + QCOMPARE(container.size(), 2); + QCOMPARE(container.first().value(), 42); + QCOMPARE((container.begin() + 1)->value(), 43); + QCOMPARE(T::instanceCount, 3); + return; + } + + QCOMPARE(T::instanceCount, 4); +} + +template <typename T, template<typename> class Container> +void containerEraseTest(QObject*) +{ + Container<T> container; + + try { + container.append(42); + container.append(43); + container.append(44); + container.append(45); + container.append(46); + } catch (...) { + // don't care + return; + } + + // sanity checks + QCOMPARE(container.size(), 5); + QCOMPARE(T::instanceCount, 5); + + // delete the first one + try { + container.erase(container.begin()); + } catch (...) { + QCOMPARE(container.size(), 5); + QCOMPARE(container.first().value(), 42); + QCOMPARE(T::instanceCount, 5); + return; + } + + QCOMPARE(container.size(), 4); + QCOMPARE(container.first().value(), 43); + QCOMPARE(T::instanceCount, 4); + + // delete the last one + try { + container.erase(container.end() - 1); + } catch (...) { + QCOMPARE(container.size(), 4); + QCOMPARE(T::instanceCount, 4); + return; + } + + QCOMPARE(container.size(), 3); + QCOMPARE(container.first().value(), 43); + QCOMPARE((container.begin() + 1)->value(), 44); + QCOMPARE((container.begin() + 2)->value(), 45); + QCOMPARE(T::instanceCount, 3); + + // delete the middle one + try { + container.erase(container.begin() + 1); + } catch (...) { + QCOMPARE(container.size(), 3); + QCOMPARE(container.first().value(), 43); + QCOMPARE((container.begin() + 1)->value(), 44); + QCOMPARE((container.begin() + 2)->value(), 45); + QCOMPARE(T::instanceCount, 3); + return; + } + + QCOMPARE(container.size(), 2); + QCOMPARE(container.first().value(), 43); + QCOMPARE((container.begin() + 1)->value(), 45); + QCOMPARE(T::instanceCount, 2); +} + +template <template<typename T> class Container> +static void containerData() +{ + QTest::addColumn<TestFunction>("testFunction"); + + QTest::newRow("insert static") << static_cast<TestFunction>(containerInsertTest<Integer, Container>); + QTest::newRow("append static") << static_cast<TestFunction>(containerAppendTest<Integer, Container>); + QTest::newRow("erase static") << static_cast<TestFunction>(containerEraseTest<Integer, Container>); + QTest::newRow("insert moveable") << static_cast<TestFunction>(containerInsertTest<IntegerMoveable, Container>); + QTest::newRow("append moveable") << static_cast<TestFunction>(containerAppendTest<IntegerMoveable, Container>); + QTest::newRow("erase moveable") << static_cast<TestFunction>(containerEraseTest<IntegerMoveable, Container>); +} + +void tst_ExceptionSafety_Objects::vector_data() +{ + containerData<QVector>(); +} + +void tst_ExceptionSafety_Objects::vector() +{ + QFETCH(TestFunction, testFunction); + + if (QLatin1String(QTest::currentDataTag()) == QLatin1String("insert static") + || QLatin1String(QTest::currentDataTag()) == QLatin1String("insert moveable")) + QSKIP("QVector::insert is currently not strongly exception safe", SkipSingle); + + doOOMTest(testFunction, 0); +} + +void tst_ExceptionSafety_Objects::list_data() +{ + containerData<QList>(); +} + +void tst_ExceptionSafety_Objects::list() +{ + QFETCH(TestFunction, testFunction); + + doOOMTest(testFunction, 0); +} + +void tst_ExceptionSafety_Objects::linkedList_data() +{ + containerData<QLinkedList>(); +} + +void tst_ExceptionSafety_Objects::linkedList() +{ + QFETCH(TestFunction, testFunction); + + doOOMTest(testFunction, 0); +} + +#endif + +QTEST_MAIN(tst_ExceptionSafety_Objects) +#include "tst_exceptionsafety_objects.moc" diff --git a/tests/auto/integrationtests/gestures/gestures.pro b/tests/auto/integrationtests/gestures/gestures.pro new file mode 100644 index 0000000000..43ae4a3fa1 --- /dev/null +++ b/tests/auto/integrationtests/gestures/gestures.pro @@ -0,0 +1,5 @@ +load(qttest_p4) +QT += widgets +SOURCES += tst_gestures.cpp + +CONFIG += insignificant_test # QTBUG-21402 diff --git a/tests/auto/integrationtests/gestures/tst_gestures.cpp b/tests/auto/integrationtests/gestures/tst_gestures.cpp new file mode 100644 index 0000000000..63345fbe88 --- /dev/null +++ b/tests/auto/integrationtests/gestures/tst_gestures.cpp @@ -0,0 +1,2345 @@ +/**************************************************************************** +** +** 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 <QtTest/qtesttouch.h> + +#include <qevent.h> +#include <qwidget.h> +#include <qlayout.h> +#include <qgesture.h> +#include <qgesturerecognizer.h> +#include <qgraphicsitem.h> +#include <qgraphicswidget.h> +#include <qgraphicsview.h> +#include <qmainwindow.h> + +#include <qdebug.h> + +//TESTED_CLASS= +//TESTED_FILES= + +static QPointF mapToGlobal(const QPointF &pt, QGraphicsItem *item, QGraphicsView *view) +{ + return view->viewport()->mapToGlobal(view->mapFromScene(item->mapToScene(pt))); +} + +class CustomGesture : public QGesture +{ + Q_OBJECT +public: + static Qt::GestureType GestureType; + + CustomGesture(QObject *parent = 0) + : QGesture(parent), serial(0) + { + } + + int serial; + + static const int SerialMaybeThreshold; + static const int SerialStartedThreshold; + static const int SerialFinishedThreshold; +}; +Qt::GestureType CustomGesture::GestureType = Qt::CustomGesture; +const int CustomGesture::SerialMaybeThreshold = 1; +const int CustomGesture::SerialStartedThreshold = 3; +const int CustomGesture::SerialFinishedThreshold = 6; + +class CustomEvent : public QEvent +{ +public: + static int EventType; + + CustomEvent(int serial_ = 0) + : QEvent(QEvent::Type(CustomEvent::EventType)), + serial(serial_), hasHotSpot(false) + { + } + + int serial; + QPointF hotSpot; + bool hasHotSpot; +}; +int CustomEvent::EventType = 0; + +class CustomGestureRecognizer : public QGestureRecognizer +{ +public: + static bool ConsumeEvents; + + CustomGestureRecognizer() + { + if (!CustomEvent::EventType) + CustomEvent::EventType = QEvent::registerEventType(); + } + + QGesture* create(QObject *) + { + return new CustomGesture; + } + + QGestureRecognizer::Result recognize(QGesture *state, QObject*, QEvent *event) + { + if (event->type() == CustomEvent::EventType) { + QGestureRecognizer::Result result = 0; + if (CustomGestureRecognizer::ConsumeEvents) + result |= QGestureRecognizer::ConsumeEventHint; + CustomGesture *g = static_cast<CustomGesture*>(state); + CustomEvent *e = static_cast<CustomEvent*>(event); + g->serial = e->serial; + if (e->hasHotSpot) + g->setHotSpot(e->hotSpot); + if (g->serial >= CustomGesture::SerialFinishedThreshold) + result |= QGestureRecognizer::FinishGesture; + else if (g->serial >= CustomGesture::SerialStartedThreshold) + result |= QGestureRecognizer::TriggerGesture; + else if (g->serial >= CustomGesture::SerialMaybeThreshold) + result |= QGestureRecognizer::MayBeGesture; + else + result = QGestureRecognizer::CancelGesture; + return result; + } + return QGestureRecognizer::Ignore; + } + + void reset(QGesture *state) + { + CustomGesture *g = static_cast<CustomGesture *>(state); + g->serial = 0; + QGestureRecognizer::reset(state); + } +}; +bool CustomGestureRecognizer::ConsumeEvents = false; + +// same as CustomGestureRecognizer but triggers early without the maybe state +class CustomContinuousGestureRecognizer : public QGestureRecognizer +{ +public: + CustomContinuousGestureRecognizer() + { + if (!CustomEvent::EventType) + CustomEvent::EventType = QEvent::registerEventType(); + } + + QGesture* create(QObject *) + { + return new CustomGesture; + } + + QGestureRecognizer::Result recognize(QGesture *state, QObject*, QEvent *event) + { + if (event->type() == CustomEvent::EventType) { + QGestureRecognizer::Result result = QGestureRecognizer::ConsumeEventHint; + CustomGesture *g = static_cast<CustomGesture *>(state); + CustomEvent *e = static_cast<CustomEvent *>(event); + g->serial = e->serial; + if (e->hasHotSpot) + g->setHotSpot(e->hotSpot); + if (g->serial >= CustomGesture::SerialFinishedThreshold) + result |= QGestureRecognizer::FinishGesture; + else if (g->serial >= CustomGesture::SerialMaybeThreshold) + result |= QGestureRecognizer::TriggerGesture; + else + result = QGestureRecognizer::CancelGesture; + return result; + } + return QGestureRecognizer::Ignore; + } + + void reset(QGesture *state) + { + CustomGesture *g = static_cast<CustomGesture *>(state); + g->serial = 0; + QGestureRecognizer::reset(state); + } +}; + +class GestureWidget : public QWidget +{ + Q_OBJECT +public: + GestureWidget(const char *name = 0, QWidget *parent = 0) + : QWidget(parent) + { + if (name) + setObjectName(QLatin1String(name)); + reset(); + acceptGestureOverride = false; + } + void reset() + { + customEventsReceived = 0; + gestureEventsReceived = 0; + gestureOverrideEventsReceived = 0; + events.clear(); + overrideEvents.clear(); + ignoredGestures.clear(); + } + + int customEventsReceived; + int gestureEventsReceived; + int gestureOverrideEventsReceived; + struct Events + { + QList<Qt::GestureType> all; + QList<Qt::GestureType> started; + QList<Qt::GestureType> updated; + QList<Qt::GestureType> finished; + QList<Qt::GestureType> canceled; + + void clear() + { + all.clear(); + started.clear(); + updated.clear(); + finished.clear(); + canceled.clear(); + } + } events, overrideEvents; + + bool acceptGestureOverride; + QSet<Qt::GestureType> ignoredGestures; + +protected: + bool event(QEvent *event) + { + Events *eventsPtr = 0; + if (event->type() == QEvent::Gesture) { + QGestureEvent *e = static_cast<QGestureEvent*>(event); + ++gestureEventsReceived; + eventsPtr = &events; + foreach(Qt::GestureType type, ignoredGestures) + e->ignore(e->gesture(type)); + } else if (event->type() == QEvent::GestureOverride) { + ++gestureOverrideEventsReceived; + eventsPtr = &overrideEvents; + if (acceptGestureOverride) + event->accept(); + } + if (eventsPtr) { + QGestureEvent *e = static_cast<QGestureEvent*>(event); + QList<QGesture*> gestures = e->gestures(); + foreach(QGesture *g, gestures) { + eventsPtr->all << g->gestureType(); + switch(g->state()) { + case Qt::GestureStarted: + emit gestureStarted(e->type(), g); + eventsPtr->started << g->gestureType(); + break; + case Qt::GestureUpdated: + emit gestureUpdated(e->type(), g); + eventsPtr->updated << g->gestureType(); + break; + case Qt::GestureFinished: + emit gestureFinished(e->type(), g); + eventsPtr->finished << g->gestureType(); + break; + case Qt::GestureCanceled: + emit gestureCanceled(e->type(), g); + eventsPtr->canceled << g->gestureType(); + break; + default: + qWarning() << "Unknown GestureState enum value:" << static_cast<int>(g->state()); + } + } + } else if (event->type() == CustomEvent::EventType) { + ++customEventsReceived; + } else { + return QWidget::event(event); + } + return true; + } + +Q_SIGNALS: + void gestureStarted(QEvent::Type, QGesture *); + void gestureUpdated(QEvent::Type, QGesture *); + void gestureFinished(QEvent::Type, QGesture *); + void gestureCanceled(QEvent::Type, QGesture *); + +public Q_SLOTS: + void deleteThis() { delete this; } +}; + +// TODO rename to sendGestureSequence +static void sendCustomGesture(CustomEvent *event, QObject *object, QGraphicsScene *scene = 0) +{ + QWeakPointer<QObject> receiver(object); + for (int i = CustomGesture::SerialMaybeThreshold; + i <= CustomGesture::SerialFinishedThreshold && receiver; ++i) { + event->serial = i; + if (scene) + scene->sendEvent(qobject_cast<QGraphicsObject *>(object), event); + else + QApplication::sendEvent(object, event); + } +} + +class tst_Gestures : public QObject +{ +Q_OBJECT + +public: + tst_Gestures(); + virtual ~tst_Gestures(); + +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + +private slots: + void customGesture(); + void autoCancelingGestures(); + void gestureOverChild(); + void multipleWidgetOnlyGestureInTree(); + void conflictingGestures(); + void conflictingGesturesInGraphicsView(); + void finishedWithoutStarted(); + void unknownGesture(); + void graphicsItemGesture(); + void graphicsView(); + void graphicsItemTreeGesture(); + void explicitGraphicsObjectTarget(); + void gestureOverChildGraphicsItem(); + void twoGesturesOnDifferentLevel(); + void multipleGesturesInTree(); + void multipleGesturesInComplexTree(); + void testMapToScene(); + void ungrabGesture(); + void consumeEventHint(); + void unregisterRecognizer(); + void autoCancelGestures(); + void autoCancelGestures2(); + void graphicsViewParentPropagation(); + void panelPropagation(); + void panelStacksBehindParent(); + void deleteGestureTargetWidget(); + void deleteGestureTargetItem_data(); + void deleteGestureTargetItem(); + void viewportCoordinates(); + void partialGesturePropagation(); + void testQGestureRecognizerCleanup(); + void testReuseCanceledGestures(); + void bug_13501_gesture_not_accepted(); +}; + +tst_Gestures::tst_Gestures() +{ +} + +tst_Gestures::~tst_Gestures() +{ +} + +void tst_Gestures::initTestCase() +{ + CustomGesture::GestureType = QGestureRecognizer::registerRecognizer(new CustomGestureRecognizer); + QVERIFY(CustomGesture::GestureType != Qt::GestureType(0)); + QVERIFY(CustomGesture::GestureType != Qt::CustomGesture); +} + +void tst_Gestures::cleanupTestCase() +{ + QGestureRecognizer::unregisterRecognizer(CustomGesture::GestureType); +} + +void tst_Gestures::init() +{ +} + +void tst_Gestures::cleanup() +{ +} + +void tst_Gestures::customGesture() +{ + GestureWidget widget; + widget.grabGesture(CustomGesture::GestureType, Qt::DontStartGestureOnChildren); + widget.show(); + QTest::qWaitForWindowShown(&widget); + + CustomEvent event; + event.hotSpot = widget.mapToGlobal(QPoint(5,5)); + event.hasHotSpot = true; + sendCustomGesture(&event, &widget); + + static const int TotalGestureEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialStartedThreshold + 1; + static const int TotalCustomEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialMaybeThreshold + 1; + QCOMPARE(widget.customEventsReceived, TotalCustomEventsCount); + QCOMPARE(widget.gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(widget.gestureOverrideEventsReceived, 0); + QCOMPARE(widget.events.all.size(), TotalGestureEventsCount); + for(int i = 0; i < widget.events.all.size(); ++i) + QCOMPARE(widget.events.all.at(i), CustomGesture::GestureType); + QCOMPARE(widget.events.started.size(), 1); + QCOMPARE(widget.events.updated.size(), TotalGestureEventsCount - 2); + QCOMPARE(widget.events.finished.size(), 1); + QCOMPARE(widget.events.canceled.size(), 0); +} + +void tst_Gestures::consumeEventHint() +{ + GestureWidget widget; + widget.grabGesture(CustomGesture::GestureType, Qt::DontStartGestureOnChildren); + + CustomGestureRecognizer::ConsumeEvents = true; + CustomEvent event; + sendCustomGesture(&event, &widget); + CustomGestureRecognizer::ConsumeEvents = false; + + QCOMPARE(widget.customEventsReceived, 0); +} + +void tst_Gestures::autoCancelingGestures() +{ + GestureWidget widget; + widget.grabGesture(CustomGesture::GestureType, Qt::DontStartGestureOnChildren); + // send partial gesture. The gesture will be in the "maybe" state, but will + // never get enough events to fire, so Qt will have to kill it. + CustomEvent ev; + for (int i = CustomGesture::SerialMaybeThreshold; + i < CustomGesture::SerialStartedThreshold; ++i) { + ev.serial = i; + QApplication::sendEvent(&widget, &ev); + } + // wait long enough so the gesture manager will cancel the gesture + QTest::qWait(5000); + QCOMPARE(widget.customEventsReceived, CustomGesture::SerialStartedThreshold - CustomGesture::SerialMaybeThreshold); + QCOMPARE(widget.gestureEventsReceived, 0); + QCOMPARE(widget.gestureOverrideEventsReceived, 0); + QCOMPARE(widget.events.all.size(), 0); +} + +void tst_Gestures::gestureOverChild() +{ + GestureWidget widget("widget"); + QVBoxLayout *l = new QVBoxLayout(&widget); + GestureWidget *child = new GestureWidget("child"); + l->addWidget(child); + + widget.grabGesture(CustomGesture::GestureType, Qt::DontStartGestureOnChildren); + + CustomEvent event; + sendCustomGesture(&event, child); + + static const int TotalGestureEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialStartedThreshold + 1; + static const int TotalCustomEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialMaybeThreshold + 1; + + QCOMPARE(child->customEventsReceived, TotalCustomEventsCount); + QCOMPARE(widget.customEventsReceived, 0); + QCOMPARE(child->gestureEventsReceived, 0); + QCOMPARE(child->gestureOverrideEventsReceived, 0); + QCOMPARE(widget.gestureEventsReceived, 0); + QCOMPARE(widget.gestureOverrideEventsReceived, 0); + + // enable gestures over the children + widget.grabGesture(CustomGesture::GestureType); + + widget.reset(); + child->reset(); + + sendCustomGesture(&event, child); + + QCOMPARE(child->customEventsReceived, TotalCustomEventsCount); + QCOMPARE(widget.customEventsReceived, 0); + + QCOMPARE(child->gestureEventsReceived, 0); + QCOMPARE(child->gestureOverrideEventsReceived, 0); + QCOMPARE(widget.gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(widget.gestureOverrideEventsReceived, 0); + for(int i = 0; i < widget.events.all.size(); ++i) + QCOMPARE(widget.events.all.at(i), CustomGesture::GestureType); + QCOMPARE(widget.events.started.size(), 1); + QCOMPARE(widget.events.updated.size(), TotalGestureEventsCount - 2); + QCOMPARE(widget.events.finished.size(), 1); + QCOMPARE(widget.events.canceled.size(), 0); +} + +void tst_Gestures::multipleWidgetOnlyGestureInTree() +{ + GestureWidget parent("parent"); + QVBoxLayout *l = new QVBoxLayout(&parent); + GestureWidget *child = new GestureWidget("child"); + l->addWidget(child); + + parent.grabGesture(CustomGesture::GestureType, Qt::DontStartGestureOnChildren); + child->grabGesture(CustomGesture::GestureType, Qt::DontStartGestureOnChildren); + + static const int TotalGestureEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialStartedThreshold + 1; + static const int TotalCustomEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialMaybeThreshold + 1; + + // sending events to the child and making sure there is no conflict + CustomEvent event; + sendCustomGesture(&event, child); + + QCOMPARE(child->customEventsReceived, TotalCustomEventsCount); + QCOMPARE(parent.customEventsReceived, 0); + QCOMPARE(child->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(child->gestureOverrideEventsReceived, 0); + QCOMPARE(parent.gestureEventsReceived, 0); + QCOMPARE(parent.gestureOverrideEventsReceived, 0); + + parent.reset(); + child->reset(); + + // same for the parent widget + sendCustomGesture(&event, &parent); + + QCOMPARE(child->customEventsReceived, 0); + QCOMPARE(parent.customEventsReceived, TotalCustomEventsCount); + QCOMPARE(child->gestureEventsReceived, 0); + QCOMPARE(child->gestureOverrideEventsReceived, 0); + QCOMPARE(parent.gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(parent.gestureOverrideEventsReceived, 0); +} + +void tst_Gestures::conflictingGestures() +{ + GestureWidget parent("parent"); + QVBoxLayout *l = new QVBoxLayout(&parent); + GestureWidget *child = new GestureWidget("child"); + l->addWidget(child); + + parent.grabGesture(CustomGesture::GestureType); + child->grabGesture(CustomGesture::GestureType); + + static const int TotalGestureEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialStartedThreshold + 1; + + // child accepts the override, parent will not receive anything + parent.acceptGestureOverride = false; + child->acceptGestureOverride = true; + + // sending events to the child and making sure there is no conflict + CustomEvent event; + sendCustomGesture(&event, child); + + QCOMPARE(child->gestureOverrideEventsReceived, 1); + QCOMPARE(child->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(parent.gestureOverrideEventsReceived, 0); + QCOMPARE(parent.gestureEventsReceived, 0); + + parent.reset(); + child->reset(); + + // parent accepts the override + parent.acceptGestureOverride = true; + child->acceptGestureOverride = false; + + // sending events to the child and making sure there is no conflict + sendCustomGesture(&event, child); + + QCOMPARE(child->gestureOverrideEventsReceived, 1); + QCOMPARE(child->gestureEventsReceived, 0); + QCOMPARE(parent.gestureOverrideEventsReceived, 1); + QCOMPARE(parent.gestureEventsReceived, TotalGestureEventsCount); + + parent.reset(); + child->reset(); + + // nobody accepts the override, we will send normal events to the closest + // context (i.e. to the child widget) and it will be propagated and + // accepted by the parent widget + parent.acceptGestureOverride = false; + child->acceptGestureOverride = false; + child->ignoredGestures << CustomGesture::GestureType; + + // sending events to the child and making sure there is no conflict + sendCustomGesture(&event, child); + + QCOMPARE(child->gestureOverrideEventsReceived, 1); + QCOMPARE(child->gestureEventsReceived, 1); + QCOMPARE(parent.gestureOverrideEventsReceived, 1); + QCOMPARE(parent.gestureEventsReceived, TotalGestureEventsCount); + + parent.reset(); + child->reset(); + + // nobody accepts the override, and nobody accepts the gesture event + parent.acceptGestureOverride = false; + child->acceptGestureOverride = false; + parent.ignoredGestures << CustomGesture::GestureType; + child->ignoredGestures << CustomGesture::GestureType; + + // sending events to the child and making sure there is no conflict + sendCustomGesture(&event, child); + + QCOMPARE(child->gestureOverrideEventsReceived, 1); + QCOMPARE(child->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(parent.gestureOverrideEventsReceived, 1); + QCOMPARE(parent.gestureEventsReceived, 1); + + parent.reset(); + child->reset(); + + // we set an attribute to make sure all gesture events are propagated + parent.grabGesture(CustomGesture::GestureType, Qt::ReceivePartialGestures); + parent.acceptGestureOverride = false; + child->acceptGestureOverride = false; + parent.ignoredGestures << CustomGesture::GestureType; + child->ignoredGestures << CustomGesture::GestureType; + + // sending events to the child and making sure there is no conflict + sendCustomGesture(&event, child); + + QCOMPARE(child->gestureOverrideEventsReceived, 1); + QCOMPARE(child->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(parent.gestureOverrideEventsReceived, 1); + QCOMPARE(parent.gestureEventsReceived, TotalGestureEventsCount); + + parent.reset(); + child->reset(); + + Qt::GestureType ContinuousGesture = QGestureRecognizer::registerRecognizer(new CustomContinuousGestureRecognizer); + static const int ContinuousGestureEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialMaybeThreshold + 1; + child->grabGesture(ContinuousGesture); + // child accepts override. And it also receives another custom gesture. + parent.acceptGestureOverride = false; + child->acceptGestureOverride = true; + sendCustomGesture(&event, child); + + QCOMPARE(child->gestureOverrideEventsReceived, 1); + QVERIFY(child->gestureEventsReceived > TotalGestureEventsCount); + QCOMPARE(child->events.all.count(), TotalGestureEventsCount + ContinuousGestureEventsCount); + QCOMPARE(parent.gestureOverrideEventsReceived, 0); + QCOMPARE(parent.gestureEventsReceived, 0); + + QGestureRecognizer::unregisterRecognizer(ContinuousGesture); +} + +void tst_Gestures::finishedWithoutStarted() +{ + GestureWidget widget; + widget.grabGesture(CustomGesture::GestureType, Qt::DontStartGestureOnChildren); + + // the gesture will claim it finished, but it was never started. + CustomEvent ev; + ev.serial = CustomGesture::SerialFinishedThreshold; + QApplication::sendEvent(&widget, &ev); + + QCOMPARE(widget.customEventsReceived, 1); + QCOMPARE(widget.gestureEventsReceived, 2); + QCOMPARE(widget.gestureOverrideEventsReceived, 0); + QCOMPARE(widget.events.all.size(), 2); + QCOMPARE(widget.events.started.size(), 1); + QCOMPARE(widget.events.updated.size(), 0); + QCOMPARE(widget.events.finished.size(), 1); + QCOMPARE(widget.events.canceled.size(), 0); +} + +void tst_Gestures::unknownGesture() +{ + GestureWidget widget; + widget.grabGesture(CustomGesture::GestureType, Qt::DontStartGestureOnChildren); + widget.grabGesture(Qt::CustomGesture, Qt::DontStartGestureOnChildren); + widget.grabGesture(Qt::GestureType(Qt::PanGesture+512), Qt::DontStartGestureOnChildren); + + CustomEvent event; + sendCustomGesture(&event, &widget); + + static const int TotalGestureEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialStartedThreshold + 1; + + QCOMPARE(widget.gestureEventsReceived, TotalGestureEventsCount); +} + +static const QColor InstanceColors[] = { + Qt::blue, Qt::red, Qt::green, Qt::gray, Qt::yellow +}; + +class GestureItem : public QGraphicsObject +{ + Q_OBJECT + static int InstanceCount; +public: + GestureItem(const char *name = 0) + { + instanceNumber = InstanceCount++; + if (name) { + setObjectName(QLatin1String(name)); + setToolTip(name); + } + size = QRectF(0, 0, 100, 100); + customEventsReceived = 0; + gestureEventsReceived = 0; + gestureOverrideEventsReceived = 0; + events.clear(); + overrideEvents.clear(); + acceptGestureOverride = false; + + scene = 0; + } + ~GestureItem() + { + --InstanceCount; + } + + int customEventsReceived; + int gestureEventsReceived; + int gestureOverrideEventsReceived; + struct Events + { + QList<Qt::GestureType> all; + QList<Qt::GestureType> started; + QList<Qt::GestureType> updated; + QList<Qt::GestureType> finished; + QList<Qt::GestureType> canceled; + + void clear() + { + all.clear(); + started.clear(); + updated.clear(); + finished.clear(); + canceled.clear(); + } + } events, overrideEvents; + + bool acceptGestureOverride; + QSet<Qt::GestureType> ignoredGestures; + QSet<Qt::GestureType> ignoredStartedGestures; + QSet<Qt::GestureType> ignoredUpdatedGestures; + QSet<Qt::GestureType> ignoredFinishedGestures; + + QRectF size; + int instanceNumber; + + void reset() + { + customEventsReceived = 0; + gestureEventsReceived = 0; + gestureOverrideEventsReceived = 0; + events.clear(); + overrideEvents.clear(); + ignoredGestures.clear(); + ignoredStartedGestures.clear(); + ignoredUpdatedGestures.clear(); + ignoredFinishedGestures.clear(); + } + + QRectF boundingRect() const + { + return size; + } + void paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *) + { + QColor color = InstanceColors[instanceNumber % (sizeof(InstanceColors)/sizeof(InstanceColors[0]))]; + p->fillRect(boundingRect(), color); + } + + bool event(QEvent *event) + { + Events *eventsPtr = 0; + if (event->type() == QEvent::Gesture) { + ++gestureEventsReceived; + eventsPtr = &events; + QGestureEvent *e = static_cast<QGestureEvent *>(event); + foreach(Qt::GestureType type, ignoredGestures) + e->ignore(e->gesture(type)); + foreach(QGesture *g, e->gestures()) { + switch (g->state()) { + case Qt::GestureStarted: + if (ignoredStartedGestures.contains(g->gestureType())) + e->ignore(g); + break; + case Qt::GestureUpdated: + if (ignoredUpdatedGestures.contains(g->gestureType())) + e->ignore(g); + break; + case Qt::GestureFinished: + if (ignoredFinishedGestures.contains(g->gestureType())) + e->ignore(g); + break; + default: + break; + } + } + } else if (event->type() == QEvent::GestureOverride) { + ++gestureOverrideEventsReceived; + eventsPtr = &overrideEvents; + if (acceptGestureOverride) + event->accept(); + } + if (eventsPtr) { + QGestureEvent *e = static_cast<QGestureEvent*>(event); + QList<QGesture*> gestures = e->gestures(); + foreach(QGesture *g, gestures) { + eventsPtr->all << g->gestureType(); + switch(g->state()) { + case Qt::GestureStarted: + eventsPtr->started << g->gestureType(); + emit gestureStarted(e->type(), g); + break; + case Qt::GestureUpdated: + eventsPtr->updated << g->gestureType(); + emit gestureUpdated(e->type(), g); + break; + case Qt::GestureFinished: + eventsPtr->finished << g->gestureType(); + emit gestureFinished(e->type(), g); + break; + case Qt::GestureCanceled: + eventsPtr->canceled << g->gestureType(); + emit gestureCanceled(e->type(), g); + break; + default: + qWarning() << "Unknown GestureState enum value:" << static_cast<int>(g->state()); + } + } + } else if (event->type() == CustomEvent::EventType) { + ++customEventsReceived; + } else { + return QGraphicsObject::event(event); + } + return true; + } + +Q_SIGNALS: + void gestureStarted(QEvent::Type, QGesture *); + void gestureUpdated(QEvent::Type, QGesture *); + void gestureFinished(QEvent::Type, QGesture *); + void gestureCanceled(QEvent::Type, QGesture *); + +public: + // some arguments for the slots below: + QGraphicsScene *scene; + +public Q_SLOTS: + void deleteThis() { delete this; } + void addSelfToScene(QEvent::Type eventType, QGesture *) + { + if (eventType == QEvent::Gesture) { + disconnect(sender(), 0, this, SLOT(addSelfToScene(QEvent::Type,QGesture*))); + scene->addItem(this); + } + } +}; +int GestureItem::InstanceCount = 0; + +void tst_Gestures::graphicsItemGesture() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + view.setWindowFlags(Qt::X11BypassWindowManagerHint); + + GestureItem *item = new GestureItem("item"); + scene.addItem(item); + item->setPos(100, 100); + + view.show(); + QTest::qWaitForWindowShown(&view); + view.ensureVisible(scene.sceneRect()); + + item->grabGesture(CustomGesture::GestureType); + + static const int TotalGestureEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialStartedThreshold + 1; + static const int TotalCustomEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialMaybeThreshold + 1; + + CustomEvent event; + // gesture without hotspot should not be delivered to items in the view + QTest::ignoreMessage(QtWarningMsg, "QGestureManager::deliverEvent: could not find the target for gesture"); + QTest::ignoreMessage(QtWarningMsg, "QGestureManager::deliverEvent: could not find the target for gesture"); + QTest::ignoreMessage(QtWarningMsg, "QGestureManager::deliverEvent: could not find the target for gesture"); + QTest::ignoreMessage(QtWarningMsg, "QGestureManager::deliverEvent: could not find the target for gesture"); + sendCustomGesture(&event, item, &scene); + + QCOMPARE(item->customEventsReceived, TotalCustomEventsCount); + QCOMPARE(item->gestureEventsReceived, 0); + QCOMPARE(item->gestureOverrideEventsReceived, 0); + + item->reset(); + + // make sure the event is properly delivered if only the hotspot is set. + event.hotSpot = mapToGlobal(QPointF(10, 10), item, &view); + event.hasHotSpot = true; + sendCustomGesture(&event, item, &scene); + + QCOMPARE(item->customEventsReceived, TotalCustomEventsCount); + QCOMPARE(item->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(item->gestureOverrideEventsReceived, 0); + QCOMPARE(item->events.all.size(), TotalGestureEventsCount); + for(int i = 0; i < item->events.all.size(); ++i) + QCOMPARE(item->events.all.at(i), CustomGesture::GestureType); + QCOMPARE(item->events.started.size(), 1); + QCOMPARE(item->events.updated.size(), TotalGestureEventsCount - 2); + QCOMPARE(item->events.finished.size(), 1); + QCOMPARE(item->events.canceled.size(), 0); + + item->reset(); + + // send gesture to the item which ignores it. + item->ignoredGestures << CustomGesture::GestureType; + + event.hotSpot = mapToGlobal(QPointF(10, 10), item, &view); + event.hasHotSpot = true; + sendCustomGesture(&event, item, &scene); + QCOMPARE(item->customEventsReceived, TotalCustomEventsCount); + QCOMPARE(item->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(item->gestureOverrideEventsReceived, 0); +} + +void tst_Gestures::graphicsView() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + view.setWindowFlags(Qt::X11BypassWindowManagerHint); + + GestureItem *item = new GestureItem("item"); + scene.addItem(item); + item->setPos(100, 100); + + view.show(); + QTest::qWaitForWindowShown(&view); + view.ensureVisible(scene.sceneRect()); + + item->grabGesture(CustomGesture::GestureType); + + static const int TotalGestureEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialStartedThreshold + 1; + static const int TotalCustomEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialMaybeThreshold + 1; + + CustomEvent event; + // make sure the event is properly delivered if only the hotspot is set. + event.hotSpot = mapToGlobal(QPointF(10, 10), item, &view); + event.hasHotSpot = true; + sendCustomGesture(&event, item, &scene); + + QCOMPARE(item->customEventsReceived, TotalCustomEventsCount); + QCOMPARE(item->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(item->gestureOverrideEventsReceived, 0); + + // change the viewport and try again + QWidget *newViewport = new QWidget; + view.setViewport(newViewport); + + item->reset(); + sendCustomGesture(&event, item, &scene); + + QCOMPARE(item->customEventsReceived, TotalCustomEventsCount); + QCOMPARE(item->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(item->gestureOverrideEventsReceived, 0); + + // change the scene and try again + QGraphicsScene newScene; + item = new GestureItem("newItem"); + newScene.addItem(item); + item->setPos(100, 100); + view.setScene(&newScene); + + item->reset(); + // first without a gesture + sendCustomGesture(&event, item, &newScene); + + QCOMPARE(item->customEventsReceived, TotalCustomEventsCount); + QCOMPARE(item->gestureEventsReceived, 0); + QCOMPARE(item->gestureOverrideEventsReceived, 0); + + // then grab the gesture and try again + item->reset(); + item->grabGesture(CustomGesture::GestureType); + sendCustomGesture(&event, item, &newScene); + + QCOMPARE(item->customEventsReceived, TotalCustomEventsCount); + QCOMPARE(item->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(item->gestureOverrideEventsReceived, 0); +} + +void tst_Gestures::graphicsItemTreeGesture() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + view.setWindowFlags(Qt::X11BypassWindowManagerHint); + + GestureItem *item1 = new GestureItem("item1"); + item1->setPos(100, 100); + item1->size = QRectF(0, 0, 350, 200); + scene.addItem(item1); + + GestureItem *item1_child1 = new GestureItem("item1_child1"); + item1_child1->setPos(50, 50); + item1_child1->size = QRectF(0, 0, 100, 100); + item1_child1->setParentItem(item1); + + GestureItem *item1_child2 = new GestureItem("item1_child2"); + item1_child2->size = QRectF(0, 0, 100, 100); + item1_child2->setPos(200, 50); + item1_child2->setParentItem(item1); + + view.show(); + QTest::qWaitForWindowShown(&view); + view.ensureVisible(scene.sceneRect()); + + item1->grabGesture(CustomGesture::GestureType); + + static const int TotalGestureEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialStartedThreshold + 1; + + CustomEvent event; + event.hotSpot = mapToGlobal(QPointF(10, 10), item1_child1, &view); + event.hasHotSpot = true; + + item1->ignoredGestures << CustomGesture::GestureType; + sendCustomGesture(&event, item1_child1, &scene); + QCOMPARE(item1_child1->gestureOverrideEventsReceived, 0); + QCOMPARE(item1_child1->gestureEventsReceived, 0); + QCOMPARE(item1_child2->gestureEventsReceived, 0); + QCOMPARE(item1_child2->gestureOverrideEventsReceived, 0); + QCOMPARE(item1->gestureOverrideEventsReceived, 0); + QCOMPARE(item1->gestureEventsReceived, TotalGestureEventsCount); + + item1->reset(); item1_child1->reset(); item1_child2->reset(); + + item1_child1->grabGesture(CustomGesture::GestureType); + + item1->ignoredGestures << CustomGesture::GestureType; + item1_child1->ignoredGestures << CustomGesture::GestureType; + sendCustomGesture(&event, item1_child1, &scene); + QCOMPARE(item1_child1->gestureOverrideEventsReceived, 1); + QCOMPARE(item1_child1->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(item1_child2->gestureEventsReceived, 0); + QCOMPARE(item1_child2->gestureOverrideEventsReceived, 0); + QCOMPARE(item1->gestureOverrideEventsReceived, 1); + QCOMPARE(item1->gestureEventsReceived, 1); +} + +void tst_Gestures::explicitGraphicsObjectTarget() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + view.setWindowFlags(Qt::X11BypassWindowManagerHint); + + GestureItem *item1 = new GestureItem("item1"); + scene.addItem(item1); + item1->setPos(100, 100); + item1->setZValue(1); + + GestureItem *item2 = new GestureItem("item2"); + scene.addItem(item2); + item2->setPos(100, 100); + item2->setZValue(5); + + GestureItem *item2_child1 = new GestureItem("item2_child1"); + scene.addItem(item2_child1); + item2_child1->setParentItem(item2); + item2_child1->setPos(10, 10); + + view.show(); + QTest::qWaitForWindowShown(&view); + view.ensureVisible(scene.sceneRect()); + + item1->grabGesture(CustomGesture::GestureType, Qt::DontStartGestureOnChildren); + item2->grabGesture(CustomGesture::GestureType, Qt::DontStartGestureOnChildren); + item2_child1->grabGesture(CustomGesture::GestureType, Qt::DontStartGestureOnChildren); + + static const int TotalGestureEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialStartedThreshold + 1; + + // sending events to item1, but the hotSpot is set to item2 + CustomEvent event; + event.hotSpot = mapToGlobal(QPointF(15, 15), item2, &view); + event.hasHotSpot = true; + + sendCustomGesture(&event, item1, &scene); + + QCOMPARE(item1->gestureEventsReceived, 0); + QCOMPARE(item1->gestureOverrideEventsReceived, 1); + QCOMPARE(item2_child1->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(item2_child1->gestureOverrideEventsReceived, 1); + QCOMPARE(item2_child1->events.all.size(), TotalGestureEventsCount); + for(int i = 0; i < item2_child1->events.all.size(); ++i) + QCOMPARE(item2_child1->events.all.at(i), CustomGesture::GestureType); + QCOMPARE(item2_child1->events.started.size(), 1); + QCOMPARE(item2_child1->events.updated.size(), TotalGestureEventsCount - 2); + QCOMPARE(item2_child1->events.finished.size(), 1); + QCOMPARE(item2_child1->events.canceled.size(), 0); + QCOMPARE(item2->gestureEventsReceived, 0); + QCOMPARE(item2->gestureOverrideEventsReceived, 1); +} + +void tst_Gestures::gestureOverChildGraphicsItem() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + view.setWindowFlags(Qt::X11BypassWindowManagerHint); + + GestureItem *item0 = new GestureItem("item0"); + scene.addItem(item0); + item0->setPos(0, 0); + item0->grabGesture(CustomGesture::GestureType); + item0->setZValue(1); + + GestureItem *item1 = new GestureItem("item1"); + scene.addItem(item1); + item1->setPos(100, 100); + item1->setZValue(5); + + GestureItem *item2 = new GestureItem("item2"); + scene.addItem(item2); + item2->setPos(100, 100); + item2->setZValue(10); + + GestureItem *item2_child1 = new GestureItem("item2_child1"); + scene.addItem(item2_child1); + item2_child1->setParentItem(item2); + item2_child1->setPos(0, 0); + + view.show(); + QTest::qWaitForWindowShown(&view); + view.ensureVisible(scene.sceneRect()); + + item1->grabGesture(CustomGesture::GestureType); + + static const int TotalGestureEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialStartedThreshold + 1; + static const int TotalCustomEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialMaybeThreshold + 1; + + CustomEvent event; + event.hotSpot = mapToGlobal(QPointF(10, 10), item2_child1, &view); + event.hasHotSpot = true; + sendCustomGesture(&event, item0, &scene); + + QCOMPARE(item0->customEventsReceived, TotalCustomEventsCount); + QCOMPARE(item2_child1->gestureEventsReceived, 0); + QCOMPARE(item2_child1->gestureOverrideEventsReceived, 0); + QCOMPARE(item2->gestureEventsReceived, 0); + QCOMPARE(item2->gestureOverrideEventsReceived, 0); + QCOMPARE(item1->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(item1->gestureOverrideEventsReceived, 0); + + item0->reset(); item1->reset(); item2->reset(); item2_child1->reset(); + item2->grabGesture(CustomGesture::GestureType); + item2->ignoredGestures << CustomGesture::GestureType; + + event.hotSpot = mapToGlobal(QPointF(10, 10), item2_child1, &view); + event.hasHotSpot = true; + sendCustomGesture(&event, item0, &scene); + + QCOMPARE(item2_child1->gestureEventsReceived, 0); + QCOMPARE(item2_child1->gestureOverrideEventsReceived, 0); + QCOMPARE(item2->gestureEventsReceived, 1); + QCOMPARE(item2->gestureOverrideEventsReceived, 1); + QCOMPARE(item1->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(item1->gestureOverrideEventsReceived, 1); + + item0->reset(); item1->reset(); item2->reset(); item2_child1->reset(); + item2->grabGesture(CustomGesture::GestureType); + item2->ignoredGestures << CustomGesture::GestureType; + item1->ignoredGestures << CustomGesture::GestureType; + + event.hotSpot = mapToGlobal(QPointF(10, 10), item2_child1, &view); + event.hasHotSpot = true; + sendCustomGesture(&event, item0, &scene); + + QCOMPARE(item2_child1->gestureEventsReceived, 0); + QCOMPARE(item2_child1->gestureOverrideEventsReceived, 0); + QCOMPARE(item2->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(item2->gestureOverrideEventsReceived, 1); + QCOMPARE(item1->gestureEventsReceived, 1); + QCOMPARE(item1->gestureOverrideEventsReceived, 1); + + item0->reset(); item1->reset(); item2->reset(); item2_child1->reset(); + item2->grabGesture(CustomGesture::GestureType); + item2->ignoredGestures << CustomGesture::GestureType; + item1->ignoredGestures << CustomGesture::GestureType; + item1->grabGesture(CustomGesture::GestureType, Qt::ReceivePartialGestures); + + event.hotSpot = mapToGlobal(QPointF(10, 10), item2_child1, &view); + event.hasHotSpot = true; + sendCustomGesture(&event, item0, &scene); + + QCOMPARE(item2_child1->gestureEventsReceived, 0); + QCOMPARE(item2_child1->gestureOverrideEventsReceived, 0); + QCOMPARE(item2->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(item2->gestureOverrideEventsReceived, 1); + QCOMPARE(item1->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(item1->gestureOverrideEventsReceived, 1); +} + +void tst_Gestures::twoGesturesOnDifferentLevel() +{ + GestureWidget parent("parent"); + QVBoxLayout *l = new QVBoxLayout(&parent); + GestureWidget *child = new GestureWidget("child"); + l->addWidget(child); + + Qt::GestureType SecondGesture = QGestureRecognizer::registerRecognizer(new CustomGestureRecognizer); + + parent.grabGesture(CustomGesture::GestureType); + child->grabGesture(SecondGesture); + + CustomEvent event; + // sending events that form a gesture to one widget, but they will be + // filtered by two different gesture recognizers and will generate two + // QGesture objects. Check that those gesture objects are delivered to + // different widgets properly. + sendCustomGesture(&event, child); + + static const int TotalGestureEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialStartedThreshold + 1; + static const int TotalCustomEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialMaybeThreshold + 1; + + QCOMPARE(child->customEventsReceived, TotalCustomEventsCount); + QCOMPARE(child->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(child->gestureOverrideEventsReceived, 0); + QCOMPARE(child->events.all.size(), TotalGestureEventsCount); + for(int i = 0; i < child->events.all.size(); ++i) + QCOMPARE(child->events.all.at(i), SecondGesture); + + QCOMPARE(parent.gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(parent.gestureOverrideEventsReceived, 0); + QCOMPARE(parent.events.all.size(), TotalGestureEventsCount); + for(int i = 0; i < child->events.all.size(); ++i) + QCOMPARE(parent.events.all.at(i), CustomGesture::GestureType); + + QGestureRecognizer::unregisterRecognizer(SecondGesture); +} + +void tst_Gestures::multipleGesturesInTree() +{ + GestureWidget a("A"); + GestureWidget *A = &a; + GestureWidget *B = new GestureWidget("B", A); + GestureWidget *C = new GestureWidget("C", B); + GestureWidget *D = new GestureWidget("D", C); + + Qt::GestureType FirstGesture = CustomGesture::GestureType; + Qt::GestureType SecondGesture = QGestureRecognizer::registerRecognizer(new CustomGestureRecognizer); + Qt::GestureType ThirdGesture = QGestureRecognizer::registerRecognizer(new CustomGestureRecognizer); + + Qt::GestureFlags flags = Qt::ReceivePartialGestures; + A->grabGesture(FirstGesture, flags); // A [1 3] + A->grabGesture(ThirdGesture, flags); // | + B->grabGesture(SecondGesture, flags); // B [ 2 3] + B->grabGesture(ThirdGesture, flags); // | + C->grabGesture(FirstGesture, flags); // C [1 2 3] + C->grabGesture(SecondGesture, flags); // | + C->grabGesture(ThirdGesture, flags); // D [1 3] + D->grabGesture(FirstGesture, flags); + D->grabGesture(ThirdGesture, flags); + + // make sure all widgets ignore events, so they get propagated. + A->ignoredGestures << FirstGesture << ThirdGesture; + B->ignoredGestures << SecondGesture << ThirdGesture; + C->ignoredGestures << FirstGesture << SecondGesture << ThirdGesture; + D->ignoredGestures << FirstGesture << ThirdGesture; + + CustomEvent event; + sendCustomGesture(&event, D); + + static const int TotalGestureEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialStartedThreshold + 1; + + // gesture override events + QCOMPARE(D->overrideEvents.all.count(FirstGesture), 1); + QCOMPARE(D->overrideEvents.all.count(SecondGesture), 0); + QCOMPARE(D->overrideEvents.all.count(ThirdGesture), 1); + + QCOMPARE(C->overrideEvents.all.count(FirstGesture), 1); + QCOMPARE(C->overrideEvents.all.count(SecondGesture), 1); + QCOMPARE(C->overrideEvents.all.count(ThirdGesture), 1); + + QCOMPARE(B->overrideEvents.all.count(FirstGesture), 0); + QCOMPARE(B->overrideEvents.all.count(SecondGesture), 1); + QCOMPARE(B->overrideEvents.all.count(ThirdGesture), 1); + + QCOMPARE(A->overrideEvents.all.count(FirstGesture), 1); + QCOMPARE(A->overrideEvents.all.count(SecondGesture), 0); + QCOMPARE(A->overrideEvents.all.count(ThirdGesture), 1); + + // normal gesture events + QCOMPARE(D->events.all.count(FirstGesture), TotalGestureEventsCount); + QCOMPARE(D->events.all.count(SecondGesture), 0); + QCOMPARE(D->events.all.count(ThirdGesture), TotalGestureEventsCount); + + QCOMPARE(C->events.all.count(FirstGesture), TotalGestureEventsCount); + QCOMPARE(C->events.all.count(SecondGesture), TotalGestureEventsCount); + QCOMPARE(C->events.all.count(ThirdGesture), TotalGestureEventsCount); + + QCOMPARE(B->events.all.count(FirstGesture), 0); + QCOMPARE(B->events.all.count(SecondGesture), TotalGestureEventsCount); + QCOMPARE(B->events.all.count(ThirdGesture), TotalGestureEventsCount); + + QCOMPARE(A->events.all.count(FirstGesture), TotalGestureEventsCount); + QCOMPARE(A->events.all.count(SecondGesture), 0); + QCOMPARE(A->events.all.count(ThirdGesture), TotalGestureEventsCount); + + QGestureRecognizer::unregisterRecognizer(SecondGesture); + QGestureRecognizer::unregisterRecognizer(ThirdGesture); +} + +void tst_Gestures::multipleGesturesInComplexTree() +{ + GestureWidget a("A"); + GestureWidget *A = &a; + GestureWidget *B = new GestureWidget("B", A); + GestureWidget *C = new GestureWidget("C", B); + GestureWidget *D = new GestureWidget("D", C); + + Qt::GestureType FirstGesture = CustomGesture::GestureType; + Qt::GestureType SecondGesture = QGestureRecognizer::registerRecognizer(new CustomGestureRecognizer); + Qt::GestureType ThirdGesture = QGestureRecognizer::registerRecognizer(new CustomGestureRecognizer); + Qt::GestureType FourthGesture = QGestureRecognizer::registerRecognizer(new CustomGestureRecognizer); + Qt::GestureType FifthGesture = QGestureRecognizer::registerRecognizer(new CustomGestureRecognizer); + Qt::GestureType SixthGesture = QGestureRecognizer::registerRecognizer(new CustomGestureRecognizer); + Qt::GestureType SeventhGesture = QGestureRecognizer::registerRecognizer(new CustomGestureRecognizer); + + Qt::GestureFlags flags = Qt::ReceivePartialGestures; + A->grabGesture(FirstGesture, flags); // A [1,3,4] + A->grabGesture(ThirdGesture, flags); // | + A->grabGesture(FourthGesture, flags); // B [2,3,5] + B->grabGesture(SecondGesture, flags); // | + B->grabGesture(ThirdGesture, flags); // C [1,2,3,6] + B->grabGesture(FifthGesture, flags); // | + C->grabGesture(FirstGesture, flags); // D [1,3,7] + C->grabGesture(SecondGesture, flags); + C->grabGesture(ThirdGesture, flags); + C->grabGesture(SixthGesture, flags); + D->grabGesture(FirstGesture, flags); + D->grabGesture(ThirdGesture, flags); + D->grabGesture(SeventhGesture, flags); + + // make sure all widgets ignore events, so they get propagated. + QSet<Qt::GestureType> allGestureTypes; + allGestureTypes << FirstGesture << SecondGesture << ThirdGesture + << FourthGesture << FifthGesture << SixthGesture << SeventhGesture; + A->ignoredGestures = B->ignoredGestures = allGestureTypes; + C->ignoredGestures = D->ignoredGestures = allGestureTypes; + + CustomEvent event; + sendCustomGesture(&event, D); + + static const int TotalGestureEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialStartedThreshold + 1; + + // gesture override events + QCOMPARE(D->overrideEvents.all.count(FirstGesture), 1); + QCOMPARE(D->overrideEvents.all.count(SecondGesture), 0); + QCOMPARE(D->overrideEvents.all.count(ThirdGesture), 1); + + QCOMPARE(C->overrideEvents.all.count(FirstGesture), 1); + QCOMPARE(C->overrideEvents.all.count(SecondGesture), 1); + QCOMPARE(C->overrideEvents.all.count(ThirdGesture), 1); + + QCOMPARE(B->overrideEvents.all.count(FirstGesture), 0); + QCOMPARE(B->overrideEvents.all.count(SecondGesture), 1); + QCOMPARE(B->overrideEvents.all.count(ThirdGesture), 1); + + QCOMPARE(A->overrideEvents.all.count(FirstGesture), 1); + QCOMPARE(A->overrideEvents.all.count(SecondGesture), 0); + QCOMPARE(A->overrideEvents.all.count(ThirdGesture), 1); + + // normal gesture events + QCOMPARE(D->events.all.count(FirstGesture), TotalGestureEventsCount); + QCOMPARE(D->events.all.count(SecondGesture), 0); + QCOMPARE(D->events.all.count(ThirdGesture), TotalGestureEventsCount); + QCOMPARE(D->events.all.count(FourthGesture), 0); + QCOMPARE(D->events.all.count(FifthGesture), 0); + QCOMPARE(D->events.all.count(SixthGesture), 0); + QCOMPARE(D->events.all.count(SeventhGesture), TotalGestureEventsCount); + + QCOMPARE(C->events.all.count(FirstGesture), TotalGestureEventsCount); + QCOMPARE(C->events.all.count(SecondGesture), TotalGestureEventsCount); + QCOMPARE(C->events.all.count(ThirdGesture), TotalGestureEventsCount); + QCOMPARE(C->events.all.count(FourthGesture), 0); + QCOMPARE(C->events.all.count(FifthGesture), 0); + QCOMPARE(C->events.all.count(SixthGesture), TotalGestureEventsCount); + QCOMPARE(C->events.all.count(SeventhGesture), 0); + + QCOMPARE(B->events.all.count(FirstGesture), 0); + QCOMPARE(B->events.all.count(SecondGesture), TotalGestureEventsCount); + QCOMPARE(B->events.all.count(ThirdGesture), TotalGestureEventsCount); + QCOMPARE(B->events.all.count(FourthGesture), 0); + QCOMPARE(B->events.all.count(FifthGesture), TotalGestureEventsCount); + QCOMPARE(B->events.all.count(SixthGesture), 0); + QCOMPARE(B->events.all.count(SeventhGesture), 0); + + QCOMPARE(A->events.all.count(FirstGesture), TotalGestureEventsCount); + QCOMPARE(A->events.all.count(SecondGesture), 0); + QCOMPARE(A->events.all.count(ThirdGesture), TotalGestureEventsCount); + QCOMPARE(A->events.all.count(FourthGesture), TotalGestureEventsCount); + QCOMPARE(A->events.all.count(FifthGesture), 0); + QCOMPARE(A->events.all.count(SixthGesture), 0); + QCOMPARE(A->events.all.count(SeventhGesture), 0); + + QGestureRecognizer::unregisterRecognizer(SecondGesture); + QGestureRecognizer::unregisterRecognizer(ThirdGesture); + QGestureRecognizer::unregisterRecognizer(FourthGesture); + QGestureRecognizer::unregisterRecognizer(FifthGesture); + QGestureRecognizer::unregisterRecognizer(SixthGesture); + QGestureRecognizer::unregisterRecognizer(SeventhGesture); +} + +void tst_Gestures::testMapToScene() +{ + QGesture gesture; + QList<QGesture*> list; + list << &gesture; + QGestureEvent event(list); + QCOMPARE(event.mapToGraphicsScene(gesture.hotSpot()), QPointF()); // not set, can't do much + + QGraphicsScene scene; + QGraphicsView view(&scene); + view.setWindowFlags(Qt::X11BypassWindowManagerHint); + + GestureItem *item0 = new GestureItem; + scene.addItem(item0); + item0->setPos(14, 16); + + view.show(); // need to show to give it a global coordinate + QTest::qWaitForWindowShown(&view); + view.ensureVisible(scene.sceneRect()); + + QPoint origin = view.mapToGlobal(QPoint()); + event.setWidget(view.viewport()); + + QCOMPARE(event.mapToGraphicsScene(origin + QPoint(100, 200)), view.mapToScene(QPoint(100, 200))); +} + +void tst_Gestures::ungrabGesture() // a method on QWidget +{ + class MockGestureWidget : public GestureWidget { + public: + MockGestureWidget(const char *name = 0, QWidget *parent = 0) + : GestureWidget(name, parent) { } + + + QSet<QGesture*> gestures; + protected: + bool event(QEvent *event) + { + if (event->type() == QEvent::Gesture) { + QGestureEvent *gestureEvent = static_cast<QGestureEvent*>(event); + if (gestureEvent) + foreach (QGesture *g, gestureEvent->gestures()) + gestures.insert(g); + } + return GestureWidget::event(event); + } + }; + + MockGestureWidget parent("A"); + MockGestureWidget *a = &parent; + MockGestureWidget *b = new MockGestureWidget("B", a); + + a->grabGesture(CustomGesture::GestureType, Qt::DontStartGestureOnChildren); + b->grabGesture(CustomGesture::GestureType); + b->ignoredGestures << CustomGesture::GestureType; + + CustomEvent event; + // sending an event will cause the QGesture objects to be instantiated for the widgets + sendCustomGesture(&event, b); + + QCOMPARE(a->gestures.count(), 1); + QPointer<QGesture> customGestureA; + customGestureA = *(a->gestures.begin()); + QVERIFY(!customGestureA.isNull()); + QCOMPARE(customGestureA->gestureType(), CustomGesture::GestureType); + + QCOMPARE(b->gestures.count(), 1); + QPointer<QGesture> customGestureB; + customGestureB = *(b->gestures.begin()); + QVERIFY(!customGestureB.isNull()); + QVERIFY(customGestureA.data() == customGestureB.data()); + QCOMPARE(customGestureB->gestureType(), CustomGesture::GestureType); + + a->gestures.clear(); + // sending an event will cause the QGesture objects to be instantiated for the widget + sendCustomGesture(&event, a); + + QCOMPARE(a->gestures.count(), 1); + customGestureA = *(a->gestures.begin()); + QVERIFY(!customGestureA.isNull()); + QCOMPARE(customGestureA->gestureType(), CustomGesture::GestureType); + QVERIFY(customGestureA.data() != customGestureB.data()); + + a->ungrabGesture(CustomGesture::GestureType); + //We changed the deletion of Gestures to lazy during QT-4022, so we can't ensure the QGesture is deleted until now + QVERIFY(!customGestureB.isNull()); + + a->gestures.clear(); + a->reset(); + // send again to 'b' and make sure a never gets it. + sendCustomGesture(&event, b); + //After all Gestures are processed in the QGestureManager, we can ensure the QGesture is now deleted + QVERIFY(customGestureA.isNull()); + QCOMPARE(a->gestureEventsReceived, 0); + QCOMPARE(a->gestureOverrideEventsReceived, 0); +} + +void tst_Gestures::unregisterRecognizer() // a method on QApplication +{ + /* + The hardest usecase to get right is when we remove a recognizer while several + of the gestures it created are in active state and we immediately add a new recognizer + for the same type (thus replacing the old one). + The expected result is that all old gestures continue till they are finished/cancelled + and the new recognizer starts creating gestures immediately at registration. + + This implies that deleting of the recognizer happens only when there are no more gestures + that it created. (since gestures might have a pointer to the recognizer) + */ + +} + +void tst_Gestures::autoCancelGestures() +{ + class MockWidget : public GestureWidget { + public: + MockWidget(const char *name) : GestureWidget(name), badGestureEvents(0) { } + + bool event(QEvent *event) + { + if (event->type() == QEvent::Gesture) { + QGestureEvent *ge = static_cast<QGestureEvent*>(event); + if (ge->gestures().count() != 1) + ++badGestureEvents; // event should contain exactly one gesture + ge->gestures().first()->setGestureCancelPolicy(QGesture::CancelAllInContext); + } + return GestureWidget::event(event); + } + + int badGestureEvents; + }; + + const Qt::GestureType secondGesture = QGestureRecognizer::registerRecognizer(new CustomGestureRecognizer); + + MockWidget parent("parent"); // this one sets the cancel policy to CancelAllInContext + parent.resize(300, 100); + parent.setWindowFlags(Qt::X11BypassWindowManagerHint); + GestureWidget *child = new GestureWidget("child", &parent); + child->setGeometry(10, 10, 100, 80); + + parent.grabGesture(CustomGesture::GestureType); + child->grabGesture(secondGesture); + parent.show(); + QTest::qWaitForWindowShown(&parent); + + /* + An event is send to both the child and the parent, when the child gets it a gesture is triggered + and send to the child. + When the parent gets the event a new gesture is triggered and delivered to the parent. When the + parent gets it he accepts it and that causes the cancel policy to activate. + The cause of that is the gesture for the child is cancelled and send to the child as such. + */ + CustomEvent event; + event.serial = CustomGesture::SerialStartedThreshold; + QApplication::sendEvent(child, &event); + QCOMPARE(child->events.all.count(), 2); + QCOMPARE(child->events.started.count(), 1); + QCOMPARE(child->events.canceled.count(), 1); + QCOMPARE(parent.events.all.count(), 1); + + // clean up, make the parent gesture finish + event.serial = CustomGesture::SerialFinishedThreshold; + QApplication::sendEvent(child, &event); + QCOMPARE(parent.events.all.count(), 2); + QCOMPARE(parent.badGestureEvents, 0); +} + +void tst_Gestures::autoCancelGestures2() +{ + class MockItem : public GestureItem { + public: + MockItem(const char *name) : GestureItem(name), badGestureEvents(0) { } + + bool event(QEvent *event) { + if (event->type() == QEvent::Gesture) { + QGestureEvent *ge = static_cast<QGestureEvent*>(event); + if (ge->gestures().count() != 1) + ++badGestureEvents; // event should contain exactly one gesture + ge->gestures().first()->setGestureCancelPolicy(QGesture::CancelAllInContext); + } + return GestureItem::event(event); + } + + int badGestureEvents; + }; + + const Qt::GestureType secondGesture = QGestureRecognizer ::registerRecognizer(new CustomGestureRecognizer); + + QGraphicsScene scene; + QGraphicsView view(&scene); + view.setWindowFlags(Qt::X11BypassWindowManagerHint); + + MockItem *parent = new MockItem("parent"); + GestureItem *child = new GestureItem("child"); + child->setParentItem(parent); + parent->setPos(0, 0); + child->setPos(10, 10); + scene.addItem(parent); + parent->grabGesture(CustomGesture::GestureType); + child->grabGesture(secondGesture); + + view.show(); + QTest::qWaitForWindowShown(&view); + view.ensureVisible(scene.sceneRect()); + + CustomEvent event; + event.serial = CustomGesture::SerialStartedThreshold; + event.hasHotSpot = true; + event.hotSpot = mapToGlobal(QPointF(5, 5), child, &view); + scene.sendEvent(child, &event); + QCOMPARE(parent->events.all.count(), 1); + QCOMPARE(child->events.started.count(), 1); + QCOMPARE(child->events.canceled.count(), 1); + QCOMPARE(child->events.all.count(), 2); + + // clean up, make the parent gesture finish + event.serial = CustomGesture::SerialFinishedThreshold; + scene.sendEvent(child, &event); + QCOMPARE(parent->events.all.count(), 2); + QCOMPARE(parent->badGestureEvents, 0); +} + +void tst_Gestures::graphicsViewParentPropagation() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + view.setWindowFlags(Qt::X11BypassWindowManagerHint); + + GestureItem *item0 = new GestureItem("item0"); + scene.addItem(item0); + item0->setPos(0, 0); + item0->grabGesture(CustomGesture::GestureType); + item0->setZValue(1); + + GestureItem *item1 = new GestureItem("item1"); + scene.addItem(item1); + item1->setPos(0, 0); + item1->setZValue(5); + + GestureItem *item1_c1 = new GestureItem("item1_child1"); + item1_c1->setParentItem(item1); + item1_c1->setPos(0, 0); + + GestureItem *item1_c1_c1 = new GestureItem("item1_child1_child1"); + item1_c1_c1->setParentItem(item1_c1); + item1_c1_c1->setPos(0, 0); + + view.show(); + QTest::qWaitForWindowShown(&view); + view.ensureVisible(scene.sceneRect()); + + item0->grabGesture(CustomGesture::GestureType, Qt::ReceivePartialGestures | Qt::IgnoredGesturesPropagateToParent); + item1->grabGesture(CustomGesture::GestureType, Qt::ReceivePartialGestures | Qt::IgnoredGesturesPropagateToParent); + item1_c1->grabGesture(CustomGesture::GestureType, Qt::IgnoredGesturesPropagateToParent); + item1_c1_c1->grabGesture(CustomGesture::GestureType, Qt::ReceivePartialGestures | Qt::IgnoredGesturesPropagateToParent); + + item0->ignoredUpdatedGestures << CustomGesture::GestureType; + item0->ignoredFinishedGestures << CustomGesture::GestureType; + item1->ignoredUpdatedGestures << CustomGesture::GestureType; + item1->ignoredFinishedGestures << CustomGesture::GestureType; + item1_c1->ignoredUpdatedGestures << CustomGesture::GestureType; + item1_c1->ignoredFinishedGestures << CustomGesture::GestureType; + item1_c1_c1->ignoredUpdatedGestures << CustomGesture::GestureType; + item1_c1_c1->ignoredFinishedGestures << CustomGesture::GestureType; + + static const int TotalGestureEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialStartedThreshold + 1; + + CustomEvent event; + event.hotSpot = mapToGlobal(QPointF(10, 10), item1_c1, &view); + event.hasHotSpot = true; + sendCustomGesture(&event, item0, &scene); + + QCOMPARE(item1_c1_c1->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(item1_c1_c1->gestureOverrideEventsReceived, 1); + QCOMPARE(item1_c1->gestureEventsReceived, TotalGestureEventsCount-1); + QCOMPARE(item1_c1->gestureOverrideEventsReceived, 1); + QCOMPARE(item1->gestureEventsReceived, TotalGestureEventsCount-1); + QCOMPARE(item1->gestureOverrideEventsReceived, 1); + QCOMPARE(item0->gestureEventsReceived, 0); + QCOMPARE(item0->gestureOverrideEventsReceived, 1); +} + +void tst_Gestures::panelPropagation() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + view.setWindowFlags(Qt::X11BypassWindowManagerHint); + + GestureItem *item0 = new GestureItem("item0"); + scene.addItem(item0); + item0->setPos(0, 0); + item0->size = QRectF(0, 0, 200, 200); + item0->grabGesture(CustomGesture::GestureType); + item0->setZValue(1); + + GestureItem *item1 = new GestureItem("item1"); + item1->grabGesture(CustomGesture::GestureType); + scene.addItem(item1); + item1->setPos(10, 10); + item1->size = QRectF(0, 0, 180, 180); + item1->setZValue(2); + + GestureItem *item1_child1 = new GestureItem("item1_child1[panel]"); + item1_child1->setFlags(QGraphicsItem::ItemIsPanel); + item1_child1->setParentItem(item1); + item1_child1->grabGesture(CustomGesture::GestureType); + item1_child1->setPos(10, 10); + item1_child1->size = QRectF(0, 0, 160, 160); + item1_child1->setZValue(5); + + GestureItem *item1_child1_child1 = new GestureItem("item1_child1_child1"); + item1_child1_child1->setParentItem(item1_child1); + item1_child1_child1->grabGesture(CustomGesture::GestureType); + item1_child1_child1->setPos(10, 10); + item1_child1_child1->size = QRectF(0, 0, 140, 140); + item1_child1_child1->setZValue(10); + + view.show(); + QTest::qWaitForWindowShown(&view); + view.ensureVisible(scene.sceneRect()); + + static const int TotalGestureEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialStartedThreshold + 1; + static const int TotalCustomEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialMaybeThreshold + 1; + + CustomEvent event; + event.hotSpot = mapToGlobal(QPointF(5, 5), item1_child1_child1, &view); + event.hasHotSpot = true; + sendCustomGesture(&event, item0, &scene); + + QCOMPARE(item0->customEventsReceived, TotalCustomEventsCount); + QCOMPARE(item1_child1_child1->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(item1_child1_child1->gestureOverrideEventsReceived, 1); + QCOMPARE(item1_child1->gestureOverrideEventsReceived, 1); + QCOMPARE(item1->gestureEventsReceived, 0); + QCOMPARE(item1->gestureOverrideEventsReceived, 0); + QCOMPARE(item0->gestureEventsReceived, 0); + QCOMPARE(item0->gestureOverrideEventsReceived, 0); + + item0->reset(); item1->reset(); item1_child1->reset(); item1_child1_child1->reset(); + + event.hotSpot = mapToGlobal(QPointF(5, 5), item1, &view); + event.hasHotSpot = true; + sendCustomGesture(&event, item1, &scene); + + QCOMPARE(item1_child1_child1->gestureEventsReceived, 0); + QCOMPARE(item1_child1_child1->gestureOverrideEventsReceived, 0); + QCOMPARE(item1_child1->gestureEventsReceived, 0); + QCOMPARE(item1_child1->gestureOverrideEventsReceived, 0); + QCOMPARE(item1->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(item1->gestureOverrideEventsReceived, 1); + QCOMPARE(item0->gestureEventsReceived, 0); + QCOMPARE(item0->gestureOverrideEventsReceived, 1); + + item0->reset(); item1->reset(); item1_child1->reset(); item1_child1_child1->reset(); + // try with a modal panel + item1_child1->setPanelModality(QGraphicsItem::PanelModal); + + event.hotSpot = mapToGlobal(QPointF(5, 5), item1, &view); + event.hasHotSpot = true; + sendCustomGesture(&event, item1, &scene); + + QCOMPARE(item1_child1_child1->gestureEventsReceived, 0); + QCOMPARE(item1_child1_child1->gestureOverrideEventsReceived, 0); + QCOMPARE(item1_child1->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(item1_child1->gestureOverrideEventsReceived, 0); + QCOMPARE(item1->gestureEventsReceived, 0); + QCOMPARE(item1->gestureOverrideEventsReceived, 0); + QCOMPARE(item0->gestureEventsReceived, 0); + QCOMPARE(item0->gestureOverrideEventsReceived, 0); + + item0->reset(); item1->reset(); item1_child1->reset(); item1_child1_child1->reset(); + // try with a modal panel, however set the hotspot to be outside of the + // panel and its parent + item1_child1->setPanelModality(QGraphicsItem::PanelModal); + + event.hotSpot = mapToGlobal(QPointF(5, 5), item0, &view); + event.hasHotSpot = true; + sendCustomGesture(&event, item1, &scene); + + QCOMPARE(item1_child1_child1->gestureEventsReceived, 0); + QCOMPARE(item1_child1_child1->gestureOverrideEventsReceived, 0); + QCOMPARE(item1_child1->gestureEventsReceived, 0); + QCOMPARE(item1_child1->gestureOverrideEventsReceived, 0); + QCOMPARE(item1->gestureEventsReceived, 0); + QCOMPARE(item1->gestureOverrideEventsReceived, 0); + QCOMPARE(item0->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(item0->gestureOverrideEventsReceived, 0); + + item0->reset(); item1->reset(); item1_child1->reset(); item1_child1_child1->reset(); + // try with a scene modal panel + item1_child1->setPanelModality(QGraphicsItem::SceneModal); + + event.hotSpot = mapToGlobal(QPointF(5, 5), item0, &view); + event.hasHotSpot = true; + sendCustomGesture(&event, item0, &scene); + + QCOMPARE(item1_child1_child1->gestureEventsReceived, 0); + QCOMPARE(item1_child1_child1->gestureOverrideEventsReceived, 0); + QCOMPARE(item1_child1->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(item1_child1->gestureOverrideEventsReceived, 0); + QCOMPARE(item1->gestureEventsReceived, 0); + QCOMPARE(item1->gestureOverrideEventsReceived, 0); + QCOMPARE(item0->gestureEventsReceived, 0); + QCOMPARE(item0->gestureOverrideEventsReceived, 0); +} + +void tst_Gestures::panelStacksBehindParent() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + view.setWindowFlags(Qt::X11BypassWindowManagerHint); + + GestureItem *item1 = new GestureItem("item1"); + item1->grabGesture(CustomGesture::GestureType); + scene.addItem(item1); + item1->setPos(10, 10); + item1->size = QRectF(0, 0, 180, 180); + item1->setZValue(2); + + GestureItem *panel = new GestureItem("panel"); + panel->setFlags(QGraphicsItem::ItemIsPanel | QGraphicsItem::ItemStacksBehindParent); + panel->setPanelModality(QGraphicsItem::PanelModal); + panel->setParentItem(item1); + panel->grabGesture(CustomGesture::GestureType); + panel->setPos(-10, -10); + panel->size = QRectF(0, 0, 200, 200); + panel->setZValue(5); + + view.show(); + QTest::qWaitForWindowShown(&view); + view.ensureVisible(scene.sceneRect()); + + static const int TotalGestureEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialStartedThreshold + 1; + + CustomEvent event; + event.hotSpot = mapToGlobal(QPointF(5, 5), item1, &view); + event.hasHotSpot = true; + sendCustomGesture(&event, item1, &scene); + + QCOMPARE(item1->gestureEventsReceived, 0); + QCOMPARE(item1->gestureOverrideEventsReceived, 0); + QCOMPARE(panel->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(panel->gestureOverrideEventsReceived, 0); +} + +void tst_Gestures::deleteGestureTargetWidget() +{ +} + +void tst_Gestures::deleteGestureTargetItem_data() +{ + QTest::addColumn<bool>("propagateUpdateGesture"); + QTest::addColumn<QString>("emitter"); + QTest::addColumn<QString>("receiver"); + QTest::addColumn<QByteArray>("signalName"); + QTest::addColumn<QByteArray>("slotName"); + + QByteArray gestureUpdated = SIGNAL(gestureUpdated(QEvent::Type,QGesture*)); + QByteArray gestureFinished = SIGNAL(gestureFinished(QEvent::Type,QGesture*)); + QByteArray deleteThis = SLOT(deleteThis()); + QByteArray deleteLater = SLOT(deleteLater()); + + QTest::newRow("delete1") + << false << "item1" << "item1" << gestureUpdated << deleteThis; + QTest::newRow("delete2") + << false << "item2" << "item2" << gestureUpdated << deleteThis; + QTest::newRow("delete3") + << false << "item1" << "item2" << gestureUpdated << deleteThis; + + QTest::newRow("deleteLater1") + << false << "item1" << "item1" << gestureUpdated << deleteLater; + QTest::newRow("deleteLater2") + << false << "item2" << "item2" << gestureUpdated << deleteLater; + QTest::newRow("deleteLater3") + << false << "item1" << "item2" << gestureUpdated << deleteLater; + QTest::newRow("deleteLater4") + << false << "item2" << "item1" << gestureUpdated << deleteLater; + + QTest::newRow("delete-self-and-propagate") + << true << "item2" << "item2" << gestureUpdated << deleteThis; + QTest::newRow("deleteLater-self-and-propagate") + << true << "item2" << "item2" << gestureUpdated << deleteLater; + QTest::newRow("propagate-to-deletedLater") + << true << "item2" << "item1" << gestureUpdated << deleteLater; +} + +void tst_Gestures::deleteGestureTargetItem() +{ + QFETCH(bool, propagateUpdateGesture); + QFETCH(QString, emitter); + QFETCH(QString, receiver); + QFETCH(QByteArray, signalName); + QFETCH(QByteArray, slotName); + + QGraphicsScene scene; + QGraphicsView view(&scene); + view.setWindowFlags(Qt::X11BypassWindowManagerHint); + + GestureItem *item1 = new GestureItem("item1"); + item1->grabGesture(CustomGesture::GestureType); + item1->setZValue(2); + scene.addItem(item1); + + GestureItem *item2 = new GestureItem("item2"); + item2->grabGesture(CustomGesture::GestureType); + item2->setZValue(5); + scene.addItem(item2); + + QMap<QString, GestureItem *> items; + items.insert(item1->objectName(), item1); + items.insert(item2->objectName(), item2); + + view.show(); + QTest::qWaitForWindowShown(&view); + view.ensureVisible(scene.sceneRect()); + + if (propagateUpdateGesture) + item2->ignoredUpdatedGestures << CustomGesture::GestureType; + connect(items.value(emitter, 0), signalName, items.value(receiver, 0), slotName); + + // some debug output to see the current test data tag, so if we crash + // we know which one caused the crash. + qDebug() << "<-- testing"; + + CustomEvent event; + event.hotSpot = mapToGlobal(QPointF(5, 5), item2, &view); + event.hasHotSpot = true; + sendCustomGesture(&event, item1, &scene); +} + +class GraphicsView : public QGraphicsView +{ +public: + GraphicsView(QGraphicsScene *scene, QWidget *parent = 0) + : QGraphicsView(scene, parent) + { + } + + using QGraphicsView::setViewportMargins; +}; + +// just making sure that even if the graphicsview has margins hotspot still +// works properly. It should use viewport for converting global coordinates to +// scene coordinates. +void tst_Gestures::viewportCoordinates() +{ + QGraphicsScene scene; + GraphicsView view(&scene); + view.setViewportMargins(10,20,15,25); + view.setWindowFlags(Qt::X11BypassWindowManagerHint); + + GestureItem *item1 = new GestureItem("item1"); + item1->grabGesture(CustomGesture::GestureType); + item1->size = QRectF(0, 0, 3, 3); + item1->setZValue(2); + scene.addItem(item1); + + view.show(); + QTest::qWaitForWindowShown(&view); + view.ensureVisible(scene.sceneRect()); + + CustomEvent event; + event.hotSpot = mapToGlobal(item1->boundingRect().center(), item1, &view); + event.hasHotSpot = true; + sendCustomGesture(&event, item1, &scene); + QVERIFY(item1->gestureEventsReceived != 0); +} + +void tst_Gestures::partialGesturePropagation() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + view.setWindowFlags(Qt::X11BypassWindowManagerHint); + + GestureItem *item1 = new GestureItem("item1"); + item1->grabGesture(CustomGesture::GestureType); + item1->setZValue(8); + scene.addItem(item1); + + GestureItem *item2 = new GestureItem("item2[partial]"); + item2->grabGesture(CustomGesture::GestureType, Qt::ReceivePartialGestures); + item2->setZValue(6); + scene.addItem(item2); + + GestureItem *item3 = new GestureItem("item3"); + item3->grabGesture(CustomGesture::GestureType); + item3->setZValue(4); + scene.addItem(item3); + + GestureItem *item4 = new GestureItem("item4[partial]"); + item4->grabGesture(CustomGesture::GestureType, Qt::ReceivePartialGestures); + item4->setZValue(2); + scene.addItem(item4); + + view.show(); + QTest::qWaitForWindowShown(&view); + view.ensureVisible(scene.sceneRect()); + + item1->ignoredUpdatedGestures << CustomGesture::GestureType; + + CustomEvent event; + event.hotSpot = mapToGlobal(QPointF(5, 5), item1, &view); + event.hasHotSpot = true; + sendCustomGesture(&event, item1, &scene); + + static const int TotalGestureEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialStartedThreshold + 1; + + QCOMPARE(item1->gestureOverrideEventsReceived, 1); + QCOMPARE(item2->gestureOverrideEventsReceived, 1); + QCOMPARE(item3->gestureOverrideEventsReceived, 1); + QCOMPARE(item4->gestureOverrideEventsReceived, 1); + + QCOMPARE(item1->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(item2->gestureEventsReceived, TotalGestureEventsCount-2); // except for started and finished + QCOMPARE(item3->gestureEventsReceived, 0); + QCOMPARE(item4->gestureEventsReceived, 0); +} + +class WinNativePan : public QPanGesture { +public: + WinNativePan() {} +}; + +class Pan : public QPanGesture { +public: + Pan() {} +}; + +class CustomPan : public QPanGesture { +public: + CustomPan() {} +}; + +// Recognizer for active gesture triggers on mouse press +class PanRecognizer : public QGestureRecognizer { +public: + enum PanType { Platform, Default, Custom }; + + PanRecognizer(int id) : m_id(id) {} + QGesture *create(QObject *) { + switch(m_id) { + case Platform: return new WinNativePan(); + case Default: return new Pan(); + default: return new CustomPan(); + } + } + + Result recognize(QGesture *, QObject *, QEvent *) { return QGestureRecognizer::Ignore; } + + const int m_id; +}; + +void tst_Gestures::testQGestureRecognizerCleanup() +{ + // Clean first the current recognizers in QGManager + QGestureRecognizer::unregisterRecognizer(Qt::PanGesture); + + // v-- Qt singleton QGManager initialization + + // Mimic QGestureManager: register both default and "platform" recognizers + // (this is done in windows when QT_NO_NATIVE_GESTURES is not defined) + PanRecognizer *def = new PanRecognizer(PanRecognizer::Default); + QGestureRecognizer::registerRecognizer(def); + PanRecognizer *plt = new PanRecognizer(PanRecognizer::Platform); + QGestureRecognizer::registerRecognizer(plt); + qDebug () << "register: default =" << def << "; platform =" << plt; + + // ^-- Qt singleton QGManager initialization + + // Here, application code would start + + // Create QGV (has a QAScrollArea, which uses Qt::PanGesture) + QMainWindow *w = new QMainWindow; + QGraphicsView *v = new QGraphicsView(); + w->setCentralWidget(v); + + // Unregister Qt recognizers + QGestureRecognizer::unregisterRecognizer(Qt::PanGesture); + + // Register a custom Pan recognizer + //QGestureRecognizer::registerRecognizer(new PanRecognizer(PanRecognizer::Custom)); + + w->show(); + QTest::qWaitForWindowShown(w); + delete w; +} + +class ReuseCanceledGesturesRecognizer : public QGestureRecognizer +{ +public: + enum Type { + RmbAndCancelAllType, + LmbType + }; + + ReuseCanceledGesturesRecognizer(Type type) : m_type(type) {} + + QGesture *create(QObject *) { + QGesture *g = new QGesture; + return g; + } + + Result recognize(QGesture *gesture, QObject *, QEvent *event) { + QMouseEvent *me = static_cast<QMouseEvent *>(event); + Qt::MouseButton mouseButton(m_type == LmbType ? Qt::LeftButton : Qt::RightButton); + + switch(event->type()) { + case QEvent::MouseButtonPress: + if (me->button() == mouseButton && gesture->state() == Qt::NoGesture) { + gesture->setHotSpot(QPointF(me->globalPos())); + if (m_type == RmbAndCancelAllType) + gesture->setGestureCancelPolicy(QGesture::CancelAllInContext); + return QGestureRecognizer::TriggerGesture; + } + break; + case QEvent::MouseButtonRelease: + if (me->button() == mouseButton && gesture->state() > Qt::NoGesture) + return QGestureRecognizer::FinishGesture; + default: + break; + } + return QGestureRecognizer::Ignore; + } +private: + Type m_type; +}; + +class ReuseCanceledGesturesWidget : public QGraphicsWidget +{ + public: + ReuseCanceledGesturesWidget(Qt::GestureType gestureType = Qt::TapGesture, QGraphicsItem *parent = 0) + : QGraphicsWidget(parent), + m_gestureType(gestureType), + m_started(0), m_updated(0), m_canceled(0), m_finished(0) + { + } + + bool event(QEvent *event) { + if (event->type() == QEvent::Gesture) { + QGesture *gesture = static_cast<QGestureEvent*>(event)->gesture(m_gestureType); + if (gesture) { + switch(gesture->state()) { + case Qt::GestureStarted: m_started++; break; + case Qt::GestureUpdated: m_updated++; break; + case Qt::GestureFinished: m_finished++; break; + case Qt::GestureCanceled: m_canceled++; break; + default: break; + } + } + return true; + } + if (event->type() == QEvent::GraphicsSceneMousePress) { + return true; + } + return QGraphicsWidget::event(event); + } + + int started() { return m_started; } + int updated() { return m_updated; } + int finished() { return m_finished; } + int canceled() { return m_canceled; } + + private: + Qt::GestureType m_gestureType; + int m_started; + int m_updated; + int m_canceled; + int m_finished; +}; + +void tst_Gestures::testReuseCanceledGestures() +{ + Qt::GestureType cancellingGestureTypeId = QGestureRecognizer::registerRecognizer( + new ReuseCanceledGesturesRecognizer(ReuseCanceledGesturesRecognizer::RmbAndCancelAllType)); + Qt::GestureType tapGestureTypeId = QGestureRecognizer::registerRecognizer( + new ReuseCanceledGesturesRecognizer(ReuseCanceledGesturesRecognizer::LmbType)); + + QMainWindow mw; + mw.setWindowFlags(Qt::X11BypassWindowManagerHint); + QGraphicsView *gv = new QGraphicsView(&mw); + QGraphicsScene *scene = new QGraphicsScene; + + gv->setScene(scene); + scene->setSceneRect(0,0,100,100); + + // Create container and add to the scene + ReuseCanceledGesturesWidget *container = new ReuseCanceledGesturesWidget; + container->grabGesture(cancellingGestureTypeId); // << container grabs canceling gesture + + // Create widget and add to the scene + ReuseCanceledGesturesWidget *target = new ReuseCanceledGesturesWidget(tapGestureTypeId, container); + target->grabGesture(tapGestureTypeId); + + container->setGeometry(scene->sceneRect()); + + scene->addItem(container); + + mw.setCentralWidget(gv); + + // Viewport needs to grab all gestures that widgets in scene grab + gv->viewport()->grabGesture(cancellingGestureTypeId); + gv->viewport()->grabGesture(tapGestureTypeId); + + mw.show(); + QTest::qWaitForWindowShown(&mw); + + QPoint targetPos(gv->mapFromScene(target->mapToScene(target->rect().center()))); + targetPos = gv->viewport()->mapFromParent(targetPos); + + // "Tap" starts on child widget + QTest::mousePress(gv->viewport(), Qt::LeftButton, 0, targetPos); + QCOMPARE(target->started(), 1); + QCOMPARE(target->updated(), 0); + QCOMPARE(target->finished(), 0); + QCOMPARE(target->canceled(), 0); + + // Canceling gesture starts on parent + QTest::mousePress(gv->viewport(), Qt::RightButton, 0, targetPos); + QCOMPARE(target->started(), 1); + QCOMPARE(target->updated(), 0); + QCOMPARE(target->finished(), 0); + QCOMPARE(target->canceled(), 1); // <- child canceled + + // Canceling gesture ends + QTest::mouseRelease(gv->viewport(), Qt::RightButton, 0, targetPos); + QCOMPARE(target->started(), 1); + QCOMPARE(target->updated(), 0); + QCOMPARE(target->finished(), 0); + QCOMPARE(target->canceled(), 1); + + // Tap would end if not canceled + QTest::mouseRelease(gv->viewport(), Qt::LeftButton, 0, targetPos); + QCOMPARE(target->started(), 1); + QCOMPARE(target->updated(), 0); + QCOMPARE(target->finished(), 0); + QCOMPARE(target->canceled(), 1); + + // New "Tap" starts + QTest::mousePress(gv->viewport(), Qt::LeftButton, 0, targetPos); + QCOMPARE(target->started(), 2); + QCOMPARE(target->updated(), 0); + QCOMPARE(target->finished(), 0); + QCOMPARE(target->canceled(), 1); + + QTest::mouseRelease(gv->viewport(), Qt::LeftButton, 0, targetPos); + QCOMPARE(target->started(), 2); + QCOMPARE(target->updated(), 0); + QCOMPARE(target->finished(), 1); + QCOMPARE(target->canceled(), 1); +} + +void tst_Gestures::conflictingGesturesInGraphicsView() +{ + QGraphicsScene scene; + GraphicsView view(&scene); + view.setWindowFlags(Qt::X11BypassWindowManagerHint); + + GestureItem *item1 = new GestureItem("item1"); + item1->grabGesture(CustomGesture::GestureType); + item1->size = QRectF(0, 0, 100, 100); + item1->setZValue(2); + scene.addItem(item1); + + GestureItem *item2 = new GestureItem("item2"); + item2->grabGesture(CustomGesture::GestureType); + item2->size = QRectF(0, 0, 100, 100); + item2->setZValue(5); + scene.addItem(item2); + + view.show(); + QTest::qWaitForWindowShown(&view); + view.ensureVisible(scene.sceneRect()); + + static const int TotalGestureEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialStartedThreshold + 1; + + CustomEvent event; + + // nobody accepts override + item1->acceptGestureOverride = false; + item2->acceptGestureOverride = false; + event.hotSpot = mapToGlobal(item2->boundingRect().center(), item2, &view); + event.hasHotSpot = true; + sendCustomGesture(&event, item2, &scene); + QCOMPARE(item2->gestureOverrideEventsReceived, 1); + QCOMPARE(item2->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(item1->gestureOverrideEventsReceived, 1); + QCOMPARE(item1->gestureEventsReceived, 0); + + item1->reset(); item2->reset(); + + // the original target accepts override + item1->acceptGestureOverride = false; + item2->acceptGestureOverride = true; + event.hotSpot = mapToGlobal(item2->boundingRect().center(), item2, &view); + event.hasHotSpot = true; + sendCustomGesture(&event, item2, &scene); + QCOMPARE(item2->gestureOverrideEventsReceived, 1); + QCOMPARE(item2->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(item1->gestureOverrideEventsReceived, 0); + QCOMPARE(item1->gestureEventsReceived, 0); + + item1->reset(); item2->reset(); + + // the item behind accepts override + item1->acceptGestureOverride = true; + item2->acceptGestureOverride = false; + event.hotSpot = mapToGlobal(item2->boundingRect().center(), item2, &view); + event.hasHotSpot = true; + sendCustomGesture(&event, item2, &scene); + + QCOMPARE(item2->gestureOverrideEventsReceived, 1); + QCOMPARE(item2->gestureEventsReceived, 0); + QCOMPARE(item1->gestureOverrideEventsReceived, 1); + QCOMPARE(item1->gestureEventsReceived, TotalGestureEventsCount); +} + +class NoConsumeWidgetBug13501 :public QWidget +{ + Q_OBJECT +protected: + bool event(QEvent *e) { + if(e->type() == QEvent::Gesture) { + return false; + } + return QWidget::event(e); + } +}; + +void tst_Gestures::bug_13501_gesture_not_accepted() +{ + // Create a gesture event that is not accepted by any widget + // make sure this does not lead to an assert in QGestureManager + NoConsumeWidgetBug13501 w; + w.grabGesture(Qt::TapGesture); + w.show(); + QTest::qWaitForWindowShown(&w); + //QTest::mousePress(&ignoreEvent, Qt::LeftButton); + QTest::touchEvent(&w).press(0, QPoint(10, 10), &w); +} + +QTEST_MAIN(tst_Gestures) +#include "tst_gestures.moc" diff --git a/tests/auto/integrationtests/integrationtests.pro b/tests/auto/integrationtests/integrationtests.pro new file mode 100644 index 0000000000..1d58f2c665 --- /dev/null +++ b/tests/auto/integrationtests/integrationtests.pro @@ -0,0 +1,35 @@ +TEMPLATE=subdirs +SUBDIRS=\ + collections \ + exceptionsafety \ + exceptionsafety_objects \ + gestures \ + lancelot \ + languagechange \ + macgui \ + macnativeevents \ + macplist \ + modeltest \ + networkselftest \ + qaccessibility \ + qcomplextext \ + qdirectpainter \ + qfocusevent \ + qmultiscreen \ + qnetworkaccessmanager_and_qprogressdialog \ + qsharedpointer_and_qwidget \ + windowsmobile \ + +wince*|!contains(QT_CONFIG, accessibility):SUBDIRS -= qaccessibility + +!mac|qpa: SUBDIRS -= \ + macgui \ + macnativeevents \ + macplist \ + +!embedded|wince*: SUBDIRS -= \ + qdirectpainter \ + qmultiscreen \ + +!linux*-g++*:SUBDIRS -= exceptionsafety_objects + diff --git a/tests/auto/integrationtests/lancelot/.gitignore b/tests/auto/integrationtests/lancelot/.gitignore new file mode 100644 index 0000000000..0a70416d57 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/.gitignore @@ -0,0 +1 @@ +tst_lancelot diff --git a/tests/auto/integrationtests/lancelot/images.qrc b/tests/auto/integrationtests/lancelot/images.qrc new file mode 100644 index 0000000000..060b52c85a --- /dev/null +++ b/tests/auto/integrationtests/lancelot/images.qrc @@ -0,0 +1,34 @@ +<!DOCTYPE RCC> +<RCC version="1.0"> +<qresource> + <file>images/alpha.png</file> + <file>images/border.png</file> + <file>images/borderimage.png</file> + <file>images/dome_argb32.png</file> + <file>images/dome_indexed.png</file> + <file>images/dome_mono_palette.png</file> + <file>images/dome_rgb32.png</file> + <file>images/face.png</file> + <file>images/gam045.png</file> + <file>images/gam100.png</file> + <file>images/image.png</file> + <file>images/masked.png</file> + <file>images/sign.png</file> + <file>images/struct-image-01.jpg</file> + <file>images/bitmap.png</file> + <file>images/dome_indexed_mask.png</file> + <file>images/dome_mono_128.png</file> + <file>images/dome_mono.png</file> + <file>images/dot.png</file> + <file>images/gam030.png</file> + <file>images/gam056.png</file> + <file>images/gam200.png</file> + <file>images/mask_100.png</file> + <file>images/mask.png</file> + <file>images/solid.png</file> + <file>images/struct-image-01.png</file> + <file>images/zebra.png</file> + <file>images/alpha2x2.png</file> + <file>images/solid2x2.png</file> +</qresource> +</RCC> diff --git a/tests/auto/integrationtests/lancelot/images/alpha.png b/tests/auto/integrationtests/lancelot/images/alpha.png Binary files differnew file mode 100644 index 0000000000..e465b2586d --- /dev/null +++ b/tests/auto/integrationtests/lancelot/images/alpha.png diff --git a/tests/auto/integrationtests/lancelot/images/alpha2x2.png b/tests/auto/integrationtests/lancelot/images/alpha2x2.png Binary files differnew file mode 100644 index 0000000000..67ecc04286 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/images/alpha2x2.png diff --git a/tests/auto/integrationtests/lancelot/images/bitmap.png b/tests/auto/integrationtests/lancelot/images/bitmap.png Binary files differnew file mode 100644 index 0000000000..d21f8f51bb --- /dev/null +++ b/tests/auto/integrationtests/lancelot/images/bitmap.png diff --git a/tests/auto/integrationtests/lancelot/images/border.png b/tests/auto/integrationtests/lancelot/images/border.png Binary files differnew file mode 100644 index 0000000000..a3d2fed0a3 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/images/border.png diff --git a/tests/auto/integrationtests/lancelot/images/borderimage.png b/tests/auto/integrationtests/lancelot/images/borderimage.png Binary files differnew file mode 100644 index 0000000000..f7f6b66227 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/images/borderimage.png diff --git a/tests/auto/integrationtests/lancelot/images/dome_argb32.png b/tests/auto/integrationtests/lancelot/images/dome_argb32.png Binary files differnew file mode 100644 index 0000000000..e3ccba0c13 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/images/dome_argb32.png diff --git a/tests/auto/integrationtests/lancelot/images/dome_indexed.png b/tests/auto/integrationtests/lancelot/images/dome_indexed.png Binary files differnew file mode 100644 index 0000000000..beefcd514e --- /dev/null +++ b/tests/auto/integrationtests/lancelot/images/dome_indexed.png diff --git a/tests/auto/integrationtests/lancelot/images/dome_indexed_mask.png b/tests/auto/integrationtests/lancelot/images/dome_indexed_mask.png Binary files differnew file mode 100644 index 0000000000..a62f29f40e --- /dev/null +++ b/tests/auto/integrationtests/lancelot/images/dome_indexed_mask.png diff --git a/tests/auto/integrationtests/lancelot/images/dome_mono.png b/tests/auto/integrationtests/lancelot/images/dome_mono.png Binary files differnew file mode 100644 index 0000000000..950c2a7494 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/images/dome_mono.png diff --git a/tests/auto/integrationtests/lancelot/images/dome_mono_128.png b/tests/auto/integrationtests/lancelot/images/dome_mono_128.png Binary files differnew file mode 100644 index 0000000000..77e48aaab7 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/images/dome_mono_128.png diff --git a/tests/auto/integrationtests/lancelot/images/dome_mono_palette.png b/tests/auto/integrationtests/lancelot/images/dome_mono_palette.png Binary files differnew file mode 100644 index 0000000000..dca3f59245 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/images/dome_mono_palette.png diff --git a/tests/auto/integrationtests/lancelot/images/dome_rgb32.png b/tests/auto/integrationtests/lancelot/images/dome_rgb32.png Binary files differnew file mode 100644 index 0000000000..27bc02a545 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/images/dome_rgb32.png diff --git a/tests/auto/integrationtests/lancelot/images/dot.png b/tests/auto/integrationtests/lancelot/images/dot.png Binary files differnew file mode 100644 index 0000000000..17a7b6a0ba --- /dev/null +++ b/tests/auto/integrationtests/lancelot/images/dot.png diff --git a/tests/auto/integrationtests/lancelot/images/face.png b/tests/auto/integrationtests/lancelot/images/face.png Binary files differnew file mode 100644 index 0000000000..4f6172d83b --- /dev/null +++ b/tests/auto/integrationtests/lancelot/images/face.png diff --git a/tests/auto/integrationtests/lancelot/images/gam030.png b/tests/auto/integrationtests/lancelot/images/gam030.png Binary files differnew file mode 100644 index 0000000000..904c9721bd --- /dev/null +++ b/tests/auto/integrationtests/lancelot/images/gam030.png diff --git a/tests/auto/integrationtests/lancelot/images/gam045.png b/tests/auto/integrationtests/lancelot/images/gam045.png Binary files differnew file mode 100644 index 0000000000..b649a8a54f --- /dev/null +++ b/tests/auto/integrationtests/lancelot/images/gam045.png diff --git a/tests/auto/integrationtests/lancelot/images/gam056.png b/tests/auto/integrationtests/lancelot/images/gam056.png Binary files differnew file mode 100644 index 0000000000..e5f959dc96 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/images/gam056.png diff --git a/tests/auto/integrationtests/lancelot/images/gam100.png b/tests/auto/integrationtests/lancelot/images/gam100.png Binary files differnew file mode 100644 index 0000000000..6c7ba5f1ed --- /dev/null +++ b/tests/auto/integrationtests/lancelot/images/gam100.png diff --git a/tests/auto/integrationtests/lancelot/images/gam200.png b/tests/auto/integrationtests/lancelot/images/gam200.png Binary files differnew file mode 100644 index 0000000000..daa20fcbc4 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/images/gam200.png diff --git a/tests/auto/integrationtests/lancelot/images/image.png b/tests/auto/integrationtests/lancelot/images/image.png Binary files differnew file mode 100644 index 0000000000..85d486a790 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/images/image.png diff --git a/tests/auto/integrationtests/lancelot/images/mask.png b/tests/auto/integrationtests/lancelot/images/mask.png Binary files differnew file mode 100644 index 0000000000..c3f3b1b6ca --- /dev/null +++ b/tests/auto/integrationtests/lancelot/images/mask.png diff --git a/tests/auto/integrationtests/lancelot/images/mask_100.png b/tests/auto/integrationtests/lancelot/images/mask_100.png Binary files differnew file mode 100644 index 0000000000..fc950dc7ed --- /dev/null +++ b/tests/auto/integrationtests/lancelot/images/mask_100.png diff --git a/tests/auto/integrationtests/lancelot/images/masked.png b/tests/auto/integrationtests/lancelot/images/masked.png Binary files differnew file mode 100644 index 0000000000..6debec534d --- /dev/null +++ b/tests/auto/integrationtests/lancelot/images/masked.png diff --git a/tests/auto/integrationtests/lancelot/images/sign.png b/tests/auto/integrationtests/lancelot/images/sign.png Binary files differnew file mode 100644 index 0000000000..6aac7e150a --- /dev/null +++ b/tests/auto/integrationtests/lancelot/images/sign.png diff --git a/tests/auto/integrationtests/lancelot/images/solid.png b/tests/auto/integrationtests/lancelot/images/solid.png Binary files differnew file mode 100644 index 0000000000..371e9c1aee --- /dev/null +++ b/tests/auto/integrationtests/lancelot/images/solid.png diff --git a/tests/auto/integrationtests/lancelot/images/solid2x2.png b/tests/auto/integrationtests/lancelot/images/solid2x2.png Binary files differnew file mode 100644 index 0000000000..ad67cd3e12 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/images/solid2x2.png diff --git a/tests/auto/integrationtests/lancelot/images/struct-image-01.jpg b/tests/auto/integrationtests/lancelot/images/struct-image-01.jpg Binary files differnew file mode 100644 index 0000000000..a74e07223b --- /dev/null +++ b/tests/auto/integrationtests/lancelot/images/struct-image-01.jpg diff --git a/tests/auto/integrationtests/lancelot/images/struct-image-01.png b/tests/auto/integrationtests/lancelot/images/struct-image-01.png Binary files differnew file mode 100644 index 0000000000..4ed08406dc --- /dev/null +++ b/tests/auto/integrationtests/lancelot/images/struct-image-01.png diff --git a/tests/auto/integrationtests/lancelot/images/zebra.png b/tests/auto/integrationtests/lancelot/images/zebra.png Binary files differnew file mode 100644 index 0000000000..ef35f20785 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/images/zebra.png diff --git a/tests/auto/integrationtests/lancelot/lancelot.pro b/tests/auto/integrationtests/lancelot/lancelot.pro new file mode 100644 index 0000000000..d34732e06e --- /dev/null +++ b/tests/auto/integrationtests/lancelot/lancelot.pro @@ -0,0 +1,15 @@ +load(qttest_p4) +QT += xml widgets +contains(QT_CONFIG, opengl)|contains(QT_CONFIG, opengles1)|contains(QT_CONFIG, opengles2):QT += opengl + +SOURCES += tst_lancelot.cpp \ + paintcommands.cpp +HEADERS += paintcommands.h +RESOURCES += images.qrc + +include($$PWD/../../../baselineserver/shared/qbaselinetest.pri) + +!wince*:DEFINES += SRCDIR=\\\"$$PWD\\\" +linux-g++-maemo:DEFINES += USE_RUNTIME_DIR + +CONFIG += insignificant_test # QTBUG-21402 diff --git a/tests/auto/integrationtests/lancelot/paintcommands.cpp b/tests/auto/integrationtests/lancelot/paintcommands.cpp new file mode 100644 index 0000000000..9659659973 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/paintcommands.cpp @@ -0,0 +1,2590 @@ +/**************************************************************************** +** +** 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 "paintcommands.h" + +#include <qdir.h> +#include <qfile.h> +#include <qfileinfo.h> +#include <qpainter.h> +#include <qbitmap.h> +#include <qtextstream.h> +#include <qtextlayout.h> +#include <qdebug.h> +#include <QStaticText> + +#ifndef QT_NO_OPENGL +#include <qglpixelbuffer.h> +#endif + +/********************************************************************************* +** everything to populate static tables +**********************************************************************************/ +const char *PaintCommands::brushStyleTable[] = { + "NoBrush", + "SolidPattern", + "Dense1Pattern", + "Dense2Pattern", + "Dense3Pattern", + "Dense4Pattern", + "Dense5Pattern", + "Dense6Pattern", + "Dense7Pattern", + "HorPattern", + "VerPattern", + "CrossPattern", + "BDiagPattern", + "FDiagPattern", + "DiagCrossPattern", + "LinearGradientPattern" +}; + +const char *PaintCommands::penStyleTable[] = { + "NoPen", + "SolidLine", + "DashLine", + "DotLine", + "DashDotLine", + "DashDotDotLine" +}; + +const char *PaintCommands::fontWeightTable[] = { + "Light", + "Normal", + "DemiBold", + "Bold", + "Black" +}; + +const char *PaintCommands::fontHintingTable[] = { + "Default", + "None", + "Vertical", + "Full" +}; + +const char *PaintCommands::clipOperationTable[] = { + "NoClip", + "ReplaceClip", + "IntersectClip", + "UniteClip" +}; + +const char *PaintCommands::spreadMethodTable[] = { + "PadSpread", + "ReflectSpread", + "RepeatSpread" +}; + +const char *PaintCommands::coordinateMethodTable[] = { + "LogicalMode", + "StretchToDeviceMode", + "ObjectBoundingMode" +}; + +const char *PaintCommands::sizeModeTable[] = { + "AbsoluteSize", + "RelativeSize" +}; + +const char *PaintCommands::compositionModeTable[] = { + "SourceOver", + "DestinationOver", + "Clear", + "Source", + "Destination", + "SourceIn", + "DestinationIn", + "SourceOut", + "DestinationOut", + "SourceAtop", + "DestinationAtop", + "Xor", + "Plus", + "Multiply", + "Screen", + "Overlay", + "Darken", + "Lighten", + "ColorDodge", + "ColorBurn", + "HardLight", + "SoftLight", + "Difference", + "Exclusion", + "SourceOrDestination", + "SourceAndDestination", + "SourceXorDestination", + "NotSourceAndNotDestination", + "NotSourceOrNotDestination", + "NotSourceXorDestination", + "NotSource", + "NotSourceAndDestination", + "SourceAndNotDestination" +}; + +const char *PaintCommands::imageFormatTable[] = { + "Invalid", + "Mono", + "MonoLSB", + "Indexed8", + "RGB32", + "ARGB32", + "ARGB32_Premultiplied", + "Format_RGB16", + "Format_ARGB8565_Premultiplied", + "Format_RGB666", + "Format_ARGB6666_Premultiplied", + "Format_RGB555", + "Format_ARGB8555_Premultiplied", + "Format_RGB888", + "Format_RGB444", + "Format_ARGB4444_Premultiplied" +}; + +int PaintCommands::translateEnum(const char *table[], const QString &pattern, int limit) +{ + QByteArray p = pattern.toLatin1().toLower(); + for (int i=0; i<limit; ++i) + if (p == QByteArray::fromRawData(table[i], qstrlen(table[i])).toLower()) + return i; + return -1; +} + +QList<PaintCommands::PaintCommandInfos> PaintCommands::s_commandInfoTable = QList<PaintCommands::PaintCommandInfos>(); +QList<QPair<QString,QStringList> > PaintCommands::s_enumsTable = QList<QPair<QString,QStringList> >(); +QMultiHash<QString, int> PaintCommands::s_commandHash; + +#define DECL_PAINTCOMMAND(identifier, method, regexp, syntax, sample) \ + s_commandInfoTable << PaintCommandInfos(QLatin1String(identifier), &PaintCommands::method, QRegExp(regexp), \ + QLatin1String(syntax), QLatin1String(sample) ); + +#define DECL_PAINTCOMMANDSECTION(title) \ + s_commandInfoTable << PaintCommandInfos(QLatin1String(title)); + +#define ADD_ENUMLIST(listCaption, cStrArray) { \ + QStringList list; \ + for (int i=0; i<int(sizeof(cStrArray)/sizeof(char*)); i++) \ + list << cStrArray[i]; \ + s_enumsTable << qMakePair(QString(listCaption), list); \ + } + +void PaintCommands::staticInit() +{ + // check if already done + if (!s_commandInfoTable.isEmpty()) return; + + // populate the command list + DECL_PAINTCOMMANDSECTION("misc"); + DECL_PAINTCOMMAND("comment", command_comment, + "^\\s*#", + "# this is some comments", + "# place your comments here"); + DECL_PAINTCOMMAND("import", command_import, + "^import\\s+\"(.*)\"$", + "import <qrcFilename>", + "import \"myfile.qrc\""); + DECL_PAINTCOMMAND("begin_block", command_begin_block, + "^begin_block\\s+(\\w*)$", + "begin_block <blockName>", + "begin_block blockName"); + DECL_PAINTCOMMAND("end_block", command_end_block, + "^end_block$", + "end_block", + "end_block"); + DECL_PAINTCOMMAND("repeat_block", command_repeat_block, + "^repeat_block\\s+(\\w*)$", + "repeat_block <blockName>", + "repeat_block blockName"); + DECL_PAINTCOMMAND("textlayout_draw", command_textlayout_draw, + "^textlayout_draw\\s+\"(.*)\"\\s+([0-9.]*)$", + "textlayout_draw <text> <width>", + "textlayout_draw \"your text\" 1.0"); + DECL_PAINTCOMMAND("abort", command_abort, + "^abort$", + "abort", + "abort"); + DECL_PAINTCOMMAND("noop", command_noop, + "^$", + "-", + "\n"); + + DECL_PAINTCOMMANDSECTION("setters"); + DECL_PAINTCOMMAND("setBackgroundMode", command_setBgMode, + "^(setBackgroundMode|setBgMode)\\s+(\\w*)$", + "setBackgroundMode <OpaqueMode|TransparentMode>", + "setBackgroundMode TransparentMode"); + DECL_PAINTCOMMAND("setBackground", command_setBackground, + "^setBackground\\s+#?(\\w*)\\s*(\\w*)?$", + "setBackground <color> [brush style enum]", + "setBackground black SolidPattern"); + DECL_PAINTCOMMAND("setOpacity", command_setOpacity, + "^setOpacity\\s+(-?\\d*\\.?\\d*)$", + "setOpacity <opacity>\n - opacity is in [0,1]", + "setOpacity 1.0"); + DECL_PAINTCOMMAND("path_setFillRule", command_path_setFillRule, + "^path_setFillRule\\s+(\\w*)\\s+(\\w*)$", + "path_setFillRule <pathName> [Winding|OddEven]", + "path_setFillRule pathName Winding"); + DECL_PAINTCOMMAND("setBrush", command_setBrush, + "^setBrush\\s+(#?[\\w.:\\/]*)\\s*(\\w*)?$", + "setBrush <pixmapFileName>\nsetBrush noBrush\nsetBrush <color> <brush style enum>", + "setBrush white SolidPattern"); + DECL_PAINTCOMMAND("setBrushOrigin", command_setBrushOrigin, + "^setBrushOrigin\\s*(-?\\w*)\\s+(-?\\w*)$", + "setBrushOrigin <dx> <dy>", + "setBrushOrigin 0 0"); + DECL_PAINTCOMMAND("brushTranslate", command_brushTranslate, + "^brushTranslate\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)$", + "brushTranslate <tx> <ty>", + "brushTranslate 0.0 0.0"); + DECL_PAINTCOMMAND("brushScale", command_brushScale, + "^brushScale\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)$", + "brushScale <kx> <ky>", + "brushScale 0.0 0.0"); + DECL_PAINTCOMMAND("brushRotate", command_brushRotate, + "^brushRotate\\s+(-?[\\w.]*)$", + "brushRotate <angle>\n - angle in degrees", + "brushRotate 0.0"); + DECL_PAINTCOMMAND("brushShear", command_brushShear, + "^brushShear\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)$", + "brushShear <sx> <sy>", + "brushShear 0.0 0.0"); + DECL_PAINTCOMMAND("setCompositionMode", command_setCompositionMode, + "^setCompositionMode\\s+([\\w_0-9]*)$", + "setCompositionMode <composition mode enum>", + "setCompositionMode SourceOver"); + DECL_PAINTCOMMAND("setFont", command_setFont, + "^setFont\\s+\"([\\w\\s]*)\"\\s*(\\w*)\\s*(\\w*)\\s*(\\w*)\\s*(\\w*)$", + "setFont <fontFace> [size] [font weight|font weight enum] [italic] [hinting enum]\n - font weight is an integer between 0 and 99", + "setFont \"times\" 12"); + DECL_PAINTCOMMAND("setPen", command_setPen, + "^setPen\\s+#?(\\w*)$", + "setPen <color>\nsetPen <pen style enum>\nsetPen brush", + "setPen black"); + DECL_PAINTCOMMAND("setPen", command_setPen2, + "^setPen\\s+(#?\\w*)\\s+([\\w.]+)\\s*(\\w*)\\s*(\\w*)\\s*(\\w*)$", + "setPen brush|<color> [width] [pen style enum] [FlatCap|SquareCap|RoundCap] [MiterJoin|BevelJoin|RoundJoin]", + "setPen black 1 FlatCap MiterJoin"); + DECL_PAINTCOMMAND("pen_setDashOffset", command_pen_setDashOffset, + "^pen_setDashOffset\\s+(-?[\\w.]+)$", + "pen_setDashOffset <offset>\n", + "pen_setDashOffset 1.0"); + DECL_PAINTCOMMAND("pen_setDashPattern", command_pen_setDashPattern, + "^pen_setDashPattern\\s+\\[([\\w\\s.]*)\\]$", + "pen_setDashPattern <[ <dash_1> <space_1> ... <dash_n> <space_n> ]>", + "pen_setDashPattern [ 2 1 4 1 3 3 ]"); + DECL_PAINTCOMMAND("pen_setCosmetic", command_pen_setCosmetic, + "^pen_setCosmetic\\s+(\\w*)$", + "pen_setCosmetic <true|false>", + "pen_setCosmetic true"); + DECL_PAINTCOMMAND("setRenderHint", command_setRenderHint, + "^setRenderHint\\s+([\\w_0-9]*)\\s*(\\w*)$", + "setRenderHint <Antialiasing|SmoothPixmapTransform> <true|false>", + "setRenderHint Antialiasing true"); + DECL_PAINTCOMMAND("clearRenderHint", command_clearRenderHint, + "^clearRenderHint$", + "clearRenderHint", + "clearRenderHint"); + + DECL_PAINTCOMMANDSECTION("gradients"); + DECL_PAINTCOMMAND("gradient_appendStop", command_gradient_appendStop, + "^gradient_appendStop\\s+([\\w.]*)\\s+#?(\\w*)$", + "gradient_appendStop <pos> <color>", + "gradient_appendStop 1.0 red"); + DECL_PAINTCOMMAND("gradient_clearStops", command_gradient_clearStops, + "^gradient_clearStops$", + "gradient_clearStops", + "gradient_clearStops"); + DECL_PAINTCOMMAND("gradient_setConical", command_gradient_setConical, + "^gradient_setConical\\s+([\\w.]*)\\s+([\\w.]*)\\s+([\\w.]*)$", + "gradient_setConical <cx> <cy> <angle>\n - angle in degrees", + "gradient_setConical 5.0 5.0 45.0"); + DECL_PAINTCOMMAND("gradient_setLinear", command_gradient_setLinear, + "^gradient_setLinear\\s+([\\w.]*)\\s+([\\w.]*)\\s+([\\w.]*)\\s+([\\w.]*)$", + "gradient_setLinear <x1> <y1> <x2> <y2>", + "gradient_setLinear 1.0 1.0 2.0 2.0"); + DECL_PAINTCOMMAND("gradient_setRadial", command_gradient_setRadial, + "^gradient_setRadial\\s+([\\w.]*)\\s+([\\w.]*)\\s+([\\w.]*)\\s?([\\w.]*)\\s?([\\w.]*)$", + "gradient_setRadial <cx> <cy> <rad> <fx> <fy>\n - C is the center\n - rad is the radius\n - F is the focal point", + "gradient_setRadial 1.0 1.0 45.0 2.0 2.0"); + DECL_PAINTCOMMAND("gradient_setRadialExtended", command_gradient_setRadialExtended, + "^gradient_setRadialExtended\\s+([\\w.]*)\\s+([\\w.]*)\\s+([\\w.]*)\\s?([\\w.]*)\\s?([\\w.]*)\\s?([\\w.]*)$", + "gradient_setRadialExtended <cx> <cy> <rad> <fx> <fy> <frad>\n - C is the center\n - rad is the center radius\n - F is the focal point\n - frad is the focal radius", + "gradient_setRadialExtended 1.0 1.0 45.0 2.0 2.0 45.0"); + DECL_PAINTCOMMAND("gradient_setLinearPen", command_gradient_setLinearPen, + "^gradient_setLinearPen\\s+([\\w.]*)\\s+([\\w.]*)\\s+([\\w.]*)\\s+([\\w.]*)$", + "gradient_setLinearPen <x1> <y1> <x2> <y2>", + "gradient_setLinearPen 1.0 1.0 2.0 2.0"); + DECL_PAINTCOMMAND("gradient_setSpread", command_gradient_setSpread, + "^gradient_setSpread\\s+(\\w*)$", + "gradient_setSpread <spread method enum>", + "gradient_setSpread PadSpread"); + DECL_PAINTCOMMAND("gradient_setCoordinateMode", command_gradient_setCoordinateMode, + "^gradient_setCoordinateMode\\s+(\\w*)$", + "gradient_setCoordinateMode <coordinate method enum>", + "gradient_setCoordinateMode ObjectBoundingMode"); + DECL_PAINTCOMMANDSECTION("drawing ops"); + DECL_PAINTCOMMAND("drawPoint", command_drawPoint, + "^drawPoint\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)$", + "drawPoint <x> <y>", + "drawPoint 10.0 10.0"); + DECL_PAINTCOMMAND("drawLine", command_drawLine, + "^drawLine\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)$", + "drawLine <x1> <y1> <x2> <y2>", + "drawLine 10.0 10.0 20.0 20.0"); + DECL_PAINTCOMMAND("drawRect", command_drawRect, + "^drawRect\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)$", + "drawRect <x> <y> <w> <h>", + "drawRect 10.0 10.0 20.0 20.0"); + DECL_PAINTCOMMAND("drawRoundRect", command_drawRoundRect, + "^drawRoundRect\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)\\s*(-?\\w*)?\\s*(-?\\w*)?$", + "drawRoundRect <x> <y> <w> <h> [rx] [ry]", + "drawRoundRect 10 10 20 20 3 3"); + DECL_PAINTCOMMAND("drawRoundedRect", command_drawRoundedRect, + "^drawRoundedRect\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)\\s*(\\w*)?$", + "drawRoundedRect <x> <y> <w> <h> <rx> <ry> [SizeMode enum]", + "drawRoundedRect 10 10 20 20 4 4 AbsoluteSize"); + DECL_PAINTCOMMAND("drawArc", command_drawArc, + "^drawArc\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)$", + "drawArc <x> <y> <w> <h> <angleStart> <angleArc>\n - angles are expressed in 1/16th of degree", + "drawArc 10 10 20 20 0 5760"); + DECL_PAINTCOMMAND("drawChord", command_drawChord, + "^drawChord\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)$", + "drawChord <x> <y> <w> <h> <angleStart> <angleArc>\n - angles are expressed in 1/16th of degree", + "drawChord 10 10 20 20 0 5760"); + DECL_PAINTCOMMAND("drawEllipse", command_drawEllipse, + "^drawEllipse\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)$", + "drawEllipse <x> <y> <w> <h>", + "drawEllipse 10.0 10.0 20.0 20.0"); + DECL_PAINTCOMMAND("drawPath", command_drawPath, + "^drawPath\\s+(\\w*)$", + "drawPath <pathName>", + "drawPath mypath"); + DECL_PAINTCOMMAND("drawPie", command_drawPie, + "^drawPie\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)$", + "drawPie <x> <y> <w> <h> <angleStart> <angleArc>\n - angles are expressed in 1/16th of degree", + "drawPie 10 10 20 20 0 5760"); + DECL_PAINTCOMMAND("drawPixmap", command_drawPixmap, + "^drawPixmap\\s+([\\w.:\\-/]*)" + "\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)\\s*(-?[\\w.]*)?\\s*(-?[\\w.]*)?" // target rect + "\\s*(-?[\\w.]*)?\\s*(-?[\\w.]*)?\\s*(-?[\\w.]*)?\\s*(-?[\\w.]*)?$", // source rect + "drawPixmap <filename> <tx> <ty> <tw> <th> <sx> <sy> <sw> <sh>" + "\n- where t means target and s means source" + "\n- a width or height of -1 means maximum space", + "drawPixmap :/images/face.png 0 0 -1 -1 0 0 -1 -1"); + DECL_PAINTCOMMAND("drawImage", command_drawImage, + "^drawImage\\s+([\\w.:\\/]*)" + "\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)\\s*(-?[\\w.]*)?\\s*(-?[\\w.]*)?" // target rect + "\\s*(-?[\\w.]*)?\\s*(-?[\\w.]*)?\\s*(-?[\\w.]*)?\\s*(-?[\\w.]*)?$", // source rect + "drawImage <filename> <tx> <ty> <tw> <th> <sx> <sy> <sw> <sh>" + "\n- where t means target and s means source" + "\n- a width or height of -1 means maximum space", + "drawImage :/images/face.png 0 0 -1 -1 0 0 -1 -1"); + DECL_PAINTCOMMAND("drawPolygon", command_drawPolygon, + "^drawPolygon\\s+\\[([\\w\\s-.]*)\\]\\s*(\\w*)$", + "drawPolygon <[ <x1> <y1> ... <xn> <yn> ]> <Winding|OddEven>", + "drawPolygon [ 1 4 6 8 5 3 ] Winding"); + DECL_PAINTCOMMAND("drawConvexPolygon", command_drawConvexPolygon, + "^drawConvexPolygon\\s+\\[([\\w\\s-.]*)\\]$", + "drawConvexPolygon <[ <x1> <y1> ... <xn> <yn> ]>", + "drawConvexPolygon [ 1 4 6 8 5 3 ]"); + DECL_PAINTCOMMAND("drawPolyline", command_drawPolyline, + "^drawPolyline\\s+\\[([\\w\\s]*)\\]$", + "drawPolyline <[ <x1> <y1> ... <xn> <yn> ]>", + "drawPolyline [ 1 4 6 8 5 3 ]"); + DECL_PAINTCOMMAND("drawText", command_drawText, + "^drawText\\s+(-?\\w*)\\s+(-?\\w*)\\s+\"(.*)\"$", + "drawText <x> <y> <text>", + "drawText 10 10 \"my text\""); + DECL_PAINTCOMMAND("drawStaticText", command_drawStaticText, + "^drawStaticText\\s+(-?\\w*)\\s+(-?\\w*)\\s+\"(.*)\"$", + "drawStaticText <x> <y> <text>", + "drawStaticText 10 10 \"my text\""); + DECL_PAINTCOMMAND("drawTiledPixmap", command_drawTiledPixmap, + "^drawTiledPixmap\\s+([\\w.:\\/]*)" + "\\s+(-?\\w*)\\s+(-?\\w*)\\s*(-?\\w*)\\s*(-?\\w*)" + "\\s*(-?\\w*)\\s*(-?\\w*)$", + "drawTiledPixmap <tile image filename> <tx> <ty> <tx> <ty> <sx> <sy>" + "\n - where t means tile" + "\n - and s is an offset in the tile", + "drawTiledPixmap :/images/alpha.png "); + + DECL_PAINTCOMMANDSECTION("painterPaths"); + DECL_PAINTCOMMAND("path_moveTo", command_path_moveTo, + "^path_moveTo\\s+([.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)$", + "path_moveTo <pathName> <x> <y>", + "path_moveTo mypath 1.0 1.0"); + DECL_PAINTCOMMAND("path_lineTo", command_path_lineTo, + "^path_lineTo\\s+([.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)$", + "path_lineTo <pathName> <x> <y>", + "path_lineTo mypath 1.0 1.0"); + DECL_PAINTCOMMAND("path_addEllipse", command_path_addEllipse, + "^path_addEllipse\\s+(\\w*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)$", + "path_addEllipse <pathName> <x1> <y1> <x2> <y2>", + "path_addEllipse mypath 10.0 10.0 20.0 20.0"); + DECL_PAINTCOMMAND("path_addPolygon", command_path_addPolygon, + "^path_addPolygon\\s+(\\w*)\\s+\\[([\\w\\s]*)\\]\\s*(\\w*)$", + "path_addPolygon <pathName> <[ <x1> <y1> ... <xn> <yn> ]>", + "path_addPolygon mypath [ 1 4 6 8 5 3 ]"); + DECL_PAINTCOMMAND("path_addRect", command_path_addRect, + "^path_addRect\\s+(\\w*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)$", + "path_addRect <pathName> <x1> <y1> <x2> <y2>", + "path_addRect mypath 10.0 10.0 20.0 20.0"); + DECL_PAINTCOMMAND("path_addText", command_path_addText, + "^path_addText\\s+(\\w*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+\"(.*)\"$", + "path_addText <pathName> <x> <y> <text>", + "path_addText mypath 10.0 20.0 \"some text\""); + DECL_PAINTCOMMAND("path_arcTo", command_path_arcTo, + "^path_arcTo\\s+(\\w*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)$", + "path_arcTo <pathName> <x> <y> <w> <h> <angleStart> <angleArc>\n - angles are expressed in degrees", + "path_arcTo mypath 0.0 0.0 10.0 10.0 0.0 360.0"); + DECL_PAINTCOMMAND("path_cubicTo", command_path_cubicTo, + "^path_cubicTo\\s+(\\w*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)$", + "path_cubicTo <pathName> <x1> <y1> <x2> <y2> <x3> <y3>", + "path_cubicTo mypath 0.0 0.0 10.0 10.0 20.0 20.0"); + DECL_PAINTCOMMAND("path_closeSubpath", command_path_closeSubpath, + "^path_closeSubpath\\s+(\\w*)$", + "path_closeSubpath <pathName>", + "path_closeSubpath mypath"); + DECL_PAINTCOMMAND("path_createOutline", command_path_createOutline, + "^path_createOutline\\s+(\\w*)\\s+(\\w*)$", + "path_createOutline <pathName> <newName>", + "path_createOutline mypath myoutline"); + DECL_PAINTCOMMAND("path_debugPrint", command_path_debugPrint, + "^path_debugPrint\\s+(\\w*)$", + "path_debugPrint <pathName>", + "path_debugPrint mypath"); + + DECL_PAINTCOMMANDSECTION("regions"); + DECL_PAINTCOMMAND("region_addRect", command_region_addRect, + "^region_addRect\\s+(\\w*)\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)$", + "region_addRect <regionName> <x1> <y1> <x2> <y2>", + "region_addRect myregion 0.0 0.0 10.0 10.0"); + DECL_PAINTCOMMAND("region_addEllipse", command_region_addEllipse, + "^region_addEllipse\\s+(\\w*)\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)$", + "region_addEllipse <regionName> <x1> <y1> <x2> <y2>", + "region_addEllipse myregion 0.0 0.0 10.0 10.0"); + + DECL_PAINTCOMMANDSECTION("clipping"); + DECL_PAINTCOMMAND("region_getClipRegion", command_region_getClipRegion, + "^region_getClipRegion\\s+(\\w*)$", + "region_getClipRegion <regionName>", + "region_getClipRegion myregion"); + DECL_PAINTCOMMAND("setClipRegion", command_setClipRegion, + "^setClipRegion\\s+(\\w*)\\s*(\\w*)$", + "setClipRegion <regionName> <clip operation enum>", + "setClipRegion myregion ReplaceClip"); + DECL_PAINTCOMMAND("path_getClipPath", command_path_getClipPath, + "^path_getClipPath\\s+([\\w0-9]*)$", + "path_getClipPath <pathName>", + "path_getClipPath mypath"); + DECL_PAINTCOMMAND("setClipPath", command_setClipPath, + "^setClipPath\\s+(\\w*)\\s*(\\w*)$", + "setClipPath <pathName> <clip operation enum>", + "setClipPath mypath ReplaceClip"); + DECL_PAINTCOMMAND("setClipRect", command_setClipRect, + "^setClipRect\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)\\s*(\\w*)$", + "setClipRect <x1> <y1> <x2> <y2> <clip operation enum>", + "setClipRect 0.0 0.0 10.0 10.0 ReplaceClip"); + DECL_PAINTCOMMAND("setClipping", command_setClipping, + "^setClipping\\s+(\\w*)$", + "setClipping <true|false>", + "setClipping true"); + + DECL_PAINTCOMMANDSECTION("surface"); + DECL_PAINTCOMMAND("surface_begin", command_surface_begin, + "^surface_begin\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)$", + "surface_begin <x> <y> <w> <h>", + "surface_begin 0.0 0.0 10.0 10.0"); + DECL_PAINTCOMMAND("surface_end", command_surface_end, + "^surface_end$", + "surface_end", + "surface_end"); + + DECL_PAINTCOMMANDSECTION("painter states"); + DECL_PAINTCOMMAND("restore", command_restore, + "^restore$", + "restore", + "restore"); + DECL_PAINTCOMMAND("save", command_save, + "^save$", + "save", + "save"); + + DECL_PAINTCOMMANDSECTION("pixmaps'n'images"); + DECL_PAINTCOMMAND("pixmap_load", command_pixmap_load, + "^pixmap_load\\s+([\\w.:\\/]*)\\s*([\\w.:\\/]*)$", + "pixmap_load <image filename> <pixmapName>", + "pixmap_load :/images/face.png myPixmap"); + DECL_PAINTCOMMAND("pixmap_setMask", command_pixmap_setMask, + "^pixmap_setMask\\s+([\\w.:\\/]*)\\s+([\\w.:\\/]*)$", + "pixmap_setMask <pixmapName> <bitmap filename>", + "pixmap_setMask myPixmap :/images/bitmap.png"); + DECL_PAINTCOMMAND("bitmap_load", command_bitmap_load, + "^bitmap_load\\s+([\\w.:\\/]*)\\s*([\\w.:\\/]*)$", + "bitmap_load <bitmap filename> <bitmapName>\n - note that the image is stored as a pixmap", + "bitmap_load :/images/bitmap.png myBitmap"); + DECL_PAINTCOMMAND("image_convertToFormat", command_image_convertToFormat, + "^image_convertToFormat\\s+([\\w.:\\/]*)\\s+([\\w.:\\/]+)\\s+([\\w0-9_]*)$", + "image_convertToFormat <sourceImageName> <destImageName> <image format enum>", + "image_convertToFormat myImage myNewImage Indexed8"); + DECL_PAINTCOMMAND("image_load", command_image_load, + "^image_load\\s+([\\w.:\\/]*)\\s*([\\w.:\\/]*)$", + "image_load <filename> <imageName>", + "image_load :/images/face.png myImage"); + DECL_PAINTCOMMAND("image_setColor", command_image_setColor, + "^image_setColor\\s+([\\w.:\\/]*)\\s+([0-9]*)\\s+#([0-9]*)$", + "image_setColor <imageName> <index> <color>", + "image_setColor myImage 0 black"); + DECL_PAINTCOMMAND("image_setColorCount", command_image_setColorCount, + "^image_setColorCount\\s+([\\w.:\\/]*)\\s+([0-9]*)$", + "image_setColorCount <imageName> <nbColors>", + "image_setColorCount myImage 128"); + + DECL_PAINTCOMMANDSECTION("transformations"); + DECL_PAINTCOMMAND("resetMatrix", command_resetMatrix, + "^resetMatrix$", + "resetMatrix", + "resetMatrix"); + DECL_PAINTCOMMAND("setMatrix", command_setMatrix, + "^setMatrix\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)$", + "setMatrix <m11> <m12> <m13> <m21> <m22> <m23> <m31> <m32> <m33>", + "setMatrix 1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0"); + DECL_PAINTCOMMAND("translate", command_translate, + "^translate\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)$", + "translate <tx> <ty>", + "translate 10.0 10.0"); + DECL_PAINTCOMMAND("rotate", command_rotate, + "^rotate\\s+(-?[\\w.]*)$", + "rotate <angle>\n - with angle in degrees", + "rotate 30.0"); + DECL_PAINTCOMMAND("rotate_x", command_rotate_x, + "^rotate_x\\s+(-?[\\w.]*)$", + "rotate_x <angle>\n - with angle in degrees", + "rotate_x 30.0"); + DECL_PAINTCOMMAND("rotate_y", command_rotate_y, + "^rotate_y\\s+(-?[\\w.]*)$", + "rotate_y <angle>\n - with angle in degrees", + "rotate_y 30.0"); + DECL_PAINTCOMMAND("scale", command_scale, + "^scale\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)$", + "scale <sx> <sy>", + "scale 2.0 1.0"); + DECL_PAINTCOMMAND("mapQuadToQuad", command_mapQuadToQuad, + "^mapQuadToQuad\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)$", + "mapQuadToQuad <x1> <y1> <x2> <y2> <x3> <y3> <x4> <y4> <x5> <y5> <x6> <y6> <x7> <y7> <x8> <y8>" + "\n - where vertices 1 to 4 defines the source quad and 5 to 8 the destination quad", + "mapQuadToQuad 0.0 0.0 1.0 1.0 0.0 0.0 -1.0 -1.0"); + + // populate the command lookup hash + for (int i=0; i<s_commandInfoTable.size(); i++) { + if (s_commandInfoTable.at(i).isSectionHeader() || + s_commandInfoTable.at(i).identifier == QLatin1String("comment") || + s_commandInfoTable.at(i).identifier == QLatin1String("noop")) + continue; + s_commandHash.insert(s_commandInfoTable.at(i).identifier, i); + } + + // populate the enums list + ADD_ENUMLIST("brush styles", brushStyleTable); + ADD_ENUMLIST("pen styles", penStyleTable); + ADD_ENUMLIST("font weights", fontWeightTable); + ADD_ENUMLIST("font hintings", fontHintingTable); + ADD_ENUMLIST("clip operations", clipOperationTable); + ADD_ENUMLIST("spread methods", spreadMethodTable); + ADD_ENUMLIST("composition modes", compositionModeTable); + ADD_ENUMLIST("image formats", imageFormatTable); + ADD_ENUMLIST("coordinate modes", coordinateMethodTable); + ADD_ENUMLIST("size modes", sizeModeTable); +} + +#undef DECL_PAINTCOMMAND +#undef ADD_ENUMLIST +/********************************************************************************* +** utility +**********************************************************************************/ +template <typename T> T PaintCommands::image_load(const QString &filepath) +{ + T t(filepath); + + if (t.isNull()) + t = T(":images/" + filepath); + + if (t.isNull()) + t = T("images/" + filepath); + + if (t.isNull()) { + QFileInfo fi(filepath); + QDir dir = fi.absoluteDir(); + dir.cdUp(); + dir.cd("images"); + QString fileName = QString("%1/%2").arg(dir.absolutePath()).arg(fi.fileName()); + t = T(fileName); + if (t.isNull() && !fileName.endsWith(".png")) { + fileName.append(".png"); + t = T(fileName); + } + } + + return t; +} + +/********************************************************************************* +** setters +**********************************************************************************/ +void PaintCommands::insertAt(int commandIndex, const QStringList &newCommands) +{ + int index = 0; + int left = newCommands.size(); + while (left--) + m_commands.insert(++commandIndex, newCommands.at(index++)); +} + +/********************************************************************************* +** run +**********************************************************************************/ +void PaintCommands::runCommand(const QString &scriptLine) +{ + if (scriptLine.isEmpty()) { + command_noop(QRegExp()); + return; + } + if (scriptLine.startsWith('#')) { + command_comment(QRegExp()); + return; + } + QString firstWord = scriptLine.section(QRegExp("\\s"), 0, 0); + QList<int> indices = s_commandHash.values(firstWord); + foreach(int idx, indices) { + const PaintCommandInfos &command = s_commandInfoTable.at(idx); + if (command.regExp.indexIn(scriptLine) >= 0) { + (this->*(command.paintMethod))(command.regExp); + return; + } + } + qWarning("ERROR: unknown command or argument syntax error in \"%s\"", qPrintable(scriptLine)); +} + +void PaintCommands::runCommands() +{ + staticInit(); + int width = m_painter->window().width(); + int height = m_painter->window().height(); + + if (width <= 0) + width = 800; + if (height <= 0) + height = 800; + + // paint background + if (m_checkers_background) { + QPixmap pm(20, 20); + pm.fill(Qt::white); + QPainter pt(&pm); + pt.fillRect(0, 0, 10, 10, QColor::fromRgba(0xffdfdfdf)); + pt.fillRect(10, 10, 10, 10, QColor::fromRgba(0xffdfdfdf)); + pt.end(); + m_painter->drawTiledPixmap(0, 0, width, height, pm); + } else { + m_painter->fillRect(0, 0, width, height, Qt::white); + } + + // run each command + m_abort = false; + for (int i=0; i<m_commands.size() && !m_abort; ++i) { + const QString &commandNow = m_commands.at(i); + m_currentCommand = commandNow; + m_currentCommandIndex = i; + runCommand(commandNow.trimmed()); + } +} + +/********************************************************************************* +** conversions +**********************************************************************************/ +int PaintCommands::convertToInt(const QString &str) +{ + return qRound(convertToDouble(str)); +} + +float PaintCommands::convertToFloat(const QString &str) +{ + return float(convertToDouble(str)); +} + +double PaintCommands::convertToDouble(const QString &str) +{ + static QRegExp re("cp([0-9])([xy])"); + if (str.toLower() == "width") { + if (m_painter->device()->devType() == Qt::Widget) + return m_painter->window().width(); + else + return 800; + } + if (str.toLower() == "height") { + if (m_painter->device()->devType() == Qt::Widget) + return m_painter->window().height(); + else + return 800; + } + if (re.indexIn(str) >= 0) { + int index = re.cap(1).toInt(); + bool is_it_x = re.cap(2) == "x"; + if (index < 0 || index >= m_controlPoints.size()) { + qWarning("ERROR: control point index=%d is out of bounds", index); + return 0; + } + return is_it_x ? m_controlPoints.at(index).x() : m_controlPoints.at(index).y(); + } + return str.toDouble(); +} + +QColor PaintCommands::convertToColor(const QString &str) +{ + static QRegExp alphaColor("#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})"); + static QRegExp opaqueColor("#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})"); + + Q_ASSERT(alphaColor.isValid()); + Q_ASSERT(opaqueColor.isValid()); + + if (alphaColor.indexIn(str) >= 0) { + return QColor(alphaColor.cap(2).toInt(0, 16), + alphaColor.cap(3).toInt(0, 16), + alphaColor.cap(4).toInt(0, 16), + alphaColor.cap(1).toInt(0, 16)); + } else if (opaqueColor.indexIn(str) >= 0) { + return QColor(opaqueColor.cap(1).toInt(0, 16), + opaqueColor.cap(2).toInt(0, 16), + opaqueColor.cap(3).toInt(0, 16)); + } + return QColor(str); +} + +/********************************************************************************* +** command implementations +**********************************************************************************/ +void PaintCommands::command_comment(QRegExp) +{ + if (m_verboseMode) + printf(" -(lance) comment: %s\n", qPrintable(m_currentCommand)); +} + +/***************************************************************************************************/ +void PaintCommands::command_import(QRegExp re) +{ + QString importFile(re.cap(1)); + QFileInfo fi(m_filepath); + QDir dir = fi.absoluteDir(); + QFile *file = new QFile(dir.absolutePath() + QDir::separator() + importFile); + + if (importFile.isEmpty() || !file->exists()) { + dir.cdUp(); + dir.cd("data"); + dir.cd("qps"); + delete file; + file = new QFile(dir.absolutePath() + QDir::separator() + importFile); + } + + if (importFile.isEmpty() || !file->exists()) { + dir.cdUp(); + dir.cd("images"); + delete file; + file = new QFile(dir.absolutePath() + QDir::separator() + importFile); + } + + if (importFile.isEmpty() || !file->exists()) { + printf(" - importing non-existing file at line %d (%s)\n", m_currentCommandIndex, + qPrintable(file->fileName())); + delete file; + return; + } + + if (!file->open(QIODevice::ReadOnly)) { + printf(" - failed to read file: '%s'\n", qPrintable(file->fileName())); + delete file; + return; + } + if (m_verboseMode) + printf(" -(lance) importing file at line %d (%s)\n", m_currentCommandIndex, + qPrintable(fi.fileName())); + + QFileInfo fileinfo(*file); + m_commands[m_currentCommandIndex] = QString("# import file (%1) start").arg(fileinfo.fileName()); + QTextStream textFile(file); + QString rawContent = textFile.readAll(); + QStringList importedData = rawContent.split('\n', QString::SkipEmptyParts); + importedData.append(QString("# import file (%1) end ---").arg(fileinfo.fileName())); + insertAt(m_currentCommandIndex, importedData); + + if (m_verboseMode) { + printf(" -(lance) Command buffer now looks like:\n"); + for (int i = 0; i < m_commands.count(); ++i) + printf(" ---> {%s}\n", qPrintable(m_commands.at(i))); + } + delete file; +} + +/***************************************************************************************************/ +void PaintCommands::command_begin_block(QRegExp re) +{ + const QString &blockName = re.cap(1); + if (m_verboseMode) + printf(" -(lance) begin_block (%s)\n", qPrintable(blockName)); + + m_commands[m_currentCommandIndex] = QString("# begin block (%1)").arg(blockName); + QStringList newBlock; + int i = m_currentCommandIndex + 1; + for (; i < m_commands.count(); ++i) { + const QString &nextCmd = m_commands.at(i); + if (nextCmd.startsWith("end_block")) { + m_commands[i] = QString("# end block (%1)").arg(blockName); + break; + } + newBlock += nextCmd; + } + + if (m_verboseMode) + for (int j = 0; j < newBlock.count(); ++j) + printf(" %d: %s\n", j, qPrintable(newBlock.at(j))); + + if (i >= m_commands.count()) + printf(" - Warning! Block doesn't have an 'end_block' marker!\n"); + + m_blockMap.insert(blockName, newBlock); +} + +/***************************************************************************************************/ +void PaintCommands::command_end_block(QRegExp) +{ + printf(" - end_block should be consumed by begin_block command.\n"); + printf(" You will never see this if your block markers are in sync\n"); + printf(" (noop)\n"); +} + +/***************************************************************************************************/ +void PaintCommands::command_repeat_block(QRegExp re) +{ + QString blockName = re.cap(1); + if (m_verboseMode) + printf(" -(lance) repeating block (%s)\n", qPrintable(blockName)); + + QStringList block = m_blockMap.value(blockName); + if (block.isEmpty()) { + printf(" - repeated block (%s) is empty!\n", qPrintable(blockName)); + return; + } + + m_commands[m_currentCommandIndex] = QString("# repeated block (%1)").arg(blockName); + insertAt(m_currentCommandIndex, block); +} + +/***************************************************************************************************/ +void PaintCommands::command_drawLine(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + double x1 = convertToDouble(caps.at(1)); + double y1 = convertToDouble(caps.at(2)); + double x2 = convertToDouble(caps.at(3)); + double y2 = convertToDouble(caps.at(4)); + + if (m_verboseMode) + printf(" -(lance) drawLine((%.2f, %.2f), (%.2f, %.2f))\n", x1, y1, x2, y2); + + m_painter->drawLine(QLineF(x1, y1, x2, y2)); +} + +/***************************************************************************************************/ +void PaintCommands::command_drawPath(QRegExp re) +{ + if (m_verboseMode) + printf(" -(lance) drawPath(name=%s)\n", qPrintable(re.cap(1))); + + QPainterPath &path = m_pathMap[re.cap(1)]; + m_painter->drawPath(path); +} + +/***************************************************************************************************/ +void PaintCommands::command_drawPixmap(QRegExp re) +{ + QPixmap pm; + pm = m_pixmapMap[re.cap(1)]; // try cache first + if (pm.isNull()) + pm = image_load<QPixmap>(re.cap(1)); + if (pm.isNull()) { + QFileInfo fi(m_filepath); + QDir dir = fi.absoluteDir(); + dir.cdUp(); + dir.cd("images"); + QString fileName = QString("%1/%2").arg(dir.absolutePath()).arg(re.cap(1)); + pm = QPixmap(fileName); + if (pm.isNull() && !fileName.endsWith(".png")) { + fileName.append(".png"); + pm = QPixmap(fileName); + } + } + if (pm.isNull()) { + fprintf(stderr, "ERROR(drawPixmap): failed to load pixmap: '%s'\n", + qPrintable(re.cap(1))); + return; + } + + qreal tx = convertToFloat(re.cap(2)); + qreal ty = convertToFloat(re.cap(3)); + qreal tw = convertToFloat(re.cap(4)); + qreal th = convertToFloat(re.cap(5)); + + qreal sx = convertToFloat(re.cap(6)); + qreal sy = convertToFloat(re.cap(7)); + qreal sw = convertToFloat(re.cap(8)); + qreal sh = convertToFloat(re.cap(9)); + + if (tw == 0) tw = -1; + if (th == 0) th = -1; + if (sw == 0) sw = -1; + if (sh == 0) sh = -1; + + if (m_verboseMode) + printf(" -(lance) drawPixmap('%s' dim=(%d, %d), depth=%d, (%f, %f, %f, %f), (%f, %f, %f, %f)\n", + qPrintable(re.cap(1)), pm.width(), pm.height(), pm.depth(), + tx, ty, tw, th, sx, sy, sw, sh); + + m_painter->drawPixmap(QRectF(tx, ty, tw, th), pm, QRectF(sx, sy, sw, sh)); +} + +/***************************************************************************************************/ +void PaintCommands::command_drawImage(QRegExp re) +{ + QImage im; + im = m_imageMap[re.cap(1)]; // try cache first + if (im.isNull()) + im = image_load<QImage>(re.cap(1)); + + if (im.isNull()) { + QFileInfo fi(m_filepath); + QDir dir = fi.absoluteDir(); + dir.cdUp(); + dir.cd("images"); + QString fileName = QString("%1/%2").arg(dir.absolutePath()).arg(re.cap(1)); + im = QImage(fileName); + if (im.isNull() && !fileName.endsWith(".png")) { + fileName.append(".png"); + im = QImage(fileName); + } + } + if (im.isNull()) { + fprintf(stderr, "ERROR(drawImage): failed to load image: '%s'\n", qPrintable(re.cap(1))); + return; + } + + qreal tx = convertToFloat(re.cap(2)); + qreal ty = convertToFloat(re.cap(3)); + qreal tw = convertToFloat(re.cap(4)); + qreal th = convertToFloat(re.cap(5)); + + qreal sx = convertToFloat(re.cap(6)); + qreal sy = convertToFloat(re.cap(7)); + qreal sw = convertToFloat(re.cap(8)); + qreal sh = convertToFloat(re.cap(9)); + + if (tw == 0) tw = -1; + if (th == 0) th = -1; + if (sw == 0) sw = -1; + if (sh == 0) sh = -1; + + if (m_verboseMode) + printf(" -(lance) drawImage('%s' dim=(%d, %d), (%f, %f, %f, %f), (%f, %f, %f, %f)\n", + qPrintable(re.cap(1)), im.width(), im.height(), tx, ty, tw, th, sx, sy, sw, sh); + + m_painter->drawImage(QRectF(tx, ty, tw, th), im, QRectF(sx, sy, sw, sh), Qt::OrderedDither | Qt::OrderedAlphaDither); +} + +/***************************************************************************************************/ +void PaintCommands::command_drawTiledPixmap(QRegExp re) +{ + QPixmap pm; + pm = m_pixmapMap[re.cap(1)]; // try cache first + if (pm.isNull()) + pm = image_load<QPixmap>(re.cap(1)); + if (pm.isNull()) { + QFileInfo fi(m_filepath); + QDir dir = fi.absoluteDir(); + dir.cdUp(); + dir.cd("images"); + QString fileName = QString("%1/%2").arg(dir.absolutePath()).arg(re.cap(1)); + pm = QPixmap(fileName); + if (pm.isNull() && !fileName.endsWith(".png")) { + fileName.append(".png"); + pm = QPixmap(fileName); + } + } + if (pm.isNull()) { + fprintf(stderr, "ERROR(drawTiledPixmap): failed to load pixmap: '%s'\n", + qPrintable(re.cap(1))); + return; + } + + int tx = convertToInt(re.cap(2)); + int ty = convertToInt(re.cap(3)); + int tw = convertToInt(re.cap(4)); + int th = convertToInt(re.cap(5)); + + int sx = convertToInt(re.cap(6)); + int sy = convertToInt(re.cap(7)); + + if (tw == 0) tw = -1; + if (th == 0) th = -1; + + if (m_verboseMode) + printf(" -(lance) drawTiledPixmap('%s' dim=(%d, %d), (%d, %d, %d, %d), (%d, %d)\n", + qPrintable(re.cap(1)), pm.width(), pm.height(), tx, ty, tw, th, sx, sy); + + m_painter->drawTiledPixmap(tx, ty, tw, th, pm, sx, sy); +} + +/***************************************************************************************************/ +void PaintCommands::command_drawPoint(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + float x = convertToFloat(caps.at(1)); + float y = convertToFloat(caps.at(2)); + + if (m_verboseMode) + printf(" -(lance) drawPoint(%.2f, %.2f)\n", x, y); + + m_painter->drawPoint(QPointF(x, y)); +} + +/***************************************************************************************************/ +void PaintCommands::command_drawPolygon(QRegExp re) +{ + static QRegExp separators("\\s"); + QStringList caps = re.capturedTexts(); + QString cap = caps.at(1); + QStringList numbers = cap.split(separators, QString::SkipEmptyParts); + + QPolygonF array; + for (int i=0; i + 1<numbers.size(); i+=2) + array.append(QPointF(convertToDouble(numbers.at(i)), convertToDouble(numbers.at(i+1)))); + + if (m_verboseMode) + printf(" -(lance) drawPolygon(size=%d)\n", array.size()); + + m_painter->drawPolygon(array, caps.at(2).toLower() == "winding" ? Qt::WindingFill : Qt::OddEvenFill); +} + +/***************************************************************************************************/ +void PaintCommands::command_drawPolyline(QRegExp re) +{ + static QRegExp separators("\\s"); + QStringList numbers = re.cap(1).split(separators, QString::SkipEmptyParts); + + QPolygonF array; + for (int i=0; i + 1<numbers.size(); i+=2) + array.append(QPointF(numbers.at(i).toFloat(),numbers.at(i+1).toFloat())); + + if (m_verboseMode) + printf(" -(lance) drawPolyline(size=%d)\n", array.size()); + + m_painter->drawPolyline(array.toPolygon()); +} + +/***************************************************************************************************/ +void PaintCommands::command_drawRect(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + float x = convertToFloat(caps.at(1)); + float y = convertToFloat(caps.at(2)); + float w = convertToFloat(caps.at(3)); + float h = convertToFloat(caps.at(4)); + + if (m_verboseMode) + printf(" -(lance) drawRect(%.2f, %.2f, %.2f, %.2f)\n", x, y, w, h); + + m_painter->drawRect(QRectF(x, y, w, h)); +} + +/***************************************************************************************************/ +void PaintCommands::command_drawRoundedRect(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + float x = convertToFloat(caps.at(1)); + float y = convertToFloat(caps.at(2)); + float w = convertToFloat(caps.at(3)); + float h = convertToFloat(caps.at(4)); + float xr = convertToFloat(caps.at(5)); + float yr = convertToFloat(caps.at(6)); + + int mode = translateEnum(sizeModeTable, caps.at(7), sizeof(sizeModeTable)/sizeof(char *)); + if (mode < 0) + mode = Qt::AbsoluteSize; + + if (m_verboseMode) + printf(" -(lance) drawRoundRect(%f, %f, %f, %f, %f, %f, %s)\n", x, y, w, h, xr, yr, mode ? "RelativeSize" : "AbsoluteSize"); + + m_painter->drawRoundedRect(QRectF(x, y, w, h), xr, yr, Qt::SizeMode(mode)); +} + +/***************************************************************************************************/ +void PaintCommands::command_drawRoundRect(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + int x = convertToInt(caps.at(1)); + int y = convertToInt(caps.at(2)); + int w = convertToInt(caps.at(3)); + int h = convertToInt(caps.at(4)); + int xs = caps.at(5).isEmpty() ? 50 : convertToInt(caps.at(5)); + int ys = caps.at(6).isEmpty() ? 50 : convertToInt(caps.at(6)); + + if (m_verboseMode) + printf(" -(lance) drawRoundRect(%d, %d, %d, %d, [%d, %d])\n", x, y, w, h, xs, ys); + + m_painter->drawRoundRect(x, y, w, h, xs, ys); +} + +/***************************************************************************************************/ +void PaintCommands::command_drawEllipse(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + float x = convertToFloat(caps.at(1)); + float y = convertToFloat(caps.at(2)); + float w = convertToFloat(caps.at(3)); + float h = convertToFloat(caps.at(4)); + + if (m_verboseMode) + printf(" -(lance) drawEllipse(%.2f, %.2f, %.2f, %.2f)\n", x, y, w, h); + + m_painter->drawEllipse(QRectF(x, y, w, h)); +} + +/***************************************************************************************************/ +void PaintCommands::command_drawPie(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + int x = convertToInt(caps.at(1)); + int y = convertToInt(caps.at(2)); + int w = convertToInt(caps.at(3)); + int h = convertToInt(caps.at(4)); + int angle = convertToInt(caps.at(5)); + int sweep = convertToInt(caps.at(6)); + + if (m_verboseMode) + printf(" -(lance) drawPie(%d, %d, %d, %d, %d, %d)\n", x, y, w, h, angle, sweep); + + m_painter->drawPie(x, y, w, h, angle, sweep); +} + +/***************************************************************************************************/ +void PaintCommands::command_drawChord(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + int x = convertToInt(caps.at(1)); + int y = convertToInt(caps.at(2)); + int w = convertToInt(caps.at(3)); + int h = convertToInt(caps.at(4)); + int angle = convertToInt(caps.at(5)); + int sweep = convertToInt(caps.at(6)); + + if (m_verboseMode) + printf(" -(lance) drawChord(%d, %d, %d, %d, %d, %d)\n", x, y, w, h, angle, sweep); + + m_painter->drawChord(x, y, w, h, angle, sweep); +} + +/***************************************************************************************************/ +void PaintCommands::command_drawArc(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + int x = convertToInt(caps.at(1)); + int y = convertToInt(caps.at(2)); + int w = convertToInt(caps.at(3)); + int h = convertToInt(caps.at(4)); + int angle = convertToInt(caps.at(5)); + int sweep = convertToInt(caps.at(6)); + + if (m_verboseMode) + printf(" -(lance) drawArc(%d, %d, %d, %d, %d, %d)\n", x, y, w, h, angle, sweep); + + m_painter->drawArc(x, y, w, h, angle, sweep); +} + +/***************************************************************************************************/ +void PaintCommands::command_drawText(QRegExp re) +{ + if (!m_shouldDrawText) + return; + QStringList caps = re.capturedTexts(); + int x = convertToInt(caps.at(1)); + int y = convertToInt(caps.at(2)); + QString txt = caps.at(3); + + if (m_verboseMode) + printf(" -(lance) drawText(%d, %d, %s)\n", x, y, qPrintable(txt)); + + m_painter->drawText(x, y, txt); +} + +void PaintCommands::command_drawStaticText(QRegExp re) +{ + if (!m_shouldDrawText) + return; + QStringList caps = re.capturedTexts(); + int x = convertToInt(caps.at(1)); + int y = convertToInt(caps.at(2)); + QString txt = caps.at(3); + + if (m_verboseMode) + printf(" -(lance) drawStaticText(%d, %d, %s)\n", x, y, qPrintable(txt)); + + m_painter->drawStaticText(x, y, QStaticText(txt)); +} + +/***************************************************************************************************/ +void PaintCommands::command_noop(QRegExp) +{ + if (m_verboseMode) + printf(" -(lance) noop: %s\n", qPrintable(m_currentCommand)); + + if (!m_currentCommand.trimmed().isEmpty()) { + fprintf(stderr, "unknown command: '%s'\n", qPrintable(m_currentCommand.trimmed())); + } +} + +/***************************************************************************************************/ +void PaintCommands::command_path_addText(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + QString name = caps.at(1); + double x = convertToDouble(caps.at(2)); + double y = convertToDouble(caps.at(3)); + QString text = caps.at(4); + + if (m_verboseMode) + printf(" -(lance) path_addText(%s, %.2f, %.2f, text=%s\n", qPrintable(name), x, y, qPrintable(text)); + + m_pathMap[name].addText(x, y, m_painter->font(), text); +} + +/***************************************************************************************************/ +void PaintCommands::command_path_addEllipse(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + QString name = caps.at(1); + double x = convertToDouble(caps.at(2)); + double y = convertToDouble(caps.at(3)); + double w = convertToDouble(caps.at(4)); + double h = convertToDouble(caps.at(5)); + + if (m_verboseMode) + printf(" -(lance) path_addEllipse(%s, %.2f, %.2f, %.2f, %.2f)\n", qPrintable(name), x, y, w, h); + + m_pathMap[name].addEllipse(x, y, w, h); +} + +/***************************************************************************************************/ +void PaintCommands::command_path_addRect(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + QString name = caps.at(1); + double x = convertToDouble(caps.at(2)); + double y = convertToDouble(caps.at(3)); + double w = convertToDouble(caps.at(4)); + double h = convertToDouble(caps.at(5)); + + if (m_verboseMode) + printf(" -(lance) path_addRect(%s, %.2f, %.2f, %.2f, %.2f)\n", qPrintable(name), x, y, w, h); + + m_pathMap[name].addRect(x, y, w, h); +} + +/***************************************************************************************************/ +void PaintCommands::command_path_addPolygon(QRegExp re) +{ + static QRegExp separators("\\s"); + QStringList caps = re.capturedTexts(); + QString name = caps.at(1); + QString cap = caps.at(2); + QStringList numbers = cap.split(separators, QString::SkipEmptyParts); + + QPolygonF array; + for (int i=0; i + 1<numbers.size(); i+=2) + array.append(QPointF(numbers.at(i).toFloat(),numbers.at(i+1).toFloat())); + + if (m_verboseMode) + printf(" -(lance) path_addPolygon(name=%s, size=%d)\n", qPrintable(name), array.size()); + + m_pathMap[name].addPolygon(array); +} + +/***************************************************************************************************/ +void PaintCommands::command_path_arcTo(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + QString name = caps.at(1); + double x = convertToDouble(caps.at(2)); + double y = convertToDouble(caps.at(3)); + double w = convertToDouble(caps.at(4)); + double h = convertToDouble(caps.at(5)); + double angle = convertToDouble(caps.at(6)); + double length = convertToDouble(caps.at(7)); + + if (m_verboseMode) + printf(" -(lance) path_arcTo(%s, %.2f, %.2f, %.2f, %.2f, angle=%.2f, len=%.2f)\n", qPrintable(name), x, y, w, h, angle, length); + + m_pathMap[name].arcTo(x, y, w, h, angle, length); +} + +/***************************************************************************************************/ +void PaintCommands::command_path_createOutline(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + QString name = caps.at(1); + QString newName = caps.at(2); + QPen pen = m_painter->pen(); + + if (m_verboseMode) + printf(" -(lance) path_createOutline(%s, name=%s, width=%d)\n", + qPrintable(name), qPrintable(newName), pen.width()); + + if (!m_pathMap.contains(name)) { + fprintf(stderr, "createOutline(), unknown path: %s\n", qPrintable(name)); + return; + } + QPainterPathStroker stroker; + stroker.setWidth(pen.widthF()); + stroker.setDashPattern(pen.style()); + stroker.setCapStyle(pen.capStyle()); + stroker.setJoinStyle(pen.joinStyle()); + m_pathMap[newName] = stroker.createStroke(m_pathMap[name]); +} + +/***************************************************************************************************/ +void PaintCommands::command_path_cubicTo(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + QString name = caps.at(1); + double x1 = convertToDouble(caps.at(2)); + double y1 = convertToDouble(caps.at(3)); + double x2 = convertToDouble(caps.at(4)); + double y2 = convertToDouble(caps.at(5)); + double x3 = convertToDouble(caps.at(6)); + double y3 = convertToDouble(caps.at(7)); + + if (m_verboseMode) + printf(" -(lance) path_cubicTo(%s, (%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f))\n", qPrintable(name), x1, y1, x2, y2, x3, y3); + + m_pathMap[name].cubicTo(x1, y1, x2, y2, x3, y3); +} + +/***************************************************************************************************/ +void PaintCommands::command_path_moveTo(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + QString name = caps.at(1); + double x1 = convertToDouble(caps.at(2)); + double y1 = convertToDouble(caps.at(3)); + + if (m_verboseMode) + printf(" -(lance) path_moveTo(%s, (%.2f, %.2f))\n", qPrintable(name), x1, y1); + + m_pathMap[name].moveTo(x1, y1); +} + +/***************************************************************************************************/ +void PaintCommands::command_path_lineTo(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + QString name = caps.at(1); + double x1 = convertToDouble(caps.at(2)); + double y1 = convertToDouble(caps.at(3)); + + if (m_verboseMode) + printf(" -(lance) path_lineTo(%s, (%.2f, %.2f))\n", qPrintable(name), x1, y1); + + m_pathMap[name].lineTo(x1, y1); +} + +/***************************************************************************************************/ +void PaintCommands::command_path_setFillRule(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + QString name = caps.at(1); + bool winding = caps.at(2).toLower() == "winding"; + + if (m_verboseMode) + printf(" -(lance) path_setFillRule(name=%s, winding=%d)\n", qPrintable(name), winding); + + m_pathMap[name].setFillRule(winding ? Qt::WindingFill : Qt::OddEvenFill); +} + +/***************************************************************************************************/ +void PaintCommands::command_path_closeSubpath(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + QString name = caps.at(1); + + if (m_verboseMode) + printf(" -(lance) path_closeSubpath(name=%s)\n", qPrintable(name)); + + m_pathMap[name].closeSubpath(); +} + +/***************************************************************************************************/ +void PaintCommands::command_path_getClipPath(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + QString name = caps.at(1); + + if (m_verboseMode) + printf(" -(lance) path_closeSubpath(name=%s)\n", qPrintable(name)); + + m_pathMap[name] = m_painter->clipPath(); +} + +/***************************************************************************************************/ +static void qt_debug_path(const QPainterPath &path, const QString &name) +{ + const char *names[] = { + "MoveTo ", + "LineTo ", + "CurveTo ", + "CurveToData" + }; + + printf("\nQPainterPath (%s): elementCount=%d\n", qPrintable(name), path.elementCount()); + for (int i=0; i<path.elementCount(); ++i) { + const QPainterPath::Element &e = path.elementAt(i); + Q_ASSERT(e.type >= 0 && e.type <= QPainterPath::CurveToDataElement); + printf(" - %3d:: %s, (%.2f, %.2f)\n", i, names[e.type], e.x, e.y); + } +} + +/***************************************************************************************************/ +void PaintCommands::command_path_debugPrint(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + QString name = caps.at(1); + qt_debug_path(m_pathMap[name], name); +} + +/***************************************************************************************************/ +void PaintCommands::command_region_addRect(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + QString name = caps.at(1); + int x = convertToInt(caps.at(2)); + int y = convertToInt(caps.at(3)); + int w = convertToInt(caps.at(4)); + int h = convertToInt(caps.at(5)); + + if (m_verboseMode) + printf(" -(lance) region_addRect(%s, %d, %d, %d, %d)\n", qPrintable(name), x, y, w, h); + + m_regionMap[name] += QRect(x, y, w, h); +} + +/***************************************************************************************************/ +void PaintCommands::command_region_addEllipse(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + QString name = caps.at(1); + int x = convertToInt(caps.at(2)); + int y = convertToInt(caps.at(3)); + int w = convertToInt(caps.at(4)); + int h = convertToInt(caps.at(5)); + + if (m_verboseMode) + printf(" -(lance) region_addEllipse(%s, %d, %d, %d, %d)\n", qPrintable(name), x, y, w, h); + + m_regionMap[name] += QRegion(x, y, w, h, QRegion::Ellipse); +} + +/***************************************************************************************************/ +void PaintCommands::command_region_getClipRegion(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + QString name = caps.at(1); + QRegion region = m_painter->clipRegion(); + + if (m_verboseMode) + printf(" -(lance) region_getClipRegion(name=%s), bounds=[%d, %d, %d, %d]\n", qPrintable(name), + region.boundingRect().x(), + region.boundingRect().y(), + region.boundingRect().width(), + region.boundingRect().height()); + + m_regionMap[name] = region; +} + +/***************************************************************************************************/ +void PaintCommands::command_resetMatrix(QRegExp) +{ + if (m_verboseMode) + printf(" -(lance) resetMatrix()\n"); + + m_painter->resetTransform(); +} + +/***************************************************************************************************/ +void PaintCommands::command_restore(QRegExp) +{ + if (m_verboseMode) + printf(" -(lance) restore()\n"); + + m_painter->restore(); +} + +/***************************************************************************************************/ +void PaintCommands::command_rotate(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + double angle = convertToDouble(caps.at(1)); + + if (m_verboseMode) + printf(" -(lance) rotate(%.2f)\n", angle); + + m_painter->rotate(angle); +} + +/***************************************************************************************************/ +void PaintCommands::command_rotate_x(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + double angle = convertToDouble(caps.at(1)); + + if (m_verboseMode) + printf(" -(lance) rotate_x(%.2f)\n", angle); + + QTransform transform; + transform.rotate(angle, Qt::XAxis); + m_painter->setTransform(transform, true); +} + +/***************************************************************************************************/ +void PaintCommands::command_rotate_y(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + double angle = convertToDouble(caps.at(1)); + + if (m_verboseMode) + printf(" -(lance) rotate_y(%.2f)\n", angle); + + QTransform transform; + transform.rotate(angle, Qt::YAxis); + m_painter->setTransform(transform, true); +} + +/***************************************************************************************************/ +void PaintCommands::command_save(QRegExp) +{ + if (m_verboseMode) + printf(" -(lance) save()\n"); + + m_painter->save(); +} + +/***************************************************************************************************/ +void PaintCommands::command_mapQuadToQuad(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + double x1 = convertToDouble(caps.at(1)); + double y1 = convertToDouble(caps.at(2)); + double x2 = convertToDouble(caps.at(3)); + double y2 = convertToDouble(caps.at(4)); + double x3 = convertToDouble(caps.at(5)); + double y3 = convertToDouble(caps.at(6)); + double x4 = convertToDouble(caps.at(7)); + double y4 = convertToDouble(caps.at(8)); + QPolygonF poly1(4); + poly1[0] = QPointF(x1, y1); + poly1[1] = QPointF(x2, y2); + poly1[2] = QPointF(x3, y3); + poly1[3] = QPointF(x4, y4); + + double x5 = convertToDouble(caps.at(9)); + double y5 = convertToDouble(caps.at(10)); + double x6 = convertToDouble(caps.at(11)); + double y6 = convertToDouble(caps.at(12)); + double x7 = convertToDouble(caps.at(13)); + double y7 = convertToDouble(caps.at(14)); + double x8 = convertToDouble(caps.at(15)); + double y8 = convertToDouble(caps.at(16)); + QPolygonF poly2(4); + poly2[0] = QPointF(x5, y5); + poly2[1] = QPointF(x6, y6); + poly2[2] = QPointF(x7, y7); + poly2[3] = QPointF(x8, y8); + + if (m_verboseMode) + printf(" -(lance) mapQuadToQuad(%.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f ->\n\t" + ",%.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", + x1, y1, x2, y2, x3, y3, x4, y4, x5, y5, x6, y6, x7, y7, x8, y8); + + QTransform trans; + + if (!QTransform::quadToQuad(poly1, poly2, trans)) { + qWarning("Couldn't perform quad to quad transformation!"); + } + + m_painter->setTransform(trans, true); +} + +/***************************************************************************************************/ +void PaintCommands::command_setMatrix(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + double m11 = convertToDouble(caps.at(1)); + double m12 = convertToDouble(caps.at(2)); + double m13 = convertToDouble(caps.at(3)); + double m21 = convertToDouble(caps.at(4)); + double m22 = convertToDouble(caps.at(5)); + double m23 = convertToDouble(caps.at(6)); + double m31 = convertToDouble(caps.at(7)); + double m32 = convertToDouble(caps.at(8)); + double m33 = convertToDouble(caps.at(9)); + + if (m_verboseMode) + printf(" -(lance) setMatrix(%.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", + m11, m12, m13, m21, m22, m23, m31, m32, m33); + + QTransform trans; + trans.setMatrix(m11, m12, m13, + m21, m22, m23, + m31, m32, m33); + + m_painter->setTransform(trans, true); +} + +/***************************************************************************************************/ +void PaintCommands::command_scale(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + double sx = convertToDouble(caps.at(1)); + double sy = convertToDouble(caps.at(2)); + + if (m_verboseMode) + printf(" -(lance) scale(%.2f, %.2f)\n", sx, sy); + + + m_painter->scale(sx, sy); +} + +/***************************************************************************************************/ +void PaintCommands::command_setBackground(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + QColor color = convertToColor(caps.at(1)); + QString pattern = caps.at(2); + + int style = translateEnum(brushStyleTable, pattern, Qt::LinearGradientPattern); + if (style < 0) + style = Qt::SolidPattern; + + if (m_verboseMode) + printf(" -(lance) setBackground(%s, %s)\n", qPrintable(color.name()), qPrintable(pattern)); + + m_painter->setBackground(QBrush(color, Qt::BrushStyle(style))); +} + +/***************************************************************************************************/ +void PaintCommands::command_setOpacity(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + double opacity = convertToDouble(caps.at(1)); + + if (m_verboseMode) + printf(" -(lance) setOpacity(%lf)\n", opacity); + + m_painter->setOpacity(opacity); +} + +/***************************************************************************************************/ +void PaintCommands::command_setBgMode(QRegExp re) +{ + QString cap = re.cap(2); + Qt::BGMode mode = Qt::TransparentMode; + if (cap.toLower() == QLatin1String("opaquemode") || cap.toLower() == QLatin1String("opaque")) + mode = Qt::OpaqueMode; + + if (m_verboseMode) + printf(" -(lance) setBackgroundMode(%s)\n", mode == Qt::OpaqueMode ? "OpaqueMode" : "TransparentMode"); + + m_painter->setBackgroundMode(mode); +} + +/***************************************************************************************************/ +void PaintCommands::command_setBrush(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + + QPixmap pm = image_load<QPixmap>(caps.at(1)); + if (!pm.isNull()) { // Assume pixmap + if (m_verboseMode) + printf(" -(lance) setBrush(pixmap=%s, width=%d, height=%d)\n", + qPrintable(caps.at(1)), pm.width(), pm.height()); + + m_painter->setBrush(QBrush(pm)); + } else if (caps.at(1).toLower() == "nobrush") { + m_painter->setBrush(Qt::NoBrush); + if (m_verboseMode) + printf(" -(lance) setBrush(Qt::NoBrush)\n"); + } else { + QColor color = convertToColor(caps.at(1)); + QString pattern = caps.at(2); + + int style = translateEnum(brushStyleTable, pattern, Qt::LinearGradientPattern); + if (style < 0) + style = Qt::SolidPattern; + + if (m_verboseMode) + printf(" -(lance) setBrush(%s, %s (%d))\n", qPrintable(color.name()), qPrintable(pattern), style); + + m_painter->setBrush(QBrush(color, Qt::BrushStyle(style))); + } +} + +/***************************************************************************************************/ +void PaintCommands::command_setBrushOrigin(QRegExp re) +{ + int x = convertToInt(re.cap(1)); + int y = convertToInt(re.cap(2)); + + if (m_verboseMode) + printf(" -(lance) setBrushOrigin(%d, %d)\n", x, y); + + m_painter->setBrushOrigin(x, y); +} + +/***************************************************************************************************/ +void PaintCommands::command_brushTranslate(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + double dx = convertToDouble(caps.at(1)); + double dy = convertToDouble(caps.at(2)); + + if (m_verboseMode) + printf(" -(lance) brushTranslate(%f, %f)\n", dx, dy); + + QBrush new_brush = m_painter->brush(); + QTransform brush_matrix = new_brush.transform(); + brush_matrix.translate(dx, dy); + new_brush.setTransform(brush_matrix); + m_painter->setBrush(new_brush); +} + +/***************************************************************************************************/ +void PaintCommands::command_brushScale(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + double sx = convertToDouble(caps.at(1)); + double sy = convertToDouble(caps.at(2)); + + if (m_verboseMode) + printf(" -(lance) brushScale(%f, %f)\n", sx, sy); + + QBrush new_brush = m_painter->brush(); + QTransform brush_matrix = new_brush.transform(); + brush_matrix.scale(sx, sy); + new_brush.setTransform(brush_matrix); + m_painter->setBrush(new_brush); +} + +/***************************************************************************************************/ +void PaintCommands::command_brushRotate(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + double rot = convertToDouble(caps.at(1)); + + if (m_verboseMode) + printf(" -(lance) brushScale(%f)\n", rot); + + QBrush new_brush = m_painter->brush(); + QTransform brush_matrix = new_brush.transform(); + brush_matrix.rotate(rot); + new_brush.setTransform(brush_matrix); + m_painter->setBrush(new_brush); +} + +/***************************************************************************************************/ +void PaintCommands::command_brushShear(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + double sx = convertToDouble(caps.at(1)); + double sy = convertToDouble(caps.at(2)); + + if (m_verboseMode) + printf(" -(lance) brushShear(%f, %f)\n", sx, sy); + + QBrush new_brush = m_painter->brush(); + QTransform brush_matrix = new_brush.transform(); + brush_matrix.shear(sx, sy); + new_brush.setTransform(brush_matrix); + m_painter->setBrush(new_brush); +} + +/***************************************************************************************************/ +void PaintCommands::command_setClipping(QRegExp re) +{ + bool clipping = re.cap(1).toLower() == "true"; + + if (m_verboseMode) + printf(" -(lance) setClipping(%d)\n", clipping); + + m_painter->setClipping(clipping); +} + +/***************************************************************************************************/ +void PaintCommands::command_setClipRect(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + int x = convertToInt(caps.at(1)); + int y = convertToInt(caps.at(2)); + int w = convertToInt(caps.at(3)); + int h = convertToInt(caps.at(4)); + + int combine = translateEnum(clipOperationTable, caps.at(5), Qt::IntersectClip + 1); + if (combine == -1) + combine = Qt::ReplaceClip; + + if (m_verboseMode) + printf(" -(lance) setClipRect(%d, %d, %d, %d), %s\n", x, y, w, h, clipOperationTable[combine]); + + m_painter->setClipRect(x, y, w, h, Qt::ClipOperation(combine)); +} + +/***************************************************************************************************/ +void PaintCommands::command_setClipPath(QRegExp re) +{ + int combine = translateEnum(clipOperationTable, re.cap(2), Qt::IntersectClip + 1); + if (combine == -1) + combine = Qt::ReplaceClip; + + if (m_verboseMode) + printf(" -(lance) setClipPath(name=%s), %s\n", qPrintable(re.cap(1)), clipOperationTable[combine]); + + if (!m_pathMap.contains(re.cap(1))) + fprintf(stderr, " - setClipPath, no such path"); + m_painter->setClipPath(m_pathMap[re.cap(1)], Qt::ClipOperation(combine)); +} + +/***************************************************************************************************/ +void PaintCommands::command_setClipRegion(QRegExp re) +{ + int combine = translateEnum(clipOperationTable, re.cap(2), Qt::IntersectClip + 1); + if (combine == -1) + combine = Qt::ReplaceClip; + QRegion r = m_regionMap[re.cap(1)]; + + if (m_verboseMode) + printf(" -(lance) setClipRegion(name=%s), bounds=[%d, %d, %d, %d], %s\n", + qPrintable(re.cap(1)), + r.boundingRect().x(), + r.boundingRect().y(), + r.boundingRect().width(), + r.boundingRect().height(), + clipOperationTable[combine]); + + m_painter->setClipRegion(m_regionMap[re.cap(1)], Qt::ClipOperation(combine)); +} + +/***************************************************************************************************/ +void PaintCommands::command_setFont(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + QString family = caps.at(1); + int size = convertToInt(caps.at(2)); + + int weight = translateEnum(fontWeightTable, re.cap(3).toLower(), 5); + if (weight != -1) { + switch (weight) { + case 0: weight = QFont::Light; break; + case 1: weight = QFont::Normal; break; + case 2: weight = QFont::DemiBold; break; + case 3: weight = QFont::Bold; break; + case 4: weight = QFont::Black; break; + } + } else { + weight = convertToInt(re.cap(3)); + } + + bool italic = caps.at(4).toLower() == "true" || caps.at(4).toLower() == "italic"; + + QFont font(family, size, weight, italic); + +#if QT_VERSION >= 0x040800 + int hinting = translateEnum(fontHintingTable, caps.at(5), 4); + if (hinting == -1) + hinting = 0; + else + font.setHintingPreference(QFont::HintingPreference(hinting)); +#else + int hinting = 1; +#endif + if (m_verboseMode) + printf(" -(lance) setFont(family=%s, size=%d, weight=%d, italic=%d hinting=%s\n", + qPrintable(family), size, weight, italic, fontHintingTable[hinting]); + + m_painter->setFont(font); +} + +/***************************************************************************************************/ +void PaintCommands::command_setPen(QRegExp re) +{ + QString cap = re.cap(1); + int style = translateEnum(penStyleTable, cap, Qt::DashDotDotLine + 1); + if (style >= 0) { + if (m_verboseMode) + printf(" -(lance) setPen(%s)\n", qPrintable(cap)); + + m_painter->setPen(Qt::PenStyle(style)); + } else if (cap.toLower() == "brush") { + QPen pen(m_painter->brush(), 0); + if (m_verboseMode) { + printf(" -(lance) setPen(brush), style=%d, color=%08x\n", + pen.brush().style(), pen.color().rgba()); + } + m_painter->setPen(pen); + } else { + QColor color = convertToColor(cap); + if (m_verboseMode) + printf(" -(lance) setPen(%s)\n", qPrintable(color.name())); + + m_painter->setPen(color); + } +} + +/***************************************************************************************************/ +void PaintCommands::command_setPen2(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + + QBrush brush; + + if (caps.at(1).toLower() == "brush") + brush = m_painter->brush(); + else + brush = convertToColor(caps.at(1)); + + double width = convertToDouble(caps.at(2)); + int penStyle = translateEnum(penStyleTable, caps.at(3), Qt::DashDotDotLine + 1); + if (penStyle < 0) + penStyle = Qt::SolidLine; + + Qt::PenCapStyle capStyle = Qt::SquareCap; + if (caps.at(4).toLower() == "flatcap") capStyle = Qt::FlatCap; + else if (caps.at(4).toLower() == "squarecap") capStyle = Qt::SquareCap; + else if (caps.at(4).toLower() == "roundcap") capStyle = Qt::RoundCap; + else if (!caps.at(4).isEmpty()) + fprintf(stderr, "ERROR: setPen, unknown capStyle: %s\n", qPrintable(caps.at(4))); + + Qt::PenJoinStyle joinStyle = Qt::BevelJoin; + if (caps.at(5).toLower() == "miterjoin") joinStyle = Qt::MiterJoin; + else if (caps.at(5).toLower() == "beveljoin") joinStyle = Qt::BevelJoin; + else if (caps.at(5).toLower() == "roundjoin") joinStyle = Qt::RoundJoin; + else if (!caps.at(5).isEmpty()) + fprintf(stderr, "ERROR: setPen, unknown joinStyle: %s\n", qPrintable(caps.at(5))); + + if (m_verboseMode) + printf(" -(lance) setPen(%s, width=%f, style=%d, cap=%d, join=%d)\n", + qPrintable(brush.color().name()), width, penStyle, capStyle, joinStyle); + + m_painter->setPen(QPen(brush, width, Qt::PenStyle(penStyle), capStyle, joinStyle)); +} + +/***************************************************************************************************/ +void PaintCommands::command_setRenderHint(QRegExp re) +{ + QString hintString = re.cap(1).toLower(); + bool on = re.cap(2).isEmpty() || re.cap(2).toLower() == "true"; + if (hintString.contains("antialiasing")) { + if (m_verboseMode) + printf(" -(lance) setRenderHint Antialiasing\n"); + + m_painter->setRenderHint(QPainter::Antialiasing, on); + } else if (hintString.contains("smoothpixmaptransform")) { + if (m_verboseMode) + printf(" -(lance) setRenderHint SmoothPixmapTransform\n"); + m_painter->setRenderHint(QPainter::SmoothPixmapTransform, on); + } else { + fprintf(stderr, "ERROR(setRenderHint): unknown hint '%s'\n", qPrintable(hintString)); + } +} + +/***************************************************************************************************/ +void PaintCommands::command_clearRenderHint(QRegExp /*re*/) +{ + m_painter->setRenderHint(QPainter::Antialiasing, false); + m_painter->setRenderHint(QPainter::SmoothPixmapTransform, false); + if (m_verboseMode) + printf(" -(lance) clearRenderHint\n"); +} + +/***************************************************************************************************/ +void PaintCommands::command_setCompositionMode(QRegExp re) +{ + QString modeString = re.cap(1).toLower(); + int mode = translateEnum(compositionModeTable, modeString, 33); + + if (mode < 0 || mode > QPainter::RasterOp_SourceAndNotDestination) { + fprintf(stderr, "ERROR: invalid mode: %s\n", qPrintable(modeString)); + return; + } + + if (m_verboseMode) + printf(" -(lance) setCompositionMode: %d: %s\n", mode, qPrintable(modeString)); + + m_painter->setCompositionMode(QPainter::CompositionMode(mode)); +} + +/***************************************************************************************************/ +void PaintCommands::command_translate(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + double dx = convertToDouble(caps.at(1)); + double dy = convertToDouble(caps.at(2)); + + if (m_verboseMode) + printf(" -(lance) translate(%f, %f)\n", dx, dy); + + m_painter->translate(dx, dy); +} + +/***************************************************************************************************/ +void PaintCommands::command_pixmap_load(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + + QString fileName = caps.at(1); + QString name = caps.at(2); + + if (name.isEmpty()) + name = fileName; + + QImage im = image_load<QImage>(fileName); + QPixmap px = QPixmap::fromImage(im, Qt::OrderedDither | Qt::OrderedAlphaDither); + + if (m_verboseMode) + printf(" -(lance) pixmap_load(%s as %s), size=[%d, %d], depth=%d\n", + qPrintable(fileName), qPrintable(name), + px.width(), px.height(), px.depth()); + + m_pixmapMap[name] = px; +} + +/***************************************************************************************************/ +void PaintCommands::command_bitmap_load(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + + QString fileName = caps.at(1); + QString name = caps.at(2); + + if (name.isEmpty()) + name = fileName; + + QBitmap bm = image_load<QBitmap>(fileName); + + if (m_verboseMode) + printf(" -(lance) bitmap_load(%s as %s), size=[%d, %d], depth=%d\n", + qPrintable(fileName), qPrintable(name), + bm.width(), bm.height(), bm.depth()); + + m_pixmapMap[name] = bm; +} + +/***************************************************************************************************/ +void PaintCommands::command_pixmap_setMask(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + QBitmap mask = image_load<QBitmap>(caps.at(2)); + + if (m_verboseMode) + printf(" -(lance) pixmap_setMask(%s, %s)\n", qPrintable(caps.at(1)), qPrintable(caps.at(2))); + + if (!m_pixmapMap[caps.at(1)].isNull()) + m_pixmapMap[caps.at(1)].setMask(mask); +} + +/***************************************************************************************************/ +void PaintCommands::command_image_load(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + + QString fileName = caps.at(1); + QString name = caps.at(2); + + if (name.isEmpty()) + name = fileName; + + QImage image = image_load<QImage>(fileName); + + if (m_verboseMode) + printf(" -(lance) image_load(%s as %s), size=[%d, %d], format=%d\n", + qPrintable(fileName), qPrintable(name), + image.width(), image.height(), image.format()); + + m_imageMap[name] = image; +} + +/***************************************************************************************************/ +void PaintCommands::command_image_setColorCount(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + + QString name = caps.at(1); + int count = convertToInt(caps.at(2)); + + if (m_verboseMode) + printf(" -(lance) image_setColorCount(%s), %d -> %d\n", + qPrintable(name), m_imageMap[name].colorCount(), count); + + m_imageMap[name].setColorCount(count); +} + +/***************************************************************************************************/ +void PaintCommands::command_image_setColor(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + + QString name = caps.at(1); + int index = convertToInt(caps.at(2)); + QColor color = convertToColor(caps.at(3)); + + if (m_verboseMode) + printf(" -(lance) image_setColor(%s), %d = %08x\n", qPrintable(name), index, color.rgba()); + + m_imageMap[name].setColor(index, color.rgba()); +} + +/***************************************************************************************************/ +void PaintCommands::command_abort(QRegExp) +{ + m_abort = true; +} + +/***************************************************************************************************/ +void PaintCommands::command_gradient_clearStops(QRegExp) +{ + if (m_verboseMode) + printf(" -(lance) gradient_clearStops\n"); + m_gradientStops.clear(); +} + +/***************************************************************************************************/ +void PaintCommands::command_gradient_appendStop(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + double pos = convertToDouble(caps.at(1)); + QColor color = convertToColor(caps.at(2)); + + if (m_verboseMode) + printf(" -(lance) gradient_appendStop(%.2f, %x)\n", pos, color.rgba()); + + m_gradientStops << QGradientStop(pos, color); +} + +/***************************************************************************************************/ +void PaintCommands::command_gradient_setLinear(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + double x1 = convertToDouble(caps.at(1)); + double y1 = convertToDouble(caps.at(2)); + double x2 = convertToDouble(caps.at(3)); + double y2 = convertToDouble(caps.at(4)); + + if (m_verboseMode) + printf(" -(lance) gradient_setLinear (%.2f, %.2f), (%.2f, %.2f), spread=%d\n", + x1, y1, x2, y2, m_gradientSpread); + + QLinearGradient lg(QPointF(x1, y1), QPointF(x2, y2)); + lg.setStops(m_gradientStops); + lg.setSpread(m_gradientSpread); + lg.setCoordinateMode(m_gradientCoordinate); + QBrush brush(lg); + QTransform brush_matrix = m_painter->brush().transform(); + brush.setTransform(brush_matrix); + m_painter->setBrush(brush); +} + +/***************************************************************************************************/ +void PaintCommands::command_gradient_setLinearPen(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + double x1 = convertToDouble(caps.at(1)); + double y1 = convertToDouble(caps.at(2)); + double x2 = convertToDouble(caps.at(3)); + double y2 = convertToDouble(caps.at(4)); + + if (m_verboseMode) + printf(" -(lance) gradient_setLinear (%.2f, %.2f), (%.2f, %.2f), spread=%d\n", + x1, y1, x2, y2, m_gradientSpread); + + QLinearGradient lg(QPointF(x1, y1), QPointF(x2, y2)); + lg.setStops(m_gradientStops); + lg.setSpread(m_gradientSpread); + lg.setCoordinateMode(m_gradientCoordinate); + QPen pen = m_painter->pen(); + pen.setBrush(lg); + m_painter->setPen(pen); +} + +/***************************************************************************************************/ +void PaintCommands::command_gradient_setRadial(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + double cx = convertToDouble(caps.at(1)); + double cy = convertToDouble(caps.at(2)); + double rad = convertToDouble(caps.at(3)); + double fx = convertToDouble(caps.at(4)); + double fy = convertToDouble(caps.at(5)); + + if (m_verboseMode) + printf(" -(lance) gradient_setRadial center=(%.2f, %.2f), radius=%.2f, focal=(%.2f, %.2f), " + "spread=%d\n", + cx, cy, rad, fx, fy, m_gradientSpread); + + QRadialGradient rg(QPointF(cx, cy), rad, QPointF(fx, fy)); + rg.setStops(m_gradientStops); + rg.setSpread(m_gradientSpread); + rg.setCoordinateMode(m_gradientCoordinate); + QBrush brush(rg); + QTransform brush_matrix = m_painter->brush().transform(); + brush.setTransform(brush_matrix); + m_painter->setBrush(brush); +} + +/***************************************************************************************************/ +void PaintCommands::command_gradient_setRadialExtended(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + double cx = convertToDouble(caps.at(1)); + double cy = convertToDouble(caps.at(2)); + double rad = convertToDouble(caps.at(3)); + double fx = convertToDouble(caps.at(4)); + double fy = convertToDouble(caps.at(5)); + double frad = convertToDouble(caps.at(6)); + + if (m_verboseMode) + printf(" -(lance) gradient_setRadialExtended center=(%.2f, %.2f), radius=%.2f, focal=(%.2f, %.2f), " + "focal radius=%.2f, spread=%d\n", + cx, cy, rad, fx, fy, frad, m_gradientSpread); + +#if QT_VERSION >= 0x040800 + QRadialGradient rg(QPointF(cx, cy), rad, QPointF(fx, fy), frad); +#else + QRadialGradient rg(QPointF(cx, cy), rad, QPointF(fx, fy)); +#endif + rg.setStops(m_gradientStops); + rg.setSpread(m_gradientSpread); + rg.setCoordinateMode(m_gradientCoordinate); + QBrush brush(rg); + QTransform brush_matrix = m_painter->brush().transform(); + brush.setTransform(brush_matrix); + m_painter->setBrush(brush); +} + +/***************************************************************************************************/ +void PaintCommands::command_gradient_setConical(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + double cx = convertToDouble(caps.at(1)); + double cy = convertToDouble(caps.at(2)); + double angle = convertToDouble(caps.at(3)); + + if (m_verboseMode) { + printf(" -(lance) gradient_setConical center=(%.2f, %.2f), angle=%.2f\n, spread=%d", + cx, cy, angle, m_gradientSpread); + } + + QConicalGradient cg(QPointF(cx, cy), angle); + cg.setStops(m_gradientStops); + cg.setSpread(m_gradientSpread); + cg.setCoordinateMode(m_gradientCoordinate); + QBrush brush(cg); + QTransform brush_matrix = m_painter->brush().transform(); + brush.setTransform(brush_matrix); + m_painter->setBrush(brush); +} + +/***************************************************************************************************/ +void PaintCommands::command_gradient_setSpread(QRegExp re) +{ + int spreadMethod = translateEnum(spreadMethodTable, re.cap(1), 3); + + if (m_verboseMode) + printf(" -(lance) gradient_setSpread %d=[%s]\n", spreadMethod, spreadMethodTable[spreadMethod]); + + m_gradientSpread = QGradient::Spread(spreadMethod); +} + +void PaintCommands::command_gradient_setCoordinateMode(QRegExp re) +{ + int coord = translateEnum(coordinateMethodTable, re.cap(1), 3); + + if (m_verboseMode) + printf(" -(lance) gradient_setCoordinateMode %d=[%s]\n", coord, + coordinateMethodTable[coord]); + + m_gradientCoordinate = QGradient::CoordinateMode(coord); +} + +/***************************************************************************************************/ +void PaintCommands::command_surface_begin(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + double x = convertToDouble(caps.at(1)); + double y = convertToDouble(caps.at(2)); + double w = convertToDouble(caps.at(3)); + double h = convertToDouble(caps.at(4)); + + if (m_surface_painter) { + fprintf(stderr, "ERROR: surface already active"); + return; + } + + if (m_verboseMode) + printf(" -(lance) surface_begin, pos=[%.2f, %.2f], size=[%.2f, %.2f]\n", x, y, w, h); + + m_surface_painter = m_painter; + + if (m_type == OpenGLType || m_type == OpenGLPBufferType) { +#ifndef QT_NO_OPENGL + m_surface_pbuffer = new QGLPixelBuffer(qRound(w), qRound(h)); + m_painter = new QPainter(m_surface_pbuffer); + m_painter->fillRect(QRect(0, 0, qRound(w), qRound(h)), Qt::transparent); +#endif +#ifdef Q_WS_X11 + } else if (m_type == WidgetType) { + m_surface_pixmap = QPixmap(qRound(w), qRound(h)); + m_surface_pixmap.fill(Qt::transparent); + m_painter = new QPainter(&m_surface_pixmap); +#endif + } else { + m_surface_image = QImage(qRound(w), qRound(h), QImage::Format_ARGB32_Premultiplied); + m_surface_image.fill(0); + m_painter = new QPainter(&m_surface_image); + } + m_surface_rect = QRectF(x, y, w, h); +} + +/***************************************************************************************************/ +void PaintCommands::command_surface_end(QRegExp) +{ + if (!m_surface_painter) { + fprintf(stderr, "ERROR: surface not active"); + return; + } + + if (m_verboseMode) + printf(" -(lance) surface_end, pos=[%.2f, %.2f], size=[%.2f, %.2f]\n", + m_surface_rect.x(), + m_surface_rect.y(), + m_surface_rect.width(), + m_surface_rect.height()); + m_painter->end(); + + delete m_painter; + m_painter = m_surface_painter; + m_surface_painter = 0; + + if (m_type == OpenGLType || m_type == OpenGLPBufferType) { +#ifndef QT_NO_OPENGL + QImage image = m_surface_pbuffer->toImage(); + QImage new_image(image.bits(), image.width(), + image.height(), QImage::Format_ARGB32_Premultiplied); + QPaintDevice *pdev = m_painter->device(); + if (pdev->devType() == QInternal::Widget) { + QWidget *w = static_cast<QWidget *>(pdev); + static_cast<QGLWidget *>(w)->makeCurrent(); + } else if (pdev->devType() == QInternal::Pbuffer) { + static_cast<QGLPixelBuffer *>(pdev)->makeCurrent(); + } + + m_painter->drawImage(m_surface_rect, new_image); + + delete m_surface_pbuffer; + m_surface_pbuffer = 0; +#endif +#ifdef Q_WS_X11 + } else if (m_type == WidgetType) { + m_painter->drawPixmap(m_surface_rect.topLeft(), m_surface_pixmap); + m_surface_pixmap = QPixmap(); +#endif + } else { + m_painter->drawImage(m_surface_rect, m_surface_image); + m_surface_image = QImage(); + } + m_surface_rect = QRectF(); +} + +/***************************************************************************************************/ +void PaintCommands::command_image_convertToFormat(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + + QString srcName = caps.at(1); + QString destName = caps.at(2); + + if (!m_imageMap.contains(srcName)) { + fprintf(stderr, "ERROR(convertToFormat): no such image '%s'\n", qPrintable(srcName)); + return; + } + + int format = translateEnum(imageFormatTable, caps.at(3), QImage::NImageFormats); + if (format < 0 || format >= QImage::NImageFormats) { + fprintf(stderr, "ERROR(convertToFormat): invalid format %d = '%s'\n", + format, qPrintable(caps.at(3))); + return; + } + + QImage src = m_imageMap[srcName]; + QImage dest = src.convertToFormat(QImage::Format(format), + Qt::OrderedAlphaDither | Qt::OrderedDither); + + if (m_verboseMode) { + printf(" -(lance) convertToFormat %s:%d -> %s:%d\n", + qPrintable(srcName), src.format(), + qPrintable(destName), dest.format()); + } + + m_imageMap[destName] = dest; +} + +/***************************************************************************************************/ +void PaintCommands::command_textlayout_draw(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + + QString text = caps.at(1); + double width = convertToDouble(caps.at(2)); + + if (m_verboseMode) + printf(" -(lance) textlayout_draw text='%s', width=%f\n", + qPrintable(text), width); + + QFont copy = m_painter->font(); + copy.setPointSize(10); + + QTextLayout layout(text, copy, m_painter->device()); + layout.beginLayout(); + + double y_offset = 0; + + while (true) { + QTextLine line = layout.createLine(); + if (!line.isValid()) + break; + line.setLineWidth(width); + line.setPosition(QPointF(0, y_offset)); + + y_offset += line.height(); + } + + layout.draw(m_painter, QPointF(0, 0)); +} + +/***************************************************************************************************/ +void PaintCommands::command_pen_setDashOffset(QRegExp re) +{ + QStringList caps = re.capturedTexts(); + double offset = convertToDouble(caps.at(1)); + + if (m_verboseMode) + printf(" -(lance) setDashOffset(%lf)\n", offset); + + QPen p = m_painter->pen(); + p.setDashOffset(offset); + m_painter->setPen(p); +} + +/***************************************************************************************************/ +void PaintCommands::command_pen_setDashPattern(QRegExp re) +{ + static QRegExp separators("\\s"); + QStringList caps = re.capturedTexts(); + QString cap = caps.at(1); + QStringList numbers = cap.split(separators, QString::SkipEmptyParts); + + QVector<qreal> pattern; + for (int i=0; i<numbers.size(); ++i) + pattern.append(convertToDouble(numbers.at(i))); + + if (m_verboseMode) + printf(" -(lance) pen_setDashPattern(size=%d)\n", pattern.size()); + + QPen p = m_painter->pen(); + p.setDashPattern(pattern); + m_painter->setPen(p); +} + +/***************************************************************************************************/ +void PaintCommands::command_pen_setCosmetic(QRegExp re) +{ + QString hm = re.capturedTexts().at(1); + bool on = hm == "true" || hm == "yes" || hm == "on"; + + if (m_verboseMode) { + printf(" -(lance) pen_setCosmetic(%s)\n", on ? "true" : "false"); + } + + QPen p = m_painter->pen(); + p.setCosmetic(on); + + m_painter->setPen(p); +} + +/***************************************************************************************************/ +void PaintCommands::command_drawConvexPolygon(QRegExp re) +{ + static QRegExp separators("\\s"); + QStringList caps = re.capturedTexts(); + QString cap = caps.at(1); + QStringList numbers = cap.split(separators, QString::SkipEmptyParts); + + QPolygonF array; + for (int i=0; i + 1<numbers.size(); i+=2) + array.append(QPointF(convertToDouble(numbers.at(i)), convertToDouble(numbers.at(i+1)))); + + if (m_verboseMode) + printf(" -(lance) drawConvexPolygon(size=%d)\n", array.size()); + + + m_painter->drawConvexPolygon(array); +} diff --git a/tests/auto/integrationtests/lancelot/paintcommands.h b/tests/auto/integrationtests/lancelot/paintcommands.h new file mode 100644 index 0000000000..925a867f2f --- /dev/null +++ b/tests/auto/integrationtests/lancelot/paintcommands.h @@ -0,0 +1,334 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ +#ifndef PAINTCOMMANDS_H +#define PAINTCOMMANDS_H + +#include <qcolor.h> +#include <qmap.h> +#include <qpainterpath.h> +#include <qregion.h> +#include <qstringlist.h> +#include <qpixmap.h> +#include <qbrush.h> +#include <qhash.h> + +QT_FORWARD_DECLARE_CLASS(QPainter) +QT_FORWARD_DECLARE_CLASS(QRegExp) +#ifndef QT_NO_OPENGL +QT_FORWARD_DECLARE_CLASS(QGLPixelBuffer) +#endif + +enum DeviceType { + WidgetType, + BitmapType, + PixmapType, + ImageType, + ImageMonoType, + OpenGLType, + OpenGLPBufferType, + PictureType, + PrinterType, + PdfType, + PsType, + GrabType, + CustomDeviceType, + CustomWidgetType, + ImageWidgetType +}; + +/************************************************************************/ +class PaintCommands +{ +public: + // construction / initialization + PaintCommands(const QStringList &cmds, int w, int h) + : m_painter(0) + , m_surface_painter(0) + , m_commands(cmds) + , m_gradientSpread(QGradient::PadSpread) + , m_gradientCoordinate(QGradient::LogicalMode) + , m_width(w) + , m_height(h) + , m_verboseMode(false) + , m_type(WidgetType) + , m_checkers_background(true) + , m_shouldDrawText(true) + { staticInit(); } + +public: + void setCheckersBackground(bool b) { staticInit(); m_checkers_background = b; } + void setContents(const QStringList &cmds) { + staticInit(); + m_blockMap.clear(); + m_pathMap.clear(); + m_pixmapMap.clear(); + m_imageMap.clear(); + m_regionMap.clear(); + m_gradientStops.clear(); + m_controlPoints.clear(); + m_gradientSpread = QGradient::PadSpread; + m_gradientCoordinate = QGradient::LogicalMode; + m_commands = cmds; + + + } + void setPainter(QPainter *pt) { staticInit(); m_painter = pt; } + void setType(DeviceType t) { staticInit(); m_type = t; } + void setFilePath(const QString &path) { staticInit(); m_filepath = path; } + void setControlPoints(const QVector<QPointF> &points) { staticInit(); m_controlPoints = points; } + void setVerboseMode(bool v) { staticInit(); m_verboseMode = v; } + void insertAt(int commandIndex, const QStringList &newCommands); + void setShouldDrawText(bool drawText) { m_shouldDrawText = drawText; } + + // run + void runCommands(); + +private: + // run + void runCommand(const QString &scriptLine); + + // conversion methods + int convertToInt(const QString &str); + double convertToDouble(const QString &str); + float convertToFloat(const QString &str); + QColor convertToColor(const QString &str); + + // commands: comments + void command_comment(QRegExp re); + + // commands: importer + void command_import(QRegExp re); + + // commands: blocks + void command_begin_block(QRegExp re); + void command_end_block(QRegExp re); + void command_repeat_block(QRegExp re); + + // commands: misc + void command_textlayout_draw(QRegExp re); + void command_abort(QRegExp re); + + // commands: noops + void command_noop(QRegExp re); + + // commands: setters + void command_setBgMode(QRegExp re); + void command_setBackground(QRegExp re); + void command_setOpacity(QRegExp re); + void command_path_setFillRule(QRegExp re); + void command_setBrush(QRegExp re); + void command_setBrushOrigin(QRegExp re); + void command_brushTranslate(QRegExp re); + void command_brushRotate(QRegExp re); + void command_brushScale(QRegExp re); + void command_brushShear(QRegExp re); + void command_setClipPath(QRegExp re); + void command_setClipRect(QRegExp re); + void command_setClipRectangle(QRegExp re); + void command_setClipRegion(QRegExp re); + void command_setClipping(QRegExp re); + void command_setCompositionMode(QRegExp re); + void command_setFont(QRegExp re); + void command_setPen(QRegExp re); + void command_setPen2(QRegExp re); + void command_pen_setDashOffset(QRegExp re); + void command_pen_setDashPattern(QRegExp re); + void command_pen_setCosmetic(QRegExp re); + void command_setRenderHint(QRegExp re); + void command_clearRenderHint(QRegExp re); + void command_gradient_appendStop(QRegExp re); + void command_gradient_clearStops(QRegExp re); + void command_gradient_setConical(QRegExp re); + void command_gradient_setLinear(QRegExp re); + void command_gradient_setRadial(QRegExp re); + void command_gradient_setRadialExtended(QRegExp re); + void command_gradient_setLinearPen(QRegExp re); + void command_gradient_setSpread(QRegExp re); + void command_gradient_setCoordinateMode(QRegExp re); + + // commands: drawing ops + void command_drawArc(QRegExp re); + void command_drawChord(QRegExp re); + void command_drawConvexPolygon(QRegExp re); + void command_drawEllipse(QRegExp re); + void command_drawImage(QRegExp re); + void command_drawLine(QRegExp re); + void command_drawPath(QRegExp re); + void command_drawPie(QRegExp re); + void command_drawPixmap(QRegExp re); + void command_drawPoint(QRegExp re); + void command_drawPolygon(QRegExp re); + void command_drawPolyline(QRegExp re); + void command_drawRect(QRegExp re); + void command_drawRoundedRect(QRegExp re); + void command_drawRoundRect(QRegExp re); + void command_drawText(QRegExp re); + void command_drawStaticText(QRegExp re); + void command_drawTiledPixmap(QRegExp re); + void command_path_addEllipse(QRegExp re); + void command_path_addPolygon(QRegExp re); + void command_path_addRect(QRegExp re); + void command_path_addText(QRegExp re); + void command_path_arcTo(QRegExp re); + void command_path_closeSubpath(QRegExp re); + void command_path_createOutline(QRegExp re); + void command_path_cubicTo(QRegExp re); + void command_path_debugPrint(QRegExp re); + void command_path_lineTo(QRegExp re); + void command_path_moveTo(QRegExp re); + void command_region_addEllipse(QRegExp re); + void command_region_addRect(QRegExp re); + + // getters + void command_region_getClipRegion(QRegExp re); + void command_path_getClipPath(QRegExp re); + + // commands: surface begin/end + void command_surface_begin(QRegExp re); + void command_surface_end(QRegExp re); + + // commands: save/restore painter state + void command_restore(QRegExp re); + void command_save(QRegExp re); + + // commands: pixmap/image + void command_pixmap_load(QRegExp re); + void command_pixmap_setMask(QRegExp re); + void command_bitmap_load(QRegExp re); + void command_image_convertToFormat(QRegExp re); + void command_image_load(QRegExp re); + void command_image_setColor(QRegExp re); + void command_image_setColorCount(QRegExp re); + + // commands: transformation + void command_resetMatrix(QRegExp re); + void command_translate(QRegExp re); + void command_rotate(QRegExp re); + void command_rotate_x(QRegExp re); + void command_rotate_y(QRegExp re); + void command_scale(QRegExp re); + void command_mapQuadToQuad(QRegExp re); + void command_setMatrix(QRegExp re); + + // attributes + QPainter *m_painter; + QPainter *m_surface_painter; + QImage m_surface_image; + QPixmap m_surface_pixmap; +#ifndef QT_NO_OPENGL + QGLPixelBuffer *m_surface_pbuffer; +#endif + QRectF m_surface_rect; + QStringList m_commands; + QString m_currentCommand; + int m_currentCommandIndex; + QString m_filepath; + QMap<QString, QStringList> m_blockMap; + QMap<QString, QPainterPath> m_pathMap; + QMap<QString, QPixmap> m_pixmapMap; + QMap<QString, QImage> m_imageMap; + QMap<QString, QRegion> m_regionMap; + QGradientStops m_gradientStops; + QGradient::Spread m_gradientSpread; + QGradient::CoordinateMode m_gradientCoordinate; + bool m_abort; + int m_width; + int m_height; + + bool m_verboseMode; + DeviceType m_type; + bool m_checkers_background; + bool m_shouldDrawText; + + QVector<QPointF> m_controlPoints; + + // painter functionalities string tables + static const char *brushStyleTable[]; + static const char *penStyleTable[]; + static const char *fontWeightTable[]; + static const char *fontHintingTable[]; + static const char *clipOperationTable[]; + static const char *spreadMethodTable[]; + static const char *coordinateMethodTable[]; + static const char *compositionModeTable[]; + static const char *imageFormatTable[]; + static const char *sizeModeTable[]; + static int translateEnum(const char *table[], const QString &pattern, int limit); + + // utility + template <typename T> T image_load(const QString &filepath); + + // commands dictionary management + static void staticInit(); + +public: + struct PaintCommandInfos + { + PaintCommandInfos(QString id, void (PaintCommands::*p)(QRegExp), QRegExp r, QString sy, QString sa) + : identifier(id) + , regExp(r) + , syntax(sy) + , sample(sa) + , paintMethod(p) + {} + PaintCommandInfos(QString title) + : identifier(title), paintMethod(0) {} + bool isSectionHeader() const { return paintMethod == 0; } + QString identifier; + QRegExp regExp; + QString syntax; + QString sample; + void (PaintCommands::*paintMethod)(QRegExp); + }; + + static PaintCommandInfos *findCommandById(const QString &identifier) { + for (int i=0; i<s_commandInfoTable.size(); i++) + if (s_commandInfoTable[i].identifier == identifier) + return &s_commandInfoTable[i]; + return 0; + } + + static QList<PaintCommandInfos> s_commandInfoTable; + static QList<QPair<QString,QStringList> > s_enumsTable; + static QMultiHash<QString, int> s_commandHash; +}; + +#endif // PAINTCOMMANDS_H diff --git a/tests/auto/integrationtests/lancelot/scripts/aliasing.qps b/tests/auto/integrationtests/lancelot/scripts/aliasing.qps new file mode 100644 index 0000000000..59878f9c4d --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/aliasing.qps @@ -0,0 +1,156 @@ + +path_moveTo convexPath 25 0 +path_lineTo convexPath 50 50 +path_lineTo convexPath 25 25 +path_lineTo convexPath 0 50 +path_closeSubpath convexPath + +pixmap_load border.png pixmap + +setRenderHint LineAntialiasing false +translate 10 10 + +begin_block drawing + setPen black 1 + setBrush 7f7fff + drawPath convexPath + + setFont "monospace" 8 + setPen black + drawText 0 68 "QwErTy@" + + + setPen black 1 + setBrush 7f7fff + drawRect 0 80 10 5 + + setPen black 1 + setBrush noBrush + drawRect 20 80 10 5 + + setPen noPen + setBrush 7f7fff + drawRect 40 80 10 5 + + + setPen black 2 + setBrush 7f7fff + drawRect 0 90 10 5 + + setPen black 2 + setBrush noBrush + drawRect 20 90 10 5 + + setPen noPen + setBrush 7f7fff + drawRect 40 90 10 5 + + + setPen black 3 + setBrush 7f7fff + drawRect 0 100 10 5 + + setPen black 3 + setBrush noBrush + drawRect 20 100 10 5 + + setPen noPen + setBrush 7f7fff + drawRect 40 100 10 5 + + + setPen black 1 + setBrush noBrush + drawLine 10 110 20 120 + drawLine 30 120 40 110 + + setPen black 2 + setBrush noBrush + drawLine 10 120 20 130 + drawLine 30 130 40 120 + + setPen black 3 + setBrush noBrush + drawLine 10 130 20 140 + drawLine 30 140 40 130 + + drawPixmap pixmap 0 150 + + setRenderHint SmoothPixmapTransform false + drawPixmap pixmap 20 150 15 15 0 0 10 10 + +end_block + +translate 0 180 +setRenderHint LineAntialiasing true +repeat_block drawing +drawText 15 185 "0.0" + +resetMatrix +translate 70.2 10.2 +setRenderHint LineAntialiasing false +repeat_block drawing +translate 0 180 +setRenderHint LineAntialiasing true +repeat_block drawing +translate -0.2 -0.2 +drawText 15 185 "0.2" + + +resetMatrix +translate 130.4 10.4 +setRenderHint LineAntialiasing false +repeat_block drawing +translate 0 180 +setRenderHint LineAntialiasing true +repeat_block drawing +translate -0.4 -0.4 +drawText 15 185 "0.4" + + +resetMatrix +translate 190.5 10.5 +setRenderHint LineAntialiasing false +repeat_block drawing +translate 0 180 +setRenderHint LineAntialiasing true +repeat_block drawing +translate -0.5 -0.5 +drawText 15 185 "0.5" + + +resetMatrix +translate 250.6 10.6 +setRenderHint LineAntialiasing false +repeat_block drawing +translate 0 180 +setRenderHint LineAntialiasing true +repeat_block drawing +translate -0.6 -0.6 +drawText 15 185 "0.6" + + +resetMatrix +translate 310.8 10.8 +setRenderHint LineAntialiasing false +repeat_block drawing +translate 0 180 +setRenderHint LineAntialiasing true +repeat_block drawing +translate -0.8 -0.8 +drawText 15 185 "0.8" + + +resetMatrix +translate 371 11 +setRenderHint LineAntialiasing false +repeat_block drawing +translate 0 180 +setRenderHint LineAntialiasing true +repeat_block drawing +drawText 15 185 "1.0" + + +resetMatrix +drawText 430 95 "Aliased" +drawText 430 275 "Anti-Aliased"
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/arcs.qps b/tests/auto/integrationtests/lancelot/scripts/arcs.qps new file mode 100644 index 0000000000..8a7a468df1 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/arcs.qps @@ -0,0 +1,68 @@ +# Version: 1 +# CheckVsReference: 5 + +setRenderHint LineAntialiasing + +setPen red + +drawEllipse 0 0 600 400 + +path_moveTo arcs 300 200 +path_arcTo arcs 0 0 600 400 0 10 +path_closeSubpath arcs + +path_moveTo arcs 300 200 +path_arcTo arcs 0 0 600 400 20 30 +path_closeSubpath arcs + +path_moveTo arcs 300 200 +path_arcTo arcs 0 0 600 400 60 45 +path_closeSubpath arcs + +path_moveTo arcs 300 200 +path_arcTo arcs 0 0 600 400 115 60 +path_closeSubpath arcs + +path_moveTo arcs 300 200 +path_arcTo arcs 0 0 600 400 180 90 +path_closeSubpath arcs + +path_moveTo arcs 590 200 +path_arcTo arcs 10 10 580 380 0 360 +path_closeSubpath arcs + +path_moveTo arcs 300 200 +path_arcTo arcs 20 20 560 360 0 -10 +path_closeSubpath arcs + +path_moveTo arcs 300 200 +path_arcTo arcs 20 20 560 360 -20 -30 +path_closeSubpath arcs + +path_moveTo arcs 300 200 +path_arcTo arcs 20 20 560 360 -60 -45 +path_closeSubpath arcs + +path_moveTo arcs 300 200 +path_arcTo arcs 20 20 560 360 -115 -60 +path_closeSubpath arcs + +path_moveTo arcs 300 200 +path_arcTo arcs 20 20 560 360 -180 -90 +path_closeSubpath arcs + +setPen black 1 solidline +setBrush #3f00ff00 +drawPath arcs + +# Then again with a matrix set... +translate 200 400 +rotate 10 +scale 0.5 0.5 +setPen red +setBrush nobrush +drawEllipse 0 0 600 400 + +setPen black 1 solidline +setBrush #3f0000ff +drawPath arcs
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/arcs2.qps b/tests/auto/integrationtests/lancelot/scripts/arcs2.qps new file mode 100644 index 0000000000..411ff08014 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/arcs2.qps @@ -0,0 +1,47 @@ +# Version: 1 +# CheckVsReference: 5 + +drawArc 100 100 100 100 0 1440 +drawArc 100 100 100 100 1440 1440 +drawArc 100 100 100 100 2880 1440 +drawArc 100 100 100 100 4320 1440 + +drawArc 100 200 100 100 0 -1440 +drawArc 100 200 100 100 -1440 -1440 +drawArc 100 200 100 100 -2880 -1440 +drawArc 100 200 100 100 -4320 -1440 + +drawArc 200 100 100 100 720 1440 +drawArc 200 100 100 100 2160 1440 +drawArc 200 100 100 100 3600 1440 +drawArc 200 100 100 100 5040 1440 + +drawArc 200 200 100 100 -720 -1440 +drawArc 200 200 100 100 -2160 -1440 +drawArc 200 200 100 100 -3600 -1440 +drawArc 200 200 100 100 -5040 -1440 + + +drawArc 300 100 100 100 3840 480 +drawArc 300 200 100 100 -3840 -480 + +drawArc 300 100 100 100 1600 1340 + +setPen black +drawArc 400 100 200 200 0 5760 +setPen white +drawArc 400 100 200 200 960 960 +drawArc 400 100 200 200 2880 960 +drawArc 400 100 200 200 4800 960 + +setPen black +drawArc 100 350 300 300 160 5760 +drawArc 100 350 300 300 320 5760 +drawArc 100 350 300 300 1920 5760 +drawArc 100 350 300 300 2080 5760 +drawArc 100 350 300 300 3680 5760 +drawArc 100 350 300 300 3840 5760 +drawArc 100 350 300 300 5440 5760 +drawArc 100 350 300 300 5600 5760 +setPen white +drawArc 100 350 300 300 0 5760 diff --git a/tests/auto/integrationtests/lancelot/scripts/background.qps b/tests/auto/integrationtests/lancelot/scripts/background.qps new file mode 100644 index 0000000000..000cfcdd91 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/background.qps @@ -0,0 +1,136 @@ +# Version: 1 +# CheckVsReference: 5% + +translate 10 30 +setBackground 7f7fff +setBackgroundMode Transparent +setPen ff7f7f + +path_moveTo path 0 0 +path_lineTo path 25 0 +path_cubicTo path 50 0 25 25 25 50 +path_lineTo path 0 50 + +bitmap_load bitmap.png bitmap + +begin_block drawing + save + drawRect 0 0 50 50 + + translate 60 0 + drawEllipse 0 0 50 50 + + translate 60 0 + drawPolygon [0 0 50 0 25 50 25 25] + + translate 60 0 + drawPath path + + translate 60 0 + drawPie 0 0 50 50 1440 2000 + + translate 60 0 + drawChord 0 0 50 50 1440 2000 + + translate 60 0 + drawLine 0 0 50 0 + drawLine 0 0 50 50 + drawLine 0 0 0 50 + + translate 60 0 + drawPolyline [0 0 50 0 25 50 25 25] + + translate 60 0 + drawArc 0 0 50 50 1440 2000 + + translate 60 0 + drawText 0 10 "Jambi-Bambi" + + translate 80 0 + drawPixmap bitmap 0 0 + restore + + save + setRenderHint Antialiasing + translate 5 55 + drawRect 0 0 50 50 + + translate 60 0 + drawEllipse 0 0 50 50 + + translate 60 0 + drawPolygon [0 0 50 0 25 50 25 25] + + translate 60 0 + drawPath path + + translate 60 0 + drawPie 0 0 50 50 1440 2000 + + translate 60 0 + drawChord 0 0 50 50 1440 2000 + + translate 60 0 + drawLine 0 0 50 0 + drawLine 0 0 50 50 + drawLine 0 0 0 50 + + translate 60 0 + drawPolyline [0 0 50 0 25 50 25 25] + + translate 60 0 + drawArc 0 0 50 50 1440 2000 + + translate 60 0 + drawText 0 10 "Jambi-Bambi" + + translate 80 0 + drawPixmap bitmap 0 0 + restore +end_block + +translate 0 160 +setBackgroundMode Transparent +setPen ff7f7f 0 dotline flatcap beveljoin +repeat_block drawing + +translate 0 160 +setBackgroundMode Opaque +setPen ff7f7f 0 dotline flatcap beveljoin +repeat_block drawing + +translate 0 160 +setBackgroundMode Transparent +setPen ff7f7f 4 dashline flatcap beveljoin +repeat_block drawing + +translate 0 160 +setBackgroundMode OpaqueMode +setPen ff7f7f 4 dashline flatcap beveljoin +repeat_block drawing + +resetMatrix + +translate 5 5 + +setBrush nobrush +setPen black +setBackgroundMode transparent +drawText 10 15 "TransparentMode with solid 0-width pen" +drawRect 0 0 685 135 + +translate 0 160 +drawText 10 15 "TransparentMode with dotted 0-width pen" +drawRect 0 0 685 135 + +translate 0 160 +drawText 10 15 "OpaqueMode with dotted 0-width pen" +drawRect 0 0 685 135 + +translate 0 160 +drawText 10 15 "TransparentMode with dotted 4-width pen" +drawRect 0 0 685 135 + +translate 0 160 +drawText 10 15 "OpaqueMode with solid 4-width pen" +drawRect 0 0 685 135 diff --git a/tests/auto/integrationtests/lancelot/scripts/background_brush.qps b/tests/auto/integrationtests/lancelot/scripts/background_brush.qps new file mode 100644 index 0000000000..ca1f944964 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/background_brush.qps @@ -0,0 +1,5 @@ +# Version: 1 +# CheckVsReference: 5% + +setBrush #00ff00 crosspattern +import "background.qps"
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/beziers.qps b/tests/auto/integrationtests/lancelot/scripts/beziers.qps new file mode 100644 index 0000000000..9b47cd05ac --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/beziers.qps @@ -0,0 +1,147 @@ +# Version: 1 +# CheckVsReference: 5% + + +setRenderHint LineAntialiasing + +translate 20 20 +path_moveTo fullSize 0 0 +path_cubicTo fullSize 200 100 -100 100 100 0 + +path_moveTo fullSize 0 200 +path_cubicTo fullSize 0 100 100 100 100 200 + +path_moveTo fullSize 0 250 +path_cubicTo fullSize 50 200 50 200 100 250 +drawPath fullSize + +translate 110 0 +scale 10 10 +path_moveTo medSize 0 0 +path_cubicTo medSize 20 10 -10 10 10 0 + +path_moveTo medSize 0 20 +path_cubicTo medSize 0 10 10 10 10 20 + +path_moveTo medSize 0 25 +path_cubicTo medSize 5 20 5 20 10 25 +drawPath medSize + +resetMatrix +translate 240 20 +scale 100 100 +path_moveTo smallSize 0 0 +path_cubicTo smallSize 2 1 -1 1 1 0 + +path_moveTo smallSize 0 2 +path_cubicTo smallSize 0 1 1 1 1 2 + +path_moveTo smallSize 0 2.5 +path_cubicTo smallSize 0.5 2 0.5 2 1 2.5 +drawPath smallSize + +resetMatrix +translate 20 300 +drawPath medSize + +resetMatrix +translate 250 -100 +path_moveTo maxSize 0 500 +path_cubicTo maxSize 1000 0 -500 0 500 500 +drawPath maxSize + +setRenderHint Antialiasing off +resetMatrix + +path_moveTo path1 0 0 +path_cubicTo path1 10 10 0 10 10 0 + +path_moveTo path2 0 0 +path_cubicTo path2 15 15 -5 15 10 0 + +path_moveTo path3 0 0 +path_cubicTo path3 20 20 -10 20 10 0 + +path_moveTo path4 0 0 +path_cubicTo path4 0 5 10 10 0 15 + +path_moveTo path5 0 10 +path_cubicTo path5 10 10 -10 20 0 0 + +path_moveTo path6 0 0 +path_cubicTo path6 10 5 -10 10 0 15 + +setPen black 2 +setBrush nobrush + +translate 10 500 +scale 3 3 +begin_block paths +save +drawPath path1 +translate 20 0 +drawPath path2 +translate 20 0 +drawPath path3 +translate 20 0 +drawPath path4 +translate 20 0 +drawPath path5 +translate 20 0 +drawPath path6 +restore +end_block + +setPen nopen +setBrush black + +translate 0 20 +repeat_block paths + +setRenderHint Antialiasing + +setPen black 2 +setBrush nobrush + +translate 120 -20 +repeat_block paths + +setPen nopen +setBrush black + +translate 0 20 +repeat_block paths + +resetMatrix +path_moveTo miterPath 20 0 +path_cubicTo miterPath 20 20 0 0 1 0 +path_lineTo miterPath -1 -0.2 + +setBrush nobrush + +translate 50 660 +scale 5 5 + +setPen black 4 solidline flatcap miterjoin +drawPath miterPath +setPen red 0 +drawPath miterPath + +path_moveTo miterPath2 21 0.2 +path_lineTo miterPath2 19 0 +path_cubicTo miterPath2 20 0 0 20 0 0 + +translate 30 0 +setPen black 4 solidline flatcap miterjoin +drawPath miterPath2 +setPen red 0 +drawPath miterPath2 + +path_moveTo wonkyPath 0 0 +path_cubicTo wonkyPath 5 15 20 0 17 0 + +translate 30 0 +setPen black 4 solidline flatcap miterjoin +drawPath wonkyPath +setPen red 0 +drawPath wonkyPath diff --git a/tests/auto/integrationtests/lancelot/scripts/bitmaps.qps b/tests/auto/integrationtests/lancelot/scripts/bitmaps.qps new file mode 100644 index 0000000000..a816b9d497 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/bitmaps.qps @@ -0,0 +1,166 @@ +# Version: 1 +# CheckVsReference: 5% + + +#setRenderHint SmoothPixmapTransform + +translate 10 50 +setBackground ff7f7f +setPen 3f3f9f + +bitmap_load dome_mono.png the_pixmap + +save + # Draw with opaque pen/bg in transparent/opaque mode + setBackgroundMode Transparent + drawPixmap the_pixmap 0 0 + setBackgroundMode Opaque + drawPixmap the_pixmap 110 0 + + translate 220 0 + + # Draw with alpha pen/bg in transparent/opaque mode + save + setBackground 7fff7f7f + setPen 7f3f3f9f + setBackgroundMode Transparent + drawPixmap the_pixmap 0 0 + setBackgroundMode Opaque + drawPixmap the_pixmap 110 0 + restore + + translate 220 0 + + # Draw with rotated opaque pen/bg in transparent/opaque mode + setBackgroundMode Transparent + save + translate 50 50 + rotate 10 + translate -50 -50 + drawPixmap the_pixmap 0 0 + restore + setBackgroundMode Opaque + translate 110 0 + save + translate 50 50 + rotate 10 + translate -50 -50 + drawPixmap the_pixmap 0 0 + restore +restore + +translate 0 150 + +save + setBackgroundMode Transparent + drawTiledPixmap the_pixmap 0 0 200 100 + setBackgroundMode Opaque + drawTiledPixmap the_pixmap 210 0 200 100 + + translate 440 -10 + save + rotate 10 + drawTiledPixmap the_pixmap 0 0 200 100 + restore +restore + +translate 0 150 +save + setBackgroundMode Transparent + drawTiledPixmap the_pixmap 0 0 200 100 10 20 + setBackgroundMode Opaque + drawTiledPixmap the_pixmap 210 0 200 100 10 20 + + translate 440 -10 + save + rotate 10 + drawTiledPixmap the_pixmap 0 0 200 100 10 20 + restore +restore + + +pixmap_setMask the_pixmap mask_100.png +drawPixmap the_pixmap 0 150 +setBackgroundMode Opaque +drawPixmap the_pixmap 110 150 + +translate 220 150 +save + translate 50 50 + rotate 10 + translate -50 -50 + setBackgroundMode Transparent + drawPixmap the_pixmap 0 0 +restore + +translate 110 0 +save + translate 50 50 + rotate 10 + translate -50 -50 + setBackgroundMode Opaque + drawPixmap the_pixmap 0 0 +restore + +resetMatrix +translate 10 650 +bitmap_load dome_mono.png the_bitmap +setBackgroundMode Transparent + +begin_block draw_subrected + drawPixmap the_bitmap 0 0 50 50 0 0 50 50 + drawPixmap the_bitmap 50 0 50 50 50 0 50 50 + drawPixmap the_bitmap 0 50 50 50 0 50 50 50 + drawPixmap the_bitmap 50 50 50 50 50 50 50 50 +end_block + +translate 110 0 +setBackgroundMode Opaque +repeat_block draw_subrected + +translate 110 0 +save + translate 20 -10 + rotate 10 + setBackgroundMode Transparent + repeat_block draw_subrected +restore + +translate 110 0 +save + translate 20 -10 + rotate 10 + setBackgroundMode Opaque + repeat_block draw_subrected +restore + +# Some helpful texts + +resetMatrix +setPen black +drawText 10 40 "Transparent" +drawText 120 40 "Opaque" +drawText 230 40 "Trans w/alpha" +drawText 340 40 "Opaque w/alpha" +drawText 450 40 "Trans w/xform" +drawText 560 40 "Opaque w/xform" + +drawText 10 190 "Transparent tiled" +drawText 220 190 "Opaque tiled" +drawText 440 190 "Opaque w/xform" + +drawText 10 340 "Transparent tiled w/offset" +drawText 220 340 "Opaque tiled w/offset" +drawText 440 340 "Opaque w/xform w/offset" + +drawText 10 490 "Trans masked" +drawText 120 490 "Opaque masked" +drawText 230 490 "masked w/xform" +drawText 340 490 "masked w/xform" + +drawText 10 640 "Subrected" +drawText 110 640 "Subrected opaque" +drawText 220 640 "subrect w/xform" +drawText 330 640 "subrect w/xform opaque" + + diff --git a/tests/auto/integrationtests/lancelot/scripts/borderimage.qps b/tests/auto/integrationtests/lancelot/scripts/borderimage.qps new file mode 100644 index 0000000000..ebd4f4d249 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/borderimage.qps @@ -0,0 +1,120 @@ +# Version: 1 +# CheckVsReference: 10% + +image_load borderimage.png borderimage +translate -128 -128 +begin_block draw_border +# top +drawImage borderimage 0 0 16 16 0 0 16 16 +drawImage borderimage 16 0 36 16 16 0 32 16 +drawImage borderimage 52 0 16 16 48 0 16 16 +# sides +drawImage borderimage 0 16 16 16 0 16 16 32 +drawImage borderimage 52 16 16 16 48 16 16 32 +#bottom +drawImage borderimage 0 32 16 16 0 48 16 16 +drawImage borderimage 16 32 36 16 16 48 32 16 +drawImage borderimage 52 32 16 16 48 48 16 16 +end_block draw_border +resetMatrix +begin_block draw_column +translate 1 1 +repeat_block draw_border +translate 0.1 64.1 +repeat_block draw_border +translate 0.1 64.1 +repeat_block draw_border +translate 0.1 64.1 +repeat_block draw_border +translate 0.1 64.1 +repeat_block draw_border +translate 0.1 64.1 +repeat_block draw_border +translate 0.1 64.1 +repeat_block draw_border +translate 0.1 64.1 +repeat_block draw_border +translate 0.1 64.1 +repeat_block draw_border +translate 0.1 64.1 +repeat_block draw_border +end_block draw_column +setRenderHint Antialiasing +resetMatrix +translate 72 0 +repeat_block draw_column +resetMatrix +scale 1.25 1.25 +translate 144 0 +repeat_block draw_border +resetMatrix +scale 1.25 1.25 +translate 246 0 +rotate 30 +repeat_block draw_border +setRenderHint SmoothPixmapTransform +resetMatrix +scale 1.25 1.25 +translate 144 120 +repeat_block draw_border +resetMatrix +scale 1.25 1.25 +translate 246 120 +rotate 30 +repeat_block draw_border +resetMatrix +translate 215 260 +scale 3.55 3.55 +rotate 30 +repeat_block draw_border +resetMatrix +setRenderHint SmoothPixmapTransform off +setRenderHint Antialiasing off +translate 480 627 +rotate 180 +repeat_block draw_column +resetMatrix +setRenderHint Antialiasing +translate 552 627 +rotate 180 +repeat_block draw_column +resetMatrix +setRenderHint Antialiasing off +translate 200.1 520.1 +begin_block one_pixel_border +drawImage borderimage 0 0 16 16 0 0 16 16 +drawImage borderimage 16 0 64 16 16 0 1 1 +drawImage borderimage 80 0 16 16 48 0 16 16 +drawImage borderimage 0 16 16 64 16 0 1 1 +drawImage borderimage 80 16 16 64 16 0 1 1 +drawImage borderimage 0 80 16 16 0 48 16 16 +drawImage borderimage 16 80 64 16 16 0 1 1 +drawImage borderimage 80 80 16 16 48 48 16 16 +end_block one_pixel_border +resetMatrix +translate 205.1 626.1 +scale 0.4 0.4 +repeat_block one_pixel_border +resetMatrix +translate 255.1 624.1 +scale 0.4 0.4 +rotate 10 +repeat_block one_pixel_border +resetMatrix +setPen red +drawRect 0 0 70 680 +drawText 10 670 "aa off" +drawRect 72 0 70 680 +drawText 80 670 "aa on" +drawRect 409 0 70 680 +drawText 419 650 "rot 180" +drawText 419 670 "aa off" +drawRect 481 0 70 680 +drawText 491 650 "rot 180" +drawText 491 670 "aa on" +drawRect 164 0 224 124 +drawText 174 114 "smoothpixmaptransform off" +drawRect 164 128 224 134 +drawText 174 252 "smoothpixmaptransform on" +drawRect 200 520 97 188 +drawText 210 698 "1x1 edges"
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/brush_pens.qps b/tests/auto/integrationtests/lancelot/scripts/brush_pens.qps new file mode 100644 index 0000000000..b9a2bc0ada --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/brush_pens.qps @@ -0,0 +1,104 @@ +# Version: 1 +# CheckVsReference: 5% + +path_addRect p 0 0 75 75 +path_addEllipse p 25 25 75 75 + +translate 10 10 + +begin_block setup_gradient + gradient_clearStops + gradient_appendStop 0 red + gradient_appendStop 0.1 blue + gradient_appendStop 0.2 yellow + gradient_appendStop 0.3 cyan + gradient_appendStop 0.4 magenta + gradient_appendStop 0.5 green + gradient_appendStop 0.6 black + gradient_appendStop 0.7 indianred + gradient_appendStop 0.8 white + gradient_appendStop 0.9 orange + gradient_appendStop 1 blue + gradient_setLinear 0 0 100 100 +end_block + +setPen brush 0 +setBrush nobrush + +begin_block drawing + save + drawLine 0 0 100 100 + + translate 0 100 + drawPath p + + translate 0 110 + drawRect 0 0 100 100 + + translate 0 110 + drawPolyline [0 0 100 0 50 50] + + drawPoint 40 40 + drawPoint 41 40 + drawPoint 42 40 + drawPoint 43 40 + drawPoint 44 40 + drawPoint 45 40 + drawPoint 46 40 + drawPoint 47 40 + drawPoint 48 40 + drawPoint 49 40 + drawPoint 50 40 + + restore +end_block + +save + translate 110 0 + save + setRenderHint Antialiasing + repeat_block drawing + restore + + setBrush dome_rgb32.png + setPen brush 0 + setBrush nobrush + + translate 110 0 + repeat_block drawing + + translate 110 0 + save + setRenderHint Antialiasing + repeat_block drawing + restore +restore + +translate 0 0 + +save + repeat_block setup_gradient + setPen brush 5 + setBrush nobrush + translate 0 350 + repeat_block drawing + + translate 110 0 + save + setRenderHint Antialiasing + repeat_block drawing + restore + + setBrush dome_rgb32.png + setPen brush 5 + setBrush nobrush + + translate 110 0 + repeat_block drawing + + translate 110 0 + save + setRenderHint Antialiasing + repeat_block drawing + restore +restore
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/brushes.qps b/tests/auto/integrationtests/lancelot/scripts/brushes.qps new file mode 100644 index 0000000000..82cbff48b9 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/brushes.qps @@ -0,0 +1,79 @@ +# Version: 1 +# CheckVsReference: 5% + +# Fill the background +drawRect 0 0 width height + +setRenderHint Antialiasing +setRenderHint SmoothPixmapTransform + +translate 10 10 +# Draw all the pattern types as 40x40 rects using green, transparent background +begin_block drawrects +setBrush green Dense1Pattern +drawRect 0 0 40 40 +setBrush green Dense2Pattern +drawRect 40 0 40 40 +setBrush green Dense3Pattern +drawRect 80 0 40 40 +setBrush green Dense4Pattern +drawRect 120 0 40 40 +setBrush green Dense5Pattern +drawRect 160 0 40 40 +setBrush green Dense6Pattern +drawRect 200 0 40 40 +setBrush green Dense7Pattern +drawRect 240 0 40 40 +setBrush green HorPattern +drawRect 280 0 40 40 +setBrush green VerPattern +drawRect 320 0 40 40 +setBrush green CrossPattern +drawRect 360 0 40 40 +setBrush green BDiagPattern +drawRect 400 0 40 40 +setBrush green FDiagPattern +drawRect 440 0 40 40 +setBrush green DiagCrossPattern +drawRect 480 0 40 40 +setBrush green SolidPattern +drawRect 520 0 40 40 +setBrush green NoBrush +drawRect 560 0 40 40 +gradient_setLinear 0 0 0 40 +drawRect 600 0 40 40 +setBrush face.png +drawRect 640 0 80 40 +end_block + +# Switch to opaque mode +setBackground #7fff7f +setBackgroundMode OpaqueMode +translate 0 50 + +# Draw all the pattern types as 40x40 rects using green, opaque background +repeat_block drawrects + +translate 50 50 +rotate 10 + + +setBackgroundMode TransparentMode +repeat_block drawrects +setBackgroundMode OpaqueMode +translate 0 40 +repeat_block drawrects + + +setBrush dot.png +setPen nopen +resetMatrix +drawRect 0 200 50 50 +drawRect 50 200 50 50 + +setPen red +setBrushOrigin 0 250 +drawRect 0 250 50 50 +setBrushOrigin 50 250 +drawRect 50 250 50 50 + diff --git a/tests/auto/integrationtests/lancelot/scripts/clippaths.qps b/tests/auto/integrationtests/lancelot/scripts/clippaths.qps new file mode 100644 index 0000000000..fba89784df --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/clippaths.qps @@ -0,0 +1,60 @@ +# Version: 1 +# CheckVsReference: 5% + +path_addRect hor 0 0 50 10 +path_addRect ver 0 0 10 50 + +translate 10 10 +setPen NoPen + +begin_block clipping +save + + setBrush 0x7f7fff + save + setClipPath hor + drawRect 0 0 100 100 + + setClipPath ver IntersectClip + setBrush black CrossPattern + drawRect 0 0 100 100 + restore + + translate 100 0 + save + setClipPath hor + drawRect 0 0 100 100 + + setClipPath ver ReplaceClip + setBrush black CrossPattern + drawRect 0 0 100 100 + restore + + translate 100 0 + save + setClipPath hor + drawRect 0 0 100 100 + + setClipPath ver UniteClip + setBrush black CrossPattern + drawRect 0 0 100 100 + restore + +restore +end_block + +translate 300 0 +setRenderHint Antialiasing +repeat_block clipping + +translate -300 100 +setRenderHint Antialiasing false +scale 1.2 1.2 +repeat_block clipping + +translate 300 0 +setRenderHint Antialiasing +setRenderHint SmoothPixmapTransform +repeat_block clipping + + diff --git a/tests/auto/integrationtests/lancelot/scripts/clipping.qps b/tests/auto/integrationtests/lancelot/scripts/clipping.qps new file mode 100644 index 0000000000..3694ff2ba7 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/clipping.qps @@ -0,0 +1,182 @@ +# Version: 1 +# CheckVsReference: 5% + +region_addRect clip 50 0 90 190 +region_addRect clip 0 50 180 90 + +region_addRect clip2 30 30 60 60 + +region_addRect clip3 10 10 60 60 + +path_cubicTo path 90 0 50 50 90 90 +path_cubicTo path 0 90 50 50 0 0 + +path_addRect path2 0 0 90 90 +path_moveTo path2 90 45 +path_arcTo path2 0 0 90 90 0 -360 + +path_addRect emptypath 0 0 0 0 +region_addRect emptyregion 0 0 0 0 + +# Normal clip rect +setClipRect 0 0 50 150 +begin_block repaint +save +setBrush red +setPen nopen +resetMatrix +region_getClipRegion tmpclip +path_getClipPath tmpclippath +drawRect 0 0 width height +setBrush #3f0000ff +setClipRegion tmpclip +drawRect 0 0 width height +setClipPath tmpclippath +setBrush #3f00ff00 +drawRect 0 0 width height +restore +end_block + +# Rotated clip rect +translate 100 0 +rotate 10 +setClipRect 0 0 50 150 +repeat_block repaint + +# simple clip region +resetMatrix +translate 0 200 +setClipRegion clip +repeat_block repaint + +# simle rotated clip region +translate 250 -10 +rotate 10 +setClipRegion clip +repeat_block repaint + +# verify that clip is not xformed with painter +resetMatrix +translate 200 0 +setClipRegion clip +rotate 30 +setBrush red +setPen nopen +drawRect 0 0 width height + +resetMatrix +translate 0 400 +save +setClipRegion clip +setClipRegion clip2 IntersectClip +repeat_block repaint +translate 0 100 +rotate 10 +setClipRegion clip +setClipRegion clip2 IntersectClip +restore + +translate 100 0 +save +setClipRegion clip3 +setClipRegion clip2 UniteClip +repeat_block repaint +translate 0 100 +rotate 10 +setClipRegion clip3 +setClipRegion clip2 UniteClip +repeat_block repaint +restore + +translate 100 0 +save +setClipPath path +repeat_block repaint +translate 50 100 +rotate 45 +setClipPath path +repeat_block repaint +restore + +translate 100 0 +save +setClipPath path +setClipPath path2 IntersectClip +repeat_block repaint +translate 0 100 +rotate 10 +setClipPath path +setClipPath path2 IntersectClip +repeat_block repaint +restore + +translate 100 0 +save +setClipPath path +setClipPath path2 UniteClip +repeat_block repaint +translate 0 100 +rotate 10 +setClipPath path +setClipPath path2 UniteClip +repeat_block repaint +restore + +translate 100 0 +save +setClipPath path +setClipRegion clip3 IntersectClip +repeat_block repaint +translate 0 100 +rotate 10 +setClipRegion clip3 +setClipPath path IntersectClip +repeat_block repaint +restore + +translate 100 0 +save +setClipPath path +setClipRegion clip3 UniteClip +repeat_block repaint +translate 0 100 +rotate 10 +setClipRegion clip3 +setClipPath path UniteClip +repeat_block repaint +restore + +# test that an empty region is not drawn. +resetMatrix +setClipRegion emptyregion +setBrush #3f00ff00 +drawRect 0 0 300 300 +drawText 50 50 "Text should be clipped away by region" +setClipping false + +setClipPath emptypath +setBrush #3fffff00 +drawRect 50 50 300 300 +drawText 70 80 "Text should be clipped away by path" + +# Test that we can extract a clipregion when a matrix is set too +resetMatrix +translate 500 10 +scale 2 1 +setBrush blue +setClipping false +rotate 5 +drawRect 0 0 100 100 +setClipRect 0 0 100 100 +resetMatrix +rotate 10 +region_getClipRegion xclip +setClipRegion xclip +resetMatrix +setBrush #7f00ff00 +drawRect 0 0 width height + +# the below used to assert in debug mode +setClipRect 10 10 20 20 +setClipping false +setClipping true
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/clipping_state.qps b/tests/auto/integrationtests/lancelot/scripts/clipping_state.qps new file mode 100644 index 0000000000..a29d3738c8 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/clipping_state.qps @@ -0,0 +1,47 @@ +# Version: 1 +# CheckVsReference: 5% + +path_addRect path1 10 10 50 50 +path_addRect path2 30 30 50 50 +# enable/disable a clip path +setPen nopen +setBrush red +setClipPath path1 +setClipPath path2 UniteClip +drawRect 0 0 100 100 +setClipping false +setBrush #630000ff +drawRect 0 0 100 100 +setClipping true +setBrush #6300ff00 +drawRect 0 0 100 100 +# enable/disable noclip +translate 150 0 +setClipPath path1 NoClip +setClipping false +setBrush #630000ff +drawRect 0 0 100 100 +setClipping true +setBrush #6300ff00 +drawRect 25 25 50 50 +# enable/disable full clipping +translate 150 0 +path_addRect path3 0 0 10 10 +path_addRect path4 20 20 10 10 +setClipPath path3 +setClipPath path4 IntersectClip +setClipping false +setBrush #630000ff +drawRect 0 0 100 100 +setClipping true +setBrush #6300ff00 +drawRect 25 25 50 50 +# disable clipping followed by setClipRect +translate 150 0 +setClipRect 0 0 50 50 ReplaceClip +setClipping false +setBrush #630000ff +drawRect 0 0 100 100 +setClipRect 25 25 75 75 IntersectClip +setBrush #6300ff00 +drawRect 25 25 50 50
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/cliprects.qps b/tests/auto/integrationtests/lancelot/scripts/cliprects.qps new file mode 100644 index 0000000000..0d28b035b4 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/cliprects.qps @@ -0,0 +1,59 @@ +# Version: 1 +# CheckVsReference: 5% + + + +translate 10 10 +setPen NoPen + +begin_block clipping +save + + setBrush 0x7f7fff + save + setClipRect 0 0 50 10 + drawRect 0 0 100 100 + + setClipRect 0 0 10 50 IntersectClip + setBrush black CrossPattern + drawRect 0 0 100 100 + restore + + translate 100 0 + save + setClipRect 0 0 50 10 + drawRect 0 0 100 100 + + setClipRect 0 0 10 50 ReplaceClip + setBrush black CrossPattern + drawRect 0 0 100 100 + restore + + translate 100 0 + save + setClipRect 0 0 50 10 + drawRect 0 0 100 100 + + setClipRect 0 0 10 50 UniteClip + setBrush black CrossPattern + drawRect 0 0 100 100 + restore + +restore +end_block + +translate 300 0 +setRenderHint Antialiasing +repeat_block clipping + +translate -300 100 +setRenderHint Antialiasing false +scale 1.2 1.2 +repeat_block clipping + +translate 300 0 +setRenderHint Antialiasing +setRenderHint SmoothPixmapTransform +repeat_block clipping + + diff --git a/tests/auto/integrationtests/lancelot/scripts/conical_gradients.qps b/tests/auto/integrationtests/lancelot/scripts/conical_gradients.qps new file mode 100644 index 0000000000..2e897b1813 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/conical_gradients.qps @@ -0,0 +1,85 @@ +# Version: 1 +# CheckVsReference: 5% + +path_addRect path 300 0 80 80 +path_addEllipse path 340 40 60 60 + +setRenderHint Antialiasing + +setPen black + +begin_block gradients +gradient_clearStops +gradient_appendStop 0 red +gradient_appendStop 0.25 orange +gradient_appendStop 0.5 yellow +gradient_appendStop 0.8 green +gradient_appendStop 1 cyan + +gradient_setConical 40 40 50 +drawRect 0 0 100 100 + +gradient_setConical 140 40 230 +drawEllipse 100 0 100 100 + +gradient_clearStops +gradient_appendStop 0 3f7f7fff +gradient_appendStop 0.5 dfdfffff +gradient_appendStop 1 7f00007f + +gradient_setConical 240 40 50 +drawPolygon [200 0 290 0 250 99] + +gradient_setConical 340 40 230 +drawPath path + +end_block + +translate 0 100 +scale 1 2 +repeat_block gradients + +resetMatrix +translate 0 300 +brushTranslate 30 0 +brushScale 0.9 0.9 +brushRotate 20 +repeat_block gradients + +# Some helpful info perhaps? +resetMatrix +setPen black + +drawText 410 50 "No XForm" +drawText 410 200 "scale 1x2" +drawText 410 300 "brush transform" +drawText 10 450 "50 deg" +drawText 110 450 "230 deg" +drawText 210 450 "50 deg w/alpha " +drawText 310 450 "230 deg w/alpha" + +setPen 3f000000 +setBrush nobrush + +begin_block ellipse_draw + setClipRect 0 0 100 100 + drawEllipse 35 35 11 11 + save + translate 40 40 + rotate -50 + drawLine -100 0 100 0 + restore + translate 100 0 +end_block + +repeat_block ellipse_draw +repeat_block ellipse_draw +repeat_block ellipse_draw + +resetMatrix +translate 0 100 +scale 1 2 +repeat_block ellipse_draw +repeat_block ellipse_draw +repeat_block ellipse_draw +repeat_block ellipse_draw
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/conical_gradients_perspectives.qps b/tests/auto/integrationtests/lancelot/scripts/conical_gradients_perspectives.qps new file mode 100644 index 0000000000..a9c14f1ce7 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/conical_gradients_perspectives.qps @@ -0,0 +1,64 @@ +# Version: 1 +# CheckVsReference: 5% + + +setRenderHint Antialiasing + +setPen #00ff00 + +translate 10 10 +# standard draw +begin_block gradient +gradient_clearStops +gradient_appendStop 0 red +gradient_appendStop 0.25 orange +gradient_appendStop 0.5 yellow +gradient_appendStop 0.8 green +gradient_appendStop 0.9 cyan +gradient_appendStop 1 red + +gradient_setSpread PadSpread +gradient_setConical 140 140 100 +drawRect 0 0 300 300 +end_block gradient + +# Rotation w/o smooth xform +save +translate 350 0 +save + setRenderHint SmoothPixmapTransform on + mapQuadToQuad 0 0 0 300 300 300 300 0 0 50 0 200 300 300 300 0 + repeat_block gradient +restore +restore + +translate 0 320 + +save + setRenderHint SmoothPixmapTransform on + mapQuadToQuad 0 0 0 300 300 300 300 0 0 0 100 300 200 300 300 0 + repeat_block gradient +restore + +save +translate 350 0 +save + setRenderHint SmoothPixmapTransform on + mapQuadToQuad 0 0 0 300 300 300 300 0 0 0 0 300 300 250 300 50 + repeat_block gradient +restore +restore + + +resetMatrix +setPen black +translate 125 20 +drawText 0 0 "No transform" +translate 350 0 +drawText 0 0 "Left Tilted" +resetMatrix +translate 125 350 +drawText 0 0 "Bottom Tilted" +translate 350 0 +drawText 0 0 "Right Tilted" +translate 120 0
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/dashes.qps b/tests/auto/integrationtests/lancelot/scripts/dashes.qps new file mode 100644 index 0000000000..649f56cf66 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/dashes.qps @@ -0,0 +1,268 @@ +# Version: 1 +# CheckVsReference: 5% + +translate 20 20 + +begin_block draw +save + save + setPen black 1 SolidLine FlatCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + setPen black 1 SolidLine SquareCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + setPen black 1 SolidLine RoundCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + + setPen black 2 SolidLine FlatCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + setPen black 2 SolidLine SquareCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + setPen black 2 SolidLine RoundCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + + setPen black 6 SolidLine FlatCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + setPen black 6 SolidLine SquareCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + setPen black 6 SolidLine RoundCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + restore + + + translate 100 0 + save + setPen black 1 DotLine FlatCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + setPen black 1 DotLine SquareCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + setPen black 1 DotLine RoundCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + + setPen black 2 DotLine FlatCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + setPen black 2 DotLine SquareCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + setPen black 2 DotLine RoundCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + + setPen black 6 DotLine FlatCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + setPen black 6 DotLine SquareCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + setPen black 6 DotLine RoundCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + restore + + translate 100 0 + save + setPen black 1 DashLine FlatCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + setPen black 1 DashLine SquareCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + setPen black 1 DashLine RoundCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + + setPen black 2 DashLine FlatCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + setPen black 2 DashLine SquareCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + setPen black 2 DashLine RoundCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + + setPen black 6 DashLine FlatCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + setPen black 6 DashLine SquareCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + setPen black 6 DashLine RoundCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + restore + + translate 100 0 + + save + setPen black 1 DashDotLine FlatCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + setPen black 1 DashDotLine SquareCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + setPen black 1 DashDotLine RoundCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + + setPen black 2 DashDotLine FlatCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + setPen black 2 DashDotLine SquareCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + setPen black 2 DashDotLine RoundCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + + setPen black 6 DashDotLine FlatCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + setPen black 6 DashDotLine SquareCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + setPen black 6 DashDotLine RoundCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + restore + + translate 100 0 + + save + setPen black 1 DashDotDotLine FlatCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + setPen black 1 DashDotDotLine SquareCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + setPen black 1 DashDotDotLine RoundCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + + setPen black 2 DashDotDotLine FlatCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + setPen black 2 DashDotDotLine SquareCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + setPen black 2 DashDotDotLine RoundCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + + setPen black 4 DashDotDotLine FlatCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + setPen black 4 DashDotDotLine SquareCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + setPen black 4 DashDotDotLine RoundCap BevelJoin + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + restore + + translate 100 0 + + save + setPen black 1 SolidLine FlatCap BevelJoin + pen_setDashPattern [1 4 9 4 27 4] + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + setPen black 1 SolidLine SquareCap BevelJoin + pen_setDashPattern [1 4 9 4 27 4] + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + setPen black 1 SolidLine RoundCap BevelJoin + pen_setDashPattern [1 4 9 4 27 4] + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + + setPen black 2 SolidLine FlatCap BevelJoin + pen_setDashPattern [1 4 9 4 27 4] + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + setPen black 2 SolidLine SquareCap BevelJoin + pen_setDashPattern [1 4 9 4 27 4] + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + setPen black 2 SolidLine RoundCap BevelJoin + pen_setDashPattern [1 4 9 4 27 4] + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + + setPen black 4 SolidLine FlatCap BevelJoin + pen_setDashPattern [1 4 9 4 27 4] + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + setPen black 4 SolidLine SquareCap BevelJoin + pen_setDashPattern [1 4 9 4 27 4] + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + setPen black 4 SolidLine RoundCap BevelJoin + pen_setDashPattern [1 4 9 4 27 4] + drawPolyline [0 0 80 0 80 30 40 20 0 30] + translate 0 40 + restore + +restore +end_block + +translate 0 400 +setRenderHint Antialiasing +repeat_block draw + +translate 0 -20 +drawText 30 0 "Solid" + +translate 100 0 +drawText 20 0 "DotLine" + +translate 100 0 +drawText 10 0 "DashLine" + +translate 100 0 +drawText 0 0 "DashDotLine" + +translate 100 0 +drawText 0 0 "DashDotDotLine" + +translate 100 0 +drawText 0 0 "CustomDashLine" + +resetMatrix + +translate 620 40 + +begin_block width_and_caps_texts + drawText 0 0 "Width=1, FlatCap" + translate 0 40 + drawText 0 0 "Width=1, SquareCap" + translate 0 40 + drawText 0 0 "Width=1, RoundCap" + translate 0 40 + drawText 0 0 "Width=2, FlatCap" + translate 0 40 + drawText 0 0 "Width=2, SquareCap" + translate 0 40 + drawText 0 0 "Width=2, RoundCap" + translate 0 40 + drawText 0 0 "Width=6, FlatCap" + translate 0 40 + drawText 0 0 "Width=6, SqareCap" + translate 0 40 + drawText 0 0 "Width=6, RoundCap" +end_block + +translate 0 80 +repeat_block width_and_caps_texts
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/degeneratebeziers.qps b/tests/auto/integrationtests/lancelot/scripts/degeneratebeziers.qps new file mode 100644 index 0000000000..fb223d5b1f --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/degeneratebeziers.qps @@ -0,0 +1,10 @@ +# Version: 1 +# CheckVsReference: 5% + +path_moveTo degenerate 3427.0918499999997948 3872.1318999999998596 +path_cubicTo degenerate 3427.0918499999997948 3872.1318999999994048 4729.4590867905308187 5176.8613451144155988 5389.9325499999995372 5837.8072499999998399 + +scale 0.05 0.05 +translate -2500 -3000 +setPen black 800 +drawPath degenerate
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/deviceclipping.qps b/tests/auto/integrationtests/lancelot/scripts/deviceclipping.qps new file mode 100644 index 0000000000..cedfc1e363 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/deviceclipping.qps @@ -0,0 +1,48 @@ +# Version: 1 +# CheckVsReference: 5% + +setBrush 0xff7f7f +setPen 0x7f0000 + +path_moveTo path -1000000 10000 +path_cubicTo path 100 100 100 150 150 400 +path_closeSubpath path + +begin_block drawing + + drawPath ellipse + + drawLine -1000000 200 200 200 + drawLine 200 -1000000 200 200 + drawLine 200 200 1000000 200 + drawLine 200 200 200 1000000 + drawLine -1000000 -1000000 200 200 + + drawPolygon [-1000000 100 100 -1000000 100 100] + drawRect 300 -500000 1000000 1000000 + + drawPath path + +end_block + +save +translate 20 20 +setBrush #0x7f7f7fff +setPen #0x7f00007f +repeat_block drawing + +translate 20 20 +setRenderHint Antialiasing +setBrush #0x7f7fff7f +setPen #0x7f007f00 +repeat_block drawing +restore + +setPen 0x00007f 2 +setRenderHint Antialiasing + +drawLine 0 -200 200 200 + +setPen 0x007f00 10 + +drawLine 0 -200 200 0
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/drawpoints.qps b/tests/auto/integrationtests/lancelot/scripts/drawpoints.qps new file mode 100644 index 0000000000..c02cd85737 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/drawpoints.qps @@ -0,0 +1,101 @@ +# Version: 1 +# CheckVsReference: 5% + +#setRenderHint Antialiasing + +setPen red 0 solidline +begin_block points +drawPoint 00 00 +drawPoint 10 00 +drawPoint 20 00 +drawPoint 30 00 +drawPoint 40 00 +drawPoint 50 00 +drawPoint 00 10 +drawPoint 10 10 +drawPoint 20 10 +drawPoint 30 10 +drawPoint 40 10 +drawPoint 50 10 +drawPoint 00 20 +drawPoint 10 20 +drawPoint 20 20 +drawPoint 30 20 +drawPoint 40 20 +drawPoint 50 20 +drawPoint 00 30 +drawPoint 10 30 +drawPoint 20 30 +drawPoint 30 30 +drawPoint 40 30 +drawPoint 50 30 +drawPoint 00 40 +drawPoint 10 40 +drawPoint 20 40 +drawPoint 30 40 +drawPoint 40 40 +drawPoint 50 40 +drawPoint 00 50 +drawPoint 10 50 +drawPoint 20 50 +drawPoint 30 50 +drawPoint 40 50 +drawPoint 50 50 +end_block points + +translate 100 0 +setPen blue 1 solidline +repeat_block points + +translate 100 0 +setPen green 5 solidline roundcap +repeat_block points + +resetMatrix + +translate 0 100 +scale 3 3 +setPen red 0 solidline roundcap +repeat_block points + +translate 60 0 +setPen blue 1 solidline roundcap +repeat_block points + +translate 60 0 +setPen green 5 solidline roundcap +repeat_block points + +resetMatrix + +translate 0 300 +scale 3 3 +setPen red 0 solidline flatcap +repeat_block points + +translate 60 0 +setPen blue 1 solidline flatcap +repeat_block points + +translate 60 0 +setPen green 5 solidline flatcap +repeat_block points + +resetMatrix +translate 10 500 +setPen black 1 solidline flatcap +drawPoint 0 0 +setPen black 2 solidline flatcap +drawPoint 3 0 +setPen black 3 solidline flatcap +drawPoint 8 0 +setPen black 4 solidline flatcap +drawPoint 15 0 +setPen black 5 solidline flatcap +drawPoint 24 0 +setPen black 6 solidline flatcap +drawPoint 35 0 +setPen black 7 solidline flatcap +drawPoint 48 0 +setPen black 8 solidline flatcap +drawPoint 63 0
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/ellipses.qps b/tests/auto/integrationtests/lancelot/scripts/ellipses.qps new file mode 100644 index 0000000000..e2cffd7f27 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/ellipses.qps @@ -0,0 +1,86 @@ +# Version: 1 +# CheckVsReference: 5% + + +surface_begin 0 0 600 600 +translate 0 50 + +setPen nopen +setBrush 0x7f000000 +gradient_clearStops +gradient_appendStop 0 red +gradient_appendStop 0.25 orange +gradient_appendStop 0.5 yellow +gradient_appendStop 0.8 green +gradient_appendStop 1 cyan + +gradient_setSpread PadSpread +gradient_setRadial 20 20 220 200 + +drawEllipse 10 10 80 80 +drawEllipse 50 50 120 90 + +translate 100 0 +brushTranslate 40 20 +brushScale 0.25 0.25 + +setPen black + +drawEllipse 10 10 80 80 +setOpacity 0.5 +setCompositionMode SourceIn +drawEllipse 50 50 120 90 +setOpacity 1.0 +setRenderHint Antialiasing +setCompositionMode Xor +brushTranslate 70 0 +translate 100 0 +drawEllipse 10 10 80 80 + +setPen nopen +drawEllipse 50 50 120 90 + +setOpacity 0.7 +setBrush red +translate 100 0 +setCompositionMode SourceOver + +drawEllipse 10 10 80 80 + +setOpacity 0.6 +setPen black 5.0 +gradient_clearStops +gradient_appendStop 0 red +gradient_appendStop 0.25 orange +gradient_appendStop 0.5 yellow +gradient_appendStop 0.8 green +gradient_appendStop 1 cyan + +gradient_setSpread PadSpread +gradient_setLinear 20 20 120 100 +drawEllipse 50 50 120 90 + + +translate 100 0 + +setOpacity 1.0 +drawEllipse 10 10 80 80 + +setCompositionMode SourceIn +setOpacity 0.7 +setPen black 3.0 +gradient_clearStops +gradient_appendStop 0 red +gradient_appendStop 0.25 orange +gradient_appendStop 0.5 yellow +gradient_appendStop 0.8 green +gradient_appendStop 1 cyan + +gradient_setSpread PadSpread +gradient_setLinear 50 50 80 90 +drawEllipse 50 50 120 90 + +surface_end + +drawText 200 220 "Testing Ellipse drawing with varios combinations" +drawText 200 240 "of features such as brushes, pens and composition modes"
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/filltest.qps b/tests/auto/integrationtests/lancelot/scripts/filltest.qps new file mode 100644 index 0000000000..2eeba2ecf6 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/filltest.qps @@ -0,0 +1,413 @@ +# Version: 1 +# CheckVsReference: 5% + +setPen nopen +setBrush red +translate 0 4 +begin_block polys +drawPolygon [0 0 2 -2 4 0] +drawPolygon [0 2 2 4 4 2] +end_block polys +translate 6 .5 +repeat_block polys +translate 6.5 0 +repeat_block polys +translate 6 .5 +repeat_block polys + +resetMatrix + +translate 0 12 +setPen black +drawPolygon [0 0 5 0 5 5 0 5] + +translate 10 0 +setPen nopen +drawPolygon [0 0 5 0 5 5 0 5] + +translate 10 0 +drawPolygon [0 0 5 0 5 5 0 5] + +setBrush black +path_addRect stroke -.5 -.5 6 6 +path_addRect stroke .5 .5 4 4 +drawPath stroke + +resetMatrix + +translate 0 65 + +setPen red +drawText 0 0 "path" +drawText 40 0 "rect" +drawText 80 0 "img" +drawText 120 0 "pix" +drawText 160 0 "brush" +setPen nopen + +translate 0 5 + +image_load border.png img +pixmap_load border.png pix + +path_addRect rect 0 0 10 10 +begin_block rects +drawPath rect +drawRect 40 0 10 10 +drawImage img 80 0 +drawPixmap pix 120 0 +setBrush border.png +drawRect 160 0 10 10 +setBrush black +end_block rects + +setPen red +drawText 180 10 "0.0" +setPen nopen + +translate 0.1 20 +repeat_block rects +setPen red +drawText 180 10 "0.1" +setPen nopen +translate 0.1 20 +repeat_block rects +setPen red +drawText 180 10 "0.2" +setPen nopen +translate 0.1 20 +repeat_block rects +setPen red +drawText 180 10 "0.3" +setPen nopen +translate 0.1 20 +repeat_block rects +setPen red +drawText 180 10 "0.4" +setPen nopen +translate 0.1 20 +repeat_block rects +setPen red +drawText 180 10 "0.5" +setPen nopen +translate 0.1 20 +repeat_block rects +setPen red +drawText 180 10 "0.6" +setPen nopen +translate 0.1 20 +repeat_block rects +setPen red +drawText 180 10 "0.7" +setPen nopen +translate 0.1 20 +repeat_block rects +setPen red +drawText 180 10 "0.8" +setPen nopen +translate 0.1 20 +repeat_block rects +setPen red +drawText 180 10 "0.9" +setPen nopen +translate 0.1 20 +repeat_block rects +setPen red +drawText 180 10 "1.0" +setPen nopen + +resetMatrix + +translate 0 400 + +setPen red +drawText 0 10 "path" +drawText 0 30 "rect" +drawText 0 50 "img" +drawText 0 70 "pix" +drawText 0 90 "brush" +drawText 0 110 "stroke" +drawText 0 130 "scale" +drawText 0 170 "rotate" +setPen nopen + +translate 50 0 + +begin_block rects +drawPath rect +drawRect 0 20 10 10 +drawImage img 0 40 +drawPixmap pix 0 60 +save +setBrush border.png +drawRect 0 80 10 10 +translate 0 100 +setBrush red +setPen black +drawRect 0 0 10 10 +setBrush border.png +setPen nopen +translate 0 20 +scale 2 2 +drawRect 0 0 10 10 +translate 10 20 +rotate 90 +drawRect 0 0 10 10 +restore +end_block rects + +setPen red +drawText -5 -10 "0.0" +setPen nopen + +translate 40 0.1 +repeat_block rects +setPen red +drawText -5 -10 "0.1" +setPen nopen +translate 40 0.1 +repeat_block rects +setPen red +drawText -5 -10 "0.2" +setPen nopen +translate 40 0.1 +repeat_block rects +setPen red +drawText -5 -10 "0.3" +setPen nopen +translate 40 0.1 +repeat_block rects +setPen red +drawText -5 -10 "0.4" +setPen nopen +translate 40 0.1 +repeat_block rects +setPen red +drawText -5 -10 "0.5" +setPen nopen +translate 40 0.1 +repeat_block rects +setPen red +drawText -5 -10 "0.6" +setPen nopen +translate 40 0.1 +repeat_block rects +setPen red +drawText -5 -10 "0.7" +setPen nopen +translate 40 0.1 +repeat_block rects +setPen red +drawText -5 -10 "0.8" +setPen nopen +translate 40 0.1 +repeat_block rects +setPen red +drawText -5 -10 "0.9" +setPen nopen +translate 40 0.1 +repeat_block rects +setPen red +drawText -5 -10 "1.0" +setPen nopen + +resetMatrix + +translate 0 620 + +setPen red +drawText 0 10 "path" +setPen nopen + +path_addRect rect2 -5 -5 10 10 + +translate 55 5 +drawPath rect2 + +translate 20 0 +rotate 10 +drawPath rect2 +rotate -10 +translate 20 0 +rotate 20 +drawPath rect2 +rotate -20 +translate 20 0 +rotate 30 +drawPath rect2 +rotate -30 +translate 20 0 +rotate 40 +drawPath rect2 +rotate -40 +translate 20 0 +rotate 50 +drawPath rect2 +rotate -50 +translate 20 0 +rotate 60 +drawPath rect2 +rotate -60 +translate 20 0 +rotate 70 +drawPath rect2 +rotate -70 +translate 20 0 +rotate 80 +drawPath rect2 +rotate -80 +translate 20 0 +rotate 90 +drawPath rect2 +rotate -90 + +resetMatrix + +translate 0 600 + +setPen red +drawText 0 10 "rect" +setPen nopen + +translate 55 5 +drawRect -5 -5 10 10 + +translate 20 0 +rotate 10 +drawRect -5 -5 10 10 +rotate -10 +translate 20 0 +rotate 20 +drawRect -5 -5 10 10 +rotate -20 +translate 20 0 +rotate 30 +drawRect -5 -5 10 10 +rotate -30 +translate 20 0 +rotate 40 +drawRect -5 -5 10 10 +rotate -40 +translate 20 0 +rotate 50 +drawRect -5 -5 10 10 +rotate -50 +translate 20 0 +rotate 60 +drawRect -5 -5 10 10 +rotate -60 +translate 20 0 +rotate 70 +drawRect -5 -5 10 10 +rotate -70 +translate 20 0 +rotate 80 +drawRect -5 -5 10 10 +rotate -80 +translate 20 0 +rotate 90 +drawRect -5 -5 10 10 +rotate -90 + +resetMatrix +path_addRect vertical 0.1 0.1 0.2 10 + +translate 0 320 +drawPath vertical +translate 2.2 0 +drawPath vertical +translate 2.2 0 +drawPath vertical +translate 2.2 0 +drawPath vertical +translate 2.2 0 +drawPath vertical + +resetMatrix +path_addRect horizontal 0.1 0.1 10 0.2 + +translate 0 340 +drawPath horizontal +translate 0 2.2 +drawPath horizontal +translate 0 2.2 +drawPath horizontal +translate 0 2.2 +drawPath horizontal +translate 0 2.2 +drawPath horizontal + +setOpacity 0.8 +resetMatrix + +translate 0.1 24.7 +translate 400 0 +#rotate 88.8 +rotate 89.9 +setBrush red +drawPolygon [0 0 300 0 0 173] +setBrush green +drawPolygon [0 173 300 0 300 173] + +resetMatrix + +translate 410 24 +path_lineTo left 0 273 +path_lineTo left 300 273 +path_cubicTo left 50 273 250 0 0 0 + +path_cubicTo right 250 0 50 273 300 273 +path_lineTo right 300 0 + +translate 310 0 +rotate 90 +setBrush red +drawPath left +setBrush green +drawPath right + +resetMatrix +translate 0.1 680.1 +setPen red +setOpacity 1 +drawText 115 -20 "0.1" +drawText 0 0 "pixmap w/ opacity" +setOpacity 0.6 +drawPixmap pix 120 -10 +translate 0 20 +setOpacity 1 +drawText 0 0 "image w/ opacity" +setOpacity 0.6 +drawImage img 120 -10 + +resetMatrix +path_lineTo fillpath 0 50 +path_lineTo fillpath 50 50 +path_moveTo fillpath 70 50 +path_lineTo fillpath 70 100 +path_lineTo fillpath 40 100 +translate 500 400 +drawPath fillpath + +resetMatrix +path_moveTo vectorarne 50 10 +path_lineTo vectorarne 50 50 +path_lineTo vectorarne 100 50 +path_addEllipse vectorarne 350 20 230 230 +path_moveTo vectorarne 500 500 +path_cubicTo vectorarne 20 20 250 30 50 150 +translate 500 550 +scale 0.4 0.4 +setRenderHint antialiasing +drawPath vectorarne + +resetMatrix +translate 200 730 +setRenderHint antialiasing off +setOpacity 1 +setPen red +drawText 0 0 "outline/fill consistency" +setPen red +setBrush green +translate 80 -30 +drawPolygon [13.6965 -99.1837 -71.4767 13.823 32.4596 -33.1847]
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/gradients.qps b/tests/auto/integrationtests/lancelot/scripts/gradients.qps new file mode 100644 index 0000000000..eb3cda9cad --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/gradients.qps @@ -0,0 +1,44 @@ +# Version: 1 +# CheckVsReference: 5% + +drawText 75 20 "Linear" +drawText 176 20 "Radial" +drawText 277 20 "Conical" +translate 0 30 +drawText 0 50 "AA off" +drawText 0 151 "AA on" + +setPen nopen + +gradient_clearStops +gradient_appendStop 0 0x00000000 +gradient_appendStop 0.001 red +gradient_appendStop 0.2 blue +gradient_appendStop 0.4 yellow +gradient_appendStop 0.6 cyan +gradient_appendStop 0.8 green +gradient_appendStop 0.999 red +gradient_appendStop 1 0x00000000 + +gradient_setSpread PadSpread +gradient_setCoordinateMode ObjectBoundingMode + +begin_block row +save +gradient_setLinear 0.1 0.0 0.9 0.0 +drawRect 50 0 100 100 + +gradient_setRadial 0.5 0.5 0.5 0.5 0.5 +translate 101 0 +drawRect 50 0 100 100 + +gradient_setConical 0.5 0.5 45 +translate 101 0 +drawRect 50 0 100 100 +restore +end_block row + +setRenderHint Antialiasing + +translate 0 101 +repeat_block row
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/hinting.qps b/tests/auto/integrationtests/lancelot/scripts/hinting.qps new file mode 100644 index 0000000000..7ce21b287a --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/hinting.qps @@ -0,0 +1,26 @@ +translate 10 50 +setFont "sansserif" 10 +drawText 0 0 "Default hinting:" +setFont "times" 12 normal normal default +drawText 0 20 "The quick brown fox jumps over the lazy dog" + +translate 0 50 +setFont "sansserif" 10 +drawText 0 0 "No hinting:" +setFont "times" 12 normal normal none +drawText 0 20 "The quick brown fox jumps over the lazy dog" + +translate 0 50 +setFont "sansserif" 10 +drawText 0 0 "Vertical hinting:" +setFont "times" 12 normal normal vertical +drawText 0 20 "The quick brown fox jumps over the lazy dog" + +translate 0 50 +setFont "sansserif" 10 +drawText 0 0 "Full hinting:" +setFont "times" 12 normal normal full +drawText 0 20 "The quick brown fox jumps over the lazy dog" + + +# Note: there is also the textlayout_draw command which might be interesting here. diff --git a/tests/auto/integrationtests/lancelot/scripts/image_formats.qps b/tests/auto/integrationtests/lancelot/scripts/image_formats.qps new file mode 100644 index 0000000000..d817d04bf8 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/image_formats.qps @@ -0,0 +1,81 @@ +# Version: 1 +# CheckVsReference: 5% + + +image_load dome_argb32.png the_pixmap +image_convertToFormat the_pixmap the_pixmap ARGB32_Premultiplied + +begin_block draw_stuff + save + image_convertToFormat the_pixmap converted ARGB32_Premultiplied + drawImage converted 0 0 + translate 0 110 + + image_convertToFormat the_pixmap converted ARGB32 + drawImage converted 0 0 + translate 0 110 + + image_convertToFormat the_pixmap converted RGB32 + drawImage converted 0 0 + translate 0 110 + + image_convertToFormat the_pixmap converted Indexed8 + drawImage converted 0 0 + translate 0 110 + + image_convertToFormat the_pixmap converted MonoLSB + drawImage converted 0 0 + translate 0 110 + + image_convertToFormat the_pixmap converted Mono + drawImage converted 0 0 + translate 0 110 + restore +end_block + + +image_load dome_argb32.png the_pixmap +translate 110 0 +repeat_block draw_stuff + + +image_load dome_rgb32.png the_pixmap +translate 110 0 +repeat_block draw_stuff + +image_load dome_indexed.png the_pixmap +translate 110 0 +repeat_block draw_stuff + + +image_load dome_mono.png the_pixmap +translate 110 0 +repeat_block draw_stuff + +image_load dome_mono_palette.png the_pixmap +translate 110 0 +repeat_block draw_stuff + +image_load dome_indexed_mask.png the_pixmap +translate 110 0 +repeat_block draw_stuff + + +# helpful texts +resetMatrix +setPen black + +drawText 10 670 "ARGB32_PM" +drawText 120 670 "ARGB32" +drawText 230 670 "RGB32" +drawText 340 670 "Indexed" +drawText 450 670 "Mono" +drawText 560 670 "Mono w/lut" +drawText 670 670 "Indexed w/mask" + +drawText 770 50 "ARGB32_PM" +drawText 770 160 "ARGB32" +drawText 770 270 "RGB32" +drawText 770 380 "Indexed" +drawText 770 490 "MonoLSB" +drawText 770 600 "Mono"
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/images.qps b/tests/auto/integrationtests/lancelot/scripts/images.qps new file mode 100644 index 0000000000..3f892400e2 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/images.qps @@ -0,0 +1,106 @@ +# Version: 1 +# CheckVsReference: 5% + + +setRenderHint Antialiasing + +setPen #00ff00 + +image_load dome_argb32.png the_image +begin_block draw_stuff + +save + + # standard draw + drawImage the_image 0 0 + + # sub recting + translate 120 0 + drawImage the_image 0 0 40 40 0 0 40 40 + drawImage the_image 60 0 40 40 60 0 40 40 + drawImage the_image 0 60 40 40 0 60 40 40 + drawImage the_image 60 60 40 40 60 60 40 40 + drawImage the_image 0 40 40 20 0 40 40 20 + drawImage the_image 60 40 40 20 60 40 40 20 + drawImage the_image 40 0 20 100 40 0 20 100 + + # subrecting w/scale + translate 120 0 + drawImage the_image 0 0 50 50 0 0 25 25 + drawImage the_image 50 0 50 50 25 0 25 25 + drawImage the_image 0 50 50 50 0 25 25 25 + drawImage the_image 50 50 50 50 25 25 25 25 + + # subrecting w/scale & smooth xform + translate 120 0 + setRenderHint SmoothPixmapTransformation + drawImage the_image 0 0 50 50 0 0 25 25 + drawImage the_image 50 0 50 50 25 0 25 25 + drawImage the_image 0 50 50 50 0 25 25 25 + drawImage the_image 50 50 50 50 25 25 25 25 + + + # Rotation w/o smooth xform + translate 120 0 + save + setRenderHint SmoothPixmapTransform off + rotate 10 + drawImage the_image 0 0 + restore + + # Rotation w smooth xform + translate 120 0 + save + setRenderHint SmoothPixmapTransform + rotate 10 + drawImage the_image 0 0 + restore + +restore + +end_block + + +translate 0 120 +image_load dome_rgb32.png the_image +repeat_block draw_stuff + +translate 0 120 +image_load dome_indexed.png the_image +repeat_block draw_stuff + +translate 0 120 +image_load dome_indexed_mask.png the_image +repeat_block draw_stuff + +translate 0 120 +image_load dome_mono.png the_image +repeat_block draw_stuff + + +resetMatrix +translate 700 60 +setPen black +drawText 0 0 "32 bit w/alpha" +translate 0 120 +drawText 0 0 "32 bit w/o alpha" +translate 0 120 +drawText 0 0 "8 bit indexed" +translate 0 120 +drawText 0 0 "8 bit indexed w/mask" +translate 0 120 +drawText 0 0 "1 bit" +resetMatrix +translate 0 600 +drawText 0 0 "normal" +translate 120 0 +drawText 0 0 "subrect" +translate 120 0 +drawText 0 0 "subrect scale" +translate 120 0 +drawText 0 0 "subrect scale smooth" +translate 120 0 +drawText 0 0 "xform" +translate 120 0 +drawText 0 0 "smooth xform" +translate 120 0
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/images2.qps b/tests/auto/integrationtests/lancelot/scripts/images2.qps new file mode 100644 index 0000000000..5159abc928 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/images2.qps @@ -0,0 +1,145 @@ +# Version: 1 +# CheckVsReference: 5% + +image_load dome_argb32.png the_image +begin_block draw_stuff + +save + # standard draw + drawImage the_image 0 0 + + # flip x + translate 220 0 + scale -1 1 + drawImage the_image 0 0 + scale -1 1 + + # flip y + translate 20 100 + scale 1 -1 + drawImage the_image 0 0 + scale 1 -1 + + # flip x and y + translate 220 0 + scale -1 -1 + drawImage the_image 0 0 + scale -1 -1 + + # flip y and scale + translate 20 10 + save + scale 1 -1.1 + drawImage the_image 0 0 + restore + + # flip y and scale + translate 220 -110 + save + scale -1.1 0.9 + drawImage the_image 0 0 + restore +restore +end_block + +setRenderHint Antialiasing + +resetMatrix +translate 0 120 +repeat_block draw_stuff + +resetMatrix +translate 720 60 +setPen black +drawText 0 0 "aliased" +translate 0 120 +drawText 0 0 "antialiased" +resetMatrix +translate 0 260 +drawText 0 0 "normal" +translate 120 0 +drawText 0 0 "flip x" +translate 120 0 +drawText 0 0 "flip y" +translate 120 0 +drawText 0 0 "flip x and y" +translate 120 0 +drawText 0 0 "flip y and scale" +translate 120 0 +drawText 0 0 "flip x and scale" +translate 120 0 + +setRenderHint SmoothPixmapTransform + +resetMatrix +translate 20 300 +drawImage border.png 0 0 100 100 1 1 8 8 +drawText 0 -5 "subrect color bleeding" +translate 0 120 +drawImage border.png 0 0 100 100 0 0 10 10 + +image_load sign.png the_image +resetMatrix +drawText 240 300 "drawImage() with varying sx/sy offsets" +translate 0 10 +drawRect 240 300 50 50 +drawImage the_image 240 300 50 50 20 0 80 80 +drawRect 300 300 50 50 +drawImage the_image 300 300 50 50 -20 0 80 80 +drawRect 240 370 50 50 +drawImage the_image 240 370 50 50 0 20 80 80 +drawRect 300 370 50 50 +drawImage the_image 300 370 50 50 0 -20 80 80 + +pixmap_load sign.png the_pixmap +translate 220 0 +translate 0 -10 +drawText 240 300 "drawPixmap() with varying sx/sy offsets" +translate 0 10 +drawRect 240 300 50 50 +drawPixmap the_pixmap 240 300 50 50 20 0 80 80 +drawRect 300 300 50 50 +drawPixmap the_pixmap 300 300 50 50 -20 0 80 80 +drawRect 240 370 50 50 +drawPixmap the_pixmap 240 370 50 50 0 20 80 80 +drawRect 300 370 50 50 +drawPixmap the_pixmap 300 370 50 50 0 -20 80 80 + + +resetMatrix +translate 0 170 +drawText 240 300 "drawImage() with varying sx/sy offsets" +translate 0 10 +drawRect 240 300 50 50 +drawImage the_image 240 300 50 50 50 0 50 50 +drawRect 300 300 50 50 +drawImage the_image 300 300 50 50 -20 0 50 50 +drawRect 240 370 50 50 +drawImage the_image 240 370 50 50 0 50 50 50 +drawRect 300 370 50 50 +drawImage the_image 300 370 50 50 0 -20 50 50 + +resetMatrix +translate 220 170 +drawText 240 300 "drawPixmap() with varying sx/sy offsets" +translate 0 10 +drawRect 240 300 50 50 +drawPixmap the_pixmap 240 300 50 50 50 0 50 50 +drawRect 300 300 50 50 +drawPixmap the_pixmap 300 300 50 50 -20 0 50 50 +drawRect 240 370 50 50 +drawPixmap the_pixmap 240 370 50 50 0 50 50 50 +drawRect 300 370 50 50 +drawPixmap the_pixmap 300 370 50 50 0 -20 50 50 + +resetMatrix +drawText 10 620 "drawImage/Pixmap() with negative x/y and sx/sy" +setPen red + +translate 20 640 +drawImage the_image -10 -10 -1 -1 -10 -10 0 0 +drawRect 0 0 80 80 + +translate 100 0 +drawPixmap the_pixmap -10 -10 -1 -1 -10 -10 0 0 +drawRect 0 0 80 80 diff --git a/tests/auto/integrationtests/lancelot/scripts/join_cap_styles.qps b/tests/auto/integrationtests/lancelot/scripts/join_cap_styles.qps new file mode 100644 index 0000000000..ed823f53f5 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/join_cap_styles.qps @@ -0,0 +1,63 @@ +# Version: 1 +# CheckVsReference: 5% + + +setRenderHint Antialiasing + +path_moveTo p 20 20 +path_cubicTo p 100 20 100 180 180 100 +path_lineTo p 20 180 +path_lineTo p 180 20 + +setPen black 20 solidline roundcap roundjoin +drawPath p +setPen red +drawPath p + +translate 200 0 +setPen black 20 solidline roundcap miterjoin +drawPath p +setPen red +drawPath p + +translate 200 0 +setPen black 20 solidline roundcap beveljoin +drawPath p +setPen red +drawPath p + +translate -400 200 +setPen black 20 solidline squarecap roundjoin +drawPath p +setPen red +drawPath p + +translate 200 0 +setPen black 20 solidline squarecap miterjoin +drawPath p +setPen red +drawPath p + +translate 200 0 +setPen black 20 solidline squarecap beveljoin +drawPath p +setPen red +drawPath p + +translate -400 200 +setPen black 20 solidline flatcap roundjoin +drawPath p +setPen red +drawPath p + +translate 200 0 +setPen black 20 solidline flatcap miterjoin +drawPath p +setPen red +drawPath p + +translate 200 0 +setPen black 20 solidline flatcap beveljoin +drawPath p +setPen red +drawPath p
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/join_cap_styles_duplicate_control_points.qps b/tests/auto/integrationtests/lancelot/scripts/join_cap_styles_duplicate_control_points.qps new file mode 100644 index 0000000000..b4630148e2 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/join_cap_styles_duplicate_control_points.qps @@ -0,0 +1,68 @@ +# Version: 1 +# CheckVsReference: 1% (0 0 600 650) + + +setRenderHint Antialiasing + +path_moveTo p 40 70 +path_lineTo p 20 70 +path_cubicTo p 20 70 40 20 80 80 + +path_moveTo p 20 120 +path_cubicTo p 50 60 80 110 80 110 +path_lineTo p 60 110 + +scale 2 2 + +setPen black 10 solidline roundcap roundjoin +drawPath p +setPen red +drawPath p + +translate 100 0 +setPen black 10 solidline roundcap miterjoin +drawPath p +setPen red +drawPath p + +translate 100 0 +setPen black 10 solidline roundcap beveljoin +drawPath p +setPen red +drawPath p + +translate -200 100 +setPen black 10 solidline squarecap roundjoin +drawPath p +setPen red +drawPath p + +translate 100 0 +setPen black 10 solidline squarecap miterjoin +drawPath p +setPen red +drawPath p + +translate 100 0 +setPen black 10 solidline squarecap beveljoin +drawPath p +setPen red +drawPath p + +translate -200 100 +setPen black 10 solidline flatcap roundjoin +drawPath p +setPen red +drawPath p + +translate 100 0 +setPen black 10 solidline flatcap miterjoin +drawPath p +setPen red +drawPath p + +translate 100 0 +setPen black 10 solidline flatcap beveljoin +drawPath p +setPen red +drawPath p
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/linear_gradients.qps b/tests/auto/integrationtests/lancelot/scripts/linear_gradients.qps new file mode 100644 index 0000000000..b1b8dd69e7 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/linear_gradients.qps @@ -0,0 +1,144 @@ +# Version: 1 +# CheckVsReference: 2% (0 0 600 750) + +path_addRect path 400 0 80 80 +path_addEllipse path 440 40 60 60 + +setRenderHint Antialiasing + +setPen black + +begin_block gradients +gradient_clearStops +gradient_appendStop 0 red +gradient_appendStop 0.25 orange +gradient_appendStop 0.5 yellow +gradient_appendStop 0.8 green +gradient_appendStop 1 cyan + +gradient_setSpread PadSpread +gradient_setLinear 20 20 70 70 +drawRect 0 0 100 100 + +gradient_setSpread ReflectSpread +gradient_setLinear 120 20 170 70 +drawEllipse 100 0 100 100 + +gradient_setSpread RepeatSpread +gradient_setLinear 220 20 270 70 +drawRoundRect 200 0 100 100 + +gradient_clearStops +gradient_appendStop 0 3f7f7fff +gradient_appendStop 0.5 dfdfffff +gradient_appendStop 1 7f00007f + +gradient_setSpread PadSpread +gradient_setLinear 320 20 340 40 +drawPolygon [300 0 390 0 350 99] + +gradient_setSpread ReflectSpread +gradient_setLinear 420 20 440 40 +drawPath path + +gradient_setSpread RepeatSpread +gradient_setLinear 520 20 540 40 +drawPie 500 0 100 100 720 4320 +end_block + +translate 0 100 +scale 1 2 +repeat_block gradients + +resetMatrix +translate 0 300 +brushTranslate 30 0 +brushScale 0.9 0.9 +brushRotate 20 +repeat_block gradients + +# Vertical gradient tests +resetMatrix +setBrush noBrush +translate 0 400 + +begin_block vertical_gradients +gradient_clearStops +gradient_appendStop 0 red +gradient_appendStop 0.25 orange +gradient_appendStop 0.5 yellow +gradient_appendStop 0.8 green +gradient_appendStop 1 cyan + +gradient_setSpread PadSpread +gradient_setLinear 20 20 20 70 +drawRect 0 0 100 100 + +gradient_setSpread ReflectSpread +gradient_setLinear 120 20 120 70 +drawEllipse 100 0 100 100 + +gradient_setSpread RepeatSpread +gradient_setLinear 220 20 220 70 +drawRoundRect 200 0 100 100 + +gradient_clearStops +gradient_appendStop 0 3f7f7fff +gradient_appendStop 0.5 dfdfffff +gradient_appendStop 1 7f00007f + +gradient_setSpread PadSpread +gradient_setLinear 320 20 320 40 +drawPolygon [300 0 390 0 350 99] + +gradient_setSpread ReflectSpread +gradient_setLinear 420 20 420 40 +drawPath path + +gradient_setSpread RepeatSpread +gradient_setLinear 520 20 520 40 +drawPie 500 0 100 100 720 4320 +end_block + +translate 0 100 +scale 1 1.5 +repeat_block vertical_gradients + +resetMatrix +translate 0 650 +brushTranslate 30 0 +brushScale 0.9 0.9 +brushRotate 20 +repeat_block vertical_gradients + +# Some helpful info perhaps? +resetMatrix +setPen black +# gradient line indicators +drawLine 20 20 70 70 +drawLine 120 20 170 70 +drawLine 220 20 270 70 +drawLine 320 20 340 40 +drawLine 420 20 440 40 +drawLine 520 20 540 40 + +drawLine 20 140 70 240 +drawLine 120 140 170 240 +drawLine 220 140 270 240 +drawLine 320 140 340 180 +drawLine 420 140 440 180 +drawLine 520 140 540 180 + +drawText 610 50 "No XForm" +drawText 610 200 "scale 1x2" +drawText 610 350 "brush transform" +drawText 610 450 "vertical brush" +drawText 610 570 "vertical brush scale 1x1.5" +drawText 610 700 "vertical brush transform" + +drawText 10 780 "Pad" +drawText 110 780 "Reflect" +drawText 210 780 "Repeat" +drawText 310 780 "Pad w/alpha" +drawText 410 780 "Reflect w/alpha" +drawText 510 780 "Repeat w/alpha"
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/linear_gradients_perspectives.qps b/tests/auto/integrationtests/lancelot/scripts/linear_gradients_perspectives.qps new file mode 100644 index 0000000000..3ea39fbe46 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/linear_gradients_perspectives.qps @@ -0,0 +1,62 @@ +# Version: 1 + + +setRenderHint Antialiasing + +setPen #00ff00 + +translate 10 10 +# standard draw +begin_block gradient +gradient_clearStops +gradient_appendStop 0 red +gradient_appendStop 0.25 orange +gradient_appendStop 0.5 yellow +gradient_appendStop 0.8 green +gradient_appendStop 1 cyan + +gradient_setSpread PadSpread +gradient_setLinear 10 10 290 290 +drawRect 0 0 300 300 +end_block gradient + +# Rotation w/o smooth xform +save +translate 350 0 +save + setRenderHint SmoothPixmapTransform on + mapQuadToQuad 0 0 0 300 300 300 300 0 0 50 0 200 300 300 300 0 + repeat_block gradient +restore +restore + +translate 0 320 + +save + setRenderHint SmoothPixmapTransform on + mapQuadToQuad 0 0 0 300 300 300 300 0 0 0 100 300 200 300 300 0 + repeat_block gradient +restore + +save +translate 350 0 +save + setRenderHint SmoothPixmapTransform on + mapQuadToQuad 0 0 0 300 300 300 300 0 0 0 0 300 300 250 300 50 + repeat_block gradient +restore +restore + + +resetMatrix +setPen black +translate 125 20 +drawText 0 0 "No transform" +translate 350 0 +drawText 0 0 "Left Tilted" +resetMatrix +translate 125 350 +drawText 0 0 "Bottom Tilted" +translate 350 0 +drawText 0 0 "Right Tilted" +translate 120 0
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/linear_resolving_gradients.qps b/tests/auto/integrationtests/lancelot/scripts/linear_resolving_gradients.qps new file mode 100644 index 0000000000..779760cb9a --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/linear_resolving_gradients.qps @@ -0,0 +1,66 @@ +# Version: 2 +# CheckVsReference: 2% (0 0 500 400) + +setRenderHint Antialiasing + +setPen black + +begin_block gradients +gradient_clearStops +gradient_appendStop 0 red +gradient_appendStop 0.25 orange +gradient_appendStop 0.5 yellow +gradient_appendStop 0.8 green +gradient_appendStop 1 cyan + +gradient_setSpread PadSpread +gradient_setCoordinateMode ObjectBoundingMode +gradient_setLinear 0.2 0.2 0.7 0.7 +drawRect 0 0 100 100 + +gradient_setSpread ReflectSpread +gradient_setLinear 0.2 0.2 0.7 0.7 +drawEllipse 100 0 100 100 + +gradient_setSpread RepeatSpread +gradient_setLinear 0.2 0.2 0.7 0.7 +drawRoundRect 200 0 100 100 + +gradient_clearStops +gradient_appendStop 0 3f7f7fff +gradient_appendStop 0.5 dfdfffff +gradient_appendStop 1 7f00007f + +gradient_setSpread PadSpread +gradient_setLinear 0.2 0.2 0.8 0.4 +drawPolygon [300 0 400 0 350 100] + +gradient_setSpread RepeatSpread +gradient_setLinear 0.2 0.2 0.4 0.4 +drawPie 400 0 100 100 0 4320 +end_block + +translate 0 100 +scale 1 2 +repeat_block gradients + +resetMatrix +translate 0 300 +brushTranslate 30 0 +brushScale 0.9 0.9 +brushRotate 20 +repeat_block gradients + +# Some helpful info perhaps? +resetMatrix +setPen black + +drawText 510 50 "No XForm" +drawText 510 200 "scale 1x2" +drawText 510 350 "brush transform" + +drawText 10 450 "Pad" +drawText 110 450 "Reflect" +drawText 210 450 "Repeat" +drawText 310 450 "Pad w/alpha" +drawText 410 450 "Repeat w/alpha"
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/lineconsistency.qps b/tests/auto/integrationtests/lancelot/scripts/lineconsistency.qps new file mode 100644 index 0000000000..0b40577659 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/lineconsistency.qps @@ -0,0 +1,72 @@ +# Version: 1 + +begin_block draw +setPen red +drawPolygon [1.1 1 3.3 30.6 23.1 39.2 38.9 6.5] +setPen black +drawLine 1.1 1 3.3 30.6 +drawLine 3.3 30.6 23.1 39.2 +drawLine 23.1 39.2 38.9 6.5 +drawLine 38.9 6.5 1.1 1 +end_block draw +drawText 0 60 "0.0 aligned" +translate 0.1 80.1 +repeat_block draw +drawText 0 60 "0.1 aligned" +translate 0.1 80.1 +repeat_block draw +drawText 0 60 "0.2 aligned" +translate 0.1 80.1 +repeat_block draw +drawText 0 60 "0.3 aligned" +translate 0.1 80.1 +repeat_block draw +drawText 0 60 "0.4 aligned" +translate 0.1 80.1 +repeat_block draw +drawText 0 60 "0.5 aligned" +translate 0.1 80.1 +repeat_block draw +drawText 0 60 "0.6 aligned" +translate 0.1 80.1 +repeat_block draw +drawText 0 60 "0.7 aligned" +translate 0.1 80.1 +repeat_block draw +drawText 0 60 "0.8 aligned" +translate 0.1 80.1 +repeat_block draw +drawText 0 60 "0.9 aligned" + +resetMatrix +translate 100 0 +setPen black +drawText 0 20 "Line and text, 0.0 aligned" +drawLine 0 21 160 21 +translate 0 40.1 +drawText 0 20 "Line and text, 0.1 aligned" +drawLine 0 21 160 21 +translate 0 40.1 +drawText 0 20 "Line and text, 0.2 aligned" +drawLine 0 21 160 21 +translate 0 40.1 +drawText 0 20 "Line and text, 0.3 aligned" +drawLine 0 21 160 21 +translate 0 40.1 +drawText 0 20 "Line and text, 0.4 aligned" +drawLine 0 21 160 21 +translate 0 40.1 +drawText 0 20 "Line and text, 0.5 aligned" +drawLine 0 21 160 21 +translate 0 40.1 +drawText 0 20 "Line and text, 0.6 aligned" +drawLine 0 21 160 21 +translate 0 40.1 +drawText 0 20 "Line and text, 0.7 aligned" +drawLine 0 21 160 21 +translate 0 40.1 +drawText 0 20 "Line and text, 0.8 aligned" +drawLine 0 21 160 21 +translate 0 40.1 +drawText 0 20 "Line and text, 0.9 aligned" +drawLine 0 21 160 21
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/linedashes.qps b/tests/auto/integrationtests/lancelot/scripts/linedashes.qps new file mode 100644 index 0000000000..ee7d18b156 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/linedashes.qps @@ -0,0 +1,94 @@ +# Version: 1 + +translate 10 10 + +setPen 0xffff0000 0 solidline squarecap +translate 50 50 +begin_block draw_lines + save + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + restore +end_block + +setPen 0xffff0000 0 dotline squarecap +translate 100 0 +repeat_block draw_lines +setPen 0xffff0000 0 dashdotline squarecap +translate 100 0 +repeat_block draw_lines +setPen 0xffff0000 0 dashdotdotline squarecap +translate 100 0 +repeat_block draw_lines +setPen 0xffff0000 0 dashline squarecap +translate 100 0 +repeat_block draw_lines
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/linedashes2.qps b/tests/auto/integrationtests/lancelot/scripts/linedashes2.qps new file mode 100644 index 0000000000..1dc4fd310e --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/linedashes2.qps @@ -0,0 +1,154 @@ +# Version: 1 +# CheckVsReference: 5% (0 0 800 800) + +translate -30 10 + +setPen 0xffff0000 0 dashline squarecap +pen_setDashPattern [10 5] + +save +translate 100 100 +begin_block lines +drawLine 100 100 200 100 +drawLine 100 100 200 200 +drawLine 100 100 100 200 +end_block + +setPen 0xffff0000 2 dashline squarecap +translate 150 0 +repeat_block lines +restore + +save +save +begin_block horizontal +drawLine 0 0 50 0 +drawLine 3 10 53 10 +drawLine 6 20 56 20 +drawLine 9 30 59 30 + +translate 0 50 + +drawLine 0 0 50 5 +drawLine 3 10 53 15 +drawLine 6 20 56 25 +drawLine 9 30 59 35 + +translate 0 50 + +drawLine 0 0 50 -5 +drawLine 3 10 53 5 +drawLine 6 20 56 15 +drawLine 9 30 59 25 +end_block +restore + +save +translate 80 0 +repeat_block horizontal +restore +save +translate 800 0 +repeat_block horizontal +restore + +translate 180 -40 +save +begin_block vertical +drawLine 0 0 0 50 +drawLine 10 3 10 53 +drawLine 20 6 20 56 +drawLine 30 9 30 59 + +translate 50 0 + +drawLine 0 0 5 50 +drawLine 10 3 15 53 +drawLine 20 6 25 56 +drawLine 30 9 35 59 + +translate 50 0 + +drawLine 0 0 -5 50 +drawLine 10 3 5 53 +drawLine 20 6 15 56 +drawLine 30 9 25 59 +end_block +restore + +save +translate 0 80 +repeat_block vertical +restore +translate 0 800 +repeat_block vertical +restore + +translate 0 200 + +setPen 0xffff0000 2 dashline squarecap +save +repeat_block horizontal +restore +save +translate 80 0 +repeat_block horizontal +restore +save +translate 780 0 +repeat_block horizontal +restore + +translate 360 -240 +save +repeat_block vertical +restore +save +translate 0 80 +repeat_block vertical +restore +translate 0 780 +repeat_block vertical + +resetMatrix +translate 40 400 +setPen 0xffff0000 5 dashdotline flatcap +pen_setDashPattern [1 1 4 1 1 4] +pen_setDashOffset -4 +drawLine 0 0 300 0 +translate 0 8 +pen_setDashOffset -2 +drawLine 0 0 300 0 +translate 0 8 +pen_setDashOffset 0 +drawLine 0 0 300 0 +translate 0 8 +pen_setDashOffset 2 +drawLine 0 0 300 0 +translate 0 8 +pen_setDashOffset 4 +drawLine 0 0 300 0 +translate 0 8 +pen_setDashOffset 6 +drawLine 0 0 300 0 +translate 0 8 +pen_setDashOffset 8 +drawLine 0 0 300 0 +translate 0 8 +pen_setDashOffset 10 +drawLine 0 0 300 0 +translate 0 8 +pen_setDashOffset 12 +drawLine 0 0 300 0 +translate 0 8 +pen_setDashOffset 14 +drawLine 0 0 300 0 +translate 0 8 +pen_setDashOffset 16 +drawLine 0 0 300 0 + +resetMatrix +setPen black 3 dashdotline +pen_setCosmetic true +translate 0 -150 +drawLine 500 160 500 410
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/linedashes2_aa.qps b/tests/auto/integrationtests/lancelot/scripts/linedashes2_aa.qps new file mode 100644 index 0000000000..c818ab62fe --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/linedashes2_aa.qps @@ -0,0 +1,5 @@ +# Version: 1 +# CheckVsReference: 1% (0 0 800 800) + +setRenderHint LineAntialiasing +import "linedashes2.qps"
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/lines.qps b/tests/auto/integrationtests/lancelot/scripts/lines.qps new file mode 100644 index 0000000000..c0daffb1b0 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/lines.qps @@ -0,0 +1,558 @@ +# Version: 1 +# CheckVsReference: 5% (0 0 310 425) + + +translate 10 10 + +begin_block draw_lines + save + translate 50 50 + + save + setPen 0x7fff0000 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + restore + + # and then draw the lines the other direction + save + setPen 0x7f0000ff + drawLine 50 0 10 0 + rotate 10 + drawLine 50 0 10 0 + rotate 10 + drawLine 50 0 10 0 + rotate 10 + drawLine 50 0 10 0 + rotate 10 + drawLine 50 0 10 0 + rotate 10 + drawLine 50 0 10 0 + rotate 10 + drawLine 50 0 10 0 + rotate 10 + drawLine 50 0 10 0 + rotate 10 + drawLine 50 0 10 0 + rotate 10 + drawLine 50 0 10 0 + rotate 10 + drawLine 50 0 10 0 + rotate 10 + drawLine 50 0 10 0 + rotate 10 + drawLine 50 0 10 0 + rotate 10 + drawLine 50 0 10 0 + rotate 10 + drawLine 50 0 10 0 + rotate 10 + drawLine 50 0 10 0 + rotate 10 + drawLine 50 0 10 0 + rotate 10 + drawLine 50 0 10 0 + rotate 10 + drawLine 50 0 10 0 + rotate 10 + drawLine 50 0 10 0 + rotate 10 + drawLine 50 0 10 0 + rotate 10 + drawLine 50 0 10 0 + rotate 10 + drawLine 50 0 10 0 + rotate 10 + drawLine 50 0 10 0 + rotate 10 + drawLine 50 0 10 0 + rotate 10 + drawLine 50 0 10 0 + rotate 10 + drawLine 50 0 10 0 + rotate 10 + drawLine 50 0 10 0 + rotate 10 + drawLine 50 0 10 0 + rotate 10 + drawLine 50 0 10 0 + rotate 10 + drawLine 50 0 10 0 + rotate 10 + drawLine 50 0 10 0 + rotate 10 + drawLine 50 0 10 0 + rotate 10 + drawLine 50 0 10 0 + rotate 10 + drawLine 50 0 10 0 + rotate 10 + drawLine 50 0 10 0 + restore + + # and now with a clip + save + setClipRect -30 -30 60 60 + setPen 0x7f00ff00 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + rotate 10 + drawLine 10 0 50 0 + restore + + restore +end_block + +save + translate 100 0 + scale 2 2 + repeat_block draw_lines +restore + +translate 0 10 + +save + translate 0 200 + setRenderHint Antialiasing + repeat_block draw_lines +restore + +save + translate 100 200 + scale 2 2 + setRenderHint Antialiasing + repeat_block draw_lines +restore + +translate 320 0 + +setPen black 0 solidline squarecap + +begin_block lines + +# 0 -> 45 degress +drawLine 100 100 200 90 +drawLine 100 100 200 80 +drawLine 100 100 200 70 +drawLine 100 100 200 60 +drawLine 100 100 200 50 +drawLine 100 100 200 40 +drawLine 100 100 200 30 +drawLine 100 100 200 20 +drawLine 100 100 200 10 + +# 45 +drawLine 100 100 200 0 + +# 45 -> 90 +drawLine 100 100 190 0 +drawLine 100 100 180 0 +drawLine 100 100 170 0 +drawLine 100 100 160 0 +drawLine 100 100 150 0 +drawLine 100 100 140 0 +drawLine 100 100 130 0 +drawLine 100 100 120 0 +drawLine 100 100 110 0 + +# 90 +drawLine 100 100 100 0 + +# 90 -> 135 +drawLine 100 100 90 0 +drawLine 100 100 80 0 +drawLine 100 100 70 0 +drawLine 100 100 60 0 +drawLine 100 100 50 0 +drawLine 100 100 40 0 +drawLine 100 100 30 0 +drawLine 100 100 20 0 +drawLine 100 100 10 0 + +# 135 +drawLine 100 100 0 0 + +# 135 -> 180 degress +drawLine 100 100 0 10 +drawLine 100 100 0 20 +drawLine 100 100 0 30 +drawLine 100 100 0 40 +drawLine 100 100 0 50 +drawLine 100 100 0 60 +drawLine 100 100 0 70 +drawLine 100 100 0 80 +drawLine 100 100 0 90 + +# 180 +drawLine 100 100 0 100 + +# 180 -> 225 +drawLine 100 100 0 110 +drawLine 100 100 0 120 +drawLine 100 100 0 130 +drawLine 100 100 0 140 +drawLine 100 100 0 150 +drawLine 100 100 0 160 +drawLine 100 100 0 170 +drawLine 100 100 0 180 +drawLine 100 100 0 190 + +# 225 +drawLine 100 100 0 200 + +# 225 -> 270 +drawLine 100 100 10 200 +drawLine 100 100 20 200 +drawLine 100 100 30 200 +drawLine 100 100 40 200 +drawLine 100 100 50 200 +drawLine 100 100 60 200 +drawLine 100 100 70 200 +drawLine 100 100 80 200 +drawLine 100 100 90 200 + +# 270 +drawLine 100 100 100 200 + +# 270 -> 315 degrees +drawLine 100 100 110 200 +drawLine 100 100 120 200 +drawLine 100 100 130 200 +drawLine 100 100 140 200 +drawLine 100 100 150 200 +drawLine 100 100 160 200 +drawLine 100 100 170 200 +drawLine 100 100 180 200 +drawLine 100 100 190 200 + +# 315 +drawLine 100 100 200 200 + +# 315 -> 360 degress +drawLine 100 100 200 100 +drawLine 100 100 200 110 +drawLine 100 100 200 120 +drawLine 100 100 200 130 +drawLine 100 100 200 140 +drawLine 100 100 200 150 +drawLine 100 100 200 160 +drawLine 100 100 200 170 +drawLine 100 100 200 180 +drawLine 100 100 200 190 + +end_block + + +setRenderHint Antialiasing +setPen 0x7fff0000 +translate 0.5 0.5 +repeat_block lines + +setPen 0x000000 8 +translate 20 240 +drawText 0 0 "Steep slopes:" + +translate 0 10 + +drawLine 0 0 -8 400 +translate 20 0 +drawLine 0 0 -7 400 +translate 20 0 +drawLine 0 0 -6 400 +translate 20 0 +drawLine 0 0 -5 400 +translate 20 0 +drawLine 0 0 -4 400 +translate 20 0 +drawLine 0 0 -3 400 +translate 20 0 +drawLine 0 0 -2 400 +translate 20 0 +drawLine 0 0 -1 400 +translate 20 0 +drawLine 0 0 0 400 +translate 20 0 +drawLine 0 0 1 400 +translate 20 0 +drawLine 0 0 2 400 +translate 20 0 +drawLine 0 0 3 400 +translate 20 0 +drawLine 0 0 4 400 +translate 20 0 +drawLine 0 0 5 400 +translate 20 0 +drawLine 0 0 6 400 +translate 20 0 +drawLine 0 0 7 400 +translate 20 0 +drawLine 0 0 8 400 + +resetMatrix + +translate 20 450 + +drawText 0 0 "Zero length lines:" + +translate 0 20 +drawText 100 10 "Square cap" +save +begin_block points +setPen 0x000000 1 solidline squarecap +drawLine 0 0 0 0 +setPen 0x000000 2 solidline squarecap +drawLine 8 0 8 0 +setPen 0x000000 3 solidline squarecap +drawLine 16 0 16 0 +setPen 0x000000 4 solidline squarecap +drawLine 24 0 24 0 +setPen 0x000000 5 solidline squarecap +drawLine 32 0 32 0 +setPen 0x000000 6 solidline squarecap +drawLine 40 0 40 0 +setPen 0x000000 7 solidline squarecap +drawLine 48 0 48 0 +setPen 0x000000 8 solidline squarecap +drawLine 57 0 57 0 +setPen 0x000000 9 solidline squarecap +drawLine 67 0 67 0 +setPen 0x000000 10 solidline squarecap +drawLine 78 0 78 0 +end_block points +restore + +translate 0 12 +setRenderHint Antialiasing off +repeat_block points +setRenderHint Antialiasing + +translate 0 20 +drawText 100 10 "Round cap" +save +begin_block points2 +setPen 0x000000 1 solidline roundcap +drawLine 0 0 0 0 +setPen 0x000000 2 solidline roundcap +drawLine 8 0 8 0 +setPen 0x000000 3 solidline roundcap +drawLine 16 0 16 0 +setPen 0x000000 4 solidline roundcap +drawLine 24 0 24 0 +setPen 0x000000 5 solidline roundcap +drawLine 32 0 32 0 +setPen 0x000000 6 solidline roundcap +drawLine 40 0 40 0 +setPen 0x000000 7 solidline roundcap +drawLine 48 0 48 0 +setPen 0x000000 8 solidline roundcap +drawLine 57 0 57 0 +setPen 0x000000 9 solidline roundcap +drawLine 67 0 67 0 +setPen 0x000000 10 solidline roundcap +drawLine 78 0 78 0 +end_block points2 +restore + +translate 0 12 +setRenderHint Antialiasing off +repeat_block points2 +setRenderHint Antialiasing + +translate 0 20 +drawText 100 10 "Flat cap" +save +begin_block points3 +setPen 0x000000 1 solidline flatcap +drawLine 0 0 0 0 +setPen 0x000000 2 solidline flatcap +drawLine 8 0 8 0 +setPen 0x000000 3 solidline flatcap +drawLine 16 0 16 0 +setPen 0x000000 4 solidline flatcap +drawLine 24 0 24 0 +setPen 0x000000 5 solidline flatcap +drawLine 32 0 32 0 +setPen 0x000000 6 solidline flatcap +drawLine 40 0 40 0 +setPen 0x000000 7 solidline flatcap +drawLine 48 0 48 0 +setPen 0x000000 8 solidline flatcap +drawLine 57 0 57 0 +setPen 0x000000 9 solidline flatcap +drawLine 67 0 67 0 +setPen 0x000000 10 solidline flatcap +drawLine 78 0 78 0 +end_block points3 +restore + +translate 0 12 +setRenderHint Antialiasing off +repeat_block points3 + +resetMatrix +translate -220 667.226 +drawText 230 -80 "Task 194266 (should see only one line):" +setPen black +drawRect 230.5 -70.5 122 12 +setRenderHint Antialiasing +setPen red +drawLine 236.842105263 -63.775117299 247.368421053 -63.775437504 + +setRenderHint Antialiasing off +resetMatrix +translate 10 640 +setPen black +drawText 0 -10 "Task 207147 (should see two lines):" +drawRect 0.5 0.5 64 64 +setRenderHint Antialiasing +setPen red + +drawLine 4.5 4.5 4.5001 60.5 +drawLine 4.5 4.5 60.5 4.5001 + +setRenderHint Antialiasing off +resetMatrix +translate 10 730 +setPen black +drawText 0 -10 "Task 229459 (should see one diagonal line):" +drawRect 0.5 0.5 64 64 +setPen red 2 solidline flatcap + +setClipRect 2 2 63 63 +drawLine 1.5 1.5 33560000 33560000
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/lines2.qps b/tests/auto/integrationtests/lancelot/scripts/lines2.qps new file mode 100644 index 0000000000..af6ad65939 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/lines2.qps @@ -0,0 +1,179 @@ +# Version: 1 +# CheckVsReference: 5% + +translate 10 20 +drawText 0 0 "Thin lines" + +translate 60 70 + +save +begin_block lines +translate 0 -60 +translate 0 5 +setPen 0x000000 0.05 +drawLine -60 -2 60 2 +translate 0 5 +setPen 0x000000 0.1 +drawLine -60 -2 60 2 +translate 0 5 +setPen 0x000000 0.15 +drawLine -60 -2 60 2 +translate 0 5 +setPen 0x000000 0.2 +drawLine -60 -2 60 2 +translate 0 5 +setPen 0x000000 0.25 +drawLine -60 -2 60 2 +translate 0 5 +setPen 0x000000 0.3 +drawLine -60 -2 60 2 +translate 0 5 +setPen 0x000000 0.35 +drawLine -60 -2 60 2 +translate 0 5 +setPen 0x000000 0.2 +drawLine -60 -2 60 2 +translate 0 5 +setPen 0x000000 0.25 +drawLine -60 -2 60 2 +translate 0 5 +setPen 0x000000 0.5 +drawLine -60 -2 60 2 +translate 0 5 +setPen 0x000000 0.55 +drawLine -60 -2 60 2 +translate 0 5 +setPen 0x000000 0.6 +drawLine -60 -2 60 2 +translate 0 5 +setPen 0x000000 0.65 +drawLine -60 -2 60 2 +translate 0 5 +setPen 0x000000 0.7 +drawLine -60 -2 60 2 +translate 0 5 +setPen 0x000000 0.75 +drawLine -60 -2 60 2 +translate 0 5 +setPen 0x000000 0.8 +drawLine -60 -2 60 2 +translate 0 5 +setPen 0x000000 0.85 +drawLine -60 -2 60 2 +translate 0 5 +setPen 0x000000 0.9 +drawLine -60 -2 60 2 +translate 0 5 +setPen 0x000000 0.95 +drawLine -60 -2 60 2 +translate 0 5 +setPen 0x000000 1 +drawLine -60 -2 60 2 +translate 0 5 +setPen 0x000000 1.05 +drawLine -60 -2 60 2 +translate 0 5 +setPen 0x000000 1.1 +drawLine -60 -2 60 2 +translate 0 5 +setPen 0x000000 1.15 +drawLine -60 -2 60 2 +translate 0 5 +setPen 0x000000 1.2 +drawLine -60 -2 60 2 +translate 0 5 +setPen 0x000000 1.25 +drawLine -60 -2 60 2 +translate 0 5 +setPen 0x000000 1.3 +drawLine -60 -2 60 2 +translate 0 5 +setPen 0x000000 1.35 +drawLine -60 -2 60 2 +translate 0 5 +setPen 0x000000 1.2 +drawLine -60 -2 60 2 +translate 0 5 +setPen 0x000000 1.25 +drawLine -60 -2 60 2 +translate 0 5 +setPen 0x000000 1.5 +drawLine -60 -2 60 2 +translate 0 5 +setPen 0x000000 1.55 +drawLine -60 -2 60 2 +translate 0 5 +setPen 0x000000 1.6 +drawLine -60 -2 60 2 +translate 0 5 +setPen 0x000000 1.65 +drawLine -60 -2 60 2 +translate 0 5 +setPen 0x000000 1.7 +drawLine -60 -2 60 2 +translate 0 5 +setPen 0x000000 1.75 +drawLine -60 -2 60 2 +translate 0 5 +setPen 0x000000 1.8 +drawLine -60 -2 60 2 +translate 0 5 +setPen 0x000000 1.85 +drawLine -60 -2 60 2 +translate 0 5 +setPen 0x000000 1.9 +drawLine -60 -2 60 2 +translate 0 5 +setPen 0x000000 1.95 +drawLine -60 -2 60 2 +translate 0 5 +setPen 0x000000 2 +drawLine -60 -2 60 2 +end_block +restore + +save +translate 150 0 +scale -1 1 +repeat_block lines +restore + +save +translate 80 220 +rotate 90 +repeat_block lines +restore + +save +translate 80 370 +rotate 90 +scale -1 1 +repeat_block lines +restore + +setRenderHint Antialiasing + +translate 300 0 + +save +repeat_block lines +restore + +save +translate 150 0 +scale -1 1 +repeat_block lines +restore + +save +translate 80 220 +rotate 90 +repeat_block lines +restore + +save +translate 80 370 +rotate 90 +scale -1 1 +repeat_block lines +restore
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/pathfill.qps b/tests/auto/integrationtests/lancelot/scripts/pathfill.qps new file mode 100644 index 0000000000..821b4684c9 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/pathfill.qps @@ -0,0 +1,38 @@ +# Version: 1 +# CheckVsReference: 1% (0 0 850 420) + +setPen afff0000 4 + +setBrush dome_rgb32.png +drawEllipse 10 10 200 200 + +setBrush dome_argb32.png +drawEllipse 220 10 200 200 + +setPen NoPen + +setBrush dome_rgb32.png +drawEllipse 10 220 200 200 + +setBrush dome_argb32.png +drawEllipse 220 220 200 200 + +setBrushOrigin -30 -30 + +setPen afff0000 4 +setBrush dome_rgb32.png +drawEllipse 430 10 200 200 + +setBrush dome_argb32.png +drawEllipse 640 10 200 200 + +setPen NoPen +setBrush dome_rgb32.png +drawEllipse 430 220 200 200 + +setBrush dome_argb32.png +drawEllipse 640 220 200 200 + +setPen black +drawText 150 450 "No offset RGB/ARGB" +drawText 550 450 "-30 offset RGB/ARGB"
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/paths.qps b/tests/auto/integrationtests/lancelot/scripts/paths.qps new file mode 100644 index 0000000000..083026ee17 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/paths.qps @@ -0,0 +1,34 @@ +# Version: 1 + +setPen black +setBrush 7f7fff + +path_moveTo star 50 0 +path_lineTo star 30 90 +path_lineTo star 100 60 +path_lineTo star 0 20 +path_lineTo star 80 100 + +setFont "times" 50 +path_addText text 0 50 "ABCD, 1234, abcd, #¤%&" + +path_addRect rectncircle 0 0 75 75 +path_addEllipse rectncircle 25 25 75 75 +path_setFillRule rectncircle winding + +path_moveTo curve 100 0 +path_cubicTo curve 100 100 50 50 0 100 + +begin_block drawing +drawPath star +translate 100 0 +drawPath rectncircle +translate 100 0 +drawPath curve +translate -200 100 +drawPath text +end_block + +translate 50 100 +rotate 10 +repeat_block drawing diff --git a/tests/auto/integrationtests/lancelot/scripts/paths_aa.qps b/tests/auto/integrationtests/lancelot/scripts/paths_aa.qps new file mode 100644 index 0000000000..4812e2fa5f --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/paths_aa.qps @@ -0,0 +1,4 @@ +# Version: 1 + +setRenderHint LineAntialiasing +import "paths.qps" diff --git a/tests/auto/integrationtests/lancelot/scripts/pens.qps b/tests/auto/integrationtests/lancelot/scripts/pens.qps new file mode 100644 index 0000000000..c72636dcef --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/pens.qps @@ -0,0 +1,133 @@ +# Version: 1 +# CheckVsReference: 1% (0 0 800 800) + + +translate 10 10 + +begin_block penstyles +setPen black 0 solidline flatcap +drawLine 0 0 100 0 +setPen black 0 dashline flatcap +drawLine 100 0 100 40 +setPen black 0 dotline flatcap +drawLine 100 40 200 0 +setPen black 0 dashdotline flatcap +drawLine 200 0 300 0 +setPen black 0 dashdotdotline flatcap +drawLine 300 0 400 40 + +translate 0 50 +setPen blue 2 solidline flatcap +drawLine 0 0 100 0 +setPen blue 2 dashline flatcap +drawLine 100 0 100 40 +setPen blue 2 dotline flatcap +drawLine 100 40 200 0 +setPen blue 2 dashdotline flatcap +drawLine 200 0 300 0 +setPen blue 2 dashdotdotline flatcap +drawLine 300 0 400 40 + +translate 0 50 +setPen red 5 solidline flatcap +drawLine 0 0 100 0 +setPen red 5 dashline flatcap +drawLine 100 0 100 40 +setPen red 5 dotline flatcap +drawLine 100 40 200 0 +setPen red 5 dashdotline flatcap +drawLine 200 0 300 0 +setPen red 5 dashdotdotline flatcap +drawLine 300 0 400 40 +end_block + +translate 0 50 +scale 1 2 +repeat_block penstyles + + +# Test cap styles +resetMatrix +translate 420 10 +setPen green 5 dashdotline flatcap +drawLine 0 0 200 0 +setPen green 5 dashdotline roundcap +drawLine 0 20 200 20 +setPen green 5 dashdotline squarecap +drawLine 0 40 200 40 + + +# Test join styles +resetMatrix +translate 420 80 +setBrush nobrush +begin_block joinstyles +setPen orange 10 solidline flatcap miterjoin +drawPolyline [ 0 0 80 0 80 80 0 80 ] + +translate 0 100 +setPen aquamarine 10 solidline squarecap beveljoin +drawPolyline [ 0 0 80 0 80 80 0 80 ] + +translate 0 100 +setPen purple 10 solidline roundcap roundjoin +drawPolyline [ 0 0 80 0 80 80 0 80 ] +end_block + +translate 130 -200 +scale 2 1 +rotate 1 +repeat_block joinstyles + +# transparent lines +resetMatrix +translate 10 400 +setPen #7f000000 +drawLine 0 0 50 0 +setPen #7f000000 1 SolidLine +drawLine 0 10 50 10 +setPen #7f000000 5 SolidLine +drawLine 0 20 50 20 +setPen #7f000000 10 SolidLine +drawLine 0 30 50 30 +setPen #7f000000 +drawLine 0 0 0 50 +setPen #7f000000 1 SolidLine +drawLine 10 0 10 50 +setPen #7f000000 5 SolidLine +drawLine 20 0 20 50 +setPen #7f000000 10 SolidLine +drawLine 30 0 30 50 + +# pen styles +resetMatrix +translate 0 500 +setPen black 0 DashLine +drawLine 20 20 100 20 +translate 0 10 +setPen black 0 DotLine +drawLine 20 20 100 20 +translate 0 10 +setPen black 0 DashDotLine +drawLine 20 20 100 20 +translate 0 10 +setPen black 0 DashDotDotLine +drawLine 20 20 100 20 + +# scaling ellipse +resetMatrix +setPen black 0.008 DashLine +translate 250 550 +rotate 30 +scale 250 250 +drawEllipse -0.4 -0.4 0.8 0.8 + +# scaling path +path_addEllipse star -0.3 -0.3 0.6 0.6 + +resetMatrix +setPen black 0.008 DashLine +translate 250 550 +rotate 30 +scale 250 250 +drawPath star diff --git a/tests/auto/integrationtests/lancelot/scripts/pens_aa.qps b/tests/auto/integrationtests/lancelot/scripts/pens_aa.qps new file mode 100644 index 0000000000..066cac365f --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/pens_aa.qps @@ -0,0 +1,6 @@ +# Version: 1 +# CheckVsReference: 1% (0 0 800 800) + +setRenderHint LineAntialiasing + +import "pens.qps"
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/pens_cosmetic.qps b/tests/auto/integrationtests/lancelot/scripts/pens_cosmetic.qps new file mode 100644 index 0000000000..d1a60d150d --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/pens_cosmetic.qps @@ -0,0 +1,110 @@ +# Version: 1 +# CheckVsReference: 1% (0 0 585 470) + +path_addEllipse path 22 0 7 7 +path_addRect path 25 5 4 4 + +translate 20 20 + +begin_block lines + save + drawLine 0 0 10 10 + drawLine 2 0 10 0 + drawLine 0 2 0 10 + drawPolygon [12 0 20 0 15 10] + drawPath path + drawEllipse 32 0 8 8 + drawPoint 36 4 + + translate 100 0 + save + scale 4 1 + drawLine 0 0 10 10 + drawLine 2 0 10 0 + drawLine 0 2 0 10 + drawPolygon [12 0 20 0 15 10] + drawPath path + drawEllipse 32 0 8 8 + drawPoint 36 4 + restore + + translate 200 0 + save + scale 1 4 + drawLine 0 0 10 10 + drawLine 2 0 10 0 + drawLine 0 2 0 10 + drawPolygon [12 0 20 0 15 10] + drawPath path + drawEllipse 32 0 8 8 + drawPoint 36 4 + restore + + translate 100 0 + save + scale 4 4 + drawLine 0 0 10 10 + drawLine 2 0 10 0 + drawLine 0 2 0 10 + drawPolygon [12 0 20 0 15 10] + drawPath path + drawEllipse 32 0 8 8 + drawPoint 36 4 + restore + restore +end_block + +drawText 580 15 "non-cosmetic, 0-width" +translate 0 50 + +setPen black 2 +repeat_block lines +drawText 580 15 "non-cosmetic, 2-width" + +translate 0 20 +translate 0 50 +setPen black 0 +pen_setCosmetic true +repeat_block lines +drawText 580 15 "cosmetic, 0-width" + +translate 0 50 +setPen black 2 +pen_setCosmetic true +repeat_block lines +drawText 580 15 "cosmetic, 2-width" + + +setRenderHint Antialiasing +translate 0 20 + +translate 0 50 +setPen black 0 +repeat_block lines +drawText 580 15 "non-cosmetic, 0-width" + +translate 0 50 + +setPen black 2 +repeat_block lines +drawText 580 15 "non-cosmetic, 2-width" + +translate 0 20 +translate 0 50 +setPen black 0 +pen_setCosmetic true +repeat_block lines +drawText 580 15 "cosmetic, 0-width" + +translate 0 50 +setPen black 2 +pen_setCosmetic true +repeat_block lines +drawText 580 15 "cosmetic, 2-width" + + +translate 0 70 +drawText 0 0 "scale(1, 1)" +drawText 150 0 "scale(4, 1)" +drawText 300 0 "scale(1, 4)" +drawText 450 0 "scale(4, 4)"
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/perspectives.qps b/tests/auto/integrationtests/lancelot/scripts/perspectives.qps new file mode 100644 index 0000000000..0b903e5260 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/perspectives.qps @@ -0,0 +1,72 @@ +# Version: 1 + + +setRenderHint Antialiasing + +setPen #00ff00 + +image_load image.png the_image + +translate 10 10 +# standard draw +drawImage the_image 0 0 + +# Rotation w/o smooth xform +save +translate 350 0 +save + setRenderHint SmoothPixmapTransform on + mapQuadToQuad 0 0 0 300 300 300 300 0 0 50 0 200 300 300 300 0 + drawImage the_image 0 0 +restore +restore + +translate 0 320 + +save + setRenderHint SmoothPixmapTransform on + mapQuadToQuad 0 0 0 300 300 300 300 0 0 0 100 300 200 300 300 0 + drawImage the_image 0 0 +restore + +save +translate 350 0 +save + setRenderHint SmoothPixmapTransform on + mapQuadToQuad 0 0 0 300 300 300 300 0 0 0 0 300 300 250 300 50 + drawImage the_image 0 0 +restore +restore + +setRenderHint SmoothPixmapTransform on +setBrush red +setPen black +resetMatrix +translate 100 720 +rotate_y 85 +scale 7 0.01 +drawRect -150 -150 300 300 + +resetMatrix +setBrush gam030.png +setPen black 30 +translate 700 700 +rotate_y -85 +scale 7 0.01 +drawRect -150 -150 300 300 + +resetMatrix +setPen black +translate 125 20 +drawText 0 0 "No transform" +translate 350 0 +drawText 0 0 "Left Tilted" +resetMatrix +translate 125 350 +drawText 0 0 "Bottom Tilted" +translate 350 0 +drawText 0 0 "Right Tilted" +translate 120 0 +resetMatrix +translate 300 760 +drawText 0 0 "Perspective Clipping"
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/perspectives2.qps b/tests/auto/integrationtests/lancelot/scripts/perspectives2.qps new file mode 100644 index 0000000000..2f6d1d653c --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/perspectives2.qps @@ -0,0 +1,309 @@ +# Version: 1 + +setRenderHint Antialiasing + +image_load zebra.png zebra_png + +image_convertToFormat zebra_png zebra ARGB32_Premultiplied + +translate 75 100 +# standard draw +begin_block row +drawImage zebra -50 -50 + +translate 90 0 +save +rotate_y 50 +drawImage zebra -50 -50 +restore + +translate 65 0 +save +rotate_y 60 +drawImage zebra -50 -50 +restore + +translate 50 0 +save +rotate_y 70 +drawImage zebra -50 -50 +restore + +translate 30 0 +save +rotate_y 80 +drawImage zebra -50 -50 +restore + +translate 24 0 +save +rotate_y 82 +drawImage zebra -50 -50 +restore + +translate 20 0 +save +rotate_y 84 +drawImage zebra -50 -50 +restore + +translate 16 0 +save +rotate_y 86 +drawImage zebra -50 -50 +restore + +translate 12 0 +save +rotate_y 87 +drawImage zebra -50 -50 +restore + +translate 8 0 +save +rotate_y 88 +drawImage zebra -50 -50 +restore + +translate 6 0 +save +rotate_y 89 +drawImage zebra -50 -50 +restore + +translate 6 0 +save +rotate_y 91 +drawImage zebra -50 -50 +restore + +translate 6 0 +save +rotate_y 92 +drawImage zebra -50 -50 +restore + +translate 8 0 +save +rotate_y 93 +drawImage zebra -50 -50 +restore + +translate 12 0 +save +rotate_y 94 +drawImage zebra -50 -50 +restore + +translate 16 0 +save +rotate_y 96 +drawImage zebra -50 -50 +restore + +translate 20 0 +save +rotate_y 98 +drawImage zebra -50 -50 +restore + +translate 24 0 +save +rotate_y 100 +drawImage zebra -50 -50 +restore + +translate 30 0 +save +rotate_y 110 +drawImage zebra -50 -50 +restore + +translate 50 0 +save +rotate_y 120 +drawImage zebra -50 -50 +restore + +translate 65 0 +save +rotate_y 130 +drawImage zebra -50 -50 +restore + +translate 90 0 +save +rotate_y 180 +drawImage zebra -50 -50 +restore +end_block + +resetMatrix +translate 75 280 +setRenderHint SmoothPixmapTransform +repeat_block row + +resetMatrix +setPen black +translate 300 20 +drawText 0 0 "Fast Pixmap Transform" +resetMatrix +translate 300 210 +drawText 0 0 "Smooth Pixmap Transform" + +resetMatrix +translate 0 400 + +image_load dome_argb32.png the_pixmap + +image_convertToFormat the_pixmap dome ARGB32 + +setRenderHint SmoothPixmapTransform false + +translate 75 100 +# standard draw +begin_block row +drawImage dome -50 -50 + +translate 90 0 +save +rotate_y 50 +drawImage dome -50 -50 +restore + +translate 65 0 +save +rotate_y 60 +drawImage dome -50 -50 +restore + +translate 50 0 +save +rotate_y 70 +drawImage dome -50 -50 +restore + +translate 30 0 +save +rotate_y 80 +drawImage dome -50 -50 +restore + +translate 24 0 +save +rotate_y 82 +drawImage dome -50 -50 +restore + +translate 20 0 +save +rotate_y 84 +drawImage dome -50 -50 +restore + +translate 16 0 +save +rotate_y 86 +drawImage dome -50 -50 +restore + +translate 12 0 +save +rotate_y 87 +drawImage dome -50 -50 +restore + +translate 8 0 +save +rotate_y 88 +drawImage dome -50 -50 +restore + +translate 6 0 +save +rotate_y 89 +drawImage dome -50 -50 +restore + +translate 6 0 +save +rotate_y 91 +drawImage dome -50 -50 +restore + +translate 6 0 +save +rotate_y 92 +drawImage dome -50 -50 +restore + +translate 8 0 +save +rotate_y 93 +drawImage dome -50 -50 +restore + +translate 12 0 +save +rotate_y 94 +drawImage dome -50 -50 +restore + +translate 16 0 +save +rotate_y 96 +drawImage dome -50 -50 +restore + +translate 20 0 +save +rotate_y 98 +drawImage dome -50 -50 +restore + +translate 24 0 +save +rotate_y 100 +drawImage dome -50 -50 +restore + +translate 30 0 +save +rotate_y 110 +drawImage dome -50 -50 +restore + +translate 50 0 +save +rotate_y 120 +drawImage dome -50 -50 +restore + +translate 65 0 +save +rotate_y 130 +drawImage dome -50 -50 +restore + +translate 90 0 +save +rotate_y 180 +drawImage dome -50 -50 +restore +end_block + +resetMatrix +translate 0 400 +translate 75 280 +setRenderHint SmoothPixmapTransform +repeat_block row + +resetMatrix +setPen black +translate 0 400 +translate 300 20 +drawText 0 0 "Fast Pixmap Transform" +resetMatrix +translate 0 400 +translate 300 210 +drawText 0 0 "Smooth Pixmap Transform"
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/pixmap_rotation.qps b/tests/auto/integrationtests/lancelot/scripts/pixmap_rotation.qps new file mode 100644 index 0000000000..2f1ffb53e6 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/pixmap_rotation.qps @@ -0,0 +1,30 @@ +# Version: 1 +# CheckVsReference: 0% (0 0 440 220) + +translate 120 120 + +begin_block drawing +save + rotate 90 + drawPixmap solid.png 0 0 + + rotate 90 + drawPixmap solid.png 0 0 + + rotate 90 + drawPixmap solid.png 0 0 + + rotate 90 + drawPixmap solid.png 0 0 +restore +end_block + +resetMatrix + +translate 340 120 +repeat_block drawing + +resetMatrix + +drawText 50 240 "Normal X form" +drawText 270 240 "Smooth xform"
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/pixmap_scaling.qps b/tests/auto/integrationtests/lancelot/scripts/pixmap_scaling.qps new file mode 100644 index 0000000000..651896f4a3 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/pixmap_scaling.qps @@ -0,0 +1,224 @@ +# Version: 1 +# CheckVsReference: 0% (0 30 600 70) +# CheckVsReference: 0% (290 130 280 60) +# CheckVsReference: 0% (0 180 250 90) + +# Hurra! Force line endings (?) + +translate 5 25 +setFont "arial" 8 + +save + drawText 15 0 "opaque image" + translate 50 50 + save + translate 1 1 + scale 20 20 + drawImage solid2x2.png 0 0 + restore + save + translate -1 1 + scale -20 20 + drawImage solid2x2.png 0 0 + restore + save + translate 1 -1 + scale 20 -20 + drawImage solid2x2.png 0 0 + restore + save + translate -1 -1 + scale -20 -20 + drawImage solid2x2.png 0 0 + restore + + +restore + +save + translate 150 0 + drawText 15 0 "alpha image" + translate 50 50 + save + translate 1 1 + scale 20 20 + drawImage alpha2x2.png 0 0 + restore + save + translate -1 1 + scale -20 20 + drawImage alpha2x2.png 0 0 + restore + save + translate 1 -1 + scale 20 -20 + drawImage alpha2x2.png 0 0 + restore + save + translate -1 -1 + scale -20 -20 + drawImage alpha2x2.png 0 0 + restore +restore + + +save + translate 0 150 + drawText 15 0 "solid pixmap" + translate 50 50 + save + translate 1 1 + scale 20 20 + drawPixmap solid2x2.png 0 0 + restore + save + translate -1 1 + scale -20 20 + drawPixmap solid2x2.png 0 0 + restore + save + translate 1 -1 + scale 20 -20 + drawPixmap solid2x2.png 0 0 + restore + save + translate -1 -1 + scale -20 -20 + drawPixmap solid2x2.png 0 0 + restore +restore + + +save + translate 150 150 + drawText 15 0 "alpha pixmap" + translate 50 50 + save + translate 1 1 + scale 20 20 + drawPixmap alpha2x2.png 0 0 + restore + save + translate -1 1 + scale -20 20 + drawPixmap alpha2x2.png 0 0 + restore + save + translate 1 -1 + scale 20 -20 + drawPixmap alpha2x2.png 0 0 + restore + save + translate -1 -1 + scale -20 -20 + drawPixmap alpha2x2.png 0 0 + restore +restore + + +save + translate 300 10 + save + drawText 0 -10 "subrect solid image" + drawImage solid2x2.png 0 0 50 5 0 0.0 2 0.2 + drawImage solid2x2.png 0 5 50 5 0 0.2 2 0.2 + drawImage solid2x2.png 0 10 50 5 0 0.4 2 0.2 + drawImage solid2x2.png 0 15 50 5 0 0.6 2 0.2 + drawImage solid2x2.png 0 20 50 5 0 0.8 2 0.2 + drawImage solid2x2.png 0 25 50 5 0 1.0 2 0.2 + drawImage solid2x2.png 0 30 50 5 0 1.2 2 0.2 + drawImage solid2x2.png 0 35 50 5 0 1.4 2 0.2 + drawImage solid2x2.png 0 40 50 5 0 1.6 2 0.2 + drawImage solid2x2.png 0 45 50 5 0 1.8 2 0.2 + translate 60 0 + drawImage solid2x2.png 0 0 5 50 0.0 0 0.2 2 + drawImage solid2x2.png 5 0 5 50 0.2 0 0.2 2 + drawImage solid2x2.png 10 0 5 50 0.4 0 0.2 2 + drawImage solid2x2.png 15 0 5 50 0.6 0 0.2 2 + drawImage solid2x2.png 20 0 5 50 0.8 0 0.2 2 + drawImage solid2x2.png 25 0 5 50 1.0 0 0.2 2 + drawImage solid2x2.png 30 0 5 50 1.2 0 0.2 2 + drawImage solid2x2.png 35 0 5 50 1.4 0 0.2 2 + drawImage solid2x2.png 40 0 5 50 1.6 0 0.2 2 + drawImage solid2x2.png 45 0 5 50 1.8 0 0.2 2 + restore + + save + translate 150 0 + drawText 0 -10 "subrect solid image" + drawImage alpha2x2.png 0 0 50 5 0 0.0 2 0.2 + drawImage alpha2x2.png 0 5 50 5 0 0.2 2 0.2 + drawImage alpha2x2.png 0 10 50 5 0 0.4 2 0.2 + drawImage alpha2x2.png 0 15 50 5 0 0.6 2 0.2 + drawImage alpha2x2.png 0 20 50 5 0 0.8 2 0.2 + drawImage alpha2x2.png 0 25 50 5 0 1.0 2 0.2 + drawImage alpha2x2.png 0 30 50 5 0 1.2 2 0.2 + drawImage alpha2x2.png 0 35 50 5 0 1.4 2 0.2 + drawImage alpha2x2.png 0 40 50 5 0 1.6 2 0.2 + drawImage alpha2x2.png 0 45 50 5 0 1.8 2 0.2 + translate 60 0 + drawImage alpha2x2.png 0 0 5 50 0.0 0 0.2 2 + drawImage alpha2x2.png 5 0 5 50 0.2 0 0.2 2 + drawImage alpha2x2.png 10 0 5 50 0.4 0 0.2 2 + drawImage alpha2x2.png 15 0 5 50 0.6 0 0.2 2 + drawImage alpha2x2.png 20 0 5 50 0.8 0 0.2 2 + drawImage alpha2x2.png 25 0 5 50 1.0 0 0.2 2 + drawImage alpha2x2.png 30 0 5 50 1.2 0 0.2 2 + drawImage alpha2x2.png 35 0 5 50 1.4 0 0.2 2 + drawImage alpha2x2.png 40 0 5 50 1.6 0 0.2 2 + drawImage alpha2x2.png 45 0 5 50 1.8 0 0.2 2 + restore + + save + translate 0 100 + drawText 0 -10 "subrect alpha pixmap" + drawPixmap solid2x2.png 0 0 50 5 0 0.0 2 0.2 + drawPixmap solid2x2.png 0 5 50 5 0 0.2 2 0.2 + drawPixmap solid2x2.png 0 10 50 5 0 0.4 2 0.2 + drawPixmap solid2x2.png 0 15 50 5 0 0.6 2 0.2 + drawPixmap solid2x2.png 0 20 50 5 0 0.8 2 0.2 + drawPixmap solid2x2.png 0 25 50 5 0 1.0 2 0.2 + drawPixmap solid2x2.png 0 30 50 5 0 1.2 2 0.2 + drawPixmap solid2x2.png 0 35 50 5 0 1.4 2 0.2 + drawPixmap solid2x2.png 0 40 50 5 0 1.6 2 0.2 + drawPixmap solid2x2.png 0 45 50 5 0 1.8 2 0.2 + translate 60 0 + drawPixmap solid2x2.png 0 0 5 50 0.0 0 0.2 2 + drawPixmap solid2x2.png 5 0 5 50 0.2 0 0.2 2 + drawPixmap solid2x2.png 10 0 5 50 0.4 0 0.2 2 + drawPixmap solid2x2.png 15 0 5 50 0.6 0 0.2 2 + drawPixmap solid2x2.png 20 0 5 50 0.8 0 0.2 2 + drawPixmap solid2x2.png 25 0 5 50 1.0 0 0.2 2 + drawPixmap solid2x2.png 30 0 5 50 1.2 0 0.2 2 + drawPixmap solid2x2.png 35 0 5 50 1.4 0 0.2 2 + drawPixmap solid2x2.png 40 0 5 50 1.6 0 0.2 2 + drawPixmap solid2x2.png 45 0 5 50 1.8 0 0.2 2 + restore + + save + translate 150 100 + drawText 0 -10 "subrect alpha pixmap" + drawPixmap alpha2x2.png 0 0 50 5 0 0.0 2 0.2 + drawPixmap alpha2x2.png 0 5 50 5 0 0.2 2 0.2 + drawPixmap alpha2x2.png 0 10 50 5 0 0.4 2 0.2 + drawPixmap alpha2x2.png 0 15 50 5 0 0.6 2 0.2 + drawPixmap alpha2x2.png 0 20 50 5 0 0.8 2 0.2 + drawPixmap alpha2x2.png 0 25 50 5 0 1.0 2 0.2 + drawPixmap alpha2x2.png 0 30 50 5 0 1.2 2 0.2 + drawPixmap alpha2x2.png 0 35 50 5 0 1.4 2 0.2 + drawPixmap alpha2x2.png 0 40 50 5 0 1.6 2 0.2 + drawPixmap alpha2x2.png 0 45 50 5 0 1.8 2 0.2 + translate 60 0 + drawPixmap alpha2x2.png 0 0 5 50 0.0 0 0.2 2 + drawPixmap alpha2x2.png 5 0 5 50 0.2 0 0.2 2 + drawPixmap alpha2x2.png 10 0 5 50 0.4 0 0.2 2 + drawPixmap alpha2x2.png 15 0 5 50 0.6 0 0.2 2 + drawPixmap alpha2x2.png 20 0 5 50 0.8 0 0.2 2 + drawPixmap alpha2x2.png 25 0 5 50 1.0 0 0.2 2 + drawPixmap alpha2x2.png 30 0 5 50 1.2 0 0.2 2 + drawPixmap alpha2x2.png 35 0 5 50 1.4 0 0.2 2 + drawPixmap alpha2x2.png 40 0 5 50 1.6 0 0.2 2 + drawPixmap alpha2x2.png 45 0 5 50 1.8 0 0.2 2 + restore + +restore
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/pixmap_subpixel.qps b/tests/auto/integrationtests/lancelot/scripts/pixmap_subpixel.qps new file mode 100644 index 0000000000..908f7c3465 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/pixmap_subpixel.qps @@ -0,0 +1,117 @@ +# Version: 1 +# CheckVsReference: 5% + + +translate 50 50 + +# Pixmaps at 0.1 offset, unclipped +begin_block draw_pixmaps +save + drawPixmap border.png 0 0 + translate 20.1 0.1 + drawPixmap border.png 0 0 + translate 20.1 0.1 + drawPixmap border.png 0 0 + translate 20.1 0.1 + drawPixmap border.png 0 0 + translate 20.1 0.1 + drawPixmap border.png 0 0 + translate 20.1 0.1 + drawPixmap border.png 0 0 + translate 20.1 0.1 + drawPixmap border.png 0 0 + translate 20.1 0.1 + drawPixmap border.png 0 0 + translate 20.1 0.1 + drawPixmap border.png 0 0 + translate 20.1 0.1 + drawPixmap border.png 0 0 + translate 20.1 0.1 + + drawPixmap border.png 0 0 + translate 20.1 0.1 + drawPixmap border.png 0 0 + translate 20.1 0.1 + drawPixmap border.png 0 0 + translate 20.1 0.1 + drawPixmap border.png 0 0 + translate 20.1 0.1 + drawPixmap border.png 0 0 + translate 20.1 0.1 + drawPixmap border.png 0 0 + translate 20.1 0.1 + drawPixmap border.png 0 0 + translate 20.1 0.1 + drawPixmap border.png 0 0 + translate 20.1 0.1 + drawPixmap border.png 0 0 + translate 20.1 0.1 + drawPixmap border.png 0 0 + translate 20.1 0.1 +restore +end_block + +# Tiled pixmaps at 0.1 offsets, unclipped +translate 0 50 +begin_block draw_tiled +save + drawTiledPixmap border.png 0 0 16 16 0 0 + translate 20.1 0.1 + drawTiledPixmap border.png 0 0 16 16 0 0 + translate 20.1 0.1 + drawTiledPixmap border.png 0 0 16 16 0 0 + translate 20.1 0.1 + drawTiledPixmap border.png 0 0 16 16 0 0 + translate 20.1 0.1 + drawTiledPixmap border.png 0 0 16 16 0 0 + translate 20.1 0.1 + drawTiledPixmap border.png 0 0 16 16 0 0 + translate 20.1 0.1 + drawTiledPixmap border.png 0 0 16 16 0 0 + translate 20.1 0.1 + drawTiledPixmap border.png 0 0 16 16 0 0 + translate 20.1 0.1 + drawTiledPixmap border.png 0 0 16 16 0 0 + translate 20.1 0.1 + drawTiledPixmap border.png 0 0 16 16 0 0 + translate 20.1 0.1 + + drawTiledPixmap border.png 0 0 16 16 0 0 + translate 20.1 0.1 + drawTiledPixmap border.png 0 0 16 16 0 0 + translate 20.1 0.1 + drawTiledPixmap border.png 0 0 16 16 0 0 + translate 20.1 0.1 + drawTiledPixmap border.png 0 0 16 16 0 0 + translate 20.1 0.1 + drawTiledPixmap border.png 0 0 16 16 0 0 + translate 20.1 0.1 + drawTiledPixmap border.png 0 0 16 16 0 0 + translate 20.1 0.1 + drawTiledPixmap border.png 0 0 16 16 0 0 + translate 20.1 0.1 + drawTiledPixmap border.png 0 0 16 16 0 0 + translate 20.1 0.1 + drawTiledPixmap border.png 0 0 16 16 0 0 + translate 20.1 0.1 + drawTiledPixmap border.png 0 0 16 16 0 0 + translate 20.1 0.1 +restore +end_block + + +path_moveTo clip 0 0 +path_lineTo clip width 0 +path_lineTo clip width 400 +path_lineTo clip 0 height +setClipPath clip + +translate 0 50 +# Pixmaps at 0.1 offset, clipped +repeat_block draw_pixmaps + + +# Tiled pixmaps at 0.1 offsets... +translate 0 50 +repeat_block draw_tiled + diff --git a/tests/auto/integrationtests/lancelot/scripts/pixmaps.qps b/tests/auto/integrationtests/lancelot/scripts/pixmaps.qps new file mode 100644 index 0000000000..8e60997acc --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/pixmaps.qps @@ -0,0 +1,106 @@ +# Version: 1 +# CheckVsReference: 1% (0 0 690 580) + + +setRenderHint Antialiasing + +setPen #00ff00 + +pixmap_load dome_argb32.png the_pixmap +begin_block draw_stuff + +save + + # standard draw + drawPixmap the_pixmap 0 0 + + # sub recting + translate 120 0 + drawPixmap the_pixmap 0 0 40 40 0 0 40 40 + drawPixmap the_pixmap 60 0 40 40 60 0 40 40 + drawPixmap the_pixmap 0 60 40 40 0 60 40 40 + drawPixmap the_pixmap 60 60 40 40 60 60 40 40 + drawPixmap the_pixmap 0 40 40 20 0 40 40 20 + drawPixmap the_pixmap 60 40 40 20 60 40 40 20 + drawPixmap the_pixmap 40 0 20 100 40 0 20 100 + + # subrecting w/scale + translate 120 0 + drawPixmap the_pixmap 0 0 50 50 0 0 25 25 + drawPixmap the_pixmap 50 0 50 50 25 0 25 25 + drawPixmap the_pixmap 0 50 50 50 0 25 25 25 + drawPixmap the_pixmap 50 50 50 50 25 25 25 25 + + # subrecting w/scale & smooth xform + translate 120 0 + setRenderHint SmoothPixmapTransformation + drawPixmap the_pixmap 0 0 50 50 0 0 25 25 + drawPixmap the_pixmap 50 0 50 50 25 0 25 25 + drawPixmap the_pixmap 0 50 50 50 0 25 25 25 + drawPixmap the_pixmap 50 50 50 50 25 25 25 25 + + + # Rotation w/o smooth xform + translate 120 0 + save + setRenderHint SmoothPixmapTransform off + rotate 10 + drawPixmap the_pixmap 0 0 + restore + + # Rotation w smooth xform + translate 120 0 + save + setRenderHint SmoothPixmapTransform + rotate 10 + drawPixmap the_pixmap 0 0 + restore + +restore + +end_block + + +translate 0 120 +pixmap_load dome_rgb32.png the_pixmap +repeat_block draw_stuff + +translate 0 120 +pixmap_load dome_indexed.png the_pixmap +repeat_block draw_stuff + +translate 0 120 +pixmap_load dome_indexed_mask.png the_pixmap +repeat_block draw_stuff + +translate 0 120 +pixmap_load dome_mono.png the_pixmap +repeat_block draw_stuff + + +resetMatrix +translate 700 60 +setPen black +drawText 0 0 "32 bit w/alpha" +translate 0 120 +drawText 0 0 "32 bit w/o alpha" +translate 0 120 +drawText 0 0 "8 bit indexed" +translate 0 120 +drawText 0 0 "8 bit indexed w/mask" +translate 0 120 +drawText 0 0 "1 bit" +resetMatrix +translate 0 600 +drawText 0 0 "normal" +translate 120 0 +drawText 0 0 "subrect" +translate 120 0 +drawText 0 0 "subrect scale" +translate 120 0 +drawText 0 0 "subrect scale smooth" +translate 120 0 +drawText 0 0 "xform" +translate 120 0 +drawText 0 0 "smooth xform" +translate 120 0
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/porter_duff.qps b/tests/auto/integrationtests/lancelot/scripts/porter_duff.qps new file mode 100644 index 0000000000..166e48a57f --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/porter_duff.qps @@ -0,0 +1,251 @@ +# Version: 1 +# CheckVsReference: 5% + + +translate 0 50 + +surface_begin 0 0 100 100 + +begin_block predraw +setRenderHint Antialiasing +setPen nopen +setBrush 0x7f000000 +drawEllipse 10 10 80 80 +end_block + +setCompositionMode SourceOver + +begin_block postdraw + + +setBrush 0x1fff0000 +drawRect 0 0 50 50 + +setBrush 0xdf00ff00 +drawRect 50 50 50 50 + +setBrush 0x7f0000ff +drawEllipse 30 30 40 40 + +# a black rectangle around +setCompositionMode SourceOver +setPen black +setBrush nobrush +drawRect 0.5 0.5 99 99 + +end_block +surface_end + + +# Destination over +surface_begin 100 0 100 100 +repeat_block predraw +setCompositionMode DestinationOver +repeat_block postdraw +surface_end + + +# Clear +surface_begin 200 0 100 100 +repeat_block predraw +setCompositionMode Clear +repeat_block postdraw +surface_end + + +# Source +surface_begin 300 0 100 100 +repeat_block predraw +setCompositionMode Source +repeat_block postdraw +surface_end + + +# Destination +surface_begin 400 0 100 100 +repeat_block predraw +setCompositionMode Destination +repeat_block postdraw +surface_end + + +# Source In +surface_begin 500 0 100 100 +repeat_block predraw +setCompositionMode SourceIn +repeat_block postdraw +surface_end + +translate 0 50 + +# Destination In +surface_begin 0 100 100 100 +repeat_block predraw +setCompositionMode DestinationIn +repeat_block postdraw +surface_end + + +# Source Out +surface_begin 100 100 100 100 +repeat_block predraw +setCompositionMode SourceOut +repeat_block postdraw +surface_end + + +# Destination Out +surface_begin 200 100 100 100 +repeat_block predraw +setCompositionMode DestinationOut +repeat_block postdraw +surface_end + + +# SourceAtop +surface_begin 300 100 100 100 +repeat_block predraw +setCompositionMode SourceAtop +repeat_block postdraw +surface_end + + +# DestinationAtop +surface_begin 400 100 100 100 +repeat_block predraw +setCompositionMode DestinationAtop +repeat_block postdraw +surface_end + + +# Xor +surface_begin 500 100 100 100 +repeat_block predraw +setCompositionMode Xor +repeat_block postdraw +surface_end + +translate 0 50 + +# Plus +surface_begin 0 200 100 100 +repeat_block predraw +setCompositionMode Plus +repeat_block postdraw +surface_end + + +# Multiply +surface_begin 100 200 100 100 +repeat_block predraw +setCompositionMode Multiply +repeat_block postdraw +surface_end + + +# Screen +surface_begin 200 200 100 100 +repeat_block predraw +setCompositionMode Screen +repeat_block postdraw +surface_end + + +# Overlay +surface_begin 300 200 100 100 +repeat_block predraw +setCompositionMode Overlay +repeat_block postdraw +surface_end + + +# Darken +surface_begin 400 200 100 100 +repeat_block predraw +setCompositionMode Darken +repeat_block postdraw +surface_end + + +# Lighten +surface_begin 500 200 100 100 +repeat_block predraw +setCompositionMode Lighten +repeat_block postdraw +surface_end + +translate 0 50 + +# ColorDodge +surface_begin 0 300 100 100 +repeat_block predraw +setCompositionMode ColorDodge +repeat_block postdraw +surface_end + + +# Multiply +surface_begin 100 300 100 100 +repeat_block predraw +setCompositionMode ColorBurn +repeat_block postdraw +surface_end + + +# Screen +surface_begin 200 300 100 100 +repeat_block predraw +setCompositionMode HardLight +repeat_block postdraw +surface_end + + +# Overlay +surface_begin 300 300 100 100 +repeat_block predraw +setCompositionMode SoftLight +repeat_block postdraw +surface_end + + +# Darken +surface_begin 400 300 100 100 +repeat_block predraw +setCompositionMode Difference +repeat_block postdraw +surface_end + + +# Lighten +surface_begin 500 300 100 100 +repeat_block predraw +setCompositionMode Exclusion +repeat_block postdraw +surface_end + +resetMatrix + +drawText 0 50 "SourceOver" +drawText 100 50 "DestinationOver" +drawText 200 50 "Clear" +drawText 300 50 "Source" +drawText 400 50 "Destination" +drawText 500 50 "SourceIn" +drawText 0 200 "DestinationIn" +drawText 100 200 "SourceOut" +drawText 200 200 "DestinationOut" +drawText 300 200 "SourceAtop" +drawText 400 200 "DestinationAtop" +drawText 500 200 "Xor" +drawText 0 350 "Plus" +drawText 100 350 "Multiply" +drawText 200 350 "Screen" +drawText 300 350 "Overlay" +drawText 400 350 "Darken" +drawText 500 350 "Lighten" +drawText 0 500 "ColorDodge" +drawText 100 500 "ColorBurn" +drawText 200 500 "HardLight" +drawText 300 500 "SoftLight" +drawText 400 500 "Difference" +drawText 500 500 "Exclusion"
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/porter_duff2.qps b/tests/auto/integrationtests/lancelot/scripts/porter_duff2.qps new file mode 100644 index 0000000000..a792d9b278 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/porter_duff2.qps @@ -0,0 +1,261 @@ +# Version: 1 +# CheckVsReference: 1% (0 50 600 100) +# CheckVsReference: 1% (0 200 600 100) +# CheckVsReference: 1% (0 350 600 100) +# CheckVsReference: 1% (0 500 600 100) + +translate 0 50 + +surface_begin 0 0 100 100 + +begin_block predraw +setRenderHint Antialiasing +setPen nopen +gradient_clearStops +gradient_appendStop 0 efff0000 +gradient_appendStop 0.5 dfffff00 +gradient_appendStop 1 ef00ff00 + +gradient_setSpread PadSpread +gradient_setLinear 10 10 90 90 +drawEllipse 10 10 80 80 +end_block + +setCompositionMode SourceOver + +begin_block postdraw + +gradient_clearStops +gradient_appendStop 0 afff0000 +gradient_appendStop 0.5 cf0000ff +gradient_appendStop 1 bf00ff00 + +gradient_setSpread PadSpread +gradient_setLinear 0 0 100 0 +drawEllipse 10 10 30 30 +drawEllipse 10 60 30 30 +drawEllipse 60 60 30 30 +drawEllipse 60 10 30 30 +drawEllipse 35 35 30 30 + +# a black rectangle around +setCompositionMode SourceOver +setPen black +setBrush nobrush +drawRect 0.5 0.5 99 99 + +end_block +surface_end + +# Destination over +surface_begin 100 0 100 100 +repeat_block predraw +setCompositionMode DestinationOver +repeat_block postdraw +surface_end + + +# Clear +surface_begin 200 0 100 100 +repeat_block predraw +setCompositionMode Clear +repeat_block postdraw +surface_end + + +# Source +surface_begin 300 0 100 100 +repeat_block predraw +setCompositionMode Source +repeat_block postdraw +surface_end + + +# Destination +surface_begin 400 0 100 100 +repeat_block predraw +setCompositionMode Destination +repeat_block postdraw +surface_end + + +# Source In +surface_begin 500 0 100 100 +repeat_block predraw +setCompositionMode SourceIn +repeat_block postdraw +surface_end + +translate 0 50 + +# Destination In +surface_begin 0 100 100 100 +repeat_block predraw +setCompositionMode DestinationIn +repeat_block postdraw +surface_end + + +# Source Out +surface_begin 100 100 100 100 +repeat_block predraw +setCompositionMode SourceOut +repeat_block postdraw +surface_end + + +# Destination Out +surface_begin 200 100 100 100 +repeat_block predraw +setCompositionMode DestinationOut +repeat_block postdraw +surface_end + + +# SourceAtop +surface_begin 300 100 100 100 +repeat_block predraw +setCompositionMode SourceAtop +repeat_block postdraw +surface_end + + +# DestinationAtop +surface_begin 400 100 100 100 +repeat_block predraw +setCompositionMode DestinationAtop +repeat_block postdraw +surface_end + + +# Xor +surface_begin 500 100 100 100 +repeat_block predraw +setCompositionMode Xor +repeat_block postdraw +surface_end + +translate 0 50 + +# Plus +surface_begin 0 200 100 100 +repeat_block predraw +setCompositionMode Plus +repeat_block postdraw +surface_end + + +# Multiply +surface_begin 100 200 100 100 +repeat_block predraw +setCompositionMode Multiply +repeat_block postdraw +surface_end + + +# Screen +surface_begin 200 200 100 100 +repeat_block predraw +setCompositionMode Screen +repeat_block postdraw +surface_end + + +# Overlay +surface_begin 300 200 100 100 +repeat_block predraw +setCompositionMode Overlay +repeat_block postdraw +surface_end + + +# Darken +surface_begin 400 200 100 100 +repeat_block predraw +setCompositionMode Darken +repeat_block postdraw +surface_end + + +# Lighten +surface_begin 500 200 100 100 +repeat_block predraw +setCompositionMode Lighten +repeat_block postdraw +surface_end + +translate 0 50 + +# ColorDodge +surface_begin 0 300 100 100 +repeat_block predraw +setCompositionMode ColorDodge +repeat_block postdraw +surface_end + + +# Multiply +surface_begin 100 300 100 100 +repeat_block predraw +setCompositionMode ColorBurn +repeat_block postdraw +surface_end + + +# Screen +surface_begin 200 300 100 100 +repeat_block predraw +setCompositionMode HardLight +repeat_block postdraw +surface_end + + +# Overlay +surface_begin 300 300 100 100 +repeat_block predraw +setCompositionMode SoftLight +repeat_block postdraw +surface_end + + +# Darken +surface_begin 400 300 100 100 +repeat_block predraw +setCompositionMode Difference +repeat_block postdraw +surface_end + + +# Lighten +surface_begin 500 300 100 100 +repeat_block predraw +setCompositionMode Exclusion +repeat_block postdraw +surface_end + +resetMatrix + +drawText 0 50 "SourceOver" +drawText 100 50 "DestinationOver" +drawText 200 50 "Clear" +drawText 300 50 "Source" +drawText 400 50 "Destination" +drawText 500 50 "SourceIn" +drawText 0 200 "DestinationIn" +drawText 100 200 "SourceOut" +drawText 200 200 "DestinationOut" +drawText 300 200 "SourceAtop" +drawText 400 200 "DestinationAtop" +drawText 500 200 "Xor" +drawText 0 350 "Plus" +drawText 100 350 "Multiply" +drawText 200 350 "Screen" +drawText 300 350 "Overlay" +drawText 400 350 "Darken" +drawText 500 350 "Lighten" +drawText 0 500 "ColorDodge" +drawText 100 500 "ColorBurn" +drawText 200 500 "HardLight" +drawText 300 500 "SoftLight" +drawText 400 500 "Difference" +drawText 500 500 "Exclusion"
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/primitives.qps b/tests/auto/integrationtests/lancelot/scripts/primitives.qps new file mode 100644 index 0000000000..f44ba27566 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/primitives.qps @@ -0,0 +1,184 @@ +# Version: 1#Version: 1 +# CheckVsReference: 5% + + +# CheckVsReference: 5% + +setBrush #ff7f7fff +setPen black 1 solidline +translate 20 20 +begin_block testblock +save +drawRect 0 0 10 10 +drawRect 20 0 20 10 +drawRect 0 20 10 20 +drawRect 20 20 20 20 +translate 50 0 +setPen NoPen +drawRect 0 0 10 10 +drawRect 20 0 20 10 +drawRect 0 20 10 20 +drawRect 20 20 20 20 +restore +save +translate 0 50 +drawEllipse 0 0 10 10 +drawEllipse 20 0 20 10 +drawEllipse 0 20 10 20 +drawEllipse 20 20 20 20 +translate 50 0 +setPen NoPen +drawEllipse 0 0 10 10 +drawEllipse 20 0 20 10 +drawEllipse 0 20 10 20 +drawEllipse 20 20 20 20 +restore +save +translate 0 100 +drawPolygon [ 0 0 30 0 30 30 10 30 10 10 40 10 40 40 0 40 ] +save +translate 0 50 +drawPolygon [ 0 0 30 0 30 30 10 30 10 10 40 10 40 40 0 40 ] Winding +translate 0 45 +drawPolyline [ 0 0 50 0 25 25 ] +restore +setPen NoPen +translate 50 0 +drawPolygon [ 0 0 30 0 30 30 10 30 10 10 40 10 40 40 0 40 ] +save +translate 0 50 +drawPolygon [ 0 0 30 0 30 30 10 30 10 10 40 10 40 40 0 40 ] Winding +restore +save +translate -20 100 +drawPie 0 0 50 50 0 1500 +restore +restore +end_block +setPen black 1 SolidLine FlatCap +translate 200 0 +scale 2 1 +rotate 10 +repeat_block testblock +resetMatrix +translate 0 250 +setBrush 7f7f7fff +translate 20 20 +repeat_block testblock +setPen black 1 SolidLine FlatCap +translate 200 0 +scale 2 1 +rotate 10 +repeat_block testblock +resetMatrix +save +setRenderHint LineAntialiasing +setBrush 7f7fff +translate 20 500 +repeat_block testblock +translate 200 0 +scale 2 1 +rotate 10 +repeat_block testblock +restore +setRenderHint LineAntialiasing false +translate 420 20 +begin_block lines +drawLine 0 0 100 0 +drawLine 0 0 100 10 +drawLine 0 0 100 20 +drawLine 0 0 100 30 +drawLine 0 0 100 40 +drawLine 0 0 100 50 +drawLine 0 0 100 60 +drawLine 0 0 100 70 +drawLine 0 0 100 80 +drawLine 0 0 100 90 +drawLine 0 0 100 100 +drawLine 0 0 90 100 +drawLine 0 0 80 100 +drawLine 0 0 70 100 +drawLine 0 0 60 100 +drawLine 0 0 50 100 +drawLine 0 0 40 100 +drawLine 0 0 30 100 +drawLine 0 0 20 100 +drawLine 0 0 10 100 +drawLine 0 0 0 100 +end_block +setRenderHint LineAntialiasing +translate 0 120 +repeat_block lines +translate 0 120 +scale 5 2 +repeat_block lines +resetMatrix +translate 420 500 +begin_block roundedrects +save +drawRoundedRect 0 0 50 30 5 5 +translate 60 0 +drawRoundedRect 0 0 50 30 7.5 7.5 +translate 60 0 +drawRoundedRect 0 0 50 30 10 10 +translate 60 0 +drawRoundedRect 0 0 50 30 12.5 12.5 +translate 60 0 +drawRoundedRect 0 0 50 30 15 15 +restore +save +translate 0 40 +drawRoundedRect 0 0 50 30 20 20 RelativeSize +translate 60 0 +drawRoundedRect 0 0 50 30 40 40 RelativeSize +translate 60 0 +drawRoundedRect 0 0 50 30 60 60 RelativeSize +translate 60 0 +drawRoundedRect 0 0 50 30 80 80 RelativeSize +translate 60 0 +drawRoundedRect 0 0 50 30 100 100 RelativeSize +restore +end_block +translate 0.5 80.5 +repeat_block roundedrects +translate -0.5 79.5 +setRenderHint Antialiasing off +repeat_block roundedrects +resetMatrix +setRenderHint Antialiasing off +setPen black 1 +begin_block drawShapes +translate 550.5 25 +rotate 45 +setBrush nobrush +drawEllipse -10 -10 20 20 +drawLine 10 0 50 0 +drawRect 50 -7 14 14 +resetMatrix +end_block +setPen black 2 +translate 25 0 +repeat_block drawShapes +setPen black 3 +translate 50 0 +repeat_block drawShapes +setPen black 4 +translate 75 0 +repeat_block drawShapes +resetMatrix +setRenderHint Antialiasing off +setPen nopen +translate 550 100 +setBrush #7f7f7fff +drawRect -0.5 -0.5 21 21 +setBrush red +drawEllipse 0 0 20 20 +setBrush nobrush +setPen black +drawEllipse 0 0 20 20 +translate 25 0 +setPen nopen +setBrush #7f7f7fff +drawRect 0 0 20 20 +setBrush red +drawEllipse 0 0 20 20
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/radial_gradients.qps b/tests/auto/integrationtests/lancelot/scripts/radial_gradients.qps new file mode 100644 index 0000000000..b55df8bde6 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/radial_gradients.qps @@ -0,0 +1,99 @@ +# Version: 1 +# CheckVsReference: 5% (0 0 600 400) + +path_addRect path 400 0 80 80 +path_addEllipse path 440 40 60 60 + +setRenderHint Antialiasing + +setPen black + +begin_block gradients +gradient_clearStops +gradient_appendStop 0 red +gradient_appendStop 0.25 orange +gradient_appendStop 0.5 yellow +gradient_appendStop 0.8 green +gradient_appendStop 1 cyan + +gradient_setSpread PadSpread +gradient_setRadial 20 20 50 40 40 +drawRect 0 0 100 100 + +gradient_setSpread ReflectSpread +gradient_setRadial 120 20 50 140 40 +drawEllipse 100 0 100 100 + +gradient_setSpread RepeatSpread +gradient_setRadial 220 20 50 240 40 +drawRoundRect 200 0 100 100 + +gradient_clearStops +gradient_appendStop 0 3f7f7fff +gradient_appendStop 0.5 dfdfffff +gradient_appendStop 1 7f00007f + +gradient_setSpread PadSpread +gradient_setRadial 320 20 50 340 40 +drawPolygon [300 0 390 0 350 99] + +gradient_setSpread ReflectSpread +gradient_setRadial 420 20 50 440 40 +drawPath path + +gradient_setSpread RepeatSpread +gradient_setRadial 520 20 50 540 40 +drawPie 500 0 100 100 720 4320 +end_block + +translate 0 100 +scale 1 2 +repeat_block gradients + +resetMatrix +translate 0 300 +brushTranslate 30 0 +brushScale 0.9 0.9 +brushRotate 20 +repeat_block gradients + +# Some helpful info perhaps? +resetMatrix +setPen black + +drawText 610 50 "No XForm" +drawText 610 200 "scale 1x2" +drawText 610 300 "brush transform" +drawText 10 450 "Pad" +drawText 110 450 "Reflect" +drawText 210 450 "Repeat" +drawText 310 450 "Pad w/alpha" +drawText 410 450 "Reflect w/alpha" +drawText 510 450 "Repeat w/alpha" + +# Radius and focal indicators +setPen 3f000000 +setBrush nobrush + +begin_block ellipse_draw +setClipRect 0 0 100 100 +drawEllipse -30 -30 100 100 +drawEllipse 35 35 11 11 +translate 100 0 +end_block + +repeat_block ellipse_draw +repeat_block ellipse_draw +repeat_block ellipse_draw +repeat_block ellipse_draw +repeat_block ellipse_draw + +resetMatrix +translate 0 100 +scale 1 2 +repeat_block ellipse_draw +repeat_block ellipse_draw +repeat_block ellipse_draw +repeat_block ellipse_draw +repeat_block ellipse_draw +repeat_block ellipse_draw
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/radial_gradients_perspectives.qps b/tests/auto/integrationtests/lancelot/scripts/radial_gradients_perspectives.qps new file mode 100644 index 0000000000..4557354dce --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/radial_gradients_perspectives.qps @@ -0,0 +1,62 @@ +# Version: 1 + + +setRenderHint Antialiasing + +setPen #00ff00 + +translate 10 10 +# standard draw +begin_block gradient +gradient_clearStops +gradient_appendStop 0 red +gradient_appendStop 0.25 orange +gradient_appendStop 0.5 yellow +gradient_appendStop 0.8 green +gradient_appendStop 1 cyan + +gradient_setSpread PadSpread +gradient_setRadial 110 100 230 230 240 +drawRect 0 0 300 300 +end_block gradient + +# Rotation w/o smooth xform +save +translate 350 0 +save + setRenderHint SmoothPixmapTransform on + mapQuadToQuad 0 0 0 300 300 300 300 0 0 50 0 200 300 300 300 0 + repeat_block gradient +restore +restore + +translate 0 320 + +save + setRenderHint SmoothPixmapTransform on + mapQuadToQuad 0 0 0 300 300 300 300 0 0 0 100 300 200 300 300 0 + repeat_block gradient +restore + +save +translate 350 0 +save + setRenderHint SmoothPixmapTransform on + mapQuadToQuad 0 0 0 300 300 300 300 0 0 0 0 300 300 250 300 50 + repeat_block gradient +restore +restore + + +resetMatrix +setPen black +translate 125 20 +drawText 0 0 "No transform" +translate 350 0 +drawText 0 0 "Left Tilted" +resetMatrix +translate 125 350 +drawText 0 0 "Bottom Tilted" +translate 350 0 +drawText 0 0 "Right Tilted" +translate 120 0
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/rasterops.qps b/tests/auto/integrationtests/lancelot/scripts/rasterops.qps new file mode 100644 index 0000000000..21f943b543 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/rasterops.qps @@ -0,0 +1,87 @@ +# Version: 1 +# CheckVsReference: 5% + +setPen NoPen + +setBrush black +drawRect 10 10 60 500 + +setCompositionMode SourceOrDestination +translate 20 20 + +begin_block drawShape + setBrush 0xffff0000 + drawEllipse 5 5 30 30 + setBrush 0xff00ff00 + drawRect 0 0 20 20 + setBrush 0xff0000ff + drawRect 20 20 20 20 +end_block + +begin_block loop + setCompositionMode SourceAndDestination + translate 0 50 +repeat_block drawShape + +setCompositionMode SourceXorDestination +translate 0 50 +repeat_block drawShape + +setCompositionMode NotSourceAndNotDestination +translate 0 50 +repeat_block drawShape + +setCompositionMode NotSourceOrNotDestination +translate 0 50 +repeat_block drawShape + +setCompositionMode NotSourceXorDestination +translate 0 50 +repeat_block drawShape + +setCompositionMode NotSource +translate 0 50 +repeat_block drawShape + +setCompositionMode NotSourceAndDestination +translate 0 50 +repeat_block drawShape + +setCompositionMode SourceAndNotDestination +translate 0 50 +repeat_block drawShape +end_block + +resetMatrix +setCompositionMode Source +setBrush white +drawRect 100 10 60 500 +translate 110 20 +repeat_block loop + +resetMatrix +setCompositionMode Source +translate 190 20 +repeat_block loop + +resetMatrix +setPen black +setCompositionMode SourceOver +translate 250 45 +drawText 20 0 "Or ROP" +translate 0 50 +drawText 20 0 "And ROP" +translate 0 50 +drawText 20 0 "Xor ROP" +translate 0 50 +drawText 20 0 "Nor ROP" +translate 0 50 +drawText 20 0 "Nand ROP" +translate 0 50 +drawText 0 0 "NSrcXorDst ROP" +translate 0 50 +drawText 20 0 "NSrc ROP" +translate 0 50 +drawText 0 0 "NSrcAndDst ROP" +translate 0 50 +drawText 0 0 "SrcAndNDst ROP"
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/sizes.qps b/tests/auto/integrationtests/lancelot/scripts/sizes.qps new file mode 100644 index 0000000000..68e01c3262 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/sizes.qps @@ -0,0 +1,90 @@ +# Version: 1 +# CheckVsReference: 5% + +setPen NoPen +setBrush black + +translate 10 10 + +begin_block testblock +drawRect 0 0 10 10 +drawRect 20 0 11 11 +drawRect 40 0 12 12 +drawRect 60 0 13 13 +drawRect 80 0 14 14 +drawRect 100 0 15 15 +drawRect 120 0 16 16 +drawRect 140 0 17 17 +drawRect 160 0 18 18 +drawRect 180 0 19 19 + +drawEllipse 0 20 10 10 +drawEllipse 20 20 11 11 +drawEllipse 40 20 12 12 +drawEllipse 60 20 13 13 +drawEllipse 80 20 14 14 +drawEllipse 100 20 15 15 +drawEllipse 120 20 16 16 +drawEllipse 140 20 17 17 +drawEllipse 160 20 18 18 +drawEllipse 180 20 19 19 + +drawRoundRect 0 40 10 10 +drawRoundRect 20 40 11 11 +drawRoundRect 40 40 12 12 +drawRoundRect 60 40 13 13 +drawRoundRect 80 40 14 14 +drawRoundRect 100 40 15 15 +drawRoundRect 120 40 16 16 +drawRoundRect 140 40 17 17 +drawRoundRect 160 40 18 18 +drawRoundRect 180 40 19 19 + +drawPie 0 60 10 10 0 4320 +drawPie 20 60 11 11 0 4320 +drawPie 40 60 12 12 0 4320 +drawPie 60 60 13 13 0 4320 +drawPie 80 60 14 14 0 4320 +drawPie 100 60 15 15 0 4320 +drawPie 120 60 16 16 0 4320 +drawPie 140 60 17 17 0 4320 +drawPie 160 60 18 18 0 4320 +drawPie 180 60 19 19 0 4320 + +drawArc 0 80 10 10 0 4320 +drawArc 20 80 11 11 0 4320 +drawArc 40 80 12 12 0 4320 +drawArc 60 80 13 13 0 4320 +drawArc 80 80 14 14 0 4320 +drawArc 100 80 15 15 0 4320 +drawArc 120 80 16 16 0 4320 +drawArc 140 80 17 17 0 4320 +drawArc 160 80 18 18 0 4320 +drawArc 180 80 19 19 0 4320 + +drawChord 0 100 10 10 0 4320 +drawChord 20 100 11 11 0 4320 +drawChord 40 100 12 12 0 4320 +drawChord 60 100 13 13 0 4320 +drawChord 80 100 14 14 0 4320 +drawChord 100 100 15 15 0 4320 +drawChord 120 100 16 16 0 4320 +drawChord 140 100 17 17 0 4320 +drawChord 160 100 18 18 0 4320 +drawChord 180 100 19 19 0 4320 + +end_block + +setPen red +translate 0 150 +repeat_block testblock + +setRenderHint LineAntialiasing + +setPen nopen +translate 0 150 +repeat_block testblock + +setPen red +translate 0 150 +repeat_block testblock diff --git a/tests/auto/integrationtests/lancelot/scripts/statictext.qps b/tests/auto/integrationtests/lancelot/scripts/statictext.qps new file mode 100644 index 0000000000..b62b623462 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/statictext.qps @@ -0,0 +1,122 @@ +drawStaticText -5 5 "Text that is drawn outside the bounds..." + +translate 20 20 +begin_block text_drawing +save + setFont "sansserif" 10 normal + drawStaticText 0 20 "sansserif 10pt, normal" + + setFont "sansserif" 12 normal + drawStaticText 0 40 "sansserif 12pt, normal" + + setFont "sansserif" 10 bold + drawStaticText 0 60 "sansserif 12pt, bold" + + setFont "sansserif" 10 bold italic + drawStaticText 0 80 "sansserif 10pt, bold italic" + + + translate 0 100 + setPen #7fff0000 + + setFont "sansserif" 10 normal + drawStaticText 0 20 "alpha sansserif 10pt, normal" + + setFont "sansserif" 12 normal + drawStaticText 0 40 "alpha sansserif 12pt, normal" + + setFont "sansserif" 10 bold + drawStaticText 0 60 "alpha sansserif 12pt, bold" + + setFont "sansserif" 10 bold italic + drawStaticText 0 80 "alpha sansserif 10pt, bold italic" + + + translate 0 100 + setPen black + save + scale 0.9 0.9 + + setFont "sansserif" 10 normal + drawStaticText 0 20 "scaled sansserif 10pt, normal" + + setFont "sansserif" 12 normal + drawStaticText 0 40 "scaled sansserif 12pt, normal" + + setFont "sansserif" 10 bold + drawStaticText 0 60 "scaled sansserif 12pt, bold" + + setFont "sansserif" 10 bold italic + drawStaticText 0 80 "scaled sansserif 10pt, bold italic" + restore + + translate 0 100 + setPen black + save + translate 200 90 + rotate 185 + + setFont "sansserif" 10 normal + drawStaticText 0 20 "scaled sansserif 10pt, normal" + + setFont "sansserif" 12 normal + drawStaticText 0 40 "scaled sansserif 12pt, normal" + + setFont "sansserif" 10 bold + drawStaticText 0 60 "scaled sansserif 12pt, bold" + + setFont "sansserif" 10 bold italic + drawStaticText 0 80 "scaled sansserif 10pt, bold italic" + restore + + translate 0 100 + gradient_appendStop 0 red + gradient_appendStop 0.5 #00ff00 + gradient_appendStop 1 blue + gradient_setLinear 0 0 200 0 + setPen brush + + setFont "sansserif" 10 normal + drawStaticText 0 0 "gradient sansserif 10pt, normal" + + setFont "sansserif" 12 normal + drawStaticText 0 20 "gradient sansserif 12pt, normal" + + setFont "sansserif" 10 bold + drawStaticText 0 40 "gradient sansserif 12pt, bold" + + setFont "sansserif" 10 bold italic + drawStaticText 0 60 "gradient sansserif 10pt, bold italic" +restore +end_block + +translate 250 0 +drawStaticText 25 520 "clipped to rectangle" +save + setPen #3f000000 + setBrush nobrush + drawRect 20 0 100 500 + setClipRect 20 0 100 500 + setPen black + repeat_block text_drawing +restore + +translate 150 0 +drawStaticText 25 520 "clipped to path" +save + path_moveTo clip 20 0 + path_cubicTo clip 0 200 40 400 20 400 + path_lineTo clip 30 500 + path_lineTo clip 30 0 + path_lineTo clip 40 0 + path_lineTo clip 40 500 + path_lineTo clip 120 500 + path_lineTo clip 120 0 + path_lineTo clip 20 0 + setPen #3f000000 + setBrush nobrush + drawPath clip + setClipPath clip + setPen black + repeat_block text_drawing +restore diff --git a/tests/auto/integrationtests/lancelot/scripts/text.qps b/tests/auto/integrationtests/lancelot/scripts/text.qps new file mode 100644 index 0000000000..d7ee83290b --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/text.qps @@ -0,0 +1,124 @@ +# Version: 1 + +drawText -5 5 "Text that is drawn outside the bounds..." + +translate 20 20 +begin_block text_drawing +save + setFont "sansserif" 10 normal + drawText 0 20 "sansserif 10pt, normal" + + setFont "sansserif" 12 normal + drawText 0 40 "sansserif 12pt, normal" + + setFont "sansserif" 10 bold + drawText 0 60 "sansserif 12pt, bold" + + setFont "sansserif" 10 bold italic + drawText 0 80 "sansserif 10pt, bold italic" + + + translate 0 100 + setPen #7fff0000 + + setFont "sansserif" 10 normal + drawText 0 20 "alpha sansserif 10pt, normal" + + setFont "sansserif" 12 normal + drawText 0 40 "alpha sansserif 12pt, normal" + + setFont "sansserif" 10 bold + drawText 0 60 "alpha sansserif 12pt, bold" + + setFont "sansserif" 10 bold italic + drawText 0 80 "alpha sansserif 10pt, bold italic" + + + translate 0 100 + setPen black + save + scale 0.9 0.9 + + setFont "sansserif" 10 normal + drawText 0 20 "scaled sansserif 10pt, normal" + + setFont "sansserif" 12 normal + drawText 0 40 "scaled sansserif 12pt, normal" + + setFont "sansserif" 10 bold + drawText 0 60 "scaled sansserif 12pt, bold" + + setFont "sansserif" 10 bold italic + drawText 0 80 "scaled sansserif 10pt, bold italic" + restore + + translate 0 100 + setPen black + save + translate 200 90 + rotate 185 + + setFont "sansserif" 10 normal + drawText 0 20 "scaled sansserif 10pt, normal" + + setFont "sansserif" 12 normal + drawText 0 40 "scaled sansserif 12pt, normal" + + setFont "sansserif" 10 bold + drawText 0 60 "scaled sansserif 12pt, bold" + + setFont "sansserif" 10 bold italic + drawText 0 80 "scaled sansserif 10pt, bold italic" + restore + + translate 0 100 + gradient_appendStop 0 red + gradient_appendStop 0.5 #00ff00 + gradient_appendStop 1 blue + gradient_setLinear 0 0 200 0 + setPen brush + + setFont "sansserif" 10 normal + drawText 0 0 "gradient sansserif 10pt, normal" + + setFont "sansserif" 12 normal + drawText 0 20 "gradient sansserif 12pt, normal" + + setFont "sansserif" 10 bold + drawText 0 40 "gradient sansserif 12pt, bold" + + setFont "sansserif" 10 bold italic + drawText 0 60 "gradient sansserif 10pt, bold italic" +restore +end_block + +translate 250 0 +drawText 25 520 "clipped to rectangle" +save + setPen #3f000000 + setBrush nobrush + drawRect 20 0 100 500 + setClipRect 20 0 100 500 + setPen black + repeat_block text_drawing +restore + +translate 150 0 +drawText 25 520 "clipped to path" +save + path_moveTo clip 20 0 + path_cubicTo clip 0 200 40 400 20 400 + path_lineTo clip 30 500 + path_lineTo clip 30 0 + path_lineTo clip 40 0 + path_lineTo clip 40 500 + path_lineTo clip 120 500 + path_lineTo clip 120 0 + path_lineTo clip 20 0 + setPen #3f000000 + setBrush nobrush + drawPath clip + setClipPath clip + setPen black + repeat_block text_drawing +restore
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/text_perspectives.qps b/tests/auto/integrationtests/lancelot/scripts/text_perspectives.qps new file mode 100644 index 0000000000..4c74306265 --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/text_perspectives.qps @@ -0,0 +1,102 @@ +# Version: 1 + + +setRenderHint Antialiasing + +setPen black + +translate 10 10 +# standard draw +begin_block text +setBrush gray +drawRect 0 0 300 300 + +setFont "times" 3 +drawText 10 10 "Hello World...." + +setFont "times" 4 +drawText 10 20 "Hello World...." + +setFont "times" 5 +drawText 10 30 "Hello World...." + +setFont "times" 6 +drawText 10 40 "Hello World...." + +setFont "times" 7 +drawText 10 50 "Hello World...." + +setFont "times" 8 +drawText 10 60 "Hello World...." + +setFont "times" 9 +drawText 10 70 "Hello World...." + +setFont "times" 10 +drawText 10 80 "Hello World...." + +setFont "times" 16 +drawText 10 100 "Hello World...." + +setFont "times" 17 +drawText 10 120 "Hello World...." + +setFont "times" 18 +drawText 10 140 "Hello World...." + +setFont "times" 20 +drawText 10 160 "Hello World...." + +setFont "times" 22 +drawText 10 180 "Hello World...." + +setFont "times" 24 +drawText 10 205 "Hello World...." + +setFont "times" 26 +drawText 10 230 "Hello World...." + +setFont "times" 32 +drawText 10 260 "Hello World...." +end_block text + +# Rotation w/o smooth xform +save +translate 350 0 +save + setRenderHint SmoothPixmapTransform on + mapQuadToQuad 0 0 0 300 300 300 300 0 0 50 0 200 300 300 300 0 + repeat_block text +restore +restore + +translate 0 320 + +save + setRenderHint SmoothPixmapTransform on + mapQuadToQuad 0 0 0 300 300 300 300 0 0 0 100 300 200 300 300 0 + repeat_block text +restore + +save +translate 350 0 +save + setRenderHint SmoothPixmapTransform on + mapQuadToQuad 0 0 0 300 300 300 300 0 0 0 0 300 300 250 300 50 + repeat_block text +restore +restore + + +resetMatrix +setPen black +translate 125 20 +drawText 0 0 "No transform" +translate 350 0 +drawText 0 0 "Left Tilted" +resetMatrix +translate 125 350 +drawText 0 0 "Bottom Tilted" +translate 350 0 +drawText 0 0 "Right Tilted" +translate 120 0
\ No newline at end of file diff --git a/tests/auto/integrationtests/lancelot/scripts/tiled_pixmap.qps b/tests/auto/integrationtests/lancelot/scripts/tiled_pixmap.qps new file mode 100644 index 0000000000..9cb5e0dd1f --- /dev/null +++ b/tests/auto/integrationtests/lancelot/scripts/tiled_pixmap.qps @@ -0,0 +1,84 @@ +# Version: 1 +# CheckVsReference: 5% (0 0 639 638) + + +translate 0 10 +setRenderHint Antialiasing + +pixmap_load dome_argb32 the_pixmap + +begin_block draw_stuff +save + + # Standard draw + drawTiledPixmap the_pixmap 0 0 150 100 0 0 + + # Standard draw with offset + translate 160 0 + drawTiledPixmap the_pixmap 0 0 150 100 25 25 + + # xformed + translate 160 0 + save + translate 10 -10 + rotate 10 + setRenderHint SmoothPixmapTransform false + drawTiledPixmap the_pixmap 0 0 150 100 25 25 + restore + + # xformed with smooth xform + translate 160 0 + save + translate 10 -10 + rotate 10 + setRenderHint SmoothPixmapTransform + drawTiledPixmap the_pixmap 0 0 150 100 25 25 + restore +restore +end_block + +translate 0 120 +pixmap_load dome_rgb32 the_pixmap +repeat_block draw_stuff + + +translate 0 120 +pixmap_load dome_indexed the_pixmap +repeat_block draw_stuff + + +translate 0 120 +pixmap_load dome_indexed_mask the_pixmap +repeat_block draw_stuff + + +translate 0 120 +pixmap_load dome_mono the_pixmap +repeat_block draw_stuff + + +################################################################################ +# Some helpful text... +# + +resetMatrix +translate 650 80 +drawText 0 0 "32 bit w/alpha" +translate 0 120 +drawText 0 0 "32 bit w/o alpha" +translate 0 120 +drawText 0 0 "8 bit indexed" +translate 0 120 +drawText 0 0 "8 bit indexed w/mask" +translate 0 120 +drawText 0 0 "1 bit" + +resetMatrix +translate 10 630 +drawText 0 0 "normal" +translate 160 0 +drawText 0 0 "offset" +translate 160 0 +drawText 0 0 "xformed" +translate 160 0 +drawText 0 0 "smooth xformed" diff --git a/tests/auto/integrationtests/lancelot/tst_lancelot.cpp b/tests/auto/integrationtests/lancelot/tst_lancelot.cpp new file mode 100644 index 0000000000..07ed74ec6a --- /dev/null +++ b/tests/auto/integrationtests/lancelot/tst_lancelot.cpp @@ -0,0 +1,356 @@ +/**************************************************************************** +** +** 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 "paintcommands.h" +#include <QPainter> +#include <QLibraryInfo> +#include <baselineprotocol.h> +#include <QHash> + +#ifndef QT_NO_OPENGL +#include <QtOpenGL> +#endif + +#ifndef SRCDIR +#define SRCDIR "." +#endif + +class tst_Lancelot : public QObject +{ +Q_OBJECT + +public: + tst_Lancelot(); + + static bool simfail; + static PlatformInfo clientInfo; + +private: + enum GraphicsEngine { + Raster = 0, + OpenGL = 1 + }; + + bool setupTestSuite(const QStringList& blacklist); + void runTestSuite(GraphicsEngine engine, QImage::Format format); + ImageItem render(const ImageItem &item, GraphicsEngine engine, QImage::Format format); + void paint(QPaintDevice *device, const QStringList &script, const QString &filePath); + + BaselineProtocol proto; + ImageItemList baseList; + QHash<QString, QStringList> scripts; + bool dryRunMode; + QString scriptsDir; + +private slots: + void initTestCase(); + void cleanupTestCase() {} + + void testRasterARGB32PM_data(); + void testRasterARGB32PM(); + void testRasterRGB32_data(); + void testRasterRGB32(); + void testRasterRGB16_data(); + void testRasterRGB16(); + +#ifndef QT_NO_OPENGL + void testOpenGL_data(); + void testOpenGL(); +#endif +}; + +bool tst_Lancelot::simfail = false; +PlatformInfo tst_Lancelot::clientInfo; + +tst_Lancelot::tst_Lancelot() +{ +} + +void tst_Lancelot::initTestCase() +{ + // Check and setup the environment. We treat failures because of test environment + // (e.g. script files not found) as just warnings, and not QFAILs, to avoid false negatives + // caused by environment or server instability + + if (!proto.connect(QLatin1String("tst_Lancelot"), &dryRunMode, clientInfo)) + QSKIP(qPrintable(proto.errorMessage()), SkipAll); + +#if defined(USE_RUNTIME_DIR) + scriptsDir = QCoreApplication::applicationDirPath() + "/scripts/"; +#else + scriptsDir = SRCDIR "/scripts/"; +#endif + QDir qpsDir(scriptsDir); + QStringList files = qpsDir.entryList(QStringList() << QLatin1String("*.qps"), QDir::Files | QDir::Readable); + if (files.isEmpty()) { + QWARN("No qps script files found in " + qpsDir.path().toLatin1()); + QSKIP("Aborted due to errors.", SkipAll); + } + + baseList.resize(files.count()); + ImageItemList::iterator it = baseList.begin(); + foreach(const QString& fileName, files) { + QFile file(scriptsDir + fileName); + file.open(QFile::ReadOnly); + QByteArray cont = file.readAll(); + scripts.insert(fileName, QString::fromLatin1(cont).split(QLatin1Char('\n'), QString::SkipEmptyParts)); + it->itemName = fileName; + it->itemChecksum = qChecksum(cont.constData(), cont.size()); + it++; + } +} + + +void tst_Lancelot::testRasterARGB32PM_data() +{ + QStringList localBlacklist; + if (!setupTestSuite(localBlacklist)) + QSKIP("Communication with baseline image server failed.", SkipAll); +} + + +void tst_Lancelot::testRasterARGB32PM() +{ + runTestSuite(Raster, QImage::Format_ARGB32_Premultiplied); +} + + +void tst_Lancelot::testRasterRGB32_data() +{ + QStringList localBlacklist; + if (!setupTestSuite(localBlacklist)) + QSKIP("Communication with baseline image server failed.", SkipAll); +} + + +void tst_Lancelot::testRasterRGB32() +{ + runTestSuite(Raster, QImage::Format_RGB32); +} + + +void tst_Lancelot::testRasterRGB16_data() +{ + QStringList localBlacklist; + if (!setupTestSuite(localBlacklist)) + QSKIP("Communication with baseline image server failed.", SkipAll); +} + + +void tst_Lancelot::testRasterRGB16() +{ + runTestSuite(Raster, QImage::Format_RGB16); +} + + +#ifndef QT_NO_OPENGL +void tst_Lancelot::testOpenGL_data() +{ + QStringList localBlacklist = QStringList() << QLatin1String("rasterops.qps"); + if (!setupTestSuite(localBlacklist)) + QSKIP("Communication with baseline image server failed.", SkipAll); +} + + +void tst_Lancelot::testOpenGL() +{ + bool ok = false; + QGLWidget glWidget; + if (glWidget.isValid() && glWidget.format().directRendering() + && ((QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_0) + || (QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Version_2_0)) + && QGLFramebufferObject::hasOpenGLFramebufferObjects()) + { + glWidget.makeCurrent(); + if (!QByteArray((const char *)glGetString(GL_VERSION)).contains("Mesa")) + ok = true; + } + if (ok) + runTestSuite(OpenGL, QImage::Format_RGB32); + else + QSKIP("System under test does not meet preconditions for GL testing. Skipping.", SkipAll); +} +#endif + + +bool tst_Lancelot::setupTestSuite(const QStringList& blacklist) +{ + QTest::addColumn<ImageItem>("baseline"); + + ImageItemList itemList(baseList); + if (!proto.requestBaselineChecksums(QTest::currentTestFunction(), &itemList)) { + QWARN(qPrintable(proto.errorMessage())); + return false; + } + + foreach(const ImageItem& item, itemList) { + if (!blacklist.contains(item.itemName)) + QTest::newRow(item.itemName.toLatin1()) << item; + } + return true; +} + + +void tst_Lancelot::runTestSuite(GraphicsEngine engine, QImage::Format format) +{ + QFETCH(ImageItem, baseline); + + if (baseline.status == ImageItem::IgnoreItem) + QSKIP("Blacklisted by baseline server.", SkipSingle); + + ImageItem rendered = render(baseline, engine, format); + static int consecutiveErrs = 0; + if (rendered.image.isNull()) { // Assume an error in the test environment, not Qt + QWARN("Error: Failed to render image."); + if (++consecutiveErrs < 3) { + QSKIP("Aborted due to errors.", SkipSingle); + } else { + consecutiveErrs = 0; + QSKIP("Too many errors, skipping rest of testfunction.", SkipAll); + } + } else { + consecutiveErrs = 0; + } + + + if (baseline.status == ImageItem::BaselineNotFound) { + if (!proto.submitNewBaseline(rendered, 0)) + QWARN("Failed to submit new baseline: " + proto.errorMessage().toLatin1()); + QSKIP("Baseline not found; new baseline created.", SkipSingle); + } + + if (!baseline.imageChecksums.contains(rendered.imageChecksums.at(0))) { + QByteArray serverMsg; + if (!proto.submitMismatch(rendered, &serverMsg)) + serverMsg = "Failed to submit mismatching image to server."; + if (dryRunMode) + qDebug() << "Dryrun mode, ignoring detected mismatch." << serverMsg; + else + QFAIL("Rendered image differs from baseline. Report:\n " + serverMsg); + } +} + + +ImageItem tst_Lancelot::render(const ImageItem &item, GraphicsEngine engine, QImage::Format format) +{ + ImageItem res = item; + res.imageChecksums.clear(); + res.image = QImage(); + QString filePath = scriptsDir + item.itemName; + QStringList script = scripts.value(item.itemName); + + if (engine == Raster) { + QImage img(800, 800, format); + paint(&img, script, QFileInfo(filePath).absoluteFilePath()); // eh yuck (filePath stuff) + res.image = img; + res.imageChecksums.append(ImageItem::computeChecksum(img)); +#ifndef QT_NO_OPENGL + } else if (engine == OpenGL) { + QGLWidget glWidget; + if (glWidget.isValid()) { + glWidget.makeCurrent(); + QGLFramebufferObjectFormat fboFormat; + fboFormat.setSamples(16); + fboFormat.setAttachment(QGLFramebufferObject::CombinedDepthStencil); + QGLFramebufferObject fbo(800, 800, fboFormat); + paint(&fbo, script, QFileInfo(filePath).absoluteFilePath()); // eh yuck (filePath stuff) + res.image = fbo.toImage().convertToFormat(format); + res.imageChecksums.append(ImageItem::computeChecksum(res.image)); + } +#endif + } + + return res; +} + +void tst_Lancelot::paint(QPaintDevice *device, const QStringList &script, const QString &filePath) +{ + QPainter p(device); + PaintCommands pcmd(script, 800, 800); + //pcmd.setShouldDrawText(false); + pcmd.setType(ImageType); + pcmd.setPainter(&p); + pcmd.setFilePath(filePath); + pcmd.runCommands(); + p.end(); + + if (simfail) { + QPainter p2(device); + p2.setPen(QPen(QBrush(Qt::cyan), 3, Qt::DashLine)); + p2.drawLine(200, 200, 600, 600); + p2.drawLine(600, 200, 200, 600); + simfail = false; + } +} + +#define main rmain +QTEST_MAIN(tst_Lancelot) +#undef main + +int main(int argc, char *argv[]) +{ + tst_Lancelot::clientInfo = PlatformInfo::localHostInfo(); + + char *fargv[20]; + int fargc = 0; + for (int i = 0; i < qMin(argc, 19); i++) { + if (!qstrcmp(argv[i], "-simfail")) { + tst_Lancelot::simfail = true; + } else if (!qstrcmp(argv[i], "-compareto") && i < argc-1) { + QString arg = QString::fromLocal8Bit(argv[++i]); + int split = arg.indexOf(QLC('=')); + if (split < 0) + continue; + QString key = arg.left(split).trimmed(); + QString value = arg.mid(split+1).trimmed(); + if (key.isEmpty() || value.isEmpty()) + continue; + tst_Lancelot::clientInfo.addOverride(key, value); + } else { + fargv[fargc++] = argv[i]; + } + } + fargv[fargc] = 0; + return rmain(fargc, fargv); +} + +#include "tst_lancelot.moc" diff --git a/tests/auto/integrationtests/languagechange/.gitignore b/tests/auto/integrationtests/languagechange/.gitignore new file mode 100644 index 0000000000..1b14ad1503 --- /dev/null +++ b/tests/auto/integrationtests/languagechange/.gitignore @@ -0,0 +1 @@ +tst_languagechange diff --git a/tests/auto/integrationtests/languagechange/languagechange.pro b/tests/auto/integrationtests/languagechange/languagechange.pro new file mode 100644 index 0000000000..c51c42ebe1 --- /dev/null +++ b/tests/auto/integrationtests/languagechange/languagechange.pro @@ -0,0 +1,5 @@ +load(qttest_p4) +QT += widgets core-private +SOURCES += tst_languagechange.cpp + +CONFIG += insignificant_test # QTBUG-21402 diff --git a/tests/auto/integrationtests/languagechange/tst_languagechange.cpp b/tests/auto/integrationtests/languagechange/tst_languagechange.cpp new file mode 100644 index 0000000000..5e97fd709e --- /dev/null +++ b/tests/auto/integrationtests/languagechange/tst_languagechange.cpp @@ -0,0 +1,286 @@ +/**************************************************************************** +** +** 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 <qapplication.h> +#include <QtCore/QSet> +#include <QtCore/QTranslator> +#include <private/qthread_p.h> +#include <QtWidgets/QInputDialog> +#include <QtWidgets/QColorDialog> +#include <QtWidgets/QFileDialog> +#include <QtWidgets/QDesktopWidget> + + +//TESTED_CLASS= +//TESTED_FILES= + +class tst_languageChange : public QObject +{ + Q_OBJECT +public: + tst_languageChange(); + +public slots: + void initTestCase(); + void cleanupTestCase(); +private slots: + void retranslatability_data(); + void retranslatability(); + +}; + + +tst_languageChange::tst_languageChange() + +{ +} + +void tst_languageChange::initTestCase() +{ +} + +void tst_languageChange::cleanupTestCase() +{ +} +/** + * Records all calls to translate() + */ +class TransformTranslator : public QTranslator +{ + Q_OBJECT +public: + TransformTranslator() : QTranslator() {} + TransformTranslator(QObject *parent) : QTranslator(parent) {} + virtual QString translate(const char *context, const char *sourceText, const char *comment = 0) const + { + QByteArray total(context); + total.append("::"); + total.append(sourceText); + if (comment) { + total.append("::"); + total.append(comment); + } + m_translations.insert(total); + QString res; + for (int i = 0; i < int(qstrlen(sourceText)); ++i) { + QChar ch = QLatin1Char(sourceText[i]); + if (ch.isLower()) { + res.append(ch.toUpper()); + } else if (ch.isUpper()) { + res.append(ch.toLower()); + } else { + res.append(ch); + } + } + return res; + } + + virtual bool isEmpty() const { return false; } + +public slots: + void install() { + QCoreApplication::installTranslator(this); + QTest::qWait(2500); + QApplication::closeAllWindows(); + } +public: + mutable QSet<QByteArray> m_translations; +}; + +enum DialogType { + InputDialog = 1, + ColorDialog, + FileDialog +}; + +typedef QSet<QByteArray> TranslationSet; +Q_DECLARE_METATYPE(TranslationSet) + +void tst_languageChange::retranslatability_data() +{ + QTest::addColumn<int>("dialogType"); + QTest::addColumn<TranslationSet >("expected"); + + //next we fill it with data + QTest::newRow( "QInputDialog" ) + << int(InputDialog) << (QSet<QByteArray>() + << "QDialogButtonBox::Cancel"); + + QTest::newRow( "QColorDialog" ) + << int(ColorDialog) << (QSet<QByteArray>() + << "QDialogButtonBox::Cancel" + << "QColorDialog::&Sat:" + << "QColorDialog::&Add to Custom Colors" + << "QColorDialog::&Green:" + << "QColorDialog::&Red:" + << "QColorDialog::Bl&ue:" + << "QColorDialog::A&lpha channel:" + << "QColorDialog::&Basic colors" + << "QColorDialog::&Custom colors" + << "QColorDialog::&Val:" + << "QColorDialog::Hu&e:"); + + QTest::newRow( "QFileDialog" ) + << int(FileDialog) << (QSet<QByteArray>() + << "QFileDialog::All Files (*)" + << "QFileDialog::Back" + << "QFileDialog::Create New Folder" + << "QFileDialog::Detail View" +#ifndef Q_OS_MAC + << "QFileDialog::File" +#endif + << "QFileDialog::Files of type:" + << "QFileDialog::Forward" + << "QFileDialog::List View" + << "QFileDialog::Look in:" + << "QFileDialog::Open" + << "QFileDialog::Parent Directory" + << "QFileDialog::Show " + << "QFileDialog::Show &hidden files" + << "QFileDialog::&Delete" + << "QFileDialog::&New Folder" + << "QFileDialog::&Rename" + << "QFileSystemModel::Date Modified" +#ifdef Q_OS_WIN + << "QFileSystemModel::My Computer" +#else + << "QFileSystemModel::Computer" +#endif + << "QFileSystemModel::Size" +#ifdef Q_OS_MAC + << "QFileSystemModel::Kind::Match OS X Finder" +#else + << "QFileSystemModel::Type::All other platforms" +#endif +// << "QFileSystemModel::%1 KB" + << "QDialogButtonBox::Cancel" + << "QDialogButtonBox::Open" + << "QFileDialog::File &name:"); +} + +void tst_languageChange::retranslatability() +{ + QFETCH( int, dialogType); + QFETCH( TranslationSet, expected); + + // This will always be queried for when a language changes + expected.insert("QApplication::QT_LAYOUT_DIRECTION::Translate this string to the string 'LTR' in left-to-right " + "languages or to 'RTL' in right-to-left languages (such as Hebrew and Arabic) to " + "get proper widget layout."); + TransformTranslator translator; + QTimer::singleShot(500, &translator, SLOT(install())); + switch (dialogType) { + case InputDialog: + (void)QInputDialog::getInteger(0, QLatin1String("title"), QLatin1String("label")); + break; + + case ColorDialog: +#ifdef Q_WS_MAC + QSKIP("The native color dialog is used on Mac OS", SkipSingle); +#else + (void)QColorDialog::getColor(); +#endif + break; + case FileDialog: { +#ifdef Q_WS_MAC + QSKIP("The native file dialog is used on Mac OS", SkipSingle); +#endif + QFileDialog dlg; + dlg.setOption(QFileDialog::DontUseNativeDialog); + QString tmpParentDir = QDir::tempPath() + "/languagechangetestdir"; + QString tmpDir = tmpParentDir + "/finaldir"; + QString fooName = tmpParentDir + "/foo"; + QDir dir; + QCOMPARE(dir.mkpath(tmpDir), true); + QCOMPARE(QFile::copy(QApplication::applicationFilePath(), fooName), true); + + dlg.setDirectory(tmpParentDir); +#ifdef Q_OS_WINCE + dlg.setDirectory("\\Windows"); +#endif + dlg.setFileMode(QFileDialog::ExistingFiles); + dlg.setViewMode(QFileDialog::Detail); + dlg.exec(); + QTest::qWait(3000); + QCOMPARE(QFile::remove(fooName), true); + QCOMPARE(dir.rmdir(tmpDir), true); + QCOMPARE(dir.rmdir(tmpParentDir), true); + break; } + } +#if 0 + QList<QByteArray> list = translator.m_translations.toList(); + qSort(list); + qDebug() << list; +#endif + // In case we use a Color dialog, we do not want to test for + // strings non existing in the dialog and which do not get + // translated. + if ((dialogType == ColorDialog) && +#ifndef Q_OS_WINCE + (qApp->desktop()->width() < 480 || qApp->desktop()->height() < 350) +#else + true // On Qt/WinCE we always use compact mode +#endif + ) { + expected.remove("QColorDialog::&Basic colors"); + expected.remove("QColorDialog::&Custom colors"); + expected.remove("QColorDialog::&Define Custom Colors >>"); + expected.remove("QColorDialog::&Add to Custom Colors"); + } + + // see if all of our *expected* translations was translated. + // (There might be more, but thats not that bad) + QSet<QByteArray> commonTranslations = expected; + commonTranslations.intersect(translator.m_translations); + if (!expected.subtract(commonTranslations).isEmpty()) { + qDebug() << "Missing:" << expected; + if (!translator.m_translations.subtract(commonTranslations).isEmpty()) + qDebug() << "Unexpected:" << translator.m_translations; + } + + QVERIFY(expected.isEmpty()); +} + +QTEST_MAIN(tst_languageChange) +#include "tst_languagechange.moc" diff --git a/tests/auto/integrationtests/macgui/.gitignore b/tests/auto/integrationtests/macgui/.gitignore new file mode 100644 index 0000000000..2b13357fac --- /dev/null +++ b/tests/auto/integrationtests/macgui/.gitignore @@ -0,0 +1 @@ +tst_macgui diff --git a/tests/auto/integrationtests/macgui/guitest.cpp b/tests/auto/integrationtests/macgui/guitest.cpp new file mode 100644 index 0000000000..521134f6b8 --- /dev/null +++ b/tests/auto/integrationtests/macgui/guitest.cpp @@ -0,0 +1,350 @@ +/**************************************************************************** +** +** 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 "guitest.h" +#include <QDebug> +#include <QWidget> +#include <QStack> +#include <QTimer> +#include <QtTest/QtTest> + +#ifdef Q_OS_MAC +# include <private/qt_mac_p.h> +#endif + + +/* + Not really a test, just prints interface info. +*/ +class PrintTest : public TestBase +{ +public: + bool operator()(InterfaceChildPair candidate) + { + qDebug() << ""; + qDebug() << "Name" << candidate.iface->text(QAccessible::Name, candidate.possibleChild); + qDebug() << "Pos" << candidate.iface->rect(candidate.possibleChild); + if (candidate.possibleChild == 0) + qDebug() << "Number of children" << candidate.iface->childCount(); + return false; + } +}; + +class NameTest : public TestBase +{ +public: + NameTest(const QString &text, QAccessible::Text textType) : text(text), textType(textType) {} + QString text; + QAccessible::Text textType; + + bool operator()(InterfaceChildPair candidate) + { + return (candidate.iface->text(textType, candidate.possibleChild) == text); + } +}; + +void WidgetNavigator::printAll(QWidget *widget) +{ + QAccessibleInterface * const iface = QAccessible::queryAccessibleInterface(widget); + deleteInDestructor(iface); + printAll(InterfaceChildPair(iface, 0)); +} + +void WidgetNavigator::printAll(InterfaceChildPair interface) +{ + PrintTest printTest; + recursiveSearch(&printTest, interface.iface, interface.possibleChild); +} + +InterfaceChildPair WidgetNavigator::find(QAccessible::Text textType, const QString &text, QWidget *start) +{ + QAccessibleInterface * const iface = QAccessible::queryAccessibleInterface(start); + deleteInDestructor(iface); + return find(textType, text, iface); +} + +InterfaceChildPair WidgetNavigator::find(QAccessible::Text textType, const QString &text, QAccessibleInterface *start) +{ + NameTest nameTest(text, textType); + return recursiveSearch(&nameTest, start, 0); +} + +/* + Recursiveley navigates the accessible hiearchy looking for an interfafce that + passsed the Test (meaning it returns true). +*/ +InterfaceChildPair WidgetNavigator::recursiveSearch(TestBase *test, QAccessibleInterface *iface, int possibleChild) +{ + QStack<InterfaceChildPair> todoInterfaces; + todoInterfaces.push(InterfaceChildPair(iface, possibleChild)); + + while (todoInterfaces.isEmpty() == false) { + InterfaceChildPair testInterface = todoInterfaces.pop(); + + if ((*test)(testInterface)) + return testInterface; + + if (testInterface.possibleChild != 0) + continue; + + const int numChildren = testInterface.iface->childCount(); + for (int i = 0; i < numChildren; ++i) { + QAccessibleInterface *childInterface = 0; + int newPossibleChild = testInterface.iface->navigate(QAccessible::Child, i + 1, &childInterface); + if (childInterface) { + todoInterfaces.push(InterfaceChildPair(childInterface, newPossibleChild)); + deleteInDestructor(childInterface); + } else if (newPossibleChild != -1) { + todoInterfaces.push(InterfaceChildPair(testInterface.iface, newPossibleChild)); + } + } + } + return InterfaceChildPair(); +} + +void WidgetNavigator::deleteInDestructor(QAccessibleInterface * interface) +{ + interfaces.insert(interface); +} + +QWidget *WidgetNavigator::getWidget(InterfaceChildPair interface) +{ + return qobject_cast<QWidget *>(interface.iface->object()); +} + +WidgetNavigator::~WidgetNavigator() +{ + foreach(QAccessibleInterface *interface, interfaces) { + delete interface; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +namespace NativeEvents { +#ifdef Q_OS_MAC + void mouseClick(const QPoint &globalPos, Qt::MouseButtons buttons, MousePosition updateMouse) + { + CGPoint position; + position.x = globalPos.x(); + position.y = globalPos.y(); + + const bool updateMousePosition = (updateMouse == UpdatePosition); + + // Mouse down. + CGPostMouseEvent(position, updateMousePosition, 3, + (buttons & Qt::LeftButton) ? true : false, + (buttons & Qt::MidButton/* Middlebutton! */) ? true : false, + (buttons & Qt::RightButton) ? true : false); + + // Mouse up. + CGPostMouseEvent(position, updateMousePosition, 3, false, false, false); + } +#else +# error Oops, NativeEvents::mouseClick() is not implemented on this platform. +#endif +}; + +/////////////////////////////////////////////////////////////////////////////// + +GuiTester::GuiTester() +{ + clearSequence(); +} + +GuiTester::~GuiTester() +{ + foreach(DelayedAction *action, actions) + delete action; +} + +bool checkPixel(QColor pixel, QColor expected) +{ + const int allowedDiff = 20; + + return !(qAbs(pixel.red() - expected.red()) > allowedDiff || + qAbs(pixel.green() - expected.green()) > allowedDiff || + qAbs(pixel.blue() - expected.blue()) > allowedDiff); +} + +/* + Tests that the pixels inside rect in image all have the given color. +*/ +bool GuiTester::isFilled(const QImage image, const QRect &rect, const QColor &color) +{ + for (int y = rect.top(); y <= rect.bottom(); ++y) + for (int x = rect.left(); x <= rect.right(); ++x) { + const QColor pixel = image.pixel(x, y); + if (checkPixel(pixel, color) == false) { +// qDebug()<< "Wrong pixel value at" << x << y << pixel.red() << pixel.green() << pixel.blue(); + return false; + } + } + return true; +} + + +/* + Tests that stuff is painted to the pixels inside rect. + This test fails if any lines in the given direction have pixels + of only one color. +*/ +bool GuiTester::isContent(const QImage image, const QRect &rect, Directions directions) +{ + if (directions & Horizontal) { + for (int y = rect.top(); y <= rect.bottom(); ++y) { + QColor currentColor = image.pixel(rect.left(), y); + bool fullRun = true; + for (int x = rect.left() + 1; x <= rect.right(); ++x) { + if (checkPixel(image.pixel(x, y), currentColor) == false) { + fullRun = false; + break; + } + } + if (fullRun) { +// qDebug() << "Single-color line at horizontal line " << y << currentColor; + return false; + } + } + return true; + } + + if (directions & Vertical) { + for (int x = rect.left(); x <= rect.right(); ++x) { + QRgb currentColor = image.pixel(x, rect.top()); + bool fullRun = true; + for (int y = rect.top() + 1; y <= rect.bottom(); ++y) { + if (checkPixel(image.pixel(x, y), currentColor) == false) { + fullRun = false; + break; + } + } + if (fullRun) { +// qDebug() << "Single-color line at vertical line" << x << currentColor; + return false; + } + } + return true; + } + return false; // shut the compiler up. +} + +void DelayedAction::run() +{ + if (next) + QTimer::singleShot(next->delay, next, SLOT(run())); +}; + +/* + Schedules a mouse click at an interface using a singleShot timer. + Only one click can be scheduled at a time. +*/ +ClickLaterAction::ClickLaterAction(InterfaceChildPair interface, Qt::MouseButtons buttons) +{ + this->useInterface = true; + this->interface = interface; + this->buttons = buttons; +} + +/* + Schedules a mouse click at a widget using a singleShot timer. + Only one click can be scheduled at a time. +*/ +ClickLaterAction::ClickLaterAction(QWidget *widget, Qt::MouseButtons buttons) +{ + this->useInterface = false; + this->widget = widget; + this->buttons = buttons; +} + +void ClickLaterAction::run() +{ + if (useInterface) { + const QPoint globalCenter = interface.iface->rect(interface.possibleChild).center(); + NativeEvents::mouseClick(globalCenter, buttons); + } else { // use widget + const QSize halfSize = widget->size() / 2; + const QPoint globalCenter = widget->mapToGlobal(QPoint(halfSize.width(), halfSize.height())); + NativeEvents::mouseClick(globalCenter, buttons); + } + DelayedAction::run(); +} + +void GuiTester::clickLater(InterfaceChildPair interface, Qt::MouseButtons buttons, int delay) +{ + clearSequence(); + addToSequence(new ClickLaterAction(interface, buttons), delay); + runSequence(); +} + +void GuiTester::clickLater(QWidget *widget, Qt::MouseButtons buttons, int delay) +{ + clearSequence(); + addToSequence(new ClickLaterAction(widget, buttons), delay); + runSequence(); +} + +void GuiTester::clearSequence() +{ + startAction = new DelayedAction(); + actions.insert(startAction); + lastAction = startAction; +} + +void GuiTester::addToSequence(DelayedAction *action, int delay) +{ + actions.insert(action); + action->delay = delay; + lastAction->next = action; + lastAction = action; +} + +void GuiTester::runSequence() +{ + QTimer::singleShot(0, startAction, SLOT(run())); +} + +void GuiTester::exitLoopSlot() +{ + QTestEventLoop::instance().exitLoop(); +} + diff --git a/tests/auto/integrationtests/macgui/guitest.h b/tests/auto/integrationtests/macgui/guitest.h new file mode 100644 index 0000000000..432021c54a --- /dev/null +++ b/tests/auto/integrationtests/macgui/guitest.h @@ -0,0 +1,186 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ +#ifndef GUITEST_H +#define GUITEST_H + +#include <QAccessibleInterface> +#include <QSet> +#include <QWidget> +#include <QPainter> + +QT_USE_NAMESPACE + +/* + GuiTest provides tools for: + - navigating the Qt Widget hiearchy using the accessibilty APIs. + - Simulating platform mouse and keybord events. +*/ + +/* + InterfaceChildPair specifies an accessibilty interface item. +*/ +class InterfaceChildPair { +public: + InterfaceChildPair() : iface(0), possibleChild(0) {} + InterfaceChildPair(QAccessibleInterface *iface, int possibleChild) + :iface(iface), possibleChild(possibleChild) + { } + + QAccessibleInterface *iface; + int possibleChild; +}; + +class TestBase { +public: + virtual bool operator()(InterfaceChildPair candidate) = 0; + virtual ~TestBase() {} +}; + +/* + WidgetNavigator navigates a Qt GUI hierarchy using the QAccessibility APIs. +*/ +class WidgetNavigator { +public: + WidgetNavigator() {}; + ~WidgetNavigator(); + + void printAll(QWidget *widget); + void printAll(InterfaceChildPair interface); + + InterfaceChildPair find(QAccessible::Text textType, const QString &text, QWidget *start); + InterfaceChildPair find(QAccessible::Text textType, const QString &text, QAccessibleInterface *start); + + InterfaceChildPair recursiveSearch(TestBase *test, QAccessibleInterface *iface, int possibleChild); + + void deleteInDestructor(QAccessibleInterface * interface); + static QWidget *getWidget(InterfaceChildPair interface); +private: + QSet<QAccessibleInterface *> interfaces; +}; + +/* + NativeEvents contains platform-specific code for simulating mouse and keybord events. + (Implemented so far: mouseClick on Mac) +*/ +namespace NativeEvents { + enum MousePosition { UpdatePosition, DontUpdatePosition }; + /* + Simulates a mouse click with button at globalPos. + */ + void mouseClick(const QPoint &globalPos, Qt::MouseButtons buttons, MousePosition updateMouse = DontUpdatePosition); +}; + +class ColorWidget : public QWidget +{ +public: + ColorWidget(QWidget *parent = 0, QColor color = QColor(Qt::red)) + : QWidget(parent), color(color) {} + + QColor color; + +protected: + void paintEvent(QPaintEvent *) + { + QPainter p(this); + p.fillRect(this->rect(), color); + } +}; + +class DelayedAction : public QObject +{ +Q_OBJECT +public: + DelayedAction() : delay(0), next(0) {} + virtual ~DelayedAction(){} +public slots: + virtual void run(); +public: + int delay; + DelayedAction *next; +}; + +class ClickLaterAction : public DelayedAction +{ +Q_OBJECT +public: + ClickLaterAction(InterfaceChildPair interface, Qt::MouseButtons buttons = Qt::LeftButton); + ClickLaterAction(QWidget *widget, Qt::MouseButtons buttons = Qt::LeftButton); +protected slots: + void run(); +private: + bool useInterface; + InterfaceChildPair interface; + QWidget *widget; + Qt::MouseButtons buttons; +}; + +/* + +*/ +class GuiTester : public QObject +{ +Q_OBJECT +public: + GuiTester(); + ~GuiTester(); + enum Direction {Horizontal = 1, Vertical = 2, HorizontalAndVertical = 3}; + Q_DECLARE_FLAGS(Directions, Direction) + bool isFilled(const QImage image, const QRect &rect, const QColor &color); + bool isContent(const QImage image, const QRect &rect, Directions directions = HorizontalAndVertical); +protected slots: + void exitLoopSlot(); +protected: + void clickLater(InterfaceChildPair interface, Qt::MouseButtons buttons = Qt::LeftButton, int delay = 300); + void clickLater(QWidget *widget, Qt::MouseButtons buttons = Qt::LeftButton, int delay = 300); + + void clearSequence(); + void addToSequence(DelayedAction *action, int delay = 0); + void runSequence(); + WidgetNavigator wn; +private: + QSet<DelayedAction *> actions; + DelayedAction *startAction; + DelayedAction *lastAction; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(GuiTester::Directions) + +#endif diff --git a/tests/auto/integrationtests/macgui/macgui.pro b/tests/auto/integrationtests/macgui/macgui.pro new file mode 100644 index 0000000000..3fbe74f7c8 --- /dev/null +++ b/tests/auto/integrationtests/macgui/macgui.pro @@ -0,0 +1,14 @@ +load(qttest_p4) +TEMPLATE = app +DEPENDPATH += . +INCLUDEPATH += . + +# Input +SOURCES += tst_macgui.cpp guitest.cpp +HEADERS += guitest.h + +QT = core-private gui-private widgets + +requires(mac) + +CONFIG+=insignificant_test # QTBUG-20984, fails unstably diff --git a/tests/auto/integrationtests/macgui/tst_macgui.cpp b/tests/auto/integrationtests/macgui/tst_macgui.cpp new file mode 100644 index 0000000000..1552db21ab --- /dev/null +++ b/tests/auto/integrationtests/macgui/tst_macgui.cpp @@ -0,0 +1,242 @@ +/**************************************************************************** +** +** 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 <QApplication> +#include <QMessageBox> +#include <QtTest/QtTest> +#include <QSplashScreen> +#include <QScrollBar> +#include <QProgressDialog> +#include <QSpinBox> + +#include <guitest.h> + +class tst_MacGui : public GuiTester +{ +Q_OBJECT +private slots: + void scrollbarPainting(); + + void dummy(); + void splashScreenModality(); + void nonModalOrder(); + + void spinBoxArrowButtons(); +}; + + +QPixmap grabWindowContents(QWidget * widget) +{ + return QPixmap::grabWindow(widget->winId()); +} + +/* + Test that vertical and horizontal mac-style scrollbars paint their + entire area. +*/ +void tst_MacGui::scrollbarPainting() +{ + ColorWidget colorWidget; + colorWidget.resize(400, 400); + + QSize scrollBarSize; + + QScrollBar verticalScrollbar(&colorWidget); + verticalScrollbar.move(10, 10); + scrollBarSize = verticalScrollbar.sizeHint(); + scrollBarSize.setHeight(200); + verticalScrollbar.resize(scrollBarSize); + + QScrollBar horizontalScrollbar(&colorWidget); + horizontalScrollbar.move(30, 10); + horizontalScrollbar.setOrientation(Qt::Horizontal); + scrollBarSize = horizontalScrollbar.sizeHint(); + scrollBarSize.setWidth(200); + horizontalScrollbar.resize(scrollBarSize); + + colorWidget.show(); + colorWidget.raise(); + QTest::qWait(100); + + QPixmap pixmap = grabWindowContents(&colorWidget); + + QVERIFY(isContent(pixmap.toImage(), verticalScrollbar.geometry(), GuiTester::Horizontal)); + QVERIFY(isContent(pixmap.toImage(), horizontalScrollbar.geometry(), GuiTester::Vertical)); +} + +// When running the auto-tests on scruffy, the first enter-the-event-loop-and-wait-for-a-click +// test that runs always times out, so we have this dummy test. +void tst_MacGui::dummy() +{ + QPixmap pix(100, 100); + QSplashScreen splash(pix); + splash.show(); + + QMessageBox *box = new QMessageBox(); + box->setText("accessible?"); + box->show(); + + // Find the "OK" button and schedule a press. + InterfaceChildPair interface = wn.find(QAccessible::Name, "OK", box); + QVERIFY(interface.iface); + const int delay = 1000; + clickLater(interface, Qt::LeftButton, delay); + + // Show dialog and and enter event loop. + connect(wn.getWidget(interface), SIGNAL(clicked()), SLOT(exitLoopSlot())); + const int timeout = 4; + QTestEventLoop::instance().enterLoop(timeout); +} + +/* + Test that a message box pops up in front of a QSplashScreen. +*/ +void tst_MacGui::splashScreenModality() +{ + QPixmap pix(300, 300); + QSplashScreen splash(pix); + splash.show(); + + QMessageBox box; + //box.setWindowFlags(box.windowFlags() | Qt::WindowStaysOnTopHint); + box.setText("accessible?"); + box.show(); + + // Find the "OK" button and schedule a press. + InterfaceChildPair interface = wn.find(QAccessible::Name, "OK", &box); + QVERIFY(interface.iface); + const int delay = 1000; + clickLater(interface, Qt::LeftButton, delay); + + // Show dialog and and enter event loop. + connect(wn.getWidget(interface), SIGNAL(clicked()), SLOT(exitLoopSlot())); + const int timeout = 4; + QTestEventLoop::instance().enterLoop(timeout); + QVERIFY(QTestEventLoop::instance().timeout() == false); +} + +class PrimaryWindowDialog : public QDialog +{ +Q_OBJECT +public: + PrimaryWindowDialog(); + QWidget *secondaryWindow; + QWidget *frontWidget; +public slots: + void showSecondaryWindow(); + void test(); +}; + +PrimaryWindowDialog::PrimaryWindowDialog() : QDialog(0) +{ + frontWidget = 0; + secondaryWindow = new ColorWidget(this); + secondaryWindow->setWindowFlags(Qt::Window); + secondaryWindow->resize(400, 400); + secondaryWindow->move(100, 100); + QTimer::singleShot(1000, this, SLOT(showSecondaryWindow())); + QTimer::singleShot(2000, this, SLOT(test())); + QTimer::singleShot(3000, this, SLOT(close())); +} + +void PrimaryWindowDialog::showSecondaryWindow() +{ + secondaryWindow->show(); +} + +void PrimaryWindowDialog::test() +{ + frontWidget = QApplication::widgetAt(secondaryWindow->mapToGlobal(QPoint(100, 100))); +} + +/* + Test that a non-modal child window of a modal dialog is shown in front + of the dialog even if the dialog becomes modal after the child window + is created. +*/ +void tst_MacGui::nonModalOrder() +{ + clearSequence(); + PrimaryWindowDialog primary; + primary.resize(400, 400); + primary.move(100, 100); + primary.exec(); + QCOMPARE(primary.frontWidget, primary.secondaryWindow); +} + +/* + Test that the QSpinBox buttons are correctly positioned with the Mac style. +*/ +void tst_MacGui::spinBoxArrowButtons() +{ + ColorWidget colorWidget; + colorWidget.resize(200, 200); + QSpinBox spinBox(&colorWidget); + QSpinBox spinBox2(&colorWidget); + spinBox2.move(0, 100); + colorWidget.show(); + QTest::qWait(100); + + // Grab an unfocused spin box. + const QImage noFocus = grabWindowContents(&colorWidget).toImage(); + + // Set focus by clicking the less button. + InterfaceChildPair lessInterface = wn.find(QAccessible::Name, "Less", &spinBox); + QVERIFY(lessInterface.iface); + const int delay = 500; + clickLater(lessInterface, Qt::LeftButton, delay); + const int timeout = 1; + QTestEventLoop::instance().enterLoop(timeout); + + // Grab a focused spin box. + const QImage focus = grabWindowContents(&colorWidget).toImage(); + + // Compare the arrow area of the less button to see if it moved. + const QRect lessRect = lessInterface.iface->rect(lessInterface.possibleChild); + const QRect lessLocalRect(colorWidget.mapFromGlobal(lessRect.topLeft()), colorWidget.mapFromGlobal(lessRect.bottomRight())); + const QRect compareRect = lessLocalRect.adjusted(5, 3, -5, -7); + QVERIFY(noFocus.copy(compareRect) == focus.copy(compareRect)); +} + +QTEST_MAIN(tst_MacGui) +#include "tst_macgui.moc" + diff --git a/tests/auto/integrationtests/macnativeevents/expectedeventlist.cpp b/tests/auto/integrationtests/macnativeevents/expectedeventlist.cpp new file mode 100644 index 0000000000..d603f82f27 --- /dev/null +++ b/tests/auto/integrationtests/macnativeevents/expectedeventlist.cpp @@ -0,0 +1,214 @@ +/**************************************************************************** +** +** 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 "expectedeventlist.h" +#include <QDebug> +#include <QCoreApplication> +#include <QAbstractEventDispatcher> +#include <QtTest/QtTest> + +ExpectedEventList::ExpectedEventList(QObject *target) + : QObject(target), eventCount(0) +{ + target->installEventFilter(this); + debug = qgetenv("NATIVEDEBUG").toInt(); + if (debug > 0) + qDebug() << "Debug level sat to:" << debug; +} + +ExpectedEventList::~ExpectedEventList() +{ + qDeleteAll(eventList); +} + +void ExpectedEventList::append(QEvent *e) +{ + eventList.append(e); + ++eventCount; +} + +void ExpectedEventList::timerEvent(QTimerEvent *) +{ + timer.stop(); + QAbstractEventDispatcher::instance()->interrupt(); +} + +bool ExpectedEventList::waitForAllEvents(int maxEventWaitTime) +{ + if (eventList.isEmpty()) + return true; + + int eventCount = eventList.size(); + timer.start(maxEventWaitTime, this); + + while (timer.isActive()) { + QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents); + if (eventList.isEmpty()) + return true; + + if (eventCount < eventList.size()){ + eventCount = eventList.size(); + timer.start(maxEventWaitTime, this); + } + } + + int eventListNr = eventCount - eventList.size() + 1; + qWarning() << "Stopped waiting for expected event nr" << eventListNr; + return false; +} + +void ExpectedEventList::compareMouseEvents(QEvent *received, QEvent *expected) +{ + QMouseEvent *e1 = static_cast<QMouseEvent *>(received); + QMouseEvent *e2 = static_cast<QMouseEvent *>(expected); + + // Do a manual check first to be able to write more sensible + // debug output if we know we're going to fail: + if (e1->pos() == e2->pos() + && (e1->globalPos() == e2->globalPos()) + && (e1->button() == e2->button()) + && (e1->buttons() == e2->buttons()) + && (e1->modifiers() == e2->modifiers())) { + if (debug > 0) + qDebug() << " Received (OK):" << e1 << e1->globalPos(); + return; // equal + } + + // INVARIANT: The two events are not equal. So we fail. Depending + // on whether debug mode is no or not, we let QTest fail. Otherwise + // we let the test continue for debugging puposes. + int eventListNr = eventCount - eventList.size(); + if (debug == 0) { + qWarning() << "Expected event" << eventListNr << "differs from received event:"; + QCOMPARE(e1->pos(), e2->pos()); + QCOMPARE(e1->globalPos(), e2->globalPos()); + QCOMPARE(e1->button(), e2->button()); + QCOMPARE(e1->buttons(), e2->buttons()); + QCOMPARE(e1->modifiers(), e2->modifiers()); + } else { + qWarning() << "*** FAIL *** : Expected event" << eventListNr << "differs from received event:"; + qWarning() << "Received:" << e1 << e1->globalPos(); + qWarning() << "Expected:" << e2 << e2->globalPos(); + } +} + +void ExpectedEventList::compareKeyEvents(QEvent *received, QEvent *expected) +{ + QKeyEvent *e1 = static_cast<QKeyEvent *>(received); + QKeyEvent *e2 = static_cast<QKeyEvent *>(expected); + + // Do a manual check first to be able to write more sensible + // debug output if we know we're going to fail: + if (e1->key() == e2->key() + && (e1->modifiers() == e2->modifiers()) + && (e1->count() == e2->count()) + && (e1->isAutoRepeat() == e2->isAutoRepeat())) { + if (debug > 0) + qDebug() << " Received (OK):" << e1 << QKeySequence(e1->key()).toString(QKeySequence::NativeText); + return; // equal + } + + // INVARIANT: The two events are not equal. So we fail. Depending + // on whether debug mode is no or not, we let QTest fail. Otherwise + // we let the test continue for debugging puposes. + int eventListNr = eventCount - eventList.size(); + if (debug == 0) { + qWarning() << "Expected event" << eventListNr << "differs from received event:"; + QCOMPARE(e1->key(), e2->key()); + QCOMPARE(e1->modifiers(), e2->modifiers()); + QCOMPARE(e1->count(), e2->count()); + QCOMPARE(e1->isAutoRepeat(), e2->isAutoRepeat()); + } else { + qWarning() << "*** FAIL *** : Expected event" << eventListNr << "differs from received event:"; + qWarning() << "Received:" << e1 << QKeySequence(e1->key()).toString(QKeySequence::NativeText); + qWarning() << "Expected:" << e2 << QKeySequence(e2->key()).toString(QKeySequence::NativeText); + } +} + +bool ExpectedEventList::eventFilter(QObject *, QEvent *received) +{ + if (debug > 1) + qDebug() << received; + if (eventList.isEmpty()) + return false; + + bool eat = false; + QEvent *expected = eventList.first(); + if (expected->type() == received->type()) { + eventList.removeFirst(); + switch (received->type()) { + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseMove: + case QEvent::MouseButtonDblClick: + case QEvent::NonClientAreaMouseButtonPress: + case QEvent::NonClientAreaMouseButtonRelease: + case QEvent::NonClientAreaMouseButtonDblClick: + case QEvent::NonClientAreaMouseMove: { + compareMouseEvents(received, expected); + eat = true; + break; + } + case QEvent::KeyPress: + case QEvent::KeyRelease: { + compareKeyEvents(received, expected); + eat = true; + break; + } + case QEvent::Resize: { + break; + } + case QEvent::WindowActivate: { + break; + } + case QEvent::WindowDeactivate: { + break; + } + default: + break; + } + if (eventList.isEmpty()) + QAbstractEventDispatcher::instance()->interrupt(); + } + + return eat; +} + diff --git a/tests/auto/integrationtests/macnativeevents/expectedeventlist.h b/tests/auto/integrationtests/macnativeevents/expectedeventlist.h new file mode 100644 index 0000000000..e3fef2d56d --- /dev/null +++ b/tests/auto/integrationtests/macnativeevents/expectedeventlist.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef EVENTFILTER +#define EVENTFILTER + +#include <QWidget> +#include <QList> +#include <QEvent> +#include <QBasicTimer> + +class ExpectedEventList : public QObject +{ + QList<QEvent *> eventList; + QBasicTimer timer; + int debug; + int eventCount; + void timerEvent(QTimerEvent *); + +public: + ExpectedEventList(QObject *target); + ~ExpectedEventList(); + void append(QEvent *e); + bool waitForAllEvents(int timeoutPerEvent = 2000); + bool eventFilter(QObject *obj, QEvent *event); + +private: + void compareMouseEvents(QEvent *event1, QEvent *event2); + void compareKeyEvents(QEvent *event1, QEvent *event2); +}; + +#endif + diff --git a/tests/auto/integrationtests/macnativeevents/macnativeevents.pro b/tests/auto/integrationtests/macnativeevents/macnativeevents.pro new file mode 100644 index 0000000000..0a0f6c8743 --- /dev/null +++ b/tests/auto/integrationtests/macnativeevents/macnativeevents.pro @@ -0,0 +1,16 @@ +###################################################################### +# Automatically generated by qmake (2.01a) Wed Nov 29 22:24:47 2006 +###################################################################### + +load(qttest_p4) +TEMPLATE = app +DEPENDPATH += . +INCLUDEPATH += . +LIBS += -framework Carbon +QT += widgets +HEADERS += qnativeevents.h nativeeventlist.h expectedeventlist.h +SOURCES += qnativeevents.cpp qnativeevents_mac.cpp +SOURCES += expectedeventlist.cpp nativeeventlist.cpp +SOURCES += tst_macnativeevents.cpp + +requires(mac) diff --git a/tests/auto/integrationtests/macnativeevents/nativeeventlist.cpp b/tests/auto/integrationtests/macnativeevents/nativeeventlist.cpp new file mode 100644 index 0000000000..8c7f609d9f --- /dev/null +++ b/tests/auto/integrationtests/macnativeevents/nativeeventlist.cpp @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** 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 "nativeeventlist.h" + +NativeEventList::NativeEventList(int defaultWaitMs) + : playbackMultiplier(1.0) + , currIndex(-1) + , wait(false) + , defaultWaitMs(defaultWaitMs) +{ + debug = qgetenv("NATIVEDEBUG").toInt(); + QString multiplier = qgetenv("NATIVEDEBUGSPEED"); + if (!multiplier.isEmpty()) + setTimeMultiplier(multiplier.toFloat()); +} + +NativeEventList::~NativeEventList() +{ + for (int i=0; i<eventList.size(); i++) + delete eventList.takeAt(i).second; +} + +void NativeEventList::sendNextEvent() +{ + QNativeEvent *e = eventList.at(currIndex).second; + if (e) { + if (debug > 0) + qDebug() << "Sending:" << *e; + QNativeInput::sendNativeEvent(*e); + } + waitNextEvent(); +} + +void NativeEventList::waitNextEvent() +{ + if (++currIndex >= eventList.size()){ + emit done(); + stop(); + return; + } + + int interval = eventList.at(currIndex).first; + QTimer::singleShot(interval * playbackMultiplier, this, SLOT(sendNextEvent())); +} + +void NativeEventList::append(QNativeEvent *event) +{ + eventList.append(QPair<int, QNativeEvent *>(defaultWaitMs, event)); +} + +void NativeEventList::append(int waitMs, QNativeEvent *event) +{ + eventList.append(QPair<int, QNativeEvent *>(waitMs, event)); +} + +void NativeEventList::play(Playback playback) +{ + waitNextEvent(); + + wait = (playback == WaitUntilFinished); + while (wait) + QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents); +} + +void NativeEventList::stop() +{ + wait = false; + QAbstractEventDispatcher::instance()->interrupt(); +} + +void NativeEventList::setTimeMultiplier(float multiplier) +{ + playbackMultiplier = multiplier; +} + diff --git a/tests/auto/integrationtests/macnativeevents/nativeeventlist.h b/tests/auto/integrationtests/macnativeevents/nativeeventlist.h new file mode 100644 index 0000000000..6bebca1613 --- /dev/null +++ b/tests/auto/integrationtests/macnativeevents/nativeeventlist.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef Q_NATIVE_PLAYBACK +#define Q_NATIVE_PLAYBACK + +#include <QtCore> +#include "qnativeevents.h" + +class NativeEventList : public QObject +{ + Q_OBJECT; + + public: + enum Playback {ReturnImmediately, WaitUntilFinished}; + + NativeEventList(int defaultWaitMs = 20); + ~NativeEventList(); + + void append(QNativeEvent *event); + void append(int waitMs, QNativeEvent *event = 0); + + void play(Playback playback = WaitUntilFinished); + void stop(); + void setTimeMultiplier(float multiplier); + +signals: + void done(); + +private slots: + void sendNextEvent(); + +private: + void waitNextEvent(); + + QList<QPair<int, QNativeEvent *> > eventList; + float playbackMultiplier; + int currIndex; + bool wait; + int defaultWaitMs; + int debug; +}; + +#endif diff --git a/tests/auto/integrationtests/macnativeevents/qnativeevents.cpp b/tests/auto/integrationtests/macnativeevents/qnativeevents.cpp new file mode 100644 index 0000000000..b530d7bbe3 --- /dev/null +++ b/tests/auto/integrationtests/macnativeevents/qnativeevents.cpp @@ -0,0 +1,378 @@ +/**************************************************************************** +** +** 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 "qnativeevents.h" + +QNativeInput::QNativeInput(bool subscribe) +{ + if (subscribe) + subscribeForNativeEvents(); +} + +QNativeInput::~QNativeInput() +{ + unsubscribeForNativeEvents(); +} + +void QNativeInput::notify(QNativeEvent *event) +{ + nativeEvent(event); +} + +void QNativeInput::nativeEvent(QNativeEvent *event) +{ + switch (event->id()){ + case QNativeMouseButtonEvent::eventId:{ + QNativeMouseButtonEvent *e = static_cast<QNativeMouseButtonEvent *>(event); + (e->clickCount > 0) ? nativeMousePressEvent(e) : nativeMouseReleaseEvent(e); + break; } + case QNativeMouseMoveEvent::eventId: + nativeMouseMoveEvent(static_cast<QNativeMouseMoveEvent *>(event)); + break; + case QNativeMouseDragEvent::eventId: + nativeMouseDragEvent(static_cast<QNativeMouseDragEvent *>(event)); + break; + case QNativeMouseWheelEvent::eventId: + nativeMouseWheelEvent(static_cast<QNativeMouseWheelEvent *>(event)); + break; + case QNativeKeyEvent::eventId:{ + QNativeKeyEvent *e = static_cast<QNativeKeyEvent *>(event); + e->press ? nativeKeyPressEvent(e) : nativeKeyReleaseEvent(e); + break; } + case QNativeModifierEvent::eventId: + nativeModifierEvent(static_cast<QNativeModifierEvent *>(event)); + break; + default: + break; + } +} + +Qt::Native::Status QNativeInput::sendNativeEvent(const QNativeEvent &event, int pid) +{ + switch (event.id()){ + case QNativeMouseMoveEvent::eventId: + return sendNativeMouseMoveEvent(static_cast<const QNativeMouseMoveEvent &>(event)); + case QNativeMouseButtonEvent::eventId: + return sendNativeMouseButtonEvent(static_cast<const QNativeMouseButtonEvent &>(event)); + case QNativeMouseDragEvent::eventId: + return sendNativeMouseDragEvent(static_cast<const QNativeMouseDragEvent &>(event)); + case QNativeMouseWheelEvent::eventId: + return sendNativeMouseWheelEvent(static_cast<const QNativeMouseWheelEvent &>(event)); + case QNativeKeyEvent::eventId: + return sendNativeKeyEvent(static_cast<const QNativeKeyEvent &>(event), pid); + case QNativeModifierEvent::eventId: + return sendNativeModifierEvent(static_cast<const QNativeModifierEvent &>(event)); + case QNativeEvent::eventId: + qWarning() << "Warning: Cannot send a pure native event. Use a sub class."; + default: + return Qt::Native::Failure; + } +} + +QNativeEvent::QNativeEvent(Qt::KeyboardModifiers modifiers) + : modifiers(modifiers){} + +QNativeMouseEvent::QNativeMouseEvent(QPoint pos, Qt::KeyboardModifiers modifiers) + : QNativeEvent(modifiers), globalPos(pos){} + +QNativeMouseMoveEvent::QNativeMouseMoveEvent(QPoint pos, Qt::KeyboardModifiers modifiers) + : QNativeMouseEvent(pos, modifiers){} + +QNativeMouseButtonEvent::QNativeMouseButtonEvent(QPoint globalPos, Qt::MouseButton button, int clickCount, Qt::KeyboardModifiers modifiers) + : QNativeMouseEvent(globalPos, modifiers), button(button), clickCount(clickCount){} + +QNativeMouseDragEvent::QNativeMouseDragEvent(QPoint globalPos, Qt::MouseButton button, Qt::KeyboardModifiers modifiers) + : QNativeMouseButtonEvent(globalPos, button, true, modifiers){} + +QNativeMouseWheelEvent::QNativeMouseWheelEvent(QPoint globalPos, int delta, Qt::KeyboardModifiers modifiers) + : QNativeMouseEvent(globalPos, modifiers), delta(delta){} + +QNativeKeyEvent::QNativeKeyEvent(int nativeKeyCode, bool press, Qt::KeyboardModifiers modifiers) + : QNativeEvent(modifiers), nativeKeyCode(nativeKeyCode), press(press), character(QChar()){} + +QNativeModifierEvent::QNativeModifierEvent(Qt::KeyboardModifiers modifiers, int nativeKeyCode) + : QNativeEvent(modifiers), nativeKeyCode(nativeKeyCode){} + +QNativeKeyEvent::QNativeKeyEvent(int nativeKeyCode, bool press, QChar character, Qt::KeyboardModifiers modifiers) + : QNativeEvent(modifiers), nativeKeyCode(nativeKeyCode), press(press), character(character){} + +static QString getButtonAsString(const QNativeMouseButtonEvent *e) +{ + switch (e->button){ + case Qt::LeftButton: + return "button = LeftButton"; + break; + case Qt::RightButton: + return "button = RightButton"; + break; + case Qt::MidButton: + return "button = MidButton"; + break; + default: + return "button = Other"; + break; + } +} + +static QString getModifiersAsString(const QNativeEvent *e) +{ + if (e->modifiers == 0) + return "modifiers = none"; + + QString tmp = "modifiers = "; + if (e->modifiers.testFlag(Qt::ShiftModifier)) + tmp += "Shift"; + if (e->modifiers.testFlag(Qt::ControlModifier)) + tmp += "Control"; + if (e->modifiers.testFlag(Qt::AltModifier)) + tmp += "Alt"; + if (e->modifiers.testFlag(Qt::MetaModifier)) + tmp += "Meta"; + return tmp; +} + +static QString getPosAsString(QPoint pos) +{ + return QString("QPoint(%1, %2)").arg(pos.x()).arg(pos.y()); +} + +static QString getBoolAsString(bool b) +{ + return b ? QString("true") : QString("false"); +} + +QString QNativeMouseMoveEvent::toString() const +{ + return QString("QNativeMouseMoveEvent(globalPos = %1 %2)").arg(getPosAsString(globalPos)) + .arg(getModifiersAsString(this)); +} + +QString QNativeMouseButtonEvent::toString() const +{ + return QString("QNativeMouseButtonEvent(globalPos = %1, %2, clickCount = %3, %4)").arg(getPosAsString(globalPos)) + .arg(getButtonAsString(this)).arg(clickCount).arg(getModifiersAsString(this)); +} + +QString QNativeMouseDragEvent::toString() const +{ + return QString("QNativeMouseDragEvent(globalPos = %1, %2, clickCount = %3, %4)").arg(getPosAsString(globalPos)) + .arg(getButtonAsString(this)).arg(clickCount).arg(getModifiersAsString(this)); +} + +QString QNativeMouseWheelEvent::toString() const +{ + return QString("QNativeMouseWheelEvent(globalPos = %1, delta = %2, %3)").arg(getPosAsString(globalPos)) + .arg(delta).arg(getModifiersAsString(this)); +} + +QString QNativeKeyEvent::toString() const +{ + return QString("QNativeKeyEvent(press = %1, native key code = %2, character = %3, %4)").arg(getBoolAsString(press)) + .arg(nativeKeyCode).arg(character.isPrint() ? character : QString("<no char>")) + .arg(getModifiersAsString(this)); +} + +QString QNativeModifierEvent::toString() const +{ + return QString("QNativeModifierEvent(%1, native key code = %2)").arg(getModifiersAsString(this)) + .arg(nativeKeyCode); +} + +QDebug operator<<(QDebug d, QNativeEvent *e) +{ + Q_UNUSED(e); + return d << e->toString(); +} + +QDebug operator<<(QDebug d, const QNativeEvent &e) +{ + Q_UNUSED(e); + return d << e.toString(); +} + +QTextStream &operator<<(QTextStream &s, QNativeEvent *e) +{ + return s << e->eventId << " " << e->modifiers << " QNativeEvent"; +} + +QTextStream &operator<<(QTextStream &s, QNativeMouseEvent *e) +{ + return s << e->eventId << " " << e->globalPos.x() << " " << e->globalPos.y() << " " << e->modifiers << " " << e->toString(); +} + +QTextStream &operator<<(QTextStream &s, QNativeMouseMoveEvent *e) +{ + return s << e->eventId << " " << e->globalPos.x() << " " << e->globalPos.y() << " " << e->modifiers << " " << e->toString(); +} + +QTextStream &operator<<(QTextStream &s, QNativeMouseButtonEvent *e) +{ + return s << e->eventId << " " << e->globalPos.x() << " " << e->globalPos.y() << " " << e->button + << " " << e->clickCount << " " << e->modifiers << " " << e->toString(); +} + +QTextStream &operator<<(QTextStream &s, QNativeMouseDragEvent *e) +{ + return s << e->eventId << " " << e->globalPos.x() << " " << e->globalPos.y() << " " << e->button << " " << e->clickCount + << " " << e->modifiers << " " << e->toString(); +} + +QTextStream &operator<<(QTextStream &s, QNativeMouseWheelEvent *e) +{ + return s << e->eventId << " " << e->globalPos.x() << " " << e->globalPos.y() << " " << e->delta + << " " << e->modifiers << " " << e->toString(); +} + +QTextStream &operator<<(QTextStream &s, QNativeKeyEvent *e) +{ + return s << e->eventId << " " << e->press << " " << e->nativeKeyCode << " " << e->character + << " " << e->modifiers << " " << e->toString(); +} + +QTextStream &operator<<(QTextStream &s, QNativeModifierEvent *e) +{ + return s << e->eventId << " " << e->modifiers << " " << e->nativeKeyCode << " " << e->toString(); +} + + + + +QTextStream &operator>>(QTextStream &s, QNativeMouseMoveEvent *e) +{ + // Skip reading eventId. + QString humanReadable; + int x, y, modifiers; + s >> x >> y >> modifiers >> humanReadable; + e->globalPos.setX(x); + e->globalPos.setY(y); + e->modifiers = Qt::KeyboardModifiers(modifiers); + return s; +} + +QTextStream &operator>>(QTextStream &s, QNativeMouseButtonEvent *e) +{ + // Skip reading eventId. + QString humanReadable; + int x, y, button, clickCount, modifiers; + s >> x >> y >> button >> clickCount >> modifiers >> humanReadable; + e->globalPos.setX(x); + e->globalPos.setY(y); + e->clickCount = clickCount; + e->modifiers = Qt::KeyboardModifiers(modifiers); + switch (button){ + case 1: + e->button = Qt::LeftButton; + break; + case 2: + e->button = Qt::RightButton; + break; + case 3: + e->button = Qt::MidButton; + break; + default: + e->button = Qt::NoButton; + break; + } + return s; +} + +QTextStream &operator>>(QTextStream &s, QNativeMouseDragEvent *e) +{ + // Skip reading eventId. + QString humanReadable; + int x, y, button, clickCount, modifiers; + s >> x >> y >> button >> clickCount >> modifiers >> humanReadable; + e->globalPos.setX(x); + e->globalPos.setY(y); + e->clickCount = clickCount; + e->modifiers = Qt::KeyboardModifiers(modifiers); + switch (button){ + case 1: + e->button = Qt::LeftButton; + break; + case 2: + e->button = Qt::RightButton; + break; + case 3: + e->button = Qt::MidButton; + break; + default: + e->button = Qt::NoButton; + break; + } + return s; +} + +QTextStream &operator>>(QTextStream &s, QNativeMouseWheelEvent *e) +{ + // Skip reading eventId. + QString humanReadable; + int x, y, modifiers; + s >> x >> y >> e->delta >> modifiers >> humanReadable; + e->globalPos.setX(x); + e->globalPos.setY(y); + e->modifiers = Qt::KeyboardModifiers(modifiers); + return s; +} + +QTextStream &operator>>(QTextStream &s, QNativeKeyEvent *e) +{ + // Skip reading eventId. + QString humanReadable; + int press, modifiers; + QString character; + s >> press >> e->nativeKeyCode >> character >> modifiers >> humanReadable; + e->press = bool(press); + e->character = character[0]; + e->modifiers = Qt::KeyboardModifiers(modifiers); + return s; +} + +QTextStream &operator>>(QTextStream &s, QNativeModifierEvent *e) +{ + // Skip reading eventId. + QString humanReadable; + int modifiers; + s >> modifiers >> e->nativeKeyCode >> humanReadable; + e->modifiers = Qt::KeyboardModifiers(modifiers); + return s; +} + diff --git a/tests/auto/integrationtests/macnativeevents/qnativeevents.h b/tests/auto/integrationtests/macnativeevents/qnativeevents.h new file mode 100644 index 0000000000..276afc4fb7 --- /dev/null +++ b/tests/auto/integrationtests/macnativeevents/qnativeevents.h @@ -0,0 +1,228 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef Q_NATIVE_INPUT +#define Q_NATIVE_INPUT + +#include <QtCore> + +namespace Qt { +namespace Native { + enum Status {Success, Failure}; +}} + +// ---------------------------------------------------------------------------- +// Declare a set of native events that can be used to communicate with +// client applications in an platform independent way +// ---------------------------------------------------------------------------- + +class QNativeEvent +{ +public: + static const int eventId = 1; + + QNativeEvent(Qt::KeyboardModifiers modifiers = Qt::NoModifier); + virtual ~QNativeEvent(){}; + virtual int id() const { return eventId; }; + virtual QString toString() const = 0; + Qt::KeyboardModifiers modifiers; // Yields for mouse events too. +}; + +class QNativeMouseEvent : public QNativeEvent { +public: + static const int eventId = 2; + + QNativeMouseEvent(){}; + QNativeMouseEvent(QPoint globalPos, Qt::KeyboardModifiers modifiers = Qt::NoModifier); + virtual ~QNativeMouseEvent(){}; + virtual int id() const { return eventId; }; + + QPoint globalPos; +}; + +class QNativeMouseMoveEvent : public QNativeMouseEvent { +public: + static const int eventId = 4; + + QNativeMouseMoveEvent(){}; + QNativeMouseMoveEvent(QPoint globalPos, Qt::KeyboardModifiers modifiers = Qt::NoModifier); + virtual ~QNativeMouseMoveEvent(){}; + virtual int id() const { return eventId; }; + virtual QString toString() const; +}; + +class QNativeMouseButtonEvent : public QNativeMouseEvent { +public: + static const int eventId = 8; + + QNativeMouseButtonEvent(){}; + QNativeMouseButtonEvent(QPoint globalPos, Qt::MouseButton button, int clickCount, Qt::KeyboardModifiers modifiers = Qt::NoModifier); + virtual ~QNativeMouseButtonEvent(){}; + virtual int id() const { return eventId; }; + virtual QString toString() const; + + Qt::MouseButton button; + int clickCount; +}; + +class QNativeMouseDragEvent : public QNativeMouseButtonEvent { +public: + static const int eventId = 16; + + QNativeMouseDragEvent(){}; + QNativeMouseDragEvent(QPoint globalPos, Qt::MouseButton button, Qt::KeyboardModifiers modifiers = Qt::NoModifier); + virtual ~QNativeMouseDragEvent(){}; + virtual int id() const { return eventId; }; + virtual QString toString() const; +}; + +class QNativeMouseWheelEvent : public QNativeMouseEvent { +public: + static const int eventId = 32; + + QNativeMouseWheelEvent(){}; + QNativeMouseWheelEvent(QPoint globalPos, int delta, Qt::KeyboardModifiers modifiers = Qt::NoModifier); + virtual ~QNativeMouseWheelEvent(){}; + virtual int id() const { return eventId; }; + virtual QString toString() const; + + int delta; +}; + +class QNativeKeyEvent : public QNativeEvent { + public: + static const int eventId = 64; + + QNativeKeyEvent(){}; + QNativeKeyEvent(int nativeKeyCode, bool press, Qt::KeyboardModifiers modifiers = Qt::NoModifier); + QNativeKeyEvent(int nativeKeyCode, bool press, QChar character, Qt::KeyboardModifiers modifiers); + virtual ~QNativeKeyEvent(){}; + virtual int id() const { return eventId; }; + virtual QString toString() const; + + int nativeKeyCode; + bool press; + QChar character; + + // Some Qt to Native mappings: + static int Key_A; + static int Key_B; + static int Key_C; + static int Key_1; + static int Key_Backspace; + static int Key_Enter; + static int Key_Del; +}; + +class QNativeModifierEvent : public QNativeEvent { +public: + static const int eventId = 128; + + QNativeModifierEvent(Qt::KeyboardModifiers modifiers = Qt::NoModifier, int nativeKeyCode = 0); + virtual ~QNativeModifierEvent(){}; + virtual int id() const { return eventId; }; + virtual QString toString() const; + + int nativeKeyCode; +}; + +// ---------------------------------------------------------------------------- +// Declare a set of related output / input functions for convenience: +// ---------------------------------------------------------------------------- + +QDebug operator<<(QDebug d, QNativeEvent *e); +QDebug operator<<(QDebug d, const QNativeEvent &e); + +QTextStream &operator<<(QTextStream &s, QNativeEvent *e); +QTextStream &operator<<(QTextStream &s, QNativeMouseEvent *e); +QTextStream &operator<<(QTextStream &s, QNativeMouseMoveEvent *e); +QTextStream &operator<<(QTextStream &s, QNativeMouseButtonEvent *e); +QTextStream &operator<<(QTextStream &s, QNativeMouseDragEvent *e); +QTextStream &operator<<(QTextStream &s, QNativeMouseWheelEvent *e); +QTextStream &operator<<(QTextStream &s, QNativeKeyEvent *e); +QTextStream &operator<<(QTextStream &s, QNativeModifierEvent *e); + +QTextStream &operator>>(QTextStream &s, QNativeMouseMoveEvent *e); +QTextStream &operator>>(QTextStream &s, QNativeMouseButtonEvent *e); +QTextStream &operator>>(QTextStream &s, QNativeMouseDragEvent *e); +QTextStream &operator>>(QTextStream &s, QNativeMouseWheelEvent *e); +QTextStream &operator>>(QTextStream &s, QNativeKeyEvent *e); +QTextStream &operator>>(QTextStream &s, QNativeModifierEvent *e); + +// ---------------------------------------------------------------------------- +// Declare the main class that is supposed to be sub-classed by components +// that are to receive native events +// ---------------------------------------------------------------------------- + +class QNativeInput +{ + public: + QNativeInput(bool subscribe = true); + virtual ~QNativeInput(); + + // Callback methods. Should be implemented by interested sub-classes: + void notify(QNativeEvent *event); + virtual void nativeEvent(QNativeEvent *event); + virtual void nativeMousePressEvent(QNativeMouseButtonEvent *){}; + virtual void nativeMouseReleaseEvent(QNativeMouseButtonEvent *){}; + virtual void nativeMouseMoveEvent(QNativeMouseMoveEvent *){}; + virtual void nativeMouseDragEvent(QNativeMouseDragEvent *){}; + virtual void nativeMouseWheelEvent(QNativeMouseWheelEvent *){}; + virtual void nativeKeyPressEvent(QNativeKeyEvent *){}; + virtual void nativeKeyReleaseEvent(QNativeKeyEvent *){}; + virtual void nativeModifierEvent(QNativeModifierEvent *){}; + + // The following methods will differ in implementation from OS to OS: + static Qt::Native::Status sendNativeMouseButtonEvent(const QNativeMouseButtonEvent &event); + static Qt::Native::Status sendNativeMouseMoveEvent(const QNativeMouseMoveEvent &event); + static Qt::Native::Status sendNativeMouseDragEvent(const QNativeMouseDragEvent &event); + static Qt::Native::Status sendNativeMouseWheelEvent(const QNativeMouseWheelEvent &event); + static Qt::Native::Status sendNativeKeyEvent(const QNativeKeyEvent &event, int pid = 0); + static Qt::Native::Status sendNativeModifierEvent(const QNativeModifierEvent &event); + // sendNativeEvent will NOT differ from OS to OS. + static Qt::Native::Status sendNativeEvent(const QNativeEvent &event, int pid = 0); + + // The following methods will differ in implementation from OS to OS: + Qt::Native::Status subscribeForNativeEvents(); + Qt::Native::Status unsubscribeForNativeEvents(); +}; + +#endif // Q_NATIVE_INPUT diff --git a/tests/auto/integrationtests/macnativeevents/qnativeevents_mac.cpp b/tests/auto/integrationtests/macnativeevents/qnativeevents_mac.cpp new file mode 100644 index 0000000000..8987a8d502 --- /dev/null +++ b/tests/auto/integrationtests/macnativeevents/qnativeevents_mac.cpp @@ -0,0 +1,382 @@ +/**************************************************************************** +** +** 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 "qnativeevents.h" +#include <Carbon/Carbon.h> +#include <QtCore> + +// ************************************************************ +// Quartz +// ************************************************************ + +static Qt::KeyboardModifiers getModifiersFromQuartzEvent(CGEventRef inEvent) +{ + Qt::KeyboardModifiers m; + CGEventFlags flags = CGEventGetFlags(inEvent); + if (flags & kCGEventFlagMaskShift || flags & kCGEventFlagMaskAlphaShift) + m |= Qt::ShiftModifier; + if (flags & kCGEventFlagMaskControl) + m |= Qt::ControlModifier; + if (flags & kCGEventFlagMaskAlternate) + m |= Qt::AltModifier; + if (flags & kCGEventFlagMaskCommand) + m |= Qt::MetaModifier; + return m; +} + +static void setModifiersFromQNativeEvent(CGEventRef inEvent, const QNativeEvent &event) +{ + CGEventFlags flags = 0; + if (event.modifiers.testFlag(Qt::ShiftModifier)) + flags |= kCGEventFlagMaskShift; + if (event.modifiers.testFlag(Qt::ControlModifier)) + flags |= kCGEventFlagMaskControl; + if (event.modifiers.testFlag(Qt::AltModifier)) + flags |= kCGEventFlagMaskAlternate; + if (event.modifiers.testFlag(Qt::MetaModifier)) + flags |= kCGEventFlagMaskCommand; + CGEventSetFlags(inEvent, flags); +} + +static QPoint getMouseLocationFromQuartzEvent(CGEventRef inEvent) +{ + CGPoint pos = CGEventGetLocation(inEvent); + QPoint tmp; + tmp.setX(pos.x); + tmp.setY(pos.y); + return tmp; +} + +static QChar getCharFromQuartzEvent(CGEventRef inEvent) +{ + UniCharCount count = 0; + UniChar c; + CGEventKeyboardGetUnicodeString(inEvent, 1, &count, &c); + return QChar(c); +} + +static CGEventRef EventHandler_Quartz(CGEventTapProxy proxy, CGEventType type, CGEventRef inEvent, void *refCon) +{ + Q_UNUSED(proxy); + QNativeInput *nativeInput = static_cast<QNativeInput *>(refCon); + switch (type){ + case kCGEventKeyDown:{ + QNativeKeyEvent e; + e.modifiers = getModifiersFromQuartzEvent(inEvent); + e.nativeKeyCode = CGEventGetIntegerValueField(inEvent, kCGKeyboardEventKeycode); + e.character = getCharFromQuartzEvent(inEvent); + e.press = true; + nativeInput->notify(&e); + break; + } + case kCGEventKeyUp:{ + QNativeKeyEvent e; + e.modifiers = getModifiersFromQuartzEvent(inEvent); + e.nativeKeyCode = CGEventGetIntegerValueField(inEvent, kCGKeyboardEventKeycode); + e.character = getCharFromQuartzEvent(inEvent); + e.press = false; + nativeInput->notify(&e); + break; + } + case kCGEventLeftMouseDown:{ + QNativeMouseButtonEvent e; + e.modifiers = getModifiersFromQuartzEvent(inEvent); + e.globalPos = getMouseLocationFromQuartzEvent(inEvent); + e.clickCount = CGEventGetIntegerValueField(inEvent, kCGMouseEventClickState); + e.button = Qt::LeftButton; + nativeInput->notify(&e); + break; + } + case kCGEventLeftMouseUp:{ + QNativeMouseButtonEvent e; + e.modifiers = getModifiersFromQuartzEvent(inEvent); + e.globalPos = getMouseLocationFromQuartzEvent(inEvent); + e.clickCount = 0; + e.button = Qt::LeftButton; + nativeInput->notify(&e); + break; + } + case kCGEventRightMouseDown:{ + QNativeMouseButtonEvent e; + e.modifiers = getModifiersFromQuartzEvent(inEvent); + e.globalPos = getMouseLocationFromQuartzEvent(inEvent); + e.clickCount = CGEventGetIntegerValueField(inEvent, kCGMouseEventClickState); + e.button = Qt::RightButton; + nativeInput->notify(&e); + break; + } + case kCGEventRightMouseUp:{ + QNativeMouseButtonEvent e; + e.modifiers = getModifiersFromQuartzEvent(inEvent); + e.globalPos = getMouseLocationFromQuartzEvent(inEvent); + e.clickCount = 0; + e.button = Qt::RightButton; + nativeInput->notify(&e); + break; + } + case kCGEventMouseMoved:{ + QNativeMouseMoveEvent e; + e.modifiers = getModifiersFromQuartzEvent(inEvent); + e.globalPos = getMouseLocationFromQuartzEvent(inEvent); + nativeInput->notify(&e); + break; + } + case kCGEventLeftMouseDragged:{ + QNativeMouseDragEvent e; + e.modifiers = getModifiersFromQuartzEvent(inEvent); + e.globalPos = getMouseLocationFromQuartzEvent(inEvent); + e.clickCount = CGEventGetIntegerValueField(inEvent, kCGMouseEventClickState); + e.button = Qt::LeftButton; + nativeInput->notify(&e); + break; + } + case kCGEventScrollWheel:{ + QNativeMouseWheelEvent e; + e.modifiers = getModifiersFromQuartzEvent(inEvent); + e.delta = CGEventGetIntegerValueField(inEvent, kCGScrollWheelEventDeltaAxis1); + e.globalPos = getMouseLocationFromQuartzEvent(inEvent); + nativeInput->notify(&e); + break; + } + case kCGEventFlagsChanged:{ + QNativeModifierEvent e; + e.modifiers = getModifiersFromQuartzEvent(inEvent); + e.nativeKeyCode = CGEventGetIntegerValueField(inEvent, kCGKeyboardEventKeycode); + nativeInput->notify(&e); + break; + } + + } + + return inEvent; +} + +Qt::Native::Status insertEventHandler_Quartz(QNativeInput *nativeInput, int pid = 0) +{ + uid_t uid = geteuid(); + if (uid != 0) + qWarning("MacNativeEvents: You must be root to listen for key events!"); + + CFMachPortRef port; + if (!pid){ + port = CGEventTapCreate(kCGHIDEventTap, + kCGHeadInsertEventTap, kCGEventTapOptionListenOnly, + kCGEventMaskForAllEvents, EventHandler_Quartz, nativeInput); + } else { + ProcessSerialNumber psn; + GetProcessForPID(pid, &psn); + port = CGEventTapCreateForPSN(&psn, + kCGHeadInsertEventTap, kCGEventTapOptionListenOnly, + kCGEventMaskForAllEvents, EventHandler_Quartz, nativeInput); + } + + CFRunLoopSourceRef eventSrc = CFMachPortCreateRunLoopSource(NULL, port, 0); + CFRunLoopAddSource((CFRunLoopRef) GetCFRunLoopFromEventLoop(GetMainEventLoop()), + eventSrc, kCFRunLoopCommonModes); + + return Qt::Native::Success; +} + +Qt::Native::Status removeEventHandler_Quartz() +{ + return Qt::Native::Success; // ToDo: +} + +Qt::Native::Status sendNativeKeyEventToProcess_Quartz(const QNativeKeyEvent &event, int pid) +{ + ProcessSerialNumber psn; + GetProcessForPID(pid, &psn); + + CGEventRef e = CGEventCreateKeyboardEvent(0, (uint)event.nativeKeyCode, event.press); + setModifiersFromQNativeEvent(e, event); + SetFrontProcess(&psn); + CGEventPostToPSN(&psn, e); + CFRelease(e); + return Qt::Native::Success; +} + +Qt::Native::Status sendNativeKeyEvent_Quartz(const QNativeKeyEvent &event) +{ + CGEventRef e = CGEventCreateKeyboardEvent(0, (uint)event.nativeKeyCode, event.press); + setModifiersFromQNativeEvent(e, event); + CGEventPost(kCGHIDEventTap, e); + CFRelease(e); + return Qt::Native::Success; +} + +Qt::Native::Status sendNativeMouseMoveEvent_Quartz(const QNativeMouseMoveEvent &event) +{ + CGPoint pos; + pos.x = event.globalPos.x(); + pos.y = event.globalPos.y(); + + CGEventRef e = CGEventCreateMouseEvent(0, kCGEventMouseMoved, pos, 0); + setModifiersFromQNativeEvent(e, event); + CGEventPost(kCGHIDEventTap, e); + CFRelease(e); + return Qt::Native::Success; +} + +Qt::Native::Status sendNativeMouseButtonEvent_Quartz(const QNativeMouseButtonEvent &event) +{ + CGPoint pos; + pos.x = event.globalPos.x(); + pos.y = event.globalPos.y(); + + CGEventType type = 0; + if (event.button == Qt::LeftButton) + type = (event.clickCount > 0) ? kCGEventLeftMouseDown : kCGEventLeftMouseUp; + else if (event.button == Qt::RightButton) + type = (event.clickCount > 0) ? kCGEventRightMouseDown : kCGEventRightMouseUp; + else + type = (event.clickCount > 0) ? kCGEventOtherMouseDown : kCGEventOtherMouseUp; + + CGEventRef e = CGEventCreateMouseEvent(0, type, pos, event.button); + setModifiersFromQNativeEvent(e, event); + CGEventSetIntegerValueField(e, kCGMouseEventClickState, event.clickCount); + CGEventPost(kCGHIDEventTap, e); + CFRelease(e); + return Qt::Native::Success; +} + +Qt::Native::Status sendNativeMouseDragEvent_Quartz(const QNativeMouseDragEvent &event) +{ + CGPoint pos; + pos.x = event.globalPos.x(); + pos.y = event.globalPos.y(); + + CGEventType type = 0; + if (event.button == Qt::LeftButton) + type = kCGEventLeftMouseDragged; + else if (event.button == Qt::RightButton) + type = kCGEventRightMouseDragged; + else + type = kCGEventOtherMouseDragged; + + CGEventRef e = CGEventCreateMouseEvent(0, type, pos, event.button); + setModifiersFromQNativeEvent(e, event); + CGEventPost(kCGHIDEventTap, e); + CFRelease(e); + return Qt::Native::Success; +} + +Qt::Native::Status sendNativeMouseWheelEvent_Quartz(const QNativeMouseWheelEvent &event) +{ + CGPoint pos; + pos.x = event.globalPos.x(); + pos.y = event.globalPos.y(); + + CGEventRef e = CGEventCreateScrollWheelEvent(0, kCGScrollEventUnitPixel, 1, 0); + CGEventSetIntegerValueField(e, kCGScrollWheelEventDeltaAxis1, event.delta); + CGEventSetLocation(e, pos); + setModifiersFromQNativeEvent(e, event); + CGEventPost(kCGHIDEventTap, e); + CFRelease(e); + + return Qt::Native::Success; +} + +Qt::Native::Status sendNativeModifierEvent_Quartz(const QNativeModifierEvent &event) +{ + CGEventRef e = CGEventCreateKeyboardEvent(0, (uint)event.nativeKeyCode, 0); + CGEventSetType(e, kCGEventFlagsChanged); + setModifiersFromQNativeEvent(e, event); + CGEventPost(kCGHIDEventTap, e); + CFRelease(e); + return Qt::Native::Success; +} + +// ************************************************************ +// QNativeInput methods: +// ************************************************************ + +Qt::Native::Status QNativeInput::sendNativeMouseButtonEvent(const QNativeMouseButtonEvent &event) +{ + return sendNativeMouseButtonEvent_Quartz(event); +} + +Qt::Native::Status QNativeInput::sendNativeMouseMoveEvent(const QNativeMouseMoveEvent &event) +{ + return sendNativeMouseMoveEvent_Quartz(event); +} + +Qt::Native::Status QNativeInput::sendNativeMouseDragEvent(const QNativeMouseDragEvent &event) +{ + return sendNativeMouseDragEvent_Quartz(event); +} + +Qt::Native::Status QNativeInput::sendNativeMouseWheelEvent(const QNativeMouseWheelEvent &event) +{ + return sendNativeMouseWheelEvent_Quartz(event); +} + +Qt::Native::Status QNativeInput::sendNativeKeyEvent(const QNativeKeyEvent &event, int pid) +{ + if (!pid) + return sendNativeKeyEvent_Quartz(event); + else + return sendNativeKeyEventToProcess_Quartz(event, pid); +} + +Qt::Native::Status QNativeInput::sendNativeModifierEvent(const QNativeModifierEvent &event) +{ + return sendNativeModifierEvent_Quartz(event); +} + +Qt::Native::Status QNativeInput::subscribeForNativeEvents() +{ + return insertEventHandler_Quartz(this); +} + +Qt::Native::Status QNativeInput::unsubscribeForNativeEvents() +{ + return removeEventHandler_Quartz(); +} + +// Some Qt to Mac mappings: +int QNativeKeyEvent::Key_A = 0; +int QNativeKeyEvent::Key_B = 11; +int QNativeKeyEvent::Key_C = 8; +int QNativeKeyEvent::Key_1 = 18; +int QNativeKeyEvent::Key_Backspace = 51; +int QNativeKeyEvent::Key_Enter = 36; +int QNativeKeyEvent::Key_Del = 117; + diff --git a/tests/auto/integrationtests/macnativeevents/tst_macnativeevents.cpp b/tests/auto/integrationtests/macnativeevents/tst_macnativeevents.cpp new file mode 100644 index 0000000000..ea30b0b5c2 --- /dev/null +++ b/tests/auto/integrationtests/macnativeevents/tst_macnativeevents.cpp @@ -0,0 +1,522 @@ +/**************************************************************************** +** +** 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 <QApplication> +#include <QWidget> +#include <QDialog> +#include <QPushButton> +#include <QtTest/QtTest> + +#include "qnativeevents.h" +#include "nativeeventlist.h" +#include "expectedeventlist.h" +#include <Carbon/Carbon.h> + +QT_USE_NAMESPACE + +class tst_MacNativeEvents : public QObject +{ +Q_OBJECT +private slots: + void testMouseMoveLocation(); + void testPushButtonPressRelease(); + void testMouseLeftDoubleClick(); + void stressTestMouseLeftDoubleClick(); + void testMouseDragInside(); + void testMouseDragOutside(); + void testMouseDragToNonClientArea(); + void testDragWindow(); + void testMouseEnter(); + void testChildDialogInFrontOfModalParent(); +// void testChildWindowInFrontOfParentWindow(); +// void testChildToolWindowInFrontOfChildNormalWindow(); + void testChildWindowInFrontOfStaysOnTopParentWindow(); + void testKeyPressOnToplevel(); + void testModifierShift(); + void testModifierAlt(); + void testModifierCtrl(); + void testModifierCtrlWithDontSwapCtrlAndMeta(); +}; + +void tst_MacNativeEvents::testMouseMoveLocation() +{ + QWidget w; + w.setMouseTracking(true); + w.show(); + QPoint p = w.geometry().center(); + + NativeEventList native; + native.append(new QNativeMouseMoveEvent(p, Qt::NoModifier)); + + ExpectedEventList expected(&w); + expected.append(new QMouseEvent(QEvent::MouseMove, w.mapFromGlobal(p), p, Qt::NoButton, Qt::NoButton, Qt::NoModifier)); + + native.play(); + QVERIFY2(expected.waitForAllEvents(), "the test did not receive all expected events!"); +} + +void tst_MacNativeEvents::testPushButtonPressRelease() +{ + // Check that a native mouse press and release generates the + // same qevents on a pushbutton: + QPushButton w("click me"); + w.show(); + QPoint p = w.geometry().center(); + + NativeEventList native; + native.append(new QNativeMouseButtonEvent(p, Qt::LeftButton, 1, Qt::NoModifier)); + native.append(new QNativeMouseButtonEvent(p, Qt::LeftButton, 0, Qt::NoModifier)); + + ExpectedEventList expected(&w); + expected.append(new QMouseEvent(QEvent::MouseButtonPress, w.mapFromGlobal(p), p, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); + expected.append(new QMouseEvent(QEvent::MouseButtonRelease, w.mapFromGlobal(p), p, Qt::LeftButton, Qt::NoButton, Qt::NoModifier)); + + native.play(); + QVERIFY2(expected.waitForAllEvents(), "the test did not receive all expected events!"); +} + +void tst_MacNativeEvents::testMouseLeftDoubleClick() +{ + // Check that a native double click makes + // the test widget receive a press-release-click-release: + QWidget w; + w.show(); + QPoint p = w.geometry().center(); + + NativeEventList native; + native.append(new QNativeMouseButtonEvent(p, Qt::LeftButton, 1, Qt::NoModifier)); + native.append(new QNativeMouseButtonEvent(p, Qt::LeftButton, 0, Qt::NoModifier)); + native.append(new QNativeMouseButtonEvent(p, Qt::LeftButton, 2, Qt::NoModifier)); + native.append(new QNativeMouseButtonEvent(p, Qt::LeftButton, 0, Qt::NoModifier)); + + ExpectedEventList expected(&w); + expected.append(new QMouseEvent(QEvent::MouseButtonPress, w.mapFromGlobal(p), p, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); + expected.append(new QMouseEvent(QEvent::MouseButtonRelease, w.mapFromGlobal(p), p, Qt::LeftButton, Qt::NoButton, Qt::NoModifier)); + expected.append(new QMouseEvent(QEvent::MouseButtonDblClick, w.mapFromGlobal(p), p, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); + expected.append(new QMouseEvent(QEvent::MouseButtonRelease, w.mapFromGlobal(p), p, Qt::LeftButton, Qt::NoButton, Qt::NoModifier)); + + native.play(); + QVERIFY2(expected.waitForAllEvents(), "the test did not receive all expected events!"); +} + +void tst_MacNativeEvents::stressTestMouseLeftDoubleClick() +{ + // Check that multiple, fast, double clicks makes + // the test widget receive correct click events + QWidget w; + w.show(); + QPoint p = w.geometry().center(); + + NativeEventList native; + ExpectedEventList expected(&w); + + for (int i=0; i<10; ++i){ + native.append(new QNativeMouseButtonEvent(p, Qt::LeftButton, 1, Qt::NoModifier)); + native.append(new QNativeMouseButtonEvent(p, Qt::LeftButton, 0, Qt::NoModifier)); + native.append(new QNativeMouseButtonEvent(p, Qt::LeftButton, 2, Qt::NoModifier)); + native.append(new QNativeMouseButtonEvent(p, Qt::LeftButton, 0, Qt::NoModifier)); + + expected.append(new QMouseEvent(QEvent::MouseButtonPress, w.mapFromGlobal(p), p, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); + expected.append(new QMouseEvent(QEvent::MouseButtonRelease, w.mapFromGlobal(p), p, Qt::LeftButton, Qt::NoButton, Qt::NoModifier)); + expected.append(new QMouseEvent(QEvent::MouseButtonDblClick, w.mapFromGlobal(p), p, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); + expected.append(new QMouseEvent(QEvent::MouseButtonRelease, w.mapFromGlobal(p), p, Qt::LeftButton, Qt::NoButton, Qt::NoModifier)); + } + + native.play(); + QVERIFY2(expected.waitForAllEvents(), "the test did not receive all expected events!"); +} + +void tst_MacNativeEvents::testMouseDragInside() +{ + // Check that a mouse drag inside a widget + // will cause press-move-release events to be delivered + QWidget w; + w.show(); + QPoint p1 = w.geometry().center(); + QPoint p2 = p1 - QPoint(10, 0); + QPoint p3 = p1 - QPoint(20, 0); + QPoint p4 = p1 - QPoint(30, 0); + + NativeEventList native; + native.append(new QNativeMouseButtonEvent(p1, Qt::LeftButton, 1, Qt::NoModifier)); + native.append(new QNativeMouseDragEvent(p2, Qt::LeftButton, Qt::NoModifier)); + native.append(new QNativeMouseDragEvent(p3, Qt::LeftButton, Qt::NoModifier)); + native.append(new QNativeMouseButtonEvent(p4, Qt::LeftButton, 0, Qt::NoModifier)); + + ExpectedEventList expected(&w); + expected.append(new QMouseEvent(QEvent::MouseButtonPress, w.mapFromGlobal(p1), p1, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); + expected.append(new QMouseEvent(QEvent::MouseMove, w.mapFromGlobal(p2), p2, Qt::NoButton, Qt::LeftButton, Qt::NoModifier)); + expected.append(new QMouseEvent(QEvent::MouseMove, w.mapFromGlobal(p3), p3, Qt::NoButton, Qt::LeftButton, Qt::NoModifier)); + expected.append(new QMouseEvent(QEvent::MouseButtonRelease, w.mapFromGlobal(p4), p4, Qt::LeftButton, Qt::NoButton, Qt::NoModifier)); + + native.play(); + QVERIFY2(expected.waitForAllEvents(), "the test did not receive all expected events!"); +} + +void tst_MacNativeEvents::testMouseDragOutside() +{ + // Check that if we drag the mouse from inside the + // widget, and release it outside, we still get mouse move + // and release events when the mouse is outside the widget. + QWidget w; + w.show(); + QPoint inside1 = w.geometry().center(); + QPoint inside2 = inside1 - QPoint(10, 0); + QPoint outside1 = w.geometry().topLeft() - QPoint(50, 0); + QPoint outside2 = outside1 - QPoint(10, 0); + + NativeEventList native; + native.append(new QNativeMouseButtonEvent(inside1, Qt::LeftButton, 1, Qt::NoModifier)); + native.append(new QNativeMouseDragEvent(inside2, Qt::LeftButton, Qt::NoModifier)); + native.append(new QNativeMouseDragEvent(outside1, Qt::LeftButton, Qt::NoModifier)); + native.append(new QNativeMouseButtonEvent(outside2, Qt::LeftButton, 0, Qt::NoModifier)); + + ExpectedEventList expected(&w); + expected.append(new QMouseEvent(QEvent::MouseButtonPress, w.mapFromGlobal(inside1), inside1, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); + expected.append(new QMouseEvent(QEvent::MouseMove, w.mapFromGlobal(inside2), inside2, Qt::NoButton, Qt::LeftButton, Qt::NoModifier)); + expected.append(new QMouseEvent(QEvent::MouseMove, w.mapFromGlobal(outside1), outside1, Qt::NoButton, Qt::LeftButton, Qt::NoModifier)); + expected.append(new QMouseEvent(QEvent::MouseButtonRelease, w.mapFromGlobal(outside2), outside2, Qt::LeftButton, Qt::NoButton, Qt::NoModifier)); + + native.play(); + QVERIFY2(expected.waitForAllEvents(), "the test did not receive all expected events!"); +} + +void tst_MacNativeEvents::testMouseDragToNonClientArea() +{ + // Check that if we drag the mouse from inside the + // widget, and release it on the title bar, we still get mouse move + // and release events when the mouse is on the title bar + QWidget w; + w.show(); + QPoint inside1 = w.geometry().center(); + QPoint inside2 = inside1 - QPoint(10, 0); + QPoint titlebar1 = w.geometry().topLeft() - QPoint(-100, 10); + QPoint titlebar2 = titlebar1 - QPoint(10, 0); + + NativeEventList native; + native.append(new QNativeMouseButtonEvent(inside1, Qt::LeftButton, 1, Qt::NoModifier)); + native.append(new QNativeMouseDragEvent(inside2, Qt::LeftButton, Qt::NoModifier)); + native.append(new QNativeMouseDragEvent(titlebar1, Qt::LeftButton, Qt::NoModifier)); + native.append(new QNativeMouseButtonEvent(titlebar2, Qt::LeftButton, 0, Qt::NoModifier)); + + ExpectedEventList expected(&w); + expected.append(new QMouseEvent(QEvent::MouseButtonPress, w.mapFromGlobal(inside1), inside1, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); + expected.append(new QMouseEvent(QEvent::MouseMove, w.mapFromGlobal(inside2), inside2, Qt::NoButton, Qt::LeftButton, Qt::NoModifier)); + expected.append(new QMouseEvent(QEvent::MouseMove, w.mapFromGlobal(titlebar1), titlebar1, Qt::NoButton, Qt::LeftButton, Qt::NoModifier)); + expected.append(new QMouseEvent(QEvent::MouseButtonRelease, w.mapFromGlobal(titlebar2), titlebar2, Qt::LeftButton, Qt::NoButton, Qt::NoModifier)); + + native.play(); + QVERIFY2(expected.waitForAllEvents(), "the test did not receive all expected events!"); +} + +void tst_MacNativeEvents::testDragWindow() +{ + // Check that if we drag the mouse from inside the + // widgets title bar, we get a move event on the window + QWidget w; + w.show(); + QPoint titlebar = w.geometry().topLeft() - QPoint(-100, 10); + QPoint moveTo = titlebar + QPoint(100, 0); + + NativeEventList native; + native.append(new QNativeMouseButtonEvent(titlebar, Qt::LeftButton, 1, Qt::NoModifier)); + native.append(new QNativeMouseDragEvent(moveTo, Qt::LeftButton, Qt::NoModifier)); + native.append(500, new QNativeMouseButtonEvent(moveTo, Qt::LeftButton, 0, Qt::NoModifier)); + + ExpectedEventList expected(&w); + expected.append(new QMouseEvent(QEvent::NonClientAreaMouseButtonPress, w.mapFromGlobal(titlebar), titlebar, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); + expected.append(new QMouseEvent(QEvent::NonClientAreaMouseButtonRelease, w.mapFromGlobal(titlebar), moveTo, Qt::LeftButton, Qt::NoButton, Qt::NoModifier)); + + native.play(); + QVERIFY2(expected.waitForAllEvents(), "the test did not receive all expected events!"); +} + +void tst_MacNativeEvents::testMouseEnter() +{ + // When a mouse enters a widget, both a mouse enter events and a + // mouse move event should be sendt. Lets test this: + QWidget w; + w.setMouseTracking(true); + w.show(); + QPoint outside = w.geometry().topLeft() - QPoint(50, 0); + QPoint inside = w.geometry().center(); + + NativeEventList native; + native.append(new QNativeMouseMoveEvent(outside, Qt::NoModifier)); + native.append(new QNativeMouseMoveEvent(inside, Qt::NoModifier)); + + ExpectedEventList expected(&w); + expected.append(new QEvent(QEvent::Enter)); + expected.append(new QMouseEvent(QEvent::MouseMove, w.mapFromGlobal(inside), inside, Qt::NoButton, Qt::NoButton, Qt::NoModifier)); + + native.play(); + QVERIFY2(expected.waitForAllEvents(), "the test did not receive all expected events!"); +} + +void tst_MacNativeEvents::testChildDialogInFrontOfModalParent() +{ + // Test that a child dialog of a modal parent dialog is + // in front of the parent, and active: + QDialog parent; + parent.setWindowModality(Qt::ApplicationModal); + QDialog child(&parent); + QPushButton button("close", &child); + connect(&button, SIGNAL(clicked()), &child, SLOT(close())); + parent.show(); + child.show(); + QPoint inside = button.mapToGlobal(button.geometry().center()); + + // Post a click on the button to close the child dialog: + NativeEventList native; + native.append(new QNativeMouseButtonEvent(inside, Qt::LeftButton, 1, Qt::NoModifier)); + native.append(new QNativeMouseButtonEvent(inside, Qt::LeftButton, 0, Qt::NoModifier)); + + native.play(); + QTest::qWait(100); + QVERIFY(!child.isVisible()); +} + +#if 0 +// This test is disabled as of Qt-4.7.4 because we cannot do it +// unless we use the Cocoa sub window API. But using that opens up +// a world of side effects that we cannot live with. So we rather +// not support child-on-top-of-parent instead. +void tst_MacNativeEvents::testChildWindowInFrontOfParentWindow() +{ + // Test that a child window always stacks in front of its parent window. + // Do this by first click on the parent, then on the child window button. + QWidget parent; + QPushButton child("a button", &parent); + child.setWindowFlags(Qt::Window); + connect(&child, SIGNAL(clicked()), &child, SLOT(close())); + parent.show(); + child.show(); + + QPoint parent_p = parent.geometry().bottomLeft() + QPoint(20, -20); + QPoint child_p = child.geometry().center(); + + NativeEventList native; + native.append(new QNativeMouseButtonEvent(parent_p, Qt::LeftButton, 1, Qt::NoModifier)); + native.append(new QNativeMouseButtonEvent(parent_p, Qt::LeftButton, 0, Qt::NoModifier)); + native.append(new QNativeMouseButtonEvent(child_p, Qt::LeftButton, 1, Qt::NoModifier)); + native.append(new QNativeMouseButtonEvent(child_p, Qt::LeftButton, 0, Qt::NoModifier)); + + native.play(); + QTest::qWait(100); + QVERIFY(!child.isVisible()); +} +#endif + +/* This test can be enabled once setStackingOrder has been fixed in qwidget_mac.mm +void tst_MacNativeEvents::testChildToolWindowInFrontOfChildNormalWindow() +{ + // Test that a child tool window always stacks in front of normal sibling windows. + // Do this by first click on the sibling, then on the tool window button. + QWidget parent; + QWidget normalChild(&parent, Qt::Window); + QPushButton toolChild("a button", &parent); + toolChild.setWindowFlags(Qt::Tool); + connect(&toolChild, SIGNAL(clicked()), &toolChild, SLOT(close())); + parent.show(); + normalChild.show(); + toolChild.show(); + + QPoint normalChild_p = normalChild.geometry().bottomLeft() + QPoint(20, -20); + QPoint toolChild_p = toolChild.geometry().center(); + + NativeEventList native; + native.append(new QNativeMouseButtonEvent(normalChild_p, Qt::LeftButton, 1, Qt::NoModifier)); + native.append(new QNativeMouseButtonEvent(normalChild_p, Qt::LeftButton, 0, Qt::NoModifier)); + native.append(new QNativeMouseButtonEvent(toolChild_p, Qt::LeftButton, 1, Qt::NoModifier)); + native.append(new QNativeMouseButtonEvent(toolChild_p, Qt::LeftButton, 0, Qt::NoModifier)); + + native.play(); + QTest::qWait(100); + QVERIFY(!toolChild.isVisible()); +} +*/ +void tst_MacNativeEvents::testChildWindowInFrontOfStaysOnTopParentWindow() +{ + // Test that a child window stacks on top of a stays-on-top parent. + QWidget parent(0, Qt::WindowStaysOnTopHint); + QPushButton button("close", &parent); + button.setWindowFlags(Qt::Window); + connect(&button, SIGNAL(clicked()), &button, SLOT(close())); + parent.show(); + button.show(); + QPoint inside = button.geometry().center(); + + // Post a click on the button to close the child dialog: + NativeEventList native; + native.append(new QNativeMouseButtonEvent(inside, Qt::LeftButton, 1, Qt::NoModifier)); + native.append(new QNativeMouseButtonEvent(inside, Qt::LeftButton, 0, Qt::NoModifier)); + + native.play(); + QTest::qWait(100); + QVERIFY(!button.isVisible()); +} + +void tst_MacNativeEvents::testKeyPressOnToplevel() +{ + // Check that we receive keyevents for + // toplevel widgets. For leagacy reasons, and according to Qt on + // other platforms (carbon port + linux), we should get these events + // even when the focus policy is set to Qt::NoFocus when there is no + // other focus widget on screen: + QWidget w; + w.show(); + + NativeEventList native; + native.append(new QNativeKeyEvent(QNativeKeyEvent::Key_A, true, Qt::NoModifier)); + native.append(new QNativeKeyEvent(QNativeKeyEvent::Key_A, false, Qt::NoModifier)); + + ExpectedEventList expected(&w); + expected.append(new QKeyEvent(QEvent::KeyPress, Qt::Key_A, Qt::NoModifier)); + expected.append(new QKeyEvent(QEvent::KeyRelease, Qt::Key_A, Qt::NoModifier)); + + native.play(); + QVERIFY2(expected.waitForAllEvents(), "the test did not receive all expected events!"); +} + +void tst_MacNativeEvents::testModifierShift() +{ + QWidget w; + w.show(); + + NativeEventList native; + native.append(new QNativeModifierEvent(Qt::ShiftModifier)); + native.append(new QNativeKeyEvent(QNativeKeyEvent::Key_A, true, Qt::ShiftModifier)); + native.append(new QNativeKeyEvent(QNativeKeyEvent::Key_A, false, Qt::ShiftModifier)); + native.append(new QNativeModifierEvent(Qt::NoModifier)); + + ExpectedEventList expected(&w); + expected.append(new QKeyEvent(QEvent::KeyPress, Qt::Key_Shift, Qt::NoModifier)); + expected.append(new QKeyEvent(QEvent::KeyPress, Qt::Key_A, Qt::ShiftModifier)); + expected.append(new QKeyEvent(QEvent::KeyRelease, Qt::Key_A, Qt::ShiftModifier)); + expected.append(new QKeyEvent(QEvent::KeyRelease, Qt::Key_Shift, Qt::ShiftModifier)); + + native.play(); + QVERIFY2(expected.waitForAllEvents(), "the test did not receive all expected events!"); +} + +void tst_MacNativeEvents::testModifierAlt() +{ + QWidget w; + w.show(); + + NativeEventList native; + native.append(new QNativeModifierEvent(Qt::AltModifier)); + native.append(new QNativeKeyEvent(QNativeKeyEvent::Key_A, true, Qt::AltModifier)); + native.append(new QNativeKeyEvent(QNativeKeyEvent::Key_A, false, Qt::AltModifier)); + native.append(new QNativeModifierEvent(Qt::NoModifier)); + + ExpectedEventList expected(&w); + expected.append(new QKeyEvent(QEvent::KeyPress, Qt::Key_Alt, Qt::NoModifier)); + expected.append(new QKeyEvent(QEvent::KeyPress, Qt::Key_A, Qt::AltModifier)); + expected.append(new QKeyEvent(QEvent::KeyRelease, Qt::Key_A, Qt::AltModifier)); + expected.append(new QKeyEvent(QEvent::KeyRelease, Qt::Key_Alt, Qt::AltModifier)); + + native.play(); + QVERIFY2(expected.waitForAllEvents(), "the test did not receive all expected events!"); +} + +void tst_MacNativeEvents::testModifierCtrl() +{ + // On Mac, we switch the Command and Control modifier by default, so that Command + // means Meta, and Control means Command. Lets check that this works: + QWidget w; + w.show(); + + QVERIFY(kControlUnicode == QKeySequence(Qt::Key_Meta).toString(QKeySequence::NativeText)[0]); + QVERIFY(kCommandUnicode == QKeySequence(Qt::Key_Control).toString(QKeySequence::NativeText)[0]); + + NativeEventList native; + native.append(new QNativeModifierEvent(Qt::ControlModifier)); + native.append(new QNativeKeyEvent(QNativeKeyEvent::Key_A, true, Qt::ControlModifier)); + native.append(new QNativeKeyEvent(QNativeKeyEvent::Key_A, false, Qt::ControlModifier)); + native.append(new QNativeModifierEvent(Qt::NoModifier)); + + ExpectedEventList expected(&w); + expected.append(new QKeyEvent(QEvent::KeyPress, Qt::Key_Meta, Qt::NoModifier)); + expected.append(new QKeyEvent(QEvent::KeyPress, Qt::Key_A, Qt::MetaModifier)); + expected.append(new QKeyEvent(QEvent::KeyRelease, Qt::Key_A, Qt::MetaModifier)); + expected.append(new QKeyEvent(QEvent::KeyRelease, Qt::Key_Meta, Qt::MetaModifier)); + + native.play(); + QVERIFY2(expected.waitForAllEvents(), "the test did not receive all expected events!"); +} + +void tst_MacNativeEvents::testModifierCtrlWithDontSwapCtrlAndMeta() +{ + // On Mac, we switch the Command and Control modifier by default, so that Command + // means Meta, and Control means Command. Lets check that the flag to swith off + // this behaviour works. While working on this test I realized that we actually + // don't (and never have) respected this flag for raw key events. Only for + // menus, through QKeySequence. I don't want to change this behaviour now, at + // least not until someone complains. So I choose to let the test just stop + // any unintended regressions instead. If we decide to resepect the the flag at one + // point, fix the test. + QCoreApplication::setAttribute(Qt::AA_MacDontSwapCtrlAndMeta); + QWidget w; + w.show(); + + QVERIFY(kCommandUnicode == QKeySequence(Qt::Key_Meta).toString(QKeySequence::NativeText)[0]); + QVERIFY(kControlUnicode == QKeySequence(Qt::Key_Control).toString(QKeySequence::NativeText)[0]); + + NativeEventList native; + native.append(new QNativeModifierEvent(Qt::ControlModifier)); + native.append(new QNativeKeyEvent(QNativeKeyEvent::Key_A, true, Qt::ControlModifier)); + native.append(new QNativeKeyEvent(QNativeKeyEvent::Key_A, false, Qt::ControlModifier)); + native.append(new QNativeModifierEvent(Qt::NoModifier)); + + ExpectedEventList expected(&w); + expected.append(new QKeyEvent(QEvent::KeyPress, Qt::Key_Meta, Qt::NoModifier)); + expected.append(new QKeyEvent(QEvent::KeyPress, Qt::Key_A, Qt::ControlModifier)); + expected.append(new QKeyEvent(QEvent::KeyRelease, Qt::Key_A, Qt::ControlModifier)); + expected.append(new QKeyEvent(QEvent::KeyRelease, Qt::Key_Meta, Qt::ControlModifier)); + + native.play(); + QVERIFY2(expected.waitForAllEvents(), "the test did not receive all expected events!"); + QCoreApplication::setAttribute(Qt::AA_MacDontSwapCtrlAndMeta, false); +} + +QTEST_MAIN(tst_MacNativeEvents) +#include "tst_macnativeevents.moc" diff --git a/tests/auto/integrationtests/macplist/app/app.pro b/tests/auto/integrationtests/macplist/app/app.pro new file mode 100644 index 0000000000..c1cf52e0ef --- /dev/null +++ b/tests/auto/integrationtests/macplist/app/app.pro @@ -0,0 +1,11 @@ +###################################################################### +# Automatically generated by qmake (2.01a) Wed Jan 7 13:01:11 2009 +###################################################################### + +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . + +# Input +SOURCES += main.cpp diff --git a/tests/auto/integrationtests/macplist/app/main.cpp b/tests/auto/integrationtests/macplist/app/main.cpp new file mode 100644 index 0000000000..ddc47a9a5f --- /dev/null +++ b/tests/auto/integrationtests/macplist/app/main.cpp @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** 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 <QtGui/QApplication> + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + + return 0; +} diff --git a/tests/auto/integrationtests/macplist/macplist.pro b/tests/auto/integrationtests/macplist/macplist.pro new file mode 100644 index 0000000000..3a0e2d43d3 --- /dev/null +++ b/tests/auto/integrationtests/macplist/macplist.pro @@ -0,0 +1,7 @@ +TEMPLATE = subdirs + +SUBDIRS = app test +CONFIG += ordered + + + diff --git a/tests/auto/integrationtests/macplist/test/test.pro b/tests/auto/integrationtests/macplist/test/test.pro new file mode 100644 index 0000000000..4751b47e00 --- /dev/null +++ b/tests/auto/integrationtests/macplist/test/test.pro @@ -0,0 +1,11 @@ +load(qttest_p4) + +SOURCES += ../tst_macplist.cpp +TARGET = ../tst_macplist +win32 { + CONFIG(debug, debug|release) { + TARGET = ../../debug/tst_macplist + } else { + TARGET = ../../release/tst_macplist + } +} diff --git a/tests/auto/integrationtests/macplist/tst_macplist.cpp b/tests/auto/integrationtests/macplist/tst_macplist.cpp new file mode 100644 index 0000000000..5b48c586b5 --- /dev/null +++ b/tests/auto/integrationtests/macplist/tst_macplist.cpp @@ -0,0 +1,194 @@ +/**************************************************************************** +** +** 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 <QtCore/QDir> +#include <QtCore/QFile> +#include <QtCore/QProcess> + +class tst_MacPlist : public QObject +{ + Q_OBJECT +public: + tst_MacPlist() {} + +private slots: +#ifdef Q_OS_MAC + void test_plist_data(); + void test_plist(); +#endif +}; + +#ifdef Q_OS_MAC +void tst_MacPlist::test_plist_data() +{ + QTest::addColumn<QString>("test_plist"); + + QTest::newRow("control") << QString::fromLatin1( +"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +"<!DOCTYPE plist SYSTEM \"file://localhost/System/Library/DTDs/PropertyList.dtd\">\n" +"<plist version=\"0.9\">\n" +"<dict>\n" +" <key>CFBundleIconFile</key>\n" +" <string></string>\n" +" <key>CFBundlePackageType</key>\n" +" <string>APPL</string>\n" +" <key>CFBundleGetInfoString</key>\n" +" <string>Created by Qt/QMake</string>\n" +" <key>CFBundleExecutable</key>\n" +" <string>app</string>\n" +" <key>CFBundleIdentifier</key>\n" +" <string>com.yourcompany.app</string>\n" +"</dict>\n" +"</plist>\n"); + + QTest::newRow("LSUIElement-as-string") << QString::fromLatin1( +"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +"<!DOCTYPE plist SYSTEM \"file://localhost/System/Library/DTDs/PropertyList.dtd\">\n" +"<plist version=\"0.9\">\n" +"<dict>\n" +" <key>CFBundleIconFile</key>\n" +" <string></string>\n" +" <key>CFBundlePackageType</key>\n" +" <string>APPL</string>\n" +" <key>CFBundleGetInfoString</key>\n" +" <string>Created by Qt/QMake</string>\n" +" <key>CFBundleExecutable</key>\n" +" <string>app</string>\n" +" <key>CFBundleIdentifier</key>\n" +" <string>com.yourcompany.app</string>\n" +" <key>LSUIElement</key>\n" +" <string>false</string>\n" +"</dict>\n" +"</plist>\n"); + + QTest::newRow("LSUIElement-as-bool") << QString::fromLatin1( +"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +"<!DOCTYPE plist SYSTEM \"file://localhost/System/Library/DTDs/PropertyList.dtd\">\n" +"<plist version=\"0.9\">\n" +"<dict>\n" +" <key>CFBundleIconFile</key>\n" +" <string></string>\n" +" <key>CFBundlePackageType</key>\n" +" <string>APPL</string>\n" +" <key>CFBundleGetInfoString</key>\n" +" <string>Created by Qt/QMake</string>\n" +" <key>CFBundleExecutable</key>\n" +" <string>app</string>\n" +" <key>CFBundleIdentifier</key>\n" +" <string>com.yourcompany.app</string>\n" +" <key>LSUIElement</key>\n" +" <false/>\n" +"</dict>\n" +"</plist>\n"); + + QTest::newRow("LSUIElement-as-int") << QString::fromLatin1( +"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +"<!DOCTYPE plist SYSTEM \"file://localhost/System/Library/DTDs/PropertyList.dtd\">\n" +"<plist version=\"0.9\">\n" +"<dict>\n" +" <key>CFBundleIconFile</key>\n" +" <string></string>\n" +" <key>CFBundlePackageType</key>\n" +" <string>APPL</string>\n" +" <key>CFBundleGetInfoString</key>\n" +" <string>Created by Qt/QMake</string>\n" +" <key>CFBundleExecutable</key>\n" +" <string>app</string>\n" +" <key>CFBundleIdentifier</key>\n" +" <string>com.yourcompany.app</string>\n" +" <key>LSUIElement</key>\n" +" <real>0</real>\n" +"</dict>\n" +"</plist>\n"); + + QTest::newRow("LSUIElement-as-garbage") << QString::fromLatin1( +"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +"<!DOCTYPE plist SYSTEM \"file://localhost/System/Library/DTDs/PropertyList.dtd\">\n" +"<plist version=\"0.9\">\n" +"<dict>\n" +" <key>CFBundleIconFile</key>\n" +" <string></string>\n" +" <key>CFBundlePackageType</key>\n" +" <string>APPL</string>\n" +" <key>CFBundleGetInfoString</key>\n" +" <string>Created by Qt/QMake</string>\n" +" <key>CFBundleExecutable</key>\n" +" <string>app</string>\n" +" <key>CFBundleIdentifier</key>\n" +" <string>com.yourcompany.app</string>\n" +" <key>LSUIElement</key>\n" +" <badkey>0</badkey>\n" +"</dict>\n" +"</plist>\n"); +} + +void tst_MacPlist::test_plist() +{ + QFETCH(QString, test_plist); + + QString infoPlist = QLatin1String("Info.plist"); + QDir dir(QCoreApplication::applicationDirPath()); + QVERIFY(dir.cdUp()); + QVERIFY(dir.cdUp()); + QVERIFY(dir.cdUp()); + QVERIFY(dir.cd(QLatin1String("app"))); + QVERIFY(dir.cd(QLatin1String("app.app"))); + QVERIFY(dir.cd(QLatin1String("Contents"))); + QVERIFY(dir.exists(infoPlist)); + { + QFile file(dir.filePath(infoPlist)); + QVERIFY(file.open(QIODevice::WriteOnly)); + QByteArray ba = test_plist.toUtf8(); + QCOMPARE(file.write(ba), qint64(ba.size())); + } + QVERIFY(dir.cd(QLatin1String("MacOS"))); + QVERIFY(dir.exists(QLatin1String("app"))); + QProcess process; + process.start(dir.filePath("app")); + QCOMPARE(process.waitForFinished(), true); + QCOMPARE(process.exitStatus(), QProcess::NormalExit); +} +#endif + +QTEST_MAIN(tst_MacPlist) +#include "tst_macplist.moc" diff --git a/tests/auto/integrationtests/modeltest/dynamictreemodel.cpp b/tests/auto/integrationtests/modeltest/dynamictreemodel.cpp new file mode 100644 index 0000000000..2f8bb0a730 --- /dev/null +++ b/tests/auto/integrationtests/modeltest/dynamictreemodel.cpp @@ -0,0 +1,340 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Stephen Kelly <steveire@gmail.com> +** 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 "dynamictreemodel.h" + +#include <QtCore/QHash> +#include <QtCore/QList> +#include <QtCore/QTimer> +#include <QtCore/QDebug> + + +DynamicTreeModel::DynamicTreeModel(QObject *parent) + : QAbstractItemModel(parent), + nextId(1) +{ +} + +QModelIndex DynamicTreeModel::index(int row, int column, const QModelIndex &parent) const +{ +// if (column != 0) +// return QModelIndex(); + + + if ( column < 0 || row < 0 ) + return QModelIndex(); + + QList<QList<qint64> > childIdColumns = m_childItems.value(parent.internalId()); + + const qint64 grandParent = findParentId(parent.internalId()); + if (grandParent >= 0) { + QList<QList<qint64> > parentTable = m_childItems.value(grandParent); + if (parent.column() >= parentTable.size()) + qFatal("%s: parent.column() must be less than parentTable.size()", Q_FUNC_INFO); + QList<qint64> parentSiblings = parentTable.at(parent.column()); + if (parent.row() >= parentSiblings.size()) + qFatal("%s: parent.row() must be less than parentSiblings.size()", Q_FUNC_INFO); + } + + if (childIdColumns.size() == 0) + return QModelIndex(); + + if (column >= childIdColumns.size()) + return QModelIndex(); + + QList<qint64> rowIds = childIdColumns.at(column); + + if ( row >= rowIds.size()) + return QModelIndex(); + + qint64 id = rowIds.at(row); + + return createIndex(row, column, reinterpret_cast<void *>(id)); + +} + +qint64 DynamicTreeModel::findParentId(qint64 searchId) const +{ + if (searchId <= 0) + return -1; + + QHashIterator<qint64, QList<QList<qint64> > > i(m_childItems); + while (i.hasNext()) + { + i.next(); + QListIterator<QList<qint64> > j(i.value()); + while (j.hasNext()) + { + QList<qint64> l = j.next(); + if (l.contains(searchId)) + { + return i.key(); + } + } + } + return -1; +} + +QModelIndex DynamicTreeModel::parent(const QModelIndex &index) const +{ + if (!index.isValid()) + return QModelIndex(); + + qint64 searchId = index.internalId(); + qint64 parentId = findParentId(searchId); + // Will never happen for valid index, but what the hey... + if (parentId <= 0) + return QModelIndex(); + + qint64 grandParentId = findParentId(parentId); + if (grandParentId < 0) + grandParentId = 0; + + int column = 0; + QList<qint64> childList = m_childItems.value(grandParentId).at(column); + + int row = childList.indexOf(parentId); + + return createIndex(row, column, reinterpret_cast<void *>(parentId)); + +} + +int DynamicTreeModel::rowCount(const QModelIndex &index ) const +{ + QList<QList<qint64> > cols = m_childItems.value(index.internalId()); + + if (cols.size() == 0 ) + return 0; + + if (index.column() > 0) + return 0; + + return cols.at(0).size(); +} + +int DynamicTreeModel::columnCount(const QModelIndex &index ) const +{ +// Q_UNUSED(index); + return m_childItems.value(index.internalId()).size(); +} + +QVariant DynamicTreeModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (Qt::DisplayRole == role) + { + return m_items.value(index.internalId()); + } + return QVariant(); +} + +void DynamicTreeModel::clear() +{ + beginResetModel(); + m_items.clear(); + m_childItems.clear(); + nextId = 1; + endResetModel(); +} + + +ModelChangeCommand::ModelChangeCommand( DynamicTreeModel *model, QObject *parent ) + : QObject(parent), m_model(model), m_numCols(1), m_startRow(-1), m_endRow(-1) +{ + +} + +QModelIndex ModelChangeCommand::findIndex(QList<int> rows) +{ + const int col = 0; + QModelIndex parent = QModelIndex(); + QListIterator<int> i(rows); + while (i.hasNext()) + { + parent = m_model->index(i.next(), col, parent); + if (!parent.isValid()) + qFatal("%s: parent must be valid", Q_FUNC_INFO); + } + return parent; +} + +ModelInsertCommand::ModelInsertCommand(DynamicTreeModel *model, QObject *parent ) + : ModelChangeCommand(model, parent) +{ + +} + +void ModelInsertCommand::doCommand() +{ + QModelIndex parent = findIndex(m_rowNumbers); + m_model->beginInsertRows(parent, m_startRow, m_endRow); + qint64 parentId = parent.internalId(); + for (int row = m_startRow; row <= m_endRow; row++) + { + for(int col = 0; col < m_numCols; col++ ) + { + if (m_model->m_childItems[parentId].size() <= col) + { + m_model->m_childItems[parentId].append(QList<qint64>()); + } +// QString name = QUuid::createUuid().toString(); + qint64 id = m_model->newId(); + QString name = QString::number(id); + + m_model->m_items.insert(id, name); + m_model->m_childItems[parentId][col].insert(row, id); + + } + } + m_model->endInsertRows(); +} + + +ModelMoveCommand::ModelMoveCommand(DynamicTreeModel *model, QObject *parent) + : ModelChangeCommand(model, parent) +{ + +} +bool ModelMoveCommand::emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow) +{ + return m_model->beginMoveRows(srcParent, srcStart, srcEnd, destParent, destRow); +} + +void ModelMoveCommand::doCommand() +{ + QModelIndex srcParent = findIndex(m_rowNumbers); + QModelIndex destParent = findIndex(m_destRowNumbers); + + if (!emitPreSignal(srcParent, m_startRow, m_endRow, destParent, m_destRow)) + { + return; + } + + for (int column = 0; column < m_numCols; ++column) + { + QList<qint64> l = m_model->m_childItems.value(srcParent.internalId())[column].mid(m_startRow, m_endRow - m_startRow + 1 ); + + for (int i = m_startRow; i <= m_endRow ; i++) + { + m_model->m_childItems[srcParent.internalId()][column].removeAt(m_startRow); + } + int d; + if (m_destRow < m_startRow) + d = m_destRow; + else + { + if (srcParent == destParent) + d = m_destRow - (m_endRow - m_startRow + 1); + else + d = m_destRow - (m_endRow - m_startRow) + 1; + } + + foreach(const qint64 id, l) + { + m_model->m_childItems[destParent.internalId()][column].insert(d++, id); + } + } + + emitPostSignal(); +} + +void ModelMoveCommand::emitPostSignal() +{ + m_model->endMoveRows(); +} + +ModelResetCommand::ModelResetCommand(DynamicTreeModel* model, QObject* parent) + : ModelMoveCommand(model, parent) +{ + +} + +ModelResetCommand::~ModelResetCommand() +{ + +} + +bool ModelResetCommand::emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow) +{ + Q_UNUSED(srcParent); + Q_UNUSED(srcStart); + Q_UNUSED(srcEnd); + Q_UNUSED(destParent); + Q_UNUSED(destRow); + + return true; +} + +void ModelResetCommand::emitPostSignal() +{ + m_model->reset(); +} + +ModelResetCommandFixed::ModelResetCommandFixed(DynamicTreeModel* model, QObject* parent) + : ModelMoveCommand(model, parent) +{ + +} + +ModelResetCommandFixed::~ModelResetCommandFixed() +{ + +} + +bool ModelResetCommandFixed::emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow) +{ + Q_UNUSED(srcParent); + Q_UNUSED(srcStart); + Q_UNUSED(srcEnd); + Q_UNUSED(destParent); + Q_UNUSED(destRow); + + m_model->beginResetModel(); + return true; +} + +void ModelResetCommandFixed::emitPostSignal() +{ + m_model->endResetModel(); +} + diff --git a/tests/auto/integrationtests/modeltest/dynamictreemodel.h b/tests/auto/integrationtests/modeltest/dynamictreemodel.h new file mode 100644 index 0000000000..81ef80cc40 --- /dev/null +++ b/tests/auto/integrationtests/modeltest/dynamictreemodel.h @@ -0,0 +1,197 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Stephen Kelly <steveire@gmail.com> +** 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$ +** +****************************************************************************/ + +#ifndef DYNAMICTREEMODEL_H +#define DYNAMICTREEMODEL_H + +#include <QtCore/QAbstractItemModel> + +#include <QtCore/QHash> +#include <QtCore/QList> + + +class DynamicTreeModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + DynamicTreeModel(QObject *parent = 0); + + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &index) const; + int rowCount(const QModelIndex &index = QModelIndex()) const; + int columnCount(const QModelIndex &index = QModelIndex()) const; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + + void clear(); + +protected slots: + + /** + Finds the parent id of the string with id @p searchId. + + Returns -1 if not found. + */ + qint64 findParentId(qint64 searchId) const; + +private: + QHash<qint64, QString> m_items; + QHash<qint64, QList<QList<qint64> > > m_childItems; + qint64 nextId; + qint64 newId() { return nextId++; }; + + QModelIndex m_nextParentIndex; + int m_nextRow; + + int m_depth; + int maxDepth; + + friend class ModelInsertCommand; + friend class ModelMoveCommand; + friend class ModelResetCommand; + friend class ModelResetCommandFixed; + +}; + + +class ModelChangeCommand : public QObject +{ + Q_OBJECT +public: + + ModelChangeCommand( DynamicTreeModel *model, QObject *parent = 0 ); + + virtual ~ModelChangeCommand() {} + + void setAncestorRowNumbers(QList<int> rowNumbers) { m_rowNumbers = rowNumbers; } + + QModelIndex findIndex(QList<int> rows); + + void setStartRow(int row) { m_startRow = row; } + + void setEndRow(int row) { m_endRow = row; } + + void setNumCols(int cols) { m_numCols = cols; } + + virtual void doCommand() = 0; + +protected: + DynamicTreeModel* m_model; + QList<int> m_rowNumbers; + int m_numCols; + int m_startRow; + int m_endRow; + +}; + +typedef QList<ModelChangeCommand*> ModelChangeCommandList; + +class ModelInsertCommand : public ModelChangeCommand +{ + Q_OBJECT + +public: + + ModelInsertCommand(DynamicTreeModel *model, QObject *parent = 0 ); + virtual ~ModelInsertCommand() {} + + virtual void doCommand(); +}; + + +class ModelMoveCommand : public ModelChangeCommand +{ + Q_OBJECT +public: + ModelMoveCommand(DynamicTreeModel *model, QObject *parent); + + virtual ~ModelMoveCommand() {} + + virtual bool emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow); + + virtual void doCommand(); + + virtual void emitPostSignal(); + + void setDestAncestors( QList<int> rows ) { m_destRowNumbers = rows; } + + void setDestRow(int row) { m_destRow = row; } + +protected: + QList<int> m_destRowNumbers; + int m_destRow; +}; + +/** + A command which does a move and emits a reset signal. +*/ +class ModelResetCommand : public ModelMoveCommand +{ + Q_OBJECT +public: + ModelResetCommand(DynamicTreeModel* model, QObject* parent = 0); + + virtual ~ModelResetCommand(); + + virtual bool emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow); + virtual void emitPostSignal(); + +}; + +/** + A command which does a move and emits a beginResetModel and endResetModel signals. +*/ +class ModelResetCommandFixed : public ModelMoveCommand +{ + Q_OBJECT +public: + ModelResetCommandFixed(DynamicTreeModel* model, QObject* parent = 0); + + virtual ~ModelResetCommandFixed(); + + virtual bool emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow); + virtual void emitPostSignal(); + +}; + + +#endif diff --git a/tests/auto/integrationtests/modeltest/modeltest.cpp b/tests/auto/integrationtests/modeltest/modeltest.cpp new file mode 100644 index 0000000000..76803dfbe5 --- /dev/null +++ b/tests/auto/integrationtests/modeltest/modeltest.cpp @@ -0,0 +1,564 @@ +/**************************************************************************** +** +** 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 <QtGui/QtGui> + +#include "modeltest.h" + +#include <QtTest/QtTest> + +Q_DECLARE_METATYPE ( QModelIndex ) + +/*! + Connect to all of the models signals. Whenever anything happens recheck everything. +*/ +ModelTest::ModelTest ( QAbstractItemModel *_model, QObject *parent ) : QObject ( parent ), model ( _model ), fetchingMore ( false ) +{ + if (!model) + qFatal("%s: model must not be null", Q_FUNC_INFO); + + connect ( model, SIGNAL ( columnsAboutToBeInserted ( const QModelIndex &, int, int ) ), + this, SLOT ( runAllTests() ) ); + connect ( model, SIGNAL ( columnsAboutToBeRemoved ( const QModelIndex &, int, int ) ), + this, SLOT ( runAllTests() ) ); + connect ( model, SIGNAL ( columnsInserted ( const QModelIndex &, int, int ) ), + this, SLOT ( runAllTests() ) ); + connect ( model, SIGNAL ( columnsRemoved ( const QModelIndex &, int, int ) ), + this, SLOT ( runAllTests() ) ); + connect ( model, SIGNAL ( dataChanged ( const QModelIndex &, const QModelIndex & ) ), + this, SLOT ( runAllTests() ) ); + connect ( model, SIGNAL ( headerDataChanged ( Qt::Orientation, int, int ) ), + this, SLOT ( runAllTests() ) ); + connect ( model, SIGNAL ( layoutAboutToBeChanged () ), this, SLOT ( runAllTests() ) ); + connect ( model, SIGNAL ( layoutChanged () ), this, SLOT ( runAllTests() ) ); + connect ( model, SIGNAL ( modelReset () ), this, SLOT ( runAllTests() ) ); + connect ( model, SIGNAL ( rowsAboutToBeInserted ( const QModelIndex &, int, int ) ), + this, SLOT ( runAllTests() ) ); + connect ( model, SIGNAL ( rowsAboutToBeRemoved ( const QModelIndex &, int, int ) ), + this, SLOT ( runAllTests() ) ); + connect ( model, SIGNAL ( rowsInserted ( const QModelIndex &, int, int ) ), + this, SLOT ( runAllTests() ) ); + connect ( model, SIGNAL ( rowsRemoved ( const QModelIndex &, int, int ) ), + this, SLOT ( runAllTests() ) ); + + // Special checks for inserting/removing + connect ( model, SIGNAL ( layoutAboutToBeChanged() ), + this, SLOT ( layoutAboutToBeChanged() ) ); + connect ( model, SIGNAL ( layoutChanged() ), + this, SLOT ( layoutChanged() ) ); + + connect ( model, SIGNAL ( rowsAboutToBeInserted ( const QModelIndex &, int, int ) ), + this, SLOT ( rowsAboutToBeInserted ( const QModelIndex &, int, int ) ) ); + connect ( model, SIGNAL ( rowsAboutToBeRemoved ( const QModelIndex &, int, int ) ), + this, SLOT ( rowsAboutToBeRemoved ( const QModelIndex &, int, int ) ) ); + connect ( model, SIGNAL ( rowsInserted ( const QModelIndex &, int, int ) ), + this, SLOT ( rowsInserted ( const QModelIndex &, int, int ) ) ); + connect ( model, SIGNAL ( rowsRemoved ( const QModelIndex &, int, int ) ), + this, SLOT ( rowsRemoved ( const QModelIndex &, int, int ) ) ); + + runAllTests(); +} + +void ModelTest::runAllTests() +{ + if ( fetchingMore ) + return; + nonDestructiveBasicTest(); + rowCount(); + columnCount(); + hasIndex(); + index(); + parent(); + data(); +} + +/*! + nonDestructiveBasicTest tries to call a number of the basic functions (not all) + to make sure the model doesn't outright segfault, testing the functions that makes sense. +*/ +void ModelTest::nonDestructiveBasicTest() +{ + QVERIFY( model->buddy ( QModelIndex() ) == QModelIndex() ); + model->canFetchMore ( QModelIndex() ); + QVERIFY( model->columnCount ( QModelIndex() ) >= 0 ); + QVERIFY( model->data ( QModelIndex() ) == QVariant() ); + fetchingMore = true; + model->fetchMore ( QModelIndex() ); + fetchingMore = false; + Qt::ItemFlags flags = model->flags ( QModelIndex() ); + QVERIFY( flags == Qt::ItemIsDropEnabled || flags == 0 ); + model->hasChildren ( QModelIndex() ); + model->hasIndex ( 0, 0 ); + model->headerData ( 0, Qt::Horizontal ); + model->index ( 0, 0 ); + model->itemData ( QModelIndex() ); + QVariant cache; + model->match ( QModelIndex(), -1, cache ); + model->mimeTypes(); + QVERIFY( model->parent ( QModelIndex() ) == QModelIndex() ); + QVERIFY( model->rowCount() >= 0 ); + QVariant variant; + model->setData ( QModelIndex(), variant, -1 ); + model->setHeaderData ( -1, Qt::Horizontal, QVariant() ); + model->setHeaderData ( 999999, Qt::Horizontal, QVariant() ); + QMap<int, QVariant> roles; + model->sibling ( 0, 0, QModelIndex() ); + model->span ( QModelIndex() ); + model->supportedDropActions(); +} + +/*! + Tests model's implementation of QAbstractItemModel::rowCount() and hasChildren() + + Models that are dynamically populated are not as fully tested here. + */ +void ModelTest::rowCount() +{ +// qDebug() << "rc"; + // check top row + QModelIndex topIndex = model->index ( 0, 0, QModelIndex() ); + int rows = model->rowCount ( topIndex ); + QVERIFY( rows >= 0 ); + if ( rows > 0 ) + QVERIFY( model->hasChildren ( topIndex ) ); + + QModelIndex secondLevelIndex = model->index ( 0, 0, topIndex ); + if ( secondLevelIndex.isValid() ) { // not the top level + // check a row count where parent is valid + rows = model->rowCount ( secondLevelIndex ); + QVERIFY( rows >= 0 ); + if ( rows > 0 ) + QVERIFY( model->hasChildren ( secondLevelIndex ) ); + } + + // The models rowCount() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::columnCount() and hasChildren() + */ +void ModelTest::columnCount() +{ + // check top row + QModelIndex topIndex = model->index ( 0, 0, QModelIndex() ); + QVERIFY( model->columnCount ( topIndex ) >= 0 ); + + // check a column count where parent is valid + QModelIndex childIndex = model->index ( 0, 0, topIndex ); + if ( childIndex.isValid() ) + QVERIFY( model->columnCount ( childIndex ) >= 0 ); + + // columnCount() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::hasIndex() + */ +void ModelTest::hasIndex() +{ +// qDebug() << "hi"; + // Make sure that invalid values returns an invalid index + QVERIFY( !model->hasIndex ( -2, -2 ) ); + QVERIFY( !model->hasIndex ( -2, 0 ) ); + QVERIFY( !model->hasIndex ( 0, -2 ) ); + + int rows = model->rowCount(); + int columns = model->columnCount(); + + // check out of bounds + QVERIFY( !model->hasIndex ( rows, columns ) ); + QVERIFY( !model->hasIndex ( rows + 1, columns + 1 ) ); + + if ( rows > 0 ) + QVERIFY( model->hasIndex ( 0, 0 ) ); + + // hasIndex() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::index() + */ +void ModelTest::index() +{ +// qDebug() << "i"; + // Make sure that invalid values returns an invalid index + QVERIFY( model->index ( -2, -2 ) == QModelIndex() ); + QVERIFY( model->index ( -2, 0 ) == QModelIndex() ); + QVERIFY( model->index ( 0, -2 ) == QModelIndex() ); + + int rows = model->rowCount(); + int columns = model->columnCount(); + + if ( rows == 0 ) + return; + + // Catch off by one errors + QVERIFY( model->index ( rows, columns ) == QModelIndex() ); + QVERIFY( model->index ( 0, 0 ).isValid() ); + + // Make sure that the same index is *always* returned + QModelIndex a = model->index ( 0, 0 ); + QModelIndex b = model->index ( 0, 0 ); + QVERIFY( a == b ); + + // index() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::parent() + */ +void ModelTest::parent() +{ +// qDebug() << "p"; + // Make sure the model wont crash and will return an invalid QModelIndex + // when asked for the parent of an invalid index. + QVERIFY( model->parent ( QModelIndex() ) == QModelIndex() ); + + if ( model->rowCount() == 0 ) + return; + + // Column 0 | Column 1 | + // QModelIndex() | | + // \- topIndex | topIndex1 | + // \- childIndex | childIndex1 | + + // Common error test #1, make sure that a top level index has a parent + // that is a invalid QModelIndex. + QModelIndex topIndex = model->index ( 0, 0, QModelIndex() ); + QVERIFY( model->parent ( topIndex ) == QModelIndex() ); + + // Common error test #2, make sure that a second level index has a parent + // that is the first level index. + if ( model->rowCount ( topIndex ) > 0 ) { + QModelIndex childIndex = model->index ( 0, 0, topIndex ); + QVERIFY( model->parent ( childIndex ) == topIndex ); + } + + // Common error test #3, the second column should NOT have the same children + // as the first column in a row. + // Usually the second column shouldn't have children. + QModelIndex topIndex1 = model->index ( 0, 1, QModelIndex() ); + if ( model->rowCount ( topIndex1 ) > 0 ) { + QModelIndex childIndex = model->index ( 0, 0, topIndex ); + QModelIndex childIndex1 = model->index ( 0, 0, topIndex1 ); + QVERIFY( childIndex != childIndex1 ); + } + + // Full test, walk n levels deep through the model making sure that all + // parent's children correctly specify their parent. + checkChildren ( QModelIndex() ); +} + +/*! + Called from the parent() test. + + A model that returns an index of parent X should also return X when asking + for the parent of the index. + + This recursive function does pretty extensive testing on the whole model in an + effort to catch edge cases. + + This function assumes that rowCount(), columnCount() and index() already work. + If they have a bug it will point it out, but the above tests should have already + found the basic bugs because it is easier to figure out the problem in + those tests then this one. + */ +void ModelTest::checkChildren ( const QModelIndex &parent, int currentDepth ) +{ + // First just try walking back up the tree. + QModelIndex p = parent; + while ( p.isValid() ) + p = p.parent(); + + // For models that are dynamically populated + if ( model->canFetchMore ( parent ) ) { + fetchingMore = true; + model->fetchMore ( parent ); + fetchingMore = false; + } + + int rows = model->rowCount ( parent ); + int columns = model->columnCount ( parent ); + + if ( rows > 0 ) + QVERIFY( model->hasChildren ( parent ) ); + + // Some further testing against rows(), columns(), and hasChildren() + QVERIFY( rows >= 0 ); + QVERIFY( columns >= 0 ); + if ( rows > 0 ) + QVERIFY( model->hasChildren ( parent ) ); + + //qDebug() << "parent:" << model->data(parent).toString() << "rows:" << rows + // << "columns:" << columns << "parent column:" << parent.column(); + + QVERIFY( !model->hasIndex ( rows + 1, 0, parent ) ); + for ( int r = 0; r < rows; ++r ) { + if ( model->canFetchMore ( parent ) ) { + fetchingMore = true; + model->fetchMore ( parent ); + fetchingMore = false; + } + QVERIFY( !model->hasIndex ( r, columns + 1, parent ) ); + for ( int c = 0; c < columns; ++c ) { + QVERIFY( model->hasIndex ( r, c, parent ) ); + QModelIndex index = model->index ( r, c, parent ); + // rowCount() and columnCount() said that it existed... + QVERIFY( index.isValid() ); + + // index() should always return the same index when called twice in a row + QModelIndex modifiedIndex = model->index ( r, c, parent ); + QVERIFY( index == modifiedIndex ); + + // Make sure we get the same index if we request it twice in a row + QModelIndex a = model->index ( r, c, parent ); + QModelIndex b = model->index ( r, c, parent ); + QVERIFY( a == b ); + + // Some basic checking on the index that is returned + QVERIFY( index.model() == model ); + QCOMPARE( index.row(), r ); + QCOMPARE( index.column(), c ); + // While you can technically return a QVariant usually this is a sign + // of a bug in data(). Disable if this really is ok in your model. +// QVERIFY( model->data ( index, Qt::DisplayRole ).isValid() ); + + // If the next test fails here is some somewhat useful debug you play with. + + if (model->parent(index) != parent) { + qDebug() << r << c << currentDepth << model->data(index).toString() + << model->data(parent).toString(); + qDebug() << index << parent << model->parent(index); +// And a view that you can even use to show the model. +// QTreeView view; +// view.setModel(model); +// view.show(); + } + + // Check that we can get back our real parent. + QCOMPARE( model->parent ( index ), parent ); + + // recursively go down the children + if ( model->hasChildren ( index ) && currentDepth < 10 ) { + //qDebug() << r << c << "has children" << model->rowCount(index); + checkChildren ( index, ++currentDepth ); + }/* else { if (currentDepth >= 10) qDebug() << "checked 10 deep"; };*/ + + // make sure that after testing the children that the index doesn't change. + QModelIndex newerIndex = model->index ( r, c, parent ); + QVERIFY( index == newerIndex ); + } + } +} + +/*! + Tests model's implementation of QAbstractItemModel::data() + */ +void ModelTest::data() +{ + // Invalid index should return an invalid qvariant + QVERIFY( !model->data ( QModelIndex() ).isValid() ); + + if ( model->rowCount() == 0 ) + return; + + // A valid index should have a valid QVariant data + QVERIFY( model->index ( 0, 0 ).isValid() ); + + // shouldn't be able to set data on an invalid index + QVERIFY( !model->setData ( QModelIndex(), QLatin1String ( "foo" ), Qt::DisplayRole ) ); + + // General Purpose roles that should return a QString + QVariant variant = model->data ( model->index ( 0, 0 ), Qt::ToolTipRole ); + if ( variant.isValid() ) { + QVERIFY( qVariantCanConvert<QString> ( variant ) ); + } + variant = model->data ( model->index ( 0, 0 ), Qt::StatusTipRole ); + if ( variant.isValid() ) { + QVERIFY( qVariantCanConvert<QString> ( variant ) ); + } + variant = model->data ( model->index ( 0, 0 ), Qt::WhatsThisRole ); + if ( variant.isValid() ) { + QVERIFY( qVariantCanConvert<QString> ( variant ) ); + } + + // General Purpose roles that should return a QSize + variant = model->data ( model->index ( 0, 0 ), Qt::SizeHintRole ); + if ( variant.isValid() ) { + QVERIFY( qVariantCanConvert<QSize> ( variant ) ); + } + + // General Purpose roles that should return a QFont + QVariant fontVariant = model->data ( model->index ( 0, 0 ), Qt::FontRole ); + if ( fontVariant.isValid() ) { + QVERIFY( qVariantCanConvert<QFont> ( fontVariant ) ); + } + + // Check that the alignment is one we know about + QVariant textAlignmentVariant = model->data ( model->index ( 0, 0 ), Qt::TextAlignmentRole ); + if ( textAlignmentVariant.isValid() ) { + int alignment = textAlignmentVariant.toInt(); + QCOMPARE( alignment, ( alignment & ( Qt::AlignHorizontal_Mask | Qt::AlignVertical_Mask ) ) ); + } + + // General Purpose roles that should return a QColor + QVariant colorVariant = model->data ( model->index ( 0, 0 ), Qt::BackgroundColorRole ); + if ( colorVariant.isValid() ) { + QVERIFY( qVariantCanConvert<QColor> ( colorVariant ) ); + } + + colorVariant = model->data ( model->index ( 0, 0 ), Qt::TextColorRole ); + if ( colorVariant.isValid() ) { + QVERIFY( qVariantCanConvert<QColor> ( colorVariant ) ); + } + + // Check that the "check state" is one we know about. + QVariant checkStateVariant = model->data ( model->index ( 0, 0 ), Qt::CheckStateRole ); + if ( checkStateVariant.isValid() ) { + int state = checkStateVariant.toInt(); + QVERIFY( state == Qt::Unchecked || + state == Qt::PartiallyChecked || + state == Qt::Checked ); + } +} + +/*! + Store what is about to be inserted to make sure it actually happens + + \sa rowsInserted() + */ +void ModelTest::rowsAboutToBeInserted ( const QModelIndex &parent, int start, int /* end */) +{ +// Q_UNUSED(end); +// qDebug() << "rowsAboutToBeInserted" << "start=" << start << "end=" << end << "parent=" << model->data ( parent ).toString() +// << "current count of parent=" << model->rowCount ( parent ); // << "display of last=" << model->data( model->index(start-1, 0, parent) ); +// qDebug() << model->index(start-1, 0, parent) << model->data( model->index(start-1, 0, parent) ); + Changing c; + c.parent = parent; + c.oldSize = model->rowCount ( parent ); + c.last = model->data ( model->index ( start - 1, 0, parent ) ); + c.next = model->data ( model->index ( start, 0, parent ) ); + insert.push ( c ); +} + +/*! + Confirm that what was said was going to happen actually did + + \sa rowsAboutToBeInserted() + */ +void ModelTest::rowsInserted ( const QModelIndex & parent, int start, int end ) +{ + Changing c = insert.pop(); + QVERIFY( c.parent == parent ); +// qDebug() << "rowsInserted" << "start=" << start << "end=" << end << "oldsize=" << c.oldSize +// << "parent=" << model->data ( parent ).toString() << "current rowcount of parent=" << model->rowCount ( parent ); + +// for (int ii=start; ii <= end; ii++) +// { +// qDebug() << "itemWasInserted:" << ii << model->data ( model->index ( ii, 0, parent )); +// } +// qDebug(); + + QVERIFY( c.oldSize + ( end - start + 1 ) == model->rowCount ( parent ) ); + QVERIFY( c.last == model->data ( model->index ( start - 1, 0, c.parent ) ) ); + + if (c.next != model->data(model->index(end + 1, 0, c.parent))) { + qDebug() << start << end; + for (int i=0; i < model->rowCount(); ++i) + qDebug() << model->index(i, 0).data().toString(); + qDebug() << c.next << model->data(model->index(end + 1, 0, c.parent)); + } + + QVERIFY( c.next == model->data ( model->index ( end + 1, 0, c.parent ) ) ); +} + +void ModelTest::layoutAboutToBeChanged() +{ + for ( int i = 0; i < qBound ( 0, model->rowCount(), 100 ); ++i ) + changing.append ( QPersistentModelIndex ( model->index ( i, 0 ) ) ); +} + +void ModelTest::layoutChanged() +{ + for ( int i = 0; i < changing.count(); ++i ) { + QPersistentModelIndex p = changing[i]; + QVERIFY( p == model->index ( p.row(), p.column(), p.parent() ) ); + } + changing.clear(); +} + +/*! + Store what is about to be inserted to make sure it actually happens + + \sa rowsRemoved() + */ +void ModelTest::rowsAboutToBeRemoved ( const QModelIndex &parent, int start, int end ) +{ +qDebug() << "ratbr" << parent << start << end; + Changing c; + c.parent = parent; + c.oldSize = model->rowCount ( parent ); + c.last = model->data ( model->index ( start - 1, 0, parent ) ); + c.next = model->data ( model->index ( end + 1, 0, parent ) ); + remove.push ( c ); +} + +/*! + Confirm that what was said was going to happen actually did + + \sa rowsAboutToBeRemoved() + */ +void ModelTest::rowsRemoved ( const QModelIndex & parent, int start, int end ) +{ + qDebug() << "rr" << parent << start << end; + Changing c = remove.pop(); + QVERIFY( c.parent == parent ); + QVERIFY( c.oldSize - ( end - start + 1 ) == model->rowCount ( parent ) ); + QVERIFY( c.last == model->data ( model->index ( start - 1, 0, c.parent ) ) ); + QVERIFY( c.next == model->data ( model->index ( start, 0, c.parent ) ) ); +} + + diff --git a/tests/auto/integrationtests/modeltest/modeltest.h b/tests/auto/integrationtests/modeltest/modeltest.h new file mode 100644 index 0000000000..e4ef478c5e --- /dev/null +++ b/tests/auto/integrationtests/modeltest/modeltest.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + + +#ifndef MODELTEST_H +#define MODELTEST_H + +#include <QtCore/QObject> +#include <QtCore/QAbstractItemModel> +#include <QtCore/QStack> + +class ModelTest : public QObject +{ + Q_OBJECT + +public: + ModelTest( QAbstractItemModel *model, QObject *parent = 0 ); + +private Q_SLOTS: + void nonDestructiveBasicTest(); + void rowCount(); + void columnCount(); + void hasIndex(); + void index(); + void parent(); + void data(); + +protected Q_SLOTS: + void runAllTests(); + void layoutAboutToBeChanged(); + void layoutChanged(); + void rowsAboutToBeInserted( const QModelIndex &parent, int start, int end ); + void rowsInserted( const QModelIndex & parent, int start, int end ); + void rowsAboutToBeRemoved( const QModelIndex &parent, int start, int end ); + void rowsRemoved( const QModelIndex & parent, int start, int end ); + +private: + void checkChildren( const QModelIndex &parent, int currentDepth = 0 ); + + QAbstractItemModel *model; + + struct Changing { + QModelIndex parent; + int oldSize; + QVariant last; + QVariant next; + }; + QStack<Changing> insert; + QStack<Changing> remove; + + bool fetchingMore; + + QList<QPersistentModelIndex> changing; +}; + +#endif diff --git a/tests/auto/integrationtests/modeltest/modeltest.pro b/tests/auto/integrationtests/modeltest/modeltest.pro new file mode 100644 index 0000000000..d4a8ba971d --- /dev/null +++ b/tests/auto/integrationtests/modeltest/modeltest.pro @@ -0,0 +1,7 @@ +load(qttest_p4) +QT += widgets +SOURCES += tst_modeltest.cpp modeltest.cpp dynamictreemodel.cpp +HEADERS += modeltest.h dynamictreemodel.h + + + diff --git a/tests/auto/integrationtests/modeltest/tst_modeltest.cpp b/tests/auto/integrationtests/modeltest/tst_modeltest.cpp new file mode 100644 index 0000000000..434537a81f --- /dev/null +++ b/tests/auto/integrationtests/modeltest/tst_modeltest.cpp @@ -0,0 +1,342 @@ +/**************************************************************************** +** +** 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 <QtGui/QtGui> +#include <QtWidgets/QtWidgets> + +#include "modeltest.h" +#include "dynamictreemodel.h" + + +class tst_ModelTest : public QObject +{ + Q_OBJECT + +public: + tst_ModelTest() {} + virtual ~tst_ModelTest() {} + +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + +private slots: + void stringListModel(); + void treeWidgetModel(); + void standardItemModel(); + void testInsertThroughProxy(); + void moveSourceItems(); + void testResetThroughProxy(); +}; + + + +void tst_ModelTest::initTestCase() +{ +} + +void tst_ModelTest::cleanupTestCase() +{ +} + +void tst_ModelTest::init() +{ + +} + +void tst_ModelTest::cleanup() +{ +} +/* + tests +*/ + +void tst_ModelTest::stringListModel() +{ + QStringListModel model; + QSortFilterProxyModel proxy; + + ModelTest t1(&model); + ModelTest t2(&proxy); + + proxy.setSourceModel(&model); + + model.setStringList(QStringList() << "2" << "3" << "1"); + model.setStringList(QStringList() << "a" << "e" << "plop" << "b" << "c" ); + + proxy.setDynamicSortFilter(true); + proxy.setFilterRegExp(QRegExp("[^b]")); +} + +void tst_ModelTest::treeWidgetModel() +{ + QTreeWidget widget; + + ModelTest t1(widget.model()); + + QTreeWidgetItem *root = new QTreeWidgetItem(&widget, QStringList("root")); + for (int i = 0; i < 20; ++i) { + new QTreeWidgetItem(root, QStringList(QString::number(i))); + } + QTreeWidgetItem *remove = root->child(2); + root->removeChild(remove); + QTreeWidgetItem *parent = new QTreeWidgetItem(&widget, QStringList("parent")); + new QTreeWidgetItem(parent, QStringList("child")); + widget.setItemHidden(parent, true); + + widget.sortByColumn(0); +} + +void tst_ModelTest::standardItemModel() +{ + QStandardItemModel model(10,10); + QSortFilterProxyModel proxy; + + + ModelTest t1(&model); + ModelTest t2(&proxy); + + proxy.setSourceModel(&model); + + model.insertRows(2, 5); + model.removeRows(4, 5); + + model.insertColumns(2, 5); + model.removeColumns(4, 5); + + model.insertRows(0,5, model.index(1,1)); + model.insertColumns(0,5, model.index(1,3)); +} + +void tst_ModelTest::testInsertThroughProxy() +{ + DynamicTreeModel *model = new DynamicTreeModel(this); + + QSortFilterProxyModel *proxy = new QSortFilterProxyModel(this); + proxy->setSourceModel(model); + + new ModelTest(proxy, this); + + ModelInsertCommand *insertCommand = new ModelInsertCommand(model, this); + insertCommand->setNumCols(4); + insertCommand->setStartRow(0); + insertCommand->setEndRow(9); + // Parent is QModelIndex() + insertCommand->doCommand(); + + insertCommand = new ModelInsertCommand(model, this); + insertCommand->setNumCols(4); + insertCommand->setAncestorRowNumbers(QList<int>() << 5); + insertCommand->setStartRow(0); + insertCommand->setEndRow(9); + insertCommand->doCommand(); + + ModelMoveCommand *moveCommand = new ModelMoveCommand(model, this); + moveCommand->setNumCols(4); + moveCommand->setStartRow(0); + moveCommand->setEndRow(0); + moveCommand->setDestRow(9); + moveCommand->setDestAncestors(QList<int>() << 5); + moveCommand->doCommand(); +} + +/** + Makes the persistent index list publicly accessible +*/ +class AccessibleProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT +public: + AccessibleProxyModel(QObject *parent = 0) : QSortFilterProxyModel(parent) {} + + QModelIndexList persistent() + { + return persistentIndexList(); + } +}; + +class ObservingObject : public QObject +{ + Q_OBJECT +public: + ObservingObject(AccessibleProxyModel *proxy, QObject *parent = 0) + : QObject(parent) + , m_proxy(proxy) + , storePersistentFailureCount(0) + , checkPersistentFailureCount(0) + { + connect(m_proxy, SIGNAL(layoutAboutToBeChanged()), SLOT(storePersistent())); + connect(m_proxy, SIGNAL(layoutChanged()), SLOT(checkPersistent())); + } + +public slots: + + void storePersistent(const QModelIndex &parent) + { + for (int row = 0; row < m_proxy->rowCount(parent); ++row) { + QModelIndex proxyIndex = m_proxy->index(row, 0, parent); + QModelIndex sourceIndex = m_proxy->mapToSource(proxyIndex); + if (!proxyIndex.isValid()) { + qWarning("%s: Invalid proxy index", Q_FUNC_INFO); + ++storePersistentFailureCount; + } + if (!sourceIndex.isValid()) { + qWarning("%s: invalid source index", Q_FUNC_INFO); + ++storePersistentFailureCount; + } + m_persistentSourceIndexes.append(sourceIndex); + m_persistentProxyIndexes.append(proxyIndex); + if (m_proxy->hasChildren(proxyIndex)) + storePersistent(proxyIndex); + } + } + + void storePersistent() + { + // This method is called from layoutAboutToBeChanged. Persistent indexes should be valid + foreach(const QModelIndex &idx, m_persistentProxyIndexes) + if (!idx.isValid()) { + qWarning("%s: persistentProxyIndexes contains invalid index", Q_FUNC_INFO); + ++storePersistentFailureCount; + } + + if (!m_proxy->persistent().isEmpty()) { + qWarning("%s: proxy should have no persistent indexes when storePersistent called", + Q_FUNC_INFO); + ++storePersistentFailureCount; + } + storePersistent(QModelIndex()); + if (m_proxy->persistent().isEmpty()) { + qWarning("%s: proxy should have persistent index after storePersistent called", + Q_FUNC_INFO); + ++storePersistentFailureCount; + } + } + + void checkPersistent() + { + for (int row = 0; row < m_persistentProxyIndexes.size(); ++row) { + QModelIndex updatedProxy = m_persistentProxyIndexes.at(row); + QModelIndex updatedSource = m_persistentSourceIndexes.at(row); + } + for (int row = 0; row < m_persistentProxyIndexes.size(); ++row) { + QModelIndex updatedProxy = m_persistentProxyIndexes.at(row); + QModelIndex updatedSource = m_persistentSourceIndexes.at(row); + if (m_proxy->mapToSource(updatedProxy) != updatedSource) { + qWarning("%s: check failed at row %d", Q_FUNC_INFO, row); + ++checkPersistentFailureCount; + } + } + m_persistentSourceIndexes.clear(); + m_persistentProxyIndexes.clear(); + } + +private: + AccessibleProxyModel *m_proxy; + QList<QPersistentModelIndex> m_persistentSourceIndexes; + QList<QPersistentModelIndex> m_persistentProxyIndexes; +public: + int storePersistentFailureCount; + int checkPersistentFailureCount; +}; + +void tst_ModelTest::moveSourceItems() +{ + DynamicTreeModel *model = new DynamicTreeModel(this); + AccessibleProxyModel *proxy = new AccessibleProxyModel(this); + proxy->setSourceModel(model); + + ModelInsertCommand *insertCommand = new ModelInsertCommand(model, this); + insertCommand->setStartRow(0); + insertCommand->setEndRow(2); + insertCommand->doCommand(); + + insertCommand = new ModelInsertCommand(model, this); + insertCommand->setAncestorRowNumbers(QList<int>() << 1); + insertCommand->setStartRow(0); + insertCommand->setEndRow(2); + insertCommand->doCommand(); + + ObservingObject observer(proxy); + + ModelMoveCommand *moveCommand = new ModelMoveCommand(model, this); + moveCommand->setStartRow(0); + moveCommand->setEndRow(0); + moveCommand->setDestAncestors(QList<int>() << 1); + moveCommand->setDestRow(0); + moveCommand->doCommand(); + + QCOMPARE(observer.storePersistentFailureCount, 0); + QCOMPARE(observer.checkPersistentFailureCount, 0); +} + +void tst_ModelTest::testResetThroughProxy() +{ + DynamicTreeModel *model = new DynamicTreeModel(this); + + ModelInsertCommand *insertCommand = new ModelInsertCommand(model, this); + insertCommand->setStartRow(0); + insertCommand->setEndRow(2); + insertCommand->doCommand(); + + QPersistentModelIndex persistent = model->index(0, 0); + + AccessibleProxyModel *proxy = new AccessibleProxyModel(this); + proxy->setSourceModel(model); + + ObservingObject observer(proxy); + observer.storePersistent(); + + ModelResetCommand *resetCommand = new ModelResetCommand(model, this); + resetCommand->setNumCols(0); + resetCommand->doCommand(); + + QCOMPARE(observer.storePersistentFailureCount, 0); + QCOMPARE(observer.checkPersistentFailureCount, 0); +} + + +QTEST_MAIN(tst_ModelTest) +#include "tst_modeltest.moc" diff --git a/tests/auto/integrationtests/networkselftest/networkselftest.pro b/tests/auto/integrationtests/networkselftest/networkselftest.pro new file mode 100644 index 0000000000..4440dc6cfd --- /dev/null +++ b/tests/auto/integrationtests/networkselftest/networkselftest.pro @@ -0,0 +1,16 @@ +load(qttest_p4) + +SOURCES += tst_networkselftest.cpp +QT = core network + +wince*: { + addFiles.files = rfc3252.txt + addFiles.path = . + DEPLOYMENT += addFiles + DEFINES += SRCDIR=\\\"\\\" +} else:vxworks*: { + DEFINES += SRCDIR=\\\"\\\" +} else { + DEFINES += SRCDIR=\\\"$$PWD/\\\" +} + diff --git a/tests/auto/integrationtests/networkselftest/rfc3252.txt b/tests/auto/integrationtests/networkselftest/rfc3252.txt new file mode 100644 index 0000000000..b80c61bf0a --- /dev/null +++ b/tests/auto/integrationtests/networkselftest/rfc3252.txt @@ -0,0 +1,899 @@ + + + + + + +Network Working Group H. Kennedy +Request for Comments: 3252 Mimezine +Category: Informational 1 April 2002 + + + Binary Lexical Octet Ad-hoc Transport + +Status of this Memo + + This memo provides information for the Internet community. It does + not specify an Internet standard of any kind. Distribution of this + memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (2002). All Rights Reserved. + +Abstract + + This document defines a reformulation of IP and two transport layer + protocols (TCP and UDP) as XML applications. + +1. Introduction + +1.1. Overview + + This document describes the Binary Lexical Octet Ad-hoc Transport + (BLOAT): a reformulation of a widely-deployed network-layer protocol + (IP [RFC791]), and two associated transport layer protocols (TCP + [RFC793] and UDP [RFC768]) as XML [XML] applications. It also + describes methods for transporting BLOAT over Ethernet and IEEE 802 + networks as well as encapsulating BLOAT in IP for gatewaying BLOAT + across the public Internet. + +1.2. Motivation + + The wild popularity of XML as a basis for application-level protocols + such as the Blocks Extensible Exchange Protocol [RFC3080], the Simple + Object Access Protocol [SOAP], and Jabber [JABBER] prompted + investigation into the possibility of extending the use of XML in the + protocol stack. Using XML at both the transport and network layer in + addition to the application layer would provide for an amazing amount + of power and flexibility while removing dependencies on proprietary + and hard-to-understand binary protocols. This protocol unification + would also allow applications to use a single XML parser for all + aspects of their operation, eliminating developer time spent figuring + out the intricacies of each new protocol, and moving the hard work of + + + + +Kennedy Informational [Page 1] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + parsing to the XML toolset. The use of XML also mitigates concerns + over "network vs. host" byte ordering which is at the root of many + network application bugs. + +1.3. Relation to Existing Protocols + + The reformulations specified in this RFC follow as closely as + possible the spirit of the RFCs on which they are based, and so MAY + contain elements or attributes that would not be needed in a pure + reworking (e.g. length attributes, which are implicit in XML.) + + The layering of network and transport protocols are maintained in + this RFC despite the optimizations that could be made if the line + were somewhat blurred (i.e. merging TCP and IP into a single, larger + element in the DTD) in order to foster future use of this protocol as + a basis for reformulating other protocols (such as ICMP.) + + Other than the encoding, the behavioral aspects of each of the + existing protocols remain unchanged. Routing, address spaces, TCP + congestion control, etc. behave as specified in the extant standards. + Adapting to new standards and experimental algorithm heuristics for + improving performance will become much easier once the move to BLOAT + has been completed. + +1.4. Requirement Levels + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in BCP 14, RFC 2119 + [RFC2119]. + +2. IPoXML + + This protocol MUST be implemented to be compliant with this RFC. + IPoXML is the root protocol REQUIRED for effective use of TCPoXML + (section 3.) and higher-level application protocols. + + The DTD for this document type can be found in section 7.1. + + The routing of IPoXML can be easily implemented on hosts with an XML + parser, as the regular structure lends itself handily to parsing and + validation of the document/datagram and then processing the + destination address, TTL, and checksum before sending it on to its + next-hop. + + The reformulation of IPv4 was chosen over IPv6 [RFC2460] due to the + wider deployment of IPv4 and the fact that implementing IPv6 as XML + would have exceeded the 1500 byte Ethernet MTU. + + + +Kennedy Informational [Page 2] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + All BLOAT implementations MUST use - and specify - the UTF-8 encoding + of RFC 2279 [RFC2279]. All BLOAT document/datagrams MUST be well- + formed and include the XMLDecl. + +2.1. IP Description + + A number of items have changed (for the better) from the original IP + specification. Bit-masks, where present have been converted into + human-readable values. IP addresses are listed in their dotted- + decimal notation [RFC1123]. Length and checksum values are present + as decimal integers. + + To calculate the length and checksum fields of the IP element, a + canonicalized form of the element MUST be used. The canonical form + SHALL have no whitespace (including newline characters) between + elements and only one space character between attributes. There + SHALL NOT be a space following the last attribute in an element. + + An iterative method SHOULD be used to calculate checksums, as the + length field will vary based on the size of the checksum. + + The payload element bears special attention. Due to the character + set restrictions of XML, the payload of IP datagrams (which MAY + contain arbitrary data) MUST be encoded for transport. This RFC + REQUIRES the contents of the payload to be encoded in the base-64 + encoding of RFC 2045 [RFC2045], but removes the requirement that the + encoded output MUST be wrapped on 76-character lines. + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 3] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +2.2. Example Datagram + + The following is an example IPoXML datagram with an empty payload: + + <?xml version="1.0" encoding="UTF-8"?> + <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd"> + <ip> + <header length="474"> + <version value="4"/> + <tos precedence="Routine" delay="Normal" throughput="Normal" + relibility="Normal" reserved="0"/> + <total.length value="461"/> + <id value="1"/> + <flags reserved="0" df="dont" mf="last"/> + <offset value="0"/> + <ttl value="255"/> + <protocol value="6"/> + <checksum value="8707"/> + <source address="10.0.0.22"/> + <destination address="10.0.0.1"/> + <options> + <end copied="0" class="0" number="0"/> + </options> + <padding pad="0"/> + </header> + <payload> + </payload> + </ip> + +3. TCPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.2. + +3.1. TCP Description + + A number of items have changed from the original TCP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + To calculate the length and checksum fields of the TCP element, a + canonicalized form of the element MUST be used as in section 2.1. + + An iterative method SHOULD be used to calculate checksums as in + section 2.1. + + The payload element MUST be encoded as in section 2.1. + + + +Kennedy Informational [Page 4] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + The TCP offset element was expanded to a maximum of 255 from 16 to + allow for the increased size of the header in XML. + + TCPoXML datagrams encapsulated by IPoXML MAY omit the <?xml?> header + as well as the <!DOCTYPE> declaration. + +3.2. Example Datagram + + The following is an example TCPoXML datagram with an empty payload: + + <?xml version="1.0" encoding="UTF-8"?> + <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd"> + <tcp> + <tcp.header> + <src port="31415"/> + <dest port="42424"/> + <sequence number="322622954"/> + <acknowledgement number="689715995"/> + <offset number=""/> + <reserved value="0"/> + <control syn="1" ack="1"/> + <window size="1"/> + <urgent pointer="0"/> + <checksum value="2988"/> + <tcp.options> + <tcp.end kind="0"/> + </tcp.options> + <padding pad="0"/> + </tcp.header> + <payload> + </payload> + </tcp> + +4. UDPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.3. + +4.1. UDP Description + + A number of items have changed from the original UDP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + + + + + + +Kennedy Informational [Page 5] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + To calculate the length and checksum fields of the UDP element, a + canonicalized form of the element MUST be used as in section 2.1. An + iterative method SHOULD be used to calculate checksums as in section + 2.1. + + The payload element MUST be encoded as in section 2.1. + + UDPoXML datagrams encapsulated by IPoXML MAY omit the <?xml?> header + as well as the <!DOCTYPE> declaration. + +4.2. Example Datagram + + The following is an example UDPoXML datagram with an empty payload: + + <?xml version="1.0" encoding="UTF-8"?> + <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd"> + <udp> + <udp.header> + <src port="31415"/> + <dest port="42424"/> + <udp.length value="143"/> + <checksum value="2988"/> + </udp.header> + <payload> + </payload> + </udp> + +5. Network Transport + + This document provides for the transmission of BLOAT datagrams over + two common families of physical layer transport. Future RFCs will + address additional transports as routing vendors catch up to the + specification, and we begin to see BLOAT routed across the Internet + backbone. + +5.1. Ethernet + + BLOAT is encapsulated in Ethernet datagrams as in [RFC894] with the + exception that the type field of the Ethernet frame MUST contain the + value 0xBEEF. The first 5 octets of the Ethernet frame payload will + be 0x3c 3f 78 6d 6c ("<?xml".) + +5.2. IEEE 802 + + BLOAT is encapsulated in IEEE 802 Networks as in [RFC1042] except + that the protocol type code for IPoXML is 0xBEEF. + + + + + +Kennedy Informational [Page 6] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +6. Gatewaying over IP + + In order to facilitate the gradual introduction of BLOAT into the + public Internet, BLOAT MAY be encapsulated in IP as in [RFC2003] to + gateway between networks that run BLOAT natively on their LANs. + +7. DTDs + + The Transport DTDs (7.2. and 7.3.) build on the definitions in the + Network DTD (7.1.) + + The DTDs are referenced by their PubidLiteral and SystemLiteral (from + [XML]) although it is understood that most IPoXML implementations + will not need to pull down the DTD, as it will normally be embedded + in the implementation, and presents something of a catch-22 if you + need to load part of your network protocol over the network. + +7.1. IPoXML DTD + + <!-- + DTD for IP over XML. + Refer to this DTD as: + + <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd"> + --> + <!-- + DTD data types: + + Digits [0..9]+ + + Precedence "NetworkControl | InternetworkControl | + CRITIC | FlashOverride | Flash | Immediate | + Priority | Routine" + + IP4Addr "dotted-decimal" notation of [RFC1123] + + Class [0..3] + + Sec "Unclassified | Confidential | EFTO | MMMM | PROG | + Restricted | Secret | Top Secret | Reserved" + + Compartments [0..65535] + + Handling [0..65535] + + TCC [0..16777216] + + --> + + + +Kennedy Informational [Page 7] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + <!ENTITY % Digits "CDATA"> + <!ENTITY % Precedence "CDATA"> + <!ENTITY % IP4Addr "CDATA"> + <!ENTITY % Class "CDATA"> + <!ENTITY % Sec "CDATA"> + <!ENTITY % Compartments "CDATA"> + <!ENTITY % Handling "CDATA"> + <!ENTITY % TCC "CDATA"> + + <!ELEMENT ip (header, payload)> + + <!ELEMENT header (version, tos, total.length, id, flags, offset, ttl, + protocol, checksum, source, destination, options, + padding)> + <!-- length of header in 32-bit words --> + <!ATTLIST header + length %Digits; #REQUIRED> + + <!ELEMENT version EMPTY> + <!-- ip version. SHOULD be "4" --> + <!ATTLIST version + value %Digits; #REQUIRED> + + <!ELEMENT tos EMPTY> + <!ATTLIST tos + precedence %Precedence; #REQUIRED + delay (normal | low) #REQUIRED + throughput (normal | high) #REQUIRED + relibility (normal | high) #REQUIRED + reserved CDATA #FIXED "0"> + + <!ELEMENT total.length EMPTY> + <!-- + total length of datagram (header and payload) in octets, MUST be + less than 65,535 (and SHOULD be less than 1024 for IPoXML on local + ethernets). + --> + <!ATTLIST total.length + value %Digits; #REQUIRED> + + <!ELEMENT id EMPTY> + <!-- 0 <= id <= 65,535 --> + <!ATTLIST id + value %Digits; #REQUIRED> + + <!ELEMENT flags EMPTY> + <!-- df = don't fragment, mf = more fragments --> + <!ATTLIST flags + + + +Kennedy Informational [Page 8] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + reserved CDATA #FIXED "0" + df (may|dont) #REQUIRED + mf (last|more) #REQUIRED> + + <!ELEMENT offset EMPTY> + <!-- 0 <= offset <= 8192 measured in 8 octet (64-bit) chunks --> + <!ATTLIST offset + value %Digits; #REQUIRED> + + <!ELEMENT ttl EMPTY> + <!-- 0 <= ttl <= 255 --> + <!ATTLIST ttl + value %Digits; #REQUIRED> + + <!ELEMENT protocol EMPTY> + <!-- 0 <= protocol <= 255 (per IANA) --> + <!ATTLIST protocol + value %Digits; #REQUIRED> + + <!ELEMENT checksum EMPTY> + <!-- 0 <= checksum <= 65535 (over header only) --> + <!ATTLIST checksum + value %Digits; #REQUIRED> + + <!ELEMENT source EMPTY> + <!ATTLIST source + address %IP4Addr; #REQUIRED> + + <!ELEMENT destination EMPTY> + <!ATTLIST destination + address %IP4Addr; #REQUIRED> + + <!ELEMENT options ( end | noop | security | loose | strict | record + | stream | timestamp )*> + + <!ELEMENT end EMPTY> + <!ATTLIST end + copied (0|1) #REQUIRED + class CDATA #FIXED "0" + number CDATA #FIXED "0"> + + <!ELEMENT noop EMPTY> + <!ATTLIST noop + copied (0|1) #REQUIRED + class CDATA #FIXED "0" + number CDATA #FIXED "1"> + + <!ELEMENT security EMPTY> + + + +Kennedy Informational [Page 9] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + <!ATTLIST security + copied CDATA #FIXED "1" + class CDATA #FIXED "0" + number CDATA #FIXED "2" + length CDATA #FIXED "11" + security %Sec; #REQUIRED + compartments %Compartments; #REQUIRED + handling %Handling; #REQUIRED + tcc %TCC; #REQUIRED> + <!ELEMENT loose (hop)+> + <!ATTLIST loose + copied CDATA #FIXED "1" + class CDATA #FIXED "0" + number CDATA #FIXED "3" + length %Digits; #REQUIRED + pointer %Digits; #REQUIRED> + + <!ELEMENT hop EMPTY> + <!ATTLIST hop + address %IP4Addr; #REQUIRED> + + <!ELEMENT strict (hop)+> + <!ATTLIST strict + copied CDATA #FIXED "1" + class CDATA #FIXED "0" + number CDATA #FIXED "9" + length %Digits; #REQUIRED + pointer %Digits; #REQUIRED> + + <!ELEMENT record (hop)+> + <!ATTLIST record + copied CDATA #FIXED "0" + class CDATA #FIXED "0" + number CDATA #FIXED "7" + length %Digits; #REQUIRED + pointer %Digits; #REQUIRED> + + <!ELEMENT stream EMPTY> + <!-- 0 <= id <= 65,535 --> + <!ATTLIST stream + copied CDATA #FIXED "1" + class CDATA #FIXED "0" + number CDATA #FIXED "8" + length CDATA #FIXED "4" + id %Digits; #REQUIRED> + + <!ELEMENT timestamp (tstamp)+> + <!-- 0 <= oflw <=15 --> + + + +Kennedy Informational [Page 10] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + <!ATTLIST timestamp + copied CDATA #FIXED "0" + class CDATA #FIXED "2" + number CDATA #FIXED "4" + length %Digits; #REQUIRED + pointer %Digits; #REQUIRED + oflw %Digits; #REQUIRED + flag (0 | 1 | 3) #REQUIRED> + + <!ELEMENT tstamp EMPTY> + <!ATTLIST tstamp + time %Digits; #REQUIRED + address %IP4Addr; #IMPLIED> + <!-- + padding to bring header to 32-bit boundary. + pad MUST be "0"* + --> + <!ELEMENT padding EMPTY> + <!ATTLIST padding + pad CDATA #REQUIRED> + + <!-- payload MUST be encoded as base-64 [RFC2045], as modified + by section 2.1 of this RFC --> + <!ELEMENT payload (CDATA)> + +7.2. TCPoXML DTD + + <!-- + DTD for TCP over XML. + Refer to this DTD as: + + <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd"> + --> + + <!-- the pseudoheader is only included for checksum calculations --> + <!ELEMENT tcp (tcp.pseudoheader?, tcp.header, payload)> + + <!ELEMENT tcp.header (src, dest, sequence, acknowledgement, offset, + reserved, control, window, checksum, urgent, + tcp.options, padding)> + + <!ELEMENT src EMPTY> + <!-- 0 <= port <= 65,535 --> + <!ATTLIST src + port %Digits; #REQUIRED> + + <!ELEMENT dest EMPTY> + <!-- 0 <= port <= 65,535 --> + + + +Kennedy Informational [Page 11] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + <!ATTLIST dest + port %Digits; #REQUIRED> + + <!ELEMENT sequence EMPTY> + <!-- 0 <= number <= 4294967295 --> + <!ATTLIST sequence + number %Digits; #REQUIRED> + + <!ELEMENT acknowledgement EMPTY> + <!-- 0 <= number <= 4294967295 --> + <!ATTLIST acknowledgement + number %Digits; #REQUIRED> + + <!ELEMENT offset EMPTY> + <!-- 0 <= number <= 255 --> + <!ATTLIST offset + number %Digits; #REQUIRED> + + <!ELEMENT reserved EMPTY> + <!ATTLIST reserved + value CDATA #FIXED "0"> + + <!ELEMENT control EMPTY> + <!ATTLIST control + urg (0|1) #IMPLIED + ack (0|1) #IMPLIED + psh (0|1) #IMPLIED + rst (0|1) #IMPLIED + syn (0|1) #IMPLIED + fin (0|1) #IMPLIED> + + <!ELEMENT window EMPTY> + <!-- 0 <= size <= 65,535 --> + <!ATTLIST window + size %Digits; #REQUIRED> + + <!-- + checksum as in ip, but with + the following pseudo-header added into the tcp element: + --> + <!ELEMENT tcp.pseudoheader (source, destination, protocol, + tcp.length)> + + <!-- + tcp header + data length in octets. does not include the size of + + the pseudoheader. + --> + + + +Kennedy Informational [Page 12] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + <!ELEMENT tcp.length EMPTY> + <!ATTLIST tcp.length + value %Digits; #REQUIRED> + + <!ELEMENT urgent EMPTY> + <!-- 0 <= pointer <= 65,535 --> + <!ATTLIST urgent + pointer %Digits; #REQUIRED> + + <!ELEMENT tcp.options (tcp.end | tcp.noop | tcp.mss)+> + + <!ELEMENT tcp.end EMPTY> + <!ATTLIST tcp.end + kind CDATA #FIXED "0"> + + <!ELEMENT tcp.noop EMPTY> + <!ATTLIST tcp.noop + kind CDATA #FIXED "1"> + + <!ELEMENT tcp.mss EMPTY> + <!ATTLIST tcp.mss + kind CDATA #FIXED "2" + length CDATA #FIXED "4" + size %Digits; #REQUIRED> + +7.3. UDPoXML DTD + + <!-- + DTD for UDP over XML. + Refer to this DTD as: + + <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd"> + --> + + <!ELEMENT udp (udp.pseudoheader?, udp.header, payload)> + + <!ELEMENT udp.header (src, dest, udp.length, checksum)> + + <!ELEMENT udp.pseudoheader (source, destination, protocol, + udp.length)> + + <!-- + udp header + data length in octets. does not include the size of + the pseudoheader. + --> + <!ELEMENT udp.length EMPTY> + <!ATTLIST udp.length + value %Digits; #REQUIRED> + + + +Kennedy Informational [Page 13] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +8. Security Considerations + + XML, as a subset of SGML, has the same security considerations as + specified in SGML Media Types [RFC1874]. Security considerations + that apply to IP, TCP and UDP also likely apply to BLOAT as it does + not attempt to correct for issues not related to message format. + +9. References + + [JABBER] Miller, J., "Jabber", draft-miller-jabber-00.txt, + February 2002. (Work in Progress) + + [RFC768] Postel, J., "User Datagram Protocol", STD 6, RFC 768, + August 1980. + + [RFC791] Postel, J., "Internet Protocol", STD 5, RFC 791, + September 1981. + + [RFC793] Postel, J., "Transmission Control Protocol", STD 7, RFC + 793, September 1981. + + [RFC894] Hornig, C., "Standard for the Transmission of IP + Datagrams over Ethernet Networks.", RFC 894, April 1984. + + [RFC1042] Postel, J. and J. Reynolds, "Standard for the + Transmission of IP Datagrams Over IEEE 802 Networks", STD + 43, RFC 1042, February 1988. + + [RFC1123] Braden, R., "Requirements for Internet Hosts - + Application and Support", RFC 1123, October 1989. + + [RFC1874] Levinson, E., "SGML Media Types", RFC 1874, December + 1995. + + [RFC2003] Perkins, C., "IP Encapsulation within IP", RFC 2003, + October 1996. + + [RFC2045] Freed, N. and N. Borenstein, "Multipurpose Internet Mail + Extensions (MIME) Part One: Format of Internet Message + Bodies", RFC 2045, November 1996. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2279] Yergeau, F., "UTF-8, a transformation format of ISO + 10646", RFC 2279, January 1998. + + + + + +Kennedy Informational [Page 14] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + [RFC2460] Deering, S. and R. Hinden, "Internet Protocol, Version 6 + (IPv6) Specification", RFC 2460, December 1998. + + [RFC3080] Rose, M., "The Blocks Extensible Exchange Protocol Core", + RFC 3080, March 2001. + + [SOAP] Box, D., Ehnebuske, D., Kakivaya, G., Layman, A., + Mendelsohn, N., Nielsen, H. F., Thatte, S. Winer, D., + "Simple Object Access Protocol (SOAP) 1.1" World Wide Web + Consortium Note, May 2000 http://www.w3.org/TR/SOAP/ + + [XML] Bray, T., Paoli, J., Sperberg-McQueen, C. M., "Extensible + Markup Language (XML)" World Wide Web Consortium + Recommendation REC- xml-19980210. + http://www.w3.org/TR/1998/REC-xml-19980210 + +10. Author's Address + + Hugh Kennedy + Mimezine + 1060 West Addison + Chicago, IL 60613 + USA + + EMail: kennedyh@engin.umich.edu + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 15] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +11. Full Copyright Statement + + Copyright (C) The Internet Society (2002). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 16] + diff --git a/tests/auto/integrationtests/networkselftest/tst_networkselftest.cpp b/tests/auto/integrationtests/networkselftest/tst_networkselftest.cpp new file mode 100644 index 0000000000..341054c533 --- /dev/null +++ b/tests/auto/integrationtests/networkselftest/tst_networkselftest.cpp @@ -0,0 +1,1029 @@ +/**************************************************************************** +** +** 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 <QtNetwork/QtNetwork> + +#include <time.h> + +#ifndef QT_NO_BEARERMANAGEMENT +#include <QtNetwork/qnetworkconfigmanager.h> +#include <QtNetwork/qnetworkconfiguration.h> +#include <QtNetwork/qnetworksession.h> +#endif + +#include "../../network-settings.h" + +class tst_NetworkSelfTest: public QObject +{ + Q_OBJECT + QHostAddress cachedIpAddress; +public: + tst_NetworkSelfTest(); + virtual ~tst_NetworkSelfTest(); + + QHostAddress serverIpAddress(); + +private slots: + void initTestCase(); + void hostTest(); + void dnsResolution_data(); + void dnsResolution(); + void serverReachability(); + void remotePortsOpen_data(); + void remotePortsOpen(); + void fileLineEndingTest(); + + // specific protocol tests + void ftpServer(); + void ftpProxyServer(); + void imapServer(); + void httpServer(); + void httpServerFiles_data(); + void httpServerFiles(); + void httpServerCGI_data(); + void httpServerCGI(); +#ifndef QT_NO_OPENSSL + void httpsServer(); +#endif + void httpProxy(); + void httpProxyBasicAuth(); + void httpProxyNtlmAuth(); + void socks5Proxy(); + void socks5ProxyAuth(); + void smbServer(); + + // ssl supported test + void supportsSsl(); +private: +#ifndef QT_NO_BEARERMANAGEMENT + QNetworkConfigurationManager *netConfMan; + QNetworkConfiguration networkConfiguration; + QScopedPointer<QNetworkSession> networkSession; +#endif +}; + +class Chat +{ +public: + enum Type { + Reconnect, + Send, + Expect, + SkipBytes, + DiscardUntil, + DiscardUntilDisconnect, + Disconnect, + RemoteDisconnect, + StartEncryption + }; + Chat(Type t, const QByteArray &d) + : data(d), type(t) + { + } + Chat(Type t, int val = 0) + : value(val), type(t) + { + } + + static inline Chat send(const QByteArray &data) + { return Chat(Send, data); } + static inline Chat expect(const QByteArray &data) + { return Chat(Expect, data); } + static inline Chat discardUntil(const QByteArray &data) + { return Chat(DiscardUntil, data); } + static inline Chat skipBytes(int count) + { return Chat(SkipBytes, count); } + + QByteArray data; + int value; + Type type; +}; + +static QString prettyByteArray(const QByteArray &array) +{ + // any control chars? + QString result; + result.reserve(array.length() + array.length() / 3); + for (int i = 0; i < array.length(); ++i) { + char c = array.at(i); + switch (c) { + case '\n': + result += "\\n"; + continue; + case '\r': + result += "\\r"; + continue; + case '\t': + result += "\\t"; + continue; + case '"': + result += "\\\""; + continue; + default: + break; + } + + if (c < 0x20 || uchar(c) >= 0x7f) { + result += '\\'; + result += QString::number(uchar(c), 8); + } else { + result += c; + } + } + return result; +} + +static bool doSocketRead(QTcpSocket *socket, int minBytesAvailable, int timeout = 4000) +{ + QElapsedTimer timer; + timer.start(); + forever { + if (socket->bytesAvailable() >= minBytesAvailable) + return true; + if (socket->state() == QAbstractSocket::UnconnectedState + || timer.elapsed() >= timeout) + return false; + if (!socket->waitForReadyRead(timeout - timer.elapsed())) + return false; + } +} + +static bool doSocketFlush(QTcpSocket *socket, int timeout = 4000) +{ +#ifndef QT_NO_OPENSSL + QSslSocket *sslSocket = qobject_cast<QSslSocket *>(socket); +#endif + QTime timer; + timer.start(); + forever { + if (socket->bytesToWrite() == 0 +#ifndef QT_NO_OPENSSL + && sslSocket->encryptedBytesToWrite() == 0 +#endif + ) + return true; + if (socket->state() == QAbstractSocket::UnconnectedState + || timer.elapsed() >= timeout) + return false; + if (!socket->waitForBytesWritten(timeout - timer.elapsed())) + return false; + } +} + +static void netChat(int port, const QList<Chat> &chat) +{ +#ifndef QT_NO_OPENSSL + QSslSocket socket; +#else + QTcpSocket socket; +#endif + + socket.connectToHost(QtNetworkSettings::serverName(), port); + qDebug() << 0 << "Connecting to server on port" << port; + QVERIFY2(socket.waitForConnected(10000), + QString("Failed to connect to server in step 0: %1").arg(socket.errorString()).toLocal8Bit()); + + // now start the chat + QList<Chat>::ConstIterator it = chat.constBegin(); + for (int i = 1; it != chat.constEnd(); ++it, ++i) { + switch (it->type) { + case Chat::Expect: { + qDebug() << i << "Expecting" << prettyByteArray(it->data); + if (!doSocketRead(&socket, it->data.length())) + QFAIL(QString("Failed to receive data in step %1: timeout").arg(i).toLocal8Bit()); + + // pop that many bytes off the socket + QByteArray received = socket.read(it->data.length()); + + // is it what we expected? + QVERIFY2(received == it->data, + QString("Did not receive expected data in step %1: data received was:\n%2") + .arg(i).arg(prettyByteArray(received)).toLocal8Bit()); + + break; + } + + case Chat::DiscardUntil: + qDebug() << i << "Discarding until" << prettyByteArray(it->data); + while (true) { + // scan the buffer until we have our string + if (!doSocketRead(&socket, it->data.length())) + QFAIL(QString("Failed to receive data in step %1: timeout").arg(i).toLocal8Bit()); + + QByteArray buffer; + buffer.resize(socket.bytesAvailable()); + socket.peek(buffer.data(), socket.bytesAvailable()); + + int pos = buffer.indexOf(it->data); + if (pos == -1) { + // data not found, keep trying + continue; + } + + buffer = socket.read(pos + it->data.length()); + qDebug() << i << "Discarded" << prettyByteArray(buffer); + break; + } + break; + + case Chat::SkipBytes: { + qDebug() << i << "Skipping" << it->value << "bytes"; + if (!doSocketRead(&socket, it->value)) + QFAIL(QString("Failed to receive data in step %1: timeout").arg(i).toLocal8Bit()); + + // now discard the bytes + QByteArray buffer = socket.read(it->value); + qDebug() << i << "Skipped" << prettyByteArray(buffer); + break; + } + + case Chat::Send: { + qDebug() << i << "Sending" << prettyByteArray(it->data); + socket.write(it->data); + if (!doSocketFlush(&socket)) { + QVERIFY2(socket.state() == QAbstractSocket::ConnectedState, + QString("Socket disconnected while sending data in step %1").arg(i).toLocal8Bit()); + QFAIL(QString("Failed to send data in step %1: timeout").arg(i).toLocal8Bit()); + } + break; + } + + case Chat::Disconnect: + qDebug() << i << "Disconnecting from host"; + socket.disconnectFromHost(); + + // is this the last command? + if (it + 1 != chat.constEnd()) + break; + + // fall through: + case Chat::RemoteDisconnect: + case Chat::DiscardUntilDisconnect: + qDebug() << i << "Waiting for remote disconnect"; + if (socket.state() != QAbstractSocket::UnconnectedState) + socket.waitForDisconnected(10000); + QVERIFY2(socket.state() == QAbstractSocket::UnconnectedState, + QString("Socket did not disconnect as expected in step %1").arg(i).toLocal8Bit()); + + // any data left? + if (it->type == Chat::DiscardUntilDisconnect) { + QByteArray buffer = socket.readAll(); + qDebug() << i << "Discarded in the process:" << prettyByteArray(buffer); + } + + if (socket.bytesAvailable() != 0) + QFAIL(QString("Unexpected bytes still on buffer when disconnecting in step %1:\n%2") + .arg(i).arg(prettyByteArray(socket.readAll())).toLocal8Bit()); + break; + + case Chat::Reconnect: + qDebug() << i << "Reconnecting to server on port" << port; + socket.connectToHost(QtNetworkSettings::serverName(), port); + QVERIFY2(socket.waitForConnected(10000), + QString("Failed to reconnect to server in step %1: %2").arg(i).arg(socket.errorString()).toLocal8Bit()); + break; + + case Chat::StartEncryption: +#ifdef QT_NO_OPENSSL + QFAIL("Internal error: SSL required for this test"); +#else + qDebug() << i << "Starting client encryption"; + socket.ignoreSslErrors(); + socket.startClientEncryption(); + QVERIFY2(socket.waitForEncrypted(5000), + QString("Failed to start client encryption in step %1: %2").arg(i) + .arg(socket.errorString()).toLocal8Bit()); + break; +#endif + } + } +} + +tst_NetworkSelfTest::tst_NetworkSelfTest() +{ +} + +tst_NetworkSelfTest::~tst_NetworkSelfTest() +{ +} + +QHostAddress tst_NetworkSelfTest::serverIpAddress() +{ + if (cachedIpAddress.protocol() == QAbstractSocket::UnknownNetworkLayerProtocol) { + // need resolving + QHostInfo resolved = QHostInfo::fromName(QtNetworkSettings::serverName()); + if(resolved.error() != QHostInfo::NoError || + resolved.addresses().isEmpty()) { + qWarning("QHostInfo::fromName failed (%d).", resolved.error()); + return QHostAddress(QHostAddress::Null); + } + cachedIpAddress = resolved.addresses().first(); + } + return cachedIpAddress; +} + +void tst_NetworkSelfTest::initTestCase() +{ +#ifndef QT_NO_BEARERMANAGEMENT + netConfMan = new QNetworkConfigurationManager(this); + networkConfiguration = netConfMan->defaultConfiguration(); + networkSession.reset(new QNetworkSession(networkConfiguration)); + if (!networkSession->isOpen()) { + networkSession->open(); + QVERIFY(networkSession->waitForOpened(30000)); + } +#endif +} + +void tst_NetworkSelfTest::hostTest() +{ + // this is a localhost self-test + QHostInfo localhost = QHostInfo::fromName("localhost"); + QCOMPARE(localhost.error(), QHostInfo::NoError); + QVERIFY(!localhost.addresses().isEmpty()); + + QTcpServer server; + QVERIFY(server.listen()); + + QTcpSocket socket; + socket.connectToHost("127.0.0.1", server.serverPort()); + QVERIFY(socket.waitForConnected(10000)); +} + +void tst_NetworkSelfTest::dnsResolution_data() +{ + QTest::addColumn<QString>("hostName"); + QTest::newRow("local-name") << QtNetworkSettings::serverLocalName(); + QTest::newRow("fqdn") << QtNetworkSettings::serverName(); +} + +void tst_NetworkSelfTest::dnsResolution() +{ + QFETCH(QString, hostName); + QHostInfo resolved = QHostInfo::fromName(hostName); + QVERIFY2(resolved.error() == QHostInfo::NoError, + QString("Failed to resolve hostname %1: %2").arg(hostName, resolved.errorString()).toLocal8Bit()); + QVERIFY2(resolved.addresses().size() > 0, "Got 0 addresses for server IP"); + + cachedIpAddress = resolved.addresses().first(); +} + +void tst_NetworkSelfTest::serverReachability() +{ + // check that we get a proper error connecting to port 12346 + QTcpSocket socket; + socket.connectToHost(QtNetworkSettings::serverName(), 12346); + + QTime timer; + timer.start(); + socket.waitForConnected(10000); + QVERIFY2(timer.elapsed() < 9900, "Connection to closed port timed out instead of refusing, something is wrong"); + + QVERIFY2(socket.state() == QAbstractSocket::UnconnectedState, "Socket connected unexpectedly!"); + QVERIFY2(socket.error() == QAbstractSocket::ConnectionRefusedError, + QString("Could not reach server: %1").arg(socket.errorString()).toLocal8Bit()); +} + +void tst_NetworkSelfTest::remotePortsOpen_data() +{ + QTest::addColumn<int>("portNumber"); + QTest::newRow("echo") << 7; + QTest::newRow("daytime") << 13; + QTest::newRow("ftp") << 21; + QTest::newRow("ssh") << 22; + QTest::newRow("imap") << 143; + QTest::newRow("http") << 80; + QTest::newRow("https") << 443; + QTest::newRow("http-proxy") << 3128; + QTest::newRow("http-proxy-auth-basic") << 3129; + QTest::newRow("http-proxy-auth-ntlm") << 3130; + QTest::newRow("socks5-proxy") << 1080; + QTest::newRow("socks5-proxy-auth") << 1081; + QTest::newRow("ftp-proxy") << 2121; + QTest::newRow("smb") << 139; +} + +void tst_NetworkSelfTest::remotePortsOpen() +{ + QFETCH(int, portNumber); + QTcpSocket socket; + socket.connectToHost(QtNetworkSettings::serverName(), portNumber); + + if (!socket.waitForConnected(10000)) { + if (socket.error() == QAbstractSocket::SocketTimeoutError) + QFAIL(QString("Network timeout connecting to the server on port %1").arg(portNumber).toLocal8Bit()); + else + QFAIL(QString("Error connecting to server on port %1: %2").arg(portNumber).arg(socket.errorString()).toLocal8Bit()); + } + QVERIFY(socket.state() == QAbstractSocket::ConnectedState); +} + + +void tst_NetworkSelfTest::fileLineEndingTest() +{ + QString referenceName = SRCDIR "/rfc3252.txt"; + long long expectedReferenceSize = 25962; + + QString lineEndingType("LF"); + + QFile reference(referenceName); + QVERIFY(reference.open(QIODevice::ReadOnly)); + QByteArray byteLine = reference.readLine(); + if(byteLine.endsWith("\r\n")) + lineEndingType = "CRLF"; + else if(byteLine.endsWith("\r")) + lineEndingType = "CR"; + + QString referenceAsTextData; + QFile referenceAsText(referenceName); + QVERIFY(referenceAsText.open(QIODevice::ReadOnly)); + referenceAsTextData = referenceAsText.readAll(); + + QVERIFY2(expectedReferenceSize == referenceAsTextData.length(), QString("Reference file %1 has %2 as line ending and file size not matching - Git checkout issue !?!").arg(referenceName, lineEndingType).toLocal8Bit()); + QVERIFY2(!lineEndingType.compare("LF"), QString("Reference file %1 has %2 as line ending - Git checkout issue !?!").arg(referenceName, lineEndingType).toLocal8Bit()); +} + +static QList<Chat> ftpChat(const QByteArray &userSuffix = QByteArray()) +{ + QList<Chat> rv; + rv << Chat::expect("220") + << Chat::discardUntil("\r\n") + << Chat::send("USER anonymous" + userSuffix + "\r\n") + << Chat::expect("331") + << Chat::discardUntil("\r\n") + << Chat::send("PASS user@hostname\r\n") + << Chat::expect("230") + << Chat::discardUntil("\r\n") + + << Chat::send("CWD pub\r\n") + << Chat::expect("250") + << Chat::discardUntil("\r\n") + << Chat::send("CWD dir-not-readable\r\n") + << Chat::expect("550") + << Chat::discardUntil("\r\n") + << Chat::send("PWD\r\n") + << Chat::expect("257 \"/pub\"\r\n") + << Chat::send("SIZE file-not-readable.txt\r\n") + << Chat::expect("213 41\r\n") + << Chat::send("CWD qxmlquery\r\n") + << Chat::expect("250") + << Chat::discardUntil("\r\n") + + << Chat::send("CWD /qtest\r\n") + << Chat::expect("250") + << Chat::discardUntil("\r\n") + << Chat::send("SIZE bigfile\r\n") + << Chat::expect("213 519240\r\n") + << Chat::send("SIZE rfc3252\r\n") + << Chat::expect("213 25962\r\n") + << Chat::send("SIZE rfc3252.txt\r\n") + << Chat::expect("213 25962\r\n") +// << Chat::send("SIZE nonASCII/german_\344\366\374\304\326\334\337\r\n") +// << Chat::expect("213 40\r\n") + + << Chat::send("QUIT\r\n"); + rv << Chat::expect("221") + << Chat::discardUntil("\r\n"); + + rv << Chat::RemoteDisconnect; + return rv; +} + +void tst_NetworkSelfTest::ftpServer() +{ + netChat(21, ftpChat()); +} + +void tst_NetworkSelfTest::ftpProxyServer() +{ + netChat(2121, ftpChat("@" + QtNetworkSettings::serverName().toLatin1())); +} + +void tst_NetworkSelfTest::imapServer() +{ + netChat(143, QList<Chat>() + << Chat::expect("* OK ") + << Chat::discardUntil("\r\n") + << Chat::send("1 CAPABILITY\r\n") + << Chat::expect("* CAPABILITY ") + << Chat::discardUntil("1 OK") + << Chat::discardUntil("\r\n") + << Chat::send("2 LOGOUT\r\n") + << Chat::discardUntil("2 OK") + << Chat::discardUntil("\r\n") + << Chat::RemoteDisconnect); +} + +void tst_NetworkSelfTest::httpServer() +{ + QString uniqueExtension; + qsrand(time(0)); +#ifndef Q_OS_WINCE + uniqueExtension = QString("%1%2%3").arg((qulonglong)this).arg(qrand()).arg((qulonglong)time(0)); +#else + uniqueExtension = QString("%1%2").arg((qulonglong)this).arg(qrand()); +#endif + + netChat(80, QList<Chat>() + // HTTP/0.9 chat: + << Chat::send("GET /\r\n") + << Chat::DiscardUntilDisconnect + + // HTTP/1.0 chat: + << Chat::Reconnect + << Chat::send("GET / HTTP/1.0\r\n" + "Host: " + QtNetworkSettings::serverName().toLatin1() + "\r\n" + "Connection: close\r\n" + "\r\n") + << Chat::expect("HTTP/1.") + << Chat::discardUntil(" ") + << Chat::expect("200 ") + << Chat::DiscardUntilDisconnect + + // HTTP/1.0 POST: + << Chat::Reconnect + << Chat::send("POST / HTTP/1.0\r\n" + "Content-Length: 5\r\n" + "Host: " + QtNetworkSettings::serverName().toLatin1() + "\r\n" + "Connection: close\r\n" + "\r\n" + "Hello") + << Chat::expect("HTTP/1.") + << Chat::discardUntil(" ") + << Chat::expect("200 ") + << Chat::DiscardUntilDisconnect + + // HTTP protected area + << Chat::Reconnect + << Chat::send("GET /qtest/protected/rfc3252.txt HTTP/1.0\r\n" + "Host: " + QtNetworkSettings::serverName().toLatin1() + "\r\n" + "Connection: close\r\n" + "\r\n") + << Chat::expect("HTTP/1.") + << Chat::discardUntil(" ") + << Chat::expect("401 ") + << Chat::DiscardUntilDisconnect + + << Chat::Reconnect + << Chat::send("HEAD /qtest/protected/rfc3252.txt HTTP/1.0\r\n" + "Host: " + QtNetworkSettings::serverName().toLatin1() + "\r\n" + "Connection: close\r\n" + "Authorization: Basic cXNvY2tzdGVzdDpwYXNzd29yZA==\r\n" + "\r\n") + << Chat::expect("HTTP/1.") + << Chat::discardUntil(" ") + << Chat::expect("200 ") + << Chat::DiscardUntilDisconnect + + // DAV area + << Chat::Reconnect + << Chat::send("HEAD /dav/ HTTP/1.0\r\n" + "Host: " + QtNetworkSettings::serverName().toLatin1() + "\r\n" + "Connection: close\r\n" + "\r\n") + << Chat::expect("HTTP/1.") + << Chat::discardUntil(" ") + << Chat::expect("200 ") + << Chat::DiscardUntilDisconnect + + // HTTP/1.0 PUT + << Chat::Reconnect + << Chat::send("PUT /dav/networkselftest-" + uniqueExtension.toLatin1() + ".txt HTTP/1.0\r\n" + "Content-Length: 5\r\n" + "Host: " + QtNetworkSettings::serverName().toLatin1() + "\r\n" + "Connection: close\r\n" + "\r\n" + "Hello") + << Chat::expect("HTTP/1.") + << Chat::discardUntil(" ") + << Chat::expect("201 ") + << Chat::DiscardUntilDisconnect + + // check that the file did get uploaded + << Chat::Reconnect + << Chat::send("HEAD /dav/networkselftest-" + uniqueExtension.toLatin1() + ".txt HTTP/1.0\r\n" + "Host: " + QtNetworkSettings::serverName().toLatin1() + "\r\n" + "Connection: close\r\n" + "\r\n") + << Chat::expect("HTTP/1.") + << Chat::discardUntil(" ") + << Chat::expect("200 ") + << Chat::discardUntil("\r\nContent-Length: 5\r\n") + << Chat::DiscardUntilDisconnect + + // HTTP/1.0 DELETE + << Chat::Reconnect + << Chat::send("DELETE /dav/networkselftest-" + uniqueExtension.toLatin1() + ".txt HTTP/1.0\r\n" + "Host: " + QtNetworkSettings::serverName().toLatin1() + "\r\n" + "Connection: close\r\n" + "\r\n") + << Chat::expect("HTTP/1.") + << Chat::discardUntil(" ") + << Chat::expect("204 ") + << Chat::DiscardUntilDisconnect + ); +} + +void tst_NetworkSelfTest::httpServerFiles_data() +{ + QTest::addColumn<QString>("uri"); + QTest::addColumn<int>("size"); + + QTest::newRow("fluke.gif") << "/qtest/fluke.gif" << -1; + QTest::newRow("bigfile") << "/qtest/bigfile" << 519240; + QTest::newRow("rfc3252.txt") << "/qtest/rfc3252.txt" << 25962; + QTest::newRow("protected/rfc3252.txt") << "/qtest/protected/rfc3252.txt" << 25962; + QTest::newRow("completelyEmptyQuery.xq") << "/qtest/qxmlquery/completelyEmptyQuery.xq" << -1; + QTest::newRow("notWellformedViaHttps.xml") << "/qtest/qxmlquery/notWellformedViaHttps.xml" << -1; + QTest::newRow("notWellformed.xml") << "/qtest/qxmlquery/notWellformed.xml" << -1; + QTest::newRow("viaHttp.xq") << "/qtest/qxmlquery/viaHttp.xq" << -1; + QTest::newRow("wellFormedViaHttps.xml") << "/qtest/qxmlquery/wellFormedViaHttps.xml" << -1; + QTest::newRow("wellFormed.xml") << "/qtest/qxmlquery/wellFormed.xml" << -1; +} + +void tst_NetworkSelfTest::httpServerFiles() +{ + QFETCH(QString, uri); + QFETCH(int, size); + + QList<Chat> chat; + chat << Chat::send("HEAD " + QUrl::toPercentEncoding(uri, "/") + " HTTP/1.0\r\n" + "Host: " + QtNetworkSettings::serverName().toLatin1() + "\r\n" + "Connection: close\r\n" + "Authorization: Basic cXNvY2tzdGVzdDpwYXNzd29yZA==\r\n" + "\r\n") + << Chat::expect("HTTP/1.") + << Chat::skipBytes(1) // HTTP/1.0 or 1.1 reply + << Chat::expect(" 200 "); + if (size != -1) + chat << Chat::discardUntil("\r\nContent-Length: " + QByteArray::number(size) + "\r\n"); + chat << Chat::DiscardUntilDisconnect; + netChat(80, chat); +} + +void tst_NetworkSelfTest::httpServerCGI_data() +{ + QTest::addColumn<QByteArray>("request"); + QTest::addColumn<QByteArray>("result"); + QTest::addColumn<QByteArray>("additionalHeader"); + + QTest::newRow("echo.cgi") + << QByteArray("GET /qtest/cgi-bin/echo.cgi?Hello+World HTTP/1.0\r\n" + "Connection: close\r\n" + "\r\n") + << QByteArray("Hello+World") + << QByteArray(); + + QTest::newRow("echo.cgi(POST)") + << QByteArray("POST /qtest/cgi-bin/echo.cgi?Hello+World HTTP/1.0\r\n" + "Connection: close\r\n" + "Content-Length: 15\r\n" + "\r\n" + "Hello, World!\r\n") + << QByteArray("Hello, World!\r\n") + << QByteArray(); + + QTest::newRow("md5sum.cgi") + << QByteArray("POST /qtest/cgi-bin/md5sum.cgi HTTP/1.0\r\n" + "Connection: close\r\n" + "Content-Length: 15\r\n" + "\r\n" + "Hello, World!\r\n") + << QByteArray("29b933a8d9a0fcef0af75f1713f4940e\n") + << QByteArray(); + + QTest::newRow("protected/md5sum.cgi") + << QByteArray("POST /qtest/protected/cgi-bin/md5sum.cgi HTTP/1.0\r\n" + "Connection: close\r\n" + "Authorization: Basic cXNvY2tzdGVzdDpwYXNzd29yZA==\r\n" + "Content-Length: 15\r\n" + "\r\n" + "Hello, World!\r\n") + << QByteArray("29b933a8d9a0fcef0af75f1713f4940e\n") + << QByteArray(); + + QTest::newRow("set-cookie.cgi") + << QByteArray("POST /qtest/cgi-bin/set-cookie.cgi HTTP/1.0\r\n" + "Connection: close\r\n" + "Content-Length: 8\r\n" + "\r\n" + "foo=bar\n") + << QByteArray("Success\n") + << QByteArray("\r\nSet-Cookie: foo=bar\r\n"); +} + +void tst_NetworkSelfTest::httpServerCGI() +{ + QFETCH(QByteArray, request); + QFETCH(QByteArray, result); + QFETCH(QByteArray, additionalHeader); + QList<Chat> chat; + chat << Chat::send(request) + << Chat::expect("HTTP/1.") << Chat::skipBytes(1) + << Chat::expect(" 200 "); + + if (!additionalHeader.isEmpty()) + chat << Chat::discardUntil(additionalHeader); + + chat << Chat::discardUntil("\r\n\r\n") + << Chat::expect(result) + << Chat::RemoteDisconnect; + netChat(80, chat); +} + +#ifndef QT_NO_OPENSSL +void tst_NetworkSelfTest::httpsServer() +{ + netChat(443, QList<Chat>() + << Chat::StartEncryption + << Chat::send("GET / HTTP/1.0\r\n" + "Host: " + QtNetworkSettings::serverName().toLatin1() + "\r\n" + "Connection: close\r\n" + "\r\n") + << Chat::expect("HTTP/1.") + << Chat::discardUntil(" ") + << Chat::expect("200 ") + << Chat::DiscardUntilDisconnect); +} +#endif + +void tst_NetworkSelfTest::httpProxy() +{ + netChat(3128, QList<Chat>() + // proxy GET by IP + << Chat::send("GET http://" + serverIpAddress().toString().toLatin1() + "/ HTTP/1.0\r\n" + "Host: " + QtNetworkSettings::serverName().toLatin1() + "\r\n" + "Proxy-connection: close\r\n" + "\r\n") + << Chat::expect("HTTP/1.") + << Chat::discardUntil(" ") + << Chat::expect("200 ") + << Chat::DiscardUntilDisconnect + + // proxy GET by hostname + << Chat::Reconnect + << Chat::send("GET http://" + QtNetworkSettings::serverName().toLatin1() + "/ HTTP/1.0\r\n" + "Host: " + QtNetworkSettings::serverName().toLatin1() + "\r\n" + "Proxy-connection: close\r\n" + "\r\n") + << Chat::expect("HTTP/1.") + << Chat::discardUntil(" ") + << Chat::expect("200 ") + << Chat::DiscardUntilDisconnect + + // proxy CONNECT by IP + << Chat::Reconnect + << Chat::send("CONNECT " + serverIpAddress().toString().toLatin1() + ":21 HTTP/1.0\r\n" + "\r\n") + << Chat::expect("HTTP/1.") + << Chat::discardUntil(" ") + << Chat::expect("200 ") + << Chat::discardUntil("\r\n\r\n") + << ftpChat() + + // proxy CONNECT by hostname + << Chat::Reconnect + << Chat::send("CONNECT " + QtNetworkSettings::serverName().toLatin1() + ":21 HTTP/1.0\r\n" + "\r\n") + << Chat::expect("HTTP/1.") + << Chat::discardUntil(" ") + << Chat::expect("200 ") + << Chat::discardUntil("\r\n\r\n") + << ftpChat() + ); +} + +void tst_NetworkSelfTest::httpProxyBasicAuth() +{ + netChat(3129, QList<Chat>() + // test auth required response + << Chat::send("GET http://" + QtNetworkSettings::serverName().toLatin1() + "/ HTTP/1.0\r\n" + "Host: " + QtNetworkSettings::serverName().toLatin1() + "\r\n" + "Proxy-connection: close\r\n" + "\r\n") + << Chat::expect("HTTP/1.") + << Chat::discardUntil(" ") + << Chat::expect("407 ") + << Chat::discardUntil("\r\nProxy-Authenticate: Basic realm=\"") + << Chat::DiscardUntilDisconnect + + // now try sending our credentials + << Chat::Reconnect + << Chat::send("GET http://" + QtNetworkSettings::serverName().toLatin1() + "/ HTTP/1.0\r\n" + "Host: " + QtNetworkSettings::serverName().toLatin1() + "\r\n" + "Proxy-connection: close\r\n" + "Proxy-Authorization: Basic cXNvY2tzdGVzdDpwYXNzd29yZA==\r\n" + "\r\n") + << Chat::expect("HTTP/1.") + << Chat::discardUntil(" ") + << Chat::expect("200 ") + << Chat::DiscardUntilDisconnect); +} + +void tst_NetworkSelfTest::httpProxyNtlmAuth() +{ + netChat(3130, QList<Chat>() + // test auth required response + << Chat::send("GET http://" + QtNetworkSettings::serverName().toLatin1() + "/ HTTP/1.0\r\n" + "Host: " + QtNetworkSettings::serverName().toLatin1() + "\r\n" + "Proxy-connection: keep-alive\r\n" // NTLM auth will disconnect + "\r\n") + << Chat::expect("HTTP/1.") + << Chat::discardUntil(" ") + << Chat::expect("407 ") + << Chat::discardUntil("\r\nProxy-Authenticate: NTLM\r\n") + << Chat::DiscardUntilDisconnect + ); +} + +// SOCKSv5 is a binary protocol +static const char handshakeNoAuth[] = "\5\1\0"; +static const char handshakeOkNoAuth[] = "\5\0"; +static const char handshakeAuthPassword[] = "\5\1\2\1\12qsockstest\10password"; +static const char handshakeOkPasswdAuth[] = "\5\2\1\0"; +static const char handshakeAuthNotOk[] = "\5\377"; +static const char connect1[] = "\5\1\0\1\177\0\0\1\0\25"; // Connect IPv4 127.0.0.1 port 21 +static const char connect1a[] = "\5\1\0\1"; // just "Connect to IPv4" +static const char connect1b[] = "\0\25"; // just "port 21" +static const char connect2[] = "\5\1\0\3\11localhost\0\25"; // Connect hostname localhost 21 +static const char connect2a[] = "\5\1\0\3"; // just "Connect to hostname" +static const char connected[] = "\5\0\0"; + +#define QBA(x) (QByteArray::fromRawData(x, -1 + sizeof(x))) + +void tst_NetworkSelfTest::socks5Proxy() +{ + union { + char buf[4]; + quint32 data; + } ip4Address; + ip4Address.data = qToBigEndian(serverIpAddress().toIPv4Address()); + + netChat(1080, QList<Chat>() + // IP address connection + << Chat::send(QByteArray(handshakeNoAuth, -1 + sizeof handshakeNoAuth)) + << Chat::expect(QByteArray(handshakeOkNoAuth, -1 + sizeof handshakeOkNoAuth)) + << Chat::send(QByteArray(connect1, -1 + sizeof connect1)) + << Chat::expect(QByteArray(connected, -1 + sizeof connected)) + << Chat::expect("\1") // IPv4 address following + << Chat::skipBytes(6) // the server's local address and port + << ftpChat() + + // connect by IP + << Chat::Reconnect + << Chat::send(QByteArray(handshakeNoAuth, -1 + sizeof handshakeNoAuth)) + << Chat::expect(QByteArray(handshakeOkNoAuth, -1 + sizeof handshakeOkNoAuth)) + << Chat::send(QBA(connect1a) + QByteArray::fromRawData(ip4Address.buf, 4) + QBA(connect1b)) + << Chat::expect(QByteArray(connected, -1 + sizeof connected)) + << Chat::expect("\1") // IPv4 address following + << Chat::skipBytes(6) // the server's local address and port + << ftpChat() + + // connect to "localhost" by hostname + << Chat::Reconnect + << Chat::send(QByteArray(handshakeNoAuth, -1 + sizeof handshakeNoAuth)) + << Chat::expect(QByteArray(handshakeOkNoAuth, -1 + sizeof handshakeOkNoAuth)) + << Chat::send(QByteArray(connect2, -1 + sizeof connect2)) + << Chat::expect(QByteArray(connected, -1 + sizeof connected)) + << Chat::expect("\1") // IPv4 address following + << Chat::skipBytes(6) // the server's local address and port + << ftpChat() + + // connect to server by its official name + << Chat::Reconnect + << Chat::send(QByteArray(handshakeNoAuth, -1 + sizeof handshakeNoAuth)) + << Chat::expect(QByteArray(handshakeOkNoAuth, -1 + sizeof handshakeOkNoAuth)) + << Chat::send(QBA(connect2a) + char(QtNetworkSettings::serverName().size()) + QtNetworkSettings::serverName().toLatin1() + QBA(connect1b)) + << Chat::expect(QByteArray(connected, -1 + sizeof connected)) + << Chat::expect("\1") // IPv4 address following + << Chat::skipBytes(6) // the server's local address and port + << ftpChat() + ); +} + +void tst_NetworkSelfTest::socks5ProxyAuth() +{ + netChat(1081, QList<Chat>() + // unauthenticated connect -- will get error + << Chat::send(QByteArray(handshakeNoAuth, -1 + sizeof handshakeNoAuth)) + << Chat::expect(QByteArray(handshakeAuthNotOk, -1 + sizeof handshakeAuthNotOk)) + << Chat::RemoteDisconnect + + // now try to connect with authentication + << Chat::Reconnect + << Chat::send(QByteArray(handshakeAuthPassword, -1 + sizeof handshakeAuthPassword)) + << Chat::expect(QByteArray(handshakeOkPasswdAuth, -1 + sizeof handshakeOkPasswdAuth)) + << Chat::send(QByteArray(connect1, -1 + sizeof connect1)) + << Chat::expect(QByteArray(connected, -1 + sizeof connected)) + << Chat::expect("\1") // IPv4 address following + << Chat::skipBytes(6) // the server's local address and port + << ftpChat() + ); +} + +void tst_NetworkSelfTest::supportsSsl() +{ +#ifdef QT_NO_OPENSSL + QFAIL("SSL not compiled in"); +#else + QVERIFY2(QSslSocket::supportsSsl(), "Could not load SSL libraries"); +#endif +} + +void tst_NetworkSelfTest::smbServer() +{ + static const char contents[] = "This is 34 bytes. Do not change..."; +#ifdef Q_OS_WIN + // use Windows's native UNC support to try and open a file on the server + QString filepath = QString("\\\\%1\\testshare\\test.pri").arg(QtNetworkSettings::winServerName()); + FILE *f = fopen(filepath.toLatin1(), "rb"); + QVERIFY2(f, qt_error_string().toLocal8Bit()); + + char buf[128]; + size_t ret = fread(buf, 1, sizeof buf, f); + fclose(f); + + QCOMPARE(ret, strlen(contents)); + QVERIFY(memcmp(buf, contents, strlen(contents)) == 0); +#else + // try to use Samba + QString progname = "smbclient"; + QProcess smbclient; + smbclient.start(progname, QIODevice::ReadOnly); + if (!smbclient.waitForStarted(2000)) + QSKIP("Could not find smbclient (from Samba), cannot continue testing", SkipAll); + if (!smbclient.waitForFinished(2000) || smbclient.exitStatus() != QProcess::NormalExit) + QSKIP("smbclient isn't working, cannot continue testing", SkipAll); + smbclient.close(); + + // try listing the server + smbclient.start(progname, QStringList() << "-g" << "-N" << "-L" << QtNetworkSettings::winServerName(), QIODevice::ReadOnly); + QVERIFY(smbclient.waitForFinished(5000)); + if (smbclient.exitStatus() != QProcess::NormalExit) + QSKIP("smbclient crashed", SkipAll); + QVERIFY2(smbclient.exitCode() == 0, "Test server not found"); + + QByteArray output = smbclient.readAll(); + QVERIFY(output.contains("Disk|testshare|")); + QVERIFY(output.contains("Disk|testsharewritable|")); + QVERIFY(output.contains("Disk|testsharelargefile|")); + qDebug() << "Test server found and shares are correct"; + + // try getting a file + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + env.insert("PAGER", "/bin/cat"); // just in case + smbclient.setProcessEnvironment(env); + smbclient.start(progname, QStringList() << "-N" << "-c" << "more test.pri" + << QString("\\\\%1\\testshare").arg(QtNetworkSettings::winServerName()), QIODevice::ReadOnly); + QVERIFY(smbclient.waitForFinished(5000)); + if (smbclient.exitStatus() != QProcess::NormalExit) + QSKIP("smbclient crashed", SkipAll); + QVERIFY2(smbclient.exitCode() == 0, "File //qt-test-server/testshare/test.pri not found"); + + output = smbclient.readAll(); + QCOMPARE(output.constData(), contents); + qDebug() << "Test file is correct"; +#endif +} + +QTEST_MAIN(tst_NetworkSelfTest) +#include "tst_networkselftest.moc" diff --git a/tests/auto/integrationtests/qaccessibility/.gitignore b/tests/auto/integrationtests/qaccessibility/.gitignore new file mode 100644 index 0000000000..6fd6ae2d48 --- /dev/null +++ b/tests/auto/integrationtests/qaccessibility/.gitignore @@ -0,0 +1 @@ +tst_qaccessibility diff --git a/tests/auto/integrationtests/qaccessibility/qaccessibility.pro b/tests/auto/integrationtests/qaccessibility/qaccessibility.pro new file mode 100644 index 0000000000..ae046cc3c3 --- /dev/null +++ b/tests/auto/integrationtests/qaccessibility/qaccessibility.pro @@ -0,0 +1,12 @@ +load(qttest_p4) +requires(contains(QT_CONFIG,accessibility)) +QT += widgets +SOURCES += tst_qaccessibility.cpp + +unix:!mac:LIBS+=-lm + +wince*: { + accessneeded.files = $$QT_BUILD_TREE\\plugins\\accessible\\*.dll + accessneeded.path = accessible + DEPLOYMENT += accessneeded +} diff --git a/tests/auto/integrationtests/qaccessibility/tst_qaccessibility.cpp b/tests/auto/integrationtests/qaccessibility/tst_qaccessibility.cpp new file mode 100644 index 0000000000..e1bd968534 --- /dev/null +++ b/tests/auto/integrationtests/qaccessibility/tst_qaccessibility.cpp @@ -0,0 +1,3372 @@ +/**************************************************************************** +** +** 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 <QtGui> +#include <QtWidgets> +#include <math.h> + +#if defined(Q_OS_WIN) && defined(interface) +# undef interface +#endif + + +#include "QtTest/qtestaccessible.h" + +#if defined(Q_OS_WINCE) +extern "C" bool SystemParametersInfo(UINT uiAction, UINT uiParam, PVOID pvParam, UINT fWinIni); +#define SPI_GETPLATFORMTYPE 257 +inline bool IsValidCEPlatform() { + wchar_t tszPlatform[64]; + if (SystemParametersInfo(SPI_GETPLATFORMTYPE, sizeof(tszPlatform) / sizeof(*tszPlatform), tszPlatform, 0)) { + QString platform = QString::fromWCharArray(tszPlatform); + if ((platform == QLatin1String("PocketPC")) || (platform == QLatin1String("Smartphone"))) + return false; + } + return true; +} +#endif + +static inline bool verifyChild(QWidget *child, QAccessibleInterface *interface, + int index, const QRect &domain) +{ + if (!child) { + qWarning("tst_QAccessibility::verifyChild: null pointer to child."); + return false; + } + + if (!interface) { + qWarning("tst_QAccessibility::verifyChild: null pointer to interface."); + return false; + } + + // Verify that we get a valid QAccessibleInterface for the child. + QAccessibleInterface *childInterface = QAccessible::queryAccessibleInterface(child); + if (!childInterface) { + qWarning("tst_QAccessibility::verifyChild: Failed to retrieve interface for child."); + return false; + } + + // QAccessibleInterface::indexOfChild(): + // Verify that indexOfChild() returns an index equal to the index passed in + int indexFromIndexOfChild = interface->indexOfChild(childInterface); + delete childInterface; + if (indexFromIndexOfChild != index) { + qWarning("tst_QAccessibility::verifyChild (indexOfChild()):"); + qWarning() << "Expected:" << index; + qWarning() << "Actual: " << indexFromIndexOfChild; + return false; + } + + // Navigate to child, compare its object and role with the interface from queryAccessibleInterface(child). + QAccessibleInterface *navigatedChildInterface = interface->child(index - 1); + if (navigatedChildInterface == 0) + return false; + + const QRect rectFromInterface = navigatedChildInterface->rect(); + delete navigatedChildInterface; + + // QAccessibleInterface::childAt(): + // Calculate global child position and check that the interface + // returns the correct index for that position. + QPoint globalChildPos = child->mapToGlobal(QPoint(0, 0)); + int indexFromChildAt = interface->childAt(globalChildPos.x(), globalChildPos.y()); + if (indexFromChildAt != index) { + qWarning("tst_QAccessibility::verifyChild (childAt()):"); + qWarning() << "Expected:" << index; + qWarning() << "Actual: " << indexFromChildAt; + return false; + } + + // QAccessibleInterface::rect(): + // Calculate global child geometry and check that the interface + // returns a QRect which is equal to the calculated QRect. + const QRect expectedGlobalRect = QRect(globalChildPos, child->size()); + if (expectedGlobalRect != rectFromInterface) { + qWarning("tst_QAccessibility::verifyChild (rect()):"); + qWarning() << "Expected:" << expectedGlobalRect; + qWarning() << "Actual: " << rectFromInterface; + return false; + } + + // Verify that the child is within its domain. + if (!domain.contains(rectFromInterface)) { + qWarning("tst_QAccessibility::verifyChild: Child is not within its domain."); + return false; + } + + return true; +} + +static inline int indexOfChild(QAccessibleInterface *parentInterface, QWidget *childWidget) +{ + if (!parentInterface || !childWidget) + return -1; + QAccessibleInterface *childInterface = QAccessibleInterface::queryAccessibleInterface(childWidget); + if (!childInterface) + return -1; + int index = parentInterface->indexOfChild(childInterface); + delete childInterface; + return index; +} + +#define EXPECT(cond) \ + do { \ + if (!errorAt && !(cond)) { \ + errorAt = __LINE__; \ + qWarning("level: %d, middle: %d, role: %d (%s)", treelevel, middle, iface->role(), #cond); \ + } \ + } while (0) + +static int verifyHierarchy(QAccessibleInterface *iface) +{ + int errorAt = 0; + static int treelevel = 0; // for error diagnostics + QAccessibleInterface *middleChild, *if2; + middleChild = 0; + ++treelevel; + int middle = iface->childCount()/2 + 1; + if (iface->childCount() >= 2) { + middleChild = iface->child(middle - 1); + } + for (int i = 0; i < iface->childCount() && !errorAt; ++i) { + if2 = iface->child(i); + EXPECT(if2 != 0); + // navigate Ancestor... + QAccessibleInterface *parent = 0; + parent = if2->parent(); + EXPECT(iface->object() == parent->object()); + delete parent; + + // navigate Sibling... +// if (middleChild) { +// entry = if2->navigate(QAccessible::Sibling, middle, &if3); +// EXPECT(entry == 0 && if3->object() == middleChild->object()); +// if (entry == 0) +// delete if3; +// EXPECT(iface->indexOfChild(middleChild) == middle); +// } + + // verify children... + if (!errorAt) + errorAt = verifyHierarchy(if2); + delete if2; + } + delete middleChild; + + --treelevel; + return errorAt; +} + + +//TESTED_FILES= + +class tst_QAccessibility : public QObject +{ + Q_OBJECT +public: + tst_QAccessibility(); + virtual ~tst_QAccessibility(); + +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); +private slots: + void eventTest(); + void customWidget(); + void deletedWidget(); + + void navigateGeometric(); + void navigateHierarchy(); + void sliderTest(); + void navigateCovered(); + void textAttributes(); + void hideShowTest(); + + void actionTest(); + + void applicationTest(); + void mainWindowTest(); + void buttonTest(); + void scrollBarTest(); + void tabTest(); + void tabWidgetTest(); + void menuTest(); + void spinBoxTest(); + void doubleSpinBoxTest(); + void textEditTest(); + void textBrowserTest(); + void mdiAreaTest(); + void mdiSubWindowTest(); + void lineEditTest(); + void workspaceTest(); + void dialogButtonBoxTest(); + void dialTest(); + void rubberBandTest(); + void abstractScrollAreaTest(); + void scrollAreaTest(); + + // Accessible table1 interface is no longer supported on X11, + // where it has been replaced by table2 interface. +#ifndef Q_OS_UNIX + void listViewTest(); + void treeWidgetTest(); + void tableWidgetTest(); + void tableViewTest(); +#else + void table2ListTest(); + void table2TreeTest(); + void table2TableTest(); +#endif + + void calendarWidgetTest(); + void dockWidgetTest(); + void comboBoxTest(); + void accessibleName(); + void labelTest(); + void accelerators(); +}; + +const double Q_PI = 3.14159265358979323846; + +QString eventName(const int ev) +{ + switch(ev) { + case 0x0001: return "SoundPlayed"; + case 0x0002: return "Alert"; + case 0x0003: return "ForegroundChanged"; + case 0x0004: return "MenuStart"; + case 0x0005: return "MenuEnd"; + case 0x0006: return "PopupMenuStart"; + case 0x0007: return "PopupMenuEnd"; + case 0x000C: return "ContextHelpStart"; + case 0x000D: return "ContextHelpEnd"; + case 0x000E: return "DragDropStart"; + case 0x000F: return "DragDropEnd"; + case 0x0010: return "DialogStart"; + case 0x0011: return "DialogEnd"; + case 0x0012: return "ScrollingStart"; + case 0x0013: return "ScrollingEnd"; + case 0x0018: return "MenuCommand"; + + case 0x0116: return "TableModelChanged"; + case 0x011B: return "TextCaretMoved"; + + case 0x8000: return "ObjectCreated"; + case 0x8001: return "ObjectDestroyed"; + case 0x8002: return "ObjectShow"; + case 0x8003: return "ObjectHide"; + case 0x8004: return "ObjectReorder"; + case 0x8005: return "Focus"; + case 0x8006: return "Selection"; + case 0x8007: return "SelectionAdd"; + case 0x8008: return "SelectionRemove"; + case 0x8009: return "SelectionWithin"; + case 0x800A: return "StateChanged"; + case 0x800B: return "LocationChanged"; + case 0x800C: return "NameChanged"; + case 0x800D: return "DescriptionChanged"; + case 0x800E: return "ValueChanged"; + case 0x800F: return "ParentChanged"; + case 0x80A0: return "HelpChanged"; + case 0x80B0: return "DefaultActionChanged"; + case 0x80C0: return "AcceleratorChanged"; + default: return "Unknown Event"; + } +} + +QAccessible::State state(QWidget * const widget) +{ + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(widget); + if (!iface) + qWarning() << "Cannot get QAccessibleInterface for widget"; + QAccessible::State state = (iface ? iface->state() : static_cast<QAccessible::State>(0)); + delete iface; + return state; +} + +class QtTestAccessibleWidget: public QWidget +{ + Q_OBJECT +public: + QtTestAccessibleWidget(QWidget *parent, const char *name): QWidget(parent) + { + setObjectName(name); + QPalette pal; + pal.setColor(backgroundRole(), Qt::black);//black is beautiful + setPalette(pal); + setFixedSize(5, 5); + } +}; + +class QtTestAccessibleWidgetIface: public QAccessibleWidget +{ +public: + QtTestAccessibleWidgetIface(QtTestAccessibleWidget *w): QAccessibleWidget(w) {} + QString text(Text t, int control = 0) const + { + if (t == Help) + return QString::fromLatin1("Help yourself"); + return QAccessibleWidget::text(t, control); + } + static QAccessibleInterface *ifaceFactory(const QString &key, QObject *o) + { + if (key == "QtTestAccessibleWidget") + return new QtTestAccessibleWidgetIface(static_cast<QtTestAccessibleWidget*>(o)); + return 0; + } +}; + +tst_QAccessibility::tst_QAccessibility() +{ +} + +tst_QAccessibility::~tst_QAccessibility() +{ +} + +void tst_QAccessibility::initTestCase() +{ + QTestAccessibility::initialize(); + QAccessible::installFactory(QtTestAccessibleWidgetIface::ifaceFactory); +} + +void tst_QAccessibility::cleanupTestCase() +{ + QTestAccessibility::cleanup(); +} + +void tst_QAccessibility::init() +{ + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::cleanup() +{ + const EventList list = QTestAccessibility::events(); + if (!list.isEmpty()) { + qWarning("%d accessibility event(s) were not handled in testfunction '%s':", list.count(), + QString(QTest::currentTestFunction()).toAscii().constData()); + for (int i = 0; i < list.count(); ++i) + qWarning(" %d: Object: %p Event: '%s' (%d) Child: %d", i + 1, list.at(i).object, + eventName(list.at(i).event).toAscii().constData(), list.at(i).event, list.at(i).child); + } + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::eventTest() +{ + QPushButton* button = new QPushButton(0); + button->setObjectName(QString("Olaf")); + + button->show(); + QVERIFY_EVENT(button, 0, QAccessible::ObjectShow); + button->setFocus(Qt::MouseFocusReason); + QTestAccessibility::clearEvents(); + QTest::mouseClick(button, Qt::LeftButton, 0); + QVERIFY_EVENT(button, 0, QAccessible::StateChanged); + QVERIFY_EVENT(button, 0, QAccessible::StateChanged); + + button->setAccessibleName("Olaf the second"); + QVERIFY_EVENT(button, 0, QAccessible::NameChanged); + button->setAccessibleDescription("This is a button labeled Olaf"); + QVERIFY_EVENT(button, 0, QAccessible::DescriptionChanged); + + button->hide(); + QVERIFY_EVENT(button, 0, QAccessible::ObjectHide); + + delete button; +} + +void tst_QAccessibility::customWidget() +{ + QtTestAccessibleWidget* widget = new QtTestAccessibleWidget(0, "Heinz"); + + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(widget); + QVERIFY(iface != 0); + QVERIFY(iface->isValid()); + QCOMPARE(iface->object(), (QObject*)widget); + QCOMPARE(iface->object()->objectName(), QString("Heinz")); + QCOMPARE(iface->text(QAccessible::Help, 0), QString("Help yourself")); + + delete iface; + delete widget; +} + +void tst_QAccessibility::deletedWidget() +{ + QtTestAccessibleWidget *widget = new QtTestAccessibleWidget(0, "Ralf"); + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(widget); + QVERIFY(iface != 0); + QVERIFY(iface->isValid()); + QCOMPARE(iface->object(), (QObject*)widget); + + delete widget; + widget = 0; + QVERIFY(!iface->isValid()); + delete iface; +} + +void tst_QAccessibility::navigateGeometric() +{ + { + static const int skip = 20; //speed the test up significantly + static const double step = Q_PI / 180; + QWidget *w = new QWidget(0); + w->setObjectName(QString("Josef")); + w->setFixedSize(400, 400); + + // center widget + QtTestAccessibleWidget *center = new QtTestAccessibleWidget(w, "Sol"); + center->move(200, 200); + + // arrange 360 widgets around it in a circle + QtTestAccessibleWidget *aw = 0; + for (int i = 0; i < 360; i += skip) { + aw = new QtTestAccessibleWidget(w, QString::number(i).toLatin1()); + aw->move( int(200.0 + 100.0 * sin(step * (double)i)), int(200.0 + 100.0 * cos(step * (double)i)) ); + } + + aw = new QtTestAccessibleWidget(w, "Earth"); + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(center); + QAccessibleInterface *target = 0; + QVERIFY(iface != 0); + QVERIFY(iface->isValid()); + + w->show(); + QCoreApplication::processEvents(); + QTest::qWait(100); + + // let one widget rotate around center + for (int i = 0; i < 360; i+=skip) { + aw->move( int(200.0 + 75.0 * sin(step * (double)i)), int(200.0 + 75.0 * cos(step * (double)i)) ); + + if (i < 45 || i > 315) { + QCOMPARE(iface->navigate(QAccessible::Down, 0, &target), 0); + } else if ( i < 135 ) { + QCOMPARE(iface->navigate(QAccessible::Right, 0, &target), 0); + } else if ( i < 225 ) { + QCOMPARE(iface->navigate(QAccessible::Up, 0, &target), 0); + } else { + QCOMPARE(iface->navigate(QAccessible::Left, 0, &target), 0); + } + + QVERIFY(target); + QVERIFY(target->isValid()); + QVERIFY(target->object()); + QCOMPARE(target->object()->objectName(), aw->objectName()); + delete target; target = 0; + } + + // test invisible widget + target = QAccessible::queryAccessibleInterface(aw); + QVERIFY(!(target->state() & QAccessible::Invisible)); + aw->hide(); + QVERIFY(target->state() & QAccessible::Invisible); + delete target; target = 0; + + aw->move(center->x() + 10, center->y()); + QCOMPARE(iface->navigate(QAccessible::Right, 0, &target), 0); + QVERIFY(target); + QVERIFY(target->isValid()); + QVERIFY(target->object()); + QVERIFY(QString(target->object()->objectName()) != "Earth"); + delete target; target = 0; + + aw->move(center->x() - 10, center->y()); + QCOMPARE(iface->navigate(QAccessible::Left, 0, &target), 0); + QVERIFY(target); + QVERIFY(target->isValid()); + QVERIFY(target->object()); + QVERIFY(QString(target->object()->objectName()) != "Earth"); + delete target; target = 0; + + aw->move(center->x(), center->y() + 10); + QCOMPARE(iface->navigate(QAccessible::Down, 0, &target), 0); + QVERIFY(target); + QVERIFY(target->isValid()); + QVERIFY(target->object()); + QVERIFY(QString(target->object()->objectName()) != "Earth"); + delete target; target = 0; + + aw->move(center->x(), center->y() - 10); + QCOMPARE(iface->navigate(QAccessible::Up, 0, &target), 0); + QVERIFY(target); + QVERIFY(target->isValid()); + QVERIFY(target->object()); + QVERIFY(QString(target->object()->objectName()) != "Earth"); + delete target; target = 0; + + delete iface; + delete w; + } + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::sliderTest() +{ + { + QSlider *slider = new QSlider(0); + slider->setObjectName(QString("Slidy")); + slider->show(); + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(slider); + QVERIFY(iface != 0); + QVERIFY(iface->isValid()); + + QCOMPARE(iface->childCount(), 0); + QCOMPARE(iface->role(), QAccessible::Slider); + + QAccessibleValueInterface *valueIface = iface->valueInterface(); + QVERIFY(valueIface != 0); + QCOMPARE(valueIface->minimumValue().toInt(), slider->minimum()); + QCOMPARE(valueIface->maximumValue().toInt(), slider->maximum()); + slider->setValue(50); + QCOMPARE(valueIface->currentValue().toInt(), slider->value()); + slider->setValue(0); + QCOMPARE(valueIface->currentValue().toInt(), slider->value()); + slider->setValue(100); + QCOMPARE(valueIface->currentValue().toInt(), slider->value()); + valueIface->setCurrentValue(77); + QCOMPARE(77, slider->value()); + + delete iface; + delete slider; + } + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::navigateCovered() +{ + { + QWidget *w = new QWidget(0); + w->setObjectName(QString("Harry")); + QWidget *w1 = new QWidget(w); + w1->setObjectName(QString("1")); + QWidget *w2 = new QWidget(w); + w2->setObjectName(QString("2")); + w->show(); +#if defined(Q_OS_UNIX) + QCoreApplication::processEvents(); + QTest::qWait(100); +#endif + + w->setFixedSize(6, 6); + w1->setFixedSize(5, 5); + w2->setFixedSize(5, 5); + w2->move(0, 0); + w1->raise(); + + QAccessibleInterface *iface1 = QAccessible::queryAccessibleInterface(w1); + QVERIFY(iface1 != 0); + QVERIFY(iface1->isValid()); + QAccessibleInterface *iface2 = QAccessible::queryAccessibleInterface(w2); + QVERIFY(iface2 != 0); + QVERIFY(iface2->isValid()); + QAccessibleInterface *iface3 = 0; + + QCOMPARE(iface1->navigate(QAccessible::Covers, -42, &iface3), -1); + QVERIFY(iface3 == 0); + QCOMPARE(iface1->navigate(QAccessible::Covers, 0, &iface3), -1); + QVERIFY(iface3 == 0); + QCOMPARE(iface1->navigate(QAccessible::Covers, 2, &iface3), -1); + QVERIFY(iface3 == 0); + + for (int loop = 0; loop < 2; ++loop) { + for (int x = 0; x < w->width(); ++x) { + for (int y = 0; y < w->height(); ++y) { + w1->move(x, y); + if (w1->geometry().intersects(w2->geometry())) { + QVERIFY(iface1->relationTo(0, iface2, 0) & QAccessible::Covers); + QVERIFY(iface2->relationTo(0, iface1, 0) & QAccessible::Covered); + QCOMPARE(iface1->navigate(QAccessible::Covered, 1, &iface3), 0); + QVERIFY(iface3 != 0); + QVERIFY(iface3->isValid()); + QCOMPARE(iface3->object(), iface2->object()); + delete iface3; iface3 = 0; + QCOMPARE(iface2->navigate(QAccessible::Covers, 1, &iface3), 0); + QVERIFY(iface3 != 0); + QVERIFY(iface3->isValid()); + QCOMPARE(iface3->object(), iface1->object()); + delete iface3; iface3 = 0; + } else { + QVERIFY(!(iface1->relationTo(0, iface2, 0) & QAccessible::Covers)); + QVERIFY(!(iface2->relationTo(0, iface1, 0) & QAccessible::Covered)); + QCOMPARE(iface1->navigate(QAccessible::Covered, 1, &iface3), -1); + QVERIFY(iface3 == 0); + QCOMPARE(iface1->navigate(QAccessible::Covers, 1, &iface3), -1); + QVERIFY(iface3 == 0); + QCOMPARE(iface2->navigate(QAccessible::Covered, 1, &iface3), -1); + QVERIFY(iface3 == 0); + QCOMPARE(iface2->navigate(QAccessible::Covers, 1, &iface3), -1); + QVERIFY(iface3 == 0); + } + } + } + if (!loop) { + // switch children for second loop + w2->raise(); + QAccessibleInterface *temp = iface1; + iface1 = iface2; + iface2 = temp; + } + } + delete iface1; iface1 = 0; + delete iface2; iface2 = 0; + iface1 = QAccessible::queryAccessibleInterface(w1); + QVERIFY(iface1 != 0); + QVERIFY(iface1->isValid()); + iface2 = QAccessible::queryAccessibleInterface(w2); + QVERIFY(iface2 != 0); + QVERIFY(iface2->isValid()); + + w1->move(0,0); + w2->move(0,0); + w1->raise(); + QVERIFY(iface1->relationTo(0, iface2, 0) & QAccessible::Covers); + QVERIFY(iface2->relationTo(0, iface1, 0) & QAccessible::Covered); + QVERIFY(!(iface1->state() & QAccessible::Invisible)); + w1->hide(); + QVERIFY(iface1->state() & QAccessible::Invisible); + QVERIFY(!(iface1->relationTo(0, iface2, 0) & QAccessible::Covers)); + QVERIFY(!(iface2->relationTo(0, iface1, 0) & QAccessible::Covered)); + QCOMPARE(iface2->navigate(QAccessible::Covered, 1, &iface3), -1); + QVERIFY(iface3 == 0); + QCOMPARE(iface1->navigate(QAccessible::Covers, 1, &iface3), -1); + QVERIFY(iface3 == 0); + + delete iface1; iface1 = 0; + delete iface2; iface2 = 0; + delete w; + } + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::navigateHierarchy() +{ + { + QWidget *w = new QWidget(0); + w->setObjectName(QString("Hans")); + w->show(); + QWidget *w1 = new QWidget(w); + w1->setObjectName(QString("1")); + w1->show(); + QWidget *w2 = new QWidget(w); + w2->setObjectName(QString("2")); + w2->show(); + QWidget *w3 = new QWidget(w); + w3->setObjectName(QString("3")); + w3->show(); + QWidget *w31 = new QWidget(w3); + w31->setObjectName(QString("31")); + w31->show(); + + QAccessibleInterface *target = 0; + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(w); + QVERIFY(iface != 0); + QVERIFY(iface->isValid()); + + QCOMPARE(iface->navigate(QAccessible::Sibling, -42, &target), -1); + QVERIFY(target == 0); + QCOMPARE(iface->navigate(QAccessible::Sibling, 42, &target), -1); + QVERIFY(target == 0); + + target = iface->child(14); + QVERIFY(target == 0); + target = iface->child(-1); + QVERIFY(target == 0); + target = iface->child(0); + QAccessibleInterface *interfaceW1 = iface->child(0); + QVERIFY(target); + QVERIFY(target->isValid()); + QCOMPARE(target->object(), (QObject*)w1); + QVERIFY(interfaceW1 != 0); + QVERIFY(interfaceW1->isValid()); + QCOMPARE(interfaceW1->object(), (QObject*)w1); + delete interfaceW1; + delete iface; iface = 0; + + QCOMPARE(target->navigate(QAccessible::Sibling, 0, &iface), -1); + QVERIFY(iface == 0); + QCOMPARE(target->navigate(QAccessible::Sibling, 42, &iface), -1); + QVERIFY(iface == 0); + QCOMPARE(target->navigate(QAccessible::Sibling, -42, &iface), -1); + QVERIFY(iface == 0); + QCOMPARE(target->navigate(QAccessible::Sibling, 2, &iface), 0); + QVERIFY(iface != 0); + QVERIFY(iface->isValid()); + QCOMPARE(iface->object(), (QObject*)w2); + delete target; target = 0; + QCOMPARE(iface->navigate(QAccessible::Sibling, 3, &target), 0); + QVERIFY(target != 0); + QVERIFY(target->isValid()); + QCOMPARE(target->object(), (QObject*)w3); + delete iface; iface = 0; + + iface = target->child(0); + QVERIFY(iface != 0); + QVERIFY(iface->isValid()); + QCOMPARE(iface->object(), (QObject*)w31); + delete target; target = 0; + + QCOMPARE(iface->navigate(QAccessible::Sibling, -1, &target), -1); + QVERIFY(target == 0); + QCOMPARE(iface->navigate(QAccessible::Sibling, 0, &target), -1); + QVERIFY(target == 0); + QCOMPARE(iface->navigate(QAccessible::Sibling, 1, &target), 0); + QVERIFY(target != 0); + QVERIFY(target->isValid()); + QCOMPARE(target->object(), (QObject*)w31); + delete iface; iface = 0; + + iface = target->parent(); + QVERIFY(iface != 0); + QVERIFY(iface->isValid()); + QCOMPARE(iface->object(), (QObject*)w3); + delete iface; iface = 0; + delete target; target = 0; + + delete w; + } + QTestAccessibility::clearEvents(); +} + +#define QSETCOMPARE(thetypename, elements, otherelements) \ + QCOMPARE((QSet<thetypename>() << elements), (QSet<thetypename>() << otherelements)) + +static QWidget *createWidgets() +{ + QWidget *w = new QWidget(); + + QHBoxLayout *box = new QHBoxLayout(w); + + int i = 0; + box->addWidget(new QComboBox(w)); + box->addWidget(new QPushButton(QString::fromAscii("widget text %1").arg(i++), w)); + box->addWidget(new QHeaderView(Qt::Vertical, w)); + box->addWidget(new QTreeView(w)); + box->addWidget(new QTreeWidget(w)); + box->addWidget(new QListView(w)); + box->addWidget(new QListWidget(w)); + box->addWidget(new QTableView(w)); + box->addWidget(new QTableWidget(w)); + box->addWidget(new QCalendarWidget(w)); + box->addWidget(new QDialogButtonBox(w)); + box->addWidget(new QGroupBox(QString::fromAscii("widget text %1").arg(i++), w)); + box->addWidget(new QFrame(w)); + box->addWidget(new QLineEdit(QString::fromAscii("widget text %1").arg(i++), w)); + box->addWidget(new QProgressBar(w)); + box->addWidget(new QTabWidget(w)); + box->addWidget(new QCheckBox(QString::fromAscii("widget text %1").arg(i++), w)); + box->addWidget(new QRadioButton(QString::fromAscii("widget text %1").arg(i++), w)); + box->addWidget(new QDial(w)); + box->addWidget(new QScrollBar(w)); + box->addWidget(new QSlider(w)); + box->addWidget(new QDateTimeEdit(w)); + box->addWidget(new QDoubleSpinBox(w)); + box->addWidget(new QSpinBox(w)); + box->addWidget(new QLabel(QString::fromAscii("widget text %1").arg(i++), w)); + box->addWidget(new QLCDNumber(w)); + box->addWidget(new QStackedWidget(w)); + box->addWidget(new QToolBox(w)); + box->addWidget(new QLabel(QString::fromAscii("widget text %1").arg(i++), w)); + box->addWidget(new QTextEdit(QString::fromAscii("widget text %1").arg(i++), w)); + + /* Not in the list + * QAbstractItemView, QGraphicsView, QScrollArea, + * QToolButton, QDockWidget, QFocusFrame, QMainWindow, QMenu, QMenuBar, QSizeGrip, QSplashScreen, QSplitterHandle, + * QStatusBar, QSvgWidget, QTabBar, QToolBar, QWorkspace, QSplitter + */ + return w; +} + +void tst_QAccessibility::accessibleName() +{ + QWidget *toplevel = createWidgets(); + toplevel->show(); +#if defined(Q_OS_UNIX) + QCoreApplication::processEvents(); + QTest::qWait(100); +#endif + QLayout *lout = toplevel->layout(); + for (int i = 0; i < lout->count(); i++) { + QLayoutItem *item = lout->itemAt(i); + QWidget *child = item->widget(); + + QString name = tr("Widget Name %1").arg(i); + child->setAccessibleName(name); + QAccessibleInterface *acc = QAccessible::queryAccessibleInterface(child); + QCOMPARE(acc->text(QAccessible::Name), name); + + QString desc = tr("Widget Description %1").arg(i); + child->setAccessibleDescription(desc); + QCOMPARE(acc->text(QAccessible::Description), desc); + + } + + delete toplevel; + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::textAttributes() +{ + QTextEdit textEdit; + int startOffset; + int endOffset; + QString attributes; + QString text("<html><head></head><body>" + "Hello, <b>this</b> is an <i><b>example</b> text</i>." + "<span style=\"font-family: monospace\">Multiple fonts are used.</span>" + "Multiple <span style=\"font-size: 8pt\">text sizes</span> are used." + "Let's give some color to <span style=\"color:#f0f1f2; background-color:#14f01e\">Qt</span>." + "</body></html>"); + + textEdit.setText(text); + QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(&textEdit); + + QAccessibleTextInterface *textInterface=interface->textInterface(); + + QVERIFY(textInterface); + QCOMPARE(textInterface->characterCount(), 112); + + attributes = textInterface->attributes(10, &startOffset, &endOffset); + QCOMPARE(startOffset, 7); + QCOMPARE(endOffset, 11); + attributes.prepend(';'); + QVERIFY(attributes.contains(QLatin1String(";font-weight:bold;"))); + + attributes = textInterface->attributes(18, &startOffset, &endOffset); + QCOMPARE(startOffset, 18); + QCOMPARE(endOffset, 25); + attributes.prepend(';'); + QVERIFY(attributes.contains(QLatin1String(";font-weight:bold;"))); + QVERIFY(attributes.contains(QLatin1String(";font-style:italic;"))); + + attributes = textInterface->attributes(34, &startOffset, &endOffset); + QCOMPARE(startOffset, 31); + QCOMPARE(endOffset, 55); + attributes.prepend(';'); + QVERIFY(attributes.contains(QLatin1String(";font-family:\"monospace\";"))); + + attributes = textInterface->attributes(65, &startOffset, &endOffset); + QCOMPARE(startOffset, 64); + QCOMPARE(endOffset, 74); + attributes.prepend(';'); + QVERIFY(attributes.contains(QLatin1String(";font-size:8pt;"))); + + attributes = textInterface->attributes(110, &startOffset, &endOffset); + QCOMPARE(startOffset, 109); + QCOMPARE(endOffset, 111); + attributes.prepend(';'); + QVERIFY(attributes.contains(QLatin1String(";background-color:rgb(20,240,30);"))); + QVERIFY(attributes.contains(QLatin1String(";color:rgb(240,241,242);"))); +} + +void tst_QAccessibility::hideShowTest() +{ + QWidget * const window = new QWidget(); + QWidget * const child = new QWidget(window); + + QVERIFY(state(window) & QAccessible::Invisible); + QVERIFY(state(child) & QAccessible::Invisible); + + QTestAccessibility::clearEvents(); + + // show() and veryfy that both window and child are not invisible and get ObjectShow events. + window->show(); + QVERIFY(state(window) ^ QAccessible::Invisible); + QVERIFY(state(child) ^ QAccessible::Invisible); + QVERIFY(QTestAccessibility::events().contains(QTestAccessibilityEvent(window, 0, QAccessible::ObjectShow))); + QVERIFY(QTestAccessibility::events().contains(QTestAccessibilityEvent(child, 0, QAccessible::ObjectShow))); + QTestAccessibility::clearEvents(); + + // hide() and veryfy that both window and child are invisible and get ObjectHide events. + window->hide(); + QVERIFY(state(window) & QAccessible::Invisible); + QVERIFY(state(child) & QAccessible::Invisible); + QVERIFY(QTestAccessibility::events().contains(QTestAccessibilityEvent(window, 0, QAccessible::ObjectHide))); + QVERIFY(QTestAccessibility::events().contains(QTestAccessibilityEvent(child, 0, QAccessible::ObjectHide))); + QTestAccessibility::clearEvents(); + + delete window; + QTestAccessibility::clearEvents(); +} + + +void tst_QAccessibility::actionTest() +{ + QCOMPARE(QAccessibleActionInterface::PressAction, QString("Press")); + + QWidget *widget = new QWidget; + widget->show(); + + QAccessibleInterface *test = QAccessible::queryAccessibleInterface(widget); + QVERIFY(test); + QVERIFY(test->isValid()); + +// QCOMPARE(test->actionText(1, QAccessible::Name, 0), QString()); +// QCOMPARE(test->actionText(0, QAccessible::Name, 1), QString()); +// QCOMPARE(test->actionText(1, QAccessible::Name, 1), QString()); +// QCOMPARE(test->actionText(QAccessible::SetFocus, QAccessible::Name, -1), QString()); + +// QCOMPARE(test->actionText(QAccessible::DefaultAction, QAccessible::Name, 0), QString("SetFocus")); +// QCOMPARE(test->actionText(QAccessible::SetFocus, QAccessible::Name, 0), QString("SetFocus")); + + delete test; + delete widget; + + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::applicationTest() +{ + QLatin1String name = QLatin1String("My Name"); + qApp->setApplicationName(name); + QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(qApp); + QCOMPARE(interface->text(QAccessible::Name, 0), name); + QCOMPARE(interface->role(), QAccessible::Application); + delete interface; +} + +void tst_QAccessibility::mainWindowTest() +{ + QMainWindow *mw = new QMainWindow; + mw->resize(300, 200); + mw->show(); // triggers layout + + QLatin1String name = QLatin1String("I am the main window"); + mw->setWindowTitle(name); + QTest::qWaitForWindowShown(mw); + QVERIFY_EVENT(mw, 0, QAccessible::ObjectShow); + + QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(mw); + QCOMPARE(interface->text(QAccessible::Name, 0), name); + QCOMPARE(interface->role(), QAccessible::Window); + delete interface; + delete mw; + QTestAccessibility::clearEvents(); +} + +class CounterButton : public QPushButton { + Q_OBJECT +public: + CounterButton(const QString& name, QWidget* parent) + : QPushButton(name, parent), clickCount(0) + { + connect(this, SIGNAL(clicked(bool)), SLOT(incClickCount())); + } + int clickCount; +public Q_SLOTS: + void incClickCount() { + ++clickCount; + } +}; + +void tst_QAccessibility::buttonTest() +{ + QWidget window; + window.setLayout(new QVBoxLayout); + + // Standard push button + CounterButton pushButton("Ok", &window); + + // toggle button + QPushButton toggleButton("Toggle", &window); + toggleButton.setCheckable(true); + + // standard checkbox + QCheckBox checkBox("Check me!", &window); + + // tristate checkbox + QCheckBox tristate("Tristate!", &window); + tristate.setTristate(TRUE); + + // radiobutton + QRadioButton radio("Radio me!", &window); + + // standard toolbutton + QToolButton toolbutton(&window); + toolbutton.setText("Tool"); + toolbutton.setMinimumSize(20,20); + + // standard toolbutton + QToolButton toggletool(&window); + toggletool.setCheckable(true); + toggletool.setText("Toggle"); + toggletool.setMinimumSize(20,20); + + // test push button + QAccessibleInterface* interface = QAccessible::queryAccessibleInterface(&pushButton); + QAccessibleActionInterface* actionInterface = interface->actionInterface(); + QVERIFY(actionInterface != 0); + QCOMPARE(interface->role(), QAccessible::PushButton); + + // currently our buttons only have click as action, press and release are missing + QCOMPARE(actionInterface->actionNames().size(), 1); + QCOMPARE(actionInterface->actionNames(), QStringList() << QAccessibleActionInterface::PressAction); + QCOMPARE(pushButton.clickCount, 0); + actionInterface->doAction(QAccessibleActionInterface::PressAction); + QTest::qWait(500); + QCOMPARE(pushButton.clickCount, 1); + delete interface; + + // test toggle button + interface = QAccessible::queryAccessibleInterface(&toggleButton); + actionInterface = interface->actionInterface(); + QCOMPARE(interface->role(), QAccessible::CheckBox); + QCOMPARE(actionInterface->actionNames(), QStringList() << QAccessibleActionInterface::CheckAction); + QCOMPARE(actionInterface->localizedActionDescription(QAccessibleActionInterface::CheckAction), QString("Checks the checkbox")); + QVERIFY(!toggleButton.isChecked()); + QVERIFY((interface->state() & QAccessible::Checked) == 0); + actionInterface->doAction(QAccessibleActionInterface::CheckAction); + QTest::qWait(500); + QVERIFY(toggleButton.isChecked()); + QCOMPARE(actionInterface->actionNames().at(0), QAccessibleActionInterface::UncheckAction); + QVERIFY(interface->state() & QAccessible::Checked); + delete interface; + + { + // test menu push button + QAction *foo = new QAction("Foo", 0); + foo->setShortcut(QKeySequence("Ctrl+F")); + QMenu *menu = new QMenu(); + menu->addAction(foo); + QPushButton menuButton; + menuButton.setMenu(menu); + menuButton.show(); + QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(&menuButton); + QCOMPARE(interface->role(), QAccessible::ButtonMenu); + QVERIFY(interface->state() & QAccessible::HasPopup); + QCOMPARE(interface->actionInterface()->actionNames(), QStringList() << QAccessibleActionInterface::ShowMenuAction); + // showing the menu enters a new event loop... +// interface->actionInterface()->doAction(QAccessibleActionInterface::ShowMenuAction); +// QTest::qWait(500); + delete interface; + delete menu; + } + + // test check box + interface = QAccessible::queryAccessibleInterface(&checkBox); + actionInterface = interface->actionInterface(); + QCOMPARE(interface->role(), QAccessible::CheckBox); + QCOMPARE(actionInterface->actionNames(), QStringList() << QAccessibleActionInterface::CheckAction); + QVERIFY((interface->state() & QAccessible::Checked) == 0); + actionInterface->doAction(QAccessibleActionInterface::CheckAction); + QTest::qWait(500); + QCOMPARE(actionInterface->actionNames(), QStringList() << QAccessibleActionInterface::UncheckAction); + QVERIFY(interface->state() & QAccessible::Checked); + QVERIFY(checkBox.isChecked()); + delete interface; + + // test radiobutton + interface = QAccessible::queryAccessibleInterface(&radio); + actionInterface = interface->actionInterface(); + QCOMPARE(interface->role(), QAccessible::RadioButton); + QCOMPARE(actionInterface->actionNames(), QStringList() << QAccessibleActionInterface::CheckAction); + QVERIFY((interface->state() & QAccessible::Checked) == 0); + actionInterface->doAction(QAccessibleActionInterface::CheckAction); + QTest::qWait(500); + QCOMPARE(actionInterface->actionNames(), QStringList() << QAccessibleActionInterface::CheckAction); + QVERIFY(interface->state() & QAccessible::Checked); + QVERIFY(checkBox.isChecked()); + delete interface; + +// // test standard toolbutton +// QVERIFY(QAccessible::queryAccessibleInterface(&toolbutton, &test)); +// QCOMPARE(test->role(), QAccessible::PushButton); +// QCOMPARE(test->defaultAction(0), QAccessible::Press); +// QCOMPARE(test->actionText(test->defaultAction(0), QAccessible::Name, 0), QString("Press")); +// QCOMPARE(test->state(), (int)QAccessible::Normal); +// test->release(); + +// // toggle tool button +// QVERIFY(QAccessible::queryAccessibleInterface(&toggletool, &test)); +// QCOMPARE(test->role(), QAccessible::CheckBox); +// QCOMPARE(test->defaultAction(0), QAccessible::Press); +// QCOMPARE(test->actionText(test->defaultAction(0), QAccessible::Name, 0), QString("Check")); +// QCOMPARE(test->state(), (int)QAccessible::Normal); +// QVERIFY(test->doAction(QAccessible::Press, 0)); +// QTest::qWait(500); +// QCOMPARE(test->actionText(test->defaultAction(0), QAccessible::Name, 0), QString("Uncheck")); +// QCOMPARE(test->state(), (int)QAccessible::Checked); +// test->release(); + +// // test menu toolbutton +// QVERIFY(QAccessible::queryAccessibleInterface(&menuToolButton, &test)); +// QCOMPARE(test->role(), QAccessible::ButtonMenu); +// QCOMPARE(test->defaultAction(0), 1); +// QCOMPARE(test->actionText(test->defaultAction(0), QAccessible::Name, 0), QString("Open")); +// QCOMPARE(test->state(), (int)QAccessible::HasPopup); +// QCOMPARE(test->actionCount(0), 1); +// QCOMPARE(test->actionText(QAccessible::Press, QAccessible::Name, 0), QString("Press")); +// test->release(); + +// // test split menu toolbutton +// QVERIFY(QAccessible::queryAccessibleInterface(&splitToolButton, &test)); +// QCOMPARE(test->childCount(), 2); +// QCOMPARE(test->role(), QAccessible::ButtonDropDown); +// QCOMPARE(test->role(1), QAccessible::PushButton); +// QCOMPARE(test->role(2), QAccessible::ButtonMenu); +// QCOMPARE(test->defaultAction(0), QAccessible::Press); +// QCOMPARE(test->defaultAction(1), QAccessible::Press); +// QCOMPARE(test->defaultAction(2), QAccessible::Press); +// QCOMPARE(test->actionText(test->defaultAction(0), QAccessible::Name, 0), QString("Press")); +// QCOMPARE(test->state(), (int)QAccessible::HasPopup); +// QCOMPARE(test->actionCount(0), 1); +// QCOMPARE(test->actionText(1, QAccessible::Name, 0), QString("Open")); +// QCOMPARE(test->actionText(test->defaultAction(1), QAccessible::Name, 1), QString("Press")); +// QCOMPARE(test->state(1), (int)QAccessible::Normal); +// QCOMPARE(test->actionText(test->defaultAction(2), QAccessible::Name, 2), QString("Open")); +// QCOMPARE(test->state(2), (int)QAccessible::HasPopup); +// test->release(); + + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::scrollBarTest() +{ + QScrollBar *scrollBar = new QScrollBar(Qt::Horizontal); + QAccessibleInterface * const scrollBarInterface = QAccessible::queryAccessibleInterface(scrollBar); + QVERIFY(scrollBarInterface); + QVERIFY(scrollBarInterface->state() & QAccessible::Invisible); + scrollBar->resize(200, 50); + scrollBar->show(); + QVERIFY(scrollBarInterface->state() ^ QAccessible::Invisible); + QVERIFY(QTestAccessibility::events().contains(QTestAccessibilityEvent(scrollBar, 0, QAccessible::ObjectShow))); + QTestAccessibility::clearEvents(); + + scrollBar->hide(); + QVERIFY(scrollBarInterface->state() & QAccessible::Invisible); + QVERIFY(QTestAccessibility::events().contains(QTestAccessibilityEvent(scrollBar, 0, QAccessible::ObjectHide))); + QTestAccessibility::clearEvents(); + + // Test that the left/right subcontrols are set to unavailable when the scrollBar is at the minimum/maximum. + scrollBar->show(); + scrollBar->setMinimum(11); + scrollBar->setMaximum(111); + + QAccessibleValueInterface *valueIface = scrollBarInterface->valueInterface(); + QVERIFY(valueIface != 0); + QCOMPARE(valueIface->minimumValue().toInt(), scrollBar->minimum()); + QCOMPARE(valueIface->maximumValue().toInt(), scrollBar->maximum()); + scrollBar->setValue(50); + QCOMPARE(valueIface->currentValue().toInt(), scrollBar->value()); + scrollBar->setValue(0); + QCOMPARE(valueIface->currentValue().toInt(), scrollBar->value()); + scrollBar->setValue(100); + QCOMPARE(valueIface->currentValue().toInt(), scrollBar->value()); + valueIface->setCurrentValue(77); + QCOMPARE(77, scrollBar->value()); + + const QRect scrollBarRect = scrollBarInterface->rect(); + QVERIFY(scrollBarRect.isValid()); + + delete scrollBarInterface; + delete scrollBar; + + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::tabTest() +{ + QTabBar *tabBar = new QTabBar(); + tabBar->show(); + + QAccessibleInterface * const interface = QAccessible::queryAccessibleInterface(tabBar); + QVERIFY(interface); + QCOMPARE(interface->childCount(), 2); + + // Test that the Invisible bit for the navigation buttons gets set + // and cleared correctly. + QAccessibleInterface *leftButton = interface->child(0); + QCOMPARE(leftButton->role(), QAccessible::PushButton); + QVERIFY(leftButton->state() & QAccessible::Invisible); + delete leftButton; + + const int lots = 5; + for (int i = 0; i < lots; ++i) + tabBar->addTab("Foo"); + + QAccessibleInterface *child1 = interface->child(0); + QAccessibleInterface *child2 = interface->child(1); + QVERIFY(child1); + QCOMPARE(child1->role(), QAccessible::PageTab); + QVERIFY(child2); + QCOMPARE(child2->role(), QAccessible::PageTab); + + QVERIFY((child1->state() & QAccessible::Invisible) == false); + tabBar->hide(); + + QCoreApplication::processEvents(); + QTest::qWait(100); + + QVERIFY(child1->state() & QAccessible::Invisible); + + tabBar->show(); + tabBar->setCurrentIndex(0); + + // Test that sending a focus action to a tab does not select it. +// child2->doAction(QAccessible::Focus, 2, QVariantList()); + QCOMPARE(tabBar->currentIndex(), 0); + + // Test that sending a press action to a tab selects it. + QVERIFY(child2->actionInterface()); + QCOMPARE(child2->actionInterface()->actionNames(), QStringList() << QAccessibleActionInterface::PressAction); + QCOMPARE(tabBar->currentIndex(), 0); + child2->actionInterface()->doAction(QAccessibleActionInterface::PressAction); + QCOMPARE(tabBar->currentIndex(), 1); + + delete tabBar; + delete interface; + delete child1; + delete child2; + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::tabWidgetTest() +{ + QTabWidget *tabWidget = new QTabWidget(); + tabWidget->show(); + + // the interface for the tab is just a container for tabbar and stacked widget + QAccessibleInterface * const interface = QAccessible::queryAccessibleInterface(tabWidget); + QVERIFY(interface); + QCOMPARE(interface->childCount(), 2); + QCOMPARE(interface->role(), QAccessible::Client); + + // Create pages, check navigation + QLabel *label1 = new QLabel("Page 1", tabWidget); + tabWidget->addTab(label1, "Tab 1"); + QLabel *label2 = new QLabel("Page 2", tabWidget); + tabWidget->addTab(label2, "Tab 2"); + + QCOMPARE(interface->childCount(), 2); + + QAccessibleInterface* tabBarInterface = 0; + // there is no special logic to sort the children, so the contents will be 1, the tab bar 2 + tabBarInterface = interface->child(1); + QVERIFY(tabBarInterface); + QCOMPARE(tabBarInterface->childCount(), 4); + QCOMPARE(tabBarInterface->role(), QAccessible::PageTabList); + + QAccessibleInterface* tabButton1Interface = tabBarInterface->child(0); + QVERIFY(tabButton1Interface); + QCOMPARE(tabButton1Interface->role(), QAccessible::PageTab); + QCOMPARE(tabButton1Interface->text(QAccessible::Name), QLatin1String("Tab 1")); + + QAccessibleInterface* tabButton2Interface = tabBarInterface->child(1); + QVERIFY(tabButton1Interface); + QCOMPARE(tabButton2Interface->role(), QAccessible::PageTab); + QCOMPARE(tabButton2Interface->text(QAccessible::Name), QLatin1String("Tab 2")); + + QAccessibleInterface* tabButtonLeft = tabBarInterface->child(2); + QVERIFY(tabButtonLeft); + QCOMPARE(tabButtonLeft->role(), QAccessible::PushButton); + QCOMPARE(tabButtonLeft->text(QAccessible::Name), QLatin1String("Scroll Left")); + + QAccessibleInterface* tabButtonRight = tabBarInterface->child(3); + QVERIFY(tabButtonRight); + QCOMPARE(tabButtonRight->role(), QAccessible::PushButton); + QCOMPARE(tabButtonRight->text(QAccessible::Name), QLatin1String("Scroll Right")); + delete tabButton1Interface; + delete tabButton2Interface; + delete tabButtonLeft; + delete tabButtonRight; + + QAccessibleInterface* stackWidgetInterface = interface->child(0); + QVERIFY(stackWidgetInterface); + QCOMPARE(stackWidgetInterface->childCount(), 2); + QCOMPARE(stackWidgetInterface->role(), QAccessible::LayeredPane); + + QAccessibleInterface* stackChild1Interface = stackWidgetInterface->child(0); + QVERIFY(stackChild1Interface); +#ifndef Q_CC_INTEL + QCOMPARE(stackChild1Interface->childCount(), 0); +#endif + QCOMPARE(stackChild1Interface->role(), QAccessible::StaticText); + QCOMPARE(stackChild1Interface->text(QAccessible::Name), QLatin1String("Page 1")); + QCOMPARE(label1, stackChild1Interface->object()); + + // Navigation in stack widgets should be consistent + QAccessibleInterface* parent = stackChild1Interface->parent(); + QVERIFY(parent); +#ifndef Q_CC_INTEL + QCOMPARE(parent->childCount(), 2); +#endif + QCOMPARE(parent->role(), QAccessible::LayeredPane); + delete parent; + + QAccessibleInterface* stackChild2Interface = stackWidgetInterface->child(1); + QVERIFY(stackChild2Interface); + QCOMPARE(stackChild2Interface->childCount(), 0); + QCOMPARE(stackChild2Interface->role(), QAccessible::StaticText); + QCOMPARE(label2, stackChild2Interface->object()); + QCOMPARE(label2->text(), stackChild2Interface->text(QAccessible::Name)); + + parent = stackChild2Interface->parent(); + QVERIFY(parent); +#ifndef Q_CC_INTEL + QCOMPARE(parent->childCount(), 2); +#endif + QCOMPARE(parent->role(), QAccessible::LayeredPane); + delete parent; + + delete tabBarInterface; + delete stackChild1Interface; + delete stackChild2Interface; + delete stackWidgetInterface; + delete interface; + delete tabWidget; + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::menuTest() +{ + { + QMainWindow mw; + mw.resize(300, 200); + QMenu *file = mw.menuBar()->addMenu("&File"); + QMenu *fileNew = file->addMenu("&New..."); + fileNew->menuAction()->setShortcut(tr("Ctrl+N")); + fileNew->addAction("Text file"); + fileNew->addAction("Image file"); + file->addAction("&Open")->setShortcut(tr("Ctrl+O")); + file->addAction("&Save")->setShortcut(tr("Ctrl+S")); + file->addSeparator(); + file->addAction("E&xit")->setShortcut(tr("Alt+F4")); + + QMenu *edit = mw.menuBar()->addMenu("&Edit"); + edit->addAction("&Undo")->setShortcut(tr("Ctrl+Z")); + edit->addAction("&Redo")->setShortcut(tr("Ctrl+Y")); + edit->addSeparator(); + edit->addAction("Cu&t")->setShortcut(tr("Ctrl+X")); + edit->addAction("&Copy")->setShortcut(tr("Ctrl+C")); + edit->addAction("&Paste")->setShortcut(tr("Ctrl+V")); + edit->addAction("&Delete")->setShortcut(tr("Del")); + edit->addSeparator(); + edit->addAction("Pr&operties"); + + mw.menuBar()->addSeparator(); + + QMenu *help = mw.menuBar()->addMenu("&Help"); + help->addAction("&Contents"); + help->addAction("&About"); + + mw.menuBar()->addAction("Action!"); + + mw.show(); // triggers layout + QTest::qWait(100); + + QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(mw.menuBar()); + QCOMPARE(verifyHierarchy(interface), 0); + + QVERIFY(interface); + QCOMPARE(interface->childCount(), 5); + QCOMPARE(interface->role(), QAccessible::MenuBar); + + QAccessibleInterface *iFile = interface->child(0); + QAccessibleInterface *iEdit = interface->child(1); + QAccessibleInterface *iSeparator = interface->child(2); + QAccessibleInterface *iHelp = interface->child(3); + QAccessibleInterface *iAction = interface->child(4); + + QCOMPARE(iFile->role(), QAccessible::MenuItem); + QCOMPARE(iEdit->role(), QAccessible::MenuItem); + QCOMPARE(iSeparator->role(), QAccessible::Separator); + QCOMPARE(iHelp->role(), QAccessible::MenuItem); + QCOMPARE(iAction->role(), QAccessible::MenuItem); +#ifndef Q_WS_MAC +#ifdef Q_OS_WINCE + if (!IsValidCEPlatform()) { + QSKIP("Tests do not work on Mobile platforms due to native menus", SkipAll); + } +#endif + QCOMPARE(mw.mapFromGlobal(interface->rect().topLeft()), mw.menuBar()->geometry().topLeft()); + QCOMPARE(interface->rect().size(), mw.menuBar()->size()); + + QVERIFY(interface->rect().contains(iFile->rect())); + QVERIFY(interface->rect().contains(iEdit->rect())); + // QVERIFY(interface->rect().contains(childSeparator->rect())); //separator might be invisible + QVERIFY(interface->rect().contains(iHelp->rect())); + QVERIFY(interface->rect().contains(iAction->rect())); +#endif + + QCOMPARE(iFile->text(QAccessible::Name, 0), QString("File")); + QCOMPARE(iEdit->text(QAccessible::Name, 0), QString("Edit")); + QCOMPARE(iSeparator->text(QAccessible::Name, 0), QString()); + QCOMPARE(iHelp->text(QAccessible::Name, 0), QString("Help")); + QCOMPARE(iAction->text(QAccessible::Name, 0), QString("Action!")); + +// TODO: Currently not working, task to fix is #100019. +#ifndef Q_OS_MAC + QCOMPARE(iFile->text(QAccessible::Accelerator, 0), tr("Alt+F")); + QCOMPARE(iEdit->text(QAccessible::Accelerator, 0), tr("Alt+E")); + QCOMPARE(iSeparator->text(QAccessible::Accelerator, 0), QString()); + QCOMPARE(iHelp->text(QAccessible::Accelerator, 0), tr("Alt+H")); + QCOMPARE(iAction->text(QAccessible::Accelerator, 0), QString()); +#endif + + QVERIFY(iFile->actionInterface()); + + QCOMPARE(iFile->actionInterface()->actionNames(), QStringList() << QAccessibleActionInterface::ShowMenuAction); + QCOMPARE(iSeparator->actionInterface()->actionNames(), QStringList()); + QCOMPARE(iHelp->actionInterface()->actionNames(), QStringList() << QAccessibleActionInterface::ShowMenuAction); + QCOMPARE(iAction->actionInterface()->actionNames(), QStringList() << QAccessibleActionInterface::PressAction); + + bool menuFade = qApp->isEffectEnabled(Qt::UI_FadeMenu); + int menuFadeDelay = 300; + iFile->actionInterface()->doAction(QAccessibleActionInterface::ShowMenuAction); + if(menuFade) + QTest::qWait(menuFadeDelay); + QVERIFY(file->isVisible() && !edit->isVisible() && !help->isVisible()); + iEdit->actionInterface()->doAction(QAccessibleActionInterface::ShowMenuAction); + if(menuFade) + QTest::qWait(menuFadeDelay); + QVERIFY(!file->isVisible() && edit->isVisible() && !help->isVisible()); + iHelp->actionInterface()->doAction(QAccessibleActionInterface::ShowMenuAction); + if(menuFade) + QTest::qWait(menuFadeDelay); + QVERIFY(!file->isVisible() && !edit->isVisible() && help->isVisible()); + iAction->actionInterface()->doAction(QAccessibleActionInterface::ShowMenuAction); + if(menuFade) + QTest::qWait(menuFadeDelay); + QVERIFY(!file->isVisible() && !edit->isVisible() && !help->isVisible()); + + QVERIFY(!interface->actionInterface()); + delete interface; + interface = QAccessible::queryAccessibleInterface(file); + QCOMPARE(interface->childCount(), 5); + QCOMPARE(interface->role(), QAccessible::PopupMenu); + + QAccessibleInterface *iFileNew = interface->child(0); + QAccessibleInterface *iFileOpen = interface->child(1); + QAccessibleInterface *iFileSave = interface->child(2); + QAccessibleInterface *iFileSeparator = interface->child(3); + QAccessibleInterface *iFileExit = interface->child(4); + + QCOMPARE(iFileNew->role(), QAccessible::MenuItem); + QCOMPARE(iFileOpen->role(), QAccessible::MenuItem); + QCOMPARE(iFileSave->role(), QAccessible::MenuItem); + QCOMPARE(iFileSeparator->role(), QAccessible::Separator); + QCOMPARE(iFileExit->role(), QAccessible::MenuItem); + QCOMPARE(iFileNew->actionInterface()->actionNames(), QStringList() << QAccessibleActionInterface::ShowMenuAction); + QCOMPARE(iFileOpen->actionInterface()->actionNames(), QStringList() << QAccessibleActionInterface::PressAction); + QCOMPARE(iFileSave->actionInterface()->actionNames(), QStringList() << QAccessibleActionInterface::PressAction); + QCOMPARE(iFileSeparator->actionInterface()->actionNames(), QStringList()); + QCOMPARE(iFileExit->actionInterface()->actionNames(), QStringList() << QAccessibleActionInterface::PressAction); + + QAccessibleInterface *iface = 0; + QAccessibleInterface *iface2 = 0; + + // traverse siblings with navigate(Sibling, ...) + int entry = interface->navigate(QAccessible::Child, 1, &iface); + QCOMPARE(entry, 0); + QVERIFY(iface); + QCOMPARE(iface->role(), QAccessible::MenuItem); + + QAccessible::Role fileRoles[5] = { + QAccessible::MenuItem, + QAccessible::MenuItem, + QAccessible::MenuItem, + QAccessible::Separator, + QAccessible::MenuItem + }; + for (int child = 0; child < 5; ++child) { + entry = iface->navigate(QAccessible::Sibling, child + 1, &iface2); + QCOMPARE(entry, 0); + QVERIFY(iface2); + QCOMPARE(iface2->role(), fileRoles[child]); + delete iface2; + } + delete iface; + + // traverse menu items with navigate(Down, ...) + entry = interface->navigate(QAccessible::Child, 1, &iface); + QCOMPARE(entry, 0); + QVERIFY(iface); + QCOMPARE(iface->role(), QAccessible::MenuItem); + + for (int child = 0; child < 4; ++child) { + entry = iface->navigate(QAccessible::Down, 1, &iface2); + delete iface; + iface = iface2; + QCOMPARE(entry, 0); + QVERIFY(iface); + QCOMPARE(iface->role(), fileRoles[child + 1]); + } + delete iface; + + // traverse menu items with navigate(Up, ...) + entry = interface->navigate(QAccessible::Child, interface->childCount(), &iface); + QCOMPARE(entry, 0); + QVERIFY(iface); + QCOMPARE(iface->role(), QAccessible::MenuItem); + + for (int child = 3; child >= 0; --child) { + entry = iface->navigate(QAccessible::Up, 1, &iface2); + delete iface; + iface = iface2; + QCOMPARE(entry, 0); + QVERIFY(iface); + QCOMPARE(iface->role(), fileRoles[child]); + } + delete iface; + + // "New" item + entry = interface->navigate(QAccessible::Child, 1, &iface); + QCOMPARE(entry, 0); + QVERIFY(iface); + QCOMPARE(iface->role(), QAccessible::MenuItem); + + // "New" menu + entry = iface->navigate(QAccessible::Child, 1, &iface2); + delete iface; + iface = iface2; + QCOMPARE(entry, 0); + QVERIFY(iface); + QCOMPARE(iface->role(), QAccessible::PopupMenu); + + // "Text file" menu item + entry = iface->navigate(QAccessible::Child, 1, &iface2); + delete iface; + iface = iface2; + QCOMPARE(entry, 0); + QVERIFY(iface); + QCOMPARE(iface->role(), QAccessible::MenuItem); + + delete iface; + + // move mouse pointer away, since that might influence the + // subsequent tests + QTest::mouseMove(&mw, QPoint(-1, -1)); + QTest::qWait(100); + if (menuFade) + QTest::qWait(menuFadeDelay); + + iFile->actionInterface()->doAction(QAccessibleActionInterface::ShowMenuAction); + iFileNew->actionInterface()->doAction(QAccessibleActionInterface::ShowMenuAction); + + QVERIFY(file->isVisible()); + QVERIFY(fileNew->isVisible()); + QVERIFY(!edit->isVisible()); + QVERIFY(!help->isVisible()); + + QTestAccessibility::clearEvents(); + mw.hide(); + + delete iFile; + delete iFileNew; + delete iFileOpen; + delete iFileSave; + delete iFileSeparator; + delete iFileExit; + + // Do not crash if the menu don't have a parent + QMenu *menu = new QMenu; + menu->addAction(QLatin1String("one")); + menu->addAction(QLatin1String("two")); + menu->addAction(QLatin1String("three")); + iface = QAccessible::queryAccessibleInterface(menu); + iface2 = iface->parent(); + QVERIFY(iface2); + QCOMPARE(iface2->role(), QAccessible::Application); + // caused a *crash* + iface2->state(); + delete iface2; + delete iface; + delete menu; + + } + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::spinBoxTest() +{ + QSpinBox * const spinBox = new QSpinBox(); + spinBox->setValue(3); + spinBox->show(); + + QAccessibleInterface * const interface = QAccessible::queryAccessibleInterface(spinBox); + QVERIFY(interface); + QCOMPARE(interface->role(), QAccessible::SpinBox); + + const QRect widgetRect = spinBox->geometry(); + const QRect accessibleRect = interface->rect(); + QCOMPARE(accessibleRect, widgetRect); + QCOMPARE(interface->text(QAccessible::Value, 0), QLatin1String("3")); + + // one child, the line edit + const int numChildren = interface->childCount(); + QCOMPARE(numChildren, 1); + QAccessibleInterface *lineEdit = interface->child(0); + + QCOMPARE(lineEdit->role(), QAccessible::EditableText); + QCOMPARE(lineEdit->text(QAccessible::Value, 0), QLatin1String("3")); + delete lineEdit; + + QVERIFY(interface->valueInterface()); + QCOMPARE(interface->valueInterface()->currentValue().toInt(), 3); + interface->valueInterface()->setCurrentValue(23); + QCOMPARE(interface->valueInterface()->currentValue().toInt(), 23); + QCOMPARE(spinBox->value(), 23); + + spinBox->setFocus(); + QTestAccessibility::clearEvents(); + QTest::keyPress(spinBox, Qt::Key_Up); + QTest::qWait(200); + EventList events = QTestAccessibility::events(); + QTestAccessibilityEvent expectedEvent(spinBox, 0, (int)QAccessible::ValueChanged); + QVERIFY(events.contains(expectedEvent)); + delete spinBox; + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::doubleSpinBoxTest() +{ + QDoubleSpinBox *doubleSpinBox = new QDoubleSpinBox; + doubleSpinBox->show(); + + QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(doubleSpinBox); + QVERIFY(interface); + + const QRect widgetRect = doubleSpinBox->geometry(); + const QRect accessibleRect = interface->rect(); + QCOMPARE(accessibleRect, widgetRect); + + // Test that we get valid rects for all the spinbox child interfaces. + const int numChildren = interface->childCount(); + for (int i = 1; i <= numChildren; ++i) { + const QRect childRect = interface->rect(i); + QVERIFY(childRect.isValid()); + } + + delete doubleSpinBox; + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::textEditTest() +{ + { + QTextEdit edit; + int startOffset; + int endOffset; + QString text = "hello world\nhow are you today?\n"; + edit.setText(text); + edit.show(); + + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(&edit); + QCOMPARE(iface->text(QAccessible::Value, 0), text); + QCOMPARE(iface->textInterface()->textAtOffset(8, QAccessible2::WordBoundary, &startOffset, &endOffset), QString("world")); + QCOMPARE(startOffset, 6); + QCOMPARE(endOffset, 11); + QCOMPARE(iface->textInterface()->textAtOffset(14, QAccessible2::LineBoundary, &startOffset, &endOffset), QString("how are you today?")); + QCOMPARE(startOffset, 12); + QCOMPARE(endOffset, 30); + QCOMPARE(iface->textInterface()->characterCount(), 31); + QFontMetrics fm(edit.font()); + QCOMPARE(iface->textInterface()->characterRect(0, QAccessible2::RelativeToParent).size(), QSize(fm.width("h"), fm.height())); + QCOMPARE(iface->textInterface()->characterRect(5, QAccessible2::RelativeToParent).size(), QSize(fm.width(" "), fm.height())); + QCOMPARE(iface->textInterface()->characterRect(6, QAccessible2::RelativeToParent).size(), QSize(fm.width("w"), fm.height())); + } + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::textBrowserTest() +{ + { + QTextBrowser textBrowser; + QString text = QLatin1String("Hello world\nhow are you today?\n"); + textBrowser.setText(text); + textBrowser.show(); + + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(&textBrowser); + QVERIFY(iface); + QCOMPARE(iface->role(), QAccessible::StaticText); + QCOMPARE(iface->text(QAccessible::Value, 0), text); + int startOffset; + int endOffset; + QCOMPARE(iface->textInterface()->textAtOffset(8, QAccessible2::WordBoundary, &startOffset, &endOffset), QString("world")); + QCOMPARE(startOffset, 6); + QCOMPARE(endOffset, 11); + QCOMPARE(iface->textInterface()->textAtOffset(14, QAccessible2::LineBoundary, &startOffset, &endOffset), QString("how are you today?")); + QCOMPARE(startOffset, 12); + QCOMPARE(endOffset, 30); + QCOMPARE(iface->textInterface()->characterCount(), 31); + } + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::mdiAreaTest() +{ + { + QMdiArea mdiArea; + mdiArea.resize(400,300); + mdiArea.show(); + const int subWindowCount = 3; + for (int i = 0; i < subWindowCount; ++i) + mdiArea.addSubWindow(new QWidget, Qt::Dialog)->show(); + + QList<QMdiSubWindow *> subWindows = mdiArea.subWindowList(); + QCOMPARE(subWindows.count(), subWindowCount); + + QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(&mdiArea); + QVERIFY(interface); + QCOMPARE(interface->childCount(), subWindowCount); + + // Right, right, right, ... + for (int i = 0; i < subWindowCount; ++i) { + QAccessibleInterface *destination = 0; + int index = interface->navigate(QAccessible::Right, i + 1, &destination); + if (i == subWindowCount - 1) { + QVERIFY(!destination); + QCOMPARE(index, -1); + } else { + QVERIFY(destination); + QCOMPARE(index, 0); + QCOMPARE(destination->object(), (QObject*)subWindows.at(i + 1)); + delete destination; + } + } + + // Left, left, left, ... + for (int i = subWindowCount; i > 0; --i) { + QAccessibleInterface *destination = 0; + int index = interface->navigate(QAccessible::Left, i, &destination); + if (i == 1) { + QVERIFY(!destination); + QCOMPARE(index, -1); + } else { + QVERIFY(destination); + QCOMPARE(index, 0); + QCOMPARE(destination->object(), (QObject*)subWindows.at(i - 2)); + delete destination; + } + } + // ### Add test for Up and Down. + + } + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::mdiSubWindowTest() +{ + { + QMdiArea mdiArea; + mdiArea.show(); + qApp->setActiveWindow(&mdiArea); +#if defined(Q_OS_UNIX) + QCoreApplication::processEvents(); + QTest::qWait(150); +#endif + + bool isSubWindowsPlacedNextToEachOther = false; + const int subWindowCount = 5; + for (int i = 0; i < subWindowCount; ++i) { + QMdiSubWindow *window = mdiArea.addSubWindow(new QPushButton("QAccessibilityTest")); + window->show(); + // Parts of this test requires that the sub windows are placed next + // to each other. In order to achieve that QMdiArea must have + // a width which is larger than subWindow->width() * subWindowCount. + if (i == 0) { + int minimumWidth = window->width() * subWindowCount + 20; + mdiArea.resize(mdiArea.size().expandedTo(QSize(minimumWidth, 0))); +#if defined(Q_OS_UNIX) + QCoreApplication::processEvents(); + QTest::qWait(100); +#endif + if (mdiArea.width() >= minimumWidth) + isSubWindowsPlacedNextToEachOther = true; + } + } + + QList<QMdiSubWindow *> subWindows = mdiArea.subWindowList(); + QCOMPARE(subWindows.count(), subWindowCount); + + QMdiSubWindow *testWindow = subWindows.at(3); + QVERIFY(testWindow); + QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(testWindow); + + // childCount + QVERIFY(interface); + QCOMPARE(interface->childCount(), 1); + + // setText / text + QCOMPARE(interface->text(QAccessible::Name, 0), QString()); + QCOMPARE(interface->text(QAccessible::Name, 1), QString()); + testWindow->setWindowTitle(QLatin1String("ReplaceMe")); + QCOMPARE(interface->text(QAccessible::Name, 0), QLatin1String("ReplaceMe")); + QCOMPARE(interface->text(QAccessible::Name, 1), QLatin1String("ReplaceMe")); + interface->setText(QAccessible::Name, 0, QLatin1String("TitleSetOnWindow")); + QCOMPARE(interface->text(QAccessible::Name, 0), QLatin1String("TitleSetOnWindow")); + interface->setText(QAccessible::Name, 1, QLatin1String("TitleSetOnChild")); + QCOMPARE(interface->text(QAccessible::Name, 0), QLatin1String("TitleSetOnChild")); + + mdiArea.setActiveSubWindow(testWindow); + + // state + QAccessible::State state = QAccessible::Normal | QAccessible::Focusable | QAccessible::Focused + | QAccessible::Movable | QAccessible::Sizeable; + QCOMPARE(interface->state(), state); + const QRect originalGeometry = testWindow->geometry(); + testWindow->showMaximized(); + state &= ~QAccessible::Sizeable; + state &= ~QAccessible::Movable; + QCOMPARE(interface->state(), state); + testWindow->showNormal(); + testWindow->move(-10, 0); + QVERIFY(interface->state() & QAccessible::Offscreen); + testWindow->setVisible(false); + QVERIFY(interface->state() & QAccessible::Invisible); + testWindow->setVisible(true); + testWindow->setEnabled(false); + QVERIFY(interface->state() & QAccessible::Unavailable); + testWindow->setEnabled(true); + qApp->setActiveWindow(&mdiArea); + mdiArea.setActiveSubWindow(testWindow); + testWindow->setFocus(); + QVERIFY(testWindow->isAncestorOf(qApp->focusWidget())); + QVERIFY(interface->state() & QAccessible::Focused); + testWindow->setGeometry(originalGeometry); + + if (isSubWindowsPlacedNextToEachOther) { + // This part of the test can only be run if the sub windows are + // placed next to each other. + QAccessibleInterface *destination = 0; + QCOMPARE(interface->navigate(QAccessible::Child, 1, &destination), 0); + QVERIFY(destination); + QCOMPARE(destination->object(), (QObject*)testWindow->widget()); + delete destination; + QCOMPARE(interface->navigate(QAccessible::Left, 0, &destination), 0); + QVERIFY(destination); + QCOMPARE(destination->object(), (QObject*)subWindows.at(2)); + delete destination; + QCOMPARE(interface->navigate(QAccessible::Right, 0, &destination), 0); + QVERIFY(destination); + QCOMPARE(destination->object(), (QObject*)subWindows.at(4)); + delete destination; + } + + // rect + const QPoint globalPos = testWindow->mapToGlobal(QPoint(0, 0)); + QCOMPARE(interface->rect(), QRect(globalPos, testWindow->size())); + testWindow->hide(); + QCOMPARE(interface->rect(), QRect()); + QCOMPARE(interface->rect(1), QRect()); + testWindow->showMinimized(); + QCOMPARE(interface->rect(1), QRect()); + testWindow->showNormal(); + testWindow->widget()->hide(); + QCOMPARE(interface->rect(1), QRect()); + testWindow->widget()->show(); + const QRect widgetGeometry = testWindow->contentsRect(); + const QPoint globalWidgetPos = QPoint(globalPos.x() + widgetGeometry.x(), + globalPos.y() + widgetGeometry.y()); + QCOMPARE(interface->rect(1), QRect(globalWidgetPos, widgetGeometry.size())); + + // childAt + QCOMPARE(interface->childAt(-10, 0), -1); + QCOMPARE(interface->childAt(globalPos.x(), globalPos.y()), 0); + QCOMPARE(interface->childAt(globalWidgetPos.x(), globalWidgetPos.y()), 1); + testWindow->widget()->hide(); + QCOMPARE(interface->childAt(globalWidgetPos.x(), globalWidgetPos.y()), 0); + + } + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::lineEditTest() +{ + QLineEdit *le = new QLineEdit; + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(le); + QVERIFY(iface); + le->show(); + + QApplication::processEvents(); + QCOMPARE(iface->childCount(), 0); + QVERIFY(iface->state() & QAccessible::Sizeable); + QVERIFY(iface->state() & QAccessible::Movable); + QCOMPARE(bool(iface->state() & QAccessible::Focusable), le->isActiveWindow()); + QVERIFY(iface->state() & QAccessible::Selectable); + QVERIFY(iface->state() & QAccessible::HasPopup); + QCOMPARE(bool(iface->state() & QAccessible::Focused), le->hasFocus()); + + QString secret(QLatin1String("secret")); + le->setText(secret); + le->setEchoMode(QLineEdit::Normal); + QVERIFY(!(iface->state() & QAccessible::Protected)); + QCOMPARE(iface->text(QAccessible::Value, 0), secret); + le->setEchoMode(QLineEdit::NoEcho); + QVERIFY(iface->state() & QAccessible::Protected); + QVERIFY(iface->text(QAccessible::Value, 0).isEmpty()); + le->setEchoMode(QLineEdit::Password); + QVERIFY(iface->state() & QAccessible::Protected); + QVERIFY(iface->text(QAccessible::Value, 0).isEmpty()); + le->setEchoMode(QLineEdit::PasswordEchoOnEdit); + QVERIFY(iface->state() & QAccessible::Protected); + QVERIFY(iface->text(QAccessible::Value, 0).isEmpty()); + le->setEchoMode(QLineEdit::Normal); + QVERIFY(!(iface->state() & QAccessible::Protected)); + QCOMPARE(iface->text(QAccessible::Value, 0), secret); + + QWidget *toplevel = new QWidget; + le->setParent(toplevel); + toplevel->show(); + QApplication::processEvents(); + QVERIFY(!(iface->state() & QAccessible::Sizeable)); + QVERIFY(!(iface->state() & QAccessible::Movable)); + QCOMPARE(bool(iface->state() & QAccessible::Focusable), le->isActiveWindow()); + QVERIFY(iface->state() & QAccessible::Selectable); + QVERIFY(iface->state() & QAccessible::HasPopup); + QCOMPARE(bool(iface->state() & QAccessible::Focused), le->hasFocus()); + + QLineEdit *le2 = new QLineEdit(toplevel); + le2->show(); + QTest::qWait(100); + le2->activateWindow(); + QTest::qWait(100); + le->setFocus(Qt::TabFocusReason); + QTestAccessibility::clearEvents(); + le2->setFocus(Qt::TabFocusReason); + QTRY_VERIFY(QTestAccessibility::events().contains(QTestAccessibilityEvent(le2, 0, QAccessible::Focus))); + + le->setText(QLatin1String("500")); + le->setValidator(new QIntValidator()); + iface->setText(QAccessible::Value, 0, QLatin1String("This text is not a number")); + QCOMPARE(le->text(), QLatin1String("500")); + + delete iface; + delete le; + delete le2; + QTestAccessibility::clearEvents(); + + // IA2 + QString cite = "I always pass on good advice. It is the only thing to do with it. It is never of any use to oneself. --Oscar Wilde"; + QLineEdit *le3 = new QLineEdit(cite, toplevel); + iface = QAccessible::queryAccessibleInterface(le3); + QAccessibleTextInterface* textIface = iface->textInterface(); + le3->deselect(); + le3->setCursorPosition(3); + QCOMPARE(textIface->cursorPosition(), 3); + QTRY_VERIFY(QTestAccessibility::events().contains(QTestAccessibilityEvent(le3, 0, QAccessible::TextCaretMoved))); + QCOMPARE(textIface->selectionCount(), 0); + QTestAccessibility::clearEvents(); + + int start, end; + QCOMPARE(textIface->text(0, 8), QString::fromLatin1("I always")); + QCOMPARE(textIface->textAtOffset(0, QAccessible2::CharBoundary,&start,&end), QString::fromLatin1("I")); + QCOMPARE(start, 0); + QCOMPARE(end, 1); + QCOMPARE(textIface->textBeforeOffset(0, QAccessible2::CharBoundary,&start,&end), QString()); + QCOMPARE(textIface->textAfterOffset(0, QAccessible2::CharBoundary,&start,&end), QString::fromLatin1(" ")); + QCOMPARE(start, 1); + QCOMPARE(end, 2); + + QCOMPARE(textIface->textAtOffset(5, QAccessible2::CharBoundary,&start,&end), QString::fromLatin1("a")); + QCOMPARE(start, 5); + QCOMPARE(end, 6); + QCOMPARE(textIface->textBeforeOffset(5, QAccessible2::CharBoundary,&start,&end), QString::fromLatin1("w")); + QCOMPARE(textIface->textAfterOffset(5, QAccessible2::CharBoundary,&start,&end), QString::fromLatin1("y")); + + QCOMPARE(textIface->textAtOffset(5, QAccessible2::WordBoundary,&start,&end), QString::fromLatin1("always")); + QCOMPARE(start, 2); + QCOMPARE(end, 8); + + QCOMPARE(textIface->textAtOffset(2, QAccessible2::WordBoundary,&start,&end), QString::fromLatin1("always")); + QCOMPARE(textIface->textAtOffset(7, QAccessible2::WordBoundary,&start,&end), QString::fromLatin1("always")); + QCOMPARE(textIface->textAtOffset(8, QAccessible2::WordBoundary,&start,&end), QString::fromLatin1(" ")); + QCOMPARE(textIface->textAtOffset(25, QAccessible2::WordBoundary,&start,&end), QString::fromLatin1("advice")); + QCOMPARE(textIface->textAtOffset(92, QAccessible2::WordBoundary,&start,&end), QString::fromLatin1("oneself")); + + QCOMPARE(textIface->textBeforeOffset(5, QAccessible2::WordBoundary,&start,&end), QString::fromLatin1(" ")); + QCOMPARE(textIface->textAfterOffset(5, QAccessible2::WordBoundary,&start,&end), QString::fromLatin1(" ")); + QCOMPARE(textIface->textAtOffset(5, QAccessible2::SentenceBoundary,&start,&end), QString::fromLatin1("I always pass on good advice. ")); + QCOMPARE(start, 0); + QCOMPARE(end, 30); + + QCOMPARE(textIface->textBeforeOffset(40, QAccessible2::SentenceBoundary,&start,&end), QString::fromLatin1("I always pass on good advice. ")); + QCOMPARE(textIface->textAfterOffset(5, QAccessible2::SentenceBoundary,&start,&end), QString::fromLatin1("It is the only thing to do with it. ")); + + QCOMPARE(textIface->textAtOffset(5, QAccessible2::ParagraphBoundary,&start,&end), cite); + QCOMPARE(start, 0); + QCOMPARE(end, cite.length()); + QCOMPARE(textIface->textAtOffset(5, QAccessible2::LineBoundary,&start,&end), cite); + QCOMPARE(textIface->textAtOffset(5, QAccessible2::NoBoundary,&start,&end), cite); + + delete iface; + delete toplevel; + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::workspaceTest() +{ + { + QWorkspace workspace; + workspace.resize(400,300); + workspace.show(); + const int subWindowCount = 3; + for (int i = 0; i < subWindowCount; ++i) { + QWidget *window = workspace.addWindow(new QWidget); + if (i > 0) + window->move(window->x() + 1, window->y()); + window->show(); + window->resize(70, window->height()); + } + + QWidgetList subWindows = workspace.windowList(); + QCOMPARE(subWindows.count(), subWindowCount); + + QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(&workspace); + QVERIFY(interface); + QCOMPARE(interface->childCount(), subWindowCount); + + // Right, right, right, ... + for (int i = 0; i < subWindowCount; ++i) { + QAccessibleInterface *destination = 0; + int index = interface->navigate(QAccessible::Right, i + 1, &destination); + if (i == subWindowCount - 1) { + QVERIFY(!destination); + QCOMPARE(index, -1); + } else { + QVERIFY(destination); + QCOMPARE(index, 0); + QCOMPARE(destination->object(), (QObject*)subWindows.at(i + 1)); + delete destination; + } + } + + // Left, left, left, ... + for (int i = subWindowCount; i > 0; --i) { + QAccessibleInterface *destination = 0; + int index = interface->navigate(QAccessible::Left, i, &destination); + if (i == 1) { + QVERIFY(!destination); + QCOMPARE(index, -1); + } else { + QVERIFY(destination); + QCOMPARE(index, 0); + QCOMPARE(destination->object(), (QObject*)subWindows.at(i - 2)); + delete destination; + } + } + // ### Add test for Up and Down. + + } + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::dialogButtonBoxTest() +{ + { + QDialogButtonBox box(QDialogButtonBox::Reset | + QDialogButtonBox::Help | + QDialogButtonBox::Ok, Qt::Horizontal); + + + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(&box); + QVERIFY(iface); + box.show(); +#if defined(Q_OS_UNIX) + QCoreApplication::processEvents(); + QTest::qWait(100); +#endif + + QApplication::processEvents(); + QCOMPARE(iface->childCount(), 3); + QCOMPARE(iface->role(), QAccessible::Grouping); + QCOMPARE(iface->role(1), QAccessible::PushButton); + QCOMPARE(iface->role(2), QAccessible::PushButton); + QCOMPARE(iface->role(3), QAccessible::PushButton); + QStringList actualOrder; + QAccessibleInterface *child; + QAccessibleInterface *leftmost; + child = iface->child(0); + // first find the leftmost button + while (child->navigate(QAccessible::Left, 1, &leftmost) != -1) { + delete child; + child = leftmost; + } + leftmost = child; + + // then traverse from left to right to find the correct order of the buttons + int right = 0; + while (right != -1) { + actualOrder << leftmost->text(QAccessible::Name, 0); + right = leftmost->navigate(QAccessible::Right, 1, &child); + delete leftmost; + leftmost = child; + } + + QStringList expectedOrder; + QDialogButtonBox::ButtonLayout btnlout = + QDialogButtonBox::ButtonLayout(QApplication::style()->styleHint(QStyle::SH_DialogButtonLayout)); + switch (btnlout) { + case QDialogButtonBox::WinLayout: + expectedOrder << QDialogButtonBox::tr("Reset") + << QDialogButtonBox::tr("OK") + << QDialogButtonBox::tr("Help"); + break; + case QDialogButtonBox::GnomeLayout: + case QDialogButtonBox::KdeLayout: + case QDialogButtonBox::MacLayout: + expectedOrder << QDialogButtonBox::tr("Help") + << QDialogButtonBox::tr("Reset") + << QDialogButtonBox::tr("OK"); + break; + } + QCOMPARE(actualOrder, expectedOrder); + delete iface; + QApplication::processEvents(); + QTestAccessibility::clearEvents(); + } + + { + QDialogButtonBox box(QDialogButtonBox::Reset | + QDialogButtonBox::Help | + QDialogButtonBox::Ok, Qt::Horizontal); + + + // Test up and down navigation + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(&box); + QVERIFY(iface); + box.setOrientation(Qt::Vertical); + box.show(); +#if defined(Q_OS_UNIX) + QCoreApplication::processEvents(); + QTest::qWait(100); +#endif + + QApplication::processEvents(); + QAccessibleInterface *child; + QStringList actualOrder; + child = iface->child(0); + // first find the topmost button + QAccessibleInterface *other; + while (child->navigate(QAccessible::Up, 1, &other) != -1) { + delete child; + child = other; + } + other = child; + + // then traverse from top to bottom to find the correct order of the buttons + actualOrder.clear(); + int right = 0; + while (right != -1) { + actualOrder << other->text(QAccessible::Name, 0); + right = other->navigate(QAccessible::Down, 1, &child); + delete other; + other = child; + } + + QStringList expectedOrder; + expectedOrder << QDialogButtonBox::tr("OK") + << QDialogButtonBox::tr("Reset") + << QDialogButtonBox::tr("Help"); + + QCOMPARE(actualOrder, expectedOrder); + delete iface; + QApplication::processEvents(); + + } + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::dialTest() +{ + { + QDial dial; + dial.setMinimum(23); + dial.setMaximum(121); + dial.setValue(42); + QCOMPARE(dial.value(), 42); + dial.show(); + + QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(&dial); + QVERIFY(interface); + QCOMPARE(interface->childCount(), 0); + + QCOMPARE(interface->text(QAccessible::Value, 0), QString::number(dial.value())); + QCOMPARE(interface->rect(), dial.geometry()); + + QAccessibleValueInterface *valueIface = interface->valueInterface(); + QVERIFY(valueIface != 0); + QCOMPARE(valueIface->minimumValue().toInt(), dial.minimum()); + QCOMPARE(valueIface->maximumValue().toInt(), dial.maximum()); + QCOMPARE(valueIface->currentValue().toInt(), 42); + dial.setValue(50); + QCOMPARE(valueIface->currentValue().toInt(), dial.value()); + dial.setValue(0); + QCOMPARE(valueIface->currentValue().toInt(), dial.value()); + dial.setValue(100); + QCOMPARE(valueIface->currentValue().toInt(), dial.value()); + valueIface->setCurrentValue(77); + QCOMPARE(77, dial.value()); + } + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::rubberBandTest() +{ + QRubberBand rubberBand(QRubberBand::Rectangle); + QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(&rubberBand); + QVERIFY(interface); + QCOMPARE(interface->role(), QAccessible::Border); + delete interface; + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::abstractScrollAreaTest() +{ + { + QAbstractScrollArea abstractScrollArea; + + QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(&abstractScrollArea); + QVERIFY(interface); + QVERIFY(!interface->rect().isValid()); + QCOMPARE(interface->childAt(200, 200), -1); + + abstractScrollArea.resize(400, 400); + abstractScrollArea.show(); +#if defined(Q_OS_UNIX) + QCoreApplication::processEvents(); + QTest::qWait(100); +#endif + const QRect globalGeometry = QRect(abstractScrollArea.mapToGlobal(QPoint(0, 0)), + abstractScrollArea.size()); + + // Viewport. + QCOMPARE(interface->childCount(), 1); + QWidget *viewport = abstractScrollArea.viewport(); + QVERIFY(viewport); + QVERIFY(verifyChild(viewport, interface, 1, globalGeometry)); + + // Horizontal scrollBar. + abstractScrollArea.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + QCOMPARE(interface->childCount(), 2); + QWidget *horizontalScrollBar = abstractScrollArea.horizontalScrollBar(); + QWidget *horizontalScrollBarContainer = horizontalScrollBar->parentWidget(); + QVERIFY(verifyChild(horizontalScrollBarContainer, interface, 2, globalGeometry)); + + // Horizontal scrollBar widgets. + QLabel *secondLeftLabel = new QLabel(QLatin1String("L2")); + abstractScrollArea.addScrollBarWidget(secondLeftLabel, Qt::AlignLeft); + QCOMPARE(interface->childCount(), 2); + + QLabel *firstLeftLabel = new QLabel(QLatin1String("L1")); + abstractScrollArea.addScrollBarWidget(firstLeftLabel, Qt::AlignLeft); + QCOMPARE(interface->childCount(), 2); + + QLabel *secondRightLabel = new QLabel(QLatin1String("R2")); + abstractScrollArea.addScrollBarWidget(secondRightLabel, Qt::AlignRight); + QCOMPARE(interface->childCount(), 2); + + QLabel *firstRightLabel = new QLabel(QLatin1String("R1")); + abstractScrollArea.addScrollBarWidget(firstRightLabel, Qt::AlignRight); + QCOMPARE(interface->childCount(), 2); + + // Vertical scrollBar. + abstractScrollArea.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + QCOMPARE(interface->childCount(), 3); + QWidget *verticalScrollBar = abstractScrollArea.verticalScrollBar(); + QWidget *verticalScrollBarContainer = verticalScrollBar->parentWidget(); + QVERIFY(verifyChild(verticalScrollBarContainer, interface, 3, globalGeometry)); + + // Vertical scrollBar widgets. + QLabel *secondTopLabel = new QLabel(QLatin1String("T2")); + abstractScrollArea.addScrollBarWidget(secondTopLabel, Qt::AlignTop); + QCOMPARE(interface->childCount(), 3); + + QLabel *firstTopLabel = new QLabel(QLatin1String("T1")); + abstractScrollArea.addScrollBarWidget(firstTopLabel, Qt::AlignTop); + QCOMPARE(interface->childCount(), 3); + + QLabel *secondBottomLabel = new QLabel(QLatin1String("B2")); + abstractScrollArea.addScrollBarWidget(secondBottomLabel, Qt::AlignBottom); + QCOMPARE(interface->childCount(), 3); + + QLabel *firstBottomLabel = new QLabel(QLatin1String("B1")); + abstractScrollArea.addScrollBarWidget(firstBottomLabel, Qt::AlignBottom); + QCOMPARE(interface->childCount(), 3); + + // CornerWidget. + abstractScrollArea.setCornerWidget(new QLabel(QLatin1String("C"))); + QCOMPARE(interface->childCount(), 4); + QWidget *cornerWidget = abstractScrollArea.cornerWidget(); + QVERIFY(verifyChild(cornerWidget, interface, 4, globalGeometry)); + + // Test navigate. + QAccessibleInterface *target = 0; + + // viewport -> Up -> NOTHING + const int viewportIndex = indexOfChild(interface, viewport); + QVERIFY(viewportIndex != -1); + QCOMPARE(interface->navigate(QAccessible::Up, viewportIndex, &target), -1); + QVERIFY(!target); + + // viewport -> Left -> NOTHING + QCOMPARE(interface->navigate(QAccessible::Left, viewportIndex, &target), -1); + QVERIFY(!target); + + // viewport -> Down -> horizontalScrollBarContainer + const int horizontalScrollBarContainerIndex = indexOfChild(interface, horizontalScrollBarContainer); + QVERIFY(horizontalScrollBarContainerIndex != -1); + QCOMPARE(interface->navigate(QAccessible::Down, viewportIndex, &target), 0); + QVERIFY(target); + QCOMPARE(target->object(), static_cast<QObject *>(horizontalScrollBarContainer)); + delete target; + target = 0; + + // horizontalScrollBarContainer -> Left -> NOTHING + QCOMPARE(interface->navigate(QAccessible::Left, horizontalScrollBarContainerIndex, &target), -1); + QVERIFY(!target); + + // horizontalScrollBarContainer -> Down -> NOTHING + QVERIFY(horizontalScrollBarContainerIndex != -1); + QCOMPARE(interface->navigate(QAccessible::Down, horizontalScrollBarContainerIndex, &target), -1); + QVERIFY(!target); + + // horizontalScrollBarContainer -> Right -> cornerWidget + const int cornerWidgetIndex = indexOfChild(interface, cornerWidget); + QVERIFY(cornerWidgetIndex != -1); + QCOMPARE(interface->navigate(QAccessible::Right, horizontalScrollBarContainerIndex, &target), 0); + QVERIFY(target); + QCOMPARE(target->object(), static_cast<QObject *>(cornerWidget)); + delete target; + target = 0; + + // cornerWidget -> Down -> NOTHING + QCOMPARE(interface->navigate(QAccessible::Down, cornerWidgetIndex, &target), -1); + QVERIFY(!target); + + // cornerWidget -> Right -> NOTHING + QVERIFY(cornerWidgetIndex != -1); + QCOMPARE(interface->navigate(QAccessible::Right, cornerWidgetIndex, &target), -1); + QVERIFY(!target); + + // cornerWidget -> Up -> verticalScrollBarContainer + const int verticalScrollBarContainerIndex = indexOfChild(interface, verticalScrollBarContainer); + QVERIFY(verticalScrollBarContainerIndex != -1); + QCOMPARE(interface->navigate(QAccessible::Up, cornerWidgetIndex, &target), 0); + QVERIFY(target); + QCOMPARE(target->object(), static_cast<QObject *>(verticalScrollBarContainer)); + delete target; + target = 0; + + // verticalScrollBarContainer -> Right -> NOTHING + QCOMPARE(interface->navigate(QAccessible::Right, verticalScrollBarContainerIndex, &target), -1); + QVERIFY(!target); + + // verticalScrollBarContainer -> Up -> NOTHING + QCOMPARE(interface->navigate(QAccessible::Up, verticalScrollBarContainerIndex, &target), -1); + QVERIFY(!target); + + // verticalScrollBarContainer -> Left -> viewport + QCOMPARE(interface->navigate(QAccessible::Left, verticalScrollBarContainerIndex, &target), 0); + QVERIFY(target); + QCOMPARE(target->object(), static_cast<QObject *>(viewport)); + delete target; + target = 0; + + QCOMPARE(verifyHierarchy(interface), 0); + + delete interface; + } + + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::scrollAreaTest() +{ + { + QScrollArea scrollArea; + scrollArea.show(); +#if defined(Q_OS_UNIX) + QCoreApplication::processEvents(); + QTest::qWait(100); +#endif + QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(&scrollArea); + QVERIFY(interface); + QCOMPARE(interface->childCount(), 1); // The viewport. + delete interface; + } + QTestAccessibility::clearEvents(); +} + +// Accessible table1 interface is no longer supported on X11, +// where it has been replaced by table2 interface. +#ifndef Q_OS_UNIX + +void tst_QAccessibility::listViewTest() +{ + { + QListView listView; + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(&listView); + QVERIFY(iface); + QCOMPARE(iface->childCount(), 1); + delete iface; + } + { + QListWidget listView; + listView.addItem(tr("A")); + listView.addItem(tr("B")); + listView.addItem(tr("C")); + listView.resize(400,400); + listView.show(); + QTest::qWait(1); // Need this for indexOfchild to work. + + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(&listView); + QCOMPARE((int)iface->role(), (int)QAccessible::Client); + QCOMPARE((int)iface->role(1), (int)QAccessible::List); + QCOMPARE(iface->childCount(), 1); + QAccessibleInterface *child; + iface->navigate(QAccessible::Child, 1, &child); + delete iface; + iface = child; + QCOMPARE(iface->text(QAccessible::Name, 1), QString("A")); + QCOMPARE(iface->text(QAccessible::Name, 2), QString("B")); + QCOMPARE(iface->text(QAccessible::Name, 3), QString("C")); + + QCOMPARE(iface->childCount(), 3); + + QAccessibleInterface *childA = 0; + QCOMPARE(iface->navigate(QAccessible::Child, 1, &childA), 0); + QVERIFY(childA); + QCOMPARE(iface->indexOfChild(childA), 1); + QCOMPARE(childA->text(QAccessible::Name, 1), QString("A")); + delete childA; + + QAccessibleInterface *childB = 0; + QCOMPARE(iface->navigate(QAccessible::Child, 2, &childB), 0); + QVERIFY(childB); + QCOMPARE(iface->indexOfChild(childB), 2); + QCOMPARE(childB->text(QAccessible::Name, 1), QString("B")); + delete childB; + + QAccessibleInterface *childC = 0; + QCOMPARE(iface->navigate(QAccessible::Child, 3, &childC), 0); + QVERIFY(childC); + QCOMPARE(iface->indexOfChild(childC), 3); + QCOMPARE(childC->text(QAccessible::Name, 1), QString("C")); + delete childC; + QTestAccessibility::clearEvents(); + + // Check for events + QTest::mouseClick(listView.viewport(), Qt::LeftButton, 0, listView.visualItemRect(listView.item(1)).center()); + QTest::mouseClick(listView.viewport(), Qt::LeftButton, 0, listView.visualItemRect(listView.item(2)).center()); + QVERIFY(QTestAccessibility::events().contains(QTestAccessibilityEvent(listView.viewport(), 2, QAccessible::Selection))); + QVERIFY(QTestAccessibility::events().contains(QTestAccessibilityEvent(listView.viewport(), 3, QAccessible::Selection))); + delete iface; + } + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::treeWidgetTest() +{ + QWidget *w = new QWidget; + QTreeWidget *tree = new QTreeWidget(w); + QHBoxLayout *l = new QHBoxLayout(w); + l->addWidget(tree); + for (int i = 0; i < 10; ++i) { + QStringList strings = QStringList() << QString::fromAscii("row: %1").arg(i) + << QString("column 1") << QString("column 2"); + + tree->addTopLevelItem(new QTreeWidgetItem(strings)); + } + w->show(); + + QAccessibleInterface *acc = QAccessible::queryAccessibleInterface(tree); + QAccessibleInterface *accViewport = 0; + int entry = acc->navigate(QAccessible::Child, 1, &accViewport); + QVERIFY(accViewport); + QCOMPARE(entry, 0); + QAccessibleInterface *accTreeItem = 0; + entry = accViewport->navigate(QAccessible::Child, 1, &accTreeItem); + QCOMPARE(entry, 0); + + QAccessibleInterface *accTreeItem2 = 0; + entry = accTreeItem->navigate(QAccessible::Sibling, 3, &accTreeItem2); + QCOMPARE(entry, 0); + QCOMPARE(accTreeItem2->text(QAccessible::Name, 0), QLatin1String("row: 1")); + + // test selected/focused state + QItemSelectionModel *selModel = tree->selectionModel(); + QVERIFY(selModel); + selModel->select(QItemSelection(tree->model()->index(0, 0), tree->model()->index(3, 0)), QItemSelectionModel::Select); + selModel->setCurrentIndex(tree->model()->index(1, 0), QItemSelectionModel::Current); + + for (int i = 1; i < 10 ; ++i) { + QAccessible::State expected; + if (i <= 5 && i >= 2) + expected = QAccessible::Selected; + if (i == 3) + expected |= QAccessible::Focused; + + QCOMPARE(accViewport->state(i) & (QAccessible::Focused | QAccessible::Selected), expected); + } + + // Test sanity of its navigation functions + QCOMPARE(verifyHierarchy(acc), 0); + + delete accTreeItem2; + delete accTreeItem; + delete accViewport; + delete acc; + delete w; + + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::tableWidgetTest() +{ + { + QWidget *topLevel = new QWidget; + QTableWidget *w = new QTableWidget(8,4,topLevel); + for (int r = 0; r < 8; ++r) { + for (int c = 0; c < 4; ++c) { + w->setItem(r, c, new QTableWidgetItem(tr("%1,%2").arg(c).arg(r))); + } + } + w->resize(100, 100); + topLevel->show(); + + QAccessibleInterface *client = QAccessible::queryAccessibleInterface(w); + QCOMPARE(client->role(), QAccessible::Client); + QCOMPARE(client->childCount(), 3); + QAccessibleInterface *view = 0; + view = client->child(0); + QCOMPARE(view->role(), QAccessible::Table); + QAccessibleInterface *ifRow; + ifRow = view->child(1); + QCOMPARE(ifRow->role(), QAccessible::Row); + QAccessibleInterface *item; + int entry = ifRow->navigate(QAccessible::Child, 1, &item); + QCOMPARE(entry, 1); + QCOMPARE(item , (QAccessibleInterface*)0); + QCOMPARE(ifRow->text(QAccessible::Name, 2), QLatin1String("0,0")); + QCOMPARE(ifRow->text(QAccessible::Name, 3), QLatin1String("1,0")); + + QCOMPARE(verifyHierarchy(client), 0); + + delete ifRow; + delete view; + delete client; + delete w; + delete topLevel; + } + QTestAccessibility::clearEvents(); +} + +class QtTestTableModel: public QAbstractTableModel +{ + Q_OBJECT + +signals: + void invalidIndexEncountered() const; + +public: + QtTestTableModel(int rows = 0, int columns = 0, QObject *parent = 0) + : QAbstractTableModel(parent), + row_count(rows), + column_count(columns) {} + + int rowCount(const QModelIndex& = QModelIndex()) const { return row_count; } + int columnCount(const QModelIndex& = QModelIndex()) const { return column_count; } + + QVariant data(const QModelIndex &idx, int role) const + { + if (!idx.isValid() || idx.row() >= row_count || idx.column() >= column_count) { + qWarning() << "Invalid modelIndex [%d,%d,%p]" << idx; + emit invalidIndexEncountered(); + return QVariant(); + } + + if (role == Qt::DisplayRole || role == Qt::EditRole) + return QString("[%1,%2,%3]").arg(idx.row()).arg(idx.column()).arg(0); + + return QVariant(); + } + + void removeLastRow() + { + beginRemoveRows(QModelIndex(), row_count - 1, row_count - 1); + --row_count; + endRemoveRows(); + } + + void removeAllRows() + { + beginRemoveRows(QModelIndex(), 0, row_count - 1); + row_count = 0; + endRemoveRows(); + } + + void removeLastColumn() + { + beginRemoveColumns(QModelIndex(), column_count - 1, column_count - 1); + --column_count; + endRemoveColumns(); + } + + void removeAllColumns() + { + beginRemoveColumns(QModelIndex(), 0, column_count - 1); + column_count = 0; + endRemoveColumns(); + } + + void reset() + { + QAbstractTableModel::reset(); + } + + int row_count; + int column_count; +}; + +class QtTestDelegate : public QItemDelegate +{ +public: + QtTestDelegate(QWidget *parent = 0) : QItemDelegate(parent) {} + + virtual QSize sizeHint(const QStyleOptionViewItem &/*option*/, const QModelIndex &/*index*/) const + { + return QSize(100,50); + } +}; + +void tst_QAccessibility::tableViewTest() +{ + { + QtTestTableModel *model = new QtTestTableModel(3, 4); + QTableView *w = new QTableView(); + w->setModel(model); + w->setItemDelegate(new QtTestDelegate(w)); + w->resize(450,200); + w->resizeColumnsToContents(); + w->resizeRowsToContents(); + w->show(); + + QAccessibleInterface *client = QAccessible::queryAccessibleInterface(w); + QAccessibleInterface *table2; + client->navigate(QAccessible::Child, 1, &table2); + QVERIFY(table2); + QCOMPARE(table2->role(1), QAccessible::Row); + QAccessibleInterface *toprow = 0; + table2->navigate(QAccessible::Child, 1, &toprow); + QVERIFY(toprow); + QCOMPARE(toprow->role(1), QAccessible::RowHeader); + QCOMPARE(toprow->role(2), QAccessible::ColumnHeader); + delete toprow; + + // call childAt() for each child until we reach the bottom, + // and do it for each row in the table + for (int y = 1; y < 5; ++y) { // this includes the special header + for (int x = 1; x < 6; ++x) { + QCOMPARE(client->role(), QAccessible::Client); + QRect globalRect = client->rect(); + QVERIFY(globalRect.isValid()); + // make sure we don't hit the vertical header ##### + QPoint p = globalRect.topLeft() + QPoint(8, 8); + p.ry() += 50 * (y - 1); + p.rx() += 100 * (x - 1); + int index = client->childAt(p.x(), p.y()); + QCOMPARE(index, 1); + QCOMPARE(client->role(index), QAccessible::Table); + + // navigate to table/viewport + QAccessibleInterface *table; + client->navigate(QAccessible::Child, index, &table); + QVERIFY(table); + index = table->childAt(p.x(), p.y()); + QCOMPARE(index, y); + QCOMPARE(table->role(index), QAccessible::Row); + QAccessibleInterface *row; + QCOMPARE(table->role(1), QAccessible::Row); + + // navigate to the row + table->navigate(QAccessible::Child, index, &row); + QVERIFY(row); + QCOMPARE(row->role(1), QAccessible::RowHeader); + index = row->childAt(p.x(), p.y()); + QVERIFY(index > 0); + if (x == 1 && y == 1) { + QCOMPARE(row->role(index), QAccessible::RowHeader); + QCOMPARE(row->text(QAccessible::Name, index), QLatin1String("")); + } else if (x > 1 && y > 1) { + QCOMPARE(row->role(index), QAccessible::Cell); + QCOMPARE(row->text(QAccessible::Name, index), QString::fromAscii("[%1,%2,0]").arg(y - 2).arg(x - 2)); + } else if (x == 1) { + QCOMPARE(row->role(index), QAccessible::RowHeader); + QCOMPARE(row->text(QAccessible::Name, index), QString::fromAscii("%1").arg(y - 1)); + } else if (y == 1) { + QCOMPARE(row->role(index), QAccessible::ColumnHeader); + QCOMPARE(row->text(QAccessible::Name, index), QString::fromAscii("%1").arg(x - 1)); + } + delete table; + delete row; + } + } + delete table2; + delete client; + delete w; + delete model; + } + QTestAccessibility::clearEvents(); +} + +#else +// Test accessible table2 interface on unix + +void tst_QAccessibility::table2ListTest() +{ + QListWidget *listView = new QListWidget; + listView->addItem("Oslo"); + listView->addItem("Berlin"); + listView->addItem("Brisbane"); + listView->resize(400,400); + listView->show(); + QTest::qWait(1); // Need this for indexOfchild to work. + QCoreApplication::processEvents(); + QTest::qWait(100); + + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(listView); + QCOMPARE(verifyHierarchy(iface), 0); + + QCOMPARE((int)iface->role(), (int)QAccessible::List); + QCOMPARE(iface->childCount(), 3); + + QAccessibleInterface *child1 = 0; + child1 = iface->child(0); + QVERIFY(child1); + QCOMPARE(iface->indexOfChild(child1), 1); + QCOMPARE(child1->text(QAccessible::Name, 0), QString("Oslo")); + QCOMPARE(child1->role(), QAccessible::ListItem); + delete child1; + + QAccessibleInterface *child2 = 0; + child2 = iface->child(1); + QVERIFY(child2); + QCOMPARE(iface->indexOfChild(child2), 2); + QCOMPARE(child2->text(QAccessible::Name, 0), QString("Berlin")); + delete child2; + + QAccessibleInterface *child3 = 0; + child3 = iface->child(2); + QVERIFY(child3); + QCOMPARE(iface->indexOfChild(child3), 3); + QCOMPARE(child3->text(QAccessible::Name, 0), QString("Brisbane")); + delete child3; + QTestAccessibility::clearEvents(); + + // Check for events + QTest::mouseClick(listView->viewport(), Qt::LeftButton, 0, listView->visualItemRect(listView->item(1)).center()); + QVERIFY(QTestAccessibility::events().contains(QTestAccessibilityEvent(listView, 2, QAccessible::Selection))); + QVERIFY(QTestAccessibility::events().contains(QTestAccessibilityEvent(listView, 2, QAccessible::Focus))); + QTest::mouseClick(listView->viewport(), Qt::LeftButton, 0, listView->visualItemRect(listView->item(2)).center()); + QVERIFY(QTestAccessibility::events().contains(QTestAccessibilityEvent(listView, 3, QAccessible::Selection))); + QVERIFY(QTestAccessibility::events().contains(QTestAccessibilityEvent(listView, 3, QAccessible::Focus))); + + listView->addItem("Munich"); + QCOMPARE(iface->childCount(), 4); + + // table 2 + QAccessibleTable2Interface *table2 = iface->table2Interface(); + QVERIFY(table2); + QCOMPARE(table2->columnCount(), 1); + QCOMPARE(table2->rowCount(), 4); + QAccessibleTable2CellInterface *cell1; + QVERIFY(cell1 = table2->cellAt(0,0)); + QCOMPARE(cell1->text(QAccessible::Name, 0), QString("Oslo")); + QAccessibleTable2CellInterface *cell4; + QVERIFY(cell4 = table2->cellAt(3,0)); + QCOMPARE(cell4->text(QAccessible::Name, 0), QString("Munich")); + QCOMPARE(cell4->role(), QAccessible::ListItem); + QCOMPARE(cell4->rowIndex(), 3); + QCOMPARE(cell4->columnIndex(), 0); + QVERIFY(!cell4->isExpandable()); + + delete cell4; + delete cell1; + delete iface; + delete listView; + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::table2TreeTest() +{ + QTreeWidget *treeView = new QTreeWidget; + treeView->setColumnCount(2); + QTreeWidgetItem *header = new QTreeWidgetItem; + header->setText(0, "Artist"); + header->setText(1, "Work"); + treeView->setHeaderItem(header); + + QTreeWidgetItem *root1 = new QTreeWidgetItem; + root1->setText(0, "Spain"); + treeView->addTopLevelItem(root1); + + QTreeWidgetItem *item1 = new QTreeWidgetItem; + item1->setText(0, "Picasso"); + item1->setText(1, "Guernica"); + root1->addChild(item1); + + QTreeWidgetItem *item2 = new QTreeWidgetItem; + item2->setText(0, "Tapies"); + item2->setText(1, "Ambrosia"); + root1->addChild(item2); + + QTreeWidgetItem *root2 = new QTreeWidgetItem; + root2->setText(0, "Austria"); + treeView->addTopLevelItem(root2); + + QTreeWidgetItem *item3 = new QTreeWidgetItem; + item3->setText(0, "Klimt"); + item3->setText(1, "The Kiss"); + root2->addChild(item3); + + treeView->resize(400,400); + treeView->show(); + + QCoreApplication::processEvents(); + QTest::qWait(100); + + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(treeView); + QCOMPARE(verifyHierarchy(iface), 0); + + QCOMPARE((int)iface->role(), (int)QAccessible::Tree); + // header and 2 rows (the others are not expanded, thus not visible) + QCOMPARE(iface->childCount(), 6); + + QAccessibleInterface *header1 = 0; + header1 = iface->child(0); + QVERIFY(header1); + QCOMPARE(iface->indexOfChild(header1), 1); + QCOMPARE(header1->text(QAccessible::Name, 0), QString("Artist")); + QCOMPARE(header1->role(), QAccessible::ColumnHeader); + delete header1; + + QAccessibleInterface *child1 = 0; + child1 = iface->child(2); + QVERIFY(child1); + QCOMPARE(iface->indexOfChild(child1), 3); + QCOMPARE(child1->text(QAccessible::Name, 0), QString("Spain")); + QCOMPARE(child1->role(), QAccessible::TreeItem); + QVERIFY(!(child1->state() & QAccessible::Expanded)); + delete child1; + + QAccessibleInterface *child2 = 0; + child2 = iface->child(4); + QVERIFY(child2); + QCOMPARE(iface->indexOfChild(child2), 5); + QCOMPARE(child2->text(QAccessible::Name, 0), QString("Austria")); + delete child2; + + QTestAccessibility::clearEvents(); + + // table 2 + QAccessibleTable2Interface *table2 = iface->table2Interface(); + QVERIFY(table2); + QCOMPARE(table2->columnCount(), 2); + QCOMPARE(table2->rowCount(), 2); + QAccessibleTable2CellInterface *cell1; + QVERIFY(cell1 = table2->cellAt(0,0)); + QCOMPARE(cell1->text(QAccessible::Name, 0), QString("Spain")); + QAccessibleTable2CellInterface *cell2; + QVERIFY(cell2 = table2->cellAt(1,0)); + QCOMPARE(cell2->text(QAccessible::Name, 0), QString("Austria")); + QCOMPARE(cell2->role(), QAccessible::TreeItem); + QCOMPARE(cell2->rowIndex(), 1); + QCOMPARE(cell2->columnIndex(), 0); + QVERIFY(cell2->isExpandable()); + QCOMPARE(iface->indexOfChild(cell2), 5); + QVERIFY(!(cell2->state() & QAccessible::Expanded)); + QCOMPARE(table2->columnDescription(1), QString("Work")); + delete cell2; + delete cell1; + + treeView->expandAll(); + + // Need this for indexOfchild to work. + QCoreApplication::processEvents(); + QTest::qWait(100); + + QCOMPARE(table2->columnCount(), 2); + QCOMPARE(table2->rowCount(), 5); + cell1 = table2->cellAt(1,0); + QCOMPARE(cell1->text(QAccessible::Name, 0), QString("Picasso")); + QCOMPARE(iface->indexOfChild(cell1), 5); // 1 based + 2 header + 2 for root item + + cell2 = table2->cellAt(4,0); + QCOMPARE(cell2->text(QAccessible::Name, 0), QString("Klimt")); + QCOMPARE(cell2->role(), QAccessible::TreeItem); + QCOMPARE(cell2->rowIndex(), 4); + QCOMPARE(cell2->columnIndex(), 0); + QVERIFY(!cell2->isExpandable()); + QCOMPARE(iface->indexOfChild(cell2), 11); + + QCOMPARE(table2->columnDescription(0), QString("Artist")); + QCOMPARE(table2->columnDescription(1), QString("Work")); + + delete iface; + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::table2TableTest() +{ + QTableWidget *tableView = new QTableWidget(3, 3); + tableView->setColumnCount(3); + QStringList hHeader; + hHeader << "h1" << "h2" << "h3"; + tableView->setHorizontalHeaderLabels(hHeader); + + QStringList vHeader; + vHeader << "v1" << "v2" << "v3"; + tableView->setVerticalHeaderLabels(vHeader); + + for (int i = 0; i<9; ++i) { + QTableWidgetItem *item = new QTableWidgetItem; + item->setText(QString::number(i/3) + QString(".") + QString::number(i%3)); + tableView->setItem(i/3, i%3, item); + } + + tableView->resize(600,600); + tableView->show(); + QTest::qWait(1); // Need this for indexOfchild to work. + QCoreApplication::processEvents(); + QTest::qWait(100); + + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(tableView); + QCOMPARE(verifyHierarchy(iface), 0); + + QCOMPARE((int)iface->role(), (int)QAccessible::Table); + // header and 2 rows (the others are not expanded, thus not visible) + QCOMPARE(iface->childCount(), 9+3+3+1); // cell+headers+topleft button + + QAccessibleInterface *cornerButton = iface->child(0); + QVERIFY(cornerButton); + QCOMPARE(iface->indexOfChild(cornerButton), 1); + QCOMPARE(cornerButton->role(), QAccessible::Pane); + delete cornerButton; + + QAccessibleInterface *child1 = iface->child(2); + QVERIFY(child1); + QCOMPARE(iface->indexOfChild(child1), 3); + QCOMPARE(child1->text(QAccessible::Name, 0), QString("h2")); + QCOMPARE(child1->role(), QAccessible::ColumnHeader); + QVERIFY(!(child1->state() & QAccessible::Expanded)); + delete child1; + + QAccessibleInterface *child2 = iface->child(10); + QVERIFY(child2); + QCOMPARE(iface->indexOfChild(child2), 11); + QCOMPARE(child2->text(QAccessible::Name, 0), QString("1.1")); + QAccessibleTable2CellInterface *cell2Iface = static_cast<QAccessibleTable2CellInterface*>(child2); + QCOMPARE(cell2Iface->rowIndex(), 1); + QCOMPARE(cell2Iface->columnIndex(), 1); + delete child2; + + QAccessibleInterface *child3 = iface->child(11); + QCOMPARE(iface->indexOfChild(child3), 12); + QCOMPARE(child3->text(QAccessible::Name, 0), QString("1.2")); + delete child3; + + QTestAccessibility::clearEvents(); + + // table 2 + QAccessibleTable2Interface *table2 = iface->table2Interface(); + QVERIFY(table2); + QCOMPARE(table2->columnCount(), 3); + QCOMPARE(table2->rowCount(), 3); + QAccessibleTable2CellInterface *cell1; + QVERIFY(cell1 = table2->cellAt(0,0)); + QCOMPARE(cell1->text(QAccessible::Name, 0), QString("0.0")); + QCOMPARE(iface->indexOfChild(cell1), 6); + + QAccessibleTable2CellInterface *cell2; + QVERIFY(cell2 = table2->cellAt(0,1)); + QCOMPARE(cell2->text(QAccessible::Name, 0), QString("0.1")); + QCOMPARE(cell2->role(), QAccessible::Cell); + QCOMPARE(cell2->rowIndex(), 0); + QCOMPARE(cell2->columnIndex(), 1); + QCOMPARE(iface->indexOfChild(cell2), 7); + delete cell2; + + QAccessibleTable2CellInterface *cell3; + QVERIFY(cell3 = table2->cellAt(1,2)); + QCOMPARE(cell3->text(QAccessible::Name, 0), QString("1.2")); + QCOMPARE(cell3->role(), QAccessible::Cell); + QCOMPARE(cell3->rowIndex(), 1); + QCOMPARE(cell3->columnIndex(), 2); + QCOMPARE(iface->indexOfChild(cell3), 12); + delete cell3; + + QCOMPARE(table2->columnDescription(0), QString("h1")); + QCOMPARE(table2->columnDescription(1), QString("h2")); + QCOMPARE(table2->columnDescription(2), QString("h3")); + QCOMPARE(table2->rowDescription(0), QString("v1")); + QCOMPARE(table2->rowDescription(1), QString("v2")); + QCOMPARE(table2->rowDescription(2), QString("v3")); + + delete iface; + + delete tableView; + + QTestAccessibility::clearEvents(); +} +#endif + +void tst_QAccessibility::calendarWidgetTest() +{ +#ifndef QT_NO_CALENDARWIDGET + { + QCalendarWidget calendarWidget; + + QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(&calendarWidget); + QVERIFY(interface); + QCOMPARE(interface->role(), QAccessible::Table); + QVERIFY(!interface->rect().isValid()); + QVERIFY(!interface->rect(1).isValid()); + QCOMPARE(interface->childAt(200, 200), -1); + + calendarWidget.resize(400, 300); + calendarWidget.show(); +#if defined(Q_OS_UNIX) + QCoreApplication::processEvents(); + QTest::qWait(100); +#endif + + // 1 = navigationBar, 2 = view. + QCOMPARE(interface->childCount(), 2); + + const QRect globalGeometry = QRect(calendarWidget.mapToGlobal(QPoint(0, 0)), + calendarWidget.size()); + QCOMPARE(interface->rect(), globalGeometry); + + QWidget *navigationBar = 0; + foreach (QObject *child, calendarWidget.children()) { + if (child->objectName() == QLatin1String("qt_calendar_navigationbar")) { + navigationBar = static_cast<QWidget *>(child); + break; + } + } + QVERIFY(navigationBar); + QVERIFY(verifyChild(navigationBar, interface, 1, globalGeometry)); + + QAbstractItemView *calendarView = 0; + foreach (QObject *child, calendarWidget.children()) { + if (child->objectName() == QLatin1String("qt_calendar_calendarview")) { + calendarView = static_cast<QAbstractItemView *>(child); + break; + } + } + QVERIFY(calendarView); + QVERIFY(verifyChild(calendarView, interface, 2, globalGeometry)); + + // Hide navigation bar. + calendarWidget.setNavigationBarVisible(false); + QCOMPARE(interface->childCount(), 1); + QVERIFY(!navigationBar->isVisible()); + + QVERIFY(verifyChild(calendarView, interface, 1, globalGeometry)); + + // Show navigation bar. + calendarWidget.setNavigationBarVisible(true); + QCOMPARE(interface->childCount(), 2); + QVERIFY(navigationBar->isVisible()); + + // Navigate to the navigation bar via Child. + QAccessibleInterface *navigationBarInterface = interface->child(0); + QVERIFY(navigationBarInterface); + QCOMPARE(navigationBarInterface->object(), (QObject*)navigationBar); + delete navigationBarInterface; + navigationBarInterface = 0; + + // Navigate to the view via Child. + QAccessibleInterface *calendarViewInterface = interface->child(1); + QVERIFY(calendarViewInterface); + QCOMPARE(calendarViewInterface->object(), (QObject*)calendarView); + delete calendarViewInterface; + calendarViewInterface = 0; + + QVERIFY(!interface->child(-1)); + + // Navigate from navigation bar -> view (Down). + QCOMPARE(interface->navigate(QAccessible::Down, 1, &calendarViewInterface), 0); + QVERIFY(calendarViewInterface); + QCOMPARE(calendarViewInterface->object(), (QObject*)calendarView); + delete calendarViewInterface; + calendarViewInterface = 0; + + // Navigate from view -> navigation bar (Up). + QCOMPARE(interface->navigate(QAccessible::Up, 2, &navigationBarInterface), 0); + QVERIFY(navigationBarInterface); + QCOMPARE(navigationBarInterface->object(), (QObject*)navigationBar); + delete navigationBarInterface; + navigationBarInterface = 0; + + } + QTestAccessibility::clearEvents(); +#endif // QT_NO_CALENDARWIDGET +} + +void tst_QAccessibility::dockWidgetTest() +{ +#ifndef QT_NO_DOCKWIDGET + // Set up a proper main window with two dock widgets + QMainWindow *mw = new QMainWindow(); + QFrame *central = new QFrame(mw); + mw->setCentralWidget(central); + QMenuBar *mb = new QMenuBar(mw); + mb->addAction(tr("&File")); + mw->setMenuBar(mb); + + QDockWidget *dock1 = new QDockWidget(mw); + mw->addDockWidget(Qt::LeftDockWidgetArea, dock1); + QPushButton *pb1 = new QPushButton(tr("Push me"), dock1); + dock1->setWidget(pb1); + + QDockWidget *dock2 = new QDockWidget(mw); + mw->addDockWidget(Qt::BottomDockWidgetArea, dock2); + QPushButton *pb2 = new QPushButton(tr("Push me"), dock2); + dock2->setWidget(pb2); + + mw->resize(600,400); + mw->show(); +#if defined(Q_OS_UNIX) + QCoreApplication::processEvents(); + QTest::qWait(100); +#endif + + QAccessibleInterface *accMainWindow = QAccessible::queryAccessibleInterface(mw); + // 4 children: menu bar, dock1, dock2, and central widget + QCOMPARE(accMainWindow->childCount(), 4); + QAccessibleInterface *accDock1 = 0; + for (int i = 1; i <= 4; ++i) { + if (accMainWindow->role(i) == QAccessible::Window) { + accDock1 = accMainWindow->child(i-1); + if (accDock1 && qobject_cast<QDockWidget*>(accDock1->object()) == dock1) { + break; + } else { + delete accDock1; + } + } + } + QVERIFY(accDock1); + QCOMPARE(accDock1->role(), QAccessible::Window); + QCOMPARE(accDock1->role(1), QAccessible::TitleBar); + QVERIFY(accDock1->rect().contains(accDock1->rect(1))); + + QPoint globalPos = dock1->mapToGlobal(QPoint(0,0)); + globalPos.rx()+=5; //### query style + globalPos.ry()+=5; + int entry = accDock1->childAt(globalPos.x(), globalPos.y()); //### + QCOMPARE(entry, 1); + QAccessibleInterface *accTitleBar = accDock1->child(entry - 1); + + QCOMPARE(accTitleBar->role(), QAccessible::TitleBar); + QCOMPARE(accDock1->indexOfChild(accTitleBar), 1); + QAccessibleInterface *acc; + acc = accTitleBar->parent(); + QVERIFY(acc); + QCOMPARE(acc->role(), QAccessible::Window); + + + delete accTitleBar; + delete accDock1; + delete pb1; + delete pb2; + delete dock1; + delete dock2; + delete mw; + QTestAccessibility::clearEvents(); +#endif // QT_NO_DOCKWIDGET +} + +void tst_QAccessibility::comboBoxTest() +{ +#if defined(Q_OS_WINCE) + if (!IsValidCEPlatform()) { + QSKIP("Test skipped on Windows Mobile test hardware", SkipAll); + } +#endif + { // not editable combobox + QComboBox combo; + combo.addItems(QStringList() << "one" << "two" << "three"); + combo.show(); + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(&combo); + QCOMPARE(verifyHierarchy(iface), 0); + + QCOMPARE(iface->role(), QAccessible::ComboBox); + QCOMPARE(iface->childCount(), 1); + +#ifdef Q_OS_UNIX + QCOMPARE(iface->text(QAccessible::Name), QLatin1String("one")); +#endif + QCOMPARE(iface->text(QAccessible::Value), QLatin1String("one")); + combo.setCurrentIndex(2); +#ifdef Q_OS_UNIX + QCOMPARE(iface->text(QAccessible::Name), QLatin1String("three")); +#endif + QCOMPARE(iface->text(QAccessible::Value), QLatin1String("three")); + + QAccessibleInterface *listIface = iface->child(0); + QCOMPARE(listIface->role(), QAccessible::List); + QCOMPARE(listIface->childCount(), 3); + + QVERIFY(!combo.view()->isVisible()); + QVERIFY(iface->actionInterface()); + QCOMPARE(iface->actionInterface()->actionNames(), QStringList() << QAccessibleActionInterface::ShowMenuAction); + iface->actionInterface()->doAction(QAccessibleActionInterface::ShowMenuAction); + QVERIFY(combo.view()->isVisible()); + + delete iface; + } + + { // editable combobox + QComboBox editableCombo; + editableCombo.show(); + editableCombo.setEditable(true); + editableCombo.addItems(QStringList() << "foo" << "bar" << "baz"); + + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(&editableCombo); + QCOMPARE(verifyHierarchy(iface), 0); + + QCOMPARE(iface->role(), QAccessible::ComboBox); + QCOMPARE(iface->childCount(), 2); + + QAccessibleInterface *listIface = iface->child(0); + QCOMPARE(listIface->role(), QAccessible::List); + QAccessibleInterface *editIface = iface->child(1); + QCOMPARE(editIface->role(), QAccessible::EditableText); + + delete listIface; + delete editIface; + delete iface; + } + + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::labelTest() +{ + QString text = "Hello World"; + QLabel *label = new QLabel(text); + label->show(); + +#if defined(Q_OS_UNIX) + QCoreApplication::processEvents(); +#endif + QTest::qWait(100); + + QAccessibleInterface *acc_label = QAccessible::queryAccessibleInterface(label); + QVERIFY(acc_label); + + QCOMPARE(acc_label->text(QAccessible::Name, 0), text); + + delete acc_label; + delete label; + QTestAccessibility::clearEvents(); + + QPixmap testPixmap(50, 50); + testPixmap.fill(); + + QLabel imageLabel; + imageLabel.setPixmap(testPixmap); + imageLabel.setToolTip("Test Description"); + + acc_label = QAccessible::queryAccessibleInterface(&imageLabel); + QVERIFY(acc_label); + + QAccessibleImageInterface *imageInterface = acc_label->imageInterface(); + QVERIFY(imageInterface); + + QCOMPARE(imageInterface->imageSize(), testPixmap.size()); + QCOMPARE(imageInterface->imageDescription(), QString::fromLatin1("Test Description")); + QCOMPARE(imageInterface->imagePosition(QAccessible2::RelativeToParent), imageLabel.geometry()); + + delete acc_label; + + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::accelerators() +{ + QWidget *window = new QWidget; + QHBoxLayout *lay = new QHBoxLayout(window); + QLabel *label = new QLabel(tr("&Line edit"), window); + QLineEdit *le = new QLineEdit(window); + lay->addWidget(label); + lay->addWidget(le); + label->setBuddy(le); + + window->show(); + + QAccessibleInterface *accLineEdit = QAccessible::queryAccessibleInterface(le); + QCOMPARE(accLineEdit->text(QAccessible::Accelerator, 0), QKeySequence(Qt::ALT).toString(QKeySequence::NativeText) + QLatin1String("L")); + QCOMPARE(accLineEdit->text(QAccessible::Accelerator, 0), QKeySequence(Qt::ALT).toString(QKeySequence::NativeText) + QLatin1String("L")); + label->setText(tr("Q &")); + QCOMPARE(accLineEdit->text(QAccessible::Accelerator, 0), QString()); + label->setText(tr("Q &&")); + QCOMPARE(accLineEdit->text(QAccessible::Accelerator, 0), QString()); + label->setText(tr("Q && A")); + QCOMPARE(accLineEdit->text(QAccessible::Accelerator, 0), QString()); + label->setText(tr("Q &&&A")); + QCOMPARE(accLineEdit->text(QAccessible::Accelerator, 0), QKeySequence(Qt::ALT).toString(QKeySequence::NativeText) + QLatin1String("A")); + label->setText(tr("Q &&A")); + QCOMPARE(accLineEdit->text(QAccessible::Accelerator, 0), QString()); + +#if !defined(QT_NO_DEBUG) && !defined(Q_WS_MAC) + QTest::ignoreMessage(QtWarningMsg, "QKeySequence::mnemonic: \"Q &A&B\" contains multiple occurrences of '&'"); +#endif + label->setText(tr("Q &A&B")); + QCOMPARE(accLineEdit->text(QAccessible::Accelerator, 0), QKeySequence(Qt::ALT).toString(QKeySequence::NativeText) + QLatin1String("A")); + +#if defined(Q_OS_UNIX) + QCoreApplication::processEvents(); +#endif + QTest::qWait(100); + delete window; + QTestAccessibility::clearEvents(); +} + +QTEST_MAIN(tst_QAccessibility) +#include "tst_qaccessibility.moc" diff --git a/tests/auto/integrationtests/qcomplextext/.gitignore b/tests/auto/integrationtests/qcomplextext/.gitignore new file mode 100644 index 0000000000..8929cd2f89 --- /dev/null +++ b/tests/auto/integrationtests/qcomplextext/.gitignore @@ -0,0 +1 @@ +tst_qcomplextext diff --git a/tests/auto/integrationtests/qcomplextext/bidireorderstring.h b/tests/auto/integrationtests/qcomplextext/bidireorderstring.h new file mode 100644 index 0000000000..e0bbf6e80b --- /dev/null +++ b/tests/auto/integrationtests/qcomplextext/bidireorderstring.h @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ +struct LV { + const char *name; + const char *logical; + const char *visual; + int basicDir; +}; + +const LV logical_visual[] = { + { "data0", "Hello", "Hello", QChar::DirL }, + { "data1", "\327\251\327\234\327\225\327\235", "\327\235\327\225\327\234\327\251", QChar::DirR }, + { "data2", "Hello \327\251\327\234\327\225\327\235", "Hello \327\235\327\225\327\234\327\251", QChar::DirL }, + { "data3", "car is \327\236\327\233\327\225\327\240\327\231\327\252 \327\220\327\225\327\230\327\225 in hebrew", "car is \327\225\327\230\327\225\327\220 \327\252\327\231\327\240\327\225\327\233\327\236 in hebrew", QChar::DirL }, + { "data4", "\327\236\327\233\327\225\327\240\327\231\327\252 \327\224\327\231\327\220 the car \327\221\327\251\327\244\327\224 \327\224\327\220\327\240\327\222\327\234\327\231\327\252", "\327\252\327\231\327\234\327\222\327\240\327\220\327\224 \327\224\327\244\327\251\327\221 the car \327\220\327\231\327\224 \327\252\327\231\327\240\327\225\327\233\327\236", QChar::DirR }, + { "data5", "he said \"\327\226\327\224 \327\251\327\225\327\225\327\224 123, 456, \327\221\327\241\327\223\327\250\" ", "he said \"\327\250\327\223\327\241\327\221 ,456 ,123 \327\224\327\225\327\225\327\251 \327\224\327\226\" ", QChar::DirL }, + { "data6", "he said \"\327\226\327\224 \327\251\327\225\327\225\327\224 (123, 456), \327\221\327\241\327\223\327\250\"", "he said \"\327\250\327\223\327\241\327\221 ,(456 ,123) \327\224\327\225\327\225\327\251 \327\224\327\226\"", QChar::DirL }, + { "data7", "he said \"\327\226\327\224 \327\251\327\225\327\225\327\224 123,456, \327\221\327\241\327\223\327\250\"", "he said \"\327\250\327\223\327\241\327\221 ,123,456 \327\224\327\225\327\225\327\251 \327\224\327\226\"", QChar::DirL }, + { "data8", "he said \"\327\226\327\224 \327\251\327\225\327\225\327\224 ,(123,456) \327\221\327\241\327\223\327\250\"", "he said \"\327\250\327\223\327\241\327\221 (123,456), \327\224\327\225\327\225\327\251 \327\224\327\226\"", QChar::DirL }, + { "data9", "\327\224\327\225\327\220 \327\220\327\236\327\250 \"it is 123, 456, ok\"", "\"it is 123, 456, ok\" \327\250\327\236\327\220 \327\220\327\225\327\224", QChar::DirR }, + { "data10", "<\327\233123>shalom</\327\233123>", "<123\327\233/>shalom<123\327\233>", QChar::DirR }, + { "data11", "<h123>\327\251\327\234\327\225\327\235</h123>", "<h123>\327\235\327\225\327\234\327\251</h123>", QChar::DirL }, + { "data12", "\327\224\327\225\327\220 \327\220\327\236\327\250 \"it is a car!\" \327\225\327\220\327\226 \327\250\327\245", "\327\245\327\250 \327\226\327\220\327\225 \"!it is a car\" \327\250\327\236\327\220 \327\220\327\225\327\224", QChar::DirR }, + { "data13", "\327\224\327\225\327\220 \327\220\327\236\327\250 \"it is a car!x\" \327\225\327\220\327\226 \327\250\327\245", "\327\245\327\250 \327\226\327\220\327\225 \"it is a car!x\" \327\250\327\236\327\220 \327\220\327\225\327\224", QChar::DirR }, + { "data14", "-2 \327\236\327\242\327\234\327\225\327\252 \327\226\327\224 \327\247\327\250", "\327\250\327\247 \327\224\327\226 \327\252\327\225\327\234\327\242\327\236 2-", QChar::DirR }, + { "data15", "-10% \327\251\327\231\327\240\327\225\327\231", "\327\231\327\225\327\240\327\231\327\251 10%-", QChar::DirR }, + { "data16", "\327\224\327\230\327\225\327\225\327\227 \327\224\327\231\327\240\327\225 2.5..5", "5..2.5 \327\225\327\240\327\231\327\224 \327\227\327\225\327\225\327\230\327\224", QChar::DirR }, + { "data17", "he said \"\327\226\327\225 \327\236\327\233\327\225\327\240\327\231\327\252!\"", "he said \"\327\252\327\231\327\240\327\225\327\233\327\236 \327\225\327\226!\"", QChar::DirL }, + { "data18", "he said \"\327\226\327\225 \327\236\327\233\327\225\327\240\327\231\327\252!\327\251\"", "he said \"\327\251!\327\252\327\231\327\240\327\225\327\233\327\236 \327\225\327\226\"", QChar::DirL }, + { "data19", "(\327\240\327\231\327\241\327\225\327\231) abc", "abc (\327\231\327\225\327\241\327\231\327\240)", QChar::DirR }, + { "data20", "abc (\327\240\327\231\327\241\327\225\327\231)", "abc (\327\231\327\225\327\241\327\231\327\240)", QChar::DirL }, + { "data21", "\327\240\327\231\327\241\327\225\327\231 23 \327\231\327\227\327\231\327\223 abc", "abc \327\223\327\231\327\227\327\231 23 \327\231\327\225\327\241\327\231\327\240", QChar::DirR }, + { "data22", "#@$ \327\240\327\231\327\241\327\225\327\231", "\327\231\327\225\327\241\327\231\327\240 $@#", QChar::DirR }, + { "data23", "\327\240\327\231\327\241\327\225\327\231 ~~~23%%% \327\231\327\227\327\231\327\223 abc ", " abc \327\223\327\231\327\227\327\231 23%%%~~~ \327\231\327\225\327\241\327\231\327\240", QChar::DirR }, + { "data24", "\327\240\327\231\327\241\327\225\327\231 abc ~~~23%%% \327\231\327\227\327\231\327\223 abc", "abc \327\223\327\231\327\227\327\231 abc ~~~23%%% \327\231\327\225\327\241\327\231\327\240", QChar::DirR }, + { "data25", "\327\240\327\231\327\241\327\225\327\231 abc@23@cde \327\231\327\227\327\231\327\223", "\327\223\327\231\327\227\327\231 abc@23@cde \327\231\327\225\327\241\327\231\327\240", QChar::DirR }, + { "data26", "\327\240\327\231\327\241\327\225\327\231 abc 23 cde \327\231\327\227\327\231\327\223", "\327\223\327\231\327\227\327\231 abc 23 cde \327\231\327\225\327\241\327\231\327\240", QChar::DirR }, + { "data27", "\327\240\327\231\327\241\327\225\327\231 abc 23 \327\231\327\227\327\231\327\223 cde", "cde \327\223\327\231\327\227\327\231 abc 23 \327\231\327\225\327\241\327\231\327\240", QChar::DirR }, + { "data28", "\327\222a 2 \327\251", "\327\251 a 2\327\222", QChar::DirR }, + { "data29", "\327\244\327\252\327\225\327\250 1*5 1-5 1/5 1+5", "1+5 1/5 1-5 5*1 \327\250\327\225\327\252\327\244", QChar::DirR }, + { "data30", "\330\272 1*5 1-5 1/5 1+5", "5+1 1/5 5-1 5*1 \330\272", QChar::DirR }, + { "data31", "\330\250 \333\261\333\262.\333\263", "\333\261\333\262.\333\263 \330\250", QChar::DirR }, + { "data32", "\330\250 12.3", "12.3 \330\250", QChar::DirR }, + { "data33", "\330\250 1.23", "1.23 \330\250", QChar::DirR }, + { "data34", "\331\276 \333\261.\333\262\333\263", "\333\261.\333\262\333\263 \331\276", QChar::DirR }, + { "data35", "\331\276\333\261.\333\262\333\263", "\333\261.\333\262\333\263\331\276", QChar::DirR }, + { "data36", "1) \327\251", "\327\251 (1", QChar::DirR }, + { "data37", "1) \327\251", "1) \327\251", QChar::DirL }, + { "data38", "\327\224-w3c", "w3c-\327\224", QChar::DirR }, + { "data39", "\327\224-w3c", "\327\224-w3c", QChar::DirL }, + { "data40", "17:25, foo", "17:25, foo", QChar::DirL }, + { "data41", "5, foo", "foo ,5", QChar::DirR }, + { "data42", "foo\nfoo", "foo\nfoo", QChar::DirL }, + { "data43", "\327\251\327\234\327\225\327\235\n\327\251\327\234\327\225\327\235", "\327\235\327\225\327\234\327\251\n\327\235\327\225\327\234\327\251", QChar::DirR }, + { "data44", "foo\n\327\251\327\234\327\225\327\235", "foo\n\327\235\327\225\327\234\327\251", QChar::DirL }, + { "data45", "\327\251\327\234\327\225\327\235\nfoo", "\327\235\327\225\327\234\327\251\nfoo", QChar::DirR }, + { "data46", "\330\250 1.23 \330\250", "\330\250 1.23 \330\250", QChar::DirR }, + { "data47", "\331\204\330\250 1.23 \331\202\330\250", "\330\250\331\202 1.23 \330\250\331\204", QChar::DirR }, + { "data48", "\330\250 1.2 \330\250", "\330\250 1.2 \330\250", QChar::DirR }, + { "data49", "\331\204\330\250 1.2 \331\202\330\250", "\330\250\331\202 1.2 \330\250\331\204", QChar::DirR }, + { "data50", "a\331\204\330\250 1.2 \331\202\330\250", "a\330\250\331\202 1.2 \330\250\331\204", QChar::DirL }, + + { "data51", "ab(\327\240\327\231)cd", "ab(\327\231\327\240)cd", QChar::DirL }, + { "data52", "ab(\327\240\327\231)cd", "cd(\327\231\327\240)ab", QChar::DirR }, + { "data53", "a(\327\231)c", "a(\327\231)c", QChar::DirL }, + { "data54", "a(\327\231)c", "c(\327\231)a", QChar::DirR }, + { "data55", "\"[\327\220]\"", "\"[\327\220]\"", QChar::DirR }, + { "data56", "\"[\327\220]\"", "\"[\327\220]\"", QChar::DirL }, + { "data57", "\331\204\330\250 \331\202\330\250", "\330\250\331\202 \330\250\331\204", QChar::DirR }, + { "data58", "\331\204\330\250 \331\202\330\250", "\330\250\331\202 \330\250\331\204", QChar::DirL }, + { "data59", "3layout", "3layout", QChar::DirL }, + { "data60", "3layout", "3layout", QChar::DirR }, + { "data61", "3l", "3l", QChar::DirR }, + { "data62", "3la", "3la", QChar::DirR }, + { "data63", "3lay", "3lay", QChar::DirR }, + +// explicit levels +// LRE: \342\200\252 +// RLE: \342\200\253 +// PDF: \342\200\254 +// LRO: \342\200\255 +// RLO: \342\200\256 + + { "override1", "\342\200\256hello\342\200\254", "\342\200\254olleh\342\200\256", QChar::DirL }, + { "override2", "\342\200\255hello\342\200\254", "\342\200\255hello\342\200\254", QChar::DirL }, + { "override3", "\342\200\255\327\251\327\234\327\225\327\235\342\200\254", "\342\200\255\327\251\327\234\327\225\327\235\342\200\254", QChar::DirL }, + { "override4", "\342\200\256\327\251\327\234\327\225\327\235\342\200\254", "\342\200\254\327\235\327\225\327\234\327\251\342\200\256", QChar::DirL }, + { "override5", "\342\200\256hello\342\200\254", "\342\200\254olleh\342\200\256", QChar::DirR }, + { "override6", "\342\200\255hello\342\200\254", "\342\200\255hello\342\200\254", QChar::DirR }, + { "override7", "\342\200\255\327\251\327\234\327\225\327\235\342\200\254", "\342\200\255\327\251\327\234\327\225\327\235\342\200\254", QChar::DirR }, + { "override8", "\342\200\256\327\251\327\234\327\225\327\235\342\200\254", "\342\200\254\327\235\327\225\327\234\327\251\342\200\256", QChar::DirR }, + + { "override9", "\327\224\342\200\255\327\251\327\234\342\200\256hello\342\200\254\327\225\327\235\342\200\254", "\342\200\255\327\251\327\234\342\200\254olleh\342\200\256\327\225\327\235\342\200\254\327\224", QChar::DirL }, + { "override10", "\327\224\342\200\255\327\251\327\234\342\200\256hello\342\200\254\327\225\327\235\342\200\254", "\342\200\255\327\251\327\234\342\200\254olleh\342\200\256\327\225\327\235\342\200\254\327\224", QChar::DirR }, + + + { "embed1", "\342\200\252hello\342\200\254", "\342\200\252hello\342\200\254", QChar::DirL }, + { "embed2", "\342\200\253hello\342\200\254", "\342\200\254hello\342\200\253", QChar::DirL }, + { "embed3", "\342\200\252hello\342\200\254", "\342\200\252hello\342\200\254", QChar::DirR }, + { "embed4", "\342\200\253hello\342\200\254", "\342\200\254hello\342\200\253", QChar::DirR }, + { "embed5", "\342\200\252\327\251\327\234\327\225\327\235\342\200\254", "\342\200\252\327\235\327\225\327\234\327\251\342\200\254", QChar::DirL }, + { "embed6", "\342\200\253\327\251\327\234\327\225\327\235\342\200\254", "\342\200\254\327\235\327\225\327\234\327\251\342\200\253", QChar::DirL }, + { "embed7", "\342\200\252\327\251\327\234\327\225\327\235\342\200\254", "\342\200\252\327\235\327\225\327\234\327\251\342\200\254", QChar::DirR }, + { "embed8", "\342\200\253\327\251\327\234\327\225\327\235\342\200\254", "\342\200\254\327\235\327\225\327\234\327\251\342\200\253", QChar::DirR }, + + { "embed9", "\342\200\252x \327\251\327\234\327\225\327\235 y\342\200\254", "\342\200\252x \327\235\327\225\327\234\327\251 y\342\200\254", QChar::DirL }, + { "embed10", "\342\200\253x \327\251\327\234\327\225\327\235 y\342\200\254", "\342\200\254y \327\235\327\225\327\234\327\251 x\342\200\253", QChar::DirL }, + { "embed11", "\342\200\252x \327\251\327\234\327\225\327\235 y\342\200\254", "\342\200\252x \327\235\327\225\327\234\327\251 y\342\200\254", QChar::DirR }, + { "embed12", "\342\200\253x \327\251\327\234\327\225\327\235 y\342\200\254", "\342\200\254y \327\235\327\225\327\234\327\251 x\342\200\253", QChar::DirR }, + { "zwsp", "+0\342\200\213f-1", "+0\342\200\213f-1", QChar::DirL }, + + { 0, 0, 0, QChar::DirON } +}; diff --git a/tests/auto/integrationtests/qcomplextext/qcomplextext.pro b/tests/auto/integrationtests/qcomplextext/qcomplextext.pro new file mode 100644 index 0000000000..e04ebcbc69 --- /dev/null +++ b/tests/auto/integrationtests/qcomplextext/qcomplextext.pro @@ -0,0 +1,5 @@ +load(qttest_p4) +QT += widgets widgets-private +QT += core-private gui-private +SOURCES += tst_qcomplextext.cpp +INCLUDEPATH += $$QT_SOURCE_TREE/src/3rdparty/harfbuzz/src diff --git a/tests/auto/integrationtests/qcomplextext/tst_qcomplextext.cpp b/tests/auto/integrationtests/qcomplextext/tst_qcomplextext.cpp new file mode 100644 index 0000000000..c72393cfe6 --- /dev/null +++ b/tests/auto/integrationtests/qcomplextext/tst_qcomplextext.cpp @@ -0,0 +1,294 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + + +// Horrible hack, but this get this out of the way for now +// Carlos Duclos, 2007-12-11 +#if !defined(Q_WS_MAC) + +#include <QtTest/QtTest> +#include <QtGui/QtGui> +#include <private/qtextengine_p.h> + +#include "bidireorderstring.h" + + +//TESTED_CLASS= +//TESTED_FILES=gui/widgets/qcombobox.h gui/widgets/qcombobox.cpp + +class tst_QComplexText : public QObject +{ +Q_OBJECT + +public: + tst_QComplexText(); + virtual ~tst_QComplexText(); + + +public slots: + void init(); + void cleanup(); +private slots: + void bidiReorderString_data(); + void bidiReorderString(); + void bidiCursor_qtbug2795(); + void bidiCursor_PDF(); + void bidiCursorMovement_data(); + void bidiCursorMovement(); + void bidiCursorLogicalMovement_data(); + void bidiCursorLogicalMovement(); +}; + +tst_QComplexText::tst_QComplexText() +{ +} + +tst_QComplexText::~tst_QComplexText() +{ + +} + +void tst_QComplexText::init() +{ +// This will be executed immediately before each test is run. +// TODO: Add initialization code here. +} + +void tst_QComplexText::cleanup() +{ +// This will be executed immediately after each test is run. +// TODO: Add cleanup code here. +} + +void tst_QComplexText::bidiReorderString_data() +{ + QTest::addColumn<QString>("logical"); + QTest::addColumn<QString>("VISUAL"); + QTest::addColumn<int>("basicDir"); + + const LV *data = logical_visual; + while ( data->name ) { + //next we fill it with data + QTest::newRow( data->name ) + << QString::fromUtf8( data->logical ) + << QString::fromUtf8( data->visual ) + << (int) data->basicDir; + data++; + } +} + +void tst_QComplexText::bidiReorderString() +{ + QFETCH( QString, logical ); + QFETCH( int, basicDir ); + + // replace \n with Unicode newline. The new algorithm ignores \n + logical.replace(QChar('\n'), QChar(0x2028)); + + QTextEngine e(logical, QFont()); + e.option.setTextDirection((QChar::Direction)basicDir == QChar::DirL ? Qt::LeftToRight : Qt::RightToLeft); + e.itemize(); + quint8 levels[256]; + int visualOrder[256]; + int nitems = e.layoutData->items.size(); + int i; + for (i = 0; i < nitems; ++i) { + //qDebug("item %d bidiLevel=%d", i, e.items[i].analysis.bidiLevel); + levels[i] = e.layoutData->items[i].analysis.bidiLevel; + } + e.bidiReorder(nitems, levels, visualOrder); + + QString visual; + for (i = 0; i < nitems; ++i) { + QScriptItem &si = e.layoutData->items[visualOrder[i]]; + QString sub = logical.mid(si.position, e.length(visualOrder[i])); + if (si.analysis.bidiLevel % 2) { + // reverse sub + QChar *a = (QChar *)sub.unicode(); + QChar *b = a + sub.length() - 1; + while (a < b) { + QChar tmp = *a; + *a = *b; + *b = tmp; + ++a; + --b; + } + a = (QChar *)sub.unicode(); + b = a + sub.length(); + while (a<b) { + *a = a->mirroredChar(); + ++a; + } + } + visual += sub; + } + // replace Unicode newline back with \n to compare. + visual.replace(QChar(0x2028), QChar('\n')); + + QTEST(visual, "VISUAL"); +} + +void tst_QComplexText::bidiCursor_qtbug2795() +{ + QString str = QString::fromUtf8("الجزيرة نت"); + QTextLayout l1(str); + + l1.beginLayout(); + QTextLine line1 = l1.createLine(); + l1.endLayout(); + + qreal x1 = line1.cursorToX(0) - line1.cursorToX(str.size()); + + str.append("1"); + QTextLayout l2(str); + l2.beginLayout(); + QTextLine line2 = l2.createLine(); + l2.endLayout(); + + qreal x2 = line2.cursorToX(0) - line2.cursorToX(str.size()); + + // The cursor should remain at the same position after a digit is appended + QVERIFY(x1 == x2); +} + +void tst_QComplexText::bidiCursorMovement_data() +{ + QTest::addColumn<QString>("logical"); + QTest::addColumn<int>("basicDir"); + + const LV *data = logical_visual; + while ( data->name ) { + //next we fill it with data + QTest::newRow( data->name ) + << QString::fromUtf8( data->logical ) + << (int) data->basicDir; + data++; + } +} + +void tst_QComplexText::bidiCursorMovement() +{ + QFETCH(QString, logical); + QFETCH(int, basicDir); + + QTextLayout layout(logical); + + QTextOption option = layout.textOption(); + option.setTextDirection(basicDir == QChar::DirL ? Qt::LeftToRight : Qt::RightToLeft); + layout.setTextOption(option); + layout.setCursorMoveStyle(Qt::VisualMoveStyle); + bool moved; + int oldPos, newPos = 0; + qreal x, newX; + + layout.beginLayout(); + QTextLine line = layout.createLine(); + layout.endLayout(); + + newX = line.cursorToX(0); + do { + oldPos = newPos; + x = newX; + newX = line.cursorToX(oldPos); + if (basicDir == QChar::DirL) { + QVERIFY(newX >= x); + newPos = layout.rightCursorPosition(oldPos); + } else + { + QVERIFY(newX <= x); + newPos = layout.leftCursorPosition(oldPos); + } + moved = (oldPos != newPos); + } while (moved); +} + +void tst_QComplexText::bidiCursorLogicalMovement_data() +{ + bidiCursorMovement_data(); +} + +void tst_QComplexText::bidiCursorLogicalMovement() +{ + QFETCH(QString, logical); + QFETCH(int, basicDir); + + QTextLayout layout(logical); + + QTextOption option = layout.textOption(); + option.setTextDirection(basicDir == QChar::DirL ? Qt::LeftToRight : Qt::RightToLeft); + layout.setTextOption(option); + bool moved; + int oldPos, newPos = 0; + + do { + oldPos = newPos; + newPos = layout.nextCursorPosition(oldPos); + QVERIFY(newPos >= oldPos); + moved = (oldPos != newPos); + } while (moved); + + do { + oldPos = newPos; + newPos = layout.previousCursorPosition(oldPos); + QVERIFY(newPos <= oldPos); + moved = (oldPos != newPos); + } while (moved); +} + +void tst_QComplexText::bidiCursor_PDF() +{ + QString str = QString::fromUtf8("\342\200\252hello\342\200\254"); + QTextLayout layout(str); + + layout.beginLayout(); + QTextLine line = layout.createLine(); + layout.endLayout(); + + int size = str.size(); + + QVERIFY(line.cursorToX(size) == line.cursorToX(size - 1)); +} + +QTEST_MAIN(tst_QComplexText) +#include "tst_qcomplextext.moc" + +#endif // Q_WS_MAC + diff --git a/tests/auto/integrationtests/qdirectpainter/.gitignore b/tests/auto/integrationtests/qdirectpainter/.gitignore new file mode 100644 index 0000000000..615dfa8ddc --- /dev/null +++ b/tests/auto/integrationtests/qdirectpainter/.gitignore @@ -0,0 +1,2 @@ +tst_qdirectpainter +runDirectPainter/runDirectPainter diff --git a/tests/auto/integrationtests/qdirectpainter/qdirectpainter.pro b/tests/auto/integrationtests/qdirectpainter/qdirectpainter.pro new file mode 100644 index 0000000000..e86b3427eb --- /dev/null +++ b/tests/auto/integrationtests/qdirectpainter/qdirectpainter.pro @@ -0,0 +1,5 @@ +TEMPLATE = subdirs +SUBDIRS = test runDirectPainter + +requires(embedded) + diff --git a/tests/auto/integrationtests/qdirectpainter/runDirectPainter/main.cpp b/tests/auto/integrationtests/qdirectpainter/runDirectPainter/main.cpp new file mode 100644 index 0000000000..af3dd90beb --- /dev/null +++ b/tests/auto/integrationtests/qdirectpainter/runDirectPainter/main.cpp @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** 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 <QApplication> +#include <QCopChannel> +#include <QDirectPainter> +#include <QImage> +#include <QScreen> +#include <QPainter> + +int main(int argc, char** argv) +{ +#ifdef Q_WS_QWS + QApplication app(argc, argv); + QDirectPainter dp; + QImage screenImage(dp.frameBuffer(), dp.screenWidth(), dp.screenHeight(), + dp.linestep(), QScreen::instance()->pixelFormat()); + QPainter p; + + QStringList args = app.arguments(); + + dp.setRegion(QRect(args.at(1).toInt(), + args.at(2).toInt(), + args.at(3).toInt(), + args.at(4).toInt())); + + for (;;) { + dp.startPainting(); + p.begin(&screenImage); + p.setPen(Qt::NoPen); + p.setBrush(Qt::blue); + p.drawRects(dp.allocatedRegion().rects()); + p.end(); + dp.endPainting(dp.allocatedRegion()); + app.processEvents(); + } +#else + Q_UNUSED(argc); + Q_UNUSED(argv); +#endif + + return 0; +} + diff --git a/tests/auto/integrationtests/qdirectpainter/runDirectPainter/runDirectPainter.pro b/tests/auto/integrationtests/qdirectpainter/runDirectPainter/runDirectPainter.pro new file mode 100644 index 0000000000..09524e90c3 --- /dev/null +++ b/tests/auto/integrationtests/qdirectpainter/runDirectPainter/runDirectPainter.pro @@ -0,0 +1,6 @@ +load(qttest_p4) +SOURCES = main.cpp +TARGET = runDirectPainter + +requires(embedded) + diff --git a/tests/auto/integrationtests/qdirectpainter/test/test.pro b/tests/auto/integrationtests/qdirectpainter/test/test.pro new file mode 100644 index 0000000000..3621e11fa9 --- /dev/null +++ b/tests/auto/integrationtests/qdirectpainter/test/test.pro @@ -0,0 +1,7 @@ +load(qttest_p4) +QT += widgets +SOURCES += ../tst_qdirectpainter.cpp +TARGET = ../tst_qdirectpainter + +requires(embedded) + diff --git a/tests/auto/integrationtests/qdirectpainter/tst_qdirectpainter.cpp b/tests/auto/integrationtests/qdirectpainter/tst_qdirectpainter.cpp new file mode 100644 index 0000000000..550c2e96c0 --- /dev/null +++ b/tests/auto/integrationtests/qdirectpainter/tst_qdirectpainter.cpp @@ -0,0 +1,245 @@ +/**************************************************************************** +** +** 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> + +//TESTED_CLASS= +//TESTED_FILES= + +#include <qwindowsystem_qws.h> +#include <qpainter.h> +#include <qdesktopwidget.h> +#include <qdirectpainter_qws.h> +#include <private/qwindowsurface_qws_p.h> +#include <private/qdrawhelper_p.h> + +class tst_QDirectPainter : public QObject +{ + Q_OBJECT + +public: + tst_QDirectPainter() {} + ~tst_QDirectPainter() {} + +private slots: + void initTestCase(); + void setGeometry_data(); + void setGeometry(); +#ifndef QT_NO_PROCESS + void regionSynchronization(); +#endif + void reservedSynchronous(); + +private: + QWSWindow* getWindow(int windId); + QColor bgColor; +}; + +class ColorPainter : public QDirectPainter +{ +public: + ColorPainter(SurfaceFlag flag = NonReserved, + const QColor &color = QColor(Qt::red)) + : QDirectPainter(0, flag), c(color) {} + + QColor color() { return c; } + +protected: + void regionChanged(const QRegion ®ion) { + QScreen::instance()->solidFill(c, region); + } + +private: + QColor c; + QRegion r; +}; + +Q_DECLARE_METATYPE(QDirectPainter::SurfaceFlag) + +void tst_QDirectPainter::initTestCase() +{ + bgColor = QColor(Qt::green); + + QWSServer *server = QWSServer::instance(); + server->setBackground(bgColor); +} + +QWSWindow* tst_QDirectPainter::getWindow(int winId) +{ + QWSServer *server = QWSServer::instance(); + foreach (QWSWindow *w, server->clientWindows()) { + if (w->winId() == winId) + return w; + } + return 0; +} + +class ColorWidget : public QWidget +{ +public: + ColorWidget(QWidget *parent = 0, const QColor &c = QColor(Qt::red)) + : QWidget(parent, Qt::FramelessWindowHint), color(c) + { + QPalette opaquePalette = palette(); + opaquePalette.setColor(backgroundRole(), color); + setPalette(opaquePalette); + setAutoFillBackground(true); + } + + void paintEvent(QPaintEvent *e) { + r += e->region(); + } + + void reset() { + r = QRegion(); + } + + QColor color; + QRegion r; +}; + +#define VERIFY_COLOR(rect, color) { \ + const QPixmap pixmap = QPixmap::grabWindow(QDesktopWidget().winId(), \ + rect.left(), rect.top(), \ + rect.width(), rect.height()); \ + QCOMPARE(pixmap.size(), rect.size()); \ + QPixmap expectedPixmap(pixmap); /* ensure equal formats */ \ + expectedPixmap.fill(color); \ + QCOMPARE(pixmap, expectedPixmap); \ +} + +void tst_QDirectPainter::setGeometry_data() +{ + QTest::addColumn<QDirectPainter::SurfaceFlag>("flag"); + + QTest::newRow("NonReserved") << QDirectPainter::NonReserved; + QTest::newRow("Reserved") << QDirectPainter::Reserved; + QTest::newRow("ReservedSynchronous") << QDirectPainter::ReservedSynchronous; +} + +void tst_QDirectPainter::setGeometry() +{ + QFETCH(QDirectPainter::SurfaceFlag, flag); + + const QRect rect(100, 100, 100, 100); + { + ColorPainter w(flag); + + w.setGeometry(rect); + QApplication::processEvents(); + QCOMPARE(w.geometry(), rect); + VERIFY_COLOR(rect, w.color()); + } + QApplication::processEvents(); + VERIFY_COLOR(rect, bgColor); +} + +#ifndef QT_NO_PROCESS +void tst_QDirectPainter::regionSynchronization() +{ + QRect dpRect(10, 10, 50, 50); + + // Start the direct painter in a different process + QProcess proc; + QStringList args; + args << QString::number(dpRect.x()) + << QString::number(dpRect.y()) + << QString::number(dpRect.width()) + << QString::number(dpRect.height()); + + proc.start("runDirectPainter/runDirectPainter", args); + QVERIFY(proc.waitForStarted(5 * 1000)); + QTest::qWait(1000); + QApplication::processEvents(); + VERIFY_COLOR(dpRect, Qt::blue); // blue hardcoded in runDirectPainter + + QTime t; + t.start(); + static int i = 0; + while (t.elapsed() < 10 * 1000) { + QApplication::processEvents(); + + ColorWidget w; + w.setGeometry(10, 10, 50, 50); + const QRect wRect = dpRect.translated(10, 0); + w.setGeometry(wRect); + w.show(); + + QApplication::processEvents(); + QApplication::processEvents(); //glib event loop workaround + VERIFY_COLOR(wRect, w.color); + ++i; + } + QVERIFY(i > 100); // sanity check + + proc.kill(); +} +#endif + +class MyObject : public QObject +{ +public: + MyObject(QObject *p = 0) : QObject(p), lastEvent(0) {} + + bool event(QEvent *e) { + lastEvent = e; + return true; + } + + QEvent *lastEvent; +}; + +void tst_QDirectPainter::reservedSynchronous() +{ + MyObject o; + QCoreApplication::postEvent(&o, new QEvent(QEvent::None)); + QDirectPainter p(0, QDirectPainter::ReservedSynchronous); + p.setRegion(QRect(5, 5, 50, 50)); + + // The event loop should not have been executed + QVERIFY(o.lastEvent == 0); + QCOMPARE(p.allocatedRegion(), QRegion(QRect(5, 5, 50, 50))); +} + +QTEST_MAIN(tst_QDirectPainter) + +#include "tst_qdirectpainter.moc" diff --git a/tests/auto/integrationtests/qfocusevent/.gitignore b/tests/auto/integrationtests/qfocusevent/.gitignore new file mode 100644 index 0000000000..f189d8aeb9 --- /dev/null +++ b/tests/auto/integrationtests/qfocusevent/.gitignore @@ -0,0 +1 @@ +tst_qfocusevent diff --git a/tests/auto/integrationtests/qfocusevent/qfocusevent.pro b/tests/auto/integrationtests/qfocusevent/qfocusevent.pro new file mode 100644 index 0000000000..d904a0b0bf --- /dev/null +++ b/tests/auto/integrationtests/qfocusevent/qfocusevent.pro @@ -0,0 +1,3 @@ +load(qttest_p4) +QT += widgets +SOURCES += tst_qfocusevent.cpp diff --git a/tests/auto/integrationtests/qfocusevent/tst_qfocusevent.cpp b/tests/auto/integrationtests/qfocusevent/tst_qfocusevent.cpp new file mode 100644 index 0000000000..931059b211 --- /dev/null +++ b/tests/auto/integrationtests/qfocusevent/tst_qfocusevent.cpp @@ -0,0 +1,395 @@ +/**************************************************************************** +** +** 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 <qapplication.h> +#include <qlineedit.h> +#include <qmenu.h> +#include <qlabel.h> +#include <qdialog.h> +#include <qevent.h> +#include <qlineedit.h> +#include <QBoxLayout> + +QT_FORWARD_DECLARE_CLASS(QWidget) + +//TESTED_CLASS= +//TESTED_FILES=gui/kernel/qevent.h gui/kernel/qevent.cpp + +class FocusLineEdit : public QLineEdit +{ +public: + FocusLineEdit( QWidget* parent = 0, const char* name = 0 ) : QLineEdit(name, parent) {} + int focusInEventReason; + int focusOutEventReason; + bool focusInEventRecieved; + bool focusInEventGotFocus; + bool focusOutEventRecieved; + bool focusOutEventLostFocus; +protected: + virtual void keyPressEvent( QKeyEvent *e ) + { +// qDebug( QString("keyPressEvent: %1").arg(e->key()) ); + QLineEdit::keyPressEvent( e ); + } + void focusInEvent( QFocusEvent* e ) + { + QLineEdit::focusInEvent( e ); + focusInEventReason = e->reason(); + focusInEventGotFocus = e->gotFocus(); + focusInEventRecieved = TRUE; + } + void focusOutEvent( QFocusEvent* e ) + { + QLineEdit::focusOutEvent( e ); + focusOutEventReason = e->reason(); + focusOutEventLostFocus = !e->gotFocus(); + focusOutEventRecieved = TRUE; + } +}; + +class tst_QFocusEvent : public QObject +{ + Q_OBJECT + +public: + tst_QFocusEvent(); + virtual ~tst_QFocusEvent(); + + + void initWidget(); + +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); +private slots: + void checkReason_Tab(); + void checkReason_ShiftTab(); +#ifndef Q_OS_WIN32 + void checkReason_BackTab(); +#endif + void checkReason_Popup(); + void checkReason_focusWidget(); + void checkReason_Shortcut(); + void checkReason_ActiveWindow(); + +private: + QWidget* testFocusWidget; + FocusLineEdit* childFocusWidgetOne; + FocusLineEdit* childFocusWidgetTwo; +}; + +tst_QFocusEvent::tst_QFocusEvent() +{ +} + +tst_QFocusEvent::~tst_QFocusEvent() +{ + +} + +void tst_QFocusEvent::initTestCase() +{ + testFocusWidget = new QWidget( 0 ); + childFocusWidgetOne = new FocusLineEdit( testFocusWidget ); + childFocusWidgetOne->setGeometry( 10, 10, 180, 20 ); + childFocusWidgetTwo = new FocusLineEdit( testFocusWidget ); + childFocusWidgetTwo->setGeometry( 10, 50, 180, 20 ); + + //qApp->setMainWidget( testFocusWidget ); Qt4 + testFocusWidget->resize( 200,100 ); + testFocusWidget->show(); +// Applications don't get focus when launched from the command line on Mac. +#ifdef Q_WS_MAC + testFocusWidget->raise(); +#endif +} + +void tst_QFocusEvent::cleanupTestCase() +{ + delete testFocusWidget; +} + +void tst_QFocusEvent::init() +{ +} + +void tst_QFocusEvent::cleanup() +{ + childFocusWidgetTwo->setGeometry( 10, 50, 180, 20 ); +} + +void tst_QFocusEvent::initWidget() +{ + // On X11 we have to ensure the event was processed before doing any checking, on Windows + // this is processed straight away. + QApplication::setActiveWindow(childFocusWidgetOne); + + for (int i = 0; i < 1000; ++i) { + if (childFocusWidgetOne->isActiveWindow() && childFocusWidgetOne->hasFocus()) + break; + childFocusWidgetOne->activateWindow(); + childFocusWidgetOne->setFocus(); + qApp->processEvents(); + QTest::qWait(100); + } + + // The first lineedit should have focus + QVERIFY( childFocusWidgetOne->hasFocus() ); + + childFocusWidgetOne->focusInEventRecieved = FALSE; + childFocusWidgetOne->focusInEventGotFocus = FALSE; + childFocusWidgetOne->focusInEventReason = -1; + childFocusWidgetOne->focusOutEventRecieved = FALSE; + childFocusWidgetOne->focusOutEventLostFocus = FALSE; + childFocusWidgetOne->focusOutEventReason = -1; + childFocusWidgetTwo->focusInEventRecieved = FALSE; + childFocusWidgetTwo->focusInEventGotFocus = FALSE; + childFocusWidgetTwo->focusInEventReason = -1; + childFocusWidgetTwo->focusOutEventRecieved = FALSE; + childFocusWidgetTwo->focusOutEventLostFocus = FALSE; + childFocusWidgetTwo->focusOutEventReason = -1; +} + +void tst_QFocusEvent::checkReason_Tab() +{ + initWidget(); + + // Now test the tab key + QTest::keyClick( childFocusWidgetOne, Qt::Key_Tab ); + + QVERIFY(childFocusWidgetOne->focusOutEventRecieved); + QVERIFY(childFocusWidgetTwo->focusInEventRecieved); + QVERIFY(childFocusWidgetOne->focusOutEventLostFocus); + QVERIFY(childFocusWidgetTwo->focusInEventGotFocus); + + QVERIFY( childFocusWidgetTwo->hasFocus() ); + QCOMPARE( childFocusWidgetOne->focusOutEventReason, (int) Qt::TabFocusReason ); + QCOMPARE( childFocusWidgetTwo->focusInEventReason, (int) Qt::TabFocusReason ); +} + +void tst_QFocusEvent::checkReason_ShiftTab() +{ + initWidget(); + + // Now test the shift + tab key + QTest::keyClick( childFocusWidgetOne, Qt::Key_Tab, Qt::ShiftModifier ); + + QVERIFY(childFocusWidgetOne->focusOutEventRecieved); + QVERIFY(childFocusWidgetTwo->focusInEventRecieved); + QVERIFY(childFocusWidgetOne->focusOutEventLostFocus); + QVERIFY(childFocusWidgetTwo->focusInEventGotFocus); + + QVERIFY( childFocusWidgetTwo->hasFocus() ); + QCOMPARE( childFocusWidgetOne->focusOutEventReason, (int)Qt::BacktabFocusReason ); + QCOMPARE( childFocusWidgetTwo->focusInEventReason, (int)Qt::BacktabFocusReason ); + +} + +/*! + In this test we verify that the Qt::KeyBacktab key is handled in a qfocusevent +*/ +// Backtab is not supported on Windows. +#ifndef Q_OS_WIN32 +void tst_QFocusEvent::checkReason_BackTab() +{ + initWidget(); + QVERIFY( childFocusWidgetOne->hasFocus() ); + + // Now test the backtab key + QTest::keyClick( childFocusWidgetOne, Qt::Key_Backtab ); + QTest::qWait(200); + + QTRY_VERIFY(childFocusWidgetOne->focusOutEventRecieved); + QVERIFY(childFocusWidgetTwo->focusInEventRecieved); + QVERIFY(childFocusWidgetOne->focusOutEventLostFocus); + QVERIFY(childFocusWidgetTwo->focusInEventGotFocus); + + QVERIFY( childFocusWidgetTwo->hasFocus() ); + QCOMPARE( childFocusWidgetOne->focusOutEventReason, int(Qt::BacktabFocusReason) ); + QCOMPARE( childFocusWidgetTwo->focusInEventReason, int(Qt::BacktabFocusReason) ); +} +#endif + +void tst_QFocusEvent::checkReason_Popup() +{ + initWidget(); + + // Now test the popup reason + QMenu* popupMenu = new QMenu( testFocusWidget ); + popupMenu->addMenu( "Test" ); + popupMenu->popup( QPoint(0,0) ); + QTest::qWait(50); + + QTRY_VERIFY(childFocusWidgetOne->focusOutEventLostFocus); + + QTRY_VERIFY( childFocusWidgetOne->hasFocus() ); + QVERIFY( !childFocusWidgetOne->focusInEventRecieved ); + QVERIFY( childFocusWidgetOne->focusOutEventRecieved ); + QVERIFY( !childFocusWidgetTwo->focusInEventRecieved ); + QVERIFY( !childFocusWidgetTwo->focusOutEventRecieved ); + QCOMPARE( childFocusWidgetOne->focusOutEventReason, int(Qt::PopupFocusReason)); + + popupMenu->hide(); + + QVERIFY(childFocusWidgetOne->focusInEventRecieved); + QVERIFY(childFocusWidgetOne->focusInEventGotFocus); + + QVERIFY( childFocusWidgetOne->hasFocus() ); + QVERIFY( childFocusWidgetOne->focusInEventRecieved ); + QVERIFY( childFocusWidgetOne->focusOutEventRecieved ); + QVERIFY( !childFocusWidgetTwo->focusInEventRecieved ); + QVERIFY( !childFocusWidgetTwo->focusOutEventRecieved ); +} + +#ifdef Q_WS_MAC +QT_BEGIN_NAMESPACE + extern void qt_set_sequence_auto_mnemonic(bool); +QT_END_NAMESPACE +#endif + +void tst_QFocusEvent::checkReason_Shortcut() +{ + initWidget(); +#ifdef Q_WS_MAC + qt_set_sequence_auto_mnemonic(true); +#endif + QLabel* label = new QLabel( "&Test", testFocusWidget ); + label->setBuddy( childFocusWidgetTwo ); + label->setGeometry( 10, 50, 90, 20 ); + childFocusWidgetTwo->setGeometry( 105, 50, 95, 20 ); + label->show(); + + QVERIFY( childFocusWidgetOne->hasFocus() ); + QVERIFY( !childFocusWidgetTwo->hasFocus() ); + + QTest::keyClick( label, Qt::Key_T, Qt::AltModifier ); + + QVERIFY(childFocusWidgetOne->focusOutEventRecieved); + QVERIFY(childFocusWidgetTwo->focusInEventRecieved); + QVERIFY(childFocusWidgetOne->focusOutEventLostFocus); + QVERIFY(childFocusWidgetTwo->focusInEventGotFocus); + + QVERIFY( childFocusWidgetTwo->hasFocus() ); + QVERIFY( !childFocusWidgetOne->focusInEventRecieved ); + QVERIFY( childFocusWidgetOne->focusOutEventRecieved ); + QCOMPARE( childFocusWidgetOne->focusOutEventReason, (int)Qt::ShortcutFocusReason ); + QVERIFY( childFocusWidgetTwo->focusInEventRecieved ); + QCOMPARE( childFocusWidgetTwo->focusInEventReason, (int)Qt::ShortcutFocusReason ); + QVERIFY( !childFocusWidgetTwo->focusOutEventRecieved ); + + label->hide(); + QVERIFY( childFocusWidgetTwo->hasFocus() ); + QVERIFY( !childFocusWidgetOne->hasFocus() ); +#ifdef Q_WS_MAC + qt_set_sequence_auto_mnemonic(false); +#endif +} + +void tst_QFocusEvent::checkReason_focusWidget() +{ + // This test checks that a widget doesn't loose + // its focuswidget just because the focuswidget looses focus. + QWidget window1; + QWidget frame1; + QWidget frame2; + QLineEdit edit1; + QLineEdit edit2; + + QVBoxLayout outerLayout; + outerLayout.addWidget(&frame1); + outerLayout.addWidget(&frame2); + window1.setLayout(&outerLayout); + + QVBoxLayout leftLayout; + QVBoxLayout rightLayout; + leftLayout.addWidget(&edit1); + rightLayout.addWidget(&edit2); + frame1.setLayout(&leftLayout); + frame2.setLayout(&rightLayout); + window1.show(); + + edit1.setFocus(); + QTest::qWait(100); + edit2.setFocus(); + + QVERIFY(frame1.focusWidget() != 0); + QVERIFY(frame2.focusWidget() != 0); +} + +void tst_QFocusEvent::checkReason_ActiveWindow() +{ + initWidget(); + + QDialog* d = new QDialog( testFocusWidget ); + d->show(); + d->activateWindow(); // ### CDE + QApplication::setActiveWindow(d); + QTest::qWaitForWindowShown(d); + + QTRY_VERIFY(childFocusWidgetOne->focusOutEventRecieved); + QVERIFY(childFocusWidgetOne->focusOutEventLostFocus); + + QVERIFY( !childFocusWidgetOne->focusInEventRecieved ); + QVERIFY( childFocusWidgetOne->focusOutEventRecieved ); + QCOMPARE( childFocusWidgetOne->focusOutEventReason, (int)Qt::ActiveWindowFocusReason); + QVERIFY( !childFocusWidgetOne->hasFocus() ); + + d->hide(); + QTest::qWait(100); + +#if defined(Q_OS_IRIX) + QEXPECT_FAIL("", "IRIX requires explicit activateWindow(), so this test does not make any sense.", Abort); +#endif + QTRY_VERIFY(childFocusWidgetOne->focusInEventRecieved); + QVERIFY(childFocusWidgetOne->focusInEventGotFocus); + + QVERIFY( childFocusWidgetOne->hasFocus() ); + QVERIFY( childFocusWidgetOne->focusInEventRecieved ); + QCOMPARE( childFocusWidgetOne->focusInEventReason, (int)Qt::ActiveWindowFocusReason); +} + + +QTEST_MAIN(tst_QFocusEvent) +#include "tst_qfocusevent.moc" diff --git a/tests/auto/integrationtests/qmultiscreen/.gitignore b/tests/auto/integrationtests/qmultiscreen/.gitignore new file mode 100644 index 0000000000..82e8d449ad --- /dev/null +++ b/tests/auto/integrationtests/qmultiscreen/.gitignore @@ -0,0 +1 @@ +tst_qmultiscreen diff --git a/tests/auto/integrationtests/qmultiscreen/qmultiscreen.pro b/tests/auto/integrationtests/qmultiscreen/qmultiscreen.pro new file mode 100644 index 0000000000..c4a983c5be --- /dev/null +++ b/tests/auto/integrationtests/qmultiscreen/qmultiscreen.pro @@ -0,0 +1,6 @@ +load(qttest_p4) +QT += widgets +SOURCES += tst_qmultiscreen.cpp + +requires(embedded) + diff --git a/tests/auto/integrationtests/qmultiscreen/tst_qmultiscreen.cpp b/tests/auto/integrationtests/qmultiscreen/tst_qmultiscreen.cpp new file mode 100644 index 0000000000..9d74d59f85 --- /dev/null +++ b/tests/auto/integrationtests/qmultiscreen/tst_qmultiscreen.cpp @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** 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> + +//TESTED_CLASS= +//TESTED_FILES= + +#include <qdesktopwidget.h> +#include <qscreen_qws.h> +#include <qscreendriverfactory_qws.h> +#include <qlabel.h> + +class tst_QMultiScreen : public QObject +{ + Q_OBJECT + +public: + tst_QMultiScreen() : screen(0), oldScreen(0) {} + ~tst_QMultiScreen() {} + +private slots: + void initTestCase(); + void cleanupTestCase(); + void widgetSetFixedSize(); + void grabWindow(); + +private: + QScreen *screen; + QScreen *oldScreen; +}; + +void tst_QMultiScreen::cleanupTestCase() +{ + screen->shutdownDevice(); + screen->disconnect(); + delete screen; + screen = 0; + + qt_screen = oldScreen; +} + +void tst_QMultiScreen::initTestCase() +{ + oldScreen = qt_screen; + + QVERIFY(QScreenDriverFactory::keys().contains(QLatin1String("Multi"))); + QVERIFY(QScreenDriverFactory::keys().contains(QLatin1String("VNC"))); + + const int id = 10; + screen = QScreenDriverFactory::create("Multi", id); + QVERIFY(screen); + QVERIFY(screen->connect(QString("Multi: " + "VNC:size=640x480:depth=32:offset=0,0:%1 " + "VNC:size=640x480:depth=32:offset=640,0:%2 " + "VNC:size=640x480:depth=16:offset=0,480:%3 " + ":%4") + .arg(id+1).arg(id+2).arg(id+3).arg(id))); + QVERIFY(screen->initDevice()); + + QDesktopWidget desktop; + QCOMPARE(desktop.numScreens(), 3); +} + +void tst_QMultiScreen::widgetSetFixedSize() +{ + QDesktopWidget desktop; + QRect maxRect; + for (int i = 0; i < desktop.numScreens(); ++i) + maxRect |= desktop.availableGeometry(i); + + maxRect = maxRect.adjusted(50, 50, -50, -50); + + // make sure we can set a size larger than a single screen (task 166368) + QWidget w; + w.setFixedSize(maxRect.size()); + w.show(); + QApplication::processEvents(); + QCOMPARE(w.geometry().size(), maxRect.size()); +} + +void tst_QMultiScreen::grabWindow() +{ + QDesktopWidget desktop; + + QVERIFY(desktop.numScreens() >= 2); + + const QRect r0 = desktop.availableGeometry(0).adjusted(50, 50, -50, -50); + const QRect r1 = desktop.availableGeometry(1).adjusted(60, 60, -60, -60); + + QWidget w; + w.setGeometry(r0); + w.show(); + + QLabel l("hi there"); + l.setGeometry(r1); + l.show(); + + QApplication::processEvents(); + QApplication::sendPostedEvents(); // workaround for glib event loop + QVERIFY(desktop.screenNumber(&w) == 0); + QVERIFY(desktop.screenNumber(&l) == 1); + + const QPixmap p0 = QPixmap::grabWindow(w.winId()); + const QPixmap p1 = QPixmap::grabWindow(l.winId()); + +// p0.save("w.png", "PNG"); +// p1.save("l.png", "PNG"); + QCOMPARE(p0.size(), w.size()); + QCOMPARE(p1.size(), l.size()); + + const QImage img0 = p0.toImage(); + const QImage img1 = p1.toImage(); + +// QPixmap::grabWidget(&w).toImage().convertToFormat(img0.format()).save("w_img.png", "PNG"); +// QPixmap::grabWidget(&l).toImage().convertToFormat(img1.format()).save("l_img.png", "PNG"); + + QImage::Format format = QImage::Format_RGB16; + QCOMPARE(img0.convertToFormat(format), + QPixmap::grabWidget(&w).toImage().convertToFormat(format)); + QCOMPARE(img1.convertToFormat(format), + QPixmap::grabWidget(&l).toImage().convertToFormat(format)); +} + +QTEST_MAIN(tst_QMultiScreen) + +#include "tst_qmultiscreen.moc" diff --git a/tests/auto/integrationtests/qnetworkaccessmanager_and_qprogressdialog/qnetworkaccessmanager_and_qprogressdialog.pro b/tests/auto/integrationtests/qnetworkaccessmanager_and_qprogressdialog/qnetworkaccessmanager_and_qprogressdialog.pro new file mode 100644 index 0000000000..9e3fcf28ba --- /dev/null +++ b/tests/auto/integrationtests/qnetworkaccessmanager_and_qprogressdialog/qnetworkaccessmanager_and_qprogressdialog.pro @@ -0,0 +1,3 @@ +load(qttest_p4) +SOURCES += tst_qnetworkaccessmanager_and_qprogressdialog.cpp +QT += network widgets diff --git a/tests/auto/integrationtests/qnetworkaccessmanager_and_qprogressdialog/tst_qnetworkaccessmanager_and_qprogressdialog.cpp b/tests/auto/integrationtests/qnetworkaccessmanager_and_qprogressdialog/tst_qnetworkaccessmanager_and_qprogressdialog.cpp new file mode 100644 index 0000000000..abf5aa528c --- /dev/null +++ b/tests/auto/integrationtests/qnetworkaccessmanager_and_qprogressdialog/tst_qnetworkaccessmanager_and_qprogressdialog.cpp @@ -0,0 +1,155 @@ +/**************************************************************************** +** +** 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 <QtGui> +#include <QtWidgets> +#include <QtCore> +#include <QtNetwork/QNetworkAccessManager> +#include <QtNetwork/QNetworkRequest> +#include <QtNetwork/QNetworkReply> +#include <qdebug.h> + +#include "../../network-settings.h" + + +class tst_QNetworkAccessManager_And_QProgressDialog : public QObject +{ + Q_OBJECT +public: + tst_QNetworkAccessManager_And_QProgressDialog(); +private slots: + void downloadCheck(); + void downloadCheck_data(); +}; + +class DownloadCheckWidget : public QWidget +{ + Q_OBJECT +public: + DownloadCheckWidget(QWidget *parent = 0) : + QWidget(parent), lateReadyRead(true), zeroCopy(false), + progressDlg(this), netmanager(this) + { + progressDlg.setRange(1, 100); + QMetaObject::invokeMethod(this, "go", Qt::QueuedConnection); + } + bool lateReadyRead; + bool zeroCopy; +public slots: + void go() + { + QNetworkRequest request(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/bigfile")); + if (zeroCopy) + request.setAttribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute, 10*1024*1024); + + QNetworkReply *reply = netmanager.get( + QNetworkRequest( + QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/bigfile") + )); + connect(reply, SIGNAL(downloadProgress(qint64, qint64)), + this, SLOT(dataReadProgress(qint64, qint64))); + connect(reply, SIGNAL(readyRead()), + this, SLOT(dataReadyRead())); + connect(reply, SIGNAL(finished()), this, SLOT(finishedFromReply())); + + progressDlg.exec(); + } + void dataReadProgress(qint64 done, qint64 total) + { + QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender()); + Q_UNUSED(reply); + progressDlg.setMaximum(total); + progressDlg.setValue(done); + } + void dataReadyRead() + { + QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender()); + Q_UNUSED(reply); + lateReadyRead = true; + } + void finishedFromReply() + { + QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender()); + lateReadyRead = false; + reply->deleteLater(); + QTestEventLoop::instance().exitLoop(); + } + +private: + QProgressDialog progressDlg; + QNetworkAccessManager netmanager; +}; + + +tst_QNetworkAccessManager_And_QProgressDialog::tst_QNetworkAccessManager_And_QProgressDialog() +{ +} + +void tst_QNetworkAccessManager_And_QProgressDialog::downloadCheck_data() +{ + QTest::addColumn<bool>("useZeroCopy"); + QTest::newRow("with-zeroCopy") << true; + QTest::newRow("without-zeroCopy") << false; +} + +void tst_QNetworkAccessManager_And_QProgressDialog::downloadCheck() +{ + QFETCH(bool, useZeroCopy); + + DownloadCheckWidget widget; + widget.zeroCopy = useZeroCopy; + widget.show(); + // run and exit on finished() + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + // run some more to catch the late readyRead() (or: to not catch it) + QTestEventLoop::instance().enterLoop(1); + QVERIFY(QTestEventLoop::instance().timeout()); + // the following fails when a readyRead() was received after finished() + QVERIFY(!widget.lateReadyRead); +} + + + +QTEST_MAIN(tst_QNetworkAccessManager_And_QProgressDialog) +#include "tst_qnetworkaccessmanager_and_qprogressdialog.moc" diff --git a/tests/auto/integrationtests/qsharedpointer_and_qwidget/qsharedpointer_and_qwidget.pro b/tests/auto/integrationtests/qsharedpointer_and_qwidget/qsharedpointer_and_qwidget.pro new file mode 100644 index 0000000000..c947e7a246 --- /dev/null +++ b/tests/auto/integrationtests/qsharedpointer_and_qwidget/qsharedpointer_and_qwidget.pro @@ -0,0 +1,4 @@ +load(qttest_p4) +QT += widgets +SOURCES += tst_qsharedpointer_and_qwidget.cpp +DEFINES += SRCDIR=\\\"$$PWD/\\\" diff --git a/tests/auto/integrationtests/qsharedpointer_and_qwidget/tst_qsharedpointer_and_qwidget.cpp b/tests/auto/integrationtests/qsharedpointer_and_qwidget/tst_qsharedpointer_and_qwidget.cpp new file mode 100644 index 0000000000..90b71c517c --- /dev/null +++ b/tests/auto/integrationtests/qsharedpointer_and_qwidget/tst_qsharedpointer_and_qwidget.cpp @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** 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 <QtWidgets/QWidget> +#include <QtWidgets/QPushButton> +#include <QtTest/QtTest> + +QT_BEGIN_NAMESPACE +namespace QtSharedPointer { + Q_CORE_EXPORT void internalSafetyCheckCleanCheck(); +} +QT_END_NAMESPACE + +class tst_QSharedPointer_and_QWidget: public QObject +{ + Q_OBJECT +private slots: + void weak_externalDelete(); + void weak_parentDelete(); + void weak_parentDelete_setParent(); + + void strong_weak(); + + void strong_sharedptrDelete(); + +public slots: + void cleanup() { check(); } + +public: + inline void check() + { +#ifdef QT_BUILD_INTERNAL + QtSharedPointer::internalSafetyCheckCleanCheck(); +#endif + } +}; + +void tst_QSharedPointer_and_QWidget::weak_externalDelete() +{ + QWidget *w = new QWidget; + QWeakPointer<QWidget> ptr = w; + + QVERIFY(!ptr.isNull()); + + delete w; + QVERIFY(ptr.isNull()); +} + +void tst_QSharedPointer_and_QWidget::weak_parentDelete() +{ + QWidget *parent = new QWidget; + QWidget *w = new QWidget(parent); + QWeakPointer<QWidget> ptr = w; + + QVERIFY(!ptr.isNull()); + + delete parent; + QVERIFY(ptr.isNull()); +} + +void tst_QSharedPointer_and_QWidget::weak_parentDelete_setParent() +{ + QWidget *parent = new QWidget; + QWidget *w = new QWidget; + QWeakPointer<QWidget> ptr = w; + w->setParent(parent); + + QVERIFY(!ptr.isNull()); + + delete parent; + QVERIFY(ptr.isNull()); +} + +// -- mixed -- + +void tst_QSharedPointer_and_QWidget::strong_weak() +{ + QSharedPointer<QWidget> ptr(new QWidget); + QWeakPointer<QWidget> weak = ptr.data(); + QWeakPointer<QWidget> weak2 = ptr; + + QVERIFY(!weak.isNull()); + QVERIFY(!weak2.isNull()); + + ptr.clear(); // deletes + + QVERIFY(weak.isNull()); + QVERIFY(weak2.isNull()); +} + + +// ---- strong management ---- + +void tst_QSharedPointer_and_QWidget::strong_sharedptrDelete() +{ + QWidget *parent = new QWidget; + QSharedPointer<QWidget> ptr(new QWidget(parent)); + QWeakPointer<QWidget> weak = ptr; + QPointer<QWidget> check = ptr.data(); + + QVERIFY(!check.isNull()); + QVERIFY(!weak.isNull()); + + ptr.clear(); // deletes + + QVERIFY(check.isNull()); + QVERIFY(weak.isNull()); + + delete parent; // mustn't crash +} + +QTEST_MAIN(tst_QSharedPointer_and_QWidget) + +#include "tst_qsharedpointer_and_qwidget.moc" diff --git a/tests/auto/integrationtests/windowsmobile/test/ddhelper.cpp b/tests/auto/integrationtests/windowsmobile/test/ddhelper.cpp new file mode 100644 index 0000000000..55469f5901 --- /dev/null +++ b/tests/auto/integrationtests/windowsmobile/test/ddhelper.cpp @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifdef Q_OS_WINCE_WM + +#include <Ddraw.h> +#include <QDebug> + +static LPDIRECTDRAW g_pDD = NULL; // DirectDraw object +static LPDIRECTDRAWSURFACE g_pDDSSurface = NULL; // DirectDraw primary surface + +static DDSCAPS ddsCaps; +static DDSURFACEDESC ddsSurfaceDesc; +static void *buffer = NULL; + +static int width = 0; +static int height = 0; +static int pitch = 0; +static int bitCount = 0; +static int windowId = 0; + +static bool initialized = false; +static bool locked = false; + +void q_lock() +{ + if (locked) { + qWarning("Direct Painter already locked (QDirectPainter::lock())"); + return; + } + locked = true; + + + memset(&ddsSurfaceDesc, 0, sizeof(ddsSurfaceDesc)); + ddsSurfaceDesc.dwSize = sizeof(ddsSurfaceDesc); + + HRESULT h = g_pDDSSurface->Lock(0, &ddsSurfaceDesc, DDLOCK_WRITEONLY, 0); + if (h != DD_OK) + qDebug() << "GetSurfaceDesc failed!"; + + width = ddsSurfaceDesc.dwWidth; + height = ddsSurfaceDesc.dwHeight; + bitCount = ddsSurfaceDesc.ddpfPixelFormat.dwRGBBitCount; + pitch = ddsSurfaceDesc.lPitch; + buffer = ddsSurfaceDesc.lpSurface; +} + +void q_unlock() +{ + if( !locked) { + qWarning("Direct Painter not locked (QDirectPainter::unlock()"); + return; + } + g_pDDSSurface->Unlock(0); + locked = false; +} + +void q_initDD() +{ + if (initialized) + return; + + DirectDrawCreate(NULL, &g_pDD, NULL); + + HRESULT h; + h = g_pDD->SetCooperativeLevel(0, DDSCL_NORMAL); + + if (h != DD_OK) + qDebug() << "cooperation level failed"; + + h = g_pDD->TestCooperativeLevel(); + if (h != DD_OK) + qDebug() << "cooperation level failed test"; + + DDSURFACEDESC ddsd; + memset(&ddsd, 0, sizeof(ddsd)); + ddsd.dwSize = sizeof(ddsd); + + ddsd.dwFlags = DDSD_CAPS; + + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + + h = g_pDD->CreateSurface(&ddsd, &g_pDDSSurface, NULL); + + if (h != DD_OK) + qDebug() << "CreateSurface failed!"; + + if (g_pDDSSurface->GetCaps(&ddsCaps) != DD_OK) + qDebug() << "GetCaps failed"; + + q_lock(); + q_unlock(); + initialized = true; +} + +uchar* q_frameBuffer() +{ + return (uchar*) buffer; +} + +int q_screenDepth() +{ + return bitCount; +} + +int q_screenWidth() +{ + return width; +} + +int q_screenHeight() +{ + return height; +} + +int q_linestep() +{ + return pitch; +} + +#endif //Q_OS_WINCE_WM + + diff --git a/tests/auto/integrationtests/windowsmobile/test/ddhelper.h b/tests/auto/integrationtests/windowsmobile/test/ddhelper.h new file mode 100644 index 0000000000..3d2f06aa36 --- /dev/null +++ b/tests/auto/integrationtests/windowsmobile/test/ddhelper.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef __DDHELPER__ +#define __DDHELPER__ + +extern uchar* q_frameBuffer(); + +extern int q_screenDepth(); + +extern int q_screenWidth(); + +extern int q_screenHeight(); + +extern int q_linestep(); + +extern void q_initDD(); + +extern void q_unlock(); + +extern void q_lock(); + +#endif //__DDHELPER__ + diff --git a/tests/auto/integrationtests/windowsmobile/test/test.pro b/tests/auto/integrationtests/windowsmobile/test/test.pro new file mode 100644 index 0000000000..595a619d2a --- /dev/null +++ b/tests/auto/integrationtests/windowsmobile/test/test.pro @@ -0,0 +1,23 @@ + +load(qttest_p4) +QT += widgets +HEADERS += ddhelper.h +SOURCES += tst_windowsmobile.cpp ddhelper.cpp +RESOURCES += windowsmobile.qrc + +TARGET = ../tst_windowsmobile + +wincewm*: { + addFiles.files = $$OUT_PWD/../testQMenuBar/*.exe + + + addFiles.path = "\\Program Files\\tst_windowsmobile" + DEPLOYMENT += addFiles +} + +wincewm*: { + LIBS += Ddraw.lib +} + + + diff --git a/tests/auto/integrationtests/windowsmobile/test/testQMenuBar_current.png b/tests/auto/integrationtests/windowsmobile/test/testQMenuBar_current.png Binary files differnew file mode 100644 index 0000000000..f0042b8fe0 --- /dev/null +++ b/tests/auto/integrationtests/windowsmobile/test/testQMenuBar_current.png diff --git a/tests/auto/integrationtests/windowsmobile/test/testSimpleWidget_current.png b/tests/auto/integrationtests/windowsmobile/test/testSimpleWidget_current.png Binary files differnew file mode 100644 index 0000000000..8086c41ce9 --- /dev/null +++ b/tests/auto/integrationtests/windowsmobile/test/testSimpleWidget_current.png diff --git a/tests/auto/integrationtests/windowsmobile/test/tst_windowsmobile.cpp b/tests/auto/integrationtests/windowsmobile/test/tst_windowsmobile.cpp new file mode 100644 index 0000000000..0dc7f578c1 --- /dev/null +++ b/tests/auto/integrationtests/windowsmobile/test/tst_windowsmobile.cpp @@ -0,0 +1,208 @@ +/**************************************************************************** +** +** 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 <QtCore/QDate> +#include <QtCore/QDebug> +#include <QtCore/QObject> +#include <QtGui> +#ifdef Q_OS_WINCE_WM +#include <windows.h> +#include "ddhelper.h" +#endif + + + +class tst_WindowsMobile : public QObject +{ + Q_OBJECT +public: + tst_WindowsMobile() + { + qApp->setCursorFlashTime (24 * 3600 * 1000); // once a day + // qApp->setCursorFlashTime (INT_MAX); +#ifdef Q_OS_WINCE_WM + q_initDD(); +#endif + } + +#if defined(Q_OS_WINCE_WM) && defined(_WIN32_WCE) && _WIN32_WCE <= 0x501 + private slots: + void testMainWindowAndMenuBar(); + void testSimpleWidget(); +#endif +}; + +#if defined(Q_OS_WINCE_WM) && defined(_WIN32_WCE) && _WIN32_WCE <= 0x501 + +bool qt_wince_is_platform(const QString &platformString) { + wchar_t tszPlatform[64]; + if (SystemParametersInfo(SPI_GETPLATFORMTYPE, + sizeof(tszPlatform)/sizeof(*tszPlatform),tszPlatform,0)) + if (0 == _tcsicmp(reinterpret_cast<const wchar_t *> (platformString.utf16()), tszPlatform)) + return true; + return false; +} + +bool qt_wince_is_smartphone() { + return qt_wince_is_platform(QString::fromLatin1("Smartphone")); +} + +void openMenu() +{ + ::mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_ABSOLUTE,450,630,0,0); + ::mouse_event(MOUSEEVENTF_LEFTUP | MOUSEEVENTF_ABSOLUTE,450,630,0,0); + QTest::qWait(2000); + ::mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_ABSOLUTE,65535,65535,0,0); + ::mouse_event(MOUSEEVENTF_LEFTUP | MOUSEEVENTF_ABSOLUTE,65535,65535,0,0); + QTest::qWait(2000); + ::mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_ABSOLUTE,55535,55535,0,0); + ::mouse_event(MOUSEEVENTF_LEFTUP | MOUSEEVENTF_ABSOLUTE,55535,55535,0,0); + QTest::qWait(2000); + ::mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_ABSOLUTE,55535,58535,0,0); + ::mouse_event(MOUSEEVENTF_LEFTUP | MOUSEEVENTF_ABSOLUTE,55535,58535,0,0); + QTest::qWait(2000); + ::mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_ABSOLUTE,40535,55535,0,0); + ::mouse_event(MOUSEEVENTF_LEFTUP | MOUSEEVENTF_ABSOLUTE,40535,55535,0,0); + QTest::qWait(2000); + ::mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_ABSOLUTE,32535,55535,0,0); + ::mouse_event(MOUSEEVENTF_LEFTUP | MOUSEEVENTF_ABSOLUTE,32535,55535,0,0); + QTest::qWait(2000); + ::mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_ABSOLUTE,65535,65535,0,0); + ::mouse_event(MOUSEEVENTF_LEFTUP | MOUSEEVENTF_ABSOLUTE,65535,65535,0,0); + QTest::qWait(2000); + ::mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_ABSOLUTE,55535,50535,0,0); + ::mouse_event(MOUSEEVENTF_LEFTUP | MOUSEEVENTF_ABSOLUTE,55535,50535,0,0); + QTest::qWait(2000); + ::mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_ABSOLUTE,55535,40535,0,0); + ::mouse_event(MOUSEEVENTF_LEFTUP | MOUSEEVENTF_ABSOLUTE,55535,40535,0,0); + QTest::qWait(2000); + ::mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_ABSOLUTE,48535,45535,0,0); + QTest::qWait(2000); + ::mouse_event(MOUSEEVENTF_LEFTUP | MOUSEEVENTF_ABSOLUTE,48535,45535,0,0); +} + +void compareScreenshots(const QString &image1, const QString &image2) +{ + QImage screenShot(image1); + QImage original(image2); + + // cut away the title bar before comparing + QDesktopWidget desktop; + QRect desktopFrameRect = desktop.frameGeometry(); + QRect desktopClientRect = desktop.availableGeometry(); + + QPainter p1(&screenShot); + QPainter p2(&original); + + //screenShot.save("scr1.png", "PNG"); + p1.fillRect(0, 0, desktopFrameRect.width(), desktopClientRect.y(), Qt::black); + p2.fillRect(0, 0, desktopFrameRect.width(), desktopClientRect.y(), Qt::black); + + //screenShot.save("scr2.png", "PNG"); + //original.save("orig1.png", "PNG"); + + QVERIFY(original == screenShot); +} + +void takeScreenShot(const QString filename) +{ + q_lock(); + QImage image = QImage(( uchar *) q_frameBuffer(), q_screenWidth(), + q_screenHeight(), q_screenWidth() * q_screenDepth() / 8, QImage::Format_RGB16); + image.save(filename, "PNG"); + q_unlock(); +} + +void tst_WindowsMobile::testMainWindowAndMenuBar() +{ + if (qt_wince_is_smartphone()) + QSKIP("This test is only for Windows Mobile", SkipAll); + + QProcess process; + process.start("testQMenuBar.exe"); + QCOMPARE(process.state(), QProcess::Running); + QTest::qWait(6000); + openMenu(); + QTest::qWait(1000); + takeScreenShot("testQMenuBar_current.png"); + process.close(); + compareScreenshots("testQMenuBar_current.png", ":/testQMenuBar_current.png"); +} + +void tst_WindowsMobile::testSimpleWidget() +{ + if (qt_wince_is_smartphone()) + QSKIP("This test is only for Windows Mobile", SkipAll); + + QMenuBar menubar; + menubar.show(); + QWidget maximized; + QPalette pal = maximized.palette(); + pal.setColor(QPalette::Background, Qt::red); + maximized.setPalette(pal); + maximized.showMaximized(); + QWidget widget; + widget.setGeometry(100, 100, 200, 200); + widget.setWindowTitle("Widget"); + widget.show(); + qApp->processEvents(); + QTest::qWait(1000); + + QWidget widget2; + widget2.setGeometry(100, 380, 300, 200); + widget2.setWindowTitle("Widget 2"); + widget2.setWindowFlags(Qt::Popup); + widget2.show(); + + qApp->processEvents(); + QTest::qWait(1000); + takeScreenShot("testSimpleWidget_current.png"); + compareScreenshots("testSimpleWidget_current.png", ":/testSimpleWidget_current.png"); +} + + +#endif //Q_OS_WINCE_WM + + +QTEST_MAIN(tst_WindowsMobile) +#include "tst_windowsmobile.moc" + diff --git a/tests/auto/integrationtests/windowsmobile/test/windowsmobile.qrc b/tests/auto/integrationtests/windowsmobile/test/windowsmobile.qrc new file mode 100644 index 0000000000..5d6f61459e --- /dev/null +++ b/tests/auto/integrationtests/windowsmobile/test/windowsmobile.qrc @@ -0,0 +1,6 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>testQMenuBar_current.png</file> + <file>testSimpleWidget_current.png</file> +</qresource> +</RCC> diff --git a/tests/auto/integrationtests/windowsmobile/testQMenuBar/main.cpp b/tests/auto/integrationtests/windowsmobile/testQMenuBar/main.cpp new file mode 100644 index 0000000000..c2099a648b --- /dev/null +++ b/tests/auto/integrationtests/windowsmobile/testQMenuBar/main.cpp @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** 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 <QtCore/QDate> +#include <QtCore/QDebug> +#include <QtCore/QObject> +#include <QtGui> +#include <windows.h> + +int main(int argc, char * argv[]) +{ + QList<QWidget*> widgets; + QApplication app(argc, argv); + + QMainWindow mainWindow; + mainWindow.setWindowTitle("Test"); + QMenu *fileMenu = mainWindow.menuBar()->addMenu("File"); + QMenu *editMenu = mainWindow.menuBar()->addMenu("Edit"); + QMenu *viewMenu = mainWindow.menuBar()->addMenu("View"); + QMenu *toolsMenu = mainWindow.menuBar()->addMenu("Tools"); + QMenu *optionsMenu = mainWindow.menuBar()->addMenu("Options"); + QMenu *helpMenu = mainWindow.menuBar()->addMenu("Help"); + + qApp->processEvents(); + + fileMenu->addAction("Open"); + QAction *close = fileMenu->addAction("Close"); + fileMenu->addSeparator(); + fileMenu->addAction("Exit"); + + close->setEnabled(false); + + editMenu->addAction("Cut"); + editMenu->addAction("Pase"); + editMenu->addAction("Copy"); + editMenu->addSeparator(); + editMenu->addAction("Find"); + + viewMenu->addAction("Hide"); + viewMenu->addAction("Show"); + viewMenu->addAction("Explore"); + QAction *visible = viewMenu->addAction("Visible"); + visible->setCheckable(true); + visible->setChecked(true); + + toolsMenu->addMenu("Hammer"); + toolsMenu->addMenu("Caliper"); + toolsMenu->addMenu("Helm"); + + optionsMenu->addMenu("Settings"); + optionsMenu->addMenu("Standard"); + optionsMenu->addMenu("Extended"); + + QMenu *subMenu = helpMenu->addMenu("Help"); + subMenu->addAction("Index"); + subMenu->addSeparator(); + subMenu->addAction("Vodoo Help"); + helpMenu->addAction("Contens"); + helpMenu->addSeparator(); + helpMenu->addAction("About"); + + QToolBar toolbar; + mainWindow.addToolBar(&toolbar); + toolbar.addAction(QIcon(qApp->style()->standardPixmap(QStyle::SP_FileIcon)), QString("textAction")); + + QTextEdit textEdit; + mainWindow.setCentralWidget(&textEdit); + + mainWindow.showMaximized(); + + app.exec(); +} diff --git a/tests/auto/integrationtests/windowsmobile/testQMenuBar/testQMenuBar.pro b/tests/auto/integrationtests/windowsmobile/testQMenuBar/testQMenuBar.pro new file mode 100644 index 0000000000..6b84ae937c --- /dev/null +++ b/tests/auto/integrationtests/windowsmobile/testQMenuBar/testQMenuBar.pro @@ -0,0 +1,4 @@ +SOURCES += main.cpp +QT += widgets +TARGET = ../testQMenuBar + diff --git a/tests/auto/integrationtests/windowsmobile/windowsmobile.pro b/tests/auto/integrationtests/windowsmobile/windowsmobile.pro new file mode 100644 index 0000000000..2e6b444d7b --- /dev/null +++ b/tests/auto/integrationtests/windowsmobile/windowsmobile.pro @@ -0,0 +1,9 @@ + +TEMPLATE = subdirs + +wincewm* { + SUBDIRS = testQMenuBar +} + SUBDIRS += test + + |