Skip to content

Commit 9c9d62a

Browse files
committed
[refactor] Make XSuite XQuery Functions more resilient to unexpected errors
1 parent 831d0b1 commit 9c9d62a

File tree

8 files changed

+376
-126
lines changed

8 files changed

+376
-126
lines changed

exist-core/pom.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -937,7 +937,13 @@
937937
<include>src/main/java/org/exist/storage/sync/SyncTask.java</include>
938938
<include>src/test/java/org/exist/storage/util/PauseFunction.java</include>
939939
<include>src/main/java/org/exist/test/ExistXmldbEmbeddedServer.java</include>
940+
<include>src/main/java/org/exist/test/runner/ExtTestAssumptionFailedFunction.java</include>
941+
<include>src/main/java/org/exist/test/runner/ExtTestErrorFunction.java</include>
940942
<include>src/main/java/org/exist/test/runner/ExtTestFailureFunction.java</include>
943+
<include>src/main/java/org/exist/test/runner/ExtTestFinishedFunction.java</include>
944+
<include>src/main/java/org/exist/test/runner/ExtTestIgnoredFunction.java</include>
945+
<include>src/main/java/org/exist/test/runner/ExtTestStartedFunction.java</include>
946+
<include>src/main/java/org/exist/test/runner/JUnitIntegrationFunction.java</include>
941947
<include>src/main/java/org/exist/test/runner/XMLTestRunner.java</include>
942948
<include>src/main/java/org/exist/test/runner/XQueryTestRunner.java</include>
943949
<include>src/main/java/org/exist/test/runner/XSuite.java</include>
@@ -1483,7 +1489,13 @@
14831489
<exclude>src/main/java/org/exist/test/DiffMatcher.java</exclude>
14841490
<exclude>src/main/java/org/exist/test/ExistXmldbEmbeddedServer.java</exclude>
14851491
<exclude>src/test/java/org/exist/test/Util.java</exclude>
1492+
<exclude>src/main/java/org/exist/test/runner/ExtTestAssumptionFailedFunction.java</exclude>
1493+
<exclude>src/main/java/org/exist/test/runner/ExtTestErrorFunction.java</exclude>
14861494
<exclude>src/main/java/org/exist/test/runner/ExtTestFailureFunction.java</exclude>
1495+
<exclude>src/main/java/org/exist/test/runner/ExtTestFinishedFunction.java</exclude>
1496+
<exclude>src/main/java/org/exist/test/runner/ExtTestIgnoredFunction.java</exclude>
1497+
<exclude>src/main/java/org/exist/test/runner/ExtTestStartedFunction.java</exclude>
1498+
<exclude>src/main/java/org/exist/test/runner/JUnitIntegrationFunction.java</exclude>
14871499
<exclude>src/main/java/org/exist/test/runner/XMLTestRunner.java</exclude>
14881500
<exclude>src/main/java/org/exist/test/runner/XQueryTestRunner.java</exclude>
14891501
<exclude>src/main/java/org/exist/test/runner/XSuite.java</exclude>
Lines changed: 47 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,28 @@
11
/*
2+
* Elemental
3+
* Copyright (C) 2024, Evolved Binary Ltd
4+
*
5+
6+
* https://www.evolvedbinary.com | https://www.elemental.xyz
7+
*
8+
* This library is free software; you can redistribute it and/or
9+
* modify it under the terms of the GNU Lesser General Public
10+
* License as published by the Free Software Foundation; version 2.1.
11+
*
12+
* This library is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+
* Lesser General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Lesser General Public
18+
* License along with this library; if not, write to the Free Software
19+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20+
*
21+
* NOTE: Parts of this file contain code from 'The eXist-db Authors'.
22+
* The original license header is included below.
23+
*
24+
* =====================================================================
25+
*
226
* eXist-db Open Source Native XML Database
327
* Copyright (C) 2001 The eXist-db Authors
428
*
@@ -19,7 +43,6 @@
1943
* License along with this library; if not, write to the Free Software
2044
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2145
*/
22-
2346
package org.exist.test.runner;
2447

2548
import org.exist.xquery.XPathException;
@@ -34,11 +57,16 @@
3457
import org.junit.runner.notification.Failure;
3558
import org.junit.runner.notification.RunNotifier;
3659

60+
import javax.annotation.Nullable;
61+
3762
import static org.exist.xquery.FunctionDSL.optParam;
3863
import static org.exist.xquery.FunctionDSL.param;
3964
import static org.exist.xquery.FunctionDSL.params;
4065

