/* * Copyright (C) 2006-2016 DLR, Germany * * All rights reserved * * http://www.rcenvironment.de/ */ package de.rcenvironment.core.utils.executor; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.File; import java.io.IOException; import org.apache.commons.exec.CommandLine; import org.apache.commons.exec.DefaultExecuteResultHandler; import org.apache.commons.exec.DefaultExecutor; import org.apache.commons.exec.ExecuteException; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import com.sun.jna.Platform; import de.rcenvironment.core.utils.common.StringUtils; import de.rcenvironment.core.utils.common.TempFileServiceAccess; /** * Tests for {@link ProcessUtils}. * * @author Tobias Rodehutskors */ public class ProcessUtilsTest { /** * This seems to be a common trick for windows to wait for some time. It pings localhost 100 times and waits between each ping one * second. */ static final String WINDOWS_WAIT_COMMAND_TEMPLATE = "ping 127.0.0.1 -n %d > NUL"; // static final String LINUX_WAIT_COMMAND = "sleep 100"; static final String LINUX_WAIT_COMMAND_TEMPLATE = "sleep %d"; static final long ONE_SECOND_AS_MILLIS = 1000; private static final String UNKOWN_PLATFORM = "Unkown platform. Currently only Windows and Linux are supported."; private static final int HUNDRED_SECONDS = 100; private File workDir; /** * Static test setup. */ @BeforeClass public static void classSetUp() { TempFileServiceAccess.setupUnitTestEnvironment(); } /** * Setup. * * @throws IOException on I/O errors */ @Before public void setUp() throws IOException { workDir = TempFileServiceAccess.getInstance().createManagedTempDir( "-unittest"); } /** * Cleanup. * * @throws IOException on I/O errors */ @After public void tearDown() throws IOException { if (workDir != null) { TempFileServiceAccess.getInstance().disposeManagedTempDirOrFile( workDir); } } /** * Starts a short running process, waits for its completion and tries to retrieve the PID of the finished process. * * @throws ExecuteException e * @throws IOException e * @throws InterruptedException e * @throws NoSuchFieldException e * @throws SecurityException e * @throws IllegalArgumentException e * @throws IllegalAccessException e */ @Test public void testGetPidOfFinishedProcess() throws ExecuteException, IOException, InterruptedException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { DefaultExecutor executor = new DefaultExecutor(); ProcessExtractor processExtractor = new ProcessExtractor(); executor.setWatchdog(processExtractor); CommandLine cl = null; if (Platform.isWindows()) { cl = ProcessUtils.constructCommandLine(""); } else if (Platform.isLinux()) { cl = ProcessUtils.constructCommandLine(":"); } else { throw new IllegalStateException(UNKOWN_PLATFORM); } int exitCode = executor.execute(cl); assertEquals(0, exitCode); Process process = processExtractor.getProcess(); assertNotNull(process); assertFalse(isRunning(process)); ProcessUtils.getPid(process); } /** * * Starts a long running process, retrieves the process id of the started process and kills it afterwards. * * @throws ExecuteException e * @throws IOException e * @throws InterruptedException e * @throws NoSuchFieldException e * @throws SecurityException e * @throws IllegalArgumentException e * @throws IllegalAccessException e */ @Test public void testGetPidOfRunningProcessAndKillProcess() throws ExecuteException, IOException, InterruptedException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { DefaultExecutor executor = new DefaultExecutor(); ProcessExtractor processExtractor = new ProcessExtractor(); executor.setWatchdog(processExtractor); // start a long running process CommandLine cl; if (Platform.isWindows()) { cl = ProcessUtils.constructCommandLine(StringUtils.format(WINDOWS_WAIT_COMMAND_TEMPLATE, HUNDRED_SECONDS)); } else if (Platform.isLinux()) { cl = ProcessUtils.constructCommandLine(StringUtils.format(ProcessUtilsTest.LINUX_WAIT_COMMAND_TEMPLATE, HUNDRED_SECONDS)); } else { throw new IllegalStateException(UNKOWN_PLATFORM); } DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler(); executor.execute(cl, resultHandler); // wait for some time to make sure that the process has started Thread.sleep(ONE_SECOND_AS_MILLIS); // extract the process and its process id Process process = processExtractor.getProcess(); assertNotNull(process); assertTrue(isRunning(process)); int pid = ProcessUtils.getPid(process); assertTrue(isRunning(process)); // kill the process tree assertTrue(ProcessUtils.killProcessTree(pid)); // verify that the process has died resultHandler.waitFor(10 * ONE_SECOND_AS_MILLIS); if (!resultHandler.hasResult()) { fail("The process was not killed within 10 seconds"); } int exitCode = resultHandler.getExitValue(); int expectedExitCode; if (Platform.isWindows()) { expectedExitCode = ProcessUtils.WINDOWS_EXIT_CODE_SIGTERM; } else { expectedExitCode = ProcessUtils.LINUX_EXIT_CODE_SIGTERM; } assertEquals(expectedExitCode, exitCode); assertFalse(isRunning(process)); } /** * Starts a process, retrieves the process id of the started process and waits for its termination. Afterwards it tries to kills it. No * exception should be thrown. * * @throws ExecuteException e * @throws IOException e * @throws InterruptedException e * @throws NoSuchFieldException e * @throws SecurityException e * @throws IllegalArgumentException e * @throws IllegalAccessException e */ @Test public void testGetPidOfRunningProcessAndKillProcessWhenFinished() throws ExecuteException, IOException, InterruptedException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { DefaultExecutor executor = new DefaultExecutor(); ProcessExtractor processExtractor = new ProcessExtractor(); executor.setWatchdog(processExtractor); // start a long running process CommandLine cl; if (Platform.isWindows()) { cl = ProcessUtils.constructCommandLine(StringUtils.format(WINDOWS_WAIT_COMMAND_TEMPLATE, 5)); } else if (Platform.isLinux()) { cl = ProcessUtils.constructCommandLine(StringUtils.format(ProcessUtilsTest.LINUX_WAIT_COMMAND_TEMPLATE, 5)); } else { throw new IllegalStateException(UNKOWN_PLATFORM); } DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler(); executor.execute(cl, resultHandler); // wait for some time to make sure that the process has started Thread.sleep(ONE_SECOND_AS_MILLIS); // extract the process and its process id Process process = processExtractor.getProcess(); assertNotNull(process); assertTrue(isRunning(process)); int pid = ProcessUtils.getPid(process); assertTrue(isRunning(process)); // wait for the process to finish resultHandler.waitFor(10 * ONE_SECOND_AS_MILLIS); // verify that the process has finished if (!resultHandler.hasResult()) { fail("The process has not finished within 10 seconds"); } int exitCode = resultHandler.getExitValue(); int expectedExitCode; if (Platform.isWindows()) { expectedExitCode = ProcessUtils.WINDOWS_EXIT_CODE_SUCCESS; } else { expectedExitCode = ProcessUtils.LINUX_EXIT_CODE_SUCCESS; } assertEquals(expectedExitCode, exitCode); assertFalse(isRunning(process)); // try to kill the process tree assertTrue(ProcessUtils.killProcessTree(pid)); } /** * TODO JAVA8 As soon as the code base is converted to Java 8 this call should be replaced with {@link java.lang.Process#isAlive()}. */ boolean isRunning(Process process) { try { process.exitValue(); return false; } catch (java.lang.IllegalThreadStateException e) { return true; } } }