/* * 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(); } } }