diff --git a/Tests/UnitTests/GUI/TestGUI.cpp b/Tests/UnitTests/GUI/TestGUI.cpp
index 4ca1e63400cbc6b0bccf6b57a2e38f1612e72b7b..e5fccadeb26b33966b3b33825beca2b7930fb3a8 100644
--- a/Tests/UnitTests/GUI/TestGUI.cpp
+++ b/Tests/UnitTests/GUI/TestGUI.cpp
@@ -1,6 +1,7 @@
 #include <QtTest>
 #include <QString>
 #include <QCoreApplication>
+
 #include "TestComboProperty.h"
 #include "TestComponentProxyModel.h"
 #include "TestComponentUtils.h"
diff --git a/Tests/UnitTests/GUI2/CMakeLists.txt b/Tests/UnitTests/GUI2/CMakeLists.txt
index 526bc502f797b2d121261b3506e76f1e40751ce0..39a7ce7d95feedcbfee9c2226fb5ec9b55a7f643 100644
--- a/Tests/UnitTests/GUI2/CMakeLists.txt
+++ b/Tests/UnitTests/GUI2/CMakeLists.txt
@@ -33,7 +33,7 @@ add_executable(${TestName} ${source_files} ${include_files})
 
 target_link_libraries(${TestName} ${BornAgainGUI_LIBRARY} gtest)
 
-qt5_use_modules(${TestName} Widgets Core Gui Designer PrintSupport Network)
+qt5_use_modules(${TestName} Widgets Core Gui Designer PrintSupport Network Test)
 
 # add execution of TestCore just after compilation
 add_custom_target(${TestName}Target ALL DEPENDS TestGUI COMMAND ${TestName})
diff --git a/Tests/UnitTests/GUI2/TestComponentProxyModel.h b/Tests/UnitTests/GUI2/TestComponentProxyModel.h
new file mode 100644
index 0000000000000000000000000000000000000000..d887b20cef81638e59baa4eb2217c2d60eaff8a0
--- /dev/null
+++ b/Tests/UnitTests/GUI2/TestComponentProxyModel.h
@@ -0,0 +1,386 @@
+#include "google_test.h"
+#include "ModelUtils.h"
+#include "SessionModel.h"
+#include "item_constants.h"
+#include "ComponentProxyModel.h"
+#include "VectorItem.h"
+#include "ProxyModelStrategy.h"
+#include "ComponentProxyStrategy.h"
+#include "ParticleItem.h"
+#include "FormFactorItems.h"
+#include "GroupItem.h"
+#include <QSignalSpy>
+#include <QDebug>
+#include "ComboProperty.h"
+#include "test_utils.h"
+
+class TestComponentProxyModel :  public ::testing::Test
+{
+public:
+    ~TestComponentProxyModel();
+};
+
+TestComponentProxyModel::~TestComponentProxyModel() = default;
+
+//! Empty proxy model.
+
+TEST_F(TestComponentProxyModel, test_emptyModel)
+{
+    ComponentProxyModel proxy;
+    EXPECT_EQ(proxy.rowCount(QModelIndex()), 0);
+    EXPECT_EQ(proxy.columnCount(QModelIndex()), static_cast<int>(SessionFlags::MAX_COLUMNS));
+    EXPECT_TRUE(proxy.sourceModel() == nullptr);
+}
+
+//! Set empty model to proxy.
+
+TEST_F(TestComponentProxyModel, test_setModel)
+{
+    SessionModel model("TestModel");
+    ComponentProxyModel proxy;
+
+    QSignalSpy spy(&proxy, &ComponentProxyModel::modelReset);
+    proxy.setSessionModel(&model);
+
+    EXPECT_EQ(spy.count(), 1);
+    EXPECT_EQ(proxy.rowCount(QModelIndex()), 0);
+    EXPECT_EQ(proxy.columnCount(QModelIndex()), static_cast<int>(SessionFlags::MAX_COLUMNS));
+    EXPECT_EQ(proxy.sourceModel(), &model);
+}
+
+//! Set model to proxy. Model already contains simple item.
+
+TEST_F(TestComponentProxyModel, test_setModelWithItem)
+{
+    SessionModel model("TestModel");
+    model.insertNewItem(Constants::PropertyType);
+
+    ComponentProxyModel proxy;
+    proxy.setSessionModel(&model);
+
+    EXPECT_EQ(model.rowCount(QModelIndex()), 1);
+    EXPECT_EQ(model.columnCount(QModelIndex()), static_cast<int>(SessionFlags::MAX_COLUMNS));
+    EXPECT_EQ(proxy.rowCount(QModelIndex()), 1);
+    EXPECT_EQ(proxy.columnCount(QModelIndex()), static_cast<int>(SessionFlags::MAX_COLUMNS));
+}
+
+//! Set model to proxy. Model already contains VectorItem.
+
+TEST_F(TestComponentProxyModel, test_setModelWithVector)
+{
+    const int ncols = static_cast<int>(SessionFlags::MAX_COLUMNS);
+
+    SessionModel model("TestModel");
+    SessionItem* item = model.insertNewItem(Constants::VectorType);
+    item->setItemValue(VectorItem::P_X, 1.0);
+    item->setItemValue(VectorItem::P_Y, 2.0);
+    item->setItemValue(VectorItem::P_Z, 3.0);
+
+    ComponentProxyModel proxy;
+    proxy.setSessionModel(&model);
+
+    // rows, cols of root index
+    EXPECT_EQ(model.rowCount(QModelIndex()), 1);
+    EXPECT_EQ(model.columnCount(QModelIndex()), ncols);
+    EXPECT_EQ(proxy.rowCount(QModelIndex()), 1);
+    EXPECT_EQ(proxy.columnCount(QModelIndex()), ncols);
+
+    // mapFromSource, mapToSource for VectorItem
+    QModelIndex sourceVector = model.index(0, 0, QModelIndex());
+    QModelIndex proxyVector = proxy.index(0, 0, QModelIndex());
+    EXPECT_TRUE(sourceVector != proxyVector);
+    EXPECT_TRUE(sourceVector.internalPointer() == proxyVector.internalPointer());
+    EXPECT_EQ(proxyVector, proxy.mapFromSource(sourceVector));
+    EXPECT_EQ(sourceVector, proxy.mapToSource(proxyVector));
+
+    // rows, cols of VectorItem
+    EXPECT_EQ(model.rowCount(sourceVector), 3); // x,y,z
+    EXPECT_EQ(model.columnCount(sourceVector), ncols);
+    EXPECT_EQ(proxy.rowCount(proxyVector), 3); // x,y,z
+    EXPECT_EQ(proxy.columnCount(proxyVector), ncols);
+
+    // second col for VectorItem
+    QModelIndex sourceVector1 = model.index(0, 1, QModelIndex());
+    QModelIndex proxyVector1 = proxy.index(0, 1, QModelIndex());
+    EXPECT_TRUE(sourceVector1 != proxyVector1);
+    EXPECT_TRUE(sourceVector1.internalPointer() == proxyVector1.internalPointer());
+    EXPECT_EQ(proxyVector1, proxy.mapFromSource(sourceVector1));
+    EXPECT_EQ(sourceVector1, proxy.mapToSource(proxyVector1));
+    EXPECT_EQ(model.rowCount(sourceVector1), 0);
+    EXPECT_EQ(model.columnCount(sourceVector1), 0);
+    EXPECT_EQ(proxy.rowCount(proxyVector1), 0);
+    EXPECT_EQ(proxy.columnCount(proxyVector1), 0);
+
+    // mapFromSource, mapToSource for P_X
+    QModelIndex sourceX = model.index(0, 0, sourceVector);
+    QModelIndex proxyX = proxy.index(0, 0, proxyVector);
+    EXPECT_TRUE(sourceX != proxyX);
+    EXPECT_TRUE(sourceX.internalPointer() == proxyX.internalPointer());
+    EXPECT_EQ(proxyX, proxy.mapFromSource(sourceX));
+    EXPECT_EQ(sourceX, proxy.mapToSource(proxyX));
+    EXPECT_TRUE(model.parent(sourceX) == sourceVector);
+    EXPECT_TRUE(proxy.parent(proxyX) == proxyVector);
+
+    // rows, cols of P_X
+    EXPECT_EQ(model.rowCount(sourceX), 0);
+    EXPECT_EQ(model.columnCount(sourceX), ncols);
+    EXPECT_EQ(proxy.rowCount(proxyX), 0);
+    EXPECT_EQ(proxy.columnCount(proxyX), ncols);
+
+    // second col for P_X
+    QModelIndex sourceX1 = model.index(0, 1, sourceVector);
+    QModelIndex proxyX1 = proxy.index(0, 1, proxyVector);
+    EXPECT_TRUE(sourceX1 != proxyX1);
+    EXPECT_TRUE(sourceX1.internalPointer() == proxyX1.internalPointer());
+    EXPECT_EQ(proxyX1, proxy.mapFromSource(sourceX1));
+    EXPECT_EQ(sourceX1, proxy.mapToSource(proxyX1));
+    EXPECT_EQ(model.rowCount(sourceX1), 0);
+    EXPECT_EQ(model.columnCount(sourceX1), 0);
+    EXPECT_EQ(proxy.rowCount(proxyX1), 0);
+    EXPECT_EQ(proxy.columnCount(proxyX1), 0);
+
+    EXPECT_TRUE(sourceX.sibling(sourceX.row(), 1) == sourceX1);
+    EXPECT_TRUE(proxyX.sibling(proxyX.row(), 1) == proxyX1);
+
+    // mapFromSource, mapToSource for P_Z
+    QModelIndex sourceZ = model.index(2, 0, sourceVector);
+    QModelIndex proxyZ = proxy.index(2, 0, proxyVector);
+    EXPECT_TRUE(sourceZ != proxyZ);
+    EXPECT_TRUE(sourceZ.internalPointer() == proxyZ.internalPointer());
+    EXPECT_EQ(proxyZ, proxy.mapFromSource(sourceZ));
+    EXPECT_EQ(sourceZ, proxy.mapToSource(proxyZ));
+
+    // rows, cols of P_Z
+    EXPECT_EQ(model.rowCount(sourceZ), 0);
+    EXPECT_EQ(model.columnCount(sourceZ), ncols);
+    EXPECT_EQ(proxy.rowCount(proxyZ), 0);
+    EXPECT_EQ(proxy.columnCount(proxyZ), ncols);
+}
+
+//! Set model to proxy. Model already contains two PropertyItems. Checking data() method.
+
+TEST_F(TestComponentProxyModel, test_displayRole)
+{
+    SessionModel model("TestModel");
+    SessionItem* item1 = model.insertNewItem(Constants::PropertyType);
+    item1->setValue(1.0);
+    SessionItem* item2 = model.insertNewItem(Constants::PropertyType);
+    item2->setValue(2.0);
+
+    EXPECT_EQ(model.data(model.index(0, 1, QModelIndex()), Qt::DisplayRole).toDouble(), 1.0);
+    EXPECT_EQ(model.data(model.index(1, 1, QModelIndex()), Qt::DisplayRole).toDouble(), 2.0);
+
+    ComponentProxyModel proxy;
+    proxy.setSessionModel(&model);
+
+    EXPECT_EQ(proxy.data(proxy.index(0, 1, QModelIndex()), Qt::DisplayRole).toDouble(), 1.0);
+    EXPECT_EQ(proxy.data(proxy.index(1, 1, QModelIndex()), Qt::DisplayRole).toDouble(), 2.0);
+}
+
+//! Set model with item to proxy. Changing the data on source and checking change propagation.
+
+TEST_F(TestComponentProxyModel, test_setData)
+{
+    SessionModel model("TestModel");
+    SessionItem* item = model.insertNewItem(Constants::PropertyType);
+    item->setValue(1.0);
+
+    ComponentProxyModel proxy;
+    proxy.setSessionModel(&model);
+
+    // checking initial data
+    EXPECT_EQ(model.data(model.index(0, 1, QModelIndex()), Qt::DisplayRole).toDouble(), 1.0);
+    EXPECT_EQ(proxy.data(proxy.index(0, 1, QModelIndex()), Qt::DisplayRole).toDouble(), 1.0);
+
+    // changing data in source and listening
+    QSignalSpy spySource(&model, &SessionModel::dataChanged);
+    QSignalSpy spyProxy(&proxy, &ComponentProxyModel::dataChanged);
+    EXPECT_TRUE(model.setData(model.index(0, 1, QModelIndex()), 2.0, Qt::DisplayRole));
+    EXPECT_EQ(item->value().toDouble(), 2.0);
+
+    // checking signaling of source
+    EXPECT_EQ(spySource.count(), 1);
+    QList<QVariant> arguments = spySource.takeFirst();
+    EXPECT_EQ(arguments.size(), 3);
+    EXPECT_EQ(arguments[0].toModelIndex(), model.index(0, 0, QModelIndex()));
+    EXPECT_EQ(arguments[1].toModelIndex(), model.index(0, 1, QModelIndex()));
+
+    // checking signaling of proxy
+    EXPECT_EQ(spyProxy.count(), 1);
+    EXPECT_EQ(proxy.data(proxy.index(0, 1, QModelIndex()), Qt::DisplayRole).toDouble(), 2.0);
+
+    // changing data in proxy
+    EXPECT_TRUE(proxy.setData(proxy.index(0, 1, QModelIndex()), 3.0, Qt::DisplayRole));
+    EXPECT_EQ(item->value().toDouble(), 3.0);
+    EXPECT_EQ(spySource.count(), 1); // ?, sould be 2
+    EXPECT_EQ(spyProxy.count(), 2);
+    EXPECT_EQ(model.data(model.index(0, 1, QModelIndex()), Qt::DisplayRole).toDouble(), 3.0);
+    EXPECT_EQ(proxy.data(proxy.index(0, 1, QModelIndex()), Qt::DisplayRole).toDouble(), 3.0);
+}
+
+//! Checks norification of proxy model then source inserts rows.
+
+TEST_F(TestComponentProxyModel, test_insertRows)
+{
+    SessionModel model("TestModel");
+
+    ComponentProxyModel proxy;
+    proxy.setSessionModel(&model);
+
+    EXPECT_TRUE(model.hasChildren(QModelIndex()) == false);
+    EXPECT_TRUE(proxy.hasChildren(QModelIndex()) == false);
+
+    QSignalSpy spyProxy(&proxy, &ComponentProxyModel::layoutChanged);
+
+    // inserting item in the source
+    model.insertNewItem(Constants::PropertyType);
+    EXPECT_EQ(spyProxy.count(), 1);
+    EXPECT_EQ(proxy.rowCount(QModelIndex()), 1);
+}
+
+//! Checking the mapping of ComponentProxyStrategy in the case of ParticleItem inserted in
+//! the source.
+
+TEST_F(TestComponentProxyModel, test_componentStrategy)
+{
+    SessionModel model("TestModel");
+
+    ComponentProxyModel proxy;
+    proxy.setProxyStrategy(new ComponentProxyStrategy);
+    proxy.setSessionModel(&model);
+
+    // inserting particle
+    SessionItem* item = model.insertNewItem(Constants::ParticleType);
+    auto group = dynamic_cast<GroupItem*>(item->getItem(ParticleItem::P_FORM_FACTOR));
+    SessionItem* ffItem = item->getGroupItem(ParticleItem::P_FORM_FACTOR);
+    EXPECT_TRUE(ffItem->parent() == group);
+    EXPECT_TRUE(ffItem->modelType() == Constants::CylinderType);
+
+    // original indices
+    QModelIndex particleIndex = model.indexOfItem(item);
+    QModelIndex groupIndex = model.indexOfItem(group);
+    QModelIndex ffIndex = model.indexOfItem(ffItem);
+    QModelIndex radiusIndex = model.indexOfItem(ffItem->getItem(CylinderItem::P_RADIUS));
+
+    // proxy indices
+    QModelIndex particleProxyIndex = proxy.mapFromSource(particleIndex);
+    EXPECT_TRUE(particleProxyIndex.isValid());
+
+    // Properties of CylinderItem should belong to group property
+    QModelIndex groupProxyIndex = proxy.mapFromSource(groupIndex);
+    EXPECT_TRUE(groupProxyIndex.isValid());
+    EXPECT_TRUE(groupProxyIndex.parent() == particleProxyIndex);
+    EXPECT_EQ(proxy.rowCount(groupProxyIndex), 2); // ff radius and height
+    EXPECT_EQ(proxy.columnCount(groupProxyIndex), 2);
+
+    // CylinderItem shouldn't exist anymore in proxy
+    QModelIndex ffProxyIndex =  proxy.mapFromSource(ffIndex);
+    EXPECT_TRUE(ffProxyIndex.isValid() == false);
+
+    QModelIndex radiusProxyIndex = proxy.mapFromSource(radiusIndex);
+    EXPECT_TRUE(radiusProxyIndex.isValid() == true);
+    EXPECT_TRUE(radiusProxyIndex.parent() == groupProxyIndex);
+}
+
+//! Checking the mapping of ComponentProxyStrategy in the case of ParticleItem inserted in
+//! the source. We are changing Particle's formfactor back and forth and checking for change
+//! in GroupProperty.
+
+TEST_F(TestComponentProxyModel, test_componentStrategyFormFactorChanges)
+{
+    SessionModel model("TestModel");
+
+    ComponentProxyModel proxy;
+    proxy.setProxyStrategy(new ComponentProxyStrategy);
+    proxy.setSessionModel(&model);
+
+    // inserting particle
+    SessionItem* item = model.insertNewItem(Constants::ParticleType);
+    auto group = dynamic_cast<GroupItem*>(item->getItem(ParticleItem::P_FORM_FACTOR));
+    SessionItem* ffItem = item->getGroupItem(ParticleItem::P_FORM_FACTOR);
+    EXPECT_TRUE(ffItem->parent() == group);
+    EXPECT_TRUE(ffItem->modelType() == Constants::CylinderType);
+
+    // changing form factor type
+    group->setCurrentType(Constants::FullSphereType);
+
+    QModelIndex groupProxyIndex = proxy.mapFromSource(model.indexOfItem(group));
+    EXPECT_EQ(proxy.rowCount(groupProxyIndex), 1); // sphere radius
+    EXPECT_EQ(proxy.columnCount(groupProxyIndex), 2);
+
+    // changing back to Cylinder
+    group->setCurrentType(Constants::CylinderType);
+    groupProxyIndex = proxy.mapFromSource(model.indexOfItem(group));
+    EXPECT_EQ(proxy.rowCount(groupProxyIndex), 2); // cylinder radius, length
+    EXPECT_EQ(proxy.columnCount(groupProxyIndex), 2);
+}
+
+//! Checking setRootIndex: proxy model should contain only items corresponding
+//! to rootIndex and its children. Adding simple PropertyItem.
+
+TEST_F(TestComponentProxyModel, test_setRootPropertyItem)
+{
+    const int ncols = static_cast<int>(SessionFlags::MAX_COLUMNS);
+    SessionModel model("TestModel");
+
+    ComponentProxyModel proxy;
+    proxy.setProxyStrategy(new ComponentProxyStrategy);
+    proxy.setSessionModel(&model);
+
+    // inserting simple property item
+    SessionItem* item = model.insertNewItem(Constants::PropertyType);
+    item->setValue(42.0);
+    proxy.setRootIndex(model.indexOfItem(item));
+
+    EXPECT_EQ(model.rowCount(QModelIndex()), 1);
+    EXPECT_EQ(model.columnCount(QModelIndex()), ncols);
+    EXPECT_EQ(proxy.rowCount(QModelIndex()), 1);
+    EXPECT_EQ(proxy.columnCount(QModelIndex()), ncols);
+
+    EXPECT_TRUE(proxy.index(0,0,QModelIndex()) == proxy.mapFromSource(model.index(0,0,QModelIndex())));
+    EXPECT_TRUE(proxy.index(0,1,QModelIndex()) == proxy.mapFromSource(model.index(0,1,QModelIndex())));
+    EXPECT_TRUE(model.index(0,0,QModelIndex()) == proxy.mapToSource(proxy.index(0,0,QModelIndex())));
+    EXPECT_TRUE(proxy.index(0,1,QModelIndex()).isValid());
+    EXPECT_TRUE(model.index(0,1,QModelIndex()) == proxy.mapToSource(proxy.index(0,1,QModelIndex())));
+
+    EXPECT_EQ(model.data(model.index(0, 1, QModelIndex()), Qt::DisplayRole).toDouble(), 42.0);
+    EXPECT_EQ(proxy.data(proxy.index(0, 1, QModelIndex()), Qt::DisplayRole).toDouble(), 42.0);
+}
+
+//! Checking setRootIndex: proxy model should contain only items corresponding
+//! to rootIndex and its children. Adding MultiLayer with two layers and setting rootIndex
+//! to one of the layer.
+
+TEST_F(TestComponentProxyModel, test_setRootIndexLayer)
+{
+
+    SessionModel model("TestModel");
+
+    ComponentProxyModel proxy;
+    proxy.setProxyStrategy(new ComponentProxyStrategy);
+    proxy.setSessionModel(&model);
+
+    // inserting multilayer with two layers
+    auto multilayer = model.insertNewItem(Constants::MultiLayerType);
+    auto layer1 = model.insertNewItem(Constants::LayerType, model.indexOfItem(multilayer));
+    auto layout = model.insertNewItem(Constants::ParticleLayoutType, model.indexOfItem(layer1));
+    model.insertNewItem(Constants::LayerType, model.indexOfItem(multilayer));
+
+    proxy.setRootIndex(model.indexOfItem(layer1));
+    EXPECT_EQ(proxy.rowCount(QModelIndex()), 1);
+    EXPECT_EQ(proxy.columnCount(QModelIndex()), 2);
+
+    QModelIndex multilayerProxyIndex = proxy.mapFromSource(model.indexOfItem(multilayer));
+    EXPECT_TRUE(multilayerProxyIndex.isValid() == false);
+
+    QModelIndex layerProxyIndex = proxy.mapFromSource(model.indexOfItem(layer1));
+    EXPECT_EQ(proxy.rowCount(layerProxyIndex), 4); // thickness, material, slices, roughness
+    EXPECT_EQ(proxy.columnCount(layerProxyIndex), 2);
+    EXPECT_TRUE(layerProxyIndex.isValid());
+    EXPECT_TRUE(layerProxyIndex.parent() == QModelIndex());
+
+    // ParticleLayout should be excluded from proxy tree
+    QModelIndex layoutProxyIndex = proxy.mapFromSource(model.indexOfItem(layout));
+    EXPECT_TRUE(layoutProxyIndex.isValid() == false);
+
+}
diff --git a/Tests/UnitTests/GUI2/TestComponentUtils.h b/Tests/UnitTests/GUI2/TestComponentUtils.h
new file mode 100644
index 0000000000000000000000000000000000000000..24b8fb33d9f852e3b973c2ce67890fc355151bbe
--- /dev/null
+++ b/Tests/UnitTests/GUI2/TestComponentUtils.h
@@ -0,0 +1,61 @@
+#include "google_test.h"
+#include "ComponentUtils.h"
+#include "SessionItem.h"
+#include "SessionModel.h"
+#include "item_constants.h"
+#include "ParticleItem.h"
+#include "FormFactorItems.h"
+#include <QDebug>
+
+class TestComponentUtils :  public ::testing::Test
+{
+public:
+    ~TestComponentUtils();
+};
+
+TestComponentUtils::~TestComponentUtils() = default;
+
+//! Testing component items of particle item.
+
+TEST_F(TestComponentUtils, test_componentItems)
+{
+    SessionModel model("TestModel");
+
+    SessionItem* particle = model.insertNewItem(Constants::ParticleType);
+    SessionItem* group = particle->getItem(ParticleItem::P_FORM_FACTOR);
+    SessionItem* ffItem = particle->getGroupItem(ParticleItem::P_FORM_FACTOR);
+
+    QList<const SessionItem*> expectedList = QList<const SessionItem*> ()
+            << group
+            << ffItem->getItem(CylinderItem::P_RADIUS)
+            << ffItem->getItem(CylinderItem::P_HEIGHT)
+            << particle->getItem(ParticleItem::P_MATERIAL)
+            << particle->getItem(ParticleItem::P_ABUNDANCE)
+            << particle->getItem(ParticleItem::P_POSITION);
+
+    auto itemList = ComponentUtils::componentItems(*particle);
+    EXPECT_EQ(itemList.size(), 6);
+    EXPECT_EQ(itemList, expectedList);
+}
+
+TEST_F(TestComponentUtils, test_componentItemsFFChange)
+{
+    SessionModel model("TestModel");
+
+    SessionItem* particle = model.insertNewItem(Constants::ParticleType);
+    SessionItem* group = particle->getItem(ParticleItem::P_FORM_FACTOR);
+
+    particle->setGroupProperty(ParticleItem::P_FORM_FACTOR, Constants::FullSphereType);
+    SessionItem* sphereItem = particle->getGroupItem(ParticleItem::P_FORM_FACTOR);
+
+    QList<const SessionItem*> expectedList = QList<const SessionItem*> ()
+            << group
+            << sphereItem->getItem(FullSphereItem::P_RADIUS)
+            << particle->getItem(ParticleItem::P_MATERIAL)
+            << particle->getItem(ParticleItem::P_ABUNDANCE)
+            << particle->getItem(ParticleItem::P_POSITION);
+
+    auto itemList = ComponentUtils::componentItems(*particle);
+    EXPECT_EQ(itemList.size(), 5);
+    EXPECT_EQ(itemList, expectedList);
+}
diff --git a/Tests/UnitTests/GUI2/TestDetectorItems.h b/Tests/UnitTests/GUI2/TestDetectorItems.h
new file mode 100644
index 0000000000000000000000000000000000000000..bba96fef2a3011f9dffefdeea5aaa425281fd368
--- /dev/null
+++ b/Tests/UnitTests/GUI2/TestDetectorItems.h
@@ -0,0 +1,58 @@
+#include "google_test.h"
+#include "RectangularDetectorItem.h"
+#include "InstrumentModel.h"
+#include "ComboProperty.h"
+#include "InstrumentItem.h"
+#include "DetectorItems.h"
+#include "ConvolutionDetectorResolution.h"
+#include "ResolutionFunction2DGaussian.h"
+#include "IDetector2D.h"
+#include "Units.h"
+
+class TestDetectorItems :  public ::testing::Test
+{
+public:
+    ~TestDetectorItems();
+};
+
+TestDetectorItems::~TestDetectorItems() = default;
+
+TEST_F(TestDetectorItems, test_detectorAlignment)
+{
+    InstrumentModel model;
+    SessionItem* detector = model.insertNewItem(Constants::RectangularDetectorType);
+
+    ComboProperty alignment
+        = detector->getItemValue(RectangularDetectorItem::P_ALIGNMENT).value<ComboProperty>();
+    // generic has some more items visible
+    alignment.setValue(Constants::ALIGNMENT_GENERIC);
+    detector->setItemValue(RectangularDetectorItem::P_ALIGNMENT,
+                           QVariant::fromValue<ComboProperty>(alignment));
+    EXPECT_TRUE(detector->getItem(RectangularDetectorItem::P_NORMAL)->isVisible());
+
+    // should be disabled if we switch
+    alignment.setValue(Constants::ALIGNMENT_TO_REFLECTED_BEAM);
+    detector->setItemValue(RectangularDetectorItem::P_ALIGNMENT,
+                           QVariant::fromValue<ComboProperty>(alignment));
+    EXPECT_FALSE(detector->getItem(RectangularDetectorItem::P_NORMAL)->isVisible());
+}
+
+TEST_F(TestDetectorItems, test_resolutionFunction)
+{
+    InstrumentModel model;
+    InstrumentItem* instrument
+        = dynamic_cast<InstrumentItem*>(model.insertNewItem(Constants::InstrumentType));
+
+    DetectorItem* detectorItem = instrument->detectorItem();
+
+    detectorItem->setGroupProperty(DetectorItem::P_RESOLUTION_FUNCTION,
+                                   Constants::ResolutionFunction2DGaussianType);
+
+    auto detector = detectorItem->createDetector();
+    auto convol
+        = dynamic_cast<const ConvolutionDetectorResolution*>(detector->detectorResolution());
+    auto gaussian
+        = dynamic_cast<const ResolutionFunction2DGaussian*>(convol->getResolutionFunction2D());
+
+    EXPECT_EQ(Units::rad2deg(gaussian->getSigmaX()), 0.02);
+}
diff --git a/Tests/UnitTests/GUI2/TestExternalProperty.h b/Tests/UnitTests/GUI2/TestExternalProperty.h
new file mode 100644
index 0000000000000000000000000000000000000000..2d398389827f6a619b7e6876789cf528ab47b190
--- /dev/null
+++ b/Tests/UnitTests/GUI2/TestExternalProperty.h
@@ -0,0 +1,99 @@
+#include "google_test.h"
+#include "ExternalProperty.h"
+#include "test_utils.h"
+
+class TestExternalProperty :  public ::testing::Test
+{
+public:
+    ~TestExternalProperty();
+    ExternalProperty propertyFromXML(const QString& buffer) {
+        return TestUtils::propertyFromXML<ExternalProperty>(buffer);
+    }
+
+};
+
+TestExternalProperty::~TestExternalProperty() = default;
+
+TEST_F(TestExternalProperty, test_initialState)
+{
+    ExternalProperty property;
+    EXPECT_TRUE(property.isValid() == false);
+    EXPECT_TRUE(property.color().isValid() == false);
+    EXPECT_TRUE(property.identifier().isEmpty() == true);
+    EXPECT_TRUE(property.text().isEmpty() == true);
+
+    // changing any property should change state to valid
+    property.setColor(QColor(Qt::red));
+    EXPECT_TRUE(property.color() == QColor(Qt::red));
+    EXPECT_TRUE(property.isValid() == true);
+    property.setColor(QColor());
+    EXPECT_TRUE(property.isValid() == false);
+    property.setText("aaa");
+    EXPECT_TRUE(property.text() == QString("aaa"));
+    EXPECT_TRUE(property.isValid() == true);
+    property.setText(QString());
+    EXPECT_TRUE(property.isValid() == false);
+}
+
+//! Testing equality operators.
+
+TEST_F(TestExternalProperty, test_equalityOperators)
+{
+    ExternalProperty prop1;
+    ExternalProperty prop2;
+
+    EXPECT_TRUE (prop1 == prop2);
+    prop1.setColor(QColor(Qt::red));
+    EXPECT_TRUE (prop1 != prop2);
+    prop2.setColor(QColor(Qt::red));
+    EXPECT_TRUE (prop1 == prop2);
+
+    prop1.setIdentifier("aaa");
+    EXPECT_TRUE (prop1 != prop2);
+    prop2.setIdentifier("aaa");
+    EXPECT_TRUE (prop1 == prop2);
+}
+
+//! Testing equality operators for QVariants based on ExternalProperty.
+//! Comparators should be enabled in main.cpp
+
+TEST_F(TestExternalProperty, test_variantEquality)
+{
+    ExternalProperty prop1;
+    ExternalProperty prop2;
+
+    EXPECT_TRUE(prop1.variant() == prop2.variant());
+    prop1.setIdentifier("aaa");
+    EXPECT_TRUE(prop1.variant() != prop2.variant());
+    prop2.setIdentifier("aaa");
+    EXPECT_TRUE(prop1.variant() == prop2.variant());
+}
+
+TEST_F(TestExternalProperty, test_toXML)
+{
+    QString expected;
+
+    // empty property to XML
+    ExternalProperty property;
+    expected = "<Parameter ParType=\"ExternalProperty\" ParRole=\"0\" Text=\"\" Color=\"\" Identifier=\"\"/>";
+    EXPECT_EQ(TestUtils::propertyToXML(property), expected);
+
+    // from XML to empty property
+    EXPECT_EQ(propertyFromXML(expected).text(), property.text());
+    EXPECT_EQ(propertyFromXML(expected).color(), property.color());
+    EXPECT_EQ(propertyFromXML(expected).identifier(), property.identifier());
+    EXPECT_TRUE(propertyFromXML(expected) == property);
+
+    // initialized property to XML
+    property.setIdentifier("{123456}");
+    property.setText("abc");
+    property.setColor(QColor(Qt::red));
+    expected = "<Parameter ParType=\"ExternalProperty\" ParRole=\"0\" Text=\"abc\" Color=\"#ffff0000\" Identifier=\"{123456}\"/>";
+    EXPECT_EQ(TestUtils::propertyToXML(property), expected);
+
+    // from XML to initialized property
+    EXPECT_EQ(propertyFromXML(expected).identifier(), property.identifier());
+    EXPECT_EQ(propertyFromXML(expected).text(), property.text());
+    EXPECT_EQ(propertyFromXML(expected).color(), property.color());
+    EXPECT_TRUE(propertyFromXML(expected) == property);
+}
diff --git a/Tests/UnitTests/GUI2/TestFTDistributionItems.h b/Tests/UnitTests/GUI2/TestFTDistributionItems.h
new file mode 100644
index 0000000000000000000000000000000000000000..df8ea8283ed09df396502e32c28f6718ebc8d597
--- /dev/null
+++ b/Tests/UnitTests/GUI2/TestFTDistributionItems.h
@@ -0,0 +1,27 @@
+#include "google_test.h"
+#include "FTDistributions1D.h"
+#include "FTDistributionItems.h"
+
+class TestFTDistributionItems :  public ::testing::Test
+{
+public:
+    ~TestFTDistributionItems();
+};
+
+TestFTDistributionItems::~TestFTDistributionItems() = default;
+
+TEST_F(TestFTDistributionItems, test_FTDistribution1DCauchy)
+{
+    // to domain
+    FTDistribution1DCauchyItem item;
+    item.setItemValue(FTDistribution1DItem::P_OMEGA, 2.0);
+    auto pdf = item.createFTDistribution();
+    const FTDistribution1DCauchy* cauchy = dynamic_cast<FTDistribution1DCauchy*>(pdf.get());
+    EXPECT_EQ(cauchy->omega(), 2.0);
+
+    // from domain
+    FTDistribution1DCauchy pdf2(3.0);
+    FTDistribution1DCauchyItem item2;
+    item2.setItemValue(FTDistribution1DGaussItem::P_OMEGA, pdf2.omega());
+    EXPECT_EQ(item2.getItemValue(FTDistribution1DGaussItem::P_OMEGA), 3.0);
+}
diff --git a/Tests/UnitTests/GUI2/TestFitParameterModel.h b/Tests/UnitTests/GUI2/TestFitParameterModel.h
new file mode 100644
index 0000000000000000000000000000000000000000..333fd61bf84841c53ba7e3edd9a7bb868051cf05
--- /dev/null
+++ b/Tests/UnitTests/GUI2/TestFitParameterModel.h
@@ -0,0 +1,240 @@
+#include "google_test.h"
+#include "JobModel.h"
+#include "FitParameterProxyModel.h"
+#include "FitParameterItems.h"
+#include "FitSuiteItem.h"
+
+class TestFitParameterModel :  public ::testing::Test
+{
+public:
+    ~TestFitParameterModel();
+};
+
+TestFitParameterModel::~TestFitParameterModel() = default;
+
+TEST_F(TestFitParameterModel, test_InitialState)
+{
+    JobModel source;
+    SessionItem* fitSuiteItem = source.insertNewItem(Constants::FitSuiteType);
+    SessionItem* container
+        = source.insertNewItem(Constants::FitParameterContainerType, fitSuiteItem->index(), -1,
+                               FitSuiteItem::T_FIT_PARAMETERS);
+    FitParameterProxyModel proxy(dynamic_cast<FitParameterContainerItem*>(container));
+
+    EXPECT_EQ(0, proxy.rowCount(QModelIndex()));
+    EXPECT_EQ(static_cast<int>(FitParameterProxyModel::MAX_COLUMNS),
+              proxy.columnCount(QModelIndex()));
+    EXPECT_EQ(container, proxy.itemForIndex(QModelIndex()));
+}
+
+TEST_F(TestFitParameterModel, test_addFitParameter)
+{
+    JobModel source;
+    SessionItem* fitSuiteItem = source.insertNewItem(Constants::FitSuiteType);
+    SessionItem* container
+        = source.insertNewItem(Constants::FitParameterContainerType, fitSuiteItem->index(), -1,
+                               FitSuiteItem::T_FIT_PARAMETERS);
+    FitParameterProxyModel proxy(dynamic_cast<FitParameterContainerItem*>(container));
+
+    // adding fit parameter
+    SessionItem* fitPar0 = source.insertNewItem(Constants::FitParameterType, container->index());
+    fitPar0->setDisplayName(QStringLiteral("par"));
+    fitPar0->setItemValue(FitParameterItem::P_MIN, 1.0);
+    fitPar0->setItemValue(FitParameterItem::P_MAX, 2.0);
+    fitPar0->setItemValue(FitParameterItem::P_START_VALUE, 3.0);
+
+    // checking index of root
+    EXPECT_EQ(1, proxy.rowCount(QModelIndex()));
+    EXPECT_EQ(FitParameterProxyModel::MAX_COLUMNS, proxy.columnCount(QModelIndex()));
+
+    // accessing item at col=0 (original FitParameterItem)
+    QModelIndex index = proxy.index(0, 0, QModelIndex());
+    EXPECT_EQ(index.row(), 0);
+    EXPECT_EQ(index.column(), 0);
+    EXPECT_EQ(proxy.rowCount(index), 0);
+    EXPECT_EQ(proxy.columnCount(index), 0); // non existing linkItem
+
+    EXPECT_EQ(fitPar0, proxy.itemForIndex(index));
+    EXPECT_EQ(fitPar0->displayName(), proxy.data(index).toString());
+    EXPECT_EQ(index, proxy.indexOfItem(fitPar0));
+
+    // accessing item at col=2
+    index = proxy.index(0, FitParameterProxyModel::PAR_MIN, QModelIndex());
+    EXPECT_EQ(index.row(), 0);
+    EXPECT_EQ(index.column(), FitParameterProxyModel::PAR_MIN);
+    EXPECT_EQ(proxy.rowCount(index), 0);
+    EXPECT_EQ(proxy.columnCount(index), 0);
+
+    EXPECT_EQ(fitPar0->getItem(FitParameterItem::P_MIN), proxy.itemForIndex(index));
+    EXPECT_EQ(fitPar0->getItemValue(FitParameterItem::P_MIN).toDouble(),
+              proxy.data(index).toDouble());
+    EXPECT_EQ(index, proxy.indexOfItem(fitPar0->getItem(FitParameterItem::P_MIN)));
+
+    // accessing item at col=3
+    index = proxy.index(0, FitParameterProxyModel::PAR_VALUE, QModelIndex());
+    EXPECT_EQ(index.row(), 0);
+    EXPECT_EQ(index.column(), FitParameterProxyModel::PAR_VALUE);
+    EXPECT_EQ(proxy.rowCount(index), 0);
+    EXPECT_EQ(proxy.columnCount(index), 0);
+
+    EXPECT_EQ(fitPar0->getItem(FitParameterItem::P_START_VALUE), proxy.itemForIndex(index));
+    EXPECT_EQ(fitPar0->getItemValue(FitParameterItem::P_START_VALUE).toDouble(),
+              proxy.data(index).toDouble());
+    EXPECT_EQ(index, proxy.indexOfItem(fitPar0->getItem(FitParameterItem::P_START_VALUE)));
+
+    // accessing item at col=4
+    index = proxy.index(0, FitParameterProxyModel::PAR_MAX, QModelIndex());
+    EXPECT_EQ(index.row(), 0);
+    EXPECT_EQ(index.column(), FitParameterProxyModel::PAR_MAX);
+    EXPECT_EQ(proxy.rowCount(index), 0);
+    EXPECT_EQ(proxy.columnCount(index), 0);
+
+    EXPECT_EQ(fitPar0->getItem(FitParameterItem::P_MAX), proxy.itemForIndex(index));
+    EXPECT_EQ(fitPar0->getItemValue(FitParameterItem::P_MAX).toDouble(),
+              proxy.data(index).toDouble());
+    EXPECT_EQ(index, proxy.indexOfItem(fitPar0->getItem(FitParameterItem::P_MAX)));
+
+    // ----------------------------------------------------
+    // adding second fit parameter
+    // ----------------------------------------------------
+    SessionItem* fitPar1 = source.insertNewItem(Constants::FitParameterType, container->index());
+    fitPar0->setDisplayName(QStringLiteral("par"));
+    fitPar0->setItemValue(FitParameterItem::P_MIN, 10.0);
+    fitPar0->setItemValue(FitParameterItem::P_MAX, 20.0);
+    fitPar0->setItemValue(FitParameterItem::P_START_VALUE, 30.0);
+
+    // checking index of root
+    EXPECT_EQ(2, proxy.rowCount(QModelIndex()));
+    EXPECT_EQ(FitParameterProxyModel::MAX_COLUMNS, proxy.columnCount(QModelIndex()));
+
+    // accessing item at col=3 for fitPar0
+    index = proxy.index(0, FitParameterProxyModel::PAR_VALUE, QModelIndex());
+    EXPECT_EQ(index.row(), 0);
+    EXPECT_EQ(index.column(), FitParameterProxyModel::PAR_VALUE);
+    EXPECT_EQ(proxy.rowCount(index), 0);
+    EXPECT_EQ(proxy.columnCount(index), 0);
+
+    EXPECT_EQ(fitPar0->getItem(FitParameterItem::P_START_VALUE), proxy.itemForIndex(index));
+    EXPECT_EQ(fitPar0->getItemValue(FitParameterItem::P_START_VALUE).toDouble(),
+              proxy.data(index).toDouble());
+    EXPECT_EQ(index, proxy.indexOfItem(fitPar0->getItem(FitParameterItem::P_START_VALUE)));
+
+    // accessing item at col=3 for fitPar1
+    index = proxy.index(1, FitParameterProxyModel::PAR_VALUE, QModelIndex());
+    EXPECT_EQ(index.row(), 1);
+    EXPECT_EQ(index.column(), FitParameterProxyModel::PAR_VALUE);
+    EXPECT_EQ(proxy.rowCount(index), 0);
+    EXPECT_EQ(proxy.columnCount(index), 0);
+
+    EXPECT_EQ(fitPar1->getItem(FitParameterItem::P_START_VALUE), proxy.itemForIndex(index));
+    EXPECT_EQ(fitPar1->getItemValue(FitParameterItem::P_START_VALUE).toDouble(),
+              proxy.data(index).toDouble());
+    EXPECT_EQ(index, proxy.indexOfItem(fitPar1->getItem(FitParameterItem::P_START_VALUE)));
+}
+
+TEST_F(TestFitParameterModel, test_addFitParameterAndLink)
+{
+    JobModel source;
+    SessionItem* fitSuiteItem = source.insertNewItem(Constants::FitSuiteType);
+    SessionItem* container
+        = source.insertNewItem(Constants::FitParameterContainerType, fitSuiteItem->index(), -1,
+                               FitSuiteItem::T_FIT_PARAMETERS);
+    FitParameterProxyModel proxy(dynamic_cast<FitParameterContainerItem*>(container));
+
+    // adding fit parameter
+    SessionItem* fitPar0 = source.insertNewItem(Constants::FitParameterType, container->index());
+    fitPar0->setDisplayName(QStringLiteral("par"));
+    fitPar0->setItemValue(FitParameterItem::P_MIN, 1.0);
+    fitPar0->setItemValue(FitParameterItem::P_MAX, 2.0);
+    fitPar0->setItemValue(FitParameterItem::P_START_VALUE, 3.0);
+
+    // adding link
+    SessionItem* link0 = source.insertNewItem(Constants::FitParameterLinkType, fitPar0->index());
+    link0->setItemValue(FitParameterLinkItem::P_LINK, "link0");
+
+    // checking index of root
+    EXPECT_EQ(1, proxy.rowCount(QModelIndex()));
+    EXPECT_EQ(FitParameterProxyModel::MAX_COLUMNS, proxy.columnCount(QModelIndex()));
+
+    // accessing item at col=0 (original FitParameterItem)
+    QModelIndex index = proxy.index(0, 0, QModelIndex());
+    EXPECT_EQ(index.row(), 0);
+    EXPECT_EQ(index.column(), 0);
+    EXPECT_EQ(proxy.rowCount(index), 1);
+    EXPECT_EQ(proxy.columnCount(index), 1); // linkItem
+
+    // testing link0 index
+    QModelIndex linkIndex = proxy.index(0, 0, index);
+    EXPECT_EQ(linkIndex.row(), 0);
+    EXPECT_EQ(linkIndex.column(), 0);
+    EXPECT_EQ(linkIndex.parent(), index);
+    EXPECT_EQ(proxy.rowCount(linkIndex), 0);
+    EXPECT_EQ(proxy.columnCount(linkIndex), 0);
+
+    EXPECT_EQ(proxy.parent(linkIndex), index);
+    EXPECT_EQ(proxy.itemForIndex(linkIndex), link0->getItem(FitParameterLinkItem::P_LINK));
+
+    EXPECT_EQ(link0->getItemValue(FitParameterLinkItem::P_LINK).toString(),
+              proxy.data(linkIndex).toString());
+    EXPECT_EQ(linkIndex, proxy.indexOfItem(link0->getItem(FitParameterLinkItem::P_LINK)));
+
+    // adding second link
+    SessionItem* link1 = source.insertNewItem(Constants::FitParameterLinkType, fitPar0->index());
+    link1->setItemValue(FitParameterLinkItem::P_LINK, "link1");
+    EXPECT_EQ(proxy.rowCount(index), 2);
+    EXPECT_EQ(proxy.columnCount(index), 1); // linkItem
+
+    linkIndex = proxy.index(1, 0, index);
+    EXPECT_EQ(linkIndex.row(), 1);
+    EXPECT_EQ(linkIndex.column(), 0);
+    EXPECT_EQ(linkIndex.parent(), index);
+    EXPECT_EQ(proxy.rowCount(linkIndex), 0);
+    EXPECT_EQ(proxy.columnCount(linkIndex), 0);
+    EXPECT_EQ(proxy.parent(linkIndex), index);
+
+    EXPECT_EQ(proxy.parent(linkIndex), index);
+    EXPECT_EQ(proxy.itemForIndex(linkIndex), link1->getItem(FitParameterLinkItem::P_LINK));
+}
+
+TEST_F(TestFitParameterModel, test_addTwoFitParameterAndLinks)
+{
+    JobModel source;
+    SessionItem* fitSuiteItem = source.insertNewItem(Constants::FitSuiteType);
+    SessionItem* container
+        = source.insertNewItem(Constants::FitParameterContainerType, fitSuiteItem->index(), -1,
+                               FitSuiteItem::T_FIT_PARAMETERS);
+    FitParameterProxyModel proxy(dynamic_cast<FitParameterContainerItem*>(container));
+
+    // adding fit parameters
+    SessionItem* fitPar0 = source.insertNewItem(Constants::FitParameterType, container->index());
+    SessionItem* link0 = source.insertNewItem(Constants::FitParameterLinkType, fitPar0->index());
+    Q_UNUSED(link0);
+
+    SessionItem* fitPar1 = source.insertNewItem(Constants::FitParameterType, container->index());
+    SessionItem* link1 = source.insertNewItem(Constants::FitParameterLinkType, fitPar1->index());
+    Q_UNUSED(link1);
+
+    // checking index of root
+    EXPECT_EQ(2, proxy.rowCount(QModelIndex()));
+    EXPECT_EQ(FitParameterProxyModel::MAX_COLUMNS, proxy.columnCount(QModelIndex()));
+
+    // accessing fitPar1
+    QModelIndex index1 = proxy.index(1, 0, QModelIndex());
+    EXPECT_EQ(index1.row(), 1);
+    EXPECT_EQ(index1.column(), 0);
+    EXPECT_EQ(index1.parent(), QModelIndex());
+    EXPECT_EQ(proxy.rowCount(index1), 1);
+    EXPECT_EQ(proxy.columnCount(index1), 1);
+
+    EXPECT_EQ(fitPar1, proxy.itemForIndex(index1));
+    EXPECT_EQ(fitPar1->displayName(), proxy.data(index1).toString());
+    EXPECT_EQ(index1, proxy.indexOfItem(fitPar1));
+
+    // accessing link1
+    QModelIndex linkIndex1 = proxy.index(0, 0, index1);
+    EXPECT_EQ(linkIndex1.row(), 0);
+    EXPECT_EQ(linkIndex1.column(), 0);
+    EXPECT_EQ(linkIndex1.parent(), index1);
+    EXPECT_EQ(proxy.rowCount(linkIndex1), 0);
+    EXPECT_EQ(proxy.columnCount(linkIndex1), 0);
+}
diff --git a/Tests/UnitTests/GUI2/TestFormFactorItems.h b/Tests/UnitTests/GUI2/TestFormFactorItems.h
new file mode 100644
index 0000000000000000000000000000000000000000..8218fa5a0914702156f17a932e78dba95efffff7
--- /dev/null
+++ b/Tests/UnitTests/GUI2/TestFormFactorItems.h
@@ -0,0 +1,30 @@
+#include "google_test.h"
+#include "FormFactors.h"
+#include "FormFactorItems.h"
+#include "Units.h"
+#include "Numeric.h"
+
+class TestFormFactorItems :  public ::testing::Test
+{
+public:
+    ~TestFormFactorItems();
+};
+
+TestFormFactorItems::~TestFormFactorItems() = default;
+
+TEST_F(TestFormFactorItems, test_AnisoPyramidItem)
+{
+    // to domain
+    AnisoPyramidItem item;
+    item.setItemValue(AnisoPyramidItem::P_LENGTH, 20.0);
+    item.setItemValue(AnisoPyramidItem::P_WIDTH, 16.0);
+    item.setItemValue(AnisoPyramidItem::P_HEIGHT, 13.0);
+    item.setItemValue(AnisoPyramidItem::P_ALPHA, 60.0);
+    auto P_ff = item.createFormFactor();
+    FormFactorAnisoPyramid* p_ff = dynamic_cast<FormFactorAnisoPyramid*>(P_ff.get());
+    EXPECT_TRUE(p_ff);
+    EXPECT_EQ(p_ff->getLength(), 20.0);
+    EXPECT_EQ(p_ff->getWidth(), 16.0);
+    EXPECT_EQ(p_ff->getHeight(), 13.0);
+    EXPECT_TRUE(Numeric::areAlmostEqual(p_ff->getAlpha(), Units::deg2rad(60.0)));
+}
diff --git a/Tests/UnitTests/GUI2/TestGUI.cpp b/Tests/UnitTests/GUI2/TestGUI.cpp
index e7c7f4e98f054ba036a6e01db6af16ba8d8ee194..67a9f454d18df18403dcc9d5ab6b3dd6b2f17453 100644
--- a/Tests/UnitTests/GUI2/TestGUI.cpp
+++ b/Tests/UnitTests/GUI2/TestGUI.cpp
@@ -2,14 +2,53 @@
 #include <QString>
 #include <QCoreApplication>
 #include "ComboProperty.h"
