File indexing completed on 2025-01-05 05:00:00
0001 /* 0002 * SPDX-FileCopyrightText: 2014 Mario Bensi <mbensi@ipsquad.net> 0003 SPDX-FileCopyrightText: 2014 Kevin Ottens <ervin@kde.org> 0004 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 0008 #include <testlib/qtest_zanshin.h> 0009 0010 #include <memory> 0011 0012 #include <QAbstractItemModelTester> 0013 #include <QColor> 0014 #include <QMimeData> 0015 0016 #include "utils/mockobject.h" 0017 0018 #include "domain/taskqueries.h" 0019 #include "domain/taskrepository.h" 0020 #include "presentation/querytreemodel.h" 0021 0022 using namespace mockitopp; 0023 0024 Q_DECLARE_METATYPE(QModelIndex) 0025 Q_DECLARE_METATYPE(QList<QColor>) 0026 0027 class QueryTreeModelTest : public QObject 0028 { 0029 Q_OBJECT 0030 public: 0031 explicit QueryTreeModelTest(QObject *parent = nullptr) 0032 : QObject(parent) 0033 { 0034 qRegisterMetaType<QModelIndex>(); 0035 } 0036 0037 private: 0038 Domain::Task::List createTasks() const 0039 { 0040 Domain::Task::List result; 0041 0042 const QStringList titles = {"first", "second", "third"}; 0043 const QList<bool> doneStates = {true, false, false}; 0044 Q_ASSERT(titles.size() == doneStates.size()); 0045 0046 result.reserve(titles.size()); 0047 for (int i = 0; i < titles.size(); i++) { 0048 auto task = Domain::Task::Ptr::create(); 0049 task->setTitle(titles.at(i)); 0050 task->setDone(doneStates.at(i)); 0051 result << task; 0052 } 0053 0054 return result; 0055 } 0056 0057 Domain::Task::List createChildrenTasks() const 0058 { 0059 Domain::Task::List result; 0060 0061 const QStringList titles = {"childFirst", "childSecond", "childThird"}; 0062 const QList<bool> doneStates = {true, false, false}; 0063 Q_ASSERT(titles.size() == doneStates.size()); 0064 0065 result.reserve(titles.size()); 0066 for (int i = 0; i < titles.size(); i++) { 0067 auto task = Domain::Task::Ptr::create(); 0068 task->setTitle(titles.at(i)); 0069 task->setDone(doneStates.at(i)); 0070 result << task; 0071 } 0072 0073 return result; 0074 } 0075 0076 static QVariant standardDataFunction(const Domain::Task::Ptr &task, int role, int) 0077 { 0078 if (role != Qt::DisplayRole && role != Qt::CheckStateRole) { 0079 return QVariant(); 0080 } 0081 0082 if (role == Qt::DisplayRole) 0083 return task->title(); 0084 else 0085 return task->isDone() ? Qt::Checked : Qt::Unchecked; 0086 } 0087 0088 private slots: 0089 void shouldHaveRoleNames() 0090 { 0091 // GIVEN 0092 auto queryGenerator = [](const QColor &) { 0093 return Domain::QueryResult<QColor>::Ptr(); 0094 }; 0095 auto flagsFunction = [](const QColor &) { 0096 return Qt::NoItemFlags; 0097 }; 0098 auto dataFunction = [](const QColor &, int, int) { 0099 return QVariant(); 0100 }; 0101 auto setDataFunction = [](const QColor &, const QVariant &, int) { 0102 return false; 0103 }; 0104 Presentation::QueryTreeModel<QColor> model(queryGenerator, flagsFunction, dataFunction, setDataFunction); 0105 0106 // WHEN 0107 auto roles = model.roleNames(); 0108 0109 // THEN 0110 QCOMPARE(roles.value(Qt::DisplayRole), QByteArray("display")); 0111 QCOMPARE(roles.value(Presentation::QueryTreeModelBase::ObjectRole), QByteArray("object")); 0112 QCOMPARE(roles.value(Presentation::QueryTreeModelBase::IconNameRole), QByteArray("icon")); 0113 QCOMPARE(roles.value(Presentation::QueryTreeModelBase::IsDefaultRole), QByteArray("default")); 0114 } 0115 0116 void shouldListTasks() 0117 { 0118 // GIVEN 0119 auto tasks = createTasks(); 0120 auto provider = Domain::QueryResultProvider<Domain::Task::Ptr>::Ptr::create(); 0121 foreach (const auto &task, tasks) 0122 provider->append(task); 0123 0124 auto childrenTasks = createChildrenTasks(); 0125 auto childrenProvider = Domain::QueryResultProvider<Domain::Task::Ptr>::Ptr::create(); 0126 foreach (const auto &task, childrenTasks) 0127 childrenProvider->append(task); 0128 0129 auto childrenList = Domain::QueryResult<Domain::Task::Ptr>::create(childrenProvider); 0130 auto emptyProvider = Domain::QueryResultProvider<Domain::Task::Ptr>::Ptr::create(); 0131 auto emptyList = Domain::QueryResult<Domain::Task::Ptr>::create(emptyProvider); 0132 0133 Utils::MockObject<Domain::TaskQueries> queryMock; 0134 queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(0)).thenReturn(childrenList); 0135 queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(1)).thenReturn(emptyList); 0136 queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(2)).thenReturn(emptyList); 0137 queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(0)).thenReturn(emptyList); 0138 queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(1)).thenReturn(emptyList); 0139 queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(2)).thenReturn(emptyList); 0140 0141 // WHEN 0142 auto queryGenerator = [&](const Domain::Task::Ptr &task) { 0143 if (!task) 0144 return Domain::QueryResult<Domain::Task::Ptr>::create(provider); 0145 else 0146 return queryMock.getInstance()->findChildren(task); 0147 }; 0148 auto flagsFunction = [](const Domain::Task::Ptr &) { 0149 return Qt::ItemIsSelectable 0150 | Qt::ItemIsEnabled 0151 | Qt::ItemIsEditable 0152 | Qt::ItemIsUserCheckable; 0153 }; 0154 auto setDataFunction = [](const Domain::Task::Ptr &, const QVariant &, int) { 0155 return false; 0156 }; 0157 Presentation::QueryTreeModel<Domain::Task::Ptr> model(queryGenerator, flagsFunction, standardDataFunction, setDataFunction, nullptr); 0158 new QAbstractItemModelTester(&model, this); 0159 0160 // THEN 0161 QCOMPARE(model.rowCount(), 3); 0162 QCOMPARE(model.rowCount(model.index(0, 0)), 3); 0163 QCOMPARE(model.rowCount(model.index(1, 0)), 0); 0164 QCOMPARE(model.rowCount(model.index(2, 0)), 0); 0165 QCOMPARE(model.rowCount(model.index(0, 0, model.index(0, 0))), 0); 0166 QCOMPARE(model.rowCount(model.index(1, 0, model.index(0, 0))), 0); 0167 QCOMPARE(model.rowCount(model.index(2, 0, model.index(0, 0))), 0); 0168 QCOMPARE(model.rowCount(model.index(3, 0, model.index(0, 0))), 3); 0169 0170 for (int i = 0; i < tasks.size(); i++) { 0171 auto task = tasks.at(i); 0172 auto index = model.index(i, 0); 0173 0174 QCOMPARE(model.data(index), model.data(index, Qt::DisplayRole)); 0175 QCOMPARE(model.data(index).toString(), task->title()); 0176 QCOMPARE(model.data(index, Qt::CheckStateRole).toInt() == Qt::Checked, task->isDone()); 0177 QCOMPARE(model.data(index, Qt::CheckStateRole).toInt() == Qt::Unchecked, !task->isDone()); 0178 } 0179 0180 for (int i = 0; i < childrenTasks.size(); i++) { 0181 auto task = childrenTasks.at(i); 0182 auto index = model.index(i, 0, model.index(0, 0)); 0183 0184 QCOMPARE(model.data(index), model.data(index, Qt::DisplayRole)); 0185 QCOMPARE(model.data(index).toString(), task->title()); 0186 QCOMPARE(model.data(index, Qt::CheckStateRole).toInt() == Qt::Checked, task->isDone()); 0187 QCOMPARE(model.data(index, Qt::CheckStateRole).toInt() == Qt::Unchecked, !task->isDone()); 0188 } 0189 } 0190 0191 void shouldDealWithNullQueriesProperly() 0192 { 0193 // GIVEN 0194 auto queryGenerator = [](const QString &) { 0195 return Domain::QueryResult<QString>::Ptr(); 0196 }; 0197 auto flagsFunction = [](const QString &) { 0198 return Qt::NoItemFlags; 0199 }; 0200 auto dataFunction = [](const QString &, int, int) { 0201 return QVariant(); 0202 }; 0203 auto setDataFunction = [](const QString &, const QVariant &, int) { 0204 return false; 0205 }; 0206 0207 // WHEN 0208 Presentation::QueryTreeModel<QString> model(queryGenerator, flagsFunction, dataFunction, setDataFunction, nullptr); 0209 new QAbstractItemModelTester(&model, this); 0210 0211 // THEN 0212 QCOMPARE(model.rowCount(), 0); 0213 } 0214 0215 void shouldReactToTaskAdd() 0216 { 0217 // GIVEN 0218 auto tasks = createTasks(); 0219 auto provider = Domain::QueryResultProvider<Domain::Task::Ptr>::Ptr::create(); 0220 provider->append(tasks.at(1)); 0221 provider->append(tasks.at(2)); 0222 0223 auto childrenTasks = createChildrenTasks(); 0224 auto childrenProvider = Domain::QueryResultProvider<Domain::Task::Ptr>::Ptr::create(); 0225 foreach (const auto &task, childrenTasks) 0226 childrenProvider->append(task); 0227 0228 auto childrenList = Domain::QueryResult<Domain::Task::Ptr>::create(childrenProvider); 0229 auto emptyProvider = Domain::QueryResultProvider<Domain::Task::Ptr>::Ptr::create(); 0230 auto emptyList = Domain::QueryResult<Domain::Task::Ptr>::create(emptyProvider); 0231 0232 Utils::MockObject<Domain::TaskQueries> queryMock; 0233 queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(1)).thenReturn(emptyList); 0234 queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(2)).thenReturn(emptyList); 0235 0236 auto queryGenerator = [&](const Domain::Task::Ptr &task) { 0237 if (!task) 0238 return Domain::QueryResult<Domain::Task::Ptr>::create(provider); 0239 else 0240 return queryMock.getInstance()->findChildren(task); 0241 }; 0242 auto flagsFunction = [](const Domain::Task::Ptr &) { 0243 return Qt::ItemIsSelectable 0244 | Qt::ItemIsEnabled 0245 | Qt::ItemIsEditable 0246 | Qt::ItemIsUserCheckable; 0247 }; 0248 0249 auto setDataFunction = [](const Domain::Task::Ptr &, const QVariant &, int) { 0250 return false; 0251 }; 0252 Presentation::QueryTreeModel<Domain::Task::Ptr> model(queryGenerator, flagsFunction, standardDataFunction, setDataFunction, nullptr); 0253 new QAbstractItemModelTester(&model, this); 0254 QSignalSpy aboutToBeInsertedSpy(&model, &QAbstractItemModel::rowsAboutToBeInserted); 0255 QSignalSpy insertedSpy(&model, &QAbstractItemModel::rowsInserted); 0256 0257 // WHEN 0258 queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(0)).thenReturn(childrenList); 0259 queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(0)).thenReturn(emptyList); 0260 queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(1)).thenReturn(emptyList); 0261 queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(2)).thenReturn(emptyList); 0262 provider->insert(0, tasks.at(0)); 0263 0264 // THEN 0265 QCOMPARE(aboutToBeInsertedSpy.size(), 1); 0266 QCOMPARE(aboutToBeInsertedSpy.first().at(0).toModelIndex(), QModelIndex()); 0267 QCOMPARE(aboutToBeInsertedSpy.first().at(1).toInt(), 0); 0268 QCOMPARE(aboutToBeInsertedSpy.first().at(2).toInt(), 0); 0269 QCOMPARE(insertedSpy.size(), 1); 0270 QCOMPARE(insertedSpy.first().at(0).toModelIndex(), QModelIndex()); 0271 QCOMPARE(insertedSpy.first().at(1).toInt(), 0); 0272 QCOMPARE(insertedSpy.first().at(2).toInt(), 0); 0273 } 0274 0275 void shouldReactToChilrenTaskAdd() 0276 { 0277 // GIVEN 0278 auto tasks = createTasks(); 0279 auto provider = Domain::QueryResultProvider<Domain::Task::Ptr>::Ptr::create(); 0280 foreach (const auto &task, tasks) 0281 provider->append(task); 0282 0283 auto childrenTasks = createChildrenTasks(); 0284 auto childrenProvider = Domain::QueryResultProvider<Domain::Task::Ptr>::Ptr::create(); 0285 childrenProvider->append(childrenTasks.at(0)); 0286 childrenProvider->append(childrenTasks.at(1)); 0287 0288 auto childrenList = Domain::QueryResult<Domain::Task::Ptr>::create(childrenProvider); 0289 auto emptyProvider = Domain::QueryResultProvider<Domain::Task::Ptr>::Ptr::create(); 0290 auto emptyList = Domain::QueryResult<Domain::Task::Ptr>::create(emptyProvider); 0291 0292 Utils::MockObject<Domain::TaskQueries> queryMock; 0293 queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(0)).thenReturn(childrenList); 0294 queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(1)).thenReturn(emptyList); 0295 queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(2)).thenReturn(emptyList); 0296 queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(0)).thenReturn(emptyList); 0297 queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(1)).thenReturn(emptyList); 0298 0299 auto queryGenerator = [&](const Domain::Task::Ptr &task) { 0300 if (!task) 0301 return Domain::QueryResult<Domain::Task::Ptr>::create(provider); 0302 else 0303 return queryMock.getInstance()->findChildren(task); 0304 }; 0305 auto flagsFunction = [](const Domain::Task::Ptr &) { 0306 return Qt::ItemIsSelectable 0307 | Qt::ItemIsEnabled 0308 | Qt::ItemIsEditable 0309 | Qt::ItemIsUserCheckable; 0310 }; 0311 auto setDataFunction = [](const Domain::Task::Ptr &, const QVariant &, int) { 0312 return false; 0313 }; 0314 Presentation::QueryTreeModel<Domain::Task::Ptr> model(queryGenerator, flagsFunction, standardDataFunction, setDataFunction, nullptr); 0315 new QAbstractItemModelTester(&model, this); 0316 QSignalSpy aboutToBeInsertedSpy(&model, &QAbstractItemModel::rowsAboutToBeInserted); 0317 QSignalSpy insertedSpy(&model, &QAbstractItemModel::rowsInserted); 0318 0319 // WHEN 0320 queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(2)).thenReturn(emptyList); 0321 childrenProvider->insert(1, tasks.at(2)); 0322 0323 // THEN 0324 QCOMPARE(aboutToBeInsertedSpy.size(), 1); 0325 QCOMPARE(aboutToBeInsertedSpy.first().at(0).toModelIndex(), model.index(0, 0)); 0326 QCOMPARE(aboutToBeInsertedSpy.first().at(1).toInt(), 1); 0327 QCOMPARE(aboutToBeInsertedSpy.first().at(2).toInt(), 1); 0328 QCOMPARE(insertedSpy.size(), 1); 0329 QCOMPARE(insertedSpy.first().at(0).toModelIndex(), model.index(0, 0)); 0330 QCOMPARE(insertedSpy.first().at(1).toInt(), 1); 0331 QCOMPARE(insertedSpy.first().at(2).toInt(), 1); 0332 } 0333 0334 void shouldReactToTaskRemove() 0335 { 0336 // GIVEN 0337 auto tasks = createTasks(); 0338 auto provider = Domain::QueryResultProvider<Domain::Task::Ptr>::Ptr::create(); 0339 foreach (const auto &task, tasks) 0340 provider->append(task); 0341 0342 auto childrenTasks = createChildrenTasks(); 0343 auto childrenProvider = Domain::QueryResultProvider<Domain::Task::Ptr>::Ptr::create(); 0344 foreach (const auto &task, childrenTasks) 0345 childrenProvider->append(task); 0346 0347 auto childrenList = Domain::QueryResult<Domain::Task::Ptr>::create(childrenProvider); 0348 auto emptyProvider = Domain::QueryResultProvider<Domain::Task::Ptr>::Ptr::create(); 0349 auto emptyList = Domain::QueryResult<Domain::Task::Ptr>::create(emptyProvider); 0350 0351 Utils::MockObject<Domain::TaskQueries> queryMock; 0352 queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(0)).thenReturn(childrenList); 0353 queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(1)).thenReturn(emptyList); 0354 queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(2)).thenReturn(emptyList); 0355 queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(0)).thenReturn(emptyList); 0356 queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(1)).thenReturn(emptyList); 0357 queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(2)).thenReturn(emptyList); 0358 0359 auto queryGenerator = [&](const Domain::Task::Ptr &task) { 0360 if (!task) 0361 return Domain::QueryResult<Domain::Task::Ptr>::create(provider); 0362 else 0363 return queryMock.getInstance()->findChildren(task); 0364 }; 0365 auto flagsFunction = [](const Domain::Task::Ptr &) { 0366 return Qt::ItemIsSelectable 0367 | Qt::ItemIsEnabled 0368 | Qt::ItemIsEditable 0369 | Qt::ItemIsUserCheckable; 0370 }; 0371 auto setDataFunction = [](const Domain::Task::Ptr &, const QVariant &, int) { 0372 return false; 0373 }; 0374 Presentation::QueryTreeModel<Domain::Task::Ptr> model(queryGenerator, flagsFunction, standardDataFunction, setDataFunction, nullptr); 0375 new QAbstractItemModelTester(&model, this); 0376 QSignalSpy aboutToBeRemovedSpy(&model, &QAbstractItemModel::rowsAboutToBeRemoved); 0377 QSignalSpy removedSpy(&model, &QAbstractItemModel::rowsRemoved); 0378 QSignalSpy aboutToBeInsertedSpy(&model, &QAbstractItemModel::rowsAboutToBeInserted); 0379 QSignalSpy insertedSpy(&model, &QAbstractItemModel::rowsInserted); 0380 0381 QModelIndex removeIndex = model.index(0, 0); 0382 0383 // WHEN 0384 // Remove children 0385 childrenProvider->removeAt(0); 0386 childrenProvider->removeAt(0); 0387 childrenProvider->removeAt(0); 0388 // Move children to Top Level 0389 provider->append(childrenTasks.at(0)); 0390 provider->append(childrenTasks.at(1)); 0391 provider->append(childrenTasks.at(2)); 0392 // Remove firt element from topLevel 0393 provider->removeAt(0); 0394 0395 // THEN 0396 QCOMPARE(aboutToBeRemovedSpy.size(), 4); 0397 QCOMPARE(removedSpy.size(), 4); 0398 for (int i = 0; i < aboutToBeRemovedSpy.size(); i++) { 0399 if (i != 3) 0400 QCOMPARE(aboutToBeRemovedSpy.at(i).at(0).toModelIndex(), removeIndex); 0401 else 0402 QCOMPARE(aboutToBeRemovedSpy.at(i).at(0).toModelIndex(), QModelIndex()); 0403 QCOMPARE(aboutToBeRemovedSpy.at(i).at(1).toInt(), 0); 0404 QCOMPARE(aboutToBeRemovedSpy.at(i).at(2).toInt(), 0); 0405 0406 if (i != 3) 0407 QCOMPARE(removedSpy.at(i).at(0).toModelIndex(), removeIndex); 0408 else 0409 QCOMPARE(removedSpy.at(i).at(0).toModelIndex(), QModelIndex()); 0410 QCOMPARE(removedSpy.at(i).at(1).toInt(), 0); 0411 QCOMPARE(removedSpy.at(i).at(2).toInt(), 0); 0412 } 0413 0414 QCOMPARE(aboutToBeInsertedSpy.size(), 3); 0415 QCOMPARE(insertedSpy.size(), 3); 0416 for (int i = 0; i < aboutToBeInsertedSpy.size(); i++) { 0417 QCOMPARE(aboutToBeInsertedSpy.at(i).at(0).toModelIndex(), QModelIndex()); 0418 QCOMPARE(aboutToBeInsertedSpy.at(i).at(1).toInt(), i + 3); 0419 QCOMPARE(aboutToBeInsertedSpy.at(i).at(2).toInt(), i + 3); 0420 QCOMPARE(insertedSpy.at(i).at(0).toModelIndex(), QModelIndex()); 0421 QCOMPARE(insertedSpy.at(i).at(1).toInt(), i + 3); 0422 QCOMPARE(insertedSpy.at(i).at(1).toInt(), i + 3); 0423 } 0424 } 0425 0426 void shouldReactToTaskChange() 0427 { 0428 // GIVEN 0429 // GIVEN 0430 auto tasks = createTasks(); 0431 auto provider = Domain::QueryResultProvider<Domain::Task::Ptr>::Ptr::create(); 0432 foreach (const auto &task, tasks) 0433 provider->append(task); 0434 0435 auto childrenTasks = createChildrenTasks(); 0436 auto childrenProvider = Domain::QueryResultProvider<Domain::Task::Ptr>::Ptr::create(); 0437 foreach (const auto &task, childrenTasks) 0438 childrenProvider->append(task); 0439 0440 auto childrenList = Domain::QueryResult<Domain::Task::Ptr>::create(childrenProvider); 0441 auto emptyProvider = Domain::QueryResultProvider<Domain::Task::Ptr>::Ptr::create(); 0442 auto emptyList = Domain::QueryResult<Domain::Task::Ptr>::create(emptyProvider); 0443 0444 Utils::MockObject<Domain::TaskQueries> queryMock; 0445 queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(0)).thenReturn(childrenList); 0446 queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(1)).thenReturn(emptyList); 0447 queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(2)).thenReturn(emptyList); 0448 queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(0)).thenReturn(emptyList); 0449 queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(1)).thenReturn(emptyList); 0450 queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(2)).thenReturn(emptyList); 0451 0452 // WHEN 0453 auto queryGenerator = [&](const Domain::Task::Ptr &task) { 0454 if (!task) 0455 return Domain::QueryResult<Domain::Task::Ptr>::create(provider); 0456 else 0457 return queryMock.getInstance()->findChildren(task); 0458 }; 0459 auto flagsFunction = [](const Domain::Task::Ptr &) { 0460 return Qt::ItemIsSelectable 0461 | Qt::ItemIsEnabled 0462 | Qt::ItemIsEditable 0463 | Qt::ItemIsUserCheckable; 0464 }; 0465 auto setDataFunction = [](const Domain::Task::Ptr &, const QVariant &, int) { 0466 return false; 0467 }; 0468 Presentation::QueryTreeModel<Domain::Task::Ptr> model(queryGenerator, flagsFunction, standardDataFunction, setDataFunction, nullptr); 0469 new QAbstractItemModelTester(&model, this); 0470 QSignalSpy dataChangedSpy(&model, &QAbstractItemModel::dataChanged); 0471 0472 // WHEN 0473 tasks.at(2)->setDone(true); 0474 childrenTasks.at(2)->setDone(true); 0475 provider->replace(2, tasks.at(2)); 0476 childrenProvider->replace(2, tasks.at(2)); 0477 0478 // THEN 0479 QCOMPARE(dataChangedSpy.size(), 2); 0480 QCOMPARE(dataChangedSpy.first().at(0).toModelIndex(), model.index(2, 0)); 0481 QCOMPARE(dataChangedSpy.first().at(1).toModelIndex(), model.index(2, 0)); 0482 QCOMPARE(dataChangedSpy.last().at(0).toModelIndex(), model.index(2, 0, model.index(0, 0))); 0483 QCOMPARE(dataChangedSpy.last().at(1).toModelIndex(), model.index(2, 0, model.index(0, 0))); 0484 } 0485 0486 void shouldAllowEditsAndChecks() 0487 { 0488 // GIVEN 0489 auto tasks = createTasks(); 0490 auto provider = Domain::QueryResultProvider<Domain::Task::Ptr>::Ptr::create(); 0491 foreach (const auto &task, tasks) 0492 provider->append(task); 0493 0494 auto childrenTasks = createChildrenTasks(); 0495 auto childrenProvider = Domain::QueryResultProvider<Domain::Task::Ptr>::Ptr::create(); 0496 foreach (const auto &task, childrenTasks) 0497 childrenProvider->append(task); 0498 0499 auto childrenList = Domain::QueryResult<Domain::Task::Ptr>::create(childrenProvider); 0500 auto emptyProvider = Domain::QueryResultProvider<Domain::Task::Ptr>::Ptr::create(); 0501 auto emptyList = Domain::QueryResult<Domain::Task::Ptr>::create(emptyProvider); 0502 0503 Utils::MockObject<Domain::TaskQueries> queryMock; 0504 queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(0)).thenReturn(childrenList); 0505 queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(1)).thenReturn(emptyList); 0506 queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(2)).thenReturn(emptyList); 0507 queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(0)).thenReturn(emptyList); 0508 queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(1)).thenReturn(emptyList); 0509 queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(2)).thenReturn(emptyList); 0510 0511 // WHEN 0512 auto queryGenerator = [&](const Domain::Task::Ptr &task) { 0513 if (!task) 0514 return Domain::QueryResult<Domain::Task::Ptr>::create(provider); 0515 else 0516 return queryMock.getInstance()->findChildren(task); 0517 }; 0518 auto flagsFunction = [](const Domain::Task::Ptr &) { 0519 return Qt::ItemIsSelectable 0520 | Qt::ItemIsEnabled 0521 | Qt::ItemIsEditable 0522 | Qt::ItemIsUserCheckable; 0523 }; 0524 auto setDataFunction = [](const Domain::Task::Ptr &, const QVariant &, int) { 0525 return false; 0526 }; 0527 Presentation::QueryTreeModel<Domain::Task::Ptr> model(queryGenerator, flagsFunction, standardDataFunction, setDataFunction, nullptr); 0528 new QAbstractItemModelTester(&model, this); 0529 0530 // WHEN 0531 // Nothing particular 0532 0533 // THEN 0534 for (int row = 0; row < tasks.size(); row++) { 0535 QVERIFY(model.flags(model.index(row, 0)) & Qt::ItemIsEditable); 0536 QVERIFY(model.flags(model.index(row, 0)) & Qt::ItemIsUserCheckable); 0537 } 0538 for (int row = 0; row < childrenTasks.size(); row++) { 0539 QVERIFY(model.flags(model.index(row, 0, model.index(0, 0))) & Qt::ItemIsEditable); 0540 QVERIFY(model.flags(model.index(row, 0, model.index(0, 0))) & Qt::ItemIsUserCheckable); 0541 } 0542 } 0543 0544 void shouldSaveChanges() 0545 { 0546 // GIVEN 0547 auto tasks = createTasks(); 0548 const int taskPos = 1; 0549 const auto task = tasks[taskPos]; 0550 auto provider = Domain::QueryResultProvider<Domain::Task::Ptr>::Ptr::create(); 0551 foreach (const auto &task, tasks) 0552 provider->append(task); 0553 0554 auto childrenTasks = createChildrenTasks(); 0555 auto childrenProvider = Domain::QueryResultProvider<Domain::Task::Ptr>::Ptr::create(); 0556 foreach (const auto &task, childrenTasks) 0557 childrenProvider->append(task); 0558 0559 auto childrenList = Domain::QueryResult<Domain::Task::Ptr>::create(childrenProvider); 0560 auto emptyProvider = Domain::QueryResultProvider<Domain::Task::Ptr>::Ptr::create(); 0561 auto emptyList = Domain::QueryResult<Domain::Task::Ptr>::create(emptyProvider); 0562 0563 Utils::MockObject<Domain::TaskQueries> queryMock; 0564 queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(0)).thenReturn(childrenList); 0565 queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(1)).thenReturn(emptyList); 0566 queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(2)).thenReturn(emptyList); 0567 queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(0)).thenReturn(emptyList); 0568 queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(1)).thenReturn(emptyList); 0569 queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(2)).thenReturn(emptyList); 0570 0571 Utils::MockObject<Domain::TaskRepository> repositoryMock; 0572 repositoryMock(&Domain::TaskRepository::update).when(task).thenReturn(nullptr); 0573 0574 auto queryGenerator = [&](const Domain::Task::Ptr &task) { 0575 if (!task) 0576 return Domain::QueryResult<Domain::Task::Ptr>::create(provider); 0577 else 0578 return queryMock.getInstance()->findChildren(task); 0579 }; 0580 auto flagsFunction = [](const Domain::Task::Ptr &) { 0581 return Qt::ItemIsSelectable 0582 | Qt::ItemIsEnabled 0583 | Qt::ItemIsEditable 0584 | Qt::ItemIsUserCheckable; 0585 }; 0586 auto setDataFunction = [&](const Domain::Task::Ptr &task, const QVariant &value, int role) { 0587 if (role != Qt::EditRole && role != Qt::CheckStateRole) { 0588 return false; 0589 } 0590 0591 if (role == Qt::EditRole) { 0592 task->setTitle(value.toString()); 0593 } else { 0594 task->setDone(value.toInt() == Qt::Checked); 0595 } 0596 0597 repositoryMock.getInstance()->update(task); 0598 return true; 0599 }; 0600 Presentation::QueryTreeModel<Domain::Task::Ptr> model(queryGenerator, flagsFunction, standardDataFunction, setDataFunction, nullptr); 0601 new QAbstractItemModelTester(&model, this); 0602 QSignalSpy titleChangedSpy(task.data(), &Domain::Task::titleChanged); 0603 QSignalSpy doneChangedSpy(task.data(), &Domain::Task::doneChanged); 0604 0605 // WHEN 0606 const auto index = model.index(taskPos, 0); 0607 model.setData(index, "alternate second"); 0608 model.setData(index, Qt::Checked, Qt::CheckStateRole); 0609 0610 // THEN 0611 QVERIFY(repositoryMock(&Domain::TaskRepository::update).when(task).exactly(2)); 0612 0613 QCOMPARE(titleChangedSpy.size(), 1); 0614 QCOMPARE(titleChangedSpy.first().first().toString(), QStringLiteral("alternate second")); 0615 QCOMPARE(doneChangedSpy.size(), 1); 0616 QCOMPARE(doneChangedSpy.first().first().toBool(), true); 0617 } 0618 0619 void shouldProvideUnderlyingObject() 0620 { 0621 // GIVEN 0622 auto provider = Domain::QueryResultProvider<QColor>::Ptr::create(); 0623 provider->append(Qt::red); 0624 provider->append(Qt::green); 0625 provider->append(Qt::blue); 0626 0627 auto queryGenerator = [&](const QColor &color) { 0628 if (!color.isValid()) 0629 return Domain::QueryResult<QColor>::create(provider); 0630 else 0631 return Domain::QueryResult<QColor>::Ptr(); 0632 }; 0633 auto flagsFunction = [](const QColor &) { 0634 return Qt::NoItemFlags; 0635 }; 0636 auto dataFunction = [](const QColor &, int, int) { 0637 return QVariant(); 0638 }; 0639 auto setDataFunction = [](const QColor &, const QVariant &, int) { 0640 return false; 0641 }; 0642 Presentation::QueryTreeModel<QColor> model(queryGenerator, flagsFunction, dataFunction, setDataFunction); 0643 new QAbstractItemModelTester(&model, this); 0644 0645 // WHEN 0646 const QModelIndex index = model.index(1, 0); 0647 const QVariant data = index.data(Presentation::QueryTreeModelBase::ObjectRole); 0648 0649 // THEN 0650 QVERIFY(data.isValid()); 0651 QCOMPARE(data.value<QColor>(), provider->data().at(1)); 0652 } 0653 0654 void shouldProvideUnderlyingTask() 0655 { 0656 // GIVEN 0657 auto provider = Domain::QueryResultProvider<Domain::Task::Ptr>::Ptr::create(); 0658 foreach (const auto &task, createTasks()) 0659 provider->append(task); 0660 0661 auto queryGenerator = [&](const Domain::Task::Ptr &task) { 0662 if (!task) 0663 return Domain::QueryResult<Domain::Task::Ptr>::create(provider); 0664 else 0665 return Domain::QueryResult<Domain::Task::Ptr>::Ptr(); 0666 }; 0667 auto flagsFunction = [](const Domain::Task::Ptr &) { 0668 return Qt::NoItemFlags; 0669 }; 0670 auto dataFunction = [](const Domain::Task::Ptr &, int, int) { 0671 return QVariant(); 0672 }; 0673 auto setDataFunction = [](const Domain::Task::Ptr &, const QVariant &, int) { 0674 return false; 0675 }; 0676 Presentation::QueryTreeModel<Domain::Task::Ptr> model(queryGenerator, flagsFunction, dataFunction, setDataFunction); 0677 new QAbstractItemModelTester(&model, this); 0678 0679 // WHEN 0680 const QModelIndex index = model.index(1, 0); 0681 const QVariant data = index.data(Presentation::QueryTreeModelBase::ObjectRole); 0682 0683 // THEN 0684 QVERIFY(data.isValid()); 0685 QVERIFY(!data.value<Domain::Task::Ptr>().isNull()); 0686 QCOMPARE(data.value<Domain::Task::Ptr>(), provider->data().at(1)); 0687 } 0688 0689 void shouldMoveOnlyDuringDragAndDrop() 0690 { 0691 // GIVEN 0692 auto queryGenerator = [&] (const QColor &) { 0693 return Domain::QueryResult<QColor>::Ptr(); 0694 }; 0695 auto flagsFunction = [] (const QColor &) { 0696 return Qt::NoItemFlags; 0697 }; 0698 auto dataFunction = [] (const QColor &, int, int) { 0699 return QVariant(); 0700 }; 0701 auto setDataFunction = [] (const QColor &, const QVariant &, int) { 0702 return false; 0703 }; 0704 auto dropFunction = [] (const QMimeData *, Qt::DropAction, const QColor &) { 0705 return false; 0706 }; 0707 auto dragFunction = [] (const QList<QColor> &) { 0708 return nullptr; 0709 }; 0710 0711 Presentation::QueryTreeModel<QColor> model(queryGenerator, flagsFunction, 0712 dataFunction, setDataFunction, 0713 dropFunction, dragFunction, nullptr); 0714 0715 // THEN 0716 QCOMPARE(model.supportedDragActions(), Qt::MoveAction); 0717 QCOMPARE(model.supportedDropActions(), Qt::MoveAction); 0718 } 0719 0720 void shouldCreateMimeData() 0721 { 0722 // GIVEN 0723 auto provider = Domain::QueryResultProvider<QColor>::Ptr::create(); 0724 provider->append(Qt::red); 0725 provider->append(Qt::green); 0726 provider->append(Qt::blue); 0727 0728 auto queryGenerator = [&] (const QColor &color) { 0729 if (!color.isValid()) 0730 return Domain::QueryResult<QColor>::create(provider); 0731 else 0732 return Domain::QueryResult<QColor>::Ptr(); 0733 }; 0734 auto flagsFunction = [] (const QColor &) { 0735 return Qt::NoItemFlags; 0736 }; 0737 auto dataFunction = [] (const QColor &, int, int) { 0738 return QVariant(); 0739 }; 0740 auto setDataFunction = [] (const QColor &, const QVariant &, int) { 0741 return false; 0742 }; 0743 auto dropFunction = [] (const QMimeData *, Qt::DropAction, const QColor &) { 0744 return false; 0745 }; 0746 auto dragFunction = [] (const QList<QColor> &colors) { 0747 auto mimeData = new QMimeData; 0748 mimeData->setColorData(QVariant::fromValue(colors)); 0749 return mimeData; 0750 }; 0751 0752 Presentation::QueryTreeModel<QColor> model(queryGenerator, flagsFunction, 0753 dataFunction, setDataFunction, 0754 dropFunction, dragFunction, nullptr); 0755 new QAbstractItemModelTester(&model, this); 0756 0757 // WHEN 0758 auto data = std::unique_ptr<QMimeData>(model.mimeData(QList<QModelIndex>() << model.index(1, 0) << model.index(2, 0))); 0759 0760 // THEN 0761 QVERIFY(data.get()); 0762 QVERIFY(model.mimeTypes().contains(QStringLiteral("application/x-zanshin-object"))); 0763 QList<QColor> colors; 0764 colors << Qt::green << Qt::blue; 0765 QCOMPARE(data->colorData().value<QList<QColor>>(), colors); 0766 } 0767 0768 void shouldDropMimeData_data() 0769 { 0770 QTest::addColumn<int>("row"); 0771 QTest::addColumn<int>("column"); 0772 QTest::addColumn<int>("parentRow"); 0773 QTest::addColumn<bool>("callExpected"); 0774 0775 QTest::newRow("drop on object") << -1 << -1 << 2 << true; 0776 QTest::newRow("drop between object") << 1 << 0 << -1 << true; 0777 QTest::newRow("drop in empty area") << -1 << -1 << -1 << true; 0778 } 0779 0780 void shouldDropMimeData() 0781 { 0782 // GIVEN 0783 QFETCH(int, row); 0784 QFETCH(int, column); 0785 QFETCH(int, parentRow); 0786 QFETCH(bool, callExpected); 0787 bool dropCalled = false; 0788 const QMimeData *droppedData = nullptr; 0789 QColor colorSeen; 0790 0791 auto provider = Domain::QueryResultProvider<QColor>::Ptr::create(); 0792 provider->append(Qt::red); 0793 provider->append(Qt::green); 0794 provider->append(Qt::blue); 0795 0796 auto queryGenerator = [&] (const QColor &color) { 0797 if (!color.isValid()) 0798 return Domain::QueryResult<QColor>::create(provider); 0799 else 0800 return Domain::QueryResult<QColor>::Ptr(); 0801 }; 0802 auto flagsFunction = [] (const QColor &) { 0803 return Qt::NoItemFlags; 0804 }; 0805 auto dataFunction = [] (const QColor &, int, int) { 0806 return QVariant(); 0807 }; 0808 auto setDataFunction = [] (const QColor &, const QVariant &, int) { 0809 return false; 0810 }; 0811 auto dropFunction = [&] (const QMimeData *data, Qt::DropAction, const QColor &color) { 0812 dropCalled = true; 0813 droppedData = data; 0814 colorSeen = color; 0815 return false; 0816 }; 0817 auto dragFunction = [] (const QList<QColor> &) -> QMimeData* { 0818 return nullptr; 0819 }; 0820 0821 Presentation::QueryTreeModel<QColor> model(queryGenerator, flagsFunction, 0822 dataFunction, setDataFunction, 0823 dropFunction, dragFunction, nullptr); 0824 new QAbstractItemModelTester(&model, this); 0825 0826 // WHEN 0827 auto data = std::make_unique<QMimeData>(); 0828 const QModelIndex parent = parentRow >= 0 ? model.index(parentRow, 0) : QModelIndex(); 0829 model.dropMimeData(data.get(), Qt::MoveAction, row, column, parent); 0830 0831 // THEN 0832 QCOMPARE(dropCalled, callExpected); 0833 if (callExpected) { 0834 QCOMPARE(droppedData, data.get()); 0835 QCOMPARE(colorSeen, parent.data(Presentation::QueryTreeModelBase::ObjectRole).value<QColor>()); 0836 } 0837 } 0838 0839 void shouldPreventCyclesByDragAndDrop() 0840 { 0841 // GIVEN 0842 bool dropCalled = false; 0843 0844 auto topProvider = Domain::QueryResultProvider<QString>::Ptr::create(); 0845 topProvider->append(QStringLiteral("1")); 0846 topProvider->append(QStringLiteral("2")); 0847 topProvider->append(QStringLiteral("3")); 0848 0849 auto firstLevelProvider = Domain::QueryResultProvider<QString>::Ptr::create(); 0850 firstLevelProvider->append(QStringLiteral("2.1")); 0851 firstLevelProvider->append(QStringLiteral("2.2")); 0852 firstLevelProvider->append(QStringLiteral("2.3")); 0853 0854 auto secondLevelProvider = Domain::QueryResultProvider<QString>::Ptr::create(); 0855 secondLevelProvider->append(QStringLiteral("2.1.1")); 0856 secondLevelProvider->append(QStringLiteral("2.1.2")); 0857 secondLevelProvider->append(QStringLiteral("2.1.3")); 0858 0859 auto queryGenerator = [&] (const QString &string) { 0860 if (string.isEmpty()) 0861 return Domain::QueryResult<QString>::create(topProvider); 0862 else if (string == QLatin1StringView("2")) 0863 return Domain::QueryResult<QString>::create(firstLevelProvider); 0864 else if (string == QLatin1StringView("2.1")) 0865 return Domain::QueryResult<QString>::create(secondLevelProvider); 0866 else 0867 return Domain::QueryResult<QString>::Ptr(); 0868 }; 0869 auto flagsFunction = [] (const QString &) { 0870 return Qt::NoItemFlags; 0871 }; 0872 auto dataFunction = [] (const QString &, int, int) { 0873 return QVariant(); 0874 }; 0875 auto setDataFunction = [] (const QString &, const QVariant &, int) { 0876 return false; 0877 }; 0878 auto dropFunction = [&] (const QMimeData *, Qt::DropAction, const QString &) { 0879 dropCalled = true; 0880 return false; 0881 }; 0882 auto dragFunction = [] (const QStringList &strings) -> QMimeData* { 0883 auto data = new QMimeData; 0884 data->setData(QStringLiteral("application/x-zanshin-object"), "object"); 0885 data->setProperty("objects", QVariant::fromValue(strings)); 0886 return data; 0887 }; 0888 0889 Presentation::QueryTreeModel<QString> model(queryGenerator, flagsFunction, 0890 dataFunction, setDataFunction, 0891 dropFunction, dragFunction, nullptr); 0892 new QAbstractItemModelTester(&model, this); 0893 0894 const auto indexes = QModelIndexList() << model.index(0, 0) 0895 << model.index(1, 0) 0896 << model.index(1, 0, model.index(1, 0)); 0897 0898 // WHEN 0899 auto data = std::unique_ptr<QMimeData>(model.mimeData(indexes)); 0900 const auto parent = model.index(1, 0, model.index(0, 0, model.index(1, 0))); 0901 model.dropMimeData(data.get(), Qt::MoveAction, -1, -1, parent); 0902 0903 // THEN 0904 QVERIFY(!dropCalled); 0905 } 0906 }; 0907 0908 ZANSHIN_TEST_MAIN(QueryTreeModelTest) 0909 0910 #include "querytreemodeltest.moc"