diff options
Diffstat (limited to 'tests/auto/corelib')
23 files changed, 1747 insertions, 79 deletions
diff --git a/tests/auto/corelib/io/qdebug/tst_qdebug.cpp b/tests/auto/corelib/io/qdebug/tst_qdebug.cpp index 7147405f3b..b43ea7cfa5 100644 --- a/tests/auto/corelib/io/qdebug/tst_qdebug.cpp +++ b/tests/auto/corelib/io/qdebug/tst_qdebug.cpp @@ -51,6 +51,7 @@ private slots: void qDebugQChar() const; void qDebugQString() const; void qDebugQStringRef() const; + void qDebugQStringView() const; void qDebugQLatin1String() const; void qDebugQByteArray() const; void qDebugQFlags() const; @@ -492,6 +493,46 @@ void tst_QDebug::qDebugQStringRef() const } } +void tst_QDebug::qDebugQStringView() const +{ + /* Use a basic string. */ + { + QLatin1String file, function; + int line = 0; + const QStringView inView = QStringViewLiteral("input"); + + MessageHandlerSetter mhs(myMessageHandler); + { qDebug() << inView; } +#ifndef QT_NO_MESSAGELOGCONTEXT + file = QLatin1String(__FILE__); line = __LINE__ - 2; function = QLatin1String(Q_FUNC_INFO); +#endif + QCOMPARE(s_msgType, QtDebugMsg); + QCOMPARE(s_msg, QLatin1String("\"input\"")); + QCOMPARE(QLatin1String(s_file), file); + QCOMPARE(s_line, line); + QCOMPARE(QLatin1String(s_function), function); + } + + /* Use a null QStringView. */ + { + QString file, function; + int line = 0; + + const QStringView inView; + + MessageHandlerSetter mhs(myMessageHandler); + { qDebug() << inView; } +#ifndef QT_NO_MESSAGELOGCONTEXT + file = __FILE__; line = __LINE__ - 2; function = Q_FUNC_INFO; +#endif + QCOMPARE(s_msgType, QtDebugMsg); + QCOMPARE(s_msg, QLatin1String("\"\"")); + QCOMPARE(QLatin1String(s_file), file); + QCOMPARE(s_line, line); + QCOMPARE(QLatin1String(s_function), function); + } +} + void tst_QDebug::qDebugQLatin1String() const { QString file, function; diff --git a/tests/auto/corelib/io/qurl/tst_qurl.cpp b/tests/auto/corelib/io/qurl/tst_qurl.cpp index ebc240c285..1cbb7ad19c 100644 --- a/tests/auto/corelib/io/qurl/tst_qurl.cpp +++ b/tests/auto/corelib/io/qurl/tst_qurl.cpp @@ -1384,18 +1384,6 @@ void tst_QUrl::compat_constructor_01_data() void tst_QUrl::compat_constructor_01() { - /* The following should work as expected: - * - * QUrlOperator op; - * op.copy( QString( "Makefile" ), - * QString("ftp://rms:grmpf12@nibbler/home/rms/tmp"), - * false ); - * - * as well as the following: - * - * QUrlOperator op; - * op.copy(QString("ftp://ftp.qt-project.org/qt/INSTALL"), "."); - */ QFETCH( QString, urlStr ); { @@ -1425,11 +1413,6 @@ void tst_QUrl::compat_constructor_02_data() void tst_QUrl::compat_constructor_02() { - /* The following should work as expected: - * - * QUrlOperator op( "ftp://ftp.qt-project.org/qt" ); - * op.copy(QString("INSTALL"), "."); - */ QFETCH( QString, urlStr ); QFETCH( QString, fileName ); diff --git a/tests/auto/corelib/itemmodels/itemmodels.pro b/tests/auto/corelib/itemmodels/itemmodels.pro index c1d75cc2cb..a09f03a7b4 100644 --- a/tests/auto/corelib/itemmodels/itemmodels.pro +++ b/tests/auto/corelib/itemmodels/itemmodels.pro @@ -7,6 +7,7 @@ qtHaveModule(gui): SUBDIRS += \ qabstractproxymodel \ qidentityproxymodel \ qitemselectionmodel \ + qsortfilterproxymodel_recursive \ qtHaveModule(widgets) { SUBDIRS += \ diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp b/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp index bc8f0c1c51..54582ee4c3 100644 --- a/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp +++ b/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp @@ -148,6 +148,9 @@ private slots: void sourceLayoutChangeLeavesValidPersistentIndexes(); void rowMoveLeavesValidPersistentIndexes(); + void emitLayoutChangedOnlyIfSortingChanged_data(); + void emitLayoutChangedOnlyIfSortingChanged(); + protected: void buildHierarchy(const QStringList &data, QAbstractItemModel *model); void checkHierarchy(const QStringList &data, const QAbstractItemModel *model); @@ -2057,8 +2060,6 @@ static void checkSortedTableModel(const QAbstractItemModel *model, const QString void tst_QSortFilterProxyModel::changeSourceDataKeepsStableSorting_qtbug1548() { - QSKIP("This test will fail, see QTBUG-1548"); - // Check that emitting dataChanged from the source model // for a change of a role which is not the sorting role // doesn't alter the sorting. In this case, we sort on the DisplayRole, @@ -3568,6 +3569,13 @@ void tst_QSortFilterProxyModel::testParentLayoutChanged() parentItem = item; } } + // item 0 + // item 10 + // - item 1 + // - item 11 + // - item 2 + // - item 12 + // ... QSortFilterProxyModel proxy; proxy.sort(0, Qt::AscendingOrder); @@ -3609,11 +3617,12 @@ void tst_QSortFilterProxyModel::testParentLayoutChanged() QVERIFY(proxy2ParentsChangedSpy.isValid()); QStandardItem *item = model.invisibleRootItem()->child(1)->child(1); + QCOMPARE(item->text(), QStringLiteral("item 11")); // Ensure mapped: proxy.mapFromSource(model.indexFromItem(item)); - item->setData("Changed"); + item->setText("Changed"); QCOMPARE(dataChangedSpy.size(), 1); QCOMPARE(layoutAboutToBeChangedSpy.size(), 1); @@ -4026,7 +4035,7 @@ public: } - QModelIndex mapToSource(const QModelIndex &proxyIndex) const + QModelIndex mapToSource(const QModelIndex &proxyIndex) const override { Q_ASSERT(sourceModel()); return QSortFilterProxyModel::mapToSource(proxyIndex); @@ -4353,5 +4362,129 @@ void tst_QSortFilterProxyModel::rowMoveLeavesValidPersistentIndexes() QVERIFY(persistentIndex.parent().isValid()); } +void tst_QSortFilterProxyModel::emitLayoutChangedOnlyIfSortingChanged_data() +{ + QTest::addColumn<int>("changedRow"); + QTest::addColumn<Qt::ItemDataRole>("changedRole"); + QTest::addColumn<QString>("newData"); + QTest::addColumn<QString>("expectedSourceRowTexts"); + QTest::addColumn<QString>("expectedProxyRowTexts"); + QTest::addColumn<int>("expectedLayoutChanged"); + + // Starting point: + // a source model with 8,7,6,5,4,3,2,1 + // a proxy model keeping only even rows and sorting them, therefore showing 2,4,6,8 + + // When setData changes ordering, layoutChanged should be emitted + QTest::newRow("ordering_change") << 0 << Qt::DisplayRole << "0" << "07654321" << "0246" << 1; + + // When setData on visible row doesn't change ordering, layoutChanged should not be emitted + QTest::newRow("no_ordering_change") << 6 << Qt::DisplayRole << "0" << "87654301" << "0468" << 0; + + // When setData happens on a filtered out row, layoutChanged should not be emitted + QTest::newRow("filtered_out") << 1 << Qt::DisplayRole << "9" << "89654321" << "2468" << 0; + + // When setData makes a row visible, layoutChanged should not be emitted (rowsInserted is emitted instead) + QTest::newRow("make_row_visible") << 7 << Qt::DisplayRole << "0" << "87654320" << "02468" << 0; + + // When setData makes a row hidden, layoutChanged should not be emitted (rowsRemoved is emitted instead) + QTest::newRow("make_row_hidden") << 4 << Qt::DisplayRole << "1" << "87651321" << "268" << 0; + + // When setData happens on an unrelated role, layoutChanged should not be emitted + QTest::newRow("unrelated_role") << 0 << Qt::DecorationRole << "" << "87654321" << "2468" << 0; + + // When many changes happen together... and trigger removal, insertion, and layoutChanged + QTest::newRow("many_changes") << -1 << Qt::DisplayRole << "3,4,2,5,6,0,7,9" << "34256079" << "0246" << 1; + + // When many changes happen together... and trigger removal, insertion, but no change in ordering of visible rows => no layoutChanged + QTest::newRow("many_changes_no_layoutChanged") << -1 << Qt::DisplayRole << "7,5,4,3,2,1,0,8" << "75432108" << "0248" << 0; +} + +void tst_QSortFilterProxyModel::emitLayoutChangedOnlyIfSortingChanged() +{ + QFETCH(int, changedRow); + QFETCH(QString, newData); + QFETCH(Qt::ItemDataRole, changedRole); + QFETCH(QString, expectedSourceRowTexts); + QFETCH(QString, expectedProxyRowTexts); + QFETCH(int, expectedLayoutChanged); + + // Custom version of QStringListModel which supports emitting dataChanged for many rows at once + class CustomStringListModel : public QAbstractListModel + { + public: + bool setData(const QModelIndex &index, const QVariant &value, int role) override + { + if (index.row() >= 0 && index.row() < lst.size() + && (role == Qt::EditRole || role == Qt::DisplayRole)) { + lst.replace(index.row(), value.toString()); + emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole}); + return true; + } + return false; + } + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override + { + if (role == Qt::DisplayRole || role == Qt::EditRole) + return lst.at(index.row()); + return QVariant(); + } + int rowCount(const QModelIndex & = QModelIndex()) const override + { + return lst.count(); + } + + void replaceData(const QStringList &newData) + { + lst = newData; + emit dataChanged(index(0, 0), index(rowCount()-1, 0), {Qt::DisplayRole, Qt::EditRole}); + } + + void emitDecorationChangedSignal() + { + const QModelIndex idx = index(0, 0); + emit dataChanged(idx, idx, {Qt::DecorationRole}); + } + private: + QStringList lst; + }; + CustomStringListModel model; + QStringList strings; + for (auto i = 8; i >= 1; --i) + strings.append(QString::number(i)); + model.replaceData(strings); + QCOMPARE(rowTexts(&model), QStringLiteral("87654321")); + + class FilterEvenRowsProxyModel : public QSortFilterProxyModel + { + public: + bool filterAcceptsRow(int srcRow, const QModelIndex& srcParent) const override + { + return sourceModel()->index(srcRow, 0, srcParent).data().toInt() % 2 == 0; + } + }; + + FilterEvenRowsProxyModel proxy; + proxy.sort(0); + proxy.setSourceModel(&model); + QCOMPARE(rowTexts(&proxy), QStringLiteral("2468")); + + QSignalSpy modelDataChangedSpy(&model, &QAbstractItemModel::dataChanged); + QSignalSpy proxyLayoutChangedSpy(&proxy, &QAbstractItemModel::layoutChanged); + + if (changedRole == Qt::DecorationRole) + model.emitDecorationChangedSignal(); + else if (changedRow == -1) + model.replaceData(newData.split(QLatin1Char(','))); + else + model.setData(model.index(changedRow, 0), newData, changedRole); + + QCOMPARE(rowTexts(&model), expectedSourceRowTexts); + QCOMPARE(rowTexts(&proxy), expectedProxyRowTexts); + QCOMPARE(modelDataChangedSpy.size(), 1); + QCOMPARE(proxyLayoutChangedSpy.size(), expectedLayoutChanged); +} + + QTEST_MAIN(tst_QSortFilterProxyModel) #include "tst_qsortfilterproxymodel.moc" diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/.gitignore b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/.gitignore new file mode 100644 index 0000000000..2007aaabbd --- /dev/null +++ b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/.gitignore @@ -0,0 +1 @@ +tst_qsortfilterproxymodel_recursive diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/qsortfilterproxymodel_recursive.pro b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/qsortfilterproxymodel_recursive.pro new file mode 100644 index 0000000000..a8b793dbc6 --- /dev/null +++ b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/qsortfilterproxymodel_recursive.pro @@ -0,0 +1,8 @@ +CONFIG += testcase +CONFIG += parallel_test +TARGET = tst_qsortfilterproxymodel_recursive + +QT += testlib + +SOURCES += tst_qsortfilterproxymodel_recursive.cpp +DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/tst_qsortfilterproxymodel_recursive.cpp b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/tst_qsortfilterproxymodel_recursive.cpp new file mode 100644 index 0000000000..54c79e0893 --- /dev/null +++ b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/tst_qsortfilterproxymodel_recursive.cpp @@ -0,0 +1,727 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, authors Filipe Azevedo <filipe.azevedo@kdab.com> and David Faure <david.faure@kdab.com> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QTest> +#include <QSignalSpy> + +#include <QtCore/QSortFilterProxyModel> +#include <QtGui/QStandardItem> + +Q_DECLARE_METATYPE(QModelIndex) + +class ModelSignalSpy : public QObject { + Q_OBJECT +public: + explicit ModelSignalSpy(QAbstractItemModel &model) { + connect(&model, &QAbstractItemModel::rowsInserted, this, &ModelSignalSpy::onRowsInserted); + connect(&model, &QAbstractItemModel::rowsRemoved, this, &ModelSignalSpy::onRowsRemoved); + connect(&model, &QAbstractItemModel::rowsAboutToBeInserted, this, &ModelSignalSpy::onRowsAboutToBeInserted); + connect(&model, &QAbstractItemModel::rowsAboutToBeRemoved, this, &ModelSignalSpy::onRowsAboutToBeRemoved); + connect(&model, &QAbstractItemModel::rowsMoved, this, &ModelSignalSpy::onRowsMoved); + connect(&model, &QAbstractItemModel::dataChanged, this, &ModelSignalSpy::onDataChanged); + connect(&model, &QAbstractItemModel::layoutChanged, this, &ModelSignalSpy::onLayoutChanged); + connect(&model, &QAbstractItemModel::modelReset, this, &ModelSignalSpy::onModelReset); + } + + QStringList mSignals; + +private Q_SLOTS: + void onRowsInserted(QModelIndex p, int start, int end) { + mSignals << QLatin1String("rowsInserted(") + textForRowSpy(p, start, end) + ')'; + } + void onRowsRemoved(QModelIndex p, int start, int end) { + mSignals << QLatin1String("rowsRemoved(") + textForRowSpy(p, start, end) + ')'; + } + void onRowsAboutToBeInserted(QModelIndex p, int start, int end) { + mSignals << QLatin1String("rowsAboutToBeInserted(") + textForRowSpy(p, start, end) + ')'; + } + void onRowsAboutToBeRemoved(QModelIndex p, int start, int end) { + mSignals << QLatin1String("rowsAboutToBeRemoved(") + textForRowSpy(p, start, end) + ')'; + } + void onRowsMoved(QModelIndex,int,int,QModelIndex,int) { + mSignals << QStringLiteral("rowsMoved"); + } + void onDataChanged(const QModelIndex &from, const QModelIndex& ) { + mSignals << QStringLiteral("dataChanged(%1)").arg(from.data().toString()); + } + void onLayoutChanged() { + mSignals << QStringLiteral("layoutChanged"); + } + void onModelReset() { + mSignals << QStringLiteral("modelReset"); + } +private: + QString textForRowSpy(const QModelIndex &parent, int start, int end) + { + QString txt = parent.data().toString(); + if (!txt.isEmpty()) + txt += QLatin1Char('.'); + txt += QString::number(start+1); + if (start != end) + txt += QLatin1Char('-') + QString::number(end+1); + return txt; + } +}; + +class TestModel : public QSortFilterProxyModel +{ + Q_OBJECT +public: + TestModel(QAbstractItemModel *sourceModel) + : QSortFilterProxyModel() + { + setRecursiveFiltering(true); + setSourceModel(sourceModel); + } + + virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override + { + return sourceModel()->index(sourceRow, 0, sourceParent).data(Qt::UserRole +1).toBool() + && QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent); + } +}; + +// Represents this tree +// - A +// - - B +// - - - C +// - - - D +// - - E +// as a single string, englobing children in brackets, like this: +// [A[B[C D] E]] +// In addition, items that match the filtering (data(UserRole+1) == true) have a * after their value. +static QString treeAsString(const QAbstractItemModel &model, const QModelIndex &parent = QModelIndex()) +{ + QString ret; + const int rowCount = model.rowCount(parent); + if (rowCount > 0) { + ret += QLatin1Char('['); + for (int row = 0 ; row < rowCount; ++row) { + if (row > 0) { + ret += ' '; + } + const QModelIndex child = model.index(row, 0, parent); + ret += child.data().toString(); + if (child.data(Qt::UserRole+1).toBool()) + ret += QLatin1Char('*'); + ret += treeAsString(model, child); + } + ret += QLatin1Char(']'); + } + return ret; +} + +// Fill a tree model based on a string representation (see treeAsString) +static void fillModel(QStandardItemModel &model, const QString &str) +{ + QCOMPARE(str.count('['), str.count(']')); + QStandardItem *item = 0; + QString data; + for ( int i = 0 ; i < str.length() ; ++i ) { + const QChar ch = str.at(i); + if ((ch == '[' || ch == ']' || ch == ' ') && !data.isEmpty()) { + if (data.endsWith('*')) { + item->setData(true, Qt::UserRole + 1); + data.chop(1); + } + item->setText(data); + data.clear(); + } + if (ch == '[') { + // Create new child + QStandardItem *child = new QStandardItem; + if (item) + item->appendRow(child); + else + model.appendRow(child); + item = child; + } else if (ch == ']') { + // Go up to parent + item = item->parent(); + } else if (ch == ' ') { + // Create new sibling + QStandardItem *child = new QStandardItem; + QStandardItem *parent = item->parent(); + if (parent) + parent->appendRow(child); + else + model.appendRow(child); + item = child; + } else { + data += ch; + } + } +} + +class tst_QSortFilterProxyModel_Recursive : public QObject +{ + Q_OBJECT +private: +private Q_SLOTS: + void testInitialFiltering_data() + { + QTest::addColumn<QString>("sourceStr"); + QTest::addColumn<QString>("proxyStr"); + + QTest::newRow("empty") << "[]" << ""; + QTest::newRow("no") << "[1]" << ""; + QTest::newRow("yes") << "[1*]" << "[1*]"; + QTest::newRow("second") << "[1 2*]" << "[2*]"; + QTest::newRow("child_yes") << "[1 2[2.1*]]" << "[2[2.1*]]"; + QTest::newRow("grandchild_yes") << "[1 2[2.1[2.1.1*]]]" << "[2[2.1[2.1.1*]]]"; + // 1, 3.1 and 4.2.1 match, so their parents are in the model + QTest::newRow("more") << "[1* 2[2.1] 3[3.1*] 4[4.1 4.2[4.2.1*]]]" << "[1* 3[3.1*] 4[4.2[4.2.1*]]]"; + } + + void testInitialFiltering() + { + QFETCH(QString, sourceStr); + QFETCH(QString, proxyStr); + + QStandardItemModel model; + fillModel(model, sourceStr); + QCOMPARE(treeAsString(model), sourceStr); + + TestModel proxy(&model); + QCOMPARE(treeAsString(proxy), proxyStr); + } + + // Test changing a role that is unrelated to the filtering. + void testUnrelatedDataChange() + { + QStandardItemModel model; + const QString sourceStr = QStringLiteral("[1[1.1[1.1.1*]]]"); + fillModel(model, sourceStr); + QCOMPARE(treeAsString(model), sourceStr); + + TestModel proxy(&model); + QCOMPARE(treeAsString(proxy), sourceStr); + + ModelSignalSpy spy(proxy); + QStandardItem *item_1_1_1 = model.item(0)->child(0)->child(0); + + // When changing the text on the item + item_1_1_1->setText(QStringLiteral("ME")); + + QCOMPARE(treeAsString(proxy), QStringLiteral("[1[1.1[ME*]]]")); + + QCOMPARE(spy.mSignals, QStringList() + << QStringLiteral("dataChanged(ME)") + << QStringLiteral("dataChanged(1.1)") + << QStringLiteral("dataChanged(1)")); + } + + // Test changing a role that is unrelated to the filtering, in a hidden item. + void testHiddenDataChange() + { + QStandardItemModel model; + const QString sourceStr = QStringLiteral("[1[1.1[1.1.1]]]"); + fillModel(model, sourceStr); + QCOMPARE(treeAsString(model), sourceStr); + + TestModel proxy(&model); + QCOMPARE(treeAsString(proxy), QString()); + + ModelSignalSpy spy(proxy); + QStandardItem *item_1_1_1 = model.item(0)->child(0)->child(0); + + // When changing the text on a hidden item + item_1_1_1->setText(QStringLiteral("ME")); + + QCOMPARE(treeAsString(proxy), QString()); + QCOMPARE(spy.mSignals, QStringList()); + } + + // Test that we properly react to a data-changed signal in a descendant and include all required rows + void testDataChangeIn_data() + { + QTest::addColumn<QString>("sourceStr"); + QTest::addColumn<QString>("initialProxyStr"); + QTest::addColumn<QString>("add"); // set the flag on this item + QTest::addColumn<QString>("expectedProxyStr"); + QTest::addColumn<QStringList>("expectedSignals"); + + QTest::newRow("toplevel") << "[1]" << "" << "1" << "[1*]" + << (QStringList() << QStringLiteral("rowsAboutToBeInserted(1)") << QStringLiteral("rowsInserted(1)")); + QTest::newRow("show_parents") << "[1[1.1[1.1.1]]]" << "" << "1.1.1" << "[1[1.1[1.1.1*]]]" + << (QStringList() << QStringLiteral("rowsAboutToBeInserted(1)") << QStringLiteral("rowsInserted(1)")); + + const QStringList insert_1_1_1 = QStringList() + << QStringLiteral("rowsAboutToBeInserted(1.1.1)") + << QStringLiteral("rowsInserted(1.1.1)") + << QStringLiteral("dataChanged(1.1)") + << QStringLiteral("dataChanged(1)") + ; + QTest::newRow("parent_visible") << "[1[1.1*[1.1.1]]]" << "[1[1.1*]]" << "1.1.1" << "[1[1.1*[1.1.1*]]]" + << insert_1_1_1; + + QTest::newRow("sibling_visible") << "[1[1.1[1.1.1 1.1.2*]]]" << "[1[1.1[1.1.2*]]]" << "1.1.1" << "[1[1.1[1.1.1* 1.1.2*]]]" + << insert_1_1_1; + + QTest::newRow("visible_cousin") << "[1[1.1[1.1.1 1.1.2[1.1.2.1*]]]]" << "[1[1.1[1.1.2[1.1.2.1*]]]]" << "1.1.1" << "[1[1.1[1.1.1* 1.1.2[1.1.2.1*]]]]" + << insert_1_1_1; + + QTest::newRow("show_parent") << "[1[1.1[1.1.1 1.1.2] 1.2*]]" << "[1[1.2*]]" << "1.1.1" << "[1[1.1[1.1.1*] 1.2*]]" + << (QStringList() + << QStringLiteral("rowsAboutToBeInserted(1.1)") + << QStringLiteral("rowsInserted(1.1)") + << QStringLiteral("dataChanged(1)")); + + QTest::newRow("with_children") << "[1[1.1[1.1.1[1.1.1.1*]]] 2*]" << "[1[1.1[1.1.1[1.1.1.1*]]] 2*]" << "1.1.1" << "[1[1.1[1.1.1*[1.1.1.1*]]] 2*]" + << (QStringList() + << QStringLiteral("dataChanged(1.1.1)") + << QStringLiteral("dataChanged(1.1)") + << QStringLiteral("dataChanged(1)")); + + } + + void testDataChangeIn() + { + QFETCH(QString, sourceStr); + QFETCH(QString, initialProxyStr); + QFETCH(QString, add); + QFETCH(QString, expectedProxyStr); + QFETCH(QStringList, expectedSignals); + + QStandardItemModel model; + fillModel(model, sourceStr); + QCOMPARE(treeAsString(model), sourceStr); + + TestModel proxy(&model); + QCOMPARE(treeAsString(proxy), initialProxyStr); + + ModelSignalSpy spy(proxy); + // When changing the data on the designated item to show this row + QStandardItem *itemToChange = itemByText(model, add); + QVERIFY(!itemToChange->data().toBool()); + itemToChange->setData(true); + + // The proxy should update as expected + QCOMPARE(treeAsString(proxy), expectedProxyStr); + + //qDebug() << spy.mSignals; + QCOMPARE(spy.mSignals, expectedSignals); + } + + void testDataChangeOut_data() + { + QTest::addColumn<QString>("sourceStr"); + QTest::addColumn<QString>("initialProxyStr"); + QTest::addColumn<QString>("remove"); // unset the flag on this item + QTest::addColumn<QString>("expectedProxyStr"); + QTest::addColumn<QStringList>("expectedSignals"); + + const QStringList remove1_1_1 = (QStringList() + << QStringLiteral("rowsAboutToBeRemoved(1.1.1)") + << QStringLiteral("rowsRemoved(1.1.1)") + << QStringLiteral("dataChanged(1.1)") + << QStringLiteral("dataChanged(1)")); + + QTest::newRow("toplevel") << "[1*]" << "[1*]" << "1" << "" + << (QStringList() << QStringLiteral("rowsAboutToBeRemoved(1)") << QStringLiteral("rowsRemoved(1)")); + + QTest::newRow("hide_parent") << "[1[1.1[1.1.1*]]]" << "[1[1.1[1.1.1*]]]" << "1.1.1" << "" << + (QStringList() + << QStringLiteral("rowsAboutToBeRemoved(1.1.1)") + << QStringLiteral("rowsRemoved(1.1.1)") + << QStringLiteral("rowsAboutToBeRemoved(1.1)") + << QStringLiteral("rowsRemoved(1.1)") + << QStringLiteral("rowsAboutToBeRemoved(1)") + << QStringLiteral("rowsRemoved(1)")); + + QTest::newRow("parent_visible") << "[1[1.1*[1.1.1*]]]" << "[1[1.1*[1.1.1*]]]" << "1.1.1" << "[1[1.1*]]" + << remove1_1_1; + + QTest::newRow("visible") << "[1[1.1[1.1.1* 1.1.2*]]]" << "[1[1.1[1.1.1* 1.1.2*]]]" << "1.1.1" << "[1[1.1[1.1.2*]]]" + << remove1_1_1; + QTest::newRow("visible_cousin") << "[1[1.1[1.1.1* 1.1.2[1.1.2.1*]]]]" << "[1[1.1[1.1.1* 1.1.2[1.1.2.1*]]]]" << "1.1.1" << "[1[1.1[1.1.2[1.1.2.1*]]]]" + << remove1_1_1; + + // The following tests trigger the removal of an ascendant. + QTest::newRow("remove_parent") << "[1[1.1[1.1.1* 1.1.2] 1.2*]]" << "[1[1.1[1.1.1*] 1.2*]]" << "1.1.1" << "[1[1.2*]]" + << (QStringList() + << QStringLiteral("rowsAboutToBeRemoved(1.1.1)") + << QStringLiteral("rowsRemoved(1.1.1)") + << QStringLiteral("rowsAboutToBeRemoved(1.1)") + << QStringLiteral("rowsRemoved(1.1)") + << QStringLiteral("dataChanged(1)")); + + QTest::newRow("with_children") << "[1[1.1[1.1.1*[1.1.1.1*]]] 2*]" << "[1[1.1[1.1.1*[1.1.1.1*]]] 2*]" << "1.1.1" << "[1[1.1[1.1.1[1.1.1.1*]]] 2*]" + << (QStringList() + << QStringLiteral("dataChanged(1.1.1)") + << QStringLiteral("dataChanged(1.1)") + << QStringLiteral("dataChanged(1)")); + + QTest::newRow("last_visible") << "[1[1.1[1.1.1* 1.1.2]]]" << "[1[1.1[1.1.1*]]]" << "1.1.1" << "" + << (QStringList() + << QStringLiteral("rowsAboutToBeRemoved(1.1.1)") + << QStringLiteral("rowsRemoved(1.1.1)") + << QStringLiteral("rowsAboutToBeRemoved(1.1)") + << QStringLiteral("rowsRemoved(1.1)") + << QStringLiteral("rowsAboutToBeRemoved(1)") + << QStringLiteral("rowsRemoved(1)")); + + } + + void testDataChangeOut() + { + QFETCH(QString, sourceStr); + QFETCH(QString, initialProxyStr); + QFETCH(QString, remove); + QFETCH(QString, expectedProxyStr); + QFETCH(QStringList, expectedSignals); + + QStandardItemModel model; + fillModel(model, sourceStr); + QCOMPARE(treeAsString(model), sourceStr); + + TestModel proxy(&model); + QCOMPARE(treeAsString(proxy), initialProxyStr); + + ModelSignalSpy spy(proxy); + + // When changing the data on the designated item to exclude this row again + QStandardItem *itemToChange = itemByText(model, remove); + QVERIFY(itemToChange->data().toBool()); + itemToChange->setData(false); + + // The proxy should update as expected + QCOMPARE(treeAsString(proxy), expectedProxyStr); + + //qDebug() << spy.mSignals; + QCOMPARE(spy.mSignals, expectedSignals); + } + + void testInsert() + { + QStandardItemModel model; + const QString sourceStr = QStringLiteral("[1[1.1[1.1.1]]]"); + fillModel(model, sourceStr); + QCOMPARE(treeAsString(model), sourceStr); + + TestModel proxy(&model); + QCOMPARE(treeAsString(proxy), QString()); + + ModelSignalSpy spy(proxy); + QStandardItem *item_1_1_1 = model.item(0)->child(0)->child(0); + QStandardItem *item_1_1_1_1 = new QStandardItem(QStringLiteral("1.1.1.1")); + item_1_1_1_1->setData(true); + item_1_1_1->appendRow(item_1_1_1_1); + QCOMPARE(treeAsString(proxy), QStringLiteral("[1[1.1[1.1.1[1.1.1.1*]]]]")); + + QCOMPARE(spy.mSignals, QStringList() << QStringLiteral("rowsAboutToBeInserted(1)") + << QStringLiteral("rowsInserted(1)")); + } + + // Start from [1[1.1[1.1.1 1.1.2[1.1.2.1*]]]] + // where 1.1.1 is hidden but 1.1 is shown, we want to insert a shown child in 1.1.1. + // The proxy ensures dataChanged is called on 1.1, + // so that 1.1.1 and 1.1.1.1 are included in the model. + void testInsertCousin() + { + QStandardItemModel model; + const QString sourceStr = QStringLiteral("[1[1.1[1.1.1 1.1.2[1.1.2.1*]]]]"); + fillModel(model, sourceStr); + QCOMPARE(treeAsString(model), sourceStr); + + TestModel proxy(&model); + QCOMPARE(treeAsString(proxy), QStringLiteral("[1[1.1[1.1.2[1.1.2.1*]]]]")); + + ModelSignalSpy spy(proxy); + { + QStandardItem *item_1_1_1_1 = new QStandardItem(QStringLiteral("1.1.1.1")); + item_1_1_1_1->setData(true); + QStandardItem *item_1_1_1 = model.item(0)->child(0)->child(0); + item_1_1_1->appendRow(item_1_1_1_1); + } + + QCOMPARE(treeAsString(proxy), QStringLiteral("[1[1.1[1.1.1[1.1.1.1*] 1.1.2[1.1.2.1*]]]]")); + //qDebug() << spy.mSignals; + QCOMPARE(spy.mSignals, QStringList() + << QStringLiteral("rowsAboutToBeInserted(1.1.1)") + << QStringLiteral("rowsInserted(1.1.1)") + << QStringLiteral("dataChanged(1.1)") + << QStringLiteral("dataChanged(1)")); + } + + void testInsertWithChildren() + { + QStandardItemModel model; + const QString sourceStr = QStringLiteral("[1[1.1]]"); + fillModel(model, sourceStr); + QCOMPARE(treeAsString(model), sourceStr); + + TestModel proxy(&model); + QCOMPARE(treeAsString(proxy), QString()); + + ModelSignalSpy spy(proxy); + { + QStandardItem *item_1_1_1 = new QStandardItem(QStringLiteral("1.1.1")); + QStandardItem *item_1_1_1_1 = new QStandardItem(QStringLiteral("1.1.1.1")); + item_1_1_1_1->setData(true); + item_1_1_1->appendRow(item_1_1_1_1); + + QStandardItem *item_1_1 = model.item(0)->child(0); + item_1_1->appendRow(item_1_1_1); + } + + QCOMPARE(treeAsString(proxy), QStringLiteral("[1[1.1[1.1.1[1.1.1.1*]]]]")); + QCOMPARE(spy.mSignals, QStringList() + << QStringLiteral("rowsAboutToBeInserted(1)") + << QStringLiteral("rowsInserted(1)")); + } + + void testInsertIntoVisibleWithChildren() + { + QStandardItemModel model; + const QString sourceStr = QStringLiteral("[1[1.1[1.1.1*]]]"); + fillModel(model, sourceStr); + QCOMPARE(treeAsString(model), sourceStr); + + TestModel proxy(&model); + QCOMPARE(treeAsString(proxy), sourceStr); + + ModelSignalSpy spy(proxy); + { + QStandardItem *item_1_1_2 = new QStandardItem(QStringLiteral("1.1.2")); + QStandardItem *item_1_1_2_1 = new QStandardItem(QStringLiteral("1.1.2.1")); + item_1_1_2_1->setData(true); + item_1_1_2->appendRow(item_1_1_2_1); + + QStandardItem *item_1_1 = model.item(0)->child(0); + item_1_1->appendRow(item_1_1_2); + } + + QCOMPARE(treeAsString(proxy), QStringLiteral("[1[1.1[1.1.1* 1.1.2[1.1.2.1*]]]]")); + QCOMPARE(spy.mSignals, QStringList() + << QStringLiteral("rowsAboutToBeInserted(1.1.2)") + << QStringLiteral("rowsInserted(1.1.2)")); + } + + void testInsertBefore() + { + QStandardItemModel model; + const QString sourceStr = "[1[1.1[1.1.2*]]]"; + fillModel(model, sourceStr); + QCOMPARE(treeAsString(model), sourceStr); + + TestModel proxy(&model); + QCOMPARE(treeAsString(proxy), sourceStr); + + ModelSignalSpy spy(proxy); + { + QStandardItem *item_1_1_1 = new QStandardItem("1.1.1"); + + QStandardItem *item_1_1 = model.item(0)->child(0); + item_1_1->insertRow(0, item_1_1_1); + } + + QCOMPARE(treeAsString(proxy), QString("[1[1.1[1.1.2*]]]")); + QCOMPARE(spy.mSignals, QStringList()); + } + + void testInsertHidden() // inserting filtered-out rows shouldn't emit anything + { + QStandardItemModel model; + const QString sourceStr = QStringLiteral("[1[1.1]]"); + fillModel(model, sourceStr); + QCOMPARE(treeAsString(model), sourceStr); + + TestModel proxy(&model); + QCOMPARE(treeAsString(proxy), QString()); + + ModelSignalSpy spy(proxy); + { + QStandardItem *item_1_1_1 = new QStandardItem(QStringLiteral("1.1.1")); + QStandardItem *item_1_1_1_1 = new QStandardItem(QStringLiteral("1.1.1.1")); + item_1_1_1->appendRow(item_1_1_1_1); + + QStandardItem *item_1_1 = model.item(0)->child(0); + item_1_1->appendRow(item_1_1_1); + } + + QCOMPARE(treeAsString(proxy), QString()); + QCOMPARE(spy.mSignals, QStringList()); + } + + void testConsecutiveInserts_data() + { + testInitialFiltering_data(); + } + + void testConsecutiveInserts() + { + QFETCH(QString, sourceStr); + QFETCH(QString, proxyStr); + + QStandardItemModel model; + TestModel proxy(&model); // this time the proxy listens to the model while we fill it + + fillModel(model, sourceStr); + QCOMPARE(treeAsString(model), sourceStr); + QCOMPARE(treeAsString(proxy), proxyStr); + } + + void testRemove_data() + { + QTest::addColumn<QString>("sourceStr"); + QTest::addColumn<QString>("initialProxyStr"); + QTest::addColumn<QString>("remove"); // remove this item + QTest::addColumn<QString>("expectedProxyStr"); + QTest::addColumn<QStringList>("expectedSignals"); + + const QStringList remove1_1_1 = (QStringList() << QStringLiteral("rowsAboutToBeRemoved(1.1.1)") << QStringLiteral("rowsRemoved(1.1.1)")); + + QTest::newRow("toplevel") << "[1* 2* 3*]" << "[1* 2* 3*]" << "1" << "[2* 3*]" + << (QStringList() << QStringLiteral("rowsAboutToBeRemoved(1)") << QStringLiteral("rowsRemoved(1)")); + + QTest::newRow("remove_hidden") << "[1 2* 3*]" << "[2* 3*]" << "1" << "[2* 3*]" << QStringList(); + + QTest::newRow("parent_hidden") << "[1[1.1[1.1.1]]]" << "" << "1.1.1" << "" << QStringList(); + + QTest::newRow("child_hidden") << "[1[1.1*[1.1.1]]]" << "[1[1.1*]]" << "1.1.1" << "[1[1.1*]]" << QStringList(); + + QTest::newRow("parent_visible") << "[1[1.1*[1.1.1*]]]" << "[1[1.1*[1.1.1*]]]" << "1.1.1" << "[1[1.1*]]" + << remove1_1_1; + + QTest::newRow("visible") << "[1[1.1[1.1.1* 1.1.2*]]]" << "[1[1.1[1.1.1* 1.1.2*]]]" << "1.1.1" << "[1[1.1[1.1.2*]]]" + << remove1_1_1; + QTest::newRow("visible_cousin") << "[1[1.1[1.1.1* 1.1.2[1.1.2.1*]]]]" << "[1[1.1[1.1.1* 1.1.2[1.1.2.1*]]]]" << "1.1.1" << "[1[1.1[1.1.2[1.1.2.1*]]]]" + << remove1_1_1; + + // The following tests trigger the removal of an ascendant. + // We could optimize the rows{AboutToBe,}Removed(1.1.1) away... + + QTest::newRow("remove_parent") << "[1[1.1[1.1.1* 1.1.2] 1.2*]]" << "[1[1.1[1.1.1*] 1.2*]]" << "1.1.1" << "[1[1.2*]]" + << (QStringList() + << QStringLiteral("rowsAboutToBeRemoved(1.1.1)") + << QStringLiteral("rowsRemoved(1.1.1)") + << QStringLiteral("rowsAboutToBeRemoved(1.1)") + << QStringLiteral("rowsRemoved(1.1)") + << QStringLiteral("dataChanged(1)")); + + QTest::newRow("with_children") << "[1[1.1[1.1.1[1.1.1.1*]]] 2*]" << "[1[1.1[1.1.1[1.1.1.1*]]] 2*]" << "1.1.1" << "[2*]" + << (QStringList() + << QStringLiteral("rowsAboutToBeRemoved(1.1.1)") + << QStringLiteral("rowsRemoved(1.1.1)") + << QStringLiteral("rowsAboutToBeRemoved(1)") + << QStringLiteral("rowsRemoved(1)")); + + QTest::newRow("last_visible") << "[1[1.1[1.1.1* 1.1.2]]]" << "[1[1.1[1.1.1*]]]" << "1.1.1" << "" + << (QStringList() + << QStringLiteral("rowsAboutToBeRemoved(1.1.1)") + << QStringLiteral("rowsRemoved(1.1.1)") + << QStringLiteral("rowsAboutToBeRemoved(1)") + << QStringLiteral("rowsRemoved(1)")); + + + } + + void testRemove() + { + QFETCH(QString, sourceStr); + QFETCH(QString, initialProxyStr); + QFETCH(QString, remove); + QFETCH(QString, expectedProxyStr); + QFETCH(QStringList, expectedSignals); + + QStandardItemModel model; + fillModel(model, sourceStr); + QCOMPARE(treeAsString(model), sourceStr); + + TestModel proxy(&model); + QCOMPARE(treeAsString(proxy), initialProxyStr); + + ModelSignalSpy spy(proxy); + QStandardItem *itemToRemove = itemByText(model, remove); + QVERIFY(itemToRemove); + if (itemToRemove->parent()) + itemToRemove->parent()->removeRow(itemToRemove->row()); + else + model.removeRow(itemToRemove->row()); + QCOMPARE(treeAsString(proxy), expectedProxyStr); + + //qDebug() << spy.mSignals; + QCOMPARE(spy.mSignals, expectedSignals); + } + + void testStandardFiltering_data() + { + QTest::addColumn<QString>("sourceStr"); + QTest::addColumn<QString>("initialProxyStr"); + QTest::addColumn<QString>("filter"); + QTest::addColumn<QString>("expectedProxyStr"); + + QTest::newRow("select_child") << "[1[1.1[1.1.1* 1.1.2*]]]" << "[1[1.1[1.1.1* 1.1.2*]]]" + << "1.1.2" << "[1[1.1[1.1.2*]]]"; + + QTest::newRow("filter_all_out") << "[1[1.1[1.1.1*]]]" << "[1[1.1[1.1.1*]]]" + << "test" << ""; + + QTest::newRow("select_parent") << "[1[1.1[1.1.1*[child*] 1.1.2*]]]" << "[1[1.1[1.1.1*[child*] 1.1.2*]]]" + << "1.1.1" << "[1[1.1[1.1.1*]]]"; + + } + + void testStandardFiltering() + { + QFETCH(QString, sourceStr); + QFETCH(QString, initialProxyStr); + QFETCH(QString, filter); + QFETCH(QString, expectedProxyStr); + + QStandardItemModel model; + fillModel(model, sourceStr); + QCOMPARE(treeAsString(model), sourceStr); + + TestModel proxy(&model); + QCOMPARE(treeAsString(proxy), initialProxyStr); + + ModelSignalSpy spy(proxy); + + //qDebug() << "setFilterFixedString"; + proxy.setFilterFixedString(filter); + + QCOMPARE(treeAsString(proxy), expectedProxyStr); + + } + +private: + QStandardItem *itemByText(const QStandardItemModel& model, const QString &text) const { + QModelIndexList list = model.match(model.index(0, 0), Qt::DisplayRole, text, 1, Qt::MatchRecursive); + return list.isEmpty() ? 0 : model.itemFromIndex(list.first()); + } +}; + +QTEST_GUILESS_MAIN(tst_QSortFilterProxyModel_Recursive) +#include "tst_qsortfilterproxymodel_recursive.moc" diff --git a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp index 143bf2f95d..28c5c53744 100644 --- a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp +++ b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp @@ -155,7 +155,9 @@ void tst_QMetaType::defined() QCOMPARE(int(QMetaTypeId2<int*>::Defined), 0); QCOMPARE(int(QMetaTypeId2<CustomQObject::CustomQEnum>::Defined), 1); QCOMPARE(int(QMetaTypeId2<CustomGadget>::Defined), 1); + QCOMPARE(int(QMetaTypeId2<CustomGadget*>::Defined), 1); QVERIFY(!QMetaTypeId2<GadgetDerived>::Defined); + QVERIFY(!QMetaTypeId2<GadgetDerived*>::Defined); QVERIFY(int(QMetaTypeId2<CustomQObject*>::Defined)); QVERIFY(!QMetaTypeId2<CustomQObject>::Defined); QVERIFY(!QMetaTypeId2<CustomNonQObject>::Defined); @@ -397,6 +399,7 @@ void tst_QMetaType::typeName_data() QTest::newRow("CustomQObject*") << ::qMetaTypeId<CustomQObject*>() << QString::fromLatin1("CustomQObject*"); QTest::newRow("CustomGadget") << ::qMetaTypeId<CustomGadget>() << QString::fromLatin1("CustomGadget"); + QTest::newRow("CustomGadget*") << ::qMetaTypeId<CustomGadget*>() << QString::fromLatin1("CustomGadget*"); QTest::newRow("CustomQObject::CustomQEnum") << ::qMetaTypeId<CustomQObject::CustomQEnum>() << QString::fromLatin1("CustomQObject::CustomQEnum"); QTest::newRow("Qt::ArrowType") << ::qMetaTypeId<Qt::ArrowType>() << QString::fromLatin1("Qt::ArrowType"); } @@ -1684,6 +1687,7 @@ public: }; Q_DECLARE_METATYPE(MyGadget); +Q_DECLARE_METATYPE(MyGadget*); Q_DECLARE_METATYPE(const QMetaObject *); Q_DECLARE_METATYPE(Qt::ScrollBarPolicy); Q_DECLARE_METATYPE(MyGadget::MyEnum); @@ -1693,16 +1697,18 @@ void tst_QMetaType::metaObject_data() QTest::addColumn<int>("type"); QTest::addColumn<const QMetaObject*>("result"); QTest::addColumn<bool>("isGadget"); + QTest::addColumn<bool>("isGadgetPtr"); QTest::addColumn<bool>("isQObjectPtr"); - QTest::newRow("QObject") << int(QMetaType::QObjectStar) << &QObject::staticMetaObject << false << true; - QTest::newRow("QFile*") << ::qMetaTypeId<QFile*>() << &QFile::staticMetaObject << false << true; - QTest::newRow("MyObject*") << ::qMetaTypeId<MyObject*>() << &MyObject::staticMetaObject << false << true; - QTest::newRow("int") << int(QMetaType::Int) << static_cast<const QMetaObject *>(0) << false << false; - QTest::newRow("QEasingCurve") << ::qMetaTypeId<QEasingCurve>() << &QEasingCurve::staticMetaObject << true << false; - QTest::newRow("MyGadget") << ::qMetaTypeId<MyGadget>() << &MyGadget::staticMetaObject << true << false; - QTest::newRow("MyEnum") << ::qMetaTypeId<MyGadget::MyEnum>() << &MyGadget::staticMetaObject << false << false; - QTest::newRow("Qt::ScrollBarPolicy") << ::qMetaTypeId<Qt::ScrollBarPolicy>() << &QObject::staticQtMetaObject << false << false; + QTest::newRow("QObject") << int(QMetaType::QObjectStar) << &QObject::staticMetaObject << false << false << true; + QTest::newRow("QFile*") << ::qMetaTypeId<QFile*>() << &QFile::staticMetaObject << false << false << true; + QTest::newRow("MyObject*") << ::qMetaTypeId<MyObject*>() << &MyObject::staticMetaObject << false << false << true; + QTest::newRow("int") << int(QMetaType::Int) << static_cast<const QMetaObject *>(0) << false << false << false; + QTest::newRow("QEasingCurve") << ::qMetaTypeId<QEasingCurve>() << &QEasingCurve::staticMetaObject << true << false << false; + QTest::newRow("MyGadget") << ::qMetaTypeId<MyGadget>() << &MyGadget::staticMetaObject << true << false << false; + QTest::newRow("MyGadget*") << ::qMetaTypeId<MyGadget*>() << &MyGadget::staticMetaObject << false << true << false; + QTest::newRow("MyEnum") << ::qMetaTypeId<MyGadget::MyEnum>() << &MyGadget::staticMetaObject << false << false << false; + QTest::newRow("Qt::ScrollBarPolicy") << ::qMetaTypeId<Qt::ScrollBarPolicy>() << &QObject::staticQtMetaObject << false << false << false; } @@ -1711,12 +1717,14 @@ void tst_QMetaType::metaObject() QFETCH(int, type); QFETCH(const QMetaObject *, result); QFETCH(bool, isGadget); + QFETCH(bool, isGadgetPtr); QFETCH(bool, isQObjectPtr); QCOMPARE(QMetaType::metaObjectForType(type), result); QMetaType mt(type); QCOMPARE(mt.metaObject(), result); QCOMPARE(!!(mt.flags() & QMetaType::IsGadget), isGadget); + QCOMPARE(!!(mt.flags() & QMetaType::PointerToGadget), isGadgetPtr); QCOMPARE(!!(mt.flags() & QMetaType::PointerToQObject), isQObjectPtr); } diff --git a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp index a546cad225..37b052bf1d 100644 --- a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp +++ b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp @@ -1205,8 +1205,6 @@ void tst_QFuture::pause() Interface.reportFinished(); } -const int resultCount = 1000; - class ResultObject : public QObject { Q_OBJECT diff --git a/tests/auto/corelib/thread/qsemaphore/tst_qsemaphore.cpp b/tests/auto/corelib/thread/qsemaphore/tst_qsemaphore.cpp index 2970b2e118..ba470a77c9 100644 --- a/tests/auto/corelib/thread/qsemaphore/tst_qsemaphore.cpp +++ b/tests/auto/corelib/thread/qsemaphore/tst_qsemaphore.cpp @@ -42,6 +42,7 @@ private slots: void tryAcquireWithTimeout(); void tryAcquireWithTimeoutStarvation(); void producerConsumer(); + void raii(); }; static QSemaphore *semaphore = 0; @@ -417,5 +418,54 @@ void tst_QSemaphore::producerConsumer() consumer.wait(); } +void tst_QSemaphore::raii() +{ + QSemaphore sem; + + QCOMPARE(sem.available(), 0); + + // basic operation: + { + QSemaphoreReleaser r0; + const QSemaphoreReleaser r1(sem); + const QSemaphoreReleaser r2(sem, 2); + + QCOMPARE(r0.semaphore(), nullptr); + QCOMPARE(r1.semaphore(), &sem); + QCOMPARE(r2.semaphore(), &sem); + } + + QCOMPARE(sem.available(), 3); + + // cancel: + { + const QSemaphoreReleaser r1(sem); + QSemaphoreReleaser r2(sem, 2); + + QCOMPARE(r2.cancel(), &sem); + QCOMPARE(r2.semaphore(), nullptr); + } + + QCOMPARE(sem.available(), 4); + + // move-assignment: + { + const QSemaphoreReleaser r1(sem); + QSemaphoreReleaser r2(sem, 2); + + QCOMPARE(sem.available(), 4); + + r2 = QSemaphoreReleaser(); + + QCOMPARE(sem.available(), 6); + + r2 = QSemaphoreReleaser(sem, 42); + + QCOMPARE(sem.available(), 6); + } + + QCOMPARE(sem.available(), 49); +} + QTEST_MAIN(tst_QSemaphore) #include "tst_qsemaphore.moc" diff --git a/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp b/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp index fdc4ecb5c8..1dcd642023 100644 --- a/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp +++ b/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp @@ -961,21 +961,6 @@ void tst_QThreadPool::cancel() QSemaphore sem(0); QSemaphore startedThreads(0); - class SemaphoreReleaser - { - QSemaphore &sem; - int n; - Q_DISABLE_COPY(SemaphoreReleaser) - public: - explicit SemaphoreReleaser(QSemaphore &sem, int n) - : sem(sem), n(n) {} - - ~SemaphoreReleaser() - { - sem.release(n); - } - }; - class BlockingRunnable : public QRunnable { public: @@ -1014,7 +999,7 @@ void tst_QThreadPool::cancel() // ensure that the QThreadPool doesn't deadlock if any of the checks fail // and cause an early return: - const SemaphoreReleaser semReleaser(sem, runs); + const QSemaphoreReleaser semReleaser(sem, runs); count.store(0); QAtomicInt dtorCounter = 0; @@ -1048,21 +1033,6 @@ void tst_QThreadPool::tryTake() QSemaphore sem(0); QSemaphore startedThreads(0); - class SemaphoreReleaser - { - QSemaphore &sem; - int n; - Q_DISABLE_COPY(SemaphoreReleaser) - public: - explicit SemaphoreReleaser(QSemaphore &sem, int n) - : sem(sem), n(n) {} - - ~SemaphoreReleaser() - { - sem.release(n); - } - }; - class BlockingRunnable : public QRunnable { public: @@ -1101,7 +1071,7 @@ void tst_QThreadPool::tryTake() // ensure that the QThreadPool doesn't deadlock if any of the checks fail // and cause an early return: - const SemaphoreReleaser semReleaser(sem, Runs); + const QSemaphoreReleaser semReleaser(sem, Runs); count.store(0); QAtomicInt dtorCounter = 0; diff --git a/tests/auto/corelib/tools/qchar/tst_qchar.cpp b/tests/auto/corelib/tools/qchar/tst_qchar.cpp index e5a6e953d3..76309d914b 100644 --- a/tests/auto/corelib/tools/qchar/tst_qchar.cpp +++ b/tests/auto/corelib/tools/qchar/tst_qchar.cpp @@ -36,6 +36,8 @@ class tst_QChar : public QObject { Q_OBJECT private slots: + void fromChar16_t(); + void fromWchar_t(); void operator_eqeq_null(); void operators_data(); void operators(); @@ -72,6 +74,33 @@ private slots: void unicodeVersion(); }; +QT_WARNING_PUSH +QT_WARNING_DISABLE_DEPRECATED + +void tst_QChar::fromChar16_t() +{ +#if !defined(Q_OS_WIN) || defined(Q_COMPILER_UNICODE_STRINGS) + QChar aUmlaut = u'\u00E4'; // German small letter a-umlaut + QCOMPARE(aUmlaut, QChar(0xE4)); + QChar replacementCharacter = u'\uFFFD'; + QCOMPARE(replacementCharacter, QChar(QChar::ReplacementCharacter)); +#else + QSKIP("This test requires C++11 char16_t support enabled in the compiler."); +#endif +} + +void tst_QChar::fromWchar_t() +{ +#if defined(Q_OS_WIN) + QChar aUmlaut = L'\u00E4'; // German small letter a-umlaut + QCOMPARE(aUmlaut, QChar(0xE4)); + QChar replacementCharacter = L'\uFFFD'; + QCOMPARE(replacementCharacter, QChar(QChar::ReplacementCharacter)); +#else + QSKIP("This is a Windows-only test."); +#endif +} + void tst_QChar::operator_eqeq_null() { { diff --git a/tests/auto/corelib/tools/qlatin1string/tst_qlatin1string.cpp b/tests/auto/corelib/tools/qlatin1string/tst_qlatin1string.cpp index a68671d899..c8373b6ae9 100644 --- a/tests/auto/corelib/tools/qlatin1string/tst_qlatin1string.cpp +++ b/tests/auto/corelib/tools/qlatin1string/tst_qlatin1string.cpp @@ -48,6 +48,7 @@ private Q_SLOTS: void midLeftRight(); void nullString(); void emptyString(); + void iterators(); void relationalOperators_data(); void relationalOperators(); }; @@ -155,6 +156,22 @@ void tst_QLatin1String::emptyString() } } +void tst_QLatin1String::iterators() +{ + QLatin1String hello("hello"); + QLatin1String olleh("olleh"); + + QVERIFY(std::equal(hello.begin(), hello.end(), + olleh.rbegin())); + QVERIFY(std::equal(hello.rbegin(), hello.rend(), + QT_MAKE_CHECKED_ARRAY_ITERATOR(olleh.begin(), olleh.size()))); + + QVERIFY(std::equal(hello.cbegin(), hello.cend(), + olleh.rbegin())); + QVERIFY(std::equal(hello.crbegin(), hello.crend(), + QT_MAKE_CHECKED_ARRAY_ITERATOR(olleh.begin(), olleh.size()))); +} + void tst_QLatin1String::relationalOperators_data() { QTest::addColumn<QLatin1StringContainer>("lhs"); diff --git a/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp b/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp index 5d13d9e454..9248970c96 100644 --- a/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp +++ b/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp @@ -2450,8 +2450,8 @@ void tst_QLocale::textDirection_data() default: break; } - QString testName = QLocalePrivate::languageToCode(QLocale::Language(language)); - QTest::newRow(testName.toLatin1().constData()) << language << int(QLocale::AnyScript) << rightToLeft; + const QLatin1String testName = QLocalePrivate::languageToCode(QLocale::Language(language)); + QTest::newRow(qPrintable(testName)) << language << int(QLocale::AnyScript) << rightToLeft; } QTest::newRow("pa_Arab") << int(QLocale::Punjabi) << int(QLocale::ArabicScript) << true; QTest::newRow("uz_Arab") << int(QLocale::Uzbek) << int(QLocale::ArabicScript) << true; diff --git a/tests/auto/corelib/tools/qstring/tst_qstring.cpp b/tests/auto/corelib/tools/qstring/tst_qstring.cpp index 03436375dd..3f118d04ce 100644 --- a/tests/auto/corelib/tools/qstring/tst_qstring.cpp +++ b/tests/auto/corelib/tools/qstring/tst_qstring.cpp @@ -6067,14 +6067,6 @@ void tst_QString::compare_data() lower += QChar(QChar::lowSurrogate(0x10428)); QTest::newRow("data8") << upper << lower << -1 << 0; - QTest::newRow("vectorized-boundaries-7") << QString("1234567") << QString("abcdefg") << -1 << -1; - QTest::newRow("vectorized-boundaries-8") << QString("12345678") << QString("abcdefgh") << -1 << -1; - QTest::newRow("vectorized-boundaries-9") << QString("123456789") << QString("abcdefghi") << -1 << -1; - - QTest::newRow("vectorized-boundaries-15") << QString("123456789012345") << QString("abcdefghiklmnop") << -1 << -1; - QTest::newRow("vectorized-boundaries-16") << QString("1234567890123456") << QString("abcdefghiklmnopq") << -1 << -1; - QTest::newRow("vectorized-boundaries-17") << QString("12345678901234567") << QString("abcdefghiklmnopqr") << -1 << -1; - // embedded nulls // These don't work as of now. It's OK that these don't work since \0 is not a valid unicode /*QTest::newRow("data10") << QString(QByteArray("\0", 1)) << QString(QByteArray("\0", 1)) << 0 << 0; @@ -6083,6 +6075,44 @@ void tst_QString::compare_data() QTest::newRow("data13") << QString("ab\0c") << QString(QByteArray("ab\0c", 4)) << 0 << 0; QTest::newRow("data14") << QString(QByteArray("ab\0c", 4)) << QString("abc") << -1 << -1; QTest::newRow("data15") << QString("abc") << QString(QByteArray("ab\0c", 4)) << 1 << 1;*/ + + // All tests below (generated by the 3 for-loops) are meant to excercise the vectorized versions + // of ucstrncmp. + + QString in1, in2; + for (int i = 0; i < 70; ++i) { + in1 += QString::number(i % 10); + in2 += QString::number((70 - i + 1) % 10); + } + Q_ASSERT(in1.length() == in2.length()); + Q_ASSERT(in1 != in2); + Q_ASSERT(in1.at(0) < in2.at(0)); + for (int i = 0; i < in1.length(); ++i) { + Q_ASSERT(in1.at(i) != in2.at(i)); + } + + for (int i = 1; i <= 65; ++i) { + QString inp1 = in1.left(i); + QString inp2 = in2.left(i); + QTest::addRow("all-different-%d", i) << inp1 << inp2 << -1 << -1; + } + + for (int i = 1; i <= 65; ++i) { + QString start(i - 1, 'a'); + + QString in = start + QLatin1Char('a'); + QTest::addRow("all-same-%d", i) << in << in << 0 << 0; + + QString in2 = start + QLatin1Char('b'); + QTest::addRow("last-different-%d", i) << in << in2 << -1 << -1; + } + + for (int i = 0; i < 16; ++i) { + QString in1(16, 'a'); + QString in2 = in1; + in2[i] = 'b'; + QTest::addRow("all-same-except-char-%d", i) << in1 << in2 << -1 << -1; + } } static bool isLatin(const QString &s) diff --git a/tests/auto/corelib/tools/qstringapisymmetry/tst_qstringapisymmetry.cpp b/tests/auto/corelib/tools/qstringapisymmetry/tst_qstringapisymmetry.cpp index 9d9b47b61e..36f479ca01 100644 --- a/tests/auto/corelib/tools/qstringapisymmetry/tst_qstringapisymmetry.cpp +++ b/tests/auto/corelib/tools/qstringapisymmetry/tst_qstringapisymmetry.cpp @@ -160,6 +160,45 @@ private Q_SLOTS: //void compare_const_char_star_const_char_star_data() { compare_data(); } //void compare_const_char_star_const_char_star() { compare_impl<const char *, const char *>(); } +private: + void mid_data(); + template <typename String> void mid_impl(); + + void left_data(); + template <typename String> void left_impl(); + + void right_data(); + template <typename String> void right_impl(); + +private Q_SLOTS: + + void mid_QString_data() { mid_data(); } + void mid_QString() { mid_impl<QString>(); } + void mid_QStringRef_data() { mid_data(); } + void mid_QStringRef() { mid_impl<QStringRef>(); } + void mid_QLatin1String_data() { mid_data(); } + void mid_QLatin1String() { mid_impl<QLatin1String>(); } + void mid_QByteArray_data() { mid_data(); } + void mid_QByteArray() { mid_impl<QByteArray>(); } + + void left_QString_data() { left_data(); } + void left_QString() { left_impl<QString>(); } + void left_QStringRef_data() { left_data(); } + void left_QStringRef() { left_impl<QStringRef>(); } + void left_QLatin1String_data() { left_data(); } + void left_QLatin1String() { left_impl<QLatin1String>(); } + void left_QByteArray_data() { left_data(); } + void left_QByteArray() { left_impl<QByteArray>(); } + + void right_QString_data() { right_data(); } + void right_QString() { right_impl<QString>(); } + void right_QStringRef_data() { right_data(); } + void right_QStringRef() { right_impl<QStringRef>(); } + void right_QLatin1String_data() { right_data(); } + void right_QLatin1String() { right_impl<QLatin1String>(); } + void right_QByteArray_data() { right_data(); } + void right_QByteArray() { right_impl<QByteArray>(); } + // // UTF-16-only checks: // @@ -300,6 +339,178 @@ void tst_QStringApiSymmetry::compare_impl() const #undef CHECK } +static QString empty = QLatin1String(""); +// the tests below rely on the fact that these objects' names match their contents: +static QString a = QStringLiteral("a"); +static QString b = QStringLiteral("b"); +static QString c = QStringLiteral("c"); +static QString ab = QStringLiteral("ab"); +static QString bc = QStringLiteral("bc"); +static QString abc = QStringLiteral("abc"); + +void tst_QStringApiSymmetry::mid_data() +{ + QTest::addColumn<QStringRef>("unicode"); + QTest::addColumn<QLatin1String>("latin1"); + QTest::addColumn<int>("pos"); + QTest::addColumn<int>("n"); + QTest::addColumn<QStringRef>("result"); + QTest::addColumn<QStringRef>("result2"); + + QTest::addRow("null") << QStringRef() << QLatin1String() << 0 << 0 << QStringRef() << QStringRef(); + QTest::addRow("empty") << QStringRef(&empty) << QLatin1String("") << 0 << 0 << QStringRef(&empty) << QStringRef(&empty); + + // Some classes' mid() implementations have a wide contract, others a narrow one + // so only test valid arguents here: +#define ROW(base, p, n, r1, r2) \ + QTest::addRow("%s%d%d", #base, p, n) << QStringRef(&base) << QLatin1String(#base) << p << n << QStringRef(&r1) << QStringRef(&r2) + + ROW(a, 0, 0, a, empty); + ROW(a, 0, 1, a, a); + ROW(a, 1, 0, empty, empty); + + ROW(ab, 0, 0, ab, empty); + ROW(ab, 0, 1, ab, a); + ROW(ab, 0, 2, ab, ab); + ROW(ab, 1, 0, b, empty); + ROW(ab, 1, 1, b, b); + ROW(ab, 2, 0, empty, empty); + + ROW(abc, 0, 0, abc, empty); + ROW(abc, 0, 1, abc, a); + ROW(abc, 0, 2, abc, ab); + ROW(abc, 0, 3, abc, abc); + ROW(abc, 1, 0, bc, empty); + ROW(abc, 1, 1, bc, b); + ROW(abc, 1, 2, bc, bc); + ROW(abc, 2, 0, c, empty); + ROW(abc, 2, 1, c, c); + ROW(abc, 3, 0, empty, empty); +#undef ROW +} + +template <typename String> +void tst_QStringApiSymmetry::mid_impl() +{ + QFETCH(const QStringRef, unicode); + QFETCH(const QLatin1String, latin1); + QFETCH(const int, pos); + QFETCH(const int, n); + QFETCH(const QStringRef, result); + QFETCH(const QStringRef, result2); + + const auto utf8 = unicode.toUtf8(); + + const auto s = make<String>(unicode, latin1, utf8); + + const auto mid = s.mid(pos); + const auto mid2 = s.mid(pos, n); + + QVERIFY(mid == result); + QCOMPARE(mid.isNull(), result.isNull()); + QCOMPARE(mid.isEmpty(), result.isEmpty()); + + QVERIFY(mid2 == result2); + QCOMPARE(mid2.isNull(), result2.isNull()); + QCOMPARE(mid2.isEmpty(), result2.isEmpty()); +} + +void tst_QStringApiSymmetry::left_data() +{ + QTest::addColumn<QStringRef>("unicode"); + QTest::addColumn<QLatin1String>("latin1"); + QTest::addColumn<int>("n"); + QTest::addColumn<QStringRef>("result"); + + QTest::addRow("null") << QStringRef() << QLatin1String() << 0 << QStringRef(); + QTest::addRow("empty") << QStringRef(&empty) << QLatin1String("") << 0 << QStringRef(&empty); + + // Some classes' left() implementations have a wide contract, others a narrow one + // so only test valid arguents here: +#define ROW(base, n, res) \ + QTest::addRow("%s%d", #base, n) << QStringRef(&base) << QLatin1String(#base) << n << QStringRef(&res); + + ROW(a, 0, empty); + ROW(a, 1, a); + + ROW(ab, 0, empty); + ROW(ab, 1, a); + ROW(ab, 2, ab); + + ROW(abc, 0, empty); + ROW(abc, 1, a); + ROW(abc, 2, ab); + ROW(abc, 3, abc); +#undef ROW +} + +template <typename String> +void tst_QStringApiSymmetry::left_impl() +{ + QFETCH(const QStringRef, unicode); + QFETCH(const QLatin1String, latin1); + QFETCH(const int, n); + QFETCH(const QStringRef, result); + + const auto utf8 = unicode.toUtf8(); + + const auto s = make<String>(unicode, latin1, utf8); + + const auto left = s.left(n); + + QVERIFY(left == result); + QCOMPARE(left.isNull(), result.isNull()); + QCOMPARE(left.isEmpty(), result.isEmpty()); +} + +void tst_QStringApiSymmetry::right_data() +{ + QTest::addColumn<QStringRef>("unicode"); + QTest::addColumn<QLatin1String>("latin1"); + QTest::addColumn<int>("n"); + QTest::addColumn<QStringRef>("result"); + + QTest::addRow("null") << QStringRef() << QLatin1String() << 0 << QStringRef(); + QTest::addRow("empty") << QStringRef(&empty) << QLatin1String("") << 0 << QStringRef(&empty); + + // Some classes' right() implementations have a wide contract, others a narrow one + // so only test valid arguents here: +#define ROW(base, n, res) \ + QTest::addRow("%s%d", #base, n) << QStringRef(&base) << QLatin1String(#base) << n << QStringRef(&res); + + ROW(a, 0, empty); + ROW(a, 1, a); + + ROW(ab, 0, empty); + ROW(ab, 1, b); + ROW(ab, 2, ab); + + ROW(abc, 0, empty); + ROW(abc, 1, c); + ROW(abc, 2, bc); + ROW(abc, 3, abc); +#undef ROW +} + +template <typename String> +void tst_QStringApiSymmetry::right_impl() +{ + QFETCH(const QStringRef, unicode); + QFETCH(const QLatin1String, latin1); + QFETCH(const int, n); + QFETCH(const QStringRef, result); + + const auto utf8 = unicode.toUtf8(); + + const auto s = make<String>(unicode, latin1, utf8); + + const auto right = s.right(n); + + QVERIFY(right == result); + QCOMPARE(right.isNull(), result.isNull()); + QCOMPARE(right.isEmpty(), result.isEmpty()); +} + // // // UTF-16-only checks: diff --git a/tests/auto/corelib/tools/qstringlist/tst_qstringlist.cpp b/tests/auto/corelib/tools/qstringlist/tst_qstringlist.cpp index 2385aa992c..9f054190e5 100644 --- a/tests/auto/corelib/tools/qstringlist/tst_qstringlist.cpp +++ b/tests/auto/corelib/tools/qstringlist/tst_qstringlist.cpp @@ -264,6 +264,15 @@ void tst_QStringList::contains() QVERIFY(list.contains("ARTHUR", Qt::CaseInsensitive)); QVERIFY(list.contains("dent", Qt::CaseInsensitive)); QVERIFY(!list.contains("hans", Qt::CaseInsensitive)); + + QVERIFY(list.contains(QLatin1String("arthur"))); + QVERIFY(!list.contains(QLatin1String("ArthuR"))); + QVERIFY(!list.contains(QLatin1String("Hans"))); + QVERIFY(list.contains(QLatin1String("arthur"), Qt::CaseInsensitive)); + QVERIFY(list.contains(QLatin1String("ArthuR"), Qt::CaseInsensitive)); + QVERIFY(list.contains(QLatin1String("ARTHUR"), Qt::CaseInsensitive)); + QVERIFY(list.contains(QLatin1String("dent"), Qt::CaseInsensitive)); + QVERIFY(!list.contains(QLatin1String("hans"), Qt::CaseInsensitive)); } void tst_QStringList::removeDuplicates_data() diff --git a/tests/auto/corelib/tools/qstringview/.gitignore b/tests/auto/corelib/tools/qstringview/.gitignore new file mode 100644 index 0000000000..5f757d448a --- /dev/null +++ b/tests/auto/corelib/tools/qstringview/.gitignore @@ -0,0 +1 @@ +tst_qstringview diff --git a/tests/auto/corelib/tools/qstringview/qstringview.pro b/tests/auto/corelib/tools/qstringview/qstringview.pro new file mode 100644 index 0000000000..e0e9973c91 --- /dev/null +++ b/tests/auto/corelib/tools/qstringview/qstringview.pro @@ -0,0 +1,6 @@ +CONFIG += testcase +TARGET = tst_qstringview +QT = core testlib +contains(QT_CONFIG, c++14):CONFIG *= c++14 +contains(QT_CONFIG, c++1z):CONFIG *= c++1z +SOURCES += tst_qstringview.cpp diff --git a/tests/auto/corelib/tools/qstringview/tst_qstringview.cpp b/tests/auto/corelib/tools/qstringview/tst_qstringview.cpp new file mode 100644 index 0000000000..2e671040dc --- /dev/null +++ b/tests/auto/corelib/tools/qstringview/tst_qstringview.cpp @@ -0,0 +1,446 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com> +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QStringView> +#include <QString> +#include <QChar> +#include <QStringRef> + +#include <QTest> + +#include <string> + +template <typename T> +using CanConvert = std::is_convertible<T, QStringView>; + +// +// QChar +// + +Q_STATIC_ASSERT(CanConvert< QString >::value); +Q_STATIC_ASSERT(CanConvert<const QString >::value); +Q_STATIC_ASSERT(CanConvert< QString&>::value); +Q_STATIC_ASSERT(CanConvert<const QString&>::value); + +Q_STATIC_ASSERT(CanConvert< QStringRef >::value); +Q_STATIC_ASSERT(CanConvert<const QStringRef >::value); +Q_STATIC_ASSERT(CanConvert< QStringRef&>::value); +Q_STATIC_ASSERT(CanConvert<const QStringRef&>::value); + + +// +// ushort +// + +Q_STATIC_ASSERT(CanConvert< ushort*>::value); +Q_STATIC_ASSERT(CanConvert<const ushort*>::value); + + +// +// char16_t +// + +#if !defined(Q_OS_WIN) || defined(Q_COMPILER_UNICODE_STRINGS) + +Q_STATIC_ASSERT(CanConvert< char16_t*>::value); +Q_STATIC_ASSERT(CanConvert<const char16_t*>::value); + +#endif + +#ifdef Q_COMPILER_UNICODE_STRINGS + +Q_STATIC_ASSERT(CanConvert< std::u16string >::value); +Q_STATIC_ASSERT(CanConvert<const std::u16string >::value); +Q_STATIC_ASSERT(CanConvert< std::u16string&>::value); +Q_STATIC_ASSERT(CanConvert<const std::u16string&>::value); + +#endif + + +// +// wchar_t +// + +Q_CONSTEXPR bool CanConvertFromWCharT = +#ifdef Q_OS_WIN + true +#else + false +#endif + ; + +Q_STATIC_ASSERT(CanConvert< wchar_t*>::value == CanConvertFromWCharT); +Q_STATIC_ASSERT(CanConvert<const wchar_t*>::value == CanConvertFromWCharT); + +Q_STATIC_ASSERT(CanConvert< std::wstring >::value == CanConvertFromWCharT); +Q_STATIC_ASSERT(CanConvert<const std::wstring >::value == CanConvertFromWCharT); +Q_STATIC_ASSERT(CanConvert< std::wstring&>::value == CanConvertFromWCharT); +Q_STATIC_ASSERT(CanConvert<const std::wstring&>::value == CanConvertFromWCharT); + + +class tst_QStringView : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void constExpr() const; + void basics() const; + void at() const; + + void fromQString() const; + void fromQStringRef() const; + + void fromQCharStar() const + { + const QChar str[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', 0 }; + fromLiteral(str); + } + + void fromUShortStar() const + { + const ushort str[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', 0 }; + fromLiteral(str); + } + + void fromChar16TStar() const + { +#if !defined(Q_OS_WIN) || defined(Q_COMPILER_UNICODE_STRINGS) + fromLiteral(u"Hello, World!"); +#else + QSKIP("This test requires C++11 char16_t support enabled in the compiler"); +#endif + } + + void fromWCharTStar() const + { +#ifdef Q_OS_WIN + fromLiteral(L"Hello, World!"); +#else + QSKIP("This is a Windows-only test"); +#endif + } + + // std::basic_string + void fromStdStringWCharT() const + { +#ifdef Q_OS_WIN + fromStdString<wchar_t>(); +#else + QSKIP("This is a Windows-only test"); +#endif + } + void fromStdStringChar16T() const + { +#ifdef Q_COMPILER_UNICODE_STRINGS + fromStdString<char16_t>(); +#else + QSKIP("This test requires C++11 char16_t support enabled in compiler & stdlib"); +#endif + } + +private: + template <typename String> + void conversion_tests(String arg) const; + template <typename Char> + void fromLiteral(const Char *arg) const; + template <typename Char, typename Container> + void fromContainer() const; + template <typename Char> + void fromStdString() const { fromContainer<Char, std::basic_string<Char> >(); } +}; + +void tst_QStringView::constExpr() const +{ + // compile-time checks +#ifdef Q_COMPILER_CONSTEXPR + { + constexpr QStringView sv; + Q_STATIC_ASSERT(sv.size() == 0); + Q_STATIC_ASSERT(sv.isNull()); + Q_STATIC_ASSERT(sv.empty()); + Q_STATIC_ASSERT(sv.isEmpty()); + Q_STATIC_ASSERT(sv.utf16() == nullptr); + } + { + constexpr QStringView sv = QStringViewLiteral(""); + Q_STATIC_ASSERT(sv.size() == 0); + Q_STATIC_ASSERT(!sv.isNull()); + Q_STATIC_ASSERT(sv.empty()); + Q_STATIC_ASSERT(sv.isEmpty()); + Q_STATIC_ASSERT(sv.utf16() != nullptr); + } + { + constexpr QStringView sv = QStringViewLiteral("Hello"); + Q_STATIC_ASSERT(sv.size() == 5); + Q_STATIC_ASSERT(!sv.empty()); + Q_STATIC_ASSERT(!sv.isEmpty()); + Q_STATIC_ASSERT(!sv.isNull()); + Q_STATIC_ASSERT(*sv.utf16() == 'H'); + Q_STATIC_ASSERT(sv[0] == QLatin1Char('H')); + Q_STATIC_ASSERT(sv.at(0) == QLatin1Char('H')); + Q_STATIC_ASSERT(sv.front() == QLatin1Char('H')); + Q_STATIC_ASSERT(sv.first() == QLatin1Char('H')); + Q_STATIC_ASSERT(sv[4] == QLatin1Char('o')); + Q_STATIC_ASSERT(sv.at(4) == QLatin1Char('o')); + Q_STATIC_ASSERT(sv.back() == QLatin1Char('o')); + Q_STATIC_ASSERT(sv.last() == QLatin1Char('o')); + } +#endif +} + +void tst_QStringView::basics() const +{ + QStringView sv1; + + // a default-constructed QStringView is null: + QVERIFY(sv1.isNull()); + // which implies it's empty(); + QVERIFY(sv1.isEmpty()); +} + +void tst_QStringView::at() const +{ + QString hello("Hello"); + QStringView sv(hello); + QCOMPARE(sv.at(0), QChar('H')); QCOMPARE(sv[0], QChar('H')); + QCOMPARE(sv.at(1), QChar('e')); QCOMPARE(sv[1], QChar('e')); + QCOMPARE(sv.at(2), QChar('l')); QCOMPARE(sv[2], QChar('l')); + QCOMPARE(sv.at(3), QChar('l')); QCOMPARE(sv[3], QChar('l')); + QCOMPARE(sv.at(4), QChar('o')); QCOMPARE(sv[4], QChar('o')); +} + +void tst_QStringView::fromQString() const +{ + QString null; + QString empty = ""; + + QVERIFY( QStringView(null).isNull()); + QVERIFY( QStringView(null).isEmpty()); + QVERIFY( QStringView(empty).isEmpty()); + QVERIFY(!QStringView(empty).isNull()); + + conversion_tests(QString("Hello World!")); +} + +void tst_QStringView::fromQStringRef() const +{ + QStringRef null; + QString emptyS = ""; + QStringRef empty(&emptyS); + + QVERIFY( QStringView(null).isNull()); + QVERIFY( QStringView(null).isEmpty()); + QVERIFY( QStringView(empty).isEmpty()); + QVERIFY(!QStringView(empty).isNull()); + + conversion_tests(QString("Hello World!").midRef(6)); +} + +template <typename Char> +void tst_QStringView::fromLiteral(const Char *arg) const +{ + const Char *null = nullptr; + const Char empty[] = { 0 }; + + QCOMPARE(QStringView(null).size(), QStringView::size_type(0)); + QCOMPARE(QStringView(null).data(), nullptr); + QCOMPARE(QStringView(empty).size(), QStringView::size_type(0)); + QCOMPARE(static_cast<const void*>(QStringView(empty).data()), + static_cast<const void*>(empty)); + + QVERIFY( QStringView(null).isNull()); + QVERIFY( QStringView(null).isEmpty()); + QVERIFY( QStringView(empty).isEmpty()); + QVERIFY(!QStringView(empty).isNull()); + + conversion_tests(arg); +} + +template <typename Char, typename Container> +void tst_QStringView::fromContainer() const +{ + const QString s = "Hello World!"; + + Container c; + // unspecified whether empty containers make null QStringViews + QVERIFY(QStringView(c).isEmpty()); + + QCOMPARE(sizeof(Char), sizeof(QChar)); + + const auto *data = reinterpret_cast<const Char *>(s.utf16()); + std::copy(data, data + s.size(), std::back_inserter(c)); + conversion_tests(std::move(c)); +} + +namespace help { +template <typename T> +size_t size(const T &t) { return size_t(t.size()); } +template <typename T> +size_t size(const T *t) +{ + size_t result = 0; + if (t) { + while (*t++) + ++result; + } + return result; +} +size_t size(const QChar *t) +{ + size_t result = 0; + if (t) { + while (!t++->isNull()) + ++result; + } + return result; +} + +template <typename T> +typename T::const_iterator cbegin(const T &t) { return t.cbegin(); } +template <typename T> +const T * cbegin(const T *t) { return t; } + +template <typename T> +typename T::const_iterator cend(const T &t) { return t.cend(); } +template <typename T> +const T * cend(const T *t) { return t + size(t); } + +template <typename T> +typename T::const_reverse_iterator crbegin(const T &t) { return t.crbegin(); } +template <typename T> +std::reverse_iterator<const T*> crbegin(const T *t) { return std::reverse_iterator<const T*>(cend(t)); } + +template <typename T> +typename T::const_reverse_iterator crend(const T &t) { return t.crend(); } +template <typename T> +std::reverse_iterator<const T*> crend(const T *t) { return std::reverse_iterator<const T*>(cbegin(t)); } + +} // namespace help + +template <typename String> +void tst_QStringView::conversion_tests(String string) const +{ + // copy-construct: + { + QStringView sv = string; + + QCOMPARE(help::size(sv), help::size(string)); + + // check iterators: + + QVERIFY(std::equal(help::cbegin(string), help::cend(string), + QT_MAKE_CHECKED_ARRAY_ITERATOR(sv.cbegin(), sv.size()))); + QVERIFY(std::equal(help::cbegin(string), help::cend(string), + QT_MAKE_CHECKED_ARRAY_ITERATOR(sv.begin(), sv.size()))); + QVERIFY(std::equal(help::crbegin(string), help::crend(string), + sv.crbegin())); + QVERIFY(std::equal(help::crbegin(string), help::crend(string), + sv.rbegin())); + } + + QStringView sv; + + // copy-assign: + { + sv = string; + + QCOMPARE(help::size(sv), help::size(string)); + + QVERIFY(std::equal(help::cbegin(string), help::cend(string), + QT_MAKE_CHECKED_ARRAY_ITERATOR(sv.cbegin(), sv.size()))); + QVERIFY(std::equal(help::cbegin(string), help::cend(string), + QT_MAKE_CHECKED_ARRAY_ITERATOR(sv.begin(), sv.size()))); + QVERIFY(std::equal(help::crbegin(string), help::crend(string), + sv.crbegin())); + QVERIFY(std::equal(help::crbegin(string), help::crend(string), + sv.rbegin())); + } + + // copy-construct from rvalue (QStringView never assumes ownership): + { + QStringView sv2 = std::move(string); + + QCOMPARE(help::size(sv2), help::size(string)); + + QVERIFY(std::equal(help::cbegin(string), help::cend(string), + QT_MAKE_CHECKED_ARRAY_ITERATOR(sv2.cbegin(), sv2.size()))); + QVERIFY(std::equal(help::cbegin(string), help::cend(string), + QT_MAKE_CHECKED_ARRAY_ITERATOR(sv2.begin(), sv2.size()))); + QVERIFY(std::equal(help::crbegin(string), help::crend(string), + sv2.crbegin())); + QVERIFY(std::equal(help::crbegin(string), help::crend(string), + sv2.rbegin())); + + QCOMPARE(help::size(sv2), help::size(sv)); + + QVERIFY(std::equal(help::cbegin(sv), help::cend(sv), + QT_MAKE_CHECKED_ARRAY_ITERATOR(sv2.cbegin(), sv2.size()))); + QVERIFY(std::equal(help::cbegin(sv), help::cend(sv), + QT_MAKE_CHECKED_ARRAY_ITERATOR(sv2.begin(), sv2.size()))); + QVERIFY(std::equal(help::crbegin(sv), help::crend(sv), + sv2.crbegin())); + QVERIFY(std::equal(help::crbegin(sv), help::crend(sv), + sv2.rbegin())); + } + + // copy-assign from rvalue (QStringView never assumes ownership): + { + QStringView sv2; + sv2 = std::move(string); + + QCOMPARE(help::size(sv2), help::size(string)); + + QVERIFY(std::equal(help::cbegin(string), help::cend(string), + QT_MAKE_CHECKED_ARRAY_ITERATOR(sv2.cbegin(), sv2.size()))); + QVERIFY(std::equal(help::cbegin(string), help::cend(string), + QT_MAKE_CHECKED_ARRAY_ITERATOR(sv2.begin(), sv2.size()))); + QVERIFY(std::equal(help::crbegin(string), help::crend(string), + sv2.crbegin())); + QVERIFY(std::equal(help::crbegin(string), help::crend(string), + sv2.rbegin())); + + QCOMPARE(help::size(sv2), help::size(sv)); + + QVERIFY(std::equal(help::cbegin(sv), help::cend(sv), + QT_MAKE_CHECKED_ARRAY_ITERATOR(sv2.cbegin(), sv2.size()))); + QVERIFY(std::equal(help::cbegin(sv), help::cend(sv), + QT_MAKE_CHECKED_ARRAY_ITERATOR(sv2.begin(), sv2.size()))); + QVERIFY(std::equal(help::crbegin(sv), help::crend(sv), + sv2.crbegin())); + QVERIFY(std::equal(help::crbegin(sv), help::crend(sv), + sv2.rbegin())); + } +} + +QTEST_APPLESS_MAIN(tst_QStringView) +#include "tst_qstringview.moc" diff --git a/tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp b/tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp index c1f2822b74..2cebe1fb6f 100644 --- a/tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp +++ b/tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp @@ -740,7 +740,7 @@ void tst_QTimeZone::icuTest() void tst_QTimeZone::tzTest() { -#if defined QT_BUILD_INTERNAL && defined Q_OS_UNIX && !defined Q_OS_MAC +#if defined QT_BUILD_INTERNAL && defined Q_OS_UNIX && !defined Q_OS_DARWIN // Known datetimes qint64 std = QDateTime(QDate(2012, 1, 1), QTime(0, 0, 0), Qt::UTC).toMSecsSinceEpoch(); qint64 dst = QDateTime(QDate(2012, 6, 1), QTime(0, 0, 0), Qt::UTC).toMSecsSinceEpoch(); @@ -903,12 +903,12 @@ void tst_QTimeZone::tzTest() QDateTime dt(QDate(2016, 3, 28), QTime(0, 0, 0), Qt::UTC); QCOMPARE(tzBarnaul.data(dt.toMSecsSinceEpoch()).abbreviation, QString("+07")); } -#endif // Q_OS_UNIX +#endif // QT_BUILD_INTERNAL && Q_OS_UNIX && !Q_OS_DARWIN } void tst_QTimeZone::macTest() { -#if defined(QT_BUILD_INTERNAL) && defined (Q_OS_MAC) +#if defined(QT_BUILD_INTERNAL) && defined(Q_OS_DARWIN) // Known datetimes qint64 std = QDateTime(QDate(2012, 1, 1), QTime(0, 0, 0), Qt::UTC).toMSecsSinceEpoch(); qint64 dst = QDateTime(QDate(2012, 6, 1), QTime(0, 0, 0), Qt::UTC).toMSecsSinceEpoch(); @@ -955,7 +955,7 @@ void tst_QTimeZone::macTest() } testCetPrivate(tzp); -#endif // Q_OS_MAC +#endif // QT_BUILD_INTERNAL && Q_OS_DARWIN } void tst_QTimeZone::darwinTypes() diff --git a/tests/auto/corelib/tools/qvarlengtharray/tst_qvarlengtharray.cpp b/tests/auto/corelib/tools/qvarlengtharray/tst_qvarlengtharray.cpp index 0806ad1318..3971353cbb 100644 --- a/tests/auto/corelib/tools/qvarlengtharray/tst_qvarlengtharray.cpp +++ b/tests/auto/corelib/tools/qvarlengtharray/tst_qvarlengtharray.cpp @@ -30,8 +30,6 @@ #include <qvarlengtharray.h> #include <qvariant.h> -const int N = 1; - class tst_QVarLengthArray : public QObject { Q_OBJECT diff --git a/tests/auto/corelib/tools/tools.pro b/tests/auto/corelib/tools/tools.pro index e45771a704..e16648c15f 100644 --- a/tests/auto/corelib/tools/tools.pro +++ b/tests/auto/corelib/tools/tools.pro @@ -56,6 +56,7 @@ SUBDIRS=\ qstringlist \ qstringmatcher \ qstringref \ + qstringview \ qtextboundaryfinder \ qtime \ qtimezone \ |