package cz.cuni.mff.d3s.been.hostruntime.task;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.OutputStream;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import javax.tools.*;
import org.apache.commons.exec.ExecuteStreamHandler;
import org.apache.commons.exec.PumpStreamHandler;
import org.apache.commons.io.FileUtils;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import cz.cuni.mff.d3s.been.debugassistant.DebugAssistant;
import cz.cuni.mff.d3s.been.hostruntime.TaskException;
public class TaskProcessTest extends Assert {
@Mock
private DebugAssistant debugAssistant;
@Mock
private DependencyDownloader dependencyDownloader;
@Mock
private CmdLineBuilder cmdLineBuilder;
private String sourceWithTimeoutAsFirstArg = //
"public class Main {" + //
" public static void main(String[] args) throws InterruptedException {" + //
" Thread.sleep(new Integer(args[0]));" + //
" }" + //
"}";
private String sourceWithStdOutWrite = //
"public class Main {" + //
" public static void main(String[] args) throws InterruptedException {" + //
" System.out.println(\"STD OUT WRITE EXAMPLE\");" + //
" }" + //
"}";
private String sourceWithStdErrWrite = //
"public class Main {" + //
" public static void main(String[] args) throws InterruptedException {" + //
" System.err.println(\"STD ERR WRITE EXAMPLE\");" + //
" }" + //
"}";
private String sourceDoingNothing = //
"public class Main {" + //
" public static void main(String[] args) throws InterruptedException {" + //
" }" + //
"}";
private String sourceWithBadExitCode = //
"public class Main {" + //
" public static void main(String[] args) throws InterruptedException {" + //
" System.exit(123);" + //
" }" + //
"}";
@Rule
public TemporaryFolder tmpFolder = new TemporaryFolder();
private File workingDirectory;
private Path wrkDirPath;
@Mock
private OutputStream fakeStdOutOutputStream;
@Mock
private OutputStream fakeStdErrOutputStream;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
workingDirectory = tmpFolder.getRoot();
wrkDirPath = workingDirectory.toPath();
}
@Test(expected = TaskException.class)
public void testExceptionIsThrownWhenProcessEndsWithErrorExitCode() throws Exception {
setUpCmdLineBuilder(sourceWithBadExitCode);
Map<String, String> environment = new HashMap<>();
TaskProcess process = new TaskProcess(cmdLineBuilder, workingDirectory.toPath(), environment, fakeStdOutOutputStream, fakeStdErrOutputStream, dependencyDownloader);
process.start();
}
@Test(expected = TaskException.class)
public void testExceptionIsThrownWhenInvalidCommandLineProvided() throws Exception {
Mockito.when(cmdLineBuilder.build()).thenReturn(new TaskCommandLine("q w e r t y"));
Map<String, String> environment = new HashMap<>();
final TaskProcess process = new TaskProcess(cmdLineBuilder, wrkDirPath, environment, fakeStdOutOutputStream, fakeStdErrOutputStream, dependencyDownloader);
process.start();
}
@Test(expected = TaskException.class)
public void testExceptionIsThrownOnTimeoutExceeded() throws Exception {
setUpCmdLineBuilderWithExecTime(sourceWithTimeoutAsFirstArg, 10);
Map<String, String> environment = new HashMap<>();
TaskProcess process = new TaskProcess(cmdLineBuilder, wrkDirPath, environment, fakeStdOutOutputStream, fakeStdErrOutputStream, dependencyDownloader);
process.setTimeout(1);
process.start();
}
@Test
public void testCorrectExitCodeIsReturned() throws Exception {
setUpCmdLineBuilder(sourceDoingNothing);
Map<String, String> environment = new HashMap<>();
final TaskProcess process = new TaskProcess(cmdLineBuilder, wrkDirPath, environment, fakeStdOutOutputStream, fakeStdErrOutputStream, dependencyDownloader);
assertEquals(0, process.start());
}
@Test
public void testTaskSuccessfullyFinishedBeforeTimeout() throws Exception {
setUpCmdLineBuilderWithExecTime(sourceWithTimeoutAsFirstArg, 0);
Map<String, String> environment = new HashMap<>();
TaskProcess process = new TaskProcess(cmdLineBuilder, wrkDirPath, environment, fakeStdOutOutputStream, fakeStdErrOutputStream, dependencyDownloader);
process.setTimeout(2);
process.start();
}
@Test
public void testTaskHandlesStdOutput() throws Exception {
setUpCmdLineBuilderWithExecTime(sourceWithStdOutWrite, 0);
Map<String, String> environment = new HashMap<>();
ByteArrayOutputStream fakeStdOutOutputStream = new ByteArrayOutputStream();
TaskProcess process = new TaskProcess(cmdLineBuilder, wrkDirPath, environment, fakeStdOutOutputStream, fakeStdErrOutputStream, dependencyDownloader);
process.setTimeout(2);
process.start();
assertEquals("STD OUT WRITE EXAMPLE\n", new String(fakeStdOutOutputStream.toByteArray()));
}
@Test
public void testTaskHandlesErrOutput() throws Exception {
setUpCmdLineBuilderWithExecTime(sourceWithStdErrWrite, 0);
Map<String, String> environment = new HashMap<>();
ByteArrayOutputStream fakeStdErrOutputStream = new ByteArrayOutputStream();
TaskProcess process = new TaskProcess(cmdLineBuilder, wrkDirPath, environment, fakeStdOutOutputStream, fakeStdErrOutputStream, dependencyDownloader);
process.setTimeout(2);
process.start();
assertEquals("STD ERR WRITE EXAMPLE\n", new String(fakeStdErrOutputStream.toByteArray()));
}
@Test(timeout = 15000)
public void testProcessIsCorrectlyKilled() throws Exception {
setUpCmdLineBuilderWithExecTime(sourceWithTimeoutAsFirstArg, 10000);
Map<String, String> environment = new HashMap<>();
ExecuteStreamHandler streamhandler = new PumpStreamHandler();
final TaskProcess process = new TaskProcess(cmdLineBuilder, wrkDirPath, environment, fakeStdOutOutputStream, fakeStdErrOutputStream, dependencyDownloader);
Thread t = new Thread() {
@Override
public void run() {
try {
process.start();
fail("expected task has been killed exception");
} catch (TaskException e) {
assertTrue(e.getMessage().contains("Task has been killed"));
}
}
};
t.start();
Thread.sleep(200);
process.kill();
t.join();
}
///////////////////////////
//
// END OF TEST METHODS
//
///////
private void setUpCmdLineBuilder(String source) throws Exception {
TaskCommandLine cmd = cmdLine(source);
Mockito.when(cmdLineBuilder.build()).thenReturn(cmd);
}
private void setUpCmdLineBuilderWithExecTime(String source, int execTime) throws Exception {
TaskCommandLine cmd = cmdLineWithExecutionTime(source, execTime);
Mockito.when(cmdLineBuilder.build()).thenReturn(cmd);
}
/**
* Creates command line for process which will sleep given amount of seconds.
* I used this ugly hack because all tests should be runnable on all operating
* systems and java is ideal candidate.
*/
private TaskCommandLine cmdLine(String source) throws Exception {
String className = "Main";
compile(source, className);
TaskCommandLine commandLine = new TaskCommandLine("java");
commandLine.addArgument("-Xms2m");
commandLine.addArgument("-Xmx4m");
commandLine.addArgument(className);
return commandLine;
}
/**
* Creates command line for process which will sleep given amount of seconds.
* I used this ugly hack because all tests should be runnable on all operating
* systems and java is ideal candidate.
*/
private TaskCommandLine cmdLineWithExecutionTime(String source, int execSeconds) throws Exception {
TaskCommandLine commandLine = cmdLine(source);
commandLine.addArgument("" + execSeconds * 1000);
return commandLine;
}
/**
* compiles java source to X.java file where X is className given as
* parameter.
*
* @param source
* source
* @param className
* class name
* @throws Exception
*/
private void compile(String source, String className) throws Exception {
File javaFile = new File(workingDirectory, className + ".java");
FileUtils.writeStringToFile(javaFile, source);
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);
Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromStrings(Arrays.asList(javaFile.getAbsolutePath()));
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, null, null, compilationUnits);
assertTrue("class " + source + " not compiled - check syntax in test definition", task.call());
fileManager.close();
}
}