You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
425 lines
17 KiB
425 lines
17 KiB
#include <QSignalSpy> |
|
#include <QSortFilterProxyModel> |
|
#include <QTest> |
|
#include <QStandardItemModel> |
|
#include <QIdentityProxyModel> |
|
#include <QItemSelectionModel> |
|
|
|
#include <kconcatenaterowsproxymodel.h> |
|
#include "test_model_helpers.h" |
|
using namespace TestModelHelpers; |
|
|
|
Q_DECLARE_METATYPE(QModelIndex) |
|
|
|
class tst_KConcatenateRowsProxyModel : public QObject |
|
{ |
|
Q_OBJECT |
|
|
|
private Q_SLOTS: |
|
|
|
void initTestCase() |
|
{ |
|
} |
|
|
|
void init() |
|
{ |
|
// Prepare some source models to use later on |
|
mod.clear(); |
|
mod.insertRow(0, makeStandardItems(QStringList() << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C"))); |
|
mod.setHorizontalHeaderLabels(QStringList() << QStringLiteral("H1") << QStringLiteral("H2") << QStringLiteral("H3")); |
|
mod.setVerticalHeaderLabels(QStringList() << QStringLiteral("One")); |
|
|
|
mod2.clear(); |
|
mod2.insertRow(0, makeStandardItems(QStringList() << QStringLiteral("D") << QStringLiteral("E") << QStringLiteral("F"))); |
|
mod2.setHorizontalHeaderLabels(QStringList() << QStringLiteral("H1") << QStringLiteral("H2") << QStringLiteral("H3")); |
|
mod2.setVerticalHeaderLabels(QStringList() << QStringLiteral("Two")); |
|
} |
|
|
|
void shouldAggregateTwoModelsCorrectly() |
|
{ |
|
// Given a combining proxy |
|
KConcatenateRowsProxyModel pm; |
|
|
|
// When adding two source models |
|
pm.addSourceModel(&mod); |
|
pm.addSourceModel(&mod2); |
|
|
|
// Then the proxy should show 2 rows |
|
QCOMPARE(pm.rowCount(), 2); |
|
QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABC")); |
|
QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("DEF")); |
|
|
|
// ... and correct headers |
|
QCOMPARE(pm.headerData(0, Qt::Horizontal).toString(), QStringLiteral("H1")); |
|
QCOMPARE(pm.headerData(1, Qt::Horizontal).toString(), QStringLiteral("H2")); |
|
QCOMPARE(pm.headerData(2, Qt::Horizontal).toString(), QStringLiteral("H3")); |
|
QCOMPARE(pm.headerData(0, Qt::Vertical).toString(), QStringLiteral("One")); |
|
QCOMPARE(pm.headerData(1, Qt::Vertical).toString(), QStringLiteral("Two")); |
|
|
|
QVERIFY(!pm.canFetchMore(QModelIndex())); |
|
} |
|
|
|
void shouldAggregateThenRemoveTwoEmptyModelsCorrectly() |
|
{ |
|
// Given a combining proxy |
|
KConcatenateRowsProxyModel pm; |
|
|
|
// When adding two empty models |
|
QSignalSpy rowATBISpy(&pm, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int))); |
|
QSignalSpy rowInsertedSpy(&pm, SIGNAL(rowsInserted(QModelIndex,int,int))); |
|
QSignalSpy rowATBRSpy(&pm, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int))); |
|
QSignalSpy rowRemovedSpy(&pm, SIGNAL(rowsRemoved(QModelIndex,int,int))); |
|
QIdentityProxyModel i1, i2; |
|
pm.addSourceModel(&i1); |
|
pm.addSourceModel(&i2); |
|
|
|
// Then the proxy should still be empty (and no signals emitted) |
|
QCOMPARE(pm.rowCount(), 0); |
|
QCOMPARE(pm.columnCount(), 0); |
|
QCOMPARE(rowATBISpy.count(), 0); |
|
QCOMPARE(rowInsertedSpy.count(), 0); |
|
|
|
// When removing the empty models |
|
pm.removeSourceModel(&i1); |
|
pm.removeSourceModel(&i2); |
|
|
|
// Then the proxy should still be empty (and no signals emitted) |
|
QCOMPARE(pm.rowCount(), 0); |
|
QCOMPARE(pm.columnCount(), 0); |
|
QCOMPARE(rowATBRSpy.count(), 0); |
|
QCOMPARE(rowRemovedSpy.count(), 0); |
|
} |
|
|
|
void shouldAggregateTwoEmptyModelsWhichThenGetFilled() |
|
{ |
|
// Given a combining proxy |
|
KConcatenateRowsProxyModel pm; |
|
|
|
// When adding two empty models |
|
QIdentityProxyModel i1, i2; |
|
pm.addSourceModel(&i1); |
|
pm.addSourceModel(&i2); |
|
|
|
QCOMPARE(pm.rowCount(), 0); |
|
QCOMPARE(pm.columnCount(), 0); |
|
|
|
i1.setSourceModel(&mod); |
|
i2.setSourceModel(&mod2); |
|
|
|
// Then the proxy should show 2 rows |
|
QCOMPARE(pm.rowCount(), 2); |
|
QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABC")); |
|
QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("DEF")); |
|
|
|
// ... and correct headers |
|
QCOMPARE(pm.headerData(0, Qt::Horizontal).toString(), QStringLiteral("H1")); |
|
QCOMPARE(pm.headerData(1, Qt::Horizontal).toString(), QStringLiteral("H2")); |
|
QCOMPARE(pm.headerData(2, Qt::Horizontal).toString(), QStringLiteral("H3")); |
|
QCOMPARE(pm.headerData(0, Qt::Vertical).toString(), QStringLiteral("One")); |
|
QCOMPARE(pm.headerData(1, Qt::Vertical).toString(), QStringLiteral("Two")); |
|
|
|
QVERIFY(!pm.canFetchMore(QModelIndex())); |
|
} |
|
|
|
void shouldHandleDataChanged() |
|
{ |
|
// Given two models combined |
|
KConcatenateRowsProxyModel pm; |
|
pm.addSourceModel(&mod); |
|
pm.addSourceModel(&mod2); |
|
QSignalSpy dataChangedSpy(&pm, SIGNAL(dataChanged(QModelIndex,QModelIndex))); |
|
|
|
// When a cell in a source model changes |
|
mod.item(0, 0)->setData("a", Qt::EditRole); |
|
|
|
// Then the change should be notified to the proxy |
|
QCOMPARE(dataChangedSpy.count(), 1); |
|
QCOMPARE(dataChangedSpy.at(0).at(0).toModelIndex(), pm.index(0, 0)); |
|
QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("aBC")); |
|
|
|
// Same test with the other model |
|
mod2.item(0, 2)->setData("f", Qt::EditRole); |
|
|
|
QCOMPARE(dataChangedSpy.count(), 2); |
|
QCOMPARE(dataChangedSpy.at(1).at(0).toModelIndex(), pm.index(1, 2)); |
|
QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("DEf")); |
|
} |
|
|
|
void shouldHandleSetData() |
|
{ |
|
// Given two models combined |
|
KConcatenateRowsProxyModel pm; |
|
pm.addSourceModel(&mod); |
|
pm.addSourceModel(&mod2); |
|
QSignalSpy dataChangedSpy(&pm, SIGNAL(dataChanged(QModelIndex,QModelIndex))); |
|
|
|
// When changing a cell using setData |
|
pm.setData(pm.index(0, 0), "a", Qt::EditRole); |
|
|
|
// Then the change should be notified to the proxy |
|
QCOMPARE(dataChangedSpy.count(), 1); |
|
QCOMPARE(dataChangedSpy.at(0).at(0).toModelIndex(), pm.index(0, 0)); |
|
QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("aBC")); |
|
|
|
// Same test with the other model |
|
pm.setData(pm.index(1, 2), "f", Qt::EditRole); |
|
|
|
QCOMPARE(dataChangedSpy.count(), 2); |
|
QCOMPARE(dataChangedSpy.at(1).at(0).toModelIndex(), pm.index(1, 2)); |
|
QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("DEf")); |
|
} |
|
|
|
void shouldHandleRowInsertionAndRemoval() |
|
{ |
|
// Given two models combined |
|
KConcatenateRowsProxyModel pm; |
|
pm.addSourceModel(&mod); |
|
pm.addSourceModel(&mod2); |
|
QSignalSpy rowATBISpy(&pm, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int))); |
|
QSignalSpy rowInsertedSpy(&pm, SIGNAL(rowsInserted(QModelIndex,int,int))); |
|
|
|
// When a source model inserts a new row |
|
QList<QStandardItem *> row; |
|
row.append(new QStandardItem(QStringLiteral("1"))); |
|
row.append(new QStandardItem(QStringLiteral("2"))); |
|
row.append(new QStandardItem(QStringLiteral("3"))); |
|
mod2.insertRow(0, row); |
|
|
|
// Then the proxy should notify its users and show changes |
|
QCOMPARE(rowSpyToText(rowATBISpy), QStringLiteral("1,1")); |
|
QCOMPARE(rowSpyToText(rowInsertedSpy), QStringLiteral("1,1")); |
|
QCOMPARE(pm.rowCount(), 3); |
|
QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABC")); |
|
QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("123")); |
|
QCOMPARE(extractRowTexts(&pm, 2), QStringLiteral("DEF")); |
|
|
|
// When removing that row |
|
QSignalSpy rowATBRSpy(&pm, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int))); |
|
QSignalSpy rowRemovedSpy(&pm, SIGNAL(rowsRemoved(QModelIndex,int,int))); |
|
mod2.removeRow(0); |
|
|
|
// Then the proxy should notify its users and show changes |
|
QCOMPARE(rowATBRSpy.count(), 1); |
|
QCOMPARE(rowATBRSpy.at(0).at(1).toInt(), 1); |
|
QCOMPARE(rowATBRSpy.at(0).at(2).toInt(), 1); |
|
QCOMPARE(rowRemovedSpy.count(), 1); |
|
QCOMPARE(rowRemovedSpy.at(0).at(1).toInt(), 1); |
|
QCOMPARE(rowRemovedSpy.at(0).at(2).toInt(), 1); |
|
QCOMPARE(pm.rowCount(), 2); |
|
QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABC")); |
|
QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("DEF")); |
|
|
|
// When removing the last row from mod2 |
|
rowATBRSpy.clear(); |
|
rowRemovedSpy.clear(); |
|
mod2.removeRow(0); |
|
|
|
// Then the proxy should notify its users and show changes |
|
QCOMPARE(rowATBRSpy.count(), 1); |
|
QCOMPARE(rowATBRSpy.at(0).at(1).toInt(), 1); |
|
QCOMPARE(rowATBRSpy.at(0).at(2).toInt(), 1); |
|
QCOMPARE(rowRemovedSpy.count(), 1); |
|
QCOMPARE(rowRemovedSpy.at(0).at(1).toInt(), 1); |
|
QCOMPARE(rowRemovedSpy.at(0).at(2).toInt(), 1); |
|
QCOMPARE(pm.rowCount(), 1); |
|
QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABC")); |
|
} |
|
|
|
void shouldAggregateAnotherModelThenRemoveModels() |
|
{ |
|
// Given two models combined, and a third model |
|
KConcatenateRowsProxyModel pm; |
|
pm.addSourceModel(&mod); |
|
pm.addSourceModel(&mod2); |
|
|
|
QStandardItemModel mod3; |
|
mod3.appendRow(makeStandardItems(QStringList() << QStringLiteral("1") << QStringLiteral("2") << QStringLiteral("3"))); |
|
mod3.appendRow(makeStandardItems(QStringList() << QStringLiteral("4") << QStringLiteral("5") << QStringLiteral("6"))); |
|
|
|
QSignalSpy rowATBISpy(&pm, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int))); |
|
QSignalSpy rowInsertedSpy(&pm, SIGNAL(rowsInserted(QModelIndex,int,int))); |
|
|
|
// When adding the new source model |
|
pm.addSourceModel(&mod3); |
|
|
|
// Then the proxy should notify its users about the two rows inserted |
|
QCOMPARE(rowSpyToText(rowATBISpy), QStringLiteral("2,3")); |
|
QCOMPARE(rowSpyToText(rowInsertedSpy), QStringLiteral("2,3")); |
|
QCOMPARE(pm.rowCount(), 4); |
|
QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABC")); |
|
QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("DEF")); |
|
QCOMPARE(extractRowTexts(&pm, 2), QStringLiteral("123")); |
|
QCOMPARE(extractRowTexts(&pm, 3), QStringLiteral("456")); |
|
|
|
// When removing that source model again |
|
QSignalSpy rowATBRSpy(&pm, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int))); |
|
QSignalSpy rowRemovedSpy(&pm, SIGNAL(rowsRemoved(QModelIndex,int,int))); |
|
pm.removeSourceModel(&mod3); |
|
|
|
// Then the proxy should notify its users about the row removed |
|
QCOMPARE(rowATBRSpy.count(), 1); |
|
QCOMPARE(rowATBRSpy.at(0).at(1).toInt(), 2); |
|
QCOMPARE(rowATBRSpy.at(0).at(2).toInt(), 3); |
|
QCOMPARE(rowRemovedSpy.count(), 1); |
|
QCOMPARE(rowRemovedSpy.at(0).at(1).toInt(), 2); |
|
QCOMPARE(rowRemovedSpy.at(0).at(2).toInt(), 3); |
|
QCOMPARE(pm.rowCount(), 2); |
|
QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABC")); |
|
QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("DEF")); |
|
|
|
// When removing model 2 |
|
rowATBRSpy.clear(); |
|
rowRemovedSpy.clear(); |
|
pm.removeSourceModel(&mod2); |
|
QCOMPARE(rowATBRSpy.count(), 1); |
|
QCOMPARE(rowATBRSpy.at(0).at(1).toInt(), 1); |
|
QCOMPARE(rowATBRSpy.at(0).at(2).toInt(), 1); |
|
QCOMPARE(rowRemovedSpy.count(), 1); |
|
QCOMPARE(rowRemovedSpy.at(0).at(1).toInt(), 1); |
|
QCOMPARE(rowRemovedSpy.at(0).at(2).toInt(), 1); |
|
QCOMPARE(pm.rowCount(), 1); |
|
QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABC")); |
|
|
|
// When removing model 1 |
|
rowATBRSpy.clear(); |
|
rowRemovedSpy.clear(); |
|
pm.removeSourceModel(&mod); |
|
QCOMPARE(rowATBRSpy.count(), 1); |
|
QCOMPARE(rowATBRSpy.at(0).at(1).toInt(), 0); |
|
QCOMPARE(rowATBRSpy.at(0).at(2).toInt(), 0); |
|
QCOMPARE(rowRemovedSpy.count(), 1); |
|
QCOMPARE(rowRemovedSpy.at(0).at(1).toInt(), 0); |
|
QCOMPARE(rowRemovedSpy.at(0).at(2).toInt(), 0); |
|
QCOMPARE(pm.rowCount(), 0); |
|
} |
|
|
|
void shouldHandleColumnInsertionAndRemoval() |
|
{ |
|
// Given two models combined |
|
KConcatenateRowsProxyModel pm; |
|
pm.addSourceModel(&mod); |
|
pm.addSourceModel(&mod2); |
|
QSignalSpy colATBISpy(&pm, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int))); |
|
QSignalSpy colInsertedSpy(&pm, SIGNAL(columnsInserted(QModelIndex,int,int))); |
|
|
|
// When the first source model inserts a new column |
|
QCOMPARE(mod.columnCount(), 3); |
|
mod.setColumnCount(4); |
|
|
|
// Then the proxy should notify its users and show changes |
|
QCOMPARE(rowSpyToText(colATBISpy), QStringLiteral("3,3")); |
|
QCOMPARE(rowSpyToText(colInsertedSpy), QStringLiteral("3,3")); |
|
QCOMPARE(pm.rowCount(), 2); |
|
QCOMPARE(pm.columnCount(), 4); |
|
QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABC ")); |
|
QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("DEF ")); |
|
} |
|
|
|
void shouldPropagateLayoutChanged() |
|
{ |
|
// Given two source models, the second one being a QSFPM |
|
KConcatenateRowsProxyModel pm; |
|
pm.addSourceModel(&mod); |
|
|
|
QStandardItemModel mod3; |
|
QList<QStandardItem *> row; |
|
row.append(new QStandardItem(QStringLiteral("1"))); |
|
row.append(new QStandardItem(QStringLiteral("2"))); |
|
row.append(new QStandardItem(QStringLiteral("3"))); |
|
mod3.insertRow(0, row); |
|
row.clear(); |
|
row.append(new QStandardItem(QStringLiteral("4"))); |
|
row.append(new QStandardItem(QStringLiteral("5"))); |
|
row.append(new QStandardItem(QStringLiteral("6"))); |
|
mod3.appendRow(row); |
|
|
|
QSortFilterProxyModel qsfpm; |
|
qsfpm.setSourceModel(&mod3); |
|
pm.addSourceModel(&qsfpm); |
|
|
|
QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABC")); |
|
QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("123")); |
|
QCOMPARE(extractRowTexts(&pm, 2), QStringLiteral("456")); |
|
|
|
// And a selection (row 1) |
|
QItemSelectionModel selection(&pm); |
|
selection.select(pm.index(1, 0), QItemSelectionModel::Select | QItemSelectionModel::Rows); |
|
const QModelIndexList lst = selection.selectedIndexes(); |
|
QCOMPARE(lst.count(), 3); |
|
for (int col = 0; col < lst.count(); ++col) { |
|
QCOMPARE(lst.at(col).row(), 1); |
|
QCOMPARE(lst.at(col).column(), col); |
|
} |
|
|
|
QSignalSpy layoutATBCSpy(&pm, SIGNAL(layoutAboutToBeChanged())); |
|
QSignalSpy layoutChangedSpy(&pm, SIGNAL(layoutChanged())); |
|
|
|
// When changing the sorting in the QSFPM |
|
qsfpm.sort(0, Qt::DescendingOrder); |
|
|
|
// Then the proxy should emit the layoutChanged signals, and show re-sorted data |
|
QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABC")); |
|
QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("456")); |
|
QCOMPARE(extractRowTexts(&pm, 2), QStringLiteral("123")); |
|
QCOMPARE(layoutATBCSpy.count(), 1); |
|
QCOMPARE(layoutChangedSpy.count(), 1); |
|
|
|
// And the selection should be updated accordingly (it became row 2) |
|
const QModelIndexList lstAfter = selection.selectedIndexes(); |
|
QCOMPARE(lstAfter.count(), 3); |
|
for (int col = 0; col < lstAfter.count(); ++col) { |
|
QCOMPARE(lstAfter.at(col).row(), 2); |
|
QCOMPARE(lstAfter.at(col).column(), col); |
|
} |
|
} |
|
|
|
void shouldReactToModelReset() |
|
{ |
|
// Given two source models, the second one being a QSFPM |
|
KConcatenateRowsProxyModel pm; |
|
pm.addSourceModel(&mod); |
|
|
|
QStandardItemModel mod3; |
|
QList<QStandardItem *> row; |
|
row.append(new QStandardItem(QStringLiteral("1"))); |
|
row.append(new QStandardItem(QStringLiteral("2"))); |
|
row.append(new QStandardItem(QStringLiteral("3"))); |
|
mod3.insertRow(0, row); |
|
row.clear(); |
|
row.append(new QStandardItem(QStringLiteral("4"))); |
|
row.append(new QStandardItem(QStringLiteral("5"))); |
|
row.append(new QStandardItem(QStringLiteral("6"))); |
|
mod3.appendRow(row); |
|
|
|
QSortFilterProxyModel qsfpm; |
|
qsfpm.setSourceModel(&mod3); |
|
pm.addSourceModel(&qsfpm); |
|
|
|
QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABC")); |
|
QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("123")); |
|
QCOMPARE(extractRowTexts(&pm, 2), QStringLiteral("456")); |
|
QSignalSpy rowATBRSpy(&pm, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int))); |
|
QSignalSpy rowRemovedSpy(&pm, SIGNAL(rowsRemoved(QModelIndex,int,int))); |
|
QSignalSpy rowATBISpy(&pm, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int))); |
|
QSignalSpy rowInsertedSpy(&pm, SIGNAL(rowsInserted(QModelIndex,int,int))); |
|
|
|
// When changing the source model of the QSFPM |
|
qsfpm.setSourceModel(&mod2); |
|
|
|
// Then the proxy should emit the row removed/inserted signals, and show the new data |
|
QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABC")); |
|
QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("DEF")); |
|
QCOMPARE(rowSpyToText(rowATBRSpy), QStringLiteral("1,2")); |
|
QCOMPARE(rowSpyToText(rowRemovedSpy), QStringLiteral("1,2")); |
|
QCOMPARE(rowSpyToText(rowATBISpy), QStringLiteral("1,1")); |
|
QCOMPARE(rowSpyToText(rowInsertedSpy), QStringLiteral("1,1")); |
|
} |
|
|
|
private: |
|
QStandardItemModel mod; |
|
QStandardItemModel mod2; |
|
}; |
|
|
|
QTEST_MAIN(tst_KConcatenateRowsProxyModel) |
|
|
|
#include "kconcatenaterowsproxymodeltest.moc"
|
|
|