Files
spring-cloud-function/spring-cloud-function-samples/function-sample-gcp-http/src/test/java/com/example/LocalServerTestSupport.java
2020-05-27 13:58:20 +02:00

140 lines
4.2 KiB
Java

/*
* 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<String> command = Arrays.asList(getJavaCommand(), "-classpath", myClassPath, Invoker.class.getName());
ProcessBuilder processBuilder = new ProcessBuilder().command(command).redirectErrorStream(true);
// Set environment variables.
Map<String, String> 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();
}
}
}