From ef1bf44531b260c5ae8f316797255e6f90eada0d Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (h)" <j.wuttke@fz-juelich.de>
Date: Wed, 5 Aug 2020 20:22:25 +0200
Subject: [PATCH] tAsserts now works with qFatal

---
 CMakeLists.txt                             |  2 +-
 Core/Basics/Assert.h                       | 21 ++++----
 Core/CMakeLists.txt                        |  4 ++
 GUI/main/CMakeLists.txt                    |  4 +-
 GUI/main/MessageHandler.cpp                | 62 ++++++++++++++++++++++
 GUI/main/MessageHandler.h                  | 17 ++++++
 GUI/main/main.cpp                          |  5 +-
 Tests/UnitTests/Core/Basics/TestAssert.cpp |  6 ++-
 Tests/UnitTests/Core/CMakeLists.txt        |  7 ++-
 9 files changed, 107 insertions(+), 21 deletions(-)
 create mode 100644 GUI/main/MessageHandler.cpp
 create mode 100644 GUI/main/MessageHandler.h

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 96d6befb381..5ed494eb306 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -67,6 +67,7 @@ if(ZERO_TOLERANCE)
     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wfatal-errors")
 endif()
 if(BORNAGAIN_GUI)
+    include(SearchQt)
     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_MESSAGELOGCONTEXT=ON")
 endif()
 include(BornAgainConfiguration)
@@ -108,7 +109,6 @@ add_subdirectory(Tests/UnitTests/Numeric)
 add_subdirectory(Tests/Performance/Core)
 
 if(BORNAGAIN_GUI)
-    include(SearchQt)
     add_subdirectory(ThirdParty/GUI)
     add_subdirectory(GUI)
     add_subdirectory(Tests/UnitTests/GUI)
diff --git a/Core/Basics/Assert.h b/Core/Basics/Assert.h
index b9a2d457f1d..fd0e15b53c4 100644
--- a/Core/Basics/Assert.h
+++ b/Core/Basics/Assert.h
@@ -15,22 +15,19 @@
 #ifndef BORNAGAIN_CORE_BASICS_ASSERT_H
 #define BORNAGAIN_CORE_BASICS_ASSERT_H
 
-#ifdef BORNAGAIN_GUI
+// ASSERT must be declared as a macro, not a function, in order for the error
+// message to correctly report the source line where the assertion failed.
+
+#ifdef QT_MESSAGELOGCONTEXT
 
 #include <QtGlobal>
-#define ASSERT(condition) \
-    if (!(condition)) \
-        qFatal("assertion failed");
+#define ASSERT(condition) if (!(condition)) qFatal("Assertion failed")
 
-#else // The non-GUI case is used by our test suite
+#else // QT_MESSAGELOGCONTEXT undefined
 
-#include <iostream>
-#define ASSERT(condition) \
-    if (!(condition)) { \
-        std::cerr << "assertion failed" << std::endl; \
-        exit(1); \
-    }
+#include <cassert>
+#define ASSERT(condition) assert(condition)
 
-#endif // BORNAGAIN_GUI
+#endif // QT_MESSAGELOGCONTEXT
 
 #endif // BORNAGAIN_CORE_BASICS_ASSERT_H
diff --git a/Core/CMakeLists.txt b/Core/CMakeLists.txt
index 505ae524815..546a2dfae39 100644
--- a/Core/CMakeLists.txt
+++ b/Core/CMakeLists.txt
@@ -154,6 +154,10 @@ target_include_directories(${library_name}
     ${CMAKE_SOURCE_DIR}/ThirdParty/Core/cerf_wrapper # TEMPORARY
     SYSTEM PUBLIC ${EIGEN3_INCLUDE_DIRS}
     )
+if(BORNAGAIN_GUI)
+    target_include_directories(${library_name} PUBLIC ${Qt5Core_INCLUDE_DIRS})
+endif()
+
 target_link_libraries(${library_name} ${Boost_LIBRARIES} ${FFTW3_LIBRARIES} ${GSL_LIBRARIES}
     ${tspectrum_LIBRARY} ${Cerf_LIBRARIES})
 
diff --git a/GUI/main/CMakeLists.txt b/GUI/main/CMakeLists.txt
index 3e2450a18db..2acb75efd7c 100644
--- a/GUI/main/CMakeLists.txt
+++ b/GUI/main/CMakeLists.txt
@@ -3,8 +3,8 @@
 ###############################################################################
 set(executable_name BornAgain)
 
-set(source_files main.cpp appoptions.cpp)
-set(include_files appoptions.h)
+set(source_files main.cpp appoptions.cpp MessageHandler.cpp)
+set(include_files appoptions.h MessageHandler.h )
 
 # -----------------------------------------------------------------------------
 # Qt configuration
