[education/rkward] rkward: Add test for restarting R backend.

Thomas Friedrichsmeier null at kde.org
Mon Jun 13 19:51:11 BST 2022


Git commit f22dd26c9db653da6f99b1071c31740923442f77 by Thomas Friedrichsmeier.
Committed on 13/06/2022 at 18:51.
Pushed by tfry into branch 'master'.

Add test for restarting R backend.

M  +45   -19   rkward/autotests/core_test.cpp
M  +1    -1    rkward/rkward.cpp

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

diff --git a/rkward/autotests/core_test.cpp b/rkward/autotests/core_test.cpp
index bb8f7f3c..dec6a98a 100644
--- a/rkward/autotests/core_test.cpp
+++ b/rkward/autotests/core_test.cpp
@@ -14,6 +14,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
 #include <QRegularExpression>
 
 #include <KAboutData>
+#include <KActionCollection>
 
 #include "../debug.h"
 #include "../rkward.h"
@@ -34,9 +35,9 @@ void RKDebug (int, int, const char* fmt, ...) {
 }
 
 /** This test suite sets up a mostly complete application. That's a bit heavy, but arguably, a) modularity isn't ideal in RKWard, and b) many of the more interesting
- *  test involve passing commands to the R backend, and then verifying the expected state in the frontend. That alone requires a fairly extensive setup in the first place.
+ *  tests involve passing commands to the R backend, and then verifying the expected state in the frontend. That alone requires a fairly extensive setup, anyway.
  *
- *  Since starting can still take several seconds, the plan, for now, is to run most tests most individual tests inside this single test suite. */
+ *  Since starting can still take several seconds, the plan, for now, is to run most individual tests inside this single test suite. */
 class RKWardCoreTest: public QObject {
 	Q_OBJECT
 
@@ -69,8 +70,26 @@ class RKWardCoreTest: public QObject {
 	void cleanGlobalenv() {
 		RInterface::issueCommand(new RCommand("rm(list=ls(all.names=TRUE))", RCommand::User));
 	}
+
+	void waitForBackendStarted() {
+		QElapsedTimer t;
+		t.start();
+		while (!(RInterface::instance()->backendIsDead() || RInterface::instance()->backendIsIdle())) {
+			if (t.elapsed() > 20000) break;
+			qApp->sendPostedEvents();
+		}
+		if (RInterface::instance()->backendIsIdle()) {
+			qDebug("Backend startup completed");
+		} else {
+			qDebug("Backend startup failed. Listing contents of /tmp/rkward.rbackend");
+			QFile f(QDir::tempPath() + "/rkward.rbackend");
+			f.open(QIODevice::ReadOnly);
+			auto output = f.readAll();
+			qDebug("%s", output.data());
+		}
+	}
     
-    QPointer<RKWardMainWindow> main_win;
+	QPointer<RKWardMainWindow> main_win;
 private slots:
 	void init() {
 	}
@@ -87,22 +106,7 @@ private slots:
 		RKSessionVars::r_binary = R_EXECUTABLE;
 		main_win = new RKWardMainWindow();
 		main_win->testmode_suppress_dialogs = true;
-
-		QElapsedTimer t;
-		t.start();
-		while (!(RInterface::instance()->backendIsDead() || RInterface::instance()->backendIsIdle())) {
-			if (t.elapsed() > 20000) break;
-			qApp->sendPostedEvents();
-		}
-		if (RInterface::instance()->backendIsIdle()) {
-			qDebug("Startup completed");
-		} else {
-			qDebug("Startup failed. Listing contents of /tmp/rkward.rbackend");
-			QFile f(QDir::tempPath() + "/rkward.rbackend");
-			f.open(QIODevice::ReadOnly);
-			auto output = f.readAll();
-			qDebug("%s", output.data());
-		}
+		waitForBackendStarted();
 	}
 
 	void getIntVector() {
@@ -218,6 +222,28 @@ private slots:
 		waitForAllFinished();  // priority_command_done must remain in scope until done
 	}
 
+	void restartRBackend() {
+		auto restart_action = RKWardMainWindow::getMain()->actionCollection()->action("restart_r");
+		QVERIFY(restart_action != nullptr);
+		RInterface::issueCommand(new RCommand("x <- 1", RCommand::User));
+		waitForAllFinished();
+		QVERIFY(RObjectList::getGlobalEnv()->findObject("x"));
+
+		auto oldiface = RInterface::instance();
+		restart_action->trigger();
+		while (RInterface::instance() == oldiface) {  // action may be delayed until next event processing
+			qApp->processEvents();
+		}
+		waitForBackendStarted();
+
+		// backend should be clean after restart
+		QVERIFY(!RObjectList::getGlobalEnv()->findObject("x"));
+		// but of course it should also be functional...
+		RInterface::issueCommand(new RCommand("x <- 1", RCommand::User));
+		waitForAllFinished();
+		QVERIFY(RObjectList::getGlobalEnv()->findObject("x"));
+	}
+
 	void cleanupTestCase()
 	{
 		// at least the backend should exit properly, to avoid creating emergency save files
diff --git a/rkward/rkward.cpp b/rkward/rkward.cpp
index 414b0561..a0657ea8 100644
--- a/rkward/rkward.cpp
+++ b/rkward/rkward.cpp
@@ -619,7 +619,7 @@ void RKWardMainWindow::initActions() {
 	restart_r->setText(i18n("Restart R Backend"));
 	connect(restart_r, &QAction::triggered, this, [this]() {
 		QString message = i18n("<p>This feature is primarily targetted at advanced users working on scripts or packages. Please proceed with caution.</p><p><b>All unsaved data in this workspace will be lost!</b> All data editors, and graphics windows will be closed.</p><p>Are you sure you want to proceed?</p>");
-		if (KMessageBox::warningContinueCancel(this, message, i18n("Restart R backend"), KGuiItem(i18n("Restart R backend"))) == KMessageBox::Continue) {
+		if (suppressModalDialogsForTesting() || (KMessageBox::warningContinueCancel(this, message, i18n("Restart R backend"), KGuiItem(i18n("Restart R backend"))) == KMessageBox::Continue)) {
 			bool forced = RInterface::instance()->backendIsDead();
 			while (!RInterface::instance()->backendIsDead() && !RInterface::instance()->backendIsIdle()) {
 				message = i18n("<p>One or more operations are pending.</p><p>If you have recently chosen to save your workspace, and you see this message, <b>your data may not be saved, yet!</b></p><p>How do you want to proceed?</p>");


More information about the rkward-tracker mailing list