BornAgain  1.19.0
Simulate and fit neutron and x-ray scattering at grazing incidence
viewmodelbase.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/viewmodelbase.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"
16 #include "mvvm/model/sessionitem.h"
19 #include "test_utils.h"
20 #include <QSignalSpy>
21 #include <QStandardItemModel>
22 
23 using namespace ModelView;
24 
25 //! Tests for ViewModelBase class.
26 
27 class ViewModelBaseTest : public ::testing::Test {
28 public:
30 
31  class TestItem : public ViewItem {
32  public:
33  TestItem() : ViewItem(nullptr, 0) {}
34  ~TestItem() override;
35  };
36 
37  using children_t = std::vector<std::unique_ptr<ViewItem>>;
38  using expected_t = std::vector<ViewItem*>;
39 
40  //! Helper function to get two vectors, each ncolumns length, in the form of a pair.
41  //! First vector contains unique_ptr objects, second vector bare pointers to same objects.
42  //! First vector is intended to be moved inside a model, second vector is to validate
43  //! the content of a model after the move.
44 
45  std::pair<children_t, expected_t> test_data(int ncolumns)
46  {
47  auto vector_of_unique = TestUtils::create_row<ViewItem, TestItem>(ncolumns);
48  auto vector_of_pointers = TestUtils::create_pointers(vector_of_unique);
49  return std::make_pair(std::move(vector_of_unique), std::move(vector_of_pointers));
50  }
51 };
52 
55 
56 //! Checking behaviour of QStandardItemModel for reference.
57 
58 TEST_F(ViewModelBaseTest, standardItemModel)
59 {
60  QStandardItemModel model;
61  auto parent = model.invisibleRootItem();
62 
63  EXPECT_EQ(model.rowCount(), 0);
64  EXPECT_EQ(model.columnCount(), 0);
65 
66  QList<QStandardItem*> children{new QStandardItem, new QStandardItem};
67  parent->appendRow(children);
68  auto index = model.index(0, 1, QModelIndex());
69  EXPECT_EQ(model.itemFromIndex(index), children.at(1));
70 
71  // construction of index for non-existing column leads to invalid index
72  auto non_existing_index = model.index(0, 2, QModelIndex());
73  EXPECT_FALSE(non_existing_index.isValid());
74  EXPECT_EQ(non_existing_index, QModelIndex());
75 
76  // attempt to retrieve item using this non-existing index leads to nullptr.
77  EXPECT_EQ(model.itemFromIndex(non_existing_index), nullptr);
78 
79  // default constructed index gives same nullptr
80  EXPECT_EQ(model.itemFromIndex(QModelIndex()), nullptr);
81 
82  // to summarize, default-constructed index, invalid index and index leading to non-existing
83  // item are the same
84 }
85 
86 //! Initial state of empty ViewModelBase.
87 
88 TEST_F(ViewModelBaseTest, initialState)
89 {
90  ViewModelBase viewmodel;
91  EXPECT_EQ(viewmodel.rowCount(), 0);
92  EXPECT_EQ(viewmodel.columnCount(), 0);
93  EXPECT_TRUE(viewmodel.rootItem() != nullptr);
94  EXPECT_EQ(viewmodel.itemFromIndex(QModelIndex()), nullptr);
95  auto non_existing_index = viewmodel.index(0, 0, QModelIndex());
96  EXPECT_FALSE(non_existing_index.isValid());
97  EXPECT_EQ(viewmodel.itemFromIndex(non_existing_index), nullptr);
98  EXPECT_EQ(viewmodel.parent(QModelIndex()), QModelIndex());
99  EXPECT_EQ(viewmodel.indexFromItem(viewmodel.rootItem()), QModelIndex());
100 }
101 
103 {
104  ViewModelBase viewmodel;
105 
106  // item to append
107  auto [children, expected] = test_data(/*ncolumns*/ 1);
108 
109  // appending one row
110  viewmodel.appendRow(viewmodel.rootItem(), std::move(children));
111  EXPECT_EQ(viewmodel.rowCount(), 1);
112  EXPECT_EQ(viewmodel.columnCount(), 1);
113 
114  // constructing index for child
115  auto child_index = viewmodel.index(0, 0, QModelIndex());
116  EXPECT_EQ(child_index.row(), 0);
117  EXPECT_EQ(child_index.column(), 0);
118  EXPECT_EQ(child_index.model(), &viewmodel);
119 
120  // indexFromItem
121  EXPECT_EQ(viewmodel.indexFromItem(expected[0]), child_index);
122 
123  // getting child from index
124  EXPECT_EQ(viewmodel.itemFromIndex(child_index), expected[0]);
125 
126  // no grand-children
127  EXPECT_EQ(viewmodel.rowCount(child_index), 0);
128  EXPECT_EQ(viewmodel.columnCount(child_index), 0);
129 
130  // parent index
131  EXPECT_EQ(viewmodel.parent(child_index), QModelIndex());
132 }
133 
134 //! Insert one row befor another.
135 
137 {
138  ViewModelBase viewmodel;
139 
140  // item to append
141  auto [children_row0, expected_row0] = test_data(/*ncolumns*/ 1);
142  auto [children_front, expected_front] = test_data(/*ncolumns*/ 1);
143 
144  // appending one row
145  viewmodel.appendRow(viewmodel.rootItem(), std::move(children_row0));
146  viewmodel.insertRow(viewmodel.rootItem(), 0, std::move(children_front));
147  EXPECT_EQ(viewmodel.rowCount(), 2);
148  EXPECT_EQ(viewmodel.columnCount(), 1);
149 
150  // constructing index for child
151  auto child_index0 = viewmodel.index(0, 0, QModelIndex());
152  auto child_index1 = viewmodel.index(1, 0, QModelIndex());
153 
154  // indexFromItem
155  EXPECT_EQ(viewmodel.indexFromItem(expected_row0[0]), child_index1);
156  EXPECT_EQ(viewmodel.indexFromItem(expected_front[0]), child_index0);
157 
158  // getting child from index
159  EXPECT_EQ(viewmodel.itemFromIndex(child_index0), expected_front[0]);
160  EXPECT_EQ(viewmodel.itemFromIndex(child_index1), expected_row0[0]);
161 }
162 
164 {
165  ViewModelBase viewmodel;
166 
167  // item to append
168  auto [children, expected] = test_data(/*ncolumns*/ 1);
169 
170  // appending one row
171  viewmodel.appendRow(viewmodel.rootItem(), std::move(children));
172  EXPECT_EQ(viewmodel.rowCount(), 1);
173  EXPECT_EQ(viewmodel.columnCount(), 1);
174 
175  // removing row
176  viewmodel.removeRow(viewmodel.rootItem(), 0);
177  EXPECT_EQ(viewmodel.rowCount(), 0);
178  EXPECT_EQ(viewmodel.columnCount(), 0);
179 }
180 
181 TEST_F(ViewModelBaseTest, appendRowToRow)
182 {
183  ViewModelBase viewmodel;
184 
185  // preparing two rows of children, two columns each
186  auto [children_row0, expected_row0] = test_data(/*ncolumns*/ 2);
187  auto [children_row1, expected_row1] = test_data(/*ncolumns*/ 2);
188 
189  // appending rows to root
190  viewmodel.appendRow(viewmodel.rootItem(), std::move(children_row0));
191  // appending rows to row
192  auto child0_index = viewmodel.index(0, 0, QModelIndex());
193  auto child1_index = viewmodel.index(0, 1, QModelIndex());
194  viewmodel.appendRow(expected_row0[0], std::move(children_row1));
195 
196  // checking results
197  EXPECT_EQ(viewmodel.rowCount(QModelIndex()), 1);
198  EXPECT_EQ(viewmodel.columnCount(QModelIndex()), 2);
199  EXPECT_EQ(viewmodel.rowCount(child0_index), 1);
200  EXPECT_EQ(viewmodel.columnCount(child0_index), 2);
201 
202  // checking parent index of children in second row
203  auto grandchild0_index = viewmodel.index(0, 0, child0_index);
204  auto grandchild1_index = viewmodel.index(0, 1, child0_index);
205  EXPECT_EQ(viewmodel.parent(grandchild0_index), child0_index);
206  EXPECT_EQ(viewmodel.parent(grandchild1_index), child0_index);
207 
208  // index of item
209  EXPECT_EQ(viewmodel.indexFromItem(expected_row0[0]), child0_index);
210  EXPECT_EQ(viewmodel.indexFromItem(expected_row0[1]), child1_index);
211  EXPECT_EQ(viewmodel.indexFromItem(expected_row1[0]), grandchild0_index);
212  EXPECT_EQ(viewmodel.indexFromItem(expected_row1[1]), grandchild1_index);
213 }
214 
215 TEST_F(ViewModelBaseTest, onRowsAppended)
216 {
217  ViewModelBase viewmodel;
218 
219  // two items to append as a single row with two columns
220  auto [children, expected] = test_data(/*ncolumns*/ 2);
221 
222  QSignalSpy spyInsert(&viewmodel, &ViewModelBase::rowsInserted);
223  QSignalSpy spyRemove(&viewmodel, &ViewModelBase::rowsRemoved);
224 
225  // appending one row
226  viewmodel.appendRow(viewmodel.rootItem(), std::move(children));
227  EXPECT_EQ(viewmodel.rowCount(), 1);
228  EXPECT_EQ(viewmodel.columnCount(), 2);
229 
230  // checking that signaling is about the parent
231  EXPECT_EQ(spyRemove.count(), 0);
232  EXPECT_EQ(spyInsert.count(), 1);
233  QList<QVariant> arguments = spyInsert.takeFirst();
234  EXPECT_EQ(arguments.size(), 3); // QModelIndex &parent, int first, int last
235  EXPECT_EQ(arguments.at(0).value<QModelIndex>(), QModelIndex());
236  EXPECT_EQ(arguments.at(1).value<int>(), 0);
237  EXPECT_EQ(arguments.at(2).value<int>(), 0);
238 
239  // getting child from index
240  auto index0 = viewmodel.index(0, 0, QModelIndex());
241  auto index1 = viewmodel.index(0, 1, QModelIndex());
242  EXPECT_EQ(viewmodel.itemFromIndex(index0), expected[0]);
243  EXPECT_EQ(viewmodel.itemFromIndex(index1), expected[1]);
244 }
245 
247 {
248  ViewModelBase viewmodel;
249 
250  // three rows of items
251  auto [children_row0, expected_row0] = test_data(/*ncolumns*/ 2);
252  auto [children_row1, expected_row1] = test_data(/*ncolumns*/ 2);
253  auto [children_row2, expected_row2] = test_data(/*ncolumns*/ 2);
254 
255  QSignalSpy spyInsert(&viewmodel, &ViewModelBase::rowsInserted);
256  QSignalSpy spyRemove(&viewmodel, &ViewModelBase::rowsRemoved);
257 
258  // appending one row
259  viewmodel.appendRow(viewmodel.rootItem(), std::move(children_row0));
260  viewmodel.appendRow(viewmodel.rootItem(), std::move(children_row1));
261  viewmodel.appendRow(viewmodel.rootItem(), std::move(children_row2));
262 
263  // removing middle row
264  viewmodel.removeRow(viewmodel.rootItem(), 1);
265 
266  // checking that signaling is about the parent
267  EXPECT_EQ(spyRemove.count(), 1);
268  EXPECT_EQ(spyInsert.count(), 3);
269  QList<QVariant> arguments = spyRemove.takeFirst();
270  EXPECT_EQ(arguments.size(), 3); // QModelIndex &parent, int first, int last
271  EXPECT_EQ(arguments.at(0).value<QModelIndex>(), QModelIndex());
272  EXPECT_EQ(arguments.at(1).value<int>(), 1);
273  EXPECT_EQ(arguments.at(2).value<int>(), 1);
274 }
275 
277 {
278  SessionItem item;
279  QVariant expected(42.0);
280  item.setData(expected);
281 
282  children_t children;
283  children.emplace_back(std::make_unique<ViewDataItem>(&item));
284 
285  ViewModelBase viewmodel;
286  viewmodel.appendRow(viewmodel.rootItem(), std::move(children));
287 
288  QModelIndex children_index = viewmodel.index(0, 0, QModelIndex());
289 
290  EXPECT_EQ(viewmodel.data(children_index, Qt::EditRole), expected);
291 }
292 
294 {
295  // creating single item
296  SessionItem item;
297  QVariant expected(42.0);
298  item.setData(expected);
299 
300  // creating view model displaying given SessionItem
301  children_t children;
302  children.emplace_back(std::make_unique<ViewDataItem>(&item));
303  ViewModelBase viewmodel;
304  viewmodel.appendRow(viewmodel.rootItem(), std::move(children));
305 
306  QSignalSpy spyData(&viewmodel, &ViewModelBase::dataChanged);
307 
308  // changing the data
309  QModelIndex children_index = viewmodel.index(0, 0, QModelIndex());
310  QVariant new_value(43.0);
311  EXPECT_TRUE(viewmodel.setData(children_index, new_value, Qt::EditRole));
312 
313  // checking signaling
314  EXPECT_EQ(spyData.count(), 1);
315  QList<QVariant> arguments = spyData.takeFirst();
316  EXPECT_EQ(arguments.size(), 3); // QModelIndex &parent, int first, int last
317  EXPECT_EQ(arguments.at(0).value<QModelIndex>(), children_index);
318  EXPECT_EQ(arguments.at(1).value<QModelIndex>(), children_index);
319  QVector<int> expected_roles{Qt::EditRole};
320  EXPECT_EQ(arguments.at(2).value<QVector<int>>(), expected_roles);
321 }
322 
324 {
325  SessionItem item;
326  QVariant expected(42.0);
327  item.setData(expected);
328  item.setDisplayName("Name");
329 
330  children_t children;
331  children.emplace_back(std::make_unique<ViewLabelItem>(&item));
332  children.emplace_back(std::make_unique<ViewDataItem>(&item));
333 
334  ViewModelBase viewmodel;
335  viewmodel.appendRow(viewmodel.rootItem(), std::move(children));
336 
337  QModelIndex label_index = viewmodel.index(0, 0, QModelIndex());
338  QModelIndex data_index = viewmodel.index(0, 1, QModelIndex());
339 
340  EXPECT_TRUE(viewmodel.flags(label_index) & Qt::ItemIsSelectable);
341  EXPECT_TRUE(viewmodel.flags(label_index) & Qt::ItemIsEnabled);
342  EXPECT_FALSE(viewmodel.flags(label_index) & Qt::ItemIsEditable);
343 
344  EXPECT_TRUE(viewmodel.flags(data_index) & Qt::ItemIsSelectable);
345  EXPECT_TRUE(viewmodel.flags(data_index) & Qt::ItemIsEnabled);
346  EXPECT_TRUE(viewmodel.flags(data_index) & Qt::ItemIsEditable);
347 }
348 
349 TEST_F(ViewModelBaseTest, clearRowsFromRoot)
350 {
351  ViewModelBase viewmodel;
352 
353  // three rows of items
354  auto [children_row0, expected_row0] = test_data(/*ncolumns*/ 2);
355  auto [children_row1, expected_row1] = test_data(/*ncolumns*/ 2);
356 
357  QSignalSpy spyInsert(&viewmodel, &ViewModelBase::rowsInserted);
358  QSignalSpy spyRemove(&viewmodel, &ViewModelBase::rowsRemoved);
359 
360  // appending one row
361  viewmodel.appendRow(viewmodel.rootItem(), std::move(children_row0));
362  viewmodel.appendRow(viewmodel.rootItem(), std::move(children_row1));
363 
364  viewmodel.clearRows(viewmodel.rootItem());
365 
366  EXPECT_EQ(viewmodel.rowCount(), 0);
367  EXPECT_EQ(viewmodel.columnCount(), 0);
368 
369  // checking that signaling is about the parent
370  EXPECT_EQ(spyRemove.count(), 1);
371  EXPECT_EQ(spyInsert.count(), 2);
372  QList<QVariant> arguments = spyRemove.takeFirst();
373  EXPECT_EQ(arguments.size(), 3); // QModelIndex &parent, int first, int last
374  EXPECT_EQ(arguments.at(0).value<QModelIndex>(), QModelIndex());
375  EXPECT_EQ(arguments.at(1).value<int>(), 0);
376  EXPECT_EQ(arguments.at(2).value<int>(), 1);
377 }
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
virtual SessionItem * setDisplayName(const std::string &name)
Sets display name (fluent interface).
Represents the view of SessionItem's data in a single cell of ViewModel.
Definition: viewitem.h:29
Base class for all view models to show content of SessionModel in Qt views.
Definition: viewmodelbase.h:31
void removeRow(ViewItem *parent, int row)
void appendRow(ViewItem *parent, std::vector< std::unique_ptr< ViewItem >> items)
Appends row of items to given parent.
ViewItem * rootItem() const
Returns a pointer to invisible root item.
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.
void clearRows(ViewItem *parent)
bool setData(const QModelIndex &index, const QVariant &value, int role) override
QModelIndex indexFromItem(const ViewItem *item) const
Returns the QModelIndex associated with the given item.
void insertRow(ViewItem *parent, int row, std::vector< std::unique_ptr< ViewItem >> items)
Insert a row of items at index 'row' to given parent.
QModelIndex parent(const QModelIndex &child) 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 for ViewModelBase class.
std::pair< children_t, expected_t > test_data(int ncolumns)
Helper function to get two vectors, each ncolumns length, in the form of a pair.
std::vector< ViewItem * > expected_t
std::vector< std::unique_ptr< ViewItem > > children_t
Defines class CLASS?
void removeRow(QGridLayout *layout, int row, bool deleteWidgets=true)
Removes row from grid layout (important: doesn't change row count).
Definition: LayoutUtils.cpp:58
materialitems.h Collection of materials to populate MaterialModel.
auto create_pointers(const std::vector< std::unique_ptr< T >> &vec)
Creates vector of pointers from vector of unique_ptr.
Definition: test_utils.h:90
Defines class CLASS?
Defines class CLASS?
Defines class CLASS?
Defines class CLASS?
TEST_F(ViewModelBaseTest, standardItemModel)
Checking behaviour of QStandardItemModel for reference.