[education/rkward] rkward: Add support for running autotests at the CPP level (in addition to the existing plugintests)

Thomas Friedrichsmeier null at kde.org
Sat Jun 11 15:24:21 BST 2022


Git commit f6db47e97d57ff03fa0abb2e496443980dff9696 by Thomas Friedrichsmeier.
Committed on 09/06/2022 at 19:58.
Pushed by tfry into branch 'master'.

Add support for running autotests at the CPP level (in addition to the existing plugintests)

M  +8    -4    rkward/CMakeLists.txt
A  +18   -0    rkward/autotests/CMakeLists.txt
A  +3    -0    rkward/autotests/README.txt
A  +76   -0    rkward/autotests/core_test.cpp  *
M  +1    -1    rkward/main.cpp
M  +3    -2    rkward/misc/rkcommonfunctions.cpp
M  +3    -2    rkward/rbackend/rkfrontendtransmitter.cpp
M  +1    -0    rkward/rbackend/rksessionvars.h
M  +3    -1    rkward/rkward.cpp
M  +2    -0    rkward/rkward.h

The files marked with a * at the end have a non valid license. Please read: https://community.kde.org/Policies/Licensing_Policy and use the headers which are listed at that page.


https://invent.kde.org/education/rkward/commit/f6db47e97d57ff03fa0abb2e496443980dff9696

diff --git a/rkward/CMakeLists.txt b/rkward/CMakeLists.txt
index c28a74ba..8026a97a 100644
--- a/rkward/CMakeLists.txt
+++ b/rkward/CMakeLists.txt
@@ -26,12 +26,14 @@ ADD_SUBDIRECTORY( core )
 ADD_SUBDIRECTORY( icons )
 ADD_SUBDIRECTORY( windows )
 ADD_SUBDIRECTORY( syntax )
+IF(BUILD_TESTING)
+	ADD_SUBDIRECTORY(autotests)
+ENDIF(BUILD_TESTING)
 
 INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} )
 
 SET(RKWard_Sources
 	rkward.cpp
-	main.cpp
 	robjectviewer.cpp
 	rkconsole.cpp
 )
