/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.core.utils.executor.testutils;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import de.rcenvironment.core.toolkitbridge.transitional.TextStreamWatcherFactory;
import de.rcenvironment.core.utils.common.textstream.TextStreamWatcher;
import de.rcenvironment.core.utils.common.textstream.receivers.CapturingTextOutReceiver;
import de.rcenvironment.core.utils.executor.LocalApacheCommandLineExecutor;
/**
* A wrapper/utility class to simplify execution of external commands in integration tests.
*
* @author Robert Mischke
*/
public class IntegrationTestExecutorUtils {
/**
* Defines a boolean filter for individual output lines.
*
* @author Robert Mischke
*/
public interface LineFilter {
/**
* @param line a text line, typically from some sort of execution
* @return true if this line should be kept for subsequent processing
*/
boolean accept(String line);
}
/**
* Holder for execution results, ie the exit code and stdout/stderr.
*
* @author Robert Mischke
*/
public static class ExecutionResult {
private static final int INITIAL_STRING_CONTAINER_SIZE = 64;
// Note: fields are left public for simplicity; encapsulate when necessary
/**
* The invoked process' exit code.
*/
public final int exitCode;
/**
* The raw captured stdout output.
*/
public final String stdout;
/**
* The stdout output, parsed into single lines for convenience.
*/
public final List<String> stdoutLines;
/**
* The stdout output, parsed into single lines and filtered for convenience; if no {@link LineFilter} is provided to the
* constructor, it points to the unfiltered list.
*/
public final List<String> filteredStdoutLines;
/**
* The raw captured stderr output.
*/
public final String stderr;
/**
* The stderr output, parsed into single lines for convenience.
*/
public final List<String> stderrLines;
/**
* The stderr output, parsed into single lines and filtered for convenience; if no {@link LineFilter} is provided to the
* constructor, it points to the unfiltered list.
*/
public final List<String> filteredStderrLines;
public ExecutionResult(int exitCode, String stdout, String stderr) throws IOException {
this(exitCode, stdout, stderr, null);
}
public ExecutionResult(int exitCode, String stdout, String stderr, LineFilter lineFilter) throws IOException {
this.exitCode = exitCode;
this.stdout = stdout;
this.stderr = stderr;
this.stdoutLines = parseIntoLines(stdout);
this.stderrLines = parseIntoLines(stderr);
if (lineFilter != null) {
filteredStdoutLines = filterLines(stdoutLines, lineFilter);
filteredStderrLines = filterLines(stderrLines, lineFilter);
} else {
filteredStdoutLines = stdoutLines;
filteredStderrLines = stderrLines;
}
}
private ExecutionResult(int exitCode, String stdout, String stderr, List<String> stdoutLines, List<String> stderrLines,
LineFilter lineFilter) throws IOException {
this.exitCode = exitCode;
this.stdout = stdout;
this.stderr = stderr;
this.stdoutLines = stdoutLines;
this.stderrLines = stderrLines;
if (lineFilter != null) {
filteredStdoutLines = filterLines(stdoutLines, lineFilter);
filteredStderrLines = filterLines(stderrLines, lineFilter);
} else {
filteredStdoutLines = null;
filteredStderrLines = null;
}
}
/**
* Applies a new {@link LineFilter} to an existing {@link ExecutionResult}, returning a result as if the filter had been applied
* during construction.
*
* @param lineFilter the new filter to apply
* @return a new (immutable) {@link ExecutionResult} object
*/
public ExecutionResult applyLineFilter(LineFilter lineFilter) {
try {
return new ExecutionResult(exitCode, stdout, stderr, stdoutLines, stderrLines, lineFilter);
} catch (IOException e) {
throw new RuntimeException("Unexpected exception while re-filtering", e);
}
}
private List<String> parseIntoLines(String rawOutput) throws IOException {
List<String> container = new ArrayList<>(INITIAL_STRING_CONTAINER_SIZE);
BufferedReader reader = new BufferedReader(new StringReader(rawOutput));
String line;
while ((line = reader.readLine()) != null) {
container.add(line);
}
return Collections.unmodifiableList(container);
}
private List<String> filterLines(List<String> input, LineFilter lineFilter) {
List<String> container = new ArrayList<>(INITIAL_STRING_CONTAINER_SIZE);
for (String line : input) {
if (lineFilter.accept(line)) {
container.add(line);
}
}
return Collections.unmodifiableList(container);
}
}
private final File workingDir;
public IntegrationTestExecutorUtils(File workingDir) {
this.workingDir = workingDir;
}
/**
* Invokes the given command line and waits for the external process to complete.
*
* @param command the command line to execute
* @return a {@link ExecutionResult} holder with exit code and stdout/stderr
* @throws IOException on I/O errors
* @throws InterruptedException on thread interruption (e.g. by test timeout)
*/
public ExecutionResult executeAndWait(String command) throws IOException, InterruptedException {
LocalApacheCommandLineExecutor executor = new LocalApacheCommandLineExecutor(workingDir);
executor.start(command);
CapturingTextOutReceiver stdoutCapture = new CapturingTextOutReceiver("");
CapturingTextOutReceiver stderrCapture = new CapturingTextOutReceiver("");
final TextStreamWatcher stdoutWatcher = TextStreamWatcherFactory.create(executor.getStdout(), stdoutCapture).start();
final TextStreamWatcher stderrWatcher = TextStreamWatcherFactory.create(executor.getStderr(), stderrCapture).start();
int exitCode = executor.waitForTermination();
stdoutWatcher.waitForTermination();
stderrWatcher.waitForTermination();
return new ExecutionResult(exitCode, stdoutCapture.getBufferedOutput(), stderrCapture.getBufferedOutput());
}
}