package org.smoothbuild.acceptance; import static com.google.common.base.Charsets.UTF_8; import static com.google.common.io.ByteStreams.copy; import static com.google.common.io.Files.createTempDir; import static java.util.Arrays.asList; import static org.junit.Assert.fail; import static org.smoothbuild.SmoothConstants.EXIT_CODE_ERROR; import static org.smoothbuild.SmoothConstants.EXIT_CODE_SUCCESS; import static org.smoothbuild.io.fs.disk.RecursiveDeleter.deleteRecursively; import static org.smoothbuild.util.Streams.inputStreamToString; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import org.junit.After; import org.junit.Before; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList.Builder; public class AcceptanceTestCase { private static final String DEFAULT_BUILD_SCRIPT_FILE = "build.smooth"; private static final String ARTIFACTS_DIR_PATH = ".smooth/artifacts/"; private static String SMOOTH_BINARY_PATH; private File projectDir; private Integer exitCode; private String outputData; private String errorData; @Before public void before() { projectDir = createTempDir(); } @After public void after() throws IOException { deleteRecursively(projectDir().toPath()); } protected void givenScript(String buildScript) throws IOException { givenRawScript(buildScript.replace('\'', '"')); } protected void givenRawScript(String buildScript) throws IOException { givenFile(DEFAULT_BUILD_SCRIPT_FILE, buildScript); } protected void givenFile(String path, String content) throws IOException { File fullPath = file(path); fullPath.getParentFile().mkdirs(); try (FileWriter writer = new FileWriter(fullPath.toString())) { content.getBytes(UTF_8); writer.write(content); } } protected void givenDir(String path) throws IOException { file(path).mkdirs(); } protected void whenSmoothBuild(String... args) { whenSmooth(join("build", args)); } protected void whenSmoothHelp(String... args) { whenSmooth(join("help", args)); } protected void whenSmoothClean(String... args) { whenSmooth(join("clean", args)); } private static String[] join(String command, String[] args) { ArrayList<String> result = new ArrayList<>(); result.add(command); result.addAll(asList(args)); return result.toArray(new String[] {}); } public void whenSmooth(String... smoothCommandArgs) { try { ProcessBuilder processBuilder = new ProcessBuilder(processArgs(smoothCommandArgs)); processBuilder.directory(projectDir()); Process process = processBuilder.start(); ExecutorService executor = Executors.newFixedThreadPool(2); Future<ByteArrayOutputStream> inputStream = executor.submit(streamReadingCallable(process.getInputStream())); Future<ByteArrayOutputStream> errorStream = executor.submit(streamReadingCallable(process.getErrorStream())); exitCode = process.waitFor(); outputData = new String(inputStream.get().toByteArray()); errorData = new String(errorStream.get().toByteArray()); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } catch (IOException e) { throw new RuntimeException(e); } catch (ExecutionException e) { throw new RuntimeException(e); } } public static ImmutableList<String> processArgs(String... params) { Builder<String> builder = ImmutableList.builder(); builder.add(smoothBinaryPath()); builder.addAll(asList(params)); return builder.build(); } private synchronized static String smoothBinaryPath() { if (SMOOTH_BINARY_PATH == null) { String smoothHome = System.getenv("smooth_home_dir"); if (smoothHome == null) { try { File repoDir = new File(".").getCanonicalFile(); ProcessBuilder processBuilder = new ProcessBuilder("ant", "install-smooth"); processBuilder.directory(repoDir); Process process = processBuilder.start(); ExecutorService executor = Executors.newFixedThreadPool(2); Future<ByteArrayOutputStream> inputStream = executor.submit(streamReadingCallable(process.getInputStream())); Future<ByteArrayOutputStream> errorStream = executor.submit(streamReadingCallable(process.getErrorStream())); int exitCode = process.waitFor(); String outputData = new String(inputStream.get().toByteArray()); String errorData = new String(errorStream.get().toByteArray()); if (exitCode != 0) { throw new RuntimeException( "Running 'ant install-smooth' failed with following output\n" + "STANDARD OUTPUT\n" + outputData + "\n" + "STANDARD ERROR\n" + errorData + "\n"); } smoothHome = repoDir.getAbsolutePath() + "/build/acceptance/smooth"; } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } catch (IOException e) { throw new RuntimeException(e); } catch (ExecutionException e) { throw new RuntimeException(e); } } SMOOTH_BINARY_PATH = smoothHome + "/smooth"; } return SMOOTH_BINARY_PATH; } private static Callable<ByteArrayOutputStream> streamReadingCallable(InputStream inputStream) { return () -> { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); copy(inputStream, outputStream); return outputStream; }; } protected void thenFinishedWithSuccess() { thenReturnedCode(EXIT_CODE_SUCCESS); } protected void thenFinishedWithError() { thenReturnedCode(EXIT_CODE_ERROR); } private void thenReturnedCode(int expected) { if (expected != exitCode.intValue()) { fail("Expected return code " + expected + " but was " + exitCode.intValue() + ".\n" + "standard out:\n" + outputData + "\n" + "standard err:\n" + errorData + "\n"); } } protected String output() { return outputData; } protected File artifact(String name) { return file(ARTIFACTS_DIR_PATH + name); } protected File file(String path) { return new File(projectDir(), path); } protected File projectDir() { return projectDir; } protected String artifactContent(String artifact) throws IOException { return inputStreamToString(new FileInputStream(artifact(artifact))); } }