Java tutorial
/* * Copyright (c) 2014 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 * * 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 io.werval.gradle; import io.werval.runtime.util.Holder; import io.werval.util.InputStreams; import java.io.File; import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.Files; import java.util.concurrent.Callable; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.ResponseHandler; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.BasicResponseHandler; import org.apache.http.impl.client.DefaultHttpClient; import org.gradle.testkit.functional.ExecutionResult; import org.gradle.testkit.functional.GradleRunner; import org.gradle.testkit.functional.GradleRunnerFactory; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import static com.jayway.awaitility.Awaitility.await; import static io.werval.api.BuildVersion.VERSION; import static io.werval.util.InputStreams.BUF_SIZE_4K; import static java.util.Arrays.asList; import static java.util.concurrent.TimeUnit.SECONDS; import static org.hamcrest.CoreMatchers.allOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.core.StringContains.containsString; import static org.junit.Assert.assertThat; /** * Assert that the {@literal secret}, {@literal start} and {@literal devshell} tasks execute successfuly. * * Generates a project, run it in dev mode using the plugin, change source code and assert code is reloaded. * <p> * As this test spawn several Gradle Daemons it ends by killing them all to leave the running system in a proper state. */ public class ApplicationPluginIntegTest extends AbstractPluginIntegTest { private static final Charset UTF_8 = Charset.forName("UTF-8"); private static final String BUILD; private static final String ROUTES; private static final String CONFIG; private static final String CONFIG_DEV; private static final String CONTROLLER; private static final String CONTROLLER_CHANGED; private static final String CONTROLLER_EXCEPTIONAL; private static final String CONTROLLER_BUILD_ERROR; static { String localRepo = new File("../repository").getAbsolutePath(); BUILD = "\n" + "buildscript {\n" + " repositories {\n" + " maven { url 'file://" + localRepo + "' }\n" + " jcenter()\n" + " }\n" + " dependencies { classpath 'io.werval:io.werval.gradle:" + VERSION + "' }\n" + "}\n" + "repositories { maven { url 'file://" + localRepo + "' } }\n" + "apply plugin: \"io.werval.application\"\n" + "dependencies {\n" + " runtime 'ch.qos.logback:logback-classic:1.1.2'\n" + "}\n" + "sourceSets {\n" + " custom\n" + " main {\n" + " output.dir( 'build/SUPPLEMENTARY_OUTPUT' )\n" + " }\n" + "}\n" + "classes.dependsOn customClasses\n" + "devshell {\n" + " openBrowser = false\n" + " sourceSets += project.sourceSets.custom\n" + " configResource = 'development.conf'\n" + "}\n" + "start {\n" + " sourceSets += project.sourceSets.custom\n" + " configResource = 'application-custom.conf'\n" + "}\n" + "\n"; CONFIG = "\n" + "app.secret = e6bcdba3bc6840aa08013ef20505a0c27f800dbbcced6fbb71e8cf197fe83866\n" + "tag = custom\n"; CONFIG_DEV = "include \"application-custom.conf\"\ntag = development\n"; ROUTES = "GET / controllers.Application.index"; CONTROLLER = "\n" + "package controllers;\n" + "import io.werval.api.outcomes.Outcome;\n" + "import static io.werval.api.context.CurrentContext.application;\n" + "import static io.werval.api.context.CurrentContext.outcomes;\n" + "public class Application\n" + "{\n" + " public Outcome index()\n" + " {\n" + " return outcomes().ok( \"I ran! \" + application().config().string( \"tag\" ) ).build();\n" + " }\n" + "}\n"; CONTROLLER_CHANGED = "\n" + "package controllers;\n" + "import io.werval.api.outcomes.Outcome;\n" + "import static io.werval.api.context.CurrentContext.outcomes;\n" + "public class Application\n" + "{\n" + " public Outcome index()\n" + " {\n" + " return outcomes().ok( \"I ran changed!\" ).build();\n" + " }\n" + "}\n"; CONTROLLER_EXCEPTIONAL = "\n" + "package controllers;\n" + "import io.werval.api.outcomes.Outcome;\n" + "import static io.werval.api.context.CurrentContext.outcomes;\n" + "public class Application\n" + "{\n" + " public Outcome index()\n" + " {\n" + " throw new RuntimeException( \"I throwed!\" );\n" + " }\n" + "}\n"; CONTROLLER_BUILD_ERROR = "\n" + "package controllers;\n" + "import io.werval.api.outcomes.Outcome;\n" + "import static io.werval.api.context.CurrentContext.outcomes;\n" + "public class Application\n" + "{\n" + " public Outcome index()\n" + " {\n" + " I FAILED TO COMPILE!\n" + " }\n" + "}\n"; new File("build/tmp/it").mkdirs(); } private final File devshellLock = new File(".devshell.lock"); @Rule public TemporaryFolder tmp = new TemporaryFolder(new File("build/tmp/it")) { @Override public void delete() { super.delete(); } }; @Before public void setupProjectLayout() throws IOException { Files.write(new File(tmp.getRoot(), "build.gradle").toPath(), BUILD.getBytes(UTF_8)); File dev = new File(tmp.getRoot(), "src/dev/resources"); Files.createDirectories(dev.toPath()); Files.write(new File(dev, "development.conf").toPath(), CONFIG_DEV.getBytes(UTF_8)); File custom = new File(tmp.getRoot(), "src/custom/resources"); Files.createDirectories(custom.toPath()); Files.write(new File(custom, "application-custom.conf").toPath(), CONFIG.getBytes(UTF_8)); File resources = new File(tmp.getRoot(), "src/main/resources"); Files.createDirectories(resources.toPath()); Files.write(new File(resources, "routes.conf").toPath(), ROUTES.getBytes(UTF_8)); File controllers = new File(tmp.getRoot(), "src/main/java/controllers"); Files.createDirectories(controllers.toPath()); Files.write(new File(controllers, "Application.java").toPath(), CONTROLLER.getBytes(UTF_8)); } @After public void cleanupDevShellLock() throws Exception { if (devshellLock.exists()) { Files.delete(devshellLock.toPath()); } } @Test public void secretTaskIntegrationTest() throws IOException { GradleRunner runner = GradleRunnerFactory.create(); runner.setDirectory(tmp.getRoot()); runner.setArguments(asList("secret")); ExecutionResult result = runner.run(); assertThat(result.getStandardOutput(), containsString("Generate new Werval Application Secret")); } @Test public void devshellTaskIntegrationTest() throws InterruptedException, IOException { final Holder<Exception> errorHolder = new Holder<>(); Thread devshellThread = new Thread(newRunnable(errorHolder, "devshell"), "gradle-werval-devshell-thread"); try { devshellThread.start(); await().atMost(60, SECONDS).until(new Callable<Boolean>() { @Override public Boolean call() throws Exception { return devshellLock.exists(); } }); if (errorHolder.isSet()) { throw new RuntimeException( "Error during werval:devshell invocation: " + errorHolder.get().getMessage(), errorHolder.get()); } final HttpClient client = new DefaultHttpClient(); final HttpGet get = new HttpGet("http://localhost:23023/"); final ResponseHandler<String> successHandler = new BasicResponseHandler(); await().atMost(60, SECONDS).pollInterval(5, SECONDS).until(new Callable<String>() { @Override public String call() throws Exception { try { return client.execute(get, successHandler); } catch (Exception ex) { return null; } } }, allOf(containsString("I ran!"), containsString("development"))); // Source code change Files.write(new File(tmp.getRoot(), "src/main/java/controllers/Application.java").toPath(), CONTROLLER_CHANGED.getBytes(UTF_8)); Thread.sleep(2000); // Wait for source code change to be detected assertThat(client.execute(get, successHandler), containsString("I ran changed!")); // Exception Files.write(new File(tmp.getRoot(), "src/main/java/controllers/Application.java").toPath(), CONTROLLER_EXCEPTIONAL.getBytes(UTF_8)); Thread.sleep(2000); // Wait for source code change to be detected HttpResponse response = client.execute(get); int code = response.getStatusLine().getStatusCode(); String body = InputStreams.readAllAsString(response.getEntity().getContent(), BUF_SIZE_4K, UTF_8); assertThat(code, is(500)); assertThat(body, containsString("I throwed!")); // Build error Files.write(new File(tmp.getRoot(), "src/main/java/controllers/Application.java").toPath(), CONTROLLER_BUILD_ERROR.getBytes(UTF_8)); Thread.sleep(2000); // Wait for source code change to be detected response = client.execute(get); code = response.getStatusLine().getStatusCode(); body = InputStreams.readAllAsString(response.getEntity().getContent(), BUF_SIZE_4K, UTF_8); assertThat(code, is(500)); assertThat(body, containsString("I FAILED TO COMPILE!")); // Back to normal Files.write(new File(tmp.getRoot(), "src/main/java/controllers/Application.java").toPath(), CONTROLLER.getBytes(UTF_8)); Thread.sleep(2000); // Wait for source code change to be detected assertThat(client.execute(get, successHandler), containsString("I ran!")); // Werval Documentation assertThat(client.execute(new HttpGet("http://localhost:23023/@doc"), successHandler), containsString("Werval Documentation")); client.getConnectionManager().shutdown(); } finally { devshellThread.interrupt(); } } @Test public void startTaskIntegrationTest() throws InterruptedException, IOException { final Holder<Exception> errorHolder = new Holder<>(); Thread runThread = new Thread(newRunnable(errorHolder, "start"), "gradle-werval-start-thread"); try { runThread.start(); final HttpClient client = new DefaultHttpClient(); final HttpGet get = new HttpGet("http://localhost:23023/"); final ResponseHandler<String> handler = new BasicResponseHandler(); await().atMost(60, SECONDS).pollInterval(5, SECONDS).until(new Callable<String>() { @Override public String call() throws Exception { try { return client.execute(get, handler); } catch (Exception ex) { return null; } } }, allOf(containsString("I ran!"), containsString("custom"))); client.getConnectionManager().shutdown(); if (errorHolder.isSet()) { throw new RuntimeException( "Error during werval:start invocation: " + errorHolder.get().getMessage(), errorHolder.get()); } } finally { runThread.interrupt(); } } private Runnable newRunnable(final Holder<Exception> errorHolder, final String task) { return new Runnable() { @Override public void run() { try { GradleRunner runner = GradleRunnerFactory.create(); runner.setDirectory(tmp.getRoot()); runner.setArguments(asList("--debug", "--stacktrace", task)); runner.run(); } catch (Exception ex) { errorHolder.set(ex); } } }; } }