From 67b7e7545986c398958b3ce951ab5c35c70fadf8 Mon Sep 17 00:00:00 2001
From: Gennady Pospelov <g.pospelov@fz-juelich.de>
Date: Tue, 19 Apr 2016 14:04:48 +0200
Subject: [PATCH] Message service restored, minimal supported project version
 introduced.

---
 GUI/coregui/Models/SessionModel.cpp           |  27 +---
 GUI/coregui/Models/SessionModel.h             |  13 +-
 GUI/coregui/Models/SessionXML.cpp             | 137 ++++++++++++------
 GUI/coregui/Models/SessionXML.h               |  11 +-
 GUI/coregui/mainwindow/projectdocument.cpp    |  17 ++-
 GUI/coregui/utils/GUIHelpers.cpp              |  32 ++++
 GUI/coregui/utils/GUIHelpers.h                |   4 +
 Tests/UnitTests/TestGUI/TestFormFactorItems.h |   1 -
 Tests/UnitTests/TestGUI/TestGUI.cpp           |   3 +
 Tests/UnitTests/TestGUI/TestGUIHelpers.h      |  35 +++++
 10 files changed, 193 insertions(+), 87 deletions(-)
 create mode 100644 Tests/UnitTests/TestGUI/TestGUIHelpers.h

diff --git a/GUI/coregui/Models/SessionModel.cpp b/GUI/coregui/Models/SessionModel.cpp
index b50a56566a7..171999d4533 100644
--- a/GUI/coregui/Models/SessionModel.cpp
+++ b/GUI/coregui/Models/SessionModel.cpp
@@ -33,10 +33,6 @@
 namespace
 {
 const int MaxCompression = 9;
-
-const QString SET_ITEM_PROPERTY_ERROR = "SET_ITEM_PROPERTY_ERROR";
-const QString ITEM_IS_NOT_INITIALIZED = "ITEM_IS_NOT_INITIALIZED";
-const QString NON_EXISTING_SUBITEM = "NON_EXISTING_SUBITEM";
 }
 
 SessionModel::SessionModel(QString model_tag, QObject *parent)
@@ -44,7 +40,6 @@ SessionModel::SessionModel(QString model_tag, QObject *parent)
     , m_root_item(0)
     , m_name("DefaultName")
     , m_model_tag(model_tag)
-    , m_messageService(0)
 {
     createRootItem();
 }
@@ -348,7 +343,7 @@ SessionItem *SessionModel::itemForIndex(const QModelIndex &index) const
     return m_root_item;
 }
 
