diff --git a/exist-ant/pom.xml b/exist-ant/pom.xml index 61dc38aa4ee..0267a240880 100644 --- a/exist-ant/pom.xml +++ b/exist-ant/pom.xml @@ -50,12 +50,14 @@ org.exist-db exist-core ${project.version} - - - junit - junit - - + + + + ${project.groupId} + exist-core + ${project.version} + test-jar + test org.exist-db @@ -77,35 +79,7 @@ test - - junit - junit - test - - - - - org.hamcrest - hamcrest-core - - - - - org.apache.ant - ant-testutil - ${ant.version} - test - - - junit - junit - - - org.hamcrest @@ -156,7 +130,6 @@ true - junit:junit diff --git a/exist-ant/src/test/java/org/exist/ant/AntBuildFileExtension.java b/exist-ant/src/test/java/org/exist/ant/AntBuildFileExtension.java index 3667b2b62ce..17b11b6b259 100644 --- a/exist-ant/src/test/java/org/exist/ant/AntBuildFileExtension.java +++ b/exist-ant/src/test/java/org/exist/ant/AntBuildFileExtension.java @@ -21,26 +21,43 @@ */ package org.exist.ant; -import org.apache.tools.ant.BuildFileRule; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.ProjectHelper; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; +import java.io.File; + /** - * JUnit 5 {@link org.junit.jupiter.api.extension.Extension} adapter for Ant's - * {@link BuildFileRule}, which ships only as a JUnit 4 {@code TestRule} with no - * upstream JUnit 5 equivalent. - * - *

{@code BuildFileRule} only overrides {@code after()} (its {@code before()} is - * the inherited no-op from {@link org.junit.rules.ExternalResource}; setup instead - * happens explicitly via {@link #configureProject(String)}), so exposing that one - * protected lifecycle hook as {@link AfterEachCallback} is sufficient to use it as - * an instance-level {@code @RegisterExtension} field without needing the JUnit 4 - * migration-support shim.

+ * Loads an Ant build file for a test and runs its targets. + *

+ * Ant's own test helper, {@code BuildFileRule}, only exists as a JUnit 4 rule, so this does + * the part of it that the tests need: configure a {@link Project} from a build file, execute + * targets, and afterwards run the build file's {@code tearDown} target if it has one. */ -public class AntBuildFileExtension extends BuildFileRule implements AfterEachCallback { +public class AntBuildFileExtension implements AfterEachCallback { + private Project project; + + public void configureProject(final String buildFile) { + final File antFile = new File(buildFile); + project = new Project(); + project.init(); + project.setUserProperty("ant.file", antFile.getAbsolutePath()); + ProjectHelper.configureProject(project, antFile); + } + + public void executeTarget(final String target) { + project.executeTarget(target); + } + + public Project getProject() { + return project; + } @Override public void afterEach(final ExtensionContext context) { - after(); + if (project != null && project.getTargets().containsKey("tearDown")) { + project.executeTarget("tearDown"); + } } } diff --git a/exist-core-jmh/pom.xml b/exist-core-jmh/pom.xml index d0667da73b0..d91fd48be87 100644 --- a/exist-core-jmh/pom.xml +++ b/exist-core-jmh/pom.xml @@ -56,6 +56,13 @@ exist-core ${project.version} + + + ${project.groupId} + exist-core + ${project.version} + test-jar + ${project.groupId} exist-index-lucene @@ -82,9 +89,10 @@ provided + org.junit.jupiter junit-jupiter-api - test @@ -189,7 +197,13 @@ true org.openjdk.jmh:jmh-generator-annprocess:jar:${jmh.version} + + org.junit.jupiter:junit-jupiter-api:jar:${junit.jupiter.version} + + + org.junit.jupiter:junit-jupiter-api:jar:${junit.jupiter.version} + diff --git a/exist-core/pom.xml b/exist-core/pom.xml index 33b060231ac..4c896c8598a 100644 --- a/exist-core/pom.xml +++ b/exist-core/pom.xml @@ -331,7 +331,7 @@ xml-apis xml-apis - + junit junit @@ -519,19 +519,10 @@ jgrapht-opt 1.5.3 - - junit - junit - - org.hamcrest hamcrest - + test @@ -555,20 +546,33 @@ org.junit.jupiter junit-jupiter-api - + test org.opentest4j opentest4j - + test org.junit.jupiter junit-jupiter-params test + + org.junit.platform + junit-platform-engine + test + + + org.junit.platform + junit-platform-commons + test + + + org.junit.platform + junit-platform-testkit + test + org.assertj assertj-core @@ -587,6 +591,7 @@ org.xmlunit xmlunit-core + test org.xmlunit @@ -597,12 +602,14 @@ org.xmlunit xmlunit-legacy test - - - junit - junit - - + + + junit + junit + test + @@ -705,6 +712,30 @@ + + org.apache.maven.plugins + maven-enforcer-plugin + + + ban-junit4 + + + + + + junit:junit:*:*:compile + junit:junit:*:*:provided + org.junit.vintage + com.googlecode.junit-toolbox + + true + JUnit 4 must not be used outside the tests that need it for xmlunit-legacy; write tests with JUnit Jupiter. + + + + + + com.mycila license-maven-plugin @@ -784,7 +815,7 @@ src/test/java/org/exist/storage/txn/ReusableTxnTest.java src/test/java/org/exist/storage/txn/TransactionManagerTestHelper.java src/test/java/org/exist/storage/txn/TxnTest.java - src/main/java/org/exist/test/DiffMatcher.java + src/test/java/org/exist/test/DiffMatcher.java src/test/java/org/exist/test/Util.java src/main/java/org/exist/util/CollectionOfArrayIterator.java src/test/java/org/exist/util/CollectionOfArrayIteratorTest.java @@ -951,7 +982,7 @@ The original license statement is also included below.]]> src/test/java/org/exist/storage/txn/ReusableTxnTest.java src/test/java/org/exist/storage/txn/TransactionManagerTestHelper.java src/test/java/org/exist/storage/txn/TxnTest.java - src/main/java/org/exist/test/DiffMatcher.java + src/test/java/org/exist/test/DiffMatcher.java src/test/java/org/exist/test/Util.java src/main/java/org/exist/util/CollectionOfArrayIterator.java src/test/java/org/exist/util/CollectionOfArrayIteratorTest.java @@ -1037,6 +1068,8 @@ The BaseX Team. The original license statement is also included below.]]> true + + junit:junit org.apache.logging.log4j:log4j-core:jar:${log4j.version} org.apache.logging.log4j:log4j-jcl:jar:${log4j.version} org.apache.logging.log4j:log4j-slf4j2-impl:jar:${log4j.version} @@ -1352,11 +1385,6 @@ The BaseX Team. The original license statement is also included below.]]>junit-jupiter-engine ${junit.jupiter.version} - - org.junit.vintage - junit-vintage-engine - ${junit.jupiter.version} - org.objenesis objenesis diff --git a/exist-core/src/main/java/org/exist/jetty/JettyStart.java b/exist-core/src/main/java/org/exist/jetty/JettyStart.java index abbd1951d11..779fe39559f 100644 --- a/exist-core/src/main/java/org/exist/jetty/JettyStart.java +++ b/exist-core/src/main/java/org/exist/jetty/JettyStart.java @@ -1057,7 +1057,7 @@ public synchronized boolean isWebAppStartedSuccessfully() { /** * When {@link #isWebAppStartedSuccessfully()} is {@code false}, holds the last startup failure - * message for test diagnostics (surfaced by {@link org.exist.test.ExistWebServer} in thrown + * message for test diagnostics (surfaced by {@code org.exist.test.ExistWebServer} in thrown * {@link IllegalStateException}s). */ public synchronized Optional getWebAppStartupFailureDetail() { diff --git a/exist-core/src/main/java/org/exist/test/runner/XSuite.java b/exist-core/src/main/java/org/exist/test/runner/XSuite.java deleted file mode 100644 index 46f1145bd1c..00000000000 --- a/exist-core/src/main/java/org/exist/test/runner/XSuite.java +++ /dev/null @@ -1,667 +0,0 @@ -/* - * eXist-db Open Source Native XML Database - * Copyright (C) 2001 The eXist-db Authors - * - * info@exist-db.org - * http://www.exist-db.org - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.exist.test.runner; - -import org.exist.storage.BrokerPool; -import org.exist.test.ExistEmbeddedServer; -import org.exist.util.XMLFilenameFilter; -import org.exist.util.XQueryFilenameFilter; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.runner.Description; -import org.junit.runner.Runner; -import org.junit.runner.notification.Failure; -import org.junit.runner.notification.RunListener; -import org.junit.runner.notification.RunNotifier; -import org.junit.runners.ParentRunner; -import org.junit.runners.model.*; - -import javax.annotation.Nullable; -import java.io.IOException; -import java.lang.annotation.*; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -/** - * Using XSuite as a runner allows you to manually - * build a suite containing tests from both: - *

- * 1. XQSuite - as defined in $EXIST_HOME/src/org/exist/xquery/lib/xqsuite/xqsuite.xql - * 2. XML Test - as defined in $EXIST_HOME/src/org/exist/xquery/lib/test.xq - *

- * To use it, annotate a class - * with @RunWith(XSuite.class) and @XSuiteClasses({"extensions/my-extension/src/test/xquery", ...}). - * When you run this class, it will run all the tests in all the suite classes. - * - * @author Adam Retter - */ -public class XSuite extends ParentRunner { - - private final List runners; - @Nullable - private final ParallelFileScheduler parallelScheduler; - - /** - * Returns an empty suite. - * - * @return a runner for an empty suite. - */ - public static Runner emptySuite() { - try { - return new XSuite((Class) null, new String[0]); - } catch (final InitializationError e) { - throw new RuntimeException("This shouldn't be possible"); - } - } - - /** - * The XSuiteFiles annotation specifies the directory/file containing the tests to be run when a class - * annotated with @RunWith(XSuite.class) is run. - */ - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.TYPE) - @Inherited - public @interface XSuiteFiles { - /** - * @return the classes to be run - */ - String[] value(); - } - - /** - * The XSuiteParallel annotation specifies that the tests will be run in parallel when a class - * annotated with @RunWith(XSuite.class) is run. - */ - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.TYPE) - @Inherited - public @interface XSuiteParallel { - } - - private static String[] getAnnotatedDirectories(final Class klass) throws InitializationError { - final XSuite.XSuiteFiles annotation = klass.getAnnotation(XSuite.XSuiteFiles.class); - if (annotation == null) { - throw new InitializationError("class '%s' must have a XSuiteFiles annotation".formatted(klass.getName())); - } - return annotation.value(); - } - - private static boolean hasParallelAnnotation(@Nullable final Class klass) { - if (klass == null) { - return false; - } - final XSuite.XSuiteParallel annotation = klass.getAnnotation(XSuite.XSuiteParallel.class); - return annotation != null; - } - - /** - * Called reflectively on classes annotated with @RunWith(XSuite.class) - * - * @param klass the root class - * @param builder builds runners for classes in the suite - * - * @throws InitializationError if the XSuite cannot be constructed - */ - public XSuite(final Class klass, final RunnerBuilder builder) throws InitializationError { - this(builder, klass, getAnnotatedDirectories(klass)); - } - - /** - * Call this when there is no single root class (for example, multiple class names - * passed on the command line to {@link org.junit.runner.JUnitCore} - * - * @param builder builds runners for classes in the suite - * @param suites the directories/files in the suite - * - * @throws InitializationError if the XSuite cannot be constructed - */ - public XSuite(final RunnerBuilder builder, final String[] suites) throws InitializationError { - this(builder, null, suites); - } - - /** - * Call this when the default builder is good enough. Left in for compatibility with JUnit 4.4. - * - * @param klass the root of the suite - * @param suites the directories/files in the suite - * - * @throws InitializationError if the XSuite cannot be constructed - */ - protected XSuite(final Class klass, final String[] suites) throws InitializationError { - this(null, klass, suites); - } - - /** - * Called by this class and subclasses once the classes making up the suite have been determined. - * - * @param builder builds runners for classes in the suite - * @param klass the root of the suite - * @param suites the directories/files in the suite - * - * @throws InitializationError if the XSuite cannot be constructed - */ - protected XSuite(final RunnerBuilder builder, final Class klass, final String[] suites) throws InitializationError { - this(klass, getRunners(suites, hasParallelAnnotation(klass))); - } - - /** - * Called by this class and subclasses once the runners making up the suite have been determined. - * - * @param klass root of the suite - * @param runners for each class in the suite, a {@link Runner} - * - * @throws InitializationError if the XSuite cannot be constructed - */ - protected XSuite(final Class klass, final List runners) throws InitializationError { - super(klass); - this.runners = Collections.unmodifiableList(runners); - if (hasParallelAnnotation(klass) && !runners.isEmpty()) { - final ParallelFileScheduler scheduler = new ParallelFileScheduler(); - setScheduler(scheduler); - this.parallelScheduler = scheduler; - } else { - this.parallelScheduler = null; - } - } - - /** - * Returns true if any of the suite paths is an XQuery file or a directory containing at least one XQuery file. - * Used to decide whether to start the DB before getRunners (so discovery can run for XQueryTestRunner). - */ - private static boolean hasAnyXQueryFiles(final String[] suites) throws IOException { - if (suites == null) { - return false; - } - final java.util.function.Predicate isXQueryFile = XQueryFilenameFilter.asPredicate(); - for (final String suite : suites) { - final Path path = Path.of(suite); - if (!Files.exists(path)) { - continue; - } - if (Files.isDirectory(path)) { - try (final Stream children = Files.list(path)) { - final boolean hasXq = children.anyMatch(p -> !Files.isDirectory(p) && isXQueryFile.test(p) && !"runTests.xql".equals(p.getFileName().toString())); - if (hasXq) { - return true; - } - } - } else if (isXQueryFile.test(path) && !"runTests.xql".equals(path.getFileName().toString())) { - return true; - } - } - return false; - } - - /** - * Starts the embedded DB if not already started. Idempotent. - * Called before getRunners when the suite contains XQuery files so discovery can use the DB. - */ - private static void ensureExistDbStarted() throws Throwable { - if (EXIST_EMBEDDED_SERVER_CLASS_INSTANCE == null) { - EXIST_EMBEDDED_SERVER_CLASS_INSTANCE = newExistDbServer(); - EXIST_EMBEDDED_SERVER_CLASS_INSTANCE.startDb(); - } - } - - /** - * Get the runners for the suiteDirectories. - * - * @param suites/files the directories in the suite - * @param parallel should a runner execute tests in parallel - * - * @throws InitializationError if the runners cannot be retrieved - */ - private static List getRunners(final String[] suites, final boolean parallel) throws InitializationError { - if(suites == null) { - return Collections.emptyList(); - } - - try { - if (hasAnyXQueryFiles(suites)) { - try { - ensureExistDbStarted(); - } catch (final Throwable t) { - throw new InitializationError(t); - } - } - - final List runners = new ArrayList<>(); - for (final String suite : suites) { - - // if directory/file does not exist - throw an exception - final Path path = Path.of(suite); - if (!Files.exists(path)) { - throw new InitializationError("XSuite does not exist: " + suite + ". path=" + path.toAbsolutePath()); - } - - if (Files.isDirectory(path)) { - // directory of files of test(s) - try (final Stream children = Files.list(path)) { - final List sorted = children.collect(Collectors.toList()); - sorted.sort(Comparator.comparing(Path::toString)); - for (final Path child : sorted) { - if (!Files.isDirectory(child)) { - final Runner runner = getRunner(child, parallel); - if (runner != null) { - runners.add(runner); - } - } - } - } - } else { - // just a file of test(s) - runners.add(getRunner(path, parallel)); - } - } - - return runners; - } catch (final IOException e) { - throw new InitializationError(e); - } - } - - private static @Nullable Runner getRunner(final Path path, final boolean parallel) throws InitializationError { - if(XMLFilenameFilter.asPredicate().test(path)) { - return new XMLTestRunner(path, parallel); - } else if(XQueryFilenameFilter.asPredicate().test(path) && !"runTests.xql".equals(path.getFileName().toString())) { - return new XQueryTestRunner(path, parallel); - } else { - return null; - } - } - - @Override - protected List getChildren() { - return runners; - } - - @Override - protected Description describeChild(final Runner child) { - return child.getDescription(); - } - - @Override - protected void runChild(final Runner runner, final RunNotifier notifier) { - if (parallelScheduler != null) { - final ParallelFileScheduler parallel = parallelScheduler; - parallel.prepareForRun(notifier); - final Description runnerDesc = runner.getDescription(); - parallel.setCurrentRunner(runnerDesc); - parallel.runnerStarted(runnerDesc, runner); - try { - runner.run(notifier); - } finally { - parallel.runnerFinished(runnerDesc); - parallel.setCurrentRunner(null); - } - } else { - runner.run(notifier); - } - } - - @Override - protected Statement withBeforeClasses(final Statement statement) { - // get @BeforeClass methods - final List befores = getTestClass().getAnnotatedMethods(BeforeClass.class); - - // inject an eXist-db Server startup as though it were an @BeforeClass - final Statement startExistDb = new StartExistDbStatement(); - - return new RunXSuiteBefores(statement, startExistDb, befores, null); - } - - @Override - protected Statement withAfterClasses(final Statement statement) { - // get @AfterClass methods - final List afters = getTestClass().getAnnotatedMethods(AfterClass.class); - - // inject an eXist-db Server shutdown as though it were an @AfterClass - final Statement stopExist = new StopExistDbStatement(); - - return new RunXSuiteAfters(statement, stopExist, afters, null); - } - - private static class RunXSuiteBefores extends Statement { - private final Statement next; - private final Object target; - private final Statement beforeXSuite; - private final List befores; - - public RunXSuiteBefores(final Statement next, final Statement beforeXSuite, final List befores, final Object target) { - this.next = next; - this.beforeXSuite = beforeXSuite; - this.befores = befores; - this.target = target; - } - - @Override - public void evaluate() throws Throwable { - beforeXSuite.evaluate(); - for (final FrameworkMethod before : befores) { - before.invokeExplosively(target); - } - next.evaluate(); - } - } - - private static class RunXSuiteAfters extends Statement { - private final Statement next; - private final Object target; - private final Statement afterXSuite; - private final List afters; - - public RunXSuiteAfters(final Statement next, final Statement afterXSuite, final List afters, final Object target) { - this.next = next; - this.afterXSuite = afterXSuite; - this.afters = afters; - this.target = target; - } - - @Override - public void evaluate() throws Throwable { - final List errors = new ArrayList<>(); - try { - next.evaluate(); - } catch (final Throwable e) { - errors.add(e); - } finally { - try { - afterXSuite.evaluate(); - } catch (final Throwable e) { - errors.add(e); - } - for (final FrameworkMethod after : afters) { - try { - after.invokeExplosively(target); - } catch (final Throwable e) { - errors.add(e); - } - } - } - MultipleFailureException.assertEmpty(errors); - } - } - - private static class StartExistDbStatement extends Statement { - @Override - public void evaluate() throws Throwable { - if (EXIST_EMBEDDED_SERVER_CLASS_INSTANCE != null) { - return; - } - EXIST_EMBEDDED_SERVER_CLASS_INSTANCE = newExistDbServer(); - EXIST_EMBEDDED_SERVER_CLASS_INSTANCE.startDb(); - } - } - - private static class StopExistDbStatement extends Statement { - @Override - public void evaluate() { - if (EXIST_EMBEDDED_SERVER_CLASS_INSTANCE == null) { - throw new IllegalStateException("EXIST_EMBEDDED_SERVER_CLASS_INSTANCE already stopped"); - } - EXIST_EMBEDDED_SERVER_CLASS_INSTANCE.stopDb(); - EXIST_EMBEDDED_SERVER_CLASS_INSTANCE = null; - } - } - - static ExistEmbeddedServer EXIST_EMBEDDED_SERVER_CLASS_INSTANCE = null; - - static ExistEmbeddedServer newExistDbServer() { - return new ExistEmbeddedServer(true, true); - } - - /** - * Runs child runners (test files) in parallel using a fixed thread pool. - * Pool size is dynamic (processors and eXist broker count) with optional override. - * Progress-based hang detection reports which file hung when no activity is seen. - * All children share the same single eXist instance; tests must not rely on execution order. - */ - private static final class ParallelFileScheduler implements RunnerScheduler { - - private static final String PARALLEL_THREADS_PROPERTY = "xsuite.parallel.threads"; - private static final String HANG_THRESHOLD_PROPERTY = "xsuite.hang.threshold.minutes"; - private static final String HANG_WATCHER_INTERVAL_PROPERTY = "xsuite.hang.watcher.interval.seconds"; - private static final int POOL_FLOOR = 2; - private static final int POOL_CAP = 32; - private static final double DEFAULT_HANG_THRESHOLD_MINUTES = 5.0; - private static final int DEFAULT_WATCHER_INTERVAL_SECONDS = 30; - - private volatile ExecutorService executor; - private final Object executorLock = new Object(); - - private final ConcurrentHashMap lastActivityByRunner = new ConcurrentHashMap<>(); - private final ConcurrentHashMap runnerLabelByDescription = new ConcurrentHashMap<>(); - private final ThreadLocal currentRunnerId = new ThreadLocal<>(); - private volatile RunNotifier notifier; - private final AtomicBoolean notifierListenerAdded = new AtomicBoolean(false); - private final AtomicBoolean hungReported = new AtomicBoolean(false); - private volatile Thread watcherThread; - - private ExecutorService getOrCreateExecutor() { - if (executor != null) { - return executor; - } - synchronized (executorLock) { - if (executor != null) { - return executor; - } - final int size = computePoolSize(); - executor = Executors.newFixedThreadPool(size); - startWatcher(); - return executor; - } - } - - private static int computePoolSize() { - final String override = System.getProperty(PARALLEL_THREADS_PROPERTY); - if (override != null && !override.isEmpty()) { - try { - return Math.max(POOL_FLOOR, Math.min(POOL_CAP, Integer.parseInt(override.trim()))); - } catch (final NumberFormatException ignored) { - // fall through to dynamic - } - } - final int processors = Runtime.getRuntime().availableProcessors(); - int brokerMax = processors; - if (EXIST_EMBEDDED_SERVER_CLASS_INSTANCE != null) { - try { - final BrokerPool pool = EXIST_EMBEDDED_SERVER_CLASS_INSTANCE.getBrokerPool(); - if (pool != null) { - brokerMax = pool.getMax(); - } - } catch (final Exception ignored) { - // use processors only - } - } - return Math.max(POOL_FLOOR, Math.min(POOL_CAP, Math.min(processors, brokerMax))); - } - - void prepareForRun(final RunNotifier notifier) { - this.notifier = notifier; - if (notifierListenerAdded.compareAndSet(false, true)) { - notifier.addListener(new RunListener() { - @Override - public void testStarted(final Description description) { - recordActivity(); - } - - @Override - public void testFinished(final Description description) { - recordActivity(); - } - - @Override - public void testFailure(final Failure failure) { - recordActivity(); - } - - @Override - public void testAssumptionFailure(final Failure failure) { - recordActivity(); - } - - @Override - public void testIgnored(final Description description) { - recordActivity(); - } - - private void recordActivity() { - final Description runnerDesc = currentRunnerId.get(); - if (runnerDesc != null) { - lastActivityByRunner.put(runnerDesc, System.currentTimeMillis()); - } - } - }); - } - } - - void setCurrentRunner(final Description description) { - if (description == null) { - currentRunnerId.remove(); - } else { - currentRunnerId.set(description); - } - } - - void runnerStarted(final Description runnerDesc, final Runner runner) { - lastActivityByRunner.put(runnerDesc, System.currentTimeMillis()); - if (runner instanceof AbstractTestRunner testRunner) { - runnerLabelByDescription.put(runnerDesc, testRunner.getSourcePath().toAbsolutePath().toString()); - } else { - runnerLabelByDescription.put(runnerDesc, runnerDesc.getDisplayName()); - } - } - - void runnerFinished(final Description runnerDesc) { - lastActivityByRunner.remove(runnerDesc); - runnerLabelByDescription.remove(runnerDesc); - } - - private void startWatcher() { - watcherThread = new Thread(this::runWatcher, "xsuite-hang-watcher"); - watcherThread.setDaemon(false); - watcherThread.start(); - } - - private static double hangThresholdMinutes() { - final String v = System.getProperty(HANG_THRESHOLD_PROPERTY); - if (v == null || v.isEmpty()) { - return DEFAULT_HANG_THRESHOLD_MINUTES; - } - try { - return Math.max(0.001, Double.parseDouble(v.trim())); - } catch (final NumberFormatException e) { - return DEFAULT_HANG_THRESHOLD_MINUTES; - } - } - - private static int watcherIntervalSeconds() { - final String v = System.getProperty(HANG_WATCHER_INTERVAL_PROPERTY); - if (v == null || v.isEmpty()) { - return DEFAULT_WATCHER_INTERVAL_SECONDS; - } - try { - return Math.max(1, Integer.parseInt(v.trim())); - } catch (final NumberFormatException e) { - return DEFAULT_WATCHER_INTERVAL_SECONDS; - } - } - - private void runWatcher() { - final long thresholdMs = (long) (hangThresholdMinutes() * 60 * 1000); - final long intervalMs = watcherIntervalSeconds() * 1000L; - while (!Thread.currentThread().isInterrupted() && executor != null && !executor.isShutdown()) { - try { - Thread.sleep(intervalMs); - } catch (final InterruptedException e) { - Thread.currentThread().interrupt(); - break; - } - if (hungReported.get()) { - break; - } - final long now = System.currentTimeMillis(); - for (final var e : lastActivityByRunner.entrySet()) { - if (now - e.getValue() > thresholdMs) { - if (hungReported.compareAndSet(false, true)) { - reportHung(e.getKey()); - final ExecutorService exec = executor; - if (exec != null) { - exec.shutdownNow(); - } - } - break; - } - } - } - } - - private void reportHung(final Description runnerDesc) { - final RunNotifier n = notifier; - if (n == null) { - return; - } - final String label = runnerLabelByDescription.getOrDefault(runnerDesc, runnerDesc.getDisplayName()); - final AssertionError err = new AssertionError( - "Test file appears hung (no activity for " + hangThresholdMinutes() + " minutes): " + label); - n.fireTestFailure(new Failure(runnerDesc, err)); - } - - @Override - public void schedule(final Runnable childStatement) { - getOrCreateExecutor().submit(childStatement); - } - - @Override - public void finished() { - final ExecutorService exec = executor; - if (exec == null) { - return; - } - exec.shutdown(); - try { - if (hungReported.get()) { - exec.awaitTermination(1, TimeUnit.MINUTES); - } else { - exec.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS); - } - } catch (final InterruptedException e) { - exec.shutdownNow(); - Thread.currentThread().interrupt(); - } - if (watcherThread != null) { - watcherThread.interrupt(); - } - } - } -} diff --git a/exist-core/src/main/java/org/exist/xquery/XQueryContext.java b/exist-core/src/main/java/org/exist/xquery/XQueryContext.java index 66bfe297372..72ee3c63a5d 100644 --- a/exist-core/src/main/java/org/exist/xquery/XQueryContext.java +++ b/exist-core/src/main/java/org/exist/xquery/XQueryContext.java @@ -444,7 +444,7 @@ public class XQueryContext implements BinaryValueManager, Context { private final Map staticDecimalFormats = hashMap(Tuple(UNNAMED_DECIMAL_FORMAT, DecimalFormat.UNNAMED)); - // Only used for testing, e.g. {@link org.exist.test.runner.XQueryTestRunner}. + // Only used for testing, e.g. {@code org.exist.test.runner.XQueryTestRunner}. private Optional testRepository = Optional.empty(); /** @@ -562,7 +562,7 @@ public void setHttpContext(@Nullable final HttpContext httpContext) { /** * Set the EXPath repository used for testing, - * only should be called from {@link org.exist.test.runner.XQueryTestRunner}. + * only should be called from {@code org.exist.test.runner.XQueryTestRunner}. * * @param testRepository the EXPath repository to use for test execution. */ diff --git a/exist-core/src/test/java/org/exist/backup/SystemExportFiltersTest.java b/exist-core/src/test/java/org/exist/backup/SystemExportFiltersTest.java index 61bfd0da7be..94d3df758b5 100644 --- a/exist-core/src/test/java/org/exist/backup/SystemExportFiltersTest.java +++ b/exist-core/src/test/java/org/exist/backup/SystemExportFiltersTest.java @@ -60,8 +60,9 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static org.exist.test.TestConstants.TEST_COLLECTION_URI; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; + /** * @author Dmitriy Shabanov @@ -79,7 +80,7 @@ public class SystemExportFiltersTest { private static final XmldbURI doc02uri = TEST_COLLECTION_URI.append("test2.xml"); private static final XmldbURI doc03uri = TEST_COLLECTION_URI.append("test3.xml"); private static final XmldbURI doc11uri = TEST_COLLECTION_URI.append("test.binary"); - + private static final String XML1 = ""; private static final String XML1_BACKUP = "test"; private static final String XML2 = @@ -172,7 +173,7 @@ public void exportImport() throws Exception { assertEquals(XML3_PROPER, serializer(broker, doc)); doc = getDoc(broker, test, doc11uri.lastSegment()); - assertTrue(doc instanceof BinaryDocument); + assertInstanceOf(BinaryDocument.class, doc); try (final InputStream is = broker.getBinaryResource(transaction, ((BinaryDocument)doc))) { assertEquals(BINARY, InputStreamUtil.readString(is, UTF_8)); } diff --git a/exist-core/src/test/java/org/exist/backup/SystemExportImportTest.java b/exist-core/src/test/java/org/exist/backup/SystemExportImportTest.java index 87e545eeb4b..f235349c55b 100644 --- a/exist-core/src/test/java/org/exist/backup/SystemExportImportTest.java +++ b/exist-core/src/test/java/org/exist/backup/SystemExportImportTest.java @@ -54,8 +54,8 @@ import org.exist.storage.txn.Txn; import static org.exist.test.TestConstants.TEST_COLLECTION_URI; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; import org.exist.test.ExistEmbeddedServer; import org.exist.util.LockException; @@ -98,14 +98,14 @@ public class SystemExportImportTest { private static XmldbURI doc03uri = TEST_COLLECTION_URI.append("test3.xml"); private static XmldbURI doc04uri = TEST_COLLECTION_URI.append("test4.xml"); private static XmldbURI doc11uri = TEST_COLLECTION_URI.append("test.binary"); - + private static String XML1 = ""; - private static String XML2 = + private static String XML2 = """ """; - private static String XML2_PROPER = + private static String XML2_PROPER = """ @@ -177,7 +177,7 @@ public void exportImport(String apiName, boolean direct, boolean zip) throws EXi assertEquals(XML4, serializer(broker, doc, withXmlDeclOutputProps)); doc = getDoc(broker, test, doc11uri.lastSegment()); - assertTrue(doc instanceof BinaryDocument); + assertInstanceOf(BinaryDocument.class, doc); try (final InputStream is = broker.getBinaryResource(transaction, ((BinaryDocument)doc))) { assertEquals(BINARY, InputStreamUtil.readString(is, UTF_8)); } @@ -189,7 +189,7 @@ public void exportImport(String apiName, boolean direct, boolean zip) throws EXi private DocumentImpl getDoc(final DBBroker broker, final Collection col, final XmldbURI uri) throws PermissionDeniedException { final DocumentImpl doc = col.getDocument(broker, uri); assertNotNull(doc); - + return doc; } @@ -199,7 +199,7 @@ private DocumentImpl getDoc(final DBBroker broker, final Collection col, final X contentsOutputProps.setProperty( OutputKeys.INDENT, "yes" ); contentsOutputProps.setProperty( EXistOutputKeys.OUTPUT_DOCTYPE, "yes" ); } - + private String serializer(final DBBroker broker, final DocumentImpl document) throws SAXException { return serializer(broker, document, contentsOutputProps); } diff --git a/exist-core/src/test/java/org/exist/backup/XMLDBRestoreTest.java b/exist-core/src/test/java/org/exist/backup/XMLDBRestoreTest.java index 2f39b353479..afcdd3f2d6f 100644 --- a/exist-core/src/test/java/org/exist/backup/XMLDBRestoreTest.java +++ b/exist-core/src/test/java/org/exist/backup/XMLDBRestoreTest.java @@ -59,8 +59,8 @@ import static org.exist.util.FileUtils.withUnixSep; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; public class XMLDBRestoreTest { @RegisterExtension @@ -316,9 +316,9 @@ private void checkMediaType(final XmldbURI collectionUri, final DocInfo backupDo final Collection collection = DatabaseManager.getCollection(XmldbURI.create(getBaseUri()).append(collectionUri).toString(), TestUtils.ADMIN_DB_USER, TestUtils.ADMIN_DB_PWD); final Resource resource = collection.getResource(backupDocInfo.name); if (backupDocInfo.type == MimeType.XML) { - assertTrue(resource instanceof XMLResource); + assertInstanceOf(XMLResource.class, resource); } else { - assertTrue(resource instanceof BinaryResource); + assertInstanceOf(BinaryResource.class, resource); } if (backupDocInfo.mediaType != null) { assertEquals(backupDocInfo.mediaType, ((EXistResource) resource).getMimeType()); diff --git a/exist-core/src/test/java/org/exist/collections/CollectionStoreTest.java b/exist-core/src/test/java/org/exist/collections/CollectionStoreTest.java index c7609c33c8c..fa684c1730c 100644 --- a/exist-core/src/test/java/org/exist/collections/CollectionStoreTest.java +++ b/exist-core/src/test/java/org/exist/collections/CollectionStoreTest.java @@ -56,8 +56,8 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; public class CollectionStoreTest { @RegisterExtension @@ -133,7 +133,7 @@ private void storeBinary(final PreserveType preserveOnCopy) throws EXistExceptio col.close(); if (lockedDoc != null) { - assertTrue(lockedDoc.getDocument() instanceof BinaryDocument); + assertInstanceOf(BinaryDocument.class, lockedDoc.getDocument()); final BinaryDocument doc = (BinaryDocument)lockedDoc.getDocument(); final Try docContent = broker.withBinaryFile(transaction, doc, is -> diff --git a/exist-core/src/test/java/org/exist/dom/memtree/DocumentImplTest.java b/exist-core/src/test/java/org/exist/dom/memtree/DocumentImplTest.java index 4cfcbd136ef..a49ea6e7d24 100644 --- a/exist-core/src/test/java/org/exist/dom/memtree/DocumentImplTest.java +++ b/exist-core/src/test/java/org/exist/dom/memtree/DocumentImplTest.java @@ -38,8 +38,9 @@ import java.io.InputStream; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import static java.nio.charset.StandardCharsets.UTF_8; /** @@ -69,7 +70,7 @@ public void checkNamespaces_xerces() throws IOException, ParserConfigurationExce final Attr attr1 = (Attr)attrs.item(index++); assertEquals(Node.ATTRIBUTE_NODE, attr1.getNodeType()); - assertTrue(attr1 instanceof AttrNSImpl); + assertInstanceOf(AttrNSImpl.class, attr1); assertEquals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, attr1.getNamespaceURI()); assertNull(attr1.getPrefix()); assertEquals(XMLConstants.XMLNS_ATTRIBUTE, attr1.getLocalName()); @@ -78,7 +79,7 @@ public void checkNamespaces_xerces() throws IOException, ParserConfigurationExce final Attr attr2 = (Attr)attrs.item(index++); assertEquals(Node.ATTRIBUTE_NODE, attr2.getNodeType()); - assertTrue(attr2 instanceof AttrNSImpl); + assertInstanceOf(AttrNSImpl.class, attr2); assertEquals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, attr2.getNamespaceURI()); assertEquals(XMLConstants.XMLNS_ATTRIBUTE, attr2.getPrefix()); assertEquals("repo", attr2.getLocalName()); @@ -134,7 +135,7 @@ public void checkNamespaces_exist() throws IOException, SAXException, ParserConf final Attr attr1 = (Attr)attrs.item(index++); assertEquals(NodeImpl.NAMESPACE_NODE, attr1.getNodeType()); - assertTrue(attr1 instanceof NamespaceNode); + assertInstanceOf(NamespaceNode.class, attr1); assertEquals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, attr1.getNamespaceURI()); assertNull(attr1.getPrefix()); assertEquals(XMLConstants.XMLNS_ATTRIBUTE, attr1.getLocalName()); diff --git a/exist-core/src/test/java/org/exist/dom/memtree/MemtreeTest.java b/exist-core/src/test/java/org/exist/dom/memtree/MemtreeTest.java index 247846def23..1b9282f7884 100644 --- a/exist-core/src/test/java/org/exist/dom/memtree/MemtreeTest.java +++ b/exist-core/src/test/java/org/exist/dom/memtree/MemtreeTest.java @@ -41,6 +41,7 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertTrue; /** @@ -388,33 +389,33 @@ public void getNode() throws IOException, ParserConfigurationException, SAXExcep doc = parse(is); } - assertTrue(doc.getNode(0) instanceof DocumentImpl); // the document node - assertTrue(doc.getNode(1) instanceof CommentImpl); // - assertTrue(doc.getNode(2) instanceof ProcessingInstructionImpl); // - assertTrue(doc.getNode(3) instanceof CommentImpl); // - assertTrue(doc.getNode(4) instanceof ProcessingInstructionImpl); // - assertTrue(doc.getNode(5) instanceof ElementImpl); // doc-element - assertTrue(doc.getNode(6) instanceof TextImpl); // doc-element/text()[1] - assertTrue(doc.getNode(7) instanceof CommentImpl); // - assertTrue(doc.getNode(8) instanceof TextImpl); // doc-element/text()[2] - assertTrue(doc.getNode(9) instanceof ElementImpl); // e1 - assertTrue(doc.getNode(10) instanceof TextImpl); // e1/text()[1] - assertTrue(doc.getNode(11) instanceof ProcessingInstructionImpl); // - assertTrue(doc.getNode(12) instanceof TextImpl); // e1/text()[2] - assertTrue(doc.getNode(13) instanceof ElementImpl); // e1_1 - assertTrue(doc.getNode(14) instanceof TextImpl); // e1_1/text()[1] - assertTrue(doc.getNode(15) instanceof TextImpl); // e1/text()[3] - assertTrue(doc.getNode(16) instanceof ElementImpl); // e1_2 - assertTrue(doc.getNode(17) instanceof TextImpl); // e1_2/text()[1] - assertTrue(doc.getNode(18) instanceof TextImpl); // e1/text()[4] - assertTrue(doc.getNode(19) instanceof TextImpl); // doc-element/text()[3] - assertTrue(doc.getNode(20) instanceof CommentImpl); // - assertTrue(doc.getNode(21) instanceof TextImpl); // doc-element/text()[4] - assertTrue(doc.getNode(22) instanceof ProcessingInstructionImpl); // - assertTrue(doc.getNode(23) instanceof CommentImpl); // - assertTrue(doc.getNode(24) instanceof ProcessingInstructionImpl); // - assertTrue(doc.getNode(25) instanceof CommentImpl); // - assertTrue(doc.getNode(26) instanceof ProcessingInstructionImpl); // + assertInstanceOf(DocumentImpl.class, doc.getNode(0)); // the document node + assertInstanceOf(CommentImpl.class, doc.getNode(1)); // + assertInstanceOf(ProcessingInstructionImpl.class, doc.getNode(2)); // + assertInstanceOf(CommentImpl.class, doc.getNode(3)); // + assertInstanceOf(ProcessingInstructionImpl.class, doc.getNode(4)); // + assertInstanceOf(ElementImpl.class, doc.getNode(5)); // doc-element + assertInstanceOf(TextImpl.class, doc.getNode(6)); // doc-element/text()[1] + assertInstanceOf(CommentImpl.class, doc.getNode(7)); // + assertInstanceOf(TextImpl.class, doc.getNode(8)); // doc-element/text()[2] + assertInstanceOf(ElementImpl.class, doc.getNode(9)); // e1 + assertInstanceOf(TextImpl.class, doc.getNode(10)); // e1/text()[1] + assertInstanceOf(ProcessingInstructionImpl.class, doc.getNode(11)); // + assertInstanceOf(TextImpl.class, doc.getNode(12)); // e1/text()[2] + assertInstanceOf(ElementImpl.class, doc.getNode(13)); // e1_1 + assertInstanceOf(TextImpl.class, doc.getNode(14)); // e1_1/text()[1] + assertInstanceOf(TextImpl.class, doc.getNode(15)); // e1/text()[3] + assertInstanceOf(ElementImpl.class, doc.getNode(16)); // e1_2 + assertInstanceOf(TextImpl.class, doc.getNode(17)); // e1_2/text()[1] + assertInstanceOf(TextImpl.class, doc.getNode(18)); // e1/text()[4] + assertInstanceOf(TextImpl.class, doc.getNode(19)); // doc-element/text()[3] + assertInstanceOf(CommentImpl.class, doc.getNode(20)); // + assertInstanceOf(TextImpl.class, doc.getNode(21)); // doc-element/text()[4] + assertInstanceOf(ProcessingInstructionImpl.class, doc.getNode(22)); // + assertInstanceOf(CommentImpl.class, doc.getNode(23)); // + assertInstanceOf(ProcessingInstructionImpl.class, doc.getNode(24)); // + assertInstanceOf(CommentImpl.class, doc.getNode(25)); // + assertInstanceOf(ProcessingInstructionImpl.class, doc.getNode(26)); // } @Test @@ -440,140 +441,140 @@ public void getChildNodes() throws IOException, ParserConfigurationException, SA // children of final Node commentBeforeDoc1 = docChildren.item(0); - assertTrue(commentBeforeDoc1 instanceof CommentImpl); + assertInstanceOf(CommentImpl.class, commentBeforeDoc1); assertEquals(0, commentBeforeDoc1.getChildNodes().getLength()); // children of final Node piBeforeDoc1 = docChildren.item(1); - assertTrue(piBeforeDoc1 instanceof ProcessingInstructionImpl); + assertInstanceOf(ProcessingInstructionImpl.class, piBeforeDoc1); assertEquals(0, piBeforeDoc1.getChildNodes().getLength()); // children of final Node commentBeforeDoc2 = docChildren.item(2); - assertTrue(commentBeforeDoc2 instanceof CommentImpl); + assertInstanceOf(CommentImpl.class, commentBeforeDoc2); assertEquals(0, commentBeforeDoc2.getChildNodes().getLength()); // children of final Node piBeforeDoc2 = docChildren.item(3); - assertTrue(piBeforeDoc2 instanceof ProcessingInstructionImpl); + assertInstanceOf(ProcessingInstructionImpl.class, piBeforeDoc2); assertEquals(0, piBeforeDoc2.getChildNodes().getLength()); // children of doc-element final Node docElement = docChildren.item(4); - assertTrue(docElement instanceof ElementImpl); + assertInstanceOf(ElementImpl.class, docElement); assertEquals("doc-element", docElement.getLocalName()); final NodeList docElementChildren = docElement.getChildNodes(); assertEquals(7, docElementChildren.getLength()); // children of doc-element/text()[1] final Node docElementText1 = docElementChildren.item(0); - assertTrue(docElementText1 instanceof TextImpl); + assertInstanceOf(TextImpl.class, docElementText1); assertEquals(0, docElementText1.getChildNodes().getLength()); // children of final Node commentBeforeE1 = docElementChildren.item(1); - assertTrue(commentBeforeE1 instanceof CommentImpl); + assertInstanceOf(CommentImpl.class, commentBeforeE1); assertEquals(0, commentBeforeE1.getChildNodes().getLength()); // children of doc-element/text()[2] final Node docElementText2 = docElementChildren.item(2); - assertTrue(docElementText2 instanceof TextImpl); + assertInstanceOf(TextImpl.class, docElementText2); assertEquals(0, docElementText2.getChildNodes().getLength()); // children of e1 final Node e1 = docElementChildren.item(3); - assertTrue(e1 instanceof ElementImpl); + assertInstanceOf(ElementImpl.class, e1); assertEquals("e1", e1.getLocalName()); final NodeList e1Children = e1.getChildNodes(); assertEquals(7, e1Children.getLength()); // children of e1/text()[1] final Node e1Text1 = e1Children.item(0); - assertTrue(e1Text1 instanceof TextImpl); + assertInstanceOf(TextImpl.class, e1Text1); assertEquals(0, e1Text1.getChildNodes().getLength()); // children of final Node piBeforeE11 = e1Children.item(1); - assertTrue(piBeforeE11 instanceof ProcessingInstructionImpl); + assertInstanceOf(ProcessingInstructionImpl.class, piBeforeE11); assertEquals(0, piBeforeE11.getChildNodes().getLength()); // children of e1/text()[2] final Node e1Text2 = e1Children.item(2); - assertTrue(e1Text2 instanceof TextImpl); + assertInstanceOf(TextImpl.class, e1Text2); assertEquals(0, e1Text2.getChildNodes().getLength()); // children of e1_1 final Node e11 = e1Children.item(3); - assertTrue(e11 instanceof ElementImpl); + assertInstanceOf(ElementImpl.class, e11); assertEquals("e1_1", e11.getLocalName()); final NodeList e11Children = e11.getChildNodes(); assertEquals(1, e11Children.getLength()); // children of e1_1/text()[1] final Node e11Text1 = e11Children.item(0); - assertTrue(e11Text1 instanceof TextImpl); + assertInstanceOf(TextImpl.class, e11Text1); assertEquals(0, e11Text1.getChildNodes().getLength()); // children of e1/text()[2] final Node e1Text3 = e1Children.item(4); - assertTrue(e1Text3 instanceof TextImpl); + assertInstanceOf(TextImpl.class, e1Text3); assertEquals(0, e1Text3.getChildNodes().getLength()); // children of e1_2 final Node e12 = e1Children.item(5); - assertTrue(e12 instanceof ElementImpl); + assertInstanceOf(ElementImpl.class, e12); assertEquals("e1_2", e12.getLocalName()); final NodeList e12Children = e12.getChildNodes(); assertEquals(1, e12Children.getLength()); // children of e1_2/text()[1] final Node e12Text1 = e12Children.item(0); - assertTrue(e12Text1 instanceof TextImpl); + assertInstanceOf(TextImpl.class, e12Text1); assertEquals(0, e12Text1.getChildNodes().getLength()); // children of e1/text()[4] final Node e1Text4 = e1Children.item(6); - assertTrue(e1Text4 instanceof TextImpl); + assertInstanceOf(TextImpl.class, e1Text4); assertEquals(0, e1Text4.getChildNodes().getLength()); // children of doc-element/text()[3] final Node docElementText3 = docElementChildren.item(4); - assertTrue(docElementText3 instanceof TextImpl); + assertInstanceOf(TextImpl.class, docElementText3); assertEquals(0, docElementText3.getChildNodes().getLength()); // children of final Node commentAfterE1 = docElementChildren.item(5); - assertTrue(commentAfterE1 instanceof CommentImpl); + assertInstanceOf(CommentImpl.class, commentAfterE1); assertEquals(0, commentAfterE1.getChildNodes().getLength()); // children of doc-element/text()[4] final Node docElementText4 = docElementChildren.item(6); - assertTrue(docElementText4 instanceof TextImpl); + assertInstanceOf(TextImpl.class, docElementText4); assertEquals(0, docElementText4.getChildNodes().getLength()); // children of final Node piAfterDoc1 = docChildren.item(5); - assertTrue(piAfterDoc1 instanceof ProcessingInstructionImpl); + assertInstanceOf(ProcessingInstructionImpl.class, piAfterDoc1); assertEquals(0, piAfterDoc1.getChildNodes().getLength()); // children of final Node commentAfterDoc1 = docChildren.item(6); - assertTrue(commentAfterDoc1 instanceof CommentImpl); + assertInstanceOf(CommentImpl.class, commentAfterDoc1); assertEquals(0, commentAfterDoc1.getChildNodes().getLength()); // children of final Node piAfterDoc2 = docChildren.item(7); - assertTrue(piAfterDoc2 instanceof ProcessingInstructionImpl); + assertInstanceOf(ProcessingInstructionImpl.class, piAfterDoc2); assertEquals(0, piAfterDoc2.getChildNodes().getLength()); // children of final Node commentAfterDoc2 = docChildren.item(8); - assertTrue(commentAfterDoc2 instanceof CommentImpl); + assertInstanceOf(CommentImpl.class, commentAfterDoc2); assertEquals(0, commentAfterDoc2.getChildNodes().getLength()); // children of final Node piAfterDoc3 = docChildren.item(9); - assertTrue(piAfterDoc3 instanceof ProcessingInstructionImpl); + assertInstanceOf(ProcessingInstructionImpl.class, piAfterDoc3); assertEquals(0, piAfterDoc3.getChildNodes().getLength()); } diff --git a/exist-core/src/test/java/org/exist/source/SourceFactoryTest.java b/exist-core/src/test/java/org/exist/source/SourceFactoryTest.java index 51b1c7d1d17..be3ddabe08f 100644 --- a/exist-core/src/test/java/org/exist/source/SourceFactoryTest.java +++ b/exist-core/src/test/java/org/exist/source/SourceFactoryTest.java @@ -38,9 +38,10 @@ import java.nio.file.Path; import static org.easymock.EasyMock.*; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNull; + @Execution(ExecutionMode.CONCURRENT) public class SourceFactoryTest { @@ -54,7 +55,7 @@ public void getSourceFromFile_contextAbsoluteFileUrl_locationAbsoluteUrl() throw final Source source = SourceFactory.getSource(null, contextPath, location, false); - assertTrue(source instanceof FileSource); + assertInstanceOf(FileSource.class, source); assertEquals(new java.io.File(libraryUrl.toURI()).getAbsolutePath(), source.path()); } @@ -67,7 +68,7 @@ public void getSourceFromFile_contextAbsoluteFile_locationAbsoluteFile() throws final Source source = SourceFactory.getSource(null, contextPath, location, false); - assertTrue(source instanceof FileSource); + assertInstanceOf(FileSource.class, source); assertEquals(new java.io.File(libraryUrl.toURI()).getAbsolutePath(), source.path()); } @@ -79,7 +80,7 @@ public void getSourceFromFile_contextAbsoluteFileUrl_locationRelative() throws I final Source source = SourceFactory.getSource(null, contextPath, location, false); - assertTrue(source instanceof FileSource); + assertInstanceOf(FileSource.class, source); assertEquals(new java.io.File(getClass().getResource("library.xqm").toURI()).getAbsolutePath(), source.path()); } @@ -91,7 +92,7 @@ public void getSourceFromFile_contextAbsoluteFile_locationRelative() throws IOEx final Source source = SourceFactory.getSource(null, contextPath, location, false); - assertTrue(source instanceof FileSource); + assertInstanceOf(FileSource.class, source); assertEquals(new java.io.File(getClass().getResource("library.xqm").toURI()).getAbsolutePath(), source.path()); } @@ -104,7 +105,7 @@ public void getSourceFromFile_contextAbsoluteDir_locationRelative() throws IOExc final Source source = SourceFactory.getSource(null, contextPath, location, false); - assertTrue(source instanceof FileSource); + assertInstanceOf(FileSource.class, source); assertEquals(Path.of(getClass().getResource("library.xqm").toURI()).toString(), source.path()); } @@ -115,7 +116,7 @@ public void getSourceFromResource_contextAbsoluteFileUrl_locationRelative() thro final Source source = SourceFactory.getSource(null, contextPath, location, false); - assertTrue(source instanceof ClassLoaderSource); + assertInstanceOf(ClassLoaderSource.class, source); assertEquals(getClass().getResource("library.xqm").getFile(), source.path()); } @@ -126,7 +127,7 @@ public void getSourceFromResource_contextAbsoluteFileUrl_locationAbsoluteUrl() t final Source source = SourceFactory.getSource(null, contextPath, location, false); - assertTrue(source instanceof ClassLoaderSource); + assertInstanceOf(ClassLoaderSource.class, source); assertEquals(getClass().getResource("library.xqm").getFile(), source.path()); } @@ -137,7 +138,7 @@ public void getSourceFromResource_contextAbsoluteFileUrl_locationRelativeUrl() t final Source source = SourceFactory.getSource(null, contextPath, location, false); - assertTrue(source instanceof ClassLoaderSource); + assertInstanceOf(ClassLoaderSource.class, source); assertEquals(getClass().getResource("library.xqm").getFile(), source.path()); } @@ -147,11 +148,11 @@ public void getSourceFromResource_contextAbsoluteFileUrl_locationRelativeUrl_bas final String location = "library.xqm"; final Source mainSource = SourceFactory.getSource(null, "", contextPath, false); - assertTrue(mainSource instanceof ClassLoaderSource); + assertInstanceOf(ClassLoaderSource.class, mainSource); final Source relativeSource = SourceFactory.getSource(null, ((ClassLoaderSource)mainSource).getSource(), location, false); - assertTrue(relativeSource instanceof ClassLoaderSource); + assertInstanceOf(ClassLoaderSource.class, relativeSource); assertEquals(getClass().getResource(location).getFile(), relativeSource.path()); } @@ -162,7 +163,7 @@ public void getSourceFromResource_contextFolderUrl_locationRelative() throws IOE final Source source = SourceFactory.getSource(null, contextPath, location, false); - assertTrue(source instanceof ClassLoaderSource); + assertInstanceOf(ClassLoaderSource.class, source); assertEquals(getClass().getResource("library.xqm").getFile(), source.path()); } @@ -173,7 +174,7 @@ public void getSourceFromResource_contextFolderUrl_locationAbsoluteUrl() throws final Source source = SourceFactory.getSource(null, contextPath, location, false); - assertTrue(source instanceof ClassLoaderSource); + assertInstanceOf(ClassLoaderSource.class, source); assertEquals(getClass().getResource("library.xqm").getFile(), source.path()); } @@ -184,7 +185,7 @@ public void getSourceFromResource_contextFolderUrl_locationRelativeUrl() throws final Source source = SourceFactory.getSource(null, contextPath, location, false); - assertTrue(source instanceof ClassLoaderSource); + assertInstanceOf(ClassLoaderSource.class, source); assertEquals(getClass().getResource("library.xqm").getFile(), source.path()); } @@ -194,11 +195,11 @@ public void getSourceFromResource_contextFolderUrl_locationRelativeUrl_basedOnSo final String location = "library.xqm"; final Source mainSource = SourceFactory.getSource(null, "", contextPath, false); - assertTrue(mainSource instanceof ClassLoaderSource); + assertInstanceOf(ClassLoaderSource.class, mainSource); final Source relativeSource = SourceFactory.getSource(null, ((ClassLoaderSource)mainSource).getSource(), location, false); - assertTrue(relativeSource instanceof ClassLoaderSource); + assertInstanceOf(ClassLoaderSource.class, relativeSource); assertEquals(getClass().getResource(location).getFile(), relativeSource.path()); } @@ -222,7 +223,7 @@ public void getSourceFromXmldb_noContext() throws IOException, PermissionDeniedE replay(mockBrokerPool, mockBroker, mockLockedDoc, mockBinDoc); final Source libSource = SourceFactory.getSource(mockBroker, contextPath, location, false); - assertTrue(libSource instanceof DBSource); + assertInstanceOf(DBSource.class, libSource); assertEquals(XmldbURI.create(location), ((DBSource)libSource).getDocumentPath()); verify(mockBrokerPool, mockBroker, mockLockedDoc, mockBinDoc); @@ -248,7 +249,7 @@ public void getSourceFromXmldb() throws IOException, PermissionDeniedException { replay(mockBrokerPool, mockBroker, mockLockedDoc, mockBinDoc); final Source libSource = SourceFactory.getSource(mockBroker, contextPath, location, false); - assertTrue(libSource instanceof DBSource); + assertInstanceOf(DBSource.class, libSource); assertEquals(XmldbURI.create(contextPath).append(location), ((DBSource)libSource).getDocumentPath()); verify(mockBrokerPool, mockBroker, mockLockedDoc, mockBinDoc); @@ -306,7 +307,7 @@ public void getSourceFromXmldbEmbedded_noContext() throws IOException, Permissio replay(mockBrokerPool, mockBroker, mockLockedDoc, mockBinDoc); final Source libSource = SourceFactory.getSource(mockBroker, contextPath, location, false); - assertTrue(libSource instanceof DBSource); + assertInstanceOf(DBSource.class, libSource); assertEquals(XmldbURI.create(location), ((DBSource)libSource).getDocumentPath()); verify(mockBrokerPool, mockBroker, mockLockedDoc, mockBinDoc); @@ -332,7 +333,7 @@ public void getSourceFromXmldbEmbedded() throws IOException, PermissionDeniedExc replay(mockBrokerPool, mockBroker, mockLockedDoc, mockBinDoc); final Source libSource = SourceFactory.getSource(mockBroker, contextPath, location, false); - assertTrue(libSource instanceof DBSource); + assertInstanceOf(DBSource.class, libSource); assertEquals(XmldbURI.create(contextPath).append(location), ((DBSource)libSource).getDocumentPath()); verify(mockBrokerPool, mockBroker, mockLockedDoc, mockBinDoc); @@ -390,7 +391,7 @@ public void getSourceFromDb() throws IOException, PermissionDeniedException { replay(mockBrokerPool, mockBroker, mockLockedDoc, mockBinDoc); final Source libSource = SourceFactory.getSource(mockBroker, contextPath, location, false); - assertTrue(libSource instanceof DBSource); + assertInstanceOf(DBSource.class, libSource); assertEquals(XmldbURI.create(contextPath).append(location), ((DBSource)libSource).getDocumentPath()); verify(mockBrokerPool, mockBroker, mockLockedDoc, mockBinDoc); @@ -416,7 +417,7 @@ public void getSourceFromDb_noContext() throws IOException, PermissionDeniedExce replay(mockBrokerPool, mockBroker, mockLockedDoc, mockBinDoc); final Source libSource = SourceFactory.getSource(mockBroker, contextPath, location, false); - assertTrue(libSource instanceof DBSource); + assertInstanceOf(DBSource.class, libSource); assertEquals(XmldbURI.create(location), ((DBSource)libSource).getDocumentPath()); verify(mockBrokerPool, mockBroker, mockLockedDoc, mockBinDoc); diff --git a/exist-core/src/test/java/org/exist/storage/BrokerPoolsTest.java b/exist-core/src/test/java/org/exist/storage/BrokerPoolsTest.java index 5a999932320..c987dfd5f03 100644 --- a/exist-core/src/test/java/org/exist/storage/BrokerPoolsTest.java +++ b/exist-core/src/test/java/org/exist/storage/BrokerPoolsTest.java @@ -35,7 +35,8 @@ import java.util.concurrent.*; import java.nio.file.Files; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author Patrick Reinhart diff --git a/exist-core/src/test/java/org/exist/storage/ValueIndexFactoryTest.java b/exist-core/src/test/java/org/exist/storage/ValueIndexFactoryTest.java index 91e82e611a6..6b7db8d961c 100644 --- a/exist-core/src/test/java/org/exist/storage/ValueIndexFactoryTest.java +++ b/exist-core/src/test/java/org/exist/storage/ValueIndexFactoryTest.java @@ -26,8 +26,10 @@ import org.exist.EXistException; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertTrue; + import org.exist.storage.btree.Value; import org.exist.xquery.value.DecimalValue; import org.exist.xquery.value.DoubleValue; @@ -82,7 +84,7 @@ public void roundTripDecimal() throws EXistException { byte data[] = ValueIndexFactory.serialize(new DecimalValue(dec), 0); Indexable value = ValueIndexFactory.deserialize(data, 0, data.length); - assertTrue(value instanceof DecimalValue); + assertInstanceOf(DecimalValue.class, value); assertEquals(dec, ((DecimalValue)value).getValue()); } @@ -93,7 +95,7 @@ public void roundTripDouble() throws EXistException { for (final double d : values) { final byte[] data = ValueIndexFactory.serialize(new DoubleValue(d), 0); final Indexable value = ValueIndexFactory.deserialize(data, 0, data.length); - assertTrue(value instanceof DoubleValue); + assertInstanceOf(DoubleValue.class, value); assertEquals(d, ((DoubleValue) value).getValue(), 0.0); } } diff --git a/exist-core/src/test/java/org/exist/storage/journal/JournalBinaryTest.java b/exist-core/src/test/java/org/exist/storage/journal/JournalBinaryTest.java index bfb46c1e8b2..7c8ae8c0e9a 100644 --- a/exist-core/src/test/java/org/exist/storage/journal/JournalBinaryTest.java +++ b/exist-core/src/test/java/org/exist/storage/journal/JournalBinaryTest.java @@ -65,8 +65,8 @@ import static java.nio.file.StandardOpenOption.CREATE_NEW; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; /** * Test expectations to check that the correct entries @@ -446,7 +446,7 @@ protected BinaryDocLocator storeAndVerify(final DBBroker broker, final Txn trans final InputSource data, final String dbFilename) throws EXistException, PermissionDeniedException, IOException, SAXException, LockException { - assertTrue(data instanceof FileInputSource); + assertInstanceOf(FileInputSource.class, data); broker.storeDocument(transaction, XmldbURI.create(dbFilename), data, MimeType.BINARY_TYPE, collection); final BinaryDocument doc = (BinaryDocument) collection.getDocument(broker, XmldbURI.create(dbFilename)); diff --git a/exist-core/src/test/java/org/exist/storage/lock/GetXMLResourceNoLockTest.java b/exist-core/src/test/java/org/exist/storage/lock/GetXMLResourceNoLockTest.java index 0709936e720..b4527b51d2f 100644 --- a/exist-core/src/test/java/org/exist/storage/lock/GetXMLResourceNoLockTest.java +++ b/exist-core/src/test/java/org/exist/storage/lock/GetXMLResourceNoLockTest.java @@ -45,7 +45,8 @@ import org.junit.jupiter.api.extension.RegisterExtension; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; /** * diff --git a/exist-core/src/test/java/org/exist/storage/vector/VectorStoreImplTest.java b/exist-core/src/test/java/org/exist/storage/vector/VectorStoreImplTest.java index 0901b278a69..2517cfb01db 100644 --- a/exist-core/src/test/java/org/exist/storage/vector/VectorStoreImplTest.java +++ b/exist-core/src/test/java/org/exist/storage/vector/VectorStoreImplTest.java @@ -31,6 +31,7 @@ import org.junit.jupiter.api.extension.RegisterExtension; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; public class VectorStoreImplTest { @@ -45,7 +46,7 @@ public void setUp() { final BrokerPool pool = existEmbeddedServer.getBrokerPool(); final VectorStore vectorStore = pool.getVectorStore(); assertNotNull(vectorStore); - assertTrue(vectorStore instanceof VectorStoreImpl); + assertInstanceOf(VectorStoreImpl.class, vectorStore); store = (VectorStoreImpl) vectorStore; store.resetEntryCountCache(); } diff --git a/exist-core/src/main/java/org/exist/test/DiffMatcher.java b/exist-core/src/test/java/org/exist/test/DiffMatcher.java similarity index 100% rename from exist-core/src/main/java/org/exist/test/DiffMatcher.java rename to exist-core/src/test/java/org/exist/test/DiffMatcher.java diff --git a/exist-core/src/main/java/org/exist/test/ExistEmbeddedServer.java b/exist-core/src/test/java/org/exist/test/ExistEmbeddedServer.java similarity index 100% rename from exist-core/src/main/java/org/exist/test/ExistEmbeddedServer.java rename to exist-core/src/test/java/org/exist/test/ExistEmbeddedServer.java diff --git a/exist-core/src/main/java/org/exist/test/ExistWebServer.java b/exist-core/src/test/java/org/exist/test/ExistWebServer.java similarity index 100% rename from exist-core/src/main/java/org/exist/test/ExistWebServer.java rename to exist-core/src/test/java/org/exist/test/ExistWebServer.java diff --git a/exist-core/src/main/java/org/exist/test/ExistXmldbEmbeddedServer.java b/exist-core/src/test/java/org/exist/test/ExistXmldbEmbeddedServer.java similarity index 100% rename from exist-core/src/main/java/org/exist/test/ExistXmldbEmbeddedServer.java rename to exist-core/src/test/java/org/exist/test/ExistXmldbEmbeddedServer.java diff --git a/exist-core/src/main/java/org/exist/test/TestConstants.java b/exist-core/src/test/java/org/exist/test/TestConstants.java similarity index 100% rename from exist-core/src/main/java/org/exist/test/TestConstants.java rename to exist-core/src/test/java/org/exist/test/TestConstants.java diff --git a/exist-core/src/main/java/org/exist/test/TransactionTestDSL.java b/exist-core/src/test/java/org/exist/test/TransactionTestDSL.java similarity index 100% rename from exist-core/src/main/java/org/exist/test/TransactionTestDSL.java rename to exist-core/src/test/java/org/exist/test/TransactionTestDSL.java diff --git a/exist-core/src/main/java/org/exist/test/XQueryAssertions.java b/exist-core/src/test/java/org/exist/test/XQueryAssertions.java similarity index 100% rename from exist-core/src/main/java/org/exist/test/XQueryAssertions.java rename to exist-core/src/test/java/org/exist/test/XQueryAssertions.java diff --git a/exist-core/src/main/java/org/exist/test/XQueryCompilationTest.java b/exist-core/src/test/java/org/exist/test/XQueryCompilationTest.java similarity index 100% rename from exist-core/src/main/java/org/exist/test/XQueryCompilationTest.java rename to exist-core/src/test/java/org/exist/test/XQueryCompilationTest.java diff --git a/exist-core/src/main/java/org/exist/test/XmlStringDiffMatcher.java b/exist-core/src/test/java/org/exist/test/XmlStringDiffMatcher.java similarity index 100% rename from exist-core/src/main/java/org/exist/test/XmlStringDiffMatcher.java rename to exist-core/src/test/java/org/exist/test/XmlStringDiffMatcher.java diff --git a/exist-core/src/main/java/org/exist/test/runner/AbstractTestRunner.java b/exist-core/src/test/java/org/exist/test/runner/AbstractTestRunner.java similarity index 86% rename from exist-core/src/main/java/org/exist/test/runner/AbstractTestRunner.java rename to exist-core/src/test/java/org/exist/test/runner/AbstractTestRunner.java index a79a1cb3ce0..33b5da54b69 100644 --- a/exist-core/src/main/java/org/exist/test/runner/AbstractTestRunner.java +++ b/exist-core/src/test/java/org/exist/test/runner/AbstractTestRunner.java @@ -38,7 +38,6 @@ import org.exist.xquery.XQueryContext; import org.exist.xquery.value.AnyURIValue; import org.exist.xquery.value.Sequence; -import org.junit.runner.Runner; import java.io.IOException; import java.nio.file.Files; @@ -50,18 +49,16 @@ import static java.util.Objects.requireNonNull; /** - * Base class for XSuite test runners. + * Base class for the runners of a file of XQSuite or XML tests. * * @author Adam Retter */ -public abstract class AbstractTestRunner extends Runner { +public abstract class AbstractTestRunner { protected final Path path; - protected final boolean parallel; - protected AbstractTestRunner(final Path path, final boolean parallel) { + protected AbstractTestRunner(final Path path) { this.path = path; - this.parallel = parallel; } /** @@ -73,6 +70,24 @@ public Path getSourcePath() { return path; } + /** + * @return the name that groups the tests of this file + */ + public abstract String getSuiteName(); + + /** + * @return the names of the tests that are expected to be reported when this file is run + */ + public abstract List getTestNames(); + + /** + * Runs the tests of this file, reporting each outcome to {@code events}. + * + * @param events receives the outcome of each test + * @param brokerPool the running database to execute the tests against + */ + public abstract void run(TestEvents events, BrokerPool brokerPool); + protected static Sequence executeQuery(final BrokerPool brokerPool, final Source query, final List>> externalVariableBindings) throws EXistException, PermissionDeniedException, XPathException, IOException, DatabaseConfigurationException { return executeQuery(brokerPool, query, externalVariableBindings, null); } @@ -132,17 +147,4 @@ protected static Sequence executeQuery(final BrokerPool brokerPool, final Source } } } - - protected static String checkDescription(final Object source, final String description) { - if (description == null) { - throw new IllegalArgumentException(source + " description is null"); - } - if (description.isEmpty()) { - throw new IllegalArgumentException(source + " description is empty"); - } - if (description.startsWith("(")) { - throw new IllegalArgumentException(source + " description '" + description + "' starts with '('"); - } - return description; - } } diff --git a/exist-core/src/test/java/org/exist/test/runner/AbstractTestRunnerTest.java b/exist-core/src/test/java/org/exist/test/runner/AbstractTestRunnerTest.java deleted file mode 100644 index 38c47094abc..00000000000 --- a/exist-core/src/test/java/org/exist/test/runner/AbstractTestRunnerTest.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * eXist-db Open Source Native XML Database - * Copyright (C) 2001 The eXist-db Authors - * - * info@exist-db.org - * http://www.exist-db.org - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.exist.test.runner; - -import org.junit.jupiter.api.Test; -import org.junit.runner.Description; -import org.junit.runner.notification.RunNotifier; - -import static org.exist.test.runner.AbstractTestRunner.checkDescription; -import static org.junit.jupiter.api.Assertions.*; - -/** - * Tests the global methods of the {@link AbstractTestRunner}. - */ -class AbstractTestRunnerTest { - @Test - void testCheckDescriptionNull() { - final IllegalArgumentException iae = assertThrows(IllegalArgumentException.class, () -> checkDescription(this, null)); - assertEquals(this + " description is null", iae.getMessage()); - } - - @Test - void testCheckDescriptionEmpty() { - final IllegalArgumentException iae = assertThrows(IllegalArgumentException.class, () -> checkDescription(this, "")); - assertEquals(this + " description is empty", iae.getMessage()); - } - - @Test - void testCheckDescriptionStartsWithBrace() { - final IllegalArgumentException iae = assertThrows(IllegalArgumentException.class, () -> checkDescription(this, "(bla)")); - assertEquals(this + " description '(bla)' starts with '('", iae.getMessage()); - } - - @Test - void testCheckDescription() { - assertEquals("testMethod", checkDescription(this, "testMethod")); - } - - @Test - void testRun() { - final AbstractTestRunner runner = new AbstractTestRunner(null, false) { - @Override - public void run(final RunNotifier notifier) { - // no action - } - - @Override - public Description getDescription() { - return Description.createTestDescription(getClass(), "testRun"); - } - }; - - final RunNotifier notifier = new RunNotifier(); - assertDoesNotThrow(() -> runner.run(notifier)); - } -} diff --git a/exist-core/src/test/java/org/exist/test/runner/DebuggabilityNavigabilitySuite.java b/exist-core/src/test/java/org/exist/test/runner/DebuggabilityNavigabilitySuite.java deleted file mode 100644 index 446621de572..00000000000 --- a/exist-core/src/test/java/org/exist/test/runner/DebuggabilityNavigabilitySuite.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * eXist-db Open Source Native XML Database - * Copyright (C) 2001 The eXist-db Authors - * - * info@exist-db.org - * http://www.exist-db.org - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.exist.test.runner; - -import org.junit.runner.RunWith; - -/** - * XSuite that runs the debuggability test resource (failing-both.xqm: one assertion failure, - * one unexpected error) so we can assert on failure stack traces (navigate to XQuery / navigate to Java). - */ -@RunWith(XSuite.class) -@XSuite.XSuiteFiles({ - "src/test/resources/org/exist/test/runner/failing-both.xqm" -}) -public class DebuggabilityNavigabilitySuite { -} diff --git a/exist-core/src/main/java/org/exist/test/runner/ExtTestAssumptionFailedFunction.java b/exist-core/src/test/java/org/exist/test/runner/ExtTestAssumptionFailedFunction.java similarity index 72% rename from exist-core/src/main/java/org/exist/test/runner/ExtTestAssumptionFailedFunction.java rename to exist-core/src/test/java/org/exist/test/runner/ExtTestAssumptionFailedFunction.java index b107a37005f..4b5cb759b36 100644 --- a/exist-core/src/main/java/org/exist/test/runner/ExtTestAssumptionFailedFunction.java +++ b/exist-core/src/test/java/org/exist/test/runner/ExtTestAssumptionFailedFunction.java @@ -29,22 +29,18 @@ import org.exist.xquery.value.Sequence; import org.exist.xquery.value.StringValue; import org.exist.xquery.value.Type; -import org.junit.AssumptionViolatedException; -import org.junit.runner.Description; -import org.junit.runner.notification.Failure; -import org.junit.runner.notification.RunNotifier; import static org.exist.xquery.FunctionDSL.optParam; import static org.exist.xquery.FunctionDSL.param; import static org.exist.xquery.FunctionDSL.params; public class ExtTestAssumptionFailedFunction extends JUnitIntegrationFunction { - public ExtTestAssumptionFailedFunction(final XQueryContext context, final String parentName, final RunNotifier notifier) { + public ExtTestAssumptionFailedFunction(final XQueryContext context, final String parentName, final TestEvents events) { super("ext-test-assumption-failed-function", params( param("name", Type.STRING, "name of the test"), optParam("error", Type.MAP_ITEM, "error detail of the test") - ), context, parentName, notifier); + ), context, parentName, events); } @Override @@ -55,25 +51,19 @@ public Sequence eval(final Sequence contextSequence, final Item contextItem) thr final Sequence arg2 = getCurrentArguments().length == 2 ? getCurrentArguments()[1] : null; final MapType assumption = arg2 != null ? (MapType)arg2.itemAt(0) : null; - final Description description = createTestDescription(name); // notify JUnit try { - final AssumptionViolatedException assumptionFailureReason = assumptionMapAsAssumptionViolationException(assumption); - - // NOTE: We remove the StackTrace, because it is not useful to have a Java Stack Trace pointing into the XML XQuery Test Suite code - assumptionFailureReason.setStackTrace(new StackTraceElement[0]); - - notifier.fireTestAssumptionFailed(new Failure(description, assumptionFailureReason)); + events.assumptionFailed(name, assumptionMessage(assumption)); } catch (final XPathException e) { //signal internal failure - notifier.fireTestFailure(new Failure(description, e)); + events.failed(name, e); } return Sequence.EMPTY_SEQUENCE; } - public AssumptionViolatedException assumptionMapAsAssumptionViolationException(final MapType assumptionMap) throws XPathException { + String assumptionMessage(final MapType assumptionMap) throws XPathException { final Sequence seqName = assumptionMap.get(new StringValue(this, "name")); final String name; if(seqName != null && !seqName.isEmpty()) { @@ -90,6 +80,6 @@ public AssumptionViolatedException assumptionMapAsAssumptionViolationException(f value = ""; } - return new AssumptionViolatedException("Assumption %" + name + " does not hold for: " + value); + return "Assumption %" + name + " does not hold for: " + value; } } diff --git a/exist-core/src/main/java/org/exist/test/runner/ExtTestErrorFunction.java b/exist-core/src/test/java/org/exist/test/runner/ExtTestErrorFunction.java similarity index 95% rename from exist-core/src/main/java/org/exist/test/runner/ExtTestErrorFunction.java rename to exist-core/src/test/java/org/exist/test/runner/ExtTestErrorFunction.java index c6c0b18a92a..9b0fcd040d0 100644 --- a/exist-core/src/main/java/org/exist/test/runner/ExtTestErrorFunction.java +++ b/exist-core/src/test/java/org/exist/test/runner/ExtTestErrorFunction.java @@ -27,9 +27,6 @@ import org.exist.xquery.XQueryContext; import org.exist.xquery.functions.map.MapType; import org.exist.xquery.value.*; -import org.junit.runner.Description; -import org.junit.runner.notification.Failure; -import org.junit.runner.notification.RunNotifier; import javax.annotation.Nullable; import java.util.Arrays; @@ -40,12 +37,12 @@ public class ExtTestErrorFunction extends JUnitIntegrationFunction { - public ExtTestErrorFunction(final XQueryContext context, final String parentName, final RunNotifier notifier) { + public ExtTestErrorFunction(final XQueryContext context, final String parentName, final TestEvents events) { super("ext-test-error-function", params( param("name", Type.STRING, "name of the test"), optParam("error", Type.MAP_ITEM, "error detail of the test. e.g. map { \"code\": $err:code, \"description\": $err:description, \"value\": $err:value, \"module\": $err:module, \"line-number\": $err:line-number, \"column-number\": $err:column-number, \"additional\": $err:additional, \"xquery-stack-trace\": $exerr:xquery-stack-trace, \"java-stack-trace\": $exerr:java-stack-trace}") - ), context, parentName, notifier); + ), context, parentName, events); } @Override @@ -56,16 +53,15 @@ public Sequence eval(final Sequence contextSequence, final Item contextItem) thr final Sequence arg2 = getCurrentArguments().length == 2 ? getCurrentArguments()[1] : null; final MapType error = arg2 != null ? (MapType)arg2.itemAt(0) : null; - final Description description = createTestDescription(name); // notify JUnit try { final XPathException errorReason = errorMapAsXPathException(name, error); logOneLineIfXQueryError(name, error); - notifier.fireTestFailure(new Failure(description, errorReason)); + events.failed(name, errorReason); } catch (final XPathException e) { //signal internal failure - notifier.fireTestFailure(new Failure(description, e)); + events.failed(name, e); } return Sequence.EMPTY_SEQUENCE; diff --git a/exist-core/src/main/java/org/exist/test/runner/ExtTestFailureFunction.java b/exist-core/src/test/java/org/exist/test/runner/ExtTestFailureFunction.java similarity index 93% rename from exist-core/src/main/java/org/exist/test/runner/ExtTestFailureFunction.java rename to exist-core/src/test/java/org/exist/test/runner/ExtTestFailureFunction.java index 92147fc3b3a..57ca4e30c3a 100644 --- a/exist-core/src/main/java/org/exist/test/runner/ExtTestFailureFunction.java +++ b/exist-core/src/test/java/org/exist/test/runner/ExtTestFailureFunction.java @@ -32,9 +32,6 @@ import org.exist.xquery.value.SequenceIterator; import org.exist.xquery.value.StringValue; import org.exist.xquery.value.Type; -import org.junit.runner.Description; -import org.junit.runner.notification.Failure; -import org.junit.runner.notification.RunNotifier; import org.opentest4j.AssertionFailedError; import org.xml.sax.SAXException; @@ -53,17 +50,17 @@ public class ExtTestFailureFunction extends JUnitIntegrationFunction { @Nullable private final Path sourcePath; - public ExtTestFailureFunction(final XQueryContext context, final String parentName, final RunNotifier notifier) { - this(context, parentName, notifier, null); + public ExtTestFailureFunction(final XQueryContext context, final String parentName, final TestEvents events) { + this(context, parentName, events, null); } - public ExtTestFailureFunction(final XQueryContext context, final String parentName, final RunNotifier notifier, @Nullable final Path sourcePath) { + public ExtTestFailureFunction(final XQueryContext context, final String parentName, final TestEvents events, @Nullable final Path sourcePath) { super("ext-test-failure-function", params( param("name", Type.STRING, "name of the test"), param("expected", Type.MAP_ITEM, "expected result of the test"), param("actual", Type.MAP_ITEM, "actual result of the test") - ), context, parentName, notifier); + ), context, parentName, events); this.sourcePath = sourcePath; } @@ -78,7 +75,6 @@ public Sequence eval(final Sequence contextSequence, final Item contextItem) thr final Sequence arg3 = getCurrentArguments()[2]; final MapType actual = (MapType)arg3.itemAt(0); - final Description description = createTestDescription(name); // notify JUnit try { @@ -104,10 +100,10 @@ public Sequence eval(final Sequence contextSequence, final Item contextItem) thr failureReason.setStackTrace(new StackTraceElement[0]); } - notifier.fireTestFailure(new Failure(description, failureReason)); + events.failed(name, failureReason); } catch (final XPathException | SAXException | IOException | IllegalStateException e) { //signal internal failure - notifier.fireTestFailure(new Failure(description, e)); + events.failed(name, e); } return Sequence.EMPTY_SEQUENCE; diff --git a/exist-core/src/main/java/org/exist/test/runner/ExtTestFinishedFunction.java b/exist-core/src/test/java/org/exist/test/runner/ExtTestFinishedFunction.java similarity index 82% rename from exist-core/src/main/java/org/exist/test/runner/ExtTestFinishedFunction.java rename to exist-core/src/test/java/org/exist/test/runner/ExtTestFinishedFunction.java index b3018742bb8..83510fda17f 100644 --- a/exist-core/src/main/java/org/exist/test/runner/ExtTestFinishedFunction.java +++ b/exist-core/src/test/java/org/exist/test/runner/ExtTestFinishedFunction.java @@ -27,16 +27,14 @@ import org.exist.xquery.value.Item; import org.exist.xquery.value.Sequence; import org.exist.xquery.value.Type; -import org.junit.runner.Description; -import org.junit.runner.notification.RunNotifier; import static org.exist.xquery.FunctionDSL.param; import static org.exist.xquery.FunctionDSL.params; public class ExtTestFinishedFunction extends JUnitIntegrationFunction { - public ExtTestFinishedFunction(final XQueryContext context, final String parentName, final RunNotifier notifier) { - super("ext-test-finished-function", params(param("name", Type.STRING, "name of the test")), context, parentName, notifier); + public ExtTestFinishedFunction(final XQueryContext context, final String parentName, final TestEvents events) { + super("ext-test-finished-function", params(param("name", Type.STRING, "name of the test")), context, parentName, events); } @Override @@ -44,9 +42,7 @@ public Sequence eval(final Sequence contextSequence, final Item contextItem) thr final Sequence arg1 = getCurrentArguments()[0]; final String name = arg1.itemAt(0).getStringValue(); - // notify JUnit - final Description description = createTestDescription(name); - notifier.fireTestFinished(description); + events.finished(name); return Sequence.EMPTY_SEQUENCE; } diff --git a/exist-core/src/main/java/org/exist/test/runner/ExtTestIgnoredFunction.java b/exist-core/src/test/java/org/exist/test/runner/ExtTestIgnoredFunction.java similarity index 83% rename from exist-core/src/main/java/org/exist/test/runner/ExtTestIgnoredFunction.java rename to exist-core/src/test/java/org/exist/test/runner/ExtTestIgnoredFunction.java index fe9c9525cb6..ee5b0d1dd96 100644 --- a/exist-core/src/main/java/org/exist/test/runner/ExtTestIgnoredFunction.java +++ b/exist-core/src/test/java/org/exist/test/runner/ExtTestIgnoredFunction.java @@ -27,16 +27,14 @@ import org.exist.xquery.value.Item; import org.exist.xquery.value.Sequence; import org.exist.xquery.value.Type; -import org.junit.runner.Description; -import org.junit.runner.notification.RunNotifier; import static org.exist.xquery.FunctionDSL.param; import static org.exist.xquery.FunctionDSL.params; public class ExtTestIgnoredFunction extends JUnitIntegrationFunction { - public ExtTestIgnoredFunction(final XQueryContext context, final String parentName, final RunNotifier notifier) { - super("ext-test-ignored-function", params(param("name", Type.STRING, "name of the test")), context, parentName, notifier); + public ExtTestIgnoredFunction(final XQueryContext context, final String parentName, final TestEvents events) { + super("ext-test-ignored-function", params(param("name", Type.STRING, "name of the test")), context, parentName, events); } @Override @@ -44,9 +42,7 @@ public Sequence eval(final Sequence contextSequence, final Item contextItem) thr final Sequence arg1 = getCurrentArguments()[0]; final String name = arg1.itemAt(0).getStringValue(); - // notify JUnit - final Description description = createTestDescription(name); - notifier.fireTestIgnored(description); + events.ignored(name); return Sequence.EMPTY_SEQUENCE; } diff --git a/exist-core/src/main/java/org/exist/test/runner/ExtTestStartedFunction.java b/exist-core/src/test/java/org/exist/test/runner/ExtTestStartedFunction.java similarity index 83% rename from exist-core/src/main/java/org/exist/test/runner/ExtTestStartedFunction.java rename to exist-core/src/test/java/org/exist/test/runner/ExtTestStartedFunction.java index c962b35ed6d..eca994b0c28 100644 --- a/exist-core/src/main/java/org/exist/test/runner/ExtTestStartedFunction.java +++ b/exist-core/src/test/java/org/exist/test/runner/ExtTestStartedFunction.java @@ -27,16 +27,14 @@ import org.exist.xquery.value.Item; import org.exist.xquery.value.Sequence; import org.exist.xquery.value.Type; -import org.junit.runner.Description; -import org.junit.runner.notification.RunNotifier; import static org.exist.xquery.FunctionDSL.param; import static org.exist.xquery.FunctionDSL.params; public class ExtTestStartedFunction extends JUnitIntegrationFunction { - public ExtTestStartedFunction(final XQueryContext context, final String parentName, final RunNotifier notifier) { - super("ext-test-started-function", params(param("name", Type.STRING, "name of the test")), context, parentName, notifier); + public ExtTestStartedFunction(final XQueryContext context, final String parentName, final TestEvents events) { + super("ext-test-started-function", params(param("name", Type.STRING, "name of the test")), context, parentName, events); } @Override @@ -44,9 +42,7 @@ public Sequence eval(final Sequence contextSequence, final Item contextItem) thr final Sequence arg1 = getCurrentArguments()[0]; final String name = arg1.itemAt(0).getStringValue(); - // notify JUnit - final Description description = createTestDescription(name); - notifier.fireTestStarted(description); + events.started(name); return Sequence.EMPTY_SEQUENCE; } diff --git a/exist-core/src/main/java/org/exist/test/runner/JUnitIntegrationFunction.java b/exist-core/src/test/java/org/exist/test/runner/JUnitIntegrationFunction.java similarity index 75% rename from exist-core/src/main/java/org/exist/test/runner/JUnitIntegrationFunction.java rename to exist-core/src/test/java/org/exist/test/runner/JUnitIntegrationFunction.java index fa24c4bc9d2..0b59a50e8f0 100644 --- a/exist-core/src/main/java/org/exist/test/runner/JUnitIntegrationFunction.java +++ b/exist-core/src/test/java/org/exist/test/runner/JUnitIntegrationFunction.java @@ -27,8 +27,6 @@ import org.exist.xquery.UserDefinedFunction; import org.exist.xquery.XQueryContext; import org.exist.xquery.value.FunctionParameterSequenceType; -import org.junit.runner.Description; -import org.junit.runner.notification.RunNotifier; import javax.xml.XMLConstants; @@ -36,25 +34,25 @@ import static org.exist.xquery.FunctionDSL.returnsNothing; /** - * Base class for XQuery functions that integrate with JUnit. + * Base class for XQuery functions that report test outcomes to a {@link TestEvents} sink. * * @author Adam Retter */ public abstract class JUnitIntegrationFunction extends UserDefinedFunction { protected final String suiteName; - protected final RunNotifier notifier; + protected final TestEvents events; - public JUnitIntegrationFunction(final String functionName, final FunctionParameterSequenceType[] paramTypes, final XQueryContext context, final String suiteName, final RunNotifier notifier) { + public JUnitIntegrationFunction(final String functionName, final FunctionParameterSequenceType[] paramTypes, final XQueryContext context, final String suiteName, final TestEvents events) { super(context, functionSignature( new QName(functionName, XMLConstants.NULL_NS_URI), - "External JUnit integration function", + "External test integration function", returnsNothing(), paramTypes )); this.suiteName = suiteName; - this.notifier = notifier; + this.events = events; } @Override @@ -65,14 +63,4 @@ public void accept(final ExpressionVisitor visitor) { visited = true; } - /** - * Create a JUnit description of the test. - * - * @param name the name of the test. - * - * @return the test description. - */ - protected Description createTestDescription(final String name) { - return Description.createTestDescription(suiteName, name); - } } diff --git a/exist-core/src/test/java/org/exist/test/runner/XSuiteDiscoveryTest.java b/exist-core/src/test/java/org/exist/test/runner/TestDiscoveryTest.java similarity index 50% rename from exist-core/src/test/java/org/exist/test/runner/XSuiteDiscoveryTest.java rename to exist-core/src/test/java/org/exist/test/runner/TestDiscoveryTest.java index 1a7d4261902..4a455a29860 100644 --- a/exist-core/src/test/java/org/exist/test/runner/XSuiteDiscoveryTest.java +++ b/exist-core/src/test/java/org/exist/test/runner/TestDiscoveryTest.java @@ -19,29 +19,25 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - package org.exist.test.runner; import org.exist.storage.BrokerPool; import org.exist.test.ExistEmbeddedServer; import org.junit.jupiter.api.Test; -import org.junit.runner.Description; -import org.junit.runners.model.InitializationError; +import org.junit.jupiter.api.extension.RegisterExtension; import java.nio.file.Path; -import java.util.ArrayList; import java.util.List; -import org.junit.jupiter.api.extension.RegisterExtension; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; /** - * Tests discovery via a single XQuery per file. - * Asserts that the discovery XQuery returns the expected test list for known files, - * and that XQueryTestRunner uses discovery when the DB is up and runs the same tests. + * Tests discovery of XQSuite tests, either by one discovery XQuery on a running database or by compiling the module. */ -public class XSuiteDiscoveryTest { +public class TestDiscoveryTest { + + private static final String RESOURCES = "src/test/resources/org/exist/test/runner/"; @RegisterExtension public ExistEmbeddedServer existEmbeddedServer = new ExistEmbeddedServer(true, true); @@ -49,8 +45,9 @@ public class XSuiteDiscoveryTest { @Test public void discoveryReturnsTestListForSingleTestFile() { final BrokerPool pool = existEmbeddedServer.getBrokerPool(); - final Path path = Path.of("src/test/resources/org/exist/test/runner/single-test.xqm").toAbsolutePath(); + final Path path = Path.of(RESOURCES + "single-test.xqm").toAbsolutePath(); final XQueryTestRunner.XQueryTestInfo info = XQueryTestRunner.runDiscovery(pool, path); + assertNotNull(info, "discovery XQuery should return test info"); assertEquals("http://exist-db.org/xquery/single-test-module", info.namespace(), "namespace"); assertEquals(1, info.testFunctions().size(), "one test function"); @@ -59,32 +56,17 @@ public void discoveryReturnsTestListForSingleTestFile() { } /** - * When the DB is up (XSuite.EXIST_EMBEDDED_SERVER_CLASS_INSTANCE set), XQueryTestRunner should use - * discovery and the runner's description should match the discovery result. + * Both ways of discovering tests must name them as the XQSuite runtime reports them, or a test + * would be discovered under one name and reported under another. */ @Test - public void runnerUsesDiscoveryWhenDbIsUp() throws InitializationError { - final Path path = Path.of("src/test/resources/org/exist/test/runner/single-test.xqm").toAbsolutePath(); + public void discoveringWithTheDatabaseAndByCompilingGiveTheSameNames() throws TestInitializationException { final BrokerPool pool = existEmbeddedServer.getBrokerPool(); - final XQueryTestRunner.XQueryTestInfo discoveryInfo = XQueryTestRunner.runDiscovery(pool, path); - assertNotNull(discoveryInfo, "discovery must succeed in this test"); - - final ExistEmbeddedServer previous = XSuite.EXIST_EMBEDDED_SERVER_CLASS_INSTANCE; - try { - XSuite.EXIST_EMBEDDED_SERVER_CLASS_INSTANCE = existEmbeddedServer; - final XQueryTestRunner runner = new XQueryTestRunner(path, false); - final Description description = runner.getDescription(); - final List children = description.getChildren(); - assertEquals(discoveryInfo.testFunctions().size(), children.size(), "runner should have same number of tests as discovery"); - final List childNames = new ArrayList<>(); - for (final Description d : children) { - childNames.add(d.getMethodName()); - } - for (int i = 0; i < discoveryInfo.testFunctions().size(); i++) { - assertEquals(discoveryInfo.testFunctions().get(i).localName(), childNames.get(i), "test name from runner should match discovery"); - } - } finally { - XSuite.EXIST_EMBEDDED_SERVER_CLASS_INSTANCE = previous; + for (final String file : List.of("single-test.xqm", "hyphenated-prefix.xqm")) { + final Path path = Path.of(RESOURCES + file).toAbsolutePath(); + final List viaDatabase = new XQueryTestRunner(path, pool).getTestNames(); + final List viaCompiling = new XQueryTestRunner(path, null).getTestNames(); + assertEquals(viaCompiling, viaDatabase, "test names discovered for " + file); } } } diff --git a/exist-core/src/test/java/org/exist/test/runner/ParallelWithHungSuite.java b/exist-core/src/test/java/org/exist/test/runner/TestEvents.java similarity index 58% rename from exist-core/src/test/java/org/exist/test/runner/ParallelWithHungSuite.java rename to exist-core/src/test/java/org/exist/test/runner/TestEvents.java index 531d35ad85c..6a85f856ac8 100644 --- a/exist-core/src/test/java/org/exist/test/runner/ParallelWithHungSuite.java +++ b/exist-core/src/test/java/org/exist/test/runner/TestEvents.java @@ -19,20 +19,28 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - package org.exist.test.runner; -import org.junit.runner.RunWith; - /** - * XSuite that runs one passing file and one file that never emits activity (hanging.xqm). - * Used by XSuiteParallelTest to verify progress-based hang detection reports the hung file. + * Receives the outcome of each test as the XQuery side of an XQSuite or XML test + * run reports it. Decouples the XQuery callback functions from any particular + * test framework, such as the JUnit Platform engine for XQSuite tests. */ -@RunWith(XSuite.class) -@XSuite.XSuiteParallel -@XSuite.XSuiteFiles({ - "src/test/resources/org/exist/test/runner/single-test.xqm", - "src/test/resources/org/exist/test/runner/hanging.xqm" -}) -public class ParallelWithHungSuite { +public interface TestEvents { + + void started(String testName); + + void finished(String testName); + + void ignored(String testName); + + /** + * An assertion failure or an error raised while running the test. + */ + void failed(String testName, Throwable reason); + + /** + * The test's assumptions did not hold, so it is aborted rather than failed. + */ + void assumptionFailed(String testName, String message); } diff --git a/exist-core/src/test/java/org/exist/test/runner/SerializationFailureMessageSuite.java b/exist-core/src/test/java/org/exist/test/runner/TestInitializationException.java similarity index 70% rename from exist-core/src/test/java/org/exist/test/runner/SerializationFailureMessageSuite.java rename to exist-core/src/test/java/org/exist/test/runner/TestInitializationException.java index fd91a39846d..ce8c0702712 100644 --- a/exist-core/src/test/java/org/exist/test/runner/SerializationFailureMessageSuite.java +++ b/exist-core/src/test/java/org/exist/test/runner/TestInitializationException.java @@ -19,18 +19,18 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - package org.exist.test.runner; -import org.junit.runner.RunWith; - /** - * XSuite that runs {@code failing-serialization.xqm} (a single assertion failure whose result is a - * node) so we can assert on how the actual value is rendered into the failure message. + * A file of tests could not be read or compiled, so its tests cannot be discovered. */ -@RunWith(XSuite.class) -@XSuite.XSuiteFiles({ - "src/test/resources/org/exist/test/runner/failing-serialization.xqm" -}) -public class SerializationFailureMessageSuite { +public class TestInitializationException extends Exception { + + public TestInitializationException(final String message) { + super(message); + } + + public TestInitializationException(final Throwable cause) { + super(cause); + } } diff --git a/exist-core/src/test/java/org/exist/test/runner/TestRunners.java b/exist-core/src/test/java/org/exist/test/runner/TestRunners.java new file mode 100644 index 00000000000..511b3ba3323 --- /dev/null +++ b/exist-core/src/test/java/org/exist/test/runner/TestRunners.java @@ -0,0 +1,67 @@ +/* + * eXist-db Open Source Native XML Database + * Copyright (C) 2001 The eXist-db Authors + * + * info@exist-db.org + * http://www.exist-db.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.exist.test.runner; + +import org.exist.storage.BrokerPool; +import org.exist.util.XMLFilenameFilter; +import org.exist.util.XQueryFilenameFilter; + +import javax.annotation.Nullable; +import java.nio.file.Path; + +/** + * Creates the runner for a file of XQSuite or XML tests. + */ +public final class TestRunners { + + private TestRunners() { + } + + /** + * @param path a file in a suite + * + * @return true if the file is an XQuery file of tests + */ + public static boolean isXQueryTestFile(final Path path) { + return XQueryFilenameFilter.asPredicate().test(path) && !"runTests.xql".equals(path.getFileName().toString()); + } + + /** + * Creates the test runner for a file of tests. + * + * @param path the XQuery or XML test file + * @param discoveryPool a running database to discover XQuery tests with, or null to compile the module instead + * + * @return the runner, or null if the file is not a test file + * + * @throws TestInitializationException if the file cannot be read as a test file + */ + public static @Nullable AbstractTestRunner newTestRunner(final Path path, @Nullable final BrokerPool discoveryPool) throws TestInitializationException { + if (XMLFilenameFilter.asPredicate().test(path)) { + return new XMLTestRunner(path); + } else if (isXQueryTestFile(path)) { + return new XQueryTestRunner(path, discoveryPool); + } else { + return null; + } + } +} diff --git a/exist-core/src/main/java/org/exist/test/runner/XMLTestRunner.java b/exist-core/src/test/java/org/exist/test/runner/XMLTestRunner.java similarity index 78% rename from exist-core/src/main/java/org/exist/test/runner/XMLTestRunner.java rename to exist-core/src/test/java/org/exist/test/runner/XMLTestRunner.java index 5496faba086..c07988c5f18 100644 --- a/exist-core/src/main/java/org/exist/test/runner/XMLTestRunner.java +++ b/exist-core/src/test/java/org/exist/test/runner/XMLTestRunner.java @@ -38,9 +38,6 @@ import org.exist.xquery.XQueryContext; import org.exist.xquery.value.FunctionReference; import org.exist.xquery.value.Sequence; -import org.junit.runner.Description; -import org.junit.runner.notification.RunNotifier; -import org.junit.runners.model.InitializationError; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; @@ -80,27 +77,27 @@ public class XMLTestRunner extends AbstractTestRunner { /** * @param path The path to the XML file containing the tests. * @param parallel whether the tests should be run in parallel. - * @throws InitializationError if the test runner could not be constructed. + * @throws TestInitializationException if the test runner could not be constructed. */ - XMLTestRunner(final Path path, final boolean parallel) throws InitializationError { - super(path, parallel); + XMLTestRunner(final Path path) throws TestInitializationException { + super(path); try { this.doc = parse(path); } catch (final ParserConfigurationException | IOException | SAXException e) { - throw new InitializationError(e); + throw new TestInitializationException(e); } this.info = extractTestInfo(path, doc); } - private static XMLTestInfo extractTestInfo(final Path path, final Document doc) throws InitializationError { + private static XMLTestInfo extractTestInfo(final Path path, final Document doc) throws TestInitializationException { String testSetName = null; String description = null; final List testNames = new ArrayList<>(); final Element docElement = doc.getDocumentElement(); if(docElement == null) { - throw new InitializationError("Invalid XML test document: " + path.toAbsolutePath()); + throw new TestInitializationException("Invalid XML test document: " + path.toAbsolutePath()); } final NodeList children = docElement.getChildNodes(); @@ -121,7 +118,7 @@ private static XMLTestInfo extractTestInfo(final Path path, final Document doc) testName = getTaskText(child); } if (testName == null) { - throw new InitializationError("Could not find @id or within of XML document:" + path.toAbsolutePath()); + throw new TestInitializationException("Could not find @id or within of XML document:" + path.toAbsolutePath()); } testNames.add(testName); break; @@ -134,15 +131,16 @@ private static XMLTestInfo extractTestInfo(final Path path, final Document doc) } if (testSetName == null) { - throw new InitializationError("Could not find in XML document: " + path.toAbsolutePath()); + throw new TestInitializationException("Could not find in XML document: " + path.toAbsolutePath()); } return new XMLTestInfo(testSetName, description, testNames); } private static @Nullable String getIdValue(final Node test) { + // as the runtime does: ($test/@id[. ne ""], $test/task)[1] final String id = ((Element)test).getAttribute("id"); - return id.isBlank() ? null : id; + return id.isEmpty() ? null : id; } private static @Nullable String getTaskText(final Node test) { @@ -150,12 +148,11 @@ private static XMLTestInfo extractTestInfo(final Path path, final Document doc) for (int j = 0; j < testChildren.getLength(); j++) { final Node testChild = testChildren.item(j); if (testChild.getNodeType() == Node.ELEMENT_NODE && testChild.getNamespaceURI() == null && "task".equals(testChild.getLocalName())) { - String textContent = testChild.getTextContent(); - if (textContent != null) { - textContent = textContent.trim(); - if (!textContent.isEmpty()) { - return textContent; - } + // the runtime reports the raw text of , including any surrounding whitespace, + // so the name must not be trimmed or it would not match the reported test + final String textContent = testChild.getTextContent(); + if (textContent != null && !textContent.isBlank()) { + return textContent; } return null; } @@ -163,22 +160,18 @@ private static XMLTestInfo extractTestInfo(final Path path, final Document doc) return null; } - private String getSuiteName() { + @Override + public String getSuiteName() { return "xmlts." + info.name(); } @Override - public Description getDescription() { - final String suiteName = checkDescription(info, getSuiteName()); - final Description description = Description.createSuiteDescription(suiteName); - for (final String childName : info.childNames()) { - description.addChild(Description.createTestDescription(suiteName, checkDescription(info, childName))); - } - return description; + public List getTestNames() { + return List.copyOf(info.childNames()); } @Override - public void run(final RunNotifier notifier) { + public void run(final TestEvents events, final BrokerPool brokerPool) { try { final String pkgName = getClass().getPackage().getName().replace('.', '/'); final Source query = new ClassLoaderSource(pkgName + "/xml-test-runner.xq"); @@ -188,19 +181,15 @@ public void run(final RunNotifier notifier) { context -> new Tuple2<>("id", Sequence.EMPTY_SEQUENCE), // set callback functions for notifying junit! - context -> new Tuple2<>("test-ignored-function", new FunctionReference(new FunctionCall(context, new ExtTestIgnoredFunction(context, getSuiteName(), notifier)))), - context -> new Tuple2<>("test-started-function", new FunctionReference(new FunctionCall(context, new ExtTestStartedFunction(context, getSuiteName(), notifier)))), - context -> new Tuple2<>("test-failure-function", new FunctionReference(new FunctionCall(context, new ExtTestFailureFunction(context, getSuiteName(), notifier, path)))), - context -> new Tuple2<>("test-assumption-failed-function", new FunctionReference(new FunctionCall(context, new ExtTestAssumptionFailedFunction(context, getSuiteName(), notifier)))), - context -> new Tuple2<>("test-error-function", new FunctionReference(new FunctionCall(context, new ExtTestErrorFunction(context, getSuiteName(), notifier)))), - context -> new Tuple2<>("test-finished-function", new FunctionReference(new FunctionCall(context, new ExtTestFinishedFunction(context, getSuiteName(), notifier)))) + context -> new Tuple2<>("test-ignored-function", new FunctionReference(new FunctionCall(context, new ExtTestIgnoredFunction(context, getSuiteName(), events)))), + context -> new Tuple2<>("test-started-function", new FunctionReference(new FunctionCall(context, new ExtTestStartedFunction(context, getSuiteName(), events)))), + context -> new Tuple2<>("test-failure-function", new FunctionReference(new FunctionCall(context, new ExtTestFailureFunction(context, getSuiteName(), events, path)))), + context -> new Tuple2<>("test-assumption-failed-function", new FunctionReference(new FunctionCall(context, new ExtTestAssumptionFailedFunction(context, getSuiteName(), events)))), + context -> new Tuple2<>("test-error-function", new FunctionReference(new FunctionCall(context, new ExtTestErrorFunction(context, getSuiteName(), events)))), + context -> new Tuple2<>("test-finished-function", new FunctionReference(new FunctionCall(context, new ExtTestFinishedFunction(context, getSuiteName(), events)))) ); - // NOTE: at this stage EXIST_EMBEDDED_SERVER_CLASS_INSTANCE in XSuite will be usable - final BrokerPool brokerPool = XSuite.EXIST_EMBEDDED_SERVER_CLASS_INSTANCE.getBrokerPool(); executeQuery(brokerPool, query, externalVariableDeclarations); - - } catch(final DatabaseConfigurationException | IOException | EXistException | PermissionDeniedException | XPathException e) { //TODO(AR) what to do here? throw new RuntimeException(e); diff --git a/exist-core/src/test/java/org/exist/test/runner/XMLTestRunnerTest.java b/exist-core/src/test/java/org/exist/test/runner/XMLTestRunnerTest.java index 70970ebcb79..c85beb3e1df 100644 --- a/exist-core/src/test/java/org/exist/test/runner/XMLTestRunnerTest.java +++ b/exist-core/src/test/java/org/exist/test/runner/XMLTestRunnerTest.java @@ -19,67 +19,57 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - package org.exist.test.runner; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -import org.junit.runner.Description; -import org.junit.runners.model.InitializationError; import java.io.IOException; import java.nio.file.Path; -import java.util.ArrayList; import java.util.List; import static java.nio.file.Files.write; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; /** * Tests the global methods of the {@link XMLTestRunner}. */ class XMLTestRunnerTest { + @TempDir Path tempPath; - XMLTestRunner runner; - @BeforeEach - void prepare() throws InitializationError, IOException { + private XMLTestRunner runnerFor(final String... lines) throws TestInitializationException, IOException { final Path xmlTestFile = tempPath.resolve("test.xml"); - final List lines = new ArrayList<>(); - lines.add(""); - lines.add(" demoTest"); - lines.add(" description for the demo test"); - lines.add(" "); - lines.add(" "); - lines.add(" taskName"); - lines.add(" "); - lines.add(""); - write(xmlTestFile, lines); - runner = new XMLTestRunner(xmlTestFile, false); + write(xmlTestFile, List.of(lines)); + return new XMLTestRunner(xmlTestFile); } @Test - void testGetDescription() { - final Description description = runner.getDescription(); - assertNotNull(description); - assertTrue(description.isSuite()); - assertTrue(description.getAnnotations().isEmpty()); - assertEquals("xmlts.demoTest", description.getDisplayName()); - assertEquals(2, description.testCount()); - final ArrayList children = description.getChildren(); - assertChild(children.getFirst(), "testId(xmlts.demoTest)"); - assertChild(children.get(1), "taskName(xmlts.demoTest)"); + void suiteAndTestNamesComeFromTheTestSet() throws TestInitializationException, IOException { + final XMLTestRunner runner = runnerFor( + "", + " demoTest", + " description for the demo test", + " ", + " ", + " taskName", + " ", + ""); + + assertEquals("xmlts.demoTest", runner.getSuiteName()); + assertEquals(List.of("testId", "taskName"), runner.getTestNames()); } - private void assertChild(final Description description, final String expectedDisplayName) { - assertFalse(description.isSuite()); - assertTrue(description.getAnnotations().isEmpty()); - assertEquals(expectedDisplayName, description.getDisplayName()); - assertEquals(1, description.testCount()); + @Test + void taskNameIsTheRawTextBecauseThatIsWhatTheRuntimeReports() throws TestInitializationException, IOException { + final XMLTestRunner runner = runnerFor( + "", + " demoTest", + " trailing space ", + ""); + + assertEquals(List.of("trailing space "), runner.getTestNames()); } } diff --git a/exist-core/src/main/java/org/exist/test/runner/XQueryFailureLog.java b/exist-core/src/test/java/org/exist/test/runner/XQueryFailureLog.java similarity index 100% rename from exist-core/src/main/java/org/exist/test/runner/XQueryFailureLog.java rename to exist-core/src/test/java/org/exist/test/runner/XQueryFailureLog.java diff --git a/exist-core/src/main/java/org/exist/test/runner/XQueryTestRunner.java b/exist-core/src/test/java/org/exist/test/runner/XQueryTestRunner.java similarity index 79% rename from exist-core/src/main/java/org/exist/test/runner/XQueryTestRunner.java rename to exist-core/src/test/java/org/exist/test/runner/XQueryTestRunner.java index 1044aebfb7f..f764389bd8a 100644 --- a/exist-core/src/main/java/org/exist/test/runner/XQueryTestRunner.java +++ b/exist-core/src/test/java/org/exist/test/runner/XQueryTestRunner.java @@ -42,12 +42,9 @@ import org.exist.xquery.value.FunctionReference; import org.exist.xquery.value.NodeValue; import org.exist.xquery.value.Sequence; -import org.junit.runner.Description; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; -import org.junit.runner.notification.RunNotifier; -import org.junit.runners.model.InitializationError; import javax.annotation.Nullable; import java.io.IOException; @@ -60,8 +57,8 @@ import static java.nio.charset.StandardCharsets.UTF_8; /** - * A JUnit test runner which can run the XQuery tests (XQSuite) - * of eXist-db using $EXIST_HOME/src/org/exist/xquery/lib/xqsuite/xqsuite.xql. + * Runs the XQuery tests (XQSuite) of a file using $EXIST_HOME/src/org/exist/xquery/lib/xqsuite/xqsuite.xql, + * reporting each outcome to a {@link TestEvents}. * * @author Adam Retter */ @@ -73,39 +70,60 @@ public class XQueryTestRunner extends AbstractTestRunner { /** * @param path The path to the XQuery file containing the XQSuite tests - * @param parallel whether the tests should be run in parallel. + * @param discoveryPool a running database to discover the tests with, or null to discover by compiling the module * - * @throws InitializationError if the test runner could not be constructed. + * @throws TestInitializationException if the test runner could not be constructed. */ - public XQueryTestRunner(final Path path, final boolean parallel) throws InitializationError { - super(path, parallel); - this.info = discoverOrExtractTestInfo(path); + public XQueryTestRunner(final Path path, @Nullable final BrokerPool discoveryPool) throws TestInitializationException { + super(path); + this.info = discoverOrExtractTestInfo(path, discoveryPool); } /** * Obtain test metadata by discovery when possible, otherwise by compiling the module. - * When the DB is already started (e.g. by XSuite), try runDiscovery first so we run a single + * When a database is available, try runDiscovery first so we run a single * discovery XQuery instead of compiling the module twice. Fall back to extractTestInfo in two * cases: (1) the DB is not started, or (2) the DB is started but runDiscovery returns null * (e.g. discovery failed, empty result, or wrong XML shape). * * @param path the path to the XQuery file containing the XQSuite tests * @return test info (from discovery or from compiling the module) - * @throws InitializationError if the runner could not be constructed + * @throws TestInitializationException if the runner could not be constructed */ - private static XQueryTestInfo discoverOrExtractTestInfo(final Path path) throws InitializationError { - if (XSuite.EXIST_EMBEDDED_SERVER_CLASS_INSTANCE != null) { - final BrokerPool pool = XSuite.EXIST_EMBEDDED_SERVER_CLASS_INSTANCE.getBrokerPool(); - if (pool != null) { - final XQueryTestInfo discovered = runDiscovery(pool, path); - if (discovered != null) { - return discovered; - } + private static XQueryTestInfo discoverOrExtractTestInfo(final Path path, @Nullable final BrokerPool pool) throws TestInitializationException { + if (pool != null) { + final XQueryTestInfo discovered = runDiscovery(pool, path); + if (discovered != null) { + return discovered; } } return extractTestInfo(path); } + /** + * The name under which the XQSuite runtime reports a test that has no explicit {@code %test:name} + * (see {@code test:get-test-name} in xqsuite.xql): the lexical name of the function, with a prefix + * dropped only if it consists solely of word characters. XPath's {@code \w} excludes punctuation, + * so a prefix such as {@code my-tests} is kept. + */ + static String runtimeTestName(final String prefix, final String localName) { + if (prefix == null || prefix.isEmpty() || prefix.codePoints().allMatch(XQueryTestRunner::isXPathWordChar)) { + return localName; + } + return prefix + ":" + localName; + } + + private static boolean isXPathWordChar(final int codePoint) { + return switch (Character.getType(codePoint)) { + case Character.CONNECTOR_PUNCTUATION, Character.DASH_PUNCTUATION, Character.START_PUNCTUATION, + Character.END_PUNCTUATION, Character.INITIAL_QUOTE_PUNCTUATION, Character.FINAL_QUOTE_PUNCTUATION, + Character.OTHER_PUNCTUATION, Character.SPACE_SEPARATOR, Character.LINE_SEPARATOR, + Character.PARAGRAPH_SEPARATOR, Character.CONTROL, Character.FORMAT, Character.SURROGATE, + Character.PRIVATE_USE, Character.UNASSIGNED -> false; + default -> true; + }; + } + private static Configuration getConfiguration() throws DatabaseConfigurationException { final Optional home = Optional.ofNullable(System.getProperty("exist.home", System.getProperty("user.dir"))).map(Path::of); final Path confFile = ConfigurationHelper.lookup("conf.xml", home); @@ -117,7 +135,7 @@ private static Configuration getConfiguration() throws DatabaseConfigurationExce } } - private static XQueryTestInfo extractTestInfo(final Path path) throws InitializationError { + private static XQueryTestInfo extractTestInfo(final Path path) throws TestInitializationException { try { final Configuration config = getConfiguration(); @@ -126,7 +144,7 @@ private static XQueryTestInfo extractTestInfo(final Path path) throws Initializa expathRepo.configure(config); expathRepo.prepare(null); } catch (final BrokerPoolServiceException e) { - throw new InitializationError(e); + throw new TestInitializationException(e); } final XQueryContext xqueryContext = new XQueryContext(config); @@ -176,7 +194,7 @@ private static XQueryTestInfo extractTestInfo(final Path path) throws Initializa if (isTest) { if (testName == null) { - testName = localFunctionSignature.getName().getLocalPart(); + testName = runtimeTestName(localFunctionSignature.getName().getPrefix(), localFunctionSignature.getName().getLocalPart()); testArity = localFunctionSignature.getArgumentCount(); } @@ -198,7 +216,7 @@ private static XQueryTestInfo extractTestInfo(final Path path) throws Initializa } } catch (final DatabaseConfigurationException | IOException | PermissionDeniedException | XPathException e) { - throw new InitializationError(e); + throw new TestInitializationException(e); } } @@ -247,7 +265,8 @@ static XQueryTestInfo runDiscovery(final BrokerPool brokerPool, final Path path) } } - private String getSuiteName() { + @Override + public String getSuiteName() { if (info.namespace() == null) { return path.getFileName().toString(); } @@ -292,40 +311,30 @@ private void pathToPackageName(String path, final StringBuilder buffer) { } @Override - public Description getDescription() { - final String suiteName = checkDescription(this, getSuiteName()); - final Description description = Description.createSuiteDescription(suiteName); - for (final XQueryTestInfo.TestFunctionDef testFunctionDef : info.testFunctions()) { - description.addChild(Description.createTestDescription(suiteName, checkDescription(testFunctionDef, testFunctionDef.localName()))); - } - return description; + public List getTestNames() { + return info.testFunctions().stream().map(XQueryTestInfo.TestFunctionDef::localName).toList(); } @Override - public void run(final RunNotifier notifier) { + public void run(final TestEvents events, final BrokerPool brokerPool) { try { final String pkgName = getClass().getPackage().getName().replace('.', '/'); final Source query = new ClassLoaderSource(pkgName + "/xquery-test-runner.xq"); final URI testModuleUri = path.toAbsolutePath().toUri(); - final String suiteName = getSuiteName(); - final List>> externalVariableDeclarations = Arrays.asList( context -> new Tuple2<>("test-module-uri", new AnyURIValue(testModuleUri)), - // set callback functions for notifying junit! - context -> new Tuple2<>("test-ignored-function", new FunctionReference(new FunctionCall(context, new ExtTestIgnoredFunction(context, suiteName, notifier)))), - context -> new Tuple2<>("test-started-function", new FunctionReference(new FunctionCall(context, new ExtTestStartedFunction(context, suiteName, notifier)))), - context -> new Tuple2<>("test-failure-function", new FunctionReference(new FunctionCall(context, new ExtTestFailureFunction(context, suiteName, notifier, path)))), - context -> new Tuple2<>("test-assumption-failed-function", new FunctionReference(new FunctionCall(context, new ExtTestAssumptionFailedFunction(context, suiteName, notifier)))), - context -> new Tuple2<>("test-error-function", new FunctionReference(new FunctionCall(context, new ExtTestErrorFunction(context, suiteName, notifier)))), - context -> new Tuple2<>("test-finished-function", new FunctionReference(new FunctionCall(context, new ExtTestFinishedFunction(context, suiteName, notifier)))) + // set callback functions for reporting test outcomes! + context -> new Tuple2<>("test-ignored-function", new FunctionReference(new FunctionCall(context, new ExtTestIgnoredFunction(context, getSuiteName(), events)))), + context -> new Tuple2<>("test-started-function", new FunctionReference(new FunctionCall(context, new ExtTestStartedFunction(context, getSuiteName(), events)))), + context -> new Tuple2<>("test-failure-function", new FunctionReference(new FunctionCall(context, new ExtTestFailureFunction(context, getSuiteName(), events, path)))), + context -> new Tuple2<>("test-assumption-failed-function", new FunctionReference(new FunctionCall(context, new ExtTestAssumptionFailedFunction(context, getSuiteName(), events)))), + context -> new Tuple2<>("test-error-function", new FunctionReference(new FunctionCall(context, new ExtTestErrorFunction(context, getSuiteName(), events)))), + context -> new Tuple2<>("test-finished-function", new FunctionReference(new FunctionCall(context, new ExtTestFinishedFunction(context, getSuiteName(), events)))) ); - // NOTE: at this stage EXIST_EMBEDDED_SERVER_CLASS_INSTANCE in XSuite will be usable - final BrokerPool brokerPool = XSuite.EXIST_EMBEDDED_SERVER_CLASS_INSTANCE.getBrokerPool(); executeQuery(brokerPool, query, externalVariableDeclarations); - } catch(final DatabaseConfigurationException | IOException | EXistException | PermissionDeniedException | XPathException e) { //TODO(AR) what to do here? throw new RuntimeException(e); diff --git a/exist-core/src/test/java/org/exist/test/runner/XQueryTestRunnerTest.java b/exist-core/src/test/java/org/exist/test/runner/XQueryTestRunnerTest.java index 4ab90071ab3..759ffdb0e1c 100644 --- a/exist-core/src/test/java/org/exist/test/runner/XQueryTestRunnerTest.java +++ b/exist-core/src/test/java/org/exist/test/runner/XQueryTestRunnerTest.java @@ -19,47 +19,55 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - package org.exist.test.runner; import org.junit.jupiter.api.Test; -import org.junit.runner.Description; -import org.junit.runners.model.InitializationError; import java.net.URISyntaxException; import java.net.URL; import java.nio.file.Path; +import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** * Tests the global methods of the {@link XQueryTestRunner}. */ class XQueryTestRunnerTest { + + private XQueryTestRunner runnerFor(final String resource) throws URISyntaxException, TestInitializationException { + final URL queryUrl = getClass().getResource(resource); + return new XQueryTestRunner(Path.of(queryUrl.toURI()), null); + } + + @Test + void suiteNameIsDerivedFromTheModuleNamespace() throws URISyntaxException, TestInitializationException { + final XQueryTestRunner runner = runnerFor("single-test.xqm"); + assertEquals("xqts.org.exist-db.xquery.single-test-module", runner.getSuiteName()); + assertEquals(List.of("f1"), runner.getTestNames()); + } + + @Test + void fileWithoutTestsIsNamedAfterTheFileAndHasNoTests() throws URISyntaxException, TestInitializationException { + final XQueryTestRunner runner = runnerFor("no-tests.xqm"); + assertEquals("no-tests.xqm", runner.getSuiteName()); + assertTrue(runner.getTestNames().isEmpty()); + } + @Test - void testGetDescription() throws URISyntaxException, InitializationError { - final URL queryUrl = getClass().getResource("single-test.xqm"); - final XQueryTestRunner runner = new XQueryTestRunner(Path.of(queryUrl.toURI()), false); - final Description description = runner.getDescription(); - assertNotNull(description); - assertTrue(description.isSuite()); - assertTrue(description.getAnnotations().isEmpty()); - assertEquals("xqts.org.exist-db.xquery.single-test-module", description.getDisplayName()); - assertEquals(1, description.testCount()); + void testNamesFollowTheNamesTheXQSuiteRuntimeReports() throws URISyntaxException, TestInitializationException { + // a prefix with a hyphen is kept, an explicit %test:name is used as is + assertEquals(List.of("my-tests:f1", "explicitly named"), runnerFor("hyphenated-prefix.xqm").getTestNames()); } @Test - void testGetDescriptionWhenNoTests() throws URISyntaxException, InitializationError { - final URL queryUrl = getClass().getResource("no-tests.xqm"); - final XQueryTestRunner runner = new XQueryTestRunner(Path.of(queryUrl.toURI()), false); - final Description description = runner.getDescription(); - assertNotNull(description); - assertFalse(description.isSuite()); - assertTrue(description.getAnnotations().isEmpty()); - assertEquals("no-tests.xqm", description.getDisplayName()); - assertEquals(1, description.testCount()); + void runtimeTestNameDropsOnlyAPrefixOfWordCharacters() { + assertEquals("f1", XQueryTestRunner.runtimeTestName("single", "f1")); + assertEquals("f1", XQueryTestRunner.runtimeTestName("t1", "f1")); + assertEquals("f1", XQueryTestRunner.runtimeTestName("", "f1")); + assertEquals("my-tests:f1", XQueryTestRunner.runtimeTestName("my-tests", "f1")); + // underscore is punctuation in XPath regular expressions, so it is not a word character + assertEquals("my_tests:f1", XQueryTestRunner.runtimeTestName("my_tests", "f1")); } } diff --git a/exist-core/src/test/java/org/exist/test/runner/XSuiteDebuggabilityTest.java b/exist-core/src/test/java/org/exist/test/runner/XSuiteDebuggabilityTest.java deleted file mode 100644 index 6b7062197d5..00000000000 --- a/exist-core/src/test/java/org/exist/test/runner/XSuiteDebuggabilityTest.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * eXist-db Open Source Native XML Database - * Copyright (C) 2001 The eXist-db Authors - * - * info@exist-db.org - * http://www.exist-db.org - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.exist.test.runner; - -import org.junit.jupiter.api.Test; -import org.junit.runner.JUnitCore; -import org.junit.runner.Result; -import org.junit.runner.notification.Failure; -import org.junit.runner.notification.RunListener; -import org.opentest4j.AssertionFailedError; - -import java.util.ArrayList; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** - * TDD tests for XSuite debuggability: failure stack traces should support - * "navigate to XQuery" (test file:line) and "navigate to Java" (underlying Java code). - */ -class XSuiteDebuggabilityTest { - - @Test - void failureFromAssertionIncludesTestFileInStackTraceOrMessage() { - final List failures = runSuiteAndCollectFailures(); - final Failure assertionFailure = failures.stream() - .filter(f -> f.getException() instanceof AssertionFailedError) - .findFirst() - .orElse(null); - assertNotNull(assertionFailure, "expected one assertion failure (ComparisonFailure) from failing-both.xqm; collected failures: " + failures.size() - + " types: " + failures.stream().map(f -> f.getException() == null ? "null" : f.getException().getClass().getName()).toList()); - - final Throwable t = assertionFailure.getException(); - final String message = t.getMessage(); - final StackTraceElement[] stack = t.getStackTrace(); - - boolean hasLocation = false; - if (stack != null && stack.length > 0) { - for (final StackTraceElement e : stack) { - final String file = e.getFileName(); - if (file != null && file.contains("failing-both")) { - hasLocation = true; - break; - } - } - } - if (!hasLocation && message != null) { - hasLocation = message.contains("failing-both"); - } - assertTrue(hasLocation, - "assertion failure should include test file (failing-both.xqm) in stack trace or message for IDE navigation (navigate to XQuery); stack.length=" + (stack != null ? stack.length : 0) + " message=" + message); - } - - @Test - void failureFromErrorIncludesOrgExistInStackTrace() { - final List failures = runSuiteAndCollectFailures(); - final Failure errorFailure = failures.stream() - .filter(f -> f.getException() instanceof org.exist.xquery.XPathException) - .findFirst() - .orElse(null); - assertNotNull(errorFailure, "expected one error failure (XPathException) from failing-both.xqm throwsUnexpectedError; xqsuite runs only functions with assert* annotations"); - final Throwable t = errorFailure.getException(); - final StackTraceElement[] stack = t.getStackTrace(); - - boolean hasOrgExist = false; - if (stack != null) { - for (final StackTraceElement e : stack) { - if (e.getClassName() != null && e.getClassName().startsWith("org.exist")) { - hasOrgExist = true; - break; - } - } - } - assertTrue(hasOrgExist, - "error failure should include org.exist in stack trace for IDE navigation (navigate to Java); stack.length=" + (stack != null ? stack.length : 0)); - } - - private static List runSuiteAndCollectFailures() { - final List collected = new ArrayList<>(); - final JUnitCore core = new JUnitCore(); - core.addListener(new RunListener() { - @Override - public void testFailure(final Failure failure) { - collected.add(failure); - } - }); - final Result result = core.run(DebuggabilityNavigabilitySuite.class); - assertFalse(result.wasSuccessful(), "suite is expected to have failures (failing-both.xqm)"); - assertTrue(collected.size() >= 2, - "expected at least 2 failures (assertion + error) from failing-both.xqm; got " + collected.size() - + " types: " + collected.stream().map(f -> f.getException() == null ? "null" : f.getException().getClass().getName()).toList()); - return collected; - } -} diff --git a/exist-core/src/test/java/org/exist/test/runner/XSuiteFailureMessageTest.java b/exist-core/src/test/java/org/exist/test/runner/XSuiteFailureMessageTest.java deleted file mode 100644 index e3f4808a7b2..00000000000 --- a/exist-core/src/test/java/org/exist/test/runner/XSuiteFailureMessageTest.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * eXist-db Open Source Native XML Database - * Copyright (C) 2001 The eXist-db Authors - * - * info@exist-db.org - * http://www.exist-db.org - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.exist.test.runner; - -import org.junit.jupiter.api.Test; -import org.junit.runner.JUnitCore; -import org.junit.runner.Result; -import org.junit.runner.notification.Failure; -import org.junit.runner.notification.RunListener; -import org.opentest4j.AssertionFailedError; - -import java.util.ArrayList; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -/** - * The XQSuite layer hands {@link ExtTestFailureFunction} values it has already serialized to - * strings. Serializing them a second time on the Java side escaped their markup, so a test whose - * result was {@code text} reported it as {@code <doc a="1">text</doc>} - * and a string result containing markup was escaped twice over. - */ -class XSuiteFailureMessageTest { - - @Test - void nodeResultIsNotEscapedIntoTheFailureMessage() { - final AssertionFailedError failure = runSuiteAndGetComparisonFailure(); - - assertEquals("text", failure.getActual().getValue(), - "a node-valued result should reach the failure message as markup, not XML-escaped"); - } - - private static AssertionFailedError runSuiteAndGetComparisonFailure() { - final List collected = new ArrayList<>(); - final JUnitCore core = new JUnitCore(); - core.addListener(new RunListener() { - @Override - public void testFailure(final Failure failure) { - collected.add(failure); - } - }); - - final Result result = core.run(SerializationFailureMessageSuite.class); - assertFalse(result.wasSuccessful(), "suite is expected to fail (failing-serialization.xqm)"); - - final AssertionFailedError comparisonFailure = collected.stream() - .map(Failure::getException) - .filter(AssertionFailedError.class::isInstance) - .map(AssertionFailedError.class::cast) - .findFirst() - .orElse(null); - assertNotNull(comparisonFailure, - "expected a ComparisonFailure from failing-serialization.xqm; collected: " + collected.size()); - - return comparisonFailure; - } -} diff --git a/exist-core/src/test/java/org/exist/test/runner/XSuiteParallelTest.java b/exist-core/src/test/java/org/exist/test/runner/XSuiteParallelTest.java deleted file mode 100644 index d378641e864..00000000000 --- a/exist-core/src/test/java/org/exist/test/runner/XSuiteParallelTest.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * eXist-db Open Source Native XML Database - * Copyright (C) 2001 The eXist-db Authors - * - * info@exist-db.org - * http://www.exist-db.org - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.exist.test.runner; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Test; -import org.junit.runner.JUnitCore; -import org.junit.runner.Result; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** - * Tests for XSuite parallel scheduler: dynamic pool size and progress-based hang detection. - */ -class XSuiteParallelTest { - - @AfterEach - void clearSystemProperties() { - System.clearProperty("xsuite.parallel.threads"); - System.clearProperty("xsuite.hang.threshold.minutes"); - System.clearProperty("xsuite.hang.watcher.interval.seconds"); - } - - @Test - void parallelSuiteRunsAndCompletesSuccessfully() { - final Result result = runSuite(ParallelPassingSuite.class); - assertTrue(result.wasSuccessful(), - "parallel suite with passing files should succeed; failures: " + result.getFailureCount()); - } - - @Test - void parallelSuiteWithThreadOverrideCompletes() { - System.setProperty("xsuite.parallel.threads", "2"); - final Result result = runSuite(ParallelPassingSuite.class); - assertTrue(result.wasSuccessful(), - "parallel suite with xsuite.parallel.threads=2 should succeed; failures: " + result.getFailureCount()); - } - - /** - * Parallel suite with two files (one passing, one that would hang if module vars were evaluated at load). - * Runs to verify the parallel path with multiple files and that hang-detection properties are read. - * Full "appears hung" behaviour is not asserted here because eXist may not evaluate library module - * variables when inspect:module-functions loads the module, so hanging.xqm can complete without hanging. - */ - @Test - void parallelSuiteWithTwoFilesCompletes() { - System.setProperty("xsuite.hang.threshold.minutes", "0.05"); - System.setProperty("xsuite.hang.watcher.interval.seconds", "1"); - final Result result = runSuite(ParallelWithHungSuite.class); - assertTrue(result.getRunCount() > 0, "suite should run and complete"); - } - - private static Result runSuite(final Class suiteClass) { - return new JUnitCore().run(suiteClass); - } -} diff --git a/exist-core/src/test/java/org/exist/test/xqsuite/EngineTestEvents.java b/exist-core/src/test/java/org/exist/test/xqsuite/EngineTestEvents.java new file mode 100644 index 00000000000..4f6874633c0 --- /dev/null +++ b/exist-core/src/test/java/org/exist/test/xqsuite/EngineTestEvents.java @@ -0,0 +1,191 @@ +/* + * eXist-db Open Source Native XML Database + * Copyright (C) 2001 The eXist-db Authors + * + * info@exist-db.org + * http://www.exist-db.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.exist.test.xqsuite; + +import org.exist.test.runner.TestEvents; +import org.opentest4j.TestAbortedException; +import org.junit.platform.engine.EngineExecutionListener; +import org.junit.platform.engine.TestExecutionResult; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Translates the outcome events of one test file into JUnit Platform execution events. + *

+ * The Platform allows exactly one result per test, whereas the XQuery side may report + * a failure before it reports that the test finished, so the result is held back + * until {@link #finished(String)}. + */ +final class EngineTestEvents implements TestEvents { + private final FileDescriptor file; + private final EngineExecutionListener listener; + /** called whenever the XQuery side reports something, to show that the file is still making progress */ + private final Runnable activity; + /** once the file is complete or abandoned, nothing more is reported, even if its thread is still running */ + private boolean closed = false; + /** how many tests of each name have been started, to match repeated names to occurrences in order */ + private final Map startedCount = new HashMap<>(); + /** the test of each name that is currently running */ + private final Map current = new HashMap<>(); + private final Map pending = new HashMap<>(); + private final Set started = new HashSet<>(); + private final Set done = new HashSet<>(); + + EngineTestEvents(final FileDescriptor file, final EngineExecutionListener listener, final Runnable activity) { + this.file = file; + this.listener = listener; + this.activity = activity; + } + + /** + * @return the names of the tests that have started but not finished + */ + synchronized List runningTests() { + return started.stream().filter(t -> !done.contains(t)).map(XQTestDescriptor::getDisplayName).sorted().toList(); + } + + /** + * Starts the next not yet started test of the given name. + */ + private XQTestDescriptor start(final String testName) { + final int index = startedCount.merge(testName, 1, Integer::sum) - 1; + final List occurrences = file.findTests(testName); + final XQTestDescriptor test; + if (index < occurrences.size()) { + test = occurrences.get(index); + } else { + // reported by the XQuery side but not found by discovery + test = file.addTest(testName); + listener.dynamicTestRegistered(test); + } + current.put(testName, test); + started.add(test); + listener.executionStarted(test); + return test; + } + + /** + * @return the running test of the given name, starting one if the XQuery side reports on a test it never started + */ + private XQTestDescriptor running(final String testName) { + final XQTestDescriptor test = current.get(testName); + return test != null && !done.contains(test) ? test : start(testName); + } + + @Override + public synchronized void started(final String testName) { + if (closed) { + return; + } + activity.run(); + start(testName); + } + + @Override + public synchronized void finished(final String testName) { + if (closed) { + return; + } + activity.run(); + final XQTestDescriptor test = running(testName); + done.add(test); + final TestExecutionResult result = pending.remove(test); + listener.executionFinished(test, result != null ? result : TestExecutionResult.successful()); + } + + @Override + public synchronized void ignored(final String testName) { + if (closed) { + return; + } + activity.run(); + final int index = startedCount.merge(testName, 1, Integer::sum) - 1; + final List occurrences = file.findTests(testName); + final XQTestDescriptor test = index < occurrences.size() ? occurrences.get(index) : file.addTest(testName); + if (index >= occurrences.size()) { + listener.dynamicTestRegistered(test); + } + started.add(test); + done.add(test); + listener.executionSkipped(test, "Ignored"); + } + + @Override + public synchronized void failed(final String testName, final Throwable reason) { + if (closed) { + return; + } + activity.run(); + final XQTestDescriptor test = running(testName); + final TestExecutionResult previous = pending.get(test); + if (previous != null && previous.getStatus() == TestExecutionResult.Status.FAILED && previous.getThrowable().isPresent()) { + previous.getThrowable().get().addSuppressed(reason); + } else { + pending.put(test, TestExecutionResult.failed(reason)); + } + } + + @Override + public synchronized void assumptionFailed(final String testName, final String message) { + if (closed) { + return; + } + activity.run(); + pending.putIfAbsent(running(testName), TestExecutionResult.aborted(new TestAbortedException(message))); + } + + /** + * Completes every test of the file that the XQuery side did not finish, so that none is silently lost. + * + * @param fileFailure the reason the whole file failed to run, or null + */ + private static String reasonSuffix(final Throwable fileFailure) { + return fileFailure != null && fileFailure.getMessage() != null ? ": " + fileFailure.getMessage() : ""; + } + + synchronized void completeOutstanding(final Throwable fileFailure) { + if (closed) { + return; + } + closed = true; + for (final XQTestDescriptor test : file.tests()) { + if (done.contains(test)) { + continue; + } + done.add(test); + if (started.contains(test)) { + final TestExecutionResult result = pending.remove(test); + listener.executionFinished(test, result != null ? result : TestExecutionResult.failed( + new AssertionError("Test started but did not finish" + reasonSuffix(fileFailure), fileFailure))); + } else { + started.add(test); + listener.executionStarted(test); + listener.executionFinished(test, TestExecutionResult.failed(new AssertionError( + "Test was discovered but never reported by the XQuery test runner" + reasonSuffix(fileFailure), fileFailure))); + } + } + } +} diff --git a/exist-core/src/test/java/org/exist/test/xqsuite/FileDescriptor.java b/exist-core/src/test/java/org/exist/test/xqsuite/FileDescriptor.java new file mode 100644 index 00000000000..c1d51d8334e --- /dev/null +++ b/exist-core/src/test/java/org/exist/test/xqsuite/FileDescriptor.java @@ -0,0 +1,81 @@ +/* + * eXist-db Open Source Native XML Database + * Copyright (C) 2001 The eXist-db Authors + * + * info@exist-db.org + * http://www.exist-db.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.exist.test.xqsuite; + +import org.exist.test.runner.AbstractTestRunner; +import org.junit.platform.engine.UniqueId; +import org.junit.platform.engine.support.descriptor.AbstractTestDescriptor; +import org.junit.platform.engine.support.descriptor.FileSource; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * A file of XQSuite or XML tests. + */ +final class FileDescriptor extends AbstractTestDescriptor { + private final AbstractTestRunner runner; + private final Map> tests = new LinkedHashMap<>(); + + FileDescriptor(final UniqueId id, final AbstractTestRunner runner) { + super(id, runner.getSourcePath().getFileName().toString(), FileSource.from(runner.getSourcePath().toFile())); + this.runner = runner; + } + + AbstractTestRunner runner() { + return runner; + } + + /** + * Adds a test. Several test functions in one file may share a name (for example overloads), + * and each is a test in its own right, so a repeated name adds a further occurrence. + */ + synchronized XQTestDescriptor addTest(final String name) { + final List occurrences = tests.computeIfAbsent(name, n -> new ArrayList<>()); + final String segment = occurrences.isEmpty() ? name : name + "#" + (occurrences.size() + 1); + final XQTestDescriptor test = new XQTestDescriptor(getUniqueId().append("test", segment), name, getSource().orElseThrow()); + occurrences.add(test); + addChild(test); + return test; + } + + /** + * @return the tests with the given name, in the order they were added + */ + synchronized List findTests(final String name) { + final List occurrences = tests.get(name); + return occurrences == null ? List.of() : new ArrayList<>(occurrences); + } + + synchronized List tests() { + final List all = new ArrayList<>(); + tests.values().forEach(all::addAll); + return all; + } + + @Override + public Type getType() { + return Type.CONTAINER; + } +} diff --git a/exist-core/src/test/java/org/exist/test/xqsuite/SuiteDescriptor.java b/exist-core/src/test/java/org/exist/test/xqsuite/SuiteDescriptor.java new file mode 100644 index 00000000000..7d7bb12fd55 --- /dev/null +++ b/exist-core/src/test/java/org/exist/test/xqsuite/SuiteDescriptor.java @@ -0,0 +1,69 @@ +/* + * eXist-db Open Source Native XML Database + * Copyright (C) 2001 The eXist-db Authors + * + * info@exist-db.org + * http://www.exist-db.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.exist.test.xqsuite; + +import org.junit.platform.engine.UniqueId; +import org.junit.platform.engine.support.descriptor.AbstractTestDescriptor; +import org.junit.platform.engine.support.descriptor.ClassSource; + +/** + * A class annotated with {@link XQSuite}: one embedded database instance serves all of its files. + */ +final class SuiteDescriptor extends AbstractTestDescriptor { + private final Class suiteClass; + private final Throwable discoveryFailure; + private final boolean parallel; + + SuiteDescriptor(final UniqueId id, final Class suiteClass, final boolean parallel, final Throwable discoveryFailure) { + super(id, suiteClass.getSimpleName(), ClassSource.from(suiteClass)); + this.suiteClass = suiteClass; + this.parallel = parallel; + this.discoveryFailure = discoveryFailure; + } + + Class suiteClass() { + return suiteClass; + } + + /** + * @return true if the files of the suite run concurrently + */ + boolean parallel() { + return parallel; + } + + /** + * @return why the suite's files could not be discovered, or null + */ + Throwable discoveryFailure() { + return discoveryFailure; + } + + /** + * A suite whose files could not be discovered is itself a test, which fails when run, + * so that the problem is visible instead of the suite silently disappearing. + */ + @Override + public Type getType() { + return discoveryFailure != null ? Type.CONTAINER_AND_TEST : Type.CONTAINER; + } +} diff --git a/exist-core/src/test/java/org/exist/test/xqsuite/SuiteRun.java b/exist-core/src/test/java/org/exist/test/xqsuite/SuiteRun.java new file mode 100644 index 00000000000..ff7f06eb96b --- /dev/null +++ b/exist-core/src/test/java/org/exist/test/xqsuite/SuiteRun.java @@ -0,0 +1,218 @@ +/* + * eXist-db Open Source Native XML Database + * Copyright (C) 2001 The eXist-db Authors + * + * info@exist-db.org + * http://www.exist-db.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.exist.test.xqsuite; + +import org.exist.storage.BrokerPool; +import org.exist.xquery.XQueryWatchDog; +import org.junit.platform.engine.EngineExecutionListener; +import org.junit.platform.engine.TestDescriptor; +import org.junit.platform.engine.TestExecutionResult; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Semaphore; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +/** + * Runs the test files of one {@link XQSuite} against a running database, and fails any file that hangs. + *

+ * Every file runs on a thread of its own, so that a file which stops making progress can be given up on + * without taking the whole run down with it. A sequential suite runs one file at a time; a parallel suite + * runs up to {@link XQSuiteSettings#parallelism(int)} files at a time. The tests within a file always run + * one after the other. + *

+ * A watcher thread checks the running files. A file that has reported nothing for longer than the hang + * threshold is abandoned: its running and not yet run tests are failed, the file is finished as + * failed, its XQuery is killed and its thread interrupted, and the remaining files carry on. Whatever the + * abandoned thread reports afterwards is ignored. + *

+ * Giving up on a file is reliable, stopping its thread is not. A thread that is sleeping or blocked ends when it + * is interrupted, and eXist can stop a query at the points where it checks for being killed, but a query that is + * spinning in code that does neither keeps running (which expressions check differs, and queries nested inside the + * test runner do not always see the kill). Such a thread is left behind, and since it can stop the database from + * shutting down the engine bounds that shutdown, see {@link XQSuiteTestEngine}. + */ +final class SuiteRun { + private final SuiteDescriptor suite; + private final EngineExecutionListener listener; + private final BrokerPool brokerPool; + private final XQSuiteSettings settings; + + private final List runs = new CopyOnWriteArrayList<>(); + private final Semaphore permits; + private final Duration hangThreshold; + + SuiteRun(final SuiteDescriptor suite, final EngineExecutionListener listener, final BrokerPool brokerPool, final XQSuiteSettings settings) { + this.suite = suite; + this.listener = listener; + this.brokerPool = brokerPool; + this.settings = settings; + this.permits = new Semaphore(suite.parallel() ? settings.parallelism(brokerPool.getMax()) : 1); + this.hangThreshold = settings.hangThreshold(); + } + + void run() throws InterruptedException { + final AtomicInteger threadNumber = new AtomicInteger(); + final ThreadFactory factory = runnable -> { + final Thread thread = new Thread(runnable, "xqsuite-" + suite.suiteClass().getSimpleName() + "-" + threadNumber.incrementAndGet()); + // a hung file's thread must not keep the JVM alive + thread.setDaemon(true); + return thread; + }; + final ExecutorService threads = Executors.newCachedThreadPool(factory); + final Thread watcher = new Thread(this::watch, "xqsuite-hang-watcher-" + suite.suiteClass().getSimpleName()); + watcher.setDaemon(true); + watcher.start(); + try { + for (final TestDescriptor child : new ArrayList<>(suite.getChildren())) { + permits.acquire(); + final FileRun run = new FileRun((FileDescriptor) child); + runs.add(run); + threads.execute(run); + } + for (final FileRun run : runs) { + run.awaitSettled(); + } + } finally { + watcher.interrupt(); + threads.shutdown(); + awaitAbandonedThreads(); + } + } + + /** + * Give the threads of abandoned files a chance to stop before the database is shut down under them. + */ + private void awaitAbandonedThreads() throws InterruptedException { + final long deadline = System.nanoTime() + settings.hangGrace().toNanos(); + for (final FileRun run : runs) { + if (run.state.get() == State.ABANDONED && run.thread != null) { + final long remainingMillis = Math.max(0, (deadline - System.nanoTime()) / 1_000_000); + run.thread.join(remainingMillis); + } + } + } + + private void watch() { + final long intervalMillis = settings.hangWatcherInterval().toMillis(); + try { + while (!Thread.currentThread().isInterrupted()) { + Thread.sleep(intervalMillis); + final long now = System.nanoTime(); + for (final FileRun run : runs) { + if (run.state.get() == State.RUNNING && now - run.lastActivityNanos > hangThreshold.toNanos()) { + run.abandon(); + } + } + } + } catch (final InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + private enum State { PENDING, RUNNING, DONE, ABANDONED } + + private final class FileRun implements Runnable { + private final FileDescriptor file; + private final EngineTestEvents events; + private final AtomicReference state = new AtomicReference<>(State.PENDING); + private final CountDownLatch settled = new CountDownLatch(1); + private volatile long lastActivityNanos = System.nanoTime(); + private volatile Thread thread; + + FileRun(final FileDescriptor file) { + this.file = file; + this.events = new EngineTestEvents(file, listener, this::touch); + } + + private void touch() { + lastActivityNanos = System.nanoTime(); + } + + void awaitSettled() throws InterruptedException { + settled.await(); + } + + @Override + public void run() { + thread = Thread.currentThread(); + touch(); + state.set(State.RUNNING); + listener.executionStarted(file); + + Throwable failure = null; + try { + file.runner().run(events, brokerPool); + } catch (final Throwable t) { + failure = t; + } + + // the watcher may have abandoned the file meanwhile, in which case everything has been reported + if (state.compareAndSet(State.RUNNING, State.DONE)) { + events.completeOutstanding(failure); + listener.executionFinished(file, failure == null ? TestExecutionResult.successful() : TestExecutionResult.failed(failure)); + settle(); + } + } + + void abandon() { + if (!state.compareAndSet(State.RUNNING, State.ABANDONED)) { + return; + } + final List running = events.runningTests(); + final AssertionError hung = new AssertionError("Test file appears hung (no activity for " + + format(hangThreshold) + "): " + file.runner().getSourcePath().toAbsolutePath() + + (running.isEmpty() ? "" : " while running " + running)); + events.completeOutstanding(hung); + listener.executionFinished(file, TestExecutionResult.failed(hung)); + + // stop the query if it can be stopped, then the thread, so that a stuck file does not keep using the database + final Thread stuck = thread; + for (final XQueryWatchDog watchDog : brokerPool.getProcessMonitor().getRunningXQueries()) { + if (stuck != null && stuck.getName().equals(watchDog.getRunningThread())) { + watchDog.kill(0); + } + } + if (stuck != null) { + stuck.interrupt(); + } + settle(); + } + + private void settle() { + permits.release(); + settled.countDown(); + } + } + + private static String format(final Duration duration) { + final long seconds = duration.toSeconds(); + return seconds >= 120 ? (seconds / 60) + " minutes" : seconds + " seconds"; + } +} diff --git a/exist-core/src/test/java/org/exist/test/xqsuite/XQSuite.java b/exist-core/src/test/java/org/exist/test/xqsuite/XQSuite.java new file mode 100644 index 00000000000..eac717f377a --- /dev/null +++ b/exist-core/src/test/java/org/exist/test/xqsuite/XQSuite.java @@ -0,0 +1,64 @@ +/* + * eXist-db Open Source Native XML Database + * Copyright (C) 2001 The eXist-db Authors + * + * info@exist-db.org + * http://www.exist-db.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.exist.test.xqsuite; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Marks a class as a suite of XQSuite and XML tests, to be run by the {@link XQSuiteTestEngine}. + *

+ * The class itself is just a marker, for example: + *

{@code
+ * @XQSuite("src/test/xquery/maps")
+ * public class MapTests {
+ * }
+ * }
+ * Static methods of the class annotated with {@code @BeforeAll} and {@code @AfterAll} + * run around the whole suite, after the embedded database has started and before it stops. + *

+ * The test files of a suite run one after the other unless {@link #parallel()} is set. In either + * case a test file that stops reporting for too long is failed instead of hanging the build, see + * {@link XQSuiteSettings#HANG_THRESHOLD_MINUTES}. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Inherited +public @interface XQSuite { + + /** + * @return the directories or files containing the tests, relative to the working directory + */ + String[] value(); + + /** + * Run the test files of the suite concurrently, against the one embedded database, with at most + * {@link XQSuiteSettings#PARALLELISM} files at a time. The tests within a file always run one + * after the other. Only set this if the files do not depend on or disturb each other's data. + * + * @return true to run the files of the suite concurrently + */ + boolean parallel() default false; +} diff --git a/exist-core/src/test/java/org/exist/test/xqsuite/XQSuiteSchedulingTest.java b/exist-core/src/test/java/org/exist/test/xqsuite/XQSuiteSchedulingTest.java new file mode 100644 index 00000000000..51670f97ae5 --- /dev/null +++ b/exist-core/src/test/java/org/exist/test/xqsuite/XQSuiteSchedulingTest.java @@ -0,0 +1,203 @@ +/* + * eXist-db Open Source Native XML Database + * Copyright (C) 2001 The eXist-db Authors + * + * info@exist-db.org + * http://www.exist-db.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.exist.test.xqsuite; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.junit.platform.engine.TestExecutionResult; +import org.junit.platform.engine.UniqueId; +import org.junit.platform.testkit.engine.EngineExecutionResults; +import org.junit.platform.testkit.engine.EngineTestKit; +import org.junit.platform.testkit.engine.Event; +import org.junit.platform.testkit.engine.EventType; +import org.opentest4j.AssertionFailedError; + +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass; + +/** + * Tests how the {@link XQSuiteTestEngine} schedules the files of a suite, and that it fails a file that hangs. + *

+ * Whether files overlap is read off the timeline of the engine's events. The fixtures each sleep for + * a few seconds, so files overlap if, and only if, they were run concurrently. + */ +class XQSuiteSchedulingTest { + + private static final String WAITS = "src/test/resources/org/exist/test/xqsuite/"; + private static final String RUNNER = "src/test/resources/org/exist/test/runner/"; + + @XQSuite({WAITS + "wait-a.xqm", WAITS + "wait-b.xqm", WAITS + "wait-c.xqm", WAITS + "wait-d.xqm"}) + static class SequentialWaits { + } + + @XQSuite(value = {WAITS + "wait-a.xqm", WAITS + "wait-b.xqm", WAITS + "wait-c.xqm", WAITS + "wait-d.xqm"}, parallel = true) + static class ParallelWaits { + } + + /** the hanging file comes first, so a sequential suite only gets to the second file if the first is given up on */ + @XQSuite({WAITS + "hang.xqm", RUNNER + "single-test.xqm"}) + static class SequentialHang { + } + + @XQSuite(value = {WAITS + "hang.xqm", RUNNER + "single-test.xqm"}, parallel = true) + static class ParallelHang { + } + + private static EngineExecutionResults run(final Class suite, final Map parameters) { + final EngineTestKit.Builder builder = EngineTestKit.engine(XQSuiteTestEngine.ENGINE_ID).selectors(selectClass(suite)); + parameters.forEach(builder::configurationParameter); + return builder.execute(); + } + + /** a hang is detected after a couple of seconds instead of minutes */ + private static Map quickHangDetection() { + return Map.of( + XQSuiteSettings.HANG_THRESHOLD_MINUTES, "0.04", + XQSuiteSettings.HANG_WATCHER_INTERVAL_SECONDS, "1", + XQSuiteSettings.HANG_GRACE_SECONDS, "20"); + } + + /** + * @return the most tests that were running at the same moment + */ + private static int maxConcurrentTests(final EngineExecutionResults results) { + final Map starts = new HashMap<>(); + final Map ends = new HashMap<>(); + for (final Event event : results.testEvents().list()) { + final UniqueId id = event.getTestDescriptor().getUniqueId(); + if (event.getType() == EventType.STARTED) { + starts.put(id, event.getTimestamp()); + } else if (event.getType() == EventType.FINISHED) { + ends.put(id, event.getTimestamp()); + } + } + // sweep over the start and end points, handling an end before a start at the same instant + record Point(Instant at, int change) { } + final List points = new ArrayList<>(); + starts.forEach((id, start) -> { + points.add(new Point(start, 1)); + points.add(new Point(ends.get(id), -1)); + }); + points.sort(Comparator.comparing(Point::at).thenComparingInt(Point::change)); + int running = 0; + int max = 0; + for (final Point point : points) { + running += point.change(); + max = Math.max(max, running); + } + return max; + } + + private static List failures(final EngineExecutionResults results) { + return results.testEvents().failed().list().stream() + .map(event -> event.getRequiredPayload(TestExecutionResult.class).getThrowable().orElseThrow()) + .toList(); + } + + @Test + void sequentialSuiteRunsOneFileAtATime() { + final EngineExecutionResults results = run(SequentialWaits.class, Map.of()); + results.testEvents().assertStatistics(stats -> stats.started(4).succeeded(4)); + assertEquals(1, maxConcurrentTests(results), "files of a sequential suite must not overlap"); + } + + @Test + void parallelSuiteRunsFilesAtTheSameTime() { + final EngineExecutionResults results = run(ParallelWaits.class, Map.of(XQSuiteSettings.PARALLELISM, "4")); + results.testEvents().assertStatistics(stats -> stats.started(4).succeeded(4)); + assertEquals(4, maxConcurrentTests(results), "all four files should have been running together"); + } + + @Test + void parallelismSettingLimitsHowManyFilesRunAtOnce() { + final EngineExecutionResults results = run(ParallelWaits.class, Map.of(XQSuiteSettings.PARALLELISM, "2")); + results.testEvents().assertStatistics(stats -> stats.started(4).succeeded(4)); + assertEquals(2, maxConcurrentTests(results), "no more than two files may run together"); + } + + @ParameterizedTest(name = "parallel suite: {0}") + @ValueSource(booleans = {false, true}) + void hungFileIsFailedAndTheOtherFilesStillRun(final boolean parallel) { + final Class suite = parallel ? ParallelHang.class : SequentialHang.class; + final EngineExecutionResults results = assertTimeoutPreemptively(Duration.ofSeconds(60), () -> run(suite, quickHangDetection())); + + results.testEvents().assertStatistics(stats -> stats.started(2).succeeded(1).failed(1)); + + final Throwable failure = failures(results).getFirst(); + assertTrue(failure instanceof AssertionError && !(failure instanceof AssertionFailedError), "the hang is reported as an assertion error: " + failure); + assertTrue(failure.getMessage().contains("appears hung"), failure.getMessage()); + assertTrue(failure.getMessage().contains("hang.xqm"), "names the file: " + failure.getMessage()); + assertTrue(failure.getMessage().contains("hangs"), "names the test that was running: " + failure.getMessage()); + + // the abandoned thread is woken up and finishes the test normally; that must not be reported as well + final Map finishes = new HashMap<>(); + results.allEvents().list().stream() + .filter(event -> event.getType() == EventType.FINISHED) + .forEach(event -> finishes.merge(event.getTestDescriptor().getUniqueId(), 1L, Long::sum)); + finishes.forEach((id, count) -> assertEquals(1L, count, id + " must finish exactly once")); + assertEquals(1, results.containerEvents().failed().count(), "the hung file, and only it, is a failed container"); + } + + @Test + void stopThatFinishesInTimeIsReportedAsStopped() throws InterruptedException { + assertTrue(XQSuiteTestEngine.stopWithin(() -> { }, Duration.ofSeconds(5))); + } + + /** + * A runaway query that ignores both interruption and the kill flag keeps the database from shutting down. + * That cannot be reproduced for real in a shared test JVM, so the bounded wait is tested on its own. + */ + @Test + void stopThatNeverReturnsIsGivenUpOn() throws InterruptedException { + final java.util.concurrent.CountDownLatch release = new java.util.concurrent.CountDownLatch(1); + try { + assertFalse(XQSuiteTestEngine.stopWithin(() -> { + try { + release.await(); + } catch (final InterruptedException e) { + Thread.currentThread().interrupt(); + } + }, Duration.ofMillis(300))); + } finally { + release.countDown(); + } + } + + @Test + void stopThatFailsIsReportedAsAFailure() { + final IllegalStateException e = org.junit.jupiter.api.Assertions.assertThrows(IllegalStateException.class, + () -> XQSuiteTestEngine.stopWithin(() -> { throw new IllegalStateException("boom"); }, Duration.ofSeconds(5))); + assertEquals("boom", e.getCause().getMessage()); + } +} diff --git a/exist-core/src/test/java/org/exist/test/xqsuite/XQSuiteSettings.java b/exist-core/src/test/java/org/exist/test/xqsuite/XQSuiteSettings.java new file mode 100644 index 00000000000..ab86dd9d57b --- /dev/null +++ b/exist-core/src/test/java/org/exist/test/xqsuite/XQSuiteSettings.java @@ -0,0 +1,119 @@ +/* + * eXist-db Open Source Native XML Database + * Copyright (C) 2001 The eXist-db Authors + * + * info@exist-db.org + * http://www.exist-db.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.exist.test.xqsuite; + +import org.junit.platform.commons.JUnitException; +import org.junit.platform.engine.ConfigurationParameters; + +import java.time.Duration; +import java.util.function.Function; + +/** + * Settings of the {@link XQSuiteTestEngine}, read from the JUnit configuration parameters + * (so they can be set with a system property, in junit-platform.properties, or by the launcher). + */ +final class XQSuiteSettings { + + /** how many test files of a parallel suite run at once */ + static final String PARALLELISM = "exist.xqsuite.parallelism"; + + /** how long a running test file may report nothing before it is considered hung, in minutes (may be fractional) */ + static final String HANG_THRESHOLD_MINUTES = "exist.xqsuite.hang.threshold.minutes"; + + /** how often running test files are checked for being hung, in seconds */ + static final String HANG_WATCHER_INTERVAL_SECONDS = "exist.xqsuite.hang.watcher.interval.seconds"; + + /** how long to wait for the thread of a hung test file to stop once it has been interrupted, in seconds */ + static final String HANG_GRACE_SECONDS = "exist.xqsuite.hang.grace.seconds"; + + private static final int MIN_PARALLELISM = 2; + private static final int MAX_PARALLELISM = 32; + private static final double DEFAULT_HANG_THRESHOLD_MINUTES = 5.0; + private static final long DEFAULT_HANG_GRACE_SECONDS = 60; + + private final ConfigurationParameters parameters; + + XQSuiteSettings(final ConfigurationParameters parameters) { + this.parameters = parameters; + } + + /** + * @param brokerPoolMax the most brokers (concurrent database users) the database allows + */ + int parallelism(final int brokerPoolMax) { + final Integer configured = parse(PARALLELISM, Integer::parseInt); + if (configured != null) { + if (configured < 1) { + throw invalid(PARALLELISM, String.valueOf(configured), "must be at least 1"); + } + return configured; + } + final int processors = Runtime.getRuntime().availableProcessors(); + return Math.max(MIN_PARALLELISM, Math.min(MAX_PARALLELISM, Math.min(processors, brokerPoolMax))); + } + + Duration hangThreshold() { + final Double minutes = parse(HANG_THRESHOLD_MINUTES, Double::parseDouble); + final double value = minutes != null ? minutes : DEFAULT_HANG_THRESHOLD_MINUTES; + if (Double.isNaN(value) || value <= 0) { + throw invalid(HANG_THRESHOLD_MINUTES, String.valueOf(value), "must be greater than 0"); + } + return Duration.ofMillis((long) (value * 60_000)); + } + + /** + * Unless set, a tenth of the hang threshold, but at least a second and at most 30 seconds. + */ + Duration hangWatcherInterval() { + final Long seconds = parse(HANG_WATCHER_INTERVAL_SECONDS, Long::parseLong); + if (seconds != null) { + if (seconds < 1) { + throw invalid(HANG_WATCHER_INTERVAL_SECONDS, String.valueOf(seconds), "must be at least 1"); + } + return Duration.ofSeconds(seconds); + } + final Duration tenth = hangThreshold().dividedBy(10); + return tenth.compareTo(Duration.ofSeconds(1)) < 0 ? Duration.ofSeconds(1) + : tenth.compareTo(Duration.ofSeconds(30)) > 0 ? Duration.ofSeconds(30) : tenth; + } + + Duration hangGrace() { + final Long seconds = parse(HANG_GRACE_SECONDS, Long::parseLong); + return Duration.ofSeconds(seconds != null ? Math.max(0, seconds) : DEFAULT_HANG_GRACE_SECONDS); + } + + private T parse(final String key, final Function parser) { + final String value = parameters.get(key).map(String::trim).filter(v -> !v.isEmpty()).orElse(null); + if (value == null) { + return null; + } + try { + return parser.apply(value); + } catch (final NumberFormatException e) { + throw invalid(key, value, "is not a number"); + } + } + + private static JUnitException invalid(final String key, final String value, final String problem) { + return new JUnitException("Invalid value '" + value + "' for configuration parameter " + key + ": " + problem); + } +} diff --git a/exist-core/src/test/java/org/exist/test/xqsuite/XQSuiteSettingsTest.java b/exist-core/src/test/java/org/exist/test/xqsuite/XQSuiteSettingsTest.java new file mode 100644 index 00000000000..6424e6a781a --- /dev/null +++ b/exist-core/src/test/java/org/exist/test/xqsuite/XQSuiteSettingsTest.java @@ -0,0 +1,102 @@ +/* + * eXist-db Open Source Native XML Database + * Copyright (C) 2001 The eXist-db Authors + * + * info@exist-db.org + * http://www.exist-db.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.exist.test.xqsuite; + +import org.junit.jupiter.api.Test; +import org.junit.platform.commons.JUnitException; +import org.junit.platform.engine.ConfigurationParameters; + +import java.time.Duration; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class XQSuiteSettingsTest { + + private static XQSuiteSettings settings(final Map values) { + return new XQSuiteSettings(new ConfigurationParameters() { + @Override + public Optional get(final String key) { + return Optional.ofNullable(values.get(key)); + } + + @Override + public Optional getBoolean(final String key) { + return get(key).map(Boolean::parseBoolean); + } + + @Override + public Set keySet() { + return values.keySet(); + } + }); + } + + @Test + void defaults() { + final XQSuiteSettings settings = settings(Map.of()); + assertEquals(Duration.ofMinutes(5), settings.hangThreshold()); + assertEquals(Duration.ofSeconds(30), settings.hangWatcherInterval()); + assertEquals(Duration.ofSeconds(60), settings.hangGrace()); + } + + @Test + void defaultParallelismFollowsTheProcessorsAndTheBrokerPoolWithinBounds() { + final XQSuiteSettings settings = settings(Map.of()); + assertEquals(2, settings.parallelism(1), "never fewer than two"); + assertEquals(Math.max(2, Math.min(32, Math.min(Runtime.getRuntime().availableProcessors(), 8))), settings.parallelism(8)); + assertTrue(settings.parallelism(1000) <= 32, "never more than 32"); + } + + @Test + void settingsOverrideTheDefaults() { + final XQSuiteSettings settings = settings(Map.of( + XQSuiteSettings.PARALLELISM, "3", + XQSuiteSettings.HANG_THRESHOLD_MINUTES, "0.5", + XQSuiteSettings.HANG_WATCHER_INTERVAL_SECONDS, "7", + XQSuiteSettings.HANG_GRACE_SECONDS, "2")); + assertEquals(3, settings.parallelism(1), "an explicit value is used as given"); + assertEquals(Duration.ofSeconds(30), settings.hangThreshold()); + assertEquals(Duration.ofSeconds(7), settings.hangWatcherInterval()); + assertEquals(Duration.ofSeconds(2), settings.hangGrace()); + } + + @Test + void watcherIntervalIsATenthOfTheThresholdWithinBounds() { + assertEquals(Duration.ofSeconds(1), settings(Map.of(XQSuiteSettings.HANG_THRESHOLD_MINUTES, "0.04")).hangWatcherInterval()); + assertEquals(Duration.ofSeconds(6), settings(Map.of(XQSuiteSettings.HANG_THRESHOLD_MINUTES, "1")).hangWatcherInterval()); + assertEquals(Duration.ofSeconds(30), settings(Map.of(XQSuiteSettings.HANG_THRESHOLD_MINUTES, "60")).hangWatcherInterval()); + } + + @Test + void invalidValuesAreRejectedAndNamed() { + assertEquals(true, assertThrows(JUnitException.class, () -> settings(Map.of(XQSuiteSettings.PARALLELISM, "many")).parallelism(8)) + .getMessage().contains(XQSuiteSettings.PARALLELISM)); + assertThrows(JUnitException.class, () -> settings(Map.of(XQSuiteSettings.PARALLELISM, "0")).parallelism(8)); + assertThrows(JUnitException.class, () -> settings(Map.of(XQSuiteSettings.HANG_THRESHOLD_MINUTES, "0")).hangThreshold()); + assertThrows(JUnitException.class, () -> settings(Map.of(XQSuiteSettings.HANG_WATCHER_INTERVAL_SECONDS, "0")).hangWatcherInterval()); + } +} diff --git a/exist-core/src/test/java/org/exist/test/xqsuite/XQSuiteTestEngine.java b/exist-core/src/test/java/org/exist/test/xqsuite/XQSuiteTestEngine.java new file mode 100644 index 00000000000..aa90eeaf64d --- /dev/null +++ b/exist-core/src/test/java/org/exist/test/xqsuite/XQSuiteTestEngine.java @@ -0,0 +1,280 @@ +/* + * eXist-db Open Source Native XML Database + * Copyright (C) 2001 The eXist-db Authors + * + * info@exist-db.org + * http://www.exist-db.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.exist.test.xqsuite; + +import org.exist.storage.BrokerPool; +import org.exist.test.ExistEmbeddedServer; +import org.exist.test.runner.AbstractTestRunner; +import org.exist.test.runner.TestRunners; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.platform.commons.support.HierarchyTraversalMode; +import org.junit.platform.commons.support.ReflectionSupport; +import org.junit.platform.engine.EngineDiscoveryRequest; +import org.junit.platform.engine.EngineExecutionListener; +import org.junit.platform.engine.ExecutionRequest; +import org.junit.platform.engine.TestDescriptor; +import org.junit.platform.engine.TestEngine; +import org.junit.platform.engine.TestExecutionResult; +import org.junit.platform.engine.UniqueId; +import org.junit.platform.engine.discovery.ClassSelector; +import org.junit.platform.engine.discovery.ClasspathRootSelector; +import org.junit.platform.engine.discovery.PackageSelector; +import org.junit.platform.engine.support.descriptor.EngineDescriptor; + +import java.io.IOException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Stream; + +/** + * A JUnit Platform engine for XQSuite and XML tests. + *

+ * Classes annotated with {@link XQSuite} are discovered; each is a container whose children are + * the test files (containers) and the tests within them. The tests themselves are run by the + * same XQuery runner as before, which reports outcomes through {@link org.exist.test.runner.TestEvents}. + */ +public final class XQSuiteTestEngine implements TestEngine { + public static final String ENGINE_ID = "exist-xqsuite"; + + /** + * Set when a database could not be stopped: a test file that was given up on as hung is still running + * and keeps the database alive, so no further suite can be run safely in this JVM. + */ + private static volatile String unusableBecause = null; + + /** + * Test files are discovered once per file for the life of the JVM: the launcher discovers each + * class several times (for example once to decide whether to run it and once to run it). + */ + private static final Map RUNNERS = new ConcurrentHashMap<>(); + + @Override + public String getId() { + return ENGINE_ID; + } + + @Override + public TestDescriptor discover(final EngineDiscoveryRequest request, final UniqueId uniqueId) { + final EngineDescriptor engine = new EngineDescriptor(uniqueId, "eXist-db XQSuite"); + + final Set> classes = new LinkedHashSet<>(); + for (final ClassSelector selector : request.getSelectorsByType(ClassSelector.class)) { + classes.add(selector.getJavaClass()); + } + for (final ClasspathRootSelector selector : request.getSelectorsByType(ClasspathRootSelector.class)) { + classes.addAll(ReflectionSupport.findAllClassesInClasspathRoot(selector.getClasspathRoot(), c -> true, n -> true)); + } + for (final PackageSelector selector : request.getSelectorsByType(PackageSelector.class)) { + classes.addAll(ReflectionSupport.findAllClassesInPackage(selector.getPackageName(), c -> true, n -> true)); + } + + try (final DiscoveryDatabase discoveryDb = new DiscoveryDatabase()) { + for (final Class clazz : classes) { + final XQSuite annotation = clazz.getAnnotation(XQSuite.class); + if (annotation != null) { + engine.addChild(discoverSuite(engine.getUniqueId(), clazz, annotation, discoveryDb)); + } + } + } + return engine; + } + + /** + * An embedded database that is only started if some file needs it for discovery. + * XQuery modules may import modules that need the database to compile, and the database is + * how tests have always been discovered, so it is used rather than compiling on its own. + */ + private static final class DiscoveryDatabase implements AutoCloseable { + private ExistEmbeddedServer server; + + BrokerPool pool() throws Exception { + if (server == null) { + server = new ExistEmbeddedServer(true, true); + server.startDb(); + } + return server.getBrokerPool(); + } + + @Override + public void close() { + if (server != null) { + server.stopDb(); + server = null; + } + } + } + + private static SuiteDescriptor discoverSuite(final UniqueId engineId, final Class clazz, final XQSuite annotation, final DiscoveryDatabase discoveryDb) { + final UniqueId suiteId = engineId.append("suite", clazz.getName()); + final List runners = new ArrayList<>(); + Throwable failure = null; + try { + for (final String suite : annotation.value()) { + final Path path = Path.of(suite); + if (!Files.exists(path)) { + throw new IOException("XQSuite does not exist: " + suite + ". path=" + path.toAbsolutePath()); + } + if (Files.isDirectory(path)) { + try (final Stream children = Files.list(path)) { + final List sorted = children.filter(p -> !Files.isDirectory(p)).sorted(Comparator.comparing(Path::toString)).toList(); + for (final Path child : sorted) { + addRunner(runners, child, discoveryDb); + } + } + } else { + addRunner(runners, path, discoveryDb); + } + } + } catch (final Throwable t) { + failure = t; + } + + final SuiteDescriptor suite = new SuiteDescriptor(suiteId, clazz, annotation.parallel(), failure); + for (final AbstractTestRunner runner : runners) { + final FileDescriptor file = new FileDescriptor(suiteId.append("file", runner.getSourcePath().toString()), runner); + for (final String testName : runner.getTestNames()) { + file.addTest(testName); + } + suite.addChild(file); + } + return suite; + } + + private static void addRunner(final List runners, final Path path, final DiscoveryDatabase discoveryDb) throws Exception { + final Path key = path.toAbsolutePath().normalize(); + AbstractTestRunner runner = RUNNERS.get(key); + if (runner == null) { + runner = TestRunners.newTestRunner(path, TestRunners.isXQueryTestFile(path) ? discoveryDb.pool() : null); + if (runner != null) { + RUNNERS.put(key, runner); + } + } + if (runner != null) { + runners.add(runner); + } + } + + @Override + public void execute(final ExecutionRequest request) { + final EngineExecutionListener listener = request.getEngineExecutionListener(); + final TestDescriptor root = request.getRootTestDescriptor(); + listener.executionStarted(root); + final XQSuiteSettings settings = new XQSuiteSettings(request.getConfigurationParameters()); + for (final TestDescriptor suite : new ArrayList<>(root.getChildren())) { + runSuite((SuiteDescriptor) suite, listener, settings); + } + listener.executionFinished(root, TestExecutionResult.successful()); + } + + private static void runSuite(final SuiteDescriptor suite, final EngineExecutionListener listener, final XQSuiteSettings settings) { + final String unusable = unusableBecause; + if (unusable != null) { + listener.executionSkipped(suite, "Not run: " + unusable); + return; + } + listener.executionStarted(suite); + Throwable failure = suite.discoveryFailure(); + ExistEmbeddedServer server = null; + boolean started = false; + try { + if (failure == null) { + server = new ExistEmbeddedServer(true, true); + server.startDb(); + started = true; + invokeStatic(suite.suiteClass(), BeforeAll.class); + new SuiteRun(suite, listener, server.getBrokerPool(), settings).run(); + } + } catch (final Throwable t) { + failure = t; + } finally { + if (started) { + try { + invokeStatic(suite.suiteClass(), AfterAll.class); + } catch (final Throwable t) { + failure = add(failure, t); + } + try { + if (!stopWithin(server::stopDb, settings.hangGrace())) { + final String reason = "the embedded database of " + suite.suiteClass().getName() + " could not be stopped within " + + settings.hangGrace().toSeconds() + " seconds; a test file that was given up on as hung is probably still running"; + unusableBecause = reason; + failure = add(failure, new IllegalStateException(reason)); + } + } catch (final Throwable t) { + failure = add(failure, t); + } + } + } + listener.executionFinished(suite, failure == null ? TestExecutionResult.successful() : TestExecutionResult.failed(failure)); + } + + private static void invokeStatic(final Class clazz, final Class annotation) { + for (final Method method : ReflectionSupport.findMethods(clazz, + m -> Modifier.isStatic(m.getModifiers()) && m.isAnnotationPresent(annotation), HierarchyTraversalMode.TOP_DOWN)) { + ReflectionSupport.invokeMethod(method, null); + } + } + + /** + * Runs {@code stop} on a thread of its own and waits for it for at most {@code timeout}. + * + * @return false if it was still running when the time was up + */ + static boolean stopWithin(final Runnable stop, final java.time.Duration timeout) throws InterruptedException { + final java.util.concurrent.atomic.AtomicReference thrown = new java.util.concurrent.atomic.AtomicReference<>(); + final Thread thread = new Thread(() -> { + try { + stop.run(); + } catch (final Throwable t) { + thrown.set(t); + } + }, "xqsuite-stop"); + thread.setDaemon(true); + thread.start(); + thread.join(timeout.toMillis()); + if (thread.isAlive()) { + return false; + } + if (thrown.get() != null) { + throw new IllegalStateException("The embedded database failed to stop", thrown.get()); + } + return true; + } + + private static Throwable add(final Throwable existing, final Throwable additional) { + if (existing == null) { + return additional; + } + existing.addSuppressed(additional); + return existing; + } +} diff --git a/exist-core/src/test/java/org/exist/test/xqsuite/XQSuiteTestEngineTest.java b/exist-core/src/test/java/org/exist/test/xqsuite/XQSuiteTestEngineTest.java new file mode 100644 index 00000000000..5bdf83ba7c9 --- /dev/null +++ b/exist-core/src/test/java/org/exist/test/xqsuite/XQSuiteTestEngineTest.java @@ -0,0 +1,155 @@ +/* + * eXist-db Open Source Native XML Database + * Copyright (C) 2001 The eXist-db Authors + * + * info@exist-db.org + * http://www.exist-db.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.exist.test.xqsuite; + +import org.junit.jupiter.api.Test; +import org.junit.platform.engine.TestExecutionResult; +import org.junit.platform.testkit.engine.EngineExecutionResults; +import org.junit.platform.testkit.engine.EngineTestKit; +import org.junit.platform.testkit.engine.Event; +import org.opentest4j.AssertionFailedError; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass; + +class XQSuiteTestEngineTest { + + private static final String FIXTURES = "src/test/resources/org/exist/test/runner/"; + + @XQSuite(FIXTURES + "single-test.xqm") + static class SingleTest { + } + + @XQSuite(FIXTURES + "failing-assertion.xqm") + static class FailingAssertion { + } + + @XQSuite(FIXTURES + "failing-both.xqm") + static class FailingBoth { + } + + @XQSuite(FIXTURES + "failing-serialization.xqm") + static class FailingSerialization { + } + + @XQSuite(FIXTURES + "no-tests.xqm") + static class NoTests { + } + + @XQSuite(FIXTURES + "hyphenated-prefix.xqm") + static class HyphenatedPrefix { + } + + @XQSuite("src/test/resources/does/not/exist.xqm") + static class MissingFile { + } + + private static EngineExecutionResults run(final Class suite) { + return EngineTestKit.engine(XQSuiteTestEngine.ENGINE_ID).selectors(selectClass(suite)).execute(); + } + + private static List failures(final EngineExecutionResults results) { + return results.testEvents().failed().list().stream() + .map(event -> event.getRequiredPayload(TestExecutionResult.class).getThrowable().orElseThrow()) + .toList(); + } + + @SuppressWarnings("PMD.JUnitTestsShouldIncludeAssert") // assertion is delegated to assertStatistics, which asserts internally + @Test + void passingTestIsReportedAsSucceeded() { + run(SingleTest.class).testEvents().assertStatistics(stats -> stats.started(1).succeeded(1).failed(0).skipped(0)); + } + + @Test + void failingAssertionIsReportedWithExpectedAndActual() { + final EngineExecutionResults results = run(FailingAssertion.class); + results.testEvents().assertStatistics(stats -> stats.started(1).failed(1)); + + final Throwable failure = failures(results).get(0); + final AssertionFailedError assertionFailure = assertInstanceOf(AssertionFailedError.class, failure); + assertTrue(assertionFailure.getExpected().getStringRepresentation().contains("expected")); + assertEquals("actual", assertionFailure.getActual().getValue()); + } + + @SuppressWarnings("PMD.JUnitTestsShouldIncludeAssert") // assertion is delegated to assertStatistics, which asserts internally + @Test + void assertionFailureAndUnexpectedErrorAreBothReported() { + run(FailingBoth.class).testEvents().assertStatistics(stats -> stats.started(2).failed(2)); + } + + @Test + void nodeResultIsNotEscapedIntoTheFailureMessage() { + final AssertionFailedError failure = assertInstanceOf(AssertionFailedError.class, failures(run(FailingSerialization.class)).get(0)); + assertEquals("text", failure.getActual().getValue(), + "a node-valued result should reach the failure message as markup, not XML-escaped"); + } + + @SuppressWarnings("PMD.JUnitTestsShouldIncludeAssert") // assertion is delegated to assertStatistics, which asserts internally + @Test + void testsAreReportedUnderTheNamesTheyWereDiscoveredWith() { + // if discovery and the XQSuite runtime disagreed on a name, a test would be reported twice: + // once as passed under the runtime's name and once as failed because it was never reported + run(HyphenatedPrefix.class).testEvents().assertStatistics(stats -> stats.started(2).succeeded(2).failed(0)); + } + + @Test + void assertionFailureLeadsToTheXQueryTestFile() { + final Throwable failure = failures(run(FailingBoth.class)).stream() + .filter(AssertionFailedError.class::isInstance) + .findFirst() + .orElseThrow(); + + final boolean inStackTrace = java.util.Arrays.stream(failure.getStackTrace()) + .anyMatch(e -> e.getFileName() != null && e.getFileName().contains("failing-both")); + final boolean inMessage = failure.getMessage() != null && failure.getMessage().contains("failing-both"); + assertTrue(inStackTrace || inMessage, "the failure should name the XQuery test file so an IDE can navigate to it"); + } + + @Test + void unexpectedErrorLeadsToTheJavaCode() { + final Throwable failure = failures(run(FailingBoth.class)).stream() + .filter(org.exist.xquery.XPathException.class::isInstance) + .findFirst() + .orElseThrow(); + + assertTrue(java.util.Arrays.stream(failure.getStackTrace()).anyMatch(e -> e.getClassName().startsWith("org.exist")), + "the error should keep its Java stack trace so an IDE can navigate to the code"); + } + + @SuppressWarnings("PMD.JUnitTestsShouldIncludeAssert") // assertion is delegated to assertStatistics, which asserts internally + @Test + void fileWithoutTestsRunsNothing() { + run(NoTests.class).testEvents().assertStatistics(stats -> stats.started(0)); + } + + @Test + void suiteThatCannotBeDiscoveredFailsInsteadOfDisappearing() { + final EngineExecutionResults results = run(MissingFile.class); + results.testEvents().assertStatistics(stats -> stats.started(1).failed(1)); + final List failed = results.testEvents().failed().list(); + assertEquals(1, failed.size()); + } +} diff --git a/exist-core/src/test/java/org/exist/test/runner/ParallelPassingSuite.java b/exist-core/src/test/java/org/exist/test/xqsuite/XQTestDescriptor.java similarity index 64% rename from exist-core/src/test/java/org/exist/test/runner/ParallelPassingSuite.java rename to exist-core/src/test/java/org/exist/test/xqsuite/XQTestDescriptor.java index fd6920e50f9..517ef0a4dd0 100644 --- a/exist-core/src/test/java/org/exist/test/runner/ParallelPassingSuite.java +++ b/exist-core/src/test/java/org/exist/test/xqsuite/XQTestDescriptor.java @@ -19,20 +19,23 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +package org.exist.test.xqsuite; -package org.exist.test.runner; - -import org.junit.runner.RunWith; +import org.junit.platform.engine.TestSource; +import org.junit.platform.engine.UniqueId; +import org.junit.platform.engine.support.descriptor.AbstractTestDescriptor; /** - * XSuite that runs passing XQuery tests in parallel. - * Used by XSuiteParallelTest to verify dynamic pool and parallel execution. + * A single XQSuite test function or XML test. */ -@RunWith(XSuite.class) -@XSuite.XSuiteParallel -@XSuite.XSuiteFiles({ - "src/test/resources/org/exist/test/runner/single-test.xqm", - "src/test/resources/org/exist/test/runner/no-tests.xqm" -}) -public class ParallelPassingSuite { +final class XQTestDescriptor extends AbstractTestDescriptor { + + XQTestDescriptor(final UniqueId id, final String name, final TestSource source) { + super(id, name, source); + } + + @Override + public Type getType() { + return Type.TEST; + } } diff --git a/exist-core/src/test/java/org/exist/util/io/FilterInputStreamCacheMonitorTest.java b/exist-core/src/test/java/org/exist/util/io/FilterInputStreamCacheMonitorTest.java index 447d8ebadad..ba04eaf91ed 100644 --- a/exist-core/src/test/java/org/exist/util/io/FilterInputStreamCacheMonitorTest.java +++ b/exist-core/src/test/java/org/exist/util/io/FilterInputStreamCacheMonitorTest.java @@ -42,7 +42,10 @@ import java.nio.file.Path; import org.junit.jupiter.api.extension.RegisterExtension; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.fail; public class FilterInputStreamCacheMonitorTest { @RegisterExtension @@ -95,8 +98,8 @@ public void binaryResult() throws XMLDBException { assertEquals(1, resourceSet.getSize()); try (final EXistResource resource = (EXistResource)resourceSet.getResource(0)) { - assertTrue(resource instanceof LocalBinaryResource); - assertTrue(((ExtendedResource)resource).getExtendedContent() instanceof BinaryValue); + assertInstanceOf(LocalBinaryResource.class, resource); + assertInstanceOf(BinaryValue.class, ((ExtendedResource) resource).getExtendedContent()); // one active binary (as it is in the result set) assertEquals(1, monitor.getActive().size()); diff --git a/exist-core/src/test/java/org/exist/xquery/CountExpressionTest.java b/exist-core/src/test/java/org/exist/xquery/CountExpressionTest.java index a2fa1a9d290..eee2c158844 100644 --- a/exist-core/src/test/java/org/exist/xquery/CountExpressionTest.java +++ b/exist-core/src/test/java/org/exist/xquery/CountExpressionTest.java @@ -33,7 +33,7 @@ import java.io.StringReader; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.fail; /** @@ -79,8 +79,8 @@ public void countTest() throws RecognitionException, XPathException, TokenStream assertEquals(XQueryParser.LITERAL_count, ast.getNextSibling().getFirstChild().getNextSibling().getNextSibling().getType()); // rank variable binding assertEquals(XQueryParser.VARIABLE_BINDING, ast.getNextSibling().getFirstChild().getNextSibling().getNextSibling().getFirstChild().getType()); - assertTrue(((ForExpr)expr.getFirst()).returnExpr instanceof OrderByClause); - assertTrue(((OrderByClause)(((ForExpr)expr.getFirst()).returnExpr)).returnExpr instanceof CountClause); + assertInstanceOf(OrderByClause.class, ((ForExpr) expr.getFirst()).returnExpr); + assertInstanceOf(CountClause.class, ((OrderByClause) (((ForExpr) expr.getFirst()).returnExpr)).returnExpr); assertEquals(new QName("rank"), ((CountClause)((OrderByClause)(((ForExpr)expr.getFirst()).returnExpr)).returnExpr).varName); } } diff --git a/exist-core/src/test/java/org/exist/xquery/ExpressionVisitorTraversalTest.java b/exist-core/src/test/java/org/exist/xquery/ExpressionVisitorTraversalTest.java index 9e0c848beef..288f15b3c92 100644 --- a/exist-core/src/test/java/org/exist/xquery/ExpressionVisitorTraversalTest.java +++ b/exist-core/src/test/java/org/exist/xquery/ExpressionVisitorTraversalTest.java @@ -30,7 +30,8 @@ import org.junit.jupiter.api.Test; import antlr.collections.AST; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.StringReader; import java.util.ArrayList; diff --git a/exist-core/src/test/java/org/exist/xquery/InternalModuleTest.java b/exist-core/src/test/java/org/exist/xquery/InternalModuleTest.java index 5c8c6819e61..4631b9a065a 100644 --- a/exist-core/src/test/java/org/exist/xquery/InternalModuleTest.java +++ b/exist-core/src/test/java/org/exist/xquery/InternalModuleTest.java @@ -42,7 +42,11 @@ import org.junit.jupiter.api.extension.RegisterExtension; import static org.exist.xquery.InternalModuleTest.TestModuleWithVariables.*; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; /** * @author Adam Retter @@ -102,7 +106,7 @@ public void requestResponseSessionVariables_4_x_x_Api() throws XMLDBException { requestResponseSessionVariablesQuery_4_x_X_Api(queryService, querySource); fail("Expected XQuery error XPST0008"); } catch (final XMLDBException e) { - assertTrue(e.getCause() instanceof XPathException); + assertInstanceOf(XPathException.class, e.getCause()); final XPathException xpe = (XPathException)e.getCause(); assertEquals(ErrorCodes.XPST0008, xpe.getErrorCode()); } diff --git a/exist-core/src/test/java/org/exist/xquery/WindowClauseTest.java b/exist-core/src/test/java/org/exist/xquery/WindowClauseTest.java index 9cd465e0f1a..dc1657d3f74 100644 --- a/exist-core/src/test/java/org/exist/xquery/WindowClauseTest.java +++ b/exist-core/src/test/java/org/exist/xquery/WindowClauseTest.java @@ -35,10 +35,11 @@ import java.io.StringReader; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; /** * @author Adam Retter @@ -74,7 +75,7 @@ void simpleWindowConditions() throws RecognitionException, XPathException, Token fail(treeParser.getErrorMessage()); } - assertTrue(expr.getFirst() instanceof WindowExpr, "Expression should be of type WindowExpr"); + assertInstanceOf(WindowExpr.class, expr.getFirst(), "Expression should be of type WindowExpr"); } @Test @@ -104,7 +105,7 @@ void complexWindowCondition() throws RecognitionException, XPathException, Token fail(treeParser.getErrorMessage()); } - assertTrue(expr.getFirst() instanceof WindowExpr, "Expression should be of type WindowExpr"); + assertInstanceOf(WindowExpr.class, expr.getFirst(), "Expression should be of type WindowExpr"); } @Test @@ -133,7 +134,7 @@ void noEndWindowCondition() throws RecognitionException, XPathException, TokenSt fail(treeParser.getErrorMessage()); } - assertTrue(expr.getFirst() instanceof WindowExpr, "Expression should be of type WindowExpr"); + assertInstanceOf(WindowExpr.class, expr.getFirst(), "Expression should be of type WindowExpr"); } @Test @@ -164,7 +165,7 @@ void slidingWindowClause() throws RecognitionException, XPathException, TokenStr fail(treeParser.getErrorMessage()); } - assertTrue(expr.getFirst() instanceof WindowExpr, "Expression should be of type WindowExpr"); + assertInstanceOf(WindowExpr.class, expr.getFirst(), "Expression should be of type WindowExpr"); assertEquals(WindowExpr.WindowType.SLIDING_WINDOW, ((WindowExpr) expr.getFirst()).getWindowType()); } @@ -198,7 +199,7 @@ void allWindowsVars() throws RecognitionException, XPathException, TokenStreamEx fail(treeParser.getErrorMessage()); } - assertTrue(expr.getFirst() instanceof WindowExpr, "Expression should be of type WindowExpr"); + assertInstanceOf(WindowExpr.class, expr.getFirst(), "Expression should be of type WindowExpr"); assertEquals(new QName("first"), ((WindowExpr) expr.getFirst()).getWindowStartCondition().getCurrentItem()); assertEquals(new QName("s"), ((WindowExpr) expr.getFirst()).getWindowStartCondition().getPosVar()); assertEquals(new QName("start-previous"), ((WindowExpr) expr.getFirst()).getWindowStartCondition().getPreviousItem()); @@ -240,7 +241,7 @@ void tumblingWindowAllWindowVarsNoOnly() throws RecognitionException, XPathExcep fail(treeParser.getErrorMessage()); } - assertTrue(expr.getFirst() instanceof WindowExpr, "Expression should be of type WindowExpr"); + assertInstanceOf(WindowExpr.class, expr.getFirst(), "Expression should be of type WindowExpr"); assertEquals(WindowExpr.WindowType.TUMBLING_WINDOW, ((WindowExpr) expr.getFirst()).getWindowType()); assertEquals(new QName("s"), ((WindowExpr) expr.getFirst()).getWindowStartCondition().getCurrentItem()); assertEquals(new QName("spos"), ((WindowExpr) expr.getFirst()).getWindowStartCondition().getPosVar()); @@ -281,7 +282,7 @@ void tumblingWindowAvgReturn() throws RecognitionException, XPathException, Toke fail(treeParser.getErrorMessage()); } - assertTrue(expr.getFirst() instanceof WindowExpr, "Expression should be of type WindowExpr"); + assertInstanceOf(WindowExpr.class, expr.getFirst(), "Expression should be of type WindowExpr"); assertNull(((WindowExpr) expr.getFirst()).getWindowStartCondition().getCurrentItem()); assertEquals(new QName("s"), ((WindowExpr) expr.getFirst()).getWindowStartCondition().getPosVar()); assertNull(((WindowExpr) expr.getFirst()).getWindowStartCondition().getPreviousItem()); @@ -319,7 +320,7 @@ void tumblingWindowNoEndWindowConditionPositional() throws RecognitionException, fail(treeParser.getErrorMessage()); } - assertTrue(expr.getFirst() instanceof WindowExpr, "Expression should be of type WindowExpr"); + assertInstanceOf(WindowExpr.class, expr.getFirst(), "Expression should be of type WindowExpr"); assertNull(((WindowExpr) expr.getFirst()).getWindowStartCondition().getCurrentItem()); assertEquals(new QName("s"), ((WindowExpr) expr.getFirst()).getWindowStartCondition().getPosVar()); assertNull(((WindowExpr) expr.getFirst()).getWindowStartCondition().getPreviousItem()); @@ -353,7 +354,7 @@ void tumblingWindowNoEndWindowConditionCurrentItem() throws RecognitionException fail(treeParser.getErrorMessage()); } - assertTrue(expr.getFirst() instanceof WindowExpr, "Expression should be of type WindowExpr"); + assertInstanceOf(WindowExpr.class, expr.getFirst(), "Expression should be of type WindowExpr"); assertEquals("first", ((WindowExpr) expr.getFirst()).getWindowStartCondition().getCurrentItem().getStringValue()); assertNull(((WindowExpr) expr.getFirst()).getWindowStartCondition().getPosVar()); assertNull(((WindowExpr) expr.getFirst()).getWindowStartCondition().getPreviousItem()); @@ -389,7 +390,7 @@ void slidingWindowAvgReturn() throws RecognitionException, XPathException, Token fail(treeParser.getErrorMessage()); } - assertTrue(expr.getFirst() instanceof WindowExpr, "Expression should be of type WindowExpr"); + assertInstanceOf(WindowExpr.class, expr.getFirst(), "Expression should be of type WindowExpr"); assertEquals(WindowExpr.WindowType.SLIDING_WINDOW, ((WindowExpr) expr.getFirst()).getWindowType()); assertNull(((WindowExpr) expr.getFirst()).getWindowStartCondition().getCurrentItem()); assertEquals(new QName("s"), ((WindowExpr) expr.getFirst()).getWindowStartCondition().getPosVar()); @@ -429,7 +430,7 @@ void slidingWindowEndWithoutOnly() throws RecognitionException, XPathException, fail(treeParser.getErrorMessage()); } - assertTrue(expr.getFirst() instanceof WindowExpr, "Expression should be of type WindowExpr"); + assertInstanceOf(WindowExpr.class, expr.getFirst(), "Expression should be of type WindowExpr"); assertEquals(WindowExpr.WindowType.SLIDING_WINDOW, ((WindowExpr) expr.getFirst()).getWindowType()); assertNull(((WindowExpr) expr.getFirst()).getWindowStartCondition().getCurrentItem()); assertEquals(new QName("s"), ((WindowExpr) expr.getFirst()).getWindowStartCondition().getPosVar()); @@ -475,7 +476,7 @@ void tumblingWindowRunUp() throws RecognitionException, XPathException, TokenStr fail(treeParser.getErrorMessage()); } - assertTrue(expr.getFirst() instanceof WindowExpr, "Expression should be of type WindowExpr"); + assertInstanceOf(WindowExpr.class, expr.getFirst(), "Expression should be of type WindowExpr"); assertEquals(WindowExpr.WindowType.TUMBLING_WINDOW, ((WindowExpr) expr.getFirst()).getWindowType()); assertEquals(new QName("first"), ((WindowExpr) expr.getFirst()).getWindowStartCondition().getCurrentItem()); assertNull(((WindowExpr) expr.getFirst()).getWindowStartCondition().getPosVar()); diff --git a/exist-core/src/test/java/org/exist/xquery/XPathQueryTest.java b/exist-core/src/test/java/org/exist/xquery/XPathQueryTest.java index 9b6aaacb02b..4064a30a715 100644 --- a/exist-core/src/test/java/org/exist/xquery/XPathQueryTest.java +++ b/exist-core/src/test/java/org/exist/xquery/XPathQueryTest.java @@ -57,6 +57,7 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; public class XPathQueryTest { @@ -84,7 +85,7 @@ private final String getBaseUri() { private final static String nested = "" + ""; - + private final static String numbers = "" + "5.622" @@ -92,7 +93,7 @@ private final String getBaseUri() { + "18.45" + "65.5416" + ""; - + private final static String numbers2 = "" + "5.622" @@ -100,7 +101,7 @@ private final String getBaseUri() { + "18.45" + "65.5416" + ""; - + private final static String namespaces = "" + "

" @@ -108,21 +109,21 @@ private final String getBaseUri() { + " This is my comment" + "
" + ""; - + private final static String strings = "" + "Hello World!" + "" + "Hello" + ""; - + private final static String nested2 = "" + "" + "" + "" + ""; - + private final static String nested3 = "" + " " + @@ -135,7 +136,7 @@ private final String getBaseUri() { " " + " " + ""; - + private final static String siblings = "" + "" + @@ -189,16 +190,16 @@ private final String getBaseUri() { "" + "]>" + ids_content; - + private final static String date = ""; - + private final static String quotes = ""Hello""; - + private final static String ws = " "; - + private final static String self = "HelloWorld!"; @@ -215,16 +216,16 @@ private final String getBaseUri() { val2 """; - + // Added by Geoff Shuetrim (geoff@galexy.net) to highlight problems with XPath queries of elements called 'xpointer'. private final static String xpointerElementName = ""; private final static String cdata_content = "Hello there \"Bob?\""; private final static String cdata_xml = ""; - + private Collection testCollection; - + private void setUp() throws ReflectiveOperationException, XMLDBException { // initialize driver Class cl = Class.forName("org.exist.xmldb.DatabaseImpl"); @@ -740,7 +741,7 @@ public void followingSiblingAxisPersistent(String apiName, String baseUri) throw result = queryResource(service, "siblings.xml", "/node()[1]/following-sibling::node()", 3); assertEquals("", result.getResource(0).getContent().toString()); final Node testElem = ((XMLResource)result.getResource(1)).getContentAsDOM(); - assertTrue(testElem instanceof Element); + assertInstanceOf(Element.class, testElem); assertEquals("test", testElem.getNodeName()); assertEquals("", result.getResource(2).getContent().toString()); @@ -1271,10 +1272,10 @@ public void predicatesBug1460610(String apiName, String baseUri) throws XMLDBExc initXPathQueryTest(apiName, baseUri); setUp(); final String xQuery = "(1, 2, 3)[ . lt 3]"; - + final XQueryService service = getQueryService(); final ResourceSet rs = service.query(xQuery); - + assertEquals(2, rs.getSize(), "SFBUG 1460610 nr of results"); assertEquals("1", rs.getResource(0).getContent().toString(), "SFBUG 1460610 1st result"); @@ -1290,10 +1291,10 @@ public void predicatesBug1537355(String apiName, String baseUri) throws XMLDBExc initXPathQueryTest(apiName, baseUri); setUp(); final String xQuery = "let $one := 1 return (1, 2, 3)[$one + 1]"; - + final XQueryService service = getQueryService(); final ResourceSet rs = service.query(xQuery); - + assertEquals(1, rs.getSize(), "SFBUG 1537355 nr of results"); assertEquals("2", rs.getResource(0).getContent().toString(), "SFBUG 1537355 result"); @@ -1312,10 +1313,10 @@ public void nestedPredicatesBug1533053(String apiName, String baseUri) throws XM " " + "let $matches := $doc/detail[source[dynamic='false'] or class] " + "return count($matches) eq 2"; - + XQueryService service = getQueryService(); ResourceSet rs = service.query(xQuery); - + assertEquals(1, rs.getSize()); assertEquals("true", rs.getResource(0).getContent().toString()); @@ -1326,11 +1327,11 @@ public void nestedPredicatesBug1533053(String apiName, String baseUri) throws XM service = getQueryService(); service.setProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); - service.setProperty(OutputKeys.INDENT, "no"); + service.setProperty(OutputKeys.INDENT, "no"); rs = service.query(xQuery); assertEquals(1, rs.getSize()); - assertXMLEqual("", + assertXMLEqual("", rs.getResource(0).getContent().toString()); } @@ -1345,21 +1346,21 @@ public void predicateBug1488303(String apiName, String baseUri) throws XMLDBExce setUp(); XQueryService service = getQueryService(); ResourceSet rs=null; - + // test one final String xQuery1 = "let $q := eXist return $q//t"; rs = service.query(xQuery1); assertEquals(1, rs.getSize(), "nr of results"); assertEquals("eXist", rs.getResource(0).getContent().toString(), "result"); - + // test two final String xQuery2 = "let $q := eXist return ($q//t)[1]"; rs = service.query(xQuery2); assertEquals(1, rs.getSize(), "nr of results"); assertEquals("eXist", rs.getResource(0).getContent().toString(), "result"); - + // This one fails http://sourceforge.net/tracker/index.php?func=detail&aid=1488303&group_id=17691&atid=117691 final String xQuery3 = "let $q := eXist return $q//t[1]"; rs = service.query(xQuery3); @@ -1378,18 +1379,18 @@ public void descendantOrSelfBug1460791(String apiName, String baseUri) throws XM setUp(); final String xQuery = "declare option exist:serialize \"method=xml indent=no\"; let $test:=aaazzz " +"return ( {$test//z}, {$test/descendant-or-self::node()/child::z} )"; - + final XQueryService service = getQueryService(); final ResourceSet rs = service.query(xQuery); - + // System.out.println("BUG1460791/1" + rs.getResource(0).getContent().toString() ); // System.out.println("BUG1460791/2" + rs.getResource(1).getContent().toString() ); - + assertEquals(2, rs.getSize(), "SFBUG 1460791 nr of results"); - + assertEquals("zzz", rs.getResource(0).getContent().toString(), "SFBUG 1460791 result part 1"); - + assertEquals("zzz", rs.getResource(1).getContent().toString(), "SFBUG 1460791 result part 2"); } @@ -1408,18 +1409,18 @@ public void xpathBug1462120(String apiName, String baseUri) throws XMLDBExceptio +"let $one:=$list[1] return ( " +"$m/Unit[string(data(@name)) eq string(data($list[1]/@aaa))]," +"
,$m/Unit[string(data(@name)) eq string(data($one/@aaa))] )"; - + final XQueryService service = getQueryService(); final ResourceSet rs = service.query(xQuery); - + assertEquals(3, rs.getSize(), "SFBUG 1462120 nr of results"); - + assertEquals("", rs.getResource(0).getContent().toString(), "SFBUG 1462120 result part 1"); - + assertEquals("
", rs.getResource(1).getContent().toString(), "SFBUG 1462120 result part 2"); - + assertEquals("", rs.getResource(2).getContent().toString(), "SFBUG 1462120 result part 3"); } @@ -1436,10 +1437,10 @@ public void predicateBugWiki1(String apiName, String baseUri) throws XMLDBExcept initXPathQueryTest(apiName, baseUri); setUp(); final String xQuery = "let $dum := 12 return $dum/el[2]"; - + final XQueryService service = getQueryService(); final ResourceSet rs = service.query(xQuery); - + assertEquals(1, rs.getSize(), "Predicate bug wiki_1"); assertEquals("2", rs.getResource(0).getContent().toString(), "Predicate bug wiki_1"); @@ -1475,11 +1476,11 @@ public void cardinalitySelfBugWiki2(String apiName, String baseUri) throws XMLDB assertEquals(1, rs.getSize(), "CardinalitySelfBUG bug wiki_2"); assertEquals("employee", rs.getResource(0).getContent().toString(), "CardinalitySelfBUG bug wiki_2"); - + } /** - * Problem in VirtualNodeSet which return 2 attributes because it + * Problem in VirtualNodeSet which return 2 attributes because it * computes every level * @see http://wiki.exist-db.org/space/XQueryBugs */ @@ -1493,7 +1494,7 @@ public void virtualNodesetBugWiki3(String apiName, String baseUri) throws XMLDBE final XQueryService service = getQueryService(); final ResourceSet rs = service.query(xQuery); - + assertEquals(1, rs.getSize(), "VirtualNodesetBUG_wiki_3"); assertEquals("", rs.getResource(0).getContent().toString(), "VirtualNodesetBUG_wiki_3"); @@ -1575,7 +1576,7 @@ public void castInPredicateBugWiki7(String apiName, String baseUri) throws XMLDB } /** - * Miscomputation of the expression context in where clause when no + * Miscomputation of the expression context in where clause when no * wrapper expression is used. Using, e.g. where data($x/@id) eq "id" works ! */ @MethodSource("data") @ParameterizedTest(name = "{0}") @@ -1732,7 +1733,7 @@ public void logicalOr(String apiName, String baseUri) throws XMLDBException, IOE setUp(); final XQueryService service = storeXMLStringAndGetQueryService("strings.xml", strings); - + ResourceSet result = queryResource(service, "strings.xml", "{() or ()}", 1); Resource r = result.getResource(0); assertXMLEqual("false", r.getContent().toString()); @@ -1828,7 +1829,7 @@ public void idRefsPersistent(String apiName, String baseUri) throws XMLDBExcepti setUp(); final XQueryService service = storeXMLStringAndGetQueryService("ids.xml", ids); - + queryResource(service, "ids.xml", "/idref('id2')", 1); queryResource(service, "ids.xml", "/idref('id1')", 2); queryResource(service, "ids.xml", "/idref(('id2', 'id1'))", 3); @@ -1953,7 +1954,7 @@ public void queryResource(String apiName, String baseUri) throws XMLDBException, XMLResource doc = testCollection.createResource("strings.xml", XMLResource.class); doc.setContent(strings); testCollection.storeResource(doc); - + doc = testCollection.createResource("strings2.xml", XMLResource.class); doc.setContent(strings); testCollection.storeResource(doc); @@ -2250,7 +2251,7 @@ public void convertToBoolean(String apiName, String baseUri) throws XMLDBExcepti .getResource(i).getContent().toString(), "false " + (i + 1)); } } - + boolean exceptionThrown = false; String message = ""; try { @@ -2278,7 +2279,7 @@ public void compile(String apiName, String baseUri) throws XMLDBException, Refle module namespace foo="urn:foo"; declare function foo:test() { "Hello World! };\ """; - + final EXistXQueryService service = (EXistXQueryService) getQueryService(); boolean exceptionOccurred = false; try { @@ -2288,7 +2289,7 @@ public void compile(String apiName, String baseUri) throws XMLDBException, Refle exceptionOccurred = true; } assertTrue(exceptionOccurred, "Expected an exception"); - + exceptionOccurred = false; try { service.compileAndCheck(invalidModule); @@ -2350,7 +2351,7 @@ public void substring(String apiName, String baseUri) throws XMLDBException, Ref initXPathQueryTest(apiName, baseUri); setUp(); final XQueryService service = getQueryService(); - + // Test cases by MIKA final String validQuery = "substring(\"MK-1234\", 4,1)"; ResourceSet result = queryAndAssert( service, validQuery, 1, validQuery); diff --git a/exist-core/src/test/java/org/exist/xquery/XQueryContextAttributesTest.java b/exist-core/src/test/java/org/exist/xquery/XQueryContextAttributesTest.java index 3c1cbd86eb4..d2fecb25445 100644 --- a/exist-core/src/test/java/org/exist/xquery/XQueryContextAttributesTest.java +++ b/exist-core/src/test/java/org/exist/xquery/XQueryContextAttributesTest.java @@ -61,10 +61,11 @@ import org.junit.jupiter.api.extension.RegisterExtension; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import static com.evolvedbinary.j8fu.tuple.Tuple.Tuple; /** @@ -135,10 +136,10 @@ public void attributesOfLibraryModuleContextCleared() throws EXistException, Loc // get the context of the library module final Module[] libraryModules = mainQueryContext.getModules("http://mod1"); assertEquals(1, libraryModules.length); - assertTrue(libraryModules[0] instanceof ExternalModule); + assertInstanceOf(ExternalModule.class, libraryModules[0]); final ExternalModule libraryModule = (ExternalModule) libraryModules[0]; final XQueryContext libraryQueryContext = libraryModule.getContext(); - assertTrue(libraryQueryContext instanceof ModuleContext); + assertInstanceOf(ModuleContext.class, libraryQueryContext); libraryQueryContext.setAttribute("attr1", "value1"); libraryQueryContext.setAttribute("attr2", "value2"); diff --git a/exist-core/src/test/java/org/exist/xquery/functions/fn/CollectionTest.java b/exist-core/src/test/java/org/exist/xquery/functions/fn/CollectionTest.java index cefaea0228c..357e5d94a11 100644 --- a/exist-core/src/test/java/org/exist/xquery/functions/fn/CollectionTest.java +++ b/exist-core/src/test/java/org/exist/xquery/functions/fn/CollectionTest.java @@ -56,7 +56,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; + public class CollectionTest { @RegisterExtension @@ -85,7 +86,7 @@ public void doc_dynamicallyAvailableCollection_absoluteUri() throws XPathExcepti assertFalse(result.isEmpty()); assertEquals(1, result.getItemCount()); - assertTrue(result.itemAt(0) instanceof Node); + assertInstanceOf(Node.class, result.itemAt(0)); final Source expectedSource = Input.fromString(doc).build(); final Source actualSource = Input.fromNode((Node)result.itemAt(0)).build(); @@ -119,7 +120,7 @@ public void doc_dynamicallyAvailableCollection_relativeUri() throws XPathExcepti assertFalse(result.isEmpty()); assertEquals(1, result.getItemCount()); - assertTrue(result.itemAt(0) instanceof Node); + assertInstanceOf(Node.class, result.itemAt(0)); final Source expectedSource = Input.fromString(doc).build(); final Source actualSource = Input.fromNode((Node)result.itemAt(0)).build(); diff --git a/exist-core/src/test/java/org/exist/xquery/functions/fn/DocTest.java b/exist-core/src/test/java/org/exist/xquery/functions/fn/DocTest.java index e599ae8935c..d1dc9c5bb9d 100644 --- a/exist-core/src/test/java/org/exist/xquery/functions/fn/DocTest.java +++ b/exist-core/src/test/java/org/exist/xquery/functions/fn/DocTest.java @@ -40,9 +40,11 @@ import static com.evolvedbinary.j8fu.Either.Left; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; + import org.exist.xmldb.EXistResource; import org.exist.xmldb.LocalXMLResource; import org.w3c.dom.Node; @@ -115,15 +117,15 @@ public void tearDown() throws XMLDBException { existEmbeddedServer.getRoot().removeResource(existEmbeddedServer.getRoot().getResource("test.xml")); } - + private void storeResource(final Collection col, final String fileName, final Class type, final String mimeType, final String content) throws XMLDBException { Resource res = col.createResource(fileName, type); res.setContent(content); - + if (mimeType != null) { ((EXistResource) res).setMimeType(mimeType); } - + col.storeResource(res); } @@ -172,7 +174,7 @@ public void doc_dynamicallyAvailableDocument_absoluteUri() throws XPathException assertFalse(result.isEmpty()); assertEquals(1, result.getItemCount()); - assertTrue(result.itemAt(0) instanceof Node); + assertInstanceOf(Node.class, result.itemAt(0)); final Source expectedSource = Input.fromString(doc).build(); final Source actualSource = Input.fromNode((Node)result.itemAt(0)).build(); @@ -206,7 +208,7 @@ public void doc_dynamicallyAvailableDocument_relativeUri() throws XPathException assertFalse(result.isEmpty()); assertEquals(1, result.getItemCount()); - assertTrue(result.itemAt(0) instanceof Node); + assertInstanceOf(Node.class, result.itemAt(0)); final Source expectedSource = Input.fromString(doc).build(); final Source actualSource = Input.fromNode((Node)result.itemAt(0)).build(); diff --git a/exist-core/src/test/java/org/exist/xquery/functions/inspect/InspectModuleTest.java b/exist-core/src/test/java/org/exist/xquery/functions/inspect/InspectModuleTest.java index 11b332f83e0..dbf706845f4 100644 --- a/exist-core/src/test/java/org/exist/xquery/functions/inspect/InspectModuleTest.java +++ b/exist-core/src/test/java/org/exist/xquery/functions/inspect/InspectModuleTest.java @@ -53,9 +53,10 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; + public class InspectModuleTest { @RegisterExtension @@ -75,7 +76,7 @@ public class InspectModuleTest { declare function x:fun1() as xs:string { "hello from fun1" }; - + (:~ : Some other description. : @@ -87,7 +88,7 @@ public class InspectModuleTest { declare function x:fun2($one as xs:int, $two as xs:float) as xs:string { "hello from fun2" }; - + (:~ : This is a multiline description and therefore : spans multiple @@ -98,7 +99,7 @@ public class InspectModuleTest { declare function x:fun3() { "hello from fun3" }; - + (:~ : An annotated function. : @@ -154,7 +155,7 @@ public class InspectModuleTest { """; private static final String MAIN_MODULE = """ import module namespace inspect = "http://exist-db.org/xquery/inspection"; - + inspect:inspect-module(xs:anyURI("xmldb:exist://%s"))/function[@name eq "%s"] """; @@ -292,7 +293,7 @@ private static void assertInspection( assertNotNull(result); assertEquals(1, result.getItemCount()); final Item item1 = result.itemAt(0); - assertTrue(item1 instanceof ElementImpl); + assertInstanceOf(ElementImpl.class, item1); final Element function = (Element)item1; diff --git a/exist-core/src/test/java/org/exist/xquery/functions/request/GetData2Test.java b/exist-core/src/test/java/org/exist/xquery/functions/request/GetData2Test.java index c672ce71acb..21070fb64c1 100644 --- a/exist-core/src/test/java/org/exist/xquery/functions/request/GetData2Test.java +++ b/exist-core/src/test/java/org/exist/xquery/functions/request/GetData2Test.java @@ -40,7 +40,10 @@ import java.nio.charset.StandardCharsets; import static org.easymock.EasyMock.*; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; /** * Unlike {@link GetDataTest} this test tries to test the code of @@ -88,7 +91,7 @@ public void xmlChunkedTransferNonBlockingAvailable() throws XPathException, IOEx assertNotNull(result); assertFalse(result.isEmpty()); assertEquals(1, result.getItemCount()); - assertTrue(result.itemAt(0) instanceof Document); + assertInstanceOf(Document.class, result.itemAt(0)); assertEquals("hello", ((Document) result.itemAt(0)).getDocumentElement().getLocalName()); assertEquals("world", ((Document) result.itemAt(0)).getDocumentElement().getTextContent()); @@ -134,7 +137,7 @@ public void xmlChunkedTransferNonBlockingNoneAvailable() throws XPathException, assertNotNull(result); assertFalse(result.isEmpty()); assertEquals(1, result.getItemCount()); - assertTrue(result.itemAt(0) instanceof Document); + assertInstanceOf(Document.class, result.itemAt(0)); assertEquals("hello", ((Document) result.itemAt(0)).getDocumentElement().getLocalName()); assertEquals("world", ((Document) result.itemAt(0)).getDocumentElement().getTextContent()); diff --git a/exist-core/src/test/java/org/exist/xquery/functions/transform/TransformTest.java b/exist-core/src/test/java/org/exist/xquery/functions/transform/TransformTest.java index 7cb2d336a29..95231b3428b 100644 --- a/exist-core/src/test/java/org/exist/xquery/functions/transform/TransformTest.java +++ b/exist-core/src/test/java/org/exist/xquery/functions/transform/TransformTest.java @@ -61,9 +61,11 @@ import static com.evolvedbinary.j8fu.tuple.Tuple.Tuple; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; + /** * @author Adam Retter */ @@ -105,18 +107,18 @@ public class TransformTest { - + - + - + Could not find listOpsFileUri document - + @@ -231,7 +233,7 @@ public void keys() throws EXistException, PermissionDeniedException, XPathExcept assertEquals(1, sequence.getItemCount()); final Item item = sequence.itemAt(0); - assertTrue(item instanceof Element); + assertInstanceOf(Element.class, item); final Element dsn_flat = ((Element)item); assertEquals("DSN_FLAT", dsn_flat.getNodeName()); diff --git a/exist-core/src/test/java/org/exist/xquery/functions/util/EvalTest.java b/exist-core/src/test/java/org/exist/xquery/functions/util/EvalTest.java index d64d0342e17..47cc04625de 100644 --- a/exist-core/src/test/java/org/exist/xquery/functions/util/EvalTest.java +++ b/exist-core/src/test/java/org/exist/xquery/functions/util/EvalTest.java @@ -39,11 +39,14 @@ import org.junit.jupiter.api.extension.RegisterExtension; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; + + /** * * @author jim.fuller@webcomposite.com @@ -164,7 +167,7 @@ public void testEvalSupplyingContextAndVariable() throws XPathException, XMLDBEx final String r = (String) result.getResource(0).getContent(); assertEquals("3", r); } - + @org.junit.jupiter.api.Test public void testEvalSupplyingContextItem() throws XPathException, XMLDBException { final String query = """ @@ -176,7 +179,7 @@ public void testEvalSupplyingContextItem() throws XPathException, XMLDBException final String r = (String) result.getResource(0).getContent(); assertEquals("London", r); } - + @org.junit.jupiter.api.Test public void evalInContextWithPreDeclaredNamespace() throws XMLDBException { createCollection("testEvalInContextWithPreDeclaredNamespace"); @@ -188,7 +191,7 @@ public void evalInContextWithPreDeclaredNamespace() throws XMLDBException { "util:eval($q)"; existEmbeddedServer.executeQuery(query); } - + @org.junit.jupiter.api.Test public void evalInContextWithPreDeclaredNamespaceAcrossLocalFunctionBoundary() throws XMLDBException { createCollection("testEvalInContextWithPreDeclaredNamespace"); @@ -203,7 +206,7 @@ public void evalInContextWithPreDeclaredNamespaceAcrossLocalFunctionBoundary() t "local:process($q)"; existEmbeddedServer.executeQuery(query); } - + //should fail with - Error while evaluating expression: /db:article. XPST0081: No namespace defined for prefix db [at line 5, column 9] @org.junit.jupiter.api.Test public void evalInContextWithPreDeclaredNamespaceAcrossModuleBoundary() { @@ -267,7 +270,7 @@ public void evalWithMissingVariableReferenceShouldReportTheSameErrorEachTime() t executeModule(testHome, testModuleName); } catch(final XMLDBException e) { final Throwable cause = e.getCause(); - assertTrue(cause instanceof XPathException); + assertInstanceOf(XPathException.class, cause); assertEquals(ErrorCodes.XPDY0002, ((XPathException) cause).getErrorCode()); } @@ -276,11 +279,11 @@ public void evalWithMissingVariableReferenceShouldReportTheSameErrorEachTime() t executeModule(testHome, testModuleName); } catch(final XMLDBException e) { final Throwable cause = e.getCause(); - assertTrue(cause instanceof XPathException); + assertInstanceOf(XPathException.class, cause); assertEquals(ErrorCodes.XPDY0002, ((XPathException)cause).getErrorCode()); } } - + private Collection createCollection(String collectionName) throws XMLDBException { Collection collection = existEmbeddedServer.getRoot().getChildCollection(collectionName); if (collection == null) { @@ -399,7 +402,7 @@ public void evalPassErrorInfo() { assertTrue(e.getMessage().contains("column 5")); } } - + private void writeModule(Collection collection, String modulename, String module) throws XMLDBException { BinaryResource res = collection.createResource(modulename, BinaryResource.class); ((EXistResource) res).setMimeType("application/xquery"); diff --git a/exist-core/src/test/java/org/exist/xquery/functions/util/ParseHtmlTest.java b/exist-core/src/test/java/org/exist/xquery/functions/util/ParseHtmlTest.java index 9e7fbeefa14..b552eb2dd8f 100644 --- a/exist-core/src/test/java/org/exist/xquery/functions/util/ParseHtmlTest.java +++ b/exist-core/src/test/java/org/exist/xquery/functions/util/ParseHtmlTest.java @@ -37,7 +37,9 @@ import javax.xml.transform.Source; import org.junit.jupiter.api.extension.RegisterExtension; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; public class ParseHtmlTest { @RegisterExtension @@ -51,7 +53,7 @@ public void parseHtml() throws EXistException, PermissionDeniedException, XPathE try (final DBBroker broker = server.getBrokerPool().getBroker()) { final Sequence result = xquery.execute(broker, query, null); assertEquals(1, result.getItemCount()); - assertTrue(result.itemAt(0) instanceof DocumentImpl); + assertInstanceOf(DocumentImpl.class, result.itemAt(0)); final Source expected = Input.fromString("

hello

").build(); final Source actual = Input.fromDocument((DocumentImpl) result.itemAt(0)).build(); diff --git a/exist-core/src/test/java/xquery/CoreTests.java b/exist-core/src/test/java/xquery/CoreTests.java index 345ffcd61be..8163db386de 100644 --- a/exist-core/src/test/java/xquery/CoreTests.java +++ b/exist-core/src/test/java/xquery/CoreTests.java @@ -21,11 +21,9 @@ */ package xquery; -import org.exist.test.runner.XSuite; -import org.junit.runner.RunWith; +import org.exist.test.xqsuite.XQSuite; -@RunWith(XSuite.class) -@XSuite.XSuiteFiles({ +@XQSuite({ "src/test/xquery" }) public class CoreTests { diff --git a/exist-core/src/test/java/xquery/arrays/ArrayTests.java b/exist-core/src/test/java/xquery/arrays/ArrayTests.java index df277f81aa6..162ef518c53 100644 --- a/exist-core/src/test/java/xquery/arrays/ArrayTests.java +++ b/exist-core/src/test/java/xquery/arrays/ArrayTests.java @@ -21,11 +21,9 @@ */ package xquery.arrays; -import org.exist.test.runner.XSuite; -import org.junit.runner.RunWith; +import org.exist.test.xqsuite.XQSuite; -@RunWith(XSuite.class) -@XSuite.XSuiteFiles({ +@XQSuite({ "src/test/xquery/arrays" }) public class ArrayTests { diff --git a/exist-core/src/test/java/xquery/dates/DateTests.java b/exist-core/src/test/java/xquery/dates/DateTests.java index 6b63f055642..12b44d0d2ac 100644 --- a/exist-core/src/test/java/xquery/dates/DateTests.java +++ b/exist-core/src/test/java/xquery/dates/DateTests.java @@ -22,11 +22,9 @@ package xquery.dates; -import org.exist.test.runner.XSuite; -import org.junit.runner.RunWith; +import org.exist.test.xqsuite.XQSuite; -@RunWith(XSuite.class) -@XSuite.XSuiteFiles({ +@XQSuite({ "src/test/xquery/dates" }) public class DateTests { diff --git a/exist-core/src/test/java/xquery/indexing/IndexingTests.java b/exist-core/src/test/java/xquery/indexing/IndexingTests.java index 662739bd79f..1facc44457a 100644 --- a/exist-core/src/test/java/xquery/indexing/IndexingTests.java +++ b/exist-core/src/test/java/xquery/indexing/IndexingTests.java @@ -21,11 +21,9 @@ */ package xquery.indexing; -import org.exist.test.runner.XSuite; -import org.junit.runner.RunWith; +import org.exist.test.xqsuite.XQSuite; -@RunWith(XSuite.class) -@XSuite.XSuiteFiles({ +@XQSuite({ "src/test/xquery/indexing" }) public class IndexingTests { diff --git a/exist-core/src/test/java/xquery/inspect/InspectTests.java b/exist-core/src/test/java/xquery/inspect/InspectTests.java index 7ee25954377..4937937ff5e 100644 --- a/exist-core/src/test/java/xquery/inspect/InspectTests.java +++ b/exist-core/src/test/java/xquery/inspect/InspectTests.java @@ -22,11 +22,9 @@ package xquery.inspect; -import org.exist.test.runner.XSuite; -import org.junit.runner.RunWith; +import org.exist.test.xqsuite.XQSuite; -@RunWith(XSuite.class) -@XSuite.XSuiteFiles({ +@XQSuite({ "src/test/xquery/inspect" }) public class InspectTests { diff --git a/exist-core/src/test/java/xquery/logicalOp/LogicalOpTests.java b/exist-core/src/test/java/xquery/logicalOp/LogicalOpTests.java index f4901afb113..1af0d0ce388 100644 --- a/exist-core/src/test/java/xquery/logicalOp/LogicalOpTests.java +++ b/exist-core/src/test/java/xquery/logicalOp/LogicalOpTests.java @@ -21,11 +21,9 @@ */ package xquery.logicalOp; -import org.exist.test.runner.XSuite; -import org.junit.runner.RunWith; +import org.exist.test.xqsuite.XQSuite; -@RunWith(XSuite.class) -@XSuite.XSuiteFiles({ +@XQSuite({ "src/test/xquery/logicalOp" }) public class LogicalOpTests { diff --git a/exist-core/src/test/java/xquery/maps/MapTests.java b/exist-core/src/test/java/xquery/maps/MapTests.java index bd5f02f9588..02446f4b77d 100644 --- a/exist-core/src/test/java/xquery/maps/MapTests.java +++ b/exist-core/src/test/java/xquery/maps/MapTests.java @@ -21,11 +21,9 @@ */ package xquery.maps; -import org.exist.test.runner.XSuite; -import org.junit.runner.RunWith; +import org.exist.test.xqsuite.XQSuite; -@RunWith(XSuite.class) -@XSuite.XSuiteFiles({ +@XQSuite({ "src/test/xquery/maps" }) public class MapTests { diff --git a/exist-core/src/test/java/xquery/numbers/NumberTests.java b/exist-core/src/test/java/xquery/numbers/NumberTests.java index f970e999e71..6bcc5b0a62e 100644 --- a/exist-core/src/test/java/xquery/numbers/NumberTests.java +++ b/exist-core/src/test/java/xquery/numbers/NumberTests.java @@ -21,11 +21,9 @@ */ package xquery.numbers; -import org.exist.test.runner.XSuite; -import org.junit.runner.RunWith; +import org.exist.test.xqsuite.XQSuite; -@RunWith(XSuite.class) -@XSuite.XSuiteFiles({ +@XQSuite({ "src/test/xquery/numbers" }) public class NumberTests { diff --git a/exist-core/src/test/java/xquery/numericOp/NumericOpTests.java b/exist-core/src/test/java/xquery/numericOp/NumericOpTests.java index 6e952eb779f..86714e63e95 100644 --- a/exist-core/src/test/java/xquery/numericOp/NumericOpTests.java +++ b/exist-core/src/test/java/xquery/numericOp/NumericOpTests.java @@ -21,12 +21,14 @@ */ package xquery.numericOp; -import org.exist.test.runner.XSuite; -import org.junit.runner.RunWith; +import org.exist.test.xqsuite.XQSuite; -@RunWith(XSuite.class) -@XSuite.XSuiteFiles({ +/** + * The files of this suite only evaluate expressions on numbers: none reads or writes the database, so they + * do not depend on each other and can run concurrently. + */ +@XQSuite(value = { "src/test/xquery/numericOp" -}) +}, parallel = true) public class NumericOpTests { } diff --git a/exist-core/src/test/java/xquery/optimizer/OptimizerTests.java b/exist-core/src/test/java/xquery/optimizer/OptimizerTests.java index 9094fd5427c..96c98cc4ecd 100644 --- a/exist-core/src/test/java/xquery/optimizer/OptimizerTests.java +++ b/exist-core/src/test/java/xquery/optimizer/OptimizerTests.java @@ -21,14 +21,12 @@ */ package xquery.optimizer; -import org.exist.test.runner.XSuite; -import org.junit.runner.RunWith; +import org.exist.test.xqsuite.XQSuite; /** * XQuery optimizer tests */ -@RunWith(XSuite.class) -@XSuite.XSuiteFiles({ +@XQSuite({ "src/test/xquery/optimizer" }) public class OptimizerTests { diff --git a/exist-core/src/test/java/xquery/securitymanager/SecurityManagerTests.java b/exist-core/src/test/java/xquery/securitymanager/SecurityManagerTests.java index b571c2cb95d..43db642e4df 100644 --- a/exist-core/src/test/java/xquery/securitymanager/SecurityManagerTests.java +++ b/exist-core/src/test/java/xquery/securitymanager/SecurityManagerTests.java @@ -21,11 +21,9 @@ */ package xquery.securitymanager; -import org.exist.test.runner.XSuite; -import org.junit.runner.RunWith; +import org.exist.test.xqsuite.XQSuite; -@RunWith(XSuite.class) -@XSuite.XSuiteFiles({ +@XQSuite({ "src/test/xquery/securitymanager" }) public class SecurityManagerTests { diff --git a/exist-core/src/test/java/xquery/system/SystemTests.java b/exist-core/src/test/java/xquery/system/SystemTests.java index 10459f0d641..4c716177404 100644 --- a/exist-core/src/test/java/xquery/system/SystemTests.java +++ b/exist-core/src/test/java/xquery/system/SystemTests.java @@ -21,11 +21,9 @@ */ package xquery.system; -import org.exist.test.runner.XSuite; -import org.junit.runner.RunWith; +import org.exist.test.xqsuite.XQSuite; -@RunWith(XSuite.class) -@XSuite.XSuiteFiles({ +@XQSuite({ "src/test/xquery/system" }) public class SystemTests { diff --git a/exist-core/src/test/java/xquery/util/UtilTests.java b/exist-core/src/test/java/xquery/util/UtilTests.java index fa52381a400..a80994b511c 100644 --- a/exist-core/src/test/java/xquery/util/UtilTests.java +++ b/exist-core/src/test/java/xquery/util/UtilTests.java @@ -21,11 +21,9 @@ */ package xquery.util; -import org.exist.test.runner.XSuite; -import org.junit.runner.RunWith; +import org.exist.test.xqsuite.XQSuite; -@RunWith(XSuite.class) -@XSuite.XSuiteFiles({ +@XQSuite({ "src/test/xquery/util" }) public class UtilTests { diff --git a/exist-core/src/test/java/xquery/validation/ValidationTests.java b/exist-core/src/test/java/xquery/validation/ValidationTests.java index d63e78d19e4..4f415b391d6 100644 --- a/exist-core/src/test/java/xquery/validation/ValidationTests.java +++ b/exist-core/src/test/java/xquery/validation/ValidationTests.java @@ -22,11 +22,9 @@ package xquery.validation; -import org.exist.test.runner.XSuite; -import org.junit.runner.RunWith; +import org.exist.test.xqsuite.XQSuite; -@RunWith(XSuite.class) -@XSuite.XSuiteFiles({ +@XQSuite({ "src/test/xquery/validation" }) public class ValidationTests { diff --git a/exist-core/src/test/java/xquery/xinclude/XIncludeTests.java b/exist-core/src/test/java/xquery/xinclude/XIncludeTests.java index a51244e1e0c..2c038061904 100644 --- a/exist-core/src/test/java/xquery/xinclude/XIncludeTests.java +++ b/exist-core/src/test/java/xquery/xinclude/XIncludeTests.java @@ -21,11 +21,9 @@ */ package xquery.xinclude; -import org.exist.test.runner.XSuite; -import org.junit.runner.RunWith; +import org.exist.test.xqsuite.XQSuite; -@RunWith(XSuite.class) -@XSuite.XSuiteFiles({ +@XQSuite({ "src/test/xquery/xinclude" }) public class XIncludeTests { diff --git a/exist-core/src/test/java/xquery/xmldb/XMLDBTests.java b/exist-core/src/test/java/xquery/xmldb/XMLDBTests.java index 166ae6fcace..b3a45ce08eb 100644 --- a/exist-core/src/test/java/xquery/xmldb/XMLDBTests.java +++ b/exist-core/src/test/java/xquery/xmldb/XMLDBTests.java @@ -21,11 +21,9 @@ */ package xquery.xmldb; -import org.exist.test.runner.XSuite; -import org.junit.runner.RunWith; +import org.exist.test.xqsuite.XQSuite; -@RunWith(XSuite.class) -@XSuite.XSuiteFiles({ +@XQSuite({ "src/test/xquery/xmldb" }) public class XMLDBTests { diff --git a/exist-core/src/test/java/xquery/xqsuite/XQSuiteTests.java b/exist-core/src/test/java/xquery/xqsuite/XQSuiteTests.java index ae84b31e7c1..fd49de251d3 100644 --- a/exist-core/src/test/java/xquery/xqsuite/XQSuiteTests.java +++ b/exist-core/src/test/java/xquery/xqsuite/XQSuiteTests.java @@ -22,11 +22,9 @@ package xquery.xqsuite; -import org.exist.test.runner.XSuite; -import org.junit.runner.RunWith; +import org.exist.test.xqsuite.XQSuite; -@RunWith(XSuite.class) -@XSuite.XSuiteFiles({ +@XQSuite({ "src/test/xquery/xqsuite" }) public class XQSuiteTests { diff --git a/exist-core/src/test/java/xquery/xquery3/XQuery3Tests.java b/exist-core/src/test/java/xquery/xquery3/XQuery3Tests.java index 1363a2bd93a..0e12851ec6d 100644 --- a/exist-core/src/test/java/xquery/xquery3/XQuery3Tests.java +++ b/exist-core/src/test/java/xquery/xquery3/XQuery3Tests.java @@ -21,11 +21,9 @@ */ package xquery.xquery3; -import org.exist.test.runner.XSuite; -import org.junit.runner.RunWith; +import org.exist.test.xqsuite.XQSuite; -@RunWith(XSuite.class) -@XSuite.XSuiteFiles({ +@XQSuite({ "src/test/xquery/xquery3", "src/test/xquery/xquery3/transform", "src/test/xquery/transform", diff --git a/exist-core/src/test/resources/META-INF/services/org.junit.platform.engine.TestEngine b/exist-core/src/test/resources/META-INF/services/org.junit.platform.engine.TestEngine new file mode 100644 index 00000000000..d35f1554a4e --- /dev/null +++ b/exist-core/src/test/resources/META-INF/services/org.junit.platform.engine.TestEngine @@ -0,0 +1 @@ +org.exist.test.xqsuite.XQSuiteTestEngine diff --git a/exist-core/src/test/resources/org/exist/test/runner/hyphenated-prefix.xqm b/exist-core/src/test/resources/org/exist/test/runner/hyphenated-prefix.xqm new file mode 100644 index 00000000000..7b66d66dec4 --- /dev/null +++ b/exist-core/src/test/resources/org/exist/test/runner/hyphenated-prefix.xqm @@ -0,0 +1,41 @@ +(: + : eXist-db Open Source Native XML Database + : Copyright (C) 2001 The eXist-db Authors + : + : info@exist-db.org + : http://www.exist-db.org + : + : This library is free software; you can redistribute it and/or + : modify it under the terms of the GNU Lesser General Public + : License as published by the Free Software Foundation; either + : version 2.1 of the License, or (at your option) any later version. + : + : This library is distributed in the hope that it will be useful, + : but WITHOUT ANY WARRANTY; without even the implied warranty of + : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + : Lesser General Public License for more details. + : + : You should have received a copy of the GNU Lesser General Public + : License along with this library; if not, write to the Free Software + : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + :) +xquery version "3.1"; + +(: A prefix containing a hyphen is not made only of word characters, so the XQSuite runtime + : reports the unannotated test as "my-tests:f1" rather than "f1". :) +module namespace my-tests = "http://exist-db.org/xquery/hyphenated-prefix-module"; + +declare namespace test = "http://exist-db.org/xquery/xqsuite"; + +declare + %test:assertEquals("hello") +function my-tests:f1() { + "hello" +}; + +declare + %test:name("explicitly named") + %test:assertEquals("hello") +function my-tests:f2() { + "hello" +}; diff --git a/exist-core/src/main/resources/org/exist/test/runner/xml-test-runner.xq b/exist-core/src/test/resources/org/exist/test/runner/xml-test-runner.xq similarity index 100% rename from exist-core/src/main/resources/org/exist/test/runner/xml-test-runner.xq rename to exist-core/src/test/resources/org/exist/test/runner/xml-test-runner.xq diff --git a/exist-core/src/main/resources/org/exist/test/runner/xquery-discovery.xq b/exist-core/src/test/resources/org/exist/test/runner/xquery-discovery.xq similarity index 78% rename from exist-core/src/main/resources/org/exist/test/runner/xquery-discovery.xq rename to exist-core/src/test/resources/org/exist/test/runner/xquery-discovery.xq index 7ddc929e8ee..5b68207e9d6 100644 --- a/exist-core/src/main/resources/org/exist/test/runner/xquery-discovery.xq +++ b/exist-core/src/test/resources/org/exist/test/runner/xquery-discovery.xq @@ -43,8 +43,16 @@ return attribute namespace { namespace-uri-from-QName($name) }, attribute prefix { prefix-from-QName($name) }, for $f in $test-functions + let $meta := util:inspect-function($f) + let $name-annotation := $meta/annotation[matches(@name, ":name")] return element f { - attribute name { local-name-from-QName(function-name($f)) }, + (: must match test:get-test-name in xqsuite.xql, which names the test in the reports :) + attribute name { + if ($name-annotation) then + string(($name-annotation/value)[1]) + else + replace($meta/@name, "^\w+:([^:]+)$", "$1") + }, attribute arity { function-arity($f) } } ) diff --git a/exist-core/src/main/resources/org/exist/test/runner/xquery-test-runner.xq b/exist-core/src/test/resources/org/exist/test/runner/xquery-test-runner.xq similarity index 100% rename from exist-core/src/main/resources/org/exist/test/runner/xquery-test-runner.xq rename to exist-core/src/test/resources/org/exist/test/runner/xquery-test-runner.xq diff --git a/exist-core/src/test/resources/org/exist/test/runner/hanging.xqm b/exist-core/src/test/resources/org/exist/test/xqsuite/hang.xqm similarity index 76% rename from exist-core/src/test/resources/org/exist/test/runner/hanging.xqm rename to exist-core/src/test/resources/org/exist/test/xqsuite/hang.xqm index 851c0941822..ac7e99f30e0 100644 --- a/exist-core/src/test/resources/org/exist/test/runner/hanging.xqm +++ b/exist-core/src/test/resources/org/exist/test/xqsuite/hang.xqm @@ -21,19 +21,14 @@ :) xquery version "3.1"; -module namespace hang = "http://exist-db.org/xquery/hang"; +module namespace hang = "http://exist-db.org/xquery/xqsuite-fixture/hang"; declare namespace test = "http://exist-db.org/xquery/xqsuite"; -declare function hang:loop() { - hang:loop() -}; - +(: sleeps for ten minutes, unless it is interrupted, in which case util:wait just returns :) declare - %test:assertEquals(1) -function hang:neverReached() { - 1 + %test:assertEquals("woke") +function hang:hangs() { + util:wait(600000), + "woke" }; - -(: Evaluated when module is used; never returns so no ext: callback is ever fired :) -declare variable $hang:blocker := hang:loop(); diff --git a/exist-core/src/test/resources/org/exist/test/xqsuite/wait-a.xqm b/exist-core/src/test/resources/org/exist/test/xqsuite/wait-a.xqm new file mode 100644 index 00000000000..60afb75be64 --- /dev/null +++ b/exist-core/src/test/resources/org/exist/test/xqsuite/wait-a.xqm @@ -0,0 +1,34 @@ +(: + : eXist-db Open Source Native XML Database + : Copyright (C) 2001 The eXist-db Authors + : + : info@exist-db.org + : http://www.exist-db.org + : + : This library is free software; you can redistribute it and/or + : modify it under the terms of the GNU Lesser General Public + : License as published by the Free Software Foundation; either + : version 2.1 of the License, or (at your option) any later version. + : + : This library is distributed in the hope that it will be useful, + : but WITHOUT ANY WARRANTY; without even the implied warranty of + : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + : Lesser General Public License for more details. + : + : You should have received a copy of the GNU Lesser General Public + : License along with this library; if not, write to the Free Software + : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + :) +xquery version "3.1"; + +module namespace wa = "http://exist-db.org/xquery/xqsuite-fixture/wait-a"; + +declare namespace test = "http://exist-db.org/xquery/xqsuite"; + +declare + %test:assertEquals("done") +function wa:waits() { + (: long enough that files run side by side if, and only if, they are run concurrently :) + util:wait(2500), + "done" +}; diff --git a/exist-core/src/test/resources/org/exist/test/xqsuite/wait-b.xqm b/exist-core/src/test/resources/org/exist/test/xqsuite/wait-b.xqm new file mode 100644 index 00000000000..af270b18280 --- /dev/null +++ b/exist-core/src/test/resources/org/exist/test/xqsuite/wait-b.xqm @@ -0,0 +1,34 @@ +(: + : eXist-db Open Source Native XML Database + : Copyright (C) 2001 The eXist-db Authors + : + : info@exist-db.org + : http://www.exist-db.org + : + : This library is free software; you can redistribute it and/or + : modify it under the terms of the GNU Lesser General Public + : License as published by the Free Software Foundation; either + : version 2.1 of the License, or (at your option) any later version. + : + : This library is distributed in the hope that it will be useful, + : but WITHOUT ANY WARRANTY; without even the implied warranty of + : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + : Lesser General Public License for more details. + : + : You should have received a copy of the GNU Lesser General Public + : License along with this library; if not, write to the Free Software + : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + :) +xquery version "3.1"; + +module namespace wb = "http://exist-db.org/xquery/xqsuite-fixture/wait-b"; + +declare namespace test = "http://exist-db.org/xquery/xqsuite"; + +declare + %test:assertEquals("done") +function wb:waits() { + (: long enough that files run side by side if, and only if, they are run concurrently :) + util:wait(2500), + "done" +}; diff --git a/exist-core/src/test/resources/org/exist/test/xqsuite/wait-c.xqm b/exist-core/src/test/resources/org/exist/test/xqsuite/wait-c.xqm new file mode 100644 index 00000000000..4ec41601604 --- /dev/null +++ b/exist-core/src/test/resources/org/exist/test/xqsuite/wait-c.xqm @@ -0,0 +1,34 @@ +(: + : eXist-db Open Source Native XML Database + : Copyright (C) 2001 The eXist-db Authors + : + : info@exist-db.org + : http://www.exist-db.org + : + : This library is free software; you can redistribute it and/or + : modify it under the terms of the GNU Lesser General Public + : License as published by the Free Software Foundation; either + : version 2.1 of the License, or (at your option) any later version. + : + : This library is distributed in the hope that it will be useful, + : but WITHOUT ANY WARRANTY; without even the implied warranty of + : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + : Lesser General Public License for more details. + : + : You should have received a copy of the GNU Lesser General Public + : License along with this library; if not, write to the Free Software + : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + :) +xquery version "3.1"; + +module namespace wc = "http://exist-db.org/xquery/xqsuite-fixture/wait-c"; + +declare namespace test = "http://exist-db.org/xquery/xqsuite"; + +declare + %test:assertEquals("done") +function wc:waits() { + (: long enough that files run side by side if, and only if, they are run concurrently :) + util:wait(2500), + "done" +}; diff --git a/exist-core/src/test/resources/org/exist/test/xqsuite/wait-d.xqm b/exist-core/src/test/resources/org/exist/test/xqsuite/wait-d.xqm new file mode 100644 index 00000000000..26af65031d0 --- /dev/null +++ b/exist-core/src/test/resources/org/exist/test/xqsuite/wait-d.xqm @@ -0,0 +1,34 @@ +(: + : eXist-db Open Source Native XML Database + : Copyright (C) 2001 The eXist-db Authors + : + : info@exist-db.org + : http://www.exist-db.org + : + : This library is free software; you can redistribute it and/or + : modify it under the terms of the GNU Lesser General Public + : License as published by the Free Software Foundation; either + : version 2.1 of the License, or (at your option) any later version. + : + : This library is distributed in the hope that it will be useful, + : but WITHOUT ANY WARRANTY; without even the implied warranty of + : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + : Lesser General Public License for more details. + : + : You should have received a copy of the GNU Lesser General Public + : License along with this library; if not, write to the Free Software + : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + :) +xquery version "3.1"; + +module namespace wd = "http://exist-db.org/xquery/xqsuite-fixture/wait-d"; + +declare namespace test = "http://exist-db.org/xquery/xqsuite"; + +declare + %test:assertEquals("done") +function wd:waits() { + (: long enough that files run side by side if, and only if, they are run concurrently :) + util:wait(2500), + "done" +}; diff --git a/exist-distribution/pom.xml b/exist-distribution/pom.xml index 3161c396fff..5ab44383fb7 100644 --- a/exist-distribution/pom.xml +++ b/exist-distribution/pom.xml @@ -69,12 +69,6 @@ exist-core ${project.version} runtime - - - junit - junit - -
${project.groupId} diff --git a/exist-indexes-jmh/pom.xml b/exist-indexes-jmh/pom.xml index d3f317d1fdb..5fb557f37b3 100644 --- a/exist-indexes-jmh/pom.xml +++ b/exist-indexes-jmh/pom.xml @@ -56,6 +56,13 @@ exist-core ${project.version} + + + ${project.groupId} + exist-core + ${project.version} + test-jar + ${project.groupId} exist-index-ngram @@ -82,6 +89,12 @@ ${jmh.version} provided + + + org.junit.jupiter + junit-jupiter-api + @@ -140,6 +153,8 @@ true org.openjdk.jmh:jmh-generator-annprocess:jar:${jmh.version} + + org.junit.jupiter:junit-jupiter-api:jar:${junit.jupiter.version} + org.junit.jupiter:junit-jupiter-api:jar:${junit.jupiter.version} + diff --git a/exist-parent/pom.xml b/exist-parent/pom.xml index 6fb402a520b..3fbaba337fd 100644 --- a/exist-parent/pom.xml +++ b/exist-parent/pom.xml @@ -638,7 +638,7 @@ junit ${junit.version} - + @@ -652,7 +652,7 @@ hamcrest ${hamcrest.version} - + @@ -1148,7 +1148,6 @@ org.apache.maven.plugins maven-surefire-plugin - 3.6.0 @@ -1161,11 +1160,6 @@ junit-jupiter-engine ${junit.jupiter.version} - - org.junit.vintage - junit-vintage-engine - ${junit.jupiter.version} - org.glassfish.jaxb jaxb-runtime @@ -1347,6 +1341,29 @@ maven-enforcer-plugin 3.6.3 + + ban-junit4 + + enforce + + + + + + + junit:junit:*:*:compile + junit:junit:*:*:provided + junit:junit:*:*:test + org.junit.vintage + com.googlecode.junit-toolbox + + true + JUnit 4 (junit:junit, the Vintage engine, junit-toolbox) must not be used; write tests with JUnit Jupiter. + + + + enforce-java diff --git a/exist-services/pom.xml b/exist-services/pom.xml index b970d8237ae..5b35c4e2b4f 100644 --- a/exist-services/pom.xml +++ b/exist-services/pom.xml @@ -49,12 +49,6 @@ org.exist-db exist-core ${project.version} - - - junit - junit - - diff --git a/exist-xqts/pom.xml b/exist-xqts/pom.xml index cff722a618b..8bd69720c76 100644 --- a/exist-xqts/pom.xml +++ b/exist-xqts/pom.xml @@ -78,6 +78,30 @@ ${project.version} + + ${project.groupId} + exist-core + ${project.version} + test-jar + runtime + + + + + org.junit.jupiter + junit-jupiter-api + runtime + + + junit + junit + runtime + + org.codehaus.mojo.appassembler @@ -90,6 +114,28 @@ + + org.apache.maven.plugins + maven-enforcer-plugin + + + ban-junit4 + + + + + + org.junit.vintage + com.googlecode.junit-toolbox + + true + The Vintage engine and junit-toolbox must not be used; write tests with JUnit Jupiter. + + + + + + org.codehaus.mojo appassembler-maven-plugin diff --git a/extensions/contentextraction/pom.xml b/extensions/contentextraction/pom.xml index 78387a5c6da..00d47368afe 100644 --- a/extensions/contentextraction/pom.xml +++ b/extensions/contentextraction/pom.xml @@ -54,12 +54,14 @@ org.exist-db exist-core ${project.version} - - - junit - junit - - + + + + ${project.groupId} + exist-core + ${project.version} + test-jar + test diff --git a/extensions/debuggee/pom.xml b/extensions/debuggee/pom.xml index 31f7ee23203..a18ab552bfd 100644 --- a/extensions/debuggee/pom.xml +++ b/extensions/debuggee/pom.xml @@ -52,6 +52,14 @@ ${project.version} + + ${project.groupId} + exist-core + ${project.version} + test-jar + test + + org.apache.mina mina-core diff --git a/extensions/exiftool/pom.xml b/extensions/exiftool/pom.xml index ff8886e33c1..52a00233b8c 100644 --- a/extensions/exiftool/pom.xml +++ b/extensions/exiftool/pom.xml @@ -50,12 +50,6 @@ org.exist-db exist-core ${project.version} - - - junit - junit - - diff --git a/extensions/expath/pom.xml b/extensions/expath/pom.xml index cb3e8aa6f56..6235513a995 100644 --- a/extensions/expath/pom.xml +++ b/extensions/expath/pom.xml @@ -50,12 +50,6 @@ org.exist-db exist-core ${project.version} - - - junit - junit - - diff --git a/extensions/exquery/modules/request/pom.xml b/extensions/exquery/modules/request/pom.xml index 0e3727bffcc..13a79043c34 100644 --- a/extensions/exquery/modules/request/pom.xml +++ b/extensions/exquery/modules/request/pom.xml @@ -63,12 +63,6 @@ org.exist-db exist-core ${project.version} - - - junit - junit - - diff --git a/extensions/exquery/restxq/pom.xml b/extensions/exquery/restxq/pom.xml index 6ff3000f030..4bd7c5db63b 100644 --- a/extensions/exquery/restxq/pom.xml +++ b/extensions/exquery/restxq/pom.xml @@ -62,12 +62,6 @@ org.exist-db exist-core ${project.version} - - - junit - junit - - @@ -169,12 +163,6 @@ ${project.version} test-jar test - - - junit - junit - - org.xmlunit @@ -186,12 +174,6 @@ exist-http-client ${project.version} test - - - junit - junit - - org.junit.jupiter @@ -216,11 +198,6 @@ test - - junit - junit - test - @@ -330,7 +307,6 @@ ${project.groupId}:exist-http-client:jar:${project.version} org.eclipse.jetty:jetty-deploy:jar:${jetty.version} org.eclipse.jetty:jetty-jmx:jar:${jetty.version} - junit:junit diff --git a/extensions/exquery/restxq/src/test/java/org/exist/extensions/exquery/restxq/impl/adapters/DomEnhancingNodeProxyAdapterTest.java b/extensions/exquery/restxq/src/test/java/org/exist/extensions/exquery/restxq/impl/adapters/DomEnhancingNodeProxyAdapterTest.java index b649203d9be..9d60742a381 100644 --- a/extensions/exquery/restxq/src/test/java/org/exist/extensions/exquery/restxq/impl/adapters/DomEnhancingNodeProxyAdapterTest.java +++ b/extensions/exquery/restxq/src/test/java/org/exist/extensions/exquery/restxq/impl/adapters/DomEnhancingNodeProxyAdapterTest.java @@ -51,6 +51,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Optional; @@ -178,14 +179,14 @@ private static
void assertProxiedCorrectly(final DocumentImpl nodeProxy.getNode(); // NOTE(AR) causes type of the node proxy to be set // check type of original - assertTrue(nodeProxy instanceof NodeProxy, "Expected instanceof NodeProxy"); + assertInstanceOf(NodeProxy.class, nodeProxy, "Expected instanceof NodeProxy"); assertFalse(domInterfaceType.isInstance(nodeProxy), "Expected not(instanceof " + domInterfaceType.getSimpleName() + ")"); // the function under test final NodeProxy nodeProxyProxy = DomEnhancingNodeProxyAdapter.create(nodeProxy); // check type of proxy - assertTrue(nodeProxyProxy instanceof NodeProxy, "Expected instanceof NodeProxy"); + assertInstanceOf(NodeProxy.class, nodeProxyProxy, "Expected instanceof NodeProxy"); assertTrue(domInterfaceType.isInstance(nodeProxyProxy), "Expected instanceof " + domInterfaceType.getSimpleName() + "; W3C Node type was: " + nodeProxyProxy.getNodeType()); // check W3C DOM Interface methods of proxy diff --git a/extensions/exquery/restxq/src/test/java/xquery/restxq/RestXqTests.java b/extensions/exquery/restxq/src/test/java/xquery/restxq/RestXqTests.java index 6a827a78c91..031eb62b789 100644 --- a/extensions/exquery/restxq/src/test/java/xquery/restxq/RestXqTests.java +++ b/extensions/exquery/restxq/src/test/java/xquery/restxq/RestXqTests.java @@ -26,11 +26,9 @@ */ package xquery.restxq; -import org.exist.test.runner.XSuite; -import org.junit.runner.RunWith; +import org.exist.test.xqsuite.XQSuite; -@RunWith(XSuite.class) -@XSuite.XSuiteFiles({ +@XQSuite({ "src/test/xquery/restxq" }) public class RestXqTests { diff --git a/extensions/exquery/restxq/src/test/resources/junit-platform.properties b/extensions/exquery/restxq/src/test/resources/junit-platform.properties deleted file mode 100644 index 7bded2afd84..00000000000 --- a/extensions/exquery/restxq/src/test/resources/junit-platform.properties +++ /dev/null @@ -1,31 +0,0 @@ -# -# Copyright © 2001, Adam Retter -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of the nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# - -# Parallelism is opt-in per class via @Execution(ExecutionMode.CONCURRENT) -junit.jupiter.execution.parallel.enabled = true -junit.jupiter.execution.parallel.mode.default = same_thread -junit.jupiter.execution.parallel.mode.classes.default = same_thread diff --git a/extensions/indexes/indexes-integration-tests/pom.xml b/extensions/indexes/indexes-integration-tests/pom.xml index dd66dc610df..9a4194c91f3 100644 --- a/extensions/indexes/indexes-integration-tests/pom.xml +++ b/extensions/indexes/indexes-integration-tests/pom.xml @@ -53,12 +53,14 @@ exist-core ${project.version} test - - - junit - junit - - + + + + ${project.groupId} + exist-core + ${project.version} + test-jar + test org.junit.jupiter @@ -90,10 +92,6 @@ org.apache.lucene lucene-queryparser - - junit - junit - @@ -142,11 +140,6 @@ runtime - - junit - junit - test - diff --git a/extensions/indexes/indexes-integration-tests/src/test/java/xquery/indexing/tests/IndexingTests.java b/extensions/indexes/indexes-integration-tests/src/test/java/xquery/indexing/tests/IndexingTests.java index 9419b464f8f..29017c9e53d 100644 --- a/extensions/indexes/indexes-integration-tests/src/test/java/xquery/indexing/tests/IndexingTests.java +++ b/extensions/indexes/indexes-integration-tests/src/test/java/xquery/indexing/tests/IndexingTests.java @@ -21,11 +21,9 @@ */ package xquery.indexing.tests; -import org.exist.test.runner.XSuite; -import org.junit.runner.RunWith; +import org.exist.test.xqsuite.XQSuite; -@RunWith(XSuite.class) -@XSuite.XSuiteFiles({ +@XQSuite({ "src/test/xquery/indexing/tests" }) public class IndexingTests { diff --git a/extensions/indexes/lucene/pom.xml b/extensions/indexes/lucene/pom.xml index 2d24b66cfdc..3fa00385a08 100644 --- a/extensions/indexes/lucene/pom.xml +++ b/extensions/indexes/lucene/pom.xml @@ -50,12 +50,14 @@ org.exist-db exist-core ${project.version} - - - junit - junit - - + + + + ${project.groupId} + exist-core + ${project.version} + test-jar + test @@ -150,12 +152,14 @@ org.xmlunit xmlunit-legacy test - - - junit - junit - - + + + junit + junit + test + @@ -171,11 +175,6 @@ test - - junit - junit - test - @@ -199,6 +198,30 @@ + + org.apache.maven.plugins + maven-enforcer-plugin + + + ban-junit4 + + + + + + junit:junit:*:*:compile + junit:junit:*:*:provided + org.junit.vintage + com.googlecode.junit-toolbox + + true + JUnit 4 must not be used outside the tests that need it for xmlunit-legacy; write tests with JUnit Jupiter. + + + + + + org.apache.maven.plugins @@ -228,6 +251,10 @@ true + + + junit:junit + net.sf.xmldb-org:xmldb-api:jar diff --git a/extensions/indexes/lucene/src/test/java/org/exist/indexing/lucene/AnalyzerConfigTest.java b/extensions/indexes/lucene/src/test/java/org/exist/indexing/lucene/AnalyzerConfigTest.java index 120acec6783..d6c5d04a9f2 100644 --- a/extensions/indexes/lucene/src/test/java/org/exist/indexing/lucene/AnalyzerConfigTest.java +++ b/extensions/indexes/lucene/src/test/java/org/exist/indexing/lucene/AnalyzerConfigTest.java @@ -40,9 +40,9 @@ import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author Adam Retter @@ -133,7 +133,7 @@ public void allParametersIntegerAndSet() throws ParserConfigurationException, IO final AnalyzerConfig.KeyTypedValue extractedConstructorArg2 = extractedConstructorArgs.get(1); assertEquals("punctuationDictionary", extractedConstructorArg2.key()); assertEquals(Set.class, extractedConstructorArg2.valueClass()); - assertTrue(extractedConstructorArg2.value() instanceof HashSet); + assertInstanceOf(HashSet.class, extractedConstructorArg2.value()); assertEquals(2, ((Set) extractedConstructorArg2.value()).size()); } @@ -162,7 +162,7 @@ public void allParametersIntAndSet() throws ParserConfigurationException, IOExce final AnalyzerConfig.KeyTypedValue extractedConstructorArg2 = extractedConstructorArgs.get(1); assertEquals("punctuationDictionary", extractedConstructorArg2.key()); assertEquals(Set.class, extractedConstructorArg2.valueClass()); - assertTrue(extractedConstructorArg2.value() instanceof HashSet); + assertInstanceOf(HashSet.class, extractedConstructorArg2.value()); assertEquals(2, ((Set) extractedConstructorArg2.value()).size()); } @@ -191,7 +191,7 @@ public void allParametersBooleanAndSet() throws ParserConfigurationException, IO final AnalyzerConfig.KeyTypedValue extractedConstructorArg2 = extractedConstructorArgs.get(1); assertEquals("punctuationDictionary", extractedConstructorArg2.key()); assertEquals(Set.class, extractedConstructorArg2.valueClass()); - assertTrue(extractedConstructorArg2.value() instanceof HashSet); + assertInstanceOf(HashSet.class, extractedConstructorArg2.value()); assertEquals(2, ((Set) extractedConstructorArg2.value()).size()); } @@ -220,7 +220,7 @@ public void allParametersPrimitiveBooleanAndSet() throws ParserConfigurationExce final AnalyzerConfig.KeyTypedValue extractedConstructorArg2 = extractedConstructorArgs.get(1); assertEquals("punctuationDictionary", extractedConstructorArg2.key()); assertEquals(Set.class, extractedConstructorArg2.valueClass()); - assertTrue(extractedConstructorArg2.value() instanceof HashSet); + assertInstanceOf(HashSet.class, extractedConstructorArg2.value()); assertEquals(2, ((Set) extractedConstructorArg2.value()).size()); } diff --git a/extensions/indexes/lucene/src/test/java/org/exist/indexing/lucene/LuceneIndexWorkerBatchSizeTest.java b/extensions/indexes/lucene/src/test/java/org/exist/indexing/lucene/LuceneIndexWorkerBatchSizeTest.java index f81f5c5998c..d310a9d7468 100644 --- a/extensions/indexes/lucene/src/test/java/org/exist/indexing/lucene/LuceneIndexWorkerBatchSizeTest.java +++ b/extensions/indexes/lucene/src/test/java/org/exist/indexing/lucene/LuceneIndexWorkerBatchSizeTest.java @@ -30,6 +30,7 @@ import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertTrue; public class LuceneIndexWorkerBatchSizeTest { @@ -62,7 +63,7 @@ public void clampBatchClausesValidValueIsPreserved() { @Test public void reindexDeleteQueryUsesKeywordDocIdAndNodeScopedCanary() { final Query query = LuceneIndexWorker.reindexNodeDeleteQueryForDocIds(List.of(new BytesRef("7"))); - assertTrue(query instanceof BooleanQuery, "Expected BooleanQuery composition"); + assertInstanceOf(BooleanQuery.class, query, "Expected BooleanQuery composition"); final BooleanQuery bq = (BooleanQuery) query; assertEquals(2, bq.clauses().size()); assertEquals(BooleanClause.Occur.MUST, bq.clauses().getFirst().occur()); diff --git a/extensions/indexes/lucene/src/test/java/org/exist/indexing/lucene/SerializeAttrMatchesTest.java b/extensions/indexes/lucene/src/test/java/org/exist/indexing/lucene/SerializeAttrMatchesTest.java index 39db44ef94a..5cdb2fcb641 100644 --- a/extensions/indexes/lucene/src/test/java/org/exist/indexing/lucene/SerializeAttrMatchesTest.java +++ b/extensions/indexes/lucene/src/test/java/org/exist/indexing/lucene/SerializeAttrMatchesTest.java @@ -56,7 +56,8 @@ import org.junit.jupiter.api.extension.RegisterExtension; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; + public class SerializeAttrMatchesTest { @RegisterExtension @@ -98,7 +99,7 @@ public void expandAttr() throws CollectionConfigurationException, LockException, assertEquals(1, seq.getItemCount()); final Item item = seq.itemAt(0); - assertTrue(item instanceof ElementImpl); + assertInstanceOf(ElementImpl.class, item); assertEquals("lemma", ((ElementImpl)item).getAttribute("exist:matches")); } } diff --git a/extensions/indexes/lucene/src/test/java/xquery/lucene/FtAttrContextTests.java b/extensions/indexes/lucene/src/test/java/xquery/lucene/FtAttrContextTests.java index a7bee56d397..72acb64be01 100644 --- a/extensions/indexes/lucene/src/test/java/xquery/lucene/FtAttrContextTests.java +++ b/extensions/indexes/lucene/src/test/java/xquery/lucene/FtAttrContextTests.java @@ -21,8 +21,7 @@ */ package xquery.lucene; -import org.exist.test.runner.XSuite; -import org.junit.runner.RunWith; +import org.exist.test.xqsuite.XQSuite; /** * Runs only the ft-attr-context XQSuite tests. @@ -30,8 +29,7 @@ * * mvn test -Dtest=FtAttrContextTests -pl extensions/indexes/lucene -DfailIfNoTests=false */ -@RunWith(XSuite.class) -@XSuite.XSuiteFiles({ +@XQSuite({ "src/test/xquery/lucene/ft-attr-context.xql" }) public class FtAttrContextTests { diff --git a/extensions/indexes/lucene/src/test/java/xquery/lucene/FtMatchTests.java b/extensions/indexes/lucene/src/test/java/xquery/lucene/FtMatchTests.java index 5c23822894c..12dd8653318 100644 --- a/extensions/indexes/lucene/src/test/java/xquery/lucene/FtMatchTests.java +++ b/extensions/indexes/lucene/src/test/java/xquery/lucene/FtMatchTests.java @@ -21,8 +21,7 @@ */ package xquery.lucene; -import org.exist.test.runner.XSuite; -import org.junit.runner.RunWith; +import org.exist.test.xqsuite.XQSuite; /** * Runs only the ft-match XQSuite tests. @@ -30,8 +29,7 @@ * * mvn -q test -Dtest=FtMatchTests -pl extensions/indexes/lucene -DfailIfNoTests=false -Denforcer.skip=true ... */ -@RunWith(XSuite.class) -@XSuite.XSuiteFiles({ +@XQSuite({ "src/test/xquery/lucene/ft-match.xql" }) public class FtMatchTests { diff --git a/extensions/indexes/lucene/src/test/java/xquery/lucene/LuceneAnalyzersTests.java b/extensions/indexes/lucene/src/test/java/xquery/lucene/LuceneAnalyzersTests.java index fa7e9a450d2..aaa367188e8 100644 --- a/extensions/indexes/lucene/src/test/java/xquery/lucene/LuceneAnalyzersTests.java +++ b/extensions/indexes/lucene/src/test/java/xquery/lucene/LuceneAnalyzersTests.java @@ -21,8 +21,7 @@ */ package xquery.lucene; -import org.exist.test.runner.XSuite; -import org.junit.runner.RunWith; +import org.exist.test.xqsuite.XQSuite; /** * Runs only the analyzers XQSuite tests in isolation. @@ -31,8 +30,7 @@ * * mvn test -Dtest=LuceneAnalyzersTests -pl extensions/indexes/lucene -DfailIfNoTests=false */ -@RunWith(XSuite.class) -@XSuite.XSuiteFiles({ +@XQSuite({ "src/test/xquery/lucene/analyzers-diacritics.xql" }) public class LuceneAnalyzersTests { diff --git a/extensions/indexes/lucene/src/test/java/xquery/lucene/LuceneIndexingTests.java b/extensions/indexes/lucene/src/test/java/xquery/lucene/LuceneIndexingTests.java index e5950c71fed..5583b41bfa7 100644 --- a/extensions/indexes/lucene/src/test/java/xquery/lucene/LuceneIndexingTests.java +++ b/extensions/indexes/lucene/src/test/java/xquery/lucene/LuceneIndexingTests.java @@ -21,8 +21,7 @@ */ package xquery.lucene; -import org.exist.test.runner.XSuite; -import org.junit.runner.RunWith; +import org.exist.test.xqsuite.XQSuite; /** * Runs only the isolated Lucene field indexing XQSuite tests. @@ -30,8 +29,7 @@ * * mvn test -Dtest=LuceneIndexingTests -pl extensions/indexes/lucene -DfailIfNoTests=false */ -@RunWith(XSuite.class) -@XSuite.XSuiteFiles({ +@XQSuite({ "src/test/xquery/lucene/ft-query-field.xqm", "src/test/xquery/lucene/self-axis-index.xqm", "src/test/xquery/lucene/field-expression-context.xqm" diff --git a/extensions/indexes/lucene/src/test/java/xquery/lucene/LuceneTests.java b/extensions/indexes/lucene/src/test/java/xquery/lucene/LuceneTests.java index 6602ce29a5c..96d7e28f03f 100644 --- a/extensions/indexes/lucene/src/test/java/xquery/lucene/LuceneTests.java +++ b/extensions/indexes/lucene/src/test/java/xquery/lucene/LuceneTests.java @@ -21,11 +21,9 @@ */ package xquery.lucene; -import org.exist.test.runner.XSuite; -import org.junit.runner.RunWith; +import org.exist.test.xqsuite.XQSuite; -@RunWith(XSuite.class) -@XSuite.XSuiteFiles({ +@XQSuite({ "src/test/xquery/lucene" }) public class LuceneTests { diff --git a/extensions/indexes/lucene/src/test/java/xquery/lucene/ReindexSecondStoreTest.java b/extensions/indexes/lucene/src/test/java/xquery/lucene/ReindexSecondStoreTest.java index 033be8bd056..4137f6cea71 100644 --- a/extensions/indexes/lucene/src/test/java/xquery/lucene/ReindexSecondStoreTest.java +++ b/extensions/indexes/lucene/src/test/java/xquery/lucene/ReindexSecondStoreTest.java @@ -21,11 +21,9 @@ */ package xquery.lucene; -import org.exist.test.runner.XSuite; -import org.junit.runner.RunWith; +import org.exist.test.xqsuite.XQSuite; -@RunWith(XSuite.class) -@XSuite.XSuiteFiles({ +@XQSuite({ "src/test/xquery/lucene/reindex-second-store.xqm" }) public class ReindexSecondStoreTest { diff --git a/extensions/indexes/lucene/src/test/java/xquery/lucene/VectorSearchTests.java b/extensions/indexes/lucene/src/test/java/xquery/lucene/VectorSearchTests.java index 19f5e904117..274670fc957 100644 --- a/extensions/indexes/lucene/src/test/java/xquery/lucene/VectorSearchTests.java +++ b/extensions/indexes/lucene/src/test/java/xquery/lucene/VectorSearchTests.java @@ -21,8 +21,7 @@ */ package xquery.lucene; -import org.exist.test.runner.XSuite; -import org.junit.runner.RunWith; +import org.exist.test.xqsuite.XQSuite; /** * Runs only the vector search XQSuite tests. @@ -30,8 +29,7 @@ * * mvn -q test -Dtest=VectorSearchTests -pl extensions/indexes/lucene -DfailIfNoTests=false -Denforcer.skip=true ... */ -@RunWith(XSuite.class) -@XSuite.XSuiteFiles({ +@XQSuite({ "src/test/xquery/lucene/vector-search.xqm", "src/test/xquery/lucene/vector-embed.xqm", "src/test/xquery/lucene/query-vector-k.xqm" diff --git a/extensions/indexes/lucene/src/test/resources/junit-platform.properties b/extensions/indexes/lucene/src/test/resources/junit-platform.properties deleted file mode 100644 index f20128033f2..00000000000 --- a/extensions/indexes/lucene/src/test/resources/junit-platform.properties +++ /dev/null @@ -1,26 +0,0 @@ -# -# eXist-db Open Source Native XML Database -# Copyright (C) 2001 The eXist-db Authors -# -# info@exist-db.org -# http://www.exist-db.org -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -# - -# Parallelism is opt-in per class via @Execution(ExecutionMode.CONCURRENT) -junit.jupiter.execution.parallel.enabled = true -junit.jupiter.execution.parallel.mode.default = same_thread -junit.jupiter.execution.parallel.mode.classes.default = same_thread diff --git a/extensions/indexes/ngram/pom.xml b/extensions/indexes/ngram/pom.xml index dcf58dd2493..5d4da2aa96f 100644 --- a/extensions/indexes/ngram/pom.xml +++ b/extensions/indexes/ngram/pom.xml @@ -50,12 +50,14 @@ org.exist-db exist-core ${project.version} - - - junit - junit - - + + + + ${project.groupId} + exist-core + ${project.version} + test-jar + test @@ -72,19 +74,16 @@ org.xmlunit xmlunit-legacy test - - - junit - junit - - - junit junit test + + @@ -105,6 +104,47 @@ true + + + org.apache.maven.plugins + maven-dependency-plugin + + + analyze + + + + junit:junit + + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + ban-junit4 + + + + + + junit:junit:*:*:compile + junit:junit:*:*:provided + org.junit.vintage + com.googlecode.junit-toolbox + + true + JUnit 4 must not be used outside the tests that need it for xmlunit-legacy; write tests with JUnit Jupiter. + + + + + + + diff --git a/extensions/indexes/ngram/src/test/java/xquery/ngram/NgramTests.java b/extensions/indexes/ngram/src/test/java/xquery/ngram/NgramTests.java index b68e35716db..bcf30a87389 100644 --- a/extensions/indexes/ngram/src/test/java/xquery/ngram/NgramTests.java +++ b/extensions/indexes/ngram/src/test/java/xquery/ngram/NgramTests.java @@ -21,11 +21,9 @@ */ package xquery.ngram; -import org.exist.test.runner.XSuite; -import org.junit.runner.RunWith; +import org.exist.test.xqsuite.XQSuite; -@RunWith(XSuite.class) -@XSuite.XSuiteFiles({ +@XQSuite({ "src/test/xquery/ngram" }) public class NgramTests { diff --git a/extensions/indexes/range/pom.xml b/extensions/indexes/range/pom.xml index c6d7351b5c9..77dc3184444 100644 --- a/extensions/indexes/range/pom.xml +++ b/extensions/indexes/range/pom.xml @@ -50,12 +50,14 @@ org.exist-db exist-core ${project.version} - - - junit - junit - - + + + + ${project.groupId} + exist-core + ${project.version} + test-jar + test org.exist-db @@ -146,11 +148,6 @@ test - - junit - junit - test - diff --git a/extensions/indexes/range/src/test/java/xquery/range/RangeTests.java b/extensions/indexes/range/src/test/java/xquery/range/RangeTests.java index b0a2d19ec53..cae8587c0cc 100644 --- a/extensions/indexes/range/src/test/java/xquery/range/RangeTests.java +++ b/extensions/indexes/range/src/test/java/xquery/range/RangeTests.java @@ -21,11 +21,9 @@ */ package xquery.range; -import org.exist.test.runner.XSuite; -import org.junit.runner.RunWith; +import org.exist.test.xqsuite.XQSuite; -@RunWith(XSuite.class) -@XSuite.XSuiteFiles({ +@XQSuite({ "src/test/xquery/range" }) public class RangeTests { diff --git a/extensions/indexes/sort/pom.xml b/extensions/indexes/sort/pom.xml index b8cc20b9431..66bd7970a16 100644 --- a/extensions/indexes/sort/pom.xml +++ b/extensions/indexes/sort/pom.xml @@ -50,23 +50,20 @@ org.exist-db exist-core ${project.version} - - - junit - junit - - + + + + ${project.groupId} + exist-core + ${project.version} + test-jar + test org.apache.logging.log4j log4j-api - - junit - junit - test - diff --git a/extensions/indexes/sort/src/test/java/xquery/sort/SortTests.java b/extensions/indexes/sort/src/test/java/xquery/sort/SortTests.java index 40bb2ff5ba1..dcf5d78c8a5 100644 --- a/extensions/indexes/sort/src/test/java/xquery/sort/SortTests.java +++ b/extensions/indexes/sort/src/test/java/xquery/sort/SortTests.java @@ -21,11 +21,9 @@ */ package xquery.sort; -import org.exist.test.runner.XSuite; -import org.junit.runner.RunWith; +import org.exist.test.xqsuite.XQSuite; -@RunWith(XSuite.class) -@XSuite.XSuiteFiles({ +@XQSuite({ "src/test/xquery/sort" }) public class SortTests { diff --git a/extensions/indexes/spatial/pom.xml b/extensions/indexes/spatial/pom.xml index 9b426445b87..37acaa46441 100644 --- a/extensions/indexes/spatial/pom.xml +++ b/extensions/indexes/spatial/pom.xml @@ -54,12 +54,14 @@ org.exist-db exist-core ${project.version} - - - junit - junit - - + + + + ${project.groupId} + exist-core + ${project.version} + test-jar + test diff --git a/extensions/indexes/vector-it/pom.xml b/extensions/indexes/vector-it/pom.xml index 9243d8b0217..bf180560973 100644 --- a/extensions/indexes/vector-it/pom.xml +++ b/extensions/indexes/vector-it/pom.xml @@ -52,12 +52,14 @@ exist-core ${project.version} test - - - junit - junit - - + + + + ${project.groupId} + exist-core + ${project.version} + test-jar + test org.junit.jupiter @@ -70,12 +72,6 @@ exist-index-lucene ${project.version} runtime - - - junit - junit - - diff --git a/extensions/modules/cache/pom.xml b/extensions/modules/cache/pom.xml index 91dd0c6c446..475f0eed813 100644 --- a/extensions/modules/cache/pom.xml +++ b/extensions/modules/cache/pom.xml @@ -50,12 +50,14 @@ org.exist-db exist-core ${project.version} - - - junit - junit - - + + + + ${project.groupId} + exist-core + ${project.version} + test-jar + test @@ -73,11 +75,6 @@ test - - junit - junit - test - diff --git a/extensions/modules/cache/src/test/java/xquery/modules/cache/CacheTests.java b/extensions/modules/cache/src/test/java/xquery/modules/cache/CacheTests.java index f26b92fa843..6c67c3523cd 100644 --- a/extensions/modules/cache/src/test/java/xquery/modules/cache/CacheTests.java +++ b/extensions/modules/cache/src/test/java/xquery/modules/cache/CacheTests.java @@ -21,11 +21,9 @@ */ package xquery.modules.cache; -import org.exist.test.runner.XSuite; -import org.junit.runner.RunWith; +import org.exist.test.xqsuite.XQSuite; -@RunWith(XSuite.class) -@XSuite.XSuiteFiles({ +@XQSuite({ "src/test/xquery/modules/cache" }) public class CacheTests { diff --git a/extensions/modules/compression/pom.xml b/extensions/modules/compression/pom.xml index 336de6cbf45..d83b501b322 100644 --- a/extensions/modules/compression/pom.xml +++ b/extensions/modules/compression/pom.xml @@ -50,12 +50,14 @@ org.exist-db exist-core ${project.version} - - - junit - junit - - + + + + ${project.groupId} + exist-core + ${project.version} + test-jar + test @@ -83,11 +85,6 @@ org.apache.logging.log4j log4j-api - - junit - junit - test - diff --git a/extensions/modules/compression/src/test/java/xquery/modules/compression/CompressionTests.java b/extensions/modules/compression/src/test/java/xquery/modules/compression/CompressionTests.java index bda0fc719f1..10584412e10 100644 --- a/extensions/modules/compression/src/test/java/xquery/modules/compression/CompressionTests.java +++ b/extensions/modules/compression/src/test/java/xquery/modules/compression/CompressionTests.java @@ -42,11 +42,9 @@ * * $Id$ */ -import org.exist.test.runner.XSuite; -import org.junit.runner.RunWith; +import org.exist.test.xqsuite.XQSuite; -@RunWith(XSuite.class) -@XSuite.XSuiteFiles({ +@XQSuite({ "src/test/xquery/modules/compression" }) public class CompressionTests { diff --git a/extensions/modules/counter/pom.xml b/extensions/modules/counter/pom.xml index 4941b30746e..685fae67cb3 100644 --- a/extensions/modules/counter/pom.xml +++ b/extensions/modules/counter/pom.xml @@ -50,12 +50,14 @@ org.exist-db exist-core ${project.version} - - - junit - junit - - + + + + ${project.groupId} + exist-core + ${project.version} + test-jar + test diff --git a/extensions/modules/cqlparser/pom.xml b/extensions/modules/cqlparser/pom.xml index c9a80b288f7..9c3a12f5d54 100644 --- a/extensions/modules/cqlparser/pom.xml +++ b/extensions/modules/cqlparser/pom.xml @@ -50,12 +50,6 @@ org.exist-db exist-core ${project.version} - - - junit - junit - - diff --git a/extensions/modules/example/pom.xml b/extensions/modules/example/pom.xml index bc26717b90c..1273fde4c76 100644 --- a/extensions/modules/example/pom.xml +++ b/extensions/modules/example/pom.xml @@ -50,12 +50,6 @@ org.exist-db exist-core ${project.version} - - - junit - junit - - diff --git a/extensions/modules/exi/pom.xml b/extensions/modules/exi/pom.xml index 32f741738a7..18135d48f0d 100644 --- a/extensions/modules/exi/pom.xml +++ b/extensions/modules/exi/pom.xml @@ -50,12 +50,6 @@ org.exist-db exist-core ${project.version} - - - junit - junit - - diff --git a/extensions/modules/expathrepo/expathrepo-trigger-test/pom.xml b/extensions/modules/expathrepo/expathrepo-trigger-test/pom.xml index aad76eea3c6..f395f162f28 100644 --- a/extensions/modules/expathrepo/expathrepo-trigger-test/pom.xml +++ b/extensions/modules/expathrepo/expathrepo-trigger-test/pom.xml @@ -59,12 +59,14 @@ exist-core ${project.version} provided - - - junit - junit - - + + + + ${project.groupId} + exist-core + ${project.version} + test-jar + test diff --git a/extensions/modules/expathrepo/pom.xml b/extensions/modules/expathrepo/pom.xml index 3b884428b88..18ba4a0d156 100644 --- a/extensions/modules/expathrepo/pom.xml +++ b/extensions/modules/expathrepo/pom.xml @@ -57,12 +57,14 @@ org.exist-db exist-core ${project.version} - - - junit - junit - - + + + + ${project.groupId} + exist-core + ${project.version} + test-jar + test @@ -96,11 +98,6 @@ test - - junit - junit - test - diff --git a/extensions/modules/expathrepo/src/test/java/xquery/modules/expathrepo/ExpathRepoTests.java b/extensions/modules/expathrepo/src/test/java/xquery/modules/expathrepo/ExpathRepoTests.java index 597dea4adc2..164df13c5e6 100644 --- a/extensions/modules/expathrepo/src/test/java/xquery/modules/expathrepo/ExpathRepoTests.java +++ b/extensions/modules/expathrepo/src/test/java/xquery/modules/expathrepo/ExpathRepoTests.java @@ -21,11 +21,9 @@ */ package xquery.modules.expathrepo; -import org.exist.test.runner.XSuite; -import org.junit.runner.RunWith; +import org.exist.test.xqsuite.XQSuite; -@RunWith(XSuite.class) -@XSuite.XSuiteFiles({ +@XQSuite({ "src/test/xquery/modules/expathrepo" }) public class ExpathRepoTests { diff --git a/extensions/modules/file/pom.xml b/extensions/modules/file/pom.xml index 5e5a0ced38f..f8444cbd74b 100644 --- a/extensions/modules/file/pom.xml +++ b/extensions/modules/file/pom.xml @@ -50,12 +50,6 @@ org.exist-db exist-core ${project.version} - - - junit - junit - - @@ -130,12 +124,6 @@ ${project.version} test-jar test - - - junit - junit - - @@ -151,11 +139,6 @@ test - - junit - junit - test - diff --git a/extensions/modules/file/src/test/java/xquery/modules/file/FileTests.java b/extensions/modules/file/src/test/java/xquery/modules/file/FileTests.java index f3abd2ce42c..9245dcc6f5f 100644 --- a/extensions/modules/file/src/test/java/xquery/modules/file/FileTests.java +++ b/extensions/modules/file/src/test/java/xquery/modules/file/FileTests.java @@ -21,11 +21,9 @@ */ package xquery.modules.file; -import org.exist.test.runner.XSuite; -import org.junit.runner.RunWith; +import org.exist.test.xqsuite.XQSuite; -@RunWith(XSuite.class) -@XSuite.XSuiteFiles({ +@XQSuite({ "src/test/xquery/modules/file" }) public class FileTests { diff --git a/extensions/modules/http-client/pom.xml b/extensions/modules/http-client/pom.xml index 728660f0d55..99178217967 100644 --- a/extensions/modules/http-client/pom.xml +++ b/extensions/modules/http-client/pom.xml @@ -50,12 +50,6 @@ org.exist-db exist-core ${project.version} - - - junit - junit - - @@ -91,12 +85,6 @@ ${project.version} test-jar test - - - junit - junit - - org.exist-db @@ -130,11 +118,6 @@ commons-io test - - junit - junit - test - diff --git a/extensions/modules/http-client/src/test/java/org/exist/xquery/modules/httpclient/xquery/HttpClientXqueryTests.java b/extensions/modules/http-client/src/test/java/org/exist/xquery/modules/httpclient/xquery/HttpClientXqueryTests.java index 8551d143b65..fe715cbce6e 100644 --- a/extensions/modules/http-client/src/test/java/org/exist/xquery/modules/httpclient/xquery/HttpClientXqueryTests.java +++ b/extensions/modules/http-client/src/test/java/org/exist/xquery/modules/httpclient/xquery/HttpClientXqueryTests.java @@ -21,53 +21,52 @@ */ package org.exist.xquery.modules.httpclient.xquery; -import com.github.tomakehurst.wiremock.junit.WireMockRule; -import org.exist.test.runner.XSuite; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.ClassRule; -import org.junit.runner.RunWith; +import com.github.tomakehurst.wiremock.WireMockServer; +import org.exist.test.xqsuite.XQSuite; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import static com.github.tomakehurst.wiremock.client.WireMock.*; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; -@RunWith(XSuite.class) -@XSuite.XSuiteFiles({ +@XQSuite({ "src/test/xquery" }) public class HttpClientXqueryTests { - @ClassRule - public static final WireMockRule wireMockRule = new WireMockRule(wireMockConfig().dynamicPort()); + private static WireMockServer wireMock; - @BeforeClass + @BeforeAll public static void setup() { + wireMock = new WireMockServer(wireMockConfig().dynamicPort()); + wireMock.start(); + // Configure some default endpoints - wireMockRule.stubFor(get(urlEqualTo("/hello")) + wireMock.stubFor(get(urlEqualTo("/hello")) .willReturn(aResponse() .withStatus(200) .withHeader("Content-Type", "text/plain") .withBody("Hello world!"))); - wireMockRule.stubFor(get(urlEqualTo("/json")) + wireMock.stubFor(get(urlEqualTo("/json")) .willReturn(aResponse() .withStatus(200) .withHeader("Content-Type", "application/json") .withBody("{\"message\": \"Hello JSON\"}"))); - wireMockRule.stubFor(get(urlEqualTo("/xml")) + wireMock.stubFor(get(urlEqualTo("/xml")) .willReturn(aResponse() .withStatus(200) .withHeader("Content-Type", "application/xml") .withBody("Hello XML"))); - wireMockRule.stubFor(post(urlEqualTo("/post")) + wireMock.stubFor(post(urlEqualTo("/post")) .willReturn(aResponse() .withStatus(201) .withHeader("Content-Type", "text/plain") .withBody("ACK"))); - wireMockRule.stubFor(get(urlEqualTo("/headers")) + wireMock.stubFor(get(urlEqualTo("/headers")) .willReturn(aResponse() .withStatus(200) .withHeader("X-Custom-Header", "X-Value") @@ -75,11 +74,12 @@ public static void setup() { .withBody("Check headers"))); // Expose port to XQuery via system property - System.setProperty("wiremock.port", String.valueOf(wireMockRule.port())); + System.setProperty("wiremock.port", String.valueOf(wireMock.port())); } - @AfterClass + @AfterAll public static void teardown() { System.clearProperty("wiremock.port"); + wireMock.stop(); } } diff --git a/extensions/modules/image/pom.xml b/extensions/modules/image/pom.xml index 4ec44076f32..f67b52e68fb 100644 --- a/extensions/modules/image/pom.xml +++ b/extensions/modules/image/pom.xml @@ -50,12 +50,14 @@ org.exist-db exist-core ${project.version} - - - junit - junit - - + + + + ${project.groupId} + exist-core + ${project.version} + test-jar + test @@ -80,11 +82,6 @@ test - - junit - junit - test - diff --git a/extensions/modules/image/src/test/java/xquery/modules/image/ImageTests.java b/extensions/modules/image/src/test/java/xquery/modules/image/ImageTests.java index 182d5c09e9d..e45a77b7e37 100644 --- a/extensions/modules/image/src/test/java/xquery/modules/image/ImageTests.java +++ b/extensions/modules/image/src/test/java/xquery/modules/image/ImageTests.java @@ -21,11 +21,9 @@ */ package xquery.modules.image; -import org.exist.test.runner.XSuite; -import org.junit.runner.RunWith; +import org.exist.test.xqsuite.XQSuite; -@RunWith(XSuite.class) -@XSuite.XSuiteFiles({ +@XQSuite({ "src/test/xquery/modules/image" }) public class ImageTests { diff --git a/extensions/modules/jndi/pom.xml b/extensions/modules/jndi/pom.xml index 5e65e6ba495..d1c54a7fdef 100644 --- a/extensions/modules/jndi/pom.xml +++ b/extensions/modules/jndi/pom.xml @@ -50,12 +50,6 @@ org.exist-db exist-core ${project.version} - - - junit - junit - - diff --git a/extensions/modules/mail/pom.xml b/extensions/modules/mail/pom.xml index 79cdfea5c25..0c49d148957 100644 --- a/extensions/modules/mail/pom.xml +++ b/extensions/modules/mail/pom.xml @@ -57,12 +57,14 @@ org.exist-db exist-core ${project.version} - - - junit - junit - - + + + + ${project.groupId} + exist-core + ${project.version} + test-jar + test @@ -143,6 +145,10 @@ com.sun.activation jakarta.activation + + junit + junit + diff --git a/extensions/modules/mail/src/test/java/org/exist/xquery/modules/mail/SendEmailIT.java b/extensions/modules/mail/src/test/java/org/exist/xquery/modules/mail/SendEmailIT.java index 54123438f3f..994ffe0bd56 100644 --- a/extensions/modules/mail/src/test/java/org/exist/xquery/modules/mail/SendEmailIT.java +++ b/extensions/modules/mail/src/test/java/org/exist/xquery/modules/mail/SendEmailIT.java @@ -74,6 +74,7 @@ import static org.exist.xquery.modules.mail.Util.withCompiledQuery; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertTrue; /** @@ -166,7 +167,7 @@ public void sendTextEmailWithXmlAttachment(SmtpImplementation smtpImplementation assertTrue(receivedMessage.getContentType().startsWith("multipart/mixed")); final Object content = receivedMessage.getContent(); - assertTrue(content instanceof MimeMultipart); + assertInstanceOf(MimeMultipart.class, content); final MimeMultipart multipartContent = (MimeMultipart) content; assertEquals(2, multipartContent.getCount()); @@ -194,7 +195,7 @@ public void sendTextEmailWithBinaryAttachment(SmtpImplementation smtpImplementat assertTrue(receivedMessage.getContentType().startsWith("multipart/mixed")); final Object content = receivedMessage.getContent(); - assertTrue(content instanceof MimeMultipart); + assertInstanceOf(MimeMultipart.class, content); final MimeMultipart multipartContent = (MimeMultipart) content; assertEquals(2, multipartContent.getCount()); @@ -223,7 +224,7 @@ public void sendTextEmailWithXmlAndBinaryAttachments(SmtpImplementation smtpImpl assertTrue(receivedMessage.getContentType().startsWith("multipart/mixed")); final Object content = receivedMessage.getContent(); - assertTrue(content instanceof MimeMultipart); + assertInstanceOf(MimeMultipart.class, content); final MimeMultipart multipartContent = (MimeMultipart) content; assertEquals(3, multipartContent.getCount()); @@ -278,7 +279,7 @@ public void sendHtmlEmailWithXmlAttachment(SmtpImplementation smtpImplementation assertTrue(receivedMessage.getContentType().startsWith("multipart/mixed")); final Object content = receivedMessage.getContent(); - assertTrue(content instanceof MimeMultipart); + assertInstanceOf(MimeMultipart.class, content); final MimeMultipart multipartContent = (MimeMultipart) content; assertEquals(2, multipartContent.getCount()); @@ -311,7 +312,7 @@ public void sendHtmlEmailWithBinaryAttachment(SmtpImplementation smtpImplementat assertTrue(receivedMessage.getContentType().startsWith("multipart/mixed")); final Object content = receivedMessage.getContent(); - assertTrue(content instanceof MimeMultipart); + assertInstanceOf(MimeMultipart.class, content); final MimeMultipart multipartContent = (MimeMultipart) content; assertEquals(2, multipartContent.getCount()); @@ -345,7 +346,7 @@ public void sendHtmlEmailWithXmlAndBinaryAttachments(SmtpImplementation smtpImpl assertTrue(receivedMessage.getContentType().startsWith("multipart/mixed")); final Object content = receivedMessage.getContent(); - assertTrue(content instanceof MimeMultipart); + assertInstanceOf(MimeMultipart.class, content); final MimeMultipart multipartContent = (MimeMultipart) content; assertEquals(3, multipartContent.getCount()); @@ -384,7 +385,7 @@ public void sendTextAndHtmlEmail(SmtpImplementation smtpImplementation, Authenti assertTrue(receivedMessage.getContentType().startsWith("multipart/alternative")); final Object content = receivedMessage.getContent(); - assertTrue(content instanceof MimeMultipart); + assertInstanceOf(MimeMultipart.class, content); final MimeMultipart multipartContent = (MimeMultipart) content; assertEquals(2, multipartContent.getCount()); @@ -420,14 +421,14 @@ public void sendTextAndHtmlEmailWithXmlAttachment(SmtpImplementation smtpImpleme assertTrue(receivedMessage.getContentType().startsWith("multipart/mixed")); final Object content = receivedMessage.getContent(); - assertTrue(content instanceof MimeMultipart); + assertInstanceOf(MimeMultipart.class, content); final MimeMultipart multipartContent = (MimeMultipart) content; assertEquals(2, multipartContent.getCount()); final BodyPart firstPart = multipartContent.getBodyPart(0); assertTrue(firstPart.getContentType().startsWith("multipart/alternative")); final Object firstPartContent = firstPart.getContent(); - assertTrue(firstPartContent instanceof MimeMultipart); + assertInstanceOf(MimeMultipart.class, firstPartContent); final MimeMultipart multipartFirstPartContent = (MimeMultipart) firstPartContent; assertEquals(2, multipartFirstPartContent.getCount()); @@ -468,14 +469,14 @@ public void sendTextAndHtmlEmailWithBinaryAttachment(SmtpImplementation smtpImpl assertTrue(receivedMessage.getContentType().startsWith("multipart/mixed")); final Object content = receivedMessage.getContent(); - assertTrue(content instanceof MimeMultipart); + assertInstanceOf(MimeMultipart.class, content); final MimeMultipart multipartContent = (MimeMultipart) content; assertEquals(2, multipartContent.getCount()); final BodyPart firstPart = multipartContent.getBodyPart(0); assertTrue(firstPart.getContentType().startsWith("multipart/alternative")); final Object firstPartContent = firstPart.getContent(); - assertTrue(firstPartContent instanceof MimeMultipart); + assertInstanceOf(MimeMultipart.class, firstPartContent); final MimeMultipart multipartFirstPartContent = (MimeMultipart) firstPartContent; assertEquals(2, multipartFirstPartContent.getCount()); @@ -517,14 +518,14 @@ public void sendTextAndHtmlEmailWithXmlAndBinaryAttachments(SmtpImplementation s assertTrue(receivedMessage.getContentType().startsWith("multipart/mixed")); final Object content = receivedMessage.getContent(); - assertTrue(content instanceof MimeMultipart); + assertInstanceOf(MimeMultipart.class, content); final MimeMultipart multipartContent = (MimeMultipart) content; assertEquals(3, multipartContent.getCount()); final BodyPart firstPart = multipartContent.getBodyPart(0); assertTrue(firstPart.getContentType().startsWith("multipart/alternative")); final Object firstPartContent = firstPart.getContent(); - assertTrue(firstPartContent instanceof MimeMultipart); + assertInstanceOf(MimeMultipart.class, firstPartContent); final MimeMultipart multipartFirstPartContent = (MimeMultipart) firstPartContent; assertEquals(2, multipartFirstPartContent.getCount()); diff --git a/extensions/modules/persistentlogin/pom.xml b/extensions/modules/persistentlogin/pom.xml index e35c270b186..26c6b32f4fc 100644 --- a/extensions/modules/persistentlogin/pom.xml +++ b/extensions/modules/persistentlogin/pom.xml @@ -50,12 +50,14 @@ org.exist-db exist-core ${project.version} - - - junit - junit - - + + + + ${project.groupId} + exist-core + ${project.version} + test-jar + test diff --git a/extensions/modules/process/pom.xml b/extensions/modules/process/pom.xml index d2aac762b22..39bb636f0f2 100644 --- a/extensions/modules/process/pom.xml +++ b/extensions/modules/process/pom.xml @@ -50,12 +50,6 @@ org.exist-db exist-core ${project.version} - - - junit - junit - - diff --git a/extensions/modules/scheduler/pom.xml b/extensions/modules/scheduler/pom.xml index a652962e15f..9bb38aab65d 100644 --- a/extensions/modules/scheduler/pom.xml +++ b/extensions/modules/scheduler/pom.xml @@ -50,12 +50,6 @@ org.exist-db exist-core ${project.version} - - - junit - junit - - diff --git a/extensions/modules/simpleql/pom.xml b/extensions/modules/simpleql/pom.xml index a4fac4b2d59..57c67974fe7 100644 --- a/extensions/modules/simpleql/pom.xml +++ b/extensions/modules/simpleql/pom.xml @@ -50,12 +50,6 @@ org.exist-db exist-core ${project.version} - - - junit - junit - - diff --git a/extensions/modules/sql-oracle/pom.xml b/extensions/modules/sql-oracle/pom.xml index c1c8ea22db6..dad26e85c90 100644 --- a/extensions/modules/sql-oracle/pom.xml +++ b/extensions/modules/sql-oracle/pom.xml @@ -49,12 +49,6 @@ org.exist-db exist-core ${project.version} - - - junit - junit - - diff --git a/extensions/modules/sql/pom.xml b/extensions/modules/sql/pom.xml index 5bcbabc2b01..cdc5e823f8b 100644 --- a/extensions/modules/sql/pom.xml +++ b/extensions/modules/sql/pom.xml @@ -50,12 +50,14 @@ org.exist-db exist-core ${project.version} - - - junit - junit - - + + + + ${project.groupId} + exist-core + ${project.version} + test-jar + test diff --git a/extensions/modules/sql/src/test/java/org/exist/xquery/modules/sql/ConnectionIT.java b/extensions/modules/sql/src/test/java/org/exist/xquery/modules/sql/ConnectionIT.java index dc1ffc93027..abb30417324 100644 --- a/extensions/modules/sql/src/test/java/org/exist/xquery/modules/sql/ConnectionIT.java +++ b/extensions/modules/sql/src/test/java/org/exist/xquery/modules/sql/ConnectionIT.java @@ -64,10 +64,11 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static org.exist.xquery.modules.sql.Util.executeQuery; import static org.exist.xquery.modules.sql.Util.withCompiledQuery; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * SQL Connection Integration Tests. @@ -101,7 +102,7 @@ public void getConnectionIsAutomaticallyClosed() throws EXistException, XPathExc // check that the handle for the sql connection that was created was valid assertEquals(1, result.getItemCount()); - assertTrue(result.itemAt(0) instanceof IntegerValue); + assertInstanceOf(IntegerValue.class, result.itemAt(0)); assertEquals(Type.LONG, result.itemAt(0).getType()); final long connectionHandle = result.itemAt(0).toJavaObject(long.class); assertFalse(connectionHandle == 0); @@ -148,17 +149,17 @@ public void getConnectionFromModuleIsAutomaticallyClosed() throws EXistException // get the context of the library module final org.exist.xquery.Module[] libraryModules = mainQueryContext.getModules("http://mymodule.com"); assertEquals(1, libraryModules.length); - assertTrue(libraryModules[0] instanceof ExternalModule); + assertInstanceOf(ExternalModule.class, libraryModules[0]); final ExternalModule libraryModule = (ExternalModule) libraryModules[0]; final XQueryContext libraryQueryContext = libraryModule.getContext(); - assertTrue(libraryQueryContext instanceof ModuleContext); + assertInstanceOf(ModuleContext.class, libraryQueryContext); // execute the query final Sequence result = executeQuery(broker, mainCompiledQuery); // check that the handle for the sql connection that was created was valid assertEquals(1, result.getItemCount()); - assertTrue(result.itemAt(0) instanceof IntegerValue); + assertInstanceOf(IntegerValue.class, result.itemAt(0)); assertEquals(Type.LONG, result.itemAt(0).getType()); final long connectionHandle = result.itemAt(0).toJavaObject(long.class); assertFalse(connectionHandle == 0); diff --git a/extensions/modules/sql/src/test/java/org/exist/xquery/modules/sql/ConnectionPoolIT.java b/extensions/modules/sql/src/test/java/org/exist/xquery/modules/sql/ConnectionPoolIT.java index 10014815a4a..59f1bb25d89 100644 --- a/extensions/modules/sql/src/test/java/org/exist/xquery/modules/sql/ConnectionPoolIT.java +++ b/extensions/modules/sql/src/test/java/org/exist/xquery/modules/sql/ConnectionPoolIT.java @@ -56,9 +56,9 @@ import static com.evolvedbinary.j8fu.tuple.Tuple.Tuple; import static org.exist.xquery.modules.sql.Util.executeQuery; import static org.exist.xquery.modules.sql.Util.withCompiledQuery; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; public class ConnectionPoolIT { @RegisterExtension @@ -86,7 +86,7 @@ public void getConnectionFromPoolIsAutomaticallyClosed() throws EXistException, // check that the handle for the sql connection that was created was valid assertEquals(1, result.getItemCount()); - assertTrue(result.itemAt(0) instanceof IntegerValue); + assertInstanceOf(IntegerValue.class, result.itemAt(0)); assertEquals(Type.LONG, result.itemAt(0).getType()); final long connectionHandle = result.itemAt(0).toJavaObject(long.class); assertFalse(connectionHandle == 0); diff --git a/extensions/modules/sql/src/test/java/org/exist/xquery/modules/sql/ExecuteIT.java b/extensions/modules/sql/src/test/java/org/exist/xquery/modules/sql/ExecuteIT.java index bacc649eaec..7ad727a6105 100644 --- a/extensions/modules/sql/src/test/java/org/exist/xquery/modules/sql/ExecuteIT.java +++ b/extensions/modules/sql/src/test/java/org/exist/xquery/modules/sql/ExecuteIT.java @@ -45,7 +45,7 @@ import static org.exist.xquery.modules.sql.Util.executeQuery; import static org.exist.xquery.modules.sql.Util.withCompiledQuery; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; /** * SQL Execute Integration Tests. @@ -90,7 +90,7 @@ private void executeForNS(final String namespace, final String prefix) throws EX // check that the namespace of the result element is in the 'sql' namespace assertEquals(1, result.getItemCount()); - assertTrue(result.itemAt(0) instanceof Element); + assertInstanceOf(Element.class, result.itemAt(0)); assertEquals(Type.ELEMENT, result.itemAt(0).getType()); final Element element = (ElementImpl) result.itemAt(0); diff --git a/extensions/modules/sql/src/test/java/org/exist/xquery/modules/sql/ImplicitConnectionCloseIT.java b/extensions/modules/sql/src/test/java/org/exist/xquery/modules/sql/ImplicitConnectionCloseIT.java index 42c7b0cbd2a..e4b50b92d33 100644 --- a/extensions/modules/sql/src/test/java/org/exist/xquery/modules/sql/ImplicitConnectionCloseIT.java +++ b/extensions/modules/sql/src/test/java/org/exist/xquery/modules/sql/ImplicitConnectionCloseIT.java @@ -79,10 +79,11 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static org.exist.xquery.modules.sql.Util.executeQuery; import static org.exist.xquery.modules.sql.Util.withCompiledQuery; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * Uses JNDI to provide a StubDataSourceFactory @@ -146,7 +147,7 @@ public void getJndiConnectionIsAutomaticallyClosed() throws EXistException, XPat // check that the handle for the sql connection that was created was valid assertEquals(1, result.getItemCount()); - assertTrue(result.itemAt(0) instanceof IntegerValue); + assertInstanceOf(IntegerValue.class, result.itemAt(0)); assertEquals(Type.LONG, result.itemAt(0).getType()); final long connectionHandle = result.itemAt(0).toJavaObject(long.class); assertFalse(connectionHandle == 0); @@ -201,17 +202,17 @@ public void getJndiConnectionFromModuleIsAutomaticallyClosed() throws EXistExcep // get the context of the library module final org.exist.xquery.Module[] libraryModules = mainQueryContext.getModules("http://mymodule.com"); assertEquals(1, libraryModules.length); - assertTrue(libraryModules[0] instanceof ExternalModule); + assertInstanceOf(ExternalModule.class, libraryModules[0]); final ExternalModule libraryModule = (ExternalModule) libraryModules[0]; final XQueryContext libraryQueryContext = libraryModule.getContext(); - assertTrue(libraryQueryContext instanceof ModuleContext); + assertInstanceOf(ModuleContext.class, libraryQueryContext); // execute the query final Sequence result = executeQuery(broker, mainCompiledQuery); // check that the handle for the sql connection that was created was valid assertEquals(1, result.getItemCount()); - assertTrue(result.itemAt(0) instanceof IntegerValue); + assertInstanceOf(IntegerValue.class, result.itemAt(0)); assertEquals(Type.LONG, result.itemAt(0).getType()); final long connectionHandle = result.itemAt(0).toJavaObject(long.class); assertFalse(connectionHandle == 0); diff --git a/extensions/modules/sql/src/test/java/org/exist/xquery/modules/sql/JndiConnectionIT.java b/extensions/modules/sql/src/test/java/org/exist/xquery/modules/sql/JndiConnectionIT.java index c0215107a6f..cb629bc898c 100644 --- a/extensions/modules/sql/src/test/java/org/exist/xquery/modules/sql/JndiConnectionIT.java +++ b/extensions/modules/sql/src/test/java/org/exist/xquery/modules/sql/JndiConnectionIT.java @@ -73,9 +73,9 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static org.exist.xquery.modules.sql.Util.executeQuery; import static org.exist.xquery.modules.sql.Util.withCompiledQuery; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotSame; /** @@ -136,7 +136,7 @@ public void getJndiConnectionIsAutomaticallyClosed() throws EXistException, XPat // check that the handle for the sql connection that was created was valid assertEquals(1, result.getItemCount()); - assertTrue(result.itemAt(0) instanceof IntegerValue); + assertInstanceOf(IntegerValue.class, result.itemAt(0)); assertEquals(Type.LONG, result.itemAt(0).getType()); final long connectionHandle = result.itemAt(0).toJavaObject(long.class); assertFalse(connectionHandle == 0); @@ -182,17 +182,17 @@ public void getJndiConnectionFromModuleIsAutomaticallyClosed() throws EXistExcep // get the context of the library module final org.exist.xquery.Module[] libraryModules = mainQueryContext.getModules("http://mymodule.com"); assertEquals(1, libraryModules.length); - assertTrue(libraryModules[0] instanceof ExternalModule); + assertInstanceOf(ExternalModule.class, libraryModules[0]); final ExternalModule libraryModule = (ExternalModule) libraryModules[0]; final XQueryContext libraryQueryContext = libraryModule.getContext(); - assertTrue(libraryQueryContext instanceof ModuleContext); + assertInstanceOf(ModuleContext.class, libraryQueryContext); // execute the query final Sequence result = executeQuery(broker, mainCompiledQuery); // check that the handle for the sql connection that was created was valid assertEquals(1, result.getItemCount()); - assertTrue(result.itemAt(0) instanceof IntegerValue); + assertInstanceOf(IntegerValue.class, result.itemAt(0)); assertEquals(Type.LONG, result.itemAt(0).getType()); final long connectionHandle = result.itemAt(0).toJavaObject(long.class); assertFalse(connectionHandle == 0); diff --git a/extensions/modules/xmldiff/pom.xml b/extensions/modules/xmldiff/pom.xml index c047b8cd19b..f705ec41ade 100644 --- a/extensions/modules/xmldiff/pom.xml +++ b/extensions/modules/xmldiff/pom.xml @@ -50,12 +50,14 @@ org.exist-db exist-core ${project.version} - - - junit - junit - - + + + + ${project.groupId} + exist-core + ${project.version} + test-jar + test @@ -78,11 +80,6 @@ icu4j - - junit - junit - test - diff --git a/extensions/modules/xmldiff/src/test/java/xquery/modules/xmldiff/XmlDiffTests.java b/extensions/modules/xmldiff/src/test/java/xquery/modules/xmldiff/XmlDiffTests.java index 54fb7c2bec4..477251388f0 100644 --- a/extensions/modules/xmldiff/src/test/java/xquery/modules/xmldiff/XmlDiffTests.java +++ b/extensions/modules/xmldiff/src/test/java/xquery/modules/xmldiff/XmlDiffTests.java @@ -32,11 +32,9 @@ */ package xquery.modules.xmldiff; -import org.exist.test.runner.XSuite; -import org.junit.runner.RunWith; +import org.exist.test.xqsuite.XQSuite; -@RunWith(XSuite.class) -@XSuite.XSuiteFiles({ +@XQSuite({ "src/test/xquery/modules/xmldiff" }) public class XmlDiffTests { diff --git a/extensions/modules/xslfo/pom.xml b/extensions/modules/xslfo/pom.xml index bd4f0cbbc54..c89cad5c469 100644 --- a/extensions/modules/xslfo/pom.xml +++ b/extensions/modules/xslfo/pom.xml @@ -55,12 +55,14 @@ org.exist-db exist-core ${project.version} - - - junit - junit - - + + + + ${project.groupId} + exist-core + ${project.version} + test-jar + test diff --git a/extensions/security/activedirectory/pom.xml b/extensions/security/activedirectory/pom.xml index 5140a40a0c1..1c7c616ff8e 100644 --- a/extensions/security/activedirectory/pom.xml +++ b/extensions/security/activedirectory/pom.xml @@ -50,12 +50,6 @@ org.exist-db exist-core ${project.version} - - - junit - junit - - org.exist-db diff --git a/extensions/security/iprange/pom.xml b/extensions/security/iprange/pom.xml index edfb305b316..e46d71e21ce 100644 --- a/extensions/security/iprange/pom.xml +++ b/extensions/security/iprange/pom.xml @@ -50,12 +50,6 @@ org.exist-db exist-core ${project.version} - - - junit - junit - - diff --git a/extensions/security/ldap/pom.xml b/extensions/security/ldap/pom.xml index 64d30741452..b479e05750a 100644 --- a/extensions/security/ldap/pom.xml +++ b/extensions/security/ldap/pom.xml @@ -50,12 +50,6 @@ org.exist-db exist-core ${project.version} - - - junit - junit - - diff --git a/extensions/vector/pom.xml b/extensions/vector/pom.xml index 251492101af..e43f2f3b191 100644 --- a/extensions/vector/pom.xml +++ b/extensions/vector/pom.xml @@ -56,10 +56,14 @@ org.exist-db exist-core ${project.version} - + + + + ${project.groupId} + exist-core + ${project.version} + test-jar + test @@ -122,7 +126,6 @@ true org.exist-db:exist-core - junit:junit diff --git a/extensions/webdav/pom.xml b/extensions/webdav/pom.xml index 72e13d51691..4cea8204dd7 100644 --- a/extensions/webdav/pom.xml +++ b/extensions/webdav/pom.xml @@ -50,12 +50,14 @@ org.exist-db exist-core ${project.version} - - - junit - junit - - + + + + ${project.groupId} + exist-core + ${project.version} + test-jar + test