BornAgain  1.19.0
Simulate and fit neutron and x-ray scattering at grazing incidence
defaultviewmodel.test.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // qt-mvvm: Model-view-view-model framework for large GUI applications
4 //
5 //! @file mvvm/tests/testviewmodel/defaultviewmodel.test.cpp
6 //! @brief Implements class CLASS?
7 //!
8 //! @homepage http://www.bornagainproject.org
9 //! @license GNU General Public License v3 or higher (see COPYING)
10 //! @copyright Forschungszentrum Jülich GmbH 2020
11 //! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS)
12 //
13 // ************************************************************************************************
14 
15 #include "folderbasedtest.h"
16 #include "google_test.h"
19 #include "mvvm/model/taginfo.h"
32 #include "test_utils.h"
33 #include <QDebug>
34 #include <QJsonObject>
35 #include <QSignalSpy>
36 
37 using namespace ModelView;
38 
40 public:
41  DefaultViewModelTest() : FolderBasedTest("test_DefaultViewModel") {}
43 };
44 
46 
48 {
49  SessionModel model;
50  DefaultViewModel viewModel(&model);
51  EXPECT_EQ(viewModel.rowCount(), 0);
52  EXPECT_EQ(viewModel.columnCount(), 0);
53  EXPECT_EQ(viewModel.sessionItemFromIndex(QModelIndex()), model.rootItem());
54 }
55 
56 //! Single property item in a model.
57 
58 TEST_F(DefaultViewModelTest, fromPropertyItem)
59 {
60  SessionModel model;
61  auto propertyItem = model.insertItem<PropertyItem>();
62  propertyItem->setData(42.0);
63 
64  DefaultViewModel viewModel(&model);
65  EXPECT_EQ(viewModel.rowCount(), 1);
66  EXPECT_EQ(viewModel.columnCount(), 2);
67 
68  // accessing first child under the root item
69  QModelIndex labelIndex = viewModel.index(0, 0);
70  QModelIndex dataIndex = viewModel.index(0, 1);
71 
72  // it should be ViewLabelItem looking at our PropertyItem item
73  auto labelItem = dynamic_cast<ViewLabelItem*>(viewModel.itemFromIndex(labelIndex));
74  ASSERT_TRUE(labelItem != nullptr);
75  EXPECT_EQ(labelItem->item(), propertyItem);
76 
77  auto dataItem = dynamic_cast<ViewDataItem*>(viewModel.itemFromIndex(dataIndex));
78  ASSERT_TRUE(dataItem != nullptr);
79  EXPECT_EQ(dataItem->item(), propertyItem);
80 }
81 
82 //! Single property item in a model, inserted after DefaultViewModel was setup.
83 
84 TEST_F(DefaultViewModelTest, initThenInsert)
85 {
86  SessionModel model;
87  DefaultViewModel viewModel(&model);
88 
89  auto propertyItem = model.insertItem<PropertyItem>();
90  propertyItem->setData(42.0);
91 
92  EXPECT_EQ(viewModel.rowCount(), 1);
93  EXPECT_EQ(viewModel.columnCount(), 2);
94 
95  // accessing first child under the root item
96  QModelIndex labelIndex = viewModel.index(0, 0);
97  QModelIndex dataIndex = viewModel.index(0, 1);
98 
99  // it should be ViewLabelItem looking at our PropertyItem item
100  auto labelItem = dynamic_cast<ViewLabelItem*>(viewModel.itemFromIndex(labelIndex));
101  ASSERT_TRUE(labelItem != nullptr);
102  EXPECT_EQ(labelItem->item(), propertyItem);
103 
104  // Feature: since our PropertyItem got it's value after ViewModel was initialized, the model
105  // still holds ViewEmptyItem and not ViewDataItem.
106  auto dataItem = dynamic_cast<ViewDataItem*>(viewModel.itemFromIndex(dataIndex));
107  ASSERT_TRUE(dataItem != nullptr);
108 }
109 
110 //! Single property item in a model.
111 
112 TEST_F(DefaultViewModelTest, sessionItemFromIndex)
113 {
114  SessionModel model;
115  auto propertyItem = model.insertItem<PropertyItem>();
116  propertyItem->setData(42.0);
117 
118  DefaultViewModel viewModel(&model);
119  EXPECT_EQ(viewModel.rowCount(), 1);
120  EXPECT_EQ(viewModel.columnCount(), 2);
121 
122  // accessing first child under the root item
123  QModelIndex labelIndex = viewModel.index(0, 0);
124  QModelIndex dataIndex = viewModel.index(0, 1);
125 
126  EXPECT_EQ(viewModel.sessionItemFromIndex(QModelIndex()), model.rootItem());
127  EXPECT_EQ(viewModel.sessionItemFromIndex(labelIndex), propertyItem);
128  EXPECT_EQ(viewModel.sessionItemFromIndex(dataIndex), propertyItem);
129 }
130 
131 //! Index from single property item.
132 
133 TEST_F(DefaultViewModelTest, indexFromSessionItem)
134 {
135  SessionModel model;
136  auto propertyItem = model.insertItem<PropertyItem>();
137  propertyItem->setData(42.0);
138 
139  DefaultViewModel viewModel(&model);
140  EXPECT_EQ(viewModel.rowCount(), 1);
141  EXPECT_EQ(viewModel.columnCount(), 2);
142 
143  // accessing first child under the root item
144  QModelIndex labelIndex = viewModel.index(0, 0);
145  QModelIndex dataIndex = viewModel.index(0, 1);
146 
147  QModelIndexList expected{labelIndex, dataIndex};
148  EXPECT_EQ(viewModel.indexOfSessionItem(propertyItem), expected);
149 }
150 
151 //! Find ViewItem's corresponding to given PropertyItem.
152 
153 TEST_F(DefaultViewModelTest, findPropertyItemView)
154 {
155  SessionModel model;
156  auto propertyItem = model.insertItem<PropertyItem>();
157  propertyItem->setData(42.0);
158 
159  DefaultViewModel viewModel(&model);
160  auto views = viewModel.findViews(propertyItem);
161 }
162 
163 //! Constructing ViewModel from single PropertyItem.
164 //! Change thickness property in SessionItem, control dataChanged signals from ViewModel.
165 
166 TEST_F(DefaultViewModelTest, propertyItemDataChanged)
167 {
168  SessionModel model;
169  auto propertyItem = model.insertItem<PropertyItem>();
170  propertyItem->setData(42.0);
171 
172  // constructing viewModel from sample model
173  DefaultViewModel viewModel(&model);
174 
175  QModelIndex dataIndex = viewModel.index(0, 1);
176 
177  QSignalSpy spyDataChanged(&viewModel, &DefaultViewModel::dataChanged);
178 
179  propertyItem->setData(50.0);
180  EXPECT_EQ(spyDataChanged.count(), 1);
181 
182  // dataChanged should report thicknessIndex and two roles
183  QList<QVariant> arguments = spyDataChanged.takeFirst();
184  EXPECT_EQ(arguments.size(), 3); // QModelIndex left, QModelIndex right, QVector<int> roles
185  EXPECT_EQ(arguments.at(0).value<QModelIndex>(), dataIndex);
186  EXPECT_EQ(arguments.at(1).value<QModelIndex>(), dataIndex);
187  QVector<int> expectedRoles = {Qt::DisplayRole, Qt::EditRole};
188  EXPECT_EQ(arguments.at(2).value<QVector<int>>(), expectedRoles);
189 }
190 
191 //! Inserting single top level item.
192 
193 TEST_F(DefaultViewModelTest, insertSingleTopItem)
194 {
195  SessionModel model;
196  DefaultViewModel viewModel(&model);
197 
198  QSignalSpy spyInsert(&viewModel, &DefaultViewModel::rowsInserted);
199 
200  // inserting single item
201  model.insertItem<SessionItem>();
202 
203  // root item should have one child
204  EXPECT_EQ(viewModel.rowCount(), 1);
205  EXPECT_EQ(viewModel.columnCount(), 2);
206 
207  // removing child
208  model.removeItem(model.rootItem(), {"", 0});
209  EXPECT_EQ(spyInsert.count(), 1);
210  EXPECT_EQ(viewModel.rowCount(), 0);
211  EXPECT_EQ(viewModel.columnCount(), 0);
212 
213  QList<QVariant> arguments = spyInsert.takeFirst();
214  EXPECT_EQ(arguments.size(), 3); // QModelIndex &parent, int first, int last
215  EXPECT_EQ(arguments.at(0).value<QModelIndex>(), QModelIndex());
216  EXPECT_EQ(arguments.at(1).value<int>(), 0);
217  EXPECT_EQ(arguments.at(2).value<int>(), 0);
218 }
219 
220 //! Removing single top level item.
221 
222 TEST_F(DefaultViewModelTest, removeSingleTopItem)
223 {
224  SessionModel model;
225 
226  // inserting single item
227  model.insertItem<SessionItem>();
228 
229  // constructing viewModel from sample model
230  DefaultViewModel viewModel(&model);
231 
232  // root item should have one child
233  EXPECT_EQ(viewModel.rowCount(), 1);
234  EXPECT_EQ(viewModel.columnCount(), 2);
235 
236  QSignalSpy spyRemove(&viewModel, &DefaultViewModel::rowsRemoved);
237 
238  // removing child
239  model.removeItem(model.rootItem(), {"", 0});
240  ASSERT_EQ(spyRemove.count(), 1);
241  EXPECT_EQ(viewModel.rowCount(), 0);
242  EXPECT_EQ(viewModel.columnCount(), 0);
243 
244  QList<QVariant> arguments = spyRemove.takeFirst();
245  ASSERT_EQ(arguments.size(), 3); // QModelIndex &parent, int first, int last
246  EXPECT_EQ(arguments.at(0).value<QModelIndex>(), QModelIndex());
247  EXPECT_EQ(arguments.at(1).value<int>(), 0);
248  EXPECT_EQ(arguments.at(2).value<int>(), 0);
249 }
250 
251 //! Remove one of two top level items.
252 
253 TEST_F(DefaultViewModelTest, removeOneOfTopItems)
254 {
255  SessionModel model;
256 
257  // inserting single item
258  model.insertItem<SessionItem>();
259  model.insertItem<SessionItem>();
260 
261  // constructing viewModel from sample model
262  DefaultViewModel viewModel(&model);
263 
264  // root item should have one child
265  EXPECT_EQ(viewModel.rowCount(), 2);
266  EXPECT_EQ(viewModel.columnCount(), 2);
267 
268  QSignalSpy spyRemove(&viewModel, &DefaultViewModel::rowsRemoved);
269  QSignalSpy spyInsert(&viewModel, &DefaultViewModel::rowsInserted);
270 
271  // removing child
272  model.removeItem(model.rootItem(), {"", 0});
273 
274  // removal was called once
275  EXPECT_EQ(spyRemove.count(), 1);
276  EXPECT_EQ(viewModel.rowCount(), 1);
277  EXPECT_EQ(viewModel.columnCount(), 2);
278 
279  // no call to insert
280  EXPECT_EQ(spyInsert.count(), 0);
281 
282  QList<QVariant> arguments = spyRemove.takeFirst();
283  EXPECT_EQ(arguments.size(), 3); // QModelIndex &parent, int first, int last
284  EXPECT_EQ(arguments.at(0).value<QModelIndex>(), QModelIndex());
285  EXPECT_EQ(arguments.at(1).value<int>(), 0);
286  EXPECT_EQ(arguments.at(2).value<int>(), 0);
287 }
288 
289 //! Single property item in ViewModel with various appearance flags.
290 
291 TEST_F(DefaultViewModelTest, propertyItemAppearance)
292 {
293  SessionModel model;
294 
295  // default item
296  auto item1 = model.insertItem<PropertyItem>();
297  item1->setData(42.0);
298 
299  // disabled item
300  auto item2 = model.insertItem<PropertyItem>();
301  item2->setData(42.0);
302  item2->setEnabled(false); // our convention: gray color, read only
303 
304  // read only item
305  auto item3 = model.insertItem<PropertyItem>();
306  item3->setData(42.0);
307  item3->setEditable(false); // our convention: normal color, read only
308 
309  // making view model
310  DefaultViewModel viewModel(&model);
311 
312  // In tests below is important that SessionItem::isEnabled==false means that item is
313  // shown in gray color. While QStandardItem::isEnabled means "no interaction".
314  // In our case QStandardItem::isEnabled should be always true.
315 
316  // ViewLabel and ViewDataItem of item1
317  EXPECT_FALSE(viewModel.flags(viewModel.index(0, 0)) & Qt::ItemIsEditable);
318  EXPECT_TRUE(viewModel.flags(viewModel.index(0, 1)) & Qt::ItemIsEditable);
319 
320  // ViewLabel and ViewDataItem of item2
321  EXPECT_FALSE(viewModel.flags(viewModel.index(1, 0)) & Qt::ItemIsEditable);
322  EXPECT_FALSE(viewModel.flags(viewModel.index(1, 1)) & Qt::ItemIsEditable);
323 
324  // ViewLabel and ViewDataItem of item2
325  EXPECT_FALSE(viewModel.flags(viewModel.index(2, 0)) & Qt::ItemIsEditable);
326  EXPECT_FALSE(viewModel.flags(viewModel.index(2, 1)) & Qt::ItemIsEditable);
327 }
328 
329 //! Signals in ViewModel when property item changes its appearance.
330 
331 TEST_F(DefaultViewModelTest, propertyItemAppearanceChanged)
332 {
333  SessionModel model;
334 
335  // default item
336  auto item = model.insertItem<PropertyItem>();
337  item->setData(42.0);
338 
339  // setting up ViewModel and spying it's dataChanged signals
340  DefaultViewModel viewModel(&model);
341  auto labelView = viewModel.itemFromIndex(viewModel.index(0, 0));
342  auto dataView = viewModel.itemFromIndex(viewModel.index(0, 1));
343  QSignalSpy spyDataChanged(&viewModel, &DefaultViewModel::dataChanged);
344 
345  // Changing item appearance
346  item->setEnabled(false);
347  EXPECT_EQ(spyDataChanged.count(), 2); // change in LabelView and DataView
348 
349  // first pack of arguments is related to ViewLabelItem
350  QList<QVariant> arguments = spyDataChanged.takeFirst();
351  EXPECT_EQ(arguments.size(), 3); // QModelIndex &parent, int first, int last
352  auto index1 = arguments.at(0).value<QModelIndex>();
353  auto index2 = arguments.at(1).value<QModelIndex>();
354  auto roles = arguments.at(2).value<QVector<int>>();
355  EXPECT_EQ(index1, viewModel.indexFromItem(labelView));
356  EXPECT_EQ(index2, viewModel.indexFromItem(labelView));
357 #if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)
358  QVector<int> expected_roles = {Qt::ForegroundRole};
359 #else
360  QVector<int> expected_roles = {Qt::TextColorRole};
361 #endif
362  EXPECT_EQ(roles, expected_roles);
363 
364  // second pack of arguments is related to ViewDataItem
365  arguments = spyDataChanged.takeFirst();
366  EXPECT_EQ(arguments.size(), 3); // QModelIndex &parent, int first, int last
367  index1 = arguments.at(0).value<QModelIndex>();
368  index2 = arguments.at(1).value<QModelIndex>();
369  roles = arguments.at(2).value<QVector<int>>();
370  EXPECT_EQ(index1, viewModel.indexFromItem(dataView));
371  EXPECT_EQ(index2, viewModel.indexFromItem(dataView));
372 
373 #if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)
374  expected_roles = {Qt::ForegroundRole};
375 #else
376  expected_roles = {Qt::TextColorRole};
377 #endif
378 
379  EXPECT_EQ(roles, expected_roles);
380 }
381 
382 //! Signals in ViewModel when property item changes its tooltips.
383 
384 TEST_F(DefaultViewModelTest, tooltipChanged)
385 {
386  SessionModel model;
387 
388  // default item
389  auto item = model.insertItem<PropertyItem>();
390  item->setData(42.0);
391  item->setToolTip("abc");
392 
393  // setting up ViewModel and spying it's dataChanged signals
394  DefaultViewModel viewModel(&model);
395  auto labelView = viewModel.itemFromIndex(viewModel.index(0, 0));
396  auto dataView = viewModel.itemFromIndex(viewModel.index(0, 1));
397 
398  EXPECT_EQ(viewModel.data(viewModel.index(0, 0), Qt::ToolTipRole).toString(), QString("abc"));
399  EXPECT_EQ(viewModel.data(viewModel.index(0, 1), Qt::ToolTipRole).toString(), QString("abc"));
400 
401  QSignalSpy spyDataChanged(&viewModel, &DefaultViewModel::dataChanged);
402 
403  // Changing tooltip
404  item->setToolTip("abc2");
405  EXPECT_EQ(spyDataChanged.count(), 2); // change in LabelView and DataView
406 
407  // first pack of arguments is related to ViewLabelItem
408  QList<QVariant> arguments = spyDataChanged.takeFirst();
409  EXPECT_EQ(arguments.size(), 3); // QModelIndex &parent, int first, int last
410  auto index1 = arguments.at(0).value<QModelIndex>();
411  auto index2 = arguments.at(1).value<QModelIndex>();
412  auto roles = arguments.at(2).value<QVector<int>>();
413  EXPECT_EQ(index1, viewModel.indexFromItem(labelView));
414  EXPECT_EQ(index2, viewModel.indexFromItem(labelView));
415  QVector<int> expected_roles = {Qt::ToolTipRole};
416  EXPECT_EQ(roles, expected_roles);
417 
418  // second pack of arguments is related to ViewDataItem
419  arguments = spyDataChanged.takeFirst();
420  EXPECT_EQ(arguments.size(), 3); // QModelIndex &parent, int first, int last
421  index1 = arguments.at(0).value<QModelIndex>();
422  index2 = arguments.at(1).value<QModelIndex>();
423  roles = arguments.at(2).value<QVector<int>>();
424  EXPECT_EQ(index1, viewModel.indexFromItem(dataView));
425  EXPECT_EQ(index2, viewModel.indexFromItem(dataView));
426 
427  expected_roles = {Qt::ToolTipRole};
428  EXPECT_EQ(roles, expected_roles);
429 }
430 
431 //! Setting property item as ROOT item.
432 
433 TEST_F(DefaultViewModelTest, setPropertyItemAsRoot)
434 {
435  SessionModel model;
436  DefaultViewModel viewModel(&model);
437 
438  QSignalSpy spyAboutReset(&viewModel, &DefaultViewModel::modelAboutToBeReset);
439  QSignalSpy spyReset(&viewModel, &DefaultViewModel::modelReset);
440 
441  auto item = model.insertItem<PropertyItem>();
442  viewModel.setRootSessionItem(item);
443 
444  // new root item doesn't have children
445  EXPECT_EQ(viewModel.rowCount(), 0);
446  EXPECT_EQ(viewModel.columnCount(), 0);
447 
448  EXPECT_EQ(spyAboutReset.count(), 1);
449  EXPECT_EQ(spyReset.count(), 1);
450 
451  // attempt to use nullptr as root item
452  EXPECT_THROW(viewModel.setRootSessionItem(nullptr), std::runtime_error);
453 
454  // attempt to use alien model
455  SessionModel model2;
456  EXPECT_THROW(viewModel.setRootSessionItem(model2.rootItem()), std::runtime_error);
457 }
458 
459 //! Setting property item as ROOT item.
460 //! Same as above, only view model was initialized after.
461 
462 TEST_F(DefaultViewModelTest, setPropertyItemAsRootAfter)
463 {
464  SessionModel model;
465  auto item = model.insertItem<PropertyItem>();
466 
467  DefaultViewModel viewModel(&model);
468  EXPECT_EQ(viewModel.rowCount(), 1);
469  EXPECT_EQ(viewModel.columnCount(), 2);
470 
471  QSignalSpy spyAboutReset(&viewModel, &DefaultViewModel::modelAboutToBeReset);
472  QSignalSpy spyReset(&viewModel, &DefaultViewModel::modelReset);
473  viewModel.setRootSessionItem(item);
474 
475  // new root item doesn't have children
476  EXPECT_EQ(viewModel.rowCount(), 0);
477  EXPECT_EQ(viewModel.columnCount(), 0);
478 
479  EXPECT_EQ(spyAboutReset.count(), 1);
480  EXPECT_EQ(spyReset.count(), 1);
481 
482  // attempt to use nullptr as root item
483  EXPECT_THROW(viewModel.setRootSessionItem(nullptr), std::runtime_error);
484 
485  // attempt to use alien model
486  SessionModel model2;
487  EXPECT_THROW(viewModel.setRootSessionItem(model2.rootItem()), std::runtime_error);
488 }
489 
490 //! Setting top level item as ROOT item (case parent and children).
491 
492 TEST_F(DefaultViewModelTest, setCompoundAsRootItem)
493 {
494  SessionModel model;
495  DefaultViewModel viewModel(&model);
496 
497  auto item = model.insertItem<CompoundItem>();
498  item->addProperty("thickness", 42.0);
499  item->addProperty<VectorItem>("position");
500  item->addProperty("radius", 43.0);
501 
502  viewModel.setRootSessionItem(item);
503 
504  EXPECT_EQ(viewModel.rowCount(), 3);
505  EXPECT_EQ(viewModel.columnCount(), 2);
506 
507  // checking vector item
508  auto index_of_vector_item = viewModel.index(1, 0);
509  EXPECT_EQ(viewModel.rowCount(index_of_vector_item), 3);
510  EXPECT_EQ(viewModel.columnCount(index_of_vector_item), 2);
511 }
512 
513 //! Setting vector item as ROOT item.
514 
515 TEST_F(DefaultViewModelTest, setVectorItemAsRoot)
516 {
517  SessionModel model;
518  auto vectorItem = model.insertItem<VectorItem>();
519 
520  DefaultViewModel viewModel(&model);
521  viewModel.setRootSessionItem(vectorItem);
522 
523  EXPECT_EQ(viewModel.rowCount(), 3);
524  EXPECT_EQ(viewModel.columnCount(), 2);
525 }
526 
527 //! On model destroyed.
528 
530 {
531  auto model = std::make_unique<SessionModel>();
532  model->insertItem<SessionItem>();
533  model->insertItem<SessionItem>();
534  model->insertItem<SessionItem>();
535 
536  DefaultViewModel viewModel(model.get());
537 
538  QSignalSpy spyAboutReset(&viewModel, &DefaultViewModel::modelAboutToBeReset);
539  QSignalSpy spyReset(&viewModel, &DefaultViewModel::modelReset);
540 
541  model->clear();
542  EXPECT_EQ(viewModel.rowCount(), 0);
543  EXPECT_EQ(viewModel.columnCount(), 0);
544 
545  EXPECT_EQ(spyAboutReset.count(), 1);
546  EXPECT_EQ(spyReset.count(), 1);
547 }
548 
549 //! On model destroyed.
550 
551 TEST_F(DefaultViewModelTest, onModelDestroyed)
552 {
553  auto model = std::make_unique<SessionModel>();
554  model->insertItem<SessionItem>();
555 
556  DefaultViewModel viewModel(model.get());
557  EXPECT_EQ(viewModel.rowCount(), 1);
558  EXPECT_EQ(viewModel.columnCount(), 2);
559 
560  model.reset();
561  EXPECT_EQ(viewModel.rowCount(), 0);
562  EXPECT_EQ(viewModel.columnCount(), 0);
563 }
564 
566 {
567  SessionModel model;
568  auto vectorItem = model.insertItem<VectorItem>();
569 
570  // constructing viewModel from sample model
571  DefaultViewModel viewModel(&model);
572 
573  // root item should have one child, item looking at our vectorItem
574  EXPECT_EQ(viewModel.rowCount(), 1);
575  EXPECT_EQ(viewModel.columnCount(), 2);
576 
577  // accessing to viewItem representing layerItem
578  QModelIndex vectorIndex = viewModel.index(0, 0);
579 
580  // it has three rows and two columns, corresponding to our P_X, P_Y, P_Z
581  EXPECT_EQ(viewModel.rowCount(vectorIndex), 3);
582  EXPECT_EQ(viewModel.columnCount(vectorIndex), 2);
583 
584  // ViewLabelItem and ViewDataItem correspondint to P_X
585  auto pxLabel =
586  dynamic_cast<ViewLabelItem*>(viewModel.itemFromIndex(viewModel.index(0, 0, vectorIndex)));
587  auto pxData =
588  dynamic_cast<ViewDataItem*>(viewModel.itemFromIndex(viewModel.index(0, 1, vectorIndex)));
589  EXPECT_EQ(pxLabel->item(), vectorItem->getItem(VectorItem::P_X));
590  EXPECT_EQ(pxData->item(), vectorItem->getItem(VectorItem::P_X));
591 
592  // ViewLabelItem and ViewDataItem correspondint to P_Y
593  pxLabel =
594  dynamic_cast<ViewLabelItem*>(viewModel.itemFromIndex(viewModel.index(1, 0, vectorIndex)));
595  pxData =
596  dynamic_cast<ViewDataItem*>(viewModel.itemFromIndex(viewModel.index(1, 1, vectorIndex)));
597  EXPECT_EQ(pxLabel->item(), vectorItem->getItem(VectorItem::P_Y));
598  EXPECT_EQ(pxData->item(), vectorItem->getItem(VectorItem::P_Y));
599 
600  // ViewLabelItem and ViewDataItem correspondint to P_Z
601  pxLabel =
602  dynamic_cast<ViewLabelItem*>(viewModel.itemFromIndex(viewModel.index(2, 0, vectorIndex)));
603  pxData =
604  dynamic_cast<ViewDataItem*>(viewModel.itemFromIndex(viewModel.index(2, 1, vectorIndex)));
605  EXPECT_EQ(pxLabel->item(), vectorItem->getItem(VectorItem::P_Z));
606  EXPECT_EQ(pxData->item(), vectorItem->getItem(VectorItem::P_Z));
607 }
608 
609 TEST_F(DefaultViewModelTest, horizontalLabels)
610 {
611  SessionModel model;
612  model.insertItem<VectorItem>();
613 
614  // constructing viewModel from sample model
615  DefaultViewModel viewModel(&model);
616 
617  EXPECT_EQ(viewModel.headerData(0, Qt::Horizontal, Qt::DisplayRole).toString(), QString("Name"));
618  EXPECT_EQ(viewModel.headerData(1, Qt::Horizontal, Qt::DisplayRole).toString(),
619  QString("Value"));
620 }
621 
622 //! Testing ViewModel signals while loading data with the help of json loader.
623 
624 TEST_F(DefaultViewModelTest, jsonConverterLoadModel)
625 {
627  QJsonObject object;
628 
629  // preparing jsob object
630  {
631  SessionModel model("TestModel");
632  model.insertItem<PropertyItem>();
634  // writing model to json
635  object = converter.to_json(model);
636  }
637 
638  // loading model
639  SessionModel model("TestModel");
640  DefaultViewModel viewmodel(&model);
641  EXPECT_EQ(viewmodel.rowCount(), 0);
642  EXPECT_EQ(viewmodel.columnCount(), 0);
643 
644  QSignalSpy spyInsert(&viewmodel, &DefaultViewModel::rowsInserted);
645  QSignalSpy spyRemove(&viewmodel, &DefaultViewModel::rowsRemoved);
646  QSignalSpy spyAboutReset(&viewmodel, &DefaultViewModel::modelAboutToBeReset);
647  QSignalSpy spyReset(&viewmodel, &DefaultViewModel::modelReset);
648 
649  converter.from_json(object, model);
650 
651  EXPECT_EQ(spyInsert.count(), 1); // FIXME shouldn't it be '0'?
652  EXPECT_EQ(spyRemove.count(), 0);
653  EXPECT_EQ(spyAboutReset.count(), 1);
654  EXPECT_EQ(spyReset.count(), 1);
655 
656  EXPECT_EQ(viewmodel.rowCount(), 1);
657  EXPECT_EQ(viewmodel.columnCount(), 2);
658 }
659 
660 //! Testing ViewModel signals while loading data with the help of json document.
661 //! Model is empty.
662 
663 TEST_F(DefaultViewModelTest, jsonDocumentLoadEmptyModel)
664 {
665  auto fileName = TestUtils::TestFileName(testDir(), "jsonDocumentLoadEmptyModel.json");
666 
667  // preparing jsob object
668  {
669  SessionModel model("TestModel");
670  JsonDocument document({&model});
671  document.save(fileName);
672  }
673 
674  // loading model
675  SessionModel model("TestModel");
676  DefaultViewModel viewmodel(&model);
677  EXPECT_EQ(viewmodel.rowCount(), 0);
678  EXPECT_EQ(viewmodel.columnCount(), 0);
679 
680  JsonDocument document({&model});
681 
682  QSignalSpy spyInsert(&viewmodel, &DefaultViewModel::rowsInserted);
683  QSignalSpy spyRemove(&viewmodel, &DefaultViewModel::rowsRemoved);
684  QSignalSpy spyAboutReset(&viewmodel, &DefaultViewModel::modelAboutToBeReset);
685  QSignalSpy spyReset(&viewmodel, &DefaultViewModel::modelReset);
686 
687  document.load(fileName);
688 
689  EXPECT_EQ(spyInsert.count(), 0);
690  EXPECT_EQ(spyRemove.count(), 0);
691  EXPECT_EQ(spyAboutReset.count(), 1);
692  EXPECT_EQ(spyReset.count(), 1);
693 
694  EXPECT_EQ(viewmodel.rowCount(), 0);
695  EXPECT_EQ(viewmodel.columnCount(), 0);
696 }
697 
698 //! Testing ViewModel signals while loading data with the help of json document.
699 //! Model is empty.
700 
701 TEST_F(DefaultViewModelTest, jsonDocumentLoadModel)
702 {
703  auto fileName = TestUtils::TestFileName(testDir(), "jsonDocumentLoadModel.json");
704 
705  // preparing jsob object
706  {
707  SessionModel model("TestModel");
708  JsonDocument document({&model});
709  model.insertItem<PropertyItem>();
710  document.save(fileName);
711  }
712 
713  // loading model
714  SessionModel model("TestModel");
715  DefaultViewModel viewmodel(&model);
716  EXPECT_EQ(viewmodel.rowCount(), 0);
717  EXPECT_EQ(viewmodel.columnCount(), 0);
718 
719  JsonDocument document({&model});
720 
721  QSignalSpy spyInsert(&viewmodel, &DefaultViewModel::rowsInserted);
722  QSignalSpy spyRemove(&viewmodel, &DefaultViewModel::rowsRemoved);
723  QSignalSpy spyAboutReset(&viewmodel, &DefaultViewModel::modelAboutToBeReset);
724  QSignalSpy spyReset(&viewmodel, &DefaultViewModel::modelReset);
725 
726  document.load(fileName);
727 
728  EXPECT_EQ(spyInsert.count(), 1);
729  EXPECT_EQ(spyRemove.count(), 0);
730  EXPECT_EQ(spyAboutReset.count(), 1);
731  EXPECT_EQ(spyReset.count(), 1);
732 
733  EXPECT_EQ(viewmodel.rowCount(), 1);
734  EXPECT_EQ(viewmodel.columnCount(), 2);
735 }
736 
737 //! Testing view model after restoring from json document.
738 
739 TEST_F(DefaultViewModelTest, vectorItemInJsonDocument)
740 {
741  auto fileName = TestUtils::TestFileName(testDir(), "vectorItemInJsonDocument.json");
742 
743  SessionModel model;
744  model.insertItem<VectorItem>();
745 
746  // constructing viewModel from sample model
747  DefaultViewModel viewmodel(&model);
748 
749  // root item should have one child, item looking at our vectorItem
750  EXPECT_EQ(viewmodel.rowCount(), 1);
751  EXPECT_EQ(viewmodel.columnCount(), 2);
752 
753  JsonDocument document({&model});
754  document.save(fileName);
755 
756  // cleaning original model
757  model.clear();
758 
759  QSignalSpy spyInsert(&viewmodel, &DefaultViewModel::rowsInserted);
760  QSignalSpy spyRemove(&viewmodel, &DefaultViewModel::rowsRemoved);
761  QSignalSpy spyAboutReset(&viewmodel, &DefaultViewModel::modelAboutToBeReset);
762  QSignalSpy spyReset(&viewmodel, &DefaultViewModel::modelReset);
763 
764  document.load(fileName);
765 
766  EXPECT_EQ(spyInsert.count(), 4);
767  EXPECT_EQ(spyRemove.count(), 0);
768  EXPECT_EQ(spyAboutReset.count(), 1);
769  EXPECT_EQ(spyReset.count(), 1);
770 
771  EXPECT_EQ(viewmodel.rowCount(), 1);
772  EXPECT_EQ(viewmodel.columnCount(), 2);
773 }
774 
775 //! Testing view model after restoring from json document.
776 //! VectorItem is made root item. Test demonstrates that controller is capable
777 //! to restore old rootSessionItem on onModelReset signal
778 
779 TEST_F(DefaultViewModelTest, vectorItemAsRootInJsonDocument)
780 {
781  auto fileName = TestUtils::TestFileName(testDir(), "vectorItemAsRootInJsonDocument.json");
782 
783  SessionModel model;
784  auto vectorItem = model.insertItem<VectorItem>();
785 
786  // constructing viewModel from sample model
787  DefaultViewModel viewmodel(&model);
788  viewmodel.setRootSessionItem(vectorItem);
789 
790  // root item should have one child, item looking at our vectorItem
791  EXPECT_EQ(viewmodel.rowCount(), 3);
792  EXPECT_EQ(viewmodel.columnCount(), 2);
793  EXPECT_EQ(viewmodel.rootSessionItem(), vectorItem);
794 
795  JsonDocument document({&model});
796  document.save(fileName);
797 
798  // model.clear(); // if we uncomment this, information about rootSessionItem will be lost
799 
800  QSignalSpy spyInsert(&viewmodel, &DefaultViewModel::rowsInserted);
801  QSignalSpy spyRemove(&viewmodel, &DefaultViewModel::rowsRemoved);
802  QSignalSpy spyAboutReset(&viewmodel, &DefaultViewModel::modelAboutToBeReset);
803  QSignalSpy spyReset(&viewmodel, &DefaultViewModel::modelReset);
804 
805  document.load(fileName);
806 
807  EXPECT_EQ(spyInsert.count(), 3);
808  EXPECT_EQ(spyRemove.count(), 0);
809  EXPECT_EQ(spyAboutReset.count(), 1);
810  EXPECT_EQ(spyReset.count(), 1);
811 
812  EXPECT_EQ(viewmodel.rootSessionItem(), model.rootItem()->children().at(0)); // vectorItem
813 
814  EXPECT_EQ(viewmodel.rowCount(), 3);
815  EXPECT_EQ(viewmodel.columnCount(), 2);
816 }
817 
818 //! Real life bug. One container with Data1DItem's, one ViewportItem with single graph.
819 //! DefaultViewModel is looking on ViewPortItem. Graph is deleted first.
820 
821 TEST_F(DefaultViewModelTest, deleteGraphVromViewport)
822 {
823  SessionModel model;
824 
825  // creating data container and single Data1DItem in it
826  auto data_container = model.insertItem<ContainerItem>();
827  auto data_item = model.insertItem<Data1DItem>(data_container);
828  data_item->setAxis<FixedBinAxisItem>(3, 0.0, 3.0);
829  data_item->setValues(std::vector<double>({1.0, 2.0, 3.0}));
830 
831  // creating Viewport with single graph
832  auto viewport_item = model.insertItem<GraphViewportItem>();
833  auto graph_item = model.insertItem<GraphItem>(viewport_item);
834  graph_item->setDataItem(data_item);
835 
836  DefaultViewModel viewmodel(&model);
837  viewmodel.setRootSessionItem(viewport_item);
838 
839  // validating that we see graph at propeer index
840  EXPECT_EQ(viewmodel.rowCount(), 3); // X, Y and Graph
841  QModelIndex graph_index = viewmodel.index(2, 0);
842  auto graphLabel = dynamic_cast<ViewLabelItem*>(viewmodel.itemFromIndex(graph_index));
843  ASSERT_TRUE(graphLabel != nullptr);
844  EXPECT_EQ(graphLabel->item(), graph_item);
845 
846  // removing graph item
847  model.removeItem(graph_item->parent(), graph_item->tagRow());
848  EXPECT_EQ(viewmodel.rowCount(), 2); // X, Y
849 
850  graph_item = model.insertItem<GraphItem>(viewport_item);
851  EXPECT_EQ(viewmodel.rowCount(), 3); // X, Y
852 }
Defines class CLASS?
Convenience class which creates a directory on disk for test content.
Complex item holding mixed SessionItem types (single properties and other CompountItems).
Definition: compounditem.h:28
T * addProperty(const std::string &name)
Adds property item of given type.
Definition: compounditem.h:43
Simple container to store any type of children.
Definition: containeritem.h:25
Represents one-dimensional data (axis and values).
Definition: data1ditem.h:30
View model to show content of SessionModel in Qt widgets: two column tree with label/data.
Item to represent fixed bin axis.
Definition: axisitems.h:75
One-dimensional graph representation of Data1DItem.
Definition: graphitem.h:29
2D viewport specialized for showing multiple GraphItem's.
Saves and restores list of SessionModel's to/from disk using json format.
Definition: jsondocument.h:29
void save(const std::string &file_name) const override
Saves models on disk.
Converter of SessionModel to/from json object with posibility to select one of convertion modes.
void from_json(const QJsonObject &json, SessionModel &model) const override
Reads json object and build the model.
Item to carry concrete editable entity (e.g.
Definition: propertyitem.h:27
The main object representing an editable/displayable/serializable entity.
Definition: sessionitem.h:38
bool setData(const T &value, int role=ItemDataRole::DATA, bool direct=false)
Sets data for a given role.
Definition: sessionitem.h:141
SessionItem * parent() const
Returns parent item. Will return nullptr if item doesn't have a parent.
std::vector< SessionItem * > children() const
Returns vector of children formed from all chidlren from all tags.
bool insertItem(SessionItem *item, const TagRow &tagrow)
Insert item into given tag under the given row.
TagRow tagRow() const
Returns TagRow of this item under which it is accessible through its parent.
Main class to hold hierarchy of SessionItem objects.
Definition: sessionmodel.h:37
SessionItem * rootItem() const
Returns root item of the model.
T * insertItem(SessionItem *parent=nullptr, const TagRow &tagrow={})
Inserts item into given parent under given tagrow.
Definition: sessionmodel.h:104
void clear(std::function< void(SessionItem *)> callback={})
Removes all items from the model.
void removeItem(SessionItem *parent, const TagRow &tagrow)
Removes given row from parent.
Vector item with three x,y,z property items.
Definition: vectoritem.h:24
static const std::string P_X
Definition: vectoritem.h:26
static const std::string P_Z
Definition: vectoritem.h:28
static const std::string P_Y
Definition: vectoritem.h:27
Represents data role of SessionItem in any cell of Qt's trees and tables.
Represents display name of SessionItem in any cell of Qt's trees and tables.
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
int rowCount(const QModelIndex &parent=QModelIndex()) const override
Qt::ItemFlags flags(const QModelIndex &index) const override
Returns the item flags for the given index.
QModelIndex indexFromItem(const ViewItem *item) const
Returns the QModelIndex associated with the given item.
int columnCount(const QModelIndex &parent=QModelIndex()) const override
ViewItem * itemFromIndex(const QModelIndex &index) const
Returns a pointer to the RefViewItem associated with the given index.
QModelIndexList indexOfSessionItem(const SessionItem *item) const
Returns list of model indices representing given SessionItem.
Definition: viewmodel.cpp:71
void setRootSessionItem(SessionItem *item)
Definition: viewmodel.cpp:52
SessionItem * sessionItemFromIndex(const QModelIndex &index) const
Definition: viewmodel.cpp:59
std::vector< ViewItem * > findViews(const ModelView::SessionItem *item) const
Returns vector of all ViewItem's representing given SessionItem.
Definition: viewmodel.cpp:81
Defines class CLASS?
Defines class CLASS?
Defines class CLASS?
Defines class CLASS?
TEST_F(DefaultViewModelTest, initialState)
Defines class CLASS?
Defines class CLASS?
Defines class CLASS?
Defines class CLASS?
Defines class CLASS?
Defines class CLASS?
Defines class CLASS?
MVVM_VIEWMODEL_EXPORT QVariant TextColorRole(const SessionItem &item)
Returns text color for given item.
materialitems.h Collection of materials to populate MaterialModel.
@ project
selective copying for saving/loading the project (tags and data created by item, updated from JSON)
QVariant ToolTipRole(const SessionItem &item, int ncol=0)
Returns tooltip for given item.
QVariant ForegroundRole(const SessionItem &item)
Returns text color for given item.
std::string TestFileName(const std::string &test_sub_dir, const std::string &file_name)
Returns full path to the file in test directory.
Definition: test_utils.cpp:52
Defines class CLASS?
Defines class CLASS?
Defines class CLASS?
Defines class CLASS?
Defines class CLASS?
Defines class CLASS?