+
 #include "TestComboProperty.h"
-#include <memory>
+#include "TestComponentProxyModel.h"
+#include "TestComponentUtils.h"
+#include "TestDetectorItems.h"
+#include "TestExternalProperty.h"
+#include "TestFitParameterModel.h"
+#include "TestFormFactorItems.h"
+#include "TestFTDistributionItems.h"
+#include "TestGroupItem.h"
+#include "TestGUICoreObjectCorrespondence.h"
+#include "TestGUIHelpers.h"
+#include "TestIntensityDataItem.h"
+#include "TestLayerItems.h"
+#include "TestLayerRoughnessItems.h"
+#include "TestLinkInstrument.h"
+#include "TestMapperCases.h"
+#include "TestMapperForItem.h"
+#include "TestMaterialModel.h"
+#include "TestMaterialPropertyController.h"
+#include "TestModelUtils.h"
+#include "TestOutputDataIOService.h"
+#include "TestParaCrystalItems.h"
+#include "TestParameterTreeUtils.h"
+#include "TestParticleCoreShell.h"
+#include "TestParticleDistributionItem.h"
+#include "TestParticleItem.h"
+#include "TestProjectDocument.h"
+#include "TestProjectUtils.h"
+#include "TestPropertyRepeater.h"
+#include "TestProxyModelStrategy.h"
+#include "TestSaveService.h"
+#include "TestSessionItemController.h"
+#include "TestSessionItem.h"
+#include "TestSessionItemUtils.h"
+#include "TestSessionModel.h"
+#include "TestSessionXML.h"
+#include "TestTranslations.h"
+#include "TestUpdateTimer.h"
 
 int main(int argc, char** argv) {
     QCoreApplication app(argc, argv);
     Q_UNUSED(app);
 
     QMetaType::registerComparators<ComboProperty>();
+    QMetaType::registerComparators<ExternalProperty>();
+    qRegisterMetaType<QAbstractItemModel::LayoutChangeHint>("LayoutChangeHint");
 
     ::testing::InitGoogleTest(&argc, argv);
 
diff --git a/Tests/UnitTests/GUI2/TestGUICoreObjectCorrespondence.h b/Tests/UnitTests/GUI2/TestGUICoreObjectCorrespondence.h
new file mode 100644
index 0000000000000000000000000000000000000000..2edd5d1263acdf8285807235683ac8976447eb3b
--- /dev/null
+++ b/Tests/UnitTests/GUI2/TestGUICoreObjectCorrespondence.h
@@ -0,0 +1,196 @@
+#include "google_test.h"
+#include "FormFactors.h"
+#include "InterferenceFunctions.h"
+#include "FormFactorItems.h"
+#include "InterferenceFunctionItems.h"
+#include "ParameterPool.h"
+#include "Units.h"
+
+class TestGUICoreObjectCorrespondence : public ::testing::Test
+{
+public:
+    ~TestGUICoreObjectCorrespondence();
+
+    void GUICoreObjectCorrespondence(const SessionItem& gui_object,
+                                     const IParameterized& core_object)
+    {
+        // First check if names correspond:
+        EXPECT_EQ(gui_object.displayName(), QString::fromStdString(core_object.getName()));
+
+        auto core_parameter_names = core_object.parameterPool()->parameterNames();
+        for (auto name : core_parameter_names) {
+            QString gui_name = QString::fromStdString(name);
+            EXPECT_TRUE(gui_object.isTag(gui_name));
+        }
+    }
+};
+
+TestGUICoreObjectCorrespondence::~TestGUICoreObjectCorrespondence() = default;
+
+TEST_F(TestGUICoreObjectCorrespondence, test_AnisoPyramid)
+{
+    AnisoPyramidItem gui_anisopyramid;
+    FormFactorAnisoPyramid core_anisopyramid(1.0, 2.0, 0.1, 45.0 * Units::degree);
+    GUICoreObjectCorrespondence(gui_anisopyramid, core_anisopyramid);
+}
+
+TEST_F(TestGUICoreObjectCorrespondence, test_Box)
+{
+    BoxItem gui_box;
+    FormFactorBox core_box(1.0, 1.5, 3.0);
+    GUICoreObjectCorrespondence(gui_box, core_box);
+}
+
+TEST_F(TestGUICoreObjectCorrespondence, test_Cone)
+{
+    ConeItem gui_cone;
+    FormFactorCone core_cone(1.0, 0.2, 45.0 * Units::degree);
+    GUICoreObjectCorrespondence(gui_cone, core_cone);
+}
+
+TEST_F(TestGUICoreObjectCorrespondence, test_Cone6)
+{
+    Cone6Item gui_cone6;
+    FormFactorCone6 core_cone6(1.0, 0.2, 45.0 * Units::degree);
+    GUICoreObjectCorrespondence(gui_cone6, core_cone6);
+}
+
+TEST_F(TestGUICoreObjectCorrespondence, test_Cuboctahedron)
+{
+    CuboctahedronItem gui_cuboctahedron;
+    FormFactorCuboctahedron core_cuboctahedron(1.0, 0.4, 1.0, 45.0 * Units::degree);
+    GUICoreObjectCorrespondence(gui_cuboctahedron, core_cuboctahedron);
+}
+
+TEST_F(TestGUICoreObjectCorrespondence, test_Dodecahedron)
+{
+    DodecahedronItem gui_dodecahedron;
+    FormFactorDodecahedron core_dodecahedron(3.0);
+    GUICoreObjectCorrespondence(gui_dodecahedron, core_dodecahedron);
+}
+
+TEST_F(TestGUICoreObjectCorrespondence, test_Dot)
+{
+    DotItem gui_dot;
+    FormFactorDot core_dot;
+    GUICoreObjectCorrespondence(gui_dot, core_dot);
+}
+
+TEST_F(TestGUICoreObjectCorrespondence, test_Cylinder)
+{
+    CylinderItem gui_cylinder;
+    FormFactorCylinder core_cylinder(1.0, 3.0);
+    GUICoreObjectCorrespondence(gui_cylinder, core_cylinder);
+}
+
+TEST_F(TestGUICoreObjectCorrespondence, test_EllipsoidalCylinder)
+{
+    EllipsoidalCylinderItem gui_ellcylinder;
+    FormFactorEllipsoidalCylinder core_ellcylinder(2.0, 1.0, 1.0);
+    GUICoreObjectCorrespondence(gui_ellcylinder, core_ellcylinder);
+}
+
+TEST_F(TestGUICoreObjectCorrespondence, test_FullSphere)
+{
+    FullSphereItem gui_sphere;
+    FormFactorFullSphere core_sphere(1.0);
+    GUICoreObjectCorrespondence(gui_sphere, core_sphere);
+}
+
+TEST_F(TestGUICoreObjectCorrespondence, test_FullSpheroid)
+{
+    FullSpheroidItem gui_spheroid;
+    FormFactorFullSpheroid core_spheroid(1.0, 2.0);
+    GUICoreObjectCorrespondence(gui_spheroid, core_spheroid);
+}
+
+TEST_F(TestGUICoreObjectCorrespondence, test_HemiEllipsoid)
+{
+    HemiEllipsoidItem gui_hemiellipsoid;
+    FormFactorHemiEllipsoid core_hemiellipsoid(2.0, 1.0, 0.5);
+    GUICoreObjectCorrespondence(gui_hemiellipsoid, core_hemiellipsoid);
+}
+
+TEST_F(TestGUICoreObjectCorrespondence, test_Icosahedron)
+{
+    IcosahedronItem gui_icosahedron;
+    FormFactorIcosahedron core_icosahedron(8.0);
+    GUICoreObjectCorrespondence(gui_icosahedron, core_icosahedron);
+}
+
+TEST_F(TestGUICoreObjectCorrespondence, test_Prism3)
+{
+    Prism3Item gui_prism3;
+    FormFactorPrism3 core_prism3(1.0, 2.0);
+    GUICoreObjectCorrespondence(gui_prism3, core_prism3);
+}
+
+TEST_F(TestGUICoreObjectCorrespondence, test_Prism6)
+{
+    Prism6Item gui_prism6;
+    FormFactorPrism6 core_prism6(1.0, 2.0);
+    GUICoreObjectCorrespondence(gui_prism6, core_prism6);
+}
+
+TEST_F(TestGUICoreObjectCorrespondence, test_Pyramid)
+{
+    PyramidItem gui_pyramid;
+    FormFactorPyramid core_pyramid(1.0, 0.2, 45.0 * Units::degree);
+    GUICoreObjectCorrespondence(gui_pyramid, core_pyramid);
+}
+
+TEST_F(TestGUICoreObjectCorrespondence, test_Ripple1)
+{
+    Ripple1Item gui_ripple1;
+    FormFactorRipple1 core_ripple1(10.0, 2.0, 1.0);
+    GUICoreObjectCorrespondence(gui_ripple1, core_ripple1);
+}
+
+TEST_F(TestGUICoreObjectCorrespondence, test_Ripple2)
+{
+    Ripple2Item gui_ripple2;
+    FormFactorRipple2 core_ripple2(10.0, 2.0, 1.0, 0.1);
+    GUICoreObjectCorrespondence(gui_ripple2, core_ripple2);
+}
+
+TEST_F(TestGUICoreObjectCorrespondence, test_Tetrahedron)
+{
+    TetrahedronItem gui_tetrahedron;
+    FormFactorTetrahedron core_tetrahedron(1.0, 0.1, 45.0 * Units::degree);
+    GUICoreObjectCorrespondence(gui_tetrahedron, core_tetrahedron);
+}
+
+TEST_F(TestGUICoreObjectCorrespondence, test_TruncatedCube)
+{
+    TruncatedCubeItem gui_trunccube;
+    FormFactorTruncatedCube core_trunccube(2.0, 0.2);
+    GUICoreObjectCorrespondence(gui_trunccube, core_trunccube);
+}
+
+TEST_F(TestGUICoreObjectCorrespondence, test_TruncatedSphere)
+{
+    TruncatedSphereItem gui_truncsphere;
+    FormFactorTruncatedSphere core_truncsphere(1.0, 0.5);
+    GUICoreObjectCorrespondence(gui_truncsphere, core_truncsphere);
+}
+
+TEST_F(TestGUICoreObjectCorrespondence, test_TruncatedSpheroid)
+{
+    TruncatedSpheroidItem gui_truncspheroid;
+    FormFactorTruncatedSpheroid core_truncspheroid(1.0, 1.5, 1.5);
+    GUICoreObjectCorrespondence(gui_truncspheroid, core_truncspheroid);
+}
+
+TEST_F(TestGUICoreObjectCorrespondence, test_RadialParacrystal)
+{
+    InterferenceFunctionRadialParaCrystalItem gui_radialparacrystal;
+    InterferenceFunctionRadialParaCrystal core_radialparacrystal(10.0, 1e-6);
+    GUICoreObjectCorrespondence(gui_radialparacrystal, core_radialparacrystal);
+}
+
+TEST_F(TestGUICoreObjectCorrespondence, test_1DLattice)
+{
+    InterferenceFunction1DLatticeItem gui_1d_lattice;
+    InterferenceFunction1DLattice core_1d_lattice(20.0, 0.0);
+    GUICoreObjectCorrespondence(gui_1d_lattice, core_1d_lattice);
+}
diff --git a/Tests/UnitTests/GUI2/TestGUIHelpers.h b/Tests/UnitTests/GUI2/TestGUIHelpers.h
new file mode 100644
index 0000000000000000000000000000000000000000..bfee368f1038f6b34af1fb5b9f62683fb219fc62
--- /dev/null
+++ b/Tests/UnitTests/GUI2/TestGUIHelpers.h
@@ -0,0 +1,31 @@
+#include "GUIHelpers.h"
+#include "google_test.h"
+
+class TestGUIHelpers : public ::testing::Test
+{
+public:
+    ~TestGUIHelpers();
+};
+
+TestGUIHelpers::~TestGUIHelpers() = default;
+
+TEST_F(TestGUIHelpers, test_VersionString)
+{
+    int vmajor(0), vminor(0), vpatch(0);
+
+    EXPECT_EQ(true, GUIHelpers::parseVersion(QString("1.5.0"), vmajor, vminor, vpatch));
+    EXPECT_EQ(1, vmajor);
+    EXPECT_EQ(5, vminor);
+    EXPECT_EQ(0, vpatch);
+
+    EXPECT_FALSE(GUIHelpers::parseVersion(QString("15.0"), vmajor, vminor, vpatch));
+
+    QString min_version("1.5.0");
+    EXPECT_EQ(GUIHelpers::isVersionMatchMinimal("1.5.0", min_version), true);
+    EXPECT_EQ(GUIHelpers::isVersionMatchMinimal("1.5.1", min_version), true);
+    EXPECT_EQ(GUIHelpers::isVersionMatchMinimal("1.6.0", min_version), true);
+    EXPECT_EQ(GUIHelpers::isVersionMatchMinimal("2.4.9", min_version), true);
+
+    EXPECT_EQ(GUIHelpers::isVersionMatchMinimal("1.4.9", min_version), false);
+    EXPECT_EQ(GUIHelpers::isVersionMatchMinimal("0.6.9", min_version), false);
+}
diff --git a/Tests/UnitTests/GUI2/TestGroupItem.h b/Tests/UnitTests/GUI2/TestGroupItem.h
new file mode 100644
index 0000000000000000000000000000000000000000..f3363ef43a4cf4eced78af9028226d4c0291869b
--- /dev/null
+++ b/Tests/UnitTests/GUI2/TestGroupItem.h
@@ -0,0 +1,114 @@
+#include "google_test.h"
+#include "test_utils.h"
+#include "GroupInfo.h"
+#include "GroupItem.h"
+#include "ComboProperty.h"
+#include "GUIHelpers.h"
+#include "SessionModel.h"
+#include "SessionItemUtils.h"
+
+class TestGroupItem : public ::testing::Test
+{
+public:
+    ~TestGroupItem();
+};
+
+TestGroupItem::~TestGroupItem() = default;
+
+TEST_F(TestGroupItem, test_groupInfo)
+{
+    GroupInfo info("Group");
+    info.add("BBB", "b_label");
+    info.add("AAA", "a_label");
+    info.add("CCC", "c_label");
+    info.setDefaultType("AAA");
+
+    // sorted group (default behavior)
+    EXPECT_EQ(info.groupType(), QString("Group"));
+    EXPECT_EQ(info.defaultType(), QString("AAA"));
+    EXPECT_EQ(info.itemTypes(), QStringList() << "AAA" << "BBB" << "CCC");
+    EXPECT_EQ(info.itemLabels(), QStringList() << "a_label" << "b_label" << "c_label");
+
+    // unsorted group
+    info = GroupInfo("Group2", false);
+    info.add("BBB2", "b_label2");
+    info.add("AAA2", "a_label2");
+    info.add("CCC2", "c_label2");
+    info.setDefaultType("AAA2");
+    EXPECT_EQ(info.defaultType(), QString("AAA2"));
+    EXPECT_EQ(info.itemTypes(), QStringList() << "BBB2" << "AAA2" << "CCC2");
+    EXPECT_EQ(info.itemLabels(), QStringList() << "b_label2" << "a_label2" << "c_label2");
+
+    // attempt to set non-existing default type
+    EXPECT_THROW(info.setDefaultType("XXX"), GUIHelpers::Error);
+
+    // attempt to add same info twice
+    EXPECT_THROW(info.add("CCC2", "c_label2"), GUIHelpers::Error);
+}
+
+TEST_F(TestGroupItem, test_CreateGroup)
+{
+    SessionModel model("TestModel");
+
+    GroupInfo groupInfo = SessionItemUtils::GetGroupInfo(Constants::FormFactorGroup);
+    EXPECT_EQ(groupInfo.defaultType(), Constants::CylinderType);
+
+    auto groupItem = dynamic_cast<GroupItem*>(model.insertNewItem(Constants::GroupItemType));
+    EXPECT_EQ(groupItem->children().size(), 0);
+    EXPECT_TRUE(groupItem->currentItem() == nullptr);
+    EXPECT_FALSE(groupItem->value().isValid());
+
+    // setting group property and checking currentItem
+    groupItem->setGroupInfo(groupInfo);
+
+    // setting group info twice
+    EXPECT_THROW(groupItem->setGroupInfo(groupInfo), GUIHelpers::Error);
+
+    // checking current item
+    EXPECT_EQ(groupItem->children().size(), 1);
+    EXPECT_EQ(groupItem->children()[0], groupItem->currentItem());
+    SessionItem* ffItem = groupItem->currentItem();
+    EXPECT_EQ(ffItem->modelType(), Constants::CylinderType);
+
+    // checking current variant
+    QVariant value = groupItem->value();
+    EXPECT_TRUE(value.canConvert<ComboProperty>() == true);
+    ComboProperty combo = value.value<ComboProperty>();
+    EXPECT_EQ(combo.getValues(), groupInfo.itemLabels());
+    int index = groupInfo.itemTypes().indexOf(groupInfo.defaultType());
+    EXPECT_EQ(combo.currentIndex(), index);
+    EXPECT_EQ(combo.getValue(), groupInfo.itemLabels().at(index));
+
+    // changing current item
+    SessionItem* newItem = groupItem->setCurrentType(Constants::FullSphereType);
+    EXPECT_EQ(newItem, groupItem->currentItem());
+    EXPECT_EQ(newItem->modelType(), Constants::FullSphereType);
+    EXPECT_EQ(groupItem->children().size(), 2);
+
+    // checking current variant
+    combo = groupItem->value().value<ComboProperty>();
+    EXPECT_EQ(combo.getValues(), groupInfo.itemLabels());
+    index = groupInfo.itemTypes().indexOf(Constants::FullSphereType);
+    EXPECT_EQ(combo.currentIndex(), index);
+    EXPECT_EQ(combo.getValue(), groupInfo.itemLabels().at(index));
+
+    // returning back to previous item
+    EXPECT_EQ(groupItem->setCurrentType(Constants::CylinderType), ffItem);
+    EXPECT_EQ(groupItem->currentItem(), ffItem);
+    EXPECT_EQ(groupItem->children().size(), 2);
+}
+
+//! Checking that GroupProperty stays functional if displayName of currentItem is changed.
+
+TEST_F(TestGroupItem, test_groupPropertyWithDisplayNames)
+{
+    GroupInfo groupInfo = SessionItemUtils::GetGroupInfo(Constants::DistributionGroup);
+
+    GroupItem groupItem;
+    groupItem.setGroupInfo(groupInfo);
+
+    SessionItem* cosineItem = groupItem.currentItem();
+    cosineItem->setDisplayName(Constants::DistributionCosineType + QString::number(0));
+
+    EXPECT_EQ(groupItem.currentItem(), cosineItem);
+}
diff --git a/Tests/UnitTests/GUI2/TestIntensityDataItem.h b/Tests/UnitTests/GUI2/TestIntensityDataItem.h
new file mode 100644
index 0000000000000000000000000000000000000000..a707619e4013352a48939c53482833fd4988b5b2
--- /dev/null
+++ b/Tests/UnitTests/GUI2/TestIntensityDataItem.h
@@ -0,0 +1,38 @@
+#include "google_test.h"
+#include "SessionModel.h"
+#include "IntensityDataItem.h"
+#include <QTest>
+
+class TestIntensityDataItem : public ::testing::Test
+{
+public:
+    ~TestIntensityDataItem();
+};
+
+TestIntensityDataItem::~TestIntensityDataItem() = default;
+
+TEST_F(TestIntensityDataItem, test_lastModified)
+{
+    SessionModel model("TempModel");
+    IntensityDataItem* item
+        = dynamic_cast<IntensityDataItem*>(model.insertNewItem(Constants::IntensityDataType));
+
+    QDateTime time = QDateTime::currentDateTime();
+    item->setLastModified(time);
+    EXPECT_EQ(time, item->lastModified());
+
+    const int nap_time(20);
+    QTest::qSleep(nap_time);
+
+    // changing item (file name)
+    item->setItemValue(IntensityDataItem::P_FILE_NAME, "name.txt");
+    QDateTime time2 = item->lastModified();
+    EXPECT_TRUE(time.msecsTo(time2) > nap_time / 2);
+
+    QTest::qSleep(nap_time);
+
+    // changing item (OutputData)
+    item->emitDataChanged();
+    QDateTime time3 = item->lastModified();
+    EXPECT_TRUE(time2.msecsTo(time3) > nap_time / 2);
+}
diff --git a/Tests/UnitTests/GUI2/TestLayerItems.h b/Tests/UnitTests/GUI2/TestLayerItems.h
new file mode 100644
index 0000000000000000000000000000000000000000..8aaa60cae6bd57516cb763dd9ffdd93d03109a34
--- /dev/null
+++ b/Tests/UnitTests/GUI2/TestLayerItems.h
@@ -0,0 +1,32 @@
+#include "google_test.h"
+#include "ApplicationModels.h"
+#include "ExternalProperty.h"
+#include "LayerItem.h"
+#include "MaterialItem.h"
+#include "MaterialModel.h"
+#include "ModelMapper.h"
+#include "SampleModel.h"
+
+class TestLayerItems : public ::testing::Test
+{
+public:
+    ~TestLayerItems();
+};
+
+TestLayerItems::~TestLayerItems() = default;
+
+//! Checking default material of the layer.
+
+TEST_F(TestLayerItems, test_LayerDefaultMaterial)
+{
+    ApplicationModels models;
+    auto layer = models.sampleModel()->insertNewItem(Constants::LayerType);
+    auto materials = models.materialModel()->topItems();
+    auto defMaterial = materials.front();
+
+    ExternalProperty material
+        = layer->getItemValue(LayerItem::P_MATERIAL).value<ExternalProperty>();
+    EXPECT_EQ(material.text(), QString("Default"));
+    EXPECT_EQ(material.identifier(),
+              defMaterial->getItemValue(MaterialItem::P_IDENTIFIER).toString());
+}
diff --git a/Tests/UnitTests/GUI2/TestLayerRoughnessItems.h b/Tests/UnitTests/GUI2/TestLayerRoughnessItems.h
new file mode 100644
index 0000000000000000000000000000000000000000..b00b56e94aeda43e5b84bbd1e2eb6a32e349fef6
--- /dev/null
+++ b/Tests/UnitTests/GUI2/TestLayerRoughnessItems.h
@@ -0,0 +1,47 @@
+#include "google_test.h"
+#include "LayerRoughness.h"
+#include "LayerRoughnessItems.h"
+#include "TransformToDomain.h"
+#include "TransformFromDomain.h"
+
+class TestLayerRoughnessItems : public ::testing::Test
+{
+public:
+    ~TestLayerRoughnessItems();
+};
+
+TestLayerRoughnessItems::~TestLayerRoughnessItems() = default;
+
+TEST_F(TestLayerRoughnessItems, test_LayerRoughnessToDomain)
+{
+    LayerBasicRoughnessItem roughnessItem;
+    roughnessItem.setItemValue(LayerBasicRoughnessItem::P_SIGMA, 10.0);
+    roughnessItem.setItemValue(LayerBasicRoughnessItem::P_HURST, 20.0);
+    roughnessItem.setItemValue(LayerBasicRoughnessItem::P_LATERAL_CORR_LENGTH, 30.0);
+
+    auto P_roughness = TransformToDomain::createLayerRoughness(roughnessItem);
+    EXPECT_EQ(P_roughness->getSigma(),
+              roughnessItem.getItemValue(LayerBasicRoughnessItem::P_SIGMA).toDouble());
+    EXPECT_EQ(P_roughness->getHurstParameter(),
+              roughnessItem.getItemValue(LayerBasicRoughnessItem::P_HURST).toDouble());
+    EXPECT_EQ(
+        P_roughness->getLatteralCorrLength(),
+        roughnessItem.getItemValue(LayerBasicRoughnessItem::P_LATERAL_CORR_LENGTH).toDouble());
+
+    LayerZeroRoughnessItem zeroRoughnessItem;
+    EXPECT_TRUE(TransformToDomain::createLayerRoughness(zeroRoughnessItem) == nullptr);
+}
+
+TEST_F(TestLayerRoughnessItems, test_LayerRoughnessFromDomain)
+{
+    LayerRoughness roughness(10.0, 20.0, 30.0);
+    LayerBasicRoughnessItem roughnessItem;
+    TransformFromDomain::setItemFromSample(&roughnessItem, &roughness);
+    EXPECT_EQ(roughness.getSigma(),
+              roughnessItem.getItemValue(LayerBasicRoughnessItem::P_SIGMA).toDouble());
+    EXPECT_EQ(roughness.getHurstParameter(),
+              roughnessItem.getItemValue(LayerBasicRoughnessItem::P_HURST).toDouble());
+    EXPECT_EQ(
+        roughness.getLatteralCorrLength(),
+        roughnessItem.getItemValue(LayerBasicRoughnessItem::P_LATERAL_CORR_LENGTH).toDouble());
+}
diff --git a/Tests/UnitTests/GUI2/TestLinkInstrument.h b/Tests/UnitTests/GUI2/TestLinkInstrument.h
new file mode 100644
index 0000000000000000000000000000000000000000..69c7077e3198b8ec92e24c8e0b09a79c65dc915f
--- /dev/null
+++ b/Tests/UnitTests/GUI2/TestLinkInstrument.h
@@ -0,0 +1,89 @@
+#include "google_test.h"
+#include "AxesItems.h"
+#include "InstrumentItem.h"
+#include "InstrumentModel.h"
+#include "IntensityDataItem.h"
+#include "JobItemUtils.h"
+#include "LinkInstrumentManager.h"
+#include "RealDataItem.h"
+#include "RealDataModel.h"
+#include "RectangularDetectorItem.h"
+#include <QSignalSpy>
+#include <QTest>
+
+class TestLinkInstrument : public ::testing::Test
+{
+public:
+    ~TestLinkInstrument();
+};
+
+TestLinkInstrument::~TestLinkInstrument() = default;
+
+//! Checks that LinkInstrumentManager listens instrument model.
+
+TEST_F(TestLinkInstrument, test_linkInstrumentManager)
+{
+    InstrumentModel instrumentModel;
+    RealDataModel realDataModel;
+    LinkInstrumentManager manager;
+    manager.setModels(&instrumentModel, &realDataModel);
+
+    // initial state of LinkInstrumentManager
+    EXPECT_EQ(manager.instrumentNames(), QStringList() << "Undefined");
+    QSignalSpy spy(&manager, SIGNAL(instrumentMapUpdated()));
+
+    // populating instrument model
+    InstrumentItem* instrument
+        = dynamic_cast<InstrumentItem*>(instrumentModel.insertNewItem(Constants::InstrumentType));
+    QString identifier = instrument->getItemValue(InstrumentItem::P_IDENTIFIER).toString();
+
+    // checking that LinkInstrumentManager was notified about new instrument
+    EXPECT_EQ(spy.count(), 1);
+    EXPECT_EQ(manager.instrumentNames(), QStringList() << "Undefined" << instrument->itemName());
+
+    EXPECT_EQ(manager.getInstrument(identifier), instrument);
+    EXPECT_EQ(manager.instrumentComboIndex(identifier), 1);
+
+    // removing instrument
+    instrumentModel.removeRow(0);
+    EXPECT_EQ(spy.count(), 2);
+    EXPECT_EQ(manager.instrumentNames(), QStringList() << "Undefined");
+    QVERIFY(manager.getInstrument(identifier) == nullptr);
+    EXPECT_EQ(manager.instrumentComboIndex(identifier), -1);
+}
+
+TEST_F(TestLinkInstrument, test_canLinkToInstrument)
+{
+    InstrumentModel instrumentModel;
+    RealDataModel realDataModel;
+    LinkInstrumentManager manager;
+    manager.setModels(&instrumentModel, &realDataModel);
+
+    // populating instrument model
+    InstrumentItem* instrument
+        = dynamic_cast<InstrumentItem*>(instrumentModel.insertNewItem(Constants::InstrumentType));
+    QString identifier = instrument->getItemValue(InstrumentItem::P_IDENTIFIER).toString();
+
+    // populating real data model, setting intensity data
+    RealDataItem* realData
+        = dynamic_cast<RealDataItem*>(realDataModel.insertNewItem(Constants::RealDataType));
+    JobItemUtils::createDefaultDetectorMap(realData->intensityDataItem(), instrument);
+
+    QVERIFY(manager.canLinkDataToInstrument(realData, identifier));
+
+    // making link
+    realData->setItemValue(RealDataItem::P_INSTRUMENT_ID, identifier);
+    EXPECT_EQ(manager.linkedItems(instrument), QList<RealDataItem*>() << realData);
+
+    // changing detector type and checking that link remain
+    instrument->setDetectorGroup(Constants::RectangularDetectorType);
+    EXPECT_EQ(manager.linkedItems(instrument), QList<RealDataItem*>() << realData);
+
+    // changing detector binning and checking that link is destroyed
+    DetectorItem* detectorItem = instrument->detectorItem();
+    auto& x_axis = detectorItem->item<BasicAxisItem>(RectangularDetectorItem::P_X_AXIS);
+    x_axis.setItemValue(BasicAxisItem::P_NBINS, 10);
+
+    EXPECT_EQ(manager.linkedItems(instrument), QList<RealDataItem*>());
+    EXPECT_EQ(realData->getItemValue(RealDataItem::P_INSTRUMENT_ID).toString(), QString());
+}
diff --git a/Tests/UnitTests/GUI2/TestMapperCases.h b/Tests/UnitTests/GUI2/TestMapperCases.h
new file mode 100644
index 0000000000000000000000000000000000000000..b1120c9e92c7f2b38c8ebb40186175b6142a218b
--- /dev/null
+++ b/Tests/UnitTests/GUI2/TestMapperCases.h
@@ -0,0 +1,60 @@
+#include "google_test.h"
+#include "ComboProperty.h"
+#include "DocumentModel.h"
+#include "ParticleItem.h"
+#include "ParticleLayoutItem.h"
+#include "SampleModel.h"
+#include "SessionItemUtils.h"
+#include "SimulationOptionsItem.h"
+#include <QtTest>
+
+using SessionItemUtils::ParentRow;
+
+class TestMapperCases : public ::testing::Test
+{
+public:
+    ~TestMapperCases();
+};
+
+TestMapperCases::~TestMapperCases() = default;
+
+TEST_F(TestMapperCases, test_ParticeleCompositionUpdate)
+{
+    SampleModel model;
+    SessionItem* multilayer = model.insertNewItem(Constants::MultiLayerType);
+    SessionItem* layer = model.insertNewItem(Constants::LayerType, multilayer->index());
+    SessionItem* layout = model.insertNewItem(Constants::ParticleLayoutType, layer->index());
+
+    // composition added to layout should have abundance enabled
+    SessionItem* compositionFree
+        = model.insertNewItem(Constants::ParticleCompositionType, layout->index());
+    EXPECT_TRUE(compositionFree->getItem(ParticleItem::P_ABUNDANCE)->isEnabled());
+
+    // composition added to distribution should have abundance disabled
+    SessionItem* distribution
+        = model.insertNewItem(Constants::ParticleDistributionType, layout->index());
+    SessionItem* composition
+        = model.insertNewItem(Constants::ParticleCompositionType, distribution->index());
+    EXPECT_TRUE(composition->getItem(ParticleItem::P_ABUNDANCE)->isEnabled() == false);
+
+    composition = distribution->takeRow(ParentRow(*composition));
+    EXPECT_TRUE(composition->getItem(ParticleItem::P_ABUNDANCE)->isEnabled());
+    delete composition;
+}
+
+TEST_F(TestMapperCases, test_SimulationOptionsComputationToggle)
+{
+    DocumentModel model;
+    model.insertNewItem(Constants::SimulationOptionsType);
+
+    SimulationOptionsItem* item = model.getSimulationOptionsItem();
+
+    ComboProperty combo
+        = item->getItemValue(SimulationOptionsItem::P_COMPUTATION_METHOD).value<ComboProperty>();
+    EXPECT_EQ(combo.getValue(), Constants::SIMULATION_ANALYTICAL);
+    EXPECT_TRUE(item->getItem(SimulationOptionsItem::P_MC_POINTS)->isEnabled() == false);
+
+    combo.setValue(Constants::SIMULATION_MONTECARLO);
+    item->setItemValue(SimulationOptionsItem::P_COMPUTATION_METHOD, combo.variant());
+    EXPECT_TRUE(item->getItem(SimulationOptionsItem::P_MC_POINTS)->isEnabled() == true);
+}
diff --git a/Tests/UnitTests/GUI2/TestMapperForItem.h b/Tests/UnitTests/GUI2/TestMapperForItem.h
new file mode 100644
index 0000000000000000000000000000000000000000..79376ceedaae6a6ac90acf2904d7d35d60fec8de
--- /dev/null
+++ b/Tests/UnitTests/GUI2/TestMapperForItem.h
@@ -0,0 +1,324 @@
+#include "google_test.h"
+#include "item_constants.h"
+#include "LayerItem.h"
+#include "MultiLayerItem.h"
+#include "SampleModel.h"
+#include "SessionItem.h"
+#include "SessionItemUtils.h"
+#include <memory>
+
+using SessionItemUtils::ParentRow;
+
+//! Test Widget which logs calling activity of ModelMapper
+class Widget
+{
+public:
+    Widget()
+        : m_onPropertyChangeCount(0), m_onChildPropertyChangeCount(0), m_onParentChangeCount(0),
+          m_onChildrenChangeCount(0), m_onSiblingsChangeCount(0), m_onAboutToRemoveChild(0)
+    {
+    }
+
+    void clear()
+    {
+        m_onPropertyChangeCount = 0;
+        m_onChildPropertyChangeCount = 0;
+        m_onParentChangeCount = 0;
+        m_onChildrenChangeCount = 0;
+        m_onSiblingsChangeCount = 0;
+        m_onAboutToRemoveChild = 0;
+        m_reported_items.clear();
+        m_reported_names.clear();
+    }
+
+    void subscribe(ModelMapper* mapper, bool with_subscription = false)
+    {
+        clear();
+
+        void* caller = (with_subscription ? this : 0);
+
+        mapper->setOnPropertyChange([this](QString name) { onPropertyChange(name); }, caller);
+
+        mapper->setOnChildPropertyChange(
+            [this](SessionItem* item, QString name) { onChildPropertyChange(item, name); }, caller);
+
+        mapper->setOnParentChange([this](SessionItem* parent) { onParentChange(parent); }, caller);
+
+        mapper->setOnChildrenChange([this](SessionItem*) { onChildrenChange(); }, caller);
+
+        mapper->setOnSiblingsChange([this]() { onSiblingsChange(); }, caller);
+
+        mapper->setOnAboutToRemoveChild([this](SessionItem* item) { onAboutToRemoveChild(item); },
+                                        caller);
+    }
+
+    void onPropertyChange(const QString& name)
+    {
+        m_reported_names.append(name);
+        m_onPropertyChangeCount++;
+    }
+
+    void onChildPropertyChange(SessionItem* item, const QString& name)
+    {
+        m_reported_items.append(item);
+        m_reported_names.append(name);
+        m_onChildPropertyChangeCount++;
+    }
+
+    void onParentChange(SessionItem* item)
+    {
+        m_reported_items.append(item);
+        m_onParentChangeCount++;
+    }
+
+    void onChildrenChange() { m_onChildrenChangeCount++; }
+
+    void onSiblingsChange() { m_onSiblingsChangeCount++; }
+
+    void unsubscribe(ModelMapper* mapper) { mapper->unsubscribe(this); }
+
+    void onAboutToRemoveChild(SessionItem* item)
+    {
+        m_reported_items.append(item);
+        m_onAboutToRemoveChild++;
+    }
+
+    int m_onPropertyChangeCount;
+    int m_onChildPropertyChangeCount;
+    int m_onParentChangeCount;
+    int m_onChildrenChangeCount;
+    int m_onSiblingsChangeCount;
+    int m_onAboutToRemoveChild;
+    QList<SessionItem*> m_reported_items;
+    QStringList m_reported_names;
+};
+
+class TestMapperForItem : public ::testing::Test
+{
+public:
+    TestMapperForItem() : m_mapped_item(0) {}
+    ~TestMapperForItem();
+
+    void setItem(SessionItem* item, Widget* widget = 0, bool with_subscription = false)
+    {
+        m_mapped_item = item;
+        m_mapper.reset(new ModelMapper);
+        m_mapper->setItem(item);
+        if (widget)
+            widget->subscribe(m_mapper.get(), with_subscription);
+    }
+
+    SessionItem* m_mapped_item;
+    std::unique_ptr<ModelMapper> m_mapper;
+};
+
+TestMapperForItem::~TestMapperForItem() = default;
+
+TEST_F(TestMapperForItem, test_initialCondition)
+{
+    Widget w;
+    EXPECT_EQ(w.m_onPropertyChangeCount, 0);
+    EXPECT_EQ(w.m_onChildPropertyChangeCount, 0);
+    EXPECT_EQ(w.m_onParentChangeCount, 0);
+    EXPECT_EQ(w.m_onChildrenChangeCount, 0);
+    EXPECT_EQ(w.m_onSiblingsChangeCount, 0);
+    EXPECT_TRUE(w.m_reported_items.isEmpty());
+    EXPECT_TRUE(w.m_reported_names.isEmpty());
+    EXPECT_TRUE(m_mapped_item == nullptr);
+    EXPECT_TRUE(!m_mapper);
+}
+
+TEST_F(TestMapperForItem, test_onPropertyChange)
+{
+    Widget w;
+    SampleModel model;
+    SessionItem* multilayer = model.insertNewItem(Constants::MultiLayerType);
+    SessionItem* layer = model.insertNewItem(Constants::LayerType, model.indexOfItem(multilayer));
+
+    // Mapper is looking on child; set property of child
+    setItem(layer, &w);
+    EXPECT_TRUE(m_mapped_item == layer);
+
+    layer->setItemValue(LayerItem::P_THICKNESS, 1.0);
+    EXPECT_EQ(w.m_onPropertyChangeCount, 1);
+    EXPECT_EQ(w.m_onChildPropertyChangeCount, 0);
+    EXPECT_EQ(w.m_onParentChangeCount, 0);
+    EXPECT_EQ(w.m_onChildrenChangeCount, 0);
+    EXPECT_EQ(w.m_onSiblingsChangeCount, 0);
+    EXPECT_TRUE(w.m_reported_items.isEmpty());
+    EXPECT_TRUE((w.m_reported_names.size() == 1)
+                && (w.m_reported_names[0] == LayerItem::P_THICKNESS));
+
+    // Mapper is looking on child; set property of parent;
+    setItem(layer, &w);
+    EXPECT_TRUE(m_mapped_item == layer);
+    multilayer->setItemValue(MultiLayerItem::P_CROSS_CORR_LENGTH, 1.0);
+    EXPECT_EQ(w.m_onPropertyChangeCount, 0);
+    EXPECT_EQ(w.m_onChildPropertyChangeCount, 0);
+    EXPECT_EQ(w.m_onParentChangeCount, 0);
+    EXPECT_EQ(w.m_onChildrenChangeCount, 0);
+    EXPECT_EQ(w.m_onSiblingsChangeCount, 0);
+    EXPECT_TRUE(w.m_reported_items.isEmpty());
+    EXPECT_TRUE(w.m_reported_names.isEmpty());
+
+    // Mapper is looking on parent; set property of child;
+    setItem(multilayer, &w);
+    EXPECT_TRUE(m_mapped_item == multilayer);
+    layer->setItemValue(LayerItem::P_THICKNESS, 2.0);
+    EXPECT_EQ(w.m_onPropertyChangeCount, 0);
+    EXPECT_EQ(w.m_onChildPropertyChangeCount, 1);
+    EXPECT_EQ(w.m_onParentChangeCount, 0);
+    EXPECT_EQ(w.m_onChildrenChangeCount, 0);
+    EXPECT_EQ(w.m_onSiblingsChangeCount, 0);
+    EXPECT_TRUE((w.m_reported_items.size() == 1) && (w.m_reported_items[0] == layer));
+    EXPECT_TRUE((w.m_reported_names.size() == 1)
+                && (w.m_reported_names[0] == LayerItem::P_THICKNESS));
+
+    // Mapper is looking on parent; set property of parent;
+    setItem(multilayer, &w);
+    EXPECT_TRUE(m_mapped_item == multilayer);
+    multilayer->setItemValue(MultiLayerItem::P_CROSS_CORR_LENGTH, 2.0);
+    EXPECT_EQ(w.m_onPropertyChangeCount, 1);
+    EXPECT_EQ(w.m_onChildPropertyChangeCount, 0);
+    EXPECT_EQ(w.m_onParentChangeCount, 0);
+    EXPECT_EQ(w.m_onChildrenChangeCount, 0);
+    EXPECT_EQ(w.m_onSiblingsChangeCount, 0);
+    EXPECT_TRUE(w.m_reported_items.isEmpty());
+    EXPECT_TRUE((w.m_reported_names.size() == 1)
+                && (w.m_reported_names[0] == MultiLayerItem::P_CROSS_CORR_LENGTH));
+}
+
+TEST_F(TestMapperForItem, test_onParentChange)
+{
+    Widget w;
+    SampleModel model;
+    SessionItem* multilayer = model.insertNewItem(Constants::MultiLayerType);
+    SessionItem* layer = model.insertNewItem(Constants::LayerType, model.indexOfItem(multilayer));
+
+    // Mapper is looking on child; changing child's parent
+    setItem(layer, &w);
+    EXPECT_TRUE(m_mapped_item == layer);
+    multilayer->takeRow(ParentRow(*layer));
+
+    EXPECT_EQ(w.m_onPropertyChangeCount, 0);
+    EXPECT_EQ(w.m_onChildPropertyChangeCount, 0);
+    EXPECT_EQ(w.m_onParentChangeCount, 1);
+    EXPECT_EQ(w.m_onChildrenChangeCount, 0);
+    EXPECT_TRUE(w.m_reported_names.isEmpty());
+}
+
+TEST_F(TestMapperForItem, test_onChildrenChange)
+{
+    Widget w;
+    SampleModel model;
+    SessionItem* multilayer = model.insertNewItem(Constants::MultiLayerType);
+
+    // Mapper is looking on parent; adding new child to parent
+    setItem(multilayer, &w);
+    EXPECT_TRUE(m_mapped_item == multilayer);
+    model.insertNewItem(Constants::LayerType, model.indexOfItem(multilayer));
+
+    EXPECT_EQ(w.m_onPropertyChangeCount, 0);
+    EXPECT_EQ(w.m_onChildPropertyChangeCount, 2);
+    EXPECT_EQ(w.m_onParentChangeCount, 0);
+    EXPECT_EQ(w.m_onChildrenChangeCount, 1);
+    EXPECT_EQ(w.m_onSiblingsChangeCount, 0);
+    EXPECT_EQ(w.m_reported_items.size(), 2);
+    EXPECT_EQ(w.m_reported_names.size(), 2);
+}
+
+TEST_F(TestMapperForItem, test_onSiblingsChange)
+{
+    Widget w;
+    SampleModel model;
+    SessionItem* multilayer = model.insertNewItem(Constants::MultiLayerType);
+    SessionItem* layer = model.insertNewItem(Constants::LayerType, model.indexOfItem(multilayer));
+
+    // Mapper is looking on child; adding another child to parent
+    setItem(layer, &w);
+    EXPECT_TRUE(m_mapped_item == layer);
+    SessionItem* layer2 = model.insertNewItem(Constants::LayerType, model.indexOfItem(multilayer));
+    Q_UNUSED(layer2);
+
+    EXPECT_EQ(w.m_onPropertyChangeCount, 0);
+    EXPECT_EQ(w.m_onChildPropertyChangeCount, 0);
+    EXPECT_EQ(w.m_onParentChangeCount, 0);
+    EXPECT_EQ(w.m_onChildrenChangeCount, 0);
+    EXPECT_EQ(w.m_onSiblingsChangeCount, 1);
+    EXPECT_TRUE(w.m_reported_items.isEmpty());
+    EXPECT_TRUE(w.m_reported_names.isEmpty());
+
+    multilayer->takeItem(1, MultiLayerItem::T_LAYERS);
+    EXPECT_EQ(w.m_onSiblingsChangeCount, 2);
+}
+
+TEST_F(TestMapperForItem, test_Subscription)
+{
+    Widget w;
+    SampleModel model;
+    SessionItem* multilayer = model.insertNewItem(Constants::MultiLayerType);
+    SessionItem* layer = model.insertNewItem(Constants::LayerType, model.indexOfItem(multilayer));
+
+    // Mapper is looking on child; set property of child
+    setItem(layer, &w, true);
+    EXPECT_TRUE(m_mapped_item == layer);
+    layer->setItemValue(LayerItem::P_THICKNESS, 1.0);
+    EXPECT_EQ(w.m_onPropertyChangeCount, 1);
+    EXPECT_EQ(w.m_onChildPropertyChangeCount, 0);
+    EXPECT_EQ(w.m_onParentChangeCount, 0);
+    EXPECT_EQ(w.m_onChildrenChangeCount, 0);
+    EXPECT_EQ(w.m_onSiblingsChangeCount, 0);
+    EXPECT_TRUE(w.m_reported_items.isEmpty());
+    EXPECT_TRUE((w.m_reported_names.size() == 1)
+                && (w.m_reported_names[0] == LayerItem::P_THICKNESS));
+
+    layer->setItemValue(LayerItem::P_THICKNESS, 2.0);
+    EXPECT_EQ(w.m_onPropertyChangeCount, 2);
+
+    // unsubscribe widget and check that it doesn't react on item value change
+    w.unsubscribe(m_mapper.get());
+    layer->setItemValue(LayerItem::P_THICKNESS, 3.0);
+    EXPECT_EQ(w.m_onPropertyChangeCount, 2);
+}
+
+TEST_F(TestMapperForItem, test_TwoWidgetsSubscription)
+{
+    Widget w1, w2;
+    SampleModel model;
+    SessionItem* multilayer = model.insertNewItem(Constants::MultiLayerType);
+    SessionItem* layer = model.insertNewItem(Constants::LayerType, model.indexOfItem(multilayer));
+
+    // Mapper is looking on child; set property of child
+    setItem(layer);
+    w1.subscribe(m_mapper.get(), true);
+    w2.subscribe(m_mapper.get(), true);
+    EXPECT_EQ(w1.m_onPropertyChangeCount, 0);
+    EXPECT_EQ(w2.m_onPropertyChangeCount, 0);
+
+    layer->setItemValue(LayerItem::P_THICKNESS, 1.0);
+    EXPECT_EQ(w1.m_onPropertyChangeCount, 1);
+    EXPECT_EQ(w2.m_onPropertyChangeCount, 1);
+
+    w1.unsubscribe(m_mapper.get());
+    layer->setItemValue(LayerItem::P_THICKNESS, 2.0);
+    EXPECT_EQ(w1.m_onPropertyChangeCount, 1);
+    EXPECT_EQ(w2.m_onPropertyChangeCount, 2);
+}
+
+TEST_F(TestMapperForItem, test_AboutToRemoveChild)
+{
+    Widget w;
+    SampleModel model;
+    SessionItem* container = model.insertNewItem(Constants::ProjectionContainerType);
+
+    SessionItem* line = model.insertNewItem(Constants::HorizontalLineMaskType, container->index());
+
+    setItem(container, &w);
+    EXPECT_EQ(w.m_onAboutToRemoveChild, 0);
+    EXPECT_EQ(w.m_reported_items.size(), 0);
+
+    line->parent()->takeRow(line->parent()->rowOfChild(line));
+    EXPECT_EQ(w.m_onAboutToRemoveChild, 1);
+    EXPECT_EQ(w.m_reported_items.size(), 1);
+    EXPECT_EQ(w.m_reported_items.back(), line);
+}
diff --git a/Tests/UnitTests/GUI2/TestMaterialModel.h b/Tests/UnitTests/GUI2/TestMaterialModel.h
new file mode 100644
index 0000000000000000000000000000000000000000..1df15b6d94c4ad04eae290cdba1b298ed0897c91
--- /dev/null
+++ b/Tests/UnitTests/GUI2/TestMaterialModel.h
@@ -0,0 +1,121 @@
+#include "google_test.h"
+#include "MaterialModel.h"
+#include "MaterialItem.h"
+#include "MaterialDataItem.h"
+#include "MaterialItemUtils.h"
+#include <memory>
+
+class TestMaterialModel : public ::testing::Test
+{
+public:
+    ~TestMaterialModel();
+};
+
+TestMaterialModel::~TestMaterialModel() = default;
+
+TEST_F(TestMaterialModel, test_ParticeleCompositionUpdate)
+{
+    std::unique_ptr<MaterialModel> model(new MaterialModel);
+
+    EXPECT_EQ(model->rowCount(QModelIndex()), 0);
+
+    const double delta(0.2), beta(0.1);
+    const QString name("MaterialName");
+    MaterialItem* item = model->addMaterial(name, delta, beta);
+
+    EXPECT_EQ(model->rowCount(QModelIndex()), 1);
+    EXPECT_EQ(model->itemForIndex(item->index()), item);
+    EXPECT_EQ(model->rowCount(QModelIndex()), 1);
+
+    EXPECT_EQ(item->itemName(), name);
+    const MaterialDataItem* refIndex
+        = dynamic_cast<const MaterialDataItem*>(item->getItem(MaterialItem::P_MATERIAL_DATA));
+    EXPECT_EQ(refIndex->getReal(), delta);
+    EXPECT_EQ(refIndex->getImag(), beta);
+}
+
+TEST_F(TestMaterialModel, test_cloneMaterial)
+{
+    std::unique_ptr<MaterialModel> model(new MaterialModel);
+
+    EXPECT_EQ(model->rowCount(QModelIndex()), 0);
+
+    const double delta(0.2), beta(0.1);
+    const QString name("MaterialName");
+    MaterialItem* item = model->addMaterial(name, delta, beta);
+    const QString origIdentifier = item->getIdentifier();
+
+    MaterialItem* clonedMaterial = model->cloneMaterial(item->index());
+    EXPECT_EQ(model->rowCount(QModelIndex()), 2);
+
+    // clone should not change identifier of original material (as it once happened)
+    EXPECT_EQ(item->getIdentifier(), origIdentifier);
+
+    // cloned material should have different identifier
+    EXPECT_TRUE(clonedMaterial->getIdentifier() != item->getIdentifier());
+
+    // checking name of cloned material
+    EXPECT_EQ(item->itemName() + " (clone)", clonedMaterial->itemName());
+
+    const MaterialDataItem* refIndex = dynamic_cast<const MaterialDataItem*>(
+        clonedMaterial->getItem(MaterialItem::P_MATERIAL_DATA));
+    EXPECT_EQ(refIndex->getReal(), delta);
+    EXPECT_EQ(refIndex->getImag(), beta);
+}
+
+//! Checks the method which returns MaterialItem from known identifier.
+
+TEST_F(TestMaterialModel, test_materialItemFromIdentifier)
+{
+    MaterialModel model;
+    MaterialItem* mat1 = model.addMaterial("aaa", 1.0, 2.0);
+    MaterialItem* mat2 = model.addMaterial("bbb", 3.0, 4.0);
+    EXPECT_TRUE(mat1 == model.materialFromIdentifier(mat1->getIdentifier()));
+    EXPECT_TRUE(mat2 == model.materialFromIdentifier(mat2->getIdentifier()));
+    EXPECT_TRUE(nullptr == model.materialFromIdentifier("non-existing-identifier"));
+}
+
+//! Checks the method which returns MaterialItem from material name.
+
+TEST_F(TestMaterialModel, test_materialItemFromName)
+{
+    MaterialModel model;
+    MaterialItem* mat1 = model.addMaterial("aaa", 1.0, 2.0);
+    MaterialItem* mat2 = model.addMaterial("bbb", 3.0, 4.0);
+    EXPECT_TRUE(mat1 == model.materialFromName(mat1->itemName()));
+    EXPECT_TRUE(mat2 == model.materialFromName(mat2->itemName()));
+    EXPECT_TRUE(nullptr == model.materialFromName("non-existing-name"));
+}
+
+//! Checks the method which construct MaterialProperty from MaterialItem.
+
+TEST_F(TestMaterialModel, test_materialPropertyFromMaterial)
+{
+    MaterialModel model;
+    MaterialItem* mat = model.addMaterial("Something", 1.0, 2.0);
+
+    ExternalProperty property = MaterialItemUtils::materialProperty(*mat);
+    EXPECT_EQ(property.text(), QString("Something"));
+    EXPECT_EQ(property.color(), mat->getColor());
+    EXPECT_EQ(property.identifier(), mat->getIdentifier());
+}
+
+//! Default MaterialProperty construction.
+
+TEST_F(TestMaterialModel, test_defaultMaterialProperty)
+{
+    MaterialModel model;
+
+    // testing default material property from MaterialItemUtils
+    // in the absence of any materials, property should be in invalid state
+    ExternalProperty property = MaterialItemUtils::defaultMaterialProperty();
+    EXPECT_TRUE(property.isValid() == false);
+
+    // adding materials to the model, default property should refer to first material in a model
+    auto mat1 = model.addMaterial("Something1", 1.0, 2.0);
+    model.addMaterial("Something2", 3.0, 4.0);
+    ExternalProperty property2 = MaterialItemUtils::defaultMaterialProperty();
+    EXPECT_EQ(property2.text(), QString("Something1"));
+    EXPECT_EQ(property2.color(), mat1->getColor());
+    EXPECT_EQ(property2.identifier(), mat1->getIdentifier());
+}
diff --git a/Tests/UnitTests/GUI2/TestMaterialPropertyController.h b/Tests/UnitTests/GUI2/TestMaterialPropertyController.h
new file mode 100644
index 0000000000000000000000000000000000000000..a3eb83a66f39dc94f8777501ad1deda102921417
--- /dev/null
+++ b/Tests/UnitTests/GUI2/TestMaterialPropertyController.h
@@ -0,0 +1,134 @@
+#include "google_test.h"
+#include <QtTest>
+#include "MaterialPropertyController.h"
+#include "MaterialModel.h"
+#include "SampleModel.h"
+#include "LayerItem.h"
+#include "MaterialItem.h"
+#include "ExternalProperty.h"
+#include "MaterialItemUtils.h"
+
+class TestMaterialPropertyController : public ::testing::Test
+{
+public:
+    ~TestMaterialPropertyController();
+};
+
+TestMaterialPropertyController::~TestMaterialPropertyController() = default;
+
+TEST_F(TestMaterialPropertyController, test_ControllerForLayer)
+{
+    MaterialModel materialModel;
+    auto mat1 = materialModel.addMaterial("name1", 1.0, 2.0);
+    materialModel.addMaterial("name2", 3.0, 4.0);
+
+    SampleModel sampleModel;
+    auto layer = sampleModel.insertNewItem(Constants::LayerType);
+
+    int property_changed(0);
+    layer->mapper()->setOnPropertyChange(
+        [&property_changed](const QString& name) {
+            if (name == LayerItem::P_MATERIAL)
+                ++property_changed;
+        },
+        this);
+
+    MaterialPropertyController controller;
+    controller.setModels(&materialModel, &sampleModel);
+    EXPECT_EQ(property_changed, 0);
+
+    // changing name of MaterialItem in MaterialModel, looking for MaterialProperty change
+    mat1->setItemName("newname");
+    //    EXPECT_EQ(property_changed, 1);
+    ExternalProperty property
+        = layer->getItemValue(LayerItem::P_MATERIAL).value<ExternalProperty>();
+    EXPECT_EQ(property.identifier(), mat1->getIdentifier());
+    EXPECT_EQ(property.text(), mat1->itemName());
+    EXPECT_EQ(property.color(), mat1->getColor());
+
+    // changing color of MaterialItem
+    ExternalProperty colorProperty = MaterialItemUtils::colorProperty(QColor(Qt::red));
+    mat1->setItemValue(MaterialItem::P_COLOR, colorProperty.variant());
+    //    EXPECT_EQ(property_changed, 2);
+    property = layer->getItemValue(LayerItem::P_MATERIAL).value<ExternalProperty>();
+    EXPECT_EQ(property.identifier(), mat1->getIdentifier());
+    EXPECT_EQ(property.text(), mat1->itemName());
+    EXPECT_EQ(property.color(), mat1->getColor());
+    EXPECT_EQ(property.color(), QColor(Qt::red));
+
+    // removing material from the model, property should become undefined
+    materialModel.removeRows(0, 1, QModelIndex());
+    //    EXPECT_EQ(property_changed, 3);
+    property = layer->getItemValue(LayerItem::P_MATERIAL).value<ExternalProperty>();
+    EXPECT_TRUE(property.isValid() == false);
+}
+
+//! Test MaterialProperty update in sample items when working on model clone.
+
+TEST_F(TestMaterialPropertyController, test_ControllerInEditorContext)
+{
+    MaterialModel materialModel;
+    auto mat1 = materialModel.addMaterial("name1", 1.0, 2.0);
+    auto mat2 = materialModel.addMaterial("name2", 1.0, 2.0);
+    auto mat3 = materialModel.addMaterial("name3", 1.0, 2.0);
+
+    SampleModel sampleModel;
+    auto layer1 = sampleModel.insertNewItem(Constants::LayerType);
+    auto layer2 = sampleModel.insertNewItem(Constants::LayerType);
+    auto layer3 = sampleModel.insertNewItem(Constants::LayerType);
+
+    MaterialPropertyController controller;
+    controller.setModels(&materialModel, &sampleModel);
+
+    layer1->setItemValue(LayerItem::P_MATERIAL,
+                         MaterialItemUtils::materialProperty(*mat1).variant());
+    layer2->setItemValue(LayerItem::P_MATERIAL,
+                         MaterialItemUtils::materialProperty(*mat2).variant());
+    layer3->setItemValue(LayerItem::P_MATERIAL,
+                         MaterialItemUtils::materialProperty(*mat3).variant());
+
+    // Making copy of material model
+    std::unique_ptr<MaterialModel> materialsCopy(materialModel.createCopy());
+    auto mat1copy = dynamic_cast<MaterialItem*>(
+        materialsCopy->itemForIndex(materialsCopy->index(0, 0, QModelIndex())));
+    auto mat2copy = dynamic_cast<MaterialItem*>(
+        materialsCopy->itemForIndex(materialsCopy->index(1, 0, QModelIndex())));
+    auto mat3copy = dynamic_cast<MaterialItem*>(
+        materialsCopy->itemForIndex(materialsCopy->index(2, 0, QModelIndex())));
+    EXPECT_EQ(mat1->getColor(), mat1copy->getColor());
+    EXPECT_EQ(mat1->itemName(), mat1copy->itemName());
+    EXPECT_EQ(mat1->getIdentifier(), mat1copy->getIdentifier());
+    EXPECT_EQ(mat2->getColor(), mat2copy->getColor());
+    EXPECT_EQ(mat2->itemName(), mat2copy->itemName());
+    EXPECT_EQ(mat2->getIdentifier(), mat2copy->getIdentifier());
+    EXPECT_EQ(mat3->itemName(), mat3copy->itemName());
+    EXPECT_EQ(mat3->getIdentifier(), mat3copy->getIdentifier());
+
+    // Removing mat2 from the copy
+    materialsCopy->removeRows(1, 1, QModelIndex());
+    mat1copy = dynamic_cast<MaterialItem*>(
+        materialsCopy->itemForIndex(materialsCopy->index(0, 0, QModelIndex())));
+    mat3copy = dynamic_cast<MaterialItem*>(
+        materialsCopy->itemForIndex(materialsCopy->index(1, 0, QModelIndex())));
+    EXPECT_EQ(mat1->getColor(), mat1copy->getColor());
+    EXPECT_EQ(mat1->itemName(), mat1copy->itemName());
+    EXPECT_EQ(mat3->getColor(), mat3copy->getColor());
+    EXPECT_EQ(mat3->itemName(), mat3copy->itemName());
+
+    // changing mat3
+    mat3copy->setItemName("name3changed");
+
+    // copying back to original model
+    materialModel.clear();
+    materialModel.initFrom(materialsCopy.get(), 0);
+    materialModel.modelLoaded();
+
+    // layer2 should have undefined material property
+    ExternalProperty property
+        = layer2->getItemValue(LayerItem::P_MATERIAL).value<ExternalProperty>();
+    EXPECT_TRUE(property.isValid() == false);
+
+    // layer3 should have different MaterialProperty name
+    property = layer3->getItemValue(LayerItem::P_MATERIAL).value<ExternalProperty>();
+    EXPECT_EQ(property.text(), QString("name3changed"));
+}
diff --git a/Tests/UnitTests/GUI2/TestModelUtils.h b/Tests/UnitTests/GUI2/TestModelUtils.h
new file mode 100644
index 0000000000000000000000000000000000000000..c1ca347fc329ca6a2e92dea2085a499e9a933722
--- /dev/null
+++ b/Tests/UnitTests/GUI2/TestModelUtils.h
@@ -0,0 +1,90 @@
+#include "google_test.h"
+#include "ModelUtils.h"
+#include "SessionModel.h"
+#include "item_constants.h"
+#include "VectorItem.h"
+#include "LayerItem.h"
+#include <QVector>
+
+class TestModelUtils : public ::testing::Test
+{
+public:
+    ~TestModelUtils();
+
+    //! Returns true if model contains given item using iterate_if procedure.
+    bool modelContainsItem(SessionModel* model, SessionItem* selectedItem)
+    {
+        bool result = false;
+        ModelUtils::iterate_if(QModelIndex(), model, [&](const QModelIndex& index) -> bool {
+            SessionItem* item = model->itemForIndex(index);
+            if (item == selectedItem)
+                result = true;
+            return item->isVisible();
+        });
+        return result;
+    }
+};
+
+TestModelUtils::~TestModelUtils() = default;
+
+//! Testing iteration over empty model.
+
+TEST_F(TestModelUtils, test_emptyModel)
+{
+    SessionModel model("TestModel");
+
+    QVector<QModelIndex> indices;
+
+    ModelUtils::iterate(QModelIndex(), &model,
+                        [&](const QModelIndex& index) { indices.push_back(index); });
+
+    EXPECT_EQ(indices.size(), 0);
+}
+
+//! Testing iteration over the model with one VectorItem inserted.
+
+TEST_F(TestModelUtils, test_vectorItem)
+{
+    SessionModel model("TestModel");
+    SessionItem* vectorItem = model.insertNewItem(Constants::VectorType);
+
+    QVector<QModelIndex> indices;
+
+    // checking indices visited during iteration
+    ModelUtils::iterate(QModelIndex(), &model,
+                        [&](const QModelIndex& index) { indices.push_back(index); });
+    EXPECT_EQ(indices.size(), 8); // (VectorItem, P_X, P_Y, P_Z) x (row, col)
+
+    indices.clear();
+    ModelUtils::iterate(QModelIndex(), &model, [&](const QModelIndex& index) {
+        if (index.column() == 0)
+            indices.push_back(index);
+    });
+
+    // checking SessionItems visited during the iteration
+    EXPECT_EQ(indices.size(), 4); // (VectorItem, P_X, P_Y, P_Z) x (row, col)
+    EXPECT_EQ(model.itemForIndex(indices.front()), vectorItem);
+    EXPECT_EQ(model.itemForIndex(indices.back()), vectorItem->getItem(VectorItem::P_Z));
+}
+
+//! Tests iteration when some children is invisible.
+
+TEST_F(TestModelUtils, test_iterateIf)
+{
+    SessionModel model("TestModel");
+    SessionItem* multilayer = model.insertNewItem(Constants::MultiLayerType);
+    SessionItem* layer = model.insertNewItem(Constants::LayerType, model.indexOfItem(multilayer));
+    SessionItem* thicknessItem = layer->getItem(LayerItem::P_THICKNESS);
+
+    layer->setVisible(true);
+    EXPECT_TRUE(modelContainsItem(&model, layer));
+    EXPECT_TRUE(modelContainsItem(&model, thicknessItem));
+
+    // Setting layer invisible will hide its children from iteration.
+    // Layer itself still will be visible.
+    layer->setVisible(false);
+
+    layer->setVisible(false);
+    EXPECT_TRUE(modelContainsItem(&model, layer));
+    EXPECT_FALSE(modelContainsItem(&model, thicknessItem));
+}
diff --git a/Tests/UnitTests/GUI2/TestOutputDataIOService.h b/Tests/UnitTests/GUI2/TestOutputDataIOService.h
new file mode 100644
index 0000000000000000000000000000000000000000..ce11036a936e4692608a08b4be18140f5946842b
--- /dev/null
+++ b/Tests/UnitTests/GUI2/TestOutputDataIOService.h
@@ -0,0 +1,263 @@
+#include "google_test.h"
+#include <memory>
+#include "ApplicationModels.h"
+#include "OutputDataIOService.h"
+#include "RealDataItem.h"
+#include "JobModel.h"
+#include "JobItem.h"
+#include "JobItemUtils.h"
+#include "IntensityDataItem.h"
+#include "OutputDataIOHistory.h"
+#include "GUIHelpers.h"
+#include "OutputData.h"
+#include "ProjectUtils.h"
+#include "IntensityDataIOFactory.h"
+#include "IntensityDataFunctions.h"
+#include "RealDataModel.h"
+#include "test_utils.h"
+#include <QTest>
+#include <QSignalSpy>
+
+namespace {
+const int nxsize = 5;
+const int nysize = 10;
+}
+
+class TestOutputDataIOService : public ::testing::Test
+{
+public:
+    ~TestOutputDataIOService();
+
+    //! Helper function to create test OutputData.
+    std::unique_ptr<OutputData<double>> createData(double value = 0.0)
+    {
+        std::unique_ptr<OutputData<double>> result(new OutputData<double>());
+        result->addAxis("x", nxsize, -1.0, 1.0);
+        result->addAxis("y", nysize, 0.0, 2.0);
+        result->setAllTo(value);
+        return result;
+    }
+
+    //! Helper function to create test RealData
+    RealDataItem* createRealData(const QString& name, ApplicationModels& models, double value = 0.0)
+    {
+        RealDataItem* result = dynamic_cast<RealDataItem*>(
+            models.realDataModel()->insertNewItem(Constants::RealDataType));
+        result->intensityDataItem()->setOutputData(createData(value).release());
+        result->setItemValue(SessionItem::P_NAME, name);
+        return result;
+    }
+
+    //! Helper function to test if data are the same.
+    bool isTheSame(const OutputData<double>& data1, const OutputData<double>& data2)
+    {
+        double diff = IntensityDataFunctions::getRelativeDifference(data1, data2);
+        return diff < 1e-10;
+    }
+
+    //! Helper function to check if file on disk represents same data.
+    bool isTheSame(const QString& fileName, const OutputData<double>& data)
+    {
+        std::unique_ptr<OutputData<double>> dataOnDisk(
+            IntensityDataIOFactory::readOutputData(fileName.toStdString()));
+        return isTheSame(*dataOnDisk, data);
+    }
+};
+
+TestOutputDataIOService::~TestOutputDataIOService() = default;
+
+//! Test methods for retrieving nonXML data.
+
+TEST_F(TestOutputDataIOService, test_nonXMLData)
+{
+    ApplicationModels models;
+
+    // initial state
+    auto dataItems = models.nonXMLData();
+    EXPECT_EQ(dataItems.size(), 0);
+
+    // adding RealDataItem
+    SessionItem* realData = models.realDataModel()->insertNewItem(Constants::RealDataType);
+    EXPECT_EQ(models.realDataModel()->nonXMLData().size(), 1);
+
+    // adding JobItem
+    SessionItem* jobItem = models.jobModel()->insertNewItem(Constants::JobItemType);
+    SessionItem* dataItem = models.jobModel()->insertNewItem(
+        Constants::IntensityDataType, jobItem->index(), -1, JobItem::T_OUTPUT);
+    EXPECT_EQ(models.jobModel()->nonXMLData().size(), 1);
+
+    // adding RealDataItem to jobItem
+    SessionItem* realData2 = models.jobModel()->insertNewItem(
+        Constants::RealDataType, jobItem->index(), -1, JobItem::T_REALDATA);
+    EXPECT_EQ(models.jobModel()->nonXMLData().size(), 2);
+
+    // checking data items of ApplicationModel
+    dataItems = models.nonXMLData();
+    EXPECT_EQ(dataItems.size(), 3);
+    EXPECT_EQ(dataItems.indexOf(realData->getItem(RealDataItem::T_INTENSITY_DATA)), 0);
+    EXPECT_EQ(dataItems.indexOf(dataItem), 1);
+    EXPECT_EQ(dataItems.indexOf(realData2->getItem(RealDataItem::T_INTENSITY_DATA)), 2);
+
+    // checking data items of OutputDataIOService
+    OutputDataIOService service(&models);
+    EXPECT_EQ(service.dataItems().size(), 3);
+}
+
+//! Tests OutputDataSaveInfo class intended for storing info about the last save.
+
+TEST_F(TestOutputDataIOService, test_OutputDataSaveInfo)
+{
+    SessionModel model("TempModel");
+    IntensityDataItem* item
+        = dynamic_cast<IntensityDataItem*>(model.insertNewItem(Constants::IntensityDataType));
+
+    item->setLastModified(QDateTime::currentDateTime());
+
+    const int nap_time(10);
+    QTest::qSleep(nap_time);
+
+    OutputDataSaveInfo info = OutputDataSaveInfo::createSaved(item);
+    EXPECT_TRUE(info.wasModifiedSinceLastSave() == false);
+
+    QTest::qSleep(nap_time);
+    item->setLastModified(QDateTime::currentDateTime());
+    EXPECT_TRUE(info.wasModifiedSinceLastSave() == true);
+}
+
+//! Tests OutputDataDirHistory class intended for storing save history of several
+//! IntensityDataItems in a directory.
+
+TEST_F(TestOutputDataIOService, test_OutputDataDirHistory)
+{
+    SessionModel model("TempModel");
+    IntensityDataItem* item1
+        = dynamic_cast<IntensityDataItem*>(model.insertNewItem(Constants::IntensityDataType));
+
+    IntensityDataItem* item2
+        = dynamic_cast<IntensityDataItem*>(model.insertNewItem(Constants::IntensityDataType));
+
+    item1->setLastModified(QDateTime::currentDateTime());
+    item2->setLastModified(QDateTime::currentDateTime());
+
+    // empty history
+    OutputDataDirHistory history;
+    EXPECT_TRUE(history.contains(item1) == false);
+    // non-existing item is treated as modified
+    EXPECT_TRUE(history.wasModifiedSinceLastSave(item1) == true);
+
+    // Saving item in a history
+    history.markAsSaved(item1);
+    history.markAsSaved(item2);
+
+    EXPECT_TRUE(history.contains(item1) == true);
+    EXPECT_TRUE(history.contains(item2) == true);
+    EXPECT_TRUE(history.wasModifiedSinceLastSave(item1) == false);
+    EXPECT_TRUE(history.wasModifiedSinceLastSave(item2) == false);
+
+    // Attempt to save same item second time
+    EXPECT_THROW(history.markAsSaved(item1), GUIHelpers::Error);
+
+    // Modifying item
+    QTest::qSleep(10);
+    item1->setLastModified(QDateTime::currentDateTime());
+
+    EXPECT_TRUE(history.wasModifiedSinceLastSave(item1) == true);
+    EXPECT_TRUE(history.wasModifiedSinceLastSave(item2) == false);
+}
+
+//! Tests OutputDataIOHistory class (save info for several independent directories).
+
+TEST_F(TestOutputDataIOService, test_OutputDataIOHistory)
+{
+    SessionModel model("TempModel");
+    IntensityDataItem* item1
+        = dynamic_cast<IntensityDataItem*>(model.insertNewItem(Constants::IntensityDataType));
+
+    IntensityDataItem* item2
+        = dynamic_cast<IntensityDataItem*>(model.insertNewItem(Constants::IntensityDataType));
+
+    item1->setLastModified(QDateTime::currentDateTime());
+    item2->setLastModified(QDateTime::currentDateTime());
+
+    OutputDataDirHistory dirHistory1;
+    dirHistory1.markAsSaved(item1);
+    dirHistory1.markAsSaved(item2);
+
+    OutputDataDirHistory dirHistory2;
+    dirHistory2.markAsSaved(item1);
+
+    // Modifying item
+    QTest::qSleep(10);
+    item1->setLastModified(QDateTime::currentDateTime());
+
+    // Creating two histories
+    OutputDataIOHistory history;
+    history.setHistory("dir1", dirHistory1);
+    history.setHistory("dir2", dirHistory2);
+
+    EXPECT_TRUE(history.wasModifiedSinceLastSave("dir1", item1) == true);
+    EXPECT_TRUE(history.wasModifiedSinceLastSave("dir2", item1) == true);
+
+    EXPECT_TRUE(history.wasModifiedSinceLastSave("dir1", item2) == false);
+    EXPECT_TRUE(history.wasModifiedSinceLastSave("dir2", item2)
+                == true); // since item2 doesn't exist
+
+    // asking info for some non-existing directory
+    EXPECT_THROW(history.wasModifiedSinceLastSave("dir3", item1), GUIHelpers::Error);
+}
+
+//! Testing saving abilities of OutputDataIOService class.
+
+TEST_F(TestOutputDataIOService, test_OutputDataIOService)
+{
+    const QString projectDir("test_OutputDataIOService");
+    TestUtils::create_dir(projectDir);
+
+    const double value1(1.0), value2(2.0), value3(3.0);
+
+    ApplicationModels models;
+    RealDataItem* realData1 = createRealData("data1", models, value1);
+    RealDataItem* realData2 = createRealData("data2", models, value2);
+
+    // Saving first time
+    OutputDataIOService service(&models);
+    service.save(projectDir);
+    QTest::qSleep(10);
+
+    // Checking existance of data on disk
+    QString fname1 = "./" + projectDir + "/" + "realdata_data1_0.int.gz";
+    QString fname2 = "./" + projectDir + "/" + "realdata_data2_0.int.gz";
+    EXPECT_TRUE(ProjectUtils::exists(fname1));
+    EXPECT_TRUE(ProjectUtils::exists(fname2));
+
+    // Reading data from disk, checking it is the same
+    std::unique_ptr<OutputData<double>> dataOnDisk1(
+        IntensityDataIOFactory::readOutputData(fname1.toStdString()));
+    std::unique_ptr<OutputData<double>> dataOnDisk2(
+        IntensityDataIOFactory::readOutputData(fname2.toStdString()));
+    EXPECT_TRUE(isTheSame(*dataOnDisk1, *realData1->intensityDataItem()->getOutputData()));
+    EXPECT_TRUE(isTheSame(*dataOnDisk2, *realData2->intensityDataItem()->getOutputData()));
+
+    // Modifying data and saving the project.
+    realData2->intensityDataItem()->setOutputData(createData(value3).release());
+    service.save(projectDir);
+    QTest::qSleep(10);
+
+    EXPECT_TRUE(isTheSame(*dataOnDisk1, *realData1->intensityDataItem()->getOutputData()));
+    EXPECT_TRUE(isTheSame(*dataOnDisk2, *realData2->intensityDataItem()->getOutputData()) == false);
+    // checking that data on disk has changed
+    dataOnDisk2.reset(IntensityDataIOFactory::readOutputData(fname2.toStdString()));
+    EXPECT_TRUE(isTheSame(*dataOnDisk2, *realData2->intensityDataItem()->getOutputData()));
+
+    // Renaming RealData and check that file on disk changed the name
+    realData2->setItemName("data2new");
+    service.save(projectDir);
+    QTest::qSleep(10);
+
+    QString fname2new = "./" + projectDir + "/" + "realdata_data2new_0.int.gz";
+    EXPECT_TRUE(ProjectUtils::exists(fname2new));
+    EXPECT_TRUE(isTheSame(fname2new, *realData2->intensityDataItem()->getOutputData()));
+
+    // Check that file with old name was removed.
+    EXPECT_TRUE(ProjectUtils::exists(fname2) == false);
+}
diff --git a/Tests/UnitTests/GUI2/TestParaCrystalItems.h b/Tests/UnitTests/GUI2/TestParaCrystalItems.h
new file mode 100644
index 0000000000000000000000000000000000000000..50c6f5244aeba41e920922d31e2623b84bc9f11e
--- /dev/null
+++ b/Tests/UnitTests/GUI2/TestParaCrystalItems.h
@@ -0,0 +1,109 @@
+#include "google_test.h"
+#include "FTDistributionItems.h"
+#include "FTDistributions2D.h"
+#include "InterferenceFunction2DParaCrystal.h"
+#include "InterferenceFunctionItems.h"
+#include "Lattice2D.h"
+#include "Lattice2DItems.h"
+#include "ParticleLayoutItem.h"
+#include "SampleModel.h"
+#include "TransformFromDomain.h"
+#include "Units.h"
+#include "item_constants.h"
+
+class TestParaCrystalItems : public ::testing::Test
+{
+public:
+    ~TestParaCrystalItems();
+};
+
+TestParaCrystalItems::~TestParaCrystalItems() = default;
+
+TEST_F(TestParaCrystalItems, test_Para2D_fromToDomain)
+{
+    double length1(10.0), length2(20.0), angle(45.0), xi(90.0);
+    double damping_length(1000.0), domain_size1(50.0), domain_size2(100.0);
+
+    InterferenceFunction2DParaCrystal orig(length1, length2, angle * Units::deg, xi * Units::deg,
+                                           damping_length);
+    orig.setDomainSizes(domain_size1, domain_size2);
+
+    double clength_x(1.0), clength_y(2.0), gamma(3.0);
+    orig.setProbabilityDistributions(
+        FTDistribution2DCauchy(clength_x, clength_y, gamma * Units::deg),
+        FTDistribution2DGauss(clength_x, clength_y, gamma * Units::deg));
+
+    // from domain
+    InterferenceFunction2DParaCrystalItem item;
+    TransformFromDomain::setItemFromSample(&item, &orig);
+
+    EXPECT_EQ(item.getItemValue(InterferenceFunction2DParaCrystalItem::P_DAMPING_LENGTH).toDouble(),
+              orig.dampingLength());
+    EXPECT_EQ(item.getItemValue(InterferenceFunction2DParaCrystalItem::P_DOMAIN_SIZE1).toDouble(),
+              orig.domainSizes()[0]);
+    EXPECT_EQ(item.getItemValue(InterferenceFunction2DParaCrystalItem::P_DOMAIN_SIZE2).toDouble(),
+              orig.domainSizes()[1]);
+    EXPECT_EQ(orig.integrationOverXi(), false);
+    EXPECT_EQ(item.getItemValue(InterferenceFunction2DParaCrystalItem::P_XI_INTEGRATION).toBool(),
+              orig.integrationOverXi());
+
+    SessionItem* latticeItem = item.getGroupItem(InterferenceFunction2DLatticeItem::P_LATTICE_TYPE);
+    EXPECT_EQ(latticeItem->modelType(), Constants::BasicLatticeType);
+    EXPECT_EQ(latticeItem->getItemValue(BasicLatticeItem::P_LATTICE_LENGTH1).toDouble(), length1);
+    EXPECT_EQ(latticeItem->getItemValue(BasicLatticeItem::P_LATTICE_LENGTH2).toDouble(), length2);
+    EXPECT_EQ(latticeItem->getItemValue(BasicLatticeItem::P_LATTICE_ANGLE).toDouble(), angle);
+    EXPECT_EQ(latticeItem->getItemValue(Lattice2DItem::P_LATTICE_ROTATION_ANGLE).toDouble(), xi);
+
+    SessionItem* pdfItem1 = item.getGroupItem(InterferenceFunction2DParaCrystalItem::P_PDF1);
+    EXPECT_EQ(pdfItem1->modelType(), Constants::FTDistribution2DCauchyType);
+    EXPECT_EQ(pdfItem1->getItemValue(FTDistribution2DItem::P_OMEGA_X).toDouble(), clength_x);
+    EXPECT_EQ(pdfItem1->getItemValue(FTDistribution2DItem::P_OMEGA_Y).toDouble(), clength_y);
+    EXPECT_EQ(pdfItem1->getItemValue(FTDistribution2DItem::P_GAMMA).toDouble(), gamma);
+
+    SessionItem* pdfItem2 = item.getGroupItem(InterferenceFunction2DParaCrystalItem::P_PDF2);
+    EXPECT_EQ(pdfItem2->modelType(), Constants::FTDistribution2DGaussType);
+    EXPECT_EQ(pdfItem2->getItemValue(FTDistribution2DItem::P_OMEGA_X).toDouble(), clength_x);
+    EXPECT_EQ(pdfItem2->getItemValue(FTDistribution2DItem::P_OMEGA_Y).toDouble(), clength_y);
+    EXPECT_EQ(pdfItem2->getItemValue(FTDistribution2DItem::P_GAMMA).toDouble(), gamma);
+
+    // to domain
+    auto ifun = item.createInterferenceFunction();
+    std::unique_ptr<InterferenceFunction2DParaCrystal> domain(
+        dynamic_cast<InterferenceFunction2DParaCrystal*>(ifun->clone()));
+    EXPECT_EQ(domain->integrationOverXi(), orig.integrationOverXi());
+    EXPECT_EQ(domain->domainSizes(), orig.domainSizes());
+    EXPECT_EQ(domain->dampingLength(), orig.dampingLength());
+    EXPECT_EQ(domain->lattice().length1(), orig.lattice().length1());
+    EXPECT_EQ(domain->lattice().length2(), orig.lattice().length2());
+    EXPECT_EQ(domain->lattice().latticeAngle(), orig.lattice().latticeAngle());
+    EXPECT_EQ(domain->lattice().rotationAngle(), orig.lattice().rotationAngle());
+}
+
+TEST_F(TestParaCrystalItems, test_Inference2DRotationAngleToggle)
+{
+    SampleModel model;
+    SessionItem* multilayer = model.insertNewItem(Constants::MultiLayerType);
+    SessionItem* layer = model.insertNewItem(Constants::LayerType, multilayer->index());
+    SessionItem* layout = model.insertNewItem(Constants::ParticleLayoutType, layer->index());
+
+    SessionItem* interference
+        = model.insertNewItem(Constants::InterferenceFunction2DParaCrystalType, layout->index(), -1,
+                              ParticleLayoutItem::T_INTERFERENCE);
+
+    // rotation (xi) should be disabled if integration is on
+    interference->setItemValue(InterferenceFunction2DParaCrystalItem::P_XI_INTEGRATION, true);
+
+    SessionItem* angleItem
+        = interference->getGroupItem(InterferenceFunction2DLatticeItem::P_LATTICE_TYPE)
+              ->getItem(Lattice2DItem::P_LATTICE_ROTATION_ANGLE);
+
+    EXPECT_FALSE(angleItem->isEnabled());
+
+    // rotation (xi) should be enabled if integration is off
+    interference->setItemValue(InterferenceFunction2DParaCrystalItem::P_XI_INTEGRATION, false);
+
+    angleItem = interference->getGroupItem(InterferenceFunction2DLatticeItem::P_LATTICE_TYPE)
+                    ->getItem(Lattice2DItem::P_LATTICE_ROTATION_ANGLE);
+
+    EXPECT_TRUE(angleItem->isEnabled());
+}
diff --git a/Tests/UnitTests/GUI2/TestParameterTreeUtils.h b/Tests/UnitTests/GUI2/TestParameterTreeUtils.h
new file mode 100644
index 0000000000000000000000000000000000000000..b4f974e146be84ca12e8a2774e75494add608502
--- /dev/null
+++ b/Tests/UnitTests/GUI2/TestParameterTreeUtils.h
@@ -0,0 +1,73 @@
+#include "google_test.h"
+#include "ParameterTreeItems.h"
+#include "ParameterTreeUtils.h"
+#include "LayerItem.h"
+#include "SampleModel.h"
+#include "ParticleItem.h"
+#include "FormFactorItems.h"
+#include "VectorItem.h"
+
+namespace {
+    const QStringList expectedParticleParameterNames = {
+        "Particle/Cylinder/Radius", "Particle/Cylinder/Height", "Particle/Abundance",
+        "Particle/Position Offset/X", "Particle/Position Offset/Y", "Particle/Position Offset/Z"};
+
+    const QStringList expectedParticleParameterTranslations = {
+        "Particle/Cylinder/Radius", "Particle/Cylinder/Height", "Particle/Abundance",
+        "Particle/PositionX", "Particle/PositionY", "Particle/PositionZ"};
+
+}
+
+class TestParameterTreeUtils : public ::testing::Test
+{
+public:
+    ~TestParameterTreeUtils();
+};
+
+TestParameterTreeUtils::~TestParameterTreeUtils() = default;
+
+//! Tests parameter names of given item.
+
+TEST_F(TestParameterTreeUtils, test_parameterTreeNames)
+{
+    SampleModel model;
+
+    SessionItem* layer = model.insertNewItem(Constants::LayerType);
+    EXPECT_EQ(ParameterTreeUtils::parameterTreeNames(layer), QStringList() << "Layer/Thickness");
+
+    SessionItem* particle = model.insertNewItem(Constants::ParticleType);
+    EXPECT_EQ(ParameterTreeUtils::parameterTreeNames(particle), expectedParticleParameterNames);
+}
+
+//! Tests translated parameter names of given item.
+
+TEST_F(TestParameterTreeUtils, test_parameterTranslatedNames)
+{
+    SampleModel model;
+
+    SessionItem* particle = model.insertNewItem(Constants::ParticleType);
+
+    EXPECT_EQ(ParameterTreeUtils::translatedParameterTreeNames(particle),
+              expectedParticleParameterTranslations);
+}
+
+//! Tests translated parameter names of given item.
+
+TEST_F(TestParameterTreeUtils, test_linkItemFromParameterName)
+{
+    SampleModel model;
+
+    SessionItem* particle = model.insertNewItem(Constants::ParticleType);
+
+    auto ffItem = static_cast<FormFactorItem*>(particle->getGroupItem(ParticleItem::P_FORM_FACTOR));
+    Q_ASSERT(ffItem);
+    EXPECT_EQ(ffItem->modelType(), Constants::CylinderType);
+
+    EXPECT_EQ(ffItem->getItem(CylinderItem::P_RADIUS),
+              ParameterTreeUtils::parameterNameToLinkedItem("Particle/Cylinder/Radius", particle));
+    EXPECT_EQ(ffItem->getItem(CylinderItem::P_HEIGHT),
+              ParameterTreeUtils::parameterNameToLinkedItem("Particle/Cylinder/Height", particle));
+    EXPECT_EQ(
+        particle->getItem(ParticleItem::P_POSITION)->getItem(VectorItem::P_X),
+        ParameterTreeUtils::parameterNameToLinkedItem("Particle/Position Offset/X", particle));
+}
diff --git a/Tests/UnitTests/GUI2/TestParticleCoreShell.h b/Tests/UnitTests/GUI2/TestParticleCoreShell.h
new file mode 100644
index 0000000000000000000000000000000000000000..f59b5ebd09258f264a15223ed2678640f92377e2
--- /dev/null
+++ b/Tests/UnitTests/GUI2/TestParticleCoreShell.h
@@ -0,0 +1,121 @@
+#include "google_test.h"
+#include "ParticleItem.h"
+#include "ParticleCoreShellItem.h"
+#include "ParticleDistributionItem.h"
+#include "ParticleCompositionItem.h"
+#include "SampleModel.h"
+#include "SessionItemUtils.h"
+#include "VectorItem.h"
+#include <QtTest>
+
+using namespace SessionItemUtils;
+
+class TestParticleCoreShell : public ::testing::Test
+{
+public:
+    ~TestParticleCoreShell();
+};
+
+TestParticleCoreShell::~TestParticleCoreShell() = default;
+
+//! Checking that adding and removing core/shell leads to enabling/disabling of their position
+//! and abundance properties.
+
+TEST_F(TestParticleCoreShell, test_propertyAppearance)
+{
+    SampleModel model;
+
+    // empty coreshell particle
+    SessionItem* coreshell = model.insertNewItem(Constants::ParticleCoreShellType);
+    EXPECT_TRUE(coreshell->getItem(ParticleItem::P_ABUNDANCE)->isEnabled());
+    EXPECT_EQ(coreshell->getItemValue(ParticleItem::P_ABUNDANCE).toDouble(), 1.0);
+    EXPECT_TRUE(coreshell->getItem(ParticleItem::P_POSITION)->isEnabled());
+    kvector_t pos = GetVectorItem(*coreshell, ParticleItem::P_POSITION);
+    EXPECT_EQ(pos.x(), 0.0);
+    EXPECT_EQ(pos.y(), 0.0);
+    EXPECT_EQ(pos.z(), 0.0);
+
+    // adding core, and checking that abundance is disabled
+    SessionItem* core = model.insertNewItem(Constants::ParticleType, coreshell->index(), -1,
+                                            ParticleCoreShellItem::T_CORE);
+    EXPECT_FALSE(core->getItem(ParticleItem::P_ABUNDANCE)->isEnabled());
+    EXPECT_TRUE(core->getItem(ParticleItem::P_POSITION)->isEnabled());
+
+    // removing core, checking that abundance restored
+    coreshell->takeRow(ParentRow(*core));
+    EXPECT_TRUE(core->getItem(ParticleItem::P_ABUNDANCE)->isEnabled());
+    EXPECT_TRUE(core->getItem(ParticleItem::P_POSITION)->isEnabled());
+    delete core;
+
+    // creating shell (not yet attached to the coreshell)
+    SessionItem* shell = model.insertNewItem(Constants::ParticleType);
+    SessionItem* positionItem = shell->getItem(ParticleItem::P_POSITION);
+    // putting some values to position and abundance
+    shell->setItemValue(ParticleItem::P_ABUNDANCE, 0.2);
+    positionItem->setItemValue(VectorItem::P_X, 1.0);
+
+    // attaching shell to coreshell and checking abundance disabled
+    model.moveParameterizedItem(shell, coreshell, -1, ParticleCoreShellItem::T_SHELL);
+    EXPECT_FALSE(shell->getItem(ParticleItem::P_ABUNDANCE)->isEnabled());
+    EXPECT_FALSE(shell->getItem(ParticleItem::P_POSITION)->isEnabled());
+    // checking that position and abundance values were reset to defaults
+    EXPECT_EQ(positionItem->getItemValue(VectorItem::P_X).toDouble(), 0.0);
+    EXPECT_EQ(shell->getItemValue(ParticleItem::P_ABUNDANCE).toDouble(), 1.0);
+
+    // removing shell and checking abundance, position restored
+    coreshell->takeRow(ParentRow(*shell));
+    EXPECT_TRUE(shell->getItem(ParticleItem::P_ABUNDANCE)->isEnabled());
+    EXPECT_TRUE(shell->getItem(ParticleItem::P_POSITION)->isEnabled());
+    delete shell;
+}
+
+//! Checking that abundance gets disabled in particle distribution context.
+
+TEST_F(TestParticleCoreShell, test_distributionContext)
+{
+    SampleModel model;
+
+    // coreshell particle
+    SessionItem* coreshell = model.insertNewItem(Constants::ParticleCoreShellType);
+    coreshell->setItemValue(ParticleItem::P_ABUNDANCE, 0.2);
+    EXPECT_TRUE(coreshell->getItem(ParticleItem::P_ABUNDANCE)->isEnabled() == true);
+    EXPECT_EQ(coreshell->getItemValue(ParticleItem::P_ABUNDANCE).toDouble(), 0.2);
+
+    // create distribution, adding coreshell to it
+    SessionItem* distribution = model.insertNewItem(Constants::ParticleDistributionType);
+    model.moveParameterizedItem(coreshell, distribution, -1, ParticleDistributionItem::T_PARTICLES);
+    // checking abundance has switched to defaults
+    EXPECT_FALSE(coreshell->getItem(ParticleItem::P_ABUNDANCE)->isEnabled());
+    EXPECT_EQ(coreshell->getItemValue(ParticleItem::P_ABUNDANCE).toDouble(), 1.0);
+
+    // removing coreshell
+    distribution->takeRow(ParentRow(*coreshell));
+    EXPECT_TRUE(coreshell->getItem(ParticleItem::P_ABUNDANCE)->isEnabled());
+    delete coreshell;
+}
+
+//! Checking that abundance gets disabled in particle composition context.
+
+TEST_F(TestParticleCoreShell, test_compositionContext)
+{
+    SampleModel model;
+
+    // coreshell particle
+    SessionItem* coreshell = model.insertNewItem(Constants::ParticleCoreShellType);
+    coreshell->setItemValue(ParticleItem::P_ABUNDANCE, 0.2);
+    EXPECT_TRUE(coreshell->getItem(ParticleItem::P_ABUNDANCE)->isEnabled());
+    EXPECT_EQ(coreshell->getItemValue(ParticleItem::P_ABUNDANCE).toDouble(), 0.2);
+
+    // create composition, adding coreshell to it
+    SessionItem* composition = model.insertNewItem(Constants::ParticleCompositionType);
+    model.moveParameterizedItem(coreshell, composition, -1, ParticleCompositionItem::T_PARTICLES);
+    // checking abundance has switched to defaults
+    EXPECT_FALSE(coreshell->getItem(ParticleItem::P_ABUNDANCE)->isEnabled());
+    EXPECT_EQ(coreshell->getItemValue(ParticleItem::P_ABUNDANCE).toDouble(), 1.0);
+
+    // removing coreshell
+    composition->takeRow(ParentRow(*coreshell));
+    EXPECT_TRUE(coreshell->getItem(ParticleItem::P_ABUNDANCE)->isEnabled());
+    delete coreshell;
+}
+
diff --git a/Tests/UnitTests/GUI2/TestParticleDistributionItem.h b/Tests/UnitTests/GUI2/TestParticleDistributionItem.h
new file mode 100644
index 0000000000000000000000000000000000000000..146ffd73a0854b843856fbf1697d0fe99f466b5a
--- /dev/null
+++ b/Tests/UnitTests/GUI2/TestParticleDistributionItem.h
@@ -0,0 +1,178 @@
+#include "google_test.h"
+#include "ComboProperty.h"
+#include "DistributionItems.h"
+#include "Distributions.h"
+#include "MaterialEditor.h"
+#include "MaterialFactoryFuncs.h"
+#include "MaterialModel.h"
+#include "Particle.h"
+#include "ParticleDistribution.h"
+#include "ParticleDistributionItem.h"
+#include "ParticleItem.h"
+#include "RealLimitsItems.h"
+#include "SampleModel.h"
+#include "TransformFromDomain.h"
+#include "FormFactors.h"
+#include <QXmlStreamWriter>
+
+namespace
+{
+const QStringList expectedCylinderParams = {"None",
+                                            "Particle/Cylinder/Radius",
+                                            "Particle/Cylinder/Height",
+                                            "Particle/Position Offset/X",
+                                            "Particle/Position Offset/Y",
+                                            "Particle/Position Offset/Z"};
+
+const QStringList expectedBoxParams = {"None",
+                                       "Particle/Box/Length",
+                                       "Particle/Box/Width",
+                                       "Particle/Box/Height",
+                                       "Particle/Position Offset/X",
+                                       "Particle/Position Offset/Y",
+                                       "Particle/Position Offset/Z"};
+}
+
+class TestParticleDistributionItem : public ::testing::Test
+{
+public:
+    ~TestParticleDistributionItem();
+};
+
+TestParticleDistributionItem::~TestParticleDistributionItem() = default;
+
+TEST_F(TestParticleDistributionItem, test_InitialState)
+{
+    SampleModel model;
+    SessionItem* distItem = model.insertNewItem(Constants::ParticleDistributionType);
+
+    EXPECT_EQ(distItem->displayName(), Constants::ParticleDistributionType);
+    EXPECT_EQ(distItem->displayName(), distItem->itemName());
+
+    // xpos, ypos, P_ABUNDANCE, P_DISTRIBUTION, P_DISTRIBUTED_PARAMETER
+    EXPECT_EQ(distItem->children().size(), 5);
+
+    EXPECT_EQ(distItem->defaultTag(), ParticleDistributionItem::T_PARTICLES);
+
+    EXPECT_EQ(distItem->acceptableDefaultItemTypes(),
+              QVector<QString>() << Constants::ParticleType << Constants::ParticleCoreShellType
+                                 << Constants::ParticleCompositionType
+                                 << Constants::MesoCrystalType);
+
+    ComboProperty prop = distItem->getItemValue(ParticleDistributionItem::P_DISTRIBUTED_PARAMETER)
+                             .value<ComboProperty>();
+    EXPECT_EQ(prop.getValues(), QStringList() << ParticleDistributionItem::NO_SELECTION);
+    EXPECT_EQ(prop.getValue(), ParticleDistributionItem::NO_SELECTION);
+}
+
+TEST_F(TestParticleDistributionItem, test_AddParticle)
+{
+    SampleModel model;
+    SessionItem* dist = model.insertNewItem(Constants::ParticleDistributionType);
+
+    // adding default particle and checking list of available parameters
+    SessionItem* particle = model.insertNewItem(Constants::ParticleType, dist->index());
+
+    EXPECT_EQ(dist->getItems().size(), 1);
+
+    ComboProperty prop = dist->getItemValue(ParticleDistributionItem::P_DISTRIBUTED_PARAMETER)
+                             .value<ComboProperty>();
+
+    EXPECT_EQ(prop.getValues(), expectedCylinderParams);
+    EXPECT_EQ(prop.getValue(), ParticleDistributionItem::NO_SELECTION);
+
+    // changing formfactor of the particle
+    particle->setGroupProperty(ParticleItem::P_FORM_FACTOR, Constants::BoxType);
+
+    prop = dist->getItemValue(ParticleDistributionItem::P_DISTRIBUTED_PARAMETER)
+               .value<ComboProperty>();
+
+    EXPECT_EQ(prop.getValues(), expectedBoxParams);
+    EXPECT_EQ(prop.getValue(), ParticleDistributionItem::NO_SELECTION);
+}
+
+TEST_F(TestParticleDistributionItem, test_FromDomain)
+{
+    const std::string pattern("/Particle/Cylinder/Radius");
+
+    // creating domain distribution
+    FormFactorCylinder cylinder(1.0, 2.0);
+    Particle particle(HomogeneousMaterial("Particle", 6e-4, 2e-8), cylinder);
+    DistributionGaussian gauss(1.0, 0.1);
+    ParameterDistribution par_distr(pattern, gauss, 100, 3.0);
+
+    ParticleDistribution particle_collection(particle, par_distr);
+
+    // creating GUI distribution
+    SampleModel model;
+    SessionItem* distItem = model.insertNewItem(Constants::ParticleDistributionType);
+    SessionItem* particleItem = model.insertNewItem(Constants::ParticleType, distItem->index());
+
+    particleItem->setGroupProperty(ParticleItem::P_FORM_FACTOR, Constants::AnisoPyramidType);
+
+    // Sets it from domain
+    TransformFromDomain::setItemFromSample(distItem, &particle_collection);
+
+    ComboProperty prop = distItem->getItemValue(ParticleDistributionItem::P_DISTRIBUTED_PARAMETER)
+                             .value<ComboProperty>();
+
+    EXPECT_EQ(prop.getValue(), ParticleDistributionItem::NO_SELECTION);
+
+    // changing particle type and check that distribution picked up domain name
+    particleItem->setGroupProperty(ParticleItem::P_FORM_FACTOR, Constants::CylinderType);
+    prop = distItem->getItemValue(ParticleDistributionItem::P_DISTRIBUTED_PARAMETER)
+               .value<ComboProperty>();
+
+    EXPECT_EQ(prop.getValue(), QString("Particle/Cylinder/Radius"));
+}
+
+TEST_F(TestParticleDistributionItem, test_FromDomainWithLimits)
+{
+    const std::string pattern("/Particle/Cylinder/Radius");
+
+    // creating domain distribution
+    FormFactorCylinder cylinder(1.0, 2.0);
+    Particle particle(HomogeneousMaterial("Particle", 6e-4, 2e-8), cylinder);
+    DistributionGaussian gauss(1.0, 0.1);
+
+    RealLimits domainLimits = RealLimits::limited(1.0, 2.0);
+    ParameterDistribution par_distr(pattern, gauss, 100, 3.0, domainLimits);
+
+    ParticleDistribution particle_collection(particle, par_distr);
+
+    // creating GUI distribution
+    SampleModel model;
+    SessionItem* partDistItem = model.insertNewItem(Constants::ParticleDistributionType);
+    model.insertNewItem(Constants::ParticleType, partDistItem->index());
+
+    //    // Sets it from domain
+    TransformFromDomain::setItemFromSample(partDistItem, &particle_collection);
+
+    SessionItem* distItem = partDistItem->getGroupItem(ParticleDistributionItem::P_DISTRIBUTION);
+    Q_ASSERT(distItem);
+    RealLimitsItem* limitsItem
+        = dynamic_cast<RealLimitsItem*>(distItem->getGroupItem(DistributionItem::P_LIMITS));
+    Q_ASSERT(limitsItem);
+
+    EXPECT_EQ(limitsItem->createRealLimits(), domainLimits);
+}
+
+TEST_F(TestParticleDistributionItem, test_Clone)
+{
+    std::unique_ptr<MaterialModel> P_materialModel(new MaterialModel());
+
+    SampleModel model1;
+    SessionItem* dist = model1.insertNewItem(Constants::ParticleDistributionType);
+    model1.insertNewItem(Constants::ParticleType, dist->index());
+
+    QString buffer1;
+    QXmlStreamWriter writer1(&buffer1);
+    model1.writeTo(&writer1);
+
+    std::unique_ptr<SampleModel> model2(model1.createCopy());
+    QString buffer2;
+    QXmlStreamWriter writer2(&buffer2);
+    model2->writeTo(&writer2);
+
+    EXPECT_EQ(buffer1, buffer2);
+}
diff --git a/Tests/UnitTests/GUI2/TestParticleItem.h b/Tests/UnitTests/GUI2/TestParticleItem.h
new file mode 100644
index 0000000000000000000000000000000000000000..206ed13b40d0f9cc506aa4766d97c81748a5de33
--- /dev/null
+++ b/Tests/UnitTests/GUI2/TestParticleItem.h
@@ -0,0 +1,74 @@
+#include "google_test.h"
+#include "GroupItem.h"
+#include "ParticleCompositionItem.h"
+#include "ParticleDistributionItem.h"
+#include "ParticleItem.h"
+#include "SampleModel.h"
+#include "SessionItem.h"
+#include "SessionItemUtils.h"
+
+using namespace SessionItemUtils;
+
+class TestParticleItem : public ::testing::Test
+{
+public:
+    ~TestParticleItem();
+};
+
+TestParticleItem::~TestParticleItem() = default;
+
+TEST_F(TestParticleItem, test_InitialState)
+{
+    SampleModel model;
+    SessionItem* item = model.insertNewItem(Constants::ParticleType);
+
+    EXPECT_EQ(item->displayName(), Constants::ParticleType);
+    EXPECT_EQ(item->displayName(), item->itemName());
+    // xpos, ypos, P_FORM_FACTOR, P_MATERIAL, P_ABUNDANCE, P_POSITION
+    EXPECT_EQ(item->children().size(), 6);
+    EXPECT_EQ(item->defaultTag(), ParticleItem::T_TRANSFORMATION);
+
+    GroupItem* group = dynamic_cast<GroupItem*>(item->getItem(ParticleItem::P_FORM_FACTOR));
+    EXPECT_EQ(group->displayName(), ParticleItem::P_FORM_FACTOR);
+    EXPECT_EQ(group->children().size(), 1);
+}
+
+TEST_F(TestParticleItem, test_compositionContext)
+{
+    SampleModel model;
+    SessionItem* particle = model.insertNewItem(Constants::ParticleType);
+    particle->setItemValue(ParticleItem::P_ABUNDANCE, 0.2);
+    EXPECT_TRUE(particle->getItem(ParticleItem::P_ABUNDANCE)->isEnabled() == true);
+    EXPECT_EQ(particle->getItemValue(ParticleItem::P_ABUNDANCE).toDouble(), 0.2);
+
+    // adding particle to composition, checking that abundance is default
+    SessionItem* composition = model.insertNewItem(Constants::ParticleCompositionType);
+    model.moveParameterizedItem(particle, composition, -1, ParticleCompositionItem::T_PARTICLES);
+    EXPECT_TRUE(particle->getItem(ParticleItem::P_ABUNDANCE)->isEnabled() == false);
+    EXPECT_EQ(particle->getItemValue(ParticleItem::P_ABUNDANCE).toDouble(), 1.0);
+
+    // removing particle, checking that abundance is enabled again
+    composition->takeRow(ParentRow(*particle));
+    EXPECT_TRUE(particle->getItem(ParticleItem::P_ABUNDANCE)->isEnabled() == true);
+    delete particle;
+}
+
+TEST_F(TestParticleItem, test_distributionContext)
+{
+    SampleModel model;
+    SessionItem* particle = model.insertNewItem(Constants::ParticleType);
+    particle->setItemValue(ParticleItem::P_ABUNDANCE, 0.2);
+    EXPECT_TRUE(particle->getItem(ParticleItem::P_ABUNDANCE)->isEnabled() == true);
+    EXPECT_EQ(particle->getItemValue(ParticleItem::P_ABUNDANCE).toDouble(), 0.2);
+
+    // adding particle to distribution, checking that abundance is default
+    SessionItem* distribution = model.insertNewItem(Constants::ParticleDistributionType);
+    model.moveParameterizedItem(particle, distribution, -1, ParticleDistributionItem::T_PARTICLES);
+    EXPECT_TRUE(particle->getItem(ParticleItem::P_ABUNDANCE)->isEnabled() == false);
+    EXPECT_EQ(particle->getItemValue(ParticleItem::P_ABUNDANCE).toDouble(), 1.0);
+
+    // removing particle, checking that abundance is enabled again
+    distribution->takeRow(ParentRow(*particle));
+    EXPECT_TRUE(particle->getItem(ParticleItem::P_ABUNDANCE)->isEnabled() == true);
+    delete particle;
+}
diff --git a/Tests/UnitTests/GUI2/TestProjectDocument.h b/Tests/UnitTests/GUI2/TestProjectDocument.h
new file mode 100644
index 0000000000000000000000000000000000000000..c17019cbbbecb74146ca4b5018512b0f433e2a34
--- /dev/null
+++ b/Tests/UnitTests/GUI2/TestProjectDocument.h
@@ -0,0 +1,128 @@
+#include "google_test.h"
+#include "ApplicationModels.h"
+#include "GUIHelpers.h"
+#include "InstrumentItem.h"
+#include "InstrumentModel.h"
+#include "IntensityDataItem.h"
+#include "JobItemUtils.h"
+#include "ProjectUtils.h"
+#include "RealDataItem.h"
+#include "RealDataModel.h"
+#include "projectdocument.h"
+#include "test_utils.h"
+#include <QFileInfo>
+#include <QSignalSpy>
+
+class TestProjectDocument : public ::testing::Test
+{
+public:
+    ~TestProjectDocument();
+
+    //! helper method to modify something in a model
+    void modify_models(ApplicationModels* models)
+    {
+        auto instrument = models->instrumentModel()->instrumentItem();
+        instrument->setItemValue(InstrumentItem::P_IDENTIFIER, GUIHelpers::createUuid());
+    }
+};
+
+TestProjectDocument::~TestProjectDocument() = default;
+
+TEST_F(TestProjectDocument, test_documentFlags)
+{
+    ProjectFlags::DocumentStatus flags;
+    EXPECT_FALSE(flags.testFlag(ProjectFlags::STATUS_OK));
+    EXPECT_FALSE(flags.testFlag(ProjectFlags::STATUS_WARNING));
+    EXPECT_FALSE(flags.testFlag(ProjectFlags::STATUS_FAILED));
+
+    ProjectFlags::setFlag(flags, ProjectFlags::STATUS_WARNING);
+    EXPECT_FALSE(flags.testFlag(ProjectFlags::STATUS_OK));
+    EXPECT_TRUE(flags.testFlag(ProjectFlags::STATUS_WARNING));
+    EXPECT_FALSE(flags.testFlag(ProjectFlags::STATUS_FAILED));
+
+    // flags.setFlag(ProjectFlags::STATUS_OK); // only in Qt5.7
+    ProjectFlags::setFlag(flags, ProjectFlags::STATUS_OK);
+    EXPECT_TRUE(flags.testFlag(ProjectFlags::STATUS_OK));
+    EXPECT_TRUE(flags.testFlag(ProjectFlags::STATUS_WARNING));
+    EXPECT_FALSE(flags.testFlag(ProjectFlags::STATUS_FAILED));
+
+    ProjectFlags::DocumentStatus flags2(ProjectFlags::STATUS_WARNING | ProjectFlags::STATUS_FAILED);
+    EXPECT_FALSE(flags2.testFlag(ProjectFlags::STATUS_OK));
+    EXPECT_TRUE(flags2.testFlag(ProjectFlags::STATUS_WARNING));
+    EXPECT_TRUE(flags2.testFlag(ProjectFlags::STATUS_FAILED));
+
+    ProjectDocument document;
+    EXPECT_TRUE(document.documentStatus() == ProjectFlags::STATUS_OK);
+    EXPECT_TRUE(document.isReady());
+    EXPECT_FALSE(document.hasWarnings());
+    EXPECT_FALSE(document.hasErrors());
+}
+
+TEST_F(TestProjectDocument, test_projectDocument)
+{
+    const QString projectDir("test_projectDocument");
+    TestUtils::create_dir(projectDir);
+    const QString projectFileName(projectDir + "/document.pro");
+
+    ApplicationModels models;
+    ProjectDocument* document = new ProjectDocument;
+    document->setApplicationModels(&models);
+
+    // Checking initial document status
+    EXPECT_TRUE(document->isModified() == false);
+    EXPECT_TRUE(document->hasValidNameAndPath() == false);
+    EXPECT_EQ(document->projectDir(), QString());
+    EXPECT_EQ(document->projectName(), QString());
+    EXPECT_EQ(document->projectFileName(), QString());
+
+    // Checking document name and isModified status after project save
+    document->save(projectFileName);
+    EXPECT_TRUE(document->hasValidNameAndPath());
+    EXPECT_EQ(document->projectDir(), projectDir);
+    EXPECT_EQ(document->projectName(), QString("document"));
+    EXPECT_EQ(document->projectFileName(), projectFileName);
+    EXPECT_FALSE(document->isModified());
+
+    QSignalSpy spyDocument(document, SIGNAL(modified()));
+    EXPECT_EQ(spyDocument.count(), 0);
+
+    // Changing document and checking its status
+    modify_models(&models);
+    EXPECT_TRUE(document->isModified());
+    EXPECT_EQ(spyDocument.count(), 1);
+
+    // Saving document
+    document->save(projectFileName);
+    EXPECT_FALSE(document->isModified());
+    EXPECT_EQ(spyDocument.count(), 2); // save triggers 'modified' signal
+
+    QFileInfo info(projectFileName);
+    EXPECT_TRUE(info.exists());
+
+    delete document;
+}
+
+TEST_F(TestProjectDocument, test_projectDocumentWithData)
+{
+    const QString projectDir("test_projectDocumentWithData");
+    TestUtils::create_dir(projectDir);
+
+    ApplicationModels models;
+    RealDataItem* realData = dynamic_cast<RealDataItem*>(
+        models.realDataModel()->insertNewItem(Constants::RealDataType));
+    Q_ASSERT(realData);
+    IntensityDataItem* intensityItem = realData->intensityDataItem();
+    JobItemUtils::createDefaultDetectorMap(intensityItem,
+                                           models.instrumentModel()->instrumentItem());
+    intensityItem->setItemValue(IntensityDataItem::P_FILE_NAME, "realdata.int.gz");
+
+    ProjectDocument* document = new ProjectDocument;
+    document->setApplicationModels(&models);
+    document->save(projectDir + "/untitled.pro");
+
+    QFileInfo info(projectDir + "/untitled.pro");
+    EXPECT_TRUE(info.exists());
+
+    info.setFile(projectDir + "/realdata.int.gz");
+    EXPECT_TRUE(info.exists());
+}
diff --git a/Tests/UnitTests/GUI2/TestProjectUtils.h b/Tests/UnitTests/GUI2/TestProjectUtils.h
new file mode 100644
index 0000000000000000000000000000000000000000..7769fcf992f910d094bdb9e7ebba6c01adcd3788
--- /dev/null
+++ b/Tests/UnitTests/GUI2/TestProjectUtils.h
@@ -0,0 +1,103 @@
+#include "google_test.h"
+#include "GUIHelpers.h"
+#include "ProjectUtils.h"
+#include <QDir>
+#include <QFile>
+#include <QTextStream>
+
+namespace
+{
+const QString& projectDir = "test_ProjectUtils";
+}
+
+class TestProjectUtils : public ::testing::Test
+{
+public:
+    ~TestProjectUtils();
+
+    //! Helper function to create test file in a given directory (directory should exist).
+    void createTestFile(const QString& dirname, const QString& fileName)
+    {
+        QString filename = dirname.isEmpty() ? fileName : dirname + "/" + fileName;
+
+        QFile file(filename);
+        if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
+            throw GUIHelpers::Error("TestProjectUtils::createTestFile() -> Error. "
+                                    "Can't create file");
+
+        QTextStream out(&file);
+        out << "Test file " << 42 << "\n";
+        file.close();
+    }
+};
+
+TestProjectUtils::~TestProjectUtils() = default;
+
+TEST_F(TestProjectUtils, test_basicFileOperations)
+{
+    QDir dir(projectDir);
+    if (dir.exists()) {
+        EXPECT_TRUE(ProjectUtils::removeRecursively(projectDir) == true);
+        EXPECT_TRUE(dir.exists() == false);
+    }
+
+    GUIHelpers::createSubdir(".", projectDir);
+    EXPECT_TRUE(ProjectUtils::exists(projectDir));
+
+    createTestFile(projectDir, "a.txt");
+    EXPECT_TRUE(ProjectUtils::exists(projectDir + "/a.txt"));
+}
+
+TEST_F(TestProjectUtils, test_nonXMLDataInDir)
+{
+    EXPECT_EQ(1, 1);
+    QStringList test_nonxml_files = QStringList() << "jobdata_job2_0.int.gz"
+                                                  << "refdata_job2_0.int.gz"
+                                                  << "realdata_box_nanodots_0.int.gz"
+                                                  << "realdata_untitled_0.int.gz";
+
+    QStringList test_some_other_files = QStringList() << "user_0.int.gz"
+                                                      << "b.txt"
+                                                      << "untitled.pro";
+
+    // creating files
+    QStringList test_files = QStringList() << test_nonxml_files << test_some_other_files;
+    for (auto name : test_files) {
+        createTestFile(projectDir, name);
+        EXPECT_TRUE(ProjectUtils::exists(projectDir + "/" + name));
+    }
+
+    // Checking nonXMLDataInDir method, should contain only files from test_nonxml_files.
+    QStringList nonxml = ProjectUtils::nonXMLDataInDir(projectDir);
+
+    nonxml.sort();
+    test_nonxml_files.sort();
+
+    EXPECT_EQ(nonxml.size(), 4);
+    EXPECT_EQ(test_nonxml_files, nonxml);
+
+    // removing nonxml files
+    EXPECT_TRUE(ProjectUtils::removeFiles(projectDir, nonxml) == true);
+
+    // checking that no files left
+    nonxml = ProjectUtils::nonXMLDataInDir(projectDir);
+    EXPECT_EQ(nonxml.size(), 0);
+}
+
+//! Test substraction of two lists (scenario: old files on disk vs new files).
+
+TEST_F(TestProjectUtils, test_stringListSubstraction)
+{
+    QStringList oldFiles = QStringList() << "a.int.gz"
+                                         << "b.int.gz"
+                                         << "c.int.gz";
+
+    QStringList newFiles = QStringList() << "d.int.gz"
+                                         << "b_renamed.int.gz"
+                                         << "c.int.gz"
+                                         << "a.int.gz";
+
+    QStringList diff = ProjectUtils::substract(oldFiles, newFiles);
+    EXPECT_EQ(diff.size(), 1);
+    EXPECT_EQ(diff.at(0), QString("b.int.gz"));
+}
diff --git a/Tests/UnitTests/GUI2/TestPropertyRepeater.h b/Tests/UnitTests/GUI2/TestPropertyRepeater.h
new file mode 100644
index 0000000000000000000000000000000000000000..7662900b44400a074de12543223d9d946a4fb6bf
--- /dev/null
+++ b/Tests/UnitTests/GUI2/TestPropertyRepeater.h
@@ -0,0 +1,189 @@
+#include "AxesItems.h"
+#include "IntensityDataItem.h"
+#include "PropertyRepeater.h"
+#include "SessionModel.h"
+#include "google_test.h"
+#include "item_constants.h"
+
+namespace
+{
+
+IntensityDataItem* createData(SessionModel& model)
+{
+    return dynamic_cast<IntensityDataItem*>(model.insertNewItem(Constants::IntensityDataType));
+}
+
+BasicAxisItem* createAxis(SessionModel& model)
+{
+    return dynamic_cast<BasicAxisItem*>(model.insertNewItem(Constants::BasicAxisType));
+}
+}
+
+class TestPropertyRepeater : public ::testing::Test
+{
+public:
+    ~TestPropertyRepeater();
+};
+
+TestPropertyRepeater::~TestPropertyRepeater() = default;
+
+//! Repeater handles two items.
+
+TEST_F(TestPropertyRepeater, test_twoItems)
+{
+    SessionModel model("test");
+
+    auto item1 = createAxis(model);
+    auto item2 = createAxis(model);
+
+    item1->setItemValue(BasicAxisItem::P_MAX, 2.0);
+    item2->setItemValue(BasicAxisItem::P_MAX, 3.0);
+
+    PropertyRepeater repeater;
+    repeater.addItem(item1);
+    repeater.addItem(item2);
+
+    // adding items to the repeater do not change values
+    EXPECT_EQ(item1->getItemValue(BasicAxisItem::P_MAX).toDouble(), 2.0);
+    EXPECT_EQ(item2->getItemValue(BasicAxisItem::P_MAX).toDouble(), 3.0);
+
+    // change of the value of one item leads to the change in another
+    item1->setItemValue(BasicAxisItem::P_MAX, 4.0);
+    EXPECT_EQ(item1->getItemValue(BasicAxisItem::P_MAX).toDouble(), 4.0);
+    EXPECT_EQ(item2->getItemValue(BasicAxisItem::P_MAX).toDouble(), 4.0);
+
+    // clearing repeater will stop update
+    repeater.clear();
+    item1->setItemValue(BasicAxisItem::P_MAX, 5.0);
+    EXPECT_EQ(item1->getItemValue(BasicAxisItem::P_MAX).toDouble(), 5.0);
+    EXPECT_EQ(item2->getItemValue(BasicAxisItem::P_MAX).toDouble(), 4.0);
+}
+
+//! Repeater handles three items.
+
+TEST_F(TestPropertyRepeater, test_threeItems)
+{
+    SessionModel model("test");
+
+    auto item1 = createAxis(model);
+    auto item2 = createAxis(model);
+    auto item3 = createAxis(model);
+
+    item1->setItemValue(BasicAxisItem::P_MAX, 1.0);
+    item2->setItemValue(BasicAxisItem::P_MAX, 2.0);
+    item3->setItemValue(BasicAxisItem::P_MAX, 3.0);
+
+    PropertyRepeater repeater;
+    repeater.addItem(item1);
+    repeater.addItem(item2);
+    repeater.addItem(item3);
+
+    // change of the value of one item leads to the change in two another
+    item1->setItemValue(BasicAxisItem::P_MAX, 4.0);
+    EXPECT_EQ(item1->getItemValue(BasicAxisItem::P_MAX).toDouble(), 4.0);
+    EXPECT_EQ(item2->getItemValue(BasicAxisItem::P_MAX).toDouble(), 4.0);
+    EXPECT_EQ(item3->getItemValue(BasicAxisItem::P_MAX).toDouble(), 4.0);
+}
+
+//! Checks if item with active properties specified receives updates only for these properties.
+
+TEST_F(TestPropertyRepeater, test_filteredBroadcast)
+{
+    SessionModel model("test");
+
+    auto item1 = createAxis(model);
+    auto item2 = createAxis(model);
+    auto item3 = createAxis(model);
+
+    item1->setItemValue(BasicAxisItem::P_MIN, 0.0);
+    item2->setItemValue(BasicAxisItem::P_MIN, 0.0);
+    item3->setItemValue(BasicAxisItem::P_MIN, 0.0);
+    item1->setItemValue(BasicAxisItem::P_MAX, 1.0);
+    item2->setItemValue(BasicAxisItem::P_MAX, 1.0);
+    item3->setItemValue(BasicAxisItem::P_MAX, 1.0);
+
+    QStringList activeProperties = QStringList() << BasicAxisItem::P_MAX;
+    PropertyRepeater repeater;
+    repeater.addItem(item1);
+    repeater.addItem(item2, activeProperties);
+    repeater.addItem(item3);
+
+    // changing two properties in item2 and checkig that only P_MAX changed in item1 and item3
+    item2->setItemValue(BasicAxisItem::P_MIN, 2.0);
+    item2->setItemValue(BasicAxisItem::P_MAX, 3.0);
+
+    EXPECT_EQ(item1->getItemValue(BasicAxisItem::P_MIN).toDouble(), 0.0);
+    EXPECT_EQ(item3->getItemValue(BasicAxisItem::P_MIN).toDouble(), 0.0);
+    EXPECT_EQ(item1->getItemValue(BasicAxisItem::P_MAX).toDouble(), 3.0);
+    EXPECT_EQ(item3->getItemValue(BasicAxisItem::P_MAX).toDouble(), 3.0);
+}
+
+TEST_F(TestPropertyRepeater, test_filteredReceive)
+{
+    SessionModel model("test");
+
+    auto item1 = createAxis(model);
+    auto item2 = createAxis(model);
+    auto item3 = createAxis(model);
+
+    item1->setItemValue(BasicAxisItem::P_MIN, 0.0);
+    item2->setItemValue(BasicAxisItem::P_MIN, 0.0);
+    item3->setItemValue(BasicAxisItem::P_MIN, 0.0);
+    item1->setItemValue(BasicAxisItem::P_MAX, 1.0);
+    item2->setItemValue(BasicAxisItem::P_MAX, 1.0);
+    item3->setItemValue(BasicAxisItem::P_MAX, 1.0);
+
+    QStringList activeProperties = QStringList() << BasicAxisItem::P_MAX;
+    PropertyRepeater repeater;
+    repeater.addItem(item1);
+    repeater.addItem(item2, activeProperties);
+    repeater.addItem(item3);
+
+    // changing two properties in item1 and checking that P_MIN of item2 remain intact
+    item1->setItemValue(BasicAxisItem::P_MIN, 2.0);
+    item1->setItemValue(BasicAxisItem::P_MAX, 3.0);
+
+    EXPECT_EQ(item2->getItemValue(BasicAxisItem::P_MIN).toDouble(), 0.0);
+    EXPECT_EQ(item2->getItemValue(BasicAxisItem::P_MAX).toDouble(), 3.0); // remain intact
+    EXPECT_EQ(item3->getItemValue(BasicAxisItem::P_MIN).toDouble(), 2.0);
+    EXPECT_EQ(item3->getItemValue(BasicAxisItem::P_MAX).toDouble(), 3.0);
+}
+
+//! Checking repeater in "repeat childs properties" mode
+
+TEST_F(TestPropertyRepeater, test_repeatAll)
+{
+    SessionModel model("test");
+
+    auto item1 = createData(model);
+    auto item2 = createData(model);
+
+    item1->xAxisItem()->setItemValue(BasicAxisItem::P_MAX, 2.0);
+    item2->xAxisItem()->setItemValue(BasicAxisItem::P_MAX, 3.0);
+
+    const bool repeat_child_properties = true;
+    PropertyRepeater repeater(nullptr, repeat_child_properties);
+    repeater.addItem(item1);
+    repeater.addItem(item2);
+
+    // adding items to the repeater do not change values
+    EXPECT_EQ(item1->getItemValue(IntensityDataItem::P_IS_INTERPOLATED).toBool(), true);
+    EXPECT_EQ(item2->getItemValue(IntensityDataItem::P_IS_INTERPOLATED).toBool(), true);
+    EXPECT_EQ(item1->getUpperX(), 2.0);
+    EXPECT_EQ(item2->getUpperX(), 3.0);
+
+    // change of the value of one item leads to the change in another
+    item1->xAxisItem()->setItemValue(BasicAxisItem::P_MAX, 4.0);
+    EXPECT_EQ(item1->getUpperX(), 4.0);
+    EXPECT_EQ(item2->getUpperX(), 4.0);
+
+    item1->setItemValue(IntensityDataItem::P_IS_INTERPOLATED, false);
+    EXPECT_EQ(item1->getItemValue(IntensityDataItem::P_IS_INTERPOLATED).toBool(), false);
+    EXPECT_EQ(item2->getItemValue(IntensityDataItem::P_IS_INTERPOLATED).toBool(), false);
+
+    // clearing repeater will stop update
+    repeater.clear();
+    item1->xAxisItem()->setItemValue(BasicAxisItem::P_MAX, 5.0);
+    EXPECT_EQ(item1->getUpperX(), 5.0);
+    EXPECT_EQ(item2->getUpperX(), 4.0);
+}
diff --git a/Tests/UnitTests/GUI2/TestProxyModelStrategy.h b/Tests/UnitTests/GUI2/TestProxyModelStrategy.h
new file mode 100644
index 0000000000000000000000000000000000000000..875d5f9641deb70cec171618a61eba14ecdb0641
--- /dev/null
+++ b/Tests/UnitTests/GUI2/TestProxyModelStrategy.h
@@ -0,0 +1,180 @@
+#include "google_test.h"
+#include "ComponentProxyModel.h"
+#include "ComponentProxyStrategy.h"
+#include "FormFactorItems.h"
+#include "ModelUtils.h"
+#include "ParticleItem.h"
+#include "ProxyModelStrategy.h"
+#include "SessionModel.h"
+#include "VectorItem.h"
+#include "item_constants.h"
+#include <QSignalSpy>
+
+class TestProxyModelStrategy : public ::testing::Test
+{
+public:
+    ~TestProxyModelStrategy();
+};
+
+TestProxyModelStrategy::~TestProxyModelStrategy() = default;
+
+//! Checking the mapping in the case of PropertyItem inserted in the source.
+
+TEST_F(TestProxyModelStrategy, test_identityStrategy)
+{
+    SessionModel model("TestModel");
+    ComponentProxyModel proxy;
+    IndentityProxyStrategy strategy;
+
+    EXPECT_EQ(strategy.sourceToProxy().size(), 0);
+    EXPECT_EQ(strategy.proxySourceParent().size(), 0);
+
+    // building the map of empty source
+    strategy.buildModelMap(&model, &proxy);
+    EXPECT_EQ(strategy.sourceToProxy().size(), 0);
+    EXPECT_EQ(strategy.proxySourceParent().size(), 0);
+
+    // building map when simple item
+    SessionItem* item = model.insertNewItem(Constants::PropertyType);
+    strategy.buildModelMap(&model, &proxy);
+    EXPECT_EQ(strategy.sourceToProxy().size(), 2);
+    EXPECT_EQ(strategy.proxySourceParent().size(), 2);
+
+    // Checking of persistent indices of source and proxy
+    auto it = strategy.sourceToProxy().begin();
+    // index of source, col=0
+    EXPECT_EQ(it.key().row(), 0);
+    EXPECT_EQ(it.key().column(), 0);
+    EXPECT_EQ(it.key().internalPointer(), item);
+    // index of proxy, col=0
+    EXPECT_EQ(it.value().row(), 0);
+    EXPECT_EQ(it.value().column(), 0);
+    EXPECT_EQ(it.value().internalPointer(), item);
+    ++it;
+    // index of source, col=1
+    EXPECT_EQ(it.key().row(), 0);
+    EXPECT_EQ(it.key().column(), 1);
+    EXPECT_EQ(it.key().internalPointer(), item);
+    // index of proxy, col=1
+    EXPECT_EQ(it.value().row(), 0);
+    EXPECT_EQ(it.value().column(), 1);
+    EXPECT_EQ(it.value().internalPointer(), item);
+
+    // Checking parent of proxy
+    it = strategy.proxySourceParent().begin();
+    EXPECT_EQ(it.key().row(), 0);
+    EXPECT_EQ(it.key().column(), 0);
+    EXPECT_EQ(it.key().internalPointer(), item);
+    EXPECT_TRUE(it.value() == QModelIndex());
+}
+
+//! Checking the mapping in the case of ParticleItem inserted in the source.
+
+TEST_F(TestProxyModelStrategy, test_identityStrategyParticle)
+{
+    SessionModel model("TestModel");
+    ComponentProxyModel proxy;
+    IndentityProxyStrategy strategy;
+
+    SessionItem* item = model.insertNewItem(Constants::ParticleType);
+
+    // building the map of source
+    strategy.buildModelMap(&model, &proxy);
+    SessionItem* group = item->getItem(ParticleItem::P_FORM_FACTOR);
+    SessionItem* ffItem = item->getGroupItem(ParticleItem::P_FORM_FACTOR);
+    EXPECT_TRUE(ffItem->parent() == group);
+    EXPECT_TRUE(ffItem->modelType() == Constants::CylinderType);
+
+    // Checking "real" parent of proxy index related to form factor.
+    // For identity model we are testing, it has to be just group property.
+    auto ffProxyIndex = strategy.sourceToProxy().value(model.indexOfItem(ffItem));
+    auto parentOfProxy = strategy.proxySourceParent().value(ffProxyIndex);
+    EXPECT_TRUE(parentOfProxy == model.indexOfItem(group));
+
+    // Checking "real" parent of Cylinders radius. It has to be CylinderItem
+    SessionItem* radiusItem = ffItem->getItem(CylinderItem::P_RADIUS);
+    auto radiusProxyIndex = strategy.sourceToProxy().value(model.indexOfItem(radiusItem));
+    parentOfProxy = strategy.proxySourceParent().value(radiusProxyIndex);
+    EXPECT_TRUE(parentOfProxy == model.indexOfItem(ffItem));
+}
+
+//! Checking the mapping of ComponentProxyStrategy in the case of ParticleItem inserted in
+//! the source.
+
+TEST_F(TestProxyModelStrategy, test_componentStrategyParticle)
+{
+    SessionModel model("TestModel");
+    ComponentProxyModel proxy;
+    ComponentProxyStrategy strategy;
+
+    SessionItem* item = model.insertNewItem(Constants::ParticleType);
+
+    // building the map of  source
+    strategy.buildModelMap(&model, &proxy);
+    SessionItem* group = item->getItem(ParticleItem::P_FORM_FACTOR);
+    SessionItem* ffItem = item->getGroupItem(ParticleItem::P_FORM_FACTOR);
+    EXPECT_TRUE(ffItem->parent() == group);
+    EXPECT_TRUE(ffItem->modelType() == Constants::CylinderType);
+
+    // original indices
+    QModelIndex particleIndex = model.indexOfItem(item);
+    QModelIndex groupIndex = model.indexOfItem(group);
+    QModelIndex ffIndex = model.indexOfItem(ffItem);
+    QModelIndex radiusIndex = model.indexOfItem(ffItem->getItem(CylinderItem::P_RADIUS));
+
+    // proxy indices
+    QModelIndex particleProxyIndex = strategy.sourceToProxy().value(particleIndex);
+    QModelIndex groupProxyIndex = strategy.sourceToProxy().value(groupIndex);
+    QModelIndex ffProxyIndex = strategy.sourceToProxy().value(ffIndex);
+    QModelIndex radiusProxyIndex = strategy.sourceToProxy().value(radiusIndex);
+    EXPECT_TRUE(particleProxyIndex.isValid());
+    EXPECT_TRUE(groupProxyIndex.isValid());
+    EXPECT_TRUE(ffProxyIndex.isValid() == false); // ff is excluded from hierarchy
+    EXPECT_TRUE(radiusProxyIndex.isValid());
+
+    // Checking "real" parents of indices
+    EXPECT_TRUE(strategy.proxySourceParent().value(ffProxyIndex) == QModelIndex());
+    EXPECT_TRUE(strategy.proxySourceParent().value(radiusProxyIndex) == groupIndex);
+    EXPECT_TRUE(strategy.proxySourceParent().value(groupProxyIndex) == particleIndex);
+}
+
+//! Checking setRootIndex: proxy model should contain only items corresponding
+//! to rootIndex and its children.
+
+TEST_F(TestProxyModelStrategy, test_setRootIndex)
+{
+    SessionModel model("TestModel");
+    ComponentProxyModel proxy;
+    ComponentProxyStrategy strategy;
+
+    SessionItem* item = model.insertNewItem(Constants::ParticleType);
+    SessionItem* group = item->getItem(ParticleItem::P_FORM_FACTOR);
+    SessionItem* ffItem = item->getGroupItem(ParticleItem::P_FORM_FACTOR);
+
+    QModelIndex particleIndex = model.indexOfItem(item);
+    QModelIndex groupIndex = model.indexOfItem(group);
+    QModelIndex ffIndex = model.indexOfItem(ffItem);
+    QModelIndex radiusIndex = model.indexOfItem(ffItem->getItem(CylinderItem::P_RADIUS));
+
+    // building the map of  source, groupItem will be rootIndex
+    strategy.setRootIndex(model.indexOfItem(group));
+    strategy.buildModelMap(&model, &proxy);
+
+    // proxy indices
+    QModelIndex particleProxyIndex = strategy.sourceToProxy().value(particleIndex);
+    QModelIndex groupProxyIndex = strategy.sourceToProxy().value(groupIndex);
+    QModelIndex ffProxyIndex = strategy.sourceToProxy().value(ffIndex);
+    QModelIndex radiusProxyIndex = strategy.sourceToProxy().value(radiusIndex);
+    EXPECT_TRUE(particleProxyIndex.isValid() == false); // particle is not in a tree
+    EXPECT_TRUE(groupProxyIndex.isValid());
+    EXPECT_EQ(groupProxyIndex.row(), 0);
+    EXPECT_EQ(groupProxyIndex.column(), 0);
+    EXPECT_TRUE(groupProxyIndex.parent() == QModelIndex());
+    EXPECT_TRUE(ffProxyIndex.isValid() == false); // ff is excluded from hierarchy
+    EXPECT_TRUE(radiusProxyIndex.isValid());
+
+    // checking that new parent of groupItem is root
+    EXPECT_TRUE(strategy.proxySourceParent().value(groupProxyIndex) == QModelIndex());
+    EXPECT_TRUE(strategy.proxySourceParent().value(ffProxyIndex) == QModelIndex());
+    EXPECT_TRUE(strategy.proxySourceParent().value(radiusProxyIndex) == groupIndex);
+}
diff --git a/Tests/UnitTests/GUI2/TestSaveService.h b/Tests/UnitTests/GUI2/TestSaveService.h
new file mode 100644
index 0000000000000000000000000000000000000000..673f6bdfb92d8a6861b5fc5e7e74d76facbc1707
--- /dev/null
+++ b/Tests/UnitTests/GUI2/TestSaveService.h
@@ -0,0 +1,225 @@
+#include "google_test.h"
+#include "ApplicationModels.h"
+#include "AutosaveController.h"
+#include "GUIHelpers.h"
+#include "InstrumentItem.h"
+#include "InstrumentModel.h"
+#include "IntensityDataItem.h"
+#include "JobItemUtils.h"
+#include "ProjectUtils.h"
+#include "RealDataItem.h"
+#include "RealDataModel.h"
+#include "SaveService.h"
+#include "projectdocument.h"
+#include "test_utils.h"
+#include <QSignalSpy>
+
+class TestSaveService : public ::testing::Test
+{
+public:
+    ~TestSaveService();
+
+    // helper method to modify something in a model
+    void modify_models(ApplicationModels* models)
+    {
+        auto instrument = models->instrumentModel()->instrumentItem();
+        instrument->setItemValue(InstrumentItem::P_IDENTIFIER, GUIHelpers::createUuid());
+    }
+};
+
+TestSaveService::~TestSaveService() = default;
+
+//! Testing AutosaveController. It watches ProjectDocument and sends autosaveRequest() when
+//! number of document changes has been accumulated.
+
+TEST_F(TestSaveService, test_autoSaveController)
+{
+    const QString projectDir("test_autoSaveController");
+    TestUtils::create_dir(projectDir);
+
+    const QString projectFileName(projectDir + "/document.pro");
+
+    const int autosave_time(100);
+
+    ApplicationModels models;
+    std::unique_ptr<ProjectDocument> document(new ProjectDocument);
+    document->setApplicationModels(&models);
+    document->save(projectFileName);
+
+    // setting up autosave
+    AutosaveController autosave;
+    autosave.setAutosaveTime(autosave_time);
+    autosave.setDocument(document.get());
+
+    // checking proposed autosave directory
+    EXPECT_EQ(autosave.autosaveDir(), QString(projectDir + "/autosave"));
+    EXPECT_EQ(autosave.autosaveName(), QString(projectDir + "/autosave/document.pro"));
+
+    QSignalSpy spyAutosave(&autosave, SIGNAL(autosaveRequest()));
+
+    // modify document once and check that autosave directory was created
+    modify_models(&models);
+    EXPECT_TRUE(document->isModified());
+    EXPECT_TRUE(spyAutosave.wait(autosave_time * 3));
+    EXPECT_EQ(spyAutosave.count(), 1);
+    EXPECT_TRUE(ProjectUtils::exists(autosave.autosaveDir()));
+
+    // saving document and checking that autosave is not triggered
+    document->save(projectFileName);
+    EXPECT_FALSE(document->isModified());
+    EXPECT_EQ(spyAutosave.count(), 1);
+
+    // modify several times and check than autosave was triggered only once
+    for (size_t i = 0; i < 10; ++i)
+        modify_models(&models);
+
+    EXPECT_TRUE(spyAutosave.wait(autosave_time * 3));
+    EXPECT_EQ(spyAutosave.count(), 2);
+
+    // remove autosave dir
+    autosave.removeAutosaveDir();
+    EXPECT_FALSE(ProjectUtils::exists(autosave.autosaveDir()));
+}
+
+//! AutosaveController shouldn't trigger autosaveRequest() if document has no name.
+
+TEST_F(TestSaveService, test_autoSaveControllerNewDocument)
+{
+    ApplicationModels models;
+    std::unique_ptr<ProjectDocument> document(new ProjectDocument);
+    document->setApplicationModels(&models);
+
+    const int autosave_time(100);
+
+    AutosaveController autosave;
+    autosave.setAutosaveTime(autosave_time);
+    autosave.setDocument(document.get());
+
+    QSignalSpy spyAutosave(&autosave, SIGNAL(autosaveRequest()));
+
+    modify_models(&models);
+    EXPECT_FALSE(spyAutosave.wait(autosave_time * 3));
+    EXPECT_EQ(spyAutosave.count(), 0);
+}
+
+//! Testing SaveService on simple documents (no heavy data).
+//! SaveService should be able to save project file and send documentSaved() signal.
+
+TEST_F(TestSaveService, test_saveService)
+{
+    const QString projectDir("test_saveService");
+    TestUtils::create_dir(projectDir);
+    const QString projectFileName(projectDir + "/document.pro");
+
+    ApplicationModels models;
+    std::unique_ptr<ProjectDocument> document(new ProjectDocument);
+    document->setApplicationModels(&models);
+    modify_models(&models);
+
+    EXPECT_FALSE(ProjectUtils::exists(projectFileName));
+
+    SaveService service;
+    QSignalSpy spySaveService(&service, SIGNAL(projectSaved()));
+
+    service.setDocument(document.get());
+    service.save(projectFileName);
+
+    EXPECT_EQ(spySaveService.count(), 1);
+    EXPECT_TRUE(ProjectUtils::exists(projectFileName));
+
+    // after save, document should be in non-modified state, project file name should be updated
+    EXPECT_EQ(document->projectFileName(), projectFileName);
+    EXPECT_FALSE(document->isModified());
+}
+
+//! Testing SaveService on documents having nonXML data.
+//! SaveService should be able to save project file (in main thread) and project nonXML
+//! in second thread.
+
+TEST_F(TestSaveService, test_saveServiceWithData)
+{
+    const QString projectDir("test_saveServiceWithData");
+    TestUtils::create_dir(projectDir);
+    const QString projectFileName(projectDir + "/document.pro");
+
+    ApplicationModels models;
+    RealDataItem* realData = dynamic_cast<RealDataItem*>(
+        models.realDataModel()->insertNewItem(Constants::RealDataType));
+    Q_ASSERT(realData);
+    IntensityDataItem* intensityItem = realData->intensityDataItem();
+    JobItemUtils::createDefaultDetectorMap(intensityItem,
+                                           models.instrumentModel()->instrumentItem());
+    intensityItem->setItemValue(IntensityDataItem::P_FILE_NAME, "realdata.int.gz");
+
+    std::unique_ptr<ProjectDocument> document(new ProjectDocument);
+    document->setApplicationModels(&models);
+
+    EXPECT_FALSE(ProjectUtils::exists(projectFileName));
+
+    SaveService service;
+    QSignalSpy spySaveService(&service, SIGNAL(projectSaved()));
+
+    service.setDocument(document.get());
+    service.save(projectFileName);
+
+    spySaveService.wait(100); // waiting saving in a thread is complete
+
+    EXPECT_EQ(spySaveService.count(), 1);
+    EXPECT_TRUE(ProjectUtils::exists(projectFileName));
+    EXPECT_TRUE(ProjectUtils::exists(projectDir + "/realdata.int.gz"));
+    EXPECT_FALSE(document->isModified());
+}
+
+//! Testing SaveService when autosave is enabled.
+
+TEST_F(TestSaveService, test_autosaveEnabled)
+{
+    const QString projectDir("test_autosaveEnabled");
+    TestUtils::create_dir(projectDir);
+    const QString projectFileName(projectDir + "/document.pro");
+
+    ApplicationModels models;
+    RealDataItem* realData = dynamic_cast<RealDataItem*>(
+        models.realDataModel()->insertNewItem(Constants::RealDataType));
+    IntensityDataItem* intensityItem = realData->intensityDataItem();
+    JobItemUtils::createDefaultDetectorMap(intensityItem,
+                                           models.instrumentModel()->instrumentItem());
+    intensityItem->setItemValue(IntensityDataItem::P_FILE_NAME, "realdata.int.gz");
+
+    std::unique_ptr<ProjectDocument> document(new ProjectDocument(projectFileName));
+    document->setApplicationModels(&models);
+
+    EXPECT_FALSE(document->isModified());
+
+    SaveService service;
+    service.setAutosaveEnabled(true);
+    const int autosave_time(100);
+    service.setAutosaveTime(autosave_time);
+    service.setDocument(document.get());
+
+    QSignalSpy spySaveService(&service, SIGNAL(projectSaved()));
+    service.save(projectFileName);
+
+    spySaveService.wait(autosave_time * 3); // waiting saving in a thread is complete
+    EXPECT_EQ(spySaveService.count(), 1);
+    EXPECT_FALSE(document->isModified());
+    EXPECT_TRUE(ProjectUtils::exists(projectDir + "/document.pro"));
+    EXPECT_TRUE(ProjectUtils::exists(projectDir + "/realdata.int.gz"));
+
+    // modify several times and check SaveService signals
+    for (size_t i = 0; i < 10; ++i)
+        modify_models(&models);
+
+    EXPECT_TRUE(document->isModified());
+
+    spySaveService.wait(autosave_time * 3); // waiting saving in a thread is complete
+    EXPECT_EQ(spySaveService.count(), 2);
+
+    EXPECT_TRUE(ProjectUtils::exists(projectDir + "/autosave/document.pro"));
+    EXPECT_TRUE(ProjectUtils::exists(projectDir + "/autosave/realdata.int.gz"));
+
+    // after autosave the project has to be still in modified state
+    EXPECT_TRUE(document->isModified());
+    // after autosave, project file name should remain the same
+    EXPECT_EQ(document->projectFileName(), projectFileName);
+}
diff --git a/Tests/UnitTests/GUI2/TestSessionItem.h b/Tests/UnitTests/GUI2/TestSessionItem.h
new file mode 100644
index 0000000000000000000000000000000000000000..827d967d3c72d6e5015c1fcea439b61cc04b3ea5
--- /dev/null
+++ b/Tests/UnitTests/GUI2/TestSessionItem.h
@@ -0,0 +1,164 @@
+#include "google_test.h"
+#include "SessionItem.h"
+#include "SessionModel.h"
+
+class TestSessionItem : public ::testing::Test
+{
+public:
+    ~TestSessionItem();
+    void verify_get_item(SessionItem* item, const QString& tag, QVector<SessionItem*> list)
+    {
+        if (list.size() > 0)
+            EXPECT_TRUE(item->getItem(tag) == list[0]);
+        else
+            EXPECT_TRUE(item->getItem(tag) == nullptr);
+        auto items = item->getItems(tag);
+        EXPECT_TRUE(items.size() == list.size());
+        EXPECT_TRUE(items == list);
+        EXPECT_TRUE(item->getItem(tag, -1) == nullptr);
+        EXPECT_TRUE(item->getItem(tag, list.size()) == nullptr);
+        for (int i = 0; i < list.size(); i++) {
+            EXPECT_TRUE(item->getItem(tag, i) == list[i]);
+        }
+    }
+};
+
+TestSessionItem::~TestSessionItem() = default;
+
+TEST_F(TestSessionItem, test_constructor)
+{
+    const QString modeltype = "This is the model type";
+    SessionItem* item = new SessionItem(modeltype);
+    EXPECT_TRUE(item->modelType() == modeltype);
+    EXPECT_TRUE(item->model() == nullptr);
+    EXPECT_TRUE(item->parent() == nullptr);
+    // TODO add some more tests for children, roles, tags ...
+}
+
+TEST_F(TestSessionItem, test_tags)
+{
+    const QString modeltype = "This is the model type";
+    const QString tag1 = "TAG1";
+    const QString tag2 = "TAG2";
+    const QString tag3 = "TAG3";
+    const QString tag4 = "TAG4";
+    SessionItem* item = new SessionItem(modeltype);
+    QVector<SessionItem*> items;
+    for (int i = 0; i < 10; i++)
+        items.append(new SessionItem(modeltype));
+
+    // before using a tag, it must be registered
+    EXPECT_TRUE(item->registerTag(tag1));
+
+    // register twice returns false
+    EXPECT_TRUE(item->registerTag(tag1) == false);
+
+    // register empty string returns false
+    EXPECT_TRUE(item->registerTag("") == false);
+
+    // now we insert one element at the beginning
+    EXPECT_TRUE(item->insertItem(0, items[0], tag1));
+
+    // insertion out of range is forbidden
+    EXPECT_TRUE(item->insertItem(-1, items[0], tag1) == false);
+    EXPECT_TRUE(item->insertItem(2, items[0], tag1) == false);
+
+    // double insertion is forbidden
+    EXPECT_TRUE(item->insertItem(0, items[0], tag1) == false);
+
+    // we try to access tagged items
+    verify_get_item(item, tag1, items.mid(0, 1));
+
+    // nullptr is not allowed
+    EXPECT_TRUE(item->insertItem(1, nullptr, tag1) == false);
+    verify_get_item(item, tag1, items.mid(0, 1));
+
+    // LIMITS
+    // register tag with limit 0 - 1
+    EXPECT_TRUE(item->registerTag(tag2, 0, 1));
+
+    EXPECT_TRUE(item->insertItem(0, items[1], tag2));
+    verify_get_item(item, tag2, items.mid(1, 1));
+    EXPECT_TRUE(item->insertItem(1, items[1], tag2) == false);
+
+    // register tag with limit 0 - 3 (using item 2 - 5)
+    EXPECT_TRUE(item->registerTag(tag3, 0, 4));
+
+    // add four items
+    for (int i = 0; i < 4; i++) {
+        EXPECT_TRUE(item->insertItem(i, items[2 + i], tag3));
+        verify_get_item(item, tag3, items.mid(2, i + 1));
+    }
+
+    // the fifth should fail
+    EXPECT_TRUE(item->insertItem(0, items[6], tag3) == false);
+
+    // items should be unchanged
+    verify_get_item(item, tag3, items.mid(2, 4));
+
+    // register tag with limit 4 - 4 add items to fill up limit
+    EXPECT_TRUE(item->registerTag(tag4, 4, 4));
+
+    // add four items
+    for (int i = 0; i < 4; i++) {
+        EXPECT_TRUE(item->insertItem(i, items[6 + i], tag4));
+        verify_get_item(item, tag4, items.mid(6, i + 1));
+    }
+    EXPECT_TRUE(item->insertItem(0, items[6], tag4) == false);
+
+    // REMOVAL
+
+    // tag4 can not be removed
+    SessionItem* last = item->takeItem(3, tag4);
+    EXPECT_TRUE(last == nullptr);
+    verify_get_item(item, tag4, items.mid(6, 4));
+
+    // remove all from tag3, checking access of tag4
+    for (int i = 0; i < 4; i++) {
+        last = item->takeItem(3 - i, tag3);
+        EXPECT_TRUE(last == items[5 - i]);
+        verify_get_item(item, tag3, items.mid(2, 3 - i));
+        verify_get_item(item, tag4, items.mid(6, 4));
+    }
+
+    delete item;
+}
+
+TEST_F(TestSessionItem, test_data_roles)
+{
+    SessionItem* item = new SessionItem("Some model type");
+    item->setData(Qt::DisplayRole, 1234);
+    EXPECT_TRUE(item->data(Qt::DisplayRole) == 1234);
+    EXPECT_TRUE(item->data(Qt::EditRole) == 1234);
+    item->setData(Qt::EditRole, 5432);
+    EXPECT_TRUE(item->data(Qt::DisplayRole) == 5432);
+    EXPECT_TRUE(item->data(Qt::EditRole) == 5432);
+    for (int i = 0; i < 10; i++) {
+        EXPECT_TRUE(item->data(SessionFlags::EndSessionRoles + i).isValid() == false);
+        item->setData(SessionFlags::EndSessionRoles + i, i);
+        EXPECT_TRUE(item->data(SessionFlags::EndSessionRoles + i) == i);
+    }
+}
+
+TEST_F(TestSessionItem, test_model_types)
+{
+    const QString model1 = "modeltype 1";
+    const QString model2 = "modeltype 2";
+    const QString model3 = "modeltype 3";
+    const QString model4 = "modeltype 4";
+    const QString model5 = "modeltype 5";
+
+    SessionItem* item = new SessionItem("modeltype does not matter");
+    EXPECT_TRUE(item->registerTag("Tag1", 0, -1, QStringList() << model1 << model2));
+    EXPECT_TRUE(item->insertItem(0, new SessionItem(model1), "Tag1"));
+    EXPECT_TRUE(item->insertItem(0, new SessionItem(model2), "Tag1"));
+    EXPECT_TRUE(item->insertItem(0, new SessionItem(model3), "Tag1") == false);
+    EXPECT_TRUE(item->insertItem(0, new SessionItem(model4), "Tag1") == false);
+    EXPECT_TRUE(item->insertItem(0, new SessionItem(model5), "Tag1") == false);
+    EXPECT_TRUE(item->registerTag("Tag2", 0, -1, QStringList() << model3 << model4 << model5));
+    EXPECT_TRUE(item->insertItem(0, new SessionItem(model1), "Tag2") == false);
+    EXPECT_TRUE(item->insertItem(0, new SessionItem(model2), "Tag2") == false);
+    EXPECT_TRUE(item->insertItem(0, new SessionItem(model3), "Tag2"));
+    EXPECT_TRUE(item->insertItem(0, new SessionItem(model4), "Tag2"));
+    EXPECT_TRUE(item->insertItem(0, new SessionItem(model5), "Tag2"));
+}
diff --git a/Tests/UnitTests/GUI2/TestSessionItemController.h b/Tests/UnitTests/GUI2/TestSessionItemController.h
new file mode 100644
index 0000000000000000000000000000000000000000..1a29d082e1826a9ea68db04cab3896c515c9ade4
--- /dev/null
+++ b/Tests/UnitTests/GUI2/TestSessionItemController.h
@@ -0,0 +1,273 @@
+#include "google_test.h"
+#include "AxesItems.h"
+#include "SessionItemController.h"
+#include "SessionModel.h"
+#include "TestSessionItemControllerHelper.h"
+#include <QObject>
+
+class TestSessionItemController : public ::testing::Test
+{
+public:
+    ~TestSessionItemController();
+};
+
+TestSessionItemController::~TestSessionItemController() = default;
+
+//! Testing helper classes which will be used for controller testing.
+
+TEST_F(TestSessionItemController, test_InitialState)
+{
+    TestListener listener;
+    EXPECT_EQ(listener.m_onItemDestroyedCount, 0);
+    EXPECT_EQ(listener.m_onPropertyChangeCount, 0);
+
+    TestObject object(&listener);
+    EXPECT_TRUE(object.currentItem() == nullptr);
+    EXPECT_EQ(object.m_is_subscribed, false);
+
+    // setting null item
+    object.setItem(nullptr);
+    EXPECT_EQ(listener.m_onItemDestroyedCount, 0);
+    EXPECT_EQ(listener.m_onPropertyChangeCount, 0);
+    EXPECT_TRUE(object.currentItem() == nullptr);
+    EXPECT_EQ(object.m_is_subscribed, false);
+
+    object.setVisible(true);
+    EXPECT_TRUE(object.currentItem() == nullptr);
+    EXPECT_EQ(object.m_is_subscribed, false);
+}
+
+//! Setting item and doing nothing.
+
+TEST_F(TestSessionItemController, test_setItem)
+{
+    TestListener listener;
+    TestObject object(&listener);
+    SessionModel model("TestModel");
+    SessionItem* item = model.insertNewItem(Constants::BasicAxisType);
+
+    object.setItem(item);
+    EXPECT_EQ(object.currentItem(), item);
+    EXPECT_EQ(object.m_is_subscribed, false);
+    EXPECT_EQ(listener.m_onItemDestroyedCount, 0);
+    EXPECT_EQ(listener.m_onPropertyChangeCount, 0);
+}
+
+//! Setting item and subscribing to it.
+
+TEST_F(TestSessionItemController, test_setItemAndSubscribeItem)
+{
+    TestListener listener;
+    TestObject object(&listener);
+    SessionModel model("TestModel");
+    SessionItem* item = model.insertNewItem(Constants::BasicAxisType);
+
+    object.setItem(item);
+    object.setVisible(true);
+    EXPECT_EQ(object.currentItem(), item);
+    EXPECT_EQ(object.m_is_subscribed, true);
+    EXPECT_EQ(listener.m_onItemDestroyedCount, 0);
+    EXPECT_EQ(listener.m_onPropertyChangeCount, 0);
+}
+
+//! Setting item properties when widget is in hidden/shown state.
+
+TEST_F(TestSessionItemController, test_onPropertyChange)
+{
+    TestListener listener;
+    TestObject object(&listener);
+    SessionModel model("TestModel");
+    SessionItem* item = model.insertNewItem(Constants::BasicAxisType);
+
+    object.setItem(item);
+    EXPECT_EQ(object.currentItem(), item);
+    EXPECT_EQ(object.m_is_subscribed, false);
+
+    // changing item, should be no reaction
+    item->setItemValue(BasicAxisItem::P_MAX, 3.0);
+    EXPECT_EQ(listener.m_onItemDestroyedCount, 0);
+    EXPECT_EQ(listener.m_onPropertyChangeCount, 0);
+
+    // setting visible and changing item
+    object.setVisible(true);
+    EXPECT_EQ(object.m_is_subscribed, true);
+    item->setItemValue(BasicAxisItem::P_MAX, 4.0);
+    EXPECT_EQ(listener.m_onItemDestroyedCount, 0);
+    EXPECT_EQ(listener.m_onPropertyChangeCount, 1);
+
+    // same value, no change expected
+    item->setItemValue(BasicAxisItem::P_MAX, 4.0);
+    EXPECT_EQ(listener.m_onPropertyChangeCount, 1);
+
+    // new value
+    item->setItemValue(BasicAxisItem::P_MAX, 4.1);
+    EXPECT_EQ(listener.m_onPropertyChangeCount, 2);
+
+    // setting same item once again, setting visible, and then checking that no double subscription
+    object.setItem(item);
+    object.setVisible(true);
+    item->setItemValue(BasicAxisItem::P_MAX, 4.2);
+    EXPECT_EQ(listener.m_onPropertyChangeCount, 3);
+
+    // setting invisible and changing item, no reaction on item value change expected
+    object.setVisible(false);
+    EXPECT_EQ(object.m_is_subscribed, false);
+    item->setItemValue(BasicAxisItem::P_MAX, 5.0);
+    EXPECT_EQ(listener.m_onItemDestroyedCount, 0);
+    EXPECT_EQ(listener.m_onPropertyChangeCount, 3);
+}
+
+//! Deleting item when widget is visible.
+
+TEST_F(TestSessionItemController, test_onItemDestroyWidgetVisible)
+{
+    TestListener listener;
+    TestObject object(&listener);
+    SessionModel model("TestModel");
+    SessionItem* item = model.insertNewItem(Constants::BasicAxisType);
+
+    object.setItem(item);
+    object.setVisible(true);
+
+    item->setItemValue(BasicAxisItem::P_MAX, 4.0);
+    EXPECT_EQ(listener.m_onItemDestroyedCount, 0);
+    EXPECT_EQ(listener.m_onPropertyChangeCount, 1);
+
+    // item deletion should lead to automatic unsubscription
+    delete item->parent()->takeRow(0);
+    EXPECT_EQ(listener.m_onItemDestroyedCount, 1);
+    EXPECT_EQ(object.m_is_subscribed, false);
+    EXPECT_TRUE(object.currentItem() == nullptr);
+
+    object.setVisible(false);
+    EXPECT_EQ(listener.m_onItemDestroyedCount, 1);
+    EXPECT_EQ(object.m_is_subscribed, false);
+    EXPECT_TRUE(object.currentItem() == nullptr);
+}
+
+TEST_F(TestSessionItemController, test_onItemDestroyWidgetHidden)
+{
+    TestListener listener;
+    TestObject object(&listener);
+    SessionModel model("TestModel");
+    SessionItem* item = model.insertNewItem(Constants::BasicAxisType);
+
+    object.setItem(item);
+    object.setVisible(true);
+
+    item->setItemValue(BasicAxisItem::P_MAX, 4.0);
+    EXPECT_EQ(listener.m_onItemDestroyedCount, 0);
+    EXPECT_EQ(listener.m_onPropertyChangeCount, 1);
+
+    object.setVisible(false);
+
+    // Deleting item when widget is hidden.
+    // Widget itself shouldn't notice onItemDestroy because of hidden state.
+    // But the controller should notice, and unsubscribe the widget.
+
+    delete item->parent()->takeRow(0);
+    EXPECT_EQ(listener.m_onItemDestroyedCount, 0);
+    EXPECT_EQ(object.m_is_subscribed, false);
+    EXPECT_TRUE(object.currentItem() == nullptr);
+
+    object.setVisible(true);
+    EXPECT_EQ(listener.m_onItemDestroyedCount, 0);
+    EXPECT_EQ(object.m_is_subscribed, false);
+    EXPECT_TRUE(object.currentItem() == nullptr);
+}
+
+//! Typical scenario when one item follows the other.
+
+TEST_F(TestSessionItemController, test_onTwoItems)
+{
+    TestListener listener;
+    TestObject object(&listener);
+    SessionModel model("TestModel");
+    SessionItem* item1 = model.insertNewItem(Constants::BasicAxisType);
+    SessionItem* item2 = model.insertNewItem(Constants::BasicAxisType);
+
+    object.setItem(item1);
+    EXPECT_EQ(object.currentItem(), item1);
+    object.setVisible(true);
+
+    item1->setItemValue(BasicAxisItem::P_MAX, 4.0);
+    EXPECT_EQ(listener.m_onItemDestroyedCount, 0);
+    EXPECT_EQ(listener.m_onPropertyChangeCount, 1);
+
+    // changing the item
+    object.setItem(item2);
+    EXPECT_EQ(object.currentItem(), item2);
+    // since by design setting the item doesn't lead to automatic subscription, we have to subscribe
+    object.setVisible(true);
+
+    // changing the value of previous item, widget shouldn't react
+    item1->setItemValue(BasicAxisItem::P_MAX, 5.0);
+    EXPECT_EQ(listener.m_onItemDestroyedCount, 0);
+    EXPECT_EQ(listener.m_onPropertyChangeCount, 1);
+
+    // changing the value of new item, widget should react
+    item2->setItemValue(BasicAxisItem::P_MAX, 6.0);
+    EXPECT_EQ(listener.m_onPropertyChangeCount, 2);
+}
+
+//! Settings two items one after another, when widget stays hidden
+
+TEST_F(TestSessionItemController, test_onTwoItemsWhenHidden)
+{
+    TestListener listener;
+    TestObject object(&listener);
+    SessionModel model("TestModel");
+    SessionItem* item1 = model.insertNewItem(Constants::BasicAxisType);
+    SessionItem* item2 = model.insertNewItem(Constants::BasicAxisType);
+
+    object.setVisible(false);
+
+    object.setItem(item1);
+    EXPECT_EQ(object.currentItem(), item1);
+
+    item1->setItemValue(BasicAxisItem::P_MAX, 4.0);
+    EXPECT_EQ(listener.m_onItemDestroyedCount, 0);
+    EXPECT_EQ(listener.m_onPropertyChangeCount, 0);
+
+    // changing the item
+    object.setItem(item2);
+    EXPECT_EQ(object.currentItem(), item2);
+
+    // changing the value of previous item, widget shouldn't react
+    item1->setItemValue(BasicAxisItem::P_MAX, 5.0);
+    EXPECT_EQ(listener.m_onItemDestroyedCount, 0);
+    EXPECT_EQ(listener.m_onPropertyChangeCount, 0);
+
+    // changing the value of new item, widget shouldn't react
+    item2->setItemValue(BasicAxisItem::P_MAX, 6.0);
+    EXPECT_EQ(listener.m_onPropertyChangeCount, 0);
+}
+
+//! Deleting the widget when item still alive.
+
+TEST_F(TestSessionItemController, test_deleteWidget)
+{
+    TestListener listener;
+    TestObject* object = new TestObject(&listener);
+    SessionModel model("TestModel");
+    SessionItem* item = model.insertNewItem(Constants::BasicAxisType);
+
+    object->setItem(item);
+    object->setVisible(true);
+
+    item->setItemValue(BasicAxisItem::P_MAX, 4.0);
+
+    EXPECT_EQ(listener.m_onItemDestroyedCount, 0);
+    EXPECT_EQ(listener.m_onPropertyChangeCount, 1);
+    EXPECT_EQ(listener.m_onWidgetDestroyed, 0);
+
+    delete object;
+
+    EXPECT_EQ(listener.m_onItemDestroyedCount, 0);
+    EXPECT_EQ(listener.m_onPropertyChangeCount, 1);
+    EXPECT_EQ(listener.m_onWidgetDestroyed, 1);
+
+    item->setItemValue(BasicAxisItem::P_MAX, 4.1);
+    EXPECT_EQ(listener.m_onItemDestroyedCount, 0);
+    EXPECT_EQ(listener.m_onPropertyChangeCount, 1);
+}
diff --git a/Tests/UnitTests/GUI2/TestSessionItemControllerHelper.cpp b/Tests/UnitTests/GUI2/TestSessionItemControllerHelper.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..014174092d3c35aa559605571a25cb3fe4c458bc
--- /dev/null
+++ b/Tests/UnitTests/GUI2/TestSessionItemControllerHelper.cpp
@@ -0,0 +1,48 @@
+#include "TestSessionItemControllerHelper.h"
+#include "SessionItem.h"
+#include "SessionItemController.h"
+
+TestListener::TestListener()
+    : m_onItemDestroyedCount(0), m_onPropertyChangeCount(0), m_onWidgetDestroyed(0)
+{
+}
+
+void TestListener::clear()
+{
+    m_onItemDestroyedCount = 0;
+    m_onPropertyChangeCount = 0;
+    m_onWidgetDestroyed = 0;
+}
+
+TestObject::TestObject(TestListener* listener)
+    : m_listener(listener), m_controller(new SessionItemController(this)), m_is_subscribed(false)
+{
+    m_controller->setSubscribeCallback([this]() { onSubscribe(); });
+    m_controller->setUnsubscribeCallback([this]() { onUnsubscribe(); });
+}
+
+TestObject::~TestObject() { m_listener->m_onWidgetDestroyed++; }
+
+void TestObject::setItem(SessionItem* item) { m_controller->setItem(item); }
+
+void TestObject::onSubscribe()
+{
+    m_is_subscribed = true;
+    currentItem()->mapper()->setOnPropertyChange(
+        [this](const QString&) { m_listener->m_onPropertyChangeCount++; }, this);
+
+    currentItem()->mapper()->setOnItemDestroy(
+        [this](SessionItem*) { m_listener->m_onItemDestroyedCount++; }, this);
+}
+
+SessionItem* TestObject::currentItem() { return m_controller->currentItem(); }
+
+void TestObject::onUnsubscribe() { m_is_subscribed = false; }
+
+void TestObject::setVisible(bool isVisible)
+{
+    if (isVisible)
+        m_controller->subscribe();
+    else
+        m_controller->unsubscribe();
+}
diff --git a/Tests/UnitTests/GUI2/TestSessionItemControllerHelper.h b/Tests/UnitTests/GUI2/TestSessionItemControllerHelper.h
new file mode 100644
index 0000000000000000000000000000000000000000..5435a43475acc3eb43e7af81a3a577fa27f95572
--- /dev/null
+++ b/Tests/UnitTests/GUI2/TestSessionItemControllerHelper.h
@@ -0,0 +1,46 @@
+#ifndef TESTSESSIONITEMCONTROLLERHELPER_H
+#define TESTSESSIONITEMCONTROLLERHELPER_H
+
+#include <QObject>
+class SessionItem;
+class SessionItemController;
+
+//! Helper classes for TestSessionItemController unit tests.
+//! Should be in separate header to avoid interference of Qt moc and google test macros.
+
+//! Helper class to test object behaviour after their death.
+
+class TestListener
+{
+public:
+    TestListener();
+    void clear();
+    int m_onItemDestroyedCount;
+    int m_onPropertyChangeCount;
+    int m_onWidgetDestroyed;
+};
+
+//! Helper class to test SessionItemController usage in widget context.
+
+class TestObject : public QObject
+{
+    Q_OBJECT
+public:
+    TestObject(TestListener* listener);
+    ~TestObject();
+
+    void setItem(SessionItem* item);
+    void onSubscribe();
+
+    SessionItem* currentItem();
+
+    void onUnsubscribe();
+
+    void setVisible(bool isVisible);
+
+    TestListener* m_listener;
+    SessionItemController* m_controller;
+    bool m_is_subscribed;
+};
+
+#endif
diff --git a/Tests/UnitTests/GUI2/TestSessionItemUtils.h b/Tests/UnitTests/GUI2/TestSessionItemUtils.h
new file mode 100644
index 0000000000000000000000000000000000000000..3b9cf0b04f4e5cc41f4e11fa0403cf4089ad2b9b
--- /dev/null
+++ b/Tests/UnitTests/GUI2/TestSessionItemUtils.h
@@ -0,0 +1,55 @@
+#include "google_test.h"
+#include "SessionItemUtils.h"
+#include "SessionModel.h"
+#include "SessionItem.h"
+#include "VectorItem.h"
+#include "item_constants.h"
+
+class TestSessionItemUtils : public ::testing::Test
+{
+public:
+    ~TestSessionItemUtils();
+};
+
+TestSessionItemUtils::~TestSessionItemUtils() = default;
+
+//! Test SessionItemUtils::ParentVisibleRow utility method.
+
+TEST_F(TestSessionItemUtils, test_ParentVisibleRow)
+{
+    SessionModel model("TestModel");
+
+    // 3 property items in root, all visible
+    auto item1 = model.insertNewItem(Constants::PropertyType);
+    auto item2 = model.insertNewItem(Constants::PropertyType);
+    auto item3 = model.insertNewItem(Constants::PropertyType);
+    EXPECT_EQ(SessionItemUtils::ParentVisibleRow(*item1), 0);
+    EXPECT_EQ(SessionItemUtils::ParentVisibleRow(*item2), 1);
+    EXPECT_EQ(SessionItemUtils::ParentVisibleRow(*item3), 2);
+
+    // one item become invisible
+    item2->setVisible(false);
+    EXPECT_EQ(SessionItemUtils::ParentVisibleRow(*item1), 0);
+    EXPECT_EQ(SessionItemUtils::ParentVisibleRow(*item2), -1);
+    EXPECT_EQ(SessionItemUtils::ParentVisibleRow(*item3), 1);
+
+    // two more items
+    auto item4 = model.insertNewItem(Constants::PropertyType);
+    auto item5 = model.insertNewItem(Constants::PropertyType);
+    item5->setVisible(false);
+    EXPECT_EQ(SessionItemUtils::ParentVisibleRow(*item1), 0);
+    EXPECT_EQ(SessionItemUtils::ParentVisibleRow(*item2), -1);
+    EXPECT_EQ(SessionItemUtils::ParentVisibleRow(*item3), 1);
+    EXPECT_EQ(SessionItemUtils::ParentVisibleRow(*item4), 2);
+    EXPECT_EQ(SessionItemUtils::ParentVisibleRow(*item5), -1);
+
+    // adding vector item
+    SessionItem* vector = model.insertNewItem(Constants::VectorType);
+    auto x = vector->getItem(VectorItem::P_X);
+    auto y = vector->getItem(VectorItem::P_Y);
+    auto z = vector->getItem(VectorItem::P_Z);
+    x->setVisible(false);
+    EXPECT_EQ(SessionItemUtils::ParentVisibleRow(*x), -1);
+    EXPECT_EQ(SessionItemUtils::ParentVisibleRow(*y), 0);
+    EXPECT_EQ(SessionItemUtils::ParentVisibleRow(*z), 1);
+}
diff --git a/Tests/UnitTests/GUI2/TestSessionModel.h b/Tests/UnitTests/GUI2/TestSessionModel.h
new file mode 100644
index 0000000000000000000000000000000000000000..3bdf918e39cc68b997d2294a9d0ef93f2faf1fb5
--- /dev/null
+++ b/Tests/UnitTests/GUI2/TestSessionModel.h
@@ -0,0 +1,135 @@
+#include "InstrumentModel.h"
+#include "JobItem.h"
+#include "JobModel.h"
+#include "MaterialModel.h"
+#include "SampleModel.h"
+#include "SessionModel.h"
+#include "google_test.h"
+#include <QXmlStreamWriter>
+#include <memory>
+
+class TestSessionModel : public ::testing::Test
+{
+public:
+    ~TestSessionModel();
+};
+
+TestSessionModel::~TestSessionModel() = default;
+
+TEST_F(TestSessionModel, test_SampleModel_CreateCopy)
+{
+    std::unique_ptr<MaterialModel> P_materialModel(new MaterialModel());
+
+    SampleModel* model1 = new SampleModel();
+    SessionItem* multilayer = model1->insertNewItem(Constants::MultiLayerType);
+    multilayer->setItemName("multilayer");
+    model1->insertNewItem(Constants::LayerType, model1->indexOfItem(multilayer));
+    SessionItem* multilayer2 = model1->insertNewItem(Constants::MultiLayerType);
+    multilayer2->setItemName("multilayer2");
+
+    QString buffer1;
+    QXmlStreamWriter writer1(&buffer1);
+    model1->writeTo(&writer1);
+
+    SampleModel* model2 = model1->createCopy();
+    QString buffer2;
+    QXmlStreamWriter writer2(&buffer2);
+    model2->writeTo(&writer2);
+
+    EXPECT_EQ(buffer1, buffer2);
+
+    delete model1;
+    delete model2;
+}
+
+TEST_F(TestSessionModel, test_SampleModel_CreatePartialCopy)
+{
+    std::unique_ptr<MaterialModel> P_materialModel(new MaterialModel());
+
+    SampleModel* model1 = new SampleModel();
+    SessionItem* multilayer1 = model1->insertNewItem(Constants::MultiLayerType);
+    multilayer1->setItemName("multilayer1");
+    model1->insertNewItem(Constants::LayerType, model1->indexOfItem(multilayer1));
+
+    SessionItem* multilayer2 = model1->insertNewItem(Constants::MultiLayerType);
+    multilayer2->setItemName("multilayer2");
+
+    SampleModel* model2 = model1->createCopy(multilayer1);
+    SessionItem* result = model2->itemForIndex(model2->index(0, 0, QModelIndex()));
+
+    EXPECT_EQ(result->itemName(), multilayer1->itemName());
+    EXPECT_EQ(result->modelType(), multilayer1->modelType());
+
+    delete model1;
+    delete model2;
+}
+
+TEST_F(TestSessionModel, test_InstrumentModel_CreateCopy)
+{
+    InstrumentModel* model1 = new InstrumentModel();
+    SessionItem* instrument1 = model1->insertNewItem(Constants::InstrumentType);
+    instrument1->setItemName("instrument1");
+
+    SessionItem* instrument2 = model1->insertNewItem(Constants::InstrumentType);
+    instrument2->setItemName("instrument2");
+
+    QString buffer1;
+    QXmlStreamWriter writer1(&buffer1);
+    model1->writeTo(&writer1);
+
+    InstrumentModel* model2 = model1->createCopy();
+    QString buffer2;
+    QXmlStreamWriter writer2(&buffer2);
+    model2->writeTo(&writer2);
+
+    EXPECT_EQ(buffer1, buffer2);
+
+    delete model1;
+    delete model2;
+}
+
+TEST_F(TestSessionModel, test_InstrumentModel_CreatePartialCopy)
+{
+    InstrumentModel* model1 = new InstrumentModel();
+    SessionItem* instrument1 = model1->insertNewItem(Constants::InstrumentType);
+    instrument1->setItemName("instrument1");
+
+    SessionItem* instrument2 = model1->insertNewItem(Constants::InstrumentType);
+    instrument2->setItemName("instrument2");
+    InstrumentModel* model2 = model1->createCopy(instrument2);
+    SessionItem* result = model2->itemForIndex(model2->index(0, 0, QModelIndex()));
+    EXPECT_EQ(result->modelType(), instrument2->modelType());
+
+    delete model1;
+    delete model2;
+}
+
+//! Test if SessionItem can be copied from one model to another. Particularly, we test
+//! here if a MultiLayerItem can be copied from SampleModel to the JobItem of JobModel
+
+TEST_F(TestSessionModel, test_copyParameterizedItem)
+{
+    std::unique_ptr<MaterialModel> P_materialModel(new MaterialModel());
+
+    SampleModel* sampleModel = new SampleModel();
+    SessionItem* multilayer1 = sampleModel->insertNewItem(Constants::MultiLayerType);
+    multilayer1->setItemName("multilayer1");
+    sampleModel->insertNewItem(Constants::LayerType, sampleModel->indexOfItem(multilayer1));
+
+    InstrumentModel* instrumentModel = new InstrumentModel();
+    SessionItem* instrument1 = instrumentModel->insertNewItem(Constants::InstrumentType);
+    instrument1->setItemName("instrument1");
+
+    JobModel* jobModel = new JobModel();
+    SessionItem* jobItem = jobModel->insertNewItem(Constants::JobItemType);
+
+    jobModel->copyParameterizedItem(multilayer1, jobItem, JobItem::T_SAMPLE);
+    EXPECT_EQ(jobItem->getTagInfo(JobItem::T_SAMPLE).childCount, 1);
+
+    jobModel->copyParameterizedItem(instrument1, jobItem, JobItem::T_INSTRUMENT);
+    EXPECT_EQ(jobItem->getTagInfo(JobItem::T_INSTRUMENT).childCount, 1);
+
+    delete sampleModel;
+    delete instrumentModel;
+    delete jobModel;
+}
diff --git a/Tests/UnitTests/GUI2/TestSessionXML.h b/Tests/UnitTests/GUI2/TestSessionXML.h
new file mode 100644
index 0000000000000000000000000000000000000000..c443f4f07e145d55b247d074dca51f82593c4453
--- /dev/null
+++ b/Tests/UnitTests/GUI2/TestSessionXML.h
@@ -0,0 +1,163 @@
+#include "google_test.h"
+#include "FormFactorItems.h"
+#include "ParticleItem.h"
+#include "SessionItem.h"
+#include "SessionModel.h"
+#include <QXmlStreamReader>
+#include <QXmlStreamWriter>
+#include <memory>
+
+namespace
+{
+QString itemToXML(SessionItem* item)
+{
+    QString result;
+    QXmlStreamWriter writer(&result);
+    SessionXML::writeTo(&writer, item);
+    return result;
+}
+
+void itemFromXML(QString buffer, SessionItem* item)
+{
+    QXmlStreamReader reader(buffer);
+    SessionXML::readItems(&reader, item);
+}
+}
+
+class TestSessionXML : public ::testing::Test
+{
+public:
+    ~TestSessionXML();
+};
+
+TestSessionXML::~TestSessionXML() = default;
+
+//! Testing to/from xml: simple property item.
+
+TEST_F(TestSessionXML, test_sessionItem)
+{
+    QString expected;
+
+    SessionModel source("TestModel");
+    source.insertNewItem(Constants::PropertyType);
+
+    expected = "<TestModel Name=\"DefaultName\">"
+               "<Item ModelType=\"Property\" Tag=\"rootTag\" DisplayName=\"Property\"/>"
+               "</TestModel>";
+    EXPECT_EQ(itemToXML(source.rootItem()), expected);
+
+    SessionModel target("TestModel");
+    itemFromXML(expected, target.rootItem());
+
+    EXPECT_EQ(target.rowCount(QModelIndex()), 1);
+    SessionItem* newItem = target.itemForIndex(target.index(0, 0, QModelIndex()));
+    EXPECT_EQ(newItem->modelType(), Constants::PropertyType);
+    EXPECT_EQ(newItem->displayName(), QString("Property"));
+    EXPECT_FALSE(newItem->value().isValid());
+}
+
+//! Testing to/from xml: FullSphereItem
+
+TEST_F(TestSessionXML, test_FullSphereItem)
+{
+    // source model, to xml
+    SessionModel source("TestModel");
+    SessionItem* sphere = source.insertNewItem(Constants::FullSphereType);
+    SessionItem* radius = sphere->getItem(FullSphereItem::P_RADIUS);
+    QString buffer = itemToXML(source.rootItem());
+
+    // target model, from xml
+    SessionModel target("TestModel");
+    itemFromXML(buffer, target.rootItem());
+
+    // checking top items in source and target models
+    SessionItem* t_sphere = target.topItem();
+    SessionItem* t_radius = t_sphere->getItem(FullSphereItem::P_RADIUS);
+
+    EXPECT_EQ(sphere->parent()->tagFromItem(sphere), t_sphere->parent()->tagFromItem(t_sphere));
+    EXPECT_EQ(sphere->displayName(), t_sphere->displayName());
+    EXPECT_EQ(sphere->modelType(), t_sphere->modelType());
+    EXPECT_EQ(sphere->numberOfChildren(), t_sphere->numberOfChildren());
+    EXPECT_EQ(sphere->getItemValue(FullSphereItem::P_RADIUS),
+              t_sphere->getItemValue(FullSphereItem::P_RADIUS));
+
+    EXPECT_EQ(radius->parent()->tagFromItem(sphere), t_radius->parent()->tagFromItem(t_sphere));
+    EXPECT_EQ(radius->displayName(), t_radius->displayName());
+    EXPECT_EQ(radius->modelType(), t_radius->modelType());
+    EXPECT_EQ(radius->numberOfChildren(), t_radius->numberOfChildren());
+    EXPECT_EQ(radius->value().toDouble(), t_radius->value().toDouble());
+
+    // final XML comparison
+    EXPECT_EQ(buffer, itemToXML(target.rootItem()));
+}
+
+TEST_F(TestSessionXML, test_twoFullSphereItems)
+{
+    // source model, to xml
+    SessionModel source("TestModel");
+    SessionItem* sphere1 = source.insertNewItem(Constants::FullSphereType);
+    sphere1->setItemValue(FullSphereItem::P_RADIUS, 1.0);
+    SessionItem* sphere2 = source.insertNewItem(Constants::FullSphereType);
+    sphere2->setItemValue(FullSphereItem::P_RADIUS, 2.0);
+    QString buffer = itemToXML(source.rootItem());
+
+    SessionModel target("TestModel");
+    itemFromXML(buffer, target.rootItem());
+
+    // final XML comparison
+    EXPECT_EQ(buffer, itemToXML(target.rootItem()));
+}
+
+TEST_F(TestSessionXML, test_emptyMultiLayer)
+{
+    SessionModel source("TestModel");
+    source.insertNewItem(Constants::MultiLayerType);
+    QString buffer = itemToXML(source.rootItem());
+
+    SessionModel target("TestModel");
+    itemFromXML(buffer, target.rootItem());
+
+    // final XML comparison
+    EXPECT_EQ(buffer, itemToXML(target.rootItem()));
+}
+
+TEST_F(TestSessionXML, test_Layer)
+{
+    SessionModel source("TestModel");
+    source.insertNewItem(Constants::LayerType);
+    QString buffer = itemToXML(source.rootItem());
+
+    SessionModel target("TestModel");
+    itemFromXML(buffer, target.rootItem());
+
+    // final XML comparison
+    EXPECT_EQ(buffer, itemToXML(target.rootItem()));
+}
+
+TEST_F(TestSessionXML, test_Particle)
+{
+    SessionModel source("TestModel");
+    source.insertNewItem(Constants::ParticleType);
+    QString buffer = itemToXML(source.rootItem());
+
+    SessionModel target("TestModel");
+    itemFromXML(buffer, target.rootItem());
+
+    // final XML comparison
+    EXPECT_EQ(buffer, itemToXML(target.rootItem()));
+}
+
+TEST_F(TestSessionXML, test_ParticleWithFF)
+{
+    SessionModel source("TestModel");
+    SessionItem* particle = source.insertNewItem(Constants::ParticleType);
+
+    particle->setGroupProperty(ParticleItem::P_FORM_FACTOR, Constants::AnisoPyramidType);
+    QString buffer = itemToXML(source.rootItem());
+
+    SessionModel target("TestModel");
+    itemFromXML(buffer, target.rootItem());
+
+    // final XML comparison
+    EXPECT_EQ(buffer, itemToXML(target.rootItem()));
+}
diff --git a/Tests/UnitTests/GUI2/TestTranslations.h b/Tests/UnitTests/GUI2/TestTranslations.h
new file mode 100644
index 0000000000000000000000000000000000000000..00cd4e80e8d73bd9c998fd82fcd34e2ab7782fce
--- /dev/null
+++ b/Tests/UnitTests/GUI2/TestTranslations.h
@@ -0,0 +1,76 @@
+#include "google_test.h"
+#include "BeamDistributionItem.h"
+#include "BeamItem.h"
+#include "DistributionItems.h"
+#include "InstrumentItem.h"
+#include "ModelPath.h"
+#include "ParticleItem.h"
+#include "RotationItems.h"
+#include "SampleModel.h"
+#include "TransformationItem.h"
+#include "VectorItem.h"
+
+class TestTranslations : public ::testing::Test
+{
+public:
+    ~TestTranslations();
+};
+
+TestTranslations::~TestTranslations() = default;
+
+TEST_F(TestTranslations, test_TranslatePosition)
+{
+    SampleModel model;
+    SessionItem* multilayer = model.insertNewItem(Constants::MultiLayerType);
+    SessionItem* layer = model.insertNewItem(Constants::LayerType, multilayer->index());
+    SessionItem* layout = model.insertNewItem(Constants::ParticleLayoutType, layer->index());
+    SessionItem* particle = model.insertNewItem(Constants::ParticleType, layout->index());
+
+    SessionItem* positionItem = particle->getItem(ParticleItem::P_POSITION);
+    SessionItem* xItem = positionItem->getItem(VectorItem::P_X);
+
+    EXPECT_EQ(ModelPath::itemPathTranslation(*xItem, multilayer->parent()),
+              QString("MultiLayer/Layer/ParticleLayout/Particle/PositionX"));
+}
+
+TEST_F(TestTranslations, test_TranslateRotation)
+{
+    SampleModel model;
+    SessionItem* multilayer = model.insertNewItem(Constants::MultiLayerType);
+    SessionItem* layer = model.insertNewItem(Constants::LayerType, multilayer->index());
+    SessionItem* layout = model.insertNewItem(Constants::ParticleLayoutType, layer->index());
+    SessionItem* particle = model.insertNewItem(Constants::ParticleType, layout->index());
+
+    SessionItem* transformation = model.insertNewItem(Constants::RotationType, particle->index(),
+                                                      -1, ParticleItem::T_TRANSFORMATION);
+
+    SessionItem* rotationItem
+        = transformation->setGroupProperty(TransformationItem::P_ROT, Constants::XRotationType);
+
+    SessionItem* angleItem = rotationItem->getItem(XRotationItem::P_ANGLE);
+    EXPECT_EQ(ModelPath::itemPathTranslation(*angleItem, multilayer->parent()),
+              QString("MultiLayer/Layer/ParticleLayout/Particle/XRotation/Angle"));
+}
+
+TEST_F(TestTranslations, test_BeamDistributionNone)
+{
+    SampleModel model;
+    SessionItem* instrument = model.insertNewItem(Constants::InstrumentType);
+    SessionItem* beam = instrument->getItem(InstrumentItem::P_BEAM);
+
+    SessionItem* wavelength = beam->getItem(BeamItem::P_WAVELENGTH);
+
+    SessionItem* distr = wavelength->getGroupItem(BeamDistributionItem::P_DISTRIBUTION);
+    EXPECT_EQ(distr->modelType(), Constants::DistributionNoneType);
+    SessionItem* value = distr->getItem(DistributionNoneItem::P_VALUE);
+
+    EXPECT_EQ(ModelPath::itemPathTranslation(*value, instrument->parent()),
+              QString("Instrument/Beam/Wavelength"));
+
+    SessionItem* inclinationAngle = beam->getItem(BeamItem::P_INCLINATION_ANGLE);
+    distr = inclinationAngle->getGroupItem(BeamDistributionItem::P_DISTRIBUTION);
+    value = distr->getItem(DistributionNoneItem::P_VALUE);
+
+    EXPECT_EQ(ModelPath::itemPathTranslation(*value, instrument->parent()),
+              QString("Instrument/Beam/InclinationAngle"));
+}
diff --git a/Tests/UnitTests/GUI2/TestUpdateTimer.h b/Tests/UnitTests/GUI2/TestUpdateTimer.h
new file mode 100644
index 0000000000000000000000000000000000000000..0b58c57ad1fca63fd98d3cd2d92efbbb9244f37e
--- /dev/null
+++ b/Tests/UnitTests/GUI2/TestUpdateTimer.h
@@ -0,0 +1,39 @@
+#include "google_test.h"
+#include "UpdateTimer.h"
+#include <QSignalSpy>
+
+class TestUpdateTimer : public ::testing::Test
+{
+public:
+    ~TestUpdateTimer();
+};
+
+TestUpdateTimer::~TestUpdateTimer() = default;
+
+TEST_F(TestUpdateTimer, test_updateTimerShort)
+{
+    EXPECT_EQ(1,2);
+    const int timer_interval(100);
+    UpdateTimer timer(timer_interval);
+
+    QSignalSpy spy(&timer, SIGNAL(timeToUpdate()));
+
+    for (int i = 0; i < 10; ++i)
+        timer.scheduleUpdate();
+
+    // Checks that after time bigger than timer interval, we have a valid signal
+    EXPECT_TRUE(spy.wait(timer_interval * 3));
+    EXPECT_EQ(spy.count(), 1);
+
+    // once again
+    timer.scheduleUpdate();
+    EXPECT_TRUE(spy.wait(timer_interval * 3));
+    EXPECT_EQ(spy.count(), 2);
+
+    // Checks that after time smaller than timer interval, we have no signals
+    for (int i = 0; i < 10; ++i)
+        timer.scheduleUpdate();
+
+    EXPECT_FALSE(spy.wait(timer_interval / 2));
+    EXPECT_EQ(spy.count(), 2);
+}