diff options
Diffstat (limited to 'tests/auto/widgets/util')
18 files changed, 6005 insertions, 0 deletions
diff --git a/tests/auto/widgets/util/qcompleter/.gitignore b/tests/auto/widgets/util/qcompleter/.gitignore new file mode 100644 index 0000000000..4db1c0d5c2 --- /dev/null +++ b/tests/auto/widgets/util/qcompleter/.gitignore @@ -0,0 +1 @@ +tst_qcompleter diff --git a/tests/auto/widgets/util/qcompleter/qcompleter.pro b/tests/auto/widgets/util/qcompleter/qcompleter.pro new file mode 100644 index 0000000000..4d58e6b813 --- /dev/null +++ b/tests/auto/widgets/util/qcompleter/qcompleter.pro @@ -0,0 +1,11 @@ +load(qttest_p4) +TEMPLATE = app +TARGET = tst_qcompleter +QT += widgets +DEPENDPATH += . +INCLUDEPATH += . .. + +# Input +SOURCES += tst_qcompleter.cpp + +CONFIG += insignificant_test # QTBUG-21424 diff --git a/tests/auto/widgets/util/qcompleter/tst_qcompleter.cpp b/tests/auto/widgets/util/qcompleter/tst_qcompleter.cpp new file mode 100644 index 0000000000..ccf3c449b3 --- /dev/null +++ b/tests/auto/widgets/util/qcompleter/tst_qcompleter.cpp @@ -0,0 +1,1519 @@ +/**************************************************************************** +** +** 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 <QtDebug> +#include <QPair> +#include <QList> +#include <QPointer> + +#include "../../../../shared/filesystem.h" + +//TESTED_CLASS= +//TESTED_FILES= + +class CsvCompleter : public QCompleter +{ + Q_OBJECT +public: + CsvCompleter(QObject *parent = 0) : QCompleter(parent), csv(true) { } + + QString pathFromIndex(const QModelIndex& sourceIndex) const; + + void setCsvCompletion(bool set) { csv = set; } + +protected: + QStringList splitPath(const QString &path) const { + return csv ? path.split(",") : QCompleter::splitPath(path); + } + +private: + bool csv; +}; + +QString CsvCompleter::pathFromIndex(const QModelIndex& si) const +{ + if (!csv) + return QCompleter::pathFromIndex(si); + + if (!si.isValid()) + return QString(); + + QModelIndex idx = si; + QStringList list; + do { + QString t = model()->data(idx, completionRole()).toString(); + list.prepend(t); + QModelIndex parent = idx.parent(); + idx = parent.sibling(parent.row(), si.column()); + } while (idx.isValid()); + + if (list.count() == 1) + return list[0]; + return list.join(","); +} + +class tst_QCompleter : public QObject +{ + Q_OBJECT +public: + tst_QCompleter(); + ~tst_QCompleter(); + +private slots: + void getSetCheck(); + + void multipleWidgets(); + void focusIn(); + + void csMatchingOnCsSortedModel_data(); + void csMatchingOnCsSortedModel(); + void ciMatchingOnCiSortedModel_data(); + void ciMatchingOnCiSortedModel(); + + void ciMatchingOnCsSortedModel_data(); + void ciMatchingOnCsSortedModel(); + void csMatchingOnCiSortedModel_data(); + void csMatchingOnCiSortedModel(); + + void directoryModel_data(); + void directoryModel(); + void fileSystemModel_data(); + void fileSystemModel(); + + void changingModel_data(); + void changingModel(); + + void sortedEngineRowCount_data(); + void sortedEngineRowCount(); + void unsortedEngineRowCount_data(); + void unsortedEngineRowCount(); + + void currentRow(); + void sortedEngineMapFromSource(); + void unsortedEngineMapFromSource(); + + void historySearch(); + + void modelDeletion(); + void setters(); + + void dynamicSortOrder(); + void disabledItems(); + + // task-specific tests below me + void task178797_activatedOnReturn(); + void task189564_omitNonSelectableItems(); + void task246056_setCompletionPrefix(); + void task250064_lostFocus(); + + void task253125_lineEditCompletion_data(); + void task253125_lineEditCompletion(); + void task247560_keyboardNavigation(); + void QTBUG_14292_filesystem(); + +private: + void filter(bool assync = false); + void testRowCount(); + enum ModelType { + CASE_SENSITIVELY_SORTED_MODEL, + CASE_INSENSITIVELY_SORTED_MODEL, + DIRECTORY_MODEL, + HISTORY_MODEL, + FILESYSTEM_MODEL + }; + void setSourceModel(ModelType); + + CsvCompleter *completer; + QTreeWidget *treeWidget; + const int completionColumn; + const int columnCount; +}; + +tst_QCompleter::tst_QCompleter() : completer(0), completionColumn(0), columnCount(3) +{ + treeWidget = new QTreeWidget; + treeWidget->setColumnCount(columnCount); +} + +tst_QCompleter::~tst_QCompleter() +{ + delete treeWidget; + delete completer; +} + +void tst_QCompleter::setSourceModel(ModelType type) +{ + QString text; + QTreeWidgetItem *parent, *child; + treeWidget->clear(); + switch(type) { + case CASE_SENSITIVELY_SORTED_MODEL: + // Creates a tree model with top level items P0, P1, .., p0, p1,.. + // Each of these items parents have children (for P0 - c0P0, c1P0,...) + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 5; j++) { + parent = new QTreeWidgetItem(treeWidget); + text.sprintf("%c%i", i == 0 ? 'P' : 'p', j); + parent->setText(completionColumn, text); + for (int k = 0; k < 5; k++) { + child = new QTreeWidgetItem(parent); + QString t = QString().sprintf("c%i", k) + text; + child->setText(completionColumn, t); + } + } + } + completer->setModel(treeWidget->model()); + completer->setCompletionColumn(completionColumn); + break; + case CASE_INSENSITIVELY_SORTED_MODEL: + case HISTORY_MODEL: + // Creates a tree model with top level items P0, p0, P1, p1,... + // Each of these items have children c0p0, c1p0,.. + for (int i = 0; i < 5; i++) { + for (int j = 0; j < 2; j++) { + parent = new QTreeWidgetItem(treeWidget); + text.sprintf("%c%i", j == 0 ? 'P' : 'p', i); + parent->setText(completionColumn, text); + for (int k = 0; k < 5; k++) { + child = new QTreeWidgetItem(parent); + QString t = QString().sprintf("c%i", k) + text; + child->setText(completionColumn, t); + } + } + } + completer->setModel(treeWidget->model()); + completer->setCompletionColumn(completionColumn); + if (type == CASE_INSENSITIVELY_SORTED_MODEL) + break; + parent = new QTreeWidgetItem(treeWidget); + parent->setText(completionColumn, QLatin1String("p3,c3p3")); + parent = new QTreeWidgetItem(treeWidget); + parent->setText(completionColumn, QLatin1String("p2,c4p2")); + break; + case DIRECTORY_MODEL: + completer->setCsvCompletion(false); + completer->setModel(new QDirModel(completer)); + completer->setCompletionColumn(0); + break; + case FILESYSTEM_MODEL: + completer->setCsvCompletion(false); + { + QFileSystemModel *m = new QFileSystemModel(completer); + m->setRootPath("/"); + completer->setModel(m); + } + completer->setCompletionColumn(0); + break; + default: + qDebug() << "Invalid type"; + } +} + +void tst_QCompleter::filter(bool assync) +{ + QFETCH(QString, filterText); + QFETCH(QString, step); + QFETCH(QString, completion); + QFETCH(QString, completionText); + + if (filterText.compare("FILTERING_OFF", Qt::CaseInsensitive) == 0) { + completer->setCompletionMode(QCompleter::UnfilteredPopupCompletion); + return; + } + + int times = 0; +retry: + + completer->setCompletionPrefix(filterText); + + for (int i = 0; i < step.length(); i++) { + int row = completer->currentRow(); + switch (step[i].toUpper().toAscii()) { + case 'P': --row; break; + case 'N': ++row; break; + case 'L': row = completer->completionCount() - 1; break; + case 'F': row = 0; break; + default: + QFAIL(qPrintable(QString( + "Problem with 'step' value in test data: %1 (only P, N, L and F are allowed)." + ).arg(step[i]))); + } + completer->setCurrentRow(row); + } + + int r = QString::compare(completer->currentCompletion(), completionText, completer->caseSensitivity()); + if (assync && r && times < 10) { + times++; + QTest::qWait(50*times); + goto retry; + } + QVERIFY(!r); +} + +// Testing get/set functions +void tst_QCompleter::getSetCheck() +{ + QStandardItemModel model(3,3); + QCompleter completer(&model); + + // QString QCompleter::completionPrefix() + // void QCompleter::setCompletionPrefix(QString) + completer.setCompletionPrefix(QString("te")); + QCOMPARE(completer.completionPrefix(), QString("te")); + completer.setCompletionPrefix(QString()); + QCOMPARE(completer.completionPrefix(), QString()); + + // ModelSorting QCompleter::modelSorting() + // void QCompleter::setModelSorting(ModelSorting) + completer.setModelSorting(QCompleter::CaseSensitivelySortedModel); + QCOMPARE(completer.modelSorting(), QCompleter::CaseSensitivelySortedModel); + completer.setModelSorting(QCompleter::CaseInsensitivelySortedModel); + QCOMPARE(completer.modelSorting(), QCompleter::CaseInsensitivelySortedModel); + completer.setModelSorting(QCompleter::UnsortedModel); + QCOMPARE(completer.modelSorting(), QCompleter::UnsortedModel); + + // CompletionMode QCompleter::completionMode() + // void QCompleter::setCompletionMode(CompletionMode) + QCOMPARE(completer.completionMode(), QCompleter::PopupCompletion); // default value + completer.setCompletionMode(QCompleter::UnfilteredPopupCompletion); + QCOMPARE(completer.completionMode(), QCompleter::UnfilteredPopupCompletion); + completer.setCompletionMode(QCompleter::InlineCompletion); + QCOMPARE(completer.completionMode(), QCompleter::InlineCompletion); + + // int QCompleter::completionColumn() + // void QCompleter::setCompletionColumn(int) + completer.setCompletionColumn(2); + QCOMPARE(completer.completionColumn(), 2); + completer.setCompletionColumn(1); + QCOMPARE(completer.completionColumn(), 1); + + // int QCompleter::completionRole() + // void QCompleter::setCompletionRole(int) + QCOMPARE(completer.completionRole(), static_cast<int>(Qt::EditRole)); // default value + completer.setCompletionRole(Qt::DisplayRole); + QCOMPARE(completer.completionRole(), static_cast<int>(Qt::DisplayRole)); + + // int QCompleter::maxVisibleItems() + // void QCompleter::setMaxVisibleItems(int) + QCOMPARE(completer.maxVisibleItems(), 7); // default value + completer.setMaxVisibleItems(10); + QCOMPARE(completer.maxVisibleItems(), 10); + QTest::ignoreMessage(QtWarningMsg, "QCompleter::setMaxVisibleItems: " + "Invalid max visible items (-2147483648) must be >= 0"); + completer.setMaxVisibleItems(INT_MIN); + QCOMPARE(completer.maxVisibleItems(), 10); // Cannot be set to something negative => old value + + // Qt::CaseSensitivity QCompleter::caseSensitivity() + // void QCompleter::setCaseSensitivity(Qt::CaseSensitivity) + QCOMPARE(completer.caseSensitivity(), Qt::CaseSensitive); // default value + completer.setCaseSensitivity(Qt::CaseInsensitive); + QCOMPARE(completer.caseSensitivity(), Qt::CaseInsensitive); + + // bool QCompleter::wrapAround() + // void QCompleter::setWrapAround(bool) + QCOMPARE(completer.wrapAround(), true); // default value + completer.setWrapAround(false); + QCOMPARE(completer.wrapAround(), false); +} + +void tst_QCompleter::csMatchingOnCsSortedModel_data() +{ + delete completer; + completer = new CsvCompleter; + completer->setModelSorting(QCompleter::CaseSensitivelySortedModel); + completer->setCaseSensitivity(Qt::CaseSensitive); + setSourceModel(CASE_SENSITIVELY_SORTED_MODEL); + + QTest::addColumn<QString>("filterText"); + QTest::addColumn<QString>("step"); + QTest::addColumn<QString>("completion"); + QTest::addColumn<QString>("completionText"); + + for (int i = 0; i < 2; i++) { + if (i == 1) + QTest::newRow("FILTERING_OFF") << "FILTERING_OFF" << "" << "" << ""; + + // Plain text filter + QTest::newRow("()") << "" << "" << "P0" << "P0"; + QTest::newRow("()F") << "" << "F" << "P0" << "P0"; + QTest::newRow("()L") << "" << "L" << "p4" << "p4"; + QTest::newRow("()L") << "" << "L" << "p4" << "p4"; + QTest::newRow("()N") << "" << "N" << "P1" << "P1"; + QTest::newRow("(P)") << "P" << "" << "P0" << "P0"; + QTest::newRow("(P)F") << "P" << "" << "P0" << "P0"; + QTest::newRow("(P)L") << "P" << "L" << "P4" << "P4"; + QTest::newRow("(p)") << "p" << "" << "p0" << "p0"; + QTest::newRow("(p)N") << "p" << "N" << "p1" << "p1"; + QTest::newRow("(p)NN") << "p" << "NN" << "p2" << "p2"; + QTest::newRow("(p)NNN") << "p" << "NNN" << "p3" << "p3"; + QTest::newRow("(p)NNNN") << "p" << "NNNN" << "p4" << "p4"; + QTest::newRow("(p1)") << "p1" << "" << "p1" << "p1"; + QTest::newRow("(p11)") << "p11" << "" << "" << ""; + + // Tree filter + QTest::newRow("(P0,)") << "P0," << "" << "c0P0" << "P0,c0P0"; + QTest::newRow("(P0,c)") << "P0,c" << "" << "c0P0" << "P0,c0P0"; + QTest::newRow("(P0,c1)") << "P0,c1" << "" << "c1P0" << "P0,c1P0"; + QTest::newRow("(P0,c3P0)") << "P0,c3P0" << "" << "c3P0" << "P0,c3P0"; + QTest::newRow("(P3,c)F") << "P3,c" << "F" << "c0P3" << "P3,c0P3"; + QTest::newRow("(P3,c)L") << "P3,c" << "L" << "c4P3" << "P3,c4P3"; + QTest::newRow("(P3,c)N") << "P3,c" << "N" << "c1P3" << "P3,c1P3"; + QTest::newRow("(P3,c)NN") << "P3,c" << "NN" << "c2P3" << "P3,c2P3"; + QTest::newRow("(P3,,c)") << "P3,,c" << "" << "" << ""; + QTest::newRow("(P3,c0P3,)") << "P3,c0P3," << "" << "" << ""; + QTest::newRow("(P,)") << "P," << "" << "" << ""; + } +} + +void tst_QCompleter::csMatchingOnCsSortedModel() +{ + filter(); +} + +void tst_QCompleter::ciMatchingOnCiSortedModel_data() +{ + delete completer; + completer = new CsvCompleter; + completer->setModelSorting(QCompleter::CaseInsensitivelySortedModel); + completer->setCaseSensitivity(Qt::CaseInsensitive); + setSourceModel(CASE_INSENSITIVELY_SORTED_MODEL); + + QTest::addColumn<QString>("filterText"); + QTest::addColumn<QString>("step"); + QTest::addColumn<QString>("completion"); + QTest::addColumn<QString>("completionText"); + + for (int i = 0; i < 2; i++) { + if (i == 1) + QTest::newRow("FILTERING_OFF") << "FILTERING_OFF" << "" << "" << ""; + + // Plain text filter + QTest::newRow("()") << "" << "" << "P0" << "P0"; + QTest::newRow("()F") << "" << "F" << "P0" << "P0"; + QTest::newRow("()L") << "" << "L" << "p4" << "p4"; + QTest::newRow("()N") << "" << "N" << "p0" << "p0"; + QTest::newRow("(P)") << "P" << "" << "P0" << "P0"; + QTest::newRow("(P)F") << "P" << "" << "P0" << "P0"; + QTest::newRow("(P)L") << "P" << "L" << "p4" << "p4"; + QTest::newRow("(p)") << "p" << "" << "P0" << "P0"; + QTest::newRow("(p)N") << "p" << "N" << "p0" << "p0"; + QTest::newRow("(p)NN") << "p" << "NN" << "P1" << "P1"; + QTest::newRow("(p)NNN") << "p" << "NNN" << "p1" << "p1"; + QTest::newRow("(p1)") << "p1" << "" << "P1" << "P1"; + QTest::newRow("(p1)N") << "p1" << "N" << "p1" << "p1"; + QTest::newRow("(p11)") << "p11" << "" << "" << ""; + + //// Tree filter + QTest::newRow("(p0,)") << "p0," << "" << "c0P0" << "P0,c0P0"; + QTest::newRow("(p0,c)") << "p0,c" << "" << "c0P0" << "P0,c0P0"; + QTest::newRow("(p0,c1)") << "p0,c1" << "" << "c1P0" << "P0,c1P0"; + QTest::newRow("(p0,c3P0)") << "p0,c3P0" << "" << "c3P0" << "P0,c3P0"; + QTest::newRow("(p3,c)F") << "p3,c" << "F" << "c0P3" << "P3,c0P3"; + QTest::newRow("(p3,c)L") << "p3,c" << "L" << "c4P3" << "P3,c4P3"; + QTest::newRow("(p3,c)N") << "p3,c" << "N" << "c1P3" << "P3,c1P3"; + QTest::newRow("(p3,c)NN") << "p3,c" << "NN" << "c2P3" << "P3,c2P3"; + QTest::newRow("(p3,,c)") << "p3,,c" << "" << "" << ""; + QTest::newRow("(p3,c0P3,)") << "p3,c0P3," << "" << "" << ""; + QTest::newRow("(p,)") << "p," << "" << "" << ""; + } +} + +void tst_QCompleter::ciMatchingOnCiSortedModel() +{ + filter(); +} + +void tst_QCompleter::ciMatchingOnCsSortedModel_data() +{ + delete completer; + completer = new CsvCompleter; + completer->setModelSorting(QCompleter::CaseSensitivelySortedModel); + setSourceModel(CASE_SENSITIVELY_SORTED_MODEL); + completer->setCaseSensitivity(Qt::CaseInsensitive); + + QTest::addColumn<QString>("filterText"); + QTest::addColumn<QString>("step"); + QTest::addColumn<QString>("completion"); + QTest::addColumn<QString>("completionText"); + + for (int i = 0; i < 2; i++) { + if (i == 1) + QTest::newRow("FILTERING_OFF") << "FILTERING_OFF" << "" << "" << ""; + + // Plain text filter + QTest::newRow("()") << "" << "" << "P0" << "P0"; + QTest::newRow("()F") << "" << "F" << "P0" << "P0"; + QTest::newRow("()L") << "" << "L" << "p4" << "p4"; + QTest::newRow("(P)") << "P" << "" << "P0" << "P0"; + QTest::newRow("(P)F") << "P" << "" << "P0" << "P0"; + QTest::newRow("(P)L") << "P" << "L" << "p4" << "p4"; + QTest::newRow("(p)") << "p" << "" << "P0" << "P0"; + QTest::newRow("(p)N") << "p" << "N" << "P1" << "P1"; + QTest::newRow("(p)NN") << "p" << "NN" << "P2" << "P2"; + QTest::newRow("(p)NNN") << "p" << "NNN" << "P3" << "P3"; + QTest::newRow("(p1)") << "p1" << "" << "P1" << "P1"; + QTest::newRow("(p1)N") << "p1" << "N" << "p1" << "p1"; + QTest::newRow("(p11)") << "p11" << "" << "" << ""; + + // Tree filter + QTest::newRow("(p0,)") << "p0," << "" << "c0P0" << "P0,c0P0"; + QTest::newRow("(p0,c)") << "p0,c" << "" << "c0P0" << "P0,c0P0"; + QTest::newRow("(p0,c1)") << "p0,c1" << "" << "c1P0" << "P0,c1P0"; + QTest::newRow("(p0,c3P0)") << "p0,c3P0" << "" << "c3P0" << "P0,c3P0"; + QTest::newRow("(p3,c)F") << "p3,c" << "F" << "c0P3" << "P3,c0P3"; + QTest::newRow("(p3,c)L") << "p3,c" << "L" << "c4P3" << "P3,c4P3"; + QTest::newRow("(p3,c)N") << "p3,c" << "N" << "c1P3" << "P3,c1P3"; + QTest::newRow("(p3,c)NN") << "p3,c" << "NN" << "c2P3" << "P3,c2P3"; + QTest::newRow("(p3,,c)") << "p3,,c" << "" << "" << ""; + QTest::newRow("(p3,c0P3,)") << "p3,c0P3," << "" << "" << ""; + QTest::newRow("(p,)") << "p," << "" << "" << ""; + } +} + +void tst_QCompleter::ciMatchingOnCsSortedModel() +{ + filter(); +} + +void tst_QCompleter::csMatchingOnCiSortedModel_data() +{ + delete completer; + completer = new CsvCompleter; + completer->setModelSorting(QCompleter::CaseInsensitivelySortedModel); + setSourceModel(CASE_INSENSITIVELY_SORTED_MODEL); + completer->setCaseSensitivity(Qt::CaseSensitive); + + QTest::addColumn<QString>("filterText"); + QTest::addColumn<QString>("step"); + QTest::addColumn<QString>("completion"); + QTest::addColumn<QString>("completionText"); + + for (int i = 0; i < 2; i++) { + if (i == 1) + QTest::newRow("FILTERING_OFF") << "FILTERING_OFF" << "" << "" << ""; + + // Plain text filter + QTest::newRow("()") << "" << "" << "P0" << "P0"; + QTest::newRow("()F") << "" << "F" << "P0" << "P0"; + QTest::newRow("()L") << "" << "L" << "p4" << "p4"; + QTest::newRow("()N") << "" << "N" << "p0" << "p0"; + QTest::newRow("(P)") << "P" << "" << "P0" << "P0"; + QTest::newRow("(P)F") << "P" << "" << "P0" << "P0"; + QTest::newRow("(P)L") << "P" << "L" << "P4" << "P4"; + QTest::newRow("(p)") << "p" << "" << "p0" << "p0"; + QTest::newRow("(p)N") << "p" << "N" << "p1" << "p1"; + QTest::newRow("(p)NN") << "p" << "NN" << "p2" << "p2"; + QTest::newRow("(p)NNN") << "p" << "NNN" << "p3" << "p3"; + QTest::newRow("(p1)") << "p1" << "" << "p1" << "p1"; + QTest::newRow("(p11)") << "p11" << "" << "" << ""; + + //// Tree filter + QTest::newRow("(p0,)") << "p0," << "" << "c0p0" << "p0,c0p0"; + QTest::newRow("(p0,c)") << "p0,c" << "" << "c0p0" << "p0,c0p0"; + QTest::newRow("(p0,c1)") << "p0,c1" << "" << "c1p0" << "p0,c1p0"; + QTest::newRow("(p0,c3P0)") << "p0,c3p0" << "" << "c3p0" << "p0,c3p0"; + QTest::newRow("(p3,c)F") << "p3,c" << "F" << "c0p3" << "p3,c0p3"; + QTest::newRow("(p3,c)L") << "p3,c" << "L" << "c4p3" << "p3,c4p3"; + QTest::newRow("(p3,c)N") << "p3,c" << "N" << "c1p3" << "p3,c1p3"; + QTest::newRow("(p3,c)NN") << "p3,c" << "NN" << "c2p3" << "p3,c2p3"; + QTest::newRow("(p3,,c)") << "p3,,c" << "" << "" << ""; + QTest::newRow("(p3,c0P3,)") << "p3,c0P3," << "" << "" << ""; + QTest::newRow("(p,)") << "p," << "" << "" << ""; + + QTest::newRow("FILTERING_OFF") << "FILTERING_OFF" << "" << "" << ""; + } +} + +void tst_QCompleter::csMatchingOnCiSortedModel() +{ + filter(); +} + +void tst_QCompleter::directoryModel_data() +{ + delete completer; + + completer = new CsvCompleter; + completer->setModelSorting(QCompleter::CaseSensitivelySortedModel); + setSourceModel(DIRECTORY_MODEL); + completer->setCaseSensitivity(Qt::CaseInsensitive); + + QTest::addColumn<QString>("filterText"); + QTest::addColumn<QString>("step"); + QTest::addColumn<QString>("completion"); + QTest::addColumn<QString>("completionText"); + + // NOTE: Add tests carefully, ensurely the paths exist on all systems + // Output is the sourceText; currentCompletionText() + + for (int i = 0; i < 2; i++) { + if (i == 1) + QTest::newRow("FILTERING_OFF") << "FILTERING_OFF" << "" << "" << ""; + +#if defined(Q_OS_WINCE) + QTest::newRow("()") << "" << "" << "/" << "/"; + QTest::newRow("()") << "\\Program" << "" << "Program Files" << "\\Program Files"; +#elif defined(Q_OS_WIN) + QTest::newRow("()") << "C" << "" << "C:" << "C:"; + QTest::newRow("()") << "C:\\Program" << "" << "Program Files" << "C:\\Program Files"; +#elif defined (Q_OS_MAC) + QTest::newRow("()") << "" << "" << "/" << "/"; + QTest::newRow("(/a)") << "/a" << "" << "Applications" << "/Applications"; + QTest::newRow("(/d)") << "/d" << "" << "Developer" << "/Developer"; +#else + QTest::newRow("()") << "" << "" << "/" << "/"; +#if !defined(Q_OS_IRIX) && !defined(Q_OS_AIX) && !defined(Q_OS_HPUX) + QTest::newRow("(/h)") << "/h" << "" << "home" << "/home"; +#endif + QTest::newRow("(/et)") << "/et" << "" << "etc" << "/etc"; + QTest::newRow("(/etc/passw)") << "/etc/passw" << "" << "passwd" << "/etc/passwd"; +#endif + } +} + +void tst_QCompleter::directoryModel() +{ + filter(); +} + +void tst_QCompleter::fileSystemModel_data() +{ + delete completer; + completer = new CsvCompleter; + completer->setModelSorting(QCompleter::CaseSensitivelySortedModel); + setSourceModel(FILESYSTEM_MODEL); + completer->setCaseSensitivity(Qt::CaseInsensitive); + + QTest::addColumn<QString>("filterText"); + QTest::addColumn<QString>("step"); + QTest::addColumn<QString>("completion"); + QTest::addColumn<QString>("completionText"); + + // NOTE: Add tests carefully, ensurely the paths exist on all systems + // Output is the sourceText; currentCompletionText() + + for (int i = 0; i < 2; i++) { + if (i == 1) + QTest::newRow("FILTERING_OFF") << "FILTERING_OFF" << "" << "" << ""; + +#if defined(Q_OS_WINCE) + QTest::newRow("()") << "" << "" << "/" << "/"; + QTest::newRow("()") << "\\Program" << "" << "Program Files" << "\\Program Files"; +#elif defined(Q_OS_WIN) + QTest::newRow("()") << "C" << "" << "C:" << "C:"; + QTest::newRow("()") << "C:\\Program" << "" << "Program Files" << "C:\\Program Files"; +#elif defined (Q_OS_MAC) + QTest::newRow("()") << "" << "" << "/" << "/"; + QTest::newRow("(/a)") << "/a" << "" << "Applications" << "/Applications"; +// QTest::newRow("(/d)") << "/d" << "" << "Developer" << "/Developer"; +#else + QTest::newRow("()") << "" << "" << "/" << "/"; +#if !defined(Q_OS_IRIX) && !defined(Q_OS_AIX) && !defined(Q_OS_HPUX) + QTest::newRow("(/h)") << "/h" << "" << "home" << "/home"; +#endif + QTest::newRow("(/et)") << "/et" << "" << "etc" << "/etc"; + QTest::newRow("(/etc/passw)") << "/etc/passw" << "" << "passwd" << "/etc/passwd"; +#endif + } +} + +void tst_QCompleter::fileSystemModel() +{ + //QFileSystemModel is assync. + filter(true); +} + + +void tst_QCompleter::changingModel_data() +{ +} + +void tst_QCompleter::changingModel() +{ + for (int i = 0; i < 2; i++) { + delete completer; + completer = new CsvCompleter; + completer->setModelSorting(QCompleter::CaseSensitivelySortedModel); + completer->setCaseSensitivity(Qt::CaseSensitive); + setSourceModel(CASE_SENSITIVELY_SORTED_MODEL); + + if (i == 1) { + completer->setCompletionMode(QCompleter::UnfilteredPopupCompletion); + } + + completer->setCompletionPrefix("p"); + completer->setCurrentRow(completer->completionCount() - 1); + QCOMPARE(completer->currentCompletion(), QString("p4")); + + // Test addition of data + QTreeWidgetItem p5item; + p5item.setText(completionColumn, "p5"); + treeWidget->addTopLevelItem(&p5item); + completer->setCompletionPrefix("p5"); + QCOMPARE(completer->currentCompletion(), QString("p5")); + + // Test removal of data + int p5index = treeWidget->indexOfTopLevelItem(&p5item); + treeWidget->takeTopLevelItem(p5index); + QCOMPARE(completer->currentCompletion(), QString("")); + + // Test clear + treeWidget->clear(); + QCOMPARE(completer->currentIndex(), QModelIndex()); + } +} + +void tst_QCompleter::testRowCount() +{ + QFETCH(QString, filterText); + QFETCH(bool, hasChildren); + QFETCH(int, rowCount); + QFETCH(int, completionCount); + + if (filterText.compare("FILTERING_OFF", Qt::CaseInsensitive) == 0) { + completer->setCompletionMode(QCompleter::UnfilteredPopupCompletion); + return; + } + + completer->setCompletionPrefix(filterText); + const QAbstractItemModel *completionModel = completer->completionModel(); + QCOMPARE(completionModel->rowCount(), rowCount); + QCOMPARE(completionCount, completionCount); + QCOMPARE(completionModel->hasChildren(), hasChildren); + QCOMPARE(completionModel->columnCount(), columnCount); +} + +void tst_QCompleter::sortedEngineRowCount_data() +{ + delete completer; + completer = new CsvCompleter; + completer->setModelSorting(QCompleter::CaseInsensitivelySortedModel); + completer->setCaseSensitivity(Qt::CaseInsensitive); + setSourceModel(CASE_INSENSITIVELY_SORTED_MODEL); + + QTest::addColumn<QString>("filterText"); + QTest::addColumn<bool>("hasChildren"); + QTest::addColumn<int>("rowCount"); + QTest::addColumn<int>("completionCount"); + + QTest::newRow("whatever") << "whatever" << false << 0 << 0; + QTest::newRow("p") << "p" << true << 10 << 10; + QTest::newRow("p1") << "p1" << true << 2 << 2; + QTest::newRow("P1,") << "P1," << true << 5 << 5; + QTest::newRow("P1,c") << "P1,c" << true << 5 << 5; + QTest::newRow("P1,cc") << "P1,cc" << false << 0 << 0; + + QTest::newRow("FILTERING_OFF") << "FILTERING_OFF" << false << 0 << 0; + + QTest::newRow("whatever(filter off)") << "whatever" << true << 10 << 0; + QTest::newRow("p1(filter off)") << "p1" << true << 10 << 2; + QTest::newRow("p1,(filter off)") << "p1," << true << 5 << 5; + QTest::newRow("p1,c(filter off)") << "p1,c" << true << 5 << 5; + QTest::newRow("P1,cc(filter off)") << "P1,cc" << true << 5 << 0; +} + +void tst_QCompleter::sortedEngineRowCount() +{ + testRowCount(); +} + +void tst_QCompleter::unsortedEngineRowCount_data() +{ + delete completer; + completer = new CsvCompleter; + completer->setModelSorting(QCompleter::CaseInsensitivelySortedModel); + completer->setCaseSensitivity(Qt::CaseSensitive); + setSourceModel(CASE_INSENSITIVELY_SORTED_MODEL); + + QTest::addColumn<QString>("filterText"); + QTest::addColumn<bool>("hasChildren"); + QTest::addColumn<int>("rowCount"); + QTest::addColumn<int>("completionCount"); + + QTest::newRow("whatever") << "whatever" << false << 0 << 0; + QTest::newRow("p") << "p" << true << 5 << 5; + QTest::newRow("p1") << "p1" << true << 1 << 1; + QTest::newRow("P1,") << "P1," << true << 5 << 5; + QTest::newRow("P1,c") << "P1,c" << true << 5 << 5; + QTest::newRow("P1,cc") << "P1,cc" << false << 0 << 0; + + QTest::newRow("FILTERING_OFF") << "FILTERING_OFF" << false << 0 << 0; + + QTest::newRow("whatever(filter off)") << "whatever" << true << 10 << 0; + QTest::newRow("p1(filter off)") << "p1" << true << 10 << 1; + QTest::newRow("p1,(filter off)") << "p1," << true << 5 << 5; + QTest::newRow("p1,c(filter off)") << "p1,c" << true << 5 << 5; + QTest::newRow("P1,cc(filter off)") << "P1,cc" << true << 5 << 0; +} + +void tst_QCompleter::unsortedEngineRowCount() +{ + testRowCount(); +} + +void tst_QCompleter::currentRow() +{ + delete completer; + completer = new CsvCompleter; + completer->setModelSorting(QCompleter::CaseInsensitivelySortedModel); + completer->setCaseSensitivity(Qt::CaseInsensitive); + setSourceModel(CASE_INSENSITIVELY_SORTED_MODEL); + + // blank text + completer->setCompletionPrefix(""); + QCOMPARE(completer->currentRow(), 0); + QVERIFY(completer->setCurrentRow(4)); + QCOMPARE(completer->currentRow(), 4); + QVERIFY(!completer->setCurrentRow(13)); + QVERIFY(completer->setCurrentRow(4)); + + // some text + completer->setCompletionPrefix("p1"); + QCOMPARE(completer->currentRow(), 0); + QVERIFY(completer->setCurrentRow(1)); + QCOMPARE(completer->currentRow(), 1); + QVERIFY(!completer->setCurrentRow(2)); + QCOMPARE(completer->currentRow(), 1); + + // invalid text + completer->setCompletionPrefix("well"); + QCOMPARE(completer->currentRow(), -1); +} + +void tst_QCompleter::sortedEngineMapFromSource() +{ + delete completer; + completer = new CsvCompleter; + completer->setModelSorting(QCompleter::CaseInsensitivelySortedModel); + completer->setCaseSensitivity(Qt::CaseInsensitive); + setSourceModel(CASE_INSENSITIVELY_SORTED_MODEL); + + QModelIndex si1, si2, pi; + QAbstractItemModel *sourceModel = completer->model(); + const QAbstractProxyModel *completionModel = + qobject_cast<const QAbstractProxyModel *>(completer->completionModel()); + + // Fitering ON + // empty + si1 = sourceModel->index(4, completionColumn); // "P2" + si2 = sourceModel->index(2, 0, si1); // "P2,c0P2" + pi = completionModel->mapFromSource(si1); + QCOMPARE(completionModel->data(pi).toString(), QLatin1String("P2")); + pi = completionModel->mapFromSource(si2); + QCOMPARE(pi.isValid(), false); + + // some text + completer->setCompletionPrefix("p"); + pi = completionModel->mapFromSource(si1); + QCOMPARE(completionModel->data(pi).toString(), QLatin1String("P2")); + pi = completionModel->mapFromSource(si2); + QCOMPARE(pi.isValid(), false); + + // more text + completer->setCompletionPrefix("p2"); + pi = completionModel->mapFromSource(si1); + QCOMPARE(completionModel->data(pi).toString(), QLatin1String("P2")); + pi = completionModel->mapFromSource(si2); + QCOMPARE(pi.isValid(), false); + + // invalid text + completer->setCompletionPrefix("whatever"); + pi = completionModel->mapFromSource(si1); + QVERIFY(!pi.isValid()); + + // Fitering OFF + completer->setCompletionMode(QCompleter::UnfilteredPopupCompletion); + // empty + si1 = sourceModel->index(4, completionColumn); // "P2" + si2 = sourceModel->index(2, 0, si1); // "P2,c0P2" + pi = completionModel->mapFromSource(si1); + QCOMPARE(completionModel->data(pi).toString(), QLatin1String("P2")); + pi = completionModel->mapFromSource(si2); + QCOMPARE(pi.isValid(), false); + + // some text + completer->setCompletionPrefix("p"); + pi = completionModel->mapFromSource(si1); + QCOMPARE(completionModel->data(pi).toString(), QLatin1String("P2")); + pi = completionModel->mapFromSource(si2); + QCOMPARE(pi.isValid(), false); + + // more text + completer->setCompletionPrefix("p2"); + pi = completionModel->mapFromSource(si1); + QCOMPARE(completionModel->data(pi).toString(), QLatin1String("P2")); + pi = completionModel->mapFromSource(si2); + QCOMPARE(pi.isValid(), false); + + // invalid text + completer->setCompletionPrefix("whatever"); + pi = completionModel->mapFromSource(si1); + QCOMPARE(completionModel->data(pi).toString(), QLatin1String("P2")); +} + +void tst_QCompleter::unsortedEngineMapFromSource() +{ + delete completer; + completer = new CsvCompleter; + completer->setCaseSensitivity(Qt::CaseInsensitive); + setSourceModel(HISTORY_MODEL); // case insensitively sorted model + completer->setModelSorting(QCompleter::UnsortedModel); + + QModelIndex si, si2, si3, pi; + QAbstractItemModel *sourceModel = completer->model(); + const QAbstractProxyModel *completionModel = + qobject_cast<const QAbstractProxyModel *>(completer->completionModel()); + + si = sourceModel->index(6, completionColumn); // "P3" + QCOMPARE(si.data().toString(), QLatin1String("P3")); + si2 = sourceModel->index(3, completionColumn, sourceModel->index(0, completionColumn)); // "P0,c3P0" + QCOMPARE(si2.data().toString(), QLatin1String("c3P0")); + si3 = sourceModel->index(10, completionColumn); // "p3,c3p3" (history) + QCOMPARE(si3.data().toString(), QLatin1String("p3,c3p3")); + + // FILTERING ON + // empty + pi = completionModel->mapFromSource(si); + QCOMPARE(completionModel->data(pi).toString(), QLatin1String("P3")); + pi = completionModel->mapFromSource(si2); + QCOMPARE(pi.isValid(), false); + pi = completionModel->mapFromSource(si3); + QCOMPARE(completionModel->data(pi).toString(), QLatin1String("p3,c3p3")); + + // some text + completer->setCompletionPrefix("P"); + pi = completionModel->mapFromSource(si); + QCOMPARE(completionModel->data(pi).toString(), QLatin1String("P3")); + pi = completionModel->mapFromSource(si2); + QCOMPARE(pi.isValid(), false); + pi = completionModel->mapFromSource(si3); + QCOMPARE(completionModel->data(pi).toString(), QLatin1String("p3,c3p3")); + + // invalid text + completer->setCompletionPrefix("whatever"); + pi = completionModel->mapFromSource(si); + QVERIFY(!pi.isValid()); + pi = completionModel->mapFromSource(si2); + QVERIFY(!pi.isValid()); + + // tree matching + completer->setCompletionPrefix("P0,c"); + pi = completionModel->mapFromSource(si); + QVERIFY(!pi.isValid()); + pi = completionModel->mapFromSource(si2); + QCOMPARE(completionModel->data(pi).toString(), QLatin1String("c3P0")); + pi = completionModel->mapFromSource(si3); + QCOMPARE(pi.isValid(), false); + + // more tree matching + completer->setCompletionPrefix("p3,"); + pi = completionModel->mapFromSource(si2); + QVERIFY(!pi.isValid()); + pi = completionModel->mapFromSource(si3); + QCOMPARE(completionModel->data(pi).toString(), QLatin1String("p3,c3p3")); + + // FILTERING OFF + // empty + completer->setCompletionPrefix(""); + completer->setCompletionMode(QCompleter::UnfilteredPopupCompletion); + pi = completionModel->mapFromSource(si); + QCOMPARE(completionModel->data(pi).toString(), QLatin1String("P3")); + + // some text + completer->setCompletionPrefix("P"); + pi = completionModel->mapFromSource(si); + QCOMPARE(completionModel->data(pi).toString(), QLatin1String("P3")); + + // more text + completer->setCompletionPrefix("P3"); + pi = completionModel->mapFromSource(si); + QCOMPARE(completionModel->data(pi).toString(), QLatin1String("P3")); + + // invalid text + completer->setCompletionPrefix("whatever"); + pi = completionModel->mapFromSource(si); + QCOMPARE(completionModel->data(pi).toString(), QLatin1String("P3")); +} + +void tst_QCompleter::historySearch() +{ + delete completer; + completer = new CsvCompleter; + completer->setModelSorting(QCompleter::CaseInsensitivelySortedModel); + completer->setCaseSensitivity(Qt::CaseSensitive); + setSourceModel(HISTORY_MODEL); + + const QAbstractProxyModel *completionModel = + qobject_cast<const QAbstractProxyModel *>(completer->completionModel()); + + // "p3,c3p3" and "p2,c4p2" are added in the tree root + + // FILTERING ON + // empty + completer->setCurrentRow(10); + QCOMPARE(completer->currentCompletion(), QLatin1String("p3,c3p3")); + + // more text + completer->setCompletionPrefix("p2"); + completer->setCurrentRow(1); + QCOMPARE(completer->currentCompletion(), QLatin1String("p2,c4p2")); + + // comma separated text + completer->setCompletionPrefix("p2,c4"); + completer->setCurrentRow(1); + QCOMPARE(completionModel->rowCount(), 2); + QCOMPARE(completer->currentCompletion(), QLatin1String("p2,c4p2")); + + // invalid text + completer->setCompletionPrefix("whatever"); + QCOMPARE(completer->currentCompletion(), QString()); + + // FILTERING OFF + completer->setCompletionPrefix(""); + completer->setCompletionMode(QCompleter::UnfilteredPopupCompletion); + completer->setCurrentRow(10); + QCOMPARE(completer->currentCompletion(), QLatin1String("p3,c3p3")); + + // more text + completer->setCompletionPrefix("p2"); + completer->setCurrentRow(1); + QCOMPARE(completer->currentCompletion(), QLatin1String("p2,c4p2")); + + // comma separated text + completer->setCompletionPrefix("p2,c4"); + QCOMPARE(completionModel->rowCount(), 5); + + // invalid text + completer->setCompletionPrefix("whatever"); + QCOMPARE(completer->currentCompletion(), QString()); +} + +void tst_QCompleter::setters() +{ + delete completer; + completer = new CsvCompleter; + QVERIFY(completer->popup() != 0); + QPointer<QDirModel> dirModel = new QDirModel(completer); + QAbstractItemModel *oldModel = completer->model(); + completer->setModel(dirModel); + QVERIFY(completer->popup()->model() != oldModel); + QVERIFY(completer->popup()->model() == completer->completionModel()); + completer->setPopup(new QListView); + QVERIFY(completer->popup()->model() == completer->completionModel()); + completer->setModel(new QStringListModel(completer)); + QVERIFY(dirModel == 0); // must have been deleted + + completer->setModel(0); + completer->setWidget(0); +} + +void tst_QCompleter::modelDeletion() +{ + delete completer; + completer = new CsvCompleter; + QStringList list; + list << "item1" << "item2" << "item3"; + QStringListModel *listModel = new QStringListModel(list); + completer->setCompletionPrefix("i"); + completer->setModel(listModel); + QVERIFY(completer->completionCount() == 3); + QListView *view = new QListView; + view->setModel(completer->completionModel()); + delete listModel; + view->show(); + qApp->processEvents(); + delete view; + QVERIFY(completer->completionCount() == 0); + QVERIFY(completer->currentRow() == -1); +} + +void tst_QCompleter::multipleWidgets() +{ + QStringList list; + list << "item1" << "item2" << "item2"; + QCompleter completer(list); + completer.setCompletionMode(QCompleter::InlineCompletion); + + QWidget window; + window.show(); + QApplication::setActiveWindow(&window); + QTest::qWaitForWindowShown(&window); + QTRY_VERIFY(qApp->activeWindow() == &window); + + QFocusEvent focusIn(QEvent::FocusIn); + QFocusEvent focusOut(QEvent::FocusOut); + + QComboBox *comboBox = new QComboBox(&window); + comboBox->setEditable(true); + comboBox->setCompleter(&completer); + comboBox->setFocus(); + comboBox->show(); + window.activateWindow(); + QApplication::setActiveWindow(&window); + QTest::qWait(50); + QTRY_VERIFY(qApp->focusWidget() == comboBox); + comboBox->lineEdit()->setText("it"); + QCOMPARE(comboBox->currentText(), QString("it")); // should not complete with setText + QTest::keyPress(comboBox, 'e'); + QCOMPARE(comboBox->currentText(), QString("item1")); + comboBox->clearEditText(); + QCOMPARE(comboBox->currentText(), QString("")); // combo box text must not change! + + QLineEdit *lineEdit = new QLineEdit(&window); + lineEdit->setCompleter(&completer); + lineEdit->show(); + lineEdit->setFocus(); + QTest::qWait(50); + QTRY_VERIFY(qApp->focusWidget() == lineEdit); + lineEdit->setText("it"); + QCOMPARE(lineEdit->text(), QString("it")); // should not completer with setText + QCOMPARE(comboBox->currentText(), QString("")); // combo box text must not change! + QTest::keyPress(lineEdit, 'e'); + QCOMPARE(lineEdit->text(), QString("item1")); + QCOMPARE(comboBox->currentText(), QString("")); // combo box text must not change! +} + +void tst_QCompleter::focusIn() +{ + QStringList list; + list << "item1" << "item2" << "item2"; + QCompleter completer(list); + + QWidget window; + window.show(); + QTest::qWait(100); + window.activateWindow(); + QApplication::setActiveWindow(&window); + QTest::qWait(100); + + QTRY_COMPARE(qApp->activeWindow(), &window); + + QComboBox *comboBox = new QComboBox(&window); + comboBox->setEditable(true); + comboBox->setCompleter(&completer); + comboBox->show(); + comboBox->lineEdit()->setText("it"); + + QLineEdit *lineEdit = new QLineEdit(&window); + lineEdit->setCompleter(&completer); + lineEdit->setText("it"); + lineEdit->show(); + + QLineEdit *lineEdit2 = new QLineEdit(&window); // has no completer! + lineEdit2->show(); + + comboBox->setFocus(); + QTRY_VERIFY(completer.widget() == comboBox); + lineEdit->setFocus(); + QTRY_VERIFY(completer.widget() == lineEdit); + comboBox->setFocus(); + QTRY_VERIFY(completer.widget() == comboBox); + lineEdit2->setFocus(); + QTRY_VERIFY(completer.widget() == comboBox); +} + +void tst_QCompleter::dynamicSortOrder() +{ + QStandardItemModel model; + QCompleter completer(&model); + completer.setModelSorting(QCompleter::CaseSensitivelySortedModel); + QStandardItem *root = model.invisibleRootItem(); + for (int i = 0; i < 20; i++) { + root->appendRow(new QStandardItem(QString("%1").arg(i))); + } + root->appendRow(new QStandardItem("13")); + root->sortChildren(0, Qt::AscendingOrder); + completer.setCompletionPrefix("1"); + QCOMPARE(completer.completionCount(), 12); + completer.setCompletionPrefix("13"); + QCOMPARE(completer.completionCount(), 2); + + root->sortChildren(0, Qt::DescendingOrder); + completer.setCompletionPrefix("13"); + QCOMPARE(completer.completionCount(), 2); + completer.setCompletionPrefix("1"); + QCOMPARE(completer.completionCount(), 12); +} + +void tst_QCompleter::disabledItems() +{ + QLineEdit lineEdit; + QStandardItemModel *model = new QStandardItemModel(&lineEdit); + QStandardItem *suggestions = new QStandardItem("suggestions"); + suggestions->setEnabled(false); + model->appendRow(suggestions); + model->appendRow(new QStandardItem("suggestions Enabled")); + QCompleter *completer = new QCompleter(model, &lineEdit); + QSignalSpy spy(completer, SIGNAL(activated(const QString &))); + lineEdit.setCompleter(completer); + lineEdit.show(); + + QTest::keyPress(&lineEdit, Qt::Key_S); + QTest::keyPress(&lineEdit, Qt::Key_U); + QAbstractItemView *view = lineEdit.completer()->popup(); + QVERIFY(view->isVisible()); + QTest::mouseClick(view->viewport(), Qt::LeftButton, 0, view->visualRect(view->model()->index(0, 0)).center()); + QCOMPARE(spy.count(), 0); + QVERIFY(view->isVisible()); + QTest::mouseClick(view->viewport(), Qt::LeftButton, 0, view->visualRect(view->model()->index(1, 0)).center()); + QCOMPARE(spy.count(), 1); + QVERIFY(!view->isVisible()); +} + +void tst_QCompleter::task178797_activatedOnReturn() +{ + QStringList words; + words << "foobar1" << "foobar2"; + QLineEdit *ledit = new QLineEdit; + QCompleter *completer = new QCompleter(words); + ledit->setCompleter(completer); + QSignalSpy spy(completer, SIGNAL(activated(const QString))); + QCOMPARE(spy.count(), 0); + ledit->show(); + QTest::keyClick(ledit, Qt::Key_F); + qApp->processEvents(); + QVERIFY(qApp->activePopupWidget()); + QTest::keyClick(qApp->activePopupWidget(), Qt::Key_Down); + qApp->processEvents(); + QTest::keyClick(qApp->activePopupWidget(), Qt::Key_Return); + qApp->processEvents(); + QCOMPARE(spy.count(), 1); +} + +class task189564_StringListModel : public QStringListModel +{ + const QString omitString; + Qt::ItemFlags flags(const QModelIndex &index) const + { + Qt::ItemFlags flags = Qt::ItemIsEnabled; + if (data(index, Qt::DisplayRole).toString() != omitString) + flags |= Qt::ItemIsSelectable; + return flags; + } +public: + task189564_StringListModel(const QString &omitString, QObject *parent = 0) + : QStringListModel(parent) + , omitString(omitString) + { + } +}; + +void tst_QCompleter::task189564_omitNonSelectableItems() +{ + const QString prefix("a"); + const int n = 5; + + QStringList strings; + for (int i = 0; i < n; ++i) + strings << QString("%1%2").arg(prefix).arg(i); + const QString omitString(strings.at(n / 2)); + task189564_StringListModel model(omitString); + model.setStringList(strings); + QCompleter completer_(&model); + completer_.setCompletionPrefix(prefix); + + QAbstractItemModel *completionModel = completer_.completionModel(); + QModelIndexList matches1 = + completionModel->match(completionModel->index(0, 0), Qt::DisplayRole, prefix, -1); + QCOMPARE(matches1.size(), n - 1); + QModelIndexList matches2 = + completionModel->match(completionModel->index(0, 0), Qt::DisplayRole, omitString); + QVERIFY(matches2.isEmpty()); +} + +class task246056_ComboBox : public QComboBox +{ + Q_OBJECT +public: + task246056_ComboBox() + { + setEditable(true); + setInsertPolicy(NoInsert); + if (completer()) { + completer()->setCompletionMode(QCompleter::PopupCompletion); + completer()->setCompletionRole(Qt::DisplayRole); + connect(lineEdit(), SIGNAL(editingFinished()), SLOT(setCompletionPrefix())); + } + } +private slots: + void setCompletionPrefix() { completer()->setCompletionPrefix(lineEdit()->text()); } +}; + +void tst_QCompleter::task246056_setCompletionPrefix() +{ + task246056_ComboBox *comboBox = new task246056_ComboBox; + QVERIFY(comboBox->completer()); + comboBox->addItem(""); + comboBox->addItem("a1"); + comboBox->addItem("a2"); + comboBox->show(); + comboBox->setFocus(); + QTest::qWait(100); + QTest::keyPress(comboBox, 'a'); + QTest::keyPress(comboBox->completer()->popup(), Qt::Key_Down); + QTest::keyPress(comboBox->completer()->popup(), Qt::Key_Down); + QTest::keyPress(comboBox->completer()->popup(), Qt::Key_Enter); // don't crash! +} + +class task250064_TextEdit : public QTextEdit +{ +public: + QCompleter *completer; + + task250064_TextEdit() + { + completer = new QCompleter; + completer->setWidget(this); + } + + void keyPressEvent (QKeyEvent *e) + { + completer->popup(); + QTextEdit::keyPressEvent(e); + } +}; + +class task250064_Widget : public QWidget +{ + Q_OBJECT +public: + task250064_TextEdit *textEdit; + + task250064_Widget(task250064_TextEdit *textEdit) + : textEdit(textEdit) + { + QTabWidget *tabWidget = new QTabWidget; + tabWidget->setFocusPolicy(Qt::ClickFocus); + tabWidget->addTab(textEdit, "untitled"); + + QVBoxLayout *layout = new QVBoxLayout(this); + layout->addWidget(tabWidget); + + textEdit->setPlainText("bla bla bla"); + textEdit->setFocus(); + } + + void setCompletionModel() + { + textEdit->completer->setModel(0); + } +}; + +void tst_QCompleter::task250064_lostFocus() +{ + task250064_TextEdit *textEdit = new task250064_TextEdit; + task250064_Widget *widget = new task250064_Widget(textEdit); + widget->show(); + QTest::qWait(100); + QTest::keyPress(textEdit, 'a'); + Qt::FocusPolicy origPolicy = textEdit->focusPolicy(); + QVERIFY(origPolicy != Qt::NoFocus); + widget->setCompletionModel(); + QCOMPARE(textEdit->focusPolicy(), origPolicy); +} + +void tst_QCompleter::task253125_lineEditCompletion_data() +{ + QTest::addColumn<QStringList>("list"); + QTest::addColumn<int>("completionMode"); + + QStringList list = QStringList() + << "alpha" << "beta" << "gamma" << "delta" << "epsilon" << "zeta" + << "eta" << "theta" << "iota" << "kappa" << "lambda" << "mu" + << "nu" << "xi" << "omicron" << "pi" << "rho" << "sigma" + << "tau" << "upsilon" << "phi" << "chi" << "psi" << "omega"; + + QTest::newRow("Inline") << list << (int)QCompleter::InlineCompletion; + QTest::newRow("Filtered") << list << (int)QCompleter::PopupCompletion; + QTest::newRow("Unfiltered") << list << (int)QCompleter::UnfilteredPopupCompletion; +} + +void tst_QCompleter::task253125_lineEditCompletion() +{ + QFETCH(QStringList, list); + QFETCH(int, completionMode); + + QStringListModel *model = new QStringListModel; + model->setStringList(list); + + QCompleter *completer = new QCompleter(list); + completer->setModel(model); + completer->setCompletionMode((QCompleter::CompletionMode)completionMode); + + QLineEdit edit; + edit.setCompleter(completer); + edit.show(); + edit.setFocus(); + +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&edit); +#endif + QTest::qWait(10); + QApplication::setActiveWindow(&edit); + QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&edit)); + + QTest::keyClick(&edit, 'i'); + QCOMPARE(edit.completer()->currentCompletion(), QString("iota")); + QTest::keyClick(edit.completer()->popup(), Qt::Key_Down); + QTest::keyClick(edit.completer()->popup(), Qt::Key_Enter); + + QCOMPARE(edit.text(), QString("iota")); + + delete completer; + delete model; +} + +void tst_QCompleter::task247560_keyboardNavigation() +{ + QStandardItemModel model; + + for (int i = 0; i < 5; i++) { + for (int j = 0; j < 5; j++) { + model.setItem(i, j, new QStandardItem(QString("row %1 column %2").arg(i).arg(j))); + } + } + + + QCompleter completer(&model); + completer.setCompletionColumn(1); + + QLineEdit edit; + edit.setCompleter(&completer); + edit.show(); + edit.setFocus(); + +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&edit); +#endif + + QTest::qWait(10); + QApplication::setActiveWindow(&edit); + QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&edit)); + + QTest::keyClick(&edit, 'r'); + QTest::keyClick(edit.completer()->popup(), Qt::Key_Down); + QTest::keyClick(edit.completer()->popup(), Qt::Key_Down); + QTest::keyClick(edit.completer()->popup(), Qt::Key_Enter); + + QCOMPARE(edit.text(), QString("row 1 column 1")); + + edit.clear(); + + QTest::keyClick(&edit, 'r'); + QTest::keyClick(edit.completer()->popup(), Qt::Key_Up); + QTest::keyClick(edit.completer()->popup(), Qt::Key_Up); + QTest::keyClick(edit.completer()->popup(), Qt::Key_Enter); + + QCOMPARE(edit.text(), QString("row 3 column 1")); +} + +void tst_QCompleter::QTBUG_14292_filesystem() +{ + FileSystem fs; + QDir tmpDir = QDir::currentPath(); + + qsrand(QTime::currentTime().msec()); + QString d = "tst_QCompleter_" + QString::number(qrand()); + QVERIFY(fs.createDirectory(tmpDir.filePath(d))); + + QVERIFY(tmpDir.cd(d)); + QVERIFY(fs.createDirectory(tmpDir.filePath("hello"))); + QVERIFY(fs.createDirectory(tmpDir.filePath("holla"))); + + QLineEdit edit; + QCompleter comp; + QFileSystemModel model; + model.setRootPath(tmpDir.path()); + comp.setModel(&model); + edit.setCompleter(&comp); + + edit.show(); + QApplication::setActiveWindow(&edit); + QTest::qWaitForWindowShown(&edit); + QTRY_VERIFY(QApplication::activeWindow() == &edit); + edit.setFocus(); + QTRY_VERIFY(edit.hasFocus()); + + QVERIFY(!comp.popup()->isVisible()); + edit.setText(tmpDir.path()); + QTest::keyClick(&edit, '/'); + QTRY_VERIFY(comp.popup()->isVisible()); + QCOMPARE(comp.popup()->model()->rowCount(), 2); + QApplication::processEvents(); + QTest::keyClick(&edit, 'h'); + QCOMPARE(comp.popup()->model()->rowCount(), 2); + QTest::keyClick(&edit, 'e'); + QCOMPARE(comp.popup()->model()->rowCount(), 1); + QTest::keyClick(&edit, 'r'); + QTRY_VERIFY(!comp.popup()->isVisible()); + QVERIFY(fs.createDirectory(tmpDir.filePath("hero"))); + QTRY_VERIFY(comp.popup()->isVisible()); + QCOMPARE(comp.popup()->model()->rowCount(), 1); + QTest::keyClick(comp.popup(), Qt::Key_Escape); + QTRY_VERIFY(!comp.popup()->isVisible()); + QVERIFY(fs.createDirectory(tmpDir.filePath("nothingThere"))); + //there is no reason creating a file should open a popup, it did in Qt 4.7.0 + QTest::qWait(60); + QVERIFY(!comp.popup()->isVisible()); + + QTest::keyClick(&edit, Qt::Key_Backspace); + QTRY_VERIFY(comp.popup()->isVisible()); + QCOMPARE(comp.popup()->model()->rowCount(), 2); + QTest::keyClick(&edit, 'm'); + QTRY_VERIFY(!comp.popup()->isVisible()); + + QWidget w; + w.show(); + QApplication::setActiveWindow(&w); + QTest::qWaitForWindowShown(&w); + QTRY_VERIFY(!edit.hasFocus() && !comp.popup()->hasFocus()); + + QVERIFY(fs.createDirectory(tmpDir.filePath("hemo"))); + //there is no reason creating a file should open a popup, it did in Qt 4.7.0 + QTest::qWait(60); + QVERIFY(!comp.popup()->isVisible()); +} + +QTEST_MAIN(tst_QCompleter) +#include "tst_qcompleter.moc" diff --git a/tests/auto/widgets/util/qscroller/qscroller.pro b/tests/auto/widgets/util/qscroller/qscroller.pro new file mode 100644 index 0000000000..de202d66aa --- /dev/null +++ b/tests/auto/widgets/util/qscroller/qscroller.pro @@ -0,0 +1,4 @@ +load(qttest_p4) + +QT += widgets +SOURCES += tst_qscroller.cpp diff --git a/tests/auto/widgets/util/qscroller/tst_qscroller.cpp b/tests/auto/widgets/util/qscroller/tst_qscroller.cpp new file mode 100644 index 0000000000..9ed4965d45 --- /dev/null +++ b/tests/auto/widgets/util/qscroller/tst_qscroller.cpp @@ -0,0 +1,530 @@ +/**************************************************************************** +** +** 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 $MODULE$ 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> +#include <QtWidgets> +#include <QtTest> +// #include <QDebug> + +class tst_QScrollerWidget : public QWidget +{ +public: + tst_QScrollerWidget() + : QWidget() + { + reset(); + } + + void reset() + { + receivedPrepare = false; + receivedScroll = false; + receivedFirst = false; + receivedLast = false; + receivedOvershoot = false; + } + + bool event(QEvent *e) + { + switch (e->type()) { + case QEvent::Gesture: + e->setAccepted(false); // better reject the event or QGestureManager will make trouble + return false; + + case QEvent::ScrollPrepare: + { + receivedPrepare = true; + QScrollPrepareEvent *se = static_cast<QScrollPrepareEvent *>(e); + se->setViewportSize(QSizeF(100,100)); + se->setContentPosRange(scrollArea); + se->setContentPos(scrollPosition); + se->accept(); + return true; + } + + case QEvent::Scroll: + { + receivedScroll = true; + QScrollEvent *se = static_cast<QScrollEvent *>(e); + // qDebug() << "Scroll for"<<this<<"pos"<<se->scrollPos()<<"ov"<<se->overshoot()<<"first"<<se->isFirst()<<"last"<<se->isLast(); + + if (se->scrollState() == QScrollEvent::ScrollStarted) + receivedFirst = true; + if (se->scrollState() == QScrollEvent::ScrollFinished) + receivedLast = true; + + currentPos = se->contentPos(); + overshoot = se->overshootDistance(); + if (!qFuzzyCompare( overshoot.x() + 1.0, 1.0 ) || + !qFuzzyCompare( overshoot.y() + 1.0, 1.0 )) + receivedOvershoot = true; + return true; + } + + default: + return QObject::event(e); + } + } + + + QRectF scrollArea; + QPointF scrollPosition; + + bool receivedPrepare; + bool receivedScroll; + bool receivedFirst; + bool receivedLast; + bool receivedOvershoot; + + QPointF currentPos; + QPointF overshoot; +}; + + +class tst_QScroller : public QObject +{ + Q_OBJECT +public: + tst_QScroller() { } + ~tst_QScroller() { } + +private: + void kineticScroll( tst_QScrollerWidget *sw, QPointF from, QPoint touchStart, QPoint touchUpdate, QPoint touchEnd); + void kineticScrollNoTest( tst_QScrollerWidget *sw, QPointF from, QPoint touchStart, QPoint touchUpdate, QPoint touchEnd); + +private slots: + void staticScrollers(); + void scrollerProperties(); + void scrollTo(); +#if !defined(QT_NO_GESTURES) && !(defined(Q_OS_MACX) && (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6)) + void scroll(); + void overshoot(); +#endif +}; + +/*! \internal + Generates touchBegin, touchUpdate and touchEnd events to trigger scrolling. + Tests some in between states but does not wait until scrolling is finished. +*/ +void tst_QScroller::kineticScroll( tst_QScrollerWidget *sw, QPointF from, QPoint touchStart, QPoint touchUpdate, QPoint touchEnd) +{ + sw->scrollPosition = from; + sw->currentPos= from; + + QScroller *s1 = QScroller::scroller(sw); + QCOMPARE( s1->state(), QScroller::Inactive ); + + QScrollerProperties sp1 = QScroller::scroller(sw)->scrollerProperties(); + int fps = 60; + + QTouchEvent::TouchPoint rawTouchPoint; + rawTouchPoint.setId(0); + + // send the touch begin event + QTouchEvent::TouchPoint touchPoint(0); + touchPoint.setState(Qt::TouchPointPressed); + touchPoint.setPos(touchStart); + touchPoint.setScenePos(touchStart); + touchPoint.setScreenPos(touchStart); + QTouchEvent touchEvent1(QEvent::TouchBegin, + QTouchEvent::TouchScreen, + Qt::NoModifier, + Qt::TouchPointPressed, + (QList<QTouchEvent::TouchPoint>() << touchPoint)); + QApplication::sendEvent(sw, &touchEvent1); + + QCOMPARE( s1->state(), QScroller::Pressed ); + + // send the touch update far enough to trigger a scroll + QTest::qWait(200); // we need to wait a little or else the speed would be infinite. now we have around 500 pixel per second. + touchPoint.setPos(touchUpdate); + touchPoint.setScenePos(touchUpdate); + touchPoint.setScreenPos(touchUpdate); + QTouchEvent touchEvent2(QEvent::TouchUpdate, + QTouchEvent::TouchScreen, + Qt::NoModifier, + Qt::TouchPointMoved, + (QList<QTouchEvent::TouchPoint>() << touchPoint)); + QApplication::sendEvent(sw, &touchEvent2); + + QCOMPARE( s1->state(), QScroller::Dragging ); + QCOMPARE( sw->receivedPrepare, true ); + + + QTest::qWait(1000 / fps * 2); // wait until the first scroll move + QCOMPARE( sw->receivedFirst, true ); + QCOMPARE( sw->receivedScroll, true ); + QCOMPARE( sw->receivedOvershoot, false ); + + // note that the scrolling goes in a different direction than the mouse move + QPoint calculatedPos = from.toPoint() - touchUpdate - touchStart; + QVERIFY(qAbs(sw->currentPos.x() - calculatedPos.x()) < 1.0); + QVERIFY(qAbs(sw->currentPos.y() - calculatedPos.y()) < 1.0); + + // send the touch end + touchPoint.setPos(touchEnd); + touchPoint.setScenePos(touchEnd); + touchPoint.setScreenPos(touchEnd); + QTouchEvent touchEvent5(QEvent::TouchEnd, + QTouchEvent::TouchScreen, + Qt::NoModifier, + Qt::TouchPointReleased, + (QList<QTouchEvent::TouchPoint>() << touchPoint)); + QApplication::sendEvent(sw, &touchEvent5); +} + +/*! \internal + Generates touchBegin, touchUpdate and touchEnd events to trigger scrolling. + This function does not have any in between tests, it does not expect the scroller to actually scroll. +*/ +void tst_QScroller::kineticScrollNoTest( tst_QScrollerWidget *sw, QPointF from, QPoint touchStart, QPoint touchUpdate, QPoint touchEnd) +{ + sw->scrollPosition = from; + sw->currentPos = from; + + QScroller *s1 = QScroller::scroller(sw); + QCOMPARE( s1->state(), QScroller::Inactive ); + + QScrollerProperties sp1 = s1->scrollerProperties(); + int fps = 60; + + QTouchEvent::TouchPoint rawTouchPoint; + rawTouchPoint.setId(0); + + // send the touch begin event + QTouchEvent::TouchPoint touchPoint(0); + touchPoint.setState(Qt::TouchPointPressed); + touchPoint.setPos(touchStart); + touchPoint.setScenePos(touchStart); + touchPoint.setScreenPos(touchStart); + QTouchEvent touchEvent1(QEvent::TouchBegin, + QTouchEvent::TouchScreen, + Qt::NoModifier, + Qt::TouchPointPressed, + (QList<QTouchEvent::TouchPoint>() << touchPoint)); + QApplication::sendEvent(sw, &touchEvent1); + + // send the touch update far enough to trigger a scroll + QTest::qWait(200); // we need to wait a little or else the speed would be infinite. now we have around 500 pixel per second. + touchPoint.setPos(touchUpdate); + touchPoint.setScenePos(touchUpdate); + touchPoint.setScreenPos(touchUpdate); + QTouchEvent touchEvent2(QEvent::TouchUpdate, + QTouchEvent::TouchScreen, + Qt::NoModifier, + Qt::TouchPointMoved, + (QList<QTouchEvent::TouchPoint>() << touchPoint)); + QApplication::sendEvent(sw, &touchEvent2); + + QTest::qWait(1000 / fps * 2); // wait until the first scroll move + + // send the touch end + touchPoint.setPos(touchEnd); + touchPoint.setScenePos(touchEnd); + touchPoint.setScreenPos(touchEnd); + QTouchEvent touchEvent5(QEvent::TouchEnd, + QTouchEvent::TouchScreen, + Qt::NoModifier, + Qt::TouchPointReleased, + (QList<QTouchEvent::TouchPoint>() << touchPoint)); + QApplication::sendEvent(sw, &touchEvent5); +} + + +void tst_QScroller::staticScrollers() +{ + // scrollers + { + QObject *o1 = new QObject(this); + QObject *o2 = new QObject(this); + + // get scroller for object + QScroller *s1 = QScroller::scroller(o1); + QScroller *s2 = QScroller::scroller(o2); + + QVERIFY(s1); + QVERIFY(s2); + QVERIFY(s1 != s2); + + QVERIFY(!QScroller::scroller(static_cast<const QObject*>(0))); + QCOMPARE(QScroller::scroller(o1), s1); + + delete o1; + delete o2; + } + + // the same for properties + { + QObject *o1 = new QObject(this); + QObject *o2 = new QObject(this); + + // get scroller for object + QScrollerProperties sp1 = QScroller::scroller(o1)->scrollerProperties(); + QScrollerProperties sp2 = QScroller::scroller(o2)->scrollerProperties(); + + // default properties should be the same + QVERIFY(sp1 == sp2); + + QCOMPARE(QScroller::scroller(o1)->scrollerProperties(), sp1); + + delete o1; + delete o2; + } +} + +void tst_QScroller::scrollerProperties() +{ + QObject *o1 = new QObject(this); + QScrollerProperties sp1 = QScroller::scroller(o1)->scrollerProperties(); + + QScrollerProperties::ScrollMetric metrics[] = + { + QScrollerProperties::MousePressEventDelay, // qreal [s] + QScrollerProperties::DragStartDistance, // qreal [m] + QScrollerProperties::DragVelocitySmoothingFactor, // qreal [0..1/s] (complex calculation involving time) v = v_new* DASF + v_old * (1-DASF) + QScrollerProperties::AxisLockThreshold, // qreal [0..1] atan(|min(dx,dy)|/|max(dx,dy)|) + + QScrollerProperties::DecelerationFactor, // slope of the curve + + QScrollerProperties::MinimumVelocity, // qreal [m/s] + QScrollerProperties::MaximumVelocity, // qreal [m/s] + QScrollerProperties::MaximumClickThroughVelocity, // qreal [m/s] + + QScrollerProperties::AcceleratingFlickMaximumTime, // qreal [s] + QScrollerProperties::AcceleratingFlickSpeedupFactor, // qreal [1..] + + QScrollerProperties::SnapPositionRatio, // qreal [0..1] + QScrollerProperties::SnapTime, // qreal [s] + + QScrollerProperties::OvershootDragResistanceFactor, // qreal [0..1] + QScrollerProperties::OvershootDragDistanceFactor, // qreal [0..1] + QScrollerProperties::OvershootScrollDistanceFactor, // qreal [0..1] + QScrollerProperties::OvershootScrollTime, // qreal [s] + }; + + for (unsigned int i = 0; i < sizeof(metrics) / sizeof(metrics[0]); i++) { + sp1.setScrollMetric(metrics[i], 0.9); + QCOMPARE(sp1.scrollMetric(metrics[i]).toDouble(), 0.9); + } + sp1.setScrollMetric(QScrollerProperties::ScrollingCurve, QEasingCurve(QEasingCurve::OutQuart)); + QCOMPARE(sp1.scrollMetric(QScrollerProperties::ScrollingCurve).toEasingCurve().type(), QEasingCurve::OutQuart); + + sp1.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy, QVariant::fromValue(QScrollerProperties::OvershootAlwaysOff)); + QCOMPARE(sp1.scrollMetric(QScrollerProperties::HorizontalOvershootPolicy).value<QScrollerProperties::OvershootPolicy>(), QScrollerProperties::OvershootAlwaysOff); + + sp1.setScrollMetric(QScrollerProperties::VerticalOvershootPolicy, QVariant::fromValue(QScrollerProperties::OvershootAlwaysOn)); + QCOMPARE(sp1.scrollMetric(QScrollerProperties::VerticalOvershootPolicy).value<QScrollerProperties::OvershootPolicy>(), QScrollerProperties::OvershootAlwaysOn); + + sp1.setScrollMetric(QScrollerProperties::FrameRate, QVariant::fromValue(QScrollerProperties::Fps20)); + QCOMPARE(sp1.scrollMetric(QScrollerProperties::FrameRate).value<QScrollerProperties::FrameRates>(), QScrollerProperties::Fps20); +} + +void tst_QScroller::scrollTo() +{ + { + tst_QScrollerWidget *sw = new tst_QScrollerWidget(); + sw->scrollArea = QRectF( 0, 0, 1000, 1000 ); + sw->scrollPosition = QPointF( 500, 500 ); + + QScroller *s1 = QScroller::scroller(sw); + QCOMPARE( s1->state(), QScroller::Inactive ); + + // a normal scroll + s1->scrollTo(QPointF(100,100), 100); + QTest::qWait(200); + + QCOMPARE( sw->receivedPrepare, true ); + QCOMPARE( sw->receivedScroll, true ); + QCOMPARE( sw->receivedFirst, true ); + QCOMPARE( sw->receivedLast, true ); + QCOMPARE( sw->receivedOvershoot, false ); + QVERIFY(qFuzzyCompare( sw->currentPos.x(), 100 )); + QVERIFY(qFuzzyCompare( sw->currentPos.y(), 100 )); + + delete sw; + } +} + +// Mac OS X < 10.6 does not support QTouchEvents. +#if !defined(QT_NO_GESTURES) && !(defined(Q_OS_MACX) && (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6)) +void tst_QScroller::scroll() +{ + // -- good case. normal scroll + tst_QScrollerWidget *sw = new tst_QScrollerWidget(); + sw->scrollArea = QRectF(0, 0, 1000, 1000); + QScroller::grabGesture(sw, QScroller::TouchGesture); + sw->setGeometry(100, 100, 400, 300); + + QScroller *s1 = QScroller::scroller(sw); + kineticScroll(sw, QPointF(500, 500), QPoint(0, 0), QPoint(100, 100), QPoint(200, 200)); + // now we should be scrolling + QCOMPARE( s1->state(), QScroller::Scrolling ); + + // wait until finished, check that no further first scroll is send + sw->receivedFirst = false; + sw->receivedScroll = false; + while (s1->state() == QScroller::Scrolling) + QTest::qWait(100); + + QCOMPARE( sw->receivedFirst, false ); + QCOMPARE( sw->receivedScroll, true ); + QCOMPARE( sw->receivedLast, true ); + QVERIFY(sw->currentPos.x() < 400); + QVERIFY(sw->currentPos.y() < 400); + + // -- try to scroll when nothing to scroll + + sw->reset(); + sw->scrollArea = QRectF(0, 0, 0, 1000); + kineticScrollNoTest(sw, QPointF(0, 500), QPoint(0, 0), QPoint(100, 0), QPoint(200, 0)); + + while (s1->state() != QScroller::Inactive) + QTest::qWait(20); + + QCOMPARE(sw->currentPos.x(), 0.0); + QCOMPARE(sw->currentPos.y(), 500.0); + + delete sw; +} +#endif + +// Mac OS X < 10.6 does not support QTouchEvents. +#if !defined(QT_NO_GESTURES) && !(defined(Q_OS_MACX) && (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6)) +void tst_QScroller::overshoot() +{ + tst_QScrollerWidget *sw = new tst_QScrollerWidget(); + sw->scrollArea = QRectF(0, 0, 1000, 1000); + QScroller::grabGesture(sw, QScroller::TouchGesture); + sw->setGeometry(100, 100, 400, 300); + + QScroller *s1 = QScroller::scroller(sw); + QScrollerProperties sp1 = s1->scrollerProperties(); + + sp1.setScrollMetric(QScrollerProperties::OvershootDragResistanceFactor, 0.5); + sp1.setScrollMetric(QScrollerProperties::OvershootDragDistanceFactor, 0.2); + sp1.setScrollMetric(QScrollerProperties::OvershootScrollDistanceFactor, 0.2); + + // -- try to scroll with overshoot (when scrollable good case) + + sp1.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy, QVariant::fromValue(QScrollerProperties::OvershootWhenScrollable)); + s1->setScrollerProperties(sp1); + kineticScrollNoTest(sw, QPointF(500, 500), QPoint(0, 0), QPoint(400, 0), QPoint(490, 0)); + + while (s1->state() != QScroller::Inactive) + QTest::qWait(20); + + //qDebug() << "Overshoot fuzzy: "<<sw->currentPos; + QVERIFY(qFuzzyCompare( sw->currentPos.x(), 0 )); + QVERIFY(qFuzzyCompare( sw->currentPos.y(), 500 )); + QCOMPARE( sw->receivedOvershoot, true ); + + // -- try to scroll with overshoot (when scrollable bad case) + sw->reset(); + sw->scrollArea = QRectF(0, 0, 0, 1000); + + sp1.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy, QVariant::fromValue(QScrollerProperties::OvershootWhenScrollable)); + s1->setScrollerProperties(sp1); + kineticScrollNoTest(sw, QPointF(0, 500), QPoint(0, 0), QPoint(400, 0), QPoint(490, 0)); + + while (s1->state() != QScroller::Inactive) + QTest::qWait(20); + + //qDebug() << "Overshoot fuzzy: "<<sw->currentPos; + QVERIFY(qFuzzyCompare( sw->currentPos.x(), 0 )); + QVERIFY(qFuzzyCompare( sw->currentPos.y(), 500 )); + QCOMPARE( sw->receivedOvershoot, false ); + + // -- try to scroll with overshoot (always on) + sw->reset(); + sw->scrollArea = QRectF(0, 0, 0, 1000); + + sp1.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy, QVariant::fromValue(QScrollerProperties::OvershootAlwaysOn)); + s1->setScrollerProperties(sp1); + kineticScrollNoTest(sw, QPointF(0, 500), QPoint(0, 0), QPoint(400, 0), QPoint(490, 0)); + + while (s1->state() != QScroller::Inactive) + QTest::qWait(20); + + //qDebug() << "Overshoot fuzzy: "<<sw->currentPos; + + QVERIFY(qFuzzyCompare( sw->currentPos.x(), 0 )); + QVERIFY(qFuzzyCompare( sw->currentPos.y(), 500 )); + QCOMPARE( sw->receivedOvershoot, true ); + + // -- try to scroll with overshoot (always off) + sw->reset(); + sw->scrollArea = QRectF(0, 0, 1000, 1000); + + sp1.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy, QVariant::fromValue(QScrollerProperties::OvershootAlwaysOff)); + s1->setScrollerProperties(sp1); + kineticScrollNoTest(sw, QPointF(500, 500), QPoint(0, 0), QPoint(400, 0), QPoint(490, 0)); + + while (s1->state() != QScroller::Inactive) + QTest::qWait(20); + + QVERIFY(qFuzzyCompare( sw->currentPos.x(), 0 )); + QVERIFY(qFuzzyCompare( sw->currentPos.y(), 500 )); + QCOMPARE( sw->receivedOvershoot, false ); + + // -- try to scroll with overshoot (always on but max overshoot = 0) + sp1.setScrollMetric(QScrollerProperties::OvershootDragDistanceFactor, 0.0); + sp1.setScrollMetric(QScrollerProperties::OvershootScrollDistanceFactor, 0.0); + sw->reset(); + sw->scrollArea = QRectF(0, 0, 1000, 1000); + + sp1.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy, QVariant::fromValue(QScrollerProperties::OvershootAlwaysOn)); + s1->setScrollerProperties(sp1); + kineticScrollNoTest(sw, QPointF(500, 500), QPoint(0, 0), QPoint(400, 0), QPoint(490, 0)); + + while (s1->state() != QScroller::Inactive) + QTest::qWait(20); + + QVERIFY(qFuzzyCompare( sw->currentPos.x(), 0 )); + QVERIFY(qFuzzyCompare( sw->currentPos.y(), 500 )); + QCOMPARE( sw->receivedOvershoot, false ); + + delete sw; +} +#endif + +QTEST_MAIN(tst_QScroller) + +#include "tst_qscroller.moc" diff --git a/tests/auto/widgets/util/qsystemtrayicon/.gitignore b/tests/auto/widgets/util/qsystemtrayicon/.gitignore new file mode 100644 index 0000000000..6da1f79209 --- /dev/null +++ b/tests/auto/widgets/util/qsystemtrayicon/.gitignore @@ -0,0 +1 @@ +tst_qsystemtrayicon diff --git a/tests/auto/widgets/util/qsystemtrayicon/icons/icon.png b/tests/auto/widgets/util/qsystemtrayicon/icons/icon.png Binary files differnew file mode 100644 index 0000000000..7528931ed8 --- /dev/null +++ b/tests/auto/widgets/util/qsystemtrayicon/icons/icon.png diff --git a/tests/auto/widgets/util/qsystemtrayicon/qsystemtrayicon.pro b/tests/auto/widgets/util/qsystemtrayicon/qsystemtrayicon.pro new file mode 100644 index 0000000000..8a6ea3d0a9 --- /dev/null +++ b/tests/auto/widgets/util/qsystemtrayicon/qsystemtrayicon.pro @@ -0,0 +1,9 @@ +############################################################ +# Project file for autotest for file qsystemtrayicon.h +############################################################ + +load(qttest_p4) +QT += widgets +SOURCES += tst_qsystemtrayicon.cpp + + diff --git a/tests/auto/widgets/util/qsystemtrayicon/tst_qsystemtrayicon.cpp b/tests/auto/widgets/util/qsystemtrayicon/tst_qsystemtrayicon.cpp new file mode 100644 index 0000000000..a4a5ed7ad9 --- /dev/null +++ b/tests/auto/widgets/util/qsystemtrayicon/tst_qsystemtrayicon.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 <QtTest/QtTest> + +#include <qcoreapplication.h> +#include <qdebug.h> +#include <qsystemtrayicon.h> +#include <qmenu.h> + +//TESTED_CLASS= +//TESTED_FILES= + +class tst_QSystemTrayIcon: public QObject +{ +Q_OBJECT + +public: + tst_QSystemTrayIcon(); + virtual ~tst_QSystemTrayIcon(); + +private slots: + void getSetCheck(); + void showHide(); + void showMessage(); + void supportsMessages(); + void lastWindowClosed(); +}; + +tst_QSystemTrayIcon::tst_QSystemTrayIcon() +{ +} + +tst_QSystemTrayIcon::~tst_QSystemTrayIcon() +{ +} + +// Testing get/set functions +void tst_QSystemTrayIcon::showHide() +{ + QSystemTrayIcon icon; + icon.setIcon(QIcon("icons/icon.png")); + icon.show(); + icon.setIcon(QIcon("icons/icon.png")); + icon.hide(); +} + +// Testing get/set functions +void tst_QSystemTrayIcon::showMessage() +{ + QSystemTrayIcon icon; + icon.setIcon(QIcon("icons/icon.png")); + + icon.showMessage("Title", "Messagecontents"); + icon.showMessage("Title", "Messagecontents", QSystemTrayIcon::NoIcon); + icon.showMessage("Title", "Messagecontents", QSystemTrayIcon::Warning); + icon.showMessage("Title", "Messagecontents", QSystemTrayIcon::Critical); + + icon.show(); + icon.showMessage("Title", "Messagecontents"); + icon.showMessage("Title", "Messagecontents", QSystemTrayIcon::NoIcon); + icon.showMessage("Title", "Messagecontents", QSystemTrayIcon::Warning); + icon.showMessage("Title", "Messagecontents", QSystemTrayIcon::Critical); +} + +// Testing get/set functions +void tst_QSystemTrayIcon::getSetCheck() +{ + QSystemTrayIcon icon; + QCOMPARE(true, icon.toolTip().isEmpty()); + icon.setToolTip("testToolTip"); + QCOMPARE(true, "testToolTip" == icon.toolTip()); + + QCOMPARE(true, icon.icon().isNull()); + icon.setIcon(QIcon("icons/icon.png")); + QCOMPARE(false, icon.icon().isNull()); + + QMenu menu; + QCOMPARE(true, icon.contextMenu() == 0); + icon.setContextMenu(&menu); + QCOMPARE(false, icon.contextMenu() == 0); +} + +void tst_QSystemTrayIcon::supportsMessages() +{ +#ifdef Q_WS_QPA + QEXPECT_FAIL("", "QTBUG-20978 QSystemTrayIcon is unimplemented for qpa", Abort); +#endif + +#if !defined(Q_WS_QWS) + QCOMPARE(QSystemTrayIcon::supportsMessages(), true ); +#else + QCOMPARE(QSystemTrayIcon::supportsMessages(), false ); +#endif + +} + +void tst_QSystemTrayIcon::lastWindowClosed() +{ + QSignalSpy spy(qApp, SIGNAL(lastWindowClosed())); + QWidget window; + QSystemTrayIcon icon; + icon.setIcon(QIcon("whatever.png")); + icon.show(); + window.show(); + QTimer::singleShot(2500, &window, SLOT(close())); + QTimer::singleShot(20000, qApp, SLOT(quit())); // in case the test fails + qApp->exec(); + QVERIFY(spy.count() == 1); +} + +QTEST_MAIN(tst_QSystemTrayIcon) +#include "tst_qsystemtrayicon.moc" diff --git a/tests/auto/widgets/util/qundogroup/.gitignore b/tests/auto/widgets/util/qundogroup/.gitignore new file mode 100644 index 0000000000..bc3e8c4691 --- /dev/null +++ b/tests/auto/widgets/util/qundogroup/.gitignore @@ -0,0 +1 @@ +tst_qundogroup diff --git a/tests/auto/widgets/util/qundogroup/qundogroup.pro b/tests/auto/widgets/util/qundogroup/qundogroup.pro new file mode 100644 index 0000000000..c2b6ceb038 --- /dev/null +++ b/tests/auto/widgets/util/qundogroup/qundogroup.pro @@ -0,0 +1,4 @@ +CONFIG += qttest_p4 +QT += widgets +SOURCES += tst_qundogroup.cpp +TARGET = tst_qundogroup diff --git a/tests/auto/widgets/util/qundogroup/testdata/qundogroup.ts b/tests/auto/widgets/util/qundogroup/testdata/qundogroup.ts new file mode 100644 index 0000000000..a059bcb486 --- /dev/null +++ b/tests/auto/widgets/util/qundogroup/testdata/qundogroup.ts @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.0" language="en"> +<context> + <name>QUndoGroup</name> + <message> + <source>Undo %1</source> + <translation>undo-prefix %1 undo-suffix</translation> + </message> + <message> + <source>Undo</source> + <comment>Default text for undo action</comment> + <translation>Undo-default-text</translation> + </message> + <message> + <source>Redo %1</source> + <translation>redo-prefix %1 redo-suffix</translation> + </message> + <message> + <source>Redo</source> + <comment>Default text for redo action</comment> + <translation>Redo-default-text</translation> + </message> +</context> +</TS> diff --git a/tests/auto/widgets/util/qundogroup/tst_qundogroup.cpp b/tests/auto/widgets/util/qundogroup/tst_qundogroup.cpp new file mode 100644 index 0000000000..6d51252e22 --- /dev/null +++ b/tests/auto/widgets/util/qundogroup/tst_qundogroup.cpp @@ -0,0 +1,668 @@ +/**************************************************************************** +** +** 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 <QUndoGroup> +#include <QUndoStack> +#include <QAction> + +// Temporarily disabling IRIX due to build issuues with GCC +#if !defined(__sgi) || defined(__sgi) && !defined(__GNUC__) + +/****************************************************************************** +** Commands +*/ + +class InsertCommand : public QUndoCommand +{ +public: + InsertCommand(QString *str, int idx, const QString &text, + QUndoCommand *parent = 0); + + virtual void undo(); + virtual void redo(); + +private: + QString *m_str; + int m_idx; + QString m_text; +}; + +class RemoveCommand : public QUndoCommand +{ +public: + RemoveCommand(QString *str, int idx, int len, QUndoCommand *parent = 0); + + virtual void undo(); + virtual void redo(); + +private: + QString *m_str; + int m_idx; + QString m_text; +}; + +class AppendCommand : public QUndoCommand +{ +public: + AppendCommand(QString *str, const QString &text, QUndoCommand *parent = 0); + + virtual void undo(); + virtual void redo(); + virtual int id() const; + virtual bool mergeWith(const QUndoCommand *other); + + bool merged; + +private: + QString *m_str; + QString m_text; +}; + +InsertCommand::InsertCommand(QString *str, int idx, const QString &text, + QUndoCommand *parent) + : QUndoCommand(parent) +{ + QVERIFY(str->length() >= idx); + + setText("insert"); + + m_str = str; + m_idx = idx; + m_text = text; +} + +void InsertCommand::redo() +{ + QVERIFY(m_str->length() >= m_idx); + + m_str->insert(m_idx, m_text); +} + +void InsertCommand::undo() +{ + QCOMPARE(m_str->mid(m_idx, m_text.length()), m_text); + + m_str->remove(m_idx, m_text.length()); +} + +RemoveCommand::RemoveCommand(QString *str, int idx, int len, QUndoCommand *parent) + : QUndoCommand(parent) +{ + QVERIFY(str->length() >= idx + len); + + setText("remove"); + + m_str = str; + m_idx = idx; + m_text = m_str->mid(m_idx, len); +} + +void RemoveCommand::redo() +{ + QCOMPARE(m_str->mid(m_idx, m_text.length()), m_text); + + m_str->remove(m_idx, m_text.length()); +} + +void RemoveCommand::undo() +{ + QVERIFY(m_str->length() >= m_idx); + + m_str->insert(m_idx, m_text); +} + +AppendCommand::AppendCommand(QString *str, const QString &text, QUndoCommand *parent) + : QUndoCommand(parent) +{ + setText("append"); + + m_str = str; + m_text = text; + merged = false; +} + +void AppendCommand::redo() +{ + m_str->append(m_text); +} + +void AppendCommand::undo() +{ + QCOMPARE(m_str->mid(m_str->length() - m_text.length()), m_text); + + m_str->truncate(m_str->length() - m_text.length()); +} + +int AppendCommand::id() const +{ + return 1; +} + +bool AppendCommand::mergeWith(const QUndoCommand *other) +{ + if (other->id() != id()) + return false; + m_text += static_cast<const AppendCommand*>(other)->m_text; + merged = true; + return true; +} + +/****************************************************************************** +** tst_QUndoStack +*/ + +class tst_QUndoGroup : public QObject +{ + Q_OBJECT +public: + tst_QUndoGroup(); + +private slots: + void setActive(); + void addRemoveStack(); + void deleteStack(); + void checkSignals(); + void addStackAndDie(); + void commandTextFormat(); +}; + +tst_QUndoGroup::tst_QUndoGroup() +{ +} + +void tst_QUndoGroup::setActive() +{ + QUndoGroup group; + QUndoStack stack1(&group), stack2(&group); + + QCOMPARE(group.activeStack(), (QUndoStack*)0); + QCOMPARE(stack1.isActive(), false); + QCOMPARE(stack2.isActive(), false); + + QUndoStack stack3; + QCOMPARE(stack3.isActive(), true); + + group.addStack(&stack3); + QCOMPARE(stack3.isActive(), false); + + stack1.setActive(); + QCOMPARE(group.activeStack(), &stack1); + QCOMPARE(stack1.isActive(), true); + QCOMPARE(stack2.isActive(), false); + QCOMPARE(stack3.isActive(), false); + + group.setActiveStack(&stack2); + QCOMPARE(group.activeStack(), &stack2); + QCOMPARE(stack1.isActive(), false); + QCOMPARE(stack2.isActive(), true); + QCOMPARE(stack3.isActive(), false); + + group.removeStack(&stack2); + QCOMPARE(group.activeStack(), (QUndoStack*)0); + QCOMPARE(stack1.isActive(), false); + QCOMPARE(stack2.isActive(), true); + QCOMPARE(stack3.isActive(), false); + + group.removeStack(&stack2); + QCOMPARE(group.activeStack(), (QUndoStack*)0); + QCOMPARE(stack1.isActive(), false); + QCOMPARE(stack2.isActive(), true); + QCOMPARE(stack3.isActive(), false); +} + +void tst_QUndoGroup::addRemoveStack() +{ + QUndoGroup group; + + QUndoStack stack1(&group); + QCOMPARE(group.stacks(), QList<QUndoStack*>() << &stack1); + + QUndoStack stack2; + group.addStack(&stack2); + QCOMPARE(group.stacks(), QList<QUndoStack*>() << &stack1 << &stack2); + + group.addStack(&stack1); + QCOMPARE(group.stacks(), QList<QUndoStack*>() << &stack1 << &stack2); + + group.removeStack(&stack1); + QCOMPARE(group.stacks(), QList<QUndoStack*>() << &stack2); + + group.removeStack(&stack1); + QCOMPARE(group.stacks(), QList<QUndoStack*>() << &stack2); + + group.removeStack(&stack2); + QCOMPARE(group.stacks(), QList<QUndoStack*>()); +} + +void tst_QUndoGroup::deleteStack() +{ + QUndoGroup group; + + QUndoStack *stack1 = new QUndoStack(&group); + QCOMPARE(group.stacks(), QList<QUndoStack*>() << stack1); + QCOMPARE(group.activeStack(), (QUndoStack*)0); + + stack1->setActive(); + QCOMPARE(group.activeStack(), stack1); + + QUndoStack *stack2 = new QUndoStack(&group); + QCOMPARE(group.stacks(), QList<QUndoStack*>() << stack1 << stack2); + QCOMPARE(group.activeStack(), stack1); + + QUndoStack *stack3 = new QUndoStack(&group); + QCOMPARE(group.stacks(), QList<QUndoStack*>() << stack1 << stack2 << stack3); + QCOMPARE(group.activeStack(), stack1); + + delete stack2; + QCOMPARE(group.stacks(), QList<QUndoStack*>() << stack1 << stack3); + QCOMPARE(group.activeStack(), stack1); + + delete stack1; + QCOMPARE(group.stacks(), QList<QUndoStack*>() << stack3); + QCOMPARE(group.activeStack(), (QUndoStack*)0); + + stack3->setActive(false); + QCOMPARE(group.activeStack(), (QUndoStack*)0); + + stack3->setActive(true); + QCOMPARE(group.activeStack(), stack3); + + group.removeStack(stack3); + QCOMPARE(group.stacks(), QList<QUndoStack*>()); + QCOMPARE(group.activeStack(), (QUndoStack*)0); + + delete stack3; +} + +static QString glue(const QString &s1, const QString &s2) +{ + QString result; + + result.append(s1); + if (!s1.isEmpty() && !s2.isEmpty()) + result.append(' '); + result.append(s2); + + return result; +} + +#define CHECK_STATE(_activeStack, _clean, _canUndo, _undoText, _canRedo, _redoText, \ + _cleanChanged, _indexChanged, _undoChanged, _redoChanged) \ + QCOMPARE(group.activeStack(), (QUndoStack*)_activeStack); \ + QCOMPARE(group.isClean(), _clean); \ + QCOMPARE(group.canUndo(), _canUndo); \ + QCOMPARE(group.undoText(), QString(_undoText)); \ + QCOMPARE(group.canRedo(), _canRedo); \ + QCOMPARE(group.redoText(), QString(_redoText)); \ + if (_indexChanged) { \ + QCOMPARE(indexChangedSpy.count(), 1); \ + indexChangedSpy.clear(); \ + } else { \ + QCOMPARE(indexChangedSpy.count(), 0); \ + } \ + if (_cleanChanged) { \ + QCOMPARE(cleanChangedSpy.count(), 1); \ + QCOMPARE(cleanChangedSpy.at(0).at(0).toBool(), _clean); \ + cleanChangedSpy.clear(); \ + } else { \ + QCOMPARE(cleanChangedSpy.count(), 0); \ + } \ + if (_undoChanged) { \ + QCOMPARE(canUndoChangedSpy.count(), 1); \ + QCOMPARE(canUndoChangedSpy.at(0).at(0).toBool(), _canUndo); \ + QCOMPARE(undo_action->isEnabled(), _canUndo); \ + QCOMPARE(undoTextChangedSpy.count(), 1); \ + QCOMPARE(undoTextChangedSpy.at(0).at(0).toString(), QString(_undoText)); \ + QCOMPARE(undo_action->text(), glue("foo", _undoText)); \ + canUndoChangedSpy.clear(); \ + undoTextChangedSpy.clear(); \ + } else { \ + QCOMPARE(canUndoChangedSpy.count(), 0); \ + QCOMPARE(undoTextChangedSpy.count(), 0); \ + } \ + if (_redoChanged) { \ + QCOMPARE(canRedoChangedSpy.count(), 1); \ + QCOMPARE(canRedoChangedSpy.at(0).at(0).toBool(), _canRedo); \ + QCOMPARE(redo_action->isEnabled(), _canRedo); \ + QCOMPARE(redoTextChangedSpy.count(), 1); \ + QCOMPARE(redoTextChangedSpy.at(0).at(0).toString(), QString(_redoText)); \ + QCOMPARE(redo_action->text(), glue("bar", _redoText)); \ + canRedoChangedSpy.clear(); \ + redoTextChangedSpy.clear(); \ + } else { \ + QCOMPARE(canRedoChangedSpy.count(), 0); \ + QCOMPARE(redoTextChangedSpy.count(), 0); \ + } + +void tst_QUndoGroup::checkSignals() +{ + QUndoGroup group; + QAction *undo_action = group.createUndoAction(0, QString("foo")); + QAction *redo_action = group.createRedoAction(0, QString("bar")); + QSignalSpy indexChangedSpy(&group, SIGNAL(indexChanged(int))); + QSignalSpy cleanChangedSpy(&group, SIGNAL(cleanChanged(bool))); + QSignalSpy canUndoChangedSpy(&group, SIGNAL(canUndoChanged(bool))); + QSignalSpy undoTextChangedSpy(&group, SIGNAL(undoTextChanged(QString))); + QSignalSpy canRedoChangedSpy(&group, SIGNAL(canRedoChanged(bool))); + QSignalSpy redoTextChangedSpy(&group, SIGNAL(redoTextChanged(QString))); + + QString str; + + CHECK_STATE(0, // activeStack + true, // clean + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false) // redoChanged + + group.undo(); + CHECK_STATE(0, // activeStack + true, // clean + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false) // redoChanged + + group.redo(); + CHECK_STATE(0, // activeStack + true, // clean + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false) // redoChanged + + QUndoStack *stack1 = new QUndoStack(&group); + CHECK_STATE(0, // activeStack + true, // clean + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false) // redoChanged + + stack1->push(new AppendCommand(&str, "foo")); + CHECK_STATE(0, // activeStack + true, // clean + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false) // redoChanged + + stack1->setActive(); + CHECK_STATE(stack1, // activeStack + false, // clean + true, // canUndo + "append", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true) // redoChanged + + stack1->push(new InsertCommand(&str, 0, "bar")); + CHECK_STATE(stack1, // activeStack + false, // clean + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true) // redoChanged + + stack1->undo(); + CHECK_STATE(stack1, // activeStack + false, // clean + true, // canUndo + "append", // undoText + true, // canRedo + "insert", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true) // redoChanged + + stack1->undo(); + CHECK_STATE(stack1, // activeStack + true, // clean + false, // canUndo + "", // undoText + true, // canRedo + "append", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true) // redoChanged + + stack1->undo(); + CHECK_STATE(stack1, // activeStack + true, // clean + false, // canUndo + "", // undoText + true, // canRedo + "append", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false) // redoChanged + + group.undo(); + CHECK_STATE(stack1, // activeStack + true, // clean + false, // canUndo + "", // undoText + true, // canRedo + "append", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false) // redoChanged + + group.redo(); + CHECK_STATE(stack1, // activeStack + false, // clean + true, // canUndo + "append", // undoText + true, // canRedo + "insert", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true) // redoChanged + + stack1->setActive(false); + CHECK_STATE(0, // activeStack + true, // clean + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true) // redoChanged + + QUndoStack *stack2 = new QUndoStack(&group); + CHECK_STATE(0, // activeStack + true, // clean + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false) // redoChanged + + stack2->setActive(); + CHECK_STATE(stack2, // activeStack + true, // clean + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true) // redoChanged + + stack1->setActive(); + CHECK_STATE(stack1, // activeStack + false, // clean + true, // canUndo + "append", // undoText + true, // canRedo + "insert", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true) // redoChanged + + delete stack1; + CHECK_STATE(0, // activeStack + true, // clean + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true) // redoChanged + + delete undo_action; + delete redo_action; +} + +void tst_QUndoGroup::addStackAndDie() +{ + // Test that QUndoStack doesn't keep a reference to QUndoGroup after the + // group is deleted. + QUndoStack *stack = new QUndoStack; + QUndoGroup *group = new QUndoGroup; + group->addStack(stack); + delete group; + stack->setActive(true); + delete stack; +} + +void tst_QUndoGroup::commandTextFormat() +{ + QString binDir = QLibraryInfo::location(QLibraryInfo::BinariesPath); + + if (QProcess::execute(binDir + "/lrelease -version") != 0) { + QSKIP("lrelease is missing or broken", SkipAll); + } + + QVERIFY(!QProcess::execute(binDir + "/lrelease testdata/qundogroup.ts")); + + QTranslator translator; + QVERIFY(translator.load("testdata/qundogroup.qm")); + qApp->installTranslator(&translator); + + QUndoGroup group; + QAction *undo_action = group.createUndoAction(0); + QAction *redo_action = group.createRedoAction(0); + + QCOMPARE(undo_action->text(), QString("Undo-default-text")); + QCOMPARE(redo_action->text(), QString("Redo-default-text")); + + QUndoStack stack(&group); + stack.setActive(); + QString str; + + stack.push(new AppendCommand(&str, "foo")); + QCOMPARE(undo_action->text(), QString("undo-prefix append undo-suffix")); + QCOMPARE(redo_action->text(), QString("Redo-default-text")); + + stack.push(new InsertCommand(&str, 0, "bar")); + stack.undo(); + QCOMPARE(undo_action->text(), QString("undo-prefix append undo-suffix")); + QCOMPARE(redo_action->text(), QString("redo-prefix insert redo-suffix")); + + stack.undo(); + QCOMPARE(undo_action->text(), QString("Undo-default-text")); + QCOMPARE(redo_action->text(), QString("redo-prefix append redo-suffix")); + + qApp->removeTranslator(&translator); +} + +#else +class tst_QUndoGroup : public QObject +{ + Q_OBJECT +public: + tst_QUndoGroup() {} + +private slots: + void setActive() { QSKIP( "Not tested on irix-g++", SkipAll); } + void addRemoveStack() { QSKIP( "Not tested on irix-g++", SkipAll); } + void deleteStack() { QSKIP( "Not tested on irix-g++", SkipAll); } + void checkSignals() { QSKIP( "Not tested on irix-g++", SkipAll); } + void addStackAndDie() { QSKIP( "Not tested on irix-g++", SkipAll); } +}; +#endif + +QTEST_MAIN(tst_QUndoGroup) + +#include "tst_qundogroup.moc" + diff --git a/tests/auto/widgets/util/qundostack/.gitignore b/tests/auto/widgets/util/qundostack/.gitignore new file mode 100644 index 0000000000..f8faf2771f --- /dev/null +++ b/tests/auto/widgets/util/qundostack/.gitignore @@ -0,0 +1 @@ +tst_qundostack diff --git a/tests/auto/widgets/util/qundostack/qundostack.pro b/tests/auto/widgets/util/qundostack/qundostack.pro new file mode 100644 index 0000000000..c8cfb70059 --- /dev/null +++ b/tests/auto/widgets/util/qundostack/qundostack.pro @@ -0,0 +1,4 @@ +CONFIG += qttest_p4 +QT += widgets +SOURCES += tst_qundostack.cpp +TARGET = tst_qundostack diff --git a/tests/auto/widgets/util/qundostack/testdata/qundostack.ts b/tests/auto/widgets/util/qundostack/testdata/qundostack.ts new file mode 100644 index 0000000000..4584036af2 --- /dev/null +++ b/tests/auto/widgets/util/qundostack/testdata/qundostack.ts @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.0" language="en"> +<context> + <name>QUndoStack</name> + <message> + <source>Undo %1</source> + <translation>undo-prefix %1 undo-suffix</translation> + </message> + <message> + <source>Undo</source> + <comment>Default text for undo action</comment> + <translation>Undo-default-text</translation> + </message> + <message> + <source>Redo %1</source> + <translation>redo-prefix %1 redo-suffix</translation> + </message> + <message> + <source>Redo</source> + <comment>Default text for redo action</comment> + <translation>Redo-default-text</translation> + </message> +</context> +</TS> diff --git a/tests/auto/widgets/util/qundostack/tst_qundostack.cpp b/tests/auto/widgets/util/qundostack/tst_qundostack.cpp new file mode 100644 index 0000000000..834c2d5d50 --- /dev/null +++ b/tests/auto/widgets/util/qundostack/tst_qundostack.cpp @@ -0,0 +1,3039 @@ +/**************************************************************************** +** +** 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 <QAction> +#include <QUndoStack> + +//TESTED_CLASS= +//TESTED_FILES= + +/****************************************************************************** +** Commands +*/ + +class InsertCommand : public QUndoCommand +{ +public: + InsertCommand(QString *str, int idx, const QString &text, + QUndoCommand *parent = 0); + + virtual void undo(); + virtual void redo(); + +private: + QString *m_str; + int m_idx; + QString m_text; +}; + +class RemoveCommand : public QUndoCommand +{ +public: + RemoveCommand(QString *str, int idx, int len, QUndoCommand *parent = 0); + + virtual void undo(); + virtual void redo(); + +private: + QString *m_str; + int m_idx; + QString m_text; +}; + +class AppendCommand : public QUndoCommand +{ +public: + AppendCommand(QString *str, const QString &text, bool _fail_merge = false, + QUndoCommand *parent = 0); + ~AppendCommand(); + + virtual void undo(); + virtual void redo(); + virtual int id() const; + virtual bool mergeWith(const QUndoCommand *other); + + bool merged; + bool fail_merge; + static int delete_cnt; + +private: + QString *m_str; + QString m_text; +}; + +class IdleCommand : public QUndoCommand +{ +public: + IdleCommand(QUndoCommand *parent = 0); + ~IdleCommand(); + + virtual void undo(); + virtual void redo(); +}; + +InsertCommand::InsertCommand(QString *str, int idx, const QString &text, + QUndoCommand *parent) + : QUndoCommand(parent) +{ + QVERIFY(str->length() >= idx); + + setText("insert"); + + m_str = str; + m_idx = idx; + m_text = text; +} + +void InsertCommand::redo() +{ + QVERIFY(m_str->length() >= m_idx); + + m_str->insert(m_idx, m_text); +} + +void InsertCommand::undo() +{ + QCOMPARE(m_str->mid(m_idx, m_text.length()), m_text); + + m_str->remove(m_idx, m_text.length()); +} + +RemoveCommand::RemoveCommand(QString *str, int idx, int len, QUndoCommand *parent) + : QUndoCommand(parent) +{ + QVERIFY(str->length() >= idx + len); + + setText("remove"); + + m_str = str; + m_idx = idx; + m_text = m_str->mid(m_idx, len); +} + +void RemoveCommand::redo() +{ + QCOMPARE(m_str->mid(m_idx, m_text.length()), m_text); + + m_str->remove(m_idx, m_text.length()); +} + +void RemoveCommand::undo() +{ + QVERIFY(m_str->length() >= m_idx); + + m_str->insert(m_idx, m_text); +} + +int AppendCommand::delete_cnt = 0; + +AppendCommand::AppendCommand(QString *str, const QString &text, bool _fail_merge, + QUndoCommand *parent) + : QUndoCommand(parent) +{ + setText("append"); + + m_str = str; + m_text = text; + merged = false; + fail_merge = _fail_merge; +} + +AppendCommand::~AppendCommand() +{ + ++delete_cnt; +} + +void AppendCommand::redo() +{ + m_str->append(m_text); +} + +void AppendCommand::undo() +{ + QCOMPARE(m_str->mid(m_str->length() - m_text.length()), m_text); + + m_str->truncate(m_str->length() - m_text.length()); +} + +int AppendCommand::id() const +{ + return 1; +} + +bool AppendCommand::mergeWith(const QUndoCommand *other) +{ + if (other->id() != id()) + return false; + if (fail_merge) + return false; + m_text += static_cast<const AppendCommand*>(other)->m_text; + merged = true; + return true; +} + +IdleCommand::IdleCommand(QUndoCommand *parent) + : QUndoCommand(parent) +{ + // "idle-item" goes to QUndoStack::{redo,undo}Text + // "idle-action" goes to all other places (e.g. QUndoView) + setText("idle-item\nidle-action"); +} + +IdleCommand::~IdleCommand() +{ +} + +void IdleCommand::redo() +{ +} + +void IdleCommand::undo() +{ +} + +/****************************************************************************** +** tst_QUndoStack +*/ + +class tst_QUndoStack : public QObject +{ + Q_OBJECT +public: + tst_QUndoStack(); + +private slots: + void undoRedo(); + void setIndex(); + void setClean(); + void clear(); + void childCommand(); + void macroBeginEnd(); + void compression(); + void undoLimit(); + void commandTextFormat(); + void separateUndoText(); +}; + +tst_QUndoStack::tst_QUndoStack() +{ +} + +static QString glue(const QString &s1, const QString &s2) +{ + QString result; + + result.append(s1); + if (!s1.isEmpty() && !s2.isEmpty()) + result.append(' '); + result.append(s2); + + return result; +} + +static void checkState(QSignalSpy &redoTextChangedSpy, + QSignalSpy &canRedoChangedSpy, + QSignalSpy &undoTextChangedSpy, + QAction *const redoAction, + QAction *const undoAction, + QSignalSpy &canUndoChangedSpy, + QSignalSpy &cleanChangedSpy, + QSignalSpy &indexChangedSpy, + QUndoStack &stack, + const bool _clean, + const int _count, + const int _index, + const bool _canUndo, + const QString &_undoText, + const bool _canRedo, + const QString &_redoText, + const bool _cleanChanged, + const bool _indexChanged, + const bool _undoChanged, + const bool _redoChanged) +{ + QCOMPARE(stack.count(), _count); + QCOMPARE(stack.isClean(), _clean); + QCOMPARE(stack.index(), _index); + QCOMPARE(stack.canUndo(), _canUndo); + QCOMPARE(stack.undoText(), QString(_undoText)); + QCOMPARE(stack.canRedo(), _canRedo); + QCOMPARE(stack.redoText(), QString(_redoText)); + if (_indexChanged) { + QCOMPARE(indexChangedSpy.count(), 1); + QCOMPARE(indexChangedSpy.at(0).at(0).toInt(), _index); + indexChangedSpy.clear(); + } else { + QCOMPARE(indexChangedSpy.count(), 0); + } + if (_cleanChanged) { + QCOMPARE(cleanChangedSpy.count(), 1); + QCOMPARE(cleanChangedSpy.at(0).at(0).toBool(), _clean); + cleanChangedSpy.clear(); + } else { + QCOMPARE(cleanChangedSpy.count(), 0); + } + if (_undoChanged) { + QCOMPARE(canUndoChangedSpy.count(), 1); + QCOMPARE(canUndoChangedSpy.at(0).at(0).toBool(), _canUndo); + QCOMPARE(undoAction->isEnabled(), _canUndo); + QCOMPARE(undoTextChangedSpy.count(), 1); + QCOMPARE(undoTextChangedSpy.at(0).at(0).toString(), QString(_undoText)); + QCOMPARE(undoAction->text(), glue("foo", _undoText)); + canUndoChangedSpy.clear(); + undoTextChangedSpy.clear(); + } else { + QCOMPARE(canUndoChangedSpy.count(), 0); + QCOMPARE(undoTextChangedSpy.count(), 0); + } + if (_redoChanged) { + QCOMPARE(canRedoChangedSpy.count(), 1); + QCOMPARE(canRedoChangedSpy.at(0).at(0).toBool(), _canRedo); + QCOMPARE(redoAction->isEnabled(), _canRedo); + QCOMPARE(redoTextChangedSpy.count(), 1); + QCOMPARE(redoTextChangedSpy.at(0).at(0).toString(), QString(_redoText)); + QCOMPARE(redoAction->text(), glue("bar", _redoText)); + canRedoChangedSpy.clear(); + redoTextChangedSpy.clear(); + } else { + QCOMPARE(canRedoChangedSpy.count(), 0); + QCOMPARE(redoTextChangedSpy.count(), 0); + } +} + +void tst_QUndoStack::undoRedo() +{ + QUndoStack stack; + QAction *undoAction = stack.createUndoAction(0, QString("foo")); + QAction *redoAction = stack.createRedoAction(0, QString("bar")); + QSignalSpy indexChangedSpy(&stack, SIGNAL(indexChanged(int))); + QSignalSpy cleanChangedSpy(&stack, SIGNAL(cleanChanged(bool))); + QSignalSpy canUndoChangedSpy(&stack, SIGNAL(canUndoChanged(bool))); + QSignalSpy undoTextChangedSpy(&stack, SIGNAL(undoTextChanged(QString))); + QSignalSpy canRedoChangedSpy(&stack, SIGNAL(canRedoChanged(bool))); + QSignalSpy redoTextChangedSpy(&stack, SIGNAL(redoTextChanged(QString))); + QString str; + + // push, undo, redo + + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 0, // count + 0, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.undo(); // nothing to undo + QCOMPARE(str, QString()); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 0, // count + 0, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.push(new InsertCommand(&str, 0, "hello")); + QCOMPARE(str, QString("hello")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 1, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new InsertCommand(&str, 2, "123")); + QCOMPARE(str, QString("he123llo")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + + stack.undo(); + QCOMPARE(str, QString("hello")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 1, // index + true, // canUndo + "insert", // undoText + true, // canRedo + "insert", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.redo(); + QCOMPARE(str, QString("he123llo")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.redo(); // nothing to redo + QCOMPARE(str, QString("he123llo")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.undo(); + QCOMPARE(str, QString("hello")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 1, // index + true, // canUndo + "insert", // undoText + true, // canRedo + "insert", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.undo(); + QCOMPARE(str, QString()); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 2, // count + 0, // index + false, // canUndo + "", // undoText + true, // canRedo + "insert", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.undo(); // nothing to undo + QCOMPARE(str, QString()); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 2, // count + 0, // index + false, // canUndo + "", // undoText + true, // canRedo + "insert", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + // push after undo - check that undone commands get deleted + + stack.redo(); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 1, // index + true, // canUndo + "insert", // undoText + true, // canRedo + "insert", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new RemoveCommand(&str, 2, 2)); + QCOMPARE(str, QString("heo")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count - still 2, last command got deleted + 2, // index + true, // canUndo + "remove", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.undo(); + QCOMPARE(str, QString("hello")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 1, // index + true, // canUndo + "insert", // undoText + true, // canRedo + "remove", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.undo(); + QCOMPARE(str, QString()); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 2, // count + 0, // index + false, // canUndo + "", // undoText + true, // canRedo + "insert", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new InsertCommand(&str, 0, "goodbye")); + QCOMPARE(str, QString("goodbye")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count - two commands got deleted + 1, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged +} + +void tst_QUndoStack::setIndex() +{ + QUndoStack stack; + QAction *undoAction = stack.createUndoAction(0, QString("foo")); + QAction *redoAction = stack.createRedoAction(0, QString("bar")); + QSignalSpy indexChangedSpy(&stack, SIGNAL(indexChanged(int))); + QSignalSpy cleanChangedSpy(&stack, SIGNAL(cleanChanged(bool))); + QSignalSpy canUndoChangedSpy(&stack, SIGNAL(canUndoChanged(bool))); + QSignalSpy undoTextChangedSpy(&stack, SIGNAL(undoTextChanged(QString))); + QSignalSpy canRedoChangedSpy(&stack, SIGNAL(canRedoChanged(bool))); + QSignalSpy redoTextChangedSpy(&stack, SIGNAL(redoTextChanged(QString))); + QString str; + + stack.setIndex(10); // should do nothing + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 0, // count + 0, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.setIndex(0); // should do nothing + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 0, // count + 0, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.setIndex(-10); // should do nothing + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 0, // count + 0, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.push(new InsertCommand(&str, 0, "hello")); + QCOMPARE(str, QString("hello")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 1, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new InsertCommand(&str, 2, "123")); + QCOMPARE(str, QString("he123llo")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.setIndex(2); + QCOMPARE(str, QString("he123llo")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.setIndex(0); + QCOMPARE(str, QString()); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 2, // count + 0, // index + false, // canUndo + "", // undoText + true, // canRedo + "insert", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.setIndex(10); // should set index to 2 + QCOMPARE(str, QString("he123llo")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.setIndex(-10); // should set index to 0 + QCOMPARE(str, QString()); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 2, // count + 0, // index + false, // canUndo + "", // undoText + true, // canRedo + "insert", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.setIndex(1); + QCOMPARE(str, QString("hello")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 1, // index + true, // canUndo + "insert", // undoText + true, // canRedo + "insert", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.setIndex(2); + QCOMPARE(str, QString("he123llo")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged +} + +void tst_QUndoStack::setClean() +{ + QUndoStack stack; + QAction *undoAction = stack.createUndoAction(0, QString("foo")); + QAction *redoAction = stack.createRedoAction(0, QString("bar")); + QSignalSpy indexChangedSpy(&stack, SIGNAL(indexChanged(int))); + QSignalSpy cleanChangedSpy(&stack, SIGNAL(cleanChanged(bool))); + QSignalSpy canUndoChangedSpy(&stack, SIGNAL(canUndoChanged(bool))); + QSignalSpy undoTextChangedSpy(&stack, SIGNAL(undoTextChanged(QString))); + QSignalSpy canRedoChangedSpy(&stack, SIGNAL(canRedoChanged(bool))); + QSignalSpy redoTextChangedSpy(&stack, SIGNAL(redoTextChanged(QString))); + QString str; + + QCOMPARE(stack.cleanIndex(), 0); + stack.setClean(); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 0, // count + 0, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + QCOMPARE(stack.cleanIndex(), 0); + + stack.push(new InsertCommand(&str, 0, "goodbye")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 1, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + QCOMPARE(stack.cleanIndex(), 0); + + stack.setClean(); + QCOMPARE(str, QString("goodbye")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 1, // count + 1, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + QCOMPARE(stack.cleanIndex(), 1); + + stack.push(new AppendCommand(&str, " cowboy")); + QCOMPARE(str, QString("goodbye cowboy")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "append", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + QCOMPARE(stack.cleanIndex(), 1); + + stack.undo(); // reaching clean state from above + QCOMPARE(str, QString("goodbye")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 2, // count + 1, // index + true, // canUndo + "insert", // undoText + true, // canRedo + "append", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + QCOMPARE(stack.cleanIndex(), 1); + + stack.undo(); + QCOMPARE(str, QString()); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 0, // index + false, // canUndo + "", // undoText + true, // canRedo + "insert", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + QCOMPARE(stack.cleanIndex(), 1); + + stack.redo(); // reaching clean state from below + QCOMPARE(str, QString("goodbye")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 2, // count + 1, // index + true, // canUndo + "insert", // undoText + true, // canRedo + "append", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + QCOMPARE(stack.cleanIndex(), 1); + + stack.undo(); + QCOMPARE(str, QString()); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 0, // index + false, // canUndo + "", // undoText + true, // canRedo + "insert", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + QCOMPARE(stack.cleanIndex(), 1); + + stack.push(new InsertCommand(&str, 0, "foo")); // the clean state gets deleted! + QCOMPARE(str, QString("foo")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 1, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + QCOMPARE(stack.cleanIndex(), -1); + + stack.undo(); + QCOMPARE(str, QString()); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 0, // index + false, // canUndo + "", // undoText + true, // canRedo + "insert", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + QCOMPARE(stack.cleanIndex(), -1); +} + +void tst_QUndoStack::clear() +{ + QUndoStack stack; + QAction *undoAction = stack.createUndoAction(this, QString("foo")); + QAction *redoAction = stack.createRedoAction(this, QString("bar")); + QSignalSpy indexChangedSpy(&stack, SIGNAL(indexChanged(int))); + QSignalSpy cleanChangedSpy(&stack, SIGNAL(cleanChanged(bool))); + QSignalSpy canUndoChangedSpy(&stack, SIGNAL(canUndoChanged(bool))); + QSignalSpy undoTextChangedSpy(&stack, SIGNAL(undoTextChanged(QString))); + QSignalSpy canRedoChangedSpy(&stack, SIGNAL(canRedoChanged(bool))); + QSignalSpy redoTextChangedSpy(&stack, SIGNAL(redoTextChanged(QString))); + QString str; + + stack.clear(); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 0, // count + 0, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.push(new InsertCommand(&str, 0, "hello")); + QCOMPARE(str, QString("hello")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 1, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new InsertCommand(&str, 2, "123")); + QCOMPARE(str, QString("he123llo")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.clear(); + QCOMPARE(str, QString("he123llo")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 0, // count + 0, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + str.clear(); + stack.push(new InsertCommand(&str, 0, "hello")); + QCOMPARE(str, QString("hello")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 1, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new InsertCommand(&str, 2, "123")); + QCOMPARE(str, QString("he123llo")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.setIndex(0); + QCOMPARE(str, QString()); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 2, // count + 0, // index + false, // canUndo + "", // undoText + true, // canRedo + "insert", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.clear(); + QCOMPARE(str, QString()); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 0, // count + 0, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged +} + +void tst_QUndoStack::childCommand() +{ + QUndoStack stack; + QAction *undoAction = stack.createUndoAction(0, QString("foo")); + QAction *redoAction = stack.createRedoAction(0, QString("bar")); + QSignalSpy indexChangedSpy(&stack, SIGNAL(indexChanged(int))); + QSignalSpy cleanChangedSpy(&stack, SIGNAL(cleanChanged(bool))); + QSignalSpy canUndoChangedSpy(&stack, SIGNAL(canUndoChanged(bool))); + QSignalSpy undoTextChangedSpy(&stack, SIGNAL(undoTextChanged(QString))); + QSignalSpy canRedoChangedSpy(&stack, SIGNAL(canRedoChanged(bool))); + QSignalSpy redoTextChangedSpy(&stack, SIGNAL(redoTextChanged(QString))); + QString str; + + stack.push(new InsertCommand(&str, 0, "hello")); + QCOMPARE(str, QString("hello")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 1, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + QUndoCommand *cmd = new QUndoCommand(); + cmd->setText("ding"); + new InsertCommand(&str, 5, "world", cmd); + new RemoveCommand(&str, 4, 1, cmd); + stack.push(cmd); + QCOMPARE(str, QString("hellworld")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "ding", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.undo(); + QCOMPARE(str, QString("hello")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 1, // index + true, // canUndo + "insert", // undoText + true, // canRedo + "ding", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.redo(); + QCOMPARE(str, QString("hellworld")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "ding", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + delete undoAction; + delete redoAction; +} + +void tst_QUndoStack::macroBeginEnd() +{ + QUndoStack stack; + QAction *undoAction = stack.createUndoAction(0, QString("foo")); + QAction *redoAction = stack.createRedoAction(0, QString("bar")); + QSignalSpy indexChangedSpy(&stack, SIGNAL(indexChanged(int))); + QSignalSpy cleanChangedSpy(&stack, SIGNAL(cleanChanged(bool))); + QSignalSpy canUndoChangedSpy(&stack, SIGNAL(canUndoChanged(bool))); + QSignalSpy undoTextChangedSpy(&stack, SIGNAL(undoTextChanged(QString))); + QSignalSpy canRedoChangedSpy(&stack, SIGNAL(canRedoChanged(bool))); + QSignalSpy redoTextChangedSpy(&stack, SIGNAL(redoTextChanged(QString))); + QString str; + + stack.beginMacro("ding"); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 0, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.setClean(); // should do nothing + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 0, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.undo(); // should do nothing + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 0, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.redo(); // should do nothing + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 0, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.setIndex(0); // should do nothing + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 0, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.endMacro(); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 1, // index - endMacro() increments index + true, // canUndo + "ding", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new InsertCommand(&str, 0, "h")); + QCOMPARE(str, QString("h")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new InsertCommand(&str, 1, "owdy")); + QCOMPARE(str, QString("howdy")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 3, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.setIndex(2); + QCOMPARE(str, QString("h")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 2, // index + true, // canUndo + "insert", // undoText + true, // canRedo + "insert", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.beginMacro("dong"); // the "owdy" command gets deleted + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 2, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new InsertCommand(&str, 1, "ello")); + QCOMPARE(str, QString("hello")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 2, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.push(new RemoveCommand(&str, 1, 2)); + QCOMPARE(str, QString("hlo")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 2, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.beginMacro("dong2"); + QCOMPARE(str, QString("hlo")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 2, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.push(new RemoveCommand(&str, 1, 1)); + QCOMPARE(str, QString("ho")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 2, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.endMacro(); + QCOMPARE(str, QString("ho")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 2, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.endMacro(); + QCOMPARE(str, QString("ho")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 3, // index + true, // canUndo + "dong", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.undo(); + QCOMPARE(str, QString("h")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 2, // index + true, // canUndo + "insert", // undoText + true, // canRedo + "dong", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.undo(); + QCOMPARE(str, QString("")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 1, // index + true, // canUndo + "ding", // undoText + true, // canRedo + "insert", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.setIndex(3); + QCOMPARE(str, QString("ho")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 3, // index + true, // canUndo + "dong", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.setIndex(1); + QCOMPARE(str, QString()); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 1, // index + true, // canUndo + "ding", // undoText + true, // canRedo + "insert", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + delete undoAction; + delete redoAction; +} + +void tst_QUndoStack::compression() +{ + QUndoStack stack; + QAction *undoAction = stack.createUndoAction(0, QString("foo")); + QAction *redoAction = stack.createRedoAction(0, QString("bar")); + QSignalSpy indexChangedSpy(&stack, SIGNAL(indexChanged(int))); + QSignalSpy cleanChangedSpy(&stack, SIGNAL(cleanChanged(bool))); + QSignalSpy canUndoChangedSpy(&stack, SIGNAL(canUndoChanged(bool))); + QSignalSpy undoTextChangedSpy(&stack, SIGNAL(undoTextChanged(QString))); + QSignalSpy canRedoChangedSpy(&stack, SIGNAL(canRedoChanged(bool))); + QSignalSpy redoTextChangedSpy(&stack, SIGNAL(redoTextChanged(QString))); + QString str; + + AppendCommand::delete_cnt = 0; + + stack.push(new InsertCommand(&str, 0, "ene")); + QCOMPARE(str, QString("ene")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 1, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new AppendCommand(&str, " due")); // #1 + QCOMPARE(str, QString("ene due")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "append", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new AppendCommand(&str, " rike")); // #2 should merge + QCOMPARE(str, QString("ene due rike")); + QCOMPARE(AppendCommand::delete_cnt, 1); // #2 should be deleted + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "append", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.setClean(); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 2, // count + 2, // index + true, // canUndo + "append", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.push(new AppendCommand(&str, " fake")); // #3 should NOT merge, since the stack was clean + QCOMPARE(str, QString("ene due rike fake")); // and we want to be able to return to this state + QCOMPARE(AppendCommand::delete_cnt, 1); // #3 should not be deleted + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 3, // index + true, // canUndo + "append", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.undo(); + QCOMPARE(str, QString("ene due rike")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 3, // count + 2, // index + true, // canUndo + "append", // undoText + true, // canRedo + "append", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.undo(); + QCOMPARE(str, QString("ene")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 1, // index + true, // canUndo + "insert", // undoText + true, // canRedo + "append", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new AppendCommand(&str, "ma", true)); // #4 clean state gets deleted! + QCOMPARE(str, QString("enema")); + QCOMPARE(AppendCommand::delete_cnt, 3); // #1 got deleted + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "append", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new AppendCommand(&str, "trix")); // #5 should NOT merge + QCOMPARE(str, QString("enematrix")); + QCOMPARE(AppendCommand::delete_cnt, 3); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 3, // index + true, // canUndo + "append", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.undo(); + QCOMPARE(str, QString("enema")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 2, // index + true, // canUndo + "append", // undoText + true, // canRedo + "append", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + // and now for command compression inside macros + + stack.setClean(); + QCOMPARE(str, QString("enema")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 3, // count + 2, // index + true, // canUndo + "append", // undoText + true, // canRedo + "append", // redoText + true, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.beginMacro("ding"); + QCOMPARE(str, QString("enema")); + QCOMPARE(AppendCommand::delete_cnt, 4); // #5 gets deleted + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 2, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + true, // undoChanged + true); // redoChanged + + AppendCommand *merge_cmd = new AppendCommand(&str, "top"); + stack.push(merge_cmd); // #6 + QCOMPARE(merge_cmd->merged, false); + QCOMPARE(str, QString("enematop")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 2, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.push(new AppendCommand(&str, "eja")); // #7 should merge + QCOMPARE(str, QString("enematopeja")); + QCOMPARE(merge_cmd->merged, true); + QCOMPARE(AppendCommand::delete_cnt, 5); // #7 gets deleted + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 2, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + merge_cmd->merged = false; + + stack.push(new InsertCommand(&str, 2, "123")); // should not merge + QCOMPARE(str, QString("en123ematopeja")); + QCOMPARE(merge_cmd->merged, false); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 2, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.endMacro(); + QCOMPARE(str, QString("en123ematopeja")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 3, // index + true, // canUndo + "ding", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.undo(); + QCOMPARE(str, QString("enema")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 3, // count + 2, // index + true, // canUndo + "append", // undoText + true, // canRedo + "ding", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.redo(); + QCOMPARE(str, QString("en123ematopeja")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 3, // index + true, // canUndo + "ding", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + delete undoAction; + delete redoAction; +} + +void tst_QUndoStack::undoLimit() +{ + QUndoStack stack; + QAction *undoAction = stack.createUndoAction(0, QString("foo")); + QAction *redoAction = stack.createRedoAction(0, QString("bar")); + QSignalSpy indexChangedSpy(&stack, SIGNAL(indexChanged(int))); + QSignalSpy cleanChangedSpy(&stack, SIGNAL(cleanChanged(bool))); + QSignalSpy canUndoChangedSpy(&stack, SIGNAL(canUndoChanged(bool))); + QSignalSpy undoTextChangedSpy(&stack, SIGNAL(undoTextChanged(QString))); + QSignalSpy canRedoChangedSpy(&stack, SIGNAL(canRedoChanged(bool))); + QSignalSpy redoTextChangedSpy(&stack, SIGNAL(redoTextChanged(QString))); + AppendCommand::delete_cnt = 0; + QString str; + + QCOMPARE(stack.undoLimit(), 0); + stack.setUndoLimit(2); + QCOMPARE(stack.undoLimit(), 2); + + stack.push(new AppendCommand(&str, "1", true)); + QCOMPARE(str, QString("1")); + QCOMPARE(AppendCommand::delete_cnt, 0); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 1, // index + true, // canUndo + "append", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new AppendCommand(&str, "2", true)); + QCOMPARE(str, QString("12")); + QCOMPARE(AppendCommand::delete_cnt, 0); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "append", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.setClean(); + QCOMPARE(str, QString("12")); + QCOMPARE(AppendCommand::delete_cnt, 0); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 2, // count + 2, // index + true, // canUndo + "append", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.push(new AppendCommand(&str, "3", true)); + QCOMPARE(str, QString("123")); + QCOMPARE(AppendCommand::delete_cnt, 1); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "append", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new AppendCommand(&str, "4", true)); + QCOMPARE(str, QString("1234")); + QCOMPARE(AppendCommand::delete_cnt, 2); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "append", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.undo(); + QCOMPARE(str, QString("123")); + QCOMPARE(AppendCommand::delete_cnt, 2); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 1, // index + true, // canUndo + "append", // undoText + true, // canRedo + "append", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.undo(); + QCOMPARE(str, QString("12")); + QCOMPARE(AppendCommand::delete_cnt, 2); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 2, // count + 0, // index + false, // canUndo + "", // undoText + true, // canRedo + "append", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new AppendCommand(&str, "3", true)); + QCOMPARE(str, QString("123")); + QCOMPARE(AppendCommand::delete_cnt, 4); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 1, // index + true, // canUndo + "append", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new AppendCommand(&str, "4", true)); + QCOMPARE(str, QString("1234")); + QCOMPARE(AppendCommand::delete_cnt, 4); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "append", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new AppendCommand(&str, "5", true)); + QCOMPARE(str, QString("12345")); + QCOMPARE(AppendCommand::delete_cnt, 5); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "append", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.undo(); + QCOMPARE(str, QString("1234")); + QCOMPARE(AppendCommand::delete_cnt, 5); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 1, // index + true, // canUndo + "append", // undoText + true, // canRedo + "append", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.undo(); + QCOMPARE(str, QString("123")); + QCOMPARE(AppendCommand::delete_cnt, 5); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 0, // index + false, // canUndo + "", // undoText + true, // canRedo + "append", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new AppendCommand(&str, "4", true)); + QCOMPARE(str, QString("1234")); + QCOMPARE(AppendCommand::delete_cnt, 7); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 1, // index + true, // canUndo + "append", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new AppendCommand(&str, "5")); + QCOMPARE(str, QString("12345")); + QCOMPARE(AppendCommand::delete_cnt, 7); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "append", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new AppendCommand(&str, "6", true)); // should be merged + QCOMPARE(str, QString("123456")); + QCOMPARE(AppendCommand::delete_cnt, 8); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "append", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.beginMacro("foo"); + QCOMPARE(str, QString("123456")); + QCOMPARE(AppendCommand::delete_cnt, 8); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 2, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new AppendCommand(&str, "7", true)); + QCOMPARE(str, QString("1234567")); + QCOMPARE(AppendCommand::delete_cnt, 8); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 2, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.push(new AppendCommand(&str, "8")); + QCOMPARE(str, QString("12345678")); + QCOMPARE(AppendCommand::delete_cnt, 8); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 2, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.endMacro(); + QCOMPARE(str, QString("12345678")); + QCOMPARE(AppendCommand::delete_cnt, 9); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "foo", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.undo(); + QCOMPARE(str, QString("123456")); + QCOMPARE(AppendCommand::delete_cnt, 9); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 1, // index + true, // canUndo + "append", // undoText + true, // canRedo + "foo", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.undo(); + QCOMPARE(str, QString("1234")); + QCOMPARE(AppendCommand::delete_cnt, 9); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 0, // index + false, // canUndo + "", // undoText + true, // canRedo + "append", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged +} + +void tst_QUndoStack::commandTextFormat() +{ + QString binDir = QLibraryInfo::location(QLibraryInfo::BinariesPath); + + if (QProcess::execute(binDir + "/lrelease -version") != 0) { + QSKIP("lrelease is missing or broken", SkipAll); + } + + QVERIFY(!QProcess::execute(binDir + "/lrelease testdata/qundostack.ts")); + + QTranslator translator; + QVERIFY(translator.load("testdata/qundostack.qm")); + qApp->installTranslator(&translator); + + QUndoStack stack; + QAction *undo_action = stack.createUndoAction(0); + QAction *redo_action = stack.createRedoAction(0); + + QCOMPARE(undo_action->text(), QString("Undo-default-text")); + QCOMPARE(redo_action->text(), QString("Redo-default-text")); + + QString str; + + stack.push(new AppendCommand(&str, "foo")); + QCOMPARE(undo_action->text(), QString("undo-prefix append undo-suffix")); + QCOMPARE(redo_action->text(), QString("Redo-default-text")); + + stack.push(new InsertCommand(&str, 0, "bar")); + stack.undo(); + QCOMPARE(undo_action->text(), QString("undo-prefix append undo-suffix")); + QCOMPARE(redo_action->text(), QString("redo-prefix insert redo-suffix")); + + stack.undo(); + QCOMPARE(undo_action->text(), QString("Undo-default-text")); + QCOMPARE(redo_action->text(), QString("redo-prefix append redo-suffix")); + + qApp->removeTranslator(&translator); +} + +void tst_QUndoStack::separateUndoText() +{ + QUndoStack stack; + QAction *undo_action = stack.createUndoAction(0); + QAction *redo_action = stack.createRedoAction(0); + + QUndoCommand *command1 = new IdleCommand(); + QUndoCommand *command2 = new IdleCommand(); + stack.push(command1); + stack.push(command2); + stack.undo(); + + QCOMPARE(undo_action->text(), QString("Undo idle-action")); + QCOMPARE(redo_action->text(), QString("Redo idle-action")); + QCOMPARE(command1->actionText(), QString("idle-action")); + + QCOMPARE(command1->text(), QString("idle-item")); + QCOMPARE(stack.text(0), QString("idle-item")); + + command1->setText("idle"); + QCOMPARE(command1->actionText(), QString("idle")); + QCOMPARE(command1->text(), QString("idle")); + + command1->setText("idle-item\nidle-action"); + QCOMPARE(command1->actionText(), QString("idle-action")); + QCOMPARE(command1->text(), QString("idle-item")); +} + +QTEST_MAIN(tst_QUndoStack) + +#include "tst_qundostack.moc" diff --git a/tests/auto/widgets/util/util.pro b/tests/auto/widgets/util/util.pro new file mode 100644 index 0000000000..b7fe35ce4c --- /dev/null +++ b/tests/auto/widgets/util/util.pro @@ -0,0 +1,11 @@ +TEMPLATE=subdirs +SUBDIRS=\ + qcompleter \ + qscroller \ + qsystemtrayicon \ + qundogroup \ + qundostack \ + +symbian:SUBDIRS -= \ + qsystemtrayicon \ + |