diff options
Diffstat (limited to 'tests/auto/qml/qqmltablemodel')
-rw-r--r-- | tests/auto/qml/qqmltablemodel/data/TestModel.qml | 50 | ||||
-rw-r--r-- | tests/auto/qml/qqmltablemodel/data/TestUtils.js | 45 | ||||
-rw-r--r-- | tests/auto/qml/qqmltablemodel/data/common.qml | 149 | ||||
-rw-r--r-- | tests/auto/qml/qqmltablemodel/data/complex.qml | 68 | ||||
-rw-r--r-- | tests/auto/qml/qqmltablemodel/data/dataAndSetData.qml | 55 | ||||
-rw-r--r-- | tests/auto/qml/qqmltablemodel/data/empty.qml | 82 | ||||
-rw-r--r-- | tests/auto/qml/qqmltablemodel/data/omitTableModelColumnIndex.qml | 47 | ||||
-rw-r--r-- | tests/auto/qml/qqmltablemodel/data/setDataThroughDelegate.qml | 86 | ||||
-rw-r--r-- | tests/auto/qml/qqmltablemodel/data/setRowsMultipleTimes.qml | 84 | ||||
-rw-r--r-- | tests/auto/qml/qqmltablemodel/qqmltablemodel.pro | 10 | ||||
-rw-r--r-- | tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp | 980 |
11 files changed, 1656 insertions, 0 deletions
diff --git a/tests/auto/qml/qqmltablemodel/data/TestModel.qml b/tests/auto/qml/qqmltablemodel/data/TestModel.qml new file mode 100644 index 0000000000..00e1fa65a7 --- /dev/null +++ b/tests/auto/qml/qqmltablemodel/data/TestModel.qml @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import Qt.labs.qmlmodels 1.0 + +import "TestUtils.js" as TestUtils + +TableModel { + id: testModel + objectName: "testModel" + + TableModelColumn { display: "name" } + TableModelColumn { display: "age" } + + rows: [ + { + name: "John", + age: 22 + }, + { + name: "Oliver", + age: 33 + } + ] +} diff --git a/tests/auto/qml/qqmltablemodel/data/TestUtils.js b/tests/auto/qml/qqmltablemodel/data/TestUtils.js new file mode 100644 index 0000000000..0b92a377bb --- /dev/null +++ b/tests/auto/qml/qqmltablemodel/data/TestUtils.js @@ -0,0 +1,45 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +function testModelRoleDataProvider(index, role, cellData) { + switch (role) { + case "display": + switch (index.column) { + case 0: + return cellData.name + case 1: + return cellData.age + } + break + case "name": + return cellData.name + case "age": + return cellData.age + } + return cellData +} diff --git a/tests/auto/qml/qqmltablemodel/data/common.qml b/tests/auto/qml/qqmltablemodel/data/common.qml new file mode 100644 index 0000000000..2f8b0c072b --- /dev/null +++ b/tests/auto/qml/qqmltablemodel/data/common.qml @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.13 +import Qt.labs.qmlmodels 1.0 + +Item { + id: root + width: 200 + height: 200 + + property alias testModel: testModel + property alias tableView: tableView + + function appendRow(personName, personAge) { + testModel.appendRow({ + name: personName, + age: personAge + }) + } + + function appendRowExtraData() { + testModel.appendRow({ + name: "Foo", + age: 99, + nonExistentRole: 123 + }) + } + + function appendRowInvalid1() { + testModel.appendRow(123) + } + + function appendRowInvalid2() { + testModel.appendRow({ + name: "Foo", + age: [] + }) + } + + function appendRowInvalid3() { + testModel.appendRow([ + { name: "Bar" }, + { age: "111" } + ]) + } + + function insertRow(personName, personAge, rowIndex) { + testModel.insertRow(rowIndex, { + name: personName, + age: personAge + }) + } + + function insertRowExtraData() { + testModel.insertRow(0, { + name: "Foo", + age: 99, + nonExistentRole: 123 + }) + } + + function insertRowInvalid1() { + testModel.insertRow(0, 123) + } + + function insertRowInvalid2() { + testModel.insertRow(0, { + name: "Foo", + age: [] + }) + } + + function insertRowInvalid3() { + testModel.insertRow(0, [ + { name: "Bar" }, + { age: "111" } + ]) + } + + function setRow(rowIndex, personName, personAge) { + testModel.setRow(rowIndex, { + name: personName, + age: personAge + }) + } + + function setRowExtraData() { + testModel.setRow(0, { + name: "Foo", + age: 99, + nonExistentRole: 123 + }) + } + + function setRowInvalid1() { + testModel.setRow(0, 123) + } + + function setRowInvalid2() { + testModel.setRow(0, { + name: "Foo", + age: [] + }) + } + + function setRowInvalid3() { + testModel.setRow(0, [ + { name: "Bar" }, + { age: "111" } + ]) + } + + TableView { + id: tableView + anchors.fill: parent + model: TestModel { + id: testModel + } + delegate: Text { + text: model.display + } + } +} diff --git a/tests/auto/qml/qqmltablemodel/data/complex.qml b/tests/auto/qml/qqmltablemodel/data/complex.qml new file mode 100644 index 0000000000..dbf53bac7e --- /dev/null +++ b/tests/auto/qml/qqmltablemodel/data/complex.qml @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.13 +import Qt.labs.qmlmodels 1.0 + +TableView { + width: 100 + height: 100 + delegate: Item { + implicitWidth: 50 + implicitHeight: 50 + + Text { + text: model.display + anchors.centerIn: parent + } + } + model: TableModel { + id: testModel + objectName: "testModel" + + TableModelColumn { + display: function(modelIndex) { return testModel.rows[modelIndex.row][0].name } + setDisplay: function(modelIndex, cellData) { testModel.rows[modelIndex.row][0].name = cellData } + } + TableModelColumn { + display: function(modelIndex) { return testModel.rows[modelIndex.row][1].age } + setDisplay: function(modelIndex, cellData) { testModel.rows[modelIndex.row][1].age = cellData } + } + + rows: [ + [ + { name: "John" }, + { age: 22 } + ], + [ + { name: "Oliver" }, + { age: 33 } + ] + ] + } +} diff --git a/tests/auto/qml/qqmltablemodel/data/dataAndSetData.qml b/tests/auto/qml/qqmltablemodel/data/dataAndSetData.qml new file mode 100644 index 0000000000..d3f726bfa1 --- /dev/null +++ b/tests/auto/qml/qqmltablemodel/data/dataAndSetData.qml @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import Qt.labs.qmlmodels 1.0 + +TableView { + width: 200; height: 200 + model: TestModel { + id: testModel + + // This is silly: in real life, store the birthdate instead of the age, + // and let the delegate calculate the age, so it won't need updating + function happyBirthday(dude) { + var row = -1; + for (var r = 0; row < 0 && r < testModel.rowCount; ++r) + if (testModel.data(testModel.index(r, 0), "display") === dude) + row = r; + var index = testModel.index(row, 1) + testModel.setData(index, "display", testModel.data(index, "display") + 1) + } + } + delegate: Text { + id: textItem + text: model.display + TapHandler { + onTapped: testModel.happyBirthday(testModel.data(testModel.index(row, 0), "display")) + } + } +} diff --git a/tests/auto/qml/qqmltablemodel/data/empty.qml b/tests/auto/qml/qqmltablemodel/data/empty.qml new file mode 100644 index 0000000000..f5afbd1d27 --- /dev/null +++ b/tests/auto/qml/qqmltablemodel/data/empty.qml @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import Qt.labs.qmlmodels 1.0 + +Item { + id: root + width: 200 + height: 200 + + property alias testModel: testModel + property alias tableView: tableView + + function setRows() { + testModel.rows = [ + { + name: "John", + age: 22 + }, + { + name: "Oliver", + age: 33 + } + ] + } + + function appendJohn() { + testModel.appendRow({ + name: "John", + age: 22 + }) + } + + function appendOliver() { + testModel.appendRow({ + name: "Oliver", + age: 33 + }) + } + + TableModel { + id: testModel + objectName: "testModel" + + TableModelColumn { display: "name" } + TableModelColumn { display: "age" } + } + TableView { + id: tableView + anchors.fill: parent + model: testModel + delegate: Text { + text: model.display + } + } +} diff --git a/tests/auto/qml/qqmltablemodel/data/omitTableModelColumnIndex.qml b/tests/auto/qml/qqmltablemodel/data/omitTableModelColumnIndex.qml new file mode 100644 index 0000000000..86bcb08fa2 --- /dev/null +++ b/tests/auto/qml/qqmltablemodel/data/omitTableModelColumnIndex.qml @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import Qt.labs.qmlmodels 1.0 + +TableModel { + objectName: "testModel" + + TableModelColumn { display: "name" } + TableModelColumn { display: "age" } + + rows: [ + { + name: "John", + age: 22 + }, + { + name: "Oliver", + age: 33 + } + ] +} diff --git a/tests/auto/qml/qqmltablemodel/data/setDataThroughDelegate.qml b/tests/auto/qml/qqmltablemodel/data/setDataThroughDelegate.qml new file mode 100644 index 0000000000..ebfe4ed930 --- /dev/null +++ b/tests/auto/qml/qqmltablemodel/data/setDataThroughDelegate.qml @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import Qt.labs.qmlmodels 1.0 + +Item { + id: root + width: 200 + height: 200 + + property alias testModel: testModel + + signal shouldModify() + signal shouldModifyInvalidRole() + signal shouldModifyInvalidType() + + function modify() { + shouldModify() + } + + function modifyInvalidRole() { + shouldModifyInvalidRole() + } + + function modifyInvalidType() { + shouldModifyInvalidType() + } + + TableView { + anchors.fill: parent + model: TestModel { + id: testModel + } + + delegate: Text { + id: textItem + text: model.display + + Connections { + target: root + enabled: column === 1 + onShouldModify: model.display = 18 + } + + Connections { + target: root + enabled: column === 0 + // Invalid: should be "display". + onShouldModifyInvalidRole: model.age = 100 + } + + Connections { + target: root + enabled: column === 1 + // Invalid: should be string. + onShouldModifyInvalidType: model.display = "Whoops" + } + } + } +} diff --git a/tests/auto/qml/qqmltablemodel/data/setRowsMultipleTimes.qml b/tests/auto/qml/qqmltablemodel/data/setRowsMultipleTimes.qml new file mode 100644 index 0000000000..01ec40270c --- /dev/null +++ b/tests/auto/qml/qqmltablemodel/data/setRowsMultipleTimes.qml @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import Qt.labs.qmlmodels 1.0 + +Item { + id: root + width: 200 + height: 200 + + property alias testModel: testModel + property alias tableView: tableView + + function setRowsValid() { + testModel.rows = [ + { + name: "Max", + age: 20 + }, + { + name: "Imum", + age: 41 + }, + { + name: "Power", + age: 89 + } + ] + } + + function setRowsInvalid() { + testModel.rows = [ + { + nope: "Nope", + age: 20 + }, + { + nope: "Nah", + age: 41 + }, + { + nope: "No", + age: 89 + } + ] + } + + TableView { + id: tableView + anchors.fill: parent + model: TestModel { + id: testModel + } + delegate: Text { + text: model.display + } + } +} diff --git a/tests/auto/qml/qqmltablemodel/qqmltablemodel.pro b/tests/auto/qml/qqmltablemodel/qqmltablemodel.pro new file mode 100644 index 0000000000..11b11132aa --- /dev/null +++ b/tests/auto/qml/qqmltablemodel/qqmltablemodel.pro @@ -0,0 +1,10 @@ +CONFIG += testcase +TARGET = tst_qqmltablemodel + +SOURCES += tst_qqmltablemodel.cpp + +include (../../shared/util.pri) + +TESTDATA = data/* + +QT += core gui qml-private qml quick-private quick testlib diff --git a/tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp b/tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp new file mode 100644 index 0000000000..113a27494d --- /dev/null +++ b/tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp @@ -0,0 +1,980 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/qtest.h> +#include <QtTest/qsignalspy.h> +#include <QtCore/qregularexpression.h> +#include <QtQml/private/qqmlengine_p.h> +#include <QtQml/private/qqmltablemodel_p.h> +#include <QtQml/qqmlcomponent.h> +#include <QtQuick/qquickitem.h> +#include <QtQuick/qquickview.h> +#include <QtQuick/private/qquicktableview_p.h> + +#include "../../shared/util.h" + +class tst_QQmlTableModel : public QQmlDataTest +{ + Q_OBJECT + +public: + tst_QQmlTableModel() {} + +private slots: + void appendRemoveRow(); + void appendRowToEmptyModel(); + void clear(); + void getRow(); + void insertRow(); + void moveRow(); + void setRow(); + void setDataThroughDelegate(); + void setRowsImperatively(); + void setRowsMultipleTimes(); + void dataAndEditing(); + void omitTableModelColumnIndex(); + void complexRow(); +}; + +void tst_QQmlTableModel::appendRemoveRow() +{ + QQuickView view(testFileUrl("common.qml")); + QCOMPARE(view.status(), QQuickView::Ready); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + QVERIFY(model); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + + QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged())); + QVERIFY(columnCountSpy.isValid()); + + QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged())); + QVERIFY(rowCountSpy.isValid()); + int rowCountSignalEmissions = 0; + + const QHash<int, QByteArray> roleNames = model->roleNames(); + QCOMPARE(roleNames.size(), 1); + QVERIFY(roleNames.values().contains("display")); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + + // Call remove() with a negative rowIndex. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*removeRow\\(\\): \"rowIndex\" cannot be negative")); + QVERIFY(QMetaObject::invokeMethod(model, "removeRow", Q_ARG(int, -1))); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + + // Call remove() with an rowIndex that is too large. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*removeRow\\(\\): \"rowIndex\" 2 is greater than or equal to rowCount\\(\\) of 2")); + QVERIFY(QMetaObject::invokeMethod(model, "removeRow", Q_ARG(int, 2))); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + + // Call remove() with a valid rowIndex but negative rows. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*removeRow\\(\\): \"rows\" is less than or equal to zero")); + QVERIFY(QMetaObject::invokeMethod(model, "removeRow", Q_ARG(int, 0), Q_ARG(int, -1))); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + + // Call remove() with a valid rowIndex but excessive rows. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*removeRow\\(\\): \"rows\" 3 exceeds available rowCount\\(\\) of 2 when removing from \"rowIndex\" 0")); + QVERIFY(QMetaObject::invokeMethod(model, "removeRow", Q_ARG(int, 0), Q_ARG(int, 3))); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + + // Call remove() without specifying the number of rows to remove; it should remove one row. + QVERIFY(QMetaObject::invokeMethod(model, "removeRow", Q_ARG(int, 0))); + QCOMPARE(model->rowCount(), 1); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), ++rowCountSignalEmissions); + + // Call append() with a row that has an unexpected role; the row should be added and the extra data ignored. + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRowExtraData")); + // Nothing should change. + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 99); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), ++rowCountSignalEmissions); + + // Call append() with a row that is an int. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*appendRow\\(\\): expected \"row\" argument to be a QJSValue, but got int instead:\nQVariant\\(int, 123\\)")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRowInvalid1")); + // Nothing should change. + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 99); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + + // Call append() with a row with a role of the wrong type. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*appendRow\\(\\): expected the property named \"age\" to be of type \"int\", but got \"QVariantList\" instead")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRowInvalid2")); + // Nothing should change. + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 99); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + + // Call append() with a row that is an array instead of a simple object. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*appendRow\\(\\): row manipulation functions do not support complex rows \\(row index: -1\\)")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRowInvalid3")); + // Nothing should change. + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + + // Call append() to insert one row. + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRow", Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40))); + QCOMPARE(model->rowCount(), 3); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 99); + QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max")); + QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 40); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), ++rowCountSignalEmissions); + + // Call remove() and specify rowIndex and rows, removing all remaining rows. + QVERIFY(QMetaObject::invokeMethod(model, "removeRow", Q_ARG(int, 0), Q_ARG(int, 3))); + QCOMPARE(model->rowCount(), 0); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")), QVariant()); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")), QVariant()); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), ++rowCountSignalEmissions); +} + +void tst_QQmlTableModel::appendRowToEmptyModel() +{ + QQuickView view(testFileUrl("empty.qml")); + QCOMPARE(view.status(), QQuickView::Ready); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + QVERIFY(model); + QCOMPARE(model->rowCount(), 0); + QCOMPARE(model->columnCount(), 2); + + QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged())); + QVERIFY(columnCountSpy.isValid()); + + QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged())); + QVERIFY(rowCountSpy.isValid()); + + QQuickTableView *tableView = view.rootObject()->property("tableView").value<QQuickTableView*>(); + QVERIFY(tableView); + QCOMPARE(tableView->rows(), 0); + QCOMPARE(tableView->columns(), 2); + + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendJohn")); + QCOMPARE(model->rowCount(), 1); + QCOMPARE(model->columnCount(), 2); + const QHash<int, QByteArray> roleNames = model->roleNames(); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), 1); + QTRY_COMPARE(tableView->rows(), 1); + QCOMPARE(tableView->columns(), 2); +} + +void tst_QQmlTableModel::clear() +{ + QQuickView view(testFileUrl("common.qml")); + QCOMPARE(view.status(), QQuickView::Ready); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + QVERIFY(model); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + + QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged())); + QVERIFY(columnCountSpy.isValid()); + + QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged())); + QVERIFY(rowCountSpy.isValid()); + + const QHash<int, QByteArray> roleNames = model->roleNames(); + QVERIFY(roleNames.values().contains("display")); + QCOMPARE(roleNames.size(), 1); + + QQuickTableView *tableView = view.rootObject()->property("tableView").value<QQuickTableView*>(); + QVERIFY(tableView); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + QVERIFY(QMetaObject::invokeMethod(model, "clear")); + QCOMPARE(model->rowCount(), 0); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")), QVariant()); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")), QVariant()); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), 1); + // Wait until updatePolish() gets called, which is where the size is recalculated. + QTRY_COMPARE(tableView->rows(), 0); + QCOMPARE(tableView->columns(), 2); +} + +void tst_QQmlTableModel::getRow() +{ + QQuickView view(testFileUrl("common.qml")); + QCOMPARE(view.status(), QQuickView::Ready); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + QVERIFY(model); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + + // Call get() with a negative row index. + QVariant returnValue; + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*getRow\\(\\): \"rowIndex\" cannot be negative")); + QVERIFY(QMetaObject::invokeMethod(model, "getRow", Q_RETURN_ARG(QVariant, returnValue), Q_ARG(int, -1))); + QVERIFY(!returnValue.isValid()); + + // Call get() with a row index that is too large. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*getRow\\(\\): \"rowIndex\" 2 is greater than or equal to rowCount\\(\\) of 2")); + QVERIFY(QMetaObject::invokeMethod(model, "getRow", Q_RETURN_ARG(QVariant, returnValue), Q_ARG(int, 2))); + QVERIFY(!returnValue.isValid()); + + // Call get() with a valid row index. + QVERIFY(QMetaObject::invokeMethod(model, "getRow", Q_RETURN_ARG(QVariant, returnValue), Q_ARG(int, 0))); + const QVariantMap rowAsVariantMap = returnValue.toMap(); + QCOMPARE(rowAsVariantMap.value(QLatin1String("name")).toString(), QLatin1String("John")); + QCOMPARE(rowAsVariantMap.value(QLatin1String("age")).toInt(), 22); +} + +void tst_QQmlTableModel::insertRow() +{ + QQuickView view(testFileUrl("common.qml")); + QCOMPARE(view.status(), QQuickView::Ready); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + QVERIFY(model); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + + QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged())); + QVERIFY(columnCountSpy.isValid()); + + QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged())); + QVERIFY(rowCountSpy.isValid()); + int rowCountSignalEmissions = 0; + + QQuickTableView *tableView = view.rootObject()->property("tableView").value<QQuickTableView*>(); + QVERIFY(tableView); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Try to insert with a negative index. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*insertRow\\(\\): \"rowIndex\" cannot be negative")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRow", + Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40), Q_ARG(QVariant, -1))); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->rowCount(), 2); + const QHash<int, QByteArray> roleNames = model->roleNames(); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Try to insert past the last allowed index. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*insertRow\\(\\): \"rowIndex\" 3 is greater than rowCount\\(\\) of 2")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRow", + Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40), Q_ARG(QVariant, 3))); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Call insert() with a row that is an int. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*insertRow\\(\\): expected \"row\" argument to be a QJSValue, but got int instead:\nQVariant\\(int, 123\\)")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRowInvalid1")); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Try to insert a row with a role of the wrong type. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*insertRow\\(\\): expected the property named \"age\" to be of type \"int\", but got \"QVariantList\" instead")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRowInvalid2")); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Try to insert a row that is an array instead of a simple object. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*insertRow\\(\\): row manipulation functions do not support complex rows \\(row index: 0\\)")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRowInvalid3")); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Try to insert a row has an unexpected role; the row should be added and the extra data ignored. + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRowExtraData")); + QCOMPARE(model->rowCount(), 3); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 99); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), ++rowCountSignalEmissions); + QTRY_COMPARE(tableView->rows(), 3); + QCOMPARE(tableView->columns(), 2); + + // Insert a row at the bottom of the table. + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRow", + Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40), Q_ARG(QVariant, 3))); + QCOMPARE(model->rowCount(), 4); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 99); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(model->data(model->index(3, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max")); + QCOMPARE(model->data(model->index(3, 1, QModelIndex()), roleNames.key("display")).toInt(), 40); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), ++rowCountSignalEmissions); + QTRY_COMPARE(tableView->rows(), 4); + QCOMPARE(tableView->columns(), 2); + + // Insert a row in the middle of the table. + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRow", + Q_ARG(QVariant, QLatin1String("Daisy")), Q_ARG(QVariant, 30), Q_ARG(QVariant, 2))); + QCOMPARE(model->rowCount(), 5); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 99); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Daisy")); + QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 30); + QCOMPARE(model->data(model->index(3, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(3, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max")); + QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("display")).toInt(), 40); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), ++rowCountSignalEmissions); + QTRY_COMPARE(tableView->rows(), 5); + QCOMPARE(tableView->columns(), 2); +} + +void tst_QQmlTableModel::moveRow() +{ + QQuickView view(testFileUrl("common.qml")); + QCOMPARE(view.status(), QQuickView::Ready); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + QVERIFY(model); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->rowCount(), 2); + + QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged())); + QVERIFY(columnCountSpy.isValid()); + + QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged())); + QVERIFY(rowCountSpy.isValid()); + int rowCountSignalEmissions = 0; + + const QHash<int, QByteArray> roleNames = model->roleNames(); + + // Append some rows. + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRow", Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40))); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRow", Q_ARG(QVariant, QLatin1String("Daisy")), Q_ARG(QVariant, 30))); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRow", Q_ARG(QVariant, QLatin1String("Trev")), Q_ARG(QVariant, 48))); + QCOMPARE(model->rowCount(), 5); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max")); + QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 40); + QCOMPARE(model->data(model->index(3, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Daisy")); + QCOMPARE(model->data(model->index(3, 1, QModelIndex()), roleNames.key("display")).toInt(), 30); + QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Trev")); + QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("display")).toInt(), 48); + rowCountSignalEmissions = 3; + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + + // Try to move with a fromRowIndex that is negative. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*moveRow\\(\\): \"fromRowIndex\" cannot be negative")); + QVERIFY(QMetaObject::invokeMethod(model, "moveRow", Q_ARG(int, -1), Q_ARG(int, 1))); + // Shouldn't have changed. + QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Trev")); + QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("display")).toInt(), 48); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + + // Try to move with a fromRowIndex that is too large. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*moveRow\\(\\): \"fromRowIndex\" 5 is greater than or equal to rowCount\\(\\)")); + QVERIFY(QMetaObject::invokeMethod(model, "moveRow", Q_ARG(int, 5), Q_ARG(int, 1))); + QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Trev")); + QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("display")).toInt(), 48); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + + // Try to move with a toRowIndex that is negative. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*moveRow\\(\\): \"toRowIndex\" cannot be negative")); + QVERIFY(QMetaObject::invokeMethod(model, "moveRow", Q_ARG(int, 0), Q_ARG(int, -1))); + // Shouldn't have changed. + QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Trev")); + QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("display")).toInt(), 48); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + + // Try to move with a toRowIndex that is too large. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*moveRow\\(\\): \"toRowIndex\" 5 is greater than or equal to rowCount\\(\\)")); + QVERIFY(QMetaObject::invokeMethod(model, "moveRow", Q_ARG(int, 0), Q_ARG(int, 5))); + QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Trev")); + QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("display")).toInt(), 48); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + + // Move the first row to the end. + QVERIFY(QMetaObject::invokeMethod(model, "moveRow", Q_ARG(int, 0), Q_ARG(int, 4))); + // The counts shouldn't have changed. + QCOMPARE(model->rowCount(), 5); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 40); + QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Daisy")); + QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 30); + QCOMPARE(model->data(model->index(3, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Trev")); + QCOMPARE(model->data(model->index(3, 1, QModelIndex()), roleNames.key("display")).toInt(), 48); + QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + + // Move it back again. + QVERIFY(QMetaObject::invokeMethod(model, "moveRow", Q_ARG(int, 4), Q_ARG(int, 0))); + QCOMPARE(model->rowCount(), 5); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max")); + QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 40); + QCOMPARE(model->data(model->index(3, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Daisy")); + QCOMPARE(model->data(model->index(3, 1, QModelIndex()), roleNames.key("display")).toInt(), 30); + QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Trev")); + QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("display")).toInt(), 48); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + + // Move the first row down one by one row. + QVERIFY(QMetaObject::invokeMethod(model, "moveRow", Q_ARG(int, 0), Q_ARG(int, 1))); + QCOMPARE(model->rowCount(), 5); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max")); + QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 40); + QCOMPARE(model->data(model->index(3, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Daisy")); + QCOMPARE(model->data(model->index(3, 1, QModelIndex()), roleNames.key("display")).toInt(), 30); + QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Trev")); + QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("display")).toInt(), 48); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); +} + +void tst_QQmlTableModel::setRow() +{ + QQuickView view(testFileUrl("common.qml")); + QCOMPARE(view.status(), QQuickView::Ready); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + QVERIFY(model); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->rowCount(), 2); + + QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged())); + QVERIFY(columnCountSpy.isValid()); + + QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged())); + QVERIFY(rowCountSpy.isValid()); + int rowCountSignalEmissions = 0; + + QQuickTableView *tableView = view.rootObject()->property("tableView").value<QQuickTableView*>(); + QVERIFY(tableView); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Try to set with a negative index. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*setRow\\(\\): \"rowIndex\" cannot be negative")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRow", + Q_ARG(QVariant, -1), Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40))); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + const QHash<int, QByteArray> roleNames = model->roleNames(); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Try to set at an index past the last allowed index. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*setRow\\(\\): \"rowIndex\" 3 is greater than rowCount\\(\\) of 2")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRow", + Q_ARG(QVariant, 3), Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40))); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Try to set a row that has an unexpected role; the row should be set and the extra data ignored. + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRowExtraData")); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 99); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Try to insert a row that is not an array. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*setRow\\(\\): expected \"row\" argument to be a QJSValue, but got int instead:\nQVariant\\(int, 123\\)")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRowInvalid1")); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 99); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Try to insert a row with a role that is of the wrong type. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*setRow\\(\\): expected the property named \"age\" to be of type \"int\", but got \"QVariantList\" instead")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRowInvalid2")); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 99); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Try to insert a row that is an array instead of a simple object. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*setRow\\(\\): row manipulation functions do not support complex rows \\(row index: 0\\)")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRowInvalid3")); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 99); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Set the first row. + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRow", + Q_ARG(QVariant, 0), Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40))); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 40); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Set the last row. + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRow", + Q_ARG(QVariant, 1), Q_ARG(QVariant, QLatin1String("Daisy")), Q_ARG(QVariant, 30))); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 40); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Daisy")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 30); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Append a row by passing an index that is equal to rowCount(). + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRow", + Q_ARG(QVariant, 2), Q_ARG(QVariant, QLatin1String("Wot")), Q_ARG(QVariant, 99))); + QCOMPARE(model->rowCount(), 3); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 40); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Daisy")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 30); + QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Wot")); + QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 99); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), ++rowCountSignalEmissions); + QTRY_COMPARE(tableView->rows(), 3); + QCOMPARE(tableView->columns(), 2); +} + +void tst_QQmlTableModel::setDataThroughDelegate() +{ + QQuickView view(testFileUrl("setDataThroughDelegate.qml")); + QCOMPARE(view.status(), QQuickView::Ready); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + QVERIFY(model); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + + QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged())); + QVERIFY(columnCountSpy.isValid()); + + QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged())); + QVERIFY(rowCountSpy.isValid()); + + const QHash<int, QByteArray> roleNames = model->roleNames(); + QCOMPARE(roleNames.size(), 1); + QVERIFY(roleNames.values().contains("display")); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), 0); + + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "modify")); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 18); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 18); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), 0); + + // Test setting a role that doesn't exist for a certain column. + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "modifyInvalidRole")); + // Should be unchanged. + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 18); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 18); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), 0); + + // Test setting a role with a value of the wrong type. + // There are two rows, so two delegates respond to the signal, which means we need to ignore two warnings. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*setData\\(\\): failed converting value QVariant\\(QString, \"Whoops\"\\) " \ + "set at row 0 column 1 with role \"display\" to \"int\"")); + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*setData\\(\\): failed converting value QVariant\\(QString, \"Whoops\"\\) " \ + "set at row 1 column 1 with role \"display\" to \"int\"")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "modifyInvalidType")); + // Should be unchanged. + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 18); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 18); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), 0); +} + +// Start off with empty rows and then set them to test rowCountChanged(). +void tst_QQmlTableModel::setRowsImperatively() +{ + QQuickView view(testFileUrl("empty.qml")); + QCOMPARE(view.status(), QQuickView::Ready); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + QVERIFY(model); + QCOMPARE(model->rowCount(), 0); + QCOMPARE(model->columnCount(), 2); + + QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged())); + QVERIFY(columnCountSpy.isValid()); + + QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged())); + QVERIFY(rowCountSpy.isValid()); + + QQuickTableView *tableView = view.rootObject()->property("tableView").value<QQuickTableView*>(); + QVERIFY(tableView); + QCOMPARE(tableView->rows(), 0); + QCOMPARE(tableView->columns(), 2); + + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRows")); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + const QHash<int, QByteArray> roleNames = model->roleNames(); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), 1); + QTRY_COMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); +} + +void tst_QQmlTableModel::setRowsMultipleTimes() +{ + QQuickView view(testFileUrl("setRowsMultipleTimes.qml")); + QCOMPARE(view.status(), QQuickView::Ready); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + QVERIFY(model); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + + QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged())); + QVERIFY(columnCountSpy.isValid()); + + QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged())); + QVERIFY(rowCountSpy.isValid()); + + QQuickTableView *tableView = view.rootObject()->property("tableView").value<QQuickTableView*>(); + QVERIFY(tableView); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Set valid rows after they've already been declared. + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRowsValid")); + QCOMPARE(model->rowCount(), 3); + QCOMPARE(model->columnCount(), 2); + const QHash<int, QByteArray> roleNames = model->roleNames(); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 20); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Imum")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 41); + QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Power")); + QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 89); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), 1); + QTRY_COMPARE(tableView->rows(), 3); + QCOMPARE(tableView->columns(), 2); + + // Set invalid rows; we should get a warning and nothing should change. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*setRows\\(\\): expected a property named \"name\" in row at index 0, but couldn't find one")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRowsInvalid")); + QCOMPARE(model->rowCount(), 3); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 20); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Imum")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 41); + QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Power")); + QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 89); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), 1); + QCOMPARE(tableView->rows(), 3); + QCOMPARE(tableView->columns(), 2); +} + +void tst_QQmlTableModel::dataAndEditing() +{ + QQuickView view(testFileUrl("dataAndSetData.qml")); + QCOMPARE(view.status(), QQuickView::Ready); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QQmlTableModel *model = view.rootObject()->property("model").value<QQmlTableModel*>(); + QVERIFY(model); + + const QHash<int, QByteArray> roleNames = model->roleNames(); + QVERIFY(roleNames.values().contains("display")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QVERIFY(QMetaObject::invokeMethod(model, "happyBirthday", Q_ARG(QVariant, QLatin1String("Oliver")))); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 34); +} + +void tst_QQmlTableModel::omitTableModelColumnIndex() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("omitTableModelColumnIndex.qml")); + QCOMPARE(component.status(), QQmlComponent::Ready); + + QScopedPointer<QQmlTableModel> model(qobject_cast<QQmlTableModel*>(component.create())); + QVERIFY(model); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + + const QHash<int, QByteArray> roleNames = model->roleNames(); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); +} + +void tst_QQmlTableModel::complexRow() +{ + QQuickView view(testFileUrl("complex.qml")); + QCOMPARE(view.status(), QQuickView::Ready); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QQuickTableView *tableView = qobject_cast<QQuickTableView*>(view.rootObject()); + QVERIFY(tableView); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + QQmlTableModel *model = tableView->model().value<QQmlTableModel*>(); + QVERIFY(model); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + + const QHash<int, QByteArray> roleNames = model->roleNames(); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); +} + +QTEST_MAIN(tst_QQmlTableModel) + +#include "tst_qqmltablemodel.moc" |