-void SessionModel::readFrom(QXmlStreamReader *reader)
+void SessionModel::readFrom(QXmlStreamReader *reader, WarningMessageService *messageService)
 {
     Q_ASSERT(reader);
 
@@ -365,7 +360,7 @@ void SessionModel::readFrom(QXmlStreamReader *reader)
 
     createRootItem();
 
-    SessionReader::readItems(reader, m_root_item);
+    SessionReader::readItems(reader, m_root_item, QString(), messageService);
     if (reader->hasError())
         throw GUIHelpers::Error(reader->errorString());
     endResetModel();
@@ -374,9 +369,6 @@ void SessionModel::readFrom(QXmlStreamReader *reader)
 
 void SessionModel::writeTo(QXmlStreamWriter *writer, SessionItem *parent)
 {
-    // MOVED OUT TO SessionXML.h
-
-    qDebug() << "SessionModel::writeTo";
     if (!parent)
         parent = m_root_item;
     SessionWriter::writeTo(writer, parent);
@@ -488,11 +480,6 @@ SessionItem *SessionModel::getTopItem(const QString &model_type,
     return result;
 }
 
-void SessionModel::setMessageService(WarningMessageService *messageService)
-{
-    m_messageService = messageService;
-}
-
 void SessionModel::initFrom(SessionModel *model, SessionItem *parent)
 {
     qDebug() << "SessionModel::initFrom() -> " << model->getModelTag() << parent;
@@ -512,16 +499,6 @@ void SessionModel::initFrom(SessionModel *model, SessionItem *parent)
     }
 }
 
-//! reports error
-void SessionModel::report_error(const QString &error_type, const QString &message)
-{
-    if(m_messageService) {
-        m_messageService->send_message(this, error_type, message);
-    } else {
-        throw GUIHelpers::Error(error_type + QString(" ") + message);
-    }
-}
-
 SessionItem* SessionModel::rootItem() const{
     return m_root_item;
 }
diff --git a/GUI/coregui/Models/SessionModel.h b/GUI/coregui/Models/SessionModel.h
index 4f1e616a16a..f417a2df441 100644
--- a/GUI/coregui/Models/SessionModel.h
+++ b/GUI/coregui/Models/SessionModel.h
@@ -20,9 +20,7 @@
 #include <QAbstractItemModel>
 #include <QtCore/QXmlStreamReader>
 #include <QtCore/QXmlStreamWriter>
-
 #include "SessionItem.h"
-
 #include "SessionXML.h"
 
 class IconProvider;
@@ -91,7 +89,7 @@ public:
     // Returns root item if index is not valid
     SessionItem *itemForIndex(const QModelIndex &index) const;
 
-    void readFrom(QXmlStreamReader *reader);
+    void readFrom(QXmlStreamReader *reader, WarningMessageService *messageService=0);
     void writeTo(QXmlStreamWriter *writer, SessionItem *parent = 0);
 
     SessionItem *moveParameterizedItem(SessionItem *item,
@@ -110,27 +108,18 @@ public:
     SessionItem *getTopItem(const QString &model_type = QString(),
                                   const QString &item_name = QString()) const;
 
-    void setMessageService(WarningMessageService *messageService);
-
     virtual void initFrom(SessionModel *model, SessionItem *parent);
     SessionItem* rootItem() const;
 
-
-
-
 protected:
     void setRootItem(SessionItem *root) {m_root_item = root;}
 
 private:
-
-    void report_error(const QString &error_type, const QString &message);
-
     SessionItem *m_root_item;
     QString m_dragged_item_type;
     QString m_name;      //!< model name
     QString m_model_tag; //!< model tag (SampleModel, InstrumentModel)
     std::unique_ptr<IconProvider> m_iconProvider;
-    WarningMessageService *m_messageService;
 };
 
 inline bool SessionModel::setHeaderData(int, Qt::Orientation, const QVariant &, int)
diff --git a/GUI/coregui/Models/SessionXML.cpp b/GUI/coregui/Models/SessionXML.cpp
index 07e3c3df49b..923c2c02e0c 100644
--- a/GUI/coregui/Models/SessionXML.cpp
+++ b/GUI/coregui/Models/SessionXML.cpp
@@ -26,10 +26,16 @@
 #include "GUIHelpers.h"
 #include "ItemFactory.h"
 #include "GroupItem.h"
-
+#include "WarningMessageService.h"
 #include <QXmlStreamWriter>
 #include <QDebug>
 
+namespace
+{
+const QString SET_ITEM_PROPERTY_ERROR = "SET_ITEM_PROPERTY_ERROR";
+const QString ITEM_IS_NOT_INITIALIZED = "ITEM_IS_NOT_INITIALIZED";
+const QString NON_EXISTING_SUBITEM = "NON_EXISTING_SUBITEM";
+}
 
 void SessionWriter::writeTo(QXmlStreamWriter *writer, SessionItem *parent)
 {
@@ -136,14 +142,17 @@ void SessionWriter::writeVariant(QXmlStreamWriter *writer, QVariant variant, int
                                    QString::number(value, 'g'));
             writer->writeAttribute(SessionXML::AngleUnitsAttribute,
                                    variant.value<AngleProperty>().getUnits());
+
         } else {
             throw GUIHelpers::Error("SessionModel::writeProperty: Parameter type not supported " + type_name);
         }
+
         writer->writeEndElement(); // end ParameterTag
     }
 }
 
