This is an automated email from the ASF dual-hosted git repository. royteeuwen pushed a commit to branch feature/SLING-12582 in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-junit-remote.git
commit 4259a14f097b18b0b07beeb09e6a640ba0ec28ae Author: Roy Teeuwen <[email protected]> AuthorDate: Wed Dec 18 12:05:12 2024 +0100 SLING-12582: Create a dynamic test executor for junit5 --- bnd.bnd | 3 +- pom.xml | 8 ++ .../junit/remote/testrunner/SlingRemoteTest.java | 8 ++ .../testrunner/dynamic/ExampleDynamicTest.java | 52 +++++++++ .../dynamic/SlingRemoteDynamicTestExecutor.java | 130 +++++++++++++++++++++ .../testrunner/dynamic/SlingRemoteTestResult.java | 44 +++++++ .../remote/testrunner/dynamic/package-info.java | 23 ++++ 7 files changed, 267 insertions(+), 1 deletion(-) diff --git a/bnd.bnd b/bnd.bnd index ab06448..bc4a998 100644 --- a/bnd.bnd +++ b/bnd.bnd @@ -3,6 +3,7 @@ Import-Package:\ org.apache.sling.testing.tools.http; resolution:=optional,\ org.apache.sling.testing.tools.sling; resolution:=optional,\ org.junit.internal.*; resolution:=optional,\ + org.junit.jupiter.*; resolution:=optional,\ * Sling-Test-Regexp:\ @@ -12,4 +13,4 @@ Sling-Test-Regexp:\ @org.apache.sling.jcr.contentparser-*.jar!/org/apache/sling/jcr/contentparser/impl/JsonTicksConverter.class -removeheaders:\ - Include-Resource \ No newline at end of file + Include-Resource diff --git a/pom.xml b/pom.xml index aff70ed..0b05358 100644 --- a/pom.xml +++ b/pom.xml @@ -60,11 +60,19 @@ <version>1.1.6</version> <scope>provided</scope> </dependency> + + <!-- junit4 for the @Rule and ParentRunner based implementations --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>compile</scope> </dependency> + <!-- junit5 for the DynamicTest based implementations --> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-api</artifactId> + <scope>compile</scope> + </dependency> <!-- SLF4J is needed at runtime (whenever SlingRemoteTestRunner is executed) --> <dependency> <groupId>org.slf4j</groupId> diff --git a/src/main/java/org/apache/sling/junit/remote/testrunner/SlingRemoteTest.java b/src/main/java/org/apache/sling/junit/remote/testrunner/SlingRemoteTest.java index 060fe3d..c45f5ee 100644 --- a/src/main/java/org/apache/sling/junit/remote/testrunner/SlingRemoteTest.java +++ b/src/main/java/org/apache/sling/junit/remote/testrunner/SlingRemoteTest.java @@ -46,6 +46,14 @@ class SlingRemoteTest { } } + public String getDescription() { + return description; + } + + public String getFailure() { + return failure; + } + Description describe() { return Description.createTestDescription(testClass, description); } diff --git a/src/main/java/org/apache/sling/junit/remote/testrunner/dynamic/ExampleDynamicTest.java b/src/main/java/org/apache/sling/junit/remote/testrunner/dynamic/ExampleDynamicTest.java new file mode 100644 index 0000000..f9b6601 --- /dev/null +++ b/src/main/java/org/apache/sling/junit/remote/testrunner/dynamic/ExampleDynamicTest.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.sling.junit.remote.testrunner.dynamic; + +import java.util.stream.Stream; + +import org.apache.sling.junit.remote.testrunner.SlingRemoteTestParameters; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +/** + * Test that runs the remote tests defined by the getTestClassesSelector + */ +public class ExampleDynamicTest implements SlingRemoteTestParameters { + + @Override + public String getJunitServletUrl() { + return "http://localhost:8080/system/sling/junit"; + } + + @Override + public String getTestClassesSelector() { + return "my.remote.test.package"; + } + + @Override + public String getTestMethodSelector() { + return null; + } + + @TestFactory + public Stream<DynamicTest> remoteTests() { + var executor = new SlingRemoteDynamicTestExecutor(this); + return executor.streamRemoteTests(); + } +} diff --git a/src/main/java/org/apache/sling/junit/remote/testrunner/dynamic/SlingRemoteDynamicTestExecutor.java b/src/main/java/org/apache/sling/junit/remote/testrunner/dynamic/SlingRemoteDynamicTestExecutor.java new file mode 100644 index 0000000..ef20368 --- /dev/null +++ b/src/main/java/org/apache/sling/junit/remote/testrunner/dynamic/SlingRemoteDynamicTestExecutor.java @@ -0,0 +1,130 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.sling.junit.remote.testrunner.dynamic; + +import javax.json.Json; +import javax.json.JsonArray; +import javax.json.JsonObject; + +import java.io.StringReader; +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Stream; + +import org.apache.sling.jcr.contentparser.impl.JsonTicksConverter; +import org.apache.sling.junit.remote.httpclient.RemoteTestHttpClient; +import org.apache.sling.junit.remote.testrunner.SlingRemoteTestParameters; +import org.apache.sling.junit.remote.testrunner.SlingTestsCountChecker; +import org.apache.sling.testing.tools.http.RequestCustomizer; +import org.apache.sling.testing.tools.http.RequestExecutor; +import org.apache.sling.testing.tools.sling.SlingTestBase; +import org.junit.jupiter.api.DynamicTest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** JUnit5 dynamic-tests based executor that talks to a remote + * Sling JUnit test servlet. Using this test + * lets a test class discover tests + * that the JUnit servlet can execute, execute + * them and report results exactly as if the tests + * ran locally. + */ +public class SlingRemoteDynamicTestExecutor { + + private static final Logger log = LoggerFactory.getLogger(SlingRemoteDynamicTestExecutor.class); + + private final SlingRemoteTestParameters testParameters; + private final RemoteTestHttpClient testHttpClient; + private final String username; + private final String password; + + private final List<SlingRemoteTestResult> children = new LinkedList<>(); + + public SlingRemoteDynamicTestExecutor(SlingRemoteTestParameters instance) { + // Set configured username using "admin" as default credential + final String configuredUsername = System.getProperty(SlingTestBase.TEST_SERVER_USERNAME); + if (configuredUsername != null && !configuredUsername.trim().isEmpty()) { + username = configuredUsername; + } else { + username = SlingTestBase.ADMIN; + } + + // Set configured password using "admin" as default credential + final String configuredPassword = System.getProperty(SlingTestBase.TEST_SERVER_PASSWORD); + if (configuredPassword != null && !configuredPassword.trim().isEmpty()) { + password = configuredPassword; + } else { + password = SlingTestBase.ADMIN; + } + + testParameters = instance; + testHttpClient = + new RemoteTestHttpClient(testParameters.getJunitServletUrl(), this.username, this.password, true); + } + + private void executeTests() throws Exception { + // Let the parameters class customize the request if desired + if (testParameters instanceof RequestCustomizer) { + testHttpClient.setRequestCustomizer((RequestCustomizer) testParameters); + } + + // Run tests remotely and get response + final RequestExecutor executor = testHttpClient.runTests( + testParameters.getTestClassesSelector(), testParameters.getTestMethodSelector(), "json"); + executor.assertContentType("application/json"); + final JsonArray json = Json.createReader( + new StringReader(JsonTicksConverter.tickToDoubleQuote(executor.getContent()))) + .readArray(); + + // Response contains an array of objects identified by + // their INFO_TYPE, extract the tests + // based on this vlaue + for (int i = 0; i < json.size(); i++) { + final JsonObject obj = json.getJsonObject(i); + if (obj.containsKey("INFO_TYPE") && "test".equals(obj.getString("INFO_TYPE"))) { + children.add(new SlingRemoteTestResult(obj)); + } + } + + log.info( + "Server-side tests executed as {} at {} with path {}", + this.username, + testParameters.getJunitServletUrl(), + testHttpClient.getTestExecutionPath()); + + // Optionally check that number of tests is as expected + if (testParameters instanceof SlingTestsCountChecker) { + ((SlingTestsCountChecker) testParameters).checkNumberOfTests(children.size()); + } + } + + public Stream<DynamicTest> streamRemoteTests() { + try { + executeTests(); + } catch (Exception e) { + throw new Error(e); + } + return children.stream() + .map(remoteTest -> DynamicTest.dynamicTest(remoteTest.getDescription(), () -> { + if (remoteTest.getFailure() != null) { + throw new AssertionError(remoteTest.getFailure()); + } + })); + } +} diff --git a/src/main/java/org/apache/sling/junit/remote/testrunner/dynamic/SlingRemoteTestResult.java b/src/main/java/org/apache/sling/junit/remote/testrunner/dynamic/SlingRemoteTestResult.java new file mode 100644 index 0000000..febecb8 --- /dev/null +++ b/src/main/java/org/apache/sling/junit/remote/testrunner/dynamic/SlingRemoteTestResult.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.sling.junit.remote.testrunner.dynamic; + +import javax.json.JsonException; +import javax.json.JsonObject; + +class SlingRemoteTestResult { + + private final String description; + private final String failure; + + public static final String DESCRIPTION = "description"; + public static final String FAILURE = "failure"; + + SlingRemoteTestResult(JsonObject json) throws JsonException { + description = json.containsKey(DESCRIPTION) ? json.getString(DESCRIPTION) : null; + failure = json.containsKey(FAILURE) ? json.getString(FAILURE) : null; + } + + public String getDescription() { + return description; + } + + public String getFailure() { + return failure; + } +} diff --git a/src/main/java/org/apache/sling/junit/remote/testrunner/dynamic/package-info.java b/src/main/java/org/apache/sling/junit/remote/testrunner/dynamic/package-info.java new file mode 100644 index 0000000..a5b943b --- /dev/null +++ b/src/main/java/org/apache/sling/junit/remote/testrunner/dynamic/package-info.java @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +@Version("1.0.0") +package org.apache.sling.junit.remote.testrunner.dynamic; + +import org.osgi.annotation.versioning.Version;
