This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/sling-whiteboard.git
commit 9a46cacd3b36be0f692af79601c6a04e5f9eb1d5 Author: Robert Munteanu <[email protected]> AuthorDate: Wed Jun 5 17:00:17 2019 +0200 Converted IntegrationTest to a proper IT - load the java agent from the compiled jar - switch to using a surefire test --- url-connection-agent/pom.xml | 12 ++ .../java/org/apache/sling/uca/impl/AgentIT.java | 163 +++++++++++++++++++++ .../org/apache/sling/uca/impl/IntegrationTest.java | 107 -------------- 3 files changed, 175 insertions(+), 107 deletions(-) diff --git a/url-connection-agent/pom.xml b/url-connection-agent/pom.xml index bc5a25f..1e96414 100644 --- a/url-connection-agent/pom.xml +++ b/url-connection-agent/pom.xml @@ -57,6 +57,18 @@ </execution> </executions> </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-failsafe-plugin</artifactId> + <executions> + <execution> + <goals> + <goal>integration-test</goal> + <goal>verify</goal> + </goals> + </execution> + </executions> + </plugin> </plugins> </build> diff --git a/url-connection-agent/src/test/java/org/apache/sling/uca/impl/AgentIT.java b/url-connection-agent/src/test/java/org/apache/sling/uca/impl/AgentIT.java new file mode 100644 index 0000000..b67a650 --- /dev/null +++ b/url-connection-agent/src/test/java/org/apache/sling/uca/impl/AgentIT.java @@ -0,0 +1,163 @@ +/* + * 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.uca.impl; + +import static java.time.Duration.ofSeconds; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTimeout; + +import java.io.IOException; +import java.lang.ProcessBuilder.Redirect; +import java.net.SocketTimeoutException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Validates that accessing URLs that exhibit connection problems results in a timeouts being fired when the agent is used + * + * <p>This test validates that the agent works when statically loaded, i.e. with a <tt>-javaagent:</tt> flag + * passed to the JVM. As such it requires launching a new JVM instance each time, otherwise the results are + * not valid.</p> + * + * <p>It does so by reusing the same JVM as the one running the test. Validation is done by looking for a + * Throwable information in the stderr and recording the exception class name and the message.</p> + * + */ +@ExtendWith(MisbehavingServerExtension.class) +public class AgentIT { + + private static final Path STDERR = Paths.get("target", "stderr.txt"); + private static final Path STDOUT = Paths.get("target", "stdout.txt"); + private static final Logger LOG = LoggerFactory.getLogger(AgentIT.class); + + /** + * Validates that connecting to a unaccessible port on an existing port fails with a connect + * timeout exception + * + * <p>It is surprisingly hard to simulate a connnection timeout. The most reliable way seems to + * be to get a firewall to drop packets, but this is very hard to do portably and safely + * in a unit test. The least bad possible solution is to access an URL that we know will timeout + * and that is able to sustain additional traffic. Maven Central is a good candidate for that.</p> + * + * @throws IOException various I/O problems + */ + @Test + public void connectTimeout() throws IOException { + + RecordedThrowable error = assertTimeout(ofSeconds(5), () -> runTest("http://repo1.maven.org:81")); + assertEquals(SocketTimeoutException.class.getName(), error.className); + assertEquals("connect timed out", error.message); + } + + /** + * Validates that connecting to a host that delays the response fails with a read timeout + * + * @throws IOException various I/O problems + */ + @Test + public void readTimeout(MisbehavingServerControl server) throws IOException { + + RecordedThrowable error = assertTimeout(ofSeconds(5), () -> runTest("http://localhost:" + server.getLocalPort())); + assertEquals(SocketTimeoutException.class.getName(), error.className); + assertEquals("Read timed out", error.message); + } + + + private RecordedThrowable runTest(String urlSpec) throws IOException, InterruptedException { + + Process process = runForkedCommandWithAgent(new URL(urlSpec), 3, 3); + int exitCode = process.waitFor(); + + LOG.info("Exited with code {}", exitCode); + LOG.info("Dump of stdout: "); + Files + .lines(STDOUT) + .forEach(LOG::info); + + LOG.info("Dump of stderr: "); + Files + .lines(STDERR) + .forEach(LOG::info); + + if ( exitCode != 0 ) { + return Files.lines(STDERR) + .filter( l -> l.startsWith("Exception in thread \"main\"")) + .map( l -> newRecordedThrowable(l) ) + .findFirst() + .orElseThrow(() -> new RuntimeException("Exit code was zero but did not find any exception information in stderr.txt")); + } + + return null; + } + + private Process runForkedCommandWithAgent(URL url, int connectTimeoutSeconds, int readTimeoutSeconds) throws IOException { + + Path jar = Files.list(Paths.get("target")) + .filter( p -> p.getFileName().toString().endsWith("-jar-with-dependencies.jar")) + .findFirst() + .orElseThrow( () -> new IllegalStateException("Did not find the agent jar. Did you run mvn package first?")); + + String javaHome = System.getProperty("java.home"); + Path javaExe = Paths.get(javaHome, "bin", "java"); + ProcessBuilder pb = new ProcessBuilder( + javaExe.toString(), + "-showversion", + "-javaagent:" + jar +"=" + TimeUnit.SECONDS.toMillis(connectTimeoutSeconds) +"," + TimeUnit.SECONDS.toMillis(readTimeoutSeconds), + "-cp", + jar.toString(), + "org.apache.sling.uca.impl.Main", + url.toString() + ); + + pb.redirectInput(Redirect.INHERIT); + pb.redirectOutput(STDOUT.toFile()); + pb.redirectError(STDERR.toFile()); + + return pb.start(); + } + + private RecordedThrowable newRecordedThrowable(String string) { + + string = string.replace("Exception in thread \"main\"", ""); + String[] parts = string.split(":"); + + return new RecordedThrowable(parts[0].trim(), parts[1].trim()); + } + + /** + * Basic information about a {@link Throwable} that was recorded in a file + */ + static class RecordedThrowable { + String className; + String message; + + public RecordedThrowable(String className, String message) { + this.className = className; + this.message = message; + } + + + } +} diff --git a/url-connection-agent/src/test/java/org/apache/sling/uca/impl/IntegrationTest.java b/url-connection-agent/src/test/java/org/apache/sling/uca/impl/IntegrationTest.java deleted file mode 100644 index 9e42394..0000000 --- a/url-connection-agent/src/test/java/org/apache/sling/uca/impl/IntegrationTest.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * 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.uca.impl; - -import static java.time.Duration.ofSeconds; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTimeout; - -import java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.SocketTimeoutException; -import java.net.URL; -import java.net.URLConnection; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.concurrent.TimeUnit; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -@ExtendWith(MisbehavingServerExtension.class) -public class IntegrationTest { - - private static final Logger LOG = LoggerFactory.getLogger(IntegrationTest.class); - - /** - * Validates that connecting to a unaccessible port on an existing port fails with a connect - * timeout exception - * - * <p>It is surprisingly hard to simulate a connnection timeout. The most reliable way seems to - * be to get a firewall to drop packets, but this is very hard to do portably and safely - * in a unit test. The least bad possible solution is to access an URL that we know will timeout - * and that is able to sustain additional traffic. Maven Central is a good candidate for that.</p> - * - * @throws IOException various I/O problems - */ - @Test - public void connectTimeout() throws IOException { - - SocketTimeoutException exception = assertThrows(SocketTimeoutException.class, - () -> assertTimeout(ofSeconds(5), () -> runTest("http://repo1.maven.org:81")) - ); - assertEquals("connect timed out", exception.getMessage()); - } - - /** - * Validates that connecting to a host that delays the response fails with a read timeout - * - * @throws IOException various I/O problems - */ - @Test - public void readTimeout(MisbehavingServerControl server) throws IOException { - - SocketTimeoutException exception = assertThrows(SocketTimeoutException.class, - () -> assertTimeout(ofSeconds(10), () -> runTest("http://localhost:" + server.getLocalPort())) - ); - assertEquals("Read timed out", exception.getMessage()); - } - - - private void runTest(String urlSpec) throws MalformedURLException, IOException, InterruptedException { - - String javaHome = System.getProperty("java.home"); - Path javaExe = Paths.get(javaHome, "bin", "java"); - ProcessBuilder pb = new ProcessBuilder(javaExe.toString(), "-version"); - pb.inheritIO(); - Process process = pb.start(); - - process.getInputStream(); - - int exitCode = process.waitFor(); - - LOG.info("Exited with code {}", exitCode); - - URL url = new URL(urlSpec); - LOG.info("connecting to {}", url); - URLConnection connection = url.openConnection(); - // TODO - remove when running through the harness - connection.setConnectTimeout((int) TimeUnit.SECONDS.toMillis(3)); - connection.setReadTimeout((int) TimeUnit.SECONDS.toMillis(3)); - connection.connect(); - LOG.info("connected"); - try ( InputStream is = connection.getInputStream()) { - while ( is.read() != -1) - ; - } - LOG.info("read"); - } -}
