blob: 4cb2d2b22f105139ef872de2e5a9f391e5ba01b7 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.brooklyn.cli;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.NoSuchElementException;
import java.util.Scanner;
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 java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.apache.brooklyn.core.entity.factory.ApplicationBuilder;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import com.google.common.collect.Lists;
/**
* Command line interface test support.
*/
public class BaseCliIntegrationTest {
// TODO does this need to be hard-coded?
private static final String BROOKLYN_BIN_PATH = "./target/brooklyn-dist/bin/brooklyn";
private static final String BROOKLYN_CLASSPATH = "./target/test-classes/:./target/classes/";
// Times in seconds to allow Brooklyn to run and produce output
private static final long DELAY = 10l;
private static final long TIMEOUT = DELAY + 30l;
private ExecutorService executor;
@BeforeMethod(alwaysRun = true)
public void setup() {
executor = Executors.newCachedThreadPool();
}
@AfterMethod(alwaysRun = true)
public void teardown() {
executor.shutdownNow();
}
/** Invoke the brooklyn script with arguments. */
public Process startBrooklyn(String...argv) throws Throwable {
ProcessBuilder pb = new ProcessBuilder();
pb.environment().remove("BROOKLYN_HOME");
pb.environment().put("BROOKLYN_CLASSPATH", BROOKLYN_CLASSPATH);
pb.command(Lists.asList(BROOKLYN_BIN_PATH, argv));
return pb.start();
}
public void testBrooklyn(Process brooklyn, BrooklynCliTest test, int expectedExit) throws Throwable {
testBrooklyn(brooklyn, test, expectedExit, false);
}
/** Tests the operation of the Brooklyn CLI. */
public void testBrooklyn(Process brooklyn, BrooklynCliTest test, int expectedExit, boolean stop) throws Throwable {
try {
Future<Integer> future = executor.submit(test);
// Send CR to stop if required
if (stop) {
OutputStream out = brooklyn.getOutputStream();
out.write('\n');
out.flush();
}
int exitStatus = future.get(TIMEOUT, TimeUnit.SECONDS);
// Check error code from process
assertEquals(exitStatus, expectedExit, "Command returned wrong status");
} catch (TimeoutException te) {
fail("Timed out waiting for process to complete", te);
} catch (ExecutionException ee) {
if (ee.getCause() instanceof AssertionError) {
throw ee.getCause();
} else throw ee;
} finally {
brooklyn.destroy();
}
}
/** A {@link Callable} that encapsulates Brooklyn CLI test logic. */
public static abstract class BrooklynCliTest implements Callable<Integer> {
private final Process brooklyn;
private String consoleOutput;
private String consoleError;
public BrooklynCliTest(Process brooklyn) {
this.brooklyn = brooklyn;
}
@Override
public Integer call() throws Exception {
// Wait for initial output
Thread.sleep(TimeUnit.SECONDS.toMillis(DELAY));
// Get the console output of running that command
consoleOutput = convertStreamToString(brooklyn.getInputStream());
consoleError = convertStreamToString(brooklyn.getErrorStream());
// Check if the output looks as expected
checkConsole();
// Return exit status on completion
return brooklyn.waitFor();
}
/** Perform test assertions on console output and error streams. */
public abstract void checkConsole();
private String convertStreamToString(InputStream is) {
try {
return new Scanner(is).useDelimiter("\\A").next();
} catch (NoSuchElementException e) {
return "";
}
}
protected void assertConsoleOutput(String...expected) {
for (String e : expected) {
assertTrue(consoleOutput.contains(e), "Execution output not logged; output=" + consoleOutput);
}
}
protected void assertNoConsoleOutput(String...expected) {
for (String e : expected) {
assertFalse(consoleOutput.contains(e), "Execution output logged; output=" + consoleOutput);
}
}
protected void assertConsoleError(String...expected) {
for (String e : expected) {
assertTrue(consoleError.contains(e), "Execution error not logged; error=" + consoleError);
}
}
protected void assertNoConsoleError(String...expected) {
for (String e : expected) {
assertFalse(consoleError.contains(e), "Execution error logged; error=" + consoleError);
}
}
protected void assertConsoleOutputEmpty() {
assertTrue(consoleOutput.isEmpty(), "Output present; output=" + consoleOutput);
}
protected void assertConsoleErrorEmpty() {
assertTrue(consoleError.isEmpty(), "Error present; error=" + consoleError);
}
};
/** An empty application for testing. */
public static class TestApplication extends ApplicationBuilder {
@Override
protected void doBuild() {
// Empty, for testing
}
}
}