[education/rkward] rkward: More tests. Fix possible hang in prevStatement()

Thomas Friedrichsmeier null at kde.org
Mon May 26 15:06:38 BST 2025


Git commit 536ed4bbdbfa06ee1a29ae0c859fea1290fa0d7e by Thomas Friedrichsmeier.
Committed on 26/05/2025 at 14:06.
Pushed by tfry into branch 'master'.

More tests. Fix possible hang in prevStatement()

M  +50   -4    rkward/autotests/rkparsedscript_test.cpp
M  +3    -2    rkward/misc/rkparsedscript.cpp

https://invent.kde.org/education/rkward/-/commit/536ed4bbdbfa06ee1a29ae0c859fea1290fa0d7e

diff --git a/rkward/autotests/rkparsedscript_test.cpp b/rkward/autotests/rkparsedscript_test.cpp
index cdf5d1aa9..7761fa28e 100644
--- a/rkward/autotests/rkparsedscript_test.cpp
+++ b/rkward/autotests/rkparsedscript_test.cpp
@@ -72,6 +72,32 @@ class RKParsedScriptTest : public QObject {
 		RK_Debug::RK_Debug_Level = DL_DEBUG;
 	}
 
+	void sanityTest() {
+		// no matter where we go, and for how long, we shall not crash or hang!
+		loadScript(u"script1.R"_s);
+		for (int startpos = 0; startpos < script.length(); ++startpos) {
+			const auto ctx0 = ps.contextAtPos(script.length() / 2);
+			auto ctx = ctx0;
+			while (ctx.valid()) ctx = ps.nextContext(ctx);
+			ctx = ctx0;
+			while (ctx.valid()) ctx = ps.nextSibling(ctx);
+			ctx = ctx0;
+			while (ctx.valid()) ctx = ps.nextSiblingOrOuter(ctx);
+			ctx = ctx0;
+			while (ctx.valid()) ctx = ps.nextStatement(ctx);
+			ctx = ctx0;
+			while (ctx.valid()) ctx = ps.prevContext(ctx);
+			ctx = ctx0;
+			while (ctx.valid()) ctx = ps.prevSibling(ctx);
+			ctx = ctx0;
+			while (ctx.valid()) ctx = ps.prevSiblingOrOuter(ctx);
+			ctx = ctx0;
+			while (ctx.valid()) ctx = ps.prevStatement(ctx);
+			ctx = ctx0;
+			while (ctx.valid()) ctx = ps.parentRegion(ctx);
+		}
+	}
+
 	void nextPrevStatement() {
 		loadScript(u"script1.R"_s);
 //		ps.serialize();
@@ -83,7 +109,7 @@ class RKParsedScriptTest : public QObject {
 		ctx = moveAndCheck(ps.nextStatement(ctx), u"Symbol19"_s);
 		ctx = moveAndCheck(ps.nextStatement(ctx), u"Symbol.x"_s);
 		ctx = moveAndCheck(ps.nextStatement(ctx), u"FunctionList"_s);
-		QVERIFY(!ps.nextStatement(ctx).valid()); // NOTE this need not be set in stone
+		QVERIFY(!ps.nextStatement(ctx).valid()); // NOTE this need not be set in stone, could e.g. wrap around
 
 		testLog("Block2");
 		ctx = moveAndCheck(ps.prevStatement(ctx), u"Symbol.x"_s);
@@ -93,6 +119,14 @@ class RKParsedScriptTest : public QObject {
 		//QVERIFY(!ps.prevStatement(ctx).valid());  // not sure that we want this
 
 		testLog("Block3");
+		ctx = ps.contextAtPos(script.indexOf(u"Symbol11"));
+		ctx = moveAndCheck(ps.nextStatement(ctx), u"Symbol13"_s);
+		ctx = moveAndCheck(ps.nextStatement(ctx), u"Symbol15"_s);
+		ctx = moveAndCheck(ps.nextStatement(ctx), u"Symbol16"_s);
+		ctx = moveAndCheck(ps.nextStatement(ctx), u"Symbol18"_s);
+		ctx = moveAndCheck(ps.nextStatement(ctx), u"Symbol19"_s);
+
+		testLog("Block4");
 		const auto symb18 = ps.contextAtPos(script.indexOf(u"Symbol18"));
 		ctx = moveAndCheck(ps.prevStatement(symb18), u"Symbol16"_s);
 		ctx = moveAndCheck(ps.prevStatement(ctx), u"Symbol15"_s);
@@ -104,12 +138,24 @@ class RKParsedScriptTest : public QObject {
 		ctx = moveAndCheck(ps.prevStatement(ctx), u"Symbol03"_s);
 		ctx = moveAndCheck(ps.prevStatement(ctx), u"Symbol01"_s);
 
-		testLog("Block4");
+		testLog("Block5");
 		const auto symb14 = ps.contextAtPos(script.indexOf(u"Symbol14"));
 		ctx = moveAndCheck(ps.prevStatement(symb14), u"Symbol11"_s);  // shall stay in parenthesis
 		ctx = moveAndCheck(ps.prevStatement(ctx), u"Symbol10"_s); // shall move out
 
-		testLog("Block5");
+		testLog("Block6");
+		ctx = ps.contextAtPos(script.indexOf(u"Argname"));
+		ctx = moveAndCheck(ps.nextStatement(ctx), u"makeFunction"_s);
+		QVERIFY(!ps.nextStatement(ctx).valid()); // NOTE this need not be set in stone, could e.g. wrap around
+
+		testLog("Block7");
+		ctx = ps.contextAtPos(script.indexOf(u"{ aaa"));
+		ctx = moveAndCheck(ps.nextStatement(ctx), u"{"_s);
+		ctx = moveAndCheck(ps.nextStatement(ctx), u"ddd"_s);
+		ctx = moveAndCheck(ps.nextStatement(ctx), u"eee"_s);
+		ctx = moveAndCheck(ps.nextStatement(ctx), u"jjj"_s);
+
+		testLog("Block7");
 		const auto symbjjj = ps.contextAtPos(script.indexOf(u"jjj"));
 		ctx = moveAndCheck(ps.prevStatement(symbjjj), u"eee"_s);
 		ctx = moveAndCheck(ps.prevStatement(ctx), u"ddd"_s);
@@ -119,7 +165,7 @@ class RKParsedScriptTest : public QObject {
 		ctx = moveAndCheck(ps.prevStatement(ctx), u"Argname"_s);
 		ctx = moveAndCheck(ps.prevStatement(ctx), u"FunctionList"_s);
 
-		testLog("Block6");
+		testLog("Block8");
 		const auto symbnest3 = ps.contextAtPos(script.indexOf(u"nest3"));
 		ctx = moveAndCheck(ps.prevStatement(symbnest3), u"nest2"_s);
 		ctx = moveAndCheck(ps.prevStatement(ctx), u"{"_s);
diff --git a/rkward/misc/rkparsedscript.cpp b/rkward/misc/rkparsedscript.cpp
index 1ad78a93e..e4b41719d 100644
--- a/rkward/misc/rkparsedscript.cpp
+++ b/rkward/misc/rkparsedscript.cpp
@@ -116,15 +116,16 @@ RKParsedScript::ContextIndex RKParsedScript::contextAtPos(int pos) const {
 }
 
 RKParsedScript::ContextIndex RKParsedScript::nextContext(const ContextIndex from) const {
+	if (!from.valid()) return ContextIndex();
 	int i = from.index + 1;
 	if (i >= (int) context_list.size()) i = -1;
 	return ContextIndex(i);
 }
 
 RKParsedScript::ContextIndex RKParsedScript::prevContext(const ContextIndex from) const {
+	if (!from.valid()) return ContextIndex();
 	int i = from.index - 1;
-	if (i >= (int) context_list.size()) i = -1;
-	return ContextIndex(i);
+	return ContextIndex(i); // automatically invalid, if reversing past 0
 }
 
 /** Find the innermost region (Context::maybeNesting()) containing this context */



More information about the rkward-tracker mailing list