diff --git a/GUI/main/MessageHandler.cpp b/GUI/main/MessageHandler.cpp
new file mode 100644
index 00000000000..db6b72c797c
--- /dev/null
+++ b/GUI/main/MessageHandler.cpp
@@ -0,0 +1,62 @@
+// ************************************************************************** //
+//
+//  BornAgain: simulate and fit scattering at grazing incidence
+//
+//! @file      GUI/main/MessageHandler.cpp
+//! @brief     Implements function MessageHandler
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2018
+//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
+//
+// ************************************************************************** //
+
+#include "GUI/main/MessageHandler.h"
+#include <iostream>
+#include <QMessageBox>
+
+#ifndef QT_NO_DEBUG
+#define context(ctx) " [" << ctx.function << "]"
+#else
+#define context(ctx) ""
+#endif
+
+//! This is set by main to be the message handler of our GUI.
+void MessageHandler(QtMsgType type, const QMessageLogContext& ctx, const QString& msg)
+{
+    switch (type) {
+        case QtDebugMsg:
+            if (!msg.size()) // KDE will pass a zero-length msg qstring
+                break;
+            std::cerr << "DEBUG: " << msg.toStdString() << std::endl;
+            break;
+        case QtInfoMsg:
+            std::cerr << "INFO: " << msg.toStdString() << context(ctx) << std::endl;
+            break;
+        case QtWarningMsg:
+        default:
+            if (msg.left(4) == "QXcb")
+                return;
+            std::cerr << "WARNING: " << msg.toStdString() <<  " [" << ctx.function << "]"
+                      << std::endl;
+            QMessageBox::warning(QApplication::activeWindow(), qAppName(), msg);
+            break;
+        case QtFatalMsg:
+            std::cerr << "FATAL: " << msg.toStdString() << " [" << ctx.function << "]" << std::endl;
+            qApp->restoreOverrideCursor();
+            QMessageBox::critical(
+                QApplication::activeWindow(), qAppName(),
+                "Sorry, you encountered a fatal bug.\n"
+                "The application will terminate.\n"
+                "Please note the following and inform the maintainers.\n\n"
+                "Error:\n"
+                    + msg
+                    + "\n"
+                      "Context:\n"
+                    + ctx.function + "\n"
+            );
+            qApp->quit();
+            exit(1);
+    }
+}
diff --git a/GUI/main/MessageHandler.h b/GUI/main/MessageHandler.h
new file mode 100644
index 00000000000..961d4248d01
--- /dev/null
+++ b/GUI/main/MessageHandler.h
@@ -0,0 +1,17 @@
+// ************************************************************************** //
+//
+//  BornAgain: simulate and fit scattering at grazing incidence
+//
+//! @file      GUI/main/MessageHandler.cpp
+//! @brief     Declares function MessageHandler
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2018
+//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
+//
+// ************************************************************************** //
+
+#include <QApplication>
+
+void MessageHandler(QtMsgType type, const QMessageLogContext& ctx, const QString& msg);
diff --git a/GUI/main/main.cpp b/GUI/main/main.cpp
index 9b6d77f59c3..e997a45ba59 100644
--- a/GUI/main/main.cpp
+++ b/GUI/main/main.cpp
@@ -17,7 +17,7 @@
 #include "GUI/coregui/mainwindow/mainwindow.h"
 #include "GUI/coregui/utils/hostosinfo.h"
 #include "GUI/main/appoptions.h"
-#include <QApplication>
+#include "GUI/main/MessageHandler.h"
 #include <QLocale>
 #include <QMetaType>
 
@@ -37,8 +37,7 @@ int main(int argc, char* argv[])
 
     QApplication app(argc, argv);
 
-    if (!options.find("with-debug"))
-        qInstallMessageHandler(messageHandler);
+    qInstallMessageHandler(MessageHandler);
 
     std::unique_ptr<SplashScreen> splash;
     if (!options.find("no-splash")) {
diff --git a/Tests/UnitTests/Core/Basics/TestAssert.cpp b/Tests/UnitTests/Core/Basics/TestAssert.cpp
index e52a734c12d..06ef68b29c7 100644
--- a/Tests/UnitTests/Core/Basics/TestAssert.cpp
+++ b/Tests/UnitTests/Core/Basics/TestAssert.cpp
@@ -8,5 +8,9 @@ class TestAssert : public ::testing::Test
 TEST_F(TestAssert, Assert)
 {
     EXPECT_NO_THROW(ASSERT(1));
-    EXPECT_EXIT(ASSERT(0), ::testing::ExitedWithCode(1), "assertion failed");
+#ifdef QT_MESSAGELOGCONTEXT
+    EXPECT_EXIT(ASSERT(0), ::testing::KilledBySignal(6), "Assertion failed");
+#else
+    EXPECT_EXIT(ASSERT(0), ::testing::KilledBySignal(6), "Assertion .* failed");
+#endif
 }
diff --git a/Tests/UnitTests/Core/CMakeLists.txt b/Tests/UnitTests/Core/CMakeLists.txt
index 294b8fcd3ee..6e312354ee6 100644
--- a/Tests/UnitTests/Core/CMakeLists.txt
+++ b/Tests/UnitTests/Core/CMakeLists.txt
@@ -2,11 +2,14 @@ include(GoogleTest) # provides gtest_discover_tests
 
 set(test UnitTestCore)
 
-include_directories(${CMAKE_SOURCE_DIR}/Tests/UnitTests/utilities)
-
 file(GLOB source_files "*/*.cpp" ${CMAKE_SOURCE_DIR}/Tests/GTestWrapper/TestAll.cpp)
 
 add_executable(${test} ${source_files})
+target_include_directories(${test} PUBLIC ${CMAKE_SOURCE_DIR}/Tests/UnitTests/utilities)
 target_link_libraries(${test} ${BornAgainCore_LIBRARY} gtest)
+if(BORNAGAIN_GUI)
+    target_include_directories(${test} PUBLIC ${Qt5Core_INCLUDE_DIRS})
+    target_link_libraries(${test} ${Qt5Core_LIBRARIES})
+endif()
 
 gtest_discover_tests(${test} TEST_PREFIX Core.Unit.)
-- 
GitLab