From 1462a1e8eb24ad052c2c40b02db136152c3d0b33 Mon Sep 17 00:00:00 2001 From: dzou Date: Tue, 26 May 2020 16:27:24 -0400 Subject: [PATCH] Fix the GCP integration tests --- .../function-sample-gcp-http/pom.xml | 7 +- .../FunctionSampleGcpIntegrationTest.java | 62 +------- .../com/example/LocalServerTestSupport.java | 139 ++++++++++++++++++ 3 files changed, 149 insertions(+), 59 deletions(-) create mode 100644 spring-cloud-function-samples/function-sample-gcp-http/src/test/java/com/example/LocalServerTestSupport.java diff --git a/spring-cloud-function-samples/function-sample-gcp-http/pom.xml b/spring-cloud-function-samples/function-sample-gcp-http/pom.xml index d07f72212..548ac0144 100644 --- a/spring-cloud-function-samples/function-sample-gcp-http/pom.xml +++ b/spring-cloud-function-samples/function-sample-gcp-http/pom.xml @@ -47,7 +47,12 @@ assertj-core test - + + com.google.cloud.functions.invoker + java-function-invoker + 1.0.0-alpha-2-rc5 + test + diff --git a/spring-cloud-function-samples/function-sample-gcp-http/src/test/java/com/example/FunctionSampleGcpIntegrationTest.java b/spring-cloud-function-samples/function-sample-gcp-http/src/test/java/com/example/FunctionSampleGcpIntegrationTest.java index 08f7bf38a..ff7eecee8 100644 --- a/spring-cloud-function-samples/function-sample-gcp-http/src/test/java/com/example/FunctionSampleGcpIntegrationTest.java +++ b/spring-cloud-function-samples/function-sample-gcp-http/src/test/java/com/example/FunctionSampleGcpIntegrationTest.java @@ -16,77 +16,23 @@ package com.example; -import java.io.BufferedReader; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import org.junit.Ignore; import org.junit.Test; import org.springframework.boot.test.web.client.TestRestTemplate; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.fail; public class FunctionSampleGcpIntegrationTest { private TestRestTemplate rest = new TestRestTemplate(); - private CountDownLatch startedSuccessfully = new CountDownLatch(1); - @Test - @Ignore - public void testSample() throws IOException { - Process process = new ProcessBuilder("./../../mvnw", "function:run").start(); - - try { - Executors.defaultThreadFactory().newThread(new OutputCapture(process.getErrorStream())).start(); - Executors.defaultThreadFactory().newThread(new OutputCapture(process.getInputStream())).start(); - - if (startedSuccessfully.await(10, TimeUnit.SECONDS)) { - String result = rest.postForObject("http://localhost:8080/", "Hello", String.class); - assertThat(result).isEqualTo("\"HELLO\""); - } - else { - fail("Failed to start the function."); - } - } - catch (InterruptedException e) { - e.printStackTrace(); - } - finally { - process.destroy(); + public void testSample() throws IOException, InterruptedException { + try (LocalServerTestSupport.ServerProcess process = LocalServerTestSupport.startServer(CloudFunctionMain.class)) { + String result = rest.postForObject("http://localhost:8080/", "Hello", String.class); + assertThat(result).isEqualTo("\"HELLO\""); } } - - class OutputCapture implements Runnable { - - private InputStream inputStream; - - OutputCapture(InputStream inputStream) { - this.inputStream = inputStream; - } - - @Override - public void run() { - try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { - String line; - while ((line = reader.readLine()) != null) { - System.out.println(line); - if (line.equals("INFO: URL: http://localhost:8080/")) { - startedSuccessfully.countDown(); - } - } - } - catch (IOException e) { - e.printStackTrace(); - } - } - - } - } diff --git a/spring-cloud-function-samples/function-sample-gcp-http/src/test/java/com/example/LocalServerTestSupport.java b/spring-cloud-function-samples/function-sample-gcp-http/src/test/java/com/example/LocalServerTestSupport.java new file mode 100644 index 000000000..8bbe71943 --- /dev/null +++ b/spring-cloud-function-samples/function-sample-gcp-http/src/test/java/com/example/LocalServerTestSupport.java @@ -0,0 +1,139 @@ +/* + * Copyright 2020-2020 the original author or authors. + * + * Licensed 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 + * + * https://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 com.example; + +import java.io.*; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import com.google.cloud.functions.invoker.runner.Invoker; + +import org.springframework.cloud.function.adapter.gcp.GcfJarLauncher; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test support class for running tests on the local Cloud Function server. + * + * @author Daniel Zou + * @author Mike Eltsufin + */ +final public class LocalServerTestSupport { + + private static final ExecutorService EXECUTOR = Executors.newCachedThreadPool(); + + private static final String SERVER_READY_STRING = "Started ServerConnector"; + + private static AtomicInteger nextPort = new AtomicInteger(8080); + + private LocalServerTestSupport() { + } + + public static ServerProcess startServer(Class springApplicationMainClass) + throws InterruptedException, IOException { + + // Get the Java class path. + String myClassPath = System.getProperty("java.class.path"); + assertThat(myClassPath).isNotNull(); + + // Setup the Java Process command line string + List command = Arrays.asList(getJavaCommand(), "-classpath", myClassPath, Invoker.class.getName()); + ProcessBuilder processBuilder = new ProcessBuilder().command(command).redirectErrorStream(true); + + // Set environment variables. + Map environment = new HashMap<>(); + environment.put("PORT", String.valueOf(nextPort.getAndIncrement())); + environment.put("K_SERVICE", "test-function"); + environment.put("FUNCTION_SIGNATURE_TYPE", "http"); + environment.put("FUNCTION_TARGET", GcfJarLauncher.class.getCanonicalName()); + environment.put("MAIN_CLASS", springApplicationMainClass.getCanonicalName()); + processBuilder.environment().putAll(environment); + + // Start the process and monitor the output logs in a separate thread. + // Once the SERVER_READY_STRING is found in the logs, we know we are ready. + Process serverProcess = processBuilder.start(); + CountDownLatch ready = new CountDownLatch(1); + + EXECUTOR.submit(() -> monitorOutput(serverProcess.getInputStream(), ready)); + boolean serverReady = ready.await(5, TimeUnit.SECONDS); + if (!serverReady) { + serverProcess.destroy(); + throw new AssertionError("Server never became ready"); + } + + return new ServerProcess(serverProcess); + } + + private static void monitorOutput(InputStream processOutput, CountDownLatch ready) { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(processOutput))) { + String line; + while ((line = reader.readLine()) != null) { + if (line.contains(SERVER_READY_STRING)) { + ready.countDown(); + } + + System.out.println(line); + + if (line.contains("WARNING")) { + throw new AssertionError("Found warning in server output:\n" + line); + } + } + } + catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + /** + * Returns the path to the java executable. + */ + private static String getJavaCommand() { + File javaHome = new File(System.getProperty("java.home")); + assertThat(javaHome.exists()).isTrue(); + + File javaBin = new File(javaHome, "bin"); + File javaCommand = new File(javaBin, "java"); + assertThat(javaCommand.exists()).isTrue(); + + return javaCommand.toString(); + } + + static class ServerProcess implements AutoCloseable { + + private final Process process; + + ServerProcess(Process process) { + this.process = process; + } + + Process process() { + return process; + } + + @Override + public void close() { + process().destroy(); + } + } +}