-void SessionReader::readItems(QXmlStreamReader *reader, SessionItem *item, const QString &topTag)
+void SessionReader::readItems(QXmlStreamReader *reader, SessionItem *item, const QString &topTag,
+                              WarningMessageService *messageService)
 {
     bool isTopItem = true;
     qDebug() << "SessionModel::readItems()  item:" << item << "topTag:" << topTag;
@@ -162,39 +171,53 @@ void SessionReader::readItems(QXmlStreamReader *reader, SessionItem *item, const
                 if (tag == SessionItem::P_NAME)
                     item->setItemName("");
                 if (model_type == Constants::PropertyType || model_type == Constants::GroupItemType) {
-                    item = item->getItem(tag);
-                    if (!item) {
-                        qDebug() << "!!";
+                    SessionItem *newItem = item->getItem(tag);
+                    if (!newItem) {
+                        QString message = QString("Unrecoverable read error for model '%1', "
+                            "Can't get item for tag '%2'").arg(item->model()->getModelTag()).arg(tag);
+                        throw GUIHelpers::Error(message);
                     }
-                    Q_ASSERT(item);
+                    item = newItem;
+
                 } else if (item->modelType() == Constants::GroupItemType) {
-                    item = item->parent()->getGroupItem(item->parent()->tagFromItem(item), model_type);
-                    if (!item) {
-                        qDebug() << "!!";
+                    SessionItem *newItem = item->parent()->getGroupItem(item->parent()
+                                                ->tagFromItem(item), model_type);
+                    if (!newItem) {
+                        QString message = QString("Unrecoverable read error for model '%1', "
+                            "Can't get group item").arg(item->model()->getModelTag());
+                        throw GUIHelpers::Error(message);
                     }
-                    Q_ASSERT(item);
+                    item = newItem;
 
                 } else {
                     if (tag == "")
                         tag = item->defaultTag();
 
-                    SessionItem *new_item(0);
+                    SessionItem *newItem(0);
                     SessionTagInfo info = item->getTagInfo(tag);
                     if (info.min == 1 && info.max == 1 && info.childCount == 1) {
-                        new_item = item->getItem(tag);
+                        newItem = item->getItem(tag);
                     } else {
-                        new_item = ItemFactory::createItem(model_type);
-                        if (!item->insertItem(-1, new_item, tag)) {
-                            Q_ASSERT(0);
+                        newItem = ItemFactory::createItem(model_type);
+                        if (!item->insertItem(-1, newItem, tag)) {
+                            QString message = QString("Attempt to create item '%1' for tag '%2' failed")
+                                              .arg(model_type).arg(tag);
+                            report_error(messageService, item, ITEM_IS_NOT_INITIALIZED, message);
+                            return;
                         }
 
                     }
 
-                    Q_ASSERT(new_item);
+                    if (!newItem) {
+                        QString message = QString("Unrecoverable read error for model '%1', "
+                            "Can't add item for tag").arg(item->model()->getModelTag()).arg(tag);
+                        throw GUIHelpers::Error(message);
+                    }
+
                     if (reader->attributes().hasAttribute(SessionXML::DisplayNameAttribute)) {
-                        new_item->setDisplayName(reader->attributes().value(SessionXML::DisplayNameAttribute).toString());
+                        newItem->setDisplayName(reader->attributes().value(SessionXML::DisplayNameAttribute).toString());
                     }
-                    item = new_item;
+                    item = newItem;
                 }
                 if (!item) {
 //                    tag = -1;
@@ -204,7 +227,7 @@ void SessionReader::readItems(QXmlStreamReader *reader, SessionItem *item, const
                 isTopItem = false;
 
             } else if (reader->name() == SessionXML::ParameterTag) {
-                readProperty(reader, item);
+                readProperty(reader, item, messageService);
             }
         } else if (reader->isEndElement()) {
             if (reader->name() == SessionXML::ItemTag) {
@@ -213,6 +236,7 @@ void SessionReader::readItems(QXmlStreamReader *reader, SessionItem *item, const
                 } else {
                     // handling the case when reading obsolete project file, when SubItem doesn't exist anymore
                     qDebug() << "!!";
+                    Q_ASSERT(0);
                 }
             }
             if (reader->name() == modelType) {
@@ -224,7 +248,8 @@ void SessionReader::readItems(QXmlStreamReader *reader, SessionItem *item, const
     }
 }
 
-QString SessionReader::readProperty(QXmlStreamReader *reader, SessionItem *item)
+QString SessionReader::readProperty(QXmlStreamReader *reader,
+        SessionItem *item, WarningMessageService *messageService)
 {
 //    qDebug() << "SessionModel::readProperty() for" << item;
     if (item)
@@ -235,48 +260,50 @@ QString SessionReader::readProperty(QXmlStreamReader *reader, SessionItem *item)
         = reader->attributes().value(SessionXML::ParameterTypeAttribute).toString();
     const int role
             = reader->attributes().value(SessionXML::ParameterRoleAttribute).toInt();
-    // qDebug() << "           SessionModel::readProperty " << item->itemName() << item->modelType()
-    // << parameter_name << parameter_type << parameter_name.toUtf8().constData();
 
     if(!item) {
         QString message = QString("Attempt to set property '%1' for non existing item")
                           .arg(parameter_name);
-//        report_error(ITEM_IS_NOT_INITIALIZED, message);
+        report_error(messageService, item, ITEM_IS_NOT_INITIALIZED, message);
         return parameter_name;
     }
 
-//    if(!item->isRegisteredTag(parameter_name)) {
-//        QString message = QString("Unknown property '%1' for item type '%2'")
-//                          .arg(parameter_name).arg(item->modelType());
-////        report_error(SET_ITEM_PROPERTY_ERROR, message);
-//        return parameter_name;
-//    }
     QVariant variant;
     if (parameter_type == "double") {
         double parameter_value
             = reader->attributes().value(SessionXML::ParameterValueAttribute).toDouble();
         variant = parameter_value;
 
-    } else if (parameter_type == "int") {
+    }
+
+    else if (parameter_type == "int") {
         int parameter_value
             = reader->attributes().value(SessionXML::ParameterValueAttribute).toInt();
         variant = parameter_value;
-    } else if (parameter_type == "bool") {
+    }
+
+    else if (parameter_type == "bool") {
         bool parameter_value
             = reader->attributes().value(SessionXML::ParameterValueAttribute).toInt();
         variant = parameter_value;
 
-    } else if (parameter_type == "QString") {
+    }
+
+    else if (parameter_type == "QString") {
         QString parameter_value
             = reader->attributes().value(SessionXML::ParameterValueAttribute).toString();
         variant = parameter_value;
 
-    } else if (parameter_type == "MaterialProperty") {
+    }
+
+    else if (parameter_type == "MaterialProperty") {
         QString identifier = reader->attributes().value(SessionXML::IdentifierAttribute).toString();
 
         MaterialProperty material_property(identifier);
         variant = material_property.getVariant();
-    } else if (parameter_type == "ComboProperty") {
+    }
+
+    else if (parameter_type == "ComboProperty") {
         QString parameter_value
             = reader->attributes().value(SessionXML::ParameterValueAttribute).toString();
 
@@ -287,7 +314,9 @@ QString SessionReader::readProperty(QXmlStreamReader *reader, SessionItem *item)
         }
         combo_property.setCachedValue(parameter_value);
         variant = combo_property.getVariant();
-    } else if (parameter_type == "ScientificDoubleProperty") {
+    }
+
+    else if (parameter_type == "ScientificDoubleProperty") {
         double parameter_value
             = reader->attributes().value(SessionXML::ParameterValueAttribute).toDouble();
 
@@ -295,22 +324,34 @@ QString SessionReader::readProperty(QXmlStreamReader *reader, SessionItem *item)
         QVariant v;
         v.setValue(scdouble_property);
         variant = v;
-    } else if (parameter_type == "GroupProperty_t") {
+    }
+
+    else if (parameter_type == "GroupProperty_t") {
         QString parameter_value
             = reader->attributes().value(SessionXML::ParameterValueAttribute).toString();
 
-        GroupProperty_t group_property
-            = item->value().value<GroupProperty_t>();
-        group_property->setCurrentType(parameter_value);
-        variant = QVariant::fromValue<GroupProperty_t>(group_property);
-    } else if (parameter_type == "ColorProperty") {
+        QVariant v = item->value();
+        if(!v.canConvert<GroupProperty_t>()) {
+            report_error(messageService, item, SET_ITEM_PROPERTY_ERROR,
+                         QStringLiteral("GroupProperty conversion failed"));
+        } else {
+            GroupProperty_t group_property = v.value<GroupProperty_t>();
+            group_property->setCurrentType(parameter_value);
+            variant = QVariant::fromValue<GroupProperty_t>(group_property);
+        }
+
+    }
+
+    else if (parameter_type == "ColorProperty") {
         int r = reader->attributes().value(SessionXML::ColorRedAttribute).toInt();
         int g = reader->attributes().value(SessionXML::ColorGreenAttribute).toInt();
         int b = reader->attributes().value(SessionXML::ColorBlueAttribute).toInt();
         int a = reader->attributes().value(SessionXML::ColorAlphaAttribute).toInt();
         ColorProperty color(QColor(r, g, b, a));
         variant = color.getVariant();
-    } else if (parameter_type == "AngleProperty") {
+    }
+
+    else if (parameter_type == "AngleProperty") {
         double parameter_value
             = reader->attributes().value(SessionXML::ParameterValueAttribute).toDouble();
         QString units = reader->attributes().value(SessionXML::AngleUnitsAttribute).toString();
@@ -323,9 +364,21 @@ QString SessionReader::readProperty(QXmlStreamReader *reader, SessionItem *item)
         throw GUIHelpers::Error("SessionModel::readProperty: "
                                 "Parameter type not supported" + parameter_type);
     }
+
     if (variant.isValid()) {
         item->setData(role, variant);
     }
 
     return parameter_name;
 }
+
+void SessionReader::report_error(WarningMessageService *messageService,
+                                 SessionItem *item, const QString &error_type,
+                                 const QString &message)
+{
+    if(messageService) {
+        messageService->send_message(item->model(), error_type, message);
+    } else {
+        throw GUIHelpers::Error(error_type + QString(" ") + message);
+    }
+}
diff --git a/GUI/coregui/Models/SessionXML.h b/GUI/coregui/Models/SessionXML.h
index f285ddb0231..1874463fbbe 100644
--- a/GUI/coregui/Models/SessionXML.h
+++ b/GUI/coregui/Models/SessionXML.h
@@ -24,6 +24,7 @@
 class QXmlStreamWriter;
 class QXmlStreamReader;
 class SessionItem;
+class WarningMessageService;
 
 namespace SessionXML {
 const QString MimeType = "application/org.bornagainproject.xml.item.z";
@@ -71,9 +72,15 @@ private:
 class BA_CORE_API_ SessionReader
 {
 public:
-    static void readItems(QXmlStreamReader *reader, SessionItem *item, const QString &topTag = QString());
+    static void readItems(QXmlStreamReader *reader, SessionItem *item,
+                          const QString &topTag = QString(),
+                          WarningMessageService *messageService=0);
 private:
-    static QString readProperty(QXmlStreamReader *reader, SessionItem *item);
+    static QString readProperty(QXmlStreamReader *reader, SessionItem *item,
+                                WarningMessageService *messageService=0);
+
+    static void report_error(WarningMessageService *messageService, SessionItem *item,
+                      const QString &error_type, const QString &message);
 };
 
 #endif // SESSIONXML_H
diff --git a/GUI/coregui/mainwindow/projectdocument.cpp b/GUI/coregui/mainwindow/projectdocument.cpp
index c8e9fe26608..0cd11ee9f9d 100644
--- a/GUI/coregui/mainwindow/projectdocument.cpp
+++ b/GUI/coregui/mainwindow/projectdocument.cpp
@@ -44,6 +44,7 @@ namespace {
 const QString OPEN_FILE_ERROR = "OPEN_FILE_ERROR";
 const QString EXCEPTION_THROW = "EXCEPTION_THROW";
 const QString XML_FORMAT_ERROR = "XML_FORMAT_ERROR";
+const QString minimal_supported_version = "1.5.0";
 }
 
 ProjectDocument::ProjectDocument()
@@ -202,7 +203,6 @@ QString ProjectDocument::getDocumentVersion() const
 {
     QString result(m_currentVersion);
     if(result.isEmpty()) result = GUIHelpers::getBornAgainVersionString();
-    return QString("1.2.0");
     return result;
 }
 
@@ -223,6 +223,15 @@ void ProjectDocument::readFrom(QIODevice *device)
                 m_currentVersion = reader.attributes()
                                             .value(ProjectDocumentXML::BornAgainVersionAttribute)
                                             .toString();
+                if(!GUIHelpers::isVersionMatchMinimal(m_currentVersion, minimal_supported_version)) {
+                    m_documentStatus = EDocumentStatus(m_documentStatus | STATUS_FAILED);
+                    QString message = QString("Can't open document version '%1', "
+                        "minimal supported version '%2'").arg(m_currentVersion)
+                            .arg(minimal_supported_version);
+                    m_messageService->send_message(this, OPEN_FILE_ERROR, message);
+                    return;
+                }
+
             }
 
             else if (reader.name() == ProjectDocumentXML::InfoTag) {
@@ -289,14 +298,12 @@ void ProjectDocument::writeTo(QIODevice *device)
 
 void ProjectDocument::readModel(SessionModel *model, QXmlStreamReader *reader)
 {
-    model->setMessageService(m_messageService);
-
-    model->readFrom(reader);
+//    model->setMessageService(m_messageService);
 
+    model->readFrom(reader, m_messageService);
     if(m_messageService->hasWarnings(model)) {
         m_documentStatus = EDocumentStatus(m_documentStatus|STATUS_WARNING);
     }
-    model->setMessageService(0);
 }
 
 //! Adjusts name of IntensityData item to possibly changed name of JobItem. Take care of old
diff --git a/GUI/coregui/utils/GUIHelpers.cpp b/GUI/coregui/utils/GUIHelpers.cpp
index 871e41d5aa0..ca8bd6cbae5 100644
--- a/GUI/coregui/utils/GUIHelpers.cpp
+++ b/GUI/coregui/utils/GUIHelpers.cpp
@@ -150,5 +150,37 @@ QString getValidFileName(const QString &proposed_name)
     return result;
 }
 
+//! parses version string into 3 numbers, returns true in the case of success
+bool parseVersion(const QString &version, int &major_num, int &minor_num, int &patch_num)
+{
+    major_num = minor_num = patch_num = 0;
+    bool success(true);
+    QStringList nums = version.split(QStringLiteral("."));
+    if(nums.size() != 3) return false;
+
+    bool ok(false);
+    major_num = nums.at(0).toInt(&ok); success &= ok;
+    minor_num = nums.at(1).toInt(&ok); success &= ok;
+    patch_num = nums.at(2).toInt(&ok); success &= ok;
+
+    return success;
+}
+
+
+//! returns true if current BornAgain version match minimal required version
+bool isVersionMatchMinimal(const QString &version, const QString &minimal_version)
+{
+    int ba_major(0), ba_minor(0), ba_patch(0);
+    if(!parseVersion(version, ba_major, ba_minor, ba_patch))
+        return false;
+
+    int minv_major(0), minv_minor(0), minv_patch(0);
+    if(!parseVersion(minimal_version, minv_major, minv_minor, minv_patch))
+        return false;
+
+    int ba = ba_major*10000 + ba_minor*100 + ba_patch;
+    int minv = minv_major*10000 + minv_minor*100 + minv_patch;
+    return ba >= minv;
+}
 
 } // namespace GUIHelpers
diff --git a/GUI/coregui/utils/GUIHelpers.h b/GUI/coregui/utils/GUIHelpers.h
index 4bec92f55f6..f87765365f5 100644
--- a/GUI/coregui/utils/GUIHelpers.h
+++ b/GUI/coregui/utils/GUIHelpers.h
@@ -57,6 +57,10 @@ BA_CORE_API_ QString getBornAgainVersionString();
 
 BA_CORE_API_ QString getValidFileName(const QString &proposed_name);
 
+BA_CORE_API_ bool parseVersion(const QString &version, int &major_num, int &minor_num, int &patch_num);
+
+BA_CORE_API_ bool isVersionMatchMinimal(const QString &version, const QString &minimal_version);
+
 template<class T, class... Ts> std::unique_ptr<T> make_unique(Ts&&... params)
 {
     return std::unique_ptr<T>(new T(std::forward<Ts>(params)...));
diff --git a/Tests/UnitTests/TestGUI/TestFormFactorItems.h b/Tests/UnitTests/TestGUI/TestFormFactorItems.h
index 6ec81cebc0f..1680afb36dd 100644
--- a/Tests/UnitTests/TestGUI/TestFormFactorItems.h
+++ b/Tests/UnitTests/TestGUI/TestFormFactorItems.h
@@ -1,7 +1,6 @@
 #ifndef TESTFORMFACTORITEMS_H
 #define TESTFORMFACTORITEMS_H
 
-
 #include <QtTest>
 #include "FormFactors.h"
 #include "FormFactorItems.h"
diff --git a/Tests/UnitTests/TestGUI/TestGUI.cpp b/Tests/UnitTests/TestGUI/TestGUI.cpp
index c99f1fc2fa3..bffe0132159 100644
--- a/Tests/UnitTests/TestGUI/TestGUI.cpp
+++ b/Tests/UnitTests/TestGUI/TestGUI.cpp
@@ -14,6 +14,7 @@
 #include "TestGUICoreObjectCorrespondence.h"
 #include "TestMapperForItem.h"
 #include "TestParticleDistributionItem.h"
+#include "TestGUIHelpers.h"
 
 int main(int argc, char** argv) {
     QCoreApplication app(argc, argv);
@@ -31,6 +32,7 @@ int main(int argc, char** argv) {
     TestMapperCases testMapperCases;
     TestMapperForItem testMapperForItem;
     TestParticleDistributionItem testParticleDistributionItem;
+    TestGUIHelpers testGUIHelpers;
 
     bool status(false);
 
@@ -47,6 +49,7 @@ int main(int argc, char** argv) {
     status |= QTest::qExec(&testSessionModel, argc, argv);
     status |= QTest::qExec(&testMapperForItem, argc, argv);
     status |= QTest::qExec(&testParticleDistributionItem, argc, argv);
+    status |= QTest::qExec(&testGUIHelpers, argc, argv);
 
     return status;
 }
diff --git a/Tests/UnitTests/TestGUI/TestGUIHelpers.h b/Tests/UnitTests/TestGUI/TestGUIHelpers.h
new file mode 100644
index 00000000000..9500889b1e9
--- /dev/null
+++ b/Tests/UnitTests/TestGUI/TestGUIHelpers.h
@@ -0,0 +1,35 @@
+#ifndef TESTGUIHELPERS_H
+#define TESTGUIHELPERS_H
+
+#include <QtTest>
+#include "GUIHelpers.h"
+
+class TestGUIHelpers : public QObject {
+    Q_OBJECT
+
+private slots:
+    void test_VersionString();
+};
+
+inline void TestGUIHelpers::test_VersionString()
+{
+    int vmajor(0), vminor(0), vpatch(0);
+
+    QCOMPARE(true, GUIHelpers::parseVersion(QString("1.5.0"), vmajor, vminor, vpatch));
+    QCOMPARE(1, vmajor);
+    QCOMPARE(5, vminor);
+    QCOMPARE(0, vpatch);
+
+    QCOMPARE(false, GUIHelpers::parseVersion(QString("15.0"), vmajor, vminor, vpatch));
+
+    QString min_version("1.5.0");
+    QCOMPARE(GUIHelpers::isVersionMatchMinimal("1.5.0", min_version), true);
+    QCOMPARE(GUIHelpers::isVersionMatchMinimal("1.5.1", min_version), true);
+    QCOMPARE(GUIHelpers::isVersionMatchMinimal("1.6.0", min_version), true);
+    QCOMPARE(GUIHelpers::isVersionMatchMinimal("2.4.9", min_version), true);
+
+    QCOMPARE(GUIHelpers::isVersionMatchMinimal("1.4.9", min_version), false);
+    QCOMPARE(GUIHelpers::isVersionMatchMinimal("0.6.9", min_version), false);
+}
+
+#endif
-- 
GitLab