@@ -45,11 +47,12 @@ LINK_DIRECTORIES(${R_SHAREDLIBDIR})
 FILE(GLOB ICON_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/icons/app-icon/*-apps-rkward.png")
 # recent ECM versions will prefer the scalable version: prepend it to the icon source list (older versions generate a warning on the SVG, but so what):
 ECM_ADD_APP_ICON(RKWard_Sources ICONS ${CMAKE_CURRENT_SOURCE_DIR}/icons/app-icon/sc-apps-rkward.svgz ${ICON_SRCS})
-ADD_EXECUTABLE(rkward ${RKWard_Sources})
+ADD_LIBRARY(rkward_lib STATIC ${RKWard_Sources}) # For auto-tests: pack everything except main in a dummy library
+TARGET_COMPILE_DEFINITIONS(rkward_lib PUBLIC -DR_EXECUTABLE="${R_EXECUTABLE}")
+ADD_EXECUTABLE(rkward main.cpp)
 
 # NOTE: These definitions are needed for the startup procedure (main.cpp), only.
 # We should switch with to target_compile_definitions once we require CMAKE 2.6+
-ADD_DEFINITIONS(-DR_EXECUTABLE="${R_EXECUTABLE}")
 ADD_DEFINITIONS(-DINSTALL_PATH="${CMAKE_INSTALL_PREFIX}")
 SET_TARGET_PROPERTIES(rkward PROPERTIES
         MACOSX_BUNDLE_BUNDLE_NAME "RKWard")
@@ -63,7 +66,8 @@ IF(APPLE)
 		@ONLY)
 ENDIF(APPLE)
 
-TARGET_LINK_LIBRARIES(rkward windows ${RKWARD_ADDLIBS} agents dialogs plugin settings dataeditor core scriptbackends rbackend misc KF5::WindowSystem Qt5::Widgets KF5::XmlGui)
+TARGET_LINK_LIBRARIES(rkward_lib windows ${RKWARD_ADDLIBS} agents dialogs plugin settings dataeditor core scriptbackends rbackend misc KF5::WindowSystem Qt5::Widgets KF5::XmlGui)
+TARGET_LINK_LIBRARIES(rkward rkward_lib)
 IF(KF5Crash_FOUND)
 	TARGET_LINK_LIBRARIES(rkward KF5::Crash)
 	SET_SOURCE_FILES_PROPERTIES(main.cpp PROPERTIES COMPILE_DEFINITIONS WITH_KCRASH=1)
diff --git a/rkward/autotests/CMakeLists.txt b/rkward/autotests/CMakeLists.txt
new file mode 100644
index 00000000..11e8a3e1
--- /dev/null
+++ b/rkward/autotests/CMakeLists.txt
@@ -0,0 +1,18 @@
+include(ECMMarkAsTest)
+
+find_package(Qt5 5.9 QUIET REQUIRED Test)
+
+macro(rkward_executable_tests)
+  foreach(_testname ${ARGN})
+    add_executable(${_testname} ${_testname}.cpp)
+
+    target_link_libraries(${_testname} PRIVATE rkward_lib Qt5::Test)
+
+    add_test(NAME rkward-${_testname} COMMAND ${_testname})
+    ecm_mark_as_test(${_testname})
+  endforeach()
+endmacro()
+
+rkward_executable_tests(
+  core_test
+)
diff --git a/rkward/autotests/README.txt b/rkward/autotests/README.txt
new file mode 100644
index 00000000..b763e5dc
--- /dev/null
+++ b/rkward/autotests/README.txt
@@ -0,0 +1,3 @@
+Note: This directoy contains autotest running at the C++-level. Please do not overlook the separate plugintests!
+
+(The plugintests are much more extensive at the time of this writing, but cannot cover internals, well.)
diff --git a/rkward/autotests/core_test.cpp b/rkward/autotests/core_test.cpp
new file mode 100644
index 00000000..ada5f77f
--- /dev/null
+++ b/rkward/autotests/core_test.cpp
@@ -0,0 +1,76 @@
+#include <QObject>
+#include <QTest>
+#include <QApplication>
+
+#include <KAboutData>
+
+#include "../debug.h"
+#include "../rkward.h"
+#include "../agents/rkquitagent.h"
+#include "../rbackend/rksessionvars.h"
+#include "../rbackend/rkrinterface.h"
+
+void RKDebug (int, int, const char*, ...) {
+	// disabled for now
+}
+
+class RKWardCoreTest: public QObject {
+    Q_OBJECT
+
+    void runCommandWithTimeout(RCommand *command, RCommandChain* chain, std::function<void(RCommand*)> callback, int timeoutms = 1000) {
+	    QString ccopy = command->command();
+	    QElapsedTimer t;
+	    t.start();
+	    bool done = false;
+	    bool *_done = &done;
+	    connect(command->notifier(), &RCommandNotifier::commandFinished, this, [_done, callback](RCommand *command) { *_done = true; callback(command); });
+	    RInterface::issueCommand(command, chain);
+	    while (!done && t.elapsed() < timeoutms) {
+		    qApp->processEvents();
+	    }
+	    if (!done) {
+		    qDebug("Command timed out: %s", qPrintable(ccopy));
+		    QFAIL("Command timed out");
+	    }
+    }
+    
+    QPointer<RKWardMainWindow> main_win;
+private slots:
+	void init() {
+	}
+	void initTestCase()
+	{
+		KAboutData::setApplicationData(KAboutData("rkward")); // needed for .rc files to load
+		RK_Debug::RK_Debug_Level = DL_WARNING;
+		qDebug(R_EXECUTABLE);
+		RKSessionVars::r_binary = R_EXECUTABLE;
+		main_win = new RKWardMainWindow();
+		main_win->testmode_suppress_dialogs = true;
+		while (!(RInterface::instance()->backendIsDead() || RInterface::instance()->backendIsIdle())) {
+			qApp->processEvents();
+		}
+		qDebug("Startup completed");
+	}
+
+	void getIntVector() {
+		auto c = new RCommand("c(1, 2, 3)", RCommand::GetIntVector);
+		runCommandWithTimeout(c, nullptr, [](RCommand *command) {
+			QCOMPARE(command->getDataType(), RData::IntVector);
+			QCOMPARE(command->getDataLength(), 3);
+			QCOMPARE(command->intVector().value(1), 2);
+		});
+	}
+
+	void cleanupTestCase()
+	{
+		// at least the backend should exit properly, to avoid creating emergency save files
+		RInterface::issueCommand(new RCommand(QString(), RCommand::QuitCommand));
+		while (!(RInterface::instance()->backendIsDead())) {
+			qApp->processEvents();
+		}
+	}
+};
+
+QTEST_MAIN(RKWardCoreTest)
+
+#include "core_test.moc"
diff --git a/rkward/main.cpp b/rkward/main.cpp
index fb6be476..cf788c07 100644
--- a/rkward/main.cpp
+++ b/rkward/main.cpp
@@ -93,7 +93,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
 #include "version.h"
 
 #ifndef R_EXECUTABLE
-#	define R_EXECUTABLE ""
+#error config problem
 #endif
 
 #ifdef Q_OS_WIN
diff --git a/rkward/misc/rkcommonfunctions.cpp b/rkward/misc/rkcommonfunctions.cpp
index 04b26f85..d82ac627 100644
--- a/rkward/misc/rkcommonfunctions.cpp
+++ b/rkward/misc/rkcommonfunctions.cpp
@@ -166,9 +166,10 @@ namespace RKCommonFunctions {
 		static QString rkward_data_dir;
 		if (rkward_data_dir.isNull ()) {
 			QString inside_build_tree = QCoreApplication::applicationDirPath() + "/rkwardinstall/";
-			if (QFileInfo(inside_build_tree).isReadable()) {
+			QString inside_build_tree2 = QCoreApplication::applicationDirPath() + "/../rkwardinstall/";
+			if (QFile::exists(inside_build_tree) || QFile::exists(inside_build_tree2)) {
 				RK_DEBUG(APP, DL_INFO, "Running from inside build tree");
-				rkward_data_dir = inside_build_tree;
+				rkward_data_dir = QFile::exists(inside_build_tree) ? inside_build_tree : inside_build_tree2;
 				return rkward_data_dir;
 			}
 #if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
diff --git a/rkward/rbackend/rkfrontendtransmitter.cpp b/rkward/rbackend/rkfrontendtransmitter.cpp
index bb7599d3..6e84042b 100644
--- a/rkward/rbackend/rkfrontendtransmitter.cpp
+++ b/rkward/rbackend/rkfrontendtransmitter.cpp
@@ -92,10 +92,11 @@ void RKFrontendTransmitter::run () {
 
 	QString backend_executable = findBackendAtPath (QCoreApplication::applicationDirPath ());
 #ifdef Q_OS_MACOS
-	if (backend_executable.isEmpty ()) backend_executable = findBackendAtPath (QCoreApplication::applicationDirPath () + "../Resources"); // an appropriate location in a standalone app-bundle
+	if (backend_executable.isEmpty ()) backend_executable = findBackendAtPath (QCoreApplication::applicationDirPath () + "/../Resources"); // an appropriate location in a standalone app-bundle
 #endif
 	if (backend_executable.isEmpty ()) backend_executable = findBackendAtPath (QCoreApplication::applicationDirPath () + "/rbackend");	// for running directly from the build-dir
-	if (backend_executable.isEmpty ()) backend_executable = findBackendAtPath (QCoreApplication::applicationDirPath () + "../lib/libexec");
+	if (backend_executable.isEmpty ()) backend_executable = findBackendAtPath (QCoreApplication::applicationDirPath () + "/../rbackend");	// for running directly from the build-test-dir
+	if (backend_executable.isEmpty ()) backend_executable = findBackendAtPath (QCoreApplication::applicationDirPath () + "/../lib/libexec");
 #ifdef Q_OS_MACOS
 	if (backend_executable.isEmpty ()) backend_executable = findBackendAtPath (QCoreApplication::applicationDirPath () + "/../../../rbackend");
 	if (backend_executable.isEmpty ()) backend_executable = findBackendAtPath (QCoreApplication::applicationDirPath () + "/../Frameworks/libexec");  // For running from .dmg created by craft --package rkward
diff --git a/rkward/rbackend/rksessionvars.h b/rkward/rbackend/rksessionvars.h
index 56ec9580..ef5a0602 100644
--- a/rkward/rbackend/rksessionvars.h
+++ b/rkward/rbackend/rksessionvars.h
@@ -49,6 +49,7 @@ private:
 	static RKParsedVersion r_version;
 	static QString r_version_string;
 friend int main(int, char**);
+friend class RKWardCoreTest;
 	static QString r_binary;
 };
 
diff --git a/rkward/rkward.cpp b/rkward/rkward.cpp
index 951796c6..414b0561 100644
--- a/rkward/rkward.cpp
+++ b/rkward/rkward.cpp
@@ -112,6 +112,8 @@ RKWardMainWindow::RKWardMainWindow () : KParts::MainWindow ((QWidget *)0, (Qt::W
 	RK_TRACE (APP);
 	RK_ASSERT (rkward_mainwin == 0);
 
+	Q_INIT_RESOURCE(resources);
+	testmode_suppress_dialogs = false;
 	gui_rebuild_locked = true;
 	no_ask_save = true;
 	workspace_modified = false;
@@ -226,7 +228,7 @@ void RKWardMainWindow::doPostInit () {
 	gui_rebuild_locked = false;
 
 	show ();
-	RKSetupWizard::doAutoCheck();
+	if (!testmode_suppress_dialogs) RKSetupWizard::doAutoCheck();
 	KMessageBox::enableMessage ("external_link_warning");  // can only be disabled per session
 
 	QUrl recover_url = RKRecoverDialog::checkRecoverCrashedWorkspace ();
diff --git a/rkward/rkward.h b/rkward/rkward.h
index 2d48c145..5554738e 100644
--- a/rkward/rkward.h
+++ b/rkward/rkward.h
@@ -187,6 +187,8 @@ private:
 
 	KatePluginIntegrationApp *katepluginintegration;
 	KXMLGUIClient *active_ui_buddy;
+friend class RKWardCoreTest;
+	bool testmode_suppress_dialogs;
 };
 
 #endif // RKWARD_H



More information about the rkward-tracker mailing list