BornAgain  1.19.0
Simulate and fit neutron and x-ray scattering at grazing incidence
viewmodelcontroller.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/viewmodelcontroller.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 "google_test.h"
24 #include "test_utils.h"
25 #include <QSignalSpy>
26 
27 using namespace ModelView;
28 
29 //! Tests of ViewModelController class.
30 
31 class ViewModelControllerTest : public ::testing::Test {
32 public:
34 
35  auto create_controller(SessionModel* session_model, ViewModelBase* view_model)
36  {
37  auto result = std::make_unique<ViewModelController>(session_model, view_model);
38  result->setRowStrategy(std::make_unique<LabelDataRowStrategy>());
39  result->setChildrenStrategy(std::make_unique<AllChildrenStrategy>());
40  result->setRootSessionItem(session_model->rootItem());
41  return result;
42  }
43 };
44 
46 
47 //! Initial state of the controller. It is in working state only after setRootItem.
48 
50 {
51  SessionModel session_model;
52  ViewModelBase view_model;
53  auto controller = std::make_unique<ViewModelController>(&session_model, &view_model);
54  EXPECT_EQ(controller->sessionModel(), &session_model);
55  EXPECT_EQ(controller->rootSessionItem(), nullptr);
56 }
57 
58 //! Initial state of the controller. Empty SessionModel, empty ViewModel.
59 
60 TEST_F(ViewModelControllerTest, create_controller)
61 {
62  SessionModel session_model;
63  ViewModelBase view_model;
64  auto controller = create_controller(&session_model, &view_model);
65 
66  EXPECT_EQ(controller->sessionModel(), &session_model);
67  EXPECT_EQ(controller->rootSessionItem(), session_model.rootItem());
68  EXPECT_EQ(view_model.columnCount(), 0);
69  EXPECT_EQ(view_model.rowCount(), 0);
70 }
71 
72 //! Single property item in a model.
73 
74 TEST_F(ViewModelControllerTest, fromPropertyItem)
75 {
76  SessionModel session_model;
77  auto propertyItem = session_model.insertItem<PropertyItem>();
78  propertyItem->setData(42.0);
79 
80  ViewModelBase view_model;
81  auto controller = create_controller(&session_model, &view_model);
82 
83  EXPECT_EQ(view_model.rowCount(), 1);
84  EXPECT_EQ(view_model.columnCount(), 2);
85 
86  // accessing first child under the root item
87  QModelIndex labelIndex = view_model.index(0, 0);
88  QModelIndex dataIndex = view_model.index(0, 1);
89 
90  // it should be ViewLabelItem and ViewDataItem looking at our PropertyItem item
91  EXPECT_EQ(view_model.itemFromIndex(labelIndex)->item_role(), ItemDataRole::DISPLAY);
92  EXPECT_EQ(view_model.itemFromIndex(labelIndex)->item(), propertyItem);
93  EXPECT_EQ(view_model.itemFromIndex(dataIndex)->item_role(), ItemDataRole::DATA);
94  EXPECT_EQ(view_model.itemFromIndex(dataIndex)->item(), propertyItem);
95 }
96 
97 //! VectorItem in a model.
98 
100 {
101  SessionModel session_model;
102  auto vectorItem = session_model.insertItem<VectorItem>();
103 
104  ViewModelBase view_model;
105  auto controller = create_controller(&session_model, &view_model);
106 
107  EXPECT_EQ(view_model.rowCount(), 1);
108  EXPECT_EQ(view_model.columnCount(), 2);
109 
110  // accessing first child under the root item
111  QModelIndex vectorLabelIndex = view_model.index(0, 0);
112  QModelIndex vectorDataIndex = view_model.index(0, 1);
113 
114  // it should be ViewLabelItem and ViewDataItem looking at our VectorItem item
115  EXPECT_EQ(view_model.itemFromIndex(vectorLabelIndex)->item_role(), ItemDataRole::DISPLAY);
116  EXPECT_EQ(view_model.itemFromIndex(vectorLabelIndex)->item(), vectorItem);
117  EXPECT_EQ(view_model.itemFromIndex(vectorDataIndex)->item_role(), ItemDataRole::DATA);
118  EXPECT_EQ(view_model.itemFromIndex(vectorDataIndex)->item(), vectorItem);
119 
120  // checking X, Y, Z
121  std::vector<SessionItem*> children = vectorItem->children();
122  for (int row = 0; row < 3; ++row) { // x, y, z
123  QModelIndex x_labelIndex = view_model.index(row, 0, vectorLabelIndex);
124  QModelIndex x_dataIndex = view_model.index(row, 1, vectorLabelIndex);
125  EXPECT_EQ(view_model.itemFromIndex(x_labelIndex)->item_role(), ItemDataRole::DISPLAY);
126  EXPECT_EQ(view_model.itemFromIndex(x_labelIndex)->item(),
127  children[static_cast<size_t>(row)]);
128  EXPECT_EQ(view_model.itemFromIndex(x_dataIndex)->item_role(), ItemDataRole::DATA);
129  EXPECT_EQ(view_model.itemFromIndex(x_dataIndex)->item(),
130  children[static_cast<size_t>(row)]);
131  }
132 }
133 
134 //! Single property item in a model, inserted after controller was setup.
135 
136 TEST_F(ViewModelControllerTest, initThenInsertProperty)
137 {
138  SessionModel session_model;
139 
140  ViewModelBase view_model;
141  QSignalSpy spyInsert(&view_model, &ViewModelBase::rowsInserted);
142  QSignalSpy spyRemove(&view_model, &ViewModelBase::rowsRemoved);
143 
144  auto controller = create_controller(&session_model, &view_model);
145  auto propertyItem = session_model.insertItem<PropertyItem>();
146  propertyItem->setData(42.0);
147 
148  // checking signaling
149  EXPECT_EQ(spyInsert.count(), 1);
150  EXPECT_EQ(spyRemove.count(), 0);
151  QList<QVariant> arguments = spyInsert.takeFirst();
152  EXPECT_EQ(arguments.size(), 3); // QModelIndex &parent, int first, int last
153  EXPECT_EQ(arguments.at(0).value<QModelIndex>(), QModelIndex());
154  EXPECT_EQ(arguments.at(1).value<int>(), 0);
155  EXPECT_EQ(arguments.at(2).value<int>(), 0);
156 
157  // checking model layout
158  EXPECT_EQ(view_model.rowCount(), 1);
159  EXPECT_EQ(view_model.columnCount(), 2);
160 
161  // accessing first child under the root item
162  QModelIndex labelIndex = view_model.index(0, 0);
163  QModelIndex dataIndex = view_model.index(0, 1);
164 
165  // it should be ViewLabelItem and ViewDataItem looking at our PropertyItem item
166  EXPECT_EQ(view_model.itemFromIndex(labelIndex)->item_role(), ItemDataRole::DISPLAY);
167  EXPECT_EQ(view_model.itemFromIndex(labelIndex)->item(), propertyItem);
168 
169  // Our PropertyItem got it's value after ViewModel was initialized, however,
170  // underlying ViewDataItem should see updated values
171  EXPECT_EQ(view_model.itemFromIndex(dataIndex)->item_role(), ItemDataRole::DATA);
172  EXPECT_EQ(view_model.itemFromIndex(dataIndex)->item(), propertyItem);
173 }
174 
175 //! Insert three property items in a model, inserted after controller was setup.
176 
177 TEST_F(ViewModelControllerTest, initThenInsertProperties)
178 {
179  SessionModel session_model;
180 
181  ViewModelBase view_model;
182  QSignalSpy spyInsert(&view_model, &ViewModelBase::rowsInserted);
183  QSignalSpy spyRemove(&view_model, &ViewModelBase::rowsRemoved);
184 
185  auto controller = create_controller(&session_model, &view_model);
186  auto item0 = session_model.insertItem<PropertyItem>();
187  auto item1 = session_model.insertItem<PropertyItem>();
188  auto item2 = session_model.insertItem<PropertyItem>();
189 
190  // checking signaling
191  EXPECT_EQ(spyInsert.count(), 3);
192 
193  // checking model layout
194  EXPECT_EQ(view_model.rowCount(), 3);
195  EXPECT_EQ(view_model.columnCount(), 2);
196 
197  EXPECT_EQ(view_model.itemFromIndex(view_model.index(0, 0))->item(), item0);
198  EXPECT_EQ(view_model.itemFromIndex(view_model.index(1, 0))->item(), item1);
199  EXPECT_EQ(view_model.itemFromIndex(view_model.index(2, 0))->item(), item2);
200 }
201 
202 //! Inserting property items in reversed order.
203 
205 {
206  SessionModel session_model;
207 
208  ViewModelBase view_model;
209  QSignalSpy spyInsert(&view_model, &ViewModelBase::rowsInserted);
210  QSignalSpy spyRemove(&view_model, &ViewModelBase::rowsRemoved);
211 
212  auto controller = create_controller(&session_model, &view_model);
213  auto item0 = session_model.insertItem<PropertyItem>();
214  // inserting in front
215  auto item1 = session_model.insertItem<PropertyItem>(session_model.rootItem(), {"", 0});
216 
217  // checking signaling
218  EXPECT_EQ(spyInsert.count(), 2);
219 
220  // checking model layout
221  EXPECT_EQ(view_model.rowCount(), 2);
222  EXPECT_EQ(view_model.columnCount(), 2);
223 
224  EXPECT_EQ(view_model.itemFromIndex(view_model.index(0, 0))->item(), item1);
225  EXPECT_EQ(view_model.itemFromIndex(view_model.index(1, 0))->item(), item0);
226 }
227 
228 //! Insert two property items in a model, inserted after controller was setup.
229 
230 TEST_F(ViewModelControllerTest, initThenInsertVector)
231 {
232  SessionModel session_model;
233 
234  ViewModelBase view_model;
235  QSignalSpy spyInsert(&view_model, &ViewModelBase::rowsInserted);
236  QSignalSpy spyRemove(&view_model, &ViewModelBase::rowsRemoved);
237 
238  auto controller = create_controller(&session_model, &view_model);
239  session_model.insertItem<VectorItem>();
240  session_model.insertItem<VectorItem>();
241 
242  // checking signaling
243  EXPECT_EQ(spyInsert.count(), 8); // two vector items and 2*(x,y,z)
244 
245  // checking model layout
246  EXPECT_EQ(view_model.rowCount(), 2);
247  EXPECT_EQ(view_model.columnCount(), 2);
248 }
249 
250 //! Insert child to parent
251 
252 TEST_F(ViewModelControllerTest, insertChildToParent)
253 {
254  SessionModel session_model;
255 
256  ViewModelBase view_model;
257  QSignalSpy spyInsert(&view_model, &ViewModelBase::rowsInserted);
258  QSignalSpy spyRemove(&view_model, &ViewModelBase::rowsRemoved);
259 
260  auto controller = create_controller(&session_model, &view_model);
261 
262  auto parent = session_model.insertItem<CompoundItem>();
263  parent->registerTag(TagInfo::universalTag("children"), /*set_as_default*/ true);
264  session_model.insertItem<SessionItem>(parent);
265  session_model.insertItem<SessionItem>(parent);
266 
267  // checking signaling
268  EXPECT_EQ(spyInsert.count(), 3);
269 
270  // checking model layout: parent and two children
271  EXPECT_EQ(view_model.rowCount(), 1);
272  EXPECT_EQ(view_model.columnCount(), 2);
273  EXPECT_EQ(view_model.rowCount(view_model.index(0, 0)), 2);
274  EXPECT_EQ(view_model.columnCount(view_model.index(0, 0)), 2);
275 }
276 
277 //! Removing single top level item.
278 
279 TEST_F(ViewModelControllerTest, removeSingleTopItem)
280 {
281  // constructing the model with single item
282  SessionModel session_model;
283  session_model.insertItem<SessionItem>();
284 
285  // constructing viewmodel and its controller
286  ViewModelBase view_model;
287  auto controller = create_controller(&session_model, &view_model);
288 
289  // root item should have one child
290  EXPECT_EQ(view_model.rowCount(), 1);
291  EXPECT_EQ(view_model.columnCount(), 2);
292 
293  QSignalSpy spyInsert(&view_model, &ViewModelBase::rowsInserted);
294  QSignalSpy spyRemove(&view_model, &ViewModelBase::rowsRemoved);
295 
296  // removing child
297  session_model.removeItem(session_model.rootItem(), {"", 0});
298  ASSERT_EQ(spyInsert.count(), 0);
299  ASSERT_EQ(spyRemove.count(), 1);
300  EXPECT_EQ(view_model.rowCount(), 0);
301  EXPECT_EQ(view_model.columnCount(), 0);
302 
303  QList<QVariant> arguments = spyRemove.takeFirst();
304  ASSERT_EQ(arguments.size(), 3); // QModelIndex &parent, int first, int last
305  EXPECT_EQ(arguments.at(0).value<QModelIndex>(), QModelIndex());
306  EXPECT_EQ(arguments.at(1).value<int>(), 0);
307  EXPECT_EQ(arguments.at(2).value<int>(), 0);
308 }
309 
310 //! Remove one of two top level items.
311 
312 TEST_F(ViewModelControllerTest, removeOneOfTopItems)
313 {
314  // constructing model with two items
315  SessionModel session_model;
316  session_model.insertItem<SessionItem>();
317  session_model.insertItem<SessionItem>();
318 
319  // constructing viewmodel and its controller
320  ViewModelBase view_model;
321  auto controller = create_controller(&session_model, &view_model);
322 
323  // root item should have one child
324  EXPECT_EQ(view_model.rowCount(), 2);
325  EXPECT_EQ(view_model.columnCount(), 2);
326 
327  QSignalSpy spyRemove(&view_model, &ViewModelBase::rowsRemoved);
328  QSignalSpy spyInsert(&view_model, &ViewModelBase::rowsInserted);
329 
330  // removing child
331  session_model.removeItem(session_model.rootItem(), {"", 0});
332 
333  // no insert was called
334  EXPECT_EQ(spyInsert.count(), 0);
335 
336  // removal was called once
337  EXPECT_EQ(spyRemove.count(), 1);
338  EXPECT_EQ(view_model.rowCount(), 1);
339  EXPECT_EQ(view_model.columnCount(), 2);
340 
341  QList<QVariant> arguments = spyRemove.takeFirst();
342  EXPECT_EQ(arguments.size(), 3); // QModelIndex &parent, int first, int last
343  EXPECT_EQ(arguments.at(0).value<QModelIndex>(), QModelIndex());
344  EXPECT_EQ(arguments.at(1).value<int>(), 0);
345  EXPECT_EQ(arguments.at(2).value<int>(), 0);
346 }
347 
348 //! Setting top level item as ROOT item
349 
351 {
352  SessionModel session_model;
353 
354  // constructing viewmodel and its controller
355  ViewModelBase view_model;
356  auto controller = create_controller(&session_model, &view_model);
357 
358  auto item = session_model.insertItem<PropertyItem>();
359 
360  controller->setRootSessionItem(item);
361 
362  // new root item doesn't have children
363  EXPECT_EQ(view_model.rowCount(), 0);
364  EXPECT_EQ(view_model.columnCount(), 0);
365 }
366 
367 //! Setting top level item as ROOT item (case parent and children).
368 
369 TEST_F(ViewModelControllerTest, setCompoundAsRootItem)
370 {
371  SessionModel session_model;
372 
373  // constructing viewmodel and its controller
374  ViewModelBase view_model;
375  auto controller = create_controller(&session_model, &view_model);
376 
377  auto item = session_model.insertItem<CompoundItem>();
378  item->addProperty("thickness", 42.0);
379  item->addProperty<VectorItem>("position");
380  item->addProperty("radius", 43.0);
381 
382  controller->setRootSessionItem(item);
383 
384  EXPECT_EQ(view_model.rowCount(), 3);
385  EXPECT_EQ(view_model.columnCount(), 2);
386 
387  // checking vector item
388  auto index_of_vector_item = view_model.index(1, 0);
389  EXPECT_EQ(view_model.rowCount(index_of_vector_item), 3);
390  EXPECT_EQ(view_model.columnCount(index_of_vector_item), 2);
391 }
392 
393 //! On model reset.
394 
396 {
397  SessionModel session_model;
398  session_model.insertItem<SessionItem>();
399  session_model.insertItem<SessionItem>();
400  session_model.insertItem<SessionItem>();
401 
402  // constructing viewmodel and its controller
403  ViewModelBase view_model;
404  auto controller = create_controller(&session_model, &view_model);
405  EXPECT_EQ(controller->rootSessionItem(), session_model.rootItem());
406 
407  QSignalSpy spyReset(&view_model, &ViewModelBase::modelReset);
408 
409  session_model.clear();
410 
411  EXPECT_EQ(spyReset.count(), 1);
412  EXPECT_EQ(view_model.rowCount(), 0);
413  EXPECT_EQ(view_model.columnCount(), 0);
414  EXPECT_EQ(controller->rootSessionItem(), session_model.rootItem());
415 }
416 
417 //! Real life scenario: initially empty SessionModel, apply ::clean, and then start to insert item.
418 
419 TEST_F(ViewModelControllerTest, onEmptyModelResetAndContinue)
420 {
421  SessionModel session_model;
422 
423  // constructing viewmodel and its controller
424  ViewModelBase view_model;
425  auto controller = create_controller(&session_model, &view_model);
426 
427  QSignalSpy spyReset(&view_model, &ViewModelBase::modelReset);
428  session_model.clear();
429 
430  EXPECT_EQ(spyReset.count(), 1);
431 
432  // inserting new item
433  QSignalSpy spyInsert(&view_model, &ViewModelBase::rowsInserted);
434  session_model.insertItem<SessionItem>();
435 
436  EXPECT_EQ(spyInsert.count(), 1);
437 }
438 
439 //! On model destroyed.
440 
441 TEST_F(ViewModelControllerTest, onModelDestroyed)
442 {
443  auto session_model = std::make_unique<SessionModel>();
444  session_model->insertItem<SessionItem>();
445 
446  // constructing viewmodel and its controller
447  ViewModelBase view_model;
448  auto controller = create_controller(session_model.get(), &view_model);
449  EXPECT_EQ(view_model.rowCount(), 1);
450  EXPECT_EQ(view_model.columnCount(), 2);
451 
452  session_model.reset();
453  EXPECT_EQ(view_model.rowCount(), 0);
454  EXPECT_EQ(view_model.columnCount(), 0);
455  EXPECT_EQ(view_model.rootItem()->item(), nullptr);
456 }
457 
459 {
460  SessionModel session_model;
461  ViewModelBase view_model;
462  auto controller = create_controller(&session_model, &view_model);
463 
464  // view of root item
465  auto views = controller->findViews(session_model.rootItem());
466  ASSERT_EQ(views.size(), 1);
467  EXPECT_EQ(views.at(0), view_model.rootItem());
468 
469  // views of VectorItem
470  auto item = session_model.insertItem<VectorItem>();
471  views = controller->findViews(item);
472  ASSERT_EQ(views.size(), 2);
473 
474  // setting as root item
475  controller->setRootSessionItem(item);
476  views = controller->findViews(item);
477  ASSERT_EQ(views.size(), 1);
478  EXPECT_EQ(views.at(0), view_model.rootItem());
479 }
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
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
void registerTag(const TagInfo &tagInfo, bool set_as_default=false)
Registers tag to hold items under given name.
bool setData(const T &value, int role=ItemDataRole::DATA, bool direct=false)
Sets data for a given role.
Definition: sessionitem.h:141
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.
static TagInfo universalTag(std::string name, std::vector< std::string > modelTypes={})
Constructs universal tag intended for unlimited amount of various items.
Definition: taginfo.cpp:34
Vector item with three x,y,z property items.
Definition: vectoritem.h:24
int item_role() const
Definition: viewitem.cpp:173
SessionItem * item() const
Definition: viewitem.cpp:168
Base class for all view models to show content of SessionModel in Qt views.
Definition: viewmodelbase.h:31
ViewItem * rootItem() const
Returns a pointer to invisible root item.
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
int rowCount(const QModelIndex &parent=QModelIndex()) const override
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.
Tests of ViewModelController class.
auto create_controller(SessionModel *session_model, ViewModelBase *view_model)
Defines class CLASS?
Defines class CLASS?
const int DATA
main data role
Definition: mvvm_types.h:30
const int DISPLAY
display name
Definition: mvvm_types.h:31
materialitems.h Collection of materials to populate MaterialModel.
Defines class CLASS?
Defines class CLASS?
Defines class CLASS?
Defines class CLASS?
Defines class CLASS?
Defines class CLASS?
Defines class CLASS?
Defines class CLASS?
TEST_F(ViewModelControllerTest, initialState)
Initial state of the controller. It is in working state only after setRootItem.