4166
public class ExtTestAssumptionFailedFunction extends JUnitIntegrationFunction {
67+
68+
private static final StringValue NAME_MAP_KEY = new StringValue("name");
69+
4270
public ExtTestAssumptionFailedFunction(final XQueryContext context, final String parentName, final RunNotifier notifier) {
4371
super("ext-test-assumption-failed-function",
4472
params(
@@ -49,47 +77,41 @@ public ExtTestAssumptionFailedFunction(final XQueryContext context, final String
4977

5078
@Override
5179
public Sequence eval(final Sequence contextSequence, final Item contextItem) throws XPathException {
52-
final Sequence arg1 = getCurrentArguments()[0];
53-
final String name = arg1.itemAt(0).getStringValue();
80+
final Sequence[] args = getCurrentArguments();
81+
if (args.length != 2) {
82+
throw new XPathException(this, "ext-test-assumption-failed-function requires 2 parameters");
83+
}
5484

55-
final Sequence arg2 = getCurrentArguments().length == 2 ? getCurrentArguments()[1] : null;
56-
final MapType assumption = arg2 != null ? (MapType)arg2.itemAt(0) : null;
85+
final Sequence argName = args[0];
86+
if (argName.isEmpty()) {
87+
throw new XPathException(this, "ext-test-assumption-failed-function requires a 'name' parameter");
88+
}
89+
final String name = safeGetStringValue(argName.itemAt(0));
90+
91+
final Sequence argAssumptionError = args[1];
92+
@Nullable final MapType assumptionError = argAssumptionError.isEmpty() ? null : (MapType) argAssumptionError.itemAt(0);
5793

5894
final Description description = createTestDescription(name);
5995

6096
// notify JUnit
6197
try {
62-
final AssumptionViolatedException assumptionFailureReason = assumptionMapAsAssumptionViolationException(assumption);
98+
final AssumptionViolatedException assumptionFailureReason = assumptionMapAsAssumptionViolationException(assumptionError);
6399

64100
// NOTE: We remove the StackTrace, because it is not useful to have a Java Stack Trace pointing into the XML XQuery Test Suite code
65101
assumptionFailureReason.setStackTrace(new StackTraceElement[0]);
66102

67103
notifier.fireTestAssumptionFailed(new Failure(description, assumptionFailureReason));
68-
} catch (final XPathException e) {
104+
} catch (final Throwable t) {
69105
//signal internal failure
70-
notifier.fireTestFailure(new Failure(description, e));
106+
notifier.fireTestFailure(new Failure(description, t));
71107
}
72108

73109
return Sequence.EMPTY_SEQUENCE;
74110
}
75111

76-
public AssumptionViolatedException assumptionMapAsAssumptionViolationException(final MapType assumptionMap) throws XPathException {
77-
final Sequence seqName = assumptionMap.get(new StringValue(this, "name"));
78-
final String name;
79-
if(seqName != null && !seqName.isEmpty()) {
80-
name = seqName.itemAt(0).getStringValue();
81-
} else {
82-
name = "";
83-
}
84-
85-
final Sequence seqValue = assumptionMap.get(new StringValue(this, "value"));
86-
final String value;
87-
if(seqValue != null && !seqValue.isEmpty()) {
88-
value = seqValue.itemAt(0).getStringValue();
89-
} else {
90-
value = "";
91-
}
92-
112+
public AssumptionViolatedException assumptionMapAsAssumptionViolationException(final MapType assumptionMap) {
113+
final String name = safeGetMapStringValue(NAME_MAP_KEY, assumptionMap, "");
114+
final String value = safeGetMapStringValue(VALUE_MAP_KEY, assumptionMap, "");
93115
return new AssumptionViolatedException("Assumption %" + name + " does not hold for: " + value);
94116
}
95117
}

exist-core/src/main/java/org/exist/test/runner/ExtTestErrorFunction.java

Lines changed: 71 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,28 @@
11
/*
2+
* Elemental
3+
* Copyright (C) 2024, Evolved Binary Ltd
4+
*
5+
6+
* https://www.evolvedbinary.com | https://www.elemental.xyz
7+
*
8+
* This library is free software; you can redistribute it and/or
9+
* modify it under the terms of the GNU Lesser General Public
10+
* License as published by the Free Software Foundation; version 2.1.
11+
*
12+
* This library is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+
* Lesser General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Lesser General Public
18+
* License along with this library; if not, write to the Free Software
19+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20+
*
21+
* NOTE: Parts of this file contain code from 'The eXist-db Authors'.
22+
* The original license header is included below.
23+
*
24+
* =====================================================================
25+
*
226
* eXist-db Open Source Native XML Database
327
* Copyright (C) 2001 The eXist-db Authors
428
*
@@ -19,7 +43,6 @@
1943
* License along with this library; if not, write to the Free Software
2044
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2145
*/
22-
2346
package org.exist.test.runner;
2447

2548
import org.exist.xquery.ErrorCodes;
@@ -40,6 +63,12 @@
4063

4164
public class ExtTestErrorFunction extends JUnitIntegrationFunction {
4265

66+
private static final StringValue DESCRIPTION_MAP_KEY = new StringValue("description");
67+
private static final StringValue CODE_MAP_KEY = new StringValue("code");
68+
private static final StringValue LINE_NUMBER_MAP_KEY = new StringValue("line-number");
69+
private static final StringValue COLUMN_NUMBER_MAP_KEY = new StringValue("column-number");
70+
private static final StringValue JAVA_STACK_TRACE_MAP_KEY = new StringValue("java-stack-trace");
71+
4372
public ExtTestErrorFunction(final XQueryContext context, final String parentName, final RunNotifier notifier) {
4473
super("ext-test-error-function",
4574
params(
@@ -50,67 +79,67 @@ public ExtTestErrorFunction(final XQueryContext context, final String parentName
5079

5180
@Override
5281
public Sequence eval(final Sequence contextSequence, final Item contextItem) throws XPathException {
53-
final Sequence arg1 = getCurrentArguments()[0];
54-
final String name = arg1.itemAt(0).getStringValue();
82+
final Sequence[] args = getCurrentArguments();
83+
if (args.length != 2) {
84+
throw new XPathException(this, "ext-test-error-function requires 2 parameters");
85+
}
86+
87+
final Sequence argName = args[0];
88+
if (argName.isEmpty()) {
89+
throw new XPathException(this, "ext-test-error-function requires a 'name' parameter");
90+
}
91+
final String name = safeGetStringValue(argName.itemAt(0));
5592

56-
final Sequence arg2 = getCurrentArguments().length == 2 ? getCurrentArguments()[1] : null;
57-
final MapType error = arg2 != null ? (MapType)arg2.itemAt(0) : null;
93+
final Sequence argError = args[1];
94+
@Nullable final MapType error = argError.isEmpty() ? null : (MapType) argError.itemAt(0);
5895

5996
final Description description = createTestDescription(name);
6097

6198
// notify JUnit
6299
try {
63-
final XPathException errorReason = errorMapAsXPathException(error);
64-
notifier.fireTestFailure(new Failure(description, errorReason));
65-
} catch (final XPathException e) {
100+
final Failure failure;
101+
if (error != null) {
102+
final XPathException errorReason = errorMapAsXPathException(error);
103+
failure = new Failure(description, errorReason);
104+
} else {
105+
failure = new Failure(description, new XPathException(this, "No error map provided to ext-test-error-function"));
106+
}
107+
notifier.fireTestFailure(failure);
108+
} catch (final Throwable t) {
66109
//signal internal failure
67-
notifier.fireTestFailure(new Failure(description, e));
110+
notifier.fireTestFailure(new Failure(description, t));
68111
}
69112

70113
return Sequence.EMPTY_SEQUENCE;
71114
}
72115

73-
private XPathException errorMapAsXPathException(final MapType errorMap) throws XPathException {
74-
final Sequence seqDescription = errorMap.get(new StringValue(this, "description"));
75-
final String description;
76-
if(seqDescription != null && !seqDescription.isEmpty()) {
77-
description = seqDescription.itemAt(0).getStringValue();
78-
} else {
79-
description = "";
80-
}
116+
private XPathException errorMapAsXPathException(final MapType errorMap) {
117+
final Sequence seqDescription = errorMap.get(DESCRIPTION_MAP_KEY);
118+
final String description = safeGetMapStringValue(DESCRIPTION_MAP_KEY, errorMap, "");
81119

82-
final Sequence seqErrorCode = errorMap.get(new StringValue(this, "code"));
120+
final Sequence seqErrorCode = errorMap.get(CODE_MAP_KEY);
83121
final ErrorCodes.ErrorCode errorCode;
84122
if(seqErrorCode != null && !seqErrorCode.isEmpty()) {
85123
errorCode = new ErrorCodes.ErrorCode(((QNameValue)seqErrorCode.itemAt(0)).getQName(), description);
86124
} else {
87125
errorCode = ErrorCodes.ERROR;
88126
}
89127

90-
final Sequence seqLineNumber = errorMap.get(new StringValue(this, "line-number"));
91-
final int lineNumber;
92-
if(seqLineNumber != null && !seqLineNumber.isEmpty()) {
93-
lineNumber = seqLineNumber.itemAt(0).toJavaObject(int.class);
94-
} else {
95-
lineNumber = -1;
96-
}
128+
final Sequence seqLineNumber = errorMap.get(LINE_NUMBER_MAP_KEY);
129+
final int lineNumber = safeGetIntValue(seqLineNumber, 0);
97130

98-
final Sequence seqColumnNumber = errorMap.get(new StringValue(this, "column-number"));
99-
final int columnNumber;
100-
if(seqColumnNumber != null && !seqColumnNumber.isEmpty()) {
101-
columnNumber = seqColumnNumber.itemAt(0).toJavaObject(int.class);
102-
} else {
103-
columnNumber = -1;
131+
final Sequence seqColumnNumber = errorMap.get(COLUMN_NUMBER_MAP_KEY);
132+
final int columnNumber = safeGetIntValue(seqColumnNumber, 0);
133+
134+
@Nullable StackTraceElement[] stackTraceElements = null;
135+
@Nullable final Sequence seqJavaStackTrace = errorMap.get(JAVA_STACK_TRACE_MAP_KEY);
136+
if (seqJavaStackTrace != null && !seqJavaStackTrace.isEmpty()) {
137+
stackTraceElements = convertStackTraceElements(seqJavaStackTrace);
104138
}
105139

106140
final XPathException xpe = new XPathException(lineNumber, columnNumber, errorCode, description);
107-
108-
@Nullable final Sequence seqJavaStackTrace = errorMap.get(new StringValue(this, "java-stack-trace"));
109-
if (seqJavaStackTrace != null && !seqJavaStackTrace.isEmpty()) {
110-
@Nullable final StackTraceElement[] stackTraceElements = convertStackTraceElements(seqJavaStackTrace);
111-
if (stackTraceElements != null) {
112-
xpe.setStackTrace(stackTraceElements);
113-
}
141+
if (stackTraceElements != null) {
142+
xpe.setStackTrace(stackTraceElements);
114143
}
115144

116145
return xpe;
@@ -119,16 +148,16 @@ private XPathException errorMapAsXPathException(final MapType errorMap) throws X
119148
private static final Pattern PTN_CAUSED_BY = Pattern.compile("Caused by:\\s([a-zA-Z0-9_$\\.]+)(?::\\s(.+))?");
120149
private static final Pattern PTN_AT = Pattern.compile("at\\s((?:[a-zA-Z0-9_$]+)(?:\\.[a-zA-Z0-9_$]+)*)\\.((?:[a-zA-Z0-9_$-]+)|(?:<init>))\\(([a-zA-Z0-9_]+\\.java):([0-9]+)\\)");
121150

122-
protected @Nullable StackTraceElement[] convertStackTraceElements(final Sequence seqJavaStackTrace) throws XPathException {
151+
protected @Nullable StackTraceElement[] convertStackTraceElements(final Sequence seqJavaStackTrace) {
123152
StackTraceElement[] traceElements = null;
124153

125154
final Matcher matcherAt = PTN_AT.matcher("");
126155

127156
// index 0 is the first `Caused by: ...`
128157
int i = 1;
129158
for ( ; i < seqJavaStackTrace.getItemCount(); i++) {
130-
final String item = seqJavaStackTrace.itemAt(i).getStringValue();
131-
final StackTraceElement stackTraceElement = convertStackTraceElement(matcherAt, item);
159+
final String item = safeGetStringValue(seqJavaStackTrace.itemAt(i));
160+
@Nullable final StackTraceElement stackTraceElement = convertStackTraceElement(matcherAt, item);
132161
if (stackTraceElement == null) {
133162
break;
134163
}
@@ -153,7 +182,7 @@ private XPathException errorMapAsXPathException(final MapType errorMap) throws X
153182
final String methodName = matcherAt.group(2);
154183
final String fileName = matcherAt.group(3);
155184
final String lineNumber = matcherAt.group(4);
156-
return new StackTraceElement(declaringClass, methodName, fileName, Integer.valueOf(lineNumber));
185+
return new StackTraceElement(declaringClass, methodName, fileName, Integer.parseInt(lineNumber));
157186
} else {
158187
return null;
159188
}

0 commit comments

Comments
 (0)