From 8fea310bbf3087cd56a53514bf3ab3e03c035c31 Mon Sep 17 00:00:00 2001 From: Gennady Pospelov <g.pospelov@fz-juelich.de> Date: Fri, 8 Dec 2017 16:52:54 +0100 Subject: [PATCH] GUI unit test migrated to google test --- Tests/UnitTests/GUI/TestGUI.cpp | 1 + Tests/UnitTests/GUI2/CMakeLists.txt | 2 +- .../UnitTests/GUI2/TestComponentProxyModel.h | 386 ++++++++++++++++++ Tests/UnitTests/GUI2/TestComponentUtils.h | 61 +++ Tests/UnitTests/GUI2/TestDetectorItems.h | 58 +++ Tests/UnitTests/GUI2/TestExternalProperty.h | 99 +++++ .../UnitTests/GUI2/TestFTDistributionItems.h | 27 ++ Tests/UnitTests/GUI2/TestFitParameterModel.h | 240 +++++++++++ Tests/UnitTests/GUI2/TestFormFactorItems.h | 30 ++ Tests/UnitTests/GUI2/TestGUI.cpp | 41 +- .../GUI2/TestGUICoreObjectCorrespondence.h | 196 +++++++++ Tests/UnitTests/GUI2/TestGUIHelpers.h | 31 ++ Tests/UnitTests/GUI2/TestGroupItem.h | 114 ++++++ Tests/UnitTests/GUI2/TestIntensityDataItem.h | 38 ++ Tests/UnitTests/GUI2/TestLayerItems.h | 32 ++ .../UnitTests/GUI2/TestLayerRoughnessItems.h | 47 +++ Tests/UnitTests/GUI2/TestLinkInstrument.h | 89 ++++ Tests/UnitTests/GUI2/TestMapperCases.h | 60 +++ Tests/UnitTests/GUI2/TestMapperForItem.h | 324 +++++++++++++++ Tests/UnitTests/GUI2/TestMaterialModel.h | 121 ++++++ .../GUI2/TestMaterialPropertyController.h | 134 ++++++ Tests/UnitTests/GUI2/TestModelUtils.h | 90 ++++ .../UnitTests/GUI2/TestOutputDataIOService.h | 263 ++++++++++++ Tests/UnitTests/GUI2/TestParaCrystalItems.h | 109 +++++ Tests/UnitTests/GUI2/TestParameterTreeUtils.h | 73 ++++ Tests/UnitTests/GUI2/TestParticleCoreShell.h | 121 ++++++ .../GUI2/TestParticleDistributionItem.h | 178 ++++++++ Tests/UnitTests/GUI2/TestParticleItem.h | 74 ++++ Tests/UnitTests/GUI2/TestProjectDocument.h | 128 ++++++ Tests/UnitTests/GUI2/TestProjectUtils.h | 103 +++++ Tests/UnitTests/GUI2/TestPropertyRepeater.h | 189 +++++++++ Tests/UnitTests/GUI2/TestProxyModelStrategy.h | 180 ++++++++ Tests/UnitTests/GUI2/TestSaveService.h | 225 ++++++++++ Tests/UnitTests/GUI2/TestSessionItem.h | 164 ++++++++ .../GUI2/TestSessionItemController.h | 273 +++++++++++++ .../GUI2/TestSessionItemControllerHelper.cpp | 48 +++ .../GUI2/TestSessionItemControllerHelper.h | 46 +++ Tests/UnitTests/GUI2/TestSessionItemUtils.h | 55 +++ Tests/UnitTests/GUI2/TestSessionModel.h | 135 ++++++ Tests/UnitTests/GUI2/TestSessionXML.h | 163 ++++++++ Tests/UnitTests/GUI2/TestTranslations.h | 76 ++++ Tests/UnitTests/GUI2/TestUpdateTimer.h | 39 ++ 42 files changed, 4861 insertions(+), 2 deletions(-) create mode 100644 Tests/UnitTests/GUI2/TestComponentProxyModel.h create mode 100644 Tests/UnitTests/GUI2/TestComponentUtils.h create mode 100644 Tests/UnitTests/GUI2/TestDetectorItems.h create mode 100644 Tests/UnitTests/GUI2/TestExternalProperty.h create mode 100644 Tests/UnitTests/GUI2/TestFTDistributionItems.h create mode 100644 Tests/UnitTests/GUI2/TestFitParameterModel.h create mode 100644 Tests/UnitTests/GUI2/TestFormFactorItems.h create mode 100644 Tests/UnitTests/GUI2/TestGUICoreObjectCorrespondence.h create mode 100644 Tests/UnitTests/GUI2/TestGUIHelpers.h create mode 100644 Tests/UnitTests/GUI2/TestGroupItem.h create mode 100644 Tests/UnitTests/GUI2/TestIntensityDataItem.h create mode 100644 Tests/UnitTests/GUI2/TestLayerItems.h create mode 100644 Tests/UnitTests/GUI2/TestLayerRoughnessItems.h create mode 100644 Tests/UnitTests/GUI2/TestLinkInstrument.h create mode 100644 Tests/UnitTests/GUI2/TestMapperCases.h create mode 100644 Tests/UnitTests/GUI2/TestMapperForItem.h create mode 100644 Tests/UnitTests/GUI2/TestMaterialModel.h create mode 100644 Tests/UnitTests/GUI2/TestMaterialPropertyController.h create mode 100644 Tests/UnitTests/GUI2/TestModelUtils.h create mode 100644 Tests/UnitTests/GUI2/TestOutputDataIOService.h create mode 100644 Tests/UnitTests/GUI2/TestParaCrystalItems.h create mode 100644 Tests/UnitTests/GUI2/TestParameterTreeUtils.h create mode 100644 Tests/UnitTests/GUI2/TestParticleCoreShell.h create mode 100644 Tests/UnitTests/GUI2/TestParticleDistributionItem.h create mode 100644 Tests/UnitTests/GUI2/TestParticleItem.h create mode 100644 Tests/UnitTests/GUI2/TestProjectDocument.h create mode 100644 Tests/UnitTests/GUI2/TestProjectUtils.h create mode 100644 Tests/UnitTests/GUI2/TestPropertyRepeater.h create mode 100644 Tests/UnitTests/GUI2/TestProxyModelStrategy.h create mode 100644 Tests/UnitTests/GUI2/TestSaveService.h create mode 100644 Tests/UnitTests/GUI2/TestSessionItem.h create mode 100644 Tests/UnitTests/GUI2/TestSessionItemController.h create mode 100644 Tests/UnitTests/GUI2/TestSessionItemControllerHelper.cpp create mode 100644 Tests/UnitTests/GUI2/TestSessionItemControllerHelper.h create mode 100644 Tests/UnitTests/GUI2/TestSessionItemUtils.h create mode 100644 Tests/UnitTests/GUI2/TestSessionModel.h create mode 100644 Tests/UnitTests/GUI2/TestSessionXML.h create mode 100644 Tests/UnitTests/GUI2/TestTranslations.h create mode 100644 Tests/UnitTests/GUI2/TestUpdateTimer.h diff --git a/Tests/UnitTests/GUI/TestGUI.cpp b/Tests/UnitTests/GUI/TestGUI.cpp index 4ca1e63400c..e5fccadeb26 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 526bc502f79..39a7ce7d95f 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 00000000000..d887b20cef8 --- /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 00000000000..24b8fb33d9f --- /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 00000000000..bba96fef2a3 --- /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 00000000000..2d398389827 --- /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 00000000000..df8ea8283ed --- /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 00000000000..333fd61bf84 --- /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 00000000000..8218fa5a091 --- /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 e7c7f4e98f0..67a9f454d18 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 00000000000..2edd5d1263a --- /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 00000000000..bfee368f103 --- /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 00000000000..f3363ef43a4 --- /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 00000000000..a707619e401 --- /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 00000000000..8aaa60cae6b --- /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 00000000000..b00b56e94ae --- /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 00000000000..69c7077e319 --- /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 00000000000..b1120c9e92c --- /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 00000000000..79376ceedaa --- /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 00000000000..1df15b6d94c --- /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 00000000000..a3eb83a66f3 --- /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 00000000000..c1ca347fc32 --- /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 00000000000..ce11036a936 --- /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 00000000000..50c6f5244ae --- /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 00000000000..b4f974e146b --- /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 00000000000..f59b5ebd092 --- /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 00000000000..146ffd73a08 --- /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 00000000000..206ed13b40d --- /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 00000000000..c17019cbbbe --- /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 00000000000..7769fcf992f --- /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 00000000000..7662900b444 --- /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 00000000000..875d5f9641d --- /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 00000000000..673f6bdfb92 --- /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 00000000000..827d967d3c7 --- /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 00000000000..1a29d082e18 --- /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 00000000000..014174092d3 --- /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 00000000000..5435a43475a --- /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 00000000000..3b9cf0b04f4 --- /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 00000000000..3bdf918e39c --- /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 00000000000..c443f4f07e1 --- /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 00000000000..00cd4e80e8d --- /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 00000000000..0b58c57ad1f --- /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); +} -- GitLab