BornAgain  1.19.0
Simulate and fit neutron and x-ray scattering at grazing incidence
undostack.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/testmodel/undostack.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"
20 #include "mvvm/model/itemutils.h"
22 #include "mvvm/model/sessionitem.h"
24 #include "mvvm/model/taginfo.h"
28 #include "toyitems.h"
29 #include "toymodel.h"
30 
31 using namespace ModelView;
32 
33 class UndoStackTest : public ::testing::Test {
34 public:
36 };
37 
39 
40 //! Checking time of life of the command during undo/redo.
41 //! This is connected with the fact, that Qt takes ownership of the command and we have to use
42 //! our own wrapper.
43 
44 TEST_F(UndoStackTest, commandTimeOfLife)
45 {
46  SessionModel model;
47  auto item = model.insertItem<PropertyItem>();
48  item->setData(42);
49 
50  std::weak_ptr<SetValueCommand> pw_command; // to trace command time of life
51 
52  UndoStack stack;
53 
54  {
55  // creating command
56  auto command =
57  std::make_shared<SetValueCommand>(item, QVariant::fromValue(43), ItemDataRole::DATA);
58  pw_command = command;
59  EXPECT_EQ(pw_command.use_count(), 1);
60 
61  // checking state of item, stack and command
62  EXPECT_EQ(item->data<int>(), 42);
63  EXPECT_FALSE(stack.canRedo());
64  EXPECT_FALSE(stack.canUndo());
65  EXPECT_EQ(stack.index(), 0);
66  EXPECT_EQ(stack.count(), 0);
67  EXPECT_EQ(command->isObsolete(), false);
68  EXPECT_FALSE(std::get<bool>(command->result()));
69 
70  // adding command to the stack, it will lead to command execution
71  stack.execute(command);
72  EXPECT_EQ(pw_command.use_count(), 2);
73 
74  // checking state of item, stack and command
75  EXPECT_EQ(item->data<int>(), 43);
76  EXPECT_FALSE(stack.canRedo());
77  EXPECT_TRUE(stack.canUndo());
78  EXPECT_EQ(stack.index(), 1);
79  EXPECT_EQ(stack.count(), 1);
80  EXPECT_EQ(command->isObsolete(), false);
81  EXPECT_TRUE(std::get<bool>(command->result()));
82 
83  // undoing
84  stack.undo();
85  EXPECT_EQ(item->data<int>(), 42);
86  EXPECT_TRUE(stack.canRedo());
87  EXPECT_FALSE(stack.canUndo());
88  EXPECT_EQ(stack.index(), 0);
89  EXPECT_EQ(stack.count(), 1);
90  EXPECT_EQ(command->isObsolete(), false);
91  EXPECT_TRUE(std::get<bool>(command->result()));
92 
93  // redoing
94  stack.redo();
95  EXPECT_EQ(item->data<int>(), 43);
96  EXPECT_FALSE(stack.canRedo());
97  EXPECT_TRUE(stack.canUndo());
98  EXPECT_EQ(stack.index(), 1);
99  EXPECT_EQ(stack.count(), 1);
100  EXPECT_EQ(command->isObsolete(), false);
101  EXPECT_TRUE(std::get<bool>(command->result()));
102  }
103 
104  EXPECT_EQ(pw_command.use_count(), 1);
105  if (auto command = pw_command.lock()) {
106  EXPECT_EQ(command->isObsolete(), false);
107  EXPECT_TRUE(std::get<bool>(command->result()));
108  }
109 }
110 
111 //! Checking time of life of the command during undo/redo.
112 //! Same as above, but command is trying to set same value. It makes it "expired" and it should
113 //! be removed from the stack.
114 
115 TEST_F(UndoStackTest, expiredCommandTimeOfLife)
116 {
117  SessionModel model;
118  auto item = model.insertItem<PropertyItem>();
119  item->setData(42);
120 
121  std::weak_ptr<SetValueCommand> pw_command; // to trace command time of life
122 
123  UndoStack stack;
124 
125  {
126  // creating command which sets the same value
127  auto command =
128  std::make_shared<SetValueCommand>(item, QVariant::fromValue(42), ItemDataRole::DATA);
129  pw_command = command;
130  EXPECT_EQ(pw_command.use_count(), 1);
131 
132  // checking state of item, stack and command
133  EXPECT_EQ(item->data<int>(), 42);
134  EXPECT_FALSE(stack.canRedo());
135  EXPECT_FALSE(stack.canUndo());
136  EXPECT_EQ(stack.index(), 0);
137  EXPECT_EQ(stack.count(), 0);
138  EXPECT_EQ(command->isObsolete(), false);
139  EXPECT_FALSE(std::get<bool>(command->result()));
140 
141  // adding command to the stack, it will lead to command execution
142  stack.execute(command);
143  EXPECT_EQ(pw_command.use_count(), 1); // was already deleted from the stack
144 
145  // checking state of item, stack and command
146  EXPECT_EQ(item->data<int>(), 42);
147  EXPECT_FALSE(stack.canRedo());
148  EXPECT_FALSE(stack.canUndo());
149  EXPECT_EQ(stack.index(), 0);
150  EXPECT_EQ(stack.count(), 0);
151  EXPECT_EQ(command->isObsolete(), true);
152  EXPECT_FALSE(std::get<bool>(command->result()));
153  }
154 
155  EXPECT_EQ(pw_command.use_count(), 0);
156  EXPECT_EQ(pw_command.lock(), nullptr);
157 }
158 
159 TEST_F(UndoStackTest, initialState)
160 {
161  SessionModel model;
162 
163  // no undo/redo stack by default
164  EXPECT_TRUE(model.undoStack() == nullptr);
165 
166  // switching undo/redo on
167  model.setUndoRedoEnabled(true);
168  auto stack = model.undoStack();
169  EXPECT_TRUE(stack != nullptr);
170 
171  // initial state of undo redo stack
172  EXPECT_TRUE(stack->isActive());
173  EXPECT_FALSE(stack->canRedo());
174  EXPECT_FALSE(stack->canUndo());
175  EXPECT_EQ(stack->index(), 0);
176 }
177 
178 TEST_F(UndoStackTest, insertNewItem)
179 {
180  const model_type modelType(Constants::BaseType);
181  SessionModel model;
182  model.setUndoRedoEnabled(true);
183  auto stack = model.undoStack();
184 
185  // inserting single item
186  auto item = model.insertItem<SessionItem>();
187  EXPECT_TRUE(item != nullptr);
188  EXPECT_EQ(item->modelType(), Constants::BaseType);
189  EXPECT_EQ(model.rootItem()->childrenCount(), 1);
190  EXPECT_EQ(stack->index(), 1);
191  EXPECT_EQ(stack->count(), 1);
192  EXPECT_FALSE(stack->canRedo());
193  EXPECT_TRUE(stack->canUndo());
194 
195  // undoing item insertion
196  stack->undo();
197  EXPECT_EQ(model.rootItem()->childrenCount(), 0);
198  EXPECT_EQ(stack->index(), 0);
199  EXPECT_EQ(stack->count(), 1);
200  EXPECT_TRUE(stack->canRedo());
201  EXPECT_FALSE(stack->canUndo());
202 
203  // redoing item insertion
204  stack->redo();
205  EXPECT_EQ(model.rootItem()->childrenCount(), 1);
206  EXPECT_EQ(Utils::ChildAt(model.rootItem(), 0)->modelType(), modelType);
207  EXPECT_EQ(stack->index(), 1);
208  EXPECT_FALSE(stack->canRedo());
209  EXPECT_TRUE(stack->canUndo());
210 
211  // clearing stack
212  stack->clear();
213  EXPECT_EQ(stack->index(), 0);
214  EXPECT_FALSE(stack->canRedo());
215  EXPECT_FALSE(stack->canUndo());
216  EXPECT_EQ(model.rootItem()->childrenCount(), 1);
217  EXPECT_EQ(Utils::ChildAt(model.rootItem(), 0)->modelType(), modelType);
218 }
219 
220 //! Insert property item, unto, redo, and checking that identifier is preserved.
221 
222 TEST_F(UndoStackTest, insertPropertyItemID)
223 {
224  SessionModel model;
225  model.setUndoRedoEnabled(true);
226  auto stack = model.undoStack();
227 
228  auto item = model.insertItem<PropertyItem>();
229  auto original_id = item->identifier();
230 
231  EXPECT_EQ(stack->index(), 1);
232  EXPECT_EQ(stack->count(), 1);
233 
234  model.undoStack()->undo();
235  EXPECT_EQ(model.rootItem()->childrenCount(), 0);
236 
237  model.undoStack()->redo();
238  EXPECT_EQ(model.rootItem()->childrenCount(), 1);
239 
240  auto restored_property_item = Utils::ChildAt(model.rootItem(), 0);
241  EXPECT_EQ(restored_property_item->modelType(), Constants::PropertyType);
242  EXPECT_EQ(restored_property_item->identifier(), original_id);
243 }
244 
245 //! Undo/redo scenario when few items inserted.
246 
247 TEST_F(UndoStackTest, insertParentAndChild)
248 {
249  SessionModel model;
250  model.setUndoRedoEnabled(true);
251  auto stack = model.undoStack();
252 
253  auto parent = model.insertItem<SessionItem>();
254  parent->registerTag(TagInfo::universalTag("defaultTag"), /*set_as_default*/ true);
255 
256  model.insertItem<PropertyItem>(parent);
257  model.insertItem<PropertyItem>(parent);
258 
259  // state of the stack after insertion of 3 items
260  EXPECT_EQ(stack->count(), 3);
261  EXPECT_EQ(stack->index(), 3);
262  EXPECT_EQ(model.rootItem()->childrenCount(), 1);
263  EXPECT_EQ(parent->childrenCount(), 2);
264 
265  // undoing two last insertions
266  stack->undo();
267  stack->undo();
268  EXPECT_EQ(stack->count(), 3);
269  EXPECT_EQ(stack->index(), 1);
270  EXPECT_EQ(model.rootItem()->childrenCount(), 1);
271  EXPECT_EQ(parent->childrenCount(), 0);
272 
273  // redoing once
274  stack->redo();
275  EXPECT_EQ(stack->count(), 3);
276  EXPECT_EQ(stack->index(), 2);
277  EXPECT_EQ(model.rootItem()->childrenCount(), 1);
278  EXPECT_EQ(parent->childrenCount(), 1);
279  EXPECT_EQ(Utils::ChildAt(parent, 0)->modelType(), Constants::PropertyType);
280 }
281 
282 //! Undo/redo scenario when item inserted and data set few times.
283 
285 {
286  const int role = ItemDataRole::DATA;
287  SessionModel model;
288  model.setUndoRedoEnabled(true);
289  auto stack = model.undoStack();
290 
291  // creating item
292  auto item = model.insertItem<SessionItem>();
293  EXPECT_FALSE(model.data(item, role).isValid());
294 
295  // setting new data
296  QVariant value(42.0);
297  model.setData(item, value, role);
298  EXPECT_EQ(model.data(item, role), value);
299 
300  EXPECT_EQ(stack->index(), 2); // insert and setData commands
301  EXPECT_FALSE(model.undoStack()->canRedo());
302  EXPECT_TRUE(model.undoStack()->canUndo());
303 
304  // undoing and checking
305  stack->undo();
306  EXPECT_EQ(stack->index(), 1);
307  EXPECT_FALSE(model.data(item, role).isValid());
308 
309  // setting data three times
310  model.setData(item, QVariant::fromValue(42.0), role);
311  model.setData(item, QVariant::fromValue(43.0), role);
312  model.setData(item, QVariant::fromValue(44.0), role);
313  EXPECT_EQ(stack->index(), 4);
314  EXPECT_EQ(model.data(item, role).value<double>(), 44.0);
315  stack->undo();
316  stack->undo();
317  EXPECT_EQ(model.data(item, role).value<double>(), 42.0);
318  stack->redo();
319  stack->redo();
320  EXPECT_EQ(model.data(item, role).value<double>(), 44.0);
321 }
322 
323 //! Undo/redo scenario when item data changed through item and not the model.
324 
325 TEST_F(UndoStackTest, setDataThroughItem)
326 {
327  const int role = ItemDataRole::DATA;
328  const QVariant value(42.0);
329 
330  SessionModel model;
331  model.setUndoRedoEnabled(true);
332  auto stack = model.undoStack();
333 
334  // creating item
335  auto item = model.insertItem<SessionItem>();
336  EXPECT_FALSE(model.data(item, role).isValid());
337 
338  // setting new data through item (and not through model)
339  item->setData(value, role);
340  EXPECT_EQ(item->data<QVariant>(role), value);
341 
342  EXPECT_EQ(stack->index(), 2); // insert and setData commands
343  EXPECT_FALSE(model.undoStack()->canRedo());
344  EXPECT_TRUE(model.undoStack()->canUndo());
345 
346  // undoing and checking
347  stack->undo();
348  EXPECT_EQ(stack->index(), 1);
349  EXPECT_FALSE(model.data(item, role).isValid());
350 }
351 
352 //! Undo/redo scenario when we set same data. Undo stack should be empty.
353 
354 TEST_F(UndoStackTest, setSameData)
355 {
356  SessionModel model;
357  auto item = model.insertItem<PropertyItem>();
358  item->setData(42.0);
359 
360  model.setUndoRedoEnabled(true);
361  auto stack = model.undoStack();
362  EXPECT_EQ(stack->index(), 0);
363  EXPECT_FALSE(model.undoStack()->canRedo());
364  EXPECT_FALSE(model.undoStack()->canUndo());
365 
366  // setting same data should not lead to appearance of command in a stack
367  item->setData(42.0);
368  EXPECT_EQ(stack->index(), 0);
369  EXPECT_FALSE(model.undoStack()->canRedo());
370  EXPECT_FALSE(model.undoStack()->canUndo());
371 }
372 
373 //! Checks if we insert item, set data and undo everything we can get back to the data.
374 
375 TEST_F(UndoStackTest, insertAndSetData)
376 {
377  const int role = ItemDataRole::DATA;
378  SessionModel model;
379  model.setUndoRedoEnabled(true);
380  auto stack = model.undoStack();
381 
382  // creating item
383  auto item = model.insertItem<SessionItem>();
384  EXPECT_FALSE(model.data(item, role).isValid());
385 
386  // setting new data
387  QVariant value(42.0);
388  model.setData(item, value, role);
389  EXPECT_EQ(model.data(item, role), value);
390 
391  EXPECT_EQ(stack->index(), 2); // insert and setData commands
392  EXPECT_FALSE(model.undoStack()->canRedo());
393  EXPECT_TRUE(model.undoStack()->canUndo());
394 
395  // undoing twice and checking
396  stack->undo();
397  stack->undo();
398  EXPECT_EQ(stack->index(), 0);
399  EXPECT_EQ(model.rootItem()->childrenCount(), 0);
400 
401  // returning all back
402  stack->redo();
403  stack->redo();
404  EXPECT_EQ(stack->index(), 2);
405  EXPECT_EQ(model.rootItem()->childrenCount(), 1);
406  item = Utils::ChildAt(model.rootItem(), 0);
407  EXPECT_EQ(model.data(item, role).value<double>(), 42.0);
408 }
409 
410 //! Inserting item, setting the data, removing row, undoing, checking item and data.
411 
413 {
414  const int role = ItemDataRole::DATA;
415  const QVariant data(42);
416 
417  SessionModel model;
418  model.setUndoRedoEnabled(true);
419  auto stack = model.undoStack();
420 
421  auto item = model.insertItem<SessionItem>();
422  item->setData(data, role);
423 
424  // initial state before removing the row
425  EXPECT_EQ(stack->count(), 2); // insert and setData commands
426  EXPECT_EQ(stack->index(), 2); // insert and setData commands
427  EXPECT_FALSE(model.undoStack()->canRedo());
428  EXPECT_TRUE(model.undoStack()->canUndo());
429  EXPECT_EQ(item->data<QVariant>(role), data);
430  EXPECT_EQ(model.rootItem()->childrenCount(), 1);
431 
432  // removing the row
433  model.removeItem(model.rootItem(), {"", 0});
434  EXPECT_EQ(stack->count(), 3);
435  EXPECT_EQ(stack->index(), 3);
436  EXPECT_EQ(model.rootItem()->childrenCount(), 0);
437 
438  // undoing and checking the data
439  stack->undo();
440  EXPECT_EQ(stack->count(), 3);
441  EXPECT_EQ(stack->index(), 2);
442  EXPECT_EQ(model.rootItem()->childrenCount(), 1);
443  item = Utils::ChildAt(model.rootItem(), 0);
444  EXPECT_EQ(model.data(item, role).value<double>(), 42.0);
445  EXPECT_EQ(item->modelType(), Constants::BaseType);
446 }
447 
448 //! Inserting parent and child, setting data to them, removing parent, undoing and checking.
449 
450 TEST_F(UndoStackTest, removeParentAndChild)
451 {
452  const int role1(ItemDataRole::DATA), role2(ItemDataRole::DISPLAY);
453  const QVariant data1(42);
454  const QVariant data2 = QVariant::fromValue(std::string("abc"));
455 
456  SessionModel model;
457  model.setUndoRedoEnabled(true);
458  auto stack = model.undoStack();
459 
460  auto parent = model.insertItem<SessionItem>();
461  parent->registerTag(TagInfo::universalTag("defaultTag"), /*set_as_default*/ true);
462 
463  parent->setData(data1, role1);
464  auto child = model.insertItem<PropertyItem>(parent);
465  child->setData(data2, role2);
466 
467  EXPECT_EQ(stack->count(), 4);
468  EXPECT_EQ(stack->index(), 4);
469 
470  // removing parent
471  model.removeItem(model.rootItem(), {"", 0});
472  EXPECT_EQ(stack->count(), 5);
473  EXPECT_EQ(stack->index(), 5);
474  EXPECT_EQ(model.rootItem()->childrenCount(), 0);
475 
476  // undoing
477  stack->undo();
478  EXPECT_EQ(stack->count(), 5);
479  EXPECT_EQ(stack->index(), 4);
480  EXPECT_EQ(model.rootItem()->childrenCount(), 1);
481  auto parent_at = Utils::ChildAt(model.rootItem(), 0);
482  auto child_at = Utils::ChildAt(parent_at, 0);
483 
484  EXPECT_EQ(parent_at->modelType(), Constants::BaseType);
485  EXPECT_EQ(child_at->modelType(), Constants::PropertyType);
486 
487  EXPECT_EQ(parent_at->data<QVariant>(role1), data1);
488  EXPECT_EQ(child_at->data<QVariant>(role2), data2);
489 }
490 
491 //! Insert item, remove row, undo and check item id.
492 
493 TEST_F(UndoStackTest, itemIdentifierOnRemove)
494 {
495  SessionModel model;
496  model.setUndoRedoEnabled(true);
497  auto stack = model.undoStack();
498 
499  auto parent = model.insertItem<SessionItem>();
500  parent->registerTag(TagInfo::universalTag("defaultTag"), /*set_as_default*/ true);
501 
502  identifier_type parent_id = parent->identifier();
503  auto child = model.insertItem<PropertyItem>(parent);
504  identifier_type child_id = child->identifier();
505 
506  // removing parent
507  model.removeItem(model.rootItem(), {"", 0});
508  EXPECT_EQ(stack->count(), 3);
509  EXPECT_EQ(stack->index(), 3);
510  EXPECT_EQ(model.rootItem()->childrenCount(), 0);
511 
512  stack->undo();
513  auto parent_at = Utils::ChildAt(model.rootItem(), 0);
514  auto child_at = Utils::ChildAt(parent_at, 0);
515  identifier_type parent_id2 = parent_at->identifier();
516  identifier_type child_id2 = child_at->identifier();
517 
518  EXPECT_EQ(parent_id, parent_id2);
519  EXPECT_EQ(child_id, child_id2);
520 }
521 
522 //! Create multilayer, add two layers, remove everything and undo.
523 //! Toy models are used here.
524 
525 TEST_F(UndoStackTest, multiLayer)
526 {
527  auto pool = std::make_shared<ItemPool>();
528 
529  ToyItems::SampleModel model(pool);
530  model.setUndoRedoEnabled(true);
531  auto stack = model.undoStack();
532 
533  // creating multi layer
534  auto parent = model.insertItem<ToyItems::MultiLayerItem>();
535  EXPECT_TRUE(dynamic_cast<ToyItems::MultiLayerItem*>(parent) != nullptr);
536  EXPECT_EQ(parent->modelType(), ToyItems::Constants::MultiLayerItemType);
537 
538  // inserting two layers
539  auto layer0 = model.insertItem<ToyItems::LayerItem>(parent);
540  auto layer1 = model.insertItem<ToyItems::LayerItem>(parent);
541 
542  // saving identifiers for further reference
543  identifier_type id_parent = parent->identifier();
544  identifier_type id_layer0 = layer0->identifier();
545  identifier_type id_layer1 = layer1->identifier();
546 
547  // checking status of unddo stack
548  EXPECT_EQ(stack->count(), 3);
549  EXPECT_EQ(stack->index(), 3);
550 
551  // removing multi layer completely
552  model.removeItem(model.rootItem(), {"", 0});
553  EXPECT_EQ(stack->count(), 4);
554  EXPECT_EQ(stack->index(), 4);
555  EXPECT_EQ(model.rootItem()->childrenCount(), 0);
556 
557  // multilayer and its two layers should gone from registration
558  EXPECT_TRUE(pool->item_for_key(id_parent) == nullptr);
559  EXPECT_TRUE(pool->item_for_key(id_layer0) == nullptr);
560  EXPECT_TRUE(pool->item_for_key(id_layer1) == nullptr);
561 
562  // undoing multilayer removal
563  stack->undo();
564  EXPECT_EQ(stack->count(), 4);
565  EXPECT_EQ(stack->index(), 3);
566 
567  // restoring pointers back
568  auto parent_at = Utils::ChildAt(model.rootItem(), 0);
569  auto layer0_at = Utils::ChildAt(parent_at, 0);
570  auto layer1_at = Utils::ChildAt(parent_at, 1);
571 
572  // checking that restored item has corrrect identifiers
573  EXPECT_EQ(parent_at->identifier(), id_parent);
574  EXPECT_EQ(layer0_at->identifier(), id_layer0);
575  EXPECT_EQ(layer1_at->identifier(), id_layer1);
576 
577  // checking tag
578  EXPECT_EQ(layer0_at->tagRow().tag, ToyItems::MultiLayerItem::T_LAYERS);
579  EXPECT_EQ(layer1_at->tagRow().tag, ToyItems::MultiLayerItem::T_LAYERS);
580  std::vector<SessionItem*> expected = {layer0_at, layer1_at};
581  EXPECT_EQ(parent_at->getItems(ToyItems::MultiLayerItem::T_LAYERS), expected);
582 }
583 
584 //! Move single layer from multilayer to another empty multilayer.
585 
586 TEST_F(UndoStackTest, moveLayerFromMultiLayer)
587 {
588  auto pool = std::make_shared<ItemPool>();
589 
590  ToyItems::SampleModel model(pool);
591  model.setUndoRedoEnabled(true);
592  auto stack = model.undoStack();
593 
594  // creating multi layer with 3 layers
595  auto multilayer0 = model.insertItem<ToyItems::MultiLayerItem>();
596  auto layer0 = model.insertItem<ToyItems::LayerItem>(multilayer0);
597  auto multilayer1 = model.insertItem<ToyItems::MultiLayerItem>();
598 
599  // saving identifiers for further reference
600  identifier_type id_multilayer0 = multilayer0->identifier();
601  identifier_type id_layer0 = layer0->identifier();
602  identifier_type id_multilayer1 = multilayer1->identifier();
603 
604  // moving layer from multilayer
605  model.moveItem(layer0, multilayer1, {"", 0});
606 
607  // checking results
608  std::vector<SessionItem*> expected = {layer0};
609  EXPECT_EQ(multilayer0->children().size(), 0);
610  EXPECT_EQ(multilayer1->children(), expected);
611  EXPECT_EQ(pool->item_for_key(id_layer0), layer0);
612 
613  // undoing
614  stack->undo();
615  EXPECT_EQ(multilayer0->children(), expected);
616  EXPECT_EQ(multilayer1->children().size(), 0);
617  EXPECT_EQ(pool->item_for_key(id_layer0), layer0);
618 }
619 
620 //! Move single layer from multilayer to another empty multilayer.
621 //! Delete second multilayer and undo.
622 
623 TEST_F(UndoStackTest, moveLayerFromMLDeleteSecond)
624 {
625  auto pool = std::make_shared<ItemPool>();
626 
627  ToyItems::SampleModel model(pool);
628  model.setUndoRedoEnabled(true);
629  auto stack = model.undoStack();
630 
631  // creating multi layer with 3 layers
632  auto multilayer0 = model.insertItem<ToyItems::MultiLayerItem>();
633  auto layer0 = model.insertItem<ToyItems::LayerItem>(multilayer0);
634  auto multilayer1 = model.insertItem<ToyItems::MultiLayerItem>();
635 
636  // saving identifiers for further reference
637  identifier_type id_multilayer0 = multilayer0->identifier();
638  identifier_type id_layer0 = layer0->identifier();
639  identifier_type id_multilayer1 = multilayer1->identifier();
640 
641  // moving layer from multilayer
642  model.moveItem(layer0, multilayer1, {"", 0});
643 
644  // checking results
645  std::vector<SessionItem*> expected = {layer0};
646  EXPECT_EQ(multilayer0->children().size(), 0);
647  EXPECT_EQ(multilayer1->children(), expected);
648  EXPECT_EQ(pool->item_for_key(id_layer0), layer0);
649 
650  // deleting second multilayer
651  model.removeItem(model.rootItem(), {"", 1});
652 
653  // undoing deletion
654  stack->undo();
655 
656  // restoring ponters
657  auto layer0_at = pool->item_for_key(id_layer0);
658  auto multilayer1_at = pool->item_for_key(id_multilayer1);
659 
660  expected = {layer0_at};
661  EXPECT_EQ(multilayer0->children().size(), 0);
662  EXPECT_EQ(multilayer1_at->children(), expected);
663 
664  // unoing move
665  stack->undo();
666 
667  EXPECT_EQ(multilayer0->children(), expected);
668  EXPECT_EQ(multilayer1_at->children().size(), 0);
669 }
670 
671 //! Create 2 multilayers, 3 layers each. Move layer from one multilayer to another.
672 //! Deleting everything and undoing.
673 
674 TEST_F(UndoStackTest, moveLayerFromMLDeleteAll)
675 {
676  auto pool = std::make_shared<ItemPool>();
677 
678  ToyItems::SampleModel model(pool);
679  model.setUndoRedoEnabled(true);
680  auto stack = model.undoStack();
681 
682  // creating multi layer with 3 layers
683  auto multilayer0 = model.insertItem<ToyItems::MultiLayerItem>();
684  auto layer0 = model.insertItem<ToyItems::LayerItem>(multilayer0);
685  auto layer1 = model.insertItem<ToyItems::LayerItem>(multilayer0);
686  auto layer2 = model.insertItem<ToyItems::LayerItem>(multilayer0);
687 
688  // saving identifiers for further reference
689  identifier_type id_multilayer0 = multilayer0->identifier();
690  identifier_type id_layer0 = layer0->identifier();
691  identifier_type id_layer1 = layer1->identifier();
692  identifier_type id_layer2 = layer2->identifier();
693 
694  // creating another multi layer with 3 layers
695  auto multilayer1 = model.insertItem<ToyItems::MultiLayerItem>();
696  auto layer3 = model.insertItem<ToyItems::LayerItem>(multilayer1);
697  auto layer4 = model.insertItem<ToyItems::LayerItem>(multilayer1);
698  auto layer5 = model.insertItem<ToyItems::LayerItem>(multilayer1);
699 
700  // saving identifiers for further reference
701  identifier_type id_multilayer1 = multilayer1->identifier();
702  identifier_type id_layer3 = layer3->identifier();
703  identifier_type id_layer4 = layer4->identifier();
704  identifier_type id_layer5 = layer5->identifier();
705 
706  // checking status of unddo stack
707  EXPECT_EQ(stack->count(), 8);
708  EXPECT_EQ(stack->index(), 8);
709 
710  // moving layer1 to second multilayer
711  model.moveItem(layer1, multilayer1, {ToyItems::MultiLayerItem::T_LAYERS, 0});
712 
713  // removing multilayers
714  model.removeItem(model.rootItem(), {"", 1});
715  model.removeItem(model.rootItem(), {"", 0});
716 
717  // checking status of unddo stack
718  EXPECT_EQ(stack->count(), 11);
719  EXPECT_EQ(stack->index(), 11);
720 
721  // undoing thrice
722  stack->undo();
723  stack->undo();
724  stack->undo();
725 
726  // restoring pointers
727  auto multilayer0_r = pool->item_for_key(id_multilayer0);
728  auto layer0_r = pool->item_for_key(id_layer0);
729  auto layer1_r = pool->item_for_key(id_layer1);
730  auto layer2_r = pool->item_for_key(id_layer2);
731  auto multilayer1_r = pool->item_for_key(id_multilayer1);
732  auto layer3_r = pool->item_for_key(id_layer3);
733  auto layer4_r = pool->item_for_key(id_layer4);
734  auto layer5_r = pool->item_for_key(id_layer5);
735 
736  // checking layers
737  std::vector<SessionItem*> expected = {layer0_r, layer1_r, layer2_r};
738  EXPECT_EQ(multilayer0_r->children(), expected);
739 
740  expected = {layer3_r, layer4_r, layer5_r};
741  EXPECT_EQ(multilayer1_r->children(), expected);
742 }
743 
744 //! Creating two multilayers. Copying layer from one multilayer to another.
745 
746 TEST_F(UndoStackTest, copyLayerFromMultilayer)
747 {
748  auto pool = std::make_shared<ItemPool>();
749  ToyItems::SampleModel model(pool);
750  model.setUndoRedoEnabled(true);
751  auto stack = model.undoStack();
752 
753  const double expected_thickness = 55.0;
754 
755  // creating multi layer with 3 layers
756  auto multilayer0 = model.insertItem<ToyItems::MultiLayerItem>();
757  auto layer0 = model.insertItem<ToyItems::LayerItem>(multilayer0);
758  layer0->setProperty(ToyItems::LayerItem::P_THICKNESS, expected_thickness);
759  auto multilayer1 = model.insertItem<ToyItems::MultiLayerItem>();
760 
761  // copying layer
762  auto layer_copy = dynamic_cast<ToyItems::LayerItem*>(model.copyItem(layer0, multilayer1));
763  EXPECT_EQ(multilayer1->itemCount(ToyItems::MultiLayerItem::T_LAYERS), 1);
764  EXPECT_EQ(layer_copy->property<double>(ToyItems::LayerItem::P_THICKNESS), expected_thickness);
765  EXPECT_TRUE(layer0->identifier() != layer_copy->identifier());
766 
767  auto id = layer_copy->identifier();
768  EXPECT_EQ(pool->item_for_key(layer_copy->identifier()), layer_copy);
769 
770  // undoing
771  stack->undo();
772  EXPECT_EQ(multilayer1->itemCount(ToyItems::MultiLayerItem::T_LAYERS), 0);
773 
774  // redoing
775  stack->redo();
776  EXPECT_EQ(multilayer1->itemCount(ToyItems::MultiLayerItem::T_LAYERS), 1);
777  EXPECT_EQ(multilayer1->getItems(ToyItems::MultiLayerItem::T_LAYERS)[0]->identifier(), id);
778 }
779 
780 //! Add item and changing its data from macros.
781 
782 TEST_F(UndoStackTest, beginMacrosEndMacros)
783 {
784  const int role = ItemDataRole::DATA;
785  const QVariant data(42);
786 
787  SessionModel model;
788  model.setUndoRedoEnabled(true);
789  auto stack = model.undoStack();
790 
791  stack->beginMacro("macro1");
792  auto item = model.insertItem<SessionItem>();
793  item->setData(data, role);
794  stack->endMacro();
795 
796  // initial state before removing the row
797  EXPECT_EQ(stack->count(), 1); // insert and setData commands
798  EXPECT_EQ(stack->index(), 1); // insert and setData commands
799  EXPECT_FALSE(model.undoStack()->canRedo());
800  EXPECT_TRUE(model.undoStack()->canUndo());
801  EXPECT_EQ(item->data<QVariant>(role), data);
802  EXPECT_EQ(model.rootItem()->childrenCount(), 1);
803 
804  // undoing and checking the data
805  stack->undo();
806  EXPECT_EQ(stack->count(), 1);
807  EXPECT_EQ(stack->index(), 0);
808  EXPECT_EQ(model.rootItem()->childrenCount(), 0);
809 
810  // redoing
811  stack->redo();
812  EXPECT_EQ(stack->count(), 1);
813  EXPECT_EQ(stack->index(), 1);
814  EXPECT_EQ(model.rootItem()->childrenCount(), 1);
815  item = Utils::ChildAt(model.rootItem(), 0);
816  EXPECT_EQ(model.data(item, role).value<double>(), 42.0);
817 }
818 
819 //! Add GraphItem and Data1DItem, addisgn data to graph, undo, then redo.
820 //! GraphItem should be pointing again to Data1DItem.
821 //! This is real bug case.
822 
823 TEST_F(UndoStackTest, insertDataAndGraph)
824 {
825  // constructing model with pool, enabling undo/redo
826  auto pool = std::make_shared<ItemPool>();
827  SessionModel model("Model", pool);
828  model.setUndoRedoEnabled(true);
829  EXPECT_EQ(model.undoStack()->index(), 0);
830  EXPECT_EQ(model.undoStack()->count(), 0);
831 
832  auto dataItem = model.insertItem<Data1DItem>();
833  auto graphItem = model.insertItem<GraphItem>();
834  graphItem->setDataItem(dataItem);
835 
836  auto data_item_identifier = dataItem->identifier();
837  auto graph_item_identifier = graphItem->identifier();
838 
839  // model has two elements, graph is pointing to the data
840  EXPECT_EQ(model.undoStack()->index(), 3);
841  EXPECT_EQ(model.undoStack()->count(), 3);
842  EXPECT_EQ(model.rootItem()->childrenCount(), 2);
843  EXPECT_EQ(graphItem->dataItem(), dataItem);
844 
845  // checking pool
846  EXPECT_EQ(pool->item_for_key(data_item_identifier), dataItem);
847  EXPECT_EQ(pool->item_for_key(graph_item_identifier), graphItem);
848 
849  // undoing once (setDataItem operation)
850  model.undoStack()->undo();
851  EXPECT_EQ(model.undoStack()->index(), 2);
852  EXPECT_EQ(model.undoStack()->count(), 3);
853  EXPECT_EQ(model.rootItem()->childrenCount(), 2);
854  EXPECT_EQ(graphItem->dataItem(), nullptr);
855 
856  // undoing two more times item
857  model.undoStack()->undo();
858  model.undoStack()->undo();
859  EXPECT_EQ(model.undoStack()->index(), 0);
860  EXPECT_EQ(model.undoStack()->count(), 3);
861  EXPECT_EQ(model.rootItem()->childrenCount(), 0);
862 
863  // redoing (dataItem is back)
864  model.undoStack()->redo();
865  EXPECT_EQ(model.undoStack()->index(), 1);
866  EXPECT_EQ(model.rootItem()->childrenCount(), 1);
867  auto restoredDataItem = model.topItem<Data1DItem>();
868  EXPECT_EQ(restoredDataItem->identifier(), data_item_identifier);
869  EXPECT_EQ(pool->item_for_key(data_item_identifier), restoredDataItem);
870 
871  // redoing (GraphItem) is back
872  model.undoStack()->redo();
873  EXPECT_EQ(model.undoStack()->index(), 2);
874  EXPECT_EQ(model.rootItem()->childrenCount(), 2);
875  auto restoredGraphItem = model.topItem<GraphItem>();
876  EXPECT_EQ(restoredGraphItem->identifier(), graph_item_identifier);
877  EXPECT_EQ(restoredGraphItem->dataItem(), nullptr);
878 
879  // redoing (graph is linked with data gaian)
880  model.undoStack()->redo();
881  EXPECT_EQ(model.undoStack()->index(), 3);
882  EXPECT_EQ(model.rootItem()->childrenCount(), 2);
883  EXPECT_EQ(restoredGraphItem->dataItem(), restoredDataItem);
884 }
885 
886 //! Setup Data1DItem via macro. Undo, then redo.
887 //! Add GraphItem and Data1DItem, addisgn data to graph, undo, then redo.
888 //! GraphItem should be pointing again to Data1DItem.
889 //! This is real bug case.
890 
891 TEST_F(UndoStackTest, insertDataItemViaMacro)
892 {
893  SessionModel model;
894  model.setUndoRedoEnabled(true);
895  EXPECT_EQ(model.undoStack()->index(), 0);
896  EXPECT_EQ(model.undoStack()->count(), 0);
897 
898  // setting up single data item via macro
899  model.undoStack()->beginMacro("AddDataItem");
900  auto dataItem = model.insertItem<Data1DItem>();
901  const std::vector<double> expected_values = {1.0, 2.0, 3.0};
902  const std::vector<double> expected_centers = {0.5, 1.5, 2.5};
903  dataItem->setAxis<FixedBinAxisItem>(3, 0.0, 3.0);
904  dataItem->setValues(expected_values);
905  model.undoStack()->endMacro();
906 
907  EXPECT_EQ(model.undoStack()->index(), 1);
908  EXPECT_EQ(model.undoStack()->count(), 1);
909 
910  // undoing and returning back
911  model.undoStack()->undo();
912  model.undoStack()->redo();
913  EXPECT_EQ(model.undoStack()->index(), 1);
914  EXPECT_EQ(model.undoStack()->count(), 1);
915 
916  auto restoredDataItem = model.topItem<Data1DItem>();
917  EXPECT_EQ(restoredDataItem->binCenters(), expected_centers);
918  EXPECT_EQ(restoredDataItem->binValues(), expected_values);
919 }
Defines class CLASS?
Represents one-dimensional data (axis and values).
Definition: data1ditem.h:30
T * setAxis(Args &&... args)
Inserts axis of given type.
Definition: data1ditem.h:56
Item to represent fixed bin axis.
Definition: axisitems.h:75
One-dimensional graph representation of Data1DItem.
Definition: graphitem.h:29
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
std::string identifier() const
Returns unique identifier.
Definition: sessionitem.cpp:87
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
int childrenCount() const
Returns total number of children in all tags.
std::vector< SessionItem * > children() const
Returns vector of children formed from all chidlren from all tags.
T property(const std::string &tag) const
Returns data stored in property item.
Definition: sessionitem.h:181
model_type modelType() const
Returns item's model type.
Definition: sessionitem.cpp:80
void setProperty(const std::string &tag, const T &value)
Sets value to property item.
Definition: sessionitem.h:190
Main class to hold hierarchy of SessionItem objects.
Definition: sessionmodel.h:37
T * topItem() const
Returns top item of the given type.
Definition: sessionmodel.h:126
bool setData(SessionItem *item, const Variant &value, int role)
Sets the data for given item.
void setUndoRedoEnabled(bool value)
Sets undo/redo either enabled or disabled. By default undo/redo is disabled.
void moveItem(SessionItem *item, SessionItem *new_parent, const TagRow &tagrow)
Move item from it's current parent to a new parent under given tag and row.
SessionItem * copyItem(const SessionItem *item, SessionItem *parent, const TagRow &tagrow={})
Copy item and insert it in parent's tag and row. Item could belong to any model/parent.
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
UndoStackInterface * undoStack() const
Returns command stack to perform undo/redo.
Variant data(SessionItem *item, int role) const
Returns the data for given item and role.
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
virtual bool canUndo() const =0
virtual int index() const =0
virtual void beginMacro(const std::string &name)=0
virtual int count() const =0
virtual bool canRedo() const =0
Default undo stack implementation.
Definition: undostack.h:30
bool canRedo() const override
Definition: undostack.cpp:48
void undo() override
Definition: undostack.cpp:63
int index() const override
Definition: undostack.cpp:53
void execute(std::shared_ptr< AbstractItemCommand > command) override
Executes the command, then pushes it in the stack for possible undo.
Definition: undostack.cpp:29
bool canUndo() const override
Definition: undostack.cpp:43
int count() const override
Definition: undostack.cpp:58
void redo() override
Definition: undostack.cpp:68
Represents a layer, with thickness and color, and possibly populated with particles.
Definition: toyitems.h:52
static const std::string P_THICKNESS
Definition: toyitems.h:54
Represents multilayer with collection of layers.
Definition: toyitems.h:44
static const std::string T_LAYERS
Definition: toyitems.h:46
Defines class CLASS?
Defines class CLASS?
Defines class CLASS?
Defines class CLASS?
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
const model_type PropertyType
Definition: mvvm_types.h:59
const model_type BaseType
Definition: mvvm_types.h:45
const int DATA
main data role
Definition: mvvm_types.h:30
const int DISPLAY
display name
Definition: mvvm_types.h:31
MVVM_MODEL_EXPORT SessionItem * ChildAt(const SessionItem *parent, int index)
Returns child at given index of parent.
Definition: itemutils.cpp:70
materialitems.h Collection of materials to populate MaterialModel.
std::string identifier_type
Definition: types.h:22
std::string model_type
Definition: types.h:23
const ModelView::model_type MultiLayerItemType
Definition: toyitems.h:30
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(UndoStackTest, commandTimeOfLife)
Checking time of life of the command during undo/redo.
Defines class CLASS?